24 #include "KDChartChart_p.h"
28 #include <QGridLayout>
33 #include <QPaintEvent>
34 #include <QLayoutItem>
35 #include <QPushButton>
36 #include <QApplication>
47 #include "KDChartPainterSaver_p.h"
53 #include "../evaldialog/evaldialog.h"
56 #include <KDABLibFakes>
66 static bool isZeroArea(
const QRect &r)
68 return !r.width() || !r.height();
71 static QString lineProlog(
int nestingDepth,
int lineno)
73 QString numbering(QString::number(lineno).rightJustified(5).append(QChar::fromAscii(
':')));
74 QString indent(nestingDepth * 4, QLatin1Char(
' '));
75 return numbering + indent;
78 static void dumpLayoutTreeRecurse(QLayout *l,
int *counter,
int depth)
80 const QLatin1String colorOn(isZeroArea(l->geometry()) ?
"\033[0m" :
"\033[32m");
81 const QLatin1String colorOff(
"\033[0m");
83 QString prolog = lineProlog(depth, *counter);
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()
92 for (
int i = 0; i < l->count(); i++) {
94 if (QLayout *childL = child->layout()) {
95 dumpLayoutTreeRecurse(childL, counter, depth + 1);
99 if (!isZeroArea(child->geometry())) {
100 prolog = lineProlog(depth + 1, *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()
113 static void dumpLayoutTree(QLayout *l)
116 dumpLayoutTreeRecurse(l, &counter, 0);
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 }
147 default: *row = -1; *column = -1;
156 class MyWidgetItem :
public QWidgetItem
159 explicit MyWidgetItem(
QWidget *w, Qt::Alignment alignment = 0)
162 setAlignment( alignment );
172 QSize sizeHint()
const override
174 QWidget* w =
const_cast< MyWidgetItem *
>( this )->widget();
175 return w->sizeHint();
178 QSize minimumSize()
const override
180 QWidget* w =
const_cast< MyWidgetItem *
>( this )->widget();
181 return w->minimumSize();
184 QSize maximumSize()
const override
186 QWidget* w =
const_cast< MyWidgetItem *
>( this )->widget();
187 return w->maximumSize();
190 Qt::Orientations expandingDirections()
const override
192 QWidget* w =
const_cast< MyWidgetItem *
>( this )->widget();
194 return Qt::Orientations(0);
196 Qt::Orientations e = w->sizePolicy().expandingDirections();
200 void setGeometry(
const QRect &g)
override
202 QWidget* w =
const_cast< MyWidgetItem *
>( this )->widget();
206 QRect geometry()
const override
208 QWidget* w =
const_cast< MyWidgetItem *
>( this )->widget();
209 return w->geometry();
212 bool hasHeightForWidth()
const override
214 QWidget* w =
const_cast< MyWidgetItem *
>( this )->widget();
215 bool ret = !isEmpty() &&
216 qobject_cast< Legend* >( w )->hasHeightForWidth();
220 int heightForWidth(
int width )
const override
222 QWidget* w =
const_cast< MyWidgetItem *
>( this )->widget();
223 int ret = w->heightForWidth( width );
227 bool isEmpty()
const override {
228 QWidget* w =
const_cast< MyWidgetItem *
>( this )->widget();
232 return w->isHidden() && w->testAttribute( Qt::WA_WState_ExplicitShowHide );
243 QLayout *layout = item->layout();
245 const int count = layout->count();
246 for (
int i = 0; i < count; i++ ) {
253 void Chart::Private::slotUnregisterDestroyedLegend(
Legend *l )
255 chart->takeLegend( l );
258 void Chart::Private::slotUnregisterDestroyedHeaderFooter(
HeaderFooter* hf )
260 chart->takeHeaderFooter( hf );
265 coordinatePlanes.removeAll( plane );
274 Chart::Private::Private(
Chart* chart_ )
276 , useNewLayoutSystem( false )
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 )
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;
303 Chart::Private::~Private()
308 struct ConnectedComponentsComparator{
309 bool operator()(
const LayoutGraphNode *lhs,
const LayoutGraphNode *rhs )
const
311 return lhs->priority < rhs->priority;
318 QHash< LayoutGraphNode*, VisitorState > visitedComponents;
319 Q_FOREACH ( LayoutGraphNode* node, nodeList )
320 visitedComponents[ node ] =
Unknown;
321 for (
int i = 0; i < nodeList.size(); ++i )
323 LayoutGraphNode *curNode = nodeList[ i ];
324 LayoutGraphNode *representativeNode = curNode;
325 if ( visitedComponents[ curNode ] !=
Visited )
327 QStack< LayoutGraphNode* > stack;
328 stack.push( curNode );
329 while ( !stack.isEmpty() )
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;
343 connectedComponents.append( representativeNode );
346 std::sort( connectedComponents.begin(), connectedComponents.end(), ConnectedComponentsComparator() );
347 return connectedComponents;
350 struct PriorityComparator{
352 PriorityComparator( QHash< AbstractCoordinatePlane*, LayoutGraphNode* > mapping )
353 : m_mapping( mapping )
357 const LayoutGraphNode *lhsNode = m_mapping[ lhs ];
359 const LayoutGraphNode *rhsNode = m_mapping[ rhs ];
361 return lhsNode->priority < rhsNode->priority;
364 const QHash< AbstractCoordinatePlane*, LayoutGraphNode* > m_mapping;
369 if ( node && node->diagramPlane && node->diagramPlane->diagram() )
378 case( CartesianAxis::Top ):
379 node->topAxesLayout =
true;
381 case( CartesianAxis::Bottom ):
382 node->bottomAxesLayout =
true;
384 case( CartesianAxis::Left ):
385 node->leftAxesLayout =
true;
387 case( CartesianAxis::Right ):
388 node->rightAxesLayout =
true;
398 lhs->topAxesLayout |= rhs->topAxesLayout;
399 rhs->topAxesLayout = lhs->topAxesLayout;
401 lhs->bottomAxesLayout |= rhs->bottomAxesLayout;
402 rhs->bottomAxesLayout = lhs->bottomAxesLayout;
404 lhs->leftAxesLayout |= rhs->leftAxesLayout;
405 rhs->leftAxesLayout = lhs->leftAxesLayout;
407 lhs->rightAxesLayout |= rhs->rightAxesLayout;
408 rhs->rightAxesLayout = lhs->rightAxesLayout;
413 Chart::Private::AxisType type,
416 if ( !plane || !plane->
diagram() )
427 if ( ( type == Chart::Private::Ordinate &&
428 ( axis->
position() == CartesianAxis::Left || axis->
position() == CartesianAxis::Right ) )
430 ( type == Chart::Private::Abscissa &&
431 ( axis->
position() == CartesianAxis::Top || axis->
position() == CartesianAxis::Bottom ) ) ) {
438 qobject_cast< AbstractCartesianDiagram* > ( curPlane->
diagram() );
445 if ( curSearchedAxis == curAxis )
447 result.append( curPlane );
448 if ( !sharedAxes->contains( curSearchedAxis ) )
449 sharedAxes->append( curSearchedAxis );
466 QHash< AbstractCoordinatePlane*, LayoutGraphNode* > planeNodeMapping;
473 allNodes.append(
new LayoutGraphNode );
474 allNodes[ allNodes.size() - 1 ]->diagramPlane = curPlane;
475 allNodes[ allNodes.size() - 1 ]->priority = allNodes.size();
477 planeNodeMapping[ curPlane ] = allNodes[ allNodes.size() - 1 ];
481 Q_FOREACH( LayoutGraphNode* curNode, allNodes )
485 Q_ASSERT( sharedAxes.size() < 2 );
487 if ( sharedAxes.size() == 1 && xSharedPlanes.size() > 1 )
491 for (
int i = 0; i < xSharedPlanes.size() - 1; ++i )
493 LayoutGraphNode *tmpNode = planeNodeMapping[ xSharedPlanes[ i ] ];
495 LayoutGraphNode *tmpNode2 = planeNodeMapping[ xSharedPlanes[ i + 1 ] ];
496 Q_ASSERT( tmpNode2 );
497 tmpNode->bottomSuccesor = tmpNode2;
509 LayoutGraphNode axisInfoNode;
510 for (
int count = 0; count < 2; ++count )
512 for (
int i = 0; i < xSharedPlanes.size(); ++i )
520 Q_ASSERT( sharedAxes.size() < 2 );
521 if ( sharedAxes.size() == 1 && ySharedPlanes.size() > 1 )
525 for (
int i = 0; i < ySharedPlanes.size() - 1; ++i )
527 LayoutGraphNode *tmpNode = planeNodeMapping[ ySharedPlanes[ i ] ];
529 LayoutGraphNode *tmpNode2 = planeNodeMapping[ ySharedPlanes[ i + 1 ] ];
530 Q_ASSERT( tmpNode2 );
531 tmpNode->leftSuccesor = tmpNode2;
543 LayoutGraphNode axisInfoNode;
544 for (
int count = 0; count < 2; ++count )
546 for (
int i = 0; i < ySharedPlanes.size(); ++i )
553 if ( curNode->diagramPlane->referenceCoordinatePlane() )
554 curNode->sharedSuccesor = planeNodeMapping[ curNode->diagramPlane->referenceCoordinatePlane() ];
560 QHash<AbstractCoordinatePlane*, PlaneInfo> Chart::Private::buildPlaneLayoutInfos()
574 QHash<CartesianAxis*, AxisInfo> axisInfos;
575 QHash<AbstractCoordinatePlane*, PlaneInfo> planeInfos;
580 planeInfos.insert( plane, p );
584 qobject_cast<AbstractCartesianDiagram*> ( abstractDiagram );
590 if ( !axisInfos.contains( axis ) ) {
597 axisInfos.insert( axis, i );
599 AxisInfo i = axisInfos[axis];
600 if ( i.plane == plane ) {
607 PlaneInfo pi = planeInfos[plane];
609 if ( !pi.referencePlane ) {
611 pi.referencePlane = i.plane;
612 if ( axis->
position() == CartesianAxis::Left ||
613 axis->
position() == CartesianAxis::Right ) {
614 pi.horizontalOffset += 1;
616 planeInfos[plane] = pi;
618 pi = planeInfos[i.plane];
619 if ( axis->
position() == CartesianAxis::Top ||
620 axis->
position() == CartesianAxis::Bottom ) {
621 pi.verticalOffset += 1;
624 planeInfos[i.plane] = pi;
630 p = planeInfos[plane];
631 if ( p.referencePlane == 0 ) {
632 p.gridLayout =
new QGridLayout();
633 p.gridLayout->setMargin( 0 );
634 planeInfos[plane] = p;
640 void Chart::Private::slotLayoutPlanes()
643 const QBoxLayout::Direction oldPlanesDirection = planesLayout ? planesLayout->direction()
644 : QBoxLayout::TopToBottom;
645 if ( planesLayout && dataAndLegendLayout )
646 dataAndLegendLayout->removeItem( planesLayout );
648 const bool hadPlanesLayout = planesLayout != 0;
649 int left, top, right, bottom;
650 if ( hadPlanesLayout )
651 planesLayout->getContentsMargins(&left, &top, &right, &bottom);
662 planeLayoutItems.clear();
666 planesLayout =
new QBoxLayout( oldPlanesDirection );
668 isPlanesLayoutDirty =
true;
670 if ( useNewLayoutSystem )
672 gridPlaneLayout =
new QGridLayout;
673 planesLayout->addLayout( gridPlaneLayout );
676 planesLayout->setContentsMargins(left, top, right, bottom);
677 planesLayout->setObjectName( QString::fromLatin1(
"planesLayout" ) );
689 QSet< CartesianAxis* > laidOutAxes;
690 for (
int i = 0; i < connectedComponents.size(); ++i )
692 LayoutGraphNode *curComponent = connectedComponents[ i ];
693 for ( LayoutGraphNode *curRowComponent = curComponent; curRowComponent; curRowComponent = curRowComponent->bottomSuccesor )
696 for ( LayoutGraphNode *curColComponent = curRowComponent; curColComponent; curColComponent = curColComponent->leftSuccesor )
698 Q_ASSERT( curColComponent->diagramPlane->diagrams().size() == 1 );
699 Q_FOREACH(
AbstractDiagram* diagram, curColComponent->diagramPlane->diagrams() )
701 const int planeRowOffset = 1;
702 const int planeColOffset = 1;
706 planeLayoutItems << curColComponent->diagramPlane;
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 )
718 gridPlaneLayout->addItem( curColComponent->sharedSuccesor->diagramPlane, row + planeRowOffset, col + planeColOffset, 2, 2 );
719 curColComponent->sharedSuccesor->diagramPlane->setParentLayout( gridPlaneLayout );
720 planeLayoutItems << curColComponent->sharedSuccesor->diagramPlane;
726 if ( curColComponent->bottomSuccesor )
729 if ( laidOutAxes.contains( axis ) )
735 case( CartesianAxis::Top ):
737 topLayout =
new QVBoxLayout;
738 topLayout->addItem( axis );
741 case( CartesianAxis::Bottom ):
743 bottomLayout =
new QVBoxLayout;
744 bottomLayout->addItem( axis );
747 case( CartesianAxis::Left ):
749 leftLayout =
new QHBoxLayout;
750 leftLayout->addItem( axis );
753 case( CartesianAxis::Right ):
756 rightLayout =
new QHBoxLayout;
758 rightLayout->addItem( axis );
762 planeLayoutItems << axis;
763 laidOutAxes.insert( axis );
766 gridPlaneLayout->addLayout( leftLayout, row + planeRowOffset, col, 2, 1,
767 Qt::AlignRight | Qt::AlignVCenter );
769 gridPlaneLayout->addLayout( rightLayout, row, col + planeColOffset + 2, 2, 1,
770 Qt::AlignLeft | Qt::AlignVCenter );
772 gridPlaneLayout->addLayout( topLayout, row, col + planeColOffset, 1, 2,
773 Qt::AlignBottom | Qt::AlignHCenter );
775 gridPlaneLayout->addLayout( bottomLayout, row + planeRowOffset + 2,
776 col + planeColOffset, 1, 2, Qt::AlignTop | Qt::AlignHCenter );
780 gridPlaneLayout->addItem( curColComponent->diagramPlane, row, col, 4, 4 );
783 col += planeColOffset + 2 + ( 1 );
788 const int rowOffset = axisOffset + 2;
800 if ( dataAndLegendLayout ) {
801 dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
802 dataAndLegendLayout->setRowStretch( 1, 1000 );
803 dataAndLegendLayout->setColumnStretch( 1, 1000 );
806 #ifdef NEW_LAYOUT_DEBUG
807 for (
int i = 0; i < gridPlaneLayout->rowCount(); ++i )
809 for (
int j = 0; j < gridPlaneLayout->columnCount(); ++j )
811 if ( gridPlaneLayout->itemAtPosition( i, j ) )
812 qDebug() << Q_FUNC_INFO <<
"item at" << i << j << gridPlaneLayout->itemAtPosition( i, j )->geometry();
814 qDebug() << Q_FUNC_INFO <<
"item at" << i << j <<
"no item present";
820 if ( hadPlanesLayout ) {
821 planesLayout->setContentsMargins( left, top, right, bottom );
824 planesLayout->setMargin( 0 );
825 planesLayout->setSpacing( 0 );
826 planesLayout->setObjectName( QString::fromLatin1(
"planesLayout" ) );
831 QHash<AbstractCoordinatePlane*, PlaneInfo> planeInfos = buildPlaneLayoutInfos();
832 QHash<AbstractAxis*, AxisInfo> axisInfos;
834 Q_ASSERT( planeInfos.contains(plane) );
835 PlaneInfo& pi = planeInfos[ plane ];
836 const int column = pi.horizontalOffset;
837 const int row = pi.verticalOffset;
839 QGridLayout *planeLayout = pi.gridLayout;
841 if ( !planeLayout ) {
842 PlaneInfo& refPi = pi;
845 while ( !planeLayout && refPi.referencePlane ) {
846 refPi = planeInfos[refPi.referencePlane];
847 planeLayout = refPi.gridLayout;
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." );
853 planesLayout->addLayout( planeLayout );
859 planeLayoutItems << plane;
861 planeLayout->addItem( plane, row, column, 1, 1, 0 );
863 planeLayout->setRowStretch( row, 2 );
864 planeLayout->setColumnStretch( column, 2 );
868 qobject_cast< AbstractCartesianDiagram* >( abstractDiagram );
873 if ( pi.referencePlane != 0 )
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;
882 if ( pi.topAxesLayout == 0 )
884 pi.topAxesLayout =
new QVBoxLayout;
885 pi.topAxesLayout->setMargin( 0 );
886 pi.topAxesLayout->setObjectName( QString::fromLatin1(
"topAxesLayout" ) );
888 if ( pi.bottomAxesLayout == 0 )
890 pi.bottomAxesLayout =
new QVBoxLayout;
891 pi.bottomAxesLayout->setMargin( 0 );
892 pi.bottomAxesLayout->setObjectName( QString::fromLatin1(
"bottomAxesLayout" ) );
894 if ( pi.leftAxesLayout == 0 )
896 pi.leftAxesLayout =
new QHBoxLayout;
897 pi.leftAxesLayout->setMargin( 0 );
898 pi.leftAxesLayout->setObjectName( QString::fromLatin1(
"leftAxesLayout" ) );
900 if ( pi.rightAxesLayout == 0 )
902 pi.rightAxesLayout =
new QHBoxLayout;
903 pi.rightAxesLayout->setMargin( 0 );
904 pi.rightAxesLayout->setObjectName( QString::fromLatin1(
"rightAxesLayout" ) );
907 if ( pi.referencePlane != 0 )
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;
917 if ( axisInfos.contains( axis ) ) {
923 planeLayoutItems << axis;
925 switch ( axis->position() ) {
926 case CartesianAxis::Top:
928 pi.topAxesLayout->addItem( axis );
930 case CartesianAxis::Bottom:
931 axis->setParentLayout( pi.bottomAxesLayout );
932 pi.bottomAxesLayout->addItem( axis );
934 case CartesianAxis::Left:
935 axis->setParentLayout( pi.leftAxesLayout );
936 pi.leftAxesLayout->addItem( axis );
938 case CartesianAxis::Right:
939 axis->setParentLayout( pi.rightAxesLayout );
940 pi.rightAxesLayout->addItem( axis );
943 Q_ASSERT_X(
false,
"Chart::paintEvent",
"unknown axis position" );
946 axisInfos.insert( axis, AxisInfo() );
953 if ( !pi.topAxesLayout->parent() ) {
954 planeLayout->addLayout( pi.topAxesLayout, row - 1, column );
956 if ( !pi.bottomAxesLayout->parent() ) {
957 planeLayout->addLayout( pi.bottomAxesLayout, row + 1, column );
959 if ( !pi.leftAxesLayout->parent() ) {
960 planeLayout->addLayout( pi.leftAxesLayout, row, column - 1 );
962 if ( !pi.rightAxesLayout->parent() ) {
963 planeLayout->addLayout( pi.rightAxesLayout,row, column + 1 );
968 #define ADD_AUTO_SPACER_IF_NEEDED( \
969 spacerRow, spacerColumn, hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ) \
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; \
980 if ( plane->isCornerSpacersEnabled() ) {
988 if ( dataAndLegendLayout ) {
989 dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
990 dataAndLegendLayout->setRowStretch( 1, 1000 );
991 dataAndLegendLayout->setColumnStretch( 1, 1000 );
998 void Chart::Private::createLayouts()
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();
1009 vLayout =
new QVBoxLayout();
1010 vLayout->setMargin( 0 );
1011 vLayout->setObjectName( QString::fromLatin1(
"vLayout" ) );
1013 layout->addLayout( vLayout, 1000 );
1014 layout->addSpacing( globalLeadingRight );
1015 rightOuterSpacer = layout->itemAt( layout->count() - 1 )->spacerItem();
1018 vLayout->addSpacing( globalLeadingTop );
1019 topOuterSpacer = vLayout->itemAt( vLayout->count() - 1 )->spacerItem();
1021 headerLayout =
new QGridLayout();
1022 headerLayout->setMargin( 0 );
1023 vLayout->addLayout( headerLayout );
1025 dataAndLegendLayout =
new QGridLayout();
1026 dataAndLegendLayout->setMargin( 0 );
1027 dataAndLegendLayout->setObjectName( QString::fromLatin1(
"dataAndLegendLayout" ) );
1028 vLayout->addLayout( dataAndLegendLayout, 1000 );
1030 footerLayout =
new QGridLayout();
1031 footerLayout->setMargin( 0 );
1032 footerLayout->setObjectName( QString::fromLatin1(
"footerLayout" ) );
1033 vLayout->addLayout( footerLayout );
1039 for (
int row = 0; row < 3; ++row ) {
1040 for (
int column = 0; column < 3; ++ 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;
1048 QGridLayout* outerLayout = headOrFoot == 0 ? headerLayout : footerLayout;
1049 outerLayout->addLayout( innerLayout, row, column, align );
1055 vLayout->addSpacing( globalLeadingBottom );
1056 bottomOuterSpacer = vLayout->itemAt( vLayout->count() - 1 )->spacerItem();
1059 dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
1060 dataAndLegendLayout->setRowStretch( 1, 1 );
1061 dataAndLegendLayout->setColumnStretch( 1, 1 );
1064 void Chart::Private::slotResizePlanes()
1066 if ( !dataAndLegendLayout ) {
1069 if ( !overrideSize.isValid() ) {
1082 void Chart::Private::updateDirtyLayouts()
1084 if ( isPlanesLayoutDirty ) {
1091 if ( isPlanesLayoutDirty || isFloatingLegendsLayoutDirty ) {
1092 chart->reLayoutFloatingLegends();
1094 isPlanesLayoutDirty =
false;
1095 isFloatingLegendsLayoutDirty =
false;
1098 void Chart::Private::reapplyInternalLayouts()
1100 QRect geo = layout->geometry();
1103 layout->setGeometry( geo );
1107 void Chart::Private::paintAll( QPainter* painter )
1109 updateDirtyLayouts();
1111 QRect rect( QPoint( 0, 0 ), overrideSize.isValid() ? overrideSize : chart->size() );
1116 AbstractAreaBase::paintBackgroundAttributes( *painter, rect, backgroundAttributes );
1118 AbstractAreaBase::paintFrameAttributes( *painter, rect, frameAttributes );
1120 chart->reLayoutFloatingLegends();
1123 planeLayoutItem->
paintAll( *painter );
1125 KDAB_FOREACH(
TextArea* textLayoutItem, textLayoutItems ) {
1126 textLayoutItem->
paintAll( *painter );
1128 KDAB_FOREACH(
Legend *legend, legends ) {
1129 const bool hidden = legend->isHidden() && legend->testAttribute( Qt::WA_WState_ExplicitShowHide );
1143 , _d( new Private( this ) )
1145 #if defined KDAB_EVAL
1146 EvalDialog::checkEvalLicense(
"KD Chart" );
1152 frameAttrs.
setPen( QPen( Qt::black ) );
1168 d->frameAttributes = a;
1173 return d->frameAttributes;
1178 d->backgroundAttributes = a;
1183 return d->backgroundAttributes;
1189 if (layout ==
d->planesLayout)
1191 if (
d->planesLayout) {
1194 for(
int i =
d->planesLayout->count() - 1; i >= 0; --i) {
1195 d->planesLayout->takeAt(i);
1197 delete d->planesLayout;
1199 d->planesLayout = qobject_cast<QBoxLayout*>( layout );
1200 d->slotLayoutPlanes();
1205 return d->planesLayout;
1210 if (
d->coordinatePlanes.isEmpty() ) {
1211 qWarning() <<
"Chart::coordinatePlane: warning: no coordinate plane defined.";
1214 return d->coordinatePlanes.first();
1220 return d->coordinatePlanes;
1231 if ( index < 0 || index >
d->coordinatePlanes.count() ) {
1237 connect( plane, SIGNAL( needUpdate() ),
this, SLOT( update() ) );
1238 connect( plane, SIGNAL( needRelayout() ),
d, SLOT( slotResizePlanes() ) ) ;
1239 connect( plane, SIGNAL( needLayoutPlanes() ),
d, SLOT( slotLayoutPlanes() ) ) ;
1241 d->coordinatePlanes.insert( index, plane );
1243 d->slotLayoutPlanes();
1249 if ( plane && oldPlane_ != plane ) {
1251 if (
d->coordinatePlanes.count() ) {
1253 oldPlane =
d->coordinatePlanes.first();
1254 if ( oldPlane == plane )
1266 const int idx =
d->coordinatePlanes.indexOf( plane );
1268 d->coordinatePlanes.takeAt( idx );
1269 disconnect( plane, 0,
d, 0 );
1270 disconnect( plane, 0,
this, 0 );
1273 d->mouseClickedPlanes.removeAll(plane);
1275 d->slotLayoutPlanes();
1291 d->globalLeadingLeft = leading;
1292 d->leftOuterSpacer->changeSize( leading, 0, QSizePolicy::Fixed, QSizePolicy::Minimum );
1293 d->reapplyInternalLayouts();
1298 return d->globalLeadingLeft;
1303 d->globalLeadingTop = leading;
1304 d->topOuterSpacer->changeSize( 0, leading, QSizePolicy::Minimum, QSizePolicy::Fixed );
1305 d->reapplyInternalLayouts();
1310 return d->globalLeadingTop;
1315 d->globalLeadingRight = leading;
1316 d->rightOuterSpacer->changeSize( leading, 0, QSizePolicy::Fixed, QSizePolicy::Minimum );
1317 d->reapplyInternalLayouts();
1322 return d->globalLeadingRight;
1327 d->globalLeadingBottom = leading;
1328 d->bottomOuterSpacer->changeSize( 0, leading, QSizePolicy::Minimum, QSizePolicy::Fixed );
1329 d->reapplyInternalLayouts();
1334 return d->globalLeadingBottom;
1339 if ( target.isEmpty() || !painter ) {
1347 if (
dynamic_cast< QWidget*
>( painter->device() ) != 0 ) {
1349 qreal( target.height() ) / qreal( geometry().size().height() ) );
1354 const qreal resX = qreal( logicalDpiX() ) / qreal( painter->device()->logicalDpiX() );
1355 const qreal resY = qreal( logicalDpiY() ) / qreal( painter->device()->logicalDpiY() );
1358 qreal( target.height() ) / qreal( geometry().size().height() ) * resY );
1361 const QPoint translation = target.topLeft();
1362 painter->translate( translation );
1367 const bool differentSize = target.size() != size();
1369 if ( differentSize ) {
1370 oldGeometry = geometry();
1371 d->isPlanesLayoutDirty =
true;
1372 d->isFloatingLegendsLayoutDirty =
true;
1374 d->dataAndLegendLayout->setGeometry( QRect( QPoint(), target.size() ) );
1377 d->overrideSize = target.size();
1378 d->paintAll( painter );
1379 d->overrideSize = QSize();
1381 if ( differentSize ) {
1383 d->dataAndLegendLayout->setGeometry( oldGeometry );
1384 d->isPlanesLayoutDirty =
true;
1385 d->isFloatingLegendsLayoutDirty =
true;
1392 painter->translate( -translation.x(), -translation.y() );
1401 d->isPlanesLayoutDirty =
true;
1402 d->isFloatingLegendsLayoutDirty =
true;
1403 QWidget::resizeEvent(
event );
1409 const bool hidden =
legend->isHidden() &&
legend->testAttribute( Qt::WA_WState_ExplicitShowHide );
1413 legend->setGeometry( QRect(
legend->geometry().topLeft(), legendSize ) );
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();
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();
1432 legend->move(
static_cast<int>(pt.x()),
static_cast<int>(pt.y()) );
1440 QPainter painter(
this );
1441 d->paintAll( &painter );
1452 qWarning(
"Unknown header/footer position" );
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* ) ) );
1475 QVBoxLayout* headerFooterLayout =
d->innerHdFtLayouts[ innerLayoutIdx ][ row ][ column ];
1479 headerFooterLayout->addItem( hf );
1481 d->slotResizePlanes();
1489 if (
d->headerFooters.count() ) {
1490 if ( ! oldHeaderFooter ) {
1491 oldHeaderFooter =
d->headerFooters.first();
1497 delete oldHeaderFooter;
1509 d, SLOT( slotUnregisterDestroyedHeaderFooter(
HeaderFooter* ) ) );
1511 d->headerFooters.takeAt( idx );
1514 d->textLayoutItems.remove(
d->textLayoutItems.indexOf(
headerFooter ) );
1516 d->slotResizePlanes();
1519 void Chart::Private::slotHeaderFooterPositionChanged(
HeaderFooter* hf )
1521 chart->takeHeaderFooter( hf );
1522 chart->addHeaderFooter( hf );
1527 if (
d->headerFooters.isEmpty() ) {
1530 return d->headerFooters.first();
1536 return d->headerFooters;
1541 Legend* legend = qobject_cast< Legend* >( aw );
1543 chart->takeLegend( legend );
1544 chart->addLegendInternal( legend,
false );
1550 addLegendInternal(
legend,
true );
1554 void Chart::addLegendInternal(
Legend* legend,
bool setMeasures )
1562 qWarning(
"Not showing legend because PositionCenter is not supported for legends." );
1569 qWarning(
"Not showing legend because of unknown legend position." );
1574 legend->setParent(
this );
1578 if ( setMeasures ) {
1580 Measure measure( textAttrs.fontSize() );
1582 measure.setValue( 20 );
1583 textAttrs.setFontSize( measure );
1588 measure.setValue( 24 );
1603 QLayoutItem* edgeItem =
d->dataAndLegendLayout->itemAtPosition( row, column );
1604 QGridLayout* alignmentsLayout =
dynamic_cast< QGridLayout*
>( edgeItem );
1605 Q_ASSERT( !edgeItem || alignmentsLayout );
1606 if ( !alignmentsLayout ) {
1607 alignmentsLayout =
new QGridLayout;
1608 d->dataAndLegendLayout->addLayout( alignmentsLayout, row, column );
1609 alignmentsLayout->setMargin( 0 );
1617 for (
int i = 0; i < 3; i++ ) {
1618 for (
int j = 0; j < 3; j++ ) {
1628 QLayoutItem* alignmentItem = alignmentsLayout->itemAtPosition( row, column );
1629 QVBoxLayout* sameAlignmentLayout =
dynamic_cast< QVBoxLayout*
>( alignmentItem );
1630 Q_ASSERT( !alignmentItem || sameAlignmentLayout );
1631 if ( !sameAlignmentLayout ) {
1632 sameAlignmentLayout =
new QVBoxLayout;
1633 alignmentsLayout->addLayout( sameAlignmentLayout, row, column );
1634 sameAlignmentLayout->setMargin( 0 );
1641 d, SLOT( slotUnregisterDestroyedLegend(
Legend* ) ) );
1646 d->slotResizePlanes();
1652 Legend* oldLegend = oldLegend_;
1653 if (
d->legends.count() ) {
1654 if ( ! oldLegend ) {
1655 oldLegend =
d->legends.first();
1656 if ( oldLegend ==
legend )
1668 const int idx =
d->legends.indexOf(
legend );
1673 d->legends.takeAt( idx );
1674 disconnect(
legend, 0,
d, 0 );
1675 disconnect(
legend, 0,
this, 0 );
1679 d->slotResizePlanes();
1685 return d->legends.isEmpty() ? 0 :
d->legends.first();
1695 const QPoint pos = mapFromGlobal(
event->globalPos() );
1699 QMouseEvent ev( QEvent::MouseButtonPress, pos,
event->globalPos(),
1702 d->mouseClickedPlanes.append( plane );
1709 const QPoint pos = mapFromGlobal(
event->globalPos() );
1713 QMouseEvent ev( QEvent::MouseButtonPress, pos,
event->globalPos(),
1722 QSet< AbstractCoordinatePlane* > eventReceivers = QSet< AbstractCoordinatePlane* >::fromList(
d->mouseClickedPlanes );
1726 eventReceivers.insert( plane );
1730 const QPoint pos = mapFromGlobal(
event->globalPos() );
1733 QMouseEvent ev( QEvent::MouseMove, pos,
event->globalPos(),
1741 QSet< AbstractCoordinatePlane* > eventReceivers = QSet< AbstractCoordinatePlane* >::fromList(
d->mouseClickedPlanes );
1745 eventReceivers.insert( plane );
1749 const QPoint pos = mapFromGlobal(
event->globalPos() );
1752 QMouseEvent ev( QEvent::MouseButtonRelease, pos,
event->globalPos(),
1757 d->mouseClickedPlanes.clear();
1762 if (
event->type() == QEvent::ToolTip ) {
1763 const QHelpEvent*
const helpEvent =
static_cast< QHelpEvent*
>(
event );
1764 for (
int stage = 0; stage < 2; ++stage) {
1771 index = diagram->
indexAt(helpEvent->pos());
1775 const QModelIndexList indexes = diagram->
indexesIn(QRect( helpEvent->pos() - QPoint(15,15), QSize(30,30)));
1776 index = indexes.isEmpty() ? QModelIndex() : indexes.front();
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 );
1790 return QWidget::event(
event );
1795 return d_func()->useNewLayoutSystem;
1800 d_func()->useNewLayoutSystem = value;