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