File indexing completed on 2024-05-12 15:34:13

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