Skip to content
qwt_dial.cpp 19 KiB
Newer Older
pixhawk's avatar
pixhawk committed
/* -*- 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
#include "qwt_dial.h"
#include "qwt_dial_needle.h"
pixhawk's avatar
pixhawk committed
#include "qwt_math.h"
#include "qwt_scale_engine.h"
#include "qwt_scale_map.h"
Bryant's avatar
Bryant committed
#include "qwt_round_scale_draw.h"
pixhawk's avatar
pixhawk committed
#include "qwt_painter.h"
Bryant's avatar
Bryant committed
#include <qpainter.h>
#include <qpalette.h>
#include <qpixmap.h>
#include <qevent.h>
#include <qalgorithms.h>
#include <qmath.h>
#include <qstyle.h>
#include <qstyleoption.h>
#include <qapplication.h>

static inline double qwtAngleDist( double a1, double a2 )
{
    double dist = qAbs( a2 - a1 );
    if ( dist > 360.0 )
        dist -= 360.0;

    return dist;
}

static inline bool qwtIsOnArc( double angle, double min, double max )
{
    if ( min < max )
    {
        return ( angle >= min ) && ( angle <= max );
    }
    else
    {
        return ( angle >= min ) || ( angle <= max );
    }
}

static inline double qwtBoundedAngle( double min, double angle, double max )
{
    double from = qwtNormalizeDegrees( min );
    double to = qwtNormalizeDegrees( max );

    double a;

    if ( qwtIsOnArc( angle, from, to ) )
    {
        a = angle;
        if ( a < min )
            a += 360.0;
    }
    else
    {
        if ( qwtAngleDist( angle, from ) <
            qwtAngleDist( angle, to ) )
        {
            a = min;
        }
        else
        {
            a = max;
        }
    }

    return a;
}
pixhawk's avatar
pixhawk committed

class QwtDial::PrivateData
{
public:
    PrivateData():
Bryant's avatar
Bryant committed
        frameShadow( Sunken ),
        lineWidth( 0 ),
        mode( RotateNeedle ),
        origin( 90.0 ),
        minScaleArc( 0.0 ),
        maxScaleArc( 0.0 ),
        needle( NULL ),
        arcOffset( 0.0 ),
        mouseOffset( 0.0 )
    {
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    ~PrivateData()
    {
pixhawk's avatar
pixhawk committed
        delete needle;
    }
    Shadow frameShadow;
    int lineWidth;

    QwtDial::Mode mode;

    double origin;
    double minScaleArc;
    double maxScaleArc;

Bryant's avatar
Bryant committed
    double scalePenWidth;
pixhawk's avatar
pixhawk committed
    QwtDialNeedle *needle;

Bryant's avatar
Bryant committed
    double arcOffset;
    double mouseOffset;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QPixmap pixmapCache;
};
pixhawk's avatar
pixhawk committed

/*!
  \brief Constructor
  \param parent Parent widget

Bryant's avatar
Bryant committed
  Create a dial widget with no needle. The scale is initialized
  to [ 0.0, 360.0 ] and 360 steps ( QwtAbstractSlider::setTotalSteps() ).
  The origin of the scale is at 90°,
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  The value is set to 0.0.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  The default mode is QwtDial::RotateNeedle.
Bryant's avatar
Bryant committed
QwtDial::QwtDial( QWidget* parent ):
    QwtAbstractSlider( parent )
pixhawk's avatar
pixhawk committed
{
    d_data = new PrivateData;

Bryant's avatar
Bryant committed
    setFocusPolicy( Qt::TabFocus );
pixhawk's avatar
pixhawk committed

    QPalette p = palette();
Bryant's avatar
Bryant committed
    for ( int i = 0; i < QPalette::NColorGroups; i++ )
    {
        const QPalette::ColorGroup colorGroup =
            static_cast<QPalette::ColorGroup>( i );
pixhawk's avatar
pixhawk committed

        // Base: background color of the circle inside the frame.
Bryant's avatar
Bryant committed
        // WindowText: background color of the circle inside the scale

        p.setColor( colorGroup, QPalette::WindowText,
            p.color( colorGroup, QPalette::Base ) );
pixhawk's avatar
pixhawk committed
    }
Bryant's avatar
Bryant committed
    setPalette( p );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QwtRoundScaleDraw* scaleDraw = new QwtRoundScaleDraw();
    scaleDraw->setRadius( 0 );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    setScaleDraw( scaleDraw );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    setScaleArc( 0.0, 360.0 ); // scale as a full circle
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    setScaleMaxMajor( 10 );
    setScaleMaxMinor( 5 );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    setValue( 0.0 );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
//!  Destructor
QwtDial::~QwtDial()
Bryant's avatar
Bryant committed
    delete d_data;
pixhawk's avatar
pixhawk committed
}

/*!
  Sets the frame shadow value from the frame style.
Bryant's avatar
Bryant committed

pixhawk's avatar
pixhawk committed
  \param shadow Frame shadow
  \sa setLineWidth(), QFrame::setFrameShadow()
*/
Bryant's avatar
Bryant committed
void QwtDial::setFrameShadow( Shadow shadow )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( shadow != d_data->frameShadow )
    {
        invalidateCache();

pixhawk's avatar
pixhawk committed
        d_data->frameShadow = shadow;
        if ( lineWidth() > 0 )
            update();
    }
}

/*!
  \return Frame shadow
Bryant's avatar
Bryant committed
  /sa setFrameShadow(), lineWidth(), QFrame::frameShadow()
pixhawk's avatar
pixhawk committed
*/
QwtDial::Shadow QwtDial::frameShadow() const
{
    return d_data->frameShadow;
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  Sets the line width of the frame
pixhawk's avatar
pixhawk committed

  \param lineWidth Line width
  \sa setFrameShadow()
*/
Bryant's avatar
Bryant committed
void QwtDial::setLineWidth( int lineWidth )
pixhawk's avatar
pixhawk committed
{
    if ( lineWidth < 0 )
        lineWidth = 0;

Bryant's avatar
Bryant committed
    if ( d_data->lineWidth != lineWidth )
    {
        invalidateCache();

pixhawk's avatar
pixhawk committed
        d_data->lineWidth = lineWidth;
        update();
    }
}

/*!
  \return Line width of the frame
  \sa setLineWidth(), frameShadow(), lineWidth()
*/
int QwtDial::lineWidth() const
{
    return d_data->lineWidth;
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \return bounding rectangle of the circle inside the frame
  \sa setLineWidth(), scaleInnerRect(), boundingRect()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
QRect QwtDial::innerRect() const
pixhawk's avatar
pixhawk committed
{
    const int lw = lineWidth();
Bryant's avatar
Bryant committed
    return boundingRect().adjusted( lw, lw, -lw, -lw );
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \return bounding rectangle of the dial including the frame
  \sa setLineWidth(), scaleInnerRect(), innerRect()
pixhawk's avatar
pixhawk committed
*/
QRect QwtDial::boundingRect() const
{
Bryant's avatar
Bryant committed
    const QRect cr = contentsRect();

    const double dim = qMin( cr.width(), cr.height() );

    QRect inner( 0, 0, dim, dim );
    inner.moveCenter( cr.center() );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    return inner;
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \return rectangle inside the scale
  \sa setLineWidth(), boundingRect(), innerRect()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
QRect QwtDial::scaleInnerRect() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QRect rect = innerRect();

    const QwtAbstractScaleDraw *sd = scaleDraw();
    if ( sd )
    {
        int scaleDist = qCeil( sd->extent( font() ) );
pixhawk's avatar
pixhawk committed
        scaleDist++; // margin
Bryant's avatar
Bryant committed

        rect.adjust( scaleDist, scaleDist, -scaleDist, -scaleDist );
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    return rect;
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \brief Change the mode of the dial.
  \param mode New mode

Bryant's avatar
Bryant committed
  In case of QwtDial::RotateNeedle the needle is rotating, in case of
pixhawk's avatar
pixhawk committed
  QwtDial::RotateScale, the needle points to origin()
  and the scale is rotating.
pixhawk's avatar
pixhawk committed
  The default mode is QwtDial::RotateNeedle.

  \sa mode(), setValue(), setOrigin()
Bryant's avatar
Bryant committed
void QwtDial::setMode( Mode mode )
Bryant's avatar
Bryant committed
    if ( mode != d_data->mode )
    {
        invalidateCache();

pixhawk's avatar
pixhawk committed
        d_data->mode = mode;
Bryant's avatar
Bryant committed
        sliderChange();
pixhawk's avatar
pixhawk committed
    }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \return Mode of the dial.
pixhawk's avatar
pixhawk committed
  \sa setMode(), origin(), setScaleArc(), value()
*/
QwtDial::Mode QwtDial::mode() const
{
    return d_data->mode;
}

Bryant's avatar
Bryant committed
  Invalidate the internal caches used to speed up repainting
 */
void QwtDial::invalidateCache()
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    d_data->pixmapCache = QPixmap();
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
   Paint the dial
   \param event Paint event
Bryant's avatar
Bryant committed
void QwtDial::paintEvent( QPaintEvent *event )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QPainter painter( this );
    painter.setClipRegion( event->region() );
pixhawk's avatar
pixhawk committed

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

Bryant's avatar
Bryant committed
    if ( d_data->mode == QwtDial::RotateScale )
    {
        painter.save();
        painter.setRenderHint( QPainter::Antialiasing, true );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        drawContents( &painter );
pixhawk's avatar
pixhawk committed

        painter.restore();
Bryant's avatar
Bryant committed
    }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const QRect r = contentsRect();
    if ( r.size() != d_data->pixmapCache.size() )
    {
        d_data->pixmapCache = QwtPainter::backingStore( this, r.size() );
        d_data->pixmapCache.fill( Qt::transparent );

        QPainter p( &d_data->pixmapCache );
        p.setRenderHint( QPainter::Antialiasing, true );
        p.translate( -r.topLeft() );
            
        if ( d_data->mode != QwtDial::RotateScale )
            drawContents( &p );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        if ( lineWidth() > 0 )
            drawFrame( &p );

        if ( d_data->mode != QwtDial::RotateNeedle )
            drawNeedle( &p );
pixhawk's avatar
pixhawk committed
    }
Bryant's avatar
Bryant committed

    painter.drawPixmap( r.topLeft(), d_data->pixmapCache );

    if ( d_data->mode == QwtDial::RotateNeedle )
        drawNeedle( &painter );

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

/*!
Bryant's avatar
Bryant committed
  Draw the focus indicator
pixhawk's avatar
pixhawk committed
  \param painter Painter
*/
Bryant's avatar
Bryant committed
void QwtDial::drawFocusIndicator( QPainter *painter ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QwtPainter::drawFocusRect( painter, this, boundingRect() );
pixhawk's avatar
pixhawk committed
}

/*!
  Draw the frame around the dial

  \param painter Painter
  \sa lineWidth(), frameShadow()
*/
Bryant's avatar
Bryant committed
void QwtDial::drawFrame( QPainter *painter )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QwtPainter::drawRoundFrame( painter, boundingRect(),
        palette(), lineWidth(), d_data->frameShadow );
pixhawk's avatar
pixhawk committed
}

/*!
  \brief Draw the contents inside the frame
Bryant's avatar
Bryant committed
  QPalette::Window is the background color outside of the frame.
  QPalette::Base is the background color inside the frame.
  QPalette::WindowText is the background color inside the scale.
pixhawk's avatar
pixhawk committed

  \param painter Painter
Bryant's avatar
Bryant committed

  \sa boundingRect(), innerRect(),
    scaleInnerRect(), QWidget::setPalette()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtDial::drawContents( QPainter *painter ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( testAttribute( Qt::WA_NoSystemBackground ) ||
        palette().brush( QPalette::Base ) !=
            palette().brush( QPalette::Window ) )
pixhawk's avatar
pixhawk committed
    {
Bryant's avatar
Bryant committed
        const QRectF br = boundingRect();
pixhawk's avatar
pixhawk committed

        painter->save();
Bryant's avatar
Bryant committed
        painter->setPen( Qt::NoPen );
        painter->setBrush( palette().brush( QPalette::Base ) );
        painter->drawEllipse( br );
pixhawk's avatar
pixhawk committed
        painter->restore();
    }

Bryant's avatar
Bryant committed
    const QRectF insideScaleRect = scaleInnerRect();
    if ( palette().brush( QPalette::WindowText ) !=
            palette().brush( QPalette::Base ) )
pixhawk's avatar
pixhawk committed
    {
        painter->save();
Bryant's avatar
Bryant committed
        painter->setPen( Qt::NoPen );
        painter->setBrush( palette().brush( QPalette::WindowText ) );
        painter->drawEllipse( insideScaleRect );
pixhawk's avatar
pixhawk committed
        painter->restore();
    }

Bryant's avatar
Bryant committed
    const QPointF center = insideScaleRect.center();
    const double radius = 0.5 * insideScaleRect.width();
pixhawk's avatar
pixhawk committed

    painter->save();
Bryant's avatar
Bryant committed
    drawScale( painter, center, radius );
pixhawk's avatar
pixhawk committed
    painter->restore();

    painter->save();
Bryant's avatar
Bryant committed
    drawScaleContents( painter, center, radius );
pixhawk's avatar
pixhawk committed
    painter->restore();
}

/*!
  Draw the needle

  \param painter Painter
  \param center Center of the dial
  \param radius Length for the needle
  \param direction Direction of the needle in degrees, counter clockwise
Bryant's avatar
Bryant committed
  \param colorGroup ColorGroup
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtDial::drawNeedle( QPainter *painter, const QPointF &center,
    double radius, double direction, QPalette::ColorGroup colorGroup ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( d_data->needle )
    {
pixhawk's avatar
pixhawk committed
        direction = 360.0 - direction; // counter clockwise
Bryant's avatar
Bryant committed
        d_data->needle->draw( painter, center, radius, direction, colorGroup );
pixhawk's avatar
pixhawk committed
    }
}

Bryant's avatar
Bryant committed
void QwtDial::drawNeedle( QPainter *painter ) const
{
    if ( !isValid() )
        return;

    QPalette::ColorGroup colorGroup;
    if ( isEnabled() )
        colorGroup = hasFocus() ? QPalette::Active : QPalette::Inactive;
    else
        colorGroup = QPalette::Disabled;

    const QRectF sr = scaleInnerRect();

    painter->save();
    painter->setRenderHint( QPainter::Antialiasing, true );
    drawNeedle( painter, sr.center(), 0.5 * sr.width(),
        transform( value() ) + 270.0, colorGroup );
    painter->restore();
}

pixhawk's avatar
pixhawk committed
/*!
  Draw the scale

  \param painter Painter
  \param center Center of the dial
  \param radius Radius of the scale
*/
Bryant's avatar
Bryant committed
void QwtDial::drawScale( QPainter *painter, 
    const QPointF &center, double radius ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QwtRoundScaleDraw *sd = const_cast<QwtRoundScaleDraw *>( scaleDraw() );
    if ( sd == NULL )
pixhawk's avatar
pixhawk committed
        return;

Bryant's avatar
Bryant committed
    sd->setRadius( radius );
    sd->moveCenter( center );
pixhawk's avatar
pixhawk committed
    QPalette pal = palette();

Bryant's avatar
Bryant committed
    const QColor textColor = pal.color( QPalette::Text );
    pal.setColor( QPalette::WindowText, textColor ); // ticks, backbone
Bryant's avatar
Bryant committed
    painter->setFont( font() );
    painter->setPen( QPen( textColor, sd->penWidth() ) );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    painter->setBrush( Qt::red );
    sd->draw( painter, pal );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
  Draw the contents inside the scale

  Paints nothing.

  \param painter Painter
  \param center Center of the contents circle
  \param radius Radius of the contents circle
*/
void QwtDial::drawScaleContents( QPainter *painter,
    const QPointF &center, double radius ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    Q_UNUSED(painter);
    Q_UNUSED(center);
    Q_UNUSED(radius);
pixhawk's avatar
pixhawk committed
}

/*!
  Set a needle for the dial

  \param needle Needle
Bryant's avatar
Bryant committed

pixhawk's avatar
pixhawk committed
  \warning The needle will be deleted, when a different needle is
Bryant's avatar
Bryant committed
           set or in ~QwtDial()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtDial::setNeedle( QwtDialNeedle *needle )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( needle != d_data->needle )
    {
pixhawk's avatar
pixhawk committed
        if ( d_data->needle )
            delete d_data->needle;

        d_data->needle = needle;
        update();
    }
}

pixhawk's avatar
pixhawk committed
  \return needle
  \sa setNeedle()
*/
const QwtDialNeedle *QwtDial::needle() const
{
    return d_data->needle;
pixhawk's avatar
pixhawk committed
}

pixhawk's avatar
pixhawk committed
  \return needle
  \sa setNeedle()
*/
QwtDialNeedle *QwtDial::needle()
{
    return d_data->needle;
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
//! \return the scale draw
QwtRoundScaleDraw *QwtDial::scaleDraw()
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return static_cast<QwtRoundScaleDraw *>( abstractScaleDraw() );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
//! \return the scale draw
const QwtRoundScaleDraw *QwtDial::scaleDraw() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return static_cast<const QwtRoundScaleDraw *>( abstractScaleDraw() );
pixhawk's avatar
pixhawk committed
}

/*!
  Set an individual scale draw

Bryant's avatar
Bryant committed
  The motivation for setting a scale draw is often
  to overload QwtRoundScaleDraw::label() to return 
  individual tick labels.
  
pixhawk's avatar
pixhawk committed
  \param scaleDraw Scale draw
  \warning The previous scale draw is deleted
*/
Bryant's avatar
Bryant committed
void QwtDial::setScaleDraw( QwtRoundScaleDraw *scaleDraw )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    setAbstractScaleDraw( scaleDraw );
    sliderChange();
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  Change the arc of the scale
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \param minArc Lower limit
  \param maxArc Upper limit
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \sa minScaleArc(), maxScaleArc()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtDial::setScaleArc( double minArc, double maxArc )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( minArc != 360.0 && minArc != -360.0 )
        minArc = ::fmod( minArc, 360.0 );
    if ( maxArc != 360.0 && maxArc != -360.0 )
        maxArc = ::fmod( maxArc, 360.0 );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    double minScaleArc = qMin( minArc, maxArc );
    double maxScaleArc = qMax( minArc, maxArc );
Bryant's avatar
Bryant committed
    if ( maxScaleArc - minScaleArc > 360.0 )
        maxScaleArc = minScaleArc + 360.0;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( ( minScaleArc != d_data->minScaleArc ) || 
        ( maxScaleArc != d_data->maxScaleArc ) )
    {
        d_data->minScaleArc = minScaleArc;
        d_data->maxScaleArc = maxScaleArc;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        invalidateCache();
        sliderChange();
pixhawk's avatar
pixhawk committed
    }
}

Bryant's avatar
Bryant committed
/*! 
  Set the lower limit for the scale arc
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \param min Lower limit of the scale arc
  \sa setScaleArc(), setMaxScaleArc()
 */
void QwtDial::setMinScaleArc( double min )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    setScaleArc( min, d_data->maxScaleArc );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*! 
  \return Lower limit of the scale arc
  \sa setScaleArc()
*/
double QwtDial::minScaleArc() const
{
    return d_data->minScaleArc;
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*! 
  Set the upper limit for the scale arc

  \param max Upper limit of the scale arc
  \sa setScaleArc(), setMinScaleArc()
 */
void QwtDial::setMaxScaleArc( double max )
{
    setScaleArc( d_data->minScaleArc, max );
}

/*! 
  \return Upper limit of the scale arc
  \sa setScaleArc()
*/
double QwtDial::maxScaleArc() const
{
    return d_data->maxScaleArc;
pixhawk's avatar
pixhawk committed
}

/*!
  \brief Change the origin

pixhawk's avatar
pixhawk committed
  The origin is the angle where scale and needle is relative to.

  \param origin New origin
  \sa origin()
*/
Bryant's avatar
Bryant committed
void QwtDial::setOrigin( double origin )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    invalidateCache();

pixhawk's avatar
pixhawk committed
    d_data->origin = origin;
Bryant's avatar
Bryant committed
    sliderChange();
pixhawk's avatar
pixhawk committed
}

/*!
  The origin is the angle where scale and needle is relative to.

  \return Origin of the dial
  \sa setOrigin()
*/
double QwtDial::origin() const
{
    return d_data->origin;
}

/*!
  \return Size hint
Bryant's avatar
Bryant committed
  \sa minimumSizeHint()
pixhawk's avatar
pixhawk committed
*/
QSize QwtDial::sizeHint() const
{
    int sh = 0;
Bryant's avatar
Bryant committed
    if ( scaleDraw() )
        sh = qCeil( scaleDraw()->extent( font() ) );
pixhawk's avatar
pixhawk committed

    const int d = 6 * sh + 2 * lineWidth();
Bryant's avatar
Bryant committed
    QSize hint( d, d ); 
    if ( !isReadOnly() )
        hint = hint.expandedTo( QApplication::globalStrut() );

    return hint;
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
  \return Minimum size hint
  \sa sizeHint()
pixhawk's avatar
pixhawk committed
QSize QwtDial::minimumSizeHint() const
pixhawk's avatar
pixhawk committed
    int sh = 0;
Bryant's avatar
Bryant committed
    if ( scaleDraw() )
        sh = qCeil( scaleDraw()->extent( font() ) );
pixhawk's avatar
pixhawk committed

    const int d = 3 * sh + 2 * lineWidth();
pixhawk's avatar
pixhawk committed
    return QSize( d, d );
}

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

  \param pos Mouse position

  \retval True, when the inner circle contains pos 
  \sa scrolledTo()
*/
bool QwtDial::isScrollPosition( const QPoint &pos ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    const QRegion region( innerRect(), QRegion::Ellipse );
    if ( region.contains( pos ) && ( pos != innerRect().center() ) )
    {
        double angle = QLineF( rect().center(), pos ).angle();
        if ( d_data->mode == QwtDial::RotateScale )
            angle = 360.0 - angle;

        double valueAngle = 
            qwtNormalizeDegrees( 90.0 - transform( value() ) );

        d_data->mouseOffset = qwtNormalizeDegrees( angle - valueAngle );
        d_data->arcOffset = scaleMap().p1();

        return true;
pixhawk's avatar
pixhawk committed
    }
Bryant's avatar
Bryant committed

    return false;
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \brief Determine the value for a new position of the
         slider handle.

  \param pos Mouse position
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \return Value for the mouse position
  \sa isScrollPosition()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
double QwtDial::scrolledTo( const QPoint &pos ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    double angle = QLineF( rect().center(), pos ).angle();
    if ( d_data->mode == QwtDial::RotateScale )
    {
        angle += scaleMap().p1() - d_data->arcOffset;
        angle = 360.0 - angle;
    }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    angle = qwtNormalizeDegrees( angle - d_data->mouseOffset );
    angle = qwtNormalizeDegrees( 90.0 - angle );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( scaleMap().pDist() >= 360.0 )
    {
        if ( angle < scaleMap().p1() )
            angle += 360.0;

        if ( !wrapping() )
        {
            double boundedAngle = angle;

            const double arc = angle - transform( value() );
            if ( qAbs( arc ) > 180.0 )
            {
                boundedAngle = ( arc > 0 ) 
                    ? scaleMap().p1() : scaleMap().p2();
pixhawk's avatar
pixhawk committed
            }
Bryant's avatar
Bryant committed

            d_data->mouseOffset += ( boundedAngle - angle );

            angle = boundedAngle;
pixhawk's avatar
pixhawk committed
        }
Bryant's avatar
Bryant committed
    }
    else
    {
        const double boundedAngle =
            qwtBoundedAngle( scaleMap().p1(), angle, scaleMap().p2() );

        if ( !wrapping() )
            d_data->mouseOffset += ( boundedAngle - angle );

        angle = boundedAngle;
pixhawk's avatar
pixhawk committed
    }

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

/*!
Bryant's avatar
Bryant committed
  Change Event handler
  \param event Change event

  Invalidates internal paint caches if necessary
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtDial::changeEvent( QEvent *event )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    switch( event->type() )
    {
        case QEvent::EnabledChange:
        case QEvent::FontChange:
        case QEvent::StyleChange:
        case QEvent::PaletteChange:
        case QEvent::LanguageChange:
        case QEvent::LocaleChange:
        {
            invalidateCache();
            break;
        }
        default:
            break;
pixhawk's avatar
pixhawk committed
    }
Bryant's avatar
Bryant committed
    
    QwtAbstractSlider::changeEvent( event );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
  Wheel Event handler
  \param event Wheel event
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtDial::wheelEvent( QWheelEvent *event )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    const QRegion region( innerRect(), QRegion::Ellipse );
    if ( region.contains( event->pos() ) )
        QwtAbstractSlider::wheelEvent( event );
}
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
void QwtDial::setAngleRange( double angle, double span )
{
    QwtRoundScaleDraw *sd = const_cast<QwtRoundScaleDraw *>( scaleDraw() );
    if ( sd  )
    {
        angle = qwtNormalizeDegrees( angle - 270.0 );
        sd->setAngleRange( angle, angle + span );
pixhawk's avatar
pixhawk committed
    }
}

/*!
Bryant's avatar
Bryant committed
  Invalidate the internal caches and call 
  QwtAbstractSlider::scaleChange()
 */
void QwtDial::scaleChange()
{
    invalidateCache();
    QwtAbstractSlider::scaleChange();
}
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
void QwtDial::sliderChange()
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    setAngleRange( d_data->origin + d_data->minScaleArc,
        d_data->maxScaleArc - d_data->minScaleArc );

    if ( mode() == RotateScale )
    {
        const double arc = transform( value() ) - scaleMap().p1();
        setAngleRange( d_data->origin - arc,
            d_data->maxScaleArc - d_data->minScaleArc );
    }

    QwtAbstractSlider::sliderChange();
pixhawk's avatar
pixhawk committed
}