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 <QList>
00027 #include <QtDebug>
00028 #include <QGridLayout>
00029 #include <QLabel>
00030 #include <QHash>
00031
00032 #include <QPainter>
00033 #include <QPaintEvent>
00034 #include <QLayoutItem>
00035 #include <QPushButton>
00036 #include <QApplication>
00037 #include <QEvent>
00038
00039 #include "KDChartChart.h"
00040 #include "KDChartChart_p.h"
00041 #include "KDChartCartesianCoordinatePlane.h"
00042 #include "KDChartAbstractCartesianDiagram.h"
00043 #include "KDChartHeaderFooter.h"
00044 #include "KDChartEnums.h"
00045 #include "KDChartLegend.h"
00046 #include "KDChartLayoutItems.h"
00047 #include <KDChartTextAttributes.h>
00048 #include <KDChartMarkerAttributes>
00049 #include "KDChartPainterSaver_p.h"
00050
00051 #if defined KDAB_EVAL
00052 #include "../evaldialog/evaldialog.h"
00053 #endif
00054
00055 #include <KDABLibFakes>
00056
00057 #define SET_ALL_MARGINS_TO_ZERO
00058
00059
00060 class MyWidgetItem : public QWidgetItem
00061 {
00062 public:
00063 explicit MyWidgetItem(QWidget *w, Qt::Alignment alignment = 0)
00064 : QWidgetItem(w) {
00065 setAlignment( alignment );
00066 }
00067 bool isEmpty() const {
00068 QWidget* w = const_cast<MyWidgetItem *>(this)->widget();
00069
00070
00071
00072 return w->isHidden() && w->testAttribute(Qt::WA_WState_ExplicitShowHide);
00073 }
00074 };
00075
00076 using namespace KDChart;
00077
00078 void Chart::Private::slotUnregisterDestroyedLegend( Legend *l )
00079 {
00080 legends.removeAll( l );
00081 slotRelayout();
00082 }
00083
00084 void Chart::Private::slotUnregisterDestroyedHeaderFooter( HeaderFooter* hf )
00085 {
00086 headerFooters.removeAll( hf );
00087 hf->removeFromParentLayout();
00088 textLayoutItems.remove( textLayoutItems.indexOf( hf ) );
00089 slotRelayout();
00090 }
00091
00092 void Chart::Private::slotUnregisterDestroyedPlane( AbstractCoordinatePlane* plane )
00093 {
00094 coordinatePlanes.removeAll( plane );
00095 Q_FOREACH ( AbstractCoordinatePlane* p, coordinatePlanes )
00096 {
00097 if ( p->referenceCoordinatePlane() == plane) {
00098 p->setReferenceCoordinatePlane(0);
00099 }
00100 }
00101 plane->layoutPlanes();
00102 }
00103
00104 Chart::Private::Private( Chart* chart_ )
00105 : chart( chart_ )
00106 , layout( 0 )
00107 , vLayout( 0 )
00108 , planesLayout( 0 )
00109 , headerLayout( 0 )
00110 , footerLayout( 0 )
00111 , dataAndLegendLayout( 0 )
00112 , globalLeadingLeft( 0 )
00113 , globalLeadingRight( 0 )
00114 , globalLeadingTop( 0 )
00115 , globalLeadingBottom( 0 )
00116 {
00117 for( int row = 0; row < 3; ++row )
00118 {
00119 for( int column = 0; column < 3; ++column )
00120 {
00121 dummyHeaders[ row ][ column ] = HorizontalLineLayoutItem();
00122 dummyFooters[ row ][ column ] = HorizontalLineLayoutItem();
00123 }
00124 }
00125 }
00126
00127 Chart::Private::~Private()
00128 {
00129 removeDummyHeaderFooters();
00130 }
00131
00132 void Chart::Private::removeDummyHeaderFooters()
00133 {
00134 for ( int row = 0; row < 3; ++row )
00135 {
00136 for ( int column = 0; column < 3; ++ column )
00137 {
00138 if( headerLayout != 0 )
00139 headerLayout->removeItem( &(dummyHeaders[ row ][ column ]) );
00140 if( footerLayout != 0 )
00141 footerLayout->removeItem( &(dummyFooters[ row ][ column ]) );
00142 }
00143 }
00144 }
00145
00146 void Chart::Private::layoutHeadersAndFooters()
00147 {
00148 removeDummyHeaderFooters();
00149
00150 bool headersLineFilled[] = { false, false, false };
00151 bool footersLineFilled[] = { false, false, false };
00152
00153 Q_FOREACH( HeaderFooter *hf, headerFooters ) {
00154
00155
00156 QGridLayout * headerFooterLayout;
00157 switch( hf->type() ){
00158 case HeaderFooter::Header:
00159 headerFooterLayout = headerLayout;
00160 break;
00161 case HeaderFooter::Footer:
00162 headerFooterLayout = footerLayout;
00163 break;
00164 default:
00165 Q_ASSERT( false );
00166 break;
00167 };
00168
00169 if( hf->position() != Position::Unknown ) {
00170 int row, column;
00171 Qt::Alignment hAlign, vAlign;
00172 if( hf->position().isNorthSide() ){
00173 row = 0;
00174 vAlign = Qt::AlignTop;
00175 }
00176 else if( hf->position().isSouthSide() ){
00177 row = 2;
00178 vAlign = Qt::AlignBottom;
00179 }
00180 else{
00181 row = 1;
00182 vAlign = Qt::AlignVCenter;
00183 }
00184 if( hf->position().isWestSide() ){
00185 column = 0;
00186 hAlign = Qt::AlignLeft;
00187 }
00188 else if( hf->position().isEastSide() ){
00189 column = 2;
00190 hAlign = Qt::AlignRight;
00191 }
00192 else{
00193 column = 1;
00194 hAlign = Qt::AlignHCenter;
00195 }
00196 switch( hf->type() ){
00197 case HeaderFooter::Header:
00198 if( !headersLineFilled[ row ] )
00199 {
00200 for( int col = 0; col < 3; ++col )
00201 headerFooterLayout->addItem( &(dummyHeaders[ row ][ col ]), row, col );
00202 headersLineFilled[ row ] = true;
00203 }
00204 break;
00205 case HeaderFooter::Footer:
00206 if( !footersLineFilled[ row ] )
00207 {
00208 for( int col = 0; col < 3; ++col )
00209 headerFooterLayout->addItem( &(dummyFooters[ row ][ col ]), row, col );
00210 footersLineFilled[ row ] = true;
00211 }
00212 break;
00213 };
00214 textLayoutItems << hf;
00215 hf->setParentLayout( headerFooterLayout );
00216 headerFooterLayout->addItem( hf, row, column, 1, 1, hAlign | vAlign );
00217 }
00218 else{
00219 qDebug( "Unknown header/footer position" );
00220 }
00221 }
00222 }
00223
00224
00225 void Chart::Private::layoutLegends()
00226 {
00227
00228
00229
00230 QList<Legend*> infos[3][3];
00231
00232 Q_FOREACH( Legend *legend, legends ) {
00233
00234 legend->needSizeHint();
00235
00236 bool bOK = true;
00237 int row, column;
00238 switch( legend->position().value() ) {
00239 case KDChartEnums::PositionNorthWest: row = 0; column = 0;
00240 break;
00241 case KDChartEnums::PositionNorth: row = 0; column = 1;
00242 break;
00243 case KDChartEnums::PositionNorthEast: row = 0; column = 2;
00244 break;
00245 case KDChartEnums::PositionEast: row = 1; column = 2;
00246 break;
00247 case KDChartEnums::PositionSouthEast: row = 2; column = 2;
00248 break;
00249 case KDChartEnums::PositionSouth: row = 2; column = 1;
00250 break;
00251 case KDChartEnums::PositionSouthWest: row = 2; column = 0;
00252 break;
00253 case KDChartEnums::PositionWest: row = 1; column = 0;
00254 break;
00255 case KDChartEnums::PositionCenter:
00256 qDebug( "Sorry: Legend not shown, because position Center is not supported." );
00257 bOK = false;
00258 break;
00259 case KDChartEnums::PositionFloating:
00260 bOK = false;
00261 break;
00262 default:
00263 qDebug( "Sorry: Legend not shown, because of unknown legend position." );
00264 bOK = false;
00265 break;
00266 }
00267 if( bOK )
00268 infos[row][column] << legend;
00269 }
00270
00271
00272 for (int iR = 0; iR < 3; ++iR) {
00273 for (int iC = 0; iC < 3; ++iC) {
00274 QList<Legend*>& list = infos[iR][iC];
00275 const int count = list.size();
00276 switch( count ){
00277 case 0:
00278 break;
00279 case 1: {
00280 Legend* legend = list.first();
00281 dataAndLegendLayout->addItem( new MyWidgetItem(legend),
00282 iR, iC, 1, 1, legend->alignment() );
00283 }
00284 break;
00285 default: {
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297 Legend* legend = list.first();
00298 Qt::Alignment alignment = legend->alignment();
00299 bool haveSameAlign = true;
00300 for (int i = 1; i < count; ++i) {
00301 legend = list.at(i);
00302 if( alignment != legend->alignment() ){
00303 haveSameAlign = false;
00304 break;
00305 }
00306 }
00307 if( haveSameAlign ){
00308 QVBoxLayout* vLayout = new QVBoxLayout();
00309 for (int i = 0; i < count; ++i) {
00310 vLayout->addItem( new MyWidgetItem(list.at(i), Qt::AlignLeft) );
00311 }
00312 dataAndLegendLayout->addLayout( vLayout, iR, iC, 1, 1, alignment );
00313 }else{
00314 QGridLayout* gridLayout = new QGridLayout();
00315 #define ADD_VBOX_WITH_LEGENDS(row, column, align) \
00316 { \
00317 QVBoxLayout* innerLayout = new QVBoxLayout(); \
00318 for (int i = 0; i < count; ++i) { \
00319 legend = list.at(i); \
00320 if( legend->alignment() == ( align ) ) \
00321 innerLayout->addItem( new MyWidgetItem(legend, Qt::AlignLeft) ); \
00322 } \
00323 gridLayout->addLayout( innerLayout, row, column, ( align ) ); \
00324 }
00325 ADD_VBOX_WITH_LEGENDS( 0, 0, Qt::AlignTop | Qt::AlignLeft )
00326 ADD_VBOX_WITH_LEGENDS( 0, 1, Qt::AlignTop | Qt::AlignHCenter )
00327 ADD_VBOX_WITH_LEGENDS( 0, 2, Qt::AlignTop | Qt::AlignRight )
00328
00329 ADD_VBOX_WITH_LEGENDS( 1, 0, Qt::AlignVCenter | Qt::AlignLeft )
00330 ADD_VBOX_WITH_LEGENDS( 1, 2, Qt::AlignVCenter | Qt::AlignRight )
00331
00332 ADD_VBOX_WITH_LEGENDS( 2, 0, Qt::AlignBottom | Qt::AlignLeft )
00333 ADD_VBOX_WITH_LEGENDS( 2, 1, Qt::AlignBottom | Qt::AlignHCenter )
00334 ADD_VBOX_WITH_LEGENDS( 2, 2, Qt::AlignBottom | Qt::AlignRight )
00335
00336 dataAndLegendLayout->addLayout( gridLayout, iR, iC, 1, 1 );
00337 }
00338 }
00339 }
00340 }
00341 }
00342 }
00343
00344
00345 QHash<AbstractCoordinatePlane*, PlaneInfo> Chart::Private::buildPlaneLayoutInfos()
00346 {
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359 QHash<CartesianAxis*, AxisInfo> axisInfos;
00360 QHash<AbstractCoordinatePlane*, PlaneInfo> planeInfos;
00361 Q_FOREACH(AbstractCoordinatePlane* plane, coordinatePlanes )
00362 {
00363 PlaneInfo p;
00364
00365 p.referencePlane = plane->referenceCoordinatePlane();
00366 planeInfos.insert( plane, p );
00367
00368 Q_FOREACH( AbstractDiagram* abstractDiagram, plane->diagrams() ) {
00369 AbstractCartesianDiagram* diagram =
00370 dynamic_cast<AbstractCartesianDiagram*> ( abstractDiagram );
00371 if( !diagram ) continue;
00372
00373 Q_FOREACH( CartesianAxis* axis, diagram->axes() ) {
00374 if ( !axisInfos.contains( axis ) ) {
00375
00376
00377
00378
00379 AxisInfo i;
00380 i.plane = plane;
00381 axisInfos.insert( axis, i );
00382 } else {
00383 AxisInfo i = axisInfos[axis];
00384 if ( i.plane == plane ) continue;
00385
00386
00387
00388
00389 PlaneInfo pi = planeInfos[plane];
00390
00391 if ( !pi.referencePlane ) {
00392
00393 pi.referencePlane = i.plane;
00394 if ( axis->position() == CartesianAxis::Left
00395 || axis->position() == CartesianAxis::Right )
00396 pi.horizontalOffset += 1;
00397 planeInfos[plane] = pi;
00398
00399 pi = planeInfos[i.plane];
00400 if ( axis->position() == CartesianAxis::Top
00401 || axis->position() == CartesianAxis::Bottom )
00402 pi.verticalOffset += 1;
00403
00404 planeInfos[i.plane] = pi;
00405 }
00406 }
00407 }
00408 }
00409
00410 p = planeInfos[plane];
00411 if ( p.referencePlane == 0 ) {
00412 p.gridLayout = new QGridLayout();
00413
00414 #if defined SET_ALL_MARGINS_TO_ZERO
00415 p.gridLayout->setMargin(0);
00416 #endif
00417 planeInfos[plane] = p;
00418 }
00419 }
00420 return planeInfos;
00421 }
00422
00423 template <typename T>
00424 static T* findOrCreateLayoutByObjectName( QLayout * parentLayout, const char* name )
00425 {
00426 T *box = qFindChild<T*>( parentLayout, QString::fromLatin1( name ) );
00427 if ( !box ) {
00428 box = new T();
00429
00430 #if defined SET_ALL_MARGINS_TO_ZERO
00431 box->setMargin(0);
00432 #endif
00433 box->setObjectName( QString::fromLatin1( name ) );
00434 box->setSizeConstraint( QLayout::SetFixedSize );
00435 }
00436 return box;
00437 }
00438
00439 static QVBoxLayout* findOrCreateVBoxLayoutByObjectName( QLayout* parentLayout, const char* name )
00440 {
00441 return findOrCreateLayoutByObjectName<QVBoxLayout>( parentLayout, name );
00442 }
00443
00444 static QHBoxLayout* findOrCreateHBoxLayoutByObjectName( QLayout* parentLayout, const char* name )
00445 {
00446 return findOrCreateLayoutByObjectName<QHBoxLayout>( parentLayout, name );
00447 }
00448
00449 void Chart::Private::slotLayoutPlanes()
00450 {
00451
00452 const QBoxLayout::Direction oldPlanesDirection =
00453 planesLayout ? planesLayout->direction() : QBoxLayout::TopToBottom;
00454 if ( planesLayout && dataAndLegendLayout )
00455 dataAndLegendLayout->removeItem( planesLayout );
00456
00457 KDAB_FOREACH( KDChart::AbstractLayoutItem* plane, planeLayoutItems ) {
00458 plane->removeFromParentLayout();
00459 }
00460 planeLayoutItems.clear();
00461 delete planesLayout;
00462
00463
00464 planesLayout = new QBoxLayout( oldPlanesDirection );
00465
00466
00467 #if defined SET_ALL_MARGINS_TO_ZERO
00468 planesLayout->setMargin(0);
00469 #endif
00470 planesLayout->setObjectName( QString::fromLatin1( "planesLayout" ) );
00471
00472
00473
00474
00475 QHash<AbstractCoordinatePlane*, PlaneInfo> planeInfos = buildPlaneLayoutInfos();
00476 QHash<AbstractAxis*, AxisInfo> axisInfos;
00477 KDAB_FOREACH( AbstractCoordinatePlane* plane, coordinatePlanes ) {
00478 Q_ASSERT( planeInfos.contains(plane) );
00479 const PlaneInfo pi = planeInfos[plane];
00480 int column = pi.horizontalOffset;
00481 int row = pi.verticalOffset;
00482
00483 QGridLayout *planeLayout = pi.gridLayout;
00484 if ( !planeLayout ) {
00485
00486
00487 planeLayout = planeInfos[pi.referencePlane].gridLayout;
00488 } else {
00489 planesLayout->addLayout( planeLayout );
00490 }
00491 Q_ASSERT( planeLayout );
00492
00493
00494
00495 planeLayoutItems << plane;
00496 plane->setParentLayout( planeLayout );
00497 planeLayout->addItem( plane, row, column, 1, 1, 0 );
00498
00499 planeLayout->setRowStretch( row, 2 );
00500 planeLayout->setColumnStretch( column, 2 );
00501
00502
00503
00504
00505 QVBoxLayout *topAxesLayout = 0;
00506 QVBoxLayout *bottomAxesLayout = 0;
00507 QHBoxLayout *leftAxesLayout = 0;
00508 QHBoxLayout *rightAxesLayout = 0;
00509
00510 KDAB_FOREACH( AbstractDiagram* abstractDiagram, plane->diagrams() )
00511 {
00512 AbstractCartesianDiagram* diagram =
00513 dynamic_cast<AbstractCartesianDiagram*> ( abstractDiagram );
00514
00515 if( !diagram ) continue;
00516
00517
00518 if( ! topAxesLayout )
00519 topAxesLayout = findOrCreateVBoxLayoutByObjectName( planeLayout, "topAxesLayout" );
00520 if( ! bottomAxesLayout )
00521 bottomAxesLayout = findOrCreateVBoxLayoutByObjectName( planeLayout, "bottomAxesLayout" );
00522 if( ! leftAxesLayout )
00523 leftAxesLayout = findOrCreateHBoxLayoutByObjectName( planeLayout, "leftAxesLayout" );
00524 if( ! rightAxesLayout )
00525 rightAxesLayout = findOrCreateHBoxLayoutByObjectName( planeLayout, "rightAxesLayout" );
00526
00527
00528
00529 KDAB_FOREACH( CartesianAxis* axis, diagram->axes() ) {
00530 if ( axisInfos.contains( axis ) ) continue;
00531 Q_ASSERT ( axis );
00532
00533 planeLayoutItems << axis;
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547 switch ( axis->position() )
00548 {
00549 case CartesianAxis::Top:
00550 axis->setParentLayout( topAxesLayout );
00551 topAxesLayout->addItem( axis );
00552 break;
00553 case CartesianAxis::Bottom:
00554 axis->setParentLayout( bottomAxesLayout );
00555 bottomAxesLayout->addItem( axis );
00556 break;
00557 case CartesianAxis::Left:
00558 axis->setParentLayout( leftAxesLayout );
00559 leftAxesLayout->addItem( axis );
00560 break;
00561 case CartesianAxis::Right:
00562 axis->setParentLayout( rightAxesLayout );
00563 rightAxesLayout->addItem( axis );
00564 break;
00565 default:
00566 Q_ASSERT_X( false, "Chart::paintEvent",
00567 "unknown axis position" );
00568 break;
00569 };
00570 axisInfos.insert( axis, AxisInfo() );
00571 }
00572
00573
00574
00575
00576 if ( !topAxesLayout->parent() )
00577 planeLayout->addLayout( topAxesLayout, row - 1, column );
00578 if ( !bottomAxesLayout->parent() )
00579 planeLayout->addLayout( bottomAxesLayout, row + 1, column );
00580 if ( !leftAxesLayout->parent() ){
00581 planeLayout->addLayout( leftAxesLayout, row, column - 1);
00582
00583
00584 }
00585 if ( !rightAxesLayout->parent() )
00586 planeLayout->addLayout( rightAxesLayout, row, column + 1);
00587 }
00588
00589
00590 #define ADD_AUTO_SPACER_IF_NEEDED( \
00591 spacerRow, spacerColumn, hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ) \
00592 { \
00593 if( hLayout || vLayout ) { \
00594 AutoSpacerLayoutItem * spacer \
00595 = new AutoSpacerLayoutItem( hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ); \
00596 planeLayout->addItem( spacer, spacerRow, spacerColumn, 1, 1 ); \
00597 spacer->setParentLayout( planeLayout ); \
00598 planeLayoutItems << spacer; \
00599 } \
00600 }
00601 ADD_AUTO_SPACER_IF_NEEDED( row-1, column-1, false, leftAxesLayout, false, topAxesLayout )
00602 ADD_AUTO_SPACER_IF_NEEDED( row+1, column-1, true, leftAxesLayout, false, bottomAxesLayout )
00603 ADD_AUTO_SPACER_IF_NEEDED( row-1, column+1, false, rightAxesLayout, true, topAxesLayout )
00604 ADD_AUTO_SPACER_IF_NEEDED( row+1, column+1, true, rightAxesLayout, true, bottomAxesLayout )
00605 }
00606
00607 if ( dataAndLegendLayout ){
00608 dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
00609 dataAndLegendLayout->setRowStretch( 1, 1000 );
00610 dataAndLegendLayout->setColumnStretch( 1, 1000 );
00611 }
00612
00613 slotRelayout();
00614
00615 }
00616
00617 void Chart::Private::createLayouts( QWidget* w )
00618 {
00619 KDAB_FOREACH( KDChart::TextArea* textLayoutItem, textLayoutItems ) {
00620 textLayoutItem->removeFromParentLayout();
00621 }
00622 textLayoutItems.clear();
00623
00624 KDAB_FOREACH( KDChart::AbstractArea* layoutItem, layoutItems ) {
00625 layoutItem->removeFromParentLayout();
00626 }
00627 layoutItems.clear();
00628
00629 removeDummyHeaderFooters();
00630
00631
00632 if ( dataAndLegendLayout) {
00633 dataAndLegendLayout->removeItem( planesLayout );
00634 planesLayout->setParent( 0 );
00635 }
00636
00637 delete layout;
00638
00639
00640 layout = new QHBoxLayout( w );
00641
00642 #if defined SET_ALL_MARGINS_TO_ZERO
00643 layout->setMargin(0);
00644 #endif
00645 layout->setObjectName( QString::fromLatin1( "Chart::Private::layout" ) );
00646 layout->addSpacing( globalLeadingLeft );
00647
00648
00649
00650 vLayout = new QVBoxLayout();
00651
00652 #if defined SET_ALL_MARGINS_TO_ZERO
00653 vLayout->setMargin(0);
00654 #endif
00655 vLayout->setObjectName( QString::fromLatin1( "vLayout" ) );
00656 layout->addLayout( vLayout, 1000 );
00657 layout->addSpacing( globalLeadingRight );
00658
00659
00660
00661
00662 vLayout->addSpacing( globalLeadingTop );
00663
00664 headerLayout = new QGridLayout();
00665
00666 #if defined SET_ALL_MARGINS_TO_ZERO
00667 headerLayout->setMargin(0);
00668 #endif
00669 vLayout->addLayout( headerLayout );
00670
00671 dataAndLegendLayout = new QGridLayout();
00672
00673 #if defined SET_ALL_MARGINS_TO_ZERO
00674 dataAndLegendLayout->setMargin(0);
00675 #endif
00676 dataAndLegendLayout->setObjectName( QString::fromLatin1( "dataAndLegendLayout" ) );
00677 vLayout->addLayout( dataAndLegendLayout, 1000 );
00678
00679 footerLayout = new QGridLayout();
00680
00681 #if defined SET_ALL_MARGINS_TO_ZERO
00682 footerLayout->setMargin(0);
00683 #endif
00684 footerLayout->setObjectName( QString::fromLatin1( "footerLayout" ) );
00685 vLayout->addLayout( footerLayout );
00686
00687 vLayout->addSpacing( globalLeadingBottom );
00688
00689
00690 dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
00691 dataAndLegendLayout->setRowStretch( 1, 1 );
00692 dataAndLegendLayout->setColumnStretch( 1, 1 );
00693
00694
00695 }
00696
00697 void Chart::Private::slotRelayout()
00698 {
00699
00700 createLayouts( chart );
00701
00702 layoutHeadersAndFooters();
00703 layoutLegends();
00704
00705
00706
00707
00708 const QRect geo( QRect( 0, 0, currentLayoutSize.width(), currentLayoutSize.height() ) );
00709 if( geo.isValid() && geo != layout->geometry() ){
00710
00711
00712
00713 layout->setGeometry( geo );
00714
00715
00716
00717 }
00718
00719
00720 KDAB_FOREACH (AbstractCoordinatePlane* plane, coordinatePlanes ) {
00721 plane->layoutDiagrams();
00722 }
00723
00724 }
00725
00726
00727
00728
00729 void Chart::Private::resizeLayout( const QSize& size )
00730 {
00731 currentLayoutSize = size;
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747 slotLayoutPlanes();
00748
00749
00750 }
00751
00752
00753 void Chart::Private::paintAll( QPainter* painter )
00754 {
00755 QRect rect( QPoint(0, 0), currentLayoutSize );
00756
00757
00758
00759
00760 KDChart::AbstractAreaBase::paintBackgroundAttributes(
00761 *painter, rect, backgroundAttributes );
00762
00763 KDChart::AbstractAreaBase::paintFrameAttributes(
00764 *painter, rect, frameAttributes );
00765
00766 chart->reLayoutFloatingLegends();
00767
00768 KDAB_FOREACH( KDChart::AbstractArea* layoutItem, layoutItems ) {
00769 layoutItem->paintAll( *painter );
00770 }
00771 KDAB_FOREACH( KDChart::AbstractLayoutItem* planeLayoutItem, planeLayoutItems ) {
00772 planeLayoutItem->paintAll( *painter );
00773 }
00774 KDAB_FOREACH( KDChart::TextArea* textLayoutItem, textLayoutItems ) {
00775 textLayoutItem->paintAll( *painter );
00776 }
00777 }
00778
00779
00780
00781 Chart::Chart ( QWidget* parent )
00782 : QWidget ( parent )
00783 , _d( new Private( this ) )
00784 {
00785 #if defined KDAB_EVAL
00786 EvalDialog::checkEvalLicense( "KD Chart" );
00787 #endif
00788
00789 FrameAttributes frameAttrs;
00790 frameAttrs.setVisible( true );
00791 frameAttrs.setPen( QPen( Qt::black ) );
00792 frameAttrs.setPadding( 1 );
00793 setFrameAttributes( frameAttrs );
00794
00795 addCoordinatePlane( new CartesianCoordinatePlane ( this ) );
00796 }
00797
00798 Chart::~Chart()
00799 {
00800 delete _d;
00801 }
00802
00803 #define d d_func()
00804
00805 void Chart::setFrameAttributes( const FrameAttributes &a )
00806 {
00807 d->frameAttributes = a;
00808 }
00809
00810 FrameAttributes Chart::frameAttributes() const
00811 {
00812 return d->frameAttributes;
00813 }
00814
00815 void Chart::setBackgroundAttributes( const BackgroundAttributes &a )
00816 {
00817 d->backgroundAttributes = a;
00818 }
00819
00820 BackgroundAttributes Chart::backgroundAttributes() const
00821 {
00822 return d->backgroundAttributes;
00823 }
00824
00825 QLayout* Chart::coordinatePlaneLayout()
00826 {
00827 return d->planesLayout;
00828 }
00829
00830 AbstractCoordinatePlane* Chart::coordinatePlane()
00831 {
00832 if ( d->coordinatePlanes.isEmpty() )
00833 {
00834 qWarning() << "Chart::coordinatePlane: warning: no coordinate plane defined.";
00835 return 0;
00836 } else {
00837 return d->coordinatePlanes.first();
00838 }
00839 }
00840
00841 CoordinatePlaneList Chart::coordinatePlanes()
00842 {
00843 return d->coordinatePlanes;
00844 }
00845
00846 void Chart::addCoordinatePlane( AbstractCoordinatePlane* plane )
00847 {
00848 connect( plane, SIGNAL( destroyedCoordinatePlane( AbstractCoordinatePlane* ) ),
00849 d, SLOT( slotUnregisterDestroyedPlane( AbstractCoordinatePlane* ) ) );
00850 connect( plane, SIGNAL( needUpdate() ), this, SLOT( update() ) );
00851 connect( plane, SIGNAL( needRelayout() ), d, SLOT( slotRelayout() ) ) ;
00852 connect( plane, SIGNAL( needLayoutPlanes() ), d, SLOT( slotLayoutPlanes() ) ) ;
00853 connect( plane, SIGNAL( propertiesChanged() ),this, SIGNAL( propertiesChanged() ) );
00854 d->coordinatePlanes.append( plane );
00855 plane->setParent( this );
00856 d->slotLayoutPlanes();
00857 }
00858
00859 void Chart::replaceCoordinatePlane( AbstractCoordinatePlane* plane,
00860 AbstractCoordinatePlane* oldPlane_ )
00861 {
00862 if( plane && oldPlane_ != plane ){
00863 AbstractCoordinatePlane* oldPlane = oldPlane_;
00864 if( d->coordinatePlanes.count() ){
00865 if( ! oldPlane )
00866 oldPlane = d->coordinatePlanes.first();
00867 takeCoordinatePlane( oldPlane );
00868 }
00869 delete oldPlane;
00870 addCoordinatePlane( plane );
00871 }
00872 }
00873
00874 void Chart::takeCoordinatePlane( AbstractCoordinatePlane* plane )
00875 {
00876 const int idx = d->coordinatePlanes.indexOf( plane );
00877 if( idx != -1 ){
00878 d->coordinatePlanes.takeAt( idx );
00879 disconnect( plane, SIGNAL( destroyedCoordinatePlane( AbstractCoordinatePlane* ) ),
00880 d, SLOT( slotUnregisterDestroyedPlane( AbstractCoordinatePlane* ) ) );
00881 plane->removeFromParentLayout();
00882 plane->setParent( 0 );
00883 }
00884 d->slotLayoutPlanes();
00885 }
00886
00887 void Chart::setGlobalLeading( int left, int top, int right, int bottom )
00888 {
00889 setGlobalLeadingLeft( left );
00890 setGlobalLeadingTop( top );
00891 setGlobalLeadingRight( right );
00892 setGlobalLeadingBottom( bottom );
00893 d->slotRelayout();
00894 }
00895
00896 void Chart::setGlobalLeadingLeft( int leading )
00897 {
00898 d->globalLeadingLeft = leading;
00899 d->slotRelayout();
00900 }
00901
00902 int Chart::globalLeadingLeft() const
00903 {
00904 return d->globalLeadingLeft;
00905 }
00906
00907 void Chart::setGlobalLeadingTop( int leading )
00908 {
00909 d->globalLeadingTop = leading;
00910 d->slotRelayout();
00911 }
00912
00913 int Chart::globalLeadingTop() const
00914 {
00915 return d->globalLeadingTop;
00916 }
00917
00918 void Chart::setGlobalLeadingRight( int leading )
00919 {
00920 d->globalLeadingRight = leading;
00921 d->slotRelayout();
00922 }
00923
00924 int Chart::globalLeadingRight() const
00925 {
00926 return d->globalLeadingRight;
00927 }
00928
00929 void Chart::setGlobalLeadingBottom( int leading )
00930 {
00931 d->globalLeadingBottom = leading;
00932 d->slotRelayout();
00933 }
00934
00935 int Chart::globalLeadingBottom() const
00936 {
00937 return d->globalLeadingBottom;
00938 }
00939
00940 void Chart::paint( QPainter* painter, const QRect& target )
00941 {
00942 if( target.isEmpty() || !painter ) return;
00943
00944
00945 GlobalMeasureScaling::instance()->setFactors(
00946 static_cast<qreal>(target.width()) /
00947 static_cast<qreal>(geometry().size().width()),
00948 static_cast<qreal>(target.height()) /
00949 static_cast<qreal>(geometry().size().height()) );
00950
00951 if( target.size() != d->currentLayoutSize ){
00952 d->resizeLayout( target.size() );
00953 }
00954 const QPoint translation = target.topLeft();
00955 painter->translate( translation );
00956
00957 d->paintAll( painter );
00958
00959
00960
00961
00962
00963 KDAB_FOREACH( Legend *legend, d->legends ) {
00964 const bool hidden = legend->isHidden() && legend->testAttribute(Qt::WA_WState_ExplicitShowHide);
00965 if ( !hidden ) {
00966
00967 legend->paintIntoRect( *painter, legend->geometry() );
00968
00969
00970 }
00971 }
00972
00973 painter->translate( -translation.x(), -translation.y() );
00974
00975 GlobalMeasureScaling::instance()->resetFactors();
00976
00977
00978 }
00979
00980 void Chart::resizeEvent ( QResizeEvent * )
00981 {
00982 d->resizeLayout( size() );
00983 KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ){
00984 plane->setGridNeedsRecalculate();
00985 }
00986 reLayoutFloatingLegends();
00987 }
00988
00989
00990 void Chart::reLayoutFloatingLegends()
00991 {
00992 KDAB_FOREACH( Legend *legend, d->legends ) {
00993 const bool hidden = legend->isHidden() && legend->testAttribute(Qt::WA_WState_ExplicitShowHide);
00994 if ( legend->position().isFloating() && !hidden ){
00995
00996 const QSize legendSize( legend->sizeHint() );
00997 legend->setGeometry( QRect( legend->geometry().topLeft(), legendSize ) );
00998
00999 const RelativePosition relPos( legend->floatingPosition() );
01000 QPointF pt( relPos.calculatedPoint( size() ) );
01001 qDebug() << pt;
01002
01003 const Qt::Alignment alignTopLeft = Qt::AlignBottom | Qt::AlignLeft;
01004 if( (relPos.alignment() & alignTopLeft) != alignTopLeft ){
01005 if( relPos.alignment() & Qt::AlignRight )
01006 pt.rx() -= legendSize.width();
01007 else if( relPos.alignment() & Qt::AlignHCenter )
01008 pt.rx() -= 0.5 * legendSize.width();
01009
01010 if( relPos.alignment() & Qt::AlignBottom )
01011 pt.ry() -= legendSize.height();
01012 else if( relPos.alignment() & Qt::AlignVCenter )
01013 pt.ry() -= 0.5 * legendSize.height();
01014 }
01015 qDebug() << pt << endl;
01016 legend->move( static_cast<int>(pt.x()), static_cast<int>(pt.y()) );
01017 }
01018 }
01019 }
01020
01021
01022 void Chart::paintEvent( QPaintEvent* )
01023 {
01024 QPainter painter( this );
01025
01026 if( size() != d->currentLayoutSize ){
01027 d->resizeLayout( size() );
01028 reLayoutFloatingLegends();
01029 }
01030
01031
01032
01033 d->paintAll( &painter );
01034 }
01035
01036 void Chart::addHeaderFooter( HeaderFooter* headerFooter )
01037 {
01038 d->headerFooters.append( headerFooter );
01039 headerFooter->setParent( this );
01040 connect( headerFooter, SIGNAL( destroyedHeaderFooter( HeaderFooter* ) ),
01041 d, SLOT( slotUnregisterDestroyedHeaderFooter( HeaderFooter* ) ) );
01042 connect( headerFooter, SIGNAL( positionChanged( HeaderFooter* ) ),
01043 d, SLOT( slotRelayout() ) );
01044 d->slotRelayout();
01045 }
01046
01047 void Chart::replaceHeaderFooter( HeaderFooter* headerFooter,
01048 HeaderFooter* oldHeaderFooter_ )
01049 {
01050 if( headerFooter && oldHeaderFooter_ != headerFooter ){
01051 HeaderFooter* oldHeaderFooter = oldHeaderFooter_;
01052 if( d->headerFooters.count() ){
01053 if( ! oldHeaderFooter )
01054 oldHeaderFooter = d->headerFooters.first();
01055 takeHeaderFooter( oldHeaderFooter );
01056 }
01057 delete oldHeaderFooter;
01058 addHeaderFooter( headerFooter );
01059 }
01060 }
01061
01062 void Chart::takeHeaderFooter( HeaderFooter* headerFooter )
01063 {
01064 const int idx = d->headerFooters.indexOf( headerFooter );
01065 if( idx != -1 ){
01066 d->headerFooters.takeAt( idx );
01067 disconnect( headerFooter, SIGNAL( destroyedHeaderFooter( HeaderFooter* ) ),
01068 d, SLOT( slotUnregisterDestroyedHeaderFooter( HeaderFooter* ) ) );
01069 headerFooter->setParent( 0 );
01070 }
01071 d->slotRelayout();
01072 }
01073
01074 HeaderFooter* Chart::headerFooter()
01075 {
01076 if( d->headerFooters.isEmpty() ) {
01077 return 0;
01078 } else {
01079 return d->headerFooters.first();
01080 }
01081 }
01082
01083 HeaderFooterList Chart::headerFooters()
01084 {
01085 return d->headerFooters;
01086 }
01087
01088
01089 void Chart::addLegend( Legend* legend )
01090 {
01091 if( ! legend ) return;
01092
01093
01094 d->legends.append( legend );
01095 legend->setParent( this );
01096
01097 TextAttributes textAttrs( legend->textAttributes() );
01098
01099 KDChart::Measure measure( textAttrs.fontSize() );
01100 measure.setRelativeMode( this, KDChartEnums::MeasureOrientationMinimum );
01101 measure.setValue( 20 );
01102 textAttrs.setFontSize( measure );
01103 legend->setTextAttributes( textAttrs );
01104
01105 textAttrs = legend->titleTextAttributes();
01106 measure.setRelativeMode( this, KDChartEnums::MeasureOrientationMinimum );
01107 measure.setValue( 24 );
01108 textAttrs.setFontSize( measure );
01109
01110 legend->setTitleTextAttributes( textAttrs );
01111
01112 legend->setReferenceArea( this );
01113
01114
01115
01116
01117
01118
01119
01120
01121
01122
01123
01124
01125 connect( legend, SIGNAL( destroyedLegend( Legend* ) ),
01126 d, SLOT( slotUnregisterDestroyedLegend( Legend* ) ) );
01127 connect( legend, SIGNAL( positionChanged( AbstractAreaWidget* ) ),
01128 d, SLOT( slotLayoutPlanes() ) );
01129 connect( legend, SIGNAL( propertiesChanged() ),this, SIGNAL( propertiesChanged() ) );
01130 legend->setVisible( true );
01131 d->slotRelayout();
01132 }
01133
01134
01135 void Chart::replaceLegend( Legend* legend, Legend* oldLegend_ )
01136 {
01137 if( legend && oldLegend_ != legend ){
01138 Legend* oldLegend = oldLegend_;
01139 if( d->legends.count() ){
01140 if( ! oldLegend )
01141 oldLegend = d->legends.first();
01142 takeLegend( oldLegend );
01143 }
01144 delete oldLegend;
01145 addLegend( legend );
01146 }
01147 }
01148
01149 void Chart::takeLegend( Legend* legend )
01150 {
01151 const int idx = d->legends.indexOf( legend );
01152 if( idx != -1 ){
01153 d->legends.takeAt( idx );
01154 disconnect( legend, SIGNAL( destroyedLegend( Legend* ) ),
01155 d, SLOT( slotUnregisterDestroyedLegend( Legend* ) ) );
01156 legend->setParent( 0 );
01157 }
01158 d->slotRelayout();
01159 }
01160
01161 Legend* Chart::legend()
01162 {
01163 if ( d->legends.isEmpty() )
01164 {
01165 return 0;
01166 } else {
01167 return d->legends.first();
01168 }
01169 }
01170
01171 LegendList Chart::legends()
01172 {
01173 return d->legends;
01174 }
01175
01176
01177 void Chart::mousePressEvent( QMouseEvent* event )
01178 {
01179 KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) {
01180 if ( plane->geometry().contains( event->pos() ) ) {
01181 if ( plane->diagrams().size() > 0 ) {
01182 QPoint pos = plane->diagram()->mapFromGlobal( event->globalPos() );
01183 QMouseEvent ev( QEvent::MouseButtonPress, pos, event->globalPos(),
01184 event->button(), event->buttons(),
01185 event->modifiers() );
01186 plane->mousePressEvent( &ev );
01187 }
01188 }
01189 }
01190 }
01191
01192
01193
01194
01195
01196
01197
01198
01199
01200
01201
01202
01203
01204
01205
01206
01207
01208
01209
01210
01211
01212
01213
01214
01215
01216
01217
01218
01219
01220
01221
01222
01223
01224
01225
01226
01227
01228
01229
01230
01231
01232
01233
01234
01235
01236
01237
01238
01239
01240
01241
01242
01243
01244
01245
01246
01247
01248
01249
01250
01251
01252
01253
01254
01255
01256
01257
01258
01259
01260
01261
01262
01263
01264
01265
01266
01267
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
01278
01279
01280
01281
01282
01283
01284
01285
01286
01287
01288
01289
01290
01291
01292
01293
01294
01295
01296
01297
01298
01299
01300
01301
01302
01303
01304
01305
01306
01307
01308
01309
01310
01311
01312
01313
01314
01315
01316
01317
01318
01319
01320
01321
01322
01323
01324
01325
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335
01336
01337
01338
01339
01340
01341
01342
01343
01344
01345
01346
01347
01348
01349
01350
01351
01352
01353
01354
01355
01356
01357
01358
01359
01360
01361
01362
01363