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