# KDChartWidget.cpp

Go to the documentation of this file.

    00001 /* -*- Mode: C++ -*-
    00002    KDChart - a multi-platform charting engine
    00003    */
    00004
    00005 /****************************************************************************
    00006  ** Copyright (C) 2001-2007 Klarälvdalens Datakonsult AB.  All rights reserved.
    00007  **
    00008  ** This file is part of the KD Chart library.
    00009  **
    00010  ** This file may be distributed and/or modified under the terms of the
    00011  ** GNU General Public License version 2 as published by the Free Software
    00012  ** Foundation and appearing in the file LICENSE.GPL included in the
    00013  ** packaging of this file.
    00014  **
    00015  ** Licensees holding valid commercial KD Chart licenses may use this file in
    00016  ** accordance with the KD Chart Commercial License Agreement provided with
    00017  ** the Software.
    00018  **
    00019  ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
    00020  ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
    00021  **
    00022  ** See http://www.kdab.net/kdchart for
    00023  **   information about KD Chart Commercial License Agreements.
    00024  **
    00025  ** Contact info@kdab.net if any conditions of this
    00026  ** licensing are not clear to you.
    00027  **
    00028  **********************************************************************/
    00029
    00030 #include <KDChartWidget.h>
    00031 #include <KDChartWidget_p.h>
    00032
    00033 #include <KDChartAbstractDiagram.h>
    00034 #include <KDChartBarDiagram.h>
    00035 #include <KDChartCartesianCoordinatePlane.h>
    00036 #include <KDChartChart.h>
    00037 #include <KDChartAbstractCoordinatePlane.h>
    00038 #include <KDChartLineDiagram.h>
    00039 #include <KDChartPieDiagram.h>
    00040 #include <KDChartPolarCoordinatePlane.h>
    00041 #include <KDChartPolarDiagram.h>
    00042 #include <KDChartRingDiagram.h>
    00043 #include <KDChartLegend.h>
    00044
    00045 #include <QDebug>
    00046
    00047 #include <KDABLibFakes>
    00048
    00049 #define d d_func()
    00050
    00051 using namespace KDChart;
    00052
    00053 Widget::Private::Private( Widget * qq )
    00054     : q( qq ),
    00055       layout( q ),
    00056       m_model( q ),
    00057       m_chart( q ),
    00058       usedDatasetWidth( 0 )
    00059 {
    00060     KDAB_SET_OBJECT_NAME( layout );
    00061     KDAB_SET_OBJECT_NAME( m_model );
    00062     KDAB_SET_OBJECT_NAME( m_chart );
    00063
    00064     layout.addWidget( &m_chart );
    00065 }
    00066
    00067 Widget::Private::~Private() {}
    00068
    00069
    00082 Widget::Widget( QWidget* parent ) :
    00083     QWidget(parent), _d( new Private( this ) )
    00084 {
    00085     // as default we have a cartesian coordinate plane ...
    00086     // ... and a line diagram
    00087     setType( Line );
    00088 }
    00089
    00093 Widget::~Widget()
    00094 {
    00095     delete _d; _d = 0;
    00096 }
    00097
    00098 void Widget::init()
    00099 {
    00100 }
    00101
    00102 void Widget::setDataset( int column, const QVector< double > & data, const QString& title )
    00103 {
    00104     if ( ! checkDatasetWidth( 1 ) )
    00105         return;
    00106
    00107     QStandardItemModel & model = d->m_model;
    00108
    00109     justifyModelSize( data.size(), column + 1 );
    00110
    00111     for( int i = 0; i < data.size(); ++i )
    00112     {
    00113         const QModelIndex index = model.index( i, column );
    00114         model.setData( index, QVariant( data[i] ), Qt::DisplayRole );
    00115     }
    00116     if ( ! title.isEmpty() )
    00117         model.setHeaderData( column, Qt::Horizontal, QVariant( title ) );
    00118 }
    00119
    00120 void Widget::setDataset( int column, const QVector< QPair< double, double > > & data, const QString& title )
    00121 {
    00122     if ( ! checkDatasetWidth( 2 ))
    00123         return;
    00124
    00125     QStandardItemModel & model = d->m_model;
    00126
    00127     justifyModelSize( data.size(), (column + 1) * 2 );
    00128
    00129     for( int i = 0; i < data.size(); ++i )
    00130     {
    00131         QModelIndex index = model.index( i, column * 2 );
    00132         model.setData( index, QVariant( data[i].first ), Qt::DisplayRole );
    00133
    00134         index = model.index( i, column * 2 + 1 );
    00135         model.setData( index, QVariant( data[i].second ), Qt::DisplayRole );
    00136     }
    00137     if ( ! title.isEmpty() ){
    00138         model.setHeaderData( column * 2,   Qt::Horizontal, QVariant( title ) );
    00139         model.setHeaderData( column * 2+1, Qt::Horizontal, QVariant( title ) );
    00140     }
    00141 }
    00142
    00143 void Widget::setDataCell( int row, int column, double data )
    00144 {
    00145     if ( ! checkDatasetWidth( 1 ) )
    00146         return;
    00147
    00148     QStandardItemModel & model = d->m_model;
    00149
    00150     justifyModelSize( row + 1, column + 1 );
    00151
    00152     const QModelIndex index = model.index( row, column );
    00153     model.setData( index, QVariant( data ), Qt::DisplayRole );
    00154 }
    00155
    00156 void Widget::setDataCell( int row, int column, QPair< double, double > data )
    00157 {
    00158     if ( ! checkDatasetWidth( 2 ))
    00159         return;
    00160
    00161     QStandardItemModel & model = d->m_model;
    00162
    00163     justifyModelSize( row + 1, (column + 1) * 2 );
    00164
    00165     QModelIndex index = model.index( row, column * 2 );
    00166     model.setData( index, QVariant( data.first ), Qt::DisplayRole );
    00167
    00168     index = model.index( row, column * 2 + 1 );
    00169     model.setData( index, QVariant( data.second ), Qt::DisplayRole );
    00170 }
    00171
    00172 /*
    00173  * Resets all data.
    00174  */
    00175 void Widget::resetData()
    00176 {
    00177     d->m_model.clear();
    00178     d->usedDatasetWidth = 0;
    00179 }
    00180
    00184 void Widget::setGlobalLeading( int left, int top, int right, int bottom )
    00185 {
    00186     d->m_chart.setGlobalLeading( left, top, right, bottom );
    00187 }
    00188
    00192 void Widget::setGlobalLeadingLeft( int leading )
    00193 {
    00194     d->m_chart.setGlobalLeadingLeft( leading );
    00195 }
    00196
    00200 int Widget::globalLeadingLeft() const
    00201 {
    00202     return d->m_chart.globalLeadingLeft();
    00203 }
    00204
    00208 void Widget::setGlobalLeadingTop( int leading )
    00209 {
    00210     d->m_chart.setGlobalLeadingTop( leading );
    00211 }
    00212
    00216 int Widget::globalLeadingTop() const
    00217 {
    00218     return d->m_chart.globalLeadingTop();
    00219 }
    00220
    00224 void Widget::setGlobalLeadingRight( int leading )
    00225 {
    00226     d->m_chart.setGlobalLeadingRight( leading );
    00227 }
    00228
    00232 int Widget::globalLeadingRight() const
    00233 {
    00234     return d->m_chart.globalLeadingRight();
    00235 }
    00236
    00240 void Widget::setGlobalLeadingBottom( int leading )
    00241 {
    00242     d->m_chart.setGlobalLeadingBottom( leading );
    00243 }
    00244
    00248 int Widget::globalLeadingBottom() const
    00249 {
    00250     return d->m_chart.globalLeadingBottom();
    00251 }
    00252
    00256 KDChart::HeaderFooter* Widget::firstHeaderFooter()
    00257 {
    00258     return d->m_chart.headerFooter();
    00259 }
    00260
    00264 QList<KDChart::HeaderFooter*> Widget::allHeadersFooters()
    00265 {
    00266     return d->m_chart.headerFooters();
    00267 }
    00268
    00272 void Widget::addHeaderFooter( const QString& text,
    00273                               HeaderFooter::HeaderFooterType type,
    00274                               Position position)
    00275 {
    00276     HeaderFooter* newHeader = new HeaderFooter( &d->m_chart );
    00277     newHeader->setType( type );
    00278     newHeader->setPosition( position );
    00279     newHeader->setText( text );
    00280     d->m_chart.addHeaderFooter( newHeader ); // we need this explicit call !
    00281 }
    00282
    00286 void Widget::addHeaderFooter( HeaderFooter* header )
    00287 {
    00288     header->setParent( &d->m_chart );
    00289     d->m_chart.addHeaderFooter( header ); // we need this explicit call !
    00290 }
    00291
    00292 void Widget::replaceHeaderFooter( HeaderFooter* header, HeaderFooter* oldHeader )
    00293 {
    00294     header->setParent( &d->m_chart );
    00295     d->m_chart.replaceHeaderFooter( header, oldHeader );
    00296 }
    00297
    00298 void Widget::takeHeaderFooter( HeaderFooter* header )
    00299 {
    00300     d->m_chart.takeHeaderFooter( header );
    00301 }
    00302
    00306 KDChart::Legend* Widget::legend()
    00307 {
    00308     return d->m_chart.legend();
    00309 }
    00310
    00314 QList<KDChart::Legend*> Widget::allLegends()
    00315 {
    00316     return d->m_chart.legends();
    00317 }
    00318
    00322 void Widget::addLegend( Position position )
    00323 {
    00324     Legend* legend = new Legend( diagram(), &d->m_chart );
    00325     legend->setPosition( position );
    00326     d->m_chart.addLegend( legend );
    00327 }
    00328
    00332 void Widget::addLegend( Legend* legend )
    00333 {
    00334     legend->setDiagram( diagram() );
    00335     legend->setParent( &d->m_chart );
    00336     d->m_chart.addLegend( legend );
    00337 }
    00338
    00339 void Widget::replaceLegend( Legend* legend, Legend* oldLegend )
    00340 {
    00341     legend->setDiagram( diagram() );
    00342     legend->setParent( &d->m_chart );
    00343     d->m_chart.replaceLegend( legend, oldLegend );
    00344 }
    00345
    00346 void Widget::takeLegend( Legend* legend )
    00347 {
    00348     d->m_chart.takeLegend( legend );
    00349 }
    00350
    00351 AbstractDiagram* Widget::diagram()
    00352 {
    00353     if ( coordinatePlane() == 0 )
    00354         qDebug() << "diagram(): coordinatePlane() was NULL";
    00355
    00356     return coordinatePlane()->diagram();
    00357 }
    00358
    00359 BarDiagram* Widget::barDiagram()
    00360 {
    00361     return dynamic_cast<BarDiagram*>( diagram() );
    00362 }
    00363 LineDiagram* Widget::lineDiagram()
    00364 {
    00365     return dynamic_cast<LineDiagram*>( diagram() );
    00366 }
    00367 PieDiagram* Widget::pieDiagram()
    00368 {
    00369     return dynamic_cast<PieDiagram*>( diagram() );
    00370 }
    00371 RingDiagram* Widget::ringDiagram()
    00372 {
    00373     return dynamic_cast<RingDiagram*>( diagram() );
    00374 }
    00375 PolarDiagram* Widget::polarDiagram()
    00376 {
    00377     return dynamic_cast<PolarDiagram*>( diagram() );
    00378 }
    00379
    00380 AbstractCoordinatePlane* Widget::coordinatePlane()
    00381 {
    00382     return d->m_chart.coordinatePlane();
    00383 }
    00384
    00385 static bool isCartesian( KDChart::Widget::ChartType type )
    00386 {
    00387     return (type == KDChart::Widget::Bar || type == KDChart::Widget::Line);
    00388 }
    00389
    00390 static bool isPolar( KDChart::Widget::ChartType type )
    00391 {
    00392     return (type == KDChart::Widget::Pie
    00393             || type == KDChart::Widget::Ring
    00394             || type == KDChart::Widget::Polar );
    00395 }
    00396
    00397 void Widget::setType( ChartType chartType, SubType chartSubType )
    00398 {
    00399     AbstractDiagram* diag = 0;
    00400     CartesianCoordinatePlane* cartPlane = 0;
    00401     PolarCoordinatePlane* polPlane = 0;
    00402
    00403
    00404     if ( chartType != type() ){
    00405         switch ( chartType )
    00406         {
    00407             case Bar:
    00408               diag = new BarDiagram( &d->m_chart, cartPlane );
    00409               break;
    00410             case Line:
    00411               diag = new LineDiagram( &d->m_chart, cartPlane );
    00412               break;
    00413             case Pie:
    00414               diag = new PieDiagram( &d->m_chart, polPlane );
    00415               break;
    00416             case Polar:
    00417               diag = new PolarDiagram( &d->m_chart, polPlane );
    00418               break;
    00419             case Ring:
    00420               diag = new RingDiagram( &d->m_chart, polPlane );
    00421               break;
    00422             case NoType:
    00423               break;
    00424         }
    00425         if ( diag != NULL )
    00426         {
    00427             if ( isPolar( type() ) && isCartesian( chartType ) )
    00428             {
    00429                 cartPlane = new CartesianCoordinatePlane( &d->m_chart );
    00430                 d->m_chart.replaceCoordinatePlane( cartPlane );
    00431             }
    00432             else if ( isCartesian( type() ) && isPolar( chartType ) )
    00433             {
    00434                 polPlane = new PolarCoordinatePlane( &d->m_chart );
    00435                 d->m_chart.replaceCoordinatePlane( polPlane );
    00436             }
    00437             else if ( isCartesian( type() ) && isCartesian( chartType ) )
    00438             {
    00439                 AbstractCartesianDiagram *old =
    00440                         qobject_cast<AbstractCartesianDiagram*>( d->m_chart.coordinatePlane()->diagram() );
    00441                 Q_FOREACH( CartesianAxis* axis, old->axes() ) {
    00442                     old->takeAxis( axis );
    00443                     qobject_cast<AbstractCartesianDiagram*>(diag)->addAxis( axis );
    00444                 }
    00445             }
    00446             diag->setModel( &d->m_model );
    00447             coordinatePlane()->replaceDiagram( diag );
    00448
    00449             LegendList legends = d->m_chart.legends();
    00450             Q_FOREACH(Legend* l, legends)
    00451                 l->setDiagram( diag );
    00452             if( d->usedDatasetWidth )
    00453                 diag->setDatasetDimension( d->usedDatasetWidth );
    00454         }
    00455     }
    00456
    00457     if ( chartSubType != subType() )
    00458         setSubType( chartSubType );
    00459 //    coordinatePlane()->show();
    00460 }
    00461
    00462 void Widget::setSubType( SubType subType )
    00463 {
    00464     BarDiagram*  barDia  = qobject_cast< BarDiagram* >(   diagram() );
    00465     LineDiagram* lineDia = qobject_cast< LineDiagram* >(  diagram() );
    00466
    00467 //FIXME(khz): Add the impl for these chart types - or remove them from here:
    00468 //    PieDiagram*   pieDia   = qobject_cast< PieDiagram* >(   diagram() );
    00469 //    PolarDiagram* polarDia = qobject_cast< PolarDiagram* >( diagram() );
    00470 //    RingDiagram*  ringDia  = qobject_cast< RingDiagram* >(  diagram() );
    00471
    00472 #define SET_SUB_TYPE(DIAGRAM, SUBTYPE) \
    00473 { \
    00474     if( DIAGRAM ) \
    00475         DIAGRAM->setType( SUBTYPE ); \
    00476 }
    00477     switch ( subType )
    00478     {
    00479         case Normal:
    00480            SET_SUB_TYPE( barDia,  BarDiagram::Normal );
    00481            SET_SUB_TYPE( lineDia, LineDiagram::Normal );
    00482            break;
    00483         case Stacked:
    00484            SET_SUB_TYPE( barDia,  BarDiagram::Stacked );
    00485            SET_SUB_TYPE( lineDia, LineDiagram::Stacked );
    00486            break;
    00487         case Percent:
    00488            SET_SUB_TYPE( barDia,  BarDiagram::Percent );
    00489            SET_SUB_TYPE( lineDia, LineDiagram::Percent );
    00490            break;
    00491         case Rows:
    00492            SET_SUB_TYPE( barDia, BarDiagram::Rows );
    00493            break;
    00494         default:
    00495            Q_ASSERT_X ( false,
    00496                         "Widget::setSubType", "Sub-type not supported!" );
    00497            break;
    00498     }
    00499 //    coordinatePlane()->show();
    00500 }
    00501
    00505 Widget::ChartType Widget::type() const
    00506 {
    00507     // PENDING(christoph) save the type out-of-band:
    00508     AbstractDiagram * const dia = const_cast<Widget*>( this )->diagram();
    00509     if ( qobject_cast< BarDiagram* >( dia ) )
    00510         return Bar;
    00511     else if ( qobject_cast< LineDiagram* >( dia ) )
    00512         return Line;
    00513     else if( qobject_cast< PieDiagram* >( dia ) )
    00514         return Pie;
    00515     else if( qobject_cast< PolarDiagram* >( dia ) )
    00516         return Polar;
    00517     else if( qobject_cast< RingDiagram* >( dia ) )
    00518         return Ring;
    00519     else
    00520         return NoType;
    00521 }
    00522
    00523 Widget::SubType Widget::subType() const
    00524 {
    00525     // PENDING(christoph) save the type out-of-band:
    00526     Widget::SubType retVal = Normal;
    00527
    00528     AbstractDiagram * const dia = const_cast<Widget*>( this )->diagram();
    00529     BarDiagram*  barDia  = qobject_cast< BarDiagram* >(   dia );
    00530     LineDiagram* lineDia = qobject_cast< LineDiagram* >(  dia );
    00531
    00532 //FIXME(khz): Add the impl for these chart types - or remove them from here:
    00533 //    PieDiagram*   pieDia   = qobject_cast< PieDiagram* >(   diagram() );
    00534 //    PolarDiagram* polarDia = qobject_cast< PolarDiagram* >( diagram() );
    00535 //    RingDiagram*  ringDia  = qobject_cast< RingDiagram* >(  diagram() );
    00536
    00537 #define TEST_SUB_TYPE(DIAGRAM, INTERNALSUBTYPE, SUBTYPE) \
    00538 { \
    00539     if( DIAGRAM && DIAGRAM->type() == INTERNALSUBTYPE ) \
    00540         retVal = SUBTYPE; \
    00541 }
    00542     const Widget::ChartType mainType = type();
    00543     switch ( mainType )
    00544     {
    00545         case Bar:
    00546            TEST_SUB_TYPE( barDia, BarDiagram::Normal,  Normal );
    00547            TEST_SUB_TYPE( barDia, BarDiagram::Stacked, Stacked );
    00548            TEST_SUB_TYPE( barDia, BarDiagram::Percent, Percent );
    00549            TEST_SUB_TYPE( barDia, BarDiagram::Rows,    Rows );
    00550            break;
    00551         case Line:
    00552            TEST_SUB_TYPE( lineDia, LineDiagram::Normal,  Normal );
    00553            TEST_SUB_TYPE( lineDia, LineDiagram::Stacked, Stacked );
    00554            TEST_SUB_TYPE( lineDia, LineDiagram::Percent, Percent );
    00555            break;
    00556         case Pie:
    00557            // no impl. yet
    00558            break;
    00559         case Polar:
    00560            // no impl. yet
    00561            break;
    00562         case Ring:
    00563            // no impl. yet
    00564            break;
    00565         default:
    00566            Q_ASSERT_X ( false,
    00567                         "Widget::subType", "Chart type not supported!" );
    00568            break;
    00569     }
    00570     return retVal;
    00571 }
    00572
    00573
    00577 bool Widget::checkDatasetWidth( int width )
    00578 {
    00579     if ( d->usedDatasetWidth == width || d->usedDatasetWidth == 0 ) {
    00580         d->usedDatasetWidth = width;
    00581         diagram()->setDatasetDimension( width );
    00582         //qDebug() << "KDChart::Widget::checkDatasetWidth():  DatasetWidth is set to" << width << "now.";
    00583         return true;
    00584     }
    00585     qDebug() << "It's impossible to mix up the different setDataset() methods on the same widget.";
    00586     return false;
    00587 }
    00588
    00592 void Widget::justifyModelSize( int rows, int columns )
    00593 {
    00594     QAbstractItemModel & model = d->m_model;
    00595     const int currentRows = model.rowCount();
    00596     const int currentCols = model.columnCount();
    00597
    00598     if ( currentCols < columns )
    00599         if ( ! model.insertColumns( currentCols, columns - currentCols ))
    00600             qDebug() << "justifyModelSize: could not increase model size.";
    00601     if ( currentRows < rows )
    00602         if ( ! model.insertRows( currentRows, rows - currentRows ))
    00603             qDebug() << "justifyModelSize: could not increase model size.";
    00604
    00605     Q_ASSERT( model.rowCount() >= rows );
    00606     Q_ASSERT( model.columnCount() >= columns );
    00607 }