00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
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
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
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
00111 if (isThemed == themed &&
00112 ((themed && themePath == imagePath) ||
00113 (!themed && path == imagePath))) {
00114 return false;
00115 }
00116
00117
00118
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
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
00151
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
00160 actualTheme()->insertIntoRectsCache(path, "_Natural", QRectF(QPointF(0,0), naturalSize));
00161 } else {
00162 naturalSize = rect.size();
00163
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
00205
00206 QPixmap p;
00207 if (cacheRendering) {
00208 if (actualTheme()->findInCache(id, p, lastModified)) {
00209
00210 return p;
00211 }
00212 }
00213
00214
00215
00216
00217
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
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
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
00273
00274
00275
00276 QHash<QString, SharedSvgRenderer::Ptr>::const_iterator it = s_renderers.constFind(path);
00277
00278 if (it != s_renderers.constEnd()) {
00279
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
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
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
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
00384 emit q->repaintNeeded();
00385 }
00386
00387 void colorsChanged()
00388 {
00389 if (!applyColors) {
00390 return;
00391 }
00392
00393 eraseRenderer();
00394
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
00528
00529
00530
00531
00532
00533
00534
00535
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
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 }
00602
00603 #include "svg.moc"
00604