1 /*
2  *  Copyright (C) 2013-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "Setting.h"
10 
11 #include "ServiceBroker.h"
12 #include "SettingDefinitions.h"
13 #include "SettingsManager.h"
14 #include "utils/StringUtils.h"
15 #include "utils/XBMCTinyXML.h"
16 #include "utils/XMLUtils.h"
17 #include "utils/log.h"
18 
19 #include <sstream>
20 #include <utility>
21 
22 template<typename TKey, typename TValue>
CheckSettingOptionsValidity(const TValue & value,const std::vector<std::pair<TKey,TValue>> & options)23 bool CheckSettingOptionsValidity(const TValue& value, const std::vector<std::pair<TKey, TValue>>& options)
24 {
25   for (const auto& it : options)
26   {
27     if (it.second == value)
28       return true;
29   }
30 
31   return false;
32 }
33 
34 template<typename TKey, typename TValue>
CheckSettingOptionsValidity(const TValue & value,const std::vector<TKey> & options)35 bool CheckSettingOptionsValidity(const TValue& value, const std::vector<TKey>& options)
36 {
37   for (const auto& it : options)
38   {
39     if (it.value == value)
40       return true;
41   }
42 
43   return false;
44 }
45 
DeserializeOptionsSort(const TiXmlElement * optionsElement,SettingOptionsSort & optionsSort)46 bool DeserializeOptionsSort(const TiXmlElement* optionsElement, SettingOptionsSort& optionsSort)
47 {
48   optionsSort = SettingOptionsSort::NoSorting;
49 
50   std::string sort;
51   if (optionsElement->QueryStringAttribute("sort", &sort) != TIXML_SUCCESS)
52     return true;
53 
54   if (StringUtils::EqualsNoCase(sort, "false") || StringUtils::EqualsNoCase(sort, "off") ||
55     StringUtils::EqualsNoCase(sort, "no") || StringUtils::EqualsNoCase(sort, "disabled"))
56     optionsSort = SettingOptionsSort::NoSorting;
57   else if (StringUtils::EqualsNoCase(sort, "asc") || StringUtils::EqualsNoCase(sort, "ascending") ||
58     StringUtils::EqualsNoCase(sort, "true") || StringUtils::EqualsNoCase(sort, "on") ||
59     StringUtils::EqualsNoCase(sort, "yes") || StringUtils::EqualsNoCase(sort, "enabled"))
60     optionsSort = SettingOptionsSort::Ascending;
61   else if (StringUtils::EqualsNoCase(sort, "desc") || StringUtils::EqualsNoCase(sort, "descending"))
62     optionsSort = SettingOptionsSort::Descending;
63   else
64     return false;
65 
66   return true;
67 }
68 
69 Logger CSetting::s_logger;
70 
CSetting(const std::string & id,CSettingsManager * settingsManager,const std::string & name)71 CSetting::CSetting(const std::string& id,
72                    CSettingsManager* settingsManager /* = nullptr */,
73                    const std::string& name /* = "CSetting" */)
74   : ISetting(id, settingsManager)
75 {
76   if (s_logger == nullptr)
77     s_logger = CServiceBroker::GetLogging().GetLogger(name);
78 }
79 
CSetting(const std::string & id,const CSetting & setting,const std::string & name)80 CSetting::CSetting(const std::string& id,
81                    const CSetting& setting,
82                    const std::string& name /* = "CSetting" */)
83   : CSetting(id, setting.m_settingsManager, name)
84 {
85   Copy(setting);
86 }
87 
MergeBasics(const CSetting & other)88 void CSetting::MergeBasics(const CSetting& other)
89 {
90   // ISetting
91   SetVisible(other.GetVisible());
92   SetLabel(other.GetLabel());
93   SetHelp(other.GetHelp());
94   SetRequirementsMet(other.MeetsRequirements());
95   // CSetting
96   SetEnabled(other.GetEnabled());
97   SetParent(other.GetParent());
98   SetLevel(other.GetLevel());
99   SetControl(const_cast<CSetting&>(other).GetControl());
100   SetDependencies(other.GetDependencies());
101 }
102 
Deserialize(const TiXmlNode * node,bool update)103 bool CSetting::Deserialize(const TiXmlNode *node, bool update /* = false */)
104 {
105   // handle <visible> conditions
106   if (!ISetting::Deserialize(node, update))
107     return false;
108 
109   auto element = node->ToElement();
110   if (element == nullptr)
111     return false;
112 
113   auto parentSetting = element->Attribute(SETTING_XML_ATTR_PARENT);
114   if (parentSetting != nullptr)
115     m_parentSetting = parentSetting;
116 
117   // get <enable>
118   bool value;
119   if (XMLUtils::GetBoolean(node, SETTING_XML_ELM_ENABLED, value))
120     m_enabled = value;
121 
122   // get the <level>
123   int level = -1;
124   if (XMLUtils::GetInt(node, SETTING_XML_ELM_LEVEL, level))
125     m_level = static_cast<SettingLevel>(level);
126 
127   if (m_level < SettingLevel::Basic || m_level > SettingLevel::Internal)
128     m_level = SettingLevel::Standard;
129 
130   auto dependencies = node->FirstChild(SETTING_XML_ELM_DEPENDENCIES);
131   if (dependencies != nullptr)
132   {
133     auto dependencyNode = dependencies->FirstChild(SETTING_XML_ELM_DEPENDENCY);
134     while (dependencyNode != nullptr)
135     {
136       CSettingDependency dependency(m_settingsManager);
137       if (dependency.Deserialize(dependencyNode))
138         m_dependencies.push_back(dependency);
139       else
140         s_logger->warn("error reading <{}> tag of \"{}\"", SETTING_XML_ELM_DEPENDENCY, m_id);
141 
142       dependencyNode = dependencyNode->NextSibling(SETTING_XML_ELM_DEPENDENCY);
143     }
144   }
145 
146   auto control = node->FirstChildElement(SETTING_XML_ELM_CONTROL);
147   if (control != nullptr)
148   {
149     auto controlType = control->Attribute(SETTING_XML_ATTR_TYPE);
150     if (controlType == nullptr)
151     {
152       s_logger->error("error reading \"{}\" attribute of <control> tag of \"{}\"",
153                       SETTING_XML_ATTR_TYPE, m_id);
154       return false;
155     }
156 
157     m_control = m_settingsManager->CreateControl(controlType);
158     if (m_control == nullptr || !m_control->Deserialize(control, update))
159     {
160       s_logger->error("error reading <{}> tag of \"{}\"", SETTING_XML_ELM_CONTROL, m_id);
161       return false;
162     }
163   }
164   else if (!update && m_level < SettingLevel::Internal && !IsReference())
165   {
166     s_logger->error("missing <{}> tag of \"{}\"", SETTING_XML_ELM_CONTROL, m_id);
167     return false;
168   }
169 
170   auto updates = node->FirstChild(SETTING_XML_ELM_UPDATES);
171   if (updates != nullptr)
172   {
173     auto updateElem = updates->FirstChildElement(SETTING_XML_ELM_UPDATE);
174     while (updateElem != nullptr)
175     {
176       CSettingUpdate settingUpdate;
177       if (settingUpdate.Deserialize(updateElem))
178       {
179         if (!m_updates.insert(settingUpdate).second)
180           s_logger->warn("duplicate <{}> definition for \"{}\"", SETTING_XML_ELM_UPDATE, m_id);
181       }
182       else
183         s_logger->warn("error reading <{}> tag of \"{}\"", SETTING_XML_ELM_UPDATE, m_id);
184 
185       updateElem = updateElem->NextSiblingElement(SETTING_XML_ELM_UPDATE);
186     }
187   }
188 
189   return true;
190 }
191 
IsEnabled() const192 bool CSetting::IsEnabled() const
193 {
194   if (m_dependencies.empty() && m_parentSetting.empty())
195     return m_enabled;
196 
197   // if the setting has a parent setting and that parent setting is disabled
198   // the setting should automatically also be disabled
199   if (!m_parentSetting.empty())
200   {
201     SettingPtr parentSetting = m_settingsManager->GetSetting(m_parentSetting);
202     if (parentSetting != nullptr && !parentSetting->IsEnabled())
203       return false;
204   }
205 
206   bool enabled = m_enabled;
207   for (const auto& dep : m_dependencies)
208   {
209     if (dep.GetType() != SettingDependencyType::Enable)
210       continue;
211 
212     if (!dep.Check())
213     {
214       enabled = false;
215       break;
216     }
217   }
218 
219   return enabled;
220 }
221 
SetEnabled(bool enabled)222 void CSetting::SetEnabled(bool enabled)
223 {
224   if (!m_dependencies.empty() || m_enabled == enabled)
225     return;
226 
227   m_enabled = enabled;
228   OnSettingPropertyChanged(shared_from_this(), "enabled");
229 }
230 
MakeReference(const std::string & referencedId)231 void CSetting::MakeReference(const std::string& referencedId /* = "" */)
232 {
233   auto tmpReferencedId = referencedId;
234   if (referencedId.empty())
235     tmpReferencedId = m_id;
236 
237   m_id = StringUtils::Format("#{}[{}]", tmpReferencedId, StringUtils::CreateUUID());
238   m_referencedId = tmpReferencedId;
239 }
240 
IsVisible() const241 bool CSetting::IsVisible() const
242 {
243   if (!ISetting::IsVisible())
244     return false;
245 
246   bool visible = true;
247   for (const auto& dep : m_dependencies)
248   {
249     if (dep.GetType() != SettingDependencyType::Visible)
250       continue;
251 
252     if (!dep.Check())
253     {
254       visible = false;
255       break;
256     }
257   }
258 
259   return visible;
260 }
261 
OnSettingChanging(const std::shared_ptr<const CSetting> & setting)262 bool CSetting::OnSettingChanging(const std::shared_ptr<const CSetting>& setting)
263 {
264   if (m_callback == nullptr)
265     return true;
266 
267   return m_callback->OnSettingChanging(setting);
268 }
269 
OnSettingChanged(const std::shared_ptr<const CSetting> & setting)270 void CSetting::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
271 {
272   if (m_callback == nullptr)
273     return;
274 
275   m_callback->OnSettingChanged(setting);
276 }
277 
OnSettingAction(const std::shared_ptr<const CSetting> & setting)278 void CSetting::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
279 {
280   if (m_callback == nullptr)
281     return;
282 
283   m_callback->OnSettingAction(setting);
284 }
285 
OnSettingUpdate(const std::shared_ptr<CSetting> & setting,const char * oldSettingId,const TiXmlNode * oldSettingNode)286 bool CSetting::OnSettingUpdate(const std::shared_ptr<CSetting>& setting,
287                                const char* oldSettingId,
288                                const TiXmlNode* oldSettingNode)
289 {
290   if (m_callback == nullptr)
291     return false;
292 
293   return m_callback->OnSettingUpdate(setting, oldSettingId, oldSettingNode);
294 }
295 
OnSettingPropertyChanged(const std::shared_ptr<const CSetting> & setting,const char * propertyName)296 void CSetting::OnSettingPropertyChanged(const std::shared_ptr<const CSetting>& setting,
297                                         const char* propertyName)
298 {
299   if (m_callback == nullptr)
300     return;
301 
302   m_callback->OnSettingPropertyChanged(setting, propertyName);
303 }
304 
Copy(const CSetting & setting)305 void CSetting::Copy(const CSetting &setting)
306 {
307   SetVisible(setting.IsVisible());
308   SetLabel(setting.GetLabel());
309   SetHelp(setting.GetHelp());
310   SetRequirementsMet(setting.MeetsRequirements());
311   m_callback = setting.m_callback;
312   m_level = setting.m_level;
313 
314   if (setting.m_control != nullptr)
315   {
316     m_control = m_settingsManager->CreateControl(setting.m_control->GetType());
317     *m_control = *setting.m_control;
318   }
319   else
320     m_control = nullptr;
321 
322   m_dependencies = setting.m_dependencies;
323   m_updates = setting.m_updates;
324   m_changed = setting.m_changed;
325 }
326 
CSettingList(const std::string & id,std::shared_ptr<CSetting> settingDefinition,CSettingsManager * settingsManager)327 CSettingList::CSettingList(const std::string& id,
328                            std::shared_ptr<CSetting> settingDefinition,
329                            CSettingsManager* settingsManager /* = nullptr */)
330   : CSetting(id, settingsManager, "CSettingList"), m_definition(std::move(settingDefinition))
331 { }
332 
CSettingList(const std::string & id,std::shared_ptr<CSetting> settingDefinition,int label,CSettingsManager * settingsManager)333 CSettingList::CSettingList(const std::string& id,
334                            std::shared_ptr<CSetting> settingDefinition,
335                            int label,
336                            CSettingsManager* settingsManager /* = nullptr */)
337   : CSetting(id, settingsManager, "CSettingList"), m_definition(std::move(settingDefinition))
338 {
339   SetLabel(label);
340 }
341 
CSettingList(const std::string & id,const CSettingList & setting)342 CSettingList::CSettingList(const std::string &id, const CSettingList &setting)
343   : CSetting(id, setting)
344 {
345   copy(setting);
346 }
347 
Clone(const std::string & id) const348 SettingPtr CSettingList::Clone(const std::string &id) const
349 {
350   if (m_definition == nullptr)
351     return nullptr;
352 
353   return std::make_shared<CSettingList>(id, *this);
354 }
355 
MergeDetails(const CSetting & other)356 void CSettingList::MergeDetails(const CSetting& other)
357 {
358   if (other.GetType() != SettingType::List)
359     return;
360 
361   const auto& listSetting = static_cast<const CSettingList&>(other);
362   if (m_definition == nullptr && listSetting.m_definition != nullptr)
363     m_definition = listSetting.m_definition;
364   if (m_defaults.empty() && !listSetting.m_defaults.empty())
365     m_defaults = listSetting.m_defaults;
366   if (m_values.empty() && !listSetting.m_values.empty())
367     m_values = listSetting.m_values;
368   if (m_delimiter == "|" && listSetting.m_delimiter != "|")
369     m_delimiter = listSetting.m_delimiter;
370   if (m_minimumItems == 0 && listSetting.m_minimumItems != 0)
371     m_minimumItems = listSetting.m_minimumItems;
372   if (m_maximumItems == -1 && listSetting.m_maximumItems != -1)
373     m_maximumItems = listSetting.m_maximumItems;
374 }
375 
Deserialize(const TiXmlNode * node,bool update)376 bool CSettingList::Deserialize(const TiXmlNode *node, bool update /* = false */)
377 {
378   CExclusiveLock lock(m_critical);
379 
380   if (m_definition == nullptr)
381     return false;
382 
383   if (!CSetting::Deserialize(node, update))
384     return false;
385 
386   auto element = node->ToElement();
387   if (element == nullptr)
388   {
389     s_logger->warn("unable to read type of list setting of {}", m_id);
390     return false;
391   }
392 
393   // deserialize the setting definition in update mode because we don't care
394   // about an invalid <default> value (which is never used)
395   if (!m_definition->Deserialize(node, true))
396     return false;
397 
398   auto constraints = node->FirstChild(SETTING_XML_ELM_CONSTRAINTS);
399   if (constraints != nullptr)
400   {
401     // read the delimiter
402     std::string delimiter;
403     if (XMLUtils::GetString(constraints, SETTING_XML_ELM_DELIMITER, delimiter) && !delimiter.empty())
404       m_delimiter = delimiter;
405 
406     XMLUtils::GetInt(constraints, SETTING_XML_ELM_MINIMUM_ITEMS, m_minimumItems);
407     if (m_minimumItems < 0)
408       m_minimumItems = 0;
409     XMLUtils::GetInt(constraints, SETTING_XML_ELM_MAXIMUM_ITEMS, m_maximumItems);
410     if (m_maximumItems <= 0)
411       m_maximumItems = -1;
412     else if (m_maximumItems < m_minimumItems)
413     {
414       s_logger->warn("invalid <{}> ({}) and/or <{}> ({}) of {}", SETTING_XML_ELM_MINIMUM_ITEMS,
415                      m_minimumItems, SETTING_XML_ELM_MAXIMUM_ITEMS, m_maximumItems, m_id);
416       return false;
417     }
418   }
419 
420   // read the default and initial values
421   std::string values;
422   if (XMLUtils::GetString(node, SETTING_XML_ELM_DEFAULT, values))
423   {
424     if (!fromString(values, m_defaults))
425     {
426       s_logger->warn("invalid <{}> definition \"{}\" of {}", SETTING_XML_ELM_DEFAULT, values, m_id);
427       return false;
428     }
429     Reset();
430   }
431 
432   return true;
433 }
434 
GetElementType() const435 SettingType CSettingList::GetElementType() const
436 {
437   CSharedLock lock(m_critical);
438 
439   if (m_definition == nullptr)
440     return SettingType::Unknown;
441 
442   return m_definition->GetType();
443 }
444 
FromString(const std::string & value)445 bool CSettingList::FromString(const std::string &value)
446 {
447   SettingList values;
448   if (!fromString(value, values))
449     return false;
450 
451   return SetValue(values);
452 }
453 
ToString() const454 std::string CSettingList::ToString() const
455 {
456   return toString(m_values);
457 }
458 
Equals(const std::string & value) const459 bool CSettingList::Equals(const std::string &value) const
460 {
461   SettingList values;
462   if (!fromString(value, values) || values.size() != m_values.size())
463     return false;
464 
465   bool ret = true;
466   for (size_t index = 0; index < values.size(); index++)
467   {
468     if (!m_values[index]->Equals(values[index]->ToString()))
469     {
470       ret = false;
471       break;
472     }
473   }
474 
475   return ret;
476 }
477 
CheckValidity(const std::string & value) const478 bool CSettingList::CheckValidity(const std::string &value) const
479 {
480   SettingList values;
481   return fromString(value, values);
482 }
483 
Reset()484 void CSettingList::Reset()
485 {
486   CExclusiveLock lock(m_critical);
487   SettingList values;
488   for (const auto& it : m_defaults)
489     values.push_back(it->Clone(it->GetId()));
490 
491   SetValue(values);
492 }
493 
FromString(const std::vector<std::string> & value)494 bool CSettingList::FromString(const std::vector<std::string> &value)
495 {
496   SettingList values;
497   if (!fromValues(value, values))
498     return false;
499 
500   return SetValue(values);
501 }
502 
SetValue(const SettingList & values)503 bool CSettingList::SetValue(const SettingList &values)
504 {
505   CExclusiveLock lock(m_critical);
506 
507   if ((int)values.size() < m_minimumItems ||
508      (m_maximumItems > 0 && (int)values.size() > m_maximumItems))
509     return false;
510 
511   bool equal = values.size() == m_values.size();
512   for (size_t index = 0; index < values.size(); index++)
513   {
514     if (values[index]->GetType() != GetElementType())
515       return false;
516 
517     if (equal &&
518         !values[index]->Equals(m_values[index]->ToString()))
519       equal = false;
520   }
521 
522   if (equal)
523     return true;
524 
525   SettingList oldValues = m_values;
526   m_values.clear();
527   m_values.insert(m_values.begin(), values.begin(), values.end());
528 
529   if (!OnSettingChanging(shared_from_base<CSettingList>()))
530   {
531     m_values = oldValues;
532 
533     // the setting couldn't be changed because one of the
534     // callback handlers failed the OnSettingChanging()
535     // callback so we need to let all the callback handlers
536     // know that the setting hasn't changed
537     OnSettingChanging(shared_from_base<CSettingList>());
538     return false;
539   }
540 
541   m_changed = toString(m_values) != toString(m_defaults);
542   OnSettingChanged(shared_from_base<CSettingList>());
543   return true;
544 }
545 
SetDefault(const SettingList & values)546 void CSettingList::SetDefault(const SettingList &values)
547 {
548   CExclusiveLock lock(m_critical);
549 
550   m_defaults.clear();
551   m_defaults.insert(m_defaults.begin(), values.begin(), values.end());
552 
553   if (!m_changed)
554   {
555     m_values.clear();
556     for (const auto& it : m_defaults)
557       m_values.push_back(it->Clone(it->GetId()));
558   }
559 }
560 
copy(const CSettingList & setting)561 void CSettingList::copy(const CSettingList &setting)
562 {
563   CSetting::Copy(setting);
564 
565   copy(setting.m_values, m_values);
566   copy(setting.m_defaults, m_defaults);
567 
568   if (setting.m_definition != nullptr)
569   {
570     auto definitionCopy = setting.m_definition->Clone(m_id + ".definition");
571     if (definitionCopy != nullptr)
572       m_definition = definitionCopy;
573   }
574 
575   m_delimiter = setting.m_delimiter;
576   m_minimumItems = setting.m_minimumItems;
577   m_maximumItems = setting.m_maximumItems;
578 }
579 
copy(const SettingList & srcValues,SettingList & dstValues)580 void CSettingList::copy(const SettingList &srcValues, SettingList &dstValues)
581 {
582   dstValues.clear();
583 
584   for (const auto& value : srcValues)
585   {
586     if (value == nullptr)
587       continue;
588 
589     SettingPtr valueCopy = value->Clone(value->GetId());
590     if (valueCopy == nullptr)
591       continue;
592 
593     dstValues.emplace_back(valueCopy);
594   }
595 }
596 
fromString(const std::string & strValue,SettingList & values) const597 bool CSettingList::fromString(const std::string &strValue, SettingList &values) const
598 {
599   return fromValues(StringUtils::Split(strValue, m_delimiter), values);
600 }
601 
fromValues(const std::vector<std::string> & strValues,SettingList & values) const602 bool CSettingList::fromValues(const std::vector<std::string> &strValues, SettingList &values) const
603 {
604   if ((int)strValues.size() < m_minimumItems ||
605      (m_maximumItems > 0 && (int)strValues.size() > m_maximumItems))
606     return false;
607 
608   bool ret = true;
609   int index = 0;
610   for (const auto& value : strValues)
611   {
612     auto settingValue = m_definition->Clone(StringUtils::Format("{}.{}", m_id, index++));
613     if (settingValue == nullptr ||
614         !settingValue->FromString(value))
615     {
616       ret = false;
617       break;
618     }
619 
620     values.emplace_back(settingValue);
621   }
622 
623   if (!ret)
624     values.clear();
625 
626   return ret;
627 }
628 
toString(const SettingList & values) const629 std::string CSettingList::toString(const SettingList &values) const
630 {
631   std::vector<std::string> strValues;
632   for (const auto& value : values)
633   {
634     if (value != nullptr)
635       strValues.push_back(value->ToString());
636   }
637 
638   return StringUtils::Join(strValues, m_delimiter);
639 }
640 
CSettingBool(const std::string & id,CSettingsManager * settingsManager)641 CSettingBool::CSettingBool(const std::string& id, CSettingsManager* settingsManager /* = nullptr */)
642   : CTraitedSetting(id, settingsManager, "CSettingBool")
643 { }
644 
CSettingBool(const std::string & id,const CSettingBool & setting)645 CSettingBool::CSettingBool(const std::string& id, const CSettingBool& setting)
646   : CTraitedSetting(id, setting, "CSettingBool")
647 {
648   copy(setting);
649 }
650 
CSettingBool(const std::string & id,int label,bool value,CSettingsManager * settingsManager)651 CSettingBool::CSettingBool(const std::string& id,
652                            int label,
653                            bool value,
654                            CSettingsManager* settingsManager /* = nullptr */)
655   : CTraitedSetting(id, settingsManager, "CSettingBool"), m_value(value), m_default(value)
656 {
657   SetLabel(label);
658 }
659 
Clone(const std::string & id) const660 SettingPtr CSettingBool::Clone(const std::string &id) const
661 {
662   return std::make_shared<CSettingBool>(id, *this);
663 }
664 
MergeDetails(const CSetting & other)665 void CSettingBool::MergeDetails(const CSetting& other)
666 {
667   if (other.GetType() != SettingType::Boolean)
668     return;
669 
670   const auto& boolSetting = static_cast<const CSettingBool&>(other);
671   if (m_default == false && boolSetting.m_default == true)
672     m_default = boolSetting.m_default;
673   if (m_value == m_default && boolSetting.m_value != m_default)
674     m_value = boolSetting.m_value;
675 }
676 
Deserialize(const TiXmlNode * node,bool update)677 bool CSettingBool::Deserialize(const TiXmlNode *node, bool update /* = false */)
678 {
679   CExclusiveLock lock(m_critical);
680 
681   if (!CSetting::Deserialize(node, update))
682     return false;
683 
684   // get the default value
685   bool value;
686   if (XMLUtils::GetBoolean(node, SETTING_XML_ELM_DEFAULT, value))
687     m_value = m_default = value;
688   else if (!update)
689   {
690     s_logger->error("error reading the default value of \"{}\"", m_id);
691     return false;
692   }
693 
694   return true;
695 }
696 
FromString(const std::string & value)697 bool CSettingBool::FromString(const std::string &value)
698 {
699   bool bValue;
700   if (!fromString(value, bValue))
701     return false;
702 
703   return SetValue(bValue);
704 }
705 
ToString() const706 std::string CSettingBool::ToString() const
707 {
708   return m_value ? "true" : "false";
709 }
710 
Equals(const std::string & value) const711 bool CSettingBool::Equals(const std::string &value) const
712 {
713   bool bValue;
714   return (fromString(value, bValue) && m_value == bValue);
715 }
716 
CheckValidity(const std::string & value) const717 bool CSettingBool::CheckValidity(const std::string &value) const
718 {
719   bool bValue;
720   return fromString(value, bValue);
721 }
722 
SetValue(bool value)723 bool CSettingBool::SetValue(bool value)
724 {
725   CExclusiveLock lock(m_critical);
726 
727   if (value == m_value)
728     return true;
729 
730   bool oldValue = m_value;
731   m_value = value;
732 
733   if (!OnSettingChanging(shared_from_base<CSettingBool>()))
734   {
735     m_value = oldValue;
736 
737     // the setting couldn't be changed because one of the
738     // callback handlers failed the OnSettingChanging()
739     // callback so we need to let all the callback handlers
740     // know that the setting hasn't changed
741     OnSettingChanging(shared_from_base<CSettingBool>());
742     return false;
743   }
744 
745   m_changed = m_value != m_default;
746   OnSettingChanged(shared_from_base<CSettingBool>());
747   return true;
748 }
749 
SetDefault(bool value)750 void CSettingBool::SetDefault(bool value)
751 {
752   CExclusiveLock lock(m_critical);
753 
754   m_default = value;
755   if (!m_changed)
756     m_value = m_default;
757 }
758 
copy(const CSettingBool & setting)759 void CSettingBool::copy(const CSettingBool &setting)
760 {
761   CSetting::Copy(setting);
762 
763   m_value = setting.m_value;
764   m_default = setting.m_default;
765 }
766 
fromString(const std::string & strValue,bool & value) const767 bool CSettingBool::fromString(const std::string &strValue, bool &value) const
768 {
769   if (StringUtils::EqualsNoCase(strValue, "true"))
770   {
771     value = true;
772     return true;
773   }
774   if (StringUtils::EqualsNoCase(strValue, "false"))
775   {
776     value = false;
777     return true;
778   }
779 
780   return false;
781 }
782 
CSettingInt(const std::string & id,CSettingsManager * settingsManager)783 CSettingInt::CSettingInt(const std::string& id, CSettingsManager* settingsManager /* = nullptr */)
784   : CTraitedSetting(id, settingsManager, "CSettingInt")
785 { }
786 
CSettingInt(const std::string & id,const CSettingInt & setting)787 CSettingInt::CSettingInt(const std::string& id, const CSettingInt& setting)
788   : CTraitedSetting(id, setting, "CSettingInt")
789 {
790   copy(setting);
791 }
792 
CSettingInt(const std::string & id,int label,int value,CSettingsManager * settingsManager)793 CSettingInt::CSettingInt(const std::string& id,
794                          int label,
795                          int value,
796                          CSettingsManager* settingsManager /* = nullptr */)
797   : CTraitedSetting(id, settingsManager, "CSettingInt"), m_value(value), m_default(value)
798 {
799   SetLabel(label);
800 }
801 
CSettingInt(const std::string & id,int label,int value,int minimum,int step,int maximum,CSettingsManager * settingsManager)802 CSettingInt::CSettingInt(const std::string& id,
803                          int label,
804                          int value,
805                          int minimum,
806                          int step,
807                          int maximum,
808                          CSettingsManager* settingsManager /* = nullptr */)
809   : CTraitedSetting(id, settingsManager, "CSettingInt"),
810     m_value(value),
811     m_default(value),
812     m_min(minimum),
813     m_step(step),
814     m_max(maximum)
815 {
816   SetLabel(label);
817 }
818 
CSettingInt(const std::string & id,int label,int value,const TranslatableIntegerSettingOptions & options,CSettingsManager * settingsManager)819 CSettingInt::CSettingInt(const std::string& id,
820                          int label,
821                          int value,
822                          const TranslatableIntegerSettingOptions& options,
823                          CSettingsManager* settingsManager /* = nullptr */)
824   : CTraitedSetting(id, settingsManager, "CSettingInt"),
825     m_value(value),
826     m_default(value),
827     m_translatableOptions(options)
828 {
829   SetLabel(label);
830 }
831 
Clone(const std::string & id) const832 SettingPtr CSettingInt::Clone(const std::string &id) const
833 {
834   return std::make_shared<CSettingInt>(id, *this);
835 }
836 
MergeDetails(const CSetting & other)837 void CSettingInt::MergeDetails(const CSetting& other)
838 {
839   if (other.GetType() != SettingType::Integer)
840     return;
841 
842   const auto& intSetting = static_cast<const CSettingInt&>(other);
843   if (m_default == 0.0 && intSetting.m_default != 0.0)
844     m_default = intSetting.m_default;
845   if (m_value == m_default && intSetting.m_value != m_default)
846     m_value = intSetting.m_value;
847   if (m_min == 0.0 && intSetting.m_min != 0.0)
848     m_min = intSetting.m_min;
849   if (m_step == 1.0 && intSetting.m_step != 1.0)
850     m_step = intSetting.m_step;
851   if (m_max == 0.0 && intSetting.m_max != 0.0)
852     m_max = intSetting.m_max;
853   if (m_translatableOptions.empty() && !intSetting.m_translatableOptions.empty())
854     m_translatableOptions = intSetting.m_translatableOptions;
855   if (m_options.empty() && !intSetting.m_options.empty())
856     m_options = intSetting.m_options;
857   if (m_optionsFillerName.empty() && !intSetting.m_optionsFillerName.empty())
858     m_optionsFillerName = intSetting.m_optionsFillerName;
859   if (m_optionsFiller == nullptr && intSetting.m_optionsFiller != nullptr)
860     m_optionsFiller = intSetting.m_optionsFiller;
861   if (m_optionsFillerData == nullptr && intSetting.m_optionsFillerData != nullptr)
862     m_optionsFillerData = intSetting.m_optionsFillerData;
863   if (m_dynamicOptions.empty() && !intSetting.m_dynamicOptions.empty())
864     m_dynamicOptions = intSetting.m_dynamicOptions;
865   if (m_optionsSort == SettingOptionsSort::NoSorting &&
866       intSetting.m_optionsSort != SettingOptionsSort::NoSorting)
867     m_optionsSort = intSetting.m_optionsSort;
868 }
869 
Deserialize(const TiXmlNode * node,bool update)870 bool CSettingInt::Deserialize(const TiXmlNode *node, bool update /* = false */)
871 {
872   CExclusiveLock lock(m_critical);
873 
874   if (!CSetting::Deserialize(node, update))
875     return false;
876 
877   // get the default value
878   int value;
879   if (XMLUtils::GetInt(node, SETTING_XML_ELM_DEFAULT, value))
880     m_value = m_default = value;
881   else if (!update)
882   {
883     s_logger->error("error reading the default value of \"{}\"", m_id);
884     return false;
885   }
886 
887   auto constraints = node->FirstChild(SETTING_XML_ELM_CONSTRAINTS);
888   if (constraints != nullptr)
889   {
890     // get the entries
891     auto options = constraints->FirstChildElement(SETTING_XML_ELM_OPTIONS);
892     if (options != nullptr && options->FirstChild() != nullptr)
893     {
894       if (!DeserializeOptionsSort(options, m_optionsSort))
895         s_logger->warn("invalid \"sort\" attribute of <" SETTING_XML_ELM_OPTIONS "> for \"{}\"",
896                        m_id);
897 
898       if (options->FirstChild()->Type() == TiXmlNode::TINYXML_TEXT)
899       {
900         m_optionsFillerName = options->FirstChild()->ValueStr();
901         if (!m_optionsFillerName.empty())
902         {
903           m_optionsFiller = reinterpret_cast<IntegerSettingOptionsFiller>(m_settingsManager->GetSettingOptionsFiller(shared_from_base<CSettingInt>()));
904         }
905       }
906       else
907       {
908         m_translatableOptions.clear();
909         auto optionElement = options->FirstChildElement(SETTING_XML_ELM_OPTION);
910         while (optionElement != nullptr)
911         {
912           TranslatableIntegerSettingOption entry;
913           if (optionElement->QueryIntAttribute(SETTING_XML_ATTR_LABEL, &entry.label) ==
914                   TIXML_SUCCESS &&
915               entry.label > 0)
916           {
917             entry.value = strtol(optionElement->FirstChild()->Value(), nullptr, 10);
918             m_translatableOptions.push_back(entry);
919           }
920           else
921           {
922             std::string label;
923             if (optionElement->QueryStringAttribute(SETTING_XML_ATTR_LABEL, &label) ==
924                 TIXML_SUCCESS)
925             {
926               int value = strtol(optionElement->FirstChild()->Value(), nullptr, 10);
927               m_options.emplace_back(label, value);
928             }
929           }
930 
931           optionElement = optionElement->NextSiblingElement(SETTING_XML_ELM_OPTION);
932         }
933       }
934     }
935 
936     // get minimum
937     XMLUtils::GetInt(constraints, SETTING_XML_ELM_MINIMUM, m_min);
938     // get step
939     XMLUtils::GetInt(constraints, SETTING_XML_ELM_STEP, m_step);
940     // get maximum
941     XMLUtils::GetInt(constraints, SETTING_XML_ELM_MAXIMUM, m_max);
942   }
943 
944   return true;
945 }
946 
FromString(const std::string & value)947 bool CSettingInt::FromString(const std::string &value)
948 {
949   int iValue;
950   if (!fromString(value, iValue))
951     return false;
952 
953   return SetValue(iValue);
954 }
955 
ToString() const956 std::string CSettingInt::ToString() const
957 {
958   std::ostringstream oss;
959   oss << m_value;
960 
961   return oss.str();
962 }
963 
Equals(const std::string & value) const964 bool CSettingInt::Equals(const std::string &value) const
965 {
966   int iValue;
967   return (fromString(value, iValue) && m_value == iValue);
968 }
969 
CheckValidity(const std::string & value) const970 bool CSettingInt::CheckValidity(const std::string &value) const
971 {
972   int iValue;
973   if (!fromString(value, iValue))
974     return false;
975 
976   return CheckValidity(iValue);
977 }
978 
CheckValidity(int value) const979 bool CSettingInt::CheckValidity(int value) const
980 {
981   if (!m_translatableOptions.empty())
982   {
983     if (!CheckSettingOptionsValidity(value, m_translatableOptions))
984       return false;
985   }
986   else if (!m_options.empty())
987   {
988     if (!CheckSettingOptionsValidity(value, m_options))
989       return false;
990   }
991   else if (m_optionsFillerName.empty() && m_optionsFiller == nullptr &&
992            m_min != m_max && (value < m_min || value > m_max))
993     return false;
994 
995   return true;
996 }
997 
SetValue(int value)998 bool CSettingInt::SetValue(int value)
999 {
1000   CExclusiveLock lock(m_critical);
1001 
1002   if (value == m_value)
1003     return true;
1004 
1005   if (!CheckValidity(value))
1006     return false;
1007 
1008   int oldValue = m_value;
1009   m_value = value;
1010 
1011   if (!OnSettingChanging(shared_from_base<CSettingInt>()))
1012   {
1013     m_value = oldValue;
1014 
1015     // the setting couldn't be changed because one of the
1016     // callback handlers failed the OnSettingChanging()
1017     // callback so we need to let all the callback handlers
1018     // know that the setting hasn't changed
1019     OnSettingChanging(shared_from_base<CSettingInt>());
1020     return false;
1021   }
1022 
1023   m_changed = m_value != m_default;
1024   OnSettingChanged(shared_from_base<CSettingInt>());
1025   return true;
1026 }
1027 
SetDefault(int value)1028 void CSettingInt::SetDefault(int value)
1029 {
1030   CExclusiveLock lock(m_critical);
1031 
1032   m_default = value;
1033   if (!m_changed)
1034     m_value = m_default;
1035 }
1036 
GetOptionsType() const1037 SettingOptionsType CSettingInt::GetOptionsType() const
1038 {
1039   CSharedLock lock(m_critical);
1040   if (!m_translatableOptions.empty())
1041     return SettingOptionsType::StaticTranslatable;
1042   if (!m_options.empty())
1043     return SettingOptionsType::Static;
1044   if (!m_optionsFillerName.empty() || m_optionsFiller != nullptr)
1045     return SettingOptionsType::Dynamic;
1046 
1047   return SettingOptionsType::Unknown;
1048 }
1049 
UpdateDynamicOptions()1050 IntegerSettingOptions CSettingInt::UpdateDynamicOptions()
1051 {
1052   CExclusiveLock lock(m_critical);
1053   IntegerSettingOptions options;
1054   if (m_optionsFiller == nullptr &&
1055      (m_optionsFillerName.empty() || m_settingsManager == nullptr))
1056     return options;
1057 
1058   if (m_optionsFiller == nullptr)
1059   {
1060     m_optionsFiller = reinterpret_cast<IntegerSettingOptionsFiller>(m_settingsManager->GetSettingOptionsFiller(shared_from_base<CSettingInt>()));
1061     if (m_optionsFiller == nullptr)
1062     {
1063       s_logger->warn("unknown options filler \"{}\" of \"{}\"", m_optionsFillerName, m_id);
1064       return options;
1065     }
1066   }
1067 
1068   int bestMatchingValue = m_value;
1069   m_optionsFiller(shared_from_base<CSettingInt>(), options, bestMatchingValue, m_optionsFillerData);
1070 
1071   if (bestMatchingValue != m_value)
1072     SetValue(bestMatchingValue);
1073 
1074   bool changed = m_dynamicOptions.size() != options.size();
1075   if (!changed)
1076   {
1077     for (size_t index = 0; index < options.size(); index++)
1078     {
1079       if (options[index].label.compare(m_dynamicOptions[index].label) != 0 ||
1080           options[index].value != m_dynamicOptions[index].value)
1081       {
1082         changed = true;
1083         break;
1084       }
1085     }
1086   }
1087 
1088   if (changed)
1089   {
1090     m_dynamicOptions = options;
1091     OnSettingPropertyChanged(shared_from_base<CSettingInt>(), "options");
1092   }
1093 
1094   return options;
1095 }
1096 
copy(const CSettingInt & setting)1097 void CSettingInt::copy(const CSettingInt &setting)
1098 {
1099   CSetting::Copy(setting);
1100 
1101   CExclusiveLock lock(m_critical);
1102 
1103   m_value = setting.m_value;
1104   m_default = setting.m_default;
1105   m_min = setting.m_min;
1106   m_step = setting.m_step;
1107   m_max = setting.m_max;
1108   m_translatableOptions = setting.m_translatableOptions;
1109   m_options = setting.m_options;
1110   m_optionsFillerName = setting.m_optionsFillerName;
1111   m_optionsFiller = setting.m_optionsFiller;
1112   m_optionsFillerData = setting.m_optionsFillerData;
1113   m_dynamicOptions = setting.m_dynamicOptions;
1114 }
1115 
fromString(const std::string & strValue,int & value)1116 bool CSettingInt::fromString(const std::string &strValue, int &value)
1117 {
1118   if (strValue.empty())
1119     return false;
1120 
1121   char *end = nullptr;
1122   value = (int)strtol(strValue.c_str(), &end, 10);
1123   if (end != nullptr && *end != '\0')
1124     return false;
1125 
1126   return true;
1127 }
1128 
CSettingNumber(const std::string & id,CSettingsManager * settingsManager)1129 CSettingNumber::CSettingNumber(const std::string& id,
1130                                CSettingsManager* settingsManager /* = nullptr */)
1131   : CTraitedSetting(id, settingsManager, "CSettingNumber")
1132 { }
1133 
CSettingNumber(const std::string & id,const CSettingNumber & setting)1134 CSettingNumber::CSettingNumber(const std::string& id, const CSettingNumber& setting)
1135   : CTraitedSetting(id, setting, "CSettingNumber")
1136 {
1137   copy(setting);
1138 }
1139 
CSettingNumber(const std::string & id,int label,float value,CSettingsManager * settingsManager)1140 CSettingNumber::CSettingNumber(const std::string& id,
1141                                int label,
1142                                float value,
1143                                CSettingsManager* settingsManager /* = nullptr */)
1144   : CTraitedSetting(id, settingsManager, "CSettingNumber"), m_value(value), m_default(value)
1145 {
1146   SetLabel(label);
1147 }
1148 
CSettingNumber(const std::string & id,int label,float value,float minimum,float step,float maximum,CSettingsManager * settingsManager)1149 CSettingNumber::CSettingNumber(const std::string& id,
1150                                int label,
1151                                float value,
1152                                float minimum,
1153                                float step,
1154                                float maximum,
1155                                CSettingsManager* settingsManager /* = nullptr */)
1156   : CTraitedSetting(id, settingsManager, "CSettingNumber"),
1157     m_value(value),
1158     m_default(value),
1159     m_min(minimum),
1160     m_step(step),
1161     m_max(maximum)
1162 {
1163   SetLabel(label);
1164 }
1165 
Clone(const std::string & id) const1166 SettingPtr CSettingNumber::Clone(const std::string &id) const
1167 {
1168   return std::make_shared<CSettingNumber>(id, *this);
1169 }
1170 
MergeDetails(const CSetting & other)1171 void CSettingNumber::MergeDetails(const CSetting& other)
1172 {
1173   if (other.GetType() != SettingType::Number)
1174     return;
1175 
1176   const auto& numberSetting = static_cast<const CSettingNumber&>(other);
1177   if (m_default == 0.0 && numberSetting.m_default != 0.0)
1178     m_default = numberSetting.m_default;
1179   if (m_value == m_default && numberSetting.m_value != m_default)
1180     m_value = numberSetting.m_value;
1181   if (m_min == 0.0 && numberSetting.m_min != 0.0)
1182     m_min = numberSetting.m_min;
1183   if (m_step == 1.0 && numberSetting.m_step != 1.0)
1184     m_step = numberSetting.m_step;
1185   if (m_max == 0.0 && numberSetting.m_max != 0.0)
1186     m_max = numberSetting.m_max;
1187 }
1188 
Deserialize(const TiXmlNode * node,bool update)1189 bool CSettingNumber::Deserialize(const TiXmlNode *node, bool update /* = false */)
1190 {
1191   CExclusiveLock lock(m_critical);
1192 
1193   if (!CSetting::Deserialize(node, update))
1194     return false;
1195 
1196   // get the default value
1197   double value;
1198   if (XMLUtils::GetDouble(node, SETTING_XML_ELM_DEFAULT, value))
1199     m_value = m_default = value;
1200   else if (!update)
1201   {
1202     s_logger->error("error reading the default value of \"{}\"", m_id);
1203     return false;
1204   }
1205 
1206   auto constraints = node->FirstChild(SETTING_XML_ELM_CONSTRAINTS);
1207   if (constraints != nullptr)
1208   {
1209     // get the minimum value
1210     XMLUtils::GetDouble(constraints, SETTING_XML_ELM_MINIMUM, m_min);
1211     // get the step value
1212     XMLUtils::GetDouble(constraints, SETTING_XML_ELM_STEP, m_step);
1213     // get the maximum value
1214     XMLUtils::GetDouble(constraints, SETTING_XML_ELM_MAXIMUM, m_max);
1215   }
1216 
1217   return true;
1218 }
1219 
FromString(const std::string & value)1220 bool CSettingNumber::FromString(const std::string &value)
1221 {
1222   double dValue;
1223   if (!fromString(value, dValue))
1224     return false;
1225 
1226   return SetValue(dValue);
1227 }
1228 
ToString() const1229 std::string CSettingNumber::ToString() const
1230 {
1231   std::ostringstream oss;
1232   oss << m_value;
1233 
1234   return oss.str();
1235 }
1236 
Equals(const std::string & value) const1237 bool CSettingNumber::Equals(const std::string &value) const
1238 {
1239   double dValue;
1240   CSharedLock lock(m_critical);
1241   return (fromString(value, dValue) && m_value == dValue);
1242 }
1243 
CheckValidity(const std::string & value) const1244 bool CSettingNumber::CheckValidity(const std::string &value) const
1245 {
1246   double dValue;
1247   if (!fromString(value, dValue))
1248     return false;
1249 
1250   return CheckValidity(dValue);
1251 }
1252 
CheckValidity(double value) const1253 bool CSettingNumber::CheckValidity(double value) const
1254 {
1255   CSharedLock lock(m_critical);
1256   if (m_min != m_max &&
1257      (value < m_min || value > m_max))
1258     return false;
1259 
1260   return true;
1261 }
1262 
SetValue(double value)1263 bool CSettingNumber::SetValue(double value)
1264 {
1265   CExclusiveLock lock(m_critical);
1266 
1267   if (value == m_value)
1268     return true;
1269 
1270   if (!CheckValidity(value))
1271     return false;
1272 
1273   double oldValue = m_value;
1274   m_value = value;
1275 
1276   if (!OnSettingChanging(shared_from_base<CSettingNumber>()))
1277   {
1278     m_value = oldValue;
1279 
1280     // the setting couldn't be changed because one of the
1281     // callback handlers failed the OnSettingChanging()
1282     // callback so we need to let all the callback handlers
1283     // know that the setting hasn't changed
1284     OnSettingChanging(shared_from_base<CSettingNumber>());
1285     return false;
1286   }
1287 
1288   m_changed = m_value != m_default;
1289   OnSettingChanged(shared_from_base<CSettingNumber>());
1290   return true;
1291 }
1292 
SetDefault(double value)1293 void CSettingNumber::SetDefault(double value)
1294 {
1295   CExclusiveLock lock(m_critical);
1296 
1297   m_default = value;
1298   if (!m_changed)
1299     m_value = m_default;
1300 }
1301 
copy(const CSettingNumber & setting)1302 void CSettingNumber::copy(const CSettingNumber &setting)
1303 {
1304   CSetting::Copy(setting);
1305   CExclusiveLock lock(m_critical);
1306 
1307   m_value = setting.m_value;
1308   m_default = setting.m_default;
1309   m_min = setting.m_min;
1310   m_step = setting.m_step;
1311   m_max = setting.m_max;
1312 }
1313 
fromString(const std::string & strValue,double & value)1314 bool CSettingNumber::fromString(const std::string &strValue, double &value)
1315 {
1316   if (strValue.empty())
1317     return false;
1318 
1319   char *end = nullptr;
1320   value = strtod(strValue.c_str(), &end);
1321   if (end != nullptr && *end != '\0')
1322     return false;
1323 
1324   return true;
1325 }
1326 
CSettingString(const std::string & id,CSettingsManager * settingsManager)1327 CSettingString::CSettingString(const std::string& id,
1328                                CSettingsManager* settingsManager /* = nullptr */)
1329   : CTraitedSetting(id, settingsManager, "CSettingString")
1330 { }
1331 
CSettingString(const std::string & id,const CSettingString & setting)1332 CSettingString::CSettingString(const std::string& id, const CSettingString& setting)
1333   : CTraitedSetting(id, setting, "CSettingString")
1334 {
1335   copy(setting);
1336 }
1337 
CSettingString(const std::string & id,int label,const std::string & value,CSettingsManager * settingsManager)1338 CSettingString::CSettingString(const std::string& id,
1339                                int label,
1340                                const std::string& value,
1341                                CSettingsManager* settingsManager /* = nullptr */)
1342   : CTraitedSetting(id, settingsManager, "CSettingString"), m_value(value), m_default(value)
1343 {
1344   SetLabel(label);
1345 }
1346 
Clone(const std::string & id) const1347 SettingPtr CSettingString::Clone(const std::string &id) const
1348 {
1349   return std::make_shared<CSettingString>(id, *this);
1350 }
1351 
MergeDetails(const CSetting & other)1352 void CSettingString::MergeDetails(const CSetting& other)
1353 {
1354   if (other.GetType() != SettingType::String)
1355     return;
1356 
1357   const auto& stringSetting = static_cast<const CSettingString&>(other);
1358   if (m_default.empty() && !stringSetting.m_default.empty())
1359     m_default = stringSetting.m_default;
1360   if (m_value == m_default && stringSetting.m_value != m_default)
1361     m_value = stringSetting.m_value;
1362   if (m_allowEmpty == false && stringSetting.m_allowEmpty == true)
1363     m_allowEmpty = stringSetting.m_allowEmpty;
1364   if (m_allowNewOption == false && stringSetting.m_allowNewOption == true)
1365     m_allowNewOption = stringSetting.m_allowNewOption;
1366   if (m_translatableOptions.empty() && !stringSetting.m_translatableOptions.empty())
1367     m_translatableOptions = stringSetting.m_translatableOptions;
1368   if (m_options.empty() && !stringSetting.m_options.empty())
1369     m_options = stringSetting.m_options;
1370   if (m_optionsFillerName.empty() && !stringSetting.m_optionsFillerName.empty())
1371     m_optionsFillerName = stringSetting.m_optionsFillerName;
1372   if (m_optionsFiller == nullptr && stringSetting.m_optionsFiller != nullptr)
1373     m_optionsFiller = stringSetting.m_optionsFiller;
1374   if (m_optionsFillerData == nullptr && stringSetting.m_optionsFillerData != nullptr)
1375     m_optionsFillerData = stringSetting.m_optionsFillerData;
1376   if (m_dynamicOptions.empty() && !stringSetting.m_dynamicOptions.empty())
1377     m_dynamicOptions = stringSetting.m_dynamicOptions;
1378   if (m_optionsSort == SettingOptionsSort::NoSorting &&
1379       stringSetting.m_optionsSort != SettingOptionsSort::NoSorting)
1380     m_optionsSort = stringSetting.m_optionsSort;
1381 }
1382 
Deserialize(const TiXmlNode * node,bool update)1383 bool CSettingString::Deserialize(const TiXmlNode *node, bool update /* = false */)
1384 {
1385   CExclusiveLock lock(m_critical);
1386 
1387   if (!CSetting::Deserialize(node, update))
1388     return false;
1389 
1390   auto constraints = node->FirstChild(SETTING_XML_ELM_CONSTRAINTS);
1391   if (constraints != nullptr)
1392   {
1393     // get allowempty (needs to be parsed before parsing the default value)
1394     XMLUtils::GetBoolean(constraints, SETTING_XML_ELM_ALLOWEMPTY, m_allowEmpty);
1395 
1396     // Values other than those in options contraints allowed to be added
1397     XMLUtils::GetBoolean(constraints, SETTING_XML_ELM_ALLOWNEWOPTION, m_allowNewOption);
1398 
1399     // get the entries
1400     auto options = constraints->FirstChildElement(SETTING_XML_ELM_OPTIONS);
1401     if (options != nullptr && options->FirstChild() != nullptr)
1402     {
1403       if (!DeserializeOptionsSort(options, m_optionsSort))
1404         s_logger->warn("invalid \"sort\" attribute of <" SETTING_XML_ELM_OPTIONS "> for \"{}\"",
1405                        m_id);
1406 
1407       if (options->FirstChild()->Type() == TiXmlNode::TINYXML_TEXT)
1408       {
1409         m_optionsFillerName = options->FirstChild()->ValueStr();
1410         if (!m_optionsFillerName.empty())
1411         {
1412           m_optionsFiller = reinterpret_cast<StringSettingOptionsFiller>(m_settingsManager->GetSettingOptionsFiller(shared_from_base<CSettingString>()));
1413         }
1414       }
1415       else
1416       {
1417         m_translatableOptions.clear();
1418         auto optionElement = options->FirstChildElement(SETTING_XML_ELM_OPTION);
1419         while (optionElement != nullptr)
1420         {
1421           TranslatableStringSettingOption entry;
1422           if (optionElement->QueryIntAttribute(SETTING_XML_ATTR_LABEL, &entry.first) == TIXML_SUCCESS && entry.first > 0)
1423           {
1424             entry.second = optionElement->FirstChild()->Value();
1425             m_translatableOptions.push_back(entry);
1426           }
1427           else
1428           {
1429             const std::string value = optionElement->FirstChild()->Value();
1430             // if a specific "label" attribute is present use it otherwise use the value as label
1431             std::string label = value;
1432             optionElement->QueryStringAttribute(SETTING_XML_ATTR_LABEL, &label);
1433 
1434             m_options.emplace_back(label, value);
1435           }
1436 
1437           optionElement = optionElement->NextSiblingElement(SETTING_XML_ELM_OPTION);
1438         }
1439       }
1440     }
1441   }
1442 
1443   // get the default value
1444   std::string value;
1445   if (XMLUtils::GetString(node, SETTING_XML_ELM_DEFAULT, value) &&
1446      (!value.empty() || m_allowEmpty))
1447     m_value = m_default = value;
1448   else if (!update && !m_allowEmpty)
1449   {
1450     s_logger->error("error reading the default value of \"{}\"", m_id);
1451     return false;
1452   }
1453 
1454   return true;
1455 }
1456 
CheckValidity(const std::string & value) const1457 bool CSettingString::CheckValidity(const std::string &value) const
1458 {
1459   CSharedLock lock(m_critical);
1460   if (!m_allowEmpty && value.empty())
1461     return false;
1462 
1463   if (!m_translatableOptions.empty())
1464   {
1465     if (!CheckSettingOptionsValidity(value, m_translatableOptions))
1466       return false;
1467   }
1468   else if (!m_options.empty() && !m_allowNewOption)
1469   {
1470     if (!CheckSettingOptionsValidity(value, m_options))
1471       return false;
1472   }
1473 
1474   return true;
1475 }
1476 
SetValue(const std::string & value)1477 bool CSettingString::SetValue(const std::string &value)
1478 {
1479   CExclusiveLock lock(m_critical);
1480 
1481   if (value == m_value)
1482     return true;
1483 
1484   if (!CheckValidity(value))
1485     return false;
1486 
1487   std::string oldValue = m_value;
1488   m_value = value;
1489 
1490   if (!OnSettingChanging(shared_from_base<CSettingString>()))
1491   {
1492     m_value = oldValue;
1493 
1494     // the setting couldn't be changed because one of the
1495     // callback handlers failed the OnSettingChanging()
1496     // callback so we need to let all the callback handlers
1497     // know that the setting hasn't changed
1498     OnSettingChanging(shared_from_base<CSettingString>());
1499     return false;
1500   }
1501 
1502   m_changed = m_value != m_default;
1503   OnSettingChanged(shared_from_base<CSettingString>());
1504   return true;
1505 }
1506 
SetDefault(const std::string & value)1507 void CSettingString::SetDefault(const std::string &value)
1508 {
1509   CSharedLock lock(m_critical);
1510 
1511   m_default = value;
1512   if (!m_changed)
1513     m_value = m_default;
1514 }
1515 
GetOptionsType() const1516 SettingOptionsType CSettingString::GetOptionsType() const
1517 {
1518   CSharedLock lock(m_critical);
1519   if (!m_translatableOptions.empty())
1520     return SettingOptionsType::StaticTranslatable;
1521   if (!m_options.empty())
1522     return SettingOptionsType::Static;
1523   if (!m_optionsFillerName.empty() || m_optionsFiller != nullptr)
1524     return SettingOptionsType::Dynamic;
1525 
1526   return SettingOptionsType::Unknown;
1527 }
1528 
UpdateDynamicOptions()1529 StringSettingOptions CSettingString::UpdateDynamicOptions()
1530 {
1531   CExclusiveLock lock(m_critical);
1532   StringSettingOptions options;
1533   if (m_optionsFiller == nullptr &&
1534      (m_optionsFillerName.empty() || m_settingsManager == nullptr))
1535     return options;
1536 
1537   if (m_optionsFiller == nullptr)
1538   {
1539     m_optionsFiller = reinterpret_cast<StringSettingOptionsFiller>(m_settingsManager->GetSettingOptionsFiller(shared_from_base<CSettingString>()));
1540     if (m_optionsFiller == nullptr)
1541     {
1542       s_logger->error("unknown options filler \"{}\" of \"{}\"", m_optionsFillerName, m_id);
1543       return options;
1544     }
1545   }
1546 
1547   std::string bestMatchingValue = m_value;
1548   m_optionsFiller(shared_from_base<CSettingString>(), options, bestMatchingValue, m_optionsFillerData);
1549 
1550   if (bestMatchingValue != m_value)
1551     SetValue(bestMatchingValue);
1552 
1553   // check if the list of items has changed
1554   bool changed = m_dynamicOptions.size() != options.size();
1555   if (!changed)
1556   {
1557     for (size_t index = 0; index < options.size(); index++)
1558     {
1559       if (options[index].label.compare(m_dynamicOptions[index].label) != 0 ||
1560           options[index].value.compare(m_dynamicOptions[index].value) != 0)
1561       {
1562         changed = true;
1563         break;
1564       }
1565     }
1566   }
1567 
1568   if (changed)
1569   {
1570     m_dynamicOptions = options;
1571     OnSettingPropertyChanged(shared_from_base<CSettingString>(), "options");
1572   }
1573 
1574   return options;
1575 }
1576 
copy(const CSettingString & setting)1577 void CSettingString::copy(const CSettingString &setting)
1578 {
1579   CSetting::Copy(setting);
1580 
1581   CExclusiveLock lock(m_critical);
1582   m_value = setting.m_value;
1583   m_default = setting.m_default;
1584   m_allowEmpty = setting.m_allowEmpty;
1585   m_allowNewOption = setting.m_allowNewOption;
1586   m_translatableOptions = setting.m_translatableOptions;
1587   m_options = setting.m_options;
1588   m_optionsFillerName = setting.m_optionsFillerName;
1589   m_optionsFiller = setting.m_optionsFiller;
1590   m_optionsFillerData = setting.m_optionsFillerData;
1591   m_dynamicOptions = setting.m_dynamicOptions;
1592 }
1593 
CSettingAction(const std::string & id,CSettingsManager * settingsManager)1594 CSettingAction::CSettingAction(const std::string& id,
1595                                CSettingsManager* settingsManager /* = nullptr */)
1596   : CSetting(id, settingsManager, "CSettingAction")
1597 { }
1598 
CSettingAction(const std::string & id,int label,CSettingsManager * settingsManager)1599 CSettingAction::CSettingAction(const std::string& id,
1600                                int label,
1601                                CSettingsManager* settingsManager /* = nullptr */)
1602   : CSetting(id, settingsManager, "CSettingAction")
1603 {
1604   SetLabel(label);
1605 }
1606 
CSettingAction(const std::string & id,const CSettingAction & setting)1607 CSettingAction::CSettingAction(const std::string& id, const CSettingAction& setting)
1608   : CSetting(id, setting, "CSettingAction"), m_data(setting.m_data)
1609 { }
1610 
Clone(const std::string & id) const1611 SettingPtr CSettingAction::Clone(const std::string &id) const
1612 {
1613   return std::make_shared<CSettingAction>(id, *this);
1614 }
1615 
MergeDetails(const CSetting & other)1616 void CSettingAction::MergeDetails(const CSetting& other)
1617 {
1618   if (other.GetType() != SettingType::Action)
1619     return;
1620 
1621   const auto& actionSetting = static_cast<const CSettingAction&>(other);
1622   if (!HasData() && actionSetting.HasData())
1623     SetData(actionSetting.GetData());
1624 }
1625 
Deserialize(const TiXmlNode * node,bool update)1626 bool CSettingAction::Deserialize(const TiXmlNode *node, bool update /* = false */)
1627 {
1628   CSharedLock lock(m_critical);
1629 
1630   if (!CSetting::Deserialize(node, update))
1631     return false;
1632 
1633   m_data = XMLUtils::GetString(node, SETTING_XML_ELM_DATA);
1634 
1635   return true;
1636 }
1637