• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.11.5 API Reference
  • KDE Home
  • Contact Us
 

Plasma

  • plasma
theme.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2006-2007 Aaron Seigo <aseigo@kde.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Library General Public License as
6  * published by the Free Software Foundation; either version 2, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this program; if not, write to the
16  * Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 #include "theme.h"
21 
22 #include <QApplication>
23 #include <QFile>
24 #include <QFileInfo>
25 #include <QMutableListIterator>
26 #include <QPair>
27 #include <QStringBuilder>
28 #include <QTimer>
29 #ifdef Q_WS_X11
30 #include <QX11Info>
31 #include "private/effectwatcher_p.h"
32 #endif
33 
34 #include <kcolorscheme.h>
35 #include <kcomponentdata.h>
36 #include <kconfiggroup.h>
37 #include <kdebug.h>
38 #include <kdirwatch.h>
39 #include <kglobal.h>
40 #include <kglobalsettings.h>
41 #include <kmanagerselection.h>
42 #include <kimagecache.h>
43 #include <ksharedconfig.h>
44 #include <kstandarddirs.h>
45 #include <kwindowsystem.h>
46 
47 
48 #include "animations/animationscriptengine_p.h"
49 #include "libplasma-theme-global.h"
50 #include "private/packages_p.h"
51 #include "windoweffects.h"
52 
53 namespace Plasma
54 {
55 
56 //NOTE: Default wallpaper can be set from the theme configuration
57 #define DEFAULT_WALLPAPER_THEME "default"
58 #define DEFAULT_WALLPAPER_SUFFIX ".png"
59 static const int DEFAULT_WALLPAPER_WIDTH = 1920;
60 static const int DEFAULT_WALLPAPER_HEIGHT = 1200;
61 
62 enum styles {
63  DEFAULTSTYLE,
64  SVGSTYLE
65 };
66 
67 enum CacheType {
68  NoCache = 0,
69  PixmapCache = 1,
70  SvgElementsCache = 2
71 };
72 Q_DECLARE_FLAGS(CacheTypes, CacheType)
73 Q_DECLARE_OPERATORS_FOR_FLAGS(CacheTypes)
74 
75 class ThemePrivate
76 {
77 public:
78  ThemePrivate(Theme *theme)
79  : q(theme),
80  colorScheme(QPalette::Active, KColorScheme::Window, KSharedConfigPtr(0)),
81  buttonColorScheme(QPalette::Active, KColorScheme::Button, KSharedConfigPtr(0)),
82  viewColorScheme(QPalette::Active, KColorScheme::View, KSharedConfigPtr(0)),
83  defaultWallpaperTheme(DEFAULT_WALLPAPER_THEME),
84  defaultWallpaperSuffix(DEFAULT_WALLPAPER_SUFFIX),
85  defaultWallpaperWidth(DEFAULT_WALLPAPER_WIDTH),
86  defaultWallpaperHeight(DEFAULT_WALLPAPER_HEIGHT),
87  pixmapCache(0),
88  cachesToDiscard(NoCache),
89  locolor(false),
90  compositingActive(KWindowSystem::self()->compositingActive()),
91  blurActive(false),
92  isDefault(false),
93  useGlobal(true),
94  hasWallpapers(false),
95  useNativeWidgetStyle(false)
96  {
97  generalFont = QApplication::font();
98  ThemeConfig config;
99  cacheTheme = config.cacheTheme();
100 
101  saveTimer = new QTimer(q);
102  saveTimer->setSingleShot(true);
103  saveTimer->setInterval(600);
104  QObject::connect(saveTimer, SIGNAL(timeout()), q, SLOT(scheduledCacheUpdate()));
105 
106  updateNotificationTimer = new QTimer(q);
107  updateNotificationTimer->setSingleShot(true);
108  updateNotificationTimer->setInterval(500);
109  QObject::connect(updateNotificationTimer, SIGNAL(timeout()), q, SLOT(notifyOfChanged()));
110 
111  if (QPixmap::defaultDepth() > 8) {
112  QObject::connect(KWindowSystem::self(), SIGNAL(compositingChanged(bool)), q, SLOT(compositingChanged(bool)));
113 #ifdef Q_WS_X11
114  //watch for blur effect property changes as well
115  if (!s_blurEffectWatcher) {
116  s_blurEffectWatcher = new EffectWatcher("_KDE_NET_WM_BLUR_BEHIND_REGION");
117  }
118 
119  QObject::connect(s_blurEffectWatcher, SIGNAL(effectChanged(bool)), q, SLOT(blurBehindChanged(bool)));
120 #endif
121  }
122  }
123 
124  ~ThemePrivate()
125  {
126  delete pixmapCache;
127  }
128 
129  KConfigGroup &config()
130  {
131  if (!cfg.isValid()) {
132  QString groupName = "Theme";
133 
134  if (!useGlobal) {
135  QString app = KGlobal::mainComponent().componentName();
136 
137  if (!app.isEmpty()) {
138  kDebug() << "using theme for app" << app;
139  groupName.append("-").append(app);
140  }
141  }
142 
143  cfg = KConfigGroup(KSharedConfig::openConfig(themeRcFile), groupName);
144  }
145 
146  return cfg;
147  }
148 
149  QString findInTheme(const QString &image, const QString &theme, bool cache = true);
150  void compositingChanged(bool active);
151  void discardCache(CacheTypes caches);
152  void scheduledCacheUpdate();
153  void scheduleThemeChangeNotification(CacheTypes caches);
154  void notifyOfChanged();
155  void colorsChanged();
156  void blurBehindChanged(bool blur);
157  bool useCache();
158  void settingsFileChanged(const QString &);
159  void setThemeName(const QString &themeName, bool writeSettings);
160  void onAppExitCleanup();
161  void processWallpaperSettings(KConfigBase *metadata);
162  void processAnimationSettings(const QString &theme, KConfigBase *metadata);
163 
164  const QString processStyleSheet(const QString &css);
165 
166  static const char *defaultTheme;
167  static const char *systemColorsTheme;
168  static const char *themeRcFile;
169  static PackageStructure::Ptr packageStructure;
170 #ifdef Q_WS_X11
171  static EffectWatcher *s_blurEffectWatcher;
172 #endif
173 
174  Theme *q;
175  QString themeName;
176  QList<QString> fallbackThemes;
177  KSharedConfigPtr colors;
178  KColorScheme colorScheme;
179  KColorScheme buttonColorScheme;
180  KColorScheme viewColorScheme;
181  KConfigGroup cfg;
182  QFont generalFont;
183  QString defaultWallpaperTheme;
184  QString defaultWallpaperSuffix;
185  int defaultWallpaperWidth;
186  int defaultWallpaperHeight;
187  KImageCache *pixmapCache;
188  KSharedConfigPtr svgElementsCache;
189  QHash<QString, QSet<QString> > invalidElements;
190  QHash<QString, QPixmap> pixmapsToCache;
191  QHash<QString, QString> keysToCache;
192  QHash<QString, QString> idsToCache;
193  QHash<QString, QString> animationMapping;
194  QHash<styles, QString> cachedStyleSheets;
195  QHash<QString, QString> discoveries;
196  QTimer *saveTimer;
197  QTimer *updateNotificationTimer;
198  int toolTipDelay;
199  CacheTypes cachesToDiscard;
200 
201  bool locolor : 1;
202  bool compositingActive : 1;
203  bool blurActive : 1;
204  bool isDefault : 1;
205  bool useGlobal : 1;
206  bool hasWallpapers : 1;
207  bool cacheTheme : 1;
208  bool useNativeWidgetStyle :1;
209 };
210 
211 PackageStructure::Ptr ThemePrivate::packageStructure(0);
212 const char *ThemePrivate::defaultTheme = "default";
213 const char *ThemePrivate::themeRcFile = "plasmarc";
214 // the system colors theme is used to cache unthemed svgs with colorization needs
215 // these svgs do not follow the theme's colors, but rather the system colors
216 const char *ThemePrivate::systemColorsTheme = "internal-system-colors";
217 #ifdef Q_WS_X11
218 EffectWatcher *ThemePrivate::s_blurEffectWatcher = 0;
219 #endif
220 
221 bool ThemePrivate::useCache()
222 {
223  if (cacheTheme && !pixmapCache) {
224  const bool isRegularTheme = themeName != systemColorsTheme;
225  const QString cacheFile = "plasma_theme_" + themeName;
226 
227  if (isRegularTheme) {
228  const QString cacheFileBase = cacheFile + "*.kcache";
229 
230  const QString path = KStandardDirs::locate("data", "desktoptheme/" + themeName + "/metadata.desktop");
231  // if the path is empty, then we haven't found the theme and so
232  // we will leave currentCacheFileName empty, resulting in the deletion of
233  // *all* matching cache files
234  QString currentCacheFileName;
235  if (!path.isEmpty()) {
236  const KPluginInfo pluginInfo(path);
237  currentCacheFileName = cacheFile + "_v" + pluginInfo.version() + ".kcache";
238  }
239 
240  // now we check for (and remove) old caches
241  foreach (const QString &file, KGlobal::dirs()->findAllResources("cache", cacheFileBase)) {
242  if (currentCacheFileName.isEmpty() ||
243  !file.endsWith(currentCacheFileName)) {
244  QFile::remove(file);
245  }
246  }
247 
248  }
249 
250  ThemeConfig config;
251  pixmapCache = new KImageCache(cacheFile, config.themeCacheKb() * 1024);
252 
253  // now we do a sanity check: if the metadata.desktop file is newer than the cache, drop
254  // the cache
255  if (isRegularTheme) {
256  //check for expired cache
257  // FIXME: when using the system colors, if they change while the application is not running
258  // the cache should be dropped; we need a way to detect system color change when the
259  // application is not running.
260  const QFile f(cacheFile);
261  const QFileInfo fileInfo(f);
262  if (fileInfo.lastModified().toTime_t() > uint(pixmapCache->lastModifiedTime())) {
263  discardCache(PixmapCache | SvgElementsCache);
264  }
265  }
266  }
267 
268  return cacheTheme;
269 }
270 
271 void ThemePrivate::onAppExitCleanup()
272 {
273  pixmapsToCache.clear();
274  delete pixmapCache;
275  pixmapCache = 0;
276  cacheTheme = false;
277 }
278 
279 QString ThemePrivate::findInTheme(const QString &image, const QString &theme, bool cache)
280 {
281  if (cache && discoveries.contains(image)) {
282  return discoveries[image];
283  }
284 
285  QString search;
286 
287  if (locolor) {
288  search = QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/locolor/") % image;
289  search = KStandardDirs::locate("data", search);
290  } else if (!compositingActive) {
291  search = QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/opaque/") % image;
292  search = KStandardDirs::locate("data", search);
293  } else if (WindowEffects::isEffectAvailable(WindowEffects::BlurBehind)) {
294  search = QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/translucent/") % image;
295  search = KStandardDirs::locate("data", search);
296  }
297 
298  //not found or compositing enabled
299  if (search.isEmpty()) {
300  search = QLatin1Literal("desktoptheme/") % theme % QLatin1Char('/') % image;
301  search = KStandardDirs::locate("data", search);
302  }
303 
304  if (cache && !search.isEmpty()) {
305  discoveries.insert(image, search);
306  }
307 
308  return search;
309 }
310 
311 void ThemePrivate::compositingChanged(bool active)
312 {
313 #ifdef Q_WS_X11
314  if (compositingActive != active) {
315  compositingActive = active;
316  //kDebug() << QTime::currentTime();
317  scheduleThemeChangeNotification(PixmapCache | SvgElementsCache);
318  }
319 #endif
320 }
321 
322 void ThemePrivate::discardCache(CacheTypes caches)
323 {
324  if (caches & PixmapCache) {
325  pixmapsToCache.clear();
326  saveTimer->stop();
327  if (pixmapCache) {
328  pixmapCache->clear();
329  }
330  } else {
331  // This deletes the object but keeps the on-disk cache for later use
332  delete pixmapCache;
333  pixmapCache = 0;
334  }
335 
336  cachedStyleSheets.clear();
337 
338  if (caches & SvgElementsCache) {
339  discoveries.clear();
340  invalidElements.clear();
341 
342  if (svgElementsCache) {
343  QFile f(svgElementsCache->name());
344  svgElementsCache = 0;
345  f.remove();
346  }
347 
348  const QString svgElementsFile = KStandardDirs::locateLocal("cache", "plasma-svgelements-" + themeName);
349  svgElementsCache = KSharedConfig::openConfig(svgElementsFile);
350  }
351 }
352 
353 void ThemePrivate::scheduledCacheUpdate()
354 {
355  if (useCache()) {
356  QHashIterator<QString, QPixmap> it(pixmapsToCache);
357  while (it.hasNext()) {
358  it.next();
359  pixmapCache->insertPixmap(idsToCache[it.key()], it.value());
360  }
361  }
362 
363  pixmapsToCache.clear();
364  keysToCache.clear();
365  idsToCache.clear();
366 }
367 
368 void ThemePrivate::colorsChanged()
369 {
370  colorScheme = KColorScheme(QPalette::Active, KColorScheme::Window, colors);
371  buttonColorScheme = KColorScheme(QPalette::Active, KColorScheme::Button, colors);
372  viewColorScheme = KColorScheme(QPalette::Active, KColorScheme::View, colors);
373  scheduleThemeChangeNotification(PixmapCache);
374 }
375 
376 void ThemePrivate::blurBehindChanged(bool blur)
377 {
378  if (blurActive != blur) {
379  blurActive = blur;
380  scheduleThemeChangeNotification(PixmapCache | SvgElementsCache);
381  }
382 }
383 
384 void ThemePrivate::scheduleThemeChangeNotification(CacheTypes caches)
385 {
386  cachesToDiscard |= caches;
387  updateNotificationTimer->start();
388 }
389 
390 void ThemePrivate::notifyOfChanged()
391 {
392  //kDebug() << cachesToDiscard;
393  discardCache(cachesToDiscard);
394  cachesToDiscard = NoCache;
395  emit q->themeChanged();
396 }
397 
398 const QString ThemePrivate::processStyleSheet(const QString &css)
399 {
400  QString stylesheet;
401  if (css.isEmpty()) {
402  stylesheet = cachedStyleSheets.value(DEFAULTSTYLE);
403  if (stylesheet.isEmpty()) {
404  stylesheet = QString("\n\
405  body {\n\
406  color: %textcolor;\n\
407  font-size: %fontsize;\n\
408  font-family: %fontfamily;\n\
409  }\n\
410  a:active { color: %activatedlink; }\n\
411  a:link { color: %link; }\n\
412  a:visited { color: %visitedlink; }\n\
413  a:hover { color: %hoveredlink; text-decoration: none; }\n\
414  ");
415  stylesheet = processStyleSheet(stylesheet);
416  cachedStyleSheets.insert(DEFAULTSTYLE, stylesheet);
417  }
418 
419  return stylesheet;
420  } else if (css == "SVG") {
421  stylesheet = cachedStyleSheets.value(SVGSTYLE);
422  if (stylesheet.isEmpty()) {
423  QString skel = ".ColorScheme-%1{color:%2;}";
424 
425  stylesheet += skel.arg("Text","%textcolor");
426  stylesheet += skel.arg("Background","%backgroundcolor");
427 
428  stylesheet += skel.arg("ButtonText","%buttontextcolor");
429  stylesheet += skel.arg("ButtonBackground","%buttonbackgroundcolor");
430  stylesheet += skel.arg("ButtonHover","%buttonhovercolor");
431  stylesheet += skel.arg("ButtonFocus","%buttonfocuscolor");
432 
433  stylesheet += skel.arg("ViewText","%viewtextcolor");
434  stylesheet += skel.arg("ViewBackground","%viewbackgroundcolor");
435  stylesheet += skel.arg("ViewHover","%viewhovercolor");
436  stylesheet += skel.arg("ViewFocus","%viewfocuscolor");
437 
438  stylesheet = processStyleSheet(stylesheet);
439  cachedStyleSheets.insert(SVGSTYLE, stylesheet);
440  }
441 
442  return stylesheet;
443  } else {
444  stylesheet = css;
445  }
446 
447  QHash<QString, QString> elements;
448  // If you add elements here, make sure their names are sufficiently unique to not cause
449  // clashes between element keys
450  elements["%textcolor"] = q->color(Theme::TextColor).name();
451  elements["%backgroundcolor"] = q->color(Theme::BackgroundColor).name();
452  elements["%visitedlink"] = q->color(Theme::VisitedLinkColor).name();
453  elements["%activatedlink"] = q->color(Theme::HighlightColor).name();
454  elements["%hoveredlink"] = q->color(Theme::HighlightColor).name();
455  elements["%link"] = q->color(Theme::LinkColor).name();
456  elements["%buttontextcolor"] = q->color(Theme::ButtonTextColor).name();
457  elements["%buttonbackgroundcolor"] = q->color(Theme::ButtonBackgroundColor).name();
458  elements["%buttonhovercolor"] = q->color(Theme::ButtonHoverColor).name();
459  elements["%buttonfocuscolor"] = q->color(Theme::ButtonFocusColor).name();
460  elements["%viewtextcolor"] = q->color(Theme::ViewTextColor).name();
461  elements["%viewbackgroundcolor"] = q->color(Theme::ViewBackgroundColor).name();
462  elements["%viewhovercolor"] = q->color(Theme::ViewHoverColor).name();
463  elements["%viewfocuscolor"] = q->color(Theme::ViewFocusColor).name();
464 
465  QFont font = q->font(Theme::DefaultFont);
466  elements["%fontsize"] = QString("%1pt").arg(font.pointSize());
467  elements["%fontfamily"] = font.family().split('[').first();
468  elements["%smallfontsize"] = QString("%1pt").arg(KGlobalSettings::smallestReadableFont().pointSize());
469 
470  QHash<QString, QString>::const_iterator it = elements.constBegin();
471  QHash<QString, QString>::const_iterator itEnd = elements.constEnd();
472  for ( ; it != itEnd; ++it) {
473  stylesheet.replace(it.key(), it.value());
474  }
475  return stylesheet;
476 }
477 
478 class ThemeSingleton
479 {
480 public:
481  ThemeSingleton()
482  {
483  self.d->isDefault = true;
484 
485  //FIXME: if/when kconfig gets change notification, this will be unnecessary
486  KDirWatch::self()->addFile(KStandardDirs::locateLocal("config", ThemePrivate::themeRcFile));
487  QObject::connect(KDirWatch::self(), SIGNAL(created(QString)), &self, SLOT(settingsFileChanged(QString)));
488  QObject::connect(KDirWatch::self(), SIGNAL(dirty(QString)), &self, SLOT(settingsFileChanged(QString)));
489  }
490 
491  Theme self;
492 };
493 
494 K_GLOBAL_STATIC(ThemeSingleton, privateThemeSelf)
495 
496 Theme *Theme::defaultTheme()
497 {
498  return &privateThemeSelf->self;
499 }
500 
501 Theme::Theme(QObject *parent)
502  : QObject(parent),
503  d(new ThemePrivate(this))
504 {
505  settingsChanged();
506  if (QCoreApplication::instance()) {
507  connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()),
508  this, SLOT(onAppExitCleanup()));
509  }
510 }
511 
512 Theme::Theme(const QString &themeName, QObject *parent)
513  : QObject(parent),
514  d(new ThemePrivate(this))
515 {
516  // turn off caching so we don't accidently trigger unnecessary disk activity at this point
517  bool useCache = d->cacheTheme;
518  d->cacheTheme = false;
519  setThemeName(themeName);
520  d->cacheTheme = useCache;
521  if (QCoreApplication::instance()) {
522  connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()),
523  this, SLOT(onAppExitCleanup()));
524  }
525 }
526 
527 Theme::~Theme()
528 {
529  if (d->svgElementsCache) {
530  QHashIterator<QString, QSet<QString> > it(d->invalidElements);
531  while (it.hasNext()) {
532  it.next();
533  KConfigGroup imageGroup(d->svgElementsCache, it.key());
534  imageGroup.writeEntry("invalidElements", it.value().toList()); //FIXME: add QSet support to KConfig
535  }
536  }
537 
538  d->onAppExitCleanup();
539  delete d;
540 }
541 
542 PackageStructure::Ptr Theme::packageStructure()
543 {
544  if (!ThemePrivate::packageStructure) {
545  ThemePrivate::packageStructure = new ThemePackage();
546  }
547 
548  return ThemePrivate::packageStructure;
549 }
550 
551 KPluginInfo::List Theme::listThemeInfo()
552 {
553  const QStringList themes = KGlobal::dirs()->findAllResources("data", "desktoptheme/*/metadata.desktop",
554  KStandardDirs::NoDuplicates);
555  return KPluginInfo::fromFiles(themes);
556 }
557 
558 void ThemePrivate::settingsFileChanged(const QString &file)
559 {
560  if (file.endsWith(themeRcFile)) {
561  config().config()->reparseConfiguration();
562  q->settingsChanged();
563  }
564 }
565 
566 void Theme::settingsChanged()
567 {
568  KConfigGroup cg = d->config();
569  d->setThemeName(cg.readEntry("name", ThemePrivate::defaultTheme), false);
570  cg = KConfigGroup(cg.config(), "PlasmaToolTips");
571  d->toolTipDelay = cg.readEntry("Delay", 700);
572 }
573 
574 void Theme::setThemeName(const QString &themeName)
575 {
576  d->setThemeName(themeName, true);
577 }
578 
579 void ThemePrivate::processWallpaperSettings(KConfigBase *metadata)
580 {
581  if (!defaultWallpaperTheme.isEmpty() && defaultWallpaperTheme != DEFAULT_WALLPAPER_THEME) {
582  return;
583  }
584 
585  KConfigGroup cg;
586  if (metadata->hasGroup("Wallpaper")) {
587  // we have a theme color config, so let's also check to see if
588  // there is a wallpaper defined in there.
589  cg = KConfigGroup(metadata, "Wallpaper");
590  } else {
591  // since we didn't find an entry in the theme, let's look in the main
592  // theme config
593  cg = config();
594  }
595 
596  defaultWallpaperTheme = cg.readEntry("defaultWallpaperTheme", DEFAULT_WALLPAPER_THEME);
597  defaultWallpaperSuffix = cg.readEntry("defaultFileSuffix", DEFAULT_WALLPAPER_SUFFIX);
598  defaultWallpaperWidth = cg.readEntry("defaultWidth", DEFAULT_WALLPAPER_WIDTH);
599  defaultWallpaperHeight = cg.readEntry("defaultHeight", DEFAULT_WALLPAPER_HEIGHT);
600 }
601 
602 void ThemePrivate::processAnimationSettings(const QString &theme, KConfigBase *metadata)
603 {
604  KConfigGroup cg(metadata, "Animations");
605  const QString animDir = QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/animations/");
606  foreach (const QString &path, cg.keyList()) {
607  const QStringList anims = cg.readEntry(path, QStringList());
608  foreach (const QString &anim, anims) {
609  if (!animationMapping.contains(anim)) {
610  kDebug() << "Registering animation. animDir: " << animDir
611  << "\tanim: " << anim
612  << "\tpath: " << path << "\t*******\n\n\n";
613  //key: desktoptheme/default/animations/+ all.js
614  //value: ZoomAnimation
615  animationMapping.insert(anim, animDir % path);
616  } else {
617  kDebug() << "************Animation already registered!\n\n\n";
618  }
619  }
620  }
621 
622 }
623 
624 void ThemePrivate::setThemeName(const QString &tempThemeName, bool writeSettings)
625 {
626  //kDebug() << tempThemeName;
627  QString theme = tempThemeName;
628  if (theme.isEmpty() || theme == themeName) {
629  // let's try and get the default theme at least
630  if (themeName.isEmpty()) {
631  theme = ThemePrivate::defaultTheme;
632  } else {
633  return;
634  }
635  }
636 
637  // we have one special theme: essentially a dummy theme used to cache things with
638  // the system colors.
639  bool realTheme = theme != systemColorsTheme;
640  if (realTheme) {
641  QString themePath = KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Char('/'));
642  if (themePath.isEmpty() && themeName.isEmpty()) {
643  themePath = KStandardDirs::locate("data", "desktoptheme/default/");
644 
645  if (themePath.isEmpty()) {
646  return;
647  }
648 
649  theme = ThemePrivate::defaultTheme;
650  }
651  }
652 
653  // check again as ThemePrivate::defaultTheme might be empty
654  if (themeName == theme) {
655  return;
656  }
657 
658  themeName = theme;
659 
660  // load the color scheme config
661  const QString colorsFile = realTheme ? KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/colors"))
662  : QString();
663 
664  //kDebug() << "we're going for..." << colorsFile << "*******************";
665 
666  // load the wallpaper settings, if any
667  if (realTheme) {
668  const QString metadataPath(KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/metadata.desktop")));
669  KConfig metadata(metadataPath);
670 
671  processWallpaperSettings(&metadata);
672 
673  AnimationScriptEngine::clearAnimations();
674  animationMapping.clear();
675  processAnimationSettings(themeName, &metadata);
676 
677  KConfigGroup cg(&metadata, "Settings");
678  useNativeWidgetStyle = cg.readEntry("UseNativeWidgetStyle", false);
679  QString fallback = cg.readEntry("FallbackTheme", QString());
680 
681  fallbackThemes.clear();
682  while (!fallback.isEmpty() && !fallbackThemes.contains(fallback)) {
683  fallbackThemes.append(fallback);
684 
685  QString metadataPath(KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/metadata.desktop")));
686  KConfig metadata(metadataPath);
687  KConfigGroup cg(&metadata, "Settings");
688  fallback = cg.readEntry("FallbackTheme", QString());
689  }
690 
691  if (!fallbackThemes.contains("oxygen")) {
692  fallbackThemes.append("oxygen");
693  }
694 
695  if (!fallbackThemes.contains(ThemePrivate::defaultTheme)) {
696  fallbackThemes.append(ThemePrivate::defaultTheme);
697  }
698 
699  foreach (const QString &theme, fallbackThemes) {
700  QString metadataPath(KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/metadata.desktop")));
701  KConfig metadata(metadataPath);
702  processAnimationSettings(theme, &metadata);
703  processWallpaperSettings(&metadata);
704  }
705  }
706 
707  if (colorsFile.isEmpty()) {
708  colors = 0;
709  QObject::connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
710  q, SLOT(colorsChanged()), Qt::UniqueConnection);
711  } else {
712  QObject::disconnect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
713  q, SLOT(colorsChanged()));
714  colors = KSharedConfig::openConfig(colorsFile);
715  }
716 
717  colorScheme = KColorScheme(QPalette::Active, KColorScheme::Window, colors);
718  buttonColorScheme = KColorScheme(QPalette::Active, KColorScheme::Button, colors);
719  viewColorScheme = KColorScheme(QPalette::Active, KColorScheme::View, colors);
720  hasWallpapers = KStandardDirs::exists(KStandardDirs::locateLocal("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/wallpapers/")));
721 
722  if (realTheme && isDefault && writeSettings) {
723  // we're the default theme, let's save our state
724  KConfigGroup &cg = config();
725  if (ThemePrivate::defaultTheme == themeName) {
726  cg.deleteEntry("name");
727  } else {
728  cg.writeEntry("name", themeName);
729  }
730  cg.sync();
731  }
732 
733  scheduleThemeChangeNotification(SvgElementsCache);
734 }
735 
736 QString Theme::themeName() const
737 {
738  return d->themeName;
739 }
740 
741 QString Theme::imagePath(const QString &name) const
742 {
743  // look for a compressed svg file in the theme
744  if (name.contains("../") || name.isEmpty()) {
745  // we don't support relative paths
746  //kDebug() << "Theme says: bad image path " << name;
747  return QString();
748  }
749 
750  const QString svgzName = name % QLatin1Literal(".svgz");
751  QString path = d->findInTheme(svgzName, d->themeName);
752 
753  if (path.isEmpty()) {
754  // try for an uncompressed svg file
755  const QString svgName = name % QLatin1Literal(".svg");
756  path = d->findInTheme(svgName, d->themeName);
757 
758  // search in fallback themes if necessary
759  for (int i = 0; path.isEmpty() && i < d->fallbackThemes.count(); ++i) {
760  if (d->themeName == d->fallbackThemes[i]) {
761  continue;
762  }
763 
764  // try a compressed svg file in the fallback theme
765  path = d->findInTheme(svgzName, d->fallbackThemes[i]);
766 
767  if (path.isEmpty()) {
768  // try an uncompressed svg file in the fallback theme
769  path = d->findInTheme(svgName, d->fallbackThemes[i]);
770  }
771  }
772  }
773 
774  /*
775  if (path.isEmpty()) {
776  kDebug() << "Theme says: bad image path " << name;
777  }
778  */
779 
780  return path;
781 }
782 
783 QString Theme::styleSheet(const QString &css) const
784 {
785  return d->processStyleSheet(css);
786 }
787 
788 QString Theme::animationPath(const QString &name) const
789 {
790  const QString path = d->animationMapping.value(name);
791  if (path.isEmpty()) {
792  //kError() << "****** FAILED TO FIND IN MAPPING!";
793  return path;
794  }
795 
796  return KStandardDirs::locate("data", path);
797 }
798 
799 QString Theme::wallpaperPath(const QSize &size) const
800 {
801  QString fullPath;
802  QString image = d->defaultWallpaperTheme;
803 
804  image.append("/contents/images/%1x%2").append(d->defaultWallpaperSuffix);
805  QString defaultImage = image.arg(d->defaultWallpaperWidth).arg(d->defaultWallpaperHeight);
806 
807  if (size.isValid()) {
808  // try to customize the paper to the size requested
809  //TODO: this should do better than just fallback to the default size.
810  // a "best fit" matching would be far better, so we don't end
811  // up returning a 1920x1200 wallpaper for a 640x480 request ;)
812  image = image.arg(size.width()).arg(size.height());
813  } else {
814  image = defaultImage;
815  }
816 
817  //TODO: the theme's wallpaper overrides regularly installed wallpapers.
818  // should it be possible for user installed (e.g. locateLocal) wallpapers
819  // to override the theme?
820  if (d->hasWallpapers) {
821  // check in the theme first
822  fullPath = d->findInTheme(QLatin1Literal("wallpapers/") % image, d->themeName);
823 
824  if (fullPath.isEmpty()) {
825  fullPath = d->findInTheme(QLatin1Literal("wallpapers/") % defaultImage, d->themeName);
826  }
827  }
828 
829  if (fullPath.isEmpty()) {
830  // we failed to find it in the theme, so look in the standard directories
831  //kDebug() << "looking for" << image;
832  fullPath = KStandardDirs::locate("wallpaper", image);
833  }
834 
835  if (fullPath.isEmpty()) {
836  // we still failed to find it in the theme, so look for the default in
837  // the standard directories
838  //kDebug() << "looking for" << defaultImage;
839  fullPath = KStandardDirs::locate("wallpaper", defaultImage);
840 
841  if (fullPath.isEmpty()) {
842  kDebug() << "exhausted every effort to find a wallpaper.";
843  }
844  }
845 
846  return fullPath;
847 }
848 
849 bool Theme::currentThemeHasImage(const QString &name) const
850 {
851  if (name.contains("../")) {
852  // we don't support relative paths
853  return false;
854  }
855 
856  return !(d->findInTheme(name % QLatin1Literal(".svgz"), d->themeName, false).isEmpty()) ||
857  !(d->findInTheme(name % QLatin1Literal(".svg"), d->themeName, false).isEmpty());
858 }
859 
860 KSharedConfigPtr Theme::colorScheme() const
861 {
862  return d->colors;
863 }
864 
865 QColor Theme::color(ColorRole role) const
866 {
867  switch (role) {
868  case TextColor:
869  return d->colorScheme.foreground(KColorScheme::NormalText).color();
870 
871  case HighlightColor:
872  return d->colorScheme.decoration(KColorScheme::HoverColor).color();
873 
874  case BackgroundColor:
875  return d->colorScheme.background(KColorScheme::NormalBackground).color();
876 
877  case ButtonTextColor:
878  return d->buttonColorScheme.foreground(KColorScheme::NormalText).color();
879 
880  case ButtonBackgroundColor:
881  return d->buttonColorScheme.background(KColorScheme::NormalBackground).color();
882 
883  case ButtonHoverColor:
884  return d->buttonColorScheme.decoration(KColorScheme::HoverColor).color();
885 
886  case ButtonFocusColor:
887  return d->buttonColorScheme.decoration(KColorScheme::FocusColor).color();
888 
889  case ViewTextColor:
890  return d->viewColorScheme.foreground(KColorScheme::NormalText).color();
891 
892  case ViewBackgroundColor:
893  return d->viewColorScheme.background(KColorScheme::NormalBackground).color();
894 
895  case ViewHoverColor:
896  return d->viewColorScheme.decoration(KColorScheme::HoverColor).color();
897 
898  case ViewFocusColor:
899  return d->viewColorScheme.decoration(KColorScheme::FocusColor).color();
900 
901  case LinkColor:
902  return d->viewColorScheme.foreground(KColorScheme::LinkText).color();
903 
904  case VisitedLinkColor:
905  return d->viewColorScheme.foreground(KColorScheme::VisitedText).color();
906  }
907 
908  return QColor();
909 }
910 
911 void Theme::setFont(const QFont &font, FontRole role)
912 {
913  Q_UNUSED(role)
914  d->generalFont = font;
915 }
916 
917 QFont Theme::font(FontRole role) const
918 {
919  switch (role) {
920  case DesktopFont: {
921  KConfigGroup cg(KGlobal::config(), "General");
922  return cg.readEntry("desktopFont", d->generalFont);
923  }
924  break;
925 
926  case DefaultFont:
927  default:
928  return d->generalFont;
929  break;
930 
931  case SmallestFont:
932  return KGlobalSettings::smallestReadableFont();
933  break;
934  }
935 
936  return d->generalFont;
937 }
938 
939 QFontMetrics Theme::fontMetrics() const
940 {
941  //TODO: allow this to be overridden with a plasma specific font?
942  return QFontMetrics(d->generalFont);
943 }
944 
945 bool Theme::windowTranslucencyEnabled() const
946 {
947  return d->compositingActive;
948 }
949 
950 void Theme::setUseGlobalSettings(bool useGlobal)
951 {
952  if (d->useGlobal == useGlobal) {
953  return;
954  }
955 
956  d->useGlobal = useGlobal;
957  d->cfg = KConfigGroup();
958  d->themeName.clear();
959  settingsChanged();
960 }
961 
962 bool Theme::useGlobalSettings() const
963 {
964  return d->useGlobal;
965 }
966 
967 bool Theme::useNativeWidgetStyle() const
968 {
969  return d->useNativeWidgetStyle;
970 }
971 
972 bool Theme::findInCache(const QString &key, QPixmap &pix)
973 {
974  if (d->useCache()) {
975  const QString id = d->keysToCache.value(key);
976  if (d->pixmapsToCache.contains(id)) {
977  pix = d->pixmapsToCache.value(id);
978  return !pix.isNull();
979  }
980 
981  QPixmap temp;
982  if (d->pixmapCache->findPixmap(key, &temp) && !temp.isNull()) {
983  pix = temp;
984  return true;
985  }
986  }
987 
988  return false;
989 }
990 
991 // BIC FIXME: Should be merged with the other findInCache method above when we break BC
992 bool Theme::findInCache(const QString &key, QPixmap &pix, unsigned int lastModified)
993 {
994  if (d->useCache() && lastModified > uint(d->pixmapCache->lastModifiedTime())) {
995  return false;
996  }
997 
998  return findInCache(key, pix);
999 }
1000 
1001 void Theme::insertIntoCache(const QString& key, const QPixmap& pix)
1002 {
1003  if (d->useCache()) {
1004  d->pixmapCache->insertPixmap(key, pix);
1005  }
1006 }
1007 
1008 void Theme::insertIntoCache(const QString& key, const QPixmap& pix, const QString& id)
1009 {
1010  if (d->useCache()) {
1011  d->pixmapsToCache.insert(id, pix);
1012 
1013  if (d->idsToCache.contains(id)) {
1014  d->keysToCache.remove(d->idsToCache[id]);
1015  }
1016 
1017  d->keysToCache.insert(key, id);
1018  d->idsToCache.insert(id, key);
1019  d->saveTimer->start();
1020  }
1021 }
1022 
1023 bool Theme::findInRectsCache(const QString &image, const QString &element, QRectF &rect) const
1024 {
1025  if (!d->svgElementsCache) {
1026  return false;
1027  }
1028 
1029  KConfigGroup imageGroup(d->svgElementsCache, image);
1030  rect = imageGroup.readEntry(element % QLatin1Literal("Size"), QRectF());
1031 
1032  if (rect.isValid()) {
1033  return true;
1034  }
1035 
1036  //Name starting by _ means the element is empty and we're asked for the size of
1037  //the whole image, so the whole image is never invalid
1038  if (element.indexOf('_') <= 0) {
1039  return false;
1040  }
1041 
1042  bool invalid = false;
1043 
1044  QHash<QString, QSet<QString> >::iterator it = d->invalidElements.find(image);
1045  if (it == d->invalidElements.end()) {
1046  QSet<QString> elements = imageGroup.readEntry("invalidElements", QStringList()).toSet();
1047  d->invalidElements.insert(image, elements);
1048  invalid = elements.contains(element);
1049  } else {
1050  invalid = it.value().contains(element);
1051  }
1052 
1053  return invalid;
1054 }
1055 
1056 QStringList Theme::listCachedRectKeys(const QString &image) const
1057 {
1058  if (!d->svgElementsCache) {
1059  return QStringList();
1060  }
1061 
1062  KConfigGroup imageGroup(d->svgElementsCache, image);
1063  QStringList keys = imageGroup.keyList();
1064 
1065  QMutableListIterator<QString> i(keys);
1066  while (i.hasNext()) {
1067  QString key = i.next();
1068  if (key.endsWith("Size")) {
1069  // The actual cache id used from outside doesn't end on "Size".
1070  key.resize(key.size() - 4);
1071  i.setValue(key);
1072  } else {
1073  i.remove();
1074  }
1075  }
1076  return keys;
1077 }
1078 
1079 void Theme::insertIntoRectsCache(const QString& image, const QString &element, const QRectF &rect)
1080 {
1081  if (!d->svgElementsCache) {
1082  return;
1083  }
1084 
1085  if (rect.isValid()) {
1086  KConfigGroup imageGroup(d->svgElementsCache, image);
1087  imageGroup.writeEntry(element % QLatin1Literal("Size"), rect);
1088  } else {
1089  QHash<QString, QSet<QString> >::iterator it = d->invalidElements.find(image);
1090  if (it == d->invalidElements.end()) {
1091  d->invalidElements[image].insert(element);
1092  } else if (!it.value().contains(element)) {
1093  if (it.value().count() > 1000) {
1094  it.value().erase(it.value().begin());
1095  }
1096 
1097  it.value().insert(element);
1098  }
1099  }
1100 }
1101 
1102 void Theme::invalidateRectsCache(const QString& image)
1103 {
1104  if (d->svgElementsCache) {
1105  KConfigGroup imageGroup(d->svgElementsCache, image);
1106  imageGroup.deleteGroup();
1107  }
1108 
1109  d->invalidElements.remove(image);
1110 }
1111 
1112 void Theme::releaseRectsCache(const QString &image)
1113 {
1114  QHash<QString, QSet<QString> >::iterator it = d->invalidElements.find(image);
1115  if (it != d->invalidElements.end()) {
1116  if (!d->svgElementsCache) {
1117  KConfigGroup imageGroup(d->svgElementsCache, it.key());
1118  imageGroup.writeEntry("invalidElements", it.value().toList());
1119  }
1120 
1121  d->invalidElements.erase(it);
1122  }
1123 }
1124 
1125 void Theme::setCacheLimit(int kbytes)
1126 {
1127  Q_UNUSED(kbytes)
1128  if (d->useCache()) {
1129  ;
1130  // Too late for you bub.
1131  // d->pixmapCache->setCacheLimit(kbytes);
1132  }
1133 }
1134 
1135 KUrl Theme::homepage() const
1136 {
1137  const QString metadataPath(KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % d->themeName % QLatin1Literal("/metadata.desktop")));
1138  KConfig metadata(metadataPath);
1139  KConfigGroup brandConfig(&metadata, "Branding");
1140  return brandConfig.readEntry("homepage", KUrl("http://www.kde.org"));
1141 }
1142 
1143 int Theme::toolTipDelay() const
1144 {
1145  return d->toolTipDelay;
1146 }
1147 
1148 }
1149 
1150 #include <theme.moc>
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Thu Sep 25 2014 04:19:21 by doxygen 1.8.3.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

Plasma

Skip menu "Plasma"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.11.5 API Reference

Skip menu "kdelibs-4.11.5 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal