Prepare 1.0 alpha3 release.
[tagua/yd.git] / src / settings.h
blob5240f2544acd8c3aadb337df1b105f8f624d2c56
1 /*
2 Copyright 2006-2007 Paolo Capriotti <paolo.capriotti@kdemail.net>
4 BSD License
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
9 1. Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
15 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #ifndef SETTINGS_H
28 #define SETTINGS_H
30 #include <map>
31 #include <vector>
32 #include <iostream>
33 #include <QDomElement>
34 #include <QDomText>
35 #include <QObject>
36 #include <QColor>
37 #include <QFont>
39 template <typename T> class TypedSettingRef;
41 template <typename T> struct ReturnType {
42 typedef T type;
43 typedef TypedSettingRef<type> wrapper;
46 template <typename T, size_t n> struct ReturnType<T[n]> {
47 typedef const T* type;
48 typedef type wrapper;
51 template <typename T>
52 struct SettingConversionBase {
53 static typename ReturnType<T>::type getValue(const QDomElement&, const QDomText& text) {
54 return text.data();
57 static void setValue(QDomElement, QDomText text, const T& val) {
58 text.setData(val);
62 template <typename T>
63 struct SettingConversion : public SettingConversionBase<T> { };
65 template <size_t n>
66 struct SettingConversion<char[n]> : public SettingConversionBase<char[n]> {
67 static const char* getValue(const QDomElement&, const QDomText& text) {
68 return qPrintable(text.data());
71 static void setValue(QDomElement, QDomText text, const char* val) {
72 text.setData(val);
76 template <>
77 struct SettingConversion<int> {
78 static int getValue(const QDomElement&, const QDomText& text) {
79 return text.data().toInt();
82 static void setValue(QDomElement, QDomText text, int val) {
83 text.setData(QString::number(val));
87 template <>
88 struct SettingConversion<QColor> {
89 static QColor getValue(const QDomElement& e, const QDomText&) {
90 int r = e.attribute("r", "0").toInt();
91 int g = e.attribute("g", "0").toInt();
92 int b = e.attribute("b", "0").toInt();
94 return QColor(r, g, b);
97 static void setValue(QDomElement e, QDomText, const QColor& val) {
98 e.setAttribute("r", val.red());
99 e.setAttribute("g", val.green());
100 e.setAttribute("b", val.blue());
104 template <>
105 struct SettingConversion<Qt::GlobalColor> : public SettingConversion<QColor> { };
107 template <>
108 struct SettingConversion<QFont> {
109 static QFont getValue(const QDomElement&, const QDomText& text) {
110 QFont res;
111 res.fromString(text.data());
112 return res;
115 static void setValue(QDomElement, QDomText text, const QFont& font) {
116 text.setData(font.toString());
120 template <>
121 struct SettingConversion<bool> {
122 static bool getValue(const QDomElement&, const QDomText& text) {
123 return text.data().toLower() == "true";
126 static void setValue(QDomElement, QDomText text, bool val) {
127 text.setData(val ? "true" : "false");
131 class SettingRefBase {
132 protected:
133 virtual QDomElement element() const = 0;
134 virtual QDomText node() const { return element().firstChild().toText(); }
135 public:
136 virtual ~SettingRefBase() { }
137 template <typename T> typename ReturnType<T>::wrapper operator|(const T& val) const;
138 template <typename T> typename ReturnType<T>::type value(const T& defaultValue) const;
139 template <typename T> typename ReturnType<T>::type value() const;
140 template <typename T> void operator>>(T& out) const;
141 operator bool() const;
142 bool flag(const QString& attr, bool def) const;
145 class SettingConstRef : public SettingRefBase {
146 QDomElement m_element;
147 protected:
148 virtual QDomElement element() const { return m_element; }
149 public:
150 SettingConstRef(const QDomElement& node);
153 class SettingRef : public SettingRefBase {
154 QDomElement m_parent;
155 QString m_key;
156 protected:
157 virtual QDomElement element() const { return m_parent.firstChildElement(m_key); }
158 public:
159 SettingRef(const QDomElement& node, const QString& key);
161 void remove();
162 template <typename T> typename ReturnType<T>::wrapper operator|=(const T& val);
163 template <typename T> typename ReturnType<T>::wrapper operator=(const T& val);
166 // class GenericSettingRef : public SettingRef {
167 // QDomElement m_element;
168 // protected:
169 // virtual QDomElement element() const { return m_element; }
170 // public:
171 // GenericSettingRef(const QDomElement& element, const QString& key);
172 // };
174 template <typename T>
175 class TypedSettingRef {
176 T m_value;
177 public:
178 TypedSettingRef(const T& value)
179 : m_value(value) { }
181 operator typename ReturnType<T>::type() const { return value(); }
182 template <typename U> void operator>>(U& out) const { out = m_value; }
183 typename ReturnType<T>::type value() const { return m_value; }
186 template <typename T> class SettingMap;
189 * @brief The Settings class provides persistent application settings on an XML file.
191 * Use this class to store your application settings if you need an hierarchical
192 * configuration stored on a human-readable text file.
194 * Settings completely abstracts the serialization and deserialization procedures for
195 * simple data types, and allows a customized XML serialization for user defined types.
197 * The configuration is hierarchical, with a Settings object representing a node of the
198 * tree. Each node can contain key-value pairs, accessed as
199 * <code>settings["key"]</code>
200 * both for reading and writing.
202 * Accessing a group returns another Settings object, and there's no need to "close" the
203 * group (like with the QSettings class) when you're done reading or writing.
205 * Settings support aggregates of objects in the form of linear or associative arrays.
207 class Settings {
208 friend class SettingArray;
209 protected:
210 QDomElement m_node;
211 virtual QDomElement node() const { return m_node; }
212 public:
213 /** Create an invalid Settings object. */
214 Settings() { }
215 virtual ~Settings() { }
216 /**
217 * Create a Settings object attached on a specified XML node.
218 * @param node An XML node to be used as the root of the Settings
220 Settings(const QDomElement& node);
222 * Create a copy of the Settings object
224 Settings(const Settings& other);
227 * Assign a Settings object to another.
229 Settings& operator=(const Settings& other);
231 /**
232 * Read the value associated with @a key. Values returned from
233 * a read operation on Settings can be converted using the
234 * template member function @a value.
236 SettingRef operator[](const QString& key);
239 * Just like the above function, but returns a constant reference.
241 SettingConstRef operator[](const QString& key) const;
244 * Overload
246 SettingRef operator[](const char* key) { return operator[](QString(key)); }
249 * Overload
251 SettingConstRef operator[](const char* key) const { return operator[](QString(key)); }
254 * Access a setting group.
255 * @param name The name of the group.
256 * @returns A Settings object representing the specified group.
258 class SettingGroup group(const QString& name) const;
261 * Shotcat to access a setting group, same as \a group
262 * @param name The name of the group.
263 * @returns A Settings object representing the specified group.
265 class SettingGroup operator()(const QString& name) const;
268 * Access a setting map with keys of type @a T.
269 * @param element The name of every map element.
270 * @param key The element acting as a key.
271 * @returns A SettingMap object representing the specified map.
272 * @sa SettingMap
274 template <typename T> SettingMap<T> map(const QString& element, const QString& key) const;
277 * Just like the above function, but clear the returned map. Use this
278 * function if the desired behaviour is to overwrite the existing map
279 * in the associated setting node.
281 template <typename T> SettingMap<T> newMap(const QString& element, const QString& key) const;
284 * Access a setting array.
285 * @param element The name of every array element.
286 * @returns a SettingArray object representing the specified array.
287 * @sa SettingArray
289 class SettingArray array(const QString& element) const;
292 * Just like the above function, but clear the returned array. Use this
293 * function if the desired behaviour is to overwrite the existing array
294 * in the associated setting node.
296 class SettingArray newArray(const QString& element) const;
298 /**
299 * Retrieve a global boolean flag for the associated setting node.
300 * Boolean flags are stored as XML attributes in the configuration file.
301 * @param attr The name of the flag.
302 * @param def The default value for the flag.
303 * @returns The flag value, or @a def, in case the specified flag does not exist.
305 bool flag(const QString& attr, bool def) const;
306 /**
307 * Set a global boolean flag for the associated setting node.
308 * Boolean flags are stored as XML attributes in the configuration file.
309 * @param attr The name of the flag.
310 * @param value The value of the flag.
312 void setFlag(const QString& attr, bool value);
315 * @note This function is internal to the Settings class framework. Do not use
316 * in client code.
318 static void ensureExistence(QDomElement& node, QDomElement parent, const QString& name);
321 #ifdef __SETTINGS_DEBUG
322 void dump() const;
323 #endif
327 * @brief A group of settings.
329 * Client code can use instance of this class just like they were Settings objects.
330 * The only external difference is that SettingGroup object can be implicitly
331 * converted to bool to test their existence.
333 * SettingGroup can be accessed with the Settings member function @a group.
334 * @sa Settings::group
336 class SettingGroup : public Settings {
337 QString m_name;
338 QDomElement m_parent;
339 protected:
340 virtual QDomElement node() const;
341 public:
342 SettingGroup(const QDomElement& parent, const QString& name);
345 * Check if the SettingGroup exists.
347 operator bool() const { return !m_node.isNull(); }
348 void remove();
352 * @brief A collection of setting nodes arranged as an associative container.
354 * A setting map is a collection of setting nodes, all having the same
355 * parent node and the same name, and containing an inner node acting as
356 * a key.
357 * The following is an example of how the configuration node containing a
358 * setting map may look like. The element name is "event" and the key name
359 * is "name".
361 * <pre>
362 * <event>
363 * <name>click</name>
364 * <action>action1</action>
365 * </event>
366 * <event>
367 * <name>double-click</name>
368 * <action>action2</action>
369 * </event>
370 * </pre>
372 * Other than the key, each element of the map may contain any arbitrarily
373 * nested nodes.
375 * To use a map, you simply access its elements by keys using the @a get member
376 * function, which returns a Settings object associated to the specified element.
378 * Elements are cached as an STL map, which is kept in sync with the configuration
379 * file, so read operation have the same complexity of the corresponding
380 * STL operations (i.e. logarithmic).
382 template <typename Key>
383 class SettingMap {
384 std::map<Key, Settings> m_map;
385 protected:
386 mutable QDomElement m_node;
387 virtual QDomElement node() const { return m_node; }
388 QString m_element;
389 QString m_key;
390 public:
391 SettingMap(const QDomElement& node, const QString& element, const QString& key);
392 virtual ~SettingMap() { }
394 * @returns The number of elements in the map.
396 uint size() const { return m_map.size(); }
399 * Retrieve an element by key.
400 * @param index The key of the element.
401 * @returns The Settings object corresponding to the specified element.
403 Settings get(const Key& index) const;
406 * Insert an element into the map.
407 * @param index The key of the element.
408 * @returns An Settings object containing only the specified key. This object
409 * can be used to add arbitrary content to the map element.
411 Settings insert(const Key& index);
414 * Clear the map, removing all its elements.
416 void clear();
420 * @brief A collection of nodes, acting as a linear container.
422 * A setting array is like a setting map without a special key
423 * node inside every element.
425 * The elements of a setting array can be only accessed by a numerical
426 * index, just like an STL vector.
428 * SettingsArray provides a STL-like container interface with
429 * random access constant iterators, so that the collection can be
430 * used for STL algorithms acting read-only on it.
432 class SettingArray {
433 std::vector<Settings> m_array;
434 protected:
435 mutable QDomElement m_node;
436 virtual QDomElement node() const { return m_node; }
437 QString m_element;
438 public:
439 typedef std::vector<Settings>::const_iterator iterator;
440 typedef std::vector<Settings>::const_iterator const_iterator;
442 SettingArray(const QDomElement& node, const QString& element);
443 virtual ~SettingArray() { }
446 * @returns The number of elements in the array.
448 uint size() const { return m_array.size(); }
451 * Retrieve an element by index.
452 * @param index The (0-based) index of the element.
453 * @returns A Settings object associated to the specified element.
455 Settings get(int index) const;
458 * Insert an element before the specified index.
459 * @param index The (0-based) index of the element.
460 * @returns An empty Settings object. This object can be used to add
461 * arbitrary content to the array element.
463 Settings insert(int index);
466 * Append an element into the array.
467 * @returns An empty Settings object. This object can be used to add
468 * arbitrary content to the array element.
470 Settings append();
473 * Clear the array, removing all its elements.
475 void clear();
478 * @returns A random access iterator pointing to the beginning of the array.
480 const_iterator begin() const { return m_array.begin(); }
483 * @returns A random access iterator pointing past the end of the array.
485 const_iterator end() const { return m_array.end(); }
487 operator bool() const { return !m_node.isNull(); }
491 // Implementation
493 template <typename T>
494 typename ReturnType<T>::type SettingRefBase::value(const T& def) const {
495 if (element().isNull()) return def;
496 else return SettingConversion<T>::getValue(element(), node());
499 template <typename T>
500 typename ReturnType<T>::type SettingRefBase::value() const {
501 return value(T());
504 template <typename T>
505 typename ReturnType<T>::wrapper SettingRefBase::operator|(const T& val) const {
506 return value(val);
509 template <typename T>
510 void SettingRefBase::operator>>(T& out) const {
511 out = value(out);
514 template <typename T>
515 typename ReturnType<T>::wrapper SettingRef::operator|=(const T& val) {
516 if (element().isNull())
517 return operator=(val);
518 return value(val);
521 template <typename T>
522 typename ReturnType<T>::wrapper SettingRef::operator=(const T& val) {
523 if (element().isNull())
524 m_parent.appendChild(m_parent.ownerDocument().createElement(m_key));
525 element().appendChild(element().ownerDocument().createTextNode(""));
526 SettingConversion<T>::setValue(element(), node(), val);
527 return val;
530 template <typename T>
531 SettingMap<T> Settings::map(const QString& element, const QString& key) const {
532 return SettingMap<T>(node(), element, key);
535 template <typename T>
536 SettingMap<T> Settings::newMap(const QString& element, const QString& key) const {
537 SettingMap<T> res(node(), element, key);
538 res.clear();
539 return res;
543 template <typename Key>
544 SettingMap<Key>::SettingMap(const QDomElement& node, const QString& element, const QString& key)
545 : m_node(node)
546 , m_element(element)
547 , m_key(key) {
548 if (!m_node.isNull()) {
549 // scan all 'name' elements of node
550 QDomNodeList elements = m_node.elementsByTagName(m_element);
551 for (int i = 0; i < elements.size(); i++) {
552 QDomElement el = elements.item(i).toElement();
554 // find the key inside el
555 QDomElement key_element = el.firstChildElement(m_key);
556 Key k = SettingConstRef(key_element).value<Key>();
558 // insert the corresponding Settings object into the map
559 m_map[k] = el;
564 template <typename Key>
565 Settings SettingMap<Key>::get(const Key& key) const {
566 return m_map.find(key)->second;
569 template <typename Key>
570 Settings SettingMap<Key>::insert(const Key& key) {
571 typename std::map<Key, Settings>::const_iterator it = m_map.find(key);
572 if (it != m_map.end())
573 return it->second;
574 else {
575 QDomElement el = node().ownerDocument().createElement(m_element);
576 node().appendChild(el);
577 Settings res = el;
578 m_map[key] = res;
580 res[m_key] = key;
581 return res;
585 template <typename Key>
586 void SettingMap<Key>::clear() {
587 QDomNode it = node().firstChild();
588 while (!it.isNull()) {
589 QDomNode nextit = it.nextSibling();
590 QDomElement e = it.toElement();
591 if (!e.isNull() && e.tagName() == m_element)
592 node().removeChild(e);
593 it = nextit;
597 #endif // SETTINGS_H