KDChartCartesianAxis.cpp

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

Generated on Thu Mar 4 23:19:10 2010 for KD Chart 2 by  doxygen 1.5.4