Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

qwt_scale_draw.cpp

00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
00002  * Qwt Widget Library
00003  * Copyright (C) 1997   Josef Wilgen
00004  * Copyright (C) 2002   Uwe Rathmann
00005  * 
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the Qwt License, Version 1.0
00008  *****************************************************************************/
00009 
00010 // vim: expandtab
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     // find the distance between tick and border
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     // find the distance between tick and border
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     // if the distance between tick and border is larger
00175     // than half of the label width/height, we set to 0
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(); // space between the labels
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     // The distance we need until there is
00244     // the height of the label font. This height is needed
00245     // for the neighbour labal.
00246 
00247     int labelDist = (int)(fmHeight / sin(angle) * cos(angle));
00248     if ( labelDist < 0 )
00249         labelDist = -labelDist;
00250 
00251     // The cast above floored labelDist. We want to ceil.
00252     labelDist++; 
00253 
00254     // For text orientations close to the scale orientation 
00255 
00256     if ( labelDist > maxDist )
00257         labelDist = maxDist;
00258 
00259     // For text orientations close to the opposite of the 
00260     // scale orientation
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() );  // penwidth can be zero
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() );  // penwidth can be zero
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 // Qt::AlignHCenter
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 // Qt::AlignVCenter
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 }

Generated on Mon Jan 30 22:16:26 2006 for Qwt User's Guide by  doxygen 1.4.4