Skip to content
Snippets Groups Projects
qwt_thermo.cpp 22.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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 );