File indexing completed on 2024-04-28 15:32: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 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 0)
0165 void KRuler::setMinValue(int value)
0166 {
0167     setMinimum(value);
0168 }
0169 #endif
0170 
0171 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 0)
0172 int KRuler::minValue() const
0173 {
0174     return minimum();
0175 }
0176 #endif
0177 
0178 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 0)
0179 void KRuler::setMaxValue(int value)
0180 {
0181     setMaximum(value);
0182 }
0183 #endif
0184 
0185 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 0)
0186 int KRuler::maxValue() const
0187 {
0188     return maximum();
0189 }
0190 #endif
0191 
0192 void KRuler::setTinyMarkDistance(int dist)
0193 {
0194     if (dist != d->tmDist) {
0195         d->tmDist = dist;
0196         update(contentsRect());
0197     }
0198 }
0199 
0200 int KRuler::tinyMarkDistance() const
0201 {
0202     return d->tmDist;
0203 }
0204 
0205 void KRuler::setLittleMarkDistance(int dist)
0206 {
0207     if (dist != d->lmDist) {
0208         d->lmDist = dist;
0209         update(contentsRect());
0210     }
0211 }
0212 
0213 int KRuler::littleMarkDistance() const
0214 {
0215     return d->lmDist;
0216 }
0217 
0218 void KRuler::setMediumMarkDistance(int dist)
0219 {
0220     if (dist != d->mmDist) {
0221         d->mmDist = dist;
0222         update(contentsRect());
0223     }
0224 }
0225 
0226 int KRuler::mediumMarkDistance() const
0227 {
0228     return d->mmDist;
0229 }
0230 
0231 void KRuler::setBigMarkDistance(int dist)
0232 {
0233     if (dist != d->bmDist) {
0234         d->bmDist = dist;
0235         update(contentsRect());
0236     }
0237 }
0238 
0239 int KRuler::bigMarkDistance() const
0240 {
0241     return d->bmDist;
0242 }
0243 
0244 void KRuler::setShowTinyMarks(bool show)
0245 {
0246     if (show != d->showtm) {
0247         d->showtm = show;
0248         update(contentsRect());
0249     }
0250 }
0251 
0252 bool KRuler::showTinyMarks() const
0253 {
0254     return d->showtm;
0255 }
0256 
0257 void KRuler::setShowLittleMarks(bool show)
0258 {
0259     if (show != d->showlm) {
0260         d->showlm = show;
0261         update(contentsRect());
0262     }
0263 }
0264 
0265 bool KRuler::showLittleMarks() const
0266 {
0267     return d->showlm;
0268 }
0269 
0270 void KRuler::setShowMediumMarks(bool show)
0271 {
0272     if (show != d->showmm) {
0273         d->showmm = show;
0274         update(contentsRect());
0275     }
0276 }
0277 
0278 bool KRuler::showMediumMarks() const
0279 {
0280     return d->showmm;
0281 }
0282 
0283 void KRuler::setShowBigMarks(bool show)
0284 {
0285     if (show != d->showbm) {
0286         d->showbm = show;
0287         update(contentsRect());
0288     }
0289 }
0290 
0291 bool KRuler::showBigMarks() const
0292 {
0293     return d->showbm;
0294 }
0295 
0296 void KRuler::setShowEndMarks(bool show)
0297 {
0298     if (show != d->showem) {
0299         d->showem = show;
0300         update(contentsRect());
0301     }
0302 }
0303 
0304 bool KRuler::showEndMarks() const
0305 {
0306     return d->showem;
0307 }
0308 
0309 void KRuler::setShowPointer(bool show)
0310 {
0311     if (show != d->showpointer) {
0312         d->showpointer = show;
0313         update(contentsRect());
0314     }
0315 }
0316 
0317 bool KRuler::showPointer() const
0318 {
0319     return d->showpointer;
0320 }
0321 
0322 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 0)
0323 void KRuler::setFrameStyle(int)
0324 {
0325 }
0326 #endif
0327 
0328 void KRuler::setShowEndLabel(bool show)
0329 {
0330     if (d->showEndL != show) {
0331         d->showEndL = show;
0332         update(contentsRect());
0333     }
0334 }
0335 
0336 bool KRuler::showEndLabel() const
0337 {
0338     return d->showEndL;
0339 }
0340 
0341 void KRuler::setEndLabel(const QString &label)
0342 {
0343     d->endlabel = label;
0344 
0345     // premeasure the fontwidth and save it
0346     if (d->dir == Qt::Vertical) {
0347         QFont font = this->font();
0348         font.setPointSize(LABEL_SIZE);
0349         QFontMetrics fm(font);
0350         d->fontWidth = fm.horizontalAdvance(d->endlabel);
0351     }
0352     update(contentsRect());
0353 }
0354 
0355 QString KRuler::endLabel() const
0356 {
0357     return d->endlabel;
0358 }
0359 
0360 void KRuler::setRulerMetricStyle(KRuler::MetricStyle style)
0361 {
0362     switch (style) {
0363     default: /* fall through */
0364     case Custom:
0365         return;
0366     case Pixel:
0367         setLittleMarkDistance(1);
0368         setMediumMarkDistance(5);
0369         setBigMarkDistance(10);
0370 
0371         setShowTinyMarks(false);
0372         setShowLittleMarks(true);
0373         setShowMediumMarks(true);
0374         setShowBigMarks(true);
0375         setShowEndMarks(true);
0376 
0377         update(contentsRect());
0378         setPixelPerMark(10.0);
0379 
0380         break;
0381     case Inch:
0382         setTinyMarkDistance(1);
0383         setLittleMarkDistance(2);
0384         setMediumMarkDistance(4);
0385         setBigMarkDistance(8);
0386 
0387         setShowTinyMarks(true);
0388         setShowLittleMarks(true);
0389         setShowMediumMarks(true);
0390         setShowBigMarks(true);
0391         setShowEndMarks(true);
0392 
0393         update(contentsRect());
0394         setPixelPerMark(9.0);
0395 
0396         break;
0397     case Millimetres: /* fall through */
0398     case Centimetres: /* fall through */
0399     case Metres:
0400         setLittleMarkDistance(1);
0401         setMediumMarkDistance(5);
0402         setBigMarkDistance(10);
0403 
0404         setShowTinyMarks(false);
0405         setShowLittleMarks(true);
0406         setShowMediumMarks(true);
0407         setShowBigMarks(true);
0408         setShowEndMarks(true);
0409 
0410         update(contentsRect());
0411         setPixelPerMark(3.0);
0412     }
0413     switch (style) {
0414     case Pixel:
0415         setEndLabel(QStringLiteral("pixel"));
0416         break;
0417     case Inch:
0418         setEndLabel(QStringLiteral("inch"));
0419         break;
0420     case Millimetres:
0421         setEndLabel(QStringLiteral("mm"));
0422         break;
0423     case Centimetres:
0424         setEndLabel(QStringLiteral("cm"));
0425         break;
0426     case Metres:
0427         setEndLabel(QStringLiteral("m"));
0428     default: /* never reached, see above switch */
0429              /* empty command */;
0430     }
0431     // if the style changes one of the values,
0432     // update would have been called inside the methods
0433     // -> no update() call needed here !
0434 }
0435 
0436 void KRuler::setPixelPerMark(double rate)
0437 {
0438     // never compare floats against each other :)
0439     d->ppm = rate;
0440     update(contentsRect());
0441 }
0442 
0443 double KRuler::pixelPerMark() const
0444 {
0445     return d->ppm;
0446 }
0447 
0448 void KRuler::setLength(int length)
0449 {
0450     int tmp;
0451     if (d->lengthFix) {
0452         tmp = length;
0453     } else {
0454         tmp = width() - length;
0455     }
0456     if (tmp != d->endOffset_length) {
0457         d->endOffset_length = tmp;
0458         update(contentsRect());
0459     }
0460 }
0461 
0462 int KRuler::length() const
0463 {
0464     if (d->lengthFix) {
0465         return d->endOffset_length;
0466     }
0467     return (width() - d->endOffset_length);
0468 }
0469 
0470 void KRuler::setLengthFixed(bool fix)
0471 {
0472     d->lengthFix = fix;
0473 }
0474 
0475 bool KRuler::lengthFixed() const
0476 {
0477     return d->lengthFix;
0478 }
0479 
0480 void KRuler::setOffset(int _offset)
0481 {
0482     // debug("set offset %i", _offset);
0483     if (d->offset != _offset) {
0484         d->offset = _offset;
0485         update(contentsRect());
0486     }
0487 }
0488 
0489 int KRuler::offset() const
0490 {
0491     return d->offset;
0492 }
0493 
0494 int KRuler::endOffset() const
0495 {
0496     if (d->lengthFix) {
0497         return (width() - d->endOffset_length);
0498     }
0499     return d->endOffset_length;
0500 }
0501 
0502 void KRuler::slideUp(int count)
0503 {
0504     if (count) {
0505         d->offset += count;
0506         update(contentsRect());
0507     }
0508 }
0509 
0510 void KRuler::slideDown(int count)
0511 {
0512     if (count) {
0513         d->offset -= count;
0514         update(contentsRect());
0515     }
0516 }
0517 
0518 void KRuler::slotNewValue(int _value)
0519 {
0520     int oldvalue = value();
0521     if (oldvalue == _value) {
0522         return;
0523     }
0524     //    setValue(_value);
0525     setValue(_value);
0526     if (value() == oldvalue) {
0527         return;
0528     }
0529     // get the rectangular of the old and the new ruler pointer
0530     // and repaint only him
0531     if (d->dir == Qt::Horizontal) {
0532         QRect oldrec(-5 + oldvalue, 10, 11, 6);
0533         QRect newrec(-5 + _value, 10, 11, 6);
0534         repaint(oldrec.united(newrec));
0535     } else {
0536         QRect oldrec(10, -5 + oldvalue, 6, 11);
0537         QRect newrec(10, -5 + _value, 6, 11);
0538         repaint(oldrec.united(newrec));
0539     }
0540 }
0541 
0542 void KRuler::slotNewOffset(int _offset)
0543 {
0544     if (d->offset != _offset) {
0545         // setOffset(_offset);
0546         d->offset = _offset;
0547         repaint(contentsRect());
0548     }
0549 }
0550 
0551 void KRuler::slotEndOffset(int offset)
0552 {
0553     int tmp;
0554     if (d->lengthFix) {
0555         tmp = width() - offset;
0556     } else {
0557         tmp = offset;
0558     }
0559     if (d->endOffset_length != tmp) {
0560         d->endOffset_length = tmp;
0561         repaint(contentsRect());
0562     }
0563 }
0564 
0565 void KRuler::paintEvent(QPaintEvent * /*e*/)
0566 {
0567     //  debug ("KRuler::drawContents, %s",(horizontal==dir)?"horizontal":"vertical");
0568 
0569     QStylePainter p(this);
0570 #ifdef PROFILING
0571     QTime time;
0572     time.start();
0573     for (int profile = 0; profile < 10; profile++) {
0574 #endif
0575 
0576         int value = this->value();
0577         int minval = minimum();
0578         int maxval;
0579         if (d->dir == Qt::Horizontal) {
0580             maxval = maximum() + d->offset - (d->lengthFix ? (height() - d->endOffset_length) : d->endOffset_length);
0581         } else {
0582             maxval = maximum() + d->offset - (d->lengthFix ? (width() - d->endOffset_length) : d->endOffset_length);
0583         }
0584         // ioffsetval = value-offset;
0585         //    pixelpm = (int)ppm;
0586         //    left  = clip.left(),
0587         //    right = clip.right();
0588         double f;
0589         double fend;
0590         double offsetmin = (double)(minval - d->offset);
0591         double offsetmax = (double)(maxval - d->offset);
0592         double fontOffset = (((double)minval) > offsetmin) ? (double)minval : offsetmin;
0593 
0594         // draw labels
0595         QFont font = p.font();
0596         font.setPointSize(LABEL_SIZE);
0597         p.setFont(font);
0598         // draw littlemarklabel
0599 
0600         // draw mediummarklabel
0601 
0602         // draw bigmarklabel
0603 
0604         // draw endlabel
0605         if (d->showEndL) {
0606             if (d->dir == Qt::Horizontal) {
0607                 p.translate(fontOffset, 0);
0608                 p.drawText(END_LABEL_X, END_LABEL_Y, d->endlabel);
0609             } else { // rotate text +pi/2 and move down a bit
0610                 // QFontMetrics fm(font);
0611 #ifdef KRULER_ROTATE_TEST
0612                 p.rotate(-90.0 + rotate);
0613                 p.translate(-8.0 - fontOffset - d->fontWidth + xtrans, ytrans);
0614 #else
0615             p.rotate(-90.0);
0616             p.translate(-8.0 - fontOffset - d->fontWidth, 0.0);
0617 #endif
0618                 p.drawText(END_LABEL_X, END_LABEL_Y, d->endlabel);
0619             }
0620             p.resetTransform();
0621         }
0622 
0623         // draw the tiny marks
0624         if (d->showtm) {
0625             fend = d->ppm * d->tmDist;
0626             for (f = offsetmin; f < offsetmax; f += fend) {
0627                 if (d->dir == Qt::Horizontal) {
0628                     p.drawLine((int)f, BASE_MARK_X1, (int)f, BASE_MARK_X2);
0629                 } else {
0630                     p.drawLine(BASE_MARK_X1, (int)f, BASE_MARK_X2, (int)f);
0631                 }
0632             }
0633         }
0634         if (d->showlm) {
0635             // draw the little marks
0636             fend = d->ppm * d->lmDist;
0637             for (f = offsetmin; f < offsetmax; f += fend) {
0638                 if (d->dir == Qt::Horizontal) {
0639                     p.drawLine((int)f, LITTLE_MARK_X1, (int)f, LITTLE_MARK_X2);
0640                 } else {
0641                     p.drawLine(LITTLE_MARK_X1, (int)f, LITTLE_MARK_X2, (int)f);
0642                 }
0643             }
0644         }
0645         if (d->showmm) {
0646             // draw medium marks
0647             fend = d->ppm * d->mmDist;
0648             for (f = offsetmin; f < offsetmax; f += fend) {
0649                 if (d->dir == Qt::Horizontal) {
0650                     p.drawLine((int)f, MIDDLE_MARK_X1, (int)f, MIDDLE_MARK_X2);
0651                 } else {
0652                     p.drawLine(MIDDLE_MARK_X1, (int)f, MIDDLE_MARK_X2, (int)f);
0653                 }
0654             }
0655         }
0656         if (d->showbm) {
0657             // draw big marks
0658             fend = d->ppm * d->bmDist;
0659             for (f = offsetmin; f < offsetmax; f += fend) {
0660                 if (d->dir == Qt::Horizontal) {
0661                     p.drawLine((int)f, BIG_MARK_X1, (int)f, BIG_MARK_X2);
0662                 } else {
0663                     p.drawLine(BIG_MARK_X1, (int)f, BIG_MARK_X2, (int)f);
0664                 }
0665             }
0666         }
0667         if (d->showem) {
0668             // draw end marks
0669             if (d->dir == Qt::Horizontal) {
0670                 p.drawLine(minval - d->offset, END_MARK_X1, minval - d->offset, END_MARK_X2);
0671                 p.drawLine(maxval - d->offset, END_MARK_X1, maxval - d->offset, END_MARK_X2);
0672             } else {
0673                 p.drawLine(END_MARK_X1, minval - d->offset, END_MARK_X2, minval - d->offset);
0674                 p.drawLine(END_MARK_X1, maxval - d->offset, END_MARK_X2, maxval - d->offset);
0675             }
0676         }
0677 
0678         // draw pointer
0679         if (d->showpointer) {
0680             QPolygon pa(4);
0681             if (d->dir == Qt::Horizontal) {
0682                 pa.setPoints(3, value - 5, 10, value + 5, 10, value /*+0*/, 15);
0683             } else {
0684                 pa.setPoints(3, 10, value - 5, 10, value + 5, 15, value /*+0*/);
0685             }
0686             p.setBrush(p.background().color());
0687             p.drawPolygon(pa);
0688         }
0689 
0690 #ifdef PROFILING
0691     }
0692     int elapsed = time.elapsed();
0693     debug("paint time %i", elapsed);
0694 #endif
0695 }
0696 
0697 #include "moc_kruler.cpp"