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 labelItem->setTextAttributes( textAttributes() );
00726 TextLayoutItem* labelItem2 =
00727 drawLabels
00728 ? new TextLayoutItem( QString::number( minValueY ),
00729 labelTA,
00730 referenceArea,
00731 KDChartEnums::MeasureOrientationMinimum,
00732 Qt::AlignLeft )
00733 : 0;
00734 labelItem2->setTextAttributes( textAttributes() );
00735 const QFontMetricsF met(
00736 drawLabels
00737 ? labelItem->realFont()
00738 : QFontMetricsF( QApplication::font(), GlobalMeasureScaling::paintDevice() ) );
00739 const qreal halfFontHeight = rulerAttr.labelMargin() >= 0 ? rulerAttr.labelMargin() : met.height() * 0.5;
00740 const qreal halfFontWidth = rulerAttr.labelMargin() >= 0 ? rulerAttr.labelMargin() : met.averageCharWidth() * 0.5;
00741
00742 if ( isAbscissa() ) {
00743
00744 if( !d->customTicksPositions.isEmpty() )
00745 {
00746 const QList< double > values = d->customTicksPositions;
00747 KDAB_FOREACH( const double v, values )
00748 {
00749 QPointF topPoint = diagramIsVertical ? QPointF( v, 0.0 ) : QPointF( 0.0, v );
00750 QPointF bottomPoint = topPoint;
00751 topPoint = plane->translate( topPoint );
00752 bottomPoint = plane->translate( bottomPoint );
00753 if ( diagramIsVertical ) {
00754 topPoint.setY( rulerRef.y() + tickLength() );
00755 bottomPoint.setY( rulerRef.y() );
00756 } else {
00757 topPoint.setX( rulerRef.x() + tickLength() );
00758 bottomPoint.setX( rulerRef.x() );
00759 }
00760
00761 context->painter()->drawLine(topPoint, bottomPoint);
00762 }
00763 }
00764
00765 if( !d->annotations.isEmpty() )
00766 {
00767 const QList< double > values = d->annotations.keys();
00768 KDAB_FOREACH( const double v, values )
00769 {
00770 QPointF topPoint = diagramIsVertical ? QPointF( v, 0.0 ) : QPointF( 0.0, v );
00771 QPointF bottomPoint = topPoint;
00772 topPoint = plane->translate( topPoint );
00773 bottomPoint = plane->translate( bottomPoint );
00774 if ( diagramIsVertical ) {
00775 topPoint.setY( rulerRef.y() + tickLength() );
00776 bottomPoint.setY( rulerRef.y() );
00777 } else {
00778 topPoint.setX( rulerRef.x() + tickLength() );
00779 bottomPoint.setX( rulerRef.x() );
00780 }
00781
00782 labelItem->setText( d->annotations[ v ] );
00783 const QSize size( labelItem->sizeHint() );
00784 if ( diagramIsVertical ) {
00785 labelItem->setGeometry(
00786 QRect(
00787 QPoint(
00788 static_cast<int>( topPoint.x() - size.width() / 2.0 ),
00789 static_cast<int>( topPoint.y() +
00790 ( position() == Bottom
00791 ? halfFontHeight
00792 : ((halfFontHeight + size.height()) * -1.0) ) ) ),
00793 size ) );
00794 } else {
00795 labelItem->setGeometry(
00796 QRect(
00797 QPoint(
00798 static_cast<int>( bottomPoint.x() +
00799 ( position() == Right
00800 ? halfFontWidth
00801 : (-halfFontWidth - size.width()) ) ),
00802
00803 static_cast<int>( topPoint.y() - ( size.height() ) * 0.5 ) ),
00804 size ) );
00805 }
00806
00807 QRect labelGeo = labelItem->geometry();
00808
00809 if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() )
00810 ptr->setClipping( false );
00811 else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() )
00812 ptr->setClipping( false );
00813
00814 labelItem->paint( ptr );
00815 }
00816 }
00817
00818 qreal labelDiff = dimX.stepWidth;
00819 const int precision = ( QString::number( labelDiff ).section( QLatin1Char('.'), 1, 2 ) ).length();
00820
00821
00822
00823
00824 if( drawLabels && hardLabelsCount > 0 && shortLabelsCount > 0 && d->annotations.isEmpty() ){
00825 bool labelsAreOverlapping = false;
00826 int iLabel = 0;
00827 qreal i = minValueX;
00828 while ( i < maxValueX-1 && !labelsAreOverlapping )
00829 {
00830 const int idx = (iLabel < hardLabelsCount ) ? iLabel : 0;
00831 const int idx2= (iLabel < hardLabelsCount - 1) ? iLabel + 1 : 0;
00832 if ( dimX.stepWidth != 1.0 && ! dim.isCalculated )
00833 {
00834
00835
00836 if( useConfiguredStepsLabels ){
00837 labelItem->setText( customizedLabel(headerLabels[ idx ]) );
00838 labelItem2->setText( customizedLabel(headerLabels[ idx2 ]) );
00839 }else{
00840
00841 labelItem->setText( customizedLabel(headerLabelsCount > i && i >= 0 ?
00842 headerLabels[static_cast<int>(i)] :
00843 QString::number( i, 'f', precision )) );
00844
00845
00846
00847 labelItem2->setText( customizedLabel(headerLabelsCount > i + labelDiff && i + labelDiff >= 0 ?
00848 headerLabels[static_cast<int>(i+labelDiff)] :
00849 QString::number( i + labelDiff, 'f', precision )) );
00850
00851 }
00852 } else {
00853
00854 const int shortIdx = (iLabel < shortLabelsCount ) ? iLabel : 0;
00855 const int shortIdx2 = (iLabel < shortLabelsCount - 1) ? iLabel + 1 : 0;
00856 labelItem->setText( customizedLabel(
00857 useShortLabels ? shortLabelsList[ shortIdx ] : labelsList[ idx ] ) );
00858 labelItem2->setText( customizedLabel(
00859 useShortLabels ? shortLabelsList[ shortIdx2 ] : labelsList[ idx2 ] ) );
00860 }
00861
00862 QPointF firstPos = diagramIsVertical ? QPointF( i, 0.0 ) : QPointF( 0.0, i );
00863 firstPos = plane->translate( firstPos );
00864
00865 QPointF secondPos = diagramIsVertical ? QPointF( i + labelDiff, 0.0 ) : QPointF( 0.0, i + labelDiff );
00866 secondPos = plane->translate( secondPos );
00867
00868 labelsAreOverlapping = labelItem->intersects( *labelItem2, firstPos, secondPos );
00869
00870 if ( ++iLabel > hardLabelsCount - 1 )
00871 iLabel = 0;
00872 if ( isLogarithmicX )
00873 i *= 10.0;
00874 else
00875 i += dimX.stepWidth;
00876
00877 }
00878
00879 useShortLabels = labelsAreOverlapping;
00880 }
00881
00882
00883 if ( drawLabels && d->annotations.isEmpty() )
00884 {
00885 qreal i = minValueX;
00886 int iLabel = 0;
00887
00888 while ( i + labelDiff < maxValueX )
00889 {
00890 const int idx = (iLabel < hardLabelsCount ) ? iLabel : 0;
00891 const int idx2= (iLabel < hardLabelsCount - 1) ? iLabel + 1 : 0;
00892
00893
00894 if ( !drawLabels || hardLabelsCount < 1 || ( dimX.stepWidth != 1.0 && ! dim.isCalculated ) )
00895 {
00896
00897
00898 if( useConfiguredStepsLabels ){
00899 labelItem->setText( customizedLabel(headerLabels[ idx ]) );
00900 labelItem2->setText( customizedLabel(headerLabels[ idx2 ]) );
00901 }else{
00902
00903 labelItem->setText( customizedLabel(headerLabelsCount > i && i >= 0 ?
00904 headerLabels[static_cast<int>(i)] :
00905 QString::number( i, 'f', precision )) );
00906
00907
00908
00909 labelItem2->setText( customizedLabel(headerLabelsCount > i + labelDiff && i + labelDiff >= 0 ?
00910 headerLabels[static_cast<int>(i+labelDiff)] :
00911 QString::number( i + labelDiff, 'f', precision )) );
00912
00913 }
00914 } else {
00915 const int shortIdx = (iLabel < shortLabelsCount ) ? iLabel : 0;
00916 const int shortIdx2 = (iLabel < shortLabelsCount - 1) ? iLabel + 1 : 0;
00917 labelItem->setText( customizedLabel(
00918 useShortLabels ? shortLabelsList[ shortIdx ] : labelsList[ idx ] ) );
00919 labelItem2->setText( customizedLabel(
00920 useShortLabels ? shortLabelsList[ shortIdx2 ] : labelsList[ idx2 ] ) );
00921 }
00922
00923 QPointF firstPos = diagramIsVertical ? QPointF( i, 0.0 ) : QPointF( 0.0, i );
00924 firstPos = plane->translate( firstPos );
00925
00926 QPointF secondPos = diagramIsVertical ? QPointF( i + labelDiff, 0.0 ) : QPointF( 0.0, i + labelDiff );
00927 secondPos = plane->translate( secondPos );
00928
00929 if ( labelItem->intersects( *labelItem2, firstPos, secondPos ) )
00930 {
00931 i = minValueX;
00932
00933
00934 labelDiff *= 10.0;
00935
00936
00937 iLabel = 0;
00938
00939 }
00940 else
00941 {
00942 i += labelDiff;
00943
00944 }
00945
00946 if ( (++iLabel > hardLabelsCount - 1) && !useConfiguredStepsLabels )
00947 {
00948 iLabel = 0;
00949 }
00950 }
00951
00952 if( minValueX + labelDiff > maxValueX )
00953 labelDiff = maxValueX - minValueX;
00954
00955
00956
00957 }
00958
00959 int idxLabel = 0;
00960 qreal iLabelF = minValueX;
00961
00962 qreal i = minValueX;
00963 qreal labelStep = 0.0;
00964
00965
00966 while( i <= maxValueX && d->annotations.isEmpty() )
00967 {
00968
00969
00970 QPointF topPoint = diagramIsVertical ? QPointF( i + ( centerAbscissaTicks ? 0.5 : 0.0 ), 0.0 ) : QPointF( 0.0, i + ( centerAbscissaTicks ? 0.5 : 0.0 ) );
00971 QPointF bottomPoint ( topPoint );
00972 topPoint = plane->translate( topPoint );
00973 bottomPoint = plane->translate( bottomPoint );
00974 if ( diagramIsVertical ) {
00975 topPoint.setY( rulerRef.y() + tickLength() );
00976 bottomPoint.setY( rulerRef.y() );
00977 } else {
00978 bottomPoint.setX( rulerRef.x() - (position() == Left ? tickLength() : -tickLength()) );
00979 topPoint.setX( rulerRef.x() );
00980 }
00981
00982 const qreal translatedValue = diagramIsVertical ? topPoint.x() : topPoint.y();
00983 bool bIsVisibleLabel;
00984 if ( diagramIsVertical )
00985 bIsVisibleLabel = ( (translatedValue >= geoRect.left() && translatedValue <= geoRect.right() && !isLogarithmicX) || i != 0.0 );
00986 else
00987 bIsVisibleLabel = ( (translatedValue >= geoRect.top() && translatedValue <= geoRect.bottom() && !isLogarithmicX) || i != 0.0 );
00988
00989
00990 bool painttick = bIsVisibleLabel && labelStep <= 0;
00991
00992
00993
00994
00995 if ( centerAbscissaTicks && i == maxValueX )
00996 painttick = false;
00997
00998 if ( bIsVisibleLabel && painttick ) {
00999 ptr->save();
01000 if ( rulerAttr.hasTickMarkPenAt( i ) )
01001 ptr->setPen( rulerAttr.tickMarkPen( i ) );
01002 else
01003 ptr->setPen( rulerAttr.majorTickMarkPen() );
01004 ptr->drawLine( topPoint, bottomPoint );
01005 ptr->restore();
01006 }
01007
01008 drawnAbscissaTicks.append( static_cast<int>( diagramIsVertical ? topPoint.x() : topPoint.y() ) );
01009 if( drawLabels ) {
01010 if( bIsVisibleLabel ){
01011 if ( isLogarithmicX )
01012 labelItem->setText( customizedLabel(QString::number( i ) ) );
01013
01014
01015
01016
01017
01018
01019
01020
01021
01022
01023 else {
01024 int idx = idxLabel + static_cast<int>(minValueX);
01025 if( hardLabelsCount ){
01026 if( useShortLabels ){
01027 if( idx >= shortLabelsList.count() )
01028 idx = 0;
01029 }else{
01030 if( idx >= labelsList.count() )
01031 idx = 0;
01032 }
01033 }
01034 labelItem->setText(
01035 customizedLabel(
01036 hardLabelsCount
01037 ? ( useShortLabels ? shortLabelsList[ idx ] : labelsList[ idx ] )
01038 : ( headerLabelsCount ? headerLabels[ idx ] : QString::number( iLabelF ))));
01039
01040 }
01041
01042
01043 if( labelStep <= 0 ) {
01044 const PainterSaver p( ptr );
01045
01046 QPoint topLeft, topRight, bottomRight, bottomLeft;
01047 const QSize size(
01048 labelItem->sizeHintAndRotatedCorners(
01049 topLeft, topRight, bottomRight, bottomLeft) );
01050 const QSize sizeUnrotated( labelItem->sizeHintUnrotated() );
01051 const int rotation = labelTA.rotation();
01052 const bool rotPositive = (rotation > 0 && rotation < 180);
01053 QPoint midOfSide(0,0);
01054 int dX = 0;
01055 int dY = 0;
01056 if( rotation ){
01057 if( rotPositive ){
01058 midOfSide = (topLeft + bottomLeft) / 2;
01059 dX = topLeft.x() - midOfSide.x();
01060 dY = bottomLeft.y() - midOfSide.y();
01061 }else{
01062 midOfSide = (topRight + bottomRight) / 2;
01063 dX = midOfSide.x() - topLeft.x();
01064 dY = midOfSide.y() - topRight.y();
01065 }
01066 }
01067
01068
01069
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080
01081
01082
01083
01084
01085
01086 QPoint topLeftPt;
01087 if( diagramIsVertical ){
01088 if( rotation ){
01089 topLeftPt = QPoint(
01090 static_cast<int>( topPoint.x() ) - dX,
01091 static_cast<int>( topPoint.y() - dY +
01092 ( position() == Bottom
01093 ? halfFontHeight
01094 : ((halfFontHeight + size.height()) * -1.0) ) ) );
01095 }else{
01096 topLeftPt = QPoint(
01097 static_cast<int>( topPoint.x() - size.width() / 2.0 ),
01098 static_cast<int>( topPoint.y() +
01099 ( position() == Bottom
01100 ? halfFontHeight
01101 : ((halfFontHeight + size.height()) * -1.0) ) ) );
01102 }
01103 }else{
01104 if( rotation ){
01105 topLeftPt = QPoint(
01106 static_cast<int>( topPoint.x() ) + dX,
01107 static_cast<int>( topPoint.y() - dY +
01108 ( position() == Bottom
01109 ? halfFontHeight
01110 : ((halfFontHeight + size.height()) * -1.0) ) ) );
01111 }else{
01112 topLeftPt = QPoint(
01113 static_cast<int>( bottomPoint.x() +
01114 ( position() == Right
01115 ? halfFontWidth
01116 : (-halfFontWidth - size.width()) ) ),
01117 static_cast<int>( topPoint.y() - ( size.height() ) * 0.5 ) );
01118 }
01119 }
01120 labelItem->setGeometry( QRect(topLeftPt, size) );
01121
01122 QRect labelGeo = labelItem->geometry();
01123
01124
01125 if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() )
01126 ptr->setClipping( false );
01127 else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() )
01128 ptr->setClipping( false );
01129
01130 if( !isLogarithmicX )
01131 labelStep = labelDiff - dimX.stepWidth;
01132
01133
01134 if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() )
01135 ptr->setClipping( false );
01136 else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() )
01137 ptr->setClipping( false );
01138
01139 labelItem->paint( ptr );
01140
01141
01142 labelItem2->setText( labelItem->text() );
01143
01144 } else {
01145 labelStep -= dimX.stepWidth;
01146 }
01147 }
01148
01149 if( hardLabelsCount ) {
01150 if( useShortLabels && idxLabel >= shortLabelsCount - 1 )
01151 idxLabel = 0;
01152 else if( !useShortLabels && idxLabel >= hardLabelsCount - 1 )
01153 idxLabel = 0;
01154 else{
01155 idxLabel += static_cast<int>(dimX.stepWidth);
01156
01157 }
01158 } else if( headerLabelsCount ) {
01159 if( ++idxLabel > headerLabelsCount - 1 ) {
01160 idxLabel = 0;
01161 }
01162 } else {
01163 iLabelF += dimX.stepWidth;
01164 }
01165 }
01166 if ( isLogarithmicX )
01167 {
01168 i *= 10.0;
01169 if( i == 0.0 )
01170 {
01171 const qreal j = dimensions.first().start;
01172 i = j == 0.0 ? 1.0 : pow( 10.0, floor( log10( j ) ) );
01173 }
01174 }
01175 else
01176 {
01177 i += dimX.stepWidth;
01178 }
01179 }
01180 } else {
01181 const PainterSaver p( ptr );
01182 const double maxLimit = maxValueY;
01183 const double steg = dimY.stepWidth;
01184 int maxLabelsWidth = 0;
01185 qreal labelValue;
01186
01187 if( drawLabels && position() == Right ){
01188
01189
01190 labelValue = minValueY;
01191 while ( labelValue <= maxLimit ) {
01192 const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) +
01193 QString::number( labelValue ) +
01194 diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true );
01195 labelItem->setText( customizedLabel( labelText ) );
01196 maxLabelsWidth = qMax( maxLabelsWidth, diagramIsVertical ? labelItem->sizeHint().width() : labelItem->sizeHint().height() );
01197 calculateNextLabel( labelValue, steg, isLogarithmicY, dimensions.last().start );
01198
01199 if(maxValueY == 0 && minValueY == 0)
01200 break;
01201 }
01202 }
01203
01204 labelValue = minValueY;
01205 qreal step = steg;
01206 bool nextLabel = false;
01207
01208
01209
01210 if( !d->customTicksPositions.isEmpty() )
01211 {
01212 const QList< double > values = d->customTicksPositions;
01213 KDAB_FOREACH( const double value, values )
01214 {
01215 QPointF annoPoint = (diagramIsVertical ? QPointF( 0.0, value ) : QPointF( value, 0.0 ));
01216 QPointF leftPoint = plane->translate( annoPoint );
01217 QPointF rightPoint = plane->translate( annoPoint );
01218
01219 if ( diagramIsVertical ) {
01220 leftPoint.setX( rulerRef.x() + tickLength() );
01221 rightPoint.setX( rulerRef.x() );
01222 } else {
01223 leftPoint.setY( rulerRef.y() + ((position() == Bottom) ? tickLength() : -tickLength()) );
01224 rightPoint.setY( rulerRef.y() );
01225 }
01226
01227 context->painter()->drawLine(leftPoint, rightPoint);
01228 }
01229 }
01230
01231 if( drawLabels )
01232 {
01233
01234 while( labelValue <= maxLimit ) {
01235 QPointF leftPoint = plane->translate( diagramIsVertical ? QPointF( 0, labelValue ) : QPointF( labelValue, 0 ) );
01236 const qreal translatedValue = diagramIsVertical ? leftPoint.y() : leftPoint.x();
01237
01238 const bool bTranslatedValueIsWithinRange = diagramIsVertical ? translatedValue > geoRect.top() && translatedValue <= geoRect.bottom()
01239 : translatedValue > geoRect.left() && translatedValue <= geoRect.right();
01240 if( bTranslatedValueIsWithinRange ){
01241 const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) +
01242 QString::number( labelValue ) +
01243 diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true );
01244 const QString label2Text = diagram()->unitPrefix( static_cast< int >( labelValue + step ), diagramOrientation, true ) +
01245 QString::number( labelValue + step ) +
01246 diagram()->unitSuffix( static_cast< int >( labelValue + step ), diagramOrientation, true );
01247 labelItem->setText( customizedLabel( labelText ) );
01248 labelItem2->setText( customizedLabel( QString::number( labelValue + step ) ) );
01249 QPointF nextPoint = plane->translate( diagramIsVertical ? QPointF( 0, labelValue + step ) : QPointF( labelValue + step, 0 ) );
01250 if ( labelItem->intersects( *labelItem2, leftPoint, nextPoint ) )
01251 {
01252 step += steg;
01253 nextLabel = false;
01254 }else{
01255 nextLabel = true;
01256 }
01257 }else{
01258 nextLabel = true;
01259 }
01260
01261 if ( nextLabel || isLogarithmicY )
01262 calculateNextLabel( labelValue, step, isLogarithmicY, dimensions.last().start );
01263 else
01264 labelValue = minValueY;
01265 }
01266
01267
01268 labelValue = minValueY;
01269
01270 if( !d->annotations.isEmpty() )
01271 {
01272 const QList< double > annotations = d->annotations.keys();
01273 KDAB_FOREACH( const double annotation, annotations )
01274 {
01275 QPointF annoPoint = (diagramIsVertical ? QPointF( 0.0, annotation ) : QPointF( annotation, 0.0 ));
01276 QPointF leftPoint = plane->translate( annoPoint );
01277 QPointF rightPoint = plane->translate( annoPoint );
01278
01279 if ( diagramIsVertical ) {
01280 leftPoint.setX( rulerRef.x() + tickLength() );
01281 rightPoint.setX( rulerRef.x() );
01282 } else {
01283 leftPoint.setY( rulerRef.y() + ((position() == Bottom) ? tickLength() : -tickLength()) );
01284 rightPoint.setY( rulerRef.y() );
01285 }
01286
01287 const qreal translatedValue = diagramIsVertical ? rightPoint.y() : rightPoint.x();
01288 const bool bIsVisibleLabel = diagramIsVertical ?
01289 ( (translatedValue >= geoRect.top() && translatedValue <= geoRect.bottom() && !isLogarithmicY) || labelValue != 0.0 )
01290 : ( (translatedValue >= geoRect.left() && translatedValue <= geoRect.right() && !isLogarithmicY) || labelValue != 0.0 );
01291
01292 if( bIsVisibleLabel )
01293 {
01294 ptr->save();
01295 if ( rulerAttr.hasTickMarkPenAt( annotation ) )
01296 ptr->setPen( rulerAttr.tickMarkPen( annotation ) );
01297 else
01298 ptr->setPen( rulerAttr.majorTickMarkPen() );
01299 ptr->drawLine( leftPoint, rightPoint );
01300 ptr->restore();
01301
01302 labelItem->setText( d->annotations[ annotation ] );
01303 const QSize labelSize( labelItem->sizeHint() );
01304 int x, y;
01305 if ( diagramIsVertical ) {
01306 x = static_cast<int>( leftPoint.x() + met.height() * ( position() == Left ? -0.5 : 0.5)
01307 - ( position() == Left ? labelSize.width() : 0.0 ) );
01308 y = static_cast<int>( leftPoint.y() - ( met.ascent() + met.descent() ) * 0.6 );
01309 } else {
01310 const qreal halfFontHeight = met.height() * 0.5;
01311 x = static_cast<int>( leftPoint.x() - labelSize.width() * 0.5 );
01312 y = static_cast<int>( (position() == Bottom ? leftPoint.y() : rightPoint.y()) +
01313 + ( position() == Bottom ? halfFontHeight : -(halfFontHeight + labelSize.height()) ) );
01314 }
01315 labelItem->setGeometry( QRect( QPoint( x, y ), labelSize ) );
01316
01317 QRect labelGeo = labelItem->geometry();
01318
01319 if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() )
01320 ptr->setClipping( false );
01321 else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() )
01322 ptr->setClipping( false );
01323
01324 labelItem->paint( ptr );
01325 }
01326 }
01327 }
01328 else
01329 {
01330 while( labelValue <= maxLimit ) {
01331
01332 const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) +
01333 QString::number( labelValue ) +
01334 diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true );
01335 labelItem->setText( customizedLabel( labelText ) );
01336 QPointF leftPoint = plane->translate( diagramIsVertical ? QPointF( 0, labelValue ) : QPointF( labelValue, 0 ) );
01337 QPointF rightPoint = leftPoint;
01338
01339 if ( diagramIsVertical ) {
01340 leftPoint.setX( rulerRef.x() + tickLength() );
01341 rightPoint.setX( rulerRef.x() );
01342 } else {
01343 leftPoint.setY( rulerRef.y() + ((position() == Bottom) ? tickLength() : -tickLength()) );
01344 rightPoint.setY( rulerRef.y() );
01345 }
01346
01347 bool bIsVisibleLabel;
01348 const qreal translatedValue = diagramIsVertical ? rightPoint.y() : rightPoint.x();
01349 if ( diagramIsVertical)
01350 bIsVisibleLabel = ( (translatedValue >= geoRect.left() && translatedValue <= geoRect.right() && !isLogarithmicX) || labelValue != 0.0 );
01351 else
01352 bIsVisibleLabel = ( (translatedValue >= geoRect.top() && translatedValue <= geoRect.bottom() && !isLogarithmicX) || labelValue != 0.0 );
01353
01354 if( bIsVisibleLabel ){
01355 ptr->save();
01356 if ( rulerAttr.hasTickMarkPenAt( labelValue ) )
01357 ptr->setPen( rulerAttr.tickMarkPen( labelValue ) );
01358 else
01359 ptr->setPen( rulerAttr.majorTickMarkPen() );
01360 ptr->drawLine( leftPoint, rightPoint );
01361 ptr->restore();
01362
01363 drawnYTicks.append( static_cast<int>( diagramIsVertical ? leftPoint.y() : leftPoint.x() ) );
01364 const QSize labelSize( labelItem->sizeHint() );
01365
01366 int x, y;
01367 if ( diagramIsVertical ) {
01368 x = static_cast<int>( leftPoint.x() + met.height() * ( position() == Left ? -0.5 : 0.5) )
01369 - ( position() == Left ? labelSize.width() : (labelSize.width() - maxLabelsWidth) );
01370 y = static_cast<int>( leftPoint.y() - ( met.ascent() + met.descent() ) * 0.6 );
01371 } else {
01372 const qreal halfFontHeight = met.height() * 0.5;
01373 x = static_cast<int>( leftPoint.x() - labelSize.width() * 0.5 );
01374 y = static_cast<int>( (position() == Bottom ? leftPoint.y() : rightPoint.y()) +
01375 + ( position() == Bottom ? halfFontHeight : -(halfFontHeight + labelSize.height()) ) );
01376 }
01377
01378 labelItem->setGeometry( QRect( QPoint( x, y ), labelSize ) );
01379 const QRect labelGeo = labelItem->geometry();
01380 const bool hadClipping = ptr->hasClipping();
01381 if( labelGeo.top() < geoRect.top() && labelGeo.bottom() > geoRect.top() )
01382 ptr->setClipping( false );
01383 else if( labelGeo.top() < geoRect.bottom() && labelGeo.bottom() > geoRect.bottom() )
01384 ptr->setClipping( false );
01385
01386
01387 if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() )
01388 ptr->setClipping( false );
01389 else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() )
01390 ptr->setClipping( false );
01391
01392 labelItem->paint( ptr );
01393 ptr->setClipping( hadClipping );
01394 }
01395
01396
01397 calculateNextLabel( labelValue, step, isLogarithmicY, dimensions.last().start );
01398 }
01399 }
01400 }
01401 }
01402 delete labelItem;
01403 delete labelItem2;
01404 }
01405
01406
01407 if ( drawSubUnitRulers && d->annotations.isEmpty() ) {
01408 ptr->save();
01409
01410 d->drawSubUnitRulers( ptr, plane, dim, rulerRef, isAbscissa() ? drawnAbscissaTicks : drawnYTicks, diagramIsVertical, rulerAttr );
01411
01412 ptr->restore();
01413 }
01414
01415 if( ! titleText().isEmpty() ) {
01416 d->drawTitleText( ptr, plane, areaGeoRect );
01417 }
01418
01419
01420 }
01421
01422
01423
01424 bool CartesianAxis::isEmpty() const
01425 {
01426 return false;
01427 }
01428
01429 Qt::Orientations CartesianAxis::expandingDirections() const
01430 {
01431 Qt::Orientations ret;
01432 switch ( position() )
01433 {
01434 case Bottom:
01435 case Top:
01436 ret = Qt::Horizontal;
01437 break;
01438 case Left:
01439 case Right:
01440 ret = Qt::Vertical;
01441 break;
01442 default:
01443 Q_ASSERT( false );
01444 break;
01445 };
01446 return ret;
01447 }
01448
01449
01450 static void calculateOverlap( int i, int first, int last,
01451 int measure,
01452 bool centerAbscissaTicks,
01453 int& firstOverlap, int& lastOverlap )
01454 {
01455 if( i == first ){
01456 if( centerAbscissaTicks ){
01457
01458
01459 }else{
01460 firstOverlap = measure / 2;
01461 }
01462 }
01463
01464 if( i == last ){
01465 if( centerAbscissaTicks ){
01466
01467
01468 }else{
01469 lastOverlap = measure / 2;
01470 }
01471 }
01472 }
01473
01474
01475 void CartesianAxis::setCachedSizeDirty() const
01476 {
01477 d->cachedMaximumSize = QSize();
01478 }
01479
01480
01481 QSize CartesianAxis::maximumSize() const
01482 {
01483 if( ! d->cachedMaximumSize.isValid() )
01484 d->cachedMaximumSize = d->calculateMaximumSize();
01485 return d->cachedMaximumSize;
01486 }
01487
01488 QSize CartesianAxis::Private::calculateMaximumSize() const
01489 {
01490 QSize result;
01491 if ( !diagram() )
01492 return result;
01493
01494 const AbstractCartesianDiagram * dia = qobject_cast< const AbstractCartesianDiagram * >( diagram() );
01495 if( dia && dia->referenceDiagram() )
01496 dia = dia->referenceDiagram();
01497 const BarDiagram *barDiagram = qobject_cast< const BarDiagram* >( dia );
01498 const Qt::Orientation diagramOrientation = barDiagram != 0 ? barDiagram->orientation() : Qt::Vertical;
01499 const bool diagramIsVertical = diagramOrientation == Qt::Vertical;
01500
01501 const TextAttributes labelTA = mAxis->textAttributes();
01502 const bool drawLabels = labelTA.isVisible();
01503
01504 const TextAttributes titleTA( titleTextAttributesWithAdjustedRotation() );
01505 const bool drawTitle = titleTA.isVisible() && ! axis()->titleText().isEmpty();
01506
01507 AbstractCoordinatePlane* plane = diagram()->coordinatePlane();
01508
01509 QObject* refArea = plane->parent();
01510 TextLayoutItem labelItem( QString(), labelTA, refArea,
01511 KDChartEnums::MeasureOrientationMinimum, Qt::AlignLeft );
01512 TextLayoutItem titleItem( axis()->titleText(), titleTA, refArea,
01513 KDChartEnums::MeasureOrientationMinimum, Qt::AlignHCenter | Qt::AlignVCenter );
01514
01515 const QFontMetrics fm( labelItem.realFont(), GlobalMeasureScaling::paintDevice() );
01516
01517 const qreal labelGap =
01518 drawLabels
01519 ? ( (diagramIsVertical ? fm.height() : fm.averageCharWidth()) / 3.0)
01520 : 0.0;
01521 const QFontMetricsF titleFM = QFontMetricsF( titleItem.realFont(), GlobalMeasureScaling::paintDevice() );
01522 const qreal titleGap =
01523 drawTitle
01524 ? ( (diagramIsVertical ? titleFM.height() : titleFM.averageCharWidth()) / 3.0)
01525 : 0.0;
01526
01527 if ( axis()->isAbscissa() ) {
01528 const bool centerAbscissaTicks = referenceDiagramNeedsCenteredAbscissaTicks(diagram());
01529 int leftOverlap = 0;
01530 int rightOverlap = 0;
01531
01532 qreal w = diagramIsVertical ? 10.0 : 0.0;
01533 qreal h = diagramIsVertical ? 0.0 : 10.0;
01534 if( drawLabels ){
01535
01536 if( !annotations.isEmpty() )
01537 {
01538 const QStringList strings = annotations.values();
01539 KDAB_FOREACH( const QString& string, strings )
01540 {
01541 labelItem.setText( string );
01542 const QSize siz = labelItem.sizeHint();
01543 if ( diagramIsVertical )
01544 h = qMax( h, static_cast< qreal >( siz.height() ) );
01545 else
01546 w = qMax( w, static_cast< qreal >( siz.width() ) );
01547 }
01548 }
01549 else if ( !axis()->labels().isEmpty() )
01550 {
01551
01552 const int first=0;
01553 const int last=axis()->labels().count()-1;
01554 const QStringList labelsList( axis()->labels() );
01555 for ( int i = first; i <= last; ++i )
01556 {
01557 labelItem.setText( axis()->customizedLabel(labelsList[ i ]) );
01558 const QSize siz = labelItem.sizeHint();
01559
01560 if ( diagramIsVertical )
01561 h = qMax( h, static_cast<qreal>(siz.height()) );
01562 else
01563 w = qMax( w, static_cast<qreal>(siz.width()) );
01564 calculateOverlap( i, first, last, diagramIsVertical ? siz.width() : siz.height(), centerAbscissaTicks,
01565 leftOverlap, rightOverlap );
01566
01567 }
01568 }
01569 else
01570 {
01571 QStringList headerLabels = diagram()->itemRowLabels();
01572 const int headerLabelsCount = headerLabels.count();
01573 if( headerLabelsCount ){
01574 if( cachedHeaderLabels == headerLabels && ( diagramIsVertical ? cachedFontHeight == fm.height() : cachedFontWidth == fm.averageCharWidth() )) {
01575 if ( diagramIsVertical )
01576 h = cachedLabelHeight;
01577 else
01578 w = cachedLabelWidth;
01579 } else {
01580 cachedHeaderLabels = headerLabels;
01581 if ( diagramIsVertical )
01582 cachedFontWidth = fm.averageCharWidth();
01583 else
01584 cachedFontHeight = fm.height();
01585 const bool useFastCalcAlgorithm
01586 = (strcmp( axis()->metaObject()->className(), "KDChart::CartesianAxis" ) == 0);
01587 const int first=0;
01588 const int last=headerLabelsCount-1;
01589 for ( int i = first;
01590 i <= last;
01591 i = (useFastCalcAlgorithm && i < last) ? last : (i+1) )
01592 {
01593 labelItem.setText( axis()->customizedLabel(headerLabels[ i ]) );
01594 const QSize siz = labelItem.sizeHint();
01595 if ( diagramIsVertical ) {
01596 h = qMax( h, static_cast<qreal>(siz.height()) );
01597 cachedLabelHeight = h;
01598 } else {
01599 cachedLabelWidth = w;
01600 w = qMax( w, static_cast<qreal>(siz.width()) );
01601 }
01602 calculateOverlap( i, first, last, diagramIsVertical ? siz.width() : siz.height(), centerAbscissaTicks,
01603 leftOverlap, rightOverlap );
01604 }
01605 }
01606 }else{
01607 labelItem.setText(
01608 axis()->customizedLabel(
01609 QString::number( diagramIsVertical ? plane->gridDimensionsList().first().end
01610 : plane->gridDimensionsList().last().end, 'f', 0 )));
01611 const QSize siz = labelItem.sizeHint();
01612 if ( diagramIsVertical )
01613 h = siz.height();
01614 else
01615 w = siz.width();
01616 calculateOverlap( 0, 0, 0, siz.width(), centerAbscissaTicks,
01617 leftOverlap, rightOverlap );
01618 }
01619 }
01620
01621 h += labelGap;
01622 }
01623
01624 if ( drawTitle ) {
01625
01626 if ( diagramIsVertical ) {
01627 h += titleItem.sizeHint().height() + titleGap;
01628 w = titleItem.sizeHint().width() + 2.0;
01629 } else {
01630 h = titleItem.sizeHint().height() + 2.0;
01631 w += titleItem.sizeHint().width() + titleGap;
01632 }
01633 }
01634
01635 if ( diagramIsVertical )
01636 h += qAbs( axis()->tickLength() ) * 3.0;
01637 else
01638 w += qAbs( axis()->tickLength() ) * 3.0;
01639 result = QSize ( static_cast<int>( w ), static_cast<int>( h ) );
01640
01641
01642
01643
01644
01645 amountOfLeftOverlap = leftOverlap;
01646 amountOfRightOverlap = rightOverlap;
01647
01648
01649
01650
01651
01652
01653 } else {
01654 int topOverlap = 0;
01655 int bottomOverlap = 0;
01656
01657 qreal w = diagramIsVertical ? 0.0 : 10.0;
01658 qreal h = diagramIsVertical ? 10.0 : 0.0;
01659 if( drawLabels ){
01660
01661
01662 if( !annotations.isEmpty() )
01663 {
01664 const QStringList strings = annotations.values();
01665 KDAB_FOREACH( const QString& string, strings )
01666 {
01667 labelItem.setText( string );
01668 const QSize siz = labelItem.sizeHint();
01669 if ( diagramIsVertical )
01670 w = qMax( w, static_cast< qreal >( siz.width() ) );
01671 else
01672 h = qMax( h, static_cast< qreal >( siz.height() ) );
01673 }
01674 }
01675 else if( axis()->labels().isEmpty() )
01676 {
01677 const DataDimension dimY = AbstractGrid::adjustedLowerUpperRange(
01678 diagramIsVertical ? plane->gridDimensionsList().last()
01679 : plane->gridDimensionsList().first(), true, true );
01680 const double step = dimY.stepWidth;
01681 const qreal minValue = dimY.start;
01682 const qreal maxValue = dimY.end;
01683 const bool isLogarithmicY = (dimY.calcMode == AbstractCoordinatePlane::Logarithmic );
01684 qreal labelValue = minValue;
01685
01686 while( labelValue <= maxValue ) {
01687 const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) +
01688 QString::number( labelValue ) +
01689 diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true );
01690 labelItem.setText( axis()->customizedLabel( labelText ) );
01691
01692 const QSize siz = labelItem.sizeHint();
01693 if ( diagramIsVertical )
01694 w = qMax( w, (qreal)siz.width() );
01695 else
01696 h = qMax( h, (qreal)siz.height() );
01697 calculateOverlap( 0, 0, 0, diagramIsVertical ? siz.height() : siz.width(), false,
01698 topOverlap, bottomOverlap );
01699 calculateNextLabel( labelValue, step, isLogarithmicY, plane->gridDimensionsList().last().start );
01700
01701 if(maxValue == 0 && minValue == 0)
01702 break;
01703 }
01704 }else{
01705
01706 const int first=0;
01707 const int last=axis()->labels().count()-1;
01708 const QStringList labelsList( axis()->labels() );
01709 for ( int i = first; i <= last; ++i )
01710 {
01711 labelItem.setText( axis()->customizedLabel(labelsList[ i ]) );
01712 const QSize siz = labelItem.sizeHint();
01713 if ( diagramIsVertical )
01714 w = qMax( w, (qreal)siz.width() );
01715 else
01716 h = qMax( h, (qreal)siz.height() );
01717 calculateOverlap( 0, 0, 0, diagramIsVertical ? siz.height() : siz.width(), false,
01718 topOverlap, bottomOverlap );
01719 }
01720 }
01721
01722 w += labelGap;
01723 }
01724
01725 if ( drawTitle ) {
01726
01727 if ( diagramIsVertical ) {
01728 w += titleItem.sizeHint().width() + titleGap;
01729 h = titleItem.sizeHint().height() + 2.0;
01730 } else {
01731 w = titleItem.sizeHint().width() + 2.0;
01732 h += titleItem.sizeHint().height() + titleGap;
01733 }
01734
01735 }
01736
01737 if ( diagramIsVertical )
01738 w += qAbs( axis()->tickLength() ) * 3.0;
01739 else
01740 h += qAbs( axis()->tickLength() ) * 3.0;
01741
01742 result = QSize ( static_cast<int>( w ), static_cast<int>( h ) );
01743
01744
01745
01746
01747
01748 amountOfTopOverlap = topOverlap;
01749 amountOfBottomOverlap = bottomOverlap;
01750
01751
01752
01753
01754
01755
01756 }
01757
01758
01759 return result;
01760 }
01761
01762 QSize CartesianAxis::minimumSize() const
01763 {
01764 return maximumSize();
01765 }
01766
01767 QSize CartesianAxis::sizeHint() const
01768 {
01769 return maximumSize();
01770 }
01771
01772 void CartesianAxis::setGeometry( const QRect& r )
01773 {
01774
01775
01776 d->geometry = r;
01777 setCachedSizeDirty();
01778 }
01779
01780 QRect CartesianAxis::geometry() const
01781 {
01782 return d->geometry;
01783 }
01784
01785 int CartesianAxis::tickLength( bool subUnitTicks ) const
01786 {
01787 int result = 0;
01788
01789 if ( isAbscissa() ) {
01790 result = position() == Top ? -4 : 3;
01791 } else {
01792 result = position() == Left ? -4 : 3;
01793 }
01794
01795 if ( subUnitTicks )
01796 result = result < 0 ? result + 1 : result - 1;
01797
01798 return result;
01799 }
01800
01801 QMap< double, QString > CartesianAxis::annotations() const
01802 {
01803 return d->annotations;
01804 }
01805
01806 void CartesianAxis::setAnnotations( const QMap< double, QString >& annotations )
01807 {
01808 if( d->annotations == annotations )
01809 return;
01810
01811 d->annotations = annotations;
01812 update();
01813 }
01814
01815 QList< double > CartesianAxis::customTicks() const
01816 {
01817 return d->customTicksPositions;
01818 }
01819
01820 void CartesianAxis::setCustomTicks( const QList< double >& customTicksPositions )
01821 {
01822 if( d->customTicksPositions == customTicksPositions )
01823 return;
01824
01825 d->customTicksPositions = customTicksPositions;
01826 update();
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
01861
01862
01863
01864
01865
01866
01867
01868