qwt_knob.cpp 20 KB
Newer Older
pixhawk's avatar
pixhawk committed
1 2 3 4 5 6 7 8 9
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2002   Uwe Rathmann
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

Bryant's avatar
Bryant committed
10 11 12 13 14
#include "qwt_knob.h"
#include "qwt_round_scale_draw.h"
#include "qwt_math.h"
#include "qwt_painter.h"
#include "qwt_scale_map.h"
pixhawk's avatar
pixhawk committed
15 16 17
#include <qpainter.h>
#include <qpalette.h>
#include <qstyle.h>
Bryant's avatar
Bryant committed
18
#include <qstyleoption.h>
pixhawk's avatar
pixhawk committed
19
#include <qevent.h>
Bryant's avatar
Bryant committed
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
#include <qmath.h>
#include <qapplication.h>

#if QT_VERSION < 0x040601
#define qAtan2(y, x) ::atan2(y, x)
#define qFabs(x) ::fabs(x)
#define qFastCos(x) qCos(x)
#define qFastSin(x) qSin(x)
#endif

static QSize qwtKnobSizeHint( const QwtKnob *knob, int min )
{
    int knobWidth = knob->knobWidth();
    if ( knobWidth <= 0 )
        knobWidth = qMax( 3 * knob->markerSize(), min );

    // Add the scale radial thickness to the knobWidth
    const int extent = qCeil( knob->scaleDraw()->extent( knob->font() ) );
    const int d = 2 * ( extent + 4 ) + knobWidth;

    int left, right, top, bottom;
    knob->getContentsMargins( &left, &top, &right, &bottom );

    return QSize( d + left + right, d + top + bottom );
}

static inline double qwtToScaleAngle( double angle )
{
    // the map is counter clockwise with the origin
    // at 90° using angles from -180° -> 180°

    double a = 90.0 - angle;
    if ( a <= -180.0 )
        a += 360.0;
    else if ( a >= 180.0 )
        a -= 360.0;

    return a;
}

static double qwtToDegrees( double value )
{
    return qwtNormalizeDegrees( 90.0 - value );
}
pixhawk's avatar
pixhawk committed
64 65 66 67

class QwtKnob::PrivateData
{
public:
Bryant's avatar
Bryant committed
68 69 70 71 72 73 74 75 76 77 78 79 80
    PrivateData():
        knobStyle( QwtKnob::Raised ),
        markerStyle( QwtKnob::Notch ),
        borderWidth( 2 ),
        borderDist( 4 ),
        scaleDist( 4 ),
        maxScaleTicks( 11 ),
        knobWidth( 0 ),
        alignment( Qt::AlignCenter ),
        markerSize( 8 ),
        totalAngle( 270.0 ),
        mouseOffset( 0.0 )
    {
pixhawk's avatar
pixhawk committed
81 82
    }

Bryant's avatar
Bryant committed
83 84 85
    QwtKnob::KnobStyle knobStyle;
    QwtKnob::MarkerStyle markerStyle;

pixhawk's avatar
pixhawk committed
86 87 88 89 90
    int borderWidth;
    int borderDist;
    int scaleDist;
    int maxScaleTicks;
    int knobWidth;
Bryant's avatar
Bryant committed
91 92
    Qt::Alignment alignment;
    int markerSize;
pixhawk's avatar
pixhawk committed
93 94 95

    double totalAngle;

Bryant's avatar
Bryant committed
96
    double mouseOffset;
pixhawk's avatar
pixhawk committed
97 98 99
};

/*!
Bryant's avatar
Bryant committed
100 101 102 103 104
  \brief Constructor

  Construct a knob with an angle of 270°. The style is
  QwtKnob::Raised and the marker style is QwtKnob::Notch.
  The width of the knob is set to 50 pixels.
pixhawk's avatar
pixhawk committed
105 106 107

  \param parent Parent widget

Bryant's avatar
Bryant committed
108 109 110 111
  \sa setTotalAngle()
*/
QwtKnob::QwtKnob( QWidget* parent ):
    QwtAbstractSlider( parent )
pixhawk's avatar
pixhawk committed
112 113 114
{
    d_data = new PrivateData;

Bryant's avatar
Bryant committed
115
    setScaleDraw( new QwtRoundScaleDraw() );
pixhawk's avatar
pixhawk committed
116 117 118

    setTotalAngle( 270.0 );

Bryant's avatar
Bryant committed
119 120 121 122 123
    setScale( 0.0, 10.0 );
    setValue( 0.0 );

    setSizePolicy( QSizePolicy::MinimumExpanding, 
        QSizePolicy::MinimumExpanding );
pixhawk's avatar
pixhawk committed
124 125 126 127 128 129 130 131 132
}

//! Destructor
QwtKnob::~QwtKnob()
{
    delete d_data;
}

/*!
Bryant's avatar
Bryant committed
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
  \brief Set the knob type 

  \param knobStyle Knob type
  \sa knobStyle(), setBorderWidth()
*/
void QwtKnob::setKnobStyle( KnobStyle knobStyle )
{
    if ( d_data->knobStyle != knobStyle )
    {
        d_data->knobStyle = knobStyle;
        update();
    }
}

/*!
    \return Marker type of the knob
    \sa setKnobStyle(), setBorderWidth()
*/
QwtKnob::KnobStyle QwtKnob::knobStyle() const
{
    return d_data->knobStyle;
}

/*!
  \brief Set the marker type of the knob

  \param markerStyle Marker type
  \sa markerStyle(), setMarkerSize()
pixhawk's avatar
pixhawk committed
161
*/
Bryant's avatar
Bryant committed
162
void QwtKnob::setMarkerStyle( MarkerStyle markerStyle )
pixhawk's avatar
pixhawk committed
163
{
Bryant's avatar
Bryant committed
164 165 166
    if ( d_data->markerStyle != markerStyle )
    {
        d_data->markerStyle = markerStyle;
pixhawk's avatar
pixhawk committed
167 168 169 170
        update();
    }
}

171
/*!
Bryant's avatar
Bryant committed
172 173
  \return Marker type of the knob
  \sa setMarkerStyle(), setMarkerSize()
pixhawk's avatar
pixhawk committed
174
*/
Bryant's avatar
Bryant committed
175
QwtKnob::MarkerStyle QwtKnob::markerStyle() const
pixhawk's avatar
pixhawk committed
176
{
Bryant's avatar
Bryant committed
177
    return d_data->markerStyle;
pixhawk's avatar
pixhawk committed
178 179 180 181 182 183
}

/*!
  \brief Set the total angle by which the knob can be turned
  \param angle Angle in degrees.

Bryant's avatar
Bryant committed
184 185 186 187 188 189 190
  The angle has to be between [10, 360] degrees. Angles above
  360 ( so that the knob can be turned several times around its axis )
  have to be set using setNumTurns().

  The default angle is 270 degrees. 

  \sa totalAngle(), setNumTurns()
pixhawk's avatar
pixhawk committed
191
*/
Bryant's avatar
Bryant committed
192
void QwtKnob::setTotalAngle ( double angle )
pixhawk's avatar
pixhawk committed
193
{
Bryant's avatar
Bryant committed
194 195 196 197
    angle = qBound( 10.0, angle, 360.0 );

    if ( angle != d_data->totalAngle )
    {
198
        d_data->totalAngle = angle;
pixhawk's avatar
pixhawk committed
199

Bryant's avatar
Bryant committed
200 201 202 203 204 205
        scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle,
            0.5 * d_data->totalAngle );

        updateGeometry();
        update();
    }
pixhawk's avatar
pixhawk committed
206 207
}

Bryant's avatar
Bryant committed
208 209 210 211
/*! 
  \return the total angle
  \sa setTotalAngle(), setNumTurns(), numTurns()
 */
212
double QwtKnob::totalAngle() const
pixhawk's avatar
pixhawk committed
213 214 215 216
{
    return d_data->totalAngle;
}

Bryant's avatar
Bryant committed
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
/*!
  \brief Set the number of turns

  When numTurns > 1 the knob can be turned several times around its axis
  - otherwise the total angle is floored to 360°.

  \sa numTurns(), totalAngle(), setTotalAngle()
*/
  
void QwtKnob::setNumTurns( int numTurns )
{
    numTurns = qMax( numTurns, 1 );

    if ( numTurns == 1 && d_data->totalAngle <= 360.0 )
        return;

    const double angle = numTurns * 360.0;
    if ( angle != d_data->totalAngle )
    {
        d_data->totalAngle = angle;

        scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle,
            0.5 * d_data->totalAngle );

        updateGeometry();
        update();
    }
}

/*!
  \return Number of turns. 

  When the total angle is below 360° numTurns() is ceiled to 1.
  \sa setNumTurns(), setTotalAngle(), totalAngle()
 */
int QwtKnob::numTurns() const
{
    return qCeil( d_data->totalAngle / 360.0 );
}

pixhawk's avatar
pixhawk committed
257 258 259 260 261
/*!
   Change the scale draw of the knob

   For changing the labels of the scales, it
   is necessary to derive from QwtRoundScaleDraw and
262
   overload QwtRoundScaleDraw::label().
pixhawk's avatar
pixhawk committed
263 264 265

   \sa scaleDraw()
*/
Bryant's avatar
Bryant committed
266
void QwtKnob::setScaleDraw( QwtRoundScaleDraw *scaleDraw )
pixhawk's avatar
pixhawk committed
267
{
Bryant's avatar
Bryant committed
268 269
    setAbstractScaleDraw( scaleDraw );
    setTotalAngle( d_data->totalAngle );
pixhawk's avatar
pixhawk committed
270 271
}

272
/*!
pixhawk's avatar
pixhawk committed
273 274 275 276 277
   \return the scale draw of the knob
   \sa setScaleDraw()
*/
const QwtRoundScaleDraw *QwtKnob::scaleDraw() const
{
Bryant's avatar
Bryant committed
278
    return static_cast<const QwtRoundScaleDraw *>( abstractScaleDraw() );
pixhawk's avatar
pixhawk committed
279 280
}

281
/*!
pixhawk's avatar
pixhawk committed
282 283 284 285 286
   \return the scale draw of the knob
   \sa setScaleDraw()
*/
QwtRoundScaleDraw *QwtKnob::scaleDraw()
{
Bryant's avatar
Bryant committed
287
    return static_cast<QwtRoundScaleDraw *>( abstractScaleDraw() );
pixhawk's avatar
pixhawk committed
288 289 290
}

/*!
Bryant's avatar
Bryant committed
291
  Calculate the bounding rectangle of the knob without the scale
pixhawk's avatar
pixhawk committed
292

Bryant's avatar
Bryant committed
293 294 295 296 297 298
  \return Bounding rectangle of the knob
  \sa knobWidth(), alignment(), QWidget::contentsRect()
 */
QRect QwtKnob::knobRect() const
{
    const QRect cr = contentsRect();
pixhawk's avatar
pixhawk committed
299

Bryant's avatar
Bryant committed
300 301
    const int extent = qCeil( scaleDraw()->extent( font() ) );
    const int d = extent + d_data->scaleDist;
pixhawk's avatar
pixhawk committed
302

Bryant's avatar
Bryant committed
303 304 305 306
    int w = d_data->knobWidth;
    if ( w <= 0 )
    {
        const int dim = qMin( cr.width(), cr.height() );
pixhawk's avatar
pixhawk committed
307

Bryant's avatar
Bryant committed
308 309 310
        w = dim - 2 * ( d );
        w = qMax( 0, w );
    }
pixhawk's avatar
pixhawk committed
311

Bryant's avatar
Bryant committed
312
    QRect r( 0, 0, w, w );
pixhawk's avatar
pixhawk committed
313

Bryant's avatar
Bryant committed
314 315 316 317 318 319 320 321 322 323 324 325
    if ( d_data->alignment & Qt::AlignLeft )
    {
        r.moveLeft( cr.left() + d );
    }
    else if ( d_data->alignment & Qt::AlignRight )
    {
        r.moveRight( cr.right() - d );
    }
    else
    {
        r.moveCenter( QPoint( cr.center().x(), r.center().y() ) );
    }
pixhawk's avatar
pixhawk committed
326

Bryant's avatar
Bryant committed
327 328 329 330 331 332 333 334 335 336 337 338
    if ( d_data->alignment & Qt::AlignTop )
    {
        r.moveTop( cr.top() + d );
    }
    else if ( d_data->alignment & Qt::AlignBottom )
    {
        r.moveBottom( cr.bottom() - d );
    }
    else 
    {
        r.moveCenter( QPoint( r.center().x(), cr.center().y() ) );
    }
pixhawk's avatar
pixhawk committed
339

Bryant's avatar
Bryant committed
340
    return r;
pixhawk's avatar
pixhawk committed
341 342 343
}

/*!
Bryant's avatar
Bryant committed
344 345 346
  \brief Determine what to do when the user presses a mouse button.

  \param pos Mouse position
pixhawk's avatar
pixhawk committed
347

Bryant's avatar
Bryant committed
348 349
  \retval True, when pos is inside the circle of the knob.
  \sa scrolledTo()
pixhawk's avatar
pixhawk committed
350
*/
Bryant's avatar
Bryant committed
351
bool QwtKnob::isScrollPosition( const QPoint &pos ) const
pixhawk's avatar
pixhawk committed
352
{
Bryant's avatar
Bryant committed
353 354 355 356 357 358 359 360 361 362 363 364 365 366
    const QRect kr = knobRect();

    const QRegion region( kr, QRegion::Ellipse );
    if ( region.contains( pos ) && ( pos != kr.center() ) )
    {
        const double angle = QLineF( kr.center(), pos ).angle();
        const double valueAngle = qwtToDegrees( transform( value() ) );

        d_data->mouseOffset = qwtNormalizeDegrees( angle - valueAngle );

        return true;
    }

    return false;
pixhawk's avatar
pixhawk committed
367 368 369
}

/*!
Bryant's avatar
Bryant committed
370
  \brief Determine the value for a new position of the mouse
pixhawk's avatar
pixhawk committed
371

Bryant's avatar
Bryant committed
372 373 374 375
  \param pos Mouse position

  \return Value for the mouse position
  \sa isScrollPosition()
pixhawk's avatar
pixhawk committed
376
*/
Bryant's avatar
Bryant committed
377
double QwtKnob::scrolledTo( const QPoint &pos ) const
pixhawk's avatar
pixhawk committed
378
{
Bryant's avatar
Bryant committed
379 380
    double angle = QLineF( rect().center(), pos ).angle();
    angle = qwtNormalizeDegrees( angle - d_data->mouseOffset );
pixhawk's avatar
pixhawk committed
381

Bryant's avatar
Bryant committed
382 383 384
    if ( scaleMap().pDist() > 360.0 )
    {
        angle = qwtToDegrees( angle );
pixhawk's avatar
pixhawk committed
385

Bryant's avatar
Bryant committed
386
        const double v = transform( value() );
pixhawk's avatar
pixhawk committed
387

Bryant's avatar
Bryant committed
388
        int numTurns = qFloor( ( v - scaleMap().p1() ) / 360.0 );
pixhawk's avatar
pixhawk committed
389

Bryant's avatar
Bryant committed
390 391 392 393 394
        double valueAngle = qwtNormalizeDegrees( v );
        if ( qAbs( valueAngle - angle ) > 180.0 )
        {
            numTurns += ( angle > valueAngle ) ? -1 : 1;
        }
pixhawk's avatar
pixhawk committed
395

Bryant's avatar
Bryant committed
396
        angle += scaleMap().p1() + numTurns * 360.0;
pixhawk's avatar
pixhawk committed
397

Bryant's avatar
Bryant committed
398 399 400 401
        if ( !wrapping() )
        {
            const double boundedAngle = 
                qBound( scaleMap().p1(), angle, scaleMap().p2() );
pixhawk's avatar
pixhawk committed
402

Bryant's avatar
Bryant committed
403 404 405
            d_data->mouseOffset += ( boundedAngle - angle );
            angle = boundedAngle;
        }
pixhawk's avatar
pixhawk committed
406
    }
Bryant's avatar
Bryant committed
407 408 409
    else
    {
        angle = qwtToScaleAngle( angle );
pixhawk's avatar
pixhawk committed
410

Bryant's avatar
Bryant committed
411 412
        const double boundedAngle = 
            qBound( scaleMap().p1(), angle, scaleMap().p2() );
pixhawk's avatar
pixhawk committed
413

Bryant's avatar
Bryant committed
414 415
        if ( !wrapping() )
            d_data->mouseOffset += ( boundedAngle - angle );
pixhawk's avatar
pixhawk committed
416

Bryant's avatar
Bryant committed
417 418
        angle = boundedAngle;
    }
pixhawk's avatar
pixhawk committed
419

Bryant's avatar
Bryant committed
420
    return invTransform( angle );
pixhawk's avatar
pixhawk committed
421 422
}

Bryant's avatar
Bryant committed
423 424 425
/*! 
  Handle QEvent::StyleChange and QEvent::FontChange;
  \param event Change event
pixhawk's avatar
pixhawk committed
426
*/
Bryant's avatar
Bryant committed
427
void QwtKnob::changeEvent( QEvent *event )
pixhawk's avatar
pixhawk committed
428
{
Bryant's avatar
Bryant committed
429 430 431 432 433 434 435 436 437 438 439 440
    switch( event->type() )
    {
        case QEvent::StyleChange:
        case QEvent::FontChange:
        {
            updateGeometry();
            update();
            break;
        }
        default:
            break;
    }
pixhawk's avatar
pixhawk committed
441 442
}

Bryant's avatar
Bryant committed
443 444 445 446 447
/*!
  Repaint the knob
  \param event Paint event
*/
void QwtKnob::paintEvent( QPaintEvent *event )
pixhawk's avatar
pixhawk committed
448
{
Bryant's avatar
Bryant committed
449
    const QRectF knobRect = this->knobRect();
pixhawk's avatar
pixhawk committed
450

Bryant's avatar
Bryant committed
451 452
    QPainter painter( this );
    painter.setClipRegion( event->region() );
pixhawk's avatar
pixhawk committed
453

Bryant's avatar
Bryant committed
454 455 456
    QStyleOption opt;
    opt.init(this);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
pixhawk's avatar
pixhawk committed
457

Bryant's avatar
Bryant committed
458
    painter.setRenderHint( QPainter::Antialiasing, true );
pixhawk's avatar
pixhawk committed
459

Bryant's avatar
Bryant committed
460 461 462 463 464 465
    if ( !knobRect.contains( event->region().boundingRect() ) )
    {
        scaleDraw()->setRadius( 0.5 * knobRect.width() + d_data->scaleDist );
        scaleDraw()->moveCenter( knobRect.center() );

        scaleDraw()->draw( &painter, palette() );
pixhawk's avatar
pixhawk committed
466
    }
Bryant's avatar
Bryant committed
467 468 469 470 471 472 473 474 475 476

    drawKnob( &painter, knobRect );

    drawMarker( &painter, knobRect, 
        qwtNormalizeDegrees( transform( value() ) ) );

    painter.setRenderHint( QPainter::Antialiasing, false );

    if ( hasFocus() )
        drawFocusIndicator( &painter );
pixhawk's avatar
pixhawk committed
477 478 479
}

/*!
Bryant's avatar
Bryant committed
480 481 482 483
  \brief Draw the knob

  \param painter painter
  \param knobRect Bounding rectangle of the knob (without scale)
pixhawk's avatar
pixhawk committed
484
*/
Bryant's avatar
Bryant committed
485
void QwtKnob::drawKnob( QPainter *painter, const QRectF &knobRect ) const
pixhawk's avatar
pixhawk committed
486
{
Bryant's avatar
Bryant committed
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
    double dim = qMin( knobRect.width(), knobRect.height() );
    dim -= d_data->borderWidth * 0.5;

    QRectF aRect( 0, 0, dim, dim );
    aRect.moveCenter( knobRect.center() );

    QPen pen( Qt::NoPen );
    if ( d_data->borderWidth > 0 )
    {
        QColor c1 = palette().color( QPalette::Light );
        QColor c2 = palette().color( QPalette::Dark );

        QLinearGradient gradient( aRect.topLeft(), aRect.bottomRight() );
        gradient.setColorAt( 0.0, c1 );
        gradient.setColorAt( 0.3, c1 );
        gradient.setColorAt( 0.7, c2 );
        gradient.setColorAt( 1.0, c2 );

        pen = QPen( gradient, d_data->borderWidth ); 
pixhawk's avatar
pixhawk committed
506 507
    }

Bryant's avatar
Bryant committed
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
    QBrush brush;
    switch( d_data->knobStyle )
    {
        case QwtKnob::Raised:
        {
            double off = 0.3 * knobRect.width();
            QRadialGradient gradient( knobRect.center(),
                knobRect.width(), knobRect.topLeft() + QPointF( off, off ) );
            
            gradient.setColorAt( 0.0, palette().color( QPalette::Midlight ) );
            gradient.setColorAt( 1.0, palette().color( QPalette::Button ) );

            brush = QBrush( gradient );

            break;
        }
        case QwtKnob::Styled:
        {
            QRadialGradient gradient(knobRect.center().x() - knobRect.width() / 3,
                knobRect.center().y() - knobRect.height() / 2,
                knobRect.width() * 1.3,
                knobRect.center().x(),
                knobRect.center().y() - knobRect.height() / 2);

            const QColor c = palette().color( QPalette::Button );
            gradient.setColorAt(0, c.lighter(110));
            gradient.setColorAt(qreal(0.5), c);
            gradient.setColorAt(qreal(0.501), c.darker(102));
            gradient.setColorAt(1, c.darker(115));

            brush = QBrush( gradient );

            break;
        }
        case QwtKnob::Sunken:
        {
            QLinearGradient gradient( 
                knobRect.topLeft(), knobRect.bottomRight() );
            gradient.setColorAt( 0.0, palette().color( QPalette::Mid ) );
            gradient.setColorAt( 0.5, palette().color( QPalette::Button ) );
            gradient.setColorAt( 1.0, palette().color( QPalette::Midlight ) );
            brush = QBrush( gradient );

            break;
        }
        case QwtKnob::Flat:
        default:
            brush = palette().brush( QPalette::Button );
    }
pixhawk's avatar
pixhawk committed
557

Bryant's avatar
Bryant committed
558 559 560
    painter->setPen( pen );
    painter->setBrush( brush );
    painter->drawEllipse( aRect );
pixhawk's avatar
pixhawk committed
561 562
}

Bryant's avatar
Bryant committed
563

pixhawk's avatar
pixhawk committed
564 565
/*!
  \brief Draw the marker at the knob's front
Bryant's avatar
Bryant committed
566 567 568 569 570

  \param painter Painter
  \param rect Bounding rectangle of the knob without scale
  \param angle Angle of the marker in degrees 
               ( clockwise, 0 at the 12 o'clock position )
pixhawk's avatar
pixhawk committed
571
*/
Bryant's avatar
Bryant committed
572 573
void QwtKnob::drawMarker( QPainter *painter, 
    const QRectF &rect, double angle ) const
pixhawk's avatar
pixhawk committed
574
{
Bryant's avatar
Bryant committed
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
    if ( d_data->markerStyle == NoMarker || !isValid() )
        return;

    const double radians = qwtRadians( angle );
    const double sinA = -qFastSin( radians );
    const double cosA = qFastCos( radians );

    const double xm = rect.center().x();
    const double ym = rect.center().y();
    const double margin = 4.0;

    double radius = 0.5 * ( rect.width() - d_data->borderWidth ) - margin;
    if ( radius < 1.0 )
        radius = 1.0;

    int markerSize = d_data->markerSize;
    if ( markerSize <= 0 )
        markerSize = qRound( 0.4 * radius );

    switch ( d_data->markerStyle )
    {
        case Notch:
        case Nub:
        {
            const double dotWidth = 
                qMin( double( markerSize ), radius);

            const double dotCenterDist = radius - 0.5 * dotWidth;
            if ( dotCenterDist > 0.0 )
            {
                const QPointF center( xm - sinA * dotCenterDist, 
                    ym - cosA * dotCenterDist );

                QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth );
                ellipse.moveCenter( center );

                QColor c1 = palette().color( QPalette::Light );
                QColor c2 = palette().color( QPalette::Mid );

                if ( d_data->markerStyle == Notch )
                    qSwap( c1, c2 );

                QLinearGradient gradient( 
                    ellipse.topLeft(), ellipse.bottomRight() );
                gradient.setColorAt( 0.0, c1 );
                gradient.setColorAt( 1.0, c2 );

                painter->setPen( Qt::NoPen );
                painter->setBrush( gradient );

                painter->drawEllipse( ellipse );
            }
            break;
        }
        case Dot:
        {
            const double dotWidth = 
                qMin( double( markerSize ), radius);

            const double dotCenterDist = radius - 0.5 * dotWidth;
            if ( dotCenterDist > 0.0 )
            {
                const QPointF center( xm - sinA * dotCenterDist, 
                    ym - cosA * dotCenterDist );

                QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth );
                ellipse.moveCenter( center );

                painter->setPen( Qt::NoPen );
                painter->setBrush( palette().color( QPalette::ButtonText ) );
                painter->drawEllipse( ellipse );
            }

            break;
        }
        case Tick:
        {
            const double rb = qMax( radius - markerSize, 1.0 );
            const double re = radius;

            const QLineF line( xm - sinA * rb, ym - cosA * rb,
                xm - sinA * re, ym - cosA * re );

            QPen pen( palette().color( QPalette::ButtonText ), 0 );
            pen.setCapStyle( Qt::FlatCap );
            painter->setPen( pen );
            painter->drawLine ( line );

            break;
        }
        case Triangle:
        {
            const double rb = qMax( radius - markerSize, 1.0 );
            const double re = radius;

            painter->translate( rect.center() );
            painter->rotate( angle - 90.0 );
            
            QPolygonF polygon;
            polygon += QPointF( re, 0.0 );
            polygon += QPointF( rb, 0.5 * ( re - rb ) );
            polygon += QPointF( rb, -0.5 * ( re - rb ) );

            painter->setPen( Qt::NoPen );
            painter->setBrush( palette().color( QPalette::ButtonText ) );
            painter->drawPolygon( polygon );

            painter->resetTransform();

            break;
        }
        default:
            break;
    }
}
pixhawk's avatar
pixhawk committed
690

Bryant's avatar
Bryant committed
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
/*!
  Draw the focus indicator
  \param painter Painter
*/
void QwtKnob::drawFocusIndicator( QPainter *painter ) const
{       
    const QRect cr = contentsRect();

    int w = d_data->knobWidth;
    if ( w <= 0 )
    {
        w = qMin( cr.width(), cr.height() );
    }
    else
    {
        const int extent = qCeil( scaleDraw()->extent( font() ) );
        w += 2 * ( extent + d_data->scaleDist );
    }
pixhawk's avatar
pixhawk committed
709

Bryant's avatar
Bryant committed
710 711
    QRect focusRect( 0, 0, w, w );
    focusRect.moveCenter( cr.center() );
pixhawk's avatar
pixhawk committed
712

Bryant's avatar
Bryant committed
713 714
    QwtPainter::drawFocusRect( painter, this, focusRect );
}  
715

Bryant's avatar
Bryant committed
716 717
/*!
  \brief Set the alignment of the knob
718

Bryant's avatar
Bryant committed
719 720
  Similar to a QLabel::alignment() the flags decide how
  to align the knob inside of contentsRect(). 
721

Bryant's avatar
Bryant committed
722
  The default setting is Qt::AlignCenter
723

Bryant's avatar
Bryant committed
724 725 726 727 728 729 730 731 732 733
  \param alignment Or'd alignment flags

  \sa alignment(), setKnobWidth(), knobRect()
 */
void QwtKnob::setAlignment( Qt::Alignment alignment )
{
    if ( d_data->alignment != alignment )
    {
        d_data->alignment = alignment;
        update();
pixhawk's avatar
pixhawk committed
734 735 736
    }
}

Bryant's avatar
Bryant committed
737 738 739 740 741 742 743 744 745
/*!
  \return Alignment of the knob inside of contentsRect()
  \sa setAlignment(), knobWidth(), knobRect()
 */
Qt::Alignment QwtKnob::alignment() const
{
    return d_data->alignment;
}

pixhawk's avatar
pixhawk committed
746 747 748
/*!
  \brief Change the knob's width.

Bryant's avatar
Bryant committed
749 750 751 752 753 754 755
  Setting a fixed value for the diameter of the knob 
  is helpful for aligning several knobs in a row.

  \param width New width

  \sa knobWidth(), setAlignment()
  \note Modifies the sizePolicy() 
pixhawk's avatar
pixhawk committed
756
*/
Bryant's avatar
Bryant committed
757
void QwtKnob::setKnobWidth( int width )
pixhawk's avatar
pixhawk committed
758
{
Bryant's avatar
Bryant committed
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
    width = qMax( width, 0 );

    if ( width != d_data->knobWidth )
    {
        QSizePolicy::Policy policy;
        if ( width > 0 )
            policy = QSizePolicy::Minimum;
        else
            policy = QSizePolicy::MinimumExpanding;

        setSizePolicy( policy, policy );

        d_data->knobWidth = width;

        updateGeometry();
        update();
    }
pixhawk's avatar
pixhawk committed
776 777 778
}

//! Return the width of the knob
779
int QwtKnob::knobWidth() const
pixhawk's avatar
pixhawk committed
780 781 782 783 784 785
{
    return d_data->knobWidth;
}

/*!
  \brief Set the knob's border width
Bryant's avatar
Bryant committed
786
  \param borderWidth new border width
pixhawk's avatar
pixhawk committed
787
*/
Bryant's avatar
Bryant committed
788
void QwtKnob::setBorderWidth( int borderWidth )
pixhawk's avatar
pixhawk committed
789
{
Bryant's avatar
Bryant committed
790 791 792 793 794
    d_data->borderWidth = qMax( borderWidth, 0 );

    updateGeometry();
    update();

pixhawk's avatar
pixhawk committed
795 796 797
}

//! Return the border width
798
int QwtKnob::borderWidth() const
pixhawk's avatar
pixhawk committed
799 800 801 802 803
{
    return d_data->borderWidth;
}

/*!
Bryant's avatar
Bryant committed
804
  \brief Set the size of the marker
pixhawk's avatar
pixhawk committed
805

Bryant's avatar
Bryant committed
806 807
  When setting a size <= 0 the marker will
  automatically scaled to 40% of the radius of the knob.
pixhawk's avatar
pixhawk committed
808

Bryant's avatar
Bryant committed
809
  \sa markerSize(), markerStyle()
pixhawk's avatar
pixhawk committed
810
*/
Bryant's avatar
Bryant committed
811
void QwtKnob::setMarkerSize( int size )
pixhawk's avatar
pixhawk committed
812
{
Bryant's avatar
Bryant committed
813 814 815 816 817
    if ( d_data->markerSize != size )
    {
        d_data->markerSize = size;
        update();
    }
pixhawk's avatar
pixhawk committed
818 819
}

Bryant's avatar
Bryant committed
820 821 822 823 824
/*! 
  \return Marker size
  \sa setMarkerSize()
 */
int QwtKnob::markerSize() const
pixhawk's avatar
pixhawk committed
825
{
Bryant's avatar
Bryant committed
826
    return d_data->markerSize;
pixhawk's avatar
pixhawk committed
827 828 829
}

/*!
Bryant's avatar
Bryant committed
830
  \return sizeHint()
pixhawk's avatar
pixhawk committed
831 832 833
*/
QSize QwtKnob::sizeHint() const
{
Bryant's avatar
Bryant committed
834 835
    const QSize hint = qwtKnobSizeHint( this, 50 );
    return hint.expandedTo( QApplication::globalStrut() );
pixhawk's avatar
pixhawk committed
836 837 838
}

/*!
Bryant's avatar
Bryant committed
839 840
  \return Minimum size hint
  \sa sizeHint()
pixhawk's avatar
pixhawk committed
841 842 843
*/
QSize QwtKnob::minimumSizeHint() const
{
Bryant's avatar
Bryant committed
844
    return qwtKnobSizeHint( this, 20 );
pixhawk's avatar
pixhawk committed
845
}