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 | |
33 | namespace Plasma |
34 | { |
35 | class ConfigLoaderHandler : public QXmlDefaultHandler |
36 | { |
37 | public: |
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 | |
45 | private: |
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 | |
67 | void 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 | |
76 | ConfigLoaderHandler::ConfigLoaderHandler(ConfigLoader *config, ConfigLoaderPrivate *d) |
77 | : QXmlDefaultHandler(), |
78 | m_config(config), |
79 | d(d) |
80 | { |
81 | resetState(); |
82 | } |
83 | |
84 | bool 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 | |
138 | bool ConfigLoaderHandler::characters(const QString &ch) |
139 | { |
140 | m_cdata.append(ch); |
141 | return true; |
142 | } |
143 | |
144 | bool 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 | |
182 | void 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 | |
350 | void 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 | |
366 | ConfigLoader::ConfigLoader(const QString &configFile, QIODevice *xml, QObject *parent) |
367 | : KConfigSkeleton(configFile, parent), |
368 | d(new ConfigLoaderPrivate) |
369 | { |
370 | d->parse(this, xml); |
371 | } |
372 | |
373 | ConfigLoader::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 |
383 | ConfigLoader::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 | |
396 | ConfigLoader::~ConfigLoader() |
397 | { |
398 | delete d; |
399 | } |
400 | |
401 | KConfigSkeletonItem *ConfigLoader::findItem(const QString &group, const QString &key) |
402 | { |
403 | return KConfigSkeleton::findItem(d->keysToNames[group + key]); |
404 | } |
405 | |
406 | KConfigSkeletonItem *ConfigLoader::findItemByName(const QString &name) |
407 | { |
408 | return KConfigSkeleton::findItem(name); |
409 | } |
410 | |
411 | QVariant 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 | |
422 | bool ConfigLoader::hasGroup(const QString &group) const |
423 | { |
424 | return d->groups.contains(group); |
425 | } |
426 | |
427 | QStringList ConfigLoader::groupList() const |
428 | { |
429 | return d->groups; |
430 | } |
431 | |
432 | void 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 | |