1 /***************************************************************************
2 * Copyright (C) 2005-2009 by Rajko Albrecht ral@alwins-world.de *
3 * http://kdesvn.alwins-world.de/ *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, 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 General Public License *
16 * 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 /* This file was part of KCachegrind.
21 Copyright (C) 2002, 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
22 Adapted for the needs of kdesvn by Rajko Albrecht <ral@alwins-world.de>
23 */
24 /*
25 * A Widget for visualizing hierarchical metrics as areas.
26 * The API is similar to QListView.
27 */
28
29 #include "drawparams.h"
30
31 #include <math.h>
32
33 #include <QFontDatabase>
34 #include <QPainter>
35
36 // set this to 1 to enable debug output
37 #define DEBUG_DRAWING 0
38 #define MAX_FIELD 12
39
40 //
41 // StoredDrawParams
42 //
StoredDrawParams()43 StoredDrawParams::StoredDrawParams()
44 : _backColor(Qt::white)
45 , _selected(false)
46 , _current(false)
47 , _shaded(true)
48 , _rotated(false)
49 , _drawFrame(false)
50 {
51 // field array has size 0
52 }
53
StoredDrawParams(const QColor & c,bool selected,bool current)54 StoredDrawParams::StoredDrawParams(const QColor &c,
55 bool selected,
56 bool current)
57 : _backColor(c)
58 , _selected(selected)
59 , _current(current)
60 , _shaded(true)
61 , _rotated(false)
62 , _drawFrame(true)
63 {
64 // field array has size 0
65 }
66
text(int f) const67 QString StoredDrawParams::text(int f) const
68 {
69 if ((f < 0) || (f >= _field.size())) {
70 return QString();
71 }
72
73 return _field[f].text;
74 }
75
pixmap(int f) const76 QPixmap StoredDrawParams::pixmap(int f) const
77 {
78 if ((f < 0) || (f >= _field.size())) {
79 return QPixmap();
80 }
81
82 return _field[f].pix;
83 }
84
position(int f) const85 DrawParams::Position StoredDrawParams::position(int f) const
86 {
87 if ((f < 0) || (f >= _field.size())) {
88 return Default;
89 }
90
91 return _field[f].pos;
92 }
93
maxLines(int f) const94 int StoredDrawParams::maxLines(int f) const
95 {
96 if ((f < 0) || (f >= _field.size())) {
97 return 0;
98 }
99
100 return _field[f].maxLines;
101 }
102
font() const103 QFont StoredDrawParams::font() const
104 {
105 return QFontDatabase::systemFont(QFontDatabase::FixedFont);
106 }
107
ensureField(int f)108 void StoredDrawParams::ensureField(int f)
109 {
110 static Field *def = nullptr;
111 if (!def) {
112 def = new Field();
113 def->pos = Default;
114 def->maxLines = 0;
115 }
116
117 if (f < 0 || f >= MAX_FIELD) {
118 return;
119 }
120
121 while (_field.size() < f + 1) {
122 _field.append(*def);
123 }
124 }
125
setField(int f,const QString & t,const QPixmap & pm,Position p,int maxLines)126 void StoredDrawParams::setField(int f, const QString &t, const QPixmap &pm,
127 Position p, int maxLines)
128 {
129 if (f < 0 || f >= MAX_FIELD) {
130 return;
131 }
132 ensureField(f);
133
134 _field[f].text = t;
135 _field[f].pix = pm;
136 _field[f].pos = p;
137 _field[f].maxLines = maxLines;
138 }
139
setText(int f,const QString & t)140 void StoredDrawParams::setText(int f, const QString &t)
141 {
142 if (f < 0 || f >= MAX_FIELD) {
143 return;
144 }
145 ensureField(f);
146
147 _field[f].text = t;
148 }
149
setPixmap(int f,QPixmap pm)150 void StoredDrawParams::setPixmap(int f, QPixmap pm)
151 {
152 if (f < 0 || f >= MAX_FIELD) {
153 return;
154 }
155 ensureField(f);
156
157 _field[f].pix = pm;
158 }
159
setPosition(int f,Position p)160 void StoredDrawParams::setPosition(int f, Position p)
161 {
162 if (f < 0 || f >= MAX_FIELD) {
163 return;
164 }
165 ensureField(f);
166
167 _field[f].pos = p;
168 }
169
setMaxLines(int f,int m)170 void StoredDrawParams::setMaxLines(int f, int m)
171 {
172 if (f < 0 || f >= MAX_FIELD) {
173 return;
174 }
175 ensureField(f);
176
177 _field[f].maxLines = m;
178 }
179
180 //
181 // RectDrawing
182 //
183
RectDrawing(const QRect & r)184 RectDrawing::RectDrawing(const QRect &r)
185 : _fm(nullptr)
186 , _dp(nullptr)
187 {
188 setRect(r);
189 }
190
~RectDrawing()191 RectDrawing::~RectDrawing()
192 {
193 delete _fm;
194 delete _dp;
195 }
196
drawParams()197 DrawParams *RectDrawing::drawParams()
198 {
199 if (!_dp) {
200 _dp = new StoredDrawParams();
201 }
202
203 return _dp;
204 }
205
setDrawParams(DrawParams * dp)206 void RectDrawing::setDrawParams(DrawParams *dp)
207 {
208 if (_dp) {
209 delete _dp;
210 }
211 _dp = dp;
212 }
213
setRect(QRect r)214 void RectDrawing::setRect(QRect r)
215 {
216 _rect = r;
217
218 _usedTopLeft = 0;
219 _usedTopCenter = 0;
220 _usedTopRight = 0;
221 _usedBottomLeft = 0;
222 _usedBottomCenter = 0;
223 _usedBottomRight = 0;
224
225 _fontHeight = 0;
226 }
227
remainingRect(DrawParams * dp)228 QRect RectDrawing::remainingRect(DrawParams *dp)
229 {
230 if (!dp) {
231 dp = drawParams();
232 }
233
234 if ((_usedTopLeft > 0) ||
235 (_usedTopCenter > 0) ||
236 (_usedTopRight > 0)) {
237 if (dp->rotated()) {
238 _rect.setLeft(_rect.left() + _fontHeight);
239 } else {
240 _rect.setTop(_rect.top() + _fontHeight);
241 }
242 }
243
244 if ((_usedBottomLeft > 0) ||
245 (_usedBottomCenter > 0) ||
246 (_usedBottomRight > 0)) {
247 if (dp->rotated()) {
248 _rect.setRight(_rect.right() - _fontHeight);
249 } else {
250 _rect.setBottom(_rect.bottom() - _fontHeight);
251 }
252 }
253 return _rect;
254 }
255
drawBack(QPainter * p,DrawParams * dp)256 void RectDrawing::drawBack(QPainter *p, DrawParams *dp)
257 {
258 if (!dp) {
259 dp = drawParams();
260 }
261 if (_rect.width() <= 0 || _rect.height() <= 0) {
262 return;
263 }
264
265 QRect r = _rect;
266 QColor normal = dp->backColor();
267 if (dp->selected()) {
268 normal = normal.lighter();
269 }
270 bool isCurrent = dp->current();
271
272 if (dp->drawFrame() || isCurrent) {
273 // 3D raised/sunken frame effect...
274 QColor high = normal.lighter();
275 QColor low = normal.darker();
276 p->setPen(isCurrent ? low : high);
277 p->drawLine(r.left(), r.top(), r.right(), r.top());
278 p->drawLine(r.left(), r.top(), r.left(), r.bottom());
279 p->setPen(isCurrent ? high : low);
280 p->drawLine(r.right(), r.top(), r.right(), r.bottom());
281 p->drawLine(r.left(), r.bottom(), r.right(), r.bottom());
282 r.setRect(r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2);
283 }
284 if (r.width() <= 0 || r.height() <= 0) {
285 return;
286 }
287 if (dp->shaded() && (r.width() > 0 && r.height() > 0)) {
288 // adjustment for drawRect semantic in Qt4: decrement height/width
289 r.setRect(r.x(), r.y(), r.width() - 1, r.height() - 1);
290
291 // some shading
292 bool goDark = qGray(normal.rgb()) > 128;
293 int rBase, gBase, bBase;
294 normal.getRgb(&rBase, &gBase, &bBase);
295 p->setBrush(Qt::NoBrush);
296
297 // shade parameters:
298 int d = 7;
299 double factor = 0.1, forth = 0.7, back1 = 0.9, toBack2 = .7, back2 = 0.97;
300
301 // coefficient corrections because of rectangle size
302 int s = r.width();
303 if (s > r.height()) {
304 s = r.height();
305 }
306 if (s < 100) {
307 forth -= .3 * (100 - s) / 100;
308 back1 -= .2 * (100 - s) / 100;
309 back2 -= .02 * (100 - s) / 100;
310 }
311
312 // maximal color difference
313 int rDiff = goDark ? -rBase / d : (255 - rBase) / d;
314 int gDiff = goDark ? -gBase / d : (255 - gBase) / d;
315 int bDiff = goDark ? -bBase / d : (255 - bBase) / d;
316
317 QColor shadeColor;
318 while (factor < .95 && (r.width() >= 0 && r.height() >= 0)) {
319 shadeColor.setRgb(qRound(rBase + factor * rDiff),
320 qRound(gBase + factor * gDiff),
321 qRound(bBase + factor * bDiff));
322 p->setPen(shadeColor);
323 p->drawRect(r);
324 r.setRect(r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2);
325 factor = 1.0 - ((1.0 - factor) * forth);
326 }
327
328 // and back (1st half)
329 while (factor > toBack2 && (r.width() >= 0 && r.height() >= 0)) {
330 shadeColor.setRgb(qRound(rBase + factor * rDiff),
331 qRound(gBase + factor * gDiff),
332 qRound(bBase + factor * bDiff));
333 p->setPen(shadeColor);
334 p->drawRect(r);
335 r.setRect(r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2);
336 factor = 1.0 - ((1.0 - factor) / back1);
337 }
338
339 // and back (2nd half)
340 while (factor > .01 && (r.width() >= 0 && r.height() >= 0)) {
341 shadeColor.setRgb(qRound(rBase + factor * rDiff),
342 qRound(gBase + factor * gDiff),
343 qRound(bBase + factor * bDiff));
344 p->setPen(shadeColor);
345 p->drawRect(r);
346 r.setRect(r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2);
347 factor = factor * back2;
348 }
349
350 normal = shadeColor;
351 // for filling, width and height has to be incremented again
352 r.setRect(r.x(), r.y(), r.width() + 1, r.height() + 1);
353 }
354
355 // fill inside
356 p->fillRect(r, normal);
357 }
358
359 /* Helper for drawField
360 * Find a line break position in a string, given a font and maximum width
361 *
362 * Returns the actually used width, and sets <breakPos>
363 */
364 static
findBreak(int & breakPos,QString text,QFontMetrics * fm,int maxWidth)365 int findBreak(int &breakPos, QString text, QFontMetrics *fm, int maxWidth)
366 {
367 int usedWidth;
368
369 // does full text fit?
370 breakPos = text.length();
371 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
372 usedWidth = fm->horizontalAdvance(text);
373 #else
374 usedWidth = fm->width(text);
375 #endif
376 if (usedWidth < maxWidth) {
377 return usedWidth;
378 }
379
380 // now lower breakPos until best position is found.
381 // first by binary search, resulting in a position a little bit too large
382 int bottomPos = 0;
383 while (qAbs(maxWidth - usedWidth) > 3 * fm->maxWidth()) {
384 int halfPos = (bottomPos + breakPos) / 2;
385 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
386 int halfWidth = fm->horizontalAdvance(text, halfPos);
387 #else
388 int halfWidth = fm->width(text, halfPos);
389 #endif
390 if (halfWidth < maxWidth) {
391 bottomPos = halfPos;
392 } else {
393 breakPos = halfPos;
394 usedWidth = halfWidth;
395 }
396 }
397
398 // final position by taking break boundaries into account.
399 // possible break boundaries are changing char categories but not middle of "Aa"
400 QChar::Category lastCat, cat;
401 int pos = breakPos;
402 lastCat = text[pos - 1].category();
403 // at minimum 2 chars before break
404 while (pos > 2) {
405 pos--;
406 cat = text[pos - 1].category();
407 if (cat == lastCat) {
408 continue;
409 }
410
411 // "Aa" has not a possible break inbetween
412 if ((cat == QChar::Letter_Uppercase) &&
413 (lastCat == QChar::Letter_Lowercase)) {
414 lastCat = cat;
415 continue;
416 }
417 lastCat = cat;
418
419 breakPos = pos;
420 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
421 usedWidth = fm->horizontalAdvance(text, breakPos);
422 #else
423 usedWidth = fm->width(text, breakPos);
424 #endif
425 if (usedWidth < maxWidth) {
426 break;
427 }
428 }
429 return usedWidth;
430 }
431
432 /* Helper for drawField
433 * Find last line break position in a string from backwards,
434 * given a font and maximum width
435 *
436 * Returns the actually used width, and sets <breakPos>
437 */
438 static
findBreakBackwards(int & breakPos,QString text,QFontMetrics * fm,int maxWidth)439 int findBreakBackwards(int &breakPos, QString text, QFontMetrics *fm, int maxWidth)
440 {
441 int usedWidth;
442
443 // does full text fit?
444 breakPos = 0;
445 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
446 usedWidth = fm->horizontalAdvance(text);
447 #else
448 usedWidth = fm->width(text);
449 #endif
450 if (usedWidth < maxWidth) {
451 return usedWidth;
452 }
453
454 // now raise breakPos until best position is found.
455 // first by binary search, resulting in a position a little bit too small
456 int topPos = text.length();
457 while (qAbs(maxWidth - usedWidth) > 3 * fm->maxWidth()) {
458 int halfPos = (breakPos + topPos) / 2;
459 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
460 int halfWidth = fm->horizontalAdvance(text.mid(halfPos));
461 #else
462 int halfWidth = fm->width(text.mid(halfPos));
463 #endif
464 if (halfWidth < maxWidth) {
465 breakPos = halfPos;
466 usedWidth = halfWidth;
467 } else {
468 topPos = halfPos;
469 }
470 }
471
472 // final position by taking break boundaries into account.
473 // possible break boundaries are changing char categories but not middle of "Aa"
474 QChar::Category lastCat, cat;
475 int pos = breakPos;
476 lastCat = text[pos].category();
477 // at minimum 2 chars before break
478 while (pos < text.length() - 2) {
479 pos++;
480 cat = text[pos].category();
481 if (cat == lastCat) {
482 continue;
483 }
484
485 // "Aa" has not a possible break inbetween
486 if ((lastCat == QChar::Letter_Uppercase) &&
487 (cat == QChar::Letter_Lowercase)) {
488 lastCat = cat;
489 continue;
490 }
491 lastCat = cat;
492
493 breakPos = pos;
494 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
495 usedWidth = fm->horizontalAdvance(text.mid(breakPos));
496 #else
497 usedWidth = fm->width(text.mid(breakPos));
498 #endif
499 if (usedWidth < maxWidth) {
500 break;
501 }
502 }
503 return usedWidth;
504 }
505
drawField(QPainter * p,int f,DrawParams * dp)506 bool RectDrawing::drawField(QPainter *p, int f, DrawParams *dp)
507 {
508 if (!dp) {
509 dp = drawParams();
510 }
511
512 if (!_fm) {
513 _fm = new QFontMetrics(dp->font());
514 _fontHeight = _fm->height();
515 }
516
517 QRect r = _rect;
518
519 int h = _fontHeight;
520 bool rotate = dp->rotated();
521 int width = (rotate ? r.height() : r.width()) - 4;
522 int height = (rotate ? r.width() : r.height());
523 int lines = height / h;
524
525 // stop if there is no space available
526 if (lines < 1) {
527 return false;
528 }
529
530 // calculate free space in first line (<unused>)
531 int pos = dp->position(f);
532 if (pos == DrawParams::Default) {
533 switch (f % 4) {
534 case 0: pos = DrawParams::TopLeft; break;
535 case 1: pos = DrawParams::TopRight; break;
536 case 2: pos = DrawParams::BottomRight; break;
537 case 3: pos = DrawParams::BottomLeft; break;
538 }
539 }
540
541 int unused = 0;
542 bool isBottom = false;
543 bool isCenter = false;
544 bool isRight = false;
545 int *used = nullptr;
546 switch (pos) {
547 case DrawParams::TopLeft:
548 used = &_usedTopLeft;
549 if (_usedTopLeft == 0) {
550 if (_usedTopCenter) {
551 unused = (width - _usedTopCenter) / 2;
552 } else {
553 unused = width - _usedTopRight;
554 }
555 }
556 break;
557
558 case DrawParams::TopCenter:
559 isCenter = true;
560 used = &_usedTopCenter;
561 if (_usedTopCenter == 0) {
562 if (_usedTopLeft > _usedTopRight) {
563 unused = width - 2 * _usedTopLeft;
564 } else {
565 unused = width - 2 * _usedTopRight;
566 }
567 }
568 break;
569
570 case DrawParams::TopRight:
571 isRight = true;
572 used = &_usedTopRight;
573 if (_usedTopRight == 0) {
574 if (_usedTopCenter) {
575 unused = (width - _usedTopCenter) / 2;
576 } else {
577 unused = width - _usedTopLeft;
578 }
579 }
580 break;
581
582 case DrawParams::BottomLeft:
583 isBottom = true;
584 used = &_usedBottomLeft;
585 if (_usedBottomLeft == 0) {
586 if (_usedBottomCenter) {
587 unused = (width - _usedBottomCenter) / 2;
588 } else {
589 unused = width - _usedBottomRight;
590 }
591 }
592 break;
593
594 case DrawParams::BottomCenter:
595 isCenter = true;
596 isBottom = true;
597 used = &_usedBottomCenter;
598 if (_usedBottomCenter == 0) {
599 if (_usedBottomLeft > _usedBottomRight) {
600 unused = width - 2 * _usedBottomLeft;
601 } else {
602 unused = width - 2 * _usedBottomRight;
603 }
604 }
605 break;
606
607 case DrawParams::BottomRight:
608 isRight = true;
609 isBottom = true;
610 used = &_usedBottomRight;
611 if (_usedBottomRight == 0) {
612 if (_usedBottomCenter) {
613 unused = (width - _usedBottomCenter) / 2;
614 } else {
615 unused = width - _usedBottomLeft;
616 }
617 }
618 break;
619 }
620 if (isBottom) {
621 if ((_usedTopLeft > 0) ||
622 (_usedTopCenter > 0) ||
623 (_usedTopRight > 0)) {
624 lines--;
625 }
626 } else if (!isBottom) {
627 if ((_usedBottomLeft > 0) ||
628 (_usedBottomCenter > 0) ||
629 (_usedBottomRight > 0)) {
630 lines--;
631 }
632 }
633 if (lines < 1) {
634 return false;
635 }
636
637 int y = isBottom ? height - h : 0;
638
639 if (unused < 0) {
640 unused = 0;
641 }
642 if (unused == 0) {
643 // no space available in last line at this position
644 y = isBottom ? (y - h) : (y + h);
645 lines--;
646
647 if (lines < 1) {
648 return false;
649 }
650
651 // new line: reset used space
652 if (isBottom) {
653 _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0;
654 } else {
655 _usedTopLeft = _usedTopCenter = _usedTopRight = 0;
656 }
657
658 unused = width;
659 }
660
661 // stop as soon as possible when there is no space for "..."
662 static int dotW = 0;
663 if (!dotW) {
664 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
665 dotW = _fm->horizontalAdvance(QLatin1String("..."));
666 #else
667 dotW = _fm->width(QLatin1String("..."));
668 #endif
669 }
670 if (width < dotW) {
671 return false;
672 }
673
674 // get text and pixmap now, only if we need to, because it is possible
675 // that they are calculated on demand (and this can take some time)
676 QString name = dp->text(f);
677 if (name.isEmpty()) {
678 return false;
679 }
680 QPixmap pix = dp->pixmap(f);
681
682 // check if pixmap can be drawn
683 int pixW = pix.width();
684 int pixH = pix.height();
685 int pixY = 0;
686 bool pixDrawn = true;
687 if (pixW > 0) {
688 pixW += 2; // X distance from pix
689 if ((width < pixW + dotW) || (height < pixH)) {
690 // do not draw
691 pixW = 0;
692 } else {
693 pixDrawn = false;
694 }
695 }
696
697 // width of text and pixmap to be drawn
698 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
699 int w = pixW + _fm->horizontalAdvance(name);
700 #else
701 int w = pixW + _fm->width(name);
702 #endif
703
704 // if we have limited space at 1st line:
705 // use it only if whole name does fit in last line...
706 if ((unused < width) && (w > unused)) {
707 y = isBottom ? (y - h) : (y + h);
708 lines--;
709
710 if (lines < 1) {
711 return false;
712 }
713
714 // new line: reset used space
715 if (isBottom) {
716 _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0;
717 } else {
718 _usedTopLeft = _usedTopCenter = _usedTopRight = 0;
719 }
720 }
721
722 p->save();
723 p->setPen((qGray(dp->backColor().rgb()) > 100) ? Qt::black : Qt::white);
724 p->setFont(dp->font());
725 if (rotate) {
726 //p->translate(r.x()+2, r.y()+r.height());
727 p->translate(r.x(), r.y() + r.height() - 2);
728 p->rotate(270);
729 } else {
730 p->translate(r.x() + 2, r.y());
731 }
732 // adjust available lines according to maxLines
733 int max = dp->maxLines(f);
734 if ((max > 0) && (lines > max)) {
735 lines = max;
736 }
737 /* loop over name parts to break up string depending on available width.
738 * every char category change is supposed a possible break,
739 * with the exception Uppercase=>Lowercase.
740 * It is good enough for numbers, Symbols...
741 *
742 * If the text is to be written at the bottom, we start with the
743 * end of the string (so everything is reverted)
744 */
745 QString remaining;
746 int origLines = lines;
747 while (lines > 0) {
748
749 // more than one line: search for line break
750 if (w > width && lines > 1) {
751 int breakPos;
752
753 if (!isBottom) {
754 w = pixW + findBreak(breakPos, name, _fm, width - pixW);
755
756 remaining = name.mid(breakPos);
757 // remove space on break point
758 if (name[breakPos - 1].category() == QChar::Separator_Space) {
759 name = name.left(breakPos - 1);
760 } else {
761 name = name.left(breakPos);
762 }
763 } else { // bottom
764 w = pixW + findBreakBackwards(breakPos, name, _fm, width - pixW);
765
766 remaining = name.left(breakPos);
767 // remove space on break point
768 if (name[breakPos].category() == QChar::Separator_Space) {
769 name = name.mid(breakPos + 1);
770 } else {
771 name = name.mid(breakPos);
772 }
773 }
774 } else {
775 remaining.clear();
776 }
777 /* truncate and add ... if needed */
778 if (w > width) {
779 name = _fm->elidedText(name, Qt::ElideRight, width - pixW);
780 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
781 w = _fm->horizontalAdvance(name) + pixW;
782 #else
783 w = _fm->width(name) + pixW;
784 #endif
785 }
786
787 int x = 0;
788 if (isCenter) {
789 x = (width - w) / 2;
790 } else if (isRight) {
791 x = width - w;
792 }
793 if (!pixDrawn) {
794 pixY = y + (h - pixH) / 2; // default: center vertically
795 if (pixH > h) {
796 pixY = isBottom ? y - (pixH - h) : y;
797 }
798
799 p->drawPixmap(x, pixY, pix);
800
801 // for distance to next text
802 pixY = isBottom ? (pixY - h - 2) : (pixY + pixH + 2);
803 pixDrawn = true;
804 }
805 p->drawText(x + pixW, y,
806 width - pixW, h,
807 Qt::AlignLeft, name);
808 y = isBottom ? (y - h) : (y + h);
809 lines--;
810
811 if (remaining.isEmpty()) {
812 break;
813 }
814 name = remaining;
815 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
816 w = pixW + _fm->horizontalAdvance(name);
817 #else
818 w = pixW + _fm->width(name);
819 #endif
820 }
821
822 // make sure the pix stays visible
823 if (pixDrawn && (pixY > 0)) {
824 if (isBottom && (pixY < y)) {
825 y = pixY;
826 }
827 if (!isBottom && (pixY > y)) {
828 y = pixY;
829 }
830 }
831
832 if (origLines > lines) {
833 // if only 1 line written, do not reset _used* vars
834 if (lines - origLines > 1) {
835 if (isBottom) {
836 _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0;
837 } else {
838 _usedTopLeft = _usedTopCenter = _usedTopRight = 0;
839 }
840 }
841
842 // take back one line
843 y = isBottom ? (y + h) : (y - h);
844 if (used) {
845 *used = w;
846 }
847 }
848
849 // update free space
850 if (!isBottom) {
851 if (rotate) {
852 _rect.setRect(r.x() + y, r.y(), r.width() - y, r.height());
853 } else {
854 _rect.setRect(r.x(), r.y() + y, r.width(), r.height() - y);
855 }
856 } else {
857 if (rotate) {
858 _rect.setRect(r.x(), r.y(), y + h, r.height());
859 } else {
860 _rect.setRect(r.x(), r.y(), r.width(), y + h);
861 }
862 }
863
864 p->restore();
865
866 return true;
867 }
868