File indexing completed on 2024-05-19 15:27:29

0001 /*
0002  * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB.  All rights reserved.
0003  *
0004  * This file is part of the KD Chart library.
0005  *
0006  * This program is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU General Public License as
0008  * published by the Free Software Foundation; either version 2 of
0009  * the License, or (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License
0017  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
0018  */
0019 
0020 #include <KChartWidget.h>
0021 #include <KChartWidget_p.h>
0022 
0023 #include <KChartAbstractDiagram.h>
0024 #include <KChartBarDiagram.h>
0025 #include <KChartChart.h>
0026 #include <KChartAbstractCoordinatePlane.h>
0027 #include <KChartLineDiagram.h>
0028 #include <KChartPlotter.h>
0029 #include <KChartPieDiagram.h>
0030 #include <KChartPolarDiagram.h>
0031 #include <KChartRingDiagram.h>
0032 #include <KChartLegend.h>
0033 #include "KChartMath_p.h"
0034 
0035 #include <QDebug>
0036 
0037 #define d d_func()
0038 
0039 using namespace KChart;
0040 
0041 Widget::Private::Private( Widget * qq )
0042     : q( qq ),
0043       layout( q ),
0044       m_model( q ),
0045       m_chart( q ),
0046       m_cartPlane( &m_chart ),
0047       m_polPlane( &m_chart ),
0048       usedDatasetWidth( 0 )
0049 {
0050     KDAB_SET_OBJECT_NAME( layout );
0051     KDAB_SET_OBJECT_NAME( m_model );
0052     KDAB_SET_OBJECT_NAME( m_chart );
0053 
0054     layout.addWidget( &m_chart );
0055 }
0056 
0057 Widget::Private::~Private() {}
0058 
0059 
0060 
0061 Widget::Widget( QWidget* parent ) :
0062     QWidget(parent), _d( new Private( this ) )
0063 {
0064     // as default we have a cartesian coordinate plane ...
0065     // ... and a line diagram
0066     setType( Line );
0067 }
0068 
0069 Widget::~Widget()
0070 {
0071     delete _d; _d = nullptr;
0072 }
0073 
0074 void Widget::init()
0075 {
0076 }
0077 
0078 void Widget::setDataset( int column, const QVector< qreal > & data, const QString& title )
0079 {
0080     if ( ! checkDatasetWidth( 1 ) )
0081         return;
0082 
0083     QStandardItemModel & model = d->m_model;
0084 
0085     justifyModelSize( data.size(), column + 1 );
0086 
0087     for ( int i = 0; i < data.size(); ++i )
0088     {
0089         const QModelIndex index = model.index( i, column );
0090         model.setData( index, QVariant( data[i] ), Qt::DisplayRole );
0091     }
0092     if ( ! title.isEmpty() )
0093         model.setHeaderData( column, Qt::Horizontal, QVariant( title ) );
0094 }
0095 
0096 void Widget::setDataset( int column, const QVector< QPair< qreal, qreal > > & data, const QString& title )
0097 {
0098     if ( ! checkDatasetWidth( 2 ))
0099         return;
0100 
0101     QStandardItemModel & model = d->m_model;
0102 
0103     justifyModelSize( data.size(), (column + 1) * 2 );
0104 
0105     for ( int i = 0; i < data.size(); ++i )
0106     {
0107         QModelIndex index = model.index( i, column * 2 );
0108         model.setData( index, QVariant( data[i].first ), Qt::DisplayRole );
0109 
0110         index = model.index( i, column * 2 + 1 );
0111         model.setData( index, QVariant( data[i].second ), Qt::DisplayRole );
0112     }
0113     if ( ! title.isEmpty() ) {
0114         model.setHeaderData( column,   Qt::Horizontal, QVariant( title ) );
0115     }
0116 }
0117 
0118 void Widget::setDataCell( int row, int column, qreal data )
0119 {
0120     if ( ! checkDatasetWidth( 1 ) )
0121         return;
0122 
0123     QStandardItemModel & model = d->m_model;
0124 
0125     justifyModelSize( row + 1, column + 1 );
0126 
0127     const QModelIndex index = model.index( row, column );
0128     model.setData( index, QVariant( data ), Qt::DisplayRole );
0129 }
0130 
0131 void Widget::setDataCell( int row, int column, QPair< qreal, qreal > data )
0132 {
0133     if ( ! checkDatasetWidth( 2 ))
0134         return;
0135 
0136     QStandardItemModel & model = d->m_model;
0137 
0138     justifyModelSize( row + 1, (column + 1) * 2 );
0139 
0140     QModelIndex index = model.index( row, column * 2 );
0141     model.setData( index, QVariant( data.first ), Qt::DisplayRole );
0142 
0143     index = model.index( row, column * 2 + 1 );
0144     model.setData( index, QVariant( data.second ), Qt::DisplayRole );
0145 }
0146 
0147 /*
0148  * Resets all data.
0149  */
0150 void Widget::resetData()
0151 {
0152     d->m_model.clear();
0153     d->usedDatasetWidth = 0;
0154 }
0155 
0156 void Widget::setGlobalLeading( int left, int top, int right, int bottom )
0157 {
0158     d->m_chart.setGlobalLeading( left, top, right, bottom );
0159 }
0160 
0161 void Widget::setGlobalLeadingLeft( int leading )
0162 {
0163     d->m_chart.setGlobalLeadingLeft( leading );
0164 }
0165 
0166 int Widget::globalLeadingLeft() const
0167 {
0168     return d->m_chart.globalLeadingLeft();
0169 }
0170 
0171 void Widget::setGlobalLeadingTop( int leading )
0172 {
0173     d->m_chart.setGlobalLeadingTop( leading );
0174 }
0175 
0176 int Widget::globalLeadingTop() const
0177 {
0178     return d->m_chart.globalLeadingTop();
0179 }
0180 
0181 void Widget::setGlobalLeadingRight( int leading )
0182 {
0183     d->m_chart.setGlobalLeadingRight( leading );
0184 }
0185 
0186 int Widget::globalLeadingRight() const
0187 {
0188     return d->m_chart.globalLeadingRight();
0189 }
0190 
0191 void Widget::setGlobalLeadingBottom( int leading )
0192 {
0193     d->m_chart.setGlobalLeadingBottom( leading );
0194 }
0195 
0196 int Widget::globalLeadingBottom() const
0197 {
0198     return d->m_chart.globalLeadingBottom();
0199 }
0200 
0201 KChart::HeaderFooter* Widget::firstHeaderFooter()
0202 {
0203     return d->m_chart.headerFooter();
0204 }
0205 
0206 QList<KChart::HeaderFooter*> Widget::allHeadersFooters()
0207 {
0208     return d->m_chart.headerFooters();
0209 }
0210 
0211 void Widget::addHeaderFooter( const QString& text,
0212                               HeaderFooter::HeaderFooterType type,
0213                               Position position)
0214 {
0215     HeaderFooter* newHeader = new HeaderFooter( &d->m_chart );
0216     newHeader->setType( type );
0217     newHeader->setPosition( position );
0218     newHeader->setText( text );
0219     d->m_chart.addHeaderFooter( newHeader ); // we need this explicit call !
0220 }
0221 
0222 void Widget::addHeaderFooter( HeaderFooter* header )
0223 {
0224     header->setParent( &d->m_chart );
0225     d->m_chart.addHeaderFooter( header ); // we need this explicit call !
0226 }
0227 
0228 void Widget::replaceHeaderFooter( HeaderFooter* header, HeaderFooter* oldHeader )
0229 {
0230     header->setParent( &d->m_chart );
0231     d->m_chart.replaceHeaderFooter( header, oldHeader );
0232 }
0233 
0234 void Widget::takeHeaderFooter( HeaderFooter* header )
0235 {
0236     d->m_chart.takeHeaderFooter( header );
0237 }
0238 
0239 KChart::Legend* Widget::legend()
0240 {
0241     return d->m_chart.legend();
0242 }
0243 
0244 QList<KChart::Legend*> Widget::allLegends()
0245 {
0246     return d->m_chart.legends();
0247 }
0248 
0249 void Widget::addLegend( Position position )
0250 {
0251     Legend* legend = new Legend( diagram(), &d->m_chart );
0252     legend->setPosition( position );
0253     d->m_chart.addLegend( legend );
0254 }
0255 
0256 void Widget::addLegend( Legend* legend )
0257 {
0258     legend->setDiagram( diagram() );
0259     legend->setParent( &d->m_chart );
0260     d->m_chart.addLegend( legend );
0261 }
0262 
0263 void Widget::replaceLegend( Legend* legend, Legend* oldLegend )
0264 {
0265     legend->setDiagram( diagram() );
0266     legend->setParent( &d->m_chart );
0267     d->m_chart.replaceLegend( legend, oldLegend );
0268 }
0269 
0270 void Widget::takeLegend( Legend* legend )
0271 {
0272     d->m_chart.takeLegend( legend );
0273 }
0274 
0275 AbstractDiagram* Widget::diagram()
0276 {
0277     if ( coordinatePlane() == nullptr )
0278         qDebug() << "diagram(): coordinatePlane() was NULL";
0279 
0280     return coordinatePlane()->diagram();
0281 }
0282 
0283 BarDiagram* Widget::barDiagram()
0284 {
0285     return dynamic_cast<BarDiagram*>( diagram() );
0286 }
0287 LineDiagram* Widget::lineDiagram()
0288 {
0289     return dynamic_cast<LineDiagram*>( diagram() );
0290 }
0291 Plotter* Widget::plotter()
0292 {
0293     return dynamic_cast<Plotter*>( diagram() );
0294 }
0295 PieDiagram* Widget::pieDiagram()
0296 {
0297     return dynamic_cast<PieDiagram*>( diagram() );
0298 }
0299 RingDiagram* Widget::ringDiagram()
0300 {
0301     return dynamic_cast<RingDiagram*>( diagram() );
0302 }
0303 PolarDiagram* Widget::polarDiagram()
0304 {
0305     return dynamic_cast<PolarDiagram*>( diagram() );
0306 }
0307 
0308 AbstractCoordinatePlane* Widget::coordinatePlane()
0309 {
0310     return d->m_chart.coordinatePlane();
0311 }
0312 
0313 static bool isCartesian( KChart::Widget::ChartType type )
0314 {
0315     return (type == KChart::Widget::Bar) || (type == KChart::Widget::Line);
0316 }
0317 
0318 static bool isPolar( KChart::Widget::ChartType type )
0319 {
0320     return     (type == KChart::Widget::Pie)
0321             || (type == KChart::Widget::Ring)
0322             || (type == KChart::Widget::Polar);
0323 }
0324 
0325 void Widget::setType( ChartType chartType, SubType chartSubType )
0326 {
0327     AbstractDiagram* diag = nullptr;
0328     const ChartType oldType = type();
0329 
0330     if ( chartType != oldType ) {
0331         if ( chartType != NoType ) {
0332             if ( isCartesian( chartType ) && ! isCartesian( oldType ) )
0333             {
0334                 if ( coordinatePlane() == &d->m_polPlane ) {
0335                     d->m_chart.takeCoordinatePlane( &d->m_polPlane );
0336                     d->m_chart.addCoordinatePlane( &d->m_cartPlane );
0337                 } else {
0338                     d->m_chart.replaceCoordinatePlane( &d->m_cartPlane );
0339                 }
0340             }
0341             else if ( isPolar( chartType ) && ! isPolar( oldType ) )
0342             {
0343                 if ( coordinatePlane() == &d->m_cartPlane ) {
0344                     d->m_chart.takeCoordinatePlane( &d->m_cartPlane );
0345                     d->m_chart.addCoordinatePlane( &d->m_polPlane );
0346                 } else {
0347                     d->m_chart.replaceCoordinatePlane( &d->m_polPlane );
0348                 }
0349             }
0350         }
0351         switch ( chartType ) {
0352             case Bar:
0353                 diag = new BarDiagram( &d->m_chart, &d->m_cartPlane );
0354                 break;
0355             case Line:
0356                 diag = new LineDiagram( &d->m_chart, &d->m_cartPlane );
0357                 break;
0358             case Plot:
0359                 diag = new Plotter( &d->m_chart, &d->m_cartPlane );
0360                 break;
0361             case Pie:
0362                 diag = new PieDiagram( &d->m_chart, &d->m_polPlane );
0363                 break;
0364             case Polar:
0365                 diag = new PolarDiagram( &d->m_chart, &d->m_polPlane );
0366                 break;
0367             case Ring:
0368                 diag = new RingDiagram( &d->m_chart, &d->m_polPlane );
0369                 break;
0370             case NoType:
0371                 break;
0372         }
0373         if ( diag != nullptr ) {
0374             if ( isCartesian( oldType ) && isCartesian( chartType ) ) {
0375                 AbstractCartesianDiagram *oldDiag =
0376                         qobject_cast<AbstractCartesianDiagram*>( coordinatePlane()->diagram() );
0377                 AbstractCartesianDiagram *newDiag =
0378                         qobject_cast<AbstractCartesianDiagram*>( diag );
0379                 Q_FOREACH( CartesianAxis* axis, oldDiag->axes() ) {
0380                     oldDiag->takeAxis( axis );
0381                     newDiag->addAxis ( axis );
0382                 }
0383             }
0384 
0385             Q_FOREACH( Legend* l, d->m_chart.legends() ) {
0386                 l->setDiagram( diag );
0387             }
0388 
0389             diag->setModel( &d->m_model );
0390             coordinatePlane()->replaceDiagram( diag );
0391 
0392             //checkDatasetWidth( d->usedDatasetWidth );
0393         }
0394         //coordinatePlane()->setGridNeedsRecalculate();
0395     }
0396 
0397     if ( chartType != NoType ) {
0398         if ( chartType != oldType || chartSubType != subType() )
0399             setSubType( chartSubType );
0400         d->m_chart.resize( size() ); // triggering immediate update
0401     }
0402 }
0403 
0404 template< class DiagramType, class Subtype >
0405 void setSubtype( AbstractDiagram *_dia, Subtype st)
0406 {
0407     if ( DiagramType *dia = qobject_cast< DiagramType * >( _dia ) ) {
0408         dia->setType( st );
0409     }
0410 }
0411 
0412 void Widget::setSubType( SubType subType )
0413 {
0414     // ### at least PieDiagram, PolarDiagram and RingDiagram are unhandled here
0415 
0416     AbstractDiagram *dia = diagram();
0417     switch ( subType ) {
0418     case Normal:
0419         setSubtype< BarDiagram >( dia, BarDiagram::Normal );
0420         setSubtype< LineDiagram >( dia, LineDiagram::Normal );
0421         setSubtype< Plotter >( dia, Plotter::Normal );
0422         break;
0423     case Stacked:
0424         setSubtype< BarDiagram >( dia, BarDiagram::Stacked );
0425         setSubtype< LineDiagram >( dia, LineDiagram::Stacked );
0426         // setSubtype< Plotter >( dia, Plotter::Stacked );
0427         break;
0428     case Percent:
0429         setSubtype< BarDiagram >( dia, BarDiagram::Percent );
0430         setSubtype< LineDiagram >( dia, LineDiagram::Percent );
0431         setSubtype< Plotter >( dia, Plotter::Percent );
0432         break;
0433     case Rows:
0434         setSubtype< BarDiagram >( dia, BarDiagram::Rows );
0435         break;
0436     default:
0437         Q_ASSERT_X ( false, "Widget::setSubType", "Sub-type not supported!" );
0438         break;
0439     }
0440 }
0441 
0442 Widget::ChartType Widget::type() const
0443 {
0444     // PENDING(christoph) save the type out-of-band:
0445     AbstractDiagram * const dia = const_cast<Widget*>( this )->diagram();
0446     if ( qobject_cast< BarDiagram* >( dia ) )
0447         return Bar;
0448     else if ( qobject_cast< LineDiagram* >( dia ) )
0449         return Line;
0450     else if ( qobject_cast< Plotter* >( dia ) )
0451         return Plot;
0452     else if ( qobject_cast< PieDiagram* >( dia ) )
0453         return Pie;
0454     else if ( qobject_cast< PolarDiagram* >( dia ) )
0455         return Polar;
0456     else if ( qobject_cast< RingDiagram* >( dia ) )
0457         return Ring;
0458     else
0459         return NoType;
0460 }
0461 
0462 Widget::SubType Widget::subType() const
0463 {
0464     // PENDING(christoph) save the type out-of-band:
0465     Widget::SubType retVal = Normal;
0466 
0467     AbstractDiagram * const dia = const_cast<Widget*>( this )->diagram();
0468     BarDiagram*  barDia     = qobject_cast< BarDiagram* >( dia );
0469     LineDiagram* lineDia    = qobject_cast< LineDiagram* >( dia );
0470     Plotter*     plotterDia = qobject_cast< Plotter* >( dia );
0471 
0472 //FIXME(khz): Add the impl for these chart types - or remove them from here:
0473 //    PieDiagram*   pieDia   = qobject_cast< PieDiagram* >( diagram() );
0474 //    PolarDiagram* polarDia = qobject_cast< PolarDiagram* >( diagram() );
0475 //    RingDiagram*  ringDia  = qobject_cast< RingDiagram* >( diagram() );
0476 
0477 #define TEST_SUB_TYPE(DIAGRAM, INTERNALSUBTYPE, SUBTYPE) \
0478 { \
0479     if ( DIAGRAM && DIAGRAM->type() == INTERNALSUBTYPE ) \
0480         retVal = SUBTYPE; \
0481 }
0482     const Widget::ChartType mainType = type();
0483     switch ( mainType )
0484     {
0485         case Bar:
0486            TEST_SUB_TYPE( barDia, BarDiagram::Normal,  Normal );
0487            TEST_SUB_TYPE( barDia, BarDiagram::Stacked, Stacked );
0488            TEST_SUB_TYPE( barDia, BarDiagram::Percent, Percent );
0489            TEST_SUB_TYPE( barDia, BarDiagram::Rows,    Rows );
0490            break;
0491         case Line:
0492             TEST_SUB_TYPE( lineDia, LineDiagram::Normal,  Normal );
0493             TEST_SUB_TYPE( lineDia, LineDiagram::Stacked, Stacked );
0494             TEST_SUB_TYPE( lineDia, LineDiagram::Percent, Percent );
0495             break;
0496         case Plot:
0497             TEST_SUB_TYPE( plotterDia, Plotter::Normal,  Normal );
0498             TEST_SUB_TYPE( plotterDia, Plotter::Percent, Percent );
0499             break;
0500         case Pie:
0501            // no impl. yet
0502            break;
0503         case Polar:
0504            // no impl. yet
0505            break;
0506         case Ring:
0507            // no impl. yet
0508            break;
0509         default:
0510            Q_ASSERT_X ( false,
0511                         "Widget::subType", "Chart type not supported!" );
0512            break;
0513     }
0514     return retVal;
0515 }
0516 
0517 
0518 bool Widget::checkDatasetWidth( int width )
0519 {
0520     if ( width == diagram()->datasetDimension() )
0521     {
0522         d->usedDatasetWidth = width;
0523         return true;
0524     }
0525     qDebug() << "The current diagram type doesn't support this data dimension.";
0526     return false;
0527 /*    if ( d->usedDatasetWidth == width || d->usedDatasetWidth == 0 ) {
0528         d->usedDatasetWidth = width;
0529         diagram()->setDatasetDimension( width );
0530         return true;
0531     }
0532     qDebug() << "It's impossible to mix up the different setDataset() methods on the same widget.";
0533     return false;*/
0534 }
0535 
0536 void Widget::justifyModelSize( int rows, int columns )
0537 {
0538     QAbstractItemModel & model = d->m_model;
0539     const int currentRows = model.rowCount();
0540     const int currentCols = model.columnCount();
0541 
0542     if ( currentCols < columns )
0543         if ( ! model.insertColumns( currentCols, columns - currentCols ))
0544             qDebug() << "justifyModelSize: could not increase model size.";
0545     if ( currentRows < rows )
0546         if ( ! model.insertRows( currentRows, rows - currentRows ))
0547             qDebug() << "justifyModelSize: could not increase model size.";
0548 
0549     Q_ASSERT( model.rowCount() >= rows );
0550     Q_ASSERT( model.columnCount() >= columns );
0551 }