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