KDChartChart.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002  ** Copyright (C) 2007 Klaralvdalens 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 void Chart::Private::layoutLegends()
00225 {
00226     // To support more than one Legend, we first collect them all
00227     // in little lists: one list per grid position.
00228     // Since the dataAndLegendLayout is a 3x3 grid, we need 9 little lists.
00229     QList<Legend*> infos[3][3];
00230 
00231     Q_FOREACH( Legend *legend, legends ) {
00232 
00233         legend->needSizeHint(); // we'll lay it out soon
00234 
00235         bool bOK = true;
00236         int row, column;
00237         switch( legend->position().value() ) {
00238             case KDChartEnums::PositionNorthWest:  row = 0;  column = 0;
00239                 break;
00240             case KDChartEnums::PositionNorth:      row = 0;  column = 1;
00241                 break;
00242             case KDChartEnums::PositionNorthEast:  row = 0;  column = 2;
00243                 break;
00244             case KDChartEnums::PositionEast:       row = 1;  column = 2;
00245                 break;
00246             case KDChartEnums::PositionSouthEast:  row = 2;  column = 2;
00247                 break;
00248             case KDChartEnums::PositionSouth:      row = 2;  column = 1;
00249                 break;
00250             case KDChartEnums::PositionSouthWest:  row = 2;  column = 0;
00251                 break;
00252             case KDChartEnums::PositionWest:       row = 1;  column = 0;
00253                 break;
00254             case KDChartEnums::PositionCenter:
00255                 qDebug( "Sorry: Legend not shown, because position Center is not supported." );
00256                 bOK = false;
00257                 break;
00258             case KDChartEnums::PositionFloating:
00259                 bOK = false;
00260                 break;
00261             default:
00262                 qDebug( "Sorry: Legend not shown, because of unknown legend position." );
00263                 bOK = false;
00264                 break;
00265         }
00266         if( bOK )
00267             infos[row][column] << legend;
00268     }
00269     // We have collected all legend information,
00270     // so we can design their layout now.
00271     for (int iR = 0; iR < 3; ++iR) {
00272         for (int iC = 0; iC < 3; ++iC) {
00273             QList<Legend*>& list = infos[iR][iC];
00274             const int count = list.size();
00275             switch( count ){
00276             case 0:
00277                 break;
00278             case 1: {
00279                     Legend* legend = list.first();
00280                     dataAndLegendLayout->addItem( new MyWidgetItem(legend),
00281                         iR, iC, 1, 1, legend->alignment() );
00282             }
00283                 break;
00284             default: {
00285                     // We have more than one legend in the same cell
00286                     // of the big dataAndLegendLayout grid.
00287                     //
00288                     // So we need to find out, if they are aligned the
00289                     // same way:
00290                     // Those legends, that are aligned the same way, will be drawn
00291                     // leftbound, on top of each other, in a little VBoxLayout.
00292                     //
00293                     // If not al of the legends are aligned the same way,
00294                     // there will be a grid with 3 cells: for left/mid/right side
00295                     // (or top/mid/bottom side, resp.) legends
00296                     Legend* legend = list.first();
00297                     Qt::Alignment alignment = legend->alignment();
00298                     bool haveSameAlign = true;
00299                     for (int i = 1; i < count; ++i) {
00300                         legend = list.at(i);
00301                         if( alignment != legend->alignment() ){
00302                             haveSameAlign = false;
00303                             break;
00304                         }
00305                     }
00306                     if( haveSameAlign ){
00307                         QVBoxLayout* vLayout = new QVBoxLayout();
00308                         for (int i = 0; i < count; ++i) {
00309                             vLayout->addItem( new MyWidgetItem(list.at(i), Qt::AlignLeft) );
00310                         }
00311                         dataAndLegendLayout->addLayout( vLayout, iR, iC, 1, 1, alignment );
00312                     }else{
00313                         QGridLayout* gridLayout = new QGridLayout();
00314 #define ADD_VBOX_WITH_LEGENDS(row, column, align) \
00315 { \
00316     QVBoxLayout* innerLayout = new QVBoxLayout(); \
00317     for (int i = 0; i < count; ++i) { \
00318         legend = list.at(i); \
00319         if( legend->alignment() == ( align ) ) \
00320             innerLayout->addItem( new MyWidgetItem(legend, Qt::AlignLeft) ); \
00321     } \
00322     gridLayout->addLayout( innerLayout, row, column, ( align  ) ); \
00323 }
00324                         ADD_VBOX_WITH_LEGENDS( 0, 0, Qt::AlignTop     | Qt::AlignLeft )
00325                         ADD_VBOX_WITH_LEGENDS( 0, 1, Qt::AlignTop     | Qt::AlignHCenter )
00326                         ADD_VBOX_WITH_LEGENDS( 0, 2, Qt::AlignTop     | Qt::AlignRight )
00327 
00328                         ADD_VBOX_WITH_LEGENDS( 1, 0, Qt::AlignVCenter | Qt::AlignLeft )
00329                         ADD_VBOX_WITH_LEGENDS( 1, 2, Qt::AlignVCenter | Qt::AlignRight )
00330 
00331                         ADD_VBOX_WITH_LEGENDS( 2, 0, Qt::AlignBottom  | Qt::AlignLeft )
00332                         ADD_VBOX_WITH_LEGENDS( 2, 1, Qt::AlignBottom  | Qt::AlignHCenter )
00333                         ADD_VBOX_WITH_LEGENDS( 2, 2, Qt::AlignBottom  | Qt::AlignRight )
00334 
00335                         dataAndLegendLayout->addLayout( gridLayout, iR, iC, 1, 1 );
00336                     }
00337                 }
00338             }
00339         }
00340     }
00341 }
00342 
00343 
00344 QHash<AbstractCoordinatePlane*, PlaneInfo> Chart::Private::buildPlaneLayoutInfos()
00345 {
00346     /* There are two ways in which planes can be caused to interact in
00347      * where they are put layouting wise: The first is the reference plane. If
00348      * such a reference plane is set, on a plane, it will use the same cell in the
00349      * layout as that one. In addition to this, planes can share an axis. In that case
00350      * they will be layed out in relation to each other as suggested by the position
00351      * of the axis. If, for example Plane1 and Plane2 share an axis at position Left,
00352      * that will result in the layout: Axis Plane1 Plane 2, vertically. If Plane1
00353      * also happens to be Plane2's referece plane, both planes are drawn over each
00354      * other. The reference plane concept allows two planes to share the same space
00355      * even if neither has any axis, and in case there are shared axis, it is used
00356      * to decided, whether the planes should be painted on top of each other or
00357      * layed out vertically or horizontally next to each other. */
00358     QHash<CartesianAxis*, AxisInfo> axisInfos;
00359     QHash<AbstractCoordinatePlane*, PlaneInfo> planeInfos;
00360     Q_FOREACH(AbstractCoordinatePlane* plane, coordinatePlanes )
00361     {
00362         PlaneInfo p;
00363         // first check if we share space with another plane
00364         p.referencePlane = plane->referenceCoordinatePlane();
00365         planeInfos.insert( plane, p );
00366 
00367         Q_FOREACH( AbstractDiagram* abstractDiagram, plane->diagrams() ) {
00368             AbstractCartesianDiagram* diagram =
00369                     dynamic_cast<AbstractCartesianDiagram*> ( abstractDiagram );
00370             if( !diagram ) continue;
00371 
00372             Q_FOREACH( CartesianAxis* axis, diagram->axes() ) {
00373                 if ( !axisInfos.contains( axis ) ) {
00374                     /* If this is the first time we see this axis, add it, with the
00375                      * current plane. The first plane added to the chart that has
00376                      * the axis associated with it thus "owns" it, and decides about
00377                      * layout. */
00378                     AxisInfo i;
00379                     i.plane = plane;
00380                     axisInfos.insert( axis, i );
00381                 } else {
00382                     AxisInfo i = axisInfos[axis];
00383                     if ( i.plane == plane ) continue; // we don't want duplicates, only shared
00384 
00385                     /* The user expects diagrams to be added on top, and to the right
00386                      * so that horizontally we need to move the new diagram, vertically
00387                      * the reference one. */
00388                     PlaneInfo pi = planeInfos[plane];
00389                     // plane-to-plane linking overrides linking via axes
00390                     if ( !pi.referencePlane ) {
00391                         // we're not the first plane to see this axis, mark us as a slave
00392                         pi.referencePlane = i.plane;
00393                         if ( axis->position() == CartesianAxis::Left
00394                             ||  axis->position() == CartesianAxis::Right )
00395                             pi.horizontalOffset += 1;
00396                         planeInfos[plane] = pi;
00397 
00398                         pi = planeInfos[i.plane];
00399                         if ( axis->position() == CartesianAxis::Top
00400                                 || axis->position() == CartesianAxis::Bottom  )
00401                             pi.verticalOffset += 1;
00402 
00403                         planeInfos[i.plane] = pi;
00404                     }
00405                 }
00406             }
00407         }
00408         // Create a new grid layout for each plane that has no reference.
00409         p = planeInfos[plane];
00410         if ( p.referencePlane == 0 ) {
00411             p.gridLayout = new QGridLayout();
00412             // TESTING(khz): set the margin of all of the layouts to Zero
00413 #if defined SET_ALL_MARGINS_TO_ZERO
00414             p.gridLayout->setMargin(0);
00415 #endif
00416             planeInfos[plane] = p;
00417         }
00418     }
00419     return planeInfos;
00420 }
00421 
00422     template <typename T>
00423 static T* findOrCreateLayoutByObjectName( QLayout * parentLayout, const char* name )
00424 {
00425     T *box = qFindChild<T*>( parentLayout, QString::fromLatin1( name ) );
00426     if ( !box ) {
00427         box = new T();
00428         // TESTING(khz): set the margin of all of the layouts to Zero
00429 #if defined SET_ALL_MARGINS_TO_ZERO
00430         box->setMargin(0);
00431 #endif
00432         box->setObjectName( QString::fromLatin1( name ) );
00433         box->setSizeConstraint( QLayout::SetFixedSize );
00434     }
00435     return box;
00436 }
00437 
00438 #if 0
00439 static QVBoxLayout* findOrCreateVBoxLayoutByObjectName( QLayout* parentLayout, const char* name )
00440 {
00441     return findOrCreateLayoutByObjectName<QVBoxLayout>( parentLayout, name );
00442 }
00443 
00444 static QHBoxLayout* findOrCreateHBoxLayoutByObjectName( QLayout* parentLayout, const char* name )
00445 {
00446     return findOrCreateLayoutByObjectName<QHBoxLayout>( parentLayout, name );
00447 }
00448 #endif
00449 
00450 void Chart::Private::slotLayoutPlanes()
00451 {
00452     //qDebug() << "KDChart::Chart is layouting the planes";
00453     const QBoxLayout::Direction oldPlanesDirection =
00454         planesLayout ? planesLayout->direction() : QBoxLayout::TopToBottom;
00455     if ( planesLayout && dataAndLegendLayout )
00456         dataAndLegendLayout->removeItem( planesLayout );
00457 
00458     KDAB_FOREACH( KDChart::AbstractLayoutItem* plane, planeLayoutItems ) {
00459         plane->removeFromParentLayout();
00460     }
00461     planeLayoutItems.clear();
00462     delete planesLayout;
00463     //hint: The direction is configurable by the user now, as
00464     //      we are using a QBoxLayout rather than a QVBoxLayout.  (khz, 2007/04/25)
00465     planesLayout = new QBoxLayout( oldPlanesDirection );
00466 
00467     // TESTING(khz): set the margin of all of the layouts to Zero
00468 #if defined SET_ALL_MARGINS_TO_ZERO
00469     planesLayout->setMargin(0);
00470 #endif
00471     planesLayout->setObjectName( QString::fromLatin1( "planesLayout" ) );
00472 
00473     /* First go through all planes and all axes and figure out whether the planes
00474      * need to coordinate. If they do, they share a grid layout, if not, each
00475      * get their own. See buildPlaneLayoutInfos() for more details. */
00476     QHash<AbstractCoordinatePlane*, PlaneInfo> planeInfos = buildPlaneLayoutInfos();
00477     QHash<AbstractAxis*, AxisInfo> axisInfos;
00478     KDAB_FOREACH( AbstractCoordinatePlane* plane, coordinatePlanes ) {
00479         Q_ASSERT( planeInfos.contains(plane) );
00480         const PlaneInfo pi = planeInfos[plane];
00481         int column = pi.horizontalOffset;
00482         int row = pi.verticalOffset;
00483         //qDebug() << "processing plane at column" << column << "and row" << row;
00484         QGridLayout *planeLayout = pi.gridLayout;
00485         if ( !planeLayout ) {
00486             // this plane is sharing an axis with another one, so use
00487             // the grid of that one as well
00488             planeLayout = planeInfos[pi.referencePlane].gridLayout;
00489         } else {
00490             planesLayout->addLayout( planeLayout );
00491         }
00492         Q_ASSERT( planeLayout );
00493         /* Put the plane in the center of the layout. If this is our own, that's
00494          * the middle of the layout, if we are sharing, it's a cell in the center
00495          * column of the shared grid. */
00496         planeLayoutItems << plane;
00497         plane->setParentLayout( planeLayout );
00498         planeLayout->addItem( plane, row, column, 1, 1, 0 );
00499         //qDebug() << "Chart slotLayoutPlanes() calls planeLayout->addItem("<< row << column << ")";
00500         planeLayout->setRowStretch(    row,    2 );
00501         planeLayout->setColumnStretch( column, 2 );
00502 
00503         // These four layouts will be instantiated in the following loop,
00504         // if they are needed.
00505         // They will then be passed to the auto-spacer items after the loop.
00506         QVBoxLayout *topAxesLayout    = 0;
00507         QVBoxLayout *bottomAxesLayout = 0;
00508         QHBoxLayout *leftAxesLayout   = 0;
00509         QHBoxLayout *rightAxesLayout  = 0;
00510 
00511         KDAB_FOREACH( AbstractDiagram* abstractDiagram, plane->diagrams() )
00512         {
00513             AbstractCartesianDiagram* diagram =
00514                 dynamic_cast<AbstractCartesianDiagram*> ( abstractDiagram );
00515             //qDebug() << "--------------- diagram ???????????????????? -----------------";
00516             if( !diagram ) continue;  // FIXME polar ?
00517             //qDebug() << "--------------- diagram ! ! ! ! ! ! ! ! ! !  -----------------";
00518 
00519             // collect all axes of a kind into sublayouts
00520             topAxesLayout = new QVBoxLayout;
00521             topAxesLayout->setObjectName( QString::fromLatin1( "topAxesLayout" ) );
00522             bottomAxesLayout = new QVBoxLayout;
00523             bottomAxesLayout->setObjectName( QString::fromLatin1( "bottomAxesLayout" ) );
00524             leftAxesLayout = new QHBoxLayout;
00525             leftAxesLayout->setObjectName( QString::fromLatin1( "leftAxesLayout" ) );
00526             rightAxesLayout = new QHBoxLayout;
00527             rightAxesLayout->setObjectName( QString::fromLatin1( "rightAxesLayout" ) );
00528 
00529             //leftAxesLayout->setSizeConstraint( QLayout::SetFixedSize );
00530 
00531             KDAB_FOREACH( CartesianAxis* axis, diagram->axes() ) {
00532                 if ( axisInfos.contains( axis ) ) continue; // already layed this one out
00533                 Q_ASSERT ( axis );
00534                 //qDebug() << "--------------- axis added to planeLayoutItems  -----------------";
00535                 planeLayoutItems << axis;
00536                 /*
00537                 // Unused code trying to use a push-model: This did not work
00538                 // since we can not re-layout the planes each time when
00539                 // Qt layouting is calling sizeHint()
00540                 connect( axis, SIGNAL( needAdjustLeftRightColumnsForOverlappingLabels(
00541                 CartesianAxis*, int, int ) ),
00542                 this, SLOT( slotAdjustLeftRightColumnsForOverlappingLabels(
00543                 CartesianAxis*, int, int ) ) );
00544                 connect( axis, SIGNAL( needAdjustTopBottomRowsForOverlappingLabels(
00545                 CartesianAxis*, int, int ) ),
00546                 this, SLOT( slotAdjustTopBottomRowsForOverlappingLabels(
00547                 CartesianAxis*, int, int ) ) );
00548                 */
00549                 switch ( axis->position() )
00550                 {
00551                     case CartesianAxis::Top:
00552                         axis->setParentLayout( topAxesLayout );
00553                         topAxesLayout->addItem( axis );
00554                         break;
00555                     case CartesianAxis::Bottom:
00556                         axis->setParentLayout( bottomAxesLayout );
00557                         bottomAxesLayout->addItem( axis );
00558                         break;
00559                     case CartesianAxis::Left:
00560                         axis->setParentLayout( leftAxesLayout );
00561                         leftAxesLayout->addItem( axis );
00562                         break;
00563                     case CartesianAxis::Right:
00564                         axis->setParentLayout( rightAxesLayout );
00565                         rightAxesLayout->addItem( axis );
00566                         break;
00567                     default:
00568                         Q_ASSERT_X( false, "Chart::paintEvent",
00569                                 "unknown axis position" );
00570                         break;
00571                 };
00572                 axisInfos.insert( axis, AxisInfo() );
00573             }
00574             /* Put each stack of axes-layouts in the cells surrounding the
00575              * associated plane. We are laying out in the oder the planes
00576              * were added, and the first one gets to lay out shared axes.
00577              * Private axes go here as well, of course. */
00578             if ( !topAxesLayout->parent() )
00579                 planeLayout->addLayout( topAxesLayout,    row - 1, column );
00580             if ( !bottomAxesLayout->parent() )
00581                 planeLayout->addLayout( bottomAxesLayout, row + 1, column );
00582             if ( !leftAxesLayout->parent() ){
00583                 planeLayout->addLayout( leftAxesLayout,   row,     column - 1);
00584                 //planeLayout->setRowStretch(    row, 0 );
00585                 //planeLayout->setColumnStretch( 0,   0 );
00586             }
00587             if ( !rightAxesLayout->parent() )
00588                 planeLayout->addLayout( rightAxesLayout,  row,     column + 1);
00589         }
00590 
00591         // use up to four auto-spacer items in the corners around the diagrams:
00592 #define ADD_AUTO_SPACER_IF_NEEDED( \
00593         spacerRow, spacerColumn, hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ) \
00594         { \
00595             if( hLayout || vLayout ) { \
00596                 AutoSpacerLayoutItem * spacer \
00597                 = new AutoSpacerLayoutItem( hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ); \
00598                 planeLayout->addItem( spacer, spacerRow, spacerColumn, 1, 1 ); \
00599                 spacer->setParentLayout( planeLayout ); \
00600                 planeLayoutItems << spacer; \
00601             } \
00602         }
00603         ADD_AUTO_SPACER_IF_NEEDED( row-1, column-1, false, leftAxesLayout,  false, topAxesLayout )
00604             ADD_AUTO_SPACER_IF_NEEDED( row+1, column-1, true,  leftAxesLayout,  false,  bottomAxesLayout )
00605             ADD_AUTO_SPACER_IF_NEEDED( row-1, column+1, false, rightAxesLayout, true, topAxesLayout )
00606             ADD_AUTO_SPACER_IF_NEEDED( row+1, column+1, true,  rightAxesLayout, true,  bottomAxesLayout )
00607     }
00608     // re-add our grid(s) to the chart's layout
00609     if ( dataAndLegendLayout ){
00610         dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
00611         dataAndLegendLayout->setRowStretch(    1, 1000 );
00612         dataAndLegendLayout->setColumnStretch( 1, 1000 );
00613     }
00614 
00615     slotRelayout();
00616     //qDebug() << "KDChart::Chart finished layouting the planes.";
00617 }
00618 
00619 void Chart::Private::createLayouts( QWidget* w )
00620 {
00621     KDAB_FOREACH( KDChart::TextArea* textLayoutItem, textLayoutItems ) {
00622         textLayoutItem->removeFromParentLayout();
00623     }
00624     textLayoutItems.clear();
00625 
00626     KDAB_FOREACH( KDChart::AbstractArea* layoutItem, layoutItems ) {
00627         layoutItem->removeFromParentLayout();
00628     }
00629     layoutItems.clear();
00630 
00631     removeDummyHeaderFooters();
00632 
00633     // layout for the planes is handled separately, so we don't want to delete it here
00634     if ( dataAndLegendLayout) {
00635         dataAndLegendLayout->removeItem( planesLayout );
00636         planesLayout->setParent( 0 );
00637     }
00638     // nuke the old bunch
00639     delete layout;
00640 
00641     // The HBox d->layout provides the left and right global leadings
00642     layout = new QHBoxLayout( w );
00643     // TESTING(khz): set the margin of all of the layouts to Zero
00644 #if defined SET_ALL_MARGINS_TO_ZERO
00645     layout->setMargin(0);
00646 #endif
00647     layout->setObjectName( QString::fromLatin1( "Chart::Private::layout" ) );
00648     layout->addSpacing( globalLeadingLeft );
00649 
00650     // The vLayout provides top and bottom global leadings and lays
00651     // out headers/footers and the data area.
00652     vLayout = new QVBoxLayout();
00653     // TESTING(khz): set the margin of all of the layouts to Zero
00654 #if defined SET_ALL_MARGINS_TO_ZERO
00655     vLayout->setMargin(0);
00656 #endif
00657     vLayout->setObjectName( QString::fromLatin1( "vLayout" ) );
00658     layout->addLayout( vLayout, 1000 );
00659     layout->addSpacing( globalLeadingRight );
00660 
00661 
00662 
00663     // 1. the gap above the top edge of the headers area
00664     vLayout->addSpacing( globalLeadingTop );
00665     // 2. the header(s) area
00666     headerLayout = new QGridLayout();
00667     // TESTING(khz): set the margin of all of the layouts to Zero
00668 #if defined SET_ALL_MARGINS_TO_ZERO
00669     headerLayout->setMargin(0);
00670 #endif
00671     vLayout->addLayout( headerLayout );
00672     // 3. the area containing coordinate plane(s), axes, legend(s)
00673     dataAndLegendLayout = new QGridLayout();
00674     // TESTING(khz): set the margin of all of the layouts to Zero
00675 #if defined SET_ALL_MARGINS_TO_ZERO
00676     dataAndLegendLayout->setMargin(0);
00677 #endif
00678     dataAndLegendLayout->setObjectName( QString::fromLatin1( "dataAndLegendLayout" ) );
00679     vLayout->addLayout( dataAndLegendLayout, 1000 );
00680     // 4. the footer(s) area
00681     footerLayout = new QGridLayout();
00682     // TESTING(khz): set the margin of all of the layouts to Zero
00683 #if defined SET_ALL_MARGINS_TO_ZERO
00684     footerLayout->setMargin(0);
00685 #endif
00686     footerLayout->setObjectName( QString::fromLatin1( "footerLayout" ) );
00687     vLayout->addLayout( footerLayout );
00688     // 5. the gap below the bottom edge of the headers area
00689     vLayout->addSpacing( globalLeadingBottom );
00690 
00691     // the data+axes area
00692     dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
00693     dataAndLegendLayout->setRowStretch(    1, 1 );
00694     dataAndLegendLayout->setColumnStretch( 1, 1 );
00695 
00696     //qDebug() << "w->rect()" << w->rect();
00697 }
00698 
00699 void Chart::Private::slotRelayout()
00700 {
00701     //qDebug() << "Chart relayouting started.";
00702     createLayouts( chart );
00703 
00704     layoutHeadersAndFooters();
00705     layoutLegends();
00706 
00707     // This triggers the qlayout, see QBoxLayout::setGeometry
00708     // The geometry is not necessarily w->rect(), when using paint(), this is why
00709     // we don't call layout->activate().
00710     const QRect geo( QRect( 0, 0, currentLayoutSize.width(), currentLayoutSize.height() ) );
00711     if( geo.isValid() && geo != layout->geometry() ){
00712         //qDebug() << "Chart slotRelayout() adjusting geometry to" << geo;
00713         //if( coordinatePlanes.count() )
00714         //    qDebug() << "           plane geo before" << coordinatePlanes.first()->geometry();
00715         layout->setGeometry( geo );
00716         //if( coordinatePlanes.count() ) {
00717         //    qDebug() << "           plane geo after " << coordinatePlanes.first()->geometry();
00718         //}
00719     }
00720 
00721     // Adapt diagram drawing to the new size
00722     KDAB_FOREACH (AbstractCoordinatePlane* plane, coordinatePlanes ) {
00723         plane->layoutDiagrams();
00724     }
00725     //qDebug() << "Chart relayouting done.";
00726 }
00727 
00728 // Called when the size of the chart changes.
00729 // So in theory, we only need to adjust geometries.
00730 // But this also needs to make sure that everything is in place for the first painting.
00731 void Chart::Private::resizeLayout( const QSize& size )
00732 {
00733     currentLayoutSize = size;
00734     //qDebug() << "Chart::resizeLayout(" << currentLayoutSize << ")";
00735 
00736     /*
00737     // We need to make sure that the legend's layouts are populated,
00738     // so that setGeometry gets proper sizeHints from them and resizes them properly.
00739     KDAB_FOREACH( Legend *legend, legends ) {
00740     // This forceRebuild will see a wrong areaGeometry, but I don't care about geometries yet,
00741     // only about the fact that legends should have their contents populated.
00742     // -> it would be better to dissociate "building contents" and "resizing" in Legend...
00743 
00744     //        legend->forceRebuild();
00745 
00746     legend->resizeLayout( size );
00747     }
00748     */
00749     slotLayoutPlanes(); // includes slotRelayout
00750 
00751     //qDebug() << "Chart::resizeLayout done";
00752 }
00753 
00754 
00755 void Chart::Private::paintAll( QPainter* painter )
00756 {
00757     QRect rect( QPoint(0, 0), currentLayoutSize );
00758 
00759     //qDebug() << this<<"::paintAll() uses layout size" << currentLayoutSize;
00760 
00761     // Paint the background (if any)
00762     KDChart::AbstractAreaBase::paintBackgroundAttributes(
00763             *painter, rect, backgroundAttributes );
00764     // Paint the frame (if any)
00765     KDChart::AbstractAreaBase::paintFrameAttributes(
00766             *painter, rect, frameAttributes );
00767 
00768     chart->reLayoutFloatingLegends();
00769 
00770     KDAB_FOREACH( KDChart::AbstractArea* layoutItem, layoutItems ) {
00771         layoutItem->paintAll( *painter );
00772     }
00773     KDAB_FOREACH( KDChart::AbstractLayoutItem* planeLayoutItem, planeLayoutItems ) {
00774         planeLayoutItem->paintAll( *painter );
00775     }
00776     KDAB_FOREACH( KDChart::TextArea* textLayoutItem, textLayoutItems ) {
00777         textLayoutItem->paintAll( *painter );
00778     }
00779 }
00780 
00781 // ******** Chart interface implementation ***********
00782 
00783 Chart::Chart ( QWidget* parent )
00784     : QWidget ( parent )
00785     , _d( new Private( this ) )
00786 {
00787 #if defined KDAB_EVAL
00788     EvalDialog::checkEvalLicense( "KD Chart" );
00789 #endif
00790 
00791     FrameAttributes frameAttrs;
00792     frameAttrs.setVisible( true );
00793     frameAttrs.setPen( QPen( Qt::black ) );
00794     frameAttrs.setPadding( 1 );
00795     setFrameAttributes( frameAttrs );
00796 
00797     addCoordinatePlane( new CartesianCoordinatePlane ( this ) );
00798 }
00799 
00800 Chart::~Chart()
00801 {
00802     delete _d;
00803 }
00804 
00805 #define d d_func()
00806 
00807 void Chart::setFrameAttributes( const FrameAttributes &a )
00808 {
00809     d->frameAttributes = a;
00810 }
00811 
00812 FrameAttributes Chart::frameAttributes() const
00813 {
00814     return d->frameAttributes;
00815 }
00816 
00817 void Chart::setBackgroundAttributes( const BackgroundAttributes &a )
00818 {
00819     d->backgroundAttributes = a;
00820 }
00821 
00822 BackgroundAttributes Chart::backgroundAttributes() const
00823 {
00824     return d->backgroundAttributes;
00825 }
00826 
00827 QLayout* Chart::coordinatePlaneLayout()
00828 {
00829     return d->planesLayout;
00830 }
00831 
00832 AbstractCoordinatePlane* Chart::coordinatePlane()
00833 {
00834     if ( d->coordinatePlanes.isEmpty() )
00835     {
00836         qWarning() << "Chart::coordinatePlane: warning: no coordinate plane defined.";
00837         return 0;
00838     } else {
00839         return d->coordinatePlanes.first();
00840     }
00841 }
00842 
00843 CoordinatePlaneList Chart::coordinatePlanes()
00844 {
00845     return d->coordinatePlanes;
00846 }
00847 
00848 void Chart::addCoordinatePlane( AbstractCoordinatePlane* plane )
00849 {
00850     connect( plane, SIGNAL( destroyedCoordinatePlane( AbstractCoordinatePlane* ) ),
00851              d,   SLOT( slotUnregisterDestroyedPlane( AbstractCoordinatePlane* ) ) );
00852     connect( plane, SIGNAL( needUpdate() ),       this,   SLOT( update() ) );
00853     connect( plane, SIGNAL( needRelayout() ),     d,      SLOT( slotRelayout() ) ) ;
00854     connect( plane, SIGNAL( needLayoutPlanes() ), d,      SLOT( slotLayoutPlanes() ) ) ;
00855     connect( plane, SIGNAL( propertiesChanged() ),this, SIGNAL( propertiesChanged() ) );
00856     d->coordinatePlanes.append( plane );
00857     plane->setParent( this );
00858     d->slotLayoutPlanes();
00859 }
00860 
00861 void Chart::replaceCoordinatePlane( AbstractCoordinatePlane* plane,
00862                                     AbstractCoordinatePlane* oldPlane_ )
00863 {
00864     if( plane && oldPlane_ != plane ){
00865         AbstractCoordinatePlane* oldPlane = oldPlane_;
00866         if( d->coordinatePlanes.count() ){
00867             if( ! oldPlane )
00868                 oldPlane = d->coordinatePlanes.first();
00869             takeCoordinatePlane( oldPlane );
00870         }
00871         delete oldPlane;
00872         addCoordinatePlane( plane );
00873     }
00874 }
00875 
00876 void Chart::takeCoordinatePlane( AbstractCoordinatePlane* plane )
00877 {
00878     const int idx = d->coordinatePlanes.indexOf( plane );
00879     if( idx != -1 ){
00880         d->coordinatePlanes.takeAt( idx );
00881         disconnect( plane, SIGNAL( destroyedCoordinatePlane( AbstractCoordinatePlane* ) ),
00882                     d, SLOT( slotUnregisterDestroyedPlane( AbstractCoordinatePlane* ) ) );
00883         plane->removeFromParentLayout();
00884         plane->setParent( 0 );
00885     }
00886     d->slotLayoutPlanes();
00887 }
00888 
00889 void Chart::setGlobalLeading( int left, int top, int right, int bottom )
00890 {
00891     setGlobalLeadingLeft( left );
00892     setGlobalLeadingTop( top );
00893     setGlobalLeadingRight( right );
00894     setGlobalLeadingBottom( bottom );
00895     d->slotRelayout();
00896 }
00897 
00898 void Chart::setGlobalLeadingLeft( int leading )
00899 {
00900     d->globalLeadingLeft = leading;
00901     d->slotRelayout();
00902 }
00903 
00904 int Chart::globalLeadingLeft() const
00905 {
00906     return d->globalLeadingLeft;
00907 }
00908 
00909 void Chart::setGlobalLeadingTop( int leading )
00910 {
00911     d->globalLeadingTop = leading;
00912     d->slotRelayout();
00913 }
00914 
00915 int Chart::globalLeadingTop() const
00916 {
00917     return d->globalLeadingTop;
00918 }
00919 
00920 void Chart::setGlobalLeadingRight( int leading )
00921 {
00922     d->globalLeadingRight = leading;
00923     d->slotRelayout();
00924 }
00925 
00926 int Chart::globalLeadingRight() const
00927 {
00928     return d->globalLeadingRight;
00929 }
00930 
00931 void Chart::setGlobalLeadingBottom( int leading )
00932 {
00933     d->globalLeadingBottom = leading;
00934     d->slotRelayout();
00935 }
00936 
00937 int Chart::globalLeadingBottom() const
00938 {
00939     return d->globalLeadingBottom;
00940 }
00941 
00942 void Chart::paint( QPainter* painter, const QRect& target )
00943 {
00944     if( target.isEmpty() || !painter ) return;
00945     //qDebug() << "Chart::paint( ..," << target << ")";
00946 
00947     GlobalMeasureScaling::setPaintDevice( painter->device() );
00948 
00949     // Output on a widget
00950     if( dynamic_cast< QWidget* >( painter->device() ) != 0 )
00951     {
00952         GlobalMeasureScaling::setFactors(
00953                 static_cast< qreal >( target.width() ) /
00954                 static_cast< qreal >( geometry().size().width() ),
00955                 static_cast< qreal >( target.height() ) /
00956                 static_cast< qreal >( geometry().size().height() ) );
00957     }
00958     // Output onto a QPixmap 
00959     else
00960     {
00961         const qreal resX = static_cast< qreal >( logicalDpiX() ) / static_cast< qreal >( painter->device()->logicalDpiX() );
00962         const qreal resY = static_cast< qreal >( logicalDpiY() ) / static_cast< qreal >( painter->device()->logicalDpiY() );
00963 
00964         GlobalMeasureScaling::setFactors( 
00965                 static_cast< qreal >( target.width() ) /
00966                 static_cast< qreal >( geometry().size().width() ) * resX,
00967                 static_cast< qreal >( target.height() ) /
00968                 static_cast< qreal >( geometry().size().height() ) * resY );
00969     }
00970 
00971 
00972     if( target.size() != d->currentLayoutSize ){
00973         d->resizeLayout( target.size() );
00974     }
00975     const QPoint translation = target.topLeft();
00976     painter->translate( translation );
00977 
00978     d->paintAll( painter );
00979 
00980     // for debugging:
00981     //painter->setPen(QPen(Qt::blue, 8));
00982     //painter->drawRect(target.adjusted(12,12,-12,-12));
00983 
00984     KDAB_FOREACH( Legend *legend, d->legends ) {
00985         const bool hidden = legend->isHidden() && legend->testAttribute(Qt::WA_WState_ExplicitShowHide);
00986         if ( !hidden ) {
00987             //qDebug() << "painting legend at " << legend->geometry();
00988             legend->paintIntoRect( *painter, legend->geometry() );
00989             //testing:
00990             //legend->paintIntoRect( *painter, legend->geometry().adjusted(-100,0,-100,0) );
00991         }
00992     }
00993 
00994     painter->translate( -translation.x(), -translation.y() );
00995 
00996     GlobalMeasureScaling::instance()->resetFactors();
00997 
00998     //qDebug() << "KDChart::Chart::paint() done.\n";
00999 }
01000 
01001 void Chart::resizeEvent ( QResizeEvent * )
01002 {
01003     d->resizeLayout( size() );
01004     KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ){
01005         plane->setGridNeedsRecalculate();
01006     }
01007     reLayoutFloatingLegends();
01008 }
01009 
01010 
01011 void Chart::reLayoutFloatingLegends()
01012 {
01013     KDAB_FOREACH( Legend *legend, d->legends ) {
01014         const bool hidden = legend->isHidden() && legend->testAttribute(Qt::WA_WState_ExplicitShowHide);
01015         if ( legend->position().isFloating() && !hidden ){
01016             // resize the legend
01017             const QSize legendSize( legend->sizeHint() );
01018             legend->setGeometry( QRect( legend->geometry().topLeft(), legendSize ) );
01019             // find the legends corner point (reference point plus any paddings)
01020             const RelativePosition relPos( legend->floatingPosition() );
01021             QPointF pt( relPos.calculatedPoint( size() ) );
01022             qDebug() << pt;
01023             // calculate the legend's top left point
01024             const Qt::Alignment alignTopLeft = Qt::AlignBottom | Qt::AlignLeft;
01025             if( (relPos.alignment() & alignTopLeft) != alignTopLeft ){
01026                 if( relPos.alignment() & Qt::AlignRight )
01027                     pt.rx() -= legendSize.width();
01028                 else if( relPos.alignment() & Qt::AlignHCenter )
01029                     pt.rx() -= 0.5 * legendSize.width();
01030 
01031                 if( relPos.alignment() & Qt::AlignBottom )
01032                     pt.ry() -= legendSize.height();
01033                 else if( relPos.alignment() & Qt::AlignVCenter )
01034                     pt.ry() -= 0.5 * legendSize.height();
01035             }
01036             qDebug() << pt << endl;
01037             legend->move( static_cast<int>(pt.x()), static_cast<int>(pt.y()) );
01038         }
01039     }
01040 }
01041 
01042 
01043 void Chart::paintEvent( QPaintEvent* )
01044 {
01045     QPainter painter( this );
01046 
01047     if( size() != d->currentLayoutSize ){
01048         d->resizeLayout( size() );
01049         reLayoutFloatingLegends();
01050     }
01051 
01052     //FIXME(khz): Paint the background/frame too!
01053     //            (can we derive Chart from AreaWidget ??)
01054     d->paintAll( &painter );
01055 }
01056 
01057 void Chart::addHeaderFooter( HeaderFooter* headerFooter )
01058 {
01059     d->headerFooters.append( headerFooter );
01060     headerFooter->setParent( this );
01061     connect( headerFooter, SIGNAL( destroyedHeaderFooter( HeaderFooter* ) ),
01062              d, SLOT( slotUnregisterDestroyedHeaderFooter( HeaderFooter* ) ) );
01063     connect( headerFooter, SIGNAL( positionChanged( HeaderFooter* ) ),
01064              d, SLOT( slotRelayout() ) );
01065     d->slotRelayout();
01066 }
01067 
01068 void Chart::replaceHeaderFooter( HeaderFooter* headerFooter,
01069                                  HeaderFooter* oldHeaderFooter_ )
01070 {
01071     if( headerFooter && oldHeaderFooter_ != headerFooter ){
01072         HeaderFooter* oldHeaderFooter = oldHeaderFooter_;
01073         if( d->headerFooters.count() ){
01074             if( ! oldHeaderFooter )
01075                 oldHeaderFooter =  d->headerFooters.first();
01076             takeHeaderFooter( oldHeaderFooter );
01077         }
01078         delete oldHeaderFooter;
01079         addHeaderFooter( headerFooter );
01080     }
01081 }
01082 
01083 void Chart::takeHeaderFooter( HeaderFooter* headerFooter )
01084 {
01085     const int idx = d->headerFooters.indexOf( headerFooter );
01086     if( idx != -1 ){
01087         d->headerFooters.takeAt( idx );
01088         disconnect( headerFooter, SIGNAL( destroyedHeaderFooter( HeaderFooter* ) ),
01089                     d, SLOT( slotUnregisterDestroyedHeaderFooter( HeaderFooter* ) ) );
01090         headerFooter->setParent( 0 );
01091     }
01092     d->slotRelayout();
01093 }
01094 
01095 HeaderFooter* Chart::headerFooter()
01096 {
01097     if( d->headerFooters.isEmpty() ) {
01098         return 0;
01099     } else {
01100         return d->headerFooters.first();
01101     }
01102 }
01103 
01104 HeaderFooterList Chart::headerFooters()
01105 {
01106     return d->headerFooters;
01107 }
01108 
01109 void Chart::addLegend( Legend* legend )
01110 {
01111     if( ! legend ) return;
01112 
01113     //qDebug() << "adding the legend";
01114     d->legends.append( legend );
01115     legend->setParent( this );
01116 
01117     TextAttributes textAttrs( legend->textAttributes() );
01118 
01119     KDChart::Measure measure( textAttrs.fontSize() );
01120     measure.setRelativeMode( this, KDChartEnums::MeasureOrientationMinimum );
01121     measure.setValue( 20 );
01122     textAttrs.setFontSize( measure );
01123     legend->setTextAttributes( textAttrs );
01124 
01125     textAttrs = legend->titleTextAttributes();
01126     measure.setRelativeMode( this, KDChartEnums::MeasureOrientationMinimum );
01127     measure.setValue( 24 );
01128     textAttrs.setFontSize( measure );
01129 
01130     legend->setTitleTextAttributes( textAttrs );
01131 
01132     legend->setReferenceArea( this );
01133 
01134 /*
01135     future: Use relative sizes for the markers too!
01136 
01137     const uint nMA = Legend::datasetCount();
01138     for( uint iMA = 0; iMA < nMA; ++iMA ){
01139         MarkerAttributes ma( legend->markerAttributes( iMA ) );
01140         ma.setMarkerSize( ... )
01141         legend->setMarkerAttributes( iMA, ma )
01142     }
01143 */
01144 
01145     connect( legend, SIGNAL( destroyedLegend( Legend* ) ),
01146              d, SLOT( slotUnregisterDestroyedLegend( Legend* ) ) );
01147     connect( legend, SIGNAL( positionChanged( AbstractAreaWidget* ) ),
01148              d, SLOT( slotLayoutPlanes() ) ); //slotRelayout() ) );
01149     connect( legend, SIGNAL( propertiesChanged() ),this, SIGNAL( propertiesChanged() ) );
01150     legend->setVisible( true );
01151     d->slotRelayout();
01152 }
01153 
01154 
01155 void Chart::replaceLegend( Legend* legend, Legend* oldLegend_ )
01156 {
01157     if( legend && oldLegend_ != legend ){
01158         Legend* oldLegend = oldLegend_;
01159         if( d->legends.count() ){
01160             if( ! oldLegend )
01161                 oldLegend = d->legends.first();
01162             takeLegend( oldLegend );
01163         }
01164         delete oldLegend;
01165         addLegend( legend );
01166     }
01167 }
01168 
01169 void Chart::takeLegend( Legend* legend )
01170 {
01171     const int idx = d->legends.indexOf( legend );
01172     if( idx != -1 ){
01173         d->legends.takeAt( idx );
01174         disconnect( legend, SIGNAL( destroyedLegend( Legend* ) ),
01175                     d, SLOT( slotUnregisterDestroyedLegend( Legend* ) ) );
01176         legend->setParent( 0 );
01177     }
01178     d->slotRelayout();
01179 }
01180 
01181 Legend* Chart::legend()
01182 {
01183     if ( d->legends.isEmpty() )
01184     {
01185         return 0;
01186     } else {
01187         return d->legends.first();
01188     }
01189 }
01190 
01191 LegendList Chart::legends()
01192 {
01193     return d->legends;
01194 }
01195 
01196 
01197 void Chart::mousePressEvent( QMouseEvent* event )
01198 {
01199     const QPoint pos = mapFromGlobal( event->globalPos() );
01200 
01201     KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes )
01202     {
01203         if ( plane->geometry().contains( event->pos() ) )
01204         {
01205             if ( plane->diagrams().size() > 0 )
01206             {
01207                 QMouseEvent ev( QEvent::MouseButtonPress, pos, event->globalPos(),
01208                                 event->button(), event->buttons(),
01209                                 event->modifiers() );
01210 
01211                 plane->mousePressEvent( &ev );
01212                 d->mouseClickedPlanes.append( plane );
01213            }
01214        }
01215     }
01216 }
01217 
01218 /*
01219 // Unused code trying to use a push-model: This did not work
01220 // since we can not re-layout the planes each time when
01221 // Qt layouting is calling sizeHint()
01222 void Chart::Private::slotAdjustLeftRightColumnsForOverlappingLabels(
01223         CartesianAxis* axis, int leftOverlap, int rightOverlap)
01224 {
01225     const QLayout* axisLayout = axis ? axis->parentLayout() : 0;
01226 
01227     if( (! leftOverlap && ! rightOverlap) || ! axis || ! axisLayout->parent() )
01228         return;
01229 
01230     bool needUpdate = false;
01231     // access the planeLayout:
01232     QGridLayout* grid = qobject_cast<QGridLayout*>(axisLayout->parent());
01233     if( grid ){
01234         // find the index of the parent layout in the planeLayout:
01235         int idx = -1;
01236         for (int i = 0; i < grid->count(); ++i)
01237             if( grid->itemAt(i) == axisLayout )
01238                 idx = i;
01239         // set the min widths of the neighboring column:
01240         if( idx > -1 ){
01241             int row, column, rowSpan, columnSpan;
01242             grid->getItemPosition( idx, &row, &column, &rowSpan, &columnSpan );
01243             const int leftColumn = column-1;
01244             const int rightColumn = column+columnSpan;
01245             // find the left/right axes layouts
01246             QHBoxLayout* leftAxesLayout=0;
01247             QHBoxLayout* rightAxesLayout=0;
01248             for( int i = 0;
01249                  (!leftAxesLayout || !rightAxesLayout) && i < grid->count();
01250                  ++i )
01251             {
01252                 int r, c, rs, cs;
01253                 grid->getItemPosition( i, &r, &c, &rs, &cs );
01254                 if( c+cs-1 == leftColumn )
01255                     leftAxesLayout = dynamic_cast<QHBoxLayout*>(grid->itemAt(i));
01256                 if( c == rightColumn )
01257                     rightAxesLayout = dynamic_cast<QHBoxLayout*>(grid->itemAt(i));
01258             }
01259             if( leftAxesLayout ){
01260                 const int leftColumnMinWidth = leftOverlap;
01261                 QLayoutItem* item = leftAxesLayout->count()
01262                         ? dynamic_cast<QLayoutItem*>(leftAxesLayout->itemAt(leftAxesLayout->count()-1))
01263                     : 0;
01264                 QSpacerItem* spacer = dynamic_cast<QSpacerItem*>(item);
01265                 if( spacer ){
01266                     if( spacer->sizeHint().width() < leftColumnMinWidth ){
01267                         needUpdate = true;
01268                         spacer->changeSize(leftColumnMinWidth, 1);
01269                         qDebug() << "adjusted left spacer->sizeHint().width() to" << spacer->sizeHint().width();
01270                     }
01271                 }else{
01272                     AbstractAxis* axis = dynamic_cast<AbstractAxis*>(item);
01273                     if( !axis || axis->sizeHint().width() < leftColumnMinWidth ){
01274                         needUpdate = true;
01275                         leftAxesLayout->insertSpacing( -1, leftColumnMinWidth );
01276                         qDebug() << "adjusted column" << leftColumn << "min width to" << leftColumnMinWidth;
01277                     }
01278                 }
01279             }
01280             if( rightAxesLayout ){
01281                 const int rightColumnMinWidth = rightOverlap;
01282                 QLayoutItem* item = rightAxesLayout->count()
01283                         ? dynamic_cast<QLayoutItem*>(rightAxesLayout->itemAt(0))
01284                     : 0;
01285                 QSpacerItem* spacer = dynamic_cast<QSpacerItem*>(item);
01286                 if( spacer ){
01287                     if( spacer->sizeHint().width() < rightColumnMinWidth ){
01288                         needUpdate = true;
01289                         spacer->changeSize(rightColumnMinWidth, 1);
01290                         qDebug() << "adjusted right spacer->sizeHint().width() to" << spacer->sizeHint().width();
01291                     }
01292                 }else{
01293                     AbstractAxis* axis = dynamic_cast<AbstractAxis*>(item);
01294                     if( !axis || axis->sizeHint().width() < rightColumnMinWidth ){
01295                         needUpdate = true;
01296                         rightAxesLayout->insertSpacing( 0, rightColumnMinWidth );
01297                         qDebug() << "adjusted column" << rightColumn << "min width to" << rightColumnMinWidth;
01298                     }
01299                 }
01300             }
01301         }
01302     }
01303     if( needUpdate ){
01304         ;// do something ...?
01305     }
01306 }
01307 
01308 
01309 void Chart::Private::slotAdjustTopBottomRowsForOverlappingLabels(
01310         CartesianAxis* axis, int topOverlap, int bottomOverlap)
01311 {
01312     const QLayout* axisLayout = axis ? axis->parentLayout() : 0;
01313 
01314     if( (! topOverlap && ! bottomOverlap) || ! axisLayout || ! axisLayout->parent() )
01315         return;
01316 
01317     // access the planeLayout:
01318     QGridLayout* grid = qobject_cast<QGridLayout*>(axisLayout->parent());
01319     if( grid ){
01320             // find the index of the parent layout in the planeLayout:
01321         int idx = -1;
01322         for (int i = 0; i < grid->count(); ++i)
01323             if( grid->itemAt(i) == axisLayout )
01324                 idx = i;
01325             // set the min widths of the neighboring column:
01326         if( idx > -1 ){
01327             int row, column, rowSpan, columnSpan;
01328             grid->getItemPosition( idx, &row, &column, &rowSpan, &columnSpan );
01329             const int topRow = row-1;
01330             const int bottomRow = row+rowSpan;
01331                 // find the left/right axes layouts
01332             QVBoxLayout* topAxesLayout=0;
01333             QVBoxLayout* bottomAxesLayout=0;
01334             for( int i = 0;
01335                  (!topAxesLayout || !bottomAxesLayout) && i < grid->count();
01336                  ++i )
01337             {
01338                 int r, c, rs, cs;
01339                 grid->getItemPosition( i, &r, &c, &rs, &cs );
01340                 if( r+rs-1 == topRow )
01341                     topAxesLayout = dynamic_cast<QVBoxLayout*>(grid->itemAt(i));
01342                 if( r == bottomRow )
01343                     bottomAxesLayout = dynamic_cast<QVBoxLayout*>(grid->itemAt(i));
01344             }
01345             if( topAxesLayout ){
01346                 const int topRowMinWidth = topOverlap;
01347                 QLayoutItem* item = topAxesLayout->count()
01348                         ? dynamic_cast<QLayoutItem*>(topAxesLayout->itemAt(topAxesLayout->count()-1))
01349                     : 0;
01350                 QSpacerItem* spacer = dynamic_cast<QSpacerItem*>(item);
01351                 if( spacer ){
01352                     if( spacer->sizeHint().height() < topRowMinWidth ){
01353                         spacer->changeSize(1, topRowMinWidth);
01354                         qDebug() << "adjusted top spacer->sizeHint().height() to" << spacer->sizeHint().height();
01355                     }
01356                 }else{
01357                     AbstractAxis* axis = dynamic_cast<AbstractAxis*>(item);
01358                     if( !axis || axis->sizeHint().height() < topRowMinWidth ){
01359                         topAxesLayout->insertSpacing( -1, topRowMinWidth );
01360                         qDebug() << "adjusted row" << topRow << "min height to" << topRowMinWidth;
01361                     }
01362                 }
01363             }
01364             if( bottomAxesLayout ){
01365                 const int bottomRowMinWidth = bottomOverlap;
01366                 QLayoutItem* item = bottomAxesLayout->count()
01367                         ? dynamic_cast<QLayoutItem*>(bottomAxesLayout->itemAt(0))
01368                     : 0;
01369                 QSpacerItem* spacer = dynamic_cast<QSpacerItem*>(item);
01370                 if( spacer ){
01371                     if( spacer->sizeHint().height() < bottomRowMinWidth ){
01372                         spacer->changeSize(1, bottomRowMinWidth);
01373                         qDebug() << "adjusted bottom spacer->sizeHint().height() to" << spacer->sizeHint().height();
01374                     }
01375                 }else{
01376                     AbstractAxis* axis = dynamic_cast<AbstractAxis*>(item);
01377                     if( !axis || axis->sizeHint().height() < bottomRowMinWidth ){
01378                         bottomAxesLayout->insertSpacing( 0, bottomRowMinWidth );
01379                         qDebug() << "adjusted row" << bottomRow << "min height to" << bottomRowMinWidth;
01380                     }
01381                 }
01382             }
01383         }
01384     }
01385 }
01386 */
01387 
01388 void Chart::mouseDoubleClickEvent( QMouseEvent* event )
01389 {
01390     const QPoint pos = mapFromGlobal( event->globalPos() );
01391 
01392     KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes )
01393     {
01394         if ( plane->geometry().contains( event->pos() ) )
01395         {
01396             if ( plane->diagrams().size() > 0 )
01397             {
01398                 QMouseEvent ev( QEvent::MouseButtonPress, pos, event->globalPos(),
01399                                 event->button(), event->buttons(),
01400                                 event->modifiers() );
01401                 plane->mouseDoubleClickEvent( &ev );
01402             }
01403         }
01404     }
01405 }
01406 
01407 void Chart::mouseMoveEvent( QMouseEvent* event )
01408 {
01409     QSet< AbstractCoordinatePlane* > eventReceivers = QSet< AbstractCoordinatePlane* >::fromList( d->mouseClickedPlanes );
01410 
01411     KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes )
01412     {
01413         if( plane->geometry().contains( event->pos() ) )
01414         {
01415             if( plane->diagrams().size() > 0 )
01416             {
01417                 eventReceivers.insert( plane );
01418             }
01419         }
01420     }
01421 
01422     const QPoint pos = mapFromGlobal( event->globalPos() );
01423 
01424     KDAB_FOREACH( AbstractCoordinatePlane* plane, eventReceivers )
01425     {
01426         QMouseEvent ev( QEvent::MouseMove, pos, event->globalPos(),
01427                          event->button(), event->buttons(),
01428                          event->modifiers() );
01429         plane->mouseMoveEvent( &ev );
01430     }
01431 }
01432 
01433 void Chart::mouseReleaseEvent( QMouseEvent* event )
01434 {
01435     QSet< AbstractCoordinatePlane* > eventReceivers = QSet< AbstractCoordinatePlane* >::fromList( d->mouseClickedPlanes );
01436 
01437     KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes )
01438     {
01439         if ( plane->geometry().contains( event->pos() ) )
01440         {
01441             if( plane->diagrams().size() > 0 )
01442             {
01443                 eventReceivers.insert( plane );
01444             }
01445         }
01446     }
01447 
01448     const QPoint pos = mapFromGlobal( event->globalPos() );
01449 
01450     KDAB_FOREACH( AbstractCoordinatePlane* plane, eventReceivers )
01451     {
01452         QMouseEvent ev( QEvent::MouseButtonRelease, pos, event->globalPos(),
01453                          event->button(), event->buttons(),
01454                          event->modifiers() );
01455         plane->mouseReleaseEvent( &ev );
01456     }
01457 
01458     d->mouseClickedPlanes.clear();
01459 }

Generated on Mon Sep 17 16:16:50 2007 for KD Chart 2 by  doxygen 1.5.1