summaryrefslogtreecommitdiffstats
path: root/xbmc/settings/lib
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xbmc/settings/lib/CMakeLists.txt31
-rw-r--r--xbmc/settings/lib/ISetting.cpp76
-rw-r--r--xbmc/settings/lib/ISetting.h139
-rw-r--r--xbmc/settings/lib/ISettingCallback.h88
-rw-r--r--xbmc/settings/lib/ISettingControl.cpp56
-rw-r--r--xbmc/settings/lib/ISettingControl.h36
-rw-r--r--xbmc/settings/lib/ISettingControlCreator.h32
-rw-r--r--xbmc/settings/lib/ISettingCreator.h35
-rw-r--r--xbmc/settings/lib/ISettingsHandler.h58
-rw-r--r--xbmc/settings/lib/ISettingsValueSerializer.h21
-rw-r--r--xbmc/settings/lib/Setting.cpp1690
-rw-r--r--xbmc/settings/lib/Setting.h536
-rw-r--r--xbmc/settings/lib/SettingCategoryAccess.cpp41
-rw-r--r--xbmc/settings/lib/SettingCategoryAccess.h47
-rw-r--r--xbmc/settings/lib/SettingConditions.cpp153
-rw-r--r--xbmc/settings/lib/SettingConditions.h105
-rw-r--r--xbmc/settings/lib/SettingDefinitions.h138
-rw-r--r--xbmc/settings/lib/SettingDependency.cpp421
-rw-r--r--xbmc/settings/lib/SettingDependency.h135
-rw-r--r--xbmc/settings/lib/SettingLevel.h21
-rw-r--r--xbmc/settings/lib/SettingRequirement.cpp37
-rw-r--r--xbmc/settings/lib/SettingRequirement.h47
-rw-r--r--xbmc/settings/lib/SettingSection.cpp359
-rw-r--r--xbmc/settings/lib/SettingSection.h186
-rw-r--r--xbmc/settings/lib/SettingType.h23
-rw-r--r--xbmc/settings/lib/SettingUpdate.cpp65
-rw-r--r--xbmc/settings/lib/SettingUpdate.h46
-rw-r--r--xbmc/settings/lib/SettingsManager.cpp1424
-rw-r--r--xbmc/settings/lib/SettingsManager.h544
29 files changed, 6590 insertions, 0 deletions
diff --git a/xbmc/settings/lib/CMakeLists.txt b/xbmc/settings/lib/CMakeLists.txt
new file mode 100644
index 0000000..a39a0ca
--- /dev/null
+++ b/xbmc/settings/lib/CMakeLists.txt
@@ -0,0 +1,31 @@
+set(SOURCES ISetting.cpp
+ ISettingControl.cpp
+ Setting.cpp
+ SettingCategoryAccess.cpp
+ SettingConditions.cpp
+ SettingDependency.cpp
+ SettingRequirement.cpp
+ SettingSection.cpp
+ SettingsManager.cpp
+ SettingUpdate.cpp)
+
+set(HEADERS ISetting.h
+ ISettingCallback.h
+ ISettingControl.h
+ ISettingControlCreator.h
+ ISettingCreator.h
+ ISettingsHandler.h
+ ISettingsValueSerializer.h
+ Setting.h
+ SettingCategoryAccess.h
+ SettingConditions.h
+ SettingDefinitions.h
+ SettingDependency.h
+ SettingLevel.h
+ SettingRequirement.h
+ SettingSection.h
+ SettingsManager.h
+ SettingType.h
+ SettingUpdate.h)
+
+core_add_library(settings_lib)
diff --git a/xbmc/settings/lib/ISetting.cpp b/xbmc/settings/lib/ISetting.cpp
new file mode 100644
index 0000000..a836f99
--- /dev/null
+++ b/xbmc/settings/lib/ISetting.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "ISetting.h"
+
+#include "SettingDefinitions.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/XMLUtils.h"
+
+#include <string>
+
+ISetting::ISetting(const std::string &id, CSettingsManager *settingsManager /* = nullptr */)
+ : m_id(id)
+ , m_settingsManager(settingsManager)
+ , m_requirementCondition(settingsManager)
+{ }
+
+bool ISetting::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ if (node == nullptr)
+ return false;
+
+ bool value;
+ if (XMLUtils::GetBoolean(node, SETTING_XML_ELM_VISIBLE, value))
+ m_visible = value;
+
+ auto element = node->ToElement();
+ if (element == nullptr)
+ return false;
+
+ int iValue = -1;
+ if (element->QueryIntAttribute(SETTING_XML_ATTR_LABEL, &iValue) == TIXML_SUCCESS && iValue > 0)
+ m_label = iValue;
+ if (element->QueryIntAttribute(SETTING_XML_ATTR_HELP, &iValue) == TIXML_SUCCESS && iValue > 0)
+ m_help = iValue;
+
+ auto requirementNode = node->FirstChild(SETTING_XML_ELM_REQUIREMENT);
+ if (requirementNode == nullptr)
+ return true;
+
+ return m_requirementCondition.Deserialize(requirementNode);
+}
+
+bool ISetting::DeserializeIdentification(const TiXmlNode* node, std::string& identification)
+{
+ return DeserializeIdentificationFromAttribute(node, SETTING_XML_ATTR_ID, identification);
+}
+
+bool ISetting::DeserializeIdentificationFromAttribute(const TiXmlNode* node,
+ const std::string& attribute,
+ std::string& identification)
+{
+ if (node == nullptr)
+ return false;
+
+ auto element = node->ToElement();
+ if (element == nullptr)
+ return false;
+
+ auto idAttribute = element->Attribute(attribute);
+ if (idAttribute == nullptr || idAttribute->empty())
+ return false;
+
+ identification = *idAttribute;
+ return true;
+}
+
+void ISetting::CheckRequirements()
+{
+ m_meetsRequirements = m_requirementCondition.Check();
+}
diff --git a/xbmc/settings/lib/ISetting.h b/xbmc/settings/lib/ISetting.h
new file mode 100644
index 0000000..8a40fe3
--- /dev/null
+++ b/xbmc/settings/lib/ISetting.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "SettingRequirement.h"
+
+#include <string>
+
+class CSettingsManager;
+class TiXmlNode;
+
+/*!
+ \ingroup settings
+ \brief Interface defining the base of all setting objects
+ */
+class ISetting
+{
+public:
+ /*!
+ \brief Creates a new setting object with the given identifier.
+
+ \param id Identifier of the setting object
+ \param settingsManager Reference to the settings manager
+ */
+ ISetting(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ virtual ~ISetting() = default;
+
+ /*!
+ \brief Deserializes the given XML node into the properties of the setting
+ object.
+
+ If the update parameter is true, the checks for mandatory properties are
+ skipped and values are only updated.
+
+ \param node XML node containing the properties of the setting object
+ \param update Whether to perform checks for mandatory properties or not
+ \return True if deserialization was successful, false otherwise
+ */
+ virtual bool Deserialize(const TiXmlNode *node, bool update = false);
+
+ /*!
+ \brief Gets the identifier of the setting object.
+
+ \return Identifier of the setting object
+ */
+ const std::string& GetId() const { return m_id; }
+ /*!
+ \brief Whether the setting object is visible or hidden.
+
+ \return True if the setting object is visible, false otherwise
+ */
+ virtual bool IsVisible() const { return m_visible; }
+ /*!
+ \brief Sets the visibility state of the setting object.
+
+ \param visible Whether the setting object shall be visible or not
+ */
+ virtual void SetVisible(bool visible) { m_visible = visible; }
+ /*!
+ \brief Gets the localizeable label ID of the setting group.
+
+ \return Localizeable label ID of the setting group
+ */
+ int GetLabel() const { return m_label; }
+ /*!
+ \brief Sets the localizeable label ID of the setting group.
+
+ \param label Localizeable label ID of the setting group
+ */
+ void SetLabel(int label) { m_label = label; }
+ /*!
+ \brief Gets the localizeable help ID of the setting group.
+
+ \return Localizeable help ID of the setting group
+ */
+ int GetHelp() const { return m_help; }
+ /*!
+ \brief Sets the localizeable help ID of the setting group.
+
+ \param label Localizeable help ID of the setting group
+ */
+ void SetHelp(int help) { m_help = help; }
+ /*!
+ \brief Whether the setting object meets all necessary requirements.
+
+ \return True if the setting object meets all necessary requirements, false otherwise
+ */
+ virtual bool MeetsRequirements() const { return m_meetsRequirements; }
+ /*!
+ \brief Checks if the setting object meets all necessary requirements.
+ */
+ virtual void CheckRequirements();
+ /*!
+ \brief Sets whether the setting object meets all necessary requirements.
+
+ \param visible Whether the setting object meets all necessary requirements or not
+ */
+ virtual void SetRequirementsMet(bool requirementsMet) { m_meetsRequirements = requirementsMet; }
+
+ /*!
+ \brief Deserializes the given XML node to retrieve a setting object's
+ identifier.
+
+ \param node XML node containing a setting object's identifier
+ \param identification Will contain the deserialized setting object's identifier
+ \return True if a setting object's identifier was deserialized, false otherwise
+ */
+ static bool DeserializeIdentification(const TiXmlNode *node, std::string &identification);
+
+protected:
+ static constexpr int DefaultLabel = -1;
+ /*!
+ \brief Deserializes the given XML node to retrieve a setting object's identifier from the given attribute.
+
+ \param node XML node containing a setting object's identifier
+ \param attribute Attribute which contains the setting object's identifier
+ \param identification Will contain the deserialized setting object's identifier
+ \return True if a setting object's identifier was deserialized, false otherwise
+ */
+ static bool DeserializeIdentificationFromAttribute(const TiXmlNode* node,
+ const std::string& attribute,
+ std::string& identification);
+
+ std::string m_id;
+ CSettingsManager *m_settingsManager;
+
+private:
+ bool m_visible = true;
+ int m_label = DefaultLabel;
+ int m_help = -1;
+ bool m_meetsRequirements = true;
+ CSettingRequirement m_requirementCondition;
+};
diff --git a/xbmc/settings/lib/ISettingCallback.h b/xbmc/settings/lib/ISettingCallback.h
new file mode 100644
index 0000000..00fc428
--- /dev/null
+++ b/xbmc/settings/lib/ISettingCallback.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include <memory>
+
+class CSetting;
+class TiXmlNode;
+
+class ISettingCallback
+{
+public:
+ virtual ~ISettingCallback() = default;
+
+ /*!
+ \brief The value of the given setting is being changed.
+
+ This callback is triggered whenever the value of a setting is being
+ changed. The given CSetting already contains the new value and the handler
+ of the callback has the possibility to allow or revert changing the value
+ of the setting. In case of a revert OnSettingChanging() is called again to
+ inform all listeners that the value change has been reverted.
+
+ \param setting The setting whose value is being changed (already containing the changed value)
+ \return True if the new value is acceptable otherwise false
+ */
+ virtual bool OnSettingChanging(const std::shared_ptr<const CSetting>& setting) { return true; }
+
+ /*!
+ \brief The value of the given setting has changed.
+
+ This callback is triggered whenever the value of a setting has been
+ successfully changed (i.e. none of the OnSettingChanging() handlers)
+ has reverted the change.
+
+ \param setting The setting whose value has been changed
+ */
+ virtual void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) {}
+
+ /*!
+ \brief The given setting has been activated.
+
+ This callback is triggered whenever the given setting has been activated.
+ This callback is only fired for CSettingAction settings.
+
+ \param setting The setting which has been activated.
+ */
+ virtual void OnSettingAction(const std::shared_ptr<const CSetting>& setting) {}
+
+ /*!
+ \brief The given setting needs to be updated.
+
+ This callback is triggered when a setting needs to be updated because its
+ value is outdated. This only happens when initially loading the value of a
+ setting and will not be triggered afterwards.
+
+ \param setting The setting which needs to be updated.
+ \param oldSettingId The id of the previous setting.
+ \param oldSettingNode The old setting node
+ \return True if the setting has been successfully updated otherwise false
+ */
+ virtual bool OnSettingUpdate(const std::shared_ptr<CSetting>& setting,
+ const char* oldSettingId,
+ const TiXmlNode* oldSettingNode)
+ {
+ return false;
+ }
+
+ /*!
+ \brief The given property of the given setting has changed
+
+ This callback is triggered when a property (e.g. enabled or the list of
+ dynamic options) has changed.
+
+ \param setting The setting which has a changed property
+ \param propertyName The string representation of the changed property
+ */
+ virtual void OnSettingPropertyChanged(const std::shared_ptr<const CSetting>& setting,
+ const char* propertyName)
+ {
+ }
+};
diff --git a/xbmc/settings/lib/ISettingControl.cpp b/xbmc/settings/lib/ISettingControl.cpp
new file mode 100644
index 0000000..3fc51a5
--- /dev/null
+++ b/xbmc/settings/lib/ISettingControl.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "ISettingControl.h"
+
+#include "ServiceBroker.h"
+#include "SettingDefinitions.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/log.h"
+
+Logger ISettingControl::s_logger;
+
+ISettingControl::ISettingControl()
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("ISettingControl");
+}
+
+bool ISettingControl::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ if (node == nullptr)
+ return false;
+
+ auto elem = node->ToElement();
+ if (elem == nullptr)
+ return false;
+
+ auto strTmp = elem->Attribute(SETTING_XML_ATTR_FORMAT);
+ std::string format;
+ if (strTmp != nullptr)
+ format = strTmp;
+ if (!SetFormat(format))
+ {
+ s_logger->error("error reading \"{}\" attribute of <control>", SETTING_XML_ATTR_FORMAT);
+ return false;
+ }
+
+ if ((strTmp = elem->Attribute(SETTING_XML_ATTR_DELAYED)) != nullptr)
+ {
+ if (!StringUtils::EqualsNoCase(strTmp, "false") && !StringUtils::EqualsNoCase(strTmp, "true"))
+ {
+ s_logger->error("error reading \"{}\" attribute of <control>", SETTING_XML_ATTR_DELAYED);
+ return false;
+ }
+ else
+ m_delayed = StringUtils::EqualsNoCase(strTmp, "true");
+ }
+
+ return true;
+}
diff --git a/xbmc/settings/lib/ISettingControl.h b/xbmc/settings/lib/ISettingControl.h
new file mode 100644
index 0000000..293d5ad
--- /dev/null
+++ b/xbmc/settings/lib/ISettingControl.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "utils/logtypes.h"
+
+#include <string>
+
+class TiXmlNode;
+
+class ISettingControl
+{
+public:
+ ISettingControl();
+ virtual ~ISettingControl() = default;
+
+ virtual std::string GetType() const = 0;
+ const std::string& GetFormat() const { return m_format; }
+ bool GetDelayed() const { return m_delayed; }
+ void SetDelayed(bool delayed) { m_delayed = delayed; }
+
+ virtual bool Deserialize(const TiXmlNode *node, bool update = false);
+ virtual bool SetFormat(const std::string &format) { return true; }
+
+protected:
+ bool m_delayed = false;
+ std::string m_format;
+
+ static Logger s_logger;
+};
diff --git a/xbmc/settings/lib/ISettingControlCreator.h b/xbmc/settings/lib/ISettingControlCreator.h
new file mode 100644
index 0000000..2f77e1c
--- /dev/null
+++ b/xbmc/settings/lib/ISettingControlCreator.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+class ISettingControl;
+
+/*!
+ \ingroup settings
+ \brief Interface for creating a new setting control of a custom setting control type.
+ */
+class ISettingControlCreator
+{
+public:
+ virtual ~ISettingControlCreator() = default;
+
+ /*!
+ \brief Creates a new setting control of the given custom setting control type.
+
+ \param controlType string representation of the setting control type
+ \return A new setting control object of the given (custom) setting control type or nullptr if the setting control type is unknown
+ */
+ virtual std::shared_ptr<ISettingControl> CreateControl(const std::string &controlType) const = 0;
+};
diff --git a/xbmc/settings/lib/ISettingCreator.h b/xbmc/settings/lib/ISettingCreator.h
new file mode 100644
index 0000000..e951c85
--- /dev/null
+++ b/xbmc/settings/lib/ISettingCreator.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+class CSetting;
+class CSettingsManager;
+
+/*!
+ \ingroup settings
+ \brief Interface for creating a new setting of a custom setting type.
+ */
+class ISettingCreator
+{
+public:
+ virtual ~ISettingCreator() = default;
+
+ /*!
+ \brief Creates a new setting of the given custom setting type.
+
+ \param settingType string representation of the setting type
+ \param settingId Identifier of the setting to be created
+ \param settingsManager Reference to the settings manager
+ \return A new setting object of the given (custom) setting type or nullptr if the setting type is unknown
+ */
+ virtual std::shared_ptr<CSetting> CreateSetting(const std::string &settingType, const std::string &settingId, CSettingsManager *settingsManager = nullptr) const = 0;
+};
diff --git a/xbmc/settings/lib/ISettingsHandler.h b/xbmc/settings/lib/ISettingsHandler.h
new file mode 100644
index 0000000..466b55a
--- /dev/null
+++ b/xbmc/settings/lib/ISettingsHandler.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+/*!
+ \ingroup settings
+ \brief Interface defining methods being called by the settings system if an
+ action is performed on multiple/all settings
+ */
+class ISettingsHandler
+{
+public:
+ virtual ~ISettingsHandler() = default;
+
+ /*!
+ \brief Settings loading has been initiated.
+
+ \return True if the settings should be loaded, false if the loading should be aborted.
+ */
+ virtual bool OnSettingsLoading() { return true; }
+ /*!
+ \brief Settings have been loaded.
+
+ This callback can be used to trigger loading other settings.
+ */
+ virtual void OnSettingsLoaded() { }
+ /*!
+ \brief Settings saving has been initiated.
+
+ \return True if the settings should be saved, false if the saving should be aborted.
+ */
+ virtual bool OnSettingsSaving() const { return true; }
+ /*!
+ \brief Settings have been saved.
+
+ This callback can be used to trigger saving other settings.
+ */
+ virtual void OnSettingsSaved() const { }
+ /*!
+ \brief Setting values have been unloaded.
+
+ This callback can be used to trigger uninitializing any state variables
+ (e.g. before re-loading the settings).
+ */
+ virtual void OnSettingsUnloaded() { }
+ /*!
+ \brief Settings have been cleared.
+
+ This callback can be used to trigger clearing any state variables.
+ */
+ virtual void OnSettingsCleared() { }
+};
diff --git a/xbmc/settings/lib/ISettingsValueSerializer.h b/xbmc/settings/lib/ISettingsValueSerializer.h
new file mode 100644
index 0000000..94bf664
--- /dev/null
+++ b/xbmc/settings/lib/ISettingsValueSerializer.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include <string>
+
+class CSettingsManager;
+
+class ISettingsValueSerializer
+{
+public:
+ virtual ~ISettingsValueSerializer() = default;
+
+ virtual std::string SerializeValues(const CSettingsManager* settingsManager) const = 0;
+};
diff --git a/xbmc/settings/lib/Setting.cpp b/xbmc/settings/lib/Setting.cpp
new file mode 100644
index 0000000..0802dd8
--- /dev/null
+++ b/xbmc/settings/lib/Setting.cpp
@@ -0,0 +1,1690 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "Setting.h"
+
+#include "ServiceBroker.h"
+#include "SettingDefinitions.h"
+#include "SettingsManager.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/XMLUtils.h"
+#include "utils/log.h"
+
+#include <mutex>
+#include <shared_mutex>
+#include <sstream>
+#include <utility>
+
+template<typename TKey, typename TValue>
+bool CheckSettingOptionsValidity(const TValue& value, const std::vector<std::pair<TKey, TValue>>& options)
+{
+ for (const auto& it : options)
+ {
+ if (it.second == value)
+ return true;
+ }
+
+ return false;
+}
+
+template<typename TKey, typename TValue>
+bool CheckSettingOptionsValidity(const TValue& value, const std::vector<TKey>& options)
+{
+ for (const auto& it : options)
+ {
+ if (it.value == value)
+ return true;
+ }
+
+ return false;
+}
+
+bool DeserializeOptionsSort(const TiXmlElement* optionsElement, SettingOptionsSort& optionsSort)
+{
+ optionsSort = SettingOptionsSort::NoSorting;
+
+ std::string sort;
+ if (optionsElement->QueryStringAttribute("sort", &sort) != TIXML_SUCCESS)
+ return true;
+
+ if (StringUtils::EqualsNoCase(sort, "false") || StringUtils::EqualsNoCase(sort, "off") ||
+ StringUtils::EqualsNoCase(sort, "no") || StringUtils::EqualsNoCase(sort, "disabled"))
+ optionsSort = SettingOptionsSort::NoSorting;
+ else if (StringUtils::EqualsNoCase(sort, "asc") || StringUtils::EqualsNoCase(sort, "ascending") ||
+ StringUtils::EqualsNoCase(sort, "true") || StringUtils::EqualsNoCase(sort, "on") ||
+ StringUtils::EqualsNoCase(sort, "yes") || StringUtils::EqualsNoCase(sort, "enabled"))
+ optionsSort = SettingOptionsSort::Ascending;
+ else if (StringUtils::EqualsNoCase(sort, "desc") || StringUtils::EqualsNoCase(sort, "descending"))
+ optionsSort = SettingOptionsSort::Descending;
+ else
+ return false;
+
+ return true;
+}
+
+Logger CSetting::s_logger;
+
+CSetting::CSetting(const std::string& id, CSettingsManager* settingsManager /* = nullptr */)
+ : ISetting(id, settingsManager)
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSetting");
+}
+
+CSetting::CSetting(const std::string& id, const CSetting& setting)
+ : CSetting(id, setting.m_settingsManager)
+{
+ Copy(setting);
+}
+
+void CSetting::MergeBasics(const CSetting& other)
+{
+ // ISetting
+ SetVisible(other.GetVisible());
+ SetLabel(other.GetLabel());
+ SetHelp(other.GetHelp());
+ SetRequirementsMet(other.MeetsRequirements());
+ // CSetting
+ SetEnabled(other.GetEnabled());
+ SetParent(other.GetParent());
+ SetLevel(other.GetLevel());
+ SetControl(const_cast<CSetting&>(other).GetControl());
+ SetDependencies(other.GetDependencies());
+}
+
+bool CSetting::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ // handle <visible> conditions
+ if (!ISetting::Deserialize(node, update))
+ return false;
+
+ auto element = node->ToElement();
+ if (element == nullptr)
+ return false;
+
+ auto parentSetting = element->Attribute(SETTING_XML_ATTR_PARENT);
+ if (parentSetting != nullptr)
+ m_parentSetting = parentSetting;
+
+ // get <enable>
+ bool value;
+ if (XMLUtils::GetBoolean(node, SETTING_XML_ELM_ENABLED, value))
+ m_enabled = value;
+
+ // get the <level>
+ int level = -1;
+ if (XMLUtils::GetInt(node, SETTING_XML_ELM_LEVEL, level))
+ m_level = static_cast<SettingLevel>(level);
+
+ if (m_level < SettingLevel::Basic || m_level > SettingLevel::Internal)
+ m_level = SettingLevel::Standard;
+
+ auto dependencies = node->FirstChild(SETTING_XML_ELM_DEPENDENCIES);
+ if (dependencies != nullptr)
+ {
+ auto dependencyNode = dependencies->FirstChild(SETTING_XML_ELM_DEPENDENCY);
+ while (dependencyNode != nullptr)
+ {
+ CSettingDependency dependency(m_settingsManager);
+ if (dependency.Deserialize(dependencyNode))
+ m_dependencies.push_back(dependency);
+ else
+ s_logger->warn("error reading <{}> tag of \"{}\"", SETTING_XML_ELM_DEPENDENCY, m_id);
+
+ dependencyNode = dependencyNode->NextSibling(SETTING_XML_ELM_DEPENDENCY);
+ }
+ }
+
+ auto control = node->FirstChildElement(SETTING_XML_ELM_CONTROL);
+ if (control != nullptr)
+ {
+ auto controlType = control->Attribute(SETTING_XML_ATTR_TYPE);
+ if (controlType == nullptr)
+ {
+ s_logger->error("error reading \"{}\" attribute of <control> tag of \"{}\"",
+ SETTING_XML_ATTR_TYPE, m_id);
+ return false;
+ }
+
+ m_control = m_settingsManager->CreateControl(controlType);
+ if (m_control == nullptr || !m_control->Deserialize(control, update))
+ {
+ s_logger->error("error reading <{}> tag of \"{}\"", SETTING_XML_ELM_CONTROL, m_id);
+ return false;
+ }
+ }
+ else if (!update && m_level < SettingLevel::Internal && !IsReference())
+ {
+ s_logger->error("missing <{}> tag of \"{}\"", SETTING_XML_ELM_CONTROL, m_id);
+ return false;
+ }
+
+ auto updates = node->FirstChild(SETTING_XML_ELM_UPDATES);
+ if (updates != nullptr)
+ {
+ auto updateElem = updates->FirstChildElement(SETTING_XML_ELM_UPDATE);
+ while (updateElem != nullptr)
+ {
+ CSettingUpdate settingUpdate;
+ if (settingUpdate.Deserialize(updateElem))
+ {
+ if (!m_updates.insert(settingUpdate).second)
+ s_logger->warn("duplicate <{}> definition for \"{}\"", SETTING_XML_ELM_UPDATE, m_id);
+ }
+ else
+ s_logger->warn("error reading <{}> tag of \"{}\"", SETTING_XML_ELM_UPDATE, m_id);
+
+ updateElem = updateElem->NextSiblingElement(SETTING_XML_ELM_UPDATE);
+ }
+ }
+
+ return true;
+}
+
+bool CSetting::IsEnabled() const
+{
+ if (m_dependencies.empty() && m_parentSetting.empty())
+ return m_enabled;
+
+ // if the setting has a parent setting and that parent setting is disabled
+ // the setting should automatically also be disabled
+ if (!m_parentSetting.empty())
+ {
+ SettingPtr parentSetting = m_settingsManager->GetSetting(m_parentSetting);
+ if (parentSetting != nullptr && !parentSetting->IsEnabled())
+ return false;
+ }
+
+ bool enabled = m_enabled;
+ for (const auto& dep : m_dependencies)
+ {
+ if (dep.GetType() != SettingDependencyType::Enable)
+ continue;
+
+ if (!dep.Check())
+ {
+ enabled = false;
+ break;
+ }
+ }
+
+ return enabled;
+}
+
+void CSetting::SetEnabled(bool enabled)
+{
+ if (!m_dependencies.empty() || m_enabled == enabled)
+ return;
+
+ m_enabled = enabled;
+ OnSettingPropertyChanged(shared_from_this(), "enabled");
+}
+
+void CSetting::MakeReference(const std::string& referencedId /* = "" */)
+{
+ auto tmpReferencedId = referencedId;
+ if (referencedId.empty())
+ tmpReferencedId = m_id;
+
+ m_id = StringUtils::Format("#{}[{}]", tmpReferencedId, StringUtils::CreateUUID());
+ m_referencedId = tmpReferencedId;
+}
+
+bool CSetting::IsVisible() const
+{
+ if (!ISetting::IsVisible())
+ return false;
+
+ bool visible = true;
+ for (const auto& dep : m_dependencies)
+ {
+ if (dep.GetType() != SettingDependencyType::Visible)
+ continue;
+
+ if (!dep.Check())
+ {
+ visible = false;
+ break;
+ }
+ }
+
+ return visible;
+}
+
+bool CSetting::OnSettingChanging(const std::shared_ptr<const CSetting>& setting)
+{
+ if (m_callback == nullptr)
+ return true;
+
+ return m_callback->OnSettingChanging(setting);
+}
+
+void CSetting::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (m_callback == nullptr)
+ return;
+
+ m_callback->OnSettingChanged(setting);
+}
+
+void CSetting::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
+{
+ if (m_callback == nullptr)
+ return;
+
+ m_callback->OnSettingAction(setting);
+}
+
+bool CSetting::DeserializeIdentification(const TiXmlNode* node,
+ std::string& identification,
+ bool& isReference)
+{
+ isReference = false;
+
+ // first check if we can simply retrieve the setting's identifier
+ if (ISetting::DeserializeIdentification(node, identification))
+ return true;
+
+ // otherwise try to retrieve a reference to another setting's identifier
+ if (!DeserializeIdentificationFromAttribute(node, SETTING_XML_ATTR_REFERENCE, identification))
+ return false;
+
+ isReference = true;
+ return true;
+}
+
+bool CSetting::OnSettingUpdate(const std::shared_ptr<CSetting>& setting,
+ const char* oldSettingId,
+ const TiXmlNode* oldSettingNode)
+{
+ if (m_callback == nullptr)
+ return false;
+
+ return m_callback->OnSettingUpdate(setting, oldSettingId, oldSettingNode);
+}
+
+void CSetting::OnSettingPropertyChanged(const std::shared_ptr<const CSetting>& setting,
+ const char* propertyName)
+{
+ if (m_callback == nullptr)
+ return;
+
+ m_callback->OnSettingPropertyChanged(setting, propertyName);
+}
+
+void CSetting::Copy(const CSetting &setting)
+{
+ SetVisible(setting.IsVisible());
+ SetLabel(setting.GetLabel());
+ SetHelp(setting.GetHelp());
+ SetRequirementsMet(setting.MeetsRequirements());
+ m_callback = setting.m_callback;
+ m_level = setting.m_level;
+
+ if (setting.m_control != nullptr)
+ {
+ m_control = m_settingsManager->CreateControl(setting.m_control->GetType());
+ *m_control = *setting.m_control;
+ }
+ else
+ m_control = nullptr;
+
+ m_dependencies = setting.m_dependencies;
+ m_updates = setting.m_updates;
+ m_changed = setting.m_changed;
+}
+
+Logger CSettingList::s_logger;
+
+CSettingList::CSettingList(const std::string& id,
+ std::shared_ptr<CSetting> settingDefinition,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSetting(id, settingsManager), m_definition(std::move(settingDefinition))
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingList");
+}
+
+CSettingList::CSettingList(const std::string& id,
+ std::shared_ptr<CSetting> settingDefinition,
+ int label,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingList(id, std::move(settingDefinition), settingsManager)
+{
+ SetLabel(label);
+}
+
+CSettingList::CSettingList(const std::string &id, const CSettingList &setting)
+ : CSetting(id, setting)
+{
+ copy(setting);
+}
+
+SettingPtr CSettingList::Clone(const std::string &id) const
+{
+ if (m_definition == nullptr)
+ return nullptr;
+
+ return std::make_shared<CSettingList>(id, *this);
+}
+
+void CSettingList::MergeDetails(const CSetting& other)
+{
+ if (other.GetType() != SettingType::List)
+ return;
+
+ const auto& listSetting = static_cast<const CSettingList&>(other);
+ if (m_definition == nullptr && listSetting.m_definition != nullptr)
+ m_definition = listSetting.m_definition;
+ if (m_defaults.empty() && !listSetting.m_defaults.empty())
+ m_defaults = listSetting.m_defaults;
+ if (m_values.empty() && !listSetting.m_values.empty())
+ m_values = listSetting.m_values;
+ if (m_delimiter == "|" && listSetting.m_delimiter != "|")
+ m_delimiter = listSetting.m_delimiter;
+ if (m_minimumItems == 0 && listSetting.m_minimumItems != 0)
+ m_minimumItems = listSetting.m_minimumItems;
+ if (m_maximumItems == -1 && listSetting.m_maximumItems != -1)
+ m_maximumItems = listSetting.m_maximumItems;
+}
+
+bool CSettingList::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (m_definition == nullptr)
+ return false;
+
+ if (!CSetting::Deserialize(node, update))
+ return false;
+
+ auto element = node->ToElement();
+ if (element == nullptr)
+ {
+ s_logger->warn("unable to read type of list setting of {}", m_id);
+ return false;
+ }
+
+ // deserialize the setting definition in update mode because we don't care
+ // about an invalid <default> value (which is never used)
+ if (!m_definition->Deserialize(node, true))
+ return false;
+
+ auto constraints = node->FirstChild(SETTING_XML_ELM_CONSTRAINTS);
+ if (constraints != nullptr)
+ {
+ // read the delimiter
+ std::string delimiter;
+ if (XMLUtils::GetString(constraints, SETTING_XML_ELM_DELIMITER, delimiter) && !delimiter.empty())
+ m_delimiter = delimiter;
+
+ XMLUtils::GetInt(constraints, SETTING_XML_ELM_MINIMUM_ITEMS, m_minimumItems);
+ if (m_minimumItems < 0)
+ m_minimumItems = 0;
+ XMLUtils::GetInt(constraints, SETTING_XML_ELM_MAXIMUM_ITEMS, m_maximumItems);
+ if (m_maximumItems <= 0)
+ m_maximumItems = -1;
+ else if (m_maximumItems < m_minimumItems)
+ {
+ s_logger->warn("invalid <{}> ({}) and/or <{}> ({}) of {}", SETTING_XML_ELM_MINIMUM_ITEMS,
+ m_minimumItems, SETTING_XML_ELM_MAXIMUM_ITEMS, m_maximumItems, m_id);
+ return false;
+ }
+ }
+
+ // read the default and initial values
+ std::string values;
+ if (XMLUtils::GetString(node, SETTING_XML_ELM_DEFAULT, values))
+ {
+ if (!fromString(values, m_defaults))
+ {
+ s_logger->warn("invalid <{}> definition \"{}\" of {}", SETTING_XML_ELM_DEFAULT, values, m_id);
+ return false;
+ }
+ Reset();
+ }
+
+ return true;
+}
+
+SettingType CSettingList::GetElementType() const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+
+ if (m_definition == nullptr)
+ return SettingType::Unknown;
+
+ return m_definition->GetType();
+}
+
+bool CSettingList::FromString(const std::string &value)
+{
+ SettingList values;
+ if (!fromString(value, values))
+ return false;
+
+ return SetValue(values);
+}
+
+std::string CSettingList::ToString() const
+{
+ return toString(m_values);
+}
+
+bool CSettingList::Equals(const std::string &value) const
+{
+ SettingList values;
+ if (!fromString(value, values) || values.size() != m_values.size())
+ return false;
+
+ bool ret = true;
+ for (size_t index = 0; index < values.size(); index++)
+ {
+ if (!m_values[index]->Equals(values[index]->ToString()))
+ {
+ ret = false;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+bool CSettingList::CheckValidity(const std::string &value) const
+{
+ SettingList values;
+ return fromString(value, values);
+}
+
+void CSettingList::Reset()
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ SettingList values;
+ for (const auto& it : m_defaults)
+ values.push_back(it->Clone(it->GetId()));
+
+ SetValue(values);
+}
+
+bool CSettingList::FromString(const std::vector<std::string> &value)
+{
+ SettingList values;
+ if (!fromValues(value, values))
+ return false;
+
+ return SetValue(values);
+}
+
+bool CSettingList::SetValue(const SettingList &values)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if ((int)values.size() < m_minimumItems ||
+ (m_maximumItems > 0 && (int)values.size() > m_maximumItems))
+ return false;
+
+ bool equal = values.size() == m_values.size();
+ for (size_t index = 0; index < values.size(); index++)
+ {
+ if (values[index]->GetType() != GetElementType())
+ return false;
+
+ if (equal &&
+ !values[index]->Equals(m_values[index]->ToString()))
+ equal = false;
+ }
+
+ if (equal)
+ return true;
+
+ SettingList oldValues = m_values;
+ m_values.clear();
+ m_values.insert(m_values.begin(), values.begin(), values.end());
+
+ if (!OnSettingChanging(shared_from_base<CSettingList>()))
+ {
+ m_values = oldValues;
+
+ // the setting couldn't be changed because one of the
+ // callback handlers failed the OnSettingChanging()
+ // callback so we need to let all the callback handlers
+ // know that the setting hasn't changed
+ OnSettingChanging(shared_from_base<CSettingList>());
+ return false;
+ }
+
+ m_changed = toString(m_values) != toString(m_defaults);
+ OnSettingChanged(shared_from_base<CSettingList>());
+ return true;
+}
+
+void CSettingList::SetDefault(const SettingList &values)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ m_defaults.clear();
+ m_defaults.insert(m_defaults.begin(), values.begin(), values.end());
+
+ if (!m_changed)
+ {
+ m_values.clear();
+ for (const auto& it : m_defaults)
+ m_values.push_back(it->Clone(it->GetId()));
+ }
+}
+
+void CSettingList::copy(const CSettingList &setting)
+{
+ CSetting::Copy(setting);
+
+ copy(setting.m_values, m_values);
+ copy(setting.m_defaults, m_defaults);
+
+ if (setting.m_definition != nullptr)
+ {
+ auto definitionCopy = setting.m_definition->Clone(m_id + ".definition");
+ if (definitionCopy != nullptr)
+ m_definition = definitionCopy;
+ }
+
+ m_delimiter = setting.m_delimiter;
+ m_minimumItems = setting.m_minimumItems;
+ m_maximumItems = setting.m_maximumItems;
+}
+
+void CSettingList::copy(const SettingList &srcValues, SettingList &dstValues)
+{
+ dstValues.clear();
+
+ for (const auto& value : srcValues)
+ {
+ if (value == nullptr)
+ continue;
+
+ SettingPtr valueCopy = value->Clone(value->GetId());
+ if (valueCopy == nullptr)
+ continue;
+
+ dstValues.emplace_back(valueCopy);
+ }
+}
+
+bool CSettingList::fromString(const std::string &strValue, SettingList &values) const
+{
+ return fromValues(StringUtils::Split(strValue, m_delimiter), values);
+}
+
+bool CSettingList::fromValues(const std::vector<std::string> &strValues, SettingList &values) const
+{
+ if ((int)strValues.size() < m_minimumItems ||
+ (m_maximumItems > 0 && (int)strValues.size() > m_maximumItems))
+ return false;
+
+ bool ret = true;
+ int index = 0;
+ for (const auto& value : strValues)
+ {
+ auto settingValue = m_definition->Clone(StringUtils::Format("{}.{}", m_id, index++));
+ if (settingValue == nullptr ||
+ !settingValue->FromString(value))
+ {
+ ret = false;
+ break;
+ }
+
+ values.emplace_back(settingValue);
+ }
+
+ if (!ret)
+ values.clear();
+
+ return ret;
+}
+
+std::string CSettingList::toString(const SettingList &values) const
+{
+ std::vector<std::string> strValues;
+ for (const auto& value : values)
+ {
+ if (value != nullptr)
+ strValues.push_back(value->ToString());
+ }
+
+ return StringUtils::Join(strValues, m_delimiter);
+}
+
+Logger CSettingBool::s_logger;
+
+CSettingBool::CSettingBool(const std::string& id, CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingBool(id, DefaultLabel, DefaultValue, settingsManager)
+{
+}
+
+CSettingBool::CSettingBool(const std::string& id, const CSettingBool& setting)
+ : CSettingBool(id, setting.m_settingsManager)
+{
+ copy(setting);
+}
+
+CSettingBool::CSettingBool(const std::string& id,
+ int label,
+ bool value,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CTraitedSetting(id, settingsManager), m_value(value), m_default(value)
+{
+ SetLabel(label);
+
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingBool");
+}
+
+SettingPtr CSettingBool::Clone(const std::string &id) const
+{
+ return std::make_shared<CSettingBool>(id, *this);
+}
+
+void CSettingBool::MergeDetails(const CSetting& other)
+{
+ if (other.GetType() != SettingType::Boolean)
+ return;
+
+ const auto& boolSetting = static_cast<const CSettingBool&>(other);
+ if (m_default == false && boolSetting.m_default == true)
+ m_default = boolSetting.m_default;
+ if (m_value == m_default && boolSetting.m_value != m_default)
+ m_value = boolSetting.m_value;
+}
+
+bool CSettingBool::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (!CSetting::Deserialize(node, update))
+ return false;
+
+ // get the default value
+ bool value;
+ if (XMLUtils::GetBoolean(node, SETTING_XML_ELM_DEFAULT, value))
+ m_value = m_default = value;
+ else if (!update)
+ {
+ s_logger->error("error reading the default value of \"{}\"", m_id);
+ return false;
+ }
+
+ return true;
+}
+
+bool CSettingBool::FromString(const std::string &value)
+{
+ bool bValue;
+ if (!fromString(value, bValue))
+ return false;
+
+ return SetValue(bValue);
+}
+
+std::string CSettingBool::ToString() const
+{
+ return m_value ? "true" : "false";
+}
+
+bool CSettingBool::Equals(const std::string &value) const
+{
+ bool bValue;
+ return (fromString(value, bValue) && m_value == bValue);
+}
+
+bool CSettingBool::CheckValidity(const std::string &value) const
+{
+ bool bValue;
+ return fromString(value, bValue);
+}
+
+bool CSettingBool::SetValue(bool value)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (value == m_value)
+ return true;
+
+ bool oldValue = m_value;
+ m_value = value;
+
+ if (!OnSettingChanging(shared_from_base<CSettingBool>()))
+ {
+ m_value = oldValue;
+
+ // the setting couldn't be changed because one of the
+ // callback handlers failed the OnSettingChanging()
+ // callback so we need to let all the callback handlers
+ // know that the setting hasn't changed
+ OnSettingChanging(shared_from_base<CSettingBool>());
+ return false;
+ }
+
+ m_changed = m_value != m_default;
+ OnSettingChanged(shared_from_base<CSettingBool>());
+ return true;
+}
+
+void CSettingBool::SetDefault(bool value)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ m_default = value;
+ if (!m_changed)
+ m_value = m_default;
+}
+
+void CSettingBool::copy(const CSettingBool &setting)
+{
+ CSetting::Copy(setting);
+
+ m_value = setting.m_value;
+ m_default = setting.m_default;
+}
+
+bool CSettingBool::fromString(const std::string &strValue, bool &value) const
+{
+ if (StringUtils::EqualsNoCase(strValue, "true"))
+ {
+ value = true;
+ return true;
+ }
+ if (StringUtils::EqualsNoCase(strValue, "false"))
+ {
+ value = false;
+ return true;
+ }
+
+ return false;
+}
+
+Logger CSettingInt::s_logger;
+
+CSettingInt::CSettingInt(const std::string& id, CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingInt(id, DefaultLabel, DefaultValue, settingsManager)
+{ }
+
+CSettingInt::CSettingInt(const std::string& id, const CSettingInt& setting)
+ : CSettingInt(id, setting.m_settingsManager)
+{
+ copy(setting);
+}
+
+CSettingInt::CSettingInt(const std::string& id,
+ int label,
+ int value,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingInt(id, label, value, DefaultMin, DefaultStep, DefaultMax, settingsManager)
+{
+ SetLabel(label);
+}
+
+CSettingInt::CSettingInt(const std::string& id,
+ int label,
+ int value,
+ int minimum,
+ int step,
+ int maximum,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CTraitedSetting(id, settingsManager),
+ m_value(value),
+ m_default(value),
+ m_min(minimum),
+ m_step(step),
+ m_max(maximum)
+{
+ SetLabel(label);
+
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingInt");
+}
+
+CSettingInt::CSettingInt(const std::string& id,
+ int label,
+ int value,
+ const TranslatableIntegerSettingOptions& options,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingInt(id, label, value, settingsManager)
+{
+ SetTranslatableOptions(options);
+}
+
+SettingPtr CSettingInt::Clone(const std::string &id) const
+{
+ return std::make_shared<CSettingInt>(id, *this);
+}
+
+void CSettingInt::MergeDetails(const CSetting& other)
+{
+ if (other.GetType() != SettingType::Integer)
+ return;
+
+ const auto& intSetting = static_cast<const CSettingInt&>(other);
+ if (m_default == 0.0 && intSetting.m_default != 0.0)
+ m_default = intSetting.m_default;
+ if (m_value == m_default && intSetting.m_value != m_default)
+ m_value = intSetting.m_value;
+ if (m_min == 0.0 && intSetting.m_min != 0.0)
+ m_min = intSetting.m_min;
+ if (m_step == 1.0 && intSetting.m_step != 1.0)
+ m_step = intSetting.m_step;
+ if (m_max == 0.0 && intSetting.m_max != 0.0)
+ m_max = intSetting.m_max;
+ if (m_translatableOptions.empty() && !intSetting.m_translatableOptions.empty())
+ m_translatableOptions = intSetting.m_translatableOptions;
+ if (m_options.empty() && !intSetting.m_options.empty())
+ m_options = intSetting.m_options;
+ if (m_optionsFillerName.empty() && !intSetting.m_optionsFillerName.empty())
+ m_optionsFillerName = intSetting.m_optionsFillerName;
+ if (m_optionsFiller == nullptr && intSetting.m_optionsFiller != nullptr)
+ m_optionsFiller = intSetting.m_optionsFiller;
+ if (m_optionsFillerData == nullptr && intSetting.m_optionsFillerData != nullptr)
+ m_optionsFillerData = intSetting.m_optionsFillerData;
+ if (m_dynamicOptions.empty() && !intSetting.m_dynamicOptions.empty())
+ m_dynamicOptions = intSetting.m_dynamicOptions;
+ if (m_optionsSort == SettingOptionsSort::NoSorting &&
+ intSetting.m_optionsSort != SettingOptionsSort::NoSorting)
+ m_optionsSort = intSetting.m_optionsSort;
+}
+
+bool CSettingInt::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (!CSetting::Deserialize(node, update))
+ return false;
+
+ // get the default value
+ int value;
+ if (XMLUtils::GetInt(node, SETTING_XML_ELM_DEFAULT, value))
+ m_value = m_default = value;
+ else if (!update)
+ {
+ s_logger->error("error reading the default value of \"{}\"", m_id);
+ return false;
+ }
+
+ auto constraints = node->FirstChild(SETTING_XML_ELM_CONSTRAINTS);
+ if (constraints != nullptr)
+ {
+ // get the entries
+ auto options = constraints->FirstChildElement(SETTING_XML_ELM_OPTIONS);
+ if (options != nullptr && options->FirstChild() != nullptr)
+ {
+ if (!DeserializeOptionsSort(options, m_optionsSort))
+ s_logger->warn("invalid \"sort\" attribute of <" SETTING_XML_ELM_OPTIONS "> for \"{}\"",
+ m_id);
+
+ if (options->FirstChild()->Type() == TiXmlNode::TINYXML_TEXT)
+ {
+ m_optionsFillerName = options->FirstChild()->ValueStr();
+ if (!m_optionsFillerName.empty())
+ {
+ m_optionsFiller = reinterpret_cast<IntegerSettingOptionsFiller>(m_settingsManager->GetSettingOptionsFiller(shared_from_base<CSettingInt>()));
+ }
+ }
+ else
+ {
+ m_translatableOptions.clear();
+ auto optionElement = options->FirstChildElement(SETTING_XML_ELM_OPTION);
+ while (optionElement != nullptr)
+ {
+ TranslatableIntegerSettingOption entry;
+ if (optionElement->QueryIntAttribute(SETTING_XML_ATTR_LABEL, &entry.label) ==
+ TIXML_SUCCESS &&
+ entry.label > 0)
+ {
+ entry.value = strtol(optionElement->FirstChild()->Value(), nullptr, 10);
+ m_translatableOptions.push_back(entry);
+ }
+ else
+ {
+ std::string label;
+ if (optionElement->QueryStringAttribute(SETTING_XML_ATTR_LABEL, &label) ==
+ TIXML_SUCCESS)
+ {
+ int value = strtol(optionElement->FirstChild()->Value(), nullptr, 10);
+ m_options.emplace_back(label, value);
+ }
+ }
+
+ optionElement = optionElement->NextSiblingElement(SETTING_XML_ELM_OPTION);
+ }
+ }
+ }
+
+ // get minimum
+ XMLUtils::GetInt(constraints, SETTING_XML_ELM_MINIMUM, m_min);
+ // get step
+ XMLUtils::GetInt(constraints, SETTING_XML_ELM_STEP, m_step);
+ // get maximum
+ XMLUtils::GetInt(constraints, SETTING_XML_ELM_MAXIMUM, m_max);
+ }
+
+ return true;
+}
+
+bool CSettingInt::FromString(const std::string &value)
+{
+ int iValue;
+ if (!fromString(value, iValue))
+ return false;
+
+ return SetValue(iValue);
+}
+
+std::string CSettingInt::ToString() const
+{
+ std::ostringstream oss;
+ oss << m_value;
+
+ return oss.str();
+}
+
+bool CSettingInt::Equals(const std::string &value) const
+{
+ int iValue;
+ return (fromString(value, iValue) && m_value == iValue);
+}
+
+bool CSettingInt::CheckValidity(const std::string &value) const
+{
+ int iValue;
+ if (!fromString(value, iValue))
+ return false;
+
+ return CheckValidity(iValue);
+}
+
+bool CSettingInt::CheckValidity(int value) const
+{
+ if (!m_translatableOptions.empty())
+ {
+ if (!CheckSettingOptionsValidity(value, m_translatableOptions))
+ return false;
+ }
+ else if (!m_options.empty())
+ {
+ if (!CheckSettingOptionsValidity(value, m_options))
+ return false;
+ }
+ else if (m_optionsFillerName.empty() && m_optionsFiller == nullptr &&
+ m_min != m_max && (value < m_min || value > m_max))
+ return false;
+
+ return true;
+}
+
+bool CSettingInt::SetValue(int value)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (value == m_value)
+ return true;
+
+ if (!CheckValidity(value))
+ return false;
+
+ int oldValue = m_value;
+ m_value = value;
+
+ if (!OnSettingChanging(shared_from_base<CSettingInt>()))
+ {
+ m_value = oldValue;
+
+ // the setting couldn't be changed because one of the
+ // callback handlers failed the OnSettingChanging()
+ // callback so we need to let all the callback handlers
+ // know that the setting hasn't changed
+ OnSettingChanging(shared_from_base<CSettingInt>());
+ return false;
+ }
+
+ m_changed = m_value != m_default;
+ OnSettingChanged(shared_from_base<CSettingInt>());
+ return true;
+}
+
+void CSettingInt::SetDefault(int value)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ m_default = value;
+ if (!m_changed)
+ m_value = m_default;
+}
+
+SettingOptionsType CSettingInt::GetOptionsType() const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ if (!m_translatableOptions.empty())
+ return SettingOptionsType::StaticTranslatable;
+ if (!m_options.empty())
+ return SettingOptionsType::Static;
+ if (!m_optionsFillerName.empty() || m_optionsFiller != nullptr)
+ return SettingOptionsType::Dynamic;
+
+ return SettingOptionsType::Unknown;
+}
+
+IntegerSettingOptions CSettingInt::UpdateDynamicOptions()
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ IntegerSettingOptions options;
+ if (m_optionsFiller == nullptr &&
+ (m_optionsFillerName.empty() || m_settingsManager == nullptr))
+ return options;
+
+ if (m_optionsFiller == nullptr)
+ {
+ m_optionsFiller = reinterpret_cast<IntegerSettingOptionsFiller>(m_settingsManager->GetSettingOptionsFiller(shared_from_base<CSettingInt>()));
+ if (m_optionsFiller == nullptr)
+ {
+ s_logger->warn("unknown options filler \"{}\" of \"{}\"", m_optionsFillerName, m_id);
+ return options;
+ }
+ }
+
+ int bestMatchingValue = m_value;
+ m_optionsFiller(shared_from_base<CSettingInt>(), options, bestMatchingValue, m_optionsFillerData);
+
+ if (bestMatchingValue != m_value)
+ SetValue(bestMatchingValue);
+
+ bool changed = m_dynamicOptions.size() != options.size();
+ if (!changed)
+ {
+ for (size_t index = 0; index < options.size(); index++)
+ {
+ if (options[index].label.compare(m_dynamicOptions[index].label) != 0 ||
+ options[index].value != m_dynamicOptions[index].value)
+ {
+ changed = true;
+ break;
+ }
+ }
+ }
+
+ if (changed)
+ {
+ m_dynamicOptions = options;
+ OnSettingPropertyChanged(shared_from_base<CSettingInt>(), "options");
+ }
+
+ return options;
+}
+
+void CSettingInt::copy(const CSettingInt &setting)
+{
+ CSetting::Copy(setting);
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ m_value = setting.m_value;
+ m_default = setting.m_default;
+ m_min = setting.m_min;
+ m_step = setting.m_step;
+ m_max = setting.m_max;
+ m_translatableOptions = setting.m_translatableOptions;
+ m_options = setting.m_options;
+ m_optionsFillerName = setting.m_optionsFillerName;
+ m_optionsFiller = setting.m_optionsFiller;
+ m_optionsFillerData = setting.m_optionsFillerData;
+ m_dynamicOptions = setting.m_dynamicOptions;
+}
+
+bool CSettingInt::fromString(const std::string &strValue, int &value)
+{
+ if (strValue.empty())
+ return false;
+
+ char *end = nullptr;
+ value = (int)strtol(strValue.c_str(), &end, 10);
+ if (end != nullptr && *end != '\0')
+ return false;
+
+ return true;
+}
+
+Logger CSettingNumber::s_logger;
+
+CSettingNumber::CSettingNumber(const std::string& id,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingNumber(id, DefaultLabel, DefaultValue, settingsManager)
+{ }
+
+CSettingNumber::CSettingNumber(const std::string& id, const CSettingNumber& setting)
+ : CSettingNumber(id, setting.m_settingsManager)
+{
+ copy(setting);
+}
+
+CSettingNumber::CSettingNumber(const std::string& id,
+ int label,
+ float value,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingNumber(id, label, value, DefaultMin, DefaultStep, DefaultMax, settingsManager)
+{
+}
+
+CSettingNumber::CSettingNumber(const std::string& id,
+ int label,
+ float value,
+ float minimum,
+ float step,
+ float maximum,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CTraitedSetting(id, settingsManager),
+ m_value(static_cast<double>(value)),
+ m_default(static_cast<double>(value)),
+ m_min(static_cast<double>(minimum)),
+ m_step(static_cast<double>(step)),
+ m_max(static_cast<double>(maximum))
+{
+ SetLabel(label);
+
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingNumber");
+}
+
+SettingPtr CSettingNumber::Clone(const std::string &id) const
+{
+ return std::make_shared<CSettingNumber>(id, *this);
+}
+
+void CSettingNumber::MergeDetails(const CSetting& other)
+{
+ if (other.GetType() != SettingType::Number)
+ return;
+
+ const auto& numberSetting = static_cast<const CSettingNumber&>(other);
+ if (m_default == 0.0 && numberSetting.m_default != 0.0)
+ m_default = numberSetting.m_default;
+ if (m_value == m_default && numberSetting.m_value != m_default)
+ m_value = numberSetting.m_value;
+ if (m_min == 0.0 && numberSetting.m_min != 0.0)
+ m_min = numberSetting.m_min;
+ if (m_step == 1.0 && numberSetting.m_step != 1.0)
+ m_step = numberSetting.m_step;
+ if (m_max == 0.0 && numberSetting.m_max != 0.0)
+ m_max = numberSetting.m_max;
+}
+
+bool CSettingNumber::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (!CSetting::Deserialize(node, update))
+ return false;
+
+ // get the default value
+ double value;
+ if (XMLUtils::GetDouble(node, SETTING_XML_ELM_DEFAULT, value))
+ m_value = m_default = value;
+ else if (!update)
+ {
+ s_logger->error("error reading the default value of \"{}\"", m_id);
+ return false;
+ }
+
+ auto constraints = node->FirstChild(SETTING_XML_ELM_CONSTRAINTS);
+ if (constraints != nullptr)
+ {
+ // get the minimum value
+ XMLUtils::GetDouble(constraints, SETTING_XML_ELM_MINIMUM, m_min);
+ // get the step value
+ XMLUtils::GetDouble(constraints, SETTING_XML_ELM_STEP, m_step);
+ // get the maximum value
+ XMLUtils::GetDouble(constraints, SETTING_XML_ELM_MAXIMUM, m_max);
+ }
+
+ return true;
+}
+
+bool CSettingNumber::FromString(const std::string &value)
+{
+ double dValue;
+ if (!fromString(value, dValue))
+ return false;
+
+ return SetValue(dValue);
+}
+
+std::string CSettingNumber::ToString() const
+{
+ std::ostringstream oss;
+ oss << m_value;
+
+ return oss.str();
+}
+
+bool CSettingNumber::Equals(const std::string &value) const
+{
+ double dValue;
+ std::shared_lock<CSharedSection> lock(m_critical);
+ return (fromString(value, dValue) && m_value == dValue);
+}
+
+bool CSettingNumber::CheckValidity(const std::string &value) const
+{
+ double dValue;
+ if (!fromString(value, dValue))
+ return false;
+
+ return CheckValidity(dValue);
+}
+
+bool CSettingNumber::CheckValidity(double value) const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ if (m_min != m_max &&
+ (value < m_min || value > m_max))
+ return false;
+
+ return true;
+}
+
+bool CSettingNumber::SetValue(double value)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (value == m_value)
+ return true;
+
+ if (!CheckValidity(value))
+ return false;
+
+ double oldValue = m_value;
+ m_value = value;
+
+ if (!OnSettingChanging(shared_from_base<CSettingNumber>()))
+ {
+ m_value = oldValue;
+
+ // the setting couldn't be changed because one of the
+ // callback handlers failed the OnSettingChanging()
+ // callback so we need to let all the callback handlers
+ // know that the setting hasn't changed
+ OnSettingChanging(shared_from_base<CSettingNumber>());
+ return false;
+ }
+
+ m_changed = m_value != m_default;
+ OnSettingChanged(shared_from_base<CSettingNumber>());
+ return true;
+}
+
+void CSettingNumber::SetDefault(double value)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ m_default = value;
+ if (!m_changed)
+ m_value = m_default;
+}
+
+void CSettingNumber::copy(const CSettingNumber &setting)
+{
+ CSetting::Copy(setting);
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ m_value = setting.m_value;
+ m_default = setting.m_default;
+ m_min = setting.m_min;
+ m_step = setting.m_step;
+ m_max = setting.m_max;
+}
+
+bool CSettingNumber::fromString(const std::string &strValue, double &value)
+{
+ if (strValue.empty())
+ return false;
+
+ char *end = nullptr;
+ value = strtod(strValue.c_str(), &end);
+ if (end != nullptr && *end != '\0')
+ return false;
+
+ return true;
+}
+
+const CSettingString::Value CSettingString::DefaultValue;
+Logger CSettingString::s_logger;
+
+CSettingString::CSettingString(const std::string& id,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingString(id, DefaultLabel, DefaultValue, settingsManager)
+{ }
+
+CSettingString::CSettingString(const std::string& id, const CSettingString& setting)
+ : CSettingString(id, setting.m_settingsManager)
+{
+ copy(setting);
+}
+
+CSettingString::CSettingString(const std::string& id,
+ int label,
+ const std::string& value,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CTraitedSetting(id, settingsManager), m_value(value), m_default(value)
+{
+ SetLabel(label);
+
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingString");
+}
+
+SettingPtr CSettingString::Clone(const std::string &id) const
+{
+ return std::make_shared<CSettingString>(id, *this);
+}
+
+void CSettingString::MergeDetails(const CSetting& other)
+{
+ if (other.GetType() != SettingType::String)
+ return;
+
+ const auto& stringSetting = static_cast<const CSettingString&>(other);
+ if (m_default.empty() && !stringSetting.m_default.empty())
+ m_default = stringSetting.m_default;
+ if (m_value == m_default && stringSetting.m_value != m_default)
+ m_value = stringSetting.m_value;
+ if (m_allowEmpty == false && stringSetting.m_allowEmpty == true)
+ m_allowEmpty = stringSetting.m_allowEmpty;
+ if (m_allowNewOption == false && stringSetting.m_allowNewOption == true)
+ m_allowNewOption = stringSetting.m_allowNewOption;
+ if (m_translatableOptions.empty() && !stringSetting.m_translatableOptions.empty())
+ m_translatableOptions = stringSetting.m_translatableOptions;
+ if (m_options.empty() && !stringSetting.m_options.empty())
+ m_options = stringSetting.m_options;
+ if (m_optionsFillerName.empty() && !stringSetting.m_optionsFillerName.empty())
+ m_optionsFillerName = stringSetting.m_optionsFillerName;
+ if (m_optionsFiller == nullptr && stringSetting.m_optionsFiller != nullptr)
+ m_optionsFiller = stringSetting.m_optionsFiller;
+ if (m_optionsFillerData == nullptr && stringSetting.m_optionsFillerData != nullptr)
+ m_optionsFillerData = stringSetting.m_optionsFillerData;
+ if (m_dynamicOptions.empty() && !stringSetting.m_dynamicOptions.empty())
+ m_dynamicOptions = stringSetting.m_dynamicOptions;
+ if (m_optionsSort == SettingOptionsSort::NoSorting &&
+ stringSetting.m_optionsSort != SettingOptionsSort::NoSorting)
+ m_optionsSort = stringSetting.m_optionsSort;
+}
+
+bool CSettingString::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (!CSetting::Deserialize(node, update))
+ return false;
+
+ auto constraints = node->FirstChild(SETTING_XML_ELM_CONSTRAINTS);
+ if (constraints != nullptr)
+ {
+ // get allowempty (needs to be parsed before parsing the default value)
+ XMLUtils::GetBoolean(constraints, SETTING_XML_ELM_ALLOWEMPTY, m_allowEmpty);
+
+ // Values other than those in options constraints allowed to be added
+ XMLUtils::GetBoolean(constraints, SETTING_XML_ELM_ALLOWNEWOPTION, m_allowNewOption);
+
+ // get the entries
+ auto options = constraints->FirstChildElement(SETTING_XML_ELM_OPTIONS);
+ if (options != nullptr && options->FirstChild() != nullptr)
+ {
+ if (!DeserializeOptionsSort(options, m_optionsSort))
+ s_logger->warn("invalid \"sort\" attribute of <" SETTING_XML_ELM_OPTIONS "> for \"{}\"",
+ m_id);
+
+ if (options->FirstChild()->Type() == TiXmlNode::TINYXML_TEXT)
+ {
+ m_optionsFillerName = options->FirstChild()->ValueStr();
+ if (!m_optionsFillerName.empty())
+ {
+ m_optionsFiller = reinterpret_cast<StringSettingOptionsFiller>(m_settingsManager->GetSettingOptionsFiller(shared_from_base<CSettingString>()));
+ }
+ }
+ else
+ {
+ m_translatableOptions.clear();
+ auto optionElement = options->FirstChildElement(SETTING_XML_ELM_OPTION);
+ while (optionElement != nullptr)
+ {
+ TranslatableStringSettingOption entry;
+ if (optionElement->QueryIntAttribute(SETTING_XML_ATTR_LABEL, &entry.first) == TIXML_SUCCESS && entry.first > 0)
+ {
+ entry.second = optionElement->FirstChild()->Value();
+ m_translatableOptions.push_back(entry);
+ }
+ else
+ {
+ const std::string value = optionElement->FirstChild()->Value();
+ // if a specific "label" attribute is present use it otherwise use the value as label
+ std::string label = value;
+ optionElement->QueryStringAttribute(SETTING_XML_ATTR_LABEL, &label);
+
+ m_options.emplace_back(label, value);
+ }
+
+ optionElement = optionElement->NextSiblingElement(SETTING_XML_ELM_OPTION);
+ }
+ }
+ }
+ }
+
+ // get the default value
+ std::string value;
+ if (XMLUtils::GetString(node, SETTING_XML_ELM_DEFAULT, value) &&
+ (!value.empty() || m_allowEmpty))
+ m_value = m_default = value;
+ else if (!update && !m_allowEmpty)
+ {
+ s_logger->error("error reading the default value of \"{}\"", m_id);
+ return false;
+ }
+
+ return true;
+}
+
+bool CSettingString::CheckValidity(const std::string &value) const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ if (!m_allowEmpty && value.empty())
+ return false;
+
+ if (!m_translatableOptions.empty())
+ {
+ if (!CheckSettingOptionsValidity(value, m_translatableOptions))
+ return false;
+ }
+ else if (!m_options.empty() && !m_allowNewOption)
+ {
+ if (!CheckSettingOptionsValidity(value, m_options))
+ return false;
+ }
+
+ return true;
+}
+
+bool CSettingString::SetValue(const std::string &value)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (value == m_value)
+ return true;
+
+ if (!CheckValidity(value))
+ return false;
+
+ std::string oldValue = m_value;
+ m_value = value;
+
+ if (!OnSettingChanging(shared_from_base<CSettingString>()))
+ {
+ m_value = oldValue;
+
+ // the setting couldn't be changed because one of the
+ // callback handlers failed the OnSettingChanging()
+ // callback so we need to let all the callback handlers
+ // know that the setting hasn't changed
+ OnSettingChanging(shared_from_base<CSettingString>());
+ return false;
+ }
+
+ m_changed = m_value != m_default;
+ OnSettingChanged(shared_from_base<CSettingString>());
+ return true;
+}
+
+void CSettingString::SetDefault(const std::string &value)
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+
+ m_default = value;
+ if (!m_changed)
+ m_value = m_default;
+}
+
+SettingOptionsType CSettingString::GetOptionsType() const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ if (!m_translatableOptions.empty())
+ return SettingOptionsType::StaticTranslatable;
+ if (!m_options.empty())
+ return SettingOptionsType::Static;
+ if (!m_optionsFillerName.empty() || m_optionsFiller != nullptr)
+ return SettingOptionsType::Dynamic;
+
+ return SettingOptionsType::Unknown;
+}
+
+StringSettingOptions CSettingString::UpdateDynamicOptions()
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ StringSettingOptions options;
+ if (m_optionsFiller == nullptr &&
+ (m_optionsFillerName.empty() || m_settingsManager == nullptr))
+ return options;
+
+ if (m_optionsFiller == nullptr)
+ {
+ m_optionsFiller = reinterpret_cast<StringSettingOptionsFiller>(m_settingsManager->GetSettingOptionsFiller(shared_from_base<CSettingString>()));
+ if (m_optionsFiller == nullptr)
+ {
+ s_logger->error("unknown options filler \"{}\" of \"{}\"", m_optionsFillerName, m_id);
+ return options;
+ }
+ }
+
+ std::string bestMatchingValue = m_value;
+ m_optionsFiller(shared_from_base<CSettingString>(), options, bestMatchingValue, m_optionsFillerData);
+
+ if (bestMatchingValue != m_value)
+ SetValue(bestMatchingValue);
+
+ // check if the list of items has changed
+ bool changed = m_dynamicOptions.size() != options.size();
+ if (!changed)
+ {
+ for (size_t index = 0; index < options.size(); index++)
+ {
+ if (options[index].label.compare(m_dynamicOptions[index].label) != 0 ||
+ options[index].value.compare(m_dynamicOptions[index].value) != 0)
+ {
+ changed = true;
+ break;
+ }
+ }
+ }
+
+ if (changed)
+ {
+ m_dynamicOptions = options;
+ OnSettingPropertyChanged(shared_from_base<CSettingString>(), "options");
+ }
+
+ return options;
+}
+
+void CSettingString::copy(const CSettingString &setting)
+{
+ CSetting::Copy(setting);
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+ m_value = setting.m_value;
+ m_default = setting.m_default;
+ m_allowEmpty = setting.m_allowEmpty;
+ m_allowNewOption = setting.m_allowNewOption;
+ m_translatableOptions = setting.m_translatableOptions;
+ m_options = setting.m_options;
+ m_optionsFillerName = setting.m_optionsFillerName;
+ m_optionsFiller = setting.m_optionsFiller;
+ m_optionsFillerData = setting.m_optionsFillerData;
+ m_dynamicOptions = setting.m_dynamicOptions;
+}
+
+Logger CSettingAction::s_logger;
+
+CSettingAction::CSettingAction(const std::string& id,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingAction(id, DefaultLabel, settingsManager)
+{ }
+
+CSettingAction::CSettingAction(const std::string& id,
+ int label,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSetting(id, settingsManager)
+{
+ SetLabel(label);
+
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingAction");
+}
+
+CSettingAction::CSettingAction(const std::string& id, const CSettingAction& setting)
+ : CSettingAction(id, setting.m_settingsManager)
+{
+ copy(setting);
+}
+
+SettingPtr CSettingAction::Clone(const std::string &id) const
+{
+ return std::make_shared<CSettingAction>(id, *this);
+}
+
+void CSettingAction::MergeDetails(const CSetting& other)
+{
+ if (other.GetType() != SettingType::Action)
+ return;
+
+ const auto& actionSetting = static_cast<const CSettingAction&>(other);
+ if (!HasData() && actionSetting.HasData())
+ SetData(actionSetting.GetData());
+}
+
+bool CSettingAction::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+
+ if (!CSetting::Deserialize(node, update))
+ return false;
+
+ m_data = XMLUtils::GetString(node, SETTING_XML_ELM_DATA);
+
+ return true;
+}
+
+void CSettingAction::copy(const CSettingAction& setting)
+{
+ CSetting::Copy(setting);
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+ m_data = setting.m_data;
+}
diff --git a/xbmc/settings/lib/Setting.h b/xbmc/settings/lib/Setting.h
new file mode 100644
index 0000000..9e3ed3f
--- /dev/null
+++ b/xbmc/settings/lib/Setting.h
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "ISetting.h"
+#include "ISettingCallback.h"
+#include "ISettingControl.h"
+#include "SettingDefinitions.h"
+#include "SettingDependency.h"
+#include "SettingLevel.h"
+#include "SettingType.h"
+#include "SettingUpdate.h"
+#include "threads/SharedSection.h"
+#include "utils/logtypes.h"
+
+#include <memory>
+#include <set>
+#include <shared_mutex>
+#include <string>
+#include <utility>
+#include <vector>
+
+enum class SettingOptionsType {
+ Unknown = 0,
+ StaticTranslatable,
+ Static,
+ Dynamic
+};
+
+class CSetting;
+using SettingPtr = std::shared_ptr<CSetting>;
+using SettingConstPtr = std::shared_ptr<const CSetting>;
+using SettingList = std::vector<SettingPtr>;
+
+/*!
+ \ingroup settings
+ \brief Setting base class containing all the properties which are common to
+ all settings independent of the setting type.
+ */
+class CSetting : public ISetting,
+ protected ISettingCallback,
+ public std::enable_shared_from_this<CSetting>
+{
+public:
+ CSetting(const std::string& id, CSettingsManager* settingsManager = nullptr);
+ CSetting(const std::string& id, const CSetting& setting);
+ ~CSetting() override = default;
+
+ virtual std::shared_ptr<CSetting> Clone(const std::string &id) const = 0;
+ void MergeBasics(const CSetting& other);
+ virtual void MergeDetails(const CSetting& other) = 0;
+
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ virtual SettingType GetType() const = 0;
+ virtual bool FromString(const std::string &value) = 0;
+ virtual std::string ToString() const = 0;
+ virtual bool Equals(const std::string &value) const = 0;
+ virtual bool CheckValidity(const std::string &value) const = 0;
+ virtual void Reset() = 0;
+
+ bool IsEnabled() const;
+ bool GetEnabled() const { return m_enabled; }
+ void SetEnabled(bool enabled);
+ bool IsDefault() const { return !m_changed; }
+ const std::string& GetParent() const { return m_parentSetting; }
+ void SetParent(const std::string& parentSetting) { m_parentSetting = parentSetting; }
+ SettingLevel GetLevel() const { return m_level; }
+ void SetLevel(SettingLevel level) { m_level = level; }
+ std::shared_ptr<const ISettingControl> GetControl() const { return m_control; }
+ std::shared_ptr<ISettingControl> GetControl() { return m_control; }
+ void SetControl(std::shared_ptr<ISettingControl> control) { m_control = std::move(control); }
+ const SettingDependencies& GetDependencies() const { return m_dependencies; }
+ void SetDependencies(const SettingDependencies &dependencies) { m_dependencies = dependencies; }
+ const std::set<CSettingUpdate>& GetUpdates() const { return m_updates; }
+
+ void SetCallback(ISettingCallback *callback) { m_callback = callback; }
+
+ bool IsReference() const { return !m_referencedId.empty(); }
+ const std::string& GetReferencedId() const { return m_referencedId; }
+ void SetReferencedId(const std::string& referencedId) { m_referencedId = referencedId; }
+ void MakeReference(const std::string& referencedId = "");
+
+ bool GetVisible() const { return ISetting::IsVisible(); }
+ // overrides of ISetting
+ bool IsVisible() const override;
+
+ // implementation of ISettingCallback
+ void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override;
+
+ /*!
+ \brief Deserializes the given XML node to retrieve a setting object's identifier and
+ whether the setting is a reference to another setting or not.
+
+ \param node XML node containing a setting object's identifier
+ \param identification Will contain the deserialized setting object's identifier
+ \param isReference Whether the setting is a reference to the setting with the determined identifier
+ \return True if a setting object's identifier was deserialized, false otherwise
+ */
+ static bool DeserializeIdentification(const TiXmlNode* node,
+ std::string& identification,
+ bool& isReference);
+
+protected:
+ // implementation of ISettingCallback
+ bool OnSettingChanging(const std::shared_ptr<const CSetting>& setting) override;
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+ bool OnSettingUpdate(const std::shared_ptr<CSetting>& setting,
+ const char* oldSettingId,
+ const TiXmlNode* oldSettingNode) override;
+ void OnSettingPropertyChanged(const std::shared_ptr<const CSetting>& setting,
+ const char* propertyName) override;
+
+ void Copy(const CSetting &setting);
+
+ template<class TSetting>
+ std::shared_ptr<TSetting> shared_from_base()
+ {
+ return std::static_pointer_cast<TSetting>(shared_from_this());
+ }
+
+ ISettingCallback *m_callback = nullptr;
+ bool m_enabled = true;
+ std::string m_parentSetting;
+ SettingLevel m_level = SettingLevel::Standard;
+ std::shared_ptr<ISettingControl> m_control;
+ SettingDependencies m_dependencies;
+ std::set<CSettingUpdate> m_updates;
+ bool m_changed = false;
+ mutable CSharedSection m_critical;
+
+ std::string m_referencedId;
+
+private:
+ static Logger s_logger;
+};
+
+template<typename TValue, SettingType TSettingType>
+class CTraitedSetting : public CSetting
+{
+public:
+ typedef TValue Value;
+
+ // implementation of CSetting
+ SettingType GetType() const override { return TSettingType; }
+
+ static SettingType Type() { return TSettingType; }
+
+protected:
+ CTraitedSetting(const std::string& id, CSettingsManager* settingsManager = nullptr)
+ : CSetting(id, settingsManager)
+ { }
+ CTraitedSetting(const std::string& id, const CTraitedSetting& setting) : CSetting(id, setting) {}
+ ~CTraitedSetting() override = default;
+};
+
+/*!
+ \ingroup settings
+ \brief List setting implementation
+ \sa CSetting
+ */
+class CSettingList : public CSetting
+{
+public:
+ CSettingList(const std::string &id, std::shared_ptr<CSetting> settingDefinition, CSettingsManager *settingsManager = nullptr);
+ CSettingList(const std::string &id, std::shared_ptr<CSetting> settingDefinition, int label, CSettingsManager *settingsManager = nullptr);
+ CSettingList(const std::string &id, const CSettingList &setting);
+ ~CSettingList() override = default;
+
+ std::shared_ptr<CSetting> Clone(const std::string &id) const override;
+ void MergeDetails(const CSetting& other) override;
+
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ SettingType GetType() const override { return SettingType::List; }
+ bool FromString(const std::string &value) override;
+ std::string ToString() const override;
+ bool Equals(const std::string &value) const override;
+ bool CheckValidity(const std::string &value) const override;
+ void Reset() override;
+
+ SettingType GetElementType() const;
+ std::shared_ptr<CSetting> GetDefinition() { return m_definition; }
+ std::shared_ptr<const CSetting> GetDefinition() const { return m_definition; }
+ void SetDefinition(std::shared_ptr<CSetting> definition) { m_definition = std::move(definition); }
+
+ const std::string& GetDelimiter() const { return m_delimiter; }
+ void SetDelimiter(const std::string &delimiter) { m_delimiter = delimiter; }
+ int GetMinimumItems() const { return m_minimumItems; }
+ void SetMinimumItems(int minimumItems) { m_minimumItems = minimumItems; }
+ int GetMaximumItems() const { return m_maximumItems; }
+ void SetMaximumItems(int maximumItems) { m_maximumItems = maximumItems; }
+
+ bool FromString(const std::vector<std::string> &value);
+
+ const SettingList& GetValue() const { return m_values; }
+ bool SetValue(const SettingList &values);
+ const SettingList& GetDefault() const { return m_defaults; }
+ void SetDefault(const SettingList &values);
+
+protected:
+ void copy(const CSettingList &setting);
+ static void copy(const SettingList &srcValues, SettingList &dstValues);
+ bool fromString(const std::string &strValue, SettingList &values) const;
+ bool fromValues(const std::vector<std::string> &strValues, SettingList &values) const;
+ std::string toString(const SettingList &values) const;
+
+ SettingList m_values;
+ SettingList m_defaults;
+ std::shared_ptr<CSetting> m_definition;
+ std::string m_delimiter = "|";
+ int m_minimumItems = 0;
+ int m_maximumItems = -1;
+
+ static Logger s_logger;
+};
+
+/*!
+ \ingroup settings
+ \brief Boolean setting implementation.
+ \sa CSetting
+ */
+class CSettingBool : public CTraitedSetting<bool, SettingType::Boolean>
+{
+public:
+ CSettingBool(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ CSettingBool(const std::string &id, const CSettingBool &setting);
+ CSettingBool(const std::string &id, int label, bool value, CSettingsManager *settingsManager = nullptr);
+ ~CSettingBool() override = default;
+
+ std::shared_ptr<CSetting> Clone(const std::string &id) const override;
+ void MergeDetails(const CSetting& other) override;
+
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ bool FromString(const std::string &value) override;
+ std::string ToString() const override;
+ bool Equals(const std::string &value) const override;
+ bool CheckValidity(const std::string &value) const override;
+ void Reset() override { SetValue(m_default); }
+
+ bool GetValue() const
+ {
+ std::shared_lock<CSharedSection> lock(m_critical);
+ return m_value;
+ }
+ bool SetValue(bool value);
+ bool GetDefault() const { return m_default; }
+ void SetDefault(bool value);
+
+private:
+ static constexpr Value DefaultValue = false;
+
+ void copy(const CSettingBool &setting);
+ bool fromString(const std::string &strValue, bool &value) const;
+
+ bool m_value = DefaultValue;
+ bool m_default = DefaultValue;
+
+ static Logger s_logger;
+};
+
+/*!
+ \ingroup settings
+ \brief Integer setting implementation
+ \sa CSetting
+ */
+class CSettingInt : public CTraitedSetting<int, SettingType::Integer>
+{
+public:
+ CSettingInt(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ CSettingInt(const std::string &id, const CSettingInt &setting);
+ CSettingInt(const std::string &id, int label, int value, CSettingsManager *settingsManager = nullptr);
+ CSettingInt(const std::string &id, int label, int value, int minimum, int step, int maximum, CSettingsManager *settingsManager = nullptr);
+ CSettingInt(const std::string &id, int label, int value, const TranslatableIntegerSettingOptions &options, CSettingsManager *settingsManager = nullptr);
+ ~CSettingInt() override = default;
+
+ std::shared_ptr<CSetting> Clone(const std::string &id) const override;
+ void MergeDetails(const CSetting& other) override;
+
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ bool FromString(const std::string &value) override;
+ std::string ToString() const override;
+ bool Equals(const std::string &value) const override;
+ bool CheckValidity(const std::string &value) const override;
+ virtual bool CheckValidity(int value) const;
+ void Reset() override { SetValue(m_default); }
+
+ int GetValue() const
+ {
+ std::shared_lock<CSharedSection> lock(m_critical);
+ return m_value;
+ }
+ bool SetValue(int value);
+ int GetDefault() const { return m_default; }
+ void SetDefault(int value);
+
+ int GetMinimum() const { return m_min; }
+ void SetMinimum(int minimum) { m_min = minimum; }
+ int GetStep() const { return m_step; }
+ void SetStep(int step) { m_step = step; }
+ int GetMaximum() const { return m_max; }
+ void SetMaximum(int maximum) { m_max = maximum; }
+
+ SettingOptionsType GetOptionsType() const;
+ const TranslatableIntegerSettingOptions& GetTranslatableOptions() const { return m_translatableOptions; }
+ void SetTranslatableOptions(const TranslatableIntegerSettingOptions &options) { m_translatableOptions = options; }
+ const IntegerSettingOptions& GetOptions() const { return m_options; }
+ void SetOptions(const IntegerSettingOptions &options) { m_options = options; }
+ const std::string& GetOptionsFillerName() const { return m_optionsFillerName; }
+ void SetOptionsFillerName(const std::string &optionsFillerName, void *data = nullptr)
+ {
+ m_optionsFillerName = optionsFillerName;
+ m_optionsFillerData = data;
+ }
+ void SetOptionsFiller(IntegerSettingOptionsFiller optionsFiller, void *data = nullptr)
+ {
+ m_optionsFiller = optionsFiller;
+ m_optionsFillerData = data;
+ }
+ IntegerSettingOptions GetDynamicOptions() const { return m_dynamicOptions; }
+ IntegerSettingOptions UpdateDynamicOptions();
+ SettingOptionsSort GetOptionsSort() const { return m_optionsSort; }
+ void SetOptionsSort(SettingOptionsSort optionsSort) { m_optionsSort = optionsSort; }
+
+private:
+ static constexpr Value DefaultValue = 0;
+ static constexpr Value DefaultMin = DefaultValue;
+ static constexpr Value DefaultStep = 1;
+ static constexpr Value DefaultMax = DefaultValue;
+
+ void copy(const CSettingInt &setting);
+ static bool fromString(const std::string &strValue, int &value);
+
+ int m_value = DefaultValue;
+ int m_default = DefaultValue;
+ int m_min = DefaultMin;
+ int m_step = DefaultStep;
+ int m_max = DefaultMax;
+ TranslatableIntegerSettingOptions m_translatableOptions;
+ IntegerSettingOptions m_options;
+ std::string m_optionsFillerName;
+ IntegerSettingOptionsFiller m_optionsFiller = nullptr;
+ void *m_optionsFillerData = nullptr;
+ IntegerSettingOptions m_dynamicOptions;
+ SettingOptionsSort m_optionsSort = SettingOptionsSort::NoSorting;
+
+ static Logger s_logger;
+};
+
+/*!
+ \ingroup settings
+ \brief Real number setting implementation.
+ \sa CSetting
+ */
+class CSettingNumber : public CTraitedSetting<double, SettingType::Number>
+{
+public:
+ CSettingNumber(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ CSettingNumber(const std::string &id, const CSettingNumber &setting);
+ CSettingNumber(const std::string &id, int label, float value, CSettingsManager *settingsManager = nullptr);
+ CSettingNumber(const std::string &id, int label, float value, float minimum, float step, float maximum, CSettingsManager *settingsManager = nullptr);
+ ~CSettingNumber() override = default;
+
+ std::shared_ptr<CSetting> Clone(const std::string &id) const override;
+ void MergeDetails(const CSetting& other) override;
+
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ bool FromString(const std::string &value) override;
+ std::string ToString() const override;
+ bool Equals(const std::string &value) const override;
+ bool CheckValidity(const std::string &value) const override;
+ virtual bool CheckValidity(double value) const;
+ void Reset() override { SetValue(m_default); }
+
+ double GetValue() const
+ {
+ std::shared_lock<CSharedSection> lock(m_critical);
+ return m_value;
+ }
+ bool SetValue(double value);
+ double GetDefault() const { return m_default; }
+ void SetDefault(double value);
+
+ double GetMinimum() const { return m_min; }
+ void SetMinimum(double minimum) { m_min = minimum; }
+ double GetStep() const { return m_step; }
+ void SetStep(double step) { m_step = step; }
+ double GetMaximum() const { return m_max; }
+ void SetMaximum(double maximum) { m_max = maximum; }
+
+private:
+ static constexpr Value DefaultValue = 0.0;
+ static constexpr Value DefaultMin = DefaultValue;
+ static constexpr Value DefaultStep = 1.0;
+ static constexpr Value DefaultMax = DefaultValue;
+
+ virtual void copy(const CSettingNumber &setting);
+ static bool fromString(const std::string &strValue, double &value);
+
+ double m_value = DefaultValue;
+ double m_default = DefaultValue;
+ double m_min = DefaultMin;
+ double m_step = DefaultStep;
+ double m_max = DefaultMax;
+
+ static Logger s_logger;
+};
+
+/*!
+ \ingroup settings
+ \brief String setting implementation.
+ \sa CSetting
+ */
+class CSettingString : public CTraitedSetting<std::string, SettingType::String>
+{
+public:
+ CSettingString(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ CSettingString(const std::string &id, const CSettingString &setting);
+ CSettingString(const std::string &id, int label, const std::string &value, CSettingsManager *settingsManager = nullptr);
+ ~CSettingString() override = default;
+
+ std::shared_ptr<CSetting> Clone(const std::string &id) const override;
+ void MergeDetails(const CSetting& other) override;
+
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ bool FromString(const std::string &value) override { return SetValue(value); }
+ std::string ToString() const override { return m_value; }
+ bool Equals(const std::string &value) const override { return m_value == value; }
+ bool CheckValidity(const std::string &value) const override;
+ void Reset() override { SetValue(m_default); }
+
+ virtual const std::string& GetValue() const
+ {
+ std::shared_lock<CSharedSection> lock(m_critical);
+ return m_value;
+ }
+ virtual bool SetValue(const std::string &value);
+ virtual const std::string& GetDefault() const { return m_default; }
+ virtual void SetDefault(const std::string &value);
+
+ virtual bool AllowEmpty() const { return m_allowEmpty; }
+ void SetAllowEmpty(bool allowEmpty) { m_allowEmpty = allowEmpty; }
+ virtual bool AllowNewOption() const { return m_allowNewOption; }
+ void SetAllowNewOption(bool allowNewOption) { m_allowNewOption = allowNewOption; }
+
+ SettingOptionsType GetOptionsType() const;
+ const TranslatableStringSettingOptions& GetTranslatableOptions() const { return m_translatableOptions; }
+ void SetTranslatableOptions(const TranslatableStringSettingOptions &options) { m_translatableOptions = options; }
+ const StringSettingOptions& GetOptions() const { return m_options; }
+ void SetOptions(const StringSettingOptions &options) { m_options = options; }
+ const std::string& GetOptionsFillerName() const { return m_optionsFillerName; }
+ void SetOptionsFillerName(const std::string &optionsFillerName, void *data = nullptr)
+ {
+ m_optionsFillerName = optionsFillerName;
+ m_optionsFillerData = data;
+ }
+ void SetOptionsFiller(StringSettingOptionsFiller optionsFiller, void *data = nullptr)
+ {
+ m_optionsFiller = optionsFiller;
+ m_optionsFillerData = data;
+ }
+ StringSettingOptions GetDynamicOptions() const { return m_dynamicOptions; }
+ StringSettingOptions UpdateDynamicOptions();
+ SettingOptionsSort GetOptionsSort() const { return m_optionsSort; }
+ void SetOptionsSort(SettingOptionsSort optionsSort) { m_optionsSort = optionsSort; }
+
+protected:
+ static const Value DefaultValue;
+
+ virtual void copy(const CSettingString &setting);
+
+ std::string m_value;
+ std::string m_default;
+ bool m_allowEmpty = false;
+ bool m_allowNewOption = false;
+ TranslatableStringSettingOptions m_translatableOptions;
+ StringSettingOptions m_options;
+ std::string m_optionsFillerName;
+ StringSettingOptionsFiller m_optionsFiller = nullptr;
+ void *m_optionsFillerData = nullptr;
+ StringSettingOptions m_dynamicOptions;
+ SettingOptionsSort m_optionsSort = SettingOptionsSort::NoSorting;
+
+ static Logger s_logger;
+};
+
+/*!
+ \ingroup settings
+ \brief Action setting implementation.
+
+ A setting action will trigger a call to the OnSettingAction() callback method
+ when activated.
+
+ \sa CSetting
+ */
+class CSettingAction : public CSetting
+{
+public:
+ CSettingAction(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ CSettingAction(const std::string &id, int label, CSettingsManager *settingsManager = nullptr);
+ CSettingAction(const std::string &id, const CSettingAction &setting);
+ ~CSettingAction() override = default;
+
+ std::shared_ptr<CSetting> Clone(const std::string &id) const override;
+ void MergeDetails(const CSetting& other) override;
+
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ SettingType GetType() const override { return SettingType::Action; }
+ bool FromString(const std::string &value) override { return CheckValidity(value); }
+ std::string ToString() const override { return ""; }
+ bool Equals(const std::string &value) const override { return value.empty(); }
+ bool CheckValidity(const std::string &value) const override { return value.empty(); }
+ void Reset() override { }
+
+ bool HasData() const { return !m_data.empty(); }
+ const std::string& GetData() const { return m_data; }
+ void SetData(const std::string& data) { m_data = data; }
+
+protected:
+ virtual void copy(const CSettingAction& setting);
+
+ std::string m_data;
+
+ static Logger s_logger;
+};
diff --git a/xbmc/settings/lib/SettingCategoryAccess.cpp b/xbmc/settings/lib/SettingCategoryAccess.cpp
new file mode 100644
index 0000000..4640586
--- /dev/null
+++ b/xbmc/settings/lib/SettingCategoryAccess.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "SettingCategoryAccess.h"
+
+#include "SettingConditions.h"
+#include "SettingsManager.h"
+
+bool CSettingCategoryAccessCondition::Check() const
+{
+ if (m_value.empty())
+ return true;
+
+ if (m_settingsManager == nullptr)
+ return false;
+
+ bool found = m_settingsManager->GetConditions().Check(m_value, "true");
+ if (m_negated)
+ return !found;
+
+ return found;
+}
+
+bool CSettingCategoryAccessConditionCombination::Check() const
+{
+ if (m_operations.empty() && m_values.empty())
+ return true;
+
+ return CSettingConditionCombination::Check();
+}
+
+CSettingCategoryAccess::CSettingCategoryAccess(CSettingsManager *settingsManager /* = nullptr */)
+ : CSettingCondition(settingsManager)
+{
+ m_operation = CBooleanLogicOperationPtr(new CSettingCategoryAccessConditionCombination(m_settingsManager));
+}
diff --git a/xbmc/settings/lib/SettingCategoryAccess.h b/xbmc/settings/lib/SettingCategoryAccess.h
new file mode 100644
index 0000000..5d4ceb8
--- /dev/null
+++ b/xbmc/settings/lib/SettingCategoryAccess.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "SettingConditions.h"
+
+#include <set>
+#include <string>
+
+class CSettingCategoryAccessCondition : public CSettingConditionItem
+{
+public:
+ explicit CSettingCategoryAccessCondition(CSettingsManager *settingsManager = nullptr)
+ : CSettingConditionItem(settingsManager)
+ { }
+ ~CSettingCategoryAccessCondition() override = default;
+
+ bool Check() const override;
+};
+
+class CSettingCategoryAccessConditionCombination : public CSettingConditionCombination
+{
+public:
+ explicit CSettingCategoryAccessConditionCombination(CSettingsManager *settingsManager = nullptr)
+ : CSettingConditionCombination(settingsManager)
+ { }
+ ~CSettingCategoryAccessConditionCombination() override = default;
+
+ bool Check() const override;
+
+private:
+ CBooleanLogicOperation* newOperation() override { return new CSettingCategoryAccessConditionCombination(m_settingsManager); }
+ CBooleanLogicValue* newValue() override { return new CSettingCategoryAccessCondition(m_settingsManager); }
+};
+
+class CSettingCategoryAccess : public CSettingCondition
+{
+public:
+ explicit CSettingCategoryAccess(CSettingsManager *settingsManager = nullptr);
+ ~CSettingCategoryAccess() override = default;
+};
diff --git a/xbmc/settings/lib/SettingConditions.cpp b/xbmc/settings/lib/SettingConditions.cpp
new file mode 100644
index 0000000..7146834
--- /dev/null
+++ b/xbmc/settings/lib/SettingConditions.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "SettingConditions.h"
+
+#include "SettingDefinitions.h"
+#include "SettingsManager.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+
+bool CSettingConditionItem::Deserialize(const TiXmlNode *node)
+{
+ if (!CBooleanLogicValue::Deserialize(node))
+ return false;
+
+ auto elem = node->ToElement();
+ if (elem == nullptr)
+ return false;
+
+ // get the "name" attribute
+ auto strAttribute = elem->Attribute(SETTING_XML_ATTR_NAME);
+ if (strAttribute != nullptr)
+ m_name = strAttribute;
+
+ // get the "setting" attribute
+ strAttribute = elem->Attribute(SETTING_XML_ATTR_SETTING);
+ if (strAttribute != nullptr)
+ m_setting = strAttribute;
+
+ return true;
+}
+
+bool CSettingConditionItem::Check() const
+{
+ if (m_settingsManager == nullptr)
+ return false;
+
+ return m_settingsManager->GetConditions().Check(m_name, m_value, m_settingsManager->GetSetting(m_setting)) == !m_negated;
+}
+
+bool CSettingConditionCombination::Check() const
+{
+ bool ok = false;
+ for (const auto& operation : m_operations)
+ {
+ if (operation == nullptr)
+ continue;
+
+ const auto combination = std::static_pointer_cast<const CSettingConditionCombination>(operation);
+ if (combination == nullptr)
+ continue;
+
+ if (combination->Check())
+ ok = true;
+ else if (m_operation == BooleanLogicOperationAnd)
+ return false;
+ }
+
+ for (const auto& value : m_values)
+ {
+ if (value == nullptr)
+ continue;
+
+ const auto condition = std::static_pointer_cast<const CSettingConditionItem>(value);
+ if (condition == nullptr)
+ continue;
+
+ if (condition->Check())
+ ok = true;
+ else if (m_operation == BooleanLogicOperationAnd)
+ return false;
+ }
+
+ return ok;
+}
+
+CSettingCondition::CSettingCondition(CSettingsManager *settingsManager /* = nullptr */)
+ : ISettingCondition(settingsManager)
+{
+ m_operation = CBooleanLogicOperationPtr(new CSettingConditionCombination(settingsManager));
+}
+
+bool CSettingCondition::Check() const
+{
+ auto combination = std::static_pointer_cast<CSettingConditionCombination>(m_operation);
+ if (combination == nullptr)
+ return false;
+
+ return combination->Check();
+}
+
+void CSettingConditionsManager::AddCondition(std::string condition)
+{
+ if (condition.empty())
+ return;
+
+ StringUtils::ToLower(condition);
+
+ m_defines.insert(condition);
+}
+
+void CSettingConditionsManager::AddDynamicCondition(std::string identifier, SettingConditionCheck condition, void *data /*= nullptr*/)
+{
+ if (identifier.empty() || condition == nullptr)
+ return;
+
+ StringUtils::ToLower(identifier);
+
+ m_conditions.emplace(identifier, std::make_pair(condition, data));
+}
+
+void CSettingConditionsManager::RemoveDynamicCondition(std::string identifier)
+{
+ if (identifier.empty())
+ return;
+
+ StringUtils::ToLower(identifier);
+
+ auto it = m_conditions.find(identifier);
+ if (it != m_conditions.end())
+ m_conditions.erase(it);
+}
+
+bool CSettingConditionsManager::Check(
+ std::string condition,
+ const std::string& value /* = "" */,
+ const std::shared_ptr<const CSetting>& setting /* = nullptr */) const
+{
+ if (condition.empty())
+ return false;
+
+ StringUtils::ToLower(condition);
+
+ // special handling of "isdefined" conditions
+ if (condition == "isdefined")
+ {
+ std::string tmpValue = value;
+ StringUtils::ToLower(tmpValue);
+
+ return m_defines.find(tmpValue) != m_defines.end();
+ }
+
+ auto conditionIt = m_conditions.find(condition);
+ if (conditionIt == m_conditions.end())
+ return false;
+
+ return conditionIt->second.first(condition, value, setting, conditionIt->second.second);
+}
diff --git a/xbmc/settings/lib/SettingConditions.h b/xbmc/settings/lib/SettingConditions.h
new file mode 100644
index 0000000..aee2c06
--- /dev/null
+++ b/xbmc/settings/lib/SettingConditions.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "SettingDefinitions.h"
+#include "utils/BooleanLogic.h"
+
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+
+class CSettingsManager;
+class CSetting;
+
+using SettingConditionCheck = bool (*)(const std::string& condition,
+ const std::string& value,
+ const std::shared_ptr<const CSetting>& setting,
+ void* data);
+
+class ISettingCondition
+{
+public:
+ explicit ISettingCondition(CSettingsManager *settingsManager)
+ : m_settingsManager(settingsManager)
+ { }
+ virtual ~ISettingCondition() = default;
+
+ virtual bool Check() const = 0;
+
+protected:
+ CSettingsManager *m_settingsManager;
+};
+
+class CSettingConditionItem : public CBooleanLogicValue, public ISettingCondition
+{
+public:
+ explicit CSettingConditionItem(CSettingsManager *settingsManager = nullptr)
+ : ISettingCondition(settingsManager)
+ { }
+ ~CSettingConditionItem() override = default;
+
+ bool Deserialize(const TiXmlNode *node) override;
+ const char* GetTag() const override { return SETTING_XML_ELM_CONDITION; }
+ bool Check() const override;
+
+protected:
+ std::string m_name;
+ std::string m_setting;
+};
+
+class CSettingConditionCombination : public CBooleanLogicOperation, public ISettingCondition
+{
+public:
+ explicit CSettingConditionCombination(CSettingsManager *settingsManager = nullptr)
+ : ISettingCondition(settingsManager)
+ { }
+ ~CSettingConditionCombination() override = default;
+
+ bool Check() const override;
+
+private:
+ CBooleanLogicOperation* newOperation() override { return new CSettingConditionCombination(m_settingsManager); }
+ CBooleanLogicValue* newValue() override { return new CSettingConditionItem(m_settingsManager); }
+};
+
+class CSettingCondition : public CBooleanLogic, public ISettingCondition
+{
+public:
+ explicit CSettingCondition(CSettingsManager *settingsManager = nullptr);
+ ~CSettingCondition() override = default;
+
+ bool Check() const override;
+};
+
+class CSettingConditionsManager
+{
+public:
+ CSettingConditionsManager() = default;
+ CSettingConditionsManager(const CSettingConditionsManager&) = delete;
+ CSettingConditionsManager const& operator=(CSettingConditionsManager const&) = delete;
+ virtual ~CSettingConditionsManager() = default;
+
+ void AddCondition(std::string condition);
+ void AddDynamicCondition(std::string identifier, SettingConditionCheck condition, void *data = nullptr);
+ void RemoveDynamicCondition(std::string identifier);
+
+ bool Check(
+ std::string condition,
+ const std::string& value = "",
+ const std::shared_ptr<const CSetting>& setting = std::shared_ptr<const CSetting>()) const;
+
+private:
+ using SettingConditionPair = std::pair<std::string, std::pair<SettingConditionCheck, void*>>;
+ using SettingConditionMap = std::map<std::string, std::pair<SettingConditionCheck, void*>>;
+
+ SettingConditionMap m_conditions;
+ std::set<std::string> m_defines;
+};
diff --git a/xbmc/settings/lib/SettingDefinitions.h b/xbmc/settings/lib/SettingDefinitions.h
new file mode 100644
index 0000000..ae18347
--- /dev/null
+++ b/xbmc/settings/lib/SettingDefinitions.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "utils/Variant.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#define SETTING_XML_ROOT "settings"
+#define SETTING_XML_ROOT_VERSION "version"
+
+#define SETTING_XML_ELM_SECTION "section"
+#define SETTING_XML_ELM_CATEGORY "category"
+#define SETTING_XML_ELM_GROUP "group"
+#define SETTING_XML_ELM_SETTING "setting"
+#define SETTING_XML_ELM_VISIBLE "visible"
+#define SETTING_XML_ELM_REQUIREMENT "requirement"
+#define SETTING_XML_ELM_CONDITION "condition"
+#define SETTING_XML_ELM_ENABLED "enable"
+#define SETTING_XML_ELM_LEVEL "level"
+#define SETTING_XML_ELM_DEFAULT "default"
+#define SETTING_XML_ELM_VALUE "value"
+#define SETTING_XML_ELM_CONTROL "control"
+#define SETTING_XML_ELM_CONSTRAINTS "constraints"
+#define SETTING_XML_ELM_OPTIONS "options"
+#define SETTING_XML_ELM_OPTION "option"
+#define SETTING_XML_ELM_MINIMUM "minimum"
+#define SETTING_XML_ELM_STEP "step"
+#define SETTING_XML_ELM_MAXIMUM "maximum"
+#define SETTING_XML_ELM_ALLOWEMPTY "allowempty"
+#define SETTING_XML_ELM_ALLOWNEWOPTION "allownewoption"
+#define SETTING_XML_ELM_DEPENDENCIES "dependencies"
+#define SETTING_XML_ELM_DEPENDENCY "dependency"
+#define SETTING_XML_ELM_UPDATES "updates"
+#define SETTING_XML_ELM_UPDATE "update"
+#define SETTING_XML_ELM_ACCESS "access"
+#define SETTING_XML_ELM_DELIMITER "delimiter"
+#define SETTING_XML_ELM_MINIMUM_ITEMS "minimumitems"
+#define SETTING_XML_ELM_MAXIMUM_ITEMS "maximumitems"
+#define SETTING_XML_ELM_DATA "data"
+
+#define SETTING_XML_ATTR_ID "id"
+#define SETTING_XML_ATTR_REFERENCE "ref"
+#define SETTING_XML_ATTR_LABEL "label"
+#define SETTING_XML_ATTR_HELP "help"
+#define SETTING_XML_ATTR_TYPE "type"
+#define SETTING_XML_ATTR_PARENT "parent"
+#define SETTING_XML_ATTR_FORMAT "format"
+#define SETTING_XML_ATTR_DELAYED "delayed"
+#define SETTING_XML_ATTR_ON "on"
+#define SETTING_XML_ATTR_OPERATOR "operator"
+#define SETTING_XML_ATTR_NAME "name"
+#define SETTING_XML_ATTR_SETTING "setting"
+#define SETTING_XML_ATTR_BEFORE "before"
+#define SETTING_XML_ATTR_AFTER "after"
+
+struct IntegerSettingOption
+{
+ IntegerSettingOption(const std::string& _label, int _value)
+ : label(_label), value(_value) {}
+
+ IntegerSettingOption(const std::string& _label,
+ const std::string& _label2,
+ int _value,
+ const std::vector<std::pair<std::string, CVariant>>& props)
+ : label(_label), label2(_label2), value(_value), properties(props)
+ {
+ }
+
+ std::string label;
+ std::string label2;
+ int value = 0;
+ std::vector<std::pair<std::string, CVariant>> properties;
+};
+
+struct StringSettingOption
+{
+ StringSettingOption(const std::string& _label, const std::string& _value)
+ : label(_label), value(_value) {}
+
+ StringSettingOption(const std::string& _label,
+ const std::string& _label2,
+ const std::string& _value,
+ const std::vector<std::pair<std::string, CVariant>>& props)
+ : label(_label), label2(_label2), value(_value), properties(props)
+ {
+ }
+
+ std::string label;
+ std::string label2;
+ std::string value;
+ std::vector<std::pair<std::string, CVariant>> properties;
+};
+
+struct TranslatableIntegerSettingOption
+{
+ TranslatableIntegerSettingOption() = default;
+ TranslatableIntegerSettingOption(int _label, int _value, const std::string& _addonId = "")
+ : label(_label), value(_value), addonId(_addonId)
+ {
+ }
+
+ int label = 0;
+ int value = 0;
+ std::string addonId; // Leaved empty for Kodi labels
+};
+
+using TranslatableIntegerSettingOptions = std::vector<TranslatableIntegerSettingOption>;
+using IntegerSettingOptions = std::vector<IntegerSettingOption>;
+using TranslatableStringSettingOption = std::pair<int, std::string>;
+using TranslatableStringSettingOptions = std::vector<TranslatableStringSettingOption>;
+using StringSettingOptions = std::vector<StringSettingOption>;
+
+class CSetting;
+using IntegerSettingOptionsFiller = void (*)(const std::shared_ptr<const CSetting>& setting,
+ IntegerSettingOptions& list,
+ int& current,
+ void* data);
+using StringSettingOptionsFiller = void (*)(const std::shared_ptr<const CSetting>& setting,
+ StringSettingOptions& list,
+ std::string& current,
+ void* data);
+
+enum class SettingOptionsSort
+{
+ NoSorting,
+ Ascending,
+ Descending
+};
diff --git a/xbmc/settings/lib/SettingDependency.cpp b/xbmc/settings/lib/SettingDependency.cpp
new file mode 100644
index 0000000..e3122da
--- /dev/null
+++ b/xbmc/settings/lib/SettingDependency.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "SettingDependency.h"
+
+#include "ServiceBroker.h"
+#include "Setting.h"
+#include "SettingDefinitions.h"
+#include "SettingsManager.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/log.h"
+
+#include <memory>
+#include <set>
+#include <stdlib.h>
+#include <string>
+
+Logger CSettingDependencyCondition::s_logger;
+
+CSettingDependencyCondition::CSettingDependencyCondition(
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingDependencyCondition(settingsManager, "", "", "")
+{
+}
+
+CSettingDependencyCondition::CSettingDependencyCondition(
+ const std::string& setting,
+ const std::string& value,
+ SettingDependencyOperator op,
+ bool negated /* = false */,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingDependencyCondition(
+ settingsManager, setting, setting, value, SettingDependencyTarget::Setting, op, negated)
+{
+}
+
+CSettingDependencyCondition::CSettingDependencyCondition(
+ const std::string& strProperty,
+ const std::string& value,
+ const std::string& setting /* = "" */,
+ bool negated /* = false */,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingDependencyCondition(settingsManager,
+ strProperty,
+ setting,
+ value,
+ SettingDependencyTarget::Property,
+ SettingDependencyOperator::Equals,
+ negated)
+{
+}
+
+CSettingDependencyCondition::CSettingDependencyCondition(
+ CSettingsManager* settingsManager,
+ const std::string& strProperty,
+ const std::string& setting,
+ const std::string& value,
+ SettingDependencyTarget target /* = SettingDependencyTarget::Unknown */,
+ SettingDependencyOperator op /* = SettingDependencyOperator::Equals */,
+ bool negated /* = false */)
+ : CSettingConditionItem(settingsManager), m_target(target), m_operator(op)
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingDependencyCondition");
+
+ m_name = strProperty;
+ m_setting = setting;
+ m_value = value;
+ m_negated = negated;
+}
+
+bool CSettingDependencyCondition::Deserialize(const TiXmlNode *node)
+{
+ if (!CSettingConditionItem::Deserialize(node))
+ return false;
+
+ auto elem = node->ToElement();
+ if (elem == nullptr)
+ return false;
+
+ m_target = SettingDependencyTarget::Setting;
+ auto strTarget = elem->Attribute(SETTING_XML_ATTR_ON);
+ if (strTarget != nullptr && !setTarget(strTarget))
+ {
+ s_logger->warn("unknown target \"{}\"", strTarget);
+ return false;
+ }
+
+ if (m_target != SettingDependencyTarget::Setting && m_name.empty())
+ {
+ s_logger->warn("missing name for dependency");
+ return false;
+ }
+
+ if (m_target == SettingDependencyTarget::Setting)
+ {
+ if (m_setting.empty())
+ {
+ s_logger->warn("missing setting for dependency");
+ return false;
+ }
+
+ m_name = m_setting;
+ }
+
+ m_operator = SettingDependencyOperator::Equals;
+ auto strOperator = elem->Attribute(SETTING_XML_ATTR_OPERATOR);
+ if (strOperator != nullptr && !setOperator(strOperator))
+ {
+ s_logger->warn("unknown operator \"{}\"", strOperator);
+ return false;
+ }
+
+ return true;
+}
+
+bool CSettingDependencyCondition::Check() const
+{
+ if (m_name.empty() ||
+ m_target == SettingDependencyTarget::Unknown ||
+ m_operator == SettingDependencyOperator::Unknown ||
+ m_settingsManager == nullptr)
+ return false;
+
+ bool result = false;
+ switch (m_target)
+ {
+ case SettingDependencyTarget::Setting:
+ {
+ if (m_setting.empty())
+ return false;
+
+ auto setting = m_settingsManager->GetSetting(m_setting);
+ if (setting == nullptr)
+ {
+ s_logger->warn("unable to check condition on unknown setting \"{}\"", m_setting);
+ return false;
+ }
+
+ switch (m_operator)
+ {
+ case SettingDependencyOperator::Equals:
+ result = setting->Equals(m_value);
+ break;
+
+ case SettingDependencyOperator::LessThan:
+ {
+ const auto value = setting->ToString();
+ if (StringUtils::IsInteger(m_value))
+ result = strtol(value.c_str(), nullptr, 0) < strtol(m_value.c_str(), nullptr, 0);
+ else
+ result = value.compare(m_value) < 0;
+ break;
+ }
+
+ case SettingDependencyOperator::GreaterThan:
+ {
+ const auto value = setting->ToString();
+ if (StringUtils::IsInteger(m_value))
+ result = strtol(value.c_str(), nullptr, 0) > strtol(m_value.c_str(), nullptr, 0);
+ else
+ result = value.compare(m_value) > 0;
+ break;
+ }
+
+ case SettingDependencyOperator::Contains:
+ result = (setting->ToString().find(m_value) != std::string::npos);
+ break;
+
+ case SettingDependencyOperator::Unknown:
+ default:
+ break;
+ }
+
+ break;
+ }
+
+ case SettingDependencyTarget::Property:
+ {
+ SettingConstPtr setting;
+ if (!m_setting.empty())
+ {
+ setting = m_settingsManager->GetSetting(m_setting);
+ if (setting == nullptr)
+ {
+ s_logger->warn("unable to check condition on unknown setting \"{}\"", m_setting);
+ return false;
+ }
+ }
+ result = m_settingsManager->GetConditions().Check(m_name, m_value, setting);
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+ return result == !m_negated;
+}
+
+bool CSettingDependencyCondition::setTarget(const std::string &target)
+{
+ if (StringUtils::EqualsNoCase(target, "setting"))
+ m_target = SettingDependencyTarget::Setting;
+ else if (StringUtils::EqualsNoCase(target, "property"))
+ m_target = SettingDependencyTarget::Property;
+ else
+ return false;
+
+ return true;
+}
+
+bool CSettingDependencyCondition::setOperator(const std::string &op)
+{
+ size_t length = 0;
+ if (StringUtils::EndsWithNoCase(op, "is"))
+ {
+ m_operator = SettingDependencyOperator::Equals;
+ length = 2;
+ }
+ else if (StringUtils::EndsWithNoCase(op, "lessthan"))
+ {
+ m_operator = SettingDependencyOperator::LessThan;
+ length = 8;
+ }
+ else if (StringUtils::EndsWithNoCase(op, "lt"))
+ {
+ m_operator = SettingDependencyOperator::LessThan;
+ length = 2;
+ }
+ else if (StringUtils::EndsWithNoCase(op, "greaterthan"))
+ {
+ m_operator = SettingDependencyOperator::GreaterThan;
+ length = 11;
+ }
+ else if (StringUtils::EndsWithNoCase(op, "gt"))
+ {
+ m_operator = SettingDependencyOperator::GreaterThan;
+ length = 2;
+ }
+ else if (StringUtils::EndsWithNoCase(op, "contains"))
+ {
+ m_operator = SettingDependencyOperator::Contains;
+ length = 8;
+ }
+
+ if (op.size() > length + 1)
+ return false;
+ if (op.size() == length + 1)
+ {
+ if (!StringUtils::StartsWith(op, "!"))
+ return false;
+ m_negated = true;
+ }
+
+ return true;
+}
+
+bool CSettingDependencyConditionCombination::Deserialize(const TiXmlNode *node)
+{
+ if (node == nullptr)
+ return false;
+
+ size_t numOperations = m_operations.size();
+ size_t numValues = m_values.size();
+
+ if (!CSettingConditionCombination::Deserialize(node))
+ return false;
+
+ if (numOperations < m_operations.size())
+ {
+ for (size_t i = numOperations; i < m_operations.size(); i++)
+ {
+ if (m_operations[i] == nullptr)
+ continue;
+
+ auto combination = static_cast<CSettingDependencyConditionCombination*>(m_operations[i].get());
+ if (combination == nullptr)
+ continue;
+
+ const std::set<std::string>& settings = combination->GetSettings();
+ m_settings.insert(settings.begin(), settings.end());
+ }
+ }
+
+ if (numValues < m_values.size())
+ {
+ for (size_t i = numValues; i < m_values.size(); i++)
+ {
+ if (m_values[i] == nullptr)
+ continue;
+
+ auto condition = static_cast<CSettingDependencyCondition*>(m_values[i].get());
+ if (condition == nullptr)
+ continue;
+
+ auto settingId = condition->GetSetting();
+ if (!settingId.empty())
+ m_settings.insert(settingId);
+ }
+ }
+
+ return true;
+}
+
+CSettingDependencyConditionCombination* CSettingDependencyConditionCombination::Add(
+ const CSettingDependencyConditionPtr& condition)
+{
+ if (condition != nullptr)
+ {
+ m_values.push_back(condition);
+
+ auto settingId = condition->GetSetting();
+ if (!settingId.empty())
+ m_settings.insert(settingId);
+ }
+
+ return this;
+}
+
+CSettingDependencyConditionCombination* CSettingDependencyConditionCombination::Add(
+ const CSettingDependencyConditionCombinationPtr& operation)
+{
+ if (operation != nullptr)
+ {
+ m_operations.push_back(operation);
+
+ const auto& settings = operation->GetSettings();
+ m_settings.insert(settings.begin(), settings.end());
+ }
+
+ return this;
+}
+
+Logger CSettingDependency::s_logger;
+
+CSettingDependency::CSettingDependency(CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingDependency(SettingDependencyType::Unknown, settingsManager)
+{
+}
+
+CSettingDependency::CSettingDependency(SettingDependencyType type,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingCondition(settingsManager), m_type(type)
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingDependency");
+
+ m_operation = CBooleanLogicOperationPtr(new CSettingDependencyConditionCombination(m_settingsManager));
+}
+
+bool CSettingDependency::Deserialize(const TiXmlNode *node)
+{
+ if (node == nullptr)
+ return false;
+
+ auto elem = node->ToElement();
+ if (elem == nullptr)
+ return false;
+
+ auto strType = elem->Attribute(SETTING_XML_ATTR_TYPE);
+ if (strType == nullptr || strlen(strType) <= 0 || !setType(strType))
+ {
+ s_logger->warn("missing or unknown dependency type definition");
+ return false;
+ }
+
+ return CSettingCondition::Deserialize(node);
+}
+
+std::set<std::string> CSettingDependency::GetSettings() const
+{
+ if (m_operation == nullptr)
+ return std::set<std::string>();
+
+ auto combination = static_cast<CSettingDependencyConditionCombination*>(m_operation.get());
+ if (combination == nullptr)
+ return std::set<std::string>();
+
+ return combination->GetSettings();
+}
+
+CSettingDependencyConditionCombinationPtr CSettingDependency::And()
+{
+ if (m_operation == nullptr)
+ m_operation = CBooleanLogicOperationPtr(new CSettingDependencyConditionCombination(m_settingsManager));
+
+ m_operation->SetOperation(BooleanLogicOperationAnd);
+
+ return std::dynamic_pointer_cast<CSettingDependencyConditionCombination>(m_operation);
+}
+
+CSettingDependencyConditionCombinationPtr CSettingDependency::Or()
+{
+ if (m_operation == nullptr)
+ m_operation = CBooleanLogicOperationPtr(new CSettingDependencyConditionCombination(m_settingsManager));
+
+ m_operation->SetOperation(BooleanLogicOperationOr);
+
+ return std::dynamic_pointer_cast<CSettingDependencyConditionCombination>(m_operation);
+}
+
+bool CSettingDependency::setType(const std::string &type)
+{
+ if (StringUtils::EqualsNoCase(type, "enable"))
+ m_type = SettingDependencyType::Enable;
+ else if (StringUtils::EqualsNoCase(type, "update"))
+ m_type = SettingDependencyType::Update;
+ else if (StringUtils::EqualsNoCase(type, "visible"))
+ m_type = SettingDependencyType::Visible;
+ else
+ return false;
+
+ return true;
+}
diff --git a/xbmc/settings/lib/SettingDependency.h b/xbmc/settings/lib/SettingDependency.h
new file mode 100644
index 0000000..5cb34db
--- /dev/null
+++ b/xbmc/settings/lib/SettingDependency.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "SettingConditions.h"
+#include "utils/BooleanLogic.h"
+#include "utils/logtypes.h"
+
+#include <list>
+#include <set>
+#include <string>
+
+enum class SettingDependencyType {
+ Unknown = 0,
+ Enable,
+ Update,
+ Visible
+};
+
+enum class SettingDependencyOperator {
+ Unknown = 0,
+ Equals,
+ LessThan,
+ GreaterThan,
+ Contains
+};
+
+enum class SettingDependencyTarget {
+ Unknown = 0,
+ Setting,
+ Property
+};
+
+class CSettingDependencyCondition : public CSettingConditionItem
+{
+public:
+ explicit CSettingDependencyCondition(CSettingsManager *settingsManager = nullptr);
+ CSettingDependencyCondition(const std::string &setting, const std::string &value,
+ SettingDependencyOperator op, bool negated = false,
+ CSettingsManager *settingsManager = nullptr);
+ CSettingDependencyCondition(const std::string &strProperty, const std::string &value,
+ const std::string &setting = "", bool negated = false,
+ CSettingsManager *settingsManager = nullptr);
+ ~CSettingDependencyCondition() override = default;
+
+ bool Deserialize(const TiXmlNode *node) override;
+ bool Check() const override;
+
+ const std::string& GetName() const { return m_name; }
+ const std::string& GetSetting() const { return m_setting; }
+ SettingDependencyTarget GetTarget() const { return m_target; }
+ SettingDependencyOperator GetOperator() const { return m_operator; }
+
+private:
+ CSettingDependencyCondition(CSettingsManager* settingsManager,
+ const std::string& strProperty,
+ const std::string& setting,
+ const std::string& value,
+ SettingDependencyTarget target = SettingDependencyTarget::Unknown,
+ SettingDependencyOperator op = SettingDependencyOperator::Equals,
+ bool negated = false);
+
+ bool setTarget(const std::string &target);
+ bool setOperator(const std::string &op);
+
+ SettingDependencyTarget m_target = SettingDependencyTarget::Unknown;
+ SettingDependencyOperator m_operator = SettingDependencyOperator::Equals;
+
+ static Logger s_logger;
+};
+
+using CSettingDependencyConditionPtr = std::shared_ptr<CSettingDependencyCondition>;
+
+class CSettingDependencyConditionCombination;
+using CSettingDependencyConditionCombinationPtr = std::shared_ptr<CSettingDependencyConditionCombination>;
+
+class CSettingDependencyConditionCombination : public CSettingConditionCombination
+{
+public:
+ explicit CSettingDependencyConditionCombination(CSettingsManager *settingsManager = nullptr)
+ : CSettingConditionCombination(settingsManager)
+ { }
+ CSettingDependencyConditionCombination(BooleanLogicOperation op, CSettingsManager *settingsManager = nullptr)
+ : CSettingConditionCombination(settingsManager)
+ {
+ SetOperation(op);
+ }
+ ~CSettingDependencyConditionCombination() override = default;
+
+ bool Deserialize(const TiXmlNode *node) override;
+
+ const std::set<std::string>& GetSettings() const { return m_settings; }
+
+ CSettingDependencyConditionCombination* Add(const CSettingDependencyConditionPtr& condition);
+ CSettingDependencyConditionCombination* Add(
+ const CSettingDependencyConditionCombinationPtr& operation);
+
+private:
+ CBooleanLogicOperation* newOperation() override { return new CSettingDependencyConditionCombination(m_settingsManager); }
+ CBooleanLogicValue* newValue() override { return new CSettingDependencyCondition(m_settingsManager); }
+
+ std::set<std::string> m_settings;
+};
+
+class CSettingDependency : public CSettingCondition
+{
+public:
+ explicit CSettingDependency(CSettingsManager *settingsManager = nullptr);
+ CSettingDependency(SettingDependencyType type, CSettingsManager *settingsManager = nullptr);
+ ~CSettingDependency() override = default;
+
+ bool Deserialize(const TiXmlNode *node) override;
+
+ SettingDependencyType GetType() const { return m_type; }
+ std::set<std::string> GetSettings() const;
+
+ CSettingDependencyConditionCombinationPtr And();
+ CSettingDependencyConditionCombinationPtr Or();
+
+private:
+ bool setType(const std::string &type);
+
+ SettingDependencyType m_type = SettingDependencyType::Unknown;
+
+ static Logger s_logger;
+};
+
+using SettingDependencies = std::list<CSettingDependency>;
+using SettingDependencyMap = std::map<std::string, SettingDependencies>;
diff --git a/xbmc/settings/lib/SettingLevel.h b/xbmc/settings/lib/SettingLevel.h
new file mode 100644
index 0000000..db8e2b5
--- /dev/null
+++ b/xbmc/settings/lib/SettingLevel.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+/*!
+ \ingroup settings
+ \brief Levels which every setting is assigned to.
+ */
+enum class SettingLevel {
+ Basic = 0,
+ Standard,
+ Advanced,
+ Expert,
+ Internal
+};
diff --git a/xbmc/settings/lib/SettingRequirement.cpp b/xbmc/settings/lib/SettingRequirement.cpp
new file mode 100644
index 0000000..5ba81ab
--- /dev/null
+++ b/xbmc/settings/lib/SettingRequirement.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "SettingRequirement.h"
+
+#include "SettingsManager.h"
+
+bool CSettingRequirementCondition::Check() const
+{
+ if (m_settingsManager == nullptr)
+ return false;
+
+ bool found = m_settingsManager->GetConditions().Check("IsDefined", m_value);
+ if (m_negated)
+ return !found;
+
+ return found;
+}
+
+bool CSettingRequirementConditionCombination::Check() const
+{
+ if (m_operations.empty() && m_values.empty())
+ return true;
+
+ return CSettingConditionCombination::Check();
+}
+
+CSettingRequirement::CSettingRequirement(CSettingsManager *settingsManager /* = nullptr */)
+ : CSettingCondition(settingsManager)
+{
+ m_operation = CBooleanLogicOperationPtr(new CSettingRequirementConditionCombination(m_settingsManager));
+}
diff --git a/xbmc/settings/lib/SettingRequirement.h b/xbmc/settings/lib/SettingRequirement.h
new file mode 100644
index 0000000..f8357a5
--- /dev/null
+++ b/xbmc/settings/lib/SettingRequirement.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "SettingConditions.h"
+
+#include <set>
+#include <string>
+
+class CSettingRequirementCondition : public CSettingConditionItem
+{
+public:
+ explicit CSettingRequirementCondition(CSettingsManager *settingsManager = nullptr)
+ : CSettingConditionItem(settingsManager)
+ { }
+ ~CSettingRequirementCondition() override = default;
+
+ bool Check() const override;
+};
+
+class CSettingRequirementConditionCombination : public CSettingConditionCombination
+{
+public:
+ explicit CSettingRequirementConditionCombination(CSettingsManager *settingsManager = nullptr)
+ : CSettingConditionCombination(settingsManager)
+ { }
+ ~CSettingRequirementConditionCombination() override = default;
+
+ bool Check() const override;
+
+private:
+ CBooleanLogicOperation* newOperation() override { return new CSettingRequirementConditionCombination(m_settingsManager); }
+ CBooleanLogicValue* newValue() override { return new CSettingRequirementCondition(m_settingsManager); }
+};
+
+class CSettingRequirement : public CSettingCondition
+{
+public:
+ explicit CSettingRequirement(CSettingsManager *settingsManager = nullptr);
+ ~CSettingRequirement() override = default;
+};
diff --git a/xbmc/settings/lib/SettingSection.cpp b/xbmc/settings/lib/SettingSection.cpp
new file mode 100644
index 0000000..4f1b564
--- /dev/null
+++ b/xbmc/settings/lib/SettingSection.cpp
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "SettingSection.h"
+
+#include "ServiceBroker.h"
+#include "SettingDefinitions.h"
+#include "SettingsManager.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/log.h"
+
+#include <algorithm>
+
+template<class T>
+void addISetting(const TiXmlNode* node, const T& item, std::vector<T>& items, bool toBegin = false)
+{
+ if (node != nullptr)
+ {
+ auto element = node->ToElement();
+ if (element != nullptr)
+ {
+ // check if there is a "before" or "after" attribute to place the setting at a specific position
+ int position = -1; // -1 => end, 0 => before, 1 => after
+ auto positionId = element->Attribute(SETTING_XML_ATTR_BEFORE);
+ if (positionId != nullptr && strlen(positionId) > 0)
+ position = 0;
+ else if ((positionId = element->Attribute(SETTING_XML_ATTR_AFTER)) != nullptr && strlen(positionId) > 0)
+ position = 1;
+
+ if (positionId != nullptr && strlen(positionId) > 0 && position >= 0)
+ {
+ for (typename std::vector<T>::iterator it = items.begin(); it != items.end(); ++it)
+ {
+ if (!StringUtils::EqualsNoCase((*it)->GetId(), positionId))
+ continue;
+
+ typename std::vector<T>::iterator positionIt = it;
+ if (position == 1)
+ ++positionIt;
+
+ items.insert(positionIt, item);
+ return;
+ }
+ }
+ }
+ }
+
+ if (!toBegin)
+ items.emplace_back(item);
+ else
+ items.insert(items.begin(), item);
+}
+
+Logger CSettingGroup::s_logger;
+
+CSettingGroup::CSettingGroup(const std::string& id,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : ISetting(id, settingsManager)
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingGroup");
+}
+
+bool CSettingGroup::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ // handle <visible> conditions
+ if (!ISetting::Deserialize(node, update))
+ return false;
+
+ auto controlElement = node->FirstChildElement(SETTING_XML_ELM_CONTROL);
+ if (controlElement != nullptr)
+ {
+ auto controlType = controlElement->Attribute(SETTING_XML_ATTR_TYPE);
+ if (controlType == nullptr || strlen(controlType) <= 0)
+ {
+ s_logger->error("unable to read control type");
+ return false;
+ }
+
+ m_control = m_settingsManager->CreateControl(controlType);
+ if (m_control == nullptr)
+ {
+ s_logger->error("unable to create new control \"{}\"", controlType);
+ return false;
+ }
+
+ if (!m_control->Deserialize(controlElement))
+ {
+ s_logger->warn("unable to read control \"{}\"", controlType);
+ m_control.reset();
+ }
+ }
+
+ auto settingElement = node->FirstChildElement(SETTING_XML_ELM_SETTING);
+ while (settingElement != nullptr)
+ {
+ std::string settingId;
+ bool isReference;
+ if (CSetting::DeserializeIdentification(settingElement, settingId, isReference))
+ {
+ auto settingIt = std::find_if(m_settings.begin(), m_settings.end(),
+ [&settingId](const SettingPtr& setting)
+ {
+ return setting->GetId() == settingId;
+ });
+
+ SettingPtr setting;
+ if (settingIt != m_settings.end())
+ setting = *settingIt;
+
+ update = (setting != nullptr);
+ if (!update)
+ {
+ auto settingType = settingElement->Attribute(SETTING_XML_ATTR_TYPE);
+ if (settingType == nullptr || strlen(settingType) <= 0)
+ {
+ s_logger->error("unable to read setting type of \"{}\"", settingId);
+ return false;
+ }
+
+ setting = m_settingsManager->CreateSetting(settingType, settingId, m_settingsManager);
+ if (setting == nullptr)
+ s_logger->error("unknown setting type \"{}\" of \"{}\"", settingType, settingId);
+ }
+
+ if (setting == nullptr)
+ s_logger->error("unable to create new setting \"{}\"", settingId);
+ else
+ {
+ if (!setting->Deserialize(settingElement, update))
+ s_logger->warn("unable to read setting \"{}\"", settingId);
+ else
+ {
+ // if the setting is a reference turn it into one
+ if (isReference)
+ setting->MakeReference();
+
+ if (!update)
+ addISetting(settingElement, setting, m_settings);
+ }
+ }
+ }
+
+ settingElement = settingElement->NextSiblingElement(SETTING_XML_ELM_SETTING);
+ }
+
+ return true;
+}
+
+SettingList CSettingGroup::GetSettings(SettingLevel level) const
+{
+ SettingList settings;
+ for (const auto& setting : m_settings)
+ {
+ if (setting->GetLevel() <= level && setting->MeetsRequirements())
+ settings.push_back(setting);
+ }
+
+ return settings;
+}
+
+void CSettingGroup::AddSetting(const SettingPtr& setting)
+{
+ addISetting(nullptr, setting, m_settings);
+}
+
+void CSettingGroup::AddSettings(const SettingList &settings)
+{
+ for (const auto& setting : settings)
+ addISetting(nullptr, setting, m_settings);
+}
+
+bool CSettingGroup::ReplaceSetting(const std::shared_ptr<const CSetting>& currentSetting,
+ const std::shared_ptr<CSetting>& newSetting)
+{
+ for (auto itSetting = m_settings.begin(); itSetting != m_settings.end(); ++itSetting)
+ {
+ if (*itSetting == currentSetting)
+ {
+ if (newSetting == nullptr)
+ m_settings.erase(itSetting);
+ else
+ *itSetting = newSetting;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+Logger CSettingCategory::s_logger;
+
+CSettingCategory::CSettingCategory(const std::string& id,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : ISetting(id, settingsManager),
+ m_accessCondition(settingsManager)
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingCategory");
+}
+
+bool CSettingCategory::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ // handle <visible> conditions
+ if (!ISetting::Deserialize(node, update))
+ return false;
+
+ auto accessNode = node->FirstChild(SETTING_XML_ELM_ACCESS);
+ if (accessNode != nullptr && !m_accessCondition.Deserialize(accessNode))
+ return false;
+
+ auto groupNode = node->FirstChild(SETTING_XML_ELM_GROUP);
+ while (groupNode != nullptr)
+ {
+ std::string groupId;
+ if (CSettingGroup::DeserializeIdentification(groupNode, groupId))
+ {
+ auto groupIt = std::find_if(m_groups.begin(), m_groups.end(),
+ [&groupId](const SettingGroupPtr& group)
+ {
+ return group->GetId() == groupId;
+ });
+
+ SettingGroupPtr group;
+ if (groupIt != m_groups.end())
+ group = *groupIt;
+
+ update = (group != nullptr);
+ if (!update)
+ group = std::make_shared<CSettingGroup>(groupId, m_settingsManager);
+
+ if (group->Deserialize(groupNode, update))
+ {
+ if (!update)
+ addISetting(groupNode, group, m_groups);
+ }
+ else
+ s_logger->warn("unable to read group \"{}\"", groupId);
+ }
+
+ groupNode = groupNode->NextSibling(SETTING_XML_ELM_GROUP);
+ }
+
+ return true;
+}
+
+SettingGroupList CSettingCategory::GetGroups(SettingLevel level) const
+{
+ SettingGroupList groups;
+ for (const auto& group : m_groups)
+ {
+ if (group->MeetsRequirements() && group->IsVisible() && group->GetSettings(level).size() > 0)
+ groups.push_back(group);
+ }
+
+ return groups;
+}
+
+bool CSettingCategory::CanAccess() const
+{
+ return m_accessCondition.Check();
+}
+
+void CSettingCategory::AddGroup(const SettingGroupPtr& group)
+{
+ addISetting(nullptr, group, m_groups, false);
+}
+
+void CSettingCategory::AddGroupToFront(const SettingGroupPtr& group)
+{
+ addISetting(nullptr, group, m_groups, true);
+}
+
+void CSettingCategory::AddGroups(const SettingGroupList &groups)
+{
+ for (const auto& group : groups)
+ addISetting(nullptr, group, m_groups);
+}
+
+Logger CSettingSection::s_logger;
+
+CSettingSection::CSettingSection(const std::string& id,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : ISetting(id, settingsManager)
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingSection");
+}
+
+bool CSettingSection::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ // handle <visible> conditions
+ if (!ISetting::Deserialize(node, update))
+ return false;
+
+ auto categoryNode = node->FirstChild(SETTING_XML_ELM_CATEGORY);
+ while (categoryNode != nullptr)
+ {
+ std::string categoryId;
+ if (CSettingCategory::DeserializeIdentification(categoryNode, categoryId))
+ {
+ auto categoryIt = std::find_if(m_categories.begin(), m_categories.end(),
+ [&categoryId](const SettingCategoryPtr& category)
+ {
+ return category->GetId() == categoryId;
+ });
+
+ SettingCategoryPtr category;
+ if (categoryIt != m_categories.end())
+ category = *categoryIt;
+
+ update = (category != nullptr);
+ if (!update)
+ category = std::make_shared<CSettingCategory>(categoryId, m_settingsManager);
+
+ if (category->Deserialize(categoryNode, update))
+ {
+ if (!update)
+ addISetting(categoryNode, category, m_categories);
+ }
+ else
+ s_logger->warn("unable to read category \"{}\"", categoryId);
+ }
+
+ categoryNode = categoryNode->NextSibling(SETTING_XML_ELM_CATEGORY);
+ }
+
+ return true;
+}
+
+SettingCategoryList CSettingSection::GetCategories(SettingLevel level) const
+{
+ SettingCategoryList categories;
+ for (const auto& category : m_categories)
+ {
+ if (category->MeetsRequirements() && category->IsVisible() && category->GetGroups(level).size() > 0)
+ categories.push_back(category);
+ }
+
+ return categories;
+}
+
+void CSettingSection::AddCategory(const SettingCategoryPtr& category)
+{
+ addISetting(nullptr, category, m_categories);
+}
+
+void CSettingSection::AddCategories(const SettingCategoryList &categories)
+{
+ for (const auto& category : categories)
+ addISetting(nullptr, category, m_categories);
+}
diff --git a/xbmc/settings/lib/SettingSection.h b/xbmc/settings/lib/SettingSection.h
new file mode 100644
index 0000000..f8e06d6
--- /dev/null
+++ b/xbmc/settings/lib/SettingSection.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "ISetting.h"
+#include "Setting.h"
+#include "SettingCategoryAccess.h"
+#include "utils/logtypes.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+class CSettingsManager;
+
+/*!
+ \ingroup settings
+ \brief Group of settings being part of a category
+ \sa CSettingCategory
+ \sa CSetting
+ */
+class CSettingGroup : public ISetting
+{
+public:
+ /*!
+ \brief Creates a new setting group with the given identifier.
+
+ \param id Identifier of the setting group
+ \param settingsManager Reference to the settings manager
+ */
+ CSettingGroup(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ ~CSettingGroup() override = default;
+
+ // implementation of ISetting
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ /*!
+ \brief Gets the full list of settings belonging to the setting group.
+
+ \return Full list of settings belonging to the setting group
+ */
+ const SettingList& GetSettings() const { return m_settings; }
+ /*!
+ \brief Gets the list of settings assigned to the given setting level (or
+ below) and that meet the requirements conditions belonging to the setting
+ group.
+
+ \param level Level the settings should be assigned to
+ \return List of settings belonging to the setting group
+ */
+ SettingList GetSettings(SettingLevel level) const;
+
+ void AddSetting(const std::shared_ptr<CSetting>& setting);
+ void AddSettings(const SettingList &settings);
+
+ bool ReplaceSetting(const std::shared_ptr<const CSetting>& currentSetting,
+ const std::shared_ptr<CSetting>& newSetting);
+
+ std::shared_ptr<const ISettingControl> GetControl() const { return m_control; }
+ std::shared_ptr<ISettingControl> GetControl() { return m_control; }
+ void SetControl(std::shared_ptr<ISettingControl> control) { m_control = std::move(control); }
+
+private:
+ SettingList m_settings;
+ std::shared_ptr<ISettingControl> m_control;
+
+ static Logger s_logger;
+};
+
+using SettingGroupPtr = std::shared_ptr<CSettingGroup>;
+using SettingGroupList = std::vector<SettingGroupPtr>;
+
+/*!
+ \ingroup settings
+ \brief Category of groups of settings being part of a section
+ \sa CSettingSection
+ \sa CSettingGroup
+ */
+class CSettingCategory : public ISetting
+{
+public:
+ /*!
+ \brief Creates a new setting category with the given identifier.
+
+ \param id Identifier of the setting category
+ \param settingsManager Reference to the settings manager
+ */
+ CSettingCategory(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ ~CSettingCategory() override = default;
+
+ // implementation of ISetting
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ /*!
+ \brief Gets the full list of setting groups belonging to the setting
+ category.
+
+ \return Full list of setting groups belonging to the setting category
+ */
+ const SettingGroupList& GetGroups() const { return m_groups; }
+ /*!
+ \brief Gets the list of setting groups belonging to the setting category
+ that contain settings assigned to the given setting level (or below) and
+ that meet the requirements and visibility conditions.
+
+ \param level Level the settings should be assigned to
+ \return List of setting groups belonging to the setting category
+ */
+ SettingGroupList GetGroups(SettingLevel level) const;
+
+ /*!
+ \brief Whether the setting category can be accessed or not.
+
+ \return True if the setting category can be accessed, false otherwise
+ */
+ bool CanAccess() const;
+
+ void AddGroup(const SettingGroupPtr& group);
+ void AddGroupToFront(const SettingGroupPtr& group);
+ void AddGroups(const SettingGroupList &groups);
+
+private:
+ SettingGroupList m_groups;
+ CSettingCategoryAccess m_accessCondition;
+
+ static Logger s_logger;
+};
+
+using SettingCategoryPtr = std::shared_ptr<CSettingCategory>;
+using SettingCategoryList = std::vector<SettingCategoryPtr>;
+
+/*!
+ \ingroup settings
+ \brief Section of setting categories
+ \sa CSettings
+ \sa CSettingCategory
+ */
+class CSettingSection : public ISetting
+{
+public:
+ /*!
+ \brief Creates a new setting section with the given identifier.
+
+ \param id Identifier of the setting section
+ \param settingsManager Reference to the settings manager
+ */
+ CSettingSection(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ ~CSettingSection() override = default;
+
+ // implementation of ISetting
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ /*!
+ \brief Gets the full list of setting categories belonging to the setting
+ section.
+
+ \return Full list of setting categories belonging to the setting section
+ */
+ const SettingCategoryList& GetCategories() const { return m_categories; }
+ /*!
+ \brief Gets the list of setting categories belonging to the setting section
+ that contain settings assigned to the given setting level (or below) and
+ that meet the requirements and visibility conditions.
+
+ \param level Level the settings should be assigned to
+ \return List of setting categories belonging to the setting section
+ */
+ SettingCategoryList GetCategories(SettingLevel level) const;
+
+ void AddCategory(const SettingCategoryPtr& category);
+ void AddCategories(const SettingCategoryList &categories);
+
+private:
+ SettingCategoryList m_categories;
+
+ static Logger s_logger;
+};
+
+using SettingSectionPtr = std::shared_ptr<CSettingSection>;
+using SettingSectionList = std::vector<SettingSectionPtr>;
diff --git a/xbmc/settings/lib/SettingType.h b/xbmc/settings/lib/SettingType.h
new file mode 100644
index 0000000..56838ab
--- /dev/null
+++ b/xbmc/settings/lib/SettingType.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+/*!
+ \ingroup settings
+ \brief Basic setting types available in the settings system.
+ */
+enum class SettingType {
+ Unknown = 0,
+ Boolean,
+ Integer,
+ Number,
+ String,
+ Action,
+ List
+};
diff --git a/xbmc/settings/lib/SettingUpdate.cpp b/xbmc/settings/lib/SettingUpdate.cpp
new file mode 100644
index 0000000..0201aab
--- /dev/null
+++ b/xbmc/settings/lib/SettingUpdate.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "SettingUpdate.h"
+
+#include "ServiceBroker.h"
+#include "SettingDefinitions.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/log.h"
+
+Logger CSettingUpdate::s_logger;
+
+CSettingUpdate::CSettingUpdate()
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingUpdate");
+}
+
+bool CSettingUpdate::Deserialize(const TiXmlNode *node)
+{
+ if (node == nullptr)
+ return false;
+
+ auto elem = node->ToElement();
+ if (elem == nullptr)
+ return false;
+
+ auto strType = elem->Attribute(SETTING_XML_ATTR_TYPE);
+ if (strType == nullptr || strlen(strType) <= 0 || !setType(strType))
+ {
+ s_logger->warn("missing or unknown update type definition");
+ return false;
+ }
+
+ if (m_type == SettingUpdateType::Rename)
+ {
+ if (node->FirstChild() == nullptr || node->FirstChild()->Type() != TiXmlNode::TINYXML_TEXT)
+ {
+ s_logger->warn("missing or invalid setting id for rename update definition");
+ return false;
+ }
+
+ m_value = node->FirstChild()->ValueStr();
+ }
+
+ return true;
+}
+
+bool CSettingUpdate::setType(const std::string &type)
+{
+ if (StringUtils::EqualsNoCase(type, "change"))
+ m_type = SettingUpdateType::Change;
+ else if (StringUtils::EqualsNoCase(type, "rename"))
+ m_type = SettingUpdateType::Rename;
+ else
+ return false;
+
+ return true;
+}
diff --git a/xbmc/settings/lib/SettingUpdate.h b/xbmc/settings/lib/SettingUpdate.h
new file mode 100644
index 0000000..65c3e86
--- /dev/null
+++ b/xbmc/settings/lib/SettingUpdate.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "utils/logtypes.h"
+
+#include <string>
+
+class TiXmlNode;
+
+enum class SettingUpdateType {
+ Unknown = 0,
+ Rename,
+ Change
+};
+
+class CSettingUpdate
+{
+public:
+ CSettingUpdate();
+ virtual ~CSettingUpdate() = default;
+
+ inline bool operator<(const CSettingUpdate& rhs) const
+ {
+ return m_type < rhs.m_type && m_value < rhs.m_value;
+ }
+
+ virtual bool Deserialize(const TiXmlNode *node);
+
+ SettingUpdateType GetType() const { return m_type; }
+ const std::string& GetValue() const { return m_value; }
+
+private:
+ bool setType(const std::string &type);
+
+ SettingUpdateType m_type = SettingUpdateType::Unknown;
+ std::string m_value;
+
+ static Logger s_logger;
+};
diff --git a/xbmc/settings/lib/SettingsManager.cpp b/xbmc/settings/lib/SettingsManager.cpp
new file mode 100644
index 0000000..9ff7d61
--- /dev/null
+++ b/xbmc/settings/lib/SettingsManager.cpp
@@ -0,0 +1,1424 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "SettingsManager.h"
+
+#include "ServiceBroker.h"
+#include "Setting.h"
+#include "SettingDefinitions.h"
+#include "SettingSection.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/log.h"
+
+#include <algorithm>
+#include <map>
+#include <mutex>
+#include <shared_mutex>
+#include <unordered_set>
+#include <utility>
+
+const uint32_t CSettingsManager::Version = 2;
+const uint32_t CSettingsManager::MinimumSupportedVersion = 0;
+
+bool ParseSettingIdentifier(const std::string& settingId, std::string& categoryTag, std::string& settingTag)
+{
+ static const std::string Separator = ".";
+
+ if (settingId.empty())
+ return false;
+
+ auto parts = StringUtils::Split(settingId, Separator);
+ if (parts.size() < 1 || parts.at(0).empty())
+ return false;
+
+ if (parts.size() == 1)
+ {
+ settingTag = parts.at(0);
+ return true;
+ }
+
+ // get the category tag and remove it from the parts
+ categoryTag = parts.at(0);
+ parts.erase(parts.begin());
+
+ // put together the setting tag
+ settingTag = StringUtils::Join(parts, Separator);
+
+ return true;
+}
+
+CSettingsManager::CSettingsManager()
+ : m_logger(CServiceBroker::GetLogging().GetLogger("CSettingsManager"))
+{
+}
+
+CSettingsManager::~CSettingsManager()
+{
+ // first clear all registered settings handler and subsettings
+ // implementations because we can't be sure that they are still valid
+ m_settingsHandlers.clear();
+ m_settingCreators.clear();
+ m_settingControlCreators.clear();
+
+ Clear();
+}
+
+uint32_t CSettingsManager::ParseVersion(const TiXmlElement* root) const
+{
+ // try to get and check the version
+ uint32_t version = 0;
+ root->QueryUnsignedAttribute(SETTING_XML_ROOT_VERSION, &version);
+
+ return version;
+}
+
+bool CSettingsManager::Initialize(const TiXmlElement *root)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ std::unique_lock<CSharedSection> settingsLock(m_settingsCritical);
+ if (m_initialized || root == nullptr)
+ return false;
+
+ if (!StringUtils::EqualsNoCase(root->ValueStr(), SETTING_XML_ROOT))
+ {
+ m_logger->error("error reading settings definition: doesn't contain <" SETTING_XML_ROOT
+ "> tag");
+ return false;
+ }
+
+ // try to get and check the version
+ uint32_t version = ParseVersion(root);
+ if (version == 0)
+ m_logger->warn("missing " SETTING_XML_ROOT_VERSION " attribute", SETTING_XML_ROOT_VERSION);
+
+ if (MinimumSupportedVersion >= version+1)
+ {
+ m_logger->error("unable to read setting definitions from version {} (minimum version: {})",
+ version, MinimumSupportedVersion);
+ return false;
+ }
+ if (version > Version)
+ {
+ m_logger->error("unable to read setting definitions from version {} (current version: {})",
+ version, Version);
+ return false;
+ }
+
+ auto sectionNode = root->FirstChild(SETTING_XML_ELM_SECTION);
+ while (sectionNode != nullptr)
+ {
+ std::string sectionId;
+ if (CSettingSection::DeserializeIdentification(sectionNode, sectionId))
+ {
+ SettingSectionPtr section = nullptr;
+ auto itSection = m_sections.find(sectionId);
+ bool update = (itSection != m_sections.end());
+ if (!update)
+ section = std::make_shared<CSettingSection>(sectionId, this);
+ else
+ section = itSection->second;
+
+ if (section->Deserialize(sectionNode, update))
+ AddSection(section);
+ else
+ {
+ m_logger->warn("unable to read section \"{}\"", sectionId);
+ }
+ }
+
+ sectionNode = sectionNode->NextSibling(SETTING_XML_ELM_SECTION);
+ }
+
+ return true;
+}
+
+bool CSettingsManager::Load(const TiXmlElement *root, bool &updated, bool triggerEvents /* = true */, std::map<std::string, SettingPtr> *loadedSettings /* = nullptr */)
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ std::unique_lock<CSharedSection> settingsLock(m_settingsCritical);
+ if (m_loaded || root == nullptr)
+ return false;
+
+ if (triggerEvents && !OnSettingsLoading())
+ return false;
+
+ // try to get and check the version
+ uint32_t version = ParseVersion(root);
+ if (version == 0)
+ m_logger->warn("missing {} attribute", SETTING_XML_ROOT_VERSION);
+
+ if (MinimumSupportedVersion >= version+1)
+ {
+ m_logger->error("unable to read setting values from version {} (minimum version: {})", version,
+ MinimumSupportedVersion);
+ return false;
+ }
+ if (version > Version)
+ {
+ m_logger->error("unable to read setting values from version {} (current version: {})", version,
+ Version);
+ return false;
+ }
+
+ if (!Deserialize(root, updated, loadedSettings))
+ return false;
+
+ if (triggerEvents)
+ OnSettingsLoaded();
+
+ return true;
+}
+
+bool CSettingsManager::Save(
+ const ISettingsValueSerializer* serializer, std::string& serializedValues) const
+{
+ if (serializer == nullptr)
+ return false;
+
+ std::shared_lock<CSharedSection> lock(m_critical);
+ std::shared_lock<CSharedSection> settingsLock(m_settingsCritical);
+ if (!m_initialized)
+ return false;
+
+ if (!OnSettingsSaving())
+ return false;
+
+ serializedValues = serializer->SerializeValues(this);
+
+ OnSettingsSaved();
+
+ return true;
+}
+
+void CSettingsManager::Unload()
+{
+ std::unique_lock<CSharedSection> lock(m_settingsCritical);
+ if (!m_loaded)
+ return;
+
+ // needs to be set before calling CSetting::Reset() to avoid calls to
+ // OnSettingChanging() and OnSettingChanged()
+ m_loaded = false;
+
+ for (auto& setting : m_settings)
+ setting.second.setting->Reset();
+
+ OnSettingsUnloaded();
+}
+
+void CSettingsManager::Clear()
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ Unload();
+
+ m_settings.clear();
+ m_sections.clear();
+
+ OnSettingsCleared();
+
+ m_initialized = false;
+}
+
+bool CSettingsManager::LoadSetting(const TiXmlNode *node, const std::string &settingId)
+{
+ bool updated = false;
+ return LoadSetting(node, settingId, updated);
+}
+
+bool CSettingsManager::LoadSetting(const TiXmlNode *node, const std::string &settingId, bool &updated)
+{
+ updated = false;
+
+ if (node == nullptr)
+ return false;
+
+ auto setting = GetSetting(settingId);
+ if (setting == nullptr)
+ return false;
+
+ return LoadSetting(node, setting, updated);
+}
+
+void CSettingsManager::SetInitialized()
+{
+ std::unique_lock<CSharedSection> lock(m_settingsCritical);
+ if (m_initialized)
+ return;
+
+ m_initialized = true;
+
+ // resolve any reference settings
+ for (const auto& section : m_sections)
+ ResolveReferenceSettings(section.second);
+
+ // remove any incomplete settings
+ CleanupIncompleteSettings();
+
+ // figure out all the dependencies between settings
+ for (const auto& setting : m_settings)
+ ResolveSettingDependencies(setting.second);
+}
+
+void CSettingsManager::AddSection(const SettingSectionPtr& section)
+{
+ if (section == nullptr)
+ return;
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+ std::unique_lock<CSharedSection> settingsLock(m_settingsCritical);
+
+ section->CheckRequirements();
+ m_sections[section->GetId()] = section;
+
+ // get all settings and add them to the settings map
+ std::set<SettingPtr> newSettings;
+ for (const auto& category : section->GetCategories())
+ {
+ category->CheckRequirements();
+ for (auto& group : category->GetGroups())
+ {
+ group->CheckRequirements();
+ for (const auto& setting : group->GetSettings())
+ {
+ AddSetting(setting);
+
+ newSettings.insert(setting);
+ }
+ }
+ }
+
+ if (m_initialized && !newSettings.empty())
+ {
+ // resolve any reference settings in the new section
+ ResolveReferenceSettings(section);
+
+ // cleanup any newly added incomplete settings
+ CleanupIncompleteSettings();
+
+ // resolve any dependencies for the newly added settings
+ for (const auto& setting : newSettings)
+ ResolveSettingDependencies(setting);
+ }
+}
+
+bool CSettingsManager::AddSetting(const std::shared_ptr<CSetting>& setting,
+ const std::shared_ptr<CSettingSection>& section,
+ const std::shared_ptr<CSettingCategory>& category,
+ const std::shared_ptr<CSettingGroup>& group)
+{
+ if (setting == nullptr || section == nullptr || category == nullptr || group == nullptr)
+ return false;
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+ std::unique_lock<CSharedSection> settingsLock(m_settingsCritical);
+
+ // check if a setting with the given ID already exists
+ if (FindSetting(setting->GetId()) != m_settings.end())
+ return false;
+
+ // if the given setting has not been added to the group yet, do it now
+ auto settings = group->GetSettings();
+ if (std::find(settings.begin(), settings.end(), setting) == settings.end())
+ group->AddSetting(setting);
+
+ // if the given group has not been added to the category yet, do it now
+ auto groups = category->GetGroups();
+ if (std::find(groups.begin(), groups.end(), group) == groups.end())
+ category->AddGroup(group);
+
+ // if the given category has not been added to the section yet, do it now
+ auto categories = section->GetCategories();
+ if (std::find(categories.begin(), categories.end(), category) == categories.end())
+ section->AddCategory(category);
+
+ // check if the given section exists and matches
+ auto sectionPtr = GetSection(section->GetId());
+ if (sectionPtr != nullptr && sectionPtr != section)
+ return false;
+
+ // if the section doesn't exist yet, add it
+ if (sectionPtr == nullptr)
+ AddSection(section);
+ else
+ {
+ // add the setting
+ AddSetting(setting);
+
+ if (m_initialized)
+ {
+ // cleanup any newly added incomplete setting
+ CleanupIncompleteSettings();
+
+ // resolve any dependencies for the newly added setting
+ ResolveSettingDependencies(setting);
+ }
+ }
+
+ return true;
+}
+
+void CSettingsManager::RegisterCallback(ISettingCallback *callback, const std::set<std::string> &settingList)
+{
+ std::unique_lock<CSharedSection> lock(m_settingsCritical);
+ if (callback == nullptr)
+ return;
+
+ for (const auto& setting : settingList)
+ {
+ auto itSetting = FindSetting(setting);
+ if (itSetting == m_settings.end())
+ {
+ if (m_initialized)
+ continue;
+
+ Setting tmpSetting = {};
+ std::pair<SettingMap::iterator, bool> tmpIt = InsertSetting(setting, tmpSetting);
+ itSetting = tmpIt.first;
+ }
+
+ itSetting->second.callbacks.insert(callback);
+ }
+}
+
+void CSettingsManager::UnregisterCallback(ISettingCallback *callback)
+{
+ std::unique_lock<CSharedSection> lock(m_settingsCritical);
+ for (auto& setting : m_settings)
+ setting.second.callbacks.erase(callback);
+}
+
+void CSettingsManager::RegisterSettingType(const std::string &settingType, ISettingCreator *settingCreator)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ if (settingType.empty() || settingCreator == nullptr)
+ return;
+
+ auto creatorIt = m_settingCreators.find(settingType);
+ if (creatorIt == m_settingCreators.end())
+ m_settingCreators.insert(std::make_pair(settingType, settingCreator));
+}
+
+void CSettingsManager::RegisterSettingControl(const std::string &controlType, ISettingControlCreator *settingControlCreator)
+{
+ if (controlType.empty() || settingControlCreator == nullptr)
+ return;
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+ auto creatorIt = m_settingControlCreators.find(controlType);
+ if (creatorIt == m_settingControlCreators.end())
+ m_settingControlCreators.insert(std::make_pair(controlType, settingControlCreator));
+}
+
+void CSettingsManager::RegisterSettingsHandler(ISettingsHandler *settingsHandler, bool bFront /* = false */)
+{
+ if (settingsHandler == nullptr)
+ return;
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+ if (find(m_settingsHandlers.begin(), m_settingsHandlers.end(), settingsHandler) == m_settingsHandlers.end())
+ {
+ if (bFront)
+ m_settingsHandlers.insert(m_settingsHandlers.begin(), settingsHandler);
+ else
+ m_settingsHandlers.emplace_back(settingsHandler);
+ }
+}
+
+void CSettingsManager::UnregisterSettingsHandler(ISettingsHandler *settingsHandler)
+{
+ if (settingsHandler == nullptr)
+ return;
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+ auto it = std::find(m_settingsHandlers.begin(), m_settingsHandlers.end(), settingsHandler);
+ if (it != m_settingsHandlers.end())
+ m_settingsHandlers.erase(it);
+}
+
+void CSettingsManager::RegisterSettingOptionsFiller(const std::string &identifier, IntegerSettingOptionsFiller optionsFiller)
+{
+ if (identifier.empty() || optionsFiller == nullptr)
+ return;
+
+ RegisterSettingOptionsFiller(identifier, reinterpret_cast<void*>(optionsFiller), SettingOptionsFillerType::Integer);
+}
+
+void CSettingsManager::RegisterSettingOptionsFiller(const std::string &identifier, StringSettingOptionsFiller optionsFiller)
+{
+ if (identifier.empty() || optionsFiller == nullptr)
+ return;
+
+ RegisterSettingOptionsFiller(identifier, reinterpret_cast<void*>(optionsFiller), SettingOptionsFillerType::String);
+}
+
+void CSettingsManager::UnregisterSettingOptionsFiller(const std::string &identifier)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ m_optionsFillers.erase(identifier);
+}
+
+void* CSettingsManager::GetSettingOptionsFiller(const SettingConstPtr& setting)
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ if (setting == nullptr)
+ return nullptr;
+
+ // get the option filler's identifier
+ std::string filler;
+ if (setting->GetType() == SettingType::Integer)
+ filler = std::static_pointer_cast<const CSettingInt>(setting)->GetOptionsFillerName();
+ else if (setting->GetType() == SettingType::String)
+ filler = std::static_pointer_cast<const CSettingString>(setting)->GetOptionsFillerName();
+
+ if (filler.empty())
+ return nullptr;
+
+ // check if such an option filler is known
+ auto fillerIt = m_optionsFillers.find(filler);
+ if (fillerIt == m_optionsFillers.end())
+ return nullptr;
+
+ if (fillerIt->second.filler == nullptr)
+ return nullptr;
+
+ // make sure the option filler's type matches the setting's type
+ switch (fillerIt->second.type)
+ {
+ case SettingOptionsFillerType::Integer:
+ {
+ if (setting->GetType() != SettingType::Integer)
+ return nullptr;
+
+ break;
+ }
+
+ case SettingOptionsFillerType::String:
+ {
+ if (setting->GetType() != SettingType::String)
+ return nullptr;
+
+ break;
+ }
+
+ default:
+ return nullptr;
+ }
+
+ return fillerIt->second.filler;
+}
+
+bool CSettingsManager::HasSettings() const
+{
+ return !m_settings.empty();
+}
+
+SettingPtr CSettingsManager::GetSetting(const std::string &id) const
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ if (id.empty())
+ return nullptr;
+
+ auto setting = FindSetting(id);
+ if (setting != m_settings.end())
+ {
+ if (setting->second.setting->IsReference())
+ return GetSetting(setting->second.setting->GetReferencedId());
+ return setting->second.setting;
+ }
+
+ m_logger->debug("requested setting ({}) was not found.", id);
+ return nullptr;
+}
+
+SettingSectionList CSettingsManager::GetSections() const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ SettingSectionList sections;
+ for (const auto& section : m_sections)
+ sections.push_back(section.second);
+
+ return sections;
+}
+
+SettingSectionPtr CSettingsManager::GetSection(std::string section) const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ if (section.empty())
+ return nullptr;
+
+ StringUtils::ToLower(section);
+
+ auto sectionIt = m_sections.find(section);
+ if (sectionIt != m_sections.end())
+ return sectionIt->second;
+
+ m_logger->debug("requested setting section ({}) was not found.", section);
+ return nullptr;
+}
+
+SettingDependencyMap CSettingsManager::GetDependencies(const std::string &id) const
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ auto setting = FindSetting(id);
+ if (setting == m_settings.end())
+ return SettingDependencyMap();
+
+ return setting->second.dependencies;
+}
+
+SettingDependencyMap CSettingsManager::GetDependencies(const SettingConstPtr& setting) const
+{
+ if (setting == nullptr)
+ return SettingDependencyMap();
+
+ return GetDependencies(setting->GetId());
+}
+
+bool CSettingsManager::GetBool(const std::string &id) const
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::Boolean)
+ return false;
+
+ return std::static_pointer_cast<CSettingBool>(setting)->GetValue();
+}
+
+bool CSettingsManager::SetBool(const std::string &id, bool value)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::Boolean)
+ return false;
+
+ return std::static_pointer_cast<CSettingBool>(setting)->SetValue(value);
+}
+
+bool CSettingsManager::ToggleBool(const std::string &id)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::Boolean)
+ return false;
+
+ return SetBool(id, !std::static_pointer_cast<CSettingBool>(setting)->GetValue());
+}
+
+int CSettingsManager::GetInt(const std::string &id) const
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::Integer)
+ return 0;
+
+ return std::static_pointer_cast<CSettingInt>(setting)->GetValue();
+}
+
+bool CSettingsManager::SetInt(const std::string &id, int value)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::Integer)
+ return false;
+
+ return std::static_pointer_cast<CSettingInt>(setting)->SetValue(value);
+}
+
+double CSettingsManager::GetNumber(const std::string &id) const
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::Number)
+ return 0.0;
+
+ return std::static_pointer_cast<CSettingNumber>(setting)->GetValue();
+}
+
+bool CSettingsManager::SetNumber(const std::string &id, double value)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::Number)
+ return false;
+
+ return std::static_pointer_cast<CSettingNumber>(setting)->SetValue(value);
+}
+
+std::string CSettingsManager::GetString(const std::string &id) const
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::String)
+ return "";
+
+ return std::static_pointer_cast<CSettingString>(setting)->GetValue();
+}
+
+bool CSettingsManager::SetString(const std::string &id, const std::string &value)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::String)
+ return false;
+
+ return std::static_pointer_cast<CSettingString>(setting)->SetValue(value);
+}
+
+std::vector< std::shared_ptr<CSetting> > CSettingsManager::GetList(const std::string &id) const
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::List)
+ return std::vector< std::shared_ptr<CSetting> >();
+
+ return std::static_pointer_cast<CSettingList>(setting)->GetValue();
+}
+
+bool CSettingsManager::SetList(const std::string &id, const std::vector< std::shared_ptr<CSetting> > &value)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::List)
+ return false;
+
+ return std::static_pointer_cast<CSettingList>(setting)->SetValue(value);
+}
+
+bool CSettingsManager::SetDefault(const std::string &id)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr)
+ return false;
+
+ setting->Reset();
+ return true;
+}
+
+void CSettingsManager::SetDefaults()
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ for (auto& setting : m_settings)
+ setting.second.setting->Reset();
+}
+
+void CSettingsManager::AddCondition(const std::string &condition)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ if (condition.empty())
+ return;
+
+ m_conditions.AddCondition(condition);
+}
+
+void CSettingsManager::AddDynamicCondition(const std::string &identifier, SettingConditionCheck condition, void *data /*= nullptr*/)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ if (identifier.empty() || condition == nullptr)
+ return;
+
+ m_conditions.AddDynamicCondition(identifier, condition, data);
+}
+
+void CSettingsManager::RemoveDynamicCondition(const std::string &identifier)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ if (identifier.empty())
+ return;
+
+ m_conditions.RemoveDynamicCondition(identifier);
+}
+
+bool CSettingsManager::Serialize(TiXmlNode *parent) const
+{
+ if (parent == nullptr)
+ return false;
+
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+
+ for (const auto& setting : m_settings)
+ {
+ if (setting.second.setting->IsReference() ||
+ setting.second.setting->GetType() == SettingType::Action)
+ continue;
+
+ TiXmlElement settingElement(SETTING_XML_ELM_SETTING);
+ settingElement.SetAttribute(SETTING_XML_ATTR_ID, setting.second.setting->GetId());
+
+ // add the default attribute
+ if (setting.second.setting->IsDefault())
+ settingElement.SetAttribute(SETTING_XML_ELM_DEFAULT, "true");
+
+ // add the value
+ TiXmlText value(setting.second.setting->ToString());
+ settingElement.InsertEndChild(value);
+
+ if (parent->InsertEndChild(settingElement) == nullptr)
+ {
+ m_logger->warn("unable to write <" SETTING_XML_ELM_SETTING " id=\"{}\"> tag",
+ setting.second.setting->GetId());
+ continue;
+ }
+ }
+
+ return true;
+}
+
+bool CSettingsManager::Deserialize(const TiXmlNode *node, bool &updated, std::map<std::string, SettingPtr> *loadedSettings /* = nullptr */)
+{
+ updated = false;
+
+ if (node == nullptr)
+ return false;
+
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+
+ // TODO: ideally this would be done by going through all <setting> elements
+ // in node but as long as we have to support the v1- format that's not possible
+ for (auto& setting : m_settings)
+ {
+ bool settingUpdated = false;
+ if (LoadSetting(node, setting.second.setting, settingUpdated))
+ {
+ updated |= settingUpdated;
+ if (loadedSettings != nullptr)
+ loadedSettings->insert(make_pair(setting.first, setting.second.setting));
+ }
+ }
+
+ return true;
+}
+
+bool CSettingsManager::OnSettingChanging(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == nullptr)
+ return false;
+
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ if (!m_loaded)
+ return true;
+
+ auto settingIt = FindSetting(setting->GetId());
+ if (settingIt == m_settings.end())
+ return false;
+
+ Setting settingData = settingIt->second;
+ // now that we have a copy of the setting's data, we can leave the lock
+ lock.unlock();
+
+ for (auto& callback : settingData.callbacks)
+ {
+ if (!callback->OnSettingChanging(setting))
+ return false;
+ }
+
+ // if this is a reference setting apply the same change to the referenced setting
+ if (setting->IsReference())
+ {
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ auto referencedSettingIt = FindSetting(setting->GetReferencedId());
+ if (referencedSettingIt != m_settings.end())
+ {
+ Setting referencedSettingData = referencedSettingIt->second;
+ // now that we have a copy of the setting's data, we can leave the lock
+ lock.unlock();
+
+ referencedSettingData.setting->FromString(setting->ToString());
+ }
+ }
+ else if (!settingData.references.empty())
+ {
+ // if the changed setting is referenced by other settings apply the same change to the referencing settings
+ std::unordered_set<SettingPtr> referenceSettings;
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ for (const auto& reference : settingData.references)
+ {
+ auto referenceSettingIt = FindSetting(reference);
+ if (referenceSettingIt != m_settings.end())
+ referenceSettings.insert(referenceSettingIt->second.setting);
+ }
+ // now that we have a copy of the setting's data, we can leave the lock
+ lock.unlock();
+
+ for (auto& referenceSetting : referenceSettings)
+ referenceSetting->FromString(setting->ToString());
+ }
+
+ return true;
+}
+
+void CSettingsManager::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ if (!m_loaded || setting == nullptr)
+ return;
+
+ auto settingIt = FindSetting(setting->GetId());
+ if (settingIt == m_settings.end())
+ return;
+
+ Setting settingData = settingIt->second;
+ // now that we have a copy of the setting's data, we can leave the lock
+ lock.unlock();
+
+ for (auto& callback : settingData.callbacks)
+ callback->OnSettingChanged(setting);
+
+ // now handle any settings which depend on the changed setting
+ auto dependencies = GetDependencies(setting);
+ for (const auto& deps : dependencies)
+ {
+ for (const auto& dep : deps.second)
+ UpdateSettingByDependency(deps.first, dep);
+ }
+}
+
+void CSettingsManager::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ if (!m_loaded || setting == nullptr)
+ return;
+
+ auto settingIt = FindSetting(setting->GetId());
+ if (settingIt == m_settings.end())
+ return;
+
+ Setting settingData = settingIt->second;
+ // now that we have a copy of the setting's data, we can leave the lock
+ lock.unlock();
+
+ for (auto& callback : settingData.callbacks)
+ callback->OnSettingAction(setting);
+}
+
+bool CSettingsManager::OnSettingUpdate(const SettingPtr& setting,
+ const char* oldSettingId,
+ const TiXmlNode* oldSettingNode)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ if (setting == nullptr)
+ return false;
+
+ auto settingIt = FindSetting(setting->GetId());
+ if (settingIt == m_settings.end())
+ return false;
+
+ Setting settingData = settingIt->second;
+ // now that we have a copy of the setting's data, we can leave the lock
+ lock.unlock();
+
+ bool ret = false;
+ for (auto& callback : settingData.callbacks)
+ ret |= callback->OnSettingUpdate(setting, oldSettingId, oldSettingNode);
+
+ return ret;
+}
+
+void CSettingsManager::OnSettingPropertyChanged(const std::shared_ptr<const CSetting>& setting,
+ const char* propertyName)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ if (!m_loaded || setting == nullptr)
+ return;
+
+ auto settingIt = FindSetting(setting->GetId());
+ if (settingIt == m_settings.end())
+ return;
+
+ Setting settingData = settingIt->second;
+ // now that we have a copy of the setting's data, we can leave the lock
+ lock.unlock();
+
+ for (auto& callback : settingData.callbacks)
+ callback->OnSettingPropertyChanged(setting, propertyName);
+
+ // check the changed property and if it may have an influence on the
+ // children of the setting
+ SettingDependencyType dependencyType = SettingDependencyType::Unknown;
+ if (StringUtils::EqualsNoCase(propertyName, "enabled"))
+ dependencyType = SettingDependencyType::Enable;
+ else if (StringUtils::EqualsNoCase(propertyName, "visible"))
+ dependencyType = SettingDependencyType::Visible;
+
+ if (dependencyType != SettingDependencyType::Unknown)
+ {
+ for (const auto& child : settingIt->second.children)
+ UpdateSettingByDependency(child, dependencyType);
+ }
+}
+
+SettingPtr CSettingsManager::CreateSetting(const std::string &settingType, const std::string &settingId, CSettingsManager *settingsManager /* = nullptr */) const
+{
+ if (StringUtils::EqualsNoCase(settingType, "boolean"))
+ return std::make_shared<CSettingBool>(settingId, const_cast<CSettingsManager*>(this));
+ else if (StringUtils::EqualsNoCase(settingType, "integer"))
+ return std::make_shared<CSettingInt>(settingId, const_cast<CSettingsManager*>(this));
+ else if (StringUtils::EqualsNoCase(settingType, "number"))
+ return std::make_shared<CSettingNumber>(settingId, const_cast<CSettingsManager*>(this));
+ else if (StringUtils::EqualsNoCase(settingType, "string"))
+ return std::make_shared<CSettingString>(settingId, const_cast<CSettingsManager*>(this));
+ else if (StringUtils::EqualsNoCase(settingType, "action"))
+ return std::make_shared<CSettingAction>(settingId, const_cast<CSettingsManager*>(this));
+ else if (settingType.size() > 6 &&
+ StringUtils::StartsWith(settingType, "list[") &&
+ StringUtils::EndsWith(settingType, "]"))
+ {
+ std::string elementType = StringUtils::Mid(settingType, 5, settingType.size() - 6);
+ SettingPtr elementSetting = CreateSetting(elementType, settingId + ".definition", const_cast<CSettingsManager*>(this));
+ if (elementSetting != nullptr)
+ return std::make_shared<CSettingList>(settingId, elementSetting, const_cast<CSettingsManager*>(this));
+ }
+
+ std::shared_lock<CSharedSection> lock(m_critical);
+ auto creator = m_settingCreators.find(settingType);
+ if (creator != m_settingCreators.end())
+ return creator->second->CreateSetting(settingType, settingId, const_cast<CSettingsManager*>(this));
+
+ return nullptr;
+}
+
+std::shared_ptr<ISettingControl> CSettingsManager::CreateControl(const std::string &controlType) const
+{
+ if (controlType.empty())
+ return nullptr;
+
+ std::shared_lock<CSharedSection> lock(m_critical);
+ auto creator = m_settingControlCreators.find(controlType);
+ if (creator != m_settingControlCreators.end() && creator->second != nullptr)
+ return creator->second->CreateControl(controlType);
+
+ return nullptr;
+}
+
+bool CSettingsManager::OnSettingsLoading()
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ for (const auto& settingsHandler : m_settingsHandlers)
+ {
+ if (!settingsHandler->OnSettingsLoading())
+ return false;
+ }
+
+ return true;
+}
+
+void CSettingsManager::OnSettingsUnloaded()
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ for (const auto& settingsHandler : m_settingsHandlers)
+ settingsHandler->OnSettingsUnloaded();
+}
+
+void CSettingsManager::OnSettingsLoaded()
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ for (const auto& settingsHandler : m_settingsHandlers)
+ settingsHandler->OnSettingsLoaded();
+}
+
+bool CSettingsManager::OnSettingsSaving() const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ for (const auto& settingsHandler : m_settingsHandlers)
+ {
+ if (!settingsHandler->OnSettingsSaving())
+ return false;
+ }
+
+ return true;
+}
+
+void CSettingsManager::OnSettingsSaved() const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ for (const auto& settingsHandler : m_settingsHandlers)
+ settingsHandler->OnSettingsSaved();
+}
+
+void CSettingsManager::OnSettingsCleared()
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ for (const auto& settingsHandler : m_settingsHandlers)
+ settingsHandler->OnSettingsCleared();
+}
+
+bool CSettingsManager::LoadSetting(const TiXmlNode* node, const SettingPtr& setting, bool& updated)
+{
+ updated = false;
+
+ if (node == nullptr || setting == nullptr)
+ return false;
+
+ if (setting->GetType() == SettingType::Action)
+ return false;
+
+ auto settingId = setting->GetId();
+ if (setting->IsReference())
+ settingId = setting->GetReferencedId();
+
+ const TiXmlElement* settingElement = nullptr;
+ // try to split the setting identifier into category and subsetting identifier (v1-)
+ std::string categoryTag, settingTag;
+ if (ParseSettingIdentifier(settingId, categoryTag, settingTag))
+ {
+ auto categoryNode = node;
+ if (!categoryTag.empty())
+ categoryNode = node->FirstChild(categoryTag);
+
+ if (categoryNode != nullptr)
+ settingElement = categoryNode->FirstChildElement(settingTag);
+ }
+
+ if (settingElement == nullptr)
+ {
+ // check if the setting is stored using its full setting identifier (v2+)
+ settingElement = node->FirstChildElement(SETTING_XML_ELM_SETTING);
+ while (settingElement != nullptr)
+ {
+ const auto id = settingElement->Attribute(SETTING_XML_ATTR_ID);
+ if (id != nullptr && settingId.compare(id) == 0)
+ break;
+
+ settingElement = settingElement->NextSiblingElement(SETTING_XML_ELM_SETTING);
+ }
+ }
+
+ if (settingElement == nullptr)
+ return false;
+
+ // check if the default="true" attribute is set for the value
+ auto isDefaultAttribute = settingElement->Attribute(SETTING_XML_ELM_DEFAULT);
+ bool isDefault = isDefaultAttribute != nullptr && StringUtils::EqualsNoCase(isDefaultAttribute, "true");
+
+ if (!setting->FromString(settingElement->FirstChild() != nullptr ? settingElement->FirstChild()->ValueStr() : StringUtils::Empty))
+ {
+ m_logger->warn("unable to read value of setting \"{}\"", settingId);
+ return false;
+ }
+
+ // check if we need to perform any update logic for the setting
+ auto updates = setting->GetUpdates();
+ for (const auto& update : updates)
+ updated |= UpdateSetting(node, setting, update);
+
+ // the setting's value hasn't been updated and is the default value
+ // so we can reset it to the default value (in case the default value has changed)
+ if (!updated && isDefault)
+ setting->Reset();
+
+ return true;
+}
+
+bool CSettingsManager::UpdateSetting(const TiXmlNode* node,
+ const SettingPtr& setting,
+ const CSettingUpdate& update)
+{
+ if (node == nullptr || setting == nullptr || update.GetType() == SettingUpdateType::Unknown)
+ return false;
+
+ bool updated = false;
+ const char *oldSetting = nullptr;
+ const TiXmlNode *oldSettingNode = nullptr;
+ if (update.GetType() == SettingUpdateType::Rename)
+ {
+ if (update.GetValue().empty())
+ return false;
+
+ oldSetting = update.GetValue().c_str();
+ std::string categoryTag, settingTag;
+ if (!ParseSettingIdentifier(oldSetting, categoryTag, settingTag))
+ return false;
+
+ auto categoryNode = node;
+ if (!categoryTag.empty())
+ {
+ categoryNode = node->FirstChild(categoryTag);
+ if (categoryNode == nullptr)
+ return false;
+ }
+
+ oldSettingNode = categoryNode->FirstChild(settingTag);
+ if (oldSettingNode == nullptr)
+ return false;
+
+ if (setting->FromString(oldSettingNode->FirstChild() != nullptr ? oldSettingNode->FirstChild()->ValueStr() : StringUtils::Empty))
+ updated = true;
+ else
+ m_logger->warn("unable to update \"{}\" through automatically renaming from \"{}\"",
+ setting->GetId(), oldSetting);
+ }
+
+ updated |= OnSettingUpdate(setting, oldSetting, oldSettingNode);
+ return updated;
+}
+
+void CSettingsManager::UpdateSettingByDependency(const std::string &settingId, const CSettingDependency &dependency)
+{
+ UpdateSettingByDependency(settingId, dependency.GetType());
+}
+
+void CSettingsManager::UpdateSettingByDependency(const std::string &settingId, SettingDependencyType dependencyType)
+{
+ auto settingIt = FindSetting(settingId);
+ if (settingIt == m_settings.end())
+ return;
+ SettingPtr setting = settingIt->second.setting;
+ if (setting == nullptr)
+ return;
+
+ switch (dependencyType)
+ {
+ case SettingDependencyType::Enable:
+ // just trigger the property changed callback and a call to
+ // CSetting::IsEnabled() will automatically determine the new
+ // enabled state
+ OnSettingPropertyChanged(setting, "enabled");
+ break;
+
+ case SettingDependencyType::Update:
+ {
+ SettingType type = setting->GetType();
+ if (type == SettingType::Integer)
+ {
+ auto settingInt = std::static_pointer_cast<CSettingInt>(setting);
+ if (settingInt->GetOptionsType() == SettingOptionsType::Dynamic)
+ settingInt->UpdateDynamicOptions();
+ }
+ else if (type == SettingType::String)
+ {
+ auto settingString = std::static_pointer_cast<CSettingString>(setting);
+ if (settingString->GetOptionsType() == SettingOptionsType::Dynamic)
+ settingString->UpdateDynamicOptions();
+ }
+ break;
+ }
+
+ case SettingDependencyType::Visible:
+ // just trigger the property changed callback and a call to
+ // CSetting::IsVisible() will automatically determine the new
+ // visible state
+ OnSettingPropertyChanged(setting, "visible");
+ break;
+
+ case SettingDependencyType::Unknown:
+ default:
+ break;
+ }
+}
+
+void CSettingsManager::AddSetting(const std::shared_ptr<CSetting>& setting)
+{
+ setting->CheckRequirements();
+
+ auto addedSetting = FindSetting(setting->GetId());
+ if (addedSetting == m_settings.end())
+ {
+ Setting tmpSetting = {};
+ auto tmpIt = InsertSetting(setting->GetId(), tmpSetting);
+ addedSetting = tmpIt.first;
+ }
+
+ if (addedSetting->second.setting == nullptr)
+ {
+ addedSetting->second.setting = setting;
+ setting->SetCallback(this);
+ }
+}
+
+void CSettingsManager::ResolveReferenceSettings(const std::shared_ptr<CSettingSection>& section)
+{
+ struct GroupedReferenceSettings
+ {
+ SettingPtr referencedSetting;
+ std::unordered_set<SettingPtr> referenceSettings;
+ };
+ std::map<std::string, GroupedReferenceSettings> groupedReferenceSettings;
+
+ // collect and group all reference(d) settings
+ auto categories = section->GetCategories();
+ for (const auto& category : categories)
+ {
+ auto groups = category->GetGroups();
+ for (auto& group : groups)
+ {
+ auto settings = group->GetSettings();
+ for (const auto& setting : settings)
+ {
+ if (setting->IsReference())
+ {
+ auto referencedSettingId = setting->GetReferencedId();
+ auto itGroupedReferenceSetting = groupedReferenceSettings.find(referencedSettingId);
+ if (itGroupedReferenceSetting == groupedReferenceSettings.end())
+ {
+ SettingPtr referencedSetting = nullptr;
+ auto itReferencedSetting = FindSetting(referencedSettingId);
+ if (itReferencedSetting == m_settings.end())
+ {
+ m_logger->warn("missing referenced setting \"{}\"", referencedSettingId);
+ continue;
+ }
+
+ GroupedReferenceSettings groupedReferenceSetting;
+ groupedReferenceSetting.referencedSetting = itReferencedSetting->second.setting;
+
+ itGroupedReferenceSetting = groupedReferenceSettings.insert(
+ std::make_pair(referencedSettingId, groupedReferenceSetting)).first;
+ }
+
+ itGroupedReferenceSetting->second.referenceSettings.insert(setting);
+ }
+ }
+ }
+ }
+
+ if (groupedReferenceSettings.empty())
+ return;
+
+ // merge all reference settings into the referenced setting
+ for (const auto& groupedReferenceSetting : groupedReferenceSettings)
+ {
+ auto itReferencedSetting = FindSetting(groupedReferenceSetting.first);
+ if (itReferencedSetting == m_settings.end())
+ continue;
+
+ for (const auto& referenceSetting : groupedReferenceSetting.second.referenceSettings)
+ {
+ groupedReferenceSetting.second.referencedSetting->MergeDetails(*referenceSetting);
+
+ itReferencedSetting->second.references.insert(referenceSetting->GetId());
+ }
+ }
+
+ // resolve any reference settings
+ for (const auto& category : categories)
+ {
+ auto groups = category->GetGroups();
+ for (auto& group : groups)
+ {
+ auto settings = group->GetSettings();
+ for (const auto& setting : settings)
+ {
+ if (setting->IsReference())
+ {
+ auto referencedSettingId = setting->GetReferencedId();
+ auto itGroupedReferenceSetting = groupedReferenceSettings.find(referencedSettingId);
+ if (itGroupedReferenceSetting != groupedReferenceSettings.end())
+ {
+ const auto referencedSetting = itGroupedReferenceSetting->second.referencedSetting;
+
+ // clone the referenced setting and copy the general properties of the reference setting
+ auto clonedReferencedSetting = referencedSetting->Clone(setting->GetId());
+ clonedReferencedSetting->SetReferencedId(referencedSettingId);
+ clonedReferencedSetting->MergeBasics(*setting);
+
+ group->ReplaceSetting(setting, clonedReferencedSetting);
+
+ // update the setting
+ auto itReferenceSetting = FindSetting(setting->GetId());
+ if (itReferenceSetting != m_settings.end())
+ itReferenceSetting->second.setting = clonedReferencedSetting;
+ }
+ }
+ }
+ }
+ }
+}
+
+void CSettingsManager::CleanupIncompleteSettings()
+{
+ // remove any empty and reference settings
+ for (auto setting = m_settings.begin(); setting != m_settings.end(); )
+ {
+ auto tmpIterator = setting++;
+ if (tmpIterator->second.setting == nullptr)
+ {
+ m_logger->warn("removing empty setting \"{}\"", tmpIterator->first);
+ m_settings.erase(tmpIterator);
+ }
+ }
+}
+
+void CSettingsManager::RegisterSettingOptionsFiller(const std::string &identifier, void *filler, SettingOptionsFillerType type)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ auto it = m_optionsFillers.find(identifier);
+ if (it != m_optionsFillers.end())
+ return;
+
+ SettingOptionsFiller optionsFiller = { filler, type };
+ m_optionsFillers.insert(make_pair(identifier, optionsFiller));
+}
+
+void CSettingsManager::ResolveSettingDependencies(const std::shared_ptr<CSetting>& setting)
+{
+ if (setting == nullptr)
+ return;
+
+ ResolveSettingDependencies(FindSetting(setting->GetId())->second);
+}
+
+void CSettingsManager::ResolveSettingDependencies(const Setting& setting)
+{
+ if (setting.setting == nullptr)
+ return;
+
+ // if the setting has a parent setting, add it to its children
+ auto parentSettingId = setting.setting->GetParent();
+ if (!parentSettingId.empty())
+ {
+ auto itParentSetting = FindSetting(parentSettingId);
+ if (itParentSetting != m_settings.end())
+ itParentSetting->second.children.insert(setting.setting->GetId());
+ }
+
+ // handle all dependencies of the setting
+ const auto& dependencies = setting.setting->GetDependencies();
+ for (const auto& deps : dependencies)
+ {
+ const auto settingIds = deps.GetSettings();
+ for (const auto& settingId : settingIds)
+ {
+ auto settingIt = FindSetting(settingId);
+ if (settingIt == m_settings.end())
+ continue;
+
+ bool newDep = true;
+ auto& settingDeps = settingIt->second.dependencies[setting.setting->GetId()];
+ for (const auto& dep : settingDeps)
+ {
+ if (dep.GetType() == deps.GetType())
+ {
+ newDep = false;
+ break;
+ }
+ }
+
+ if (newDep)
+ settingDeps.push_back(deps);
+ }
+ }
+}
+
+CSettingsManager::SettingMap::const_iterator CSettingsManager::FindSetting(std::string settingId) const
+{
+ StringUtils::ToLower(settingId);
+ return m_settings.find(settingId);
+}
+
+CSettingsManager::SettingMap::iterator CSettingsManager::FindSetting(std::string settingId)
+{
+ StringUtils::ToLower(settingId);
+ return m_settings.find(settingId);
+}
+
+std::pair<CSettingsManager::SettingMap::iterator, bool> CSettingsManager::InsertSetting(std::string settingId, const Setting& setting)
+{
+ StringUtils::ToLower(settingId);
+ return m_settings.insert(std::make_pair(settingId, setting));
+}
diff --git a/xbmc/settings/lib/SettingsManager.h b/xbmc/settings/lib/SettingsManager.h
new file mode 100644
index 0000000..161c1a9
--- /dev/null
+++ b/xbmc/settings/lib/SettingsManager.h
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "ISettingCallback.h"
+#include "ISettingControlCreator.h"
+#include "ISettingCreator.h"
+#include "ISettingsHandler.h"
+#include "ISettingsValueSerializer.h"
+#include "Setting.h"
+#include "SettingConditions.h"
+#include "SettingDefinitions.h"
+#include "SettingDependency.h"
+#include "threads/SharedSection.h"
+#include "utils/logtypes.h"
+
+#include <map>
+#include <set>
+#include <unordered_set>
+#include <vector>
+
+class CSettingCategory;
+class CSettingGroup;
+class CSettingSection;
+class CSettingUpdate;
+
+class TiXmlElement;
+class TiXmlNode;
+
+/*!
+ \ingroup settings
+ \brief Settings manager responsible for initializing, loading and handling
+ all settings.
+ */
+class CSettingsManager : public ISettingCreator,
+ public ISettingControlCreator,
+ private ISettingCallback,
+ private ISettingsHandler
+{
+public:
+ /*!
+ \brief Creates a new (uninitialized) settings manager.
+ */
+ CSettingsManager();
+ ~CSettingsManager() override;
+
+ static const uint32_t Version;
+ static const uint32_t MinimumSupportedVersion;
+
+ // implementation of ISettingCreator
+ std::shared_ptr<CSetting> CreateSetting(const std::string &settingType, const std::string &settingId, CSettingsManager *settingsManager = nullptr) const override;
+
+ // implementation of ISettingControlCreator
+ std::shared_ptr<ISettingControl> CreateControl(const std::string &controlType) const override;
+
+ uint32_t GetVersion() const { return Version; }
+ uint32_t GetMinimumSupportedVersion() const { return MinimumSupportedVersion; }
+
+ /*!
+ \brief Try to get the version of the setting definitions/values represented by the given XML element.
+
+ \param root XML element representing setting definitions/values
+ \return Version of the setting definitions/values or 0 if no version has been specified
+ */
+ uint32_t ParseVersion(const TiXmlElement* root) const;
+
+ /*!
+ \brief Initializes the settings manager using the setting definitions
+ represented by the given XML element.
+
+ \param root XML element representing setting definitions
+ \return True if the XML element was successfully deserialized into setting definitions, false otherwise
+ */
+ bool Initialize(const TiXmlElement *root);
+ /*!
+ \brief Loads setting values from the given XML element.
+
+ \param root XML element containing setting values
+ \param updated Whether some settings were automatically updated
+ \param triggerEvents Whether to trigger ISettingCallback methods
+ \param loadedSettings A list to fill with all the successfully loaded settings
+ \return True if the setting values were successfully loaded, false otherwise
+ */
+ bool Load(const TiXmlElement *root, bool &updated, bool triggerEvents = true, std::map<std::string, std::shared_ptr<CSetting>> *loadedSettings = nullptr);
+ /*!
+ \brief Saves the setting values using the given serializer.
+
+ \param serializer Settings value serializer to use
+ \return True if the setting values were successfully serialized, false otherwise
+ */
+ bool Save(const ISettingsValueSerializer* serializer, std::string& serializedValues) const;
+ /*!
+ \brief Unloads the previously loaded setting values.
+
+ The values of all the settings are reset to their default values.
+ */
+ void Unload();
+ /*!
+ \brief Clears the complete settings manager.
+
+ This removes all initialized settings, groups, categories and sections and
+ returns to the uninitialized state. Any registered callbacks or
+ implementations stay registered.
+ */
+ void Clear();
+
+ /*!
+ \brief Loads the setting being represented by the given XML node with the
+ given identifier.
+
+ \param node XML node representing the setting to load
+ \param settingId Setting identifier
+ \return True if the setting was successfully loaded from the given XML node, false otherwise
+ */
+ bool LoadSetting(const TiXmlNode *node, const std::string &settingId);
+
+ /*!
+ \brief Loads the setting being represented by the given XML node with the
+ given identifier.
+
+ \param node XML node representing the setting to load
+ \param settingId Setting identifier
+ \param updated Set to true if the setting's value was updated
+ \return True if the setting was successfully loaded from the given XML node, false otherwise
+ */
+ bool LoadSetting(const TiXmlNode *node, const std::string &settingId, bool &updated);
+
+ /*!
+ \brief Tells the settings system that the initialization is complete.
+
+ Setting values can only be loaded after a complete and successful
+ initialization of the settings system.
+ */
+ void SetInitialized();
+ /*!
+ \brief Returns whether the settings system has been initialized or not.
+ */
+ bool IsInitialized() const { return m_initialized; }
+ /*!
+ \brief Tells the settings system that all setting values
+ have been loaded.
+
+ This manual trigger is necessary to enable the ISettingCallback methods
+ being executed.
+ */
+ void SetLoaded() { m_loaded = true; }
+ /*!
+ \brief Returns whether the settings system has been loaded or not.
+ */
+ bool IsLoaded() const { return m_loaded; }
+
+ /*!
+ \brief Adds the given section, its categories, groups and settings.
+
+ This is possible before and after the setting definitions have been
+ initialized.
+ */
+ void AddSection(const std::shared_ptr<CSettingSection>& section);
+
+ /*!
+ \brief Adds the given setting to the given group in the given category in
+ the given section;
+
+ If the given section has not been added yet, it is added. If the given
+ category has not been added to the given section yet, it is added. If the
+ given group has not been added to the given category yet, it is added. If
+ the given setting has not been added to the given group yet, it is added.
+
+ This is possible before and after the setting definitions have been
+ initialized.
+
+ \param setting New setting to be added
+ \param section Section the new setting should be added to
+ \param category Category the new setting should be added to
+ \param group Group the new setting should be added to
+ \return True if the setting has been added, false otherwise
+ */
+ bool AddSetting(const std::shared_ptr<CSetting>& setting,
+ const std::shared_ptr<CSettingSection>& section,
+ const std::shared_ptr<CSettingCategory>& category,
+ const std::shared_ptr<CSettingGroup>& group);
+
+ /*!
+ \brief Registers the given ISettingCallback implementation to be triggered
+ for the given list of settings.
+
+ \param settingsHandler ISettingsHandler implementation
+ \param settingList List of settings to trigger the given ISettingCallback implementation
+ */
+ void RegisterCallback(ISettingCallback *callback, const std::set<std::string> &settingList);
+ /*!
+ \brief Unregisters the given ISettingCallback implementation.
+
+ \param callback ISettingCallback implementation
+ */
+ void UnregisterCallback(ISettingCallback *callback);
+
+ /*!
+ \brief Registers a custom setting type and its ISettingCreator
+ implementation.
+
+ When a setting definition for a registered custom setting type is found its
+ ISettingCreator implementation is called to create and deserialize the
+ setting definition.
+
+ \param settingType String representation of the custom setting type
+ \param settingCreator ISettingCreator implementation
+ */
+ void RegisterSettingType(const std::string &settingType, ISettingCreator *settingCreator);
+
+ /*!
+ \brief Registers a custom setting control type and its
+ ISettingControlCreator implementation
+
+ When a setting control definition for a registered custom setting control
+ type is found its ISettingControlCreator implementation is called to create
+ and deserialize the setting control definition.
+
+ \param controlType String representation of the custom setting control type
+ \param settingControlCreator ISettingControlCreator implementation
+ */
+ void RegisterSettingControl(const std::string &controlType, ISettingControlCreator *settingControlCreator);
+
+ /*!
+ \brief Registers the given ISettingsHandler implementation.
+
+ \param settingsHandler ISettingsHandler implementation
+ \param bFront If True, insert the handler in front of other registered handlers, insert at the end otherwise.
+ */
+ void RegisterSettingsHandler(ISettingsHandler *settingsHandler, bool bFront = false);
+ /*!
+ \brief Unregisters the given ISettingsHandler implementation.
+
+ \param settingsHandler ISettingsHandler implementation
+ */
+ void UnregisterSettingsHandler(ISettingsHandler *settingsHandler);
+
+ /*!
+ \brief Registers the given integer setting options filler under the given identifier.
+
+ \param identifier Setting options filler identifier
+ \param optionsFiller Integer setting options filler implementation
+ */
+ void RegisterSettingOptionsFiller(const std::string &identifier, IntegerSettingOptionsFiller optionsFiller);
+ /*!
+ \brief Registers the given string setting options filler under the given identifier.
+
+ \param identifier Setting options filler identifier
+ \param optionsFiller String setting options filler implementation
+ */
+ void RegisterSettingOptionsFiller(const std::string &identifier, StringSettingOptionsFiller optionsFiller);
+ /*!
+ \brief Unregisters the setting options filler registered under the given identifier.
+
+ \param identifier Setting options filler identifier
+ */
+ void UnregisterSettingOptionsFiller(const std::string &identifier);
+ /*!
+ \brief Gets the implementation of the setting options filler used by the
+ given setting.
+
+ \param setting Setting object
+ \return Implementation of the setting options filler (either IntegerSettingOptionsFiller or StringSettingOptionsFiller)
+ */
+ void* GetSettingOptionsFiller(const std::shared_ptr<const CSetting>& setting);
+
+ /*!
+ \brief Checks whether any settings have been initialized.
+
+ \return True if at least one setting has been initialized, false otherwise*/
+ bool HasSettings() const;
+
+ /*!
+ \brief Gets the setting with the given identifier.
+
+ \param id Setting identifier
+ \return Setting object with the given identifier or nullptr if the identifier is unknown
+ */
+ std::shared_ptr<CSetting> GetSetting(const std::string &id) const;
+ /*!
+ \brief Gets the full list of setting sections.
+
+ \return List of setting sections
+ */
+ std::vector<std::shared_ptr<CSettingSection>> GetSections() const;
+ /*!
+ \brief Gets the setting section with the given identifier.
+
+ \param section Setting section identifier
+ \return Setting section with the given identifier or nullptr if the identifier is unknown
+ */
+ std::shared_ptr<CSettingSection> GetSection(std::string section) const;
+ /*!
+ \brief Gets a map of settings (and their dependencies) which depend on
+ the setting with the given identifier.
+
+ It is important to note that the returned dependencies are not the
+ dependencies of the setting with the given identifier but the settings
+ (and their dependencies) which depend on the setting with the given
+ identifier.
+
+ \param id Setting identifier
+ \return Map of settings (and their dependencies) which depend on the setting with the given identifier
+ */
+ SettingDependencyMap GetDependencies(const std::string &id) const;
+ /*!
+ \brief Gets a map of settings (and their dependencies) which depend on
+ the given setting.
+
+ It is important to note that the returned dependencies are not the
+ dependencies of the given setting but the settings (and their dependencies)
+ which depend on the given setting.
+
+ \param setting Setting object
+ \return Map of settings (and their dependencies) which depend on the given setting
+ */
+ SettingDependencyMap GetDependencies(const std::shared_ptr<const CSetting>& setting) const;
+
+ /*!
+ \brief Gets the boolean value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \return Boolean value of the setting with the given identifier
+ */
+ bool GetBool(const std::string &id) const;
+ /*!
+ \brief Gets the integer value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \return Integer value of the setting with the given identifier
+ */
+ int GetInt(const std::string &id) const;
+ /*!
+ \brief Gets the real number value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \return Real number value of the setting with the given identifier
+ */
+ double GetNumber(const std::string &id) const;
+ /*!
+ \brief Gets the string value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \return String value of the setting with the given identifier
+ */
+ std::string GetString(const std::string &id) const;
+ /*!
+ \brief Gets the values of the list setting with the given identifier.
+
+ \param id Setting identifier
+ \return List of values of the setting with the given identifier
+ */
+ std::vector< std::shared_ptr<CSetting> > GetList(const std::string &id) const;
+
+ /*!
+ \brief Sets the boolean value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \param value Boolean value to set
+ \return True if setting the value was successful, false otherwise
+ */
+ bool SetBool(const std::string &id, bool value);
+ /*!
+ \brief Toggles the boolean value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \return True if toggling the boolean value was successful, false otherwise
+ */
+ bool ToggleBool(const std::string &id);
+ /*!
+ \brief Sets the integer value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \param value Integer value to set
+ \return True if setting the value was successful, false otherwise
+ */
+ bool SetInt(const std::string &id, int value);
+ /*!
+ \brief Sets the real number value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \param value Real number value to set
+ \return True if setting the value was successful, false otherwise
+ */
+ bool SetNumber(const std::string &id, double value);
+ /*!
+ \brief Sets the string value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \param value String value to set
+ \return True if setting the value was successful, false otherwise
+ */
+ bool SetString(const std::string &id, const std::string &value);
+ /*!
+ \brief Sets the values of the list setting with the given identifier.
+
+ \param id Setting identifier
+ \param value Values to set
+ \return True if setting the values was successful, false otherwise
+ */
+ bool SetList(const std::string &id, const std::vector< std::shared_ptr<CSetting> > &value);
+
+ /*!
+ \brief Sets the value of the setting to its default.
+
+ \param id Setting identifier
+ \return True if setting the value to its default was successful, false otherwise
+ */
+ bool SetDefault(const std::string &id);
+ /*!
+ \brief Sets the value of all settings to their default.
+ */
+ void SetDefaults();
+
+ /*!
+ \brief Gets the setting conditions manager used by the settings manager.
+
+ \return Setting conditions manager used by the settings manager.
+ */
+ const CSettingConditionsManager& GetConditions() const { return m_conditions; }
+ /*!
+ \brief Adds the given static condition.
+
+ A static condition is just a string. If a static condition is evaluated,
+ the result depends on whether the condition's value is defined or not.
+
+ \param condition Static condition string/value
+ */
+ void AddCondition(const std::string &condition);
+ /*!
+ \brief Adds the given dynamic condition.
+
+ A dynamic condition has an identifier and an implementation which is
+ triggered when the condition is evaluated.
+
+ \param identifier Identifier of the dynamic condition
+ \param condition Implementation of the dynamic condition
+ \param data Opaque data pointer, will be passed back to SettingConditionCheck function
+ */
+ void AddDynamicCondition(const std::string &identifier, SettingConditionCheck condition, void *data = nullptr);
+
+ /*!
+ \brief Removes the given dynamic condition.
+
+ \param identifier Identifier of the dynamic condition
+ */
+ void RemoveDynamicCondition(const std::string &identifier);
+
+private:
+ // implementation of ISettingCallback
+ bool OnSettingChanging(const std::shared_ptr<const CSetting>& setting) override;
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+ void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override;
+ bool OnSettingUpdate(const std::shared_ptr<CSetting>& setting,
+ const char* oldSettingId,
+ const TiXmlNode* oldSettingNode) override;
+ void OnSettingPropertyChanged(const std::shared_ptr<const CSetting>& setting,
+ const char* propertyName) override;
+
+ // implementation of ISettingsHandler
+ bool OnSettingsLoading() override;
+ void OnSettingsLoaded() override;
+ void OnSettingsUnloaded() override;
+ bool OnSettingsSaving() const override;
+ void OnSettingsSaved() const override;
+ void OnSettingsCleared() override;
+
+ bool Serialize(TiXmlNode *parent) const;
+ bool Deserialize(const TiXmlNode *node, bool &updated, std::map<std::string, std::shared_ptr<CSetting>> *loadedSettings = nullptr);
+
+ bool LoadSetting(const TiXmlNode* node, const std::shared_ptr<CSetting>& setting, bool& updated);
+ bool UpdateSetting(const TiXmlNode* node,
+ const std::shared_ptr<CSetting>& setting,
+ const CSettingUpdate& update);
+ void UpdateSettingByDependency(const std::string &settingId, const CSettingDependency &dependency);
+ void UpdateSettingByDependency(const std::string &settingId, SettingDependencyType dependencyType);
+
+ void AddSetting(const std::shared_ptr<CSetting>& setting);
+
+ void ResolveReferenceSettings(const std::shared_ptr<CSettingSection>& section);
+ void CleanupIncompleteSettings();
+
+ enum class SettingOptionsFillerType {
+ Unknown = 0,
+ Integer,
+ String
+ };
+
+ void RegisterSettingOptionsFiller(const std::string &identifier, void *filler, SettingOptionsFillerType type);
+
+ using CallbackSet = std::set<ISettingCallback *>;
+ struct Setting {
+ std::shared_ptr<CSetting> setting;
+ SettingDependencyMap dependencies;
+ std::set<std::string> children;
+ CallbackSet callbacks;
+ std::unordered_set<std::string> references;
+ };
+
+ using SettingMap = std::map<std::string, Setting>;
+
+ void ResolveSettingDependencies(const std::shared_ptr<CSetting>& setting);
+ void ResolveSettingDependencies(const Setting& setting);
+
+ SettingMap::const_iterator FindSetting(std::string settingId) const;
+ SettingMap::iterator FindSetting(std::string settingId);
+ std::pair<SettingMap::iterator, bool> InsertSetting(std::string settingId, const Setting& setting);
+
+ bool m_initialized = false;
+ bool m_loaded = false;
+
+ SettingMap m_settings;
+ using SettingSectionMap = std::map<std::string, std::shared_ptr<CSettingSection>>;
+ SettingSectionMap m_sections;
+
+ using SettingCreatorMap = std::map<std::string, ISettingCreator*>;
+ SettingCreatorMap m_settingCreators;
+
+ using SettingControlCreatorMap = std::map<std::string, ISettingControlCreator*>;
+ SettingControlCreatorMap m_settingControlCreators;
+
+ using SettingsHandlers = std::vector<ISettingsHandler*>;
+ SettingsHandlers m_settingsHandlers;
+
+ CSettingConditionsManager m_conditions;
+
+ struct SettingOptionsFiller {
+ void *filler;
+ SettingOptionsFillerType type;
+ };
+ using SettingOptionsFillerMap = std::map<std::string, SettingOptionsFiller>;
+ SettingOptionsFillerMap m_optionsFillers;
+
+ mutable CSharedSection m_critical;
+ mutable CSharedSection m_settingsCritical;
+
+ Logger m_logger;
+};