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 "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
00065
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
00088 return false;
00089 }
00090
00091
00092
00093
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
00161 if( ! d->diagram() || ! d->diagram()->coordinatePlane() ) {
00162
00163 return;
00164 }
00165 AbstractCoordinatePlane* plane = d->diagram()->coordinatePlane();
00166 if( plane ){
00167 plane->layoutPlanes();
00168
00169 }
00170 }
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
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
00241
00242 ctx.setRectangle(
00243 QRectF (
00244
00245 QPointF(rect.left(), rect.top()),
00246 QSizeF(rect.width(), rect.height() ) ) );
00247
00248 QRegion clipRegion( rect.adjusted( -1, -1, 1, 1 ) );
00249 painter->save();
00250 painter->setClipRegion( clipRegion );
00251 paintCtx( &ctx );
00252 painter->restore();
00253
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
00271
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
00276 if( drawnTicks.count() > nextMayBeTick )
00277 mayBeTick = drawnTicks[ nextMayBeTick ];
00278
00279 if ( isMinorTickMark || !rulerAttr.showMajorTickMarks() ) {
00280 if ( isAbscissa ) {
00281
00282 QPointF topPoint = diagramIsVertical ? QPointF( f, 0 ) : QPointF( 0, f );
00283 QPointF bottomPoint( topPoint );
00284
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
00307
00308 QPointF leftPoint = plane->translate( diagramIsVertical ? QPointF( 0, f ) : QPointF( f, 0 ) );
00309
00310
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
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
00398
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
00425
00426 titleItem.setGeometry( QRect( QPoint(-size.width() / 2, -size.height() / 2), size ) );
00427
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
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
00462
00463 if( ! d->diagram()->model() )
00464 return;
00465
00466
00467
00468
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
00478
00479
00480
00481
00482
00483 DataDimensionsList dimensions( plane->gridDimensionsList() );
00484
00485
00486
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
00492 if ( diagramIsVertical ) {
00493
00494
00495
00496
00497
00498
00499 dimX = AbstractGrid::adjustedLowerUpperRange( dimensions.first(), true, true );
00500 dimY = AbstractGrid::adjustedLowerUpperRange( dimensions.last(), true, true );
00501
00502
00503
00504
00505
00506
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
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529 const qreal MinimumPixelsBetweenRulers = qMin( dimX.stepWidth, dimY.stepWidth );
00530
00531
00532
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
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
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
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
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
00593
00594
00595
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
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
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
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
00652 ptr->setPen ( PrintingParameters::scalePen( labelTA.pen() ) );
00653
00654
00655
00656 const QObject* referenceArea = plane->parent();
00657
00658
00659 QVector< int > drawnAbscissaTicks;
00660
00661 QVector< int > drawnYTicks;
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671 const bool centerAbscissaTicks = referenceDiagramNeedsCenteredAbscissaTicks( d->diagram() );
00672
00673
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
00686 headerLabels =
00687 isOrdinate()
00688 ? d->diagram()->datasetLabels()
00689 : d->diagram()->itemRowLabels();
00690
00691
00692 useConfiguredStepsLabels = isAbscissa() &&
00693 dimX.stepWidth &&
00694 (( (headerLabels.count() - 1)/ dimX.stepWidth ) != numberOfUnitRulers);
00695 if( useConfiguredStepsLabels ) {
00696 numberOfUnitRulers = ( headerLabels.count() - 1 )/ dimX.stepWidth;
00697
00698
00699 QStringList configuredStepsLabels;
00700 double value = dimX.start;
00701 configuredStepsLabels << QString::number( value );
00702
00703 for( int i = 0; i < numberOfUnitRulers; i++ )
00704 {
00705
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
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
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
00804
00805
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
00815
00816 if( useConfiguredStepsLabels ){
00817 labelItem->setText( customizedLabel(headerLabels[ iLabel ]) );
00818 labelItem2->setText(customizedLabel(headerLabels[ iLabel+1 ]) );
00819 }else{
00820
00821 labelItem->setText( customizedLabel(headerLabelsCount > i && i >= 0 ?
00822 headerLabels[static_cast<int>(i)] :
00823 QString::number( i, 'f', precision )) );
00824
00825
00826
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
00831
00832 }
00833 } else {
00834
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
00855 if ( ++iLabel > hardLabelsCount - 1 )
00856 iLabel = 0;
00857 if ( isLogarithmicX )
00858 i *= 10.0;
00859 else
00860 i += dimX.stepWidth;
00861
00862
00863 }
00864
00865
00866 useShortLabels = labelsAreOverlapping;
00867 }
00868
00869
00870 if ( drawLabels && d->annotations.isEmpty() )
00871 {
00872 qreal i = minValueX;
00873 int iLabel = 0;
00874
00875 while ( i + labelDiff < maxValueX )
00876 {
00877
00878
00879 if ( !drawLabels || hardLabelsCount < 1 || ( dimX.stepWidth != 1.0 && ! dim.isCalculated ) )
00880 {
00881
00882
00883 if( useConfiguredStepsLabels ){
00884 labelItem->setText( customizedLabel(headerLabels[ iLabel ]) );
00885 labelItem2->setText(customizedLabel(headerLabels[ iLabel+1 ]) );
00886 }else{
00887
00888 labelItem->setText( customizedLabel(headerLabelsCount > i && i >= 0 ?
00889 headerLabels[static_cast<int>(i)] :
00890 QString::number( i, 'f', precision )) );
00891
00892
00893
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
00898
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
00923 labelDiff *= 10.0;
00924
00925
00926
00927 iLabel = 0;
00928
00929
00930 }
00931 else
00932 {
00933 i += labelDiff;
00934
00935
00936 }
00937
00938 if ( (++iLabel > hardLabelsCount - 1) && !useConfiguredStepsLabels )
00939 {
00940 iLabel = 0;
00941 }
00942 }
00943
00944 if( minValueX + labelDiff > maxValueX )
00945 labelDiff = maxValueX - minValueX;
00946
00947
00948
00949 }
00950
00951 int idxLabel = 0;
00952 qreal iLabelF = minValueX;
00953
00954 qreal i = minValueX;
00955 qreal labelStep = 0.0;
00956
00957
00958 while( i <= maxValueX && d->annotations.isEmpty() )
00959 {
00960
00961
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
00982 bool painttick = bIsVisibleLabel && labelStep <= 0;;
00983
00984
00985
00986
00987
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
01007
01008
01009
01010
01011
01012
01013
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
01033 }
01034
01035
01036 if( labelStep <= 0 ) {
01037 const PainterSaver p( ptr );
01038
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
01062
01063
01064
01065
01066
01067
01068
01069
01070
01071
01072
01073
01074
01075
01076
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
01117
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
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
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
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
01182
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
01199
01200 if( drawLabels )
01201 {
01202
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
01207
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
01238 labelValue = minValueY;
01239
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
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
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
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
01368 calculateNextLabel( labelValue, step, isLogarithmicY, dimensions.last().start );
01369 }
01370 }
01371 }
01372 }
01373 delete labelItem;
01374 delete labelItem2;
01375 }
01376
01377
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
01391 }
01392
01393
01394
01395 bool CartesianAxis::isEmpty() const
01396 {
01397 return false;
01398 }
01399
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 );
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
01429
01430 }else{
01431 firstOverlap = measure / 2;
01432 }
01433 }
01434
01435 if( i == last ){
01436 if( centerAbscissaTicks ){
01437
01438
01439 }else{
01440 lastOverlap = measure / 2;
01441 }
01442 }
01443 }
01444
01445
01446 void CartesianAxis::setCachedSizeDirty() const
01447 {
01448 d->cachedMaximumSize = QSize();
01449 }
01450
01451
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
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
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
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
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
01592 h += labelGap;
01593 }
01594
01595 if ( drawTitle ) {
01596
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
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
01613
01614
01615
01616 amountOfLeftOverlap = leftOverlap;
01617 amountOfRightOverlap = rightOverlap;
01618
01619
01620
01621
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
01632
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,
01669 topOverlap, bottomOverlap );
01670 calculateNextLabel( labelValue, step, isLogarithmicY, plane->gridDimensionsList().last().start );
01671 }
01672 }else{
01673
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,
01686 topOverlap, bottomOverlap );
01687 }
01688 }
01689
01690 w += labelGap;
01691 }
01692
01693 if ( drawTitle ) {
01694
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
01703 }
01704
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
01712
01713
01714
01715
01716 amountOfTopOverlap = topOverlap;
01717 amountOfBottomOverlap = bottomOverlap;
01718
01719
01720
01721
01722
01723
01724 }
01725
01726
01727 return result;
01728 }
01729
01730 QSize CartesianAxis::minimumSize() const
01731 {
01732 return maximumSize();
01733 }
01734
01735 QSize CartesianAxis::sizeHint() const
01736 {
01737 return maximumSize();
01738 }
01739
01740 void CartesianAxis::setGeometry( const QRect& r )
01741 {
01742
01743
01744 d->geometry = r;
01745 setCachedSizeDirty();
01746 }
01747
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
01785
01786
01787
01788
01789
01790
01791
01792
01793
01794
01795
01796
01797
01798
01799
01800
01801
01802
01803
01804
01805
01806
01807
01808
01809
01810
01811
01812
01813
01814
01815
01816
01817
01818
01819
01820
01821
01822