1
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
3
* Copyright (C) 1997 Josef Wilgen
4
* Copyright (C) 2002 Uwe Rathmann
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
*****************************************************************************/
15
#include "qwt_painter.h"
16
#include "qwt_polygon.h"
17
#include "qwt_scale_div.h"
18
#include "qwt_scale_map.h"
19
#include "qwt_scale_draw.h"
21
#if QT_VERSION < 0x040000
23
#define QwtMatrix QWMatrix
26
#define QwtMatrix QMatrix
29
class QwtScaleDraw::PrivateData
34
alignment(QwtScaleDraw::BottomScale),
45
#if QT_VERSION < 0x040000
48
Qt::Alignment labelAlignment;
56
The range of the scale is initialized to [0, 100],
57
The position is at (0, 0) with a length of 100.
58
The orientation is QwtAbstractScaleDraw::Bottom.
60
QwtScaleDraw::QwtScaleDraw()
62
d_data = new QwtScaleDraw::PrivateData;
67
QwtScaleDraw::QwtScaleDraw(const QwtScaleDraw &other):
68
QwtAbstractScaleDraw(other)
70
d_data = new QwtScaleDraw::PrivateData(*other.d_data);
74
QwtScaleDraw::~QwtScaleDraw()
79
//! Assignment operator
80
QwtScaleDraw &QwtScaleDraw::operator=(const QwtScaleDraw &other)
82
*(QwtAbstractScaleDraw*)this = (const QwtAbstractScaleDraw &)other;
83
*d_data = *other.d_data;
88
Return alignment of the scale
91
QwtScaleDraw::Alignment QwtScaleDraw::alignment() const
93
return d_data->alignment;
97
Set the alignment of the scale
99
The default alignment is QwtScaleDraw::BottomScale
102
void QwtScaleDraw::setAlignment(Alignment align)
104
d_data->alignment = align;
108
Return the orientation
110
TopScale, BottomScale are horizontal (Qt::Horizontal) scales,
111
LeftScale, RightScale are vertical (Qt::Vertical) scales.
115
Qt::Orientation QwtScaleDraw::orientation() const
117
switch(d_data->alignment)
121
return Qt::Horizontal;
130
\brief Determine the minimum border distance
132
This member function returns the minimum space
133
needed to draw the mark labels at the scale's endpoints.
136
\param start Start border distance
137
\param end End border distance
139
void QwtScaleDraw::getBorderDistHint(const QFont &font,
140
int &start, int &end ) const
145
if ( !hasComponent(QwtAbstractScaleDraw::Labels) )
148
const QwtValueList &ticks = scaleDiv().ticks(QwtScaleDiv::MajorTick);
149
if ( ticks.count() == 0 )
152
QRect lr = labelRect(font, ticks[0]);
154
// find the distance between tick and border
155
int off = qwtAbs(map().transform(ticks[0]) - qRound(map().p1()));
157
if ( orientation() == Qt::Vertical )
158
end = lr.bottom() + 1 - off;
160
start = -lr.left() - off;
162
const int lastTick = ticks.count() - 1;
163
lr = labelRect(font, ticks[lastTick]);
165
// find the distance between tick and border
166
off = qwtAbs(map().transform(ticks[lastTick]) - qRound(map().p2()));
168
if ( orientation() == Qt::Vertical )
169
start = -lr.top() - off;
171
end = lr.right() + 1 - off;
173
// if the distance between tick and border is larger
174
// than half of the label width/height, we set to 0
183
Determine the minimum distance between two labels, that is necessary
184
that the texts don't overlap.
187
\return The maximum width of a label
189
\sa getBorderDistHint()
192
int QwtScaleDraw::minLabelDist(const QFont &font) const
194
if ( !hasComponent(QwtAbstractScaleDraw::Labels) )
197
const QwtValueList &ticks = scaleDiv().ticks(QwtScaleDiv::MajorTick);
198
if (ticks.count() == 0)
201
const QFontMetrics fm(font);
203
const bool vertical = (orientation() == Qt::Vertical);
206
QRect bRect2 = labelRect(font, ticks[0]);
209
bRect2.setRect(-bRect2.bottom(), 0, bRect2.height(), bRect2.width());
213
for (uint i = 1; i < (uint)ticks.count(); i++ )
216
bRect2 = labelRect(font, ticks[i]);
219
bRect2.setRect(-bRect2.bottom(), 0,
220
bRect2.height(), bRect2.width());
223
int dist = fm.leading(); // space between the labels
224
if ( bRect1.right() > 0 )
225
dist += bRect1.right();
226
if ( bRect2.left() < 0 )
227
dist += -bRect2.left();
229
if ( dist > maxDist )
233
double angle = labelRotation() / 180.0 * M_PI;
237
if ( sin(angle) == 0.0 )
240
const int fmHeight = fm.ascent() - 2;
242
// The distance we need until there is
243
// the height of the label font. This height is needed
244
// for the neighbour labal.
246
int labelDist = (int)(fmHeight / sin(angle) * cos(angle));
248
labelDist = -labelDist;
250
// The cast above floored labelDist. We want to ceil.
253
// For text orientations close to the scale orientation
255
if ( labelDist > maxDist )
258
// For text orientations close to the opposite of the
261
if ( labelDist < fmHeight )
262
labelDist = fmHeight;
268
Calculate the width/height that is needed for a
269
vertical/horizontal scale.
271
The extent is calculated from the pen width of the backbone,
272
the major tick length, the spacing and the maximum width/height
275
\param pen Pen that is used for painting backbone and ticks
276
\param font Font used for painting the labels
280
int QwtScaleDraw::extent(const QPen &pen, const QFont &font) const
284
if ( hasComponent(QwtAbstractScaleDraw::Labels) )
286
if ( orientation() == Qt::Vertical )
287
d = maxLabelWidth(font);
289
d = maxLabelHeight(font);
295
if ( hasComponent(QwtAbstractScaleDraw::Ticks) )
297
d += majTickLength();
300
if ( hasComponent(QwtAbstractScaleDraw::Backbone) )
302
const int pw = qwtMax( 1, pen.width() ); // penwidth can be zero
306
d = qwtMax(d, minimumExtent());
311
Calculate the minimum length that is needed to draw the scale
313
\param pen Pen that is used for painting backbone and ticks
314
\param font Font used for painting the labels
318
int QwtScaleDraw::minLength(const QPen &pen, const QFont &font) const
320
int startDist, endDist;
321
getBorderDistHint(font, startDist, endDist);
323
const QwtScaleDiv &sd = scaleDiv();
325
const uint minorCount =
326
sd.ticks(QwtScaleDiv::MinorTick).count() +
327
sd.ticks(QwtScaleDiv::MediumTick).count();
328
const uint majorCount =
329
sd.ticks(QwtScaleDiv::MajorTick).count();
331
int lengthForLabels = 0;
332
if ( hasComponent(QwtAbstractScaleDraw::Labels) )
334
if ( majorCount >= 2 )
335
lengthForLabels = minLabelDist(font) * (majorCount - 1);
338
int lengthForTicks = 0;
339
if ( hasComponent(QwtAbstractScaleDraw::Ticks) )
341
const int pw = qwtMax( 1, pen.width() ); // penwidth can be zero
342
lengthForTicks = 2 * (majorCount + minorCount) * pw;
345
return startDist + endDist + qwtMax(lengthForLabels, lengthForTicks);
349
Find the position, where to paint a label
351
The position has a distance of majTickLength() + spacing() + 1
352
from the backbone. The direction depends on the alignment()
356
QPoint QwtScaleDraw::labelPosition( double value) const
358
const int tval = map().transform(value);
359
int dist = spacing() + 1;
360
if ( hasComponent(QwtAbstractScaleDraw::Ticks) )
361
dist += majTickLength();
370
px = d_data->pos.x() + dist;
376
px = d_data->pos.x() - dist;
383
py = d_data->pos.y() + dist;
389
py = d_data->pos.y() - dist;
394
return QPoint(px, py);
400
\param painter Painter
401
\param value Value of the tick
402
\param len Lenght of the tick
404
\sa drawBackbone(), drawLabel()
406
void QwtScaleDraw::drawTick(QPainter *painter, double value, int len) const
411
int pw2 = qwtMin((int)painter->pen().width(), len) / 2;
413
QwtScaleMap scaleMap = map();
414
const QwtMetricsMap metricsMap = QwtPainter::metricsMap();
415
QPoint pos = d_data->pos;
417
if ( !metricsMap.isIdentity() )
420
The perfect position of the ticks is important.
421
To avoid rounding errors we have to use
424
QwtPainter::resetMetricsMap();
426
pos = metricsMap.layoutToDevice(pos);
428
if ( orientation() == Qt::Vertical )
430
scaleMap.setPaintInterval(
431
metricsMap.layoutToDeviceY((int)scaleMap.p1()),
432
metricsMap.layoutToDeviceY((int)scaleMap.p2())
434
len = metricsMap.layoutToDeviceX(len);
438
scaleMap.setPaintInterval(
439
metricsMap.layoutToDeviceX((int)scaleMap.p1()),
440
metricsMap.layoutToDeviceX((int)scaleMap.p2())
442
len = metricsMap.layoutToDeviceY(len);
446
const int tval = scaleMap.transform(value);
452
#if QT_VERSION < 0x040000
453
QwtPainter::drawLine(painter, pos.x() + pw2, tval,
454
pos.x() - len - 2 * pw2, tval);
456
QwtPainter::drawLine(painter, pos.x() - pw2, tval,
457
pos.x() - len, tval);
464
#if QT_VERSION < 0x040000
465
QwtPainter::drawLine(painter, pos.x(), tval,
466
pos.x() + len + pw2, tval);
468
QwtPainter::drawLine(painter, pos.x() + pw2, tval,
469
pos.x() + len, tval);
476
#if QT_VERSION < 0x040000
477
QwtPainter::drawLine(painter, tval, pos.y(),
478
tval, pos.y() + len + 2 * pw2);
480
QwtPainter::drawLine(painter, tval, pos.y() + pw2,
481
tval, pos.y() + len);
488
#if QT_VERSION < 0x040000
489
QwtPainter::drawLine(painter, tval, pos.y() + pw2,
490
tval, pos.y() - len - 2 * pw2);
492
QwtPainter::drawLine(painter, tval, pos.y() - pw2,
493
tval, pos.y() - len);
498
QwtPainter::setMetricsMap(metricsMap); // restore metrics map
502
Draws the baseline of the scale
503
\param painter Painter
505
\sa drawTick(), drawLabel()
507
void QwtScaleDraw::drawBackbone(QPainter *painter) const
509
const int bw2 = painter->pen().width() / 2;
511
const QPoint &pos = d_data->pos;
512
const int len = d_data->len - 1;
517
QwtPainter::drawLine(painter, pos.x() - bw2,
518
pos.y(), pos.x() - bw2, pos.y() + len );
521
QwtPainter::drawLine(painter, pos.x() + bw2,
522
pos.y(), pos.x() + bw2, pos.y() + len);
525
QwtPainter::drawLine(painter, pos.x(), pos.y() - bw2,
526
pos.x() + len, pos.y() - bw2);
529
QwtPainter::drawLine(painter, pos.x(), pos.y() + bw2,
530
pos.x() + len, pos.y() + bw2);
536
\brief Move the position of the scale
538
The meaning of the parameter pos depends on the alignment:
540
<dt>QwtScaleDraw::LeftScale
541
<dd>The origin is the topmost point of the
542
backbone. The backbone is a vertical line.
543
Scale marks and labels are drawn
544
at the left of the backbone.
545
<dt>QwtScaleDraw::RightScale
546
<dd>The origin is the topmost point of the
547
backbone. The backbone is a vertical line.
548
Scale marks and labels are drawn
549
at the right of the backbone.
550
<dt>QwtScaleDraw::TopScale
551
<dd>The origin is the leftmost point of the
552
backbone. The backbone is a horizontal line.
553
Scale marks and labels are drawn
555
<dt>QwtScaleDraw::BottomScale
556
<dd>The origin is the leftmost point of the
557
backbone. The backbone is a horizontal line
558
Scale marks and labels are drawn
562
\param pos Origin of the scale
564
\sa pos(), setLength()
566
void QwtScaleDraw::move(const QPoint &pos)
573
\return Origin of the scale
576
QPoint QwtScaleDraw::pos() const
582
Set the length of the backbone.
584
The length doesn't include the space needed for
587
\sa move(), minLabelDist()
589
void QwtScaleDraw::setLength(int length)
591
if ( length >= 0 && length < 10 )
593
if ( length < 0 && length > -10 )
596
d_data->len = length;
601
\return the length of the backbone
602
\sa setLength(), pos()
604
int QwtScaleDraw::length() const
610
Draws the label for a major scale tick
612
\param painter Painter
615
\sa drawTick(), drawBackbone(), boundingLabelRect()
617
void QwtScaleDraw::drawLabel(QPainter *painter, double value) const
619
QwtText lbl = tickLabel(painter->font(), value);
623
const QPoint pos = labelPosition(value);
625
QSize labelSize = lbl.textSize(painter->font());
626
if ( labelSize.height() % 2 )
627
labelSize.setHeight(labelSize.height() + 1);
629
const QwtMatrix m = labelMatrix( pos, labelSize);
632
#if QT_VERSION < 0x040000
633
painter->setWorldMatrix(m, true);
635
painter->setMatrix(m, true);
638
lbl.draw (painter, QRect(QPoint(0, 0), labelSize) );
643
Find the bounding rect for the label. The coordinates of
644
the rect are absolute coordinates ( calculated from pos() ).
645
in direction of the tick.
647
\param font Font used for painting
652
QRect QwtScaleDraw::boundingLabelRect(const QFont &font, double value) const
654
QwtText lbl = tickLabel(font, value);
658
const QPoint pos = labelPosition(value);
659
QSize labelSize = lbl.textSize(font);
660
if ( labelSize.height() % 2 )
661
labelSize.setHeight(labelSize.height() + 1);
663
const QwtMatrix m = labelMatrix( pos, labelSize);
664
return m.mapRect(QRect(QPoint(0, 0), labelSize));
668
Calculate the matrix that is needed to paint a label
669
depending on its alignment and rotation.
671
\param pos Position where to paint the label
672
\param size Size of the label
674
\sa setLabelAlignment(), setLabelRotation()
676
QwtMatrix QwtScaleDraw::labelMatrix(
677
const QPoint &pos, const QSize &size) const
680
m.translate(pos.x(), pos.y());
681
m.rotate(labelRotation());
683
int flags = labelAlignment();
691
flags = Qt::AlignRight | Qt::AlignVCenter;
697
flags = Qt::AlignLeft | Qt::AlignVCenter;
703
flags = Qt::AlignHCenter | Qt::AlignBottom;
709
flags = Qt::AlignHCenter | Qt::AlignTop;
715
const int w = size.width();
716
const int h = size.height();
720
if ( flags & Qt::AlignLeft )
722
else if ( flags & Qt::AlignRight )
724
else // Qt::AlignHCenter
727
if ( flags & Qt::AlignTop )
729
else if ( flags & Qt::AlignBottom )
731
else // Qt::AlignVCenter
740
Find the bounding rect for the label. The coordinates of
741
the rect are relative to spacing + ticklength from the backbone
742
in direction of the tick.
744
\param font Font used for painting
747
QRect QwtScaleDraw::labelRect(const QFont &font, double value) const
749
QwtText lbl = tickLabel(font, value);
751
return QRect(0, 0, 0, 0);
753
const QPoint pos = labelPosition(value);
755
QSize labelSize = lbl.textSize(font);
756
if ( labelSize.height() % 2 )
758
labelSize.setHeight(labelSize.height() + 1);
761
const QwtMatrix m = labelMatrix(pos, labelSize);
764
QRect br = QwtMetricsMap::translate(m, QRect(QPoint(0, 0), labelSize));
767
pol.setPoint(0, 0, 0);
768
pol.setPoint(1, 0, labelSize.height() - 1 );
769
pol.setPoint(2, labelSize.width() - 1, 0);
770
pol.setPoint(3, labelSize.width() - 1, labelSize.height() - 1 );
772
pol = QwtMetricsMap::translate(m, pol);
773
QRect br = pol.boundingRect();
776
#if QT_VERSION < 0x040000
777
br.moveBy(-pos.x(), -pos.y());
779
br.translate(-pos.x(), -pos.y());
786
Calculate the size that is needed to draw a label
788
\param font Label font
791
QSize QwtScaleDraw::labelSize(const QFont &font, double value) const
793
return labelRect(font, value).size();
799
When changing the rotation, it might be necessary to
800
adjust the label flags too. Finding a useful combination is
801
often the result of try and error.
803
\param rotation Angle in degrees. When changing the label rotation,
804
the label flags often needs to be adjusted too.
806
\sa setLabelAlignment(), labelRotation(), labelAlignment().
809
void QwtScaleDraw::setLabelRotation(double rotation)
811
d_data->labelRotation = rotation;
815
\return the label rotation
816
\sa setLabelRotation(), labelAlignment()
818
double QwtScaleDraw::labelRotation() const
820
return d_data->labelRotation;
824
\brief Change the label flags
826
Labels are aligned to the point ticklength + spacing away from the backbone.
828
The alignment is relative to the orientation of the label text.
829
In case of an flags of 0 the label will be aligned
830
depending on the orientation of the scale:
832
QwtScaleDraw::TopScale: Qt::AlignHCenter | Qt::AlignTop\n
833
QwtScaleDraw::BottomScale: Qt::AlignHCenter | Qt::AlignBottom\n
834
QwtScaleDraw::LeftScale: Qt::AlignLeft | Qt::AlignVCenter\n
835
QwtScaleDraw::RightScale: Qt::AlignRight | Qt::AlignVCenter\n
837
Changing the alignment is often necessary for rotated labels.
839
\param alignment Or'd Qt::AlignmentFlags <see qnamespace.h>
841
\sa setLabelRotation(), labelRotation(), labelAlignment()
842
\warning The various alignments might be confusing.
843
The alignment of the label is not the alignment
844
of the scale and is not the alignment of the flags
845
(QwtText::flags()) returned from QwtAbstractScaleDraw::label().
848
#if QT_VERSION < 0x040000
849
void QwtScaleDraw::setLabelAlignment(int alignment)
851
void QwtScaleDraw::setLabelAlignment(Qt::Alignment alignment)
854
d_data->labelAlignment = alignment;
858
\return the label flags
859
\sa setLabelAlignment(), labelRotation()
861
#if QT_VERSION < 0x040000
862
int QwtScaleDraw::labelAlignment() const
864
Qt::Alignment QwtScaleDraw::labelAlignment() const
867
return d_data->labelAlignment;
872
\return the maximum width of a label
874
int QwtScaleDraw::maxLabelWidth(const QFont &font) const
878
const QwtValueList &ticks = scaleDiv().ticks(QwtScaleDiv::MajorTick);
879
for (uint i = 0; i < (uint)ticks.count(); i++)
881
const double v = ticks[i];
882
if ( scaleDiv().contains(v) )
884
const int w = labelSize(font, ticks[i]).width();
895
\return the maximum height of a label
897
int QwtScaleDraw::maxLabelHeight(const QFont &font) const
901
const QwtValueList &ticks = scaleDiv().ticks(QwtScaleDiv::MajorTick);
902
for (uint i = 0; i < (uint)ticks.count(); i++)
904
const double v = ticks[i];
905
if ( scaleDiv().contains(v) )
907
const int h = labelSize(font, ticks[i]).height();
916
void QwtScaleDraw::updateMap()
918
QwtScaleMap &sm = scaleMap();
919
if ( orientation() == Qt::Vertical )
920
sm.setPaintInterval(d_data->pos.y() + d_data->len, d_data->pos.y());
922
sm.setPaintInterval(d_data->pos.x(), d_data->pos.x() + d_data->len);