34 #include "KDChartAbstractDiagram_p.h"
38 #include "KDChartPainterSaver_p.h"
40 #include <QAbstractTextDocumentLayout>
42 #include <QApplication>
44 #include <KDABLibFakes>
47 using namespace KDChart;
49 LabelPaintInfo::LabelPaintInfo()
53 LabelPaintInfo::LabelPaintInfo(
const QModelIndex& _index,
const DataValueAttributes& _attrs,
54 const QPainterPath& _labelArea,
const QPointF& _markerPos,
55 bool _isValuePositive,
const QString& _value )
58 , labelArea( _labelArea )
59 , markerPos( _markerPos )
60 , isValuePositive( _isValuePositive )
65 LabelPaintInfo::LabelPaintInfo(
const LabelPaintInfo& other )
66 : index( other.index )
67 , attrs( other.attrs )
68 , labelArea( other.labelArea )
69 , markerPos( other.markerPos )
70 , isValuePositive( other.isValuePositive )
71 , value( other.value )
75 AbstractDiagram::Private::Private()
77 , doDumpPaintTime( false )
80 , allowOverlappingDataValueTexts( false )
81 , antiAliasing( true )
83 , datasetDimension( 1 )
84 , databoundariesDirty( true )
85 , mCachedFontMetrics( QFontMetrics( qApp->font() ) )
89 AbstractDiagram::Private::~Private()
91 if ( attributesModel && qobject_cast<PrivateAttributesModel*>(attributesModel) )
92 delete attributesModel;
95 void AbstractDiagram::Private::init()
104 bool AbstractDiagram::Private::usesExternalAttributesModel()
const
106 return ( ! attributesModel.isNull() ) &&
107 ( ! qobject_cast<PrivateAttributesModel*>(attributesModel) );
110 void AbstractDiagram::Private::setAttributesModel(
AttributesModel* amodel )
112 if ( !attributesModel.isNull() &&
114 delete attributesModel;
116 attributesModel = amodel;
119 AbstractDiagram::Private::Private(
const AbstractDiagram::Private& rhs ) :
121 doDumpPaintTime( rhs.doDumpPaintTime ),
124 attributesModelRootIndex( QModelIndex() ),
125 attributesModel( rhs.attributesModel ),
126 allowOverlappingDataValueTexts( rhs.allowOverlappingDataValueTexts ),
127 antiAliasing( rhs.antiAliasing ),
128 percent( rhs.percent ),
129 datasetDimension( rhs.datasetDimension ),
130 mCachedFontMetrics( rhs.cachedFontMetrics() )
133 attributesModel->initFrom( rhs.attributesModel );
137 qreal AbstractDiagram::Private::calcPercentValue(
const QModelIndex & index )
const
140 for (
int col = 0; col < attributesModel->columnCount( QModelIndex() ); col++ )
141 sum += attributesModel->data( attributesModel->index( index.row(), col, QModelIndex() ) ).toReal();
144 return attributesModel->data( attributesModel->mapFromSource( index ) ).toReal() / sum * 100.0;
147 void AbstractDiagram::Private::addLabel(
148 LabelPaintCache* cache,
149 const QModelIndex& index,
150 const CartesianDiagramDataCompressor::CachePosition* position,
153 const qreal value, qreal favoriteAngle )
155 CartesianDiagramDataCompressor::AggregatedDataValueAttributes allAttrs(
156 aggregatedAttrs( index, position ) );
159 for ( it = allAttrs.constBegin(); it != allAttrs.constEnd(); ++it ) {
165 const bool isPositive = ( value >= 0.0 );
169 if ( relPos.referencePosition().isUnknown() ) {
170 relPos.setReferencePosition( isPositive ? autoPositionPositive : autoPositionNegative );
174 if ( isTransposed() ) {
184 relPos.setReferencePosition(
Position( posValue ) );
188 const QPointF referencePoint = relPos.referencePoint();
189 if ( !diagram->coordinatePlane()->isVisiblePoint( referencePoint ) ) {
199 QSizeF relativeMeasureSize( fontHeight, fontHeight );
208 const QString text = formatDataValueText( dva, index, value );
210 doc.setDocumentMargin( 0 );
211 if ( Qt::mightBeRichText( text ) ) {
214 doc.setPlainText( text );
218 doc.setDefaultFont( calculatedFont );
220 const QRectF plainRect = doc.documentLayout()->frameBoundingRect( doc.rootFrame() );
257 QTransform transform;
260 QPointF calcPoint = relPos.calculatedPoint( relativeMeasureSize );
261 transform.translate( calcPoint.x(), calcPoint.y() );
264 if ( relPos.alignment() & Qt::AlignLeft ) {
266 }
else if ( relPos.alignment() & Qt::AlignRight ) {
271 if ( relPos.alignment() & Qt::AlignTop ) {
273 }
else if ( relPos.alignment() & Qt::AlignBottom ) {
276 transform.translate( qreal( dx ) * plainRect.width() * 0.5,
277 qreal( dy ) * plainRect.height() * 0.5 );
280 transform.translate( plainRect.center().x(), plainRect.center().y() );
285 transform.rotate( rotation );
286 transform.translate( -plainRect.center().x(), -plainRect.center().y() );
289 QPainterPath labelArea;
290 labelArea.addPolygon( transform.mapToPolygon( plainRect.toRect() ) );
291 labelArea.closeSubpath();
294 cache->paintReplay.append( LabelPaintInfo( it.key(), dva, labelArea,
295 referencePoint, value >= 0.0, text ) );
299 const QFontMetrics* AbstractDiagram::Private::cachedFontMetrics(
const QFont& font,
302 if ( ( font != mCachedFont ) || ( paintDevice != mCachedPaintDevice ) ) {
303 mCachedFontMetrics = QFontMetrics( font, const_cast<QPaintDevice *>( paintDevice ) );
306 return &mCachedFontMetrics;
309 const QFontMetrics AbstractDiagram::Private::cachedFontMetrics()
const
311 return mCachedFontMetrics;
314 QString AbstractDiagram::Private::formatNumber( qreal value,
int decimalDigits )
const
316 const int digits = qMax(decimalDigits, 0);
317 const qreal roundingEpsilon = pow( 0.1, digits ) * ( value >= 0.0 ? 0.5 : -0.5 );
318 QString asString = QString::number( value + roundingEpsilon,
'f' );
319 const int decimalPos = asString.indexOf( QLatin1Char(
'.' ) );
320 if ( decimalPos < 0 ) {
324 int last = qMin( decimalPos + digits, asString.length() - 1 );
326 while ( last > decimalPos && asString[ last ] == QLatin1Char(
'0' ) ) {
329 if ( last == decimalPos ) {
332 asString.chop( asString.length() - last - 1 );
336 void AbstractDiagram::Private::forgetAlreadyPaintedDataValues()
338 alreadyDrawnDataValueTexts.clear();
339 prevPaintedDataValueText.clear();
342 void AbstractDiagram::Private::paintDataValueTextsAndMarkers(
344 const LabelPaintCache &cache,
346 bool justCalculateRect ,
347 QRectF* cumulatedBoundingRect )
349 if ( justCalculateRect && !cumulatedBoundingRect ) {
350 qWarning() << Q_FUNC_INFO <<
"Neither painting nor finding the bounding rect, what are we doing?";
353 const PainterSaver painterSaver( ctx->
painter() );
354 ctx->
painter()->setClipping(
false );
356 if ( paintMarkers && !justCalculateRect ) {
357 KDAB_FOREACH (
const LabelPaintInfo& info, cache.paintReplay ) {
358 diagram->paintMarker( ctx->
painter(), info.index, info.markerPos );
368 m.setAbsoluteValue( 6.0 );
372 forgetAlreadyPaintedDataValues();
374 KDAB_FOREACH (
const LabelPaintInfo& info, cache.paintReplay ) {
375 const QPointF pos = info.labelArea.elementAt( 0 );
376 paintDataValueText( ctx->
painter(), info.attrs, pos, info.isValuePositive,
377 info.value, justCalculateRect, cumulatedBoundingRect );
380 if ( comment.isEmpty() ) {
385 Qt::AlignHCenter | Qt::AlignVCenter );
386 const QRect rect( pos.toPoint(), item.sizeHint() );
388 if (cumulatedBoundingRect) {
389 (*cumulatedBoundingRect) |= rect;
391 if ( !justCalculateRect ) {
399 const QModelIndex& index, qreal value )
const
405 value = calcPercentValue( index );
415 ret.prepend( dva.
prefix() );
416 ret.append( dva.
suffix() );
421 void AbstractDiagram::Private::paintDataValueText(
423 const QModelIndex& index,
426 bool justCalculateRect ,
427 QRectF* cumulatedBoundingRect )
430 const QString text = formatDataValueText( dva, index, value );
431 paintDataValueText( painter, dva, pos, value >= 0.0, text,
432 justCalculateRect, cumulatedBoundingRect );
435 void AbstractDiagram::Private::paintDataValueText(
439 bool valueIsPositive,
441 bool justCalculateRect ,
442 QRectF* cumulatedBoundingRect )
452 prevPaintedDataValueText = text;
455 doc.setDocumentMargin( 0.0 );
456 if ( Qt::mightBeRichText( text ) ) {
459 doc.setPlainText( text );
464 const PainterSaver painterSaver( painter );
467 doc.setDefaultFont( calculatedFont );
468 QAbstractTextDocumentLayout::PaintContext context;
469 context.palette = diagram->palette();
470 context.palette.setColor( QPalette::Text, ta.
pen().color() );
472 QAbstractTextDocumentLayout*
const layout = doc.documentLayout();
473 layout->setPaintDevice( painter->device() );
475 painter->translate( pos.x(), pos.y() );
480 painter->rotate( rotation );
483 QTransform transform = painter->worldTransform();
491 const QRectF br( layout->frameBoundingRect( doc.rootFrame() ) );
492 QPolygon pr = transform.mapToPolygon( br.toRect() );
496 path.addPolygon( pr );
500 for (
int i = alreadyDrawnDataValueTexts.count() - 1; i >= 0; i-- ) {
501 if ( alreadyDrawnDataValueTexts.at( i ).intersects( path ) ) {
508 alreadyDrawnDataValueTexts << path;
513 QRectF rect = layout->frameBoundingRect( doc.rootFrame() );
514 if ( cumulatedBoundingRect ) {
515 (*cumulatedBoundingRect) |= transform.mapRect( rect );
517 if ( !justCalculateRect ) {
518 bool paintBack =
false;
520 if ( back.isVisible() ) {
524 painter->setBrush( QBrush() );
529 if ( frame.isVisible() ) {
531 painter->
setPen( frame.pen() );
532 radius = frame.cornerRadius();
536 QRectF borderRect( QPointF( 0, 0 ), rect.size() );
537 painter->drawRoundedRect( borderRect, radius, radius );
539 layout->draw( painter, context );
544 QModelIndex AbstractDiagram::Private::indexAt(
const QPoint& point )
const
546 QModelIndexList l = indexesAt( point );
551 return QModelIndex();
554 QModelIndexList AbstractDiagram::Private::indexesAt(
const QPoint& point )
const
556 return reverseMapper.indexesAt( point );
559 QModelIndexList AbstractDiagram::Private::indexesIn(
const QRect& rect )
const
561 return reverseMapper.indexesIn( rect );
564 CartesianDiagramDataCompressor::AggregatedDataValueAttributes AbstractDiagram::Private::aggregatedAttrs(
565 const QModelIndex& index,
566 const CartesianDiagramDataCompressor::CachePosition* position )
const
568 Q_UNUSED( position );
569 CartesianDiagramDataCompressor::AggregatedDataValueAttributes allAttrs;
570 allAttrs[index] = diagram->dataValueAttributes( index );
574 void AbstractDiagram::Private::setDatasetAttrs(
int dataset,
const QVariant& data,
int role )
580 int column = dataset * datasetDimension;
589 for (
int i = 0; i < columnSpan; i++ ) {
590 attributesModel->setHeaderData( column + i, Qt::Horizontal, data, role );
594 QVariant AbstractDiagram::Private::datasetAttrs(
int dataset,
int role )
const
597 int column = dataset * datasetDimension;
598 return attributesModel->headerData( column, Qt::Horizontal, role );
601 void AbstractDiagram::Private::resetDatasetAttrs(
int dataset,
int role )
604 int column = dataset * datasetDimension;
605 attributesModel->resetHeaderData( column, Qt::Horizontal, role );
608 bool AbstractDiagram::Private::isTransposed()
const
624 return barDiagram->
orientation() == Qt::Horizontal;
627 LineAttributesInfo::LineAttributesInfo()
631 LineAttributesInfo::LineAttributesInfo(
const QModelIndex& _index,
const QPointF& _value,
const QPointF& _nextValue )
634 , nextValue ( _nextValue )