KDChartAbstractDiagram.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2010 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 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 }
00238 
00239 void AbstractDiagram::setModel( QAbstractItemModel * newModel )
00240 {
00241     if( model() )
00242     {
00243         disconnect( model(), SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) );
00244         disconnect( model(), SIGNAL( columnsInserted( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) );
00245         disconnect( model(), SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) );
00246         disconnect( model(), SIGNAL( columnsRemoved( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) );
00247         disconnect( model(), SIGNAL( modelReset() ), this, SLOT( setDataBoundariesDirty() ) );
00248         disconnect( model(), SIGNAL( layoutChanged() ), this, SLOT( setDataBoundariesDirty() ) );
00249         disconnect( model(), SIGNAL( dataChanged(QModelIndex,QModelIndex) ), this, SIGNAL( modelDataChanged() ));
00250     }
00251     QAbstractItemView::setModel( newModel );
00252     AttributesModel* amodel = new PrivateAttributesModel( newModel, this );
00253     amodel->initFrom( d->attributesModel );
00254     d->setAttributesModel(amodel);
00255     scheduleDelayedItemsLayout();
00256     setDataBoundariesDirty();
00257     if( model() )
00258     {
00259         connect( model(), SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) );
00260         connect( model(), SIGNAL( columnsInserted( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) );
00261         connect( model(), SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) );
00262         connect( model(), SIGNAL( columnsRemoved( QModelIndex, int, int ) ), this, SLOT( setDataBoundariesDirty() ) );
00263         connect( model(), SIGNAL( modelReset() ), this, SLOT( setDataBoundariesDirty() ) );
00264         connect( model(), SIGNAL( layoutChanged() ), this, SLOT( setDataBoundariesDirty() ) );
00265         connect( model(), SIGNAL( dataChanged(QModelIndex,QModelIndex) ), this, SIGNAL( modelDataChanged() ));
00266     }
00267     emit modelsChanged();
00268 }
00269         
00270 void AbstractDiagram::setSelectionModel( QItemSelectionModel* newSelectionModel )
00271 {
00272     if( selectionModel() )
00273     {
00274         disconnect( selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), this, SIGNAL( modelsChanged() ) );
00275         disconnect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SIGNAL( modelsChanged() ) );
00276     }
00277     QAbstractItemView::setSelectionModel( newSelectionModel );
00278     if( selectionModel() )
00279     {
00280         connect( selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), this, SIGNAL( modelsChanged() ) );
00281         connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SIGNAL( modelsChanged() ) );
00282     }
00283     emit modelsChanged();
00284 }
00285 
00291 void AbstractDiagram::setAttributesModel( AttributesModel* amodel )
00292 {
00293     if( amodel->sourceModel() != model() ) {
00294         qWarning("KDChart::AbstractDiagram::setAttributesModel() failed: "
00295                  "Trying to set an attributesmodel which works on a different "
00296                  "model than the diagram.");
00297         return;
00298     }
00299     if( qobject_cast<PrivateAttributesModel*>(amodel) ) {
00300         qWarning("KDChart::AbstractDiagram::setAttributesModel() failed: "
00301                  "Trying to set an attributesmodel that is private to another diagram.");
00302         return;
00303     }
00304     d->setAttributesModel(amodel);
00305     scheduleDelayedItemsLayout();
00306     setDataBoundariesDirty();
00307     emit modelsChanged();
00308 }
00309 
00310 bool AbstractDiagram::usesExternalAttributesModel()const
00311 {
00312     return d->usesExternalAttributesModel();
00313 }
00314 
00316 AttributesModel* AbstractDiagram::attributesModel() const
00317 {
00318     return d->attributesModel;
00319 }
00320 
00321 QModelIndex AbstractDiagram::conditionallyMapFromSource( const QModelIndex & index ) const
00322 {
00323     Q_ASSERT( !index.isValid() || index.model() == attributesModel() || index.model() == attributesModel()->sourceModel() );
00324     return index.model() == attributesModel()
00325             ? index
00326             : attributesModel()->mapFromSource( index );
00327 }
00328 
00330 void AbstractDiagram::setRootIndex ( const QModelIndex& idx )
00331 {
00332     QAbstractItemView::setRootIndex(idx);
00333     setAttributesModelRootIndex( d->attributesModel->mapFromSource(idx) );
00334 }
00335 
00337 void AbstractDiagram::setAttributesModelRootIndex( const QModelIndex& idx )
00338 {
00339     d->attributesModelRootIndex=idx;
00340     setDataBoundariesDirty();
00341     scheduleDelayedItemsLayout();
00342 }
00343 
00346 QModelIndex AbstractDiagram::attributesModelRootIndex() const
00347 {
00348     if ( !d->attributesModelRootIndex.isValid() )
00349         d->attributesModelRootIndex = d->attributesModel->mapFromSource( rootIndex() );
00350     return d->attributesModelRootIndex;
00351 }
00352 
00353 void AbstractDiagram::setCoordinatePlane( AbstractCoordinatePlane* parent )
00354 {
00355     d->plane = parent;
00356 }
00357 
00358 void AbstractDiagram::doItemsLayout()
00359 {
00360     if ( d->plane ) {
00361         d->plane->layoutDiagrams();
00362         update();
00363     }
00364     QAbstractItemView::doItemsLayout();
00365 }
00366 
00367 void AbstractDiagram::dataChanged( const QModelIndex &topLeft,
00368                                    const QModelIndex &bottomRight )
00369 {
00370     Q_UNUSED( topLeft );
00371     Q_UNUSED( bottomRight );
00372     // We are still too dumb to do intelligent updates...
00373     setDataBoundariesDirty();
00374     scheduleDelayedItemsLayout();
00375 }
00376 
00377 
00378 void AbstractDiagram::setHidden( const QModelIndex & index, bool hidden )
00379 {
00380     d->attributesModel->setData(
00381         conditionallyMapFromSource( index ),
00382         qVariantFromValue( hidden ),
00383         DataHiddenRole );
00384     emit dataHidden();
00385 }
00386 
00387 void AbstractDiagram::setHidden( int dataset, bool hidden )
00388 {
00389     // To store the flag for a dataset, we use the first column
00390     // that's associated with it. (i.e., with a dataset dimension
00391     // of two, the column of the keys)
00392     d->setDatasetAttrs( dataset, qVariantFromValue( hidden ), DataHiddenRole );
00393     emit dataHidden();
00394 }
00395 
00396 void AbstractDiagram::setHidden( bool hidden )
00397 {
00398     d->attributesModel->setModelData(
00399         qVariantFromValue( hidden ),
00400         DataHiddenRole );
00401     emit dataHidden();
00402 }
00403 
00404 bool AbstractDiagram::isHidden() const
00405 {
00406     return qVariantValue<bool>(
00407         attributesModel()->modelData( DataHiddenRole ) );
00408 }
00409 
00410 bool AbstractDiagram::isHidden( int dataset ) const
00411 {
00412     const QVariant boolFlag( d->datasetAttrs( dataset, DataHiddenRole ) );
00413     if( boolFlag.isValid() )
00414         return qVariantValue< bool >( boolFlag );
00415     return isHidden();
00416 }
00417 
00418 bool AbstractDiagram::isHidden( const QModelIndex & index ) const
00419 {
00420     return qVariantValue<bool>(
00421         attributesModel()->data(
00422             conditionallyMapFromSource(index),
00423             DataHiddenRole ) );
00424 }
00425 
00426 
00427 void AbstractDiagram::setDataValueAttributes( const QModelIndex & index,
00428                                               const DataValueAttributes & a )
00429 {
00430     d->attributesModel->setData(
00431         conditionallyMapFromSource( index ),
00432         qVariantFromValue( a ),
00433         DataValueLabelAttributesRole );
00434     emit propertiesChanged();
00435 }
00436 
00437 
00438 void AbstractDiagram::setDataValueAttributes( int dataset, const DataValueAttributes & a )
00439 {
00440     d->setDatasetAttrs( dataset, qVariantFromValue( a ), DataValueLabelAttributesRole );
00441     emit propertiesChanged();
00442 }
00443 
00444 DataValueAttributes AbstractDiagram::dataValueAttributes() const
00445 {
00446     return qVariantValue<DataValueAttributes>(
00447         attributesModel()->modelData( KDChart::DataValueLabelAttributesRole ) );
00448 }
00449 
00450 DataValueAttributes AbstractDiagram::dataValueAttributes( int dataset ) const
00451 {
00452     /*
00453     The following did not work!
00454     (khz, 2008-01-25)
00455     If there was some attrs specified for the 0-th cells of a dataset,
00456     then this logic would return the cell's settings instead of the header settings:
00457 
00458     return qVariantValue<DataValueAttributes>(
00459         attributesModel()->data( attributesModel()->mapFromSource(columnToIndex( column )),
00460         KDChart::DataValueLabelAttributesRole ) );
00461     */
00462 
00463     const QVariant headerAttrs(
00464         d->datasetAttrs( dataset, KDChart::DataValueLabelAttributesRole ) );
00465     if( headerAttrs.isValid() )
00466         return qVariantValue< DataValueAttributes >( headerAttrs );
00467     return dataValueAttributes();
00468 }
00469 
00470 DataValueAttributes AbstractDiagram::dataValueAttributes( const QModelIndex & index ) const
00471 {
00472     return qVariantValue<DataValueAttributes>(
00473         attributesModel()->data(
00474             conditionallyMapFromSource( index ),
00475             KDChart::DataValueLabelAttributesRole ) );
00476 }
00477 
00478 void AbstractDiagram::setDataValueAttributes( const DataValueAttributes & a )
00479 {
00480     d->attributesModel->setModelData( qVariantFromValue( a ), DataValueLabelAttributesRole );
00481     emit propertiesChanged();
00482 }
00483 
00484 void AbstractDiagram::setAllowOverlappingDataValueTexts( bool allow )
00485 {
00486     d->allowOverlappingDataValueTexts = allow;
00487     emit propertiesChanged();
00488 }
00489 
00490 bool AbstractDiagram::allowOverlappingDataValueTexts() const
00491 {
00492     return d->allowOverlappingDataValueTexts;
00493 }
00494 
00495 void AbstractDiagram::setAntiAliasing( bool enabled )
00496 {
00497     d->antiAliasing = enabled;
00498     emit propertiesChanged();
00499 }
00500 
00501 bool AbstractDiagram::antiAliasing() const
00502 {
00503     return d->antiAliasing;
00504 }
00505 
00506 void AbstractDiagram::setPercentMode ( bool percent )
00507 {
00508     d->percent = percent;
00509     emit propertiesChanged();
00510 }
00511 
00512 bool AbstractDiagram::percentMode() const
00513 {
00514     return d->percent;
00515 }
00516 
00517 
00518 void AbstractDiagram::paintDataValueText( QPainter* painter,
00519                                           const QModelIndex& index,
00520                                           const QPointF& pos,
00521                                           double value )
00522 {
00523     d->paintDataValueText( this, painter, index, pos, value );
00524 }
00525 
00526 
00527 QString AbstractDiagram::roundValues( double value,
00528                                       const int decimalPos,
00529                                       const int decimalDigits ) const
00530 {
00531     return d->roundValues( value, decimalPos, decimalDigits );
00532 }
00533 
00534 void AbstractDiagram::paintDataValueTexts( QPainter* painter )
00535 {
00536     if ( !checkInvariants() ) return;
00537     const int rowCount = model()->rowCount(rootIndex());
00538     const int columnCount = model()->columnCount(rootIndex());
00539     d->clearListOfAlreadyDrawnDataValueTexts();
00540     for ( int i=datasetDimension()-1; i<columnCount; i += datasetDimension() ) {
00541        for ( int j=0; j< rowCount; ++j ) {
00542            const QModelIndex index = model()->index( j, i, rootIndex() );
00543            double value = model()->data( index ).toDouble();
00544            const QPointF pos = coordinatePlane()->translate( QPointF( j, value ) );
00545            paintDataValueText( painter, index, pos, value );
00546        }
00547     }
00548 }
00549 
00550 
00551 void AbstractDiagram::paintMarker( QPainter* painter,
00552                                    const DataValueAttributes& a,
00553                                    const QModelIndex& index,
00554                                    const QPointF& pos )
00555 {
00556     if ( !checkInvariants() || !a.isVisible() ) return;
00557     const MarkerAttributes ma = a.markerAttributes();
00558     if ( !ma.isVisible() ) return;
00559 
00560     const PainterSaver painterSaver( painter );
00561     // the size of the marker - unscaled
00562     const QSizeF maSize( ma.markerSize().width() / painter->matrix().m11(), 
00563                          ma.markerSize().height() / painter->matrix().m22() );
00564     QBrush indexBrush( brush( index ) );
00565     QPen indexPen( ma.pen() );
00566     if ( ma.markerColor().isValid() )
00567         indexBrush.setColor( ma.markerColor() );
00568 
00569     paintMarker( painter, ma, indexBrush, indexPen, pos, maSize );
00570 
00571     // workaround: BC cannot be changed, otherwise we would pass the
00572     // index down to next-lower paintMarker function. So far, we
00573     // basically save a circle of radius maSize at pos in the
00574     // reverseMapper. This means that ^^^ this version of paintMarker
00575     // needs to be called to reverse-map the marker.
00576     d->reverseMapper.addCircle( index.row(), index.column(), pos, 2 * maSize );
00577 }
00578 
00579 void AbstractDiagram::paintMarker( QPainter* painter,
00580                                    const QModelIndex& index,
00581                                    const QPointF& pos )
00582 {
00583     if ( !checkInvariants() ) return;
00584     paintMarker( painter, dataValueAttributes( index ), index, pos );
00585 }
00586 
00587 void AbstractDiagram::paintMarker( QPainter* painter,
00588                                    const MarkerAttributes& markerAttributes,
00589                                    const QBrush& brush,
00590                                    const QPen& pen,
00591                                    const QPointF& pos,
00592                                    const QSizeF& maSize )
00593 {
00594     const QPen oldPen( painter->pen() );
00595     // Pen is used to paint 4Pixels - 1 Pixel - Ring and FastCross types.
00596     // make sure to use the brush color - see above in those cases.
00597     const bool isFourPixels = (markerAttributes.markerStyle() == MarkerAttributes::Marker4Pixels);
00598     if( isFourPixels || (markerAttributes.markerStyle() == MarkerAttributes::Marker1Pixel) ){
00599         // for high-performance point charts with tiny point markers:
00600         painter->setPen( PrintingParameters::scalePen( QPen( brush.color().light() ) ) );
00601         if( isFourPixels ){
00602             const qreal x = pos.x();
00603             const qreal y = pos.y();
00604             painter->drawLine( QPointF(x-1.0,y-1.0),
00605                                QPointF(x+1.0,y-1.0) );
00606             painter->drawLine( QPointF(x-1.0,y),
00607                                QPointF(x+1.0,y) );
00608             painter->drawLine( QPointF(x-1.0,y+1.0),
00609                                QPointF(x+1.0,y+1.0) );
00610         }
00611         painter->drawPoint( pos );
00612     }else{
00613         const PainterSaver painterSaver( painter );
00614         // we only a solid line surrounding the markers
00615         QPen painterPen( pen );
00616         painterPen.setStyle( Qt::SolidLine );
00617         painter->setPen( PrintingParameters::scalePen( painterPen ) );
00618         painter->setBrush( brush );
00619         painter->setRenderHint ( QPainter::Antialiasing );
00620         painter->translate( pos );
00621         switch ( markerAttributes.markerStyle() ) {
00622             case MarkerAttributes::MarkerCircle:
00623                 painter->drawEllipse( QRectF( 0 - maSize.height()/2, 0 - maSize.width()/2,
00624                             maSize.height(), maSize.width()) );
00625                 break;
00626             case MarkerAttributes::MarkerSquare:
00627                 {
00628                     QRectF rect( 0 - maSize.width()/2, 0 - maSize.height()/2,
00629                                 maSize.width(), maSize.height() );
00630                     painter->drawRect( rect );
00631                     break;
00632                 }
00633             case MarkerAttributes::MarkerDiamond:
00634                 {
00635                     QVector <QPointF > diamondPoints;
00636                     QPointF top, left, bottom, right;
00637                     top    = QPointF( 0, 0 - maSize.height()/2 );
00638                     left   = QPointF( 0 - maSize.width()/2, 0 );
00639                     bottom = QPointF( 0, maSize.height()/2 );
00640                     right  = QPointF( maSize.width()/2, 0 );
00641                     diamondPoints << top << left << bottom << right;
00642                     painter->drawPolygon( diamondPoints );
00643                     break;
00644                 }
00645             // both handled on top of the method:
00646             case MarkerAttributes::Marker1Pixel:
00647             case MarkerAttributes::Marker4Pixels:
00648                     break;
00649             case MarkerAttributes::MarkerRing:
00650                 {
00651                     painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) );
00652                     painter->setBrush( Qt::NoBrush );
00653                     painter->drawEllipse( QRectF( 0 - maSize.height()/2, 0 - maSize.width()/2,
00654                                         maSize.height(), maSize.width()) );
00655                     break;
00656                 }
00657             case MarkerAttributes::MarkerCross:
00658                 {
00659                     // Note: Markers can have outline,
00660                     //       so just drawing two rects is NOT the solution here!
00661                     const qreal w02 = maSize.width() * 0.2;
00662                     const qreal w05 = maSize.width() * 0.5;
00663                     const qreal h02 = maSize.height()* 0.2;
00664                     const qreal h05 = maSize.height()* 0.5;
00665                     QVector <QPointF > crossPoints;
00666                     QPointF p[12];
00667                     p[ 0] = QPointF( -w02, -h05 );
00668                     p[ 1] = QPointF(  w02, -h05 );
00669                     p[ 2] = QPointF(  w02, -h02 );
00670                     p[ 3] = QPointF(  w05, -h02 );
00671                     p[ 4] = QPointF(  w05,  h02 );
00672                     p[ 5] = QPointF(  w02,  h02 );
00673                     p[ 6] = QPointF(  w02,  h05 );
00674                     p[ 7] = QPointF( -w02,  h05 );
00675                     p[ 8] = QPointF( -w02,  h02 );
00676                     p[ 9] = QPointF( -w05,  h02 );
00677                     p[10] = QPointF( -w05, -h02 );
00678                     p[11] = QPointF( -w02, -h02 );
00679                     for( int i=0; i<12; ++i )
00680                         crossPoints << p[i];
00681                     crossPoints << p[0];
00682                     painter->drawPolygon( crossPoints );
00683                     break;
00684                 }
00685             case MarkerAttributes::MarkerFastCross:
00686                 {
00687                     QPointF left, right, top, bottom;
00688                     left  = QPointF( -maSize.width()/2, 0 );
00689                     right = QPointF( maSize.width()/2, 0 );
00690                     top   = QPointF( 0, -maSize.height()/2 );
00691                     bottom= QPointF( 0, maSize.height()/2 );
00692                     painter->setPen( PrintingParameters::scalePen( QPen( brush.color() ) ) );
00693                     painter->drawLine( left, right );
00694                     painter->drawLine(  top, bottom );
00695                     break;
00696                 }
00697             case MarkerAttributes::NoMarker:
00698                 break;
00699             default:
00700                 Q_ASSERT_X ( false, "paintMarkers()",
00701                             "Type item does not match a defined Marker Type." );
00702         }
00703     }
00704     painter->setPen( oldPen );
00705 }
00706 
00707 void AbstractDiagram::paintMarkers( QPainter* painter )
00708 {
00709     if ( !checkInvariants() ) return;
00710     const int rowCount = model()->rowCount(rootIndex());
00711     const int columnCount = model()->columnCount(rootIndex());
00712     for ( int i=datasetDimension()-1; i<columnCount; i += datasetDimension() ) {
00713        for ( int j=0; j< rowCount; ++j ) {
00714            const QModelIndex index = model()->index( j, i, rootIndex() );
00715            double value = model()->data( index ).toDouble();
00716            const QPointF pos = coordinatePlane()->translate( QPointF( j, value ) );
00717            paintMarker( painter, index, pos );
00718        }
00719     }
00720 }
00721 
00722 
00723 void AbstractDiagram::setPen( const QModelIndex& index, const QPen& pen )
00724 {
00725     attributesModel()->setData(
00726         conditionallyMapFromSource( index ),
00727         qVariantFromValue( pen ), DatasetPenRole );
00728     emit propertiesChanged();
00729 }
00730 
00731 void AbstractDiagram::setPen( const QPen& pen )
00732 {
00733     attributesModel()->setModelData(
00734         qVariantFromValue( pen ), DatasetPenRole );
00735     emit propertiesChanged();
00736 }
00737 
00738 void AbstractDiagram::setPen( int dataset, const QPen& pen )
00739 {
00740     d->setDatasetAttrs( dataset, qVariantFromValue( pen ), DatasetPenRole );
00741     emit propertiesChanged();
00742 }
00743 
00744 QPen AbstractDiagram::pen() const
00745 {
00746     return qVariantValue<QPen>(
00747         attributesModel()->data( DatasetPenRole ) );
00748 }
00749 
00750 QPen AbstractDiagram::pen( int dataset ) const
00751 {
00752     const QVariant penSettings( d->datasetAttrs( dataset, DatasetPenRole ) );
00753     if( penSettings.isValid() )
00754         return qVariantValue< QPen >( penSettings );
00755     return pen();
00756 }
00757 
00758 QPen AbstractDiagram::pen( const QModelIndex& index ) const
00759 {
00760     return qVariantValue<QPen>(
00761         attributesModel()->data(
00762             conditionallyMapFromSource( index ),
00763             DatasetPenRole ) );
00764 }
00765 
00766 void AbstractDiagram::setBrush( const QModelIndex& index, const QBrush& brush )
00767 {
00768     attributesModel()->setData(
00769         conditionallyMapFromSource( index ),
00770         qVariantFromValue( brush ), DatasetBrushRole );
00771     emit propertiesChanged();
00772 }
00773 
00774 void AbstractDiagram::setBrush( const QBrush& brush )
00775 {
00776     attributesModel()->setModelData(
00777         qVariantFromValue( brush ), DatasetBrushRole );
00778     emit propertiesChanged();
00779 }
00780 
00781 void AbstractDiagram::setBrush( int dataset, const QBrush& brush )
00782 {
00783     d->setDatasetAttrs( dataset, qVariantFromValue( brush ), DatasetBrushRole );
00784     emit propertiesChanged();
00785 }
00786 
00787 QBrush AbstractDiagram::brush() const
00788 {
00789     return qVariantValue<QBrush>(
00790         attributesModel()->data( DatasetBrushRole ) );
00791 }
00792 
00793 QBrush AbstractDiagram::brush( int dataset ) const
00794 {
00795     const QVariant brushSettings( d->datasetAttrs( dataset, DatasetBrushRole ) );
00796     if( brushSettings.isValid() )
00797         return qVariantValue< QBrush >( brushSettings );
00798     return brush();
00799 }
00800 
00801 QBrush AbstractDiagram::brush( const QModelIndex& index ) const
00802 {
00803     return qVariantValue<QBrush>(
00804         attributesModel()->data( conditionallyMapFromSource( index ), DatasetBrushRole ) );
00805 }
00806 
00813 void AbstractDiagram::setUnitPrefix( const QString& prefix, int column, Qt::Orientation orientation )
00814 {
00815     d->unitPrefixMap[ column ][ orientation ]= prefix;
00816 }
00817 
00823 void AbstractDiagram::setUnitPrefix( const QString& prefix, Qt::Orientation orientation )
00824 {
00825     d->unitPrefix[ orientation ] = prefix;
00826 }
00827 
00834 void AbstractDiagram::setUnitSuffix( const QString& suffix, int column, Qt::Orientation orientation )
00835 {
00836     d->unitSuffixMap[ column ][ orientation ]= suffix;
00837 }
00838 
00844 void AbstractDiagram::setUnitSuffix( const QString& suffix, Qt::Orientation orientation )
00845 {
00846     d->unitSuffix[ orientation ] = suffix;
00847 }
00848 
00856 QString AbstractDiagram::unitPrefix( int column, Qt::Orientation orientation, bool fallback ) const
00857 {
00858     if( !fallback || d->unitPrefixMap[ column ].contains( orientation ) )
00859         return d->unitPrefixMap[ column ][ orientation ];
00860     return d->unitPrefix[ orientation ];
00861 }
00862 
00867 QString AbstractDiagram::unitPrefix( Qt::Orientation orientation ) const
00868 {
00869     return d->unitPrefix[ orientation ];
00870 }
00871 
00879 QString AbstractDiagram::unitSuffix( int column, Qt::Orientation orientation, bool fallback ) const
00880 {
00881     if( !fallback || d->unitSuffixMap[ column ].contains( orientation ) )
00882         return d->unitSuffixMap[ column ][ orientation ];
00883     return d->unitSuffix[ orientation ];
00884 }
00885 
00890 QString AbstractDiagram::unitSuffix( Qt::Orientation orientation ) const
00891 {
00892     return d->unitSuffix[ orientation ];
00893 }
00894 
00895 // implement QAbstractItemView:
00896 QRect AbstractDiagram::visualRect( const QModelIndex &index ) const
00897 {
00898     return d->reverseMapper.boundingRect( index.row(), index.column() ).toRect();
00899 }
00900 
00901 void AbstractDiagram::scrollTo(const QModelIndex &, ScrollHint )
00902 {}
00903 
00904 // indexAt ... down below
00905 
00906 QModelIndex AbstractDiagram::moveCursor(CursorAction, Qt::KeyboardModifiers )
00907 { return QModelIndex(); }
00908 
00909 int AbstractDiagram::horizontalOffset() const
00910 { return 0; }
00911 
00912 int AbstractDiagram::verticalOffset() const
00913 { return 0; }
00914 
00915 bool AbstractDiagram::isIndexHidden(const QModelIndex &) const
00916 { return true; }
00917 
00918 void AbstractDiagram::setSelection(const QRect& rect , QItemSelectionModel::SelectionFlags command )
00919 {
00920     const QModelIndexList indexes = d->indexesIn( rect );
00921     QItemSelection selection;
00922     KDAB_FOREACH( const QModelIndex& index, indexes )
00923     {
00924         selection.append( QItemSelectionRange( index ) );
00925     }
00926     selectionModel()->select( selection, command );
00927 }
00928 
00929 QRegion AbstractDiagram::visualRegionForSelection(const QItemSelection &selection) const
00930 {
00931     QPolygonF polygon;
00932     KDAB_FOREACH( const QModelIndex& index, selection.indexes() )
00933     {
00934         polygon << d->reverseMapper.polygon(index.row(), index.column());
00935     }
00936     return polygon.isEmpty() ? QRegion() : QRegion( polygon.toPolygon() );
00937 }
00938 
00939 QRegion AbstractDiagram::visualRegion(const QModelIndex &index) const
00940 {
00941     QPolygonF polygon = d->reverseMapper.polygon(index.row(), index.column());
00942     return polygon.isEmpty() ? QRegion() : QRegion( polygon.toPolygon() );
00943 }
00944 
00945 void KDChart::AbstractDiagram::useDefaultColors( )
00946 {
00947     d->attributesModel->setPaletteType( AttributesModel::PaletteTypeDefault );
00948 }
00949 
00950 void KDChart::AbstractDiagram::useSubduedColors( )
00951 {
00952     d->attributesModel->setPaletteType( AttributesModel::PaletteTypeSubdued );
00953 }
00954 
00955 void KDChart::AbstractDiagram::useRainbowColors( )
00956 {
00957     d->attributesModel->setPaletteType( AttributesModel::PaletteTypeRainbow );
00958 }
00959 
00960 QStringList AbstractDiagram::itemRowLabels() const
00961 {
00962     QStringList ret;
00963     if( model() ){
00964         //qDebug() << "AbstractDiagram::itemRowLabels(): " << attributesModel()->rowCount(attributesModelRootIndex()) << "entries";
00965         const int rowCount = attributesModel()->rowCount(attributesModelRootIndex());
00966         for( int i = 0; i < rowCount; ++i ){
00967             //qDebug() << "item row label: " << attributesModel()->headerData( i, Qt::Vertical, Qt::DisplayRole ).toString();
00968             ret << unitPrefix( i, Qt::Horizontal, true ) +
00969                    attributesModel()->headerData( i, Qt::Vertical, Qt::DisplayRole ).toString() +
00970                    unitSuffix( i, Qt::Horizontal, true );
00971         }
00972     }
00973     return ret;
00974 }
00975 
00976 QStringList AbstractDiagram::datasetLabels() const
00977 {
00978     QStringList ret;
00979     if( model() == 0 )
00980         return ret;
00981     
00982     const int columnCount = attributesModel()->columnCount(attributesModelRootIndex());
00983     for( int i = 0; i < columnCount; i += datasetDimension() )
00984         ret << attributesModel()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString();
00985     
00986     return ret;
00987 }
00988 
00989 QList<QBrush> AbstractDiagram::datasetBrushes() const
00990 {
00991     QList<QBrush> ret;
00992     if( model() == 0 )
00993         return ret;
00994 
00995     const int datasetCount = attributesModel()->columnCount(attributesModelRootIndex()) / datasetDimension();
00996     for ( int dataset = 0; dataset < datasetCount; dataset++ )
00997         ret << brush( dataset );
00998 
00999     return ret;
01000 }
01001 
01002 QList<QPen> AbstractDiagram::datasetPens() const
01003 {
01004     QList<QPen> ret;
01005     if( model() == 0 )
01006         return ret;
01007     
01008     const int datasetCount = attributesModel()->columnCount(attributesModelRootIndex()) / datasetDimension();
01009     for ( int dataset = 0; dataset < datasetCount; dataset++ )
01010         ret << pen( dataset );
01011     
01012     return ret;
01013 }
01014 
01015 QList<MarkerAttributes> AbstractDiagram::datasetMarkers() const
01016 {
01017     QList<MarkerAttributes> ret;
01018     if( model() == 0 )
01019         return ret;
01020     
01021     const int datasetCount = attributesModel()->columnCount(attributesModelRootIndex()) / datasetDimension();
01022     for ( int dataset = 0; dataset < datasetCount; dataset++ )
01023         ret << dataValueAttributes( dataset ).markerAttributes();
01024 
01025     return ret;
01026 }
01027 
01028 bool AbstractDiagram::checkInvariants( bool justReturnTheStatus ) const
01029 {
01030     if( ! justReturnTheStatus ){
01031         Q_ASSERT_X ( model(), "AbstractDiagram::checkInvariants()",
01032                     "There is no usable model set, for the diagram." );
01033 
01034         Q_ASSERT_X ( coordinatePlane(), "AbstractDiagram::checkInvariants()",
01035                     "There is no usable coordinate plane set, for the diagram." );
01036     }
01037     return model() && coordinatePlane();
01038 }
01039 
01040 int AbstractDiagram::datasetDimension( ) const
01041 {
01042     return d->datasetDimension;
01043 }
01044 
01045 void AbstractDiagram::setDatasetDimension( int dimension )
01046 {
01047     Q_UNUSED( dimension );
01048     qDebug() << "Setting the dataset dimension using AbstractDiagram::setDatasetDimension is obsolete. Use the specific diagram types instead.";
01049 }
01050 
01051 void AbstractDiagram::setDatasetDimensionInternal( int dimension )
01052 {
01053     Q_ASSERT( dimension != 0 );
01054     
01055     if ( d->datasetDimension == dimension ) return;
01056     d->datasetDimension = dimension;
01057     setDataBoundariesDirty();
01058     emit layoutChanged( this );
01059 }
01060 
01061 double AbstractDiagram::valueForCell( int row, int column ) const
01062 {
01063     return d->attributesModel->data(
01064             d->attributesModel->index( row, column, attributesModelRootIndex() ) ).toDouble();
01065 }
01066 
01067 void AbstractDiagram::update() const
01068 {
01069     //qDebug("KDChart::AbstractDiagram::update() called");
01070     if( d->plane )
01071         d->plane->update();
01072 }
01073 
01074 QModelIndex AbstractDiagram::indexAt( const QPoint& point ) const
01075 {
01076     return d->indexAt( point );
01077 }
01078 
01079 QModelIndexList AbstractDiagram::indexesAt( const QPoint& point ) const
01080 {
01081     return d->indexesAt( point );
01082 }
01083