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 }