summaryrefslogtreecommitdiffstats
path: root/xbmc/settings/lib/Setting.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/settings/lib/Setting.cpp')
-rw-r--r--xbmc/settings/lib/Setting.cpp1690
1 files changed, 1690 insertions, 0 deletions
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;
+}