KD Chart 2  [rev.2.8]
KDChartChart.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Copyright (C) 2001-2021 Klaralvdalens Datakonsult AB. All rights reserved.
3 **
4 ** This file is part of the KD Chart library.
5 **
6 ** Licensees holding valid commercial KD Chart licenses may use this file in
7 ** accordance with the KD Chart Commercial License Agreement provided with
8 ** the Software.
9 **
10 **
11 ** This file may be distributed and/or modified under the terms of the
12 ** GNU General Public License version 2 and version 3 as published by the
13 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
14 **
15 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17 **
18 ** Contact info@kdab.com if any conditions of this licensing are not
19 ** clear to you.
20 **
21 **********************************************************************/
22 
23 #include "KDChartChart.h"
24 #include "KDChartChart_p.h"
25 
26 #include <QList>
27 #include <QtDebug>
28 #include <QGridLayout>
29 #include <QLabel>
30 #include <QHash>
31 #include <QToolTip>
32 #include <QPainter>
33 #include <QPaintEvent>
34 #include <QLayoutItem>
35 #include <QPushButton>
36 #include <QApplication>
37 #include <QEvent>
38 
41 #include "KDChartHeaderFooter.h"
42 #include "KDChartEnums.h"
43 #include "KDChartLegend.h"
44 #include "KDChartLayoutItems.h"
45 #include <KDChartTextAttributes.h>
47 #include "KDChartPainterSaver_p.h"
49 
50 #include <algorithm>
51 
52 #if defined KDAB_EVAL
53 #include "../evaldialog/evaldialog.h"
54 #endif
55 
56 #include <KDABLibFakes>
57 
58 #if 0
59 // dumpLayoutTree dumps a QLayout tree in a hopefully easy to read format to stderr - feel free to
60 // use, improve and extend; it is very useful for looking at any layout problem.
61 
62 #include <typeinfo>
63 
64 // this is this different from both QRect::isEmpty() and QRect::isNull() for "wrong" QRects,
65 // i.e. those where topLeft() is actually below and / or right of bottomRight().
66 static bool isZeroArea(const QRect &r)
67 {
68  return !r.width() || !r.height();
69 }
70 
71 static QString lineProlog(int nestingDepth, int lineno)
72 {
73  QString numbering(QString::number(lineno).rightJustified(5).append(QChar::fromAscii(':')));
74  QString indent(nestingDepth * 4, QLatin1Char(' '));
75  return numbering + indent;
76 }
77 
78 static void dumpLayoutTreeRecurse(QLayout *l, int *counter, int depth)
79 {
80  const QLatin1String colorOn(isZeroArea(l->geometry()) ? "\033[0m" : "\033[32m");
81  const QLatin1String colorOff("\033[0m");
82 
83  QString prolog = lineProlog(depth, *counter);
84  (*counter)++;
85 
86  qDebug() << colorOn + prolog << l->metaObject()->className() << l->geometry()
87  << "hint" << l->sizeHint()
88  << l->hasHeightForWidth() << "min" << l->minimumSize()
89  << "max" << l->maximumSize()
90  << l->expandingDirections() << l->alignment()
91  << colorOff;
92  for (int i = 0; i < l->count(); i++) {
93  QLayoutItem *child = l->itemAt(i);
94  if (QLayout *childL = child->layout()) {
95  dumpLayoutTreeRecurse(childL, counter, depth + 1);
96  } else {
97  // The isZeroArea check culls usually largely useless output - you might want to remove it in
98  // some debugging situations. Add a boolean parameter to this and dumpLayoutTree() if you do.
99  if (!isZeroArea(child->geometry())) {
100  prolog = lineProlog(depth + 1, *counter);
101  (*counter)++;
102  qDebug() << colorOn + prolog << typeid(*child).name() << child->geometry()
103  << "hint" << child->sizeHint()
104  << child->hasHeightForWidth() << "min" << child->minimumSize()
105  << "max" << child->maximumSize()
106  << child->expandingDirections() << child->alignment()
107  << colorOff;
108  }
109  }
110  }
111 }
112 
113 static void dumpLayoutTree(QLayout *l)
114 {
115  int counter = 0;
116  dumpLayoutTreeRecurse(l, &counter, 0);
117 }
118 #endif
119 
120 static const Qt::Alignment s_gridAlignments[ 3 ][ 3 ] = { // [ row ][ column ]
121  { Qt::AlignTop | Qt::AlignLeft, Qt::AlignTop | Qt::AlignHCenter, Qt::AlignTop | Qt::AlignRight },
122  { Qt::AlignVCenter | Qt::AlignLeft, Qt::AlignVCenter | Qt::AlignHCenter, Qt::AlignVCenter | Qt::AlignRight },
123  { Qt::AlignBottom | Qt::AlignLeft, Qt::AlignBottom | Qt::AlignHCenter, Qt::AlignBottom | Qt::AlignRight }
124 };
125 
126 static void getRowAndColumnForPosition(KDChartEnums::PositionValue pos, int* row, int* column)
127 {
128  switch ( pos ) {
129  case KDChartEnums::PositionNorthWest: *row = 0; *column = 0;
130  break;
131  case KDChartEnums::PositionNorth: *row = 0; *column = 1;
132  break;
133  case KDChartEnums::PositionNorthEast: *row = 0; *column = 2;
134  break;
135  case KDChartEnums::PositionEast: *row = 1; *column = 2;
136  break;
137  case KDChartEnums::PositionSouthEast: *row = 2; *column = 2;
138  break;
139  case KDChartEnums::PositionSouth: *row = 2; *column = 1;
140  break;
141  case KDChartEnums::PositionSouthWest: *row = 2; *column = 0;
142  break;
143  case KDChartEnums::PositionWest: *row = 1; *column = 0;
144  break;
145  case KDChartEnums::PositionCenter: *row = 1; *column = 1;
146  break;
147  default: *row = -1; *column = -1;
148  break;
149  }
150 }
151 
152 using namespace KDChart;
153 
154 // Layout widgets even if they are not visible (that's why isEmpty() is overridden) - at least that
155 // was the original reason...
156 class MyWidgetItem : public QWidgetItem
157 {
158 public:
159  explicit MyWidgetItem(QWidget *w, Qt::Alignment alignment = 0)
160  : QWidgetItem( w )
161  {
162  setAlignment( alignment );
163  }
164 
165  // All of the methods are reimplemented from QWidgetItem, and work around some oddity in QLayout and / or
166  // KD Chart - I forgot the details between writing this code as an experiment and committing it, very
167  // sorry about that!
168  // Feel free to comment out any of them and then try the line-breaking feature in horizontal legends in
169  // the Legends/Advanced example. It will not work well in various ways - won't get enough space and look
170  // very broken, will inhibit resizing the window etc.
171 
172  QSize sizeHint() const override
173  {
174  QWidget* w = const_cast< MyWidgetItem * >( this )->widget();
175  return w->sizeHint();
176  }
177 
178  QSize minimumSize() const override
179  {
180  QWidget* w = const_cast< MyWidgetItem * >( this )->widget();
181  return w->minimumSize();
182  }
183 
184  QSize maximumSize() const override
185  {
186  QWidget* w = const_cast< MyWidgetItem * >( this )->widget();
187  return w->maximumSize();
188  }
189 
190  Qt::Orientations expandingDirections() const override
191  {
192  QWidget* w = const_cast< MyWidgetItem * >( this )->widget();
193  if ( isEmpty() ) {
194  return Qt::Orientations(0);
195  }
196  Qt::Orientations e = w->sizePolicy().expandingDirections();
197  return e;
198  }
199 
200  void setGeometry(const QRect &g) override
201  {
202  QWidget* w = const_cast< MyWidgetItem * >( this )->widget();
203  w->setGeometry(g);
204  }
205 
206  QRect geometry() const override
207  {
208  QWidget* w = const_cast< MyWidgetItem * >( this )->widget();
209  return w->geometry();
210  }
211 
212  bool hasHeightForWidth() const override
213  {
214  QWidget* w = const_cast< MyWidgetItem * >( this )->widget();
215  bool ret = !isEmpty() &&
216  qobject_cast< Legend* >( w )->hasHeightForWidth();
217  return ret;
218  }
219 
220  int heightForWidth( int width ) const override
221  {
222  QWidget* w = const_cast< MyWidgetItem * >( this )->widget();
223  int ret = w->heightForWidth( width );
224  return ret;
225  }
226 
227  bool isEmpty() const override {
228  QWidget* w = const_cast< MyWidgetItem * >( this )->widget();
229  // legend->hide() should indeed hide the legend,
230  // but a legend in a chart that hasn't been shown yet isn't hidden
231  // (as can happen when using Chart::paint() without showing the chart)
232  return w->isHidden() && w->testAttribute( Qt::WA_WState_ExplicitShowHide );
233  }
234 };
235 
236 // When "abusing" QLayouts to lay out items with different geometry from the backing QWidgets,
237 // some manual work is required to correctly update all the sublayouts.
238 // This is because all the convenient ways to deal with QLayouts assume QWidgets somewhere.
239 // What this does is somewhat similar to QLayout::activate(), but it never refers to the parent
240 // QWidget which has the wrong geometry.
241 static void invalidateLayoutTree( QLayoutItem *item )
242 {
243  QLayout *layout = item->layout();
244  if ( layout ) {
245  const int count = layout->count();
246  for ( int i = 0; i < count; i++ ) {
247  invalidateLayoutTree( layout->itemAt( i ) );
248  }
249  }
250  item->invalidate();
251 }
252 
253 void Chart::Private::slotUnregisterDestroyedLegend( Legend *l )
254 {
255  chart->takeLegend( l );
256 }
257 
258 void Chart::Private::slotUnregisterDestroyedHeaderFooter( HeaderFooter* hf )
259 {
260  chart->takeHeaderFooter( hf );
261 }
262 
263 void Chart::Private::slotUnregisterDestroyedPlane( AbstractCoordinatePlane* plane )
264 {
265  coordinatePlanes.removeAll( plane );
266  Q_FOREACH ( AbstractCoordinatePlane* p, coordinatePlanes ) {
267  if ( p->referenceCoordinatePlane() == plane) {
269  }
270  }
271  plane->layoutPlanes();
272 }
273 
274 Chart::Private::Private( Chart* chart_ )
275  : chart( chart_ )
276  , useNewLayoutSystem( false )
277  , layout( 0 )
278  , vLayout( 0 )
279  , planesLayout( 0 )
280  , headerLayout( 0 )
281  , footerLayout( 0 )
282  , dataAndLegendLayout( 0 )
283  , leftOuterSpacer( 0 )
284  , rightOuterSpacer( 0 )
285  , topOuterSpacer( 0 )
286  , bottomOuterSpacer( 0 )
287  , isFloatingLegendsLayoutDirty( true )
288  , isPlanesLayoutDirty( true )
289  , globalLeadingLeft( 0 )
290  , globalLeadingRight( 0 )
291  , globalLeadingTop( 0 )
292  , globalLeadingBottom( 0 )
293 {
294  for ( int row = 0; row < 3; ++row ) {
295  for ( int column = 0; column < 3; ++column ) {
296  for ( int i = 0; i < 2; i++ ) {
297  innerHdFtLayouts[ i ][ row ][ column ] = 0;
298  }
299  }
300  }
301 }
302 
303 Chart::Private::~Private()
304 {
305 }
306 
308 struct ConnectedComponentsComparator{
309  bool operator()( const LayoutGraphNode *lhs, const LayoutGraphNode *rhs ) const
310  {
311  return lhs->priority < rhs->priority;
312  }
313 };
314 
316 {
317  QVector< LayoutGraphNode* >connectedComponents;
318  QHash< LayoutGraphNode*, VisitorState > visitedComponents;
319  Q_FOREACH ( LayoutGraphNode* node, nodeList )
320  visitedComponents[ node ] = Unknown;
321  for ( int i = 0; i < nodeList.size(); ++i )
322  {
323  LayoutGraphNode *curNode = nodeList[ i ];
324  LayoutGraphNode *representativeNode = curNode;
325  if ( visitedComponents[ curNode ] != Visited )
326  {
327  QStack< LayoutGraphNode* > stack;
328  stack.push( curNode );
329  while ( !stack.isEmpty() )
330  {
331  curNode = stack.pop();
332  Q_ASSERT( visitedComponents[ curNode ] != Visited );
333  visitedComponents[ curNode ] = Visited;
334  if ( curNode->bottomSuccesor && visitedComponents[ curNode->bottomSuccesor ] != Visited )
335  stack.push( curNode->bottomSuccesor );
336  if ( curNode->leftSuccesor && visitedComponents[ curNode->leftSuccesor ] != Visited )
337  stack.push( curNode->leftSuccesor );
338  if ( curNode->sharedSuccesor && visitedComponents[ curNode->sharedSuccesor ] != Visited )
339  stack.push( curNode->sharedSuccesor );
340  if ( curNode->priority < representativeNode->priority )
341  representativeNode = curNode;
342  }
343  connectedComponents.append( representativeNode );
344  }
345  }
346  std::sort( connectedComponents.begin(), connectedComponents.end(), ConnectedComponentsComparator() );
347  return connectedComponents;
348 }
349 
350 struct PriorityComparator{
351 public:
352  PriorityComparator( QHash< AbstractCoordinatePlane*, LayoutGraphNode* > mapping )
353  : m_mapping( mapping )
354  {}
355  bool operator() ( AbstractCoordinatePlane *lhs, AbstractCoordinatePlane *rhs ) const
356  {
357  const LayoutGraphNode *lhsNode = m_mapping[ lhs ];
358  Q_ASSERT( lhsNode );
359  const LayoutGraphNode *rhsNode = m_mapping[ rhs ];
360  Q_ASSERT( rhsNode );
361  return lhsNode->priority < rhsNode->priority;
362  }
363 
364  const QHash< AbstractCoordinatePlane*, LayoutGraphNode* > m_mapping;
365 };
366 
367 void checkExistingAxes( LayoutGraphNode* node )
368 {
369  if ( node && node->diagramPlane && node->diagramPlane->diagram() )
370  {
371  AbstractCartesianDiagram *diag = qobject_cast< AbstractCartesianDiagram* >( node->diagramPlane->diagram() );
372  if ( diag )
373  {
374  Q_FOREACH( const CartesianAxis* axis, diag->axes() )
375  {
376  switch ( axis->position() )
377  {
378  case( CartesianAxis::Top ):
379  node->topAxesLayout = true;
380  break;
381  case( CartesianAxis::Bottom ):
382  node->bottomAxesLayout = true;
383  break;
384  case( CartesianAxis::Left ):
385  node->leftAxesLayout = true;
386  break;
387  case( CartesianAxis::Right ):
388  node->rightAxesLayout = true;
389  break;
390  }
391  }
392  }
393  }
394 }
395 
396 static void mergeNodeAxisInformation( LayoutGraphNode* lhs, LayoutGraphNode* rhs )
397 {
398  lhs->topAxesLayout |= rhs->topAxesLayout;
399  rhs->topAxesLayout = lhs->topAxesLayout;
400 
401  lhs->bottomAxesLayout |= rhs->bottomAxesLayout;
402  rhs->bottomAxesLayout = lhs->bottomAxesLayout;
403 
404  lhs->leftAxesLayout |= rhs->leftAxesLayout;
405  rhs->leftAxesLayout = lhs->leftAxesLayout;
406 
407  lhs->rightAxesLayout |= rhs->rightAxesLayout;
408  rhs->rightAxesLayout = lhs->rightAxesLayout;
409 }
410 
412  const CoordinatePlaneList& list,
413  Chart::Private::AxisType type,
414  QVector< CartesianAxis* >* sharedAxes )
415 {
416  if ( !plane || !plane->diagram() )
417  return CoordinatePlaneList();
418  Q_ASSERT( plane );
419  Q_ASSERT( plane->diagram() );
420  CoordinatePlaneList result;
421  AbstractCartesianDiagram* diagram = qobject_cast< AbstractCartesianDiagram* >( plane->diagram() );
422  if ( !diagram )
423  return CoordinatePlaneList();
424 
426  KDAB_FOREACH( CartesianAxis* axis, diagram->axes() ) {
427  if ( ( type == Chart::Private::Ordinate &&
428  ( axis->position() == CartesianAxis::Left || axis->position() == CartesianAxis::Right ) )
429  ||
430  ( type == Chart::Private::Abscissa &&
431  ( axis->position() == CartesianAxis::Top || axis->position() == CartesianAxis::Bottom ) ) ) {
432  axes.append( axis );
433  }
434  }
435  Q_FOREACH( AbstractCoordinatePlane *curPlane, list )
436  {
437  AbstractCartesianDiagram* diagram =
438  qobject_cast< AbstractCartesianDiagram* > ( curPlane->diagram() );
439  if ( !diagram )
440  continue;
441  Q_FOREACH( CartesianAxis* curSearchedAxis, axes )
442  {
443  Q_FOREACH( CartesianAxis* curAxis, diagram->axes() )
444  {
445  if ( curSearchedAxis == curAxis )
446  {
447  result.append( curPlane );
448  if ( !sharedAxes->contains( curSearchedAxis ) )
449  sharedAxes->append( curSearchedAxis );
450  }
451  }
452  }
453  }
454 
455  return result;
456 }
457 
464 QVector< LayoutGraphNode* > Chart::Private::buildPlaneLayoutGraph()
465 {
466  QHash< AbstractCoordinatePlane*, LayoutGraphNode* > planeNodeMapping;
468  // create all nodes and a mapping between plane and nodes
469  Q_FOREACH( AbstractCoordinatePlane* curPlane, coordinatePlanes )
470  {
471  if ( curPlane->diagram() )
472  {
473  allNodes.append( new LayoutGraphNode );
474  allNodes[ allNodes.size() - 1 ]->diagramPlane = curPlane;
475  allNodes[ allNodes.size() - 1 ]->priority = allNodes.size();
476  checkExistingAxes( allNodes[ allNodes.size() - 1 ] );
477  planeNodeMapping[ curPlane ] = allNodes[ allNodes.size() - 1 ];
478  }
479  }
480  // build the graph connections
481  Q_FOREACH( LayoutGraphNode* curNode, allNodes )
482  {
483  QVector< CartesianAxis* > sharedAxes;
484  CoordinatePlaneList xSharedPlanes = findSharingAxisDiagrams( curNode->diagramPlane, coordinatePlanes, Abscissa, &sharedAxes );
485  Q_ASSERT( sharedAxes.size() < 2 );
486  // TODO duplicated code make a method out of it
487  if ( sharedAxes.size() == 1 && xSharedPlanes.size() > 1 )
488  {
489  //xSharedPlanes.removeAll( sharedAxes.first()->diagram()->coordinatePlane() );
490  //std::sort( xSharedPlanes.begin(), xSharedPlanes.end(), PriorityComparator( planeNodeMapping ) );
491  for ( int i = 0; i < xSharedPlanes.size() - 1; ++i )
492  {
493  LayoutGraphNode *tmpNode = planeNodeMapping[ xSharedPlanes[ i ] ];
494  Q_ASSERT( tmpNode );
495  LayoutGraphNode *tmpNode2 = planeNodeMapping[ xSharedPlanes[ i + 1 ] ];
496  Q_ASSERT( tmpNode2 );
497  tmpNode->bottomSuccesor = tmpNode2;
498  }
499 // if ( sharedAxes.first()->diagram() && sharedAxes.first()->diagram()->coordinatePlane() )
500 // {
501 // LayoutGraphNode *lastNode = planeNodeMapping[ xSharedPlanes.last() ];
502 // Q_ASSERT( lastNode );
503 // Q_ASSERT( sharedAxes.first()->diagram()->coordinatePlane() );
504 // LayoutGraphNode *ownerNode = planeNodeMapping[ sharedAxes.first()->diagram()->coordinatePlane() ];
505 // Q_ASSERT( ownerNode );
506 // lastNode->bottomSuccesor = ownerNode;
507 // }
508  //merge AxisInformation, needs a two pass run
509  LayoutGraphNode axisInfoNode;
510  for ( int count = 0; count < 2; ++count )
511  {
512  for ( int i = 0; i < xSharedPlanes.size(); ++i )
513  {
514  mergeNodeAxisInformation( &axisInfoNode, planeNodeMapping[ xSharedPlanes[ i ] ] );
515  }
516  }
517  }
518  sharedAxes.clear();
519  CoordinatePlaneList ySharedPlanes = findSharingAxisDiagrams( curNode->diagramPlane, coordinatePlanes, Ordinate, &sharedAxes );
520  Q_ASSERT( sharedAxes.size() < 2 );
521  if ( sharedAxes.size() == 1 && ySharedPlanes.size() > 1 )
522  {
523  //ySharedPlanes.removeAll( sharedAxes.first()->diagram()->coordinatePlane() );
524  //std::sort( ySharedPlanes.begin(), ySharedPlanes.end(), PriorityComparator( planeNodeMapping ) );
525  for ( int i = 0; i < ySharedPlanes.size() - 1; ++i )
526  {
527  LayoutGraphNode *tmpNode = planeNodeMapping[ ySharedPlanes[ i ] ];
528  Q_ASSERT( tmpNode );
529  LayoutGraphNode *tmpNode2 = planeNodeMapping[ ySharedPlanes[ i + 1 ] ];
530  Q_ASSERT( tmpNode2 );
531  tmpNode->leftSuccesor = tmpNode2;
532  }
533 // if ( sharedAxes.first()->diagram() && sharedAxes.first()->diagram()->coordinatePlane() )
534 // {
535 // LayoutGraphNode *lastNode = planeNodeMapping[ ySharedPlanes.last() ];
536 // Q_ASSERT( lastNode );
537 // Q_ASSERT( sharedAxes.first()->diagram()->coordinatePlane() );
538 // LayoutGraphNode *ownerNode = planeNodeMapping[ sharedAxes.first()->diagram()->coordinatePlane() ];
539 // Q_ASSERT( ownerNode );
540 // lastNode->bottomSuccesor = ownerNode;
541 // }
542  //merge AxisInformation, needs a two pass run
543  LayoutGraphNode axisInfoNode;
544  for ( int count = 0; count < 2; ++count )
545  {
546  for ( int i = 0; i < ySharedPlanes.size(); ++i )
547  {
548  mergeNodeAxisInformation( &axisInfoNode, planeNodeMapping[ ySharedPlanes[ i ] ] );
549  }
550  }
551  }
552  sharedAxes.clear();
553  if ( curNode->diagramPlane->referenceCoordinatePlane() )
554  curNode->sharedSuccesor = planeNodeMapping[ curNode->diagramPlane->referenceCoordinatePlane() ];
555  }
556 
557  return allNodes;
558 }
559 
560 QHash<AbstractCoordinatePlane*, PlaneInfo> Chart::Private::buildPlaneLayoutInfos()
561 {
562  /* There are two ways in which planes can be caused to interact in
563  * where they are put layouting wise: The first is the reference plane. If
564  * such a reference plane is set, on a plane, it will use the same cell in the
565  * layout as that one. In addition to this, planes can share an axis. In that case
566  * they will be laid out in relation to each other as suggested by the position
567  * of the axis. If, for example Plane1 and Plane2 share an axis at position Left,
568  * that will result in the layout: Axis Plane1 Plane 2, vertically. If Plane1
569  * also happens to be Plane2's referece plane, both planes are drawn over each
570  * other. The reference plane concept allows two planes to share the same space
571  * even if neither has any axis, and in case there are shared axis, it is used
572  * to decided, whether the planes should be painted on top of each other or
573  * laid out vertically or horizontally next to each other. */
574  QHash<CartesianAxis*, AxisInfo> axisInfos;
575  QHash<AbstractCoordinatePlane*, PlaneInfo> planeInfos;
576  Q_FOREACH(AbstractCoordinatePlane* plane, coordinatePlanes ) {
577  PlaneInfo p;
578  // first check if we share space with another plane
579  p.referencePlane = plane->referenceCoordinatePlane();
580  planeInfos.insert( plane, p );
581 
582  Q_FOREACH( AbstractDiagram* abstractDiagram, plane->diagrams() ) {
583  AbstractCartesianDiagram* diagram =
584  qobject_cast<AbstractCartesianDiagram*> ( abstractDiagram );
585  if ( !diagram ) {
586  continue;
587  }
588 
589  Q_FOREACH( CartesianAxis* axis, diagram->axes() ) {
590  if ( !axisInfos.contains( axis ) ) {
591  /* If this is the first time we see this axis, add it, with the
592  * current plane. The first plane added to the chart that has
593  * the axis associated with it thus "owns" it, and decides about
594  * layout. */
595  AxisInfo i;
596  i.plane = plane;
597  axisInfos.insert( axis, i );
598  } else {
599  AxisInfo i = axisInfos[axis];
600  if ( i.plane == plane ) {
601  continue; // we don't want duplicates, only shared
602  }
603 
604  /* The user expects diagrams to be added on top, and to the right
605  * so that horizontally we need to move the new diagram, vertically
606  * the reference one. */
607  PlaneInfo pi = planeInfos[plane];
608  // plane-to-plane linking overrides linking via axes
609  if ( !pi.referencePlane ) {
610  // we're not the first plane to see this axis, mark us as a slave
611  pi.referencePlane = i.plane;
612  if ( axis->position() == CartesianAxis::Left ||
613  axis->position() == CartesianAxis::Right ) {
614  pi.horizontalOffset += 1;
615  }
616  planeInfos[plane] = pi;
617 
618  pi = planeInfos[i.plane];
619  if ( axis->position() == CartesianAxis::Top ||
620  axis->position() == CartesianAxis::Bottom ) {
621  pi.verticalOffset += 1;
622  }
623 
624  planeInfos[i.plane] = pi;
625  }
626  }
627  }
628  }
629  // Create a new grid layout for each plane that has no reference.
630  p = planeInfos[plane];
631  if ( p.referencePlane == 0 ) {
632  p.gridLayout = new QGridLayout();
633  p.gridLayout->setMargin( 0 );
634  planeInfos[plane] = p;
635  }
636  }
637  return planeInfos;
638 }
639 
640 void Chart::Private::slotLayoutPlanes()
641 {
642  /*TODO make sure this is really needed */
643  const QBoxLayout::Direction oldPlanesDirection = planesLayout ? planesLayout->direction()
644  : QBoxLayout::TopToBottom;
645  if ( planesLayout && dataAndLegendLayout )
646  dataAndLegendLayout->removeItem( planesLayout );
647 
648  const bool hadPlanesLayout = planesLayout != 0;
649  int left, top, right, bottom;
650  if ( hadPlanesLayout )
651  planesLayout->getContentsMargins(&left, &top, &right, &bottom);
652 
653  KDAB_FOREACH( AbstractLayoutItem* plane, planeLayoutItems ) {
654  plane->removeFromParentLayout();
655  }
656  //TODO they should get a correct parent, but for now it works
657  KDAB_FOREACH( AbstractLayoutItem* plane, planeLayoutItems ) {
658  if ( dynamic_cast< AutoSpacerLayoutItem* >( plane ) )
659  delete plane;
660  }
661 
662  planeLayoutItems.clear();
663  delete planesLayout;
664  //hint: The direction is configurable by the user now, as
665  // we are using a QBoxLayout rather than a QVBoxLayout. (khz, 2007/04/25)
666  planesLayout = new QBoxLayout( oldPlanesDirection );
667 
668  isPlanesLayoutDirty = true; // here we create the layouts; we need to "run" them before painting
669 
670  if ( useNewLayoutSystem )
671  {
672  gridPlaneLayout = new QGridLayout;
673  planesLayout->addLayout( gridPlaneLayout );
674 
675  if (hadPlanesLayout)
676  planesLayout->setContentsMargins(left, top, right, bottom);
677  planesLayout->setObjectName( QString::fromLatin1( "planesLayout" ) );
678 
679  /* First go through all planes and all axes and figure out whether the planes
680  * need to coordinate. If they do, they share a grid layout, if not, each
681  * get their own. See buildPlaneLayoutInfos() for more details. */
682 
683  QVector< LayoutGraphNode* > vals = buildPlaneLayoutGraph();
684  //qDebug() << Q_FUNC_INFO << "GraphNodes" << vals.size();
686  //qDebug() << Q_FUNC_INFO << "SubGraphs" << connectedComponents.size();
687  int row = 0;
688  int col = 0;
689  QSet< CartesianAxis* > laidOutAxes;
690  for ( int i = 0; i < connectedComponents.size(); ++i )
691  {
692  LayoutGraphNode *curComponent = connectedComponents[ i ];
693  for ( LayoutGraphNode *curRowComponent = curComponent; curRowComponent; curRowComponent = curRowComponent->bottomSuccesor )
694  {
695  col = 0;
696  for ( LayoutGraphNode *curColComponent = curRowComponent; curColComponent; curColComponent = curColComponent->leftSuccesor )
697  {
698  Q_ASSERT( curColComponent->diagramPlane->diagrams().size() == 1 );
699  Q_FOREACH( AbstractDiagram* diagram, curColComponent->diagramPlane->diagrams() )
700  {
701  const int planeRowOffset = 1;//curColComponent->topAxesLayout ? 1 : 0;
702  const int planeColOffset = 1;//curColComponent->leftAxesLayout ? 1 : 0;
703  //qDebug() << Q_FUNC_INFO << row << col << planeRowOffset << planeColOffset;
704 
705  //qDebug() << Q_FUNC_INFO << row + planeRowOffset << col + planeColOffset;
706  planeLayoutItems << curColComponent->diagramPlane;
707  AbstractCartesianDiagram *cartDiag = qobject_cast< AbstractCartesianDiagram* >( diagram );
708  if ( cartDiag )
709  {
710  gridPlaneLayout->addItem( curColComponent->diagramPlane, row + planeRowOffset, col + planeColOffset, 2, 2 );
711  curColComponent->diagramPlane->setParentLayout( gridPlaneLayout );
712  QHBoxLayout *leftLayout = 0;
713  QHBoxLayout *rightLayout = 0;
714  QVBoxLayout *topLayout = 0;
715  QVBoxLayout *bottomLayout = 0;
716  if ( curComponent->sharedSuccesor )
717  {
718  gridPlaneLayout->addItem( curColComponent->sharedSuccesor->diagramPlane, row + planeRowOffset, col + planeColOffset, 2, 2 );
719  curColComponent->sharedSuccesor->diagramPlane->setParentLayout( gridPlaneLayout );
720  planeLayoutItems << curColComponent->sharedSuccesor->diagramPlane;
721  }
722  Q_FOREACH( CartesianAxis* axis, cartDiag->axes() )
723  {
724  if ( axis->isAbscissa() )
725  {
726  if ( curColComponent->bottomSuccesor )
727  continue;
728  }
729  if ( laidOutAxes.contains( axis ) )
730  continue;
731  // if ( axis->diagram() != diagram )
732  // continue;
733  switch ( axis->position() )
734  {
735  case( CartesianAxis::Top ):
736  if ( !topLayout )
737  topLayout = new QVBoxLayout;
738  topLayout->addItem( axis );
739  axis->setParentLayout( topLayout );
740  break;
741  case( CartesianAxis::Bottom ):
742  if ( !bottomLayout )
743  bottomLayout = new QVBoxLayout;
744  bottomLayout->addItem( axis );
745  axis->setParentLayout( bottomLayout );
746  break;
747  case( CartesianAxis::Left ):
748  if ( !leftLayout )
749  leftLayout = new QHBoxLayout;
750  leftLayout->addItem( axis );
751  axis->setParentLayout( leftLayout );
752  break;
753  case( CartesianAxis::Right ):
754  if ( !rightLayout )
755  {
756  rightLayout = new QHBoxLayout;
757  }
758  rightLayout->addItem( axis );
759  axis->setParentLayout( rightLayout );
760  break;
761  }
762  planeLayoutItems << axis;
763  laidOutAxes.insert( axis );
764  }
765  if ( leftLayout )
766  gridPlaneLayout->addLayout( leftLayout, row + planeRowOffset, col, 2, 1,
767  Qt::AlignRight | Qt::AlignVCenter );
768  if ( rightLayout )
769  gridPlaneLayout->addLayout( rightLayout, row, col + planeColOffset + 2, 2, 1,
770  Qt::AlignLeft | Qt::AlignVCenter );
771  if ( topLayout )
772  gridPlaneLayout->addLayout( topLayout, row, col + planeColOffset, 1, 2,
773  Qt::AlignBottom | Qt::AlignHCenter );
774  if ( bottomLayout )
775  gridPlaneLayout->addLayout( bottomLayout, row + planeRowOffset + 2,
776  col + planeColOffset, 1, 2, Qt::AlignTop | Qt::AlignHCenter );
777  }
778  else
779  {
780  gridPlaneLayout->addItem( curColComponent->diagramPlane, row, col, 4, 4 );
781  curColComponent->diagramPlane->setParentLayout( gridPlaneLayout );
782  }
783  col += planeColOffset + 2 + ( 1 );
784  }
785  }
786  int axisOffset = 2;//curRowComponent->topAxesLayout ? 1 : 0;
787  //axisOffset += curRowComponent->bottomAxesLayout ? 1 : 0;
788  const int rowOffset = axisOffset + 2;
789  row += rowOffset;
790  }
791 
792  // if ( planesLayout->direction() == QBoxLayout::TopToBottom )
793  // ++row;
794  // else
795  // ++col;
796  }
797 
798  qDeleteAll( vals );
799  // re-add our grid(s) to the chart's layout
800  if ( dataAndLegendLayout ) {
801  dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
802  dataAndLegendLayout->setRowStretch( 1, 1000 );
803  dataAndLegendLayout->setColumnStretch( 1, 1000 );
804  }
805  slotResizePlanes();
806 #ifdef NEW_LAYOUT_DEBUG
807  for ( int i = 0; i < gridPlaneLayout->rowCount(); ++i )
808  {
809  for ( int j = 0; j < gridPlaneLayout->columnCount(); ++j )
810  {
811  if ( gridPlaneLayout->itemAtPosition( i, j ) )
812  qDebug() << Q_FUNC_INFO << "item at" << i << j << gridPlaneLayout->itemAtPosition( i, j )->geometry();
813  else
814  qDebug() << Q_FUNC_INFO << "item at" << i << j << "no item present";
815  }
816  }
817  //qDebug() << Q_FUNC_INFO << "Relayout ended";
818 #endif
819  } else {
820  if ( hadPlanesLayout ) {
821  planesLayout->setContentsMargins( left, top, right, bottom );
822  }
823 
824  planesLayout->setMargin( 0 );
825  planesLayout->setSpacing( 0 );
826  planesLayout->setObjectName( QString::fromLatin1( "planesLayout" ) );
827 
828  /* First go through all planes and all axes and figure out whether the planes
829  * need to coordinate. If they do, they share a grid layout, if not, each
830  * gets their own. See buildPlaneLayoutInfos() for more details. */
831  QHash<AbstractCoordinatePlane*, PlaneInfo> planeInfos = buildPlaneLayoutInfos();
832  QHash<AbstractAxis*, AxisInfo> axisInfos;
833  KDAB_FOREACH( AbstractCoordinatePlane* plane, coordinatePlanes ) {
834  Q_ASSERT( planeInfos.contains(plane) );
835  PlaneInfo& pi = planeInfos[ plane ];
836  const int column = pi.horizontalOffset;
837  const int row = pi.verticalOffset;
838  //qDebug() << "processing plane at column" << column << "and row" << row;
839  QGridLayout *planeLayout = pi.gridLayout;
840 
841  if ( !planeLayout ) {
842  PlaneInfo& refPi = pi;
843  // if this plane is sharing an axis with another one, recursively check for the original plane and use
844  // the grid of that as planeLayout.
845  while ( !planeLayout && refPi.referencePlane ) {
846  refPi = planeInfos[refPi.referencePlane];
847  planeLayout = refPi.gridLayout;
848  }
849  Q_ASSERT_X( planeLayout,
850  "Chart::Private::slotLayoutPlanes()",
851  "Invalid reference plane. Please check that the reference plane has been added to the Chart." );
852  } else {
853  planesLayout->addLayout( planeLayout );
854  }
855 
856  /* Put the plane in the center of the layout. If this is our own, that's
857  * the middle of the layout, if we are sharing, it's a cell in the center
858  * column of the shared grid. */
859  planeLayoutItems << plane;
860  plane->setParentLayout( planeLayout );
861  planeLayout->addItem( plane, row, column, 1, 1, 0 );
862  //qDebug() << "Chart slotLayoutPlanes() calls planeLayout->addItem("<< row << column << ")";
863  planeLayout->setRowStretch( row, 2 );
864  planeLayout->setColumnStretch( column, 2 );
865  KDAB_FOREACH( AbstractDiagram* abstractDiagram, plane->diagrams() )
866  {
867  AbstractCartesianDiagram* diagram =
868  qobject_cast< AbstractCartesianDiagram* >( abstractDiagram );
869  if ( !diagram ) {
870  continue; // FIXME what about polar ?
871  }
872 
873  if ( pi.referencePlane != 0 )
874  {
875  pi.topAxesLayout = planeInfos[ pi.referencePlane ].topAxesLayout;
876  pi.bottomAxesLayout = planeInfos[ pi.referencePlane ].bottomAxesLayout;
877  pi.leftAxesLayout = planeInfos[ pi.referencePlane ].leftAxesLayout;
878  pi.rightAxesLayout = planeInfos[ pi.referencePlane ].rightAxesLayout;
879  }
880 
881  // collect all axes of a kind into sublayouts
882  if ( pi.topAxesLayout == 0 )
883  {
884  pi.topAxesLayout = new QVBoxLayout;
885  pi.topAxesLayout->setMargin( 0 );
886  pi.topAxesLayout->setObjectName( QString::fromLatin1( "topAxesLayout" ) );
887  }
888  if ( pi.bottomAxesLayout == 0 )
889  {
890  pi.bottomAxesLayout = new QVBoxLayout;
891  pi.bottomAxesLayout->setMargin( 0 );
892  pi.bottomAxesLayout->setObjectName( QString::fromLatin1( "bottomAxesLayout" ) );
893  }
894  if ( pi.leftAxesLayout == 0 )
895  {
896  pi.leftAxesLayout = new QHBoxLayout;
897  pi.leftAxesLayout->setMargin( 0 );
898  pi.leftAxesLayout->setObjectName( QString::fromLatin1( "leftAxesLayout" ) );
899  }
900  if ( pi.rightAxesLayout == 0 )
901  {
902  pi.rightAxesLayout = new QHBoxLayout;
903  pi.rightAxesLayout->setMargin( 0 );
904  pi.rightAxesLayout->setObjectName( QString::fromLatin1( "rightAxesLayout" ) );
905  }
906 
907  if ( pi.referencePlane != 0 )
908  {
909  planeInfos[ pi.referencePlane ].topAxesLayout = pi.topAxesLayout;
910  planeInfos[ pi.referencePlane ].bottomAxesLayout = pi.bottomAxesLayout;
911  planeInfos[ pi.referencePlane ].leftAxesLayout = pi.leftAxesLayout;
912  planeInfos[ pi.referencePlane ].rightAxesLayout = pi.rightAxesLayout;
913  }
914 
915  //pi.leftAxesLayout->setSizeConstraint( QLayout::SetFixedSize );
916  KDAB_FOREACH( CartesianAxis* axis, diagram->axes() ) {
917  if ( axisInfos.contains( axis ) ) {
918  continue; // already laid out this one
919  }
920  Q_ASSERT ( axis );
921  axis->setCachedSizeDirty();
922  //qDebug() << "--------------- axis added to planeLayoutItems -----------------";
923  planeLayoutItems << axis;
924 
925  switch ( axis->position() ) {
926  case CartesianAxis::Top:
927  axis->setParentLayout( pi.topAxesLayout );
928  pi.topAxesLayout->addItem( axis );
929  break;
930  case CartesianAxis::Bottom:
931  axis->setParentLayout( pi.bottomAxesLayout );
932  pi.bottomAxesLayout->addItem( axis );
933  break;
934  case CartesianAxis::Left:
935  axis->setParentLayout( pi.leftAxesLayout );
936  pi.leftAxesLayout->addItem( axis );
937  break;
938  case CartesianAxis::Right:
939  axis->setParentLayout( pi.rightAxesLayout );
940  pi.rightAxesLayout->addItem( axis );
941  break;
942  default:
943  Q_ASSERT_X( false, "Chart::paintEvent", "unknown axis position" );
944  break;
945  };
946  axisInfos.insert( axis, AxisInfo() );
947  }
948  /* Put each stack of axes-layouts in the cells surrounding the
949  * associated plane. We are laying out in the oder the planes
950  * were added, and the first one gets to lay out shared axes.
951  * Private axes go here as well, of course. */
952 
953  if ( !pi.topAxesLayout->parent() ) {
954  planeLayout->addLayout( pi.topAxesLayout, row - 1, column );
955  }
956  if ( !pi.bottomAxesLayout->parent() ) {
957  planeLayout->addLayout( pi.bottomAxesLayout, row + 1, column );
958  }
959  if ( !pi.leftAxesLayout->parent() ) {
960  planeLayout->addLayout( pi.leftAxesLayout, row, column - 1 );
961  }
962  if ( !pi.rightAxesLayout->parent() ) {
963  planeLayout->addLayout( pi.rightAxesLayout,row, column + 1 );
964  }
965  }
966 
967  // use up to four auto-spacer items in the corners around the diagrams:
968  #define ADD_AUTO_SPACER_IF_NEEDED( \
969  spacerRow, spacerColumn, hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ) \
970  { \
971  if ( hLayout || vLayout ) { \
972  AutoSpacerLayoutItem * spacer \
973  = new AutoSpacerLayoutItem( hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ); \
974  planeLayout->addItem( spacer, spacerRow, spacerColumn, 1, 1 ); \
975  spacer->setParentLayout( planeLayout ); \
976  planeLayoutItems << spacer; \
977  } \
978  }
979 
980  if ( plane->isCornerSpacersEnabled() ) {
981  ADD_AUTO_SPACER_IF_NEEDED( row - 1, column - 1, false, pi.leftAxesLayout, false, pi.topAxesLayout )
982  ADD_AUTO_SPACER_IF_NEEDED( row + 1, column - 1, true, pi.leftAxesLayout, false, pi.bottomAxesLayout )
983  ADD_AUTO_SPACER_IF_NEEDED( row - 1, column + 1, false, pi.rightAxesLayout, true, pi.topAxesLayout )
984  ADD_AUTO_SPACER_IF_NEEDED( row + 1, column + 1, true, pi.rightAxesLayout, true, pi.bottomAxesLayout )
985  }
986  }
987  // re-add our grid(s) to the chart's layout
988  if ( dataAndLegendLayout ) {
989  dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
990  dataAndLegendLayout->setRowStretch( 1, 1000 );
991  dataAndLegendLayout->setColumnStretch( 1, 1000 );
992  }
993 
994  slotResizePlanes();
995  }
996 }
997 
998 void Chart::Private::createLayouts()
999 {
1000  // The toplevel layout provides the left and right global margins
1001  layout = new QHBoxLayout( chart );
1002  layout->setMargin( 0 );
1003  layout->setObjectName( QString::fromLatin1( "Chart::Private::layout" ) );
1004  layout->addSpacing( globalLeadingLeft );
1005  leftOuterSpacer = layout->itemAt( layout->count() - 1 )->spacerItem();
1006 
1007  // The vLayout provides top and bottom global margins and lays
1008  // out headers, footers and the diagram area.
1009  vLayout = new QVBoxLayout();
1010  vLayout->setMargin( 0 );
1011  vLayout->setObjectName( QString::fromLatin1( "vLayout" ) );
1012 
1013  layout->addLayout( vLayout, 1000 );
1014  layout->addSpacing( globalLeadingRight );
1015  rightOuterSpacer = layout->itemAt( layout->count() - 1 )->spacerItem();
1016 
1017  // 1. the spacing above the header area
1018  vLayout->addSpacing( globalLeadingTop );
1019  topOuterSpacer = vLayout->itemAt( vLayout->count() - 1 )->spacerItem();
1020  // 2. the header area
1021  headerLayout = new QGridLayout();
1022  headerLayout->setMargin( 0 );
1023  vLayout->addLayout( headerLayout );
1024  // 3. the area containing coordinate planes, axes, and legends
1025  dataAndLegendLayout = new QGridLayout();
1026  dataAndLegendLayout->setMargin( 0 );
1027  dataAndLegendLayout->setObjectName( QString::fromLatin1( "dataAndLegendLayout" ) );
1028  vLayout->addLayout( dataAndLegendLayout, 1000 );
1029  // 4. the footer area
1030  footerLayout = new QGridLayout();
1031  footerLayout->setMargin( 0 );
1032  footerLayout->setObjectName( QString::fromLatin1( "footerLayout" ) );
1033  vLayout->addLayout( footerLayout );
1034 
1035  // 5. Prepare the header / footer layout cells:
1036  // Each of the 9 header cells (the 9 footer cells)
1037  // contain their own QVBoxLayout
1038  // since there can be more than one header (footer) per cell.
1039  for ( int row = 0; row < 3; ++row ) {
1040  for ( int column = 0; column < 3; ++ column ) {
1041  const Qt::Alignment align = s_gridAlignments[ row ][ column ];
1042  for ( int headOrFoot = 0; headOrFoot < 2; headOrFoot++ ) {
1043  QVBoxLayout* innerLayout = new QVBoxLayout();
1044  innerLayout->setMargin( 0 );
1045  innerLayout->setAlignment( align );
1046  innerHdFtLayouts[ headOrFoot ][ row ][ column ] = innerLayout;
1047 
1048  QGridLayout* outerLayout = headOrFoot == 0 ? headerLayout : footerLayout;
1049  outerLayout->addLayout( innerLayout, row, column, align );
1050  }
1051  }
1052  }
1053 
1054  // 6. the spacing below the footer area
1055  vLayout->addSpacing( globalLeadingBottom );
1056  bottomOuterSpacer = vLayout->itemAt( vLayout->count() - 1 )->spacerItem();
1057 
1058  // the data+axes area
1059  dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
1060  dataAndLegendLayout->setRowStretch( 1, 1 );
1061  dataAndLegendLayout->setColumnStretch( 1, 1 );
1062 }
1063 
1064 void Chart::Private::slotResizePlanes()
1065 {
1066  if ( !dataAndLegendLayout ) {
1067  return;
1068  }
1069  if ( !overrideSize.isValid() ) {
1070  // activate() takes the size from the layout's parent QWidget, which is not updated when overrideSize
1071  // is set. So don't let the layout grab the wrong size in that case.
1072  // When overrideSize *is* set, we call layout->setGeometry() in paint( QPainter*, const QRect& ),
1073  // which also "activates" the layout in the sense that it distributes space internally.
1074  layout->activate();
1075  }
1076  // Adapt diagram drawing to the new size
1077  KDAB_FOREACH (AbstractCoordinatePlane* plane, coordinatePlanes ) {
1078  plane->layoutDiagrams();
1079  }
1080 }
1081 
1082 void Chart::Private::updateDirtyLayouts()
1083 {
1084  if ( isPlanesLayoutDirty ) {
1085  Q_FOREACH ( AbstractCoordinatePlane* p, coordinatePlanes ) {
1087  p->layoutPlanes();
1088  p->layoutDiagrams();
1089  }
1090  }
1091  if ( isPlanesLayoutDirty || isFloatingLegendsLayoutDirty ) {
1092  chart->reLayoutFloatingLegends();
1093  }
1094  isPlanesLayoutDirty = false;
1095  isFloatingLegendsLayoutDirty = false;
1096 }
1097 
1098 void Chart::Private::reapplyInternalLayouts()
1099 {
1100  QRect geo = layout->geometry();
1101 
1102  invalidateLayoutTree( layout );
1103  layout->setGeometry( geo );
1104  slotResizePlanes();
1105 }
1106 
1107 void Chart::Private::paintAll( QPainter* painter )
1108 {
1109  updateDirtyLayouts();
1110 
1111  QRect rect( QPoint( 0, 0 ), overrideSize.isValid() ? overrideSize : chart->size() );
1112 
1113  //qDebug() << this<<"::paintAll() uses layout size" << currentLayoutSize;
1114 
1115  // Paint the background (if any)
1116  AbstractAreaBase::paintBackgroundAttributes( *painter, rect, backgroundAttributes );
1117  // Paint the frame (if any)
1118  AbstractAreaBase::paintFrameAttributes( *painter, rect, frameAttributes );
1119 
1120  chart->reLayoutFloatingLegends();
1121 
1122  KDAB_FOREACH( AbstractLayoutItem* planeLayoutItem, planeLayoutItems ) {
1123  planeLayoutItem->paintAll( *painter );
1124  }
1125  KDAB_FOREACH( TextArea* textLayoutItem, textLayoutItems ) {
1126  textLayoutItem->paintAll( *painter );
1127  }
1128  KDAB_FOREACH( Legend *legend, legends ) {
1129  const bool hidden = legend->isHidden() && legend->testAttribute( Qt::WA_WState_ExplicitShowHide );
1130  if ( !hidden ) {
1131  //qDebug() << "painting legend at " << legend->geometry();
1132  legend->paintIntoRect( *painter, legend->geometry() );
1133  }
1134  }
1135 }
1136 
1137 // ******** Chart interface implementation ***********
1138 
1139 #define d d_func()
1140 
1141 Chart::Chart ( QWidget* parent )
1142  : QWidget ( parent )
1143  , _d( new Private( this ) )
1144 {
1145 #if defined KDAB_EVAL
1146  EvalDialog::checkEvalLicense( "KD Chart" );
1147 #endif
1148 
1149  FrameAttributes frameAttrs;
1150 // no frame per default...
1151 // frameAttrs.setVisible( true );
1152  frameAttrs.setPen( QPen( Qt::black ) );
1153  frameAttrs.setPadding( 1 );
1154  setFrameAttributes( frameAttrs );
1155 
1157 
1158  d->createLayouts();
1159 }
1160 
1162 {
1163  delete d;
1164 }
1165 
1167 {
1168  d->frameAttributes = a;
1169 }
1170 
1172 {
1173  return d->frameAttributes;
1174 }
1175 
1177 {
1178  d->backgroundAttributes = a;
1179 }
1180 
1182 {
1183  return d->backgroundAttributes;
1184 }
1185 
1186 //TODO KDChart 3.0; change QLayout into QBoxLayout::Direction
1187 void Chart::setCoordinatePlaneLayout( QLayout * layout )
1188 {
1189  if (layout == d->planesLayout)
1190  return;
1191  if (d->planesLayout) {
1192  // detach all QLayoutItem's the previous planesLayout has cause
1193  // otherwise deleting the planesLayout would delete them too.
1194  for(int i = d->planesLayout->count() - 1; i >= 0; --i) {
1195  d->planesLayout->takeAt(i);
1196  }
1197  delete d->planesLayout;
1198  }
1199  d->planesLayout = qobject_cast<QBoxLayout*>( layout );
1200  d->slotLayoutPlanes();
1201 }
1202 
1204 {
1205  return d->planesLayout;
1206 }
1207 
1209 {
1210  if ( d->coordinatePlanes.isEmpty() ) {
1211  qWarning() << "Chart::coordinatePlane: warning: no coordinate plane defined.";
1212  return 0;
1213  } else {
1214  return d->coordinatePlanes.first();
1215  }
1216 }
1217 
1219 {
1220  return d->coordinatePlanes;
1221 }
1222 
1224 {
1225  // Append
1226  insertCoordinatePlane( d->coordinatePlanes.count(), plane );
1227 }
1228 
1230 {
1231  if ( index < 0 || index > d->coordinatePlanes.count() ) {
1232  return;
1233  }
1234 
1235  connect( plane, SIGNAL( destroyedCoordinatePlane( AbstractCoordinatePlane* ) ),
1236  d, SLOT( slotUnregisterDestroyedPlane( AbstractCoordinatePlane* ) ) );
1237  connect( plane, SIGNAL( needUpdate() ), this, SLOT( update() ) );
1238  connect( plane, SIGNAL( needRelayout() ), d, SLOT( slotResizePlanes() ) ) ;
1239  connect( plane, SIGNAL( needLayoutPlanes() ), d, SLOT( slotLayoutPlanes() ) ) ;
1240  connect( plane, SIGNAL( propertiesChanged() ),this, SIGNAL( propertiesChanged() ) );
1241  d->coordinatePlanes.insert( index, plane );
1242  plane->setParent( this );
1243  d->slotLayoutPlanes();
1244 }
1245 
1247  AbstractCoordinatePlane* oldPlane_ )
1248 {
1249  if ( plane && oldPlane_ != plane ) {
1250  AbstractCoordinatePlane* oldPlane = oldPlane_;
1251  if ( d->coordinatePlanes.count() ) {
1252  if ( ! oldPlane ) {
1253  oldPlane = d->coordinatePlanes.first();
1254  if ( oldPlane == plane )
1255  return;
1256  }
1257  takeCoordinatePlane( oldPlane );
1258  }
1259  delete oldPlane;
1260  addCoordinatePlane( plane );
1261  }
1262 }
1263 
1265 {
1266  const int idx = d->coordinatePlanes.indexOf( plane );
1267  if ( idx != -1 ) {
1268  d->coordinatePlanes.takeAt( idx );
1269  disconnect( plane, 0, d, 0 );
1270  disconnect( plane, 0, this, 0 );
1271  plane->removeFromParentLayout();
1272  plane->setParent( 0 );
1273  d->mouseClickedPlanes.removeAll(plane);
1274  }
1275  d->slotLayoutPlanes();
1276  // Need to emit the signal: In case somebody has connected the signal
1277  // to her own slot for e.g. calling update() on a widget containing the chart.
1278  emit propertiesChanged();
1279 }
1280 
1281 void Chart::setGlobalLeading( int left, int top, int right, int bottom )
1282 {
1283  setGlobalLeadingLeft( left );
1284  setGlobalLeadingTop( top );
1285  setGlobalLeadingRight( right );
1286  setGlobalLeadingBottom( bottom );
1287 }
1288 
1289 void Chart::setGlobalLeadingLeft( int leading )
1290 {
1291  d->globalLeadingLeft = leading;
1292  d->leftOuterSpacer->changeSize( leading, 0, QSizePolicy::Fixed, QSizePolicy::Minimum );
1293  d->reapplyInternalLayouts();
1294 }
1295 
1297 {
1298  return d->globalLeadingLeft;
1299 }
1300 
1301 void Chart::setGlobalLeadingTop( int leading )
1302 {
1303  d->globalLeadingTop = leading;
1304  d->topOuterSpacer->changeSize( 0, leading, QSizePolicy::Minimum, QSizePolicy::Fixed );
1305  d->reapplyInternalLayouts();
1306 }
1307 
1309 {
1310  return d->globalLeadingTop;
1311 }
1312 
1313 void Chart::setGlobalLeadingRight( int leading )
1314 {
1315  d->globalLeadingRight = leading;
1316  d->rightOuterSpacer->changeSize( leading, 0, QSizePolicy::Fixed, QSizePolicy::Minimum );
1317  d->reapplyInternalLayouts();
1318 }
1319 
1321 {
1322  return d->globalLeadingRight;
1323 }
1324 
1326 {
1327  d->globalLeadingBottom = leading;
1328  d->bottomOuterSpacer->changeSize( 0, leading, QSizePolicy::Minimum, QSizePolicy::Fixed );
1329  d->reapplyInternalLayouts();
1330 }
1331 
1333 {
1334  return d->globalLeadingBottom;
1335 }
1336 
1337 void Chart::paint( QPainter* painter, const QRect& target )
1338 {
1339  if ( target.isEmpty() || !painter ) {
1340  return;
1341  }
1342 
1344  GlobalMeasureScaling::setPaintDevice( painter->device() );
1345 
1346  // Output on a widget
1347  if ( dynamic_cast< QWidget* >( painter->device() ) != 0 ) {
1348  GlobalMeasureScaling::setFactors( qreal( target.width() ) / qreal( geometry().size().width() ),
1349  qreal( target.height() ) / qreal( geometry().size().height() ) );
1350  } else {
1351  // Output onto a QPixmap
1352  PrintingParameters::setScaleFactor( qreal( painter->device()->logicalDpiX() ) / qreal( logicalDpiX() ) );
1353 
1354  const qreal resX = qreal( logicalDpiX() ) / qreal( painter->device()->logicalDpiX() );
1355  const qreal resY = qreal( logicalDpiY() ) / qreal( painter->device()->logicalDpiY() );
1356 
1357  GlobalMeasureScaling::setFactors( qreal( target.width() ) / qreal( geometry().size().width() ) * resX,
1358  qreal( target.height() ) / qreal( geometry().size().height() ) * resY );
1359  }
1360 
1361  const QPoint translation = target.topLeft();
1362  painter->translate( translation );
1363 
1364  // the following layout logic has the disadvantage that repeatedly calling this method can
1365  // cause a relayout every time, but since this method's main use seems to be printing, the
1366  // gratuitous relayouts shouldn't be much of a performance problem.
1367  const bool differentSize = target.size() != size();
1368  QRect oldGeometry;
1369  if ( differentSize ) {
1370  oldGeometry = geometry();
1371  d->isPlanesLayoutDirty = true;
1372  d->isFloatingLegendsLayoutDirty = true;
1373  invalidateLayoutTree( d->dataAndLegendLayout );
1374  d->dataAndLegendLayout->setGeometry( QRect( QPoint(), target.size() ) );
1375  }
1376 
1377  d->overrideSize = target.size();
1378  d->paintAll( painter );
1379  d->overrideSize = QSize();
1380 
1381  if ( differentSize ) {
1382  invalidateLayoutTree( d->dataAndLegendLayout );
1383  d->dataAndLegendLayout->setGeometry( oldGeometry );
1384  d->isPlanesLayoutDirty = true;
1385  d->isFloatingLegendsLayoutDirty = true;
1386  }
1387 
1388  // for debugging
1389  // painter->setPen( QPen( Qt::blue, 8 ) );
1390  // painter->drawRect( target );
1391 
1392  painter->translate( -translation.x(), -translation.y() );
1393 
1397 }
1398 
1399 void Chart::resizeEvent ( QResizeEvent* event )
1400 {
1401  d->isPlanesLayoutDirty = true;
1402  d->isFloatingLegendsLayoutDirty = true;
1403  QWidget::resizeEvent( event );
1404 }
1405 
1407 {
1408  KDAB_FOREACH( Legend *legend, d->legends ) {
1409  const bool hidden = legend->isHidden() && legend->testAttribute( Qt::WA_WState_ExplicitShowHide );
1410  if ( legend->position().isFloating() && !hidden ) {
1411  // resize the legend
1412  const QSize legendSize( legend->sizeHint() );
1413  legend->setGeometry( QRect( legend->geometry().topLeft(), legendSize ) );
1414  // find the legends corner point (reference point plus any paddings)
1415  const RelativePosition relPos( legend->floatingPosition() );
1416  QPointF pt( relPos.calculatedPoint( size() ) );
1417  //qDebug() << pt;
1418  // calculate the legend's top left point
1419  const Qt::Alignment alignTopLeft = Qt::AlignBottom | Qt::AlignLeft;
1420  if ( (relPos.alignment() & alignTopLeft) != alignTopLeft ) {
1421  if ( relPos.alignment() & Qt::AlignRight )
1422  pt.rx() -= legendSize.width();
1423  else if ( relPos.alignment() & Qt::AlignHCenter )
1424  pt.rx() -= 0.5 * legendSize.width();
1425 
1426  if ( relPos.alignment() & Qt::AlignBottom )
1427  pt.ry() -= legendSize.height();
1428  else if ( relPos.alignment() & Qt::AlignVCenter )
1429  pt.ry() -= 0.5 * legendSize.height();
1430  }
1431  //qDebug() << pt << endl;
1432  legend->move( static_cast<int>(pt.x()), static_cast<int>(pt.y()) );
1433  }
1434  }
1435 }
1436 
1437 
1438 void Chart::paintEvent( QPaintEvent* )
1439 {
1440  QPainter painter( this );
1441  d->paintAll( &painter );
1442  emit finishedDrawing();
1443 }
1444 
1446 {
1447  Q_ASSERT( hf->type() == HeaderFooter::Header || hf->type() == HeaderFooter::Footer );
1448  int row;
1449  int column;
1450  getRowAndColumnForPosition( hf->position().value(), &row, &column );
1451  if ( row == -1 ) {
1452  qWarning( "Unknown header/footer position" );
1453  return;
1454  }
1455 
1456  d->headerFooters.append( hf );
1457  d->textLayoutItems.append( hf );
1458  connect( hf, SIGNAL( destroyedHeaderFooter( HeaderFooter* ) ),
1459  d, SLOT( slotUnregisterDestroyedHeaderFooter( HeaderFooter* ) ) );
1460  connect( hf, SIGNAL( positionChanged( HeaderFooter* ) ),
1461  d, SLOT( slotHeaderFooterPositionChanged( HeaderFooter* ) ) );
1462 
1463  // set the text attributes (why?)
1464 
1465  TextAttributes textAttrs( hf->textAttributes() );
1466  Measure measure( textAttrs.fontSize() );
1468  measure.setValue( 20 );
1469  textAttrs.setFontSize( measure );
1470  hf->setTextAttributes( textAttrs );
1471 
1472  // add it to the appropriate layout
1473 
1474  int innerLayoutIdx = hf->type() == HeaderFooter::Header ? 0 : 1;
1475  QVBoxLayout* headerFooterLayout = d->innerHdFtLayouts[ innerLayoutIdx ][ row ][ column ];
1476 
1477  hf->setParentLayout( headerFooterLayout );
1478  hf->setAlignment( s_gridAlignments[ row ][ column ] );
1479  headerFooterLayout->addItem( hf );
1480 
1481  d->slotResizePlanes();
1482 }
1483 
1485  HeaderFooter* oldHeaderFooter_ )
1486 {
1487  if ( headerFooter && oldHeaderFooter_ != headerFooter ) {
1488  HeaderFooter* oldHeaderFooter = oldHeaderFooter_;
1489  if ( d->headerFooters.count() ) {
1490  if ( ! oldHeaderFooter ) {
1491  oldHeaderFooter = d->headerFooters.first();
1492  if ( oldHeaderFooter == headerFooter )
1493  return;
1494  }
1495  takeHeaderFooter( oldHeaderFooter );
1496  }
1497  delete oldHeaderFooter;
1499  }
1500 }
1501 
1503 {
1504  const int idx = d->headerFooters.indexOf( headerFooter );
1505  if ( idx == -1 ) {
1506  return;
1507  }
1508  disconnect( headerFooter, SIGNAL( destroyedHeaderFooter( HeaderFooter* ) ),
1509  d, SLOT( slotUnregisterDestroyedHeaderFooter( HeaderFooter* ) ) );
1510 
1511  d->headerFooters.takeAt( idx );
1514  d->textLayoutItems.remove( d->textLayoutItems.indexOf( headerFooter ) );
1515 
1516  d->slotResizePlanes();
1517 }
1518 
1519 void Chart::Private::slotHeaderFooterPositionChanged( HeaderFooter* hf )
1520 {
1521  chart->takeHeaderFooter( hf );
1522  chart->addHeaderFooter( hf );
1523 }
1524 
1526 {
1527  if ( d->headerFooters.isEmpty() ) {
1528  return 0;
1529  } else {
1530  return d->headerFooters.first();
1531  }
1532 }
1533 
1535 {
1536  return d->headerFooters;
1537 }
1538 
1539 void Chart::Private::slotLegendPositionChanged( AbstractAreaWidget* aw )
1540 {
1541  Legend* legend = qobject_cast< Legend* >( aw );
1542  Q_ASSERT( legend );
1543  chart->takeLegend( legend );
1544  chart->addLegendInternal( legend, false );
1545 }
1546 
1547 void Chart::addLegend( Legend* legend )
1548 {
1549  legend->show();
1550  addLegendInternal( legend, true );
1551  emit propertiesChanged();
1552 }
1553 
1554 void Chart::addLegendInternal( Legend* legend, bool setMeasures )
1555 {
1556  if ( !legend ) {
1557  return;
1558  }
1559 
1561  if ( pos == KDChartEnums::PositionCenter ) {
1562  qWarning( "Not showing legend because PositionCenter is not supported for legends." );
1563  }
1564 
1565  int row;
1566  int column;
1567  getRowAndColumnForPosition( pos, &row, &column );
1568  if ( row < 0 && pos != KDChartEnums::PositionFloating ) {
1569  qWarning( "Not showing legend because of unknown legend position." );
1570  return;
1571  }
1572 
1573  d->legends.append( legend );
1574  legend->setParent( this );
1575 
1576  // set text attributes (why?)
1577 
1578  if ( setMeasures ) {
1579  TextAttributes textAttrs( legend->textAttributes() );
1580  Measure measure( textAttrs.fontSize() );
1581  measure.setRelativeMode( this, KDChartEnums::MeasureOrientationMinimum );
1582  measure.setValue( 20 );
1583  textAttrs.setFontSize( measure );
1584  legend->setTextAttributes( textAttrs );
1585 
1586  textAttrs = legend->titleTextAttributes();
1587  measure.setRelativeMode( this, KDChartEnums::MeasureOrientationMinimum );
1588  measure.setValue( 24 );
1589  textAttrs.setFontSize( measure );
1590 
1591  legend->setTitleTextAttributes( textAttrs );
1592  legend->setReferenceArea( this );
1593  }
1594 
1595  // add it to the appropriate layout
1596 
1597  if ( pos != KDChartEnums::PositionFloating ) {
1598  legend->needSizeHint();
1599 
1600  // in each edge and corner of the outer layout, there's a grid for the different alignments that we create
1601  // on demand. we don't remove it when empty.
1602 
1603  QLayoutItem* edgeItem = d->dataAndLegendLayout->itemAtPosition( row, column );
1604  QGridLayout* alignmentsLayout = dynamic_cast< QGridLayout* >( edgeItem );
1605  Q_ASSERT( !edgeItem || alignmentsLayout ); // if it exists, it must be a QGridLayout
1606  if ( !alignmentsLayout ) {
1607  alignmentsLayout = new QGridLayout;
1608  d->dataAndLegendLayout->addLayout( alignmentsLayout, row, column );
1609  alignmentsLayout->setMargin( 0 );
1610  }
1611 
1612  // in case there are several legends in the same edge or corner with the same alignment, they are stacked
1613  // vertically using a QVBoxLayout. it is created on demand as above.
1614 
1615  row = 1;
1616  column = 1;
1617  for ( int i = 0; i < 3; i++ ) {
1618  for ( int j = 0; j < 3; j++ ) {
1619  Qt::Alignment align = s_gridAlignments[ i ][ j ];
1620  if ( align == legend->alignment() ) {
1621  row = i;
1622  column = j;
1623  break;
1624  }
1625  }
1626  }
1627 
1628  QLayoutItem* alignmentItem = alignmentsLayout->itemAtPosition( row, column );
1629  QVBoxLayout* sameAlignmentLayout = dynamic_cast< QVBoxLayout* >( alignmentItem );
1630  Q_ASSERT( !alignmentItem || sameAlignmentLayout ); // if it exists, it must be a QVBoxLayout
1631  if ( !sameAlignmentLayout ) {
1632  sameAlignmentLayout = new QVBoxLayout;
1633  alignmentsLayout->addLayout( sameAlignmentLayout, row, column );
1634  sameAlignmentLayout->setMargin( 0 );
1635  }
1636 
1637  sameAlignmentLayout->addItem( new MyWidgetItem( legend, legend->alignment() ) );
1638  }
1639 
1640  connect( legend, SIGNAL( destroyedLegend( Legend* ) ),
1641  d, SLOT( slotUnregisterDestroyedLegend( Legend* ) ) );
1642  connect( legend, SIGNAL( positionChanged( AbstractAreaWidget* ) ),
1643  d, SLOT( slotLegendPositionChanged( AbstractAreaWidget* ) ) );
1644  connect( legend, SIGNAL( propertiesChanged() ), this, SIGNAL( propertiesChanged() ) );
1645 
1646  d->slotResizePlanes();
1647 }
1648 
1649 void Chart::replaceLegend( Legend* legend, Legend* oldLegend_ )
1650 {
1651  if ( legend && oldLegend_ != legend ) {
1652  Legend* oldLegend = oldLegend_;
1653  if ( d->legends.count() ) {
1654  if ( ! oldLegend ) {
1655  oldLegend = d->legends.first();
1656  if ( oldLegend == legend )
1657  return;
1658  }
1659  takeLegend( oldLegend );
1660  }
1661  delete oldLegend;
1662  addLegend( legend );
1663  }
1664 }
1665 
1666 void Chart::takeLegend( Legend* legend )
1667 {
1668  const int idx = d->legends.indexOf( legend );
1669  if ( idx == -1 ) {
1670  return;
1671  }
1672 
1673  d->legends.takeAt( idx );
1674  disconnect( legend, 0, d, 0 );
1675  disconnect( legend, 0, this, 0 );
1676  // the following removes the legend from its layout and destroys its MyWidgetItem (the link to the layout)
1677  legend->setParent( 0 );
1678 
1679  d->slotResizePlanes();
1680  emit propertiesChanged();
1681 }
1682 
1684 {
1685  return d->legends.isEmpty() ? 0 : d->legends.first();
1686 }
1687 
1689 {
1690  return d->legends;
1691 }
1692 
1693 void Chart::mousePressEvent( QMouseEvent* event )
1694 {
1695  const QPoint pos = mapFromGlobal( event->globalPos() );
1696 
1697  KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) {
1698  if ( plane->geometry().contains( event->pos() ) && plane->diagrams().size() > 0 ) {
1699  QMouseEvent ev( QEvent::MouseButtonPress, pos, event->globalPos(),
1700  event->button(), event->buttons(), event->modifiers() );
1701  plane->mousePressEvent( &ev );
1702  d->mouseClickedPlanes.append( plane );
1703  }
1704  }
1705 }
1706 
1707 void Chart::mouseDoubleClickEvent( QMouseEvent* event )
1708 {
1709  const QPoint pos = mapFromGlobal( event->globalPos() );
1710 
1711  KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) {
1712  if ( plane->geometry().contains( event->pos() ) && plane->diagrams().size() > 0 ) {
1713  QMouseEvent ev( QEvent::MouseButtonPress, pos, event->globalPos(),
1714  event->button(), event->buttons(), event->modifiers() );
1715  plane->mouseDoubleClickEvent( &ev );
1716  }
1717  }
1718 }
1719 
1720 void Chart::mouseMoveEvent( QMouseEvent* event )
1721 {
1722  QSet< AbstractCoordinatePlane* > eventReceivers = QSet< AbstractCoordinatePlane* >::fromList( d->mouseClickedPlanes );
1723 
1724  KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) {
1725  if ( plane->geometry().contains( event->pos() ) && plane->diagrams().size() > 0 ) {
1726  eventReceivers.insert( plane );
1727  }
1728  }
1729 
1730  const QPoint pos = mapFromGlobal( event->globalPos() );
1731 
1732  KDAB_FOREACH( AbstractCoordinatePlane* plane, eventReceivers ) {
1733  QMouseEvent ev( QEvent::MouseMove, pos, event->globalPos(),
1734  event->button(), event->buttons(), event->modifiers() );
1735  plane->mouseMoveEvent( &ev );
1736  }
1737 }
1738 
1739 void Chart::mouseReleaseEvent( QMouseEvent* event )
1740 {
1741  QSet< AbstractCoordinatePlane* > eventReceivers = QSet< AbstractCoordinatePlane* >::fromList( d->mouseClickedPlanes );
1742 
1743  KDAB_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) {
1744  if ( plane->geometry().contains( event->pos() ) && plane->diagrams().size() > 0 ) {
1745  eventReceivers.insert( plane );
1746  }
1747  }
1748 
1749  const QPoint pos = mapFromGlobal( event->globalPos() );
1750 
1751  KDAB_FOREACH( AbstractCoordinatePlane* plane, eventReceivers ) {
1752  QMouseEvent ev( QEvent::MouseButtonRelease, pos, event->globalPos(),
1753  event->button(), event->buttons(), event->modifiers() );
1754  plane->mouseReleaseEvent( &ev );
1755  }
1756 
1757  d->mouseClickedPlanes.clear();
1758 }
1759 
1760 bool Chart::event( QEvent* event )
1761 {
1762  if ( event->type() == QEvent::ToolTip ) {
1763  const QHelpEvent* const helpEvent = static_cast< QHelpEvent* >( event );
1764  for (int stage = 0; stage < 2; ++stage) {
1765  KDAB_FOREACH( const AbstractCoordinatePlane* const plane, d->coordinatePlanes ) {
1766  KDAB_FOREACH( const AbstractDiagram* diagram, plane->diagrams() ) {
1767 
1768  QModelIndex index;
1769  if (stage == 0) {
1770  // First search at the exact position
1771  index = diagram->indexAt(helpEvent->pos());
1772  }
1773  else {
1774  // Second, search in a larger area, which is easier to hit on screens with higher DPI.
1775  const QModelIndexList indexes = diagram->indexesIn(QRect( helpEvent->pos() - QPoint(15,15), QSize(30,30)));
1776  index = indexes.isEmpty() ? QModelIndex() : indexes.front();
1777  }
1778 
1779  const QVariant toolTip = index.data( Qt::ToolTipRole );
1780  if ( toolTip.isValid() ) {
1781  const QPoint pos = mapFromGlobal( helpEvent->pos() );
1782  const QRect rect( pos - QPoint( 1, 1 ), QSize( 3, 3 ) );
1783  QToolTip::showText( QCursor::pos(), toolTip.toString(), this, rect );
1784  return true;
1785  }
1786  }
1787  }
1788  }
1789  }
1790  return QWidget::event( event );
1791 }
1792 
1794 {
1795  return d_func()->useNewLayoutSystem;
1796 }
1798 {
1799  if ( d_func()->useNewLayoutSystem != value )
1800  d_func()->useNewLayoutSystem = value;
1801 }
KDChart::GlobalMeasureScaling::setFactors
static void setFactors(qreal factorX, qreal factorY)
Set new factors to be used by all Measure objects from now on.
Definition: KDChartMeasure.cpp:205
KDChart::Chart::globalLeadingBottom
int globalLeadingBottom
Definition: KDChartChart.h:103
KDChart::PrintingParameters::resetScaleFactor
static void resetScaleFactor()
Definition: KDChartPrintingParameters.cpp:43
KDChart::GlobalMeasureScaling::paintDevice
static QPaintDevice * paintDevice()
Return the paint device to use for calculating font metrics.
Definition: KDChartMeasure.cpp:227
KDChart::Chart::headerFooter
HeaderFooter * headerFooter()
The first header or footer of the chart.
Definition: KDChartChart.cpp:1525
KDChartEnums::PositionValue
PositionValue
Numerical values of the static KDChart::Position instances, for using a Position::value() with a swit...
Definition: KDChartEnums.h:192
ADD_AUTO_SPACER_IF_NEEDED
#define ADD_AUTO_SPACER_IF_NEEDED(spacerRow, spacerColumn, hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout)
KDChart::Chart::globalLeadingLeft
int globalLeadingLeft
Definition: KDChartChart.h:104
KDChart::Position::isFloating
bool isFloating() const
Definition: KDChartPosition.cpp:174
KDChart::TextLayoutItem::setTextAttributes
void setTextAttributes(const TextAttributes &a)
Use this to specify the text attributes to be used for this item.
Definition: KDChartLayoutItems.cpp:277
QWidget
Class only listed here to document inheritance of some KDChart classes.
KDChart::Legend::alignment
Qt::Alignment alignment() const
Returns the alignment of a non-floating legend.
Definition: KDChartLegend.cpp:474
VisitorState
VisitorState
Definition: KDChartChart.cpp:307
mergeNodeAxisInformation
static void mergeNodeAxisInformation(LayoutGraphNode *lhs, LayoutGraphNode *rhs)
Definition: KDChartChart.cpp:396
KDChartEnums::PositionSouthEast
@ PositionSouthEast
Definition: KDChartEnums.h:199
KDChart::Chart::setGlobalLeading
void setGlobalLeading(int left, int top, int right, int bottom)
Set the padding between the margin of the widget and the area that the contents are drawn into.
Definition: KDChartChart.cpp:1281
KDChart::AbstractCoordinatePlane::setParent
void setParent(Chart *parent)
Called internally by KDChart::Chart.
Definition: KDChartAbstractCoordinatePlane.cpp:188
KDChart::CartesianAxis::position
virtual Position position() const
Definition: KDChartCartesianAxis.cpp:488
KDChart::Chart::replaceHeaderFooter
void replaceHeaderFooter(HeaderFooter *headerFooter, HeaderFooter *oldHeaderFooter=0)
Replaces the old header (or footer, resp.), or appends the new header or footer, it there is none yet...
Definition: KDChartChart.cpp:1484
QList
Definition: KDChartPosition.h:36
KDChart::AbstractCoordinatePlane::mouseMoveEvent
virtual void mouseMoveEvent(QMouseEvent *event)
Definition: KDChartAbstractCoordinatePlane.cpp:407
KDChart::Chart::globalLeadingTop
int globalLeadingTop
Definition: KDChartChart.h:102
KDChart::Legend::setReferenceArea
void setReferenceArea(const QWidget *area)
Specifies the reference area for font size of title text, and for font size of the item texts,...
Definition: KDChartLegend.cpp:278
KDChart::AbstractAreaWidget
An area in the chart with a background, a frame, etc.
Definition: KDChartAbstractAreaWidget.h:45
KDChart::Chart::replaceLegend
void replaceLegend(Legend *legend, Legend *oldLegend=0)
Replaces the old legend, or appends the new legend, it there is none yet.
Definition: KDChartChart.cpp:1649
KDChart::AbstractCoordinatePlane::mousePressEvent
virtual void mousePressEvent(QMouseEvent *event)
Definition: KDChartAbstractCoordinatePlane.cpp:300
KDChart::Chart::insertCoordinatePlane
void insertCoordinatePlane(int index, AbstractCoordinatePlane *plane)
Inserts a coordinate plane to the chart at index index.
Definition: KDChartChart.cpp:1229
KDChart::AbstractCoordinatePlane::layoutPlanes
void layoutPlanes()
Calling layoutPlanes() on the plane triggers the global KDChart::Chart::slotLayoutPlanes()
Definition: KDChartAbstractCoordinatePlane.cpp:265
KDChart::Chart::setGlobalLeadingTop
void setGlobalLeadingTop(int leading)
Set the padding between the start of the widget and the start of the area that is used for drawing at...
Definition: KDChartChart.cpp:1301
KDChart::Legend::needSizeHint
void needSizeHint() override
Call this to trigger an conditional re-building of the widget's internals.
Definition: KDChartLegend.cpp:156
KDChart::Chart::mousePressEvent
void mousePressEvent(QMouseEvent *event) override
reimp
Definition: KDChartChart.cpp:1693
s_gridAlignments
static const Qt::Alignment s_gridAlignments[3][3]
Definition: KDChartChart.cpp:120
KDChart::Chart::takeCoordinatePlane
void takeCoordinatePlane(AbstractCoordinatePlane *plane)
Removes the coordinate plane from the chart, without deleting it.
Definition: KDChartChart.cpp:1264
KDChart::AbstractCoordinatePlane::geometry
QRect geometry() const override
pure virtual in QLayoutItem
Definition: KDChartAbstractCoordinatePlane.cpp:248
KDChart::GlobalMeasureScaling::setPaintDevice
static void setPaintDevice(QPaintDevice *paintDevice)
Set the paint device to use for calculating font metrics.
Definition: KDChartMeasure.cpp:222
KDChart::CartesianAxis
The class for cartesian axes.
Definition: KDChartCartesianAxis.h:43
KDChart::Chart::replaceCoordinatePlane
void replaceCoordinatePlane(AbstractCoordinatePlane *plane, AbstractCoordinatePlane *oldPlane=0)
Replaces the old coordinate plane, or appends the plane, it there is none yet.
Definition: KDChartChart.cpp:1246
KDChart::AbstractCoordinatePlane::setReferenceCoordinatePlane
void setReferenceCoordinatePlane(AbstractCoordinatePlane *plane)
Set another coordinate plane to be used as the reference plane for this one.
Definition: KDChartAbstractCoordinatePlane.cpp:178
KDChart::Chart::takeHeaderFooter
void takeHeaderFooter(HeaderFooter *headerFooter)
Removes the header (or footer, resp.) from the chart, without deleting it.
Definition: KDChartChart.cpp:1502
KDChart::Measure::setValue
void setValue(qreal val)
Definition: KDChartMeasure.h:61
KDChart::Chart::backgroundAttributes
BackgroundAttributes backgroundAttributes() const
Definition: KDChartChart.cpp:1181
KDChart::Chart::paint
void paint(QPainter *painter, const QRect &target)
Paints all the contents of the chart.
Definition: KDChartChart.cpp:1337
KDChart
Definition: KDChartAbstractCartesianDiagram.h:30
KDChart::AbstractCoordinatePlane::diagram
AbstractDiagram * diagram()
Definition: KDChartAbstractCoordinatePlane.cpp:130
KDChart::Chart::mouseReleaseEvent
void mouseReleaseEvent(QMouseEvent *event) override
reimp
Definition: KDChartChart.cpp:1739
KDChartCartesianCoordinatePlane.h
KDChart::AbstractLayoutItem::paintAll
virtual void paintAll(QPainter &painter)
Default impl: just call paint.
Definition: KDChartLayoutItems.cpp:64
KDChart::AbstractLayoutItem::removeFromParentLayout
void removeFromParentLayout()
Definition: KDChartLayoutItems.h:81
d
#define d
Definition: KDChartChart.cpp:1139
KDChart::Legend::sizeHint
QSize sizeHint() const override
Definition: KDChartLegend.cpp:145
KDChartEnums::PositionEast
@ PositionEast
Definition: KDChartEnums.h:198
KDChart::AutoSpacerLayoutItem
Definition: KDChartLayoutItems.h:449
KDChart::Position::value
KDChartEnums::PositionValue value() const
Returns an integer value corresponding to this Position.
Definition: KDChartPosition.cpp:126
KDChartPrintingParameters.h
KDChart::Chart::frameAttributes
FrameAttributes frameAttributes() const
Definition: KDChartChart.cpp:1171
KDChart::Chart::legends
LegendList legends()
The list of all legends associated with the chart.
Definition: KDChartChart.cpp:1688
KDChartLegend.h
KDChart::TextAttributes
A set of text attributes.
Definition: KDChartTextAttributes.h:47
KDChart::Measure
Measure is used to specify relative and absolute sizes in KDChart, e.g.
Definition: KDChartMeasure.h:52
KDChartEnums::PositionSouth
@ PositionSouth
Definition: KDChartEnums.h:200
KDChart::Chart::addHeaderFooter
void addHeaderFooter(HeaderFooter *headerFooter)
Adds a header or a footer to the chart.
Definition: KDChartChart.cpp:1445
KDChart::FrameAttributes::setPen
void setPen(const QPen &pen)
Definition: KDChartFrameAttributes.cpp:98
KDChart::Chart::mouseDoubleClickEvent
void mouseDoubleClickEvent(QMouseEvent *event) override
reimp
Definition: KDChartChart.cpp:1707
KDChart::Chart::resizeEvent
void resizeEvent(QResizeEvent *event) override
Adjusts the internal layout when the chart is resized.
Definition: KDChartChart.cpp:1399
KDChart::GlobalMeasureScaling::instance
static GlobalMeasureScaling * instance()
Definition: KDChartMeasure.cpp:199
KDChart::Chart::coordinatePlaneLayout
QLayout * coordinatePlaneLayout()
Definition: KDChartChart.cpp:1203
KDChart::Chart::coordinatePlanes
CoordinatePlaneList coordinatePlanes()
The list of coordinate planes.
Definition: KDChartChart.cpp:1218
KDChart::AbstractCoordinatePlane::referenceCoordinatePlane
AbstractCoordinatePlane * referenceCoordinatePlane() const
There are two ways, in which planes can be caused to interact, in where they are put layouting wise: ...
Definition: KDChartAbstractCoordinatePlane.cpp:183
QPaintDevice
KDChartEnums::PositionNorthEast
@ PositionNorthEast
Definition: KDChartEnums.h:197
KDChartMarkerAttributes.h
KDChart::Legend::setTitleTextAttributes
void setTitleTextAttributes(const TextAttributes &a)
Definition: KDChartLegend.cpp:745
KDChart::AbstractCartesianDiagram
Base class for diagrams based on a cartesian coordianate system.
Definition: KDChartAbstractCartesianDiagram.h:42
KDChart::Chart::setUseNewLayoutSystem
void setUseNewLayoutSystem(bool value)
Definition: KDChartChart.cpp:1797
KDChartEnums.h
Definition of global enums.
KDChart::RelativePosition
Defines relative position information: reference area, position in this area (reference position),...
Definition: KDChartRelativePosition.h:58
KDChart::Chart::propertiesChanged
void propertiesChanged()
Emitted upon change of a property of the Chart or any of its components.
KDChartAbstractCartesianDiagram.h
findSharingAxisDiagrams
static CoordinatePlaneList findSharingAxisDiagrams(AbstractCoordinatePlane *plane, const CoordinatePlaneList &list, Chart::Private::AxisType type, QVector< CartesianAxis * > *sharedAxes)
Definition: KDChartChart.cpp:411
KDChart::Chart::setBackgroundAttributes
void setBackgroundAttributes(const BackgroundAttributes &a)
Specify the background attributes to be used, by default there is no background.
Definition: KDChartChart.cpp:1176
invalidateLayoutTree
static void invalidateLayoutTree(QLayoutItem *item)
Definition: KDChartChart.cpp:241
KDChart::Chart::~Chart
~Chart() override
Definition: KDChartChart.cpp:1161
KDChart::RelativePosition::calculatedPoint
const QPointF calculatedPoint(const QSizeF &autoSize) const
Calculate a point, accordin to the reference area/position and the padding.
Definition: KDChartRelativePosition.cpp:187
KDChart::Chart::setGlobalLeadingLeft
void setGlobalLeadingLeft(int leading)
Set the padding between the start of the widget and the start of the area that is used for drawing on...
Definition: KDChartChart.cpp:1289
KDChart::BackgroundAttributes
Set of attributes usable for background pixmaps.
Definition: KDChartBackgroundAttributes.h:37
KDChart::Chart::addLegend
void addLegend(Legend *legend)
Add the given legend to the chart.
Definition: KDChartChart.cpp:1547
KDChart::Chart::setFrameAttributes
void setFrameAttributes(const FrameAttributes &a)
Specify the frame attributes to be used, by default is it a thin black line.
Definition: KDChartChart.cpp:1166
KDChart::Chart::setGlobalLeadingRight
void setGlobalLeadingRight(int leading)
Set the padding between the start of the widget and the start of the area that is used for drawing on...
Definition: KDChartChart.cpp:1313
KDChart::TextAttributes::setFontSize
void setFontSize(const Measure &measure)
Set the size of the font used for rendering text.
Definition: KDChartTextAttributes.cpp:140
KDChartLayoutItems.h
KDChart::PrintingParameters::setScaleFactor
static void setScaleFactor(const qreal scaleFactor)
Definition: KDChartPrintingParameters.cpp:38
KDChart::AbstractCoordinatePlane::diagrams
AbstractDiagramList diagrams()
Definition: KDChartAbstractCoordinatePlane.cpp:140
checkExistingAxes
void checkExistingAxes(LayoutGraphNode *node)
Definition: KDChartChart.cpp:367
KDChart::AbstractDiagram
AbstractDiagram defines the interface for diagram classes.
Definition: KDChartAbstractDiagram.h:51
KDChart::GlobalMeasureScaling::resetFactors
static void resetFactors()
Restore factors to the values before the previous call to setFactors.
Definition: KDChartMeasure.cpp:210
KDChart::AbstractDiagram::indexAt
QModelIndex indexAt(const QPoint &point) const override
[reimplemented]
Definition: KDChartAbstractDiagram.cpp:995
KDChart::TextLayoutItem::textAttributes
TextAttributes textAttributes() const
Returns the text attributes to be used for this item.
Definition: KDChartLayoutItems.cpp:292
KDChart::AbstractCoordinatePlane::setGridNeedsRecalculate
void setGridNeedsRecalculate()
Used by the chart to clear the cached grid data.
Definition: KDChartAbstractCoordinatePlane.cpp:173
KDChart::Legend::floatingPosition
const RelativePosition floatingPosition() const
Returns the position of a floating legend.
Definition: KDChartLegend.cpp:516
KDChart::Legend
Legend defines the interface for the legend drawing class.
Definition: KDChartLegend.h:56
KDChart::FrameAttributes::setPadding
void setPadding(int padding)
Definition: KDChartFrameAttributes.cpp:118
KDChart::AbstractCoordinatePlane::mouseReleaseEvent
virtual void mouseReleaseEvent(QMouseEvent *event)
Definition: KDChartAbstractCoordinatePlane.cpp:354
KDChart::FrameAttributes
A set of attributes for frames around items.
Definition: KDChartFrameAttributes.h:37
KDChart::CoordinatePlaneList
QList< AbstractCoordinatePlane * > CoordinatePlaneList
Definition: KDChartChart.h:74
KDChart::Chart::legend
Legend * legend()
The first legend of the chart or 0 if there was none added to the chart.
Definition: KDChartChart.cpp:1683
KDChart::Chart::reLayoutFloatingLegends
void reLayoutFloatingLegends()
Definition: KDChartChart.cpp:1406
KDChart::Legend::setTextAttributes
void setTextAttributes(const TextAttributes &a)
Definition: KDChartLegend.cpp:717
KDChart::Chart::takeLegend
void takeLegend(Legend *legend)
Removes the legend from the chart, without deleting it.
Definition: KDChartChart.cpp:1666
KDChart::Chart::finishedDrawing
void finishedDrawing()
KDChart::Chart::setGlobalLeadingBottom
void setGlobalLeadingBottom(int leading)
Set the padding between the start of the widget and the start of the area that is used for drawing on...
Definition: KDChartChart.cpp:1325
KDChartTextAttributes.h
KDChartEnums::PositionCenter
@ PositionCenter
Definition: KDChartEnums.h:194
KDChart::Chart::setCoordinatePlaneLayout
void setCoordinatePlaneLayout(QLayout *layout)
Set the coordinate plane layout that should be used as model for the internal used layout.
Definition: KDChartChart.cpp:1187
KDChartChart.h
KDChart::Chart::useNewLayoutSystem
bool useNewLayoutSystem
Definition: KDChartChart.h:106
KDChart::Chart::event
bool event(QEvent *event) override
reimp
Definition: KDChartChart.cpp:1760
KDChartEnums::PositionNorth
@ PositionNorth
Definition: KDChartEnums.h:196
KDChart::AbstractCoordinatePlane::mouseDoubleClickEvent
virtual void mouseDoubleClickEvent(QMouseEvent *event)
Definition: KDChartAbstractCoordinatePlane.cpp:340
KDChartEnums::PositionFloating
@ PositionFloating
Definition: KDChartEnums.h:203
KDChart::Legend::titleTextAttributes
TextAttributes titleTextAttributes() const
Definition: KDChartLegend.cpp:754
KDChart::TextAttributes::fontSize
Measure fontSize() const
Definition: KDChartTextAttributes.cpp:145
KDChart::TextArea
A text area in the chart with a background, a frame, etc.
Definition: KDChartTextArea.h:48
KDChart::CartesianAxis::isAbscissa
virtual bool isAbscissa() const
Definition: KDChartCartesianAxis.cpp:528
KDChart::Chart::globalLeadingRight
int globalLeadingRight
Definition: KDChartChart.h:105
KDChart::Chart::paintEvent
void paintEvent(QPaintEvent *event) override
Draws the background and frame, then calls paint().
Definition: KDChartChart.cpp:1438
KDChart::AbstractLayoutItem
Base class for all layout items of KD Chart.
Definition: KDChartLayoutItems.h:52
KDChart::CartesianCoordinatePlane
Cartesian coordinate plane.
Definition: KDChartCartesianCoordinatePlane.h:40
KDChart::Legend::textAttributes
TextAttributes textAttributes() const
Definition: KDChartLegend.cpp:726
KDChart::Chart::addCoordinatePlane
void addCoordinatePlane(AbstractCoordinatePlane *plane)
Adds a coordinate plane to the chart.
Definition: KDChartChart.cpp:1223
QVector
Definition: KDChartWidget.h:34
KDChart::AbstractAreaWidget::paintIntoRect
virtual void paintIntoRect(QPainter &painter, const QRect &rect)
Draws the background and frame, then calls paint().
Definition: KDChartAbstractAreaWidget.cpp:105
getRowAndColumnForPosition
static void getRowAndColumnForPosition(KDChartEnums::PositionValue pos, int *row, int *column)
Definition: KDChartChart.cpp:126
KDChart::Chart
A chart with one or more diagrams.
Definition: KDChartChart.h:99
getPrioritySortedConnectedComponents
static QVector< LayoutGraphNode * > getPrioritySortedConnectedComponents(QVector< LayoutGraphNode * > &nodeList)
Definition: KDChartChart.cpp:315
KDChart::Chart::headerFooters
HeaderFooterList headerFooters()
The list of headers and footers associated with the chart.
Definition: KDChartChart.cpp:1534
Unknown
@ Unknown
Definition: KDChartChart.cpp:307
KDChart::AbstractDiagram::indexesIn
QModelIndexList indexesIn(const QRect &rect) const
Definition: KDChartAbstractDiagram.cpp:1005
KDChart::AbstractCartesianDiagram::axes
virtual KDChart::CartesianAxisList axes() const
Definition: KDChartAbstractCartesianDiagram.cpp:103
KDChart::CartesianAxis::setCachedSizeDirty
void setCachedSizeDirty() const
Definition: KDChartCartesianAxis.cpp:953
Visited
@ Visited
Definition: KDChartChart.cpp:307
KDChart::TextArea::paintAll
void paintAll(QPainter &painter) override
Call paintAll, if you want the background and the frame to be drawn before the normal paint() is invo...
Definition: KDChartTextArea.cpp:80
QLayoutItem
Class only listed here to document inheritance of some KDChart classes.
KDChart::Measure::setRelativeMode
void setRelativeMode(const QObject *area, KDChartEnums::MeasureOrientation orientation)
The reference area must either be derived from AbstractArea or from QWidget, so it can also be derive...
Definition: KDChartMeasure.h:71
KDChartEnums::PositionNorthWest
@ PositionNorthWest
Definition: KDChartEnums.h:195
KDChartEnums::PositionSouthWest
@ PositionSouthWest
Definition: KDChartEnums.h:201
KDChartEnums::MeasureOrientationMinimum
@ MeasureOrientationMinimum
Definition: KDChartEnums.h:293
KDChartEnums::PositionWest
@ PositionWest
Definition: KDChartEnums.h:202
KDChart::RelativePosition::alignment
Qt::Alignment alignment() const
Definition: KDChartRelativePosition.cpp:127
KDChart::Chart::mouseMoveEvent
void mouseMoveEvent(QMouseEvent *event) override
reimp
Definition: KDChartChart.cpp:1720
KDChart::AbstractCoordinatePlane
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane,...
Definition: KDChartAbstractCoordinatePlane.h:45
KDChart::Chart::coordinatePlane
AbstractCoordinatePlane * coordinatePlane()
Each chart must have at least one coordinate plane.
Definition: KDChartChart.cpp:1208
KDChart::AbstractCoordinatePlane::layoutDiagrams
virtual void layoutDiagrams()=0
Distribute the available space among the diagrams and axes.
KDChart::Legend::position
Position position() const
Returns the position of a non-floating legend.
Definition: KDChartLegend.cpp:460
KDChart::AbstractLayoutItem::setParentLayout
void setParentLayout(QLayout *lay)
Definition: KDChartLayoutItems.h:73

Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/

https://www.kdab.com/development-resources/qt-tools/kd-chart/