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

Plasma

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • 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
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal