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

Plasma

theme.cpp
Go to the documentation of this file.
00001 /*
00002  *   Copyright 2006-2007 Aaron Seigo <aseigo@kde.org>
00003  *
00004  *   This program is free software; you can redistribute it and/or modify
00005  *   it under the terms of the GNU Library General Public License as
00006  *   published by the Free Software Foundation; either version 2, or
00007  *   (at your option) any later version.
00008  *
00009  *   This program is distributed in the hope that it will be useful,
00010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *   GNU General Public License for more details
00013  *
00014  *   You should have received a copy of the GNU Library General Public
00015  *   License along with this program; if not, write to the
00016  *   Free Software Foundation, Inc.,
00017  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00018  */
00019 
00020 #include "theme.h"
00021 
00022 #include <QApplication>
00023 #include <QFile>
00024 #include <QFileInfo>
00025 #include <QMutableListIterator>
00026 #include <QPair>
00027 #include <QStringBuilder>
00028 #include <QTimer>
00029 #ifdef Q_WS_X11
00030 #include <QX11Info>
00031 #include "private/effectwatcher_p.h"
00032 #endif
00033 
00034 #include <kcolorscheme.h>
00035 #include <kcomponentdata.h>
00036 #include <kconfiggroup.h>
00037 #include <kdebug.h>
00038 #include <kdirwatch.h>
00039 #include <kglobal.h>
00040 #include <kglobalsettings.h>
00041 #include <kmanagerselection.h>
00042 #include <kimagecache.h>
00043 #include <ksharedconfig.h>
00044 #include <kstandarddirs.h>
00045 #include <kwindowsystem.h>
00046 
00047 
00048 #include "animations/animationscriptengine_p.h"
00049 #include "libplasma-theme-global.h"
00050 #include "private/packages_p.h"
00051 #include "windoweffects.h"
00052 
00053 namespace Plasma
00054 {
00055 
00056 //NOTE: Default wallpaper can be set from the theme configuration
00057 #define DEFAULT_WALLPAPER_THEME "default"
00058 #define DEFAULT_WALLPAPER_SUFFIX ".png"
00059 static const int DEFAULT_WALLPAPER_WIDTH = 1920;
00060 static const int DEFAULT_WALLPAPER_HEIGHT = 1200;
00061 
00062 enum styles {
00063     DEFAULTSTYLE,
00064     SVGSTYLE
00065 };
00066 
00067 enum CacheType {
00068     NoCache = 0,
00069     PixmapCache = 1,
00070     SvgElementsCache = 2
00071 };
00072 Q_DECLARE_FLAGS(CacheTypes, CacheType)
00073 Q_DECLARE_OPERATORS_FOR_FLAGS(CacheTypes)
00074 
00075 class ThemePrivate
00076 {
00077 public:
00078     ThemePrivate(Theme *theme)
00079         : q(theme),
00080           colorScheme(QPalette::Active, KColorScheme::Window, KSharedConfigPtr(0)),
00081           buttonColorScheme(QPalette::Active, KColorScheme::Button, KSharedConfigPtr(0)),
00082           viewColorScheme(QPalette::Active, KColorScheme::View, KSharedConfigPtr(0)),
00083           defaultWallpaperTheme(DEFAULT_WALLPAPER_THEME),
00084           defaultWallpaperSuffix(DEFAULT_WALLPAPER_SUFFIX),
00085           defaultWallpaperWidth(DEFAULT_WALLPAPER_WIDTH),
00086           defaultWallpaperHeight(DEFAULT_WALLPAPER_HEIGHT),
00087           pixmapCache(0),
00088           cachesToDiscard(NoCache),
00089           locolor(false),
00090           compositingActive(KWindowSystem::self()->compositingActive()),
00091           blurActive(false),
00092           isDefault(false),
00093           useGlobal(true),
00094           hasWallpapers(false),
00095           useNativeWidgetStyle(false)
00096     {
00097         generalFont = QApplication::font();
00098         ThemeConfig config;
00099         cacheTheme = config.cacheTheme();
00100 
00101         saveTimer = new QTimer(q);
00102         saveTimer->setSingleShot(true);
00103         saveTimer->setInterval(600);
00104         QObject::connect(saveTimer, SIGNAL(timeout()), q, SLOT(scheduledCacheUpdate()));
00105 
00106         updateNotificationTimer = new QTimer(q);
00107         updateNotificationTimer->setSingleShot(true);
00108         updateNotificationTimer->setInterval(500);
00109         QObject::connect(updateNotificationTimer, SIGNAL(timeout()), q, SLOT(notifyOfChanged()));
00110 
00111         if (QPixmap::defaultDepth() > 8) {
00112             QObject::connect(KWindowSystem::self(), SIGNAL(compositingChanged(bool)), q, SLOT(compositingChanged(bool)));
00113 #ifdef Q_WS_X11
00114             //watch for blur effect property changes as well
00115             if (!s_blurEffectWatcher) {
00116                 s_blurEffectWatcher = new EffectWatcher("_KDE_NET_WM_BLUR_BEHIND_REGION");
00117             }
00118 
00119             QObject::connect(s_blurEffectWatcher, SIGNAL(effectChanged(bool)), q, SLOT(blurBehindChanged(bool)));
00120 #endif
00121         }
00122     }
00123 
00124     ~ThemePrivate()
00125     {
00126        delete pixmapCache;
00127     }
00128 
00129     KConfigGroup &config()
00130     {
00131         if (!cfg.isValid()) {
00132             QString groupName = "Theme";
00133 
00134             if (!useGlobal) {
00135                 QString app = KGlobal::mainComponent().componentName();
00136 
00137                 if (!app.isEmpty()) {
00138                     kDebug() << "using theme for app" << app;
00139                     groupName.append("-").append(app);
00140                 }
00141             }
00142 
00143             cfg = KConfigGroup(KSharedConfig::openConfig(themeRcFile), groupName);
00144         }
00145 
00146         return cfg;
00147     }
00148 
00149     QString findInTheme(const QString &image, const QString &theme, bool cache = true);
00150     void compositingChanged(bool active);
00151     void discardCache(CacheTypes caches);
00152     void scheduledCacheUpdate();
00153     void scheduleThemeChangeNotification(CacheTypes caches);
00154     void notifyOfChanged();
00155     void colorsChanged();
00156     void blurBehindChanged(bool blur);
00157     bool useCache();
00158     void settingsFileChanged(const QString &);
00159     void setThemeName(const QString &themeName, bool writeSettings);
00160     void onAppExitCleanup();
00161     void processWallpaperSettings(KConfigBase *metadata);
00162     void processAnimationSettings(const QString &theme, KConfigBase *metadata);
00163 
00164     const QString processStyleSheet(const QString &css);
00165 
00166     static const char *defaultTheme;
00167     static const char *systemColorsTheme;
00168     static const char *themeRcFile;
00169     static PackageStructure::Ptr packageStructure;
00170 #ifdef Q_WS_X11
00171     static EffectWatcher *s_blurEffectWatcher;
00172 #endif
00173     
00174     Theme *q;
00175     QString themeName;
00176     QList<QString> fallbackThemes;
00177     KSharedConfigPtr colors;
00178     KColorScheme colorScheme;
00179     KColorScheme buttonColorScheme;
00180     KColorScheme viewColorScheme;
00181     KConfigGroup cfg;
00182     QFont generalFont;
00183     QString defaultWallpaperTheme;
00184     QString defaultWallpaperSuffix;
00185     int defaultWallpaperWidth;
00186     int defaultWallpaperHeight;
00187     KImageCache *pixmapCache;
00188     KSharedConfigPtr svgElementsCache;
00189     QHash<QString, QSet<QString> > invalidElements;
00190     QHash<QString, QPixmap> pixmapsToCache;
00191     QHash<QString, QString> keysToCache;
00192     QHash<QString, QString> idsToCache;
00193     QHash<QString, QString> animationMapping;
00194     QHash<styles, QString> cachedStyleSheets;
00195     QHash<QString, QString> discoveries;
00196     QTimer *saveTimer;
00197     QTimer *updateNotificationTimer;
00198     int toolTipDelay;
00199     CacheTypes cachesToDiscard;
00200 
00201     bool locolor : 1;
00202     bool compositingActive : 1;
00203     bool blurActive : 1;
00204     bool isDefault : 1;
00205     bool useGlobal : 1;
00206     bool hasWallpapers : 1;
00207     bool cacheTheme : 1;
00208     bool useNativeWidgetStyle :1;
00209 };
00210 
00211 PackageStructure::Ptr ThemePrivate::packageStructure(0);
00212 const char *ThemePrivate::defaultTheme = "default";
00213 const char *ThemePrivate::themeRcFile = "plasmarc";
00214 // the system colors theme is used to cache unthemed svgs with colorization needs
00215 // these svgs do not follow the theme's colors, but rather the system colors
00216 const char *ThemePrivate::systemColorsTheme = "internal-system-colors";
00217 #ifdef Q_WS_X11
00218 EffectWatcher *ThemePrivate::s_blurEffectWatcher = 0;
00219 #endif
00220 
00221 bool ThemePrivate::useCache()
00222 {
00223     if (cacheTheme && !pixmapCache) {
00224         ThemeConfig config;
00225         pixmapCache = new KImageCache("plasma_theme_" + themeName, config.themeCacheKb() * 1024);
00226         if (themeName != systemColorsTheme) {
00227             //check for expired cache
00228             // FIXME: when using the system colors, if they change while the application is not running
00229             // the cache should be dropped; we need a way to detect system color change when the
00230             // application is not running.
00231             QFile f(KStandardDirs::locate("data", "desktoptheme/" + themeName + "/metadata.desktop"));
00232             QFileInfo info(f);
00233             if (info.lastModified().toTime_t() > uint(pixmapCache->lastModifiedTime())) {
00234                 pixmapCache->clear();
00235             }
00236         }
00237     }
00238 
00239     return cacheTheme;
00240 }
00241 
00242 void ThemePrivate::onAppExitCleanup()
00243 {
00244     pixmapsToCache.clear();
00245     delete pixmapCache;
00246     pixmapCache = 0;
00247     cacheTheme = false;
00248 }
00249 
00250 QString ThemePrivate::findInTheme(const QString &image, const QString &theme, bool cache)
00251 {
00252     if (cache && discoveries.contains(image)) {
00253         return discoveries[image];
00254     }
00255 
00256     QString search;
00257 
00258     if (locolor) {
00259         search = QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/locolor/") % image;
00260         search =  KStandardDirs::locate("data", search);
00261     } else if (!compositingActive) {
00262         search = QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/opaque/") % image;
00263         search =  KStandardDirs::locate("data", search);
00264     } else if (WindowEffects::isEffectAvailable(WindowEffects::BlurBehind)) {
00265         search = QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/translucent/") % image;
00266         search =  KStandardDirs::locate("data", search);
00267     }
00268 
00269     //not found or compositing enabled
00270     if (search.isEmpty()) {
00271         search = QLatin1Literal("desktoptheme/") % theme % QLatin1Char('/') % image;
00272         search =  KStandardDirs::locate("data", search);
00273     }
00274 
00275     if (cache && !search.isEmpty()) {
00276         discoveries.insert(image, search);
00277     }
00278 
00279     return search;
00280 }
00281 
00282 void ThemePrivate::compositingChanged(bool active)
00283 {
00284 #ifdef Q_WS_X11
00285     if (compositingActive != active) {
00286         compositingActive = active;
00287         //kDebug() << QTime::currentTime();
00288         scheduleThemeChangeNotification(PixmapCache | SvgElementsCache);
00289     }
00290 #endif
00291 }
00292 
00293 void ThemePrivate::discardCache(CacheTypes caches)
00294 {
00295     if (caches & PixmapCache) {
00296         pixmapsToCache.clear();
00297         saveTimer->stop();
00298         if (pixmapCache) {
00299             pixmapCache->clear();
00300         }
00301     } else {
00302         // This deletes the object but keeps the on-disk cache for later use
00303         delete pixmapCache;
00304         pixmapCache = 0;
00305     }
00306 
00307     cachedStyleSheets.clear();
00308 
00309     if (caches & SvgElementsCache) {
00310         discoveries.clear();
00311         invalidElements.clear();
00312 
00313         if (svgElementsCache) {
00314             QFile f(svgElementsCache->name());
00315             svgElementsCache = 0;
00316             f.remove();
00317         }
00318 
00319         const QString svgElementsFile = KStandardDirs::locateLocal("cache", "plasma-svgelements-" + themeName);
00320         svgElementsCache = KSharedConfig::openConfig(svgElementsFile);
00321     }
00322 }
00323 
00324 void ThemePrivate::scheduledCacheUpdate()
00325 {
00326     if (useCache()) {
00327         QHashIterator<QString, QPixmap> it(pixmapsToCache);
00328         while (it.hasNext()) {
00329             it.next();
00330             pixmapCache->insertPixmap(idsToCache[it.key()], it.value());
00331         }
00332     }
00333 
00334     pixmapsToCache.clear();
00335     keysToCache.clear();
00336     idsToCache.clear();
00337 }
00338 
00339 void ThemePrivate::colorsChanged()
00340 {
00341     colorScheme = KColorScheme(QPalette::Active, KColorScheme::Window, colors);
00342     buttonColorScheme = KColorScheme(QPalette::Active, KColorScheme::Button, colors);
00343     viewColorScheme = KColorScheme(QPalette::Active, KColorScheme::View, colors);
00344     scheduleThemeChangeNotification(PixmapCache);
00345 }
00346 
00347 void ThemePrivate::blurBehindChanged(bool blur)
00348 {
00349     if (blurActive != blur) {
00350         blurActive = blur;
00351         scheduleThemeChangeNotification(PixmapCache | SvgElementsCache);
00352     }
00353 }
00354 
00355 void ThemePrivate::scheduleThemeChangeNotification(CacheTypes caches)
00356 {
00357     cachesToDiscard |= caches;
00358     updateNotificationTimer->start();
00359 }
00360 
00361 void ThemePrivate::notifyOfChanged()
00362 {
00363     //kDebug() << cachesToDiscard;
00364     discardCache(cachesToDiscard);
00365     cachesToDiscard = NoCache;
00366     emit q->themeChanged();
00367 }
00368 
00369 const QString ThemePrivate::processStyleSheet(const QString &css)
00370 {
00371     QString stylesheet;
00372     if (css.isEmpty()) {
00373         stylesheet = cachedStyleSheets.value(DEFAULTSTYLE);
00374         if (stylesheet.isEmpty()) {
00375             stylesheet = QString("\n\
00376                         body {\n\
00377                             color: %textcolor;\n\
00378                             font-size: %fontsize;\n\
00379                             font-family: %fontfamily;\n\
00380                         }\n\
00381                         a:active  { color: %activatedlink; }\n\
00382                         a:link    { color: %link; }\n\
00383                         a:visited { color: %visitedlink; }\n\
00384                         a:hover   { color: %hoveredlink; text-decoration: none; }\n\
00385                         ");
00386             stylesheet = processStyleSheet(stylesheet);
00387             cachedStyleSheets.insert(DEFAULTSTYLE, stylesheet);
00388         }
00389 
00390         return stylesheet;
00391     } else if (css == "SVG") {
00392         stylesheet = cachedStyleSheets.value(SVGSTYLE);
00393         if (stylesheet.isEmpty()) {
00394             QString skel = ".ColorScheme-%1{color:%2;}";
00395 
00396             stylesheet += skel.arg("Text","%textcolor");
00397             stylesheet += skel.arg("Background","%backgroundcolor");
00398 
00399             stylesheet += skel.arg("ButtonText","%buttontextcolor");
00400             stylesheet += skel.arg("ButtonBackground","%buttonbackgroundcolor");
00401             stylesheet += skel.arg("ButtonHover","%buttonhovercolor");
00402             stylesheet += skel.arg("ButtonFocus","%buttonfocuscolor");
00403 
00404             stylesheet += skel.arg("ViewText","%viewtextcolor");
00405             stylesheet += skel.arg("ViewBackground","%viewbackgroundcolor");
00406             stylesheet += skel.arg("ViewHover","%viewhovercolor");
00407             stylesheet += skel.arg("ViewFocus","%viewfocuscolor");
00408 
00409             stylesheet = processStyleSheet(stylesheet);
00410             cachedStyleSheets.insert(SVGSTYLE, stylesheet);
00411         }
00412 
00413         return stylesheet;
00414     } else {
00415         stylesheet = css;
00416     }
00417 
00418     QHash<QString, QString> elements;
00419     // If you add elements here, make sure their names are sufficiently unique to not cause
00420     // clashes between element keys
00421     elements["%textcolor"] = q->color(Theme::TextColor).name();
00422     elements["%backgroundcolor"] = q->color(Theme::BackgroundColor).name();
00423     elements["%visitedlink"] = q->color(Theme::VisitedLinkColor).name();
00424     elements["%activatedlink"] = q->color(Theme::HighlightColor).name();
00425     elements["%hoveredlink"] = q->color(Theme::HighlightColor).name();
00426     elements["%link"] = q->color(Theme::LinkColor).name();
00427     elements["%buttontextcolor"] = q->color(Theme::ButtonTextColor).name();
00428     elements["%buttonbackgroundcolor"] = q->color(Theme::ButtonBackgroundColor).name();
00429     elements["%buttonhovercolor"] = q->color(Theme::ButtonHoverColor).name();
00430     elements["%buttonfocuscolor"] = q->color(Theme::ButtonFocusColor).name();
00431     elements["%viewtextcolor"] = q->color(Theme::ViewTextColor).name();
00432     elements["%viewbackgroundcolor"] = q->color(Theme::ViewBackgroundColor).name();
00433     elements["%viewhovercolor"] = q->color(Theme::ViewHoverColor).name();
00434     elements["%viewfocuscolor"] = q->color(Theme::ViewFocusColor).name();
00435 
00436     QFont font = q->font(Theme::DefaultFont);
00437     elements["%fontsize"] = QString("%1pt").arg(font.pointSize());
00438     elements["%fontfamily"] = font.family().split('[').first();
00439     elements["%smallfontsize"] = QString("%1pt").arg(KGlobalSettings::smallestReadableFont().pointSize());
00440 
00441     QHash<QString, QString>::const_iterator it = elements.constBegin();
00442     QHash<QString, QString>::const_iterator itEnd = elements.constEnd();
00443     for ( ; it != itEnd; ++it) {
00444         stylesheet.replace(it.key(), it.value());
00445     }
00446     return stylesheet;
00447 }
00448 
00449 class ThemeSingleton
00450 {
00451 public:
00452     ThemeSingleton()
00453     {
00454         self.d->isDefault = true;
00455 
00456         //FIXME: if/when kconfig gets change notification, this will be unnecessary
00457         KDirWatch::self()->addFile(KStandardDirs::locateLocal("config", ThemePrivate::themeRcFile));
00458         QObject::connect(KDirWatch::self(), SIGNAL(created(QString)), &self, SLOT(settingsFileChanged(QString)));
00459         QObject::connect(KDirWatch::self(), SIGNAL(dirty(QString)), &self, SLOT(settingsFileChanged(QString)));
00460     }
00461 
00462    Theme self;
00463 };
00464 
00465 K_GLOBAL_STATIC(ThemeSingleton, privateThemeSelf)
00466 
00467 Theme *Theme::defaultTheme()
00468 {
00469     return &privateThemeSelf->self;
00470 }
00471 
00472 Theme::Theme(QObject *parent)
00473     : QObject(parent),
00474       d(new ThemePrivate(this))
00475 {
00476     settingsChanged();
00477     if (QCoreApplication::instance()) {
00478         connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()),
00479                 this, SLOT(onAppExitCleanup()));
00480     }
00481 }
00482 
00483 Theme::Theme(const QString &themeName, QObject *parent)
00484     : QObject(parent),
00485       d(new ThemePrivate(this))
00486 {
00487     // turn off caching so we don't accidently trigger unnecessary disk activity at this point
00488     bool useCache = d->cacheTheme;
00489     d->cacheTheme = false;
00490     setThemeName(themeName);
00491     d->cacheTheme = useCache;
00492     if (QCoreApplication::instance()) {
00493         connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()),
00494                 this, SLOT(onAppExitCleanup()));
00495     }
00496 }
00497 
00498 Theme::~Theme()
00499 {
00500     if (d->svgElementsCache) {
00501         QHashIterator<QString, QSet<QString> > it(d->invalidElements);
00502         while (it.hasNext()) {
00503             it.next();
00504             KConfigGroup imageGroup(d->svgElementsCache, it.key());
00505             imageGroup.writeEntry("invalidElements", it.value().toList()); //FIXME: add QSet support to KConfig
00506         }
00507     }
00508 
00509     d->onAppExitCleanup();
00510     delete d;
00511 }
00512 
00513 PackageStructure::Ptr Theme::packageStructure()
00514 {
00515     if (!ThemePrivate::packageStructure) {
00516         ThemePrivate::packageStructure = new ThemePackage();
00517     }
00518 
00519     return ThemePrivate::packageStructure;
00520 }
00521 
00522 KPluginInfo::List Theme::listThemeInfo()
00523 {
00524     const QStringList themes = KGlobal::dirs()->findAllResources("data", "desktoptheme/*/metadata.desktop",
00525                                                            KStandardDirs::NoDuplicates);
00526     return KPluginInfo::fromFiles(themes);
00527 }
00528 
00529 void ThemePrivate::settingsFileChanged(const QString &file)
00530 {
00531     if (file.endsWith(themeRcFile)) {
00532         config().config()->reparseConfiguration();
00533         q->settingsChanged();
00534     }
00535 }
00536 
00537 void Theme::settingsChanged()
00538 {
00539     KConfigGroup cg = d->config();
00540     d->setThemeName(cg.readEntry("name", ThemePrivate::defaultTheme), false);
00541     cg = KConfigGroup(cg.config(), "PlasmaToolTips");
00542     d->toolTipDelay = cg.readEntry("Delay", 700);
00543 }
00544 
00545 void Theme::setThemeName(const QString &themeName)
00546 {
00547     d->setThemeName(themeName, true);
00548 }
00549 
00550 void ThemePrivate::processWallpaperSettings(KConfigBase *metadata)
00551 {
00552     if (!defaultWallpaperTheme.isEmpty() && defaultWallpaperTheme != DEFAULT_WALLPAPER_THEME) {
00553         return;
00554     }
00555 
00556     KConfigGroup cg;
00557     if (metadata->hasGroup("Wallpaper")) {
00558         // we have a theme color config, so let's also check to see if
00559         // there is a wallpaper defined in there.
00560         cg = KConfigGroup(metadata, "Wallpaper");
00561     } else {
00562         // since we didn't find an entry in the theme, let's look in the main
00563         // theme config
00564         cg = config();
00565     }
00566 
00567     defaultWallpaperTheme = cg.readEntry("defaultWallpaperTheme", DEFAULT_WALLPAPER_THEME);
00568     defaultWallpaperSuffix = cg.readEntry("defaultFileSuffix", DEFAULT_WALLPAPER_SUFFIX);
00569     defaultWallpaperWidth = cg.readEntry("defaultWidth", DEFAULT_WALLPAPER_WIDTH);
00570     defaultWallpaperHeight = cg.readEntry("defaultHeight", DEFAULT_WALLPAPER_HEIGHT);
00571 }
00572 
00573 void ThemePrivate::processAnimationSettings(const QString &theme, KConfigBase *metadata)
00574 {
00575     KConfigGroup cg(metadata, "Animations");
00576     const QString animDir = QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/animations/");
00577     foreach (const QString &path, cg.keyList()) {
00578         const QStringList anims = cg.readEntry(path, QStringList());
00579         foreach (const QString &anim, anims) {
00580             if (!animationMapping.contains(anim)) {
00581                 kDebug() << "Registering animation. animDir: " << animDir
00582                          << "\tanim: " << anim
00583                          << "\tpath: " << path << "\t*******\n\n\n";
00584                 //key: desktoptheme/default/animations/+ all.js
00585                 //value: ZoomAnimation
00586                 animationMapping.insert(anim, animDir % path);
00587             } else {
00588                 kDebug() << "************Animation already registered!\n\n\n";
00589             }
00590         }
00591     }
00592 
00593 }
00594 
00595 void ThemePrivate::setThemeName(const QString &tempThemeName, bool writeSettings)
00596 {
00597     //kDebug() << tempThemeName;
00598     QString theme = tempThemeName;
00599     if (theme.isEmpty() || theme == themeName) {
00600         // let's try and get the default theme at least
00601         if (themeName.isEmpty()) {
00602             theme = ThemePrivate::defaultTheme;
00603         } else {
00604             return;
00605         }
00606     }
00607 
00608     // we have one special theme: essentially a dummy theme used to cache things with
00609     // the system colors.
00610     bool realTheme = theme != systemColorsTheme;
00611     if (realTheme) {
00612         QString themePath = KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Char('/'));
00613         if (themePath.isEmpty() && themeName.isEmpty()) {
00614             themePath = KStandardDirs::locate("data", "desktoptheme/default/");
00615 
00616             if (themePath.isEmpty()) {
00617                 return;
00618             }
00619 
00620             theme = ThemePrivate::defaultTheme;
00621         }
00622     }
00623 
00624     // check again as ThemePrivate::defaultTheme might be empty
00625     if (themeName == theme) {
00626         return;
00627     }
00628 
00629     themeName = theme;
00630 
00631     // load the color scheme config
00632     const QString colorsFile = realTheme ? KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/colors"))
00633                                          : QString();
00634 
00635     //kDebug() << "we're going for..." << colorsFile << "*******************";
00636 
00637     // load the wallpaper settings, if any
00638     if (realTheme) {
00639         const QString metadataPath(KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/metadata.desktop")));
00640         KConfig metadata(metadataPath);
00641 
00642         processWallpaperSettings(&metadata);
00643 
00644         AnimationScriptEngine::clearAnimations();
00645         animationMapping.clear();
00646         processAnimationSettings(themeName, &metadata);
00647 
00648         KConfigGroup cg(&metadata, "Settings");
00649         useNativeWidgetStyle = cg.readEntry("UseNativeWidgetStyle", false);
00650         QString fallback = cg.readEntry("FallbackTheme", QString());
00651 
00652         fallbackThemes.clear();
00653         while (!fallback.isEmpty() && !fallbackThemes.contains(fallback)) {
00654             fallbackThemes.append(fallback);
00655 
00656             QString metadataPath(KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/metadata.desktop")));
00657             KConfig metadata(metadataPath);
00658             KConfigGroup cg(&metadata, "Settings");
00659             fallback = cg.readEntry("FallbackTheme", QString());
00660         }
00661 
00662         if (!fallbackThemes.contains("oxygen")) {
00663             fallbackThemes.append("oxygen");
00664         }
00665 
00666         if (!fallbackThemes.contains(ThemePrivate::defaultTheme)) {
00667             fallbackThemes.append(ThemePrivate::defaultTheme);
00668         }
00669 
00670         foreach (const QString &theme, fallbackThemes) {
00671             QString metadataPath(KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/metadata.desktop")));
00672             KConfig metadata(metadataPath);
00673             processAnimationSettings(theme, &metadata);
00674             processWallpaperSettings(&metadata);
00675         }
00676     }
00677 
00678     if (colorsFile.isEmpty()) {
00679         colors = 0;
00680         QObject::connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
00681                          q, SLOT(colorsChanged()), Qt::UniqueConnection);
00682     } else {
00683         QObject::disconnect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
00684                             q, SLOT(colorsChanged()));
00685         colors = KSharedConfig::openConfig(colorsFile);
00686     }
00687 
00688     colorScheme = KColorScheme(QPalette::Active, KColorScheme::Window, colors);
00689     buttonColorScheme = KColorScheme(QPalette::Active, KColorScheme::Button, colors);
00690     viewColorScheme = KColorScheme(QPalette::Active, KColorScheme::View, colors);
00691     hasWallpapers = KStandardDirs::exists(KStandardDirs::locateLocal("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/wallpapers/")));
00692 
00693     if (realTheme && isDefault && writeSettings) {
00694         // we're the default theme, let's save our state
00695         KConfigGroup &cg = config();
00696         if (ThemePrivate::defaultTheme == themeName) {
00697             cg.deleteEntry("name");
00698         } else {
00699             cg.writeEntry("name", themeName);
00700         }
00701         cg.sync();
00702     }
00703 
00704     scheduleThemeChangeNotification(SvgElementsCache);
00705 }
00706 
00707 QString Theme::themeName() const
00708 {
00709     return d->themeName;
00710 }
00711 
00712 QString Theme::imagePath(const QString &name) const
00713 {
00714     // look for a compressed svg file in the theme
00715     if (name.contains("../") || name.isEmpty()) {
00716         // we don't support relative paths
00717         //kDebug() << "Theme says: bad image path " << name;
00718         return QString();
00719     }
00720 
00721     const QString svgzName = name % QLatin1Literal(".svgz");
00722     QString path = d->findInTheme(svgzName, d->themeName);
00723 
00724     if (path.isEmpty()) {
00725         // try for an uncompressed svg file
00726         const QString svgName = name % QLatin1Literal(".svg");
00727         path = d->findInTheme(svgName, d->themeName);
00728 
00729         // search in fallback themes if necessary
00730         for (int i = 0; path.isEmpty() && i < d->fallbackThemes.count(); ++i) {
00731             if (d->themeName == d->fallbackThemes[i]) {
00732                 continue;
00733             }
00734 
00735             // try a compressed svg file in the fallback theme
00736             path = d->findInTheme(svgzName, d->fallbackThemes[i]);
00737 
00738             if (path.isEmpty()) {
00739                 // try an uncompressed svg file in the fallback theme
00740                 path = d->findInTheme(svgName, d->fallbackThemes[i]);
00741             }
00742         }
00743     }
00744 
00745     /*
00746     if (path.isEmpty()) {
00747         kDebug() << "Theme says: bad image path " << name;
00748     }
00749     */
00750 
00751     return path;
00752 }
00753 
00754 QString Theme::styleSheet(const QString &css) const
00755 {
00756     return d->processStyleSheet(css);
00757 }
00758 
00759 QString Theme::animationPath(const QString &name) const
00760 {
00761     const QString path = d->animationMapping.value(name);
00762     if (path.isEmpty()) {
00763         //kError() << "****** FAILED TO FIND IN MAPPING!";
00764         return path;
00765     }
00766 
00767     return KStandardDirs::locate("data", path);
00768 }
00769 
00770 QString Theme::wallpaperPath(const QSize &size) const
00771 {
00772     QString fullPath;
00773     QString image = d->defaultWallpaperTheme;
00774 
00775     image.append("/contents/images/%1x%2").append(d->defaultWallpaperSuffix);
00776     QString defaultImage = image.arg(d->defaultWallpaperWidth).arg(d->defaultWallpaperHeight);
00777 
00778     if (size.isValid()) {
00779         // try to customize the paper to the size requested
00780         //TODO: this should do better than just fallback to the default size.
00781         //      a "best fit" matching would be far better, so we don't end
00782         //      up returning a 1920x1200 wallpaper for a 640x480 request ;)
00783         image = image.arg(size.width()).arg(size.height());
00784     } else {
00785         image = defaultImage;
00786     }
00787 
00788     //TODO: the theme's wallpaper overrides regularly installed wallpapers.
00789     //      should it be possible for user installed (e.g. locateLocal) wallpapers
00790     //      to override the theme?
00791     if (d->hasWallpapers) {
00792         // check in the theme first
00793         fullPath = d->findInTheme(QLatin1Literal("wallpapers/") % image, d->themeName);
00794 
00795         if (fullPath.isEmpty()) {
00796             fullPath = d->findInTheme(QLatin1Literal("wallpapers/") % defaultImage, d->themeName);
00797         }
00798     }
00799 
00800     if (fullPath.isEmpty()) {
00801         // we failed to find it in the theme, so look in the standard directories
00802         //kDebug() << "looking for" << image;
00803         fullPath = KStandardDirs::locate("wallpaper", image);
00804     }
00805 
00806     if (fullPath.isEmpty()) {
00807         // we still failed to find it in the theme, so look for the default in
00808         // the standard directories
00809         //kDebug() << "looking for" << defaultImage;
00810         fullPath = KStandardDirs::locate("wallpaper", defaultImage);
00811 
00812         if (fullPath.isEmpty()) {
00813             kDebug() << "exhausted every effort to find a wallpaper.";
00814         }
00815     }
00816 
00817     return fullPath;
00818 }
00819 
00820 bool Theme::currentThemeHasImage(const QString &name) const
00821 {
00822     if (name.contains("../")) {
00823         // we don't support relative paths
00824         return false;
00825     }
00826 
00827     return !(d->findInTheme(name % QLatin1Literal(".svgz"), d->themeName, false).isEmpty()) ||
00828            !(d->findInTheme(name % QLatin1Literal(".svg"), d->themeName, false).isEmpty());
00829 }
00830 
00831 KSharedConfigPtr Theme::colorScheme() const
00832 {
00833     return d->colors;
00834 }
00835 
00836 QColor Theme::color(ColorRole role) const
00837 {
00838     switch (role) {
00839         case TextColor:
00840             return d->colorScheme.foreground(KColorScheme::NormalText).color();
00841 
00842         case HighlightColor:
00843             return d->colorScheme.decoration(KColorScheme::HoverColor).color();
00844 
00845         case BackgroundColor:
00846             return d->colorScheme.background(KColorScheme::NormalBackground).color();
00847 
00848         case ButtonTextColor:
00849             return d->buttonColorScheme.foreground(KColorScheme::NormalText).color();
00850 
00851         case ButtonBackgroundColor:
00852             return d->buttonColorScheme.background(KColorScheme::NormalBackground).color();
00853 
00854         case ButtonHoverColor:
00855             return d->buttonColorScheme.decoration(KColorScheme::HoverColor).color();
00856 
00857         case ButtonFocusColor:
00858             return d->buttonColorScheme.decoration(KColorScheme::FocusColor).color();
00859 
00860         case ViewTextColor:
00861             return d->viewColorScheme.foreground(KColorScheme::NormalText).color();
00862 
00863         case ViewBackgroundColor:
00864             return d->viewColorScheme.background(KColorScheme::NormalBackground).color();
00865 
00866         case ViewHoverColor:
00867             return d->viewColorScheme.decoration(KColorScheme::HoverColor).color();
00868 
00869         case ViewFocusColor:
00870             return d->viewColorScheme.decoration(KColorScheme::FocusColor).color();
00871 
00872         case LinkColor:
00873             return d->viewColorScheme.foreground(KColorScheme::LinkText).color();
00874 
00875         case VisitedLinkColor:
00876             return d->viewColorScheme.foreground(KColorScheme::VisitedText).color();
00877     }
00878 
00879     return QColor();
00880 }
00881 
00882 void Theme::setFont(const QFont &font, FontRole role)
00883 {
00884     Q_UNUSED(role)
00885     d->generalFont = font;
00886 }
00887 
00888 QFont Theme::font(FontRole role) const
00889 {
00890     switch (role) {
00891     case DesktopFont: {
00892         KConfigGroup cg(KGlobal::config(), "General");
00893         return cg.readEntry("desktopFont", d->generalFont);
00894         }
00895         break;
00896 
00897     case DefaultFont:
00898     default:
00899         return d->generalFont;
00900         break;
00901 
00902     case SmallestFont:
00903         return KGlobalSettings::smallestReadableFont();
00904         break;
00905     }
00906 
00907     return d->generalFont;
00908 }
00909 
00910 QFontMetrics Theme::fontMetrics() const
00911 {
00912     //TODO: allow this to be overridden with a plasma specific font?
00913     return QFontMetrics(d->generalFont);
00914 }
00915 
00916 bool Theme::windowTranslucencyEnabled() const
00917 {
00918     return d->compositingActive;
00919 }
00920 
00921 void Theme::setUseGlobalSettings(bool useGlobal)
00922 {
00923     if (d->useGlobal == useGlobal) {
00924         return;
00925     }
00926 
00927     d->useGlobal = useGlobal;
00928     d->cfg = KConfigGroup();
00929     d->themeName.clear();
00930     settingsChanged();
00931 }
00932 
00933 bool Theme::useGlobalSettings() const
00934 {
00935     return d->useGlobal;
00936 }
00937 
00938 bool Theme::useNativeWidgetStyle() const
00939 {
00940     return d->useNativeWidgetStyle;
00941 }
00942 
00943 bool Theme::findInCache(const QString &key, QPixmap &pix)
00944 {
00945     if (d->useCache()) {
00946         const QString id = d->keysToCache.value(key);
00947         if (d->pixmapsToCache.contains(id)) {
00948             pix = d->pixmapsToCache.value(id);
00949             return !pix.isNull();
00950         }
00951 
00952         QPixmap temp;
00953         if (d->pixmapCache->findPixmap(key, &temp) && !temp.isNull()) {
00954             pix = temp;
00955             return true;
00956         }
00957     }
00958 
00959     return false;
00960 }
00961 
00962 // BIC FIXME: Should be merged with the other findInCache method above when we break BC
00963 bool Theme::findInCache(const QString &key, QPixmap &pix, unsigned int lastModified)
00964 {
00965     if (d->useCache() && lastModified > uint(d->pixmapCache->lastModifiedTime())) {
00966         return false;
00967     }
00968 
00969     return findInCache(key, pix);
00970 }
00971 
00972 void Theme::insertIntoCache(const QString& key, const QPixmap& pix)
00973 {
00974     if (d->useCache()) {
00975         d->pixmapCache->insertPixmap(key, pix);
00976     }
00977 }
00978 
00979 void Theme::insertIntoCache(const QString& key, const QPixmap& pix, const QString& id)
00980 {
00981     if (d->useCache()) {
00982         d->pixmapsToCache.insert(id, pix);
00983 
00984         if (d->idsToCache.contains(id)) {
00985             d->keysToCache.remove(d->idsToCache[id]);
00986         }
00987 
00988         d->keysToCache.insert(key, id);
00989         d->idsToCache.insert(id, key);
00990         d->saveTimer->start();
00991     }
00992 }
00993 
00994 bool Theme::findInRectsCache(const QString &image, const QString &element, QRectF &rect) const
00995 {
00996     if (!d->svgElementsCache) {
00997         return false;
00998     }
00999 
01000     KConfigGroup imageGroup(d->svgElementsCache, image);
01001     rect = imageGroup.readEntry(element % QLatin1Literal("Size"), QRectF());
01002 
01003     if (rect.isValid()) {
01004         return true;
01005     }
01006 
01007     //Name starting by _ means the element is empty and we're asked for the size of
01008     //the whole image, so the whole image is never invalid
01009     if (element.indexOf('_') <= 0) {
01010         return false;
01011     }
01012 
01013     bool invalid = false;
01014 
01015     QHash<QString, QSet<QString> >::iterator it = d->invalidElements.find(image);
01016     if (it == d->invalidElements.end()) {
01017         QSet<QString> elements = imageGroup.readEntry("invalidElements", QStringList()).toSet();
01018         d->invalidElements.insert(image, elements);
01019         invalid = elements.contains(element);
01020     } else {
01021         invalid = it.value().contains(element);
01022     }
01023 
01024     return invalid;
01025 }
01026 
01027 QStringList Theme::listCachedRectKeys(const QString &image) const
01028 {
01029     if (!d->svgElementsCache) {
01030         return QStringList();
01031     }
01032 
01033     KConfigGroup imageGroup(d->svgElementsCache, image);
01034     QStringList keys = imageGroup.keyList();
01035 
01036     QMutableListIterator<QString> i(keys);
01037     while (i.hasNext()) {
01038         QString key = i.next();
01039         if (key.endsWith("Size")) {
01040             // The actual cache id used from outside doesn't end on "Size".
01041             key.resize(key.size() - 4);
01042             i.setValue(key);
01043         } else {
01044             i.remove();
01045         }
01046     }
01047     return keys;
01048 }
01049 
01050 void Theme::insertIntoRectsCache(const QString& image, const QString &element, const QRectF &rect)
01051 {
01052     if (!d->svgElementsCache) {
01053         return;
01054     }
01055 
01056     if (rect.isValid()) {
01057         KConfigGroup imageGroup(d->svgElementsCache, image);
01058         imageGroup.writeEntry(element % QLatin1Literal("Size"), rect);
01059     } else {
01060         QHash<QString, QSet<QString> >::iterator it = d->invalidElements.find(image);
01061         if (it == d->invalidElements.end()) {
01062             d->invalidElements[image].insert(element);
01063         } else if (!it.value().contains(element)) {
01064             if (it.value().count() > 1000) {
01065                 it.value().erase(it.value().begin());
01066             }
01067 
01068             it.value().insert(element);
01069         }
01070     }
01071 }
01072 
01073 void Theme::invalidateRectsCache(const QString& image)
01074 {
01075     if (d->svgElementsCache) {
01076         KConfigGroup imageGroup(d->svgElementsCache, image);
01077         imageGroup.deleteGroup();
01078     }
01079 
01080     d->invalidElements.remove(image);
01081 }
01082 
01083 void Theme::releaseRectsCache(const QString &image)
01084 {
01085     QHash<QString, QSet<QString> >::iterator it = d->invalidElements.find(image);
01086     if (it != d->invalidElements.end()) {
01087         if (!d->svgElementsCache) {
01088             KConfigGroup imageGroup(d->svgElementsCache, it.key());
01089             imageGroup.writeEntry("invalidElements", it.value().toList());
01090         }
01091 
01092         d->invalidElements.erase(it);
01093     }
01094 }
01095 
01096 void Theme::setCacheLimit(int kbytes)
01097 {
01098     Q_UNUSED(kbytes)
01099     if (d->useCache()) {
01100         ;
01101         // Too late for you bub.
01102         // d->pixmapCache->setCacheLimit(kbytes);
01103     }
01104 }
01105 
01106 KUrl Theme::homepage() const
01107 {
01108     const QString metadataPath(KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % d->themeName % QLatin1Literal("/metadata.desktop")));
01109     KConfig metadata(metadataPath);
01110     KConfigGroup brandConfig(&metadata, "Branding");
01111     return brandConfig.readEntry("homepage", KUrl("http://www.kde.org"));
01112 }
01113 
01114 int Theme::toolTipDelay() const
01115 {
01116     return d->toolTipDelay;
01117 }
01118 
01119 }
01120 
01121 #include <theme.moc>
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed May 2 2012 17:36:20 by doxygen 1.8.0 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.8.3 API Reference

Skip menu "kdelibs-4.8.3 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