Skip to content
qwt_abstract_scale_draw.cpp 9.48 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
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_abstract_scale_draw.h"
pixhawk's avatar
pixhawk committed
#include "qwt_math.h"
#include "qwt_text.h"
#include "qwt_painter.h"
#include "qwt_scale_map.h"
Bryant's avatar
Bryant committed
#include <qpainter.h>
#include <qpalette.h>
#include <qmap.h>
#include <qlocale.h>
pixhawk's avatar
pixhawk committed

class QwtAbstractScaleDraw::PrivateData
{
public:
    PrivateData():
Bryant's avatar
Bryant committed
        spacing( 4.0 ),
        penWidth( 0 ),
        minExtent( 0.0 )
    {
        components = QwtAbstractScaleDraw::Backbone 
            | QwtAbstractScaleDraw::Ticks 
            | QwtAbstractScaleDraw::Labels;

        tickLength[QwtScaleDiv::MinorTick] = 4.0;
        tickLength[QwtScaleDiv::MediumTick] = 6.0;
        tickLength[QwtScaleDiv::MajorTick] = 8.0;
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    ScaleComponents components;
pixhawk's avatar
pixhawk committed
    QwtScaleMap map;
Bryant's avatar
Bryant committed
    QwtScaleDiv scaleDiv;
Bryant's avatar
Bryant committed
    double spacing;
    double tickLength[QwtScaleDiv::NTickTypes];
    int penWidth;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    double minExtent;
pixhawk's avatar
pixhawk committed

    QMap<double, QwtText> labelCache;
};

/*!
  \brief Constructor

  The range of the scale is initialized to [0, 100],
  The spacing (distance between ticks and labels) is
  set to 4, the tick lengths are set to 4,6 and 8 pixels
*/
QwtAbstractScaleDraw::QwtAbstractScaleDraw()
{
    d_data = new QwtAbstractScaleDraw::PrivateData;
}

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

pixhawk's avatar
pixhawk committed
  En/Disable a component of the scale

  \param component Scale component
  \param enable On/Off

Bryant's avatar
Bryant committed
  \sa hasComponent()
pixhawk's avatar
pixhawk committed
*/
void QwtAbstractScaleDraw::enableComponent(
Bryant's avatar
Bryant committed
    ScaleComponent component, bool enable )
pixhawk's avatar
pixhawk committed
{
    if ( enable )
        d_data->components |= component;
    else
        d_data->components &= ~component;
}

/*!
  Check if a component is enabled
Bryant's avatar
Bryant committed

  \param component Component type
  \return true, when component is enabled
  \sa enableComponent()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
bool QwtAbstractScaleDraw::hasComponent( ScaleComponent component ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return ( d_data->components & component );
pixhawk's avatar
pixhawk committed
}

/*!
  Change the scale division
Bryant's avatar
Bryant committed
  \param scaleDiv New scale division
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtAbstractScaleDraw::setScaleDiv( const QwtScaleDiv &scaleDiv )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    d_data->scaleDiv = scaleDiv;
    d_data->map.setScaleInterval( scaleDiv.lowerBound(), scaleDiv.upperBound() );
pixhawk's avatar
pixhawk committed
    d_data->labelCache.clear();
}

/*!
  Change the transformation of the scale
  \param transformation New scale transformation
*/
void QwtAbstractScaleDraw::setTransformation(
Bryant's avatar
Bryant committed
    QwtTransform *transformation )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    d_data->map.setTransformation( transformation );
pixhawk's avatar
pixhawk committed
}

//! \return Map how to translate between scale and pixel values
Bryant's avatar
Bryant committed
const QwtScaleMap &QwtAbstractScaleDraw::scaleMap() const
pixhawk's avatar
pixhawk committed
{
    return d_data->map;
}

//! \return Map how to translate between scale and pixel values
QwtScaleMap &QwtAbstractScaleDraw::scaleMap()
pixhawk's avatar
pixhawk committed
{
    return d_data->map;
}

//! \return scale division
const QwtScaleDiv& QwtAbstractScaleDraw::scaleDiv() const
{
Bryant's avatar
Bryant committed
    return d_data->scaleDiv;
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \brief Specify the width of the scale pen
  \param width Pen width
  \sa penWidth()
*/
void QwtAbstractScaleDraw::setPenWidth( int width )
{
    if ( width < 0 )
        width = 0;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( width != d_data->penWidth )
        d_data->penWidth = width;
}
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
/*!
    \return Scale pen width
    \sa setPenWidth()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
int QwtAbstractScaleDraw::penWidth() const
{
    return d_data->penWidth;
}
pixhawk's avatar
pixhawk committed

/*!
  \brief Draw the scale

  \param painter    The painter

  \param palette    Palette, text color is used for the labels,
pixhawk's avatar
pixhawk committed
                    foreground color for ticks and backbone
*/
Bryant's avatar
Bryant committed
void QwtAbstractScaleDraw::draw( QPainter *painter,
    const QPalette& palette ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    painter->save();
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QPen pen = painter->pen();
    pen.setWidth( d_data->penWidth );
    pen.setCosmetic( false );
    painter->setPen( pen );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
    {
        painter->save();
        painter->setPen( palette.color( QPalette::Text ) ); // ignore pen style

        const QList<double> &majorTicks =
            d_data->scaleDiv.ticks( QwtScaleDiv::MajorTick );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        for ( int i = 0; i < majorTicks.count(); i++ )
        {
pixhawk's avatar
pixhawk committed
            const double v = majorTicks[i];
Bryant's avatar
Bryant committed
            if ( d_data->scaleDiv.contains( v ) )
                drawLabel( painter, v );
pixhawk's avatar
pixhawk committed
        }

        painter->restore();
    }

Bryant's avatar
Bryant committed
    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
    {
pixhawk's avatar
pixhawk committed
        painter->save();

        QPen pen = painter->pen();
Bryant's avatar
Bryant committed
        pen.setColor( palette.color( QPalette::WindowText ) );
        pen.setCapStyle( Qt::FlatCap );

        painter->setPen( pen );
pixhawk's avatar
pixhawk committed

        for ( int tickType = QwtScaleDiv::MinorTick;
Bryant's avatar
Bryant committed
            tickType < QwtScaleDiv::NTickTypes; tickType++ )
        {
            const QList<double> &ticks = d_data->scaleDiv.ticks( tickType );
            for ( int i = 0; i < ticks.count(); i++ )
            {
pixhawk's avatar
pixhawk committed
                const double v = ticks[i];
Bryant's avatar
Bryant committed
                if ( d_data->scaleDiv.contains( v ) )
                    drawTick( painter, v, d_data->tickLength[tickType] );
pixhawk's avatar
pixhawk committed
            }
        }

        painter->restore();
    }

Bryant's avatar
Bryant committed
    if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
    {
pixhawk's avatar
pixhawk committed
        painter->save();

        QPen pen = painter->pen();
Bryant's avatar
Bryant committed
        pen.setColor( palette.color( QPalette::WindowText ) );
        pen.setCapStyle( Qt::FlatCap );

        painter->setPen( pen );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        drawBackbone( painter );
pixhawk's avatar
pixhawk committed

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

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

/*!
  \brief Set the spacing between tick and labels

  The spacing is the distance between ticks and labels.
  The default spacing is 4 pixels.

  \param spacing Spacing

Bryant's avatar
Bryant committed
  \sa spacing()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtAbstractScaleDraw::setSpacing( double spacing )
pixhawk's avatar
pixhawk committed
{
    if ( spacing < 0 )
        spacing = 0;

    d_data->spacing = spacing;
}

/*!
  \brief Get the spacing

  The spacing is the distance between ticks and labels.
  The default spacing is 4 pixels.

Bryant's avatar
Bryant committed
  \return Spacing
  \sa setSpacing()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
double QwtAbstractScaleDraw::spacing() const
pixhawk's avatar
pixhawk committed
{
    return d_data->spacing;
}

/*!
  \brief Set a minimum for the extent

Bryant's avatar
Bryant committed
  The extent is calculated from the components of the
pixhawk's avatar
pixhawk committed
  scale draw. In situations, where the labels are
  changing and the layout depends on the extent (f.e scrolling
  a scale), setting an upper limit as minimum extent will
  avoid jumps of the layout.

  \param minExtent Minimum extent

  \sa extent(), minimumExtent()
*/
Bryant's avatar
Bryant committed
void QwtAbstractScaleDraw::setMinimumExtent( double minExtent )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( minExtent < 0.0 )
        minExtent = 0.0;
pixhawk's avatar
pixhawk committed

    d_data->minExtent = minExtent;
}

/*!
  Get the minimum extent
Bryant's avatar
Bryant committed
  \return Minimum extent
pixhawk's avatar
pixhawk committed
  \sa extent(), setMinimumExtent()
*/
Bryant's avatar
Bryant committed
double QwtAbstractScaleDraw::minimumExtent() const
pixhawk's avatar
pixhawk committed
{
    return d_data->minExtent;
}

/*!
  Set the length of the ticks
pixhawk's avatar
pixhawk committed
  \param tickType Tick type
  \param length New length

  \warning the length is limited to [0..1000]
*/
void QwtAbstractScaleDraw::setTickLength(
Bryant's avatar
Bryant committed
    QwtScaleDiv::TickType tickType, double length )
pixhawk's avatar
pixhawk committed
{
    if ( tickType < QwtScaleDiv::MinorTick ||
Bryant's avatar
Bryant committed
        tickType > QwtScaleDiv::MajorTick )
    {
pixhawk's avatar
pixhawk committed
        return;
    }

Bryant's avatar
Bryant committed
    if ( length < 0.0 )
        length = 0.0;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const double maxTickLen = 1000.0;
pixhawk's avatar
pixhawk committed
    if ( length > maxTickLen )
Bryant's avatar
Bryant committed
        length = maxTickLen;
pixhawk's avatar
pixhawk committed

    d_data->tickLength[tickType] = length;
}

/*!
Bryant's avatar
Bryant committed
    \return Length of the ticks
    \sa setTickLength(), maxTickLength()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
double QwtAbstractScaleDraw::tickLength( QwtScaleDiv::TickType tickType ) const
pixhawk's avatar
pixhawk committed
{
    if ( tickType < QwtScaleDiv::MinorTick ||
Bryant's avatar
Bryant committed
        tickType > QwtScaleDiv::MajorTick )
    {
pixhawk's avatar
pixhawk committed
        return 0;
    }

    return d_data->tickLength[tickType];
}

/*!
Bryant's avatar
Bryant committed
   \return Length of the longest tick

   Useful for layout calculations
   \sa tickLength(), setTickLength()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
double QwtAbstractScaleDraw::maxTickLength() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    double length = 0.0;
    for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
        length = qMax( length, d_data->tickLength[i] );

    return length;
pixhawk's avatar
pixhawk committed
}

/*!
  \brief Convert a value into its representing label
pixhawk's avatar
pixhawk committed

  The value is converted to a plain text using
Bryant's avatar
Bryant committed
  QLocale().toString(value).
pixhawk's avatar
pixhawk committed
  This method is often overloaded by applications to have individual
  labels.

  \param value Value
  \return Label string.
*/
Bryant's avatar
Bryant committed
QwtText QwtAbstractScaleDraw::label( double value ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return QLocale().toString( value );
pixhawk's avatar
pixhawk committed
}

/*!
   \brief Convert a value into its representing label and cache it.

   The conversion between value and label is called very often
   in the layout and painting code. Unfortunately the
   calculation of the label sizes might be slow (really slow
   for rich text in Qt4), so it's necessary to cache the labels.
pixhawk's avatar
pixhawk committed

   \param font Font
   \param value Value

   \return Tick label
*/
const QwtText &QwtAbstractScaleDraw::tickLabel(
Bryant's avatar
Bryant committed
    const QFont &font, double value ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QMap<double, QwtText>::const_iterator it = d_data->labelCache.find( value );
    if ( it == d_data->labelCache.end() )
    {
        QwtText lbl = label( value );
        lbl.setRenderFlags( 0 );
        lbl.setLayoutAttribute( QwtText::MinimumLayout );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        ( void )lbl.textSize( font ); // initialize the internal cache
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        it = d_data->labelCache.insert( value, lbl );
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    return ( *it );
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
   Invalidate the cache used by tickLabel()
pixhawk's avatar
pixhawk committed

   The cache is invalidated, when a new QwtScaleDiv is set. If
   the labels need to be changed. while the same QwtScaleDiv is set,
Bryant's avatar
Bryant committed
   invalidateCache() needs to be called manually.
pixhawk's avatar
pixhawk committed
*/
void QwtAbstractScaleDraw::invalidateCache()
{
    d_data->labelCache.clear();
}