KD Chart 2
[rev.2.5]
|
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 }