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