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_polygon.h"
00017 #include "qwt_scale_div.h"
00018 #include "qwt_scale_map.h"
00019 #include "qwt_scale_draw.h"
00020
00021 #if QT_VERSION < 0x040000
00022 #include <qwmatrix.h>
00023 #define QwtMatrix QWMatrix
00024 #else
00025 #include <qmatrix.h>
00026 #define QwtMatrix QMatrix
00027 #endif
00028
00029 class QwtScaleDraw::PrivateData
00030 {
00031 public:
00032 PrivateData():
00033 len(0),
00034 alignment(QwtScaleDraw::BottomScale),
00035 labelAlignment(0),
00036 labelRotation(0.0)
00037 {
00038 }
00039
00040 QPoint pos;
00041 int len;
00042
00043 Alignment alignment;
00044
00045 #if QT_VERSION < 0x040000
00046 int labelAlignment;
00047 #else
00048 Qt::Alignment labelAlignment;
00049 #endif
00050 double labelRotation;
00051 };
00052
00060 QwtScaleDraw::QwtScaleDraw()
00061 {
00062 d_data = new QwtScaleDraw::PrivateData;
00063 setLength(100);
00064 }
00065
00067 QwtScaleDraw::QwtScaleDraw(const QwtScaleDraw &other):
00068 QwtAbstractScaleDraw(other)
00069 {
00070 d_data = new QwtScaleDraw::PrivateData(*other.d_data);
00071 }
00072
00074 QwtScaleDraw::~QwtScaleDraw()
00075 {
00076 delete d_data;
00077 }
00078
00080 QwtScaleDraw &QwtScaleDraw::operator=(const QwtScaleDraw &other)
00081 {
00082 *(QwtAbstractScaleDraw*)this = (const QwtAbstractScaleDraw &)other;
00083 *d_data = *other.d_data;
00084 return *this;
00085 }
00086
00091 QwtScaleDraw::Alignment QwtScaleDraw::alignment() const
00092 {
00093 return d_data->alignment;
00094 }
00095
00102 void QwtScaleDraw::setAlignment(Alignment align)
00103 {
00104 d_data->alignment = align;
00105 }
00106
00115 Qt::Orientation QwtScaleDraw::orientation() const
00116 {
00117 switch(d_data->alignment)
00118 {
00119 case TopScale:
00120 case BottomScale:
00121 return Qt::Horizontal;
00122 case LeftScale:
00123 case RightScale:
00124 default:
00125 return Qt::Vertical;
00126 }
00127 }
00128
00139 void QwtScaleDraw::getBorderDistHint(const QFont &font,
00140 int &start, int &end ) const
00141 {
00142 start = 0;
00143 end = 0;
00144
00145 if ( !hasComponent(QwtAbstractScaleDraw::Labels) )
00146 return;
00147
00148 const QwtValueList &ticks = scaleDiv().ticks(QwtScaleDiv::MajorTick);
00149 if ( ticks.count() == 0 )
00150 return;
00151
00152 QRect lr = labelRect(font, ticks[0]);
00153
00154
00155 int off = qwtAbs(map().transform(ticks[0]) - qRound(map().p1()));
00156
00157 if ( orientation() == Qt::Vertical )
00158 end = lr.bottom() + 1 - off;
00159 else
00160 start = -lr.left() - off;
00161
00162 const int lastTick = ticks.count() - 1;
00163 lr = labelRect(font, ticks[lastTick]);
00164
00165
00166 off = qwtAbs(map().transform(ticks[lastTick]) - qRound(map().p2()));
00167
00168 if ( orientation() == Qt::Vertical )
00169 start = -lr.top() - off;
00170 else
00171 end = lr.right() + 1 - off;
00172
00173
00174
00175
00176 if ( start < 0 )
00177 start = 0;
00178 if ( end < 0 )
00179 end = 0;
00180 }
00181
00192 int QwtScaleDraw::minLabelDist(const QFont &font) const
00193 {
00194 if ( !hasComponent(QwtAbstractScaleDraw::Labels) )
00195 return 0;
00196
00197 const QwtValueList &ticks = scaleDiv().ticks(QwtScaleDiv::MajorTick);
00198 if (ticks.count() == 0)
00199 return 0;
00200
00201 const QFontMetrics fm(font);
00202
00203 const bool vertical = (orientation() == Qt::Vertical);
00204
00205 QRect bRect1;
00206 QRect bRect2 = labelRect(font, ticks[0]);
00207 if ( vertical )
00208 {
00209 bRect2.setRect(-bRect2.bottom(), 0, bRect2.height(), bRect2.width());
00210 }
00211 int maxDist = 0;
00212
00213 for (uint i = 1; i < (uint)ticks.count(); i++ )
00214 {
00215 bRect1 = bRect2;
00216 bRect2 = labelRect(font, ticks[i]);
00217 if ( vertical )
00218 {
00219 bRect2.setRect(-bRect2.bottom(), 0,
00220 bRect2.height(), bRect2.width());
00221 }
00222
00223 int dist = fm.leading();
00224 if ( bRect1.right() > 0 )
00225 dist += bRect1.right();
00226 if ( bRect2.left() < 0 )
00227 dist += -bRect2.left();
00228
00229 if ( dist > maxDist )
00230 maxDist = dist;
00231 }
00232
00233 double angle = labelRotation() / 180.0 * M_PI;
00234 if ( vertical )
00235 angle += M_PI / 2;
00236
00237 if ( sin(angle) == 0.0 )
00238 return maxDist;
00239
00240 const int fmHeight = fm.ascent() - 2;
00241
00242
00243
00244
00245
00246 int labelDist = (int)(fmHeight / sin(angle) * cos(angle));
00247 if ( labelDist < 0 )
00248 labelDist = -labelDist;
00249
00250
00251 labelDist++;
00252
00253
00254
00255 if ( labelDist > maxDist )
00256 labelDist = maxDist;
00257
00258
00259
00260
00261 if ( labelDist < fmHeight )
00262 labelDist = fmHeight;
00263
00264 return labelDist;
00265 }
00266
00280 int QwtScaleDraw::extent(const QPen &pen, const QFont &font) const
00281 {
00282 int d = 0;
00283
00284 if ( hasComponent(QwtAbstractScaleDraw::Labels) )
00285 {
00286 if ( orientation() == Qt::Vertical )
00287 d = maxLabelWidth(font);
00288 else
00289 d = maxLabelHeight(font);
00290
00291 if ( d > 0 )
00292 d += spacing();
00293 }
00294
00295 if ( hasComponent(QwtAbstractScaleDraw::Ticks) )
00296 {
00297 d += majTickLength();
00298 }
00299
00300 if ( hasComponent(QwtAbstractScaleDraw::Backbone) )
00301 {
00302 const int pw = qwtMax( 1, pen.width() );
00303 d += pw;
00304 }
00305
00306 d = qwtMax(d, minimumExtent());
00307 return d;
00308 }
00309
00318 int QwtScaleDraw::minLength(const QPen &pen, const QFont &font) const
00319 {
00320 int startDist, endDist;
00321 getBorderDistHint(font, startDist, endDist);
00322
00323 const QwtScaleDiv &sd = scaleDiv();
00324
00325 const uint minorCount =
00326 sd.ticks(QwtScaleDiv::MinorTick).count() +
00327 sd.ticks(QwtScaleDiv::MediumTick).count();
00328 const uint majorCount =
00329 sd.ticks(QwtScaleDiv::MajorTick).count();
00330
00331 int lengthForLabels = 0;
00332 if ( hasComponent(QwtAbstractScaleDraw::Labels) )
00333 {
00334 if ( majorCount >= 2 )
00335 lengthForLabels = minLabelDist(font) * (majorCount - 1);
00336 }
00337
00338 int lengthForTicks = 0;
00339 if ( hasComponent(QwtAbstractScaleDraw::Ticks) )
00340 {
00341 const int pw = qwtMax( 1, pen.width() );
00342 lengthForTicks = 2 * (majorCount + minorCount) * pw;
00343 }
00344
00345 return startDist + endDist + qwtMax(lengthForLabels, lengthForTicks);
00346 }
00347
00356 QPoint QwtScaleDraw::labelPosition( double value) const
00357 {
00358 const int tval = map().transform(value);
00359 int dist = spacing() + 1;
00360 if ( hasComponent(QwtAbstractScaleDraw::Ticks) )
00361 dist += majTickLength();
00362
00363 int px = 0;
00364 int py = 0;
00365
00366 switch(alignment())
00367 {
00368 case RightScale:
00369 {
00370 px = d_data->pos.x() + dist;
00371 py = tval;
00372 break;
00373 }
00374 case LeftScale:
00375 {
00376 px = d_data->pos.x() - dist;
00377 py = tval;
00378 break;
00379 }
00380 case BottomScale:
00381 {
00382 px = tval;
00383 py = d_data->pos.y() + dist;
00384 break;
00385 }
00386 case TopScale:
00387 {
00388 px = tval;
00389 py = d_data->pos.y() - dist;
00390 break;
00391 }
00392 }
00393
00394 return QPoint(px, py);
00395 }
00396
00406 void QwtScaleDraw::drawTick(QPainter *painter, double value, int len) const
00407 {
00408 if ( len <= 0 )
00409 return;
00410
00411 int pw2 = qwtMin((int)painter->pen().width(), len) / 2;
00412
00413 QwtScaleMap scaleMap = map();
00414 const QwtMetricsMap metricsMap = QwtPainter::metricsMap();
00415 QPoint pos = d_data->pos;
00416
00417 if ( !metricsMap.isIdentity() )
00418 {
00419
00420
00421
00422
00423
00424 QwtPainter::resetMetricsMap();
00425
00426 pos = metricsMap.layoutToDevice(pos);
00427
00428 if ( orientation() == Qt::Vertical )
00429 {
00430 scaleMap.setPaintInterval(
00431 metricsMap.layoutToDeviceY((int)scaleMap.p1()),
00432 metricsMap.layoutToDeviceY((int)scaleMap.p2())
00433 );
00434 len = metricsMap.layoutToDeviceX(len);
00435 }
00436 else
00437 {
00438 scaleMap.setPaintInterval(
00439 metricsMap.layoutToDeviceX((int)scaleMap.p1()),
00440 metricsMap.layoutToDeviceX((int)scaleMap.p2())
00441 );
00442 len = metricsMap.layoutToDeviceY(len);
00443 }
00444 }
00445
00446 const int tval = scaleMap.transform(value);
00447
00448 switch(alignment())
00449 {
00450 case LeftScale:
00451 {
00452 #if QT_VERSION < 0x040000
00453 QwtPainter::drawLine(painter, pos.x() + pw2, tval,
00454 pos.x() - len - 2 * pw2, tval);
00455 #else
00456 QwtPainter::drawLine(painter, pos.x() - pw2, tval,
00457 pos.x() - len, tval);
00458 #endif
00459 break;
00460 }
00461
00462 case RightScale:
00463 {
00464 #if QT_VERSION < 0x040000
00465 QwtPainter::drawLine(painter, pos.x(), tval,
00466 pos.x() + len + pw2, tval);
00467 #else
00468 QwtPainter::drawLine(painter, pos.x() + pw2, tval,
00469 pos.x() + len, tval);
00470 #endif
00471 break;
00472 }
00473
00474 case BottomScale:
00475 {
00476 #if QT_VERSION < 0x040000
00477 QwtPainter::drawLine(painter, tval, pos.y(),
00478 tval, pos.y() + len + 2 * pw2);
00479 #else
00480 QwtPainter::drawLine(painter, tval, pos.y() + pw2,
00481 tval, pos.y() + len);
00482 #endif
00483 break;
00484 }
00485
00486 case TopScale:
00487 {
00488 #if QT_VERSION < 0x040000
00489 QwtPainter::drawLine(painter, tval, pos.y() + pw2,
00490 tval, pos.y() - len - 2 * pw2);
00491 #else
00492 QwtPainter::drawLine(painter, tval, pos.y() - pw2,
00493 tval, pos.y() - len);
00494 #endif
00495 break;
00496 }
00497 }
00498 QwtPainter::setMetricsMap(metricsMap);
00499 }
00500
00507 void QwtScaleDraw::drawBackbone(QPainter *painter) const
00508 {
00509 const int bw2 = painter->pen().width() / 2;
00510
00511 const QPoint &pos = d_data->pos;
00512 const int len = d_data->len - 1;
00513
00514 switch(alignment())
00515 {
00516 case LeftScale:
00517 QwtPainter::drawLine(painter, pos.x() - bw2,
00518 pos.y(), pos.x() - bw2, pos.y() + len );
00519 break;
00520 case RightScale:
00521 QwtPainter::drawLine(painter, pos.x() + bw2,
00522 pos.y(), pos.x() + bw2, pos.y() + len);
00523 break;
00524 case TopScale:
00525 QwtPainter::drawLine(painter, pos.x(), pos.y() - bw2,
00526 pos.x() + len, pos.y() - bw2);
00527 break;
00528 case BottomScale:
00529 QwtPainter::drawLine(painter, pos.x(), pos.y() + bw2,
00530 pos.x() + len, pos.y() + bw2);
00531 break;
00532 }
00533 }
00534
00566 void QwtScaleDraw::move(const QPoint &pos)
00567 {
00568 d_data->pos = pos;
00569 updateMap();
00570 }
00571
00576 QPoint QwtScaleDraw::pos() const
00577 {
00578 return d_data->pos;
00579 }
00580
00589 void QwtScaleDraw::setLength(int length)
00590 {
00591 if ( length >= 0 && length < 10 )
00592 length = 10;
00593 if ( length < 0 && length > -10 )
00594 length = -10;
00595
00596 d_data->len = length;
00597 updateMap();
00598 }
00599
00604 int QwtScaleDraw::length() const
00605 {
00606 return d_data->len;
00607 }
00608
00617 void QwtScaleDraw::drawLabel(QPainter *painter, double value) const
00618 {
00619 QwtText lbl = tickLabel(painter->font(), value);
00620 if ( lbl.isEmpty() )
00621 return;
00622
00623 QPoint pos = labelPosition(value);
00624
00625 QSize labelSize = lbl.textSize(painter->font());
00626 if ( labelSize.height() % 2 )
00627 labelSize.setHeight(labelSize.height() + 1);
00628
00629 const QwtMetricsMap metricsMap = QwtPainter::metricsMap();
00630 QwtPainter::resetMetricsMap();
00631
00632 labelSize = metricsMap.layoutToDevice(labelSize);
00633 pos = metricsMap.layoutToDevice(pos);
00634
00635 const QwtMatrix m = labelMatrix( pos, labelSize);
00636
00637 painter->save();
00638 #if QT_VERSION < 0x040000
00639 painter->setWorldMatrix(m, true);
00640 #else
00641 painter->setMatrix(m, true);
00642 #endif
00643
00644 lbl.draw (painter, QRect(QPoint(0, 0), labelSize) );
00645
00646 QwtPainter::setMetricsMap(metricsMap);
00647
00648 painter->restore();
00649 }
00650
00661 QRect QwtScaleDraw::boundingLabelRect(const QFont &font, double value) const
00662 {
00663 QwtText lbl = tickLabel(font, value);
00664 if ( lbl.isEmpty() )
00665 return QRect();
00666
00667 const QPoint pos = labelPosition(value);
00668 QSize labelSize = lbl.textSize(font);
00669 if ( labelSize.height() % 2 )
00670 labelSize.setHeight(labelSize.height() + 1);
00671
00672 const QwtMatrix m = labelMatrix( pos, labelSize);
00673 return m.mapRect(QRect(QPoint(0, 0), labelSize));
00674 }
00675
00685 QwtMatrix QwtScaleDraw::labelMatrix(
00686 const QPoint &pos, const QSize &size) const
00687 {
00688 QwtMatrix m;
00689 m.translate(pos.x(), pos.y());
00690 m.rotate(labelRotation());
00691
00692 int flags = labelAlignment();
00693 if ( flags == 0 )
00694 {
00695 switch(alignment())
00696 {
00697 case RightScale:
00698 {
00699 if ( flags == 0 )
00700 flags = Qt::AlignRight | Qt::AlignVCenter;
00701 break;
00702 }
00703 case LeftScale:
00704 {
00705 if ( flags == 0 )
00706 flags = Qt::AlignLeft | Qt::AlignVCenter;
00707 break;
00708 }
00709 case BottomScale:
00710 {
00711 if ( flags == 0 )
00712 flags = Qt::AlignHCenter | Qt::AlignBottom;
00713 break;
00714 }
00715 case TopScale:
00716 {
00717 if ( flags == 0 )
00718 flags = Qt::AlignHCenter | Qt::AlignTop;
00719 break;
00720 }
00721 }
00722 }
00723
00724 const int w = size.width();
00725 const int h = size.height();
00726
00727 int x, y;
00728
00729 if ( flags & Qt::AlignLeft )
00730 x = -w;
00731 else if ( flags & Qt::AlignRight )
00732 x = -(w % 2);
00733 else
00734 x = -(w / 2);
00735
00736 if ( flags & Qt::AlignTop )
00737 y = -h ;
00738 else if ( flags & Qt::AlignBottom )
00739 y = -(h % 2);
00740 else
00741 y = -(h/2);
00742
00743 m.translate(x, y);
00744
00745 return m;
00746 }
00747
00756 QRect QwtScaleDraw::labelRect(const QFont &font, double value) const
00757 {
00758 QwtText lbl = tickLabel(font, value);
00759 if ( lbl.isEmpty() )
00760 return QRect(0, 0, 0, 0);
00761
00762 const QPoint pos = labelPosition(value);
00763
00764 QSize labelSize = lbl.textSize(font);
00765 if ( labelSize.height() % 2 )
00766 {
00767 labelSize.setHeight(labelSize.height() + 1);
00768 }
00769
00770 const QwtMatrix m = labelMatrix(pos, labelSize);
00771
00772 #if 0
00773 QRect br = QwtMetricsMap::translate(m, QRect(QPoint(0, 0), labelSize));
00774 #else
00775 QwtPolygon pol(4);
00776 pol.setPoint(0, 0, 0);
00777 pol.setPoint(1, 0, labelSize.height() - 1 );
00778 pol.setPoint(2, labelSize.width() - 1, 0);
00779 pol.setPoint(3, labelSize.width() - 1, labelSize.height() - 1 );
00780
00781 pol = QwtMetricsMap::translate(m, pol);
00782 QRect br = pol.boundingRect();
00783 #endif
00784
00785 #if QT_VERSION < 0x040000
00786 br.moveBy(-pos.x(), -pos.y());
00787 #else
00788 br.translate(-pos.x(), -pos.y());
00789 #endif
00790
00791 return br;
00792 }
00793
00800 QSize QwtScaleDraw::labelSize(const QFont &font, double value) const
00801 {
00802 return labelRect(font, value).size();
00803 }
00804
00818 void QwtScaleDraw::setLabelRotation(double rotation)
00819 {
00820 d_data->labelRotation = rotation;
00821 }
00822
00827 double QwtScaleDraw::labelRotation() const
00828 {
00829 return d_data->labelRotation;
00830 }
00831
00857 #if QT_VERSION < 0x040000
00858 void QwtScaleDraw::setLabelAlignment(int alignment)
00859 #else
00860 void QwtScaleDraw::setLabelAlignment(Qt::Alignment alignment)
00861 #endif
00862 {
00863 d_data->labelAlignment = alignment;
00864 }
00865
00870 #if QT_VERSION < 0x040000
00871 int QwtScaleDraw::labelAlignment() const
00872 #else
00873 Qt::Alignment QwtScaleDraw::labelAlignment() const
00874 #endif
00875 {
00876 return d_data->labelAlignment;
00877 }
00878
00883 int QwtScaleDraw::maxLabelWidth(const QFont &font) const
00884 {
00885 int maxWidth = 0;
00886
00887 const QwtValueList &ticks = scaleDiv().ticks(QwtScaleDiv::MajorTick);
00888 for (uint i = 0; i < (uint)ticks.count(); i++)
00889 {
00890 const double v = ticks[i];
00891 if ( scaleDiv().contains(v) )
00892 {
00893 const int w = labelSize(font, ticks[i]).width();
00894 if ( w > maxWidth )
00895 maxWidth = w;
00896 }
00897 }
00898
00899 return maxWidth;
00900 }
00901
00906 int QwtScaleDraw::maxLabelHeight(const QFont &font) const
00907 {
00908 int maxHeight = 0;
00909
00910 const QwtValueList &ticks = scaleDiv().ticks(QwtScaleDiv::MajorTick);
00911 for (uint i = 0; i < (uint)ticks.count(); i++)
00912 {
00913 const double v = ticks[i];
00914 if ( scaleDiv().contains(v) )
00915 {
00916 const int h = labelSize(font, ticks[i]).height();
00917 if ( h > maxHeight )
00918 maxHeight = h;
00919 }
00920 }
00921
00922 return maxHeight;
00923 }
00924
00925 void QwtScaleDraw::updateMap()
00926 {
00927 QwtScaleMap &sm = scaleMap();
00928 if ( orientation() == Qt::Vertical )
00929 sm.setPaintInterval(d_data->pos.y() + d_data->len, d_data->pos.y());
00930 else
00931 sm.setPaintInterval(d_data->pos.x(), d_data->pos.x() + d_data->len);
00932 }