# 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 }
    00165
    00170 const QwtRoundScaleDraw *QwtKnob::scaleDraw() const
    00171 {
    00172     return (QwtRoundScaleDraw *)abstractScaleDraw();
    00173 }
    00174
    00179 QwtRoundScaleDraw *QwtKnob::scaleDraw()
    00180 {
    00181     return (QwtRoundScaleDraw *)abstractScaleDraw();
    00182 }
    00183
    00189 void QwtKnob::drawKnob(QPainter *painter, const QRect &r)
    00190 {
    00191 #if QT_VERSION < 0x040000
    00192     const QBrush buttonBrush = colorGroup().brush(QColorGroup::Button);
    00193     const QColor buttonTextColor = colorGroup().buttonText();
    00194     const QColor lightColor = colorGroup().light();
    00195     const QColor darkColor = colorGroup().dark();
    00196 #else
    00197     const QBrush buttonBrush = palette().brush(QPalette::Button);
    00198     const QColor buttonTextColor = palette().color(QPalette::ButtonText);
    00199     const QColor lightColor = palette().color(QPalette::Light);
    00200     const QColor darkColor = palette().color(QPalette::Dark);
    00201 #endif
    00202
    00203     const int bw2 = d_data->borderWidth / 2;
    00204
    00205     const int radius = (qwtMin(r.width(), r.height()) - bw2) / 2;
    00206
    00207     const QRect aRect(
    00208         r.center().x() - radius, r.center().y() - radius,
    00209         2 * radius, 2 * radius);
    00210
    00211     //
    00212     // draw button face
    00213     //
    00214     painter->setBrush(buttonBrush);
    00215     painter->drawEllipse(aRect);
    00216
    00217     //
    00218     // draw button shades
    00219     //
    00220     QPen pn;
    00221     pn.setWidth(d_data->borderWidth);
    00222
    00223     pn.setColor(lightColor);
    00224     painter->setPen(pn);
    00225     painter->drawArc(aRect, 45*16, 180*16);
    00226
    00227     pn.setColor(darkColor);
    00228     painter->setPen(pn);
    00229     painter->drawArc(aRect, 225*16, 180*16);
    00230
    00231     //
    00232     // draw marker
    00233     //
    00234     if ( isValid() )
    00235         drawMarker(painter, d_data->angle, buttonTextColor);
    00236 }
    00237
    00244 void QwtKnob::valueChange()
    00245 {
    00246     recalcAngle();
    00247     update();
    00248     QwtAbstractSlider::valueChange();
    00249 }
    00250
    00257 double QwtKnob::getValue(const QPoint &p)
    00258 {
    00259     const double dx = double((rect().x() + rect().width() / 2) - p.x() );
    00260     const double dy = double((rect().y() + rect().height() / 2) - p.y() );
    00261
    00262     const double arc = atan2(-dx,dy) * 180.0 / M_PI;
    00263
    00264     double newValue =  0.5 * (minValue() + maxValue())
    00265        + (arc + d_data->nTurns * 360.0) * (maxValue() - minValue())
    00266       / d_data->totalAngle;
    00267
    00268     const double oneTurn = fabs(maxValue() - minValue()) * 360.0 / d_data->totalAngle;
    00269     const double eqValue = value() + mouseOffset();
    00270
    00271     if (fabs(newValue - eqValue) > 0.5 * oneTurn)
    00272     {
    00273         if (newValue < eqValue)
    00274            newValue += oneTurn;
    00275         else
    00276            newValue -= oneTurn;
    00277     }
    00278
    00279     return newValue;
    00280 }
    00281
    00288 void QwtKnob::getScrollMode(const QPoint &p, int &scrollMode, int &direction)
    00289 {
    00290     const int r = d_data->knobRect.width() / 2;
    00291
    00292     const int dx = d_data->knobRect.x() + r - p.x();
    00293     const int dy = d_data->knobRect.y() + r - p.y();
    00294
    00295     if ( (dx * dx) + (dy * dy) <= (r * r)) // point is inside the knob
    00296     {
    00297         scrollMode = ScrMouse;
    00298         direction = 0;
    00299     }
    00300     else                                // point lies outside
    00301     {
    00302         scrollMode = ScrTimer;
    00303         double arc = atan2(double(-dx),double(dy)) * 180.0 / M_PI;
    00304         if ( arc < d_data->angle)
    00305            direction = -1;
    00306         else if (arc > d_data->angle)
    00307            direction = 1;
    00308         else
    00309            direction = 0;
    00310     }
    00311 }
    00312
    00313
    00319 void QwtKnob::rangeChange()
    00320 {
    00321     if (autoScale())
    00322         rescale(minValue(), maxValue());
    00323
    00324     layoutKnob();
    00325     recalcAngle();
    00326 }
    00327
    00331 void QwtKnob::resizeEvent(QResizeEvent *)
    00332 {
    00333     layoutKnob( false );
    00334 }
    00335
    00337 //  the current rect and fonts.
    00338 //  \param update_geometry  notify the layout system and call update
    00339 //         to redraw the scale
    00340 void QwtKnob::layoutKnob( bool update_geometry )
    00341 {
    00342     const QRect r = rect();
    00343     const int radius = d_data->knobWidth / 2;
    00344
    00345     d_data->knobRect.setWidth(2 * radius);
    00346     d_data->knobRect.setHeight(2 * radius);
    00347     d_data->knobRect.moveCenter(r.center());
    00348
    00349     scaleDraw()->setRadius(radius + d_data->scaleDist);
    00350     scaleDraw()->moveCenter(r.center());
    00351
    00352     if ( update_geometry )
    00353     {
    00354         updateGeometry();
    00355         update();
    00356     }
    00357 }
    00358
    00362 void QwtKnob::paintEvent(QPaintEvent *e)
    00363 {
    00364     const QRect &ur = e->rect();
    00365     if ( ur.isValid() )
    00366     {
    00367 #if QT_VERSION < 0x040000
    00368         QwtPaintBuffer paintBuffer(this, ur);
    00369         draw(paintBuffer.painter(), ur);
    00370 #else
    00371         QPainter painter(this);
    00372         if ( paintEngine()->hasFeature(QPaintEngine::Antialiasing) )
    00373             painter.setRenderHint(QPainter::Antialiasing);
    00374         draw(&painter, ur);
    00375 #endif
    00376     }
    00377 }
    00378
    00382 void QwtKnob::draw(QPainter *painter, const QRect& ur)
    00383 {
    00384     if ( !d_data->knobRect.contains( ur ) ) // event from valueChange()
    00385     {
    00386 #if QT_VERSION < 0x040000
    00387         scaleDraw()->draw( painter, colorGroup() );
    00388 #else
    00389         scaleDraw()->draw( painter, palette() );
    00390 #endif
    00391     }
    00392
    00393     drawKnob( painter, d_data->knobRect );
    00394
    00395     if ( hasFocus() )
    00396         QwtPainter::drawFocusRect(painter, this);
    00397 }
    00398
    00405 void QwtKnob::drawMarker(QPainter *p, double arc, const QColor &c)
    00406 {
    00407     const double rarc = arc * M_PI / 180.0;
    00408     const double ca = cos(rarc);
    00409     const double sa = - sin(rarc);
    00410
    00411     int radius = d_data->knobRect.width() / 2 - d_data->borderWidth;
    00412     if (radius < 3)
    00413         radius = 3;
    00414
    00415     const int ym = d_data->knobRect.y() + radius + d_data->borderWidth;
    00416     const int xm = d_data->knobRect.x() + radius + d_data->borderWidth;
    00417
    00418     switch (d_data->symbol)
    00419     {
    00420         case Dot:
    00421         {
    00422             p->setBrush(c);
    00423             p->setPen(Qt::NoPen);
    00424
    00425             const double rb = double(qwtMax(radius - 4 - d_data->dotWidth / 2, 0));
    00426             p->drawEllipse(xm - qRound(sa * rb) - d_data->dotWidth / 2,
    00427                    ym - qRound(ca * rb) - d_data->dotWidth / 2,
    00428                    d_data->dotWidth, d_data->dotWidth);
    00429             break;
    00430         }
    00431         case Line:
    00432         {
    00433             p->setPen(QPen(c, 2));
    00434
    00435             const double rb = qwtMax(double((radius - 4) / 3.0), 0.0);
    00436             const double re = qwtMax(double(radius - 4), 0.0);
    00437
    00438             p->drawLine ( xm - qRound(sa * rb), ym - qRound(ca * rb),
    00439                 xm - qRound(sa * re), ym - qRound(ca * re));
    00440
    00441             break;
    00442         }
    00443     }
    00444 }
    00445
    00452 void QwtKnob::setKnobWidth(int w)
    00453 {
    00454     d_data->knobWidth = qwtMax(w,5);
    00455     layoutKnob();
    00456 }
    00457
    00459 int QwtKnob::knobWidth() const
    00460 {
    00461     return d_data->knobWidth;
    00462 }
    00463
    00468 void QwtKnob::setBorderWidth(int bw)
    00469 {
    00470     d_data->borderWidth = qwtMax(bw, 0);
    00471     layoutKnob();
    00472 }
    00473
    00475 int QwtKnob::borderWidth() const
    00476 {
    00477     return d_data->borderWidth;
    00478 }
    00479
    00484 void QwtKnob::recalcAngle()
    00485 {
    00486     //
    00487     // calculate the angle corresponding to the value
    00488     //
    00489     if (maxValue() == minValue())
    00490     {
    00491         d_data->angle = 0;
    00492         d_data->nTurns = 0;
    00493     }
    00494     else
    00495     {
    00496         d_data->angle = (value() - 0.5 * (minValue() + maxValue()))
    00497             / (maxValue() - minValue()) * d_data->totalAngle;
    00498         d_data->nTurns = floor((d_data->angle + 180.0) / 360.0);
    00499         d_data->angle = d_data->angle - d_data->nTurns * 360.0;
    00500     }
    00501 }
    00502
    00503
    00508 void QwtKnob::scaleChange()
    00509 {
    00510     layoutKnob();
    00511 }
    00512
    00517 void QwtKnob::fontChange(const QFont &f)
    00518 {
    00519     QwtAbstractSlider::fontChange( f );
    00520     layoutKnob();
    00521 }
    00522
    00526 QSize QwtKnob::sizeHint() const
    00527 {
    00528     return minimumSizeHint();
    00529 }
    00530
    00536 QSize QwtKnob::minimumSizeHint() const
    00537 {
    00538     // Add the scale radial thickness to the knobWidth
    00539     const int sh = scaleDraw()->extent( QPen(), font() );
    00540     const int d = 2 * sh + 2 * d_data->scaleDist + d_data->knobWidth;
    00541
    00542     return QSize( d, d );
    00543 }