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