File indexing completed on 2024-05-12 05:46:44
0001 /* 0002 * Copyright 2008-2010 by Aaron Seigo <aseigo@kde.org> 0003 * Copyright 2008-2010 Marco Martin <notmart@gmail.com> 0004 * 0005 * This program is free software; you can redistribute it and/or modify 0006 * it under the terms of the GNU Library General Public License as 0007 * published by the Free Software Foundation; either version 2, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details 0014 * 0015 * You should have received a copy of the GNU Library General Public 0016 * License along with this program; if not, write to the 0017 * Free Software Foundation, Inc., 0018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0019 */ 0020 0021 #include "framesvg.h" 0022 #include "private/framesvg_p.h" 0023 0024 #include <QAtomicInt> 0025 #include <QBitmap> 0026 #include <QCryptographicHash> 0027 #include <QPainter> 0028 #include <QRegion> 0029 #include <QSize> 0030 #include <QStringBuilder> 0031 #include <QTimer> 0032 0033 #include <QDebug> 0034 0035 #include "theme.h" 0036 #include "private/svg_p.h" 0037 #include "private/framesvg_helpers.h" 0038 #include "debug_p.h" 0039 0040 namespace Plasma 0041 { 0042 0043 QHash<ThemePrivate *, QHash<QString, QWeakPointer<FrameData>> > FrameSvgPrivate::s_sharedFrames; 0044 0045 // Any attempt to generate a frame whose width or height is larger than this 0046 // will be rejected 0047 static const int MAX_FRAME_SIZE = 100000; 0048 0049 FrameData::~FrameData() 0050 { 0051 FrameSvgPrivate::s_sharedFrames[theme].remove(cacheId); 0052 } 0053 0054 FrameSvg::FrameSvg(QObject *parent) 0055 : Svg(parent), 0056 d(new FrameSvgPrivate(this)) 0057 { 0058 connect(this, &FrameSvg::repaintNeeded, this, std::bind(&FrameSvgPrivate::updateNeeded, d)); 0059 } 0060 0061 FrameSvg::~FrameSvg() 0062 { 0063 delete d; 0064 } 0065 0066 void FrameSvg::setImagePath(const QString &path) 0067 { 0068 if (path == imagePath()) { 0069 return; 0070 } 0071 0072 clearCache(); 0073 0074 setContainsMultipleImages(true); 0075 Svg::d->setImagePath(path); 0076 if (!d->repaintBlocked) { 0077 d->updateFrameData(); 0078 } 0079 } 0080 0081 void FrameSvg::setEnabledBorders(const EnabledBorders borders) 0082 { 0083 if (borders == d->enabledBorders) { 0084 return; 0085 } 0086 0087 d->enabledBorders = borders; 0088 0089 if (!d->repaintBlocked) { 0090 d->updateFrameData(); 0091 } 0092 } 0093 0094 FrameSvg::EnabledBorders FrameSvg::enabledBorders() const 0095 { 0096 return d->enabledBorders; 0097 } 0098 0099 void FrameSvg::setElementPrefix(Plasma::Types::Location location) 0100 { 0101 switch (location) { 0102 case Types::TopEdge: 0103 setElementPrefix(QStringLiteral("north")); 0104 break; 0105 case Types::BottomEdge: 0106 setElementPrefix(QStringLiteral("south")); 0107 break; 0108 case Types::LeftEdge: 0109 setElementPrefix(QStringLiteral("west")); 0110 break; 0111 case Types::RightEdge: 0112 setElementPrefix(QStringLiteral("east")); 0113 break; 0114 default: 0115 setElementPrefix(QString()); 0116 break; 0117 } 0118 0119 d->location = location; 0120 } 0121 0122 void FrameSvg::setElementPrefix(const QString &prefix) 0123 { 0124 if (!hasElement(prefix % QLatin1String("-center"))) { 0125 d->prefix.clear(); 0126 } else { 0127 d->prefix = prefix; 0128 if (!d->prefix.isEmpty()) { 0129 d->prefix += QLatin1Char('-'); 0130 } 0131 } 0132 d->requestedPrefix = prefix; 0133 0134 d->location = Types::Floating; 0135 0136 if (!d->repaintBlocked) { 0137 d->updateFrameData(); 0138 } 0139 } 0140 0141 bool FrameSvg::hasElementPrefix(const QString &prefix) const 0142 { 0143 //for now it simply checks if a center element exists, 0144 //because it could make sense for certain themes to not have all the elements 0145 if (prefix.isEmpty()) { 0146 return hasElement(QStringLiteral("center")); 0147 } 0148 if (prefix.endsWith(QLatin1Char('-'))) { 0149 return hasElement(prefix % QLatin1String("center")); 0150 } 0151 0152 return hasElement(prefix % QLatin1String("-center")); 0153 } 0154 0155 bool FrameSvg::hasElementPrefix(Plasma::Types::Location location) const 0156 { 0157 switch (location) { 0158 case Types::TopEdge: 0159 return hasElementPrefix(QStringLiteral("north")); 0160 case Types::BottomEdge: 0161 return hasElementPrefix(QStringLiteral("south")); 0162 case Types::LeftEdge: 0163 return hasElementPrefix(QStringLiteral("west")); 0164 case Types::RightEdge: 0165 return hasElementPrefix(QStringLiteral("east")); 0166 default: 0167 return hasElementPrefix(QString()); 0168 } 0169 } 0170 0171 QString FrameSvg::prefix() 0172 { 0173 return d->requestedPrefix; 0174 } 0175 0176 void FrameSvg::resizeFrame(const QSizeF &size) 0177 { 0178 if (imagePath().isEmpty()) { 0179 return; 0180 } 0181 0182 if (size.isEmpty()) { 0183 #ifndef NDEBUG 0184 // qCDebug(LOG_PLASMA) << "Invalid size" << size; 0185 #endif 0186 return; 0187 } 0188 0189 if (d->frame && size.toSize() == d->frame->frameSize) { 0190 return; 0191 } 0192 d->pendingFrameSize = size.toSize(); 0193 0194 if (!d->repaintBlocked) { 0195 d->updateFrameData(FrameSvgPrivate::UpdateFrame); 0196 } 0197 } 0198 0199 QSizeF FrameSvg::frameSize() const 0200 { 0201 if (!d->frame) { 0202 return QSize(-1, -1); 0203 } else { 0204 return d->frameSize(d->frame.data()); 0205 } 0206 } 0207 0208 qreal FrameSvg::marginSize(const Plasma::Types::MarginEdge edge) const 0209 { 0210 if (!d->frame) { 0211 return .0; 0212 } 0213 0214 if (d->frame->noBorderPadding) { 0215 return .0; 0216 } 0217 0218 switch (edge) { 0219 case Plasma::Types::TopMargin: 0220 return d->frame->topMargin; 0221 0222 case Plasma::Types::LeftMargin: 0223 return d->frame->leftMargin; 0224 0225 case Plasma::Types::RightMargin: 0226 return d->frame->rightMargin; 0227 0228 //Plasma::BottomMargin 0229 default: 0230 return d->frame->bottomMargin; 0231 } 0232 } 0233 0234 qreal FrameSvg::fixedMarginSize(const Plasma::Types::MarginEdge edge) const 0235 { 0236 if (!d->frame) { 0237 return .0; 0238 } 0239 0240 if (d->frame->noBorderPadding) { 0241 return .0; 0242 } 0243 0244 switch (edge) { 0245 case Plasma::Types::TopMargin: 0246 return d->frame->fixedTopMargin; 0247 0248 case Plasma::Types::LeftMargin: 0249 return d->frame->fixedLeftMargin; 0250 0251 case Plasma::Types::RightMargin: 0252 return d->frame->fixedRightMargin; 0253 0254 //Plasma::BottomMargin 0255 default: 0256 return d->frame->fixedBottomMargin; 0257 } 0258 } 0259 0260 void FrameSvg::getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const 0261 { 0262 if (!d->frame || d->frame->noBorderPadding) { 0263 left = top = right = bottom = 0; 0264 return; 0265 } 0266 0267 top = d->frame->topMargin; 0268 left = d->frame->leftMargin; 0269 right = d->frame->rightMargin; 0270 bottom = d->frame->bottomMargin; 0271 } 0272 0273 void FrameSvg::getFixedMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const 0274 { 0275 if (!d->frame || d->frame->noBorderPadding) { 0276 left = top = right = bottom = 0; 0277 return; 0278 } 0279 0280 top = d->frame->fixedTopMargin; 0281 left = d->frame->fixedLeftMargin; 0282 right = d->frame->fixedRightMargin; 0283 bottom = d->frame->fixedBottomMargin; 0284 } 0285 0286 QRectF FrameSvg::contentsRect() const 0287 { 0288 if (d->frame) { 0289 QRectF rect(QPoint(0,0), d->frame->frameSize); 0290 return rect.adjusted(d->frame->leftMargin, d->frame->topMargin, -d->frame->rightMargin, -d->frame->bottomMargin); 0291 } else { 0292 return QRectF(); 0293 } 0294 } 0295 0296 QPixmap FrameSvg::alphaMask() const 0297 { 0298 //FIXME: the distinction between overlay and 0299 return d->alphaMask(); 0300 } 0301 0302 QRegion FrameSvg::mask() const 0303 { 0304 QRegion result; 0305 if (!d->frame) { 0306 return result; 0307 } 0308 0309 QString id = d->cacheId(d->frame.data(), QString()); 0310 0311 QRegion* obj = d->frame->cachedMasks.object(id); 0312 0313 if (!obj) { 0314 obj = new QRegion(QBitmap(d->alphaMask().mask())); 0315 result = *obj; 0316 d->frame->cachedMasks.insert(id, obj); 0317 } 0318 else { 0319 result = *obj; 0320 } 0321 return result; 0322 } 0323 0324 void FrameSvg::setCacheAllRenderedFrames(bool cache) 0325 { 0326 if (d->cacheAll && !cache) { 0327 clearCache(); 0328 } 0329 0330 d->cacheAll = cache; 0331 } 0332 0333 bool FrameSvg::cacheAllRenderedFrames() const 0334 { 0335 return d->cacheAll; 0336 } 0337 0338 void FrameSvg::clearCache() 0339 { 0340 if (d->frame) { 0341 d->frame->cachedBackground = QPixmap(); 0342 d->frame->cachedMasks.clear(); 0343 } 0344 if (d->maskFrame) { 0345 d->maskFrame->cachedBackground = QPixmap(); 0346 d->maskFrame->cachedMasks.clear(); 0347 } 0348 } 0349 0350 QPixmap FrameSvg::framePixmap() 0351 { 0352 if (d->frame->cachedBackground.isNull()) { 0353 d->generateBackground(d->frame); 0354 } 0355 0356 return d->frame->cachedBackground; 0357 } 0358 0359 void FrameSvg::paintFrame(QPainter *painter, const QRectF &target, const QRectF &source) 0360 { 0361 if (d->frame->cachedBackground.isNull()) { 0362 d->generateBackground(d->frame); 0363 if (d->frame->cachedBackground.isNull()) { 0364 return; 0365 } 0366 } 0367 0368 painter->drawPixmap(target, d->frame->cachedBackground, source.isValid() ? source : target); 0369 } 0370 0371 void FrameSvg::paintFrame(QPainter *painter, const QPointF &pos) 0372 { 0373 if (d->frame->cachedBackground.isNull()) { 0374 d->generateBackground(d->frame); 0375 if (d->frame->cachedBackground.isNull()) { 0376 return; 0377 } 0378 } 0379 0380 painter->drawPixmap(pos, d->frame->cachedBackground); 0381 } 0382 0383 //#define DEBUG_FRAMESVG_CACHE 0384 FrameSvgPrivate::~FrameSvgPrivate() = default; 0385 0386 QPixmap FrameSvgPrivate::alphaMask() 0387 { 0388 QString maskPrefix; 0389 0390 if (q->hasElement(QLatin1String("mask-") % prefix % QLatin1String("center"))) { 0391 maskPrefix = QStringLiteral("mask-"); 0392 } 0393 0394 if (maskPrefix.isNull()) { 0395 if (frame->cachedBackground.isNull()) { 0396 generateBackground(frame); 0397 } 0398 return frame->cachedBackground; 0399 } 0400 0401 // We are setting the prefix only temporary to generate 0402 // the needed mask image 0403 const QString maskRequestedPrefix = requestedPrefix.isEmpty() ? QStringLiteral("mask") : maskPrefix % requestedPrefix; 0404 maskPrefix = maskPrefix % prefix; 0405 0406 if (!maskFrame) { 0407 maskFrame = lookupOrCreateMaskFrame(frame, maskPrefix, maskRequestedPrefix); 0408 if (!maskFrame->cachedBackground.isNull()) { 0409 return maskFrame->cachedBackground; 0410 } 0411 updateSizes(maskFrame); 0412 generateBackground(maskFrame); 0413 return maskFrame->cachedBackground; 0414 } 0415 0416 const bool shouldUpdate = maskFrame->enabledBorders != frame->enabledBorders 0417 || maskFrame->frameSize != frameSize(frame.data()) 0418 || maskFrame->imagePath != frame->imagePath; 0419 if (shouldUpdate) { 0420 maskFrame = lookupOrCreateMaskFrame(frame, maskPrefix, maskRequestedPrefix); 0421 if (!maskFrame->cachedBackground.isNull()) { 0422 return maskFrame->cachedBackground; 0423 } 0424 updateSizes(maskFrame); 0425 } 0426 0427 if (maskFrame->cachedBackground.isNull()) { 0428 generateBackground(maskFrame); 0429 } 0430 0431 return maskFrame->cachedBackground; 0432 } 0433 0434 QSharedPointer<FrameData> FrameSvgPrivate::lookupOrCreateMaskFrame(const QSharedPointer<FrameData> &frame, const QString &maskPrefix, const QString &maskRequestedPrefix) 0435 { 0436 const QString key = cacheId(frame.data(), maskPrefix); 0437 QSharedPointer<FrameData> mask = s_sharedFrames[q->theme()->d].value(key); 0438 0439 // See if we can find a suitable candidate in the shared frames. 0440 // If there is one, use it. 0441 if (mask) { 0442 return mask; 0443 } 0444 0445 mask.reset(new FrameData(*frame.data(), q)); 0446 mask->prefix = maskPrefix; 0447 mask->requestedPrefix = maskRequestedPrefix; 0448 mask->theme = q->theme()->d; 0449 mask->imagePath = frame->imagePath; 0450 mask->enabledBorders = frame->enabledBorders; 0451 mask->frameSize = frameSize(frame).toSize(); 0452 mask->cacheId = key; 0453 s_sharedFrames[q->theme()->d].insert(key, mask); 0454 0455 return mask; 0456 } 0457 0458 void FrameSvgPrivate::generateBackground(const QSharedPointer<FrameData> &frame) 0459 { 0460 if (!frame->cachedBackground.isNull() || !q->hasElementPrefix(frame->prefix)) { 0461 return; 0462 } 0463 0464 const QString id = cacheId(frame.data(), frame->prefix); 0465 0466 bool frameCached = !frame->cachedBackground.isNull(); 0467 bool overlayCached = false; 0468 const bool overlayAvailable = !frame->prefix.startsWith(QLatin1String("mask-")) && q->hasElement(frame->prefix % QLatin1String("overlay")); 0469 QPixmap overlay; 0470 if (q->isUsingRenderingCache()) { 0471 frameCached = q->theme()->findInCache(id, frame->cachedBackground) && !frame->cachedBackground.isNull(); 0472 0473 if (overlayAvailable) { 0474 overlayCached = q->theme()->findInCache(QLatin1String("overlay_") % id, overlay) && !overlay.isNull(); 0475 } 0476 } 0477 0478 if (!frameCached) { 0479 generateFrameBackground(frame); 0480 } 0481 0482 //Overlays 0483 QSize overlaySize; 0484 QPoint actualOverlayPos = QPoint(0, 0); 0485 if (overlayAvailable && !overlayCached) { 0486 overlaySize = q->elementSize(frame->prefix % QLatin1String("overlay")); 0487 0488 if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-pos-right"))) { 0489 actualOverlayPos.setX(frame->frameSize.width() - overlaySize.width()); 0490 } else if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-pos-bottom"))) { 0491 actualOverlayPos.setY(frame->frameSize.height() - overlaySize.height()); 0492 //Stretched or Tiled? 0493 } else if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-stretch"))) { 0494 overlaySize = frameSize(frame).toSize(); 0495 } else { 0496 if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-horizontal"))) { 0497 overlaySize.setWidth(frameSize(frame).width()); 0498 } 0499 if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-vertical"))) { 0500 overlaySize.setHeight(frameSize(frame).height()); 0501 } 0502 } 0503 0504 overlay = alphaMask(); 0505 QPainter overlayPainter(&overlay); 0506 overlayPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); 0507 //Tiling? 0508 if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-horizontal")) || 0509 q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-vertical"))) { 0510 0511 QSize s = q->size(); 0512 q->resize(q->elementSize(frame->prefix % QLatin1String("overlay"))); 0513 0514 overlayPainter.drawTiledPixmap(QRect(QPoint(0, 0), overlaySize), q->pixmap(frame->prefix % QLatin1String("overlay"))); 0515 q->resize(s); 0516 } else { 0517 q->paint(&overlayPainter, QRect(actualOverlayPos, overlaySize), frame->prefix % QLatin1String("overlay")); 0518 } 0519 0520 overlayPainter.end(); 0521 } 0522 0523 if (!frameCached) { 0524 cacheFrame(frame->prefix, frame->cachedBackground, overlayCached ? overlay : QPixmap()); 0525 } 0526 0527 if (!overlay.isNull()) { 0528 QPainter p(&frame->cachedBackground); 0529 p.setCompositionMode(QPainter::CompositionMode_SourceOver); 0530 p.drawPixmap(actualOverlayPos, overlay, QRect(actualOverlayPos, overlaySize)); 0531 } 0532 } 0533 0534 void FrameSvgPrivate::generateFrameBackground(const QSharedPointer<FrameData> &frame) 0535 { 0536 //qCDebug(LOG_PLASMA) << "generating background"; 0537 const QSize size = frameSize(frame).toSize() * q->devicePixelRatio(); 0538 0539 if (!size.isValid()) { 0540 #ifndef NDEBUG 0541 // qCDebug(LOG_PLASMA) << "Invalid frame size" << size; 0542 #endif 0543 return; 0544 } 0545 if (size.width() >= MAX_FRAME_SIZE || size.height() >= MAX_FRAME_SIZE) { 0546 qCWarning(LOG_PLASMA) << "Not generating frame background for a size whose width or height is more than" << MAX_FRAME_SIZE << size; 0547 return; 0548 } 0549 0550 frame->cachedBackground = QPixmap(size); 0551 frame->cachedBackground.fill(Qt::transparent); 0552 QPainter p(&frame->cachedBackground); 0553 p.setCompositionMode(QPainter::CompositionMode_Source); 0554 p.setRenderHint(QPainter::SmoothPixmapTransform); 0555 0556 QRect contentRect = contentGeometry(frame, size); 0557 paintCenter(p, frame, contentRect, size); 0558 0559 paintCorner(p, frame, FrameSvg::LeftBorder|FrameSvg::TopBorder, contentRect); 0560 paintCorner(p, frame, FrameSvg::RightBorder|FrameSvg::TopBorder, contentRect); 0561 paintCorner(p, frame, FrameSvg::LeftBorder|FrameSvg::BottomBorder, contentRect); 0562 paintCorner(p, frame, FrameSvg::RightBorder|FrameSvg::BottomBorder, contentRect); 0563 0564 // Sides 0565 const int leftHeight = q->elementSize(frame->prefix % QLatin1String("left")).height(); 0566 paintBorder(p, frame, FrameSvg::LeftBorder, QSize(frame->leftWidth, leftHeight) * q->devicePixelRatio(), contentRect); 0567 const int rightHeight = q->elementSize(frame->prefix % QLatin1String("right")).height(); 0568 paintBorder(p, frame, FrameSvg::RightBorder, QSize(frame->rightWidth, rightHeight) * q->devicePixelRatio(), contentRect); 0569 0570 const int topWidth = q->elementSize(frame->prefix % QLatin1String("top")).width(); 0571 paintBorder(p, frame, FrameSvg::TopBorder, QSize(topWidth, frame->topHeight) * q->devicePixelRatio(), contentRect); 0572 const int bottomWidth = q->elementSize(frame->prefix % QLatin1String("bottom")).width(); 0573 paintBorder(p, frame, FrameSvg::BottomBorder, QSize(bottomWidth, frame->bottomHeight) * q->devicePixelRatio(), contentRect); 0574 p.end(); 0575 0576 frame->cachedBackground.setDevicePixelRatio(q->devicePixelRatio()); 0577 } 0578 0579 QRect FrameSvgPrivate::contentGeometry(const QSharedPointer<FrameData> &frame, const QSize& size) const 0580 { 0581 const QSize contentSize(size.width() - frame->leftWidth * q->devicePixelRatio() - frame->rightWidth * q->devicePixelRatio(), 0582 size.height() - frame->topHeight * q->devicePixelRatio() - frame->bottomHeight * q->devicePixelRatio()); 0583 QRect contentRect(QPoint(0,0), contentSize); 0584 if (frame->enabledBorders & FrameSvg::LeftBorder && q->hasElement(frame->prefix % QLatin1String("left"))) { 0585 contentRect.translate(frame->leftWidth * q->devicePixelRatio(), 0); 0586 } 0587 0588 // Corners 0589 if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(frame->prefix % QLatin1String("top"))) { 0590 contentRect.translate(0, frame->topHeight * q->devicePixelRatio()); 0591 } 0592 return contentRect; 0593 } 0594 0595 void FrameSvgPrivate::updateFrameData(UpdateType updateType) 0596 { 0597 auto fd = frame; 0598 QString newKey; 0599 0600 if (fd) { 0601 const QString oldKey = fd->cacheId; 0602 0603 const QString oldPath = fd->imagePath; 0604 const FrameSvg::EnabledBorders oldBorders = fd->enabledBorders; 0605 const QSize currentSize = fd->frameSize; 0606 0607 fd->enabledBorders = enabledBorders; 0608 fd->frameSize = pendingFrameSize; 0609 fd->imagePath = q->imagePath(); 0610 0611 newKey = cacheId(fd.data(), prefix); 0612 0613 //reset frame to old values 0614 fd->enabledBorders = oldBorders; 0615 fd->frameSize = currentSize; 0616 fd->imagePath = oldPath; 0617 0618 //FIXME: something more efficient than string comparison? 0619 if (oldKey == newKey) { 0620 return; 0621 } 0622 0623 //qCDebug(LOG_PLASMA) << "looking for" << newKey; 0624 auto newFd = FrameSvgPrivate::s_sharedFrames[q->theme()->d].value(newKey); 0625 if (newFd) { 0626 //qCDebug(LOG_PLASMA) << "FOUND IT!" << newFd->refcount; 0627 // we've found a match, use that one 0628 Q_ASSERT(newKey == newFd.lock()->cacheId); 0629 frame = newFd; 0630 return; 0631 } 0632 0633 fd.reset(new FrameData(*fd, q)); 0634 } else { 0635 fd.reset(new FrameData(q, QString())); 0636 } 0637 0638 frame = fd; 0639 fd->prefix = prefix; 0640 fd->requestedPrefix = requestedPrefix; 0641 //updateSizes(); 0642 fd->enabledBorders = enabledBorders; 0643 fd->frameSize = pendingFrameSize; 0644 fd->imagePath = q->imagePath(); 0645 //was fd just created empty now? 0646 if (newKey.isEmpty()) { 0647 newKey = cacheId(fd.data(), prefix); 0648 } 0649 0650 // we know it isn't in s_sharedFrames due to the check above, so insert it now 0651 FrameSvgPrivate::s_sharedFrames[q->theme()->d].insert(newKey, fd); 0652 fd->cacheId = newKey; 0653 fd->theme = q->theme()->d; 0654 if (updateType == UpdateFrameAndMargins) { 0655 updateAndSignalSizes(); 0656 } else { 0657 updateSizes(frame); 0658 } 0659 } 0660 0661 void FrameSvgPrivate::paintCenter(QPainter& p, const QSharedPointer<FrameData> &frame, const QRect& contentRect, const QSize& fullSize) 0662 { 0663 if (!contentRect.isEmpty()) { 0664 const QString centerElementId = frame->prefix % QLatin1String("center"); 0665 if (frame->tileCenter) { 0666 QSize centerTileSize = q->elementSize(centerElementId); 0667 QPixmap center(centerTileSize); 0668 center.fill(Qt::transparent); 0669 0670 QPainter centerPainter(¢er); 0671 centerPainter.setCompositionMode(QPainter::CompositionMode_Source); 0672 q->paint(¢erPainter, QRect(QPoint(0, 0), centerTileSize),centerElementId); 0673 0674 if (frame->composeOverBorder) { 0675 p.drawTiledPixmap(QRect(QPoint(0, 0), fullSize), center); 0676 } else { 0677 p.drawTiledPixmap(FrameSvgHelpers::sectionRect(FrameSvg::NoBorder, contentRect, fullSize * q->devicePixelRatio()), center); 0678 } 0679 } else { 0680 if (frame->composeOverBorder) { 0681 q->paint(&p, QRect(QPoint(0, 0), fullSize), 0682 centerElementId); 0683 } else { 0684 q->paint(&p, FrameSvgHelpers::sectionRect(FrameSvg::NoBorder, contentRect, fullSize * q->devicePixelRatio()), centerElementId); 0685 } 0686 } 0687 } 0688 0689 if (frame->composeOverBorder) { 0690 p.setCompositionMode(QPainter::CompositionMode_DestinationIn); 0691 p.drawPixmap(QRect(QPoint(0, 0), fullSize), alphaMask()); 0692 p.setCompositionMode(QPainter::CompositionMode_SourceOver); 0693 } 0694 } 0695 0696 void FrameSvgPrivate::paintBorder(QPainter& p, const QSharedPointer<FrameData> &frame, const FrameSvg::EnabledBorders borders, const QSize& size, const QRect& contentRect) const 0697 { 0698 QString side = frame->prefix % FrameSvgHelpers::borderToElementId(borders); 0699 if (frame->enabledBorders & borders && q->hasElement(side) && !size.isEmpty()) { 0700 if (frame->stretchBorders) { 0701 q->paint(&p, FrameSvgHelpers::sectionRect(borders, contentRect, frame->frameSize * q->devicePixelRatio()), side); 0702 } else { 0703 QPixmap px(size); 0704 px.fill(Qt::transparent); 0705 0706 QPainter sidePainter(&px); 0707 sidePainter.setCompositionMode(QPainter::CompositionMode_Source); 0708 q->paint(&sidePainter, QRect(QPoint(0, 0), size), side); 0709 0710 p.drawTiledPixmap(FrameSvgHelpers::sectionRect(borders, contentRect, frame->frameSize * q->devicePixelRatio()), px); 0711 } 0712 } 0713 } 0714 0715 void FrameSvgPrivate::paintCorner(QPainter& p, const QSharedPointer<FrameData> &frame, Plasma::FrameSvg::EnabledBorders border, const QRect& contentRect) const 0716 { 0717 // Draw the corner only if both borders in both directions are enabled. 0718 if ((frame->enabledBorders & border) != border) { 0719 return; 0720 } 0721 const QString corner = frame->prefix % FrameSvgHelpers::borderToElementId(border); 0722 if (q->hasElement(corner)) { 0723 q->paint(&p, FrameSvgHelpers::sectionRect(border, contentRect, frame->frameSize * q->devicePixelRatio()), corner); 0724 } 0725 } 0726 0727 QString FrameSvgPrivate::cacheId(FrameData *frame, const QString &prefixToSave) const 0728 { 0729 const QSize size = frameSize(frame).toSize(); 0730 const QLatin1Char s('_'); 0731 return QString::number(frame->enabledBorders) % s % QString::number(size.width()) % s % QString::number(size.height()) % s % QString::number(q->scaleFactor()) % s % QString::number(q->devicePixelRatio()) % s % prefixToSave % s % frame->imagePath; 0732 } 0733 0734 void FrameSvgPrivate::cacheFrame(const QString &prefixToSave, const QPixmap &background, const QPixmap &overlay) 0735 { 0736 if (!q->isUsingRenderingCache()) { 0737 return; 0738 } 0739 0740 //insert background 0741 if (!frame) { 0742 return; 0743 } 0744 0745 const QString id = cacheId(frame.data(), prefixToSave); 0746 0747 //qCDebug(LOG_PLASMA)<<"Saving to cache frame"<<id; 0748 0749 q->theme()->insertIntoCache(id, background, QString::number((qint64)q, 16) % prefixToSave); 0750 0751 if (!overlay.isNull()) { 0752 //insert overlay 0753 q->theme()->insertIntoCache(QLatin1String("overlay_") % id, overlay, QString::number((qint64)q, 16) % prefixToSave % QLatin1String("overlay")); 0754 } 0755 } 0756 0757 void FrameSvgPrivate::updateSizes(FrameData *frame) const 0758 { 0759 //qCDebug(LOG_PLASMA) << "!!!!!!!!!!!!!!!!!!!!!! updating sizes" << prefix; 0760 Q_ASSERT(frame); 0761 0762 QSize s = q->size(); 0763 q->resize(); 0764 if (!frame->cachedBackground.isNull()) { 0765 frame->cachedBackground = QPixmap(); 0766 } 0767 0768 //This has the same size regardless the border is enabled or not 0769 frame->fixedTopHeight = q->elementSize(frame->prefix % QLatin1String("top")).height(); 0770 0771 if (q->hasElement(frame->prefix % QLatin1String("hint-top-margin"))) { 0772 frame->fixedTopMargin = q->elementSize(frame->prefix % QLatin1String("hint-top-margin")).height(); 0773 } else { 0774 frame->fixedTopMargin = frame->fixedTopHeight; 0775 } 0776 0777 //The same, but its size depends from the margin being enabled 0778 if (frame->enabledBorders & FrameSvg::TopBorder) { 0779 frame->topMargin = frame->fixedTopMargin; 0780 frame->topHeight = frame->fixedTopHeight; 0781 } else { 0782 frame->topMargin = frame->topHeight = 0; 0783 } 0784 0785 frame->fixedLeftWidth = q->elementSize(frame->prefix % QLatin1String("left")).width(); 0786 0787 if (q->hasElement(frame->prefix % QLatin1String("hint-left-margin"))) { 0788 frame->fixedLeftMargin = q->elementSize(frame->prefix % QLatin1String("hint-left-margin")).width(); 0789 } else { 0790 frame->fixedLeftMargin = frame->fixedLeftWidth; 0791 } 0792 0793 if (frame->enabledBorders & FrameSvg::LeftBorder) { 0794 frame->leftMargin = frame->fixedLeftMargin; 0795 frame->leftWidth = frame->fixedLeftWidth; 0796 } else { 0797 frame->leftMargin = frame->leftWidth = 0; 0798 } 0799 0800 frame->fixedRightWidth = q->elementSize(frame->prefix % QLatin1String("right")).width(); 0801 0802 if (q->hasElement(frame->prefix % QLatin1String("hint-right-margin"))) { 0803 frame->fixedRightMargin = q->elementSize(frame->prefix % QLatin1String("hint-right-margin")).width(); 0804 } else { 0805 frame->fixedRightMargin = frame->fixedRightWidth; 0806 } 0807 0808 if (frame->enabledBorders & FrameSvg::RightBorder) { 0809 frame->rightMargin = frame->fixedRightMargin; 0810 frame->rightWidth = frame->fixedRightWidth; 0811 } else { 0812 frame->rightMargin = frame->rightWidth = 0; 0813 } 0814 0815 frame->fixedBottomHeight = q->elementSize(frame->prefix % QLatin1String("bottom")).height(); 0816 0817 if (q->hasElement(frame->prefix % QLatin1String("hint-bottom-margin"))) { 0818 frame->fixedBottomMargin = q->elementSize(frame->prefix % QLatin1String("hint-bottom-margin")).height(); 0819 } else { 0820 frame->fixedBottomMargin = frame->fixedBottomHeight; 0821 } 0822 0823 if (frame->enabledBorders & FrameSvg::BottomBorder) { 0824 frame->bottomMargin = frame->fixedBottomMargin; 0825 frame->bottomHeight = frame->fixedBottomHeight; 0826 } else { 0827 frame->bottomMargin = frame->bottomHeight = 0; 0828 } 0829 0830 frame->composeOverBorder = (q->hasElement(frame->prefix % QLatin1String("hint-compose-over-border")) && 0831 q->hasElement(QLatin1String("mask-") % frame->prefix % QLatin1String("center"))); 0832 0833 //since it's rectangular, topWidth and bottomWidth must be the same 0834 //the ones that don't have a frame->prefix is for retrocompatibility 0835 frame->tileCenter = (q->hasElement(QStringLiteral("hint-tile-center")) || q->hasElement(frame->prefix % QLatin1String("hint-tile-center"))); 0836 frame->noBorderPadding = (q->hasElement(QStringLiteral("hint-no-border-padding")) || q->hasElement(frame->prefix % QLatin1String("hint-no-border-padding"))); 0837 frame->stretchBorders = (q->hasElement(QStringLiteral("hint-stretch-borders")) || q->hasElement(frame->prefix % QLatin1String("hint-stretch-borders"))); 0838 q->resize(s); 0839 } 0840 0841 void FrameSvgPrivate::updateNeeded() 0842 { 0843 q->setElementPrefix(requestedPrefix); 0844 //frame not created yet? 0845 if (!frame) { 0846 return; 0847 } 0848 q->clearCache(); 0849 updateSizes(frame); 0850 } 0851 0852 void FrameSvgPrivate::updateAndSignalSizes() 0853 { 0854 //frame not created yet? 0855 if (!frame) { 0856 return; 0857 } 0858 updateSizes(frame); 0859 emit q->repaintNeeded(); 0860 } 0861 0862 QSizeF FrameSvgPrivate::frameSize(FrameData *frame) const 0863 { 0864 if (!frame) { 0865 return QSizeF(); 0866 } 0867 0868 if (!frame->frameSize.isValid()) { 0869 updateSizes(frame); 0870 frame->frameSize = q->size(); 0871 } 0872 0873 return frame->frameSize; 0874 } 0875 0876 QString FrameSvg::actualPrefix() const 0877 { 0878 return d->prefix; 0879 } 0880 0881 bool FrameSvg::isRepaintBlocked() const 0882 { 0883 return d->repaintBlocked; 0884 } 0885 0886 void FrameSvg::setRepaintBlocked(bool blocked) 0887 { 0888 d->repaintBlocked = blocked; 0889 0890 if (!blocked) { 0891 d->updateFrameData(); 0892 } 0893 } 0894 0895 } // Plasma namespace