00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include <QPainter>
00027 #include <QDebug>
00028
00029 #include "KDChartBarDiagram.h"
00030 #include "KDChartBarDiagram_p.h"
00031 #include "KDChartThreeDBarAttributes.h"
00032 #include "KDChartPosition.h"
00033 #include "KDChartAttributesModel.h"
00034 #include "KDChartAbstractGrid.h"
00035
00036 #include <KDABLibFakes>
00037
00038
00039 using namespace KDChart;
00040
00041 BarDiagram::Private::Private()
00042 : barType( Normal )
00043 , maxDepth ( 0 )
00044 {
00045 }
00046
00047 BarDiagram::Private::~Private() {}
00048
00049 #define d d_func()
00050
00051
00052 BarDiagram::BarDiagram( QWidget* parent, CartesianCoordinatePlane* plane ) :
00053 AbstractCartesianDiagram( new Private(), parent, plane )
00054 {
00055 init();
00056 }
00057
00058 void BarDiagram::init()
00059 {
00060 }
00061
00062 BarDiagram::~BarDiagram()
00063 {
00064 }
00065
00066 BarDiagram * BarDiagram::clone() const
00067 {
00068 return new BarDiagram( new Private( *d ) );
00069 }
00070
00071 void BarDiagram::setType( const BarType type )
00072 {
00073 if ( type == d->barType ) return;
00074
00075 d->barType = type;
00076
00077 setPercentMode( type == BarDiagram::Percent );
00078 setDataBoundariesDirty();
00079 emit layoutChanged( this );
00080 }
00081
00082 BarDiagram::BarType BarDiagram::type() const
00083 {
00084 return d->barType;
00085 }
00086
00087 void BarDiagram::setBarAttributes( const BarAttributes & ta )
00088 {
00089 d->attributesModel->setModelData( qVariantFromValue( ta ), BarAttributesRole );
00090 }
00091
00092 void BarDiagram::setBarAttributes( int column, const BarAttributes & ta )
00093 {
00094 d->attributesModel->setHeaderData(
00095 column, Qt::Vertical,
00096 qVariantFromValue( ta ),
00097 BarAttributesRole );
00098 }
00099
00100 void BarDiagram::setBarAttributes( const QModelIndex & index, const BarAttributes & ta )
00101 {
00102 attributesModel()->setData(
00103 d->attributesModel->mapFromSource( index ),
00104 qVariantFromValue( ta ),
00105 BarAttributesRole );
00106 }
00107
00108 BarAttributes BarDiagram::barAttributes() const
00109 {
00110 return qVariantValue<BarAttributes>(
00111 d->attributesModel->data( KDChart::BarAttributesRole ) );
00112 }
00113
00114 BarAttributes BarDiagram::barAttributes( int column ) const
00115 {
00116 return qVariantValue<BarAttributes>(
00117 d->attributesModel->data(
00118 d->attributesModel->mapFromSource( columnToIndex( column ) ),
00119 KDChart::BarAttributesRole ) );
00120 }
00121
00122 BarAttributes BarDiagram::barAttributes( const QModelIndex & index ) const
00123 {
00124 return qVariantValue<BarAttributes>(
00125 d->attributesModel->data(
00126 d->attributesModel->mapFromSource( index ),
00127 KDChart::BarAttributesRole ) );
00128 }
00129
00130 void BarDiagram::setThreeDBarAttributes( const ThreeDBarAttributes & threeDAttrs )
00131 {
00132 setDataBoundariesDirty();
00133 d->attributesModel->setModelData( qVariantFromValue( threeDAttrs ), ThreeDBarAttributesRole );
00134 emit layoutChanged( this );
00135 }
00136
00137 void BarDiagram::setThreeDBarAttributes( int column, const ThreeDBarAttributes & threeDAttrs )
00138 {
00139 setDataBoundariesDirty();
00140 d->attributesModel->setHeaderData(
00141 column, Qt::Vertical,
00142 qVariantFromValue( threeDAttrs ),
00143 ThreeDBarAttributesRole );
00144 emit layoutChanged( this );
00145 }
00146
00147 void BarDiagram::setThreeDBarAttributes( const QModelIndex & index, const ThreeDBarAttributes & threeDAttrs )
00148 {
00149 setDataBoundariesDirty();
00150 d->attributesModel->setData(
00151 d->attributesModel->mapFromSource(index),
00152 qVariantFromValue( threeDAttrs ),
00153 ThreeDBarAttributesRole );
00154 emit layoutChanged( this );
00155 }
00156
00157 ThreeDBarAttributes BarDiagram::threeDBarAttributes() const
00158 {
00159 return qVariantValue<ThreeDBarAttributes>(
00160 d->attributesModel->data( KDChart::ThreeDBarAttributesRole ) );
00161 }
00162
00163 ThreeDBarAttributes BarDiagram::threeDBarAttributes( int column ) const
00164 {
00165 return qVariantValue<ThreeDBarAttributes>(
00166 d->attributesModel->data(
00167 d->attributesModel->mapFromSource( columnToIndex( column ) ),
00168 KDChart::ThreeDBarAttributesRole ) );
00169 }
00170
00171 ThreeDBarAttributes BarDiagram::threeDBarAttributes( const QModelIndex & index ) const
00172 {
00173 return qVariantValue<ThreeDBarAttributes>(
00174 d->attributesModel->data(
00175 d->attributesModel->mapFromSource(index),
00176 KDChart::ThreeDBarAttributesRole ) );
00177 }
00178
00179 double BarDiagram::threeDItemDepth( const QModelIndex & index ) const
00180 {
00181 return threeDBarAttributes( index ).validDepth();
00182 }
00183
00184 double BarDiagram::threeDItemDepth( int column ) const
00185 {
00186 return qVariantValue<ThreeDBarAttributes>(
00187 d->attributesModel->headerData (
00188 column,
00189 Qt::Vertical,
00190 KDChart::ThreeDBarAttributesRole ) ).validDepth();
00191 }
00192
00193 void BarDiagram::resizeEvent ( QResizeEvent*)
00194 {
00195
00196 }
00197
00198 const QPair<QPointF, QPointF> BarDiagram::calculateDataBoundaries() const
00199 {
00200 if ( !checkInvariants(true) ) return QPair<QPointF, QPointF>( QPointF( 0, 0 ), QPointF( 0, 0 ) );
00201 const int rowCount = d->attributesModel->rowCount(attributesModelRootIndex());
00202 const int colCount = d->attributesModel->columnCount(attributesModelRootIndex());
00203
00204 double xMin = 0;
00205 double xMax = rowCount;
00206 double yMin = 0, yMax = 0;
00207
00208
00209
00210
00211 switch ( type() ){
00212 case BarDiagram::Normal:
00213 {
00214 bool bStarting = true;
00215 for ( int i=0; i<colCount; ++i ) {
00216 for ( int j=0; j< rowCount; ++j ) {
00217 const double value = d->attributesModel->data( d->attributesModel->index( j, i, attributesModelRootIndex() ) ).toDouble();
00218
00219
00220
00221 if( bStarting ){
00222 yMin = value;
00223 yMax = value;
00224 bStarting = false;
00225 }else{
00226 yMin = qMin( yMin, value );
00227 yMax = qMax( yMax, value );
00228 }
00229 }
00230 }
00231 }
00232 break;
00233 case BarDiagram::Stacked:
00234 {
00235 bool bStarting = true;
00236 for ( int j=0; j< rowCount; ++j ) {
00237
00238 double stackedValues = 0;
00239 for ( int i=0; i<colCount ; ++i ) {
00240 QModelIndex idx = model()->index( j, i, rootIndex() );
00241 stackedValues += model()->data( idx ).toDouble();
00242
00243
00244
00245 if( bStarting ){
00246 yMin = stackedValues;
00247 yMax = stackedValues;
00248 bStarting = false;
00249 }else{
00250 yMin = qMin( yMin, stackedValues );
00251 yMax = qMax( yMax, stackedValues );
00252 }
00253 }
00254 }
00255 }
00256 break;
00257 case BarDiagram::Percent:
00258 {
00259 for ( int i=0; i<colCount; ++i ) {
00260 for ( int j=0; j< rowCount; ++j ) {
00261
00262 QModelIndex idx = model()->index( j, i, rootIndex() );
00263
00264 double value = model()->data( idx ).toDouble();
00265 if ( value > 0 )
00266 yMax = qMax( yMax, value );
00267 }
00268 }
00269 }
00270 break;
00271 case BarDiagram::Rows:
00272 {
00273 qDebug()<< "KDChartBarDiagram::calculateDataBoundaries"
00274 << "Sorry Type Rows not implemented yet";
00275 break;
00276 }
00277
00278
00279 default:
00280 Q_ASSERT_X ( false, "calculateDataBoundaries()",
00281 "Type item does not match a defined bar chart Type." );
00282 }
00283
00284
00285 if ( yMax == yMin ) {
00286 if ( yMin == 0.0 )
00287 yMax = 0.1;
00288 else
00289 yMax = 0.0;
00290 }
00291 QPointF bottomLeft ( QPointF( xMin, yMin ) );
00292 QPointF topRight ( QPointF( xMax, yMax ) );
00293
00294
00295 return QPair<QPointF, QPointF> ( bottomLeft, topRight );
00296 }
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310 void BarDiagram::calculateValueAndGapWidths( int rowCount,int colCount,
00311 double groupWidth,
00312 double& outBarWidth,
00313 double& outSpaceBetweenBars,
00314 double& outSpaceBetweenGroups )
00315 {
00316 Q_UNUSED( rowCount );
00317
00318 BarAttributes ba = barAttributes( model()->index( 0, 0, rootIndex() ) );
00319
00320
00321
00322
00323
00324
00325
00326
00327 const double units = colCount
00328 + (colCount-1) * ba.barGapFactor()
00329 + 1 * ba.groupGapFactor();
00330
00331 double unitWidth = groupWidth / units;
00332 outBarWidth = unitWidth;
00333 outSpaceBetweenBars += unitWidth * ba.barGapFactor();
00334
00335
00336 if ( outSpaceBetweenBars < 0 )
00337 outSpaceBetweenBars = 0;
00338 outSpaceBetweenGroups += unitWidth * ba.groupGapFactor();
00339 }
00340
00341 void BarDiagram::paint( PaintContext* ctx )
00342 {
00343
00344
00345 if ( !checkInvariants(true) )
00346 return;
00347
00348
00349 QPointF boundLeft, boundRight;
00350 QPair<QPointF,QPointF> boundaries = dataBoundaries();
00351 if( !AbstractGrid::isBoundariesValid(boundaries) ) return;
00352
00353 CartesianCoordinatePlane* plane = dynamic_cast<KDChart::CartesianCoordinatePlane*>( coordinatePlane() );
00354 if( ! plane ) return;
00355
00356 boundLeft = plane->translate( boundaries.first );
00357 boundRight = plane->translate( boundaries.second );
00358 double width = boundRight.x() - boundLeft.x();
00359
00360 const int rowCount = d->attributesModel->rowCount(attributesModelRootIndex());
00361 const int colCount = d->attributesModel->columnCount(attributesModelRootIndex());
00362 DataValueTextInfoList list;
00363 BarAttributes ba = barAttributes( model()->index( 0, 0, rootIndex() ) );
00364 double barWidth = 0;
00365 double maxDepth = 0;
00366 double spaceBetweenBars = 0;
00367 double spaceBetweenGroups = 0;
00368 double groupWidth = width/ (rowCount + 2);
00369
00370
00371 if ( ba.useFixedBarWidth() ) {
00372 barWidth = ba.fixedBarWidth();
00373 groupWidth += barWidth;
00374
00375
00376
00377 if ( groupWidth < 0 )
00378 groupWidth = 0;
00379
00380 if ( groupWidth * rowCount > ctx->rectangle().width() )
00381 groupWidth = ctx->rectangle().width() / rowCount;
00382 }
00383
00384
00385 double maxLimit = rowCount * (groupWidth + ((colCount-1) * ba.fixedDataValueGap()) );
00386
00387
00388 if ( ba.useFixedDataValueGap() ) {
00389 if ( ctx->rectangle().width() > maxLimit )
00390 spaceBetweenBars += ba.fixedDataValueGap();
00391 else
00392 spaceBetweenBars = ((ctx->rectangle().width()/rowCount) - groupWidth)/(colCount-1);
00393 }
00394
00395
00396 if ( ba.useFixedValueBlockGap() )
00397 spaceBetweenGroups += ba.fixedValueBlockGap();
00398
00399 calculateValueAndGapWidths( rowCount, colCount,groupWidth,
00400 barWidth, spaceBetweenBars, spaceBetweenGroups );
00401
00402
00403 switch ( type() )
00404 {
00405 case BarDiagram::Normal:
00406 {
00407
00408 for ( int i=0; i<rowCount; ++i ) {
00409 double offset = -groupWidth/2 + spaceBetweenGroups/2;
00410
00411 if ( ba.useFixedDataValueGap() ) {
00412 if ( spaceBetweenBars > 0 ) {
00413 if ( ctx->rectangle().width() > maxLimit )
00414 offset -= ba.fixedDataValueGap();
00415 else
00416 offset -= ((ctx->rectangle().width()/rowCount) - groupWidth)/(colCount-1);
00417
00418 } else {
00419
00420 offset += barWidth/2;
00421 }
00422 }
00423
00424 for ( int j=0; j< colCount; ++j ) {
00425
00426 const qreal value = d->attributesModel->data( d->attributesModel->index( i, j, attributesModelRootIndex() ) ).toDouble();
00427 QPointF topPoint = plane->translate( QPointF( i + 0.5, value ) );
00428 QPointF bottomPoint = plane->translate( QPointF( i, 0 ) );
00429 const double barHeight = bottomPoint.y() - topPoint.y();
00430 topPoint.setX( topPoint.x() + offset );
00431
00432 const QModelIndex index = model()->index( i, j, rootIndex() );
00433
00434
00435 const QRectF rect( topPoint, QSizeF( barWidth, barHeight ) );
00436 d->appendDataValueTextInfoToList( this, list, index, PositionPoints( rect ),
00437 Position::NorthWest, Position::SouthEast,
00438 value );
00439 paintBars( ctx, index, rect, maxDepth );
00440
00441 offset += barWidth + spaceBetweenBars;
00442 }
00443 }
00444 }
00445 break;
00446 case BarDiagram::Stacked:
00447 {
00448 for ( int i = 0; i<colCount; ++i ) {
00449 double offset = spaceBetweenGroups;
00450 for ( int j = 0; j< rowCount; ++j ) {
00451 QModelIndex index = model()->index( j, i, rootIndex() );
00452 ThreeDBarAttributes threeDAttrs = threeDBarAttributes( index );
00453 double value = 0, stackedValues = 0;
00454 QPointF point, previousPoint;
00455
00456 if ( threeDAttrs.isEnabled() ) {
00457 if ( barWidth > 0 )
00458 barWidth = (width - ((offset+(threeDAttrs.depth()))*rowCount))/ rowCount;
00459 if ( barWidth <= 0 ) {
00460 barWidth = 0;
00461 maxDepth = offset - (width/rowCount);
00462 }
00463 } else
00464 barWidth = (ctx->rectangle().width() - (offset*rowCount))/ rowCount ;
00465
00466 value = model()->data( index ).toDouble();
00467 for ( int k = i; k >= 0 ; --k )
00468 stackedValues += model()->data( model()->index( j, k, rootIndex() ) ).toDouble();
00469 point = plane->translate( QPointF( j, stackedValues ) );
00470 point.setX( point.x() + offset/2 );
00471 previousPoint = plane->translate( QPointF( j, stackedValues - value ) );
00472 const double barHeight = previousPoint.y() - point.y();
00473
00474 const QRectF rect( point, QSizeF( barWidth , barHeight ) );
00475 d->appendDataValueTextInfoToList( this, list, index, PositionPoints( rect ),
00476 Position::NorthWest, Position::SouthEast,
00477 value );
00478 paintBars( ctx, index, rect, maxDepth );
00479 }
00480
00481 }
00482 }
00483 break;
00484 case BarDiagram::Percent:
00485 {
00486 double maxValue = 100;
00487 double sumValues = 0;
00488 QVector <double > sumValuesVector;
00489
00490
00491 for ( int j=0; j<rowCount; ++j ) {
00492 for ( int i=0; i<colCount; ++i ) {
00493 double tmpValue = model()->data( model()->index( j, i, rootIndex() ) ).toDouble();
00494 if ( tmpValue > 0 )
00495 sumValues += tmpValue;
00496 if ( i == colCount-1 ) {
00497 sumValuesVector << sumValues ;
00498 sumValues = 0;
00499 }
00500 }
00501 }
00502
00503
00504 for ( int i = 0; i<colCount; ++i ) {
00505 double offset = spaceBetweenGroups;
00506 for ( int j=0; j<rowCount ; ++j ) {
00507 double value = 0, stackedValues = 0;
00508 QPointF point, previousPoint;
00509 QModelIndex index = model()->index( j, i, rootIndex() );
00510 ThreeDBarAttributes threeDAttrs = threeDBarAttributes( index );
00511
00512 if ( threeDAttrs.isEnabled() ){
00513 if ( barWidth > 0 )
00514 barWidth = (width - ((offset+(threeDAttrs.depth()))*rowCount))/ rowCount;
00515 if ( barWidth <= 0 ) {
00516 barWidth = 0;
00517 maxDepth = offset - ( width/rowCount);
00518 }
00519 }else{
00520 barWidth = (ctx->rectangle().width() - (offset*rowCount))/ rowCount;
00521 }
00522
00523 value = model()->data( index ).toDouble();
00524
00525
00526
00527 for ( int k = i; k >= 0 ; --k ) {
00528 double val = model()->data( model()->index( j, k, rootIndex() ) ).toDouble();
00529 if ( val > 0)
00530 stackedValues += val;
00531 }
00532
00533 if ( sumValuesVector.at( j ) != 0 && value > 0 ) {
00534 point = plane->translate( QPointF( j, stackedValues/sumValuesVector.at(j)* maxValue ) );
00535
00536 point.setX( point.x() + offset/2 );
00537
00538 previousPoint = plane->translate( QPointF( j, (stackedValues - value)/sumValuesVector.at(j)* maxValue ) );
00539 }
00540 const double barHeight = previousPoint.y() - point.y();
00541
00542 const QRectF rect( point, QSizeF( barWidth, barHeight ) );
00543 d->appendDataValueTextInfoToList( this, list, index, PositionPoints( rect ),
00544 Position::NorthWest, Position::SouthEast,
00545 value );
00546 paintBars( ctx, index, rect, maxDepth );
00547
00548 }
00549 }
00550 }
00551 break;
00552 default:
00553 Q_ASSERT_X ( false, "paint()",
00554 "Type item does not match a defined bar chart Type." );
00555 }
00556
00557
00558 d->paintDataValueTextsAndMarkers( this, ctx, list, false );
00559 }
00560
00561 void BarDiagram::paintBars( PaintContext* ctx, const QModelIndex& index, const QRectF& bar, double& maxDepth )
00562 {
00563 QRectF isoRect;
00564 QPolygonF topPoints, sidePoints;
00565 ThreeDBarAttributes threeDAttrs = threeDBarAttributes( index );
00566 double usedDepth;
00567
00568
00569 QBrush indexBrush ( brush( index ) );
00570 QPen indexPen( pen( index ) );
00571 PainterSaver painterSaver( ctx->painter() );
00572 if ( antiAliasing() )
00573 ctx->painter()->setRenderHint ( QPainter::Antialiasing );
00574 ctx->painter()->setBrush( indexBrush );
00575 ctx->painter()->setPen( indexPen );
00576 if ( threeDAttrs.isEnabled() ) {
00577 bool stackedMode = false;
00578 bool percentMode = false;
00579 bool paintTop = true;
00580 if ( maxDepth )
00581 threeDAttrs.setDepth( -maxDepth );
00582 QPointF boundRight = coordinatePlane()->translate( dataBoundaries().second );
00583
00584 switch ( type() )
00585 {
00586 case BarDiagram::Normal:
00587 usedDepth = threeDAttrs.depth()/4;
00588 stackedMode = false;
00589 percentMode = false;
00590 break;
00591 case BarDiagram::Stacked:
00592 usedDepth = threeDAttrs.depth();
00593 stackedMode = true;
00594 percentMode = false;
00595 break;
00596 case BarDiagram::Percent:
00597 usedDepth = threeDAttrs.depth();
00598 stackedMode = false;
00599 percentMode = true;
00600 break;
00601 default:
00602 Q_ASSERT_X ( false, "dataBoundaries()",
00603 "Type item does not match a defined bar chart Type." );
00604 }
00605 isoRect = bar.translated( usedDepth, -usedDepth );
00606
00607
00608
00609 if ( isoRect.height() < 0 ) {
00610 topPoints << isoRect.bottomLeft() << isoRect.bottomRight()
00611 << bar.bottomRight() << bar.bottomLeft();
00612 if ( stackedMode ) {
00613
00614 if ( index.column() == 0 ) {
00615 paintTop = true;
00616 }
00617 else
00618 paintTop = false;
00619 }
00620
00621 } else {
00622 ctx->painter()->drawRect( isoRect );
00623 topPoints << bar.topLeft() << bar.topRight() << isoRect.topRight() << isoRect.topLeft();
00624 }
00625
00626 if ( percentMode && isoRect.height() == 0 )
00627 paintTop = false;
00628
00629 bool needToSetClippingOffForTop = false;
00630 if ( paintTop ){
00631
00632
00633 bool drawIt = false;
00634 bool hasPointOutside = false;
00635 const QRectF r( ctx->rectangle().adjusted(0,-1,1,0) );
00636 KDAB_FOREACH( QPointF pt, topPoints ) {
00637 if( r.contains( pt ) )
00638 drawIt = true;
00639 else
00640 hasPointOutside = true;
00641 }
00642 if( drawIt ){
00643 needToSetClippingOffForTop = hasPointOutside && ctx->painter()->hasClipping();
00644 if( needToSetClippingOffForTop )
00645 ctx->painter()->setClipping( false );
00646 ctx->painter()->drawPolygon( topPoints );
00647 if( needToSetClippingOffForTop )
00648 ctx->painter()->setClipping( true );
00649 }
00650 }
00651
00652
00653
00654 sidePoints << bar.topRight() << isoRect.topRight() << isoRect.bottomRight() << bar.bottomRight();
00655 if ( bar.height() != 0 ){
00656 if( needToSetClippingOffForTop )
00657 ctx->painter()->setClipping( false );
00658 ctx->painter()->drawPolygon( sidePoints );
00659 if( needToSetClippingOffForTop )
00660 ctx->painter()->setClipping( true );
00661 }
00662 }
00663
00664 if ( bar.height() != 0 )
00665 ctx->painter()->drawRect( bar );
00666
00667 d->maxDepth = threeDAttrs.depth();
00668 }
00669
00670 void BarDiagram::resize ( const QSizeF& )
00671 {
00672 }
00673
00674 const int BarDiagram::numberOfAbscissaSegments () const
00675 {
00676 return d->attributesModel->rowCount(attributesModelRootIndex());
00677 }
00678
00679 const int BarDiagram::numberOfOrdinateSegments () const
00680 {
00681 return d->attributesModel->columnCount(attributesModelRootIndex());
00682 }
00683
00684 #undef d