1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4//
5// W A R N I N G
6// -------------
7//
8// This file is not part of the Qt API. It exists purely as an
9// implementation detail. This header file may change from version to
10// version without notice, or even be removed.
11//
12// We mean it.
13//
14
15#include "qgtk3json_p.h"
16#include <QtCore/QFile>
17#include <QMetaEnum>
18
19QT_BEGIN_NAMESPACE
20
21QLatin1String QGtk3Json::fromPalette(QPlatformTheme::Palette palette)
22{
23 return QLatin1String(QMetaEnum::fromType<QPlatformTheme::Palette>().valueToKey(value: static_cast<int>(palette)));
24}
25
26QLatin1String QGtk3Json::fromGtkState(GtkStateFlags state)
27{
28 return QGtk3Interface::fromGtkState(state);
29}
30
31QLatin1String fromColor(const QColor &color)
32{
33 return QLatin1String(QByteArray(color.name(format: QColor::HexRgb).toLatin1()));
34}
35
36QLatin1String QGtk3Json::fromColorRole(QPalette::ColorRole role)
37{
38 return QLatin1String(QMetaEnum::fromType<QPalette::ColorRole>().valueToKey(value: static_cast<int>(role)));
39}
40
41QLatin1String QGtk3Json::fromColorGroup(QPalette::ColorGroup group)
42{
43 return QLatin1String(QMetaEnum::fromType<QPalette::ColorGroup>().valueToKey(value: static_cast<int>(group)));
44}
45
46QLatin1String QGtk3Json::fromGdkSource(QGtk3Interface::QGtkColorSource source)
47{
48 return QLatin1String(QMetaEnum::fromType<QGtk3Interface::QGtkColorSource>().valueToKey(value: static_cast<int>(source)));
49}
50
51QLatin1String QGtk3Json::fromWidgetType(QGtk3Interface::QGtkWidget widgetType)
52{
53 return QLatin1String(QMetaEnum::fromType<QGtk3Interface::QGtkWidget>().valueToKey(value: static_cast<int>(widgetType)));
54}
55
56QLatin1String QGtk3Json::fromColorScheme(Qt::ColorScheme app)
57{
58 return QLatin1String(QMetaEnum::fromType<Qt::ColorScheme>().valueToKey(value: static_cast<int>(app)));
59}
60
61#define CONVERT(type, key, def)\
62 bool ok;\
63 const int intVal = QMetaEnum::fromType<type>().keyToValue(key.toLatin1().constData(), &ok);\
64 return ok ? static_cast<type>(intVal) : type::def
65
66Qt::ColorScheme QGtk3Json::toColorScheme(const QString &colorScheme)
67{
68 CONVERT(Qt::ColorScheme, colorScheme, Unknown);
69}
70
71QPlatformTheme::Palette QGtk3Json::toPalette(const QString &palette)
72{
73 CONVERT(QPlatformTheme::Palette, palette, NPalettes);
74}
75
76GtkStateFlags QGtk3Json::toGtkState(const QString &type)
77{
78 int i = QGtk3Interface::toGtkState(state: type);
79 if (i < 0)
80 return GTK_STATE_FLAG_NORMAL;
81 return static_cast<GtkStateFlags>(i);
82}
83
84QColor toColor(const QStringView &color)
85{
86 return QColor::fromString(name: color);
87}
88
89QPalette::ColorRole QGtk3Json::toColorRole(const QString &role)
90{
91 CONVERT(QPalette::ColorRole, role, NColorRoles);
92}
93
94QPalette::ColorGroup QGtk3Json::toColorGroup(const QString &group)
95{
96 CONVERT(QPalette::ColorGroup, group, NColorGroups);
97}
98
99QGtk3Interface::QGtkColorSource QGtk3Json::toGdkSource(const QString &source)
100{
101 CONVERT(QGtk3Interface::QGtkColorSource, source, Background);
102}
103
104QLatin1String QGtk3Json::fromSourceType(QGtk3Storage::SourceType sourceType)
105{
106 return QLatin1String(QMetaEnum::fromType<QGtk3Storage::SourceType>().valueToKey(value: static_cast<int>(sourceType)));
107}
108
109QGtk3Storage::SourceType QGtk3Json::toSourceType(const QString &sourceType)
110{
111 CONVERT(QGtk3Storage::SourceType, sourceType, Invalid);
112}
113
114QGtk3Interface::QGtkWidget QGtk3Json::toWidgetType(const QString &widgetType)
115{
116 CONVERT(QGtk3Interface::QGtkWidget, widgetType, gtk_offscreen_window);
117}
118
119#undef CONVERT
120
121bool QGtk3Json::save(const QGtk3Storage::PaletteMap &map, const QString &fileName,
122 QJsonDocument::JsonFormat format)
123{
124 QJsonDocument doc = save(map);
125 if (doc.isEmpty()) {
126 qWarning() << "Nothing to save to" << fileName;
127 return false;
128 }
129
130 QFile file(fileName);
131 if (!file.open(flags: QIODevice::WriteOnly)) {
132 qWarning() << "Unable to open file" << fileName << "for writing.";
133 return false;
134 }
135
136 if (!file.write(data: doc.toJson(format))) {
137 qWarning() << "Unable to serialize Json document.";
138 return false;
139 }
140
141 file.close();
142 qInfo() << "Saved mapping data to" << fileName;
143 return true;
144}
145
146const QJsonDocument QGtk3Json::save(const QGtk3Storage::PaletteMap &map)
147{
148 QJsonObject paletteObject;
149 for (auto paletteIterator = map.constBegin(); paletteIterator != map.constEnd();
150 ++paletteIterator) {
151 const QGtk3Storage::BrushMap &bm = paletteIterator.value();
152 QFlatMap<QPalette::ColorRole, QGtk3Storage::BrushMap> brushMaps;
153 for (auto brushIterator = bm.constBegin(); brushIterator != bm.constEnd();
154 ++brushIterator) {
155 const QPalette::ColorRole role = brushIterator.key().colorRole;
156 if (brushMaps.contains(key: role)) {
157 brushMaps.value(key: role).insert(key: brushIterator.key(), value: brushIterator.value());
158 } else {
159 QGtk3Storage::BrushMap newMap;
160 newMap.insert(key: brushIterator.key(), value: brushIterator.value());
161 brushMaps.insert(key: role, value: newMap);
162 }
163 }
164
165 QJsonObject brushArrayObject;
166 for (auto brushMapIterator = brushMaps.constBegin();
167 brushMapIterator != brushMaps.constEnd(); ++brushMapIterator) {
168
169 QJsonArray brushArray;
170 int brushIndex = 0;
171 const QGtk3Storage::BrushMap &bm = brushMapIterator.value();
172 for (auto brushIterator = bm.constBegin(); brushIterator != bm.constEnd();
173 ++brushIterator) {
174 QJsonObject brushObject;
175 const QGtk3Storage::TargetBrush tb = brushIterator.key();
176 QGtk3Storage::Source s = brushIterator.value();
177 brushObject.insert(key: ceColorGroup, value: fromColorGroup(group: tb.colorGroup));
178 brushObject.insert(key: ceColorScheme, value: fromColorScheme(app: tb.colorScheme));
179 brushObject.insert(key: ceSourceType, value: fromSourceType(sourceType: s.sourceType));
180
181 QJsonObject sourceObject;
182 switch (s.sourceType) {
183 case QGtk3Storage::SourceType::Gtk: {
184 sourceObject.insert(key: ceGtkWidget, value: fromWidgetType(widgetType: s.gtk3.gtkWidgetType));
185 sourceObject.insert(key: ceGdkSource, value: fromGdkSource(source: s.gtk3.source));
186 sourceObject.insert(key: ceGtkState, value: fromGtkState(state: s.gtk3.state));
187 sourceObject.insert(key: ceWidth, value: s.gtk3.width);
188 sourceObject.insert(key: ceHeight, value: s.gtk3.height);
189 }
190 break;
191
192 case QGtk3Storage::SourceType::Fixed: {
193 QJsonObject fixedObject;
194 fixedObject.insert(key: ceColor, value: s.fix.fixedBrush.color().name());
195 fixedObject.insert(key: ceWidth, value: s.fix.fixedBrush.texture().width());
196 fixedObject.insert(key: ceHeight, value: s.fix.fixedBrush.texture().height());
197 sourceObject.insert(key: ceBrush, value: fixedObject);
198 }
199 break;
200
201 case QGtk3Storage::SourceType::Modified:{
202 sourceObject.insert(key: ceColorGroup, value: fromColorGroup(group: s.rec.colorGroup));
203 sourceObject.insert(key: ceColorRole, value: fromColorRole(role: s.rec.colorRole));
204 sourceObject.insert(key: ceColorScheme, value: fromColorScheme(app: s.rec.colorScheme));
205 sourceObject.insert(key: ceRed, value: s.rec.deltaRed);
206 sourceObject.insert(key: ceGreen, value: s.rec.deltaGreen);
207 sourceObject.insert(key: ceBlue, value: s.rec.deltaBlue);
208 sourceObject.insert(key: ceWidth, value: s.rec.width);
209 sourceObject.insert(key: ceHeight, value: s.rec.height);
210 sourceObject.insert(key: ceLighter, value: s.rec.lighter);
211 }
212 break;
213
214 case QGtk3Storage::SourceType::Invalid:
215 break;
216 }
217
218 brushObject.insert(key: ceData, value: sourceObject);
219 brushArray.insert(i: brushIndex, value: brushObject);
220 ++brushIndex;
221 }
222 brushArrayObject.insert(key: fromColorRole(role: brushMapIterator.key()), value: brushArray);
223 }
224 paletteObject.insert(key: fromPalette(palette: paletteIterator.key()), value: brushArrayObject);
225 }
226
227 QJsonObject top;
228 top.insert(key: cePalettes, value: paletteObject);
229 return paletteObject.keys().isEmpty() ? QJsonDocument() : QJsonDocument(top);
230}
231
232bool QGtk3Json::load(QGtk3Storage::PaletteMap &map, const QString &fileName)
233{
234 QFile file(fileName);
235 if (!file.open(flags: QIODevice::ReadOnly)) {
236 qCWarning(lcQGtk3Interface) << "Unable to open file:" << fileName;
237 return false;
238 }
239
240 QJsonParseError err;
241 QJsonDocument doc = QJsonDocument::fromJson(json: file.readAll(), error: &err);
242 if (err.error != QJsonParseError::NoError) {
243 qWarning(catFunc: lcQGtk3Interface) << "Unable to parse Json document from" << fileName
244 << err.error << err.errorString();
245 return false;
246 }
247
248 if (Q_LIKELY(load(map, doc))) {
249 qInfo() << "GTK mapping successfully imported from" << fileName;
250 return true;
251 }
252
253 qWarning() << "File" << fileName << "could not be loaded.";
254 return false;
255}
256
257bool QGtk3Json::load(QGtk3Storage::PaletteMap &map, const QJsonDocument &doc)
258{
259#define GETSTR(obj, key)\
260 if (!obj.contains(key)) {\
261 qCInfo(lcQGtk3Interface) << key << "missing for palette" << paletteName\
262 << ", Brush" << colorRoleName;\
263 return false;\
264 }\
265 value = obj[key].toString()
266
267#define GETINT(obj, key, var) GETSTR(obj, key);\
268 if (!obj[key].isDouble()) {\
269 qCInfo(lcQGtk3Interface) << key << "type mismatch" << value\
270 << "is not an integer!"\
271 << "(Palette" << paletteName << "), Brush" << colorRoleName;\
272 return false;\
273 }\
274 const int var = obj[key].toInt()
275
276 map.clear();
277 const QJsonObject top(doc.object());
278 if (doc.isEmpty() || top.isEmpty() || !top.contains(key: cePalettes)) {
279 qCInfo(lcQGtk3Interface) << "Document does not contain Palettes.";
280 return false;
281 }
282
283 const QStringList &paletteList = top[cePalettes].toObject().keys();
284 for (const QString &paletteName : paletteList) {
285 bool ok;
286 const int intVal = QMetaEnum::fromType<QPlatformTheme::Palette>().keyToValue(key: paletteName
287 .toLatin1().constData(), ok: &ok);
288 if (!ok) {
289 qCInfo(lcQGtk3Interface) << "Invalid Palette name:" << paletteName;
290 return false;
291 }
292 const QJsonObject &paletteObject = top[cePalettes][paletteName].toObject();
293 const QStringList &brushList = paletteObject.keys();
294 if (brushList.isEmpty()) {
295 qCInfo(lcQGtk3Interface) << "Palette" << paletteName << "does not contain brushes";
296 return false;
297 }
298
299 const QPlatformTheme::Palette paletteType = static_cast<QPlatformTheme::Palette>(intVal);
300 QGtk3Storage::BrushMap brushes;
301 const QStringList &colorRoles = paletteObject.keys();
302 for (const QString &colorRoleName : colorRoles) {
303 const int intVal = QMetaEnum::fromType<QPalette::ColorRole>().keyToValue(key: colorRoleName
304 .toLatin1().constData(), ok: &ok);
305 if (!ok) {
306 qCInfo(lcQGtk3Interface) << "Palette" << paletteName
307 << "contains invalid color role" << colorRoleName;
308 return false;
309 }
310 const QPalette::ColorRole colorRole = static_cast<QPalette::ColorRole>(intVal);
311 const QJsonArray &brushArray = paletteObject[colorRoleName].toArray();
312 for (int brushIndex = 0; brushIndex < brushArray.size(); ++brushIndex) {
313 const QJsonObject brushObject = brushArray.at(i: brushIndex).toObject();
314 if (brushObject.isEmpty()) {
315 qCInfo(lcQGtk3Interface) << "Brush specification missing at for palette"
316 << paletteName << ", Brush" << colorRoleName;
317 return false;
318 }
319
320 QString value;
321 GETSTR(brushObject, ceSourceType);
322 const QGtk3Storage::SourceType sourceType = toSourceType(sourceType: value);
323 GETSTR(brushObject, ceColorGroup);
324 const QPalette::ColorGroup colorGroup = toColorGroup(group: value);
325 GETSTR(brushObject, ceColorScheme);
326 const Qt::ColorScheme colorScheme = toColorScheme(colorScheme: value);
327 QGtk3Storage::TargetBrush tb(colorGroup, colorRole, colorScheme);
328 QGtk3Storage::Source s;
329
330 if (!brushObject.contains(key: ceData) || !brushObject[ceData].isObject()) {
331 qCInfo(lcQGtk3Interface) << "Source specification missing for palette" << paletteName
332 << "Brush" << colorRoleName;
333 return false;
334 }
335 const QJsonObject &sourceObject = brushObject[ceData].toObject();
336
337 switch (sourceType) {
338 case QGtk3Storage::SourceType::Gtk: {
339 GETSTR(sourceObject, ceGdkSource);
340 const QGtk3Interface::QGtkColorSource gtkSource = toGdkSource(source: value);
341 GETSTR(sourceObject, ceGtkState);
342 const GtkStateFlags gtkState = toGtkState(type: value);
343 GETSTR(sourceObject, ceGtkWidget);
344 const QGtk3Interface::QGtkWidget widgetType = toWidgetType(widgetType: value);
345 GETINT(sourceObject, ceHeight, height);
346 GETINT(sourceObject, ceWidth, width);
347 s = QGtk3Storage::Source(widgetType, gtkSource, gtkState, width, height);
348 }
349 break;
350
351 case QGtk3Storage::SourceType::Fixed: {
352 if (!sourceObject.contains(key: ceBrush)) {
353 qCInfo(lcQGtk3Interface) << "Fixed brush specification missing for palette" << paletteName
354 << "Brush" << colorRoleName;
355 return false;
356 }
357 const QJsonObject &fixedSource = sourceObject[ceBrush].toObject();
358 GETINT(fixedSource, ceWidth, width);
359 GETINT(fixedSource, ceHeight, height);
360 GETSTR(fixedSource, ceColor);
361 const QColor color(value);
362 if (!color.isValid()) {
363 qCInfo(lcQGtk3Interface) << "Color" << value << "can't be parsed for:" << paletteName
364 << "Brush" << colorRoleName;
365 return false;
366 }
367 const QBrush fixedBrush = (width < 0 && height < 0)
368 ? QBrush(color, QPixmap(width, height))
369 : QBrush(color);
370 s = QGtk3Storage::Source(fixedBrush);
371 }
372 break;
373
374 case QGtk3Storage::SourceType::Modified: {
375 GETSTR(sourceObject, ceColorGroup);
376 const QPalette::ColorGroup colorGroup = toColorGroup(group: value);
377 GETSTR(sourceObject, ceColorRole);
378 const QPalette::ColorRole colorRole = toColorRole(role: value);
379 GETSTR(sourceObject, ceColorScheme);
380 const Qt::ColorScheme colorScheme = toColorScheme(colorScheme: value);
381 GETINT(sourceObject, ceLighter, lighter);
382 GETINT(sourceObject, ceRed, red);
383 GETINT(sourceObject, ceBlue, blue);
384 GETINT(sourceObject, ceGreen, green);
385 s = QGtk3Storage::Source(colorGroup, colorRole, colorScheme,
386 lighter, red, green, blue);
387 }
388 break;
389
390 case QGtk3Storage::SourceType::Invalid:
391 qInfo(catFunc: lcQGtk3Interface) << "Invalid source type for palette" << paletteName
392 << "Brush." << colorRoleName;
393 return false;
394 }
395 brushes.insert(key: tb, value: s);
396 }
397 }
398 map.insert(key: paletteType, value: brushes);
399 }
400 return true;
401}
402
403QT_END_NAMESPACE
404
405

source code of qtbase/src/plugins/platformthemes/gtk3/qgtk3json.cpp