plasma-framework/framesvg.cpp
Marco Martin 86a15c25a0 consider the enabled borders in the cacheid.
detach from the shared pool if necessary when the enabled borders change, just as with the size.
fixes soe glitches in dialogs and the notifications animation

svn path=/trunk/KDE/kdelibs/; revision=1184283
2010-10-09 18:07:17 +00:00

1028 lines
32 KiB
C++

/*
* Copyright 2008 by Aaron Seigo <aseigo@kde.org>
* Copyright 2008 Marco Martin <notmart@gmail.com>
*
* 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 <QAtomicInt>
#include <QBitmap>
#include <QCryptographicHash>
#include <QPainter>
#include <QRegion>
#include <QSize>
#include <QStringBuilder>
#include <QTimer>
#include <kdebug.h>
#include <plasma/theme.h>
#include <plasma/applet.h>
namespace Plasma
{
QHash<QString, FrameData *> 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<QString, FrameData*>::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 (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<QString, FrameData*>::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<QString, FrameData*> 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<QString, FrameData *> 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<QString, FrameData *> 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(&center);
centerPainter.setCompositionMode(QPainter::CompositionMode_Source);
q->paint(&centerPainter, 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"<<id;
Theme::defaultTheme()->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"