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 }