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 void Chart::Private::layoutLegends()
00225 {
00226
00227
00228
00229 QList<Legend*> infos[3][3];
00230
00231 Q_FOREACH( Legend *legend, legends ) {
00232
00233 legend->needSizeHint();
00234
00235 bool bOK = true;
00236 int row, column;
00237 switch( legend->position().value() ) {
00238 case KDChartEnums::PositionNorthWest: row = 0; column = 0;
00239 break;
00240 case KDChartEnums::PositionNorth: row = 0; column = 1;
00241 break;
00242 case KDChartEnums::PositionNorthEast: row = 0; column = 2;
00243 break;
00244 case KDChartEnums::PositionEast: row = 1; column = 2;
00245 break;
00246 case KDChartEnums::PositionSouthEast: row = 2; column = 2;
00247 break;
00248 case KDChartEnums::PositionSouth: row = 2; column = 1;
00249 break;
00250 case KDChartEnums::PositionSouthWest: row = 2; column = 0;
00251 break;
00252 case KDChartEnums::PositionWest: row = 1; column = 0;
00253 break;
00254 case KDChartEnums::PositionCenter:
00255 qDebug( "Sorry: Legend not shown, because position Center is not supported." );
00256 bOK = false;
00257 break;
00258 case KDChartEnums::PositionFloating:
00259 bOK = false;
00260 break;
00261 default:
00262 qDebug( "Sorry: Legend not shown, because of unknown legend position." );
00263 bOK = false;
00264 break;
00265 }
00266 if( bOK )
00267 infos[row][column] << legend;
00268 }
00269
00270
00271 for (int iR = 0; iR < 3; ++iR) {
00272 for (int iC = 0; iC < 3; ++iC) {
00273 QList<Legend*>& list = infos[iR][iC];
00274 const int count = list.size();
00275 switch( count ){
00276 case 0:
00277 break;
00278 case 1: {
00279 Legend* legend = list.first();
00280 dataAndLegendLayout->addItem( new MyWidgetItem(legend),
00281 iR, iC, 1, 1, legend->alignment() );
00282 }
00283 break;
00284 default: {
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296 Legend* legend = list.first();
00297 Qt::Alignment alignment = legend->alignment();
00298 bool haveSameAlign = true;
00299 for (int i = 1; i < count; ++i) {
00300 legend = list.at(i);
00301 if( alignment != legend->alignment() ){
00302 haveSameAlign = false;
00303 break;
00304 }
00305 }
00306 if( haveSameAlign ){
00307 QVBoxLayout* vLayout = new QVBoxLayout();
00308 for (int i = 0; i < count; ++i) {
00309 vLayout->addItem( new MyWidgetItem(list.at(i), Qt::AlignLeft) );
00310 }
00311 dataAndLegendLayout->addLayout( vLayout, iR, iC, 1, 1, alignment );
00312 }else{
00313 QGridLayout* gridLayout = new QGridLayout();
00314 #define ADD_VBOX_WITH_LEGENDS(row, column, align) \
00315 { \
00316 QVBoxLayout* innerLayout = new QVBoxLayout(); \
00317 for (int i = 0; i < count; ++i) { \
00318 legend = list.at(i); \
00319 if( legend->alignment() == ( align ) ) \
00320 innerLayout->addItem( new MyWidgetItem(legend, Qt::AlignLeft) ); \
00321 } \
00322 gridLayout->addLayout( innerLayout, row, column, ( align ) ); \
00323 }
00324 ADD_VBOX_WITH_LEGENDS( 0, 0, Qt::AlignTop | Qt::AlignLeft )
00325 ADD_VBOX_WITH_LEGENDS( 0, 1, Qt::AlignTop | Qt::AlignHCenter )
00326 ADD_VBOX_WITH_LEGENDS( 0, 2, Qt::AlignTop | Qt::AlignRight )
00327
00328 ADD_VBOX_WITH_LEGENDS( 1, 0, Qt::AlignVCenter | Qt::AlignLeft )
00329 ADD_VBOX_WITH_LEGENDS( 1, 2, Qt::AlignVCenter | Qt::AlignRight )
00330
00331 ADD_VBOX_WITH_LEGENDS( 2, 0, Qt::AlignBottom | Qt::AlignLeft )
00332 ADD_VBOX_WITH_LEGENDS( 2, 1, Qt::AlignBottom | Qt::AlignHCenter )
00333 ADD_VBOX_WITH_LEGENDS( 2, 2, Qt::AlignBottom | Qt::AlignRight )
00334
00335 dataAndLegendLayout->addLayout( gridLayout, iR, iC, 1, 1 );
00336 }
00337 }
00338 }
00339 }
00340 }
00341 }
00342
00343
00344 QHash<AbstractCoordinatePlane*, PlaneInfo> Chart::Private::buildPlaneLayoutInfos()
00345 {
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358 QHash<CartesianAxis*, AxisInfo> axisInfos;
00359 QHash<AbstractCoordinatePlane*, PlaneInfo> planeInfos;
00360 Q_FOREACH(AbstractCoordinatePlane* plane, coordinatePlanes )
00361 {
00362 PlaneInfo p;
00363
00364 p.referencePlane = plane->referenceCoordinatePlane();
00365 planeInfos.insert( plane, p );
00366
00367 Q_FOREACH( AbstractDiagram* abstractDiagram, plane->diagrams() ) {
00368 AbstractCartesianDiagram* diagram =
00369 dynamic_cast<AbstractCartesianDiagram*> ( abstractDiagram );
00370 if( !diagram ) continue;
00371
00372 Q_FOREACH( CartesianAxis* axis, diagram->axes() ) {
00373 if ( !axisInfos.contains( axis ) ) {
00374
00375
00376
00377
00378 AxisInfo i;
00379 i.plane = plane;
00380 axisInfos.insert( axis, i );
00381 } else {
00382 AxisInfo i = axisInfos[axis];
00383 if ( i.plane == plane ) continue;
00384
00385
00386
00387
00388 PlaneInfo pi = planeInfos[plane];
00389
00390 if ( !pi.referencePlane ) {
00391
00392 pi.referencePlane = i.plane;
00393 if ( axis->position() == CartesianAxis::Left
00394 || axis->position() == CartesianAxis::Right )
00395 pi.horizontalOffset += 1;
00396 planeInfos[plane] = pi;
00397
00398 pi = planeInfos[i.plane];
00399 if ( axis->position() == CartesianAxis::Top
00400 || axis->position() == CartesianAxis::Bottom )
00401 pi.verticalOffset += 1;
00402
00403 planeInfos[i.plane] = pi;
00404 }
00405 }
00406 }
00407 }
00408
00409 p = planeInfos[plane];
00410 if ( p.referencePlane == 0 ) {
00411 p.gridLayout = new QGridLayout();
00412
00413 #if defined SET_ALL_MARGINS_TO_ZERO
00414 p.gridLayout->setMargin(0);
00415 #endif
00416 planeInfos[plane] = p;
00417 }
00418 }
00419 return planeInfos;
00420 }
00421
00422 template <typename T>
00423 static T* findOrCreateLayoutByObjectName( QLayout * parentLayout, const char* name )
00424 {
00425 T *box = qFindChild<T*>( parentLayout, QString::fromLatin1( name ) );
00426 if ( !box ) {
00427 box = new T();
00428
00429 #if defined SET_ALL_MARGINS_TO_ZERO
00430 box->setMargin(0);
00431 #endif
00432 box->setObjectName( QString::fromLatin1( name ) );
00433 box->setSizeConstraint( QLayout::SetFixedSize );
00434 }
00435 return box;
00436 }
00437
00438 #if 0
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 #endif
00449
00450 void Chart::Private::slotLayoutPlanes()
00451 {
00452
00453 const QBoxLayout::Direction oldPlanesDirection =
00454 planesLayout ? planesLayout->direction() : QBoxLayout::TopToBottom;
00455 if ( planesLayout && dataAndLegendLayout )
00456 dataAndLegendLayout->removeItem( planesLayout );
00457
00458 KDAB_FOREACH( KDChart::AbstractLayoutItem* plane, planeLayoutItems ) {
00459 plane->removeFromParentLayout();
00460 }
00461 planeLayoutItems.clear();
00462 delete planesLayout;
00463
00464
00465 planesLayout = new QBoxLayout( oldPlanesDirection );
00466
00467
00468 #if defined SET_ALL_MARGINS_TO_ZERO
00469 planesLayout->setMargin(0);
00470 #endif
00471 planesLayout->setObjectName( QString::fromLatin1( "planesLayout" ) );
00472
00473
00474
00475
00476 QHash<AbstractCoordinatePlane*, PlaneInfo> planeInfos = buildPlaneLayoutInfos();
00477 QHash<AbstractAxis*, AxisInfo> axisInfos;
00478 KDAB_FOREACH( AbstractCoordinatePlane* plane, coordinatePlanes ) {
00479 Q_ASSERT( planeInfos.contains(plane) );
00480 const PlaneInfo pi = planeInfos[plane];
00481 int column = pi.horizontalOffset;
00482 int row = pi.verticalOffset;
00483
00484 QGridLayout *planeLayout = pi.gridLayout;
00485 if ( !planeLayout ) {
00486
00487
00488 planeLayout = planeInfos[pi.referencePlane].gridLayout;
00489 } else {
00490 planesLayout->addLayout( planeLayout );
00491 }
00492 Q_ASSERT( planeLayout );
00493
00494
00495
00496 planeLayoutItems << plane;
00497 plane->setParentLayout( planeLayout );
00498 planeLayout->addItem( plane, row, column, 1, 1, 0 );
00499
00500 planeLayout->setRowStretch( row, 2 );
00501 planeLayout->setColumnStretch( column, 2 );
00502
00503
00504
00505
00506 QVBoxLayout *topAxesLayout = 0;
00507 QVBoxLayout *bottomAxesLayout = 0;
00508 QHBoxLayout *leftAxesLayout = 0;
00509 QHBoxLayout *rightAxesLayout = 0;
00510
00511 KDAB_FOREACH( AbstractDiagram* abstractDiagram, plane->diagrams() )
00512 {
00513 AbstractCartesianDiagram* diagram =
00514 dynamic_cast<AbstractCartesianDiagram*> ( abstractDiagram );
00515
00516 if( !diagram ) continue;
00517
00518
00519
00520 topAxesLayout = new QVBoxLayout;
00521 topAxesLayout->setObjectName( QString::fromLatin1( "topAxesLayout" ) );
00522 bottomAxesLayout = new QVBoxLayout;
00523 bottomAxesLayout->setObjectName( QString::fromLatin1( "bottomAxesLayout" ) );
00524 leftAxesLayout = new QHBoxLayout;
00525 leftAxesLayout->setObjectName( QString::fromLatin1( "leftAxesLayout" ) );
00526 rightAxesLayout = new QHBoxLayout;
00527 rightAxesLayout->setObjectName( QString::fromLatin1( "rightAxesLayout" ) );
00528
00529
00530
00531 KDAB_FOREACH( CartesianAxis* axis, diagram->axes() ) {
00532 if ( axisInfos.contains( axis ) ) continue;
00533 Q_ASSERT ( axis );
00534
00535 planeLayoutItems << axis;
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549 switch ( axis->position() )
00550 {
00551 case CartesianAxis::Top:
00552 axis->setParentLayout( topAxesLayout );
00553 topAxesLayout->addItem( axis );
00554 break;
00555 case CartesianAxis::Bottom:
00556 axis->setParentLayout( bottomAxesLayout );
00557 bottomAxesLayout->addItem( axis );
00558 break;
00559 case CartesianAxis::Left:
00560 axis->setParentLayout( leftAxesLayout );
00561 leftAxesLayout->addItem( axis );
00562 break;
00563 case CartesianAxis::Right:
00564 axis->setParentLayout( rightAxesLayout );
00565 rightAxesLayout->addItem( axis );
00566 break;
00567 default:
00568 Q_ASSERT_X( false, "Chart::paintEvent",
00569 "unknown axis position" );
00570 break;
00571 };
00572 axisInfos.insert( axis, AxisInfo() );
00573 }
00574
00575
00576
00577
00578 if ( !topAxesLayout->parent() )
00579 planeLayout->addLayout( topAxesLayout, row - 1, column );
00580 if ( !bottomAxesLayout->parent() )
00581 planeLayout->addLayout( bottomAxesLayout, row + 1, column );
00582 if ( !leftAxesLayout->parent() ){
00583 planeLayout->addLayout( leftAxesLayout, row, column - 1);
00584
00585
00586 }
00587 if ( !rightAxesLayout->parent() )
00588 planeLayout->addLayout( rightAxesLayout, row, column + 1);
00589 }
00590
00591
00592 #define ADD_AUTO_SPACER_IF_NEEDED( \
00593 spacerRow, spacerColumn, hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ) \
00594 { \
00595 if( hLayout || vLayout ) { \
00596 AutoSpacerLayoutItem * spacer \
00597 = new AutoSpacerLayoutItem( hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ); \
00598 planeLayout->addItem( spacer, spacerRow, spacerColumn, 1, 1 ); \
00599 spacer->setParentLayout( planeLayout ); \
00600 planeLayoutItems << spacer; \
00601 } \
00602 }
00603 ADD_AUTO_SPACER_IF_NEEDED( row-1, column-1, false, leftAxesLayout, false, topAxesLayout )
00604 ADD_AUTO_SPACER_IF_NEEDED( row+1, column-1, true, leftAxesLayout, false, bottomAxesLayout )
00605 ADD_AUTO_SPACER_IF_NEEDED( row-1, column+1, false, rightAxesLayout, true, topAxesLayout )
00606 ADD_AUTO_SPACER_IF_NEEDED( row+1, column+1, true, rightAxesLayout, true, bottomAxesLayout )
00607 }
00608
00609 if ( dataAndLegendLayout ){
00610 dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
00611 dataAndLegendLayout->setRowStretch( 1, 1000 );
00612 dataAndLegendLayout->setColumnStretch( 1, 1000 );
00613 }
00614
00615 slotRelayout();
00616
00617 }
00618
00619 void Chart::Private::createLayouts( QWidget* w )
00620 {
00621 KDAB_FOREACH( KDChart::TextArea* textLayoutItem, textLayoutItems ) {
00622 textLayoutItem->removeFromParentLayout();
00623 }
00624 textLayoutItems.clear();
00625
00626 KDAB_FOREACH( KDChart::AbstractArea* layoutItem, layoutItems ) {
00627 layoutItem->removeFromParentLayout();
00628 }
00629 layoutItems.clear();
00630
00631 removeDummyHeaderFooters();
00632
00633
00634 if ( dataAndLegendLayout) {
00635 dataAndLegendLayout->removeItem( planesLayout );
00636 planesLayout->setParent( 0 );
00637 }
00638
00639 delete layout;
00640
00641
00642 layout = new QHBoxLayout( w );
00643
00644 #if defined SET_ALL_MARGINS_TO_ZERO
00645 layout->setMargin(0);
00646 #endif
00647 layout->setObjectName( QString::fromLatin1( "Chart::Private::layout" ) );
00648 layout->addSpacing( globalLeadingLeft );
00649
00650
00651
00652 vLayout = new QVBoxLayout();
00653
00654 #if defined SET_ALL_MARGINS_TO_ZERO
00655 vLayout->setMargin(0);
00656 #endif
00657 vLayout->setObjectName( QString::fromLatin1( "vLayout" ) );
00658 layout->addLayout( vLayout, 1000 );
00659 layout->addSpacing( globalLeadingRight );
00660
00661
00662
00663
00664 vLayout->addSpacing( globalLeadingTop );
00665
00666 headerLayout = new QGridLayout();
00667
00668 #if defined SET_ALL_MARGINS_TO_ZERO
00669 headerLayout->setMargin(0);
00670 #endif
00671 vLayout->addLayout( headerLayout );
00672
00673 dataAndLegendLayout = new QGridLayout();
00674
00675 #if defined SET_ALL_MARGINS_TO_ZERO
00676 dataAndLegendLayout->setMargin(0);
00677 #endif
00678 dataAndLegendLayout->setObjectName( QString::fromLatin1( "dataAndLegendLayout" ) );
00679 vLayout->addLayout( dataAndLegendLayout, 1000 );
00680
00681 footerLayout = new QGridLayout();
00682
00683 #if defined SET_ALL_MARGINS_TO_ZERO
00684 footerLayout->setMargin(0);
00685 #endif
00686 footerLayout->setObjectName( QString::fromLatin1( "footerLayout" ) );
00687 vLayout->addLayout( footerLayout );
00688
00689 vLayout->addSpacing( globalLeadingBottom );
00690
00691
00692 dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
00693 dataAndLegendLayout->setRowStretch( 1, 1 );
00694 dataAndLegendLayout->setColumnStretch( 1, 1 );
00695
00696
00697 }
00698
00699 void Chart::Private::slotRelayout()
00700 {
00701
00702 createLayouts( chart );
00703
00704 layoutHeadersAndFooters();
00705 layoutLegends();
00706
00707
00708
00709
00710 const QRect geo( QRect( 0, 0, currentLayoutSize.width(), currentLayoutSize.height() ) );
00711 if( geo.isValid() && geo != layout->geometry() ){
00712
00713
00714
00715 layout->setGeometry( geo );
00716
00717
00718
00719 }
00720
00721
00722 KDAB_FOREACH (AbstractCoordinatePlane* plane, coordinatePlanes ) {
00723 plane->layoutDiagrams();
00724 }
00725
00726 }
00727
00728
00729
00730
00731 void Chart::Private::resizeLayout( const QSize& size )
00732 {
00733 currentLayoutSize = size;
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749 slotLayoutPlanes();
00750
00751
00752 }
00753
00754
00755 void Chart::Private::paintAll( QPainter* painter )
00756 {
00757 QRect rect( QPoint(0, 0), currentLayoutSize );
00758
00759
00760
00761
00762 KDChart::AbstractAreaBase::paintBackgroundAttributes(
00763 *painter, rect, backgroundAttributes );
00764
00765 KDChart::AbstractAreaBase::paintFrameAttributes(
00766 *painter, rect, frameAttributes );
00767
00768 chart->reLayoutFloatingLegends();
00769
00770 KDAB_FOREACH( KDChart::AbstractArea* layoutItem, layoutItems ) {
00771 layoutItem->paintAll( *painter );
00772 }
00773 KDAB_FOREACH( KDChart::AbstractLayoutItem* planeLayoutItem, planeLayoutItems ) {
00774 planeLayoutItem->paintAll( *painter );
00775 }
00776 KDAB_FOREACH( KDChart::TextArea* textLayoutItem, textLayoutItems ) {
00777 textLayoutItem->paintAll( *painter );
00778 }
00779 }
00780
00781
00782
00783 Chart::Chart ( QWidget* parent )
00784 : QWidget ( parent )
00785 , _d( new Private( this ) )
00786 {
00787 #if defined KDAB_EVAL
00788 EvalDialog::checkEvalLicense( "KD Chart" );
00789 #endif
00790
00791 FrameAttributes frameAttrs;
00792 frameAttrs.setVisible( true );
00793 frameAttrs.setPen( QPen( Qt::black ) );
00794 frameAttrs.setPadding( 1 );
00795 setFrameAttributes( frameAttrs );
00796
00797 addCoordinatePlane( new CartesianCoordinatePlane ( this ) );
00798 }
00799
00800 Chart::~Chart()
00801 {
00802 delete _d;
00803 }
00804
00805 #define d d_func()
00806
00807 void Chart::setFrameAttributes( const FrameAttributes &a )
00808 {
00809 d->frameAttributes = a;
00810 }
00811
00812 FrameAttributes Chart::frameAttributes() const
00813 {
00814 return d->frameAttributes;
00815 }
00816
00817 void Chart::setBackgroundAttributes( const BackgroundAttributes &a )
00818 {
00819 d->backgroundAttributes = a;
00820 }
00821
00822 BackgroundAttributes Chart::backgroundAttributes() const
00823 {
00824 return d->backgroundAttributes;
00825 }
00826
00827 QLayout* Chart::coordinatePlaneLayout()
00828 {
00829 return d->planesLayout;
00830 }
00831
00832 AbstractCoordinatePlane* Chart::coordinatePlane()
00833 {
00834 if ( d->coordinatePlanes.isEmpty() )
00835 {
00836 qWarning() << "Chart::coordinatePlane: warning: no coordinate plane defined.";
00837 return 0;
00838 } else {
00839 return d->coordinatePlanes.first();
00840 }
00841 }
00842
00843 CoordinatePlaneList Chart::coordinatePlanes()
00844 {
00845 return d->coordinatePlanes;
00846 }
00847
00848 void Chart::addCoordinatePlane( AbstractCoordinatePlane* plane )
00849 {
00850 connect( plane, SIGNAL( destroyedCoordinatePlane( AbstractCoordinatePlane* ) ),
00851 d, SLOT( slotUnregisterDestroyedPlane( AbstractCoordinatePlane* ) ) );
00852 connect( plane, SIGNAL( needUpdate() ), this, SLOT( update() ) );
00853 connect( plane, SIGNAL( needRelayout() ), d, SLOT( slotRelayout() ) ) ;
00854 connect( plane, SIGNAL( needLayoutPlanes() ), d, SLOT( slotLayoutPlanes() ) ) ;
00855 connect( plane, SIGNAL( propertiesChanged() ),this, SIGNAL( propertiesChanged() ) );
00856 d->coordinatePlanes.append( plane );
00857 plane->setParent( this );
00858 d->slotLayoutPlanes();
00859 }
00860
00861 void Chart::replaceCoordinatePlane( AbstractCoordinatePlane* plane,
00862 AbstractCoordinatePlane* oldPlane_ )
00863 {
00864 if( plane && oldPlane_ != plane ){
00865 AbstractCoordinatePlane* oldPlane = oldPlane_;
00866 if( d->coordinatePlanes.count() ){
00867 if( ! oldPlane )
00868 oldPlane = d->coordinatePlanes.first();
00869 takeCoordinatePlane( oldPlane );
00870 }
00871 delete oldPlane;
00872 addCoordinatePlane( plane );
00873 }
00874 }
00875
00876 void Chart::takeCoordinatePlane( AbstractCoordinatePlane* plane )
00877 {
00878 const int idx = d->coordinatePlanes.indexOf( plane );
00879 if( idx != -1 ){
00880 d->coordinatePlanes.takeAt( idx );
00881 disconnect( plane, SIGNAL( destroyedCoordinatePlane( AbstractCoordinatePlane* ) ),
00882 d, SLOT( slotUnregisterDestroyedPlane( AbstractCoordinatePlane* ) ) );
00883 plane->removeFromParentLayout();
00884 plane->setParent( 0 );
00885 }
00886 d->slotLayoutPlanes();
00887 }
00888
00889 void Chart::setGlobalLeading( int left, int top, int right, int bottom )
00890 {
00891 setGlobalLeadingLeft( left );
00892 setGlobalLeadingTop( top );
00893 setGlobalLeadingRight( right );
00894 setGlobalLeadingBottom( bottom );
00895 d->slotRelayout();
00896 }
00897
00898 void Chart::setGlobalLeadingLeft( int leading )
00899 {
00900 d->globalLeadingLeft = leading;
00901 d->slotRelayout();
00902 }
00903
00904 int Chart::globalLeadingLeft() const
00905 {
00906 return d->globalLeadingLeft;
00907 }
00908
00909 void Chart::setGlobalLeadingTop( int leading )
00910 {
00911 d->globalLeadingTop = leading;
00912 d->slotRelayout();
00913 }
00914
00915 int Chart::globalLeadingTop() const
00916 {
00917 return d->globalLeadingTop;
00918 }
00919
00920 void Chart::setGlobalLeadingRight( int leading )
00921 {
00922 d->globalLeadingRight = leading;
00923 d->slotRelayout();
00924 }
00925
00926 int Chart::globalLeadingRight() const
00927 {
00928 return d->globalLeadingRight;
00929 }
00930
00931 void Chart::setGlobalLeadingBottom( int leading )
00932 {
00933 d->globalLeadingBottom = leading;
00934 d->slotRelayout();
00935 }
00936
00937 int Chart::globalLeadingBottom() const
00938 {
00939 return d->globalLeadingBottom;
00940 }
00941
00942 void Chart::paint( QPainter* painter, const QRect& target )
00943 {
00944 if( target.isEmpty() || !painter ) return;
00945
00946
00947 GlobalMeasureScaling::setPaintDevice( painter->device() );
00948
00949
00950 if( dynamic_cast< QWidget* >( painter->device() ) != 0 )
00951 {
00952 GlobalMeasureScaling::setFactors(
00953 static_cast< qreal >( target.width() ) /
00954 static_cast< qreal >( geometry().size().width() ),
00955 static_cast< qreal >( target.height() ) /
00956 static_cast< qreal >( geometry().size().height() ) );
00957 }
00958
00959 else
00960 {
00961 const qreal resX = static_cast< qreal >( logicalDpiX() ) / static_cast< qreal >( painter->device()->logicalDpiX() );
00962 const qreal resY = static_cast< qreal >( logicalDpiY() ) / static_cast< qreal >( painter->device()->logicalDpiY() );
00963
00964 GlobalMeasureScaling::setFactors(
00965 static_cast< qreal >( target.width() ) /
00966 static_cast< qreal >( geometry().size().width() ) * resX,
00967 static_cast< qreal >( target.height() ) /
00968 static_cast< qreal >( geometry().size().height() ) * resY );
00969 }
00970
00971
00972 if( target.size() != d->currentLayoutSize ){
00973 d->resizeLayout( target.size() );
00974 }
00975 const QPoint translation = target.topLeft();
00976 painter->translate( translation );
00977
00978 d->paintAll( painter );
00979
00980
00981
00982
00983
00984 KDAB_FOREACH( Legend *legend, d->legends ) {
00985 const bool hidden = legend->isHidden() && legend->testAttribute(Qt::WA_WState_ExplicitShowHide);
00986 if ( !hidden ) {
00987
00988 legend->paintIntoRect( *painter, legend->geometry() );
00989
00990
00991 }
00992 }
00993
00994 painter->translate( -translation.x(), -translation.y() );
00995
00996 GlobalMeasureScaling::instance()->resetFactors();
00997
00998
00999 }
01000
01001 void Chart::resizeEvent ( QResizeEvent * )
01002 {
01003 d->resizeLayout( size() );
01004 KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ){
01005 plane->setGridNeedsRecalculate();
01006 }
01007 reLayoutFloatingLegends();
01008 }
01009
01010
01011 void Chart::reLayoutFloatingLegends()
01012 {
01013 KDAB_FOREACH( Legend *legend, d->legends ) {
01014 const bool hidden = legend->isHidden() && legend->testAttribute(Qt::WA_WState_ExplicitShowHide);
01015 if ( legend->position().isFloating() && !hidden ){
01016
01017 const QSize legendSize( legend->sizeHint() );
01018 legend->setGeometry( QRect( legend->geometry().topLeft(), legendSize ) );
01019
01020 const RelativePosition relPos( legend->floatingPosition() );
01021 QPointF pt( relPos.calculatedPoint( size() ) );
01022 qDebug() << pt;
01023
01024 const Qt::Alignment alignTopLeft = Qt::AlignBottom | Qt::AlignLeft;
01025 if( (relPos.alignment() & alignTopLeft) != alignTopLeft ){
01026 if( relPos.alignment() & Qt::AlignRight )
01027 pt.rx() -= legendSize.width();
01028 else if( relPos.alignment() & Qt::AlignHCenter )
01029 pt.rx() -= 0.5 * legendSize.width();
01030
01031 if( relPos.alignment() & Qt::AlignBottom )
01032 pt.ry() -= legendSize.height();
01033 else if( relPos.alignment() & Qt::AlignVCenter )
01034 pt.ry() -= 0.5 * legendSize.height();
01035 }
01036 qDebug() << pt << endl;
01037 legend->move( static_cast<int>(pt.x()), static_cast<int>(pt.y()) );
01038 }
01039 }
01040 }
01041
01042
01043 void Chart::paintEvent( QPaintEvent* )
01044 {
01045 QPainter painter( this );
01046
01047 if( size() != d->currentLayoutSize ){
01048 d->resizeLayout( size() );
01049 reLayoutFloatingLegends();
01050 }
01051
01052
01053
01054 d->paintAll( &painter );
01055 }
01056
01057 void Chart::addHeaderFooter( HeaderFooter* headerFooter )
01058 {
01059 d->headerFooters.append( headerFooter );
01060 headerFooter->setParent( this );
01061 connect( headerFooter, SIGNAL( destroyedHeaderFooter( HeaderFooter* ) ),
01062 d, SLOT( slotUnregisterDestroyedHeaderFooter( HeaderFooter* ) ) );
01063 connect( headerFooter, SIGNAL( positionChanged( HeaderFooter* ) ),
01064 d, SLOT( slotRelayout() ) );
01065 d->slotRelayout();
01066 }
01067
01068 void Chart::replaceHeaderFooter( HeaderFooter* headerFooter,
01069 HeaderFooter* oldHeaderFooter_ )
01070 {
01071 if( headerFooter && oldHeaderFooter_ != headerFooter ){
01072 HeaderFooter* oldHeaderFooter = oldHeaderFooter_;
01073 if( d->headerFooters.count() ){
01074 if( ! oldHeaderFooter )
01075 oldHeaderFooter = d->headerFooters.first();
01076 takeHeaderFooter( oldHeaderFooter );
01077 }
01078 delete oldHeaderFooter;
01079 addHeaderFooter( headerFooter );
01080 }
01081 }
01082
01083 void Chart::takeHeaderFooter( HeaderFooter* headerFooter )
01084 {
01085 const int idx = d->headerFooters.indexOf( headerFooter );
01086 if( idx != -1 ){
01087 d->headerFooters.takeAt( idx );
01088 disconnect( headerFooter, SIGNAL( destroyedHeaderFooter( HeaderFooter* ) ),
01089 d, SLOT( slotUnregisterDestroyedHeaderFooter( HeaderFooter* ) ) );
01090 headerFooter->setParent( 0 );
01091 }
01092 d->slotRelayout();
01093 }
01094
01095 HeaderFooter* Chart::headerFooter()
01096 {
01097 if( d->headerFooters.isEmpty() ) {
01098 return 0;
01099 } else {
01100 return d->headerFooters.first();
01101 }
01102 }
01103
01104 HeaderFooterList Chart::headerFooters()
01105 {
01106 return d->headerFooters;
01107 }
01108
01109 void Chart::addLegend( Legend* legend )
01110 {
01111 if( ! legend ) return;
01112
01113
01114 d->legends.append( legend );
01115 legend->setParent( this );
01116
01117 TextAttributes textAttrs( legend->textAttributes() );
01118
01119 KDChart::Measure measure( textAttrs.fontSize() );
01120 measure.setRelativeMode( this, KDChartEnums::MeasureOrientationMinimum );
01121 measure.setValue( 20 );
01122 textAttrs.setFontSize( measure );
01123 legend->setTextAttributes( textAttrs );
01124
01125 textAttrs = legend->titleTextAttributes();
01126 measure.setRelativeMode( this, KDChartEnums::MeasureOrientationMinimum );
01127 measure.setValue( 24 );
01128 textAttrs.setFontSize( measure );
01129
01130 legend->setTitleTextAttributes( textAttrs );
01131
01132 legend->setReferenceArea( this );
01133
01134
01135
01136
01137
01138
01139
01140
01141
01142
01143
01144
01145 connect( legend, SIGNAL( destroyedLegend( Legend* ) ),
01146 d, SLOT( slotUnregisterDestroyedLegend( Legend* ) ) );
01147 connect( legend, SIGNAL( positionChanged( AbstractAreaWidget* ) ),
01148 d, SLOT( slotLayoutPlanes() ) );
01149 connect( legend, SIGNAL( propertiesChanged() ),this, SIGNAL( propertiesChanged() ) );
01150 legend->setVisible( true );
01151 d->slotRelayout();
01152 }
01153
01154
01155 void Chart::replaceLegend( Legend* legend, Legend* oldLegend_ )
01156 {
01157 if( legend && oldLegend_ != legend ){
01158 Legend* oldLegend = oldLegend_;
01159 if( d->legends.count() ){
01160 if( ! oldLegend )
01161 oldLegend = d->legends.first();
01162 takeLegend( oldLegend );
01163 }
01164 delete oldLegend;
01165 addLegend( legend );
01166 }
01167 }
01168
01169 void Chart::takeLegend( Legend* legend )
01170 {
01171 const int idx = d->legends.indexOf( legend );
01172 if( idx != -1 ){
01173 d->legends.takeAt( idx );
01174 disconnect( legend, SIGNAL( destroyedLegend( Legend* ) ),
01175 d, SLOT( slotUnregisterDestroyedLegend( Legend* ) ) );
01176 legend->setParent( 0 );
01177 }
01178 d->slotRelayout();
01179 }
01180
01181 Legend* Chart::legend()
01182 {
01183 if ( d->legends.isEmpty() )
01184 {
01185 return 0;
01186 } else {
01187 return d->legends.first();
01188 }
01189 }
01190
01191 LegendList Chart::legends()
01192 {
01193 return d->legends;
01194 }
01195
01196
01197 void Chart::mousePressEvent( QMouseEvent* event )
01198 {
01199 const QPoint pos = mapFromGlobal( event->globalPos() );
01200
01201 KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes )
01202 {
01203 if ( plane->geometry().contains( event->pos() ) )
01204 {
01205 if ( plane->diagrams().size() > 0 )
01206 {
01207 QMouseEvent ev( QEvent::MouseButtonPress, pos, event->globalPos(),
01208 event->button(), event->buttons(),
01209 event->modifiers() );
01210
01211 plane->mousePressEvent( &ev );
01212 d->mouseClickedPlanes.append( plane );
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
01364
01365
01366
01367
01368
01369
01370
01371
01372
01373
01374
01375
01376
01377
01378
01379
01380
01381
01382
01383
01384
01385
01386
01387
01388 void Chart::mouseDoubleClickEvent( QMouseEvent* event )
01389 {
01390 const QPoint pos = mapFromGlobal( event->globalPos() );
01391
01392 KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes )
01393 {
01394 if ( plane->geometry().contains( event->pos() ) )
01395 {
01396 if ( plane->diagrams().size() > 0 )
01397 {
01398 QMouseEvent ev( QEvent::MouseButtonPress, pos, event->globalPos(),
01399 event->button(), event->buttons(),
01400 event->modifiers() );
01401 plane->mouseDoubleClickEvent( &ev );
01402 }
01403 }
01404 }
01405 }
01406
01407 void Chart::mouseMoveEvent( QMouseEvent* event )
01408 {
01409 QSet< AbstractCoordinatePlane* > eventReceivers = QSet< AbstractCoordinatePlane* >::fromList( d->mouseClickedPlanes );
01410
01411 KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes )
01412 {
01413 if( plane->geometry().contains( event->pos() ) )
01414 {
01415 if( plane->diagrams().size() > 0 )
01416 {
01417 eventReceivers.insert( plane );
01418 }
01419 }
01420 }
01421
01422 const QPoint pos = mapFromGlobal( event->globalPos() );
01423
01424 KDAB_FOREACH( AbstractCoordinatePlane* plane, eventReceivers )
01425 {
01426 QMouseEvent ev( QEvent::MouseMove, pos, event->globalPos(),
01427 event->button(), event->buttons(),
01428 event->modifiers() );
01429 plane->mouseMoveEvent( &ev );
01430 }
01431 }
01432
01433 void Chart::mouseReleaseEvent( QMouseEvent* event )
01434 {
01435 QSet< AbstractCoordinatePlane* > eventReceivers = QSet< AbstractCoordinatePlane* >::fromList( d->mouseClickedPlanes );
01436
01437 KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes )
01438 {
01439 if ( plane->geometry().contains( event->pos() ) )
01440 {
01441 if( plane->diagrams().size() > 0 )
01442 {
01443 eventReceivers.insert( plane );
01444 }
01445 }
01446 }
01447
01448 const QPoint pos = mapFromGlobal( event->globalPos() );
01449
01450 KDAB_FOREACH( AbstractCoordinatePlane* plane, eventReceivers )
01451 {
01452 QMouseEvent ev( QEvent::MouseButtonRelease, pos, event->globalPos(),
01453 event->button(), event->buttons(),
01454 event->modifiers() );
01455 plane->mouseReleaseEvent( &ev );
01456 }
01457
01458 d->mouseClickedPlanes.clear();
01459 }