1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2007 Thiago Macieira <thiago@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "kconfiggui_export.h"
9#include <kconfiggroup.h>
10
11#include <QColor>
12#include <QDebug>
13#include <QFont>
14
15#include <kconfiggroup_p.h>
16
17/**
18 * Try to read a GUI type from config group @p cg at key @p key.
19 * @p input is the default value and also indicates the type to be read.
20 * @p output is to be set with the value that has been read.
21 *
22 * @returns true if something was handled (even if output was set to clear or default)
23 * or false if nothing was handled (e.g., Core type)
24 */
25static bool readEntryGui(const QByteArray &data, const char *key, const QVariant &input, QVariant &output)
26{
27 const auto errString = [&]() {
28 return QStringLiteral("\"%1\" - conversion from \"%3\" to %2 failed")
29 .arg(args: QLatin1String(key), args: QLatin1String(input.typeName()), args: QLatin1String(data.constData()));
30 };
31
32 // set in case of failure
33 output = input;
34
35 switch (static_cast<QMetaType::Type>(input.userType())) {
36 case QMetaType::QColor: {
37 if (data.isEmpty() || data == "invalid") {
38 output = QColor(); // return what was stored
39 return true;
40 } else if (data.at(i: 0) == '#') {
41 QColor col = QColor::fromString(name: QUtf8StringView(data.constData(), data.length()));
42 if (!col.isValid()) {
43 qCritical() << qPrintable(errString());
44 }
45 output = col;
46 return true;
47 } else if (!data.contains(c: ',')) {
48 QColor col = QColor::fromString(name: QUtf8StringView(data.constData(), data.length()));
49 if (!col.isValid()) {
50 qCritical() << qPrintable(errString());
51 }
52 output = col;
53 return true;
54 } else {
55 const QList<QByteArray> list = data.split(sep: ',');
56 const int count = list.count();
57
58 if (count != 3 && count != 4) {
59 qCritical() //
60 << qPrintable(errString()) //
61 << qPrintable(QStringLiteral(" (wrong format: expected '%1' items, read '%2')").arg(QStringLiteral("3' or '4")).arg(count));
62 return true; // return default
63 }
64
65 int temp[4];
66 // bounds check components
67 for (int i = 0; i < count; i++) {
68 bool ok;
69 const int j = temp[i] = list.at(i).toInt(ok: &ok);
70 if (!ok) { // failed to convert to int
71 qCritical() << qPrintable(errString()) << " (integer conversion failed)";
72 return true; // return default
73 }
74 if (j < 0 || j > 255) {
75 static const char *const components[] = {"red", "green", "blue", "alpha"};
76 qCritical() << qPrintable(errString())
77 << qPrintable(QStringLiteral(" (bounds error: %1 component %2)")
78 .arg(QLatin1String(components[i]), //
79 j < 0 ? QStringLiteral("< 0") : QStringLiteral("> 255")));
80 return true; // return default
81 }
82 }
83 QColor aColor(temp[0], temp[1], temp[2]);
84 if (count == 4) {
85 aColor.setAlpha(temp[3]);
86 }
87
88 if (aColor.isValid()) {
89 output = aColor;
90 } else {
91 qCritical() << qPrintable(errString());
92 }
93 return true;
94 }
95 }
96
97 case QMetaType::QFont: {
98 QVariant tmp = QString::fromUtf8(utf8: data.constData(), size: data.length());
99 if (tmp.canConvert<QFont>()) {
100 output = tmp;
101 } else {
102 qCritical() << qPrintable(errString());
103 }
104 return true;
105 }
106 case QMetaType::QPixmap:
107 case QMetaType::QImage:
108 case QMetaType::QBrush:
109 case QMetaType::QPalette:
110 case QMetaType::QIcon:
111 case QMetaType::QRegion:
112 case QMetaType::QBitmap:
113 case QMetaType::QCursor:
114 case QMetaType::QSizePolicy:
115 case QMetaType::QPen:
116 // we may want to handle these in the future
117
118 default:
119 break;
120 }
121
122 return false; // not handled
123}
124
125/**
126 * Try to write a GUI type @p prop to config group @p cg at key @p key.
127 *
128 * @returns true if something was handled (even if an empty value was written)
129 * or false if nothing was handled (e.g., Core type)
130 */
131static bool writeEntryGui(KConfigGroup *cg, const char *key, const QVariant &prop, KConfigGroup::WriteConfigFlags pFlags)
132{
133 switch (static_cast<QMetaType::Type>(prop.userType())) {
134 case QMetaType::QColor: {
135 const QColor rColor = prop.value<QColor>();
136
137 if (!rColor.isValid()) {
138 cg->writeEntry(key, value: "invalid", pFlags);
139 return true;
140 }
141
142 QList<int> list;
143 list.insert(i: 0, t: rColor.red());
144 list.insert(i: 1, t: rColor.green());
145 list.insert(i: 2, t: rColor.blue());
146 if (rColor.alpha() != 255) {
147 list.insert(i: 3, t: rColor.alpha());
148 }
149
150 cg->writeEntry(key, list, pFlags);
151 return true;
152 }
153 case QMetaType::QFont: {
154 QFont f = prop.value<QFont>();
155 // If the styleName property is set for a QFont, using setBold(true) would
156 // lead to Qt using an "emboldended"/synthetic font style instead of using
157 // the bold style provided by the font itself; the latter looks much better
158 // than the former. For more details see:
159 // https://bugreports.qt.io/browse/QTBUG-63792
160 // https://bugs.kde.org/show_bug.cgi?id=378523
161 /* clang-format off */
162 if (f.weight() == QFont::Normal
163 && (f.styleName() == QLatin1String("Regular")
164 || f.styleName() == QLatin1String("Normal")
165 || f.styleName() == QLatin1String("Book")
166 || f.styleName() == QLatin1String("Roman"))) { /* clang-format on */
167 f.setStyleName(QString());
168 }
169 cg->writeEntry(key, value: f.toString().toUtf8(), pFlags);
170 return true;
171 }
172 case QMetaType::QPixmap:
173 case QMetaType::QImage:
174 case QMetaType::QBrush:
175 case QMetaType::QPalette:
176 case QMetaType::QIcon:
177 case QMetaType::QRegion:
178 case QMetaType::QBitmap:
179 case QMetaType::QCursor:
180 case QMetaType::QSizePolicy:
181 case QMetaType::QPen:
182 // we may want to handle one of these in the future
183 break;
184
185 default:
186 break;
187 }
188
189 return false;
190}
191
192// Not static, because for static builds we use it in the kconfigguistaticinitializer.cpp file
193// Exported as we need this to force linking in consumers that read GUI types from KConfig, but
194// which are otherwise not using this library (and thus linking with --as-needed or MSVC would
195// break things)
196KCONFIGGUI_EXPORT int initKConfigGroupGui()
197{
198 _kde_internal_KConfigGroupGui.readEntryGui = readEntryGui;
199 _kde_internal_KConfigGroupGui.writeEntryGui = writeEntryGui;
200 return 42; // because 42 is nicer than 1 or 0
201}
202
203#ifdef Q_CONSTRUCTOR_FUNCTION
204Q_CONSTRUCTOR_FUNCTION(initKConfigGroupGui)
205#else
206static int dummyKConfigGroupGui = initKConfigGroupGui();
207#endif
208

source code of kconfig/src/gui/kconfiggroupgui.cpp