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

Plasma

  • sources
  • kde-4.12
  • kdelibs
  • plasma
framesvg.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2008-2010 by Aaron Seigo <aseigo@kde.org>
3  * Copyright 2008-2010 Marco Martin <notmart@gmail.com>
4  *
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.
9  *
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
14  *
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.
19  */
20 
21 #include "framesvg.h"
22 #include "private/framesvg_p.h"
23 
24 #include <QAtomicInt>
25 #include <QBitmap>
26 #include <QCryptographicHash>
27 #include <QPainter>
28 #include <QRegion>
29 #include <QSize>
30 #include <QStringBuilder>
31 #include <QTimer>
32 
33 #include <kdebug.h>
34 
35 #include <applet.h>
36 #include <theme.h>
37 #include <private/svg_p.h>
38 
39 namespace Plasma
40 {
41 
42 
43 QHash<QString, FrameData *> FrameSvgPrivate::s_sharedFrames;
44 
45 // Any attempt to generate a frame whose width or height is larger than this
46 // will be rejected
47 static const int MAX_FRAME_SIZE = 100000;
48 
49 FrameSvg::FrameSvg(QObject *parent)
50  : Svg(parent),
51  d(new FrameSvgPrivate(this))
52 {
53  connect(this, SIGNAL(repaintNeeded()), this, SLOT(updateNeeded()));
54  d->frames.insert(QString(), new FrameData(this));
55 }
56 
57 FrameSvg::~FrameSvg()
58 {
59  delete d;
60 }
61 
62 void FrameSvg::setImagePath(const QString &path)
63 {
64  if (path == imagePath()) {
65  return;
66  }
67 
68  bool updateNeeded = true;
69  clearCache();
70 
71  FrameData *fd = d->frames[d->prefix];
72  if (fd->refcount() == 1) {
73  // we're the only user of it, let's remove it from the shared keys
74  // we don't want to deref it, however, as we'll still be using it
75  const QString oldKey = d->cacheId(fd, d->prefix);
76  FrameSvgPrivate::s_sharedFrames.remove(oldKey);
77  } else {
78  // others are using this frame, so deref it for ourselves
79  fd->deref(this);
80  fd = 0;
81  }
82 
83  Svg::d->setImagePath(path);
84 
85  if (!fd) {
86  // we need to replace our frame, start by looking in the frame cache
87  FrameData *oldFd = d->frames[d->prefix];
88  const QString key = d->cacheId(oldFd, d->prefix);
89  fd = FrameSvgPrivate::s_sharedFrames.value(key);
90 
91  if (fd) {
92  // we found one, so ref it and use it; we also don't need to (or want to!)
93  // trigger a full update of the frame since it is already the one we want
94  // and likely already rendered just fine
95  fd->ref(this);
96  updateNeeded = false;
97  } else {
98  // nothing exists for us in the cache, so create a new FrameData based
99  // on the old one
100  fd = new FrameData(*oldFd, this);
101  }
102 
103  d->frames.insert(d->prefix, fd);
104  }
105 
106  setContainsMultipleImages(true);
107  if (updateNeeded) {
108  // ensure our frame is in the cache
109  const QString key = d->cacheId(fd, d->prefix);
110  FrameSvgPrivate::s_sharedFrames.insert(key, fd);
111 
112  // this will emit repaintNeeded() as well when it is done
113  d->updateAndSignalSizes();
114  } else {
115  emit repaintNeeded();
116  }
117 }
118 
119 void FrameSvg::setEnabledBorders(const EnabledBorders borders)
120 {
121  if (borders == d->frames[d->prefix]->enabledBorders) {
122  return;
123  }
124 
125  FrameData *fd = d->frames[d->prefix];
126 
127  const QString oldKey = d->cacheId(fd, d->prefix);
128  const EnabledBorders oldBorders = fd->enabledBorders;
129  fd->enabledBorders = borders;
130  const QString newKey = d->cacheId(fd, d->prefix);
131  fd->enabledBorders = oldBorders;
132 
133  //kDebug() << "looking for" << newKey;
134  FrameData *newFd = FrameSvgPrivate::s_sharedFrames.value(newKey);
135  if (newFd) {
136  //kDebug() << "FOUND IT!" << newFd->refcount;
137  // we've found a math, so insert that new one and ref it ..
138  newFd->ref(this);
139  d->frames.insert(d->prefix, newFd);
140 
141  //.. then deref the old one and if it's no longer used, get rid of it
142  if (fd->deref(this)) {
143  //const QString oldKey = d->cacheId(fd, d->prefix);
144  //kDebug() << "1. Removing it" << oldKey << fd->refcount;
145  FrameSvgPrivate::s_sharedFrames.remove(oldKey);
146  delete fd;
147  }
148 
149  return;
150  }
151 
152  if (fd->refcount() == 1) {
153  // we're the only user of it, let's remove it from the shared keys
154  // we don't want to deref it, however, as we'll still be using it
155  FrameSvgPrivate::s_sharedFrames.remove(oldKey);
156  } else {
157  // others are using it, but we wish to change its size. so deref it,
158  // then create a copy of it (we're automatically ref'd via the ctor),
159  // then insert it into our frames.
160  fd->deref(this);
161  fd = new FrameData(*fd, this);
162  d->frames.insert(d->prefix, fd);
163  }
164 
165  fd->enabledBorders = borders;
166  d->updateAndSignalSizes();
167 }
168 
169 FrameSvg::EnabledBorders FrameSvg::enabledBorders() const
170 {
171  if (d->frames.isEmpty()) {
172  return NoBorder;
173  }
174 
175  QHash<QString, FrameData*>::const_iterator it = d->frames.constFind(d->prefix);
176 
177  if (it != d->frames.constEnd()) {
178  return it.value()->enabledBorders;
179  } else {
180  return NoBorder;
181  }
182 }
183 
184 void FrameSvg::setElementPrefix(Plasma::Location location)
185 {
186  switch (location) {
187  case TopEdge:
188  setElementPrefix("north");
189  break;
190  case BottomEdge:
191  setElementPrefix("south");
192  break;
193  case LeftEdge:
194  setElementPrefix("west");
195  break;
196  case RightEdge:
197  setElementPrefix("east");
198  break;
199  default:
200  setElementPrefix(QString());
201  break;
202  }
203 
204  d->location = location;
205 }
206 
207 void FrameSvg::setElementPrefix(const QString &prefix)
208 {
209  const QString oldPrefix(d->prefix);
210 
211  if (!hasElement(prefix % "-center")) {
212  d->prefix.clear();
213  } else {
214  d->prefix = prefix;
215  if (!d->prefix.isEmpty()) {
216  d->prefix += '-';
217  }
218  }
219 
220  FrameData *oldFrameData = d->frames.value(oldPrefix);
221  if (oldPrefix == d->prefix && oldFrameData) {
222  return;
223  }
224 
225  if (!d->frames.contains(d->prefix)) {
226  if (oldFrameData) {
227  FrameData *newFd = 0;
228  if (!oldFrameData->frameSize.isEmpty()) {
229  const QString key = d->cacheId(oldFrameData, d->prefix);
230  newFd = FrameSvgPrivate::s_sharedFrames.value(key);
231  }
232 
233  // we need to put this in the cache if we didn't find it in the shared frames
234  // and we have a size; if we don't have a size, we'll catch it later
235  const bool cache = !newFd && !oldFrameData->frameSize.isEmpty();
236  if (newFd) {
237  newFd->ref(this);
238  } else {
239  newFd = new FrameData(*oldFrameData, this);
240  }
241 
242  d->frames.insert(d->prefix, newFd);
243 
244  if (cache) {
245  // we have to cache after inserting the frame since the cacheId requires the
246  // frame to be in the frames collection already
247  const QString key = d->cacheId(oldFrameData, d->prefix);
248  //kDebug() << this << " 1. inserting as" << key;
249 
250  FrameSvgPrivate::s_sharedFrames.insert(key, newFd);
251  }
252  } else {
253  // couldn't find anything useful, so we just create something here
254  // we don't have a size for it yet, so don't bother trying to share it just yet
255  FrameData *newFd = new FrameData(this);
256  d->frames.insert(d->prefix, newFd);
257  }
258 
259  d->updateSizes();
260  }
261 
262  if (!d->cacheAll) {
263  d->frames.remove(oldPrefix);
264  if (oldFrameData) {
265  if (oldFrameData->deref(this)) {
266  const QString oldKey = d->cacheId(oldFrameData, oldPrefix);
267  FrameSvgPrivate::s_sharedFrames.remove(oldKey);
268  delete oldFrameData;
269  }
270  }
271  }
272 
273  d->location = Floating;
274 }
275 
276 bool FrameSvg::hasElementPrefix(const QString & prefix) const
277 {
278  //for now it simply checks if a center element exists,
279  //because it could make sense for certain themes to not have all the elements
280  if (prefix.isEmpty()) {
281  return hasElement("center");
282  } else {
283  return hasElement(prefix % "-center");
284  }
285 }
286 
287 bool FrameSvg::hasElementPrefix(Plasma::Location location) const
288 {
289  switch (location) {
290  case TopEdge:
291  return hasElementPrefix("north");
292  break;
293  case BottomEdge:
294  return hasElementPrefix("south");
295  break;
296  case LeftEdge:
297  return hasElementPrefix("west");
298  break;
299  case RightEdge:
300  return hasElementPrefix("east");
301  break;
302  default:
303  return hasElementPrefix(QString());
304  break;
305  }
306 }
307 
308 QString FrameSvg::prefix()
309 {
310  if (d->prefix.isEmpty()) {
311  return d->prefix;
312  }
313 
314  return d->prefix.left(d->prefix.size() - 1);
315 }
316 
317 void FrameSvg::resizeFrame(const QSizeF &size)
318 {
319  if (imagePath().isEmpty()) {
320  return;
321  }
322 
323  if (size.isEmpty()) {
324  //kDebug() << "Invalid size" << size;
325  return;
326  }
327 
328  FrameData *fd = d->frames[d->prefix];
329  if (size == fd->frameSize) {
330  return;
331  }
332 
333  const QString oldKey = d->cacheId(fd, d->prefix);
334  const QSize currentSize = fd->frameSize;
335  fd->frameSize = size.toSize();
336  const QString newKey = d->cacheId(fd, d->prefix);
337  fd->frameSize = currentSize;
338 
339  //kDebug() << "looking for" << newKey;
340  FrameData *newFd = FrameSvgPrivate::s_sharedFrames.value(newKey);
341  if (newFd) {
342  //kDebug() << "FOUND IT!" << newFd->refcount;
343  // we've found a math, so insert that new one and ref it ..
344  newFd->ref(this);
345  d->frames.insert(d->prefix, newFd);
346 
347  //.. then deref the old one and if it's no longer used, get rid of it
348  if (fd->deref(this)) {
349  //const QString oldKey = d->cacheId(fd, d->prefix);
350  //kDebug() << "1. Removing it" << oldKey << fd->refcount;
351  FrameSvgPrivate::s_sharedFrames.remove(oldKey);
352  delete fd;
353  }
354 
355  return;
356  }
357 
358  if (fd->refcount() == 1) {
359  // we're the only user of it, let's remove it from the shared keys
360  // we don't want to deref it, however, as we'll still be using it
361  FrameSvgPrivate::s_sharedFrames.remove(oldKey);
362  } else {
363  // others are using it, but we wish to change its size. so deref it,
364  // then create a copy of it (we're automatically ref'd via the ctor),
365  // then insert it into our frames.
366  fd->deref(this);
367  fd = new FrameData(*fd, this);
368  d->frames.insert(d->prefix, fd);
369  }
370 
371  d->updateSizes();
372  fd->frameSize = size.toSize();
373  // we know it isn't in s_sharedFrames due to the check above, so insert it now
374  FrameSvgPrivate::s_sharedFrames.insert(newKey, fd);
375 }
376 
377 QSizeF FrameSvg::frameSize() const
378 {
379  QHash<QString, FrameData*>::const_iterator it = d->frames.constFind(d->prefix);
380 
381  if (it == d->frames.constEnd()) {
382  return QSize(-1, -1);
383  } else {
384  return d->frameSize(it.value());
385  }
386 }
387 
388 qreal FrameSvg::marginSize(const Plasma::MarginEdge edge) const
389 {
390  if (d->frames[d->prefix]->noBorderPadding) {
391  return .0;
392  }
393 
394  switch (edge) {
395  case Plasma::TopMargin:
396  return d->frames[d->prefix]->topMargin;
397  break;
398 
399  case Plasma::LeftMargin:
400  return d->frames[d->prefix]->leftMargin;
401  break;
402 
403  case Plasma::RightMargin:
404  return d->frames[d->prefix]->rightMargin;
405  break;
406 
407  //Plasma::BottomMargin
408  default:
409  return d->frames[d->prefix]->bottomMargin;
410  break;
411  }
412 }
413 
414 void FrameSvg::getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const
415 {
416  FrameData *frame = d->frames[d->prefix];
417 
418  if (frame->noBorderPadding) {
419  left = top = right = bottom = 0;
420  return;
421  }
422 
423  top = frame->topMargin;
424  left = frame->leftMargin;
425  right = frame->rightMargin;
426  bottom = frame->bottomMargin;
427 }
428 
429 QRectF FrameSvg::contentsRect() const
430 {
431  QSizeF size(frameSize());
432 
433  if (size.isValid()) {
434  QRectF rect(QPointF(0, 0), size);
435  FrameData *frame = d->frames[d->prefix];
436 
437  return rect.adjusted(frame->leftMargin, frame->topMargin,
438  -frame->rightMargin, -frame->bottomMargin);
439  } else {
440  return QRectF();
441  }
442 }
443 
444 QPixmap FrameSvg::alphaMask() const
445 {
446  //FIXME: the distinction between overlay and
447  return d->alphaMask();
448 }
449 
450 QRegion FrameSvg::mask() const
451 {
452  FrameData *frame = d->frames[d->prefix];
453  QString id = d->cacheId(frame, QString());
454  if (!frame->cachedMasks.contains(id)) {
455  //TODO: Implement a better way to cap the number of cached masks
456  if (frame->cachedMasks.count() > frame->MAX_CACHED_MASKS) {
457  frame->cachedMasks.clear();
458  }
459  frame->cachedMasks.insert(id, QRegion(QBitmap(d->alphaMask().alphaChannel().createMaskFromColor(Qt::black))));
460  }
461  return frame->cachedMasks[id];
462 }
463 
464 void FrameSvg::setCacheAllRenderedFrames(bool cache)
465 {
466  if (d->cacheAll && !cache) {
467  clearCache();
468  }
469 
470  d->cacheAll = cache;
471 }
472 
473 bool FrameSvg::cacheAllRenderedFrames() const
474 {
475  return d->cacheAll;
476 }
477 
478 void FrameSvg::clearCache()
479 {
480  FrameData *frame = d->frames[d->prefix];
481 
482  // delete all the frames that aren't this one
483  QMutableHashIterator<QString, FrameData*> it(d->frames);
484  while (it.hasNext()) {
485  FrameData *p = it.next().value();
486  if (frame != p) {
487  //TODO: should we clear from the Theme pixmap cache as well?
488  if (p->deref(this)) {
489  const QString key = d->cacheId(p, it.key());
490  FrameSvgPrivate::s_sharedFrames.remove(key);
491  p->cachedBackground = QPixmap();
492  }
493 
494  it.remove();
495  }
496  }
497 }
498 
499 QPixmap FrameSvg::framePixmap()
500 {
501  FrameData *frame = d->frames[d->prefix];
502  if (frame->cachedBackground.isNull()) {
503  d->generateBackground(frame);
504  if (frame->cachedBackground.isNull()) {
505  return QPixmap();
506  }
507  }
508 
509  return frame->cachedBackground;
510 }
511 
512 void FrameSvg::paintFrame(QPainter *painter, const QRectF &target, const QRectF &source)
513 {
514  FrameData *frame = d->frames[d->prefix];
515  if (frame->cachedBackground.isNull()) {
516  d->generateBackground(frame);
517  if (frame->cachedBackground.isNull()) {
518  return;
519  }
520  }
521 
522  painter->drawPixmap(target, frame->cachedBackground, source.isValid() ? source : target);
523 }
524 
525 void FrameSvg::paintFrame(QPainter *painter, const QPointF &pos)
526 {
527  FrameData *frame = d->frames[d->prefix];
528  if (frame->cachedBackground.isNull()) {
529  d->generateBackground(frame);
530  if (frame->cachedBackground.isNull()) {
531  return;
532  }
533  }
534 
535  painter->drawPixmap(pos, frame->cachedBackground);
536 }
537 
538 //#define DEBUG_FRAMESVG_CACHE
539 FrameSvgPrivate::~FrameSvgPrivate()
540 {
541 #ifdef DEBUG_FRAMESVG_CACHE
542  kDebug() << "*************" << q << q->imagePath() << "****************";
543 #endif
544 
545  QHashIterator<QString, FrameData *> it(frames);
546  while (it.hasNext()) {
547  it.next();
548  if (it.value()) {
549  // we remove all references from this widget to the frame, and delete it if we're the
550  // last user
551  if (it.value()->removeRefs(q)) {
552  const QString key = cacheId(it.value(), it.key());
553 #ifdef DEBUG_FRAMESVG_CACHE
554  kDebug() << "2. Removing it" << key << it.value() << it.value()->refcount() << s_sharedFrames.contains(key);
555 #endif
556  s_sharedFrames.remove(key);
557  delete it.value();
558  }
559 #ifdef DEBUG_FRAMESVG_CACHE
560  else {
561  kDebug() << "still shared:" << cacheId(it.value(), it.key()) << it.value() << it.value()->refcount() << it.value()->isUsed();
562  }
563  } else {
564  kDebug() << "lost our value for" << it.key();
565 #endif
566  }
567  }
568 
569 #ifdef DEBUG_FRAMESVG_CACHE
570  QHashIterator<QString, FrameData *> it2(s_sharedFrames);
571  int shares = 0;
572  while (it2.hasNext()) {
573  it2.next();
574  const int rc = it2.value()->refcount();
575  if (rc == 0) {
576  kDebug() << " LOST!" << it2.key() << rc << it2.value();// << it2.value()->references;
577  } else {
578  kDebug() << " " << it2.key() << rc << it2.value();
579  foreach (FrameSvg *data, it2.value()->references.keys()) {
580  kDebug( )<< " " << (void*)data << it2.value()->references[data];
581  }
582  shares += rc - 1;
583  }
584  }
585  kDebug() << "#####################################" << s_sharedFrames.count() << ", pixmaps saved:" << shares;
586 #endif
587 
588  frames.clear();
589 }
590 
591 QPixmap FrameSvgPrivate::alphaMask()
592 {
593  FrameData *frame = frames[prefix];
594  QString maskPrefix;
595 
596  if (q->hasElement("mask-" % prefix % "center")) {
597  maskPrefix = "mask-";
598  }
599 
600  if (maskPrefix.isNull()) {
601  if (frame->cachedBackground.isNull()) {
602  generateBackground(frame);
603  if (frame->cachedBackground.isNull()) {
604  return QPixmap();
605  }
606  }
607 
608  return frame->cachedBackground;
609  } else {
610  QString oldPrefix = prefix;
611 
612  // We are setting the prefix only temporary to generate
613  // the needed mask image
614  prefix = maskPrefix % oldPrefix;
615 
616  if (!frames.contains(prefix)) {
617  const QString key = cacheId(frame, prefix);
618  // see if we can find a suitable candidate in the shared frames
619  // if successful, ref and insert, otherwise create a new one
620  // and insert that into both the shared frames and our frames.
621  FrameData *maskFrame = s_sharedFrames.value(key);
622 
623  if (maskFrame) {
624  maskFrame->ref(q);
625  } else {
626  maskFrame = new FrameData(*frame, q);
627  s_sharedFrames.insert(key, maskFrame);
628  }
629 
630  frames.insert(prefix, maskFrame);
631  updateSizes();
632  }
633 
634  FrameData *maskFrame = frames[prefix];
635  if (maskFrame->cachedBackground.isNull() || maskFrame->frameSize != frameSize(frame)) {
636  const QString oldKey = cacheId(maskFrame, prefix);
637  maskFrame->frameSize = frameSize(frame).toSize();
638  const QString newKey = cacheId(maskFrame, prefix);
639  if (s_sharedFrames.contains(oldKey)) {
640  s_sharedFrames.remove(oldKey);
641  s_sharedFrames.insert(newKey, maskFrame);
642  }
643 
644  maskFrame->cachedBackground = QPixmap();
645 
646  generateBackground(maskFrame);
647  if (maskFrame->cachedBackground.isNull()) {
648  return QPixmap();
649  }
650  }
651 
652  prefix = oldPrefix;
653  return maskFrame->cachedBackground;
654  }
655 }
656 
657 void FrameSvgPrivate::generateBackground(FrameData *frame)
658 {
659  if (!frame->cachedBackground.isNull() || !q->hasElementPrefix(q->prefix())) {
660  return;
661  }
662 
663  const QString id = cacheId(frame, prefix);
664 
665  Theme *theme = Theme::defaultTheme();
666  bool frameCached = !frame->cachedBackground.isNull();
667  bool overlayCached = false;
668  const bool overlayAvailable = !prefix.startsWith(QLatin1String("mask-")) && q->hasElement(prefix % "overlay");
669  QPixmap overlay;
670  if (q->isUsingRenderingCache()) {
671  frameCached = theme->findInCache(id, frame->cachedBackground) && !frame->cachedBackground.isNull();
672 
673  if (overlayAvailable) {
674  overlayCached = theme->findInCache("overlay_" % id, overlay) && !overlay.isNull();
675  }
676  }
677 
678  if (!frameCached) {
679  generateFrameBackground(frame);
680  }
681 
682  //Overlays
683  QSize overlaySize;
684  QPoint actualOverlayPos = QPoint(0, 0);
685  if (overlayAvailable && !overlayCached) {
686  QPoint pos = QPoint(0, 0);
687  overlaySize = q->elementSize(prefix % "overlay");
688 
689  //Random pos, stretched and tiled are mutually exclusive
690  if (q->hasElement(prefix % "hint-overlay-random-pos")) {
691  actualOverlayPos = overlayPos;
692  } else if (q->hasElement(prefix % "hint-overlay-pos-right")) {
693  actualOverlayPos.setX(frame->frameSize.width() - overlaySize.width());
694  } else if (q->hasElement(prefix % "hint-overlay-pos-bottom")) {
695  actualOverlayPos.setY(frame->frameSize.height() - overlaySize.height());
696  //Stretched or Tiled?
697  } else if (q->hasElement(prefix % "hint-overlay-stretch")) {
698  overlaySize = frameSize(frame).toSize();
699  } else {
700  if (q->hasElement(prefix % "hint-overlay-tile-horizontal")) {
701  overlaySize.setWidth(frameSize(frame).width());
702  }
703  if (q->hasElement(prefix % "hint-overlay-tile-vertical")) {
704  overlaySize.setHeight(frameSize(frame).height());
705  }
706  }
707 
708  overlay = alphaMask();
709  QPainter overlayPainter(&overlay);
710  overlayPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
711  //Tiling?
712  if (q->hasElement(prefix % "hint-overlay-tile-horizontal") ||
713  q->hasElement(prefix % "hint-overlay-tile-vertical")) {
714 
715  QSize s = q->size();
716  q->resize(q->elementSize(prefix % "overlay"));
717 
718  overlayPainter.drawTiledPixmap(QRect(QPoint(0,0), overlaySize), q->pixmap(prefix % "overlay"));
719  q->resize(s);
720  } else {
721  q->paint(&overlayPainter, QRect(actualOverlayPos, overlaySize), prefix % "overlay");
722  }
723 
724  overlayPainter.end();
725  }
726 
727  if (!frameCached) {
728  cacheFrame(prefix, frame->cachedBackground, overlayCached ? overlay : QPixmap());
729  }
730 
731  if (!overlay.isNull()) {
732  QPainter p(&frame->cachedBackground);
733  p.setCompositionMode(QPainter::CompositionMode_SourceOver);
734  p.drawPixmap(actualOverlayPos, overlay, QRect(actualOverlayPos, overlaySize));
735  }
736 }
737 
738 void FrameSvgPrivate::generateFrameBackground(FrameData *frame)
739 {
740  //kDebug() << "generating background";
741  const QSizeF size = frameSize(frame);
742  const int topWidth = q->elementSize(prefix % "top").width();
743  const int leftHeight = q->elementSize(prefix % "left").height();
744  const int topOffset = 0;
745  const int leftOffset = 0;
746 
747 
748  if (!size.isValid()) {
749  kDebug() << "Invalid frame size" << size;
750  return;
751  }
752  if (size.width() >= MAX_FRAME_SIZE || size.height() >= MAX_FRAME_SIZE) {
753  kWarning() << "Not generating frame background for a size whose width or height is more than" << MAX_FRAME_SIZE << size;
754  return;
755  }
756 
757  const int contentWidth = size.width() - frame->leftWidth - frame->rightWidth;
758  const int contentHeight = size.height() - frame->topHeight - frame->bottomHeight;
759  int contentTop = 0;
760  int contentLeft = 0;
761  int rightOffset = contentWidth;
762  int bottomOffset = contentHeight;
763 
764  frame->cachedBackground = QPixmap(frame->leftWidth + contentWidth + frame->rightWidth,
765  frame->topHeight + contentHeight + frame->bottomHeight);
766  frame->cachedBackground.fill(Qt::transparent);
767  QPainter p(&frame->cachedBackground);
768  p.setCompositionMode(QPainter::CompositionMode_Source);
769  p.setRenderHint(QPainter::SmoothPixmapTransform);
770 
771  //CENTER
772  if (frame->tileCenter) {
773  if (contentHeight > 0 && contentWidth > 0) {
774  const int centerTileHeight = q->elementSize(prefix % "center").height();
775  const int centerTileWidth = q->elementSize(prefix % "center").width();
776  QPixmap center(centerTileWidth, centerTileHeight);
777  center.fill(Qt::transparent);
778 
779  {
780  QPainter centerPainter(&center);
781  centerPainter.setCompositionMode(QPainter::CompositionMode_Source);
782  q->paint(&centerPainter, QRect(QPoint(0, 0), q->elementSize(prefix % "center")), prefix % "center");
783  }
784 
785  if (frame->composeOverBorder) {
786  p.drawTiledPixmap(QRect(QPoint(0, 0), size.toSize()), center);
787  } else {
788  p.drawTiledPixmap(QRect(frame->leftWidth, frame->topHeight,
789  contentWidth, contentHeight), center);
790  }
791  }
792  } else {
793  if (contentHeight > 0 && contentWidth > 0) {
794  if (frame->composeOverBorder) {
795  q->paint(&p, QRect(QPoint(0, 0), size.toSize()),
796  prefix % "center");
797  } else {
798  q->paint(&p, QRect(frame->leftWidth, frame->topHeight,
799  contentWidth, contentHeight),
800  prefix % "center");
801  }
802  }
803  }
804 
805  if (frame->composeOverBorder) {
806  p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
807  p.drawPixmap(QRect(QPoint(0, 0), size.toSize()), alphaMask());
808  p.setCompositionMode(QPainter::CompositionMode_SourceOver);
809  }
810 
811  if (frame->enabledBorders & FrameSvg::LeftBorder && q->hasElement(prefix % "left")) {
812  rightOffset += frame->leftWidth;
813  }
814 
815  // Corners
816  if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(prefix % "top")) {
817  contentTop = frame->topHeight;
818  bottomOffset += frame->topHeight;
819 
820  if (q->hasElement(prefix % "topleft") && frame->enabledBorders & FrameSvg::LeftBorder) {
821  q->paint(&p, QRect(leftOffset, topOffset, frame->leftWidth, frame->topHeight), prefix % "topleft");
822 
823  contentLeft = frame->leftWidth;
824  }
825 
826  if (q->hasElement(prefix % "topright") && frame->enabledBorders & FrameSvg::RightBorder) {
827  q->paint(&p, QRect(rightOffset, topOffset, frame->rightWidth, frame->topHeight), prefix % "topright");
828  }
829  }
830 
831  if (frame->enabledBorders & FrameSvg::BottomBorder && q->hasElement(prefix % "bottom")) {
832  if (q->hasElement(prefix % "bottomleft") && frame->enabledBorders & FrameSvg::LeftBorder) {
833  q->paint(&p, QRect(leftOffset, bottomOffset, frame->leftWidth, frame->bottomHeight), prefix % "bottomleft");
834 
835  contentLeft = frame->leftWidth;
836  }
837 
838  if (frame->enabledBorders & FrameSvg::RightBorder && q->hasElement(prefix % "bottomright")) {
839  q->paint(&p, QRect(rightOffset, bottomOffset, frame->rightWidth, frame->bottomHeight), prefix % "bottomright");
840  }
841  }
842 
843  // Sides
844  if (frame->stretchBorders) {
845  if (frame->enabledBorders & FrameSvg::LeftBorder || frame->enabledBorders & FrameSvg::RightBorder) {
846  if (q->hasElement(prefix % "left") &&
847  frame->enabledBorders & FrameSvg::LeftBorder &&
848  contentHeight > 0) {
849  q->paint(&p, QRect(leftOffset, contentTop, frame->leftWidth, contentHeight), prefix % "left");
850  }
851 
852  if (q->hasElement(prefix % "right") &&
853  frame->enabledBorders & FrameSvg::RightBorder &&
854  contentHeight > 0) {
855  q->paint(&p, QRect(rightOffset, contentTop, frame->rightWidth, contentHeight), prefix % "right");
856  }
857  }
858 
859  if (frame->enabledBorders & FrameSvg::TopBorder || frame->enabledBorders & FrameSvg::BottomBorder) {
860  if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(prefix % "top") &&
861  contentWidth > 0) {
862  q->paint(&p, QRect(contentLeft, topOffset, contentWidth, frame->topHeight), prefix % "top");
863  }
864 
865  if (frame->enabledBorders & FrameSvg::BottomBorder && q->hasElement(prefix % "bottom") &&
866  contentWidth > 0) {
867  q->paint(&p, QRect(contentLeft, bottomOffset, contentWidth, frame->bottomHeight), prefix % "bottom");
868  }
869  }
870  } else {
871  if (frame->enabledBorders & FrameSvg::LeftBorder && q->hasElement(prefix % "left")
872  && leftHeight > 0 && frame->leftWidth > 0) {
873  QPixmap left(frame->leftWidth, leftHeight);
874  left.fill(Qt::transparent);
875 
876  QPainter sidePainter(&left);
877  sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
878  q->paint(&sidePainter, QRect(QPoint(0, 0), left.size()), prefix % "left");
879 
880  p.drawTiledPixmap(QRect(leftOffset, contentTop, frame->leftWidth, contentHeight), left);
881  }
882 
883  if (frame->enabledBorders & FrameSvg::RightBorder && q->hasElement(prefix % "right") &&
884  leftHeight > 0 && frame->rightWidth > 0) {
885  QPixmap right(frame->rightWidth, leftHeight);
886  right.fill(Qt::transparent);
887 
888  QPainter sidePainter(&right);
889  sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
890  q->paint(&sidePainter, QRect(QPoint(0, 0), right.size()), prefix % "right");
891 
892  p.drawTiledPixmap(QRect(rightOffset, contentTop, frame->rightWidth, contentHeight), right);
893  }
894 
895  if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(prefix % "top")
896  && topWidth > 0 && frame->topHeight > 0) {
897  QPixmap top(topWidth, frame->topHeight);
898  top.fill(Qt::transparent);
899 
900  QPainter sidePainter(&top);
901  sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
902  q->paint(&sidePainter, QRect(QPoint(0, 0), top.size()), prefix % "top");
903 
904  p.drawTiledPixmap(QRect(contentLeft, topOffset, contentWidth, frame->topHeight), top);
905  }
906 
907  if (frame->enabledBorders & FrameSvg::BottomBorder && q->hasElement(prefix % "bottom")
908  && topWidth > 0 && frame->bottomHeight > 0) {
909  QPixmap bottom(topWidth, frame->bottomHeight);
910  bottom.fill(Qt::transparent);
911 
912  QPainter sidePainter(&bottom);
913  sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
914  q->paint(&sidePainter, QRect(QPoint(0, 0), bottom.size()), prefix % "bottom");
915 
916  p.drawTiledPixmap(QRect(contentLeft, bottomOffset, contentWidth, frame->bottomHeight), bottom);
917  }
918  }
919 
920 }
921 
922 QString FrameSvgPrivate::cacheId(FrameData *frame, const QString &prefixToSave) const
923 {
924  const QSize size = frameSize(frame).toSize();
925  const QLatin1Char s('_');
926  return QString::number(frame->enabledBorders) % s % QString::number(size.width()) % s % QString::number(size.height()) % s % prefixToSave % s % q->imagePath();
927 }
928 
929 void FrameSvgPrivate::cacheFrame(const QString &prefixToSave, const QPixmap &background, const QPixmap &overlay)
930 {
931  if (!q->isUsingRenderingCache()) {
932  return;
933  }
934 
935  //insert background
936  FrameData *frame = frames.value(prefixToSave);
937 
938  if (!frame) {
939  return;
940  }
941 
942  const QString id = cacheId(frame, prefixToSave);
943 
944  //kDebug()<<"Saving to cache frame"<<id;
945 
946  Theme::defaultTheme()->insertIntoCache(id, background, QString::number((qint64)q, 16) % prefixToSave);
947 
948  if (!overlay.isNull()) {
949  //insert overlay
950  Theme::defaultTheme()->insertIntoCache("overlay_" % id, overlay, QString::number((qint64)q, 16) % prefixToSave % "overlay");
951  }
952 }
953 
954 void FrameSvgPrivate::updateSizes() const
955 {
956  //kDebug() << "!!!!!!!!!!!!!!!!!!!!!! updating sizes" << prefix;
957  FrameData *frame = frames[prefix];
958  Q_ASSERT(frame);
959 
960  QSize s = q->size();
961  q->resize();
962  frame->cachedBackground = QPixmap();
963 
964  if (frame->enabledBorders & FrameSvg::TopBorder) {
965  frame->topHeight = q->elementSize(prefix % "top").height();
966 
967  if (q->hasElement(prefix % "hint-top-margin")) {
968  frame->topMargin = q->elementSize(prefix % "hint-top-margin").height();
969  } else {
970  frame->topMargin = frame->topHeight;
971  }
972  } else {
973  frame->topMargin = frame->topHeight = 0;
974  }
975 
976  if (frame->enabledBorders & FrameSvg::LeftBorder) {
977  frame->leftWidth = q->elementSize(prefix % "left").width();
978 
979  if (q->hasElement(prefix % "hint-left-margin")) {
980  frame->leftMargin = q->elementSize(prefix % "hint-left-margin").width();
981  } else {
982  frame->leftMargin = frame->leftWidth;
983  }
984  } else {
985  frame->leftMargin = frame->leftWidth = 0;
986  }
987 
988  if (frame->enabledBorders & FrameSvg::RightBorder) {
989  frame->rightWidth = q->elementSize(prefix % "right").width();
990 
991  if (q->hasElement(prefix % "hint-right-margin")) {
992  frame->rightMargin = q->elementSize(prefix % "hint-right-margin").width();
993  } else {
994  frame->rightMargin = frame->rightWidth;
995  }
996  } else {
997  frame->rightMargin = frame->rightWidth = 0;
998  }
999 
1000  if (frame->enabledBorders & FrameSvg::BottomBorder) {
1001  frame->bottomHeight = q->elementSize(prefix % "bottom").height();
1002 
1003  if (q->hasElement(prefix % "hint-bottom-margin")) {
1004  frame->bottomMargin = q->elementSize(prefix % "hint-bottom-margin").height();
1005  } else {
1006  frame->bottomMargin = frame->bottomHeight;
1007  }
1008  } else {
1009  frame->bottomMargin = frame->bottomHeight = 0;
1010  }
1011 
1012  frame->composeOverBorder = (q->hasElement(prefix % "hint-compose-over-border") &&
1013  q->hasElement("mask-" % prefix % "center"));
1014 
1015  //since it's rectangular, topWidth and bottomWidth must be the same
1016  //the ones that don't have a prefix is for retrocompatibility
1017  frame->tileCenter = (q->hasElement("hint-tile-center") || q->hasElement(prefix % "hint-tile-center"));
1018  frame->noBorderPadding = (q->hasElement("hint-no-border-padding") || q->hasElement(prefix % "hint-no-border-padding"));
1019  frame->stretchBorders = (q->hasElement("hint-stretch-borders") || q->hasElement(prefix % "hint-stretch-borders"));
1020  q->resize(s);
1021 }
1022 
1023 void FrameSvgPrivate::updateNeeded()
1024 {
1025  q->clearCache();
1026  updateSizes();
1027 }
1028 
1029 void FrameSvgPrivate::updateAndSignalSizes()
1030 {
1031  updateSizes();
1032  emit q->repaintNeeded();
1033 }
1034 
1035 QSizeF FrameSvgPrivate::frameSize(FrameData *frame) const
1036 {
1037  if (!frame->frameSize.isValid()) {
1038  updateSizes();
1039  frame->frameSize = q->size();
1040  }
1041 
1042  return frame->frameSize;
1043 }
1044 
1045 void FrameData::ref(FrameSvg *svg)
1046 {
1047  references[svg] = references[svg] + 1;
1048  //kDebug() << this << svg << references[svg];
1049 }
1050 
1051 bool FrameData::deref(FrameSvg *svg)
1052 {
1053  references[svg] = references[svg] - 1;
1054  //kDebug() << this << svg << references[svg];
1055  if (references[svg] < 1) {
1056  references.remove(svg);
1057  }
1058 
1059  return references.isEmpty();
1060 }
1061 
1062 bool FrameData::removeRefs(FrameSvg *svg)
1063 {
1064  references.remove(svg);
1065  return references.isEmpty();
1066 }
1067 
1068 bool FrameData::isUsed() const
1069 {
1070  return !references.isEmpty();
1071 }
1072 
1073 int FrameData::refcount() const
1074 {
1075  return references.count();
1076 }
1077 
1078 } // Plasma namespace
1079 
1080 #include "framesvg.moc"
Plasma::FrameSvg::paintFrame
Q_INVOKABLE void paintFrame(QPainter *painter, const QRectF &target, const QRectF &source=QRectF())
Paints the loaded SVG with the elements that represents the border.
Definition: framesvg.cpp:512
Plasma::FrameSvg::mask
Q_INVOKABLE QRegion mask() const
Returns a mask that tightly contains the fully opaque areas of the svg.
Definition: framesvg.cpp:450
Plasma::FrameSvg::LeftBorder
Definition: framesvg.h:91
Plasma::FrameSvg::setCacheAllRenderedFrames
Q_INVOKABLE void setCacheAllRenderedFrames(bool cache)
Sets whether saving all the rendered prefixes in a cache or not.
Definition: framesvg.cpp:464
Plasma::Svg::repaintNeeded
void repaintNeeded()
Emitted whenever the SVG data has changed in such a way that a repaint is required.
Plasma::FrameSvg::NoBorder
Definition: framesvg.h:88
Plasma::FrameSvg::~FrameSvg
~FrameSvg()
Definition: framesvg.cpp:57
Plasma::MarginEdge
MarginEdge
Definition: plasma.h:236
Plasma::FrameSvg::contentsRect
Q_INVOKABLE QRectF contentsRect() const
Definition: framesvg.cpp:429
Plasma::Svg::size
QSize size() const
The size of the SVG.
theme.h
Plasma::Svg::imagePath
QString imagePath() const
The SVG file to render.
QObject
Plasma::FrameSvg::FrameSvg
FrameSvg(QObject *parent=0)
Constructs a new FrameSvg that paints the proper named subelements as borders.
Definition: framesvg.cpp:49
Plasma::Svg::hasElement
Q_INVOKABLE bool hasElement(const QString &elementId) const
Check whether an element exists in the loaded SVG.
Definition: svg.cpp:732
Plasma::FrameSvg::resizeFrame
Q_INVOKABLE void resizeFrame(const QSizeF &size)
Resize the frame maintaining the same border size.
Definition: framesvg.cpp:317
Plasma::FrameSvg::alphaMask
QPixmap alphaMask() const
Definition: framesvg.cpp:444
Plasma::Location
Location
The Location enumeration describes where on screen an element, such as an Applet or its managing cont...
Definition: plasma.h:108
Plasma::FrameSvg::TopBorder
Definition: framesvg.h:89
Plasma::FrameSvg::hasElementPrefix
Q_INVOKABLE bool hasElementPrefix(const QString &prefix) const
Definition: framesvg.cpp:276
Plasma::FrameSvg::prefix
Q_INVOKABLE QString prefix()
Returns the prefix for SVG elements of the FrameSvg.
Definition: framesvg.cpp:308
Plasma::MAX_FRAME_SIZE
static const int MAX_FRAME_SIZE
Definition: framesvg.cpp:47
applet.h
Plasma::FrameSvg::setImagePath
Q_INVOKABLE void setImagePath(const QString &path)
Loads a new Svg.
Definition: framesvg.cpp:62
Plasma::TopEdge
Along the top of the screen.
Definition: plasma.h:114
Plasma::Svg::setContainsMultipleImages
void setContainsMultipleImages(bool multiple)
Set whether the SVG contains a single image or multiple ones.
Definition: svg.cpp:769
Plasma::FrameSvg::clearCache
Q_INVOKABLE void clearCache()
Deletes the internal cache freeing memory: use this if you want to switch the rendered element and yo...
Definition: framesvg.cpp:478
Plasma::FrameSvg::BottomBorder
Definition: framesvg.h:90
Plasma::TopMargin
The top margin.
Definition: plasma.h:237
Plasma::RightMargin
The right margin.
Definition: plasma.h:240
Plasma::Floating
Free floating.
Definition: plasma.h:109
Plasma::FrameSvg::cacheAllRenderedFrames
Q_INVOKABLE bool cacheAllRenderedFrames() const
Definition: framesvg.cpp:473
Plasma::BottomEdge
Along the bottom of the screen.
Definition: plasma.h:115
Plasma::FrameSvg::framePixmap
Q_INVOKABLE QPixmap framePixmap()
Returns a pixmap of the SVG represented by this object.
Definition: framesvg.cpp:499
Plasma::Theme::defaultTheme
static Theme * defaultTheme()
Singleton pattern accessor.
Definition: theme.cpp:544
Plasma::FrameSvg::enabledBorders
EnabledBorders enabledBorders() const
Convenience method to get the enabled borders.
Plasma::FrameSvg::RightBorder
Definition: framesvg.h:92
framesvg.h
Plasma::FrameSvg::frameSize
Q_INVOKABLE QSizeF frameSize() const
Definition: framesvg.cpp:377
Plasma::Theme::insertIntoCache
void insertIntoCache(const QString &key, const QPixmap &pix)
Insert specified pixmap into the cache.
Definition: theme.cpp:1054
Plasma::FrameSvg::setElementPrefix
Q_INVOKABLE void setElementPrefix(Plasma::Location location)
Sets the prefix (.
Definition: framesvg.cpp:184
Plasma::FrameSvg::getMargins
Q_INVOKABLE void getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const
Convenience method that extracts the size of the four margins in the four output parameters.
Definition: framesvg.cpp:414
Plasma::RightEdge
Along the right side of the screen.
Definition: plasma.h:117
Plasma::FrameSvg::marginSize
Q_INVOKABLE qreal marginSize(const Plasma::MarginEdge edge) const
Returns the margin size given the margin edge we want.
Definition: framesvg.cpp:388
Plasma::LeftEdge
Along the left side of the screen.
Definition: plasma.h:116
Plasma::Svg
A theme aware image-centric SVG class.
Definition: svg.h:56
Plasma::FrameSvg::setEnabledBorders
void setEnabledBorders(const EnabledBorders borders)
Sets what borders should be painted.
Definition: framesvg.cpp:119
Plasma::LeftMargin
The left margin.
Definition: plasma.h:239
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:48:33 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

Plasma

Skip menu "Plasma"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs API Reference

Skip menu "kdelibs API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  • kjsembed
  •   WTF
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Nepomuk-Core
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal