KD Chart 2  [rev.2.5]
KDChartChart.cpp
Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2012 Klaralvdalens Datakonsult AB.  All rights reserved.
00003 **
00004 ** This file is part of the KD Chart library.
00005 **
00006 ** Licensees holding valid commercial KD Chart licenses may use this file in
00007 ** accordance with the KD Chart Commercial License Agreement provided with
00008 ** the Software.
00009 **
00010 **
00011 ** This file may be distributed and/or modified under the terms of the
00012 ** GNU General Public License version 2 and version 3 as published by the
00013 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
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 ** Contact info@kdab.com if any conditions of this licensing are not
00019 ** clear to you.
00020 **
00021 **********************************************************************/
00022 
00023 #include "KDChartChart.h"
00024 #include "KDChartChart_p.h"
00025 
00026 #include <QList>
00027 #include <QtDebug>
00028 #include <QGridLayout>
00029 #include <QLabel>
00030 #include <QHash>
00031 #include <QToolTip>
00032 #include <QPainter>
00033 #include <QPaintEvent>
00034 #include <QLayoutItem>
00035 #include <QPushButton>
00036 #include <QApplication>
00037 #include <QEvent>
00038 
00039 #include "KDChartCartesianCoordinatePlane.h"
00040 #include "KDChartAbstractCartesianDiagram.h"
00041 #include "KDChartHeaderFooter.h"
00042 #include "KDChartEnums.h"
00043 #include "KDChartLegend.h"
00044 #include "KDChartLayoutItems.h"
00045 #include <KDChartTextAttributes.h>
00046 #include <KDChartMarkerAttributes.h>
00047 #include "KDChartPainterSaver_p.h"
00048 #include "KDChartPrintingParameters.h"
00049 
00050 #include <algorithm>
00051 
00052 #if defined KDAB_EVAL
00053 #include "../evaldialog/evaldialog.h"
00054 #endif
00055 
00056 #include <KDABLibFakes>
00057 
00058 static const Qt::Alignment s_gridAlignments[ 3 ][ 3 ] = { // [ row ][ column ]
00059     { Qt::AlignTop | Qt::AlignLeft,     Qt::AlignTop | Qt::AlignHCenter,     Qt::AlignTop | Qt::AlignRight },
00060     { Qt::AlignVCenter | Qt::AlignLeft, Qt::AlignVCenter | Qt::AlignHCenter, Qt::AlignVCenter | Qt::AlignRight },
00061     { Qt::AlignBottom | Qt::AlignLeft,  Qt::AlignBottom | Qt::AlignHCenter,  Qt::AlignBottom | Qt::AlignRight }
00062 };
00063 
00064 // Layout widgets even if they are not visible
00065 class MyWidgetItem : public QWidgetItem
00066 {
00067 public:
00068     explicit MyWidgetItem(QWidget *w, Qt::Alignment alignment = 0)
00069         : QWidgetItem( w )
00070     {
00071         setAlignment( alignment );
00072     }
00073 
00074     /*reimp*/
00075     bool isEmpty() const {
00076         QWidget* w = const_cast< MyWidgetItem * >( this )->widget();
00077         // legend->hide() should indeed hide the legend,
00078         // but a legend in a chart that hasn't been shown yet isn't hidden
00079         // (as can happen when using Chart::paint() without showing the chart)
00080         return w->isHidden() && w->testAttribute( Qt::WA_WState_ExplicitShowHide );
00081     }
00082 };
00083 
00084 using namespace KDChart;
00085 
00086 void Chart::Private::slotUnregisterDestroyedLegend( Legend *l )
00087 {
00088     legends.removeAll( l );
00089     slotRelayout();
00090 }
00091 
00092 void Chart::Private::slotUnregisterDestroyedHeaderFooter( HeaderFooter* hf )
00093 {
00094     headerFooters.removeAll( hf );
00095     hf->removeFromParentLayout();
00096     textLayoutItems.remove( textLayoutItems.indexOf( hf ) );
00097     slotRelayout();
00098 }
00099 
00100 void Chart::Private::slotUnregisterDestroyedPlane( AbstractCoordinatePlane* plane )
00101 {
00102     coordinatePlanes.removeAll( plane );
00103     Q_FOREACH ( AbstractCoordinatePlane* p, coordinatePlanes ) {
00104         if ( p->referenceCoordinatePlane() == plane) {
00105             p->setReferenceCoordinatePlane( 0 );
00106         }
00107     }
00108     plane->layoutPlanes();
00109 }
00110 
00111 Chart::Private::Private( Chart* chart_ )
00112     : chart( chart_ )
00113     , useNewLayoutSystem( false )
00114     , layout( 0 )
00115     , vLayout( 0 )
00116     , planesLayout( 0 )
00117     , headerLayout( 0 )
00118     , footerLayout( 0 )
00119     , dataAndLegendLayout( 0 )
00120     , globalLeadingLeft( 0 )
00121     , globalLeadingRight( 0 )
00122     , globalLeadingTop( 0 )
00123     , globalLeadingBottom( 0 )
00124 {
00125     for ( int row = 0; row < 3; ++row ) {
00126         for ( int column = 0; column < 3; ++column ) {
00127             for ( int i = 0; i < 2; i++ ) {
00128                 innerHdFtLayouts[ i ][ row ][ column ] = 0;
00129             }
00130         }
00131     }
00132 }
00133 
00134 Chart::Private::~Private()
00135 {
00136 }
00137 
00138 void Chart::Private::layoutHeadersAndFooters()
00139 {
00140     Q_FOREACH( HeaderFooter *hf, headerFooters ) {
00141         Q_ASSERT( hf->type() == HeaderFooter::Header || hf->type() == HeaderFooter::Footer );
00142         if ( hf->position() == Position::Unknown ) {
00143             qWarning( "Unknown header/footer position" );
00144             continue;
00145         }
00146 
00147         int row = 1;
00148         if ( hf->position().isNorthSide() ) {
00149             row = 0;
00150         } else if ( hf->position().isSouthSide() ) {
00151             row = 2;
00152         }
00153 
00154         int column = 1;
00155         if ( hf->position().isWestSide() ) {
00156             column = 0;
00157         } else if ( hf->position().isEastSide() ) {
00158             column = 2;
00159         }
00160 
00161         textLayoutItems << hf;
00162         int innerLayoutIdx = hf->type() == HeaderFooter::Header ? 0 : 1;
00163         QVBoxLayout* headerFooterLayout = innerHdFtLayouts[ innerLayoutIdx ][ row ][ column ];
00164         hf->setParentLayout( headerFooterLayout );
00165         hf->setAlignment( s_gridAlignments[ row ][ column ] );
00166         headerFooterLayout->addItem( hf );
00167     }
00168 }
00169 
00170 void Chart::Private::layoutLegends()
00171 {
00172     //qDebug() << "starting Chart::Private::layoutLegends()";
00173     // To support more than one Legend, we first collect them all
00174     // in little lists: one list per grid position.
00175     // Since the dataAndLegendLayout is a 3x3 grid, we need 9 little lists.
00176     QList<Legend*> infoGrid[3][3];
00177 
00178     Q_FOREACH( Legend *legend, legends ) {
00179         legend->needSizeHint(); // we'll lay it out soon
00180 
00181         int row = -1;
00182         int column = -1;
00183         //qDebug() << legend->position().name();
00184         switch ( legend->position().value() ) {
00185         case KDChartEnums::PositionNorthWest:  row = 0;  column = 0;
00186             break;
00187         case KDChartEnums::PositionNorth:      row = 0;  column = 1;
00188             break;
00189         case KDChartEnums::PositionNorthEast:  row = 0;  column = 2;
00190             break;
00191         case KDChartEnums::PositionEast:       row = 1;  column = 2;
00192             break;
00193         case KDChartEnums::PositionSouthEast:  row = 2;  column = 2;
00194             break;
00195         case KDChartEnums::PositionSouth:      row = 2;  column = 1;
00196             break;
00197         case KDChartEnums::PositionSouthWest:  row = 2;  column = 0;
00198             break;
00199         case KDChartEnums::PositionWest:       row = 1;  column = 0;
00200             break;
00201         case KDChartEnums::PositionCenter:
00202             qDebug( "Sorry: Legend not shown because position Center is not supported." );
00203             break;
00204         case KDChartEnums::PositionFloating:
00205             break;
00206         default:
00207             qDebug( "Sorry: Legend not shown because of unknown legend position." );
00208             break;
00209         }
00210         if ( row >= 0 ) {
00211             infoGrid[row][column] << legend;
00212         }
00213     }
00214     // We have collected all legend information,
00215     // so we can design their layout now.
00216     for ( int row = 0; row < 3; ++row ) {
00217         for ( int column = 0; column < 3; ++column ) {
00218             const QList< Legend* >& list = infoGrid[row][column];
00219             if ( list.isEmpty() ) {
00220                 continue;
00221             }
00222 
00223             if ( list.count() == 1 ) {
00224                 Legend* legend = list.first();
00225                 dataAndLegendLayout->addItem( new MyWidgetItem( legend ), row, column,
00226                                               1, 1, legend->alignment() );
00227             } else {
00228                 // We have more than one legend in the same cell of the dataAndLegendLayout grid.
00229                 //
00230                 // Legends with the same alignment will be drawn left-aligned,
00231                 // vertically stacked in a VBoxLayout.
00232                 //
00233                 // If not all legends use the same alignment they are put into a 3x3 grid
00234                 // for left/hCenter/right and top/vCenter/bottom.
00235                 Legend* legend = list.first();
00236                 Qt::Alignment alignment = legend->alignment();
00237                 bool haveSameAlign = true;
00238                 KDAB_FOREACH( Legend* legend, list ) {
00239                     if ( alignment != legend->alignment() ) {
00240                         haveSameAlign = false;
00241                         break;
00242                     }
00243                 }
00244                 if ( haveSameAlign ) {
00245                     QVBoxLayout* vLayout = new QVBoxLayout();
00246                     vLayout->setMargin( 0 );
00247                     Q_FOREACH( Legend* legend, list) {
00248                         vLayout->addItem( new MyWidgetItem( legend, Qt::AlignLeft ) );
00249                     }
00250                     dataAndLegendLayout->addLayout( vLayout, row, column, 1, 1, alignment );
00251                 } else {
00252                     QGridLayout* grid = new QGridLayout();
00253                     grid->setMargin( 0 );
00254 
00255                     for ( int innerRow = 0; innerRow < 3; innerRow++ ) {
00256                         for ( int innerColumn = 0; innerColumn < 3; innerColumn++ ) {
00257                             Qt::Alignment align = s_gridAlignments[ innerRow ][ innerColumn ];
00258                             QVBoxLayout* innerLayout = new QVBoxLayout();
00259                             KDAB_FOREACH( Legend* legend, list ) {
00260                                 if ( legend->alignment() == align ) {
00261                                     innerLayout->addItem( new MyWidgetItem( legend, Qt::AlignLeft ) );
00262                                 }
00263                             }
00264                             grid->addLayout( innerLayout, innerRow, innerColumn, align );
00265                         }
00266                     }
00267 
00268                     dataAndLegendLayout->addLayout( grid, row, column, 1, 1 );
00269                 }
00270             }
00271         }
00272     }
00273     //qDebug() << "finished Chart::Private::layoutLegends()";
00274 }
00275 enum VisitorState{ Visited, Unknown };
00276 struct ConnectedComponentsComparator{
00277     bool operator()( const LayoutGraphNode *lhs, const LayoutGraphNode *rhs ) const
00278     {
00279         return lhs->priority < rhs->priority;
00280     }
00281 };
00282 
00283 static QVector< LayoutGraphNode* > getPrioritySortedConnectedComponents( QVector< LayoutGraphNode* > &nodeList )
00284 {
00285     QVector< LayoutGraphNode* >connectedComponents;
00286     QHash< LayoutGraphNode*, VisitorState > visitedComponents;
00287     Q_FOREACH ( LayoutGraphNode* node, nodeList )
00288         visitedComponents[ node ] = Unknown;
00289     for ( int i = 0; i < nodeList.size(); ++i )
00290     {
00291         LayoutGraphNode *curNode = nodeList[ i ];
00292         LayoutGraphNode *representativeNode = curNode;
00293         if ( visitedComponents[ curNode ] != Visited )
00294         {
00295             QStack< LayoutGraphNode* > stack;
00296             stack.push( curNode );
00297             while ( !stack.isEmpty() )
00298             {
00299                 curNode = stack.pop();
00300                 Q_ASSERT( visitedComponents[ curNode ] != Visited );
00301                 visitedComponents[ curNode ] = Visited;
00302                 if ( curNode->bottomSuccesor && visitedComponents[ curNode->bottomSuccesor ] != Visited )
00303                     stack.push( curNode->bottomSuccesor );
00304                 if ( curNode->leftSuccesor && visitedComponents[ curNode->leftSuccesor ] != Visited )
00305                     stack.push( curNode->leftSuccesor );
00306                 if ( curNode->sharedSuccesor && visitedComponents[ curNode->sharedSuccesor ] != Visited )
00307                     stack.push( curNode->sharedSuccesor );
00308                 if ( curNode->priority < representativeNode->priority )
00309                     representativeNode = curNode;
00310             }
00311             connectedComponents.append( representativeNode );
00312         }
00313     }
00314     std::sort( connectedComponents.begin(), connectedComponents.end(), ConnectedComponentsComparator() );
00315     return connectedComponents;
00316 }
00317 
00318 struct PriorityComparator{
00319 public:
00320     PriorityComparator( QHash< AbstractCoordinatePlane*, LayoutGraphNode* > mapping )
00321         : m_mapping( mapping )
00322     {}
00323     bool operator() ( AbstractCoordinatePlane *lhs, AbstractCoordinatePlane *rhs ) const
00324     {
00325         const LayoutGraphNode *lhsNode = m_mapping[ lhs ];
00326         Q_ASSERT( lhsNode );
00327         const LayoutGraphNode *rhsNode = m_mapping[ rhs ];
00328         Q_ASSERT( rhsNode );
00329         return lhsNode->priority < rhsNode->priority;
00330     }
00331 
00332     const QHash< AbstractCoordinatePlane*, LayoutGraphNode* > m_mapping;
00333 };
00334 
00335 void checkExistingAxes( LayoutGraphNode* node )
00336 {
00337     if ( node && node->diagramPlane && node->diagramPlane->diagram() )
00338     {
00339         AbstractCartesianDiagram *diag = qobject_cast< AbstractCartesianDiagram* >( node->diagramPlane->diagram() );
00340         if ( diag )
00341         {
00342             Q_FOREACH( const CartesianAxis* axis, diag->axes() )
00343             {
00344                 switch( axis->position() )
00345                 {
00346                 case( CartesianAxis::Top ):
00347                     node->topAxesLayout = true;
00348                     break;
00349                 case( CartesianAxis::Bottom ):
00350                     node->bottomAxesLayout = true;
00351                     break;
00352                 case( CartesianAxis::Left ):
00353                     node->leftAxesLayout = true;
00354                     break;
00355                 case( CartesianAxis::Right ):
00356                     node->rightAxesLayout = true;
00357                     break;
00358                 }
00359             }
00360         }
00361     }
00362 }
00363 
00364 static void mergeNodeAxisInformation( LayoutGraphNode* lhs, LayoutGraphNode* rhs )
00365 {
00366     lhs->topAxesLayout |= rhs->topAxesLayout;
00367     rhs->topAxesLayout = lhs->topAxesLayout;
00368 
00369     lhs->bottomAxesLayout |= rhs->bottomAxesLayout;
00370     rhs->bottomAxesLayout = lhs->bottomAxesLayout;
00371 
00372     lhs->leftAxesLayout |= rhs->leftAxesLayout;
00373     rhs->leftAxesLayout = lhs->leftAxesLayout;
00374 
00375     lhs->rightAxesLayout |= rhs->rightAxesLayout;
00376     rhs->rightAxesLayout = lhs->rightAxesLayout;
00377 }
00378 
00379 static CoordinatePlaneList findSharingAxisDiagrams( AbstractCoordinatePlane* plane,
00380                                                     const CoordinatePlaneList& list,
00381                                                     Chart::Private::AxisType type,
00382                                                     QVector< CartesianAxis* >* sharedAxes )
00383 {
00384     if ( !plane || !plane->diagram() )
00385         return CoordinatePlaneList();
00386     Q_ASSERT( plane );
00387     Q_ASSERT( plane->diagram() );
00388     CoordinatePlaneList result;
00389     AbstractCartesianDiagram* diagram = qobject_cast< AbstractCartesianDiagram* >( plane->diagram() );
00390     if ( !diagram )
00391         return CoordinatePlaneList();
00392 
00393     QList< CartesianAxis* > axes;
00394     KDAB_FOREACH( CartesianAxis* axis, diagram->axes() ) {
00395         if ( ( type == Chart::Private::Ordinate &&
00396               ( axis->position() == CartesianAxis::Left || axis->position() == CartesianAxis::Right ) )
00397             ||
00398              ( type == Chart::Private::Abscissa &&
00399               ( axis->position() == CartesianAxis::Top || axis->position() == CartesianAxis::Bottom ) ) ) {
00400             axes.append( axis );
00401         }
00402     }
00403     Q_FOREACH( AbstractCoordinatePlane *curPlane, list )
00404     {
00405         AbstractCartesianDiagram* diagram =
00406                 qobject_cast< AbstractCartesianDiagram* > ( curPlane->diagram() );
00407         if ( !diagram )
00408             continue;
00409         Q_FOREACH( CartesianAxis* curSearchedAxis, axes )
00410         {
00411             Q_FOREACH( CartesianAxis* curAxis, diagram->axes() )
00412             {
00413                 if ( curSearchedAxis == curAxis )
00414                 {
00415                     result.append( curPlane );
00416                     if ( !sharedAxes->contains( curSearchedAxis ) )
00417                         sharedAxes->append( curSearchedAxis );
00418                 }
00419             }
00420         }
00421     }
00422 
00423     return result;
00424 }
00425 
00432 QVector< LayoutGraphNode* > Chart::Private::buildPlaneLayoutGraph()
00433 {
00434     QHash< AbstractCoordinatePlane*, LayoutGraphNode* > planeNodeMapping;
00435     QVector< LayoutGraphNode* > allNodes;
00436     // create all nodes and a mapping between plane and nodes
00437     Q_FOREACH( AbstractCoordinatePlane* curPlane, coordinatePlanes )
00438     {
00439         if ( curPlane->diagram() )
00440         {
00441             allNodes.append( new LayoutGraphNode );
00442             allNodes[ allNodes.size() - 1 ]->diagramPlane = curPlane;
00443             allNodes[ allNodes.size() - 1 ]->priority = allNodes.size();
00444             checkExistingAxes( allNodes[ allNodes.size() - 1 ] );
00445             planeNodeMapping[ curPlane ] = allNodes[ allNodes.size() - 1 ];
00446         }
00447     }
00448     // build the graph connections
00449     Q_FOREACH( LayoutGraphNode* curNode, allNodes )
00450     {
00451         QVector< CartesianAxis* > sharedAxes;
00452         CoordinatePlaneList xSharedPlanes = findSharingAxisDiagrams( curNode->diagramPlane, coordinatePlanes, Abscissa, &sharedAxes );
00453         Q_ASSERT( sharedAxes.size() < 2 );
00454         // TODO duplicated code make a method out of it
00455         if ( sharedAxes.size() == 1 && xSharedPlanes.size() > 1 )
00456         {
00457             //xSharedPlanes.removeAll( sharedAxes.first()->diagram()->coordinatePlane() );
00458             //std::sort( xSharedPlanes.begin(), xSharedPlanes.end(), PriorityComparator( planeNodeMapping ) );
00459             for ( int i = 0; i < xSharedPlanes.size() - 1; ++i )
00460             {
00461                 LayoutGraphNode *tmpNode = planeNodeMapping[ xSharedPlanes[ i ] ];
00462                 Q_ASSERT( tmpNode );
00463                 LayoutGraphNode *tmpNode2 = planeNodeMapping[ xSharedPlanes[ i + 1 ] ];
00464                 Q_ASSERT( tmpNode2 );
00465                 tmpNode->bottomSuccesor = tmpNode2;
00466             }
00467 //            if ( sharedAxes.first()->diagram() &&  sharedAxes.first()->diagram()->coordinatePlane() )
00468 //            {
00469 //                LayoutGraphNode *lastNode = planeNodeMapping[ xSharedPlanes.last() ];
00470 //                Q_ASSERT( lastNode );
00471 //                Q_ASSERT( sharedAxes.first()->diagram()->coordinatePlane() );
00472 //                LayoutGraphNode *ownerNode = planeNodeMapping[ sharedAxes.first()->diagram()->coordinatePlane() ];
00473 //                Q_ASSERT( ownerNode );
00474 //                lastNode->bottomSuccesor = ownerNode;
00475 //            }
00476             //merge AxisInformation, needs a two pass run
00477             LayoutGraphNode axisInfoNode;
00478             for ( int count = 0; count < 2; ++count )
00479             {
00480                 for ( int i = 0; i < xSharedPlanes.size(); ++i )
00481                 {
00482                     mergeNodeAxisInformation( &axisInfoNode, planeNodeMapping[ xSharedPlanes[ i ] ] );
00483                 }
00484             }
00485         }
00486         sharedAxes.clear();
00487         CoordinatePlaneList ySharedPlanes = findSharingAxisDiagrams( curNode->diagramPlane, coordinatePlanes, Ordinate, &sharedAxes );
00488         Q_ASSERT( sharedAxes.size() < 2 );
00489         if ( sharedAxes.size() == 1 && ySharedPlanes.size() > 1 )
00490         {
00491             //ySharedPlanes.removeAll( sharedAxes.first()->diagram()->coordinatePlane() );
00492             //std::sort( ySharedPlanes.begin(), ySharedPlanes.end(), PriorityComparator( planeNodeMapping ) );
00493             for ( int i = 0; i < ySharedPlanes.size() - 1; ++i )
00494             {
00495                 LayoutGraphNode *tmpNode = planeNodeMapping[ ySharedPlanes[ i ] ];
00496                 Q_ASSERT( tmpNode );
00497                 LayoutGraphNode *tmpNode2 = planeNodeMapping[ ySharedPlanes[ i + 1 ] ];
00498                 Q_ASSERT( tmpNode2 );
00499                 tmpNode->leftSuccesor = tmpNode2;
00500             }
00501 //            if ( sharedAxes.first()->diagram() &&  sharedAxes.first()->diagram()->coordinatePlane() )
00502 //            {
00503 //                LayoutGraphNode *lastNode = planeNodeMapping[ ySharedPlanes.last() ];
00504 //                Q_ASSERT( lastNode );
00505 //                Q_ASSERT( sharedAxes.first()->diagram()->coordinatePlane() );
00506 //                LayoutGraphNode *ownerNode = planeNodeMapping[ sharedAxes.first()->diagram()->coordinatePlane() ];
00507 //                Q_ASSERT( ownerNode );
00508 //                lastNode->bottomSuccesor = ownerNode;
00509 //            }
00510             //merge AxisInformation, needs a two pass run
00511             LayoutGraphNode axisInfoNode;
00512             for ( int count = 0; count < 2; ++count )
00513             {
00514                 for ( int i = 0; i < ySharedPlanes.size(); ++i )
00515                 {
00516                     mergeNodeAxisInformation( &axisInfoNode, planeNodeMapping[ ySharedPlanes[ i ] ] );
00517                 }
00518             }
00519         }
00520         sharedAxes.clear();
00521         if ( curNode->diagramPlane->referenceCoordinatePlane() )
00522             curNode->sharedSuccesor = planeNodeMapping[ curNode->diagramPlane->referenceCoordinatePlane() ];
00523     }
00524 
00525     return allNodes;
00526 }
00527 
00528 QHash<AbstractCoordinatePlane*, PlaneInfo> Chart::Private::buildPlaneLayoutInfos()
00529 {
00530     /* There are two ways in which planes can be caused to interact in
00531      * where they are put layouting wise: The first is the reference plane. If
00532      * such a reference plane is set, on a plane, it will use the same cell in the
00533      * layout as that one. In addition to this, planes can share an axis. In that case
00534      * they will be laid out in relation to each other as suggested by the position
00535      * of the axis. If, for example Plane1 and Plane2 share an axis at position Left,
00536      * that will result in the layout: Axis Plane1 Plane 2, vertically. If Plane1
00537      * also happens to be Plane2's referece plane, both planes are drawn over each
00538      * other. The reference plane concept allows two planes to share the same space
00539      * even if neither has any axis, and in case there are shared axis, it is used
00540      * to decided, whether the planes should be painted on top of each other or
00541      * laid out vertically or horizontally next to each other. */
00542     QHash<CartesianAxis*, AxisInfo> axisInfos;
00543     QHash<AbstractCoordinatePlane*, PlaneInfo> planeInfos;
00544     Q_FOREACH(AbstractCoordinatePlane* plane, coordinatePlanes ) {
00545         PlaneInfo p;
00546         // first check if we share space with another plane
00547         p.referencePlane = plane->referenceCoordinatePlane();
00548         planeInfos.insert( plane, p );
00549 
00550         Q_FOREACH( AbstractDiagram* abstractDiagram, plane->diagrams() ) {
00551             AbstractCartesianDiagram* diagram =
00552                     qobject_cast<AbstractCartesianDiagram*> ( abstractDiagram );
00553             if ( !diagram ) {
00554                 continue;
00555             }
00556 
00557             Q_FOREACH( CartesianAxis* axis, diagram->axes() ) {
00558                 if ( !axisInfos.contains( axis ) ) {
00559                     /* If this is the first time we see this axis, add it, with the
00560                      * current plane. The first plane added to the chart that has
00561                      * the axis associated with it thus "owns" it, and decides about
00562                      * layout. */
00563                     AxisInfo i;
00564                     i.plane = plane;
00565                     axisInfos.insert( axis, i );
00566                 } else {
00567                     AxisInfo i = axisInfos[axis];
00568                     if ( i.plane == plane ) {
00569                         continue; // we don't want duplicates, only shared
00570                     }
00571 
00572                     /* The user expects diagrams to be added on top, and to the right
00573                      * so that horizontally we need to move the new diagram, vertically
00574                      * the reference one. */
00575                     PlaneInfo pi = planeInfos[plane];
00576                     // plane-to-plane linking overrides linking via axes
00577                     if ( !pi.referencePlane ) {
00578                         // we're not the first plane to see this axis, mark us as a slave
00579                         pi.referencePlane = i.plane;
00580                         if ( axis->position() == CartesianAxis::Left ||
00581                              axis->position() == CartesianAxis::Right ) {
00582                             pi.horizontalOffset += 1;
00583                         }
00584                         planeInfos[plane] = pi;
00585 
00586                         pi = planeInfos[i.plane];
00587                         if ( axis->position() == CartesianAxis::Top ||
00588                              axis->position() == CartesianAxis::Bottom )  {
00589                             pi.verticalOffset += 1;
00590                         }
00591 
00592                         planeInfos[i.plane] = pi;
00593                     }
00594                 }
00595             }
00596         }
00597         // Create a new grid layout for each plane that has no reference.
00598         p = planeInfos[plane];
00599         if ( p.referencePlane == 0 ) {
00600             p.gridLayout = new QGridLayout();
00601             p.gridLayout->setMargin( 0 );
00602             planeInfos[plane] = p;
00603         }
00604     }
00605     return planeInfos;
00606 }
00607 
00608 void Chart::Private::slotLayoutPlanes()
00609 {
00610     if ( useNewLayoutSystem )
00611     {
00612         /*TODO make sure this is really needed */
00613         const QBoxLayout::Direction oldPlanesDirection =
00614             planesLayout ? planesLayout->direction() : QBoxLayout::TopToBottom;
00615         if ( planesLayout && dataAndLegendLayout )
00616             dataAndLegendLayout->removeItem( planesLayout );
00617         //*/
00618         const bool hadPlanesLayout = planesLayout != 0;
00619         int left, top, right, bottom;
00620         if(hadPlanesLayout)
00621             planesLayout->getContentsMargins(&left, &top, &right, &bottom);
00622 
00623         KDAB_FOREACH( KDChart::AbstractLayoutItem* plane, planeLayoutItems ) {
00624             plane->removeFromParentLayout();
00625         }
00626         //TODO they should get a correct parent, but for now it works
00627         KDAB_FOREACH( KDChart::AbstractLayoutItem* plane, planeLayoutItems ) {
00628             if ( dynamic_cast< KDChart::AutoSpacerLayoutItem* >( plane ) )
00629                 delete plane;
00630         }
00631         //qDebug() << Q_FUNC_INFO << "Relayout called";
00632         planeLayoutItems.clear();
00633         delete planesLayout;
00634         //hint: The direction is configurable by the user now, as
00635         //      we are using a QBoxLayout rather than a QVBoxLayout.  (khz, 2007/04/25)
00636         planesLayout = new QBoxLayout( oldPlanesDirection );
00637 
00638         gridPlaneLayout = new QGridLayout;
00639         planesLayout->addLayout( gridPlaneLayout );
00640 
00641         if(hadPlanesLayout)
00642             planesLayout->setContentsMargins(left, top, right, bottom);
00643         planesLayout->setObjectName( QString::fromLatin1( "planesLayout" ) );
00644 
00645         /* First go through all planes and all axes and figure out whether the planes
00646          * need to coordinate. If they do, they share a grid layout, if not, each
00647          * get their own. See buildPlaneLayoutInfos() for more details. */
00648 
00649         QVector< LayoutGraphNode* > vals = buildPlaneLayoutGraph();
00650         //qDebug() << Q_FUNC_INFO << "GraphNodes" << vals.size();
00651         QVector< LayoutGraphNode* > connectedComponents = getPrioritySortedConnectedComponents( vals );
00652         //qDebug() << Q_FUNC_INFO << "SubGraphs" << connectedComponents.size();
00653         int row = 0;
00654         int col = 0;
00655         QHash< CartesianAxis*, bool > layoutedAxes;
00656         for ( int i = 0; i < connectedComponents.size(); ++i )
00657         {
00658             LayoutGraphNode *curComponent = connectedComponents[ i ];
00659             for ( LayoutGraphNode *curRowComponent = curComponent; curRowComponent; curRowComponent = curRowComponent->bottomSuccesor )
00660             {
00661                 col = 0;
00662                 for ( LayoutGraphNode *curColComponent = curRowComponent; curColComponent; curColComponent = curColComponent->leftSuccesor )
00663                 {
00664                     Q_ASSERT( curColComponent->diagramPlane->diagrams().size() == 1 );
00665                     Q_FOREACH( AbstractDiagram* diagram, curColComponent->diagramPlane->diagrams() )
00666                     {
00667                         const int planeRowOffset = 1;//curColComponent->topAxesLayout ? 1 : 0;
00668                         const int planeColOffset = 1;//curColComponent->leftAxesLayout ? 1 : 0;
00669                         //qDebug() << Q_FUNC_INFO << row << col << planeRowOffset << planeColOffset;
00670 
00671                         //qDebug() << Q_FUNC_INFO << row + planeRowOffset << col + planeColOffset;
00672                         planeLayoutItems << curColComponent->diagramPlane;
00673                         AbstractCartesianDiagram *cartDiag = qobject_cast< AbstractCartesianDiagram* >( diagram );
00674                         if ( cartDiag )
00675                         {
00676                             gridPlaneLayout->addItem( curColComponent->diagramPlane, row + planeRowOffset, col + planeColOffset, 2, 2 );
00677                             curColComponent->diagramPlane->setParentLayout( gridPlaneLayout );
00678                             QHBoxLayout *leftLayout = 0;
00679                             QHBoxLayout *rightLayout = 0;
00680                             QVBoxLayout *topLayout = 0;
00681                             QVBoxLayout *bottomLayout = 0;
00682                             if ( curComponent->sharedSuccesor )
00683                             {
00684                                 gridPlaneLayout->addItem( curColComponent->sharedSuccesor->diagramPlane, row + planeRowOffset, col + planeColOffset, 2, 2 );
00685                                 curColComponent->sharedSuccesor->diagramPlane->setParentLayout( gridPlaneLayout );
00686                                 planeLayoutItems << curColComponent->sharedSuccesor->diagramPlane;
00687                             }
00688                             Q_FOREACH( CartesianAxis* axis, cartDiag->axes() )
00689                             {
00690                                 if ( axis->isAbscissa() )
00691                                 {
00692                                     if ( curColComponent->bottomSuccesor )
00693                                         continue;
00694                                 }
00695                                 if ( layoutedAxes.contains( axis ) )
00696                                     continue;
00697         //                        if ( axis->diagram() != diagram )
00698         //                            continue;
00699                                 switch( axis->position() )
00700                                 {
00701                                 case( CartesianAxis::Top ):
00702                                     if ( !topLayout )
00703                                         topLayout = new QVBoxLayout;
00704                                     topLayout->addItem( axis );
00705                                     axis->setParentLayout( topLayout );
00706                                     break;
00707                                 case( CartesianAxis::Bottom ):
00708                                     if ( !bottomLayout )
00709                                         bottomLayout = new QVBoxLayout;
00710                                     bottomLayout->addItem( axis );
00711                                     axis->setParentLayout( bottomLayout );
00712                                     break;
00713                                 case( CartesianAxis::Left ):
00714                                     if ( !leftLayout )
00715                                         leftLayout = new QHBoxLayout;
00716                                     leftLayout->addItem( axis );
00717                                     axis->setParentLayout( leftLayout );
00718                                     break;
00719                                 case( CartesianAxis::Right ):
00720                                     if ( !rightLayout )
00721                                     {
00722                                         rightLayout = new QHBoxLayout;
00723                                     }
00724                                     rightLayout->addItem( axis );
00725                                     axis->setParentLayout( rightLayout );
00726                                     break;
00727                                 }
00728                                 planeLayoutItems << axis;
00729                                 layoutedAxes[ axis ] = true;
00730                             }
00731                             if ( leftLayout )
00732                                 gridPlaneLayout->addLayout( leftLayout, row + planeRowOffset, col, 2, 1 );
00733                             if ( rightLayout )
00734                                 gridPlaneLayout->addLayout( rightLayout, row, col + planeColOffset + 2, 2, 1 );
00735                             if ( topLayout )
00736                                 gridPlaneLayout->addLayout( topLayout, row, col + planeColOffset, 1, 2 );
00737                             if ( bottomLayout )
00738                                 gridPlaneLayout->addLayout( bottomLayout, row + planeRowOffset + 2, col + planeColOffset, 1, 2 );
00739                         }
00740                         else
00741                         {
00742                             gridPlaneLayout->addItem( curColComponent->diagramPlane, row, col, 4, 4 );
00743                             curColComponent->diagramPlane->setParentLayout( gridPlaneLayout );
00744                         }
00745                         col += planeColOffset + 2 + ( 1 );
00746                     }
00747                 }
00748                 int axisOffset = 2;//curRowComponent->topAxesLayout ? 1 : 0;
00749                 //axisOffset += curRowComponent->bottomAxesLayout ? 1 : 0;
00750                 const int rowOffset = axisOffset + 2;
00751                 row += rowOffset;
00752             }
00753 
00754     //        if ( planesLayout->direction() == QBoxLayout::TopToBottom )
00755     //            ++row;
00756     //        else
00757     //            ++col;
00758         }
00759 
00760         qDeleteAll( vals );
00761         // re-add our grid(s) to the chart's layout
00762         if ( dataAndLegendLayout ) {
00763             dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
00764             dataAndLegendLayout->setRowStretch( 1, 1000 );
00765             dataAndLegendLayout->setColumnStretch( 1, 1000 );
00766         }
00767         slotRelayout();
00768 #ifdef NEW_LAYOUT_DEBUG
00769         for ( int i = 0; i < gridPlaneLayout->rowCount(); ++i )
00770         {
00771             for ( int j = 0; j < gridPlaneLayout->columnCount(); ++j )
00772             {
00773                 if ( gridPlaneLayout->itemAtPosition( i, j ) )
00774                     qDebug() << Q_FUNC_INFO << "item at" << i << j << gridPlaneLayout->itemAtPosition( i, j )->geometry();
00775                 else
00776                     qDebug() << Q_FUNC_INFO << "item at" << i << j << "no item present";
00777             }
00778         }
00779         //qDebug() << Q_FUNC_INFO << "Relayout ended";
00780 #endif
00781     } else {
00782         const QBoxLayout::Direction oldPlanesDirection =
00783             planesLayout ? planesLayout->direction() : QBoxLayout::TopToBottom;
00784         if ( planesLayout && dataAndLegendLayout )
00785             dataAndLegendLayout->removeItem( planesLayout );
00786 
00787         const bool hadPlanesLayout = planesLayout != 0;
00788         int left, top, right, bottom;
00789         if ( hadPlanesLayout ) {
00790             planesLayout->getContentsMargins( &left, &top, &right, &bottom );
00791         }
00792 
00793         KDAB_FOREACH( KDChart::AbstractLayoutItem* plane, planeLayoutItems ) {
00794             plane->removeFromParentLayout();
00795         }
00796         planeLayoutItems.clear();
00797         delete planesLayout;
00798         //hint: The direction is configurable by the user now, as
00799         //      we are using a QBoxLayout rather than a QVBoxLayout.  (khz, 2007/04/25)
00800         planesLayout = new QBoxLayout( oldPlanesDirection );
00801 
00802         if ( hadPlanesLayout ) {
00803             planesLayout->setContentsMargins( left, top, right, bottom );
00804         }
00805 
00806         planesLayout->setMargin( 0 );
00807         planesLayout->setSpacing( 0 );
00808         planesLayout->setObjectName( QString::fromLatin1( "planesLayout" ) );
00809 
00810         /* First go through all planes and all axes and figure out whether the planes
00811          * need to coordinate. If they do, they share a grid layout, if not, each
00812          * gets their own. See buildPlaneLayoutInfos() for more details. */
00813         QHash<AbstractCoordinatePlane*, PlaneInfo> planeInfos = buildPlaneLayoutInfos();
00814         QHash<AbstractAxis*, AxisInfo> axisInfos;
00815         KDAB_FOREACH( AbstractCoordinatePlane* plane, coordinatePlanes ) {
00816             Q_ASSERT( planeInfos.contains(plane) );
00817             PlaneInfo& pi = planeInfos[ plane ];
00818             const int column = pi.horizontalOffset;
00819             const int row = pi.verticalOffset;
00820             //qDebug() << "processing plane at column" << column << "and row" << row;
00821             QGridLayout *planeLayout = pi.gridLayout;
00822 
00823             if ( !planeLayout ) {
00824                 PlaneInfo& refPi = pi;
00825                 // if this plane is sharing an axis with another one, recursively check for the original plane and use
00826                 // the grid of that as planeLayout.
00827                 while ( !planeLayout && refPi.referencePlane ) {
00828                     refPi = planeInfos[refPi.referencePlane];
00829                     planeLayout = refPi.gridLayout;
00830                 }
00831                 Q_ASSERT_X( planeLayout,
00832                             "Chart::Private::slotLayoutPlanes()",
00833                             "Invalid reference plane. Please check that the reference plane has been added to the Chart." );
00834             } else {
00835                 planesLayout->addLayout( planeLayout );
00836             }
00837 
00838             /* Put the plane in the center of the layout. If this is our own, that's
00839              * the middle of the layout, if we are sharing, it's a cell in the center
00840              * column of the shared grid. */
00841             planeLayoutItems << plane;
00842             plane->setParentLayout( planeLayout );
00843             planeLayout->addItem( plane, row, column, 1, 1, 0 );
00844             //qDebug() << "Chart slotLayoutPlanes() calls planeLayout->addItem("<< row << column << ")";
00845             planeLayout->setRowStretch( row, 2 );
00846             planeLayout->setColumnStretch( column, 2 );
00847             KDAB_FOREACH( AbstractDiagram* abstractDiagram, plane->diagrams() )
00848             {
00849                 AbstractCartesianDiagram* diagram =
00850                     qobject_cast< AbstractCartesianDiagram* >( abstractDiagram );
00851                 if ( !diagram ) {
00852                     continue;  // FIXME what about polar ?
00853                 }
00854 
00855                 if ( pi.referencePlane != 0 )
00856                 {
00857                     pi.topAxesLayout = planeInfos[ pi.referencePlane ].topAxesLayout;
00858                     pi.bottomAxesLayout = planeInfos[ pi.referencePlane ].bottomAxesLayout;
00859                     pi.leftAxesLayout = planeInfos[ pi.referencePlane ].leftAxesLayout;
00860                     pi.rightAxesLayout = planeInfos[ pi.referencePlane ].rightAxesLayout;
00861                 }
00862 
00863                 // collect all axes of a kind into sublayouts
00864                 if ( pi.topAxesLayout == 0 )
00865                 {
00866                     pi.topAxesLayout = new QVBoxLayout;
00867                     pi.topAxesLayout->setMargin( 0 );
00868                     pi.topAxesLayout->setObjectName( QString::fromLatin1( "topAxesLayout" ) );
00869                 }
00870                 if ( pi.bottomAxesLayout == 0 )
00871                 {
00872                     pi.bottomAxesLayout = new QVBoxLayout;
00873                     pi.bottomAxesLayout->setMargin( 0 );
00874                     pi.bottomAxesLayout->setObjectName( QString::fromLatin1( "bottomAxesLayout" ) );
00875                 }
00876                 if ( pi.leftAxesLayout == 0 )
00877                 {
00878                     pi.leftAxesLayout = new QHBoxLayout;
00879                     pi.leftAxesLayout->setMargin( 0 );
00880                     pi.leftAxesLayout->setObjectName( QString::fromLatin1( "leftAxesLayout" ) );
00881                 }
00882                 if ( pi.rightAxesLayout == 0 )
00883                 {
00884                     pi.rightAxesLayout = new QHBoxLayout;
00885                     pi.rightAxesLayout->setMargin( 0 );
00886                     pi.rightAxesLayout->setObjectName( QString::fromLatin1( "rightAxesLayout" ) );
00887                 }
00888 
00889                 if ( pi.referencePlane != 0 )
00890                 {
00891                     planeInfos[ pi.referencePlane ].topAxesLayout = pi.topAxesLayout;
00892                     planeInfos[ pi.referencePlane ].bottomAxesLayout = pi.bottomAxesLayout;
00893                     planeInfos[ pi.referencePlane ].leftAxesLayout = pi.leftAxesLayout;
00894                     planeInfos[ pi.referencePlane ].rightAxesLayout = pi.rightAxesLayout;
00895                 }
00896 
00897                 //pi.leftAxesLayout->setSizeConstraint( QLayout::SetFixedSize );
00898                 KDAB_FOREACH( CartesianAxis* axis, diagram->axes() ) {
00899                     if ( axisInfos.contains( axis ) ) {
00900                         continue; // already laid out this one
00901                     }
00902                     Q_ASSERT ( axis );
00903                     axis->setCachedSizeDirty();
00904                     //qDebug() << "--------------- axis added to planeLayoutItems  -----------------";
00905                     planeLayoutItems << axis;
00906 
00907                     switch ( axis->position() ) {
00908                     case CartesianAxis::Top:
00909                         axis->setParentLayout( pi.topAxesLayout );
00910                         pi.topAxesLayout->addItem( axis );
00911                         break;
00912                     case CartesianAxis::Bottom:
00913                         axis->setParentLayout( pi.bottomAxesLayout );
00914                         pi.bottomAxesLayout->addItem( axis );
00915                         break;
00916                     case CartesianAxis::Left:
00917                         axis->setParentLayout( pi.leftAxesLayout );
00918                         pi.leftAxesLayout->addItem( axis );
00919                         break;
00920                     case CartesianAxis::Right:
00921                         axis->setParentLayout( pi.rightAxesLayout );
00922                         pi.rightAxesLayout->addItem( axis );
00923                         break;
00924                     default:
00925                         Q_ASSERT_X( false, "Chart::paintEvent", "unknown axis position" );
00926                         break;
00927                     };
00928                     axisInfos.insert( axis, AxisInfo() );
00929                 }
00930                 /* Put each stack of axes-layouts in the cells surrounding the
00931                  * associated plane. We are laying out in the oder the planes
00932                  * were added, and the first one gets to lay out shared axes.
00933                  * Private axes go here as well, of course. */
00934 
00935                 if ( !pi.topAxesLayout->parent() ) {
00936                     planeLayout->addLayout( pi.topAxesLayout, row - 1, column );
00937                 }
00938                 if ( !pi.bottomAxesLayout->parent() ) {
00939                     planeLayout->addLayout( pi.bottomAxesLayout, row + 1, column );
00940                 }
00941                 if ( !pi.leftAxesLayout->parent() ) {
00942                     planeLayout->addLayout( pi.leftAxesLayout, row, column - 1 );
00943                 }
00944                 if ( !pi.rightAxesLayout->parent() ) {
00945                     planeLayout->addLayout( pi.rightAxesLayout,row, column + 1 );
00946                 }
00947             }
00948 
00949             // use up to four auto-spacer items in the corners around the diagrams:
00950     #define ADD_AUTO_SPACER_IF_NEEDED( \
00951             spacerRow, spacerColumn, hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ) \
00952             { \
00953                 if ( hLayout || vLayout ) { \
00954                     AutoSpacerLayoutItem * spacer \
00955                     = new AutoSpacerLayoutItem( hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ); \
00956                     planeLayout->addItem( spacer, spacerRow, spacerColumn, 1, 1 ); \
00957                     spacer->setParentLayout( planeLayout ); \
00958                     planeLayoutItems << spacer; \
00959                 } \
00960             }
00961 
00962             if ( plane->isCornerSpacersEnabled() ) {
00963                 ADD_AUTO_SPACER_IF_NEEDED( row - 1, column - 1, false, pi.leftAxesLayout,  false, pi.topAxesLayout )
00964                 ADD_AUTO_SPACER_IF_NEEDED( row + 1, column - 1, true,  pi.leftAxesLayout,  false,  pi.bottomAxesLayout )
00965                 ADD_AUTO_SPACER_IF_NEEDED( row - 1, column + 1, false, pi.rightAxesLayout, true, pi.topAxesLayout )
00966                 ADD_AUTO_SPACER_IF_NEEDED( row + 1, column + 1, true,  pi.rightAxesLayout, true,  pi.bottomAxesLayout )
00967             }
00968         }
00969         // re-add our grid(s) to the chart's layout
00970         if ( dataAndLegendLayout ) {
00971             dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
00972             dataAndLegendLayout->setRowStretch( 1, 1000 );
00973             dataAndLegendLayout->setColumnStretch( 1, 1000 );
00974         }
00975 
00976         slotRelayout();
00977     }
00978 }
00979 
00980 void Chart::Private::createLayouts()
00981 {
00982     KDAB_FOREACH( KDChart::TextArea* textLayoutItem, textLayoutItems ) {
00983         textLayoutItem->removeFromParentLayout();
00984     }
00985     textLayoutItems.clear();
00986 
00987     // layout for the planes is handled separately, so we don't want to delete it here
00988     if ( dataAndLegendLayout) {
00989         dataAndLegendLayout->removeItem( planesLayout );
00990         planesLayout->setParent( 0 );
00991     }
00992     // start from scratch
00993     delete layout;
00994 
00995     // The toplevel layout provides the left and right global margins
00996     layout = new QHBoxLayout( chart );
00997     layout->setMargin( 0 );
00998     layout->setObjectName( QString::fromLatin1( "Chart::Private::layout" ) );
00999     layout->addSpacing( globalLeadingLeft );
01000 
01001     // The vLayout provides top and bottom global margins and lays
01002     // out headers, footers and the diagram area.
01003     vLayout = new QVBoxLayout();
01004     vLayout->setMargin( 0 );
01005     vLayout->setObjectName( QString::fromLatin1( "vLayout" ) );
01006 
01007     layout->addLayout( vLayout, 1000 );
01008     layout->addSpacing( globalLeadingRight );
01009 
01010     // 1. the gap above the top edge of the headers area
01011     vLayout->addSpacing( globalLeadingTop );
01012     // 2. the header(s) area
01013     headerLayout = new QGridLayout();
01014     headerLayout->setMargin( 0 );
01015     vLayout->addLayout( headerLayout );
01016     // 3. the area containing coordinate plane(s), axes, legend(s)
01017     dataAndLegendLayout = new QGridLayout();
01018     dataAndLegendLayout->setMargin( 0 );
01019     dataAndLegendLayout->setObjectName( QString::fromLatin1( "dataAndLegendLayout" ) );
01020     vLayout->addLayout( dataAndLegendLayout, 1000 );
01021     // 4. the footer(s) area
01022     footerLayout = new QGridLayout();
01023     footerLayout->setMargin( 0 );
01024     footerLayout->setObjectName( QString::fromLatin1( "footerLayout" ) );
01025     vLayout->addLayout( footerLayout );
01026 
01027     // 5. Prepare the header / footer layout cells:
01028     //    Each of the 9 header cells (the 9 footer cells)
01029     //    contain their own QVBoxLayout
01030     //    since there can be more than one header (footer) per cell.
01031     for ( int row = 0; row < 3; ++row ) {
01032         for ( int column = 0; column < 3; ++ column ) {
01033             const Qt::Alignment align = s_gridAlignments[ row ][ column ];
01034             for ( int headOrFoot = 0; headOrFoot < 2; headOrFoot++ ) {
01035                 QVBoxLayout* innerLayout = new QVBoxLayout();
01036                 innerLayout->setMargin( 0 );
01037                 innerLayout->setAlignment( align );
01038                 innerHdFtLayouts[ headOrFoot ][ row ][ column ] = innerLayout;
01039 
01040                 QGridLayout* outerLayout = headOrFoot == 0 ? headerLayout : footerLayout;
01041                 outerLayout->addLayout( innerLayout, row, column, align );
01042             }
01043         }
01044     }
01045 
01046     // 6. the gap below the bottom edge of the headers area
01047     vLayout->addSpacing( globalLeadingBottom );
01048 
01049     // the data+axes area
01050     dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
01051     dataAndLegendLayout->setRowStretch( 1, 1 );
01052     dataAndLegendLayout->setColumnStretch( 1, 1 );
01053 }
01054 
01055 void Chart::Private::slotRelayout()
01056 {
01057     //qDebug() << "Chart relayouting started.";
01058     createLayouts();
01059 
01060     layoutHeadersAndFooters();
01061     layoutLegends();
01062 
01063     // This triggers the qlayout, see QBoxLayout::setGeometry
01064     // The geometry is not necessarily w->rect(), when using paint(), this is why
01065     // we don't call layout->activate().
01066     const QRect geo( QRect( 0, 0, currentLayoutSize.width(), currentLayoutSize.height() ) );
01067     if ( geo.isValid() && geo != layout->geometry() ) {
01068         //qDebug() << "Chart slotRelayout() adjusting geometry to" << geo;
01069         //if ( coordinatePlanes.count() )
01070         //    qDebug() << "           plane geo before" << coordinatePlanes.first()->geometry();
01071         layout->setGeometry( geo );
01072         //if ( coordinatePlanes.count() ) {
01073         //    qDebug() << "           plane geo after " << coordinatePlanes.first()->geometry();
01074         //}
01075     }
01076 
01077     // Adapt diagram drawing to the new size
01078     KDAB_FOREACH (AbstractCoordinatePlane* plane, coordinatePlanes ) {
01079         plane->layoutDiagrams();
01080     }
01081     //qDebug() << "Chart relayouting done.";
01082 }
01083 
01084 // Called when the size of the chart changes.
01085 // So in theory, we only need to adjust geometries.
01086 // But this also needs to make sure that everything is in place for the first painting.
01087 void Chart::Private::resizeLayout( const QSize& size )
01088 {
01089     currentLayoutSize = size;
01090     //qDebug() << "Chart::resizeLayout(" << currentLayoutSize << ")";
01091 
01092     /*
01093     // We need to make sure that the legend's layouts are populated,
01094     // so that setGeometry gets proper sizeHints from them and resizes them properly.
01095     KDAB_FOREACH( Legend *legend, legends ) {
01096     // This forceRebuild will see a wrong areaGeometry, but I don't care about geometries yet,
01097     // only about the fact that legends should have their contents populated.
01098     // -> it would be better to dissociate "building contents" and "resizing" in Legend...
01099 
01100     //        legend->forceRebuild();
01101 
01102     legend->resizeLayout( size );
01103     }
01104     */
01105     slotLayoutPlanes(); // includes slotRelayout
01106 
01107     //qDebug() << "Chart::resizeLayout done";
01108 }
01109 
01110 
01111 void Chart::Private::paintAll( QPainter* painter )
01112 {
01113     QRect rect( QPoint(0, 0), currentLayoutSize );
01114 
01115     //qDebug() << this<<"::paintAll() uses layout size" << currentLayoutSize;
01116 
01117     // Paint the background (if any)
01118     KDChart::AbstractAreaBase::paintBackgroundAttributes( *painter, rect, backgroundAttributes );
01119     // Paint the frame (if any)
01120     KDChart::AbstractAreaBase::paintFrameAttributes( *painter, rect, frameAttributes );
01121 
01122     chart->reLayoutFloatingLegends();
01123 
01124     KDAB_FOREACH( KDChart::AbstractLayoutItem* planeLayoutItem, planeLayoutItems ) {
01125         planeLayoutItem->paintAll( *painter );
01126     }
01127     KDAB_FOREACH( KDChart::TextArea* textLayoutItem, textLayoutItems ) {
01128         textLayoutItem->paintAll( *painter );
01129     }
01130 }
01131 
01132 // ******** Chart interface implementation ***********
01133 
01134 Chart::Chart ( QWidget* parent )
01135     : QWidget ( parent )
01136     , _d( new Private( this ) )
01137 {
01138 #if defined KDAB_EVAL
01139     EvalDialog::checkEvalLicense( "KD Chart" );
01140 #endif
01141 
01142     FrameAttributes frameAttrs;
01143 // no frame per default...
01144 //    frameAttrs.setVisible( true );
01145     frameAttrs.setPen( QPen( Qt::black ) );
01146     frameAttrs.setPadding( 1 );
01147     setFrameAttributes( frameAttrs );
01148 
01149     addCoordinatePlane( new CartesianCoordinatePlane ( this ) );
01150 }
01151 
01152 Chart::~Chart()
01153 {
01154     delete _d;
01155 }
01156 
01157 #define d d_func()
01158 
01159 void Chart::setFrameAttributes( const FrameAttributes &a )
01160 {
01161     d->frameAttributes = a;
01162 }
01163 
01164 FrameAttributes Chart::frameAttributes() const
01165 {
01166     return d->frameAttributes;
01167 }
01168 
01169 void Chart::setBackgroundAttributes( const BackgroundAttributes &a )
01170 {
01171     d->backgroundAttributes = a;
01172 }
01173 
01174 BackgroundAttributes Chart::backgroundAttributes() const
01175 {
01176     return d->backgroundAttributes;
01177 }
01178 
01179 //TODO KDChart 3.0; change QLayout into QBoxLayout::Direction
01180 void Chart::setCoordinatePlaneLayout( QLayout * layout )
01181 {
01182     delete d->planesLayout;
01183     d->planesLayout = qobject_cast<QBoxLayout*>( layout );
01184     d->slotLayoutPlanes();
01185 }
01186 
01187 QLayout* Chart::coordinatePlaneLayout()
01188 {
01189     return d->planesLayout;
01190 }
01191 
01192 AbstractCoordinatePlane* Chart::coordinatePlane()
01193 {
01194     if ( d->coordinatePlanes.isEmpty() ) {
01195         qWarning() << "Chart::coordinatePlane: warning: no coordinate plane defined.";
01196         return 0;
01197     } else {
01198         return d->coordinatePlanes.first();
01199     }
01200 }
01201 
01202 CoordinatePlaneList Chart::coordinatePlanes()
01203 {
01204     return d->coordinatePlanes;
01205 }
01206 
01207 void Chart::addCoordinatePlane( AbstractCoordinatePlane* plane )
01208 {
01209     // Append
01210     insertCoordinatePlane( d->coordinatePlanes.count(), plane );
01211 }
01212 
01213 void Chart::insertCoordinatePlane( int index, AbstractCoordinatePlane* plane )
01214 {
01215     if ( index < 0 || index > d->coordinatePlanes.count() ) {
01216         return;
01217     }
01218 
01219     connect( plane, SIGNAL( destroyedCoordinatePlane( AbstractCoordinatePlane* ) ),
01220              d,   SLOT( slotUnregisterDestroyedPlane( AbstractCoordinatePlane* ) ) );
01221     connect( plane, SIGNAL( needUpdate() ),       this,   SLOT( update() ) );
01222     connect( plane, SIGNAL( needRelayout() ),     d,      SLOT( slotRelayout() ) ) ;
01223     connect( plane, SIGNAL( needLayoutPlanes() ), d,      SLOT( slotLayoutPlanes() ) ) ;
01224     connect( plane, SIGNAL( propertiesChanged() ),this, SIGNAL( propertiesChanged() ) );
01225     d->coordinatePlanes.insert( index, plane );
01226     plane->setParent( this );
01227     d->slotLayoutPlanes();
01228 }
01229 
01230 void Chart::replaceCoordinatePlane( AbstractCoordinatePlane* plane,
01231                                     AbstractCoordinatePlane* oldPlane_ )
01232 {
01233     if ( plane && oldPlane_ != plane ) {
01234         AbstractCoordinatePlane* oldPlane = oldPlane_;
01235         if ( d->coordinatePlanes.count() ) {
01236             if ( ! oldPlane ) {
01237                 oldPlane = d->coordinatePlanes.first();
01238                 if ( oldPlane == plane )
01239                     return;
01240             }
01241             takeCoordinatePlane( oldPlane );
01242         }
01243         delete oldPlane;
01244         addCoordinatePlane( plane );
01245     }
01246 }
01247 
01248 void Chart::takeCoordinatePlane( AbstractCoordinatePlane* plane )
01249 {
01250     const int idx = d->coordinatePlanes.indexOf( plane );
01251     if ( idx != -1 ) {
01252         d->coordinatePlanes.takeAt( idx );
01253         disconnect( plane, SIGNAL( destroyedCoordinatePlane( AbstractCoordinatePlane* ) ),
01254                     d, SLOT( slotUnregisterDestroyedPlane( AbstractCoordinatePlane* ) ) );
01255         plane->removeFromParentLayout();
01256         plane->setParent( 0 );
01257         d->mouseClickedPlanes.removeAll(plane);
01258     }
01259     d->slotLayoutPlanes();
01260     // Need to emit the signal: In case somebody has connected the signal
01261     // to her own slot for e.g. calling update() on a widget containing the chart.
01262     emit propertiesChanged();
01263 }
01264 
01265 void Chart::setGlobalLeading( int left, int top, int right, int bottom )
01266 {
01267     setGlobalLeadingLeft( left );
01268     setGlobalLeadingTop( top );
01269     setGlobalLeadingRight( right );
01270     setGlobalLeadingBottom( bottom );
01271     d->slotRelayout();
01272 }
01273 
01274 void Chart::setGlobalLeadingLeft( int leading )
01275 {
01276     d->globalLeadingLeft = leading;
01277     d->slotRelayout();
01278 }
01279 
01280 int Chart::globalLeadingLeft() const
01281 {
01282     return d->globalLeadingLeft;
01283 }
01284 
01285 void Chart::setGlobalLeadingTop( int leading )
01286 {
01287     d->globalLeadingTop = leading;
01288     d->slotRelayout();
01289 }
01290 
01291 int Chart::globalLeadingTop() const
01292 {
01293     return d->globalLeadingTop;
01294 }
01295 
01296 void Chart::setGlobalLeadingRight( int leading )
01297 {
01298     d->globalLeadingRight = leading;
01299     d->slotRelayout();
01300 }
01301 
01302 int Chart::globalLeadingRight() const
01303 {
01304     return d->globalLeadingRight;
01305 }
01306 
01307 void Chart::setGlobalLeadingBottom( int leading )
01308 {
01309     d->globalLeadingBottom = leading;
01310     d->slotRelayout();
01311 }
01312 
01313 int Chart::globalLeadingBottom() const
01314 {
01315     return d->globalLeadingBottom;
01316 }
01317 
01318 void Chart::paint( QPainter* painter, const QRect& target )
01319 {
01320     if ( target.isEmpty() || !painter ) {
01321         return;
01322     }
01323 
01324     QPaintDevice* prevDevice = GlobalMeasureScaling::paintDevice();
01325     GlobalMeasureScaling::setPaintDevice( painter->device() );
01326 
01327     // Output on a widget
01328     if ( dynamic_cast< QWidget* >( painter->device() ) != 0 ) {
01329         GlobalMeasureScaling::setFactors( qreal( target.width() ) / qreal( geometry().size().width() ),
01330                                           qreal( target.height() ) / qreal( geometry().size().height() ) );
01331     } else {
01332         // Output onto a QPixmap
01333         PrintingParameters::setScaleFactor( qreal( painter->device()->logicalDpiX() ) / qreal( logicalDpiX() ) );
01334 
01335         const qreal resX = qreal( logicalDpiX() ) / qreal( painter->device()->logicalDpiX() );
01336         const qreal resY = qreal( logicalDpiY() ) / qreal( painter->device()->logicalDpiY() );
01337 
01338         GlobalMeasureScaling::setFactors( qreal( target.width() ) / qreal( geometry().size().width() ) * resX,
01339                                           qreal( target.height() ) / qreal( geometry().size().height() ) * resY );
01340     }
01341 
01342 
01343     if ( target.size() != d->currentLayoutSize ) {
01344         d->resizeLayout( target.size() );
01345     }
01346     const QPoint translation = target.topLeft();
01347     painter->translate( translation );
01348 
01349     d->paintAll( painter );
01350 
01351     // for debugging:
01352     //painter->setPen(QPen(Qt::blue, 8));
01353     //painter->drawRect(target.adjusted(12,12,-12,-12));
01354 
01355     KDAB_FOREACH( Legend *legend, d->legends ) {
01356         const bool hidden = legend->isHidden() && legend->testAttribute( Qt::WA_WState_ExplicitShowHide );
01357         if ( !hidden ) {
01358             //qDebug() << "painting legend at " << legend->geometry();
01359             legend->paintIntoRect( *painter, legend->geometry() );
01360         }
01361     }
01362 
01363     painter->translate( -translation.x(), -translation.y() );
01364 
01365     GlobalMeasureScaling::instance()->resetFactors();
01366     PrintingParameters::resetScaleFactor();
01367     GlobalMeasureScaling::setPaintDevice( prevDevice );
01368 }
01369 
01370 void Chart::resizeEvent ( QResizeEvent * )
01371 {
01372     d->resizeLayout( size() );
01373     KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) {
01374         plane->setGridNeedsRecalculate();
01375     }
01376     reLayoutFloatingLegends();
01377 }
01378 
01379 
01380 void Chart::reLayoutFloatingLegends()
01381 {
01382     KDAB_FOREACH( Legend *legend, d->legends ) {
01383         const bool hidden = legend->isHidden() && legend->testAttribute(Qt::WA_WState_ExplicitShowHide);
01384         if ( legend->position().isFloating() && !hidden ) {
01385             // resize the legend
01386             const QSize legendSize( legend->sizeHint() );
01387             legend->setGeometry( QRect( legend->geometry().topLeft(), legendSize ) );
01388             // find the legends corner point (reference point plus any paddings)
01389             const RelativePosition relPos( legend->floatingPosition() );
01390             QPointF pt( relPos.calculatedPoint( size() ) );
01391             //qDebug() << pt;
01392             // calculate the legend's top left point
01393             const Qt::Alignment alignTopLeft = Qt::AlignBottom | Qt::AlignLeft;
01394             if ( (relPos.alignment() & alignTopLeft) != alignTopLeft ) {
01395                 if ( relPos.alignment() & Qt::AlignRight )
01396                     pt.rx() -= legendSize.width();
01397                 else if ( relPos.alignment() & Qt::AlignHCenter )
01398                     pt.rx() -= 0.5 * legendSize.width();
01399 
01400                 if ( relPos.alignment() & Qt::AlignBottom )
01401                     pt.ry() -= legendSize.height();
01402                 else if ( relPos.alignment() & Qt::AlignVCenter )
01403                     pt.ry() -= 0.5 * legendSize.height();
01404             }
01405             //qDebug() << pt << endl;
01406             legend->move( static_cast<int>(pt.x()), static_cast<int>(pt.y()) );
01407         }
01408     }
01409 }
01410 
01411 
01412 void Chart::paintEvent( QPaintEvent* )
01413 {
01414     QPainter painter( this );
01415 
01416     if ( size() != d->currentLayoutSize ) {
01417         d->resizeLayout( size() );
01418         reLayoutFloatingLegends();
01419     }
01420 
01421     d->paintAll( &painter );
01422     emit finishedDrawing();
01423 }
01424 
01425 void Chart::addHeaderFooter( HeaderFooter* headerFooter )
01426 {
01427     TextAttributes textAttrs( headerFooter->textAttributes() );
01428     KDChart::Measure measure( textAttrs.fontSize() );
01429     measure.setRelativeMode( this, KDChartEnums::MeasureOrientationMinimum );
01430     measure.setValue( 20 );
01431     textAttrs.setFontSize( measure );
01432     headerFooter->setTextAttributes( textAttrs );
01433 
01434     d->headerFooters.append( headerFooter );
01435     headerFooter->setParent( this );
01436     connect( headerFooter, SIGNAL( destroyedHeaderFooter( HeaderFooter* ) ),
01437              d, SLOT( slotUnregisterDestroyedHeaderFooter( HeaderFooter* ) ) );
01438     connect( headerFooter, SIGNAL( positionChanged( HeaderFooter* ) ),
01439              d, SLOT( slotRelayout() ) );
01440     d->slotRelayout();
01441 }
01442 
01443 void Chart::replaceHeaderFooter( HeaderFooter* headerFooter,
01444                                  HeaderFooter* oldHeaderFooter_ )
01445 {
01446     if ( headerFooter && oldHeaderFooter_ != headerFooter ) {
01447         HeaderFooter* oldHeaderFooter = oldHeaderFooter_;
01448         if ( d->headerFooters.count() ) {
01449             if ( ! oldHeaderFooter ) {
01450                 oldHeaderFooter = d->headerFooters.first();
01451                 if ( oldHeaderFooter == headerFooter )
01452                     return;
01453             }
01454             takeHeaderFooter( oldHeaderFooter );
01455         }
01456         delete oldHeaderFooter;
01457         addHeaderFooter( headerFooter );
01458     }
01459 }
01460 
01461 void Chart::takeHeaderFooter( HeaderFooter* headerFooter )
01462 {
01463     const int idx = d->headerFooters.indexOf( headerFooter );
01464     if ( idx != -1 ) {
01465         d->headerFooters.takeAt( idx );
01466         disconnect( headerFooter, SIGNAL( destroyedHeaderFooter( HeaderFooter* ) ),
01467                     d, SLOT( slotUnregisterDestroyedHeaderFooter( HeaderFooter* ) ) );
01468         headerFooter->setParent( 0 );
01469     }
01470     d->slotRelayout();
01471     // Need to emit the signal: In case somebody has connected the signal
01472     // to her own slot for e.g. calling update() on a widget containing the chart.
01473     emit propertiesChanged();
01474 }
01475 
01476 HeaderFooter* Chart::headerFooter()
01477 {
01478     if ( d->headerFooters.isEmpty() ) {
01479         return 0;
01480     } else {
01481         return d->headerFooters.first();
01482     }
01483 }
01484 
01485 HeaderFooterList Chart::headerFooters()
01486 {
01487     return d->headerFooters;
01488 }
01489 
01490 void Chart::addLegend( Legend* legend )
01491 {
01492     if ( ! legend ) return;
01493 
01494     //qDebug() << "adding the legend";
01495     d->legends.append( legend );
01496     legend->setParent( this );
01497 
01498     TextAttributes textAttrs( legend->textAttributes() );
01499 
01500     KDChart::Measure measure( textAttrs.fontSize() );
01501     measure.setRelativeMode( this, KDChartEnums::MeasureOrientationMinimum );
01502     measure.setValue( 20 );
01503     textAttrs.setFontSize( measure );
01504     legend->setTextAttributes( textAttrs );
01505 
01506     textAttrs = legend->titleTextAttributes();
01507     measure.setRelativeMode( this, KDChartEnums::MeasureOrientationMinimum );
01508     measure.setValue( 24 );
01509     textAttrs.setFontSize( measure );
01510 
01511     legend->setTitleTextAttributes( textAttrs );
01512 
01513     legend->setReferenceArea( this );
01514 
01515 /*
01516     future: Use relative sizes for the markers too!
01517 
01518     const uint nMA = Legend::datasetCount();
01519     for ( uint iMA = 0; iMA < nMA; ++iMA ) {
01520         MarkerAttributes ma( legend->markerAttributes( iMA ) );
01521         ma.setMarkerSize( ... )
01522         legend->setMarkerAttributes( iMA, ma )
01523     }
01524 */
01525 
01526     connect( legend, SIGNAL( destroyedLegend( Legend* ) ),
01527              d, SLOT( slotUnregisterDestroyedLegend( Legend* ) ) );
01528     connect( legend, SIGNAL( positionChanged( AbstractAreaWidget* ) ),
01529              d, SLOT( slotLayoutPlanes() ) ); //slotRelayout() ) );
01530     connect( legend, SIGNAL( propertiesChanged() ),
01531              this,   SIGNAL( propertiesChanged() ) );
01532     legend->setVisible( true );
01533     d->slotRelayout();
01534 }
01535 
01536 
01537 void Chart::replaceLegend( Legend* legend, Legend* oldLegend_ )
01538 {
01539     if ( legend && oldLegend_ != legend ) {
01540         Legend* oldLegend = oldLegend_;
01541         if ( d->legends.count() ) {
01542             if ( ! oldLegend ) {
01543                 oldLegend = d->legends.first();
01544                 if ( oldLegend == legend )
01545                     return;
01546             }
01547             takeLegend( oldLegend );
01548         }
01549         delete oldLegend;
01550         addLegend( legend );
01551     }
01552 }
01553 
01554 void Chart::takeLegend( Legend* legend )
01555 {
01556     const int idx = d->legends.indexOf( legend );
01557     if ( idx != -1 ) {
01558         d->legends.takeAt( idx );
01559         disconnect( legend, SIGNAL( destroyedLegend( Legend* ) ),
01560                     d, SLOT( slotUnregisterDestroyedLegend( Legend* ) ) );
01561         disconnect( legend, SIGNAL( positionChanged( AbstractAreaWidget* ) ),
01562                     d, SLOT( slotLayoutPlanes() ) ); //slotRelayout() ) );
01563         disconnect( legend, SIGNAL( propertiesChanged() ),
01564                     this,   SIGNAL( propertiesChanged() ) );
01565         legend->setParent( 0 );
01566         legend->setVisible( false );
01567     }
01568     d->slotRelayout();
01569 
01570     // Need to emit the signal: In case somebody has connected the signal
01571     // to her own slot for e.g. calling update() on a widget containing the chart.
01572     // Note:
01573     // We do this ourselves in examples/DrawIntoPainter/mainwindow.cpp
01574     emit propertiesChanged();
01575 }
01576 
01577 Legend* Chart::legend()
01578 {
01579     return d->legends.isEmpty() ? 0 : d->legends.first();
01580 }
01581 
01582 LegendList Chart::legends()
01583 {
01584     return d->legends;
01585 }
01586 
01587 
01588 void Chart::mousePressEvent( QMouseEvent* event )
01589 {
01590     const QPoint pos = mapFromGlobal( event->globalPos() );
01591 
01592     KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes )
01593     {
01594         if ( plane->geometry().contains( event->pos() ) )
01595         {
01596             if ( plane->diagrams().size() > 0 )
01597             {
01598                 QMouseEvent ev( QEvent::MouseButtonPress, pos, event->globalPos(),
01599                                 event->button(), event->buttons(),
01600                                 event->modifiers() );
01601 
01602                 plane->mousePressEvent( &ev );
01603                 d->mouseClickedPlanes.append( plane );
01604            }
01605        }
01606     }
01607 }
01608 
01609 void Chart::mouseDoubleClickEvent( QMouseEvent* event )
01610 {
01611     const QPoint pos = mapFromGlobal( event->globalPos() );
01612 
01613     KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes )
01614     {
01615         if ( plane->geometry().contains( event->pos() ) )
01616         {
01617             if ( plane->diagrams().size() > 0 )
01618             {
01619                 QMouseEvent ev( QEvent::MouseButtonPress, pos, event->globalPos(),
01620                                 event->button(), event->buttons(),
01621                                 event->modifiers() );
01622                 plane->mouseDoubleClickEvent( &ev );
01623             }
01624         }
01625     }
01626 }
01627 
01628 void Chart::mouseMoveEvent( QMouseEvent* event )
01629 {
01630     QSet< AbstractCoordinatePlane* > eventReceivers = QSet< AbstractCoordinatePlane* >::fromList( d->mouseClickedPlanes );
01631 
01632     KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes )
01633     {
01634         if ( plane->geometry().contains( event->pos() ) )
01635         {
01636             if ( plane->diagrams().size() > 0 )
01637             {
01638                 eventReceivers.insert( plane );
01639             }
01640         }
01641     }
01642 
01643     const QPoint pos = mapFromGlobal( event->globalPos() );
01644 
01645     KDAB_FOREACH( AbstractCoordinatePlane* plane, eventReceivers )
01646     {
01647         QMouseEvent ev( QEvent::MouseMove, pos, event->globalPos(),
01648                          event->button(), event->buttons(),
01649                          event->modifiers() );
01650         plane->mouseMoveEvent( &ev );
01651     }
01652 }
01653 
01654 void Chart::mouseReleaseEvent( QMouseEvent* event )
01655 {
01656     QSet< AbstractCoordinatePlane* > eventReceivers = QSet< AbstractCoordinatePlane* >::fromList( d->mouseClickedPlanes );
01657 
01658     KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes )
01659     {
01660         if ( plane->geometry().contains( event->pos() ) )
01661         {
01662             if ( plane->diagrams().size() > 0 )
01663             {
01664                 eventReceivers.insert( plane );
01665             }
01666         }
01667     }
01668 
01669     const QPoint pos = mapFromGlobal( event->globalPos() );
01670 
01671     KDAB_FOREACH( AbstractCoordinatePlane* plane, eventReceivers )
01672     {
01673         QMouseEvent ev( QEvent::MouseButtonRelease, pos, event->globalPos(),
01674                          event->button(), event->buttons(),
01675                          event->modifiers() );
01676         plane->mouseReleaseEvent( &ev );
01677     }
01678 
01679     d->mouseClickedPlanes.clear();
01680 }
01681 
01682 bool Chart::event( QEvent* event )
01683 {
01684     if ( event->type() == QEvent::ToolTip ) {
01685         const QHelpEvent* const helpEvent = static_cast< QHelpEvent* >( event );
01686         KDAB_FOREACH( const AbstractCoordinatePlane* const plane, d->coordinatePlanes ) {
01687             KDAB_FOREACH( const AbstractDiagram* diagram, plane->diagrams() ) {
01688                 const QModelIndex index = diagram->indexAt( helpEvent->pos() );
01689                 const QVariant toolTip = index.data( Qt::ToolTipRole );
01690                 if ( toolTip.isValid() ) {
01691                     QPoint pos = mapFromGlobal( helpEvent->pos() );
01692                     QRect rect( pos - QPoint( 1, 1 ), QSize( 3, 3 ) );
01693                     QToolTip::showText( QCursor::pos(), toolTip.toString(), this, rect );
01694                     return true;
01695                 }
01696             }
01697         }
01698     }
01699     return QWidget::event( event );
01700 }
01701 
01702 bool Chart::useNewLayoutSystem() const
01703 {
01704     return d_func()->useNewLayoutSystem;
01705 }
01706 void Chart::setUseNewLayoutSystem( bool value )
01707 {
01708     if ( d_func()->useNewLayoutSystem != value )
01709         d_func()->useNewLayoutSystem = value;
01710 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Defines

Klarälvdalens Datakonsult AB (KDAB)
Qt-related services and products
http://www.kdab.com/
http://www.kdab.com/products/kd-chart/