KDChartCartesianAxis.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB.  All rights reserved.
00003 **
00004 ** This file is part of the KD Chart library.
00005 **
00006 ** Licensees holding valid commercial KD Chart licenses may use this file in
00007 ** accordance with the KD Chart Commercial License Agreement provided with
00008 ** the Software.
00009 **
00010 **
00011 ** This file may be distributed and/or modified under the terms of the
00012 ** GNU General Public License version 2 and version 3 as published by the
00013 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
00014 **
00015 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00016 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00017 **
00018 ** Contact info@kdab.com if any conditions of this licensing are not
00019 ** clear to you.
00020 **
00021 **********************************************************************/
00022 
00023 #include "KDChartCartesianAxis.h"
00024 #include "KDChartCartesianAxis_p.h"
00025 
00026 #include <cmath>
00027 
00028 #include <QtDebug>
00029 #include <QPainter>
00030 #include <QPen>
00031 #include <QBrush>
00032 #include <QApplication>
00033 
00034 #include "KDChartPaintContext.h"
00035 #include "KDChartChart.h"
00036 #include "KDChartAbstractCartesianDiagram.h"
00037 #include "KDChartAbstractGrid.h"
00038 #include "KDChartPainterSaver_p.h"
00039 #include "KDChartLayoutItems.h"
00040 #include "KDChartBarDiagram.h"
00041 #include "KDChartStockDiagram.h"
00042 #include "KDChartLineDiagram.h"
00043 #include "KDChartPrintingParameters.h"
00044 
00045 #include <KDABLibFakes>
00046 
00047 #include <limits>
00048 
00049 using namespace KDChart;
00050 
00051 #define d (d_func())
00052 
00053 CartesianAxis::CartesianAxis ( AbstractCartesianDiagram* diagram )
00054     : AbstractAxis ( new Private( diagram, this ), diagram )
00055 {
00056     init();
00057 }
00058 
00059 CartesianAxis::~CartesianAxis ()
00060 {
00061     // when we remove the first axis it will unregister itself and
00062     // propagate the next one to the primary, thus the while loop
00063     while ( d->mDiagram ) {
00064         AbstractCartesianDiagram *cd = qobject_cast<AbstractCartesianDiagram*>( d->mDiagram );
00065         cd->takeAxis( this );
00066     }
00067     Q_FOREACH( AbstractDiagram *diagram, d->secondaryDiagrams ) {
00068         AbstractCartesianDiagram *cd = qobject_cast<AbstractCartesianDiagram*>( diagram );
00069         cd->takeAxis( this );
00070     }
00071 }
00072 
00073 void CartesianAxis::init ()
00074 {
00075     d->position = Bottom;
00076     setCachedSizeDirty();
00077 }
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         labelItem->setTextAttributes( textAttributes() );
00726         TextLayoutItem* labelItem2 =
00727             drawLabels
00728             ? new TextLayoutItem( QString::number( minValueY ),
00729                                   labelTA,
00730                                   referenceArea,
00731                                   KDChartEnums::MeasureOrientationMinimum,
00732                                   Qt::AlignLeft )
00733             : 0;
00734         labelItem2->setTextAttributes( textAttributes() );
00735         const QFontMetricsF met(
00736             drawLabels
00737             ? labelItem->realFont()
00738             : QFontMetricsF( QApplication::font(), GlobalMeasureScaling::paintDevice() ) );
00739         const qreal halfFontHeight = rulerAttr.labelMargin() >= 0 ? rulerAttr.labelMargin() : met.height() * 0.5;
00740         const qreal halfFontWidth = rulerAttr.labelMargin() >= 0 ? rulerAttr.labelMargin() : met.averageCharWidth() * 0.5;
00741 
00742         if ( isAbscissa() ) {
00743             //Draw ticks at custom postions on x-axis
00744             if( !d->customTicksPositions.isEmpty() )
00745             {
00746                 const QList< double > values = d->customTicksPositions;
00747                 KDAB_FOREACH( const double v, values )
00748                 {
00749                    QPointF topPoint = diagramIsVertical ? QPointF( v, 0.0 ) : QPointF( 0.0, v );
00750                    QPointF bottomPoint = topPoint;
00751                    topPoint = plane->translate( topPoint );
00752                    bottomPoint = plane->translate( bottomPoint );
00753                    if ( diagramIsVertical ) {
00754                        topPoint.setY( rulerRef.y() + tickLength() );
00755                        bottomPoint.setY( rulerRef.y() );
00756                    } else {
00757                        topPoint.setX( rulerRef.x() + tickLength() );
00758                        bottomPoint.setX( rulerRef.x() );
00759                    }
00760 
00761                    context->painter()->drawLine(topPoint, bottomPoint);
00762                }
00763             }
00764 
00765             if( !d->annotations.isEmpty() )
00766             {
00767                 const QList< double > values = d->annotations.keys();
00768                 KDAB_FOREACH( const double v, values )
00769                 {
00770                    QPointF topPoint = diagramIsVertical ? QPointF( v, 0.0 ) : QPointF( 0.0, v );
00771                    QPointF bottomPoint = topPoint;
00772                    topPoint = plane->translate( topPoint );
00773                    bottomPoint = plane->translate( bottomPoint );
00774                    if ( diagramIsVertical ) {
00775                        topPoint.setY( rulerRef.y() + tickLength() );
00776                        bottomPoint.setY( rulerRef.y() );
00777                    } else {
00778                        topPoint.setX( rulerRef.x() + tickLength() );
00779                        bottomPoint.setX( rulerRef.x() );
00780                    }
00781 
00782                    labelItem->setText( d->annotations[ v ] );
00783                    const QSize size( labelItem->sizeHint() );
00784                    if ( diagramIsVertical ) {
00785                     labelItem->setGeometry(
00786                         QRect(
00787                             QPoint(
00788                                 static_cast<int>( topPoint.x() - size.width() / 2.0 ),
00789                                 static_cast<int>( topPoint.y() +
00790                                                 ( position() == Bottom
00791                                                     ? halfFontHeight
00792                                                     : ((halfFontHeight + size.height()) * -1.0) ) ) ),
00793                             size ) );
00794                    } else {
00795                     labelItem->setGeometry(
00796                         QRect(
00797                             QPoint(
00798                                 static_cast<int>( bottomPoint.x() +
00799                                                 ( position() == Right
00800                                                     ? halfFontWidth
00801                                                     : (-halfFontWidth - size.width()) ) ),
00802 
00803                                 static_cast<int>( topPoint.y() - ( size.height() ) * 0.5 ) ),
00804                             size ) );
00805                    }
00806 
00807                    QRect labelGeo = labelItem->geometry();
00808                    // if our item would only half fit, we disable clipping for that one
00809                    if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() )
00810                        ptr->setClipping( false );
00811                    else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() )
00812                        ptr->setClipping( false );
00813 
00814                    labelItem->paint( ptr );
00815                 }
00816             }
00817 
00818             qreal labelDiff = dimX.stepWidth;
00819             const int precision = ( QString::number( labelDiff ).section( QLatin1Char('.'), 1,  2 ) ).length();
00820 
00821             // If we have a labels list AND a short labels list, we first find out,
00822             // if there is enough space for showing ALL of the long labels:
00823             // If not, use the short labels.
00824             if( drawLabels && hardLabelsCount > 0 && shortLabelsCount > 0 && d->annotations.isEmpty() ){
00825                 bool labelsAreOverlapping = false;
00826                 int iLabel = 0;
00827                 qreal i = minValueX;
00828                 while ( i < maxValueX-1 && !labelsAreOverlapping )
00829                 {
00830                     const int idx = (iLabel < hardLabelsCount    ) ? iLabel     : 0;
00831                     const int idx2= (iLabel < hardLabelsCount - 1) ? iLabel + 1 : 0;
00832                     if ( dimX.stepWidth != 1.0 && ! dim.isCalculated )
00833                     {
00834                         // Check intersects for the header label - we need to pass the full string
00835                         // here and not only the i value.
00836                         if( useConfiguredStepsLabels ){
00837                             labelItem->setText( customizedLabel(headerLabels[ idx ]) );
00838                             labelItem2->setText( customizedLabel(headerLabels[ idx2 ]) );
00839                         }else{
00840                             //qDebug() << "i + labelDiff " << i + labelDiff;
00841                             labelItem->setText( customizedLabel(headerLabelsCount > i && i >= 0 ?
00842                                     headerLabels[static_cast<int>(i)] :
00843                                     QString::number( i, 'f', precision )) );
00844                             //           qDebug() << "1 - labelItem->text() " << labelItem->text();
00845                             //qDebug() << "labelDiff" << labelDiff
00846                             //        << "  index" << i+labelDiff << "  count" << headerLabelsCount;
00847                             labelItem2->setText( customizedLabel(headerLabelsCount > i + labelDiff && i + labelDiff >= 0 ?
00848                                     headerLabels[static_cast<int>(i+labelDiff)] :
00849                                     QString::number( i + labelDiff, 'f', precision )) );
00850                             //qDebug() << "2 - labelItem->text() " << labelItem->text();
00851                         }
00852                     } else {
00853                         //qDebug() << iLabel << i << "("<<hardLabelsCount<<")   :";
00854                         const int shortIdx =  (iLabel < shortLabelsCount    ) ? iLabel     : 0;
00855                         const int shortIdx2 = (iLabel < shortLabelsCount - 1) ? iLabel + 1 : 0;
00856                         labelItem->setText(  customizedLabel(
00857                                 useShortLabels ? shortLabelsList[ shortIdx ] : labelsList[ idx ] ) );
00858                         labelItem2->setText( customizedLabel(
00859                                 useShortLabels ? shortLabelsList[ shortIdx2 ] : labelsList[ idx2 ] ) );
00860                     }
00861 
00862                     QPointF firstPos = diagramIsVertical ? QPointF( i, 0.0 ) : QPointF( 0.0, i );
00863                     firstPos = plane->translate( firstPos );
00864 
00865                     QPointF secondPos = diagramIsVertical ? QPointF( i + labelDiff, 0.0 ) : QPointF( 0.0, i + labelDiff );
00866                     secondPos = plane->translate( secondPos );
00867 
00868                     labelsAreOverlapping = labelItem->intersects( *labelItem2, firstPos, secondPos );
00869 
00870                     if ( ++iLabel > hardLabelsCount - 1 )
00871                         iLabel = 0;
00872                     if ( isLogarithmicX )
00873                         i *= 10.0;
00874                     else
00875                         i += dimX.stepWidth;
00876                     //qDebug() << labelsAreOverlapping << iLabel << i << labelsAreOverlapping << firstPos << secondPos.x()-firstPos .x() << labelItem->text() << labelItem2->text();
00877                 }
00878 
00879                 useShortLabels = labelsAreOverlapping;
00880             }
00881 
00882             //      qDebug() << "initial labelDiff " << labelDiff;
00883             if ( drawLabels && d->annotations.isEmpty() )
00884             {
00885                 qreal i = minValueX;
00886                 int iLabel = 0;
00887 
00888                 while ( i + labelDiff < maxValueX )
00889                 {
00890                     const int idx = (iLabel < hardLabelsCount    ) ? iLabel     : 0;
00891                     const int idx2= (iLabel < hardLabelsCount - 1) ? iLabel + 1 : 0;
00892                     //qDebug() << "drawLabels" << drawLabels << "  hardLabelsCount" << hardLabelsCount
00893                     //        << "  dimX.stepWidth" << dimX.stepWidth << "  dim.isCalculated" << dim.isCalculated;
00894                     if ( !drawLabels || hardLabelsCount < 1 || ( dimX.stepWidth != 1.0 && ! dim.isCalculated ) )
00895                     {
00896                         // Check intersects for the header label - we need to pass the full string
00897                         // here and not only the i value.
00898                         if( useConfiguredStepsLabels ){
00899                             labelItem->setText( customizedLabel(headerLabels[ idx ]) );
00900                             labelItem2->setText( customizedLabel(headerLabels[ idx2 ]) );
00901                         }else{
00902                             //qDebug() << "i + labelDiff " << i + labelDiff;
00903                             labelItem->setText( customizedLabel(headerLabelsCount > i && i >= 0 ?
00904                                     headerLabels[static_cast<int>(i)] :
00905                                     QString::number( i, 'f', precision )) );
00906                             //           qDebug() << "1 - labelItem->text() " << labelItem->text();
00907                             //qDebug() << "labelDiff" << labelDiff
00908                             //        << "  index" << i+labelDiff << "  count" << headerLabelsCount;
00909                             labelItem2->setText( customizedLabel(headerLabelsCount > i + labelDiff && i + labelDiff >= 0 ?
00910                                     headerLabels[static_cast<int>(i+labelDiff)] :
00911                                     QString::number( i + labelDiff, 'f', precision )) );
00912                             //qDebug() << "2 - labelItem->text() " << labelItem->text();
00913                         }
00914                     } else {
00915                         const int shortIdx =  (iLabel < shortLabelsCount    ) ? iLabel     : 0;
00916                         const int shortIdx2 = (iLabel < shortLabelsCount - 1) ? iLabel + 1 : 0;
00917                         labelItem->setText(  customizedLabel(
00918                                 useShortLabels ? shortLabelsList[ shortIdx ] : labelsList[ idx ] ) );
00919                         labelItem2->setText( customizedLabel(
00920                                 useShortLabels ? shortLabelsList[ shortIdx2 ] : labelsList[ idx2 ] ) );
00921                     }
00922 
00923                     QPointF firstPos = diagramIsVertical ? QPointF( i, 0.0 ) : QPointF( 0.0, i );
00924                     firstPos = plane->translate( firstPos );
00925 
00926                     QPointF secondPos = diagramIsVertical ? QPointF( i + labelDiff, 0.0 ) : QPointF( 0.0, i + labelDiff );
00927                     secondPos = plane->translate( secondPos );
00928 
00929                     if ( labelItem->intersects( *labelItem2, firstPos, secondPos ) )
00930                     {
00931                         i = minValueX;
00932 
00933                         // fix for issue #4179:
00934                         labelDiff *= 10.0;
00935                         // old code: labelDiff += labelDiff;
00936 
00937                         iLabel = 0;
00938                         //qDebug() << firstPos << secondPos.x()-firstPos .x() << labelItem->text() << labelItem2->text() << labelDiff;
00939                     }
00940                     else
00941                     {
00942                         i += labelDiff;
00943                         //qDebug() << firstPos << secondPos.x()-firstPos .x() << labelItem->text() << labelItem2->text();
00944                     }
00945 
00946                     if ( (++iLabel > hardLabelsCount - 1) && !useConfiguredStepsLabels )
00947                     {
00948                         iLabel = 0;
00949                     }
00950                 }
00951                 // fixing bugz issue #5018 without breaking issue #4179:
00952                 if( minValueX + labelDiff > maxValueX )
00953                     labelDiff = maxValueX - minValueX;
00954                 // This makes sure the first and the last X label are drawn
00955                 // if there is not enouth place to draw some more of them
00956                 // according to labelDiff calculation performed above.
00957             }
00958 
00959             int idxLabel = 0;
00960             qreal iLabelF = minValueX;
00961             //qDebug() << iLabelF;
00962             qreal i = minValueX;
00963             qreal labelStep = 0.0;
00964             //    qDebug() << "dimX.stepWidth:" << dimX.stepWidth  << "labelDiff:" << labelDiff;
00965             //dimX.stepWidth = 0.5;
00966             while( i <= maxValueX && d->annotations.isEmpty() )
00967             {
00968                 // Line charts: we want the first tick to begin at 0.0 not at 0.5 otherwise labels and
00969                 // values does not fit each others
00970                 QPointF topPoint = diagramIsVertical ? QPointF( i + ( centerAbscissaTicks ? 0.5 : 0.0 ), 0.0 ) : QPointF( 0.0, i + ( centerAbscissaTicks ? 0.5 : 0.0 ) );
00971                 QPointF bottomPoint ( topPoint );
00972                 topPoint = plane->translate( topPoint );
00973                 bottomPoint = plane->translate( bottomPoint );
00974                 if ( diagramIsVertical ) {
00975                     topPoint.setY( rulerRef.y() + tickLength() );
00976                     bottomPoint.setY( rulerRef.y() );
00977                 } else {
00978                     bottomPoint.setX( rulerRef.x() - (position() == Left ? tickLength() : -tickLength()) );
00979                     topPoint.setX( rulerRef.x() );
00980                 }
00981 
00982                 const qreal translatedValue = diagramIsVertical ? topPoint.x() : topPoint.y();
00983                 bool bIsVisibleLabel;
00984                 if ( diagramIsVertical )
00985                     bIsVisibleLabel = ( (translatedValue >= geoRect.left() && translatedValue <= geoRect.right() && !isLogarithmicX) || i != 0.0 );
00986                 else
00987                     bIsVisibleLabel = ( (translatedValue >= geoRect.top() && translatedValue <= geoRect.bottom() && !isLogarithmicX) || i != 0.0 );
00988 
00989                 // fix for issue #4179:
00990                 bool painttick = bIsVisibleLabel && labelStep <= 0;
00991                 // old code: bool painttick = true;
00992 
00993                 //Dont paint more ticks than we need
00994                 //when diagram type is Bar
00995                 if (  centerAbscissaTicks && i == maxValueX )
00996                     painttick = false;
00997 
00998                 if ( bIsVisibleLabel && painttick ) {
00999                     ptr->save();
01000                     if ( rulerAttr.hasTickMarkPenAt( i ) )
01001                         ptr->setPen( rulerAttr.tickMarkPen( i ) );
01002                     else
01003                         ptr->setPen( rulerAttr.majorTickMarkPen() );
01004                     ptr->drawLine( topPoint, bottomPoint );
01005                     ptr->restore();
01006                 }
01007 
01008                 drawnAbscissaTicks.append( static_cast<int>( diagramIsVertical ? topPoint.x() : topPoint.y() ) );
01009                 if( drawLabels ) {
01010                     if( bIsVisibleLabel ){
01011                         if ( isLogarithmicX )
01012                             labelItem->setText( customizedLabel(QString::number( i ) ) );
01013                         /* We don't need that
01014                         * it causes header labels to be skipped even if there is enough
01015                         * space for them to displayed.
01016                         * Commenting for now - I need to test more in details - Let me know if I am wrong here.
01017                         */
01018                         /*
01019                         else if( (dimX.stepWidth != 1.0) && ! dimX.isCalculated ) {
01020                         labelItem->setText( customizedLabel(QString::number( i, 'f', 0 )) );
01021                         }
01022                         */
01023                         else {
01024                             int idx = idxLabel + static_cast<int>(minValueX);
01025                             if( hardLabelsCount ){
01026                                 if( useShortLabels ){
01027                                     if( idx >= shortLabelsList.count() )
01028                                         idx = 0;
01029                                 }else{
01030                                     if( idx >= labelsList.count() )
01031                                         idx = 0;
01032                                 }
01033                             }
01034                             labelItem->setText(
01035                                     customizedLabel(
01036                                           hardLabelsCount
01037                                     ? ( useShortLabels    ? shortLabelsList[ idx ] : labelsList[ idx ] )
01038                                 : ( headerLabelsCount ? headerLabels[ idx ] : QString::number( iLabelF ))));
01039                             //qDebug() << "x - labelItem->text() " << labelItem->text() << headerLabelsCount;
01040                         }
01041                         // No need to call labelItem->setParentWidget(), since we are using
01042                         // the layout item temporarily only.
01043                         if( labelStep <= 0 ) {
01044                             const PainterSaver p( ptr );
01045                             //const QSize size( labelItem->sizeHint() );
01046                             QPoint topLeft, topRight, bottomRight, bottomLeft;
01047                             const QSize size(
01048                                     labelItem->sizeHintAndRotatedCorners(
01049                                             topLeft, topRight, bottomRight, bottomLeft) );
01050                             const QSize sizeUnrotated( labelItem->sizeHintUnrotated() );
01051                             const int rotation = labelTA.rotation();
01052                             const bool rotPositive = (rotation > 0 && rotation < 180);
01053                             QPoint midOfSide(0,0);
01054                             int dX = 0;
01055                             int dY = 0;
01056                             if( rotation ){
01057                                 if( rotPositive ){
01058                                     midOfSide = (topLeft + bottomLeft)  / 2;
01059                                     dX = topLeft.x() - midOfSide.x();
01060                                     dY = bottomLeft.y() - midOfSide.y();
01061                                 }else{
01062                                     midOfSide = (topRight + bottomRight) / 2;
01063                                     dX = midOfSide.x() - topLeft.x();
01064                                     dY = midOfSide.y() - topRight.y();
01065                                 }
01066                             }
01067                             /*
01068                             if( i == 2 ){
01069                                 qDebug()<<"------"<<size<<topPoint<<topLeft<<topRight<<bottomRight<<bottomLeft<<"   m:"<<midOfSide<<" dx"<<dX<<" dy"<<dY;
01070                                 ptr->setPen( Qt::black );
01071                                 QRectF rect(topPoint, QSizeF(sizeUnrotated));
01072                                 ptr->drawRect( rect );
01073                                 ptr->drawRect( QRectF(topPoint, QSizeF(2,2)) );
01074                                 ptr->drawRect( QRectF(topPoint+topLeft, QSizeF(2,2)) );
01075                                 ptr->drawRect( QRectF(topPoint+bottomLeft, QSizeF(2,2)) );
01076                                 ptr->drawRect( QRectF(topPoint+bottomRight, QSizeF(2,2)) );
01077                                 ptr->drawRect( QRectF(topPoint+topRight, QSizeF(2,2)) );
01078                                 ptr->drawRect( QRectF(topPoint+midOfSide, QSizeF(2,2)) );
01079                                 ptr->setPen( Qt::green );
01080                                 rect = QRectF(topPoint, QSizeF(size));
01081                                 ptr->drawRect( rect );
01082                                 ptr->drawRect( QRectF(QPointF((rect.topLeft()  + rect.bottomLeft())  / 2.0 - QPointF(2.0,2.0)), QSizeF(3.0,3.0)) );
01083                                 //ptr->drawRect( QRectF(QPointF((rect.topRight() + rect.bottomRight()) / 2.0 - QPointF(2.0,2.0)), QSizeF(3.0,3.0)) );
01084                             }
01085                             */
01086                             QPoint topLeftPt;
01087                             if( diagramIsVertical ){
01088                                 if( rotation ){
01089                                     topLeftPt = QPoint(
01090                                         static_cast<int>( topPoint.x() ) - dX,
01091                                         static_cast<int>( topPoint.y()   - dY +
01092                                                         ( position() == Bottom
01093                                                             ? halfFontHeight
01094                                                             : ((halfFontHeight + size.height()) * -1.0) ) ) );
01095                                 }else{
01096                                     topLeftPt = QPoint(
01097                                         static_cast<int>( topPoint.x() - size.width() / 2.0 ),
01098                                         static_cast<int>( topPoint.y() +
01099                                                         ( position() == Bottom
01100                                                             ? halfFontHeight
01101                                                             : ((halfFontHeight + size.height()) * -1.0) ) ) );
01102                                 }
01103                             }else{
01104                                 if( rotation ){
01105                                     topLeftPt = QPoint(
01106                                         static_cast<int>( topPoint.x() ) + dX,
01107                                         static_cast<int>( topPoint.y()   - dY +
01108                                                         ( position() == Bottom
01109                                                             ? halfFontHeight
01110                                                             : ((halfFontHeight + size.height()) * -1.0) ) ) );
01111                                 }else{
01112                                     topLeftPt = QPoint(
01113                                         static_cast<int>( bottomPoint.x() +
01114                                                             ( position() == Right
01115                                                                 ? halfFontWidth
01116                                                                 : (-halfFontWidth - size.width()) ) ),
01117                                         static_cast<int>( topPoint.y() - ( size.height() ) * 0.5 ) );
01118                                 }
01119                             }
01120                             labelItem->setGeometry( QRect(topLeftPt, size) );
01121 
01122                             QRect labelGeo = labelItem->geometry();
01123                             //ptr->drawRect(labelGeo);
01124                             // if our item would only half fit, we disable clipping for that one
01125                             if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() )
01126                                 ptr->setClipping( false );
01127                             else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() )
01128                                 ptr->setClipping( false );
01129 
01130                             if( !isLogarithmicX )
01131                                 labelStep = labelDiff - dimX.stepWidth;
01132 
01133                             // if our item would only half fit, we disable clipping for that one
01134                             if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() )
01135                                 ptr->setClipping( false );
01136                             else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() )
01137                                 ptr->setClipping( false );
01138 
01139                             labelItem->paint( ptr );
01140 
01141                             // do not call customizedLabel() again:
01142                             labelItem2->setText( labelItem->text() );
01143 
01144                         } else {
01145                             labelStep -= dimX.stepWidth;
01146                         }
01147                     }
01148 
01149                     if( hardLabelsCount ) {
01150                         if( useShortLabels && idxLabel >= shortLabelsCount - 1 )
01151                             idxLabel = 0;
01152                         else if( !useShortLabels && idxLabel >= hardLabelsCount - 1 )
01153                             idxLabel = 0;
01154                         else{
01155                             idxLabel += static_cast<int>(dimX.stepWidth);
01156                             //qDebug() << "dimX.stepWidth:" << dimX.stepWidth << "  idxLabel:" << idxLabel;
01157                         }
01158                     } else if( headerLabelsCount ) {
01159                         if( ++idxLabel > headerLabelsCount - 1 ) {
01160                             idxLabel = 0;
01161                         }
01162                     } else {
01163                         iLabelF += dimX.stepWidth;
01164                     }
01165                 }
01166                 if ( isLogarithmicX )
01167                 {
01168                     i *= 10.0;
01169                     if( i == 0.0 )
01170                     {
01171                         const qreal j = dimensions.first().start;
01172                         i = j == 0.0 ? 1.0 : pow( 10.0, floor( log10( j ) ) );
01173                     }
01174                 }
01175                 else
01176                 {
01177                     i += dimX.stepWidth;
01178                 }
01179             }
01180         } else {
01181             const PainterSaver p( ptr );
01182             const double maxLimit = maxValueY;
01183             const double steg = dimY.stepWidth;
01184             int maxLabelsWidth = 0;
01185             qreal labelValue;
01186 
01187             if( drawLabels && position() == Right ){
01188                 // Find the widest label, so we to know how much we need to right-shift
01189                 // our labels, to get them drawn right aligned:
01190                 labelValue = minValueY;
01191                 while ( labelValue <= maxLimit ) {
01192                     const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) +
01193                                               QString::number( labelValue ) +
01194                                               diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true );
01195                     labelItem->setText( customizedLabel( labelText ) );
01196                     maxLabelsWidth = qMax( maxLabelsWidth, diagramIsVertical ? labelItem->sizeHint().width() : labelItem->sizeHint().height() );
01197                     calculateNextLabel( labelValue, steg, isLogarithmicY, dimensions.last().start );
01198 
01199                     if(maxValueY == 0 && minValueY == 0)
01200                        break;
01201                 }
01202             }
01203 
01204             labelValue = minValueY;
01205             qreal step = steg;
01206             bool nextLabel = false;
01207             //qDebug("minValueY: %f   maxLimit: %f   steg: %f", minValueY, maxLimit, steg);
01208 
01209             //Draws custom tick marks in the y-axis
01210             if( !d->customTicksPositions.isEmpty() )
01211             {
01212                 const QList< double > values = d->customTicksPositions;
01213                 KDAB_FOREACH( const double value, values )
01214                 {
01215                     QPointF annoPoint = (diagramIsVertical ? QPointF( 0.0, value ) : QPointF( value, 0.0 ));
01216                     QPointF leftPoint = plane->translate( annoPoint );
01217                     QPointF rightPoint = plane->translate( annoPoint );
01218 
01219                     if ( diagramIsVertical ) {
01220                         leftPoint.setX( rulerRef.x() + tickLength() );
01221                         rightPoint.setX( rulerRef.x() );
01222                     } else {
01223                         leftPoint.setY( rulerRef.y() + ((position() == Bottom) ? tickLength() : -tickLength()) );
01224                         rightPoint.setY( rulerRef.y() );
01225                     }
01226 
01227                     context->painter()->drawLine(leftPoint, rightPoint);
01228                 }
01229             }
01230 
01231             if( drawLabels )
01232             {
01233                 // first calculate the steps depending on labels colision
01234                 while( labelValue <= maxLimit ) {
01235                     QPointF leftPoint = plane->translate( diagramIsVertical ? QPointF( 0, labelValue ) : QPointF( labelValue, 0 ) );
01236                     const qreal translatedValue = diagramIsVertical ? leftPoint.y() : leftPoint.x();
01237                     //qDebug() << "geoRect:" << geoRect << "   geoRect.top()" << geoRect.top() << "geoRect.bottom()" << geoRect.bottom() << "  translatedValue:" << translatedValue;
01238                     const bool bTranslatedValueIsWithinRange = diagramIsVertical ? translatedValue > geoRect.top() && translatedValue <= geoRect.bottom()
01239                                                                                  : translatedValue > geoRect.left() && translatedValue <= geoRect.right();
01240                     if( bTranslatedValueIsWithinRange ){
01241                         const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) +
01242                                                   QString::number( labelValue ) +
01243                                                   diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true );
01244                         const QString label2Text = diagram()->unitPrefix( static_cast< int >( labelValue + step ), diagramOrientation, true ) +
01245                                                    QString::number( labelValue + step ) +
01246                                                    diagram()->unitSuffix( static_cast< int >( labelValue + step ), diagramOrientation, true );
01247                         labelItem->setText(  customizedLabel( labelText ) );
01248                         labelItem2->setText( customizedLabel( QString::number( labelValue + step ) ) );
01249                         QPointF nextPoint = plane->translate(  diagramIsVertical ? QPointF( 0,  labelValue + step ) : QPointF( labelValue + step, 0 ) );
01250                         if ( labelItem->intersects( *labelItem2, leftPoint, nextPoint ) )
01251                         {
01252                             step += steg;
01253                             nextLabel = false;
01254                         }else{
01255                             nextLabel = true;
01256                         }
01257                     }else{
01258                         nextLabel = true;
01259                     }
01260 
01261                     if ( nextLabel || isLogarithmicY )
01262                         calculateNextLabel( labelValue, step, isLogarithmicY, dimensions.last().start );
01263                     else
01264                         labelValue = minValueY;
01265                 }
01266 
01267                 // Second - Paint the labels
01268                 labelValue = minValueY;
01269                 //qDebug() << "axis labels starting at" << labelValue << "step width" << step;
01270                 if( !d->annotations.isEmpty() )
01271                 {
01272                     const QList< double > annotations = d->annotations.keys();
01273                     KDAB_FOREACH( const double annotation, annotations )
01274                     {
01275                         QPointF annoPoint = (diagramIsVertical ? QPointF( 0.0, annotation ) : QPointF( annotation, 0.0 ));
01276                         QPointF leftPoint = plane->translate( annoPoint );
01277                         QPointF rightPoint = plane->translate( annoPoint );
01278 
01279                         if ( diagramIsVertical ) {
01280                             leftPoint.setX( rulerRef.x() + tickLength() );
01281                             rightPoint.setX( rulerRef.x() );
01282                         } else {
01283                             leftPoint.setY( rulerRef.y() + ((position() == Bottom) ? tickLength() : -tickLength()) );
01284                             rightPoint.setY( rulerRef.y() );
01285                         }
01286 
01287                         const qreal translatedValue = diagramIsVertical ? rightPoint.y() : rightPoint.x();
01288                         const bool bIsVisibleLabel = diagramIsVertical ?
01289                                 ( (translatedValue >= geoRect.top() && translatedValue <= geoRect.bottom() && !isLogarithmicY) || labelValue != 0.0 )
01290                               : ( (translatedValue >= geoRect.left() && translatedValue <= geoRect.right() && !isLogarithmicY) || labelValue != 0.0 );
01291 
01292                         if( bIsVisibleLabel )
01293                         {
01294                             ptr->save();
01295                             if ( rulerAttr.hasTickMarkPenAt( annotation ) )
01296                                 ptr->setPen( rulerAttr.tickMarkPen( annotation ) );
01297                             else
01298                                 ptr->setPen( rulerAttr.majorTickMarkPen() );
01299                             ptr->drawLine( leftPoint, rightPoint );
01300                             ptr->restore();
01301 
01302                             labelItem->setText( d->annotations[ annotation ] );
01303                             const QSize labelSize( labelItem->sizeHint() );
01304                             int x, y;
01305                             if ( diagramIsVertical ) {
01306                                 x = static_cast<int>( leftPoint.x() + met.height() * ( position() == Left ? -0.5 : 0.5)
01307                                     - ( position() == Left ? labelSize.width() : 0.0 ) );
01308                                 y = static_cast<int>( leftPoint.y() - ( met.ascent() + met.descent() ) * 0.6 );
01309                             } else {
01310                                 const qreal halfFontHeight = met.height() * 0.5;
01311                                 x = static_cast<int>( leftPoint.x() - labelSize.width() * 0.5 );
01312                                 y = static_cast<int>( (position() == Bottom ? leftPoint.y() : rightPoint.y()) +
01313                                                                 + ( position() == Bottom ? halfFontHeight : -(halfFontHeight + labelSize.height()) ) );
01314                             }
01315                             labelItem->setGeometry( QRect( QPoint( x, y ), labelSize ) );
01316 
01317                             QRect labelGeo = labelItem->geometry();
01318                             // if our item would only half fit, we disable clipping for that one
01319                             if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() )
01320                                 ptr->setClipping( false );
01321                             else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() )
01322                                 ptr->setClipping( false );
01323 
01324                             labelItem->paint( ptr );
01325                         }
01326                     }
01327                 }
01328                 else
01329                 {
01330                     while( labelValue <= maxLimit ) {
01331                         //qDebug() << "value now" << labelValue;
01332                         const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) +
01333                                                   QString::number( labelValue ) +
01334                                                   diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true );
01335                         labelItem->setText( customizedLabel( labelText ) );
01336                         QPointF leftPoint = plane->translate( diagramIsVertical ? QPointF( 0, labelValue ) : QPointF( labelValue, 0 ) );
01337                         QPointF rightPoint = leftPoint;
01338 
01339                         if ( diagramIsVertical ) {
01340                             leftPoint.setX( rulerRef.x() + tickLength() );
01341                             rightPoint.setX( rulerRef.x() );
01342                         } else {
01343                             leftPoint.setY( rulerRef.y() + ((position() == Bottom) ? tickLength() : -tickLength()) );
01344                             rightPoint.setY( rulerRef.y() );
01345                         }
01346 
01347                         bool bIsVisibleLabel;
01348                         const qreal translatedValue = diagramIsVertical ? rightPoint.y() : rightPoint.x();
01349                         if ( diagramIsVertical)
01350                             bIsVisibleLabel = ( (translatedValue >= geoRect.left() && translatedValue <= geoRect.right() && !isLogarithmicX) || labelValue != 0.0 );
01351                         else
01352                             bIsVisibleLabel = ( (translatedValue >= geoRect.top() && translatedValue <= geoRect.bottom() && !isLogarithmicX) || labelValue != 0.0 );
01353 
01354                         if( bIsVisibleLabel ){
01355                             ptr->save();
01356                             if ( rulerAttr.hasTickMarkPenAt( labelValue ) )
01357                                 ptr->setPen( rulerAttr.tickMarkPen( labelValue ) );
01358                             else
01359                                 ptr->setPen( rulerAttr.majorTickMarkPen() );
01360                             ptr->drawLine( leftPoint, rightPoint );
01361                             ptr->restore();
01362 
01363                             drawnYTicks.append( static_cast<int>( diagramIsVertical ? leftPoint.y() : leftPoint.x() ) );
01364                             const QSize labelSize( labelItem->sizeHint() );
01365 
01366                             int x, y;
01367                             if ( diagramIsVertical ) {
01368                                 x = static_cast<int>( leftPoint.x() + met.height() * ( position() == Left ? -0.5 : 0.5) )
01369                                     - ( position() == Left ? labelSize.width() : (labelSize.width() - maxLabelsWidth) );
01370                                 y = static_cast<int>( leftPoint.y() - ( met.ascent() + met.descent() ) * 0.6 );
01371                             } else {
01372                                 const qreal halfFontHeight = met.height() * 0.5;
01373                                 x = static_cast<int>( leftPoint.x() - labelSize.width() * 0.5 );
01374                                 y = static_cast<int>( (position() == Bottom ? leftPoint.y() : rightPoint.y()) +
01375                                                                 + ( position() == Bottom ? halfFontHeight : -(halfFontHeight + labelSize.height()) ) );
01376                             }
01377 
01378                             labelItem->setGeometry( QRect( QPoint( x, y ), labelSize ) );
01379                             const QRect labelGeo = labelItem->geometry();
01380                             const bool hadClipping = ptr->hasClipping();
01381                             if( labelGeo.top() < geoRect.top() && labelGeo.bottom() > geoRect.top() )
01382                                ptr->setClipping( false );
01383                             else if( labelGeo.top() < geoRect.bottom() && labelGeo.bottom() > geoRect.bottom() )
01384                                ptr->setClipping( false );
01385 
01386                             // if our item would only half fit, we disable clipping for that one
01387                             if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() )
01388                                 ptr->setClipping( false );
01389                             else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() )
01390                                 ptr->setClipping( false );
01391 
01392                             labelItem->paint( ptr );
01393                             ptr->setClipping( hadClipping );
01394                         }
01395 
01396                         //qDebug() << step;
01397                         calculateNextLabel( labelValue, step, isLogarithmicY, dimensions.last().start );
01398                     }
01399                 }
01400             }
01401         }
01402         delete labelItem;
01403         delete labelItem2;
01404     }
01405 
01406     // this draws the subunit rulers
01407     if ( drawSubUnitRulers && d->annotations.isEmpty() ) {
01408         ptr->save();
01409 
01410         d->drawSubUnitRulers( ptr, plane, dim, rulerRef, isAbscissa() ? drawnAbscissaTicks : drawnYTicks, diagramIsVertical, rulerAttr );
01411 
01412         ptr->restore();
01413     }
01414 
01415     if( ! titleText().isEmpty() ) {
01416         d->drawTitleText( ptr, plane, areaGeoRect );
01417     }
01418 
01419     //qDebug() << "KDChart::CartesianAxis::paintCtx() done.";
01420 }
01421 
01422 
01423 /* pure virtual in QLayoutItem */
01424 bool CartesianAxis::isEmpty() const
01425 {
01426     return false; // if the axis exists, it has some (perhaps default) content
01427 }
01428 /* pure virtual in QLayoutItem */
01429 Qt::Orientations CartesianAxis::expandingDirections() const
01430 {
01431     Qt::Orientations ret;
01432     switch ( position() )
01433     {
01434     case Bottom:
01435     case Top:
01436         ret = Qt::Horizontal;
01437         break;
01438     case Left:
01439     case Right:
01440         ret = Qt::Vertical;
01441         break;
01442     default:
01443         Q_ASSERT( false ); // all positions need to be handeld
01444         break;
01445     };
01446     return ret;
01447 }
01448 
01449 
01450 static void calculateOverlap( int i, int first, int last,
01451                               int measure,
01452                               bool centerAbscissaTicks,
01453                               int& firstOverlap, int& lastOverlap )
01454 {
01455     if( i == first ){
01456         if( centerAbscissaTicks ){
01457             //TODO(khz): Calculate the amount of left overlap
01458             //           for bar diagrams.
01459         }else{
01460             firstOverlap = measure / 2;
01461         }
01462     }
01463     // we test both bounds in on go: first and last might be equal
01464     if( i == last ){
01465         if( centerAbscissaTicks ){
01466             //TODO(khz): Calculate the amount of right overlap
01467             //           for bar diagrams.
01468         }else{
01469             lastOverlap = measure / 2;
01470         }
01471     }
01472 }
01473 
01474 
01475 void CartesianAxis::setCachedSizeDirty() const
01476 {
01477     d->cachedMaximumSize = QSize();
01478 }
01479 
01480 /* pure virtual in QLayoutItem */
01481 QSize CartesianAxis::maximumSize() const
01482 {
01483     if( ! d->cachedMaximumSize.isValid() )
01484         d->cachedMaximumSize = d->calculateMaximumSize();
01485     return d->cachedMaximumSize;
01486 }
01487 
01488 QSize CartesianAxis::Private::calculateMaximumSize() const
01489 {
01490     QSize result;
01491     if ( !diagram() )
01492         return result;
01493 
01494     const AbstractCartesianDiagram * dia = qobject_cast< const AbstractCartesianDiagram * >( diagram() );
01495     if( dia && dia->referenceDiagram() )
01496         dia = dia->referenceDiagram();
01497     const BarDiagram *barDiagram = qobject_cast< const BarDiagram* >( dia );
01498     const Qt::Orientation diagramOrientation = barDiagram != 0 ? barDiagram->orientation() : Qt::Vertical;
01499     const bool diagramIsVertical = diagramOrientation == Qt::Vertical;
01500 
01501     const TextAttributes labelTA = mAxis->textAttributes();
01502     const bool drawLabels = labelTA.isVisible();
01503 
01504     const TextAttributes titleTA( titleTextAttributesWithAdjustedRotation() );
01505     const bool drawTitle = titleTA.isVisible() && ! axis()->titleText().isEmpty();
01506 
01507     AbstractCoordinatePlane* plane = diagram()->coordinatePlane();
01508     //qDebug() << this<<"::maximumSize() uses plane geometry" << plane->geometry();
01509     QObject* refArea = plane->parent();
01510     TextLayoutItem labelItem( QString(), labelTA, refArea,
01511                               KDChartEnums::MeasureOrientationMinimum, Qt::AlignLeft );
01512     TextLayoutItem titleItem( axis()->titleText(), titleTA, refArea,
01513                               KDChartEnums::MeasureOrientationMinimum, Qt::AlignHCenter | Qt::AlignVCenter );
01514 
01515     const QFontMetrics fm( labelItem.realFont(), GlobalMeasureScaling::paintDevice() );
01516 
01517     const qreal labelGap =
01518         drawLabels
01519         ? ( (diagramIsVertical ? fm.height() : fm.averageCharWidth()) / 3.0)
01520         : 0.0;
01521     const QFontMetricsF titleFM = QFontMetricsF( titleItem.realFont(), GlobalMeasureScaling::paintDevice() );
01522     const qreal titleGap =
01523         drawTitle
01524         ? ( (diagramIsVertical ? titleFM.height() : titleFM.averageCharWidth()) / 3.0)
01525         : 0.0;
01526 
01527     if ( axis()->isAbscissa() ) {
01528         const bool centerAbscissaTicks = referenceDiagramNeedsCenteredAbscissaTicks(diagram());
01529         int leftOverlap = 0;
01530         int rightOverlap = 0;
01531 
01532         qreal w = diagramIsVertical ? 10.0 : 0.0;
01533         qreal h = diagramIsVertical ? 0.0 : 10.0;
01534         if( drawLabels ){
01535             // if there're no label strings, we take the biggest needed number as height
01536             if( !annotations.isEmpty() )
01537             {
01538                 const QStringList strings = annotations.values();
01539                 KDAB_FOREACH( const QString& string, strings )
01540                 {
01541                     labelItem.setText( string );
01542                     const QSize siz = labelItem.sizeHint();
01543                     if ( diagramIsVertical )
01544                         h = qMax( h, static_cast< qreal >( siz.height() ) );
01545                     else
01546                         w = qMax( w, static_cast< qreal >( siz.width() ) );
01547                 }
01548             }
01549             else if ( !axis()->labels().isEmpty() )
01550             {
01551                 // find the longest label text:
01552                 const int first=0;
01553                 const int last=axis()->labels().count()-1;
01554                 const QStringList labelsList( axis()->labels() );
01555                 for ( int i = first; i <= last; ++i )
01556                 {
01557                     labelItem.setText( axis()->customizedLabel(labelsList[ i ]) );
01558                     const QSize siz = labelItem.sizeHint();
01559                     //qDebug()<<siz;
01560                     if ( diagramIsVertical )
01561                         h = qMax( h, static_cast<qreal>(siz.height()) );
01562                     else
01563                         w = qMax( w, static_cast<qreal>(siz.width()) );
01564                     calculateOverlap( i, first, last, diagramIsVertical ? siz.width() : siz.height(), centerAbscissaTicks,
01565                                       leftOverlap, rightOverlap );
01566 
01567                 }
01568             }
01569             else
01570             {
01571                 QStringList headerLabels = diagram()->itemRowLabels();
01572                 const int headerLabelsCount = headerLabels.count();
01573                 if( headerLabelsCount ){
01574                     if( cachedHeaderLabels == headerLabels && ( diagramIsVertical ? cachedFontHeight == fm.height() : cachedFontWidth == fm.averageCharWidth() )) {
01575                         if ( diagramIsVertical )
01576                             h = cachedLabelHeight;
01577                         else
01578                             w = cachedLabelWidth;
01579                     } else {
01580                         cachedHeaderLabels = headerLabels;
01581                         if ( diagramIsVertical )
01582                             cachedFontWidth = fm.averageCharWidth();
01583                         else
01584                             cachedFontHeight = fm.height();
01585                         const bool useFastCalcAlgorithm
01586                             = (strcmp( axis()->metaObject()->className(), "KDChart::CartesianAxis" ) == 0);
01587                         const int first=0;
01588                         const int last=headerLabelsCount-1;
01589                         for ( int i = first;
01590                             i <= last;
01591                             i = (useFastCalcAlgorithm && i < last) ? last : (i+1) )
01592                         {
01593                             labelItem.setText( axis()->customizedLabel(headerLabels[ i ]) );
01594                             const QSize siz = labelItem.sizeHint();
01595                             if ( diagramIsVertical ) {
01596                                 h = qMax( h, static_cast<qreal>(siz.height()) );
01597                                 cachedLabelHeight = h;
01598                             } else {
01599                                 cachedLabelWidth = w;
01600                                 w = qMax( w, static_cast<qreal>(siz.width()) );
01601                             }
01602                             calculateOverlap( i, first, last, diagramIsVertical ? siz.width() : siz.height(), centerAbscissaTicks,
01603                                             leftOverlap, rightOverlap );
01604                         }
01605                     }
01606                 }else{
01607                     labelItem.setText(
01608                             axis()->customizedLabel(
01609                                     QString::number( diagramIsVertical ? plane->gridDimensionsList().first().end
01610                                                                        : plane->gridDimensionsList().last().end, 'f', 0 )));
01611                     const QSize siz = labelItem.sizeHint();
01612                     if ( diagramIsVertical )
01613                         h = siz.height();
01614                     else
01615                         w = siz.width();
01616                     calculateOverlap( 0, 0, 0, siz.width(), centerAbscissaTicks,
01617                                       leftOverlap, rightOverlap );
01618                 }
01619             }
01620             // we leave a little gap between axis labels and bottom (or top, resp.) side of axis
01621             h += labelGap;
01622         }
01623         // space for a possible title:
01624         if ( drawTitle ) {
01625             // we add the title height and leave a little gap between axis labels and axis title
01626             if ( diagramIsVertical ) {
01627                 h += titleItem.sizeHint().height() + titleGap;
01628                 w = titleItem.sizeHint().width() + 2.0;
01629             } else {
01630                 h = titleItem.sizeHint().height() + 2.0;
01631                 w += titleItem.sizeHint().width() + titleGap;
01632             }
01633         }
01634         // space for the ticks
01635         if ( diagramIsVertical )
01636             h += qAbs( axis()->tickLength() ) * 3.0;
01637         else
01638             w += qAbs( axis()->tickLength() ) * 3.0;
01639         result = QSize ( static_cast<int>( w ), static_cast<int>( h ) );
01640 
01641         //qDebug()<<"calculated size of x axis:"<<result;
01642 
01643         // If necessary adjust the widths
01644         // of the left (or right, resp.) side neighboring columns:
01645         amountOfLeftOverlap = leftOverlap;
01646         amountOfRightOverlap = rightOverlap;
01647         /* Unused code for a push-model:
01648         if( leftOverlap || rightOverlap ){
01649             QTimer::singleShot(200, const_cast<CartesianAxis*>(this),
01650                                SLOT(adjustLeftRightGridColumnWidths()));
01651         }
01652         */
01653     } else {
01654         int topOverlap = 0;
01655         int bottomOverlap = 0;
01656 
01657         qreal w = diagramIsVertical ? 0.0 : 10.0;
01658         qreal h = diagramIsVertical ? 10.0 : 0.0;
01659         if( drawLabels ){
01660             // if there're no label strings, we loop through the values
01661             // taking the longest (not largest) number - e.g. 0.00001 is longer than 100
01662             if( !annotations.isEmpty() )
01663             {
01664                 const QStringList strings = annotations.values();
01665                 KDAB_FOREACH( const QString& string, strings )
01666                 {
01667                     labelItem.setText( string );
01668                     const QSize siz = labelItem.sizeHint();
01669                     if ( diagramIsVertical )
01670                         w = qMax( w, static_cast< qreal >( siz.width() ) );
01671                     else
01672                         h = qMax( h, static_cast< qreal >( siz.height() ) );
01673                 }
01674             }
01675             else if( axis()->labels().isEmpty() )
01676             {
01677                 const DataDimension dimY = AbstractGrid::adjustedLowerUpperRange(
01678                         diagramIsVertical ? plane->gridDimensionsList().last()
01679                                           : plane->gridDimensionsList().first(), true, true );
01680                 const double step = dimY.stepWidth;
01681                 const qreal minValue = dimY.start;
01682                 const qreal maxValue = dimY.end;
01683                 const bool isLogarithmicY = (dimY.calcMode == AbstractCoordinatePlane::Logarithmic );
01684                 qreal labelValue = minValue;
01685 
01686                 while( labelValue <= maxValue ) {
01687                     const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) +
01688                                             QString::number( labelValue ) +
01689                                             diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true );
01690                     labelItem.setText( axis()->customizedLabel( labelText ) );
01691 
01692                     const QSize siz = labelItem.sizeHint();
01693                     if ( diagramIsVertical )
01694                         w = qMax( w, (qreal)siz.width() );
01695                     else
01696                         h = qMax( h, (qreal)siz.height() );
01697                     calculateOverlap( 0, 0, 0, diagramIsVertical ? siz.height() : siz.width(), false,// bar diagram flag is ignored for Ordinates
01698                                     topOverlap, bottomOverlap );
01699                     calculateNextLabel( labelValue, step, isLogarithmicY, plane->gridDimensionsList().last().start );
01700 
01701                     if(maxValue == 0 && minValue == 0)
01702                         break;
01703                 }
01704             }else{
01705                 // find the longest label text:
01706                 const int first=0;
01707                 const int last=axis()->labels().count()-1;
01708                 const QStringList labelsList( axis()->labels() );
01709                 for ( int i = first; i <= last; ++i )
01710                 {
01711                     labelItem.setText( axis()->customizedLabel(labelsList[ i ]) );
01712                     const QSize siz = labelItem.sizeHint();
01713                     if ( diagramIsVertical )
01714                                             w = qMax( w, (qreal)siz.width() );
01715                                         else
01716                                             h = qMax( h, (qreal)siz.height() );
01717                     calculateOverlap( 0, 0, 0, diagramIsVertical ? siz.height() : siz.width(), false,// bar diagram flag is ignored for Ordinates
01718                                       topOverlap, bottomOverlap );
01719                 }
01720             }
01721             // we leave a little gap between axis labels and left (or right, resp.) side of axis
01722             w += labelGap;
01723         }
01724         // space for a possible title:
01725         if ( drawTitle ) {
01726             // we add the title height and leave a little gap between axis labels and axis title
01727             if ( diagramIsVertical ) {
01728                 w += titleItem.sizeHint().width() + titleGap;
01729                 h = titleItem.sizeHint().height() + 2.0;
01730             } else {
01731                 w = titleItem.sizeHint().width() + 2.0;
01732                 h += titleItem.sizeHint().height() + titleGap;
01733             }
01734             //qDebug() << "left/right axis title item size-hint:" << titleItem.sizeHint();
01735         }
01736         // space for the ticks
01737         if ( diagramIsVertical )
01738             w += qAbs( axis()->tickLength() ) * 3.0;
01739         else
01740             h += qAbs( axis()->tickLength() ) * 3.0;
01741 
01742         result = QSize ( static_cast<int>( w ), static_cast<int>( h ) );
01743         //qDebug() << "left/right axis width:" << result << "   w:" << w;
01744 
01745 
01746         // If necessary adjust the heights
01747         // of the top (or bottom, resp.) side neighboring rows:
01748         amountOfTopOverlap = topOverlap;
01749         amountOfBottomOverlap = bottomOverlap;
01750         /* Unused code for a push-model:
01751         if( topOverlap || bottomOverlap ){
01752             QTimer::singleShot(200, const_cast<CartesianAxis*>(this),
01753                                SLOT(adjustTopBottomGridRowHeights()));
01754         }
01755         */
01756     }
01757 //qDebug() << "*******************" << result;
01758     //result=QSize(0,0);
01759     return result;
01760 }
01761 /* pure virtual in QLayoutItem */
01762 QSize CartesianAxis::minimumSize() const
01763 {
01764     return maximumSize();
01765 }
01766 /* pure virtual in QLayoutItem */
01767 QSize CartesianAxis::sizeHint() const
01768 {
01769     return maximumSize();
01770 }
01771 /* pure virtual in QLayoutItem */
01772 void CartesianAxis::setGeometry( const QRect& r )
01773 {
01774 //    qDebug() << "KDChart::CartesianAxis::setGeometry(" << r << ") called"
01775 //             << (isAbscissa() ? "for Abscissa":"for Ordinate") << "axis";
01776     d->geometry = r;
01777     setCachedSizeDirty();
01778 }
01779 /* pure virtual in QLayoutItem */
01780 QRect CartesianAxis::geometry() const
01781 {
01782     return d->geometry;
01783 }
01784 
01785 int CartesianAxis::tickLength( bool subUnitTicks ) const
01786 {
01787     int result = 0;
01788 
01789     if ( isAbscissa() ) {
01790         result = position() == Top ? -4 : 3;
01791     } else {
01792         result = position() == Left ? -4 : 3;
01793     }
01794 
01795     if ( subUnitTicks )
01796         result = result < 0 ? result + 1 : result - 1;
01797 
01798     return result;
01799 }
01800 
01801 QMap< double, QString > CartesianAxis::annotations() const
01802 {
01803     return d->annotations;
01804 }
01805 
01806 void CartesianAxis::setAnnotations( const QMap< double, QString >& annotations )
01807 {
01808     if( d->annotations == annotations )
01809         return;
01810 
01811     d->annotations = annotations;
01812     update();
01813 }
01814 
01815 QList< double > CartesianAxis::customTicks() const
01816 {
01817     return d->customTicksPositions;
01818 }
01819 
01820 void CartesianAxis::setCustomTicks( const QList< double >& customTicksPositions )
01821 {
01822     if( d->customTicksPositions == customTicksPositions )
01823         return;
01824 
01825     d->customTicksPositions = customTicksPositions;
01826     update();
01827 }
01828 
01829 
01830 /* unused code from KDChartCartesianAxis.h for using a push-model:
01831 Q_SIGNALS:
01832     void needAdjustLeftRightColumnsForOverlappingLabels(
01833             CartesianAxis* axis, int left, int right );
01834     void needAdjustTopBottomRowsForOverlappingLabels(
01835             CartesianAxis* axis, int top, int bottom );
01836 private Q_SLOTS:
01837     void adjustLeftRightGridColumnWidths();
01838     void adjustTopBottomGridRowHeights();
01839 */
01840 
01841 /*
01842 // Unused code trying to use a push-model: This did not work
01843 // since we can not re-layout the planes each time when
01844 // Qt layouting is calling sizeHint()
01845 void CartesianAxis::adjustLeftRightGridColumnWidths()
01846 {
01847     if( ! d->amountOfLeftOverlap && ! d->amountOfRightOverlap )
01848         return;
01849     const int leftOverlap = d->amountOfLeftOverlap;
01850     const int rightOverlap= d->amountOfRightOverlap;
01851     d->amountOfLeftOverlap = 0;
01852     d->amountOfRightOverlap = 0;
01853     emit needAdjustLeftRightColumnsForOverlappingLabels(
01854             this, leftOverlap, rightOverlap );
01855 }
01856 
01857 void CartesianAxis::adjustTopBottomGridRowHeights()
01858 {
01859     if( ! d->amountOfTopOverlap && ! d->amountOfBottomOverlap )
01860         return;
01861     const int topOverlap = d->amountOfTopOverlap;
01862     const int bottomOverlap= d->amountOfBottomOverlap;
01863     d->amountOfTopOverlap = 0;
01864     d->amountOfBottomOverlap = 0;
01865     emit needAdjustTopBottomRowsForOverlappingLabels(
01866             this, topOverlap, bottomOverlap );
01867 }
01868 */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Defines

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