2 Copyright 2006-2007 Paolo Capriotti <paolo.capriotti@kdemail.net>
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
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.
33 #include <QDomElement>
39 template <typename T
> class TypedSettingRef
;
41 template <typename T
> struct ReturnType
{
43 typedef TypedSettingRef
<type
> wrapper
;
46 template <typename T
, size_t n
> struct ReturnType
<T
[n
]> {
47 typedef const T
* type
;
52 struct SettingConversionBase
{
53 static typename ReturnType
<T
>::type
getValue(const QDomElement
&, const QDomText
& text
) {
57 static void setValue(QDomElement
, QDomText text
, const T
& val
) {
63 struct SettingConversion
: public SettingConversionBase
<T
> { };
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
) {
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
));
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());
105 struct SettingConversion
<Qt::GlobalColor
> : public SettingConversion
<QColor
> { };
108 struct SettingConversion
<QFont
> {
109 static QFont
getValue(const QDomElement
&, const QDomText
& text
) {
111 res
.fromString(text
.data());
115 static void setValue(QDomElement
, QDomText text
, const QFont
& font
) {
116 text
.setData(font
.toString());
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
{
133 virtual QDomElement
element() const = 0;
134 virtual QDomText
node() const { return element().firstChild().toText(); }
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
;
148 virtual QDomElement
element() const { return m_element
; }
150 SettingConstRef(const QDomElement
& node
);
153 class SettingRef
: public SettingRefBase
{
154 QDomElement m_parent
;
157 virtual QDomElement
element() const { return m_parent
.firstChildElement(m_key
); }
159 SettingRef(const QDomElement
& node
, const QString
& key
);
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;
169 // virtual QDomElement element() const { return m_element; }
171 // GenericSettingRef(const QDomElement& element, const QString& key);
174 template <typename T
>
175 class TypedSettingRef
{
178 TypedSettingRef(const T
& 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.
208 friend class SettingArray
;
211 virtual QDomElement
node() const { return m_node
; }
213 /** Create an invalid Settings object. */
215 virtual ~Settings() { }
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
);
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;
246 SettingRef
operator[](const char* key
) { return operator[](QString(key
)); }
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.
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.
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;
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;
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
318 static void ensureExistence(QDomElement
& node
, QDomElement parent
, const QString
& name
);
321 #ifdef __SETTINGS_DEBUG
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
{
338 QDomElement m_parent
;
340 virtual QDomElement
node() const;
342 SettingGroup(const QDomElement
& parent
, const QString
& name
);
345 * Check if the SettingGroup exists.
347 operator bool() const { return !m_node
.isNull(); }
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
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
364 <action>action1</action>
367 <name>double-click</name>
368 <action>action2</action>
372 * Other than the key, each element of the map may contain any arbitrarily
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
>
384 std::map
<Key
, Settings
> m_map
;
386 mutable QDomElement m_node
;
387 virtual QDomElement
node() const { return m_node
; }
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.
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.
433 std::vector
<Settings
> m_array
;
435 mutable QDomElement m_node
;
436 virtual QDomElement
node() const { return m_node
; }
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.
473 * Clear the array, removing all its elements.
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(); }
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 {
504 template <typename T
>
505 typename ReturnType
<T
>::wrapper
SettingRefBase::operator|(const T
& val
) const {
509 template <typename T
>
510 void SettingRefBase::operator>>(T
& out
) const {
514 template <typename T
>
515 typename ReturnType
<T
>::wrapper
SettingRef::operator|=(const T
& val
) {
516 if (element().isNull())
517 return operator=(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
);
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
);
543 template <typename Key
>
544 SettingMap
<Key
>::SettingMap(const QDomElement
& node
, const QString
& element
, const QString
& 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
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())
575 QDomElement el
= node().ownerDocument().createElement(m_element
);
576 node().appendChild(el
);
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
);