2 * Copyright 2008 by Aaron Seigo <aseigo@kde.org>
3 * Copyright 2008 Marco Martin <notmart@gmail.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Library General Public License as
7 * published by the Free Software Foundation; either version 2, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details
15 * You should have received a copy of the GNU Library General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 #include "private/framesvg_p.h"
29 #include <QCryptographicHash>
33 #include <plasma/theme.h>
34 #include <plasma/applet.h>
39 FrameSvg::FrameSvg(QObject
*parent
)
41 d(new FrameSvgPrivate(this))
43 connect(this, SIGNAL(repaintNeeded()), this, SLOT(updateNeeded()));
44 d
->frames
.insert(QString(), new FrameData());
46 d
->saveTimer
= new QTimer(this);
47 d
->saveTimer
->setSingleShot(true);
48 connect(d
->saveTimer
, SIGNAL(timeout()), this, SLOT(scheduledCacheUpdate()));
56 void FrameSvg::setImagePath(const QString
&path
)
58 if (path
== imagePath()) {
62 Svg::setImagePath(path
);
63 setContainsMultipleImages(true);
66 d
->updateAndSignalSizes();
69 void FrameSvg::setEnabledBorders(const EnabledBorders borders
)
71 if (borders
== d
->frames
[d
->prefix
]->enabledBorders
) {
75 d
->frames
[d
->prefix
]->enabledBorders
= borders
;
76 d
->updateAndSignalSizes();
79 FrameSvg::EnabledBorders
FrameSvg::enabledBorders() const
81 QHash
<QString
, FrameData
*>::const_iterator it
= d
->frames
.constFind(d
->prefix
);
83 if (it
!= d
->frames
.constEnd()) {
84 return it
.value()->enabledBorders
;
90 void FrameSvg::setElementPrefix(Plasma::Location location
)
94 setElementPrefix("north");
97 setElementPrefix("south");
100 setElementPrefix("west");
103 setElementPrefix("east");
106 setElementPrefix(QString());
109 d
->location
= location
;
112 void FrameSvg::setElementPrefix(const QString
& prefix
)
114 const QString
oldPrefix(d
->prefix
);
116 if (!hasElement(prefix
+ "-center")) {
120 if (!d
->prefix
.isEmpty()) {
126 if (oldPrefix
== d
->prefix
&& d
->frames
[oldPrefix
]) {
130 if (!d
->frames
.contains(d
->prefix
)) {
131 d
->frames
.insert(d
->prefix
, new FrameData(*(d
->frames
[oldPrefix
])));
136 delete d
->frames
[oldPrefix
];
137 d
->framesToSave
.removeAll(oldPrefix
);
138 d
->frames
.remove(oldPrefix
);
141 d
->location
= Floating
;
144 bool FrameSvg::hasElementPrefix(const QString
& prefix
) const
146 //for now it simply checks if a center element exists,
147 //because it could make sense for certain themes to not have all the elements
148 if (prefix
.isEmpty()) {
149 return hasElement("center");
151 return hasElement(prefix
+ "-center");
155 bool FrameSvg::hasElementPrefix(Plasma::Location location
) const
159 return hasElementPrefix("north");
162 return hasElementPrefix("south");
165 return hasElementPrefix("west");
168 return hasElementPrefix("east");
171 return hasElementPrefix(QString());
176 QString
FrameSvg::prefix()
178 if (d
->prefix
.isEmpty()) {
182 return d
->prefix
.left(d
->prefix
.size() - 1);
185 void FrameSvg::resizeFrame(const QSizeF
&size
)
187 if (size
.isEmpty()) {
188 kWarning() << "Invalid size" << size
;
192 if (size
== d
->frames
[d
->prefix
]->frameSize
) {
197 d
->frames
[d
->prefix
]->frameSize
= size
.toSize();
200 QSizeF
FrameSvg::frameSize() const
202 QHash
<QString
, FrameData
*>::const_iterator it
= d
->frames
.constFind(d
->prefix
);
204 if (it
!= d
->frames
.constEnd()) {
205 return it
.value()->frameSize
;
207 return QSize(-1, -1);
211 qreal
FrameSvg::marginSize(const Plasma::MarginEdge edge
) const
213 if (d
->frames
[d
->prefix
]->noBorderPadding
) {
218 case Plasma::TopMargin
:
219 return d
->frames
[d
->prefix
]->topMargin
;
222 case Plasma::LeftMargin
:
223 return d
->frames
[d
->prefix
]->leftMargin
;
226 case Plasma::RightMargin
:
227 return d
->frames
[d
->prefix
]->rightMargin
;
230 //Plasma::BottomMargin
232 return d
->frames
[d
->prefix
]->bottomMargin
;
237 void FrameSvg::getMargins(qreal
&left
, qreal
&top
, qreal
&right
, qreal
&bottom
) const
239 FrameData
*frame
= d
->frames
[d
->prefix
];
241 if (!frame
|| frame
->noBorderPadding
) {
242 left
= top
= right
= bottom
= 0;
246 top
= frame
->topMargin
;
247 left
= frame
->leftMargin
;
248 right
= frame
->rightMargin
;
249 bottom
= frame
->bottomMargin
;
252 QRectF
FrameSvg::contentsRect() const
254 QSizeF
size(frameSize());
256 if (size
.isValid()) {
257 QRectF
rect(QPointF(0, 0), size
);
258 FrameData
*frame
= d
->frames
[d
->prefix
];
260 return rect
.adjusted(frame
->leftMargin
, frame
->topMargin
,
261 -frame
->rightMargin
, -frame
->bottomMargin
);
267 QPixmap
FrameSvg::alphaMask() const
269 FrameData
*frame
= d
->frames
[d
->prefix
];
271 if (hasElement("mask-" + d
->prefix
+ "center")) {
272 QString oldPrefix
= d
->prefix
;
274 // We are setting the prefix only temporary to generate
275 // the needed mask image
276 d
->prefix
= "mask-" + oldPrefix
;
278 if (!d
->frames
.contains(d
->prefix
)) {
279 d
->frames
.insert(d
->prefix
, new FrameData(*(d
->frames
[oldPrefix
])));
283 FrameData
*maskFrame
= d
->frames
[d
->prefix
];
284 if (maskFrame
->cachedBackground
.isNull() || maskFrame
->frameSize
!= frame
->frameSize
) {
285 maskFrame
->frameSize
= frame
->frameSize
;
286 maskFrame
->cachedBackground
= QPixmap();
288 d
->generateBackground(maskFrame
);
289 if (maskFrame
->cachedBackground
.isNull()) {
294 d
->prefix
= oldPrefix
;
295 return maskFrame
->cachedBackground
;
297 if (frame
->cachedBackground
.isNull()) {
298 d
->generateBackground(frame
);
299 if (frame
->cachedBackground
.isNull()) {
303 return frame
->cachedBackground
;
307 QRegion
FrameSvg::mask() const
309 FrameData
*frame
= d
->frames
[d
->prefix
];
310 frame
->cachedMask
= QRegion(QBitmap(alphaMask().alphaChannel().createMaskFromColor(Qt::black
)));
311 return frame
->cachedMask
;
314 void FrameSvg::setCacheAllRenderedFrames(bool cache
)
316 if (d
->cacheAll
&& !cache
) {
323 bool FrameSvg::cacheAllRenderedFrames() const
328 void FrameSvg::clearCache()
330 FrameData
*frame
= d
->frames
[d
->prefix
];
332 d
->saveTimer
->stop();
333 d
->framesToSave
.clear();
335 // delete all the frames that aren't this one
336 QMutableHashIterator
<QString
, FrameData
*> it(d
->frames
);
337 while (it
.hasNext()) {
338 FrameData
*p
= it
.next().value();
347 QPixmap
FrameSvg::framePixmap()
349 FrameData
*frame
= d
->frames
[d
->prefix
];
350 if (frame
->cachedBackground
.isNull()) {
351 d
->generateBackground(frame
);
352 if (frame
->cachedBackground
.isNull()) {
358 return frame
->cachedBackground
;
361 void FrameSvg::paintFrame(QPainter
*painter
, const QRectF
&target
, const QRectF
&source
)
363 FrameData
*frame
= d
->frames
[d
->prefix
];
364 if (frame
->cachedBackground
.isNull()) {
365 d
->generateBackground(frame
);
366 if (frame
->cachedBackground
.isNull()) {
371 painter
->drawPixmap(target
, frame
->cachedBackground
, source
.isValid() ? source
: target
);
374 void FrameSvg::paintFrame(QPainter
*painter
, const QPointF
&pos
)
376 FrameData
*frame
= d
->frames
[d
->prefix
];
377 if (frame
->cachedBackground
.isNull()) {
378 d
->generateBackground(frame
);
379 if (frame
->cachedBackground
.isNull()) {
384 painter
->drawPixmap(pos
, frame
->cachedBackground
);
387 void FrameSvgPrivate::generateBackground(FrameData
*frame
)
389 if (!frame
->cachedBackground
.isNull()) {
394 QString id
= QString::fromLatin1("%5_%4_%3_%2_%1_").
395 arg(frame
->enabledBorders
).arg(frame
->frameSize
.width()).arg(frame
->frameSize
.height()).arg(prefix
).arg(q
->imagePath());
397 Theme
*theme
= Theme::defaultTheme();
398 if (theme
->findInCache(id
, frame
->cachedBackground
) && !frame
->cachedBackground
.isNull()) {
402 //kDebug() << "generating background";
403 const int topWidth
= q
->elementSize(prefix
+ "top").width();
404 const int leftHeight
= q
->elementSize(prefix
+ "left").height();
405 const int topOffset
= 0;
406 const int leftOffset
= 0;
409 if (!frame
->frameSize
.isValid()) {
410 kWarning() << "Invalid frame size" << frame
->frameSize
;
414 const int contentWidth
= frame
->frameSize
.width() - frame
->leftWidth
- frame
->rightWidth
;
415 const int contentHeight
= frame
->frameSize
.height() - frame
->topHeight
- frame
->bottomHeight
;
418 int rightOffset
= contentWidth
;
419 int bottomOffset
= contentHeight
;
421 frame
->cachedBackground
= QPixmap(frame
->leftWidth
+ contentWidth
+ frame
->rightWidth
,
422 frame
->topHeight
+ contentHeight
+ frame
->bottomHeight
);
423 frame
->cachedBackground
.fill(Qt::transparent
);
424 QPainter
p(&frame
->cachedBackground
);
425 p
.setCompositionMode(QPainter::CompositionMode_Source
);
426 p
.setRenderHint(QPainter::SmoothPixmapTransform
);
429 if (frame
->tileCenter
) {
430 if (contentHeight
> 0 && contentWidth
> 0) {
431 const int centerTileHeight
= q
->elementSize(prefix
+ "center").height();
432 const int centerTileWidth
= q
->elementSize(prefix
+ "center").width();
433 QPixmap
center(centerTileWidth
, centerTileHeight
);
434 center
.fill(Qt::transparent
);
437 QPainter
centerPainter(¢er
);
438 centerPainter
.setCompositionMode(QPainter::CompositionMode_Source
);
439 q
->paint(¢erPainter
, QRect(QPoint(0, 0), q
->elementSize(prefix
+ "center")), prefix
+ "center");
442 p
.drawTiledPixmap(QRect(frame
->leftWidth
, frame
->topHeight
,
443 contentWidth
, contentHeight
), center
);
446 if (contentHeight
> 0 && contentWidth
> 0) {
447 q
->paint(&p
, QRect(frame
->leftWidth
, frame
->topHeight
,
448 contentWidth
, contentHeight
),
453 if (frame
->enabledBorders
& FrameSvg::LeftBorder
&& q
->hasElement(prefix
+ "left")) {
454 rightOffset
+= frame
->leftWidth
;
458 if (frame
->enabledBorders
& FrameSvg::TopBorder
&& q
->hasElement(prefix
+ "top")) {
459 contentTop
= frame
->topHeight
;
460 bottomOffset
+= frame
->topHeight
;
462 if (q
->hasElement(prefix
+ "topleft") && frame
->enabledBorders
& FrameSvg::LeftBorder
) {
463 q
->paint(&p
, QRect(leftOffset
, topOffset
, frame
->leftWidth
, frame
->topHeight
), prefix
+ "topleft");
465 contentLeft
= frame
->leftWidth
;
468 if (q
->hasElement(prefix
+ "topright") && frame
->enabledBorders
& FrameSvg::RightBorder
) {
469 q
->paint(&p
, QRect(rightOffset
, topOffset
, frame
->rightWidth
, frame
->topHeight
), prefix
+ "topright");
473 if (frame
->enabledBorders
& FrameSvg::BottomBorder
&& q
->hasElement(prefix
+ "bottom")) {
474 if (q
->hasElement(prefix
+ "bottomleft") && frame
->enabledBorders
& FrameSvg::LeftBorder
) {
475 q
->paint(&p
, QRect(leftOffset
, bottomOffset
, frame
->leftWidth
, frame
->bottomHeight
), prefix
+ "bottomleft");
477 contentLeft
= frame
->leftWidth
;
480 if (frame
->enabledBorders
& FrameSvg::RightBorder
&& q
->hasElement(prefix
+ "bottomright")) {
481 q
->paint(&p
, QRect(rightOffset
, bottomOffset
, frame
->rightWidth
, frame
->bottomHeight
), prefix
+ "bottomright");
486 if (frame
->stretchBorders
) {
487 if (frame
->enabledBorders
& FrameSvg::LeftBorder
|| frame
->enabledBorders
& FrameSvg::RightBorder
) {
488 if (q
->hasElement(prefix
+ "left") &&
489 frame
->enabledBorders
& FrameSvg::LeftBorder
) {
490 q
->paint(&p
, QRect(leftOffset
, contentTop
, frame
->leftWidth
, contentHeight
), prefix
+ "left");
493 if (q
->hasElement(prefix
+ "right") &&
494 frame
->enabledBorders
& FrameSvg::RightBorder
) {
495 q
->paint(&p
, QRect(rightOffset
, contentTop
, frame
->rightWidth
, contentHeight
), prefix
+ "right");
499 if (frame
->enabledBorders
& FrameSvg::TopBorder
|| frame
->enabledBorders
& FrameSvg::BottomBorder
) {
500 if (frame
->enabledBorders
& FrameSvg::TopBorder
&& q
->hasElement(prefix
+ "top")) {
501 q
->paint(&p
, QRect(contentLeft
, topOffset
, contentWidth
, frame
->topHeight
), prefix
+ "top");
504 if (frame
->enabledBorders
& FrameSvg::BottomBorder
&& q
->hasElement(prefix
+ "bottom")) {
505 q
->paint(&p
, QRect(contentLeft
, bottomOffset
, contentWidth
, frame
->bottomHeight
), prefix
+ "bottom");
509 if (frame
->enabledBorders
& FrameSvg::LeftBorder
&& q
->hasElement(prefix
+ "left")
510 && leftHeight
> 0 && frame
->leftWidth
> 0) {
511 QPixmap
left(frame
->leftWidth
, leftHeight
);
512 left
.fill(Qt::transparent
);
514 QPainter
sidePainter(&left
);
515 sidePainter
.setCompositionMode(QPainter::CompositionMode_Source
);
516 q
->paint(&sidePainter
, QRect(QPoint(0, 0), left
.size()), prefix
+ "left");
518 p
.drawTiledPixmap(QRect(leftOffset
, contentTop
, frame
->leftWidth
, contentHeight
), left
);
521 if (frame
->enabledBorders
& FrameSvg::RightBorder
&& q
->hasElement(prefix
+ "right") &&
522 leftHeight
> 0 && frame
->rightWidth
> 0) {
523 QPixmap
right(frame
->rightWidth
, leftHeight
);
524 right
.fill(Qt::transparent
);
526 QPainter
sidePainter(&right
);
527 sidePainter
.setCompositionMode(QPainter::CompositionMode_Source
);
528 q
->paint(&sidePainter
, QRect(QPoint(0, 0), right
.size()), prefix
+ "right");
530 p
.drawTiledPixmap(QRect(rightOffset
, contentTop
, frame
->rightWidth
, contentHeight
), right
);
533 if (frame
->enabledBorders
& FrameSvg::TopBorder
&& q
->hasElement(prefix
+ "top")
534 && topWidth
> 0 && frame
->topHeight
> 0) {
535 QPixmap
top(topWidth
, frame
->topHeight
);
536 top
.fill(Qt::transparent
);
538 QPainter
sidePainter(&top
);
539 sidePainter
.setCompositionMode(QPainter::CompositionMode_Source
);
540 q
->paint(&sidePainter
, QRect(QPoint(0, 0), top
.size()), prefix
+ "top");
542 p
.drawTiledPixmap(QRect(contentLeft
, topOffset
, contentWidth
, frame
->topHeight
), top
);
545 if (frame
->enabledBorders
& FrameSvg::BottomBorder
&& q
->hasElement(prefix
+ "bottom")
546 && topWidth
> 0 && frame
->bottomHeight
> 0) {
547 QPixmap
bottom(topWidth
, frame
->bottomHeight
);
548 bottom
.fill(Qt::transparent
);
550 QPainter
sidePainter(&bottom
);
551 sidePainter
.setCompositionMode(QPainter::CompositionMode_Source
);
552 q
->paint(&sidePainter
, QRect(QPoint(0, 0), bottom
.size()), prefix
+ "bottom");
554 p
.drawTiledPixmap(QRect(contentLeft
, bottomOffset
, contentWidth
, frame
->bottomHeight
), bottom
);
559 if (!prefix
.startsWith("mask-") && q
->hasElement(prefix
+"overlay")) {
560 QPoint pos
= QPoint(0, 0);
561 QSize overlaySize
= q
->elementSize(prefix
+"overlay");
563 //Random pos, stretched and tiled are mutually exclusive
564 if (q
->hasElement(prefix
+"hint-overlay-random-pos")) {
566 //Stretched or Tiled?
567 } else if (q
->hasElement(prefix
+"hint-overlay-stretch")) {
568 overlaySize
= frame
->frameSize
;
570 if (q
->hasElement(prefix
+"hint-overlay-tile-horizontal")) {
571 overlaySize
.setWidth(frame
->frameSize
.width());
573 if (q
->hasElement(prefix
+"hint-overlay-tile-vertical")) {
574 overlaySize
.setHeight(frame
->frameSize
.height());
578 QString id
= QString::fromLatin1("overlay_%7_%6_%5_%4_%3_%2_%1_").
579 arg(overlayPos
.y()).arg(overlayPos
.x()).arg(frame
->enabledBorders
).arg(frame
->frameSize
.width()).arg(frame
->frameSize
.height()).arg(prefix
).arg(q
->imagePath());
581 QPixmap overlay
= q
->alphaMask();
583 QPainter
overlayPainter(&overlay
);
584 overlayPainter
.setCompositionMode(QPainter::CompositionMode_SourceIn
);
587 if (q
->hasElement(prefix
+"hint-overlay-tile-horizontal") ||
588 q
->hasElement(prefix
+"hint-overlay-tile-vertical")) {
591 q
->resize(q
->elementSize(prefix
+"overlay"));
593 overlayPainter
.drawTiledPixmap(QRect(QPoint(0,0), overlaySize
), q
->pixmap(prefix
+"overlay"));
596 q
->paint(&overlayPainter
, QRect(overlayPos
, overlaySize
), prefix
+"overlay");
598 overlayPainter
.end();
600 p
.setCompositionMode(QPainter::CompositionMode_SourceOver
);
602 p
.drawPixmap(overlayPos
, overlay
, QRect(overlayPos
, overlaySize
));
605 if (!framesToSave
.contains(prefix
)) {
606 framesToSave
.append(prefix
);
609 saveTimer
->start(300);
613 void FrameSvgPrivate::scheduledCacheUpdate()
615 foreach ( QString prefixToSave
, framesToSave
) {
617 FrameData
*frame
= frames
[prefix
];
618 framesToSave
.removeAll(prefixToSave
);
620 QString id
= QString::fromLatin1("%5_%4_%3_%2_%1_").
621 arg(frame
->enabledBorders
).arg(frame
->frameSize
.width()).arg(frame
->frameSize
.height()).arg(prefix
).arg(q
->imagePath());
623 //kDebug()<<"Saving to cache frame"<<id;
625 Theme::defaultTheme()->insertIntoCache(id
, frame
->cachedBackground
);
628 id
= QString::fromLatin1("overlay_%7_%6_%5_%4_%3_%2_%1_").
629 arg(overlayPos
.y()).arg(overlayPos
.x()).arg(frame
->enabledBorders
).arg(frame
->frameSize
.width()).arg(frame
->frameSize
.height()).arg(prefix
).arg(q
->imagePath());
631 Theme::defaultTheme()->insertIntoCache(id
, frame
->cachedBackground
);
635 void FrameSvgPrivate::updateSizes()
637 //kDebug() << "!!!!!!!!!!!!!!!!!!!!!! updating sizes" << prefix;
638 FrameData
*frame
= frames
[prefix
];
643 frame
->cachedBackground
= QPixmap();
644 frame
->cachedMask
= QRegion();
646 if (frame
->enabledBorders
& FrameSvg::TopBorder
) {
647 frame
->topHeight
= q
->elementSize(prefix
+ "top").height();
649 if (q
->hasElement(prefix
+ "hint-top-margin")) {
650 frame
->topMargin
= q
->elementSize(prefix
+ "hint-top-margin").height();
652 frame
->topMargin
= frame
->topHeight
;
655 frame
->topMargin
= frame
->topHeight
= 0;
658 if (frame
->enabledBorders
& FrameSvg::LeftBorder
) {
659 frame
->leftWidth
= q
->elementSize(prefix
+ "left").width();
661 if (q
->hasElement(prefix
+ "hint-left-margin")) {
662 frame
->leftMargin
= q
->elementSize(prefix
+ "hint-left-margin").width();
664 frame
->leftMargin
= frame
->leftWidth
;
667 frame
->leftMargin
= frame
->leftWidth
= 0;
670 if (frame
->enabledBorders
& FrameSvg::RightBorder
) {
671 frame
->rightWidth
= q
->elementSize(prefix
+ "right").width();
673 if (q
->hasElement(prefix
+ "hint-right-margin")) {
674 frame
->rightMargin
= q
->elementSize(prefix
+ "hint-right-margin").width();
676 frame
->rightMargin
= frame
->rightWidth
;
679 frame
->rightMargin
= frame
->rightWidth
= 0;
682 if (frame
->enabledBorders
& FrameSvg::BottomBorder
) {
683 frame
->bottomHeight
= q
->elementSize(prefix
+ "bottom").height();
685 if (q
->hasElement(prefix
+ "hint-bottom-margin")) {
686 frame
->bottomMargin
= q
->elementSize(prefix
+ "hint-bottom-margin").height();
688 frame
->bottomMargin
= frame
->bottomHeight
;
691 frame
->bottomMargin
= frame
->bottomHeight
= 0;
694 //since it's rectangular, topWidth and bottomWidth must be the same
695 frame
->tileCenter
= q
->hasElement("hint-tile-center");
696 frame
->noBorderPadding
= q
->hasElement("hint-no-border-padding");
697 frame
->stretchBorders
= q
->hasElement("hint-stretch-borders");
701 void FrameSvgPrivate::updateNeeded()
707 void FrameSvgPrivate::updateAndSignalSizes()
710 emit q
->repaintNeeded();
713 } // Plasma namespace
715 #include "framesvg.moc"