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 const 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 QwtMatrix m = labelMatrix( pos, labelSize);
00630
00631 painter->save();
00632 #if QT_VERSION < 0x040000
00633 painter->setWorldMatrix(m, true);
00634 #else
00635 painter->setMatrix(m, true);
00636 #endif
00637
00638 lbl.draw (painter, QRect(QPoint(0, 0), labelSize) );
00639 painter->restore();
00640 }
00641
00652 QRect QwtScaleDraw::boundingLabelRect(const QFont &font, double value) const
00653 {
00654 QwtText lbl = tickLabel(font, value);
00655 if ( lbl.isEmpty() )
00656 return QRect();
00657
00658 const QPoint pos = labelPosition(value);
00659 QSize labelSize = lbl.textSize(font);
00660 if ( labelSize.height() % 2 )
00661 labelSize.setHeight(labelSize.height() + 1);
00662
00663 const QwtMatrix m = labelMatrix( pos, labelSize);
00664 return m.mapRect(QRect(QPoint(0, 0), labelSize));
00665 }
00666
00676 QwtMatrix QwtScaleDraw::labelMatrix(
00677 const QPoint &pos, const QSize &size) const
00678 {
00679 QwtMatrix m;
00680 m.translate(pos.x(), pos.y());
00681 m.rotate(labelRotation());
00682
00683 int flags = labelAlignment();
00684 if ( flags == 0 )
00685 {
00686 switch(alignment())
00687 {
00688 case RightScale:
00689 {
00690 if ( flags == 0 )
00691 flags = Qt::AlignRight | Qt::AlignVCenter;
00692 break;
00693 }
00694 case LeftScale:
00695 {
00696 if ( flags == 0 )
00697 flags = Qt::AlignLeft | Qt::AlignVCenter;
00698 break;
00699 }
00700 case BottomScale:
00701 {
00702 if ( flags == 0 )
00703 flags = Qt::AlignHCenter | Qt::AlignBottom;
00704 break;
00705 }
00706 case TopScale:
00707 {
00708 if ( flags == 0 )
00709 flags = Qt::AlignHCenter | Qt::AlignTop;
00710 break;
00711 }
00712 }
00713 }
00714
00715 const int w = size.width();
00716 const int h = size.height();
00717
00718 int x, y;
00719
00720 if ( flags & Qt::AlignLeft )
00721 x = -w;
00722 else if ( flags & Qt::AlignRight )
00723 x = -(w % 2);
00724 else
00725 x = -(w / 2);
00726
00727 if ( flags & Qt::AlignTop )
00728 y = -h ;
00729 else if ( flags & Qt::AlignBottom )
00730 y = -(h % 2);
00731 else
00732 y = -(h/2);
00733
00734 m.translate(x, y);
00735
00736 return m;
00737 }
00738
00747 QRect QwtScaleDraw::labelRect(const QFont &font, double value) const
00748 {
00749 QwtText lbl = tickLabel(font, value);
00750 if ( lbl.isEmpty() )
00751 return QRect(0, 0, 0, 0);
00752
00753 const QPoint pos = labelPosition(value);
00754
00755 QSize labelSize = lbl.textSize(font);
00756 if ( labelSize.height() % 2 )
00757 {
00758 labelSize.setHeight(labelSize.height() + 1);
00759 }
00760
00761 const QwtMatrix m = labelMatrix(pos, labelSize);
00762
00763 #if 0
00764 QRect br = QwtMetricsMap::translate(m, QRect(QPoint(0, 0), labelSize));
00765 #else
00766 QwtPolygon pol(4);
00767 pol.setPoint(0, 0, 0);
00768 pol.setPoint(1, 0, labelSize.height() - 1 );
00769 pol.setPoint(2, labelSize.width() - 1, 0);
00770 pol.setPoint(3, labelSize.width() - 1, labelSize.height() - 1 );
00771
00772 pol = QwtMetricsMap::translate(m, pol);
00773 QRect br = pol.boundingRect();
00774 #endif
00775
00776 #if QT_VERSION < 0x040000
00777 br.moveBy(-pos.x(), -pos.y());
00778 #else
00779 br.translate(-pos.x(), -pos.y());
00780 #endif
00781
00782 return br;
00783 }
00784
00791 QSize QwtScaleDraw::labelSize(const QFont &font, double value) const
00792 {
00793 return labelRect(font, value).size();
00794 }
00795
00809 void QwtScaleDraw::setLabelRotation(double rotation)
00810 {
00811 d_data->labelRotation = rotation;
00812 }
00813
00818 double QwtScaleDraw::labelRotation() const
00819 {
00820 return d_data->labelRotation;
00821 }
00822
00848 #if QT_VERSION < 0x040000
00849 void QwtScaleDraw::setLabelAlignment(int alignment)
00850 #else
00851 void QwtScaleDraw::setLabelAlignment(Qt::Alignment alignment)
00852 #endif
00853 {
00854 d_data->labelAlignment = alignment;
00855 }
00856
00861 #if QT_VERSION < 0x040000
00862 int QwtScaleDraw::labelAlignment() const
00863 #else
00864 Qt::Alignment QwtScaleDraw::labelAlignment() const
00865 #endif
00866 {
00867 return d_data->labelAlignment;
00868 }
00869
00874 int QwtScaleDraw::maxLabelWidth(const QFont &font) const
00875 {
00876 int maxWidth = 0;
00877
00878 const QwtValueList &ticks = scaleDiv().ticks(QwtScaleDiv::MajorTick);
00879 for (uint i = 0; i < (uint)ticks.count(); i++)
00880 {
00881 const double v = ticks[i];
00882 if ( scaleDiv().contains(v) )
00883 {
00884 const int w = labelSize(font, ticks[i]).width();
00885 if ( w > maxWidth )
00886 maxWidth = w;
00887 }
00888 }
00889
00890 return maxWidth;
00891 }
00892
00897 int QwtScaleDraw::maxLabelHeight(const QFont &font) const
00898 {
00899 int maxHeight = 0;
00900
00901 const QwtValueList &ticks = scaleDiv().ticks(QwtScaleDiv::MajorTick);
00902 for (uint i = 0; i < (uint)ticks.count(); i++)
00903 {
00904 const double v = ticks[i];
00905 if ( scaleDiv().contains(v) )
00906 {
00907 const int h = labelSize(font, ticks[i]).height();
00908 if ( h > maxHeight )
00909 maxHeight = h;
00910 }
00911 }
00912
00913 return maxHeight;
00914 }
00915
00916 void QwtScaleDraw::updateMap()
00917 {
00918 QwtScaleMap &sm = scaleMap();
00919 if ( orientation() == Qt::Vertical )
00920 sm.setPaintInterval(d_data->pos.y() + d_data->len, d_data->pos.y());
00921 else
00922 sm.setPaintInterval(d_data->pos.x(), d_data->pos.x() + d_data->len);
00923 }