Plasma

theme.cpp
1 /*
2  SPDX-FileCopyrightText: 2006-2007 Aaron Seigo <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "theme.h"
8 #include "private/svg_p.h"
9 #include "private/theme_p.h"
10 
11 #include <QFile>
12 #include <QFileInfo>
13 #include <QFontDatabase>
14 #include <QFontMetrics>
15 #include <QMutableListIterator>
16 #include <QPair>
17 #include <QStringBuilder>
18 #include <QThread>
19 #include <QTimer>
20 
21 #include "config-plasma.h"
22 
23 #include <KColorScheme>
24 #include <KConfigGroup>
25 #include <KDirWatch>
26 #include <KImageCache>
27 #include <KWindowEffects>
28 #include <QDebug>
29 #include <QStandardPaths>
30 
31 #include "debug_p.h"
32 
33 namespace Plasma
34 {
36  : QObject(parent)
37 {
38  if (!ThemePrivate::globalTheme) {
39  ThemePrivate::globalTheme = new ThemePrivate;
40  ThemePrivate::globalTheme->settingsChanged(false);
42  connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, ThemePrivate::globalTheme, &ThemePrivate::onAppExitCleanup);
43  }
44  }
45  ThemePrivate::globalTheme->ref.ref();
46  d = ThemePrivate::globalTheme;
47 
48  connect(d, &ThemePrivate::themeChanged, this, &Theme::themeChanged);
49  connect(d, &ThemePrivate::defaultFontChanged, this, &Theme::defaultFontChanged);
50  connect(d, &ThemePrivate::smallestFontChanged, this, &Theme::smallestFontChanged);
51 }
52 
53 Theme::Theme(const QString &themeName, QObject *parent)
54  : QObject(parent)
55 {
56  auto &priv = ThemePrivate::themes[themeName];
57  if (!priv) {
58  priv = new ThemePrivate;
60  connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, priv, &ThemePrivate::onAppExitCleanup);
61  }
62  }
63 
64  priv->ref.ref();
65  d = priv;
66 
67  // turn off caching so we don't accidentally trigger unnecessary disk activity at this point
68  bool useCache = d->cacheTheme;
69  d->cacheTheme = false;
70  d->setThemeName(themeName, false, false);
71  d->cacheTheme = useCache;
72  d->fixedName = true;
73  connect(d, &ThemePrivate::themeChanged, this, &Theme::themeChanged);
74 }
75 
76 Theme::~Theme()
77 {
78  if (d == ThemePrivate::globalTheme) {
79  if (!d->ref.deref()) {
80  disconnect(ThemePrivate::globalTheme, nullptr, this, nullptr);
81  delete ThemePrivate::globalTheme;
82  ThemePrivate::globalTheme = nullptr;
83  d = nullptr;
84  }
85  } else {
86  if (!d->ref.deref()) {
87  delete ThemePrivate::themes.take(d->themeName);
88  }
89  }
90 }
91 
92 void Theme::setThemeName(const QString &themeName)
93 {
94  if (d->themeName == themeName) {
95  return;
96  }
97 
98  if (d != ThemePrivate::globalTheme) {
99  disconnect(QCoreApplication::instance(), nullptr, d, nullptr);
100  if (!d->ref.deref()) {
101  delete ThemePrivate::themes.take(d->themeName);
102  }
103 
104  auto &priv = ThemePrivate::themes[themeName];
105  if (!priv) {
106  priv = new ThemePrivate;
108  connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, priv, &ThemePrivate::onAppExitCleanup);
109  }
110  }
111  priv->ref.ref();
112  d = priv;
113  connect(d, &ThemePrivate::themeChanged, this, &Theme::themeChanged);
114  }
115 
116  d->setThemeName(themeName, true, true);
117 }
118 
119 QString Theme::themeName() const
120 {
121  return d->themeName;
122 }
123 
124 QString Theme::imagePath(const QString &name) const
125 {
126  // look for a compressed svg file in the theme
127  if (name.contains(QLatin1String("../")) || name.isEmpty()) {
128  // we don't support relative paths
129  // qCDebug(LOG_PLASMA) << "Theme says: bad image path " << name;
130  return QString();
131  }
132 
133  const QString svgzName = name % QLatin1String(".svgz");
134  QString path = d->findInTheme(svgzName, d->themeName);
135 
136  if (path.isEmpty()) {
137  // try for an uncompressed svg file
138  const QString svgName = name % QLatin1String(".svg");
139  path = d->findInTheme(svgName, d->themeName);
140 
141  // search in fallback themes if necessary
142  for (int i = 0; path.isEmpty() && i < d->fallbackThemes.count(); ++i) {
143  if (d->themeName == d->fallbackThemes[i]) {
144  continue;
145  }
146 
147  // try a compressed svg file in the fallback theme
148  path = d->findInTheme(svgzName, d->fallbackThemes[i]);
149 
150  if (path.isEmpty()) {
151  // try an uncompressed svg file in the fallback theme
152  path = d->findInTheme(svgName, d->fallbackThemes[i]);
153  }
154  }
155  }
156 
157  return path;
158 }
159 
160 QString Theme::backgroundPath(const QString &image) const
161 {
162  return d->imagePath(themeName(), QStringLiteral("/appbackgrounds/"), image);
163 }
164 
165 QString Theme::styleSheet(const QString &css) const
166 {
167  return d->processStyleSheet(css, Svg::Status::Normal);
168 }
169 
170 QPalette Theme::palette() const
171 {
172  return d->palette;
173 }
174 
175 QPalette Theme::globalPalette()
176 {
177  if (!ThemePrivate::globalTheme) {
178  ThemePrivate::globalTheme = new ThemePrivate;
179  ThemePrivate::globalTheme->settingsChanged(false);
180  }
181  return ThemePrivate::globalTheme->palette;
182 }
183 
184 QString Theme::wallpaperPath(const QSize &size) const
185 {
186  QString fullPath;
187  QString image = d->defaultWallpaperTheme + QStringLiteral("/contents/images/%1x%2") + d->defaultWallpaperSuffix;
188  QString defaultImage = image.arg(d->defaultWallpaperWidth).arg(d->defaultWallpaperHeight);
189 
190  if (size.isValid()) {
191  // try to customize the paper to the size requested
192  // TODO: this should do better than just fallback to the default size.
193  // a "best fit" matching would be far better, so we don't end
194  // up returning a 1920x1200 wallpaper for a 640x480 request ;)
195  image = image.arg(size.width()).arg(size.height());
196  } else {
197  image = defaultImage;
198  }
199 
200  // TODO: the theme's wallpaper overrides regularly installed wallpapers.
201  // should it be possible for user installed (e.g. locateLocal) wallpapers
202  // to override the theme?
203  if (d->hasWallpapers) {
204  // check in the theme first
205  fullPath = d->findInTheme(QLatin1String("wallpapers/") % image, d->themeName);
206 
207  if (fullPath.isEmpty()) {
208  fullPath = d->findInTheme(QLatin1String("wallpapers/") % defaultImage, d->themeName);
209  }
210  }
211 
212  if (fullPath.isEmpty()) {
213  // we failed to find it in the theme, so look in the standard directories
214  // qCDebug(LOG_PLASMA) << "looking for" << image;
216  }
217 
218  if (fullPath.isEmpty()) {
219  // we still failed to find it in the theme, so look for the default in
220  // the standard directories
221  // qCDebug(LOG_PLASMA) << "looking for" << defaultImage;
222  fullPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("wallpapers/") + defaultImage);
223  }
224 
225  return fullPath;
226 }
227 
228 QString Theme::wallpaperPathForSize(int width, int height) const
229 {
230  return Plasma::Theme::wallpaperPath(QSize(width, height));
231 }
232 
233 bool Theme::currentThemeHasImage(const QString &name) const
234 {
235  if (name.contains(QLatin1String("../"))) {
236  // we don't support relative paths
237  return false;
238  }
239 
240  QString path = d->findInTheme(name % QLatin1String(".svgz"), d->themeName);
241  if (path.isEmpty()) {
242  path = d->findInTheme(name % QLatin1String(".svg"), d->themeName);
243  }
244  return path.contains(QLatin1String("/" PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % d->themeName);
245 }
246 
247 KSharedConfigPtr Theme::colorScheme() const
248 {
249  return d->colors;
250 }
251 
252 QColor Theme::color(ColorRole role, ColorGroup group) const
253 {
254  return d->color(role, group);
255 }
256 
257 void Theme::setUseGlobalSettings(bool useGlobal)
258 {
259  if (d->useGlobal == useGlobal) {
260  return;
261  }
262 
263  d->useGlobal = useGlobal;
264  d->cfg = KConfigGroup();
265  d->themeName.clear();
266  d->settingsChanged(true);
267 }
268 
269 bool Theme::useGlobalSettings() const
270 {
271  return d->useGlobal;
272 }
273 
274 bool Theme::findInCache(const QString &key, QPixmap &pix, unsigned int lastModified)
275 {
276  // TODO KF6: Make lastModified non-optional.
277  if (lastModified == 0) {
278  qCWarning(LOG_PLASMA) << "findInCache with a lastModified timestamp of 0 is deprecated";
279  return false;
280  }
281 
282  if (!d->useCache()) {
283  return false;
284  }
285 
286  if (lastModified > uint(d->pixmapCache->lastModifiedTime().toSecsSinceEpoch())) {
287  return false;
288  }
289 
290  const QString id = d->keysToCache.value(key);
291  const auto it = d->pixmapsToCache.constFind(id);
292  if (it != d->pixmapsToCache.constEnd()) {
293  pix = *it;
294  return !pix.isNull();
295  }
296 
297  QPixmap temp;
298  if (d->pixmapCache->findPixmap(key, &temp) && !temp.isNull()) {
299  pix = temp;
300  return true;
301  }
302 
303  return false;
304 }
305 
306 void Theme::insertIntoCache(const QString &key, const QPixmap &pix)
307 {
308  if (d->useCache()) {
309  d->pixmapCache->insertPixmap(key, pix);
310  }
311 }
312 
313 void Theme::insertIntoCache(const QString &key, const QPixmap &pix, const QString &id)
314 {
315  if (d->useCache()) {
316  d->pixmapsToCache[id] = pix;
317  d->keysToCache[key] = id;
318  d->idsToCache[id] = key;
319 
320  // always start timer in d->pixmapSaveTimer's thread
321  QMetaObject::invokeMethod(d->pixmapSaveTimer, "start", Qt::QueuedConnection);
322  }
323 }
324 
325 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 78)
326 bool Theme::findInRectsCache(const QString &image, const QString &element, QRectF &rect) const
327 {
328  if (!d->useCache()) {
329  return false;
330  }
331 
332  bool ok = false;
333  uint id = element.toLong(&ok);
334  if (!ok) {
335  return false;
336  }
337 
338  return SvgRectsCache::instance()->findElementRect(id, image, rect);
339 }
340 
342 {
343  if (!d->useCache()) {
344  return QStringList();
345  }
346 
347  return SvgRectsCache::instance()->cachedKeysForPath(image);
348 }
349 
350 void Theme::insertIntoRectsCache(const QString &image, const QString &element, const QRectF &rect)
351 {
352  if (!d->useCache()) {
353  return;
354  }
355 
356  bool ok = false;
357  uint id = element.toLong(&ok);
358  if (!ok) {
359  return;
360  }
361 
362  uint secs = QDateTime::currentSecsSinceEpoch();
363  SvgRectsCache::instance()->insert(id, image, rect, secs);
364 }
365 
367 {
368  SvgRectsCache::instance()->dropImageFromCache(image);
369 }
370 
372 {
373  Q_UNUSED(image);
374  // No op: the internal svg cache always writes the invalid elements in the proper place
375 }
376 #endif
377 
378 void Theme::setCacheLimit(int kbytes)
379 {
380  d->cacheSize = kbytes;
381  delete d->pixmapCache;
382  d->pixmapCache = nullptr;
383 }
384 
385 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 67)
387 {
388  return KPluginInfo(d->pluginMetaData);
389 }
390 #endif
391 
393 {
394  return d->pluginMetaData;
395 }
396 
397 QFont Theme::defaultFont() const
398 {
399  return QGuiApplication::font();
400 }
401 
402 QFont Theme::smallestFont() const
403 {
405 }
406 
407 QSizeF Theme::mSize(const QFont &font) const
408 {
409  return QFontMetrics(font).boundingRect(QStringLiteral("M")).size();
410 }
411 
413 {
414  return d->backgroundContrastEnabled;
415 }
416 
418 {
419  return d->adaptiveTransparencyEnabled;
420 }
421 
423 {
424  if (qIsNaN(d->backgroundContrast)) {
425  // Make up sensible default values, based on the background color
426  // If we're using a dark background color, darken the background
427  if (qGray(color(Plasma::Theme::BackgroundColor).rgb()) < 127) {
428  return 0.45;
429  // for a light theme lighten up the background
430  } else {
431  return 0.3;
432  }
433  }
434  return d->backgroundContrast;
435 }
436 
438 {
439  if (qIsNaN(d->backgroundIntensity)) {
440  if (qGray(color(Plasma::Theme::BackgroundColor).rgb()) < 127) {
441  return 0.45;
442  } else {
443  return 1.9;
444  }
445  }
446  return d->backgroundIntensity;
447 }
448 
450 {
451  if (qIsNaN(d->backgroundSaturation)) {
452  return 1.7;
453  }
454  return d->backgroundSaturation;
455 }
456 
458 {
459  return d->blurBehindEnabled;
460 }
461 
462 }
463 
464 #include "moc_theme.cpp"
void setUseGlobalSettings(bool useGlobal)
Tells the theme whether to follow the global settings or use application specific settings.
Definition: theme.cpp:257
void insertIntoCache(const QString &key, const QPixmap &pix)
Insert specified pixmap into the cache.
Definition: theme.cpp:306
qreal backgroundSaturation() const
This method allows Plasma to set a background contrast effect for a given theme, improving readabilit...
Definition: theme.cpp:449
bool isValid() const const
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
Namespace for everything in libplasma.
Definition: datamodel.cpp:14
QColor color(ColorRole role, ColorGroup group=NormalColorGroup) const
Returns the text color to be used by items resting on the background.
Definition: theme.cpp:252
QSize size() const const
void releaseRectsCache(const QString &image)
Frees up memory used by cached information for a given image without removing the permanent record of...
Definition: theme.cpp:371
KPluginInfo pluginInfo() const
Definition: theme.cpp:386
QFont systemFont(QFontDatabase::SystemFont type)
QRect boundingRect(QChar ch) const const
int width() const const
@ BackgroundColor
the default background color
Definition: theme.h:62
QString imagePath(const QString &name) const
Retrieve the path for an SVG image in the current theme.
Definition: theme.cpp:124
void smallestFontChanged()
Notifier for change of smallestFont property.
bool blurBehindEnabled() const
This method allows Plasma to enable and disable the blurring of what is behind the background for a g...
Definition: theme.cpp:457
QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
qint64 currentSecsSinceEpoch()
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
KPluginMetaData metadata() const
Definition: theme.cpp:392
KSharedConfigPtr colorScheme() const
Returns the color scheme configurationthat goes along this theme.
Definition: theme.cpp:247
int height() const const
bool isEmpty() const const
QCoreApplication * instance()
qreal backgroundContrast() const
This method allows Plasma to set a background contrast effect for a given theme, improving readabilit...
Definition: theme.cpp:422
QueuedConnection
void themeChanged()
Emitted when the user changes the theme.
void setCacheLimit(int kbytes)
Sets the maximum size of the cache (in kilobytes).
Definition: theme.cpp:378
void setThemeName(const QString &themeName)
Sets the current theme being used.
Definition: theme.cpp:92
Q_INVOKABLE QSizeF mSize(const QFont &font=QGuiApplication::font()) const
Returns the size of the letter "M" as rendered on the screen with the given font.
Definition: theme.cpp:407
void invalidateRectsCache(const QString &image)
Discards all the information about a given image from the rectangle disk cache.
Definition: theme.cpp:366
bool isNull() const const
bool backgroundContrastEnabled() const
This method allows Plasma to enable and disable the background contrast effect for a given theme,...
Definition: theme.cpp:412
bool findInRectsCache(const QString &image, const QString &element, QRectF &rect) const
Tries to load the rect of a sub element from a disk cache.
Definition: theme.cpp:326
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
bool adaptiveTransparencyEnabled() const
This method allows Plasma to enable and disable the adaptive transparency option of the panel,...
Definition: theme.cpp:417
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
void defaultFontChanged()
Notifier for change of defaultFont property.
qreal backgroundIntensity() const
This method allows Plasma to set a background contrast effect for a given theme, improving readabilit...
Definition: theme.cpp:437
Theme(QObject *parent=nullptr)
Default constructor.
Definition: theme.cpp:35
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool currentThemeHasImage(const QString &name) const
Checks if this theme has an image named in a certain way.
Definition: theme.cpp:233
bool findInCache(const QString &key, QPixmap &pix, unsigned int lastModified=0)
This is an overloaded member provided to check with file timestamp where cache is still valid.
Definition: theme.cpp:274
QStringList listCachedRectKeys(const QString &image) const
Returns a list of all keys of cached rects for the given image.
Definition: theme.cpp:341
void insertIntoRectsCache(const QString &image, const QString &element, const QRectF &rect)
Inserts a rectangle of a sub element of an image into a disk cache.
Definition: theme.cpp:350
long toLong(bool *ok, int base) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Feb 7 2023 04:15:02 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.