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 &center,
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 &center,
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