1 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
2  * Qwt Widget Library
3  * Copyright (C) 1997   Josef Wilgen
4  * Copyright (C) 2002   Uwe Rathmann
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the Qwt License, Version 1.0
8  *****************************************************************************/
9 
10 #include "qwt_abstract_scale_draw.h"
11 #include "qwt_math.h"
12 #include "qwt_text.h"
13 #include "qwt_painter.h"
14 #include "qwt_scale_map.h"
15 #include <qpainter.h>
16 #include <qpalette.h>
17 #include <qmap.h>
18 #include <qlocale.h>
19 
20 class QwtAbstractScaleDraw::PrivateData
21 {
22 public:
PrivateData()23     PrivateData():
24         spacing( 4.0 ),
25         penWidth( 0 ),
26         minExtent( 0.0 )
27     {
28         components = QwtAbstractScaleDraw::Backbone
29             | QwtAbstractScaleDraw::Ticks
30             | QwtAbstractScaleDraw::Labels;
31 
32         tickLength[QwtScaleDiv::MinorTick] = 4.0;
33         tickLength[QwtScaleDiv::MediumTick] = 6.0;
34         tickLength[QwtScaleDiv::MajorTick] = 8.0;
35     }
36 
37     ScaleComponents components;
38 
39     QwtScaleMap map;
40     QwtScaleDiv scaleDiv;
41 
42     double spacing;
43     double tickLength[QwtScaleDiv::NTickTypes];
44     int penWidth;
45 
46     double minExtent;
47 
48     QMap<double, QwtText> labelCache;
49 };
50 
51 /*!
52   \brief Constructor
53 
54   The range of the scale is initialized to [0, 100],
55   The spacing (distance between ticks and labels) is
56   set to 4, the tick lengths are set to 4,6 and 8 pixels
57 */
QwtAbstractScaleDraw()58 QwtAbstractScaleDraw::QwtAbstractScaleDraw()
59 {
60     d_data = new QwtAbstractScaleDraw::PrivateData;
61 }
62 
63 //! Destructor
~QwtAbstractScaleDraw()64 QwtAbstractScaleDraw::~QwtAbstractScaleDraw()
65 {
66     delete d_data;
67 }
68 
69 /*!
70   En/Disable a component of the scale
71 
72   \param component Scale component
73   \param enable On/Off
74 
75   \sa hasComponent()
76 */
enableComponent(ScaleComponent component,bool enable)77 void QwtAbstractScaleDraw::enableComponent(
78     ScaleComponent component, bool enable )
79 {
80     if ( enable )
81         d_data->components |= component;
82     else
83         d_data->components &= ~component;
84 }
85 
86 /*!
87   Check if a component is enabled
88 
89   \param component Component type
90   \return true, when component is enabled
91   \sa enableComponent()
92 */
hasComponent(ScaleComponent component) const93 bool QwtAbstractScaleDraw::hasComponent( ScaleComponent component ) const
94 {
95     return ( d_data->components & component );
96 }
97 
98 /*!
99   Change the scale division
100   \param scaleDiv New scale division
101 */
setScaleDiv(const QwtScaleDiv & scaleDiv)102 void QwtAbstractScaleDraw::setScaleDiv( const QwtScaleDiv &scaleDiv )
103 {
104     d_data->scaleDiv = scaleDiv;
105     d_data->map.setScaleInterval( scaleDiv.lowerBound(), scaleDiv.upperBound() );
106     d_data->labelCache.clear();
107 }
108 
109 /*!
110   Change the transformation of the scale
111   \param transformation New scale transformation
112 */
setTransformation(QwtTransform * transformation)113 void QwtAbstractScaleDraw::setTransformation(
114     QwtTransform *transformation )
115 {
116     d_data->map.setTransformation( transformation );
117 }
118 
119 //! \return Map how to translate between scale and pixel values
scaleMap() const120 const QwtScaleMap &QwtAbstractScaleDraw::scaleMap() const
121 {
122     return d_data->map;
123 }
124 
125 //! \return Map how to translate between scale and pixel values
scaleMap()126 QwtScaleMap &QwtAbstractScaleDraw::scaleMap()
127 {
128     return d_data->map;
129 }
130 
131 //! \return scale division
scaleDiv() const132 const QwtScaleDiv& QwtAbstractScaleDraw::scaleDiv() const
133 {
134     return d_data->scaleDiv;
135 }
136 
137 /*!
138   \brief Specify the width of the scale pen
139   \param width Pen width
140   \sa penWidth()
141 */
setPenWidth(int width)142 void QwtAbstractScaleDraw::setPenWidth( int width )
143 {
144     if ( width < 0 )
145         width = 0;
146 
147     if ( width != d_data->penWidth )
148         d_data->penWidth = width;
149 }
150 
151 /*!
152     \return Scale pen width
153     \sa setPenWidth()
154 */
penWidth() const155 int QwtAbstractScaleDraw::penWidth() const
156 {
157     return d_data->penWidth;
158 }
159 
160 /*!
161   \brief Draw the scale
162 
163   \param painter    The painter
164 
165   \param palette    Palette, text color is used for the labels,
166                     foreground color for ticks and backbone
167 */
draw(QPainter * painter,const QPalette & palette) const168 void QwtAbstractScaleDraw::draw( QPainter *painter,
169     const QPalette& palette ) const
170 {
171     painter->save();
172 
173     QPen pen = painter->pen();
174     pen.setWidth( d_data->penWidth );
175     pen.setCosmetic( false );
176     painter->setPen( pen );
177 
178     if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
179     {
180         painter->save();
181         painter->setPen( palette.color( QPalette::Text ) ); // ignore pen style
182 
183         const QList<double> &majorTicks =
184             d_data->scaleDiv.ticks( QwtScaleDiv::MajorTick );
185 
186         for ( int i = 0; i < majorTicks.count(); i++ )
187         {
188             const double v = majorTicks[i];
189             if ( d_data->scaleDiv.contains( v ) )
190                 drawLabel( painter, v );
191         }
192 
193         painter->restore();
194     }
195 
196     if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
197     {
198         painter->save();
199 
200         pen = painter->pen();
201         pen.setColor( palette.color( QPalette::WindowText ) );
202         pen.setCapStyle( Qt::FlatCap );
203 
204         painter->setPen( pen );
205 
206         for ( int tickType = QwtScaleDiv::MinorTick;
207             tickType < QwtScaleDiv::NTickTypes; tickType++ )
208         {
209             const double tickLen = d_data->tickLength[tickType];
210             if ( tickLen <= 0.0 )
211                 continue;
212 
213             const QList<double> &ticks = d_data->scaleDiv.ticks( tickType );
214             for ( int i = 0; i < ticks.count(); i++ )
215             {
216                 const double v = ticks[i];
217                 if ( d_data->scaleDiv.contains( v ) )
218                     drawTick( painter, v, tickLen );
219             }
220         }
221 
222         painter->restore();
223     }
224 
225     if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
226     {
227         painter->save();
228 
229         pen = painter->pen();
230         pen.setColor( palette.color( QPalette::WindowText ) );
231         pen.setCapStyle( Qt::FlatCap );
232 
233         painter->setPen( pen );
234 
235         drawBackbone( painter );
236 
237         painter->restore();
238     }
239 
240     painter->restore();
241 }
242 
243 /*!
244   \brief Set the spacing between tick and labels
245 
246   The spacing is the distance between ticks and labels.
247   The default spacing is 4 pixels.
248 
249   \param spacing Spacing
250 
251   \sa spacing()
252 */
setSpacing(double spacing)253 void QwtAbstractScaleDraw::setSpacing( double spacing )
254 {
255     if ( spacing < 0 )
256         spacing = 0;
257 
258     d_data->spacing = spacing;
259 }
260 
261 /*!
262   \brief Get the spacing
263 
264   The spacing is the distance between ticks and labels.
265   The default spacing is 4 pixels.
266 
267   \return Spacing
268   \sa setSpacing()
269 */
spacing() const270 double QwtAbstractScaleDraw::spacing() const
271 {
272     return d_data->spacing;
273 }
274 
275 /*!
276   \brief Set a minimum for the extent
277 
278   The extent is calculated from the components of the
279   scale draw. In situations, where the labels are
280   changing and the layout depends on the extent (f.e scrolling
281   a scale), setting an upper limit as minimum extent will
282   avoid jumps of the layout.
283 
284   \param minExtent Minimum extent
285 
286   \sa extent(), minimumExtent()
287 */
setMinimumExtent(double minExtent)288 void QwtAbstractScaleDraw::setMinimumExtent( double minExtent )
289 {
290     if ( minExtent < 0.0 )
291         minExtent = 0.0;
292 
293     d_data->minExtent = minExtent;
294 }
295 
296 /*!
297   Get the minimum extent
298   \return Minimum extent
299   \sa extent(), setMinimumExtent()
300 */
minimumExtent() const301 double QwtAbstractScaleDraw::minimumExtent() const
302 {
303     return d_data->minExtent;
304 }
305 
306 /*!
307   Set the length of the ticks
308 
309   \param tickType Tick type
310   \param length New length
311 
312   \warning the length is limited to [0..1000]
313 */
setTickLength(QwtScaleDiv::TickType tickType,double length)314 void QwtAbstractScaleDraw::setTickLength(
315     QwtScaleDiv::TickType tickType, double length )
316 {
317     if ( tickType < QwtScaleDiv::MinorTick ||
318         tickType > QwtScaleDiv::MajorTick )
319     {
320         return;
321     }
322 
323     if ( length < 0.0 )
324         length = 0.0;
325 
326     const double maxTickLen = 1000.0;
327     if ( length > maxTickLen )
328         length = maxTickLen;
329 
330     d_data->tickLength[tickType] = length;
331 }
332 
333 /*!
334     \return Length of the ticks
335     \sa setTickLength(), maxTickLength()
336 */
tickLength(QwtScaleDiv::TickType tickType) const337 double QwtAbstractScaleDraw::tickLength( QwtScaleDiv::TickType tickType ) const
338 {
339     if ( tickType < QwtScaleDiv::MinorTick ||
340         tickType > QwtScaleDiv::MajorTick )
341     {
342         return 0;
343     }
344 
345     return d_data->tickLength[tickType];
346 }
347 
348 /*!
349    \return Length of the longest tick
350 
351    Useful for layout calculations
352    \sa tickLength(), setTickLength()
353 */
maxTickLength() const354 double QwtAbstractScaleDraw::maxTickLength() const
355 {
356     double length = 0.0;
357     for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ )
358         length = qMax( length, d_data->tickLength[i] );
359 
360     return length;
361 }
362 
363 /*!
364   \brief Convert a value into its representing label
365 
366   The value is converted to a plain text using
367   QLocale().toString(value).
368   This method is often overloaded by applications to have individual
369   labels.
370 
371   \param value Value
372   \return Label string.
373 */
label(double value) const374 QwtText QwtAbstractScaleDraw::label( double value ) const
375 {
376     return QLocale().toString( value );
377 }
378 
379 /*!
380    \brief Convert a value into its representing label and cache it.
381 
382    The conversion between value and label is called very often
383    in the layout and painting code. Unfortunately the
384    calculation of the label sizes might be slow (really slow
385    for rich text in Qt4), so it's necessary to cache the labels.
386 
387    \param font Font
388    \param value Value
389 
390    \return Tick label
391 */
tickLabel(const QFont & font,double value) const392 const QwtText &QwtAbstractScaleDraw::tickLabel(
393     const QFont &font, double value ) const
394 {
395     QMap<double, QwtText>::const_iterator it1 = d_data->labelCache.constFind( value );
396     if ( it1 != d_data->labelCache.constEnd() )
397         return *it1;
398 
399     QwtText lbl = label( value );
400     lbl.setRenderFlags( 0 );
401     lbl.setLayoutAttribute( QwtText::MinimumLayout );
402 
403     ( void )lbl.textSize( font ); // initialize the internal cache
404 
405     QMap<double, QwtText>::iterator it2 = d_data->labelCache.insert( value, lbl );
406     return *it2;
407 }
408 
409 /*!
410    Invalidate the cache used by tickLabel()
411 
412    The cache is invalidated, when a new QwtScaleDiv is set. If
413    the labels need to be changed. while the same QwtScaleDiv is set,
414    invalidateCache() needs to be called manually.
415 */
invalidateCache()416 void QwtAbstractScaleDraw::invalidateCache()
417 {
418     d_data->labelCache.clear();
419 }
420