Skip to content
Snippets Groups Projects
qwt_scale_draw.cpp 22.1 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
    
    pixhawk's avatar
    pixhawk committed
     * 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_scale_draw.h"
    
    pixhawk's avatar
    pixhawk committed
    #include "qwt_scale_div.h"
    #include "qwt_scale_map.h"
    
    Bryant's avatar
    Bryant committed
    #include "qwt_math.h"
    #include "qwt_painter.h"
    #include <qpen.h>
    #include <qpainter.h>
    #include <qmath.h>
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
    #if QT_VERSION < 0x040601
    #define qFastSin(x) qSin(x)
    #define qFastCos(x) qCos(x)
    
    pixhawk's avatar
    pixhawk committed
    #endif
    
    class QwtScaleDraw::PrivateData
    {
    public:
        PrivateData():
    
    Bryant's avatar
    Bryant committed
            len( 0 ),
            alignment( QwtScaleDraw::BottomScale ),
            labelAlignment( 0 ),
            labelRotation( 0.0 )
        {
    
    pixhawk's avatar
    pixhawk committed
        }
    
    
    Bryant's avatar
    Bryant committed
        QPointF pos;
        double len;
    
    pixhawk's avatar
    pixhawk committed
    
        Alignment alignment;
    
        Qt::Alignment labelAlignment;
        double labelRotation;
    };
    
    /*!
      \brief Constructor
    
      The range of the scale is initialized to [0, 100],
      The position is at (0, 0) with a length of 100.
      The orientation is QwtAbstractScaleDraw::Bottom.
    */
    QwtScaleDraw::QwtScaleDraw()
    {
        d_data = new QwtScaleDraw::PrivateData;
    
    Bryant's avatar
    Bryant committed
        setLength( 100 );
    
    pixhawk's avatar
    pixhawk committed
    }
    
    //! Destructor
    QwtScaleDraw::~QwtScaleDraw()
    {
        delete d_data;
    }
    
    
    pixhawk's avatar
    pixhawk committed
       Return alignment of the scale
       \sa setAlignment()
    
    Bryant's avatar
    Bryant committed
       \return Alignment of the scale
    
    pixhawk's avatar
    pixhawk committed
    */
    
    QwtScaleDraw::Alignment QwtScaleDraw::alignment() const
    
    pixhawk's avatar
    pixhawk committed
    {
    
        return d_data->alignment;
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /*!
       Set the alignment of the scale
    
    
    Bryant's avatar
    Bryant committed
       \param align Alignment of the scale
    
    
    pixhawk's avatar
    pixhawk committed
       The default alignment is QwtScaleDraw::BottomScale
       \sa alignment()
    */
    
    Bryant's avatar
    Bryant committed
    void QwtScaleDraw::setAlignment( Alignment align )
    
    pixhawk's avatar
    pixhawk committed
    {
        d_data->alignment = align;
    }
    
    /*!
      Return the orientation
    
      TopScale, BottomScale are horizontal (Qt::Horizontal) scales,
      LeftScale, RightScale are vertical (Qt::Vertical) scales.
    
    
    Bryant's avatar
    Bryant committed
      \return Orientation of the scale
    
    
    pixhawk's avatar
    pixhawk committed
      \sa alignment()
    */
    Qt::Orientation QwtScaleDraw::orientation() const
    {
    
    Bryant's avatar
    Bryant committed
        switch ( d_data->alignment )
        {
            case TopScale:
            case BottomScale:
                return Qt::Horizontal;
            case LeftScale:
            case RightScale:
            default:
                return Qt::Vertical;
    
    pixhawk's avatar
    pixhawk committed
        }
    }
    
    /*!
      \brief Determine the minimum border distance
    
      This member function returns the minimum space
      needed to draw the mark labels at the scale's endpoints.
    
      \param font Font
      \param start Start border distance
      \param end End border distance
    */
    
    Bryant's avatar
    Bryant committed
    void QwtScaleDraw::getBorderDistHint( 
        const QFont &font, int &start, int &end ) const
    
    pixhawk's avatar
    pixhawk committed
    {
        start = 0;
        end = 0;
    
    Bryant's avatar
    Bryant committed
        if ( !hasComponent( QwtAbstractScaleDraw::Labels ) )
    
    pixhawk's avatar
    pixhawk committed
            return;
    
    
    Bryant's avatar
    Bryant committed
        const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
    
        if ( ticks.count() == 0 )
    
    pixhawk's avatar
    pixhawk committed
            return;
    
    
    Bryant's avatar
    Bryant committed
        // Find the ticks, that are mapped to the borders.
        // minTick is the tick, that is mapped to the top/left-most position
        // in widget coordinates.
    
        double minTick = ticks[0];
        double minPos = scaleMap().transform( minTick );
        double maxTick = minTick;
        double maxPos = minPos;
    
        for ( int i = 1; i < ticks.count(); i++ )
        {
            const double tickPos = scaleMap().transform( ticks[i] );
            if ( tickPos < minPos )
            {
                minTick = ticks[i];
                minPos = tickPos;
            }
            if ( tickPos > scaleMap().transform( maxTick ) )
            {
                maxTick = ticks[i];
                maxPos = tickPos;
            }
        }
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        double e = 0.0;
        double s = 0.0;
    
    pixhawk's avatar
    pixhawk committed
        if ( orientation() == Qt::Vertical )
    
    Bryant's avatar
    Bryant committed
        {
            s = -labelRect( font, minTick ).top();
            s -= qAbs( minPos - qRound( scaleMap().p2() ) );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
            e = labelRect( font, maxTick ).bottom();
            e -= qAbs( maxPos - scaleMap().p1() );
        }
    
    pixhawk's avatar
    pixhawk committed
        else
    
    Bryant's avatar
    Bryant committed
        {
            s = -labelRect( font, minTick ).left();
            s -= qAbs( minPos - scaleMap().p1() );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
            e = labelRect( font, maxTick ).right();
            e -= qAbs( maxPos - scaleMap().p2() );
        }
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        if ( s < 0.0 )
            s = 0.0;
        if ( e < 0.0 )
            e = 0.0;
    
        start = qCeil( s );
        end = qCeil( e );
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /*!
      Determine the minimum distance between two labels, that is necessary
      that the texts don't overlap.
    
      \param font Font
      \return The maximum width of a label
    
      \sa getBorderDistHint()
    */
    
    
    Bryant's avatar
    Bryant committed
    int QwtScaleDraw::minLabelDist( const QFont &font ) const
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
        if ( !hasComponent( QwtAbstractScaleDraw::Labels ) )
    
    pixhawk's avatar
    pixhawk committed
            return 0;
    
    
    Bryant's avatar
    Bryant committed
        const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
        if ( ticks.isEmpty() )
    
    pixhawk's avatar
    pixhawk committed
            return 0;
    
    
    Bryant's avatar
    Bryant committed
        const QFontMetrics fm( font );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        const bool vertical = ( orientation() == Qt::Vertical );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        QRectF bRect1;
        QRectF bRect2 = labelRect( font, ticks[0] );
        if ( vertical )
        {
            bRect2.setRect( -bRect2.bottom(), 0.0, bRect2.height(), bRect2.width() );
    
    pixhawk's avatar
    pixhawk committed
        }
    
    
    Bryant's avatar
    Bryant committed
        double maxDist = 0.0;
    
        for ( int i = 1; i < ticks.count(); i++ )
        {
    
    pixhawk's avatar
    pixhawk committed
            bRect1 = bRect2;
    
    Bryant's avatar
    Bryant committed
            bRect2 = labelRect( font, ticks[i] );
            if ( vertical )
            {
                bRect2.setRect( -bRect2.bottom(), 0.0,
                    bRect2.height(), bRect2.width() );
    
    pixhawk's avatar
    pixhawk committed
            }
    
    
    Bryant's avatar
    Bryant committed
            double dist = fm.leading(); // space between the labels
    
    pixhawk's avatar
    pixhawk committed
            if ( bRect1.right() > 0 )
                dist += bRect1.right();
            if ( bRect2.left() < 0 )
                dist += -bRect2.left();
    
            if ( dist > maxDist )
                maxDist = dist;
        }
    
    
    Bryant's avatar
    Bryant committed
        double angle = qwtRadians( labelRotation() ); 
    
    pixhawk's avatar
    pixhawk committed
        if ( vertical )
            angle += M_PI / 2;
    
    
    Bryant's avatar
    Bryant committed
        const double sinA = qFastSin( angle ); // qreal -> double
        if ( qFuzzyCompare( sinA + 1.0, 1.0 ) )
            return qCeil( maxDist );
    
    pixhawk's avatar
    pixhawk committed
    
    
        const int fmHeight = fm.ascent() - 2;
    
    pixhawk's avatar
    pixhawk committed
    
        // The distance we need until there is
        // the height of the label font. This height is needed
    
    Bryant's avatar
    Bryant committed
        // for the neighbored label.
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        double labelDist = fmHeight / qFastSin( angle ) * qFastCos( angle );
    
    pixhawk's avatar
    pixhawk committed
        if ( labelDist < 0 )
            labelDist = -labelDist;
    
    
        // For text orientations close to the scale orientation
    
    pixhawk's avatar
    pixhawk committed
    
        if ( labelDist > maxDist )
            labelDist = maxDist;
    
    
        // For text orientations close to the opposite of the
    
    pixhawk's avatar
    pixhawk committed
        // scale orientation
    
        if ( labelDist < fmHeight )
            labelDist = fmHeight;
    
    
    Bryant's avatar
    Bryant committed
        return qCeil( labelDist );
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /*!
       Calculate the width/height that is needed for a
       vertical/horizontal scale.
    
       The extent is calculated from the pen width of the backbone,
       the major tick length, the spacing and the maximum width/height
       of the labels.
    
       \param font Font used for painting the labels
    
    Bryant's avatar
    Bryant committed
       \return Extent
    
    pixhawk's avatar
    pixhawk committed
    
       \sa minLength()
    */
    
    Bryant's avatar
    Bryant committed
    double QwtScaleDraw::extent( const QFont &font ) const
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
        double d = 0;
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
        {
    
    pixhawk's avatar
    pixhawk committed
            if ( orientation() == Qt::Vertical )
    
    Bryant's avatar
    Bryant committed
                d = maxLabelWidth( font );
    
    pixhawk's avatar
    pixhawk committed
            else
    
    Bryant's avatar
    Bryant committed
                d = maxLabelHeight( font );
    
    pixhawk's avatar
    pixhawk committed
    
            if ( d > 0 )
                d += spacing();
        }
    
    
    Bryant's avatar
    Bryant committed
        if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
        {
            d += maxTickLength();
    
    pixhawk's avatar
    pixhawk committed
        }
    
    
    Bryant's avatar
    Bryant committed
        if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
        {
            const double pw = qMax( 1, penWidth() );  // pen width can be zero
    
    pixhawk's avatar
    pixhawk committed
            d += pw;
        }
    
    
    Bryant's avatar
    Bryant committed
        d = qMax( d, minimumExtent() );
    
    pixhawk's avatar
    pixhawk committed
        return d;
    }
    
    /*!
       Calculate the minimum length that is needed to draw the scale
    
       \param font Font used for painting the labels
    
    Bryant's avatar
    Bryant committed
       \return Minimum length that is needed to draw the scale
    
    pixhawk's avatar
    pixhawk committed
    
       \sa extent()
    */
    
    Bryant's avatar
    Bryant committed
    int QwtScaleDraw::minLength( const QFont &font ) const
    
    pixhawk's avatar
    pixhawk committed
    {
        int startDist, endDist;
    
    Bryant's avatar
    Bryant committed
        getBorderDistHint( font, startDist, endDist );
    
    pixhawk's avatar
    pixhawk committed
    
        const QwtScaleDiv &sd = scaleDiv();
    
        const uint minorCount =
    
    Bryant's avatar
    Bryant committed
            sd.ticks( QwtScaleDiv::MinorTick ).count() +
            sd.ticks( QwtScaleDiv::MediumTick ).count();
    
    pixhawk's avatar
    pixhawk committed
        const uint majorCount =
    
    Bryant's avatar
    Bryant committed
            sd.ticks( QwtScaleDiv::MajorTick ).count();
    
    pixhawk's avatar
    pixhawk committed
    
        int lengthForLabels = 0;
    
    Bryant's avatar
    Bryant committed
        if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
            lengthForLabels = minLabelDist( font ) * majorCount;
    
    pixhawk's avatar
    pixhawk committed
    
        int lengthForTicks = 0;
    
    Bryant's avatar
    Bryant committed
        if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
        {
            const double pw = qMax( 1, penWidth() );  // penwidth can be zero
            lengthForTicks = qCeil( ( majorCount + minorCount ) * ( pw + 1.0 ) );
    
    pixhawk's avatar
    pixhawk committed
        }
    
    
    Bryant's avatar
    Bryant committed
        return startDist + endDist + qMax( lengthForLabels, lengthForTicks );
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /*!
       Find the position, where to paint a label
    
    
    Bryant's avatar
    Bryant committed
       The position has a distance that depends on the length of the ticks 
       in direction of the alignment().
    
    pixhawk's avatar
    pixhawk committed
    
       \param value Value
    
    Bryant's avatar
    Bryant committed
       \return Position, where to paint a label
    
    pixhawk's avatar
    pixhawk committed
    */
    
    Bryant's avatar
    Bryant committed
    QPointF QwtScaleDraw::labelPosition( double value ) const
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
        const double tval = scaleMap().transform( value );
        double dist = spacing();
        if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
            dist += qMax( 1, penWidth() );
    
        if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
            dist += tickLength( QwtScaleDiv::MajorTick );
    
        double px = 0;
        double py = 0;
    
        switch ( alignment() )
        {
            case RightScale:
            {
                px = d_data->pos.x() + dist;
                py = tval;
                break;
            }
            case LeftScale:
            {
                px = d_data->pos.x() - dist;
                py = tval;
                break;
            }
            case BottomScale:
            {
                px = tval;
                py = d_data->pos.y() + dist;
                break;
            }
            case TopScale:
            {
                px = tval;
                py = d_data->pos.y() - dist;
                break;
            }
    
    pixhawk's avatar
    pixhawk committed
        }
    
    
    Bryant's avatar
    Bryant committed
        return QPointF( px, py );
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /*!
       Draw a tick
    
       \param painter Painter
       \param value Value of the tick
    
    Bryant's avatar
    Bryant committed
       \param len Length of the tick
    
    pixhawk's avatar
    pixhawk committed
    
       \sa drawBackbone(), drawLabel()
    */
    
    Bryant's avatar
    Bryant committed
    void QwtScaleDraw::drawTick( QPainter *painter, double value, double len ) const
    
    pixhawk's avatar
    pixhawk committed
    {
        if ( len <= 0 )
            return;
    
    
    Bryant's avatar
    Bryant committed
        const bool roundingAlignment = QwtPainter::roundingAlignment( painter );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        QPointF pos = d_data->pos;
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        double tval = scaleMap().transform( value );
        if ( roundingAlignment )
            tval = qRound( tval );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        const int pw = penWidth();
        int a = 0;
        if ( pw > 1 && roundingAlignment )
            a = 1;
    
    Bryant's avatar
    Bryant committed
        switch ( alignment() )
        {
            case LeftScale:
            {
                double x1 = pos.x() + a;
                double x2 = pos.x() + a - pw - len;
                if ( roundingAlignment )
                {
                    x1 = qRound( x1 );
                    x2 = qRound( x2 );
                }
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
                QwtPainter::drawLine( painter, x1, tval, x2, tval );
                break;
            }
    
            case RightScale:
            {
                double x1 = pos.x();
                double x2 = pos.x() + pw + len;
                if ( roundingAlignment )
                {
                    x1 = qRound( x1 );
                    x2 = qRound( x2 );
                }
    
                QwtPainter::drawLine( painter, x1, tval, x2, tval );
                break;
            }
    
            case BottomScale:
            {
                double y1 = pos.y();
                double y2 = pos.y() + pw + len;
                if ( roundingAlignment )
                {
                    y1 = qRound( y1 );
                    y2 = qRound( y2 );
                }
    
                QwtPainter::drawLine( painter, tval, y1, tval, y2 );
                break;
            }
    
            case TopScale:
            {
                double y1 = pos.y() + a;
                double y2 = pos.y() - pw - len + a;
                if ( roundingAlignment )
                {
                    y1 = qRound( y1 );
                    y2 = qRound( y2 );
                }
    
                QwtPainter::drawLine( painter, tval, y1, tval, y2 );
                break;
            }
    
    pixhawk's avatar
    pixhawk committed
        }
    }
    
    
    pixhawk's avatar
    pixhawk committed
       Draws the baseline of the scale
       \param painter Painter
    
       \sa drawTick(), drawLabel()
    */
    
    Bryant's avatar
    Bryant committed
    void QwtScaleDraw::drawBackbone( QPainter *painter ) const
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
        const bool doAlign = QwtPainter::roundingAlignment( painter );
    
        const QPointF &pos = d_data->pos;
        const double len = d_data->len;
        const int pw = qMax( penWidth(), 1 );
    
        // pos indicates a border not the center of the backbone line
        // so we need to shift its position depending on the pen width
        // and the alignment of the scale
    
        double off;
        if ( doAlign )
        {
            if ( alignment() == LeftScale || alignment() == TopScale )
                off = ( pw - 1 ) / 2;
            else
                off = pw / 2;
        }
        else
        {
            off = 0.5 * penWidth();
        }
    
        switch ( alignment() )
        {
            case LeftScale:
            {
                double x = pos.x() - off;
                if ( doAlign )
                    x = qRound( x );
    
                QwtPainter::drawLine( painter, x, pos.y(), x, pos.y() + len );
                break;
            }
            case RightScale:
            {
                double x = pos.x() + off;
                if ( doAlign )
                    x = qRound( x );
    
                QwtPainter::drawLine( painter, x, pos.y(), x, pos.y() + len );
                break;
            }
            case TopScale:
            {
                double y = pos.y() - off;
                if ( doAlign )
                    y = qRound( y );
    
                QwtPainter::drawLine( painter, pos.x(), y, pos.x() + len, y );
                break;
            }
            case BottomScale:
            {
                double y = pos.y() + off;
                if ( doAlign )
                    y = qRound( y );
    
                QwtPainter::drawLine( painter, pos.x(), y, pos.x() + len, y );
                break;
            }
    
    pixhawk's avatar
    pixhawk committed
        }
    }
    
    /*!
      \brief Move the position of the scale
    
      The meaning of the parameter pos depends on the alignment:
      <dl>
      <dt>QwtScaleDraw::LeftScale
      <dd>The origin is the topmost point of the
    
          backbone. The backbone is a vertical line.
          Scale marks and labels are drawn
    
    pixhawk's avatar
    pixhawk committed
          at the left of the backbone.
      <dt>QwtScaleDraw::RightScale
      <dd>The origin is the topmost point of the
    
          backbone. The backbone is a vertical line.
    
    pixhawk's avatar
    pixhawk committed
          Scale marks and labels are drawn
          at the right of the backbone.
      <dt>QwtScaleDraw::TopScale
      <dd>The origin is the leftmost point of the
    
          backbone. The backbone is a horizontal line.
    
    pixhawk's avatar
    pixhawk committed
          Scale marks and labels are drawn
          above the backbone.
      <dt>QwtScaleDraw::BottomScale
      <dd>The origin is the leftmost point of the
    
          backbone. The backbone is a horizontal line
    
    pixhawk's avatar
    pixhawk committed
          Scale marks and labels are drawn
          below the backbone.
      </dl>
    
      \param pos Origin of the scale
    
      \sa pos(), setLength()
    */
    
    Bryant's avatar
    Bryant committed
    void QwtScaleDraw::move( const QPointF &pos )
    
    pixhawk's avatar
    pixhawk committed
    {
        d_data->pos = pos;
        updateMap();
    }
    
    
    pixhawk's avatar
    pixhawk committed
       \return Origin of the scale
       \sa move(), length()
    */
    
    Bryant's avatar
    Bryant committed
    QPointF QwtScaleDraw::pos() const
    
    pixhawk's avatar
    pixhawk committed
    {
        return d_data->pos;
    }
    
    /*!
      Set the length of the backbone.
    
    pixhawk's avatar
    pixhawk committed
      The length doesn't include the space needed for
      overlapping labels.
    
    
    Bryant's avatar
    Bryant committed
      \param length Length of the backbone
    
    
    pixhawk's avatar
    pixhawk committed
      \sa move(), minLabelDist()
    */
    
    Bryant's avatar
    Bryant committed
    void QwtScaleDraw::setLength( double length )
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
    #if 1
    
    pixhawk's avatar
    pixhawk committed
        if ( length >= 0 && length < 10 )
            length = 10;
    
    Bryant's avatar
    Bryant committed
    
        // why should we accept negative lengths ???
    
    pixhawk's avatar
    pixhawk committed
        if ( length < 0 && length > -10 )
            length = -10;
    
    Bryant's avatar
    Bryant committed
    #else
        length = qMax( length, 10 );
    #endif
    
    pixhawk's avatar
    pixhawk committed
        d_data->len = length;
        updateMap();
    }
    
    
    pixhawk's avatar
    pixhawk committed
       \return the length of the backbone
       \sa setLength(), pos()
    */
    
    Bryant's avatar
    Bryant committed
    double QwtScaleDraw::length() const
    
    pixhawk's avatar
    pixhawk committed
    {
        return d_data->len;
    }
    
    
    pixhawk's avatar
    pixhawk committed
       Draws the label for a major scale tick
    
       \param painter Painter
       \param value Value
    
       \sa drawTick(), drawBackbone(), boundingLabelRect()
    */
    
    Bryant's avatar
    Bryant committed
    void QwtScaleDraw::drawLabel( QPainter *painter, double value ) const
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
        QwtText lbl = tickLabel( painter->font(), value );
    
    pixhawk's avatar
    pixhawk committed
        if ( lbl.isEmpty() )
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        QPointF pos = labelPosition( value );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        QSizeF labelSize = lbl.textSize( painter->font() );
    
    Bryant's avatar
    Bryant committed
        const QTransform transform = labelTransformation( pos, labelSize );
    
    pixhawk's avatar
    pixhawk committed
    
        painter->save();
    
    Bryant's avatar
    Bryant committed
        painter->setWorldTransform( transform, true );
    
        lbl.draw ( painter, QRect( QPoint( 0, 0 ), labelSize.toSize() ) );
    
    pixhawk's avatar
    pixhawk committed
    
        painter->restore();
    }
    
    /*!
    
    Bryant's avatar
    Bryant committed
      \brief Find the bounding rectangle for the label.
    
      The coordinates of the rectangle are absolute ( calculated from pos() ).
    
    pixhawk's avatar
    pixhawk committed
      in direction of the tick.
    
      \param font Font used for painting
      \param value Value
    
    
    Bryant's avatar
    Bryant committed
      \return Bounding rectangle
    
    pixhawk's avatar
    pixhawk committed
      \sa labelRect()
    */
    
    Bryant's avatar
    Bryant committed
    QRect QwtScaleDraw::boundingLabelRect( const QFont &font, double value ) const
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
        QwtText lbl = tickLabel( font, value );
    
    pixhawk's avatar
    pixhawk committed
        if ( lbl.isEmpty() )
    
            return QRect();
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        const QPointF pos = labelPosition( value );
        QSizeF labelSize = lbl.textSize( font );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        const QTransform transform = labelTransformation( pos, labelSize );
        return transform.mapRect( QRect( QPoint( 0, 0 ), labelSize.toSize() ) );
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /*!
    
    Bryant's avatar
    Bryant committed
       Calculate the transformation that is needed to paint a label
    
    pixhawk's avatar
    pixhawk committed
       depending on its alignment and rotation.
    
       \param pos Position where to paint the label
       \param size Size of the label
    
    
    Bryant's avatar
    Bryant committed
       \return Transformation matrix
    
    pixhawk's avatar
    pixhawk committed
       \sa setLabelAlignment(), setLabelRotation()
    */
    
    Bryant's avatar
    Bryant committed
    QTransform QwtScaleDraw::labelTransformation(
        const QPointF &pos, const QSizeF &size ) const
    
    Bryant's avatar
    Bryant committed
        QTransform transform;
        transform.translate( pos.x(), pos.y() );
        transform.rotate( labelRotation() );
    
    pixhawk's avatar
    pixhawk committed
        int flags = labelAlignment();
    
    Bryant's avatar
    Bryant committed
        if ( flags == 0 )
        {
            switch ( alignment() )
            {
                case RightScale:
                {
                    if ( flags == 0 )
                        flags = Qt::AlignRight | Qt::AlignVCenter;
                    break;
                }
                case LeftScale:
                {
                    if ( flags == 0 )
                        flags = Qt::AlignLeft | Qt::AlignVCenter;
                    break;
                }
                case BottomScale:
                {
                    if ( flags == 0 )
                        flags = Qt::AlignHCenter | Qt::AlignBottom;
                    break;
                }
                case TopScale:
                {
                    if ( flags == 0 )
                        flags = Qt::AlignHCenter | Qt::AlignTop;
                    break;
                }
    
    pixhawk's avatar
    pixhawk committed
            }
        }
    
    
    Bryant's avatar
    Bryant committed
        double x, y;
    
    pixhawk's avatar
    pixhawk committed
        if ( flags & Qt::AlignLeft )
    
    Bryant's avatar
    Bryant committed
            x = -size.width();
    
    pixhawk's avatar
    pixhawk committed
        else if ( flags & Qt::AlignRight )
    
    Bryant's avatar
    Bryant committed
            x = 0.0;
    
    pixhawk's avatar
    pixhawk committed
        else // Qt::AlignHCenter
    
    Bryant's avatar
    Bryant committed
            x = -( 0.5 * size.width() );
    
    pixhawk's avatar
    pixhawk committed
        if ( flags & Qt::AlignTop )
    
    Bryant's avatar
    Bryant committed
            y = -size.height();
    
    pixhawk's avatar
    pixhawk committed
        else if ( flags & Qt::AlignBottom )
    
    Bryant's avatar
    Bryant committed
            y = 0;
    
    pixhawk's avatar
    pixhawk committed
        else // Qt::AlignVCenter
    
    Bryant's avatar
    Bryant committed
            y = -( 0.5 * size.height() );
    
    Bryant's avatar
    Bryant committed
        transform.translate( x, y );
    
    Bryant's avatar
    Bryant committed
        return transform;
    
    pixhawk's avatar
    pixhawk committed
    
    /*!
    
    Bryant's avatar
    Bryant committed
      Find the bounding rectangle for the label. The coordinates of
      the rectangle are relative to spacing + tick length from the backbone
    
    pixhawk's avatar
    pixhawk committed
      in direction of the tick.
    
      \param font Font used for painting
      \param value Value
    
    Bryant's avatar
    Bryant committed
    
       \return Bounding rectangle that is needed to draw a label
    
    pixhawk's avatar
    pixhawk committed
    */
    
    Bryant's avatar
    Bryant committed
    QRectF QwtScaleDraw::labelRect( const QFont &font, double value ) const
    
    Bryant's avatar
    Bryant committed
        QwtText lbl = tickLabel( font, value );
    
    pixhawk's avatar
    pixhawk committed
        if ( lbl.isEmpty() )
    
    Bryant's avatar
    Bryant committed
            return QRectF( 0.0, 0.0, 0.0, 0.0 );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        const QPointF pos = labelPosition( value );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        const QSizeF labelSize = lbl.textSize( font );
        const QTransform transform = labelTransformation( pos, labelSize );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        QRectF br = transform.mapRect( QRectF( QPointF( 0, 0 ), labelSize ) );
        br.translate( -pos.x(), -pos.y() );
    
    pixhawk's avatar
    pixhawk committed
    
        return br;
    }
    
    /*!
       Calculate the size that is needed to draw a label
    
       \param font Label font
       \param value Value
    
    Bryant's avatar
    Bryant committed
    
       \return Size that is needed to draw a label
    
    pixhawk's avatar
    pixhawk committed
    */
    
    Bryant's avatar
    Bryant committed
    QSizeF QwtScaleDraw::labelSize( const QFont &font, double value ) const
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
        return labelRect( font, value ).size();
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /*!
      Rotate all labels.
    
      When changing the rotation, it might be necessary to
      adjust the label flags too. Finding a useful combination is
      often the result of try and error.
    
      \param rotation Angle in degrees. When changing the label rotation,
                      the label flags often needs to be adjusted too.
    
      \sa setLabelAlignment(), labelRotation(), labelAlignment().
    
    */
    
    Bryant's avatar
    Bryant committed
    void QwtScaleDraw::setLabelRotation( double rotation )
    
    pixhawk's avatar
    pixhawk committed
    {
        d_data->labelRotation = rotation;
    }
    
    /*!
      \return the label rotation
      \sa setLabelRotation(), labelAlignment()
    */
    double QwtScaleDraw::labelRotation() const
    {
        return d_data->labelRotation;
    }
    
    /*!
      \brief Change the label flags
    
    
    Bryant's avatar
    Bryant committed
      Labels are aligned to the point tick length + spacing away from the backbone.
    
    pixhawk's avatar
    pixhawk committed
    
      The alignment is relative to the orientation of the label text.
    
      In case of an flags of 0 the label will be aligned
      depending on the orientation of the scale:
    
    
    pixhawk's avatar
    pixhawk committed
          QwtScaleDraw::TopScale: Qt::AlignHCenter | Qt::AlignTop\n
          QwtScaleDraw::BottomScale: Qt::AlignHCenter | Qt::AlignBottom\n
          QwtScaleDraw::LeftScale: Qt::AlignLeft | Qt::AlignVCenter\n
          QwtScaleDraw::RightScale: Qt::AlignRight | Qt::AlignVCenter\n
    
    pixhawk's avatar
    pixhawk committed
      Changing the alignment is often necessary for rotated labels.
    
    Bryant's avatar
    Bryant committed
      \param alignment Or'd Qt::AlignmentFlags see <qnamespace.h>
    
    pixhawk's avatar
    pixhawk committed
    
      \sa setLabelRotation(), labelRotation(), labelAlignment()
    
      \warning The various alignments might be confusing.
    
    pixhawk's avatar
    pixhawk committed
               The alignment of the label is not the alignment
               of the scale and is not the alignment of the flags
    
    Bryant's avatar
    Bryant committed
               ( QwtText::flags() ) returned from QwtAbstractScaleDraw::label().
    
    Bryant's avatar
    Bryant committed
    void QwtScaleDraw::setLabelAlignment( Qt::Alignment alignment )
    
    pixhawk's avatar
    pixhawk committed
    {
        d_data->labelAlignment = alignment;
    
    pixhawk's avatar
    pixhawk committed
    
    /*!
      \return the label flags
      \sa setLabelAlignment(), labelRotation()
    */
    Qt::Alignment QwtScaleDraw::labelAlignment() const
    {
        return d_data->labelAlignment;
    }
    
    /*!
      \param font Font
      \return the maximum width of a label
    */
    
    Bryant's avatar
    Bryant committed
    int QwtScaleDraw::maxLabelWidth( const QFont &font ) const
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
        double maxWidth = 0.0;
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
        for ( int i = 0; i < ticks.count(); i++ )
        {
    
    pixhawk's avatar
    pixhawk committed
            const double v = ticks[i];
    
    Bryant's avatar
    Bryant committed
            if ( scaleDiv().contains( v ) )
            {
                const double w = labelSize( font, ticks[i] ).width();
    
    pixhawk's avatar
    pixhawk committed
                if ( w > maxWidth )
                    maxWidth = w;
            }
        }
    
    
    Bryant's avatar
    Bryant committed
        return qCeil( maxWidth );
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /*!
      \param font Font
      \return the maximum height of a label
    */
    
    Bryant's avatar
    Bryant committed
    int QwtScaleDraw::maxLabelHeight( const QFont &font ) const
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
        double maxHeight = 0.0;
    
    Bryant's avatar
    Bryant committed
        const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick );
        for ( int i = 0; i < ticks.count(); i++ )
        {
    
    pixhawk's avatar
    pixhawk committed
            const double v = ticks[i];
    
    Bryant's avatar
    Bryant committed
            if ( scaleDiv().contains( v ) )
            {
                const double h = labelSize( font, ticks[i] ).height();
    
    pixhawk's avatar
    pixhawk committed
                if ( h > maxHeight )
    
    Bryant's avatar
    Bryant committed
        return qCeil( maxHeight );
    
    pixhawk's avatar
    pixhawk committed
    
    void QwtScaleDraw::updateMap()
    {
    
    Bryant's avatar
    Bryant committed
        const QPointF pos = d_data->pos;
        double len = d_data->len;
    
    
    pixhawk's avatar
    pixhawk committed
        QwtScaleMap &sm = scaleMap();
        if ( orientation() == Qt::Vertical )
    
    Bryant's avatar
    Bryant committed
            sm.setPaintInterval( pos.y() + len, pos.y() );
    
    pixhawk's avatar
    pixhawk committed
        else
    
    Bryant's avatar
    Bryant committed
            sm.setPaintInterval( pos.x(), pos.x() + len );
    
    pixhawk's avatar
    pixhawk committed
    }