2 * Copyright (C) 2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
24 #include "threads/SingleLock.h"
25 #include "utils/log.h"
26 #include "utils/StringUtils.h"
27 #include "utils/XBMCTinyXML.h"
28 #include "utils/XMLUtils.h"
30 #define XML_ELM_LEVEL "level"
31 #define XML_ELM_DEFAULT "default"
32 #define XML_ELM_VALUE "value"
34 #define XML_ELM_CONSTRAINTS "constraints"
35 #define XML_ELM_OPTIONS "options"
36 #define XML_ELM_MINIMUM "minimum"
37 #define XML_ELM_STEP "step"
38 #define XML_ELM_MAXIMUM "maximum"
40 CSetting::CSetting(const std::string &id, CSettingsManager *settingsManager /* = NULL */)
41 : ISetting(id, settingsManager),
43 m_label(-1), m_help(-1),
44 m_level(SettingLevelStandard),
48 CSetting::CSetting(const std::string &id, const CSetting &setting)
49 : ISetting(id, setting.m_settingsManager),
51 m_label(-1), m_help(-1),
52 m_level(SettingLevelStandard),
59 bool CSetting::Deserialize(const TiXmlNode *node, bool update /* = false */)
61 // handle <visible> conditions
62 if (!ISetting::Deserialize(node, update))
65 const TiXmlElement *element = node->ToElement();
69 // get the attributes label and help
71 if (element->QueryIntAttribute(XML_ATTR_LABEL, &m_label) == TIXML_SUCCESS && tmp > 0)
73 if (element->QueryIntAttribute(XML_ATTR_HELP, &m_help) == TIXML_SUCCESS && tmp > 0)
78 if (XMLUtils::GetInt(node, XML_ELM_LEVEL, level))
79 m_level = (SettingLevel)level;
81 if (m_level < (int)SettingLevelBasic || m_level > (int)SettingLevelInternal)
82 m_level = SettingLevelStandard;
84 const TiXmlNode *dependencies = node->FirstChild("dependencies");
85 if (dependencies != NULL)
87 const TiXmlNode *dependencyNode = dependencies->FirstChild("dependency");
88 while (dependencyNode != NULL)
90 CSettingDependency dependency(m_settingsManager);
91 if (dependency.Deserialize(dependencyNode))
92 m_dependencies.push_back(dependency);
94 CLog::Log(LOGWARNING, "CSetting: error reading <dependency> tag of \"%s\"", m_id.c_str());
96 dependencyNode = dependencyNode->NextSibling("dependency");
100 const TiXmlElement *control = node->FirstChildElement("control");
103 if (!m_control.Deserialize(control, update) ||
104 m_control.GetType() == SettingControlTypeNone)
106 CLog::Log(LOGERROR, "CSetting: error reading <control> tag of \"%s\"", m_id.c_str());
111 const TiXmlNode *updates = node->FirstChild("updates");
114 const TiXmlElement *updateElem = updates->FirstChildElement("update");
115 while (updateElem != NULL)
117 CSettingUpdate update;
118 if (update.Deserialize(updateElem))
120 if (!m_updates.insert(update).second)
121 CLog::Log(LOGWARNING, "CSetting: duplicate <update> definition for \"%s\"", m_id.c_str());
124 CLog::Log(LOGWARNING, "CSetting: error reading <update> tag of \"%s\"", m_id.c_str());
126 updateElem = updateElem->NextSiblingElement("update");
130 if ((m_control.GetType() == SettingControlTypeSpinner || m_control.GetType() == SettingControlTypeEdit) &&
131 m_control.GetFormat() == SettingControlFormatNone)
133 CLog::Log(LOGERROR, "CSetting: invalid <control> tag of \"%s\"", m_id.c_str());
140 bool CSetting::OnSettingChanging(const CSetting *setting)
142 if (m_callback == NULL)
145 return m_callback->OnSettingChanging(setting);
148 void CSetting::OnSettingChanged(const CSetting *setting)
150 if (m_callback == NULL)
153 m_callback->OnSettingChanged(setting);
156 void CSetting::OnSettingAction(const CSetting *setting)
158 if (m_callback == NULL)
161 m_callback->OnSettingAction(setting);
164 bool CSetting::OnSettingUpdate(CSetting* &setting, const char *oldSettingId, const TiXmlNode *oldSettingNode)
166 if (m_callback == NULL)
169 return m_callback->OnSettingUpdate(setting, oldSettingId, oldSettingNode);
172 void CSetting::Copy(const CSetting &setting)
174 SetVisible(setting.IsVisible());
175 m_callback = setting.m_callback;
176 m_label = setting.m_label;
177 m_help = setting.m_help;
178 m_level = setting.m_level;
179 m_control = setting.m_control;
180 m_dependencies = setting.m_dependencies;
181 m_updates = setting.m_updates;
182 m_changed = setting.m_changed;
185 CSettingBool::CSettingBool(const std::string &id, CSettingsManager *settingsManager /* = NULL */)
186 : CSetting(id, settingsManager),
187 m_value(false), m_default(false)
189 m_control.SetType(SettingControlTypeCheckmark);
190 m_control.SetFormat(SettingControlFormatBoolean);
191 m_control.SetAttributes(SettingControlAttributeNone);
194 CSettingBool::CSettingBool(const std::string &id, const CSettingBool &setting)
195 : CSetting(id, setting)
200 CSettingBool::CSettingBool(const std::string &id, int label, bool value, CSettingsManager *settingsManager /* = NULL */)
201 : CSetting(id, settingsManager),
202 m_value(value), m_default(value)
206 m_control.SetType(SettingControlTypeCheckmark);
207 m_control.SetFormat(SettingControlFormatBoolean);
208 m_control.SetAttributes(SettingControlAttributeNone);
211 bool CSettingBool::Deserialize(const TiXmlNode *node, bool update /* = false */)
213 CSingleLock lock(m_critical);
215 if (!CSetting::Deserialize(node, update))
218 if (m_control.GetType() != SettingControlTypeCheckmark ||
219 m_control.GetFormat() != SettingControlFormatBoolean ||
220 m_control.GetAttributes() != SettingControlAttributeNone)
222 CLog::Log(LOGERROR, "CSettingBool: invalid <control> of \"%s\"", m_id.c_str());
226 // get the default value
228 if (XMLUtils::GetBoolean(node, XML_ELM_DEFAULT, value))
229 m_value = m_default = value;
232 CLog::Log(LOGERROR, "CSettingBool: error reading the default value of \"%s\"", m_id.c_str());
239 bool CSettingBool::FromString(const std::string &value)
242 if (!fromString(value, bValue))
245 return SetValue(bValue);
248 std::string CSettingBool::ToString() const
250 return m_value ? "true" : "false";
253 bool CSettingBool::Equals(const std::string &value) const
256 return (fromString(value, bValue) && m_value == bValue);
259 bool CSettingBool::CheckValidity(const std::string &value) const
262 return fromString(value, bValue);
265 bool CSettingBool::SetValue(bool value)
267 CSingleLock lock(m_critical);
269 if (value == m_value)
272 bool oldValue = m_value;
275 if (!OnSettingChanging(this))
279 // the setting couldn't be changed because one of the
280 // callback handlers failed the OnSettingChanging()
281 // callback so we need to let all the callback handlers
282 // know that the setting hasn't changed
283 OnSettingChanging(this);
287 m_changed = m_value != m_default;
288 OnSettingChanged(this);
292 void CSettingBool::SetDefault(bool value)
294 CSingleLock lock(m_critical);
301 void CSettingBool::copy(const CSettingBool &setting)
303 CSetting::Copy(setting);
305 m_value = setting.m_value;
306 m_default = setting.m_default;
309 bool CSettingBool::fromString(const std::string &strValue, bool &value) const
311 CSingleLock lock(m_critical);
313 if (StringUtils::EqualsNoCase(strValue, "true"))
318 if (StringUtils::EqualsNoCase(strValue, "false"))
327 CSettingInt::CSettingInt(const std::string &id, CSettingsManager *settingsManager /* = NULL */)
328 : CSetting(id, settingsManager),
329 m_value(0), m_default(0),
330 m_min(0), m_step(1), m_max(0),
331 m_format(-1), m_labelMin(-1), m_strFormat("%i")
334 CSettingInt::CSettingInt(const std::string &id, const CSettingInt &setting)
335 : CSetting(id, setting)
340 CSettingInt::CSettingInt(const std::string &id, int label, int value, int minimum, int step, int maximum, int format, int minimumLabel, CSettingsManager *settingsManager /* = NULL */)
341 : CSetting(id, settingsManager),
342 m_value(value), m_default(value),
343 m_min(minimum), m_step(step), m_max(maximum),
344 m_format(format), m_labelMin(minimumLabel), m_strFormat("%i")
348 m_control.SetType(SettingControlTypeSpinner);
350 m_control.SetFormat(SettingControlFormatInteger);
352 m_control.SetFormat(SettingControlFormatString);
353 m_control.SetAttributes(SettingControlAttributeNone);
356 CSettingInt::CSettingInt(const std::string &id, int label, int value, int minimum, int step, int maximum, const std::string &format, CSettingsManager *settingsManager /* = NULL */)
357 : CSetting(id, settingsManager),
358 m_value(value), m_default(value),
359 m_min(minimum), m_step(step), m_max(maximum),
360 m_format(-1), m_labelMin(-1), m_strFormat(format)
364 m_control.SetType(SettingControlTypeSpinner);
368 m_control.SetFormat(SettingControlFormatInteger);
371 m_control.SetFormat(SettingControlFormatString);
372 m_control.SetAttributes(SettingControlAttributeNone);
375 CSettingInt::CSettingInt(const std::string &id, int label, int value, const SettingOptions &options, CSettingsManager *settingsManager /* = NULL */)
376 : CSetting(id, settingsManager),
377 m_value(value), m_default(value),
378 m_min(0), m_step(1), m_max(0),
379 m_format(-1), m_labelMin(-1), m_strFormat("%i"),
384 m_control.SetType(SettingControlTypeSpinner);
385 m_control.SetFormat(SettingControlFormatString);
386 m_control.SetAttributes(SettingControlAttributeNone);
389 bool CSettingInt::Deserialize(const TiXmlNode *node, bool update /* = false */)
391 CSingleLock lock(m_critical);
393 if (!CSetting::Deserialize(node, update))
396 if (m_control.GetType() == SettingControlTypeCheckmark)
398 CLog::Log(LOGERROR, "CSettingInt: invalid <control> of \"%s\"", m_id.c_str());
402 // get the default value
404 if (XMLUtils::GetInt(node, XML_ELM_DEFAULT, value))
405 m_value = m_default = value;
408 CLog::Log(LOGERROR, "CSettingInt: error reading the default value of \"%s\"", m_id.c_str());
412 const TiXmlNode *constraints = node->FirstChild(XML_ELM_CONSTRAINTS);
413 if (constraints != NULL)
416 const TiXmlNode *options = constraints->FirstChild(XML_ELM_OPTIONS);
417 if (options != NULL && options->FirstChild() != NULL)
419 if (options->FirstChild()->Type() == TiXmlNode::TINYXML_TEXT)
420 m_optionsFiller = options->FirstChild()->ValueStr();
424 const TiXmlElement *optionElement = options->FirstChildElement("option");
425 while (optionElement != NULL)
427 std::pair<int, int> entry;
428 if (optionElement->QueryIntAttribute("label", &entry.first) == TIXML_SUCCESS && entry.first > 0)
430 entry.second = strtol(optionElement->FirstChild()->Value(), NULL, 10);
431 m_options.push_back(entry);
434 optionElement = optionElement->NextSiblingElement("option");
440 if (XMLUtils::GetInt(constraints, XML_ELM_MINIMUM, m_min) &&
441 m_control.GetFormat() == SettingControlFormatString)
443 const TiXmlElement *minimumElement = constraints->FirstChildElement(XML_ELM_MINIMUM);
444 if (minimumElement->QueryIntAttribute(XML_ATTR_LABEL, &m_labelMin) != TIXML_SUCCESS)
448 XMLUtils::GetInt(constraints, XML_ELM_STEP, m_step);
450 XMLUtils::GetInt(constraints, XML_ELM_MAXIMUM, m_max);
452 if (m_control.GetFormat() == SettingControlFormatString)
454 XMLUtils::GetInt(constraints, "formatlabel", m_format);
458 CStdString strFormat;
459 if (XMLUtils::GetString(constraints, "format", strFormat) && !strFormat.empty())
460 m_strFormat = strFormat;
468 bool CSettingInt::FromString(const std::string &value)
471 if (!fromString(value, iValue))
474 return SetValue(iValue);
477 std::string CSettingInt::ToString() const
479 std::ostringstream oss;
485 bool CSettingInt::Equals(const std::string &value) const
488 return (fromString(value, iValue) && m_value == iValue);
491 bool CSettingInt::CheckValidity(const std::string &value) const
494 if (!fromString(value, iValue))
497 return CheckValidity(iValue);
500 bool CSettingInt::CheckValidity(int value) const
502 if (!m_options.empty())
504 //if the setting is an std::map, check if we got a valid value before assigning it
506 for (SettingOptions::const_iterator it = m_options.begin(); it != m_options.end(); it++)
508 if (it->second == value)
518 else if (m_optionsFiller.empty() && m_min != m_max &&
519 (value < m_min || value > m_max))
525 bool CSettingInt::SetValue(int value)
527 CSingleLock lock(m_critical);
529 if (value == m_value)
532 if (!CheckValidity(value))
535 int oldValue = m_value;
538 if (!OnSettingChanging(this))
542 // the setting couldn't be changed because one of the
543 // callback handlers failed the OnSettingChanging()
544 // callback so we need to let all the callback handlers
545 // know that the setting hasn't changed
546 OnSettingChanging(this);
550 m_changed = m_value != m_default;
551 OnSettingChanged(this);
555 void CSettingInt::SetDefault(int value)
557 CSingleLock lock(m_critical);
564 void CSettingInt::copy(const CSettingInt &setting)
566 CSetting::Copy(setting);
568 m_value = setting.m_value;
569 m_default = setting.m_default;
570 m_min = setting.m_min;
571 m_step = setting.m_step;
572 m_max = setting.m_max;
573 m_format = setting.m_format;
574 m_labelMin = setting.m_labelMin;
575 m_strFormat = setting.m_strFormat;
576 m_options = setting.m_options;
577 m_optionsFiller = setting.m_optionsFiller;
580 bool CSettingInt::fromString(const std::string &strValue, int &value) const
582 if (strValue.empty())
586 value = (int)strtol(strValue.c_str(), &end, 10);
587 if (end != NULL && *end != '\0')
593 CSettingNumber::CSettingNumber(const std::string &id, CSettingsManager *settingsManager /* = NULL */)
594 : CSetting(id, settingsManager),
595 m_value(0.0), m_default(0.0),
596 m_min(0.0), m_step(1.0), m_max(0.0)
599 CSettingNumber::CSettingNumber(const std::string &id, const CSettingNumber &setting)
600 : CSetting(id, setting)
605 CSettingNumber::CSettingNumber(const std::string &id, int label, float value, float minimum, float step, float maximum, CSettingsManager *settingsManager /* = NULL */)
606 : CSetting(id, settingsManager),
607 m_value(value), m_default(value),
608 m_min(minimum), m_step(step), m_max(maximum)
612 m_control.SetType(SettingControlTypeSpinner);
613 m_control.SetFormat(SettingControlFormatNumber);
614 m_control.SetAttributes(SettingControlAttributeNone);
617 bool CSettingNumber::Deserialize(const TiXmlNode *node, bool update /* = false */)
619 CSingleLock lock(m_critical);
621 if (!CSetting::Deserialize(node, update))
624 if (m_control.GetType() == SettingControlTypeCheckmark)
626 CLog::Log(LOGERROR, "CSettingInt: invalid <control> of \"%s\"", m_id.c_str());
630 // get the default value
632 if (XMLUtils::GetDouble(node, XML_ELM_DEFAULT, value))
633 m_value = m_default = value;
636 CLog::Log(LOGERROR, "CSettingNumber: error reading the default value of \"%s\"", m_id.c_str());
640 const TiXmlNode *constraints = node->FirstChild(XML_ELM_CONSTRAINTS);
641 if (constraints != NULL)
643 // get the minimum value
644 XMLUtils::GetDouble(constraints, XML_ELM_MINIMUM, m_min);
645 // get the step value
646 XMLUtils::GetDouble(constraints, XML_ELM_STEP, m_step);
647 // get the maximum value
648 XMLUtils::GetDouble(constraints, XML_ELM_MAXIMUM, m_max);
654 bool CSettingNumber::FromString(const std::string &value)
657 if (!fromString(value, dValue))
660 return SetValue(dValue);
663 std::string CSettingNumber::ToString() const
665 std::ostringstream oss;
671 bool CSettingNumber::Equals(const std::string &value) const
674 return (fromString(value, dValue) && m_value == dValue);
677 bool CSettingNumber::CheckValidity(const std::string &value) const
680 if (!fromString(value, dValue))
683 return CheckValidity(dValue);
686 bool CSettingNumber::CheckValidity(double value) const
688 if (m_min != m_max &&
689 (value < m_min || value > m_max))
695 bool CSettingNumber::SetValue(double value)
697 CSingleLock lock(m_critical);
699 if (value == m_value)
702 if (!CheckValidity(value))
705 double oldValue = m_value;
708 if (!OnSettingChanging(this))
712 // the setting couldn't be changed because one of the
713 // callback handlers failed the OnSettingChanging()
714 // callback so we need to let all the callback handlers
715 // know that the setting hasn't changed
716 OnSettingChanging(this);
720 m_changed = m_value != m_default;
721 OnSettingChanged(this);
725 void CSettingNumber::SetDefault(double value)
727 CSingleLock lock(m_critical);
734 void CSettingNumber::copy(const CSettingNumber &setting)
736 CSetting::Copy(setting);
738 m_value = setting.m_value;
739 m_default = setting.m_default;
740 m_min = setting.m_min;
741 m_step = setting.m_step;
742 m_max = setting.m_max;
745 bool CSettingNumber::fromString(const std::string &strValue, double &value) const
747 if (strValue.empty())
751 value = (int)strtod(strValue.c_str(), &end);
752 if (end != NULL && *end != '\0')
758 CSettingString::CSettingString(const std::string &id, CSettingsManager *settingsManager /* = NULL */)
759 : CSetting(id, settingsManager),
760 m_allowEmpty(false), m_heading(-1)
763 CSettingString::CSettingString(const std::string &id, const CSettingString &setting)
764 : CSetting(id, setting)
769 CSettingString::CSettingString(const std::string &id, int label, const std::string &value, CSettingsManager *settingsManager /* = NULL */)
770 : CSetting(id, settingsManager),
771 m_value(value), m_default(value),
772 m_allowEmpty(false), m_heading(-1)
776 m_control.SetType(SettingControlTypeEdit);
777 m_control.SetFormat(SettingControlFormatString);
778 m_control.SetAttributes(SettingControlAttributeNone);
781 bool CSettingString::Deserialize(const TiXmlNode *node, bool update /* = false */)
783 CSingleLock lock(m_critical);
785 if (!CSetting::Deserialize(node, update))
788 if (m_control.GetType() == SettingControlTypeCheckmark ||
789 (m_control.GetType() == SettingControlTypeSpinner && (m_control.GetFormat() == SettingControlFormatInteger || m_control.GetFormat() == SettingControlFormatNumber)) ||
790 (m_control.GetType() == SettingControlTypeEdit && m_control.GetFormat() == SettingControlFormatNumber))
792 CLog::Log(LOGERROR, "CSettingString: invalid <control> of \"%s\"", m_id.c_str());
796 XMLUtils::GetInt(node, "heading", m_heading);
798 const TiXmlNode *constraints = node->FirstChild(XML_ELM_CONSTRAINTS);
799 if (constraints != NULL)
801 // get allowempty (needs to be parsed before parsing the default value)
802 XMLUtils::GetBoolean(constraints, "allowempty", m_allowEmpty);
805 const TiXmlNode *options = constraints->FirstChild(XML_ELM_OPTIONS);
806 if (options != NULL && options->FirstChild() != NULL &&
807 options->FirstChild()->Type() == TiXmlNode::TINYXML_TEXT)
808 m_optionsFiller = options->FirstChild()->ValueStr();
811 // get the default value
813 if (XMLUtils::GetString(node, XML_ELM_DEFAULT, value))
814 m_value = m_default = value;
815 else if (!update && !m_allowEmpty)
817 CLog::Log(LOGERROR, "CSettingString: error reading the default value of \"%s\"", m_id.c_str());
824 bool CSettingString::CheckValidity(const std::string &value) const
826 if (!m_allowEmpty && value.empty())
832 bool CSettingString::SetValue(const std::string &value)
834 CSingleLock lock(m_critical);
836 if (value == m_value)
839 if (!CheckValidity(value))
842 std::string oldValue = m_value;
845 if (!OnSettingChanging(this))
849 // the setting couldn't be changed because one of the
850 // callback handlers failed the OnSettingChanging()
851 // callback so we need to let all the callback handlers
852 // know that the setting hasn't changed
853 OnSettingChanging(this);
857 m_changed = m_value != m_default;
858 OnSettingChanged(this);
862 void CSettingString::SetDefault(const std::string &value)
864 CSingleLock lock(m_critical);
871 void CSettingString::copy(const CSettingString &setting)
873 CSetting::Copy(setting);
875 m_value = setting.m_value;
876 m_default = setting.m_default;
877 m_allowEmpty = setting.m_allowEmpty;
878 m_heading = setting.m_heading;
881 CSettingAction::CSettingAction(const std::string &id, CSettingsManager *settingsManager /* = NULL */)
882 : CSetting(id, settingsManager)
884 m_control.SetType(SettingControlTypeButton);
885 m_control.SetFormat(SettingControlFormatAction);
886 m_control.SetAttributes(SettingControlAttributeNone);
889 CSettingAction::CSettingAction(const std::string &id, const CSettingAction &setting)
890 : CSetting(id, setting)
892 m_control.SetType(SettingControlTypeButton);
893 m_control.SetFormat(SettingControlFormatAction);
894 m_control.SetAttributes(SettingControlAttributeNone);
897 bool CSettingAction::Deserialize(const TiXmlNode *node, bool update /* = false */)
899 CSingleLock lock(m_critical);
901 if (!CSetting::Deserialize(node, update))
904 if (m_control.GetType() != SettingControlTypeButton ||
905 m_control.GetFormat() != SettingControlFormatAction ||
906 m_control.GetAttributes() != SettingControlAttributeNone)
908 CLog::Log(LOGERROR, "CSettingAction: invalid <control> of \"%s\"", m_id.c_str());