# qwt_knob.cpp

    00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
    00002  * Qwt Widget Library
    00003  * Copyright (C) 1997   Josef Wilgen
    00004  * Copyright (C) 2002   Uwe Rathmann
    00005  *
    00006  * This library is free software; you can redistribute it and/or
    00007  * modify it under the terms of the Qwt License, Version 1.0
    00008  *****************************************************************************/
    00009
    00010 #include <qpainter.h>
    00011 #if QT_VERSION >= 0x040000
    00012 #include <qpaintengine.h>
    00013 #endif
    00014 #include <qpalette.h>
    00015 #include <qstyle.h>
    00016 #include <qevent.h>
    00017 #include "qwt_round_scale_draw.h"
    00018 #include "qwt_knob.h"
    00019 #include "qwt_math.h"
    00020 #include "qwt_painter.h"
    00021 #include "qwt_paint_buffer.h"
    00022
    00023 class QwtKnob::PrivateData
    00024 {
    00025 public:
    00026     PrivateData()
    00027     {
    00028         angle = 0.0;
    00029         nTurns = 0.0;
    00030         borderWidth = 2;
    00031         borderDist = 4;
    00032         totalAngle = 270.0;
    00033         scaleDist = 4;
    00034         symbol = Line;
    00035         maxScaleTicks = 11;
    00036         knobWidth = 50;
    00037         dotWidth = 8;
    00038     }
    00039
    00040     int borderWidth;
    00041     int borderDist;
    00042     int scaleDist;
    00043     int maxScaleTicks;
    00044     int knobWidth;
    00045     int dotWidth;
    00046
    00047     Symbol symbol;
    00048     double angle;
    00049     double totalAngle;
    00050     double nTurns;
    00051
    00052     QRect knobRect; // bounding rect of the knob without scale
    00053 };
    00054
    00059 QwtKnob::QwtKnob(QWidget* parent):
    00060     QwtAbstractSlider(Qt::Horizontal, parent)
    00061 {
    00062     initKnob();
    00063 }
    00064
    00065 #if QT_VERSION < 0x040000
    00066
    00071 QwtKnob::QwtKnob(QWidget* parent, const char *name):
    00072     QwtAbstractSlider(Qt::Horizontal, parent)
    00073 {
    00074     setName(name);
    00075     initKnob();
    00076 }
    00077 #endif
    00078
    00079 void QwtKnob::initKnob()
    00080 {
    00081 #if QT_VERSION < 0x040000
    00082     setWFlags(Qt::WNoAutoErase);
    00083 #endif
    00084
    00085     d_data = new PrivateData;
    00086
    00087     setScaleDraw(new QwtRoundScaleDraw());
    00088
    00089     setUpdateTime(50);
    00090     setTotalAngle( 270.0 );
    00091     recalcAngle();
    00092     setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
    00093
    00094     setRange(0.0, 10.0, 1.0);
    00095     setValue(0.0);
    00096 }
    00097
    00099 QwtKnob::~QwtKnob()
    00100 {
    00101     delete d_data;
    00102 }
    00103
    00108 void QwtKnob::setSymbol(QwtKnob::Symbol s)
    00109 {
    00110     if ( d_data->symbol != s )
    00111     {
    00112         d_data->symbol = s;
    00113         update();
    00114     }
    00115 }
    00116
    00121 QwtKnob::Symbol QwtKnob::symbol() const
    00122 {
    00123     return d_data->symbol;
    00124 }
    00125
    00134 void QwtKnob::setTotalAngle (double angle)
    00135 {
    00136     if (angle < 10.0)
    00137        d_data->totalAngle = 10.0;
    00138     else
    00139        d_data->totalAngle = angle;
    00140
    00141     scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle,
    00142         0.5 * d_data->totalAngle);
    00143     layoutKnob();
    00144 }
    00145
    00147 double QwtKnob::totalAngle() const
    00148 {
    00149     return d_data->totalAngle;
    00150 }
    00151
    00161 void QwtKnob::setScaleDraw(QwtRoundScaleDraw *scaleDraw)
    00162 {
    00163     setAbstractScaleDraw(scaleDraw);
    00164     setTotalAngle(d_data->totalAngle);
    00165 }
    00166
    00171 const QwtRoundScaleDraw *QwtKnob::scaleDraw() const
    00172 {
    00173     return (QwtRoundScaleDraw *)abstractScaleDraw();
    00174 }
    00175
    00180 QwtRoundScaleDraw *QwtKnob::scaleDraw()
    00181 {
    00182     return (QwtRoundScaleDraw *)abstractScaleDraw();
    00183 }
    00184
    00190 void QwtKnob::drawKnob(QPainter *painter, const QRect &r)
    00191 {
    00192 #if QT_VERSION < 0x040000
    00193     const QBrush buttonBrush = colorGroup().brush(QColorGroup::Button);
    00194     const QColor buttonTextColor = colorGroup().buttonText();
    00195     const QColor lightColor = colorGroup().light();
    00196     const QColor darkColor = colorGroup().dark();
    00197 #else
    00198     const QBrush buttonBrush = palette().brush(QPalette::Button);
    00199     const QColor buttonTextColor = palette().color(QPalette::ButtonText);
    00200     const QColor lightColor = palette().color(QPalette::Light);
    00201     const QColor darkColor = palette().color(QPalette::Dark);
    00202 #endif
    00203
    00204     const int bw2 = d_data->borderWidth / 2;
    00205
    00206     const int radius = (qwtMin(r.width(), r.height()) - bw2) / 2;
    00207
    00208     const QRect aRect(
    00209         r.center().x() - radius, r.center().y() - radius,
    00210         2 * radius, 2 * radius);
    00211
    00212     //
    00213     // draw button face
    00214     //
    00215     painter->setBrush(buttonBrush);
    00216     painter->drawEllipse(aRect);
    00217
    00218     //
    00219     // draw button shades
    00220     //
    00221     QPen pn;
    00222     pn.setWidth(d_data->borderWidth);
    00223
    00224     pn.setColor(lightColor);
    00225     painter->setPen(pn);
    00226     painter->drawArc(aRect, 45*16, 180*16);
    00227
    00228     pn.setColor(darkColor);
    00229     painter->setPen(pn);
    00230     painter->drawArc(aRect, 225*16, 180*16);
    00231
    00232     //
    00233     // draw marker
    00234     //
    00235     if ( isValid() )
    00236         drawMarker(painter, d_data->angle, buttonTextColor);
    00237 }
    00238
    00245 void QwtKnob::valueChange()
    00246 {
    00247     recalcAngle();
    00248     update();
    00249     QwtAbstractSlider::valueChange();
    00250 }
    00251
    00258 double QwtKnob::getValue(const QPoint &p)
    00259 {
    00260     const double dx = double((rect().x() + rect().width() / 2) - p.x() );
    00261     const double dy = double((rect().y() + rect().height() / 2) - p.y() );
    00262
    00263     const double arc = atan2(-dx,dy) * 180.0 / M_PI;
    00264
    00265     double newValue =  0.5 * (minValue() + maxValue())
    00266        + (arc + d_data->nTurns * 360.0) * (maxValue() - minValue())
    00267       / d_data->totalAngle;
    00268
    00269     const double oneTurn = fabs(maxValue() - minValue()) * 360.0 / d_data->totalAngle;
    00270     const double eqValue = value() + mouseOffset();
    00271
    00272     if (fabs(newValue - eqValue) > 0.5 * oneTurn)
    00273     {
    00274         if (newValue < eqValue)
    00275            newValue += oneTurn;
    00276         else
    00277            newValue -= oneTurn;
    00278     }
    00279
    00280     return newValue;
    00281 }
    00282
    00289 void QwtKnob::getScrollMode(const QPoint &p, int &scrollMode, int &direction)
    00290 {
    00291     const int r = d_data->knobRect.width() / 2;
    00292
    00293     const int dx = d_data->knobRect.x() + r - p.x();
    00294     const int dy = d_data->knobRect.y() + r - p.y();
    00295
    00296     if ( (dx * dx) + (dy * dy) <= (r * r)) // point is inside the knob
    00297     {
    00298         scrollMode = ScrMouse;
    00299         direction = 0;
    00300     }
    00301     else                                // point lies outside
    00302     {
    00303         scrollMode = ScrTimer;
    00304         double arc = atan2(double(-dx),double(dy)) * 180.0 / M_PI;
    00305         if ( arc < d_data->angle)
    00306            direction = -1;
    00307         else if (arc > d_data->angle)
    00308            direction = 1;
    00309         else
    00310            direction = 0;
    00311     }
    00312 }
    00313
    00314
    00320 void QwtKnob::rangeChange()
    00321 {
    00322     if (autoScale())
    00323         rescale(minValue(), maxValue());
    00324
    00325     layoutKnob();
    00326     recalcAngle();
    00327 }
    00328
    00332 void QwtKnob::resizeEvent(QResizeEvent *)
    00333 {
    00334     layoutKnob( false );
    00335 }
    00336
    00338 //  the current rect and fonts.
    00339 //  \param update_geometry  notify the layout system and call update
    00340 //         to redraw the scale
    00341 void QwtKnob::layoutKnob( bool update_geometry )
    00342 {
    00343     const QRect r = rect();
    00344     const int radius = d_data->knobWidth / 2;
    00345
    00346     d_data->knobRect.setWidth(2 * radius);
    00347     d_data->knobRect.setHeight(2 * radius);
    00348     d_data->knobRect.moveCenter(r.center());
    00349
    00350     scaleDraw()->setRadius(radius + d_data->scaleDist);
    00351     scaleDraw()->moveCenter(r.center());
    00352
    00353     if ( update_geometry )
    00354     {
    00355         updateGeometry();
    00356         update();
    00357     }
    00358 }
    00359
    00363 void QwtKnob::paintEvent(QPaintEvent *e)
    00364 {
    00365     const QRect &ur = e->rect();
    00366     if ( ur.isValid() )
    00367     {
    00368 #if QT_VERSION < 0x040000
    00369         QwtPaintBuffer paintBuffer(this, ur);
    00370         draw(paintBuffer.painter(), ur);
    00371 #else
    00372         QPainter painter(this);
    00373         if ( paintEngine()->hasFeature(QPaintEngine::Antialiasing) )
    00374             painter.setRenderHint(QPainter::Antialiasing);
    00375         draw(&painter, ur);
    00376 #endif
    00377     }
    00378 }
    00379
    00383 void QwtKnob::draw(QPainter *painter, const QRect& ur)
    00384 {
    00385     if ( !d_data->knobRect.contains( ur ) ) // event from valueChange()
    00386     {
    00387 #if QT_VERSION < 0x040000
    00388         scaleDraw()->draw( painter, colorGroup() );
    00389 #else
    00390         scaleDraw()->draw( painter, palette() );
    00391 #endif
    00392     }
    00393
    00394     drawKnob( painter, d_data->knobRect );
    00395
    00396     if ( hasFocus() )
    00397         QwtPainter::drawFocusRect(painter, this);
    00398 }
    00399
    00406 void QwtKnob::drawMarker(QPainter *p, double arc, const QColor &c)
    00407 {
    00408     const double rarc = arc * M_PI / 180.0;
    00409     const double ca = cos(rarc);
    00410     const double sa = - sin(rarc);
    00411
    00412     int radius = d_data->knobRect.width() / 2 - d_data->borderWidth;
    00413     if (radius < 3)
    00414         radius = 3;
    00415
    00416     const int ym = d_data->knobRect.y() + radius + d_data->borderWidth;
    00417     const int xm = d_data->knobRect.x() + radius + d_data->borderWidth;
    00418
    00419     switch (d_data->symbol)
    00420     {
    00421         case Dot:
    00422         {
    00423             p->setBrush(c);
    00424             p->setPen(Qt::NoPen);
    00425
    00426             const double rb = double(qwtMax(radius - 4 - d_data->dotWidth / 2, 0));
    00427             p->drawEllipse(xm - qRound(sa * rb) - d_data->dotWidth / 2,
    00428                    ym - qRound(ca * rb) - d_data->dotWidth / 2,
    00429                    d_data->dotWidth, d_data->dotWidth);
    00430             break;
    00431         }
    00432         case Line:
    00433         {
    00434             p->setPen(QPen(c, 2));
    00435
    00436             const double rb = qwtMax(double((radius - 4) / 3.0), 0.0);
    00437             const double re = qwtMax(double(radius - 4), 0.0);
    00438
    00439             p->drawLine ( xm - qRound(sa * rb), ym - qRound(ca * rb),
    00440                 xm - qRound(sa * re), ym - qRound(ca * re));
    00441
    00442             break;
    00443         }
    00444     }
    00445 }
    00446
    00453 void QwtKnob::setKnobWidth(int w)
    00454 {
    00455     d_data->knobWidth = qwtMax(w,5);
    00456     layoutKnob();
    00457 }
    00458
    00460 int QwtKnob::knobWidth() const
    00461 {
    00462     return d_data->knobWidth;
    00463 }
    00464
    00469 void QwtKnob::setBorderWidth(int bw)
    00470 {
    00471     d_data->borderWidth = qwtMax(bw, 0);
    00472     layoutKnob();
    00473 }
    00474
    00476 int QwtKnob::borderWidth() const
    00477 {
    00478     return d_data->borderWidth;
    00479 }
    00480
    00485 void QwtKnob::recalcAngle()
    00486 {
    00487     //
    00488     // calculate the angle corresponding to the value
    00489     //
    00490     if (maxValue() == minValue())
    00491     {
    00492         d_data->angle = 0;
    00493         d_data->nTurns = 0;
    00494     }
    00495     else
    00496     {
    00497         d_data->angle = (value() - 0.5 * (minValue() + maxValue()))
    00498             / (maxValue() - minValue()) * d_data->totalAngle;
    00499         d_data->nTurns = floor((d_data->angle + 180.0) / 360.0);
    00500         d_data->angle = d_data->angle - d_data->nTurns * 360.0;
    00501     }
    00502 }
    00503
    00504
    00509 void QwtKnob::scaleChange()
    00510 {
    00511     layoutKnob();
    00512 }
    00513
    00518 void QwtKnob::fontChange(const QFont &f)
    00519 {
    00520     QwtAbstractSlider::fontChange( f );
    00521     layoutKnob();
    00522 }
    00523
    00527 QSize QwtKnob::sizeHint() const
    00528 {
    00529     return minimumSizeHint();
    00530 }
    00531
    00537 QSize QwtKnob::minimumSizeHint() const
    00538 {
    00539     // Add the scale radial thickness to the knobWidth
    00540     const int sh = scaleDraw()->extent( QPen(), font() );
    00541     const int d = 2 * sh + 2 * d_data->scaleDist + d_data->knobWidth;
    00542
    00543     return QSize( d, d );
    00544 }