Skip to content
qwt_dial.cpp 28.3 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
 *****************************************************************************/

#include <math.h>
#include <qpainter.h>
#if QT_VERSION >= 0x040000
#include <qbitmap.h>
#include <qpalette.h>
#endif
#include <qpixmap.h>
#include <qevent.h>
#include "qwt_math.h"
#include "qwt_scale_engine.h"
#include "qwt_scale_map.h"
#include "qwt_paint_buffer.h"
#include "qwt_painter.h"
#include "qwt_dial_needle.h"
#include "qwt_dial.h"

class QwtDial::PrivateData
{
public:
    PrivateData():
        visibleBackground(true),
        frameShadow(Sunken),
        lineWidth(0),
        mode(RotateNeedle),
        origin(90.0),
        minScaleArc(0.0),
        maxScaleArc(0.0),
        scaleDraw(0),
        maxMajIntv(36),
        maxMinIntv(10),
        scaleStep(0.0),
        needle(0) {
pixhawk's avatar
pixhawk committed
    }

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

    QwtDial::Mode mode;

    double origin;
    double minScaleArc;
    double maxScaleArc;

    QwtDialScaleDraw *scaleDraw;
    int maxMajIntv;
    int maxMinIntv;
    double scaleStep;

    QwtDialNeedle *needle;

    static double previousDir;
};

double QwtDial::PrivateData::previousDir = -1.0;

/*!
  Constructor

  \param parent Parent dial widget
*/
QwtDialScaleDraw::QwtDialScaleDraw(QwtDial *parent):
    d_parent(parent),
    d_penWidth(1)
{
}

/*!
  Set the pen width used for painting the scale

  \param penWidth Pen width
  \sa penWidth(), QwtDial::drawScale()
*/
pixhawk's avatar
pixhawk committed
void QwtDialScaleDraw::setPenWidth(uint penWidth)
{
    d_penWidth = penWidth;
}

/*!
  \return Pen width used for painting the scale
  \sa setPenWidth, QwtDial::drawScale()
*/
uint QwtDialScaleDraw::penWidth() const
{
    return d_penWidth;
}

pixhawk's avatar
pixhawk committed
  Call QwtDial::scaleLabel of the parent dial widget.

  \param value Value to display
pixhawk's avatar
pixhawk committed
  \sa QwtDial::scaleLabel
pixhawk's avatar
pixhawk committed
QwtText QwtDialScaleDraw::label(double value) const
{
    if ( d_parent == NULL )
        return QwtRoundScaleDraw::label(value);

    return d_parent->scaleLabel(value);
}

/*!
  \brief Constructor
  \param parent Parent widget

  Create a dial widget with no scale and no needle.
pixhawk's avatar
pixhawk committed
  The default origin is 90.0 with no valid value. It accepts
  mouse and keyboard inputs and has no step size. The default mode
  is QwtDial::RotateNeedle.
pixhawk's avatar
pixhawk committed

QwtDial::QwtDial(QWidget* parent):
    QwtAbstractSlider(Qt::Horizontal, parent)
{
    initDial();
}

#if QT_VERSION < 0x040000
/*!
  \brief Constructor
  \param parent Parent widget
  \param name Object name

  Create a dial widget with no scale and no needle.
pixhawk's avatar
pixhawk committed
  The default origin is 90.0 with no valid value. It accepts
  mouse and keyboard inputs and has no step size. The default mode
  is QwtDial::RotateNeedle.
pixhawk's avatar
pixhawk committed
QwtDial::QwtDial(QWidget* parent, const char *name):
    QwtAbstractSlider(Qt::Horizontal, parent)
{
    setName(name);
    initDial();
}
#endif

void QwtDial::initDial()
{
    d_data = new PrivateData;

#if QT_VERSION < 0x040000
    setWFlags(Qt::WNoAutoErase);
pixhawk's avatar
pixhawk committed

#if QT_VERSION >= 0x040000
    using namespace Qt;
#endif
    setFocusPolicy(TabFocus);

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

        // Base: background color of the circle inside the frame.
        // Foreground: background color of the circle inside the scale

#if QT_VERSION < 0x040000
        p.setColor(cg, QColorGroup::Foreground,
                   p.color(cg, QColorGroup::Base));
pixhawk's avatar
pixhawk committed
#else
        p.setColor(cg, QPalette::Foreground,
                   p.color(cg, QPalette::Base));
pixhawk's avatar
pixhawk committed
#endif
    }
    setPalette(p);

    d_data->scaleDraw = new QwtDialScaleDraw(this);
    d_data->scaleDraw->setRadius(0);

    setScaleArc(0.0, 360.0); // scale as a full circle
    setRange(0.0, 360.0, 1.0, 10); // degrees as deafult
}

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

/*!
  Show/Hide the area outside of the frame
  \param show Show if true, hide if false

  \sa hasVisibleBackground(), setMask()
  \warning When QwtDial is a toplevel widget the window
           border might disappear too.
*/
void QwtDial::showBackground(bool show)
{
    if ( d_data->visibleBackground != show ) {
pixhawk's avatar
pixhawk committed
        d_data->visibleBackground = show;
        updateMask();
    }
}

/*!
  true when the area outside of the frame is visible

  \sa showBackground(), setMask()
*/
bool QwtDial::hasVisibleBackground() const
{
    return d_data->visibleBackground;
pixhawk's avatar
pixhawk committed
}

/*!
  Sets the frame shadow value from the frame style.
  \param shadow Frame shadow
  \sa setLineWidth(), QFrame::setFrameShadow()
*/
void QwtDial::setFrameShadow(Shadow shadow)
{
    if ( shadow != d_data->frameShadow ) {
pixhawk's avatar
pixhawk committed
        d_data->frameShadow = shadow;
        if ( lineWidth() > 0 )
            update();
    }
}

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

/*!
  Sets the line width

  \param lineWidth Line width
  \sa setFrameShadow()
*/
void QwtDial::setLineWidth(int lineWidth)
{
    if ( lineWidth < 0 )
        lineWidth = 0;

    if ( d_data->lineWidth != lineWidth ) {
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
}

/*!
  \return bounding rect of the circle inside the frame
  \sa setLineWidth(), scaleContentsRect(), boundingRect()
*/
QRect QwtDial::contentsRect() const
{
    const int lw = lineWidth();

    QRect r = boundingRect();
    if ( lw > 0 ) {
        r.setRect(r.x() + lw, r.y() + lw,
                  r.width() - 2 * lw, r.height() - 2 * lw);
pixhawk's avatar
pixhawk committed
    }
    return r;
}

/*!
  \return bounding rect of the dial including the frame
  \sa setLineWidth(), scaleContentsRect(), contentsRect()
*/
QRect QwtDial::boundingRect() const
{
    const int radius = qwtMin(width(), height()) / 2;

    QRect r(0, 0, 2 * radius, 2 * radius);
    r.moveCenter(rect().center());
    return r;
}

/*!
  \return rect inside the scale
  \sa setLineWidth(), boundingRect(), contentsRect()
*/
QRect QwtDial::scaleContentsRect() const
{
#if QT_VERSION < 0x040000
    const QPen scalePen(colorGroup().text(), 0, Qt::NoPen);
#else
    const QPen scalePen(palette().text(), 0, Qt::NoPen);
#endif

    int scaleDist = 0;
    if ( d_data->scaleDraw ) {
pixhawk's avatar
pixhawk committed
        scaleDist = d_data->scaleDraw->extent(scalePen, font());
        scaleDist++; // margin
    }

    const QRect rect = contentsRect();
    return QRect(rect.x() + scaleDist, rect.y() + scaleDist,
                 rect.width() - 2 * scaleDist, rect.height() - 2 * scaleDist);
pixhawk's avatar
pixhawk committed
}

/*!
  \brief Change the mode of the meter.
  \param mode New mode

pixhawk's avatar
pixhawk committed
  The value of the meter is indicated by the difference
  between north of the scale and the direction of the needle.
  In case of QwtDial::RotateNeedle north is pointing
  to the origin() and the needle is rotating, in case of
  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()
pixhawk's avatar
pixhawk committed
void QwtDial::setMode(Mode mode)
{
    if ( mode != d_data->mode ) {
pixhawk's avatar
pixhawk committed
        d_data->mode = mode;
pixhawk's avatar
pixhawk committed
    }
pixhawk's avatar
pixhawk committed

pixhawk's avatar
pixhawk committed
  \return mode of the dial.
pixhawk's avatar
pixhawk committed
  The value of the dial is indicated by the difference
  between the origin and the direction of the needle.
  In case of QwtDial::RotateNeedle the scale arc is fixed
  to the origin() and the needle is rotating, in case of
  QwtDial::RotateScale, the needle points to origin()
  and the scale is rotating.
pixhawk's avatar
pixhawk committed
  The default mode is QwtDial::RotateNeedle.

  \sa setMode(), origin(), setScaleArc(), value()
*/
QwtDial::Mode QwtDial::mode() const
{
    return d_data->mode;
}

/*!
    Sets whether it is possible to step the value from the highest value to
pixhawk's avatar
pixhawk committed
    the lowest value and vice versa to on.

    \param wrapping en/disables wrapping

    \sa wrapping(), QwtDoubleRange::periodic()
    \note The meaning of wrapping is like the wrapping property of QSpinBox,
          but not like it is used in QDial.
pixhawk's avatar
pixhawk committed
*/
void QwtDial::setWrapping(bool wrapping)
{
    setPeriodic(wrapping);
pixhawk's avatar
pixhawk committed

/*!
    wrapping() holds whether it is possible to step the value from the
    highest value to the lowest value and vice versa.
pixhawk's avatar
pixhawk committed

    \sa setWrapping(), QwtDoubleRange::setPeriodic()
    \note The meaning of wrapping is like the wrapping property of QSpinBox,
          but not like it is used in QDial.
*/
pixhawk's avatar
pixhawk committed
bool QwtDial::wrapping() const
{
    return periodic();
}

pixhawk's avatar
pixhawk committed
   Resize the dial widget
   \param e Resize event
*/
void QwtDial::resizeEvent(QResizeEvent *e)
{
    QWidget::resizeEvent(e);

    if ( !hasVisibleBackground() )
        updateMask();
}

/*!
   Paint the dial
pixhawk's avatar
pixhawk committed
   \param e Paint event
*/
void QwtDial::paintEvent(QPaintEvent *e)
{
    const QRect &ur = e->rect();
    if ( ur.isValid() ) {
pixhawk's avatar
pixhawk committed
#if QT_VERSION < 0x040000
        QwtPaintBuffer paintBuffer(this, ur);
        QPainter &painter = *paintBuffer.painter();
#else
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing, true);
#endif

        painter.save();
        drawContents(&painter);
        painter.restore();

        painter.save();
        drawFrame(&painter);
        painter.restore();

        if ( hasFocus() )
            drawFocusIndicator(&painter);
    }
}

/*!
  Draw a dotted round circle, if !isReadOnly()

  \param painter Painter
*/
void QwtDial::drawFocusIndicator(QPainter *painter) const
{
    if ( !isReadOnly() ) {
pixhawk's avatar
pixhawk committed
        QRect focusRect = contentsRect();

        const int margin = 2;
        focusRect.setRect(
pixhawk's avatar
pixhawk committed
            focusRect.x() + margin,
            focusRect.y() + margin,
            focusRect.width() - 2 * margin,
            focusRect.height() - 2 * margin);

#if QT_VERSION < 0x040000
        QColor color = colorGroup().color(QColorGroup::Base);
#else
        QColor color = palette().color(QPalette::Base);
#endif
        if (color.isValid()) {
pixhawk's avatar
pixhawk committed
            const QColor gray(Qt::gray);

            int h, s, v;
#if QT_VERSION < 0x040000
            color.hsv(&h, &s, &v);
#else
            color.getHsv(&h, &s, &v);
#endif
            color = (v > 128) ? gray.dark(120) : gray.light(120);
pixhawk's avatar
pixhawk committed
            color = Qt::darkGray;

        painter->save();
        painter->setBrush(Qt::NoBrush);
        painter->setPen(QPen(color, 0, Qt::DotLine));
        painter->drawEllipse(focusRect);
        painter->restore();
    }
}

/*!
  Draw the frame around the dial

  \param painter Painter
  \sa lineWidth(), frameShadow()
*/
void QwtDial::drawFrame(QPainter *painter)
{
    const int lw = lineWidth();
    const int off = (lw + 1) % 2;

    QRect r = boundingRect();
    r.setRect(r.x() + lw / 2 - off, r.y() + lw / 2 - off,
              r.width() - lw + off + 1, r.height() - lw + off + 1);
pixhawk's avatar
pixhawk committed
#if QT_VERSION >= 0x040000
#ifdef __GNUC__
#endif
    r.setX(r.x() + 1);
    r.setY(r.y() + 1);
    r.setWidth(r.width() - 2);
    r.setHeight(r.height() - 2);
#endif

    if ( lw > 0 ) {
        switch(d_data->frameShadow) {
        case QwtDial::Raised:
pixhawk's avatar
pixhawk committed
#if QT_VERSION < 0x040000
            QwtPainter::drawRoundFrame(painter, r,
                                       lw, colorGroup(), false);
pixhawk's avatar
pixhawk committed
#else
            QwtPainter::drawRoundFrame(painter, r,
                                       lw, palette(), false);
pixhawk's avatar
pixhawk committed
#endif
            break;
        case QwtDial::Sunken:
pixhawk's avatar
pixhawk committed
#if QT_VERSION < 0x040000
            QwtPainter::drawRoundFrame(painter, r,
                                       lw, colorGroup(), true);
pixhawk's avatar
pixhawk committed
#else
            QwtPainter::drawRoundFrame(painter, r,
                                       lw, palette(), true);
pixhawk's avatar
pixhawk committed
#endif
            break;
        default: { // Plain
            painter->save();
            painter->setPen(QPen(Qt::black, lw));
            painter->setBrush(Qt::NoBrush);
            painter->drawEllipse(r);
            painter->restore();
        }
pixhawk's avatar
pixhawk committed
        }
    }
}

/*!
  \brief Draw the contents inside the frame
pixhawk's avatar
pixhawk committed
  QColorGroup::Background is the background color outside of the frame.
  QColorGroup::Base is the background color inside the frame.
  QColorGroup::Foreground is the background color inside the scale.

  \param painter Painter
  \sa boundingRect(), contentsRect(),
    scaleContentsRect(), QWidget::setPalette
*/
void QwtDial::drawContents(QPainter *painter) const
{
#if QT_VERSION < 0x040000
    if ( backgroundMode() == Qt::NoBackground ||
            colorGroup().brush(QColorGroup::Base) !=
pixhawk's avatar
pixhawk committed
            colorGroup().brush(QColorGroup::Background) )
#else
    if ( testAttribute(Qt::WA_NoSystemBackground) ||
            palette().brush(QPalette::Base) !=
pixhawk's avatar
pixhawk committed
            palette().brush(QPalette::Background) )
#endif
    {

        const QRect br = boundingRect();

        painter->save();
        painter->setPen(Qt::NoPen);

#if QT_VERSION < 0x040000
        painter->setBrush(colorGroup().brush(QColorGroup::Base));
#else
        painter->setBrush(palette().brush(QPalette::Base));
#endif

        painter->drawEllipse(br);
        painter->restore();
    }


    const QRect insideScaleRect = scaleContentsRect();
#if QT_VERSION < 0x040000
    if ( colorGroup().brush(QColorGroup::Foreground) !=
            colorGroup().brush(QColorGroup::Base) )
pixhawk's avatar
pixhawk committed
#else
    if ( palette().brush(QPalette::Foreground) !=
            palette().brush(QPalette::Base) )
pixhawk's avatar
pixhawk committed
#endif
    {
        painter->save();
        painter->setPen(Qt::NoPen);

#if QT_VERSION < 0x040000
        painter->setBrush(colorGroup().brush(QColorGroup::Foreground));
#else
        painter->setBrush(palette().brush(QPalette::Foreground));
#endif

        painter->drawEllipse(insideScaleRect.x() - 1, insideScaleRect.y() - 1,
                             insideScaleRect.width(), insideScaleRect.height() );
pixhawk's avatar
pixhawk committed

        painter->restore();
    }

    const QPoint center = insideScaleRect.center();
    const int radius = insideScaleRect.width() / 2;

    painter->save();
    drawScaleContents(painter, center, radius);
    painter->restore();

    double direction = d_data->origin;

    if (isValid()) {
pixhawk's avatar
pixhawk committed
        direction = d_data->origin + d_data->minScaleArc;
        if ( maxValue() > minValue() && d_data->maxScaleArc > d_data->minScaleArc ) {
            const double ratio =
pixhawk's avatar
pixhawk committed
                (value() - minValue()) / (maxValue() - minValue());
            direction += ratio * (d_data->maxScaleArc - d_data->minScaleArc);
        }

        if ( direction >= 360.0 )
            direction -= 360.0;
    }

    double origin = d_data->origin;
    if ( mode() == RotateScale ) {
pixhawk's avatar
pixhawk committed
        origin -= direction - d_data->origin;
        direction = d_data->origin;
    }

    painter->save();
    drawScale(painter, center, radius, origin, d_data->minScaleArc, d_data->maxScaleArc);
    painter->restore();

    if ( isValid() ) {
pixhawk's avatar
pixhawk committed
        QPalette::ColorGroup cg;
        if ( isEnabled() )
            cg = hasFocus() ? QPalette::Active : QPalette::Inactive;
        else
            cg = QPalette::Disabled;

        painter->save();
        drawNeedle(painter, center, radius, direction, cg);
        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
  \param cg ColorGroup
*/
void QwtDial::drawNeedle(QPainter *painter, const QPoint &center,
                         int radius, double direction, QPalette::ColorGroup cg) const
pixhawk's avatar
pixhawk committed
{
    if ( d_data->needle ) {
pixhawk's avatar
pixhawk committed
        direction = 360.0 - direction; // counter clockwise
        d_data->needle->draw(painter, center, radius, direction, cg);
    }
}

/*!
  Draw the scale

  \param painter Painter
  \param center Center of the dial
  \param radius Radius of the scale
  \param origin Origin of the scale
  \param minArc Minimum of the arc
  \param maxArc Minimum of the arc

pixhawk's avatar
pixhawk committed
  \sa QwtAbstractScaleDraw::setAngleRange
*/
void QwtDial::drawScale(QPainter *painter, const QPoint &center,
                        int radius, double origin, double minArc, double maxArc) const
pixhawk's avatar
pixhawk committed
{
    if ( d_data->scaleDraw == NULL )
        return;

    origin -= 270.0; // hardcoded origin of QwtScaleDraw

    double angle = maxArc - minArc;
    if ( angle > 360.0 )
        angle = fmod(angle, 360.0);

    minArc += origin;
    if ( minArc < -360.0 )
        minArc = fmod(minArc, 360.0);
pixhawk's avatar
pixhawk committed
    maxArc = minArc + angle;
    if ( maxArc > 360.0 ) {
pixhawk's avatar
pixhawk committed
        // QwtAbstractScaleDraw::setAngleRange accepts only values
        // in the range [-360.0..360.0]
        minArc -= 360.0;
        maxArc -= 360.0;
    }
pixhawk's avatar
pixhawk committed
    painter->setFont(font());

    d_data->scaleDraw->setAngleRange(minArc, maxArc);
    d_data->scaleDraw->setRadius(radius);
    d_data->scaleDraw->moveCenter(center);

#if QT_VERSION < 0x040000
    QColorGroup cg = colorGroup();

    const QColor textColor = cg.color(QColorGroup::Text);
    cg.setColor(QColorGroup::Foreground, textColor);
    painter->setPen(QPen(textColor, d_data->scaleDraw->penWidth()));
pixhawk's avatar
pixhawk committed
    d_data->scaleDraw->draw(painter, cg);
#else
    QPalette pal = palette();

    const QColor textColor = pal.color(QPalette::Text);
    pal.setColor(QPalette::Foreground, textColor); //ticks, backbone
pixhawk's avatar
pixhawk committed
    painter->setPen(QPen(textColor, d_data->scaleDraw->penWidth()));

    d_data->scaleDraw->draw(painter, pal);
#endif
}

void QwtDial::drawScaleContents(QPainter *,
                                const QPoint &, int) const
pixhawk's avatar
pixhawk committed
{
    // empty default implementation
}

/*!
  Set a needle for the dial

  Qwt is missing a set of good looking needles.
pixhawk's avatar
pixhawk committed
  Contributions are very welcome.

  \param needle Needle
  \warning The needle will be deleted, when a different needle is
    set or in ~QwtDial()
*/
void QwtDial::setNeedle(QwtDialNeedle *needle)
{
    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
}

//! QwtDoubleRange update hook
void QwtDial::rangeChange()
{
    updateScale();
}

pixhawk's avatar
pixhawk committed
  Update the scale with the current attributes
  \sa setScale()
*/
void QwtDial::updateScale()
{
    if ( d_data->scaleDraw ) {
pixhawk's avatar
pixhawk committed
        QwtLinearScaleEngine scaleEngine;

        const QwtScaleDiv scaleDiv = scaleEngine.divideScale(
                                         minValue(), maxValue(),
                                         d_data->maxMajIntv, d_data->maxMinIntv, d_data->scaleStep);
pixhawk's avatar
pixhawk committed

        d_data->scaleDraw->setTransformation(scaleEngine.transformation());
        d_data->scaleDraw->setScaleDiv(scaleDiv);
    }
}

//! Return the scale draw
QwtDialScaleDraw *QwtDial::scaleDraw()
{
    return d_data->scaleDraw;
pixhawk's avatar
pixhawk committed
}

//! Return the scale draw
const QwtDialScaleDraw *QwtDial::scaleDraw() const
{
    return d_data->scaleDraw;
pixhawk's avatar
pixhawk committed
}

/*!
  Set an individual scale draw

  \param scaleDraw Scale draw
  \warning The previous scale draw is deleted
*/
void QwtDial::setScaleDraw(QwtDialScaleDraw *scaleDraw)
{
    if ( scaleDraw != d_data->scaleDraw ) {
pixhawk's avatar
pixhawk committed
        if ( d_data->scaleDraw )
            delete d_data->scaleDraw;
pixhawk's avatar
pixhawk committed
        d_data->scaleDraw = scaleDraw;
        updateScale();
        update();
    }
}

/*!
  Change the intervals of the scale
  \sa QwtAbstractScaleDraw::setScale
*/
void QwtDial::setScale(int maxMajIntv, int maxMinIntv, double step)
{
    d_data->maxMajIntv = maxMajIntv;
    d_data->maxMinIntv = maxMinIntv;
    d_data->scaleStep = step;

    updateScale();
}

/*!
  A wrapper method for accessing the scale draw.
pixhawk's avatar
pixhawk committed

  - options == 0\n
    No visible scale: setScaleDraw(NULL)
  - options & ScaleBackbone\n
    En/disable the backbone of the scale.
  - options & ScaleTicks\n
    En/disable the ticks of the scale.
  - options & ScaleLabel\n
    En/disable scale labels

  \sa QwtAbstractScaleDraw::enableComponent
pixhawk's avatar
pixhawk committed
*/
void QwtDial::setScaleOptions(int options)
{
    if ( options == 0 )
        setScaleDraw(NULL);

    QwtDialScaleDraw *sd = d_data->scaleDraw;
    if ( sd == NULL )
        return;

    sd->enableComponent(QwtAbstractScaleDraw::Backbone,
                        options & ScaleBackbone);

    sd->enableComponent(QwtAbstractScaleDraw::Ticks,
                        options & ScaleTicks);
pixhawk's avatar
pixhawk committed

    sd->enableComponent(QwtAbstractScaleDraw::Labels,
                        options & ScaleLabel);
pixhawk's avatar
pixhawk committed
}

//! See: QwtAbstractScaleDraw::setTickLength, QwtDialScaleDraw::setPenWidth
void QwtDial::setScaleTicks(int minLen, int medLen,
                            int majLen, int penWidth)
pixhawk's avatar
pixhawk committed
{
    QwtDialScaleDraw *sd = d_data->scaleDraw;
    if ( sd ) {
pixhawk's avatar
pixhawk committed
        sd->setTickLength(QwtScaleDiv::MinorTick, minLen);
        sd->setTickLength(QwtScaleDiv::MediumTick, medLen);
        sd->setTickLength(QwtScaleDiv::MajorTick, majLen);
        sd->setPenWidth(penWidth);
    }
}

/*!
   Find the label for a value

   \param value Value
   \return label
pixhawk's avatar
pixhawk committed
*/
QwtText QwtDial::scaleLabel(double value) const
{
#if 1
    if ( value == -0 )
        value = 0;
#endif

    return QString::number(value);
}

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

//! \return Upper limit of the scale arc
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()
*/
void QwtDial::setOrigin(double origin)
{
    d_data->origin = origin;
    update();
}

/*!
  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;
}

/*!
  Change the arc of the scale

  \param minArc Lower limit
  \param maxArc Upper limit
*/
void QwtDial::setScaleArc(double minArc, double maxArc)
{
    if ( minArc != 360.0 && minArc != -360.0 )
        minArc = fmod(minArc, 360.0);
    if ( maxArc != 360.0 && maxArc != -360.0 )
        maxArc = fmod(maxArc, 360.0);

    d_data->minScaleArc = qwtMin(minArc, maxArc);
    d_data->maxScaleArc = qwtMax(minArc, maxArc);
    if ( d_data->maxScaleArc - d_data->minScaleArc > 360.0 )
        d_data->maxScaleArc = d_data->minScaleArc + 360.0;
pixhawk's avatar
pixhawk committed
    update();
}

//! QwtDoubleRange update hook
void QwtDial::valueChange()
{
    update();
    QwtAbstractSlider::valueChange();
}

/*!
  \return Size hint
*/
QSize QwtDial::sizeHint() const
{
    int sh = 0;
    if ( d_data->scaleDraw )
        sh = d_data->scaleDraw->extent( QPen(), font() );

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

pixhawk's avatar
pixhawk committed
  \brief Return a minimum size hint
  \warning The return value of QwtDial::minimumSizeHint() depends on the
           font and the scale.
pixhawk's avatar
pixhawk committed
QSize QwtDial::minimumSizeHint() const
pixhawk's avatar
pixhawk committed
    int sh = 0;
    if ( d_data->scaleDraw )
        sh = d_data->scaleDraw->extent(QPen(), font() );

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

static double line2Radians(const QPoint &p1, const QPoint &p2)
{
    const QPoint p = p2 - p1;

    double angle;
    if ( p.x() == 0 )
        angle = ( p.y() <= 0 ) ? M_PI_2 : 3 * M_PI_2;
pixhawk's avatar
pixhawk committed
        angle = atan(double(-p.y()) / double(p.x()));
        if ( p.x() < 0 )
            angle += M_PI;
        if ( angle < 0.0 )
            angle += 2 * M_PI;
    }