File indexing completed on 2024-05-12 05:44:25

0001 /***************************************************************************
0002  *   Copyright (C) 2005-2009 by Rajko Albrecht  ral@alwins-world.de        *
0003  *   https://kde.org/applications/development/org.kde.kdesvn               *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program; if not, write to the                         *
0017  *   Free Software Foundation, Inc.,                                       *
0018  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
0019  ***************************************************************************/
0020 /* This file was part of KCachegrind.
0021    Copyright (C) 2002, 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
0022    Adapted for the needs of kdesvn  by Rajko Albrecht <ral@alwins-world.de>
0023 */
0024 /*
0025  * A Widget for visualizing hierarchical metrics as areas.
0026  * The API is similar to QListView.
0027  */
0028 
0029 #include "drawparams.h"
0030 
0031 #include <math.h>
0032 
0033 #include <QFontDatabase>
0034 #include <QPainter>
0035 
0036 // set this to 1 to enable debug output
0037 #define DEBUG_DRAWING 0
0038 #define MAX_FIELD 12
0039 
0040 //
0041 // StoredDrawParams
0042 //
0043 StoredDrawParams::StoredDrawParams()
0044     : _backColor(Qt::white)
0045     , _selected(false)
0046     , _current(false)
0047     , _shaded(true)
0048     , _rotated(false)
0049     , _drawFrame(false)
0050 {
0051     // field array has size 0
0052 }
0053 
0054 StoredDrawParams::StoredDrawParams(const QColor &c, bool selected, bool current)
0055     : _backColor(c)
0056     , _selected(selected)
0057     , _current(current)
0058     , _shaded(true)
0059     , _rotated(false)
0060     , _drawFrame(true)
0061 {
0062     // field array has size 0
0063 }
0064 
0065 QString StoredDrawParams::text(int f) const
0066 {
0067     if ((f < 0) || (f >= _field.size())) {
0068         return QString();
0069     }
0070 
0071     return _field[f].text;
0072 }
0073 
0074 QPixmap StoredDrawParams::pixmap(int f) const
0075 {
0076     if ((f < 0) || (f >= _field.size())) {
0077         return QPixmap();
0078     }
0079 
0080     return _field[f].pix;
0081 }
0082 
0083 DrawParams::Position StoredDrawParams::position(int f) const
0084 {
0085     if ((f < 0) || (f >= _field.size())) {
0086         return Default;
0087     }
0088 
0089     return _field[f].pos;
0090 }
0091 
0092 int StoredDrawParams::maxLines(int f) const
0093 {
0094     if ((f < 0) || (f >= _field.size())) {
0095         return 0;
0096     }
0097 
0098     return _field[f].maxLines;
0099 }
0100 
0101 QFont StoredDrawParams::font() const
0102 {
0103     return QFontDatabase::systemFont(QFontDatabase::FixedFont);
0104 }
0105 
0106 void StoredDrawParams::ensureField(int f)
0107 {
0108     static Field *def = nullptr;
0109     if (!def) {
0110         def = new Field();
0111         def->pos = Default;
0112         def->maxLines = 0;
0113     }
0114 
0115     if (f < 0 || f >= MAX_FIELD) {
0116         return;
0117     }
0118 
0119     while (_field.size() < f + 1) {
0120         _field.append(*def);
0121     }
0122 }
0123 
0124 void StoredDrawParams::setField(int f, const QString &t, const QPixmap &pm, Position p, int maxLines)
0125 {
0126     if (f < 0 || f >= MAX_FIELD) {
0127         return;
0128     }
0129     ensureField(f);
0130 
0131     _field[f].text = t;
0132     _field[f].pix = pm;
0133     _field[f].pos = p;
0134     _field[f].maxLines = maxLines;
0135 }
0136 
0137 void StoredDrawParams::setText(int f, const QString &t)
0138 {
0139     if (f < 0 || f >= MAX_FIELD) {
0140         return;
0141     }
0142     ensureField(f);
0143 
0144     _field[f].text = t;
0145 }
0146 
0147 void StoredDrawParams::setPixmap(int f, QPixmap pm)
0148 {
0149     if (f < 0 || f >= MAX_FIELD) {
0150         return;
0151     }
0152     ensureField(f);
0153 
0154     _field[f].pix = pm;
0155 }
0156 
0157 void StoredDrawParams::setPosition(int f, Position p)
0158 {
0159     if (f < 0 || f >= MAX_FIELD) {
0160         return;
0161     }
0162     ensureField(f);
0163 
0164     _field[f].pos = p;
0165 }
0166 
0167 void StoredDrawParams::setMaxLines(int f, int m)
0168 {
0169     if (f < 0 || f >= MAX_FIELD) {
0170         return;
0171     }
0172     ensureField(f);
0173 
0174     _field[f].maxLines = m;
0175 }
0176 
0177 //
0178 // RectDrawing
0179 //
0180 
0181 RectDrawing::RectDrawing(const QRect &r)
0182     : _fm(nullptr)
0183     , _dp(nullptr)
0184 {
0185     setRect(r);
0186 }
0187 
0188 RectDrawing::~RectDrawing()
0189 {
0190     delete _fm;
0191     delete _dp;
0192 }
0193 
0194 DrawParams *RectDrawing::drawParams()
0195 {
0196     if (!_dp) {
0197         _dp = new StoredDrawParams();
0198     }
0199 
0200     return _dp;
0201 }
0202 
0203 void RectDrawing::setDrawParams(DrawParams *dp)
0204 {
0205     if (_dp) {
0206         delete _dp;
0207     }
0208     _dp = dp;
0209 }
0210 
0211 void RectDrawing::setRect(QRect r)
0212 {
0213     _rect = r;
0214 
0215     _usedTopLeft = 0;
0216     _usedTopCenter = 0;
0217     _usedTopRight = 0;
0218     _usedBottomLeft = 0;
0219     _usedBottomCenter = 0;
0220     _usedBottomRight = 0;
0221 
0222     _fontHeight = 0;
0223 }
0224 
0225 QRect RectDrawing::remainingRect(DrawParams *dp)
0226 {
0227     if (!dp) {
0228         dp = drawParams();
0229     }
0230 
0231     if ((_usedTopLeft > 0) || (_usedTopCenter > 0) || (_usedTopRight > 0)) {
0232         if (dp->rotated()) {
0233             _rect.setLeft(_rect.left() + _fontHeight);
0234         } else {
0235             _rect.setTop(_rect.top() + _fontHeight);
0236         }
0237     }
0238 
0239     if ((_usedBottomLeft > 0) || (_usedBottomCenter > 0) || (_usedBottomRight > 0)) {
0240         if (dp->rotated()) {
0241             _rect.setRight(_rect.right() - _fontHeight);
0242         } else {
0243             _rect.setBottom(_rect.bottom() - _fontHeight);
0244         }
0245     }
0246     return _rect;
0247 }
0248 
0249 void RectDrawing::drawBack(QPainter *p, DrawParams *dp)
0250 {
0251     if (!dp) {
0252         dp = drawParams();
0253     }
0254     if (_rect.width() <= 0 || _rect.height() <= 0) {
0255         return;
0256     }
0257 
0258     QRect r = _rect;
0259     QColor normal = dp->backColor();
0260     if (dp->selected()) {
0261         normal = normal.lighter();
0262     }
0263     bool isCurrent = dp->current();
0264 
0265     if (dp->drawFrame() || isCurrent) {
0266         // 3D raised/sunken frame effect...
0267         QColor high = normal.lighter();
0268         QColor low = normal.darker();
0269         p->setPen(isCurrent ? low : high);
0270         p->drawLine(r.left(), r.top(), r.right(), r.top());
0271         p->drawLine(r.left(), r.top(), r.left(), r.bottom());
0272         p->setPen(isCurrent ? high : low);
0273         p->drawLine(r.right(), r.top(), r.right(), r.bottom());
0274         p->drawLine(r.left(), r.bottom(), r.right(), r.bottom());
0275         r.setRect(r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2);
0276     }
0277     if (r.width() <= 0 || r.height() <= 0) {
0278         return;
0279     }
0280     if (dp->shaded() && (r.width() > 0 && r.height() > 0)) {
0281         // adjustment for drawRect semantic in Qt4: decrement height/width
0282         r.setRect(r.x(), r.y(), r.width() - 1, r.height() - 1);
0283 
0284         // some shading
0285         bool goDark = qGray(normal.rgb()) > 128;
0286         int rBase, gBase, bBase;
0287         normal.getRgb(&rBase, &gBase, &bBase);
0288         p->setBrush(Qt::NoBrush);
0289 
0290         // shade parameters:
0291         int d = 7;
0292         double factor = 0.1, forth = 0.7, back1 = 0.9, toBack2 = .7, back2 = 0.97;
0293 
0294         // coefficient corrections because of rectangle size
0295         int s = r.width();
0296         if (s > r.height()) {
0297             s = r.height();
0298         }
0299         if (s < 100) {
0300             forth -= .3 * (100 - s) / 100;
0301             back1 -= .2 * (100 - s) / 100;
0302             back2 -= .02 * (100 - s) / 100;
0303         }
0304 
0305         // maximal color difference
0306         int rDiff = goDark ? -rBase / d : (255 - rBase) / d;
0307         int gDiff = goDark ? -gBase / d : (255 - gBase) / d;
0308         int bDiff = goDark ? -bBase / d : (255 - bBase) / d;
0309 
0310         QColor shadeColor;
0311         while (factor < .95 && (r.width() >= 0 && r.height() >= 0)) {
0312             shadeColor.setRgb(qRound(rBase + factor * rDiff), qRound(gBase + factor * gDiff), qRound(bBase + factor * bDiff));
0313             p->setPen(shadeColor);
0314             p->drawRect(r);
0315             r.setRect(r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2);
0316             factor = 1.0 - ((1.0 - factor) * forth);
0317         }
0318 
0319         // and back (1st half)
0320         while (factor > toBack2 && (r.width() >= 0 && r.height() >= 0)) {
0321             shadeColor.setRgb(qRound(rBase + factor * rDiff), qRound(gBase + factor * gDiff), qRound(bBase + factor * bDiff));
0322             p->setPen(shadeColor);
0323             p->drawRect(r);
0324             r.setRect(r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2);
0325             factor = 1.0 - ((1.0 - factor) / back1);
0326         }
0327 
0328         // and back (2nd half)
0329         while (factor > .01 && (r.width() >= 0 && r.height() >= 0)) {
0330             shadeColor.setRgb(qRound(rBase + factor * rDiff), qRound(gBase + factor * gDiff), qRound(bBase + factor * bDiff));
0331             p->setPen(shadeColor);
0332             p->drawRect(r);
0333             r.setRect(r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2);
0334             factor = factor * back2;
0335         }
0336 
0337         normal = shadeColor;
0338         // for filling, width and height has to be incremented again
0339         r.setRect(r.x(), r.y(), r.width() + 1, r.height() + 1);
0340     }
0341 
0342     // fill inside
0343     p->fillRect(r, normal);
0344 }
0345 
0346 /* Helper for drawField
0347  * Find a line break position in a string, given a font and maximum width
0348  *
0349  * Returns the actually used width, and sets <breakPos>
0350  */
0351 static int findBreak(int &breakPos, QString text, QFontMetrics *fm, int maxWidth)
0352 {
0353     int usedWidth;
0354 
0355     // does full text fit?
0356     breakPos = text.length();
0357     usedWidth = fm->horizontalAdvance(text);
0358     if (usedWidth < maxWidth) {
0359         return usedWidth;
0360     }
0361 
0362     // now lower breakPos until best position is found.
0363     // first by binary search, resulting in a position a little bit too large
0364     int bottomPos = 0;
0365     while (qAbs(maxWidth - usedWidth) > 3 * fm->maxWidth()) {
0366         int halfPos = (bottomPos + breakPos) / 2;
0367         int halfWidth = fm->horizontalAdvance(text, halfPos);
0368         if (halfWidth < maxWidth) {
0369             bottomPos = halfPos;
0370         } else {
0371             breakPos = halfPos;
0372             usedWidth = halfWidth;
0373         }
0374     }
0375 
0376     // final position by taking break boundaries into account.
0377     // possible break boundaries are changing char categories but not middle of "Aa"
0378     QChar::Category lastCat, cat;
0379     int pos = breakPos;
0380     lastCat = text[pos - 1].category();
0381     // at minimum 2 chars before break
0382     while (pos > 2) {
0383         pos--;
0384         cat = text[pos - 1].category();
0385         if (cat == lastCat) {
0386             continue;
0387         }
0388 
0389         // "Aa" has not a possible break inbetween
0390         if ((cat == QChar::Letter_Uppercase) && (lastCat == QChar::Letter_Lowercase)) {
0391             lastCat = cat;
0392             continue;
0393         }
0394         lastCat = cat;
0395 
0396         breakPos = pos;
0397         usedWidth = fm->horizontalAdvance(text, breakPos);
0398         if (usedWidth < maxWidth) {
0399             break;
0400         }
0401     }
0402     return usedWidth;
0403 }
0404 
0405 /* Helper for drawField
0406  * Find last line break position in a string from backwards,
0407  * given a font and maximum width
0408  *
0409  * Returns the actually used width, and sets <breakPos>
0410  */
0411 static int findBreakBackwards(int &breakPos, QString text, QFontMetrics *fm, int maxWidth)
0412 {
0413     int usedWidth;
0414 
0415     // does full text fit?
0416     breakPos = 0;
0417     usedWidth = fm->horizontalAdvance(text);
0418     if (usedWidth < maxWidth) {
0419         return usedWidth;
0420     }
0421 
0422     // now raise breakPos until best position is found.
0423     // first by binary search, resulting in a position a little bit too small
0424     int topPos = text.length();
0425     while (qAbs(maxWidth - usedWidth) > 3 * fm->maxWidth()) {
0426         int halfPos = (breakPos + topPos) / 2;
0427         int halfWidth = fm->horizontalAdvance(text.mid(halfPos));
0428         if (halfWidth < maxWidth) {
0429             breakPos = halfPos;
0430             usedWidth = halfWidth;
0431         } else {
0432             topPos = halfPos;
0433         }
0434     }
0435 
0436     // final position by taking break boundaries into account.
0437     // possible break boundaries are changing char categories but not middle of "Aa"
0438     QChar::Category lastCat, cat;
0439     int pos = breakPos;
0440     lastCat = text[pos].category();
0441     // at minimum 2 chars before break
0442     while (pos < text.length() - 2) {
0443         pos++;
0444         cat = text[pos].category();
0445         if (cat == lastCat) {
0446             continue;
0447         }
0448 
0449         // "Aa" has not a possible break inbetween
0450         if ((lastCat == QChar::Letter_Uppercase) && (cat == QChar::Letter_Lowercase)) {
0451             lastCat = cat;
0452             continue;
0453         }
0454         lastCat = cat;
0455 
0456         breakPos = pos;
0457         usedWidth = fm->horizontalAdvance(text.mid(breakPos));
0458         if (usedWidth < maxWidth) {
0459             break;
0460         }
0461     }
0462     return usedWidth;
0463 }
0464 
0465 bool RectDrawing::drawField(QPainter *p, int f, DrawParams *dp)
0466 {
0467     if (!dp) {
0468         dp = drawParams();
0469     }
0470 
0471     if (!_fm) {
0472         _fm = new QFontMetrics(dp->font());
0473         _fontHeight = _fm->height();
0474     }
0475 
0476     QRect r = _rect;
0477 
0478     int h = _fontHeight;
0479     bool rotate = dp->rotated();
0480     int width = (rotate ? r.height() : r.width()) - 4;
0481     int height = (rotate ? r.width() : r.height());
0482     int lines = height / h;
0483 
0484     // stop if there is no space available
0485     if (lines < 1) {
0486         return false;
0487     }
0488 
0489     // calculate free space in first line (<unused>)
0490     int pos = dp->position(f);
0491     if (pos == DrawParams::Default) {
0492         switch (f % 4) {
0493         case 0:
0494             pos = DrawParams::TopLeft;
0495             break;
0496         case 1:
0497             pos = DrawParams::TopRight;
0498             break;
0499         case 2:
0500             pos = DrawParams::BottomRight;
0501             break;
0502         case 3:
0503             pos = DrawParams::BottomLeft;
0504             break;
0505         }
0506     }
0507 
0508     int unused = 0;
0509     bool isBottom = false;
0510     bool isCenter = false;
0511     bool isRight = false;
0512     int *used = nullptr;
0513     switch (pos) {
0514     case DrawParams::TopLeft:
0515         used = &_usedTopLeft;
0516         if (_usedTopLeft == 0) {
0517             if (_usedTopCenter) {
0518                 unused = (width - _usedTopCenter) / 2;
0519             } else {
0520                 unused = width - _usedTopRight;
0521             }
0522         }
0523         break;
0524 
0525     case DrawParams::TopCenter:
0526         isCenter = true;
0527         used = &_usedTopCenter;
0528         if (_usedTopCenter == 0) {
0529             if (_usedTopLeft > _usedTopRight) {
0530                 unused = width - 2 * _usedTopLeft;
0531             } else {
0532                 unused = width - 2 * _usedTopRight;
0533             }
0534         }
0535         break;
0536 
0537     case DrawParams::TopRight:
0538         isRight = true;
0539         used = &_usedTopRight;
0540         if (_usedTopRight == 0) {
0541             if (_usedTopCenter) {
0542                 unused = (width - _usedTopCenter) / 2;
0543             } else {
0544                 unused = width - _usedTopLeft;
0545             }
0546         }
0547         break;
0548 
0549     case DrawParams::BottomLeft:
0550         isBottom = true;
0551         used = &_usedBottomLeft;
0552         if (_usedBottomLeft == 0) {
0553             if (_usedBottomCenter) {
0554                 unused = (width - _usedBottomCenter) / 2;
0555             } else {
0556                 unused = width - _usedBottomRight;
0557             }
0558         }
0559         break;
0560 
0561     case DrawParams::BottomCenter:
0562         isCenter = true;
0563         isBottom = true;
0564         used = &_usedBottomCenter;
0565         if (_usedBottomCenter == 0) {
0566             if (_usedBottomLeft > _usedBottomRight) {
0567                 unused = width - 2 * _usedBottomLeft;
0568             } else {
0569                 unused = width - 2 * _usedBottomRight;
0570             }
0571         }
0572         break;
0573 
0574     case DrawParams::BottomRight:
0575         isRight = true;
0576         isBottom = true;
0577         used = &_usedBottomRight;
0578         if (_usedBottomRight == 0) {
0579             if (_usedBottomCenter) {
0580                 unused = (width - _usedBottomCenter) / 2;
0581             } else {
0582                 unused = width - _usedBottomLeft;
0583             }
0584         }
0585         break;
0586     }
0587     if (isBottom) {
0588         if ((_usedTopLeft > 0) || (_usedTopCenter > 0) || (_usedTopRight > 0)) {
0589             lines--;
0590         }
0591     } else if (!isBottom) {
0592         if ((_usedBottomLeft > 0) || (_usedBottomCenter > 0) || (_usedBottomRight > 0)) {
0593             lines--;
0594         }
0595     }
0596     if (lines < 1) {
0597         return false;
0598     }
0599 
0600     int y = isBottom ? height - h : 0;
0601 
0602     if (unused < 0) {
0603         unused = 0;
0604     }
0605     if (unused == 0) {
0606         // no space available in last line at this position
0607         y = isBottom ? (y - h) : (y + h);
0608         lines--;
0609 
0610         if (lines < 1) {
0611             return false;
0612         }
0613 
0614         // new line: reset used space
0615         if (isBottom) {
0616             _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0;
0617         } else {
0618             _usedTopLeft = _usedTopCenter = _usedTopRight = 0;
0619         }
0620 
0621         unused = width;
0622     }
0623 
0624     // stop as soon as possible when there is no space for "..."
0625     static int dotW = 0;
0626     if (!dotW) {
0627         dotW = _fm->horizontalAdvance(QLatin1String("..."));
0628     }
0629     if (width < dotW) {
0630         return false;
0631     }
0632 
0633     // get text and pixmap now, only if we need to, because it is possible
0634     // that they are calculated on demand (and this can take some time)
0635     QString name = dp->text(f);
0636     if (name.isEmpty()) {
0637         return false;
0638     }
0639     QPixmap pix = dp->pixmap(f);
0640 
0641     // check if pixmap can be drawn
0642     int pixW = pix.width();
0643     int pixH = pix.height();
0644     int pixY = 0;
0645     bool pixDrawn = true;
0646     if (pixW > 0) {
0647         pixW += 2; // X distance from pix
0648         if ((width < pixW + dotW) || (height < pixH)) {
0649             // do not draw
0650             pixW = 0;
0651         } else {
0652             pixDrawn = false;
0653         }
0654     }
0655 
0656     // width of text and pixmap to be drawn
0657     int w = pixW + _fm->horizontalAdvance(name);
0658 
0659     // if we have limited space at 1st line:
0660     // use it only if whole name does fit in last line...
0661     if ((unused < width) && (w > unused)) {
0662         y = isBottom ? (y - h) : (y + h);
0663         lines--;
0664 
0665         if (lines < 1) {
0666             return false;
0667         }
0668 
0669         // new line: reset used space
0670         if (isBottom) {
0671             _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0;
0672         } else {
0673             _usedTopLeft = _usedTopCenter = _usedTopRight = 0;
0674         }
0675     }
0676 
0677     p->save();
0678     p->setPen((qGray(dp->backColor().rgb()) > 100) ? Qt::black : Qt::white);
0679     p->setFont(dp->font());
0680     if (rotate) {
0681         // p->translate(r.x()+2, r.y()+r.height());
0682         p->translate(r.x(), r.y() + r.height() - 2);
0683         p->rotate(270);
0684     } else {
0685         p->translate(r.x() + 2, r.y());
0686     }
0687     // adjust available lines according to maxLines
0688     int max = dp->maxLines(f);
0689     if ((max > 0) && (lines > max)) {
0690         lines = max;
0691     }
0692     /* loop over name parts to break up string depending on available width.
0693      * every char category change is supposed a possible break,
0694      * with the exception Uppercase=>Lowercase.
0695      * It is good enough for numbers, Symbols...
0696      *
0697      * If the text is to be written at the bottom, we start with the
0698      * end of the string (so everything is reverted)
0699      */
0700     QString remaining;
0701     int origLines = lines;
0702     while (lines > 0) {
0703         // more than one line: search for line break
0704         if (w > width && lines > 1) {
0705             int breakPos;
0706 
0707             if (!isBottom) {
0708                 w = pixW + findBreak(breakPos, name, _fm, width - pixW);
0709 
0710                 remaining = name.mid(breakPos);
0711                 // remove space on break point
0712                 if (name[breakPos - 1].category() == QChar::Separator_Space) {
0713                     name = name.left(breakPos - 1);
0714                 } else {
0715                     name = name.left(breakPos);
0716                 }
0717             } else { // bottom
0718                 w = pixW + findBreakBackwards(breakPos, name, _fm, width - pixW);
0719 
0720                 remaining = name.left(breakPos);
0721                 // remove space on break point
0722                 if (name[breakPos].category() == QChar::Separator_Space) {
0723                     name = name.mid(breakPos + 1);
0724                 } else {
0725                     name = name.mid(breakPos);
0726                 }
0727             }
0728         } else {
0729             remaining.clear();
0730         }
0731         /* truncate and add ... if needed */
0732         if (w > width) {
0733             name = _fm->elidedText(name, Qt::ElideRight, width - pixW);
0734             w = _fm->horizontalAdvance(name) + pixW;
0735         }
0736 
0737         int x = 0;
0738         if (isCenter) {
0739             x = (width - w) / 2;
0740         } else if (isRight) {
0741             x = width - w;
0742         }
0743         if (!pixDrawn) {
0744             pixY = y + (h - pixH) / 2; // default: center vertically
0745             if (pixH > h) {
0746                 pixY = isBottom ? y - (pixH - h) : y;
0747             }
0748 
0749             p->drawPixmap(x, pixY, pix);
0750 
0751             // for distance to next text
0752             pixY = isBottom ? (pixY - h - 2) : (pixY + pixH + 2);
0753             pixDrawn = true;
0754         }
0755         p->drawText(x + pixW, y, width - pixW, h, Qt::AlignLeft, name);
0756         y = isBottom ? (y - h) : (y + h);
0757         lines--;
0758 
0759         if (remaining.isEmpty()) {
0760             break;
0761         }
0762         name = remaining;
0763         w = pixW + _fm->horizontalAdvance(name);
0764     }
0765 
0766     // make sure the pix stays visible
0767     if (pixDrawn && (pixY > 0)) {
0768         if (isBottom && (pixY < y)) {
0769             y = pixY;
0770         }
0771         if (!isBottom && (pixY > y)) {
0772             y = pixY;
0773         }
0774     }
0775 
0776     if (origLines > lines) {
0777         // if only 1 line written, do not reset _used* vars
0778         if (lines - origLines > 1) {
0779             if (isBottom) {
0780                 _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0;
0781             } else {
0782                 _usedTopLeft = _usedTopCenter = _usedTopRight = 0;
0783             }
0784         }
0785 
0786         // take back one line
0787         y = isBottom ? (y + h) : (y - h);
0788         if (used) {
0789             *used = w;
0790         }
0791     }
0792 
0793     // update free space
0794     if (!isBottom) {
0795         if (rotate) {
0796             _rect.setRect(r.x() + y, r.y(), r.width() - y, r.height());
0797         } else {
0798             _rect.setRect(r.x(), r.y() + y, r.width(), r.height() - y);
0799         }
0800     } else {
0801         if (rotate) {
0802             _rect.setRect(r.x(), r.y(), y + h, r.height());
0803         } else {
0804             _rect.setRect(r.x(), r.y(), r.width(), y + h);
0805         }
0806     }
0807 
0808     p->restore();
0809 
0810     return true;
0811 }