File indexing completed on 2024-05-12 15:34:13
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 KConfigSkeleton::ItemEnum *enumItem = new KConfigSkeleton::ItemEnum(m_config->currentGroup(), m_key, *d->newInt(), m_enumChoices, m_default.toUInt()); 0184 m_config->addItem(enumItem, m_name); 0185 item = enumItem; 0186 } else if (m_type == QLatin1String("font")) { 0187 item = m_config->addItemFont(m_name, *d->newFont(), QFont(m_default), m_key); 0188 } else if (m_type == QLatin1String("int")) { 0189 KConfigSkeleton::ItemInt *intItem = m_config->addItemInt(m_name, *d->newInt(), m_default.toInt(), m_key); 0190 0191 if (m_haveMin) { 0192 intItem->setMinValue(m_min); 0193 } 0194 0195 if (m_haveMax) { 0196 intItem->setMaxValue(m_max); 0197 } 0198 0199 item = intItem; 0200 } else if (m_type == QLatin1String("password")) { 0201 item = m_config->addItemPassword(m_name, *d->newString(), m_default, m_key); 0202 } else if (m_type == QLatin1String("path")) { 0203 item = m_config->addItemPath(m_name, *d->newString(), m_default, m_key); 0204 } else if (m_type == QLatin1String("string")) { 0205 item = m_config->addItemString(m_name, *d->newString(), m_default, m_key); 0206 } else if (m_type == QLatin1String("stringlist")) { 0207 // FIXME: the split() is naive and will break on lists with ,'s in them 0208 // empty parts are not wanted in this case 0209 item = m_config->addItemStringList(m_name, *d->newStringList(), m_default.split(QLatin1Char(','), Qt::SkipEmptyParts), m_key); 0210 } else if (m_type == QLatin1String("uint")) { 0211 KConfigSkeleton::ItemUInt *uintItem = m_config->addItemUInt(m_name, *d->newUint(), m_default.toUInt(), m_key); 0212 if (m_haveMin) { 0213 uintItem->setMinValue(m_min); 0214 } 0215 if (m_haveMax) { 0216 uintItem->setMaxValue(m_max); 0217 } 0218 item = uintItem; 0219 } else if (m_type == QLatin1String("url")) { 0220 m_key = (m_key.isEmpty()) ? m_name : m_key; 0221 KConfigSkeleton::ItemUrl *urlItem = new KConfigSkeleton::ItemUrl(m_config->currentGroup(), m_key, *d->newUrl(), QUrl::fromUserInput(m_default)); 0222 m_config->addItem(urlItem, m_name); 0223 item = urlItem; 0224 } else if (m_type == QLatin1String("double")) { 0225 KConfigSkeleton::ItemDouble *doubleItem = m_config->addItemDouble(m_name, *d->newDouble(), m_default.toDouble(), m_key); 0226 if (m_haveMin) { 0227 doubleItem->setMinValue(m_min); 0228 } 0229 if (m_haveMax) { 0230 doubleItem->setMaxValue(m_max); 0231 } 0232 item = doubleItem; 0233 } else if (m_type == QLatin1String("intlist")) { 0234 QList<int> defaultList; 0235 const QStringList tmpList = m_default.split(QLatin1Char(','), Qt::SkipEmptyParts); 0236 for (const QString &tmp : tmpList) { 0237 defaultList.append(tmp.toInt()); 0238 } 0239 item = m_config->addItemIntList(m_name, *d->newIntList(), defaultList, m_key); 0240 } else if (m_type == QLatin1String("longlong")) { 0241 KConfigSkeleton::ItemLongLong *longlongItem = m_config->addItemLongLong(m_name, *d->newLongLong(), m_default.toLongLong(), m_key); 0242 if (m_haveMin) { 0243 longlongItem->setMinValue(m_min); 0244 } 0245 if (m_haveMax) { 0246 longlongItem->setMaxValue(m_max); 0247 } 0248 item = longlongItem; 0249 /* No addItemPathList in KConfigSkeleton ? 0250 } else if (m_type == "PathList") { 0251 //FIXME: the split() is naive and will break on lists with ,'s in them 0252 item = m_config->addItemPathList(m_name, *d->newStringList(), m_default.split(","), m_key); 0253 */ 0254 } else if (m_type == QLatin1String("point")) { 0255 QPoint defaultPoint; 0256 const QStringList tmpList = m_default.split(QLatin1Char(',')); 0257 if (tmpList.size() >= 2) { 0258 defaultPoint.setX(tmpList[0].toInt()); 0259 defaultPoint.setY(tmpList[1].toInt()); 0260 } 0261 item = m_config->addItemPoint(m_name, *d->newPoint(), defaultPoint, m_key); 0262 } else if (m_type == QLatin1String("rect")) { 0263 QRect defaultRect; 0264 const QStringList tmpList = m_default.split(QLatin1Char(',')); 0265 if (tmpList.size() >= 4) { 0266 defaultRect.setCoords(tmpList[0].toInt(), tmpList[1].toInt(), tmpList[2].toInt(), tmpList[3].toInt()); 0267 } 0268 item = m_config->addItemRect(m_name, *d->newRect(), defaultRect, m_key); 0269 } else if (m_type == QLatin1String("size")) { 0270 QSize defaultSize; 0271 const QStringList tmpList = m_default.split(QLatin1Char(',')); 0272 if (tmpList.size() >= 2) { 0273 defaultSize.setWidth(tmpList[0].toInt()); 0274 defaultSize.setHeight(tmpList[1].toInt()); 0275 } 0276 item = m_config->addItemSize(m_name, *d->newSize(), defaultSize, m_key); 0277 } else if (m_type == QLatin1String("ulonglong")) { 0278 KConfigSkeleton::ItemULongLong *ulonglongItem = m_config->addItemULongLong(m_name, *d->newULongLong(), m_default.toULongLong(), m_key); 0279 if (m_haveMin) { 0280 ulonglongItem->setMinValue(m_min); 0281 } 0282 if (m_haveMax) { 0283 ulonglongItem->setMaxValue(m_max); 0284 } 0285 item = ulonglongItem; 0286 /* No addItemUrlList in KConfigSkeleton ? 0287 } else if (m_type == "urllist") { 0288 //FIXME: the split() is naive and will break on lists with ,'s in them 0289 QStringList tmpList = m_default.split(","); 0290 QList<QUrl> defaultList; 0291 foreach (const QString& tmp, tmpList) { 0292 defaultList.append(QUrl(tmp)); 0293 } 0294 item = m_config->addItemUrlList(m_name, *d->newUrlList(), defaultList, m_key);*/ 0295 } 0296 0297 if (item) { 0298 item->setLabel(m_label); 0299 item->setWhatsThis(m_whatsThis); 0300 d->keysToNames.insert(item->group() + item->key(), item->name()); 0301 } 0302 } 0303 0304 void ConfigLoaderHandler::resetState() 0305 { 0306 m_haveMin = false; 0307 m_min = 0; 0308 m_haveMax = false; 0309 m_max = 0; 0310 m_name.clear(); 0311 m_type.clear(); 0312 m_label.clear(); 0313 m_default.clear(); 0314 m_key.clear(); 0315 m_whatsThis.clear(); 0316 m_enumChoices.clear(); 0317 m_inChoice = false; 0318 } 0319 0320 KConfigLoader::KConfigLoader(const QString &configFile, QIODevice *xml, QObject *parent) 0321 : KConfigSkeleton(configFile, parent) 0322 , d(new ConfigLoaderPrivate) 0323 { 0324 d->parse(this, xml); 0325 } 0326 0327 KConfigLoader::KConfigLoader(KSharedConfigPtr config, QIODevice *xml, QObject *parent) 0328 : KConfigSkeleton(std::move(config), parent) 0329 , d(new ConfigLoaderPrivate) 0330 { 0331 d->parse(this, xml); 0332 } 0333 0334 // FIXME: obviously this is broken and should be using the group as the root, 0335 // but KConfigSkeleton does not currently support this. it will eventually though, 0336 // at which point this can be addressed properly 0337 KConfigLoader::KConfigLoader(const KConfigGroup &config, QIODevice *xml, QObject *parent) 0338 : KConfigSkeleton(KSharedConfig::openConfig(config.config()->name(), config.config()->openFlags(), config.config()->locationType()), parent) 0339 , d(new ConfigLoaderPrivate) 0340 { 0341 KConfigGroup group = config.parent(); 0342 d->baseGroup = config.name(); 0343 while (group.isValid() && group.name() != QLatin1String("<default>")) { 0344 d->baseGroup = group.name() + QLatin1Char('\x1d') + d->baseGroup; 0345 group = group.parent(); 0346 } 0347 d->parse(this, xml); 0348 } 0349 0350 KConfigLoader::~KConfigLoader() 0351 { 0352 delete d; 0353 } 0354 0355 KConfigSkeletonItem *KConfigLoader::findItem(const QString &group, const QString &key) const 0356 { 0357 return KConfigSkeleton::findItem(d->keysToNames[group + key]); 0358 } 0359 0360 KConfigSkeletonItem *KConfigLoader::findItemByName(const QString &name) const 0361 { 0362 return KConfigSkeleton::findItem(name); 0363 } 0364 0365 QVariant KConfigLoader::property(const QString &name) const 0366 { 0367 KConfigSkeletonItem *item = KConfigSkeleton::findItem(name); 0368 0369 if (item) { 0370 return item->property(); 0371 } 0372 0373 return QVariant(); 0374 } 0375 0376 bool KConfigLoader::hasGroup(const QString &group) const 0377 { 0378 return d->groups.contains(group); 0379 } 0380 0381 QStringList KConfigLoader::groupList() const 0382 { 0383 return d->groups; 0384 } 0385 0386 #if KCONFIGCORE_BUILD_DEPRECATED_SINCE(5, 0) 0387 bool KConfigLoader::usrWriteConfig() 0388 #else 0389 bool KConfigLoader::usrSave() 0390 #endif 0391 { 0392 if (d->saveDefaults) { 0393 const auto listItems = items(); 0394 for (const auto &item : listItems) { 0395 config()->group(item->group()).writeEntry(item->key(), ""); 0396 } 0397 } 0398 return true; 0399 }