File indexing completed on 2024-05-12 03:54:28

0001 /*
0002     SPDX-FileCopyrightText: 2007 Aaron Seigo <aseigo@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "kconfigloader.h"
0008 #include "kconfigloader_p.h"
0009 #include "kconfigloaderhandler_p.h"
0010 
0011 #include <QColor>
0012 #include <QFont>
0013 #include <QHash>
0014 #include <QUrl>
0015 
0016 #include <QDebug>
0017 
0018 void ConfigLoaderPrivate::parse(KConfigLoader *loader, QIODevice *xml)
0019 {
0020     clearData();
0021     loader->clearItems();
0022 
0023     if (xml) {
0024         ConfigLoaderHandler handler(loader, this);
0025         handler.parse(xml);
0026     }
0027 }
0028 
0029 ConfigLoaderHandler::ConfigLoaderHandler(KConfigLoader *config, ConfigLoaderPrivate *d)
0030     : m_config(config)
0031     , d(d)
0032 {
0033     resetState();
0034 }
0035 
0036 bool ConfigLoaderHandler::parse(QIODevice *input)
0037 {
0038     if (!input->open(QIODevice::ReadOnly)) {
0039         qWarning() << "Impossible to open device";
0040         return false;
0041     }
0042     QXmlStreamReader reader(input);
0043 
0044     while (!reader.atEnd()) {
0045         reader.readNext();
0046         if (reader.hasError()) {
0047             return false;
0048         }
0049 
0050         switch (reader.tokenType()) {
0051         case QXmlStreamReader::StartElement:
0052             startElement(reader.name(), reader.attributes());
0053             break;
0054         case QXmlStreamReader::EndElement:
0055             endElement(reader.name());
0056             break;
0057         case QXmlStreamReader::Characters:
0058             if (!reader.isWhitespace() && !reader.text().trimmed().isEmpty()) {
0059                 m_cdata.append(reader.text());
0060             }
0061             break;
0062         default:
0063             break;
0064         }
0065     }
0066 
0067     if (!reader.isEndDocument()) {
0068         return false;
0069     }
0070 
0071     return true;
0072 }
0073 
0074 static bool caseInsensitiveCompare(const QStringView a, const QLatin1String b)
0075 {
0076     return a.compare(b, Qt::CaseInsensitive) == 0;
0077 }
0078 
0079 void ConfigLoaderHandler::startElement(const QStringView localName, const QXmlStreamAttributes &attrs)
0080 {
0081     // qDebug() << "ConfigLoaderHandler::startElement(" << localName << qName;
0082     if (caseInsensitiveCompare(localName, QLatin1String("group"))) {
0083         QString group;
0084         for (const auto &attr : attrs) {
0085             const auto attrName = attr.name();
0086             if (caseInsensitiveCompare(attrName, QLatin1String("name"))) {
0087                 // qDebug() << "set group to" << attrs.value(i);
0088                 group = attr.value().toString();
0089             }
0090         }
0091         if (group.isEmpty()) {
0092             group = d->baseGroup;
0093         } else {
0094             d->groups.append(group);
0095             if (!d->baseGroup.isEmpty()) {
0096                 group = d->baseGroup + QLatin1Char('\x1d') + group;
0097             }
0098         }
0099 
0100         if (m_config) {
0101             m_config->setCurrentGroup(group);
0102         }
0103     } else if (caseInsensitiveCompare(localName, QLatin1String("entry"))) {
0104         for (const auto &attr : attrs) {
0105             const auto attrName = attr.name();
0106             if (caseInsensitiveCompare(attrName, QLatin1String("name"))) {
0107                 m_name = attr.value().trimmed().toString();
0108             } else if (caseInsensitiveCompare(attrName, QLatin1String("type"))) {
0109                 m_type = attr.value().toString().toLower();
0110             } else if (caseInsensitiveCompare(attrName, QLatin1String("key"))) {
0111                 m_key = attr.value().trimmed().toString();
0112             }
0113         }
0114     } else if (caseInsensitiveCompare(localName, QLatin1String("choice"))) {
0115         m_choice.name.clear();
0116         m_choice.label.clear();
0117         m_choice.whatsThis.clear();
0118         for (const auto &attr : attrs) {
0119             const auto attrName = attr.name();
0120             if (caseInsensitiveCompare(attrName, QLatin1String("name"))) {
0121                 m_choice.name = attr.value().toString();
0122             }
0123         }
0124         m_inChoice = true;
0125     }
0126 }
0127 
0128 void ConfigLoaderHandler::endElement(const QStringView localName)
0129 {
0130     //     qDebug() << "ConfigLoaderHandler::endElement(" << localName << qName;
0131     if (caseInsensitiveCompare(localName, QLatin1String("entry"))) {
0132         addItem();
0133         resetState();
0134     } else if (caseInsensitiveCompare(localName, QLatin1String("label"))) {
0135         if (m_inChoice) {
0136             m_choice.label = m_cdata.trimmed();
0137         } else {
0138             m_label = m_cdata.trimmed();
0139         }
0140     } else if (caseInsensitiveCompare(localName, QLatin1String("whatsthis"))) {
0141         if (m_inChoice) {
0142             m_choice.whatsThis = m_cdata.trimmed();
0143         } else {
0144             m_whatsThis = m_cdata.trimmed();
0145         }
0146     } else if (caseInsensitiveCompare(localName, QLatin1String("default"))) {
0147         m_default = m_cdata.trimmed();
0148     } else if (caseInsensitiveCompare(localName, QLatin1String("min"))) {
0149         m_min = m_cdata.toInt(&m_haveMin);
0150     } else if (caseInsensitiveCompare(localName, QLatin1String("max"))) {
0151         m_max = m_cdata.toInt(&m_haveMax);
0152     } else if (caseInsensitiveCompare(localName, QLatin1String("choice"))) {
0153         m_enumChoices.append(m_choice);
0154         m_inChoice = false;
0155     }
0156 
0157     m_cdata.clear();
0158 }
0159 
0160 void ConfigLoaderHandler::addItem()
0161 {
0162     if (m_name.isEmpty()) {
0163         if (m_key.isEmpty()) {
0164             return;
0165         }
0166 
0167         m_name = m_key;
0168     }
0169 
0170     m_name.remove(QLatin1Char(' '));
0171 
0172     KConfigSkeletonItem *item = nullptr;
0173 
0174     if (m_type == QLatin1String("bool")) {
0175         const bool defaultValue = caseInsensitiveCompare(m_default, QLatin1String("true"));
0176         item = m_config->addItemBool(m_name, *d->newBool(), defaultValue, m_key);
0177     } else if (m_type == QLatin1String("color")) {
0178         item = m_config->addItemColor(m_name, *d->newColor(), QColor(m_default), m_key);
0179     } else if (m_type == QLatin1String("datetime")) {
0180         item = m_config->addItemDateTime(m_name, *d->newDateTime(), QDateTime::fromString(m_default), m_key);
0181     } else if (m_type == QLatin1String("enum")) {
0182         m_key = (m_key.isEmpty()) ? m_name : m_key;
0183 
0184         bool ok = false;
0185         int defaultValue = m_default.toInt(&ok);
0186         if (!ok) {
0187             for (int i = 0; i < m_enumChoices.size(); i++) {
0188                 if (m_default == m_enumChoices[i].name) {
0189                     defaultValue = i;
0190                     break;
0191                 }
0192             }
0193         }
0194 
0195         KConfigSkeleton::ItemEnum *enumItem = new KConfigSkeleton::ItemEnum(m_config->currentGroup(), m_key, *d->newInt(), m_enumChoices, defaultValue);
0196         m_config->addItem(enumItem, m_name);
0197         item = enumItem;
0198     } else if (m_type == QLatin1String("font")) {
0199         item = m_config->addItemFont(m_name, *d->newFont(), QFont(m_default), m_key);
0200     } else if (m_type == QLatin1String("int")) {
0201         KConfigSkeleton::ItemInt *intItem = m_config->addItemInt(m_name, *d->newInt(), m_default.toInt(), m_key);
0202 
0203         if (m_haveMin) {
0204             intItem->setMinValue(m_min);
0205         }
0206 
0207         if (m_haveMax) {
0208             intItem->setMaxValue(m_max);
0209         }
0210 
0211         item = intItem;
0212     } else if (m_type == QLatin1String("password")) {
0213         item = m_config->addItemPassword(m_name, *d->newString(), m_default, m_key);
0214     } else if (m_type == QLatin1String("path")) {
0215         item = m_config->addItemPath(m_name, *d->newString(), m_default, m_key);
0216     } else if (m_type == QLatin1String("string")) {
0217         item = m_config->addItemString(m_name, *d->newString(), m_default, m_key);
0218     } else if (m_type == QLatin1String("stringlist")) {
0219         // FIXME: the split() is naive and will break on lists with ,'s in them
0220         // empty parts are not wanted in this case
0221         item = m_config->addItemStringList(m_name, *d->newStringList(), m_default.split(QLatin1Char(','), Qt::SkipEmptyParts), m_key);
0222     } else if (m_type == QLatin1String("uint")) {
0223         KConfigSkeleton::ItemUInt *uintItem = m_config->addItemUInt(m_name, *d->newUint(), m_default.toUInt(), m_key);
0224         if (m_haveMin) {
0225             uintItem->setMinValue(m_min);
0226         }
0227         if (m_haveMax) {
0228             uintItem->setMaxValue(m_max);
0229         }
0230         item = uintItem;
0231     } else if (m_type == QLatin1String("url")) {
0232         m_key = (m_key.isEmpty()) ? m_name : m_key;
0233         KConfigSkeleton::ItemUrl *urlItem = new KConfigSkeleton::ItemUrl(m_config->currentGroup(), m_key, *d->newUrl(), QUrl::fromUserInput(m_default));
0234         m_config->addItem(urlItem, m_name);
0235         item = urlItem;
0236     } else if (m_type == QLatin1String("double")) {
0237         KConfigSkeleton::ItemDouble *doubleItem = m_config->addItemDouble(m_name, *d->newDouble(), m_default.toDouble(), m_key);
0238         if (m_haveMin) {
0239             doubleItem->setMinValue(m_min);
0240         }
0241         if (m_haveMax) {
0242             doubleItem->setMaxValue(m_max);
0243         }
0244         item = doubleItem;
0245     } else if (m_type == QLatin1String("intlist")) {
0246         QList<int> defaultList;
0247         const QStringList tmpList = m_default.split(QLatin1Char(','), Qt::SkipEmptyParts);
0248         for (const QString &tmp : tmpList) {
0249             defaultList.append(tmp.toInt());
0250         }
0251         item = m_config->addItemIntList(m_name, *d->newIntList(), defaultList, m_key);
0252     } else if (m_type == QLatin1String("longlong")) {
0253         KConfigSkeleton::ItemLongLong *longlongItem = m_config->addItemLongLong(m_name, *d->newLongLong(), m_default.toLongLong(), m_key);
0254         if (m_haveMin) {
0255             longlongItem->setMinValue(m_min);
0256         }
0257         if (m_haveMax) {
0258             longlongItem->setMaxValue(m_max);
0259         }
0260         item = longlongItem;
0261         /* No addItemPathList in KConfigSkeleton ?
0262         } else if (m_type == "PathList") {
0263             //FIXME: the split() is naive and will break on lists with ,'s in them
0264             item = m_config->addItemPathList(m_name, *d->newStringList(), m_default.split(","), m_key);
0265         */
0266     } else if (m_type == QLatin1String("point")) {
0267         QPoint defaultPoint;
0268         const QStringList tmpList = m_default.split(QLatin1Char(','));
0269         if (tmpList.size() >= 2) {
0270             defaultPoint.setX(tmpList[0].toInt());
0271             defaultPoint.setY(tmpList[1].toInt());
0272         }
0273         item = m_config->addItemPoint(m_name, *d->newPoint(), defaultPoint, m_key);
0274     } else if (m_type == QLatin1String("pointf")) {
0275         QPointF defaultPointF;
0276         const auto tmpList = QStringView(m_default).split(u',');
0277         if (tmpList.size() >= 2) {
0278             defaultPointF.setX(tmpList[0].toDouble());
0279             defaultPointF.setY(tmpList[1].toDouble());
0280         }
0281         item = m_config->addItemPointF(m_name, *d->newPointF(), defaultPointF, m_key);
0282     } else if (m_type == QLatin1String("rect")) {
0283         QRect defaultRect;
0284         const QStringList tmpList = m_default.split(QLatin1Char(','));
0285         if (tmpList.size() >= 4) {
0286             defaultRect.setCoords(tmpList[0].toInt(), tmpList[1].toInt(), tmpList[2].toInt(), tmpList[3].toInt());
0287         }
0288         item = m_config->addItemRect(m_name, *d->newRect(), defaultRect, m_key);
0289     } else if (m_type == QLatin1String("rectf")) {
0290         QRectF defaultRectF;
0291         const auto tmpList = QStringView(m_default).split(u',');
0292         if (tmpList.size() >= 4) {
0293             defaultRectF.setCoords(tmpList[0].toDouble(), tmpList[1].toDouble(), tmpList[2].toDouble(), tmpList[3].toDouble());
0294         }
0295         item = m_config->addItemRectF(m_name, *d->newRectF(), defaultRectF, m_key);
0296     } else if (m_type == QLatin1String("size")) {
0297         QSize defaultSize;
0298         const QStringList tmpList = m_default.split(QLatin1Char(','));
0299         if (tmpList.size() >= 2) {
0300             defaultSize.setWidth(tmpList[0].toInt());
0301             defaultSize.setHeight(tmpList[1].toInt());
0302         }
0303         item = m_config->addItemSize(m_name, *d->newSize(), defaultSize, m_key);
0304     } else if (m_type == QLatin1String("sizef")) {
0305         QSizeF defaultSizeF;
0306         const auto tmpList = QStringView(m_default).split(u',');
0307         if (tmpList.size() >= 2) {
0308             defaultSizeF.setWidth(tmpList[0].toDouble());
0309             defaultSizeF.setHeight(tmpList[1].toDouble());
0310         }
0311         item = m_config->addItemSizeF(m_name, *d->newSizeF(), defaultSizeF, m_key);
0312     } else if (m_type == QLatin1String("ulonglong")) {
0313         KConfigSkeleton::ItemULongLong *ulonglongItem = m_config->addItemULongLong(m_name, *d->newULongLong(), m_default.toULongLong(), m_key);
0314         if (m_haveMin) {
0315             ulonglongItem->setMinValue(m_min);
0316         }
0317         if (m_haveMax) {
0318             ulonglongItem->setMaxValue(m_max);
0319         }
0320         item = ulonglongItem;
0321         /* No addItemUrlList in KConfigSkeleton ?
0322         } else if (m_type == "urllist") {
0323             //FIXME: the split() is naive and will break on lists with ,'s in them
0324             QStringList tmpList = m_default.split(",");
0325             QList<QUrl> defaultList;
0326             foreach (const QString& tmp, tmpList) {
0327                 defaultList.append(QUrl(tmp));
0328             }
0329             item = m_config->addItemUrlList(m_name, *d->newUrlList(), defaultList, m_key);*/
0330     }
0331 
0332     if (item) {
0333         item->setLabel(m_label);
0334         item->setWhatsThis(m_whatsThis);
0335         d->keysToNames.insert(item->group() + item->key(), item->name());
0336     }
0337 }
0338 
0339 void ConfigLoaderHandler::resetState()
0340 {
0341     m_haveMin = false;
0342     m_min = 0;
0343     m_haveMax = false;
0344     m_max = 0;
0345     m_name.clear();
0346     m_type.clear();
0347     m_label.clear();
0348     m_default.clear();
0349     m_key.clear();
0350     m_whatsThis.clear();
0351     m_enumChoices.clear();
0352     m_inChoice = false;
0353 }
0354 
0355 KConfigLoader::KConfigLoader(const QString &configFile, QIODevice *xml, QObject *parent)
0356     : KConfigSkeleton(configFile, parent)
0357     , d(new ConfigLoaderPrivate)
0358 {
0359     d->parse(this, xml);
0360 }
0361 
0362 KConfigLoader::KConfigLoader(KSharedConfigPtr config, QIODevice *xml, QObject *parent)
0363     : KConfigSkeleton(std::move(config), parent)
0364     , d(new ConfigLoaderPrivate)
0365 {
0366     d->parse(this, xml);
0367 }
0368 
0369 // FIXME: obviously this is broken and should be using the group as the root,
0370 //       but KConfigSkeleton does not currently support this. it will eventually though,
0371 //       at which point this can be addressed properly
0372 KConfigLoader::KConfigLoader(const KConfigGroup &config, QIODevice *xml, QObject *parent)
0373     : KConfigSkeleton(KSharedConfig::openConfig(config.config()->name(), config.config()->openFlags(), config.config()->locationType()), parent)
0374     , d(new ConfigLoaderPrivate)
0375 {
0376     KConfigGroup group = config.parent();
0377     d->baseGroup = config.name();
0378     while (group.isValid() && group.name() != QLatin1String("<default>")) {
0379         d->baseGroup = group.name() + QLatin1Char('\x1d') + d->baseGroup;
0380         group = group.parent();
0381     }
0382     d->parse(this, xml);
0383 }
0384 
0385 KConfigLoader::~KConfigLoader()
0386 {
0387     delete d;
0388 }
0389 
0390 KConfigSkeletonItem *KConfigLoader::findItem(const QString &group, const QString &key) const
0391 {
0392     return KConfigSkeleton::findItem(d->keysToNames[group + key]);
0393 }
0394 
0395 KConfigSkeletonItem *KConfigLoader::findItemByName(const QString &name) const
0396 {
0397     return KConfigSkeleton::findItem(name);
0398 }
0399 
0400 QVariant KConfigLoader::property(const QString &name) const
0401 {
0402     KConfigSkeletonItem *item = KConfigSkeleton::findItem(name);
0403 
0404     if (item) {
0405         return item->property();
0406     }
0407 
0408     return QVariant();
0409 }
0410 
0411 bool KConfigLoader::hasGroup(const QString &group) const
0412 {
0413     return d->groups.contains(group);
0414 }
0415 
0416 QStringList KConfigLoader::groupList() const
0417 {
0418     return d->groups;
0419 }
0420 
0421 bool KConfigLoader::usrSave()
0422 {
0423     if (d->saveDefaults) {
0424         const auto listItems = items();
0425         for (const auto &item : listItems) {
0426             config()->group(item->group()).writeEntry(item->key(), "");
0427         }
0428     }
0429     return true;
0430 }