00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include <cmath>
00027
00028 #include <QtDebug>
00029 #include <QPainter>
00030 #include <QPen>
00031 #include <QBrush>
00032 #include <QApplication>
00033
00034 #include "KDChartPaintContext.h"
00035 #include "KDChartChart.h"
00036 #include "KDChartCartesianAxis.h"
00037 #include "KDChartCartesianAxis_p.h"
00038 #include "KDChartAbstractCartesianDiagram.h"
00039 #include "KDChartAbstractGrid.h"
00040 #include "KDChartPainterSaver_p.h"
00041 #include "KDChartLayoutItems.h"
00042 #include "KDChartBarDiagram.h"
00043
00044 #include <KDABLibFakes>
00045
00046 #include <limits>
00047
00048 using namespace KDChart;
00049
00050 #define d (d_func())
00051
00052 CartesianAxis::CartesianAxis ( AbstractCartesianDiagram* diagram )
00053 : AbstractAxis ( new Private( diagram, this ), diagram )
00054 {
00055 init();
00056 }
00057
00058 CartesianAxis::~CartesianAxis ()
00059 {
00060
00061
00062 while ( d->mDiagram ) {
00063 AbstractCartesianDiagram *cd = qobject_cast<AbstractCartesianDiagram*>( d->mDiagram );
00064 cd->takeAxis( this );
00065 }
00066 Q_FOREACH( AbstractDiagram *diagram, d->secondaryDiagrams ) {
00067 AbstractCartesianDiagram *cd = qobject_cast<AbstractCartesianDiagram*>( diagram );
00068 cd->takeAxis( this );
00069 }
00070 }
00071
00072 void CartesianAxis::init ()
00073 {
00074 d->position = Bottom;
00075 }
00076
00077
00078 bool CartesianAxis::compare( const CartesianAxis* other )const
00079 {
00080 if( other == this ) return true;
00081 if( ! other ){
00082
00083 return false;
00084 }
00085
00086
00087
00088
00089
00090 return ( static_cast<const AbstractAxis*>(this)->compare( other ) ) &&
00091 ( position() == other->position() ) &&
00092 ( titleText() == other->titleText() ) &&
00093 ( titleTextAttributes() == other->titleTextAttributes() );
00094 }
00095
00096
00097 void CartesianAxis::setTitleText( const QString& text )
00098 {
00099 d->titleText = text;
00100 layoutPlanes();
00101 }
00102
00103 QString CartesianAxis::titleText() const
00104 {
00105 return d->titleText;
00106 }
00107
00108 void CartesianAxis::setTitleTextAttributes( const TextAttributes &a )
00109 {
00110 d->titleTextAttributes = a;
00111 d->useDefaultTextAttributes = false;
00112 layoutPlanes();
00113 }
00114
00115 TextAttributes CartesianAxis::titleTextAttributes() const
00116 {
00117 if( hasDefaultTitleTextAttributes() ){
00118 TextAttributes ta( textAttributes() );
00119 Measure me( ta.fontSize() );
00120 me.setValue( me.value() * 1.5 );
00121 ta.setFontSize( me );
00122 return ta;
00123 }
00124 return d->titleTextAttributes;
00125 }
00126
00127 void CartesianAxis::resetTitleTextAttributes()
00128 {
00129 d->useDefaultTextAttributes = true;
00130 layoutPlanes();
00131 }
00132
00133 bool CartesianAxis::hasDefaultTitleTextAttributes() const
00134 {
00135 return d->useDefaultTextAttributes;
00136 }
00137
00138
00139 void CartesianAxis::setPosition ( Position p )
00140 {
00141 d->position = p;
00142 layoutPlanes();
00143 }
00144
00145 const CartesianAxis::Position CartesianAxis::position() const
00146 {
00147 return d->position;
00148 }
00149
00150 void CartesianAxis::layoutPlanes()
00151 {
00152
00153 if( ! d->diagram() || ! d->diagram()->coordinatePlane() ) {
00154
00155 return;
00156 }
00157 AbstractCoordinatePlane* plane = d->diagram()->coordinatePlane();
00158 if( plane ){
00159 plane->layoutPlanes();
00160
00161 }
00162 }
00163
00164 bool CartesianAxis::isAbscissa() const
00165 {
00166 return position() == Bottom || position() == Top;
00167 }
00168
00169 bool CartesianAxis::isOrdinate() const
00170 {
00171 return position() == Left || position() == Right;
00172 }
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193 void CartesianAxis::paint( QPainter* painter )
00194 {
00195 if( ! d->diagram() || ! d->diagram()->coordinatePlane() ) return;
00196 PaintContext ctx;
00197 ctx.setPainter ( painter );
00198 ctx.setCoordinatePlane( d->diagram()->coordinatePlane() );
00199 const QRect rect( areaGeometry() );
00200
00201
00202
00203 ctx.setRectangle(
00204 QRectF (
00205
00206 QPointF(rect.left(), rect.top()),
00207 QSizeF(rect.width(), rect.height() ) ) );
00208
00209 QRegion clipRegion( rect.adjusted( -1, -1, 1, 1 ) );
00210 painter->save();
00211 painter->setClipRegion( clipRegion );
00212 paintCtx( &ctx );
00213 painter->restore();
00214
00215 }
00216
00217 void CartesianAxis::Private::drawSubUnitRulers( QPainter* painter, CartesianCoordinatePlane* plane, const DataDimension& dim,
00218 const QPointF& rulerRef, const QVector<int>& drawnTicks ) const
00219 {
00220 const QRect geoRect( axis()->geometry() );
00221 int nextMayBeTick = 0;
00222 int mayBeTick = 0;
00223 int logSubstep = 0;
00224 qreal f = dim.start;
00225 qreal fLogSubstep = f;
00226 const bool isAbscissa = axis()->isAbscissa();
00227 const bool isLogarithmic = (dim.calcMode == AbstractCoordinatePlane::Logarithmic );
00228 const int subUnitTickLength = axis()->tickLength( true );
00229 while ( dim.end - f > std::numeric_limits< float >::epsilon() ) {
00230 if( drawnTicks.count() > nextMayBeTick )
00231 mayBeTick = drawnTicks[ nextMayBeTick ];
00232 if ( isAbscissa ) {
00233
00234 QPointF topPoint ( f, 0 );
00235 QPointF bottomPoint ( f, 0 );
00236
00237 topPoint = plane->translate( topPoint );
00238 bottomPoint = plane->translate( bottomPoint );
00239 topPoint.setY( rulerRef.y() + subUnitTickLength );
00240 bottomPoint.setY( rulerRef.y() );
00241 if( qAbs( mayBeTick - topPoint.x() ) > 1 )
00242 painter->drawLine( topPoint, bottomPoint );
00243 else {
00244 ++nextMayBeTick;
00245 }
00246 } else {
00247
00248 QPointF leftPoint = plane->translate( QPointF( 0, f ) );
00249
00250
00251 if( qAbs( mayBeTick - leftPoint.y() ) > 1 ){
00252 const qreal translatedValue = leftPoint.y();
00253 if( translatedValue > geoRect.top() && translatedValue <= geoRect.bottom() ){
00254 QPointF rightPoint ( 0, f );
00255 rightPoint = plane->translate( rightPoint );
00256 leftPoint.setX( rulerRef.x() + subUnitTickLength );
00257 rightPoint.setX( rulerRef.x() );
00258 painter->drawLine( leftPoint, rightPoint );
00259 }
00260 } else {
00261 ++nextMayBeTick;
00262 }
00263 }
00264 if ( isLogarithmic ){
00265 if( logSubstep == 9 ){
00266 fLogSubstep *= 10.0;
00267 if( fLogSubstep == 0 )
00268 fLogSubstep = 1.0;
00269 logSubstep = 0;
00270 }
00271 f += fLogSubstep;
00272 ++logSubstep;
00273 }else{
00274 f += dim.subStepWidth;
00275 }
00276 }
00277 }
00278
00279
00280 const TextAttributes CartesianAxis::Private::titleTextAttributesWithAdjustedRotation() const
00281 {
00282 TextAttributes titleTA( titleTextAttributes );
00283 if( axis()->isOrdinate() ){
00284 int rotation = titleTA.rotation() + 270;
00285 if( rotation >= 360 )
00286 rotation -= 360;
00287
00288
00289 if( rotation < 90 )
00290 rotation = 0;
00291 else if( rotation < 180 )
00292 rotation = 90;
00293 else if( rotation < 270 )
00294 rotation = 180;
00295 else if( rotation < 360 )
00296 rotation = 270;
00297 else
00298 rotation = 0;
00299
00300 titleTA.setRotation( rotation );
00301 }
00302 return titleTA;
00303 }
00304
00305
00306 void CartesianAxis::Private::drawTitleText( QPainter* painter, CartesianCoordinatePlane* plane, const QRect& areaGeoRect ) const
00307 {
00308 const TextAttributes titleTA( titleTextAttributesWithAdjustedRotation() );
00309 if( titleTA.isVisible() ) {
00310 TextLayoutItem titleItem( titleText,
00311 titleTA,
00312 plane->parent(),
00313 KDChartEnums::MeasureOrientationMinimum,
00314 Qt::AlignHCenter|Qt::AlignVCenter );
00315 QPointF point;
00316 const QSize size( titleItem.sizeHint() );
00317
00318
00319 switch( position )
00320 {
00321 case Top:
00322 point.setX( areaGeoRect.left() + areaGeoRect.width() / 2.0);
00323 point.setY( areaGeoRect.top() + size.height() / 2 );
00324 break;
00325 case Bottom:
00326 point.setX( areaGeoRect.left() + areaGeoRect.width() / 2.0);
00327 point.setY( areaGeoRect.bottom() - size.height() / 2 );
00328 break;
00329 case Left:
00330 point.setX( areaGeoRect.left() + size.width() / 2 );
00331 point.setY( areaGeoRect.top() + areaGeoRect.height() / 2.0);
00332 break;
00333 case Right:
00334 point.setX( areaGeoRect.right() - size.width() / 2 );
00335 point.setY( areaGeoRect.top() + areaGeoRect.height() / 2.0);
00336 break;
00337 }
00338 PainterSaver painterSaver( painter );
00339 painter->translate( point );
00340
00341
00342 titleItem.setGeometry( QRect( QPoint(-size.width() / 2, -size.height() / 2), size ) );
00343
00344 titleItem.paint( painter );
00345 }
00346 }
00347
00348
00349 static void calculateNextLabel( qreal& labelValue, qreal step, bool isLogarithmic)
00350 {
00351 if ( isLogarithmic ){
00352 labelValue *= 10.0;
00353 if( labelValue == 0.0 )
00354 labelValue = 1.0;
00355 }else{
00356
00357 labelValue += step;
00358 }
00359
00360
00361 }
00362
00363
00364 static bool referenceDiagramIsBarDiagram( const AbstractDiagram * diagram )
00365 {
00366 const AbstractCartesianDiagram * dia =
00367 qobject_cast< const AbstractCartesianDiagram * >( diagram );
00368 if( dia && dia->referenceDiagram() )
00369 dia = dia->referenceDiagram();
00370 return qobject_cast< const BarDiagram* >( dia ) != 0;
00371 }
00372
00373
00374 void CartesianAxis::paintCtx( PaintContext* context )
00375 {
00376
00377 Q_ASSERT_X ( d->diagram(), "CartesianAxis::paint",
00378 "Function call not allowed: The axis is not assigned to any diagram." );
00379
00380 CartesianCoordinatePlane* plane = dynamic_cast<CartesianCoordinatePlane*>(context->coordinatePlane());
00381 Q_ASSERT_X ( plane, "CartesianAxis::paint",
00382 "Bad function call: PaintContext::coodinatePlane() NOT a cartesian plane." );
00383
00384
00385
00386 if( ! d->diagram()->model() )
00387 return;
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397 DataDimensionsList dimensions( plane->gridDimensionsList() );
00398
00399
00400
00401 Q_ASSERT_X ( dimensions.count() == 2, "CartesianAxis::paint",
00402 "Error: plane->gridDimensionsList() did not return exactly two dimensions." );
00403 DataDimension dimX =
00404 AbstractGrid::adjustedLowerUpperRange( dimensions.first(), true, true );
00405 const DataDimension dimY =
00406 AbstractGrid::adjustedLowerUpperRange( dimensions.last(), true, true );
00407 const DataDimension& dim = (isAbscissa() ? dimX : dimY);
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423 const qreal MinimumPixelsBetweenRulers = qMin( dimX.stepWidth, dimY.stepWidth );
00424
00425
00426
00427 const qreal absRange = qAbs( dim.distance() );
00428
00429 qreal numberOfUnitRulers;
00430 if ( isAbscissa() ) {
00431 if( dimX.isCalculated )
00432 numberOfUnitRulers = absRange / qAbs( dimX.stepWidth ) + 1.0;
00433 else
00434 numberOfUnitRulers = d->diagram()->model()->rowCount() - 1.0;
00435 }else{
00436 numberOfUnitRulers = absRange / qAbs( dimY.stepWidth ) + 1.0;
00437
00438 }
00439
00440
00441
00442 qreal numberOfSubUnitRulers;
00443 if ( isAbscissa() ){
00444 if( dimX.isCalculated )
00445 numberOfSubUnitRulers = absRange / qAbs( dimX.subStepWidth ) + 1.0;
00446 else
00447 numberOfSubUnitRulers = dimX.subStepWidth>0 ? absRange / qAbs( dimX.subStepWidth ) + 1.0 : 0.0;
00448 }else{
00449 numberOfSubUnitRulers = absRange / qAbs( dimY.subStepWidth ) + 1.0;
00450 }
00451
00452
00453 const QPointF p1 = plane->translate( QPointF(dimX.start, dimY.start) );
00454 const QPointF p2 = plane->translate( QPointF(dimX.end, dimY.end) );
00455
00456 double screenRange;
00457 if ( isAbscissa() )
00458 {
00459 screenRange = qAbs ( p1.x() - p2.x() );
00460 } else {
00461 screenRange = qAbs ( p1.y() - p2.y() );
00462 }
00463
00464 const bool useItemCountLabels = isAbscissa() && ! dimX.isCalculated;
00465
00466 const bool drawUnitRulers = screenRange / ( numberOfUnitRulers / dimX.stepWidth ) > MinimumPixelsBetweenRulers;
00467 const bool drawSubUnitRulers =
00468 (numberOfSubUnitRulers != 0.0) &&
00469 (screenRange / numberOfSubUnitRulers > MinimumPixelsBetweenRulers);
00470
00471 const TextAttributes labelTA = textAttributes();
00472 const bool drawLabels = labelTA.isVisible();
00473
00474
00475 QPointF rulerRef;
00476 const QRect areaGeoRect( areaGeometry() );
00477 const QRect geoRect( geometry() );
00478 QRectF rulerRect;
00479 double rulerWidth;
00480 double rulerHeight;
00481
00482 QPainter* const ptr = context->painter();
00483
00484
00485
00486
00487
00488 rulerWidth = areaGeoRect.width();
00489 rulerHeight = areaGeoRect.height();
00490 switch( position() )
00491 {
00492 case Top:
00493 rulerRef.setX( areaGeoRect.topLeft().x() );
00494 rulerRef.setY( areaGeoRect.topLeft().y() + rulerHeight );
00495 break;
00496 case Bottom:
00497 rulerRef.setX( areaGeoRect.bottomLeft().x() );
00498 rulerRef.setY( areaGeoRect.bottomLeft().y() - rulerHeight );
00499 break;
00500 case Right:
00501 rulerRef.setX( areaGeoRect.bottomRight().x() - rulerWidth );
00502 rulerRef.setY( areaGeoRect.bottomRight().y() );
00503 break;
00504 case Left:
00505 rulerRef.setX( areaGeoRect.bottomLeft().x() + rulerWidth );
00506 rulerRef.setY( areaGeoRect.bottomLeft().y() );
00507 break;
00508 }
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522 const qreal minValueY = dimY.start;
00523 const qreal maxValueY = dimY.end;
00524 const qreal minValueX = dimX.start;
00525 const qreal maxValueX = dimX.end;
00526 const bool isLogarithmicX = (dimX.calcMode == AbstractCoordinatePlane::Logarithmic );
00527 const bool isLogarithmicY = (dimY.calcMode == AbstractCoordinatePlane::Logarithmic );
00528
00529 #ifdef AXES_PAINTING_DEBUG
00530 qDebug() << "CartesianAxis::paint: reference values:" << endl
00531 << "-- range x/y: " << dimX.distance() << "/" << dimY.distance() << endl
00532 << "-- absRange: " << absRange << endl
00533 << "-- numberOfUnitRulers: " << numberOfUnitRulers << endl
00534 << "-- screenRange: " << screenRange << endl
00535 << "-- drawUnitRulers: " << drawUnitRulers << endl
00536 << "-- drawLabels: " << drawLabels << endl
00537 << "-- ruler reference point:: " << rulerRef << endl
00538 << "-- minValueX: " << minValueX << " maxValueX: " << maxValueX << endl
00539 << "-- minValueY: " << minValueY << " maxValueY: " << maxValueY << endl
00540 ;
00541 #endif
00542
00543
00544 ptr->setPen ( labelTA.pen() );
00545
00546
00547
00548 const QObject* referenceArea = plane->parent();
00549
00550
00551 QVector< int > drawnXTicks;
00552
00553 QVector< int > drawnYTicks;
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563 const bool isBarDiagram = referenceDiagramIsBarDiagram(d->diagram());
00564
00565
00566 if ( drawUnitRulers ) {
00567 const QStringList labelsList( labels() );
00568 const QStringList shortLabelsList( shortLabels() );
00569 const int hardLabelsCount = labelsList.count();
00570 const int shortLabelsCount = shortLabelsList.count();
00571 bool useShortLabels = false;
00572
00573
00574 bool useConfiguredStepsLabels = false;
00575 QStringList headerLabels;
00576 if( useItemCountLabels ){
00577
00578 headerLabels =
00579 isOrdinate()
00580 ? d->diagram()->datasetLabels()
00581 : d->diagram()->itemRowLabels();
00582
00583 useConfiguredStepsLabels = isAbscissa() &&
00584 dimX.stepWidth &&
00585 (( (headerLabels.count() - 1)/ dimX.stepWidth ) != numberOfUnitRulers);
00586 if( useConfiguredStepsLabels ) {
00587 numberOfUnitRulers = ( headerLabels.count() - 1 )/ dimX.stepWidth;
00588
00589
00590 QStringList configuredStepsLabels;
00591 double value = headerLabels.isEmpty() ? 0.0 : headerLabels.first().toDouble();
00592 configuredStepsLabels << QString::number( value );
00593 for ( int i = 0; i < numberOfUnitRulers; i++ ) {
00594
00595 value += dimX.stepWidth;
00596 configuredStepsLabels.append( QString::number( value ) );
00597 }
00598 headerLabels = configuredStepsLabels;
00599 }
00600
00601 if ( isBarDiagram )
00602 headerLabels.append( QString::null );
00603 }
00604
00605
00606 const int headerLabelsCount = headerLabels.count();
00607
00608
00609 TextLayoutItem* labelItem =
00610 drawLabels
00611 ? new TextLayoutItem( QString::number( minValueY ),
00612 labelTA,
00613 referenceArea,
00614 KDChartEnums::MeasureOrientationMinimum,
00615 Qt::AlignLeft )
00616 : 0;
00617 TextLayoutItem* labelItem2 =
00618 drawLabels
00619 ? new TextLayoutItem( QString::number( minValueY ),
00620 labelTA,
00621 referenceArea,
00622 KDChartEnums::MeasureOrientationMinimum,
00623 Qt::AlignLeft )
00624 : 0;
00625 const QFontMetricsF met(
00626 drawLabels
00627 ? labelItem->realFont()
00628 : QFontMetricsF( QApplication::font(), GlobalMeasureScaling::paintDevice() ) );
00629 const qreal halfFontHeight = met.height() * 0.5;
00630
00631 if ( isAbscissa() ) {
00632
00633
00634
00635 if( drawLabels && hardLabelsCount > 0 && shortLabelsCount > 0 ){
00636 bool labelsAreOverlapping = false;
00637 int iLabel = 0;
00638 qreal i = minValueX;
00639 while ( i < maxValueX && !labelsAreOverlapping )
00640 {
00641 if ( dimX.stepWidth != 1.0 && ! dim.isCalculated )
00642 {
00643 labelItem->setText( customizedLabel(QString::number( i, 'f', 0 )) );
00644 labelItem2->setText( customizedLabel(QString::number( i + dimX.stepWidth, 'f', 0 )) );
00645 } else {
00646
00647 int index = iLabel;
00648 labelItem->setText( customizedLabel(labelsList[ index < hardLabelsCount ? index : 0 ]) );
00649 labelItem2->setText( customizedLabel(labelsList[ index < hardLabelsCount - 1 ? index + 1 : 0]) );
00650 }
00651 QPointF firstPos( i, 0.0 );
00652 firstPos = plane->translate( firstPos );
00653
00654 QPointF secondPos( i + dimX.stepWidth, 0.0 );
00655 secondPos = plane->translate( secondPos );
00656
00657 labelsAreOverlapping = labelItem->intersects( *labelItem2, firstPos, secondPos );
00658 if ( iLabel++ > hardLabelsCount - 1 )
00659 iLabel = 0;
00660 if ( isLogarithmicX )
00661 i *= 10.0;
00662 else
00663 i += dimX.stepWidth;
00664
00665 }
00666
00667 useShortLabels = labelsAreOverlapping;
00668 }
00669
00670 qreal labelDiff = dimX.stepWidth;
00671
00672 if ( drawLabels )
00673 {
00674
00675 qreal i = minValueX;
00676 int iLabel = 0;
00677 const int precision = ( QString::number( labelDiff ).section( QLatin1Char('.'), 1, 2 ) ).length();
00678
00679 while ( i + labelDiff < maxValueX )
00680 {
00681
00682
00683
00684 if ( !drawLabels || hardLabelsCount < 1 || ( dimX.stepWidth != 1.0 && ! dim.isCalculated ) )
00685 {
00686
00687
00688 if( useConfiguredStepsLabels ){
00689 labelItem->setText( customizedLabel(headerLabels[ iLabel ]) );
00690 labelItem2->setText(customizedLabel(headerLabels[ iLabel+1 ]) );
00691 }else{
00692
00693 labelItem->setText( customizedLabel(headerLabelsCount > i && i >= 0 ?
00694 headerLabels[static_cast<int>(i)] :
00695 QString::number( i, 'f', precision )) );
00696
00697
00698
00699 labelItem2->setText( customizedLabel(headerLabelsCount > i + labelDiff && i + labelDiff >= 0 ?
00700 headerLabels[static_cast<int>(i+labelDiff)] :
00701 QString::number( i + labelDiff, 'f', precision )) );
00702
00703
00704 }
00705 } else {
00706 const int idx = (iLabel < hardLabelsCount ) ? iLabel : 0;
00707 const int idx2= (iLabel < hardLabelsCount - 1) ? iLabel + 1 : 0;
00708 const int shortIdx = (iLabel < shortLabelsCount ) ? iLabel : 0;
00709 const int shortIdx2 = (iLabel < shortLabelsCount - 1) ? iLabel + 1 : 0;
00710 labelItem->setText( customizedLabel(
00711 useShortLabels ? shortLabelsList[ shortIdx ] : labelsList[ idx ] ) );
00712 labelItem2->setText( customizedLabel(
00713 useShortLabels ? shortLabelsList[ shortIdx2 ] : labelsList[ idx2 ] ) );
00714 }
00715
00716 QPointF firstPos( i, 0.0 );
00717 firstPos = plane->translate( firstPos );
00718
00719 QPointF secondPos( i + labelDiff, 0.0 );
00720 secondPos = plane->translate( secondPos );
00721
00722
00723 if ( labelItem->intersects( *labelItem2, firstPos, secondPos ) )
00724 {
00725 i = minValueX;
00726
00727
00728 labelDiff *= 10.0;
00729
00730
00731
00732 iLabel = 0;
00733 }
00734 else
00735 {
00736 i += labelDiff;
00737 }
00738
00739 ++iLabel;
00740 if ( (iLabel > hardLabelsCount - 1) && !useConfiguredStepsLabels )
00741 {
00742 iLabel = 0;
00743 }
00744 }
00745 }
00746
00747 int idxLabel = 0;
00748 qreal iLabelF = minValueX;
00749
00750 qreal i = minValueX;
00751 qreal labelStep = 0.0;
00752
00753
00754 while( i <= maxValueX ) {
00755
00756
00757 QPointF topPoint ( i + ( isBarDiagram ? 0.5 : 0.0 ), 0.0 );
00758 QPointF bottomPoint ( topPoint );
00759 topPoint = plane->translate( topPoint );
00760 bottomPoint = plane->translate( bottomPoint );
00761 topPoint.setY( rulerRef.y() + tickLength() );
00762 bottomPoint.setY( rulerRef.y() );
00763
00764 const qreal translatedValue = topPoint.x();
00765 const bool bIsVisibleLabel =
00766 ( translatedValue >= geoRect.left() && translatedValue <= geoRect.right() );
00767
00768
00769 bool painttick = bIsVisibleLabel && labelStep <= 0;;
00770
00771
00772
00773
00774
00775 if ( isBarDiagram && i == maxValueX )
00776 painttick = false;
00777
00778 if ( bIsVisibleLabel && painttick )
00779 ptr->drawLine( topPoint, bottomPoint );
00780
00781 drawnXTicks.append( static_cast<int>( topPoint.x() ) );
00782 if( drawLabels ) {
00783 if( bIsVisibleLabel ){
00784 if ( isLogarithmicX )
00785 labelItem->setText( customizedLabel(QString::number( i, 'f', 0 )) );
00786
00787
00788
00789
00790
00791
00792
00793
00794
00795
00796 else {
00797 const int idx = idxLabel + static_cast<int>(minValueX);
00798 labelItem->setText(
00799 customizedLabel(
00800 hardLabelsCount
00801 ? ( useShortLabels ? shortLabelsList[ idx ] : labelsList[ idx ] )
00802 : ( headerLabelsCount ? headerLabels[ idx ] : QString::number( iLabelF ))));
00803
00804 }
00805
00806
00807 if( labelStep <= 0 ) {
00808 const PainterSaver p( ptr );
00809 const QSize size( labelItem->sizeHint() );
00810 labelItem->setGeometry(
00811 QRect(
00812 QPoint(
00813 static_cast<int>( topPoint.x() - size.width() / 2 ),
00814 static_cast<int>( topPoint.y() +
00815 ( position() == Bottom
00816 ? halfFontHeight
00817 : ((halfFontHeight + size.height()) * -1.0) ) ) ),
00818 size ) );
00819
00820 QRect labelGeo = labelItem->geometry();
00821
00822 if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() )
00823 ptr->setClipping( false );
00824 if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() )
00825 ptr->setClipping( false );
00826
00827 labelItem->setGeometry( labelGeo );
00828
00829 labelStep = labelDiff - dimX.stepWidth;
00830 labelItem->paint( ptr );
00831
00832
00833 labelItem2->setText( labelItem->text() );
00834
00835 } else {
00836 labelStep -= dimX.stepWidth;
00837 }
00838 }
00839
00840 if( hardLabelsCount ) {
00841 if( useShortLabels && idxLabel >= shortLabelsCount - 1 )
00842 idxLabel = 0;
00843 else if( !useShortLabels && idxLabel >= hardLabelsCount - 1 )
00844 idxLabel = 0;
00845 else{
00846 idxLabel += static_cast<int>(dimX.stepWidth);
00847
00848 }
00849 } else if( headerLabelsCount ) {
00850 if( idxLabel >= headerLabelsCount - 1 ) {
00851 idxLabel = 0;
00852 }else
00853 ++idxLabel;
00854 } else {
00855 iLabelF += dimX.stepWidth;
00856 }
00857 }
00858 if ( isLogarithmicX )
00859 {
00860 i *= 10.0;
00861 if( i == 0.0 )
00862 i = 1.0;
00863 }
00864 else
00865 {
00866 i += dimX.stepWidth;
00867 }
00868 }
00869 } else {
00870 const PainterSaver p( ptr );
00871 const double maxLimit = maxValueY;
00872 const double steg = dimY.stepWidth;
00873 int maxLabelsWidth = 0;
00874 qreal labelValue;
00875 if( drawLabels && position() == Right ){
00876
00877
00878 labelValue = minValueY;
00879 while ( labelValue <= maxLimit ) {
00880 const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), Qt::Vertical, true ) +
00881 QString::number( labelValue ) +
00882 diagram()->unitSuffix( static_cast< int >( labelValue ), Qt::Vertical, true );
00883 labelItem->setText( customizedLabel( labelText ) );
00884 maxLabelsWidth = qMax( maxLabelsWidth, labelItem->sizeHint().width() );
00885
00886 calculateNextLabel( labelValue, steg, isLogarithmicY );
00887 }
00888 }
00889
00890 ptr->setClipping( false );
00891 labelValue = minValueY;
00892 qreal step = steg;
00893 bool nextLabel = false;
00894
00895
00896 if( drawLabels )
00897 {
00898
00899 while( labelValue <= maxLimit ) {
00900 QPointF leftPoint = plane->translate( QPointF( 0, labelValue ) );
00901 const qreal translatedValue = leftPoint.y();
00902
00903
00904 if( translatedValue > geoRect.top() && translatedValue <= geoRect.bottom() ){
00905 const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), Qt::Vertical, true ) +
00906 QString::number( labelValue ) +
00907 diagram()->unitSuffix( static_cast< int >( labelValue ), Qt::Vertical, true );
00908 const QString label2Text = diagram()->unitPrefix( static_cast< int >( labelValue + step ), Qt::Vertical, true ) +
00909 QString::number( labelValue + step ) +
00910 diagram()->unitSuffix( static_cast< int >( labelValue + step ), Qt::Vertical, true );
00911 labelItem->setText( customizedLabel( labelText ) );
00912 labelItem2->setText( customizedLabel( QString::number( labelValue + step ) ) );
00913 QPointF nextPoint = plane->translate( QPointF( 0, labelValue + step ) );
00914 if ( labelItem->intersects( *labelItem2, leftPoint, nextPoint ) )
00915 {
00916 step += steg;
00917 nextLabel = false;
00918 }else{
00919 nextLabel = true;
00920 }
00921 }else{
00922 nextLabel = true;
00923 }
00924
00925 if ( nextLabel || isLogarithmicY )
00926 calculateNextLabel( labelValue, step, isLogarithmicY );
00927 else
00928 labelValue = minValueY;
00929 }
00930
00931
00932 labelValue = minValueY;
00933
00934 while( labelValue <= maxLimit ) {
00935
00936 const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), Qt::Vertical, true ) +
00937 QString::number( labelValue ) +
00938 diagram()->unitSuffix( static_cast< int >( labelValue ), Qt::Vertical, true );
00939 labelItem->setText( customizedLabel( labelText ) );
00940 QPointF leftPoint = plane->translate( QPointF( 0, labelValue ) );
00941 QPointF rightPoint ( 0.0, labelValue );
00942 rightPoint = plane->translate( rightPoint );
00943 leftPoint.setX( rulerRef.x() + tickLength() );
00944 rightPoint.setX( rulerRef.x() );
00945
00946 const qreal translatedValue = rightPoint.y();
00947 const bool bIsVisibleLabel =
00948 ( translatedValue >= geoRect.top() && translatedValue <= geoRect.bottom() );
00949
00950 if( bIsVisibleLabel ){
00951 ptr->drawLine( leftPoint, rightPoint );
00952 drawnYTicks.append( static_cast<int>( leftPoint.y() ) );
00953 const QSize labelSize( labelItem->sizeHint() );
00954 leftPoint.setX( leftPoint.x() );
00955 const int x =
00956 static_cast<int>( leftPoint.x() + met.height() * ( position() == Left ? -0.5 : 0.5) )
00957 - ( position() == Left ? labelSize.width() : (labelSize.width() - maxLabelsWidth) );
00958 const int y =
00959 static_cast<int>( leftPoint.y() - ( met.ascent() + met.descent() ) * 0.6 );
00960 labelItem->setGeometry( QRect( QPoint( x, y ), labelSize ) );
00961 labelItem->paint( ptr );
00962 }
00963
00964 calculateNextLabel( labelValue, step, isLogarithmicY );
00965 }
00966 }
00967 }
00968 delete labelItem;
00969 delete labelItem2;
00970 }
00971
00972
00973 if ( drawSubUnitRulers ) {
00974 d->drawSubUnitRulers( ptr, plane, dim, rulerRef, isAbscissa() ? drawnXTicks : drawnYTicks );
00975 }
00976
00977 if( ! titleText().isEmpty() ){
00978 d->drawTitleText( ptr, plane, areaGeoRect );
00979 }
00980
00981
00982 }
00983
00984
00985
00986 bool CartesianAxis::isEmpty() const
00987 {
00988 return false;
00989 }
00990
00991 Qt::Orientations CartesianAxis::expandingDirections() const
00992 {
00993 Qt::Orientations ret;
00994 switch ( position() )
00995 {
00996 case Bottom:
00997 case Top:
00998 ret = Qt::Horizontal;
00999 break;
01000 case Left:
01001 case Right:
01002 ret = Qt::Vertical;
01003 break;
01004 default:
01005 Q_ASSERT( false );
01006 break;
01007 };
01008 return ret;
01009 }
01010
01011
01012 static void calculateOverlap( int i, int first, int last,
01013 int measure,
01014 bool isBarDiagram,
01015 int& firstOverlap, int& lastOverlap )
01016 {
01017 if( i == first ){
01018 if( isBarDiagram ){
01019
01020
01021 }else{
01022 firstOverlap = measure / 2;
01023 }
01024 }
01025
01026 if( i == last ){
01027 if( isBarDiagram ){
01028
01029
01030 }else{
01031 lastOverlap = measure / 2;
01032 }
01033 }
01034 }
01035
01036
01037
01038 QSize CartesianAxis::maximumSize() const
01039 {
01040 QSize result;
01041 if ( !d->diagram() )
01042 return result;
01043
01044 const TextAttributes labelTA = textAttributes();
01045 const bool drawLabels = labelTA.isVisible();
01046
01047 const TextAttributes titleTA( d->titleTextAttributesWithAdjustedRotation() );
01048 const bool drawTitle = titleTA.isVisible() && ! titleText().isEmpty();
01049
01050 AbstractCoordinatePlane* plane = d->diagram()->coordinatePlane();
01051
01052 QObject* refArea = plane->parent();
01053 TextLayoutItem labelItem( QString::null, labelTA, refArea,
01054 KDChartEnums::MeasureOrientationMinimum, Qt::AlignLeft );
01055 TextLayoutItem titleItem( titleText(), titleTA, refArea,
01056 KDChartEnums::MeasureOrientationMinimum, Qt::AlignHCenter | Qt::AlignVCenter );
01057 const qreal labelGap =
01058 drawLabels
01059 ? (QFontMetricsF( labelItem.realFont(), GlobalMeasureScaling::paintDevice() ).height() / 3.0)
01060 : 0.0;
01061 const qreal titleGap =
01062 drawTitle
01063 ? (QFontMetricsF( titleItem.realFont(), GlobalMeasureScaling::paintDevice() ).height() / 3.0)
01064 : 0.0;
01065
01066 switch ( position() )
01067 {
01068 case Bottom:
01069 case Top: {
01070 const bool isBarDiagram = referenceDiagramIsBarDiagram(d->diagram());
01071 int leftOverlap = 0;
01072 int rightOverlap = 0;
01073
01074 qreal w = 10.0;
01075 qreal h = 0.0;
01076 if( drawLabels ){
01077
01078 if ( labels().count() ){
01079
01080 const int first=0;
01081 const int last=labels().count()-1;
01082 const QStringList labelsList( labels() );
01083 for ( int i = first; i <= last; ++i )
01084 {
01085 labelItem.setText( customizedLabel(labelsList[ i ]) );
01086 const QSize siz = labelItem.sizeHint();
01087 h = qMax( h, static_cast<qreal>(siz.height()) );
01088 calculateOverlap( i, first, last, siz.width(), isBarDiagram,
01089 leftOverlap, rightOverlap );
01090
01091 }
01092 }else{
01093 QStringList headerLabels = d->diagram()->itemRowLabels();
01094 const int headerLabelsCount = headerLabels.count();
01095 if( headerLabelsCount ){
01096 const bool useFastCalcAlgorithm
01097 = (strcmp( metaObject()->className(), "KDChart::CartesianAxis" ) == 0);
01098 const int first=0;
01099 const int last=headerLabelsCount-1;
01100 for ( int i = first;
01101 i <= last;
01102 i = (useFastCalcAlgorithm && i < last) ? last : (i+1) )
01103 {
01104 labelItem.setText( customizedLabel(headerLabels[ i ]) );
01105 const QSize siz = labelItem.sizeHint();
01106 h = qMax( h, static_cast<qreal>(siz.height()) );
01107 calculateOverlap( i, first, last, siz.width(), isBarDiagram,
01108 leftOverlap, rightOverlap );
01109 }
01110 }else{
01111 labelItem.setText(
01112 customizedLabel(
01113 QString::number( plane->gridDimensionsList().first().end, 'f', 0 )));
01114 const QSize siz = labelItem.sizeHint();
01115 h = siz.height();
01116 calculateOverlap( 0, 0, 0, siz.width(), isBarDiagram,
01117 leftOverlap, rightOverlap );
01118 }
01119 }
01120
01121 h += labelGap;
01122 }
01123
01124 if ( drawTitle ) {
01125
01126 h += titleItem.sizeHint().height() + titleGap;
01127 w = titleItem.sizeHint().width() + 2.0;
01128 }
01129
01130 h += qAbs( tickLength() ) * 3.0;
01131 result = QSize ( static_cast<int>( w ), static_cast<int>( h ) );
01132
01133
01134
01135
01136 d->amountOfLeftOverlap = leftOverlap;
01137 d->amountOfRightOverlap = rightOverlap;
01138
01139
01140
01141
01142
01143
01144 }
01145 break;
01146 case Left:
01147 case Right: {
01148 int topOverlap = 0;
01149 int bottomOverlap = 0;
01150
01151 qreal w = 0.0;
01152 qreal h = 10.0;
01153 if( drawLabels ){
01154
01155 if ( labels().count() == 0 )
01156 {
01157 labelItem.setText(
01158 customizedLabel(
01159 QString::number( plane->gridDimensionsList().last().end, 'f', 0 )));
01160 const QSize siz = labelItem.sizeHint();
01161 w = siz.width();
01162 calculateOverlap( 0, 0, 0, siz.height(), false,
01163 topOverlap, bottomOverlap );
01164 }else{
01165
01166 const int first=0;
01167 const int last=labels().count()-1;
01168 const QStringList labelsList( labels() );
01169 for ( int i = first; i <= last; ++i )
01170 {
01171 labelItem.setText( customizedLabel(labelsList[ i ]) );
01172 const QSize siz = labelItem.sizeHint();
01173 qreal lw = siz.width();
01174 w = qMax( w, lw );
01175 calculateOverlap( 0, 0, 0, siz.height(), false,
01176 topOverlap, bottomOverlap );
01177 }
01178 }
01179
01180 w += labelGap;
01181 }
01182
01183 if ( drawTitle ) {
01184
01185 w += titleItem.sizeHint().width() + titleGap;
01186 h = titleItem.sizeHint().height() + 2.0;
01187
01188 }
01189
01190 w += qAbs( tickLength() ) * 3.0;
01191
01192 result = QSize ( static_cast<int>( w ), static_cast<int>( h ) );
01193
01194
01195
01196
01197
01198 d->amountOfTopOverlap = topOverlap;
01199 d->amountOfBottomOverlap = bottomOverlap;
01200
01201
01202
01203
01204
01205
01206 }
01207 break;
01208 default:
01209 Q_ASSERT( false );
01210 break;
01211 };
01212
01213
01214 return result;
01215 }
01216
01217 QSize CartesianAxis::minimumSize() const
01218 {
01219 return maximumSize();
01220 }
01221
01222 QSize CartesianAxis::sizeHint() const
01223 {
01224 return maximumSize();
01225 }
01226
01227 void CartesianAxis::setGeometry( const QRect& r )
01228 {
01229
01230
01231 d->geometry = r;
01232 }
01233
01234 QRect CartesianAxis::geometry() const
01235 {
01236 return d->geometry;
01237 }
01238
01239 int CartesianAxis::tickLength( bool subUnitTicks ) const
01240 {
01241 int result = 0;
01242
01243 if ( isAbscissa() ) {
01244 result = position() == Top ? -4 : 3;
01245 } else {
01246 result = position() == Left ? -4 : 3;
01247 }
01248
01249 if ( subUnitTicks )
01250 result = result < 0 ? result + 1 : result - 1;
01251
01252 return result;
01253 }
01254
01255
01256
01257
01258
01259
01260
01261
01262
01263
01264
01265
01266
01267
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
01278
01279
01280
01281
01282
01283
01284
01285
01286
01287
01288
01289
01290
01291
01292
01293
01294
01295
01296
01297