File indexing completed on 2024-05-12 03:54:27
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2007 Thiago Macieira <thiago@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kconfiggui_export.h" 0009 #include <kconfiggroup.h> 0010 0011 #include <QColor> 0012 #include <QDebug> 0013 #include <QFont> 0014 0015 #include <kconfiggroup_p.h> 0016 0017 /** 0018 * Try to read a GUI type from config group @p cg at key @p key. 0019 * @p input is the default value and also indicates the type to be read. 0020 * @p output is to be set with the value that has been read. 0021 * 0022 * @returns true if something was handled (even if output was set to clear or default) 0023 * or false if nothing was handled (e.g., Core type) 0024 */ 0025 static bool readEntryGui(const QByteArray &data, const char *key, const QVariant &input, QVariant &output) 0026 { 0027 const auto errString = [&]() { 0028 return QStringLiteral("\"%1\" - conversion from \"%3\" to %2 failed") 0029 .arg(QLatin1String(key), QLatin1String(input.typeName()), QLatin1String(data.constData())); 0030 }; 0031 0032 // set in case of failure 0033 output = input; 0034 0035 switch (static_cast<QMetaType::Type>(input.userType())) { 0036 case QMetaType::QColor: { 0037 if (data.isEmpty() || data == "invalid") { 0038 output = QColor(); // return what was stored 0039 return true; 0040 } else if (data.at(0) == '#') { 0041 QColor col = QColor::fromString(QUtf8StringView(data.constData(), data.length())); 0042 if (!col.isValid()) { 0043 qCritical() << qPrintable(errString()); 0044 } 0045 output = col; 0046 return true; 0047 } else if (!data.contains(',')) { 0048 QColor col = QColor::fromString(QUtf8StringView(data.constData(), data.length())); 0049 if (!col.isValid()) { 0050 qCritical() << qPrintable(errString()); 0051 } 0052 output = col; 0053 return true; 0054 } else { 0055 const QList<QByteArray> list = data.split(','); 0056 const int count = list.count(); 0057 0058 if (count != 3 && count != 4) { 0059 qCritical() // 0060 << qPrintable(errString()) // 0061 << qPrintable(QStringLiteral(" (wrong format: expected '%1' items, read '%2')").arg(QStringLiteral("3' or '4")).arg(count)); 0062 return true; // return default 0063 } 0064 0065 int temp[4]; 0066 // bounds check components 0067 for (int i = 0; i < count; i++) { 0068 bool ok; 0069 const int j = temp[i] = list.at(i).toInt(&ok); 0070 if (!ok) { // failed to convert to int 0071 qCritical() << qPrintable(errString()) << " (integer conversion failed)"; 0072 return true; // return default 0073 } 0074 if (j < 0 || j > 255) { 0075 static const char *const components[] = {"red", "green", "blue", "alpha"}; 0076 qCritical() << qPrintable(errString()) 0077 << qPrintable(QStringLiteral(" (bounds error: %1 component %2)") 0078 .arg(QLatin1String(components[i]), // 0079 j < 0 ? QStringLiteral("< 0") : QStringLiteral("> 255"))); 0080 return true; // return default 0081 } 0082 } 0083 QColor aColor(temp[0], temp[1], temp[2]); 0084 if (count == 4) { 0085 aColor.setAlpha(temp[3]); 0086 } 0087 0088 if (aColor.isValid()) { 0089 output = aColor; 0090 } else { 0091 qCritical() << qPrintable(errString()); 0092 } 0093 return true; 0094 } 0095 } 0096 0097 case QMetaType::QFont: { 0098 QVariant tmp = QString::fromUtf8(data.constData(), data.length()); 0099 if (tmp.canConvert<QFont>()) { 0100 output = tmp; 0101 } else { 0102 qCritical() << qPrintable(errString()); 0103 } 0104 return true; 0105 } 0106 case QMetaType::QPixmap: 0107 case QMetaType::QImage: 0108 case QMetaType::QBrush: 0109 case QMetaType::QPalette: 0110 case QMetaType::QIcon: 0111 case QMetaType::QRegion: 0112 case QMetaType::QBitmap: 0113 case QMetaType::QCursor: 0114 case QMetaType::QSizePolicy: 0115 case QMetaType::QPen: 0116 // we may want to handle these in the future 0117 0118 default: 0119 break; 0120 } 0121 0122 return false; // not handled 0123 } 0124 0125 /** 0126 * Try to write a GUI type @p prop to config group @p cg at key @p key. 0127 * 0128 * @returns true if something was handled (even if an empty value was written) 0129 * or false if nothing was handled (e.g., Core type) 0130 */ 0131 static bool writeEntryGui(KConfigGroup *cg, const char *key, const QVariant &prop, KConfigGroup::WriteConfigFlags pFlags) 0132 { 0133 switch (static_cast<QMetaType::Type>(prop.userType())) { 0134 case QMetaType::QColor: { 0135 const QColor rColor = prop.value<QColor>(); 0136 0137 if (!rColor.isValid()) { 0138 cg->writeEntry(key, "invalid", pFlags); 0139 return true; 0140 } 0141 0142 QList<int> list; 0143 list.insert(0, rColor.red()); 0144 list.insert(1, rColor.green()); 0145 list.insert(2, rColor.blue()); 0146 if (rColor.alpha() != 255) { 0147 list.insert(3, rColor.alpha()); 0148 } 0149 0150 cg->writeEntry(key, list, pFlags); 0151 return true; 0152 } 0153 case QMetaType::QFont: { 0154 QFont f = prop.value<QFont>(); 0155 // If the styleName property is set for a QFont, using setBold(true) would 0156 // lead to Qt using an "emboldended"/synthetic font style instead of using 0157 // the bold style provided by the font itself; the latter looks much better 0158 // than the former. For more details see: 0159 // https://bugreports.qt.io/browse/QTBUG-63792 0160 // https://bugs.kde.org/show_bug.cgi?id=378523 0161 /* clang-format off */ 0162 if (f.weight() == QFont::Normal 0163 && (f.styleName() == QLatin1String("Regular") 0164 || f.styleName() == QLatin1String("Normal") 0165 || f.styleName() == QLatin1String("Book") 0166 || f.styleName() == QLatin1String("Roman"))) { /* clang-format on */ 0167 f.setStyleName(QString()); 0168 } 0169 cg->writeEntry(key, f.toString().toUtf8(), pFlags); 0170 return true; 0171 } 0172 case QMetaType::QPixmap: 0173 case QMetaType::QImage: 0174 case QMetaType::QBrush: 0175 case QMetaType::QPalette: 0176 case QMetaType::QIcon: 0177 case QMetaType::QRegion: 0178 case QMetaType::QBitmap: 0179 case QMetaType::QCursor: 0180 case QMetaType::QSizePolicy: 0181 case QMetaType::QPen: 0182 // we may want to handle one of these in the future 0183 break; 0184 0185 default: 0186 break; 0187 } 0188 0189 return false; 0190 } 0191 0192 // Not static, because for static builds we use it in the kconfigguistaticinitializer.cpp file 0193 // Exported as we need this to force linking in consumers that read GUI types from KConfig, but 0194 // which are otherwise not using this library (and thus linking with --as-needed or MSVC would 0195 // break things) 0196 KCONFIGGUI_EXPORT int initKConfigGroupGui() 0197 { 0198 _kde_internal_KConfigGroupGui.readEntryGui = readEntryGui; 0199 _kde_internal_KConfigGroupGui.writeEntryGui = writeEntryGui; 0200 return 42; // because 42 is nicer than 1 or 0 0201 } 0202 0203 #ifdef Q_CONSTRUCTOR_FUNCTION 0204 Q_CONSTRUCTOR_FUNCTION(initKConfigGroupGui) 0205 #else 0206 static int dummyKConfigGroupGui = initKConfigGroupGui(); 0207 #endif