KD Chart 2 [rev.2.4]

KDChartCartesianAxis.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2012 Klaralvdalens Datakonsult AB.  All rights reserved.
00003 **
00004 ** This file is part of the KD Chart library.
00005 **
00006 ** Licensees holding valid commercial KD Chart licenses may use this file in
00007 ** accordance with the KD Chart Commercial License Agreement provided with
00008 ** the Software.
00009 **
00010 **
00011 ** This file may be distributed and/or modified under the terms of the
00012 ** GNU General Public License version 2 and version 3 as published by the
00013 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
00014 **
00015 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00016 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00017 **
00018 ** Contact info@kdab.com if any conditions of this licensing are not
00019 ** clear to you.
00020 **
00021 **********************************************************************/
00022 
00023 #include "KDChartCartesianAxis.h"
00024 #include "KDChartCartesianAxis_p.h"
00025 
00026 #include <cmath>
00027 
00028 #include <QtDebug>
00029 #include <QPainter>
00030 #include <QPen>
00031 #include <QBrush>
00032 #include <QApplication>
00033 
00034 #include "KDChartPaintContext.h"
00035 #include "KDChartChart.h"
00036 #include "KDChartAbstractCartesianDiagram.h"
00037 #include "KDChartAbstractGrid.h"
00038 #include "KDChartPainterSaver_p.h"
00039 #include "KDChartLayoutItems.h"
00040 #include "KDChartBarDiagram.h"
00041 #include "KDChartStockDiagram.h"
00042 #include "KDChartLineDiagram.h"
00043 #include "KDChartPrintingParameters.h"
00044 
00045 #include <KDABLibFakes>
00046 
00047 #include <limits>
00048 
00049 using namespace KDChart;
00050 
00051 #define d (d_func())
00052 
00053 CartesianAxis::CartesianAxis ( AbstractCartesianDiagram* diagram )
00054     : AbstractAxis ( new Private( diagram, this ), diagram )
00055 {
00056     init();
00057 }
00058 
00059 CartesianAxis::~CartesianAxis ()
00060 {
00061     // when we remove the first axis it will unregister itself and
00062     // propagate the next one to the primary, thus the while loop
00063     while ( d->mDiagram ) {
00064         AbstractCartesianDiagram *cd = qobject_cast<AbstractCartesianDiagram*>( d->mDiagram );
00065         cd->takeAxis( this );
00066     }
00067     Q_FOREACH( AbstractDiagram *diagram, d->secondaryDiagrams ) {
00068         AbstractCartesianDiagram *cd = qobject_cast<AbstractCartesianDiagram*>( diagram );
00069         cd->takeAxis( this );
00070     }
00071 }
00072 
00073 void CartesianAxis::init ()
00074 {
00075     d->position = Bottom;
00076     setCachedSizeDirty();
00077     connect( this, SIGNAL( coordinateSystemChanged() ), SLOT( coordinateSystemChanged() ) );
00078 }
00079 
00080 
00081 bool CartesianAxis::compare( const CartesianAxis* other )const
00082 {
00083     if( other == this ) return true;
00084     if( ! other ){
00085         //qDebug() << "CartesianAxis::compare() cannot compare to Null pointer";
00086         return false;
00087     }
00088     /*
00089     qDebug() << (position()            == other->position());
00090     qDebug() << (titleText()           == other->titleText());
00091     qDebug() << (titleTextAttributes() == other->titleTextAttributes());
00092     */
00093     return  ( static_cast<const AbstractAxis*>(this)->compare( other ) ) &&
00094             ( position()            == other->position() ) &&
00095             ( titleText()           == other->titleText() ) &&
00096             ( titleTextAttributes() == other->titleTextAttributes() );
00097 }
00098 
00099 void CartesianAxis::coordinateSystemChanged()
00100 {
00101     layoutPlanes();
00102 }
00103 
00104 
00105 void CartesianAxis::setTitleText( const QString& text )
00106 {
00107     d->titleText = text;
00108     layoutPlanes();
00109 }
00110 
00111 QString CartesianAxis::titleText() const
00112 {
00113     return d->titleText;
00114 }
00115 
00116 void CartesianAxis::setTitleTextAttributes( const TextAttributes &a )
00117 {
00118     d->titleTextAttributes = a;
00119     d->useDefaultTextAttributes = false;
00120     layoutPlanes();
00121 }
00122 
00123 TextAttributes CartesianAxis::titleTextAttributes() const
00124 {
00125     if( hasDefaultTitleTextAttributes() ){
00126         TextAttributes ta( textAttributes() );
00127         Measure me( ta.fontSize() );
00128         me.setValue( me.value() * 1.5 );
00129         ta.setFontSize( me );
00130         return ta;
00131     }
00132     return d->titleTextAttributes;
00133 }
00134 
00135 void CartesianAxis::resetTitleTextAttributes()
00136 {
00137     d->useDefaultTextAttributes = true;
00138     layoutPlanes();
00139 }
00140 
00141 bool CartesianAxis::hasDefaultTitleTextAttributes() const
00142 {
00143     return d->useDefaultTextAttributes;
00144 }
00145 
00146 
00147 void CartesianAxis::setPosition ( Position p )
00148 {
00149     d->position = p;
00150     layoutPlanes();
00151 }
00152 
00153 #if QT_VERSION < 0x040400 || defined(Q_COMPILER_MANGLES_RETURN_TYPE)
00154 const
00155 #endif
00156 CartesianAxis::Position CartesianAxis::position() const
00157 {
00158     return d->position;
00159 }
00160 
00161 void CartesianAxis::layoutPlanes()
00162 {
00163     //qDebug() << "CartesianAxis::layoutPlanes()";
00164     if( ! d->diagram() || ! d->diagram()->coordinatePlane() ) {
00165         //qDebug() << "CartesianAxis::layoutPlanes(): Sorry, found no plane.";
00166         return;
00167     }
00168     AbstractCoordinatePlane* plane = d->diagram()->coordinatePlane();
00169     if( plane ){
00170         plane->layoutPlanes();
00171         //qDebug() << "CartesianAxis::layoutPlanes() OK";
00172     }
00173 }
00174 
00175 /*
00176   void CartesianAxis::paintEvent( QPaintEvent* event )
00177   {
00178   Q_UNUSED( event );
00179 
00180   if( ! d->diagram() || ! d->diagram()->coordinatePlane() ) return;
00181 
00182   PaintContext context;
00183   QPainter painter( this );
00184   context.setPainter( &painter );
00185   AbstractCoordinatePlane* plane = d->diagram()->coordinatePlane();
00186   context.setCoordinatePlane( plane );
00187   QRectF rect = QRectF ( 1, 1, plane->width() - 3, plane->height() - 3 );
00188   context.setRectangle( rect );
00189   d->geometry.setSize( size() );
00190   paintCtx( &context );
00191   }
00192 */
00193 
00194 
00195 static bool referenceDiagramIsBarDiagram( const AbstractDiagram * diagram )
00196 {
00197     const AbstractCartesianDiagram * dia =
00198             qobject_cast< const AbstractCartesianDiagram * >( diagram );
00199     if( dia && dia->referenceDiagram() )
00200         dia = dia->referenceDiagram();
00201     return qobject_cast< const BarDiagram* >( dia ) != 0;
00202 }
00203 
00204 static bool referenceDiagramNeedsCenteredAbscissaTicks( const AbstractDiagram *diagram )
00205 {
00206     const AbstractCartesianDiagram * dia =
00207             qobject_cast< const AbstractCartesianDiagram * >( diagram );
00208     if( dia && dia->referenceDiagram() )
00209         dia = dia->referenceDiagram();
00210     if ( qobject_cast< const BarDiagram* >( dia ) )
00211         return true;
00212     if ( qobject_cast< const StockDiagram* >( dia ) )
00213         return true;
00214 
00215     const LineDiagram * lineDiagram = qobject_cast< const LineDiagram* >( dia );
00216     return lineDiagram && lineDiagram->centerDataPoints();
00217 }
00218 
00219 bool CartesianAxis::isAbscissa() const
00220 {
00221     const Qt::Orientation diagramOrientation = referenceDiagramIsBarDiagram( d->diagram() ) ? ((BarDiagram*)(d->diagram()))->orientation()
00222                                                                                             : Qt::Vertical;
00223     return diagramOrientation == Qt::Vertical ? position() == Bottom || position() == Top
00224                                               : position() == Left   || position() == Right;
00225 }
00226 
00227 bool CartesianAxis::isOrdinate() const
00228 {
00229     const Qt::Orientation diagramOrientation = referenceDiagramIsBarDiagram( d->diagram() ) ? ((BarDiagram*)(d->diagram()))->orientation()
00230                                                                                             : Qt::Vertical;
00231     return diagramOrientation == Qt::Vertical ? position() == Left   || position() == Right
00232                                               : position() == Bottom || position() == Top;
00233 }
00234 
00235 void CartesianAxis::paint( QPainter* painter )
00236 {
00237     if( ! d->diagram() || ! d->diagram()->coordinatePlane() ) return;
00238     PaintContext ctx;
00239     ctx.setPainter ( painter );
00240     ctx.setCoordinatePlane( d->diagram()->coordinatePlane() );
00241     const QRect rect( areaGeometry() );
00242 
00243     //qDebug() << "CartesianAxis::paint( QPainter* painter )  " << " areaGeometry()():" << rect << " sizeHint():" << sizeHint();
00244 
00245     ctx.setRectangle(
00246         QRectF (
00247             //QPointF(0, 0),
00248             QPointF(rect.left(), rect.top()),
00249             QSizeF(rect.width(), rect.height() ) ) );
00250     // enabling clipping so that we're not drawing outside
00251     QRegion clipRegion( rect.adjusted( -1, -1, 1, 1 ) );
00252     painter->save();
00253     painter->setClipRegion( clipRegion );
00254     paintCtx( &ctx );
00255     painter->restore();
00256     //qDebug() << "KDChart::CartesianAxis::paint() done.";
00257 }
00258 
00259 void CartesianAxis::Private::drawSubUnitRulers( QPainter* painter, CartesianCoordinatePlane* plane, const DataDimension& dim,
00260                                                 const QPointF& rulerRef, const QVector<int>& drawnTicks, const bool diagramIsVertical,
00261                         const RulerAttributes& rulerAttr ) const
00262 {
00263     const QRect geoRect( axis()->geometry() );
00264     int nextMayBeTick = 0;
00265     int mayBeTick = 0;
00266     int logSubstep = 0;
00267     qreal f = dim.start;
00268     qreal fLogSubstep = f;
00269     const bool isAbscissa = axis()->isAbscissa();
00270     const bool isLogarithmic = (dim.calcMode == AbstractCoordinatePlane::Logarithmic );
00271     const int subUnitTickLength = axis()->tickLength( true );
00272 
00273     // Use negative limit to ensure that also the last tick is painted,
00274     // which is needed if major tick marks are disabled
00275     while ( dim.end - f > -std::numeric_limits< float >::epsilon() ) {
00276         const qreal quotient = f / dim.stepWidth;
00277         const bool isMinorTickMark = qAbs(qRound(quotient) - quotient) > std::numeric_limits< float >::epsilon();
00278         // 'Drawn' ticks isn't quite the right naming here, it also counts major tick marks, which are not drawn.
00279         if( drawnTicks.count() > nextMayBeTick )
00280             mayBeTick = drawnTicks[ nextMayBeTick ];
00281         // Paint minor tick mark only if there is no major tick mark drawn at this point
00282         if ( isMinorTickMark || !rulerAttr.showMajorTickMarks() ) {
00283             if ( isAbscissa ) {
00284                 // for the x-axis
00285                 QPointF topPoint = diagramIsVertical ? QPointF( f, 0 ) : QPointF( 0, f );
00286                 QPointF bottomPoint( topPoint );
00287                 // we don't draw the sub ticks, if we are at the same position as a normal tick
00288                 topPoint = plane->translate( topPoint );
00289                 bottomPoint = plane->translate( bottomPoint );
00290                 if ( diagramIsVertical ) {
00291                     topPoint.setY( rulerRef.y() + subUnitTickLength );
00292                     bottomPoint.setY( rulerRef.y() );
00293                 } else {
00294                     topPoint.setX( rulerRef.x() + subUnitTickLength );
00295                     bottomPoint.setX( rulerRef.x() );
00296                 }
00297                 if( qAbs( mayBeTick - topPoint.x() ) > 1 )
00298                 {
00299                     if ( rulerAttr.hasTickMarkPenAt( topPoint.x() ) )
00300                         painter->setPen( rulerAttr.tickMarkPen( topPoint.x() ) );
00301                     else
00302                         painter->setPen( rulerAttr.minorTickMarkPen() );
00303                     painter->drawLine( topPoint, bottomPoint );
00304                 }
00305                 else {
00306                     ++nextMayBeTick;
00307                 }
00308             } else {
00309                 // for the y-axis
00310 
00311                 QPointF leftPoint = plane->translate( diagramIsVertical ? QPointF( 0, f ) : QPointF( f, 0 ) );
00312                 //qDebug() << "geoRect:" << geoRect << "   geoRect.top()" << geoRect.top() << "geoRect.bottom()" << geoRect.bottom() << "  translatedValue:" << translatedValue;
00313                 // we don't draw the sub ticks, if we are at the same position as a normal tick
00314                 if( qAbs( mayBeTick - diagramIsVertical ? leftPoint.y() : leftPoint.x() ) > 1 ){
00315                     const qreal translatedValue = leftPoint.y();
00316                     bool translatedValueIsWithinBoundaries;
00317                     if ( diagramIsVertical ) {
00318                         translatedValueIsWithinBoundaries = translatedValue > geoRect.top() && translatedValue <= geoRect.bottom();
00319                     } else {
00320                         translatedValueIsWithinBoundaries = translatedValue > geoRect.left() && translatedValue <= geoRect.right();
00321                     }
00322                     if( translatedValueIsWithinBoundaries ){
00323                         QPointF rightPoint = diagramIsVertical ? QPointF( 0, f ) : QPointF( f, 0 );
00324                         rightPoint = plane->translate( rightPoint );
00325                         if ( diagramIsVertical ) {
00326                             leftPoint.setX( rulerRef.x() + subUnitTickLength );
00327                             rightPoint.setX( rulerRef.x() );
00328                         } else {
00329                             leftPoint.setY( rulerRef.y() + (position == Bottom ? subUnitTickLength : -subUnitTickLength) );
00330                             rightPoint.setY( rulerRef.y() );
00331                         }
00332                         if ( rulerAttr.hasTickMarkPenAt( f ) )
00333                             painter->setPen( rulerAttr.tickMarkPen( f ) );
00334                         else
00335                             painter->setPen( rulerAttr.minorTickMarkPen() );
00336                         painter->drawLine( leftPoint, rightPoint );
00337                     }
00338                 } else {
00339                     ++nextMayBeTick;
00340                 }
00341                 }
00342         }
00343         if ( isLogarithmic ){
00344             if( logSubstep == 9 ){                
00345                 fLogSubstep *= ( fLogSubstep > 0.0 ) ? 10.0 : 0.1;
00346                 if( fLogSubstep == 0 )
00347                     fLogSubstep = 0.01;
00348                 logSubstep = 0;
00349                 f = fLogSubstep;
00350             }
00351             else
00352             {
00353                 f += fLogSubstep;
00354             }
00355             ++logSubstep;
00356         }else{
00357             f += dim.subStepWidth;
00358         }
00359     }
00360 }
00361 
00362 
00363 const TextAttributes CartesianAxis::Private::titleTextAttributesWithAdjustedRotation() const
00364 {
00365     TextAttributes titleTA( titleTextAttributes );
00366     if( (position == Left || position == Right) ){
00367         int rotation = titleTA.rotation() + 270;
00368         if( rotation >= 360 )
00369             rotation -= 360;
00370 
00371         // limit the allowed values to 0, 90, 180, 270:
00372         if( rotation  < 90 )
00373             rotation = 0;
00374         else if( rotation  < 180 )
00375             rotation = 90;
00376         else if( rotation  < 270 )
00377             rotation = 180;
00378         else if( rotation  < 360 )
00379             rotation = 270;
00380         else
00381             rotation = 0;
00382 
00383         titleTA.setRotation( rotation );
00384     }
00385     return titleTA;
00386 }
00387 
00388 void CartesianAxis::setTitleSpace( qreal axisTitleSpace )
00389 {
00390     d->axisTitleSpace = axisTitleSpace;
00391 }
00392 
00393 qreal CartesianAxis::titleSpace() const
00394 {
00395     return d->axisTitleSpace;
00396 }
00397 
00398 void CartesianAxis::Private::drawTitleText( QPainter* painter, CartesianCoordinatePlane* plane, const QRect& areaGeoRect ) const
00399 {
00400     const TextAttributes titleTA( titleTextAttributesWithAdjustedRotation() );
00401     if( titleTA.isVisible() ) {
00402         TextLayoutItem titleItem( titleText,
00403                                   titleTA,
00404                                   plane->parent(),
00405                                   KDChartEnums::MeasureOrientationMinimum,
00406                                   Qt::AlignHCenter|Qt::AlignVCenter );
00407         QPointF point;
00408         QSize size( titleItem.sizeHint() );
00409         //FIXME(khz): We definitely need to provide a way that users can decide
00410         //            the position of an axis title.
00411         switch( position )
00412         {
00413         case Top:
00414             point.setX( areaGeoRect.left() + areaGeoRect.width() / 2.0);
00415             point.setY( areaGeoRect.top()  + (size.height() / 2)*1/axisTitleSpace );
00416             size.setWidth( qMin( size.width(), axis()->geometry().width() ) );
00417             break;
00418         case Bottom:
00419             point.setX( areaGeoRect.left() + areaGeoRect.width() / 2.0);
00420             point.setY( areaGeoRect.bottom() - (size.height()/2)*1/axisTitleSpace);
00421             size.setWidth( qMin( size.width(), axis()->geometry().width() ) );
00422             break;
00423         case Left:
00424             point.setX( areaGeoRect.left() + (size.width() / 2)*1/axisTitleSpace);
00425             point.setY( areaGeoRect.top() + areaGeoRect.height() / 2.0);
00426             size.setHeight( qMin( size.height(), axis()->geometry().height() ) );
00427             break;
00428         case Right:
00429             point.setX( areaGeoRect.right() - (size.width() / 2)*1/axisTitleSpace);
00430             point.setY( areaGeoRect.top() + areaGeoRect.height() / 2.0);
00431             size.setHeight( qMin( size.height(), axis()->geometry().height() ) );
00432             break;
00433         }
00434         const PainterSaver painterSaver( painter );
00435         painter->translate( point );
00436         //if( axis()->isOrdinate() )
00437         //    painter->rotate( 270.0 );
00438         titleItem.setGeometry( QRect( QPoint(-size.width() / 2, -size.height() / 2), size ) );
00439         //painter->drawRect(titleItem.geometry().adjusted(0,0,-1,-1));
00440         titleItem.paint( painter );
00441     }
00442 }
00443 
00444 
00445 static void calculateNextLabel( qreal& labelValue, qreal step, bool isLogarithmic, qreal min )
00446 {
00447     if ( isLogarithmic ){
00448         if( step > 0.0 )
00449             labelValue *= 10.0;
00450         else
00451             labelValue /= 10.0;
00452         if( labelValue == 0.0 )
00453             labelValue = pow( 10.0, floor( log10( min ) ) );
00454     }else{
00455         //qDebug() << "new axis label:" << labelValue << "+" << step << "=" << labelValue+step;
00456         labelValue += step;
00457         if( qAbs(labelValue) < 1.0e-15 )
00458             labelValue = 0.0;
00459     }
00460 }
00461 
00462 
00463 void CartesianAxis::paintCtx( PaintContext* context )
00464 {
00465 
00466     Q_ASSERT_X ( d->diagram(), "CartesianAxis::paint",
00467                  "Function call not allowed: The axis is not assigned to any diagram." );
00468 
00469     CartesianCoordinatePlane* plane = dynamic_cast<CartesianCoordinatePlane*>(context->coordinatePlane());
00470     Q_ASSERT_X ( plane, "CartesianAxis::paint",
00471                  "Bad function call: PaintContext::coodinatePlane() NOT a cartesian plane." );
00472 
00473     // note: Not having any data model assigned is no bug
00474     //       but we can not draw an axis then either.
00475     if( ! d->diagram()->model() )
00476         return;
00477 
00478     // Determine the diagram that specifies the orientation of the diagram we're painting here
00479     // That diagram is the reference diagram, if it exists, or otherwise the diagram itself.
00480     // Note: In KDChart 2.3 or earlier, only a bar diagram can be vertical instead of horizontal.
00481     const AbstractCartesianDiagram * refDiagram = qobject_cast< const AbstractCartesianDiagram * >( d->diagram() );
00482     if( refDiagram && refDiagram->referenceDiagram() )
00483         refDiagram = refDiagram->referenceDiagram();
00484     const BarDiagram *barDiagram = qobject_cast< const BarDiagram* >( refDiagram );
00485     const Qt::Orientation diagramOrientation = barDiagram ? barDiagram->orientation() : Qt::Vertical;
00486     const bool diagramIsVertical = diagramOrientation == Qt::Vertical;
00487 
00488     /*
00489      * let us paint the labels at a
00490      * smaller resolution
00491      * Same mini pixel value as for
00492      * Cartesian Grid
00493      */
00494     //const qreal MinimumPixelsBetweenRulers = 1.0;
00495     DataDimensionsList dimensions( plane->gridDimensionsList() );
00496     //qDebug("CartesianAxis::paintCtx() gets DataDimensionsList.first():   start: %f   end: %f   stepWidth: %f", dimensions.first().start, dimensions.first().end, dimensions.first().stepWidth);
00497 
00498     // test for programming errors: critical
00499     Q_ASSERT_X ( dimensions.count() == 2, "CartesianAxis::paint",
00500                  "Error: plane->gridDimensionsList() did not return exactly two dimensions." );
00501     DataDimension dimX, dimY;
00502     DataDimension dim;
00503     // If the diagram is horizontal, we need to inverse the x/y ranges
00504     if ( diagramIsVertical ) {
00505         /*double yStart = dimY.start;
00506         double yEnd = dimY.end;
00507         dimY.start = dimX.start;
00508         dimY.end = dimX.end;
00509         dimX.start = yStart;
00510         dimX.end = yEnd;*/
00511         dimX = AbstractGrid::adjustedLowerUpperRange( dimensions.first(), true, true );
00512         dimY = AbstractGrid::adjustedLowerUpperRange( dimensions.last(), true, true );
00513 
00514         // FIXME
00515         // Ugly workaround for dimensions being bound to both, the x coordinate direction and the abscissa
00516         //if ( referenceDiagramIsPercentLyingBarDiagram ) {
00517         //  dimY.stepWidth = 10.0;
00518         //  dimY.subStepWidth = 2.0;
00519         //}
00520     } else {
00521         dimX = AbstractGrid::adjustedLowerUpperRange( dimensions.last(), true, true );
00522         dimY = AbstractGrid::adjustedLowerUpperRange( dimensions.first(), true, true );
00523     }
00524     dim = (isAbscissa() ? dimX : dimY);
00525 
00526     /*
00527     if(isAbscissa())
00528         qDebug() << "         " << "Abscissa:" << dimX.start <<".."<<dimX.end <<"  step"<<dimX.stepWidth<<"  sub step"<<dimX.subStepWidth;
00529     else
00530         qDebug() << "         " << "Ordinate:" << dimY.start <<".."<<dimY.end <<"  step"<<dimY.stepWidth<<"  sub step"<<dimY.subStepWidth;
00531     */
00532 
00533     /*
00534      * let us paint the labels at a
00535      * smaller resolution
00536      * Same mini pixel value as for
00537      * Cartesian Grid
00538      */
00539     const qreal MinimumPixelsBetweenRulers = qMin(  dimX.stepWidth,  dimY.stepWidth );//1.0;
00540 
00541     // preparation: calculate the range that will be displayed:
00542     const qreal absRange = qAbs( dim.distance() );
00543 
00544     qreal numberOfUnitRulers;
00545     if ( isAbscissa() ) {
00546         if( dimX.isCalculated )
00547             numberOfUnitRulers = absRange / qAbs( dimX.stepWidth ) + 1.0;
00548         else
00549             numberOfUnitRulers = d->diagram()->model()->rowCount(d->diagram()->rootIndex()) - 1.0;
00550     }else{
00551         numberOfUnitRulers = absRange / qAbs( dimY.stepWidth ) + 1.0;
00552     }
00553 
00554     //    qDebug() << "absRange" << absRange << "dimY.stepWidth:" << dimY.stepWidth << "numberOfUnitRulers:" << numberOfUnitRulers;
00555 
00556     qreal numberOfSubUnitRulers;
00557     if ( isAbscissa() ){
00558         if( dimX.isCalculated )
00559             numberOfSubUnitRulers = absRange / qAbs( dimX.subStepWidth ) + 1.0;
00560         else
00561             numberOfSubUnitRulers = dimX.subStepWidth>0 ? absRange / qAbs( dimX.subStepWidth ) + 1.0 : 0.0;
00562     }else{
00563         numberOfSubUnitRulers = absRange / qAbs( dimY.subStepWidth ) + 1.0;
00564     }
00565 
00566     // - calculate the absolute range in screen pixels:
00567     const QPointF p1 = plane->translate( diagramIsVertical ? QPointF(dimX.start, dimY.start) : QPointF(dimY.start, dimX.start) );
00568     const QPointF p2 = plane->translate( diagramIsVertical ? QPointF(dimX.end,   dimY.end)   : QPointF(dimY.end,   dimX.end  ) );
00569 
00570     double screenRange;
00571     if ( isAbscissa() )
00572         screenRange = qAbs ( p1.x() - p2.x() );
00573     else
00574         screenRange = qAbs ( p1.y() - p2.y() );
00575 
00576     const bool useItemCountLabels = isAbscissa() && ! dimX.isCalculated;
00577 
00578     // attributes used to customize ruler appearance
00579     const RulerAttributes rulerAttr = rulerAttributes();
00580 
00581     const bool drawUnitRulers = rulerAttr.showMajorTickMarks() && (screenRange / ( numberOfUnitRulers / dimX.stepWidth ) > MinimumPixelsBetweenRulers);
00582     const bool drawSubUnitRulers = rulerAttr.showMinorTickMarks() &&
00583         (numberOfSubUnitRulers != 0.0) &&
00584         (screenRange / numberOfSubUnitRulers > MinimumPixelsBetweenRulers);
00585 
00586     const TextAttributes labelTA = textAttributes();
00587     const bool drawLabels = labelTA.isVisible();
00588 
00589     // - find the reference point at which to start drawing and the increment (line distance);
00590     QPointF rulerRef;
00591     const QRect areaGeoRect( areaGeometry() );
00592     const QRect geoRect( geometry() );
00593     QRectF rulerRect;
00594 
00595     QPainter* const ptr = context->painter();
00596 
00597     //for debugging: if( isAbscissa() )ptr->drawRect(areaGeoRect.adjusted(0,0,-1,-1));
00598     //qDebug() << "         " << (isAbscissa() ? "Abscissa":"Ordinate") << "axis painting with geometry" << areaGeoRect;
00599 
00600     // FIXME references are of course different for all locations:
00601     switch( position() )
00602     {
00603     case Top:
00604         rulerRef.setX( areaGeoRect.topLeft().x() );
00605         rulerRef.setY( areaGeoRect.topLeft().y() + areaGeoRect.height() );
00606         break;
00607     case Bottom:
00608         rulerRef.setX( areaGeoRect.bottomLeft().x() );
00609         rulerRef.setY( areaGeoRect.bottomLeft().y() - areaGeoRect.height() );
00610         break;
00611     case Right:
00612         rulerRef.setX( areaGeoRect.bottomRight().x() - areaGeoRect.width() );
00613         rulerRef.setY( areaGeoRect.bottomRight().y() );
00614         break;
00615     case Left:
00616         rulerRef.setX( areaGeoRect.bottomLeft().x() + areaGeoRect.width() );
00617         rulerRef.setY( areaGeoRect.bottomLeft().y() );
00618         break;
00619     }
00620 
00621     // set up the lines to paint:
00622 
00623     // set up a map of integer positions,
00624 
00625     // - starting with the fourth
00626     // - the the halfs
00627     // - then the tens
00628     // this will override all halfs and fourth that hit a higher-order ruler
00629     // MAKE SURE TO START AT (0, 0)!
00630 
00631     // set up a reference point,  a step vector and a unit vector for the drawing:
00632 
00633     const qreal minValueY = dimY.start;
00634     const qreal maxValueY = dimY.end;
00635     const qreal minValueX = dimX.start;
00636     const qreal maxValueX = dimX.end;
00637     const bool isLogarithmicX = (dimX.calcMode == AbstractCoordinatePlane::Logarithmic );
00638     const bool isLogarithmicY = (dimY.calcMode == AbstractCoordinatePlane::Logarithmic );
00639 //#define AXES_PAINTING_DEBUG 1
00640 #ifdef AXES_PAINTING_DEBUG
00641     qDebug() << "CartesianAxis::paint: reference values:" << endl
00642              << "-- range x/y: " << dimX.distance() << "/" << dimY.distance() << endl
00643              << "-- absRange: " << absRange << endl
00644              << "-- numberOfUnitRulers: " << numberOfUnitRulers << endl
00645              << "-- screenRange: " << screenRange << endl
00646              << "-- drawUnitRulers: " << drawUnitRulers << endl
00647              << "-- drawLabels: " << drawLabels << endl
00648              << "-- ruler reference point:: " << rulerRef << endl
00649              << "-- minValueX: " << minValueX << "   maxValueX: " << maxValueX << endl
00650              << "-- minValueY: " << minValueY << "   maxValueY: " << maxValueY << endl
00651         ;
00652 #endif
00653 
00654     // solving issue #4075 in a quick way:
00655     ptr->setPen ( PrintingParameters::scalePen( labelTA.pen() ) ); // perhaps we want to add a setter method later?
00656 
00657     //ptr->setPen ( Qt::black );
00658 
00659     const QObject* referenceArea = plane->parent();
00660 
00661     // that QVector contains all drawn x-ticks (so no subticks are drawn there also)
00662     QVector< int > drawnAbscissaTicks;
00663     // and that does the same for the y-ticks
00664     QVector< int > drawnYTicks;
00665 
00666     /*
00667      * Find out if it is a bar diagram
00668      * bar diagrams display their data per column
00669      * we need to handle the last label another way
00670      * 1 - Last label == QString null ( Header Labels )
00671      * 2 - Display labels and ticks in the middle of the column
00672      */
00673 
00674     const bool centerAbscissaTicks = referenceDiagramNeedsCenteredAbscissaTicks( d->diagram() );
00675 
00676     // this draws the unit rulers
00677     if ( drawUnitRulers ) {
00678         const QStringList labelsList(      labels() );
00679         const QStringList shortLabelsList( shortLabels() );
00680         const int hardLabelsCount  = labelsList.count();
00681         const int shortLabelsCount = shortLabelsList.count();
00682         bool useShortLabels = false;
00683 
00684         bool useConfiguredStepsLabels = false;
00685         QStringList headerLabels;
00686         if( useItemCountLabels ){
00687             //qDebug() << (isOrdinate() ? "is Ordinate" : "is Abscissa");
00688             headerLabels =
00689                 isOrdinate()
00690                 ? d->diagram()->datasetLabels()
00691                 : d->diagram()->itemRowLabels();
00692             //qDebug() << numberOfUnitRulers;
00693             // check if configured stepWidth
00694             useConfiguredStepsLabels = isAbscissa() &&
00695                     dimX.stepWidth &&
00696                     (( (headerLabels.count() - 1)/ dimX.stepWidth ) != numberOfUnitRulers);
00697             if( useConfiguredStepsLabels ) {
00698                 numberOfUnitRulers = ( headerLabels.count() - 1 )/ dimX.stepWidth;
00699                 // we need to register data values for the steps
00700                 // in case it is configured by the user
00701                 QStringList configuredStepsLabels;
00702                 double value = dimX.start;// headerLabels.isEmpty() ? 0.0 : headerLabels.first().toDouble();
00703                 configuredStepsLabels << QString::number( value );
00704 
00705                 for( int i = 0; i < numberOfUnitRulers; i++ )
00706                 {
00707                     //qDebug() << value;
00708                     value += dimX.stepWidth;
00709                     configuredStepsLabels.append( d->diagram()->unitPrefix( i, diagramIsVertical ? Qt::Horizontal : Qt::Vertical, true ) +
00710                                                   QString::number( value ) +
00711                                                   d->diagram()->unitSuffix( i, diagramIsVertical ? Qt::Horizontal : Qt::Vertical, true ) );
00712                 }
00713                 headerLabels = configuredStepsLabels;
00714             }
00715 
00716             if (  centerAbscissaTicks )
00717                 headerLabels.append( QString() );
00718         }
00719         RulerAttributes rulerAttr = rulerAttributes();
00720         if ( rulerAttr.showRulerLine() )
00721         {
00722             QPointF start;
00723             QPointF end;
00724             switch( position() )
00725             {
00726             case( CartesianAxis::Bottom ):
00727                 start = QPointF( dimX.start, dimY.start );
00728                 end = QPointF( dimX.end, dimY.start );
00729                 break;
00730             case( CartesianAxis::Top ):
00731                 start = QPointF( dimX.start, dimY.end );
00732                 end = QPointF( dimX.end, dimY.end );
00733                 break;
00734             case( CartesianAxis::Left ):
00735                 start = QPointF( dimX.start, dimY.start );
00736                 end = QPointF( dimX.start, dimY.end );
00737                 break;
00738             case( CartesianAxis::Right ):
00739                 start = QPointF( dimX.end, dimY.start );
00740                 end = QPointF( dimX.end, dimY.end );
00741                 break;
00742             }
00743             start = plane->translate( start );
00744             end = plane->translate( end );
00745             bool clip = context->painter()->hasClipping();
00746             context->painter()->setClipping( false );
00747             context->painter()->drawLine( start, end );
00748             context->painter()->setClipping( clip );
00749 
00750         }
00751 
00752         const int headerLabelsCount = headerLabels.count();
00753         //qDebug() << "headerLabelsCount" << headerLabelsCount;
00754 
00755         TextLayoutItem* labelItem =
00756             drawLabels
00757             ? new TextLayoutItem( QString::number( minValueY ),
00758                                   labelTA,
00759                                   referenceArea,
00760                                   KDChartEnums::MeasureOrientationMinimum,
00761                                   Qt::AlignLeft )
00762             : 0;
00763         labelItem->setTextAttributes( textAttributes() );
00764         TextLayoutItem* labelItem2 =
00765             drawLabels
00766             ? new TextLayoutItem( QString::number( minValueY ),
00767                                   labelTA,
00768                                   referenceArea,
00769                                   KDChartEnums::MeasureOrientationMinimum,
00770                                   Qt::AlignLeft )
00771             : 0;
00772         labelItem2->setTextAttributes( textAttributes() );
00773         const QFontMetricsF met(
00774             drawLabels
00775             ? labelItem->realFont()
00776             : QFontMetricsF( QApplication::font(), GlobalMeasureScaling::paintDevice() ) );
00777         const qreal halfFontHeight = rulerAttr.labelMargin() >= 0 ? rulerAttr.labelMargin() : met.height() * 0.5;
00778         const qreal halfFontWidth = rulerAttr.labelMargin() >= 0 ? rulerAttr.labelMargin() : met.averageCharWidth() * 0.5;
00779 
00780         if ( isAbscissa() ) {
00781             //Draw ticks at custom postions on x-axis
00782             if( !d->customTicksPositions.isEmpty() )
00783             {
00784                 const QList< double > values = d->customTicksPositions;
00785                 KDAB_FOREACH( const double v, values )
00786                 {
00787                    QPointF topPoint = diagramIsVertical ? QPointF( v, 0.0 ) : QPointF( 0.0, v );
00788                    QPointF bottomPoint = topPoint;
00789                    topPoint = plane->translate( topPoint );
00790                    bottomPoint = plane->translate( bottomPoint );
00791                    if ( diagramIsVertical ) {
00792                        topPoint.setY( rulerRef.y() + tickLength() );
00793                        bottomPoint.setY( rulerRef.y() );
00794                    } else {
00795                        topPoint.setX( rulerRef.x() + tickLength() );
00796                        bottomPoint.setX( rulerRef.x() );
00797                    }
00798 
00799                    context->painter()->drawLine(topPoint, bottomPoint);
00800                }
00801             }
00802 
00803             if( !d->annotations.isEmpty() )
00804             {
00805                 const QList< double > values = d->annotations.keys();
00806                 KDAB_FOREACH( const double v, values )
00807                 {
00808                    QPointF topPoint = diagramIsVertical ? QPointF( v, 0.0 ) : QPointF( 0.0, v );
00809                    QPointF bottomPoint = topPoint;
00810                    topPoint = plane->translate( topPoint );
00811                    bottomPoint = plane->translate( bottomPoint );
00812                    if ( diagramIsVertical ) {
00813                        topPoint.setY( rulerRef.y() + tickLength() );
00814                        bottomPoint.setY( rulerRef.y() );
00815                    } else {
00816                        topPoint.setX( rulerRef.x() + tickLength() );
00817                        bottomPoint.setX( rulerRef.x() );
00818                    }
00819 
00820                    labelItem->setText( d->annotations[ v ] );
00821                    const QSize size( labelItem->sizeHint() );
00822                    if ( diagramIsVertical ) {
00823                     labelItem->setGeometry(
00824                         QRect(
00825                             QPoint(
00826                                 static_cast<int>( topPoint.x() - size.width() / 2.0 ),
00827                                 static_cast<int>( topPoint.y() +
00828                                                 ( position() == Bottom
00829                                                     ? halfFontHeight
00830                                                     : ((halfFontHeight + size.height()) * -1.0) ) ) ),
00831                             size ) );
00832                    } else {
00833                     labelItem->setGeometry(
00834                         QRect(
00835                             QPoint(
00836                                 static_cast<int>( bottomPoint.x() +
00837                                                 ( position() == Right
00838                                                     ? halfFontWidth
00839                                                     : (-halfFontWidth - size.width()) ) ),
00840 
00841                                 static_cast<int>( topPoint.y() - ( size.height() ) * 0.5 ) ),
00842                             size ) );
00843                    }
00844 
00845                    QRect labelGeo = labelItem->geometry();
00846                    // if our item would only half fit, we disable clipping for that one
00847                    if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() )
00848                        ptr->setClipping( false );
00849                    else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() )
00850                        ptr->setClipping( false );
00851 
00852                    labelItem->paint( ptr );
00853                 }
00854             }
00855 
00856             qreal labelDiff = dimX.stepWidth;
00857             const int precision = ( QString::number( labelDiff ).section( QLatin1Char('.'), 1,  2 ) ).length();
00858 
00859             // If we have a labels list AND a short labels list, we first find out,
00860             // if there is enough space for showing ALL of the long labels:
00861             // If not, use the short labels.
00862             if( drawLabels && hardLabelsCount > 0 && shortLabelsCount > 0 && d->annotations.isEmpty() ){
00863                 bool labelsAreOverlapping = false;
00864                 int iLabel = 0;
00865                 qreal i = minValueX;
00866                 while ( i < maxValueX-1 && !labelsAreOverlapping )
00867                 {
00868                     const int idx = (iLabel < hardLabelsCount    ) ? iLabel     : 0;
00869                     const int idx2= (iLabel < hardLabelsCount - 1) ? iLabel + 1 : 0;
00870                     if ( dimX.stepWidth != 1.0 && ! dim.isCalculated )
00871                     {
00872                         // Check intersects for the header label - we need to pass the full string
00873                         // here and not only the i value.
00874                         if( useConfiguredStepsLabels ){
00875                             labelItem->setText( customizedLabel(headerLabels[ idx ]) );
00876                             labelItem2->setText( customizedLabel(headerLabels[ idx2 ]) );
00877                         }else{
00878                             //qDebug() << "i + labelDiff " << i + labelDiff;
00879                             labelItem->setText( customizedLabel(headerLabelsCount > i && i >= 0 ?
00880                                     headerLabels[static_cast<int>(i)] :
00881                                     QString::number( i, 'f', precision )) );
00882                             //           qDebug() << "1 - labelItem->text() " << labelItem->text();
00883                             //qDebug() << "labelDiff" << labelDiff
00884                             //        << "  index" << i+labelDiff << "  count" << headerLabelsCount;
00885                             labelItem2->setText( customizedLabel(headerLabelsCount > i + labelDiff && i + labelDiff >= 0 ?
00886                                     headerLabels[static_cast<int>(i+labelDiff)] :
00887                                     QString::number( i + labelDiff, 'f', precision )) );
00888                             //qDebug() << "2 - labelItem->text() " << labelItem->text();
00889                         }
00890                     } else {
00891                         //qDebug() << iLabel << i << "("<<hardLabelsCount<<")   :";
00892                         const int shortIdx =  (iLabel < shortLabelsCount    ) ? iLabel     : 0;
00893                         const int shortIdx2 = (iLabel < shortLabelsCount - 1) ? iLabel + 1 : 0;
00894                         labelItem->setText(  customizedLabel(
00895                                 useShortLabels ? shortLabelsList[ shortIdx ] : labelsList[ idx ] ) );
00896                         labelItem2->setText( customizedLabel(
00897                                 useShortLabels ? shortLabelsList[ shortIdx2 ] : labelsList[ idx2 ] ) );
00898                     }
00899 
00900                     QPointF firstPos = diagramIsVertical ? QPointF( i, 0.0 ) : QPointF( 0.0, i );
00901                     firstPos = plane->translate( firstPos );
00902 
00903                     QPointF secondPos = diagramIsVertical ? QPointF( i + labelDiff, 0.0 ) : QPointF( 0.0, i + labelDiff );
00904                     secondPos = plane->translate( secondPos );
00905 
00906                     labelsAreOverlapping = labelItem->intersects( *labelItem2, firstPos, secondPos );
00907 
00908                     if ( ++iLabel > hardLabelsCount - 1 )
00909                         iLabel = 0;
00910                     if ( isLogarithmicX )
00911                         i *= 10.0;
00912                     else
00913                         i += dimX.stepWidth;
00914                     //qDebug() << labelsAreOverlapping << iLabel << i << labelsAreOverlapping << firstPos << secondPos.x()-firstPos .x() << labelItem->text() << labelItem2->text();
00915                 }
00916 
00917                 useShortLabels = labelsAreOverlapping;
00918             }
00919 
00920             //      qDebug() << "initial labelDiff " << labelDiff;
00921             if ( drawLabels && d->annotations.isEmpty() )
00922             {
00923                 qreal i = minValueX;
00924                 int iLabel = 0;
00925 
00926                 while ( i + labelDiff < maxValueX )
00927                 {
00928                     const int idx = (iLabel < hardLabelsCount    ) ? iLabel     : 0;
00929                     const int idx2= (iLabel < hardLabelsCount - 1) ? iLabel + 1 : 0;
00930                     //qDebug() << "drawLabels" << drawLabels << "  hardLabelsCount" << hardLabelsCount
00931                     //        << "  dimX.stepWidth" << dimX.stepWidth << "  dim.isCalculated" << dim.isCalculated;
00932                     if ( !drawLabels || hardLabelsCount < 1 || ( dimX.stepWidth != 1.0 && ! dim.isCalculated ) )
00933                     {
00934                         // Check intersects for the header label - we need to pass the full string
00935                         // here and not only the i value.
00936                         if( useConfiguredStepsLabels ){
00937                             labelItem->setText( customizedLabel(headerLabels[ idx ]) );
00938                             labelItem2->setText( customizedLabel(headerLabels[ idx2 ]) );
00939                         }else{
00940                             //qDebug() << "i + labelDiff " << i + labelDiff;
00941                             labelItem->setText( customizedLabel(headerLabelsCount > i && i >= 0 ?
00942                                     headerLabels[static_cast<int>(i)] :
00943                                     QString::number( i, 'f', precision )) );
00944                             //           qDebug() << "1 - labelItem->text() " << labelItem->text();
00945                             //qDebug() << "labelDiff" << labelDiff
00946                             //        << "  index" << i+labelDiff << "  count" << headerLabelsCount;
00947                             labelItem2->setText( customizedLabel(headerLabelsCount > i + labelDiff && i + labelDiff >= 0 ?
00948                                     headerLabels[static_cast<int>(i+labelDiff)] :
00949                                     QString::number( i + labelDiff, 'f', precision )) );
00950                             //qDebug() << "2 - labelItem->text() " << labelItem->text();
00951                         }
00952                     } else {
00953                         const int shortIdx =  (iLabel < shortLabelsCount    ) ? iLabel     : 0;
00954                         const int shortIdx2 = (iLabel < shortLabelsCount - 1) ? iLabel + 1 : 0;
00955                         labelItem->setText(  customizedLabel(
00956                                 useShortLabels ? shortLabelsList[ shortIdx ] : labelsList[ idx ] ) );
00957                         labelItem2->setText( customizedLabel(
00958                                 useShortLabels ? shortLabelsList[ shortIdx2 ] : labelsList[ idx2 ] ) );
00959                     }
00960 
00961                     QPointF firstPos = diagramIsVertical ? QPointF( i, 0.0 ) : QPointF( 0.0, i );
00962                     firstPos = plane->translate( firstPos );
00963 
00964                     QPointF secondPos = diagramIsVertical ? QPointF( i + labelDiff, 0.0 ) : QPointF( 0.0, i + labelDiff );
00965                     secondPos = plane->translate( secondPos );
00966 
00967                     if ( labelItem->intersects( *labelItem2, firstPos, secondPos ) )
00968                     {
00969                         i = minValueX;
00970 
00971                         // fix for issue #4179:
00972                         labelDiff *= 10.0;
00973                         // old code: labelDiff += labelDiff;
00974 
00975                         iLabel = 0;
00976                         //qDebug() << firstPos << secondPos.x()-firstPos .x() << labelItem->text() << labelItem2->text() << labelDiff;
00977                     }
00978                     else
00979                     {
00980                         i += labelDiff;
00981                         //qDebug() << firstPos << secondPos.x()-firstPos .x() << labelItem->text() << labelItem2->text();
00982                     }
00983 
00984                     if ( (++iLabel > hardLabelsCount - 1) && !useConfiguredStepsLabels )
00985                     {
00986                         iLabel = 0;
00987                     }
00988                 }
00989                 // fixing bugz issue #5018 without breaking issue #4179:
00990                 if( minValueX + labelDiff > maxValueX )
00991                     labelDiff = maxValueX - minValueX;
00992                 // This makes sure the first and the last X label are drawn
00993                 // if there is not enouth place to draw some more of them
00994                 // according to labelDiff calculation performed above.
00995             }
00996 
00997             int idxLabel = 0;
00998             qreal iLabelF = minValueX;
00999             //qDebug() << iLabelF;
01000             qreal i = minValueX;
01001             qreal labelStep = 0.0;
01002             //    qDebug() << "dimX.stepWidth:" << dimX.stepWidth  << "labelDiff:" << labelDiff;
01003             //dimX.stepWidth = 0.5;
01004             while( i <= maxValueX && d->annotations.isEmpty() )
01005             {
01006                 // Line charts: we want the first tick to begin at 0.0 not at 0.5 otherwise labels and
01007                 // values does not fit each others
01008                 QPointF topPoint = diagramIsVertical ? QPointF( i + ( centerAbscissaTicks ? 0.5 : 0.0 ), 0.0 ) : QPointF( 0.0, i + ( centerAbscissaTicks ? 0.5 : 0.0 ) );
01009                 QPointF bottomPoint ( topPoint );
01010                 topPoint = plane->translate( topPoint );
01011                 bottomPoint = plane->translate( bottomPoint );
01012                 if ( diagramIsVertical ) {
01013                     topPoint.setY( rulerRef.y() + tickLength() );
01014                     bottomPoint.setY( rulerRef.y() );
01015                 } else {
01016                     bottomPoint.setX( rulerRef.x() - (position() == Left ? tickLength() : -tickLength()) );
01017                     topPoint.setX( rulerRef.x() );
01018                 }
01019 
01020                 const qreal translatedValue = diagramIsVertical ? topPoint.x() : topPoint.y();
01021                 bool bIsVisibleLabel;
01022                 if ( diagramIsVertical )
01023                     bIsVisibleLabel = ( (translatedValue >= geoRect.left() && translatedValue <= geoRect.right() && !isLogarithmicX) || i != 0.0 );
01024                 else
01025                     bIsVisibleLabel = ( (translatedValue >= geoRect.top() && translatedValue <= geoRect.bottom() && !isLogarithmicX) || i != 0.0 );
01026 
01027                 // fix for issue #4179:
01028                 bool painttick = bIsVisibleLabel && labelStep <= 0;
01029                 // old code: bool painttick = true;
01030 
01031                 //Dont paint more ticks than we need
01032                 //when diagram type is Bar
01033                 if (  centerAbscissaTicks && i == maxValueX )
01034                     painttick = false;
01035 
01036                 if ( bIsVisibleLabel && painttick ) {
01037                     ptr->save();
01038                     if ( rulerAttr.hasTickMarkPenAt( i ) )
01039                         ptr->setPen( rulerAttr.tickMarkPen( i ) );
01040                     else
01041                         ptr->setPen( rulerAttr.majorTickMarkPen() );
01042                     ptr->drawLine( topPoint, bottomPoint );
01043                     ptr->restore();
01044                 }
01045 
01046                 drawnAbscissaTicks.append( static_cast<int>( diagramIsVertical ? topPoint.x() : topPoint.y() ) );
01047                 if( drawLabels ) {
01048                     if( bIsVisibleLabel ){
01049                         if ( isLogarithmicX )
01050                             labelItem->setText( customizedLabel(QString::number( i ) ) );
01051                         /* We don't need that
01052                         * it causes header labels to be skipped even if there is enough
01053                         * space for them to displayed.
01054                         * Commenting for now - I need to test more in details - Let me know if I am wrong here.
01055                         */
01056                         /*
01057                         else if( (dimX.stepWidth != 1.0) && ! dimX.isCalculated ) {
01058                         labelItem->setText( customizedLabel(QString::number( i, 'f', 0 )) );
01059                         }
01060                         */
01061                         else {
01062                             int idx = idxLabel + static_cast<int>(minValueX);
01063                             if( hardLabelsCount ){
01064                                 if( useShortLabels ){
01065                                     if( idx >= shortLabelsList.count() )
01066                                         idx = 0;
01067                                 }else{
01068                                     if( idx >= labelsList.count() )
01069                                         idx = 0;
01070                                 }
01071                             }
01072                             labelItem->setText(
01073                                     customizedLabel(
01074                                           hardLabelsCount
01075                                     ? ( useShortLabels    ? shortLabelsList[ idx ] : labelsList[ idx ] )
01076                                 : ( headerLabelsCount ? headerLabels[ idx ] : QString::number( iLabelF ))));
01077                             //qDebug() << "x - labelItem->text() " << labelItem->text() << headerLabelsCount;
01078                         }
01079                         // No need to call labelItem->setParentWidget(), since we are using
01080                         // the layout item temporarily only.
01081                         if( labelStep <= 0 ) {
01082                             const PainterSaver p( ptr );
01083                             //const QSize size( labelItem->sizeHint() );
01084                             QPoint topLeft, topRight, bottomRight, bottomLeft;
01085                             const QSize size(
01086                                     labelItem->sizeHintAndRotatedCorners(
01087                                             topLeft, topRight, bottomRight, bottomLeft) );
01088                             const QSize sizeUnrotated( labelItem->sizeHintUnrotated() );
01089                             const int rotation = labelTA.rotation();
01090                             const bool rotPositive = (rotation > 0 && rotation < 180);
01091                             QPoint midOfSide(0,0);
01092                             int dX = 0;
01093                             int dY = 0;
01094                             if( rotation ){
01095                                 if( rotPositive ){
01096                                     midOfSide = (topLeft + bottomLeft)  / 2;
01097                                     dX = topLeft.x() - midOfSide.x();
01098                                     dY = bottomLeft.y() - midOfSide.y();
01099                                 }else{
01100                                     midOfSide = (topRight + bottomRight) / 2;
01101                                     dX = midOfSide.x() - topLeft.x();
01102                                     dY = midOfSide.y() - topRight.y();
01103                                 }
01104                             }
01105                             /*
01106                             if( i == 2 ){
01107                                 qDebug()<<"------"<<size<<topPoint<<topLeft<<topRight<<bottomRight<<bottomLeft<<"   m:"<<midOfSide<<" dx"<<dX<<" dy"<<dY;
01108                                 ptr->setPen( Qt::black );
01109                                 QRectF rect(topPoint, QSizeF(sizeUnrotated));
01110                                 ptr->drawRect( rect );
01111                                 ptr->drawRect( QRectF(topPoint, QSizeF(2,2)) );
01112                                 ptr->drawRect( QRectF(topPoint+topLeft, QSizeF(2,2)) );
01113                                 ptr->drawRect( QRectF(topPoint+bottomLeft, QSizeF(2,2)) );
01114                                 ptr->drawRect( QRectF(topPoint+bottomRight, QSizeF(2,2)) );
01115                                 ptr->drawRect( QRectF(topPoint+topRight, QSizeF(2,2)) );
01116                                 ptr->drawRect( QRectF(topPoint+midOfSide, QSizeF(2,2)) );
01117                                 ptr->setPen( Qt::green );
01118                                 rect = QRectF(topPoint, QSizeF(size));
01119                                 ptr->drawRect( rect );
01120                                 ptr->drawRect( QRectF(QPointF((rect.topLeft()  + rect.bottomLeft())  / 2.0 - QPointF(2.0,2.0)), QSizeF(3.0,3.0)) );
01121                                 //ptr->drawRect( QRectF(QPointF((rect.topRight() + rect.bottomRight()) / 2.0 - QPointF(2.0,2.0)), QSizeF(3.0,3.0)) );
01122                             }
01123                             */
01124                             QPoint topLeftPt;
01125                             if( diagramIsVertical ){
01126                                 if( rotation ){
01127                                     topLeftPt = QPoint(
01128                                         static_cast<int>( topPoint.x() ) - dX,
01129                                         static_cast<int>( topPoint.y()   - dY +
01130                                                         ( position() == Bottom
01131                                                             ? halfFontHeight
01132                                                             : ((halfFontHeight + size.height()) * -1.0) ) ) );
01133                                 }else{
01134                                     topLeftPt = QPoint(
01135                                         static_cast<int>( topPoint.x() - size.width() / 2.0 ),
01136                                         static_cast<int>( topPoint.y() +
01137                                                         ( position() == Bottom
01138                                                             ? halfFontHeight
01139                                                             : ((halfFontHeight + size.height()) * -1.0) ) ) );
01140                                 }
01141                             }else{
01142                                 if( rotation ){
01143                                     topLeftPt = QPoint(
01144                                         static_cast<int>( topPoint.x() ) + dX,
01145                                         static_cast<int>( topPoint.y()   - dY +
01146                                                         ( position() == Bottom
01147                                                             ? halfFontHeight
01148                                                             : ((halfFontHeight + size.height()) * -1.0) ) ) );
01149                                 }else{
01150                                     topLeftPt = QPoint(
01151                                         static_cast<int>( bottomPoint.x() +
01152                                                             ( position() == Right
01153                                                                 ? halfFontWidth
01154                                                                 : (-halfFontWidth - size.width()) ) ),
01155                                         static_cast<int>( topPoint.y() - ( size.height() ) * 0.5 ) );
01156                                 }
01157                             }
01158                             labelItem->setGeometry( QRect(topLeftPt, size) );
01159 
01160                             QRect labelGeo = labelItem->geometry();
01161                             //ptr->drawRect(labelGeo);
01162                             // if our item would only half fit, we disable clipping for that one
01163                             if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() )
01164                                 ptr->setClipping( false );
01165                             else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() )
01166                                 ptr->setClipping( false );
01167 
01168                             if( !isLogarithmicX )
01169                                 labelStep = labelDiff - dimX.stepWidth;
01170 
01171                             // if our item would only half fit, we disable clipping for that one
01172                             if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() )
01173                                 ptr->setClipping( false );
01174                             else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() )
01175                                 ptr->setClipping( false );
01176 
01177                             labelItem->paint( ptr );
01178 
01179                             // do not call customizedLabel() again:
01180                             labelItem2->setText( labelItem->text() );
01181 
01182                         } else {
01183                             labelStep -= dimX.stepWidth;
01184                         }
01185                     }
01186 
01187                     if( hardLabelsCount ) {
01188                         if( useShortLabels && idxLabel >= shortLabelsCount - 1 )
01189                             idxLabel = 0;
01190                         else if( !useShortLabels && idxLabel >= hardLabelsCount - 1 )
01191                             idxLabel = 0;
01192                         else{
01193                             idxLabel += static_cast<int>(dimX.stepWidth);
01194                             //qDebug() << "dimX.stepWidth:" << dimX.stepWidth << "  idxLabel:" << idxLabel;
01195                         }
01196                     } else if( headerLabelsCount ) {
01197                         if( ++idxLabel > headerLabelsCount - 1 ) {
01198                             idxLabel = 0;
01199                         }
01200                     } else {
01201                         iLabelF += dimX.stepWidth;
01202                     }
01203                 }
01204                 if ( isLogarithmicX )
01205                 {
01206                     i *= 10.0;
01207                     if( i == 0.0 )
01208                     {
01209                         const qreal j = dimensions.first().start;
01210                         i = j == 0.0 ? 1.0 : pow( 10.0, floor( log10( j ) ) );
01211                     }
01212                 }
01213                 else
01214                 {
01215                     i += dimX.stepWidth;
01216                 }
01217             }
01218         } else {
01219             const PainterSaver p( ptr );
01220             const double maxLimit = maxValueY;
01221             const double steg = dimY.stepWidth;
01222             int maxLabelsWidth = 0;
01223             qreal labelValue;
01224 
01225             if( drawLabels && position() == Right ){
01226                 // Find the widest label, so we to know how much we need to right-shift
01227                 // our labels, to get them drawn right aligned:
01228                 labelValue = minValueY;
01229                 while ( labelValue <= maxLimit ) {
01230                     const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) +
01231                                               QString::number( labelValue ) +
01232                                               diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true );
01233                     labelItem->setText( customizedLabel( labelText ) );
01234                     maxLabelsWidth = qMax( maxLabelsWidth, diagramIsVertical ? labelItem->sizeHint().width() : labelItem->sizeHint().height() );
01235                     calculateNextLabel( labelValue, steg, isLogarithmicY, dimensions.last().start );
01236 
01237                     if(maxValueY == 0 && minValueY == 0)
01238                        break;
01239                 }
01240             }
01241 
01242             labelValue = minValueY;
01243             qreal step = steg;
01244             bool nextLabel = false;
01245             //qDebug("minValueY: %f   maxLimit: %f   steg: %f", minValueY, maxLimit, steg);
01246 
01247             //Draws custom tick marks in the y-axis
01248             if( !d->customTicksPositions.isEmpty() )
01249             {
01250                 const QList< double > values = d->customTicksPositions;
01251                 KDAB_FOREACH( const double value, values )
01252                 {
01253                     QPointF annoPoint = (diagramIsVertical ? QPointF( 0.0, value ) : QPointF( value, 0.0 ));
01254                     QPointF leftPoint = plane->translate( annoPoint );
01255                     QPointF rightPoint = plane->translate( annoPoint );
01256 
01257                     if ( diagramIsVertical ) {
01258                         leftPoint.setX( rulerRef.x() + tickLength() );
01259                         rightPoint.setX( rulerRef.x() );
01260                     } else {
01261                         leftPoint.setY( rulerRef.y() + ((position() == Bottom) ? tickLength() : -tickLength()) );
01262                         rightPoint.setY( rulerRef.y() );
01263                     }
01264 
01265                     context->painter()->drawLine(leftPoint, rightPoint);
01266                 }
01267             }
01268 
01269             if( drawLabels )
01270             {
01271                 // first calculate the steps depending on labels colision
01272                 while( labelValue <= maxLimit ) {
01273                     QPointF leftPoint = plane->translate( diagramIsVertical ? QPointF( 0, labelValue ) : QPointF( labelValue, 0 ) );
01274                     const qreal translatedValue = diagramIsVertical ? leftPoint.y() : leftPoint.x();
01275                     //qDebug() << "geoRect:" << geoRect << "   geoRect.top()" << geoRect.top() << "geoRect.bottom()" << geoRect.bottom() << "  translatedValue:" << translatedValue;
01276                     const bool bTranslatedValueIsWithinRange = diagramIsVertical ? translatedValue > geoRect.top() && translatedValue <= geoRect.bottom()
01277                                                                                  : translatedValue > geoRect.left() && translatedValue <= geoRect.right();
01278                     if( bTranslatedValueIsWithinRange ){
01279                         const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) +
01280                                                   QString::number( labelValue ) +
01281                                                   diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true );
01282                         const QString label2Text = diagram()->unitPrefix( static_cast< int >( labelValue + step ), diagramOrientation, true ) +
01283                                                    QString::number( labelValue + step ) +
01284                                                    diagram()->unitSuffix( static_cast< int >( labelValue + step ), diagramOrientation, true );
01285                         labelItem->setText(  customizedLabel( labelText ) );
01286                         labelItem2->setText( customizedLabel( QString::number( labelValue + step ) ) );
01287                         QPointF nextPoint = plane->translate(  diagramIsVertical ? QPointF( 0,  labelValue + step ) : QPointF( labelValue + step, 0 ) );
01288                         if ( labelItem->intersects( *labelItem2, leftPoint, nextPoint ) )
01289                         {
01290                             step += steg;
01291                             nextLabel = false;
01292                         }else{
01293                             nextLabel = true;
01294                         }
01295                     }else{
01296                         nextLabel = true;
01297                     }
01298 
01299                     if ( nextLabel || isLogarithmicY )
01300                         calculateNextLabel( labelValue, step, isLogarithmicY, dimensions.last().start );
01301                     else
01302                         labelValue = minValueY;
01303                 }
01304 
01305                 // Second - Paint the labels
01306                 labelValue = minValueY;
01307                 //qDebug() << "axis labels starting at" << labelValue << "step width" << step;
01308                 if( !d->annotations.isEmpty() )
01309                 {
01310                     const QList< double > annotations = d->annotations.keys();
01311                     KDAB_FOREACH( const double annotation, annotations )
01312                     {
01313                         QPointF annoPoint = (diagramIsVertical ? QPointF( 0.0, annotation ) : QPointF( annotation, 0.0 ));
01314                         QPointF leftPoint = plane->translate( annoPoint );
01315                         QPointF rightPoint = plane->translate( annoPoint );
01316 
01317                         if ( diagramIsVertical ) {
01318                             leftPoint.setX( rulerRef.x() + tickLength() );
01319                             rightPoint.setX( rulerRef.x() );
01320                         } else {
01321                             leftPoint.setY( rulerRef.y() + ((position() == Bottom) ? tickLength() : -tickLength()) );
01322                             rightPoint.setY( rulerRef.y() );
01323                         }
01324 
01325                         const qreal translatedValue = diagramIsVertical ? rightPoint.y() : rightPoint.x();
01326                         const bool bIsVisibleLabel = diagramIsVertical ?
01327                                 ( (translatedValue >= geoRect.top() && translatedValue <= geoRect.bottom() && !isLogarithmicY) || labelValue != 0.0 )
01328                               : ( (translatedValue >= geoRect.left() && translatedValue <= geoRect.right() && !isLogarithmicY) || labelValue != 0.0 );
01329 
01330                         if( bIsVisibleLabel )
01331                         {
01332                             ptr->save();
01333                             if ( rulerAttr.hasTickMarkPenAt( annotation ) )
01334                                 ptr->setPen( rulerAttr.tickMarkPen( annotation ) );
01335                             else
01336                                 ptr->setPen( rulerAttr.majorTickMarkPen() );
01337                             ptr->drawLine( leftPoint, rightPoint );
01338                             ptr->restore();
01339 
01340                             labelItem->setText( d->annotations[ annotation ] );
01341                             const QSize labelSize( labelItem->sizeHint() );
01342                             int x, y;
01343                             if ( diagramIsVertical ) {
01344                                 x = static_cast<int>( leftPoint.x() + met.height() * ( position() == Left ? -0.5 : 0.5)
01345                                     - ( position() == Left ? labelSize.width() : 0.0 ) );
01346                                 y = static_cast<int>( leftPoint.y() - ( met.ascent() + met.descent() ) * 0.6 );
01347                             } else {
01348                                 const qreal halfFontHeight = met.height() * 0.5;
01349                                 x = static_cast<int>( leftPoint.x() - labelSize.width() * 0.5 );
01350                                 y = static_cast<int>( (position() == Bottom ? leftPoint.y() : rightPoint.y()) +
01351                                                                 + ( position() == Bottom ? halfFontHeight : -(halfFontHeight + labelSize.height()) ) );
01352                             }
01353                             labelItem->setGeometry( QRect( QPoint( x, y ), labelSize ) );
01354 
01355                             QRect labelGeo = labelItem->geometry();
01356                             // if our item would only half fit, we disable clipping for that one
01357                             if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() )
01358                                 ptr->setClipping( false );
01359                             else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() )
01360                                 ptr->setClipping( false );
01361 
01362                             labelItem->paint( ptr );
01363                         }
01364                     }
01365                 }
01366                 else
01367                 {
01368                     while( labelValue <= maxLimit ) {
01369                         //qDebug() << "value now" << labelValue;
01370                         const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) +
01371                                                   QString::number( labelValue ) +
01372                                                   diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true );
01373                         labelItem->setText( customizedLabel( labelText ) );
01374                         QPointF leftPoint = plane->translate( diagramIsVertical ? QPointF( 0, labelValue ) : QPointF( labelValue, 0 ) );
01375                         QPointF rightPoint = leftPoint;
01376 
01377                         if ( diagramIsVertical ) {
01378                             leftPoint.setX( rulerRef.x() + tickLength() );
01379                             rightPoint.setX( rulerRef.x() );
01380                         } else {
01381                             leftPoint.setY( rulerRef.y() + ((position() == Bottom) ? tickLength() : -tickLength()) );
01382                             rightPoint.setY( rulerRef.y() );
01383                         }
01384 
01385                         bool bIsVisibleLabel;
01386                         const qreal translatedValue = diagramIsVertical ? rightPoint.y() : rightPoint.x();
01387                         if ( diagramIsVertical)
01388                             bIsVisibleLabel = ( (translatedValue >= geoRect.left() && translatedValue <= geoRect.right() && !isLogarithmicX) || labelValue != 0.0 );
01389                         else
01390                             bIsVisibleLabel = ( (translatedValue >= geoRect.top() && translatedValue <= geoRect.bottom() && !isLogarithmicX) || labelValue != 0.0 );
01391 
01392                         if( bIsVisibleLabel ){
01393                             ptr->save();
01394                             if ( rulerAttr.hasTickMarkPenAt( labelValue ) )
01395                                 ptr->setPen( rulerAttr.tickMarkPen( labelValue ) );
01396                             else
01397                                 ptr->setPen( rulerAttr.majorTickMarkPen() );
01398                             ptr->drawLine( leftPoint, rightPoint );
01399                             ptr->restore();
01400 
01401                             drawnYTicks.append( static_cast<int>( diagramIsVertical ? leftPoint.y() : leftPoint.x() ) );
01402                             const QSize labelSize( labelItem->sizeHint() );
01403 
01404                             int x, y;
01405                             if ( diagramIsVertical ) {
01406                                 x = static_cast<int>( leftPoint.x() + met.height() * ( position() == Left ? -0.5 : 0.5) )
01407                                     - ( position() == Left ? labelSize.width() : (labelSize.width() - maxLabelsWidth) );
01408                                 y = static_cast<int>( leftPoint.y() - ( met.ascent() + met.descent() ) * 0.6 );
01409                             } else {
01410                                 const qreal halfFontHeight = met.height() * 0.5;
01411                                 x = static_cast<int>( leftPoint.x() - labelSize.width() * 0.5 );
01412                                 y = static_cast<int>( (position() == Bottom ? leftPoint.y() : rightPoint.y()) +
01413                                                                 + ( position() == Bottom ? halfFontHeight : -(halfFontHeight + labelSize.height()) ) );
01414                             }
01415 
01416                             labelItem->setGeometry( QRect( QPoint( x, y ), labelSize ) );
01417                             const QRect labelGeo = labelItem->geometry();
01418                             const bool hadClipping = ptr->hasClipping();
01419                             if( labelGeo.top() < geoRect.top() && labelGeo.bottom() > geoRect.top() )
01420                                ptr->setClipping( false );
01421                             else if( labelGeo.top() < geoRect.bottom() && labelGeo.bottom() > geoRect.bottom() )
01422                                ptr->setClipping( false );
01423 
01424                             // if our item would only half fit, we disable clipping for that one
01425                             if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() )
01426                                 ptr->setClipping( false );
01427                             else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() )
01428                                 ptr->setClipping( false );
01429 
01430                             labelItem->paint( ptr );
01431                             ptr->setClipping( hadClipping );
01432                         }
01433 
01434                         //qDebug() << step;
01435                         calculateNextLabel( labelValue, step, isLogarithmicY, dimensions.last().start );
01436                     }
01437                 }
01438             }
01439         }
01440         delete labelItem;
01441         delete labelItem2;
01442     }
01443 
01444     // this draws the subunit rulers
01445     if ( drawSubUnitRulers && d->annotations.isEmpty() ) {
01446         ptr->save();
01447 
01448         d->drawSubUnitRulers( ptr, plane, dim, rulerRef, isAbscissa() ? drawnAbscissaTicks : drawnYTicks, diagramIsVertical, rulerAttr );
01449 
01450         ptr->restore();
01451     }
01452 
01453     if( ! titleText().isEmpty() ) {
01454         d->drawTitleText( ptr, plane, areaGeoRect );
01455     }
01456 
01457     //qDebug() << "KDChart::CartesianAxis::paintCtx() done.";
01458 }
01459 
01460 
01461 /* pure virtual in QLayoutItem */
01462 bool CartesianAxis::isEmpty() const
01463 {
01464     return false; // if the axis exists, it has some (perhaps default) content
01465 }
01466 /* pure virtual in QLayoutItem */
01467 Qt::Orientations CartesianAxis::expandingDirections() const
01468 {
01469     Qt::Orientations ret;
01470     switch ( position() )
01471     {
01472     case Bottom:
01473     case Top:
01474         ret = Qt::Horizontal;
01475         break;
01476     case Left:
01477     case Right:
01478         ret = Qt::Vertical;
01479         break;
01480     default:
01481         Q_ASSERT( false ); // all positions need to be handeld
01482         break;
01483     };
01484     return ret;
01485 }
01486 
01487 
01488 static void calculateOverlap( int i, int first, int last,
01489                               int measure,
01490                               bool centerAbscissaTicks,
01491                               int& firstOverlap, int& lastOverlap )
01492 {
01493     if( i == first ){
01494         if( centerAbscissaTicks ){
01495             //TODO(khz): Calculate the amount of left overlap
01496             //           for bar diagrams.
01497         }else{
01498             firstOverlap = measure / 2;
01499         }
01500     }
01501     // we test both bounds in on go: first and last might be equal
01502     if( i == last ){
01503         if( centerAbscissaTicks ){
01504             //TODO(khz): Calculate the amount of right overlap
01505             //           for bar diagrams.
01506         }else{
01507             lastOverlap = measure / 2;
01508         }
01509     }
01510 }
01511 
01512 
01513 void CartesianAxis::setCachedSizeDirty() const
01514 {
01515     d->cachedMaximumSize = QSize();
01516 }
01517 
01518 /* pure virtual in QLayoutItem */
01519 QSize CartesianAxis::maximumSize() const
01520 {
01521     if( ! d->cachedMaximumSize.isValid() )
01522         d->cachedMaximumSize = d->calculateMaximumSize();
01523     return d->cachedMaximumSize;
01524 }
01525 
01526 QSize CartesianAxis::Private::calculateMaximumSize() const
01527 {
01528     QSize result;
01529     if ( !diagram() )
01530         return result;
01531 
01532     const AbstractCartesianDiagram * dia = qobject_cast< const AbstractCartesianDiagram * >( diagram() );
01533     if( dia && dia->referenceDiagram() )
01534         dia = dia->referenceDiagram();
01535     const BarDiagram *barDiagram = qobject_cast< const BarDiagram* >( dia );
01536     const Qt::Orientation diagramOrientation = barDiagram != 0 ? barDiagram->orientation() : Qt::Vertical;
01537     const bool diagramIsVertical = diagramOrientation == Qt::Vertical;
01538 
01539     const TextAttributes labelTA = mAxis->textAttributes();
01540     const bool drawLabels = labelTA.isVisible();
01541 
01542     const TextAttributes titleTA( titleTextAttributesWithAdjustedRotation() );
01543     const bool drawTitle = titleTA.isVisible() && ! axis()->titleText().isEmpty();
01544 
01545     AbstractCoordinatePlane* plane = diagram()->coordinatePlane();
01546     //qDebug() << this<<"::maximumSize() uses plane geometry" << plane->geometry();
01547     QObject* refArea = plane->parent();
01548     TextLayoutItem labelItem( QString(), labelTA, refArea,
01549                               KDChartEnums::MeasureOrientationMinimum, Qt::AlignLeft );
01550     TextLayoutItem titleItem( axis()->titleText(), titleTA, refArea,
01551                               KDChartEnums::MeasureOrientationMinimum, Qt::AlignHCenter | Qt::AlignVCenter );
01552 
01553     const QFontMetrics fm( labelItem.realFont(), GlobalMeasureScaling::paintDevice() );
01554 
01555     const qreal labelGap =
01556         drawLabels
01557         ? ( (diagramIsVertical ? fm.height() : fm.averageCharWidth()) / 3.0)
01558         : 0.0;
01559     const QFontMetricsF titleFM = QFontMetricsF( titleItem.realFont(), GlobalMeasureScaling::paintDevice() );
01560     const qreal titleGap =
01561         drawTitle
01562         ? ( (diagramIsVertical ? titleFM.height() : titleFM.averageCharWidth()) / 3.0)
01563         : 0.0;
01564 
01565     if ( axis()->isAbscissa() ) {
01566         const bool centerAbscissaTicks = referenceDiagramNeedsCenteredAbscissaTicks(diagram());
01567         int leftOverlap = 0;
01568         int rightOverlap = 0;
01569 
01570         qreal w = diagramIsVertical ? 10.0 : 0.0;
01571         qreal h = diagramIsVertical ? 0.0 : 10.0;
01572         if( drawLabels ){
01573             // if there're no label strings, we take the biggest needed number as height
01574             if( !annotations.isEmpty() )
01575             {
01576                 const QStringList strings = annotations.values();
01577                 KDAB_FOREACH( const QString& string, strings )
01578                 {
01579                     labelItem.setText( string );
01580                     const QSize siz = labelItem.sizeHint();
01581                     if ( diagramIsVertical )
01582                         h = qMax( h, static_cast< qreal >( siz.height() ) );
01583                     else
01584                         w = qMax( w, static_cast< qreal >( siz.width() ) );
01585                 }
01586             }
01587             else if ( !axis()->labels().isEmpty() )
01588             {
01589                 // find the longest label text:
01590                 const int first=0;
01591                 const int last=axis()->labels().count()-1;
01592                 const QStringList labelsList( axis()->labels() );
01593                 for ( int i = first; i <= last; ++i )
01594                 {
01595                     labelItem.setText( axis()->customizedLabel(labelsList[ i ]) );
01596                     const QSize siz = labelItem.sizeHint();
01597                     //qDebug()<<siz;
01598                     if ( diagramIsVertical )
01599                         h = qMax( h, static_cast<qreal>(siz.height()) );
01600                     else
01601                         w = qMax( w, static_cast<qreal>(siz.width()) );
01602                     calculateOverlap( i, first, last, diagramIsVertical ? siz.width() : siz.height(), centerAbscissaTicks,
01603                                       leftOverlap, rightOverlap );
01604 
01605                 }
01606             }
01607             else
01608             {
01609                 QStringList headerLabels = diagram()->itemRowLabels();
01610                 const int headerLabelsCount = headerLabels.count();
01611                 if( headerLabelsCount ){
01612                     if( cachedHeaderLabels == headerLabels && ( diagramIsVertical ? cachedFontHeight == fm.height() : cachedFontWidth == fm.averageCharWidth() )) {
01613                         if ( diagramIsVertical )
01614                             h = cachedLabelHeight;
01615                         else
01616                             w = cachedLabelWidth;
01617                     } else {
01618                         cachedHeaderLabels = headerLabels;
01619                         if ( diagramIsVertical )
01620                             cachedFontWidth = fm.averageCharWidth();
01621                         else
01622                             cachedFontHeight = fm.height();
01623                         const bool useFastCalcAlgorithm
01624                             = (strcmp( axis()->metaObject()->className(), "KDChart::CartesianAxis" ) == 0);
01625                         const int first=0;
01626                         const int last=headerLabelsCount-1;
01627                         for ( int i = first;
01628                             i <= last;
01629                             i = (useFastCalcAlgorithm && i < last) ? last : (i+1) )
01630                         {
01631                             labelItem.setText( axis()->customizedLabel(headerLabels[ i ]) );
01632                             const QSize siz = labelItem.sizeHint();
01633                             if ( diagramIsVertical ) {
01634                                 h = qMax( h, static_cast<qreal>(siz.height()) );
01635                                 cachedLabelHeight = h;
01636                             } else {
01637                                 cachedLabelWidth = w;
01638                                 w = qMax( w, static_cast<qreal>(siz.width()) );
01639                             }
01640                             calculateOverlap( i, first, last, diagramIsVertical ? siz.width() : siz.height(), centerAbscissaTicks,
01641                                             leftOverlap, rightOverlap );
01642                         }
01643                     }
01644                 }else{
01645                     labelItem.setText(
01646                             axis()->customizedLabel(
01647                                     QString::number( diagramIsVertical ? plane->gridDimensionsList().first().end
01648                                                                        : plane->gridDimensionsList().last().end, 'f', 0 )));
01649                     const QSize siz = labelItem.sizeHint();
01650                     if ( diagramIsVertical )
01651                         h = siz.height();
01652                     else
01653                         w = siz.width();
01654                     calculateOverlap( 0, 0, 0, siz.width(), centerAbscissaTicks,
01655                                       leftOverlap, rightOverlap );
01656                 }
01657             }
01658             // we leave a little gap between axis labels and bottom (or top, resp.) side of axis
01659             h += labelGap;
01660         }
01661         // space for a possible title:
01662         if ( drawTitle ) {
01663             // we add the title height and leave a little gap between axis labels and axis title
01664             if ( diagramIsVertical ) {
01665                 h += titleItem.sizeHint().height() + titleGap;
01666                 w = titleItem.sizeHint().width() + 2.0;
01667             } else {
01668                 h = titleItem.sizeHint().height() + 2.0;
01669                 w += titleItem.sizeHint().width() + titleGap;
01670             }
01671         }
01672         // space for the ticks
01673         if ( diagramIsVertical )
01674             h += qAbs( axis()->tickLength() ) * 3.0;
01675         else
01676             w += qAbs( axis()->tickLength() ) * 3.0;
01677         result = QSize ( static_cast<int>( w ), static_cast<int>( h ) );
01678 
01679         //qDebug()<<"calculated size of x axis:"<<result;
01680 
01681         // If necessary adjust the widths
01682         // of the left (or right, resp.) side neighboring columns:
01683         amountOfLeftOverlap = leftOverlap;
01684         amountOfRightOverlap = rightOverlap;
01685         /* Unused code for a push-model:
01686         if( leftOverlap || rightOverlap ){
01687             QTimer::singleShot(200, const_cast<CartesianAxis*>(this),
01688                                SLOT(adjustLeftRightGridColumnWidths()));
01689         }
01690         */
01691     } else {
01692         int topOverlap = 0;
01693         int bottomOverlap = 0;
01694 
01695         qreal w = diagramIsVertical ? 0.0 : 10.0;
01696         qreal h = diagramIsVertical ? 10.0 : 0.0;
01697         if( drawLabels ){
01698             // if there're no label strings, we loop through the values
01699             // taking the longest (not largest) number - e.g. 0.00001 is longer than 100
01700             if( !annotations.isEmpty() )
01701             {
01702                 const QStringList strings = annotations.values();
01703                 KDAB_FOREACH( const QString& string, strings )
01704                 {
01705                     labelItem.setText( string );
01706                     const QSize siz = labelItem.sizeHint();
01707                     if ( diagramIsVertical )
01708                         w = qMax( w, static_cast< qreal >( siz.width() ) );
01709                     else
01710                         h = qMax( h, static_cast< qreal >( siz.height() ) );
01711                 }
01712             }
01713             else if( axis()->labels().isEmpty() )
01714             {
01715                 const DataDimension dimY = AbstractGrid::adjustedLowerUpperRange(
01716                         diagramIsVertical ? plane->gridDimensionsList().last()
01717                                           : plane->gridDimensionsList().first(), true, true );
01718                 const double step = dimY.stepWidth;
01719                 const qreal minValue = dimY.start;
01720                 const qreal maxValue = dimY.end;
01721                 const bool isLogarithmicY = (dimY.calcMode == AbstractCoordinatePlane::Logarithmic );
01722                 qreal labelValue = minValue;
01723 
01724                 while( labelValue <= maxValue ) {
01725                     const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) +
01726                                             QString::number( labelValue ) +
01727                                             diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true );
01728                     labelItem.setText( axis()->customizedLabel( labelText ) );
01729 
01730                     const QSize siz = labelItem.sizeHint();
01731                     if ( diagramIsVertical )
01732                         w = qMax( w, (qreal)siz.width() );
01733                     else
01734                         h = qMax( h, (qreal)siz.height() );
01735                     calculateOverlap( 0, 0, 0, diagramIsVertical ? siz.height() : siz.width(), false,// bar diagram flag is ignored for Ordinates
01736                                     topOverlap, bottomOverlap );
01737                     calculateNextLabel( labelValue, step, isLogarithmicY, plane->gridDimensionsList().last().start );
01738 
01739                     if(maxValue == 0 && minValue == 0)
01740                         break;
01741                 }
01742             }else{
01743                 // find the longest label text:
01744                 const int first=0;
01745                 const int last=axis()->labels().count()-1;
01746                 const QStringList labelsList( axis()->labels() );
01747                 for ( int i = first; i <= last; ++i )
01748                 {
01749                     labelItem.setText( axis()->customizedLabel(labelsList[ i ]) );
01750                     const QSize siz = labelItem.sizeHint();
01751                     if ( diagramIsVertical )
01752                                             w = qMax( w, (qreal)siz.width() );
01753                                         else
01754                                             h = qMax( h, (qreal)siz.height() );
01755                     calculateOverlap( 0, 0, 0, diagramIsVertical ? siz.height() : siz.width(), false,// bar diagram flag is ignored for Ordinates
01756                                       topOverlap, bottomOverlap );
01757                 }
01758             }
01759             // we leave a little gap between axis labels and left (or right, resp.) side of axis
01760             w += labelGap;
01761         }
01762         // space for a possible title:
01763         if ( drawTitle ) {
01764             // we add the title height and leave a little gap between axis labels and axis title
01765             if ( diagramIsVertical ) {
01766                 w += titleItem.sizeHint().width() + titleGap;
01767                 h = titleItem.sizeHint().height() + 2.0;
01768             } else {
01769                 w = titleItem.sizeHint().width() + 2.0;
01770                 h += titleItem.sizeHint().height() + titleGap;
01771             }
01772             //qDebug() << "left/right axis title item size-hint:" << titleItem.sizeHint();
01773         }
01774         // space for the ticks
01775         if ( diagramIsVertical )
01776             w += qAbs( axis()->tickLength() ) * 3.0;
01777         else
01778             h += qAbs( axis()->tickLength() ) * 3.0;
01779 
01780         result = QSize ( static_cast<int>( w ), static_cast<int>( h ) );
01781         //qDebug() << "left/right axis width:" << result << "   w:" << w;
01782 
01783 
01784         // If necessary adjust the heights
01785         // of the top (or bottom, resp.) side neighboring rows:
01786         amountOfTopOverlap = topOverlap;
01787         amountOfBottomOverlap = bottomOverlap;
01788         /* Unused code for a push-model:
01789         if( topOverlap || bottomOverlap ){
01790             QTimer::singleShot(200, const_cast<CartesianAxis*>(this),
01791                                SLOT(adjustTopBottomGridRowHeights()));
01792         }
01793         */
01794     }
01795 //qDebug() << "*******************" << result;
01796     //result=QSize(0,0);
01797     return result;
01798 }
01799 /* pure virtual in QLayoutItem */
01800 QSize CartesianAxis::minimumSize() const
01801 {
01802     return maximumSize();
01803 }
01804 /* pure virtual in QLayoutItem */
01805 QSize CartesianAxis::sizeHint() const
01806 {
01807     return maximumSize();
01808 }
01809 /* pure virtual in QLayoutItem */
01810 void CartesianAxis::setGeometry( const QRect& r )
01811 {
01812 //    qDebug() << "KDChart::CartesianAxis::setGeometry(" << r << ") called"
01813 //             << (isAbscissa() ? "for Abscissa":"for Ordinate") << "axis";
01814     d->geometry = r;
01815     setCachedSizeDirty();
01816 }
01817 /* pure virtual in QLayoutItem */
01818 QRect CartesianAxis::geometry() const
01819 {
01820     return d->geometry;
01821 }
01822 
01823 int CartesianAxis::tickLength( bool subUnitTicks ) const
01824 {
01825     int result = 0;
01826 
01827     if ( isAbscissa() ) {
01828         result = position() == Top ? -4 : 3;
01829     } else {
01830         result = position() == Left ? -4 : 3;
01831     }
01832 
01833     if ( subUnitTicks )
01834         result = result < 0 ? result + 1 : result - 1;
01835 
01836     return result;
01837 }
01838 
01839 QMap< double, QString > CartesianAxis::annotations() const
01840 {
01841     return d->annotations;
01842 }
01843 
01844 void CartesianAxis::setAnnotations( const QMap< double, QString >& annotations )
01845 {
01846     if( d->annotations == annotations )
01847         return;
01848 
01849     d->annotations = annotations;
01850     update();
01851 }
01852 
01853 QList< double > CartesianAxis::customTicks() const
01854 {
01855     return d->customTicksPositions;
01856 }
01857 
01858 void CartesianAxis::setCustomTicks( const QList< double >& customTicksPositions )
01859 {
01860     if( d->customTicksPositions == customTicksPositions )
01861         return;
01862 
01863     d->customTicksPositions = customTicksPositions;
01864     update();
01865 }
01866 
01867 
01868 /* unused code from KDChartCartesianAxis.h for using a push-model:
01869 Q_SIGNALS:
01870     void needAdjustLeftRightColumnsForOverlappingLabels(
01871             CartesianAxis* axis, int left, int right );
01872     void needAdjustTopBottomRowsForOverlappingLabels(
01873             CartesianAxis* axis, int top, int bottom );
01874 private Q_SLOTS:
01875     void adjustLeftRightGridColumnWidths();
01876     void adjustTopBottomGridRowHeights();
01877 */
01878 
01879 /*
01880 // Unused code trying to use a push-model: This did not work
01881 // since we can not re-layout the planes each time when
01882 // Qt layouting is calling sizeHint()
01883 void CartesianAxis::adjustLeftRightGridColumnWidths()
01884 {
01885     if( ! d->amountOfLeftOverlap && ! d->amountOfRightOverlap )
01886         return;
01887     const int leftOverlap = d->amountOfLeftOverlap;
01888     const int rightOverlap= d->amountOfRightOverlap;
01889     d->amountOfLeftOverlap = 0;
01890     d->amountOfRightOverlap = 0;
01891     emit needAdjustLeftRightColumnsForOverlappingLabels(
01892             this, leftOverlap, rightOverlap );
01893 }
01894 
01895 void CartesianAxis::adjustTopBottomGridRowHeights()
01896 {
01897     if( ! d->amountOfTopOverlap && ! d->amountOfBottomOverlap )
01898         return;
01899     const int topOverlap = d->amountOfTopOverlap;
01900     const int bottomOverlap= d->amountOfBottomOverlap;
01901     d->amountOfTopOverlap = 0;
01902     d->amountOfBottomOverlap = 0;
01903     emit needAdjustTopBottomRowsForOverlappingLabels(
01904             this, topOverlap, bottomOverlap );
01905 }
01906 */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Defines

Klarälvdalens Datakonsult AB (KDAB)
Qt-related services and products
http://www.kdab.com/
http://www.kdab.com/products/kd-chart/