KD Chart 2
[rev.2.5]
|
00001 /**************************************************************************** 00002 ** Copyright (C) 2001-2012 Klaralvdalens Datakonsult AB. All rights reserved. 00003 ** 00004 ** This file is part of the KD Chart library. 00005 ** 00006 ** Licensees holding valid commercial KD Chart licenses may use this file in 00007 ** accordance with the KD Chart Commercial License Agreement provided with 00008 ** the Software. 00009 ** 00010 ** 00011 ** This file may be distributed and/or modified under the terms of the 00012 ** GNU General Public License version 2 and version 3 as published by the 00013 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. 00014 ** 00015 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 00016 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 00017 ** 00018 ** Contact info@kdab.com if any conditions of this licensing are not 00019 ** clear to you. 00020 ** 00021 **********************************************************************/ 00022 00023 #include "KDChartAbstractDiagram.h" 00024 #include "KDChartAbstractDiagram_p.h" 00025 00026 #include <QPainter> 00027 #include <QDebug> 00028 #include <QApplication> 00029 #include <QAbstractProxyModel> 00030 #include <QAbstractTextDocumentLayout> 00031 #include <QStandardItemModel> 00032 #include <QSizeF> 00033 #include <QTextDocument> 00034 00035 #include "KDChartAbstractCoordinatePlane.h" 00036 #include "KDChartChart.h" 00037 #include "KDChartDataValueAttributes.h" 00038 #include "KDChartTextAttributes.h" 00039 #include "KDChartMarkerAttributes.h" 00040 #include "KDChartAbstractThreeDAttributes.h" 00041 #include "KDChartThreeDLineAttributes.h" 00042 #include "KDChartPainterSaver_p.h" 00043 00044 #include <KDABLibFakes> 00045 00046 #include <limits> 00047 00048 using namespace KDChart; 00049 00050 #define d d_func() 00051 00052 AbstractDiagram::AbstractDiagram ( QWidget* parent, AbstractCoordinatePlane* plane ) 00053 : QAbstractItemView ( parent ), _d( new Private() ) 00054 { 00055 _d->init( plane ); 00056 init(); 00057 } 00058 00059 AbstractDiagram::~AbstractDiagram() 00060 { 00061 emit aboutToBeDestroyed(); 00062 delete _d; 00063 } 00064 00065 void AbstractDiagram::init() 00066 { 00067 _d->diagram = this; 00068 d->reverseMapper.setDiagram( this ); 00069 } 00070 00071 00072 bool AbstractDiagram::compare( const AbstractDiagram* other ) const 00073 { 00074 if( other == this ) return true; 00075 if ( !other ) { 00076 return false; 00077 } 00078 return // compare QAbstractScrollArea properties 00079 (horizontalScrollBarPolicy() == other->horizontalScrollBarPolicy()) && 00080 (verticalScrollBarPolicy() == other->verticalScrollBarPolicy()) && 00081 // compare QFrame properties 00082 (frameShadow() == other->frameShadow()) && 00083 (frameShape() == other->frameShape()) && 00084 // frameWidth is a read-only property defined by the style, it should not be in here: 00085 // (frameWidth() == other->frameWidth()) && 00086 (lineWidth() == other->lineWidth()) && 00087 (midLineWidth() == other->midLineWidth()) && 00088 // compare QAbstractItemView properties 00089 (alternatingRowColors() == other->alternatingRowColors()) && 00090 (hasAutoScroll() == other->hasAutoScroll()) && 00091 #if QT_VERSION > 0x040199 00092 (dragDropMode() == other->dragDropMode()) && 00093 (dragDropOverwriteMode() == other->dragDropOverwriteMode()) && 00094 (horizontalScrollMode() == other->horizontalScrollMode ()) && 00095 (verticalScrollMode() == other->verticalScrollMode()) && 00096 #endif 00097 (dragEnabled() == other->dragEnabled()) && 00098 (editTriggers() == other->editTriggers()) && 00099 (iconSize() == other->iconSize()) && 00100 (selectionBehavior() == other->selectionBehavior()) && 00101 (selectionMode() == other->selectionMode()) && 00102 (showDropIndicator() == other->showDropIndicator()) && 00103 (tabKeyNavigation() == other->tabKeyNavigation()) && 00104 (textElideMode() == other->textElideMode()) && 00105 // compare all of the properties stored in the attributes model 00106 attributesModel()->compare( other->attributesModel() ) && 00107 // compare own properties 00108 (rootIndex().column() == other->rootIndex().column()) && 00109 (rootIndex().row() == other->rootIndex().row()) && 00110 (allowOverlappingDataValueTexts() == other->allowOverlappingDataValueTexts()) && 00111 (antiAliasing() == other->antiAliasing()) && 00112 (percentMode() == other->percentMode()) && 00113 (datasetDimension() == other->datasetDimension()); 00114 } 00115 00116 AbstractCoordinatePlane* AbstractDiagram::coordinatePlane() const 00117 { 00118 return d->plane; 00119 } 00120 00121 const QPair<QPointF, QPointF> AbstractDiagram::dataBoundaries () const 00122 { 00123 if( d->databoundariesDirty ){ 00124 d->databoundaries = calculateDataBoundaries (); 00125 d->databoundariesDirty = false; 00126 } 00127 return d->databoundaries; 00128 } 00129 00130 void AbstractDiagram::setDataBoundariesDirty() const 00131 { 00132 d->databoundariesDirty = true; 00133 update(); 00134 } 00135 00136 void AbstractDiagram::setModel( QAbstractItemModel * newModel ) 00137 { 00138 if( model() ) 00139 { 00140 disconnect( model(), SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); 00141 disconnect( model(), SIGNAL( columnsInserted( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); 00142 disconnect( model(), SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); 00143 disconnect( model(), SIGNAL( columnsRemoved( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); 00144 disconnect( model(), SIGNAL( modelReset() ), this, SLOT( setDataBoundariesDirty() ) ); 00145 disconnect( model(), SIGNAL( layoutChanged() ), this, SLOT( setDataBoundariesDirty() ) ); 00146 disconnect( model(), SIGNAL( dataChanged(QModelIndex,QModelIndex) ), this, SIGNAL( modelDataChanged() )); 00147 } 00148 QAbstractItemView::setModel( newModel ); 00149 AttributesModel* amodel = new PrivateAttributesModel( newModel, this ); 00150 amodel->initFrom( d->attributesModel ); 00151 d->setAttributesModel(amodel); 00152 scheduleDelayedItemsLayout(); 00153 setDataBoundariesDirty(); 00154 if( model() ) 00155 { 00156 connect( model(), SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); 00157 connect( model(), SIGNAL( columnsInserted( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); 00158 connect( model(), SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); 00159 connect( model(), SIGNAL( columnsRemoved( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); 00160 connect( model(), SIGNAL( modelReset() ), this, SLOT( setDataBoundariesDirty() ) ); 00161 connect( model(), SIGNAL( layoutChanged() ), this, SLOT( setDataBoundariesDirty() ) ); 00162 connect( model(), SIGNAL( dataChanged(QModelIndex,QModelIndex) ), this, SIGNAL( modelDataChanged() )); 00163 } 00164 emit modelsChanged(); 00165 } 00166 00167 void AbstractDiagram::setSelectionModel( QItemSelectionModel* newSelectionModel ) 00168 { 00169 if( selectionModel() ) 00170 { 00171 disconnect( selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), this, SIGNAL( modelsChanged() ) ); 00172 disconnect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SIGNAL( modelsChanged() ) ); 00173 } 00174 QAbstractItemView::setSelectionModel( newSelectionModel ); 00175 if( selectionModel() ) 00176 { 00177 connect( selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), this, SIGNAL( modelsChanged() ) ); 00178 connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SIGNAL( modelsChanged() ) ); 00179 } 00180 emit modelsChanged(); 00181 } 00182 00188 void AbstractDiagram::setAttributesModel( AttributesModel* amodel ) 00189 { 00190 if( amodel->sourceModel() != model() ) { 00191 qWarning("KDChart::AbstractDiagram::setAttributesModel() failed: " 00192 "Trying to set an attributesmodel which works on a different " 00193 "model than the diagram."); 00194 return; 00195 } 00196 if( qobject_cast<PrivateAttributesModel*>(amodel) ) { 00197 qWarning("KDChart::AbstractDiagram::setAttributesModel() failed: " 00198 "Trying to set an attributesmodel that is private to another diagram."); 00199 return; 00200 } 00201 d->setAttributesModel(amodel); 00202 scheduleDelayedItemsLayout(); 00203 setDataBoundariesDirty(); 00204 emit modelsChanged(); 00205 } 00206 00207 bool AbstractDiagram::usesExternalAttributesModel() const 00208 { 00209 return d->usesExternalAttributesModel(); 00210 } 00211 00212 AttributesModel* AbstractDiagram::attributesModel() const 00213 { 00214 return d->attributesModel; 00215 } 00216 00217 QModelIndex AbstractDiagram::conditionallyMapFromSource( const QModelIndex & index ) const 00218 { 00219 Q_ASSERT( !index.isValid() || index.model() == attributesModel() || index.model() == attributesModel()->sourceModel() ); 00220 return index.model() == attributesModel() ? index : attributesModel()->mapFromSource( index ); 00221 } 00222 00224 void AbstractDiagram::setRootIndex ( const QModelIndex& idx ) 00225 { 00226 QAbstractItemView::setRootIndex( idx ); 00227 setAttributesModelRootIndex( d->attributesModel->mapFromSource( idx ) ); 00228 } 00229 00231 void AbstractDiagram::setAttributesModelRootIndex( const QModelIndex& idx ) 00232 { 00233 d->attributesModelRootIndex = idx; 00234 setDataBoundariesDirty(); 00235 scheduleDelayedItemsLayout(); 00236 } 00237 00240 QModelIndex AbstractDiagram::attributesModelRootIndex() const 00241 { 00242 if ( !d->attributesModelRootIndex.isValid() ) 00243 d->attributesModelRootIndex = d->attributesModel->mapFromSource( rootIndex() ); 00244 return d->attributesModelRootIndex; 00245 } 00246 00247 void AbstractDiagram::setCoordinatePlane( AbstractCoordinatePlane* parent ) 00248 { 00249 d->plane = parent; 00250 } 00251 00252 void AbstractDiagram::doItemsLayout() 00253 { 00254 if ( d->plane ) { 00255 d->plane->layoutDiagrams(); 00256 update(); 00257 } 00258 QAbstractItemView::doItemsLayout(); 00259 } 00260 00261 void AbstractDiagram::dataChanged( const QModelIndex &topLeft, 00262 const QModelIndex &bottomRight ) 00263 { 00264 Q_UNUSED( topLeft ); 00265 Q_UNUSED( bottomRight ); 00266 // We are still too dumb to do intelligent updates... 00267 setDataBoundariesDirty(); 00268 scheduleDelayedItemsLayout(); 00269 } 00270 00271 00272 void AbstractDiagram::setHidden( const QModelIndex & index, bool hidden ) 00273 { 00274 d->attributesModel->setData( 00275 conditionallyMapFromSource( index ), 00276 qVariantFromValue( hidden ), 00277 DataHiddenRole ); 00278 emit dataHidden(); 00279 } 00280 00281 void AbstractDiagram::setHidden( int dataset, bool hidden ) 00282 { 00283 d->setDatasetAttrs( dataset, qVariantFromValue( hidden ), DataHiddenRole ); 00284 emit dataHidden(); 00285 } 00286 00287 void AbstractDiagram::setHidden( bool hidden ) 00288 { 00289 d->attributesModel->setModelData( qVariantFromValue( hidden ), DataHiddenRole ); 00290 emit dataHidden(); 00291 } 00292 00293 bool AbstractDiagram::isHidden() const 00294 { 00295 return qVariantValue< bool >( attributesModel()->modelData( DataHiddenRole ) ); 00296 } 00297 00298 bool AbstractDiagram::isHidden( int dataset ) const 00299 { 00300 const QVariant boolFlag( d->datasetAttrs( dataset, DataHiddenRole ) ); 00301 if( boolFlag.isValid() ) 00302 return qVariantValue< bool >( boolFlag ); 00303 return isHidden(); 00304 } 00305 00306 bool AbstractDiagram::isHidden( const QModelIndex & index ) const 00307 { 00308 const QVariant boolFlag( attributesModel()->data( conditionallyMapFromSource( index ), 00309 DataHiddenRole ) ); 00310 if ( boolFlag.isValid() ) { 00311 return qVariantValue< bool >( boolFlag ); 00312 } 00313 int dataset = index.column() / d->datasetDimension; 00314 return isHidden( dataset ); 00315 } 00316 00317 00318 void AbstractDiagram::setDataValueAttributes( const QModelIndex & index, 00319 const DataValueAttributes & a ) 00320 { 00321 d->attributesModel->setData( conditionallyMapFromSource( index ), qVariantFromValue( a ), 00322 DataValueLabelAttributesRole ); 00323 emit propertiesChanged(); 00324 } 00325 00326 00327 void AbstractDiagram::setDataValueAttributes( int dataset, const DataValueAttributes & a ) 00328 { 00329 d->setDatasetAttrs( dataset, qVariantFromValue( a ), DataValueLabelAttributesRole ); 00330 emit propertiesChanged(); 00331 } 00332 00333 DataValueAttributes AbstractDiagram::dataValueAttributes() const 00334 { 00335 return qVariantValue<DataValueAttributes>( 00336 attributesModel()->modelData( KDChart::DataValueLabelAttributesRole ) ); 00337 } 00338 00339 DataValueAttributes AbstractDiagram::dataValueAttributes( int dataset ) const 00340 { 00341 /* 00342 The following did not work! 00343 (khz, 2008-01-25) 00344 If there was some attrs specified for the 0-th cells of a dataset, 00345 then this logic would return the cell's settings instead of the header settings: 00346 00347 return qVariantValue<DataValueAttributes>( 00348 attributesModel()->data( attributesModel()->mapFromSource(columnToIndex( column )), 00349 KDChart::DataValueLabelAttributesRole ) ); 00350 */ 00351 00352 const QVariant headerAttrs( 00353 d->datasetAttrs( dataset, KDChart::DataValueLabelAttributesRole ) ); 00354 if( headerAttrs.isValid() ) 00355 return qVariantValue< DataValueAttributes >( headerAttrs ); 00356 return dataValueAttributes(); 00357 } 00358 00359 DataValueAttributes AbstractDiagram::dataValueAttributes( const QModelIndex & index ) const 00360 { 00361 return qVariantValue<DataValueAttributes>( 00362 attributesModel()->data( 00363 conditionallyMapFromSource( index ), 00364 KDChart::DataValueLabelAttributesRole ) ); 00365 } 00366 00367 void AbstractDiagram::setDataValueAttributes( const DataValueAttributes & a ) 00368 { 00369 d->attributesModel->setModelData( qVariantFromValue( a ), DataValueLabelAttributesRole ); 00370 emit propertiesChanged(); 00371 } 00372 00373 void AbstractDiagram::setAllowOverlappingDataValueTexts( bool allow ) 00374 { 00375 DataValueAttributes attrs = dataValueAttributes(); 00376 attrs.setShowOverlappingDataLabels( allow ); 00377 setDataValueAttributes( attrs ); 00378 d->allowOverlappingDataValueTexts = allow; 00379 emit propertiesChanged(); 00380 } 00381 00382 bool AbstractDiagram::allowOverlappingDataValueTexts() const 00383 { 00384 return d->allowOverlappingDataValueTexts; 00385 } 00386 00387 void AbstractDiagram::setAntiAliasing( bool enabled ) 00388 { 00389 d->antiAliasing = enabled; 00390 emit propertiesChanged(); 00391 } 00392 00393 bool AbstractDiagram::antiAliasing() const 00394 { 00395 return d->antiAliasing; 00396 } 00397 00398 void AbstractDiagram::setPercentMode ( bool percent ) 00399 { 00400 d->percent = percent; 00401 emit propertiesChanged(); 00402 } 00403 00404 bool AbstractDiagram::percentMode() const 00405 { 00406 return d->percent; 00407 } 00408 00409 00410 void AbstractDiagram::paintDataValueText( QPainter* painter, 00411 const QModelIndex& index, 00412 const QPointF& pos, 00413 qreal value ) 00414 { 00415 d->paintDataValueText( painter, index, pos, value ); 00416 } 00417 00418 00419 void AbstractDiagram::paintDataValueTexts( QPainter* painter ) 00420 { 00421 if ( !checkInvariants() ) { 00422 return; 00423 } 00424 00425 d->forgetAlreadyPaintedDataValues(); 00426 const int rowCount = model()->rowCount( rootIndex() ); 00427 const int columnCount = model()->columnCount( rootIndex() ); 00428 for ( int column = 0; column < columnCount; column += datasetDimension() ) { 00429 for ( int row = 0; row < rowCount; ++row ) { 00430 QModelIndex index = model()->index( row, column, rootIndex() ); // checked 00431 qreal x; 00432 qreal y; 00433 if ( datasetDimension() == 1 ) { 00434 x = row; 00435 y = index.data().toReal(); 00436 } else { 00437 x = index.data().toReal(); 00438 y = model()->index( row, column + 1, rootIndex() ).data().toReal(); 00439 } 00440 paintDataValueText( painter, index, coordinatePlane()->translate( QPointF( x, y ) ), y ); 00441 } 00442 } 00443 } 00444 00445 00446 void AbstractDiagram::paintMarker( QPainter* painter, 00447 const DataValueAttributes& a, 00448 const QModelIndex& index, 00449 const QPointF& pos ) 00450 { 00451 if ( !checkInvariants() || !a.isVisible() ) return; 00452 const MarkerAttributes ma = a.markerAttributes(); 00453 if ( !ma.isVisible() ) return; 00454 00455 const PainterSaver painterSaver( painter ); 00456 // the size of the marker - unscaled 00457 const QSizeF maSize( ma.markerSize().width() / painter->matrix().m11(), 00458 ma.markerSize().height() / painter->matrix().m22() ); 00459 QBrush indexBrush( brush( index ) ); 00460 QPen indexPen( ma.pen() ); 00461 if ( ma.markerColor().isValid() ) 00462 indexBrush.setColor( ma.markerColor() ); 00463 00464 paintMarker( painter, ma, indexBrush, indexPen, pos, maSize ); 00465 00466 // workaround: BC cannot be changed, otherwise we would pass the 00467 // index down to next-lower paintMarker function. So far, we 00468 // basically save a circle of radius maSize at pos in the 00469 // reverseMapper. This means that ^^^ this version of paintMarker 00470 // needs to be called to reverse-map the marker. 00471 d->reverseMapper.addCircle( index.row(), index.column(), pos, 2 * maSize ); 00472 } 00473 00474 void AbstractDiagram::paintMarker( QPainter* painter, 00475 const QModelIndex& index, 00476 const QPointF& pos ) 00477 { 00478 if ( !checkInvariants() ) return; 00479 paintMarker( painter, dataValueAttributes( index ), index, pos ); 00480 } 00481 00482 void AbstractDiagram::paintMarker( QPainter* painter, 00483 const MarkerAttributes& markerAttributes, 00484 const QBrush& brush, 00485 const QPen& pen, 00486 const QPointF& pos, 00487 const QSizeF& maSize ) 00488 { 00489 const QPen oldPen( painter->pen() ); 00490 // Pen is used to paint 4Pixels - 1 Pixel - Ring and FastCross types. 00491 // make sure to use the brush color - see above in those cases. 00492 const bool isFourPixels = (markerAttributes.markerStyle() == MarkerAttributes::Marker4Pixels); 00493 if( isFourPixels || (markerAttributes.markerStyle() == MarkerAttributes::Marker1Pixel) ){ 00494 // for high-performance point charts with tiny point markers: 00495 painter->setPen( PrintingParameters::scalePen( QPen( brush.color().light() ) ) ); 00496 if( isFourPixels ){ 00497 const qreal x = pos.x(); 00498 const qreal y = pos.y(); 00499 painter->drawLine( QPointF(x-1.0,y-1.0), 00500 QPointF(x+1.0,y-1.0) ); 00501 painter->drawLine( QPointF(x-1.0,y), 00502 QPointF(x+1.0,y) ); 00503 painter->drawLine( QPointF(x-1.0,y+1.0), 00504 QPointF(x+1.0,y+1.0) ); 00505 } 00506 painter->drawPoint( pos ); 00507 }else{ 00508 const PainterSaver painterSaver( painter ); 00509 QPen painterPen( pen ); 00510 painter->setPen( PrintingParameters::scalePen( painterPen ) ); 00511 painter->setBrush( brush ); 00512 painter->setRenderHint ( QPainter::Antialiasing ); 00513 painter->translate( pos ); 00514 switch ( markerAttributes.markerStyle() ) { 00515 case MarkerAttributes::MarkerCircle: 00516 { 00517 if ( markerAttributes.threeD() ) { 00518 QRadialGradient grad; 00519 grad.setCoordinateMode( QGradient::ObjectBoundingMode ); 00520 QColor drawColor = brush.color(); 00521 grad.setCenter( 0.5, 0.5 ); 00522 grad.setRadius( 1.0 ); 00523 grad.setFocalPoint( 0.35, 0.35 ); 00524 grad.setColorAt( 0.00, drawColor.lighter( 150 ) ); 00525 grad.setColorAt( 0.20, drawColor ); 00526 grad.setColorAt( 0.50, drawColor.darker( 150 ) ); 00527 grad.setColorAt( 0.75, drawColor.darker( 200 ) ); 00528 grad.setColorAt( 0.95, drawColor.darker( 250 ) ); 00529 grad.setColorAt( 1.00, drawColor.darker( 200 ) ); 00530 QBrush newBrush( grad ); 00531 newBrush.setMatrix( brush.matrix() ); 00532 painter->setBrush( newBrush ); 00533 } 00534 painter->drawEllipse( QRectF( 0 - maSize.height()/2, 0 - maSize.width()/2, 00535 maSize.height(), maSize.width()) ); 00536 } 00537 break; 00538 case MarkerAttributes::MarkerSquare: 00539 { 00540 QRectF rect( 0 - maSize.width()/2, 0 - maSize.height()/2, 00541 maSize.width(), maSize.height() ); 00542 painter->drawRect( rect ); 00543 break; 00544 } 00545 case MarkerAttributes::MarkerDiamond: 00546 { 00547 QVector <QPointF > diamondPoints; 00548 QPointF top, left, bottom, right; 00549 top = QPointF( 0, 0 - maSize.height()/2 ); 00550 left = QPointF( 0 - maSize.width()/2, 0 ); 00551 bottom = QPointF( 0, maSize.height()/2 ); 00552 right = QPointF( maSize.width()/2, 0 ); 00553 diamondPoints << top << left << bottom << right; 00554 painter->drawPolygon( diamondPoints ); 00555 break; 00556 } 00557 // both handled on top of the method: 00558 case MarkerAttributes::Marker1Pixel: 00559 case MarkerAttributes::Marker4Pixels: 00560 break; 00561 case MarkerAttributes::MarkerRing: 00562 { 00563 painter->setBrush( Qt::NoBrush ); 00564 painter->drawEllipse( QRectF( 0 - maSize.height()/2, 0 - maSize.width()/2, 00565 maSize.height(), maSize.width()) ); 00566 break; 00567 } 00568 case MarkerAttributes::MarkerCross: 00569 { 00570 // Note: Markers can have outline, 00571 // so just drawing two rects is NOT the solution here! 00572 const qreal w02 = maSize.width() * 0.2; 00573 const qreal w05 = maSize.width() * 0.5; 00574 const qreal h02 = maSize.height()* 0.2; 00575 const qreal h05 = maSize.height()* 0.5; 00576 QVector <QPointF > crossPoints; 00577 QPointF p[12]; 00578 p[ 0] = QPointF( -w02, -h05 ); 00579 p[ 1] = QPointF( w02, -h05 ); 00580 p[ 2] = QPointF( w02, -h02 ); 00581 p[ 3] = QPointF( w05, -h02 ); 00582 p[ 4] = QPointF( w05, h02 ); 00583 p[ 5] = QPointF( w02, h02 ); 00584 p[ 6] = QPointF( w02, h05 ); 00585 p[ 7] = QPointF( -w02, h05 ); 00586 p[ 8] = QPointF( -w02, h02 ); 00587 p[ 9] = QPointF( -w05, h02 ); 00588 p[10] = QPointF( -w05, -h02 ); 00589 p[11] = QPointF( -w02, -h02 ); 00590 for( int i=0; i<12; ++i ) 00591 crossPoints << p[i]; 00592 crossPoints << p[0]; 00593 painter->drawPolygon( crossPoints ); 00594 break; 00595 } 00596 case MarkerAttributes::MarkerFastCross: 00597 { 00598 QPointF left, right, top, bottom; 00599 left = QPointF( -maSize.width()/2, 0 ); 00600 right = QPointF( maSize.width()/2, 0 ); 00601 top = QPointF( 0, -maSize.height()/2 ); 00602 bottom= QPointF( 0, maSize.height()/2 ); 00603 painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) ); 00604 painter->drawLine( left, right ); 00605 painter->drawLine( top, bottom ); 00606 break; 00607 } 00608 case MarkerAttributes::NoMarker: 00609 break; 00610 case MarkerAttributes::PainterPathMarker: 00611 { 00612 QPainterPath path = markerAttributes.customMarkerPath(); 00613 painter->scale(maSize.height()/path.boundingRect().height(), 00614 maSize.width()/path.boundingRect().width()); 00615 painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) ); 00616 painter->drawPath(path); 00617 break; 00618 } 00619 default: 00620 Q_ASSERT_X ( false, "paintMarkers()", 00621 "Type item does not match a defined Marker Type." ); 00622 } 00623 } 00624 painter->setPen( oldPen ); 00625 } 00626 00627 void AbstractDiagram::paintMarkers( QPainter* painter ) 00628 { 00629 if ( !checkInvariants() ) { 00630 return; 00631 } 00632 00633 const int rowCount = model()->rowCount( rootIndex() ); 00634 const int columnCount = model()->columnCount( rootIndex() ); 00635 for ( int column = 0; column < columnCount; column += datasetDimension() ) { 00636 for ( int row = 0; row < rowCount; ++row ) { 00637 QModelIndex index = model()->index( row, column, rootIndex() ); // checked 00638 qreal x; 00639 qreal y; 00640 if ( datasetDimension() == 1 ) { 00641 x = row; 00642 y = index.data().toReal(); 00643 } else { 00644 x = index.data().toReal(); 00645 y = model()->index( row, column + 1, rootIndex() ).data().toReal(); 00646 } 00647 paintMarker( painter, index, coordinatePlane()->translate( QPointF( x, y ) ) ); 00648 } 00649 } 00650 } 00651 00652 00653 void AbstractDiagram::setPen( const QModelIndex& index, const QPen& pen ) 00654 { 00655 attributesModel()->setData( 00656 conditionallyMapFromSource( index ), 00657 qVariantFromValue( pen ), DatasetPenRole ); 00658 emit propertiesChanged(); 00659 } 00660 00661 void AbstractDiagram::setPen( const QPen& pen ) 00662 { 00663 attributesModel()->setModelData( 00664 qVariantFromValue( pen ), DatasetPenRole ); 00665 emit propertiesChanged(); 00666 } 00667 00668 void AbstractDiagram::setPen( int dataset, const QPen& pen ) 00669 { 00670 d->setDatasetAttrs( dataset, qVariantFromValue( pen ), DatasetPenRole ); 00671 emit propertiesChanged(); 00672 } 00673 00674 QPen AbstractDiagram::pen() const 00675 { 00676 return qVariantValue<QPen>( 00677 attributesModel()->data( DatasetPenRole ) ); 00678 } 00679 00680 QPen AbstractDiagram::pen( int dataset ) const 00681 { 00682 const QVariant penSettings( d->datasetAttrs( dataset, DatasetPenRole ) ); 00683 if( penSettings.isValid() ) 00684 return qVariantValue< QPen >( penSettings ); 00685 return pen(); 00686 } 00687 00688 QPen AbstractDiagram::pen( const QModelIndex& index ) const 00689 { 00690 return qVariantValue<QPen>( 00691 attributesModel()->data( 00692 conditionallyMapFromSource( index ), 00693 DatasetPenRole ) ); 00694 } 00695 00696 void AbstractDiagram::setBrush( const QModelIndex& index, const QBrush& brush ) 00697 { 00698 attributesModel()->setData( 00699 conditionallyMapFromSource( index ), 00700 qVariantFromValue( brush ), DatasetBrushRole ); 00701 emit propertiesChanged(); 00702 } 00703 00704 void AbstractDiagram::setBrush( const QBrush& brush ) 00705 { 00706 attributesModel()->setModelData( 00707 qVariantFromValue( brush ), DatasetBrushRole ); 00708 emit propertiesChanged(); 00709 } 00710 00711 void AbstractDiagram::setBrush( int dataset, const QBrush& brush ) 00712 { 00713 d->setDatasetAttrs( dataset, qVariantFromValue( brush ), DatasetBrushRole ); 00714 emit propertiesChanged(); 00715 } 00716 00717 QBrush AbstractDiagram::brush() const 00718 { 00719 return qVariantValue<QBrush>( 00720 attributesModel()->data( DatasetBrushRole ) ); 00721 } 00722 00723 QBrush AbstractDiagram::brush( int dataset ) const 00724 { 00725 const QVariant brushSettings( d->datasetAttrs( dataset, DatasetBrushRole ) ); 00726 if( brushSettings.isValid() ) 00727 return qVariantValue< QBrush >( brushSettings ); 00728 return brush(); 00729 } 00730 00731 QBrush AbstractDiagram::brush( const QModelIndex& index ) const 00732 { 00733 return qVariantValue<QBrush>( 00734 attributesModel()->data( conditionallyMapFromSource( index ), DatasetBrushRole ) ); 00735 } 00736 00743 void AbstractDiagram::setUnitPrefix( const QString& prefix, int column, Qt::Orientation orientation ) 00744 { 00745 d->unitPrefixMap[ column ][ orientation ]= prefix; 00746 } 00747 00753 void AbstractDiagram::setUnitPrefix( const QString& prefix, Qt::Orientation orientation ) 00754 { 00755 d->unitPrefix[ orientation ] = prefix; 00756 } 00757 00764 void AbstractDiagram::setUnitSuffix( const QString& suffix, int column, Qt::Orientation orientation ) 00765 { 00766 d->unitSuffixMap[ column ][ orientation ]= suffix; 00767 } 00768 00774 void AbstractDiagram::setUnitSuffix( const QString& suffix, Qt::Orientation orientation ) 00775 { 00776 d->unitSuffix[ orientation ] = suffix; 00777 } 00778 00786 QString AbstractDiagram::unitPrefix( int column, Qt::Orientation orientation, bool fallback ) const 00787 { 00788 if( !fallback || d->unitPrefixMap[ column ].contains( orientation ) ) 00789 return d->unitPrefixMap[ column ][ orientation ]; 00790 return d->unitPrefix[ orientation ]; 00791 } 00792 00797 QString AbstractDiagram::unitPrefix( Qt::Orientation orientation ) const 00798 { 00799 return d->unitPrefix[ orientation ]; 00800 } 00801 00809 QString AbstractDiagram::unitSuffix( int column, Qt::Orientation orientation, bool fallback ) const 00810 { 00811 if( !fallback || d->unitSuffixMap[ column ].contains( orientation ) ) 00812 return d->unitSuffixMap[ column ][ orientation ]; 00813 return d->unitSuffix[ orientation ]; 00814 } 00815 00820 QString AbstractDiagram::unitSuffix( Qt::Orientation orientation ) const 00821 { 00822 return d->unitSuffix[ orientation ]; 00823 } 00824 00825 // implement QAbstractItemView: 00826 QRect AbstractDiagram::visualRect( const QModelIndex &index ) const 00827 { 00828 return d->reverseMapper.boundingRect( index.row(), index.column() ).toRect(); 00829 } 00830 00831 void AbstractDiagram::scrollTo(const QModelIndex &, ScrollHint ) 00832 {} 00833 00834 // indexAt ... down below 00835 00836 QModelIndex AbstractDiagram::moveCursor(CursorAction, Qt::KeyboardModifiers ) 00837 { return QModelIndex(); } 00838 00839 int AbstractDiagram::horizontalOffset() const 00840 { return 0; } 00841 00842 int AbstractDiagram::verticalOffset() const 00843 { return 0; } 00844 00845 bool AbstractDiagram::isIndexHidden(const QModelIndex &) const 00846 { return true; } 00847 00848 void AbstractDiagram::setSelection(const QRect& rect , QItemSelectionModel::SelectionFlags command ) 00849 { 00850 const QModelIndexList indexes = d->indexesIn( rect ); 00851 QItemSelection selection; 00852 KDAB_FOREACH( const QModelIndex& index, indexes ) 00853 { 00854 selection.append( QItemSelectionRange( index ) ); 00855 } 00856 selectionModel()->select( selection, command ); 00857 } 00858 00859 QRegion AbstractDiagram::visualRegionForSelection(const QItemSelection &selection) const 00860 { 00861 QPolygonF polygon; 00862 KDAB_FOREACH( const QModelIndex& index, selection.indexes() ) 00863 { 00864 polygon << d->reverseMapper.polygon(index.row(), index.column()); 00865 } 00866 return polygon.isEmpty() ? QRegion() : QRegion( polygon.toPolygon() ); 00867 } 00868 00869 QRegion AbstractDiagram::visualRegion(const QModelIndex &index) const 00870 { 00871 QPolygonF polygon = d->reverseMapper.polygon(index.row(), index.column()); 00872 return polygon.isEmpty() ? QRegion() : QRegion( polygon.toPolygon() ); 00873 } 00874 00875 void KDChart::AbstractDiagram::useDefaultColors( ) 00876 { 00877 d->attributesModel->setPaletteType( AttributesModel::PaletteTypeDefault ); 00878 } 00879 00880 void KDChart::AbstractDiagram::useSubduedColors( ) 00881 { 00882 d->attributesModel->setPaletteType( AttributesModel::PaletteTypeSubdued ); 00883 } 00884 00885 void KDChart::AbstractDiagram::useRainbowColors( ) 00886 { 00887 d->attributesModel->setPaletteType( AttributesModel::PaletteTypeRainbow ); 00888 } 00889 00890 QStringList AbstractDiagram::itemRowLabels() const 00891 { 00892 QStringList ret; 00893 if( model() ){ 00894 //qDebug() << "AbstractDiagram::itemRowLabels(): " << attributesModel()->rowCount(attributesModelRootIndex()) << "entries"; 00895 const int rowCount = attributesModel()->rowCount(attributesModelRootIndex()); 00896 for( int i = 0; i < rowCount; ++i ){ 00897 //qDebug() << "item row label: " << attributesModel()->headerData( i, Qt::Vertical, Qt::DisplayRole ).toString(); 00898 ret << unitPrefix( i, Qt::Horizontal, true ) + 00899 attributesModel()->headerData( i, Qt::Vertical, Qt::DisplayRole ).toString() + 00900 unitSuffix( i, Qt::Horizontal, true ); 00901 } 00902 } 00903 return ret; 00904 } 00905 00906 QStringList AbstractDiagram::datasetLabels() const 00907 { 00908 QStringList ret; 00909 if ( !model() ) { 00910 return ret; 00911 } 00912 const int datasetCount = d->datasetCount(); 00913 for ( int i = 0; i < datasetCount; ++i ) { 00914 ret << d->datasetAttrs( i, Qt::DisplayRole ).toString(); 00915 } 00916 return ret; 00917 } 00918 00919 QList<QBrush> AbstractDiagram::datasetBrushes() const 00920 { 00921 QList<QBrush> ret; 00922 if ( !model() ) { 00923 return ret; 00924 } 00925 const int datasetCount = d->datasetCount(); 00926 for ( int i = 0; i < datasetCount; ++i ) { 00927 ret << brush( i ); 00928 } 00929 return ret; 00930 } 00931 00932 QList<QPen> AbstractDiagram::datasetPens() const 00933 { 00934 QList<QPen> ret; 00935 if ( !model() ) { 00936 return ret; 00937 } 00938 const int datasetCount = d->datasetCount(); 00939 for ( int i = 0; i < datasetCount; ++i ) { 00940 ret << pen( i ); 00941 } 00942 return ret; 00943 } 00944 00945 QList<MarkerAttributes> AbstractDiagram::datasetMarkers() const 00946 { 00947 QList<MarkerAttributes> ret; 00948 if ( !model() ) { 00949 return ret; 00950 } 00951 const int datasetCount = d->datasetCount(); 00952 for ( int i = 0; i < datasetCount; ++i ) { 00953 ret << dataValueAttributes( i ).markerAttributes(); 00954 } 00955 return ret; 00956 } 00957 00958 bool AbstractDiagram::checkInvariants( bool justReturnTheStatus ) const 00959 { 00960 if( ! justReturnTheStatus ){ 00961 Q_ASSERT_X ( model(), "AbstractDiagram::checkInvariants()", 00962 "There is no usable model set, for the diagram." ); 00963 00964 Q_ASSERT_X ( coordinatePlane(), "AbstractDiagram::checkInvariants()", 00965 "There is no usable coordinate plane set, for the diagram." ); 00966 } 00967 return model() && coordinatePlane(); 00968 } 00969 00970 int AbstractDiagram::datasetDimension( ) const 00971 { 00972 return d->datasetDimension; 00973 } 00974 00975 void AbstractDiagram::setDatasetDimension( int dimension ) 00976 { 00977 Q_UNUSED( dimension ); 00978 qDebug() << "Setting the dataset dimension using AbstractDiagram::setDatasetDimension is " 00979 "obsolete. Use the specific diagram types instead."; 00980 } 00981 00982 void AbstractDiagram::setDatasetDimensionInternal( int dimension ) 00983 { 00984 Q_ASSERT( dimension != 0 ); 00985 if ( d->datasetDimension == dimension ) { 00986 return; 00987 } 00988 d->datasetDimension = dimension; 00989 d->attributesModel->setDatasetDimension( dimension ); 00990 setDataBoundariesDirty(); 00991 emit layoutChanged( this ); 00992 } 00993 00994 qreal AbstractDiagram::valueForCell( int row, int column ) const 00995 { 00996 if ( !d->attributesModel->hasIndex( row, column, attributesModelRootIndex() ) ) { 00997 qWarning() << "AbstractDiagram::valueForCell(): Requesting value for invalid index!"; 00998 return std::numeric_limits<qreal>::quiet_NaN(); 00999 } 01000 return d->attributesModel->data( 01001 d->attributesModel->index( row, column, attributesModelRootIndex() ) ).toReal(); // checked 01002 } 01003 01004 void AbstractDiagram::update() const 01005 { 01006 if ( d->plane ) { 01007 d->plane->update(); 01008 } 01009 } 01010 01011 QModelIndex AbstractDiagram::indexAt( const QPoint& point ) const 01012 { 01013 return d->indexAt( point ); 01014 } 01015 01016 QModelIndexList AbstractDiagram::indexesAt( const QPoint& point ) const 01017 { 01018 return d->indexesAt( point ); 01019 } 01020 01021 QModelIndexList AbstractDiagram::indexesIn( const QRect& rect ) const 01022 { 01023 return d->indexesIn( rect ); 01024 }