1 | /* |
2 | This file is part of the KDE libraries |
3 | SPDX-FileCopyrightText: 1998 Jörg Habenicht <j.habenicht@europemail.com> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.0-or-later |
6 | */ |
7 | |
8 | #include "kruler.h" |
9 | |
10 | #include <QFont> |
11 | #include <QPolygon> |
12 | #include <QStylePainter> |
13 | |
14 | #define INIT_VALUE 0 |
15 | #define INIT_MIN_VALUE 0 |
16 | #define INIT_MAX_VALUE 100 |
17 | #define INIT_TINY_MARK_DISTANCE 1 |
18 | #define INIT_LITTLE_MARK_DISTANCE 5 |
19 | #define INIT_MIDDLE_MARK_DISTANCE (INIT_LITTLE_MARK_DISTANCE * 2) |
20 | #define INIT_BIG_MARK_DISTANCE (INIT_LITTLE_MARK_DISTANCE * 10) |
21 | #define INIT_SHOW_TINY_MARK false |
22 | #define INIT_SHOW_LITTLE_MARK true |
23 | #define INIT_SHOW_MEDIUM_MARK true |
24 | #define INIT_SHOW_BIG_MARK true |
25 | #define INIT_SHOW_END_MARK true |
26 | #define INIT_SHOW_POINTER true |
27 | #define INIT_SHOW_END_LABEL true |
28 | |
29 | #define INIT_PIXEL_PER_MARK (double)10.0 /* distance between 2 base marks in pixel */ |
30 | #define INIT_OFFSET (-20) |
31 | #define INIT_LENGTH_FIX true |
32 | #define INIT_END_OFFSET 0 |
33 | |
34 | #define FIX_WIDTH 20 /* widget width in pixel */ |
35 | #define LINE_END (FIX_WIDTH - 3) |
36 | #define END_MARK_LENGTH (FIX_WIDTH - 6) |
37 | #define END_MARK_X2 LINE_END |
38 | #define END_MARK_X1 (END_MARK_X2 - END_MARK_LENGTH) |
39 | #define BIG_MARK_LENGTH (END_MARK_LENGTH * 3 / 4) |
40 | #define BIG_MARK_X2 LINE_END |
41 | #define BIG_MARK_X1 (BIG_MARK_X2 - BIG_MARK_LENGTH) |
42 | #define MIDDLE_MARK_LENGTH (END_MARK_LENGTH / 2) |
43 | #define MIDDLE_MARK_X2 LINE_END |
44 | #define MIDDLE_MARK_X1 (MIDDLE_MARK_X2 - MIDDLE_MARK_LENGTH) |
45 | #define LITTLE_MARK_LENGTH (MIDDLE_MARK_LENGTH / 2) |
46 | #define LITTLE_MARK_X2 LINE_END |
47 | #define LITTLE_MARK_X1 (LITTLE_MARK_X2 - LITTLE_MARK_LENGTH) |
48 | #define BASE_MARK_LENGTH (LITTLE_MARK_LENGTH / 2) |
49 | #define BASE_MARK_X2 LINE_END |
50 | #define BASE_MARK_X1 (BASE_MARK_X2 - BASE_MARK_LENGTH) |
51 | |
52 | #define LABEL_SIZE 8 |
53 | #define END_LABEL_X 4 |
54 | #define END_LABEL_Y (END_LABEL_X + LABEL_SIZE - 2) |
55 | |
56 | #undef PROFILING |
57 | |
58 | #ifdef PROFILING |
59 | #include <qdatetime.h> |
60 | #endif |
61 | |
62 | class KRulerPrivate |
63 | { |
64 | public: |
65 | int endOffset_length; /* marks the offset at the end of the ruler |
66 | * i.e. right side at horizontal and down side |
67 | * at vertical rulers. |
68 | * the ruler end mark is moved endOffset_length |
69 | * ticks away from the widget end. |
70 | * positive offset moves end mark inside the ruler. |
71 | * if lengthFix is true, endOffset_length holds the |
72 | * length of the ruler. |
73 | */ |
74 | int fontWidth; // ONLY valid for vertical rulers |
75 | |
76 | QAbstractSlider range; |
77 | Qt::Orientation dir; |
78 | int tmDist; |
79 | int lmDist; |
80 | int mmDist; |
81 | int bmDist; |
82 | int offset; |
83 | bool showtm : 1; /* show tiny, little, medium, big, endmarks */ |
84 | bool showlm : 1; |
85 | bool showmm : 1; |
86 | bool showbm : 1; |
87 | bool showem : 1; |
88 | |
89 | bool showpointer : 1; |
90 | bool showEndL : 1; |
91 | bool lengthFix : 1; |
92 | |
93 | double ppm; /* pixel per mark */ |
94 | |
95 | QString endlabel; |
96 | }; |
97 | |
98 | KRuler::KRuler(QWidget *parent) |
99 | : QAbstractSlider(parent) |
100 | , d(new KRulerPrivate) |
101 | { |
102 | setRange(INIT_MIN_VALUE, INIT_MAX_VALUE); |
103 | setPageStep(10); |
104 | setValue(INIT_VALUE); |
105 | initWidget(orientation: Qt::Horizontal); |
106 | setFixedHeight(FIX_WIDTH); |
107 | } |
108 | |
109 | KRuler::KRuler(Qt::Orientation orient, QWidget *parent, Qt::WindowFlags f) |
110 | : QAbstractSlider(parent) |
111 | , d(new KRulerPrivate) |
112 | { |
113 | setRange(INIT_MIN_VALUE, INIT_MAX_VALUE); |
114 | setPageStep(10); |
115 | setValue(INIT_VALUE); |
116 | setWindowFlags(f); |
117 | initWidget(orientation: orient); |
118 | if (orient == Qt::Horizontal) { |
119 | setFixedHeight(FIX_WIDTH); |
120 | } else { |
121 | setFixedWidth(FIX_WIDTH); |
122 | } |
123 | } |
124 | |
125 | KRuler::KRuler(Qt::Orientation orient, int widgetWidth, QWidget *parent, Qt::WindowFlags f) |
126 | : QAbstractSlider(parent) |
127 | , d(new KRulerPrivate) |
128 | { |
129 | setRange(INIT_MIN_VALUE, INIT_MAX_VALUE); |
130 | setPageStep(10); |
131 | setValue(INIT_VALUE); |
132 | setWindowFlags(f); |
133 | initWidget(orientation: orient); |
134 | if (orient == Qt::Horizontal) { |
135 | setFixedHeight(widgetWidth); |
136 | } else { |
137 | setFixedWidth(widgetWidth); |
138 | } |
139 | } |
140 | |
141 | void KRuler::initWidget(Qt::Orientation orientation) |
142 | { |
143 | d->showpointer = INIT_SHOW_POINTER; |
144 | d->showEndL = INIT_SHOW_END_LABEL; |
145 | d->lengthFix = INIT_LENGTH_FIX; |
146 | d->endOffset_length = INIT_END_OFFSET; |
147 | |
148 | d->tmDist = INIT_TINY_MARK_DISTANCE; |
149 | d->lmDist = INIT_LITTLE_MARK_DISTANCE; |
150 | d->mmDist = INIT_MIDDLE_MARK_DISTANCE; |
151 | d->bmDist = INIT_BIG_MARK_DISTANCE; |
152 | d->offset = INIT_OFFSET; |
153 | d->showtm = INIT_SHOW_TINY_MARK; |
154 | d->showlm = INIT_SHOW_LITTLE_MARK; |
155 | d->showmm = INIT_SHOW_MEDIUM_MARK; |
156 | d->showbm = INIT_SHOW_BIG_MARK; |
157 | d->showem = INIT_SHOW_END_MARK; |
158 | d->ppm = INIT_PIXEL_PER_MARK; |
159 | d->dir = orientation; |
160 | } |
161 | |
162 | KRuler::~KRuler() = default; |
163 | |
164 | void KRuler::setTinyMarkDistance(int dist) |
165 | { |
166 | if (dist != d->tmDist) { |
167 | d->tmDist = dist; |
168 | update(contentsRect()); |
169 | } |
170 | } |
171 | |
172 | int KRuler::tinyMarkDistance() const |
173 | { |
174 | return d->tmDist; |
175 | } |
176 | |
177 | void KRuler::setLittleMarkDistance(int dist) |
178 | { |
179 | if (dist != d->lmDist) { |
180 | d->lmDist = dist; |
181 | update(contentsRect()); |
182 | } |
183 | } |
184 | |
185 | int KRuler::littleMarkDistance() const |
186 | { |
187 | return d->lmDist; |
188 | } |
189 | |
190 | void KRuler::setMediumMarkDistance(int dist) |
191 | { |
192 | if (dist != d->mmDist) { |
193 | d->mmDist = dist; |
194 | update(contentsRect()); |
195 | } |
196 | } |
197 | |
198 | int KRuler::mediumMarkDistance() const |
199 | { |
200 | return d->mmDist; |
201 | } |
202 | |
203 | void KRuler::setBigMarkDistance(int dist) |
204 | { |
205 | if (dist != d->bmDist) { |
206 | d->bmDist = dist; |
207 | update(contentsRect()); |
208 | } |
209 | } |
210 | |
211 | int KRuler::bigMarkDistance() const |
212 | { |
213 | return d->bmDist; |
214 | } |
215 | |
216 | void KRuler::setShowTinyMarks(bool show) |
217 | { |
218 | if (show != d->showtm) { |
219 | d->showtm = show; |
220 | update(contentsRect()); |
221 | } |
222 | } |
223 | |
224 | bool KRuler::showTinyMarks() const |
225 | { |
226 | return d->showtm; |
227 | } |
228 | |
229 | void KRuler::setShowLittleMarks(bool show) |
230 | { |
231 | if (show != d->showlm) { |
232 | d->showlm = show; |
233 | update(contentsRect()); |
234 | } |
235 | } |
236 | |
237 | bool KRuler::showLittleMarks() const |
238 | { |
239 | return d->showlm; |
240 | } |
241 | |
242 | void KRuler::setShowMediumMarks(bool show) |
243 | { |
244 | if (show != d->showmm) { |
245 | d->showmm = show; |
246 | update(contentsRect()); |
247 | } |
248 | } |
249 | |
250 | bool KRuler::showMediumMarks() const |
251 | { |
252 | return d->showmm; |
253 | } |
254 | |
255 | void KRuler::setShowBigMarks(bool show) |
256 | { |
257 | if (show != d->showbm) { |
258 | d->showbm = show; |
259 | update(contentsRect()); |
260 | } |
261 | } |
262 | |
263 | bool KRuler::showBigMarks() const |
264 | { |
265 | return d->showbm; |
266 | } |
267 | |
268 | void KRuler::setShowEndMarks(bool show) |
269 | { |
270 | if (show != d->showem) { |
271 | d->showem = show; |
272 | update(contentsRect()); |
273 | } |
274 | } |
275 | |
276 | bool KRuler::showEndMarks() const |
277 | { |
278 | return d->showem; |
279 | } |
280 | |
281 | void KRuler::setShowPointer(bool show) |
282 | { |
283 | if (show != d->showpointer) { |
284 | d->showpointer = show; |
285 | update(contentsRect()); |
286 | } |
287 | } |
288 | |
289 | bool KRuler::showPointer() const |
290 | { |
291 | return d->showpointer; |
292 | } |
293 | |
294 | void KRuler::setShowEndLabel(bool show) |
295 | { |
296 | if (d->showEndL != show) { |
297 | d->showEndL = show; |
298 | update(contentsRect()); |
299 | } |
300 | } |
301 | |
302 | bool KRuler::showEndLabel() const |
303 | { |
304 | return d->showEndL; |
305 | } |
306 | |
307 | void KRuler::setEndLabel(const QString &label) |
308 | { |
309 | d->endlabel = label; |
310 | |
311 | // premeasure the fontwidth and save it |
312 | if (d->dir == Qt::Vertical) { |
313 | QFont font = this->font(); |
314 | font.setPointSize(LABEL_SIZE); |
315 | QFontMetrics fm(font); |
316 | d->fontWidth = fm.horizontalAdvance(d->endlabel); |
317 | } |
318 | update(contentsRect()); |
319 | } |
320 | |
321 | QString KRuler::endLabel() const |
322 | { |
323 | return d->endlabel; |
324 | } |
325 | |
326 | void KRuler::setRulerMetricStyle(KRuler::MetricStyle style) |
327 | { |
328 | switch (style) { |
329 | default: /* fall through */ |
330 | case Custom: |
331 | return; |
332 | case Pixel: |
333 | setLittleMarkDistance(1); |
334 | setMediumMarkDistance(5); |
335 | setBigMarkDistance(10); |
336 | |
337 | setShowTinyMarks(false); |
338 | setShowLittleMarks(true); |
339 | setShowMediumMarks(true); |
340 | setShowBigMarks(true); |
341 | setShowEndMarks(true); |
342 | |
343 | update(contentsRect()); |
344 | setPixelPerMark(10.0); |
345 | |
346 | break; |
347 | case Inch: |
348 | setTinyMarkDistance(1); |
349 | setLittleMarkDistance(2); |
350 | setMediumMarkDistance(4); |
351 | setBigMarkDistance(8); |
352 | |
353 | setShowTinyMarks(true); |
354 | setShowLittleMarks(true); |
355 | setShowMediumMarks(true); |
356 | setShowBigMarks(true); |
357 | setShowEndMarks(true); |
358 | |
359 | update(contentsRect()); |
360 | setPixelPerMark(9.0); |
361 | |
362 | break; |
363 | case Millimetres: /* fall through */ |
364 | case Centimetres: /* fall through */ |
365 | case Metres: |
366 | setLittleMarkDistance(1); |
367 | setMediumMarkDistance(5); |
368 | setBigMarkDistance(10); |
369 | |
370 | setShowTinyMarks(false); |
371 | setShowLittleMarks(true); |
372 | setShowMediumMarks(true); |
373 | setShowBigMarks(true); |
374 | setShowEndMarks(true); |
375 | |
376 | update(contentsRect()); |
377 | setPixelPerMark(3.0); |
378 | } |
379 | switch (style) { |
380 | case Pixel: |
381 | setEndLabel(QStringLiteral("pixel" )); |
382 | break; |
383 | case Inch: |
384 | setEndLabel(QStringLiteral("inch" )); |
385 | break; |
386 | case Millimetres: |
387 | setEndLabel(QStringLiteral("mm" )); |
388 | break; |
389 | case Centimetres: |
390 | setEndLabel(QStringLiteral("cm" )); |
391 | break; |
392 | case Metres: |
393 | setEndLabel(QStringLiteral("m" )); |
394 | default: /* never reached, see above switch */ |
395 | /* empty command */; |
396 | } |
397 | // if the style changes one of the values, |
398 | // update would have been called inside the methods |
399 | // -> no update() call needed here ! |
400 | } |
401 | |
402 | void KRuler::setPixelPerMark(double rate) |
403 | { |
404 | // never compare floats against each other :) |
405 | d->ppm = rate; |
406 | update(contentsRect()); |
407 | } |
408 | |
409 | double KRuler::pixelPerMark() const |
410 | { |
411 | return d->ppm; |
412 | } |
413 | |
414 | void KRuler::setLength(int length) |
415 | { |
416 | int tmp; |
417 | if (d->lengthFix) { |
418 | tmp = length; |
419 | } else { |
420 | tmp = width() - length; |
421 | } |
422 | if (tmp != d->endOffset_length) { |
423 | d->endOffset_length = tmp; |
424 | update(contentsRect()); |
425 | } |
426 | } |
427 | |
428 | int KRuler::length() const |
429 | { |
430 | if (d->lengthFix) { |
431 | return d->endOffset_length; |
432 | } |
433 | return (width() - d->endOffset_length); |
434 | } |
435 | |
436 | void KRuler::setLengthFixed(bool fix) |
437 | { |
438 | d->lengthFix = fix; |
439 | } |
440 | |
441 | bool KRuler::lengthFixed() const |
442 | { |
443 | return d->lengthFix; |
444 | } |
445 | |
446 | void KRuler::setOffset(int _offset) |
447 | { |
448 | // debug("set offset %i", _offset); |
449 | if (d->offset != _offset) { |
450 | d->offset = _offset; |
451 | update(contentsRect()); |
452 | } |
453 | } |
454 | |
455 | int KRuler::offset() const |
456 | { |
457 | return d->offset; |
458 | } |
459 | |
460 | int KRuler::endOffset() const |
461 | { |
462 | if (d->lengthFix) { |
463 | return (width() - d->endOffset_length); |
464 | } |
465 | return d->endOffset_length; |
466 | } |
467 | |
468 | void KRuler::slideUp(int count) |
469 | { |
470 | if (count) { |
471 | d->offset += count; |
472 | update(contentsRect()); |
473 | } |
474 | } |
475 | |
476 | void KRuler::slideDown(int count) |
477 | { |
478 | if (count) { |
479 | d->offset -= count; |
480 | update(contentsRect()); |
481 | } |
482 | } |
483 | |
484 | void KRuler::slotNewValue(int _value) |
485 | { |
486 | int oldvalue = value(); |
487 | if (oldvalue == _value) { |
488 | return; |
489 | } |
490 | // setValue(_value); |
491 | setValue(_value); |
492 | if (value() == oldvalue) { |
493 | return; |
494 | } |
495 | // get the rectangular of the old and the new ruler pointer |
496 | // and repaint only him |
497 | if (d->dir == Qt::Horizontal) { |
498 | QRect oldrec(-5 + oldvalue, 10, 11, 6); |
499 | QRect newrec(-5 + _value, 10, 11, 6); |
500 | repaint(oldrec.united(r: newrec)); |
501 | } else { |
502 | QRect oldrec(10, -5 + oldvalue, 6, 11); |
503 | QRect newrec(10, -5 + _value, 6, 11); |
504 | repaint(oldrec.united(r: newrec)); |
505 | } |
506 | } |
507 | |
508 | void KRuler::slotNewOffset(int _offset) |
509 | { |
510 | if (d->offset != _offset) { |
511 | // setOffset(_offset); |
512 | d->offset = _offset; |
513 | repaint(contentsRect()); |
514 | } |
515 | } |
516 | |
517 | void KRuler::slotEndOffset(int offset) |
518 | { |
519 | int tmp; |
520 | if (d->lengthFix) { |
521 | tmp = width() - offset; |
522 | } else { |
523 | tmp = offset; |
524 | } |
525 | if (d->endOffset_length != tmp) { |
526 | d->endOffset_length = tmp; |
527 | repaint(contentsRect()); |
528 | } |
529 | } |
530 | |
531 | void KRuler::paintEvent(QPaintEvent * /*e*/) |
532 | { |
533 | // debug ("KRuler::drawContents, %s",(horizontal==dir)?"horizontal":"vertical"); |
534 | |
535 | QStylePainter p(this); |
536 | #ifdef PROFILING |
537 | QTime time; |
538 | time.start(); |
539 | for (int profile = 0; profile < 10; profile++) { |
540 | #endif |
541 | |
542 | int value = this->value(); |
543 | int minval = minimum(); |
544 | int maxval; |
545 | if (d->dir == Qt::Horizontal) { |
546 | maxval = maximum() + d->offset - (d->lengthFix ? (height() - d->endOffset_length) : d->endOffset_length); |
547 | } else { |
548 | maxval = maximum() + d->offset - (d->lengthFix ? (width() - d->endOffset_length) : d->endOffset_length); |
549 | } |
550 | // ioffsetval = value-offset; |
551 | // pixelpm = (int)ppm; |
552 | // left = clip.left(), |
553 | // right = clip.right(); |
554 | double f; |
555 | double fend; |
556 | double offsetmin = (double)(minval - d->offset); |
557 | double offsetmax = (double)(maxval - d->offset); |
558 | double fontOffset = (((double)minval) > offsetmin) ? (double)minval : offsetmin; |
559 | |
560 | // draw labels |
561 | QFont font = p.font(); |
562 | font.setPointSize(LABEL_SIZE); |
563 | p.setFont(font); |
564 | // draw littlemarklabel |
565 | |
566 | // draw mediummarklabel |
567 | |
568 | // draw bigmarklabel |
569 | |
570 | // draw endlabel |
571 | if (d->showEndL) { |
572 | if (d->dir == Qt::Horizontal) { |
573 | p.translate(dx: fontOffset, dy: 0); |
574 | p.drawText(END_LABEL_X, END_LABEL_Y, s: d->endlabel); |
575 | } else { // rotate text +pi/2 and move down a bit |
576 | // QFontMetrics fm(font); |
577 | #ifdef KRULER_ROTATE_TEST |
578 | p.rotate(-90.0 + rotate); |
579 | p.translate(-8.0 - fontOffset - d->fontWidth + xtrans, ytrans); |
580 | #else |
581 | p.rotate(a: -90.0); |
582 | p.translate(dx: -8.0 - fontOffset - d->fontWidth, dy: 0.0); |
583 | #endif |
584 | p.drawText(END_LABEL_X, END_LABEL_Y, s: d->endlabel); |
585 | } |
586 | p.resetTransform(); |
587 | } |
588 | |
589 | // draw the tiny marks |
590 | if (d->showtm) { |
591 | fend = d->ppm * d->tmDist; |
592 | for (f = offsetmin; f < offsetmax; f += fend) { |
593 | if (d->dir == Qt::Horizontal) { |
594 | p.drawLine(x1: (int)f, BASE_MARK_X1, x2: (int)f, BASE_MARK_X2); |
595 | } else { |
596 | p.drawLine(BASE_MARK_X1, y1: (int)f, BASE_MARK_X2, y2: (int)f); |
597 | } |
598 | } |
599 | } |
600 | if (d->showlm) { |
601 | // draw the little marks |
602 | fend = d->ppm * d->lmDist; |
603 | for (f = offsetmin; f < offsetmax; f += fend) { |
604 | if (d->dir == Qt::Horizontal) { |
605 | p.drawLine(x1: (int)f, LITTLE_MARK_X1, x2: (int)f, LITTLE_MARK_X2); |
606 | } else { |
607 | p.drawLine(LITTLE_MARK_X1, y1: (int)f, LITTLE_MARK_X2, y2: (int)f); |
608 | } |
609 | } |
610 | } |
611 | if (d->showmm) { |
612 | // draw medium marks |
613 | fend = d->ppm * d->mmDist; |
614 | for (f = offsetmin; f < offsetmax; f += fend) { |
615 | if (d->dir == Qt::Horizontal) { |
616 | p.drawLine(x1: (int)f, MIDDLE_MARK_X1, x2: (int)f, MIDDLE_MARK_X2); |
617 | } else { |
618 | p.drawLine(MIDDLE_MARK_X1, y1: (int)f, MIDDLE_MARK_X2, y2: (int)f); |
619 | } |
620 | } |
621 | } |
622 | if (d->showbm) { |
623 | // draw big marks |
624 | fend = d->ppm * d->bmDist; |
625 | for (f = offsetmin; f < offsetmax; f += fend) { |
626 | if (d->dir == Qt::Horizontal) { |
627 | p.drawLine(x1: (int)f, BIG_MARK_X1, x2: (int)f, BIG_MARK_X2); |
628 | } else { |
629 | p.drawLine(BIG_MARK_X1, y1: (int)f, BIG_MARK_X2, y2: (int)f); |
630 | } |
631 | } |
632 | } |
633 | if (d->showem) { |
634 | // draw end marks |
635 | if (d->dir == Qt::Horizontal) { |
636 | p.drawLine(x1: minval - d->offset, END_MARK_X1, x2: minval - d->offset, END_MARK_X2); |
637 | p.drawLine(x1: maxval - d->offset, END_MARK_X1, x2: maxval - d->offset, END_MARK_X2); |
638 | } else { |
639 | p.drawLine(END_MARK_X1, y1: minval - d->offset, END_MARK_X2, y2: minval - d->offset); |
640 | p.drawLine(END_MARK_X1, y1: maxval - d->offset, END_MARK_X2, y2: maxval - d->offset); |
641 | } |
642 | } |
643 | |
644 | // draw pointer |
645 | if (d->showpointer) { |
646 | QPolygon pa(4); |
647 | if (d->dir == Qt::Horizontal) { |
648 | pa.setPoints(nPoints: 3, firstx: value - 5, firsty: 10, value + 5, 10, value /*+0*/, 15); |
649 | } else { |
650 | pa.setPoints(nPoints: 3, firstx: 10, firsty: value - 5, 10, value + 5, 15, value /*+0*/); |
651 | } |
652 | p.setBrush(p.background().color()); |
653 | p.drawPolygon(polygon: pa); |
654 | } |
655 | |
656 | #ifdef PROFILING |
657 | } |
658 | int elapsed = time.elapsed(); |
659 | debug("paint time %i" , elapsed); |
660 | #endif |
661 | } |
662 | |
663 | #include "moc_kruler.cpp" |
664 | |