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

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

Skip menu "kdelibs"
  • 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
Generated for kdelibs by doxygen 1.7.5
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