• Skip to content
  • Skip to link menu
KDE 4.3 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 
00027 #include <kcolorscheme.h>
00028 #include <kconfiggroup.h>
00029 #include <kdebug.h>
00030 #include <kiconeffect.h>
00031 #include <kglobalsettings.h>
00032 #include <ksharedptr.h>
00033 #include <ksvgrenderer.h>
00034 
00035 #include "applet.h"
00036 #include "package.h"
00037 #include "theme.h"
00038 
00039 namespace Plasma
00040 {
00041 
00042 class SharedSvgRenderer : public KSvgRenderer, public QSharedData
00043 {
00044     public:
00045         typedef KSharedPtr<SharedSvgRenderer> Ptr;
00046 
00047         SharedSvgRenderer(QObject *parent = 0)
00048             : KSvgRenderer(parent)
00049         {}
00050 
00051         SharedSvgRenderer(const QString &filename, QObject *parent = 0)
00052             : KSvgRenderer(filename, parent)
00053         {}
00054 
00055         SharedSvgRenderer(const QByteArray &contents, QObject *parent = 0)
00056             : KSvgRenderer(contents, parent)
00057         {}
00058 
00059         ~SharedSvgRenderer()
00060         {
00061             //kDebug() << "leaving this world for a better one.";
00062         }
00063 };
00064 
00065 class SvgPrivate
00066 {
00067     public:
00068         SvgPrivate(Svg *svg)
00069             : q(svg),
00070               theme(0),
00071               renderer(0),
00072               lastModified(0),
00073               multipleImages(false),
00074               themed(false),
00075               applyColors(false),
00076               cacheRendering(true)
00077         {
00078         }
00079 
00080         ~SvgPrivate()
00081         {
00082             eraseRenderer();
00083         }
00084 
00085         //This function is meant for the rects cache
00086         QString cacheId(const QString &elementId)
00087         {
00088             if (size.isValid() && size != naturalSize) {
00089                 return QString("%3_%2_%1").arg(int(size.height()))
00090                                         .arg(int(size.width()))
00091                                         .arg(elementId);
00092             } else {
00093                 return QString("%2_%1").arg("Natural")
00094                                         .arg(elementId);
00095             }
00096         }
00097 
00098         //This function is meant for the pixmap cache
00099         QString cachePath(const QString &path, const QSize &size)
00100         {
00101              return QString("%3_%2_%1_").arg(int(size.height()))
00102                                         .arg(int(size.width()))
00103                                         .arg(path);
00104         }
00105 
00106         bool setImagePath(const QString &imagePath)
00107         {
00108             bool isThemed = !QDir::isAbsolutePath(imagePath);
00109 
00110             // lets check to see if we're already set to this file
00111             if (isThemed == themed &&
00112                 ((themed && themePath == imagePath) ||
00113                  (!themed && path == imagePath))) {
00114                 return false;
00115             }
00116 
00117             // if we don't have any path right now and are going to set one,
00118             // then lets not schedule a repaint because we are just initializing!
00119             bool updateNeeded = true; 
00120 
00121             if (themed) {
00122                 QObject::disconnect(actualTheme(), SIGNAL(themeChanged()),
00123                                     q, SLOT(themeChanged()));
00124             }
00125 
00126             themed = isThemed;
00127             path.clear();
00128             themePath.clear();
00129             localRectCache.clear();
00130 
00131             if (themed) {
00132                 themePath = imagePath;
00133                 QObject::connect(actualTheme(), SIGNAL(themeChanged()), q, SLOT(themeChanged()));
00134             } else if (QFile::exists(imagePath)) {
00135                 path = imagePath;
00136             } else {
00137                 kDebug() << "file '" << path << "' does not exist!";
00138             }
00139 
00140             // check if svg wants colorscheme applied
00141             QObject::disconnect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
00142                                 q, SLOT(colorsChanged()));
00143 
00144             checkApplyColorHint();
00145             if (applyColors && !actualTheme()->colorScheme()) {
00146                 QObject::connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
00147                                  q, SLOT(colorsChanged()));
00148             }
00149 
00150             // also images with absolute path needs to have a natural size initialized,
00151             // even if looks a bit weird using Theme to store non-themed stuff
00152             if (themed || QFile::exists(imagePath)) {
00153                 QRectF rect;
00154                 bool found = actualTheme()->findInRectsCache(path, "_Natural", rect);
00155 
00156                 if (!found) {
00157                     createRenderer();
00158                     naturalSize = renderer->defaultSize();
00159                     //kDebug() << "natural size for" << path << "from renderer is" << naturalSize;
00160                     actualTheme()->insertIntoRectsCache(path, "_Natural", QRectF(QPointF(0,0), naturalSize));
00161                 } else {
00162                     naturalSize = rect.size();
00163                     //kDebug() << "natural size for" << path << "from cache is" << naturalSize;
00164                 }
00165             }
00166 
00167             if (!themed) {
00168                 QFile f(imagePath);
00169                 QFileInfo info(f);
00170                 lastModified = info.lastModified().toTime_t();
00171             }
00172 
00173             return updateNeeded;
00174         }
00175 
00176         Theme *actualTheme()
00177         {
00178             if (!theme) {
00179                 theme = Plasma::Theme::defaultTheme();
00180             }
00181 
00182             return theme;
00183         }
00184 
00185         QPixmap findInCache(const QString &elementId, const QSizeF &s = QSizeF())
00186         {
00187             QSize size;
00188             if (elementId.isEmpty() || (multipleImages && s.isValid())) {
00189                 size = s.toSize();
00190             } else {
00191                 size = elementRect(elementId).size().toSize();
00192             }
00193 
00194             if (size.isEmpty()) {
00195                 return QPixmap();
00196             }
00197 
00198             QString id = cachePath(path, size);
00199 
00200             if (!elementId.isEmpty()) {
00201                 id.append(elementId);
00202             }
00203 
00204             //kDebug() << "id is " << id;
00205 
00206             QPixmap p;
00207             if (cacheRendering) {
00208                 if (actualTheme()->findInCache(id, p, lastModified)) {
00209                     //kDebug() << "found cached version of " << id << p.size();
00210                     return p;
00211                 }
00212             }
00213 
00214             //kDebug() << "didn't find cached version of " << id << ", so re-rendering";
00215 
00216             //kDebug() << "size for " << elementId << " is " << s;
00217             // we have to re-render this puppy
00218 
00219             p = QPixmap(size);
00220 
00221             p.fill(Qt::transparent);
00222             QPainter renderPainter(&p);
00223 
00224             createRenderer();
00225             if (elementId.isEmpty()) {
00226                 renderer->render(&renderPainter);
00227             } else {
00228                 renderer->render(&renderPainter, elementId);
00229             }
00230 
00231             renderPainter.end();
00232 
00233             // Apply current color scheme if the svg asks for it
00234             if (applyColors) {
00235                 QImage itmp = p.toImage();
00236                 KIconEffect::colorize(itmp, actualTheme()->color(Theme::BackgroundColor), 1.0);
00237                 p = p.fromImage(itmp);
00238             }
00239 
00240             if (cacheRendering) {
00241                 actualTheme()->insertIntoCache(id, p, QString::number((qint64)q, 16)+elementId);
00242             }
00243 
00244             return p;
00245         }
00246 
00247         void createRenderer()
00248         {
00249             if (renderer) {
00250                 return;
00251             }
00252 
00253             //kDebug() << kBacktrace();
00254             if (themed && path.isEmpty()) {
00255                 Applet *applet = qobject_cast<Applet*>(q->parent());
00256                 if (applet && applet->package()) {
00257                     path = applet->package()->filePath("images", themePath + ".svg");
00258 
00259                     if (path.isEmpty()) {
00260                         path = applet->package()->filePath("images", themePath + ".svgz");
00261                     }
00262                 }
00263 
00264                 if (path.isEmpty()) {
00265                     path = actualTheme()->imagePath(themePath);
00266                     if (path.isEmpty()) {
00267                         kWarning() << "No image path found for" << themePath;
00268                     }
00269                 }
00270             }
00271 
00272             //kDebug() << "********************************";
00273             //kDebug() << "FAIL! **************************";
00274             //kDebug() << path << "**";
00275 
00276             QHash<QString, SharedSvgRenderer::Ptr>::const_iterator it = s_renderers.constFind(path);
00277 
00278             if (it != s_renderers.constEnd()) {
00279                 //kDebug() << "gots us an existing one!";
00280                 renderer = it.value();
00281             } else {
00282                 if (path.isEmpty())
00283                     renderer = new SharedSvgRenderer();
00284                 else
00285                     renderer = new SharedSvgRenderer(path);
00286                 s_renderers[path] = renderer;
00287             }
00288 
00289             if (size == QSizeF()) {
00290                 size = renderer->defaultSize();
00291             }
00292         }
00293 
00294         void eraseRenderer()
00295         {
00296             if (renderer && renderer.count() == 2) {
00297                 // this and the cache reference it
00298                 s_renderers.erase(s_renderers.find(path));
00299 
00300                 if (theme) {
00301                     theme->releaseRectsCache(path);
00302                 }
00303             }
00304 
00305             renderer = 0;
00306             localRectCache.clear();
00307         }
00308 
00309         QRectF elementRect(const QString &elementId)
00310         {
00311             if (themed && path.isEmpty()) {
00312                 path = actualTheme()->imagePath(themePath);
00313             }
00314 
00315             QString id = cacheId(elementId);
00316             if (localRectCache.contains(id)) {
00317                 return localRectCache.value(id);
00318             }
00319 
00320             QRectF rect;
00321             bool found = actualTheme()->findInRectsCache(path, id, rect);
00322 
00323             if (found) {
00324                 localRectCache.insert(id, rect);
00325                 return rect;
00326             }
00327 
00328             return findAndCacheElementRect(elementId);
00329         }
00330 
00331         QRectF findAndCacheElementRect(const QString &elementId)
00332         {
00333             createRenderer();
00334             QRectF elementRect = renderer->elementExists(elementId) ?
00335                                  renderer->boundsOnElement(elementId) : QRectF();
00336             naturalSize = renderer->defaultSize();
00337             //kDebug() << "natural size for" << path << "is" << naturalSize;
00338             qreal dx = size.width() / naturalSize.width();
00339             qreal dy = size.height() / naturalSize.height();
00340 
00341             elementRect = QRectF(elementRect.x() * dx, elementRect.y() * dy,
00342                                  elementRect.width() * dx, elementRect.height() * dy);
00343 
00344             actualTheme()->insertIntoRectsCache(path, cacheId(elementId), elementRect);
00345             return elementRect;
00346         }
00347 
00348         QMatrix matrixForElement(const QString &elementId)
00349         {
00350             createRenderer();
00351             return renderer->matrixForElement(elementId);
00352         }
00353 
00354         void checkApplyColorHint()
00355         {
00356             applyColors = elementRect("hint-apply-color-scheme").isValid();
00357         }
00358 
00359         void themeChanged()
00360         {
00361             // check if new theme svg wants colorscheme applied
00362             bool wasApplyColors = applyColors;
00363             checkApplyColorHint();
00364             if (applyColors && actualTheme()->colorScheme()) {
00365                 if (!wasApplyColors) {
00366                     QObject::connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
00367                                      q, SLOT(colorsChanged()));
00368                 }
00369             } else {
00370                 QObject::disconnect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
00371                                     q, SLOT(colorsChanged()));
00372             }
00373 
00374             if (!themed) {
00375                 return;
00376             }
00377 
00378             QString currentPath = themePath;
00379             themePath.clear();
00380             eraseRenderer();
00381             setImagePath(currentPath);
00382 
00383             //kDebug() << themePath << ">>>>>>>>>>>>>>>>>> theme changed";
00384             emit q->repaintNeeded();
00385         }
00386 
00387         void colorsChanged()
00388         {
00389             if (!applyColors) {
00390                 return;
00391             }
00392 
00393             eraseRenderer();
00394             //kDebug() << "repaint needed from colorsChanged";
00395             emit q->repaintNeeded();
00396         }
00397 
00398         static QHash<QString, SharedSvgRenderer::Ptr> s_renderers;
00399 
00400         Svg *q;
00401         QPointer<Theme> theme;
00402         QHash<QString, QRectF> localRectCache;
00403         SharedSvgRenderer::Ptr renderer;
00404         QString themePath;
00405         QString path;
00406         QSizeF size;
00407         QSizeF naturalSize;
00408         unsigned int lastModified;
00409         bool multipleImages : 1;
00410         bool themed : 1;
00411         bool applyColors : 1;
00412         bool cacheRendering : 1;
00413 };
00414 
00415 QHash<QString, SharedSvgRenderer::Ptr> SvgPrivate::s_renderers;
00416 
00417 Svg::Svg(QObject *parent)
00418     : QObject(parent),
00419       d(new SvgPrivate(this))
00420 {
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(QRectF(rect.topLeft(), pix.size()), 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     return d->elementRect(elementId).isValid();
00519 }
00520 
00521 QString Svg::elementAtPoint(const QPoint &point) const
00522 {
00523     Q_UNUSED(point)
00524 
00525     return QString();
00526 /*
00527 FIXME: implement when Qt can support us!
00528     d->createRenderer();
00529     QSizeF naturalSize = d->renderer->defaultSize();
00530     qreal dx = d->size.width() / naturalSize.width();
00531     qreal dy = d->size.height() / naturalSize.height();
00532     //kDebug() << point << "is really"
00533     //         << QPoint(point.x() *dx, naturalSize.height() - point.y() * dy);
00534 
00535     return QString(); // d->renderer->elementAtPoint(QPoint(point.x() *dx, naturalSize.height() - point.y() * dy));
00536     */
00537 }
00538 
00539 bool Svg::isValid() const
00540 {
00541     if (d->path.isNull() && d->themePath.isNull()) {
00542         return false;
00543     }
00544 
00545     d->createRenderer();
00546     return d->renderer->isValid();
00547 }
00548 
00549 void Svg::setContainsMultipleImages(bool multiple)
00550 {
00551     d->multipleImages = multiple;
00552 }
00553 
00554 bool Svg::containsMultipleImages() const
00555 {
00556     return d->multipleImages;
00557 }
00558 
00559 void Svg::setImagePath(const QString &svgFilePath)
00560 {
00561     d->eraseRenderer();
00562     d->setImagePath(svgFilePath);
00563     //kDebug() << "repaintNeeded";
00564     emit repaintNeeded();
00565 }
00566 
00567 QString Svg::imagePath() const
00568 {
00569    return d->themed ? d->themePath : d->path;
00570 }
00571 
00572 void Svg::setUsingRenderingCache(bool useCache)
00573 {
00574     d->cacheRendering = useCache;
00575 }
00576 
00577 bool Svg::isUsingRenderingCache() const
00578 {
00579     return d->cacheRendering;
00580 }
00581 
00582 void Svg::setTheme(Plasma::Theme *theme)
00583 {
00584     if (d->theme) {
00585         disconnect(d->theme, 0, this, 0);
00586     }
00587 
00588     d->theme = theme;
00589     if (!imagePath().isEmpty()) {
00590         QString path = imagePath();
00591         d->themePath.clear();
00592         setImagePath(path);
00593     }
00594 }
00595 
00596 Theme *Svg::theme() const
00597 {
00598     return d->theme;
00599 }
00600 
00601 } // Plasma namespace
00602 
00603 #include "svg.moc"
00604 

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
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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