KDChartCartesianAxis.cpp

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