# qwt_wheel.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 <qevent.h>
    00011 #include <qdrawutil.h>
    00012 #include <qpainter.h>
    00013 #include <qstyle.h>
    00014 #include "qwt_math.h"
    00015 #include "qwt_painter.h"
    00016 #include "qwt_paint_buffer.h"
    00017 #include "qwt_wheel.h"
    00018
    00019 #define NUM_COLORS 30
    00020
    00021 class QwtWheel::PrivateData
    00022 {
    00023 public:
    00024     PrivateData()
    00025     {
    00026         viewAngle = 175.0;
    00027         totalAngle = 360.0;
    00028         tickCnt = 10;
    00029         intBorder = 2;
    00030         borderWidth = 2;
    00031         wheelWidth = 20;
    00032 #if QT_VERSION < 0x040000
    00033         allocContext = 0;
    00034 #endif
    00035     };
    00036
    00037     QRect sliderRect;
    00038     double viewAngle;
    00039     double totalAngle;
    00040     int tickCnt;
    00041     int intBorder;
    00042     int borderWidth;
    00043     int wheelWidth;
    00044 #if QT_VERSION < 0x040000
    00045     int allocContext;
    00046 #endif
    00047     QColor colors[NUM_COLORS];
    00048 };
    00049
    00051 QwtWheel::QwtWheel(QWidget *parent):
    00052     QwtAbstractSlider(Qt::Horizontal, parent)
    00053 {
    00054     initWheel();
    00055 }
    00056
    00057 #if QT_VERSION < 0x040000
    00058 QwtWheel::QwtWheel(QWidget *parent, const char *name):
    00059     QwtAbstractSlider(Qt::Horizontal, parent)
    00060 {
    00061     setName(name);
    00062     initWheel();
    00063 }
    00064 #endif
    00065
    00066 void QwtWheel::initWheel()
    00067 {
    00068     d_data = new PrivateData;
    00069
    00070 #if QT_VERSION < 0x040000
    00071     setWFlags(Qt::WNoAutoErase);
    00072 #endif
    00073
    00074     setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
    00075
    00076 #if QT_VERSION >= 0x040000
    00077     setAttribute(Qt::WA_WState_OwnSizePolicy, false);
    00078 #else
    00079     clearWState( WState_OwnSizePolicy );
    00080 #endif
    00081
    00082     setUpdateTime(50);
    00083 }
    00084
    00086 QwtWheel::~QwtWheel()
    00087 {
    00088 #if QT_VERSION < 0x040000
    00089     if ( d_data->allocContext )
    00090         QColor::destroyAllocContext( d_data->allocContext );
    00091 #endif
    00092     delete d_data;
    00093 }
    00094
    00096 void QwtWheel::setColorArray()
    00097 {
    00098     if ( !d_data->colors )
    00099         return;
    00100
    00101 #if QT_VERSION < 0x040000
    00102     const QColor light = colorGroup().light();
    00103     const QColor dark = colorGroup().dark();
    00104 #else
    00105     const QColor light = palette().color(QPalette::Light);
    00106     const QColor dark = palette().color(QPalette::Dark);
    00107 #endif
    00108
    00109     if ( !d_data->colors[0].isValid() ||
    00110         d_data->colors[0] != light ||
    00111         d_data->colors[NUM_COLORS - 1] != dark )
    00112     {
    00113 #if QT_VERSION < 0x040000
    00114         if ( d_data->allocContext )
    00115             QColor::destroyAllocContext( d_data->allocContext );
    00116
    00117         d_data->allocContext = QColor::enterAllocContext();
    00118 #endif
    00119
    00120         d_data->colors[0] = light;
    00121         d_data->colors[NUM_COLORS - 1] = dark;
    00122
    00123         int dh, ds, dv, lh, ls, lv;
    00124 #if QT_VERSION < 0x040000
    00125         d_data->colors[0].rgb(&lh, &ls, &lv);
    00126         d_data->colors[NUM_COLORS - 1].rgb(&dh, &ds, &dv);
    00127 #else
    00128         d_data->colors[0].getRgb(&lh, &ls, &lv);
    00129         d_data->colors[NUM_COLORS - 1].getRgb(&dh, &ds, &dv);
    00130 #endif
    00131
    00132         for ( int i = 1; i < NUM_COLORS - 1; ++i )
    00133         {
    00134             const double factor = double(i) / double(NUM_COLORS);
    00135
    00136             d_data->colors[i].setRgb( lh + int( double(dh - lh) * factor ),
    00137                       ls + int( double(ds - ls) * factor ),
    00138                       lv + int( double(dv - lv) * factor ));
    00139         }
    00140 #if QT_VERSION < 0x040000
    00141         QColor::leaveAllocContext();
    00142 #endif
    00143     }
    00144 }
    00145
    00154 void QwtWheel::setTickCnt(int cnt)
    00155 {
    00156     d_data->tickCnt = qwtLim( cnt, 6, 50 );
    00157     update();
    00158 }
    00159
    00160 int QwtWheel::tickCnt() const
    00161 {
    00162     return d_data->tickCnt;
    00163 }
    00164
    00168 double QwtWheel::mass() const
    00169 {
    00170     return QwtAbstractSlider::mass();
    00171 }
    00172
    00183 void QwtWheel::setInternalBorder( int w )
    00184 {
    00185     const int d = qwtMin( width(), height() ) / 3;
    00186     w = qwtMin( w, d );
    00187     d_data->intBorder = qwtMax( w, 1 );
    00188     layoutWheel();
    00189 }
    00190
    00191 int QwtWheel::internalBorder() const
    00192 {
    00193     return d_data->intBorder;
    00194 }
    00195
    00197 void QwtWheel::drawWheelBackground( QPainter *p, const QRect &r )
    00198 {
    00199     p->save();
    00200
    00201     //
    00202     // initialize pens
    00203     //
    00204 #if QT_VERSION < 0x040000
    00205     const QColor light = colorGroup().light();
    00206     const QColor dark = colorGroup().dark();
    00207 #else
    00208     const QColor light = palette().color(QPalette::Light);
    00209     const QColor dark = palette().color(QPalette::Dark);
    00210 #endif
    00211
    00212     QPen lightPen;
    00213     lightPen.setColor(light);
    00214     lightPen.setWidth(d_data->intBorder);
    00215
    00216     QPen darkPen;
    00217     darkPen.setColor(dark);
    00218     darkPen.setWidth(d_data->intBorder);
    00219
    00220     setColorArray();
    00221
    00222     //
    00223     // initialize auxiliary variables
    00224     //
    00225
    00226     const int nFields = NUM_COLORS * 13 / 10;
    00227     const int hiPos = nFields - NUM_COLORS + 1;
    00228
    00229     if ( orientation() == Qt::Horizontal )
    00230     {
    00231         const int rx = r.x();
    00232         int ry = r.y() + d_data->intBorder;
    00233         const int rh = r.height() - 2* d_data->intBorder;
    00234         const int rw = r.width();
    00235         //
    00236         //  draw shaded background
    00237         //
    00238         int x1 = rx;
    00239         for (int i = 1; i < nFields; i++ )
    00240         {
    00241             const int x2 = rx + (rw * i) / nFields;
    00242             p->fillRect(x1, ry, x2-x1 + 1 ,rh, d_data->colors[qwtAbs(i-hiPos)]);
    00243             x1 = x2 + 1;
    00244         }
    00245         p->fillRect(x1, ry, rw - (x1 - rx), rh, d_data->colors[NUM_COLORS - 1]);
    00246
    00247         //
    00248         // draw internal border
    00249         //
    00250         p->setPen(lightPen);
    00251         ry = r.y() + d_data->intBorder / 2;
    00252         p->drawLine(r.x(), ry, r.x() + r.width() , ry);
    00253
    00254         p->setPen(darkPen);
    00255         ry = r.y() + r.height() - (d_data->intBorder - d_data->intBorder / 2);
    00256         p->drawLine(r.x(), ry , r.x() + r.width(), ry);
    00257     }
    00258     else // Qt::Vertical
    00259     {
    00260         int rx = r.x() + d_data->intBorder;
    00261         const int ry = r.y();
    00262         const int rh = r.height();
    00263         const int rw = r.width() - 2 * d_data->intBorder;
    00264
    00265         //
    00266         // draw shaded background
    00267         //
    00268         int y1 = ry;
    00269         for ( int i = 1; i < nFields; i++ )
    00270         {
    00271             const int y2 = ry + (rh * i) / nFields;
    00272             p->fillRect(rx, y1, rw, y2-y1 + 1, d_data->colors[qwtAbs(i-hiPos)]);
    00273             y1 = y2 + 1;
    00274         }
    00275         p->fillRect(rx, y1, rw, rh - (y1 - ry), d_data->colors[NUM_COLORS - 1]);
    00276
    00277         //
    00278         //  draw internal borders
    00279         //
    00280         p->setPen(lightPen);
    00281         rx = r.x() + d_data->intBorder / 2;
    00282         p->drawLine(rx, r.y(), rx, r.y() + r.height());
    00283
    00284         p->setPen(darkPen);
    00285         rx = r.x() + r.width() - (d_data->intBorder - d_data->intBorder / 2);
    00286         p->drawLine(rx, r.y(), rx , r.y() + r.height());
    00287     }
    00288
    00289     p->restore();
    00290 }
    00291
    00292
    00304 void QwtWheel::setTotalAngle(double angle)
    00305 {
    00306     if ( angle < 0.0 )
    00307         angle = 0.0;
    00308
    00309     d_data->totalAngle = angle;
    00310     update();
    00311 }
    00312
    00313 double QwtWheel::totalAngle() const
    00314 {
    00315     return d_data->totalAngle;
    00316 }
    00317
    00325 void QwtWheel::setOrientation(Qt::Orientation o)
    00326 {
    00327     if ( orientation() == o )
    00328         return;
    00329
    00330 #if QT_VERSION >= 0x040000
    00331     if ( !testAttribute(Qt::WA_WState_OwnSizePolicy) )
    00332 #else
    00333     if ( !testWState( WState_OwnSizePolicy ) )
    00334 #endif
    00335     {
    00336         QSizePolicy sp = sizePolicy();
    00337         sp.transpose();
    00338         setSizePolicy(sp);
    00339
    00340 #if QT_VERSION >= 0x040000
    00341         setAttribute(Qt::WA_WState_OwnSizePolicy, false);
    00342 #else
    00343         clearWState( WState_OwnSizePolicy );
    00344 #endif
    00345     }
    00346
    00347     QwtAbstractSlider::setOrientation(o);
    00348     layoutWheel();
    00349 }
    00350
    00359 void QwtWheel::setViewAngle(double angle)
    00360 {
    00361     d_data->viewAngle = qwtLim( angle, 10.0, 175.0 );
    00362     update();
    00363 }
    00364
    00365 double QwtWheel::viewAngle() const
    00366 {
    00367     return d_data->viewAngle;
    00368 }
    00369
    00375 void QwtWheel::drawWheel( QPainter *p, const QRect &r )
    00376 {
    00377     //
    00378     // draw background gradient
    00379     //
    00380     drawWheelBackground( p, r );
    00381
    00382     if ( maxValue() == minValue() || d_data->totalAngle == 0.0 )
    00383         return;
    00384
    00385 #if QT_VERSION < 0x040000
    00386     const QColor light = colorGroup().light();
    00387     const QColor dark = colorGroup().dark();
    00388 #else
    00389     const QColor light = palette().color(QPalette::Light);
    00390     const QColor dark = palette().color(QPalette::Dark);
    00391 #endif
    00392
    00393     const double sign = (minValue() < maxValue()) ? 1.0 : -1.0;
    00394     double cnvFactor = qwtAbs(d_data->totalAngle / (maxValue() - minValue()));
    00395     const double halfIntv = 0.5 * d_data->viewAngle / cnvFactor;
    00396     const double loValue = value() - halfIntv;
    00397     const double hiValue = value() + halfIntv;
    00398     const double tickWidth = 360.0 / double(d_data->tickCnt) / cnvFactor;
    00399     const double sinArc = sin(d_data->viewAngle * M_PI / 360.0);
    00400     cnvFactor *= M_PI / 180.0;
    00401
    00402
    00403     //
    00404     // draw grooves
    00405     //
    00406     if ( orientation() == Qt::Horizontal )
    00407     {
    00408         const double halfSize = double(r.width()) * 0.5;
    00409
    00410         int l1 = r.y() + d_data->intBorder;
    00411         int l2 = r.y() + r.height() - d_data->intBorder - 1;
    00412
    00413         // draw one point over the border if border > 1
    00414         if ( d_data->intBorder > 1 )
    00415         {
    00416             l1 --;
    00417             l2 ++;
    00418         }
    00419
    00420         const int maxpos = r.x() + r.width() - 2;
    00421         const int minpos = r.x() + 2;
    00422
    00423         //
    00424         // draw tick marks
    00425         //
    00426         for ( double tickValue = ceil(loValue / tickWidth) * tickWidth;
    00427             tickValue < hiValue; tickValue += tickWidth )
    00428         {
    00429             //
    00430             //  calculate position
    00431             //
    00432             const int tickPos = r.x() + r.width()
    00433                 - int( halfSize
    00434                     * (sinArc + sign *  sin((tickValue - value()) * cnvFactor))
    00435                     / sinArc);
    00436             //
    00437             // draw vertical line
    00438             //
    00439             if ( (tickPos <= maxpos) && (tickPos > minpos) )
    00440             {
    00441                 p->setPen(dark);
    00442                 p->drawLine(tickPos -1 , l1, tickPos - 1,  l2 );
    00443                 p->setPen(light);
    00444                 p->drawLine(tickPos, l1, tickPos, l2);
    00445             }
    00446         }
    00447     }
    00448     else if ( orientation() == Qt::Vertical )
    00449     {
    00450         const double halfSize = double(r.height()) * 0.5;
    00451
    00452         int l1 = r.x() + d_data->intBorder;
    00453         int l2 = r.x() + r.width() - d_data->intBorder - 1;
    00454
    00455         if ( d_data->intBorder > 1 )
    00456         {
    00457             l1--;
    00458             l2++;
    00459         }
    00460
    00461         const int maxpos = r.y() + r.height() - 2;
    00462         const int minpos = r.y() + 2;
    00463
    00464         //
    00465         // draw tick marks
    00466         //
    00467         for ( double tickValue = ceil(loValue / tickWidth) * tickWidth;
    00468             tickValue < hiValue; tickValue += tickWidth )
    00469         {
    00470
    00471             //
    00472             // calculate position
    00473             //
    00474             const int tickPos = r.y() + int( halfSize *
    00475                 (sinArc + sign * sin((tickValue - value()) * cnvFactor))
    00476                 / sinArc);
    00477
    00478             //
    00479             //  draw horizontal line
    00480             //
    00481             if ( (tickPos <= maxpos) && (tickPos > minpos) )
    00482             {
    00483                 p->setPen(dark);
    00484                 p->drawLine(l1, tickPos - 1 ,l2, tickPos - 1);
    00485                 p->setPen(light);
    00486                 p->drawLine(l1, tickPos, l2, tickPos);
    00487             }
    00488         }
    00489     }
    00490 }
    00491
    00492
    00494 double QwtWheel::getValue( const QPoint &p )
    00495 {
    00496     // The reference position is arbitrary, but the
    00497     // sign of the offset is important
    00498     int w, dx;
    00499     if ( orientation() == Qt::Vertical )
    00500     {
    00501         w = d_data->sliderRect.height();
    00502         dx = d_data->sliderRect.y() - p.y();
    00503     }
    00504     else
    00505     {
    00506         w = d_data->sliderRect.width();
    00507         dx = p.x() - d_data->sliderRect.x();
    00508     }
    00509
    00510     // w pixels is an arc of viewAngle degrees,
    00511     // so we convert change in pixels to change in angle
    00512     const double ang = dx * d_data->viewAngle / w;
    00513
    00514     // value range maps to totalAngle degrees,
    00515     // so convert the change in angle to a change in value
    00516     const double val = ang * ( maxValue() - minValue() ) / d_data->totalAngle;
    00517
    00518     // Note, range clamping and rasterizing to step is automatically
    00519     // handled by QwtAbstractSlider, so we simply return the change in value
    00520     return val;
    00521 }
    00522
    00524 void QwtWheel::resizeEvent(QResizeEvent *)
    00525 {
    00526     layoutWheel( false );
    00527 }
    00528
    00530 //  the current rect and fonts.
    00531 //  \param update_geometry  notify the layout system and call update
    00532 //         to redraw the scale
    00533 void QwtWheel::layoutWheel( bool update_geometry )
    00534 {
    00535     const QRect r = this->rect();
    00536     d_data->sliderRect.setRect(r.x() + d_data->borderWidth, r.y() + d_data->borderWidth,
    00537         r.width() - 2*d_data->borderWidth, r.height() - 2*d_data->borderWidth);
    00538
    00539     if ( update_geometry )
    00540     {
    00541         updateGeometry();
    00542         update();
    00543     }
    00544 }
    00545
    00547 void QwtWheel::paintEvent(QPaintEvent *e)
    00548 {
    00549     // Use double-buffering
    00550     const QRect &ur = e->rect();
    00551     if ( ur.isValid() )
    00552     {
    00553 #if QT_VERSION < 0x040000
    00554         QwtPaintBuffer paintBuffer(this, ur);
    00555         draw(paintBuffer.painter(), ur);
    00556 #else
    00557         QPainter painter(this);
    00558         draw(&painter, ur);
    00559 #endif
    00560     }
    00561 }
    00562
    00564 void QwtWheel::draw(QPainter *painter, const QRect&)
    00565 {
    00566     qDrawShadePanel( painter, rect().x(), rect().y(),
    00567         rect().width(), rect().height(),
    00568 #if QT_VERSION < 0x040000
    00569         colorGroup(),
    00570 #else
    00571         palette(),
    00572 #endif
    00573         true, d_data->borderWidth );
    00574
    00575     drawWheel( painter, d_data->sliderRect );
    00576
    00577     if ( hasFocus() )
    00578         QwtPainter::drawFocusRect(painter, this);
    00579 }
    00580
    00582 void QwtWheel::valueChange()
    00583 {
    00584     QwtAbstractSlider::valueChange();
    00585     update();
    00586 }
    00587
    00588
    00596 void QwtWheel::getScrollMode( const QPoint &p, int &scrollMode, int &direction)
    00597 {
    00598     if ( d_data->sliderRect.contains(p) )
    00599         scrollMode = ScrMouse;
    00600     else
    00601         scrollMode = ScrNone;
    00602
    00603     direction = 0;
    00604 }
    00605
    00612 void QwtWheel::setMass(double val)
    00613 {
    00614     QwtAbstractSlider::setMass(val);
    00615 }
    00616
    00624 void QwtWheel::setWheelWidth(int w)
    00625 {
    00626     d_data->wheelWidth = w;
    00627     layoutWheel();
    00628 }
    00629
    00633 QSize QwtWheel::sizeHint() const
    00634 {
    00635     return minimumSizeHint();
    00636 }
    00637
    00642 QSize QwtWheel::minimumSizeHint() const
    00643 {
    00644     QSize sz( 3*d_data->wheelWidth + 2*d_data->borderWidth,
    00645     d_data->wheelWidth + 2*d_data->borderWidth );
    00646     if ( orientation() != Qt::Horizontal )
    00647         sz.transpose();
    00648     return sz;
    00649 }
    00650
    00654 void QwtWheel::paletteChange( const QPalette& )
    00655 {
    00656     update();
    00657 }
    00658