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 #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
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
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
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
00110 if (isThemed == themed &&
00111 ((themed && themePath == imagePath) ||
00112 (!themed && path == imagePath))) {
00113 return false;
00114 }
00115
00116
00117
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
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
00182
00183 Theme *theme = Theme::defaultTheme();
00184 QPixmap p;
00185
00186 if (theme->findInCache(id, p)) {
00187
00188 return p;
00189 } else {
00190
00191 }
00192
00193
00194
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
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
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
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
00261
00262
00263
00264 QHash<QString, SharedSvgRenderer::Ptr>::const_iterator it = s_renderers.constFind(path);
00265
00266 if (it != s_renderers.constEnd()) {
00267
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
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
00362 eraseRenderer();
00363
00364
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
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
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
00542
00543
00544
00545
00546
00547
00548
00549
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 }
00586
00587 #include "svg.moc"
00588