00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include <qpen.h>
00013 #include <qpainter.h>
00014 #include "qwt_math.h"
00015 #include "qwt_painter.h"
00016 #include "qwt_scale_div.h"
00017 #include "qwt_scale_map.h"
00018 #include "qwt_scale_draw.h"
00019
00020 #if QT_VERSION < 0x040000
00021 #include <qwmatrix.h>
00022 #define QwtMatrix QWMatrix
00023 #define QwtPointArray QPointArray
00024 #else
00025 #include <qmatrix.h>
00026 #define QwtMatrix QMatrix
00027 #define QwtPointArray QPolygon
00028 #endif
00029
00030 class QwtScaleDraw::PrivateData
00031 {
00032 public:
00033 PrivateData():
00034 len(0),
00035 alignment(QwtScaleDraw::BottomScale),
00036 labelAlignment(0),
00037 labelRotation(0.0)
00038 {
00039 }
00040
00041 QPoint pos;
00042 int len;
00043
00044 Alignment alignment;
00045
00046 #if QT_VERSION < 0x040000
00047 int labelAlignment;
00048 #else
00049 Qt::Alignment labelAlignment;
00050 #endif
00051 double labelRotation;
00052 };
00053
00061 QwtScaleDraw::QwtScaleDraw()
00062 {
00063 d_data = new QwtScaleDraw::PrivateData;
00064 setLength(100);
00065 }
00066
00068 QwtScaleDraw::QwtScaleDraw(const QwtScaleDraw &other):
00069 QwtAbstractScaleDraw(other)
00070 {
00071 d_data = new QwtScaleDraw::PrivateData(*other.d_data);
00072 }
00073
00075 QwtScaleDraw::~QwtScaleDraw()
00076 {
00077 delete d_data;
00078 }
00079
00081 QwtScaleDraw &QwtScaleDraw::operator=(const QwtScaleDraw &other)
00082 {
00083 *(QwtAbstractScaleDraw*)this = (const QwtAbstractScaleDraw &)other;
00084 *d_data = *other.d_data;
00085 return *this;
00086 }
00087
00092 QwtScaleDraw::Alignment QwtScaleDraw::alignment() const
00093 {
00094 return d_data->alignment;
00095 }
00096
00103 void QwtScaleDraw::setAlignment(Alignment align)
00104 {
00105 d_data->alignment = align;
00106 }
00107
00116 Qt::Orientation QwtScaleDraw::orientation() const
00117 {
00118 switch(d_data->alignment)
00119 {
00120 case TopScale:
00121 case BottomScale:
00122 return Qt::Horizontal;
00123 case LeftScale:
00124 case RightScale:
00125 default:
00126 return Qt::Vertical;
00127 }
00128 }
00129
00140 void QwtScaleDraw::getBorderDistHint(const QFont &font,
00141 int &start, int &end ) const
00142 {
00143 start = 0;
00144 end = 0;
00145
00146 if ( !hasComponent(QwtAbstractScaleDraw::Labels) )
00147 return;
00148
00149 const QwtTickList &ticks = scaleDiv().ticks(QwtScaleDiv::MajorTick);
00150 if ( ticks.count() == 0 )
00151 return;
00152
00153 QRect lr = labelRect(font, ticks[0]);
00154
00155
00156 int off = qwtAbs(map().transform(ticks[0]) - qRound(map().p1()));
00157
00158 if ( orientation() == Qt::Vertical )
00159 end = lr.bottom() + 1 - off;
00160 else
00161 start = -lr.left() - off;
00162
00163 const int lastTick = ticks.count() - 1;
00164 lr = labelRect(font, ticks[lastTick]);
00165
00166
00167 off = qwtAbs(map().transform(ticks[lastTick]) - qRound(map().p2()));
00168
00169 if ( orientation() == Qt::Vertical )
00170 start = -lr.top() - off;
00171 else
00172 end = lr.right() + 1 - off;
00173
00174
00175
00176
00177 if ( start < 0 )
00178 start = 0;
00179 if ( end < 0 )
00180 end = 0;
00181 }
00182
00193 int QwtScaleDraw::minLabelDist(const QFont &font) const
00194 {
00195 if ( !hasComponent(QwtAbstractScaleDraw::Labels) )
00196 return 0;
00197
00198 const QwtTickList &ticks = scaleDiv().ticks(QwtScaleDiv::MajorTick);
00199 if (ticks.count() == 0)
00200 return 0;
00201
00202 const QFontMetrics fm(font);
00203
00204 const bool vertical = (orientation() == Qt::Vertical);
00205
00206 QRect bRect1;
00207 QRect bRect2 = labelRect(font, ticks[0]);
00208 if ( vertical )
00209 {
00210 bRect2.setRect(-bRect2.bottom(), 0, bRect2.height(), bRect2.width());
00211 }
00212 int maxDist = 0;
00213
00214 for (uint i = 1; i < (uint)ticks.count(); i++ )
00215 {
00216 bRect1 = bRect2;
00217 bRect2 = labelRect(font, ticks[i]);
00218 if ( vertical )
00219 {
00220 bRect2.setRect(-bRect2.bottom(), 0,
00221 bRect2.height(), bRect2.width());
00222 }
00223
00224 int dist = fm.leading();
00225 if ( bRect1.right() > 0 )
00226 dist += bRect1.right();
00227 if ( bRect2.left() < 0 )
00228 dist += -bRect2.left();
00229
00230 if ( dist > maxDist )
00231 maxDist = dist;
00232 }
00233
00234 double angle = labelRotation() / 180.0 * M_PI;
00235 if ( vertical )
00236 angle += M_PI / 2;
00237
00238 if ( sin(angle) == 0.0 )
00239 return maxDist;
00240
00241 const int fmHeight = fm.ascent() - 2;
00242
00243
00244
00245
00246
00247 int labelDist = (int)(fmHeight / sin(angle) * cos(angle));
00248 if ( labelDist < 0 )
00249 labelDist = -labelDist;
00250
00251
00252 labelDist++;
00253
00254
00255
00256 if ( labelDist > maxDist )
00257 labelDist = maxDist;
00258
00259
00260
00261
00262 if ( labelDist < fmHeight )
00263 labelDist = fmHeight;
00264
00265 return labelDist;
00266 }
00267
00281 int QwtScaleDraw::extent(const QPen &pen, const QFont &font) const
00282 {
00283 int d = 0;
00284
00285 if ( hasComponent(QwtAbstractScaleDraw::Labels) )
00286 {
00287 if ( orientation() == Qt::Vertical )
00288 d = maxLabelWidth(font);
00289 else
00290 d = maxLabelHeight(font);
00291
00292 if ( d > 0 )
00293 d += spacing();
00294 }
00295
00296 if ( hasComponent(QwtAbstractScaleDraw::Ticks) )
00297 {
00298 d += majTickLength();
00299 }
00300
00301 if ( hasComponent(QwtAbstractScaleDraw::Backbone) )
00302 {
00303 const int pw = qwtMax( 1, pen.width() );
00304 d += pw;
00305 }
00306
00307 d = qwtMax(d, minimumExtent());
00308 return d;
00309 }
00310
00319 int QwtScaleDraw::minLength(const QPen &pen, const QFont &font) const
00320 {
00321 int startDist, endDist;
00322 getBorderDistHint(font, startDist, endDist);
00323
00324 const QwtScaleDiv &sd = scaleDiv();
00325
00326 const uint minorCount =
00327 sd.ticks(QwtScaleDiv::MinorTick).count() +
00328 sd.ticks(QwtScaleDiv::MediumTick).count();
00329 const uint majorCount =
00330 sd.ticks(QwtScaleDiv::MajorTick).count();
00331
00332 int lengthForLabels = 0;
00333 if ( hasComponent(QwtAbstractScaleDraw::Labels) )
00334 {
00335 if ( majorCount >= 2 )
00336 lengthForLabels = minLabelDist(font) * (majorCount - 1);
00337 }
00338
00339 int lengthForTicks = 0;
00340 if ( hasComponent(QwtAbstractScaleDraw::Ticks) )
00341 {
00342 const int pw = qwtMax( 1, pen.width() );
00343 lengthForTicks = 2 * (majorCount + minorCount) * pw;
00344 }
00345
00346 return startDist + endDist + qwtMax(lengthForLabels, lengthForTicks);
00347 }
00348
00357 QPoint QwtScaleDraw::labelPosition( double value) const
00358 {
00359 const int tval = map().transform(value);
00360 int dist = spacing() + 1;
00361 if ( hasComponent(QwtAbstractScaleDraw::Ticks) )
00362 dist += majTickLength();
00363
00364 int px = 0;
00365 int py = 0;
00366
00367 switch(alignment())
00368 {
00369 case RightScale:
00370 {
00371 px = d_data->pos.x() + dist;
00372 py = tval;
00373 break;
00374 }
00375 case LeftScale:
00376 {
00377 px = d_data->pos.x() - dist;
00378 py = tval;
00379 break;
00380 }
00381 case BottomScale:
00382 {
00383 px = tval;
00384 py = d_data->pos.y() + dist;
00385 break;
00386 }
00387 case TopScale:
00388 {
00389 px = tval;
00390 py = d_data->pos.y() - dist;
00391 break;
00392 }
00393 }
00394
00395 return QPoint(px, py);
00396 }
00397
00407 void QwtScaleDraw::drawTick(QPainter *painter, double value, int len) const
00408 {
00409 if ( len <= 0 )
00410 return;
00411
00412 const int tval = map().transform(value);
00413
00414 switch(alignment())
00415 {
00416 case LeftScale:
00417 QwtPainter::drawLine(painter, d_data->pos.x(), tval,
00418 d_data->pos.x() - len, tval);
00419 break;
00420
00421 case RightScale:
00422 QwtPainter::drawLine(painter, d_data->pos.x(), tval,
00423 d_data->pos.x() + len, tval);
00424 break;
00425
00426 case BottomScale:
00427 QwtPainter::drawLine(painter, tval, d_data->pos.y(),
00428 tval, d_data->pos.y() + len);
00429 break;
00430
00431 case TopScale:
00432 QwtPainter::drawLine(painter, tval, d_data->pos.y(),
00433 tval, d_data->pos.y() - len);
00434 break;
00435 }
00436 }
00437
00444 void QwtScaleDraw::drawBackbone(QPainter *painter) const
00445 {
00446 const int bw2 = painter->pen().width() / 2;
00447
00448 const QPoint &pos = d_data->pos;
00449 const int len = d_data->len - 1;
00450
00451 switch(alignment())
00452 {
00453 case LeftScale:
00454 QwtPainter::drawLine(painter, pos.x() - bw2,
00455 pos.y(), pos.x() - bw2, pos.y() + len);
00456 break;
00457 case RightScale:
00458 QwtPainter::drawLine(painter, pos.x() + bw2,
00459 pos.y(), pos.x() + bw2, pos.y() + len);
00460 break;
00461 case TopScale:
00462 QwtPainter::drawLine(painter, pos.x(), pos.y() - bw2,
00463 pos.x() + len, pos.y() - bw2);
00464 break;
00465 case BottomScale:
00466 QwtPainter::drawLine(painter, pos.x(), pos.y() + bw2,
00467 pos.x() + len, pos.y() + bw2);
00468 break;
00469 }
00470 }
00471
00503 void QwtScaleDraw::move(const QPoint &pos)
00504 {
00505 d_data->pos = pos;
00506 updateMap();
00507 }
00508
00513 QPoint QwtScaleDraw::pos() const
00514 {
00515 return d_data->pos;
00516 }
00517
00526 void QwtScaleDraw::setLength(int length)
00527 {
00528 d_data->len = qwtMax(length, 10);
00529 updateMap();
00530 }
00531
00536 int QwtScaleDraw::length() const
00537 {
00538 return d_data->len;
00539 }
00540
00549 void QwtScaleDraw::drawLabel(QPainter *painter, double value) const
00550 {
00551 QwtText lbl = tickLabel(painter->font(), value);
00552 if ( lbl.isEmpty() )
00553 return;
00554
00555 const QPoint pos = labelPosition(value);
00556
00557 QSize labelSize = lbl.textSize(painter->font());
00558 if ( labelSize.height() % 2 )
00559 labelSize.setHeight(labelSize.height() + 1);
00560
00561 const QwtMatrix m = labelMatrix( pos, labelSize);
00562
00563 painter->save();
00564 #if QT_VERSION < 0x040000
00565 painter->setWorldMatrix(m, true);
00566 #else
00567 painter->setMatrix(m, true);
00568 #endif
00569
00570 lbl.draw (painter, QRect(QPoint(0, 0), labelSize) );
00571 painter->restore();
00572 }
00573
00583 QwtMatrix QwtScaleDraw::labelMatrix(
00584 const QPoint &pos, const QSize &size) const
00585 {
00586 QwtMatrix m;
00587 m.translate(pos.x(), pos.y());
00588 m.rotate(labelRotation());
00589
00590 int flags = labelAlignment();
00591 if ( flags == 0 )
00592 {
00593 switch(alignment())
00594 {
00595 case RightScale:
00596 {
00597 if ( flags == 0 )
00598 flags = Qt::AlignRight | Qt::AlignVCenter;
00599 break;
00600 }
00601 case LeftScale:
00602 {
00603 if ( flags == 0 )
00604 flags = Qt::AlignLeft | Qt::AlignVCenter;
00605 break;
00606 }
00607 case BottomScale:
00608 {
00609 if ( flags == 0 )
00610 flags = Qt::AlignHCenter | Qt::AlignBottom;
00611 break;
00612 }
00613 case TopScale:
00614 {
00615 if ( flags == 0 )
00616 flags = Qt::AlignHCenter | Qt::AlignTop;
00617 break;
00618 }
00619 }
00620 }
00621
00622 const int w = size.width();
00623 const int h = size.height();
00624
00625 int x, y;
00626
00627 if ( flags & Qt::AlignLeft )
00628 x = -w;
00629 else if ( flags & Qt::AlignRight )
00630 x = -(w % 2);
00631 else
00632 x = -(w / 2);
00633
00634 if ( flags & Qt::AlignTop )
00635 y = -h ;
00636 else if ( flags & Qt::AlignBottom )
00637 y = -(h % 2);
00638 else
00639 y = -(h/2);
00640
00641 m.translate(x, y);
00642
00643 return m;
00644 }
00645
00654 QRect QwtScaleDraw::labelRect(const QFont &font, double value) const
00655 {
00656 QwtText lbl = tickLabel(font, value);
00657 if ( lbl.isEmpty() )
00658 return QRect(0, 0, 0, 0);
00659
00660 const QPoint pos = labelPosition(value);
00661
00662 QSize labelSize = lbl.textSize(font);
00663 if ( labelSize.height() % 2 )
00664 {
00665 #ifdef __GNUC__
00666 #warning canvas content is one pixel wrong for fonts with odd pixels height
00667 #endif
00668 labelSize.setHeight(labelSize.height() + 1);
00669 }
00670
00671 const QwtMatrix m = labelMatrix(pos, labelSize);
00672
00673 #if 0
00674 QRect br = QwtMetricsMap::translate(m, QRect(QPoint(0, 0), labelSize));
00675 #else
00676 QwtPointArray pol(4);
00677 pol.setPoint(0, 0, 0);
00678 pol.setPoint(1, 0, labelSize.height() - 1 );
00679 pol.setPoint(2, labelSize.width() - 1, 0);
00680 pol.setPoint(3, labelSize.width() - 1, labelSize.height() - 1 );
00681
00682 pol = QwtMetricsMap::translate(m, pol);
00683 QRect br = pol.boundingRect();
00684 #endif
00685
00686 #if QT_VERSION < 0x040000
00687 br.moveBy(-pos.x(), -pos.y());
00688 #else
00689 br.translate(-pos.x(), -pos.y());
00690 #endif
00691
00692 return br;
00693 }
00694
00701 QSize QwtScaleDraw::labelSize(const QFont &font, double value) const
00702 {
00703 return labelRect(font, value).size();
00704 }
00705
00719 void QwtScaleDraw::setLabelRotation(double rotation)
00720 {
00721 d_data->labelRotation = rotation;
00722 }
00723
00728 double QwtScaleDraw::labelRotation() const
00729 {
00730 return d_data->labelRotation;
00731 }
00732
00758 #if QT_VERSION < 0x040000
00759 void QwtScaleDraw::setLabelAlignment(int alignment)
00760 #else
00761 void QwtScaleDraw::setLabelAlignment(Qt::Alignment alignment)
00762 #endif
00763 {
00764 d_data->labelAlignment = alignment;
00765 }
00766
00771 #if QT_VERSION < 0x040000
00772 int QwtScaleDraw::labelAlignment() const
00773 #else
00774 Qt::Alignment QwtScaleDraw::labelAlignment() const
00775 #endif
00776 {
00777 return d_data->labelAlignment;
00778 }
00779
00784 int QwtScaleDraw::maxLabelWidth(const QFont &font) const
00785 {
00786 int maxWidth = 0;
00787
00788 const QwtTickList &ticks = scaleDiv().ticks(QwtScaleDiv::MajorTick);
00789 for (uint i = 0; i < (uint)ticks.count(); i++)
00790 {
00791 const double v = ticks[i];
00792 if ( scaleDiv().contains(v) )
00793 {
00794 const int w = labelSize(font, ticks[i]).width();
00795 if ( w > maxWidth )
00796 maxWidth = w;
00797 }
00798 }
00799
00800 return maxWidth;
00801 }
00802
00807 int QwtScaleDraw::maxLabelHeight(const QFont &font) const
00808 {
00809 int maxHeight = 0;
00810
00811 const QwtTickList &ticks = scaleDiv().ticks(QwtScaleDiv::MajorTick);
00812 for (uint i = 0; i < (uint)ticks.count(); i++)
00813 {
00814 const double v = ticks[i];
00815 if ( scaleDiv().contains(v) )
00816 {
00817 const int h = labelSize(font, ticks[i]).height();
00818 if ( h > maxHeight )
00819 maxHeight = h;
00820 }
00821 }
00822
00823 return maxHeight;
00824 }
00825
00826 void QwtScaleDraw::updateMap()
00827 {
00828 QwtScaleMap &sm = scaleMap();
00829 if ( orientation() == Qt::Vertical )
00830 sm.setPaintInterval(d_data->pos.y() + d_data->len - 1, d_data->pos.y());
00831 else
00832 sm.setPaintInterval(d_data->pos.x(), d_data->pos.x() + d_data->len - 1);
00833 }