File indexing completed on 2024-05-12 15:54:12

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