34 #include "KDChartAbstractDiagram_p.h"
38 #include "KDChartPainterSaver_p.h"
40 #include <QAbstractTextDocumentLayout>
42 #include <QApplication>
44 #include <KDABLibFakes>
49 LabelPaintInfo::LabelPaintInfo() :
50 isValuePositive( false )
54 LabelPaintInfo::LabelPaintInfo(
const QModelIndex& _index,
const DataValueAttributes& _attrs,
55 const QPainterPath& _labelArea,
const QPointF& _markerPos,
56 bool _isValuePositive,
const QString& _value )
59 , labelArea( _labelArea )
60 , markerPos( _markerPos )
61 , isValuePositive( _isValuePositive )
66 LabelPaintInfo::LabelPaintInfo(
const LabelPaintInfo& other )
67 : index( other.index )
68 , attrs( other.attrs )
69 , labelArea( other.labelArea )
70 , markerPos( other.markerPos )
71 , isValuePositive( other.isValuePositive )
72 , value( other.value )
76 AbstractDiagram::Private::Private()
78 , doDumpPaintTime( false )
81 , allowOverlappingDataValueTexts( false )
82 , antiAliasing( true )
84 , datasetDimension( 1 )
85 , databoundariesDirty( true )
86 , mCachedFontMetrics( QFontMetrics( qApp->font() ) )
90 AbstractDiagram::Private::~Private()
92 if ( attributesModel && qobject_cast<PrivateAttributesModel*>(attributesModel) )
93 delete attributesModel;
96 void AbstractDiagram::Private::init()
105 bool AbstractDiagram::Private::usesExternalAttributesModel()
const
107 return ( ! attributesModel.isNull() ) &&
108 ( ! qobject_cast<PrivateAttributesModel*>(attributesModel) );
111 void AbstractDiagram::Private::setAttributesModel(
AttributesModel* amodel )
113 if ( attributesModel == amodel ) {
117 if ( !attributesModel.isNull() ) {
118 if ( qobject_cast< PrivateAttributesModel* >( attributesModel ) ) {
119 delete attributesModel;
121 disconnect( attributesModel, SIGNAL( rowsInserted( QModelIndex,
int,
int ) ),
122 diagram, SLOT( setDataBoundariesDirty() ) );
123 disconnect( attributesModel, SIGNAL( columnsInserted( QModelIndex,
int,
int ) ),
124 diagram, SLOT( setDataBoundariesDirty() ) );
125 disconnect( attributesModel, SIGNAL( rowsRemoved( QModelIndex,
int,
int ) ),
126 diagram, SLOT( setDataBoundariesDirty() ) );
127 disconnect( attributesModel, SIGNAL( columnsRemoved( QModelIndex,
int,
int ) ),
128 diagram, SLOT( setDataBoundariesDirty() ) );
129 disconnect( attributesModel, SIGNAL( modelReset() ),
130 diagram, SLOT( setDataBoundariesDirty() ) );
131 disconnect( attributesModel, SIGNAL( layoutChanged() ),
132 diagram, SLOT( setDataBoundariesDirty() ) );
133 disconnect( attributesModel, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ),
134 diagram, SIGNAL( modelDataChanged() ));
138 emit diagram->attributesModelAboutToChange( amodel, attributesModel );
140 connect( amodel, SIGNAL( rowsInserted( QModelIndex,
int,
int ) ),
141 diagram, SLOT( setDataBoundariesDirty() ) );
142 connect( amodel, SIGNAL( columnsInserted( QModelIndex,
int,
int ) ),
143 diagram, SLOT( setDataBoundariesDirty() ) );
144 connect( amodel, SIGNAL( rowsRemoved( QModelIndex,
int,
int ) ),
145 diagram, SLOT( setDataBoundariesDirty() ) );
146 connect( amodel, SIGNAL( columnsRemoved( QModelIndex,
int,
int ) ),
147 diagram, SLOT( setDataBoundariesDirty() ) );
148 connect( amodel, SIGNAL( modelReset() ),
149 diagram, SLOT( setDataBoundariesDirty() ) );
150 connect( amodel, SIGNAL( layoutChanged() ),
151 diagram, SLOT( setDataBoundariesDirty() ) );
152 connect( amodel, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ),
153 diagram, SIGNAL( modelDataChanged() ));
155 attributesModel = amodel;
158 AbstractDiagram::Private::Private(
const AbstractDiagram::Private& rhs ) :
160 doDumpPaintTime( rhs.doDumpPaintTime ),
163 attributesModelRootIndex( QModelIndex() ),
164 attributesModel( rhs.attributesModel ),
165 allowOverlappingDataValueTexts( rhs.allowOverlappingDataValueTexts ),
166 antiAliasing( rhs.antiAliasing ),
167 percent( rhs.percent ),
168 datasetDimension( rhs.datasetDimension ),
169 mCachedFontMetrics( rhs.cachedFontMetrics() )
172 attributesModel->initFrom( rhs.attributesModel );
176 qreal AbstractDiagram::Private::calcPercentValue(
const QModelIndex & index )
const
179 for (
int col = 0; col < attributesModel->columnCount( QModelIndex() ); col++ )
180 sum += attributesModel->data( attributesModel->index( index.row(), col, QModelIndex() ) ).toReal();
183 return attributesModel->data( attributesModel->mapFromSource( index ) ).toReal() / sum * 100.0;
186 void AbstractDiagram::Private::addLabel(
187 LabelPaintCache* cache,
188 const QModelIndex& index,
189 const CartesianDiagramDataCompressor::CachePosition* position,
192 const qreal value, qreal favoriteAngle )
194 CartesianDiagramDataCompressor::AggregatedDataValueAttributes allAttrs(
195 aggregatedAttrs( index, position ) );
198 for ( it = allAttrs.constBegin(); it != allAttrs.constEnd(); ++it ) {
204 const bool isPositive = ( value >= 0.0 );
207 relPos.setReferencePoints( points );
208 if ( relPos.referencePosition().isUnknown() ) {
209 relPos.setReferencePosition( isPositive ? autoPositionPositive : autoPositionNegative );
213 if ( isTransposed() ) {
223 relPos.setReferencePosition(
Position( posValue ) );
227 const QPointF referencePoint = relPos.referencePoint();
228 if ( !diagram->coordinatePlane()->isVisiblePoint( referencePoint ) ) {
238 QSizeF relativeMeasureSize( fontHeight, fontHeight );
247 const QString text = formatDataValueText( dva, index, value );
249 doc.setDocumentMargin( 0 );
250 if ( Qt::mightBeRichText( text ) ) {
253 doc.setPlainText( text );
257 doc.setDefaultFont( calculatedFont );
259 const QRectF plainRect = doc.documentLayout()->frameBoundingRect( doc.rootFrame() );
296 QTransform transform;
299 QPointF calcPoint = relPos.calculatedPoint( relativeMeasureSize );
300 transform.translate( calcPoint.x(), calcPoint.y() );
303 if ( relPos.alignment() & Qt::AlignLeft ) {
305 }
else if ( relPos.alignment() & Qt::AlignRight ) {
310 if ( relPos.alignment() & Qt::AlignTop ) {
312 }
else if ( relPos.alignment() & Qt::AlignBottom ) {
315 transform.translate( qreal( dx ) * plainRect.width() * 0.5,
316 qreal( dy ) * plainRect.height() * 0.5 );
319 transform.translate( plainRect.center().x(), plainRect.center().y() );
324 transform.rotate( rotation );
325 transform.translate( -plainRect.center().x(), -plainRect.center().y() );
328 QPainterPath labelArea;
335 labelArea.addPolygon( transform.map( QPolygon( plainRect.toRect(),
true ) ) );
338 cache->paintReplay.append( LabelPaintInfo( it.key(), dva, labelArea,
339 referencePoint, value >= 0.0, text ) );
343 const QFontMetrics* AbstractDiagram::Private::cachedFontMetrics(
const QFont& font,
346 if ( ( font != mCachedFont ) || ( paintDevice != mCachedPaintDevice ) ) {
347 mCachedFontMetrics = QFontMetrics( font,
const_cast<QPaintDevice *
>( paintDevice ) );
350 return &mCachedFontMetrics;
353 const QFontMetrics AbstractDiagram::Private::cachedFontMetrics()
const
355 return mCachedFontMetrics;
358 QString AbstractDiagram::Private::formatNumber( qreal value,
int decimalDigits )
const
360 const int digits = qMax(decimalDigits, 0);
361 const qreal roundingEpsilon = pow( 0.1, digits ) * ( value >= 0.0 ? 0.5 : -0.5 );
362 QString asString = QString::number( value + roundingEpsilon,
'f' );
363 const int decimalPos = asString.indexOf( QLatin1Char(
'.' ) );
364 if ( decimalPos < 0 ) {
368 int last = qMin( decimalPos + digits, asString.length() - 1 );
370 while ( last > decimalPos && asString[ last ] == QLatin1Char(
'0' ) ) {
373 if ( last == decimalPos ) {
376 asString.chop( asString.length() - last - 1 );
380 void AbstractDiagram::Private::forgetAlreadyPaintedDataValues()
382 alreadyDrawnDataValueTexts.clear();
383 prevPaintedDataValueText.clear();
386 void AbstractDiagram::Private::paintDataValueTextsAndMarkers(
388 const LabelPaintCache &cache,
390 bool justCalculateRect ,
391 QRectF* cumulatedBoundingRect )
393 if ( justCalculateRect && !cumulatedBoundingRect ) {
394 qWarning() << Q_FUNC_INFO <<
"Neither painting nor finding the bounding rect, what are we doing?";
397 const PainterSaver painterSaver( ctx->
painter() );
398 ctx->
painter()->setClipping(
false );
400 if ( paintMarkers && !justCalculateRect ) {
401 KDAB_FOREACH (
const LabelPaintInfo& info, cache.paintReplay ) {
402 diagram->paintMarker( ctx->
painter(), info.index, info.markerPos );
412 m.setAbsoluteValue( 6.0 );
416 forgetAlreadyPaintedDataValues();
418 KDAB_FOREACH (
const LabelPaintInfo& info, cache.paintReplay ) {
419 const QPointF pos = info.labelArea.elementAt( 0 );
420 paintDataValueText( ctx->
painter(), info.attrs, pos, info.isValuePositive,
421 info.value, justCalculateRect, cumulatedBoundingRect );
424 if ( comment.isEmpty() ) {
429 Qt::AlignHCenter | Qt::AlignVCenter );
430 const QRect rect( pos.toPoint(), item.sizeHint() );
432 if (cumulatedBoundingRect) {
433 (*cumulatedBoundingRect) |= rect;
435 if ( !justCalculateRect ) {
436 item.setGeometry( rect );
440 if ( cumulatedBoundingRect ) {
441 *cumulatedBoundingRect = ctx->
painter()->transform().inverted().mapRect( *cumulatedBoundingRect );
446 const QModelIndex& index, qreal value )
const
452 value = calcPercentValue( index );
462 ret.prepend( dva.
prefix() );
463 ret.append( dva.
suffix() );
468 void AbstractDiagram::Private::paintDataValueText(
470 const QModelIndex& index,
473 bool justCalculateRect ,
474 QRectF* cumulatedBoundingRect )
477 const QString text = formatDataValueText( dva, index, value );
478 paintDataValueText( painter, dva, pos, value >= 0.0, text,
479 justCalculateRect, cumulatedBoundingRect );
482 void AbstractDiagram::Private::paintDataValueText(
486 bool valueIsPositive,
488 bool justCalculateRect ,
489 QRectF* cumulatedBoundingRect )
499 prevPaintedDataValueText = text;
502 doc.setDocumentMargin( 0.0 );
503 if ( Qt::mightBeRichText( text ) ) {
506 doc.setPlainText( text );
511 const PainterSaver painterSaver( painter );
514 doc.setDefaultFont( calculatedFont );
515 QAbstractTextDocumentLayout::PaintContext context;
516 context.palette = diagram->palette();
517 context.palette.setColor( QPalette::Text, ta.
pen().color() );
519 QAbstractTextDocumentLayout*
const layout = doc.documentLayout();
520 layout->setPaintDevice( painter->device() );
522 painter->translate( pos.x(), pos.y() );
527 painter->rotate( rotation );
530 QTransform transform = painter->worldTransform();
538 const QRectF br( layout->frameBoundingRect( doc.rootFrame() ) );
539 QPolygon pr = transform.mapToPolygon( br.toRect() );
543 path.addPolygon( pr );
547 for (
int i = alreadyDrawnDataValueTexts.count() - 1; i >= 0; i-- ) {
548 if ( alreadyDrawnDataValueTexts.at( i ).intersects( path ) ) {
555 alreadyDrawnDataValueTexts << path;
560 QRectF rect = layout->frameBoundingRect( doc.rootFrame() );
561 if ( cumulatedBoundingRect ) {
562 (*cumulatedBoundingRect) |= transform.mapRect( rect );
564 if ( !justCalculateRect ) {
565 bool paintBack =
false;
567 if ( back.isVisible() ) {
569 painter->setBrush( back.brush() );
571 painter->setBrush( QBrush() );
576 if ( frame.isVisible() ) {
578 painter->setPen( frame.pen() );
579 radius = frame.cornerRadius();
583 QRectF borderRect( QPointF( 0, 0 ), rect.size() );
584 painter->drawRoundedRect( borderRect, radius, radius );
586 layout->draw( painter, context );
591 QModelIndex AbstractDiagram::Private::indexAt(
const QPoint& point )
const
593 QModelIndexList l = indexesAt( point );
598 return QModelIndex();
601 QModelIndexList AbstractDiagram::Private::indexesAt(
const QPoint& point )
const
603 return reverseMapper.indexesAt( point );
606 QModelIndexList AbstractDiagram::Private::indexesIn(
const QRect& rect )
const
608 return reverseMapper.indexesIn( rect );
611 CartesianDiagramDataCompressor::AggregatedDataValueAttributes AbstractDiagram::Private::aggregatedAttrs(
612 const QModelIndex& index,
613 const CartesianDiagramDataCompressor::CachePosition* position )
const
615 Q_UNUSED( position );
616 CartesianDiagramDataCompressor::AggregatedDataValueAttributes allAttrs;
617 allAttrs[index] = diagram->dataValueAttributes( index );
621 void AbstractDiagram::Private::setDatasetAttrs(
int dataset,
const QVariant& data,
int role )
627 int column = dataset * datasetDimension;
636 for (
int i = 0; i < columnSpan; i++ ) {
637 attributesModel->setHeaderData( column + i, Qt::Horizontal, data, role );
641 QVariant AbstractDiagram::Private::datasetAttrs(
int dataset,
int role )
const
644 int column = dataset * datasetDimension;
645 return attributesModel->headerData( column, Qt::Horizontal, role );
648 void AbstractDiagram::Private::resetDatasetAttrs(
int dataset,
int role )
651 int column = dataset * datasetDimension;
652 attributesModel->resetHeaderData( column, Qt::Horizontal, role );
655 bool AbstractDiagram::Private::isTransposed()
const
667 const BarDiagram* barDiagram = qobject_cast< const BarDiagram* >( refDiagram );
671 return barDiagram->
orientation() == Qt::Horizontal;
674 LineAttributesInfo::LineAttributesInfo()
678 LineAttributesInfo::LineAttributesInfo(
const QModelIndex& _index,
const QPointF& _value,
const QPointF& _nextValue )
681 , nextValue ( _nextValue )