1 /*
2  * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB.  All rights reserved.
3  *
4  * This file is part of the KD Chart library.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #include "KChartAbstractDiagram.h"
21 #include "KChartAbstractDiagram_p.h"
22 
23 #include <QPainter>
24 #include <QPainterPath>
25 #include <QDebug>
26 #include <QApplication>
27 #include <QAbstractProxyModel>
28 #include <QSizeF>
29 #include <QPainterPath>
30 
31 #include "KChartAbstractCoordinatePlane.h"
32 #include "KChartChart.h"
33 #include "KChartDataValueAttributes.h"
34 #include "KChartTextAttributes.h"
35 #include "KChartMarkerAttributes.h"
36 #include "KChartAbstractThreeDAttributes.h"
37 #include "KChartThreeDLineAttributes.h"
38 #include "KChartPainterSaver_p.h"
39 
40 #include <limits>
41 
42 using namespace KChart;
43 
44 #define d d_func()
45 
AbstractDiagram(QWidget * parent,AbstractCoordinatePlane * plane)46 AbstractDiagram::AbstractDiagram ( QWidget* parent, AbstractCoordinatePlane* plane )
47     : QAbstractItemView ( parent ), _d( new Private() )
48 {
49     _d->init( plane );
50     init();
51 }
52 
~AbstractDiagram()53 AbstractDiagram::~AbstractDiagram()
54 {
55     Q_EMIT aboutToBeDestroyed();
56     delete _d;
57 }
58 
init()59 void AbstractDiagram::init()
60 {
61     _d->diagram = this;
62     d->reverseMapper.setDiagram( this );
63 }
64 
65 
compare(const AbstractDiagram * other) const66 bool AbstractDiagram::compare( const AbstractDiagram* other ) const
67 {
68     if ( other == this ) return true;
69     if ( !other ) {
70         return false;
71     }
72     return  // compare QAbstractScrollArea properties
73             (horizontalScrollBarPolicy() == other->horizontalScrollBarPolicy()) &&
74             (verticalScrollBarPolicy()   == other->verticalScrollBarPolicy()) &&
75             // compare QFrame properties
76             (frameShadow()  == other->frameShadow()) &&
77             (frameShape()   == other->frameShape()) &&
78 // frameWidth is a read-only property defined by the style, it should not be in here:
79             // (frameWidth()   == other->frameWidth()) &&
80             (lineWidth()    == other->lineWidth()) &&
81             (midLineWidth() == other->midLineWidth()) &&
82             // compare QAbstractItemView properties
83             (alternatingRowColors()  == other->alternatingRowColors()) &&
84             (hasAutoScroll()         == other->hasAutoScroll()) &&
85             (dragDropMode()          == other->dragDropMode()) &&
86             (dragDropOverwriteMode() == other->dragDropOverwriteMode()) &&
87             (horizontalScrollMode()  == other->horizontalScrollMode ()) &&
88             (verticalScrollMode()    == other->verticalScrollMode()) &&
89             (dragEnabled()           == other->dragEnabled()) &&
90             (editTriggers()          == other->editTriggers()) &&
91             (iconSize()              == other->iconSize()) &&
92             (selectionBehavior()     == other->selectionBehavior()) &&
93             (selectionMode()         == other->selectionMode()) &&
94             (showDropIndicator()     == other->showDropIndicator()) &&
95             (tabKeyNavigation()      == other->tabKeyNavigation()) &&
96             (textElideMode()         == other->textElideMode()) &&
97             // compare all of the properties stored in the attributes model
98             attributesModel()->compare( other->attributesModel() ) &&
99             // compare own properties
100             (rootIndex().column()             == other->rootIndex().column()) &&
101             (rootIndex().row()                == other->rootIndex().row()) &&
102             (allowOverlappingDataValueTexts() == other->allowOverlappingDataValueTexts()) &&
103             (antiAliasing()                   == other->antiAliasing()) &&
104             (percentMode()                    == other->percentMode()) &&
105             (datasetDimension()               == other->datasetDimension());
106 }
107 
coordinatePlane() const108 AbstractCoordinatePlane* AbstractDiagram::coordinatePlane() const
109 {
110     return d->plane;
111 }
112 
dataBoundaries() const113 const QPair<QPointF, QPointF> AbstractDiagram::dataBoundaries () const
114 {
115     if ( d->databoundariesDirty ) {
116         d->databoundaries = calculateDataBoundaries ();
117         d->databoundariesDirty = false;
118     }
119     return d->databoundaries;
120 }
121 
setDataBoundariesDirty() const122 void AbstractDiagram::setDataBoundariesDirty() const
123 {
124     d->databoundariesDirty = true;
125     update();
126 }
127 
resize(const QSizeF & size)128 void AbstractDiagram::resize(const QSizeF& size)
129 {
130     d->diagramSize = size;
131     QAbstractItemView::resize( size.toSize() );
132 }
133 
setModel(QAbstractItemModel * newModel)134 void AbstractDiagram::setModel( QAbstractItemModel * newModel )
135 {
136     if ( newModel == model() ) {
137         return;
138     }
139 
140     AttributesModel* amodel = new PrivateAttributesModel( newModel, this );
141     amodel->initFrom( d->attributesModel );
142     d->setAttributesModel(amodel);
143 
144     QAbstractItemView::setModel( newModel );
145 
146     scheduleDelayedItemsLayout();
147     setDataBoundariesDirty();
148     Q_EMIT modelsChanged();
149 }
150 
setSelectionModel(QItemSelectionModel * newSelectionModel)151 void AbstractDiagram::setSelectionModel( QItemSelectionModel* newSelectionModel )
152 {
153     if ( selectionModel() )
154     {
155         disconnect( selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SIGNAL(modelsChanged()) );
156         disconnect( selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SIGNAL(modelsChanged()) );
157     }
158     QAbstractItemView::setSelectionModel( newSelectionModel );
159     if ( selectionModel() )
160     {
161         connect( selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SIGNAL(modelsChanged()) );
162         connect( selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SIGNAL(modelsChanged()) );
163     }
164     Q_EMIT modelsChanged();
165 }
166 
167 /* Sets an external AttributesModel on this diagram. By default, a diagram has it's
168   own internal set of attributes, but an external one can be set. This can be used to
169   share attributes between several diagrams. The diagram does not take ownership of the
170   attributesmodel.
171 
172   @param amodel The AttributesModel to use for this diagram.
173 */
setAttributesModel(AttributesModel * amodel)174 void AbstractDiagram::setAttributesModel( AttributesModel* amodel )
175 {
176     if ( amodel->sourceModel() != model() ) {
177         qWarning("KChart::AbstractDiagram::setAttributesModel() failed: "
178                  "Trying to set an attributesmodel which works on a different "
179                  "model than the diagram.");
180         return;
181     }
182     if ( qobject_cast<PrivateAttributesModel*>(amodel) ) {
183         qWarning("KChart::AbstractDiagram::setAttributesModel() failed: "
184                  "Trying to set an attributesmodel that is private to another diagram.");
185         return;
186     }
187 
188     d->setAttributesModel( amodel );
189     scheduleDelayedItemsLayout();
190     setDataBoundariesDirty();
191     Q_EMIT modelsChanged();
192 }
193 
usesExternalAttributesModel() const194 bool AbstractDiagram::usesExternalAttributesModel() const
195 {
196     return d->usesExternalAttributesModel();
197 }
198 
attributesModel() const199 AttributesModel* AbstractDiagram::attributesModel() const
200 {
201     return d->attributesModel;
202 }
203 
conditionallyMapFromSource(const QModelIndex & index) const204 QModelIndex AbstractDiagram::conditionallyMapFromSource( const QModelIndex & index ) const
205 {
206     Q_ASSERT( !index.isValid() || index.model() == attributesModel() || index.model() == attributesModel()->sourceModel() );
207     return index.model() == attributesModel() ? index : attributesModel()->mapFromSource( index );
208 }
209 
210 /* \reimpl */
setRootIndex(const QModelIndex & idx)211 void AbstractDiagram::setRootIndex( const QModelIndex& idx )
212 {
213     QAbstractItemView::setRootIndex( idx );
214     setAttributesModelRootIndex( d->attributesModel->mapFromSource( idx ) );
215 }
216 
setAttributesModelRootIndex(const QModelIndex & idx)217 void AbstractDiagram::setAttributesModelRootIndex( const QModelIndex& idx )
218 {
219     d->attributesModelRootIndex = idx;
220     setDataBoundariesDirty();
221     scheduleDelayedItemsLayout();
222 }
223 
attributesModelRootIndex() const224 QModelIndex AbstractDiagram::attributesModelRootIndex() const
225 {
226     if ( !d->attributesModelRootIndex.isValid() )
227         d->attributesModelRootIndex = d->attributesModel->mapFromSource( rootIndex() );
228     return d->attributesModelRootIndex;
229 }
230 
setCoordinatePlane(AbstractCoordinatePlane * parent)231 void AbstractDiagram::setCoordinatePlane( AbstractCoordinatePlane* parent )
232 {
233     d->plane = parent;
234 }
235 
doItemsLayout()236 void AbstractDiagram::doItemsLayout()
237 {
238     if ( d->plane ) {
239         d->plane->layoutDiagrams();
240         update();
241     }
242     QAbstractItemView::doItemsLayout();
243 }
244 
dataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight,const QVector<int> &)245 void AbstractDiagram::dataChanged( const QModelIndex &topLeft,
246                                    const QModelIndex &bottomRight,
247                                    const QVector<int> & )
248 {
249     Q_UNUSED( topLeft );
250     Q_UNUSED( bottomRight );
251     // We are still too dumb to do intelligent updates...
252     setDataBoundariesDirty();
253     scheduleDelayedItemsLayout();
254 }
255 
256 
setHidden(const QModelIndex & index,bool hidden)257 void AbstractDiagram::setHidden( const QModelIndex & index, bool hidden )
258 {
259     d->attributesModel->setData(
260         conditionallyMapFromSource( index ),
261         QVariant::fromValue( hidden ),
262         DataHiddenRole );
263     Q_EMIT dataHidden();
264 }
265 
setHidden(int dataset,bool hidden)266 void AbstractDiagram::setHidden( int dataset, bool hidden )
267 {
268     d->setDatasetAttrs( dataset, QVariant::fromValue( hidden ), DataHiddenRole );
269     Q_EMIT dataHidden();
270 }
271 
setHidden(bool hidden)272 void AbstractDiagram::setHidden( bool hidden )
273 {
274     d->attributesModel->setModelData( QVariant::fromValue( hidden ), DataHiddenRole );
275     Q_EMIT dataHidden();
276 }
277 
isHidden() const278 bool AbstractDiagram::isHidden() const
279 {
280     return attributesModel()->modelData( DataHiddenRole ).value< bool >();
281 }
282 
isHidden(int dataset) const283 bool AbstractDiagram::isHidden( int dataset ) const
284 {
285     const QVariant boolFlag( d->datasetAttrs( dataset, DataHiddenRole ) );
286     if ( boolFlag.isValid() )
287         return boolFlag.value< bool >();
288     return isHidden();
289 }
290 
isHidden(const QModelIndex & index) const291 bool AbstractDiagram::isHidden( const QModelIndex & index ) const
292 {
293     const QVariant boolFlag( attributesModel()->data( conditionallyMapFromSource( index ),
294                                                       DataHiddenRole ) );
295     if ( boolFlag.isValid() ) {
296         return boolFlag.value< bool >();
297     }
298     int dataset = index.column() / d->datasetDimension;
299     return isHidden( dataset );
300 }
301 
302 
setDataValueAttributes(const QModelIndex & index,const DataValueAttributes & a)303 void AbstractDiagram::setDataValueAttributes( const QModelIndex & index,
304                                               const DataValueAttributes & a )
305 {
306     d->attributesModel->setData( conditionallyMapFromSource( index ), QVariant::fromValue( a ),
307                                  DataValueLabelAttributesRole );
308     Q_EMIT propertiesChanged();
309 }
310 
311 
setDataValueAttributes(int dataset,const DataValueAttributes & a)312 void AbstractDiagram::setDataValueAttributes( int dataset, const DataValueAttributes & a )
313 {
314     d->setDatasetAttrs( dataset, QVariant::fromValue( a ), DataValueLabelAttributesRole );
315     Q_EMIT propertiesChanged();
316 }
317 
dataValueAttributes() const318 DataValueAttributes AbstractDiagram::dataValueAttributes() const
319 {
320     return attributesModel()->modelData( KChart::DataValueLabelAttributesRole ).value< DataValueAttributes >();
321 }
322 
dataValueAttributes(int dataset) const323 DataValueAttributes AbstractDiagram::dataValueAttributes( int dataset ) const
324 {
325     /*
326     The following did not work!
327     (khz, 2008-01-25)
328     If there was some attrs specified for the 0-th cells of a dataset,
329     then this logic would return the cell's settings instead of the header settings:
330 
331     return qVariantValue<DataValueAttributes>(
332         attributesModel()->data( attributesModel()->mapFromSource(columnToIndex( column )),
333         KChart::DataValueLabelAttributesRole ) );
334     */
335 
336     const QVariant headerAttrs(
337         d->datasetAttrs( dataset, KChart::DataValueLabelAttributesRole ) );
338     if ( headerAttrs.isValid() )
339         return headerAttrs.value< DataValueAttributes >();
340     return dataValueAttributes();
341 }
342 
dataValueAttributes(const QModelIndex & index) const343 DataValueAttributes AbstractDiagram::dataValueAttributes( const QModelIndex & index ) const
344 {
345     return attributesModel()->data(
346             conditionallyMapFromSource( index ),
347             KChart::DataValueLabelAttributesRole ).value< DataValueAttributes >();
348 }
349 
setDataValueAttributes(const DataValueAttributes & a)350 void AbstractDiagram::setDataValueAttributes( const DataValueAttributes & a )
351 {
352     d->attributesModel->setModelData( QVariant::fromValue( a ), DataValueLabelAttributesRole );
353     Q_EMIT propertiesChanged();
354 }
355 
setAllowOverlappingDataValueTexts(bool allow)356 void AbstractDiagram::setAllowOverlappingDataValueTexts( bool allow )
357 {
358     DataValueAttributes attrs = dataValueAttributes();
359     attrs.setShowOverlappingDataLabels( allow );
360     setDataValueAttributes( attrs );
361     d->allowOverlappingDataValueTexts = allow;
362     Q_EMIT propertiesChanged();
363 }
364 
allowOverlappingDataValueTexts() const365 bool AbstractDiagram::allowOverlappingDataValueTexts() const
366 {
367     return d->allowOverlappingDataValueTexts;
368 }
369 
setAntiAliasing(bool enabled)370 void AbstractDiagram::setAntiAliasing( bool enabled )
371 {
372     d->antiAliasing = enabled;
373     Q_EMIT propertiesChanged();
374 }
375 
antiAliasing() const376 bool AbstractDiagram::antiAliasing() const
377 {
378     return d->antiAliasing;
379 }
380 
setPercentMode(bool percent)381 void AbstractDiagram::setPercentMode ( bool percent )
382 {
383     d->percent = percent;
384     Q_EMIT propertiesChanged();
385 }
386 
percentMode() const387 bool AbstractDiagram::percentMode() const
388 {
389     return d->percent;
390 }
391 
392 
paintDataValueText(QPainter * painter,const QModelIndex & index,const QPointF & pos,qreal value)393 void AbstractDiagram::paintDataValueText( QPainter* painter,
394                                           const QModelIndex& index,
395                                           const QPointF& pos,
396                                           qreal value )
397 {
398     d->paintDataValueText( painter, index, pos, value );
399 }
400 
401 
paintDataValueTexts(QPainter * painter)402 void AbstractDiagram::paintDataValueTexts( QPainter* painter )
403 {
404     if ( !checkInvariants() ) {
405         return;
406     }
407 
408     d->forgetAlreadyPaintedDataValues();
409     const int rowCount = model()->rowCount( rootIndex() );
410     const int columnCount = model()->columnCount( rootIndex() );
411     for ( int column = 0; column < columnCount; column += datasetDimension() ) {
412         for ( int row = 0; row < rowCount; ++row ) {
413             QModelIndex index = model()->index( row, column, rootIndex() ); // checked
414             qreal x;
415             qreal y;
416             if ( datasetDimension() == 1 ) {
417                 x = row;
418                 y = index.data().toReal();
419             } else {
420                 x = index.data().toReal();
421                 y = model()->index( row, column + 1, rootIndex() ).data().toReal();
422             }
423             paintDataValueText( painter, index, coordinatePlane()->translate( QPointF( x, y ) ), y );
424         }
425     }
426 }
427 
428 
paintMarker(QPainter * painter,const DataValueAttributes & a,const QModelIndex & index,const QPointF & pos)429 void AbstractDiagram::paintMarker( QPainter* painter,
430                                    const DataValueAttributes& a,
431                                    const QModelIndex& index,
432                                    const QPointF& pos )
433 {
434     if ( !checkInvariants() || !a.isVisible() ) return;
435     const MarkerAttributes ma = a.markerAttributes();
436     if ( !ma.isVisible() ) return;
437 
438     const PainterSaver painterSaver( painter );
439 
440     QSizeF maSize = ma.markerSize();
441     const qreal diagramWidth = d->diagramSize.width();
442     const qreal diagramHeight = d->diagramSize.height();
443 
444     switch( ma.markerSizeMode() ) {
445     case MarkerAttributes::AbsoluteSize:
446         // Unscaled, i.e. without the painter's "zoom"
447         maSize.rwidth()  /= painter->matrix().m11();
448         maSize.rheight() /= painter->matrix().m22();
449         break;
450     case MarkerAttributes::AbsoluteSizeScaled:
451         // Keep maSize as is. It is specified directly in pixels and desired
452         // to be effected by the painter's "zoom".
453         break;
454     case MarkerAttributes::RelativeToDiagramWidthHeightMin:
455         maSize *= qMin( diagramWidth, diagramHeight );
456         break;
457     }
458 
459     QBrush indexBrush( brush( index ) );
460     QPen indexPen( ma.pen() );
461     if ( ma.markerColor().isValid() )
462         indexBrush.setColor( ma.markerColor() );
463 
464     paintMarker( painter, ma, indexBrush, indexPen, pos, maSize );
465 
466     // workaround: BC cannot be changed, otherwise we would pass the
467     // index down to next-lower paintMarker function. So far, we
468     // basically save a circle of radius maSize at pos in the
469     // reverseMapper. This means that ^^^ this version of paintMarker
470     // needs to be called to reverse-map the marker.
471     d->reverseMapper.addCircle( index.row(), index.column(), pos, 2 * maSize );
472 }
473 
paintMarker(QPainter * painter,const QModelIndex & index,const QPointF & pos)474 void AbstractDiagram::paintMarker( QPainter* painter,
475                                    const QModelIndex& index,
476                                    const QPointF& pos )
477 {
478     if ( !checkInvariants() ) return;
479     paintMarker( painter, dataValueAttributes( index ), index, pos );
480 }
481 
paintMarker(QPainter * painter,const MarkerAttributes & markerAttributes,const QBrush & brush,const QPen & pen,const QPointF & pos,const QSizeF & maSize)482 void AbstractDiagram::paintMarker( QPainter* painter,
483                                    const MarkerAttributes& markerAttributes,
484                                    const QBrush& brush,
485                                    const QPen& pen,
486                                    const QPointF& pos,
487                                    const QSizeF& maSize )
488 {
489     const QPen oldPen( painter->pen() );
490     // Pen is used to paint 4Pixels - 1 Pixel - Ring and FastCross types.
491     // make sure to use the brush color - see above in those cases.
492     const bool isFourPixels = (markerAttributes.markerStyle() == MarkerAttributes::Marker4Pixels);
493     if ( isFourPixels || (markerAttributes.markerStyle() == MarkerAttributes::Marker1Pixel) ) {
494         // for high-performance point charts with tiny point markers:
495         painter->setPen( PrintingParameters::scalePen( QPen( brush.color().lighter() ) ) );
496         if ( isFourPixels ) {
497             const qreal x = pos.x();
498             const qreal y = pos.y();
499             painter->drawLine( QPointF(x-1.0,y-1.0),
500                                QPointF(x+1.0,y-1.0) );
501             painter->drawLine( QPointF(x-1.0,y),
502                                QPointF(x+1.0,y) );
503             painter->drawLine( QPointF(x-1.0,y+1.0),
504                                QPointF(x+1.0,y+1.0) );
505         }
506         painter->drawPoint( pos );
507     } else {
508         const PainterSaver painterSaver( painter );
509         QPen painterPen( pen );
510         painter->setPen( PrintingParameters::scalePen( painterPen ) );
511         painter->setBrush( brush );
512         painter->setRenderHint ( QPainter::Antialiasing );
513         painter->translate( pos );
514         switch ( markerAttributes.markerStyle() ) {
515             case MarkerAttributes::MarkerCircle:
516             {
517                 if ( markerAttributes.threeD() ) {
518                     QRadialGradient grad;
519                     grad.setCoordinateMode( QGradient::ObjectBoundingMode );
520                     QColor drawColor = brush.color();
521                     grad.setCenter( 0.5, 0.5 );
522                     grad.setRadius( 1.0 );
523                     grad.setFocalPoint( 0.35, 0.35 );
524                     grad.setColorAt( 0.00, drawColor.lighter( 150 ) );
525                     grad.setColorAt( 0.20, drawColor );
526                     grad.setColorAt( 0.50, drawColor.darker( 150 ) );
527                     grad.setColorAt( 0.75, drawColor.darker( 200 ) );
528                     grad.setColorAt( 0.95, drawColor.darker( 250 ) );
529                     grad.setColorAt( 1.00, drawColor.darker( 200 ) );
530                     QBrush newBrush( grad );
531                     newBrush.setMatrix( brush.matrix() );
532                     painter->setBrush( newBrush );
533                 }
534                 painter->drawEllipse( QRectF( 0 - maSize.height()/2, 0 - maSize.width()/2,
535                             maSize.height(), maSize.width()) );
536             }
537                 break;
538             case MarkerAttributes::MarkerSquare:
539                 {
540                     QRectF rect( 0 - maSize.width()/2, 0 - maSize.height()/2,
541                                 maSize.width(), maSize.height() );
542                     painter->drawRect( rect );
543                     break;
544                 }
545             case MarkerAttributes::MarkerDiamond:
546                 {
547                     QVector <QPointF > diamondPoints;
548                     QPointF top, left, bottom, right;
549                     top    = QPointF( 0, 0 - maSize.height()/2 );
550                     left   = QPointF( 0 - maSize.width()/2, 0 );
551                     bottom = QPointF( 0, maSize.height()/2 );
552                     right  = QPointF( maSize.width()/2, 0 );
553                     diamondPoints << top << left << bottom << right;
554                     painter->drawPolygon( diamondPoints );
555                     break;
556                 }
557             // both handled on top of the method:
558             case MarkerAttributes::Marker1Pixel:
559             case MarkerAttributes::Marker4Pixels:
560                     break;
561             case MarkerAttributes::MarkerRing:
562                 {
563                     painter->setBrush( Qt::NoBrush );
564                     painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) );
565                     painter->drawEllipse( QRectF( 0 - maSize.height()/2, 0 - maSize.width()/2,
566                                         maSize.height(), maSize.width()) );
567                     break;
568                 }
569             case MarkerAttributes::MarkerCross:
570                 {
571                     // Note: Markers can have outline,
572                     //       so just drawing two rects is NOT the solution here!
573                     const qreal w02 = maSize.width() * 0.2;
574                     const qreal w05 = maSize.width() * 0.5;
575                     const qreal h02 = maSize.height()* 0.2;
576                     const qreal h05 = maSize.height()* 0.5;
577                     QVector <QPointF > crossPoints;
578                     QPointF p[12];
579                     p[ 0] = QPointF( -w02, -h05 );
580                     p[ 1] = QPointF( w02, -h05 );
581                     p[ 2] = QPointF( w02, -h02 );
582                     p[ 3] = QPointF( w05, -h02 );
583                     p[ 4] = QPointF( w05,  h02 );
584                     p[ 5] = QPointF( w02,  h02 );
585                     p[ 6] = QPointF( w02,  h05 );
586                     p[ 7] = QPointF( -w02,  h05 );
587                     p[ 8] = QPointF( -w02,  h02 );
588                     p[ 9] = QPointF( -w05,  h02 );
589                     p[10] = QPointF( -w05, -h02 );
590                     p[11] = QPointF( -w02, -h02 );
591                     for ( int i=0; i<12; ++i )
592                         crossPoints << p[i];
593                     crossPoints << p[0];
594                     painter->drawPolygon( crossPoints );
595                     break;
596                 }
597             case MarkerAttributes::MarkerFastCross:
598                 {
599                     QPointF left, right, top, bottom;
600                     left  = QPointF( -maSize.width()/2, 0 );
601                     right = QPointF( maSize.width()/2, 0 );
602                     top   = QPointF( 0, -maSize.height()/2 );
603                     bottom= QPointF( 0, maSize.height()/2 );
604                     painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) );
605                     painter->drawLine( left, right );
606                     painter->drawLine( top, bottom );
607                     break;
608                 }
609             case MarkerAttributes::MarkerArrowDown:
610                 {
611                     QVector <QPointF > arrowPoints;
612                     QPointF topLeft, topRight, bottom;
613                     topLeft  = QPointF( 0 - maSize.width()/2, 0 - maSize.height()/2 );
614                     topRight = QPointF( maSize.width()/2, 0 - maSize.height()/2 );
615                     bottom   = QPointF( 0, maSize.height()/2 );
616                     arrowPoints << topLeft << bottom << topRight;
617                     painter->drawPolygon( arrowPoints );
618                     break;
619                 }
620             case MarkerAttributes::MarkerArrowUp:
621                 {
622                     QVector <QPointF > arrowPoints;
623                     QPointF top, bottomLeft, bottomRight;
624                     top         = QPointF( 0, 0 - maSize.height()/2 );
625                     bottomLeft  = QPointF( 0 - maSize.width()/2, maSize.height()/2 );
626                     bottomRight = QPointF( maSize.width()/2, maSize.height()/2 );
627                     arrowPoints << top << bottomLeft << bottomRight;
628                     painter->drawPolygon( arrowPoints );
629                     break;
630                 }
631             case MarkerAttributes::MarkerArrowRight:
632                 {
633                     QVector <QPointF > arrowPoints;
634                     QPointF right, topLeft, bottomLeft;
635                     right      = QPointF( maSize.width()/2, 0 );
636                     topLeft    = QPointF( 0 - maSize.width()/2, 0 - maSize.height()/2 );
637                     bottomLeft = QPointF( 0 - maSize.width()/2, maSize.height()/2 );
638                     arrowPoints << topLeft << bottomLeft << right;
639                     painter->drawPolygon( arrowPoints );
640                     break;
641                 }
642             case MarkerAttributes::MarkerArrowLeft:
643                 {
644                     QVector <QPointF > arrowPoints;
645                     QPointF left, topRight, bottomRight;
646                     left        = QPointF( 0 - maSize.width()/2, 0 );
647                     topRight    = QPointF( maSize.width()/2, 0 - maSize.height()/2 );
648                     bottomRight = QPointF( maSize.width()/2, maSize.height()/2 );
649                     arrowPoints << left << bottomRight << topRight;
650                     painter->drawPolygon( arrowPoints );
651                     break;
652                 }
653             case MarkerAttributes::MarkerBowTie:
654             case MarkerAttributes::MarkerHourGlass:
655                 {
656                     QVector <QPointF > points;
657                     QPointF topLeft, topRight, bottomLeft, bottomRight;
658                     topLeft     = QPointF( 0 - maSize.width()/2, 0 - maSize.height()/2);
659                     topRight    = QPointF( maSize.width()/2, 0 - maSize.height()/2 );
660                     bottomLeft  = QPointF( 0 - maSize.width()/2, maSize.height()/2 );
661                     bottomRight = QPointF( maSize.width()/2, maSize.height()/2 );
662                     if ( markerAttributes.markerStyle() == MarkerAttributes::MarkerBowTie)
663                         points << topLeft << bottomLeft << topRight << bottomRight;
664                     else
665                         points << topLeft << bottomRight << bottomLeft << topRight;
666                     painter->drawPolygon( points );
667                     break;
668                 }
669             case MarkerAttributes::MarkerStar:
670                 {
671                     const qreal w01 = maSize.width() * 0.1;
672                     const qreal w05 = maSize.width() * 0.5;
673                     const qreal h01 = maSize.height() * 0.1;
674                     const qreal h05 = maSize.height() * 0.5;
675                     QVector <QPointF > points;
676                     QPointF p1 = QPointF(    0, -h05 );
677                     QPointF p2 = QPointF( -w01, -h01 );
678                     QPointF p3 = QPointF( -w05,    0 );
679                     QPointF p4 = QPointF( -w01,  h01 );
680                     QPointF p5 = QPointF(    0,  h05 );
681                     QPointF p6 = QPointF(  w01,  h01 );
682                     QPointF p7 = QPointF( w05,    0 );
683                     QPointF p8 = QPointF( w01, -h01 );
684                     points << p1 << p2 << p3 << p4 << p5 << p6 << p7 << p8;
685                     painter->drawPolygon( points );
686                     break;
687                 }
688             case MarkerAttributes::MarkerX:
689                 {
690                     const qreal w01 = maSize.width() * 0.1;
691                     const qreal w04 = maSize.width() * 0.4;
692                     const qreal w05 = maSize.width() * 0.5;
693                     const qreal h01 = maSize.height() * 0.1;
694                     const qreal h04 = maSize.height() * 0.4;
695                     const qreal h05 = maSize.height() * 0.5;
696                     QVector <QPointF > crossPoints;
697                     QPointF p1 = QPointF( -w04, -h05 );
698                     QPointF p2 = QPointF( -w05, -h04 );
699                     QPointF p3 = QPointF( -w01,  0 );
700                     QPointF p4 = QPointF( -w05,  h04 );
701                     QPointF p5 = QPointF( -w04,  h05 );
702                     QPointF p6 = QPointF(  0,    h01 );
703                     QPointF p7 = QPointF(  w04,  h05 );
704                     QPointF p8 = QPointF(  w05,  h04 );
705                     QPointF p9 = QPointF(  w01,  0 );
706                     QPointF p10 = QPointF( w05, -h04 );
707                     QPointF p11 = QPointF( w04, -h05 );
708                     QPointF p12 = QPointF( 0,   -h01 );
709                     crossPoints << p1 << p2 << p3 << p4 << p5 << p6
710                                 << p7 << p8 << p9 << p10 << p11 << p12;
711                     painter->drawPolygon( crossPoints );
712                     break;
713                 }
714             case MarkerAttributes::MarkerAsterisk:
715                 {
716                     // Note: Markers can have outline,
717                     //       so just drawing three lines is NOT the solution here!
718                     // The idea that we use is to draw 3 lines anyway, but convert their
719                     // outlines to QPainterPaths which are then united and filled.
720                     const qreal w04 = maSize.width() * 0.4;
721                     const qreal h02 = maSize.height() * 0.2;
722                     const qreal h05 = maSize.height() * 0.5;
723                     //QVector <QPointF > crossPoints;
724                     QPointF p1 = QPointF(    0, -h05 );
725                     QPointF p2 = QPointF( -w04, -h02 );
726                     QPointF p3 = QPointF( -w04,  h02 );
727                     QPointF p4 = QPointF(    0,  h05 );
728                     QPointF p5 = QPointF(  w04,  h02 );
729                     QPointF p6 = QPointF(  w04, -h02 );
730                     QPen pen = painter->pen();
731                     QPainterPathStroker stroker;
732                     stroker.setWidth( pen.widthF() );
733                     stroker.setCapStyle( pen.capStyle() );
734 
735                     QPainterPath path;
736                     QPainterPath dummyPath;
737                     dummyPath.moveTo( p1 );
738                     dummyPath.lineTo( p4 );
739                     path = stroker.createStroke( dummyPath );
740 
741                     dummyPath = QPainterPath();
742                     dummyPath.moveTo( p2 );
743                     dummyPath.lineTo( p5 );
744                     path = path.united( stroker.createStroke( dummyPath ) );
745 
746                     dummyPath = QPainterPath();
747                     dummyPath.moveTo( p3 );
748                     dummyPath.lineTo( p6 );
749                     path = path.united( stroker.createStroke( dummyPath ) );
750 
751                     painter->drawPath( path );
752                     break;
753                 }
754             case MarkerAttributes::MarkerHorizontalBar:
755                 {
756                     const qreal w05 = maSize.width() * 0.5;
757                     const qreal h02 = maSize.height()* 0.2;
758                     QVector <QPointF > points;
759                     QPointF p1 = QPointF( -w05, -h02 );
760                     QPointF p2 = QPointF( -w05,  h02 );
761                     QPointF p3 = QPointF(  w05,  h02 );
762                     QPointF p4 = QPointF(  w05, -h02 );
763                     points << p1 << p2 << p3 << p4;
764                     painter->drawPolygon( points );
765                     break;
766                 }
767             case MarkerAttributes::MarkerVerticalBar:
768                 {
769                     const qreal w02 = maSize.width() * 0.2;
770                     const qreal h05 = maSize.height()* 0.5;
771                     QVector <QPointF > points;
772                     QPointF p1 = QPointF( -w02, -h05 );
773                     QPointF p2 = QPointF( -w02,  h05 );
774                     QPointF p3 = QPointF(  w02,  h05 );
775                     QPointF p4 = QPointF(  w02, -h05 );
776                     points << p1 << p2 << p3 << p4;
777                     painter->drawPolygon( points );
778                     break;
779                 }
780             case MarkerAttributes::NoMarker:
781                 break;
782             case MarkerAttributes::PainterPathMarker:
783                 {
784                     QPainterPath path = markerAttributes.customMarkerPath();
785                     const QRectF pathBoundingRect = path.boundingRect();
786                     const qreal xScaling = maSize.height() / pathBoundingRect.height();
787                     const qreal yScaling = maSize.width() / pathBoundingRect.width();
788                     const qreal scaling = qMin( xScaling, yScaling );
789                     painter->scale( scaling, scaling );
790                     painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) );
791                     painter->drawPath(path);
792                     break;
793                 }
794             default:
795                 Q_ASSERT_X ( false, "paintMarkers()",
796                             "Type item does not match a defined Marker Type." );
797         }
798     }
799     painter->setPen( oldPen );
800 }
801 
paintMarkers(QPainter * painter)802 void AbstractDiagram::paintMarkers( QPainter* painter )
803 {
804     if ( !checkInvariants() ) {
805         return;
806     }
807 
808     const int rowCount = model()->rowCount( rootIndex() );
809     const int columnCount = model()->columnCount( rootIndex() );
810     for ( int column = 0; column < columnCount; column += datasetDimension() ) {
811         for ( int row = 0; row < rowCount; ++row ) {
812             QModelIndex index = model()->index( row, column, rootIndex() ); // checked
813             qreal x;
814             qreal y;
815             if ( datasetDimension() == 1 ) {
816                 x = row;
817                 y = index.data().toReal();
818             } else {
819                 x = index.data().toReal();
820                 y = model()->index( row, column + 1, rootIndex() ).data().toReal();
821             }
822             paintMarker( painter, index, coordinatePlane()->translate( QPointF( x, y ) ) );
823         }
824     }
825 }
826 
827 
setPen(const QModelIndex & index,const QPen & pen)828 void AbstractDiagram::setPen( const QModelIndex& index, const QPen& pen )
829 {
830     attributesModel()->setData(
831         conditionallyMapFromSource( index ),
832         QVariant::fromValue( pen ), DatasetPenRole );
833     Q_EMIT propertiesChanged();
834 }
835 
setPen(const QPen & pen)836 void AbstractDiagram::setPen( const QPen& pen )
837 {
838     attributesModel()->setModelData(
839         QVariant::fromValue( pen ), DatasetPenRole );
840     Q_EMIT propertiesChanged();
841 }
842 
setPen(int dataset,const QPen & pen)843 void AbstractDiagram::setPen( int dataset, const QPen& pen )
844 {
845     d->setDatasetAttrs( dataset, QVariant::fromValue( pen ), DatasetPenRole );
846     Q_EMIT propertiesChanged();
847 }
848 
pen() const849 QPen AbstractDiagram::pen() const
850 {
851     return attributesModel()->data( DatasetPenRole ).value< QPen >();
852 }
853 
pen(int dataset) const854 QPen AbstractDiagram::pen( int dataset ) const
855 {
856     const QVariant penSettings( d->datasetAttrs( dataset, DatasetPenRole ) );
857     if ( penSettings.isValid() )
858         return penSettings.value< QPen >();
859     return pen();
860 }
861 
pen(const QModelIndex & index) const862 QPen AbstractDiagram::pen( const QModelIndex& index ) const
863 {
864     return  attributesModel()->data(
865             conditionallyMapFromSource( index ),
866             DatasetPenRole ).value< QPen >();
867 }
868 
setBrush(const QModelIndex & index,const QBrush & brush)869 void AbstractDiagram::setBrush( const QModelIndex& index, const QBrush& brush )
870 {
871     attributesModel()->setData(
872         conditionallyMapFromSource( index ),
873         QVariant::fromValue( brush ), DatasetBrushRole );
874     Q_EMIT propertiesChanged();
875 }
876 
setBrush(const QBrush & brush)877 void AbstractDiagram::setBrush( const QBrush& brush )
878 {
879     attributesModel()->setModelData(
880         QVariant::fromValue( brush ), DatasetBrushRole );
881     Q_EMIT propertiesChanged();
882 }
883 
setBrush(int dataset,const QBrush & brush)884 void AbstractDiagram::setBrush( int dataset, const QBrush& brush )
885 {
886     d->setDatasetAttrs( dataset, QVariant::fromValue( brush ), DatasetBrushRole );
887     Q_EMIT propertiesChanged();
888 }
889 
brush() const890 QBrush AbstractDiagram::brush() const
891 {
892     return attributesModel()->data( DatasetBrushRole ).value< QBrush >();
893 }
894 
brush(int dataset) const895 QBrush AbstractDiagram::brush( int dataset ) const
896 {
897     const QVariant brushSettings( d->datasetAttrs( dataset, DatasetBrushRole ) );
898     if ( brushSettings.isValid() )
899         return brushSettings.value< QBrush >();
900     return brush();
901 }
902 
brush(const QModelIndex & index) const903 QBrush AbstractDiagram::brush( const QModelIndex& index ) const
904 {
905     return
906         attributesModel()->data( conditionallyMapFromSource( index ), DatasetBrushRole ).value< QBrush >();
907 }
908 
909 /*
910   * Sets the unit prefix for one value
911   * @param prefix the prefix to be set
912   * @param column the value using that prefix
913   * @param orientation the orientantion of the axis to set
914   */
setUnitPrefix(const QString & prefix,int column,Qt::Orientation orientation)915 void AbstractDiagram::setUnitPrefix( const QString& prefix, int column, Qt::Orientation orientation )
916 {
917     d->unitPrefixMap[ column ][ orientation ]= prefix;
918 }
919 
920 /*
921   * Sets the unit prefix for all values
922   * @param prefix the prefix to be set
923   * @param orientation the orientantion of the axis to set
924   */
setUnitPrefix(const QString & prefix,Qt::Orientation orientation)925 void AbstractDiagram::setUnitPrefix( const QString& prefix, Qt::Orientation orientation )
926 {
927     d->unitPrefix[ orientation ] = prefix;
928 }
929 
930 /*
931   * Sets the unit suffix for one value
932   * @param suffix the suffix to be set
933   * @param column the value using that suffix
934   * @param orientation the orientantion of the axis to set
935   */
setUnitSuffix(const QString & suffix,int column,Qt::Orientation orientation)936 void AbstractDiagram::setUnitSuffix( const QString& suffix, int column, Qt::Orientation orientation )
937 {
938     d->unitSuffixMap[ column ][ orientation ]= suffix;
939 }
940 
941 /*
942   * Sets the unit suffix for all values
943   * @param suffix the suffix to be set
944   * @param orientation the orientantion of the axis to set
945   */
setUnitSuffix(const QString & suffix,Qt::Orientation orientation)946 void AbstractDiagram::setUnitSuffix( const QString& suffix, Qt::Orientation orientation )
947 {
948     d->unitSuffix[ orientation ] = suffix;
949 }
950 
951 /*
952   * Returns the unit prefix for a special value
953   * @param column the value which's prefix is requested
954   * @param orientation the orientation of the axis
955   * @param fallback if true, the global prefix is return when no specific one is set for that value
956   * @return the unit prefix
957   */
unitPrefix(int column,Qt::Orientation orientation,bool fallback) const958 QString AbstractDiagram::unitPrefix( int column, Qt::Orientation orientation, bool fallback ) const
959 {
960     if ( !fallback || d->unitPrefixMap[ column ].contains( orientation ) )
961         return d->unitPrefixMap[ column ][ orientation ];
962     return d->unitPrefix[ orientation ];
963 }
964 
965 /* Returns the global unit prefix
966   * @param orientation the orientation of the axis
967   * @return the unit prefix
968   */
unitPrefix(Qt::Orientation orientation) const969 QString AbstractDiagram::unitPrefix( Qt::Orientation orientation ) const
970 {
971     return d->unitPrefix[ orientation ];
972 }
973 
974 /*
975   * Returns the unit suffix for a special value
976   * @param column the value which's suffix is requested
977   * @param orientation the orientation of the axis
978   * @param fallback if true, the global suffix is return when no specific one is set for that value
979   * @return the unit suffix
980   */
unitSuffix(int column,Qt::Orientation orientation,bool fallback) const981 QString AbstractDiagram::unitSuffix( int column, Qt::Orientation orientation, bool fallback ) const
982 {
983     if ( !fallback || d->unitSuffixMap[ column ].contains( orientation ) )
984         return d->unitSuffixMap[ column ][ orientation ];
985     return d->unitSuffix[ orientation ];
986 }
987 
988 /* Returns the global unit suffix
989   * @param orientation the orientation of the axis
990   * @return the unit siffix
991   */
unitSuffix(Qt::Orientation orientation) const992 QString AbstractDiagram::unitSuffix( Qt::Orientation orientation ) const
993 {
994     return d->unitSuffix[ orientation ];
995 }
996 
997 // implement QAbstractItemView:
visualRect(const QModelIndex & index) const998 QRect AbstractDiagram::visualRect( const QModelIndex &index ) const
999 {
1000     return d->reverseMapper.boundingRect( index.row(), index.column() ).toRect();
1001 }
1002 
scrollTo(const QModelIndex &,ScrollHint)1003 void AbstractDiagram::scrollTo(const QModelIndex &, ScrollHint )
1004 {}
1005 
1006 // indexAt ... down below
1007 
moveCursor(CursorAction,Qt::KeyboardModifiers)1008 QModelIndex AbstractDiagram::moveCursor(CursorAction, Qt::KeyboardModifiers )
1009 { return QModelIndex(); }
1010 
horizontalOffset() const1011 int AbstractDiagram::horizontalOffset() const
1012 { return 0; }
1013 
verticalOffset() const1014 int AbstractDiagram::verticalOffset() const
1015 { return 0; }
1016 
isIndexHidden(const QModelIndex &) const1017 bool AbstractDiagram::isIndexHidden(const QModelIndex &) const
1018 { return true; }
1019 
setSelection(const QRect & rect,QItemSelectionModel::SelectionFlags command)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 
visualRegionForSelection(const QItemSelection & selection) const1031 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 
visualRegion(const QModelIndex & index) const1043 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 
useDefaultColors()1049 void KChart::AbstractDiagram::useDefaultColors( )
1050 {
1051     d->attributesModel->setPaletteType( AttributesModel::PaletteTypeDefault );
1052 }
1053 
useSubduedColors()1054 void KChart::AbstractDiagram::useSubduedColors( )
1055 {
1056     d->attributesModel->setPaletteType( AttributesModel::PaletteTypeSubdued );
1057 }
1058 
useRainbowColors()1059 void KChart::AbstractDiagram::useRainbowColors( )
1060 {
1061     d->attributesModel->setPaletteType( AttributesModel::PaletteTypeRainbow );
1062 }
1063 
itemRowLabels() const1064 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 
datasetLabels() const1080 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 
datasetBrushes() const1093 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 
datasetPens() const1106 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 
datasetMarkers() const1119 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 
checkInvariants(bool justReturnTheStatus) const1132 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 
datasetDimension() const1144 int AbstractDiagram::datasetDimension( ) const
1145 {
1146     return d->datasetDimension;
1147 }
1148 
setDatasetDimension(int dimension)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 
setDatasetDimensionInternal(int dimension)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 
valueForCell(int row,int column) const1168 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 
update() const1178 void AbstractDiagram::update() const
1179 {
1180     if ( d->plane ) {
1181         d->plane->update();
1182     }
1183 }
1184 
indexAt(const QPoint & point) const1185 QModelIndex AbstractDiagram::indexAt( const QPoint& point ) const
1186 {
1187     return d->indexAt( point );
1188 }
1189 
indexesAt(const QPoint & point) const1190 QModelIndexList AbstractDiagram::indexesAt( const QPoint& point ) const
1191 {
1192     return d->indexesAt( point );
1193 }
1194 
indexesIn(const QRect & rect) const1195 QModelIndexList AbstractDiagram::indexesIn( const QRect& rect ) const
1196 {
1197     return d->indexesIn( rect );
1198 }
1199