00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "qwt_dial.h"
00011 #include "qwt_dial_needle.h"
00012 #include "qwt_math.h"
00013 #include "qwt_scale_engine.h"
00014 #include "qwt_scale_map.h"
00015 #include "qwt_round_scale_draw.h"
00016 #include "qwt_painter.h"
00017 #include <qpainter.h>
00018 #include <qpalette.h>
00019 #include <qpixmap.h>
00020 #include <qevent.h>
00021 #include <qalgorithms.h>
00022 #include <qmath.h>
00023 #include <qstyle.h>
00024 #include <qstyleoption.h>
00025 #include <qapplication.h>
00026
00027 static inline double qwtAngleDist( double a1, double a2 )
00028 {
00029 double dist = qAbs( a2 - a1 );
00030 if ( dist > 360.0 )
00031 dist -= 360.0;
00032
00033 return dist;
00034 }
00035
00036 static inline bool qwtIsOnArc( double angle, double min, double max )
00037 {
00038 if ( min < max )
00039 {
00040 return ( angle >= min ) && ( angle <= max );
00041 }
00042 else
00043 {
00044 return ( angle >= min ) || ( angle <= max );
00045 }
00046 }
00047
00048 static inline double qwtBoundedAngle( double min, double angle, double max )
00049 {
00050 double from = qwtNormalizeDegrees( min );
00051 double to = qwtNormalizeDegrees( max );
00052
00053 double a;
00054
00055 if ( qwtIsOnArc( angle, from, to ) )
00056 {
00057 a = angle;
00058 if ( a < min )
00059 a += 360.0;
00060 }
00061 else
00062 {
00063 if ( qwtAngleDist( angle, from ) <
00064 qwtAngleDist( angle, to ) )
00065 {
00066 a = min;
00067 }
00068 else
00069 {
00070 a = max;
00071 }
00072 }
00073
00074 return a;
00075 }
00076
00077 class QwtDial::PrivateData
00078 {
00079 public:
00080 PrivateData():
00081 frameShadow( Sunken ),
00082 lineWidth( 0 ),
00083 mode( RotateNeedle ),
00084 origin( 90.0 ),
00085 minScaleArc( 0.0 ),
00086 maxScaleArc( 0.0 ),
00087 needle( NULL ),
00088 arcOffset( 0.0 ),
00089 mouseOffset( 0.0 )
00090 {
00091 }
00092
00093 ~PrivateData()
00094 {
00095 delete needle;
00096 }
00097 Shadow frameShadow;
00098 int lineWidth;
00099
00100 QwtDial::Mode mode;
00101
00102 double origin;
00103 double minScaleArc;
00104 double maxScaleArc;
00105
00106 QwtDialNeedle *needle;
00107
00108 double arcOffset;
00109 double mouseOffset;
00110
00111 QPixmap pixmapCache;
00112 };
00113
00126 QwtDial::QwtDial( QWidget* parent ):
00127 QwtAbstractSlider( parent )
00128 {
00129 d_data = new PrivateData;
00130
00131 setFocusPolicy( Qt::TabFocus );
00132
00133 QPalette p = palette();
00134 for ( int i = 0; i < QPalette::NColorGroups; i++ )
00135 {
00136 const QPalette::ColorGroup colorGroup =
00137 static_cast<QPalette::ColorGroup>( i );
00138
00139
00140
00141
00142 p.setColor( colorGroup, QPalette::WindowText,
00143 p.color( colorGroup, QPalette::Base ) );
00144 }
00145 setPalette( p );
00146
00147 QwtRoundScaleDraw* scaleDraw = new QwtRoundScaleDraw();
00148 scaleDraw->setRadius( 0 );
00149
00150 setScaleDraw( scaleDraw );
00151
00152 setScaleArc( 0.0, 360.0 );
00153
00154 setScaleMaxMajor( 10 );
00155 setScaleMaxMinor( 5 );
00156
00157 setValue( 0.0 );
00158 }
00159
00161 QwtDial::~QwtDial()
00162 {
00163 delete d_data;
00164 }
00165
00172 void QwtDial::setFrameShadow( Shadow shadow )
00173 {
00174 if ( shadow != d_data->frameShadow )
00175 {
00176 invalidateCache();
00177
00178 d_data->frameShadow = shadow;
00179 if ( lineWidth() > 0 )
00180 update();
00181 }
00182 }
00183
00188 QwtDial::Shadow QwtDial::frameShadow() const
00189 {
00190 return d_data->frameShadow;
00191 }
00192
00199 void QwtDial::setLineWidth( int lineWidth )
00200 {
00201 if ( lineWidth < 0 )
00202 lineWidth = 0;
00203
00204 if ( d_data->lineWidth != lineWidth )
00205 {
00206 invalidateCache();
00207
00208 d_data->lineWidth = lineWidth;
00209 update();
00210 }
00211 }
00212
00217 int QwtDial::lineWidth() const
00218 {
00219 return d_data->lineWidth;
00220 }
00221
00226 QRect QwtDial::innerRect() const
00227 {
00228 const int lw = lineWidth();
00229 return boundingRect().adjusted( lw, lw, -lw, -lw );
00230 }
00231
00236 QRect QwtDial::boundingRect() const
00237 {
00238 const QRect cr = contentsRect();
00239
00240 const double dim = qMin( cr.width(), cr.height() );
00241
00242 QRect inner( 0, 0, dim, dim );
00243 inner.moveCenter( cr.center() );
00244
00245 return inner;
00246 }
00247
00252 QRect QwtDial::scaleInnerRect() const
00253 {
00254 QRect rect = innerRect();
00255
00256 const QwtAbstractScaleDraw *sd = scaleDraw();
00257 if ( sd )
00258 {
00259 int scaleDist = qCeil( sd->extent( font() ) );
00260 scaleDist++;
00261
00262 rect.adjust( scaleDist, scaleDist, -scaleDist, -scaleDist );
00263 }
00264
00265 return rect;
00266 }
00267
00280 void QwtDial::setMode( Mode mode )
00281 {
00282 if ( mode != d_data->mode )
00283 {
00284 invalidateCache();
00285
00286 d_data->mode = mode;
00287 sliderChange();
00288 }
00289 }
00290
00295 QwtDial::Mode QwtDial::mode() const
00296 {
00297 return d_data->mode;
00298 }
00299
00303 void QwtDial::invalidateCache()
00304 {
00305 d_data->pixmapCache = QPixmap();
00306 }
00307
00312 void QwtDial::paintEvent( QPaintEvent *event )
00313 {
00314 QPainter painter( this );
00315 painter.setClipRegion( event->region() );
00316
00317 QStyleOption opt;
00318 opt.init(this);
00319 style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
00320
00321 if ( d_data->mode == QwtDial::RotateScale )
00322 {
00323 painter.save();
00324 painter.setRenderHint( QPainter::Antialiasing, true );
00325
00326 drawContents( &painter );
00327
00328 painter.restore();
00329 }
00330
00331 const QRect r = contentsRect();
00332 if ( r.size() != d_data->pixmapCache.size() )
00333 {
00334 d_data->pixmapCache = QwtPainter::backingStore( this, r.size() );
00335 d_data->pixmapCache.fill( Qt::transparent );
00336
00337 QPainter p( &d_data->pixmapCache );
00338 p.setRenderHint( QPainter::Antialiasing, true );
00339 p.translate( -r.topLeft() );
00340
00341 if ( d_data->mode != QwtDial::RotateScale )
00342 drawContents( &p );
00343
00344 if ( lineWidth() > 0 )
00345 drawFrame( &p );
00346
00347 if ( d_data->mode != QwtDial::RotateNeedle )
00348 drawNeedle( &p );
00349 }
00350
00351 painter.drawPixmap( r.topLeft(), d_data->pixmapCache );
00352
00353 if ( d_data->mode == QwtDial::RotateNeedle )
00354 drawNeedle( &painter );
00355
00356 if ( hasFocus() )
00357 drawFocusIndicator( &painter );
00358 }
00359
00364 void QwtDial::drawFocusIndicator( QPainter *painter ) const
00365 {
00366 QwtPainter::drawFocusRect( painter, this, boundingRect() );
00367 }
00368
00375 void QwtDial::drawFrame( QPainter *painter )
00376 {
00377 QwtPainter::drawRoundFrame( painter, boundingRect(),
00378 palette(), lineWidth(), d_data->frameShadow );
00379 }
00380
00393 void QwtDial::drawContents( QPainter *painter ) const
00394 {
00395 if ( testAttribute( Qt::WA_NoSystemBackground ) ||
00396 palette().brush( QPalette::Base ) !=
00397 palette().brush( QPalette::Window ) )
00398 {
00399 const QRectF br = boundingRect();
00400
00401 painter->save();
00402 painter->setPen( Qt::NoPen );
00403 painter->setBrush( palette().brush( QPalette::Base ) );
00404 painter->drawEllipse( br );
00405 painter->restore();
00406 }
00407
00408 const QRectF insideScaleRect = scaleInnerRect();
00409 if ( palette().brush( QPalette::WindowText ) !=
00410 palette().brush( QPalette::Base ) )
00411 {
00412 painter->save();
00413 painter->setPen( Qt::NoPen );
00414 painter->setBrush( palette().brush( QPalette::WindowText ) );
00415 painter->drawEllipse( insideScaleRect );
00416 painter->restore();
00417 }
00418
00419 const QPointF center = insideScaleRect.center();
00420 const double radius = 0.5 * insideScaleRect.width();
00421
00422 painter->save();
00423 drawScale( painter, center, radius );
00424 painter->restore();
00425
00426 painter->save();
00427 drawScaleContents( painter, center, radius );
00428 painter->restore();
00429 }
00430
00440 void QwtDial::drawNeedle( QPainter *painter, const QPointF ¢er,
00441 double radius, double direction, QPalette::ColorGroup colorGroup ) const
00442 {
00443 if ( d_data->needle )
00444 {
00445 direction = 360.0 - direction;
00446 d_data->needle->draw( painter, center, radius, direction, colorGroup );
00447 }
00448 }
00449
00450 void QwtDial::drawNeedle( QPainter *painter ) const
00451 {
00452 if ( !isValid() )
00453 return;
00454
00455 QPalette::ColorGroup colorGroup;
00456 if ( isEnabled() )
00457 colorGroup = hasFocus() ? QPalette::Active : QPalette::Inactive;
00458 else
00459 colorGroup = QPalette::Disabled;
00460
00461 const QRectF sr = scaleInnerRect();
00462
00463 painter->save();
00464 painter->setRenderHint( QPainter::Antialiasing, true );
00465 drawNeedle( painter, sr.center(), 0.5 * sr.width(),
00466 scaleMap().transform( value() ) + 270.0, colorGroup );
00467 painter->restore();
00468 }
00469
00477 void QwtDial::drawScale( QPainter *painter,
00478 const QPointF ¢er, double radius ) const
00479 {
00480 QwtRoundScaleDraw *sd = const_cast<QwtRoundScaleDraw *>( scaleDraw() );
00481 if ( sd == NULL )
00482 return;
00483
00484 sd->setRadius( radius );
00485 sd->moveCenter( center );
00486
00487 QPalette pal = palette();
00488
00489 const QColor textColor = pal.color( QPalette::Text );
00490 pal.setColor( QPalette::WindowText, textColor );
00491
00492 painter->setFont( font() );
00493 painter->setPen( QPen( textColor, sd->penWidth() ) );
00494
00495 painter->setBrush( Qt::red );
00496 sd->draw( painter, pal );
00497 }
00498
00508 void QwtDial::drawScaleContents( QPainter *painter,
00509 const QPointF ¢er, double radius ) const
00510 {
00511 Q_UNUSED(painter);
00512 Q_UNUSED(center);
00513 Q_UNUSED(radius);
00514 }
00515
00524 void QwtDial::setNeedle( QwtDialNeedle *needle )
00525 {
00526 if ( needle != d_data->needle )
00527 {
00528 if ( d_data->needle )
00529 delete d_data->needle;
00530
00531 d_data->needle = needle;
00532 update();
00533 }
00534 }
00535
00540 const QwtDialNeedle *QwtDial::needle() const
00541 {
00542 return d_data->needle;
00543 }
00544
00549 QwtDialNeedle *QwtDial::needle()
00550 {
00551 return d_data->needle;
00552 }
00553
00555 QwtRoundScaleDraw *QwtDial::scaleDraw()
00556 {
00557 return static_cast<QwtRoundScaleDraw *>( abstractScaleDraw() );
00558 }
00559
00561 const QwtRoundScaleDraw *QwtDial::scaleDraw() const
00562 {
00563 return static_cast<const QwtRoundScaleDraw *>( abstractScaleDraw() );
00564 }
00565
00576 void QwtDial::setScaleDraw( QwtRoundScaleDraw *scaleDraw )
00577 {
00578 setAbstractScaleDraw( scaleDraw );
00579 sliderChange();
00580 }
00581
00590 void QwtDial::setScaleArc( double minArc, double maxArc )
00591 {
00592 if ( minArc != 360.0 && minArc != -360.0 )
00593 minArc = ::fmod( minArc, 360.0 );
00594 if ( maxArc != 360.0 && maxArc != -360.0 )
00595 maxArc = ::fmod( maxArc, 360.0 );
00596
00597 double minScaleArc = qMin( minArc, maxArc );
00598 double maxScaleArc = qMax( minArc, maxArc );
00599
00600 if ( maxScaleArc - minScaleArc > 360.0 )
00601 maxScaleArc = minScaleArc + 360.0;
00602
00603 if ( ( minScaleArc != d_data->minScaleArc ) ||
00604 ( maxScaleArc != d_data->maxScaleArc ) )
00605 {
00606 d_data->minScaleArc = minScaleArc;
00607 d_data->maxScaleArc = maxScaleArc;
00608
00609 invalidateCache();
00610 sliderChange();
00611 }
00612 }
00613
00620 void QwtDial::setMinScaleArc( double min )
00621 {
00622 setScaleArc( min, d_data->maxScaleArc );
00623 }
00624
00629 double QwtDial::minScaleArc() const
00630 {
00631 return d_data->minScaleArc;
00632 }
00633
00640 void QwtDial::setMaxScaleArc( double max )
00641 {
00642 setScaleArc( d_data->minScaleArc, max );
00643 }
00644
00649 double QwtDial::maxScaleArc() const
00650 {
00651 return d_data->maxScaleArc;
00652 }
00653
00662 void QwtDial::setOrigin( double origin )
00663 {
00664 invalidateCache();
00665
00666 d_data->origin = origin;
00667 sliderChange();
00668 }
00669
00676 double QwtDial::origin() const
00677 {
00678 return d_data->origin;
00679 }
00680
00685 QSize QwtDial::sizeHint() const
00686 {
00687 int sh = 0;
00688 if ( scaleDraw() )
00689 sh = qCeil( scaleDraw()->extent( font() ) );
00690
00691 const int d = 6 * sh + 2 * lineWidth();
00692
00693 QSize hint( d, d );
00694 if ( !isReadOnly() )
00695 hint = hint.expandedTo( QApplication::globalStrut() );
00696
00697 return hint;
00698 }
00699
00704 QSize QwtDial::minimumSizeHint() const
00705 {
00706 int sh = 0;
00707 if ( scaleDraw() )
00708 sh = qCeil( scaleDraw()->extent( font() ) );
00709
00710 const int d = 3 * sh + 2 * lineWidth();
00711
00712 return QSize( d, d );
00713 }
00714
00723 bool QwtDial::isScrollPosition( const QPoint &pos ) const
00724 {
00725 const QRegion region( innerRect(), QRegion::Ellipse );
00726 if ( region.contains( pos ) && ( pos != innerRect().center() ) )
00727 {
00728 double angle = QLineF( rect().center(), pos ).angle();
00729 if ( d_data->mode == QwtDial::RotateScale )
00730 angle = 360.0 - angle;
00731
00732 double valueAngle =
00733 qwtNormalizeDegrees( 90.0 - scaleMap().transform( value() ) );
00734
00735 d_data->mouseOffset = qwtNormalizeDegrees( angle - valueAngle );
00736 d_data->arcOffset = scaleMap().p1();
00737
00738 return true;
00739 }
00740
00741 return false;
00742 }
00743
00753 double QwtDial::scrolledTo( const QPoint &pos ) const
00754 {
00755 double angle = QLineF( rect().center(), pos ).angle();
00756 if ( d_data->mode == QwtDial::RotateScale )
00757 {
00758 angle += scaleMap().p1() - d_data->arcOffset;
00759 angle = 360.0 - angle;
00760 }
00761
00762 angle = qwtNormalizeDegrees( angle - d_data->mouseOffset );
00763 angle = qwtNormalizeDegrees( 90.0 - angle );
00764
00765 if ( scaleMap().pDist() >= 360.0 )
00766 {
00767 if ( angle < scaleMap().p1() )
00768 angle += 360.0;
00769
00770 if ( !wrapping() )
00771 {
00772 double boundedAngle = angle;
00773
00774 const double arc = angle - scaleMap().transform( value() );
00775 if ( qAbs( arc ) > 180.0 )
00776 {
00777 boundedAngle = ( arc > 0 )
00778 ? scaleMap().p1() : scaleMap().p2();
00779 }
00780
00781 d_data->mouseOffset += ( boundedAngle - angle );
00782
00783 angle = boundedAngle;
00784 }
00785 }
00786 else
00787 {
00788 const double boundedAngle =
00789 qwtBoundedAngle( scaleMap().p1(), angle, scaleMap().p2() );
00790
00791 if ( !wrapping() )
00792 d_data->mouseOffset += ( boundedAngle - angle );
00793
00794 angle = boundedAngle;
00795 }
00796
00797 return scaleMap().invTransform( angle );
00798 }
00799
00806 void QwtDial::changeEvent( QEvent *event )
00807 {
00808 switch( event->type() )
00809 {
00810 case QEvent::EnabledChange:
00811 case QEvent::FontChange:
00812 case QEvent::StyleChange:
00813 case QEvent::PaletteChange:
00814 case QEvent::LanguageChange:
00815 case QEvent::LocaleChange:
00816 {
00817 invalidateCache();
00818 break;
00819 }
00820 default:
00821 break;
00822 }
00823
00824 QwtAbstractSlider::changeEvent( event );
00825 }
00826
00831 void QwtDial::wheelEvent( QWheelEvent *event )
00832 {
00833 const QRegion region( innerRect(), QRegion::Ellipse );
00834 if ( region.contains( event->pos() ) )
00835 QwtAbstractSlider::wheelEvent( event );
00836 }
00837
00838 void QwtDial::setAngleRange( double angle, double span )
00839 {
00840 QwtRoundScaleDraw *sd = const_cast<QwtRoundScaleDraw *>( scaleDraw() );
00841 if ( sd )
00842 {
00843 angle = qwtNormalizeDegrees( angle - 270.0 );
00844 sd->setAngleRange( angle, angle + span );
00845 }
00846 }
00847
00852 void QwtDial::scaleChange()
00853 {
00854 invalidateCache();
00855 QwtAbstractSlider::scaleChange();
00856 }
00857
00858 void QwtDial::sliderChange()
00859 {
00860 setAngleRange( d_data->origin + d_data->minScaleArc,
00861 d_data->maxScaleArc - d_data->minScaleArc );
00862
00863 if ( mode() == RotateScale )
00864 {
00865 const double arc = scaleMap().transform( value() ) - scaleMap().p1();
00866 setAngleRange( d_data->origin - arc,
00867 d_data->maxScaleArc - d_data->minScaleArc );
00868 }
00869
00870 QwtAbstractSlider::sliderChange();
00871 }