File indexing completed on 2024-04-28 03:59:13

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1998 Jörg Habenicht <j.habenicht@europemail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kruler.h"
0009 
0010 #include <QFont>
0011 #include <QPolygon>
0012 #include <QStylePainter>
0013 
0014 #define INIT_VALUE 0
0015 #define INIT_MIN_VALUE 0
0016 #define INIT_MAX_VALUE 100
0017 #define INIT_TINY_MARK_DISTANCE 1
0018 #define INIT_LITTLE_MARK_DISTANCE 5
0019 #define INIT_MIDDLE_MARK_DISTANCE (INIT_LITTLE_MARK_DISTANCE * 2)
0020 #define INIT_BIG_MARK_DISTANCE (INIT_LITTLE_MARK_DISTANCE * 10)
0021 #define INIT_SHOW_TINY_MARK false
0022 #define INIT_SHOW_LITTLE_MARK true
0023 #define INIT_SHOW_MEDIUM_MARK true
0024 #define INIT_SHOW_BIG_MARK true
0025 #define INIT_SHOW_END_MARK true
0026 #define INIT_SHOW_POINTER true
0027 #define INIT_SHOW_END_LABEL true
0028 
0029 #define INIT_PIXEL_PER_MARK (double)10.0 /* distance between 2 base marks in pixel */
0030 #define INIT_OFFSET (-20)
0031 #define INIT_LENGTH_FIX true
0032 #define INIT_END_OFFSET 0
0033 
0034 #define FIX_WIDTH 20 /* widget width in pixel */
0035 #define LINE_END (FIX_WIDTH - 3)
0036 #define END_MARK_LENGTH (FIX_WIDTH - 6)
0037 #define END_MARK_X2 LINE_END
0038 #define END_MARK_X1 (END_MARK_X2 - END_MARK_LENGTH)
0039 #define BIG_MARK_LENGTH (END_MARK_LENGTH * 3 / 4)
0040 #define BIG_MARK_X2 LINE_END
0041 #define BIG_MARK_X1 (BIG_MARK_X2 - BIG_MARK_LENGTH)
0042 #define MIDDLE_MARK_LENGTH (END_MARK_LENGTH / 2)
0043 #define MIDDLE_MARK_X2 LINE_END
0044 #define MIDDLE_MARK_X1 (MIDDLE_MARK_X2 - MIDDLE_MARK_LENGTH)
0045 #define LITTLE_MARK_LENGTH (MIDDLE_MARK_LENGTH / 2)
0046 #define LITTLE_MARK_X2 LINE_END
0047 #define LITTLE_MARK_X1 (LITTLE_MARK_X2 - LITTLE_MARK_LENGTH)
0048 #define BASE_MARK_LENGTH (LITTLE_MARK_LENGTH / 2)
0049 #define BASE_MARK_X2 LINE_END
0050 #define BASE_MARK_X1 (BASE_MARK_X2 - BASE_MARK_LENGTH)
0051 
0052 #define LABEL_SIZE 8
0053 #define END_LABEL_X 4
0054 #define END_LABEL_Y (END_LABEL_X + LABEL_SIZE - 2)
0055 
0056 #undef PROFILING
0057 
0058 #ifdef PROFILING
0059 #include <qdatetime.h>
0060 #endif
0061 
0062 class KRulerPrivate
0063 {
0064 public:
0065     int endOffset_length; /* marks the offset at the end of the ruler
0066                            * i.e. right side at horizontal and down side
0067                            * at vertical rulers.
0068                            * the ruler end mark is moved endOffset_length
0069                            * ticks away from the widget end.
0070                            * positive offset moves end mark inside the ruler.
0071                            * if lengthFix is true, endOffset_length holds the
0072                            * length of the ruler.
0073                            */
0074     int fontWidth; // ONLY valid for vertical rulers
0075 
0076     QAbstractSlider range;
0077     Qt::Orientation dir;
0078     int tmDist;
0079     int lmDist;
0080     int mmDist;
0081     int bmDist;
0082     int offset;
0083     bool showtm : 1; /* show tiny, little, medium, big, endmarks */
0084     bool showlm : 1;
0085     bool showmm : 1;
0086     bool showbm : 1;
0087     bool showem : 1;
0088 
0089     bool showpointer : 1;
0090     bool showEndL : 1;
0091     bool lengthFix : 1;
0092 
0093     double ppm; /* pixel per mark */
0094 
0095     QString endlabel;
0096 };
0097 
0098 KRuler::KRuler(QWidget *parent)
0099     : QAbstractSlider(parent)
0100     , d(new KRulerPrivate)
0101 {
0102     setRange(INIT_MIN_VALUE, INIT_MAX_VALUE);
0103     setPageStep(10);
0104     setValue(INIT_VALUE);
0105     initWidget(Qt::Horizontal);
0106     setFixedHeight(FIX_WIDTH);
0107 }
0108 
0109 KRuler::KRuler(Qt::Orientation orient, QWidget *parent, Qt::WindowFlags f)
0110     : QAbstractSlider(parent)
0111     , d(new KRulerPrivate)
0112 {
0113     setRange(INIT_MIN_VALUE, INIT_MAX_VALUE);
0114     setPageStep(10);
0115     setValue(INIT_VALUE);
0116     setWindowFlags(f);
0117     initWidget(orient);
0118     if (orient == Qt::Horizontal) {
0119         setFixedHeight(FIX_WIDTH);
0120     } else {
0121         setFixedWidth(FIX_WIDTH);
0122     }
0123 }
0124 
0125 KRuler::KRuler(Qt::Orientation orient, int widgetWidth, QWidget *parent, Qt::WindowFlags f)
0126     : QAbstractSlider(parent)
0127     , d(new KRulerPrivate)
0128 {
0129     setRange(INIT_MIN_VALUE, INIT_MAX_VALUE);
0130     setPageStep(10);
0131     setValue(INIT_VALUE);
0132     setWindowFlags(f);
0133     initWidget(orient);
0134     if (orient == Qt::Horizontal) {
0135         setFixedHeight(widgetWidth);
0136     } else {
0137         setFixedWidth(widgetWidth);
0138     }
0139 }
0140 
0141 void KRuler::initWidget(Qt::Orientation orientation)
0142 {
0143     d->showpointer = INIT_SHOW_POINTER;
0144     d->showEndL = INIT_SHOW_END_LABEL;
0145     d->lengthFix = INIT_LENGTH_FIX;
0146     d->endOffset_length = INIT_END_OFFSET;
0147 
0148     d->tmDist = INIT_TINY_MARK_DISTANCE;
0149     d->lmDist = INIT_LITTLE_MARK_DISTANCE;
0150     d->mmDist = INIT_MIDDLE_MARK_DISTANCE;
0151     d->bmDist = INIT_BIG_MARK_DISTANCE;
0152     d->offset = INIT_OFFSET;
0153     d->showtm = INIT_SHOW_TINY_MARK;
0154     d->showlm = INIT_SHOW_LITTLE_MARK;
0155     d->showmm = INIT_SHOW_MEDIUM_MARK;
0156     d->showbm = INIT_SHOW_BIG_MARK;
0157     d->showem = INIT_SHOW_END_MARK;
0158     d->ppm = INIT_PIXEL_PER_MARK;
0159     d->dir = orientation;
0160 }
0161 
0162 KRuler::~KRuler() = default;
0163 
0164 void KRuler::setTinyMarkDistance(int dist)
0165 {
0166     if (dist != d->tmDist) {
0167         d->tmDist = dist;
0168         update(contentsRect());
0169     }
0170 }
0171 
0172 int KRuler::tinyMarkDistance() const
0173 {
0174     return d->tmDist;
0175 }
0176 
0177 void KRuler::setLittleMarkDistance(int dist)
0178 {
0179     if (dist != d->lmDist) {
0180         d->lmDist = dist;
0181         update(contentsRect());
0182     }
0183 }
0184 
0185 int KRuler::littleMarkDistance() const
0186 {
0187     return d->lmDist;
0188 }
0189 
0190 void KRuler::setMediumMarkDistance(int dist)
0191 {
0192     if (dist != d->mmDist) {
0193         d->mmDist = dist;
0194         update(contentsRect());
0195     }
0196 }
0197 
0198 int KRuler::mediumMarkDistance() const
0199 {
0200     return d->mmDist;
0201 }
0202 
0203 void KRuler::setBigMarkDistance(int dist)
0204 {
0205     if (dist != d->bmDist) {
0206         d->bmDist = dist;
0207         update(contentsRect());
0208     }
0209 }
0210 
0211 int KRuler::bigMarkDistance() const
0212 {
0213     return d->bmDist;
0214 }
0215 
0216 void KRuler::setShowTinyMarks(bool show)
0217 {
0218     if (show != d->showtm) {
0219         d->showtm = show;
0220         update(contentsRect());
0221     }
0222 }
0223 
0224 bool KRuler::showTinyMarks() const
0225 {
0226     return d->showtm;
0227 }
0228 
0229 void KRuler::setShowLittleMarks(bool show)
0230 {
0231     if (show != d->showlm) {
0232         d->showlm = show;
0233         update(contentsRect());
0234     }
0235 }
0236 
0237 bool KRuler::showLittleMarks() const
0238 {
0239     return d->showlm;
0240 }
0241 
0242 void KRuler::setShowMediumMarks(bool show)
0243 {
0244     if (show != d->showmm) {
0245         d->showmm = show;
0246         update(contentsRect());
0247     }
0248 }
0249 
0250 bool KRuler::showMediumMarks() const
0251 {
0252     return d->showmm;
0253 }
0254 
0255 void KRuler::setShowBigMarks(bool show)
0256 {
0257     if (show != d->showbm) {
0258         d->showbm = show;
0259         update(contentsRect());
0260     }
0261 }
0262 
0263 bool KRuler::showBigMarks() const
0264 {
0265     return d->showbm;
0266 }
0267 
0268 void KRuler::setShowEndMarks(bool show)
0269 {
0270     if (show != d->showem) {
0271         d->showem = show;
0272         update(contentsRect());
0273     }
0274 }
0275 
0276 bool KRuler::showEndMarks() const
0277 {
0278     return d->showem;
0279 }
0280 
0281 void KRuler::setShowPointer(bool show)
0282 {
0283     if (show != d->showpointer) {
0284         d->showpointer = show;
0285         update(contentsRect());
0286     }
0287 }
0288 
0289 bool KRuler::showPointer() const
0290 {
0291     return d->showpointer;
0292 }
0293 
0294 void KRuler::setShowEndLabel(bool show)
0295 {
0296     if (d->showEndL != show) {
0297         d->showEndL = show;
0298         update(contentsRect());
0299     }
0300 }
0301 
0302 bool KRuler::showEndLabel() const
0303 {
0304     return d->showEndL;
0305 }
0306 
0307 void KRuler::setEndLabel(const QString &label)
0308 {
0309     d->endlabel = label;
0310 
0311     // premeasure the fontwidth and save it
0312     if (d->dir == Qt::Vertical) {
0313         QFont font = this->font();
0314         font.setPointSize(LABEL_SIZE);
0315         QFontMetrics fm(font);
0316         d->fontWidth = fm.horizontalAdvance(d->endlabel);
0317     }
0318     update(contentsRect());
0319 }
0320 
0321 QString KRuler::endLabel() const
0322 {
0323     return d->endlabel;
0324 }
0325 
0326 void KRuler::setRulerMetricStyle(KRuler::MetricStyle style)
0327 {
0328     switch (style) {
0329     default: /* fall through */
0330     case Custom:
0331         return;
0332     case Pixel:
0333         setLittleMarkDistance(1);
0334         setMediumMarkDistance(5);
0335         setBigMarkDistance(10);
0336 
0337         setShowTinyMarks(false);
0338         setShowLittleMarks(true);
0339         setShowMediumMarks(true);
0340         setShowBigMarks(true);
0341         setShowEndMarks(true);
0342 
0343         update(contentsRect());
0344         setPixelPerMark(10.0);
0345 
0346         break;
0347     case Inch:
0348         setTinyMarkDistance(1);
0349         setLittleMarkDistance(2);
0350         setMediumMarkDistance(4);
0351         setBigMarkDistance(8);
0352 
0353         setShowTinyMarks(true);
0354         setShowLittleMarks(true);
0355         setShowMediumMarks(true);
0356         setShowBigMarks(true);
0357         setShowEndMarks(true);
0358 
0359         update(contentsRect());
0360         setPixelPerMark(9.0);
0361 
0362         break;
0363     case Millimetres: /* fall through */
0364     case Centimetres: /* fall through */
0365     case Metres:
0366         setLittleMarkDistance(1);
0367         setMediumMarkDistance(5);
0368         setBigMarkDistance(10);
0369 
0370         setShowTinyMarks(false);
0371         setShowLittleMarks(true);
0372         setShowMediumMarks(true);
0373         setShowBigMarks(true);
0374         setShowEndMarks(true);
0375 
0376         update(contentsRect());
0377         setPixelPerMark(3.0);
0378     }
0379     switch (style) {
0380     case Pixel:
0381         setEndLabel(QStringLiteral("pixel"));
0382         break;
0383     case Inch:
0384         setEndLabel(QStringLiteral("inch"));
0385         break;
0386     case Millimetres:
0387         setEndLabel(QStringLiteral("mm"));
0388         break;
0389     case Centimetres:
0390         setEndLabel(QStringLiteral("cm"));
0391         break;
0392     case Metres:
0393         setEndLabel(QStringLiteral("m"));
0394     default: /* never reached, see above switch */
0395              /* empty command */;
0396     }
0397     // if the style changes one of the values,
0398     // update would have been called inside the methods
0399     // -> no update() call needed here !
0400 }
0401 
0402 void KRuler::setPixelPerMark(double rate)
0403 {
0404     // never compare floats against each other :)
0405     d->ppm = rate;
0406     update(contentsRect());
0407 }
0408 
0409 double KRuler::pixelPerMark() const
0410 {
0411     return d->ppm;
0412 }
0413 
0414 void KRuler::setLength(int length)
0415 {
0416     int tmp;
0417     if (d->lengthFix) {
0418         tmp = length;
0419     } else {
0420         tmp = width() - length;
0421     }
0422     if (tmp != d->endOffset_length) {
0423         d->endOffset_length = tmp;
0424         update(contentsRect());
0425     }
0426 }
0427 
0428 int KRuler::length() const
0429 {
0430     if (d->lengthFix) {
0431         return d->endOffset_length;
0432     }
0433     return (width() - d->endOffset_length);
0434 }
0435 
0436 void KRuler::setLengthFixed(bool fix)
0437 {
0438     d->lengthFix = fix;
0439 }
0440 
0441 bool KRuler::lengthFixed() const
0442 {
0443     return d->lengthFix;
0444 }
0445 
0446 void KRuler::setOffset(int _offset)
0447 {
0448     // debug("set offset %i", _offset);
0449     if (d->offset != _offset) {
0450         d->offset = _offset;
0451         update(contentsRect());
0452     }
0453 }
0454 
0455 int KRuler::offset() const
0456 {
0457     return d->offset;
0458 }
0459 
0460 int KRuler::endOffset() const
0461 {
0462     if (d->lengthFix) {
0463         return (width() - d->endOffset_length);
0464     }
0465     return d->endOffset_length;
0466 }
0467 
0468 void KRuler::slideUp(int count)
0469 {
0470     if (count) {
0471         d->offset += count;
0472         update(contentsRect());
0473     }
0474 }
0475 
0476 void KRuler::slideDown(int count)
0477 {
0478     if (count) {
0479         d->offset -= count;
0480         update(contentsRect());
0481     }
0482 }
0483 
0484 void KRuler::slotNewValue(int _value)
0485 {
0486     int oldvalue = value();
0487     if (oldvalue == _value) {
0488         return;
0489     }
0490     //    setValue(_value);
0491     setValue(_value);
0492     if (value() == oldvalue) {
0493         return;
0494     }
0495     // get the rectangular of the old and the new ruler pointer
0496     // and repaint only him
0497     if (d->dir == Qt::Horizontal) {
0498         QRect oldrec(-5 + oldvalue, 10, 11, 6);
0499         QRect newrec(-5 + _value, 10, 11, 6);
0500         repaint(oldrec.united(newrec));
0501     } else {
0502         QRect oldrec(10, -5 + oldvalue, 6, 11);
0503         QRect newrec(10, -5 + _value, 6, 11);
0504         repaint(oldrec.united(newrec));
0505     }
0506 }
0507 
0508 void KRuler::slotNewOffset(int _offset)
0509 {
0510     if (d->offset != _offset) {
0511         // setOffset(_offset);
0512         d->offset = _offset;
0513         repaint(contentsRect());
0514     }
0515 }
0516 
0517 void KRuler::slotEndOffset(int offset)
0518 {
0519     int tmp;
0520     if (d->lengthFix) {
0521         tmp = width() - offset;
0522     } else {
0523         tmp = offset;
0524     }
0525     if (d->endOffset_length != tmp) {
0526         d->endOffset_length = tmp;
0527         repaint(contentsRect());
0528     }
0529 }
0530 
0531 void KRuler::paintEvent(QPaintEvent * /*e*/)
0532 {
0533     //  debug ("KRuler::drawContents, %s",(horizontal==dir)?"horizontal":"vertical");
0534 
0535     QStylePainter p(this);
0536 #ifdef PROFILING
0537     QTime time;
0538     time.start();
0539     for (int profile = 0; profile < 10; profile++) {
0540 #endif
0541 
0542         int value = this->value();
0543         int minval = minimum();
0544         int maxval;
0545         if (d->dir == Qt::Horizontal) {
0546             maxval = maximum() + d->offset - (d->lengthFix ? (height() - d->endOffset_length) : d->endOffset_length);
0547         } else {
0548             maxval = maximum() + d->offset - (d->lengthFix ? (width() - d->endOffset_length) : d->endOffset_length);
0549         }
0550         // ioffsetval = value-offset;
0551         //    pixelpm = (int)ppm;
0552         //    left  = clip.left(),
0553         //    right = clip.right();
0554         double f;
0555         double fend;
0556         double offsetmin = (double)(minval - d->offset);
0557         double offsetmax = (double)(maxval - d->offset);
0558         double fontOffset = (((double)minval) > offsetmin) ? (double)minval : offsetmin;
0559 
0560         // draw labels
0561         QFont font = p.font();
0562         font.setPointSize(LABEL_SIZE);
0563         p.setFont(font);
0564         // draw littlemarklabel
0565 
0566         // draw mediummarklabel
0567 
0568         // draw bigmarklabel
0569 
0570         // draw endlabel
0571         if (d->showEndL) {
0572             if (d->dir == Qt::Horizontal) {
0573                 p.translate(fontOffset, 0);
0574                 p.drawText(END_LABEL_X, END_LABEL_Y, d->endlabel);
0575             } else { // rotate text +pi/2 and move down a bit
0576                 // QFontMetrics fm(font);
0577 #ifdef KRULER_ROTATE_TEST
0578                 p.rotate(-90.0 + rotate);
0579                 p.translate(-8.0 - fontOffset - d->fontWidth + xtrans, ytrans);
0580 #else
0581             p.rotate(-90.0);
0582             p.translate(-8.0 - fontOffset - d->fontWidth, 0.0);
0583 #endif
0584                 p.drawText(END_LABEL_X, END_LABEL_Y, d->endlabel);
0585             }
0586             p.resetTransform();
0587         }
0588 
0589         // draw the tiny marks
0590         if (d->showtm) {
0591             fend = d->ppm * d->tmDist;
0592             for (f = offsetmin; f < offsetmax; f += fend) {
0593                 if (d->dir == Qt::Horizontal) {
0594                     p.drawLine((int)f, BASE_MARK_X1, (int)f, BASE_MARK_X2);
0595                 } else {
0596                     p.drawLine(BASE_MARK_X1, (int)f, BASE_MARK_X2, (int)f);
0597                 }
0598             }
0599         }
0600         if (d->showlm) {
0601             // draw the little marks
0602             fend = d->ppm * d->lmDist;
0603             for (f = offsetmin; f < offsetmax; f += fend) {
0604                 if (d->dir == Qt::Horizontal) {
0605                     p.drawLine((int)f, LITTLE_MARK_X1, (int)f, LITTLE_MARK_X2);
0606                 } else {
0607                     p.drawLine(LITTLE_MARK_X1, (int)f, LITTLE_MARK_X2, (int)f);
0608                 }
0609             }
0610         }
0611         if (d->showmm) {
0612             // draw medium marks
0613             fend = d->ppm * d->mmDist;
0614             for (f = offsetmin; f < offsetmax; f += fend) {
0615                 if (d->dir == Qt::Horizontal) {
0616                     p.drawLine((int)f, MIDDLE_MARK_X1, (int)f, MIDDLE_MARK_X2);
0617                 } else {
0618                     p.drawLine(MIDDLE_MARK_X1, (int)f, MIDDLE_MARK_X2, (int)f);
0619                 }
0620             }
0621         }
0622         if (d->showbm) {
0623             // draw big marks
0624             fend = d->ppm * d->bmDist;
0625             for (f = offsetmin; f < offsetmax; f += fend) {
0626                 if (d->dir == Qt::Horizontal) {
0627                     p.drawLine((int)f, BIG_MARK_X1, (int)f, BIG_MARK_X2);
0628                 } else {
0629                     p.drawLine(BIG_MARK_X1, (int)f, BIG_MARK_X2, (int)f);
0630                 }
0631             }
0632         }
0633         if (d->showem) {
0634             // draw end marks
0635             if (d->dir == Qt::Horizontal) {
0636                 p.drawLine(minval - d->offset, END_MARK_X1, minval - d->offset, END_MARK_X2);
0637                 p.drawLine(maxval - d->offset, END_MARK_X1, maxval - d->offset, END_MARK_X2);
0638             } else {
0639                 p.drawLine(END_MARK_X1, minval - d->offset, END_MARK_X2, minval - d->offset);
0640                 p.drawLine(END_MARK_X1, maxval - d->offset, END_MARK_X2, maxval - d->offset);
0641             }
0642         }
0643 
0644         // draw pointer
0645         if (d->showpointer) {
0646             QPolygon pa(4);
0647             if (d->dir == Qt::Horizontal) {
0648                 pa.setPoints(3, value - 5, 10, value + 5, 10, value /*+0*/, 15);
0649             } else {
0650                 pa.setPoints(3, 10, value - 5, 10, value + 5, 15, value /*+0*/);
0651             }
0652             p.setBrush(p.background().color());
0653             p.drawPolygon(pa);
0654         }
0655 
0656 #ifdef PROFILING
0657     }
0658     int elapsed = time.elapsed();
0659     debug("paint time %i", elapsed);
0660 #endif
0661 }
0662 
0663 #include "moc_kruler.cpp"