Skip to content
qwt_thermo.cpp 22.5 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_thermo.h"
pixhawk's avatar
pixhawk committed
#include "qwt_scale_engine.h"
#include "qwt_scale_draw.h"
#include "qwt_scale_map.h"
Bryant's avatar
Bryant committed
#include "qwt_color_map.h"
#include <qpainter.h>
#include <qevent.h>
#include <qdrawutil.h>
#include <qstyle.h>
#include <qstyleoption.h>
#include <qmath.h>

static inline void qwtDrawLine( QPainter *painter, int pos, 
    const QColor &color, const QRect &pipeRect, const QRect &liquidRect,
    Qt::Orientation orientation )
{
    painter->setPen( color );
    if ( orientation == Qt::Horizontal )
    {
        if ( pos >= liquidRect.left() && pos < liquidRect.right() )
            painter->drawLine( pos, pipeRect.top(), pos, pipeRect.bottom() );
    }
    else
    {
        if ( pos >= liquidRect.top() && pos < liquidRect.bottom() )
            painter->drawLine( pipeRect.left(), pos, pipeRect.right(), pos );
    }
}

QVector<double> qwtTickList( const QwtScaleDiv &scaleDiv )
{
    QVector<double> values;

    double lowerLimit = scaleDiv.interval().minValue();
    double upperLimit = scaleDiv.interval().maxValue();

    if ( upperLimit < lowerLimit )
        qSwap( lowerLimit, upperLimit );

    values += lowerLimit;

    for ( int tickType = QwtScaleDiv::MinorTick;
        tickType < QwtScaleDiv::NTickTypes; tickType++ )
    {
        const QList<double> ticks = scaleDiv.ticks( tickType );

        for ( int i = 0; i < ticks.count(); i++ )
        {
            const double v = ticks[i];
            if ( v > lowerLimit && v < upperLimit )
                values += v;
        }       
    }   

    values += upperLimit;
    
    return values;
}
pixhawk's avatar
pixhawk committed

class QwtThermo::PrivateData
{
public:
    PrivateData():
Bryant's avatar
Bryant committed
        orientation( Qt::Vertical ),
        scalePosition( QwtThermo::TrailingScale ),
        spacing( 3 ),
        borderWidth( 2 ),
        pipeWidth( 10 ),
        alarmLevel( 0.0 ),
        alarmEnabled( false ),
        autoFillPipe( true ),
        originMode( QwtThermo::OriginMinimum ),
        origin( 0.0 ),
        colorMap( NULL ),
        value( 0.0 )
    {
        rangeFlags = QwtInterval::IncludeBorders;
    }

    ~PrivateData()
    {
        delete colorMap;
    }
pixhawk's avatar
pixhawk committed

    Qt::Orientation orientation;
Bryant's avatar
Bryant committed
    QwtThermo::ScalePosition scalePosition;

    int spacing;
pixhawk's avatar
pixhawk committed
    int borderWidth;
Bryant's avatar
Bryant committed
    int pipeWidth;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QwtInterval::BorderFlags rangeFlags;
pixhawk's avatar
pixhawk committed
    double alarmLevel;
    bool alarmEnabled;
Bryant's avatar
Bryant committed
    bool autoFillPipe;
    QwtThermo::OriginMode originMode;
    double origin;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QwtColorMap *colorMap;

    double value;
};
pixhawk's avatar
pixhawk committed

pixhawk's avatar
pixhawk committed
  Constructor
  \param parent Parent widget
*/
Bryant's avatar
Bryant committed
QwtThermo::QwtThermo( QWidget *parent ):
    QwtAbstractScale( parent )
pixhawk's avatar
pixhawk committed
{
    d_data = new PrivateData;

Bryant's avatar
Bryant committed
    QSizePolicy policy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
    if ( d_data->orientation == Qt::Vertical )
pixhawk's avatar
pixhawk committed
        policy.transpose();

Bryant's avatar
Bryant committed
    setSizePolicy( policy );
Bryant's avatar
Bryant committed
    setAttribute( Qt::WA_WState_OwnSizePolicy, false );
    layoutThermo( true );
pixhawk's avatar
pixhawk committed
}

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

Bryant's avatar
Bryant committed
/*!
  \brief Exclude/Include min/max values
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  According to the flags minValue() and maxValue()
  are included/excluded from the pipe. In case of an
  excluded value the corresponding tick is painted
  1 pixel off of the pipeRect().
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  F.e. when a minimum
  of 0.0 has to be displayed as an empty pipe the minValue()
  needs to be excluded.

  \param flags Range flags
  \sa rangeFlags()
*/
void QwtThermo::setRangeFlags( QwtInterval::BorderFlags flags )
Bryant's avatar
Bryant committed
    if ( d_data->rangeFlags != flags )
    {
        d_data->rangeFlags = flags;
        update();
    }
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
  \return Range flags
  \sa setRangeFlags()
*/
QwtInterval::BorderFlags QwtThermo::rangeFlags() const
Bryant's avatar
Bryant committed
    return d_data->rangeFlags;
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
  Set the current value.

  \param value New Value
  \sa value()
*/
void QwtThermo::setValue( double value )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( d_data->value != value )
    {
        d_data->value = value;
pixhawk's avatar
pixhawk committed
        update();
    }
}

//! Return the value.
double QwtThermo::value() const
{
    return d_data->value;
pixhawk's avatar
pixhawk committed
}

/*!
  \brief Set a scale draw

  For changing the labels of the scales, it
  is necessary to derive from QwtScaleDraw and
  overload QwtScaleDraw::label().

  \param scaleDraw ScaleDraw object, that has to be created with
Bryant's avatar
Bryant committed
                   new and will be deleted in ~QwtThermo() or the next
pixhawk's avatar
pixhawk committed
                   call of setScaleDraw().
*/
Bryant's avatar
Bryant committed
void QwtThermo::setScaleDraw( QwtScaleDraw *scaleDraw )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    setAbstractScaleDraw( scaleDraw );
pixhawk's avatar
pixhawk committed
}

/*!
   \return the scale draw of the thermo
   \sa setScaleDraw()
*/
const QwtScaleDraw *QwtThermo::scaleDraw() const
{
Bryant's avatar
Bryant committed
    return static_cast<const QwtScaleDraw *>( abstractScaleDraw() );
pixhawk's avatar
pixhawk committed
}

/*!
   \return the scale draw of the thermo
   \sa setScaleDraw()
*/
QwtScaleDraw *QwtThermo::scaleDraw()
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return static_cast<QwtScaleDraw *>( abstractScaleDraw() );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
  Paint event handler
  \param event Paint event
*/
void QwtThermo::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
    const QRect tRect = pipeRect();

    if ( !tRect.contains( event->rect() ) )
    {
        if ( d_data->scalePosition != QwtThermo::NoScale )
            scaleDraw()->draw( &painter, palette() );
pixhawk's avatar
pixhawk committed
    }
Bryant's avatar
Bryant committed

    const int bw = d_data->borderWidth;

    const QBrush brush = palette().brush( QPalette::Base );
    qDrawShadePanel( &painter, 
        tRect.adjusted( -bw, -bw, bw, bw ),
        palette(), true, bw, 
        d_data->autoFillPipe ? &brush : NULL );

    drawLiquid( &painter, tRect );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*! 
  Resize event handler
  \param event Resize event
*/
void QwtThermo::resizeEvent( QResizeEvent *event )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    Q_UNUSED( event );
pixhawk's avatar
pixhawk committed
    layoutThermo( false );
}

Bryant's avatar
Bryant committed
/*! 
  Qt change event handler
  \param event Event
*/
void QwtThermo::changeEvent( QEvent *event )
{
    switch( event->type() )
    {
        case QEvent::StyleChange:
        case QEvent::FontChange:
        {
            layoutThermo( true );
            break;
        }
        default:
            break;
    }
}

pixhawk's avatar
pixhawk committed
/*!
  Recalculate the QwtThermo geometry and layout based on
Bryant's avatar
Bryant committed
  pipeRect() and the fonts.

pixhawk's avatar
pixhawk committed
  \param update_geometry notify the layout system and call update
         to redraw the scale
*/
void QwtThermo::layoutThermo( bool update_geometry )
{
Bryant's avatar
Bryant committed
    const QRect tRect = pipeRect();
    const int bw = d_data->borderWidth + d_data->spacing;
    const bool inverted = ( upperBound() < lowerBound() );

    int from, to;

    if ( d_data->orientation == Qt::Horizontal )
    {
        from = tRect.left();
        to = tRect.right();

        if ( d_data->rangeFlags & QwtInterval::ExcludeMinimum )
        {
            if ( inverted )
                to++;
            else
                from--;
        }
        if ( d_data->rangeFlags & QwtInterval::ExcludeMaximum )
        {
            if ( inverted )
                from--;
            else
                to++;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        if ( d_data->scalePosition == QwtThermo::TrailingScale )
Bryant's avatar
Bryant committed
            scaleDraw()->setAlignment( QwtScaleDraw::TopScale );
            scaleDraw()->move( from, tRect.top() - bw );
        }
        else
        {
            scaleDraw()->setAlignment( QwtScaleDraw::BottomScale );
            scaleDraw()->move( from, tRect.bottom() + bw );
Bryant's avatar
Bryant committed

        scaleDraw()->setLength( to - from );
    }
    else // Qt::Vertical
    {
        from = tRect.top();
        to = tRect.bottom();

        if ( d_data->rangeFlags & QwtInterval::ExcludeMinimum )
        {
            if ( inverted )
                from--;
            else
                to++;
pixhawk's avatar
pixhawk committed
        }
Bryant's avatar
Bryant committed
        if ( d_data->rangeFlags & QwtInterval::ExcludeMaximum )
        {
            if ( inverted )
                to++;
            else
                from--;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        if ( d_data->scalePosition == QwtThermo::LeadingScale )
Bryant's avatar
Bryant committed
            scaleDraw()->setAlignment( QwtScaleDraw::RightScale );
            scaleDraw()->move( tRect.right() + bw, from );
Bryant's avatar
Bryant committed
        else
        {
            scaleDraw()->setAlignment( QwtScaleDraw::LeftScale );
            scaleDraw()->move( tRect.left() - bw, from );
pixhawk's avatar
pixhawk committed
        }
Bryant's avatar
Bryant committed

        scaleDraw()->setLength( to - from );
pixhawk's avatar
pixhawk committed
    }
Bryant's avatar
Bryant committed

    if ( update_geometry )
    {
pixhawk's avatar
pixhawk committed
        updateGeometry();
        update();
    }
}

/*!
Bryant's avatar
Bryant committed
  \return Bounding rectangle of the pipe ( without borders )
          in widget coordinates
*/
QRect QwtThermo::pipeRect() const
{
    int mbd = 0;
    if ( d_data->scalePosition != QwtThermo::NoScale )
    {
        int d1, d2;
        scaleDraw()->getBorderDistHint( font(), d1, d2 );
        mbd = qMax( d1, d2 );
    }
    const int bw = d_data->borderWidth;
    const int scaleOff = bw + mbd;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const QRect cr = contentsRect();
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QRect pipeRect = cr;
    if ( d_data->orientation == Qt::Horizontal )
    {
        pipeRect.adjust( scaleOff, 0, -scaleOff, 0 );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        if ( d_data->scalePosition == QwtThermo::TrailingScale )
            pipeRect.setTop( cr.top() + cr.height() - bw - d_data->pipeWidth );
        else
            pipeRect.setTop( bw );

        pipeRect.setHeight( d_data->pipeWidth );
    }
    else // Qt::Vertical
    {
        pipeRect.adjust( 0, scaleOff, 0, -scaleOff );

        if ( d_data->scalePosition == QwtThermo::LeadingScale )
            pipeRect.setLeft( bw );
        else 
            pipeRect.setLeft( cr.left() + cr.width() - bw - d_data->pipeWidth );

        pipeRect.setWidth( d_data->pipeWidth );
    }

    return pipeRect;
}

/*!
  \brief Set the orientation.
  \param orientation Allowed values are Qt::Horizontal and Qt::Vertical.

  \sa orientation(), scalePosition()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtThermo::setOrientation( Qt::Orientation orientation )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( orientation == d_data->orientation )
pixhawk's avatar
pixhawk committed
        return;

Bryant's avatar
Bryant committed
    d_data->orientation = orientation;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) )
    {
        QSizePolicy sp = sizePolicy();
        sp.transpose();
        setSizePolicy( sp );

        setAttribute( Qt::WA_WState_OwnSizePolicy, false );
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    layoutThermo( true );
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \return Orientation
  \sa setOrientation()
*/
Qt::Orientation QwtThermo::orientation() const
{
    return d_data->orientation;
}
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
/*!
  \brief Change how the origin is determined.
  \sa originMode(), serOrigin(), origin()
 */
void QwtThermo::setOriginMode( OriginMode m )
{
    if ( m == d_data->originMode )
        return;
Bryant's avatar
Bryant committed
    d_data->originMode = m;
    update();
}
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
/*!
  \return Mode, how the origin is determined.
  \sa setOriginMode(), serOrigin(), origin()
 */
QwtThermo::OriginMode QwtThermo::originMode() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_data->originMode;
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
  \brief Specifies the custom origin.

  If originMode is set to OriginCustom this property controls where the
  liquid starts.

  \param origin New origin level
  \sa setOriginMode(), originMode(), origin()
 */
void QwtThermo::setOrigin( double origin )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( origin == d_data->origin )
        return;

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

/*!
  \return Origin of the thermo, when OriginCustom is enabled
  \sa setOrigin(), setOriginMode(), originMode()
 */
double QwtThermo::origin() const
{
    return d_data->origin;
}

/*!
  \brief Change the position of the scale
  \param scalePosition Position of the scale.

  \sa ScalePosition, scalePosition()
*/
void QwtThermo::setScalePosition( ScalePosition scalePosition )
{
    if ( d_data->scalePosition == scalePosition )
        return;

    d_data->scalePosition = scalePosition;

    if ( testAttribute( Qt::WA_WState_Polished ) )
        layoutThermo( true );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
   \return Scale position.
   \sa setScalePosition()
*/
QwtThermo::ScalePosition QwtThermo::scalePosition() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_data->scalePosition;
pixhawk's avatar
pixhawk committed
}

//! Notify a scale change.
void QwtThermo::scaleChange()
{
Bryant's avatar
Bryant committed
    layoutThermo( true );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
   Redraw the liquid in thermometer pipe.
   \param painter Painter
   \param pipeRect Bounding rectangle of the pipe without borders
*/
void QwtThermo::drawLiquid( 
    QPainter *painter, const QRect &pipeRect ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    painter->save();
    painter->setClipRect( pipeRect, Qt::IntersectClip );
    painter->setPen( Qt::NoPen );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const QwtScaleMap scaleMap = scaleDraw()->scaleMap();
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QRect liquidRect = fillRect( pipeRect );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( d_data->colorMap != NULL )
    {
        const QwtInterval interval = scaleDiv().interval().normalized();
Bryant's avatar
Bryant committed
        // Because the positions of the ticks are rounded
        // we calculate the colors for the rounded tick values
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        QVector<double> values = qwtTickList( scaleDraw()->scaleDiv() );
Bryant's avatar
Bryant committed
        if ( scaleMap.isInverting() )
            qSort( values.begin(), values.end(), qGreater<double>() );
        else
            qSort( values.begin(), values.end(), qLess<double>() );

        int from;
        if ( !values.isEmpty() )
        {
            from = qRound( scaleMap.transform( values[0] ) );
            qwtDrawLine( painter, from,
                d_data->colorMap->color( interval, values[0] ),
                pipeRect, liquidRect, d_data->orientation );
pixhawk's avatar
pixhawk committed
        }

Bryant's avatar
Bryant committed
        for ( int i = 1; i < values.size(); i++ )
        {
            const int to = qRound( scaleMap.transform( values[i] ) );

            for ( int pos = from + 1; pos < to; pos++ )
            {
                const double v = scaleMap.invTransform( pos );

                qwtDrawLine( painter, pos, 
                    d_data->colorMap->color( interval, v ),
                    pipeRect, liquidRect, d_data->orientation );
pixhawk's avatar
pixhawk committed
            }
Bryant's avatar
Bryant committed

            qwtDrawLine( painter, to,
                d_data->colorMap->color( interval, values[i] ),
                pipeRect, liquidRect, d_data->orientation );

            from = to;
        }
    }
    else
    {
        if ( !liquidRect.isEmpty() && d_data->alarmEnabled )
        {
            const QRect r = alarmRect( liquidRect );
            if ( !r.isEmpty() )
            {
                painter->fillRect( r, palette().brush( QPalette::Highlight ) );
                liquidRect = QRegion( liquidRect ).subtracted( r ).boundingRect();
pixhawk's avatar
pixhawk committed
            }
        }
Bryant's avatar
Bryant committed

        painter->fillRect( liquidRect, palette().brush( QPalette::ButtonText ) );
pixhawk's avatar
pixhawk committed
    }

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

Bryant's avatar
Bryant committed
/*!
  \brief Change the spacing between pipe and scale
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  A spacing of 0 means, that the backbone of the scale is below
  the pipe.

  The default setting is 3 pixels.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \param spacing Number of pixels
  \sa spacing();
*/
void QwtThermo::setSpacing( int spacing )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( spacing <= 0 )
        spacing = 0;

    if ( spacing != d_data->spacing  )
    {
        d_data->spacing = spacing;
        layoutThermo( true );
pixhawk's avatar
pixhawk committed
    }
}

Bryant's avatar
Bryant committed
/*!
  \return Number of pixels between pipe and scale
  \sa setSpacing()
*/
int QwtThermo::spacing() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_data->spacing;
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
   Set the border width of the pipe.
   \param width Border width
   \sa borderWidth()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtThermo::setBorderWidth( int width )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( width <= 0 )
        width = 0;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( width != d_data->borderWidth  )
    {
        d_data->borderWidth = width;
        layoutThermo( true );
    }
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
   \return Border width of the thermometer pipe.
   \sa setBorderWidth()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
int QwtThermo::borderWidth() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_data->borderWidth;
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
  \brief Assign a color map for the fill color

  \param colorMap Color map
  \warning The alarm threshold has no effect, when
           a color map has been assigned
*/
void QwtThermo::setColorMap( QwtColorMap *colorMap )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( colorMap != d_data->colorMap )
    {
        delete d_data->colorMap;
        d_data->colorMap = colorMap;
    }
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \return Color map for the fill color
  \warning The alarm threshold has no effect, when
           a color map has been assigned
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
QwtColorMap *QwtThermo::colorMap()
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_data->colorMap;
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
  \return Color map for the fill color
  \warning The alarm threshold has no effect, when
           a color map has been assigned
*/
const QwtColorMap *QwtThermo::colorMap() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_data->colorMap;
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \brief Change the brush of the liquid.
 
  Changes the QPalette::ButtonText brush of the palette.

  \param brush New brush. 
  \sa fillBrush(), QWidget::setPalette()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtThermo::setFillBrush( const QBrush& brush )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QPalette pal = palette();
    pal.setBrush( QPalette::ButtonText, brush );
    setPalette( pal );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
  \return Liquid ( QPalette::ButtonText ) brush. 
  \sa setFillBrush(), QWidget::palette()
*/
QBrush QwtThermo::fillBrush() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return palette().brush( QPalette::ButtonText );
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \brief Specify the liquid brush above the alarm threshold

  Changes the QPalette::Highlight brush of the palette.

  \param brush New brush. 
  \sa alarmBrush(), QWidget::setPalette()

  \warning The alarm threshold has no effect, when
           a color map has been assigned
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtThermo::setAlarmBrush( const QBrush& brush )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QPalette pal = palette();
    pal.setBrush( QPalette::Highlight, brush );
    setPalette( pal );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
  \return Liquid brush ( QPalette::Highlight ) above the alarm threshold.
  \sa setAlarmBrush(), QWidget::palette()

  \warning The alarm threshold has no effect, when
           a color map has been assigned
*/
QBrush QwtThermo::alarmBrush() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return palette().brush( QPalette::Highlight );
pixhawk's avatar
pixhawk committed
}

Bryant's avatar
Bryant committed
/*!
  Specify the alarm threshold.

  \param level Alarm threshold
  \sa alarmLevel()

  \warning The alarm threshold has no effect, when
           a color map has been assigned
*/
void QwtThermo::setAlarmLevel( double level )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    d_data->alarmLevel = level;
pixhawk's avatar
pixhawk committed
    d_data->alarmEnabled = 1;
    update();
}

Bryant's avatar
Bryant committed
/*!
  \return Alarm threshold.
  \sa setAlarmLevel()

  \warning The alarm threshold has no effect, when
           a color map has been assigned
*/
pixhawk's avatar
pixhawk committed
double QwtThermo::alarmLevel() const
{
    return d_data->alarmLevel;
}

Bryant's avatar
Bryant committed
/*!
  Change the width of the pipe.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \param width Width of the pipe
  \sa pipeWidth()
*/
void QwtThermo::setPipeWidth( int width )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( width > 0 )
    {
        d_data->pipeWidth = width;
        layoutThermo( true );
    }
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \return Width of the pipe.
  \sa setPipeWidth()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
int QwtThermo::pipeWidth() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_data->pipeWidth;
pixhawk's avatar
pixhawk committed
}

/*!
  \brief Enable or disable the alarm threshold
Bryant's avatar
Bryant committed
  \param on true (disabled) or false (enabled)

  \warning The alarm threshold has no effect, when
           a color map has been assigned
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtThermo::setAlarmEnabled( bool on )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    d_data->alarmEnabled = on;
pixhawk's avatar
pixhawk committed
    update();
}

Bryant's avatar
Bryant committed
/*! 
  \return True, when the alarm threshold is enabled.

  \warning The alarm threshold has no effect, when
           a color map has been assigned
*/
pixhawk's avatar
pixhawk committed
bool QwtThermo::alarmEnabled() const
{
    return d_data->alarmEnabled;
}

/*!
  \return the minimum size hint
Bryant's avatar
Bryant committed
  \sa minimumSizeHint()
pixhawk's avatar
pixhawk committed
*/
QSize QwtThermo::sizeHint() const
{
    return minimumSizeHint();
}

/*!
Bryant's avatar
Bryant committed
  \return Minimum size hint
pixhawk's avatar
pixhawk committed
  \warning The return value depends on the font and the scale.
Bryant's avatar
Bryant committed
  \sa sizeHint()
pixhawk's avatar
pixhawk committed
*/
QSize QwtThermo::minimumSizeHint() const
{
    int w = 0, h = 0;

Bryant's avatar
Bryant committed
    if ( d_data->scalePosition != NoScale )
    {
        const int sdExtent = qCeil( scaleDraw()->extent( font() ) );
        const int sdLength = scaleDraw()->minLength( font() );
pixhawk's avatar
pixhawk committed

        w = sdLength;
Bryant's avatar
Bryant committed
        h = d_data->pipeWidth + sdExtent + d_data->spacing;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    }
    else // no scale
    {
pixhawk's avatar
pixhawk committed
        w = 200;
Bryant's avatar
Bryant committed
        h = d_data->pipeWidth;
pixhawk's avatar
pixhawk committed
    }

    if ( d_data->orientation == Qt::Vertical )
Bryant's avatar
Bryant committed
        qSwap( w, h );
pixhawk's avatar
pixhawk committed

    w += 2 * d_data->borderWidth;
    h += 2 * d_data->borderWidth;

Bryant's avatar
Bryant committed
    // finally add the margins
    int left, right, top, bottom;
    getContentsMargins( &left, &top, &right, &bottom );
    w += left + right;
    h += top + bottom;

pixhawk's avatar
pixhawk committed
    return QSize( w, h );
}

Bryant's avatar
Bryant committed
/*!
  \brief Calculate the filled rectangle of the pipe

  \param pipeRect Rectangle of the pipe
  \return Rectangle to be filled ( fill and alarm brush )

  \sa pipeRect(), alarmRect()
 */
QRect QwtThermo::fillRect( const QRect &pipeRect ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    double origin;        
    if ( d_data->originMode == OriginMinimum )
    {
        origin = qMin( lowerBound(), upperBound() );
    }
    else if ( d_data->originMode == OriginMaximum )
    {
        origin = qMax( lowerBound(), upperBound() );
    }
    else // OriginCustom
    {
        origin = d_data->origin;
    }

    const QwtScaleMap scaleMap = scaleDraw()->scaleMap();

    int from = qRound( scaleMap.transform( d_data->value ) );
    int to = qRound( scaleMap.transform( origin ) );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( to < from )
        qSwap( from, to );
    
    QRect fillRect = pipeRect;
    if ( d_data->orientation == Qt::Horizontal )
    {
        fillRect.setLeft( from );
        fillRect.setRight( to );
    }
    else // Qt::Vertical
    {
        fillRect.setTop( from );
        fillRect.setBottom( to );
    }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    return fillRect.normalized();
pixhawk's avatar
pixhawk committed
}
Bryant's avatar
Bryant committed

/*!
  \brief Calculate the alarm rectangle of the pipe

  \param fillRect Filled rectangle in the pipe
  \return Rectangle to be filled with the alarm brush

  \sa pipeRect(), fillRect(), alarmLevel(), alarmBrush()
 */
QRect QwtThermo::alarmRect( const QRect &fillRect ) const
{
    QRect alarmRect( 0, 0, -1, -1); // something invalid

    if ( !d_data->alarmEnabled )
        return alarmRect;

    const bool inverted = ( upperBound() < lowerBound() );
    
    bool increasing;
    if ( d_data->originMode == OriginCustom )
    {
        increasing = d_data->value > d_data->origin;
    }
    else
    {
        increasing = d_data->originMode == OriginMinimum;
    }

    const QwtScaleMap map = scaleDraw()->scaleMap();
    const int alarmPos = qRound( map.transform( d_data->alarmLevel ) );
    const int valuePos = qRound( map.transform( d_data->value ) );
    
    if ( d_data->orientation == Qt::Horizontal )
    {
        int v1, v2;
        if ( inverted )
        {
            v1 = fillRect.left();

            v2 = alarmPos - 1;
            v2 = qMin( v2, increasing ? fillRect.right() : valuePos );
        }
        else
        {
            v1 = alarmPos + 1;
            v1 = qMax( v1, increasing ? fillRect.left() : valuePos );

            v2 = fillRect.right();

        }
        alarmRect.setRect( v1, fillRect.top(), v2 - v1 + 1, fillRect.height() );
    }
    else
    {
        int v1, v2;
        if ( inverted )
        {
            v1 = alarmPos + 1;
            v1 = qMax( v1, increasing ? fillRect.top() : valuePos );

            v2 = fillRect.bottom();
        }
        else
        {
            v1 = fillRect.top();

            v2 = alarmPos - 1;
            v2 = qMin( v2, increasing ? fillRect.bottom() : valuePos );
        }
        alarmRect.setRect( fillRect.left(), v1, fillRect.width(), v2 - v1 + 1 );