KConfig

kconfiggroup.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
4 SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
5 SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <kalle@kde.org>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#include "kconfiggroup.h"
11#include "kconfiggroup_p.h"
12
13#include "kconfig.h"
14#include "kconfig_core_log_settings.h"
15#include "kconfig_p.h"
16#include "kconfigdata_p.h"
17#include "ksharedconfig.h"
18
19#include <QDate>
20#include <QDir>
21#include <QFile>
22#include <QPoint>
23#include <QRect>
24#include <QSharedData>
25#include <QString>
26#include <QTextStream>
27#include <QUrl>
28#include <QUuid>
29
30#include <algorithm>
31#include <array>
32#include <math.h>
33#include <stdlib.h>
34
35class KConfigGroupPrivate : public QSharedData
36{
37public:
38 KConfigGroupPrivate(KConfig *owner, bool isImmutable, bool isConst, const QString &name)
39 : mOwner(owner)
40 , mName(name)
41 , bImmutable(isImmutable)
42 , bConst(isConst)
43 {
44 if (Q_UNLIKELY(!mOwner->name().isEmpty() && mOwner->accessMode() == KConfigBase::NoAccess)) {
45 qCWarning(KCONFIG_CORE_LOG) << "Created a KConfigGroup on an inaccessible config location" << mOwner->name() << name;
46 }
47 }
48
49 KConfigGroupPrivate(const KSharedConfigPtr &owner, const QString &name)
50 : sOwner(owner)
51 , mOwner(sOwner.data())
52 , mName(name)
53 , bImmutable(name.isEmpty() ? owner->isImmutable() : owner->isGroupImmutable(name))
54 , bConst(false)
55 {
56 if (Q_UNLIKELY(!mOwner->name().isEmpty() && mOwner->accessMode() == KConfigBase::NoAccess)) {
57 qCWarning(KCONFIG_CORE_LOG) << "Created a KConfigGroup on an inaccessible config location" << mOwner->name() << name;
58 }
59 }
60
61 KConfigGroupPrivate(KConfigGroup *parent, bool isImmutable, bool isConst, const QString &name)
62 : sOwner(parent->d->sOwner)
63 , mOwner(parent->d->mOwner)
64 , mName(name)
65 , bImmutable(isImmutable)
66 , bConst(isConst)
67 {
68 if (!parent->d->mName.isEmpty()) {
69 mParent = parent->d;
70 }
71 }
72
73 KConfigGroupPrivate(const KConfigGroupPrivate *other, bool isImmutable, const QString &name)
74 : sOwner(other->sOwner)
75 , mOwner(other->mOwner)
76 , mName(name)
77 , bImmutable(isImmutable)
78 , bConst(other->bConst)
79 {
80 if (!other->mName.isEmpty()) {
81 mParent = const_cast<KConfigGroupPrivate *>(other);
82 }
83 }
84
85 KSharedConfig::Ptr sOwner;
86 KConfig *mOwner;
88 QString mName;
89
90 /* bitfield */
91 const bool bImmutable : 1; // is this group immutable?
92 const bool bConst : 1; // is this group read-only?
93
94 QString fullName() const
95 {
96 if (!mParent) {
97 return name();
98 }
99 return mParent->fullName(mName);
100 }
101
102 QString name() const
103 {
104 if (mName.isEmpty()) {
105 return QStringLiteral("<default>");
106 }
107 return mName;
108 }
109
110 QString fullName(const QString &aGroup) const
111 {
112 if (mName.isEmpty()) {
113 return aGroup;
114 }
115 return fullName() + QLatin1Char('\x1d') + aGroup;
116 }
117
118 static QExplicitlySharedDataPointer<KConfigGroupPrivate> create(KConfigBase *master, const QString &name, bool isImmutable, bool isConst)
119 {
121 if (dynamic_cast<KConfigGroup *>(master)) {
122 data = new KConfigGroupPrivate(static_cast<KConfigGroup *>(master), isImmutable, isConst, name);
123 } else {
124 data = new KConfigGroupPrivate(dynamic_cast<KConfig *>(master), isImmutable, isConst, name);
125 }
126 return data;
127 }
128
129 static QByteArray serializeList(const QList<QByteArray> &list);
130 static QStringList deserializeList(const QString &data);
131};
132
133QByteArray KConfigGroupPrivate::serializeList(const QList<QByteArray> &list)
134{
135 QByteArray value;
136
137 if (!list.isEmpty()) {
138 auto it = list.cbegin();
139 const auto end = list.cend();
140
141 value = QByteArray(*it).replace('\\', QByteArrayLiteral("\\\\")).replace(',', QByteArrayLiteral("\\,"));
142
143 while (++it != end) {
144 // In the loop, so it is not done when there is only one element.
145 // Doing it repeatedly is a pretty cheap operation.
146 value.reserve(4096);
147
148 value += ',';
149 value += QByteArray(*it).replace('\\', QByteArrayLiteral("\\\\")).replace(',', QByteArrayLiteral("\\,"));
150 }
151
152 // To be able to distinguish an empty list from a list with one empty element.
153 if (value.isEmpty()) {
154 value = QByteArrayLiteral("\\0");
155 }
156 }
157
158 return value;
159}
160
161QStringList KConfigGroupPrivate::deserializeList(const QString &data)
162{
163 if (data.isEmpty()) {
164 return QStringList();
165 }
166 if (data == QLatin1String("\\0")) {
167 return QStringList(QString());
168 }
169 QStringList value;
170 QString val;
171 val.reserve(data.size());
172 bool quoted = false;
173 for (int p = 0; p < data.length(); p++) {
174 if (quoted) {
175 val += data[p];
176 quoted = false;
177 } else if (data[p].unicode() == '\\') {
178 quoted = true;
179 } else if (data[p].unicode() == ',') {
180 val.squeeze(); // release any unused memory
181 value.append(val);
182 val.clear();
183 val.reserve(data.size() - p);
184 } else {
185 val += data[p];
186 }
187 }
188 value.append(val);
189 return value;
190}
191
192static QVarLengthArray<int, 8> asIntList(QByteArrayView string)
193{
194 int start = 0;
195 int next = start;
197 while ((next = string.indexOf(',', start)) != -1) {
198 ret.push_back(string.sliced(start, next - start).toInt());
199 start = next + 1;
200 }
201 ret.push_back(string.sliced(start, string.size() - start).toInt());
202 return ret;
203}
204
205static QVarLengthArray<qreal, 8> asRealList(QByteArrayView string)
206{
207 int start = 0;
208 int next = start;
210 while ((next = string.indexOf(',', start)) != -1) {
211 ret.push_back(string.sliced(start, next - start).toDouble());
212 start = next + 1;
213 }
214 ret.push_back(string.sliced(start, string.size() - start).toDouble());
215 return ret;
216}
217
218static QString errString(const char *pKey, const QByteArray &value, const QVariant &aDefault)
219{
220 return QStringLiteral("\"%1\" - conversion of \"%3\" to %2 failed")
222}
223
224static QString formatError(int expected, int got)
225{
226 return QStringLiteral(" (wrong format: expected %1 items, got %2)").arg(expected).arg(got);
227}
228
229QVariant KConfigGroup::convertToQVariant(const char *pKey, const QByteArray &value, const QVariant &aDefault)
230{
231 // if a type handler is added here you must add a QVConversions definition
232 // to kconfigconversioncheck_p.h, or KConfigConversionCheck::to_QVariant will not allow
233 // readEntry<T> to convert to QVariant.
234 switch (static_cast<QMetaType::Type>(aDefault.userType())) {
236 return QVariant();
238 // this should return the raw string not the dollar expanded string.
239 // imho if processed string is wanted should call
240 // readEntry(key, QString) not readEntry(key, QVariant)
241 return QString::fromUtf8(value);
242 case QMetaType::QUuid:
243 return QUuid::fromString(value);
246 return KConfigGroupPrivate::deserializeList(QString::fromUtf8(value));
248 return value;
249 case QMetaType::Bool: {
250 static const std::array<const char *, 4> negatives = {"false", "no", "off", "0"};
251
252 return std::all_of(negatives.begin(), negatives.end(), [value](const char *negativeString) {
253 return value.compare(negativeString, Qt::CaseInsensitive) != 0;
254 });
255 }
257 case QMetaType::Float:
258 case QMetaType::Int:
259 case QMetaType::UInt:
262 QVariant tmp = value;
263 if (!tmp.convert(aDefault.metaType())) {
264 tmp = aDefault;
265 }
266 return tmp;
267 }
268 case QMetaType::QPoint: {
269 const auto list = asIntList(value);
270
271 if (list.count() != 2) {
272 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(2, list.count());
273 return aDefault;
274 }
275 return QPoint(list.at(0), list.at(1));
276 }
277 case QMetaType::QPointF: {
278 const auto list = asRealList(value);
279
280 if (list.count() != 2) {
281 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(2, list.count());
282 return aDefault;
283 }
284 return QPointF(list.at(0), list.at(1));
285 }
286 case QMetaType::QRect: {
287 const auto list = asIntList(value);
288
289 if (list.count() != 4) {
290 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(4, list.count());
291 return aDefault;
292 }
293 const QRect rect(list.at(0), list.at(1), list.at(2), list.at(3));
294 if (!rect.isValid()) {
295 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
296 return aDefault;
297 }
298 return rect;
299 }
300 case QMetaType::QRectF: {
301 const auto list = asRealList(value);
302
303 if (list.count() != 4) {
304 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(4, list.count());
305 return aDefault;
306 }
307 const QRectF rect(list.at(0), list.at(1), list.at(2), list.at(3));
308 if (!rect.isValid()) {
309 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
310 return aDefault;
311 }
312 return rect;
313 }
314 case QMetaType::QSize: {
315 const auto list = asIntList(value);
316
317 if (list.count() != 2) {
318 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(2, list.count());
319 return aDefault;
320 }
321 const QSize size(list.at(0), list.at(1));
322 if (!size.isValid()) {
323 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
324 return aDefault;
325 }
326 return size;
327 }
328 case QMetaType::QSizeF: {
329 const auto list = asRealList(value);
330
331 if (list.count() != 2) {
332 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(2, list.count());
333 return aDefault;
334 }
335 const QSizeF size(list.at(0), list.at(1));
336 if (!size.isValid()) {
337 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
338 return aDefault;
339 }
340 return size;
341 }
343 const auto list = asRealList(value);
344 if (list.count() < 6) {
345 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(6, list.count());
346 return aDefault;
347 }
348 const QDate date(list.at(0), list.at(1), list.at(2));
349 const qreal totalSeconds = list.at(5);
350 qreal seconds;
351 const qreal fractional = modf(totalSeconds, &seconds);
352 const qreal milliseconds = round(fractional * 1000.0);
353 const QTime time(list.at(3), list.at(4), seconds, milliseconds);
354 const QDateTime dt(date, time);
355 if (!dt.isValid()) {
356 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
357 return aDefault;
358 }
359 return dt;
360 }
361 case QMetaType::QDate: {
362 auto list = asIntList(value);
363 // list.count == 6 -> don't break config files that stored QDate as QDateTime
364 if (list.count() != 3 && list.count() != 6) {
365 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault) << formatError(3, list.count());
366 return aDefault;
367 }
368 const QDate date(list.at(0), list.at(1), list.at(2));
369 if (!date.isValid()) {
370 qCWarning(KCONFIG_CORE_LOG) << errString(pKey, value, aDefault);
371 return aDefault;
372 }
373 return date;
374 }
376 case QMetaType::QFont:
377 qCWarning(KCONFIG_CORE_LOG) << "KConfigGroup::readEntry was passed GUI type '" << aDefault.typeName()
378 << "' but KConfigGui isn't linked! If it is linked to your program, "
379 "this is a platform bug. Please inform the KDE developers";
380 break;
381 case QMetaType::QUrl:
382 return QUrl(QString::fromUtf8(value));
383
384 default:
385 break;
386 }
387
388 qCWarning(KCONFIG_CORE_LOG) << "unhandled type " << aDefault.typeName();
389 return QVariant();
390}
391
392static bool cleanHomeDirPath(QString &path, const QString &homeDir)
393{
394#ifdef Q_OS_WIN // safer
395 if (!QDir::toNativeSeparators(path).startsWith(QDir::toNativeSeparators(homeDir))) {
396 return false;
397 }
398#else
399 if (!path.startsWith(homeDir)) {
400 return false;
401 }
402#endif
403
404 int len = homeDir.length();
405 // replace by "$HOME" if possible
406 if (len && (path.length() == len || path[len] == QLatin1Char('/'))) {
407 path.replace(0, len, QStringLiteral("$HOME"));
408 return true;
409 }
410
411 return false;
412}
413
414static QString translatePath(QString path) // krazy:exclude=passbyvalue
415{
416 if (path.isEmpty()) {
417 return path;
418 }
419
420 // only "our" $HOME should be interpreted
422
423 const bool startsWithFile = path.startsWith(QLatin1String("file:"), Qt::CaseInsensitive);
424 path = startsWithFile ? QUrl(path).toLocalFile() : path;
425
426 if (QDir::isRelativePath(path)) {
427 return path;
428 }
429
430 // Use the same thing as what expandString() will do, to keep data intact
431#ifdef Q_OS_WIN
432 const QString homeDir = QDir::homePath();
433#else
434 const QString homeDir = QFile::decodeName(qgetenv("HOME"));
435#endif
436 (void)cleanHomeDirPath(path, homeDir);
437
438 if (startsWithFile) {
440 }
441
442 return path;
443}
444
446 : d()
447{
448}
449
451{
452 return bool(d);
453}
454
455KConfigGroupGui _kde_internal_KConfigGroupGui;
456static inline bool readEntryGui(const QByteArray &data, const char *key, const QVariant &input, QVariant &output)
457{
458 if (_kde_internal_KConfigGroupGui.readEntryGui) {
459 return _kde_internal_KConfigGroupGui.readEntryGui(data, key, input, output);
460 }
461 return false;
462}
463
464static inline bool writeEntryGui(KConfigGroup *cg, const char *key, const QVariant &input, KConfigGroup::WriteConfigFlags flags)
465{
466 if (_kde_internal_KConfigGroupGui.writeEntryGui) {
467 return _kde_internal_KConfigGroupGui.writeEntryGui(cg, key, input, flags);
468 }
469 return false;
470}
471
473 : d(KConfigGroupPrivate::create(master, _group, master->isGroupImmutable(_group), false))
474{
475}
476
477KConfigGroup::KConfigGroup(const KConfigBase *master, const QString &_group)
478 : d(KConfigGroupPrivate::create(const_cast<KConfigBase *>(master), _group, master->isGroupImmutable(_group), true))
479{
480}
481
483 : d(new KConfigGroupPrivate(master, _group))
484{
485}
486
487KConfigGroup &KConfigGroup::operator=(const KConfigGroup &rhs)
488{
489 d = rhs.d;
490 return *this;
491}
492
494 : d(rhs.d)
495{
496}
497
498KConfigGroup::~KConfigGroup()
499{
500 d.reset();
501}
502
504{
505 Q_ASSERT_X(isValid(), "KConfigGroup::groupImpl", "accessing an invalid group");
506 Q_ASSERT_X(!aGroup.isEmpty(), "KConfigGroup::groupImpl", "can not have an unnamed child group");
507
508 KConfigGroup newGroup;
509
510 newGroup.d = new KConfigGroupPrivate(this, isGroupImmutableImpl(aGroup), d->bConst, aGroup);
511
512 return newGroup;
513}
514
516{
517 Q_ASSERT_X(isValid(), "KConfigGroup::groupImpl", "accessing an invalid group");
518 Q_ASSERT_X(!aGroup.isEmpty(), "KConfigGroup::groupImpl", "can not have an unnamed child group");
519
520 KConfigGroup newGroup;
521
522 newGroup.d = new KConfigGroupPrivate(const_cast<KConfigGroup *>(this), isGroupImmutableImpl(aGroup), true, aGroup);
523
524 return newGroup;
525}
526
528{
529 Q_ASSERT_X(isValid(), "KConfigGroup::parent", "accessing an invalid group");
530
531 KConfigGroup parentGroup;
532
533 if (d->mParent) {
534 parentGroup.d = d->mParent;
535 } else {
536 parentGroup.d = new KConfigGroupPrivate(d->mOwner, d->mOwner->isImmutable(), d->bConst, QString());
537 // make sure we keep the refcount up on the KConfig object
538 parentGroup.d->sOwner = d->sOwner;
539 }
540
541 return parentGroup;
542}
543
545{
546 Q_ASSERT_X(isValid(), "KConfigGroup::deleteGroup", "accessing an invalid group");
547 Q_ASSERT_X(!d->bConst, "KConfigGroup::deleteGroup", "deleting a read-only group");
548
549 config()->deleteGroup(d->fullName(), flags);
550}
551
553{
554 Q_ASSERT_X(isValid(), "KConfigGroup::name", "accessing an invalid group");
555
556 return d->name();
557}
558
560{
561 Q_ASSERT_X(isValid(), "KConfigGroup::exists", "accessing an invalid group");
562
563 return config()->hasGroup(d->fullName());
564}
565
567{
568 Q_ASSERT_X(isValid(), "KConfigGroup::sync", "accessing an invalid group");
569
570 if (!d->bConst) {
571 return config()->sync();
572 }
573
574 return false;
575}
576
578{
579 Q_ASSERT_X(isValid(), "KConfigGroup::entryMap", "accessing an invalid group");
580
581 return config()->entryMap(d->fullName());
582}
583
585{
586 Q_ASSERT_X(isValid(), "KConfigGroup::config", "accessing an invalid group");
587
588 return d->mOwner;
589}
590
592{
593 Q_ASSERT_X(isValid(), "KConfigGroup::config", "accessing an invalid group");
594
595 return d->mOwner;
596}
597
598bool KConfigGroup::isEntryImmutable(const char *key) const
599{
600 Q_ASSERT_X(isValid(), "KConfigGroup::isEntryImmutable", "accessing an invalid group");
601
602 return (isImmutable() || !config()->d_func()->canWriteEntry(d->fullName(), key, config()->readDefaults()));
603}
604
606{
607 return isEntryImmutable(key.toUtf8().constData());
608}
609
611{
612 return readEntryUntranslated(pKey.toUtf8().constData(), aDefault);
613}
614
615QString KConfigGroup::readEntryUntranslated(const char *key, const QString &aDefault) const
616{
617 Q_ASSERT_X(isValid(), "KConfigGroup::readEntryUntranslated", "accessing an invalid group");
618
619 QString result = config()->d_func()->lookupData(d->fullName(), key, KEntryMap::SearchFlags(), nullptr);
620 if (result.isNull()) {
621 return aDefault;
622 }
623 return result;
624}
625
626QString KConfigGroup::readEntry(const char *key, const char *aDefault) const
627{
628 return readEntry(key, QString::fromUtf8(aDefault));
629}
630
631QString KConfigGroup::readEntry(const QString &key, const char *aDefault) const
632{
633 return readEntry(key.toUtf8().constData(), aDefault);
634}
635
636QString KConfigGroup::readEntry(const char *key, const QString &aDefault) const
637{
638 Q_ASSERT_X(isValid(), "KConfigGroup::readEntry", "accessing an invalid group");
639
640 bool expand = false;
641
642 // read value from the entry map
643 QString aValue = config()->d_func()->lookupData(d->fullName(), key, KEntryMap::SearchLocalized, &expand);
644 if (aValue.isNull()) {
645 aValue = aDefault;
646 }
647
648 if (expand) {
649 return KConfigPrivate::expandString(aValue);
650 }
651
652 return aValue;
653}
654
655QString KConfigGroup::readEntry(const QString &key, const QString &aDefault) const
656{
657 return readEntry(key.toUtf8().constData(), aDefault);
658}
659
660QStringList KConfigGroup::readEntry(const char *key, const QStringList &aDefault) const
661{
662 Q_ASSERT_X(isValid(), "KConfigGroup::readEntry", "accessing an invalid group");
663
664 const QString data = readEntry(key, QString());
665 if (data.isNull()) {
666 return aDefault;
667 }
668
669 return KConfigGroupPrivate::deserializeList(data);
670}
671
672QStringList KConfigGroup::readEntry(const QString &key, const QStringList &aDefault) const
673{
674 return readEntry(key.toUtf8().constData(), aDefault);
675}
676
677QVariant KConfigGroup::readEntry(const char *key, const QVariant &aDefault) const
678{
679 Q_ASSERT_X(isValid(), "KConfigGroup::readEntry", "accessing an invalid group");
680
681 const QByteArray data = config()->d_func()->lookupData(d->fullName(), key, KEntryMap::SearchLocalized);
682 if (data.isNull()) {
683 return aDefault;
684 }
685
686 QVariant value;
687 if (!readEntryGui(data, key, aDefault, value)) {
688 return convertToQVariant(key, data, aDefault);
689 }
690
691 return value;
692}
693
694QVariant KConfigGroup::readEntry(const QString &key, const QVariant &aDefault) const
695{
696 return readEntry(key.toUtf8().constData(), aDefault);
697}
698
699QVariantList KConfigGroup::readEntry(const char *key, const QVariantList &aDefault) const
700{
701 Q_ASSERT_X(isValid(), "KConfigGroup::readEntry", "accessing an invalid group");
702
703 const QString data = readEntry(key, QString());
704 if (data.isNull()) {
705 return aDefault;
706 }
707
708 const auto &list = KConfigGroupPrivate::deserializeList(data);
709
710 QVariantList value;
711 value.reserve(list.count());
712 for (const QString &v : list) {
713 value << v;
714 }
715
716 return value;
717}
718
719QVariantList KConfigGroup::readEntry(const QString &key, const QVariantList &aDefault) const
720{
721 return readEntry(key.toUtf8().constData(), aDefault);
722}
723
725{
726 return readXdgListEntry(key.toUtf8().constData(), aDefault);
727}
728
729QStringList KConfigGroup::readXdgListEntry(const char *key, const QStringList &aDefault) const
730{
731 Q_ASSERT_X(isValid(), "KConfigGroup::readXdgListEntry", "accessing an invalid group");
732
733 const QString data = readEntry(key, QString());
734 if (data.isNull()) {
735 return aDefault;
736 }
737
738 QStringList value;
739 QString val;
740 val.reserve(data.size());
741 // XXX List serialization being a separate layer from low-level parsing is
742 // probably a bug. No affected entries are defined, though.
743 bool quoted = false;
744 for (int p = 0; p < data.length(); p++) {
745 if (quoted) {
746 val += data[p];
747 quoted = false;
748 } else if (data[p] == QLatin1Char('\\')) {
749 quoted = true;
750 } else if (data[p] == QLatin1Char(';')) {
751 value.append(val);
752 val.clear();
753 val.reserve(data.size() - p);
754 } else {
755 val += data[p];
756 }
757 }
758 if (!val.isEmpty()) {
759 value.append(val);
760 }
761 return value;
762}
763
764QString KConfigGroup::readPathEntry(const QString &pKey, const QString &aDefault) const
765{
766 return readPathEntry(pKey.toUtf8().constData(), aDefault);
767}
768
769QString KConfigGroup::readPathEntry(const char *key, const QString &aDefault) const
770{
771 Q_ASSERT_X(isValid(), "KConfigGroup::readPathEntry", "accessing an invalid group");
772
773 bool expand = false;
774
775 QString aValue = config()->d_func()->lookupData(d->fullName(), key, KEntryMap::SearchLocalized, &expand);
776 if (aValue.isNull()) {
777 aValue = aDefault;
778 }
779
780 return KConfigPrivate::expandString(aValue);
781}
782
784{
785 return readPathEntry(pKey.toUtf8().constData(), aDefault);
786}
787
788QStringList KConfigGroup::readPathEntry(const char *key, const QStringList &aDefault) const
789{
790 Q_ASSERT_X(isValid(), "KConfigGroup::readPathEntry", "accessing an invalid group");
791
792 const QString data = readPathEntry(key, QString());
793 if (data.isNull()) {
794 return aDefault;
795 }
796
797 return KConfigGroupPrivate::deserializeList(data);
798}
799
800void KConfigGroup::writeEntry(const char *key, const QString &value, WriteConfigFlags flags)
801{
802 Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
803 Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
804
805 writeEntry(key, value.toUtf8(), flags);
806}
807
808void KConfigGroup::writeEntry(const QString &key, const QString &value, WriteConfigFlags flags)
809{
810 writeEntry(key.toUtf8().constData(), value, flags);
811}
812
813void KConfigGroup::writeEntry(const QString &key, const char *value, WriteConfigFlags pFlags)
814{
815 Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
816 Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
817
818 writeEntry(key.toUtf8().constData(), QVariant(QString::fromLatin1(value)), pFlags);
819}
820
821void KConfigGroup::writeEntry(const char *key, const char *value, WriteConfigFlags pFlags)
822{
823 writeEntry(key, QVariant(QString::fromLatin1(value)), pFlags);
824}
825
826void KConfigGroup::writeEntry(const char *key, const QByteArray &value, WriteConfigFlags flags)
827{
828 Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
829 Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
830
831 config()->d_func()->putData(d->fullName(), key, value.isNull() ? QByteArray("") : value, flags);
832}
833
834void KConfigGroup::writeEntry(const QString &key, const QByteArray &value, WriteConfigFlags pFlags)
835{
836 writeEntry(key.toUtf8().constData(), value, pFlags);
837}
838
839void KConfigGroup::writeEntry(const char *key, const QStringList &list, WriteConfigFlags flags)
840{
841 Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
842 Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
843
844 QList<QByteArray> balist;
845 balist.reserve(list.count());
846
847 for (const QString &entry : list) {
848 balist.append(entry.toUtf8());
849 }
850
851 writeEntry(key, KConfigGroupPrivate::serializeList(balist), flags);
852}
853
855{
856 writeEntry(key.toUtf8().constData(), list, flags);
857}
858
859void KConfigGroup::writeEntry(const char *key, const QVariantList &list, WriteConfigFlags flags)
860{
861 Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
862 Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
863
865 data.reserve(list.count());
866
867 for (const QVariant &v : list) {
868 if (v.userType() == QMetaType::QByteArray) {
869 data << v.toByteArray();
870 } else {
871 data << v.toString().toUtf8();
872 }
873 }
874
875 writeEntry(key, KConfigGroupPrivate::serializeList(data), flags);
876}
877
878void KConfigGroup::writeEntry(const char *key, const QVariant &value, WriteConfigFlags flags)
879{
880 Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
881 Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
882
883 if (writeEntryGui(this, key, value, flags)) {
884 return; // GUI type that was handled
885 }
886
887 QByteArray data;
888 // if a type handler is added here you must add a QVConversions definition
889 // to kconfigconversioncheck_p.h, or KConfigConversionCheck::to_QVariant will not allow
890 // writeEntry<T> to convert to QVariant.
891 switch (static_cast<QMetaType::Type>(value.userType())) {
893 data = "";
894 break;
896 data = value.toByteArray();
897 break;
899 case QMetaType::Int:
900 case QMetaType::UInt:
902 case QMetaType::Float:
903 case QMetaType::Bool:
906 data = value.toString().toUtf8();
907 break;
909 if (!value.canConvert<QStringList>()) {
910 qCWarning(KCONFIG_CORE_LOG) << "not all types in \"" << key
911 << "\" can convert to QString,"
912 " information will be lost";
913 }
914 Q_FALLTHROUGH();
916 writeEntry(key, value.toList(), flags);
917 return;
918 case QMetaType::QPoint: {
919 const QPoint rPoint = value.toPoint();
920
921 const QVariantList list{rPoint.x(), rPoint.y()};
922
923 writeEntry(key, list, flags);
924 return;
925 }
926 case QMetaType::QPointF: {
927 const QPointF point = value.toPointF();
928
929 const QVariantList list{point.x(), point.y()};
930
931 writeEntry(key, list, flags);
932 return;
933 }
934 case QMetaType::QRect: {
935 const QRect rRect = value.toRect();
936
937 const QVariantList list{rRect.left(), rRect.top(), rRect.width(), rRect.height()};
938
939 writeEntry(key, list, flags);
940 return;
941 }
942 case QMetaType::QRectF: {
943 const QRectF rRectF = value.toRectF();
944
945 const QVariantList list{rRectF.left(), rRectF.top(), rRectF.width(), rRectF.height()};
946
947 writeEntry(key, list, flags);
948 return;
949 }
950 case QMetaType::QSize: {
951 const QSize rSize = value.toSize();
952
953 const QVariantList list{rSize.width(), rSize.height()};
954
955 writeEntry(key, list, flags);
956 return;
957 }
958 case QMetaType::QUuid: {
959 writeEntry(key, value.toString(), flags);
960 return;
961 }
962 case QMetaType::QSizeF: {
963 const QSizeF rSizeF = value.toSizeF();
964
965 const QVariantList list{rSizeF.width(), rSizeF.height()};
966
967 writeEntry(key, list, flags);
968 return;
969 }
970 case QMetaType::QDate: {
971 const QDate date = value.toDate();
972
973 const QVariantList list{date.year(), date.month(), date.day()};
974
975 writeEntry(key, list, flags);
976 return;
977 }
979 const QDateTime rDateTime = value.toDateTime();
980
981 const QTime time = rDateTime.time();
982 const QDate date = rDateTime.date();
983
984 const QVariantList list{
985 date.year(),
986 date.month(),
987 date.day(),
988
989 time.hour(),
990 time.minute(),
991 time.second() + time.msec() / 1000.0,
992 };
993
994 writeEntry(key, list, flags);
995 return;
996 }
997
999 case QMetaType::QFont:
1000 qCWarning(KCONFIG_CORE_LOG) << "KConfigGroup::writeEntry was passed GUI type '" << value.typeName()
1001 << "' but KConfigGui isn't linked! If it is linked to your program, this is a platform bug. "
1002 "Please inform the KDE developers";
1003 break;
1004 case QMetaType::QUrl:
1005 data = QUrl(value.toUrl()).toString().toUtf8();
1006 break;
1007 default:
1008 qCWarning(KCONFIG_CORE_LOG) << "KConfigGroup::writeEntry - unhandled type" << value.typeName() << "in group" << name();
1009 }
1010
1011 writeEntry(key, data, flags);
1012}
1013
1014void KConfigGroup::writeEntry(const QString &key, const QVariant &value, WriteConfigFlags flags)
1015{
1016 writeEntry(key.toUtf8().constData(), value, flags);
1017}
1018
1019void KConfigGroup::writeEntry(const QString &key, const QVariantList &list, WriteConfigFlags flags)
1020{
1021 writeEntry(key.toUtf8().constData(), list, flags);
1022}
1023
1025{
1026 writeXdgListEntry(key.toUtf8().constData(), value, pFlags);
1027}
1028
1029void KConfigGroup::writeXdgListEntry(const char *key, const QStringList &list, WriteConfigFlags flags)
1030{
1031 Q_ASSERT_X(isValid(), "KConfigGroup::writeXdgListEntry", "accessing an invalid group");
1032 Q_ASSERT_X(!d->bConst, "KConfigGroup::writeXdgListEntry", "writing to a read-only group");
1033
1034 QString value;
1035 value.reserve(4096);
1036
1037 // XXX List serialization being a separate layer from low-level escaping is
1038 // probably a bug. No affected entries are defined, though.
1039 for (QString val : list) { // clazy:exclude=range-loop
1040 val.replace(QLatin1Char('\\'), QLatin1String("\\\\")).replace(QLatin1Char(';'), QLatin1String("\\;"));
1041 value += val + QLatin1Char(';');
1042 }
1043
1044 writeEntry(key, value, flags);
1045}
1046
1047void KConfigGroup::writePathEntry(const QString &pKey, const QString &path, WriteConfigFlags pFlags)
1048{
1049 writePathEntry(pKey.toUtf8().constData(), path, pFlags);
1050}
1051
1052void KConfigGroup::writePathEntry(const char *pKey, const QString &path, WriteConfigFlags pFlags)
1053{
1054 Q_ASSERT_X(isValid(), "KConfigGroup::writePathEntry", "accessing an invalid group");
1055 Q_ASSERT_X(!d->bConst, "KConfigGroup::writePathEntry", "writing to a read-only group");
1056
1057 config()->d_func()->putData(d->fullName(), pKey, translatePath(path).toUtf8(), pFlags, true);
1058}
1059
1061{
1062 writePathEntry(pKey.toUtf8().constData(), value, pFlags);
1063}
1064
1065void KConfigGroup::writePathEntry(const char *pKey, const QStringList &value, WriteConfigFlags pFlags)
1066{
1067 Q_ASSERT_X(isValid(), "KConfigGroup::writePathEntry", "accessing an invalid group");
1068 Q_ASSERT_X(!d->bConst, "KConfigGroup::writePathEntry", "writing to a read-only group");
1069
1070 QList<QByteArray> list;
1071 list.reserve(value.length());
1072 for (const QString &path : value) {
1073 list << translatePath(path).toUtf8();
1074 }
1075
1076 config()->d_func()->putData(d->fullName(), pKey, KConfigGroupPrivate::serializeList(list), pFlags, true);
1077}
1078
1080{
1081 Q_ASSERT_X(isValid(), "KConfigGroup::deleteEntry", "accessing an invalid group");
1082 Q_ASSERT_X(!d->bConst, "KConfigGroup::deleteEntry", "deleting from a read-only group");
1083
1084 config()->d_func()->putData(d->fullName(), key, QByteArray(), flags);
1085}
1086
1088{
1089 deleteEntry(key.toUtf8().constData(), flags);
1090}
1091
1093{
1094 Q_ASSERT_X(isValid(), "KConfigGroup::revertToDefault", "accessing an invalid group");
1095 Q_ASSERT_X(!d->bConst, "KConfigGroup::revertToDefault", "writing to a read-only group");
1096
1097 config()->d_func()->revertEntry(d->fullName(), key, flags);
1098}
1099
1101{
1102 revertToDefault(key.toUtf8().constData(), flags);
1103}
1104
1105bool KConfigGroup::hasDefault(const char *key) const
1106{
1107 Q_ASSERT_X(isValid(), "KConfigGroup::hasDefault", "accessing an invalid group");
1108
1109 KEntryMap::SearchFlags flags = KEntryMap::SearchDefaults | KEntryMap::SearchLocalized;
1110
1111 return !config()->d_func()->lookupData(d->fullName(), key, flags).isNull();
1112}
1113
1114bool KConfigGroup::hasDefault(const QString &key) const
1115{
1116 return hasDefault(key.toUtf8().constData());
1117}
1118
1119bool KConfigGroup::hasKey(const char *key) const
1120{
1121 Q_ASSERT_X(isValid(), "KConfigGroup::hasKey", "accessing an invalid group");
1122
1123 KEntryMap::SearchFlags flags = KEntryMap::SearchLocalized;
1124 if (config()->readDefaults()) {
1125 flags |= KEntryMap::SearchDefaults;
1126 }
1127
1128 return !config()->d_func()->lookupData(d->fullName(), key, flags).isNull();
1129}
1130
1131bool KConfigGroup::hasKey(const QString &key) const
1132{
1133 return hasKey(key.toUtf8().constData());
1134}
1135
1137{
1138 Q_ASSERT_X(isValid(), "KConfigGroup::isImmutable", "accessing an invalid group");
1139
1140 return d->bImmutable;
1141}
1142
1144{
1145 Q_ASSERT_X(isValid(), "KConfigGroup::groupList", "accessing an invalid group");
1146
1147 return config()->d_func()->groupList(d->fullName());
1148}
1149
1151{
1152 Q_ASSERT_X(isValid(), "KConfigGroup::keyList", "accessing an invalid group");
1153
1154 return config()->d_func()->usedKeyList(d->fullName());
1155}
1156
1158{
1159 Q_ASSERT_X(isValid(), "KConfigGroup::markAsClean", "accessing an invalid group");
1160
1161 config()->markAsClean();
1162}
1163
1165{
1166 Q_ASSERT_X(isValid(), "KConfigGroup::accessMode", "accessing an invalid group");
1167
1168 return config()->accessMode();
1169}
1170
1172{
1173 Q_ASSERT_X(isValid(), "KConfigGroup::hasGroupImpl", "accessing an invalid group");
1174
1175 return config()->hasGroup(d->fullName(b));
1176}
1177
1179{
1180 Q_ASSERT_X(isValid(), "KConfigGroup::deleteGroupImpl", "accessing an invalid group");
1181 Q_ASSERT_X(!d->bConst, "KConfigGroup::deleteGroupImpl", "deleting from a read-only group");
1182
1183 config()->deleteGroup(d->fullName(b), flags);
1184}
1185
1187{
1188 Q_ASSERT_X(isValid(), "KConfigGroup::isGroupImmutableImpl", "accessing an invalid group");
1189
1190 if (!hasGroupImpl(groupName)) { // group doesn't exist yet
1191 return d->bImmutable; // child groups are immutable if the parent is immutable.
1192 }
1193
1194 return config()->isGroupImmutable(d->fullName(groupName));
1195}
1196
1198{
1199 Q_ASSERT_X(isValid(), "KConfigGroup::copyTo", "accessing an invalid group");
1200 Q_ASSERT(other != nullptr);
1201
1202 if (KConfigGroup *otherGroup = dynamic_cast<KConfigGroup *>(other)) {
1203 config()->d_func()->copyGroup(d->fullName(), otherGroup->d->fullName(), otherGroup, pFlags);
1204 } else if (KConfig *otherConfig = dynamic_cast<KConfig *>(other)) {
1205 KConfigGroup newGroup = otherConfig->group(d->fullName());
1206 otherConfig->d_func()->copyGroup(d->fullName(), d->fullName(), &newGroup, pFlags);
1207 } else {
1208 Q_ASSERT_X(false, "KConfigGroup::copyTo", "unknown type of KConfigBase");
1209 }
1210}
1211
1213{
1214 Q_ASSERT_X(isValid(), "KConfigGroup::reparent", "accessing an invalid group");
1215 Q_ASSERT_X(!d->bConst, "KConfigGroup::reparent", "reparenting a read-only group");
1216 Q_ASSERT_X(!d->bImmutable, "KConfigGroup::reparent", "reparenting an immutable group");
1217 Q_ASSERT(parent != nullptr);
1218
1219 KConfigGroup oldGroup(*this);
1220
1221 d = KConfigGroupPrivate::create(parent, d->mName, false, false);
1222 oldGroup.copyTo(this, pFlags);
1223 oldGroup.deleteGroup(); // so that the entries with the old group name are deleted on sync
1224}
1225
1226void KConfigGroup::moveValue(const char *key, KConfigGroup &other, WriteConfigFlags pFlags)
1227{
1228 const QString groupName = name();
1229 const auto entry = config()->d_ptr->lookupInternalEntry(groupName, key, KEntryMap::SearchLocalized);
1230
1231 // Only write the entry if it is not null, if it is a global enry there is no point in moving it
1232 if (!entry.mValue.isNull() && !entry.bGlobal) {
1233 deleteEntry(key, pFlags);
1234 KEntryMap::EntryOptions options = KEntryMap::EntryOption::EntryDirty;
1235 if (entry.bDeleted) {
1236 options |= KEntryMap::EntryDeleted;
1237 }
1238
1239 if (entry.bExpand) {
1240 options |= KEntryMap::EntryExpansion;
1241 }
1242
1243 other.config()->d_ptr->setEntryData(other.name(), key, entry.mValue, options);
1244 }
1245}
1246
1248{
1249 Q_ASSERT(isValid());
1250 Q_ASSERT(other.isValid());
1251
1252 for (const auto key : keys) {
1253 moveValue(key, other, pFlags);
1254 }
1255}
1256
1258{
1259 Q_ASSERT(isValid());
1260 Q_ASSERT(other.isValid());
1261
1262 const QStringList keys = keyList();
1263 for (const QString &key : keys) {
1264 moveValue(key.toUtf8().constData(), other, pFlags);
1265 }
1266}
Interface to interact with configuration.
Definition kconfigbase.h:31
void deleteGroup(const QString &group, WriteConfigFlags flags=Normal)
Delete group.
AccessMode
Possible return values for accessMode().
bool isGroupImmutable(const QString &group) const
Can changes be made to the entries in group?
bool hasGroup(const QString &group) const
Returns true if the specified group is known about.
KConfigGroup group(const QString &group)
Returns an object for the named subgroup.
A class for one specific group in a KConfig object.
void reparent(KConfigBase *parent, WriteConfigFlags pFlags=Normal)
Changes the configuration object that this group belongs to.
T readEntry(const QString &key, const T &aDefault) const
Reads the value of an entry specified by pKey in the current group.
void writeXdgListEntry(const QString &pKey, const QStringList &value, WriteConfigFlags pFlags=Normal)
Writes a list of strings to the config object, following XDG desktop entry spec separator semantics.
QString name() const
The name of this group.
bool hasDefault(const QString &key) const
Whether a default is specified for an entry in either the system wide configuration file or the globa...
bool hasKey(const QString &key) const
Checks whether the key has an entry in this group.
QString readPathEntry(const QString &pKey, const QString &aDefault) const
Reads a path.
bool hasGroupImpl(const QString &groupName) const override
void writePathEntry(const QString &pKey, const QString &path, WriteConfigFlags pFlags=Normal)
Writes a file path to the configuration.
void revertToDefault(const QString &key, WriteConfigFlags pFlag=WriteConfigFlags())
Reverts an entry to the default settings.
bool isEntryImmutable(const QString &key) const
Checks if it is possible to change the given entry.
void writeEntry(const QString &key, const QVariant &value, WriteConfigFlags pFlags=Normal)
Writes a value to the configuration object.
AccessMode accessMode() const override
void deleteGroup(WriteConfigFlags pFlags=Normal)
Delete all entries in the entire group.
void deleteGroupImpl(const QString &groupName, WriteConfigFlags flags) override
bool isValid() const
Whether the group is valid.
bool isGroupImmutableImpl(const QString &groupName) const override
void markAsClean() override
KConfigGroup()
Constructs an invalid group.
bool isImmutable() const override
Whether this group may be changed.
KConfig * config()
Return the config object that this group belongs to.
bool exists() const
Check whether the containing KConfig object actually contains a group with this name.
void deleteEntry(const QString &pKey, WriteConfigFlags pFlags=Normal)
Deletes the entry specified by pKey in the current group.
bool sync() override
void moveValuesTo(const QList< const char * > &keys, KConfigGroup &other, WriteConfigFlags pFlags=Normal)
Moves the key-value pairs from one config group to the other.
KConfigGroup parent() const
Returns the group that this group belongs to.
QMap< QString, QString > entryMap() const
Returns a map (tree) of entries for all entries in this group.
QStringList readXdgListEntry(const QString &pKey, const QStringList &aDefault=QStringList()) const
Reads a list of strings from the config object with semicolons separating them (i....
QStringList groupList() const override
QString readEntryUntranslated(const QString &pKey, const QString &aDefault=QString()) const
Reads an untranslated string entry.
KConfigGroup groupImpl(const QString &groupName) override
QStringList keyList() const
Returns a list of keys this group contains.
void copyTo(KConfigBase *other, WriteConfigFlags pFlags=Normal) const
Copies the entries in this group to another configuration object.
The central class of the KDE configuration data system.
Definition kconfig.h:56
AccessMode accessMode() const override
Definition kconfig.cpp:807
void markAsClean() override
Definition kconfig.cpp:513
QMap< QString, QString > entryMap(const QString &aGroup=QString()) const
Returns a map (tree) of entries in a particular group.
Definition kconfig.cpp:382
bool sync() override
Definition kconfig.cpp:407
QString name() const
Returns the filename used to store the configuration.
Definition kconfig.cpp:560
QStringList groupList() const override
Definition kconfig.cpp:300
Q_SCRIPTABLE Q_NOREPLY void start()
QString name(GameStandardAction id)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
QString path(const QString &relativePath)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QAction * next(const QObject *recvr, const char *slot, QObject *parent)
const char * constData() const const
bool isEmpty() const const
bool isNull() const const
QByteArray & replace(QByteArrayView before, QByteArrayView after)
void reserve(qsizetype size)
int day() const const
int month() const const
int year() const const
QDate date() const const
QTime time() const const
QString homePath()
bool isRelativePath(const QString &path)
QString toNativeSeparators(const QString &pathName)
QString decodeName(const QByteArray &localFileName)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
const_iterator cbegin() const const
const_iterator cend() const const
qsizetype count() const const
bool isEmpty() const const
qsizetype length() const const
void reserve(qsizetype size)
int x() const const
int y() const const
qreal x() const const
qreal y() const const
int height() const const
int left() const const
int top() const const
int width() const const
qreal height() const const
qreal left() const const
qreal top() const const
qreal width() const const
int height() const const
int width() const const
qreal height() const const
qreal width() const const
QString arg(Args &&... args) const const
void clear()
QString fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
bool isNull() const const
qsizetype length() const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
void reserve(qsizetype size)
qsizetype size() const const
void squeeze()
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QByteArray toUtf8() const const
CaseInsensitive
int hour() const const
int minute() const const
int msec() const const
int second() const const
QUrl fromLocalFile(const QString &localFile)
QString toLocalFile() const const
QString toString(FormattingOptions options) const const
QUuid fromString(QAnyStringView string)
bool canConvert() const const
bool convert(QMetaType targetType)
QMetaType metaType() const const
QByteArray toByteArray() const const
QDate toDate() const const
QDateTime toDateTime() const const
QList< QVariant > toList() const const
QPoint toPoint() const const
QPointF toPointF() const const
QRect toRect() const const
QRectF toRectF() const const
QSize toSize() const const
QSizeF toSizeF() const const
QString toString() const const
QUrl toUrl() const const
const char * typeName() const const
int userType() const const
void push_back(T &&t)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:18:27 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.