qwt_abstract_scale_draw.cpp 9.48 KB
Newer Older
pixhawk's avatar
pixhawk committed
1 2 3 4
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2002   Uwe Rathmann
5
 *
pixhawk's avatar
pixhawk committed
6 7 8 9
 * 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
10
#include "qwt_abstract_scale_draw.h"
pixhawk's avatar
pixhawk committed
11 12 13 14
#include "qwt_math.h"
#include "qwt_text.h"
#include "qwt_painter.h"
#include "qwt_scale_map.h"
Bryant's avatar
Bryant committed
15 16 17 18
#include <qpainter.h>
#include <qpalette.h>
#include <qmap.h>
#include <qlocale.h>
pixhawk's avatar
pixhawk committed
19 20 21 22 23

class QwtAbstractScaleDraw::PrivateData
{
public:
    PrivateData():
Bryant's avatar
Bryant committed
24 25 26 27 28 29 30 31 32 33 34
        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
35 36
    }

Bryant's avatar
Bryant committed
37
    ScaleComponents components;
38

pixhawk's avatar
pixhawk committed
39
    QwtScaleMap map;
Bryant's avatar
Bryant committed
40
    QwtScaleDiv scaleDiv;
41

Bryant's avatar
Bryant committed
42 43 44
    double spacing;
    double tickLength[QwtScaleDiv::NTickTypes];
    int penWidth;
pixhawk's avatar
pixhawk committed
45

Bryant's avatar
Bryant committed
46
    double minExtent;
pixhawk's avatar
pixhawk committed
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

    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;
}

69
/*!
pixhawk's avatar
pixhawk committed
70 71 72 73 74
  En/Disable a component of the scale

  \param component Scale component
  \param enable On/Off

Bryant's avatar
Bryant committed
75
  \sa hasComponent()
pixhawk's avatar
pixhawk committed
76 77
*/
void QwtAbstractScaleDraw::enableComponent(
Bryant's avatar
Bryant committed
78
    ScaleComponent component, bool enable )
pixhawk's avatar
pixhawk committed
79 80 81 82 83 84 85
{
    if ( enable )
        d_data->components |= component;
    else
        d_data->components &= ~component;
}

86 87
/*!
  Check if a component is enabled
Bryant's avatar
Bryant committed
88 89 90 91

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

/*!
  Change the scale division
Bryant's avatar
Bryant committed
100
  \param scaleDiv New scale division
pixhawk's avatar
pixhawk committed
101
*/
Bryant's avatar
Bryant committed
102
void QwtAbstractScaleDraw::setScaleDiv( const QwtScaleDiv &scaleDiv )
pixhawk's avatar
pixhawk committed
103
{
Bryant's avatar
Bryant committed
104 105
    d_data->scaleDiv = scaleDiv;
    d_data->map.setScaleInterval( scaleDiv.lowerBound(), scaleDiv.upperBound() );
pixhawk's avatar
pixhawk committed
106 107 108 109 110 111 112 113
    d_data->labelCache.clear();
}

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

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

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

131 132 133
//! \return scale division
const QwtScaleDiv& QwtAbstractScaleDraw::scaleDiv() const
{
Bryant's avatar
Bryant committed
134
    return d_data->scaleDiv;
pixhawk's avatar
pixhawk committed
135 136 137
}

/*!
Bryant's avatar
Bryant committed
138 139 140 141 142 143 144 145
  \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
146

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

Bryant's avatar
Bryant committed
151 152 153
/*!
    \return Scale pen width
    \sa setPenWidth()
pixhawk's avatar
pixhawk committed
154
*/
Bryant's avatar
Bryant committed
155 156 157 158
int QwtAbstractScaleDraw::penWidth() const
{
    return d_data->penWidth;
}
pixhawk's avatar
pixhawk committed
159 160 161 162 163 164

/*!
  \brief Draw the scale

  \param painter    The painter

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

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

Bryant's avatar
Bryant committed
178 179 180 181 182 183 184
    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
185

Bryant's avatar
Bryant committed
186 187
        for ( int i = 0; i < majorTicks.count(); i++ )
        {
pixhawk's avatar
pixhawk committed
188
            const double v = majorTicks[i];
Bryant's avatar
Bryant committed
189 190
            if ( d_data->scaleDiv.contains( v ) )
                drawLabel( painter, v );
pixhawk's avatar
pixhawk committed
191 192 193 194 195
        }

        painter->restore();
    }

Bryant's avatar
Bryant committed
196 197
    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
    {
pixhawk's avatar
pixhawk committed
198 199 200
        painter->save();

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

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

206
        for ( int tickType = QwtScaleDiv::MinorTick;
Bryant's avatar
Bryant committed
207 208 209 210 211
            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
212
                const double v = ticks[i];
Bryant's avatar
Bryant committed
213 214
                if ( d_data->scaleDiv.contains( v ) )
                    drawTick( painter, v, d_data->tickLength[tickType] );
pixhawk's avatar
pixhawk committed
215 216 217 218 219 220
            }
        }

        painter->restore();
    }

Bryant's avatar
Bryant committed
221 222
    if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
    {
pixhawk's avatar
pixhawk committed
223 224 225
        painter->save();

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

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

Bryant's avatar
Bryant committed
231
        drawBackbone( painter );
pixhawk's avatar
pixhawk committed
232 233 234

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

    painter->restore();
pixhawk's avatar
pixhawk committed
237 238 239 240 241 242 243 244 245 246
}

/*!
  \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
247
  \sa spacing()
pixhawk's avatar
pixhawk committed
248
*/
Bryant's avatar
Bryant committed
249
void QwtAbstractScaleDraw::setSpacing( double spacing )
pixhawk's avatar
pixhawk committed
250 251 252 253 254 255 256 257 258 259 260 261 262
{
    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
263 264
  \return Spacing
  \sa setSpacing()
pixhawk's avatar
pixhawk committed
265
*/
Bryant's avatar
Bryant committed
266
double QwtAbstractScaleDraw::spacing() const
pixhawk's avatar
pixhawk committed
267 268 269 270 271 272 273
{
    return d_data->spacing;
}

/*!
  \brief Set a minimum for the extent

Bryant's avatar
Bryant committed
274
  The extent is calculated from the components of the
pixhawk's avatar
pixhawk committed
275 276 277 278 279 280 281 282 283
  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
284
void QwtAbstractScaleDraw::setMinimumExtent( double minExtent )
pixhawk's avatar
pixhawk committed
285
{
Bryant's avatar
Bryant committed
286 287
    if ( minExtent < 0.0 )
        minExtent = 0.0;
pixhawk's avatar
pixhawk committed
288 289 290 291 292 293

    d_data->minExtent = minExtent;
}

/*!
  Get the minimum extent
Bryant's avatar
Bryant committed
294
  \return Minimum extent
pixhawk's avatar
pixhawk committed
295 296
  \sa extent(), setMinimumExtent()
*/
Bryant's avatar
Bryant committed
297
double QwtAbstractScaleDraw::minimumExtent() const
pixhawk's avatar
pixhawk committed
298 299 300 301 302 303
{
    return d_data->minExtent;
}

/*!
  Set the length of the ticks
304

pixhawk's avatar
pixhawk committed
305 306 307 308 309 310
  \param tickType Tick type
  \param length New length

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

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

Bryant's avatar
Bryant committed
322
    const double maxTickLen = 1000.0;
pixhawk's avatar
pixhawk committed
323
    if ( length > maxTickLen )
Bryant's avatar
Bryant committed
324
        length = maxTickLen;
pixhawk's avatar
pixhawk committed
325 326 327 328 329

    d_data->tickLength[tickType] = length;
}

/*!
Bryant's avatar
Bryant committed
330 331
    \return Length of the ticks
    \sa setTickLength(), maxTickLength()
pixhawk's avatar
pixhawk committed
332
*/
Bryant's avatar
Bryant committed
333
double QwtAbstractScaleDraw::tickLength( QwtScaleDiv::TickType tickType ) const
pixhawk's avatar
pixhawk committed
334
{
335
    if ( tickType < QwtScaleDiv::MinorTick ||
Bryant's avatar
Bryant committed
336 337
        tickType > QwtScaleDiv::MajorTick )
    {
pixhawk's avatar
pixhawk committed
338 339 340 341 342 343 344
        return 0;
    }

    return d_data->tickLength[tickType];
}

/*!
Bryant's avatar
Bryant committed
345 346 347 348
   \return Length of the longest tick

   Useful for layout calculations
   \sa tickLength(), setTickLength()
pixhawk's avatar
pixhawk committed
349
*/
Bryant's avatar
Bryant committed
350
double QwtAbstractScaleDraw::maxTickLength() const
pixhawk's avatar
pixhawk committed
351
{
Bryant's avatar
Bryant committed
352 353 354 355 356
    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
357 358 359
}

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

362
  The value is converted to a plain text using
Bryant's avatar
Bryant committed
363
  QLocale().toString(value).
pixhawk's avatar
pixhawk committed
364 365 366 367 368 369
  This method is often overloaded by applications to have individual
  labels.

  \param value Value
  \return Label string.
*/
Bryant's avatar
Bryant committed
370
QwtText QwtAbstractScaleDraw::label( double value ) const
pixhawk's avatar
pixhawk committed
371
{
Bryant's avatar
Bryant committed
372
    return QLocale().toString( value );
pixhawk's avatar
pixhawk committed
373 374 375 376 377 378 379 380
}

/*!
   \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
381
   for rich text in Qt4), so it's necessary to cache the labels.
pixhawk's avatar
pixhawk committed
382 383 384 385 386 387 388

   \param font Font
   \param value Value

   \return Tick label
*/
const QwtText &QwtAbstractScaleDraw::tickLabel(
Bryant's avatar
Bryant committed
389
    const QFont &font, double value ) const
pixhawk's avatar
pixhawk committed
390
{
Bryant's avatar
Bryant committed
391 392 393 394 395 396
    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
397

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

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

Bryant's avatar
Bryant committed
403
    return ( *it );
pixhawk's avatar
pixhawk committed
404 405 406
}

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

   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
411
   invalidateCache() needs to be called manually.
pixhawk's avatar
pixhawk committed
412 413 414 415 416
*/
void QwtAbstractScaleDraw::invalidateCache()
{
    d_data->labelCache.clear();
}