1 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
2 * Qwt Widget Library
3 * Copyright (C) 1997 Josef Wilgen
4 * Copyright (C) 2002 Uwe Rathmann
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the Qwt License, Version 1.0
8 *****************************************************************************/
9
10 #include <math.h>
11 #include <qpainter.h>
12 #if QT_VERSION >= 0x040000
13 #include <qbitmap.h>
14 #include <qpalette.h>
15 #endif
16 #include <qpixmap.h>
17 #include <qevent.h>
18 #include "qwt_math.h"
19 #include "qwt_scale_engine.h"
20 #include "qwt_scale_map.h"
21 #include "qwt_paint_buffer.h"
22 #include "qwt_painter.h"
23 #include "qwt_dial_needle.h"
24 #include "qwt_dial.h"
25
26 class QwtDial::PrivateData
27 {
28 public:
PrivateData()29 PrivateData():
30 visibleBackground(true),
31 frameShadow(Sunken),
32 lineWidth(0),
33 mode(RotateNeedle),
34 direction(Clockwise),
35 origin(90.0),
36 minScaleArc(0.0),
37 maxScaleArc(0.0),
38 scaleDraw(0),
39 maxMajIntv(36),
40 maxMinIntv(10),
41 scaleStep(0.0),
42 needle(0)
43 {
44 }
45
~PrivateData()46 ~PrivateData()
47 {
48 delete scaleDraw;
49 delete needle;
50 }
51 bool visibleBackground;
52 Shadow frameShadow;
53 int lineWidth;
54
55 QwtDial::Mode mode;
56 QwtDial::Direction direction;
57
58 double origin;
59 double minScaleArc;
60 double maxScaleArc;
61
62 QwtDialScaleDraw *scaleDraw;
63 int maxMajIntv;
64 int maxMinIntv;
65 double scaleStep;
66
67 QwtDialNeedle *needle;
68
69 static double previousDir;
70 };
71
72 double QwtDial::PrivateData::previousDir = -1.0;
73
74 /*!
75 Constructor
76
77 \param parent Parent dial widget
78 */
QwtDialScaleDraw(QwtDial * parent)79 QwtDialScaleDraw::QwtDialScaleDraw(QwtDial *parent):
80 d_parent(parent),
81 d_penWidth(1)
82 {
83 }
84
85 /*!
86 Set the pen width used for painting the scale
87
88 \param penWidth Pen width
89 \sa penWidth(), QwtDial::drawScale()
90 */
91
setPenWidth(uint penWidth)92 void QwtDialScaleDraw::setPenWidth(uint penWidth)
93 {
94 d_penWidth = penWidth;
95 }
96
97 /*!
98 \return Pen width used for painting the scale
99 \sa setPenWidth, QwtDial::drawScale()
100 */
penWidth() const101 uint QwtDialScaleDraw::penWidth() const
102 {
103 return d_penWidth;
104 }
105
106 /*!
107 Call QwtDial::scaleLabel of the parent dial widget.
108
109 \param value Value to display
110
111 \sa QwtDial::scaleLabel()
112 */
label(double value) const113 QwtText QwtDialScaleDraw::label(double value) const
114 {
115 if ( d_parent == NULL )
116 return QwtRoundScaleDraw::label(value);
117
118 return d_parent->scaleLabel(value);
119 }
120
121 /*!
122 \brief Constructor
123 \param parent Parent widget
124
125 Create a dial widget with no scale and no needle.
126 The default origin is 90.0 with no valid value. It accepts
127 mouse and keyboard inputs and has no step size. The default mode
128 is QwtDial::RotateNeedle.
129 */
130
QwtDial(QWidget * parent)131 QwtDial::QwtDial(QWidget* parent):
132 QwtAbstractSlider(Qt::Horizontal, parent)
133 {
134 initDial();
135 }
136
137 #if QT_VERSION < 0x040000
138 /*!
139 \brief Constructor
140 \param parent Parent widget
141 \param name Object name
142
143 Create a dial widget with no scale and no needle.
144 The default origin is 90.0 with no valid value. It accepts
145 mouse and keyboard inputs and has no step size. The default mode
146 is QwtDial::RotateNeedle.
147 */
QwtDial(QWidget * parent,const char * name)148 QwtDial::QwtDial(QWidget* parent, const char *name):
149 QwtAbstractSlider(Qt::Horizontal, parent)
150 {
151 setName(name);
152 initDial();
153 }
154 #endif
155
initDial()156 void QwtDial::initDial()
157 {
158 d_data = new PrivateData;
159
160 #if QT_VERSION < 0x040000
161 setWFlags(Qt::WNoAutoErase);
162 #endif
163
164 #if QT_VERSION >= 0x040000
165 using namespace Qt;
166 #endif
167 setFocusPolicy(TabFocus);
168
169 QPalette p = palette();
170 for ( int i = 0; i < QPalette::NColorGroups; i++ )
171 {
172 const QPalette::ColorGroup cg = (QPalette::ColorGroup)i;
173
174 // Base: background color of the circle inside the frame.
175 // Foreground: background color of the circle inside the scale
176
177 #if QT_VERSION < 0x040000
178 p.setColor(cg, QColorGroup::Foreground,
179 p.color(cg, QColorGroup::Base));
180 #else
181 p.setColor(cg, QPalette::WindowText,
182 p.color(cg, QPalette::Base));
183 #endif
184 }
185 setPalette(p);
186
187 d_data->scaleDraw = new QwtDialScaleDraw(this);
188 d_data->scaleDraw->setRadius(0);
189
190 setScaleArc(0.0, 360.0); // scale as a full circle
191 setRange(0.0, 360.0, 1.0, 10); // degrees as deafult
192 }
193
194 //! Destructor
~QwtDial()195 QwtDial::~QwtDial()
196 {
197 delete d_data;
198 }
199
200 /*!
201 Show/Hide the area outside of the frame
202 \param show Show if true, hide if false
203
204 \sa hasVisibleBackground(), setMask()
205 \warning When QwtDial is a toplevel widget the window
206 border might disappear too.
207 */
showBackground(bool show)208 void QwtDial::showBackground(bool show)
209 {
210 if ( d_data->visibleBackground != show )
211 {
212 d_data->visibleBackground = show;
213 updateMask();
214 }
215 }
216
217 /*!
218 true when the area outside of the frame is visible
219
220 \sa showBackground(), setMask()
221 */
hasVisibleBackground() const222 bool QwtDial::hasVisibleBackground() const
223 {
224 return d_data->visibleBackground;
225 }
226
227 /*!
228 Sets the frame shadow value from the frame style.
229 \param shadow Frame shadow
230 \sa setLineWidth(), QFrame::setFrameShadow()
231 */
setFrameShadow(Shadow shadow)232 void QwtDial::setFrameShadow(Shadow shadow)
233 {
234 if ( shadow != d_data->frameShadow )
235 {
236 d_data->frameShadow = shadow;
237 if ( lineWidth() > 0 )
238 update();
239 }
240 }
241
242 /*!
243 \return Frame shadow
244 /sa setFrameShadow(), lineWidth(), QFrame::frameShadow
245 */
frameShadow() const246 QwtDial::Shadow QwtDial::frameShadow() const
247 {
248 return d_data->frameShadow;
249 }
250
251 /*!
252 Sets the line width
253
254 \param lineWidth Line width
255 \sa setFrameShadow()
256 */
setLineWidth(int lineWidth)257 void QwtDial::setLineWidth(int lineWidth)
258 {
259 if ( lineWidth < 0 )
260 lineWidth = 0;
261
262 if ( d_data->lineWidth != lineWidth )
263 {
264 d_data->lineWidth = lineWidth;
265 update();
266 }
267 }
268
269 /*!
270 \return Line width of the frame
271 \sa setLineWidth(), frameShadow(), lineWidth()
272 */
lineWidth() const273 int QwtDial::lineWidth() const
274 {
275 return d_data->lineWidth;
276 }
277
278 /*!
279 \return bounding rect of the circle inside the frame
280 \sa setLineWidth(), scaleContentsRect(), boundingRect()
281 */
contentsRect() const282 QRect QwtDial::contentsRect() const
283 {
284 const int lw = lineWidth();
285
286 QRect r = boundingRect();
287 if ( lw > 0 )
288 {
289 r.setRect(r.x() + lw, r.y() + lw,
290 r.width() - 2 * lw, r.height() - 2 * lw);
291 }
292 return r;
293 }
294
295 /*!
296 \return bounding rect of the dial including the frame
297 \sa setLineWidth(), scaleContentsRect(), contentsRect()
298 */
boundingRect() const299 QRect QwtDial::boundingRect() const
300 {
301 const int radius = qwtMin(width(), height()) / 2;
302
303 QRect r(0, 0, 2 * radius, 2 * radius);
304 r.moveCenter(rect().center());
305 return r;
306 }
307
308 /*!
309 \return rect inside the scale
310 \sa setLineWidth(), boundingRect(), contentsRect()
311 */
scaleContentsRect() const312 QRect QwtDial::scaleContentsRect() const
313 {
314 #if QT_VERSION < 0x040000
315 const QPen scalePen(colorGroup().text(), 0, Qt::NoPen);
316 #else
317 const QPen scalePen(palette().text(), 0, Qt::NoPen);
318 #endif
319
320 int scaleDist = 0;
321 if ( d_data->scaleDraw )
322 {
323 scaleDist = d_data->scaleDraw->extent(scalePen, font());
324 scaleDist++; // margin
325 }
326
327 const QRect rect = contentsRect();
328 return QRect(rect.x() + scaleDist, rect.y() + scaleDist,
329 rect.width() - 2 * scaleDist, rect.height() - 2 * scaleDist);
330 }
331
332 /*!
333 \brief Change the mode of the meter.
334 \param mode New mode
335
336 The value of the meter is indicated by the difference
337 between north of the scale and the direction of the needle.
338 In case of QwtDial::RotateNeedle north is pointing
339 to the origin() and the needle is rotating, in case of
340 QwtDial::RotateScale, the needle points to origin()
341 and the scale is rotating.
342
343 The default mode is QwtDial::RotateNeedle.
344
345 \sa mode(), setValue(), setOrigin()
346 */
setMode(Mode mode)347 void QwtDial::setMode(Mode mode)
348 {
349 if ( mode != d_data->mode )
350 {
351 d_data->mode = mode;
352 update();
353 }
354 }
355
356 /*!
357 \return mode of the dial.
358
359 The value of the dial is indicated by the difference
360 between the origin and the direction of the needle.
361 In case of QwtDial::RotateNeedle the scale arc is fixed
362 to the origin() and the needle is rotating, in case of
363 QwtDial::RotateScale, the needle points to origin()
364 and the scale is rotating.
365
366 The default mode is QwtDial::RotateNeedle.
367
368 \sa setMode(), origin(), setScaleArc(), value()
369 */
mode() const370 QwtDial::Mode QwtDial::mode() const
371 {
372 return d_data->mode;
373 }
374
375 /*!
376 Sets whether it is possible to step the value from the highest value to
377 the lowest value and vice versa to on.
378
379 \param wrapping en/disables wrapping
380
381 \sa wrapping(), QwtDoubleRange::periodic()
382 \note The meaning of wrapping is like the wrapping property of QSpinBox,
383 but not like it is used in QDial.
384 */
setWrapping(bool wrapping)385 void QwtDial::setWrapping(bool wrapping)
386 {
387 setPeriodic(wrapping);
388 }
389
390 /*!
391 wrapping() holds whether it is possible to step the value from the
392 highest value to the lowest value and vice versa.
393
394 \sa setWrapping(), QwtDoubleRange::setPeriodic()
395 \note The meaning of wrapping is like the wrapping property of QSpinBox,
396 but not like it is used in QDial.
397 */
wrapping() const398 bool QwtDial::wrapping() const
399 {
400 return periodic();
401 }
402
403 /*!
404 Set the direction of the dial (clockwise/counterclockwise)
405
406 Direction direction
407 \sa direction()
408 */
setDirection(Direction direction)409 void QwtDial::setDirection(Direction direction)
410 {
411 if ( direction != d_data->direction )
412 {
413 d_data->direction = direction;
414 update();
415 }
416 }
417
418 /*!
419 \return Direction of the dial
420
421 The default direction of a dial is QwtDial::Clockwise
422
423 \sa setDirection()
424 */
direction() const425 QwtDial::Direction QwtDial::direction() const
426 {
427 return d_data->direction;
428 }
429
430 /*!
431 Resize the dial widget
432 \param e Resize event
433 */
resizeEvent(QResizeEvent * e)434 void QwtDial::resizeEvent(QResizeEvent *e)
435 {
436 QWidget::resizeEvent(e);
437
438 if ( !hasVisibleBackground() )
439 updateMask();
440 }
441
442 /*!
443 Paint the dial
444 \param e Paint event
445 */
paintEvent(QPaintEvent * e)446 void QwtDial::paintEvent(QPaintEvent *e)
447 {
448 const QRect &ur = e->rect();
449 if ( ur.isValid() )
450 {
451 #if QT_VERSION < 0x040000
452 QwtPaintBuffer paintBuffer(this, ur);
453 QPainter &painter = *paintBuffer.painter();
454 #else
455 QPainter painter(this);
456 painter.setRenderHint(QPainter::Antialiasing, true);
457 #endif
458
459 painter.save();
460 drawContents(&painter);
461 painter.restore();
462
463 painter.save();
464 drawFrame(&painter);
465 painter.restore();
466
467 if ( hasFocus() )
468 drawFocusIndicator(&painter);
469 }
470 }
471
472 /*!
473 Draw a dotted round circle, if !isReadOnly()
474
475 \param painter Painter
476 */
drawFocusIndicator(QPainter * painter) const477 void QwtDial::drawFocusIndicator(QPainter *painter) const
478 {
479 if ( !isReadOnly() )
480 {
481 QRect focusRect = contentsRect();
482
483 const int margin = 2;
484 focusRect.setRect(
485 focusRect.x() + margin,
486 focusRect.y() + margin,
487 focusRect.width() - 2 * margin,
488 focusRect.height() - 2 * margin);
489
490 #if QT_VERSION < 0x040000
491 QColor color = colorGroup().color(QColorGroup::Base);
492 #else
493 QColor color = palette().color(QPalette::Base);
494 #endif
495 if (color.isValid())
496 {
497 const QColor gray(Qt::gray);
498
499 int h, s, v;
500 #if QT_VERSION < 0x040000
501 color.hsv(&h, &s, &v);
502 #else
503 color.getHsv(&h, &s, &v);
504 #endif
505 color = (v > 128) ? gray.darker(120) : gray.lighter(120);
506 }
507 else
508 color = Qt::darkGray;
509
510 painter->save();
511 painter->setBrush(Qt::NoBrush);
512 painter->setPen(QPen(color, 0, Qt::DotLine));
513 painter->drawEllipse(focusRect);
514 painter->restore();
515 }
516 }
517
518 /*!
519 Draw the frame around the dial
520
521 \param painter Painter
522 \sa lineWidth(), frameShadow()
523 */
drawFrame(QPainter * painter)524 void QwtDial::drawFrame(QPainter *painter)
525 {
526 const int lw = lineWidth();
527 const int off = (lw + 1) % 2;
528
529 QRect r = boundingRect();
530 r.setRect(r.x() + lw / 2 - off, r.y() + lw / 2 - off,
531 r.width() - lw + off + 1, r.height() - lw + off + 1);
532 #if QT_VERSION >= 0x040000
533 r.setX(r.x() + 1);
534 r.setY(r.y() + 1);
535 r.setWidth(r.width() - 2);
536 r.setHeight(r.height() - 2);
537 #endif
538
539 if ( lw > 0 )
540 {
541 switch(d_data->frameShadow)
542 {
543 case QwtDial::Raised:
544 #if QT_VERSION < 0x040000
545 QwtPainter::drawRoundFrame(painter, r,
546 lw, colorGroup(), false);
547 #else
548 QwtPainter::drawRoundFrame(painter, r,
549 lw, palette(), false);
550 #endif
551 break;
552 case QwtDial::Sunken:
553 #if QT_VERSION < 0x040000
554 QwtPainter::drawRoundFrame(painter, r,
555 lw, colorGroup(), true);
556 #else
557 QwtPainter::drawRoundFrame(painter, r,
558 lw, palette(), true);
559 #endif
560 break;
561 default: // Plain
562 {
563 painter->save();
564 painter->setPen(QPen(Qt::black, lw));
565 painter->setBrush(Qt::NoBrush);
566 painter->drawEllipse(r);
567 painter->restore();
568 }
569 }
570 }
571 }
572
573 /*!
574 \brief Draw the contents inside the frame
575
576 QColorGroup::Background is the background color outside of the frame.
577 QColorGroup::Base is the background color inside the frame.
578 QColorGroup::Foreground is the background color inside the scale.
579
580 \param painter Painter
581 \sa boundingRect(), contentsRect(),
582 scaleContentsRect(), QWidget::setPalette()
583 */
drawContents(QPainter * painter) const584 void QwtDial::drawContents(QPainter *painter) const
585 {
586 #if QT_VERSION < 0x040000
587 if ( backgroundMode() == Qt::NoBackground ||
588 colorGroup().brush(QColorGroup::Base) !=
589 colorGroup().brush(QColorGroup::Background) )
590 #else
591 if ( testAttribute(Qt::WA_NoSystemBackground) ||
592 palette().brush(QPalette::Base) !=
593 palette().brush(QPalette::Window) )
594 #endif
595 {
596
597 const QRect br = boundingRect();
598
599 painter->save();
600 painter->setPen(Qt::NoPen);
601
602 #if QT_VERSION < 0x040000
603 painter->setBrush(colorGroup().brush(QColorGroup::Base));
604 #else
605 painter->setBrush(palette().brush(QPalette::Base));
606 #endif
607
608 painter->drawEllipse(br);
609 painter->restore();
610 }
611
612
613 const QRect insideScaleRect = scaleContentsRect();
614 #if QT_VERSION < 0x040000
615 if ( colorGroup().brush(QColorGroup::Foreground) !=
616 colorGroup().brush(QColorGroup::Base) )
617 #else
618 if ( palette().brush(QPalette::WindowText) !=
619 palette().brush(QPalette::Base) )
620 #endif
621 {
622 painter->save();
623 painter->setPen(Qt::NoPen);
624
625 #if QT_VERSION < 0x040000
626 painter->setBrush(colorGroup().brush(QColorGroup::Foreground));
627 #else
628 painter->setBrush(palette().brush(QPalette::WindowText));
629 #endif
630
631 painter->drawEllipse(insideScaleRect.x() - 1, insideScaleRect.y() - 1,
632 insideScaleRect.width(), insideScaleRect.height() );
633
634 painter->restore();
635 }
636
637 const QPoint center = insideScaleRect.center();
638 const int radius = insideScaleRect.width() / 2;
639
640 painter->save();
641 drawScaleContents(painter, center, radius);
642 painter->restore();
643
644 double direction = d_data->origin;
645
646 if (isValid())
647 {
648 direction = d_data->minScaleArc;
649 if ( maxValue() > minValue() && d_data->maxScaleArc > d_data->minScaleArc )
650 {
651 const double ratio =
652 (value() - minValue()) / (maxValue() - minValue());
653 direction += ratio * (d_data->maxScaleArc - d_data->minScaleArc);
654 }
655
656 if ( d_data->direction == QwtDial::CounterClockwise )
657 direction = d_data->maxScaleArc - (direction - d_data->minScaleArc);
658
659 direction += d_data->origin;
660 if ( direction >= 360.0 )
661 direction -= 360.0;
662 else if ( direction < 0.0 )
663 direction += 360.0;
664 }
665
666 double origin = d_data->origin;
667 if ( mode() == RotateScale )
668 {
669 origin -= direction - d_data->origin;
670 direction = d_data->origin;
671 }
672
673 painter->save();
674 drawScale(painter, center, radius, origin,
675 d_data->minScaleArc, d_data->maxScaleArc);
676 painter->restore();
677
678 if ( isValid() )
679 {
680 QPalette::ColorGroup cg;
681 if ( isEnabled() )
682 cg = hasFocus() ? QPalette::Active : QPalette::Inactive;
683 else
684 cg = QPalette::Disabled;
685
686 painter->save();
687 drawNeedle(painter, center, radius, direction, cg);
688 painter->restore();
689 }
690 }
691
692 /*!
693 Draw the needle
694
695 \param painter Painter
696 \param center Center of the dial
697 \param radius Length for the needle
698 \param direction Direction of the needle in degrees, counter clockwise
699 \param cg ColorGroup
700 */
drawNeedle(QPainter * painter,const QPoint & center,int radius,double direction,QPalette::ColorGroup cg) const701 void QwtDial::drawNeedle(QPainter *painter, const QPoint ¢er,
702 int radius, double direction, QPalette::ColorGroup cg) const
703 {
704 if ( d_data->needle )
705 {
706 direction = 360.0 - direction; // counter clockwise
707 d_data->needle->draw(painter, center, radius, direction, cg);
708 }
709 }
710
711 /*!
712 Draw the scale
713
714 \param painter Painter
715 \param center Center of the dial
716 \param radius Radius of the scale
717 \param origin Origin of the scale
718 \param minArc Minimum of the arc
719 \param maxArc Minimum of the arc
720
721 \sa QwtAbstractScaleDraw::setAngleRange()
722 */
drawScale(QPainter * painter,const QPoint & center,int radius,double origin,double minArc,double maxArc) const723 void QwtDial::drawScale(QPainter *painter, const QPoint ¢er,
724 int radius, double origin, double minArc, double maxArc) const
725 {
726 if ( d_data->scaleDraw == NULL )
727 return;
728
729 origin -= 270.0; // hardcoded origin of QwtScaleDraw
730
731 double angle = maxArc - minArc;
732 if ( angle > 360.0 )
733 angle = ::fmod(angle, 360.0);
734
735 minArc += origin;
736 if ( minArc < -360.0 )
737 minArc = ::fmod(minArc, 360.0);
738
739 maxArc = minArc + angle;
740 if ( maxArc > 360.0 )
741 {
742 // QwtAbstractScaleDraw::setAngleRange accepts only values
743 // in the range [-360.0..360.0]
744 minArc -= 360.0;
745 maxArc -= 360.0;
746 }
747
748 if ( d_data->direction == QwtDial::CounterClockwise )
749 qSwap(minArc, maxArc);
750
751 painter->setFont(font());
752
753 d_data->scaleDraw->setAngleRange(minArc, maxArc);
754 d_data->scaleDraw->setRadius(radius);
755 d_data->scaleDraw->moveCenter(center);
756
757 #if QT_VERSION < 0x040000
758 QColorGroup cg = colorGroup();
759
760 const QColor textColor = cg.color(QColorGroup::Text);
761 cg.setColor(QColorGroup::Foreground, textColor);
762 painter->setPen(QPen(textColor, d_data->scaleDraw->penWidth()));
763
764 d_data->scaleDraw->draw(painter, cg);
765 #else
766 QPalette pal = palette();
767
768 const QColor textColor = pal.color(QPalette::Text);
769 pal.setColor(QPalette::WindowText, textColor); //ticks, backbone
770
771 painter->setPen(QPen(textColor, d_data->scaleDraw->penWidth()));
772
773 d_data->scaleDraw->draw(painter, pal);
774 #endif
775 }
776
drawScaleContents(QPainter *,const QPoint &,int) const777 void QwtDial::drawScaleContents(QPainter *,
778 const QPoint &, int) const
779 {
780 // empty default implementation
781 }
782
783 /*!
784 Set a needle for the dial
785
786 Qwt is missing a set of good looking needles.
787 Contributions are very welcome.
788
789 \param needle Needle
790 \warning The needle will be deleted, when a different needle is
791 set or in ~QwtDial()
792 */
setNeedle(QwtDialNeedle * needle)793 void QwtDial::setNeedle(QwtDialNeedle *needle)
794 {
795 if ( needle != d_data->needle )
796 {
797 if ( d_data->needle )
798 delete d_data->needle;
799
800 d_data->needle = needle;
801 update();
802 }
803 }
804
805 /*!
806 \return needle
807 \sa setNeedle()
808 */
needle() const809 const QwtDialNeedle *QwtDial::needle() const
810 {
811 return d_data->needle;
812 }
813
814 /*!
815 \return needle
816 \sa setNeedle()
817 */
needle()818 QwtDialNeedle *QwtDial::needle()
819 {
820 return d_data->needle;
821 }
822
823 //! QwtDoubleRange update hook
rangeChange()824 void QwtDial::rangeChange()
825 {
826 updateScale();
827 }
828
829 /*!
830 Update the scale with the current attributes
831 \sa setScale()
832 */
updateScale()833 void QwtDial::updateScale()
834 {
835 if ( d_data->scaleDraw )
836 {
837 QwtLinearScaleEngine scaleEngine;
838
839 const QwtScaleDiv scaleDiv = scaleEngine.divideScale(
840 minValue(), maxValue(),
841 d_data->maxMajIntv, d_data->maxMinIntv, d_data->scaleStep);
842
843 d_data->scaleDraw->setTransformation(scaleEngine.transformation());
844 d_data->scaleDraw->setScaleDiv(scaleDiv);
845 }
846 }
847
848 //! Return the scale draw
scaleDraw()849 QwtDialScaleDraw *QwtDial::scaleDraw()
850 {
851 return d_data->scaleDraw;
852 }
853
854 //! Return the scale draw
scaleDraw() const855 const QwtDialScaleDraw *QwtDial::scaleDraw() const
856 {
857 return d_data->scaleDraw;
858 }
859
860 /*!
861 Set an individual scale draw
862
863 \param scaleDraw Scale draw
864 \warning The previous scale draw is deleted
865 */
setScaleDraw(QwtDialScaleDraw * scaleDraw)866 void QwtDial::setScaleDraw(QwtDialScaleDraw *scaleDraw)
867 {
868 if ( scaleDraw != d_data->scaleDraw )
869 {
870 if ( d_data->scaleDraw )
871 delete d_data->scaleDraw;
872
873 d_data->scaleDraw = scaleDraw;
874 updateScale();
875 update();
876 }
877 }
878
879 /*!
880 Change the intervals of the scale
881 \sa QwtAbstractScaleDraw::setScale()
882 */
setScale(int maxMajIntv,int maxMinIntv,double step)883 void QwtDial::setScale(int maxMajIntv, int maxMinIntv, double step)
884 {
885 d_data->maxMajIntv = maxMajIntv;
886 d_data->maxMinIntv = maxMinIntv;
887 d_data->scaleStep = step;
888
889 updateScale();
890 }
891
892 /*!
893 A wrapper method for accessing the scale draw.
894
895 - options == 0\n
896 No visible scale: setScaleDraw(NULL)
897 - options & ScaleBackbone\n
898 En/disable the backbone of the scale.
899 - options & ScaleTicks\n
900 En/disable the ticks of the scale.
901 - options & ScaleLabel\n
902 En/disable scale labels
903
904 \sa QwtAbstractScaleDraw::enableComponent()
905 */
setScaleOptions(int options)906 void QwtDial::setScaleOptions(int options)
907 {
908 if ( options == 0 )
909 setScaleDraw(NULL);
910
911 QwtDialScaleDraw *sd = d_data->scaleDraw;
912 if ( sd == NULL )
913 return;
914
915 sd->enableComponent(QwtAbstractScaleDraw::Backbone,
916 options & ScaleBackbone);
917
918 sd->enableComponent(QwtAbstractScaleDraw::Ticks,
919 options & ScaleTicks);
920
921 sd->enableComponent(QwtAbstractScaleDraw::Labels,
922 options & ScaleLabel);
923 }
924
925 /*!
926 Assign length and width of the ticks
927
928 \param minLen Length of the minor ticks
929 \param medLen Length of the medium ticks
930 \param majLen Length of the major ticks
931 \param penWidth Width of the pen for all ticks
932
933 \sa QwtAbstractScaleDraw::setTickLength(), QwtDialScaleDraw::setPenWidth()
934 */
setScaleTicks(int minLen,int medLen,int majLen,int penWidth)935 void QwtDial::setScaleTicks(int minLen, int medLen,
936 int majLen, int penWidth)
937 {
938 QwtDialScaleDraw *sd = d_data->scaleDraw;
939 if ( sd )
940 {
941 sd->setTickLength(QwtScaleDiv::MinorTick, minLen);
942 sd->setTickLength(QwtScaleDiv::MediumTick, medLen);
943 sd->setTickLength(QwtScaleDiv::MajorTick, majLen);
944 sd->setPenWidth(penWidth);
945 }
946 }
947
948 /*!
949 Find the label for a value
950
951 \param value Value
952 \return label
953 */
scaleLabel(double value) const954 QwtText QwtDial::scaleLabel(double value) const
955 {
956 #if 1
957 if ( value == -0 )
958 value = 0;
959 #endif
960
961 return QString::number(value);
962 }
963
964 //! \return Lower limit of the scale arc
minScaleArc() const965 double QwtDial::minScaleArc() const
966 {
967 return d_data->minScaleArc;
968 }
969
970 //! \return Upper limit of the scale arc
maxScaleArc() const971 double QwtDial::maxScaleArc() const
972 {
973 return d_data->maxScaleArc;
974 }
975
976 /*!
977 \brief Change the origin
978
979 The origin is the angle where scale and needle is relative to.
980
981 \param origin New origin
982 \sa origin()
983 */
setOrigin(double origin)984 void QwtDial::setOrigin(double origin)
985 {
986 d_data->origin = origin;
987 update();
988 }
989
990 /*!
991 The origin is the angle where scale and needle is relative to.
992
993 \return Origin of the dial
994 \sa setOrigin()
995 */
origin() const996 double QwtDial::origin() const
997 {
998 return d_data->origin;
999 }
1000
1001 /*!
1002 Change the arc of the scale
1003
1004 \param minArc Lower limit
1005 \param maxArc Upper limit
1006 */
setScaleArc(double minArc,double maxArc)1007 void QwtDial::setScaleArc(double minArc, double maxArc)
1008 {
1009 if ( minArc != 360.0 && minArc != -360.0 )
1010 minArc = fmod(minArc, 360.0);
1011 if ( maxArc != 360.0 && maxArc != -360.0 )
1012 maxArc = fmod(maxArc, 360.0);
1013
1014 d_data->minScaleArc = qwtMin(minArc, maxArc);
1015 d_data->maxScaleArc = qwtMax(minArc, maxArc);
1016 if ( d_data->maxScaleArc - d_data->minScaleArc > 360.0 )
1017 d_data->maxScaleArc = d_data->minScaleArc + 360.0;
1018
1019 update();
1020 }
1021
1022 //! QwtDoubleRange update hook
valueChange()1023 void QwtDial::valueChange()
1024 {
1025 update();
1026 QwtAbstractSlider::valueChange();
1027 }
1028
1029 /*!
1030 \return Size hint
1031 */
sizeHint() const1032 QSize QwtDial::sizeHint() const
1033 {
1034 int sh = 0;
1035 if ( d_data->scaleDraw )
1036 sh = d_data->scaleDraw->extent( QPen(), font() );
1037
1038 const int d = 6 * sh + 2 * lineWidth();
1039
1040 return QSize( d, d );
1041 }
1042
1043 /*!
1044 \brief Return a minimum size hint
1045 \warning The return value of QwtDial::minimumSizeHint() depends on the
1046 font and the scale.
1047 */
minimumSizeHint() const1048 QSize QwtDial::minimumSizeHint() const
1049 {
1050 int sh = 0;
1051 if ( d_data->scaleDraw )
1052 sh = d_data->scaleDraw->extent(QPen(), font() );
1053
1054 const int d = 3 * sh + 2 * lineWidth();
1055
1056 return QSize( d, d );
1057 }
1058
line2Radians(const QPoint & p1,const QPoint & p2)1059 static double line2Radians(const QPoint &p1, const QPoint &p2)
1060 {
1061 const QPoint p = p2 - p1;
1062
1063 double angle;
1064 if ( p.x() == 0 )
1065 angle = ( p.y() <= 0 ) ? M_PI_2 : 3 * M_PI_2;
1066 else
1067 {
1068 angle = atan(double(-p.y()) / double(p.x()));
1069 if ( p.x() < 0 )
1070 angle += M_PI;
1071 if ( angle < 0.0 )
1072 angle += 2 * M_PI;
1073 }
1074 return 360.0 - angle * 180.0 / M_PI;
1075 }
1076
1077 /*!
1078 Find the value for a given position
1079
1080 \param pos Position
1081 \return Value
1082 */
getValue(const QPoint & pos)1083 double QwtDial::getValue(const QPoint &pos)
1084 {
1085 if ( d_data->maxScaleArc == d_data->minScaleArc || maxValue() == minValue() )
1086 return minValue();
1087
1088 double dir = line2Radians(rect().center(), pos) - d_data->origin;
1089 if ( dir < 0.0 )
1090 dir += 360.0;
1091
1092 if ( mode() == RotateScale )
1093 dir = 360.0 - dir;
1094
1095 // The position might be in the area that is outside the scale arc.
1096 // We need the range of the scale if it was a complete circle.
1097
1098 const double completeCircle = 360.0 / (d_data->maxScaleArc - d_data->minScaleArc)
1099 * (maxValue() - minValue());
1100
1101 double posValue = minValue() + completeCircle * dir / 360.0;
1102
1103 if ( scrollMode() == ScrMouse )
1104 {
1105 if ( d_data->previousDir >= 0.0 ) // valid direction
1106 {
1107 // We have to find out whether the mouse is moving
1108 // clock or counter clockwise
1109
1110 bool clockWise = false;
1111
1112 const double angle = dir - d_data->previousDir;
1113 if ( (angle >= 0.0 && angle <= 180.0) || angle < -180.0 )
1114 clockWise = true;
1115
1116 if ( clockWise )
1117 {
1118 if ( dir < d_data->previousDir && mouseOffset() > 0.0 )
1119 {
1120 // We passed 360 -> 0
1121 setMouseOffset(mouseOffset() - completeCircle);
1122 }
1123
1124 if ( wrapping() )
1125 {
1126 if ( posValue - mouseOffset() > maxValue() )
1127 {
1128 // We passed maxValue and the value will be set
1129 // to minValue. We have to adjust the mouseOffset.
1130
1131 setMouseOffset(posValue - minValue());
1132 }
1133 }
1134 else
1135 {
1136 if ( posValue - mouseOffset() > maxValue() ||
1137 value() == maxValue() )
1138 {
1139 // We fix the value at maxValue by adjusting
1140 // the mouse offset.
1141
1142 setMouseOffset(posValue - maxValue());
1143 }
1144 }
1145 }
1146 else
1147 {
1148 if ( dir > d_data->previousDir && mouseOffset() < 0.0 )
1149 {
1150 // We passed 0 -> 360
1151 setMouseOffset(mouseOffset() + completeCircle);
1152 }
1153
1154 if ( wrapping() )
1155 {
1156 if ( posValue - mouseOffset() < minValue() )
1157 {
1158 // We passed minValue and the value will be set
1159 // to maxValue. We have to adjust the mouseOffset.
1160
1161 setMouseOffset(posValue - maxValue());
1162 }
1163 }
1164 else
1165 {
1166 if ( posValue - mouseOffset() < minValue() ||
1167 value() == minValue() )
1168 {
1169 // We fix the value at minValue by adjusting
1170 // the mouse offset.
1171
1172 setMouseOffset(posValue - minValue());
1173 }
1174 }
1175 }
1176 }
1177 d_data->previousDir = dir;
1178 }
1179
1180 return posValue;
1181 }
1182
1183 /*!
1184 See QwtAbstractSlider::getScrollMode()
1185
1186 \param pos point where the mouse was pressed
1187 \retval scrollMode The scrolling mode
1188 \retval direction direction: 1, 0, or -1.
1189
1190 \sa QwtAbstractSlider::getScrollMode()
1191 */
getScrollMode(const QPoint & pos,int & scrollMode,int & direction)1192 void QwtDial::getScrollMode(const QPoint &pos, int &scrollMode, int &direction)
1193 {
1194 direction = 0;
1195 scrollMode = ScrNone;
1196
1197 const QRegion region(contentsRect(), QRegion::Ellipse);
1198 if ( region.contains(pos) && pos != rect().center() )
1199 {
1200 scrollMode = ScrMouse;
1201 d_data->previousDir = -1.0;
1202 }
1203 }
1204
1205 /*!
1206 Handles key events
1207
1208 - Key_Down, KeyLeft\n
1209 Decrement by 1
1210 - Key_Prior\n
1211 Decrement by pageSize()
1212 - Key_Home\n
1213 Set the value to minValue()
1214
1215 - Key_Up, KeyRight\n
1216 Increment by 1
1217 - Key_Next\n
1218 Increment by pageSize()
1219 - Key_End\n
1220 Set the value to maxValue()
1221
1222 \param event Key event
1223 \sa isReadOnly()
1224 */
keyPressEvent(QKeyEvent * event)1225 void QwtDial::keyPressEvent(QKeyEvent *event)
1226 {
1227 if ( isReadOnly() )
1228 {
1229 event->ignore();
1230 return;
1231 }
1232
1233 if ( !isValid() )
1234 return;
1235
1236 double previous = prevValue();
1237 switch ( event->key() )
1238 {
1239 case Qt::Key_Down:
1240 case Qt::Key_Left:
1241 QwtDoubleRange::incValue(-1);
1242 break;
1243 #if QT_VERSION < 0x040000
1244 case Qt::Key_Prior:
1245 #else
1246 case Qt::Key_PageUp:
1247 #endif
1248 QwtDoubleRange::incValue(-pageSize());
1249 break;
1250 case Qt::Key_Home:
1251 setValue(minValue());
1252 break;
1253
1254 case Qt::Key_Up:
1255 case Qt::Key_Right:
1256 QwtDoubleRange::incValue(1);
1257 break;
1258 #if QT_VERSION < 0x040000
1259 case Qt::Key_Next:
1260 #else
1261 case Qt::Key_PageDown:
1262 #endif
1263 QwtDoubleRange::incValue(pageSize());
1264 break;
1265 case Qt::Key_End:
1266 setValue(maxValue());
1267 break;
1268 default:;
1269 event->ignore();
1270 }
1271
1272 if (value() != previous)
1273 emit sliderMoved(value());
1274 }
1275
1276 /*!
1277 \brief Update the mask of the dial
1278
1279 In case of "hasVisibleBackground() == false", the backgound is
1280 transparent by a mask.
1281
1282 \sa showBackground(), hasVisibleBackground()
1283 */
updateMask()1284 void QwtDial::updateMask()
1285 {
1286 if ( d_data->visibleBackground )
1287 clearMask();
1288 else
1289 setMask(QRegion(boundingRect(), QRegion::Ellipse));
1290 }
1291