KConfig

kconfigloader.cpp
1/*
2 SPDX-FileCopyrightText: 2007 Aaron Seigo <aseigo@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "kconfigloader.h"
8#include "kconfigloader_p.h"
9#include "kconfigloaderhandler_p.h"
10
11#include <QColor>
12#include <QFont>
13#include <QHash>
14#include <QUrl>
15
16#include <QDebug>
17
18void ConfigLoaderPrivate::parse(KConfigLoader *loader, QIODevice *xml)
19{
20 clearData();
21 loader->clearItems();
22
23 if (xml) {
24 ConfigLoaderHandler handler(loader, this);
25 handler.parse(xml);
26 }
27}
28
29ConfigLoaderHandler::ConfigLoaderHandler(KConfigLoader *config, ConfigLoaderPrivate *d)
30 : m_config(config)
31 , d(d)
32{
33 resetState();
34}
35
36bool ConfigLoaderHandler::parse(QIODevice *input)
37{
38 if (!input->open(QIODevice::ReadOnly)) {
39 qWarning() << "Impossible to open device";
40 return false;
41 }
42 QXmlStreamReader reader(input);
43
44 while (!reader.atEnd()) {
45 reader.readNext();
46 if (reader.hasError()) {
47 return false;
48 }
49
50 switch (reader.tokenType()) {
52 startElement(reader.name(), reader.attributes());
53 break;
55 endElement(reader.name());
56 break;
58 if (!reader.isWhitespace() && !reader.text().trimmed().isEmpty()) {
59 m_cdata.append(reader.text());
60 }
61 break;
62 default:
63 break;
64 }
65 }
66
67 if (!reader.isEndDocument()) {
68 return false;
69 }
70
71 return true;
72}
73
74static bool caseInsensitiveCompare(const QStringView a, const QLatin1String b)
75{
76 return a.compare(b, Qt::CaseInsensitive) == 0;
77}
78
79void ConfigLoaderHandler::startElement(const QStringView localName, const QXmlStreamAttributes &attrs)
80{
81 // qDebug() << "ConfigLoaderHandler::startElement(" << localName << qName;
82 if (caseInsensitiveCompare(localName, QLatin1String("group"))) {
83 QString group;
84 for (const auto &attr : attrs) {
85 const auto attrName = attr.name();
86 if (caseInsensitiveCompare(attrName, QLatin1String("name"))) {
87 // qDebug() << "set group to" << attrs.value(i);
88 group = attr.value().toString();
89 }
90 }
91 if (group.isEmpty()) {
92 group = d->baseGroup;
93 } else {
94 d->groups.append(group);
95 if (!d->baseGroup.isEmpty()) {
96 group = d->baseGroup + QLatin1Char('\x1d') + group;
97 }
98 }
99
100 if (m_config) {
101 m_config->setCurrentGroup(group);
102 }
103 } else if (caseInsensitiveCompare(localName, QLatin1String("entry"))) {
104 for (const auto &attr : attrs) {
105 const auto attrName = attr.name();
106 if (caseInsensitiveCompare(attrName, QLatin1String("name"))) {
107 m_name = attr.value().trimmed().toString();
108 } else if (caseInsensitiveCompare(attrName, QLatin1String("type"))) {
109 m_type = attr.value().toString().toLower();
110 } else if (caseInsensitiveCompare(attrName, QLatin1String("key"))) {
111 m_key = attr.value().trimmed().toString();
112 }
113 }
114 } else if (caseInsensitiveCompare(localName, QLatin1String("choice"))) {
115 m_choice.name.clear();
116 m_choice.label.clear();
117 m_choice.whatsThis.clear();
118 for (const auto &attr : attrs) {
119 const auto attrName = attr.name();
120 if (caseInsensitiveCompare(attrName, QLatin1String("name"))) {
121 m_choice.name = attr.value().toString();
122 }
123 }
124 m_inChoice = true;
125 }
126}
127
128void ConfigLoaderHandler::endElement(const QStringView localName)
129{
130 // qDebug() << "ConfigLoaderHandler::endElement(" << localName << qName;
131 if (caseInsensitiveCompare(localName, QLatin1String("entry"))) {
132 addItem();
133 resetState();
134 } else if (caseInsensitiveCompare(localName, QLatin1String("label"))) {
135 if (m_inChoice) {
136 m_choice.label = std::move(m_cdata).trimmed();
137 } else {
138 m_label = std::move(m_cdata).trimmed();
139 }
140 } else if (caseInsensitiveCompare(localName, QLatin1String("whatsthis"))) {
141 if (m_inChoice) {
142 m_choice.whatsThis = std::move(m_cdata).trimmed();
143 } else {
144 m_whatsThis = std::move(m_cdata).trimmed();
145 }
146 } else if (caseInsensitiveCompare(localName, QLatin1String("default"))) {
147 m_default = std::move(m_cdata).trimmed();
148 } else if (caseInsensitiveCompare(localName, QLatin1String("min"))) {
149 m_min = m_cdata.toInt(&m_haveMin);
150 } else if (caseInsensitiveCompare(localName, QLatin1String("max"))) {
151 m_max = m_cdata.toInt(&m_haveMax);
152 } else if (caseInsensitiveCompare(localName, QLatin1String("choice"))) {
153 m_enumChoices.append(m_choice);
154 m_inChoice = false;
155 }
156
157 m_cdata.clear();
158}
159
160void ConfigLoaderHandler::addItem()
161{
162 if (m_name.isEmpty()) {
163 if (m_key.isEmpty()) {
164 return;
165 }
166
167 m_name = m_key;
168 }
169
170 m_name.remove(QLatin1Char(' '));
171
172 KConfigSkeletonItem *item = nullptr;
173
174 if (m_type == QLatin1String("bool")) {
175 const bool defaultValue = caseInsensitiveCompare(m_default, QLatin1String("true"));
176 item = m_config->addItemBool(m_name, *d->newBool(), defaultValue, m_key);
177 } else if (m_type == QLatin1String("color")) {
178 item = m_config->addItemColor(m_name, *d->newColor(), QColor(m_default), m_key);
179 } else if (m_type == QLatin1String("datetime")) {
180 item = m_config->addItemDateTime(m_name, *d->newDateTime(), QDateTime::fromString(m_default), m_key);
181 } else if (m_type == QLatin1String("enum")) {
182 m_key = (m_key.isEmpty()) ? m_name : m_key;
183
184 bool ok = false;
185 int defaultValue = m_default.toInt(&ok);
186 if (!ok) {
187 for (int i = 0; i < m_enumChoices.size(); i++) {
188 if (m_default == m_enumChoices[i].name) {
189 defaultValue = i;
190 break;
191 }
192 }
193 }
194
195 KConfigSkeleton::ItemEnum *enumItem = new KConfigSkeleton::ItemEnum(m_config->currentGroup(), m_key, *d->newInt(), m_enumChoices, defaultValue);
196 m_config->addItem(enumItem, m_name);
197 item = enumItem;
198 } else if (m_type == QLatin1String("font")) {
199 item = m_config->addItemFont(m_name, *d->newFont(), QFont(m_default), m_key);
200 } else if (m_type == QLatin1String("int")) {
201 KConfigSkeleton::ItemInt *intItem = m_config->addItemInt(m_name, *d->newInt(), m_default.toInt(), m_key);
202
203 if (m_haveMin) {
204 intItem->setMinValue(m_min);
205 }
206
207 if (m_haveMax) {
208 intItem->setMaxValue(m_max);
209 }
210
211 item = intItem;
212 } else if (m_type == QLatin1String("password")) {
213 item = m_config->addItemPassword(m_name, *d->newString(), m_default, m_key);
214 } else if (m_type == QLatin1String("path")) {
215 item = m_config->addItemPath(m_name, *d->newString(), m_default, m_key);
216 } else if (m_type == QLatin1String("string")) {
217 item = m_config->addItemString(m_name, *d->newString(), m_default, m_key);
218 } else if (m_type == QLatin1String("stringlist")) {
219 // FIXME: the split() is naive and will break on lists with ,'s in them
220 // empty parts are not wanted in this case
221 item = m_config->addItemStringList(m_name, *d->newStringList(), m_default.split(QLatin1Char(','), Qt::SkipEmptyParts), m_key);
222 } else if (m_type == QLatin1String("uint")) {
223 KConfigSkeleton::ItemUInt *uintItem = m_config->addItemUInt(m_name, *d->newUint(), m_default.toUInt(), m_key);
224 if (m_haveMin) {
225 uintItem->setMinValue(m_min);
226 }
227 if (m_haveMax) {
228 uintItem->setMaxValue(m_max);
229 }
230 item = uintItem;
231 } else if (m_type == QLatin1String("url")) {
232 m_key = (m_key.isEmpty()) ? m_name : m_key;
233 KConfigSkeleton::ItemUrl *urlItem = new KConfigSkeleton::ItemUrl(m_config->currentGroup(), m_key, *d->newUrl(), QUrl::fromUserInput(m_default));
234 m_config->addItem(urlItem, m_name);
235 item = urlItem;
236 } else if (m_type == QLatin1String("double")) {
237 KConfigSkeleton::ItemDouble *doubleItem = m_config->addItemDouble(m_name, *d->newDouble(), m_default.toDouble(), m_key);
238 if (m_haveMin) {
239 doubleItem->setMinValue(m_min);
240 }
241 if (m_haveMax) {
242 doubleItem->setMaxValue(m_max);
243 }
244 item = doubleItem;
245 } else if (m_type == QLatin1String("intlist")) {
246 QList<int> defaultList;
247 const QList<QStringView> tmpList = QStringView(m_default).split(QLatin1Char(','), Qt::SkipEmptyParts);
248 for (const QStringView tmp : tmpList) {
249 defaultList.append(tmp.toInt());
250 }
251 item = m_config->addItemIntList(m_name, *d->newIntList(), defaultList, m_key);
252 } else if (m_type == QLatin1String("longlong")) {
253 KConfigSkeleton::ItemLongLong *longlongItem = m_config->addItemLongLong(m_name, *d->newLongLong(), m_default.toLongLong(), m_key);
254 if (m_haveMin) {
255 longlongItem->setMinValue(m_min);
256 }
257 if (m_haveMax) {
258 longlongItem->setMaxValue(m_max);
259 }
260 item = longlongItem;
261 /* No addItemPathList in KConfigSkeleton ?
262 } else if (m_type == "PathList") {
263 //FIXME: the split() is naive and will break on lists with ,'s in them
264 item = m_config->addItemPathList(m_name, *d->newStringList(), m_default.split(","), m_key);
265 */
266 } else if (m_type == QLatin1String("point")) {
267 QPoint defaultPoint;
268 const QList<QStringView> tmpList = QStringView(m_default).split(QLatin1Char(','));
269 if (tmpList.size() >= 2) {
270 defaultPoint.setX(tmpList[0].toInt());
271 defaultPoint.setY(tmpList[1].toInt());
272 }
273 item = m_config->addItemPoint(m_name, *d->newPoint(), defaultPoint, m_key);
274 } else if (m_type == QLatin1String("pointf")) {
275 QPointF defaultPointF;
276 const auto tmpList = QStringView(m_default).split(u',');
277 if (tmpList.size() >= 2) {
278 defaultPointF.setX(tmpList[0].toDouble());
279 defaultPointF.setY(tmpList[1].toDouble());
280 }
281 item = m_config->addItemPointF(m_name, *d->newPointF(), defaultPointF, m_key);
282 } else if (m_type == QLatin1String("rect")) {
283 QRect defaultRect;
284 const QList<QStringView> tmpList = QStringView(m_default).split(QLatin1Char(','));
285 if (tmpList.size() >= 4) {
286 defaultRect.setCoords(tmpList[0].toInt(), tmpList[1].toInt(), tmpList[2].toInt(), tmpList[3].toInt());
287 }
288 item = m_config->addItemRect(m_name, *d->newRect(), defaultRect, m_key);
289 } else if (m_type == QLatin1String("rectf")) {
290 QRectF defaultRectF;
291 const auto tmpList = QStringView(m_default).split(u',');
292 if (tmpList.size() >= 4) {
293 defaultRectF.setCoords(tmpList[0].toDouble(), tmpList[1].toDouble(), tmpList[2].toDouble(), tmpList[3].toDouble());
294 }
295 item = m_config->addItemRectF(m_name, *d->newRectF(), defaultRectF, m_key);
296 } else if (m_type == QLatin1String("size")) {
298 const QList<QStringView> tmpList = QStringView(m_default).split(QLatin1Char(','));
299 if (tmpList.size() >= 2) {
300 defaultSize.setWidth(tmpList[0].toInt());
301 defaultSize.setHeight(tmpList[1].toInt());
302 }
303 item = m_config->addItemSize(m_name, *d->newSize(), defaultSize, m_key);
304 } else if (m_type == QLatin1String("sizef")) {
305 QSizeF defaultSizeF;
306 const auto tmpList = QStringView(m_default).split(u',');
307 if (tmpList.size() >= 2) {
308 defaultSizeF.setWidth(tmpList[0].toDouble());
309 defaultSizeF.setHeight(tmpList[1].toDouble());
310 }
311 item = m_config->addItemSizeF(m_name, *d->newSizeF(), defaultSizeF, m_key);
312 } else if (m_type == QLatin1String("ulonglong")) {
313 KConfigSkeleton::ItemULongLong *ulonglongItem = m_config->addItemULongLong(m_name, *d->newULongLong(), m_default.toULongLong(), m_key);
314 if (m_haveMin) {
315 ulonglongItem->setMinValue(m_min);
316 }
317 if (m_haveMax) {
318 ulonglongItem->setMaxValue(m_max);
319 }
320 item = ulonglongItem;
321 /* No addItemUrlList in KConfigSkeleton ?
322 } else if (m_type == "urllist") {
323 //FIXME: the split() is naive and will break on lists with ,'s in them
324 QStringList tmpList = m_default.split(",");
325 QList<QUrl> defaultList;
326 foreach (const QString& tmp, tmpList) {
327 defaultList.append(QUrl(tmp));
328 }
329 item = m_config->addItemUrlList(m_name, *d->newUrlList(), defaultList, m_key);*/
330 }
331
332 if (item) {
333 item->setLabel(m_label);
334 item->setWhatsThis(m_whatsThis);
335 d->keysToNames.insert(item->group() + item->key(), item->name());
336 }
337}
338
339void ConfigLoaderHandler::resetState()
340{
341 m_haveMin = false;
342 m_min = 0;
343 m_haveMax = false;
344 m_max = 0;
345 m_name.clear();
346 m_type.clear();
347 m_label.clear();
348 m_default.clear();
349 m_key.clear();
350 m_whatsThis.clear();
351 m_enumChoices.clear();
352 m_inChoice = false;
353}
354
355KConfigLoader::KConfigLoader(const QString &configFile, QIODevice *xml, QObject *parent)
356 : KConfigSkeleton(configFile, parent)
357 , d(new ConfigLoaderPrivate)
358{
359 d->parse(this, xml);
360}
361
363 : KConfigSkeleton(std::move(config), parent)
364 , d(new ConfigLoaderPrivate)
365{
366 d->parse(this, xml);
367}
368
369// FIXME: obviously this is broken and should be using the group as the root,
370// but KConfigSkeleton does not currently support this. it will eventually though,
371// at which point this can be addressed properly
373 : KConfigSkeleton(KSharedConfig::openConfig(config.config()->name(), config.config()->openFlags(), config.config()->locationType()), parent)
374 , d(new ConfigLoaderPrivate)
375{
376 KConfigGroup group = config.parent();
377 d->baseGroup = config.name();
378 while (group.isValid() && group.name() != QLatin1String("<default>")) {
379 d->baseGroup = group.name() + QLatin1Char('\x1d') + d->baseGroup;
380 group = group.parent();
381 }
382 d->parse(this, xml);
383}
384
385KConfigLoader::~KConfigLoader()
386{
387 delete d;
388}
389
391{
392 return KConfigSkeleton::findItem(d->keysToNames[group + key]);
393}
394
399
401{
403
404 if (item) {
405 return item->property();
406 }
407
408 return QVariant();
409}
410
411bool KConfigLoader::hasGroup(const QString &group) const
412{
413 return d->groups.contains(group);
414}
415
417{
418 return d->groups;
419}
420
422{
423 if (d->saveDefaults) {
424 const auto listItems = items();
425 for (const auto &item : listItems) {
426 config()->group(item->group()).writeEntry(item->key(), "");
427 }
428 }
429 return true;
430}
KConfigGroup group(const QString &group)
Returns an object for the named subgroup.
A class for one specific group in a KConfig object.
QString name() const
The name of this group.
void writeEntry(const QString &key, const QVariant &value, WriteConfigFlags pFlags=Normal)
Writes a value to the configuration object.
bool isValid() const
Whether the group is valid.
KConfigGroup parent() const
Returns the group that this group belongs to.
A KConfigSkeleton that populates itself based on KConfigXT XML.
KConfigSkeletonItem * findItemByName(const QString &name) const
Finds an item by its name.
KConfigSkeletonItem * findItem(const QString &group, const QString &key) const
Finds the item for the given group and key.
bool usrSave() override
Perform the actual writing of the configuration file.
QStringList groupList() const
bool hasGroup(const QString &group) const
Check to see if a group exists.
QVariant property(const QString &name) const
Returns the property (variantized value) of the named item.
KConfigLoader(const QString &configFile, QIODevice *xml, QObject *parent=nullptr)
Creates a KConfigSkeleton populated using the definition found in the XML data passed in.
Class for storing a preferences setting.
QString name() const
Return internal name of entry.
QString key() const
Return config file key.
void setLabel(const QString &l)
Set label providing a translated one-line description of the item.
QString group() const
Return name of config file group.
void setWhatsThis(const QString &w)
Set WhatsThis description of item.
virtual QVariant property() const =0
Return item as property.
Class for handling preferences settings for an application.
QString name() const
Returns the filename used to store the configuration.
Definition kconfig.cpp:564
Class for handling a floating point preference item.
void setMinValue(double)
Set the minimum value for the item.
void setMaxValue(double)
Set the maximum value for the item.
Class for handling enums.
Class for handling a 32-bit integer preferences item.
void setMaxValue(qint32)
Set the maximum value for the item.
void setMinValue(qint32)
Set the minimum value for the item.
Class for handling a 64-bit integer preferences item.
void setMaxValue(qint64)
Set the maximum value for the item.
void setMinValue(qint64)
Set the minimum value for the item.
Class for handling an unsigned 32-bit integer preferences item.
void setMinValue(quint32)
Set the minimum value for the item.
void setMaxValue(quint32)
Set the maximum value for the item.
Class for handling unsigned 64-bit integer preferences item.
void setMaxValue(quint64)
Set the maximum value for the item.
void setMinValue(quint64)
Set the minimum value for the item.
Class for handling a url preferences item.
KConfigSkeletonItem * findItem(const QString &name) const
Lookup item by name.
KConfig * config()
Return the KConfig object used for reading and writing the settings.
KConfigSkeletonItem::List items() const
Return list of items managed by this KCoreConfigSkeleton object.
void clearItems()
Removes and deletes all items.
KConfig variant using shared memory.
KREPORT_EXPORT QPageSize::PageSizeId defaultSize()
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
virtual bool open(QIODeviceBase::OpenMode mode)
void append(QList< T > &&value)
qsizetype size() const const
void setX(int x)
void setY(int y)
void setX(qreal x)
void setY(qreal y)
void setCoords(int x1, int y1, int x2, int y2)
void setCoords(qreal x1, qreal y1, qreal x2, qreal y2)
void setHeight(qreal height)
void setWidth(qreal width)
QString & append(QChar ch)
void clear()
bool isEmpty() const const
int toInt(bool *ok, int base) const const
QString toLower() const const
QString trimmed() const const
int compare(QChar ch) const const
QList< QStringView > split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
CaseInsensitive
SkipEmptyParts
QUrl fromUserInput(const QString &userInput, const QString &workingDirectory, UserInputResolutionOptions options)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 3 2024 11:51:17 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.