# 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 #include <qpalette.h>
    00012 #include <qstyle.h>
    00013 #include <qevent.h>
    00014 #include "qwt_round_scale_draw.h"
    00015 #include "qwt_knob.h"
    00016 #include "qwt_math.h"
    00017 #include "qwt_painter.h"
    00018 #include "qwt_paint_buffer.h"
    00019
    00020 class QwtKnob::PrivateData
    00021 {
    00022 public:
    00023     PrivateData()
    00024     {
    00025         angle = 0.0;
    00026         nTurns = 0.0;
    00027         borderWidth = 2;
    00028         borderDist = 4;
    00029         totalAngle = 270.0;
    00030         scaleDist = 4;
    00031         symbol = Line;
    00032         maxScaleTicks = 11;
    00033         knobWidth = 50;
    00034         dotWidth = 8;
    00035     }
    00036
    00037     int borderWidth;
    00038     int borderDist;
    00039     int scaleDist;
    00040     int maxScaleTicks;
    00041     int knobWidth;
    00042     int dotWidth;
    00043
    00044     Symbol symbol;
    00045     double angle;
    00046     double totalAngle;
    00047     double nTurns;
    00048
    00049     QRect knobRect; // bounding rect of the knob without scale
    00050 };
    00051
    00056 QwtKnob::QwtKnob(QWidget* parent):
    00057     QwtAbstractSlider(Qt::Horizontal, parent)
    00058 {
    00059     initKnob();
    00060 }
    00061
    00062 #if QT_VERSION < 0x040000
    00063
    00068 QwtKnob::QwtKnob(QWidget* parent, const char *name):
    00069     QwtAbstractSlider(Qt::Horizontal, parent)
    00070 {
    00071     setName(name);
    00072     initKnob();
    00073 }
    00074 #endif
    00075
    00076 void QwtKnob::initKnob()
    00077 {
    00078 #if QT_VERSION < 0x040000
    00079     setWFlags(Qt::WNoAutoErase);
    00080 #endif
    00081
    00082     d_data = new PrivateData;
    00083
    00084     setScaleDraw(new QwtRoundScaleDraw());
    00085
    00086     setUpdateTime(50);
    00087     setTotalAngle( 270.0 );
    00088     recalcAngle();
    00089     setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
    00090
    00091     setRange(0.0, 10.0, 1.0);
    00092     setValue(0.0);
    00093 }
    00094
    00096 QwtKnob::~QwtKnob()
    00097 {
    00098     delete d_data;
    00099 }
    00100
    00105 void QwtKnob::setSymbol(QwtKnob::Symbol s)
    00106 {
    00107     if ( d_data->symbol != s )
    00108     {
    00109         d_data->symbol = s;
    00110         update();
    00111     }
    00112 }
    00113
    00118 QwtKnob::Symbol QwtKnob::symbol() const
    00119 {
    00120     return d_data->symbol;
    00121 }
    00122
    00131 void QwtKnob::setTotalAngle (double angle)
    00132 {
    00133     if (angle < 10.0)
    00134        d_data->totalAngle = 10.0;
    00135     else
    00136        d_data->totalAngle = angle;
    00137
    00138     scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle,
    00139         0.5 * d_data->totalAngle);
    00140     layoutKnob();
    00141 }
    00142
    00144 double QwtKnob::totalAngle() const
    00145 {
    00146     return d_data->totalAngle;
    00147 }
    00148
    00158 void QwtKnob::setScaleDraw(QwtRoundScaleDraw *scaleDraw)
    00159 {
    00160     setAbstractScaleDraw(scaleDraw);
    00161     setTotalAngle(d_data->totalAngle);
    00162 }
    00163
    00168 const QwtRoundScaleDraw *QwtKnob::scaleDraw() const
    00169 {
    00170     return (QwtRoundScaleDraw *)abstractScaleDraw();
    00171 }
    00172
    00177 QwtRoundScaleDraw *QwtKnob::scaleDraw()
    00178 {
    00179     return (QwtRoundScaleDraw *)abstractScaleDraw();
    00180 }
    00181
    00187 void QwtKnob::drawKnob(QPainter *painter, const QRect &r)
    00188 {
    00189 #if QT_VERSION < 0x040000
    00190     const QBrush buttonBrush = colorGroup().brush(QColorGroup::Button);
    00191     const QColor buttonTextColor = colorGroup().buttonText();
    00192     const QColor lightColor = colorGroup().light();
    00193     const QColor darkColor = colorGroup().dark();
    00194 #else
    00195     const QBrush buttonBrush = palette().brush(QPalette::Button);
    00196     const QColor buttonTextColor = palette().color(QPalette::ButtonText);
    00197     const QColor lightColor = palette().color(QPalette::Light);
    00198     const QColor darkColor = palette().color(QPalette::Dark);
    00199 #endif
    00200
    00201     const int bw2 = d_data->borderWidth / 2;
    00202
    00203     const int radius = (qwtMin(r.width(), r.height()) - bw2) / 2;
    00204
    00205     const QRect aRect(
    00206         r.center().x() - radius, r.center().y() - radius,
    00207         2 * radius, 2 * radius);
    00208
    00209     //
    00210     // draw button face
    00211     //
    00212     painter->setBrush(buttonBrush);
    00213     painter->drawEllipse(aRect);
    00214
    00215     //
    00216     // draw button shades
    00217     //
    00218     QPen pn;
    00219     pn.setWidth(d_data->borderWidth);
    00220
    00221     pn.setColor(lightColor);
    00222     painter->setPen(pn);
    00223     painter->drawArc(aRect, 45*16, 180*16);
    00224
    00225     pn.setColor(darkColor);
    00226     painter->setPen(pn);
    00227     painter->drawArc(aRect, 225*16, 180*16);
    00228
    00229     //
    00230     // draw marker
    00231     //
    00232     if ( isValid() )
    00233         drawMarker(painter, d_data->angle, buttonTextColor);
    00234 }
    00235
    00242 void QwtKnob::valueChange()
    00243 {
    00244     recalcAngle();
    00245     update();
    00246     QwtAbstractSlider::valueChange();
    00247 }
    00248
    00255 double QwtKnob::getValue(const QPoint &p)
    00256 {
    00257     const double dx = double((rect().x() + rect().width() / 2) - p.x() );
    00258     const double dy = double((rect().y() + rect().height() / 2) - p.y() );
    00259
    00260     const double arc = atan2(-dx,dy) * 180.0 / M_PI;
    00261
    00262     double newValue =  0.5 * (minValue() + maxValue())
    00263        + (arc + d_data->nTurns * 360.0) * (maxValue() - minValue())
    00264       / d_data->totalAngle;
    00265
    00266     const double oneTurn = fabs(maxValue() - minValue()) * 360.0 / d_data->totalAngle;
    00267     const double eqValue = value() + mouseOffset();
    00268
    00269     if (fabs(newValue - eqValue) > 0.5 * oneTurn)
    00270     {
    00271         if (newValue < eqValue)
    00272            newValue += oneTurn;
    00273         else
    00274            newValue -= oneTurn;
    00275     }
    00276
    00277     return newValue;
    00278 }
    00279
    00286 void QwtKnob::getScrollMode(const QPoint &p, int &scrollMode, int &direction)
    00287 {
    00288     const int r = d_data->knobRect.width() / 2;
    00289
    00290     const int dx = d_data->knobRect.x() + r - p.x();
    00291     const int dy = d_data->knobRect.y() + r - p.y();
    00292
    00293     if ( (dx * dx) + (dy * dy) <= (r * r)) // point is inside the knob
    00294     {
    00295         scrollMode = ScrMouse;
    00296         direction = 0;
    00297     }
    00298     else                                // point lies outside
    00299     {
    00300         scrollMode = ScrTimer;
    00301         double arc = atan2(double(-dx),double(dy)) * 180.0 / M_PI;
    00302         if ( arc < d_data->angle)
    00303            direction = -1;
    00304         else if (arc > d_data->angle)
    00305            direction = 1;
    00306         else
    00307            direction = 0;
    00308     }
    00309 }
    00310
    00311
    00317 void QwtKnob::rangeChange()
    00318 {
    00319     if (autoScale())
    00320         rescale(minValue(), maxValue());
    00321
    00322     layoutKnob();
    00323     recalcAngle();
    00324 }
    00325
    00329 void QwtKnob::resizeEvent(QResizeEvent *)
    00330 {
    00331     layoutKnob( false );
    00332 }
    00333
    00335 //  the current rect and fonts.
    00336 //  \param update_geometry  notify the layout system and call update
    00337 //         to redraw the scale
    00338 void QwtKnob::layoutKnob( bool update_geometry )
    00339 {
    00340     const QRect r = rect();
    00341     const int radius = d_data->knobWidth / 2;
    00342
    00343     d_data->knobRect.setWidth(2 * radius);
    00344     d_data->knobRect.setHeight(2 * radius);
    00345     d_data->knobRect.moveCenter(r.center());
    00346
    00347     scaleDraw()->setRadius(radius + d_data->scaleDist);
    00348     scaleDraw()->moveCenter(r.center());
    00349
    00350     if ( update_geometry )
    00351     {
    00352         updateGeometry();
    00353         update();
    00354     }
    00355 }
    00356
    00360 void QwtKnob::paintEvent(QPaintEvent *e)
    00361 {
    00362     const QRect &ur = e->rect();
    00363     if ( ur.isValid() )
    00364     {
    00365 #if QT_VERSION < 0x040000
    00366         QwtPaintBuffer paintBuffer(this, ur);
    00367         draw(paintBuffer.painter(), ur);
    00368 #else
    00369         QPainter painter(this);
    00370         painter.setRenderHint(QPainter::Antialiasing);
    00371         draw(&painter, ur);
    00372 #endif
    00373     }
    00374 }
    00375
    00379 void QwtKnob::draw(QPainter *painter, const QRect& ur)
    00380 {
    00381     if ( !d_data->knobRect.contains( ur ) ) // event from valueChange()
    00382     {
    00383 #if QT_VERSION < 0x040000
    00384         scaleDraw()->draw( painter, colorGroup() );
    00385 #else
    00386         scaleDraw()->draw( painter, palette() );
    00387 #endif
    00388     }
    00389
    00390     drawKnob( painter, d_data->knobRect );
    00391
    00392     if ( hasFocus() )
    00393         QwtPainter::drawFocusRect(painter, this);
    00394 }
    00395
    00402 void QwtKnob::drawMarker(QPainter *p, double arc, const QColor &c)
    00403 {
    00404     const double rarc = arc * M_PI / 180.0;
    00405     const double ca = cos(rarc);
    00406     const double sa = - sin(rarc);
    00407
    00408     int radius = d_data->knobRect.width() / 2 - d_data->borderWidth;
    00409     if (radius < 3)
    00410         radius = 3;
    00411
    00412     const int ym = d_data->knobRect.y() + radius + d_data->borderWidth;
    00413     const int xm = d_data->knobRect.x() + radius + d_data->borderWidth;
    00414
    00415     switch (d_data->symbol)
    00416     {
    00417         case Dot:
    00418         {
    00419             p->setBrush(c);
    00420             p->setPen(Qt::NoPen);
    00421
    00422             const double rb = double(qwtMax(radius - 4 - d_data->dotWidth / 2, 0));
    00423             p->drawEllipse(xm - qRound(sa * rb) - d_data->dotWidth / 2,
    00424                    ym - qRound(ca * rb) - d_data->dotWidth / 2,
    00425                    d_data->dotWidth, d_data->dotWidth);
    00426             break;
    00427         }
    00428         case Line:
    00429         {
    00430             p->setPen(QPen(c, 2));
    00431
    00432             const double rb = qwtMax(double((radius - 4) / 3.0), 0.0);
    00433             const double re = qwtMax(double(radius - 4), 0.0);
    00434
    00435             p->drawLine ( xm - qRound(sa * rb), ym - qRound(ca * rb),
    00436                 xm - qRound(sa * re), ym - qRound(ca * re));
    00437
    00438             break;
    00439         }
    00440     }
    00441 }
    00442
    00449 void QwtKnob::setKnobWidth(int w)
    00450 {
    00451     d_data->knobWidth = qwtMax(w,5);
    00452     layoutKnob();
    00453 }
    00454
    00456 int QwtKnob::knobWidth() const
    00457 {
    00458     return d_data->knobWidth;
    00459 }
    00460
    00465 void QwtKnob::setBorderWidth(int bw)
    00466 {
    00467     d_data->borderWidth = qwtMax(bw, 0);
    00468     layoutKnob();
    00469 }
    00470
    00472 int QwtKnob::borderWidth() const
    00473 {
    00474     return d_data->borderWidth;
    00475 }
    00476
    00481 void QwtKnob::recalcAngle()
    00482 {
    00483     //
    00484     // calculate the angle corresponding to the value
    00485     //
    00486     if (maxValue() == minValue())
    00487     {
    00488         d_data->angle = 0;
    00489         d_data->nTurns = 0;
    00490     }
    00491     else
    00492     {
    00493         d_data->angle = (value() - 0.5 * (minValue() + maxValue()))
    00494             / (maxValue() - minValue()) * d_data->totalAngle;
    00495         d_data->nTurns = floor((d_data->angle + 180.0) / 360.0);
    00496         d_data->angle = d_data->angle - d_data->nTurns * 360.0;
    00497     }
    00498 }
    00499
    00500
    00505 void QwtKnob::scaleChange()
    00506 {
    00507     layoutKnob();
    00508 }
    00509
    00514 void QwtKnob::fontChange(const QFont &f)
    00515 {
    00516     QwtAbstractSlider::fontChange( f );
    00517     layoutKnob();
    00518 }
    00519
    00523 QSize QwtKnob::sizeHint() const
    00524 {
    00525     return minimumSizeHint();
    00526 }
    00527
    00533 QSize QwtKnob::minimumSizeHint() const
    00534 {
    00535     // Add the scale radial thickness to the knobWidth
    00536     const int sh = scaleDraw()->extent( QPen(), font() );
    00537     const int d = 2 * sh + 2 * d_data->scaleDist + d_data->knobWidth;
    00538
    00539     return QSize( d, d );
    00540 }