KD Chart 2 [rev.2.4]
|
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 00043 #include <KDABLibFakes> 00044 00045 #define PI 3.141592653589793 00046 00047 using namespace KDChart; 00048 00049 AbstractDiagram::Private::Private() 00050 : plane( 0 ) 00051 , attributesModel( new PrivateAttributesModel(0,0) ) 00052 , allowOverlappingDataValueTexts( false ) 00053 , antiAliasing( true ) 00054 , percent( false ) 00055 , datasetDimension( 1 ) 00056 , databoundariesDirty(true) 00057 , lastRoundedValue() 00058 , lastX( 0 ) 00059 , mCachedFontMetrics( QFontMetrics( qApp->font() ) ) 00060 { 00061 } 00062 00063 AbstractDiagram::Private::~Private() 00064 { 00065 if( attributesModel && qobject_cast<PrivateAttributesModel*>(attributesModel) ) 00066 delete attributesModel; 00067 } 00068 00069 void AbstractDiagram::Private::init() 00070 { 00071 } 00072 00073 void AbstractDiagram::Private::init( AbstractCoordinatePlane* newPlane ) 00074 { 00075 plane = newPlane; 00076 } 00077 00078 bool AbstractDiagram::Private::usesExternalAttributesModel()const 00079 { 00080 return ( ! attributesModel.isNull() ) && 00081 ( ! qobject_cast<PrivateAttributesModel*>(attributesModel) ); 00082 } 00083 00084 void AbstractDiagram::Private::setAttributesModel( AttributesModel* amodel ) 00085 { 00086 if( !attributesModel.isNull() && 00087 qobject_cast<PrivateAttributesModel*>(attributesModel) ) { 00088 delete attributesModel; 00089 } 00090 attributesModel = amodel; 00091 } 00092 00093 AbstractDiagram::Private::Private( const AbstractDiagram::Private& rhs ) : 00094 // Do not copy the plane 00095 plane( 0 ), 00096 attributesModelRootIndex( QModelIndex() ), 00097 attributesModel( rhs.attributesModel ), 00098 allowOverlappingDataValueTexts( rhs.allowOverlappingDataValueTexts ), 00099 antiAliasing( rhs.antiAliasing ), 00100 percent( rhs.percent ), 00101 datasetDimension( rhs.datasetDimension ), 00102 mCachedFontMetrics( rhs.cachedFontMetrics() ) 00103 { 00104 attributesModel = new PrivateAttributesModel( 0, 0); 00105 attributesModel->initFrom( rhs.attributesModel ); 00106 } 00107 00108 #define d d_func() 00109 00110 AbstractDiagram::AbstractDiagram ( QWidget* parent, AbstractCoordinatePlane* plane ) 00111 : QAbstractItemView ( parent ), _d( new Private() ) 00112 { 00113 _d->init( plane ); 00114 init(); 00115 } 00116 00117 AbstractDiagram::~AbstractDiagram() 00118 { 00119 emit aboutToBeDestroyed(); 00120 delete _d; 00121 } 00122 00123 void AbstractDiagram::init() 00124 { 00125 d->reverseMapper.setDiagram( this ); 00126 } 00127 00128 00129 bool AbstractDiagram::compare( const AbstractDiagram* other )const 00130 { 00131 if( other == this ) return true; 00132 if( ! other ){ 00133 //qDebug() << "AbstractDiagram::compare() cannot compare to Null pointer"; 00134 return false; 00135 } 00136 /* 00137 qDebug() << "\n AbstractDiagram::compare() QAbstractScrollArea:"; 00138 // compare QAbstractScrollArea properties 00139 qDebug() << 00140 ((horizontalScrollBarPolicy() == other->horizontalScrollBarPolicy()) && 00141 (verticalScrollBarPolicy() == other->verticalScrollBarPolicy())); 00142 qDebug() << "AbstractDiagram::compare() QFrame:"; 00143 // compare QFrame properties 00144 qDebug() << 00145 ((frameShadow() == other->frameShadow()) && 00146 (frameShape() == other->frameShape()) && 00147 (frameWidth() == other->frameWidth()) && 00148 (lineWidth() == other->lineWidth()) && 00149 (midLineWidth() == other->midLineWidth())); 00150 qDebug() << "AbstractDiagram::compare() QAbstractItemView:"; 00151 // compare QAbstractItemView properties 00152 qDebug() << 00153 ((alternatingRowColors() == other->alternatingRowColors()) && 00154 (hasAutoScroll() == other->hasAutoScroll()) && 00155 #if QT_VERSION > 0x040199 00156 (dragDropMode() == other->dragDropMode()) && 00157 (dragDropOverwriteMode() == other->dragDropOverwriteMode()) && 00158 (horizontalScrollMode() == other->horizontalScrollMode ()) && 00159 (verticalScrollMode() == other->verticalScrollMode()) && 00160 #endif 00161 (dragEnabled() == other->dragEnabled()) && 00162 (editTriggers() == other->editTriggers()) && 00163 (iconSize() == other->iconSize()) && 00164 (selectionBehavior() == other->selectionBehavior()) && 00165 (selectionMode() == other->selectionMode()) && 00166 (showDropIndicator() == other->showDropIndicator()) && 00167 (tabKeyNavigation() == other->tabKeyNavigation()) && 00168 (textElideMode() == other->textElideMode())); 00169 qDebug() << "AbstractDiagram::compare() AttributesModel: "; 00170 // compare all of the properties stored in the attributes model 00171 qDebug() << attributesModel()->compare( other->attributesModel() ); 00172 qDebug() << "AbstractDiagram::compare() own:"; 00173 // compare own properties 00174 qDebug() << 00175 ((rootIndex().column() == other->rootIndex().column()) && 00176 (rootIndex().row() == other->rootIndex().row()) && 00177 (allowOverlappingDataValueTexts() == other->allowOverlappingDataValueTexts()) && 00178 (antiAliasing() == other->antiAliasing()) && 00179 (percentMode() == other->percentMode()) && 00180 (datasetDimension() == other->datasetDimension())); 00181 */ 00182 return // compare QAbstractScrollArea properties 00183 (horizontalScrollBarPolicy() == other->horizontalScrollBarPolicy()) && 00184 (verticalScrollBarPolicy() == other->verticalScrollBarPolicy()) && 00185 // compare QFrame properties 00186 (frameShadow() == other->frameShadow()) && 00187 (frameShape() == other->frameShape()) && 00188 // frameWidth is a read-only property defined by the style, it should not be in here: 00189 // (frameWidth() == other->frameWidth()) && 00190 (lineWidth() == other->lineWidth()) && 00191 (midLineWidth() == other->midLineWidth()) && 00192 // compare QAbstractItemView properties 00193 (alternatingRowColors() == other->alternatingRowColors()) && 00194 (hasAutoScroll() == other->hasAutoScroll()) && 00195 #if QT_VERSION > 0x040199 00196 (dragDropMode() == other->dragDropMode()) && 00197 (dragDropOverwriteMode() == other->dragDropOverwriteMode()) && 00198 (horizontalScrollMode() == other->horizontalScrollMode ()) && 00199 (verticalScrollMode() == other->verticalScrollMode()) && 00200 #endif 00201 (dragEnabled() == other->dragEnabled()) && 00202 (editTriggers() == other->editTriggers()) && 00203 (iconSize() == other->iconSize()) && 00204 (selectionBehavior() == other->selectionBehavior()) && 00205 (selectionMode() == other->selectionMode()) && 00206 (showDropIndicator() == other->showDropIndicator()) && 00207 (tabKeyNavigation() == other->tabKeyNavigation()) && 00208 (textElideMode() == other->textElideMode()) && 00209 // compare all of the properties stored in the attributes model 00210 attributesModel()->compare( other->attributesModel() ) && 00211 // compare own properties 00212 (rootIndex().column() == other->rootIndex().column()) && 00213 (rootIndex().row() == other->rootIndex().row()) && 00214 (allowOverlappingDataValueTexts() == other->allowOverlappingDataValueTexts()) && 00215 (antiAliasing() == other->antiAliasing()) && 00216 (percentMode() == other->percentMode()) && 00217 (datasetDimension() == other->datasetDimension()); 00218 } 00219 00220 AbstractCoordinatePlane* AbstractDiagram::coordinatePlane() const 00221 { 00222 return d->plane; 00223 } 00224 00225 const QPair<QPointF, QPointF> AbstractDiagram::dataBoundaries () const 00226 { 00227 if( d->databoundariesDirty ){ 00228 d->databoundaries = calculateDataBoundaries (); 00229 d->databoundariesDirty = false; 00230 } 00231 return d->databoundaries; 00232 } 00233 00234 void AbstractDiagram::setDataBoundariesDirty() const 00235 { 00236 d->databoundariesDirty = true; 00237 update(); 00238 } 00239 00240 void AbstractDiagram::setModel( QAbstractItemModel * newModel ) 00241 { 00242 if( model() ) 00243 { 00244 disconnect( model(), SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); 00245 disconnect( model(), SIGNAL( columnsInserted( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); 00246 disconnect( model(), SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); 00247 disconnect( model(), SIGNAL( columnsRemoved( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); 00248 disconnect( model(), SIGNAL( modelReset() ), this, SLOT( setDataBoundariesDirty() ) ); 00249 disconnect( model(), SIGNAL( layoutChanged() ), this, SLOT( setDataBoundariesDirty() ) ); 00250 disconnect( model(), SIGNAL( dataChanged(QModelIndex,QModelIndex) ), this, SIGNAL( modelDataChanged() )); 00251 } 00252 QAbstractItemView::setModel( newModel ); 00253 AttributesModel* amodel = new PrivateAttributesModel( newModel, this ); 00254 amodel->initFrom( d->attributesModel ); 00255 d->setAttributesModel(amodel); 00256 scheduleDelayedItemsLayout(); 00257 setDataBoundariesDirty(); 00258 if( model() ) 00259 { 00260 connect( model(), SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); 00261 connect( model(), SIGNAL( columnsInserted( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); 00262 connect( model(), SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); 00263 connect( model(), SIGNAL( columnsRemoved( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) ); 00264 connect( model(), SIGNAL( modelReset() ), this, SLOT( setDataBoundariesDirty() ) ); 00265 connect( model(), SIGNAL( layoutChanged() ), this, SLOT( setDataBoundariesDirty() ) ); 00266 connect( model(), SIGNAL( dataChanged(QModelIndex,QModelIndex) ), this, SIGNAL( modelDataChanged() )); 00267 } 00268 emit modelsChanged(); 00269 } 00270 00271 void AbstractDiagram::setSelectionModel( QItemSelectionModel* newSelectionModel ) 00272 { 00273 if( selectionModel() ) 00274 { 00275 disconnect( selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), this, SIGNAL( modelsChanged() ) ); 00276 disconnect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SIGNAL( modelsChanged() ) ); 00277 } 00278 QAbstractItemView::setSelectionModel( newSelectionModel ); 00279 if( selectionModel() ) 00280 { 00281 connect( selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), this, SIGNAL( modelsChanged() ) ); 00282 connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SIGNAL( modelsChanged() ) ); 00283 } 00284 emit modelsChanged(); 00285 } 00286 00292 void AbstractDiagram::setAttributesModel( AttributesModel* amodel ) 00293 { 00294 if( amodel->sourceModel() != model() ) { 00295 qWarning("KDChart::AbstractDiagram::setAttributesModel() failed: " 00296 "Trying to set an attributesmodel which works on a different " 00297 "model than the diagram."); 00298 return; 00299 } 00300 if( qobject_cast<PrivateAttributesModel*>(amodel) ) { 00301 qWarning("KDChart::AbstractDiagram::setAttributesModel() failed: " 00302 "Trying to set an attributesmodel that is private to another diagram."); 00303 return; 00304 } 00305 d->setAttributesModel(amodel); 00306 scheduleDelayedItemsLayout(); 00307 setDataBoundariesDirty(); 00308 emit modelsChanged(); 00309 } 00310 00311 bool AbstractDiagram::usesExternalAttributesModel()const 00312 { 00313 return d->usesExternalAttributesModel(); 00314 } 00315 00317 AttributesModel* AbstractDiagram::attributesModel() const 00318 { 00319 return d->attributesModel; 00320 } 00321 00322 QModelIndex AbstractDiagram::conditionallyMapFromSource( const QModelIndex & index ) const 00323 { 00324 Q_ASSERT( !index.isValid() || index.model() == attributesModel() || index.model() == attributesModel()->sourceModel() ); 00325 return index.model() == attributesModel() 00326 ? index 00327 : attributesModel()->mapFromSource( index ); 00328 } 00329 00331 void AbstractDiagram::setRootIndex ( const QModelIndex& idx ) 00332 { 00333 QAbstractItemView::setRootIndex(idx); 00334 setAttributesModelRootIndex( d->attributesModel->mapFromSource(idx) ); 00335 } 00336 00338 void AbstractDiagram::setAttributesModelRootIndex( const QModelIndex& idx ) 00339 { 00340 d->attributesModelRootIndex=idx; 00341 setDataBoundariesDirty(); 00342 scheduleDelayedItemsLayout(); 00343 } 00344 00347 QModelIndex AbstractDiagram::attributesModelRootIndex() const 00348 { 00349 if ( !d->attributesModelRootIndex.isValid() ) 00350 d->attributesModelRootIndex = d->attributesModel->mapFromSource( rootIndex() ); 00351 return d->attributesModelRootIndex; 00352 } 00353 00354 void AbstractDiagram::setCoordinatePlane( AbstractCoordinatePlane* parent ) 00355 { 00356 d->plane = parent; 00357 } 00358 00359 void AbstractDiagram::doItemsLayout() 00360 { 00361 if ( d->plane ) { 00362 d->plane->layoutDiagrams(); 00363 update(); 00364 } 00365 QAbstractItemView::doItemsLayout(); 00366 } 00367 00368 void AbstractDiagram::dataChanged( const QModelIndex &topLeft, 00369 const QModelIndex &bottomRight ) 00370 { 00371 Q_UNUSED( topLeft ); 00372 Q_UNUSED( bottomRight ); 00373 // We are still too dumb to do intelligent updates... 00374 setDataBoundariesDirty(); 00375 scheduleDelayedItemsLayout(); 00376 } 00377 00378 00379 void AbstractDiagram::setHidden( const QModelIndex & index, bool hidden ) 00380 { 00381 d->attributesModel->setData( 00382 conditionallyMapFromSource( index ), 00383 qVariantFromValue( hidden ), 00384 DataHiddenRole ); 00385 emit dataHidden(); 00386 } 00387 00388 void AbstractDiagram::setHidden( int dataset, bool hidden ) 00389 { 00390 // To store the flag for a dataset, we use the first column 00391 // that's associated with it. (i.e., with a dataset dimension 00392 // of two, the column of the keys) 00393 d->setDatasetAttrs( dataset, qVariantFromValue( hidden ), DataHiddenRole ); 00394 emit dataHidden(); 00395 } 00396 00397 void AbstractDiagram::setHidden( bool hidden ) 00398 { 00399 d->attributesModel->setModelData( 00400 qVariantFromValue( hidden ), 00401 DataHiddenRole ); 00402 emit dataHidden(); 00403 } 00404 00405 bool AbstractDiagram::isHidden() const 00406 { 00407 return qVariantValue<bool>( 00408 attributesModel()->modelData( DataHiddenRole ) ); 00409 } 00410 00411 bool AbstractDiagram::isHidden( int dataset ) const 00412 { 00413 const QVariant boolFlag( d->datasetAttrs( dataset, DataHiddenRole ) ); 00414 if( boolFlag.isValid() ) 00415 return qVariantValue< bool >( boolFlag ); 00416 return isHidden(); 00417 } 00418 00419 bool AbstractDiagram::isHidden( const QModelIndex & index ) const 00420 { 00421 return qVariantValue<bool>( 00422 attributesModel()->data( 00423 conditionallyMapFromSource(index), 00424 DataHiddenRole ) ); 00425 } 00426 00427 00428 void AbstractDiagram::setDataValueAttributes( const QModelIndex & index, 00429 const DataValueAttributes & a ) 00430 { 00431 d->attributesModel->setData( 00432 conditionallyMapFromSource( index ), 00433 qVariantFromValue( a ), 00434 DataValueLabelAttributesRole ); 00435 emit propertiesChanged(); 00436 } 00437 00438 00439 void AbstractDiagram::setDataValueAttributes( int dataset, const DataValueAttributes & a ) 00440 { 00441 d->setDatasetAttrs( dataset, qVariantFromValue( a ), DataValueLabelAttributesRole ); 00442 emit propertiesChanged(); 00443 } 00444 00445 DataValueAttributes AbstractDiagram::dataValueAttributes() const 00446 { 00447 return qVariantValue<DataValueAttributes>( 00448 attributesModel()->modelData( KDChart::DataValueLabelAttributesRole ) ); 00449 } 00450 00451 DataValueAttributes AbstractDiagram::dataValueAttributes( int dataset ) const 00452 { 00453 /* 00454 The following did not work! 00455 (khz, 2008-01-25) 00456 If there was some attrs specified for the 0-th cells of a dataset, 00457 then this logic would return the cell's settings instead of the header settings: 00458 00459 return qVariantValue<DataValueAttributes>( 00460 attributesModel()->data( attributesModel()->mapFromSource(columnToIndex( column )), 00461 KDChart::DataValueLabelAttributesRole ) ); 00462 */ 00463 00464 const QVariant headerAttrs( 00465 d->datasetAttrs( dataset, KDChart::DataValueLabelAttributesRole ) ); 00466 if( headerAttrs.isValid() ) 00467 return qVariantValue< DataValueAttributes >( headerAttrs ); 00468 return dataValueAttributes(); 00469 } 00470 00471 DataValueAttributes AbstractDiagram::dataValueAttributes( const QModelIndex & index ) const 00472 { 00473 return qVariantValue<DataValueAttributes>( 00474 attributesModel()->data( 00475 conditionallyMapFromSource( index ), 00476 KDChart::DataValueLabelAttributesRole ) ); 00477 } 00478 00479 void AbstractDiagram::setDataValueAttributes( const DataValueAttributes & a ) 00480 { 00481 d->attributesModel->setModelData( qVariantFromValue( a ), DataValueLabelAttributesRole ); 00482 emit propertiesChanged(); 00483 } 00484 00485 void AbstractDiagram::setAllowOverlappingDataValueTexts( bool allow ) 00486 { 00487 d->allowOverlappingDataValueTexts = allow; 00488 emit propertiesChanged(); 00489 } 00490 00491 bool AbstractDiagram::allowOverlappingDataValueTexts() const 00492 { 00493 return d->allowOverlappingDataValueTexts; 00494 } 00495 00496 void AbstractDiagram::setAntiAliasing( bool enabled ) 00497 { 00498 d->antiAliasing = enabled; 00499 emit propertiesChanged(); 00500 } 00501 00502 bool AbstractDiagram::antiAliasing() const 00503 { 00504 return d->antiAliasing; 00505 } 00506 00507 void AbstractDiagram::setPercentMode ( bool percent ) 00508 { 00509 d->percent = percent; 00510 emit propertiesChanged(); 00511 } 00512 00513 bool AbstractDiagram::percentMode() const 00514 { 00515 return d->percent; 00516 } 00517 00518 00519 void AbstractDiagram::paintDataValueText( QPainter* painter, 00520 const QModelIndex& index, 00521 const QPointF& pos, 00522 double value ) 00523 { 00524 d->paintDataValueText( this, painter, index, pos, value ); 00525 } 00526 00527 00528 QString AbstractDiagram::roundValues( double value, 00529 const int decimalPos, 00530 const int decimalDigits ) const 00531 { 00532 return d->roundValues( value, decimalPos, decimalDigits ); 00533 } 00534 00535 void AbstractDiagram::paintDataValueTexts( QPainter* painter ) 00536 { 00537 if ( !checkInvariants() ) return; 00538 const int rowCount = model()->rowCount(rootIndex()); 00539 const int columnCount = model()->columnCount(rootIndex()); 00540 d->clearListOfAlreadyDrawnDataValueTexts(); 00541 for ( int i=datasetDimension()-1; i<columnCount; i += datasetDimension() ) { 00542 for ( int j=0; j< rowCount; ++j ) { 00543 const QModelIndex index = model()->index( j, i, rootIndex() ); 00544 double value = model()->data( index ).toDouble(); 00545 const QPointF pos = coordinatePlane()->translate( QPointF( j, value ) ); 00546 paintDataValueText( painter, index, pos, value ); 00547 } 00548 } 00549 } 00550 00551 00552 void AbstractDiagram::paintMarker( QPainter* painter, 00553 const DataValueAttributes& a, 00554 const QModelIndex& index, 00555 const QPointF& pos ) 00556 { 00557 if ( !checkInvariants() || !a.isVisible() ) return; 00558 const MarkerAttributes ma = a.markerAttributes(); 00559 if ( !ma.isVisible() ) return; 00560 00561 const PainterSaver painterSaver( painter ); 00562 // the size of the marker - unscaled 00563 const QSizeF maSize( ma.markerSize().width() / painter->matrix().m11(), 00564 ma.markerSize().height() / painter->matrix().m22() ); 00565 QBrush indexBrush( brush( index ) ); 00566 QPen indexPen( ma.pen() ); 00567 if ( ma.markerColor().isValid() ) 00568 indexBrush.setColor( ma.markerColor() ); 00569 00570 paintMarker( painter, ma, indexBrush, indexPen, pos, maSize ); 00571 00572 // workaround: BC cannot be changed, otherwise we would pass the 00573 // index down to next-lower paintMarker function. So far, we 00574 // basically save a circle of radius maSize at pos in the 00575 // reverseMapper. This means that ^^^ this version of paintMarker 00576 // needs to be called to reverse-map the marker. 00577 d->reverseMapper.addCircle( index.row(), index.column(), pos, 2 * maSize ); 00578 } 00579 00580 void AbstractDiagram::paintMarker( QPainter* painter, 00581 const QModelIndex& index, 00582 const QPointF& pos ) 00583 { 00584 if ( !checkInvariants() ) return; 00585 paintMarker( painter, dataValueAttributes( index ), index, pos ); 00586 } 00587 00588 void AbstractDiagram::paintMarker( QPainter* painter, 00589 const MarkerAttributes& markerAttributes, 00590 const QBrush& brush, 00591 const QPen& pen, 00592 const QPointF& pos, 00593 const QSizeF& maSize ) 00594 { 00595 const QPen oldPen( painter->pen() ); 00596 // Pen is used to paint 4Pixels - 1 Pixel - Ring and FastCross types. 00597 // make sure to use the brush color - see above in those cases. 00598 const bool isFourPixels = (markerAttributes.markerStyle() == MarkerAttributes::Marker4Pixels); 00599 if( isFourPixels || (markerAttributes.markerStyle() == MarkerAttributes::Marker1Pixel) ){ 00600 // for high-performance point charts with tiny point markers: 00601 painter->setPen( PrintingParameters::scalePen( QPen( brush.color().light() ) ) ); 00602 if( isFourPixels ){ 00603 const qreal x = pos.x(); 00604 const qreal y = pos.y(); 00605 painter->drawLine( QPointF(x-1.0,y-1.0), 00606 QPointF(x+1.0,y-1.0) ); 00607 painter->drawLine( QPointF(x-1.0,y), 00608 QPointF(x+1.0,y) ); 00609 painter->drawLine( QPointF(x-1.0,y+1.0), 00610 QPointF(x+1.0,y+1.0) ); 00611 } 00612 painter->drawPoint( pos ); 00613 }else{ 00614 const PainterSaver painterSaver( painter ); 00615 QPen painterPen( pen ); 00616 painter->setPen( PrintingParameters::scalePen( painterPen ) ); 00617 painter->setBrush( brush ); 00618 painter->setRenderHint ( QPainter::Antialiasing ); 00619 painter->translate( pos ); 00620 switch ( markerAttributes.markerStyle() ) { 00621 case MarkerAttributes::MarkerCircle: 00622 painter->drawEllipse( QRectF( 0 - maSize.height()/2, 0 - maSize.width()/2, 00623 maSize.height(), maSize.width()) ); 00624 break; 00625 case MarkerAttributes::MarkerSquare: 00626 { 00627 QRectF rect( 0 - maSize.width()/2, 0 - maSize.height()/2, 00628 maSize.width(), maSize.height() ); 00629 painter->drawRect( rect ); 00630 break; 00631 } 00632 case MarkerAttributes::MarkerDiamond: 00633 { 00634 QVector <QPointF > diamondPoints; 00635 QPointF top, left, bottom, right; 00636 top = QPointF( 0, 0 - maSize.height()/2 ); 00637 left = QPointF( 0 - maSize.width()/2, 0 ); 00638 bottom = QPointF( 0, maSize.height()/2 ); 00639 right = QPointF( maSize.width()/2, 0 ); 00640 diamondPoints << top << left << bottom << right; 00641 painter->drawPolygon( diamondPoints ); 00642 break; 00643 } 00644 // both handled on top of the method: 00645 case MarkerAttributes::Marker1Pixel: 00646 case MarkerAttributes::Marker4Pixels: 00647 break; 00648 case MarkerAttributes::MarkerRing: 00649 { 00650 painter->setBrush( Qt::NoBrush ); 00651 painter->drawEllipse( QRectF( 0 - maSize.height()/2, 0 - maSize.width()/2, 00652 maSize.height(), maSize.width()) ); 00653 break; 00654 } 00655 case MarkerAttributes::MarkerCross: 00656 { 00657 // Note: Markers can have outline, 00658 // so just drawing two rects is NOT the solution here! 00659 const qreal w02 = maSize.width() * 0.2; 00660 const qreal w05 = maSize.width() * 0.5; 00661 const qreal h02 = maSize.height()* 0.2; 00662 const qreal h05 = maSize.height()* 0.5; 00663 QVector <QPointF > crossPoints; 00664 QPointF p[12]; 00665 p[ 0] = QPointF( -w02, -h05 ); 00666 p[ 1] = QPointF( w02, -h05 ); 00667 p[ 2] = QPointF( w02, -h02 ); 00668 p[ 3] = QPointF( w05, -h02 ); 00669 p[ 4] = QPointF( w05, h02 ); 00670 p[ 5] = QPointF( w02, h02 ); 00671 p[ 6] = QPointF( w02, h05 ); 00672 p[ 7] = QPointF( -w02, h05 ); 00673 p[ 8] = QPointF( -w02, h02 ); 00674 p[ 9] = QPointF( -w05, h02 ); 00675 p[10] = QPointF( -w05, -h02 ); 00676 p[11] = QPointF( -w02, -h02 ); 00677 for( int i=0; i<12; ++i ) 00678 crossPoints << p[i]; 00679 crossPoints << p[0]; 00680 painter->drawPolygon( crossPoints ); 00681 break; 00682 } 00683 case MarkerAttributes::MarkerFastCross: 00684 { 00685 QPointF left, right, top, bottom; 00686 left = QPointF( -maSize.width()/2, 0 ); 00687 right = QPointF( maSize.width()/2, 0 ); 00688 top = QPointF( 0, -maSize.height()/2 ); 00689 bottom= QPointF( 0, maSize.height()/2 ); 00690 painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) ); 00691 painter->drawLine( left, right ); 00692 painter->drawLine( top, bottom ); 00693 break; 00694 } 00695 case MarkerAttributes::NoMarker: 00696 break; 00697 default: 00698 Q_ASSERT_X ( false, "paintMarkers()", 00699 "Type item does not match a defined Marker Type." ); 00700 } 00701 } 00702 painter->setPen( oldPen ); 00703 } 00704 00705 void AbstractDiagram::paintMarkers( QPainter* painter ) 00706 { 00707 if ( !checkInvariants() ) return; 00708 const int rowCount = model()->rowCount(rootIndex()); 00709 const int columnCount = model()->columnCount(rootIndex()); 00710 for ( int i=datasetDimension()-1; i<columnCount; i += datasetDimension() ) { 00711 for ( int j=0; j< rowCount; ++j ) { 00712 const QModelIndex index = model()->index( j, i, rootIndex() ); 00713 double value = model()->data( index ).toDouble(); 00714 const QPointF pos = coordinatePlane()->translate( QPointF( j, value ) ); 00715 paintMarker( painter, index, pos ); 00716 } 00717 } 00718 } 00719 00720 00721 void AbstractDiagram::setPen( const QModelIndex& index, const QPen& pen ) 00722 { 00723 attributesModel()->setData( 00724 conditionallyMapFromSource( index ), 00725 qVariantFromValue( pen ), DatasetPenRole ); 00726 emit propertiesChanged(); 00727 } 00728 00729 void AbstractDiagram::setPen( const QPen& pen ) 00730 { 00731 attributesModel()->setModelData( 00732 qVariantFromValue( pen ), DatasetPenRole ); 00733 emit propertiesChanged(); 00734 } 00735 00736 void AbstractDiagram::setPen( int dataset, const QPen& pen ) 00737 { 00738 d->setDatasetAttrs( dataset, qVariantFromValue( pen ), DatasetPenRole ); 00739 emit propertiesChanged(); 00740 } 00741 00742 QPen AbstractDiagram::pen() const 00743 { 00744 return qVariantValue<QPen>( 00745 attributesModel()->data( DatasetPenRole ) ); 00746 } 00747 00748 QPen AbstractDiagram::pen( int dataset ) const 00749 { 00750 const QVariant penSettings( d->datasetAttrs( dataset, DatasetPenRole ) ); 00751 if( penSettings.isValid() ) 00752 return qVariantValue< QPen >( penSettings ); 00753 return pen(); 00754 } 00755 00756 QPen AbstractDiagram::pen( const QModelIndex& index ) const 00757 { 00758 return qVariantValue<QPen>( 00759 attributesModel()->data( 00760 conditionallyMapFromSource( index ), 00761 DatasetPenRole ) ); 00762 } 00763 00764 void AbstractDiagram::setBrush( const QModelIndex& index, const QBrush& brush ) 00765 { 00766 attributesModel()->setData( 00767 conditionallyMapFromSource( index ), 00768 qVariantFromValue( brush ), DatasetBrushRole ); 00769 emit propertiesChanged(); 00770 } 00771 00772 void AbstractDiagram::setBrush( const QBrush& brush ) 00773 { 00774 attributesModel()->setModelData( 00775 qVariantFromValue( brush ), DatasetBrushRole ); 00776 emit propertiesChanged(); 00777 } 00778 00779 void AbstractDiagram::setBrush( int dataset, const QBrush& brush ) 00780 { 00781 d->setDatasetAttrs( dataset, qVariantFromValue( brush ), DatasetBrushRole ); 00782 emit propertiesChanged(); 00783 } 00784 00785 QBrush AbstractDiagram::brush() const 00786 { 00787 return qVariantValue<QBrush>( 00788 attributesModel()->data( DatasetBrushRole ) ); 00789 } 00790 00791 QBrush AbstractDiagram::brush( int dataset ) const 00792 { 00793 const QVariant brushSettings( d->datasetAttrs( dataset, DatasetBrushRole ) ); 00794 if( brushSettings.isValid() ) 00795 return qVariantValue< QBrush >( brushSettings ); 00796 return brush(); 00797 } 00798 00799 QBrush AbstractDiagram::brush( const QModelIndex& index ) const 00800 { 00801 return qVariantValue<QBrush>( 00802 attributesModel()->data( conditionallyMapFromSource( index ), DatasetBrushRole ) ); 00803 } 00804 00811 void AbstractDiagram::setUnitPrefix( const QString& prefix, int column, Qt::Orientation orientation ) 00812 { 00813 d->unitPrefixMap[ column ][ orientation ]= prefix; 00814 } 00815 00821 void AbstractDiagram::setUnitPrefix( const QString& prefix, Qt::Orientation orientation ) 00822 { 00823 d->unitPrefix[ orientation ] = prefix; 00824 } 00825 00832 void AbstractDiagram::setUnitSuffix( const QString& suffix, int column, Qt::Orientation orientation ) 00833 { 00834 d->unitSuffixMap[ column ][ orientation ]= suffix; 00835 } 00836 00842 void AbstractDiagram::setUnitSuffix( const QString& suffix, Qt::Orientation orientation ) 00843 { 00844 d->unitSuffix[ orientation ] = suffix; 00845 } 00846 00854 QString AbstractDiagram::unitPrefix( int column, Qt::Orientation orientation, bool fallback ) const 00855 { 00856 if( !fallback || d->unitPrefixMap[ column ].contains( orientation ) ) 00857 return d->unitPrefixMap[ column ][ orientation ]; 00858 return d->unitPrefix[ orientation ]; 00859 } 00860 00865 QString AbstractDiagram::unitPrefix( Qt::Orientation orientation ) const 00866 { 00867 return d->unitPrefix[ orientation ]; 00868 } 00869 00877 QString AbstractDiagram::unitSuffix( int column, Qt::Orientation orientation, bool fallback ) const 00878 { 00879 if( !fallback || d->unitSuffixMap[ column ].contains( orientation ) ) 00880 return d->unitSuffixMap[ column ][ orientation ]; 00881 return d->unitSuffix[ orientation ]; 00882 } 00883 00888 QString AbstractDiagram::unitSuffix( Qt::Orientation orientation ) const 00889 { 00890 return d->unitSuffix[ orientation ]; 00891 } 00892 00893 // implement QAbstractItemView: 00894 QRect AbstractDiagram::visualRect( const QModelIndex &index ) const 00895 { 00896 return d->reverseMapper.boundingRect( index.row(), index.column() ).toRect(); 00897 } 00898 00899 void AbstractDiagram::scrollTo(const QModelIndex &, ScrollHint ) 00900 {} 00901 00902 // indexAt ... down below 00903 00904 QModelIndex AbstractDiagram::moveCursor(CursorAction, Qt::KeyboardModifiers ) 00905 { return QModelIndex(); } 00906 00907 int AbstractDiagram::horizontalOffset() const 00908 { return 0; } 00909 00910 int AbstractDiagram::verticalOffset() const 00911 { return 0; } 00912 00913 bool AbstractDiagram::isIndexHidden(const QModelIndex &) const 00914 { return true; } 00915 00916 void AbstractDiagram::setSelection(const QRect& rect , QItemSelectionModel::SelectionFlags command ) 00917 { 00918 const QModelIndexList indexes = d->indexesIn( rect ); 00919 QItemSelection selection; 00920 KDAB_FOREACH( const QModelIndex& index, indexes ) 00921 { 00922 selection.append( QItemSelectionRange( index ) ); 00923 } 00924 selectionModel()->select( selection, command ); 00925 } 00926 00927 QRegion AbstractDiagram::visualRegionForSelection(const QItemSelection &selection) const 00928 { 00929 QPolygonF polygon; 00930 KDAB_FOREACH( const QModelIndex& index, selection.indexes() ) 00931 { 00932 polygon << d->reverseMapper.polygon(index.row(), index.column()); 00933 } 00934 return polygon.isEmpty() ? QRegion() : QRegion( polygon.toPolygon() ); 00935 } 00936 00937 QRegion AbstractDiagram::visualRegion(const QModelIndex &index) const 00938 { 00939 QPolygonF polygon = d->reverseMapper.polygon(index.row(), index.column()); 00940 return polygon.isEmpty() ? QRegion() : QRegion( polygon.toPolygon() ); 00941 } 00942 00943 void KDChart::AbstractDiagram::useDefaultColors( ) 00944 { 00945 d->attributesModel->setPaletteType( AttributesModel::PaletteTypeDefault ); 00946 } 00947 00948 void KDChart::AbstractDiagram::useSubduedColors( ) 00949 { 00950 d->attributesModel->setPaletteType( AttributesModel::PaletteTypeSubdued ); 00951 } 00952 00953 void KDChart::AbstractDiagram::useRainbowColors( ) 00954 { 00955 d->attributesModel->setPaletteType( AttributesModel::PaletteTypeRainbow ); 00956 } 00957 00958 QStringList AbstractDiagram::itemRowLabels() const 00959 { 00960 QStringList ret; 00961 if( model() ){ 00962 //qDebug() << "AbstractDiagram::itemRowLabels(): " << attributesModel()->rowCount(attributesModelRootIndex()) << "entries"; 00963 const int rowCount = attributesModel()->rowCount(attributesModelRootIndex()); 00964 for( int i = 0; i < rowCount; ++i ){ 00965 //qDebug() << "item row label: " << attributesModel()->headerData( i, Qt::Vertical, Qt::DisplayRole ).toString(); 00966 ret << unitPrefix( i, Qt::Horizontal, true ) + 00967 attributesModel()->headerData( i, Qt::Vertical, Qt::DisplayRole ).toString() + 00968 unitSuffix( i, Qt::Horizontal, true ); 00969 } 00970 } 00971 return ret; 00972 } 00973 00974 QStringList AbstractDiagram::datasetLabels() const 00975 { 00976 QStringList ret; 00977 if( model() == 0 ) 00978 return ret; 00979 00980 const int columnCount = attributesModel()->columnCount(attributesModelRootIndex()); 00981 for( int i = 0; i < columnCount; i += datasetDimension() ) 00982 ret << attributesModel()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString(); 00983 00984 return ret; 00985 } 00986 00987 QList<QBrush> AbstractDiagram::datasetBrushes() const 00988 { 00989 QList<QBrush> ret; 00990 if( model() == 0 ) 00991 return ret; 00992 00993 const int datasetCount = attributesModel()->columnCount(attributesModelRootIndex()) / datasetDimension(); 00994 for ( int dataset = 0; dataset < datasetCount; dataset++ ) 00995 ret << brush( dataset ); 00996 00997 return ret; 00998 } 00999 01000 QList<QPen> AbstractDiagram::datasetPens() const 01001 { 01002 QList<QPen> ret; 01003 if( model() == 0 ) 01004 return ret; 01005 01006 const int datasetCount = attributesModel()->columnCount(attributesModelRootIndex()) / datasetDimension(); 01007 for ( int dataset = 0; dataset < datasetCount; dataset++ ) 01008 ret << pen( dataset ); 01009 01010 return ret; 01011 } 01012 01013 QList<MarkerAttributes> AbstractDiagram::datasetMarkers() const 01014 { 01015 QList<MarkerAttributes> ret; 01016 if( model() == 0 ) 01017 return ret; 01018 01019 const int datasetCount = attributesModel()->columnCount(attributesModelRootIndex()) / datasetDimension(); 01020 for ( int dataset = 0; dataset < datasetCount; dataset++ ) 01021 ret << dataValueAttributes( dataset ).markerAttributes(); 01022 01023 return ret; 01024 } 01025 01026 bool AbstractDiagram::checkInvariants( bool justReturnTheStatus ) const 01027 { 01028 if( ! justReturnTheStatus ){ 01029 Q_ASSERT_X ( model(), "AbstractDiagram::checkInvariants()", 01030 "There is no usable model set, for the diagram." ); 01031 01032 Q_ASSERT_X ( coordinatePlane(), "AbstractDiagram::checkInvariants()", 01033 "There is no usable coordinate plane set, for the diagram." ); 01034 } 01035 return model() && coordinatePlane(); 01036 } 01037 01038 int AbstractDiagram::datasetDimension( ) const 01039 { 01040 return d->datasetDimension; 01041 } 01042 01043 void AbstractDiagram::setDatasetDimension( int dimension ) 01044 { 01045 Q_UNUSED( dimension ); 01046 qDebug() << "Setting the dataset dimension using AbstractDiagram::setDatasetDimension is obsolete. Use the specific diagram types instead."; 01047 } 01048 01049 void AbstractDiagram::setDatasetDimensionInternal( int dimension ) 01050 { 01051 Q_ASSERT( dimension != 0 ); 01052 01053 if ( d->datasetDimension == dimension ) return; 01054 d->datasetDimension = dimension; 01055 setDataBoundariesDirty(); 01056 emit layoutChanged( this ); 01057 } 01058 01059 double AbstractDiagram::valueForCell( int row, int column ) const 01060 { 01061 return d->attributesModel->data( 01062 d->attributesModel->index( row, column, attributesModelRootIndex() ) ).toDouble(); 01063 } 01064 01065 void AbstractDiagram::update() const 01066 { 01067 //qDebug("KDChart::AbstractDiagram::update() called"); 01068 if( d->plane ) 01069 d->plane->update(); 01070 } 01071 01072 QModelIndex AbstractDiagram::indexAt( const QPoint& point ) const 01073 { 01074 return d->indexAt( point ); 01075 } 01076 01077 QModelIndexList AbstractDiagram::indexesAt( const QPoint& point ) const 01078 { 01079 return d->indexesAt( point ); 01080 } 01081