• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

Plasma

framesvg.cpp

Go to the documentation of this file.
00001 /*
00002  *   Copyright 2008-2010 by Aaron Seigo <aseigo@kde.org>
00003  *   Copyright 2008-2010 Marco Martin <notmart@gmail.com>
00004  *
00005  *   This program is free software; you can redistribute it and/or modify
00006  *   it under the terms of the GNU Library General Public License as
00007  *   published by the Free Software Foundation; either version 2, or
00008  *   (at your option) any later version.
00009  *
00010  *   This program is distributed in the hope that it will be useful,
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *   GNU General Public License for more details
00014  *
00015  *   You should have received a copy of the GNU Library General Public
00016  *   License along with this program; if not, write to the
00017  *   Free Software Foundation, Inc.,
00018  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00019  */
00020 
00021 #include "framesvg.h"
00022 #include "private/framesvg_p.h"
00023 
00024 #include <QAtomicInt>
00025 #include <QBitmap>
00026 #include <QCryptographicHash>
00027 #include <QPainter>
00028 #include <QRegion>
00029 #include <QSize>
00030 #include <QStringBuilder>
00031 #include <QTimer>
00032 
00033 #include <kdebug.h>
00034 
00035 #include <applet.h>
00036 #include <theme.h>
00037 #include <private/svg_p.h>
00038 
00039 namespace Plasma
00040 {
00041 
00042 
00043 QHash<QString, FrameData *> FrameSvgPrivate::s_sharedFrames;
00044 
00045 // Any attempt to generate a frame whose width or height is larger than this
00046 // will be rejected
00047 static const int MAX_FRAME_SIZE = 100000;
00048 
00049 FrameSvg::FrameSvg(QObject *parent)
00050     : Svg(parent),
00051       d(new FrameSvgPrivate(this))
00052 {
00053     connect(this, SIGNAL(repaintNeeded()), this, SLOT(updateNeeded()));
00054     d->frames.insert(QString(), new FrameData(this));
00055 }
00056 
00057 FrameSvg::~FrameSvg()
00058 {
00059     delete d;
00060 }
00061 
00062 void FrameSvg::setImagePath(const QString &path)
00063 {
00064     if (path == imagePath()) {
00065         return;
00066     }
00067 
00068     bool updateNeeded = true;
00069     clearCache();
00070 
00071     FrameData *fd = d->frames[d->prefix];
00072     if (fd->refcount() == 1) {
00073         // we're the only user of it, let's remove it from the shared keys
00074         // we don't want to deref it, however, as we'll still be using it
00075         const QString oldKey = d->cacheId(fd, d->prefix);
00076         FrameSvgPrivate::s_sharedFrames.remove(oldKey);
00077     } else {
00078         // others are using this frame, so deref it for ourselves
00079         fd->deref(this);
00080         fd = 0;
00081     }
00082 
00083     Svg::d->setImagePath(path);
00084 
00085     if (!fd) {
00086         // we need to replace our frame, start by looking in the frame cache
00087         FrameData *oldFd = d->frames[d->prefix];
00088         const QString key = d->cacheId(oldFd, d->prefix);
00089         fd = FrameSvgPrivate::s_sharedFrames.value(key);
00090 
00091         if (fd) {
00092             // we found one, so ref it and use it; we also don't need to (or want to!)
00093             // trigger a full update of the frame since it is already the one we want
00094             // and likely already rendered just fine
00095             fd->ref(this);
00096             updateNeeded = false;
00097         } else {
00098             // nothing exists for us in the cache, so create a new FrameData based
00099             // on the old one
00100             fd = new FrameData(*oldFd, this);
00101         }
00102 
00103         d->frames.insert(d->prefix, fd);
00104     }
00105 
00106     setContainsMultipleImages(true);
00107     if (updateNeeded) {
00108         // ensure our frame is in the cache
00109         const QString key = d->cacheId(fd, d->prefix);
00110         FrameSvgPrivate::s_sharedFrames.insert(key, fd);
00111 
00112         // this will emit repaintNeeded() as well when it is done
00113         d->updateAndSignalSizes();
00114     } else {
00115         emit repaintNeeded();
00116     }
00117 }
00118 
00119 void FrameSvg::setEnabledBorders(const EnabledBorders borders)
00120 {
00121     if (borders == d->frames[d->prefix]->enabledBorders) {
00122         return;
00123     }
00124 
00125     FrameData *fd = d->frames[d->prefix];
00126 
00127     const QString oldKey = d->cacheId(fd, d->prefix);
00128     const EnabledBorders oldBorders = fd->enabledBorders;
00129     fd->enabledBorders = borders;
00130     const QString newKey = d->cacheId(fd, d->prefix);
00131     fd->enabledBorders = oldBorders;
00132 
00133     //kDebug() << "looking for" << newKey;
00134     FrameData *newFd = FrameSvgPrivate::s_sharedFrames.value(newKey);
00135     if (newFd) {
00136         //kDebug() << "FOUND IT!" << newFd->refcount;
00137         // we've found a math, so insert that new one and ref it ..
00138         newFd->ref(this);
00139         d->frames.insert(d->prefix, newFd);
00140 
00141         //.. then deref the old one and if it's no longer used, get rid of it
00142         if (fd->deref(this)) {
00143             //const QString oldKey = d->cacheId(fd, d->prefix);
00144             //kDebug() << "1. Removing it" << oldKey << fd->refcount;
00145             FrameSvgPrivate::s_sharedFrames.remove(oldKey);
00146             delete fd;
00147         }
00148 
00149         return;
00150     }
00151 
00152     if (fd->refcount() == 1) {
00153         // we're the only user of it, let's remove it from the shared keys
00154         // we don't want to deref it, however, as we'll still be using it
00155         FrameSvgPrivate::s_sharedFrames.remove(oldKey);
00156     } else {
00157         // others are using it, but we wish to change its size. so deref it,
00158         // then create a copy of it (we're automatically ref'd via the ctor),
00159         // then insert it into our frames.
00160         fd->deref(this);
00161         fd = new FrameData(*fd, this);
00162         d->frames.insert(d->prefix, fd);
00163     }
00164 
00165     fd->enabledBorders = borders;
00166     d->updateAndSignalSizes();
00167 }
00168 
00169 FrameSvg::EnabledBorders FrameSvg::enabledBorders() const
00170 {
00171     if (d->frames.isEmpty()) {
00172         return NoBorder;
00173     }
00174 
00175     QHash<QString, FrameData*>::const_iterator it = d->frames.constFind(d->prefix);
00176 
00177     if (it != d->frames.constEnd()) {
00178         return it.value()->enabledBorders;
00179     } else {
00180         return NoBorder;
00181     }
00182 }
00183 
00184 void FrameSvg::setElementPrefix(Plasma::Location location)
00185 {
00186     switch (location) {
00187         case TopEdge:
00188             setElementPrefix("north");
00189             break;
00190         case BottomEdge:
00191             setElementPrefix("south");
00192             break;
00193         case LeftEdge:
00194             setElementPrefix("west");
00195             break;
00196         case RightEdge:
00197             setElementPrefix("east");
00198             break;
00199         default:
00200             setElementPrefix(QString());
00201             break;
00202     }
00203 
00204     d->location = location;
00205 }
00206 
00207 void FrameSvg::setElementPrefix(const QString &prefix)
00208 {
00209     const QString oldPrefix(d->prefix);
00210 
00211     if (!hasElement(prefix % "-center")) {
00212         d->prefix.clear();
00213     } else {
00214         d->prefix = prefix;
00215         if (!d->prefix.isEmpty()) {
00216             d->prefix += '-';
00217         }
00218     }
00219 
00220     FrameData *oldFrameData = d->frames.value(oldPrefix);
00221     if (oldPrefix == d->prefix && oldFrameData) {
00222         return;
00223     }
00224 
00225     if (!d->frames.contains(d->prefix)) {
00226         if (oldFrameData) {
00227             FrameData *newFd = 0;
00228             if (!oldFrameData->frameSize.isEmpty()) {
00229                 const QString key = d->cacheId(oldFrameData, d->prefix);
00230                 newFd = FrameSvgPrivate::s_sharedFrames.value(key);
00231             }
00232 
00233             // we need to put this in the cache if we didn't find it in the shared frames
00234             // and we have a size; if we don't have a size, we'll catch it later
00235             const bool cache = !newFd && !oldFrameData->frameSize.isEmpty();
00236             if (newFd) {
00237                 newFd->ref(this);
00238             } else  {
00239                 newFd = new FrameData(*oldFrameData, this);
00240             }
00241 
00242             d->frames.insert(d->prefix, newFd);
00243 
00244             if (cache) {
00245                 // we have to cache after inserting the frame since the cacheId requires the
00246                 // frame to be in the frames collection already
00247                 const QString key = d->cacheId(oldFrameData, d->prefix);
00248                 //kDebug() << this << "     1. inserting as" << key;
00249 
00250                 FrameSvgPrivate::s_sharedFrames.insert(key, newFd);
00251             }
00252         } else {
00253             // couldn't find anything useful, so we just create something here
00254             // we don't have a size for it yet, so don't bother trying to share it just yet
00255             FrameData *newFd = new FrameData(this);
00256             d->frames.insert(d->prefix, newFd);
00257         }
00258 
00259         d->updateSizes();
00260     }
00261 
00262     if (!d->cacheAll) {
00263         d->frames.remove(oldPrefix);
00264         if (oldFrameData) {
00265             if (oldFrameData->deref(this)) {
00266                 const QString oldKey = d->cacheId(oldFrameData, oldPrefix);
00267                 FrameSvgPrivate::s_sharedFrames.remove(oldKey);
00268                 delete oldFrameData;
00269             }
00270         }
00271     }
00272 
00273     d->location = Floating;
00274 }
00275 
00276 bool FrameSvg::hasElementPrefix(const QString & prefix) const
00277 {
00278     //for now it simply checks if a center element exists,
00279     //because it could make sense for certain themes to not have all the elements
00280     if (prefix.isEmpty()) {
00281         return hasElement("center");
00282     } else {
00283         return hasElement(prefix % "-center");
00284     }
00285 }
00286 
00287 bool FrameSvg::hasElementPrefix(Plasma::Location location) const
00288 {
00289     switch (location) {
00290         case TopEdge:
00291             return hasElementPrefix("north");
00292             break;
00293         case BottomEdge:
00294             return hasElementPrefix("south");
00295             break;
00296         case LeftEdge:
00297             return hasElementPrefix("west");
00298             break;
00299         case RightEdge:
00300             return hasElementPrefix("east");
00301             break;
00302         default:
00303             return hasElementPrefix(QString());
00304             break;
00305     }
00306 }
00307 
00308 QString FrameSvg::prefix()
00309 {
00310     if (d->prefix.isEmpty()) {
00311         return d->prefix;
00312     }
00313 
00314     return d->prefix.left(d->prefix.size() - 1);
00315 }
00316 
00317 void FrameSvg::resizeFrame(const QSizeF &size)
00318 {
00319     if (imagePath().isEmpty()) {
00320         return;
00321     }
00322 
00323     if (size.isEmpty()) {
00324         kWarning() << "Invalid size" << size;
00325         return;
00326     }
00327 
00328     FrameData *fd = d->frames[d->prefix];
00329     if (size == fd->frameSize) {
00330         return;
00331     }
00332 
00333     const QString oldKey = d->cacheId(fd, d->prefix);
00334     const QSize currentSize = fd->frameSize;
00335     fd->frameSize = size.toSize();
00336     const QString newKey = d->cacheId(fd, d->prefix);
00337     fd->frameSize = currentSize;
00338 
00339     //kDebug() << "looking for" << newKey;
00340     FrameData *newFd = FrameSvgPrivate::s_sharedFrames.value(newKey);
00341     if (newFd) {
00342         //kDebug() << "FOUND IT!" << newFd->refcount;
00343         // we've found a math, so insert that new one and ref it ..
00344         newFd->ref(this);
00345         d->frames.insert(d->prefix, newFd);
00346 
00347         //.. then deref the old one and if it's no longer used, get rid of it
00348         if (fd->deref(this)) {
00349             //const QString oldKey = d->cacheId(fd, d->prefix);
00350             //kDebug() << "1. Removing it" << oldKey << fd->refcount;
00351             FrameSvgPrivate::s_sharedFrames.remove(oldKey);
00352             delete fd;
00353         }
00354 
00355         return;
00356     }
00357 
00358     if (fd->refcount() == 1) {
00359         // we're the only user of it, let's remove it from the shared keys
00360         // we don't want to deref it, however, as we'll still be using it
00361         FrameSvgPrivate::s_sharedFrames.remove(oldKey);
00362     } else {
00363         // others are using it, but we wish to change its size. so deref it,
00364         // then create a copy of it (we're automatically ref'd via the ctor),
00365         // then insert it into our frames.
00366         fd->deref(this);
00367         fd = new FrameData(*fd, this);
00368         d->frames.insert(d->prefix, fd);
00369     }
00370 
00371     d->updateSizes();
00372     fd->frameSize = size.toSize();
00373     // we know it isn't in s_sharedFrames due to the check above, so insert it now
00374     FrameSvgPrivate::s_sharedFrames.insert(newKey, fd);
00375 }
00376 
00377 QSizeF FrameSvg::frameSize() const
00378 {
00379     QHash<QString, FrameData*>::const_iterator it = d->frames.constFind(d->prefix);
00380 
00381     if (it == d->frames.constEnd()) {
00382         return QSize(-1, -1);
00383     } else {
00384         return d->frameSize(it.value());
00385     }
00386 }
00387 
00388 qreal FrameSvg::marginSize(const Plasma::MarginEdge edge) const
00389 {
00390     if (d->frames[d->prefix]->noBorderPadding) {
00391         return .0;
00392     }
00393 
00394     switch (edge) {
00395     case Plasma::TopMargin:
00396         return d->frames[d->prefix]->topMargin;
00397     break;
00398 
00399     case Plasma::LeftMargin:
00400         return d->frames[d->prefix]->leftMargin;
00401     break;
00402 
00403     case Plasma::RightMargin:
00404         return d->frames[d->prefix]->rightMargin;
00405     break;
00406 
00407     //Plasma::BottomMargin
00408     default:
00409         return d->frames[d->prefix]->bottomMargin;
00410     break;
00411     }
00412 }
00413 
00414 void FrameSvg::getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const
00415 {
00416     FrameData *frame = d->frames[d->prefix];
00417 
00418     if (frame->noBorderPadding) {
00419         left = top = right = bottom = 0;
00420         return;
00421     }
00422 
00423     top = frame->topMargin;
00424     left = frame->leftMargin;
00425     right = frame->rightMargin;
00426     bottom = frame->bottomMargin;
00427 }
00428 
00429 QRectF FrameSvg::contentsRect() const
00430 {
00431     QSizeF size(frameSize());
00432 
00433     if (size.isValid()) {
00434         QRectF rect(QPointF(0, 0), size);
00435         FrameData *frame = d->frames[d->prefix];
00436 
00437         return rect.adjusted(frame->leftMargin, frame->topMargin,
00438                              -frame->rightMargin, -frame->bottomMargin);
00439     } else {
00440         return QRectF();
00441     }
00442 }
00443 
00444 QPixmap FrameSvg::alphaMask() const
00445 {
00446     //FIXME: the distinction between overlay and 
00447     return d->alphaMask();
00448 }
00449 
00450 QRegion FrameSvg::mask() const
00451 {
00452     FrameData *frame = d->frames[d->prefix];
00453     QString id = d->cacheId(frame, QString());
00454     if (!frame->cachedMasks.contains(id)) {
00455         //TODO: Implement a better way to cap the number of cached masks
00456         if (frame->cachedMasks.count() > frame->MAX_CACHED_MASKS) {
00457             frame->cachedMasks.clear();
00458         }
00459         frame->cachedMasks.insert(id, QRegion(QBitmap(d->alphaMask().alphaChannel().createMaskFromColor(Qt::black))));
00460     }
00461     return frame->cachedMasks[id];
00462 }
00463 
00464 void FrameSvg::setCacheAllRenderedFrames(bool cache)
00465 {
00466     if (d->cacheAll && !cache) {
00467         clearCache();
00468     }
00469 
00470     d->cacheAll = cache;
00471 }
00472 
00473 bool FrameSvg::cacheAllRenderedFrames() const
00474 {
00475     return d->cacheAll;
00476 }
00477 
00478 void FrameSvg::clearCache()
00479 {
00480     FrameData *frame = d->frames[d->prefix];
00481 
00482     // delete all the frames that aren't this one
00483     QMutableHashIterator<QString, FrameData*> it(d->frames);
00484     while (it.hasNext()) {
00485         FrameData *p = it.next().value();
00486         if (frame != p) {
00487             //TODO: should we clear from the Theme pixmap cache as well?
00488             if (p->deref(this)) {
00489                 const QString key = d->cacheId(p, it.key());
00490                 FrameSvgPrivate::s_sharedFrames.remove(key);
00491                 p->cachedBackground = QPixmap();
00492             }
00493 
00494             it.remove();
00495         }
00496     }
00497 }
00498 
00499 QPixmap FrameSvg::framePixmap()
00500 {
00501     FrameData *frame = d->frames[d->prefix];
00502     if (frame->cachedBackground.isNull()) {
00503         d->generateBackground(frame);
00504         if (frame->cachedBackground.isNull()) {
00505             return QPixmap();
00506         }
00507     }
00508 
00509     return frame->cachedBackground;
00510 }
00511 
00512 void FrameSvg::paintFrame(QPainter *painter, const QRectF &target, const QRectF &source)
00513 {
00514     FrameData *frame = d->frames[d->prefix];
00515     if (frame->cachedBackground.isNull()) {
00516         d->generateBackground(frame);
00517         if (frame->cachedBackground.isNull()) {
00518             return;
00519         }
00520     }
00521 
00522     painter->drawPixmap(target, frame->cachedBackground, source.isValid() ? source : target);
00523 }
00524 
00525 void FrameSvg::paintFrame(QPainter *painter, const QPointF &pos)
00526 {
00527     FrameData *frame = d->frames[d->prefix];
00528     if (frame->cachedBackground.isNull()) {
00529         d->generateBackground(frame);
00530         if (frame->cachedBackground.isNull()) {
00531             return;
00532         }
00533     }
00534 
00535     painter->drawPixmap(pos, frame->cachedBackground);
00536 }
00537 
00538 //#define DEBUG_FRAMESVG_CACHE
00539 FrameSvgPrivate::~FrameSvgPrivate()
00540 {
00541 #ifdef DEBUG_FRAMESVG_CACHE
00542     kDebug() << "*************" << q << q->imagePath() << "****************";
00543 #endif
00544 
00545     QHashIterator<QString, FrameData *> it(frames);
00546     while (it.hasNext()) {
00547         it.next();
00548         if (it.value()) {
00549             // we remove all references from this widget to the frame, and delete it if we're the
00550             // last user
00551             if (it.value()->removeRefs(q)) {
00552                 const QString key = cacheId(it.value(), it.key());
00553 #ifdef DEBUG_FRAMESVG_CACHE
00554                 kDebug() << "2. Removing it" << key << it.value() << it.value()->refcount() << s_sharedFrames.contains(key);
00555 #endif
00556                 s_sharedFrames.remove(key);
00557                 delete it.value();
00558             }
00559 #ifdef DEBUG_FRAMESVG_CACHE
00560             else {
00561                 kDebug() << "still shared:" << cacheId(it.value(), it.key()) << it.value() << it.value()->refcount() << it.value()->isUsed();
00562             }
00563         } else {
00564             kDebug() << "lost our value for" << it.key();
00565 #endif
00566         }
00567     }
00568 
00569 #ifdef DEBUG_FRAMESVG_CACHE
00570     QHashIterator<QString, FrameData *> it2(s_sharedFrames);
00571     int shares = 0;
00572     while (it2.hasNext()) {
00573         it2.next();
00574         const int rc = it2.value()->refcount();
00575         if (rc == 0) {
00576             kDebug() << "     LOST!" << it2.key() << rc << it2.value();// << it2.value()->references;
00577         } else {
00578             kDebug() << "          " << it2.key() << rc << it2.value();
00579             foreach (FrameSvg *data, it2.value()->references.keys()) {
00580                 kDebug( )<< "            " << (void*)data << it2.value()->references[data];
00581             }
00582             shares += rc - 1;
00583         }
00584     }
00585     kDebug() << "#####################################" << s_sharedFrames.count() << ", pixmaps saved:" << shares;
00586 #endif
00587 
00588     frames.clear();
00589 }
00590 
00591 QPixmap FrameSvgPrivate::alphaMask()
00592 {
00593     FrameData *frame = frames[prefix];
00594     QString maskPrefix;
00595 
00596     if (q->hasElement("mask-" % prefix % "center")) {
00597         maskPrefix = "mask-";
00598     }
00599 
00600     if (maskPrefix.isNull()) {
00601         if (frame->cachedBackground.isNull()) {
00602             generateBackground(frame);
00603             if (frame->cachedBackground.isNull()) {
00604                 return QPixmap();
00605             }
00606         }
00607 
00608         return frame->cachedBackground;
00609     } else {
00610         QString oldPrefix = prefix;
00611 
00612         // We are setting the prefix only temporary to generate
00613         // the needed mask image
00614         prefix = maskPrefix % oldPrefix;
00615 
00616         if (!frames.contains(prefix)) {
00617             const QString key = cacheId(frame, prefix);
00618             // see if we can find a suitable candidate in the shared frames
00619             // if successful, ref and insert, otherwise create a new one
00620             // and insert that into both the shared frames and our frames.
00621             FrameData *maskFrame = s_sharedFrames.value(key);
00622 
00623             if (maskFrame) {
00624                 maskFrame->ref(q);
00625             } else {
00626                 maskFrame = new FrameData(*frame, q);
00627                 s_sharedFrames.insert(key, maskFrame);
00628             }
00629 
00630             frames.insert(prefix, maskFrame);
00631             updateSizes();
00632         }
00633 
00634         FrameData *maskFrame = frames[prefix];
00635         if (maskFrame->cachedBackground.isNull() || maskFrame->frameSize != frameSize(frame)) {
00636             const QString oldKey = cacheId(maskFrame, prefix);
00637             maskFrame->frameSize = frameSize(frame).toSize();
00638             const QString newKey = cacheId(maskFrame, prefix);
00639             if (s_sharedFrames.contains(oldKey)) {
00640                 s_sharedFrames.remove(oldKey);
00641                 s_sharedFrames.insert(newKey, maskFrame);
00642             }
00643 
00644             maskFrame->cachedBackground = QPixmap();
00645 
00646             generateBackground(maskFrame);
00647             if (maskFrame->cachedBackground.isNull()) {
00648                 return QPixmap();
00649             }
00650         }
00651 
00652         prefix = oldPrefix;
00653         return maskFrame->cachedBackground;
00654     }
00655 }
00656 
00657 void FrameSvgPrivate::generateBackground(FrameData *frame)
00658 {
00659     if (!frame->cachedBackground.isNull() || !q->hasElementPrefix(q->prefix())) {
00660         return;
00661     }
00662 
00663     const QString id = cacheId(frame, prefix);
00664 
00665     Theme *theme = Theme::defaultTheme();
00666     bool frameCached = !frame->cachedBackground.isNull();
00667     bool overlayCached = false;
00668     const bool overlayAvailable = !prefix.startsWith("mask-") && q->hasElement(prefix % "overlay");
00669     QPixmap overlay;
00670     if (q->isUsingRenderingCache()) {
00671         frameCached = theme->findInCache(id, frame->cachedBackground) && !frame->cachedBackground.isNull();
00672 
00673         if (overlayAvailable) {
00674             overlayCached = theme->findInCache("overlay_" % id, overlay) && !overlay.isNull();
00675         }
00676     }
00677 
00678     if (!frameCached) {
00679         generateFrameBackground(frame);
00680     }
00681 
00682     //Overlays
00683     QSize overlaySize;
00684     QPoint actualOverlayPos = QPoint(0, 0);
00685     if (overlayAvailable && !overlayCached) {
00686         QPoint pos = QPoint(0, 0);
00687         overlaySize = q->elementSize(prefix % "overlay");
00688 
00689         //Random pos, stretched and tiled are mutually exclusive
00690         if (q->hasElement(prefix % "hint-overlay-random-pos")) {
00691             actualOverlayPos = overlayPos;
00692         } else if (q->hasElement(prefix % "hint-overlay-pos-right")) {
00693             actualOverlayPos.setX(frame->frameSize.width() - overlaySize.width());
00694         } else if (q->hasElement(prefix % "hint-overlay-pos-bottom")) {
00695             actualOverlayPos.setY(frame->frameSize.height() - overlaySize.height());
00696         //Stretched or Tiled?
00697         } else if (q->hasElement(prefix % "hint-overlay-stretch")) {
00698             overlaySize = frameSize(frame).toSize();
00699         } else {
00700             if (q->hasElement(prefix % "hint-overlay-tile-horizontal")) {
00701                 overlaySize.setWidth(frameSize(frame).width());
00702             }
00703             if (q->hasElement(prefix % "hint-overlay-tile-vertical")) {
00704                 overlaySize.setHeight(frameSize(frame).height());
00705             }
00706         }
00707 
00708         overlay = alphaMask();
00709         QPainter overlayPainter(&overlay);
00710         overlayPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
00711         //Tiling?
00712         if (q->hasElement(prefix % "hint-overlay-tile-horizontal") ||
00713             q->hasElement(prefix % "hint-overlay-tile-vertical")) {
00714 
00715             QSize s = q->size();
00716             q->resize(q->elementSize(prefix % "overlay"));
00717 
00718             overlayPainter.drawTiledPixmap(QRect(QPoint(0,0), overlaySize), q->pixmap(prefix % "overlay"));
00719             q->resize(s);
00720         } else {
00721             q->paint(&overlayPainter, QRect(actualOverlayPos, overlaySize), prefix % "overlay");
00722         }
00723 
00724         overlayPainter.end();
00725     }
00726 
00727     if (!frameCached) {
00728         cacheFrame(prefix, frame->cachedBackground, overlayCached ? overlay : QPixmap());
00729     }
00730 
00731     if (!overlay.isNull()) {
00732         QPainter p(&frame->cachedBackground);
00733         p.setCompositionMode(QPainter::CompositionMode_SourceOver);
00734         p.drawPixmap(actualOverlayPos, overlay, QRect(actualOverlayPos, overlaySize));
00735     }
00736 }
00737 
00738 void FrameSvgPrivate::generateFrameBackground(FrameData *frame)
00739 {
00740     //kDebug() << "generating background";
00741     const QSizeF size = frameSize(frame);
00742     const int topWidth = q->elementSize(prefix % "top").width();
00743     const int leftHeight = q->elementSize(prefix % "left").height();
00744     const int topOffset = 0;
00745     const int leftOffset = 0;
00746 
00747 
00748     if (!size.isValid()) {
00749         kWarning() << "Invalid frame size" << size;
00750         return;
00751     }
00752     if (size.width() >= MAX_FRAME_SIZE || size.height() >= MAX_FRAME_SIZE) {
00753         kWarning() << "Not generating frame background for a size whose width or height is more than" << MAX_FRAME_SIZE << size;
00754         return;
00755     }
00756 
00757     const int contentWidth = size.width() - frame->leftWidth  - frame->rightWidth;
00758     const int contentHeight = size.height() - frame->topHeight  - frame->bottomHeight;
00759     int contentTop = 0;
00760     int contentLeft = 0;
00761     int rightOffset = contentWidth;
00762     int bottomOffset = contentHeight;
00763 
00764     frame->cachedBackground = QPixmap(frame->leftWidth + contentWidth + frame->rightWidth,
00765                                       frame->topHeight + contentHeight + frame->bottomHeight);
00766     frame->cachedBackground.fill(Qt::transparent);
00767     QPainter p(&frame->cachedBackground);
00768     p.setCompositionMode(QPainter::CompositionMode_Source);
00769     p.setRenderHint(QPainter::SmoothPixmapTransform);
00770 
00771     //CENTER
00772     if (frame->tileCenter) {
00773         if (contentHeight > 0 && contentWidth > 0) {
00774             const int centerTileHeight = q->elementSize(prefix % "center").height();
00775             const int centerTileWidth = q->elementSize(prefix % "center").width();
00776             QPixmap center(centerTileWidth, centerTileHeight);
00777             center.fill(Qt::transparent);
00778 
00779             {
00780                 QPainter centerPainter(&center);
00781                 centerPainter.setCompositionMode(QPainter::CompositionMode_Source);
00782                 q->paint(&centerPainter, QRect(QPoint(0, 0), q->elementSize(prefix % "center")), prefix % "center");
00783             }
00784 
00785             if (frame->composeOverBorder) {
00786                 p.drawTiledPixmap(QRect(QPoint(0, 0), size.toSize()), center);
00787             } else {
00788                 p.drawTiledPixmap(QRect(frame->leftWidth, frame->topHeight,
00789                                         contentWidth, contentHeight), center);
00790             }
00791         }
00792     } else {
00793         if (contentHeight > 0 && contentWidth > 0) {
00794             if (frame->composeOverBorder) {
00795                 q->paint(&p, QRect(QPoint(0, 0), size.toSize()),
00796                                prefix % "center");
00797             } else {
00798                 q->paint(&p, QRect(frame->leftWidth, frame->topHeight,
00799                                 contentWidth, contentHeight),
00800                                 prefix % "center");
00801             }
00802         }
00803     }
00804 
00805     if (frame->composeOverBorder) {
00806         p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
00807         p.drawPixmap(QRect(QPoint(0, 0), size.toSize()), alphaMask());
00808         p.setCompositionMode(QPainter::CompositionMode_SourceOver);
00809     }
00810 
00811     if (frame->enabledBorders & FrameSvg::LeftBorder && q->hasElement(prefix % "left")) {
00812         rightOffset += frame->leftWidth;
00813     }
00814 
00815     // Corners
00816     if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(prefix % "top")) {
00817         contentTop = frame->topHeight;
00818         bottomOffset += frame->topHeight;
00819 
00820         if (q->hasElement(prefix % "topleft") && frame->enabledBorders & FrameSvg::LeftBorder) {
00821             q->paint(&p, QRect(leftOffset, topOffset, frame->leftWidth, frame->topHeight), prefix % "topleft");
00822 
00823             contentLeft = frame->leftWidth;
00824         }
00825 
00826         if (q->hasElement(prefix % "topright") && frame->enabledBorders & FrameSvg::RightBorder) {
00827             q->paint(&p, QRect(rightOffset, topOffset, frame->rightWidth, frame->topHeight), prefix % "topright");
00828         }
00829     }
00830 
00831     if (frame->enabledBorders & FrameSvg::BottomBorder && q->hasElement(prefix % "bottom")) {
00832         if (q->hasElement(prefix % "bottomleft") && frame->enabledBorders & FrameSvg::LeftBorder) {
00833             q->paint(&p, QRect(leftOffset, bottomOffset, frame->leftWidth, frame->bottomHeight), prefix % "bottomleft");
00834 
00835             contentLeft = frame->leftWidth;
00836         }
00837 
00838         if (frame->enabledBorders & FrameSvg::RightBorder && q->hasElement(prefix % "bottomright")) {
00839             q->paint(&p, QRect(rightOffset, bottomOffset, frame->rightWidth, frame->bottomHeight), prefix % "bottomright");
00840         }
00841     }
00842 
00843     // Sides
00844     if (frame->stretchBorders) {
00845         if (frame->enabledBorders & FrameSvg::LeftBorder || frame->enabledBorders & FrameSvg::RightBorder) {
00846             if (q->hasElement(prefix % "left") &&
00847                 frame->enabledBorders & FrameSvg::LeftBorder) {
00848                 q->paint(&p, QRect(leftOffset, contentTop, frame->leftWidth, contentHeight), prefix % "left");
00849             }
00850 
00851             if (q->hasElement(prefix % "right") &&
00852                 frame->enabledBorders & FrameSvg::RightBorder) {
00853                 q->paint(&p, QRect(rightOffset, contentTop, frame->rightWidth, contentHeight), prefix % "right");
00854             }
00855         }
00856 
00857         if (frame->enabledBorders & FrameSvg::TopBorder || frame->enabledBorders & FrameSvg::BottomBorder) {
00858             if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(prefix % "top")) {
00859                 q->paint(&p, QRect(contentLeft, topOffset, contentWidth, frame->topHeight), prefix % "top");
00860             }
00861 
00862             if (frame->enabledBorders & FrameSvg::BottomBorder && q->hasElement(prefix % "bottom")) {
00863                 q->paint(&p, QRect(contentLeft, bottomOffset, contentWidth, frame->bottomHeight), prefix % "bottom");
00864             }
00865         }
00866     } else {
00867         if (frame->enabledBorders & FrameSvg::LeftBorder && q->hasElement(prefix % "left")
00868                 && leftHeight > 0 && frame->leftWidth > 0) {
00869             QPixmap left(frame->leftWidth, leftHeight);
00870             left.fill(Qt::transparent);
00871 
00872             QPainter sidePainter(&left);
00873             sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
00874             q->paint(&sidePainter, QRect(QPoint(0, 0), left.size()), prefix % "left");
00875 
00876             p.drawTiledPixmap(QRect(leftOffset, contentTop, frame->leftWidth, contentHeight), left);
00877         }
00878 
00879         if (frame->enabledBorders & FrameSvg::RightBorder && q->hasElement(prefix % "right") &&
00880                 leftHeight > 0 && frame->rightWidth > 0) {
00881             QPixmap right(frame->rightWidth, leftHeight);
00882             right.fill(Qt::transparent);
00883 
00884             QPainter sidePainter(&right);
00885             sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
00886             q->paint(&sidePainter, QRect(QPoint(0, 0), right.size()), prefix % "right");
00887 
00888             p.drawTiledPixmap(QRect(rightOffset, contentTop, frame->rightWidth, contentHeight), right);
00889         }
00890 
00891         if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(prefix % "top")
00892                 && topWidth > 0 && frame->topHeight > 0) {
00893             QPixmap top(topWidth, frame->topHeight);
00894             top.fill(Qt::transparent);
00895 
00896             QPainter sidePainter(&top);
00897             sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
00898             q->paint(&sidePainter, QRect(QPoint(0, 0), top.size()), prefix % "top");
00899 
00900             p.drawTiledPixmap(QRect(contentLeft, topOffset, contentWidth, frame->topHeight), top);
00901         }
00902 
00903         if (frame->enabledBorders & FrameSvg::BottomBorder && q->hasElement(prefix % "bottom")
00904                 && topWidth > 0 && frame->bottomHeight > 0) {
00905             QPixmap bottom(topWidth, frame->bottomHeight);
00906             bottom.fill(Qt::transparent);
00907 
00908             QPainter sidePainter(&bottom);
00909             sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
00910             q->paint(&sidePainter, QRect(QPoint(0, 0), bottom.size()), prefix % "bottom");
00911 
00912             p.drawTiledPixmap(QRect(contentLeft, bottomOffset, contentWidth, frame->bottomHeight), bottom);
00913         }
00914     }
00915 
00916 }
00917 
00918 QString FrameSvgPrivate::cacheId(FrameData *frame, const QString &prefixToSave) const
00919 {
00920     const QSize size = frameSize(frame).toSize();
00921     const QLatin1Char s('_');
00922     return QString::number(frame->enabledBorders) % s % QString::number(size.width()) % s % QString::number(size.height()) % s % prefixToSave % s % q->imagePath(); 
00923 }
00924 
00925 void FrameSvgPrivate::cacheFrame(const QString &prefixToSave, const QPixmap &background, const QPixmap &overlay)
00926 {
00927     if (!q->isUsingRenderingCache()) {
00928         return;
00929     }
00930 
00931     //insert background
00932     FrameData *frame = frames.value(prefixToSave);
00933 
00934     if (!frame) {
00935         return;
00936     }
00937 
00938     const QString id = cacheId(frame, prefixToSave);
00939 
00940     //kDebug()<<"Saving to cache frame"<<id;
00941 
00942     Theme::defaultTheme()->insertIntoCache(id, background, QString::number((qint64)q, 16) % prefixToSave);
00943 
00944     if (!overlay.isNull()) {
00945         //insert overlay
00946         Theme::defaultTheme()->insertIntoCache("overlay_" % id, overlay, QString::number((qint64)q, 16) % prefixToSave % "overlay");
00947     }
00948 }
00949 
00950 void FrameSvgPrivate::updateSizes() const
00951 {
00952     //kDebug() << "!!!!!!!!!!!!!!!!!!!!!! updating sizes" << prefix;
00953     FrameData *frame = frames[prefix];
00954     Q_ASSERT(frame);
00955 
00956     QSize s = q->size();
00957     q->resize();
00958     frame->cachedBackground = QPixmap();
00959 
00960     if (frame->enabledBorders & FrameSvg::TopBorder) {
00961         frame->topHeight = q->elementSize(prefix % "top").height();
00962 
00963         if (q->hasElement(prefix % "hint-top-margin")) {
00964             frame->topMargin = q->elementSize(prefix % "hint-top-margin").height();
00965         } else {
00966             frame->topMargin = frame->topHeight;
00967         }
00968     } else {
00969         frame->topMargin = frame->topHeight = 0;
00970     }
00971 
00972     if (frame->enabledBorders & FrameSvg::LeftBorder) {
00973         frame->leftWidth = q->elementSize(prefix % "left").width();
00974 
00975         if (q->hasElement(prefix % "hint-left-margin")) {
00976             frame->leftMargin = q->elementSize(prefix % "hint-left-margin").width();
00977         } else {
00978             frame->leftMargin = frame->leftWidth;
00979         }
00980     } else {
00981         frame->leftMargin = frame->leftWidth = 0;
00982     }
00983 
00984     if (frame->enabledBorders & FrameSvg::RightBorder) {
00985         frame->rightWidth = q->elementSize(prefix % "right").width();
00986 
00987         if (q->hasElement(prefix % "hint-right-margin")) {
00988             frame->rightMargin = q->elementSize(prefix % "hint-right-margin").width();
00989         } else {
00990             frame->rightMargin = frame->rightWidth;
00991         }
00992     } else {
00993         frame->rightMargin = frame->rightWidth = 0;
00994     }
00995 
00996     if (frame->enabledBorders & FrameSvg::BottomBorder) {
00997         frame->bottomHeight = q->elementSize(prefix % "bottom").height();
00998 
00999         if (q->hasElement(prefix % "hint-bottom-margin")) {
01000             frame->bottomMargin = q->elementSize(prefix % "hint-bottom-margin").height();
01001         } else {
01002             frame->bottomMargin = frame->bottomHeight;
01003         }
01004     } else {
01005         frame->bottomMargin = frame->bottomHeight = 0;
01006     }
01007 
01008     frame->composeOverBorder = (q->hasElement(prefix % "hint-compose-over-border") &&
01009                                 q->hasElement(prefix % "mask-center"));
01010 
01011     //since it's rectangular, topWidth and bottomWidth must be the same
01012     //the ones that don't have a prefix is for retrocompatibility
01013     frame->tileCenter = q->hasElement("hint-tile-center");
01014     frame->noBorderPadding = q->hasElement("hint-no-border-padding");
01015     frame->stretchBorders = q->hasElement("hint-stretch-borders");
01016     q->resize(s);
01017 }
01018 
01019 void FrameSvgPrivate::updateNeeded()
01020 {
01021     q->clearCache();
01022     updateSizes();
01023 }
01024 
01025 void FrameSvgPrivate::updateAndSignalSizes()
01026 {
01027     updateSizes();
01028     emit q->repaintNeeded();
01029 }
01030 
01031 QSizeF FrameSvgPrivate::frameSize(FrameData *frame) const
01032 {
01033     if (!frame->frameSize.isValid()) {
01034         updateSizes();
01035         frame->frameSize = q->size();
01036     }
01037 
01038     return frame->frameSize;
01039 }
01040 
01041 void FrameData::ref(FrameSvg *svg)
01042 {
01043     references[svg] = references[svg] + 1;
01044     //kDebug() << this << svg << references[svg];
01045 }
01046 
01047 bool FrameData::deref(FrameSvg *svg)
01048 {
01049     references[svg] = references[svg] - 1;
01050     //kDebug() << this << svg << references[svg];
01051     if (references[svg] < 1) {
01052         references.remove(svg);
01053     }
01054 
01055     return references.isEmpty();
01056 }
01057 
01058 bool FrameData::removeRefs(FrameSvg *svg)
01059 {
01060     references.remove(svg);
01061     return references.isEmpty();
01062 }
01063 
01064 bool FrameData::isUsed() const
01065 {
01066     return !references.isEmpty();
01067 }
01068 
01069 int FrameData::refcount() const
01070 {
01071     return references.count();
01072 }
01073 
01074 } // Plasma namespace
01075 
01076 #include "framesvg.moc"

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
  • 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.3
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