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