KDChartAbstractDiagram.cpp

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

Generated on Thu Mar 4 23:19:09 2010 for KD Chart 2 by  doxygen 1.5.4