File indexing completed on 2024-05-12 04:20:29

0001 /*
0002  * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
0003  *
0004  * This file is part of the KD Chart library.
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "KChartAbstractDiagram.h"
0010 #include "KChartAbstractDiagram_p.h"
0011 
0012 #include <QPainter>
0013 #include <QPainterPath>
0014 #include <QDebug>
0015 #include <QApplication>
0016 #include <QAbstractProxyModel>
0017 #include <QSizeF>
0018 #include <QPainterPath>
0019 
0020 #include "KChartAbstractCoordinatePlane.h"
0021 #include "KChartChart.h"
0022 #include "KChartDataValueAttributes.h"
0023 #include "KChartTextAttributes.h"
0024 #include "KChartAbstractThreeDAttributes.h"
0025 #include "KChartThreeDLineAttributes.h"
0026 #include "KChartPainterSaver_p.h"
0027 
0028 #include <limits>
0029 
0030 using namespace KChart;
0031 
0032 #define d d_func()
0033 
0034 AbstractDiagram::AbstractDiagram ( QWidget* parent, AbstractCoordinatePlane* plane )
0035     : QAbstractItemView ( parent ), _d( new Private() )
0036 {
0037     _d->init( plane );
0038     init();
0039 }
0040 
0041 AbstractDiagram::~AbstractDiagram()
0042 {
0043     Q_EMIT aboutToBeDestroyed();
0044     delete _d;
0045 }
0046 
0047 void AbstractDiagram::init()
0048 {
0049     _d->diagram = this;
0050     d->reverseMapper.setDiagram( this );
0051 }
0052 
0053 
0054 bool AbstractDiagram::compare( const AbstractDiagram* other ) const
0055 {
0056     if ( other == this ) return true;
0057     if ( !other ) {
0058         return false;
0059     }
0060     return  // compare QAbstractScrollArea properties
0061             (horizontalScrollBarPolicy() == other->horizontalScrollBarPolicy()) &&
0062             (verticalScrollBarPolicy()   == other->verticalScrollBarPolicy()) &&
0063             // compare QFrame properties
0064             (frameShadow()  == other->frameShadow()) &&
0065             (frameShape()   == other->frameShape()) &&
0066 // frameWidth is a read-only property defined by the style, it should not be in here:
0067             // (frameWidth()   == other->frameWidth()) &&
0068             (lineWidth()    == other->lineWidth()) &&
0069             (midLineWidth() == other->midLineWidth()) &&
0070             // compare QAbstractItemView properties
0071             (alternatingRowColors()  == other->alternatingRowColors()) &&
0072             (hasAutoScroll()         == other->hasAutoScroll()) &&
0073             (dragDropMode()          == other->dragDropMode()) &&
0074             (dragDropOverwriteMode() == other->dragDropOverwriteMode()) &&
0075             (horizontalScrollMode()  == other->horizontalScrollMode ()) &&
0076             (verticalScrollMode()    == other->verticalScrollMode()) &&
0077             (dragEnabled()           == other->dragEnabled()) &&
0078             (editTriggers()          == other->editTriggers()) &&
0079             (iconSize()              == other->iconSize()) &&
0080             (selectionBehavior()     == other->selectionBehavior()) &&
0081             (selectionMode()         == other->selectionMode()) &&
0082             (showDropIndicator()     == other->showDropIndicator()) &&
0083             (tabKeyNavigation()      == other->tabKeyNavigation()) &&
0084             (textElideMode()         == other->textElideMode()) &&
0085             // compare all of the properties stored in the attributes model
0086             attributesModel()->compare( other->attributesModel() ) &&
0087             // compare own properties
0088             (rootIndex().column()             == other->rootIndex().column()) &&
0089             (rootIndex().row()                == other->rootIndex().row()) &&
0090             (allowOverlappingDataValueTexts() == other->allowOverlappingDataValueTexts()) &&
0091             (antiAliasing()                   == other->antiAliasing()) &&
0092             (percentMode()                    == other->percentMode()) &&
0093             (datasetDimension()               == other->datasetDimension());
0094 }
0095 
0096 AbstractCoordinatePlane* AbstractDiagram::coordinatePlane() const
0097 {
0098     return d->plane;
0099 }
0100 
0101 const QPair<QPointF, QPointF> AbstractDiagram::dataBoundaries () const
0102 {
0103     if ( d->databoundariesDirty ) {
0104         d->databoundaries = calculateDataBoundaries ();
0105         d->databoundariesDirty = false;
0106     }
0107     return d->databoundaries;
0108 }
0109 
0110 void AbstractDiagram::setDataBoundariesDirty() const
0111 {
0112     d->databoundariesDirty = true;
0113     update();
0114 }
0115 
0116 void AbstractDiagram::resize(const QSizeF& size)
0117 {
0118     d->diagramSize = size;
0119     QAbstractItemView::resize( size.toSize() );
0120 }
0121 
0122 void AbstractDiagram::setModel( QAbstractItemModel * newModel )
0123 {
0124     if ( newModel == model() ) {
0125         return;
0126     }
0127 
0128     AttributesModel* amodel = new PrivateAttributesModel( newModel, this );
0129     amodel->initFrom( d->attributesModel );
0130     d->setAttributesModel(amodel);
0131 
0132     QAbstractItemView::setModel( newModel );
0133 
0134     scheduleDelayedItemsLayout();
0135     setDataBoundariesDirty();
0136     Q_EMIT modelsChanged();
0137 }
0138 
0139 void AbstractDiagram::setSelectionModel( QItemSelectionModel* newSelectionModel )
0140 {
0141     if ( selectionModel() )
0142     {
0143         disconnect( selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SIGNAL(modelsChanged()) );
0144         disconnect( selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SIGNAL(modelsChanged()) );
0145     }
0146     QAbstractItemView::setSelectionModel( newSelectionModel );
0147     if ( selectionModel() )
0148     {
0149         connect( selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SIGNAL(modelsChanged()) );
0150         connect( selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SIGNAL(modelsChanged()) );
0151     }
0152     Q_EMIT modelsChanged();
0153 }
0154 
0155 /* Sets an external AttributesModel on this diagram. By default, a diagram has it's
0156   own internal set of attributes, but an external one can be set. This can be used to
0157   share attributes between several diagrams. The diagram does not take ownership of the
0158   attributesmodel.
0159 
0160   @param amodel The AttributesModel to use for this diagram.
0161 */
0162 void AbstractDiagram::setAttributesModel( AttributesModel* amodel )
0163 {
0164     if ( amodel->sourceModel() != model() ) {
0165         qWarning("KChart::AbstractDiagram::setAttributesModel() failed: "
0166                  "Trying to set an attributesmodel which works on a different "
0167                  "model than the diagram.");
0168         return;
0169     }
0170     if ( qobject_cast<PrivateAttributesModel*>(amodel) ) {
0171         qWarning("KChart::AbstractDiagram::setAttributesModel() failed: "
0172                  "Trying to set an attributesmodel that is private to another diagram.");
0173         return;
0174     }
0175 
0176     d->setAttributesModel( amodel );
0177     scheduleDelayedItemsLayout();
0178     setDataBoundariesDirty();
0179     Q_EMIT modelsChanged();
0180 }
0181 
0182 bool AbstractDiagram::usesExternalAttributesModel() const
0183 {
0184     return d->usesExternalAttributesModel();
0185 }
0186 
0187 AttributesModel* AbstractDiagram::attributesModel() const
0188 {
0189     return d->attributesModel;
0190 }
0191 
0192 QModelIndex AbstractDiagram::conditionallyMapFromSource( const QModelIndex & index ) const
0193 {
0194     Q_ASSERT( !index.isValid() || index.model() == attributesModel() || index.model() == attributesModel()->sourceModel() );
0195     return index.model() == attributesModel() ? index : attributesModel()->mapFromSource( index );
0196 }
0197 
0198 /* \reimpl */
0199 void AbstractDiagram::setRootIndex( const QModelIndex& idx )
0200 {
0201     QAbstractItemView::setRootIndex( idx );
0202     setAttributesModelRootIndex( d->attributesModel->mapFromSource( idx ) );
0203 }
0204 
0205 void AbstractDiagram::setAttributesModelRootIndex( const QModelIndex& idx )
0206 {
0207     d->attributesModelRootIndex = idx;
0208     setDataBoundariesDirty();
0209     scheduleDelayedItemsLayout();
0210 }
0211 
0212 QModelIndex AbstractDiagram::attributesModelRootIndex() const
0213 {
0214     if ( !d->attributesModelRootIndex.isValid() )
0215         d->attributesModelRootIndex = d->attributesModel->mapFromSource( rootIndex() );
0216     return d->attributesModelRootIndex;
0217 }
0218 
0219 void AbstractDiagram::setCoordinatePlane( AbstractCoordinatePlane* parent )
0220 {
0221     d->plane = parent;
0222 }
0223 
0224 void AbstractDiagram::doItemsLayout()
0225 {
0226     if ( d->plane ) {
0227         d->plane->layoutDiagrams();
0228         update();
0229     }
0230     QAbstractItemView::doItemsLayout();
0231 }
0232 
0233 void AbstractDiagram::dataChanged( const QModelIndex &topLeft,
0234                                    const QModelIndex &bottomRight,
0235                                    const QVector<int> & )
0236 {
0237     Q_UNUSED( topLeft );
0238     Q_UNUSED( bottomRight );
0239     // We are still too dumb to do intelligent updates...
0240     setDataBoundariesDirty();
0241     scheduleDelayedItemsLayout();
0242 }
0243 
0244 
0245 void AbstractDiagram::setHidden( const QModelIndex & index, bool hidden )
0246 {
0247     d->attributesModel->setData(
0248         conditionallyMapFromSource( index ),
0249         QVariant::fromValue( hidden ),
0250         DataHiddenRole );
0251     Q_EMIT dataHidden();
0252 }
0253 
0254 void AbstractDiagram::setHidden( int dataset, bool hidden )
0255 {
0256     d->setDatasetAttrs( dataset, QVariant::fromValue( hidden ), DataHiddenRole );
0257     Q_EMIT dataHidden();
0258 }
0259 
0260 void AbstractDiagram::setHidden( bool hidden )
0261 {
0262     d->attributesModel->setModelData( QVariant::fromValue( hidden ), DataHiddenRole );
0263     Q_EMIT dataHidden();
0264 }
0265 
0266 bool AbstractDiagram::isHidden() const
0267 {
0268     return attributesModel()->modelData( DataHiddenRole ).value< bool >();
0269 }
0270 
0271 bool AbstractDiagram::isHidden( int dataset ) const
0272 {
0273     const QVariant boolFlag( d->datasetAttrs( dataset, DataHiddenRole ) );
0274     if ( boolFlag.isValid() )
0275         return boolFlag.value< bool >();
0276     return isHidden();
0277 }
0278 
0279 bool AbstractDiagram::isHidden( const QModelIndex & index ) const
0280 {
0281     const QVariant boolFlag( attributesModel()->data( conditionallyMapFromSource( index ),
0282                                                       DataHiddenRole ) );
0283     if ( boolFlag.isValid() ) {
0284         return boolFlag.value< bool >();
0285     }
0286     int dataset = index.column() / d->datasetDimension;
0287     return isHidden( dataset );
0288 }
0289 
0290 
0291 void AbstractDiagram::setDataValueAttributes( const QModelIndex & index,
0292                                               const DataValueAttributes & a )
0293 {
0294     d->attributesModel->setData( conditionallyMapFromSource( index ), QVariant::fromValue( a ),
0295                                  DataValueLabelAttributesRole );
0296     Q_EMIT propertiesChanged();
0297 }
0298 
0299 
0300 void AbstractDiagram::setDataValueAttributes( int dataset, const DataValueAttributes & a )
0301 {
0302     d->setDatasetAttrs( dataset, QVariant::fromValue( a ), DataValueLabelAttributesRole );
0303     Q_EMIT propertiesChanged();
0304 }
0305 
0306 DataValueAttributes AbstractDiagram::dataValueAttributes() const
0307 {
0308     return attributesModel()->modelData( KChart::DataValueLabelAttributesRole ).value< DataValueAttributes >();
0309 }
0310 
0311 DataValueAttributes AbstractDiagram::dataValueAttributes( int dataset ) const
0312 {
0313     /*
0314     The following did not work!
0315     (khz, 2008-01-25)
0316     If there was some attrs specified for the 0-th cells of a dataset,
0317     then this logic would return the cell's settings instead of the header settings:
0318 
0319     return qVariantValue<DataValueAttributes>(
0320         attributesModel()->data( attributesModel()->mapFromSource(columnToIndex( column )),
0321         KChart::DataValueLabelAttributesRole ) );
0322     */
0323 
0324     const QVariant headerAttrs(
0325         d->datasetAttrs( dataset, KChart::DataValueLabelAttributesRole ) );
0326     if ( headerAttrs.isValid() )
0327         return headerAttrs.value< DataValueAttributes >();
0328     return dataValueAttributes();
0329 }
0330 
0331 DataValueAttributes AbstractDiagram::dataValueAttributes( const QModelIndex & index ) const
0332 {
0333     return attributesModel()->data(
0334             conditionallyMapFromSource( index ),
0335             KChart::DataValueLabelAttributesRole ).value< DataValueAttributes >();
0336 }
0337 
0338 void AbstractDiagram::setDataValueAttributes( const DataValueAttributes & a )
0339 {
0340     d->attributesModel->setModelData( QVariant::fromValue( a ), DataValueLabelAttributesRole );
0341     Q_EMIT propertiesChanged();
0342 }
0343 
0344 void AbstractDiagram::setAllowOverlappingDataValueTexts( bool allow )
0345 {
0346     DataValueAttributes attrs = dataValueAttributes();
0347     attrs.setShowOverlappingDataLabels( allow );
0348     setDataValueAttributes( attrs );
0349     d->allowOverlappingDataValueTexts = allow;
0350     Q_EMIT propertiesChanged();
0351 }
0352 
0353 bool AbstractDiagram::allowOverlappingDataValueTexts() const
0354 {
0355     return d->allowOverlappingDataValueTexts;
0356 }
0357 
0358 void AbstractDiagram::setAntiAliasing( bool enabled )
0359 {
0360     d->antiAliasing = enabled;
0361     Q_EMIT propertiesChanged();
0362 }
0363 
0364 bool AbstractDiagram::antiAliasing() const
0365 {
0366     return d->antiAliasing;
0367 }
0368 
0369 void AbstractDiagram::setPercentMode ( bool percent )
0370 {
0371     d->percent = percent;
0372     Q_EMIT propertiesChanged();
0373 }
0374 
0375 bool AbstractDiagram::percentMode() const
0376 {
0377     return d->percent;
0378 }
0379 
0380 
0381 void AbstractDiagram::paintDataValueText( QPainter* painter,
0382                                           const QModelIndex& index,
0383                                           const QPointF& pos,
0384                                           qreal value )
0385 {
0386     d->paintDataValueText( painter, index, pos, value );
0387 }
0388 
0389 
0390 void AbstractDiagram::paintDataValueTexts( QPainter* painter )
0391 {
0392     if ( !checkInvariants() ) {
0393         return;
0394     }
0395 
0396     d->forgetAlreadyPaintedDataValues();
0397     const int rowCount = model()->rowCount( rootIndex() );
0398     const int columnCount = model()->columnCount( rootIndex() );
0399     for ( int column = 0; column < columnCount; column += datasetDimension() ) {
0400         for ( int row = 0; row < rowCount; ++row ) {
0401             QModelIndex index = model()->index( row, column, rootIndex() ); // checked
0402             qreal x;
0403             qreal y;
0404             if ( datasetDimension() == 1 ) {
0405                 x = row;
0406                 y = index.data().toReal();
0407             } else {
0408                 x = index.data().toReal();
0409                 y = model()->index( row, column + 1, rootIndex() ).data().toReal();
0410             }
0411             paintDataValueText( painter, index, coordinatePlane()->translate( QPointF( x, y ) ), y );
0412         }
0413     }
0414 }
0415 
0416 
0417 void AbstractDiagram::paintMarker( QPainter* painter,
0418                                    const DataValueAttributes& a,
0419                                    const QModelIndex& index,
0420                                    const QPointF& pos )
0421 {
0422     if ( !checkInvariants() || !a.isVisible() ) return;
0423     const MarkerAttributes ma = a.markerAttributes();
0424     if ( !ma.isVisible() ) return;
0425 
0426     const PainterSaver painterSaver( painter );
0427 
0428     QSizeF maSize = ma.markerSize();
0429     const qreal diagramWidth = d->diagramSize.width();
0430     const qreal diagramHeight = d->diagramSize.height();
0431 
0432     switch( ma.markerSizeMode() ) {
0433     case MarkerAttributes::AbsoluteSize:
0434         // Unscaled, i.e. without the painter's "zoom"
0435         maSize.rwidth()  /= painter->transform().m11();
0436         maSize.rheight() /= painter->transform().m22();
0437         break;
0438     case MarkerAttributes::AbsoluteSizeScaled:
0439         // Keep maSize as is. It is specified directly in pixels and desired
0440         // to be effected by the painter's "zoom".
0441         break;
0442     case MarkerAttributes::RelativeToDiagramWidthHeightMin:
0443         maSize *= qMin( diagramWidth, diagramHeight );
0444         break;
0445     }
0446 
0447     QBrush indexBrush( brush( index ) );
0448     QPen indexPen( ma.pen() );
0449     if ( ma.markerColor().isValid() )
0450         indexBrush.setColor( ma.markerColor() );
0451 
0452     paintMarker( painter, ma, indexBrush, indexPen, pos, maSize );
0453 
0454     // workaround: BC cannot be changed, otherwise we would pass the
0455     // index down to next-lower paintMarker function. So far, we
0456     // basically save a circle of radius maSize at pos in the
0457     // reverseMapper. This means that ^^^ this version of paintMarker
0458     // needs to be called to reverse-map the marker.
0459     if ((ma.markerStyle() == MarkerAttributes::Marker4Pixels) || (ma.markerStyle() == MarkerAttributes::Marker1Pixel)) {
0460         auto rect = QRectF({}, maSize);
0461         rect.moveCenter(pos);
0462         d->reverseMapper.addRect( index.row(), index.column(), rect );
0463     } else {
0464         d->reverseMapper.addCircle( index.row(), index.column(), pos, 2 * maSize );
0465     }
0466 }
0467 
0468 void AbstractDiagram::paintMarker( QPainter* painter,
0469                                    const QModelIndex& index,
0470                                    const QPointF& pos )
0471 {
0472     if ( !checkInvariants() ) return;
0473     paintMarker( painter, dataValueAttributes( index ), index, pos );
0474 }
0475 
0476 void AbstractDiagram::paintMarker( QPainter* painter,
0477                                    const MarkerAttributes& markerAttributes,
0478                                    const QBrush& brush,
0479                                    const QPen& pen,
0480                                    const QPointF& pos,
0481                                    const QSizeF& maSize )
0482 {
0483     const QPen oldPen( painter->pen() );
0484     // Pen is used to paint 4Pixels - 1 Pixel - Ring and FastCross types.
0485     // make sure to use the brush color - see above in those cases.
0486     const bool isFourPixels = (markerAttributes.markerStyle() == MarkerAttributes::Marker4Pixels);
0487     if ( isFourPixels || (markerAttributes.markerStyle() == MarkerAttributes::Marker1Pixel) ) {
0488         // for high-performance point charts with tiny point markers:
0489         painter->setPen( PrintingParameters::scalePen( QPen( brush.color().lighter() ) ) );
0490         if ( isFourPixels ) {
0491             const qreal x = pos.x();
0492             const qreal y = pos.y();
0493             painter->drawLine( QPointF(x-1.0,y-1.0),
0494                                QPointF(x+1.0,y-1.0) );
0495             painter->drawLine( QPointF(x-1.0,y),
0496                                QPointF(x+1.0,y) );
0497             painter->drawLine( QPointF(x-1.0,y+1.0),
0498                                QPointF(x+1.0,y+1.0) );
0499         }
0500         painter->drawPoint( pos );
0501     } else {
0502         const PainterSaver painterSaver( painter );
0503         QPen painterPen( pen );
0504         painter->setPen( PrintingParameters::scalePen( painterPen ) );
0505         painter->setBrush( brush );
0506         painter->setRenderHint ( QPainter::Antialiasing );
0507         painter->translate( pos );
0508         switch ( markerAttributes.markerStyle() ) {
0509             case MarkerAttributes::MarkerCircle:
0510             {
0511                 if ( markerAttributes.threeD() ) {
0512                     QRadialGradient grad;
0513                     grad.setCoordinateMode( QGradient::ObjectBoundingMode );
0514                     QColor drawColor = brush.color();
0515                     grad.setCenter( 0.5, 0.5 );
0516                     grad.setRadius( 1.0 );
0517                     grad.setFocalPoint( 0.35, 0.35 );
0518                     grad.setColorAt( 0.00, drawColor.lighter( 150 ) );
0519                     grad.setColorAt( 0.20, drawColor );
0520                     grad.setColorAt( 0.50, drawColor.darker( 150 ) );
0521                     grad.setColorAt( 0.75, drawColor.darker( 200 ) );
0522                     grad.setColorAt( 0.95, drawColor.darker( 250 ) );
0523                     grad.setColorAt( 1.00, drawColor.darker( 200 ) );
0524                     QBrush newBrush( grad );
0525                     newBrush.setTransform( brush.transform() );
0526                     painter->setBrush( newBrush );
0527                 }
0528                 painter->drawEllipse( QRectF( 0 - maSize.height()/2, 0 - maSize.width()/2,
0529                             maSize.height(), maSize.width()) );
0530             }
0531                 break;
0532             case MarkerAttributes::MarkerSquare:
0533                 {
0534                     QRectF rect( 0 - maSize.width()/2, 0 - maSize.height()/2,
0535                                 maSize.width(), maSize.height() );
0536                     painter->drawRect( rect );
0537                     break;
0538                 }
0539             case MarkerAttributes::MarkerDiamond:
0540                 {
0541                     QVector <QPointF > diamondPoints;
0542                     QPointF top, left, bottom, right;
0543                     top    = QPointF( 0, 0 - maSize.height()/2 );
0544                     left   = QPointF( 0 - maSize.width()/2, 0 );
0545                     bottom = QPointF( 0, maSize.height()/2 );
0546                     right  = QPointF( maSize.width()/2, 0 );
0547                     diamondPoints << top << left << bottom << right;
0548                     painter->drawPolygon( diamondPoints );
0549                     break;
0550                 }
0551             // both handled on top of the method:
0552             case MarkerAttributes::Marker1Pixel:
0553             case MarkerAttributes::Marker4Pixels:
0554                     break;
0555             case MarkerAttributes::MarkerRing:
0556                 {
0557                     painter->setBrush( Qt::NoBrush );
0558                     painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) );
0559                     painter->drawEllipse( QRectF( 0 - maSize.height()/2, 0 - maSize.width()/2,
0560                                         maSize.height(), maSize.width()) );
0561                     break;
0562                 }
0563             case MarkerAttributes::MarkerCross:
0564                 {
0565                     // Note: Markers can have outline,
0566                     //       so just drawing two rects is NOT the solution here!
0567                     const qreal w02 = maSize.width() * 0.2;
0568                     const qreal w05 = maSize.width() * 0.5;
0569                     const qreal h02 = maSize.height()* 0.2;
0570                     const qreal h05 = maSize.height()* 0.5;
0571                     QVector <QPointF > crossPoints;
0572                     QPointF p[12];
0573                     p[ 0] = QPointF( -w02, -h05 );
0574                     p[ 1] = QPointF( w02, -h05 );
0575                     p[ 2] = QPointF( w02, -h02 );
0576                     p[ 3] = QPointF( w05, -h02 );
0577                     p[ 4] = QPointF( w05,  h02 );
0578                     p[ 5] = QPointF( w02,  h02 );
0579                     p[ 6] = QPointF( w02,  h05 );
0580                     p[ 7] = QPointF( -w02,  h05 );
0581                     p[ 8] = QPointF( -w02,  h02 );
0582                     p[ 9] = QPointF( -w05,  h02 );
0583                     p[10] = QPointF( -w05, -h02 );
0584                     p[11] = QPointF( -w02, -h02 );
0585                     for ( int i=0; i<12; ++i )
0586                         crossPoints << p[i];
0587                     crossPoints << p[0];
0588                     painter->drawPolygon( crossPoints );
0589                     break;
0590                 }
0591             case MarkerAttributes::MarkerFastCross:
0592                 {
0593                     QPointF left, right, top, bottom;
0594                     left  = QPointF( -maSize.width()/2, 0 );
0595                     right = QPointF( maSize.width()/2, 0 );
0596                     top   = QPointF( 0, -maSize.height()/2 );
0597                     bottom= QPointF( 0, maSize.height()/2 );
0598                     painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) );
0599                     painter->drawLine( left, right );
0600                     painter->drawLine( top, bottom );
0601                     break;
0602                 }
0603             case MarkerAttributes::MarkerArrowDown:
0604                 {
0605                     QVector <QPointF > arrowPoints;
0606                     QPointF topLeft, topRight, bottom;
0607                     topLeft  = QPointF( 0 - maSize.width()/2, 0 - maSize.height()/2 );
0608                     topRight = QPointF( maSize.width()/2, 0 - maSize.height()/2 );
0609                     bottom   = QPointF( 0, maSize.height()/2 );
0610                     arrowPoints << topLeft << bottom << topRight;
0611                     painter->drawPolygon( arrowPoints );
0612                     break;
0613                 }
0614             case MarkerAttributes::MarkerArrowUp:
0615                 {
0616                     QVector <QPointF > arrowPoints;
0617                     QPointF top, bottomLeft, bottomRight;
0618                     top         = QPointF( 0, 0 - maSize.height()/2 );
0619                     bottomLeft  = QPointF( 0 - maSize.width()/2, maSize.height()/2 );
0620                     bottomRight = QPointF( maSize.width()/2, maSize.height()/2 );
0621                     arrowPoints << top << bottomLeft << bottomRight;
0622                     painter->drawPolygon( arrowPoints );
0623                     break;
0624                 }
0625             case MarkerAttributes::MarkerArrowRight:
0626                 {
0627                     QVector <QPointF > arrowPoints;
0628                     QPointF right, topLeft, bottomLeft;
0629                     right      = QPointF( maSize.width()/2, 0 );
0630                     topLeft    = QPointF( 0 - maSize.width()/2, 0 - maSize.height()/2 );
0631                     bottomLeft = QPointF( 0 - maSize.width()/2, maSize.height()/2 );
0632                     arrowPoints << topLeft << bottomLeft << right;
0633                     painter->drawPolygon( arrowPoints );
0634                     break;
0635                 }
0636             case MarkerAttributes::MarkerArrowLeft:
0637                 {
0638                     QVector <QPointF > arrowPoints;
0639                     QPointF left, topRight, bottomRight;
0640                     left        = QPointF( 0 - maSize.width()/2, 0 );
0641                     topRight    = QPointF( maSize.width()/2, 0 - maSize.height()/2 );
0642                     bottomRight = QPointF( maSize.width()/2, maSize.height()/2 );
0643                     arrowPoints << left << bottomRight << topRight;
0644                     painter->drawPolygon( arrowPoints );
0645                     break;
0646                 }
0647             case MarkerAttributes::MarkerBowTie:
0648             case MarkerAttributes::MarkerHourGlass:
0649                 {
0650                     QVector <QPointF > points;
0651                     QPointF topLeft, topRight, bottomLeft, bottomRight;
0652                     topLeft     = QPointF( 0 - maSize.width()/2, 0 - maSize.height()/2);
0653                     topRight    = QPointF( maSize.width()/2, 0 - maSize.height()/2 );
0654                     bottomLeft  = QPointF( 0 - maSize.width()/2, maSize.height()/2 );
0655                     bottomRight = QPointF( maSize.width()/2, maSize.height()/2 );
0656                     if ( markerAttributes.markerStyle() == MarkerAttributes::MarkerBowTie)
0657                         points << topLeft << bottomLeft << topRight << bottomRight;
0658                     else
0659                         points << topLeft << bottomRight << bottomLeft << topRight;
0660                     painter->drawPolygon( points );
0661                     break;
0662                 }
0663             case MarkerAttributes::MarkerStar:
0664                 {
0665                     const qreal w01 = maSize.width() * 0.1;
0666                     const qreal w05 = maSize.width() * 0.5;
0667                     const qreal h01 = maSize.height() * 0.1;
0668                     const qreal h05 = maSize.height() * 0.5;
0669                     QVector <QPointF > points;
0670                     QPointF p1 = QPointF(    0, -h05 );
0671                     QPointF p2 = QPointF( -w01, -h01 );
0672                     QPointF p3 = QPointF( -w05,    0 );
0673                     QPointF p4 = QPointF( -w01,  h01 );
0674                     QPointF p5 = QPointF(    0,  h05 );
0675                     QPointF p6 = QPointF(  w01,  h01 );
0676                     QPointF p7 = QPointF( w05,    0 );
0677                     QPointF p8 = QPointF( w01, -h01 );
0678                     points << p1 << p2 << p3 << p4 << p5 << p6 << p7 << p8;
0679                     painter->drawPolygon( points );
0680                     break;
0681                 }
0682             case MarkerAttributes::MarkerX:
0683                 {
0684                     const qreal w01 = maSize.width() * 0.1;
0685                     const qreal w04 = maSize.width() * 0.4;
0686                     const qreal w05 = maSize.width() * 0.5;
0687                     const qreal h01 = maSize.height() * 0.1;
0688                     const qreal h04 = maSize.height() * 0.4;
0689                     const qreal h05 = maSize.height() * 0.5;
0690                     QVector <QPointF > crossPoints;
0691                     QPointF p1 = QPointF( -w04, -h05 );
0692                     QPointF p2 = QPointF( -w05, -h04 );
0693                     QPointF p3 = QPointF( -w01,  0 );
0694                     QPointF p4 = QPointF( -w05,  h04 );
0695                     QPointF p5 = QPointF( -w04,  h05 );
0696                     QPointF p6 = QPointF(  0,    h01 );
0697                     QPointF p7 = QPointF(  w04,  h05 );
0698                     QPointF p8 = QPointF(  w05,  h04 );
0699                     QPointF p9 = QPointF(  w01,  0 );
0700                     QPointF p10 = QPointF( w05, -h04 );
0701                     QPointF p11 = QPointF( w04, -h05 );
0702                     QPointF p12 = QPointF( 0,   -h01 );
0703                     crossPoints << p1 << p2 << p3 << p4 << p5 << p6
0704                                 << p7 << p8 << p9 << p10 << p11 << p12;
0705                     painter->drawPolygon( crossPoints );
0706                     break;
0707                 }
0708             case MarkerAttributes::MarkerAsterisk:
0709                 {
0710                     // Note: Markers can have outline,
0711                     //       so just drawing three lines is NOT the solution here!
0712                     // The idea that we use is to draw 3 lines anyway, but convert their
0713                     // outlines to QPainterPaths which are then united and filled.
0714                     const qreal w04 = maSize.width() * 0.4;
0715                     const qreal h02 = maSize.height() * 0.2;
0716                     const qreal h05 = maSize.height() * 0.5;
0717                     //QVector <QPointF > crossPoints;
0718                     QPointF p1 = QPointF(    0, -h05 );
0719                     QPointF p2 = QPointF( -w04, -h02 );
0720                     QPointF p3 = QPointF( -w04,  h02 );
0721                     QPointF p4 = QPointF(    0,  h05 );
0722                     QPointF p5 = QPointF(  w04,  h02 );
0723                     QPointF p6 = QPointF(  w04, -h02 );
0724                     QPen pen = painter->pen();
0725                     QPainterPathStroker stroker;
0726                     stroker.setWidth( pen.widthF() );
0727                     stroker.setCapStyle( pen.capStyle() );
0728 
0729                     QPainterPath path;
0730                     QPainterPath dummyPath;
0731                     dummyPath.moveTo( p1 );
0732                     dummyPath.lineTo( p4 );
0733                     path = stroker.createStroke( dummyPath );
0734 
0735                     dummyPath = QPainterPath();
0736                     dummyPath.moveTo( p2 );
0737                     dummyPath.lineTo( p5 );
0738                     path = path.united( stroker.createStroke( dummyPath ) );
0739 
0740                     dummyPath = QPainterPath();
0741                     dummyPath.moveTo( p3 );
0742                     dummyPath.lineTo( p6 );
0743                     path = path.united( stroker.createStroke( dummyPath ) );
0744 
0745                     painter->drawPath( path );
0746                     break;
0747                 }
0748             case MarkerAttributes::MarkerHorizontalBar:
0749                 {
0750                     const qreal w05 = maSize.width() * 0.5;
0751                     const qreal h02 = maSize.height()* 0.2;
0752                     QVector <QPointF > points;
0753                     QPointF p1 = QPointF( -w05, -h02 );
0754                     QPointF p2 = QPointF( -w05,  h02 );
0755                     QPointF p3 = QPointF(  w05,  h02 );
0756                     QPointF p4 = QPointF(  w05, -h02 );
0757                     points << p1 << p2 << p3 << p4;
0758                     painter->drawPolygon( points );
0759                     break;
0760                 }
0761             case MarkerAttributes::MarkerVerticalBar:
0762                 {
0763                     const qreal w02 = maSize.width() * 0.2;
0764                     const qreal h05 = maSize.height()* 0.5;
0765                     QVector <QPointF > points;
0766                     QPointF p1 = QPointF( -w02, -h05 );
0767                     QPointF p2 = QPointF( -w02,  h05 );
0768                     QPointF p3 = QPointF(  w02,  h05 );
0769                     QPointF p4 = QPointF(  w02, -h05 );
0770                     points << p1 << p2 << p3 << p4;
0771                     painter->drawPolygon( points );
0772                     break;
0773                 }
0774             case MarkerAttributes::NoMarker:
0775                 break;
0776             case MarkerAttributes::PainterPathMarker:
0777                 {
0778                     QPainterPath path = markerAttributes.customMarkerPath();
0779                     const QRectF pathBoundingRect = path.boundingRect();
0780                     const qreal xScaling = maSize.height() / pathBoundingRect.height();
0781                     const qreal yScaling = maSize.width() / pathBoundingRect.width();
0782                     const qreal scaling = qMin( xScaling, yScaling );
0783                     painter->scale( scaling, scaling );
0784                     painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) );
0785                     painter->drawPath(path);
0786                     break;
0787                 }
0788             default:
0789                 Q_ASSERT_X ( false, "paintMarkers()",
0790                             "Type item does not match a defined Marker Type." );
0791         }
0792     }
0793     painter->setPen( oldPen );
0794 }
0795 
0796 void AbstractDiagram::paintMarkers( QPainter* painter )
0797 {
0798     if ( !checkInvariants() ) {
0799         return;
0800     }
0801 
0802     const int rowCount = model()->rowCount( rootIndex() );
0803     const int columnCount = model()->columnCount( rootIndex() );
0804     for ( int column = 0; column < columnCount; column += datasetDimension() ) {
0805         for ( int row = 0; row < rowCount; ++row ) {
0806             QModelIndex index = model()->index( row, column, rootIndex() ); // checked
0807             qreal x;
0808             qreal y;
0809             if ( datasetDimension() == 1 ) {
0810                 x = row;
0811                 y = index.data().toReal();
0812             } else {
0813                 x = index.data().toReal();
0814                 y = model()->index( row, column + 1, rootIndex() ).data().toReal();
0815             }
0816             paintMarker( painter, index, coordinatePlane()->translate( QPointF( x, y ) ) );
0817         }
0818     }
0819 }
0820 
0821 
0822 void AbstractDiagram::setPen( const QModelIndex& index, const QPen& pen )
0823 {
0824     attributesModel()->setData(
0825         conditionallyMapFromSource( index ),
0826         QVariant::fromValue( pen ), DatasetPenRole );
0827     Q_EMIT propertiesChanged();
0828 }
0829 
0830 void AbstractDiagram::setPen( const QPen& pen )
0831 {
0832     attributesModel()->setModelData(
0833         QVariant::fromValue( pen ), DatasetPenRole );
0834     Q_EMIT propertiesChanged();
0835 }
0836 
0837 void AbstractDiagram::setPen( int dataset, const QPen& pen )
0838 {
0839     d->setDatasetAttrs( dataset, QVariant::fromValue( pen ), DatasetPenRole );
0840     Q_EMIT propertiesChanged();
0841 }
0842 
0843 QPen AbstractDiagram::pen() const
0844 {
0845     return attributesModel()->data( DatasetPenRole ).value< QPen >();
0846 }
0847 
0848 QPen AbstractDiagram::pen( int dataset ) const
0849 {
0850     const QVariant penSettings( d->datasetAttrs( dataset, DatasetPenRole ) );
0851     if ( penSettings.isValid() )
0852         return penSettings.value< QPen >();
0853     return pen();
0854 }
0855 
0856 QPen AbstractDiagram::pen( const QModelIndex& index ) const
0857 {
0858     return  attributesModel()->data(
0859             conditionallyMapFromSource( index ),
0860             DatasetPenRole ).value< QPen >();
0861 }
0862 
0863 void AbstractDiagram::setBrush( const QModelIndex& index, const QBrush& brush )
0864 {
0865     attributesModel()->setData(
0866         conditionallyMapFromSource( index ),
0867         QVariant::fromValue( brush ), DatasetBrushRole );
0868     Q_EMIT propertiesChanged();
0869 }
0870 
0871 void AbstractDiagram::setBrush( const QBrush& brush )
0872 {
0873     attributesModel()->setModelData(
0874         QVariant::fromValue( brush ), DatasetBrushRole );
0875     Q_EMIT propertiesChanged();
0876 }
0877 
0878 void AbstractDiagram::setBrush( int dataset, const QBrush& brush )
0879 {
0880     d->setDatasetAttrs( dataset, QVariant::fromValue( brush ), DatasetBrushRole );
0881     Q_EMIT propertiesChanged();
0882 }
0883 
0884 QBrush AbstractDiagram::brush() const
0885 {
0886     return attributesModel()->data( DatasetBrushRole ).value< QBrush >();
0887 }
0888 
0889 QBrush AbstractDiagram::brush( int dataset ) const
0890 {
0891     const QVariant brushSettings( d->datasetAttrs( dataset, DatasetBrushRole ) );
0892     if ( brushSettings.isValid() )
0893         return brushSettings.value< QBrush >();
0894     return brush();
0895 }
0896 
0897 QBrush AbstractDiagram::brush( const QModelIndex& index ) const
0898 {
0899     return 
0900         attributesModel()->data( conditionallyMapFromSource( index ), DatasetBrushRole ).value< QBrush >();
0901 }
0902 
0903 /*
0904   * Sets the unit prefix for one value
0905   * @param prefix the prefix to be set
0906   * @param column the value using that prefix
0907   * @param orientation the orientantion of the axis to set
0908   */
0909 void AbstractDiagram::setUnitPrefix( const QString& prefix, int column, Qt::Orientation orientation )
0910 {
0911     d->unitPrefixMap[ column ][ orientation ]= prefix;
0912 }
0913 
0914 /*
0915   * Sets the unit prefix for all values
0916   * @param prefix the prefix to be set
0917   * @param orientation the orientantion of the axis to set
0918   */
0919 void AbstractDiagram::setUnitPrefix( const QString& prefix, Qt::Orientation orientation )
0920 {
0921     d->unitPrefix[ orientation ] = prefix;
0922 }
0923 
0924 /*
0925   * Sets the unit suffix for one value
0926   * @param suffix the suffix to be set
0927   * @param column the value using that suffix
0928   * @param orientation the orientantion of the axis to set
0929   */
0930 void AbstractDiagram::setUnitSuffix( const QString& suffix, int column, Qt::Orientation orientation )
0931 {
0932     d->unitSuffixMap[ column ][ orientation ]= suffix;
0933 }
0934 
0935 /*
0936   * Sets the unit suffix for all values
0937   * @param suffix the suffix to be set
0938   * @param orientation the orientantion of the axis to set
0939   */
0940 void AbstractDiagram::setUnitSuffix( const QString& suffix, Qt::Orientation orientation )
0941 {
0942     d->unitSuffix[ orientation ] = suffix;
0943 }
0944 
0945 /*
0946   * Returns the unit prefix for a special value
0947   * @param column the value which's prefix is requested
0948   * @param orientation the orientation of the axis
0949   * @param fallback if true, the global prefix is return when no specific one is set for that value
0950   * @return the unit prefix
0951   */
0952 QString AbstractDiagram::unitPrefix( int column, Qt::Orientation orientation, bool fallback ) const
0953 {
0954     if ( !fallback || d->unitPrefixMap[ column ].contains( orientation ) )
0955         return d->unitPrefixMap[ column ][ orientation ];
0956     return d->unitPrefix[ orientation ];
0957 }
0958 
0959 /* Returns the global unit prefix
0960   * @param orientation the orientation of the axis
0961   * @return the unit prefix
0962   */
0963 QString AbstractDiagram::unitPrefix( Qt::Orientation orientation ) const
0964 {
0965     return d->unitPrefix[ orientation ];
0966 }
0967 
0968 /*
0969   * Returns the unit suffix for a special value
0970   * @param column the value which's suffix is requested
0971   * @param orientation the orientation of the axis
0972   * @param fallback if true, the global suffix is return when no specific one is set for that value
0973   * @return the unit suffix
0974   */
0975 QString AbstractDiagram::unitSuffix( int column, Qt::Orientation orientation, bool fallback ) const
0976 {
0977     if ( !fallback || d->unitSuffixMap[ column ].contains( orientation ) )
0978         return d->unitSuffixMap[ column ][ orientation ];
0979     return d->unitSuffix[ orientation ];
0980 }
0981 
0982 /* Returns the global unit suffix
0983   * @param orientation the orientation of the axis
0984   * @return the unit suffix
0985   */
0986 QString AbstractDiagram::unitSuffix( Qt::Orientation orientation ) const
0987 {
0988     return d->unitSuffix[ orientation ];
0989 }
0990 
0991 // implement QAbstractItemView:
0992 QRect AbstractDiagram::visualRect( const QModelIndex &index ) const
0993 {
0994     return d->reverseMapper.boundingRect( index.row(), index.column() ).toRect();
0995 }
0996 
0997 void AbstractDiagram::scrollTo(const QModelIndex &, ScrollHint )
0998 {}
0999 
1000 // indexAt ... down below
1001 
1002 QModelIndex AbstractDiagram::moveCursor(CursorAction, Qt::KeyboardModifiers )
1003 { return QModelIndex(); }
1004 
1005 int AbstractDiagram::horizontalOffset() const
1006 { return 0; }
1007 
1008 int AbstractDiagram::verticalOffset() const
1009 { return 0; }
1010 
1011 bool AbstractDiagram::isIndexHidden(const QModelIndex &) const
1012 { return true; }
1013 
1014 void AbstractDiagram::setSelection(const QRect& rect , QItemSelectionModel::SelectionFlags command )
1015 {
1016     const QModelIndexList indexes = d->indexesIn( rect );
1017     QItemSelection selection;
1018     for ( const QModelIndex& index : indexes )
1019     {
1020         selection.append( QItemSelectionRange( index ) );
1021     }
1022     selectionModel()->select( selection, command );
1023 }
1024 
1025 QRegion AbstractDiagram::visualRegionForSelection(const QItemSelection &selection) const
1026 {
1027     QPolygonF polygon;
1028     const auto indexes = selection.indexes();
1029     polygon.reserve(indexes.count());
1030     for ( const QModelIndex& index : indexes )
1031     {
1032         polygon << d->reverseMapper.polygon(index.row(), index.column());
1033     }
1034     return polygon.isEmpty() ? QRegion() : QRegion( polygon.toPolygon() );
1035 }
1036 
1037 QRegion AbstractDiagram::visualRegion(const QModelIndex &index) const
1038 {
1039     QPolygonF polygon = d->reverseMapper.polygon(index.row(), index.column());
1040     return polygon.isEmpty() ? QRegion() : QRegion( polygon.toPolygon() );
1041 }
1042 
1043 void KChart::AbstractDiagram::useDefaultColors( )
1044 {
1045     d->attributesModel->setPaletteType( AttributesModel::PaletteTypeDefault );
1046 }
1047 
1048 void KChart::AbstractDiagram::useSubduedColors( )
1049 {
1050     d->attributesModel->setPaletteType( AttributesModel::PaletteTypeSubdued );
1051 }
1052 
1053 void KChart::AbstractDiagram::useRainbowColors( )
1054 {
1055     d->attributesModel->setPaletteType( AttributesModel::PaletteTypeRainbow );
1056 }
1057 
1058 QStringList AbstractDiagram::itemRowLabels() const
1059 {
1060     QStringList ret;
1061     if ( model() ) {
1062         //qDebug() << "AbstractDiagram::itemRowLabels(): " << attributesModel()->rowCount(attributesModelRootIndex()) << "entries";
1063         const int rowCount = attributesModel()->rowCount(attributesModelRootIndex());
1064         for ( int i = 0; i < rowCount; ++i ) {
1065             //qDebug() << "item row label: " << attributesModel()->headerData( i, Qt::Vertical, Qt::DisplayRole ).toString();
1066             ret << unitPrefix( i, Qt::Horizontal, true ) +
1067                    attributesModel()->headerData( i, Qt::Vertical, Qt::DisplayRole ).toString() +
1068                    unitSuffix( i, Qt::Horizontal, true );
1069         }
1070     }
1071     return ret;
1072 }
1073 
1074 QStringList AbstractDiagram::datasetLabels() const
1075 {
1076     QStringList ret;
1077     if ( !model() ) {
1078         return ret;
1079     }
1080     const int datasetCount = d->datasetCount();
1081     for ( int i = 0; i < datasetCount; ++i ) {
1082         ret << d->datasetAttrs( i, Qt::DisplayRole ).toString();
1083     }
1084     return ret;
1085 }
1086 
1087 QList<QBrush> AbstractDiagram::datasetBrushes() const
1088 {
1089     QList<QBrush> ret;
1090     if ( !model() ) {
1091         return ret;
1092     }
1093     const int datasetCount = d->datasetCount();
1094     for ( int i = 0; i < datasetCount; ++i ) {
1095         ret << brush( i );
1096     }
1097     return ret;
1098 }
1099 
1100 QList<QPen> AbstractDiagram::datasetPens() const
1101 {
1102     QList<QPen> ret;
1103     if ( !model() ) {
1104         return ret;
1105     }
1106     const int datasetCount = d->datasetCount();
1107     for ( int i = 0; i < datasetCount; ++i ) {
1108         ret << pen( i );
1109     }
1110     return ret;
1111 }
1112 
1113 QList<MarkerAttributes> AbstractDiagram::datasetMarkers() const
1114 {
1115     QList<MarkerAttributes> ret;
1116     if ( !model() ) {
1117         return ret;
1118     }
1119     const int datasetCount = d->datasetCount();
1120     for ( int i = 0; i < datasetCount; ++i ) {
1121         ret << dataValueAttributes( i ).markerAttributes();
1122     }
1123     return ret;
1124 }
1125 
1126 bool AbstractDiagram::checkInvariants( bool justReturnTheStatus ) const
1127 {
1128     if ( ! justReturnTheStatus ) {
1129         Q_ASSERT_X ( model(), "AbstractDiagram::checkInvariants()",
1130                     "There is no usable model set, for the diagram." );
1131 
1132         Q_ASSERT_X ( coordinatePlane(), "AbstractDiagram::checkInvariants()",
1133                     "There is no usable coordinate plane set, for the diagram." );
1134     }
1135     return model() && coordinatePlane();
1136 }
1137 
1138 int AbstractDiagram::datasetDimension( ) const
1139 {
1140     return d->datasetDimension;
1141 }
1142 
1143 void AbstractDiagram::setDatasetDimension( int dimension )
1144 {
1145     Q_UNUSED( dimension );
1146     qDebug() << "Setting the dataset dimension using AbstractDiagram::setDatasetDimension is "
1147                 "obsolete. Use the specific diagram types instead.";
1148 }
1149 
1150 void AbstractDiagram::setDatasetDimensionInternal( int dimension )
1151 {
1152     Q_ASSERT( dimension != 0 );
1153     if ( d->datasetDimension == dimension ) {
1154         return;
1155     }
1156     d->datasetDimension = dimension;
1157     d->attributesModel->setDatasetDimension( dimension );
1158     setDataBoundariesDirty();
1159     Q_EMIT layoutChanged( this );
1160 }
1161 
1162 qreal AbstractDiagram::valueForCell( int row, int column ) const
1163 {
1164     if ( !d->attributesModel->hasIndex( row, column, attributesModelRootIndex() ) ) {
1165         qWarning() << "AbstractDiagram::valueForCell(): Requesting value for invalid index!";
1166         return std::numeric_limits<qreal>::quiet_NaN();
1167     }
1168     return d->attributesModel->data(
1169             d->attributesModel->index( row, column, attributesModelRootIndex() ) ).toReal(); // checked
1170 }
1171 
1172 void AbstractDiagram::update() const
1173 {
1174     if ( d->plane ) {
1175         d->plane->update();
1176     }
1177 }
1178 
1179 QModelIndex AbstractDiagram::indexAt( const QPoint& point ) const
1180 {
1181     return d->indexAt( point );
1182 }
1183 
1184 QModelIndexList AbstractDiagram::indexesAt( const QPoint& point ) const
1185 {
1186     return d->indexesAt( point );
1187 }
1188 
1189 QModelIndexList AbstractDiagram::indexesIn( const QRect& rect ) const
1190 {
1191     return d->indexesIn( rect );
1192 }