diff --git a/examples/DrawIntoPainter/framewidget.h b/examples/DrawIntoPainter/framewidget.h index b5ca671..5623150 100644 --- a/examples/DrawIntoPainter/framewidget.h +++ b/examples/DrawIntoPainter/framewidget.h @@ -1,47 +1,47 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FRAMEWIDGET_H #define FRAMEWIDGET_H #include namespace KChart { class Chart; } class FrameWidget : public QWidget { Q_OBJECT public: - explicit FrameWidget( QWidget * parent = 0, Qt::WindowFlags f = 0 ); + explicit FrameWidget( QWidget * parent = 0, Qt::WindowFlags f = Qt::WindowFlags() ); void paintEvent( QPaintEvent* ) Q_DECL_OVERRIDE; void setChart( KChart::Chart* chart ); private: KChart::Chart* mChart; }; #endif /* FRAMEWIDGET_H */ diff --git a/examples/Gantt/legend_example/entrydialog.h b/examples/Gantt/legend_example/entrydialog.h index d0f1522..10d9a3a 100644 --- a/examples/Gantt/legend_example/entrydialog.h +++ b/examples/Gantt/legend_example/entrydialog.h @@ -1,68 +1,68 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ENTRYDIALOG_H #define ENTRYDIALOG_H #include #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; namespace Ui { class EntryDialog; } QT_END_NAMESPACE namespace KGantt { class ConstraintModel; } class EntryDialog : public QDialog { Q_OBJECT public: - explicit EntryDialog( const QAbstractItemModel* model, QWidget* parent = 0, Qt::WindowFlags f = 0 ); + explicit EntryDialog( const QAbstractItemModel* model, QWidget* parent = 0, Qt::WindowFlags f = Qt::WindowFlags() ); void initFrom( const QModelIndex& index, const KGantt::ConstraintModel* constraintModel ); QString name() const; int type() const; QDateTime startDate() const; QDateTime endDate() const; int completion() const; bool readOnly() const; QModelIndex depends() const; QString legend() const; private slots: void updateEndDate( const QDateTime& startDate ); void disableEditing( bool disable ); void typeChanged( int index ); private: void init(); void addDependItem( const QAbstractItemModel* model, const QModelIndex& index, int indent = 0 ); QList indexList; const QAbstractItemModel* model; Ui::EntryDialog* ui; }; #endif /* ENTRYDIALOG_H */ diff --git a/examples/Gantt/legend_example/mainwindow.h b/examples/Gantt/legend_example/mainwindow.h index 8882da5..b305095 100644 --- a/examples/Gantt/legend_example/mainwindow.h +++ b/examples/Gantt/legend_example/mainwindow.h @@ -1,86 +1,86 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include QT_BEGIN_NAMESPACE class QStandardItemModel; class QCloseEvent; namespace Ui { class MainWindow; } QT_END_NAMESPACE namespace KGantt { class ConstraintModel; class DateTimeGrid; class Legend; } class MainWindow : public QMainWindow { Q_OBJECT public: - explicit MainWindow( QWidget * parent = 0, Qt::WindowFlags flags = 0 ); + explicit MainWindow( QWidget * parent = 0, Qt::WindowFlags flags = Qt::WindowFlags() ); virtual ~MainWindow(); void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; private slots: void addNewEntry(); void removeEntry(); void showContextMenu( const QPoint& ); void enableActions( const QItemSelection& selected ); void zoomIn(); void zoomOut(); void zoomFit(); void scaleAuto(); void scaleHour(); void scaleDay(); void scaleWeek(); void scaleMonth(); private: void initModel(); void initActions(); void initItemDelegate(); void initGrid(); void setReadOnly( const QModelIndex& index, bool readOnly ); void addConstraint( const QModelIndex& index1, const QModelIndex& index2 ); QStandardItemModel* model; KGantt::ConstraintModel* constraintModel; KGantt::DateTimeGrid* grid; KGantt::Legend* smallLegend; KGantt::Legend* detailedLegend; QAction* newEntryAction; QAction* removeEntryAction; QAction* zoomInAction; QAction* zoomOutAction; QAction* zoomFitAction; Ui::MainWindow* ui; }; #endif /* MAINWINDOW_H */ diff --git a/qtests/DrawIntoPainter/framewidget.h b/qtests/DrawIntoPainter/framewidget.h index b5ca671..5623150 100644 --- a/qtests/DrawIntoPainter/framewidget.h +++ b/qtests/DrawIntoPainter/framewidget.h @@ -1,47 +1,47 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FRAMEWIDGET_H #define FRAMEWIDGET_H #include namespace KChart { class Chart; } class FrameWidget : public QWidget { Q_OBJECT public: - explicit FrameWidget( QWidget * parent = 0, Qt::WindowFlags f = 0 ); + explicit FrameWidget( QWidget * parent = 0, Qt::WindowFlags f = Qt::WindowFlags() ); void paintEvent( QPaintEvent* ) Q_DECL_OVERRIDE; void setChart( KChart::Chart* chart ); private: KChart::Chart* mChart; }; #endif /* FRAMEWIDGET_H */ diff --git a/src/KChart/KChartChart.cpp b/src/KChart/KChartChart.cpp index e14a823..0d8c39c 100644 --- a/src/KChart/KChartChart.cpp +++ b/src/KChart/KChartChart.cpp @@ -1,1805 +1,1805 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartChart.h" #include "KChartChart_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "KChartCartesianCoordinatePlane.h" #include "KChartAbstractCartesianDiagram.h" #include "KChartHeaderFooter.h" #include "KChartEnums.h" #include "KChartLegend.h" #include "KChartLayoutItems.h" #include #include #include "KChartPainterSaver_p.h" #include "KChartPrintingParameters.h" #include #if defined KDAB_EVAL #include "../evaldialog/evaldialog.h" #endif #if 0 // dumpLayoutTree dumps a QLayout tree in a hopefully easy to read format to stderr - feel free to // use, improve and extend; it is very useful for looking at any layout problem. #include // this is this different from both QRect::isEmpty() and QRect::isNull() for "wrong" QRects, // i.e. those where topLeft() is actually below and / or right of bottomRight(). static bool isZeroArea(const QRect &r) { return !r.width() || !r.height(); } static QString lineProlog(int nestingDepth, int lineno) { QString numbering(QString::number(lineno).rightJustified(5).append(QChar::fromAscii(':'))); QString indent(nestingDepth * 4, QLatin1Char(' ')); return numbering + indent; } static void dumpLayoutTreeRecurse(QLayout *l, int *counter, int depth) { const QLatin1String colorOn(isZeroArea(l->geometry()) ? "\033[0m" : "\033[32m"); const QLatin1String colorOff("\033[0m"); QString prolog = lineProlog(depth, *counter); (*counter)++; qDebug() << colorOn + prolog << l->metaObject()->className() << l->geometry() << "hint" << l->sizeHint() << l->hasHeightForWidth() << "min" << l->minimumSize() << "max" << l->maximumSize() << l->expandingDirections() << l->alignment() << colorOff; for (int i = 0; i < l->count(); i++) { QLayoutItem *child = l->itemAt(i); if (QLayout *childL = child->layout()) { dumpLayoutTreeRecurse(childL, counter, depth + 1); } else { // The isZeroArea check culls usually largely useless output - you might want to remove it in // some debugging situations. Add a boolean parameter to this and dumpLayoutTree() if you do. if (!isZeroArea(child->geometry())) { prolog = lineProlog(depth + 1, *counter); (*counter)++; qDebug() << colorOn + prolog << typeid(*child).name() << child->geometry() << "hint" << child->sizeHint() << child->hasHeightForWidth() << "min" << child->minimumSize() << "max" << child->maximumSize() << child->expandingDirections() << child->alignment() << colorOff; } } } } static void dumpLayoutTree(QLayout *l) { int counter = 0; dumpLayoutTreeRecurse(l, &counter, 0); } #endif static const Qt::Alignment s_gridAlignments[ 3 ][ 3 ] = { // [ row ][ column ] { Qt::AlignTop | Qt::AlignLeft, Qt::AlignTop | Qt::AlignHCenter, Qt::AlignTop | Qt::AlignRight }, { Qt::AlignVCenter | Qt::AlignLeft, Qt::AlignVCenter | Qt::AlignHCenter, Qt::AlignVCenter | Qt::AlignRight }, { Qt::AlignBottom | Qt::AlignLeft, Qt::AlignBottom | Qt::AlignHCenter, Qt::AlignBottom | Qt::AlignRight } }; static void getRowAndColumnForPosition(KChartEnums::PositionValue pos, int* row, int* column) { switch ( pos ) { case KChartEnums::PositionNorthWest: *row = 0; *column = 0; break; case KChartEnums::PositionNorth: *row = 0; *column = 1; break; case KChartEnums::PositionNorthEast: *row = 0; *column = 2; break; case KChartEnums::PositionEast: *row = 1; *column = 2; break; case KChartEnums::PositionSouthEast: *row = 2; *column = 2; break; case KChartEnums::PositionSouth: *row = 2; *column = 1; break; case KChartEnums::PositionSouthWest: *row = 2; *column = 0; break; case KChartEnums::PositionWest: *row = 1; *column = 0; break; case KChartEnums::PositionCenter: *row = 1; *column = 1; break; default: *row = -1; *column = -1; break; } } using namespace KChart; // Layout widgets even if they are not visible (that's why isEmpty() is overridden) - at least that // was the original reason... class MyWidgetItem : public QWidgetItem { public: - explicit MyWidgetItem(QWidget *w, Qt::Alignment alignment = 0) + explicit MyWidgetItem(QWidget *w, Qt::Alignment alignment = Qt::Alignment()) : QWidgetItem( w ) { setAlignment( alignment ); } // All of the methods are reimplemented from QWidgetItem, and work around some oddity in QLayout and / or // KD Chart - I forgot the details between writing this code as an experiment and committing it, very // sorry about that! // Feel free to comment out any of them and then try the line-breaking feature in horizontal legends in // the Legends/Advanced example. It will not work well in various ways - won't get enough space and look // very broken, will inhibit resizing the window etc. QSize sizeHint() const Q_DECL_OVERRIDE { QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); return w->sizeHint(); } QSize minimumSize() const Q_DECL_OVERRIDE { QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); return w->minimumSize(); } QSize maximumSize() const Q_DECL_OVERRIDE { // Not just passing on w->maximumSize() fixes that the size policy of Legend is disregarded, making // Legend take all available space, which makes both the Legend internal layout and the overall // layout of chart + legend look bad. QWidget::maximumSize() is not a virtual method, it's a // property, so "overriding" that one would be even uglier, and prevent user-set property // values from doing anything. QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); QSize ret = w->maximumSize(); const QSize hint = w->sizeHint(); const QSizePolicy::Policy hPolicy = w->sizePolicy().horizontalPolicy(); if (hPolicy == QSizePolicy::Fixed || hPolicy == QSizePolicy::Maximum) { ret.rwidth() = hint.width(); } const QSizePolicy::Policy vPolicy = w->sizePolicy().verticalPolicy(); if (vPolicy == QSizePolicy::Fixed || vPolicy == QSizePolicy::Maximum) { ret.rheight() = hint.height(); } return ret; } Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE { QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); if ( isEmpty() ) { return Qt::Orientations(0); } Qt::Orientations e = w->sizePolicy().expandingDirections(); return e; } void setGeometry(const QRect &g) Q_DECL_OVERRIDE { QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); w->setGeometry(g); } QRect geometry() const Q_DECL_OVERRIDE { QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); return w->geometry(); } bool hasHeightForWidth() const Q_DECL_OVERRIDE { QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); bool ret = !isEmpty() && qobject_cast< Legend* >( w )->hasHeightForWidth(); return ret; } int heightForWidth( int width ) const Q_DECL_OVERRIDE { QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); int ret = w->heightForWidth( width ); return ret; } bool isEmpty() const Q_DECL_OVERRIDE { QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); // legend->hide() should indeed hide the legend, // but a legend in a chart that hasn't been shown yet isn't hidden // (as can happen when using Chart::paint() without showing the chart) return w->isHidden() && w->testAttribute( Qt::WA_WState_ExplicitShowHide ); } }; // When "abusing" QLayouts to lay out items with different geometry from the backing QWidgets, // some manual work is required to correctly update all the sublayouts. // This is because all the convenient ways to deal with QLayouts assume QWidgets somewhere. // What this does is somewhat similar to QLayout::activate(), but it never refers to the parent // QWidget which has the wrong geometry. static void invalidateLayoutTree( QLayoutItem *item ) { QLayout *layout = item->layout(); if ( layout ) { const int count = layout->count(); for ( int i = 0; i < count; i++ ) { invalidateLayoutTree( layout->itemAt( i ) ); } } item->invalidate(); } void Chart::Private::slotUnregisterDestroyedLegend( Legend *l ) { chart->takeLegend( l ); } void Chart::Private::slotUnregisterDestroyedHeaderFooter( HeaderFooter* hf ) { chart->takeHeaderFooter( hf ); } void Chart::Private::slotUnregisterDestroyedPlane( AbstractCoordinatePlane* plane ) { coordinatePlanes.removeAll( plane ); Q_FOREACH ( AbstractCoordinatePlane* p, coordinatePlanes ) { if ( p->referenceCoordinatePlane() == plane) { p->setReferenceCoordinatePlane( 0 ); } } plane->layoutPlanes(); } Chart::Private::Private( Chart* chart_ ) : chart( chart_ ) , useNewLayoutSystem( false ) , layout( 0 ) , vLayout( 0 ) , planesLayout( 0 ) , headerLayout( 0 ) , footerLayout( 0 ) , dataAndLegendLayout( 0 ) , leftOuterSpacer( 0 ) , rightOuterSpacer( 0 ) , topOuterSpacer( 0 ) , bottomOuterSpacer( 0 ) , isFloatingLegendsLayoutDirty( true ) , isPlanesLayoutDirty( true ) , globalLeadingLeft( 0 ) , globalLeadingRight( 0 ) , globalLeadingTop( 0 ) , globalLeadingBottom( 0 ) { for ( int row = 0; row < 3; ++row ) { for ( int column = 0; column < 3; ++column ) { for ( int i = 0; i < 2; i++ ) { innerHdFtLayouts[ i ][ row ][ column ] = 0; } } } } Chart::Private::~Private() { } enum VisitorState{ Visited, Unknown }; struct ConnectedComponentsComparator{ bool operator()( const LayoutGraphNode *lhs, const LayoutGraphNode *rhs ) const { return lhs->priority < rhs->priority; } }; static QVector< LayoutGraphNode* > getPrioritySortedConnectedComponents( QVector< LayoutGraphNode* > &nodeList ) { QVector< LayoutGraphNode* >connectedComponents; QHash< LayoutGraphNode*, VisitorState > visitedComponents; Q_FOREACH ( LayoutGraphNode* node, nodeList ) visitedComponents[ node ] = Unknown; for ( int i = 0; i < nodeList.size(); ++i ) { LayoutGraphNode *curNode = nodeList[ i ]; LayoutGraphNode *representativeNode = curNode; if ( visitedComponents[ curNode ] != Visited ) { QStack< LayoutGraphNode* > stack; stack.push( curNode ); while ( !stack.isEmpty() ) { curNode = stack.pop(); Q_ASSERT( visitedComponents[ curNode ] != Visited ); visitedComponents[ curNode ] = Visited; if ( curNode->bottomSuccesor && visitedComponents[ curNode->bottomSuccesor ] != Visited ) stack.push( curNode->bottomSuccesor ); if ( curNode->leftSuccesor && visitedComponents[ curNode->leftSuccesor ] != Visited ) stack.push( curNode->leftSuccesor ); if ( curNode->sharedSuccesor && visitedComponents[ curNode->sharedSuccesor ] != Visited ) stack.push( curNode->sharedSuccesor ); if ( curNode->priority < representativeNode->priority ) representativeNode = curNode; } connectedComponents.append( representativeNode ); } } std::sort( connectedComponents.begin(), connectedComponents.end(), ConnectedComponentsComparator() ); return connectedComponents; } struct PriorityComparator{ public: PriorityComparator( QHash< AbstractCoordinatePlane*, LayoutGraphNode* > mapping ) : m_mapping( mapping ) {} bool operator() ( AbstractCoordinatePlane *lhs, AbstractCoordinatePlane *rhs ) const { const LayoutGraphNode *lhsNode = m_mapping[ lhs ]; Q_ASSERT( lhsNode ); const LayoutGraphNode *rhsNode = m_mapping[ rhs ]; Q_ASSERT( rhsNode ); return lhsNode->priority < rhsNode->priority; } const QHash< AbstractCoordinatePlane*, LayoutGraphNode* > m_mapping; }; void checkExistingAxes( LayoutGraphNode* node ) { if ( node && node->diagramPlane && node->diagramPlane->diagram() ) { AbstractCartesianDiagram *diag = qobject_cast< AbstractCartesianDiagram* >( node->diagramPlane->diagram() ); if ( diag ) { Q_FOREACH( const CartesianAxis* axis, diag->axes() ) { switch ( axis->position() ) { case( CartesianAxis::Top ): node->topAxesLayout = true; break; case( CartesianAxis::Bottom ): node->bottomAxesLayout = true; break; case( CartesianAxis::Left ): node->leftAxesLayout = true; break; case( CartesianAxis::Right ): node->rightAxesLayout = true; break; } } } } } static void mergeNodeAxisInformation( LayoutGraphNode* lhs, LayoutGraphNode* rhs ) { lhs->topAxesLayout |= rhs->topAxesLayout; rhs->topAxesLayout = lhs->topAxesLayout; lhs->bottomAxesLayout |= rhs->bottomAxesLayout; rhs->bottomAxesLayout = lhs->bottomAxesLayout; lhs->leftAxesLayout |= rhs->leftAxesLayout; rhs->leftAxesLayout = lhs->leftAxesLayout; lhs->rightAxesLayout |= rhs->rightAxesLayout; rhs->rightAxesLayout = lhs->rightAxesLayout; } static CoordinatePlaneList findSharingAxisDiagrams( AbstractCoordinatePlane* plane, const CoordinatePlaneList& list, Chart::Private::AxisType type, QVector< CartesianAxis* >* sharedAxes ) { if ( !plane || !plane->diagram() ) return CoordinatePlaneList(); Q_ASSERT( plane ); Q_ASSERT( plane->diagram() ); CoordinatePlaneList result; AbstractCartesianDiagram* diagram = qobject_cast< AbstractCartesianDiagram* >( plane->diagram() ); if ( !diagram ) return CoordinatePlaneList(); QList< CartesianAxis* > axes; Q_FOREACH( CartesianAxis* axis, diagram->axes() ) { if ( ( type == Chart::Private::Ordinate && ( axis->position() == CartesianAxis::Left || axis->position() == CartesianAxis::Right ) ) || ( type == Chart::Private::Abscissa && ( axis->position() == CartesianAxis::Top || axis->position() == CartesianAxis::Bottom ) ) ) { axes.append( axis ); } } Q_FOREACH( AbstractCoordinatePlane *curPlane, list ) { AbstractCartesianDiagram* diagram = qobject_cast< AbstractCartesianDiagram* > ( curPlane->diagram() ); if ( !diagram ) continue; Q_FOREACH( CartesianAxis* curSearchedAxis, axes ) { Q_FOREACH( CartesianAxis* curAxis, diagram->axes() ) { if ( curSearchedAxis == curAxis ) { result.append( curPlane ); if ( !sharedAxes->contains( curSearchedAxis ) ) sharedAxes->append( curSearchedAxis ); } } } } return result; } /** * this method determines the needed layout of the graph * taking care of the sharing problematic * its NOT allowed to have a diagram that shares * more than one axis in the same direction */ QVector< LayoutGraphNode* > Chart::Private::buildPlaneLayoutGraph() { QHash< AbstractCoordinatePlane*, LayoutGraphNode* > planeNodeMapping; QVector< LayoutGraphNode* > allNodes; // create all nodes and a mapping between plane and nodes Q_FOREACH( AbstractCoordinatePlane* curPlane, coordinatePlanes ) { if ( curPlane->diagram() ) { allNodes.append( new LayoutGraphNode ); allNodes[ allNodes.size() - 1 ]->diagramPlane = curPlane; allNodes[ allNodes.size() - 1 ]->priority = allNodes.size(); checkExistingAxes( allNodes[ allNodes.size() - 1 ] ); planeNodeMapping[ curPlane ] = allNodes[ allNodes.size() - 1 ]; } } // build the graph connections Q_FOREACH( LayoutGraphNode* curNode, allNodes ) { QVector< CartesianAxis* > sharedAxes; CoordinatePlaneList xSharedPlanes = findSharingAxisDiagrams( curNode->diagramPlane, coordinatePlanes, Abscissa, &sharedAxes ); Q_ASSERT( sharedAxes.size() < 2 ); // TODO duplicated code make a method out of it if ( sharedAxes.size() == 1 && xSharedPlanes.size() > 1 ) { //xSharedPlanes.removeAll( sharedAxes.first()->diagram()->coordinatePlane() ); //std::sort( xSharedPlanes.begin(), xSharedPlanes.end(), PriorityComparator( planeNodeMapping ) ); for ( int i = 0; i < xSharedPlanes.size() - 1; ++i ) { LayoutGraphNode *tmpNode = planeNodeMapping[ xSharedPlanes[ i ] ]; Q_ASSERT( tmpNode ); LayoutGraphNode *tmpNode2 = planeNodeMapping[ xSharedPlanes[ i + 1 ] ]; Q_ASSERT( tmpNode2 ); tmpNode->bottomSuccesor = tmpNode2; } // if ( sharedAxes.first()->diagram() && sharedAxes.first()->diagram()->coordinatePlane() ) // { // LayoutGraphNode *lastNode = planeNodeMapping[ xSharedPlanes.last() ]; // Q_ASSERT( lastNode ); // Q_ASSERT( sharedAxes.first()->diagram()->coordinatePlane() ); // LayoutGraphNode *ownerNode = planeNodeMapping[ sharedAxes.first()->diagram()->coordinatePlane() ]; // Q_ASSERT( ownerNode ); // lastNode->bottomSuccesor = ownerNode; // } //merge AxisInformation, needs a two pass run LayoutGraphNode axisInfoNode; for ( int count = 0; count < 2; ++count ) { for ( int i = 0; i < xSharedPlanes.size(); ++i ) { mergeNodeAxisInformation( &axisInfoNode, planeNodeMapping[ xSharedPlanes[ i ] ] ); } } } sharedAxes.clear(); CoordinatePlaneList ySharedPlanes = findSharingAxisDiagrams( curNode->diagramPlane, coordinatePlanes, Ordinate, &sharedAxes ); Q_ASSERT( sharedAxes.size() < 2 ); if ( sharedAxes.size() == 1 && ySharedPlanes.size() > 1 ) { //ySharedPlanes.removeAll( sharedAxes.first()->diagram()->coordinatePlane() ); //std::sort( ySharedPlanes.begin(), ySharedPlanes.end(), PriorityComparator( planeNodeMapping ) ); for ( int i = 0; i < ySharedPlanes.size() - 1; ++i ) { LayoutGraphNode *tmpNode = planeNodeMapping[ ySharedPlanes[ i ] ]; Q_ASSERT( tmpNode ); LayoutGraphNode *tmpNode2 = planeNodeMapping[ ySharedPlanes[ i + 1 ] ]; Q_ASSERT( tmpNode2 ); tmpNode->leftSuccesor = tmpNode2; } // if ( sharedAxes.first()->diagram() && sharedAxes.first()->diagram()->coordinatePlane() ) // { // LayoutGraphNode *lastNode = planeNodeMapping[ ySharedPlanes.last() ]; // Q_ASSERT( lastNode ); // Q_ASSERT( sharedAxes.first()->diagram()->coordinatePlane() ); // LayoutGraphNode *ownerNode = planeNodeMapping[ sharedAxes.first()->diagram()->coordinatePlane() ]; // Q_ASSERT( ownerNode ); // lastNode->bottomSuccesor = ownerNode; // } //merge AxisInformation, needs a two pass run LayoutGraphNode axisInfoNode; for ( int count = 0; count < 2; ++count ) { for ( int i = 0; i < ySharedPlanes.size(); ++i ) { mergeNodeAxisInformation( &axisInfoNode, planeNodeMapping[ ySharedPlanes[ i ] ] ); } } } sharedAxes.clear(); if ( curNode->diagramPlane->referenceCoordinatePlane() ) curNode->sharedSuccesor = planeNodeMapping[ curNode->diagramPlane->referenceCoordinatePlane() ]; } return allNodes; } QHash Chart::Private::buildPlaneLayoutInfos() { /* There are two ways in which planes can be caused to interact in * where they are put layouting wise: The first is the reference plane. If * such a reference plane is set, on a plane, it will use the same cell in the * layout as that one. In addition to this, planes can share an axis. In that case * they will be laid out in relation to each other as suggested by the position * of the axis. If, for example Plane1 and Plane2 share an axis at position Left, * that will result in the layout: Axis Plane1 Plane 2, vertically. If Plane1 * also happens to be Plane2's referece plane, both planes are drawn over each * other. The reference plane concept allows two planes to share the same space * even if neither has any axis, and in case there are shared axis, it is used * to decided, whether the planes should be painted on top of each other or * laid out vertically or horizontally next to each other. */ QHash axisInfos; QHash planeInfos; Q_FOREACH(AbstractCoordinatePlane* plane, coordinatePlanes ) { PlaneInfo p; // first check if we share space with another plane p.referencePlane = plane->referenceCoordinatePlane(); planeInfos.insert( plane, p ); Q_FOREACH( AbstractDiagram* abstractDiagram, plane->diagrams() ) { AbstractCartesianDiagram* diagram = qobject_cast ( abstractDiagram ); if ( !diagram ) { continue; } Q_FOREACH( CartesianAxis* axis, diagram->axes() ) { if ( !axisInfos.contains( axis ) ) { /* If this is the first time we see this axis, add it, with the * current plane. The first plane added to the chart that has * the axis associated with it thus "owns" it, and decides about * layout. */ AxisInfo i; i.plane = plane; axisInfos.insert( axis, i ); } else { AxisInfo i = axisInfos[axis]; if ( i.plane == plane ) { continue; // we don't want duplicates, only shared } /* The user expects diagrams to be added on top, and to the right * so that horizontally we need to move the new diagram, vertically * the reference one. */ PlaneInfo pi = planeInfos[plane]; // plane-to-plane linking overrides linking via axes if ( !pi.referencePlane ) { // we're not the first plane to see this axis, mark us as a slave pi.referencePlane = i.plane; if ( axis->position() == CartesianAxis::Left || axis->position() == CartesianAxis::Right ) { pi.horizontalOffset += 1; } planeInfos[plane] = pi; pi = planeInfos[i.plane]; if ( axis->position() == CartesianAxis::Top || axis->position() == CartesianAxis::Bottom ) { pi.verticalOffset += 1; } planeInfos[i.plane] = pi; } } } } // Create a new grid layout for each plane that has no reference. p = planeInfos[plane]; if ( p.referencePlane == 0 ) { p.gridLayout = new QGridLayout(); p.gridLayout->setMargin( 0 ); planeInfos[plane] = p; } } return planeInfos; } void Chart::Private::slotLayoutPlanes() { /*TODO make sure this is really needed */ const QBoxLayout::Direction oldPlanesDirection = planesLayout ? planesLayout->direction() : QBoxLayout::TopToBottom; if ( planesLayout && dataAndLegendLayout ) dataAndLegendLayout->removeItem( planesLayout ); const bool hadPlanesLayout = planesLayout != 0; int left, top, right, bottom; if ( hadPlanesLayout ) planesLayout->getContentsMargins(&left, &top, &right, &bottom); Q_FOREACH( AbstractLayoutItem* plane, planeLayoutItems ) { plane->removeFromParentLayout(); } //TODO they should get a correct parent, but for now it works Q_FOREACH( AbstractLayoutItem* plane, planeLayoutItems ) { if ( dynamic_cast< AutoSpacerLayoutItem* >( plane ) ) delete plane; } planeLayoutItems.clear(); delete planesLayout; //hint: The direction is configurable by the user now, as // we are using a QBoxLayout rather than a QVBoxLayout. (khz, 2007/04/25) planesLayout = new QBoxLayout( oldPlanesDirection ); isPlanesLayoutDirty = true; // here we create the layouts; we need to "run" them before painting if ( useNewLayoutSystem ) { gridPlaneLayout = new QGridLayout; planesLayout->addLayout( gridPlaneLayout ); if (hadPlanesLayout) planesLayout->setContentsMargins(left, top, right, bottom); planesLayout->setObjectName( QString::fromLatin1( "planesLayout" ) ); /* First go through all planes and all axes and figure out whether the planes * need to coordinate. If they do, they share a grid layout, if not, each * get their own. See buildPlaneLayoutInfos() for more details. */ QVector< LayoutGraphNode* > vals = buildPlaneLayoutGraph(); //qDebug() << Q_FUNC_INFO << "GraphNodes" << vals.size(); QVector< LayoutGraphNode* > connectedComponents = getPrioritySortedConnectedComponents( vals ); //qDebug() << Q_FUNC_INFO << "SubGraphs" << connectedComponents.size(); int row = 0; int col = 0; QSet< CartesianAxis* > laidOutAxes; for ( int i = 0; i < connectedComponents.size(); ++i ) { LayoutGraphNode *curComponent = connectedComponents[ i ]; for ( LayoutGraphNode *curRowComponent = curComponent; curRowComponent; curRowComponent = curRowComponent->bottomSuccesor ) { col = 0; for ( LayoutGraphNode *curColComponent = curRowComponent; curColComponent; curColComponent = curColComponent->leftSuccesor ) { Q_ASSERT( curColComponent->diagramPlane->diagrams().size() == 1 ); Q_FOREACH( AbstractDiagram* diagram, curColComponent->diagramPlane->diagrams() ) { const int planeRowOffset = 1;//curColComponent->topAxesLayout ? 1 : 0; const int planeColOffset = 1;//curColComponent->leftAxesLayout ? 1 : 0; //qDebug() << Q_FUNC_INFO << row << col << planeRowOffset << planeColOffset; //qDebug() << Q_FUNC_INFO << row + planeRowOffset << col + planeColOffset; planeLayoutItems << curColComponent->diagramPlane; AbstractCartesianDiagram *cartDiag = qobject_cast< AbstractCartesianDiagram* >( diagram ); if ( cartDiag ) { gridPlaneLayout->addItem( curColComponent->diagramPlane, row + planeRowOffset, col + planeColOffset, 2, 2 ); curColComponent->diagramPlane->setParentLayout( gridPlaneLayout ); QHBoxLayout *leftLayout = 0; QHBoxLayout *rightLayout = 0; QVBoxLayout *topLayout = 0; QVBoxLayout *bottomLayout = 0; if ( curComponent->sharedSuccesor ) { gridPlaneLayout->addItem( curColComponent->sharedSuccesor->diagramPlane, row + planeRowOffset, col + planeColOffset, 2, 2 ); curColComponent->sharedSuccesor->diagramPlane->setParentLayout( gridPlaneLayout ); planeLayoutItems << curColComponent->sharedSuccesor->diagramPlane; } Q_FOREACH( CartesianAxis* axis, cartDiag->axes() ) { if ( axis->isAbscissa() ) { if ( curColComponent->bottomSuccesor ) continue; } if ( laidOutAxes.contains( axis ) ) continue; // if ( axis->diagram() != diagram ) // continue; switch ( axis->position() ) { case( CartesianAxis::Top ): if ( !topLayout ) topLayout = new QVBoxLayout; topLayout->addItem( axis ); axis->setParentLayout( topLayout ); break; case( CartesianAxis::Bottom ): if ( !bottomLayout ) bottomLayout = new QVBoxLayout; bottomLayout->addItem( axis ); axis->setParentLayout( bottomLayout ); break; case( CartesianAxis::Left ): if ( !leftLayout ) leftLayout = new QHBoxLayout; leftLayout->addItem( axis ); axis->setParentLayout( leftLayout ); break; case( CartesianAxis::Right ): if ( !rightLayout ) { rightLayout = new QHBoxLayout; } rightLayout->addItem( axis ); axis->setParentLayout( rightLayout ); break; } planeLayoutItems << axis; laidOutAxes.insert( axis ); } if ( leftLayout ) gridPlaneLayout->addLayout( leftLayout, row + planeRowOffset, col, 2, 1, Qt::AlignRight | Qt::AlignVCenter ); if ( rightLayout ) gridPlaneLayout->addLayout( rightLayout, row, col + planeColOffset + 2, 2, 1, Qt::AlignLeft | Qt::AlignVCenter ); if ( topLayout ) gridPlaneLayout->addLayout( topLayout, row, col + planeColOffset, 1, 2, Qt::AlignBottom | Qt::AlignHCenter ); if ( bottomLayout ) gridPlaneLayout->addLayout( bottomLayout, row + planeRowOffset + 2, col + planeColOffset, 1, 2, Qt::AlignTop | Qt::AlignHCenter ); } else { gridPlaneLayout->addItem( curColComponent->diagramPlane, row, col, 4, 4 ); curColComponent->diagramPlane->setParentLayout( gridPlaneLayout ); } col += planeColOffset + 2 + ( 1 ); } } int axisOffset = 2;//curRowComponent->topAxesLayout ? 1 : 0; //axisOffset += curRowComponent->bottomAxesLayout ? 1 : 0; const int rowOffset = axisOffset + 2; row += rowOffset; } // if ( planesLayout->direction() == QBoxLayout::TopToBottom ) // ++row; // else // ++col; } qDeleteAll( vals ); // re-add our grid(s) to the chart's layout if ( dataAndLegendLayout ) { dataAndLegendLayout->addLayout( planesLayout, 1, 1 ); dataAndLegendLayout->setRowStretch( 1, 1000 ); dataAndLegendLayout->setColumnStretch( 1, 1000 ); } slotResizePlanes(); #ifdef NEW_LAYOUT_DEBUG for ( int i = 0; i < gridPlaneLayout->rowCount(); ++i ) { for ( int j = 0; j < gridPlaneLayout->columnCount(); ++j ) { if ( gridPlaneLayout->itemAtPosition( i, j ) ) qDebug() << Q_FUNC_INFO << "item at" << i << j << gridPlaneLayout->itemAtPosition( i, j )->geometry(); else qDebug() << Q_FUNC_INFO << "item at" << i << j << "no item present"; } } //qDebug() << Q_FUNC_INFO << "Relayout ended"; #endif } else { if ( hadPlanesLayout ) { planesLayout->setContentsMargins( left, top, right, bottom ); } planesLayout->setMargin( 0 ); planesLayout->setSpacing( 0 ); planesLayout->setObjectName( QString::fromLatin1( "planesLayout" ) ); /* First go through all planes and all axes and figure out whether the planes * need to coordinate. If they do, they share a grid layout, if not, each * gets their own. See buildPlaneLayoutInfos() for more details. */ QHash planeInfos = buildPlaneLayoutInfos(); QHash axisInfos; Q_FOREACH( AbstractCoordinatePlane* plane, coordinatePlanes ) { Q_ASSERT( planeInfos.contains(plane) ); PlaneInfo& pi = planeInfos[ plane ]; const int column = pi.horizontalOffset; const int row = pi.verticalOffset; //qDebug() << "processing plane at column" << column << "and row" << row; QGridLayout *planeLayout = pi.gridLayout; if ( !planeLayout ) { PlaneInfo& refPi = pi; // if this plane is sharing an axis with another one, recursively check for the original plane and use // the grid of that as planeLayout. while ( !planeLayout && refPi.referencePlane ) { refPi = planeInfos[refPi.referencePlane]; planeLayout = refPi.gridLayout; } Q_ASSERT_X( planeLayout, "Chart::Private::slotLayoutPlanes()", "Invalid reference plane. Please check that the reference plane has been added to the Chart." ); } else { planesLayout->addLayout( planeLayout ); } /* Put the plane in the center of the layout. If this is our own, that's * the middle of the layout, if we are sharing, it's a cell in the center * column of the shared grid. */ planeLayoutItems << plane; plane->setParentLayout( planeLayout ); - planeLayout->addItem( plane, row, column, 1, 1, 0 ); + planeLayout->addItem( plane, row, column, 1, 1 ); //qDebug() << "Chart slotLayoutPlanes() calls planeLayout->addItem("<< row << column << ")"; planeLayout->setRowStretch( row, 2 ); planeLayout->setColumnStretch( column, 2 ); Q_FOREACH( AbstractDiagram* abstractDiagram, plane->diagrams() ) { AbstractCartesianDiagram* diagram = qobject_cast< AbstractCartesianDiagram* >( abstractDiagram ); if ( !diagram ) { continue; // FIXME what about polar ? } if ( pi.referencePlane != 0 ) { pi.topAxesLayout = planeInfos[ pi.referencePlane ].topAxesLayout; pi.bottomAxesLayout = planeInfos[ pi.referencePlane ].bottomAxesLayout; pi.leftAxesLayout = planeInfos[ pi.referencePlane ].leftAxesLayout; pi.rightAxesLayout = planeInfos[ pi.referencePlane ].rightAxesLayout; } // collect all axes of a kind into sublayouts if ( pi.topAxesLayout == 0 ) { pi.topAxesLayout = new QVBoxLayout; pi.topAxesLayout->setMargin( 0 ); pi.topAxesLayout->setObjectName( QString::fromLatin1( "topAxesLayout" ) ); } if ( pi.bottomAxesLayout == 0 ) { pi.bottomAxesLayout = new QVBoxLayout; pi.bottomAxesLayout->setMargin( 0 ); pi.bottomAxesLayout->setObjectName( QString::fromLatin1( "bottomAxesLayout" ) ); } if ( pi.leftAxesLayout == 0 ) { pi.leftAxesLayout = new QHBoxLayout; pi.leftAxesLayout->setMargin( 0 ); pi.leftAxesLayout->setObjectName( QString::fromLatin1( "leftAxesLayout" ) ); } if ( pi.rightAxesLayout == 0 ) { pi.rightAxesLayout = new QHBoxLayout; pi.rightAxesLayout->setMargin( 0 ); pi.rightAxesLayout->setObjectName( QString::fromLatin1( "rightAxesLayout" ) ); } if ( pi.referencePlane != 0 ) { planeInfos[ pi.referencePlane ].topAxesLayout = pi.topAxesLayout; planeInfos[ pi.referencePlane ].bottomAxesLayout = pi.bottomAxesLayout; planeInfos[ pi.referencePlane ].leftAxesLayout = pi.leftAxesLayout; planeInfos[ pi.referencePlane ].rightAxesLayout = pi.rightAxesLayout; } //pi.leftAxesLayout->setSizeConstraint( QLayout::SetFixedSize ); Q_FOREACH( CartesianAxis* axis, diagram->axes() ) { if ( axisInfos.contains( axis ) ) { continue; // already laid out this one } Q_ASSERT ( axis ); axis->setCachedSizeDirty(); //qDebug() << "--------------- axis added to planeLayoutItems -----------------"; planeLayoutItems << axis; switch ( axis->position() ) { case CartesianAxis::Top: axis->setParentLayout( pi.topAxesLayout ); pi.topAxesLayout->addItem( axis ); break; case CartesianAxis::Bottom: axis->setParentLayout( pi.bottomAxesLayout ); pi.bottomAxesLayout->addItem( axis ); break; case CartesianAxis::Left: axis->setParentLayout( pi.leftAxesLayout ); pi.leftAxesLayout->addItem( axis ); break; case CartesianAxis::Right: axis->setParentLayout( pi.rightAxesLayout ); pi.rightAxesLayout->addItem( axis ); break; default: Q_ASSERT_X( false, "Chart::paintEvent", "unknown axis position" ); break; }; axisInfos.insert( axis, AxisInfo() ); } /* Put each stack of axes-layouts in the cells surrounding the * associated plane. We are laying out in the oder the planes * were added, and the first one gets to lay out shared axes. * Private axes go here as well, of course. */ if ( !pi.topAxesLayout->parent() ) { planeLayout->addLayout( pi.topAxesLayout, row - 1, column ); } if ( !pi.bottomAxesLayout->parent() ) { planeLayout->addLayout( pi.bottomAxesLayout, row + 1, column ); } if ( !pi.leftAxesLayout->parent() ) { planeLayout->addLayout( pi.leftAxesLayout, row, column - 1 ); } if ( !pi.rightAxesLayout->parent() ) { planeLayout->addLayout( pi.rightAxesLayout,row, column + 1 ); } } // use up to four auto-spacer items in the corners around the diagrams: #define ADD_AUTO_SPACER_IF_NEEDED( \ spacerRow, spacerColumn, hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ) \ { \ if ( hLayout || vLayout ) { \ AutoSpacerLayoutItem * spacer \ = new AutoSpacerLayoutItem( hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ); \ planeLayout->addItem( spacer, spacerRow, spacerColumn, 1, 1 ); \ spacer->setParentLayout( planeLayout ); \ planeLayoutItems << spacer; \ } \ } if ( plane->isCornerSpacersEnabled() ) { ADD_AUTO_SPACER_IF_NEEDED( row - 1, column - 1, false, pi.leftAxesLayout, false, pi.topAxesLayout ) ADD_AUTO_SPACER_IF_NEEDED( row + 1, column - 1, true, pi.leftAxesLayout, false, pi.bottomAxesLayout ) ADD_AUTO_SPACER_IF_NEEDED( row - 1, column + 1, false, pi.rightAxesLayout, true, pi.topAxesLayout ) ADD_AUTO_SPACER_IF_NEEDED( row + 1, column + 1, true, pi.rightAxesLayout, true, pi.bottomAxesLayout ) } } // re-add our grid(s) to the chart's layout if ( dataAndLegendLayout ) { dataAndLegendLayout->addLayout( planesLayout, 1, 1 ); dataAndLegendLayout->setRowStretch( 1, 1000 ); dataAndLegendLayout->setColumnStretch( 1, 1000 ); } slotResizePlanes(); } } void Chart::Private::createLayouts() { // The toplevel layout provides the left and right global margins layout = new QHBoxLayout( chart ); layout->setMargin( 0 ); layout->setObjectName( QString::fromLatin1( "Chart::Private::layout" ) ); layout->addSpacing( globalLeadingLeft ); leftOuterSpacer = layout->itemAt( layout->count() - 1 )->spacerItem(); // The vLayout provides top and bottom global margins and lays // out headers, footers and the diagram area. vLayout = new QVBoxLayout(); vLayout->setMargin( 0 ); vLayout->setObjectName( QString::fromLatin1( "vLayout" ) ); layout->addLayout( vLayout, 1000 ); layout->addSpacing( globalLeadingRight ); rightOuterSpacer = layout->itemAt( layout->count() - 1 )->spacerItem(); // 1. the spacing above the header area vLayout->addSpacing( globalLeadingTop ); topOuterSpacer = vLayout->itemAt( vLayout->count() - 1 )->spacerItem(); // 2. the header area headerLayout = new QGridLayout(); headerLayout->setMargin( 0 ); vLayout->addLayout( headerLayout ); // 3. the area containing coordinate planes, axes, and legends dataAndLegendLayout = new QGridLayout(); dataAndLegendLayout->setMargin( 0 ); dataAndLegendLayout->setObjectName( QString::fromLatin1( "dataAndLegendLayout" ) ); vLayout->addLayout( dataAndLegendLayout, 1000 ); // 4. the footer area footerLayout = new QGridLayout(); footerLayout->setMargin( 0 ); footerLayout->setObjectName( QString::fromLatin1( "footerLayout" ) ); vLayout->addLayout( footerLayout ); // 5. Prepare the header / footer layout cells: // Each of the 9 header cells (the 9 footer cells) // contain their own QVBoxLayout // since there can be more than one header (footer) per cell. for ( int row = 0; row < 3; ++row ) { for ( int column = 0; column < 3; ++ column ) { const Qt::Alignment align = s_gridAlignments[ row ][ column ]; for ( int headOrFoot = 0; headOrFoot < 2; headOrFoot++ ) { QVBoxLayout* innerLayout = new QVBoxLayout(); innerLayout->setMargin( 0 ); innerLayout->setAlignment( align ); innerHdFtLayouts[ headOrFoot ][ row ][ column ] = innerLayout; QGridLayout* outerLayout = headOrFoot == 0 ? headerLayout : footerLayout; outerLayout->addLayout( innerLayout, row, column, align ); } } } // 6. the spacing below the footer area vLayout->addSpacing( globalLeadingBottom ); bottomOuterSpacer = vLayout->itemAt( vLayout->count() - 1 )->spacerItem(); // the data+axes area dataAndLegendLayout->addLayout( planesLayout, 1, 1 ); dataAndLegendLayout->setRowStretch( 1, 1 ); dataAndLegendLayout->setColumnStretch( 1, 1 ); } void Chart::Private::slotResizePlanes() { if ( !dataAndLegendLayout ) { return; } if ( !overrideSize.isValid() ) { // activate() takes the size from the layout's parent QWidget, which is not updated when overrideSize // is set. So don't let the layout grab the wrong size in that case. // When overrideSize *is* set, we call layout->setGeometry() in paint( QPainter*, const QRect& ), // which also "activates" the layout in the sense that it distributes space internally. layout->activate(); } // Adapt diagram drawing to the new size Q_FOREACH (AbstractCoordinatePlane* plane, coordinatePlanes ) { plane->layoutDiagrams(); } } void Chart::Private::updateDirtyLayouts() { if ( isPlanesLayoutDirty ) { Q_FOREACH ( AbstractCoordinatePlane* p, coordinatePlanes ) { p->setGridNeedsRecalculate(); p->layoutPlanes(); p->layoutDiagrams(); } } if ( isPlanesLayoutDirty || isFloatingLegendsLayoutDirty ) { chart->reLayoutFloatingLegends(); } isPlanesLayoutDirty = false; isFloatingLegendsLayoutDirty = false; } void Chart::Private::reapplyInternalLayouts() { QRect geo = layout->geometry(); invalidateLayoutTree( layout ); layout->setGeometry( geo ); slotResizePlanes(); } void Chart::Private::paintAll( QPainter* painter ) { updateDirtyLayouts(); QRect rect( QPoint( 0, 0 ), overrideSize.isValid() ? overrideSize : chart->size() ); //qDebug() << this<<"::paintAll() uses layout size" << currentLayoutSize; // Paint the background (if any) AbstractAreaBase::paintBackgroundAttributes( *painter, rect, backgroundAttributes ); // Paint the frame (if any) AbstractAreaBase::paintFrameAttributes( *painter, rect, frameAttributes ); chart->reLayoutFloatingLegends(); Q_FOREACH( AbstractLayoutItem* planeLayoutItem, planeLayoutItems ) { planeLayoutItem->paintAll( *painter ); } Q_FOREACH( TextArea* textLayoutItem, textLayoutItems ) { textLayoutItem->paintAll( *painter ); } Q_FOREACH( Legend *legend, legends ) { const bool hidden = legend->isHidden() && legend->testAttribute( Qt::WA_WState_ExplicitShowHide ); if ( !hidden ) { //qDebug() << "painting legend at " << legend->geometry(); legend->paintIntoRect( *painter, legend->geometry() ); } } } // ******** Chart interface implementation *********** #define d d_func() Chart::Chart ( QWidget* parent ) : QWidget ( parent ) , _d( new Private( this ) ) { #if defined KDAB_EVAL EvalDialog::checkEvalLicense( "KD Chart" ); #endif FrameAttributes frameAttrs; // no frame per default... // frameAttrs.setVisible( true ); frameAttrs.setPen( QPen( Qt::black ) ); frameAttrs.setPadding( 1 ); setFrameAttributes( frameAttrs ); addCoordinatePlane( new CartesianCoordinatePlane ( this ) ); d->createLayouts(); } Chart::~Chart() { delete d; } void Chart::setFrameAttributes( const FrameAttributes &a ) { d->frameAttributes = a; } FrameAttributes Chart::frameAttributes() const { return d->frameAttributes; } void Chart::setBackgroundAttributes( const BackgroundAttributes &a ) { d->backgroundAttributes = a; } BackgroundAttributes Chart::backgroundAttributes() const { return d->backgroundAttributes; } //TODO KChart 3.0; change QLayout into QBoxLayout::Direction void Chart::setCoordinatePlaneLayout( QLayout * layout ) { if (layout == d->planesLayout) return; if (d->planesLayout) { // detach all QLayoutItem's the previous planesLayout has cause // otherwise deleting the planesLayout would delete them too. for(int i = d->planesLayout->count() - 1; i >= 0; --i) { d->planesLayout->takeAt(i); } delete d->planesLayout; } d->planesLayout = qobject_cast( layout ); d->slotLayoutPlanes(); } QLayout* Chart::coordinatePlaneLayout() { return d->planesLayout; } AbstractCoordinatePlane* Chart::coordinatePlane() { if ( d->coordinatePlanes.isEmpty() ) { qWarning() << "Chart::coordinatePlane: warning: no coordinate plane defined."; return 0; } else { return d->coordinatePlanes.first(); } } CoordinatePlaneList Chart::coordinatePlanes() { return d->coordinatePlanes; } void Chart::addCoordinatePlane( AbstractCoordinatePlane* plane ) { // Append insertCoordinatePlane( d->coordinatePlanes.count(), plane ); } void Chart::insertCoordinatePlane( int index, AbstractCoordinatePlane* plane ) { if ( index < 0 || index > d->coordinatePlanes.count() ) { return; } connect( plane, SIGNAL(destroyedCoordinatePlane(AbstractCoordinatePlane*)), d, SLOT(slotUnregisterDestroyedPlane(AbstractCoordinatePlane*)) ); connect( plane, SIGNAL(needUpdate()), this, SLOT(update()) ); connect( plane, SIGNAL(needRelayout()), d, SLOT(slotResizePlanes()) ) ; connect( plane, SIGNAL(needLayoutPlanes()), d, SLOT(slotLayoutPlanes()) ) ; connect( plane, SIGNAL(propertiesChanged()),this, SIGNAL(propertiesChanged()) ); d->coordinatePlanes.insert( index, plane ); plane->setParent( this ); d->slotLayoutPlanes(); } void Chart::replaceCoordinatePlane( AbstractCoordinatePlane* plane, AbstractCoordinatePlane* oldPlane_ ) { if ( plane && oldPlane_ != plane ) { AbstractCoordinatePlane* oldPlane = oldPlane_; if ( d->coordinatePlanes.count() ) { if ( ! oldPlane ) { oldPlane = d->coordinatePlanes.first(); if ( oldPlane == plane ) return; } takeCoordinatePlane( oldPlane ); } delete oldPlane; addCoordinatePlane( plane ); } } void Chart::takeCoordinatePlane( AbstractCoordinatePlane* plane ) { const int idx = d->coordinatePlanes.indexOf( plane ); if ( idx != -1 ) { d->coordinatePlanes.takeAt( idx ); disconnect( plane, 0, d, 0 ); disconnect( plane, 0, this, 0 ); plane->removeFromParentLayout(); plane->setParent( 0 ); d->mouseClickedPlanes.removeAll(plane); } d->slotLayoutPlanes(); // Need to emit the signal: In case somebody has connected the signal // to her own slot for e.g. calling update() on a widget containing the chart. emit propertiesChanged(); } void Chart::setGlobalLeading( int left, int top, int right, int bottom ) { setGlobalLeadingLeft( left ); setGlobalLeadingTop( top ); setGlobalLeadingRight( right ); setGlobalLeadingBottom( bottom ); } void Chart::setGlobalLeadingLeft( int leading ) { d->globalLeadingLeft = leading; d->leftOuterSpacer->changeSize( leading, 0, QSizePolicy::Fixed, QSizePolicy::Minimum ); d->reapplyInternalLayouts(); } int Chart::globalLeadingLeft() const { return d->globalLeadingLeft; } void Chart::setGlobalLeadingTop( int leading ) { d->globalLeadingTop = leading; d->topOuterSpacer->changeSize( 0, leading, QSizePolicy::Minimum, QSizePolicy::Fixed ); d->reapplyInternalLayouts(); } int Chart::globalLeadingTop() const { return d->globalLeadingTop; } void Chart::setGlobalLeadingRight( int leading ) { d->globalLeadingRight = leading; d->rightOuterSpacer->changeSize( leading, 0, QSizePolicy::Fixed, QSizePolicy::Minimum ); d->reapplyInternalLayouts(); } int Chart::globalLeadingRight() const { return d->globalLeadingRight; } void Chart::setGlobalLeadingBottom( int leading ) { d->globalLeadingBottom = leading; d->bottomOuterSpacer->changeSize( 0, leading, QSizePolicy::Minimum, QSizePolicy::Fixed ); d->reapplyInternalLayouts(); } int Chart::globalLeadingBottom() const { return d->globalLeadingBottom; } void Chart::paint( QPainter* painter, const QRect& target ) { if ( target.isEmpty() || !painter ) { return; } QPaintDevice* prevDevice = GlobalMeasureScaling::paintDevice(); GlobalMeasureScaling::setPaintDevice( painter->device() ); // Output on a widget if ( dynamic_cast< QWidget* >( painter->device() ) != 0 ) { GlobalMeasureScaling::setFactors( qreal( target.width() ) / qreal( geometry().size().width() ), qreal( target.height() ) / qreal( geometry().size().height() ) ); } else { // Output onto a QPixmap PrintingParameters::setScaleFactor( qreal( painter->device()->logicalDpiX() ) / qreal( logicalDpiX() ) ); const qreal resX = qreal( logicalDpiX() ) / qreal( painter->device()->logicalDpiX() ); const qreal resY = qreal( logicalDpiY() ) / qreal( painter->device()->logicalDpiY() ); GlobalMeasureScaling::setFactors( qreal( target.width() ) / qreal( geometry().size().width() ) * resX, qreal( target.height() ) / qreal( geometry().size().height() ) * resY ); } const QPoint translation = target.topLeft(); painter->translate( translation ); // the following layout logic has the disadvantage that repeatedly calling this method can // cause a relayout every time, but since this method's main use seems to be printing, the // gratuitous relayouts shouldn't be much of a performance problem. const bool differentSize = target.size() != size(); QRect oldGeometry; if ( differentSize ) { oldGeometry = geometry(); d->isPlanesLayoutDirty = true; d->isFloatingLegendsLayoutDirty = true; invalidateLayoutTree( d->dataAndLegendLayout ); d->dataAndLegendLayout->setGeometry( QRect( QPoint(), target.size() ) ); } d->overrideSize = target.size(); d->paintAll( painter ); d->overrideSize = QSize(); if ( differentSize ) { invalidateLayoutTree( d->dataAndLegendLayout ); d->dataAndLegendLayout->setGeometry( oldGeometry ); d->isPlanesLayoutDirty = true; d->isFloatingLegendsLayoutDirty = true; } // for debugging // painter->setPen( QPen( Qt::blue, 8 ) ); // painter->drawRect( target ); painter->translate( -translation.x(), -translation.y() ); GlobalMeasureScaling::instance()->resetFactors(); PrintingParameters::resetScaleFactor(); GlobalMeasureScaling::setPaintDevice( prevDevice ); } void Chart::resizeEvent ( QResizeEvent* event ) { d->isPlanesLayoutDirty = true; d->isFloatingLegendsLayoutDirty = true; QWidget::resizeEvent( event ); } void Chart::reLayoutFloatingLegends() { Q_FOREACH( Legend *legend, d->legends ) { const bool hidden = legend->isHidden() && legend->testAttribute( Qt::WA_WState_ExplicitShowHide ); if ( legend->position().isFloating() && !hidden ) { // resize the legend const QSize legendSize( legend->sizeHint() ); legend->setGeometry( QRect( legend->geometry().topLeft(), legendSize ) ); // find the legends corner point (reference point plus any paddings) const RelativePosition relPos( legend->floatingPosition() ); QPointF pt( relPos.calculatedPoint( size() ) ); //qDebug() << pt; // calculate the legend's top left point const Qt::Alignment alignTopLeft = Qt::AlignBottom | Qt::AlignLeft; if ( (relPos.alignment() & alignTopLeft) != alignTopLeft ) { if ( relPos.alignment() & Qt::AlignRight ) pt.rx() -= legendSize.width(); else if ( relPos.alignment() & Qt::AlignHCenter ) pt.rx() -= 0.5 * legendSize.width(); if ( relPos.alignment() & Qt::AlignBottom ) pt.ry() -= legendSize.height(); else if ( relPos.alignment() & Qt::AlignVCenter ) pt.ry() -= 0.5 * legendSize.height(); } //qDebug() << pt << endl; legend->move( static_cast(pt.x()), static_cast(pt.y()) ); } } } void Chart::paintEvent( QPaintEvent* ) { QPainter painter( this ); d->paintAll( &painter ); emit finishedDrawing(); } void Chart::addHeaderFooter( HeaderFooter* hf ) { Q_ASSERT( hf->type() == HeaderFooter::Header || hf->type() == HeaderFooter::Footer ); int row; int column; getRowAndColumnForPosition( hf->position().value(), &row, &column ); if ( row == -1 ) { qWarning( "Unknown header/footer position" ); return; } d->headerFooters.append( hf ); d->textLayoutItems.append( hf ); connect( hf, SIGNAL(destroyedHeaderFooter(HeaderFooter*)), d, SLOT(slotUnregisterDestroyedHeaderFooter(HeaderFooter*)) ); connect( hf, SIGNAL(positionChanged(HeaderFooter*)), d, SLOT(slotHeaderFooterPositionChanged(HeaderFooter*)) ); // set the text attributes (why?) TextAttributes textAttrs( hf->textAttributes() ); Measure measure( textAttrs.fontSize() ); measure.setRelativeMode( this, KChartEnums::MeasureOrientationMinimum ); measure.setValue( 20 ); textAttrs.setFontSize( measure ); hf->setTextAttributes( textAttrs ); // add it to the appropriate layout int innerLayoutIdx = hf->type() == HeaderFooter::Header ? 0 : 1; QVBoxLayout* headerFooterLayout = d->innerHdFtLayouts[ innerLayoutIdx ][ row ][ column ]; hf->setParentLayout( headerFooterLayout ); hf->setAlignment( s_gridAlignments[ row ][ column ] ); headerFooterLayout->addItem( hf ); d->slotResizePlanes(); } void Chart::replaceHeaderFooter( HeaderFooter* headerFooter, HeaderFooter* oldHeaderFooter_ ) { if ( headerFooter && oldHeaderFooter_ != headerFooter ) { HeaderFooter* oldHeaderFooter = oldHeaderFooter_; if ( d->headerFooters.count() ) { if ( ! oldHeaderFooter ) { oldHeaderFooter = d->headerFooters.first(); if ( oldHeaderFooter == headerFooter ) return; } takeHeaderFooter( oldHeaderFooter ); } delete oldHeaderFooter; addHeaderFooter( headerFooter ); } } void Chart::takeHeaderFooter( HeaderFooter* headerFooter ) { const int idx = d->headerFooters.indexOf( headerFooter ); if ( idx == -1 ) { return; } disconnect( headerFooter, SIGNAL(destroyedHeaderFooter(HeaderFooter*)), d, SLOT(slotUnregisterDestroyedHeaderFooter(HeaderFooter*)) ); d->headerFooters.takeAt( idx ); headerFooter->removeFromParentLayout(); headerFooter->setParentLayout( 0 ); d->textLayoutItems.remove( d->textLayoutItems.indexOf( headerFooter ) ); d->slotResizePlanes(); } void Chart::Private::slotHeaderFooterPositionChanged( HeaderFooter* hf ) { chart->takeHeaderFooter( hf ); chart->addHeaderFooter( hf ); } HeaderFooter* Chart::headerFooter() { if ( d->headerFooters.isEmpty() ) { return 0; } else { return d->headerFooters.first(); } } HeaderFooterList Chart::headerFooters() { return d->headerFooters; } void Chart::Private::slotLegendPositionChanged( AbstractAreaWidget* aw ) { Legend* legend = qobject_cast< Legend* >( aw ); Q_ASSERT( legend ); chart->takeLegend( legend ); chart->addLegendInternal( legend, false ); } void Chart::addLegend( Legend* legend ) { legend->show(); addLegendInternal( legend, true ); emit propertiesChanged(); } void Chart::addLegendInternal( Legend* legend, bool setMeasures ) { if ( !legend ) { return; } KChartEnums::PositionValue pos = legend->position().value(); if ( pos == KChartEnums::PositionCenter ) { qWarning( "Not showing legend because PositionCenter is not supported for legends." ); } int row; int column; getRowAndColumnForPosition( pos, &row, &column ); if ( row < 0 && pos != KChartEnums::PositionFloating ) { qWarning( "Not showing legend because of unknown legend position." ); return; } d->legends.append( legend ); legend->setParent( this ); // set text attributes (why?) if ( setMeasures ) { TextAttributes textAttrs( legend->textAttributes() ); Measure measure( textAttrs.fontSize() ); measure.setRelativeMode( this, KChartEnums::MeasureOrientationMinimum ); measure.setValue( 20 ); textAttrs.setFontSize( measure ); legend->setTextAttributes( textAttrs ); textAttrs = legend->titleTextAttributes(); measure.setRelativeMode( this, KChartEnums::MeasureOrientationMinimum ); measure.setValue( 24 ); textAttrs.setFontSize( measure ); legend->setTitleTextAttributes( textAttrs ); legend->setReferenceArea( this ); } // add it to the appropriate layout if ( pos != KChartEnums::PositionFloating ) { legend->needSizeHint(); // in each edge and corner of the outer layout, there's a grid for the different alignments that we create // on demand. we don't remove it when empty. QLayoutItem* edgeItem = d->dataAndLegendLayout->itemAtPosition( row, column ); QGridLayout* alignmentsLayout = dynamic_cast< QGridLayout* >( edgeItem ); Q_ASSERT( !edgeItem || alignmentsLayout ); // if it exists, it must be a QGridLayout if ( !alignmentsLayout ) { alignmentsLayout = new QGridLayout; d->dataAndLegendLayout->addLayout( alignmentsLayout, row, column ); alignmentsLayout->setMargin( 0 ); } // in case there are several legends in the same edge or corner with the same alignment, they are stacked // vertically using a QVBoxLayout. it is created on demand as above. row = 1; column = 1; for ( int i = 0; i < 3; i++ ) { for ( int j = 0; j < 3; j++ ) { Qt::Alignment align = s_gridAlignments[ i ][ j ]; if ( align == legend->alignment() ) { row = i; column = j; break; } } } QLayoutItem* alignmentItem = alignmentsLayout->itemAtPosition( row, column ); QVBoxLayout* sameAlignmentLayout = dynamic_cast< QVBoxLayout* >( alignmentItem ); Q_ASSERT( !alignmentItem || sameAlignmentLayout ); // if it exists, it must be a QVBoxLayout if ( !sameAlignmentLayout ) { sameAlignmentLayout = new QVBoxLayout; alignmentsLayout->addLayout( sameAlignmentLayout, row, column ); sameAlignmentLayout->setMargin( 0 ); } sameAlignmentLayout->addItem( new MyWidgetItem( legend, legend->alignment() ) ); } connect( legend, SIGNAL(destroyedLegend(Legend*)), d, SLOT(slotUnregisterDestroyedLegend(Legend*)) ); connect( legend, SIGNAL(positionChanged(AbstractAreaWidget*)), d, SLOT(slotLegendPositionChanged(AbstractAreaWidget*)) ); connect( legend, SIGNAL(propertiesChanged()), this, SIGNAL(propertiesChanged()) ); d->slotResizePlanes(); } void Chart::replaceLegend( Legend* legend, Legend* oldLegend_ ) { if ( legend && oldLegend_ != legend ) { Legend* oldLegend = oldLegend_; if ( d->legends.count() ) { if ( ! oldLegend ) { oldLegend = d->legends.first(); if ( oldLegend == legend ) return; } takeLegend( oldLegend ); } delete oldLegend; addLegend( legend ); } } void Chart::takeLegend( Legend* legend ) { const int idx = d->legends.indexOf( legend ); if ( idx == -1 ) { return; } d->legends.takeAt( idx ); disconnect( legend, 0, d, 0 ); disconnect( legend, 0, this, 0 ); // the following removes the legend from its layout and destroys its MyWidgetItem (the link to the layout) legend->setParent( 0 ); d->slotResizePlanes(); emit propertiesChanged(); } Legend* Chart::legend() { return d->legends.isEmpty() ? 0 : d->legends.first(); } LegendList Chart::legends() { return d->legends; } void Chart::mousePressEvent( QMouseEvent* event ) { const QPoint pos = mapFromGlobal( event->globalPos() ); Q_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) { if ( plane->geometry().contains( event->pos() ) && plane->diagrams().size() > 0 ) { QMouseEvent ev( QEvent::MouseButtonPress, pos, event->globalPos(), event->button(), event->buttons(), event->modifiers() ); plane->mousePressEvent( &ev ); d->mouseClickedPlanes.append( plane ); } } } void Chart::mouseDoubleClickEvent( QMouseEvent* event ) { const QPoint pos = mapFromGlobal( event->globalPos() ); Q_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) { if ( plane->geometry().contains( event->pos() ) && plane->diagrams().size() > 0 ) { QMouseEvent ev( QEvent::MouseButtonPress, pos, event->globalPos(), event->button(), event->buttons(), event->modifiers() ); plane->mouseDoubleClickEvent( &ev ); } } } void Chart::mouseMoveEvent( QMouseEvent* event ) { QSet< AbstractCoordinatePlane* > eventReceivers = QSet< AbstractCoordinatePlane* >::fromList( d->mouseClickedPlanes ); Q_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) { if ( plane->geometry().contains( event->pos() ) && plane->diagrams().size() > 0 ) { eventReceivers.insert( plane ); } } const QPoint pos = mapFromGlobal( event->globalPos() ); Q_FOREACH( AbstractCoordinatePlane* plane, eventReceivers ) { QMouseEvent ev( QEvent::MouseMove, pos, event->globalPos(), event->button(), event->buttons(), event->modifiers() ); plane->mouseMoveEvent( &ev ); } } void Chart::mouseReleaseEvent( QMouseEvent* event ) { QSet< AbstractCoordinatePlane* > eventReceivers = QSet< AbstractCoordinatePlane* >::fromList( d->mouseClickedPlanes ); Q_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) { if ( plane->geometry().contains( event->pos() ) && plane->diagrams().size() > 0 ) { eventReceivers.insert( plane ); } } const QPoint pos = mapFromGlobal( event->globalPos() ); Q_FOREACH( AbstractCoordinatePlane* plane, eventReceivers ) { QMouseEvent ev( QEvent::MouseButtonRelease, pos, event->globalPos(), event->button(), event->buttons(), event->modifiers() ); plane->mouseReleaseEvent( &ev ); } d->mouseClickedPlanes.clear(); } bool Chart::event( QEvent* event ) { if ( event->type() == QEvent::ToolTip ) { const QHelpEvent* const helpEvent = static_cast< QHelpEvent* >( event ); Q_FOREACH( const AbstractCoordinatePlane* const plane, d->coordinatePlanes ) { // iterate diagrams in reverse, so that the top-most painted diagram is // queried first for a tooltip before the diagrams behind it const ConstAbstractDiagramList& diagrams = plane->diagrams(); for (int i = diagrams.size() - 1; i >= 0; --i) { const AbstractDiagram* diagram = diagrams[i]; if (diagram->isHidden()) { continue; } const QModelIndex index = diagram->indexAt( helpEvent->pos() ); const QVariant toolTip = index.data( Qt::ToolTipRole ); if ( toolTip.isValid() ) { QPoint pos = mapFromGlobal( helpEvent->pos() ); QRect rect( pos - QPoint( 1, 1 ), QSize( 3, 3 ) ); QToolTip::showText( QCursor::pos(), toolTip.toString(), this, rect ); return true; } } } } return QWidget::event( event ); } bool Chart::useNewLayoutSystem() const { return d_func()->useNewLayoutSystem; } void Chart::setUseNewLayoutSystem( bool value ) { if ( d_func()->useNewLayoutSystem != value ) d_func()->useNewLayoutSystem = value; } diff --git a/src/KChart/KChartLayoutItems.cpp b/src/KChart/KChartLayoutItems.cpp index 4af97d9..b69fde4 100644 --- a/src/KChart/KChartLayoutItems.cpp +++ b/src/KChart/KChartLayoutItems.cpp @@ -1,1026 +1,1026 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartLayoutItems.h" #include "KTextDocument.h" #include "KChartAbstractArea.h" #include "KChartAbstractDiagram.h" #include "KChartBackgroundAttributes.h" #include "KChartFrameAttributes.h" #include "KChartPaintContext.h" #include "KChartPainterSaver_p.h" #include "KChartPrintingParameters.h" #include "KChartMath_p.h" #include #include #include #include #include #include #include #include #include #include #include //#define DEBUG_ITEMS_PAINT /** Inform the item about its widget: This enables the item, to trigger that widget's update, whenever the size of the item's contents has changed. Thus, you need to call setParentWidget on every item, that has a non-fixed size. */ void KChart::AbstractLayoutItem::setParentWidget( QWidget* widget ) { mParent = widget; } void KChart::AbstractLayoutItem::paintAll( QPainter& painter ) { paint( &painter ); } /** * Default impl: Paint the complete item using its layouted position and size. */ void KChart::AbstractLayoutItem::paintCtx( PaintContext* context ) { if ( context ) paint( context->painter() ); } /** Report changed size hint: ask the parent widget to recalculate the layout. */ void KChart::AbstractLayoutItem::sizeHintChanged() const { // This is exactly like what QWidget::updateGeometry does. // qDebug("KChart::AbstractLayoutItem::sizeHintChanged() called"); if ( mParent ) { if ( mParent->layout() ) mParent->layout()->invalidate(); else QApplication::postEvent( mParent, new QEvent( QEvent::LayoutRequest ) ); } } KChart::TextBubbleLayoutItem::TextBubbleLayoutItem( const QString& text, const KChart::TextAttributes& attributes, const QObject* area, KChartEnums::MeasureOrientation orientation, Qt::Alignment alignment ) : AbstractLayoutItem( alignment ), m_text( new TextLayoutItem( text, attributes, area, orientation, alignment ) ) { } KChart::TextBubbleLayoutItem::TextBubbleLayoutItem() : AbstractLayoutItem( Qt::AlignLeft ), m_text( new TextLayoutItem() ) { } KChart::TextBubbleLayoutItem::~TextBubbleLayoutItem() { delete m_text; } void KChart::TextBubbleLayoutItem::setAutoReferenceArea( const QObject* area ) { m_text->setAutoReferenceArea( area ); } const QObject* KChart::TextBubbleLayoutItem::autoReferenceArea() const { return m_text->autoReferenceArea(); } void KChart::TextBubbleLayoutItem::setText( const QString& text ) { m_text->setText( text ); } QString KChart::TextBubbleLayoutItem::text() const { return m_text->text(); } void KChart::TextBubbleLayoutItem::setTextAttributes( const TextAttributes& a ) { m_text->setTextAttributes( a ); } KChart::TextAttributes KChart::TextBubbleLayoutItem::textAttributes() const { return m_text->textAttributes(); } bool KChart::TextBubbleLayoutItem::isEmpty() const { return m_text->isEmpty(); } Qt::Orientations KChart::TextBubbleLayoutItem::expandingDirections() const { return m_text->expandingDirections(); } QSize KChart::TextBubbleLayoutItem::maximumSize() const { const int border = borderWidth(); return m_text->maximumSize() + QSize( 2 * border, 2 * border ); } QSize KChart::TextBubbleLayoutItem::minimumSize() const { const int border = borderWidth(); return m_text->minimumSize() + QSize( 2 * border, 2 * border ); } QSize KChart::TextBubbleLayoutItem::sizeHint() const { const int border = borderWidth(); return m_text->sizeHint() + QSize( 2 * border, 2 * border ); } void KChart::TextBubbleLayoutItem::setGeometry( const QRect& r ) { const int border = borderWidth(); m_text->setGeometry( r.adjusted( border, border, -border, -border ) ); } QRect KChart::TextBubbleLayoutItem::geometry() const { const int border = borderWidth(); return m_text->geometry().adjusted( -border, -border, border, border ); } void KChart::TextBubbleLayoutItem::paint( QPainter* painter ) { const QPen oldPen = painter->pen(); const QBrush oldBrush = painter->brush(); painter->setPen( Qt::black ); painter->setBrush( QColor( 255, 255, 220 ) ); painter->drawRoundRect( geometry(), 10 ); painter->setPen( oldPen ); painter->setBrush( oldBrush ); m_text->paint( painter ); } int KChart::TextBubbleLayoutItem::borderWidth() const { return 1; } KChart::TextLayoutItem::TextLayoutItem( const QString& text, const KChart::TextAttributes& attributes, const QObject* area, KChartEnums::MeasureOrientation orientation, Qt::Alignment alignment ) : AbstractLayoutItem( alignment ) , mText( text ) , mTextAlignment( alignment ) , mAttributes( attributes ) , mAutoReferenceArea( area ) , mAutoReferenceOrientation( orientation ) , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint() , cachedFontSize( 0.0 ) , cachedFont( mAttributes.font() ) { } KChart::TextLayoutItem::TextLayoutItem() : AbstractLayoutItem( Qt::AlignLeft ) , mText() , mTextAlignment( Qt::AlignLeft ) , mAttributes() , mAutoReferenceArea( 0 ) , mAutoReferenceOrientation( KChartEnums::MeasureOrientationHorizontal ) , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint() , cachedFontSize( 0.0 ) , cachedFont( mAttributes.font() ) { } void KChart::TextLayoutItem::setAutoReferenceArea( const QObject* area ) { mAutoReferenceArea = area; cachedSizeHint = QSize(); sizeHint(); } const QObject* KChart::TextLayoutItem::autoReferenceArea() const { return mAutoReferenceArea; } void KChart::TextLayoutItem::setText(const QString & text) { mText = text; cachedSizeHint = QSize(); sizeHint(); if ( mParent ) mParent->update(); } QString KChart::TextLayoutItem::text() const { return mText; } void KChart::TextLayoutItem::setTextAlignment( Qt::Alignment alignment) { if ( mTextAlignment == alignment ) return; mTextAlignment = alignment; if ( mParent ) mParent->update(); } Qt::Alignment KChart::TextLayoutItem::textAlignment() const { return mTextAlignment; } /** \brief Use this to specify the text attributes to be used for this item. \sa textAttributes */ void KChart::TextLayoutItem::setTextAttributes( const TextAttributes &a ) { mAttributes = a; cachedFont = a.font(); cachedSizeHint = QSize(); // invalidate size hint sizeHint(); if ( mParent ) mParent->update(); } /** Returns the text attributes to be used for this item. \sa setTextAttributes */ KChart::TextAttributes KChart::TextLayoutItem::textAttributes() const { return mAttributes; } Qt::Orientations KChart::TextLayoutItem::expandingDirections() const { - return 0; // Grow neither vertically nor horizontally + return Qt::Orientations(); // Grow neither vertically nor horizontally } QRect KChart::TextLayoutItem::geometry() const { return mRect; } bool KChart::TextLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KChart::TextLayoutItem::maximumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } QSize KChart::TextLayoutItem::minimumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } void KChart::TextLayoutItem::setGeometry( const QRect& r ) { mRect = r; } // returns the bounding box of rect rotated around its center QRectF rotatedRect( const QRectF& rect, qreal rotation ) { QTransform t; QPointF center = rect.center(); t.translate( center.x(), center.y() ); t.rotate( rotation ); t.translate( -center.x(), -center.y() ); return t.mapRect( rect ); } qreal KChart::TextLayoutItem::fitFontSizeToGeometry() const { QFont f = realFont(); const qreal origResult = f.pointSizeF(); qreal result = origResult; const qreal minSize = mAttributes.minimalFontSize().value(); const QSize mySize = geometry().size(); if ( mySize.isNull() ) { return result; } QFontMetrics fm( f ); while ( true ) { const QSizeF textSize = rotatedRect( fm.boundingRect( mText ), mAttributes.rotation() ).normalized().size(); if ( textSize.height() <= mySize.height() && textSize.width() <= mySize.width() ) { return result; } result -= 0.5; if ( minSize > 0 && result < minSize ) { return result + 0.5; } else if ( result <= 0.0 ) { return origResult; } f.setPointSizeF( result ); fm = QFontMetrics( f ); } } qreal KChart::TextLayoutItem::realFontSize() const { return mAttributes.calculatedFontSize( mAutoReferenceArea, mAutoReferenceOrientation ); } bool KChart::TextLayoutItem::maybeUpdateRealFont() const { const qreal fntSiz = realFontSize(); const bool doUpdate = !cachedSizeHint.isValid() || cachedFontSize != fntSiz; if ( doUpdate && fntSiz > 0.0 ) { cachedFontSize = fntSiz; cachedFont.setPointSizeF( fntSiz ); } return doUpdate; // "didUpdate" by now } QFont KChart::TextLayoutItem::realFont() const { maybeUpdateRealFont(); return cachedFont; } QPolygon KChart::TextLayoutItem::boundingPolygon() const { // should probably call sizeHint() here, but that one is expensive (see TODO there) return mCachedBoundingPolygon; } bool KChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPointF& myPos, const QPointF& otherPos ) const { return intersects( other, myPos.toPoint(), otherPos.toPoint() ); } bool KChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPoint& myPos, const QPoint& otherPos ) const { QRegion myRegion( boundingPolygon().translated( myPos - otherPos ) ); QRegion otherRegion( other.boundingPolygon() ); return myRegion.intersects( otherRegion ); } QSize KChart::TextLayoutItem::sizeHint() const { // ### we only really need to recalculate the size hint when mAttributes.rotation has *changed* if ( maybeUpdateRealFont() || mAttributes.rotation() || !cachedSizeHint.isValid() ) { const QSize newSizeHint( calcSizeHint( cachedFont ) ); Q_ASSERT( newSizeHint.isValid() ); if ( newSizeHint != cachedSizeHint ) { cachedSizeHint = newSizeHint; sizeHintChanged(); } } return cachedSizeHint; } QSize KChart::TextLayoutItem::sizeHintUnrotated() const { maybeUpdateRealFont(); // make sure the cached font is up to date return unrotatedSizeHint( cachedFont ); } // PENDING(kalle) Support auto shrink QSize KChart::TextLayoutItem::unrotatedTextSize( QFont fnt ) const { if ( fnt == QFont() ) { fnt = realFont(); // this is the cached font in most cases } const QFontMetricsF fm( fnt, GlobalMeasureScaling::paintDevice() ); QRect veryLarge( 0, 0, 100000, 100000 ); // this overload of boundingRect() interprets \n as line breaks, not as regular characters. return fm.boundingRect( veryLarge, Qt::AlignLeft | Qt::AlignTop, mText ).size().toSize(); } int KChart::TextLayoutItem::marginWidth() const { return marginWidth( unrotatedTextSize() ); } int KChart::TextLayoutItem::marginWidth( const QSize& textSize ) const { return qMin ( QApplication::style()->pixelMetric( QStyle::PM_ButtonMargin, 0, 0 ), // decrease frame size if the text is small textSize.height() * 2 / 3 ); } QSize KChart::TextLayoutItem::unrotatedSizeHint( const QFont& fnt ) const { QSize ret = unrotatedTextSize( fnt ); const int margin = marginWidth( ret ); ret += QSize( margin, margin ); return ret; } QSize KChart::TextLayoutItem::calcSizeHint( const QFont& font ) const { const QSize size = unrotatedSizeHint( font ); QPoint topLeft( -size.width() * 0.5, -size.height() * 0.5 ); if ( !mAttributes.rotation() ) { mCachedBoundingPolygon.resize( 4 ); // using the same winding order as returned by QPolygon QTransform::mapToPolygon(const QRect&), // which is: 0-1: top edge, 1-2: right edge, 2-3: bottom edge, 3-0: left edge (of input rect) mCachedBoundingPolygon[ 0 ] = topLeft; mCachedBoundingPolygon[ 1 ] = topLeft + QPoint( size.width(), 0 ); // top right mCachedBoundingPolygon[ 2 ] = topLeft + QPoint( size.width(), size.height() ); // bottom right mCachedBoundingPolygon[ 3 ] = topLeft + QPoint( 0, size.height() ); // bottom left return size; } const QRect rect( topLeft, size ); QTransform t; t.rotate( mAttributes.rotation() ); mCachedBoundingPolygon = t.mapToPolygon( rect ); return mCachedBoundingPolygon.boundingRect().size(); } void KChart::TextLayoutItem::paint( QPainter* painter ) { if ( !mRect.isValid() ) { return; } const PainterSaver painterSaver( painter ); QFont f = realFont(); if ( mAttributes.autoShrink() ) { f.setPointSizeF( fitFontSizeToGeometry() ); } painter->setFont( f ); QSize innerSize = unrotatedTextSize(); QRectF rect = QRectF( QPointF( 0, 0 ), innerSize ); rect.translate( -rect.center() ); painter->translate( mRect.center() ); painter->rotate( mAttributes.rotation() ); #ifdef DEBUG_ITEMS_PAINT painter->setPen( Qt::red ); painter->drawRect( rect ); #endif painter->setPen( PrintingParameters::scalePen( mAttributes.pen() ) ); QTextDocument* document = mAttributes.textDocument(); if ( document ) { document->setPageSize( rect.size() ); document->setHtml( mText ); QAbstractTextDocumentLayout::PaintContext paintcontext; // ### this doesn't work for rotated painting because clip does not translate the painting // TODO translate the painting either using a QTransform or one of QPainter's transform stages paintcontext.clip = rect; document->documentLayout()->draw( painter, paintcontext ); } else { painter->drawText( rect, mTextAlignment, mText ); } } KChart::HorizontalLineLayoutItem::HorizontalLineLayoutItem() : AbstractLayoutItem( Qt::AlignCenter ) { } Qt::Orientations KChart::HorizontalLineLayoutItem::expandingDirections() const { return Qt::Horizontal; } QRect KChart::HorizontalLineLayoutItem::geometry() const { return mRect; } bool KChart::HorizontalLineLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KChart::HorizontalLineLayoutItem::maximumSize() const { return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); } QSize KChart::HorizontalLineLayoutItem::minimumSize() const { return QSize( 0, 0 ); } void KChart::HorizontalLineLayoutItem::setGeometry( const QRect& r ) { mRect = r; } QSize KChart::HorizontalLineLayoutItem::sizeHint() const { return QSize( -1, 3 ); // see qframe.cpp } void KChart::HorizontalLineLayoutItem::paint( QPainter* painter ) { if ( !mRect.isValid() ) return; painter->drawLine( QPointF( mRect.left(), mRect.center().y() ), QPointF( mRect.right(), mRect.center().y() ) ); } KChart::VerticalLineLayoutItem::VerticalLineLayoutItem() : AbstractLayoutItem( Qt::AlignCenter ) { } Qt::Orientations KChart::VerticalLineLayoutItem::expandingDirections() const { return Qt::Vertical; } QRect KChart::VerticalLineLayoutItem::geometry() const { return mRect; } bool KChart::VerticalLineLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KChart::VerticalLineLayoutItem::maximumSize() const { return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); } QSize KChart::VerticalLineLayoutItem::minimumSize() const { return QSize( 0, 0 ); } void KChart::VerticalLineLayoutItem::setGeometry( const QRect& r ) { mRect = r; } QSize KChart::VerticalLineLayoutItem::sizeHint() const { return QSize( 3, -1 ); // see qframe.cpp } void KChart::VerticalLineLayoutItem::paint( QPainter* painter ) { if ( !mRect.isValid() ) return; painter->drawLine( QPointF( mRect.center().x(), mRect.top() ), QPointF( mRect.center().x(), mRect.bottom() ) ); } KChart::MarkerLayoutItem::MarkerLayoutItem( KChart::AbstractDiagram* diagram, const MarkerAttributes& marker, const QBrush& brush, const QPen& pen, Qt::Alignment alignment ) : AbstractLayoutItem( alignment ) , mDiagram( diagram ) , mMarker( marker ) , mBrush( brush ) , mPen( pen ) { } Qt::Orientations KChart::MarkerLayoutItem::expandingDirections() const { - return 0; // Grow neither vertically nor horizontally + return Qt::Orientations(); // Grow neither vertically nor horizontally } QRect KChart::MarkerLayoutItem::geometry() const { return mRect; } bool KChart::MarkerLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KChart::MarkerLayoutItem::maximumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } QSize KChart::MarkerLayoutItem::minimumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } void KChart::MarkerLayoutItem::setGeometry( const QRect& r ) { mRect = r; } QSize KChart::MarkerLayoutItem::sizeHint() const { //qDebug() << "KChart::MarkerLayoutItem::sizeHint() returns:"<(( rect.width() - siz.width()) / 2.0 ), static_cast(( rect.height() - siz.height()) / 2.0 ) ); #ifdef DEBUG_ITEMS_PAINT QPointF oldPos = pos; #endif // And finally, drawMarker() assumes the position to be the center // of the marker, adjust again. pos += QPointF( static_cast( siz.width() ) / 2.0, static_cast( siz.height() )/ 2.0 ); diagram->paintMarker( painter, marker, brush, pen, pos.toPoint(), siz ); #ifdef DEBUG_ITEMS_PAINT const QPen oldPen( painter->pen() ); painter->setPen( Qt::red ); painter->drawRect( QRect( oldPos.toPoint(), siz ) ); painter->setPen( oldPen ); #endif } KChart::LineLayoutItem::LineLayoutItem( KChart::AbstractDiagram* diagram, int length, const QPen& pen, Qt::Alignment legendLineSymbolAlignment, Qt::Alignment alignment ) : AbstractLayoutItem( alignment ) , mDiagram( diagram ) , mLength( length ) , mPen( pen ) , mLegendLineSymbolAlignment(legendLineSymbolAlignment) { // enforce a minimum pen width if ( pen.width() < 2 ) mPen.setWidth( 2 ); } Qt::Orientations KChart::LineLayoutItem::expandingDirections() const { - return 0; // Grow neither vertically nor horizontally + return Qt::Orientations(); // Grow neither vertically nor horizontally } QRect KChart::LineLayoutItem::geometry() const { return mRect; } bool KChart::LineLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KChart::LineLayoutItem::maximumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } QSize KChart::LineLayoutItem::minimumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } void KChart::LineLayoutItem::setGeometry( const QRect& r ) { mRect = r; } QSize KChart::LineLayoutItem::sizeHint() const { return QSize( mLength, mPen.width() + 2 ); } void KChart::LineLayoutItem::setLegendLineSymbolAlignment(Qt::Alignment legendLineSymbolAlignment) { if (mLegendLineSymbolAlignment == legendLineSymbolAlignment) return; mLegendLineSymbolAlignment = legendLineSymbolAlignment; } Qt::Alignment KChart::LineLayoutItem::legendLineSymbolAlignment() const { return mLegendLineSymbolAlignment; } void KChart::LineLayoutItem::paint( QPainter* painter ) { paintIntoRect( painter, mRect, mPen, mLegendLineSymbolAlignment ); } void KChart::LineLayoutItem::paintIntoRect( QPainter* painter, const QRect& rect, const QPen& pen, Qt::Alignment lineAlignment) { if ( ! rect.isValid() ) return; const QPen oldPen = painter->pen(); painter->setPen( PrintingParameters::scalePen( pen ) ); qreal y = 0; if (lineAlignment == Qt::AlignTop) y = rect.top(); else if (lineAlignment == Qt::AlignBottom) y = rect.bottom(); else y = rect.center().y(); painter->drawLine( QPointF( rect.left(), y ), QPointF( rect.right(), y ) ); painter->setPen( oldPen ); } KChart::LineWithMarkerLayoutItem::LineWithMarkerLayoutItem( KChart::AbstractDiagram* diagram, int lineLength, const QPen& linePen, int markerOffs, const MarkerAttributes& marker, const QBrush& markerBrush, const QPen& markerPen, Qt::Alignment alignment ) : AbstractLayoutItem( alignment ) , mDiagram( diagram ) , mLineLength( lineLength ) , mLinePen( linePen ) , mMarkerOffs( markerOffs ) , mMarker( marker ) , mMarkerBrush( markerBrush ) , mMarkerPen( markerPen ) { } Qt::Orientations KChart::LineWithMarkerLayoutItem::expandingDirections() const { - return 0; // Grow neither vertically nor horizontally + return Qt::Orientations(); // Grow neither vertically nor horizontally } QRect KChart::LineWithMarkerLayoutItem::geometry() const { return mRect; } bool KChart::LineWithMarkerLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KChart::LineWithMarkerLayoutItem::maximumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } QSize KChart::LineWithMarkerLayoutItem::minimumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } void KChart::LineWithMarkerLayoutItem::setGeometry( const QRect& r ) { mRect = r; } QSize KChart::LineWithMarkerLayoutItem::sizeHint() const { const QSize lineSize( mLineLength, mLinePen.width() + 2 ); return lineSize.expandedTo( mMarker.markerSize().toSize() ); } void KChart::LineWithMarkerLayoutItem::paint( QPainter* painter ) { // paint the line over the full width, into the vertical middle of the rect LineLayoutItem::paintIntoRect( painter, mRect, mLinePen, Qt::AlignCenter ); // paint the marker with the given offset from the left side of the line const QRect r( QPoint( mRect.x()+mMarkerOffs, mRect.y() ), QSize( mMarker.markerSize().toSize().width(), mRect.height() ) ); MarkerLayoutItem::paintIntoRect( painter, r, mDiagram, mMarker, mMarkerBrush, mMarkerPen ); } KChart::AutoSpacerLayoutItem::AutoSpacerLayoutItem( bool layoutIsAtTopPosition, QHBoxLayout *rightLeftLayout, bool layoutIsAtLeftPosition, QVBoxLayout *topBottomLayout ) : AbstractLayoutItem( Qt::AlignCenter ) , mLayoutIsAtTopPosition( layoutIsAtTopPosition ) , mRightLeftLayout( rightLeftLayout ) , mLayoutIsAtLeftPosition( layoutIsAtLeftPosition ) , mTopBottomLayout( topBottomLayout ) { } Qt::Orientations KChart::AutoSpacerLayoutItem::expandingDirections() const { - return 0; // Grow neither vertically nor horizontally + return Qt::Orientations(); // Grow neither vertically nor horizontally } QRect KChart::AutoSpacerLayoutItem::geometry() const { return mRect; } bool KChart::AutoSpacerLayoutItem::isEmpty() const { return true; // never empty, otherwise the layout item would not exist } QSize KChart::AutoSpacerLayoutItem::maximumSize() const { return sizeHint(); } QSize KChart::AutoSpacerLayoutItem::minimumSize() const { return sizeHint(); } void KChart::AutoSpacerLayoutItem::setGeometry( const QRect& r ) { mRect = r; } static void updateCommonBrush( QBrush& commonBrush, bool& bStart, const KChart::AbstractArea& area ) { const KChart::BackgroundAttributes ba( area.backgroundAttributes() ); const bool hasSimpleBrush = ( ! area.frameAttributes().isVisible() && ba.isVisible() && ba.pixmapMode() == KChart::BackgroundAttributes::BackgroundPixmapModeNone && ba.brush().gradient() == 0 ); if ( bStart ) { bStart = false; commonBrush = hasSimpleBrush ? ba.brush() : QBrush(); } else { if ( ! hasSimpleBrush || ba.brush() != commonBrush ) { commonBrush = QBrush(); } } } QSize KChart::AutoSpacerLayoutItem::sizeHint() const { QBrush commonBrush; bool bStart=true; // calculate the maximal overlap of the top/bottom axes: int topBottomOverlap = 0; if ( mTopBottomLayout ) { for (int i = 0; i < mTopBottomLayout->count(); ++i) { AbstractArea* area = dynamic_cast(mTopBottomLayout->itemAt(i)); if ( area ) { //qDebug() << "AutoSpacerLayoutItem testing" << area; topBottomOverlap = qMax( topBottomOverlap, mLayoutIsAtLeftPosition ? area->rightOverlap() : area->leftOverlap() ); updateCommonBrush( commonBrush, bStart, *area ); } } } // calculate the maximal overlap of the left/right axes: int leftRightOverlap = 0; if ( mRightLeftLayout ) { for (int i = 0; i < mRightLeftLayout->count(); ++i) { AbstractArea* area = dynamic_cast(mRightLeftLayout->itemAt(i)); if ( area ) { //qDebug() << "AutoSpacerLayoutItem testing" << area; leftRightOverlap = qMax( leftRightOverlap, mLayoutIsAtTopPosition ? area->bottomOverlap() : area->topOverlap() ); updateCommonBrush( commonBrush, bStart, *area ); } } } if ( topBottomOverlap > 0 && leftRightOverlap > 0 ) mCommonBrush = commonBrush; else mCommonBrush = QBrush(); mCachedSize = QSize( topBottomOverlap, leftRightOverlap ); //qDebug() << mCachedSize; return mCachedSize; } void KChart::AutoSpacerLayoutItem::paint( QPainter* painter ) { if ( mParentLayout && mRect.isValid() && mCachedSize.isValid() && mCommonBrush.style() != Qt::NoBrush ) { QPoint p1( mRect.topLeft() ); QPoint p2( mRect.bottomRight() ); if ( mLayoutIsAtLeftPosition ) p1.rx() += mCachedSize.width() - mParentLayout->spacing(); else p2.rx() -= mCachedSize.width() - mParentLayout->spacing(); if ( mLayoutIsAtTopPosition ) { p1.ry() += mCachedSize.height() - mParentLayout->spacing() - 1; p2.ry() -= 1; } else p2.ry() -= mCachedSize.height() - mParentLayout->spacing() - 1; //qDebug() << mLayoutIsAtTopPosition << mLayoutIsAtLeftPosition; //qDebug() << mRect; //qDebug() << mParentLayout->margin(); //qDebug() << QRect( p1, p2 ); const QPoint oldBrushOrigin( painter->brushOrigin() ); const QBrush oldBrush( painter->brush() ); const QPen oldPen( painter->pen() ); const QPointF newTopLeft( painter->deviceMatrix().map( p1 ) ); painter->setBrushOrigin( newTopLeft ); painter->setBrush( mCommonBrush ); painter->setPen( Qt::NoPen ); painter->drawRect( QRect( p1, p2 ) ); painter->setBrushOrigin( oldBrushOrigin ); painter->setBrush( oldBrush ); painter->setPen( oldPen ); } // debug code: #if 0 //qDebug() << "KChart::AutoSpacerLayoutItem::paint()"; if ( !mRect.isValid() ) return; painter->drawRect( mRect ); painter->drawLine( QPointF( mRect.topLeft(), mRect.bottomRight() ) ); painter->drawLine( QPointF( mRect.topRight(), mRect.bottomLeft() ) ); #endif } diff --git a/src/KChart/KChartLayoutItems.h b/src/KChart/KChartLayoutItems.h index 5e9dfff..2437457 100644 --- a/src/KChart/KChartLayoutItems.h +++ b/src/KChart/KChartLayoutItems.h @@ -1,476 +1,476 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTLAYOUTITEMS_H #define KCHARTLAYOUTITEMS_H #include #include #include #include #include #include #include "KChartTextAttributes.h" #include "KChartMarkerAttributes.h" QT_BEGIN_NAMESPACE class QPainter; class KTextDocument; QT_END_NAMESPACE // TODO remove QRectF rotatedRect( const QRectF& pt, qreal rotation ); namespace KChart { class AbstractDiagram; class PaintContext; /** * Base class for all layout items of KChart * \internal */ class KCHART_EXPORT AbstractLayoutItem : public QLayoutItem { public: - AbstractLayoutItem( Qt::Alignment itemAlignment = 0 ) : + AbstractLayoutItem( Qt::Alignment itemAlignment = Qt::Alignment() ) : QLayoutItem( itemAlignment ), mParent( 0 ), mParentLayout( 0 ) {} /** * Default impl: just call paint. * * Derived classes like KChart::AbstractArea are providing * additional action here. */ virtual void paintAll( QPainter& painter ); virtual void paint( QPainter* ) = 0; virtual void paintCtx( PaintContext* context ); virtual void setParentWidget( QWidget* widget ); virtual void sizeHintChanged() const; void setParentLayout( QLayout* lay ) { mParentLayout = lay; } QLayout* parentLayout() { return mParentLayout; } void removeFromParentLayout() { if ( mParentLayout ) { if ( widget() ) mParentLayout->removeWidget( widget() ); else mParentLayout->removeItem( this ); } } protected: QWidget* mParent; QLayout* mParentLayout; }; /** * Layout item showing a text *\internal */ class KCHART_EXPORT TextLayoutItem : public AbstractLayoutItem { public: TextLayoutItem(); TextLayoutItem( const QString& text, const TextAttributes& attributes, const QObject* autoReferenceArea, KChartEnums::MeasureOrientation autoReferenceOrientation, - Qt::Alignment alignment = 0 ); + Qt::Alignment alignment = Qt::Alignment() ); void setAutoReferenceArea( const QObject* area ); const QObject* autoReferenceArea() const; void setText(const QString & text); QString text() const; void setTextAlignment( Qt::Alignment ); Qt::Alignment textAlignment() const; void setTextAttributes( const TextAttributes& a ); TextAttributes textAttributes() const; /** pure virtual in QLayoutItem */ bool isEmpty() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize maximumSize() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize minimumSize() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize sizeHint() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QRect geometry() const Q_DECL_OVERRIDE; virtual int marginWidth() const; virtual QSize sizeHintUnrotated() const; virtual bool intersects( const TextLayoutItem& other, const QPointF& myPos, const QPointF& otherPos ) const; virtual bool intersects( const TextLayoutItem& other, const QPoint& myPos, const QPoint& otherPos ) const; virtual qreal realFontSize() const; virtual QFont realFont() const; void paint( QPainter* ) Q_DECL_OVERRIDE; QPolygon boundingPolygon() const; private: bool maybeUpdateRealFont() const; QSize unrotatedSizeHint( const QFont& fnt = QFont() ) const; QSize unrotatedTextSize( QFont fnt = QFont() ) const; QSize calcSizeHint( const QFont& font ) const; int marginWidth( const QSize& textSize ) const; qreal fitFontSizeToGeometry() const; QRect mRect; QString mText; Qt::Alignment mTextAlignment; TextAttributes mAttributes; const QObject* mAutoReferenceArea; KChartEnums::MeasureOrientation mAutoReferenceOrientation; mutable QSize cachedSizeHint; mutable QPolygon mCachedBoundingPolygon; mutable qreal cachedFontSize; mutable QFont cachedFont; }; class KCHART_EXPORT TextBubbleLayoutItem : public AbstractLayoutItem { public: TextBubbleLayoutItem(); TextBubbleLayoutItem( const QString& text, const TextAttributes& attributes, const QObject* autoReferenceArea, KChartEnums::MeasureOrientation autoReferenceOrientation, - Qt::Alignment alignment = 0 ); + Qt::Alignment alignment = Qt::Alignment() ); ~TextBubbleLayoutItem(); void setAutoReferenceArea( const QObject* area ); const QObject* autoReferenceArea() const; void setText(const QString & text); QString text() const; void setTextAttributes( const TextAttributes& a ); TextAttributes textAttributes() const; /** pure virtual in QLayoutItem */ bool isEmpty() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize maximumSize() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize minimumSize() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize sizeHint() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QRect geometry() const Q_DECL_OVERRIDE; void paint( QPainter* painter ) Q_DECL_OVERRIDE; protected: int borderWidth() const; private: TextLayoutItem* const m_text; }; /** * Layout item showing a data point marker * \internal */ class KCHART_EXPORT MarkerLayoutItem : public AbstractLayoutItem { public: MarkerLayoutItem( AbstractDiagram* diagram, const MarkerAttributes& marker, const QBrush& brush, const QPen& pen, - Qt::Alignment alignment = 0 ); + Qt::Alignment alignment = Qt::Alignment() ); Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; QRect geometry() const Q_DECL_OVERRIDE; bool isEmpty() const Q_DECL_OVERRIDE; QSize maximumSize() const Q_DECL_OVERRIDE; QSize minimumSize() const Q_DECL_OVERRIDE; void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; QSize sizeHint() const Q_DECL_OVERRIDE; void paint( QPainter* ) Q_DECL_OVERRIDE; static void paintIntoRect( QPainter* painter, const QRect& rect, AbstractDiagram* diagram, const MarkerAttributes& marker, const QBrush& brush, const QPen& pen ); private: AbstractDiagram* mDiagram; QRect mRect; MarkerAttributes mMarker; QBrush mBrush; QPen mPen; }; /** * Layout item showing a coloured line * \internal */ class KCHART_EXPORT LineLayoutItem : public AbstractLayoutItem { public: LineLayoutItem( AbstractDiagram* diagram, int length, const QPen& pen, Qt::Alignment mLegendLineSymbolAlignment, - Qt::Alignment alignment = 0 ); + Qt::Alignment alignment = Qt::Alignment() ); Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; QRect geometry() const Q_DECL_OVERRIDE; bool isEmpty() const Q_DECL_OVERRIDE; QSize maximumSize() const Q_DECL_OVERRIDE; QSize minimumSize() const Q_DECL_OVERRIDE; void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; QSize sizeHint() const Q_DECL_OVERRIDE; void setLegendLineSymbolAlignment(Qt::Alignment legendLineSymbolAlignment); virtual Qt::Alignment legendLineSymbolAlignment() const; void paint( QPainter* ) Q_DECL_OVERRIDE; static void paintIntoRect( QPainter* painter, const QRect& rect, const QPen& pen, Qt::Alignment lineAlignment); private: AbstractDiagram* mDiagram; //TODO: not used. remove it int mLength; QPen mPen; QRect mRect; Qt::Alignment mLegendLineSymbolAlignment; }; /** * Layout item showing a coloured line and a data point marker * \internal */ class KCHART_EXPORT LineWithMarkerLayoutItem : public AbstractLayoutItem { public: LineWithMarkerLayoutItem( AbstractDiagram* diagram, int lineLength, const QPen& linePen, int markerOffs, const MarkerAttributes& marker, const QBrush& markerBrush, const QPen& markerPen, - Qt::Alignment alignment = 0 ); + Qt::Alignment alignment = Qt::Alignment() ); Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; QRect geometry() const Q_DECL_OVERRIDE; bool isEmpty() const Q_DECL_OVERRIDE; QSize maximumSize() const Q_DECL_OVERRIDE; QSize minimumSize() const Q_DECL_OVERRIDE; void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; QSize sizeHint() const Q_DECL_OVERRIDE; void paint( QPainter* ) Q_DECL_OVERRIDE; private: AbstractDiagram* mDiagram; QRect mRect; int mLineLength; QPen mLinePen; int mMarkerOffs; MarkerAttributes mMarker; QBrush mMarkerBrush; QPen mMarkerPen; }; /** * Layout item showing a horizontal line * \internal */ class KCHART_EXPORT HorizontalLineLayoutItem : public AbstractLayoutItem { public: HorizontalLineLayoutItem(); Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; QRect geometry() const Q_DECL_OVERRIDE; bool isEmpty() const Q_DECL_OVERRIDE; QSize maximumSize() const Q_DECL_OVERRIDE; QSize minimumSize() const Q_DECL_OVERRIDE; void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; QSize sizeHint() const Q_DECL_OVERRIDE; void paint( QPainter* ) Q_DECL_OVERRIDE; private: QRect mRect; }; /** * Layout item showing a vertial line * \internal */ class KCHART_EXPORT VerticalLineLayoutItem : public AbstractLayoutItem { public: VerticalLineLayoutItem(); Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; QRect geometry() const Q_DECL_OVERRIDE; bool isEmpty() const Q_DECL_OVERRIDE; QSize maximumSize() const Q_DECL_OVERRIDE; QSize minimumSize() const Q_DECL_OVERRIDE; void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; QSize sizeHint() const Q_DECL_OVERRIDE; void paint( QPainter* ) Q_DECL_OVERRIDE; private: QRect mRect; }; /** * @brief An empty layout item * \internal * * The AutoSpacerLayoutItem is automatically put into each corner cell of * the planeLayout grid: one of its reference-layouts is a QVBoxLayout (for * the top, or bottom axes resp.), the other one is a QHBoxLayout (for the * left/right sided axes). * * The spacer reserves enough space so all of the AbstractAreas contained * in the two reference-layouts can display not only their in-bounds * content but also their overlapping content reaching out of their area. * * KChart's layouting is applying this schema: \verbatim +------------------+-------------------------+-----------------+ | +--------------+ | +---------------------+ | +-------------+ | | | | | | QVBoxLayout for | | | | | | | AUTO | | | the top axis/axes | | | AUTO | | | | SPACER | | +---------------------+ | | SPACER | | | | ITEM | | | | | | ITEM | | | | | | | | | | | | | +--------------+ | +---------------------+ | +-------------+ | +------------------+-------------------------+-----------------+ | +--------+-----+ | +---------------------+ | +-------+-----+ | | | | | | | | | | | | | | | | | | | | | | | | | | | QHBox- | | | | | | | Right | | | | | Layout | | | | | | | | | | | | | | | | | | | axes | | | | | for | | | | | | | | | | | | | | | | | | | layout| | | | | the | | | | DIAGRAM(s) | | | | | | | | | | | | | | | | | | | | left | | | | | | | | | | | | | | | | | | | | | | | | axis | | | | | | | | | | | | or | | | | | | | | | | | | axes | | | | | | | | | | | | | | | | | | | | | | | +--------+-----+ | +---------------------+ | +-------+-----+ | +------------------+-------------------------+-----------------+ | +--------------+ | +---------------------+ | +-------------+ | | | | | | QVBoxLayout for | | | | | | | AUTO | | | the bottom axes | | | AUTO | | | | SPACER | | +---------------------+ | | SPACER | | | | ITEM | | | | | | ITEM | | | | | | | | | | | | | +--------------+ | +---------------------+ | +-------------+ | +------------------+-------------------------+-----------------+ \endverbatim * * A typical use case is an Abscissa axis with long labels: \verbatim 2 -| | 1 -| | 0 -+------------------------------------ | | | | | Monday Tuesday Wednesday Thursday Friday \endverbatim * The last letters of the word "Friday" would have been * cut off in previous versions of KChart - that is * if you did not call KChart::Chart::setGlobalLeading(). * * Now the word will be shown completely because there * is an auto-spacer-item taking care for the additional * space needed in the lower/right corner. */ class KCHART_EXPORT AutoSpacerLayoutItem : public AbstractLayoutItem { public: AutoSpacerLayoutItem( bool layoutIsAtTopPosition, QHBoxLayout *rightLeftLayout, bool layoutIsAtLeftPosition, QVBoxLayout *topBottomLayout ); Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; QRect geometry() const Q_DECL_OVERRIDE; bool isEmpty() const Q_DECL_OVERRIDE; QSize maximumSize() const Q_DECL_OVERRIDE; QSize minimumSize() const Q_DECL_OVERRIDE; void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; QSize sizeHint() const Q_DECL_OVERRIDE; void paint( QPainter* ) Q_DECL_OVERRIDE; private: QRect mRect; bool mLayoutIsAtTopPosition; QHBoxLayout *mRightLeftLayout; bool mLayoutIsAtLeftPosition; QVBoxLayout *mTopBottomLayout; mutable QBrush mCommonBrush; mutable QSize mCachedSize; }; } #endif /* KCHARTLAYOUTITEMS_H */ diff --git a/tests/Gantt/apireview/entrydialog.h b/tests/Gantt/apireview/entrydialog.h index 44db3d6..d6c03df 100644 --- a/tests/Gantt/apireview/entrydialog.h +++ b/tests/Gantt/apireview/entrydialog.h @@ -1,67 +1,67 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ENTRYDIALOG_H #define ENTRYDIALOG_H #include #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; namespace Ui { class EntryDialog; } QT_END_NAMESPACE namespace KGantt { class ConstraintModel; } class EntryDialog : public QDialog { Q_OBJECT public: - EntryDialog( const QAbstractItemModel* model, QWidget* parent = 0, Qt::WindowFlags f = 0 ); + EntryDialog( const QAbstractItemModel* model, QWidget* parent = 0, Qt::WindowFlags f = Qt::WindowFlags() ); void initFrom( const QModelIndex& index, const KGantt::ConstraintModel* constraintModel ); QString name() const; int type() const; QDateTime startDate() const; QDateTime endDate() const; int completion() const; bool readOnly() const; QModelIndex depends() const; QString legend() const; private slots: void updateEndDate( const QDateTime& startDate ); void disableEditing( bool disable ); private: void init(); void addDependItem( const QAbstractItemModel* model, const QModelIndex& index, int indent = 0 ); QList indexList; const QAbstractItemModel* model; Ui::EntryDialog* ui; }; #endif /* ENTRYDIALOG_H */ diff --git a/tests/Gantt/apireview/mainwindow.h b/tests/Gantt/apireview/mainwindow.h index 64f727f..1657126 100644 --- a/tests/Gantt/apireview/mainwindow.h +++ b/tests/Gantt/apireview/mainwindow.h @@ -1,83 +1,83 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include QT_BEGIN_NAMESPACE class QStandardItem; class QStandardItemModel; namespace Ui { class MainWindow; } QT_END_NAMESPACE namespace KGantt { class ConstraintModel; class DateTimeGrid; } class MainWindow : public QMainWindow { Q_OBJECT public: - MainWindow( QWidget * parent = 0, Qt::WindowFlags flags = 0 ); + MainWindow( QWidget * parent = 0, Qt::WindowFlags flags = Qt::WindowFlags() ); private slots: void addNewEntry(); void removeEntry(); void addDemoEntry(); void printPreview(); void showContextMenu( const QPoint& ); void enableActions( const QItemSelection& selected ); void zoomIn(); void zoomOut(); void slotClicked( const QModelIndex& ); void slotDoubleClicked( const QModelIndex& ); private: void initModel(); void initActions(); void initItemDelegate(); void initGrid(); void setReadOnly( const QModelIndex& index, bool readOnly ); void addConstraint( const QModelIndex& index1, const QModelIndex& index2 ); void addConstraint( const QStandardItem* item1, const QStandardItem* item2 ); QStandardItemModel* model; KGantt::ConstraintModel* constraintModel; KGantt::DateTimeGrid* grid; int dayWidth; QAction* newEntryAction; QAction* removeEntryAction; QAction* demoAction; QAction* printAction; QAction* zoomInAction; QAction* zoomOutAction; Ui::MainWindow* ui; }; #endif /* MAINWINDOW_H */