1 /*
2 SPDX-FileCopyrightText: 2006-2007 Aaron Seigo <aseigo@kde.org>
3 SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8 #include "theme_p.h"
9 #include "debug_p.h"
10 #include "framesvg.h"
11 #include "framesvg_p.h"
12 #include "svg_p.h"
13
14 #include <QDir>
15 #include <QFile>
16 #include <QFileInfo>
17 #include <QFontDatabase>
18 #include <QGuiApplication>
19
20 #include <KDirWatch>
21 #include <KIconLoader>
22 #include <KIconTheme>
23 #include <KWindowEffects>
24
25 namespace Plasma
26 {
27 const char ThemePrivate::defaultTheme[] = "default";
28 const char ThemePrivate::themeRcFile[] = "plasmarc";
29 // the system colors theme is used to cache unthemed svgs with colorization needs
30 // these svgs do not follow the theme's colors, but rather the system colors
31 const char ThemePrivate::systemColorsTheme[] = "internal-system-colors";
32 #if HAVE_X11
33 EffectWatcher *ThemePrivate::s_backgroundContrastEffectWatcher = nullptr;
34 #endif
35
36 ThemePrivate *ThemePrivate::globalTheme = nullptr;
37 QHash<QString, ThemePrivate *> ThemePrivate::themes = QHash<QString, ThemePrivate *>();
38
ThemePrivate(QObject * parent)39 ThemePrivate::ThemePrivate(QObject *parent)
40 : QObject(parent)
41 , colorScheme(QPalette::Active, KColorScheme::Window, KSharedConfigPtr(nullptr))
42 , selectionColorScheme(QPalette::Active, KColorScheme::Selection, KSharedConfigPtr(nullptr))
43 , buttonColorScheme(QPalette::Active, KColorScheme::Button, KSharedConfigPtr(nullptr))
44 , viewColorScheme(QPalette::Active, KColorScheme::View, KSharedConfigPtr(nullptr))
45 , complementaryColorScheme(QPalette::Active, KColorScheme::Complementary, KSharedConfigPtr(nullptr))
46 , headerColorScheme(QPalette::Active, KColorScheme::Header, KSharedConfigPtr(nullptr))
47 , tooltipColorScheme(QPalette::Active, KColorScheme::Tooltip, KSharedConfigPtr(nullptr))
48 , defaultWallpaperTheme(QStringLiteral(DEFAULT_WALLPAPER_THEME))
49 , defaultWallpaperSuffix(QStringLiteral(DEFAULT_WALLPAPER_SUFFIX))
50 , defaultWallpaperWidth(DEFAULT_WALLPAPER_WIDTH)
51 , defaultWallpaperHeight(DEFAULT_WALLPAPER_HEIGHT)
52 , pixmapCache(nullptr)
53 , cacheSize(0)
54 , cachesToDiscard(NoCache)
55 , compositingActive(KWindowSystem::self()->compositingActive())
56 , backgroundContrastActive(KWindowEffects::isEffectAvailable(KWindowEffects::BackgroundContrast))
57 , isDefault(true)
58 , useGlobal(true)
59 , hasWallpapers(false)
60 , fixedName(false)
61 , backgroundContrast(qQNaN())
62 , backgroundIntensity(qQNaN())
63 , backgroundSaturation(qQNaN())
64 , backgroundContrastEnabled(true)
65 , adaptiveTransparencyEnabled(false)
66 , blurBehindEnabled(true)
67 , apiMajor(1)
68 , apiMinor(0)
69 , apiRevision(0)
70 {
71 ThemeConfig config;
72 cacheTheme = config.cacheTheme();
73
74 pixmapSaveTimer = new QTimer(this);
75 pixmapSaveTimer->setSingleShot(true);
76 pixmapSaveTimer->setInterval(600);
77 QObject::connect(pixmapSaveTimer, &QTimer::timeout, this, &ThemePrivate::scheduledCacheUpdate);
78
79 updateNotificationTimer = new QTimer(this);
80 updateNotificationTimer->setSingleShot(true);
81 updateNotificationTimer->setInterval(100);
82 QObject::connect(updateNotificationTimer, &QTimer::timeout, this, &ThemePrivate::notifyOfChanged);
83
84 if (QPixmap::defaultDepth() > 8) {
85 #if HAVE_X11
86 // watch for background contrast effect property changes as well
87 if (!s_backgroundContrastEffectWatcher) {
88 s_backgroundContrastEffectWatcher = new EffectWatcher(QStringLiteral("_KDE_NET_WM_BACKGROUND_CONTRAST_REGION"));
89 }
90
91 QObject::connect(s_backgroundContrastEffectWatcher, &EffectWatcher::effectChanged, this, [this](bool active) {
92 if (backgroundContrastActive != active) {
93 backgroundContrastActive = active;
94 scheduleThemeChangeNotification(PixmapCache | SvgElementsCache);
95 }
96 });
97 #endif
98 }
99 QCoreApplication::instance()->installEventFilter(this);
100
101 const QString configFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + QLatin1String(themeRcFile);
102 KDirWatch::self()->addFile(configFile);
103
104 // Catch both, direct changes to the config file ...
105 connect(KDirWatch::self(), &KDirWatch::dirty, this, &ThemePrivate::settingsFileChanged);
106 // ... but also remove/recreate cycles, like KConfig does it
107 connect(KDirWatch::self(), &KDirWatch::created, this, &ThemePrivate::settingsFileChanged);
108
109 QObject::connect(KIconLoader::global(), &KIconLoader::iconChanged, this, [this]() {
110 scheduleThemeChangeNotification(PixmapCache | SvgElementsCache);
111 });
112
113 connect(KWindowSystem::self(), &KWindowSystem::compositingChanged, this, &ThemePrivate::compositingChanged);
114 }
115
~ThemePrivate()116 ThemePrivate::~ThemePrivate()
117 {
118 FrameSvgPrivate::s_sharedFrames.remove(this);
119 delete pixmapCache;
120 }
121
config()122 KConfigGroup &ThemePrivate::config()
123 {
124 if (!cfg.isValid()) {
125 QString groupName = QStringLiteral("Theme");
126
127 if (!useGlobal) {
128 QString app = QCoreApplication::applicationName();
129
130 if (!app.isEmpty()) {
131 #ifndef NDEBUG
132 // qCDebug(LOG_PLASMA) << "using theme for app" << app;
133 #endif
134 groupName.append(QLatin1Char('-')).append(app);
135 }
136 }
137 cfg = KConfigGroup(KSharedConfig::openConfig(QFile::decodeName(themeRcFile)), groupName);
138 }
139
140 return cfg;
141 }
142
useCache()143 bool ThemePrivate::useCache()
144 {
145 bool cachesTooOld = false;
146
147 if (cacheTheme && !pixmapCache) {
148 if (cacheSize == 0) {
149 ThemeConfig config;
150 cacheSize = config.themeCacheKb();
151 }
152 const bool isRegularTheme = themeName != QLatin1String(systemColorsTheme);
153 QString cacheFile = QLatin1String("plasma_theme_") + themeName;
154
155 // clear any cached values from the previous theme cache
156 themeVersion.clear();
157
158 if (!themeMetadataPath.isEmpty()) {
159 KDirWatch::self()->removeFile(themeMetadataPath);
160 }
161 if (isRegularTheme) {
162 themeMetadataPath =
163 QStandardPaths::locate(QStandardPaths::GenericDataLocation,
164 QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % themeName % QStringLiteral("/metadata.desktop"));
165 const auto *iconTheme = KIconLoader::global()->theme();
166 if (iconTheme) {
167 iconThemeMetadataPath = iconTheme->dir() + QStringLiteral("index.theme");
168 }
169
170 Q_ASSERT(!themeMetadataPath.isEmpty() || themeName.isEmpty());
171 const QString cacheFileBase = cacheFile + QLatin1String("*.kcache");
172
173 QString currentCacheFileName;
174 if (!themeMetadataPath.isEmpty()) {
175 // now we record the theme version, if we can
176 const KPluginInfo pluginInfo(themeMetadataPath);
177 if (pluginInfo.isValid()) {
178 themeVersion = pluginInfo.version();
179 }
180 if (!themeVersion.isEmpty()) {
181 cacheFile += QLatin1String("_v") + themeVersion;
182 currentCacheFileName = cacheFile + QLatin1String(".kcache");
183 }
184
185 // watch the metadata file for changes at runtime
186 KDirWatch::self()->addFile(themeMetadataPath);
187 QObject::connect(KDirWatch::self(), &KDirWatch::created, this, &ThemePrivate::settingsFileChanged, Qt::UniqueConnection);
188 QObject::connect(KDirWatch::self(), &KDirWatch::dirty, this, &ThemePrivate::settingsFileChanged, Qt::UniqueConnection);
189
190 if (!iconThemeMetadataPath.isEmpty()) {
191 KDirWatch::self()->addFile(iconThemeMetadataPath);
192 }
193 }
194
195 // now we check for, and remove if necessary, old caches
196 QDir cacheDir(QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation));
197 cacheDir.setNameFilters(QStringList({cacheFileBase}));
198
199 const auto files = cacheDir.entryInfoList();
200 for (const QFileInfo &file : files) {
201 if (currentCacheFileName.isEmpty() //
202 || !file.absoluteFilePath().endsWith(currentCacheFileName)) {
203 QFile::remove(file.absoluteFilePath());
204 }
205 }
206 }
207
208 // now we do a sanity check: if the metadata.desktop file is newer than the cache, drop the cache
209 if (isRegularTheme && !themeMetadataPath.isEmpty()) {
210 // now we check to see if the theme metadata file itself is newer than the pixmap cache
211 // this is done before creating the pixmapCache object since that can change the mtime
212 // on the cache file
213
214 // FIXME: when using the system colors, if they change while the application is not running
215 // the cache should be dropped; we need a way to detect system color change when the
216 // application is not running.
217 // check for expired cache
218 const QString cacheFilePath =
219 QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1Char('/') + cacheFile + QLatin1String(".kcache");
220 if (!cacheFilePath.isEmpty()) {
221 const QFileInfo cacheFileInfo(cacheFilePath);
222 const QFileInfo metadataFileInfo(themeMetadataPath);
223 const QFileInfo iconThemeMetadataFileInfo(iconThemeMetadataPath);
224
225 cachesTooOld = (cacheFileInfo.lastModified().toSecsSinceEpoch() < metadataFileInfo.lastModified().toSecsSinceEpoch())
226 || (cacheFileInfo.lastModified().toSecsSinceEpoch() < iconThemeMetadataFileInfo.lastModified().toSecsSinceEpoch());
227 }
228 }
229
230 ThemeConfig config;
231 pixmapCache = new KImageCache(cacheFile, config.themeCacheKb() * 1024);
232 pixmapCache->setEvictionPolicy(KSharedDataCache::EvictLeastRecentlyUsed);
233
234 if (cachesTooOld) {
235 discardCache(PixmapCache | SvgElementsCache);
236 }
237 }
238
239 if (cacheTheme) {
240 QString currentIconThemePath;
241 const auto *iconTheme = KIconLoader::global()->theme();
242 if (iconTheme) {
243 currentIconThemePath = iconTheme->dir();
244 }
245
246 const QString oldIconThemePath = SvgRectsCache::instance()->iconThemePath();
247 if (oldIconThemePath != currentIconThemePath) {
248 discardCache(PixmapCache | SvgElementsCache);
249 SvgRectsCache::instance()->setIconThemePath(currentIconThemePath);
250 }
251 }
252
253 return cacheTheme;
254 }
255
onAppExitCleanup()256 void ThemePrivate::onAppExitCleanup()
257 {
258 pixmapsToCache.clear();
259 delete pixmapCache;
260 pixmapCache = nullptr;
261 cacheTheme = false;
262 }
263
imagePath(const QString & theme,const QString & type,const QString & image)264 QString ThemePrivate::imagePath(const QString &theme, const QString &type, const QString &image)
265 {
266 QString subdir = QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % type % image;
267 return QStandardPaths::locate(QStandardPaths::GenericDataLocation, subdir);
268 }
269
findInTheme(const QString & image,const QString & theme,bool cache)270 QString ThemePrivate::findInTheme(const QString &image, const QString &theme, bool cache)
271 {
272 if (cache) {
273 auto it = discoveries.constFind(image);
274 if (it != discoveries.constEnd()) {
275 return it.value();
276 }
277 }
278
279 QString type = QStringLiteral("/");
280 if (!compositingActive) {
281 type = QStringLiteral("/opaque/");
282 } else if (backgroundContrastActive) {
283 type = QStringLiteral("/translucent/");
284 }
285
286 QString search = imagePath(theme, type, image);
287
288 // not found or compositing enabled
289 if (search.isEmpty()) {
290 search = imagePath(theme, QStringLiteral("/"), image);
291 }
292
293 if (cache && !search.isEmpty()) {
294 discoveries.insert(image, search);
295 }
296
297 return search;
298 }
299
compositingChanged(bool active)300 void ThemePrivate::compositingChanged(bool active)
301 {
302 #if HAVE_X11
303 if (compositingActive != active) {
304 compositingActive = active;
305 // qCDebug(LOG_PLASMA) << QTime::currentTime();
306 scheduleThemeChangeNotification(PixmapCache | SvgElementsCache);
307 }
308 #endif
309 }
310
discardCache(CacheTypes caches)311 void ThemePrivate::discardCache(CacheTypes caches)
312 {
313 if (caches & PixmapCache) {
314 pixmapsToCache.clear();
315 pixmapSaveTimer->stop();
316 if (pixmapCache) {
317 pixmapCache->clear();
318 }
319 } else {
320 // This deletes the object but keeps the on-disk cache for later use
321 delete pixmapCache;
322 pixmapCache = nullptr;
323 }
324
325 cachedDefaultStyleSheet = QString();
326 cachedSvgStyleSheets.clear();
327 cachedSelectedSvgStyleSheets.clear();
328
329 if (caches & SvgElementsCache) {
330 discoveries.clear();
331 }
332 }
333
scheduledCacheUpdate()334 void ThemePrivate::scheduledCacheUpdate()
335 {
336 if (useCache()) {
337 QHashIterator<QString, QPixmap> it(pixmapsToCache);
338 while (it.hasNext()) {
339 it.next();
340 pixmapCache->insertPixmap(idsToCache[it.key()], it.value());
341 }
342 }
343
344 pixmapsToCache.clear();
345 keysToCache.clear();
346 idsToCache.clear();
347 }
348
colorsChanged()349 void ThemePrivate::colorsChanged()
350 {
351 // in the case the theme follows the desktop settings, refetch the colorschemes
352 // and discard the svg pixmap cache
353 if (!colors) {
354 KSharedConfig::openConfig()->reparseConfiguration();
355 }
356 colorScheme = KColorScheme(QPalette::Active, KColorScheme::Window, colors);
357 buttonColorScheme = KColorScheme(QPalette::Active, KColorScheme::Button, colors);
358 viewColorScheme = KColorScheme(QPalette::Active, KColorScheme::View, colors);
359 selectionColorScheme = KColorScheme(QPalette::Active, KColorScheme::Selection, colors);
360 complementaryColorScheme = KColorScheme(QPalette::Active, KColorScheme::Complementary, colors);
361 headerColorScheme = KColorScheme(QPalette::Active, KColorScheme::Header, colors);
362 tooltipColorScheme = KColorScheme(QPalette::Active, KColorScheme::Tooltip, colors);
363 palette = KColorScheme::createApplicationPalette(colors);
364 scheduleThemeChangeNotification(PixmapCache | SvgElementsCache);
365 Q_EMIT applicationPaletteChange();
366 }
367
scheduleThemeChangeNotification(CacheTypes caches)368 void ThemePrivate::scheduleThemeChangeNotification(CacheTypes caches)
369 {
370 cachesToDiscard |= caches;
371 updateNotificationTimer->start();
372 }
373
notifyOfChanged()374 void ThemePrivate::notifyOfChanged()
375 {
376 // qCDebug(LOG_PLASMA) << cachesToDiscard;
377 discardCache(cachesToDiscard);
378 cachesToDiscard = NoCache;
379 Q_EMIT themeChanged();
380 }
381
processStyleSheet(const QString & css,Plasma::Svg::Status status)382 const QString ThemePrivate::processStyleSheet(const QString &css, Plasma::Svg::Status status)
383 {
384 QString stylesheet;
385 if (css.isEmpty()) {
386 stylesheet = cachedDefaultStyleSheet;
387 if (stylesheet.isEmpty()) {
388 /* clang-format off */
389 stylesheet = QStringLiteral(
390 "\n\
391 body {\n\
392 color: %textcolor;\n\
393 generalfont-size: %fontsize;\n\
394 font-family: %fontfamily;\n\
395 }\n\
396 a:active { color: %activatedlink; }\n\
397 a:link { color: %link; }\n\
398 a:visited { color: %visitedlink; }\n\
399 a:hover { color: %hoveredlink; text-decoration: none; }\n\
400 ");
401 /* clang-format on */
402 stylesheet = cachedDefaultStyleSheet = processStyleSheet(stylesheet, status);
403 }
404
405 return stylesheet;
406 } else {
407 stylesheet = css;
408 }
409
410 QHash<QString, QString> elements;
411 // If you add elements here, make sure their names are sufficiently unique to not cause
412 // clashes between element keys
413 elements[QStringLiteral("%textcolor")] =
414 color(status == Svg::Status::Selected ? Theme::HighlightedTextColor : Theme::TextColor, Theme::NormalColorGroup).name();
415 elements[QStringLiteral("%backgroundcolor")] =
416 color(status == Svg::Status::Selected ? Theme::HighlightColor : Theme::BackgroundColor, Theme::NormalColorGroup).name();
417 elements[QStringLiteral("%highlightcolor")] = color(Theme::HighlightColor, Theme::NormalColorGroup).name();
418 elements[QStringLiteral("%highlightedtextcolor")] = color(Theme::HighlightedTextColor, Theme::NormalColorGroup).name();
419 elements[QStringLiteral("%visitedlink")] = color(Theme::VisitedLinkColor, Theme::NormalColorGroup).name();
420 elements[QStringLiteral("%activatedlink")] = color(Theme::HighlightColor, Theme::NormalColorGroup).name();
421 elements[QStringLiteral("%hoveredlink")] = color(Theme::HighlightColor, Theme::NormalColorGroup).name();
422 elements[QStringLiteral("%link")] = color(Theme::LinkColor, Theme::NormalColorGroup).name();
423 elements[QStringLiteral("%positivetextcolor")] = color(Theme::PositiveTextColor, Theme::NormalColorGroup).name();
424 elements[QStringLiteral("%neutraltextcolor")] = color(Theme::NeutralTextColor, Theme::NormalColorGroup).name();
425 elements[QStringLiteral("%negativetextcolor")] = color(Theme::NegativeTextColor, Theme::NormalColorGroup).name();
426
427 elements[QStringLiteral("%buttontextcolor")] =
428 color(status == Svg::Status::Selected ? Theme::HighlightedTextColor : Theme::TextColor, Theme::ButtonColorGroup).name();
429 elements[QStringLiteral("%buttonbackgroundcolor")] =
430 color(status == Svg::Status::Selected ? Theme::HighlightColor : Theme::BackgroundColor, Theme::ButtonColorGroup).name();
431 elements[QStringLiteral("%buttonhovercolor")] = color(Theme::HoverColor, Theme::ButtonColorGroup).name();
432 elements[QStringLiteral("%buttonfocuscolor")] = color(Theme::FocusColor, Theme::ButtonColorGroup).name();
433 elements[QStringLiteral("%buttonhighlightedtextcolor")] = color(Theme::HighlightedTextColor, Theme::ButtonColorGroup).name();
434 elements[QStringLiteral("%buttonpositivetextcolor")] = color(Theme::PositiveTextColor, Theme::ButtonColorGroup).name();
435 elements[QStringLiteral("%buttonneutraltextcolor")] = color(Theme::NeutralTextColor, Theme::ButtonColorGroup).name();
436 elements[QStringLiteral("%buttonnegativetextcolor")] = color(Theme::NegativeTextColor, Theme::ButtonColorGroup).name();
437
438 elements[QStringLiteral("%viewtextcolor")] =
439 color(status == Svg::Status::Selected ? Theme::HighlightedTextColor : Theme::TextColor, Theme::ViewColorGroup).name();
440 elements[QStringLiteral("%viewbackgroundcolor")] =
441 color(status == Svg::Status::Selected ? Theme::HighlightColor : Theme::BackgroundColor, Theme::ViewColorGroup).name();
442 elements[QStringLiteral("%viewhovercolor")] = color(Theme::HoverColor, Theme::ViewColorGroup).name();
443 elements[QStringLiteral("%viewfocuscolor")] = color(Theme::FocusColor, Theme::ViewColorGroup).name();
444 elements[QStringLiteral("%viewhighlightedtextcolor")] = color(Theme::HighlightedTextColor, Theme::ViewColorGroup).name();
445 elements[QStringLiteral("%viewpositivetextcolor")] = color(Theme::PositiveTextColor, Theme::ViewColorGroup).name();
446 elements[QStringLiteral("%viewneutraltextcolor")] = color(Theme::NeutralTextColor, Theme::ViewColorGroup).name();
447 elements[QStringLiteral("%viewnegativetextcolor")] = color(Theme::NegativeTextColor, Theme::ViewColorGroup).name();
448
449 elements[QStringLiteral("%tooltiptextcolor")] =
450 color(status == Svg::Status::Selected ? Theme::HighlightedTextColor : Theme::TextColor, Theme::ToolTipColorGroup).name();
451 elements[QStringLiteral("%tooltipbackgroundcolor")] =
452 color(status == Svg::Status::Selected ? Theme::HighlightColor : Theme::BackgroundColor, Theme::ToolTipColorGroup).name();
453 elements[QStringLiteral("%tooltiphovercolor")] = color(Theme::HoverColor, Theme::ToolTipColorGroup).name();
454 elements[QStringLiteral("%tooltipfocuscolor")] = color(Theme::FocusColor, Theme::ToolTipColorGroup).name();
455 elements[QStringLiteral("%tooltiphighlightedtextcolor")] = color(Theme::HighlightedTextColor, Theme::ToolTipColorGroup).name();
456 elements[QStringLiteral("%tooltippositivetextcolor")] = color(Theme::PositiveTextColor, Theme::ToolTipColorGroup).name();
457 elements[QStringLiteral("%tooltipneutraltextcolor")] = color(Theme::NeutralTextColor, Theme::ToolTipColorGroup).name();
458 elements[QStringLiteral("%tooltipnegativetextcolor")] = color(Theme::NegativeTextColor, Theme::ToolTipColorGroup).name();
459
460 elements[QStringLiteral("%complementarytextcolor")] =
461 color(status == Svg::Status::Selected ? Theme::HighlightedTextColor : Theme::TextColor, Theme::ComplementaryColorGroup).name();
462 elements[QStringLiteral("%complementarybackgroundcolor")] =
463 color(status == Svg::Status::Selected ? Theme::HighlightColor : Theme::BackgroundColor, Theme::ComplementaryColorGroup).name();
464 elements[QStringLiteral("%complementaryhovercolor")] = color(Theme::HoverColor, Theme::ComplementaryColorGroup).name();
465 elements[QStringLiteral("%complementaryfocuscolor")] = color(Theme::FocusColor, Theme::ComplementaryColorGroup).name();
466 elements[QStringLiteral("%complementaryhighlightedtextcolor")] = color(Theme::HighlightedTextColor, Theme::ComplementaryColorGroup).name();
467 elements[QStringLiteral("%complementarypositivetextcolor")] = color(Theme::PositiveTextColor, Theme::ComplementaryColorGroup).name();
468 elements[QStringLiteral("%complementaryneutraltextcolor")] = color(Theme::NeutralTextColor, Theme::ComplementaryColorGroup).name();
469 elements[QStringLiteral("%complementarynegativetextcolor")] = color(Theme::NegativeTextColor, Theme::ComplementaryColorGroup).name();
470
471 elements[QStringLiteral("%headertextcolor")] =
472 color(status == Svg::Status::Selected ? Theme::HighlightedTextColor : Theme::TextColor, Theme::HeaderColorGroup).name();
473 elements[QStringLiteral("%headerbackgroundcolor")] =
474 color(status == Svg::Status::Selected ? Theme::HighlightColor : Theme::BackgroundColor, Theme::HeaderColorGroup).name();
475 elements[QStringLiteral("%headerhovercolor")] = color(Theme::HoverColor, Theme::HeaderColorGroup).name();
476 elements[QStringLiteral("%headerfocuscolor")] = color(Theme::FocusColor, Theme::HeaderColorGroup).name();
477 elements[QStringLiteral("%headerhighlightedtextcolor")] = color(Theme::HighlightedTextColor, Theme::HeaderColorGroup).name();
478 elements[QStringLiteral("%headerpositivetextcolor")] = color(Theme::PositiveTextColor, Theme::HeaderColorGroup).name();
479 elements[QStringLiteral("%headerneutraltextcolor")] = color(Theme::NeutralTextColor, Theme::HeaderColorGroup).name();
480 elements[QStringLiteral("%headernegativetextcolor")] = color(Theme::NegativeTextColor, Theme::HeaderColorGroup).name();
481
482 QFont font = QGuiApplication::font();
483 elements[QStringLiteral("%fontsize")] = QStringLiteral("%1pt").arg(font.pointSize());
484 QString family{font.family()};
485 family.truncate(family.indexOf(QLatin1Char('[')));
486 elements[QStringLiteral("%fontfamily")] = family;
487 elements[QStringLiteral("%smallfontsize")] = QStringLiteral("%1pt").arg(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont).pointSize());
488
489 QHash<QString, QString>::const_iterator it = elements.constBegin();
490 QHash<QString, QString>::const_iterator itEnd = elements.constEnd();
491 for (; it != itEnd; ++it) {
492 stylesheet.replace(it.key(), it.value());
493 }
494 return stylesheet;
495 }
496
svgStyleSheet(Plasma::Theme::ColorGroup group,Plasma::Svg::Status status)497 const QString ThemePrivate::svgStyleSheet(Plasma::Theme::ColorGroup group, Plasma::Svg::Status status)
498 {
499 QString stylesheet = (status == Svg::Status::Selected) ? cachedSelectedSvgStyleSheets.value(group) : cachedSvgStyleSheets.value(group);
500 if (stylesheet.isEmpty()) {
501 QString skel = QStringLiteral(".ColorScheme-%1{color:%2;}");
502
503 switch (group) {
504 case Theme::ButtonColorGroup:
505 stylesheet += skel.arg(QStringLiteral("Text"), QStringLiteral("%buttontextcolor"));
506 stylesheet += skel.arg(QStringLiteral("Background"), QStringLiteral("%buttonbackgroundcolor"));
507
508 stylesheet += skel.arg(QStringLiteral("Highlight"), QStringLiteral("%buttonhovercolor"));
509 stylesheet += skel.arg(QStringLiteral("HighlightedText"), QStringLiteral("%buttonhighlightedtextcolor"));
510 stylesheet += skel.arg(QStringLiteral("PositiveText"), QStringLiteral("%buttonpositivetextcolor"));
511 stylesheet += skel.arg(QStringLiteral("NeutralText"), QStringLiteral("%buttonneutraltextcolor"));
512 stylesheet += skel.arg(QStringLiteral("NegativeText"), QStringLiteral("%buttonnegativetextcolor"));
513 break;
514 case Theme::ViewColorGroup:
515 stylesheet += skel.arg(QStringLiteral("Text"), QStringLiteral("%viewtextcolor"));
516 stylesheet += skel.arg(QStringLiteral("Background"), QStringLiteral("%viewbackgroundcolor"));
517
518 stylesheet += skel.arg(QStringLiteral("Highlight"), QStringLiteral("%viewhovercolor"));
519 stylesheet += skel.arg(QStringLiteral("HighlightedText"), QStringLiteral("%viewhighlightedtextcolor"));
520 stylesheet += skel.arg(QStringLiteral("PositiveText"), QStringLiteral("%viewpositivetextcolor"));
521 stylesheet += skel.arg(QStringLiteral("NeutralText"), QStringLiteral("%viewneutraltextcolor"));
522 stylesheet += skel.arg(QStringLiteral("NegativeText"), QStringLiteral("%viewnegativetextcolor"));
523 break;
524 case Theme::ComplementaryColorGroup:
525 stylesheet += skel.arg(QStringLiteral("Text"), QStringLiteral("%complementarytextcolor"));
526 stylesheet += skel.arg(QStringLiteral("Background"), QStringLiteral("%complementarybackgroundcolor"));
527
528 stylesheet += skel.arg(QStringLiteral("Highlight"), QStringLiteral("%complementaryhovercolor"));
529 stylesheet += skel.arg(QStringLiteral("HighlightedText"), QStringLiteral("%complementaryhighlightedtextcolor"));
530 stylesheet += skel.arg(QStringLiteral("PositiveText"), QStringLiteral("%complementarypositivetextcolor"));
531 stylesheet += skel.arg(QStringLiteral("NeutralText"), QStringLiteral("%complementaryneutraltextcolor"));
532 stylesheet += skel.arg(QStringLiteral("NegativeText"), QStringLiteral("%complementarynegativetextcolor"));
533 break;
534 case Theme::HeaderColorGroup:
535 stylesheet += skel.arg(QStringLiteral("Text"), QStringLiteral("%headertextcolor"));
536 stylesheet += skel.arg(QStringLiteral("Background"), QStringLiteral("%headerbackgroundcolor"));
537
538 stylesheet += skel.arg(QStringLiteral("Highlight"), QStringLiteral("%headerhovercolor"));
539 stylesheet += skel.arg(QStringLiteral("HighlightedText"), QStringLiteral("%headerhighlightedtextcolor"));
540 stylesheet += skel.arg(QStringLiteral("PositiveText"), QStringLiteral("%headerpositivetextcolor"));
541 stylesheet += skel.arg(QStringLiteral("NeutralText"), QStringLiteral("%headerneutraltextcolor"));
542 stylesheet += skel.arg(QStringLiteral("NegativeText"), QStringLiteral("%headernegativetextcolor"));
543 break;
544 case Theme::ToolTipColorGroup:
545 stylesheet += skel.arg(QStringLiteral("Text"), QStringLiteral("%tooltiptextcolor"));
546 stylesheet += skel.arg(QStringLiteral("Background"), QStringLiteral("%tooltipbackgroundcolor"));
547
548 stylesheet += skel.arg(QStringLiteral("Highlight"), QStringLiteral("%tooltiphovercolor"));
549 stylesheet += skel.arg(QStringLiteral("HighlightedText"), QStringLiteral("%tooltiphighlightedtextcolor"));
550 stylesheet += skel.arg(QStringLiteral("PositiveText"), QStringLiteral("%tooltippositivetextcolor"));
551 stylesheet += skel.arg(QStringLiteral("NeutralText"), QStringLiteral("%tooltipneutraltextcolor"));
552 stylesheet += skel.arg(QStringLiteral("NegativeText"), QStringLiteral("%tooltipnegativetextcolor"));
553 break;
554 default:
555 stylesheet += skel.arg(QStringLiteral("Text"), QStringLiteral("%textcolor"));
556 stylesheet += skel.arg(QStringLiteral("Background"), QStringLiteral("%backgroundcolor"));
557
558 stylesheet += skel.arg(QStringLiteral("Highlight"), QStringLiteral("%highlightcolor"));
559 stylesheet += skel.arg(QStringLiteral("HighlightedText"), QStringLiteral("%highlightedtextcolor"));
560 stylesheet += skel.arg(QStringLiteral("PositiveText"), QStringLiteral("%positivetextcolor"));
561 stylesheet += skel.arg(QStringLiteral("NeutralText"), QStringLiteral("%neutraltextcolor"));
562 stylesheet += skel.arg(QStringLiteral("NegativeText"), QStringLiteral("%negativetextcolor"));
563 }
564
565 stylesheet += skel.arg(QStringLiteral("ButtonText"), QStringLiteral("%buttontextcolor"));
566 stylesheet += skel.arg(QStringLiteral("ButtonBackground"), QStringLiteral("%buttonbackgroundcolor"));
567 stylesheet += skel.arg(QStringLiteral("ButtonHover"), QStringLiteral("%buttonhovercolor"));
568 stylesheet += skel.arg(QStringLiteral("ButtonFocus"), QStringLiteral("%buttonfocuscolor"));
569 stylesheet += skel.arg(QStringLiteral("ButtonHighlightedText"), QStringLiteral("%buttonhighlightedtextcolor"));
570 stylesheet += skel.arg(QStringLiteral("ButtonPositiveText"), QStringLiteral("%buttonpositivetextcolor"));
571 stylesheet += skel.arg(QStringLiteral("ButtonNeutralText"), QStringLiteral("%buttonneutraltextcolor"));
572 stylesheet += skel.arg(QStringLiteral("ButtonNegativeText"), QStringLiteral("%buttonnegativetextcolor"));
573
574 stylesheet += skel.arg(QStringLiteral("ViewText"), QStringLiteral("%viewtextcolor"));
575 stylesheet += skel.arg(QStringLiteral("ViewBackground"), QStringLiteral("%viewbackgroundcolor"));
576 stylesheet += skel.arg(QStringLiteral("ViewHover"), QStringLiteral("%viewhovercolor"));
577 stylesheet += skel.arg(QStringLiteral("ViewFocus"), QStringLiteral("%viewfocuscolor"));
578 stylesheet += skel.arg(QStringLiteral("ViewHighlightedText"), QStringLiteral("%viewhighlightedtextcolor"));
579 stylesheet += skel.arg(QStringLiteral("ViewPositiveText"), QStringLiteral("%viewpositivetextcolor"));
580 stylesheet += skel.arg(QStringLiteral("ViewNeutralText"), QStringLiteral("%viewneutraltextcolor"));
581 stylesheet += skel.arg(QStringLiteral("ViewNegativeText"), QStringLiteral("%viewnegativetextcolor"));
582
583 stylesheet += skel.arg(QStringLiteral("ComplementaryText"), QStringLiteral("%complementarytextcolor"));
584 stylesheet += skel.arg(QStringLiteral("ComplementaryBackground"), QStringLiteral("%complementarybackgroundcolor"));
585 stylesheet += skel.arg(QStringLiteral("ComplementaryHover"), QStringLiteral("%complementaryhovercolor"));
586 stylesheet += skel.arg(QStringLiteral("ComplementaryFocus"), QStringLiteral("%complementaryfocuscolor"));
587 stylesheet += skel.arg(QStringLiteral("ComplementaryHighlightedText"), QStringLiteral("%complementaryhighlightedtextcolor"));
588 stylesheet += skel.arg(QStringLiteral("ComplementaryPositiveText"), QStringLiteral("%complementarypositivetextcolor"));
589 stylesheet += skel.arg(QStringLiteral("ComplementaryNeutralText"), QStringLiteral("%complementaryneutraltextcolor"));
590 stylesheet += skel.arg(QStringLiteral("ComplementaryNegativeText"), QStringLiteral("%complementarynegativetextcolor"));
591
592 stylesheet += skel.arg(QStringLiteral("HeaderText"), QStringLiteral("%headertextcolor"));
593 stylesheet += skel.arg(QStringLiteral("HeaderBackground"), QStringLiteral("%headerbackgroundcolor"));
594 stylesheet += skel.arg(QStringLiteral("HeaderHover"), QStringLiteral("%headerhovercolor"));
595 stylesheet += skel.arg(QStringLiteral("HeaderFocus"), QStringLiteral("%headerfocuscolor"));
596 stylesheet += skel.arg(QStringLiteral("HeaderHighlightedText"), QStringLiteral("%headerhighlightedtextcolor"));
597 stylesheet += skel.arg(QStringLiteral("HeaderPositiveText"), QStringLiteral("%headerpositivetextcolor"));
598 stylesheet += skel.arg(QStringLiteral("HeaderNeutralText"), QStringLiteral("%headerneutraltextcolor"));
599 stylesheet += skel.arg(QStringLiteral("HeaderNegativeText"), QStringLiteral("%headernegativetextcolor"));
600
601 stylesheet += skel.arg(QStringLiteral("TootipText"), QStringLiteral("%tooltiptextcolor"));
602 stylesheet += skel.arg(QStringLiteral("TootipBackground"), QStringLiteral("%tooltipbackgroundcolor"));
603 stylesheet += skel.arg(QStringLiteral("TootipHover"), QStringLiteral("%tooltiphovercolor"));
604 stylesheet += skel.arg(QStringLiteral("TootipFocus"), QStringLiteral("%tooltipfocuscolor"));
605 stylesheet += skel.arg(QStringLiteral("TootipHighlightedText"), QStringLiteral("%tooltiphighlightedtextcolor"));
606 stylesheet += skel.arg(QStringLiteral("TootipPositiveText"), QStringLiteral("%tooltippositivetextcolor"));
607 stylesheet += skel.arg(QStringLiteral("TootipNeutralText"), QStringLiteral("%tooltipneutraltextcolor"));
608 stylesheet += skel.arg(QStringLiteral("TootipNegativeText"), QStringLiteral("%tooltipnegativetextcolor"));
609
610 stylesheet = processStyleSheet(stylesheet, status);
611 if (status == Svg::Status::Selected) {
612 cachedSelectedSvgStyleSheets.insert(group, stylesheet);
613 } else {
614 cachedSvgStyleSheets.insert(group, stylesheet);
615 }
616 }
617
618 return stylesheet;
619 }
620
settingsFileChanged(const QString & file)621 void ThemePrivate::settingsFileChanged(const QString &file)
622 {
623 qCDebug(LOG_PLASMA) << "settingsFile: " << file;
624 if (file == themeMetadataPath) {
625 const KPluginInfo pluginInfo(themeMetadataPath);
626 if (!pluginInfo.isValid() || themeVersion != pluginInfo.version()) {
627 scheduleThemeChangeNotification(SvgElementsCache);
628 }
629 } else if (file.endsWith(QLatin1String(themeRcFile))) {
630 config().config()->reparseConfiguration();
631 settingsChanged(true);
632 }
633 }
634
settingsChanged(bool emitChanges)635 void ThemePrivate::settingsChanged(bool emitChanges)
636 {
637 if (fixedName) {
638 return;
639 }
640 // qCDebug(LOG_PLASMA) << "Settings Changed!";
641 KConfigGroup cg = config();
642 setThemeName(cg.readEntry("name", ThemePrivate::defaultTheme), false, emitChanges);
643 }
644
color(Theme::ColorRole role,Theme::ColorGroup group) const645 QColor ThemePrivate::color(Theme::ColorRole role, Theme::ColorGroup group) const
646 {
647 const KColorScheme *scheme = nullptr;
648
649 // Before 5.0 Plasma theme really only used Normal and Button
650 // many old themes are built on this assumption and will break
651 // otherwise
652 if (apiMajor < 5 && group != Theme::NormalColorGroup) {
653 group = Theme::ButtonColorGroup;
654 }
655
656 switch (group) {
657 case Theme::ButtonColorGroup: {
658 scheme = &buttonColorScheme;
659 break;
660 }
661
662 case Theme::ViewColorGroup: {
663 scheme = &viewColorScheme;
664 break;
665 }
666
667 // this doesn't have a real kcolorscheme
668 case Theme::ComplementaryColorGroup: {
669 scheme = &complementaryColorScheme;
670 break;
671 }
672
673 case Theme::HeaderColorGroup: {
674 scheme = &headerColorScheme;
675 break;
676 }
677
678 case Theme::ToolTipColorGroup: {
679 scheme = &tooltipColorScheme;
680 break;
681 }
682
683 case Theme::NormalColorGroup:
684 default: {
685 scheme = &colorScheme;
686 break;
687 }
688 }
689
690 switch (role) {
691 case Theme::TextColor:
692 return scheme->foreground(KColorScheme::NormalText).color();
693
694 case Theme::BackgroundColor:
695 return scheme->background(KColorScheme::NormalBackground).color();
696
697 case Theme::HoverColor:
698 return scheme->decoration(KColorScheme::HoverColor).color();
699
700 case Theme::HighlightColor:
701 return selectionColorScheme.background(KColorScheme::NormalBackground).color();
702
703 case Theme::FocusColor:
704 return scheme->decoration(KColorScheme::FocusColor).color();
705
706 case Theme::LinkColor:
707 return scheme->foreground(KColorScheme::LinkText).color();
708
709 case Theme::VisitedLinkColor:
710 return scheme->foreground(KColorScheme::VisitedText).color();
711
712 case Theme::HighlightedTextColor:
713 return selectionColorScheme.foreground(KColorScheme::NormalText).color();
714
715 case Theme::PositiveTextColor:
716 return scheme->foreground(KColorScheme::PositiveText).color();
717 case Theme::NeutralTextColor:
718 return scheme->foreground(KColorScheme::NeutralText).color();
719 case Theme::NegativeTextColor:
720 return scheme->foreground(KColorScheme::NegativeText).color();
721 case Theme::DisabledTextColor:
722 return scheme->foreground(KColorScheme::InactiveText).color();
723 }
724
725 return QColor();
726 }
727
processWallpaperSettings(KConfigBase * metadata)728 void ThemePrivate::processWallpaperSettings(KConfigBase *metadata)
729 {
730 if (!defaultWallpaperTheme.isEmpty() && defaultWallpaperTheme != QLatin1String(DEFAULT_WALLPAPER_THEME)) {
731 return;
732 }
733
734 KConfigGroup cg;
735 if (metadata->hasGroup("Wallpaper")) {
736 // we have a theme color config, so let's also check to see if
737 // there is a wallpaper defined in there.
738 cg = KConfigGroup(metadata, "Wallpaper");
739 } else {
740 // since we didn't find an entry in the theme, let's look in the main
741 // theme config
742 cg = config();
743 }
744
745 defaultWallpaperTheme = cg.readEntry("defaultWallpaperTheme", DEFAULT_WALLPAPER_THEME);
746 defaultWallpaperSuffix = cg.readEntry("defaultFileSuffix", DEFAULT_WALLPAPER_SUFFIX);
747 defaultWallpaperWidth = cg.readEntry("defaultWidth", DEFAULT_WALLPAPER_WIDTH);
748 defaultWallpaperHeight = cg.readEntry("defaultHeight", DEFAULT_WALLPAPER_HEIGHT);
749 }
750
processContrastSettings(KConfigBase * metadata)751 void ThemePrivate::processContrastSettings(KConfigBase *metadata)
752 {
753 KConfigGroup cg;
754 if (metadata->hasGroup("ContrastEffect")) {
755 cg = KConfigGroup(metadata, "ContrastEffect");
756 backgroundContrastEnabled = cg.readEntry("enabled", false);
757
758 backgroundContrast = cg.readEntry("contrast", qQNaN());
759 backgroundIntensity = cg.readEntry("intensity", qQNaN());
760 backgroundSaturation = cg.readEntry("saturation", qQNaN());
761 } else {
762 backgroundContrastEnabled = false;
763 }
764 }
765
processAdaptiveTransparencySettings(KConfigBase * metadata)766 void ThemePrivate::processAdaptiveTransparencySettings(KConfigBase *metadata)
767 {
768 KConfigGroup cg;
769 if (metadata->hasGroup("AdaptiveTransparency")) {
770 cg = KConfigGroup(metadata, "AdaptiveTransparency");
771 adaptiveTransparencyEnabled = cg.readEntry("enabled", false);
772 } else {
773 adaptiveTransparencyEnabled = false;
774 }
775 }
776
processBlurBehindSettings(KConfigBase * metadata)777 void ThemePrivate::processBlurBehindSettings(KConfigBase *metadata)
778 {
779 KConfigGroup cg;
780 if (metadata->hasGroup("BlurBehindEffect")) {
781 cg = KConfigGroup(metadata, "BlurBehindEffect");
782 blurBehindEnabled = cg.readEntry("enabled", true);
783 } else {
784 blurBehindEnabled = true;
785 }
786 }
787
setThemeName(const QString & tempThemeName,bool writeSettings,bool emitChanged)788 void ThemePrivate::setThemeName(const QString &tempThemeName, bool writeSettings, bool emitChanged)
789 {
790 QString theme = tempThemeName;
791 if (theme.isEmpty() || theme == themeName) {
792 // let's try and get the default theme at least
793 if (themeName.isEmpty()) {
794 theme = QLatin1String(ThemePrivate::defaultTheme);
795 } else {
796 return;
797 }
798 }
799
800 // we have one special theme: essentially a dummy theme used to cache things with
801 // the system colors.
802 bool realTheme = theme != QLatin1String(systemColorsTheme);
803 if (realTheme) {
804 QString themePath =
805 QStandardPaths::locate(QStandardPaths::GenericDataLocation,
806 QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % QLatin1String("/metadata.desktop"));
807
808 if (themePath.isEmpty() && themeName.isEmpty()) {
809 // note: can't use QStringLiteral("foo" "bar") on Windows
810 themePath = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
811 QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/default"),
812 QStandardPaths::LocateDirectory);
813
814 if (themePath.isEmpty()) {
815 return;
816 }
817
818 theme = QLatin1String(ThemePrivate::defaultTheme);
819 }
820 }
821
822 // check again as ThemePrivate::defaultTheme might be empty
823 if (themeName == theme) {
824 return;
825 }
826
827 themeName = theme;
828
829 // load the color scheme config
830 const QString colorsFile = realTheme
831 ? QStandardPaths::locate(QStandardPaths::GenericDataLocation,
832 QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % QLatin1String("/colors"))
833 : QString();
834
835 // qCDebug(LOG_PLASMA) << "we're going for..." << colorsFile << "*******************";
836
837 if (colorsFile.isEmpty()) {
838 colors = nullptr;
839 } else {
840 colors = KSharedConfig::openConfig(colorsFile);
841 }
842
843 colorScheme = KColorScheme(QPalette::Active, KColorScheme::Window, colors);
844 selectionColorScheme = KColorScheme(QPalette::Active, KColorScheme::Selection, colors);
845 buttonColorScheme = KColorScheme(QPalette::Active, KColorScheme::Button, colors);
846 viewColorScheme = KColorScheme(QPalette::Active, KColorScheme::View, colors);
847 complementaryColorScheme = KColorScheme(QPalette::Active, KColorScheme::Complementary, colors);
848 headerColorScheme = KColorScheme(QPalette::Active, KColorScheme::Header, colors);
849 tooltipColorScheme = KColorScheme(QPalette::Active, KColorScheme::Tooltip, colors);
850 palette = KColorScheme::createApplicationPalette(colors);
851 const QString wallpaperPath = QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % QLatin1String("/wallpapers/");
852 hasWallpapers = !QStandardPaths::locate(QStandardPaths::GenericDataLocation, wallpaperPath, QStandardPaths::LocateDirectory).isEmpty();
853
854 // load the wallpaper settings, if any
855 if (realTheme) {
856 const QString metadataPath(
857 QStandardPaths::locate(QStandardPaths::GenericDataLocation,
858 QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % QLatin1String("/metadata.desktop")));
859 KConfig metadata(metadataPath, KConfig::SimpleConfig);
860 pluginMetaData = KPluginMetaData(metadataPath);
861
862 processContrastSettings(&metadata);
863 processBlurBehindSettings(&metadata);
864 processAdaptiveTransparencySettings(&metadata);
865
866 processWallpaperSettings(&metadata);
867
868 KConfigGroup cg(&metadata, "Settings");
869 QString fallback = cg.readEntry("FallbackTheme", QString());
870
871 fallbackThemes.clear();
872 while (!fallback.isEmpty() && !fallbackThemes.contains(fallback)) {
873 fallbackThemes.append(fallback);
874
875 QString metadataPath(
876 QStandardPaths::locate(QStandardPaths::GenericDataLocation,
877 QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % fallback % QStringLiteral("/metadata.desktop")));
878 KConfig metadata(metadataPath, KConfig::SimpleConfig);
879 KConfigGroup cg(&metadata, "Settings");
880 fallback = cg.readEntry("FallbackTheme", QString());
881 }
882
883 if (!fallbackThemes.contains(QLatin1String(ThemePrivate::defaultTheme))) {
884 fallbackThemes.append(QLatin1String(ThemePrivate::defaultTheme));
885 }
886
887 for (const QString &theme : std::as_const(fallbackThemes)) {
888 QString metadataPath(
889 QStandardPaths::locate(QStandardPaths::GenericDataLocation,
890 QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % QStringLiteral("/metadata.desktop")));
891 KConfig metadata(metadataPath, KConfig::SimpleConfig);
892 processWallpaperSettings(&metadata);
893 }
894
895 // Check for what Plasma version the theme has been done
896 // There are some behavioral differences between KDE4 Plasma and Plasma 5
897 cg = KConfigGroup(&metadata, "Desktop Entry");
898 const QString apiVersion = cg.readEntry("X-Plasma-API", QString());
899 apiMajor = 1;
900 apiMinor = 0;
901 apiRevision = 0;
902 if (!apiVersion.isEmpty()) {
903 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
904 const QVector<QStringView> parts = QStringView(apiVersion).split(QLatin1Char('.'));
905 #else
906 const QVector<QStringRef> parts = apiVersion.splitRef(QLatin1Char('.'));
907 #endif
908 if (!parts.isEmpty()) {
909 apiMajor = parts.value(0).toInt();
910 }
911 if (parts.count() > 1) {
912 apiMinor = parts.value(1).toInt();
913 }
914 if (parts.count() > 2) {
915 apiRevision = parts.value(2).toInt();
916 }
917 }
918 }
919
920 if (realTheme && isDefault && writeSettings) {
921 // we're the default theme, let's save our status
922 KConfigGroup &cg = config();
923 cg.writeEntry("name", themeName);
924 cg.sync();
925 }
926
927 if (emitChanged) {
928 scheduleThemeChangeNotification(PixmapCache | SvgElementsCache);
929 }
930 }
931
eventFilter(QObject * watched,QEvent * event)932 bool ThemePrivate::eventFilter(QObject *watched, QEvent *event)
933 {
934 if (watched == QCoreApplication::instance()) {
935 if (event->type() == QEvent::ApplicationPaletteChange) {
936 colorsChanged();
937 }
938 if (event->type() == QEvent::ApplicationFontChange || event->type() == QEvent::FontChange) {
939 Q_EMIT defaultFontChanged();
940 Q_EMIT smallestFontChanged();
941 }
942 }
943 return QObject::eventFilter(watched, event);
944 }
945
946 }
947
948 #include "moc_theme_p.cpp"
949