9#include "KChartChart.h"
10#include "KChartChart_p.h"
22#include <QApplication>
25#include "KChartCartesianCoordinatePlane.h"
26#include "KChartAbstractCartesianDiagram.h"
27#include "KChartHeaderFooter.h"
29#include "KChartLegend.h"
30#include "KChartLayoutItems.h"
31#include <KChartTextAttributes.h>
32#include <KChartMarkerAttributes.h>
33#include "KChartPainterSaver_p.h"
34#include "KChartPrintingParameters.h"
39#include "../evaldialog/evaldialog.h"
50static bool isZeroArea(
const QRect &r)
55static QString lineProlog(
int nestingDepth,
int lineno)
59 return numbering + indent;
62static void dumpLayoutTreeRecurse(
QLayout *l,
int *counter,
int depth)
67 QString prolog = lineProlog(depth, *counter);
76 for (
int i = 0; i < l->
count(); i++) {
79 dumpLayoutTreeRecurse(childL, counter, depth + 1);
83 if (!isZeroArea(child->
geometry())) {
84 prolog = lineProlog(depth + 1, *counter);
86 qDebug() << colorOn + prolog <<
typeid(*child).name() << child->
geometry()
97static void dumpLayoutTree(
QLayout *l)
100 dumpLayoutTreeRecurse(l, &counter, 0);
113 case KChartEnums::PositionNorthWest: *row = 0; *column = 0;
115 case KChartEnums::PositionNorth: *row = 0; *column = 1;
117 case KChartEnums::PositionNorthEast: *row = 0; *column = 2;
119 case KChartEnums::PositionEast: *row = 1; *column = 2;
121 case KChartEnums::PositionSouthEast: *row = 2; *column = 2;
123 case KChartEnums::PositionSouth: *row = 2; *column = 1;
125 case KChartEnums::PositionSouthWest: *row = 2; *column = 0;
127 case KChartEnums::PositionWest: *row = 1; *column = 0;
129 case KChartEnums::PositionCenter: *row = 1; *column = 1;
131 default: *row = -1; *column = -1;
156 QSize sizeHint()
const override
162 QSize minimumSize()
const override
168 QSize maximumSize()
const override
199 void setGeometry(
const QRect &g)
override
205 QRect geometry()
const override
211 bool hasHeightForWidth()
const override
214 bool ret = !isEmpty() &&
215 qobject_cast< Legend* >( w )->hasHeightForWidth();
219 int heightForWidth(
int width )
const override
226 bool isEmpty()
const override {
240static void invalidateLayoutTree(
QLayoutItem *item )
244 const int count = layout->
count();
245 for (
int i = 0; i < count; i++ ) {
246 invalidateLayoutTree( layout->
itemAt( i ) );
252void Chart::Private::slotUnregisterDestroyedLegend(
Legend *l )
254 chart->takeLegend( l );
257void Chart::Private::slotUnregisterDestroyedHeaderFooter(
HeaderFooter* hf )
259 chart->takeHeaderFooter( hf );
264 coordinatePlanes.removeAll( plane );
266 if ( p->referenceCoordinatePlane() == plane) {
267 p->setReferenceCoordinatePlane(
nullptr );
273Chart::Private::Private(
Chart* chart_ )
275 , useNewLayoutSystem( false )
278 , planesLayout(nullptr)
279 , headerLayout(nullptr)
280 , footerLayout(nullptr)
281 , dataAndLegendLayout(nullptr)
282 , leftOuterSpacer(nullptr)
283 , rightOuterSpacer(nullptr)
284 , topOuterSpacer(nullptr)
285 , bottomOuterSpacer(nullptr)
286 , isFloatingLegendsLayoutDirty( true )
287 , isPlanesLayoutDirty( true )
288 , globalLeadingLeft(0)
289 , globalLeadingRight(0)
290 , globalLeadingTop(0)
291 , globalLeadingBottom(0)
293 for (
int row = 0; row < 3; ++row ) {
294 for (
int column = 0; column < 3; ++column ) {
295 for (
int i = 0; i < 2; i++ ) {
296 innerHdFtLayouts[ i ][ row ][ column ] =
nullptr;
302Chart::Private::~Private()
306enum VisitorState{ Visited,
Unknown };
307struct ConnectedComponentsComparator{
308 bool operator()(
const LayoutGraphNode *lhs,
const LayoutGraphNode *rhs )
const
310 return lhs->priority < rhs->priority;
318 for ( LayoutGraphNode* node : nodeList )
319 visitedComponents[ node ] =
Unknown;
320 for (
int i = 0; i < nodeList.size(); ++i )
322 LayoutGraphNode *curNode = nodeList[ i ];
323 LayoutGraphNode *representativeNode = curNode;
324 if ( visitedComponents[ curNode ] != Visited )
327 stack.
push( curNode );
330 curNode = stack.
pop();
331 Q_ASSERT( visitedComponents[ curNode ] != Visited );
332 visitedComponents[ curNode ] = Visited;
333 if ( curNode->bottomSuccesor && visitedComponents[ curNode->bottomSuccesor ] != Visited )
334 stack.
push( curNode->bottomSuccesor );
335 if ( curNode->leftSuccesor && visitedComponents[ curNode->leftSuccesor ] != Visited )
336 stack.
push( curNode->leftSuccesor );
337 if ( curNode->sharedSuccesor && visitedComponents[ curNode->sharedSuccesor ] != Visited )
338 stack.
push( curNode->sharedSuccesor );
339 if ( curNode->priority < representativeNode->priority )
340 representativeNode = curNode;
342 connectedComponents.
append( representativeNode );
345 std::sort( connectedComponents.
begin(), connectedComponents.
end(), ConnectedComponentsComparator() );
346 return connectedComponents;
349struct PriorityComparator{
352 : m_mapping( mapping )
356 const LayoutGraphNode *lhsNode = m_mapping[ lhs ];
358 const LayoutGraphNode *rhsNode = m_mapping[ rhs ];
360 return lhsNode->priority < rhsNode->priority;
366void checkExistingAxes( LayoutGraphNode* node )
368 if ( node && node->diagramPlane && node->diagramPlane->diagram() )
376 switch ( axis->position() )
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;
396static void mergeNodeAxisInformation( LayoutGraphNode* lhs, LayoutGraphNode* rhs )
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 );
471 if ( curPlane->diagram() )
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 ];
481 for ( LayoutGraphNode* curNode : std::as_const(allNodes) )
484 CoordinatePlaneList xSharedPlanes = findSharingAxisDiagrams( curNode->diagramPlane, coordinatePlanes, Abscissa, &sharedAxes );
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 )
514 mergeNodeAxisInformation( &axisInfoNode, planeNodeMapping[ xSharedPlanes[ i ] ] );
519 CoordinatePlaneList ySharedPlanes = findSharingAxisDiagrams( curNode->diagramPlane, coordinatePlanes, Ordinate, &sharedAxes );
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 )
548 mergeNodeAxisInformation( &axisInfoNode, planeNodeMapping[ ySharedPlanes[ i ] ] );
553 if ( curNode->diagramPlane->referenceCoordinatePlane() )
554 curNode->sharedSuccesor = planeNodeMapping[ curNode->diagramPlane->referenceCoordinatePlane() ];
580 planeInfos.
insert( plane, p );
583 const auto diagrams = plane->
diagrams();
586 qobject_cast<AbstractCartesianDiagram*> ( abstractDiagram );
593 if ( !axisInfos.
contains( axis ) ) {
600 axisInfos.
insert( axis, i );
602 AxisInfo i = axisInfos[axis];
603 if ( i.plane == plane ) {
610 PlaneInfo pi = planeInfos[plane];
612 if ( !pi.referencePlane ) {
614 pi.referencePlane = i.plane;
615 if ( axis->position() == CartesianAxis::Left ||
616 axis->position() == CartesianAxis::Right ) {
617 pi.horizontalOffset += 1;
619 planeInfos[plane] = pi;
621 pi = planeInfos[i.plane];
622 if ( axis->position() == CartesianAxis::Top ||
623 axis->position() == CartesianAxis::Bottom ) {
624 pi.verticalOffset += 1;
627 planeInfos[i.plane] = pi;
633 p = planeInfos[plane];
634 if ( p.referencePlane ==
nullptr ) {
636 p.gridLayout->setContentsMargins( 0, 0, 0, 0 );
637 planeInfos[plane] = p;
643void Chart::Private::slotLayoutPlanes()
648 if ( planesLayout && dataAndLegendLayout )
649 dataAndLegendLayout->removeItem( planesLayout );
651 const bool hadPlanesLayout = planesLayout !=
nullptr;
653 if ( hadPlanesLayout )
654 planesLayout->getContentsMargins(&left, &top, &right, &bottom);
657 plane->removeFromParentLayout();
665 planeLayoutItems.clear();
669 planesLayout =
new QBoxLayout( oldPlanesDirection );
671 isPlanesLayoutDirty =
true;
673 if ( useNewLayoutSystem )
676 planesLayout->
addLayout( gridPlaneLayout );
679 planesLayout->setContentsMargins(left, top, right, bottom);
693 for (
int i = 0; i < connectedComponents.
size(); ++i )
695 LayoutGraphNode *curComponent = connectedComponents[ i ];
696 for ( LayoutGraphNode *curRowComponent = curComponent; curRowComponent; curRowComponent = curRowComponent->bottomSuccesor )
699 for ( LayoutGraphNode *curColComponent = curRowComponent; curColComponent; curColComponent = curColComponent->leftSuccesor )
701 Q_ASSERT( curColComponent->diagramPlane->diagrams().size() == 1 );
702 const auto diags{curColComponent->diagramPlane->diagrams()};
705 const int planeRowOffset = 1;
706 const int planeColOffset = 1;
710 planeLayoutItems << curColComponent->diagramPlane;
714 gridPlaneLayout->addItem( curColComponent->diagramPlane, row + planeRowOffset, col + planeColOffset, 2, 2 );
715 curColComponent->diagramPlane->setParentLayout( gridPlaneLayout );
720 if ( curComponent->sharedSuccesor )
722 gridPlaneLayout->addItem( curColComponent->sharedSuccesor->diagramPlane, row + planeRowOffset, col + planeColOffset, 2, 2 );
723 curColComponent->sharedSuccesor->diagramPlane->setParentLayout( gridPlaneLayout );
724 planeLayoutItems << curColComponent->sharedSuccesor->diagramPlane;
726 const auto axes = cartDiag->
axes();
728 if ( axis->isAbscissa() )
730 if ( curColComponent->bottomSuccesor )
737 switch ( axis->position() )
739 case( CartesianAxis::Top ):
743 axis->setParentLayout( topLayout );
745 case( CartesianAxis::Bottom ):
749 axis->setParentLayout( bottomLayout );
751 case( CartesianAxis::Left ):
755 axis->setParentLayout( leftLayout );
757 case( CartesianAxis::Right ):
763 axis->setParentLayout( rightLayout );
766 planeLayoutItems << axis;
767 laidOutAxes.
insert( axis );
770 gridPlaneLayout->addLayout( leftLayout, row + planeRowOffset, col, 2, 1,
773 gridPlaneLayout->addLayout( rightLayout, row, col + planeColOffset + 2, 2, 1,
776 gridPlaneLayout->addLayout( topLayout, row, col + planeColOffset, 1, 2,
779 gridPlaneLayout->addLayout( bottomLayout, row + planeRowOffset + 2,
784 gridPlaneLayout->addItem( curColComponent->diagramPlane, row, col, 4, 4 );
785 curColComponent->diagramPlane->setParentLayout( gridPlaneLayout );
787 col += planeColOffset + 2 + ( 1 );
792 const int rowOffset = axisOffset + 2;
804 if ( dataAndLegendLayout ) {
805 dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
806 dataAndLegendLayout->setRowStretch( 1, 1000 );
807 dataAndLegendLayout->setColumnStretch( 1, 1000 );
810#ifdef NEW_LAYOUT_DEBUG
811 for (
int i = 0; i < gridPlaneLayout->rowCount(); ++i )
813 for (
int j = 0; j < gridPlaneLayout->columnCount(); ++j )
815 if ( gridPlaneLayout->itemAtPosition( i, j ) )
816 qDebug() << Q_FUNC_INFO <<
"item at" << i << j << gridPlaneLayout->itemAtPosition( i, j )->geometry();
818 qDebug() << Q_FUNC_INFO <<
"item at" << i << j <<
"no item present";
824 if ( hadPlanesLayout ) {
825 planesLayout->setContentsMargins( left, top, right, bottom );
828 planesLayout->setContentsMargins( 0, 0, 0, 0 );
829 planesLayout->setSpacing( 0 );
838 Q_ASSERT( planeInfos.
contains(plane) );
839 PlaneInfo& pi = planeInfos[ plane ];
840 const int column = pi.horizontalOffset;
841 const int row = pi.verticalOffset;
845 if ( !planeLayout ) {
846 PlaneInfo& refPi = pi;
849 while ( !planeLayout && refPi.referencePlane ) {
850 refPi = planeInfos[refPi.referencePlane];
851 planeLayout = refPi.gridLayout;
853 Q_ASSERT_X( planeLayout,
854 "Chart::Private::slotLayoutPlanes()",
855 "Invalid reference plane. Please check that the reference plane has been added to the Chart." );
857 planesLayout->addLayout( planeLayout );
863 planeLayoutItems << plane;
864 plane->setParentLayout( planeLayout );
865 planeLayout->
addItem( plane, row, column, 1, 1 );
870 const auto diagrams = plane->diagrams();
874 qobject_cast< AbstractCartesianDiagram* >( abstractDiagram );
879 if ( pi.referencePlane !=
nullptr )
881 pi.topAxesLayout = planeInfos[ pi.referencePlane ].topAxesLayout;
882 pi.bottomAxesLayout = planeInfos[ pi.referencePlane ].bottomAxesLayout;
883 pi.leftAxesLayout = planeInfos[ pi.referencePlane ].leftAxesLayout;
884 pi.rightAxesLayout = planeInfos[ pi.referencePlane ].rightAxesLayout;
888 if ( pi.topAxesLayout ==
nullptr )
894 if ( pi.bottomAxesLayout ==
nullptr )
900 if ( pi.leftAxesLayout ==
nullptr )
906 if ( pi.rightAxesLayout ==
nullptr )
913 if ( pi.referencePlane !=
nullptr )
915 planeInfos[ pi.referencePlane ].topAxesLayout = pi.topAxesLayout;
916 planeInfos[ pi.referencePlane ].bottomAxesLayout = pi.bottomAxesLayout;
917 planeInfos[ pi.referencePlane ].leftAxesLayout = pi.leftAxesLayout;
918 planeInfos[ pi.referencePlane ].rightAxesLayout = pi.rightAxesLayout;
928 axis->setCachedSizeDirty();
930 planeLayoutItems << axis;
932 switch ( axis->position() ) {
933 case CartesianAxis::Top:
934 axis->setParentLayout( pi.topAxesLayout );
935 pi.topAxesLayout->addItem( axis );
937 case CartesianAxis::Bottom:
938 axis->setParentLayout( pi.bottomAxesLayout );
939 pi.bottomAxesLayout->addItem( axis );
941 case CartesianAxis::Left:
942 axis->setParentLayout( pi.leftAxesLayout );
943 pi.leftAxesLayout->addItem( axis );
945 case CartesianAxis::Right:
946 axis->setParentLayout( pi.rightAxesLayout );
947 pi.rightAxesLayout->addItem( axis );
950 Q_ASSERT_X(
false,
"Chart::paintEvent",
"unknown axis position" );
953 axisInfos.
insert( axis, AxisInfo() );
960 if ( !pi.topAxesLayout->parent() ) {
961 planeLayout->
addLayout( pi.topAxesLayout, row - 1, column );
963 if ( !pi.bottomAxesLayout->parent() ) {
964 planeLayout->
addLayout( pi.bottomAxesLayout, row + 1, column );
966 if ( !pi.leftAxesLayout->parent() ) {
967 planeLayout->
addLayout( pi.leftAxesLayout, row, column - 1 );
969 if ( !pi.rightAxesLayout->parent() ) {
970 planeLayout->
addLayout( pi.rightAxesLayout,row, column + 1 );
975 #define ADD_AUTO_SPACER_IF_NEEDED( \
976 spacerRow, spacerColumn, hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ) \
978 if ( hLayout || vLayout ) { \
979 AutoSpacerLayoutItem * spacer \
980 = new AutoSpacerLayoutItem( hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ); \
981 planeLayout->addItem( spacer, spacerRow, spacerColumn, 1, 1 ); \
982 spacer->setParentLayout( planeLayout ); \
983 planeLayoutItems << spacer; \
987 if ( plane->isCornerSpacersEnabled() ) {
988 ADD_AUTO_SPACER_IF_NEEDED( row - 1, column - 1,
false, pi.leftAxesLayout,
false, pi.topAxesLayout )
989 ADD_AUTO_SPACER_IF_NEEDED( row + 1, column - 1, true, pi.leftAxesLayout, false, pi.bottomAxesLayout )
990 ADD_AUTO_SPACER_IF_NEEDED( row - 1, column + 1, false, pi.rightAxesLayout, true, pi.topAxesLayout )
991 ADD_AUTO_SPACER_IF_NEEDED( row + 1, column + 1, true, pi.rightAxesLayout, true, pi.bottomAxesLayout )
995 if ( dataAndLegendLayout ) {
996 dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
997 dataAndLegendLayout->setRowStretch( 1, 1000 );
998 dataAndLegendLayout->setColumnStretch( 1, 1000 );
1005void Chart::Private::createLayouts()
1011 layout->addSpacing( globalLeadingLeft );
1017 vLayout->setContentsMargins( 0, 0, 0, 0 );
1020 layout->addLayout( vLayout, 1000 );
1021 layout->addSpacing( globalLeadingRight );
1025 vLayout->addSpacing( globalLeadingTop );
1026 topOuterSpacer = vLayout->itemAt( vLayout->count() - 1 )->
spacerItem();
1029 headerLayout->setContentsMargins( 0, 0, 0, 0 );
1030 vLayout->addLayout( headerLayout );
1033 dataAndLegendLayout->setContentsMargins( 0, 0, 0, 0 );
1035 vLayout->addLayout( dataAndLegendLayout, 1000 );
1038 footerLayout->setContentsMargins( 0, 0, 0, 0 );
1040 vLayout->addLayout( footerLayout );
1046 for (
int row = 0; row < 3; ++row ) {
1047 for (
int column = 0; column < 3; ++ column ) {
1048 const Qt::Alignment align = s_gridAlignments[ row ][ column ];
1049 for (
int headOrFoot = 0; headOrFoot < 2; headOrFoot++ ) {
1053 innerHdFtLayouts[ headOrFoot ][ row ][ column ] = innerLayout;
1055 QGridLayout* outerLayout = headOrFoot == 0 ? headerLayout : footerLayout;
1056 outerLayout->
addLayout( innerLayout, row, column, align );
1062 vLayout->addSpacing( globalLeadingBottom );
1063 bottomOuterSpacer = vLayout->itemAt( vLayout->count() - 1 )->spacerItem();
1066 dataAndLegendLayout->addLayout( planesLayout, 1, 1 );
1067 dataAndLegendLayout->setRowStretch( 1, 1 );
1068 dataAndLegendLayout->setColumnStretch( 1, 1 );
1071void Chart::Private::slotResizePlanes()
1073 if ( !dataAndLegendLayout ) {
1076 if ( !overrideSize.isValid() ) {
1085 plane->layoutDiagrams();
1089void Chart::Private::updateDirtyLayouts()
1091 if ( isPlanesLayoutDirty ) {
1093 p->setGridNeedsRecalculate();
1095 p->layoutDiagrams();
1098 if ( isPlanesLayoutDirty || isFloatingLegendsLayoutDirty ) {
1099 chart->reLayoutFloatingLegends();
1101 isPlanesLayoutDirty =
false;
1102 isFloatingLegendsLayoutDirty =
false;
1105void Chart::Private::reapplyInternalLayouts()
1109 invalidateLayoutTree( layout );
1114void Chart::Private::paintAll(
QPainter* painter )
1116 updateDirtyLayouts();
1118 QRect rect(
QPoint( 0, 0 ), overrideSize.isValid() ? overrideSize : chart->size() );
1123 AbstractAreaBase::paintBackgroundAttributes( *painter, rect, backgroundAttributes );
1125 AbstractAreaBase::paintFrameAttributes( *painter, rect, frameAttributes );
1127 chart->reLayoutFloatingLegends();
1130 planeLayoutItem->paintAll( *painter );
1132 for(
TextArea* textLayoutItem : std::as_const(textLayoutItems) ) {
1133 textLayoutItem->paintAll( *painter );
1135 for (
Legend *legend : std::as_const(legends) ) {
1139 legend->paintIntoRect( *painter, legend->geometry() );
1148Chart::Chart (
QWidget* parent )
1150 , _d( new Private( this ) )
1152#if defined KDAB_EVAL
1153 EvalDialog::checkEvalLicense(
"KD Chart" );
1160 frameAttrs.setPadding( 1 );
1161 setFrameAttributes( frameAttrs );
1175 for (
auto legend : d->legends) {
1184 d->frameAttributes = a;
1189 return d->frameAttributes;
1194 d->backgroundAttributes = a;
1199 return d->backgroundAttributes;
1203void Chart::setCoordinatePlaneLayout(
QLayout * layout )
1205 if (layout == d->planesLayout)
1207 if (d->planesLayout) {
1210 for(
int i = d->planesLayout->count() - 1; i >= 0; --i) {
1211 d->planesLayout->takeAt(i);
1213 delete d->planesLayout;
1216 d->slotLayoutPlanes();
1219QLayout* Chart::coordinatePlaneLayout()
1221 return d->planesLayout;
1226 if ( d->coordinatePlanes.isEmpty() ) {
1227 qWarning() <<
"Chart::coordinatePlane: warning: no coordinate plane defined.";
1230 return d->coordinatePlanes.first();
1236 return d->coordinatePlanes;
1242 insertCoordinatePlane( d->coordinatePlanes.count(), plane );
1247 if ( index < 0 || index > d->coordinatePlanes.count() ) {
1252 d, &Private::slotUnregisterDestroyedPlane );
1257 d->coordinatePlanes.insert( index, plane );
1259 d->slotLayoutPlanes();
1265 if ( plane && oldPlane_ != plane ) {
1267 if ( d->coordinatePlanes.count() ) {
1269 oldPlane = d->coordinatePlanes.first();
1270 if ( oldPlane == plane )
1273 takeCoordinatePlane( oldPlane );
1276 addCoordinatePlane( plane );
1282 const int idx = d->coordinatePlanes.indexOf( plane );
1284 d->coordinatePlanes.takeAt( idx );
1287 plane->removeFromParentLayout();
1289 d->mouseClickedPlanes.removeAll(plane);
1291 d->slotLayoutPlanes();
1294 Q_EMIT propertiesChanged();
1297void Chart::setGlobalLeading(
int left,
int top,
int right,
int bottom )
1299 setGlobalLeadingLeft( left );
1300 setGlobalLeadingTop( top );
1301 setGlobalLeadingRight( right );
1302 setGlobalLeadingBottom( bottom );
1305void Chart::setGlobalLeadingLeft(
int leading )
1307 d->globalLeadingLeft = leading;
1309 d->reapplyInternalLayouts();
1312int Chart::globalLeadingLeft()
const
1314 return d->globalLeadingLeft;
1317void Chart::setGlobalLeadingTop(
int leading )
1319 d->globalLeadingTop = leading;
1321 d->reapplyInternalLayouts();
1324int Chart::globalLeadingTop()
const
1326 return d->globalLeadingTop;
1329void Chart::setGlobalLeadingRight(
int leading )
1331 d->globalLeadingRight = leading;
1333 d->reapplyInternalLayouts();
1336int Chart::globalLeadingRight()
const
1338 return d->globalLeadingRight;
1341void Chart::setGlobalLeadingBottom(
int leading )
1343 d->globalLeadingBottom = leading;
1345 d->reapplyInternalLayouts();
1348int Chart::globalLeadingBottom()
const
1350 return d->globalLeadingBottom;
1355 if ( rect.
isEmpty() || !painter ) {
1361 int prevScaleFactor = PrintingParameters::scaleFactor();
1363 PrintingParameters::setScaleFactor( qreal( painter->
device()->
logicalDpiX() ) / qreal( logicalDpiX() ) );
1365 const QRect oldGeometry( geometry() );
1366 if ( oldGeometry != rect ) {
1367 setGeometry( rect );
1368 d->isPlanesLayoutDirty =
true;
1369 d->isFloatingLegendsLayoutDirty =
true;
1372 d->paintAll( painter );
1379 if ( oldGeometry != rect ) {
1380 setGeometry( oldGeometry );
1381 d->isPlanesLayoutDirty =
true;
1382 d->isFloatingLegendsLayoutDirty =
true;
1385 PrintingParameters::setScaleFactor( prevScaleFactor );
1391 d->isPlanesLayoutDirty =
true;
1392 d->isFloatingLegendsLayoutDirty =
true;
1396void Chart::reLayoutFloatingLegends()
1398 for(
Legend *legend : std::as_const(d->legends) ) {
1400 if ( legend->position().isFloating() && !hidden ) {
1402 const QSize legendSize( legend->sizeHint() );
1403 legend->setGeometry(
QRect( legend->geometry().topLeft(), legendSize ) );
1410 if ( (relPos.alignment() & alignTopLeft) != alignTopLeft ) {
1412 pt.rx() -= legendSize.width();
1414 pt.rx() -= 0.5 * legendSize.width();
1417 pt.ry() -= legendSize.height();
1419 pt.ry() -= 0.5 * legendSize.height();
1422 legend->move(
static_cast<int>(pt.x()),
static_cast<int>(pt.y()) );
1431 d->paintAll( &painter );
1432 Q_EMIT finishedDrawing();
1437 Q_ASSERT( hf->type() == HeaderFooter::Header || hf->type() == HeaderFooter::Footer );
1440 getRowAndColumnForPosition( hf->position().
value(), &row, &column );
1442 qWarning(
"Unknown header/footer position" );
1446 d->headerFooters.append( hf );
1447 d->textLayoutItems.append( hf );
1448 connect( hf, &HeaderFooter::destroyedHeaderFooter,
1449 d, &Private::slotUnregisterDestroyedHeaderFooter );
1450 connect( hf, &HeaderFooter::positionChanged,
1451 d, &Private::slotHeaderFooterPositionChanged );
1457 measure.
setRelativeMode(
this, KChartEnums::MeasureOrientationMinimum );
1458 measure.setValue( 20 );
1464 int innerLayoutIdx = hf->type() == HeaderFooter::Header ? 0 : 1;
1465 QVBoxLayout* headerFooterLayout = d->innerHdFtLayouts[ innerLayoutIdx ][ row ][ column ];
1467 hf->setParentLayout( headerFooterLayout );
1469 headerFooterLayout->
addItem( hf );
1471 d->slotResizePlanes();
1477 if ( headerFooter && oldHeaderFooter_ != headerFooter ) {
1479 if ( d->headerFooters.count() ) {
1480 if ( ! oldHeaderFooter ) {
1481 oldHeaderFooter = d->headerFooters.first();
1482 if ( oldHeaderFooter == headerFooter )
1485 takeHeaderFooter( oldHeaderFooter );
1487 delete oldHeaderFooter;
1488 addHeaderFooter( headerFooter );
1494 const int idx = d->headerFooters.indexOf( headerFooter );
1498 disconnect( headerFooter, &HeaderFooter::destroyedHeaderFooter,
1499 d, &Private::slotUnregisterDestroyedHeaderFooter );
1501 d->headerFooters.takeAt( idx );
1502 headerFooter->removeFromParentLayout();
1503 headerFooter->setParentLayout(
nullptr );
1504 d->textLayoutItems.remove( d->textLayoutItems.indexOf( headerFooter ) );
1506 d->slotResizePlanes();
1509void Chart::Private::slotHeaderFooterPositionChanged(
HeaderFooter* hf )
1511 chart->takeHeaderFooter( hf );
1512 chart->addHeaderFooter( hf );
1517 if ( d->headerFooters.isEmpty() ) {
1520 return d->headerFooters.first();
1526 return d->headerFooters;
1531 Legend* legend = qobject_cast< Legend* >( aw );
1533 chart->takeLegend( legend );
1534 chart->addLegendInternal( legend,
false );
1540 addLegendInternal( legend,
true );
1541 Q_EMIT propertiesChanged();
1544void Chart::addLegendInternal(
Legend* legend,
bool setMeasures )
1551 if ( pos == KChartEnums::PositionCenter ) {
1552 qWarning(
"Not showing legend because PositionCenter is not supported for legends." );
1557 getRowAndColumnForPosition( pos, &row, &column );
1558 if ( row < 0 && pos != KChartEnums::PositionFloating ) {
1559 qWarning(
"Not showing legend because of unknown legend position." );
1563 d->legends.append( legend );
1568 if ( setMeasures ) {
1570 Measure measure( textAttrs.fontSize() );
1571 measure.setRelativeMode(
this, KChartEnums::MeasureOrientationMinimum );
1572 measure.setValue( 20 );
1573 textAttrs.setFontSize( measure );
1574 legend->setTextAttributes( textAttrs );
1576 textAttrs = legend->titleTextAttributes();
1577 measure.setRelativeMode(
this, KChartEnums::MeasureOrientationMinimum );
1578 measure.setValue( 24 );
1581 legend->setTitleTextAttributes( textAttrs );
1587 if ( pos != KChartEnums::PositionFloating ) {
1593 QLayoutItem* edgeItem = d->dataAndLegendLayout->itemAtPosition( row, column );
1595 Q_ASSERT( !edgeItem || alignmentsLayout );
1596 if ( !alignmentsLayout ) {
1598 d->dataAndLegendLayout->addLayout( alignmentsLayout, row, column );
1607 for (
int i = 0; i < 3; i++ ) {
1608 for (
int j = 0; j < 3; j++ ) {
1620 Q_ASSERT( !alignmentItem || sameAlignmentLayout );
1621 if ( !sameAlignmentLayout ) {
1623 alignmentsLayout->
addLayout( sameAlignmentLayout, row, column );
1627 sameAlignmentLayout->
addItem(
new MyWidgetItem( legend, legend->
alignment() ) );
1630 connect( legend, &Legend::destroyedLegend,
1631 d, &Private::slotUnregisterDestroyedLegend );
1632 connect( legend, &Legend::positionChanged,
1633 d, &Private::slotLegendPositionChanged );
1634 connect( legend, &Legend::propertiesChanged,
this, &Chart::propertiesChanged );
1636 d->slotResizePlanes();
1641 if ( legend && oldLegend_ != legend ) {
1642 Legend* oldLegend = oldLegend_;
1643 if ( d->legends.count() ) {
1644 if ( ! oldLegend ) {
1645 oldLegend = d->legends.first();
1646 if ( oldLegend == legend )
1649 takeLegend( oldLegend );
1652 addLegend( legend );
1658 const int idx = d->legends.indexOf( legend );
1663 d->legends.takeAt( idx );
1665 disconnect( legend,
nullptr,
this,
nullptr );
1669 d->slotResizePlanes();
1670 Q_EMIT propertiesChanged();
1675 return d->legends.isEmpty() ? nullptr : d->legends.first();
1688 if ( plane->geometry().contains(
event->pos() ) && plane->diagrams().size() > 0 ) {
1691 plane->mousePressEvent( &ev );
1692 d->mouseClickedPlanes.append( plane );
1702 if ( plane->geometry().contains(
event->pos() ) && plane->diagrams().size() > 0 ) {
1705 plane->mouseDoubleClickEvent( &ev );
1715 if ( plane->geometry().contains(
event->pos() ) && plane->diagrams().size() > 0 ) {
1716 eventReceivers.insert( plane );
1725 plane->mouseMoveEvent( &ev );
1734 if ( plane->geometry().contains(
event->pos() ) && plane->diagrams().size() > 0 ) {
1735 eventReceivers.insert( plane );
1744 plane->mouseReleaseEvent( &ev );
1747 d->mouseClickedPlanes.clear();
1758 for (
int i = diagrams.
size() - 1; i >= 0; --i) {
1777bool Chart::useNewLayoutSystem()
const
1779 return d_func()->useNewLayoutSystem;
1781void Chart::setUseNewLayoutSystem(
bool value )
1783 if ( d_func()->useNewLayoutSystem != value )
1784 d_func()->useNewLayoutSystem = value;
Definition of global enums.
PositionValue
Numerical values of the static KChart::Position instances, for using a Position::value() with a switc...
Base class for diagrams based on a cartesian coordianate system.
virtual KChart::CartesianAxisList axes() const
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane,...
void layoutPlanes()
Calling layoutPlanes() on the plane triggers the global KChart::Chart::slotLayoutPlanes()
void propertiesChanged()
Emitted upon change of a property of the Coordinate Plane or any of its components.
void setParent(Chart *parent)
Called internally by KChart::Chart.
void needRelayout()
Emitted when plane needs to trigger the Chart's layouting.
AbstractDiagramList diagrams()
AbstractDiagram * diagram()
void destroyedCoordinatePlane(KChart::AbstractCoordinatePlane *)
Emitted when this coordinate plane is destroyed.
void needLayoutPlanes()
Emitted when plane needs to trigger the Chart's layouting of the coord.
void needUpdate()
Emitted when plane needs to update its drawings.
AbstractCoordinatePlane * referenceCoordinatePlane() const
There are two ways, in which planes can be caused to interact, in where they are put layouting wise: ...
AbstractDiagram defines the interface for diagram classes.
QModelIndex indexAt(const QPoint &point) const override
\reimpl
bool isHidden() const
Retrieve the hidden status specified globally.
Base class for all layout items of KChart.
Set of attributes usable for background pixmaps.
The class for cartesian axes.
Cartesian coordinate plane.
A set of attributes for frames around items.
static QPaintDevice * paintDevice()
Return the paint device to use for calculating font metrics.
static void setPaintDevice(QPaintDevice *paintDevice)
Set the paint device to use for calculating font metrics.
Legend defines the interface for the legend drawing class.
void needSizeHint() override
Call this to trigger an conditional re-building of the widget's internals.
Qt::Alignment alignment() const
Returns the alignment of a non-floating legend.
void setReferenceArea(const QWidget *area)
Specifies the reference area for font size of title text, and for font size of the item texts,...
Position position() const
Returns the position of a non-floating legend.
Measure is used to specify relative and absolute sizes in KChart, e.g.
void setRelativeMode(const QObject *area, KChartEnums::MeasureOrientation orientation)
The reference area must either be derived from AbstractArea or from QWidget, so it can also be derive...
KChartEnums::PositionValue value() const
Returns an integer value corresponding to this Position.
Defines relative position information: reference area, position in this area (reference position),...
A text area in the chart with a background, a frame, etc.
A set of text attributes.
void setFontSize(const Measure &measure)
Set the size of the font used for rendering text.
void setTextAttributes(const TextAttributes &a)
Use this to specify the text attributes to be used for this item.
TextAttributes textAttributes() const
Returns the text attributes to be used for this item.
QAction * hint(const QObject *recvr, const char *slot, QObject *parent)
GeoCoordinates geo(const QVariant &location)
virtual void addItem(QLayoutItem *item) override
virtual void addItem(QLayoutItem *item) override
void addLayout(QLayout *layout, int row, int column, Qt::Alignment alignment)
QLayoutItem * itemAtPosition(int row, int column) const const
void setColumnStretch(int column, int stretch)
void setRowStretch(int row, int stretch)
bool contains(const Key &key) const const
iterator insert(const Key &key, const T &value)
const QPoint & pos() const const
virtual int count() const const=0
virtual Qt::Orientations expandingDirections() const const override
virtual QRect geometry() const const override
virtual QLayoutItem * itemAt(int index) const const=0
virtual QSize maximumSize() const const override
virtual QSize minimumSize() const const override
bool setAlignment(QLayout *l, Qt::Alignment alignment)
void setContentsMargins(const QMargins &margins)
virtual void setGeometry(const QRect &r) override
Qt::Alignment alignment() const const
virtual Qt::Orientations expandingDirections() const const=0
virtual QRect geometry() const const=0
virtual bool hasHeightForWidth() const const
virtual void invalidate()
virtual QLayout * layout()
virtual QSize maximumSize() const const=0
virtual QSize minimumSize() const const=0
void setAlignment(Qt::Alignment alignment)
virtual QSize sizeHint() const const=0
virtual QSpacerItem * spacerItem()
void append(QList< T > &&value)
bool contains(const AT &value) const const
bool isEmpty() const const
qsizetype size() const const
QVariant data(int role) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
T qobject_cast(QObject *object)
void setObjectName(QAnyStringView name)
int logicalDpiX() const const
QPaintDevice * device() const const
void translate(const QPoint &offset)
virtual bool event(QEvent *ev) override
QPointF mapFromGlobal(const QPointF &point) const const
virtual void mouseDoubleClickEvent(QMouseEvent *event)
virtual void mouseMoveEvent(QMouseEvent *event)
virtual void mousePressEvent(QMouseEvent *event)
virtual void mouseReleaseEvent(QMouseEvent *event)
QSizeF size() const const
bool isEmpty() const const
bool contains(const QSet< T > &other) const const
iterator insert(const T &value)
virtual QSpacerItem * spacerItem() override
QString fromLatin1(QByteArrayView str)
QString number(double n, char format, int precision)
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)
void showText(const QPoint &pos, const QString &text, QWidget *w, const QRect &rect, int msecDisplayTime)
bool isValid() const const
QString toString() const const