1/*
2 * Copyright 2007 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Library General Public License as
6 * published by the Free Software Foundation; either version 2, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20#include "configloader.h"
21#include "private/configloader_p.h"
22
23#include <QColor>
24#include <QFont>
25#include <QHash>
26#include <QXmlContentHandler>
27#include <QXmlInputSource>
28#include <QXmlSimpleReader>
29
30#include <kdebug.h>
31#include <kurl.h>
32
33namespace Plasma
34{
35class ConfigLoaderHandler : public QXmlDefaultHandler
36{
37public:
38 ConfigLoaderHandler(ConfigLoader *config, ConfigLoaderPrivate *d);
39 bool startElement(const QString &namespaceURI, const QString &localName,
40 const QString &qName, const QXmlAttributes &atts);
41 bool endElement(const QString &namespaceURI, const QString &localName,
42 const QString &qName);
43 bool characters(const QString &ch);
44
45private:
46 void addItem();
47 void resetState();
48
49 ConfigLoader *m_config;
50 ConfigLoaderPrivate *d;
51 int m_min;
52 int m_max;
53 QString m_name;
54 QString m_key;
55 QString m_type;
56 QString m_label;
57 QString m_default;
58 QString m_cdata;
59 QString m_whatsThis;
60 KConfigSkeleton::ItemEnum::Choice m_choice;
61 QList<KConfigSkeleton::ItemEnum::Choice> m_enumChoices;
62 bool m_haveMin;
63 bool m_haveMax;
64 bool m_inChoice;
65};
66
67void ConfigLoaderPrivate::parse(ConfigLoader *loader, QIODevice *xml)
68{
69 QXmlInputSource source(xml);
70 QXmlSimpleReader reader;
71 ConfigLoaderHandler handler(loader, this);
72 reader.setContentHandler(&handler);
73 reader.parse(&source, false);
74}
75
76ConfigLoaderHandler::ConfigLoaderHandler(ConfigLoader *config, ConfigLoaderPrivate *d)
77 : QXmlDefaultHandler(),
78 m_config(config),
79 d(d)
80{
81 resetState();
82}
83
84bool ConfigLoaderHandler::startElement(const QString &namespaceURI, const QString &localName,
85 const QString &qName, const QXmlAttributes &attrs)
86{
87 Q_UNUSED(namespaceURI)
88 Q_UNUSED(qName)
89
90// kDebug() << "ConfigLoaderHandler::startElement(" << localName << qName;
91 int numAttrs = attrs.count();
92 QString tag = localName.toLower();
93 if (tag == "group") {
94 QString group;
95 for (int i = 0; i < numAttrs; ++i) {
96 QString name = attrs.localName(i).toLower();
97 if (name == "name") {
98 //kDebug() << "set group to" << attrs.value(i);
99 group = attrs.value(i);
100 }
101 }
102 if (group.isEmpty()) {
103 group = d->baseGroup;
104 } else {
105 d->groups.append(group);
106 if (!d->baseGroup.isEmpty()) {
107 group = d->baseGroup + '\x1d' + group;
108 }
109 }
110 m_config->setCurrentGroup(group);
111 } else if (tag == "entry") {
112 for (int i = 0; i < numAttrs; ++i) {
113 QString name = attrs.localName(i).toLower();
114 if (name == "name") {
115 m_name = attrs.value(i).trimmed();
116 } else if (name == "type") {
117 m_type = attrs.value(i).toLower();
118 } else if (name == "key") {
119 m_key = attrs.value(i).trimmed();
120 }
121 }
122 } else if (tag == "choice") {
123 m_choice.name.clear();
124 m_choice.label.clear();
125 m_choice.whatsThis.clear();
126 for (int i = 0; i < numAttrs; ++i) {
127 QString name = attrs.localName(i).toLower();
128 if (name == "name") {
129 m_choice.name = attrs.value(i);
130 }
131 }
132 m_inChoice = true;
133 }
134
135 return true;
136}
137
138bool ConfigLoaderHandler::characters(const QString &ch)
139{
140 m_cdata.append(ch);
141 return true;
142}
143
144bool ConfigLoaderHandler::endElement(const QString &namespaceURI,
145 const QString &localName, const QString &qName)
146{
147 Q_UNUSED(namespaceURI)
148 Q_UNUSED(qName)
149
150// kDebug() << "ConfigLoaderHandler::endElement(" << localName << qName;
151 const QString tag = localName.toLower();
152 if (tag == "entry") {
153 addItem();
154 resetState();
155 } else if (tag == "label") {
156 if (m_inChoice) {
157 m_choice.label = m_cdata.trimmed();
158 } else {
159 m_label = m_cdata.trimmed();
160 }
161 } else if (tag == "whatsthis") {
162 if (m_inChoice) {
163 m_choice.whatsThis = m_cdata.trimmed();
164 } else {
165 m_whatsThis = m_cdata.trimmed();
166 }
167 } else if (tag == "default") {
168 m_default = m_cdata.trimmed();
169 } else if (tag == "min") {
170 m_min = m_cdata.toInt(&m_haveMin);
171 } else if (tag == "max") {
172 m_max = m_cdata.toInt(&m_haveMax);
173 } else if (tag == "choice") {
174 m_enumChoices.append(m_choice);
175 m_inChoice = false;
176 }
177
178 m_cdata.clear();
179 return true;
180}
181
182void ConfigLoaderHandler::addItem()
183{
184 if (m_name.isEmpty()) {
185 if (m_key.isEmpty()) {
186 return;
187 }
188
189 m_name = m_key;
190 }
191
192 m_name.remove(' ');
193
194 KConfigSkeletonItem *item = 0;
195
196 if (m_type == "bool") {
197 bool defaultValue = m_default.toLower() == "true";
198 item = m_config->addItemBool(m_name, *d->newBool(), defaultValue, m_key);
199 } else if (m_type == "color") {
200 QColor color;
201
202 if (m_default.count(',') == 3) {
203 const QStringList values = m_default.split(',');
204
205 color = QColor(values.at(0).toInt(), values.at(1).toInt(), values.at(2).toInt(), values.at(3).toInt());
206 } else {
207 color = QColor(m_default);
208 }
209
210 item = m_config->addItemColor(m_name, *d->newColor(), color, m_key);
211 } else if (m_type == "datetime") {
212 item = m_config->addItemDateTime(m_name, *d->newDateTime(),
213 QDateTime::fromString(m_default), m_key);
214 } else if (m_type == "enum") {
215 m_key = (m_key.isEmpty()) ? m_name : m_key;
216 KConfigSkeleton::ItemEnum *enumItem =
217 new KConfigSkeleton::ItemEnum(m_config->currentGroup(),
218 m_key, *d->newInt(),
219 m_enumChoices,
220 m_default.toUInt());
221 m_config->addItem(enumItem, m_name);
222 item = enumItem;
223 } else if (m_type == "font") {
224 item = m_config->addItemFont(m_name, *d->newFont(), QFont(m_default), m_key);
225 } else if (m_type == "int") {
226 KConfigSkeleton::ItemInt *intItem = m_config->addItemInt(m_name, *d->newInt(),
227 m_default.toInt(), m_key);
228
229 if (m_haveMin) {
230 intItem->setMinValue(m_min);
231 }
232
233 if (m_haveMax) {
234 intItem->setMaxValue(m_max);
235 }
236
237 item = intItem;
238 } else if (m_type == "password") {
239 item = m_config->addItemPassword(m_name, *d->newString(), m_default, m_key);
240 } else if (m_type == "path") {
241 item = m_config->addItemPath(m_name, *d->newString(), m_default, m_key);
242 } else if (m_type == "string") {
243 item = m_config->addItemString(m_name, *d->newString(), m_default, m_key);
244 } else if (m_type == "stringlist") {
245 //FIXME: the split() is naive and will break on lists with ,'s in them
246 item = m_config->addItemStringList(m_name, *d->newStringList(),
247 m_default.split(','), m_key);
248 } else if (m_type == "uint") {
249 KConfigSkeleton::ItemUInt *uintItem =
250 m_config->addItemUInt(m_name, *d->newUint(), m_default.toUInt(), m_key);
251 if (m_haveMin) {
252 uintItem->setMinValue(m_min);
253 }
254 if (m_haveMax) {
255 uintItem->setMaxValue(m_max);
256 }
257 item = uintItem;
258 } else if (m_type == "url") {
259 m_key = (m_key.isEmpty()) ? m_name : m_key;
260 KConfigSkeleton::ItemUrl *urlItem =
261 new KConfigSkeleton::ItemUrl(m_config->currentGroup(),
262 m_key, *d->newUrl(),
263 m_default);
264 m_config->addItem(urlItem, m_name);
265 item = urlItem;
266 } else if (m_type == "double") {
267 KConfigSkeleton::ItemDouble *doubleItem = m_config->addItemDouble(m_name,
268 *d->newDouble(), m_default.toDouble(), m_key);
269 if (m_haveMin) {
270 doubleItem->setMinValue(m_min);
271 }
272 if (m_haveMax) {
273 doubleItem->setMaxValue(m_max);
274 }
275 item = doubleItem;
276 } else if (m_type == "intlist") {
277 QStringList tmpList = m_default.split(',');
278 QList<int> defaultList;
279 foreach (const QString &tmp, tmpList) {
280 defaultList.append(tmp.toInt());
281 }
282 item = m_config->addItemIntList(m_name, *d->newIntList(), defaultList, m_key);
283 } else if (m_type == "longlong") {
284 KConfigSkeleton::ItemLongLong *longlongItem = m_config->addItemLongLong(m_name,
285 *d->newLongLong(), m_default.toLongLong(), m_key);
286 if (m_haveMin) {
287 longlongItem->setMinValue(m_min);
288 }
289 if (m_haveMax) {
290 longlongItem->setMaxValue(m_max);
291 }
292 item = longlongItem;
293 /* No addItemPathList in KConfigSkeleton ?
294 } else if (m_type == "PathList") {
295 //FIXME: the split() is naive and will break on lists with ,'s in them
296 item = m_config->addItemPathList(m_name, *d->newStringList(), m_default.split(","), m_key);
297 */
298 } else if (m_type == "point") {
299 QPoint defaultPoint;
300 QStringList tmpList = m_default.split(',');
301 if (tmpList.size() >= 2) {
302 defaultPoint.setX(tmpList[0].toInt());
303 defaultPoint.setY(tmpList[1].toInt());
304 }
305 item = m_config->addItemPoint(m_name, *d->newPoint(), defaultPoint, m_key);
306 } else if (m_type == "rect") {
307 QRect defaultRect;
308 QStringList tmpList = m_default.split(',');
309 if (tmpList.size() >= 4) {
310 defaultRect.setCoords(tmpList[0].toInt(), tmpList[1].toInt(),
311 tmpList[2].toInt(), tmpList[3].toInt());
312 }
313 item = m_config->addItemRect(m_name, *d->newRect(), defaultRect, m_key);
314 } else if (m_type == "size") {
315 QSize defaultSize;
316 QStringList tmpList = m_default.split(',');
317 if (tmpList.size() >= 2) {
318 defaultSize.setWidth(tmpList[0].toInt());
319 defaultSize.setHeight(tmpList[1].toInt());
320 }
321 item = m_config->addItemSize(m_name, *d->newSize(), defaultSize, m_key);
322 } else if (m_type == "ulonglong") {
323 KConfigSkeleton::ItemULongLong *ulonglongItem =
324 m_config->addItemULongLong(m_name, *d->newULongLong(), m_default.toULongLong(), m_key);
325 if (m_haveMin) {
326 ulonglongItem->setMinValue(m_min);
327 }
328 if (m_haveMax) {
329 ulonglongItem->setMaxValue(m_max);
330 }
331 item = ulonglongItem;
332 /* No addItemUrlList in KConfigSkeleton ?
333 } else if (m_type == "urllist") {
334 //FIXME: the split() is naive and will break on lists with ,'s in them
335 QStringList tmpList = m_default.split(",");
336 KUrl::List defaultList;
337 foreach (const QString& tmp, tmpList) {
338 defaultList.append(KUrl(tmp));
339 }
340 item = m_config->addItemUrlList(m_name, *d->newUrlList(), defaultList, m_key);*/
341 }
342
343 if (item) {
344 item->setLabel(m_label);
345 item->setWhatsThis(m_whatsThis);
346 d->keysToNames.insert(item->group() + item->key(), item->name());
347 }
348}
349
350void ConfigLoaderHandler::resetState()
351{
352 m_haveMin = false;
353 m_min = 0;
354 m_haveMax = false;
355 m_max = 0;
356 m_name.clear();
357 m_type.clear();
358 m_label.clear();
359 m_default.clear();
360 m_key.clear();
361 m_whatsThis.clear();
362 m_enumChoices.clear();
363 m_inChoice = false;
364}
365
366ConfigLoader::ConfigLoader(const QString &configFile, QIODevice *xml, QObject *parent)
367 : KConfigSkeleton(configFile, parent),
368 d(new ConfigLoaderPrivate)
369{
370 d->parse(this, xml);
371}
372
373ConfigLoader::ConfigLoader(KSharedConfigPtr config, QIODevice *xml, QObject *parent)
374 : KConfigSkeleton(config, parent),
375 d(new ConfigLoaderPrivate)
376{
377 d->parse(this, xml);
378}
379
380//FIXME: obviously this is broken and should be using the group as the root,
381// but KConfigSkeleton does not currently support this. it will eventually though,
382// at which point this can be addressed properly
383ConfigLoader::ConfigLoader(const KConfigGroup *config, QIODevice *xml, QObject *parent)
384 : KConfigSkeleton(KSharedConfig::openConfig(config->config()->name(), KConfig::SimpleConfig), parent),
385 d(new ConfigLoaderPrivate)
386{
387 KConfigGroup group = config->parent();
388 d->baseGroup = config->name();
389 while (group.isValid() && group.name() != "<default>") {
390 d->baseGroup = group.name() + '\x1d' + d->baseGroup;
391 group = group.parent();
392 }
393 d->parse(this, xml);
394}
395
396ConfigLoader::~ConfigLoader()
397{
398 delete d;
399}
400
401KConfigSkeletonItem *ConfigLoader::findItem(const QString &group, const QString &key)
402{
403 return KConfigSkeleton::findItem(d->keysToNames[group + key]);
404}
405
406KConfigSkeletonItem *ConfigLoader::findItemByName(const QString &name)
407{
408 return KConfigSkeleton::findItem(name);
409}
410
411QVariant ConfigLoader::property(const QString &name)
412{
413 KConfigSkeletonItem *item = KConfigSkeleton::findItem(name);
414
415 if (item) {
416 return item->property();
417 }
418
419 return QVariant();
420}
421
422bool ConfigLoader::hasGroup(const QString &group) const
423{
424 return d->groups.contains(group);
425}
426
427QStringList ConfigLoader::groupList() const
428{
429 return d->groups;
430}
431
432void ConfigLoader::usrWriteConfig()
433{
434 if (d->saveDefaults) {
435 KConfigSkeletonItem::List itemList = items();
436 for(int i = 0; i < itemList.size(); i++) {
437 KConfigGroup cg(config(), itemList.at(i)->group());
438 cg.writeEntry(itemList.at(i)->key(), "");
439 }
440 }
441}
442
443} // Plasma namespace
444