KD Chart 2 [rev.2.4]
|
00001 /**************************************************************************** 00002 ** Copyright (C) 2001-2012 Klaralvdalens Datakonsult AB. All rights reserved. 00003 ** 00004 ** This file is part of the KD Chart library. 00005 ** 00006 ** Licensees holding valid commercial KD Chart licenses may use this file in 00007 ** accordance with the KD Chart Commercial License Agreement provided with 00008 ** the Software. 00009 ** 00010 ** 00011 ** This file may be distributed and/or modified under the terms of the 00012 ** GNU General Public License version 2 and version 3 as published by the 00013 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. 00014 ** 00015 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 00016 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 00017 ** 00018 ** Contact info@kdab.com if any conditions of this licensing are not 00019 ** clear to you. 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 // when we remove the first axis it will unregister itself and 00062 // propagate the next one to the primary, thus the while loop 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 //qDebug() << "CartesianAxis::compare() cannot compare to Null pointer"; 00086 return false; 00087 } 00088 /* 00089 qDebug() << (position() == other->position()); 00090 qDebug() << (titleText() == other->titleText()); 00091 qDebug() << (titleTextAttributes() == other->titleTextAttributes()); 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 //qDebug() << "CartesianAxis::layoutPlanes()"; 00164 if( ! d->diagram() || ! d->diagram()->coordinatePlane() ) { 00165 //qDebug() << "CartesianAxis::layoutPlanes(): Sorry, found no plane."; 00166 return; 00167 } 00168 AbstractCoordinatePlane* plane = d->diagram()->coordinatePlane(); 00169 if( plane ){ 00170 plane->layoutPlanes(); 00171 //qDebug() << "CartesianAxis::layoutPlanes() OK"; 00172 } 00173 } 00174 00175 /* 00176 void CartesianAxis::paintEvent( QPaintEvent* event ) 00177 { 00178 Q_UNUSED( event ); 00179 00180 if( ! d->diagram() || ! d->diagram()->coordinatePlane() ) return; 00181 00182 PaintContext context; 00183 QPainter painter( this ); 00184 context.setPainter( &painter ); 00185 AbstractCoordinatePlane* plane = d->diagram()->coordinatePlane(); 00186 context.setCoordinatePlane( plane ); 00187 QRectF rect = QRectF ( 1, 1, plane->width() - 3, plane->height() - 3 ); 00188 context.setRectangle( rect ); 00189 d->geometry.setSize( size() ); 00190 paintCtx( &context ); 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 //qDebug() << "CartesianAxis::paint( QPainter* painter ) " << " areaGeometry()():" << rect << " sizeHint():" << sizeHint(); 00244 00245 ctx.setRectangle( 00246 QRectF ( 00247 //QPointF(0, 0), 00248 QPointF(rect.left(), rect.top()), 00249 QSizeF(rect.width(), rect.height() ) ) ); 00250 // enabling clipping so that we're not drawing outside 00251 QRegion clipRegion( rect.adjusted( -1, -1, 1, 1 ) ); 00252 painter->save(); 00253 painter->setClipRegion( clipRegion ); 00254 paintCtx( &ctx ); 00255 painter->restore(); 00256 //qDebug() << "KDChart::CartesianAxis::paint() done."; 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 // Use negative limit to ensure that also the last tick is painted, 00274 // which is needed if major tick marks are disabled 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 // 'Drawn' ticks isn't quite the right naming here, it also counts major tick marks, which are not drawn. 00279 if( drawnTicks.count() > nextMayBeTick ) 00280 mayBeTick = drawnTicks[ nextMayBeTick ]; 00281 // Paint minor tick mark only if there is no major tick mark drawn at this point 00282 if ( isMinorTickMark || !rulerAttr.showMajorTickMarks() ) { 00283 if ( isAbscissa ) { 00284 // for the x-axis 00285 QPointF topPoint = diagramIsVertical ? QPointF( f, 0 ) : QPointF( 0, f ); 00286 QPointF bottomPoint( topPoint ); 00287 // we don't draw the sub ticks, if we are at the same position as a normal tick 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 // for the y-axis 00310 00311 QPointF leftPoint = plane->translate( diagramIsVertical ? QPointF( 0, f ) : QPointF( f, 0 ) ); 00312 //qDebug() << "geoRect:" << geoRect << " geoRect.top()" << geoRect.top() << "geoRect.bottom()" << geoRect.bottom() << " translatedValue:" << translatedValue; 00313 // we don't draw the sub ticks, if we are at the same position as a normal tick 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 // limit the allowed values to 0, 90, 180, 270: 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 //FIXME(khz): We definitely need to provide a way that users can decide 00410 // the position of an axis title. 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 //if( axis()->isOrdinate() ) 00437 // painter->rotate( 270.0 ); 00438 titleItem.setGeometry( QRect( QPoint(-size.width() / 2, -size.height() / 2), size ) ); 00439 //painter->drawRect(titleItem.geometry().adjusted(0,0,-1,-1)); 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 //qDebug() << "new axis label:" << labelValue << "+" << step << "=" << labelValue+step; 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 // note: Not having any data model assigned is no bug 00474 // but we can not draw an axis then either. 00475 if( ! d->diagram()->model() ) 00476 return; 00477 00478 // Determine the diagram that specifies the orientation of the diagram we're painting here 00479 // That diagram is the reference diagram, if it exists, or otherwise the diagram itself. 00480 // Note: In KDChart 2.3 or earlier, only a bar diagram can be vertical instead of horizontal. 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 * let us paint the labels at a 00490 * smaller resolution 00491 * Same mini pixel value as for 00492 * Cartesian Grid 00493 */ 00494 //const qreal MinimumPixelsBetweenRulers = 1.0; 00495 DataDimensionsList dimensions( plane->gridDimensionsList() ); 00496 //qDebug("CartesianAxis::paintCtx() gets DataDimensionsList.first(): start: %f end: %f stepWidth: %f", dimensions.first().start, dimensions.first().end, dimensions.first().stepWidth); 00497 00498 // test for programming errors: critical 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 // If the diagram is horizontal, we need to inverse the x/y ranges 00504 if ( diagramIsVertical ) { 00505 /*double yStart = dimY.start; 00506 double yEnd = dimY.end; 00507 dimY.start = dimX.start; 00508 dimY.end = dimX.end; 00509 dimX.start = yStart; 00510 dimX.end = yEnd;*/ 00511 dimX = AbstractGrid::adjustedLowerUpperRange( dimensions.first(), true, true ); 00512 dimY = AbstractGrid::adjustedLowerUpperRange( dimensions.last(), true, true ); 00513 00514 // FIXME 00515 // Ugly workaround for dimensions being bound to both, the x coordinate direction and the abscissa 00516 //if ( referenceDiagramIsPercentLyingBarDiagram ) { 00517 // dimY.stepWidth = 10.0; 00518 // dimY.subStepWidth = 2.0; 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 if(isAbscissa()) 00528 qDebug() << " " << "Abscissa:" << dimX.start <<".."<<dimX.end <<" step"<<dimX.stepWidth<<" sub step"<<dimX.subStepWidth; 00529 else 00530 qDebug() << " " << "Ordinate:" << dimY.start <<".."<<dimY.end <<" step"<<dimY.stepWidth<<" sub step"<<dimY.subStepWidth; 00531 */ 00532 00533 /* 00534 * let us paint the labels at a 00535 * smaller resolution 00536 * Same mini pixel value as for 00537 * Cartesian Grid 00538 */ 00539 const qreal MinimumPixelsBetweenRulers = qMin( dimX.stepWidth, dimY.stepWidth );//1.0; 00540 00541 // preparation: calculate the range that will be displayed: 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 // qDebug() << "absRange" << absRange << "dimY.stepWidth:" << dimY.stepWidth << "numberOfUnitRulers:" << numberOfUnitRulers; 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 // - calculate the absolute range in screen pixels: 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 // attributes used to customize ruler appearance 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 // - find the reference point at which to start drawing and the increment (line distance); 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 //for debugging: if( isAbscissa() )ptr->drawRect(areaGeoRect.adjusted(0,0,-1,-1)); 00598 //qDebug() << " " << (isAbscissa() ? "Abscissa":"Ordinate") << "axis painting with geometry" << areaGeoRect; 00599 00600 // FIXME references are of course different for all locations: 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 // set up the lines to paint: 00622 00623 // set up a map of integer positions, 00624 00625 // - starting with the fourth 00626 // - the the halfs 00627 // - then the tens 00628 // this will override all halfs and fourth that hit a higher-order ruler 00629 // MAKE SURE TO START AT (0, 0)! 00630 00631 // set up a reference point, a step vector and a unit vector for the drawing: 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 //#define AXES_PAINTING_DEBUG 1 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 // solving issue #4075 in a quick way: 00655 ptr->setPen ( PrintingParameters::scalePen( labelTA.pen() ) ); // perhaps we want to add a setter method later? 00656 00657 //ptr->setPen ( Qt::black ); 00658 00659 const QObject* referenceArea = plane->parent(); 00660 00661 // that QVector contains all drawn x-ticks (so no subticks are drawn there also) 00662 QVector< int > drawnAbscissaTicks; 00663 // and that does the same for the y-ticks 00664 QVector< int > drawnYTicks; 00665 00666 /* 00667 * Find out if it is a bar diagram 00668 * bar diagrams display their data per column 00669 * we need to handle the last label another way 00670 * 1 - Last label == QString null ( Header Labels ) 00671 * 2 - Display labels and ticks in the middle of the column 00672 */ 00673 00674 const bool centerAbscissaTicks = referenceDiagramNeedsCenteredAbscissaTicks( d->diagram() ); 00675 00676 // this draws the unit rulers 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 //qDebug() << (isOrdinate() ? "is Ordinate" : "is Abscissa"); 00688 headerLabels = 00689 isOrdinate() 00690 ? d->diagram()->datasetLabels() 00691 : d->diagram()->itemRowLabels(); 00692 //qDebug() << numberOfUnitRulers; 00693 // check if configured stepWidth 00694 useConfiguredStepsLabels = isAbscissa() && 00695 dimX.stepWidth && 00696 (( (headerLabels.count() - 1)/ dimX.stepWidth ) != numberOfUnitRulers); 00697 if( useConfiguredStepsLabels ) { 00698 numberOfUnitRulers = ( headerLabels.count() - 1 )/ dimX.stepWidth; 00699 // we need to register data values for the steps 00700 // in case it is configured by the user 00701 QStringList configuredStepsLabels; 00702 double value = dimX.start;// headerLabels.isEmpty() ? 0.0 : headerLabels.first().toDouble(); 00703 configuredStepsLabels << QString::number( value ); 00704 00705 for( int i = 0; i < numberOfUnitRulers; i++ ) 00706 { 00707 //qDebug() << value; 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 RulerAttributes rulerAttr = rulerAttributes(); 00720 if ( rulerAttr.showRulerLine() ) 00721 { 00722 QPointF start; 00723 QPointF end; 00724 switch( position() ) 00725 { 00726 case( CartesianAxis::Bottom ): 00727 start = QPointF( dimX.start, dimY.start ); 00728 end = QPointF( dimX.end, dimY.start ); 00729 break; 00730 case( CartesianAxis::Top ): 00731 start = QPointF( dimX.start, dimY.end ); 00732 end = QPointF( dimX.end, dimY.end ); 00733 break; 00734 case( CartesianAxis::Left ): 00735 start = QPointF( dimX.start, dimY.start ); 00736 end = QPointF( dimX.start, dimY.end ); 00737 break; 00738 case( CartesianAxis::Right ): 00739 start = QPointF( dimX.end, dimY.start ); 00740 end = QPointF( dimX.end, dimY.end ); 00741 break; 00742 } 00743 start = plane->translate( start ); 00744 end = plane->translate( end ); 00745 bool clip = context->painter()->hasClipping(); 00746 context->painter()->setClipping( false ); 00747 context->painter()->drawLine( start, end ); 00748 context->painter()->setClipping( clip ); 00749 00750 } 00751 00752 const int headerLabelsCount = headerLabels.count(); 00753 //qDebug() << "headerLabelsCount" << headerLabelsCount; 00754 00755 TextLayoutItem* labelItem = 00756 drawLabels 00757 ? new TextLayoutItem( QString::number( minValueY ), 00758 labelTA, 00759 referenceArea, 00760 KDChartEnums::MeasureOrientationMinimum, 00761 Qt::AlignLeft ) 00762 : 0; 00763 labelItem->setTextAttributes( textAttributes() ); 00764 TextLayoutItem* labelItem2 = 00765 drawLabels 00766 ? new TextLayoutItem( QString::number( minValueY ), 00767 labelTA, 00768 referenceArea, 00769 KDChartEnums::MeasureOrientationMinimum, 00770 Qt::AlignLeft ) 00771 : 0; 00772 labelItem2->setTextAttributes( textAttributes() ); 00773 const QFontMetricsF met( 00774 drawLabels 00775 ? labelItem->realFont() 00776 : QFontMetricsF( QApplication::font(), GlobalMeasureScaling::paintDevice() ) ); 00777 const qreal halfFontHeight = rulerAttr.labelMargin() >= 0 ? rulerAttr.labelMargin() : met.height() * 0.5; 00778 const qreal halfFontWidth = rulerAttr.labelMargin() >= 0 ? rulerAttr.labelMargin() : met.averageCharWidth() * 0.5; 00779 00780 if ( isAbscissa() ) { 00781 //Draw ticks at custom postions on x-axis 00782 if( !d->customTicksPositions.isEmpty() ) 00783 { 00784 const QList< double > values = d->customTicksPositions; 00785 KDAB_FOREACH( const double v, values ) 00786 { 00787 QPointF topPoint = diagramIsVertical ? QPointF( v, 0.0 ) : QPointF( 0.0, v ); 00788 QPointF bottomPoint = topPoint; 00789 topPoint = plane->translate( topPoint ); 00790 bottomPoint = plane->translate( bottomPoint ); 00791 if ( diagramIsVertical ) { 00792 topPoint.setY( rulerRef.y() + tickLength() ); 00793 bottomPoint.setY( rulerRef.y() ); 00794 } else { 00795 topPoint.setX( rulerRef.x() + tickLength() ); 00796 bottomPoint.setX( rulerRef.x() ); 00797 } 00798 00799 context->painter()->drawLine(topPoint, bottomPoint); 00800 } 00801 } 00802 00803 if( !d->annotations.isEmpty() ) 00804 { 00805 const QList< double > values = d->annotations.keys(); 00806 KDAB_FOREACH( const double v, values ) 00807 { 00808 QPointF topPoint = diagramIsVertical ? QPointF( v, 0.0 ) : QPointF( 0.0, v ); 00809 QPointF bottomPoint = topPoint; 00810 topPoint = plane->translate( topPoint ); 00811 bottomPoint = plane->translate( bottomPoint ); 00812 if ( diagramIsVertical ) { 00813 topPoint.setY( rulerRef.y() + tickLength() ); 00814 bottomPoint.setY( rulerRef.y() ); 00815 } else { 00816 topPoint.setX( rulerRef.x() + tickLength() ); 00817 bottomPoint.setX( rulerRef.x() ); 00818 } 00819 00820 labelItem->setText( d->annotations[ v ] ); 00821 const QSize size( labelItem->sizeHint() ); 00822 if ( diagramIsVertical ) { 00823 labelItem->setGeometry( 00824 QRect( 00825 QPoint( 00826 static_cast<int>( topPoint.x() - size.width() / 2.0 ), 00827 static_cast<int>( topPoint.y() + 00828 ( position() == Bottom 00829 ? halfFontHeight 00830 : ((halfFontHeight + size.height()) * -1.0) ) ) ), 00831 size ) ); 00832 } else { 00833 labelItem->setGeometry( 00834 QRect( 00835 QPoint( 00836 static_cast<int>( bottomPoint.x() + 00837 ( position() == Right 00838 ? halfFontWidth 00839 : (-halfFontWidth - size.width()) ) ), 00840 00841 static_cast<int>( topPoint.y() - ( size.height() ) * 0.5 ) ), 00842 size ) ); 00843 } 00844 00845 QRect labelGeo = labelItem->geometry(); 00846 // if our item would only half fit, we disable clipping for that one 00847 if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() ) 00848 ptr->setClipping( false ); 00849 else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() ) 00850 ptr->setClipping( false ); 00851 00852 labelItem->paint( ptr ); 00853 } 00854 } 00855 00856 qreal labelDiff = dimX.stepWidth; 00857 const int precision = ( QString::number( labelDiff ).section( QLatin1Char('.'), 1, 2 ) ).length(); 00858 00859 // If we have a labels list AND a short labels list, we first find out, 00860 // if there is enough space for showing ALL of the long labels: 00861 // If not, use the short labels. 00862 if( drawLabels && hardLabelsCount > 0 && shortLabelsCount > 0 && d->annotations.isEmpty() ){ 00863 bool labelsAreOverlapping = false; 00864 int iLabel = 0; 00865 qreal i = minValueX; 00866 while ( i < maxValueX-1 && !labelsAreOverlapping ) 00867 { 00868 const int idx = (iLabel < hardLabelsCount ) ? iLabel : 0; 00869 const int idx2= (iLabel < hardLabelsCount - 1) ? iLabel + 1 : 0; 00870 if ( dimX.stepWidth != 1.0 && ! dim.isCalculated ) 00871 { 00872 // Check intersects for the header label - we need to pass the full string 00873 // here and not only the i value. 00874 if( useConfiguredStepsLabels ){ 00875 labelItem->setText( customizedLabel(headerLabels[ idx ]) ); 00876 labelItem2->setText( customizedLabel(headerLabels[ idx2 ]) ); 00877 }else{ 00878 //qDebug() << "i + labelDiff " << i + labelDiff; 00879 labelItem->setText( customizedLabel(headerLabelsCount > i && i >= 0 ? 00880 headerLabels[static_cast<int>(i)] : 00881 QString::number( i, 'f', precision )) ); 00882 // qDebug() << "1 - labelItem->text() " << labelItem->text(); 00883 //qDebug() << "labelDiff" << labelDiff 00884 // << " index" << i+labelDiff << " count" << headerLabelsCount; 00885 labelItem2->setText( customizedLabel(headerLabelsCount > i + labelDiff && i + labelDiff >= 0 ? 00886 headerLabels[static_cast<int>(i+labelDiff)] : 00887 QString::number( i + labelDiff, 'f', precision )) ); 00888 //qDebug() << "2 - labelItem->text() " << labelItem->text(); 00889 } 00890 } else { 00891 //qDebug() << iLabel << i << "("<<hardLabelsCount<<") :"; 00892 const int shortIdx = (iLabel < shortLabelsCount ) ? iLabel : 0; 00893 const int shortIdx2 = (iLabel < shortLabelsCount - 1) ? iLabel + 1 : 0; 00894 labelItem->setText( customizedLabel( 00895 useShortLabels ? shortLabelsList[ shortIdx ] : labelsList[ idx ] ) ); 00896 labelItem2->setText( customizedLabel( 00897 useShortLabels ? shortLabelsList[ shortIdx2 ] : labelsList[ idx2 ] ) ); 00898 } 00899 00900 QPointF firstPos = diagramIsVertical ? QPointF( i, 0.0 ) : QPointF( 0.0, i ); 00901 firstPos = plane->translate( firstPos ); 00902 00903 QPointF secondPos = diagramIsVertical ? QPointF( i + labelDiff, 0.0 ) : QPointF( 0.0, i + labelDiff ); 00904 secondPos = plane->translate( secondPos ); 00905 00906 labelsAreOverlapping = labelItem->intersects( *labelItem2, firstPos, secondPos ); 00907 00908 if ( ++iLabel > hardLabelsCount - 1 ) 00909 iLabel = 0; 00910 if ( isLogarithmicX ) 00911 i *= 10.0; 00912 else 00913 i += dimX.stepWidth; 00914 //qDebug() << labelsAreOverlapping << iLabel << i << labelsAreOverlapping << firstPos << secondPos.x()-firstPos .x() << labelItem->text() << labelItem2->text(); 00915 } 00916 00917 useShortLabels = labelsAreOverlapping; 00918 } 00919 00920 // qDebug() << "initial labelDiff " << labelDiff; 00921 if ( drawLabels && d->annotations.isEmpty() ) 00922 { 00923 qreal i = minValueX; 00924 int iLabel = 0; 00925 00926 while ( i + labelDiff < maxValueX ) 00927 { 00928 const int idx = (iLabel < hardLabelsCount ) ? iLabel : 0; 00929 const int idx2= (iLabel < hardLabelsCount - 1) ? iLabel + 1 : 0; 00930 //qDebug() << "drawLabels" << drawLabels << " hardLabelsCount" << hardLabelsCount 00931 // << " dimX.stepWidth" << dimX.stepWidth << " dim.isCalculated" << dim.isCalculated; 00932 if ( !drawLabels || hardLabelsCount < 1 || ( dimX.stepWidth != 1.0 && ! dim.isCalculated ) ) 00933 { 00934 // Check intersects for the header label - we need to pass the full string 00935 // here and not only the i value. 00936 if( useConfiguredStepsLabels ){ 00937 labelItem->setText( customizedLabel(headerLabels[ idx ]) ); 00938 labelItem2->setText( customizedLabel(headerLabels[ idx2 ]) ); 00939 }else{ 00940 //qDebug() << "i + labelDiff " << i + labelDiff; 00941 labelItem->setText( customizedLabel(headerLabelsCount > i && i >= 0 ? 00942 headerLabels[static_cast<int>(i)] : 00943 QString::number( i, 'f', precision )) ); 00944 // qDebug() << "1 - labelItem->text() " << labelItem->text(); 00945 //qDebug() << "labelDiff" << labelDiff 00946 // << " index" << i+labelDiff << " count" << headerLabelsCount; 00947 labelItem2->setText( customizedLabel(headerLabelsCount > i + labelDiff && i + labelDiff >= 0 ? 00948 headerLabels[static_cast<int>(i+labelDiff)] : 00949 QString::number( i + labelDiff, 'f', precision )) ); 00950 //qDebug() << "2 - labelItem->text() " << labelItem->text(); 00951 } 00952 } else { 00953 const int shortIdx = (iLabel < shortLabelsCount ) ? iLabel : 0; 00954 const int shortIdx2 = (iLabel < shortLabelsCount - 1) ? iLabel + 1 : 0; 00955 labelItem->setText( customizedLabel( 00956 useShortLabels ? shortLabelsList[ shortIdx ] : labelsList[ idx ] ) ); 00957 labelItem2->setText( customizedLabel( 00958 useShortLabels ? shortLabelsList[ shortIdx2 ] : labelsList[ idx2 ] ) ); 00959 } 00960 00961 QPointF firstPos = diagramIsVertical ? QPointF( i, 0.0 ) : QPointF( 0.0, i ); 00962 firstPos = plane->translate( firstPos ); 00963 00964 QPointF secondPos = diagramIsVertical ? QPointF( i + labelDiff, 0.0 ) : QPointF( 0.0, i + labelDiff ); 00965 secondPos = plane->translate( secondPos ); 00966 00967 if ( labelItem->intersects( *labelItem2, firstPos, secondPos ) ) 00968 { 00969 i = minValueX; 00970 00971 // fix for issue #4179: 00972 labelDiff *= 10.0; 00973 // old code: labelDiff += labelDiff; 00974 00975 iLabel = 0; 00976 //qDebug() << firstPos << secondPos.x()-firstPos .x() << labelItem->text() << labelItem2->text() << labelDiff; 00977 } 00978 else 00979 { 00980 i += labelDiff; 00981 //qDebug() << firstPos << secondPos.x()-firstPos .x() << labelItem->text() << labelItem2->text(); 00982 } 00983 00984 if ( (++iLabel > hardLabelsCount - 1) && !useConfiguredStepsLabels ) 00985 { 00986 iLabel = 0; 00987 } 00988 } 00989 // fixing bugz issue #5018 without breaking issue #4179: 00990 if( minValueX + labelDiff > maxValueX ) 00991 labelDiff = maxValueX - minValueX; 00992 // This makes sure the first and the last X label are drawn 00993 // if there is not enouth place to draw some more of them 00994 // according to labelDiff calculation performed above. 00995 } 00996 00997 int idxLabel = 0; 00998 qreal iLabelF = minValueX; 00999 //qDebug() << iLabelF; 01000 qreal i = minValueX; 01001 qreal labelStep = 0.0; 01002 // qDebug() << "dimX.stepWidth:" << dimX.stepWidth << "labelDiff:" << labelDiff; 01003 //dimX.stepWidth = 0.5; 01004 while( i <= maxValueX && d->annotations.isEmpty() ) 01005 { 01006 // Line charts: we want the first tick to begin at 0.0 not at 0.5 otherwise labels and 01007 // values does not fit each others 01008 QPointF topPoint = diagramIsVertical ? QPointF( i + ( centerAbscissaTicks ? 0.5 : 0.0 ), 0.0 ) : QPointF( 0.0, i + ( centerAbscissaTicks ? 0.5 : 0.0 ) ); 01009 QPointF bottomPoint ( topPoint ); 01010 topPoint = plane->translate( topPoint ); 01011 bottomPoint = plane->translate( bottomPoint ); 01012 if ( diagramIsVertical ) { 01013 topPoint.setY( rulerRef.y() + tickLength() ); 01014 bottomPoint.setY( rulerRef.y() ); 01015 } else { 01016 bottomPoint.setX( rulerRef.x() - (position() == Left ? tickLength() : -tickLength()) ); 01017 topPoint.setX( rulerRef.x() ); 01018 } 01019 01020 const qreal translatedValue = diagramIsVertical ? topPoint.x() : topPoint.y(); 01021 bool bIsVisibleLabel; 01022 if ( diagramIsVertical ) 01023 bIsVisibleLabel = ( (translatedValue >= geoRect.left() && translatedValue <= geoRect.right() && !isLogarithmicX) || i != 0.0 ); 01024 else 01025 bIsVisibleLabel = ( (translatedValue >= geoRect.top() && translatedValue <= geoRect.bottom() && !isLogarithmicX) || i != 0.0 ); 01026 01027 // fix for issue #4179: 01028 bool painttick = bIsVisibleLabel && labelStep <= 0; 01029 // old code: bool painttick = true; 01030 01031 //Dont paint more ticks than we need 01032 //when diagram type is Bar 01033 if ( centerAbscissaTicks && i == maxValueX ) 01034 painttick = false; 01035 01036 if ( bIsVisibleLabel && painttick ) { 01037 ptr->save(); 01038 if ( rulerAttr.hasTickMarkPenAt( i ) ) 01039 ptr->setPen( rulerAttr.tickMarkPen( i ) ); 01040 else 01041 ptr->setPen( rulerAttr.majorTickMarkPen() ); 01042 ptr->drawLine( topPoint, bottomPoint ); 01043 ptr->restore(); 01044 } 01045 01046 drawnAbscissaTicks.append( static_cast<int>( diagramIsVertical ? topPoint.x() : topPoint.y() ) ); 01047 if( drawLabels ) { 01048 if( bIsVisibleLabel ){ 01049 if ( isLogarithmicX ) 01050 labelItem->setText( customizedLabel(QString::number( i ) ) ); 01051 /* We don't need that 01052 * it causes header labels to be skipped even if there is enough 01053 * space for them to displayed. 01054 * Commenting for now - I need to test more in details - Let me know if I am wrong here. 01055 */ 01056 /* 01057 else if( (dimX.stepWidth != 1.0) && ! dimX.isCalculated ) { 01058 labelItem->setText( customizedLabel(QString::number( i, 'f', 0 )) ); 01059 } 01060 */ 01061 else { 01062 int idx = idxLabel + static_cast<int>(minValueX); 01063 if( hardLabelsCount ){ 01064 if( useShortLabels ){ 01065 if( idx >= shortLabelsList.count() ) 01066 idx = 0; 01067 }else{ 01068 if( idx >= labelsList.count() ) 01069 idx = 0; 01070 } 01071 } 01072 labelItem->setText( 01073 customizedLabel( 01074 hardLabelsCount 01075 ? ( useShortLabels ? shortLabelsList[ idx ] : labelsList[ idx ] ) 01076 : ( headerLabelsCount ? headerLabels[ idx ] : QString::number( iLabelF )))); 01077 //qDebug() << "x - labelItem->text() " << labelItem->text() << headerLabelsCount; 01078 } 01079 // No need to call labelItem->setParentWidget(), since we are using 01080 // the layout item temporarily only. 01081 if( labelStep <= 0 ) { 01082 const PainterSaver p( ptr ); 01083 //const QSize size( labelItem->sizeHint() ); 01084 QPoint topLeft, topRight, bottomRight, bottomLeft; 01085 const QSize size( 01086 labelItem->sizeHintAndRotatedCorners( 01087 topLeft, topRight, bottomRight, bottomLeft) ); 01088 const QSize sizeUnrotated( labelItem->sizeHintUnrotated() ); 01089 const int rotation = labelTA.rotation(); 01090 const bool rotPositive = (rotation > 0 && rotation < 180); 01091 QPoint midOfSide(0,0); 01092 int dX = 0; 01093 int dY = 0; 01094 if( rotation ){ 01095 if( rotPositive ){ 01096 midOfSide = (topLeft + bottomLeft) / 2; 01097 dX = topLeft.x() - midOfSide.x(); 01098 dY = bottomLeft.y() - midOfSide.y(); 01099 }else{ 01100 midOfSide = (topRight + bottomRight) / 2; 01101 dX = midOfSide.x() - topLeft.x(); 01102 dY = midOfSide.y() - topRight.y(); 01103 } 01104 } 01105 /* 01106 if( i == 2 ){ 01107 qDebug()<<"------"<<size<<topPoint<<topLeft<<topRight<<bottomRight<<bottomLeft<<" m:"<<midOfSide<<" dx"<<dX<<" dy"<<dY; 01108 ptr->setPen( Qt::black ); 01109 QRectF rect(topPoint, QSizeF(sizeUnrotated)); 01110 ptr->drawRect( rect ); 01111 ptr->drawRect( QRectF(topPoint, QSizeF(2,2)) ); 01112 ptr->drawRect( QRectF(topPoint+topLeft, QSizeF(2,2)) ); 01113 ptr->drawRect( QRectF(topPoint+bottomLeft, QSizeF(2,2)) ); 01114 ptr->drawRect( QRectF(topPoint+bottomRight, QSizeF(2,2)) ); 01115 ptr->drawRect( QRectF(topPoint+topRight, QSizeF(2,2)) ); 01116 ptr->drawRect( QRectF(topPoint+midOfSide, QSizeF(2,2)) ); 01117 ptr->setPen( Qt::green ); 01118 rect = QRectF(topPoint, QSizeF(size)); 01119 ptr->drawRect( rect ); 01120 ptr->drawRect( QRectF(QPointF((rect.topLeft() + rect.bottomLeft()) / 2.0 - QPointF(2.0,2.0)), QSizeF(3.0,3.0)) ); 01121 //ptr->drawRect( QRectF(QPointF((rect.topRight() + rect.bottomRight()) / 2.0 - QPointF(2.0,2.0)), QSizeF(3.0,3.0)) ); 01122 } 01123 */ 01124 QPoint topLeftPt; 01125 if( diagramIsVertical ){ 01126 if( rotation ){ 01127 topLeftPt = QPoint( 01128 static_cast<int>( topPoint.x() ) - dX, 01129 static_cast<int>( topPoint.y() - dY + 01130 ( position() == Bottom 01131 ? halfFontHeight 01132 : ((halfFontHeight + size.height()) * -1.0) ) ) ); 01133 }else{ 01134 topLeftPt = QPoint( 01135 static_cast<int>( topPoint.x() - size.width() / 2.0 ), 01136 static_cast<int>( topPoint.y() + 01137 ( position() == Bottom 01138 ? halfFontHeight 01139 : ((halfFontHeight + size.height()) * -1.0) ) ) ); 01140 } 01141 }else{ 01142 if( rotation ){ 01143 topLeftPt = QPoint( 01144 static_cast<int>( topPoint.x() ) + dX, 01145 static_cast<int>( topPoint.y() - dY + 01146 ( position() == Bottom 01147 ? halfFontHeight 01148 : ((halfFontHeight + size.height()) * -1.0) ) ) ); 01149 }else{ 01150 topLeftPt = QPoint( 01151 static_cast<int>( bottomPoint.x() + 01152 ( position() == Right 01153 ? halfFontWidth 01154 : (-halfFontWidth - size.width()) ) ), 01155 static_cast<int>( topPoint.y() - ( size.height() ) * 0.5 ) ); 01156 } 01157 } 01158 labelItem->setGeometry( QRect(topLeftPt, size) ); 01159 01160 QRect labelGeo = labelItem->geometry(); 01161 //ptr->drawRect(labelGeo); 01162 // if our item would only half fit, we disable clipping for that one 01163 if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() ) 01164 ptr->setClipping( false ); 01165 else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() ) 01166 ptr->setClipping( false ); 01167 01168 if( !isLogarithmicX ) 01169 labelStep = labelDiff - dimX.stepWidth; 01170 01171 // if our item would only half fit, we disable clipping for that one 01172 if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() ) 01173 ptr->setClipping( false ); 01174 else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() ) 01175 ptr->setClipping( false ); 01176 01177 labelItem->paint( ptr ); 01178 01179 // do not call customizedLabel() again: 01180 labelItem2->setText( labelItem->text() ); 01181 01182 } else { 01183 labelStep -= dimX.stepWidth; 01184 } 01185 } 01186 01187 if( hardLabelsCount ) { 01188 if( useShortLabels && idxLabel >= shortLabelsCount - 1 ) 01189 idxLabel = 0; 01190 else if( !useShortLabels && idxLabel >= hardLabelsCount - 1 ) 01191 idxLabel = 0; 01192 else{ 01193 idxLabel += static_cast<int>(dimX.stepWidth); 01194 //qDebug() << "dimX.stepWidth:" << dimX.stepWidth << " idxLabel:" << idxLabel; 01195 } 01196 } else if( headerLabelsCount ) { 01197 if( ++idxLabel > headerLabelsCount - 1 ) { 01198 idxLabel = 0; 01199 } 01200 } else { 01201 iLabelF += dimX.stepWidth; 01202 } 01203 } 01204 if ( isLogarithmicX ) 01205 { 01206 i *= 10.0; 01207 if( i == 0.0 ) 01208 { 01209 const qreal j = dimensions.first().start; 01210 i = j == 0.0 ? 1.0 : pow( 10.0, floor( log10( j ) ) ); 01211 } 01212 } 01213 else 01214 { 01215 i += dimX.stepWidth; 01216 } 01217 } 01218 } else { 01219 const PainterSaver p( ptr ); 01220 const double maxLimit = maxValueY; 01221 const double steg = dimY.stepWidth; 01222 int maxLabelsWidth = 0; 01223 qreal labelValue; 01224 01225 if( drawLabels && position() == Right ){ 01226 // Find the widest label, so we to know how much we need to right-shift 01227 // our labels, to get them drawn right aligned: 01228 labelValue = minValueY; 01229 while ( labelValue <= maxLimit ) { 01230 const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) + 01231 QString::number( labelValue ) + 01232 diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true ); 01233 labelItem->setText( customizedLabel( labelText ) ); 01234 maxLabelsWidth = qMax( maxLabelsWidth, diagramIsVertical ? labelItem->sizeHint().width() : labelItem->sizeHint().height() ); 01235 calculateNextLabel( labelValue, steg, isLogarithmicY, dimensions.last().start ); 01236 01237 if(maxValueY == 0 && minValueY == 0) 01238 break; 01239 } 01240 } 01241 01242 labelValue = minValueY; 01243 qreal step = steg; 01244 bool nextLabel = false; 01245 //qDebug("minValueY: %f maxLimit: %f steg: %f", minValueY, maxLimit, steg); 01246 01247 //Draws custom tick marks in the y-axis 01248 if( !d->customTicksPositions.isEmpty() ) 01249 { 01250 const QList< double > values = d->customTicksPositions; 01251 KDAB_FOREACH( const double value, values ) 01252 { 01253 QPointF annoPoint = (diagramIsVertical ? QPointF( 0.0, value ) : QPointF( value, 0.0 )); 01254 QPointF leftPoint = plane->translate( annoPoint ); 01255 QPointF rightPoint = plane->translate( annoPoint ); 01256 01257 if ( diagramIsVertical ) { 01258 leftPoint.setX( rulerRef.x() + tickLength() ); 01259 rightPoint.setX( rulerRef.x() ); 01260 } else { 01261 leftPoint.setY( rulerRef.y() + ((position() == Bottom) ? tickLength() : -tickLength()) ); 01262 rightPoint.setY( rulerRef.y() ); 01263 } 01264 01265 context->painter()->drawLine(leftPoint, rightPoint); 01266 } 01267 } 01268 01269 if( drawLabels ) 01270 { 01271 // first calculate the steps depending on labels colision 01272 while( labelValue <= maxLimit ) { 01273 QPointF leftPoint = plane->translate( diagramIsVertical ? QPointF( 0, labelValue ) : QPointF( labelValue, 0 ) ); 01274 const qreal translatedValue = diagramIsVertical ? leftPoint.y() : leftPoint.x(); 01275 //qDebug() << "geoRect:" << geoRect << " geoRect.top()" << geoRect.top() << "geoRect.bottom()" << geoRect.bottom() << " translatedValue:" << translatedValue; 01276 const bool bTranslatedValueIsWithinRange = diagramIsVertical ? translatedValue > geoRect.top() && translatedValue <= geoRect.bottom() 01277 : translatedValue > geoRect.left() && translatedValue <= geoRect.right(); 01278 if( bTranslatedValueIsWithinRange ){ 01279 const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) + 01280 QString::number( labelValue ) + 01281 diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true ); 01282 const QString label2Text = diagram()->unitPrefix( static_cast< int >( labelValue + step ), diagramOrientation, true ) + 01283 QString::number( labelValue + step ) + 01284 diagram()->unitSuffix( static_cast< int >( labelValue + step ), diagramOrientation, true ); 01285 labelItem->setText( customizedLabel( labelText ) ); 01286 labelItem2->setText( customizedLabel( QString::number( labelValue + step ) ) ); 01287 QPointF nextPoint = plane->translate( diagramIsVertical ? QPointF( 0, labelValue + step ) : QPointF( labelValue + step, 0 ) ); 01288 if ( labelItem->intersects( *labelItem2, leftPoint, nextPoint ) ) 01289 { 01290 step += steg; 01291 nextLabel = false; 01292 }else{ 01293 nextLabel = true; 01294 } 01295 }else{ 01296 nextLabel = true; 01297 } 01298 01299 if ( nextLabel || isLogarithmicY ) 01300 calculateNextLabel( labelValue, step, isLogarithmicY, dimensions.last().start ); 01301 else 01302 labelValue = minValueY; 01303 } 01304 01305 // Second - Paint the labels 01306 labelValue = minValueY; 01307 //qDebug() << "axis labels starting at" << labelValue << "step width" << step; 01308 if( !d->annotations.isEmpty() ) 01309 { 01310 const QList< double > annotations = d->annotations.keys(); 01311 KDAB_FOREACH( const double annotation, annotations ) 01312 { 01313 QPointF annoPoint = (diagramIsVertical ? QPointF( 0.0, annotation ) : QPointF( annotation, 0.0 )); 01314 QPointF leftPoint = plane->translate( annoPoint ); 01315 QPointF rightPoint = plane->translate( annoPoint ); 01316 01317 if ( diagramIsVertical ) { 01318 leftPoint.setX( rulerRef.x() + tickLength() ); 01319 rightPoint.setX( rulerRef.x() ); 01320 } else { 01321 leftPoint.setY( rulerRef.y() + ((position() == Bottom) ? tickLength() : -tickLength()) ); 01322 rightPoint.setY( rulerRef.y() ); 01323 } 01324 01325 const qreal translatedValue = diagramIsVertical ? rightPoint.y() : rightPoint.x(); 01326 const bool bIsVisibleLabel = diagramIsVertical ? 01327 ( (translatedValue >= geoRect.top() && translatedValue <= geoRect.bottom() && !isLogarithmicY) || labelValue != 0.0 ) 01328 : ( (translatedValue >= geoRect.left() && translatedValue <= geoRect.right() && !isLogarithmicY) || labelValue != 0.0 ); 01329 01330 if( bIsVisibleLabel ) 01331 { 01332 ptr->save(); 01333 if ( rulerAttr.hasTickMarkPenAt( annotation ) ) 01334 ptr->setPen( rulerAttr.tickMarkPen( annotation ) ); 01335 else 01336 ptr->setPen( rulerAttr.majorTickMarkPen() ); 01337 ptr->drawLine( leftPoint, rightPoint ); 01338 ptr->restore(); 01339 01340 labelItem->setText( d->annotations[ annotation ] ); 01341 const QSize labelSize( labelItem->sizeHint() ); 01342 int x, y; 01343 if ( diagramIsVertical ) { 01344 x = static_cast<int>( leftPoint.x() + met.height() * ( position() == Left ? -0.5 : 0.5) 01345 - ( position() == Left ? labelSize.width() : 0.0 ) ); 01346 y = static_cast<int>( leftPoint.y() - ( met.ascent() + met.descent() ) * 0.6 ); 01347 } else { 01348 const qreal halfFontHeight = met.height() * 0.5; 01349 x = static_cast<int>( leftPoint.x() - labelSize.width() * 0.5 ); 01350 y = static_cast<int>( (position() == Bottom ? leftPoint.y() : rightPoint.y()) + 01351 + ( position() == Bottom ? halfFontHeight : -(halfFontHeight + labelSize.height()) ) ); 01352 } 01353 labelItem->setGeometry( QRect( QPoint( x, y ), labelSize ) ); 01354 01355 QRect labelGeo = labelItem->geometry(); 01356 // if our item would only half fit, we disable clipping for that one 01357 if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() ) 01358 ptr->setClipping( false ); 01359 else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() ) 01360 ptr->setClipping( false ); 01361 01362 labelItem->paint( ptr ); 01363 } 01364 } 01365 } 01366 else 01367 { 01368 while( labelValue <= maxLimit ) { 01369 //qDebug() << "value now" << labelValue; 01370 const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) + 01371 QString::number( labelValue ) + 01372 diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true ); 01373 labelItem->setText( customizedLabel( labelText ) ); 01374 QPointF leftPoint = plane->translate( diagramIsVertical ? QPointF( 0, labelValue ) : QPointF( labelValue, 0 ) ); 01375 QPointF rightPoint = leftPoint; 01376 01377 if ( diagramIsVertical ) { 01378 leftPoint.setX( rulerRef.x() + tickLength() ); 01379 rightPoint.setX( rulerRef.x() ); 01380 } else { 01381 leftPoint.setY( rulerRef.y() + ((position() == Bottom) ? tickLength() : -tickLength()) ); 01382 rightPoint.setY( rulerRef.y() ); 01383 } 01384 01385 bool bIsVisibleLabel; 01386 const qreal translatedValue = diagramIsVertical ? rightPoint.y() : rightPoint.x(); 01387 if ( diagramIsVertical) 01388 bIsVisibleLabel = ( (translatedValue >= geoRect.left() && translatedValue <= geoRect.right() && !isLogarithmicX) || labelValue != 0.0 ); 01389 else 01390 bIsVisibleLabel = ( (translatedValue >= geoRect.top() && translatedValue <= geoRect.bottom() && !isLogarithmicX) || labelValue != 0.0 ); 01391 01392 if( bIsVisibleLabel ){ 01393 ptr->save(); 01394 if ( rulerAttr.hasTickMarkPenAt( labelValue ) ) 01395 ptr->setPen( rulerAttr.tickMarkPen( labelValue ) ); 01396 else 01397 ptr->setPen( rulerAttr.majorTickMarkPen() ); 01398 ptr->drawLine( leftPoint, rightPoint ); 01399 ptr->restore(); 01400 01401 drawnYTicks.append( static_cast<int>( diagramIsVertical ? leftPoint.y() : leftPoint.x() ) ); 01402 const QSize labelSize( labelItem->sizeHint() ); 01403 01404 int x, y; 01405 if ( diagramIsVertical ) { 01406 x = static_cast<int>( leftPoint.x() + met.height() * ( position() == Left ? -0.5 : 0.5) ) 01407 - ( position() == Left ? labelSize.width() : (labelSize.width() - maxLabelsWidth) ); 01408 y = static_cast<int>( leftPoint.y() - ( met.ascent() + met.descent() ) * 0.6 ); 01409 } else { 01410 const qreal halfFontHeight = met.height() * 0.5; 01411 x = static_cast<int>( leftPoint.x() - labelSize.width() * 0.5 ); 01412 y = static_cast<int>( (position() == Bottom ? leftPoint.y() : rightPoint.y()) + 01413 + ( position() == Bottom ? halfFontHeight : -(halfFontHeight + labelSize.height()) ) ); 01414 } 01415 01416 labelItem->setGeometry( QRect( QPoint( x, y ), labelSize ) ); 01417 const QRect labelGeo = labelItem->geometry(); 01418 const bool hadClipping = ptr->hasClipping(); 01419 if( labelGeo.top() < geoRect.top() && labelGeo.bottom() > geoRect.top() ) 01420 ptr->setClipping( false ); 01421 else if( labelGeo.top() < geoRect.bottom() && labelGeo.bottom() > geoRect.bottom() ) 01422 ptr->setClipping( false ); 01423 01424 // if our item would only half fit, we disable clipping for that one 01425 if( labelGeo.left() < geoRect.left() && labelGeo.right() > geoRect.left() ) 01426 ptr->setClipping( false ); 01427 else if( labelGeo.left() < geoRect.right() && labelGeo.right() > geoRect.right() ) 01428 ptr->setClipping( false ); 01429 01430 labelItem->paint( ptr ); 01431 ptr->setClipping( hadClipping ); 01432 } 01433 01434 //qDebug() << step; 01435 calculateNextLabel( labelValue, step, isLogarithmicY, dimensions.last().start ); 01436 } 01437 } 01438 } 01439 } 01440 delete labelItem; 01441 delete labelItem2; 01442 } 01443 01444 // this draws the subunit rulers 01445 if ( drawSubUnitRulers && d->annotations.isEmpty() ) { 01446 ptr->save(); 01447 01448 d->drawSubUnitRulers( ptr, plane, dim, rulerRef, isAbscissa() ? drawnAbscissaTicks : drawnYTicks, diagramIsVertical, rulerAttr ); 01449 01450 ptr->restore(); 01451 } 01452 01453 if( ! titleText().isEmpty() ) { 01454 d->drawTitleText( ptr, plane, areaGeoRect ); 01455 } 01456 01457 //qDebug() << "KDChart::CartesianAxis::paintCtx() done."; 01458 } 01459 01460 01461 /* pure virtual in QLayoutItem */ 01462 bool CartesianAxis::isEmpty() const 01463 { 01464 return false; // if the axis exists, it has some (perhaps default) content 01465 } 01466 /* pure virtual in QLayoutItem */ 01467 Qt::Orientations CartesianAxis::expandingDirections() const 01468 { 01469 Qt::Orientations ret; 01470 switch ( position() ) 01471 { 01472 case Bottom: 01473 case Top: 01474 ret = Qt::Horizontal; 01475 break; 01476 case Left: 01477 case Right: 01478 ret = Qt::Vertical; 01479 break; 01480 default: 01481 Q_ASSERT( false ); // all positions need to be handeld 01482 break; 01483 }; 01484 return ret; 01485 } 01486 01487 01488 static void calculateOverlap( int i, int first, int last, 01489 int measure, 01490 bool centerAbscissaTicks, 01491 int& firstOverlap, int& lastOverlap ) 01492 { 01493 if( i == first ){ 01494 if( centerAbscissaTicks ){ 01495 //TODO(khz): Calculate the amount of left overlap 01496 // for bar diagrams. 01497 }else{ 01498 firstOverlap = measure / 2; 01499 } 01500 } 01501 // we test both bounds in on go: first and last might be equal 01502 if( i == last ){ 01503 if( centerAbscissaTicks ){ 01504 //TODO(khz): Calculate the amount of right overlap 01505 // for bar diagrams. 01506 }else{ 01507 lastOverlap = measure / 2; 01508 } 01509 } 01510 } 01511 01512 01513 void CartesianAxis::setCachedSizeDirty() const 01514 { 01515 d->cachedMaximumSize = QSize(); 01516 } 01517 01518 /* pure virtual in QLayoutItem */ 01519 QSize CartesianAxis::maximumSize() const 01520 { 01521 if( ! d->cachedMaximumSize.isValid() ) 01522 d->cachedMaximumSize = d->calculateMaximumSize(); 01523 return d->cachedMaximumSize; 01524 } 01525 01526 QSize CartesianAxis::Private::calculateMaximumSize() const 01527 { 01528 QSize result; 01529 if ( !diagram() ) 01530 return result; 01531 01532 const AbstractCartesianDiagram * dia = qobject_cast< const AbstractCartesianDiagram * >( diagram() ); 01533 if( dia && dia->referenceDiagram() ) 01534 dia = dia->referenceDiagram(); 01535 const BarDiagram *barDiagram = qobject_cast< const BarDiagram* >( dia ); 01536 const Qt::Orientation diagramOrientation = barDiagram != 0 ? barDiagram->orientation() : Qt::Vertical; 01537 const bool diagramIsVertical = diagramOrientation == Qt::Vertical; 01538 01539 const TextAttributes labelTA = mAxis->textAttributes(); 01540 const bool drawLabels = labelTA.isVisible(); 01541 01542 const TextAttributes titleTA( titleTextAttributesWithAdjustedRotation() ); 01543 const bool drawTitle = titleTA.isVisible() && ! axis()->titleText().isEmpty(); 01544 01545 AbstractCoordinatePlane* plane = diagram()->coordinatePlane(); 01546 //qDebug() << this<<"::maximumSize() uses plane geometry" << plane->geometry(); 01547 QObject* refArea = plane->parent(); 01548 TextLayoutItem labelItem( QString(), labelTA, refArea, 01549 KDChartEnums::MeasureOrientationMinimum, Qt::AlignLeft ); 01550 TextLayoutItem titleItem( axis()->titleText(), titleTA, refArea, 01551 KDChartEnums::MeasureOrientationMinimum, Qt::AlignHCenter | Qt::AlignVCenter ); 01552 01553 const QFontMetrics fm( labelItem.realFont(), GlobalMeasureScaling::paintDevice() ); 01554 01555 const qreal labelGap = 01556 drawLabels 01557 ? ( (diagramIsVertical ? fm.height() : fm.averageCharWidth()) / 3.0) 01558 : 0.0; 01559 const QFontMetricsF titleFM = QFontMetricsF( titleItem.realFont(), GlobalMeasureScaling::paintDevice() ); 01560 const qreal titleGap = 01561 drawTitle 01562 ? ( (diagramIsVertical ? titleFM.height() : titleFM.averageCharWidth()) / 3.0) 01563 : 0.0; 01564 01565 if ( axis()->isAbscissa() ) { 01566 const bool centerAbscissaTicks = referenceDiagramNeedsCenteredAbscissaTicks(diagram()); 01567 int leftOverlap = 0; 01568 int rightOverlap = 0; 01569 01570 qreal w = diagramIsVertical ? 10.0 : 0.0; 01571 qreal h = diagramIsVertical ? 0.0 : 10.0; 01572 if( drawLabels ){ 01573 // if there're no label strings, we take the biggest needed number as height 01574 if( !annotations.isEmpty() ) 01575 { 01576 const QStringList strings = annotations.values(); 01577 KDAB_FOREACH( const QString& string, strings ) 01578 { 01579 labelItem.setText( string ); 01580 const QSize siz = labelItem.sizeHint(); 01581 if ( diagramIsVertical ) 01582 h = qMax( h, static_cast< qreal >( siz.height() ) ); 01583 else 01584 w = qMax( w, static_cast< qreal >( siz.width() ) ); 01585 } 01586 } 01587 else if ( !axis()->labels().isEmpty() ) 01588 { 01589 // find the longest label text: 01590 const int first=0; 01591 const int last=axis()->labels().count()-1; 01592 const QStringList labelsList( axis()->labels() ); 01593 for ( int i = first; i <= last; ++i ) 01594 { 01595 labelItem.setText( axis()->customizedLabel(labelsList[ i ]) ); 01596 const QSize siz = labelItem.sizeHint(); 01597 //qDebug()<<siz; 01598 if ( diagramIsVertical ) 01599 h = qMax( h, static_cast<qreal>(siz.height()) ); 01600 else 01601 w = qMax( w, static_cast<qreal>(siz.width()) ); 01602 calculateOverlap( i, first, last, diagramIsVertical ? siz.width() : siz.height(), centerAbscissaTicks, 01603 leftOverlap, rightOverlap ); 01604 01605 } 01606 } 01607 else 01608 { 01609 QStringList headerLabels = diagram()->itemRowLabels(); 01610 const int headerLabelsCount = headerLabels.count(); 01611 if( headerLabelsCount ){ 01612 if( cachedHeaderLabels == headerLabels && ( diagramIsVertical ? cachedFontHeight == fm.height() : cachedFontWidth == fm.averageCharWidth() )) { 01613 if ( diagramIsVertical ) 01614 h = cachedLabelHeight; 01615 else 01616 w = cachedLabelWidth; 01617 } else { 01618 cachedHeaderLabels = headerLabels; 01619 if ( diagramIsVertical ) 01620 cachedFontWidth = fm.averageCharWidth(); 01621 else 01622 cachedFontHeight = fm.height(); 01623 const bool useFastCalcAlgorithm 01624 = (strcmp( axis()->metaObject()->className(), "KDChart::CartesianAxis" ) == 0); 01625 const int first=0; 01626 const int last=headerLabelsCount-1; 01627 for ( int i = first; 01628 i <= last; 01629 i = (useFastCalcAlgorithm && i < last) ? last : (i+1) ) 01630 { 01631 labelItem.setText( axis()->customizedLabel(headerLabels[ i ]) ); 01632 const QSize siz = labelItem.sizeHint(); 01633 if ( diagramIsVertical ) { 01634 h = qMax( h, static_cast<qreal>(siz.height()) ); 01635 cachedLabelHeight = h; 01636 } else { 01637 cachedLabelWidth = w; 01638 w = qMax( w, static_cast<qreal>(siz.width()) ); 01639 } 01640 calculateOverlap( i, first, last, diagramIsVertical ? siz.width() : siz.height(), centerAbscissaTicks, 01641 leftOverlap, rightOverlap ); 01642 } 01643 } 01644 }else{ 01645 labelItem.setText( 01646 axis()->customizedLabel( 01647 QString::number( diagramIsVertical ? plane->gridDimensionsList().first().end 01648 : plane->gridDimensionsList().last().end, 'f', 0 ))); 01649 const QSize siz = labelItem.sizeHint(); 01650 if ( diagramIsVertical ) 01651 h = siz.height(); 01652 else 01653 w = siz.width(); 01654 calculateOverlap( 0, 0, 0, siz.width(), centerAbscissaTicks, 01655 leftOverlap, rightOverlap ); 01656 } 01657 } 01658 // we leave a little gap between axis labels and bottom (or top, resp.) side of axis 01659 h += labelGap; 01660 } 01661 // space for a possible title: 01662 if ( drawTitle ) { 01663 // we add the title height and leave a little gap between axis labels and axis title 01664 if ( diagramIsVertical ) { 01665 h += titleItem.sizeHint().height() + titleGap; 01666 w = titleItem.sizeHint().width() + 2.0; 01667 } else { 01668 h = titleItem.sizeHint().height() + 2.0; 01669 w += titleItem.sizeHint().width() + titleGap; 01670 } 01671 } 01672 // space for the ticks 01673 if ( diagramIsVertical ) 01674 h += qAbs( axis()->tickLength() ) * 3.0; 01675 else 01676 w += qAbs( axis()->tickLength() ) * 3.0; 01677 result = QSize ( static_cast<int>( w ), static_cast<int>( h ) ); 01678 01679 //qDebug()<<"calculated size of x axis:"<<result; 01680 01681 // If necessary adjust the widths 01682 // of the left (or right, resp.) side neighboring columns: 01683 amountOfLeftOverlap = leftOverlap; 01684 amountOfRightOverlap = rightOverlap; 01685 /* Unused code for a push-model: 01686 if( leftOverlap || rightOverlap ){ 01687 QTimer::singleShot(200, const_cast<CartesianAxis*>(this), 01688 SLOT(adjustLeftRightGridColumnWidths())); 01689 } 01690 */ 01691 } else { 01692 int topOverlap = 0; 01693 int bottomOverlap = 0; 01694 01695 qreal w = diagramIsVertical ? 0.0 : 10.0; 01696 qreal h = diagramIsVertical ? 10.0 : 0.0; 01697 if( drawLabels ){ 01698 // if there're no label strings, we loop through the values 01699 // taking the longest (not largest) number - e.g. 0.00001 is longer than 100 01700 if( !annotations.isEmpty() ) 01701 { 01702 const QStringList strings = annotations.values(); 01703 KDAB_FOREACH( const QString& string, strings ) 01704 { 01705 labelItem.setText( string ); 01706 const QSize siz = labelItem.sizeHint(); 01707 if ( diagramIsVertical ) 01708 w = qMax( w, static_cast< qreal >( siz.width() ) ); 01709 else 01710 h = qMax( h, static_cast< qreal >( siz.height() ) ); 01711 } 01712 } 01713 else if( axis()->labels().isEmpty() ) 01714 { 01715 const DataDimension dimY = AbstractGrid::adjustedLowerUpperRange( 01716 diagramIsVertical ? plane->gridDimensionsList().last() 01717 : plane->gridDimensionsList().first(), true, true ); 01718 const double step = dimY.stepWidth; 01719 const qreal minValue = dimY.start; 01720 const qreal maxValue = dimY.end; 01721 const bool isLogarithmicY = (dimY.calcMode == AbstractCoordinatePlane::Logarithmic ); 01722 qreal labelValue = minValue; 01723 01724 while( labelValue <= maxValue ) { 01725 const QString labelText = diagram()->unitPrefix( static_cast< int >( labelValue ), diagramOrientation, true ) + 01726 QString::number( labelValue ) + 01727 diagram()->unitSuffix( static_cast< int >( labelValue ), diagramOrientation, true ); 01728 labelItem.setText( axis()->customizedLabel( labelText ) ); 01729 01730 const QSize siz = labelItem.sizeHint(); 01731 if ( diagramIsVertical ) 01732 w = qMax( w, (qreal)siz.width() ); 01733 else 01734 h = qMax( h, (qreal)siz.height() ); 01735 calculateOverlap( 0, 0, 0, diagramIsVertical ? siz.height() : siz.width(), false,// bar diagram flag is ignored for Ordinates 01736 topOverlap, bottomOverlap ); 01737 calculateNextLabel( labelValue, step, isLogarithmicY, plane->gridDimensionsList().last().start ); 01738 01739 if(maxValue == 0 && minValue == 0) 01740 break; 01741 } 01742 }else{ 01743 // find the longest label text: 01744 const int first=0; 01745 const int last=axis()->labels().count()-1; 01746 const QStringList labelsList( axis()->labels() ); 01747 for ( int i = first; i <= last; ++i ) 01748 { 01749 labelItem.setText( axis()->customizedLabel(labelsList[ i ]) ); 01750 const QSize siz = labelItem.sizeHint(); 01751 if ( diagramIsVertical ) 01752 w = qMax( w, (qreal)siz.width() ); 01753 else 01754 h = qMax( h, (qreal)siz.height() ); 01755 calculateOverlap( 0, 0, 0, diagramIsVertical ? siz.height() : siz.width(), false,// bar diagram flag is ignored for Ordinates 01756 topOverlap, bottomOverlap ); 01757 } 01758 } 01759 // we leave a little gap between axis labels and left (or right, resp.) side of axis 01760 w += labelGap; 01761 } 01762 // space for a possible title: 01763 if ( drawTitle ) { 01764 // we add the title height and leave a little gap between axis labels and axis title 01765 if ( diagramIsVertical ) { 01766 w += titleItem.sizeHint().width() + titleGap; 01767 h = titleItem.sizeHint().height() + 2.0; 01768 } else { 01769 w = titleItem.sizeHint().width() + 2.0; 01770 h += titleItem.sizeHint().height() + titleGap; 01771 } 01772 //qDebug() << "left/right axis title item size-hint:" << titleItem.sizeHint(); 01773 } 01774 // space for the ticks 01775 if ( diagramIsVertical ) 01776 w += qAbs( axis()->tickLength() ) * 3.0; 01777 else 01778 h += qAbs( axis()->tickLength() ) * 3.0; 01779 01780 result = QSize ( static_cast<int>( w ), static_cast<int>( h ) ); 01781 //qDebug() << "left/right axis width:" << result << " w:" << w; 01782 01783 01784 // If necessary adjust the heights 01785 // of the top (or bottom, resp.) side neighboring rows: 01786 amountOfTopOverlap = topOverlap; 01787 amountOfBottomOverlap = bottomOverlap; 01788 /* Unused code for a push-model: 01789 if( topOverlap || bottomOverlap ){ 01790 QTimer::singleShot(200, const_cast<CartesianAxis*>(this), 01791 SLOT(adjustTopBottomGridRowHeights())); 01792 } 01793 */ 01794 } 01795 //qDebug() << "*******************" << result; 01796 //result=QSize(0,0); 01797 return result; 01798 } 01799 /* pure virtual in QLayoutItem */ 01800 QSize CartesianAxis::minimumSize() const 01801 { 01802 return maximumSize(); 01803 } 01804 /* pure virtual in QLayoutItem */ 01805 QSize CartesianAxis::sizeHint() const 01806 { 01807 return maximumSize(); 01808 } 01809 /* pure virtual in QLayoutItem */ 01810 void CartesianAxis::setGeometry( const QRect& r ) 01811 { 01812 // qDebug() << "KDChart::CartesianAxis::setGeometry(" << r << ") called" 01813 // << (isAbscissa() ? "for Abscissa":"for Ordinate") << "axis"; 01814 d->geometry = r; 01815 setCachedSizeDirty(); 01816 } 01817 /* pure virtual in QLayoutItem */ 01818 QRect CartesianAxis::geometry() const 01819 { 01820 return d->geometry; 01821 } 01822 01823 int CartesianAxis::tickLength( bool subUnitTicks ) const 01824 { 01825 int result = 0; 01826 01827 if ( isAbscissa() ) { 01828 result = position() == Top ? -4 : 3; 01829 } else { 01830 result = position() == Left ? -4 : 3; 01831 } 01832 01833 if ( subUnitTicks ) 01834 result = result < 0 ? result + 1 : result - 1; 01835 01836 return result; 01837 } 01838 01839 QMap< double, QString > CartesianAxis::annotations() const 01840 { 01841 return d->annotations; 01842 } 01843 01844 void CartesianAxis::setAnnotations( const QMap< double, QString >& annotations ) 01845 { 01846 if( d->annotations == annotations ) 01847 return; 01848 01849 d->annotations = annotations; 01850 update(); 01851 } 01852 01853 QList< double > CartesianAxis::customTicks() const 01854 { 01855 return d->customTicksPositions; 01856 } 01857 01858 void CartesianAxis::setCustomTicks( const QList< double >& customTicksPositions ) 01859 { 01860 if( d->customTicksPositions == customTicksPositions ) 01861 return; 01862 01863 d->customTicksPositions = customTicksPositions; 01864 update(); 01865 } 01866 01867 01868 /* unused code from KDChartCartesianAxis.h for using a push-model: 01869 Q_SIGNALS: 01870 void needAdjustLeftRightColumnsForOverlappingLabels( 01871 CartesianAxis* axis, int left, int right ); 01872 void needAdjustTopBottomRowsForOverlappingLabels( 01873 CartesianAxis* axis, int top, int bottom ); 01874 private Q_SLOTS: 01875 void adjustLeftRightGridColumnWidths(); 01876 void adjustTopBottomGridRowHeights(); 01877 */ 01878 01879 /* 01880 // Unused code trying to use a push-model: This did not work 01881 // since we can not re-layout the planes each time when 01882 // Qt layouting is calling sizeHint() 01883 void CartesianAxis::adjustLeftRightGridColumnWidths() 01884 { 01885 if( ! d->amountOfLeftOverlap && ! d->amountOfRightOverlap ) 01886 return; 01887 const int leftOverlap = d->amountOfLeftOverlap; 01888 const int rightOverlap= d->amountOfRightOverlap; 01889 d->amountOfLeftOverlap = 0; 01890 d->amountOfRightOverlap = 0; 01891 emit needAdjustLeftRightColumnsForOverlappingLabels( 01892 this, leftOverlap, rightOverlap ); 01893 } 01894 01895 void CartesianAxis::adjustTopBottomGridRowHeights() 01896 { 01897 if( ! d->amountOfTopOverlap && ! d->amountOfBottomOverlap ) 01898 return; 01899 const int topOverlap = d->amountOfTopOverlap; 01900 const int bottomOverlap= d->amountOfBottomOverlap; 01901 d->amountOfTopOverlap = 0; 01902 d->amountOfBottomOverlap = 0; 01903 emit needAdjustTopBottomRowsForOverlappingLabels( 01904 this, topOverlap, bottomOverlap ); 01905 } 01906 */