/* * Copyright 2008 by Aaron Seigo * Copyright 2008 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "framesvg.h" #include "private/framesvg_p.h" #include #include #include #include #include #include #include #include #include #include #include namespace Plasma { QHash FrameSvgPrivate::s_sharedFrames; // Any attempt to generate a frame whose width or height is larger than this // will be rejected static const int MAX_FRAME_SIZE = 100000; FrameSvg::FrameSvg(QObject *parent) : Svg(parent), d(new FrameSvgPrivate(this)) { connect(this, SIGNAL(repaintNeeded()), this, SLOT(updateNeeded())); d->frames.insert(QString(), new FrameData(this)); } FrameSvg::~FrameSvg() { delete d; } void FrameSvg::setImagePath(const QString &path) { if (path == imagePath()) { return; } Svg::setImagePath(path); setContainsMultipleImages(true); clearCache(); d->updateAndSignalSizes(); } void FrameSvg::setEnabledBorders(const EnabledBorders borders) { if (borders == d->frames[d->prefix]->enabledBorders) { return; } FrameData *fd = d->frames[d->prefix]; const QString oldKey = d->cacheId(fd, d->prefix); const EnabledBorders oldBorders = fd->enabledBorders; fd->enabledBorders = borders; const QString newKey = d->cacheId(fd, d->prefix); fd->enabledBorders = oldBorders; //kDebug() << "looking for" << newKey; FrameData *newFd = FrameSvgPrivate::s_sharedFrames.value(newKey); if (newFd) { //kDebug() << "FOUND IT!" << newFd->refcount; // we've found a math, so insert that new one and ref it .. newFd->ref(this); d->frames.insert(d->prefix, newFd); //.. then deref the old one and if it's no longer used, get rid of it if (fd->deref(this)) { //const QString oldKey = d->cacheId(fd, d->prefix); //kDebug() << "1. Removing it" << oldKey << fd->refcount; FrameSvgPrivate::s_sharedFrames.remove(oldKey); delete fd; } return; } if (fd->refcount() == 1) { // we're the only user of it, let's remove it from the shared keys // we don't want to deref it, however, as we'll still be using it FrameSvgPrivate::s_sharedFrames.remove(oldKey); } else { // others are using it, but we wish to change its size. so deref it, // then create a copy of it (we're automatically ref'd via the ctor), // then insert it into our frames. fd->deref(this); fd = new FrameData(*fd, this); d->frames.insert(d->prefix, fd); } fd->enabledBorders = borders; d->updateAndSignalSizes(); } FrameSvg::EnabledBorders FrameSvg::enabledBorders() const { if (d->frames.isEmpty()) { return NoBorder; } QHash::const_iterator it = d->frames.constFind(d->prefix); if (it != d->frames.constEnd()) { return it.value()->enabledBorders; } else { return NoBorder; } } void FrameSvg::setElementPrefix(Plasma::Location location) { switch (location) { case TopEdge: setElementPrefix("north"); break; case BottomEdge: setElementPrefix("south"); break; case LeftEdge: setElementPrefix("west"); break; case RightEdge: setElementPrefix("east"); break; default: setElementPrefix(QString()); break; } d->location = location; } void FrameSvg::setElementPrefix(const QString &prefix) { const QString oldPrefix(d->prefix); if (!hasElement(prefix % "-center")) { d->prefix.clear(); } else { d->prefix = prefix; if (!d->prefix.isEmpty()) { d->prefix += '-'; } } FrameData *oldFrameData = d->frames.value(oldPrefix); if (oldPrefix == d->prefix && oldFrameData) { return; } if (!d->frames.contains(d->prefix)) { if (oldFrameData) { FrameData *newFd = 0; if (!oldFrameData->frameSize.isEmpty()) { const QString key = d->cacheId(oldFrameData, d->prefix); newFd = FrameSvgPrivate::s_sharedFrames.value(key); } // we need to put this in the cache if we didn't find it in the shared frames // and we have a size; if we don't have a size, we'll catch it later const bool cache = !newFd && !oldFrameData->frameSize.isEmpty(); if (newFd) { newFd->ref(this); } else { newFd = new FrameData(*oldFrameData, this); } d->frames.insert(d->prefix, newFd); if (cache) { // we have to cache after inserting the frame since the cacheId requires the // frame to be in the frames collection already const QString key = d->cacheId(oldFrameData, d->prefix); //kDebug() << this << " 1. inserting as" << key; FrameSvgPrivate::s_sharedFrames.insert(key, newFd); } } else { // couldn't find anything useful, so we just create something here // we don't have a size for it yet, so don't bother trying to share it just yet FrameData *newFd = new FrameData(this); d->frames.insert(d->prefix, newFd); } d->updateSizes(); } if (!d->cacheAll) { d->frames.remove(oldPrefix); if (oldFrameData) { if (oldFrameData->deref(this)) { const QString oldKey = d->cacheId(oldFrameData, oldPrefix); FrameSvgPrivate::s_sharedFrames.remove(oldKey); delete oldFrameData; } } } d->location = Floating; } bool FrameSvg::hasElementPrefix(const QString & prefix) const { //for now it simply checks if a center element exists, //because it could make sense for certain themes to not have all the elements if (prefix.isEmpty()) { return hasElement("center"); } else { return hasElement(prefix % "-center"); } } bool FrameSvg::hasElementPrefix(Plasma::Location location) const { switch (location) { case TopEdge: return hasElementPrefix("north"); break; case BottomEdge: return hasElementPrefix("south"); break; case LeftEdge: return hasElementPrefix("west"); break; case RightEdge: return hasElementPrefix("east"); break; default: return hasElementPrefix(QString()); break; } } QString FrameSvg::prefix() { if (d->prefix.isEmpty()) { return d->prefix; } return d->prefix.left(d->prefix.size() - 1); } void FrameSvg::resizeFrame(const QSizeF &size) { if (imagePath().isEmpty()) { return; } if (size.isEmpty()) { kWarning() << "Invalid size" << size; return; } FrameData *fd = d->frames[d->prefix]; if (size == fd->frameSize) { return; } const QString oldKey = d->cacheId(fd, d->prefix); const QSize currentSize = fd->frameSize; fd->frameSize = size.toSize(); const QString newKey = d->cacheId(fd, d->prefix); fd->frameSize = currentSize; //kDebug() << "looking for" << newKey; FrameData *newFd = FrameSvgPrivate::s_sharedFrames.value(newKey); if (newFd) { //kDebug() << "FOUND IT!" << newFd->refcount; // we've found a math, so insert that new one and ref it .. newFd->ref(this); d->frames.insert(d->prefix, newFd); //.. then deref the old one and if it's no longer used, get rid of it if (fd->deref(this)) { //const QString oldKey = d->cacheId(fd, d->prefix); //kDebug() << "1. Removing it" << oldKey << fd->refcount; FrameSvgPrivate::s_sharedFrames.remove(oldKey); delete fd; } return; } if (fd->refcount() == 1) { // we're the only user of it, let's remove it from the shared keys // we don't want to deref it, however, as we'll still be using it FrameSvgPrivate::s_sharedFrames.remove(oldKey); } else { // others are using it, but we wish to change its size. so deref it, // then create a copy of it (we're automatically ref'd via the ctor), // then insert it into our frames. fd->deref(this); fd = new FrameData(*fd, this); d->frames.insert(d->prefix, fd); } d->updateSizes(); fd->frameSize = size.toSize(); // we know it isn't in s_sharedFrames do the check above, so insert it now FrameSvgPrivate::s_sharedFrames.insert(newKey, fd); } QSizeF FrameSvg::frameSize() const { QHash::const_iterator it = d->frames.constFind(d->prefix); if (it == d->frames.constEnd()) { return QSize(-1, -1); } else { return d->frameSize(it.value()); } } qreal FrameSvg::marginSize(const Plasma::MarginEdge edge) const { if (d->frames[d->prefix]->noBorderPadding) { return .0; } switch (edge) { case Plasma::TopMargin: return d->frames[d->prefix]->topMargin; break; case Plasma::LeftMargin: return d->frames[d->prefix]->leftMargin; break; case Plasma::RightMargin: return d->frames[d->prefix]->rightMargin; break; //Plasma::BottomMargin default: return d->frames[d->prefix]->bottomMargin; break; } } void FrameSvg::getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const { FrameData *frame = d->frames[d->prefix]; if (frame->noBorderPadding) { left = top = right = bottom = 0; return; } top = frame->topMargin; left = frame->leftMargin; right = frame->rightMargin; bottom = frame->bottomMargin; } QRectF FrameSvg::contentsRect() const { QSizeF size(frameSize()); if (size.isValid()) { QRectF rect(QPointF(0, 0), size); FrameData *frame = d->frames[d->prefix]; return rect.adjusted(frame->leftMargin, frame->topMargin, -frame->rightMargin, -frame->bottomMargin); } else { return QRectF(); } } QPixmap FrameSvg::alphaMask() const { //FIXME: the distinction between overlay and return d->alphaMask(); } QRegion FrameSvg::mask() const { FrameData *frame = d->frames[d->prefix]; QString id = d->cacheId(frame, QString()); if (!frame->cachedMasks.contains(id)) { //TODO: Implement a better way to cap the number of cached masks if (frame->cachedMasks.count() > frame->MAX_CACHED_MASKS) { frame->cachedMasks.clear(); } frame->cachedMasks.insert(id, QRegion(QBitmap(d->alphaMask().alphaChannel().createMaskFromColor(Qt::black)))); } return frame->cachedMasks[id]; } void FrameSvg::setCacheAllRenderedFrames(bool cache) { if (d->cacheAll && !cache) { clearCache(); } d->cacheAll = cache; } bool FrameSvg::cacheAllRenderedFrames() const { return d->cacheAll; } void FrameSvg::clearCache() { FrameData *frame = d->frames[d->prefix]; // delete all the frames that aren't this one QMutableHashIterator it(d->frames); while (it.hasNext()) { FrameData *p = it.next().value(); if (frame != p) { //TODO: should we clear from the Theme pixmap cache as well? if (p->deref(this)) { const QString key = d->cacheId(p, it.key()); FrameSvgPrivate::s_sharedFrames.remove(key); delete p; } it.remove(); } } } QPixmap FrameSvg::framePixmap() { FrameData *frame = d->frames[d->prefix]; if (frame->cachedBackground.isNull()) { d->generateBackground(frame); if (frame->cachedBackground.isNull()) { return QPixmap(); } } return frame->cachedBackground; } void FrameSvg::paintFrame(QPainter *painter, const QRectF &target, const QRectF &source) { FrameData *frame = d->frames[d->prefix]; if (frame->cachedBackground.isNull()) { d->generateBackground(frame); if (frame->cachedBackground.isNull()) { return; } } painter->drawPixmap(target, frame->cachedBackground, source.isValid() ? source : target); } void FrameSvg::paintFrame(QPainter *painter, const QPointF &pos) { FrameData *frame = d->frames[d->prefix]; if (frame->cachedBackground.isNull()) { d->generateBackground(frame); if (frame->cachedBackground.isNull()) { return; } } painter->drawPixmap(pos, frame->cachedBackground); } //#define DEBUG_FRAMESVG_CACHE FrameSvgPrivate::~FrameSvgPrivate() { #ifdef DEBUG_FRAMESVG_CACHE kDebug() << "*************" << q << q->imagePath() << "****************"; #endif QHashIterator it(frames); while (it.hasNext()) { it.next(); if (it.value()) { // we remove all references from this widget to the frame, and delete it if we're the // last user if (it.value()->removeRefs(q)) { const QString key = cacheId(it.value(), it.key()); #ifdef DEBUG_FRAMESVG_CACHE kDebug() << "2. Removing it" << key << it.value() << it.value()->refcount() << s_sharedFrames.contains(key); #endif s_sharedFrames.remove(key); delete it.value(); } #ifdef DEBUG_FRAMESVG_CACHE else { kDebug() << "still shared:" << cacheId(it.value(), it.key()) << it.value() << it.value()->refcount() << it.value()->isUsed(); } } else { kDebug() << "lost our value for" << it.key(); #endif } } #ifdef DEBUG_FRAMESVG_CACHE QHashIterator it2(s_sharedFrames); int shares = 0; while (it2.hasNext()) { it2.next(); const int rc = it2.value()->refcount(); if (rc == 0) { kDebug() << " LOST!" << it2.key() << rc << it2.value();// << it2.value()->references; } else { kDebug() << " " << it2.key() << rc << it2.value(); foreach (FrameSvg *data, it2.value()->references.keys()) { kDebug( )<< " " << (void*)data << it2.value()->references[data]; } shares += rc - 1; } } kDebug() << "#####################################" << s_sharedFrames.count() << ", pixmaps saved:" << shares; #endif frames.clear(); } QPixmap FrameSvgPrivate::alphaMask() { FrameData *frame = frames[prefix]; QString maskPrefix; if (q->hasElement("mask-" % prefix % "center")) { maskPrefix = "mask-"; } if (maskPrefix.isNull()) { if (frame->cachedBackground.isNull()) { generateBackground(frame); if (frame->cachedBackground.isNull()) { return QPixmap(); } } return frame->cachedBackground; } else { QString oldPrefix = prefix; // We are setting the prefix only temporary to generate // the needed mask image prefix = maskPrefix % oldPrefix; if (!frames.contains(prefix)) { const QString key = cacheId(frame, prefix); // see if we can find a suitable candidate in the shared frames // if successful, ref and insert, otherwise create a new one // and insert that into both the shared frames and our frames. FrameData *maskFrame = s_sharedFrames.value(key); if (maskFrame) { maskFrame->ref(q); } else { maskFrame = new FrameData(*frame, q); s_sharedFrames.insert(key, maskFrame); } frames.insert(prefix, maskFrame); updateSizes(); } FrameData *maskFrame = frames[prefix]; if (maskFrame->cachedBackground.isNull() || maskFrame->frameSize != frameSize(frame)) { const QString oldKey = cacheId(maskFrame, prefix); maskFrame->frameSize = frameSize(frame).toSize(); const QString newKey = cacheId(maskFrame, prefix); if (s_sharedFrames.contains(oldKey)) { s_sharedFrames.remove(oldKey); s_sharedFrames.insert(newKey, maskFrame); } maskFrame->cachedBackground = QPixmap(); generateBackground(maskFrame); if (maskFrame->cachedBackground.isNull()) { return QPixmap(); } } prefix = oldPrefix; return maskFrame->cachedBackground; } } void FrameSvgPrivate::generateBackground(FrameData *frame) { if (!frame->cachedBackground.isNull()) { return; } const QString id = cacheId(frame, prefix); Theme *theme = Theme::defaultTheme(); bool frameCached = !frame->cachedBackground.isNull(); bool overlayCached = false; const bool overlayAvailable = !prefix.startsWith("mask-") && q->hasElement(prefix % "overlay"); QPixmap overlay; if (q->isUsingRenderingCache()) { frameCached = theme->findInCache(id, frame->cachedBackground) && !frame->cachedBackground.isNull(); if (overlayAvailable) { overlayCached = theme->findInCache("overlay_" % id, overlay) && !overlay.isNull(); } } if (!frameCached) { generateFrameBackground(frame); } //Overlays QSize overlaySize; QPoint actualOverlayPos = QPoint(0, 0); if (overlayAvailable && !overlayCached) { QPoint pos = QPoint(0, 0); overlaySize = q->elementSize(prefix % "overlay"); //Random pos, stretched and tiled are mutually exclusive if (q->hasElement(prefix % "hint-overlay-random-pos")) { actualOverlayPos = overlayPos; } else if (q->hasElement(prefix % "hint-overlay-pos-right")) { actualOverlayPos.setX(frame->frameSize.width() - overlaySize.width()); } else if (q->hasElement(prefix % "hint-overlay-pos-bottom")) { actualOverlayPos.setY(frame->frameSize.height() - overlaySize.height()); //Stretched or Tiled? } else if (q->hasElement(prefix % "hint-overlay-stretch")) { overlaySize = frameSize(frame).toSize(); } else { if (q->hasElement(prefix % "hint-overlay-tile-horizontal")) { overlaySize.setWidth(frameSize(frame).width()); } if (q->hasElement(prefix % "hint-overlay-tile-vertical")) { overlaySize.setHeight(frameSize(frame).height()); } } overlay = alphaMask(); QPainter overlayPainter(&overlay); overlayPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); //Tiling? if (q->hasElement(prefix % "hint-overlay-tile-horizontal") || q->hasElement(prefix % "hint-overlay-tile-vertical")) { QSize s = q->size(); q->resize(q->elementSize(prefix % "overlay")); overlayPainter.drawTiledPixmap(QRect(QPoint(0,0), overlaySize), q->pixmap(prefix % "overlay")); q->resize(s); } else { q->paint(&overlayPainter, QRect(actualOverlayPos, overlaySize), prefix % "overlay"); } overlayPainter.end(); } if (!frameCached) { cacheFrame(prefix, frame->cachedBackground, overlayCached ? overlay : QPixmap()); } if (!overlay.isNull()) { QPainter p(&frame->cachedBackground); p.setCompositionMode(QPainter::CompositionMode_SourceOver); p.drawPixmap(actualOverlayPos, overlay, QRect(actualOverlayPos, overlaySize)); } } void FrameSvgPrivate::generateFrameBackground(FrameData *frame) { //kDebug() << "generating background"; const QSizeF size = frameSize(frame); const int topWidth = q->elementSize(prefix % "top").width(); const int leftHeight = q->elementSize(prefix % "left").height(); const int topOffset = 0; const int leftOffset = 0; if (!size.isValid()) { kWarning() << "Invalid frame size" << size; return; } if (size.width() >= MAX_FRAME_SIZE || size.height() >= MAX_FRAME_SIZE) { kWarning() << "Not generating frame background for a size whose width or height is more than" << MAX_FRAME_SIZE << size; return; } const int contentWidth = size.width() - frame->leftWidth - frame->rightWidth; const int contentHeight = size.height() - frame->topHeight - frame->bottomHeight; int contentTop = 0; int contentLeft = 0; int rightOffset = contentWidth; int bottomOffset = contentHeight; frame->cachedBackground = QPixmap(frame->leftWidth + contentWidth + frame->rightWidth, frame->topHeight + contentHeight + frame->bottomHeight); frame->cachedBackground.fill(Qt::transparent); QPainter p(&frame->cachedBackground); p.setCompositionMode(QPainter::CompositionMode_Source); p.setRenderHint(QPainter::SmoothPixmapTransform); //CENTER if (frame->tileCenter) { if (contentHeight > 0 && contentWidth > 0) { const int centerTileHeight = q->elementSize(prefix % "center").height(); const int centerTileWidth = q->elementSize(prefix % "center").width(); QPixmap center(centerTileWidth, centerTileHeight); center.fill(Qt::transparent); { QPainter centerPainter(¢er); centerPainter.setCompositionMode(QPainter::CompositionMode_Source); q->paint(¢erPainter, QRect(QPoint(0, 0), q->elementSize(prefix % "center")), prefix % "center"); } if (frame->composeOverBorder) { p.drawTiledPixmap(QRect(QPoint(0, 0), size.toSize()), center); } else { p.drawTiledPixmap(QRect(frame->leftWidth, frame->topHeight, contentWidth, contentHeight), center); } } } else { if (contentHeight > 0 && contentWidth > 0) { if (frame->composeOverBorder) { q->paint(&p, QRect(QPoint(0, 0), size.toSize()), prefix % "center"); } else { q->paint(&p, QRect(frame->leftWidth, frame->topHeight, contentWidth, contentHeight), prefix % "center"); } } } if (frame->composeOverBorder) { p.setCompositionMode(QPainter::CompositionMode_DestinationIn); p.drawPixmap(QRect(QPoint(0, 0), size.toSize()), alphaMask()); p.setCompositionMode(QPainter::CompositionMode_SourceOver); } if (frame->enabledBorders & FrameSvg::LeftBorder && q->hasElement(prefix % "left")) { rightOffset += frame->leftWidth; } // Corners if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(prefix % "top")) { contentTop = frame->topHeight; bottomOffset += frame->topHeight; if (q->hasElement(prefix % "topleft") && frame->enabledBorders & FrameSvg::LeftBorder) { q->paint(&p, QRect(leftOffset, topOffset, frame->leftWidth, frame->topHeight), prefix % "topleft"); contentLeft = frame->leftWidth; } if (q->hasElement(prefix % "topright") && frame->enabledBorders & FrameSvg::RightBorder) { q->paint(&p, QRect(rightOffset, topOffset, frame->rightWidth, frame->topHeight), prefix % "topright"); } } if (frame->enabledBorders & FrameSvg::BottomBorder && q->hasElement(prefix % "bottom")) { if (q->hasElement(prefix % "bottomleft") && frame->enabledBorders & FrameSvg::LeftBorder) { q->paint(&p, QRect(leftOffset, bottomOffset, frame->leftWidth, frame->bottomHeight), prefix % "bottomleft"); contentLeft = frame->leftWidth; } if (frame->enabledBorders & FrameSvg::RightBorder && q->hasElement(prefix % "bottomright")) { q->paint(&p, QRect(rightOffset, bottomOffset, frame->rightWidth, frame->bottomHeight), prefix % "bottomright"); } } // Sides if (frame->stretchBorders) { if (frame->enabledBorders & FrameSvg::LeftBorder || frame->enabledBorders & FrameSvg::RightBorder) { if (q->hasElement(prefix % "left") && frame->enabledBorders & FrameSvg::LeftBorder) { q->paint(&p, QRect(leftOffset, contentTop, frame->leftWidth, contentHeight), prefix % "left"); } if (q->hasElement(prefix % "right") && frame->enabledBorders & FrameSvg::RightBorder) { q->paint(&p, QRect(rightOffset, contentTop, frame->rightWidth, contentHeight), prefix % "right"); } } if (frame->enabledBorders & FrameSvg::TopBorder || frame->enabledBorders & FrameSvg::BottomBorder) { if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(prefix % "top")) { q->paint(&p, QRect(contentLeft, topOffset, contentWidth, frame->topHeight), prefix % "top"); } if (frame->enabledBorders & FrameSvg::BottomBorder && q->hasElement(prefix % "bottom")) { q->paint(&p, QRect(contentLeft, bottomOffset, contentWidth, frame->bottomHeight), prefix % "bottom"); } } } else { if (frame->enabledBorders & FrameSvg::LeftBorder && q->hasElement(prefix % "left") && leftHeight > 0 && frame->leftWidth > 0) { QPixmap left(frame->leftWidth, leftHeight); left.fill(Qt::transparent); QPainter sidePainter(&left); sidePainter.setCompositionMode(QPainter::CompositionMode_Source); q->paint(&sidePainter, QRect(QPoint(0, 0), left.size()), prefix % "left"); p.drawTiledPixmap(QRect(leftOffset, contentTop, frame->leftWidth, contentHeight), left); } if (frame->enabledBorders & FrameSvg::RightBorder && q->hasElement(prefix % "right") && leftHeight > 0 && frame->rightWidth > 0) { QPixmap right(frame->rightWidth, leftHeight); right.fill(Qt::transparent); QPainter sidePainter(&right); sidePainter.setCompositionMode(QPainter::CompositionMode_Source); q->paint(&sidePainter, QRect(QPoint(0, 0), right.size()), prefix % "right"); p.drawTiledPixmap(QRect(rightOffset, contentTop, frame->rightWidth, contentHeight), right); } if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(prefix % "top") && topWidth > 0 && frame->topHeight > 0) { QPixmap top(topWidth, frame->topHeight); top.fill(Qt::transparent); QPainter sidePainter(&top); sidePainter.setCompositionMode(QPainter::CompositionMode_Source); q->paint(&sidePainter, QRect(QPoint(0, 0), top.size()), prefix % "top"); p.drawTiledPixmap(QRect(contentLeft, topOffset, contentWidth, frame->topHeight), top); } if (frame->enabledBorders & FrameSvg::BottomBorder && q->hasElement(prefix % "bottom") && topWidth > 0 && frame->bottomHeight > 0) { QPixmap bottom(topWidth, frame->bottomHeight); bottom.fill(Qt::transparent); QPainter sidePainter(&bottom); sidePainter.setCompositionMode(QPainter::CompositionMode_Source); q->paint(&sidePainter, QRect(QPoint(0, 0), bottom.size()), prefix % "bottom"); p.drawTiledPixmap(QRect(contentLeft, bottomOffset, contentWidth, frame->bottomHeight), bottom); } } } QString FrameSvgPrivate::cacheId(FrameData *frame, const QString &prefixToSave) const { const QSize size = frameSize(frame).toSize(); const QChar s('_'); return QString::number(frame->enabledBorders) % s % QString::number(size.width()) % s % QString::number(size.height()) % s % prefixToSave % s % q->imagePath(); } void FrameSvgPrivate::cacheFrame(const QString &prefixToSave, const QPixmap &background, const QPixmap &overlay) { if (!q->isUsingRenderingCache()) { return; } //insert background FrameData *frame = frames.value(prefixToSave); if (!frame) { return; } const QString id = cacheId(frame, prefixToSave); //kDebug()<<"Saving to cache frame"<insertIntoCache(id, background, QString::number((qint64)q, 16) % prefixToSave); if (!overlay.isNull()) { //insert overlay Theme::defaultTheme()->insertIntoCache("overlay_" % id, overlay, QString::number((qint64)q, 16) % prefixToSave % "overlay"); } } void FrameSvgPrivate::updateSizes() const { //kDebug() << "!!!!!!!!!!!!!!!!!!!!!! updating sizes" << prefix; FrameData *frame = frames[prefix]; Q_ASSERT(frame); QSize s = q->size(); q->resize(); frame->cachedBackground = QPixmap(); if (frame->enabledBorders & FrameSvg::TopBorder) { frame->topHeight = q->elementSize(prefix % "top").height(); if (q->hasElement(prefix % "hint-top-margin")) { frame->topMargin = q->elementSize(prefix % "hint-top-margin").height(); } else { frame->topMargin = frame->topHeight; } } else { frame->topMargin = frame->topHeight = 0; } if (frame->enabledBorders & FrameSvg::LeftBorder) { frame->leftWidth = q->elementSize(prefix % "left").width(); if (q->hasElement(prefix % "hint-left-margin")) { frame->leftMargin = q->elementSize(prefix % "hint-left-margin").width(); } else { frame->leftMargin = frame->leftWidth; } } else { frame->leftMargin = frame->leftWidth = 0; } if (frame->enabledBorders & FrameSvg::RightBorder) { frame->rightWidth = q->elementSize(prefix % "right").width(); if (q->hasElement(prefix % "hint-right-margin")) { frame->rightMargin = q->elementSize(prefix % "hint-right-margin").width(); } else { frame->rightMargin = frame->rightWidth; } } else { frame->rightMargin = frame->rightWidth = 0; } if (frame->enabledBorders & FrameSvg::BottomBorder) { frame->bottomHeight = q->elementSize(prefix % "bottom").height(); if (q->hasElement(prefix % "hint-bottom-margin")) { frame->bottomMargin = q->elementSize(prefix % "hint-bottom-margin").height(); } else { frame->bottomMargin = frame->bottomHeight; } } else { frame->bottomMargin = frame->bottomHeight = 0; } frame->composeOverBorder = (q->hasElement(prefix % "hint-compose-over-border") && q->hasElement(prefix % "mask-center")); //since it's rectangular, topWidth and bottomWidth must be the same //the ones that don't have a prefix is for retrocompatibility frame->tileCenter = q->hasElement("hint-tile-center"); frame->noBorderPadding = q->hasElement("hint-no-border-padding"); frame->stretchBorders = q->hasElement("hint-stretch-borders"); q->resize(s); } void FrameSvgPrivate::updateNeeded() { q->clearCache(); updateSizes(); } void FrameSvgPrivate::updateAndSignalSizes() { updateSizes(); emit q->repaintNeeded(); } QSizeF FrameSvgPrivate::frameSize(FrameData *frame) const { if (!frame->frameSize.isValid()) { updateSizes(); frame->frameSize = q->size(); } return frame->frameSize; } void FrameData::ref(FrameSvg *svg) { references[svg] = references[svg] + 1; //kDebug() << this << svg << references[svg]; } bool FrameData::deref(FrameSvg *svg) { references[svg] = references[svg] - 1; //kDebug() << this << svg << references[svg]; if (references[svg] < 1) { references.remove(svg); } return references.isEmpty(); } bool FrameData::removeRefs(FrameSvg *svg) { references.remove(svg); return references.isEmpty(); } bool FrameData::isUsed() const { return !references.isEmpty(); } int FrameData::refcount() const { return references.count(); } } // Plasma namespace #include "framesvg.moc"