Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

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 */

Generated on Thu May 10 11:06:25 2007 for KD Chart 2 by doxygen 1.3.6