• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Plasma

svg.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 "svg.h"
00021 
00022 #include <QDir>
00023 #include <QMatrix>
00024 #include <QPainter>
00025 #include <QSharedData>
00026 #include <QTimer>
00027 
00028 #include <kcolorscheme.h>
00029 #include <kconfiggroup.h>
00030 #include <kdebug.h>
00031 #include <kiconeffect.h>
00032 #include <kglobalsettings.h>
00033 #include <ksharedptr.h>
00034 #include <ksvgrenderer.h>
00035 
00036 #include "applet.h"
00037 #include "package.h"
00038 #include "theme.h"
00039 
00040 namespace Plasma
00041 {
00042 
00043 class SharedSvgRenderer : public KSvgRenderer, public QSharedData
00044 {
00045     public:
00046         typedef KSharedPtr<SharedSvgRenderer> Ptr;
00047 
00048         SharedSvgRenderer(QObject *parent = 0)
00049             : KSvgRenderer(parent)
00050         {}
00051 
00052         SharedSvgRenderer(const QString &filename, QObject *parent = 0)
00053             : KSvgRenderer(filename, parent)
00054         {}
00055 
00056         SharedSvgRenderer(const QByteArray &contents, QObject *parent = 0)
00057             : KSvgRenderer(contents, parent)
00058         {}
00059 
00060         ~SharedSvgRenderer()
00061         {
00062             //kDebug() << "leaving this world for a better one.";
00063         }
00064 };
00065 
00066 class SvgPrivate
00067 {
00068     public:
00069         SvgPrivate(Svg *svg)
00070             : q(svg),
00071               saveTimer(0),
00072               renderer(0),
00073               multipleImages(false),
00074               themed(false),
00075               applyColors(false)
00076         {
00077         }
00078 
00079         ~SvgPrivate()
00080         {
00081             eraseRenderer();
00082         }
00083 
00084         //This function is meant for the rects cache
00085         QString cacheId(const QString &elementId)
00086         {
00087             if (size.isValid() && size != naturalSize) {
00088                 return QString("%3_%2_%1").arg(int(size.height()))
00089                                         .arg(int(size.width()))
00090                                         .arg(elementId);
00091             } else {
00092                 return QString("%2_%1").arg("Natural")
00093                                         .arg(elementId);
00094             }
00095         }
00096 
00097         //This function is meant for the pixmap cache
00098         QString cachePath(const QString &path, const QSize &size)
00099         {
00100              return QString("%3_%2_%1_").arg(int(size.height()))
00101                                        .arg(int(size.width()))
00102                                        .arg(path);
00103         }
00104 
00105         bool setImagePath(const QString &imagePath, Svg *q)
00106         {
00107             bool isThemed = !QDir::isAbsolutePath(imagePath);
00108 
00109             // lets check to see if we're already set to this file
00110             if (isThemed == themed &&
00111                 ((themed && themePath == imagePath) ||
00112                  (!themed && path == imagePath))) {
00113                 return false;
00114             }
00115 
00116             // if we don't have any path right now and are going to set one,
00117             // then lets not schedule a repaint because we are just initializing!
00118             bool updateNeeded = true; 
00119 
00120             if (themed) {
00121                 QObject::disconnect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()),
00122                                     q, SLOT(themeChanged()));
00123                 QObject::disconnect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
00124                                     q, SLOT(colorsChanged()));
00125             }
00126 
00127             themed = isThemed;
00128             path.clear();
00129             themePath.clear();
00130 
00131             if (themed) {
00132                 themePath = imagePath;
00133                 QObject::connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()),
00134                                  q, SLOT(themeChanged()));
00135 
00136                 // check if svg wants colorscheme applied
00137                 checkApplyColorHint();
00138                 if (applyColors && !Theme::defaultTheme()->colorScheme()) {
00139                     QObject::connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
00140                                      q, SLOT(colorsChanged()));
00141                 }
00142 
00143                 QRectF rect;
00144                 bool found = Theme::defaultTheme()->findInRectsCache(path, "_Natural", rect);
00145 
00146                 if (found && !rect.isValid()) {
00147                     createRenderer();
00148                     naturalSize = renderer->defaultSize();
00149                     Theme::defaultTheme()->insertIntoRectsCache(path, "_Natural", QRectF(QPointF(0,0), naturalSize));
00150                 } else {
00151                     naturalSize = rect.size();
00152                 }
00153             } else if (QFile::exists(imagePath)) {
00154                 path = imagePath;
00155             } else {
00156                 kDebug() << "file '" << path << "' does not exist!";
00157             }
00158 
00159             return updateNeeded;
00160         }
00161 
00162         QPixmap findInCache(const QString &elementId, const QSizeF &s = QSizeF())
00163         {
00164             QSize size;
00165             if (elementId.isEmpty() || (multipleImages && s.isValid())) {
00166                 size = s.toSize();
00167             } else {
00168                 size = elementRect(elementId).size().toSize();
00169             }
00170 
00171             if (size.isEmpty()) {
00172                 return QPixmap();
00173             }
00174 
00175             QString id = cachePath(path, size);
00176 
00177             if (!elementId.isEmpty()) {
00178                 id.append(elementId);
00179             }
00180 
00181             //kDebug() << "id is " << id;
00182 
00183             Theme *theme = Theme::defaultTheme();
00184             QPixmap p;
00185 
00186             if (theme->findInCache(id, p)) {
00187                 //kDebug() << "found cached version of " << id << p.size();
00188                 return p;
00189             } else {
00190                 //kDebug() << "didn't find cached version of " << id << ", so re-rendering";
00191             }
00192 
00193             //kDebug() << "size for " << elementId << " is " << s;
00194             // we have to re-render this puppy
00195 
00196             p = QPixmap(size);
00197 
00198             p.fill(Qt::transparent);
00199             QPainter renderPainter(&p);
00200 
00201             createRenderer();
00202             if (elementId.isEmpty()) {
00203                 renderer->render(&renderPainter);
00204             } else {
00205                 renderer->render(&renderPainter, elementId);
00206             }
00207 
00208             renderPainter.end();
00209 
00210             // Apply current color scheme if the svg asks for it
00211             if (applyColors) {
00212                 QImage itmp = p.toImage();
00213                 KIconEffect::colorize(itmp, theme->color(Theme::BackgroundColor), 1.0);
00214                 p = p.fromImage(itmp);
00215             }
00216 
00217             if (!itemsToSave.contains(id)) {
00218                 itemsToSave.insert(id, p);
00219                 saveTimer->start(300);
00220             }
00221 
00222             return p;
00223         }
00224 
00225         void scheduledCacheUpdate()
00226         {
00227             QHash<QString, QPixmap>::const_iterator i = itemsToSave.constBegin();
00228 
00229             while (i != itemsToSave.constEnd()) {
00230                 //kDebug()<<"Saving item to cache: "<<i.key();
00231                 Theme::defaultTheme()->insertIntoCache(i.key(), i.value());
00232                 ++i;
00233             }
00234 
00235             itemsToSave.clear();
00236         }
00237 
00238         void createRenderer()
00239         {
00240             if (renderer) {
00241                 return;
00242             }
00243 
00244             //kDebug() << kBacktrace();
00245             if (themed && path.isEmpty()) {
00246                 Applet *applet = qobject_cast<Applet*>(q->parent());
00247                 if (applet && applet->package()) {
00248                     path = applet->package()->filePath("images", themePath + ".svg");
00249 
00250                     if (path.isEmpty()) {
00251                         path = applet->package()->filePath("images", themePath + ".svgz");
00252                     }
00253                 }
00254 
00255                 if (path.isEmpty()) {
00256                     path = Plasma::Theme::defaultTheme()->imagePath(themePath);
00257                 }
00258             }
00259 
00260             //kDebug() << "********************************";
00261             //kDebug() << "FAIL! **************************";
00262             //kDebug() << path << "**";
00263 
00264             QHash<QString, SharedSvgRenderer::Ptr>::const_iterator it = s_renderers.constFind(path);
00265 
00266             if (it != s_renderers.constEnd()) {
00267                 //kDebug() << "gots us an existing one!";
00268                 renderer = it.value();
00269             } else {
00270                 renderer = new SharedSvgRenderer(path);
00271                 s_renderers[path] = renderer;
00272             }
00273 
00274             if (size == QSizeF()) {
00275                 size = renderer->defaultSize();
00276             }
00277         }
00278 
00279         void eraseRenderer()
00280         {
00281             if (renderer && renderer.count() == 2) {
00282                 // this and the cache reference it
00283                 s_renderers.erase(s_renderers.find(path));
00284                 Plasma::Theme::defaultTheme()->releaseRectsCache(path);
00285             }
00286 
00287             renderer = 0;
00288             localRectCache.clear();
00289         }
00290 
00291         QRectF elementRect(const QString &elementId)
00292         {
00293             if (themed && path.isEmpty()) {
00294                 path = Plasma::Theme::defaultTheme()->imagePath(themePath);
00295             }
00296 
00297             QString id = cacheId(elementId);
00298             if (localRectCache.contains(id)) {
00299                 return localRectCache.value(id);
00300             }
00301 
00302             QRectF rect;
00303             bool found = Theme::defaultTheme()->findInRectsCache(path, id, rect);
00304 
00305             if (found) {
00306                 localRectCache.insert(id, rect);
00307                 return rect;
00308             }
00309 
00310             return findAndCacheElementRect(elementId);
00311         }
00312 
00313         QRectF findAndCacheElementRect(const QString &elementId)
00314         {
00315             createRenderer();
00316             QRectF elementRect = renderer->elementExists(elementId) ?
00317                                  renderer->boundsOnElement(elementId) : QRectF();
00318             naturalSize = renderer->defaultSize();
00319             qreal dx = size.width() / naturalSize.width();
00320             qreal dy = size.height() / naturalSize.height();
00321 
00322             elementRect = QRectF(elementRect.x() * dx, elementRect.y() * dy,
00323                                  elementRect.width() * dx, elementRect.height() * dy);
00324             Theme::defaultTheme()->insertIntoRectsCache(path, cacheId(elementId), elementRect);
00325 
00326             return elementRect;
00327         }
00328 
00329         QMatrix matrixForElement(const QString &elementId)
00330         {
00331             createRenderer();
00332             return renderer->matrixForElement(elementId);
00333         }
00334 
00335         void checkApplyColorHint()
00336         {
00337             QRectF elementRect;
00338             bool found = Theme::defaultTheme()->findInRectsCache(themePath, cacheId("hint-apply-color-scheme"), elementRect);
00339 
00340             if (found) {
00341                 applyColors = elementRect.isValid();
00342             } else {
00343                 findAndCacheElementRect("hint-apply-color-scheme");
00344                 applyColors = renderer->elementExists("hint-apply-color-scheme");
00345             }
00346         }
00347 
00348         void themeChanged()
00349         {
00350             if (!themed) {
00351                 return;
00352             }
00353 
00354             QString newPath = Theme::defaultTheme()->imagePath(themePath);
00355 
00356             if (path == newPath) {
00357                 return;
00358             }
00359 
00360             path = newPath;
00361             //delete d->renderer; we're a KSharedPtr
00362             eraseRenderer();
00363 
00364             // check if new theme svg wants colorscheme applied
00365             bool wasApplyColors = applyColors;
00366             checkApplyColorHint();
00367             if (applyColors && !Theme::defaultTheme()->colorScheme()) {
00368                 if (!wasApplyColors) {
00369                     QObject::connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
00370                                      q, SLOT(colorsChanged()));
00371                 }
00372             } else {
00373                 QObject::disconnect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
00374                                     q, SLOT(colorsChanged()));
00375             }
00376 
00377             localRectCache.clear();
00378             itemsToSave.clear();
00379             saveTimer->stop();
00380 
00381             //kDebug() << themePath << ">>>>>>>>>>>>>>>>>> theme changed";
00382             emit q->repaintNeeded();
00383         }
00384 
00385         void colorsChanged()
00386         {
00387             if (!applyColors) {
00388                 return;
00389             }
00390 
00391             eraseRenderer();
00392             itemsToSave.clear();
00393             saveTimer->stop();
00394             emit q->repaintNeeded();
00395         }
00396 
00397         Svg *q;
00398         static QHash<QString, SharedSvgRenderer::Ptr> s_renderers;
00399         QHash<QString, QRectF> localRectCache;
00400         QHash<QString, QPixmap> itemsToSave;
00401         QTimer *saveTimer;
00402         SharedSvgRenderer::Ptr renderer;
00403         QString themePath;
00404         QString path;
00405         QSizeF size;
00406         QSizeF naturalSize;
00407         bool multipleImages;
00408         bool themed;
00409         bool applyColors;
00410 };
00411 
00412 QHash<QString, SharedSvgRenderer::Ptr> SvgPrivate::s_renderers;
00413 
00414 Svg::Svg(QObject *parent)
00415     : QObject(parent),
00416       d(new SvgPrivate(this))
00417 {
00418     d->saveTimer = new QTimer(this);
00419     d->saveTimer->setSingleShot(true);
00420     connect(d->saveTimer, SIGNAL(timeout()), this, SLOT(scheduledCacheUpdate()));
00421 }
00422 
00423 Svg::~Svg()
00424 {
00425     delete d;
00426 }
00427 
00428 QPixmap Svg::pixmap(const QString &elementID)
00429 {
00430     if (elementID.isNull() || d->multipleImages) {
00431         return d->findInCache(elementID, size());
00432     } else {
00433         return d->findInCache(elementID);
00434     }
00435 }
00436 
00437 void Svg::paint(QPainter *painter, const QPointF &point, const QString &elementID)
00438 {
00439     QPixmap pix(elementID.isNull() ? d->findInCache(elementID, size()) :
00440                                      d->findInCache(elementID));
00441 
00442     if (pix.isNull()) {
00443         return;
00444     }
00445 
00446     painter->drawPixmap(QRectF(point, pix.size()), pix, QRectF(QPointF(0, 0), pix.size()));
00447 }
00448 
00449 void Svg::paint(QPainter *painter, int x, int y, const QString &elementID)
00450 {
00451     paint(painter, QPointF(x, y), elementID);
00452 }
00453 
00454 void Svg::paint(QPainter *painter, const QRectF &rect, const QString &elementID)
00455 {
00456     QPixmap pix(d->findInCache(elementID, rect.size()));
00457     painter->drawPixmap(rect, pix, QRectF(QPointF(0, 0), pix.size()));
00458 }
00459 
00460 void Svg::paint(QPainter *painter, int x, int y, int width, int height, const QString &elementID)
00461 {
00462     QPixmap pix(d->findInCache(elementID, QSizeF(width, height)));
00463     painter->drawPixmap(x, y, pix, 0, 0, pix.size().width(), pix.size().height());
00464 }
00465 
00466 QSize Svg::size() const
00467 {
00468     if (d->size.isEmpty()) {
00469         d->size = d->naturalSize;
00470     }
00471 
00472     return d->size.toSize();
00473 }
00474 
00475 void Svg::resize(qreal width, qreal height)
00476 {
00477     resize(QSize(width, height));
00478 }
00479 
00480 void Svg::resize(const QSizeF &size)
00481 {
00482     if (qFuzzyCompare(size.width(), d->size.width()) &&
00483         qFuzzyCompare(size.height(), d->size.height())) {
00484         return;
00485     }
00486 
00487     d->size = size;
00488     d->localRectCache.clear();
00489 }
00490 
00491 void Svg::resize()
00492 {
00493     if (qFuzzyCompare(d->naturalSize.width(), d->size.width()) &&
00494         qFuzzyCompare(d->naturalSize.height(), d->size.height())) {
00495         return;
00496     }
00497 
00498     d->size = d->naturalSize;
00499     d->localRectCache.clear();
00500 }
00501 
00502 QSize Svg::elementSize(const QString &elementId) const
00503 {
00504     return d->elementRect(elementId).size().toSize();
00505 }
00506 
00507 QRectF Svg::elementRect(const QString &elementId) const
00508 {
00509     return d->elementRect(elementId);
00510 }
00511 
00512 bool Svg::hasElement(const QString &elementId) const
00513 {
00514     if (d->path.isNull() && d->themePath.isNull()) {
00515         return false;
00516     }
00517 
00518     QString id = d->cacheId(elementId);
00519     if (d->localRectCache.contains(id)) {
00520         return d->localRectCache.value(id).isValid();
00521     }
00522 
00523     QRectF elementRect;
00524     bool found = Theme::defaultTheme()->findInRectsCache(d->path, id, elementRect);
00525 
00526     if (found) {
00527         d->localRectCache.insert(id, elementRect);
00528         return elementRect.isValid();
00529     } else {
00530 //        kDebug() << "** ** *** !!!!!!!! *** ** ** creating renderer due to hasElement miss" << d->path << elementId;
00531         return d->findAndCacheElementRect(elementId).isValid();
00532     }
00533 }
00534 
00535 QString Svg::elementAtPoint(const QPoint &point) const
00536 {
00537     Q_UNUSED(point)
00538 
00539     return QString();
00540 /*
00541 FIXME: implement when Qt can support us!
00542     d->createRenderer();
00543     QSizeF naturalSize = d->renderer->defaultSize();
00544     qreal dx = d->size.width() / naturalSize.width();
00545     qreal dy = d->size.height() / naturalSize.height();
00546     //kDebug() << point << "is really"
00547     //         << QPoint(point.x() *dx, naturalSize.height() - point.y() * dy);
00548 
00549     return QString(); // d->renderer->elementAtPoint(QPoint(point.x() *dx, naturalSize.height() - point.y() * dy));
00550     */
00551 }
00552 
00553 bool Svg::isValid() const
00554 {
00555     if (d->path.isNull() && d->themePath.isNull()) {
00556         return false;
00557     }
00558 
00559     d->createRenderer();
00560     return d->renderer->isValid();
00561 }
00562 
00563 void Svg::setContainsMultipleImages(bool multiple)
00564 {
00565     d->multipleImages = multiple;
00566 }
00567 
00568 bool Svg::containsMultipleImages() const
00569 {
00570     return d->multipleImages;
00571 }
00572 
00573 void Svg::setImagePath(const QString &svgFilePath)
00574 {
00575     d->setImagePath(svgFilePath, this);
00576     d->eraseRenderer();
00577     emit repaintNeeded();
00578 }
00579 
00580 QString Svg::imagePath() const
00581 {
00582    return d->themed ? d->themePath : d->path;
00583 }
00584 
00585 } // Plasma namespace
00586 
00587 #include "svg.moc"
00588 

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
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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