# KDChartChart.cpp

Go to the documentation of this file.

    00001 /****************************************************************************
    00002  ** Copyright (C) 2006 Klar�vdalens Datakonsult AB.  All rights reserved.
    00003  **
    00004  ** This file is part of the KD Chart library.
    00005  **
    00006  ** This file may be distributed and/or modified under the terms of the
    00007  ** GNU General Public License version 2 as published by the Free Software
    00008  ** Foundation and appearing in the file LICENSE.GPL included in the
    00009  ** packaging of this file.
    00010  **
    00011  ** Licensees holding valid commercial KD Chart licenses may use this file in
    00012  ** accordance with the KD Chart Commercial License Agreement provided with
    00013  ** the Software.
    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  ** See http://www.kdab.net/kdchart for
    00019  **   information about KDChart Commercial License Agreements.
    00020  **
    00021  ** Contact info@kdab.net if any conditions of this
    00022  ** licensing are not clear to you.
    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 // Layout widgets even if they are not visible
    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     /*reimp*/ bool isEmpty() const {
    00068         QWidget* w = const_cast<MyWidgetItem *>(this)->widget();
    00069         // legend->hide() should indeed hide the legend,
    00070         // but a legend in a chart that hasn't been shown yet isn't hidden
    00071         // (as can happen when using Chart::paint() without showing the chart)
    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         // for now, there are only two types of Header/Footer,
    00155         // we use a pointer to the right layout, depending on the type():
    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 ); // all types need to be handled
    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     // To support more than one Legend, we first collect them all
    00228     // in little lists: one list per grid position.
    00229     // Since the dataAndLegendLayout is a 3x3 grid, we need 9 little lists.
    00230     QList<Legend*> infos[3][3];
    00231
    00232     Q_FOREACH( Legend *legend, legends ) {
    00233
    00234         legend->needSizeHint(); // we'll lay it out soon
    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     // We have collected all legend information,
    00271     // so we can design their layout now.
    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                     // We have more than one legend in the same cell
    00287                     // of the big dataAndLegendLayout grid.
    00288                     //
    00289                     // So we need to find out, if they are aligned the
    00290                     // same way:
    00291                     // Those legends, that are aligned the same way, will be drawn
    00292                     // leftbound, on top of each other, in a little VBoxLayout.
    00293                     //
    00294                     // If not al of the legends are aligned the same way,
    00295                     // there will be a grid with 3 cells: for left/mid/right side
    00296                     // (or top/mid/bottom side, resp.) legends
    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     /* There are two ways in which planes can be caused to interact in
    00348      * where they are put layouting wise: The first is the reference plane. If
    00349      * such a reference plane is set, on a plane, it will use the same cell in the
    00350      * layout as that one. In addition to this, planes can share an axis. In that case
    00351      * they will be layed out in relation to each other as suggested by the position
    00352      * of the axis. If, for example Plane1 and Plane2 share an axis at position Left,
    00353      * that will result in the layout: Axis Plane1 Plane 2, vertically. If Plane1
    00354      * also happens to be Plane2's referece plane, both planes are drawn over each
    00355      * other. The reference plane concept allows two planes to share the same space
    00356      * even if neither has any axis, and in case there are shared axis, it is used
    00357      * to decided, whether the planes should be painted on top of each other or
    00358      * layed out vertically or horizontally next to each other. */
    00359     QHash<CartesianAxis*, AxisInfo> axisInfos;
    00360     QHash<AbstractCoordinatePlane*, PlaneInfo> planeInfos;
    00361     Q_FOREACH(AbstractCoordinatePlane* plane, coordinatePlanes )
    00362     {
    00363         PlaneInfo p;
    00364         // first check if we share space with another plane
    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                     /* If this is the first time we see this axis, add it, with the
    00376                      * current plane. The first plane added to the chart that has
    00377                      * the axis associated with it thus "owns" it, and decides about
    00378                      * layout. */
    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; // we don't want duplicates, only shared
    00385
    00386                     /* The user expects diagrams to be added on top, and to the right
    00387                      * so that horizontally we need to move the new diagram, vertically
    00388                      * the reference one. */
    00389                     PlaneInfo pi = planeInfos[plane];
    00390                     // plane-to-plane linking overrides linking via axes
    00391                     if ( !pi.referencePlane ) {
    00392                         // we're not the first plane to see this axis, mark us as a slave
    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         // Create a new grid layout for each plane that has no reference.
    00410         p = planeInfos[plane];
    00411         if ( p.referencePlane == 0 ) {
    00412             p.gridLayout = new QGridLayout();
    00413             // TESTING(khz): set the margin of all of the layouts to Zero
    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         // TESTING(khz): set the margin of all of the layouts to Zero
    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     //qDebug() << "KDChart::Chart is layouting the planes";
    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     //hint: The direction is configurable by the user now, as
    00463     //      we are using a QBoxLayout rather than a QVBoxLayout.  (khz, 2007/04/25)
    00464     planesLayout = new QBoxLayout( oldPlanesDirection );
    00465
    00466     // TESTING(khz): set the margin of all of the layouts to Zero
    00467 #if defined SET_ALL_MARGINS_TO_ZERO
    00468     planesLayout->setMargin(0);
    00469 #endif
    00470     planesLayout->setObjectName( QString::fromLatin1( "planesLayout" ) );
    00471
    00472     /* First go through all planes and all axes and figure out whether the planes
    00473      * need to coordinate. If they do, they share a grid layout, if not, each
    00474      * get their own. See buildPlaneLayoutInfos() for more details. */
    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         //qDebug() << "processing plane at column" << column << "and row" << row;
    00483         QGridLayout *planeLayout = pi.gridLayout;
    00484         if ( !planeLayout ) {
    00485             // this plane is sharing an axis with another one, so use
    00486             // the grid of that one as well
    00487             planeLayout = planeInfos[pi.referencePlane].gridLayout;
    00488         } else {
    00489             planesLayout->addLayout( planeLayout );
    00490         }
    00491         Q_ASSERT( planeLayout );
    00492         /* Put the plane in the center of the layout. If this is our own, that's
    00493          * the middle of the layout, if we are sharing, it's a cell in the center
    00494          * column of the shared grid. */
    00495         planeLayoutItems << plane;
    00496         plane->setParentLayout( planeLayout );
    00497         planeLayout->addItem( plane, row, column, 1, 1, 0 );
    00498         //qDebug() << "Chart slotLayoutPlanes() calls planeLayout->addItem("<< row << column << ")";
    00499         planeLayout->setRowStretch(    row,    2 );
    00500         planeLayout->setColumnStretch( column, 2 );
    00501
    00502         // These four layouts will be instantiated in the following loop,
    00503         // if they are needed.
    00504         // They will then be passed to the auto-spacer items after the loop.
    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             //qDebug() << "--------------- diagram ???????????????????? -----------------";
    00515             if( !diagram ) continue;  // FIXME polar ?
    00516             //qDebug() << "--------------- diagram ! ! ! ! ! ! ! ! ! !  -----------------";
    00517             // collect all axes of a kind into sublayouts
    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             //leftAxesLayout->setSizeConstraint( QLayout::SetFixedSize );
    00528
    00529             KDAB_FOREACH( CartesianAxis* axis, diagram->axes() ) {
    00530                 if ( axisInfos.contains( axis ) ) continue; // already layed this one out
    00531                 Q_ASSERT ( axis );
    00532                 //qDebug() << "--------------- axis added to planeLayoutItems  -----------------";
    00533                 planeLayoutItems << axis;
    00534                 /*
    00535                 // Unused code trying to use a push-model: This did not work
    00536                 // since we can not re-layout the planes each time when
    00537                 // Qt layouting is calling sizeHint()
    00538                 connect( axis, SIGNAL( needAdjustLeftRightColumnsForOverlappingLabels(
    00539                                 CartesianAxis*, int, int ) ),
    00540                          this, SLOT( slotAdjustLeftRightColumnsForOverlappingLabels(
    00541                                  CartesianAxis*, int, int ) ) );
    00542                 connect( axis, SIGNAL( needAdjustTopBottomRowsForOverlappingLabels(
    00543                                 CartesianAxis*, int, int ) ),
    00544                          this, SLOT( slotAdjustTopBottomRowsForOverlappingLabels(
    00545                                  CartesianAxis*, int, int ) ) );
    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             /* Put each stack of axes-layouts in the cells surrounding the
    00573              * associated plane. We are laying out in the oder the planes
    00574              * were added, and the first one gets to lay out shared axes.
    00575              * Private axes go here as well, of course. */
    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                 //planeLayout->setRowStretch(    row, 0 );
    00583                 //planeLayout->setColumnStretch( 0,   0 );
    00584             }
    00585             if ( !rightAxesLayout->parent() )
    00586                 planeLayout->addLayout( rightAxesLayout,  row,     column + 1);
    00587         }
    00588
    00589         // use up to four auto-spacer items in the corners around the diagrams:
    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     // re-add our grid(s) to the chart's layout
    00607     if ( dataAndLegendLayout ){
    00608         dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
    00609         dataAndLegendLayout->setRowStretch(    1, 1000 );
    00610         dataAndLegendLayout->setColumnStretch( 1, 1000 );
    00611     }
    00612
    00613     slotRelayout();
    00614     //qDebug() << "KDChart::Chart finished layouting the planes.";
    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     // layout for the planes is handled separately, so we don't want to delete it here
    00632     if ( dataAndLegendLayout) {
    00633         dataAndLegendLayout->removeItem( planesLayout );
    00634         planesLayout->setParent( 0 );
    00635     }
    00636     // nuke the old bunch
    00637     delete layout;
    00638
    00639     // The HBox d->layout provides the left and right global leadings
    00640     layout = new QHBoxLayout( w );
    00641     // TESTING(khz): set the margin of all of the layouts to Zero
    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     // The vLayout provides top and bottom global leadings and lays
    00649     // out headers/footers and the data area.
    00650     vLayout = new QVBoxLayout();
    00651     // TESTING(khz): set the margin of all of the layouts to Zero
    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     // 1. the gap above the top edge of the headers area
    00662     vLayout->addSpacing( globalLeadingTop );
    00663     // 2. the header(s) area
    00664     headerLayout = new QGridLayout();
    00665     // TESTING(khz): set the margin of all of the layouts to Zero
    00666 #if defined SET_ALL_MARGINS_TO_ZERO
    00667     headerLayout->setMargin(0);
    00668 #endif
    00669     vLayout->addLayout( headerLayout );
    00670     // 3. the area containing coordinate plane(s), axes, legend(s)
    00671     dataAndLegendLayout = new QGridLayout();
    00672     // TESTING(khz): set the margin of all of the layouts to Zero
    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     // 4. the footer(s) area
    00679     footerLayout = new QGridLayout();
    00680     // TESTING(khz): set the margin of all of the layouts to Zero
    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     // 5. the gap below the bottom edge of the headers area
    00687     vLayout->addSpacing( globalLeadingBottom );
    00688
    00689     // the data+axes area
    00690     dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
    00691     dataAndLegendLayout->setRowStretch(    1, 1 );
    00692     dataAndLegendLayout->setColumnStretch( 1, 1 );
    00693
    00694     //qDebug() << "w->rect()" << w->rect();
    00695 }
    00696
    00697 void Chart::Private::slotRelayout()
    00698 {
    00699     //qDebug() << "Chart relayouting started.";
    00700     createLayouts( chart );
    00701
    00702     layoutHeadersAndFooters();
    00703     layoutLegends();
    00704
    00705     // This triggers the qlayout, see QBoxLayout::setGeometry
    00706     // The geometry is not necessarily w->rect(), when using paint(), this is why
    00707     // we don't call layout->activate().
    00708     const QRect geo( QRect( 0, 0, currentLayoutSize.width(), currentLayoutSize.height() ) );
    00709     if( geo.isValid() && geo != layout->geometry() ){
    00710         //qDebug() << "Chart slotRelayout() adjusting geometry to" << geo;
    00711         //if( coordinatePlanes.count() )
    00712         //    qDebug() << "           plane geo before" << coordinatePlanes.first()->geometry();
    00713         layout->setGeometry( geo );
    00714         //if( coordinatePlanes.count() ) {
    00715         //    qDebug() << "           plane geo after " << coordinatePlanes.first()->geometry();
    00716         //}
    00717     }
    00718
    00719     // Adapt diagram drawing to the new size
    00720     KDAB_FOREACH (AbstractCoordinatePlane* plane, coordinatePlanes ) {
    00721         plane->layoutDiagrams();
    00722     }
    00723     //qDebug() << "Chart relayouting done.";
    00724 }
    00725
    00726 // Called when the size of the chart changes.
    00727 // So in theory, we only need to adjust geometries.
    00728 // But this also needs to make sure that everything is in place for the first painting.
    00729 void Chart::Private::resizeLayout( const QSize& size )
    00730 {
    00731     currentLayoutSize = size;
    00732     //qDebug() << "Chart::resizeLayout(" << currentLayoutSize << ")";
    00733
    00734 /*
    00735     // We need to make sure that the legend's layouts are populated,
    00736     // so that setGeometry gets proper sizeHints from them and resizes them properly.
    00737     KDAB_FOREACH( Legend *legend, legends ) {
    00738         // This forceRebuild will see a wrong areaGeometry, but I don't care about geometries yet,
    00739         // only about the fact that legends should have their contents populated.
    00740         // -> it would be better to dissociate "building contents" and "resizing" in Legend...
    00741
    00742 //        legend->forceRebuild();
    00743
    00744         legend->resizeLayout( size );
    00745     }
    00746 */
    00747     slotLayoutPlanes(); // includes slotRelayout
    00748
    00749     //qDebug() << "Chart::resizeLayout done";
    00750 }
    00751
    00752
    00753 void Chart::Private::paintAll( QPainter* painter )
    00754 {
    00755     QRect rect( QPoint(0, 0), currentLayoutSize );
    00756
    00757     //qDebug() << this<<"::paintAll() uses layout size" << currentLayoutSize;
    00758
    00759     // Paint the background (if any)
    00760     KDChart::AbstractAreaBase::paintBackgroundAttributes(
    00761         *painter, rect, backgroundAttributes );
    00762     // Paint the frame (if any)
    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 // ******** Chart interface implementation ***********
    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     //qDebug() << "Chart::paint( ..," << target << ")";
    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     // for debugging:
    00960     //painter->setPen(QPen(Qt::blue, 8));
    00961     //painter->drawRect(target.adjusted(12,12,-12,-12));
    00962
    00963     KDAB_FOREACH( Legend *legend, d->legends ) {
    00964         const bool hidden = legend->isHidden() && legend->testAttribute(Qt::WA_WState_ExplicitShowHide);
    00965         if ( !hidden ) {
    00966             //qDebug() << "painting legend at " << legend->geometry();
    00967             legend->paintIntoRect( *painter, legend->geometry() );
    00968             //testing:
    00969             //legend->paintIntoRect( *painter, legend->geometry().adjusted(-100,0,-100,0) );
    00970         }
    00971     }
    00972
    00973     painter->translate( -translation.x(), -translation.y() );
    00974
    00975     GlobalMeasureScaling::instance()->resetFactors();
    00976
    00977     //qDebug() << "KDChart::Chart::paint() done.\n";
    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             // resize the legend
    00996             const QSize legendSize( legend->sizeHint() );
    00997             legend->setGeometry( QRect( legend->geometry().topLeft(), legendSize ) );
    00998             // find the legends corner point (reference point plus any paddings)
    00999             const RelativePosition relPos( legend->floatingPosition() );
    01000             QPointF pt( relPos.calculatedPoint( size() ) );
    01001             qDebug() << pt;
    01002             // calculate the legend's top left point
    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     //FIXME(khz): Paint the background/frame too!
    01032     //            (can we derive Chart from AreaWidget ??)
    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     //qDebug() << "adding the legend";
    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     future: Use relative sizes for the markers too!
    01116
    01117     const uint nMA = Legend::datasetCount();
    01118     for( uint iMA = 0; iMA < nMA; ++iMA ){
    01119         MarkerAttributes ma( legend->markerAttributes( iMA ) );
    01120         ma.setMarkerSize( ... )
    01121         legend->setMarkerAttributes( iMA, ma )
    01122     }
    01123 */
    01124
    01125     connect( legend, SIGNAL( destroyedLegend( Legend* ) ),
    01126              d, SLOT( slotUnregisterDestroyedLegend( Legend* ) ) );
    01127     connect( legend, SIGNAL( positionChanged( AbstractAreaWidget* ) ),
    01128              d, SLOT( slotLayoutPlanes() ) ); //slotRelayout() ) );
    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 // Unused code trying to use a push-model: This did not work
    01197 // since we can not re-layout the planes each time when
    01198 // Qt layouting is calling sizeHint()
    01199 void Chart::Private::slotAdjustLeftRightColumnsForOverlappingLabels(
    01200         CartesianAxis* axis, int leftOverlap, int rightOverlap)
    01201 {
    01202     const QLayout* axisLayout = axis ? axis->parentLayout() : 0;
    01203
    01204     if( (! leftOverlap && ! rightOverlap) || ! axis || ! axisLayout->parent() )
    01205         return;
    01206
    01207     bool needUpdate = false;
    01208     // access the planeLayout:
    01209     QGridLayout* grid = qobject_cast<QGridLayout*>(axisLayout->parent());
    01210     if( grid ){
    01211         // find the index of the parent layout in the planeLayout:
    01212         int idx = -1;
    01213         for (int i = 0; i < grid->count(); ++i)
    01214             if( grid->itemAt(i) == axisLayout )
    01215                 idx = i;
    01216         // set the min widths of the neighboring column:
    01217         if( idx > -1 ){
    01218             int row, column, rowSpan, columnSpan;
    01219             grid->getItemPosition( idx, &row, &column, &rowSpan, &columnSpan );
    01220             const int leftColumn = column-1;
    01221             const int rightColumn = column+columnSpan;
    01222             // find the left/right axes layouts
    01223             QHBoxLayout* leftAxesLayout=0;
    01224             QHBoxLayout* rightAxesLayout=0;
    01225             for( int i = 0;
    01226                  (!leftAxesLayout || !rightAxesLayout) && i < grid->count();
    01227                  ++i )
    01228             {
    01229                 int r, c, rs, cs;
    01230                 grid->getItemPosition( i, &r, &c, &rs, &cs );
    01231                 if( c+cs-1 == leftColumn )
    01232                     leftAxesLayout = dynamic_cast<QHBoxLayout*>(grid->itemAt(i));
    01233                 if( c == rightColumn )
    01234                     rightAxesLayout = dynamic_cast<QHBoxLayout*>(grid->itemAt(i));
    01235             }
    01236             if( leftAxesLayout ){
    01237                 const int leftColumnMinWidth = leftOverlap;
    01238                 QLayoutItem* item = leftAxesLayout->count()
    01239                         ? dynamic_cast<QLayoutItem*>(leftAxesLayout->itemAt(leftAxesLayout->count()-1))
    01240                     : 0;
    01241                 QSpacerItem* spacer = dynamic_cast<QSpacerItem*>(item);
    01242                 if( spacer ){
    01243                     if( spacer->sizeHint().width() < leftColumnMinWidth ){
    01244                         needUpdate = true;
    01245                         spacer->changeSize(leftColumnMinWidth, 1);
    01246                         qDebug() << "adjusted left spacer->sizeHint().width() to" << spacer->sizeHint().width();
    01247                     }
    01248                 }else{
    01249                     AbstractAxis* axis = dynamic_cast<AbstractAxis*>(item);
    01250                     if( !axis || axis->sizeHint().width() < leftColumnMinWidth ){
    01251                         needUpdate = true;
    01252                         leftAxesLayout->insertSpacing( -1, leftColumnMinWidth );
    01253                         qDebug() << "adjusted column" << leftColumn << "min width to" << leftColumnMinWidth;
    01254                     }
    01255                 }
    01256             }
    01257             if( rightAxesLayout ){
    01258                 const int rightColumnMinWidth = rightOverlap;
    01259                 QLayoutItem* item = rightAxesLayout->count()
    01260                         ? dynamic_cast<QLayoutItem*>(rightAxesLayout->itemAt(0))
    01261                     : 0;
    01262                 QSpacerItem* spacer = dynamic_cast<QSpacerItem*>(item);
    01263                 if( spacer ){
    01264                     if( spacer->sizeHint().width() < rightColumnMinWidth ){
    01265                         needUpdate = true;
    01266                         spacer->changeSize(rightColumnMinWidth, 1);
    01267                         qDebug() << "adjusted right spacer->sizeHint().width() to" << spacer->sizeHint().width();
    01268                     }
    01269                 }else{
    01270                     AbstractAxis* axis = dynamic_cast<AbstractAxis*>(item);
    01271                     if( !axis || axis->sizeHint().width() < rightColumnMinWidth ){
    01272                         needUpdate = true;
    01273                         rightAxesLayout->insertSpacing( 0, rightColumnMinWidth );
    01274                         qDebug() << "adjusted column" << rightColumn << "min width to" << rightColumnMinWidth;
    01275                     }
    01276                 }
    01277             }
    01278         }
    01279     }
    01280     if( needUpdate ){
    01281         ;// do something ...?
    01282     }
    01283 }
    01284
    01285
    01286 void Chart::Private::slotAdjustTopBottomRowsForOverlappingLabels(
    01287         CartesianAxis* axis, int topOverlap, int bottomOverlap)
    01288 {
    01289     const QLayout* axisLayout = axis ? axis->parentLayout() : 0;
    01290
    01291     if( (! topOverlap && ! bottomOverlap) || ! axisLayout || ! axisLayout->parent() )
    01292         return;
    01293
    01294     // access the planeLayout:
    01295     QGridLayout* grid = qobject_cast<QGridLayout*>(axisLayout->parent());
    01296     if( grid ){
    01297             // find the index of the parent layout in the planeLayout:
    01298         int idx = -1;
    01299         for (int i = 0; i < grid->count(); ++i)
    01300             if( grid->itemAt(i) == axisLayout )
    01301                 idx = i;
    01302             // set the min widths of the neighboring column:
    01303         if( idx > -1 ){
    01304             int row, column, rowSpan, columnSpan;
    01305             grid->getItemPosition( idx, &row, &column, &rowSpan, &columnSpan );
    01306             const int topRow = row-1;
    01307             const int bottomRow = row+rowSpan;
    01308                 // find the left/right axes layouts
    01309             QVBoxLayout* topAxesLayout=0;
    01310             QVBoxLayout* bottomAxesLayout=0;
    01311             for( int i = 0;
    01312                  (!topAxesLayout || !bottomAxesLayout) && i < grid->count();
    01313                  ++i )
    01314             {
    01315                 int r, c, rs, cs;
    01316                 grid->getItemPosition( i, &r, &c, &rs, &cs );
    01317                 if( r+rs-1 == topRow )
    01318                     topAxesLayout = dynamic_cast<QVBoxLayout*>(grid->itemAt(i));
    01319                 if( r == bottomRow )
    01320                     bottomAxesLayout = dynamic_cast<QVBoxLayout*>(grid->itemAt(i));
    01321             }
    01322             if( topAxesLayout ){
    01323                 const int topRowMinWidth = topOverlap;
    01324                 QLayoutItem* item = topAxesLayout->count()
    01325                         ? dynamic_cast<QLayoutItem*>(topAxesLayout->itemAt(topAxesLayout->count()-1))
    01326                     : 0;
    01327                 QSpacerItem* spacer = dynamic_cast<QSpacerItem*>(item);
    01328                 if( spacer ){
    01329                     if( spacer->sizeHint().height() < topRowMinWidth ){
    01330                         spacer->changeSize(1, topRowMinWidth);
    01331                         qDebug() << "adjusted top spacer->sizeHint().height() to" << spacer->sizeHint().height();
    01332                     }
    01333                 }else{
    01334                     AbstractAxis* axis = dynamic_cast<AbstractAxis*>(item);
    01335                     if( !axis || axis->sizeHint().height() < topRowMinWidth ){
    01336                         topAxesLayout->insertSpacing( -1, topRowMinWidth );
    01337                         qDebug() << "adjusted row" << topRow << "min height to" << topRowMinWidth;
    01338                     }
    01339                 }
    01340             }
    01341             if( bottomAxesLayout ){
    01342                 const int bottomRowMinWidth = bottomOverlap;
    01343                 QLayoutItem* item = bottomAxesLayout->count()
    01344                         ? dynamic_cast<QLayoutItem*>(bottomAxesLayout->itemAt(0))
    01345                     : 0;
    01346                 QSpacerItem* spacer = dynamic_cast<QSpacerItem*>(item);
    01347                 if( spacer ){
    01348                     if( spacer->sizeHint().height() < bottomRowMinWidth ){
    01349                         spacer->changeSize(1, bottomRowMinWidth);
    01350                         qDebug() << "adjusted bottom spacer->sizeHint().height() to" << spacer->sizeHint().height();
    01351                     }
    01352                 }else{
    01353                     AbstractAxis* axis = dynamic_cast<AbstractAxis*>(item);
    01354                     if( !axis || axis->sizeHint().height() < bottomRowMinWidth ){
    01355                         bottomAxesLayout->insertSpacing( 0, bottomRowMinWidth );
    01356                         qDebug() << "adjusted row" << bottomRow << "min height to" << bottomRowMinWidth;
    01357                     }
    01358                 }
    01359             }
    01360         }
    01361     }
    01362 }
    01363 */