diff --git a/examples/Axis/Chart/mainwindow.h b/examples/Axis/Chart/mainwindow.h index 1ea13af..7c5b06d 100644 --- a/examples/Axis/Chart/mainwindow.h +++ b/examples/Axis/Chart/mainwindow.h @@ -1,47 +1,47 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include namespace KChart { class Chart; class BarDiagram; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); private: KChart::Chart* m_chart; TableModel m_model; KChart::BarDiagram* m_lines; }; #endif /* MAINWINDOW_H */ diff --git a/examples/Axis/Labels/AdjustedCartesianAxis.h b/examples/Axis/Labels/AdjustedCartesianAxis.h index ed85bfa..7acbe22 100644 --- a/examples/Axis/Labels/AdjustedCartesianAxis.h +++ b/examples/Axis/Labels/AdjustedCartesianAxis.h @@ -1,50 +1,50 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ADJUSTED_CARTESIAN_AXIS_H #define ADJUSTED_CARTESIAN_AXIS_H #include #include class AdjustedCartesianAxis: public KChart::CartesianAxis { Q_OBJECT Q_DISABLE_COPY( AdjustedCartesianAxis ) public: - explicit AdjustedCartesianAxis( KChart::AbstractCartesianDiagram* diagram = 0 ); + explicit AdjustedCartesianAxis( KChart::AbstractCartesianDiagram* diagram = nullptr ); const QString customizedLabel( const QString& label ) const Q_DECL_OVERRIDE; void setBounds( qreal lower, qreal upper ) { m_lowerBound = lower; m_upperBound = upper; } qreal lowerBound() const { return m_lowerBound; } qreal upperBound() const { return m_upperBound; } private: qreal m_lowerBound; qreal m_upperBound; }; #endif // ADJUSTED_CARTESIAN_AXIS_H diff --git a/examples/Axis/Labels/mainwindow.h b/examples/Axis/Labels/mainwindow.h index 548d902..c6d79f6 100644 --- a/examples/Axis/Labels/mainwindow.h +++ b/examples/Axis/Labels/mainwindow.h @@ -1,56 +1,56 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include namespace KChart { class Chart; class DatasetProxyModel; class LineDiagram; class CartesianAxis; class Legend; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); private slots: void annotationsToggled( bool ); void gridLinesOnAnnotationsToggled( bool ); private: KChart::Chart* m_chart; KChart::CartesianAxis* m_xAxis; TableModel m_model; KChart::DatasetProxyModel* m_datasetProxy; KChart::LineDiagram* m_lines; KChart::Legend* m_legend; }; #endif /* MAINWINDOW_H */ diff --git a/examples/Axis/Parameters/mainwindow.h b/examples/Axis/Parameters/mainwindow.h index 9a48b94..329afba 100644 --- a/examples/Axis/Parameters/mainwindow.h +++ b/examples/Axis/Parameters/mainwindow.h @@ -1,73 +1,73 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include namespace KChart { class Chart; class DatasetProxyModel; class LineDiagram; class LineAttributes; class CartesianAxis; class Legend; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); private slots: void on_lineTypeCB_currentIndexChanged( const QString & text ); void on_paintLegendCB_toggled( bool checked ); void on_paintValuesCB_toggled( bool checked ); void on_paintMarkersCB_toggled( bool checked ); void on_markersStyleCB_currentIndexChanged( const QString & text ); void on_markersWidthSB_valueChanged( int i ); void on_markersHeightSB_valueChanged( int i); void on_displayAreasCB_toggled( bool checked ); void on_transparencySB_valueChanged( int i ); void on_zoomFactorSB_valueChanged( double factor ); void on_hSBar_valueChanged( int value ); void on_vSBar_valueChanged( int value ); private: KChart::Chart* m_chart; TableModel m_model; KChart::DatasetProxyModel* m_datasetProxy; KChart::LineDiagram* m_lines; KChart::Legend* m_legend; // mutable KChart::CartesianAxis xAxis; //mutable KChart::CartesianAxis yAxis; }; #endif /* MAINWINDOW_H */ diff --git a/examples/Background/main.cpp b/examples/Background/main.cpp index 83e39eb..851c9d4 100644 --- a/examples/Background/main.cpp +++ b/examples/Background/main.cpp @@ -1,125 +1,125 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include using namespace KChart; class ChartWidget : public QWidget { Q_OBJECT public: - explicit ChartWidget(QWidget* parent=0) + explicit ChartWidget(QWidget* parent = nullptr) : QWidget(parent) { m_model.insertRows( 0, 2, QModelIndex() ); m_model.insertColumns( 0, 3, QModelIndex() ); for (int row = 0; row < 3; ++row) { for (int column = 0; column < 3; ++column) { QModelIndex index = m_model.index(row, column, QModelIndex()); m_model.setData(index, QVariant(row+1 * column) ); } } QPixmap* pixmap = new QPixmap ( "background.png" ); BarDiagram* diagram = new BarDiagram; diagram->setModel(&m_model); m_chart.coordinatePlane()->replaceDiagram(diagram); // Add at one Header and set it up HeaderFooter* header = new HeaderFooter( &m_chart ); header->setPosition( Position::North ); header->setText( "A Simple Bar Chart" ); m_chart.addHeaderFooter( header ); // Configure the Header text attributes TextAttributes hta; hta.setPen( QPen( Qt::blue ) ); // let the header resize itself // together with the widget. // so-called relative size Measure m( 35.0 ); m.setRelativeMode( header->autoReferenceArea(), KChartEnums::MeasureOrientationMinimum ); hta.setFontSize( m ); // min font size m.setValue( 3.0 ); m.setCalculationMode( KChartEnums::MeasureCalculationModeAbsolute ); hta.setMinimalFontSize( m ); header->setTextAttributes( hta ); // Configure the plane's Background BackgroundAttributes pba; pba.setPixmap( *pixmap ); pba.setPixmapMode( BackgroundAttributes::BackgroundPixmapModeStretched ); pba.setVisible( true ); diagram->coordinatePlane()->setBackgroundAttributes( pba ); // Configure the Header's Background BackgroundAttributes hba; hba.setBrush( Qt::white ); hba.setVisible( true ); header->setBackgroundAttributes( hba ); // Configure the plane Frame attributes FrameAttributes pfa; pfa.setPen( QPen ( QBrush( Qt::blue ), 2 ) ); pfa.setVisible( true ); diagram->coordinatePlane()->setFrameAttributes( pfa ); // Configure the header Frame attributes FrameAttributes hfa; hfa.setPen( QPen ( QBrush( Qt::darkGray ), 2 ) ); hfa.setPadding( 2 ); hfa.setVisible( true ); header->setFrameAttributes( hfa ); QVBoxLayout* l = new QVBoxLayout(this); l->addWidget(&m_chart); setLayout(l); } private: Chart m_chart; QStandardItemModel m_model; QPixmap pixmap; }; int main( int argc, char** argv ) { QApplication app( argc, argv ); ChartWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/examples/Bars/Advanced/mainwindow.cpp b/examples/Bars/Advanced/mainwindow.cpp index 3acd1ec..9697ab1 100644 --- a/examples/Bars/Advanced/mainwindow.cpp +++ b/examples/Bars/Advanced/mainwindow.cpp @@ -1,231 +1,231 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mainwindow.h" #include #include #include #include #include #include #include #include #include #include using namespace KChart; MainWindow::MainWindow( QWidget* parent ) : QWidget( parent ) { setupUi( this ); QHBoxLayout* chartLayout = new QHBoxLayout( chartFrame ); m_chart = new Chart(); chartLayout->addWidget( m_chart ); m_model.loadFromCSV( ":/data" ); // Set up the diagram m_bars = new BarDiagram(); m_bars->setModel( &m_model ); // define custom color for the borders of the bars QPen pen( m_bars->pen() ); pen.setColor( Qt::black ); pen.setWidth( 0 ); m_bars->setPen( pen ); m_chart->coordinatePlane()->replaceDiagram( m_bars ); // define custom colours for the bars QList bcolor; bcolor.append(Qt::darkGreen); bcolor.append(Qt::green); bcolor.append(Qt::darkRed); bcolor.append(Qt::red); for (int row=0; row < m_model.columnCount(); ++row) { m_bars->setBrush(row, QBrush(bcolor[row])); } // Configure the plane's Background BackgroundAttributes pba; //pba.setBrush( QBrush(QColor(0x20,0x20,0x60)) ); pba.setVisible( true ); m_chart->coordinatePlane()->setBackgroundAttributes( pba ); m_chart->setGlobalLeadingTop( 20 ); } void MainWindow::on_barTypeCB_currentIndexChanged( const QString & text ) { if ( text == "Normal" ) m_bars->setType( BarDiagram::Normal ); else if ( text == "Stacked" ) m_bars->setType( BarDiagram::Stacked ); else if ( text == "Percent" ) m_bars->setType( BarDiagram::Percent ); else qWarning (" Does not match any type"); m_chart->update(); } void MainWindow::on_barOrientationCB_currentIndexChanged(const QString & text) { if ( text == "Vertical" ) m_bars->setOrientation( Qt::Vertical ); else if ( text == "Horizontal" ) m_bars->setOrientation( Qt::Horizontal ); else qWarning (" Does not match any orientation"); m_chart->update(); } void MainWindow::on_paintValuesCB_toggled( bool checked ) { Q_UNUSED( checked ); // We set the DataValueAttributes on a per-column basis here, // because we want the texts to be printed in different // colours - according to their respective dataset's colour. const QFont font(QFont( "Comic", 10 )); const int colCount = m_bars->model()->columnCount(); for ( int iColumn = 0; iColumnbrush( iColumn ) ); DataValueAttributes a( m_bars->dataValueAttributes( iColumn ) ); TextAttributes ta( a.textAttributes() ); ta.setRotation( 0 ); ta.setFont( font ); ta .setPen( QPen( brush.color() ) ); if ( checked ) ta.setVisible( true ); else ta.setVisible( false ); a.setTextAttributes( ta ); a.setVisible( true ); m_bars->setDataValueAttributes( iColumn, a); } m_chart->update(); } void MainWindow::on_paintThreeDBarsCB_toggled( bool checked ) { ThreeDBarAttributes td( m_bars->threeDBarAttributes() ); qreal defaultDepth = td.depth(); if ( checked ) { td.setEnabled( true ); if ( threeDDepthCB->isChecked() ) td.setDepth( depthSB->value() ); else td.setDepth( defaultDepth ); } else { td.setEnabled( false ); } m_bars->setThreeDBarAttributes( td ); m_chart->update(); } void MainWindow::on_markColumnCB_toggled( bool checked ) { const int column = markColumnSB->value(); QPen pen( m_bars->pen( column ) ); if ( checked ) { pen.setColor( Qt::yellow ); pen.setStyle( Qt::DashLine ); pen.setWidth( 3 ); } else { pen.setColor( Qt::black ); pen.setStyle( Qt::SolidLine ); pen.setWidth( 1 ); } m_bars->setPen( column, pen ); m_chart->update(); } void MainWindow::on_depthSB_valueChanged( int i ) { Q_UNUSED( i ); if ( threeDDepthCB->isChecked() && paintThreeDBarsCB->isChecked() ) on_paintThreeDBarsCB_toggled( true ); } void MainWindow::on_threeDDepthCB_toggled( bool checked ) { Q_UNUSED( checked ); if ( paintThreeDBarsCB->isChecked() ) on_paintThreeDBarsCB_toggled( true ); } void MainWindow::on_markColumnSB_valueChanged( int i ) { QPen pen( m_bars->pen( i ) ); markColumnCB->setChecked( pen.color() == Qt::yellow ); } void MainWindow::on_widthSB_valueChanged( int value ) { if ( widthCB->isChecked() ) { BarAttributes ba( m_bars->barAttributes() ); ba.setFixedBarWidth( value ); ba.setUseFixedBarWidth( true ); m_bars->setBarAttributes( ba ); } m_chart->update(); } void MainWindow::on_widthCB_toggled( bool checked ) { if ( checked ) { on_widthSB_valueChanged( widthSB->value() ); } else { BarAttributes ba( m_bars->barAttributes() ); ba.setUseFixedBarWidth( false ); m_bars->setBarAttributes( ba ); m_chart->update(); } } void MainWindow::on_fixPlaneSizeCB_toggled( bool checked ) { CartesianCoordinatePlane* plane = qobject_cast( m_chart->coordinatePlane() ); - if ( plane == 0 ) + if ( plane == nullptr ) return; plane->setFixedDataCoordinateSpaceRelation( checked ); // just a little adjustment: // Reset the zoom settings to their initial values // when the releation-adjust checkbox is unchecked: if ( ! checked ) { m_chart->coordinatePlane()->setZoomFactorX( 1.0 ); m_chart->coordinatePlane()->setZoomFactorY( 1.0 ); m_chart->coordinatePlane()->setZoomCenter( QPointF(0.5, 0.5) ); } m_chart->update(); } diff --git a/examples/Bars/Advanced/mainwindow.h b/examples/Bars/Advanced/mainwindow.h index 8fd24ae..7b0519e 100644 --- a/examples/Bars/Advanced/mainwindow.h +++ b/examples/Bars/Advanced/mainwindow.h @@ -1,60 +1,60 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include namespace KChart { class Chart; class BarDiagram; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); private slots: void on_barTypeCB_currentIndexChanged( const QString & text ); void on_barOrientationCB_currentIndexChanged( const QString & text ); void on_paintValuesCB_toggled( bool checked ); void on_paintThreeDBarsCB_toggled( bool checked ); void on_markColumnCB_toggled( bool checked ); void on_markColumnSB_valueChanged( int i ); void on_threeDDepthCB_toggled( bool checked ); void on_depthSB_valueChanged( int i ); void on_widthCB_toggled( bool checked ); void on_widthSB_valueChanged( int i ); void on_fixPlaneSizeCB_toggled( bool checked ); private: KChart::Chart* m_chart; KChart::BarDiagram* m_bars; TableModel m_model; }; #endif /* MAINWINDOW_H */ diff --git a/examples/Bars/Parameters/main.cpp b/examples/Bars/Parameters/main.cpp index ba538d9..d2f00a1 100644 --- a/examples/Bars/Parameters/main.cpp +++ b/examples/Bars/Parameters/main.cpp @@ -1,108 +1,108 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include using namespace KChart; class ChartWidget : public QWidget { Q_OBJECT public: - explicit ChartWidget( QWidget *parent = 0 ); + explicit ChartWidget( QWidget *parent = nullptr ); private: Chart m_chart; QStandardItemModel m_model; }; ChartWidget::ChartWidget( QWidget *parent ) : QWidget( parent ) { m_model.insertRows( 0, 2, QModelIndex() ); m_model.insertColumns( 0, 3, QModelIndex() ); for ( int row = 0; row < 3; ++row ) { for ( int column = 0; column < 3; ++column ) { QModelIndex index = m_model.index( row, column, QModelIndex() ); m_model.setData(index, row * 3 + column ); } } BarDiagram* diagram = new KChart::BarDiagram; diagram->setModel( &m_model ); BarAttributes ba( diagram->barAttributes() ); //set the bar width and enable it ba.setFixedBarWidth( 140 ); ba.setUseFixedBarWidth( true ); //configure gap between values //and blocks ba.setGroupGapFactor( 0.50 ); ba.setBarGapFactor( 0.125 ); //assign to the diagram diagram->setBarAttributes( ba ); // display the values DataValueAttributes dva( diagram->dataValueAttributes() ); TextAttributes ta( dva.textAttributes() ); //rotate if you wish //ta.setRotation( 0 ); ta.setFont( QFont( "Comic", 9 ) ); ta .setPen( QPen( QColor( Qt::darkGreen ) ) ); ta.setVisible( true ); dva.setTextAttributes( ta ); dva.setVisible( true ); diagram->setDataValueAttributes( dva ); //draw a surrounding line around bars QPen linePen; linePen.setColor( Qt::magenta ); linePen.setWidth( 4 ); linePen.setStyle( Qt::DotLine ); //draw only around a dataset //to draw around all the bars // call setPen( myPen ); diagram->setPen( 1, linePen ); m_chart.coordinatePlane()->replaceDiagram( diagram ); m_chart.setGlobalLeadingTop( 40 ); QVBoxLayout *l = new QVBoxLayout( this ); l->addWidget( &m_chart ); setLayout( l ); } int main( int argc, char** argv ) { QApplication app( argc, argv ); ChartWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/examples/Bars/Simple/main.cpp b/examples/Bars/Simple/main.cpp index 7ba1d2e..59119aa 100644 --- a/examples/Bars/Simple/main.cpp +++ b/examples/Bars/Simple/main.cpp @@ -1,106 +1,106 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include class ChartWidget : public QWidget { Q_OBJECT public: - explicit ChartWidget(QWidget* parent=0) + explicit ChartWidget(QWidget* parent = nullptr) : QWidget(parent) { m_model.insertRows( 0, 2, QModelIndex() ); m_model.insertColumns( 0, 3, QModelIndex() ); for (int row = 0; row < 3; ++row) { for (int column = 0; column < 3; ++column) { QModelIndex index = m_model.index(row, column, QModelIndex()); m_model.setData(index, QVariant(row+1 * column) ); /* // show tooltips: m_model.setData(index, QString("" "" "
RowColumnValue
%1%2%3
") .arg(row).arg(column).arg(row+1 * column), Qt::ToolTipRole ); */ } } /* { // show a comment at one data item: const int row = 0; const int column = 2; const QModelIndex index = m_model.index(row, column, QModelIndex()); m_model.setData( index, QString("Value %1/%2: %3") .arg( row ) .arg( column ) .arg( m_model.data( index ).toInt() ), KChart::CommentRole ); } */ KChart::BarDiagram* diagram = new KChart::BarDiagram; diagram->setModel(&m_model); diagram->setPen( QPen( Qt::black, 0 ) ); m_chart.coordinatePlane()->replaceDiagram(diagram); QVBoxLayout* l = new QVBoxLayout(this); l->addWidget(&m_chart); setLayout(l); /* // render chart to a SVG QSvgGenerator generator; generator.setFileName("/home/kdab/chart.svg"); generator.setSize(QSize(300, 300)); generator.setViewBox(QRect(0, 0, 300, 300)); generator.setTitle(tr("SVG Chart")); QPainter painter; painter.begin(&generator); painter.setRenderHint(QPainter::Antialiasing); m_chart.paint(&painter, generator.viewBox()); painter.end(); */ } private: KChart::Chart m_chart; QStandardItemModel m_model; }; int main( int argc, char** argv ) { QApplication app( argc, argv ); ChartWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/examples/DataValueTexts/mainwindow.h b/examples/DataValueTexts/mainwindow.h index cc10cf9..da7dc58 100644 --- a/examples/DataValueTexts/mainwindow.h +++ b/examples/DataValueTexts/mainwindow.h @@ -1,83 +1,83 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include #include namespace KChart { class Chart; class BarDiagram; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); private slots: void on_scopeOneBarRB_toggled( bool checked ); void on_scopeBarDatasetSB_valueChanged( int i ); void on_scopeBarItemSB_valueChanged( int i ); void on_scopeDatasetRB_toggled( bool checked ); void on_scopeDatasetSB_valueChanged( int i ); void on_scopeCommonRB_toggled( bool checked ); void on_paintValuesCB_toggled( bool checked ); void on_fontCombo_currentIndexChanged( const QString & text ); void on_relativeSizeSB_valueChanged( int i ); void on_minimumSizeSB_valueChanged( int i ); void on_rotationSB_valueChanged( int i ); void on_posPosCombo_currentIndexChanged( const QString & text ); void on_posAlignCombo_currentIndexChanged( const QString & text ); void on_posPadHoriSB_valueChanged( int i ); void on_posPadVertSB_valueChanged( int i ); void on_negPosCombo_currentIndexChanged( const QString & text ); void on_negAlignCombo_currentIndexChanged( const QString & text ); void on_negPadHoriSB_valueChanged( int i ); void on_negPadVertSB_valueChanged( int i ); void on_labelLE_textEdited( const QString & text ); void on_prefixLE_textEdited( const QString & text ); void on_suffixLE_textEdited( const QString & text ); private: const QModelIndex currentIndex() const; const KChart::DataValueAttributes attributes() const; const char* positionToScreenName( const KChart::Position& pos ) const; const Qt::Alignment alignmentFromScreeName( const QString& name ) const; const QString alignmentToScreenName( const Qt::Alignment& align ) const; void populateWidgets(); void setAttributes( const KChart::DataValueAttributes& da ); KChart::Chart* m_chart; KChart::BarDiagram* m_bars; TableModel m_model; }; #endif /* MAINWINDOW_H */ diff --git a/examples/DrawIntoPainter/framewidget.cpp b/examples/DrawIntoPainter/framewidget.cpp index dca4bf4..4e37e0f 100644 --- a/examples/DrawIntoPainter/framewidget.cpp +++ b/examples/DrawIntoPainter/framewidget.cpp @@ -1,77 +1,77 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "framewidget.h" #include #include #include FrameWidget::FrameWidget( QWidget* parent, Qt::WindowFlags f ) : QWidget( parent, f ) - , mChart( 0 ) + , mChart( nullptr ) { // this bloc left empty intentionally } void FrameWidget::setChart( KChart::Chart* chart ) { mChart = chart; // This is necessary because Chart can't automatically schedule somebody else (this object) to // call its custom paint method. connect( mChart, SIGNAL(propertiesChanged()), SLOT(update()) ); } void FrameWidget::paintEvent( QPaintEvent* e ) { if ( !mChart ) { QWidget::paintEvent( e ); } else { QPainter painter( this ); const int wid = 64; const QRect r( rect() ); const QPen oldPen( painter.pen() ); const QBrush oldBrush( painter.brush() ); // paint below the chart painter.setPen( QPen( Qt::NoPen ) ); painter.setBrush( QBrush( QColor( 0xd0, 0xd0, 0xff ) ) ); painter.drawEllipse( r.left(), r.top(), wid, wid ); painter.drawEllipse( r.left() + r.width() - wid - 1, r.top(), wid, wid ); painter.drawEllipse( r.left(), r.top() + r.height() - wid - 1, wid, wid ); painter.drawEllipse( r.left() + r.width() - wid - 1, r.top() + r.height() - wid - 1, wid, wid ); painter.setBrush( oldBrush ); painter.setPen( oldPen ); // paint the chart mChart->paint( &painter, QRect( r.left() + wid / 2, r.top() + wid / 2, r.width() - wid, r.height() - wid ) ); // paint over the chart painter.setPen( QPen( Qt::NoPen ) ); painter.setBrush( QBrush( QColor( 0xd0, 0xff, 0xff ) ) ); const int wid2 = 40; const int gap = ( wid - wid2 ) / 2; painter.drawEllipse( r.left() + gap, r.top() + gap, wid2, wid2 ); painter.drawEllipse( r.left() + r.width() - wid + gap - 1, r.top() + gap, wid2, wid2 ); painter.drawEllipse( r.left() + gap, r.top() + r.height() - wid + gap - 1, wid2, wid2 ); painter.drawEllipse( r.left() + r.width() - wid + gap - 1, r.top() + r.height() - wid + gap - 1, wid2, wid2 ); painter.setBrush( oldBrush ); painter.setPen( oldPen ); } } diff --git a/examples/DrawIntoPainter/framewidget.h b/examples/DrawIntoPainter/framewidget.h index 5623150..10d74f6 100644 --- a/examples/DrawIntoPainter/framewidget.h +++ b/examples/DrawIntoPainter/framewidget.h @@ -1,47 +1,47 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FRAMEWIDGET_H #define FRAMEWIDGET_H #include namespace KChart { class Chart; } class FrameWidget : public QWidget { Q_OBJECT public: - explicit FrameWidget( QWidget * parent = 0, Qt::WindowFlags f = Qt::WindowFlags() ); + explicit FrameWidget( QWidget * parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags() ); void paintEvent( QPaintEvent* ) Q_DECL_OVERRIDE; void setChart( KChart::Chart* chart ); private: KChart::Chart* mChart; }; #endif /* FRAMEWIDGET_H */ diff --git a/examples/DrawIntoPainter/mainwindow.cpp b/examples/DrawIntoPainter/mainwindow.cpp index a4d0e58..787e69f 100644 --- a/examples/DrawIntoPainter/mainwindow.cpp +++ b/examples/DrawIntoPainter/mainwindow.cpp @@ -1,411 +1,411 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mainwindow.h" #include "framewidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KChart; static QPixmap drawIntoPixmap( const QSize& size, KChart::Chart* chart ) { QPixmap pix( size ); pix.fill( Qt::white ); QPainter painter( &pix ); chart->paint( &painter, QRect( 0, 0, size.width(), size.height() ) ); return pix; } // When set, this example uses FrameWidget which uses Chart::paint to paint itself. // When not set, this example uses a Chart widget directly. #define USE_FRAME_WIDGET 1 MainWindow::MainWindow( QWidget* parent ) : QWidget( parent ) { setupUi( this ); QHBoxLayout* chartLayout = new QHBoxLayout( chartFrame ); #ifdef USE_FRAME_WIDGET FrameWidget* chartFrameWidget = new FrameWidget(); chartLayout->addWidget( chartFrameWidget ); #endif hSBar->setVisible( false ); vSBar->setVisible( false ); m_model.loadFromCSV( ":/data" ); // Set up the diagram m_lines = new LineDiagram(); m_lines->setModel( &m_model ); CartesianAxis *xAxis = new CartesianAxis( m_lines ); CartesianAxis *yAxis = new CartesianAxis( m_lines ); CartesianAxis *axisTop = new CartesianAxis( m_lines ); CartesianAxis *axisRight = new CartesianAxis( m_lines ); xAxis->setPosition( KChart::CartesianAxis::Bottom ); yAxis->setPosition( KChart::CartesianAxis::Left ); axisTop->setPosition( KChart::CartesianAxis::Top ); axisRight->setPosition( KChart::CartesianAxis::Right ); m_lines->addAxis( xAxis ); m_lines->addAxis( yAxis ); m_lines->addAxis( axisTop ); m_lines->addAxis( axisRight ); m_chart = new Chart(); //m_chart->setGlobalLeading(10,10,10,10); // by default there is no leading #ifdef USE_FRAME_WIDGET chartFrameWidget->setChart( m_chart ); // make sure, we re-draw after changing one of the chart's properties connect( m_chart, SIGNAL(propertiesChanged()), chartFrameWidget, SLOT(update()) ) ; #else chartLayout->addWidget( m_chart ); #endif m_chart->coordinatePlane()->replaceDiagram( m_lines ); for ( int iColumn = 0; iColumn < m_lines->model()->columnCount(); ++iColumn ) { QPen pen(m_lines->pen( iColumn )); pen.setWidth(4); m_lines->setPen( iColumn, pen ); } FrameAttributes faChart( m_chart->frameAttributes() ); faChart.setVisible( true ); faChart.setPen( QPen(QColor( 0x60, 0x60, 0xb0 ), 8 ) ); m_chart->setFrameAttributes( faChart ); BackgroundAttributes baChart( m_chart->backgroundAttributes() ); baChart.setVisible( true ); baChart.setBrush( QColor( 0xd0, 0xd0, 0xff ) ); m_chart->setBackgroundAttributes( baChart ); // Set up the legend m_legend = new Legend( m_lines, m_chart ); m_legend->setPosition( Position::South ); m_legend->setAlignment( Qt::AlignRight ); m_legend->setShowLines( false ); m_legend->setTitleText( tr( "Legend" ) ); m_legend->setOrientation( Qt::Horizontal ); // setting the legend frame and background to the same color: const QColor legendColor( 0xff, 0xe0, 0x80 ); FrameAttributes faLegend( m_legend->frameAttributes() ); faLegend.setVisible( true ); faLegend.setPen( QPen( legendColor, 1 ) ); m_legend->setFrameAttributes( faLegend ); BackgroundAttributes baLegend( m_legend->backgroundAttributes() ); baLegend.setVisible( true ); baLegend.setBrush( legendColor ); m_legend->setBackgroundAttributes( baLegend ); m_chart->addLegend( m_legend ); // for illustration we paint the same chart at different sizes: QSize size1 = QSize( 200, 200 ); QSize size2 = QSize( 800, 800 ); m_pix1 = drawIntoPixmap( size1, m_chart ); m_pix2 = drawIntoPixmap( size2, m_chart ); m_pix2 = m_pix2.scaled( size1 ); m_smallChart1 = new QLabel( this ); m_smallChart1->setWindowTitle( "200x200" ); m_smallChart1->setPixmap( m_pix1 ); m_smallChart1->setFixedSize( m_pix1.size() ); m_smallChart1->move( width() - m_pix1.width() * 2, height() / 2 - m_pix1.height() - 5 ); m_smallChart1->show(); m_smallChart2 = new QLabel( this ); m_smallChart2->setWindowTitle( "800x800 scaled down" ); m_smallChart2->setPixmap( m_pix2 ); m_smallChart2->setFixedSize( m_pix2.size() ); m_smallChart2->move( width() - m_pix2.width() * 2, height() / 2 + 5 ); m_smallChart2->show(); faChart.setPen( QPen( QColor( 0xb0, 0xb0, 0xff ), 8 ) ); m_chart->setFrameAttributes( faChart ); // initialize attributes; this is necessary because we need to enable data value attributes before // any of them (e.g. only markers) can be displayed. but if we enable data value attributs, a default // data value text is included, even if we only wanted to set markers. so we enable DVA and then // individually disable the parts we don't want. on_paintValuesCB_toggled( false ); on_paintMarkersCB_toggled( false ); } void MainWindow::on_lineTypeCB_currentIndexChanged( const QString & text ) { if ( text == "Normal" ) m_lines->setType( LineDiagram::Normal ); else if ( text == "Stacked" ) m_lines->setType( LineDiagram::Stacked ); else if ( text == "Percent" ) m_lines->setType( LineDiagram::Percent ); else qWarning (" Does not match any type"); } void MainWindow::on_paintLegendCB_toggled( bool checked ) { KChart::Legend* legend = m_chart->legend(); - if ( checked != ( legend != 0 ) ) { + if ( checked != ( legend != nullptr ) ) { if ( checked ) m_chart->addLegend( m_legend ); else m_chart->takeLegend( legend ); } } void MainWindow::on_paintValuesCB_toggled( bool checked ) { const int colCount = m_lines->model()->columnCount(); for ( int iColumn = 0; iColumn < colCount; ++iColumn ) { DataValueAttributes a = m_lines->dataValueAttributes( iColumn ); a.setVisible( true ); TextAttributes ta = a.textAttributes(); ta.setRotation( 0 ); ta.setFont( QFont( "Comic", 10 ) ); ta.setPen( m_lines->brush( iColumn ).color() ); ta.setVisible( checked ); a.setTextAttributes( ta ); m_lines->setDataValueAttributes( iColumn, a ); } } void MainWindow::on_paintMarkersCB_toggled( bool checked ) { paintMarkers( checked, QSize() ); } void MainWindow::on_markersStyleCB_currentIndexChanged( const QString & text ) { Q_UNUSED( text ); on_paintMarkersCB_toggled( paintMarkersCB->isChecked() ); } void MainWindow::on_markersWidthSB_valueChanged( int i ) { Q_UNUSED( i ); markersHeightSB->setValue( markersWidthSB->value() ); on_paintMarkersCB_toggled( paintMarkersCB->isChecked() ); } void MainWindow::on_markersHeightSB_valueChanged( int i ) { Q_UNUSED( i ); markersWidthSB->setValue( markersHeightSB->value() ); on_paintMarkersCB_toggled( paintMarkersCB->isChecked() ); } void MainWindow::on_displayAreasCB_toggled( bool checked ) { const int colCount = m_lines->model()->columnCount(); for ( int iColumn = 0; iColumn < colCount; ++iColumn ) { LineAttributes la( m_lines->lineAttributes( iColumn ) ); la.setDisplayArea( checked ); if ( checked ) la.setTransparency( transparencySB->value() ); m_lines->setLineAttributes( iColumn, la ); } } void MainWindow::on_transparencySB_valueChanged( int alpha ) { Q_UNUSED( alpha ); if ( !displayAreasCB->isChecked() ) displayAreasCB->setChecked( true ); else on_displayAreasCB_toggled( true ); } void MainWindow::on_zoomFactorSB_valueChanged( qreal factor ) { const bool isZoomedIn = factor > 1.0f; hSBar->setVisible( isZoomedIn ); vSBar->setVisible( isZoomedIn ); if ( !isZoomedIn ) { hSBar->setValue( 500 ); vSBar->setValue( 500 ); } m_chart->coordinatePlane()->setZoomFactorX( factor ); m_chart->coordinatePlane()->setZoomFactorY( factor ); } void MainWindow::on_hSBar_valueChanged( int hPos ) { m_chart->coordinatePlane()->setZoomCenter( QPointF( hPos / 1000.0, vSBar->value() / 1000.0 ) ); m_chart->update(); } void MainWindow::on_vSBar_valueChanged( int vPos ) { m_chart->coordinatePlane()->setZoomCenter( QPointF( hSBar->value() / 1000.0, vPos / 1000.0 ) ); } // since DataValue markers have no relative sizing mode we need to scale them for printing void MainWindow::paintMarkers( bool checked, const QSize& printSize ) { MarkerAttributes::MarkerStylesMap map; map.insert( 0, MarkerAttributes::MarkerSquare ); map.insert( 1, MarkerAttributes::MarkerCircle ); map.insert( 2, MarkerAttributes::MarkerRing ); map.insert( 3, MarkerAttributes::MarkerCross ); map.insert( 4, MarkerAttributes::MarkerDiamond ); // next: Specify column- / cell-specific attributes! const int colCount = m_lines->model()->columnCount(); for ( int iColumn = 0; iColumn < colCount; ++iColumn ) { DataValueAttributes dva = m_lines->dataValueAttributes( iColumn ); dva.setVisible( true ); MarkerAttributes ma( dva.markerAttributes() ); switch ( markersStyleCB->currentIndex() ) { case 0: ma.setMarkerStyle( MarkerAttributes::MarkerSquare ); break; case 1: // Column-specific attributes ma.setMarkerStyle( map.value( iColumn ) ); break; case 2: ma.setMarkerStyle( MarkerAttributes::MarkerCircle ); break; case 3: ma.setMarkerStyle( MarkerAttributes::MarkerDiamond ); break; case 4: ma.setMarkerStyle( MarkerAttributes::Marker1Pixel ); break; case 5: ma.setMarkerStyle( MarkerAttributes::Marker4Pixels ); break; case 6: ma.setMarkerStyle( MarkerAttributes::MarkerRing ); break; case 7: ma.setMarkerStyle( MarkerAttributes::MarkerCross ); break; case 8: ma.setMarkerStyle( MarkerAttributes::MarkerFastCross ); break; default: Q_ASSERT( false ); } ma.setVisible( checked ); qreal factorWidth = printSize.isValid() ? ( printSize.width() / m_chart->rect().width() ) : 1.0f; qreal factorHeight = printSize.isValid() ? ( printSize.height() / m_chart->rect().height() ) : 1.0f; ma.setMarkerSize( QSize( markersWidthSB->value() * factorWidth, markersHeightSB->value() * factorHeight ) ); dva.setMarkerAttributes( ma ); m_lines->setDataValueAttributes( iColumn, dva ); // make a special one for certain values DataValueAttributes yellowAttributes( dva ); MarkerAttributes yellowMarker( yellowAttributes.markerAttributes() ); yellowMarker.setMarkerColor( Qt::yellow ); yellowAttributes.setMarkerAttributes( yellowMarker ); const int rowCount = m_lines->model()->rowCount(); for ( int j = 0; j < rowCount; ++j ) { QModelIndex index = m_lines->model()->index( j, iColumn, QModelIndex() ); QBrush brush = m_lines->model()->headerData( iColumn, Qt::Vertical, DatasetBrushRole ).value(); qreal value = m_lines->model()->data( index ).toReal(); /* Set a specific color - marker for a specific value */ if ( value == 13 ) { m_lines->setDataValueAttributes( index, yellowAttributes ); } } } } void MainWindow::on_savePB_clicked() { const QString file = QFileDialog::getSaveFileName( this, tr( "Choose PNG File..." ) ); if ( file.isEmpty() ) return; qDebug() << "Painting into PNG"; QPixmap qpix( 600, 600 ); QPainter painter( &qpix ); painter.setBrush( Qt::white ); painter.fillRect( 0, 0, 600, 600, Qt::white); m_chart->paint( &painter, QRect( 100, 100, 400, 400 ) ); painter.end(); qpix.save( file, "PNG" ); qDebug() << "Painting into PNG - done"; } void MainWindow::on_savePDF_clicked() { const QString file = QFileDialog::getSaveFileName( this, tr( "Choose PDF File..." ) ); if ( file.isEmpty() ) return; qDebug() << "Painting into PDF"; QPrinter printer( QPrinter::HighResolution ); printer.setOrientation( QPrinter::Landscape ); printer.setOutputFormat( QPrinter::PdfFormat ); printer.setOutputFileName( file ); paintMarkers( paintMarkersCB->isChecked(), printer.pageRect().size() ); QPainter painter; painter.begin( &printer ); m_chart->paint( &painter, printer.pageRect() ); painter.end(); paintMarkers( paintMarkersCB->isChecked(), m_chart->geometry().size() ); qDebug() << "Painting into PDF - done"; } void MainWindow::resizeEvent( QResizeEvent * ) { m_smallChart1->move( width() - m_pix1.width() * 2, height() / 2 - m_pix1.height() - 5 ); m_smallChart2->move( width() - m_pix2.width() * 2, height() / 2 + 5 ); } diff --git a/examples/DrawIntoPainter/mainwindow.h b/examples/DrawIntoPainter/mainwindow.h index c7242a8..4a75993 100644 --- a/examples/DrawIntoPainter/mainwindow.h +++ b/examples/DrawIntoPainter/mainwindow.h @@ -1,83 +1,83 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include #include namespace KChart { class Chart; class DatasetProxyModel; class LineDiagram; class Legend; } QT_BEGIN_NAMESPACE class QLabel; QT_END_NAMESPACE class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); protected: /** * Adjusts the two small charts when the window is resized. */ /* reimp */ void resizeEvent ( QResizeEvent * ) Q_DECL_OVERRIDE; private slots: void on_lineTypeCB_currentIndexChanged( const QString & text ); void on_paintLegendCB_toggled( bool checked ); void on_paintValuesCB_toggled( bool checked ); void on_paintMarkersCB_toggled( bool checked ); void on_markersStyleCB_currentIndexChanged( const QString & text ); void on_markersWidthSB_valueChanged( int i ); void on_markersHeightSB_valueChanged( int i); void on_displayAreasCB_toggled( bool checked ); void on_transparencySB_valueChanged( int i ); void on_zoomFactorSB_valueChanged( qreal factor ); void on_hSBar_valueChanged( int value ); void on_vSBar_valueChanged( int value ); void on_savePB_clicked(); void on_savePDF_clicked(); private: void paintMarkers( bool checked, const QSize& printSize ); private: KChart::Chart* m_chart; TableModel m_model; KChart::DatasetProxyModel* m_datasetProxy; KChart::LineDiagram* m_lines; KChart::Legend* m_legend; QPixmap m_pix1, m_pix2; QLabel* m_smallChart1; QLabel* m_smallChart2; }; #endif /* MAINWINDOW_H */ diff --git a/examples/EmptyValues/mainwindow.h b/examples/EmptyValues/mainwindow.h index 76d71d2..d446611 100644 --- a/examples/EmptyValues/mainwindow.h +++ b/examples/EmptyValues/mainwindow.h @@ -1,47 +1,47 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include namespace KChart { class Chart; class BarDiagram; } class MainWindow : public QWidget { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); virtual ~MainWindow() { } private: KChart::Chart* m_chart; KChart::BarDiagram* m_bars; TableModel m_model; }; #endif /* MAINWINDOW_H */ diff --git a/examples/Gantt/legend_example/entrydelegate.h b/examples/Gantt/legend_example/entrydelegate.h index 458833c..80fed80 100644 --- a/examples/Gantt/legend_example/entrydelegate.h +++ b/examples/Gantt/legend_example/entrydelegate.h @@ -1,42 +1,42 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ENTRYDELEGATE_H #define ENTRYDELEGATE_H #include namespace KGantt { class ConstraintModel; } class EntryDelegate : public QItemDelegate { public: - explicit EntryDelegate( KGantt::ConstraintModel* constraintModel, QObject* parent = 0 ); + explicit EntryDelegate( KGantt::ConstraintModel* constraintModel, QObject* parent = nullptr ); bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ) Q_DECL_OVERRIDE; private: void addConstraint(const QModelIndex & index1, const QModelIndex & index2); void setReadOnly(const QModelIndex & index, bool readOnly); KGantt::ConstraintModel* constraintModel; }; #endif /* ENTRYDELEGATE_H */ diff --git a/examples/Gantt/legend_example/entrydialog.h b/examples/Gantt/legend_example/entrydialog.h index 10d9a3a..a99a4c9 100644 --- a/examples/Gantt/legend_example/entrydialog.h +++ b/examples/Gantt/legend_example/entrydialog.h @@ -1,68 +1,68 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ENTRYDIALOG_H #define ENTRYDIALOG_H #include #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; namespace Ui { class EntryDialog; } QT_END_NAMESPACE namespace KGantt { class ConstraintModel; } class EntryDialog : public QDialog { Q_OBJECT public: - explicit EntryDialog( const QAbstractItemModel* model, QWidget* parent = 0, Qt::WindowFlags f = Qt::WindowFlags() ); + explicit EntryDialog( const QAbstractItemModel* model, QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags() ); void initFrom( const QModelIndex& index, const KGantt::ConstraintModel* constraintModel ); QString name() const; int type() const; QDateTime startDate() const; QDateTime endDate() const; int completion() const; bool readOnly() const; QModelIndex depends() const; QString legend() const; private slots: void updateEndDate( const QDateTime& startDate ); void disableEditing( bool disable ); void typeChanged( int index ); private: void init(); void addDependItem( const QAbstractItemModel* model, const QModelIndex& index, int indent = 0 ); QList indexList; const QAbstractItemModel* model; Ui::EntryDialog* ui; }; #endif /* ENTRYDIALOG_H */ diff --git a/examples/Gantt/legend_example/mainwindow.cpp b/examples/Gantt/legend_example/mainwindow.cpp index 3a42068..27e4d5c 100644 --- a/examples/Gantt/legend_example/mainwindow.cpp +++ b/examples/Gantt/legend_example/mainwindow.cpp @@ -1,390 +1,390 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mainwindow.h" #include "ui_mainwindow.h" #include "entrydelegate.h" #include "entrydialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include class MyStandardItem : public QStandardItem { public: MyStandardItem( const QVariant& v ) : QStandardItem() { setData( v, Qt::DisplayRole ); } MyStandardItem( const QString& v ) : QStandardItem() { setData( v, Qt::DisplayRole ); } }; MainWindow::MainWindow( QWidget* parent, Qt::WindowFlags flags ) : QMainWindow( parent, flags ), - smallLegend( 0 ), - detailedLegend( 0 ), + smallLegend( nullptr ), + detailedLegend( nullptr ), ui( new Ui::MainWindow ) { ui->setupUi( this ); initModel(); initActions(); initItemDelegate(); initGrid(); QTreeView* leftView = qobject_cast( ui->ganttView->leftView() ); Q_ASSERT( leftView ); leftView->setColumnHidden( 1, true ); leftView->setColumnHidden( 2, true ); leftView->setColumnHidden( 3, true ); leftView->setColumnHidden( 4, true ); leftView->setColumnHidden( 5, true ); leftView->header()->setStretchLastSection( true ); connect( ui->ganttView->leftView(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)) ); connect( ui->ganttView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(enableActions(QItemSelection)) ); } MainWindow::~MainWindow() { } void MainWindow::closeEvent(QCloseEvent *event) { delete smallLegend; delete detailedLegend; event->accept(); } void MainWindow::initModel() { model = new QStandardItemModel( 0, 6, this ); model->setHeaderData( 0, Qt::Horizontal, tr( "Tree View of Entries" ) ); ui->ganttView->setModel( model ); QStandardItemModel* lmodel = new QStandardItemModel; lmodel->appendRow( QList() << new MyStandardItem( QVariant() ) << new MyStandardItem( KGantt::TypeEvent ) << new MyStandardItem( QVariant() ) << new MyStandardItem( QVariant() ) << new MyStandardItem( QVariant() ) << new MyStandardItem( QString::fromLatin1("Event") ) ); lmodel->appendRow( QList() << new MyStandardItem( QVariant() ) << new MyStandardItem( KGantt::TypeTask ) << new MyStandardItem( QVariant() ) << new MyStandardItem( QVariant() ) << new MyStandardItem( QVariant() ) << new MyStandardItem( QString::fromLatin1("Task") ) ); lmodel->appendRow( QList() << new MyStandardItem( QVariant() ) << new MyStandardItem( KGantt::TypeSummary ) << new MyStandardItem( QVariant() ) << new MyStandardItem( QVariant() ) << new MyStandardItem( QVariant() ) << new MyStandardItem( QString::fromLatin1("Summary") ) ); smallLegend = new KGantt::Legend(); smallLegend->setWindowTitle( tr( "Legend" ) ); smallLegend->show(); smallLegend->setModel( lmodel ); detailedLegend = new KGantt::Legend(); detailedLegend->setWindowTitle( tr( "List" ) ); detailedLegend->show(); detailedLegend->setModel( model ); constraintModel = new KGantt::ConstraintModel( this ); ui->ganttView->setConstraintModel( constraintModel ); } void MainWindow::initActions() { newEntryAction = new QAction( tr( "New entry" ), this ); newEntryAction->setShortcut( QKeySequence::New ); connect( newEntryAction, SIGNAL(triggered()), this, SLOT(addNewEntry()) ); removeEntryAction = new QAction( tr( "Remove entry" ), this ); removeEntryAction->setShortcut( QKeySequence::Delete ); connect( removeEntryAction, SIGNAL(triggered()), this, SLOT(removeEntry()) ); zoomInAction = new QAction( tr( "Zoom In" ), this ); zoomInAction->setShortcut( QKeySequence::ZoomIn ); connect( zoomInAction, SIGNAL(triggered()), this, SLOT(zoomIn()) ); zoomOutAction = new QAction( tr( "Zoom Out" ), this ); zoomOutAction->setShortcut( QKeySequence::ZoomOut ); connect( zoomOutAction, SIGNAL(triggered()), this, SLOT(zoomOut()) ); zoomFitAction = new QAction( tr( "Zoom to Fit" ), this ); connect( zoomFitAction, SIGNAL(triggered()), this, SLOT(zoomFit()) ); ui->ganttView->leftView()->setContextMenuPolicy( Qt::CustomContextMenu ); ui->ganttView->leftView()->addAction( newEntryAction ); ui->ganttView->leftView()->addAction( removeEntryAction ); QMenu* entryMenu = menuBar()->addMenu( tr( "Entry" ) ); entryMenu->addAction( newEntryAction ); entryMenu->addAction( removeEntryAction ); QMenu* zoomMenu = menuBar()->addMenu( tr( "Zoom" ) ); zoomMenu->addAction( zoomInAction ); zoomMenu->addAction( zoomOutAction ); zoomMenu->addAction( zoomFitAction ); QMenu* scaleMenu = menuBar()->addMenu( tr( "Scale" ) ); scaleMenu->addAction( tr( "Auto" ), this, SLOT(scaleAuto()) ); scaleMenu->addAction( tr( "Hour" ), this, SLOT(scaleHour()) ); scaleMenu->addAction( tr( "Day" ), this, SLOT(scaleDay()) ); scaleMenu->addAction( tr( "Week" ), this, SLOT(scaleWeek()) ); scaleMenu->addAction( tr( "Month" ), this, SLOT(scaleMonth()) ); enableActions( QItemSelection() ); } void MainWindow::initItemDelegate() { EntryDelegate* delegate = new EntryDelegate( constraintModel, this ); ui->ganttView->leftView()->setItemDelegate( delegate ); } void MainWindow::initGrid() { grid = new KGantt::DateTimeGrid(); grid->setDayWidth( 70 ); ui->ganttView->setGrid( grid ); } void MainWindow::showContextMenu( const QPoint& pos ) { if ( !ui->ganttView->leftView()->indexAt( pos ).isValid() ) ui->ganttView->selectionModel()->clearSelection(); QMenu menu( ui->ganttView->leftView() ); menu.addAction( newEntryAction ); menu.addAction( removeEntryAction ); menu.exec( ui->ganttView->leftView()->viewport()->mapToGlobal( pos ) ); } void MainWindow::enableActions(const QItemSelection & selected) { if ( selected.indexes().isEmpty() ) { newEntryAction->setEnabled( true ); removeEntryAction->setEnabled( false ); return; } QModelIndex selectedIndex = selected.indexes()[0]; if ( model->data( model->index( selectedIndex.row(), 1 ) ) == KGantt::TypeEvent || model->data( model->index( selectedIndex.row(), 1 ) ) == KGantt::TypeTask ) { newEntryAction->setEnabled( false ); removeEntryAction->setEnabled( true ); return; } newEntryAction->setEnabled( true ); removeEntryAction->setEnabled( true ); } void MainWindow::addNewEntry() { QPointer dialog = new EntryDialog( model ); dialog->setWindowTitle( tr( "New Entry") ); if ( dialog->exec() == QDialog::Rejected || !dialog ) { delete dialog; return; } QModelIndexList selectedIndexes = ui->ganttView->selectionModel()->selectedIndexes(); const QModelIndex parent = selectedIndexes.value( 0 ); if ( !model->insertRow( model->rowCount( parent ), parent ) ) return; int row = model->rowCount( parent ) - 1; if ( row == 0 && parent.isValid() ) model->insertColumns( model->columnCount( parent ), 5, parent ); model->setData( model->index( row, 0, parent ), dialog->name() ); model->setData( model->index( row, 1, parent ), dialog->type() ); if ( dialog->type() != KGantt::TypeSummary ) { model->setData( model->index( row, 2, parent ), dialog->startDate(), KGantt::StartTimeRole ); model->setData( model->index( row, 3, parent ), dialog->endDate(), KGantt::EndTimeRole ); } model->setData( model->index( row, 4, parent ), dialog->completion() ); const QString legend( dialog->legend() ); if ( ! legend.isEmpty() ) model->setData( model->index( row, 5, parent ), legend ); addConstraint( dialog->depends(), model->index( row, 0, parent ) ); setReadOnly( model->index( row, 0, parent ), dialog->readOnly() ); delete dialog; } void MainWindow::setReadOnly(const QModelIndex & index, bool readOnly) { int row = index.row(); const QModelIndex parent = index.parent(); QStandardItem* item; item = model->itemFromIndex( model->index( row, 0, parent ) ); item->setFlags( readOnly ? item->flags() & ~Qt::ItemIsEditable : item->flags() | Qt::ItemIsEditable ); item = model->itemFromIndex( model->index( row, 1, parent ) ); item->setFlags( readOnly ? item->flags() & ~Qt::ItemIsEditable : item->flags() | Qt::ItemIsEditable ); item = model->itemFromIndex( model->index( row, 2, parent ) ); item->setFlags( readOnly ? item->flags() & ~Qt::ItemIsEditable : item->flags() | Qt::ItemIsEditable ); item = model->itemFromIndex( model->index( row, 3, parent ) ); item->setFlags( readOnly ? item->flags() & ~Qt::ItemIsEditable : item->flags() | Qt::ItemIsEditable ); item = model->itemFromIndex( model->index( row, 4, parent ) ); item->setFlags( readOnly ? item->flags() & ~Qt::ItemIsEditable : item->flags() | Qt::ItemIsEditable ); } void MainWindow::addConstraint(const QModelIndex & index1, const QModelIndex & index2) { if ( !index1.isValid() || !index2.isValid() ) return; KGantt::Constraint c( index1, index2 ); ui->ganttView->constraintModel()->addConstraint( c ); } void MainWindow::removeEntry() { QModelIndexList selectedIndexes = ui->ganttView->selectionModel()->selectedIndexes(); QModelIndex index = selectedIndexes.value( 0 ); if ( !index.isValid() ) return; model->removeRow( index.row(), index.parent() ); } void MainWindow::zoomIn() { qreal dayWidth = grid->dayWidth() + 10; if ( dayWidth > 400 ) grid->setScale( KGantt::DateTimeGrid::ScaleHour ); grid->setDayWidth( dayWidth ); } void MainWindow::zoomOut() { qreal dayWidth = grid->dayWidth() - 10; if ( dayWidth < 10 ) dayWidth = 10; if ( dayWidth <= 400 ) grid->setScale( KGantt::DateTimeGrid::ScaleDay ); grid->setDayWidth( dayWidth ); } void MainWindow::zoomFit() { QModelIndexList selectedIndexes = ui->ganttView->selectionModel()->selectedIndexes(); if ( selectedIndexes.isEmpty() ) { return; } KGantt::Span span; Q_FOREACH( QModelIndex idx, selectedIndexes ) { const KGantt::Span s = grid->mapToChart( grid->model()->index( idx.row(), 0 ) ); if ( span.isValid() ) { span = span.expandedTo( s ); } else { span = s; } } span.setLength( span.length()+20 ); span.setStart( span.start()-10 ); qDebug() << selectedIndexes << span; const qreal view_width = ui->ganttView->graphicsView()->viewport()->width(); const QDateTime start = grid->mapFromChart( span.start() ).value(); const QDateTime end = grid->mapFromChart( span.end() ).value(); qreal delta = start.date().daysTo(end.date()); delta += start.time().msecsTo(end.time())/( 1000.*24.*60.*60. ); qDebug() << view_width << "/" << delta; grid->setDayWidth( view_width/( std::max( 1., delta ) ) ); qDebug() << "daywidth set to" << grid->dayWidth(); qDebug() << "start scroll to" << grid->mapToChart( start ); ui->ganttView->graphicsView()->horizontalScrollBar()->setValue( grid->mapToChart( start ) ); } void MainWindow::scaleAuto() { KGantt::DateTimeGrid* grid = static_cast(ui->ganttView->grid()); grid->setScale( KGantt::DateTimeGrid::ScaleAuto ); } void MainWindow::scaleHour() { KGantt::DateTimeGrid* grid = static_cast(ui->ganttView->grid()); grid->setScale( KGantt::DateTimeGrid::ScaleHour ); } void MainWindow::scaleDay() { KGantt::DateTimeGrid* grid = static_cast(ui->ganttView->grid()); grid->setScale( KGantt::DateTimeGrid::ScaleDay ); } void MainWindow::scaleWeek() { KGantt::DateTimeGrid* grid = static_cast(ui->ganttView->grid()); grid->setScale( KGantt::DateTimeGrid::ScaleWeek ); } void MainWindow::scaleMonth() { KGantt::DateTimeGrid* grid = static_cast(ui->ganttView->grid()); grid->setScale( KGantt::DateTimeGrid::ScaleMonth ); } diff --git a/examples/Gantt/legend_example/mainwindow.h b/examples/Gantt/legend_example/mainwindow.h index b305095..37b1f5c 100644 --- a/examples/Gantt/legend_example/mainwindow.h +++ b/examples/Gantt/legend_example/mainwindow.h @@ -1,86 +1,86 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include QT_BEGIN_NAMESPACE class QStandardItemModel; class QCloseEvent; namespace Ui { class MainWindow; } QT_END_NAMESPACE namespace KGantt { class ConstraintModel; class DateTimeGrid; class Legend; } class MainWindow : public QMainWindow { Q_OBJECT public: - explicit MainWindow( QWidget * parent = 0, Qt::WindowFlags flags = Qt::WindowFlags() ); + explicit MainWindow( QWidget * parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags() ); virtual ~MainWindow(); void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; private slots: void addNewEntry(); void removeEntry(); void showContextMenu( const QPoint& ); void enableActions( const QItemSelection& selected ); void zoomIn(); void zoomOut(); void zoomFit(); void scaleAuto(); void scaleHour(); void scaleDay(); void scaleWeek(); void scaleMonth(); private: void initModel(); void initActions(); void initItemDelegate(); void initGrid(); void setReadOnly( const QModelIndex& index, bool readOnly ); void addConstraint( const QModelIndex& index1, const QModelIndex& index2 ); QStandardItemModel* model; KGantt::ConstraintModel* constraintModel; KGantt::DateTimeGrid* grid; KGantt::Legend* smallLegend; KGantt::Legend* detailedLegend; QAction* newEntryAction; QAction* removeEntryAction; QAction* zoomInAction; QAction* zoomOutAction; QAction* zoomFitAction; Ui::MainWindow* ui; }; #endif /* MAINWINDOW_H */ diff --git a/examples/Gantt/project/mainwindow.cpp b/examples/Gantt/project/mainwindow.cpp index d408fc4..3038082 100644 --- a/examples/Gantt/project/mainwindow.cpp +++ b/examples/Gantt/project/mainwindow.cpp @@ -1,478 +1,478 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mainwindow.h" #include "projectmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class ItemTypeComboBox : public QComboBox { Q_OBJECT Q_PROPERTY( KGantt::ItemType itemType READ itemType WRITE setItemType ) public: - explicit ItemTypeComboBox( QWidget* parent=0 ); + explicit ItemTypeComboBox( QWidget* parent = nullptr ); KGantt::ItemType itemType() const; public slots: void setItemType( KGantt::ItemType typ ); }; ItemTypeComboBox::ItemTypeComboBox( QWidget* parent ) : QComboBox( parent ) { addItem( tr( "Task" ), QVariant( KGantt::TypeTask ) ); addItem( tr( "Event" ), QVariant( KGantt::TypeEvent ) ); addItem( tr( "Summary" ), QVariant( KGantt::TypeSummary ) ); } KGantt::ItemType ItemTypeComboBox::itemType() const { return static_cast( itemData( currentIndex() ).toInt() ); } void ItemTypeComboBox::setItemType( KGantt::ItemType typ ) { setCurrentIndex( typ-1 ); } class MyItemDelegate : public KGantt::ItemDelegate { public: - explicit MyItemDelegate( QObject* parent=0 ); + explicit MyItemDelegate( QObject* parent = nullptr ); /*reimp*/ QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& idx ) const Q_DECL_OVERRIDE; /*reimp*/ void setEditorData( QWidget* editor, const QModelIndex& index ) const Q_DECL_OVERRIDE; /*reimp*/ void setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex & index ) const Q_DECL_OVERRIDE; protected: /*reimp*/void drawDisplay( QPainter* painter, const QStyleOptionViewItem & option, const QRect& rect, const QString& text ) const Q_DECL_OVERRIDE; }; MyItemDelegate::MyItemDelegate( QObject* parent ) : KGantt::ItemDelegate( parent ) { } QWidget* MyItemDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& idx ) const { qDebug() << "MyItemDelegate::createEditor("<(editor)) && index.isValid() ) { c->setItemType(static_cast(index.data(Qt::EditRole).toInt())); } else { ItemDelegate::setEditorData(editor,index); } } void MyItemDelegate::setModelData ( QWidget* editor, QAbstractItemModel* model, const QModelIndex & index ) const { ItemTypeComboBox* c; if ( (c = qobject_cast(editor)) && index.isValid() ) { model->setData(index,c->itemType()); } else { ItemDelegate::setModelData(editor,model,index); } } void MyItemDelegate::drawDisplay( QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, const QString& text ) const { //qDebug() << "MyItemDelegate::drawDisplay(" <(text.toInt()); QString str; switch (typ) { case KGantt::TypeTask: str = tr("Task"); break; case KGantt::TypeEvent: str = tr("Event"); break; case KGantt::TypeSummary: str = tr("Summary"); break; default: str = tr("None"); break; } ItemDelegate::drawDisplay(painter,option,rect,str); } /////////////////////////////////////////////////////////////////////////////// // Provide custom background and foreground /////////////////////////////////////////////////////////////////////////////// class DateTimeGrid : public KGantt::DateTimeGrid { public: - DateTimeGrid(QObject* parent=0) { + DateTimeGrid(QObject* parent = nullptr) { setParent(parent); setFreeDays( QSet() ); setFreeDaysBrush( QBrush( Qt::NoBrush ) ); } ~DateTimeGrid() { } //virtual void paintUserDefinedHeader(QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, const KGantt::DateTimeScaleFormatter* formatter, QWidget* widget = 0); void drawBackground(QPainter* painter, const QRectF& rect) Q_DECL_OVERRIDE; void drawForeground(QPainter* painter, const QRectF& rect) Q_DECL_OVERRIDE; }; void DateTimeGrid::drawBackground(QPainter* painter, const QRectF& rect) { QLinearGradient grad; grad.setCoordinateMode( QGradient::ObjectBoundingMode ); grad.setStart( 0.5, 0.5 ); grad.setFinalStop( 0.5, 0.0 ); grad.setSpread( QGradient::ReflectSpread ); // grad.setCenter( 0.5, 0.5 ); // grad.setFocalPoint( 0.5, 0.5 ); // grad.setRadius( 0.5 ); QColor currentColor = Qt::blue; for ( qreal i = 0; i <= 1.0; i += 0.1 ) { currentColor = currentColor.lighter( 100 + 20 * i ); grad.setColorAt( i, currentColor ); } QBrush brush( grad); //brush.setColor(Qt::lightGray); QRectF r = computeRect(QDateTime::currentDateTime(), QDateTime::currentDateTime().addDays(2), rect); painter->fillRect(r, brush); } void DateTimeGrid::drawForeground(QPainter* painter, const QRectF& rect) { painter->save(); QRectF r = computeRect(QDateTime::currentDateTime(), QDateTime::currentDateTime().addDays(2), rect); static QString text("Holiday"); QFont font = painter->font(); font.setPixelSize(r.width()/5); QFontMetrics fm(font); int width = fm.width(text); int height = fm.boundingRect(text).height(); painter->translate(r.center()); painter->translate(-width/2, height/2); painter->setFont(font); painter->drawText(0, 0, text); painter->restore(); } /* void DateTimeGrid::paintUserDefinedHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, const KGantt::DateTimeScaleFormatter* formatter, QWidget* widget) { const QStyle* const style = widget ? widget->style() : QApplication::style(); QDateTime dt = formatter->currentRangeBegin( mapToDateTime( offset + exposedRect.left() ) ).toUTC(); qreal x = mapFromDateTime( dt ); while ( x < exposedRect.right() + offset ) { const QDateTime next = formatter->nextRangeBegin( dt ); const qreal nextx = mapFromDateTime( next ); QStyleOptionHeader opt; if ( widget ) opt.init( widget ); opt.rect = QRectF( x - offset+1, headerRect.top(), qMax( 1., nextx-x-1 ), headerRect.height() ).toAlignedRect(); //opt.state = QStyle::State_Raised | QStyle::State_Enabled; opt.textAlignment = formatter->alignment(); opt.text = formatter->text( dt ); // use white text on black background opt.palette.setColor(QPalette::Window, QColor("black")); opt.palette.setColor(QPalette::ButtonText, QColor("white")); style->drawControl( QStyle::CE_Header, &opt, painter, widget ); dt = next; x = nextx; } } */ MainWindow::MainWindow( QWidget* parent ) : QMainWindow( parent ), m_model( new ProjectModel( this ) ), m_view( new KGantt::View ) { m_view->setModel( m_model ); m_view->setSelectionModel( new QItemSelectionModel(m_model)); // slotToolsNewItem(); m_view->leftView()->setItemDelegateForColumn( 1, new MyItemDelegate( this ) ); m_view->leftView()->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded ); m_view->graphicsView()->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded ); m_view->setGrid(new DateTimeGrid(this)); //QItemEditorCreatorBase *creator = new QItemEditorCreator("itemType"); //QItemEditorFactory* factory = new QItemEditorFactory; //factory->registerEditor( QVariant( KGantt::TypeTask ).type(), creator ); //m_view->itemDelegate()->setItemEditorFactory( factory ); setCentralWidget( m_view ); QMenuBar* mb = menuBar(); QMenu* fileMenu = new QMenu( tr( "&File" ) ); #ifndef QT_NO_PRINTER fileMenu->addAction( tr( "&Save as PDF..." ), this, SLOT(slotFileSavePdf()) ); fileMenu->addAction( tr( "&Print..." ), this, SLOT(slotFilePrint()) ); #endif fileMenu->addSeparator(); fileMenu->addAction( tr( "&Quit" ), this, SLOT(slotFileQuit()) ); mb->addMenu( fileMenu ); QMenu* toolsMenu = new QMenu( tr( "&Tools" ) ); toolsMenu->addAction( tr( "&New Item" ), this, SLOT(slotToolsNewItem()) ); toolsMenu->addAction( tr( "&Add Item" ), this, SLOT(slotToolsAppendItem()) ); toolsMenu->addSeparator(); QMenu *alignMenu = toolsMenu->addMenu( tr( "Ali&gn" ) ); alignMenu->addAction( tr( "&Left" ), this, SLOT(slotAlignLeft()) ); alignMenu->addAction( tr( "&Center" ), this, SLOT(slotAlignCenter()) ); alignMenu->addAction( tr( "&Right" ), this, SLOT(slotAlignRight()) ); alignMenu->addAction( tr( "&Hidden" ), this, SLOT(slotAlignHidden()) ); toolsMenu->addSeparator(); toolsMenu->addAction( tr( "&Collapse All" ), this, SLOT(slotCollapseAll()) ); toolsMenu->addAction( tr( "&Expand All" ), this, SLOT(slotExpandAll()) ); mb->addMenu( toolsMenu ); /* slotToolsNewItem(); slotToolsNewItem(); slotToolsNewItem(); for (int i = 0; i < 3; ++i) { m_model->setData(m_model->index(i,2,QModelIndex()), qVariantFromValue(QDateTime::currentDateTime().addDays(i)), KGantt::StartTimeRole); m_model->setData(m_model->index(i,3,QModelIndex()), qVariantFromValue(QDateTime::currentDateTime().addDays(i+1)), KGantt::EndTimeRole); } m_view->setConstraintModel(new KGantt::ConstraintModel(m_view)); m_view->constraintModel()->addConstraint(KGantt::Constraint(m_model->index(0,0,QModelIndex()),m_model->index(1,0,QModelIndex()))); m_view->constraintModel()->addConstraint(KGantt::Constraint(m_model->index(1,0,QModelIndex()),m_model->index(2,0,QModelIndex()))); */ } SavePdfDialog::SavePdfDialog(QWidget *parent) : QDialog(parent) { setModal(true); setWindowTitle(tr("Save as PDF")); QVBoxLayout *l = new QVBoxLayout(this); setLayout(l); QHBoxLayout *fileLayout = new QHBoxLayout(this); l->addLayout(fileLayout); QLabel *fileLabel = new QLabel(tr("File:"), this); fileLayout->addWidget(fileLabel); m_fileEdit = new QLineEdit(this); fileLabel->setBuddy(m_fileEdit); m_fileEdit->setText(QFileInfo(QDir::homePath(), "gantt.pdf").absoluteFilePath()); fileLayout->addWidget(m_fileEdit); QPushButton *m_fileButton = new QPushButton("...", this); connect(m_fileButton, SIGNAL(clicked()), this, SLOT(fileButtonClicked())); fileLayout->addWidget(m_fileButton); m_rowLabels = new QCheckBox(tr("Row Header"), this); m_rowLabels->setChecked(true); l->addWidget(m_rowLabels); m_columnLabels = new QCheckBox(tr("Column Header"), this); m_columnLabels->setChecked(true); l->addWidget(m_columnLabels); QDialogButtonBox *btnBox = new QDialogButtonBox(this); btnBox->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel); connect(btnBox, SIGNAL(accepted()), this, SLOT(accept())); connect(btnBox, SIGNAL(rejected()), this, SLOT(reject())); l->addWidget(btnBox); resize(QSize(400, 100).expandedTo(minimumSizeHint())); } void SavePdfDialog::fileButtonClicked() { const QString file = QFileDialog::getSaveFileName(this, tr("Choose PDF File..."), QString(), tr("PDF files (*.pdf)")); if (!file.isEmpty()) m_fileEdit->setText(file); } void MainWindow::slotFileSavePdf() { #ifndef QT_NO_PRINTER SavePdfDialog dialog(this); if (dialog.exec() != QDialog::Accepted) return; const QString file = dialog.m_fileEdit->text(); if (file.isEmpty()) return; const bool drawRowLabels = dialog.m_rowLabels->isChecked(); const bool drawColumnLabels = dialog.m_columnLabels->isChecked(); QPrinter printer(QPrinter::HighResolution); printer.setOrientation(QPrinter::Landscape); printer.setColorMode(QPrinter::Color); printer.setPageMargins(0.2, 0.2, 0.2, 0.2, QPrinter::Point); printer.setOutputFormat(QPrinter::PdfFormat); printer.setOutputFileName(file); m_view->print(&printer, drawRowLabels, drawColumnLabels); #endif } void MainWindow::slotFilePrint() { #ifndef QT_NO_PRINTER QPrinter printer(QPrinter::HighResolution); printer.setOrientation(QPrinter::Landscape); printer.setColorMode(QPrinter::Color); QPrintDialog dialog(&printer, this); if (dialog.exec() != QDialog::Accepted) return; m_view->print(&printer); #endif } void MainWindow::slotFileQuit() { // TODO QApplication::instance()->quit(); } void MainWindow::slotToolsNewItem() { QModelIndex idx = m_view->selectionModel()->currentIndex(); if ( idx.isValid() ) { qDebug() << "MainWindow::slotToolsNewItem" << idx; m_model->insertRows( 0, 1, m_model->index( idx.row(),0,idx.parent() ) ); } else { m_model->insertRows( 0, 1, m_view->rootIndex() ); } } void MainWindow::slotToolsAppendItem() { QModelIndex idx = m_view->selectionModel()->currentIndex(); if ( idx.isValid() ) { qDebug() << "MainWindow::slotToolsAppendItem" << idx; m_model->insertRows( m_model->rowCount( idx ), 1, m_model->index( idx.row(),0,idx.parent() ) ); } else { m_model->insertRows( m_model->rowCount( m_view->rootIndex() ), 1, m_view->rootIndex() ); } } void MainWindow::slotCollapseAll() { // don't use the treeview's collapseAll/expandAll methods but use the one provided by the // view cause that one will take care to update everyt6hing as needed. //QTreeView* view = qobject_cast( m_view->leftView() ); //view->collapseAll(); QModelIndex idx = m_view->selectionModel()->currentIndex(); if ( idx.isValid() ) m_view->collapseAll(); } void MainWindow::slotExpandAll() { // don't use the treeview's collapseAll/expandAll methods but use the one provided by the // view cause that one will take care to update everyt6hing as needed. //QTreeView* view = qobject_cast( m_view->leftView() ); //view->expandAll(); QModelIndex idx = m_view->selectionModel()->currentIndex(); if ( idx.isValid() ) m_view->expandAll(); } void MainWindow::slotAlignLeft() { QModelIndex idx = m_view->selectionModel()->currentIndex(); if ( idx.isValid() ) { m_model->setData( idx, KGantt::StyleOptionGanttItem::Left, KGantt::TextPositionRole ); } } void MainWindow::slotAlignCenter() { QModelIndex idx = m_view->selectionModel()->currentIndex(); if ( idx.isValid() ) { m_model->setData( idx, KGantt::StyleOptionGanttItem::Center, KGantt::TextPositionRole ); } } void MainWindow::slotAlignRight() { QModelIndex idx = m_view->selectionModel()->currentIndex(); if ( idx.isValid() ) { m_model->setData( idx, KGantt::StyleOptionGanttItem::Right, KGantt::TextPositionRole ); } } void MainWindow::slotAlignHidden() { QModelIndex idx = m_view->selectionModel()->currentIndex(); if ( idx.isValid() ) { m_model->setData( idx, KGantt::StyleOptionGanttItem::Hidden, KGantt::TextPositionRole ); } } #include "mainwindow.moc" diff --git a/examples/Gantt/project/mainwindow.h b/examples/Gantt/project/mainwindow.h index e246ea7..e74a909 100644 --- a/examples/Gantt/project/mainwindow.h +++ b/examples/Gantt/project/mainwindow.h @@ -1,71 +1,71 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include class QLineEdit; class QCheckBox; namespace KGantt { class View; } class ProjectModel; class SavePdfDialog : public QDialog { Q_OBJECT public: QLineEdit *m_fileEdit; QCheckBox *m_rowLabels; QCheckBox *m_columnLabels; - SavePdfDialog(QWidget *parent = 0); + SavePdfDialog(QWidget *parent = nullptr); private slots: void fileButtonClicked(); }; class MainWindow : public QMainWindow { Q_OBJECT public: - explicit MainWindow( QWidget* parent = 0 ); + explicit MainWindow( QWidget* parent = nullptr ); private slots: void slotFileSavePdf(); void slotFilePrint(); void slotFileQuit(); void slotToolsNewItem(); void slotToolsAppendItem(); void slotCollapseAll(); void slotExpandAll(); void slotAlignLeft(); void slotAlignCenter(); void slotAlignRight(); void slotAlignHidden(); private: ProjectModel* m_model; KGantt::View* m_view; }; #endif /* MAINWINDOW_H */ diff --git a/examples/Gantt/project/projectmodel.cpp b/examples/Gantt/project/projectmodel.cpp index eb892ba..3f9ac4c 100644 --- a/examples/Gantt/project/projectmodel.cpp +++ b/examples/Gantt/project/projectmodel.cpp @@ -1,357 +1,357 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "projectmodel.h" #include #include #include #include #include #include #include typedef QAbstractItemModel BASE; class ProjectModel::Node { public: - explicit Node( Node* parent = 0 ); + explicit Node( Node* parent = nullptr ); virtual ~Node(); void addChild( Node* ); void insertChild( int i, Node* ); void removeChild( Node* ); Node* parent() const { return m_parent; } int childCount() const { return m_children.count(); } int childNumber( Node* n ) const { return m_children.indexOf( n ); } Node* child( int i ) const { return m_children.at( i ); } void setParent( Node* p ) { m_parent = p; } void setStart( const QDateTime& dt ) { m_bStart = m_start; m_start = dt; } void setEnd( const QDateTime& dt ) { if ( m_end.isValid() ) m_bEnd = m_end; m_end = dt; } void setLabel( const QString& str ) { m_label = str; } void setType( int t ) { m_type = static_cast( t ); if ( !m_start.isValid() ) m_start = m_bStart; if ( !m_end.isValid() ) m_end = m_bEnd; } void setCompletion( int c ) { m_completion = c; } void setPosition( KGantt::StyleOptionGanttItem::Position p ) { m_position = p; } QDateTime start() const { return m_start; } QDateTime end() const { return m_end; } QString label() const { return m_label; } KGantt::ItemType type() const { return m_type; } int completion() const { return m_completion; } KGantt::StyleOptionGanttItem::Position position() const { return m_position; } private: Node* m_parent; QList m_children; KGantt::ItemType m_type; QDateTime m_start, m_end; QDateTime m_bStart, m_bEnd; QString m_label; int m_completion; KGantt::StyleOptionGanttItem::Position m_position; }; static int unnamed_count = 0; ProjectModel::Node::Node( Node* parent ) : m_parent( parent ), m_type( KGantt::TypeTask ), m_start( QDateTime::currentDateTime() ), m_end( QDateTime::currentDateTime().addDays( 1 ) ), m_label( tr( "Unnamed task %1" ).arg( ++unnamed_count ) ), m_completion( -1 ), m_position( KGantt::StyleOptionGanttItem::Right ) { if ( m_parent ) m_parent->addChild( this ); } ProjectModel::Node::~Node() { while ( !m_children.isEmpty() ) delete m_children.front(); if ( m_parent ) m_parent->removeChild( this ); } void ProjectModel::Node::addChild( Node* child ) { child->setParent( this ); m_children.push_back( child ); } void ProjectModel::Node::insertChild( int i, Node* child ) { child->setParent( this ); m_children.insert( i, child ); } void ProjectModel::Node::removeChild( Node* child ) { - child->setParent( 0 ); + child->setParent( nullptr ); m_children.removeAll( child ); } ProjectModel::ProjectModel( QObject* parent ) : QAbstractItemModel( parent ), m_root( new Node ) { } ProjectModel::~ProjectModel() { delete m_root; } bool ProjectModel::load( const QString& filename ) { Q_UNUSED(filename) // TODO: read data delete m_root; m_root = new Node; return true; } bool ProjectModel::save( const QString& filename ) { Q_UNUSED(filename); return true; } int ProjectModel::rowCount( const QModelIndex& idx ) const { if ( idx.isValid() ) return static_cast( idx.internalPointer() )->childCount(); else return m_root->childCount(); } int ProjectModel::columnCount( const QModelIndex& idx ) const { Q_UNUSED(idx); return 5; } QModelIndex ProjectModel::index( int row, int col, const QModelIndex& parent ) const { Node* p = m_root; if ( parent.isValid() ) { p = static_cast( parent.internalPointer() ); } if ( row < 0 || row >= p->childCount() ) return QModelIndex(); else return createIndex( row, col, p->child( row ) ); } QModelIndex ProjectModel::parent( const QModelIndex& idx ) const { if ( !idx.isValid() ) return QModelIndex(); Node* n = static_cast( idx.internalPointer() ); assert( n ); Node* p = n->parent(); if ( p==m_root )return QModelIndex(); Node* pp = p->parent(); assert( pp ); return createIndex( pp->childNumber( p ), 0, p ); } QVariant ProjectModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation != Qt::Horizontal || role != Qt::DisplayRole ) return QVariant(); switch ( section ) { case 0: return tr( "Name" ); case 1: return tr( "Type" ); case 2: return tr( "Start" ); case 3: return tr( "End" ); case 4: return tr( "Completion %" ); default: return QVariant(); } } QVariant ProjectModel::data( const QModelIndex& idx, int role ) const { if ( !idx.isValid() ) return QVariant(); Node* n = static_cast( idx.internalPointer() ); assert( n ); if ( idx.column() == 0 ) { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return n->label(); case KGantt::TextPositionRole: return n->position(); } } else if ( idx.column() == 1 ) { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return qVariantFromValue( n->type() ); } } else if ( idx.column() == 2 ) { switch ( role ) { case Qt::DisplayRole: return n->start().date().toString("dd-MM-yyyy"); case Qt::EditRole: case KGantt::StartTimeRole: return n->start(); } } else if ( idx.column() == 3 ) { switch ( role ) { case Qt::DisplayRole: return n->end().date().toString("dd-MM-yyyy"); case Qt::EditRole: case KGantt::EndTimeRole: return n->end(); } } else if ( idx.column() == 4 && n->completion() >= 0 ) { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return n->completion(); } } else if ( idx.column() == 5 ) { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return n->label(); case KGantt::ItemTypeRole: return n->type(); case KGantt::StartTimeRole: return n->start(); case KGantt::EndTimeRole: return n->end(); case KGantt::TaskCompletionRole: if ( n->completion() >= 0 ) return n->completion(); break; } } return QVariant(); } bool ProjectModel::setData( const QModelIndex& idx, const QVariant& value, int role ) { if ( !idx.isValid() ) return false; qDebug() << "ProjectModel::setData" << idx.column() << role << value; Node* n = static_cast( idx.internalPointer() ); assert( n ); if ( idx.column() == 0 ) { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: n->setLabel( value.toString() ); emit dataChanged( idx, idx ); break; case KGantt::TextPositionRole: n->setPosition( static_cast(value.toInt()) ); emit dataChanged( idx, idx ); break; } } else if ( idx.column() == 1 ) { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: n->setType( value.toInt() ); emit dataChanged( idx, idx ); break; } } else if ( idx.column() == 2 ) { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: case KGantt::StartTimeRole: n->setStart(value.toDateTime()); emit dataChanged( idx, idx ); break; } } else if ( idx.column() == 3 ) { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: case KGantt::EndTimeRole: n->setEnd(value.toDateTime()); emit dataChanged( idx, idx ); break; } } else if ( idx.column() == 4 ) { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: n->setCompletion(value.toInt()); emit dataChanged( idx, idx ); break; } } return true; } bool ProjectModel::insertRows( int row, int count, const QModelIndex& parent ) { beginInsertRows( parent, row, row+count-1 ); Node* p = m_root; if ( parent.isValid() ) p = static_cast( parent.internalPointer() ); assert( p ); for ( int i = 0; i < count; ++i ) { Node* n = new Node; p->insertChild( row+i, n ); } endInsertRows(); return true; } Qt::ItemFlags ProjectModel::flags( const QModelIndex& idx ) const { return BASE::flags( idx ) | Qt::ItemIsEditable; } diff --git a/examples/Gantt/project/projectmodel.h b/examples/Gantt/project/projectmodel.h index 29b3660..8d18b82 100644 --- a/examples/Gantt/project/projectmodel.h +++ b/examples/Gantt/project/projectmodel.h @@ -1,56 +1,56 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PROJECTMODEL_H #define PROJECTMODEL_H #include class ProjectModel : public QAbstractItemModel { Q_OBJECT public: - explicit ProjectModel( QObject* parent = 0 ); + explicit ProjectModel( QObject* parent = nullptr ); virtual ~ProjectModel(); /*reimp*/ int rowCount( const QModelIndex& idx ) const Q_DECL_OVERRIDE; /*reimp*/ int columnCount( const QModelIndex& idx ) const Q_DECL_OVERRIDE; /*reimp*/ QModelIndex index( int row, int col, const QModelIndex& parent = QModelIndex() ) const Q_DECL_OVERRIDE; /*reimp*/ QModelIndex parent( const QModelIndex& idx ) const Q_DECL_OVERRIDE; /*reimp*/QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const Q_DECL_OVERRIDE; /*reimp*/ QVariant data( const QModelIndex& idx, int role = Qt::DisplayRole ) const Q_DECL_OVERRIDE; /*reimp*/ bool setData( const QModelIndex& idx, const QVariant& value, int role = Qt::DisplayRole ) Q_DECL_OVERRIDE; /*reimp*/ bool insertRows( int row, int count, const QModelIndex& parent = QModelIndex() ) Q_DECL_OVERRIDE; /*reimp*/ Qt::ItemFlags flags( const QModelIndex& ) const Q_DECL_OVERRIDE; bool load( const QString& filename ); bool save( const QString& filename ); private: class Node; Node* m_root; }; #endif /* PROJECTMODEL_H */ diff --git a/examples/Grids/CartesianGrid/main.cpp b/examples/Grids/CartesianGrid/main.cpp index 45f4230..8626caa 100644 --- a/examples/Grids/CartesianGrid/main.cpp +++ b/examples/Grids/CartesianGrid/main.cpp @@ -1,188 +1,188 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KChart; class ChartWidget : public QWidget { Q_OBJECT public: - explicit ChartWidget(QWidget* parent=0) + explicit ChartWidget(QWidget* parent = nullptr) : QWidget(parent) { m_model.insertRows( 0, 6, QModelIndex() ); m_model.insertColumns( 0, 4, QModelIndex() ); for (int row = 0; row < 6; ++row) { for (int column = 0; column < 4; ++column) { QModelIndex index = m_model.index(row, column, QModelIndex()); m_model.setData(index, QVariant(row*0.5 + column) ); } } LineDiagram* diagram = new LineDiagram; diagram->setModel(&m_model); CartesianAxis *xAxis = new CartesianAxis( diagram ); CartesianAxis *yAxis = new CartesianAxis ( diagram ); xAxis->setPosition ( KChart::CartesianAxis::Bottom ); yAxis->setPosition ( KChart::CartesianAxis::Left ); diagram->addAxis( xAxis ); diagram->addAxis( yAxis ); m_chart.coordinatePlane()->replaceDiagram(diagram); /* Header */ // Add at one Header and set it up HeaderFooter* header = new HeaderFooter( &m_chart ); header->setPosition( Position::North ); header->setText( "A Line Chart with Grid Configured" ); m_chart.addHeaderFooter( header ); // Configure the Header text attributes TextAttributes hta; hta.setPen( QPen( Qt::red ) ); // let the header resize itself // together with the widget. // so-called relative size Measure m( 35.0 ); m.setRelativeMode( header->autoReferenceArea(), KChartEnums::MeasureOrientationMinimum ); hta.setFontSize( m ); // min font size m.setValue( 3.0 ); m.setCalculationMode( KChartEnums::MeasureCalculationModeAbsolute ); hta.setMinimalFontSize( m ); header->setTextAttributes( hta ); // Configure the Header's Background BackgroundAttributes hba; hba.setBrush( Qt::white ); hba.setVisible( true ); header->setBackgroundAttributes( hba ); // Configure the header Frame attributes FrameAttributes hfa; hfa.setPen( QPen ( QBrush( Qt::darkGray ), 2 ) ); hfa.setPadding( 2 ); hfa.setVisible( true ); header->setFrameAttributes( hfa ); // diagram->coordinatePlane returns an abstract plane one. // if we want to specify the orientation we need to cast // as follow CartesianCoordinatePlane* plane = static_cast ( diagram->coordinatePlane() ); /* Configure grid steps and pen */ // Vertical GridAttributes gv ( plane->gridAttributes( Qt::Vertical ) ); // Configure a grid pen // I know it is horrible // just for demo'ing QPen gridPen( Qt::gray ); gridPen.setWidth( 2 ); gv.setGridPen( gridPen ); // Configure a sub-grid pen QPen subGridPen( Qt::darkGray ); subGridPen.setStyle( Qt::DotLine ); gv.setSubGridPen( subGridPen ); // Display a blue zero line gv.setZeroLinePen( QPen( Qt::blue ) ); // change step and substep width // or any of those. gv.setGridStepWidth( 1.0 ); gv.setGridSubStepWidth( 0.5 ); gv.setGridVisible( true ); gv.setSubGridVisible( true ); // Horizontal GridAttributes gh = plane->gridAttributes( Qt::Horizontal ); gh.setGridPen( gridPen ); gh.setGridStepWidth( 0.5 ); gh.setSubGridPen( subGridPen ); gh.setGridSubStepWidth( 0.1 ); plane->setGridAttributes( Qt::Vertical, gv ); plane->setGridAttributes( Qt::Horizontal, gh ); // Data Values Display and position const int colCount = diagram->model()->columnCount(diagram->rootIndex()); for ( int iColumn = 0; iColumndataValueAttributes( iColumn ) ); RelativePosition pos ( a.position( true ) ); pos.setAlignment( Qt::AlignRight ); a.setPositivePosition( pos ); QBrush brush( diagram->brush( iColumn ) ); TextAttributes ta( a.textAttributes() ); ta.setRotation( 0 ); ta.setFont( QFont( "Comic", 10 ) ); ta.setPen( QPen( brush.color() ) ); ta.setVisible( true ); a.setVisible( true ); a.setTextAttributes( ta ); diagram->setDataValueAttributes( iColumn, a ); } QVBoxLayout* l = new QVBoxLayout(this); l->addWidget(&m_chart); m_chart.setGlobalLeadingRight( 20 ); setLayout(l); } private: Chart m_chart; QStandardItemModel m_model; QPixmap pixmap; }; int main( int argc, char** argv ) { QApplication app( argc, argv ); ChartWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/examples/Grids/PolarGrid/mainwindow.h b/examples/Grids/PolarGrid/mainwindow.h index 9afc193..d3bd7d1 100644 --- a/examples/Grids/PolarGrid/mainwindow.h +++ b/examples/Grids/PolarGrid/mainwindow.h @@ -1,74 +1,74 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include namespace KChart { class Chart; class DatasetProxyModel; class PolarDiagram; class PolarCoordinatePlane; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); private slots: // start position of circular grid void on_startPositionSB_valueChanged( double pos ); void on_startPositionSL_valueChanged( int pos ); // show grid void on_circularGridCB_toggled( bool toggle ); void on_sagittalGridCB_toggled( bool toggle ); /* planned for future release: // show axes void on_circularAxisCB_toggled( bool toggle ); void on_sagittalAxisCB_toggled( bool toggle ); */ private: void initKChartClasses(); void wireUpKChartClasses(); void setItemModelData(); // For a change we do not use our special TableModel here, // but we just use the standard model that comes with Qt. QStandardItemModel m_model; KChart::Chart* m_chart; KChart::PolarDiagram* m_diagram; KChart::PolarCoordinatePlane* m_polarPlane; int m_currentFactor; int m_currentDirection; int m_currentSlice; }; #endif /* MAINWINDOW_H */ diff --git a/examples/HeadersFooters/Advanced/mainwindow.h b/examples/HeadersFooters/Advanced/mainwindow.h index 4eed45b..24b0692 100644 --- a/examples/HeadersFooters/Advanced/mainwindow.h +++ b/examples/HeadersFooters/Advanced/mainwindow.h @@ -1,61 +1,61 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include #include "ui_mainwindow.h" #include "ui_addheaderdialog.h" #include namespace KChart { class Chart; class DatasetProxyModel; class LineDiagram; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); private slots: void on_addHeaderPB_clicked(); void on_editHeaderPB_clicked(); void on_removeHeaderPB_clicked(); void on_headersTV_itemSelectionChanged(); private: void setupAddHeaderDialog( QDialog* dlg, Ui::AddHeaderDialog& conf ) const; KChart::Chart* m_chart; TableModel m_model; KChart::DatasetProxyModel* m_datasetProxy; KChart::LineDiagram* m_lines; }; #endif /* MAINWINDOW_H */ diff --git a/examples/HeadersFooters/HeadersFootersParameters/main.cpp b/examples/HeadersFooters/HeadersFootersParameters/main.cpp index 6551843..f81bdd5 100644 --- a/examples/HeadersFooters/HeadersFootersParameters/main.cpp +++ b/examples/HeadersFooters/HeadersFootersParameters/main.cpp @@ -1,106 +1,106 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include using namespace KChart; class ChartWidget : public QWidget { Q_OBJECT public: - explicit ChartWidget(QWidget* parent=0) + explicit ChartWidget(QWidget* parent = nullptr) : QWidget(parent) { m_model.insertRows( 0, 2, QModelIndex() ); m_model.insertColumns( 0, 3, QModelIndex() ); for (int row = 0; row < 3; ++row) { for (int column = 0; column < 3; ++column) { QModelIndex index = m_model.index(row, column, QModelIndex()); m_model.setData(index, QVariant(row+1 * column) ); } } BarDiagram* diagram = new BarDiagram; diagram->setModel(&m_model); m_chart.coordinatePlane()->replaceDiagram(diagram); // Add at one Header and set it up HeaderFooter* header = new HeaderFooter( &m_chart ); header->setPosition( Position::North ); header->setText( "A Simple Bar Chart" ); m_chart.addHeaderFooter( header ); // Configure the Header text attributes TextAttributes hta; hta.setPen( QPen( Qt::blue ) ); // let the header resize itself // together with the widget. // so-called relative size Measure m( 35.0 ); m.setRelativeMode( header->autoReferenceArea(), KChartEnums::MeasureOrientationMinimum ); hta.setFontSize( m ); // min font size m.setValue( 3.0 ); m.setCalculationMode( KChartEnums::MeasureCalculationModeAbsolute ); hta.setMinimalFontSize( m ); header->setTextAttributes( hta ); // Configure the header Background attributes BackgroundAttributes hba; hba.setBrush( Qt::white ); hba.setVisible( true ); header->setBackgroundAttributes( hba ); // Configure the header Frame attributes FrameAttributes hfa; hfa.setPen( QPen ( QBrush( Qt::darkGray ), 2 ) ); hfa.setVisible( true ); header->setFrameAttributes( hfa ); QVBoxLayout* l = new QVBoxLayout(this); l->addWidget(&m_chart); m_chart.setGlobalLeadingTop( 10 ); setLayout(l); } private: Chart m_chart; QStandardItemModel m_model; }; int main( int argc, char** argv ) { QApplication app( argc, argv ); ChartWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/examples/Legends/LegendAdvanced/derivedaddlegenddialog.h b/examples/Legends/LegendAdvanced/derivedaddlegenddialog.h index b9a9c96..fd2c5d3 100644 --- a/examples/Legends/LegendAdvanced/derivedaddlegenddialog.h +++ b/examples/Legends/LegendAdvanced/derivedaddlegenddialog.h @@ -1,33 +1,33 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DERIVEDADDLEGENDDIALOG_H #define DERIVEDADDLEGENDDIALOG_H #include "ui_addlegenddialog.h" class DerivedAddLegendDialog : public QDialog, public Ui::AddLegendDialog { Q_OBJECT public: - DerivedAddLegendDialog( QWidget* parent = 0 ); + DerivedAddLegendDialog( QWidget* parent = nullptr ); }; #endif // DERIVEDADDLEGENDDIALOG_H diff --git a/examples/Legends/LegendAdvanced/mainwindow.h b/examples/Legends/LegendAdvanced/mainwindow.h index 9d2e0ae..8f509ff 100644 --- a/examples/Legends/LegendAdvanced/mainwindow.h +++ b/examples/Legends/LegendAdvanced/mainwindow.h @@ -1,60 +1,60 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include #include "ui_mainwindow.h" #include "derivedaddlegenddialog.h" #include namespace KChart { class Chart; class LineDiagram; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); private slots: void on_addLegendPB_clicked(); void on_editLegendPB_clicked(); void on_removeLegendPB_clicked(); void on_legendsTV_itemSelectionChanged(); private: void initAddLegendDialog( DerivedAddLegendDialog& conf, Qt::Alignment alignment ) const; KChart::Chart* m_chart; TableModel m_model; KChart::LineDiagram* m_lines; QMap alignmentMap; }; #endif /* MAINWINDOW_H */ diff --git a/examples/Legends/LegendParameters/main.cpp b/examples/Legends/LegendParameters/main.cpp index d4e5658..1021fba 100644 --- a/examples/Legends/LegendParameters/main.cpp +++ b/examples/Legends/LegendParameters/main.cpp @@ -1,132 +1,132 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include using namespace KChart; class ChartWidget : public QWidget { Q_OBJECT public: - explicit ChartWidget(QWidget* parent=0) + explicit ChartWidget(QWidget* parent = nullptr) : QWidget(parent) { m_model.insertRows( 0, 2, QModelIndex() ); m_model.insertColumns( 0, 3, QModelIndex() ); for (int row = 0; row < 3; ++row) { for (int column = 0; column < 3; ++column) { QModelIndex index = m_model.index(row, column, QModelIndex()); m_model.setData(index, QVariant(row+1 * column) ); } } BarDiagram* diagram = new BarDiagram; diagram->setModel(&m_model); m_chart.coordinatePlane()->replaceDiagram(diagram); // Add at one legend and set it up Legend* legend = new Legend( diagram, &m_chart ); legend->setPosition( Position::North ); legend->setAlignment( Qt::AlignCenter ); legend->setShowLines( true ); legend->setSpacing( 5 ); legend->setTitleText( tr( "Legend" ) ); legend->setOrientation( Qt::Horizontal ); m_chart.addLegend( legend ); // Configure the items markers MarkerAttributes lma ( legend->markerAttributes( 0 ) ); lma.setMarkerStyle( MarkerAttributes::MarkerDiamond ); legend->setMarkerAttributes( 0, lma ); lma.setMarkerStyle( MarkerAttributes::MarkerCircle ); legend->setMarkerAttributes( 1, lma ); // Configure Legend Title and labels legend->setTitleText( "Bars" ); legend->setText( 0, "Series 1" ); legend->setText( 1, "Series 2" ); legend->setText( 2, "Series 3" ); // adjust the legend item's font: TextAttributes lta( legend->textAttributes() ); lta.setPen( QPen( Qt::darkGray ) ); Measure me( lta.fontSize() ); me.setValue( me.value() * 1.5 ); lta.setFontSize( Measure( 9, KChartEnums::MeasureCalculationModeAbsolute ) ); legend->setTextAttributes( lta ); // adjust the legend title's font: lta = legend->titleTextAttributes(); lta.setPen( QPen( Qt::darkGray ) ); me = lta.fontSize(); me.setValue( me.value() * 1.5 ); lta.setFontSize( me ); legend->setTitleTextAttributes( lta ); // Configure a pen to surround // the markers with a border QPen markerPen; markerPen.setColor( Qt::darkGray ); markerPen.setWidth( 2 ); // Pending Michel use datasetCount() here as soon // as it is fixed for ( uint i = 0; i < /*legend->datasetCount()*/ 3; i++ ) legend->setPen( i, markerPen ); // Add a background to your legend BackgroundAttributes ba; ba.setBrush( Qt::white ); ba.setVisible( true ); legend->setBackgroundAttributes( ba ); FrameAttributes fa; fa.setPen( markerPen ); fa.setVisible( true ); legend->setFrameAttributes( fa ); QVBoxLayout* l = new QVBoxLayout(this); l->addWidget(&m_chart); m_chart.setGlobalLeadingTop( 10 ); setLayout(l); } private: Chart m_chart; QStandardItemModel m_model; }; int main( int argc, char** argv ) { QApplication app( argc, argv ); ChartWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/examples/Lines/Advanced/mainwindow.h b/examples/Lines/Advanced/mainwindow.h index 50df12d..4fc8467 100644 --- a/examples/Lines/Advanced/mainwindow.h +++ b/examples/Lines/Advanced/mainwindow.h @@ -1,71 +1,71 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include namespace KChart { class Chart; class LineDiagram; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); private: bool eventFilter(QObject* target, QEvent* event) Q_DECL_OVERRIDE; private slots: void on_lineTypeCB_currentIndexChanged( const QString & text ); void on_paintValuesCB_toggled( bool checked ); void on_centerDataPointsCB_toggled( bool checked ); void on_threeDModeCB_toggled( bool checked ); void on_depthSB_valueChanged( int i ); void on_animateAreasCB_toggled( bool checked ); void on_highlightAreaCB_toggled( bool checked ); void on_highlightAreaSB_valueChanged( int i ); void setHighlightArea( int row, int column, int opacity, bool checked, bool doUpdate ); void on_trackAreasCB_toggled( bool checked ); void on_trackAreasSB_valueChanged( int i ); void setTrackedArea( int column, bool checked, bool doUpdate ); void slot_timerFired(); void on_reverseHorizontalCB_toggled( bool checked ); void on_reverseVerticalCB_toggled( bool checked ); private: KChart::Chart* m_chart; KChart::LineDiagram* m_lines; TableModel m_model; int m_curRow; int m_curColumn; int m_curOpacity; }; #endif /* MAINWINDOW_H */ diff --git a/examples/Lines/AreaPerCell/main.cpp b/examples/Lines/AreaPerCell/main.cpp index ac00a5a..b4a2b0e 100644 --- a/examples/Lines/AreaPerCell/main.cpp +++ b/examples/Lines/AreaPerCell/main.cpp @@ -1,81 +1,81 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include using namespace KChart; class ChartWidget : public QWidget { Q_OBJECT public: - explicit ChartWidget(QWidget* parent=0) + explicit ChartWidget(QWidget* parent = nullptr) : QWidget(parent) { m_model.insertRows( 0,6, QModelIndex() ); m_model.insertColumns( 0,1, QModelIndex() ); m_model.setData( m_model.index( 0, 0, QModelIndex()), 15); m_model.setData( m_model.index( 1, 0, QModelIndex()), 11); m_model.setData( m_model.index( 2, 0, QModelIndex()), 7); m_model.setData( m_model.index( 3, 0, QModelIndex()), 3); m_model.setData( m_model.index( 4, 0, QModelIndex()), -1); m_model.setData( m_model.index( 5, 0, QModelIndex()), -5); LineDiagram* diagram = new LineDiagram; diagram->setModel(&m_model); m_chart.coordinatePlane()->replaceDiagram(diagram); // paint the areas in a few cells // using different brushes LineAttributes la3(diagram->lineAttributes(m_model.index(3,0,QModelIndex()))); la3.setDisplayArea( true ); la3.setTransparency( 150 ); diagram->setBrush(m_model.index(1,0,QModelIndex()),QBrush(Qt::green)); diagram->setLineAttributes( m_model.index( 1, 0, QModelIndex()),la3); diagram->setBrush(m_model.index(3,0,QModelIndex()),QBrush(Qt::yellow)); diagram->setLineAttributes(m_model.index(3,0,QModelIndex()),la3); diagram->setBrush(m_model.index(4,0,QModelIndex()),QBrush(Qt::red)); diagram->setLineAttributes(m_model.index(4,0,QModelIndex()),la3); QVBoxLayout* l = new QVBoxLayout(this); l->addWidget(&m_chart); setLayout(l); } private: Chart m_chart; QStandardItemModel m_model; }; int main( int argc, char** argv ) { QApplication app( argc, argv ); ChartWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/examples/Lines/Parameters/main.cpp b/examples/Lines/Parameters/main.cpp index 7f2deb8..72a615d 100644 --- a/examples/Lines/Parameters/main.cpp +++ b/examples/Lines/Parameters/main.cpp @@ -1,124 +1,124 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include using namespace KChart; class ChartWidget : public QWidget { Q_OBJECT public: - explicit ChartWidget(QWidget* parent=0) + explicit ChartWidget(QWidget* parent = nullptr) : QWidget(parent) { m_model.insertRows( 0,5, QModelIndex() ); m_model.insertColumns( 0,5, QModelIndex() ); qreal increase = 1.15; for ( int i = 0; i < 5; ++i ) { increase += 0.06; for ( int j = 0; j < 5; ++j ) { m_model.setData( m_model.index( i,j,QModelIndex() ), ( increase + i ) * j * (1.0 + 0.1 * (rand() % 10)) ); } } LineDiagram* diagram = new LineDiagram; diagram->setModel(&m_model); // Display values // 1 - Call the relevant attributes DataValueAttributes dva( diagram->dataValueAttributes() ); // 2 - We want to configure the font and colors // for the data values text. TextAttributes ta( dva.textAttributes() ); dva.setDecimalDigits( 2 ); // Append a prefix/suffix to the // data value labels being displayed // //dva.setPrefix( "* " ); dva.setSuffix( " Ohm" ); //rotate if you wish //ta.setRotation( 0 ); // 3 - Set up your text attributes ta.setFont( QFont( "Comic") ); ta .setPen( QPen( QColor( Qt::darkGreen ) ) ); ta.setVisible( true ); // font size Measure me( ta.fontSize() ); me.setValue( me.value() * 0.25 ); ta.setFontSize( me ); // 4 - Assign the text attributes to your // DataValuesAttributes dva.setTextAttributes( ta ); dva.setVisible( true ); // 5 - Assign to the diagram diagram->setDataValueAttributes( dva ); // Set thick line widths for all datasets for ( int i = 0; i < m_model.rowCount(); ++i ) { QPen pen( diagram->pen( i ) ); pen.setWidth( 17 ); diagram->setPen( i, pen ); } // Draw one of the sections of a line differently. // 1 - Retrieve the pen for the dataset and change // its style. // This allow us to keep the line original color. QPen linePen( diagram->pen( 1 ) ); linePen.setColor( Qt::yellow ); linePen.setWidth( 7 ); linePen.setStyle( Qt::DashLine ); // 2 - Change the Pen for a section within a line // while assigning it to the diagram diagram->setPen( m_model.index( 1, 1, QModelIndex() ), linePen ); // 3 - Assign to the chart m_chart.coordinatePlane()->replaceDiagram(diagram); m_chart.setGlobalLeadingRight( 50 ); QVBoxLayout* l = new QVBoxLayout(this); l->addWidget(&m_chart); setLayout(l); } private: Chart m_chart; QStandardItemModel m_model; }; int main( int argc, char** argv ) { QApplication app( argc, argv ); ChartWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/examples/Lines/PointChart/mainwindow.h b/examples/Lines/PointChart/mainwindow.h index a38b87b..f5d511e 100644 --- a/examples/Lines/PointChart/mainwindow.h +++ b/examples/Lines/PointChart/mainwindow.h @@ -1,62 +1,62 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include namespace KChart { class Chart; class LineDiagram; } QT_BEGIN_NAMESPACE class QPainterPath; QT_END_NAMESPACE class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); ~MainWindow(); private slots: void on_lineTypeCB_currentIndexChanged( const QString & text ); void on_paintValuesCB_toggled( bool checked ); void on_paintMarkersCB_toggled( bool checked ); void on_paintLinesCB_toggled( bool checked ); void on_markersStyleCB_currentIndexChanged( const QString & text ); void on_markersWidthSB_valueChanged( int i ); void on_markersHeightSB_valueChanged( int i); private: KChart::Chart* m_chart; KChart::LineDiagram* m_lines; TableModel m_model; QPainterPath* path; }; #endif /* MAINWINDOW_H */ diff --git a/examples/ModelView/HidingData/mainwindow.h b/examples/ModelView/HidingData/mainwindow.h index b2b5fad..bf98cf3 100644 --- a/examples/ModelView/HidingData/mainwindow.h +++ b/examples/ModelView/HidingData/mainwindow.h @@ -1,56 +1,56 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" QT_BEGIN_NAMESPACE class QAbstractItemModel; QT_END_NAMESPACE namespace KChart { class Chart; class LineDiagram; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); private slots: void on_showDataset1CB_toggled( bool checked ); void on_showDataset2CB_toggled( bool checked ); void on_showDataset3CB_toggled( bool checked ); void setHidden( int dataset, bool hidden ); void openFile(const QString &path); private: KChart::Chart * m_chart; KChart::LineDiagram * m_lines; QAbstractItemModel * m_model; }; #endif /* MAINWINDOW_H */ diff --git a/examples/NoValues/mainwindow.cpp b/examples/NoValues/mainwindow.cpp index f482fd9..bdc10b1 100644 --- a/examples/NoValues/mainwindow.cpp +++ b/examples/NoValues/mainwindow.cpp @@ -1,125 +1,125 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mainwindow.h" #include #include #include #include #include #include #include #include #include using namespace KChart; class EmptyModel : public QAbstractItemModel { public: - EmptyModel( QObject* parent = 0 ) + EmptyModel( QObject* parent = nullptr ) : QAbstractItemModel( parent ) { //qDebug() << "EmptyModel::EmptyModel()"; } ~EmptyModel() { //qDebug() << "EmptyModel::~EmptyModel()"; } int columnCount( const QModelIndex& parent = QModelIndex() ) const Q_DECL_OVERRIDE { Q_UNUSED( parent ); //qDebug() << "EmptyModel::columnCount(...)"; return 0; } int rowCount( const QModelIndex& parent = QModelIndex() ) const Q_DECL_OVERRIDE { Q_UNUSED( parent ); //qDebug() << "EmptyModel::rowCount(...)"; return 0; } // NOTE: The following method will not be called by KD Chart, // because the model is returning 0 for columnCount() / rowCount(). QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const Q_DECL_OVERRIDE { Q_UNUSED( role ); qDebug() << "EmptyModel::data(" << index.row() << index.column() << ")"; Q_ASSERT_X( false, "EmptyModel::data", "We should not end here..." ); return QVariant(); } QModelIndex index( int row, int column, const QModelIndex& parent = QModelIndex() ) const Q_DECL_OVERRIDE { Q_UNUSED( row ); Q_UNUSED( column ); Q_UNUSED( parent ); //qDebug() << "EmptyModel::index(" << row << column << ")"; return QModelIndex(); } QModelIndex parent( const QModelIndex& parent ) const Q_DECL_OVERRIDE { Q_UNUSED( parent ); //qDebug() << "EmptyModel::parent(...)"; return QModelIndex(); } }; MainWindow::MainWindow( QWidget* parent ) : QWidget( parent ) { QHBoxLayout* chartLayout = new QHBoxLayout( this ); m_chart = new Chart(); m_chart->setGlobalLeading( 5, 5, 5, 5 ); chartLayout->addWidget( m_chart ); m_model = new EmptyModel( this ); // model contains no data at all // Set up the diagram m_bars = new LineDiagram(); m_bars->setModel( m_model ); CartesianAxis *xAxis = new CartesianAxis( m_bars ); CartesianAxis *yAxis = new CartesianAxis ( m_bars ); xAxis->setPosition ( KChart::CartesianAxis::Bottom ); yAxis->setPosition ( KChart::CartesianAxis::Left ); xAxis->setTitleText ( "Abscissa axis at the bottom" ); yAxis->setTitleText ( "Ordinate axis at the left side" ); m_bars->addAxis( xAxis ); m_bars->addAxis( yAxis ); m_chart->coordinatePlane()->replaceDiagram( m_bars ); Legend* legend = new Legend( m_bars, m_chart ); legend->setPosition( Position::South ); legend->setAlignment( Qt::AlignCenter ); legend->setShowLines( true ); legend->setTitleText("This is the legend - showing no data either"); legend->setOrientation( Qt::Horizontal ); legend->addDiagram( m_bars ); m_chart->addLegend( legend ); } diff --git a/examples/NoValues/mainwindow.h b/examples/NoValues/mainwindow.h index 31d7a96..f2bd402 100644 --- a/examples/NoValues/mainwindow.h +++ b/examples/NoValues/mainwindow.h @@ -1,48 +1,48 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include namespace KChart { class Chart; class BarDiagram; class LineDiagram; } class MainWindow : public QWidget { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); virtual ~MainWindow() { } private: KChart::Chart* m_chart; KChart::LineDiagram* m_bars; QAbstractItemModel* m_model; }; #endif /* MAINWINDOW_H */ diff --git a/examples/Pie/Advanced/mainwindow.h b/examples/Pie/Advanced/mainwindow.h index baa5992..48dcced 100644 --- a/examples/Pie/Advanced/mainwindow.h +++ b/examples/Pie/Advanced/mainwindow.h @@ -1,71 +1,71 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE namespace KChart { class Chart; class PieDiagram; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); private slots: // start position void on_startPositionSB_valueChanged( double pos ); void on_startPositionSL_valueChanged( int pos ); // explode void on_explodeSubmitPB_clicked(); void on_animateExplosionCB_toggled( bool toggle ); void setExplodeFactor( int column, qreal value ); // animation void slotNextFrame(); // 3D void on_threeDGB_toggled( bool toggle ); void on_threeDFactorSB_valueChanged( int factor ); private: KChart::Chart* m_chart; TableModel m_model; KChart::PieDiagram* m_pie; QTimer* m_timer; int m_currentFactor; int m_currentDirection; int m_currentSlice; }; #endif /* MAINWINDOW_H */ diff --git a/examples/Pie/Parameters/main.cpp b/examples/Pie/Parameters/main.cpp index 74e02ac..2176ccf 100644 --- a/examples/Pie/Parameters/main.cpp +++ b/examples/Pie/Parameters/main.cpp @@ -1,132 +1,132 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include using namespace KChart; class ChartWidget : public QWidget { Q_OBJECT public: - explicit ChartWidget(QWidget* parent=0) + explicit ChartWidget(QWidget* parent = nullptr) : QWidget(parent) { m_model.insertRows( 0, 1, QModelIndex() ); m_model.insertColumns( 0, 6, QModelIndex() ); for (int row = 0; row < 1; ++row) { for (int column = 0; column < 6; ++column) { QModelIndex index = m_model.index(row, column, QModelIndex()); m_model.setData(index, QVariant(row+1 * column+1) ); // this shows the index as static comments: // m_model.setData(index, QString("row: %1, column: %2").arg(row).arg(column), KChart::CommentRole); // this shows the index as volatile tooltips: m_model.setData(index, QString("row: %1, column: %2").arg(row).arg(column), Qt::ToolTipRole); } } // We need a Polar plane for the Pie type PolarCoordinatePlane* polarPlane = new PolarCoordinatePlane( &m_chart ); // replace the default Cartesian plane with // our Polar plane m_chart.replaceCoordinatePlane( polarPlane ); // assign the model to our pie diagram PieDiagram* diagram = new PieDiagram; diagram->setModel(&m_model); // Configure some Pie specifical attributes // explode a section PieAttributes pa( diagram->pieAttributes() ); pa.setExplodeFactor( 0.1 ); // Assign the attributes // to the diagram diagram->setPieAttributes( 1, pa ); // Configure a generic attribute // available to all chart types QPen sectionPen; sectionPen.setWidth( 5 ); sectionPen.setStyle( Qt::DashLine ); sectionPen.setColor( Qt::magenta ); diagram->setPen( 1, sectionPen ); // Display data values // not implemented yet - disable for now const QFont font(QFont( "Comic", 10 )); const int colCount = diagram->model()->columnCount(); for ( int iColumn = 0; iColumndataValueAttributes( iColumn ) ); TextAttributes ta( dva.textAttributes() ); ta.setRotation( 0 ); ta.setFont( font ); ta .setPen( QPen( Qt::darkBlue ) ); ta.setVisible( true ); dva.setTextAttributes( ta ); BackgroundAttributes back( dva.backgroundAttributes() ); back.setBrush( QBrush( QColor(255,0,0) ) ); back.setVisible( true ); dva.setBackgroundAttributes( back ); RelativePosition posPos( dva.positivePosition() ); posPos.setReferencePosition( KChart::Position::North ); posPos.setAlignment( Qt::AlignCenter ); posPos.setHorizontalPadding( KChart::Measure(0.0) ); posPos.setVerticalPadding( KChart::Measure(-1000.0) ); dva.setPositivePosition( posPos ); dva.setVisible( true ); diagram->setDataValueAttributes( iColumn, dva); } // Assign our diagram to the Chart m_chart.coordinatePlane()->replaceDiagram(diagram); QVBoxLayout* l = new QVBoxLayout(this); l->addWidget(&m_chart); setLayout(l); } private: Chart m_chart; QStandardItemModel m_model; }; int main( int argc, char** argv ) { QApplication app( argc, argv ); ChartWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/examples/Plane/AdjustedBoundaries/main.cpp b/examples/Plane/AdjustedBoundaries/main.cpp index 20a20f6..458c4ce 100644 --- a/examples/Plane/AdjustedBoundaries/main.cpp +++ b/examples/Plane/AdjustedBoundaries/main.cpp @@ -1,204 +1,204 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KChart; class ChartWidget : public QWidget { Q_OBJECT public: - explicit ChartWidget(QWidget* parent=0) + explicit ChartWidget(QWidget* parent = nullptr) : QWidget(parent) { m_model.insertRows( 0, 2, QModelIndex() ); m_model.insertColumns( 0, 3, QModelIndex() ); for (int row = 0; row < 3; ++row) { for (int column = 0; column < 3; ++column) { QModelIndex index = m_model.index(row, column, QModelIndex()); m_model.setData(index, QVariant(row+10 * column) ); } } BarDiagram* diagram = new BarDiagram; diagram->setModel(&m_model); /* * AXIS */ CartesianAxis *xAxis = new CartesianAxis( diagram ); CartesianAxis *yAxis = new CartesianAxis ( diagram ); xAxis->setPosition ( KChart::CartesianAxis::Bottom ); yAxis->setPosition ( KChart::CartesianAxis::Left ); diagram->addAxis( xAxis ); diagram->addAxis( yAxis ); /* * AXIS LABELS */ // set the following to 0, to see the default Abscissa labels // (== X headers, as read from the data file) #if 1 QStringList months; months << "January" << "February"; xAxis->setLabels( months ); QStringList shortMonths; shortMonths << "Jan" << "Feb"; xAxis->setShortLabels( shortMonths ); #endif /* * DATA VALUES SETTINGS */ // We set the DataValueAttributes on a per-column basis here, // because we want the texts to be printed in different //colours - according to their respective dataset's colour. const QFont font(QFont( "Comic", 10 )); const int colCount = diagram->model()->columnCount(); for ( int iColumn = 0; iColumnbrush( iColumn ) ); DataValueAttributes a( diagram->dataValueAttributes( iColumn ) ); TextAttributes ta( a.textAttributes() ); ta.setRotation( 0 ); ta.setFont( font ); ta .setPen( QPen( brush.color() ) ); ta.setVisible( true ); a.setTextAttributes( ta ); a.setSuffix( "%" ); a.setVisible( true ); diagram->setDataValueAttributes( iColumn, a); } /* * VERTICAL RANGE SETTINGS */ // m_chart.coordinatePlane() returns an abstract plane. // We need to cast in order to be able set our own boundaries. CartesianCoordinatePlane* plane1 = static_cast (m_chart.coordinatePlane()); plane1->setVerticalRange( QPair( 0.0, 100.0 ) ); plane1->replaceDiagram(diagram); /* * HEADER SETTINGS */ // Add at one Header and set it up HeaderFooter* header = new HeaderFooter( &m_chart ); header->setPosition( Position::North ); header->setText( "A Bar Chart with Adjusted Vertical Range" ); m_chart.addHeaderFooter( header ); // Configure the Header text attributes TextAttributes hta; hta.setPen( QPen( Qt::red ) ); // let the header resize itself // together with the widget. // so-called relative size Measure m( 35.0 ); m.setRelativeMode( header->autoReferenceArea(), KChartEnums::MeasureOrientationMinimum ); hta.setFontSize( m ); // min font size m.setValue( 3.0 ); m.setCalculationMode( KChartEnums::MeasureCalculationModeAbsolute ); hta.setMinimalFontSize( m ); header->setTextAttributes( hta ); // Configure the Header's Background BackgroundAttributes hba; hba.setBrush( Qt::white ); hba.setVisible( true ); header->setBackgroundAttributes( hba ); // Configure the header Frame attributes FrameAttributes hfa; hfa.setPen( QPen ( QBrush( Qt::darkGray ), 2 ) ); hfa.setPadding( 2 ); hfa.setVisible( true ); header->setFrameAttributes( hfa ); /* * GRIDS SETTINGS */ // retrieve your grid attributes // display grid and sub-grid GridAttributes ga ( plane1->gridAttributes( Qt::Vertical ) ); // Configure a sub-grid pen QPen subGridPen( Qt::darkGray ); subGridPen.setStyle( Qt::DotLine ); ga.setSubGridPen( subGridPen ); // change step and substep width // to fit the boundaries ga.setGridStepWidth( 10.0 ); ga.setGridSubStepWidth( 5.0 ); ga.setGridVisible( true ); ga.setSubGridVisible( true ); // Assign your grid to the plane plane1->setGridAttributes( Qt::Vertical, ga ); QVBoxLayout* l = new QVBoxLayout(this); m_chart.setGlobalLeadingTop( 5 ); l->addWidget(&m_chart); setLayout(l); } private: Chart m_chart; QStandardItemModel m_model; QPixmap pixmap; }; int main( int argc, char** argv ) { QApplication app( argc, argv ); ChartWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/examples/Plane/OverlayedPlanes/mainwindow.h b/examples/Plane/OverlayedPlanes/mainwindow.h index c4f0d77..02655a7 100644 --- a/examples/Plane/OverlayedPlanes/mainwindow.h +++ b/examples/Plane/OverlayedPlanes/mainwindow.h @@ -1,62 +1,62 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include namespace KChart { class Chart; class DatasetProxyModel; class LineDiagram; class LineAttributes; class CartesianAxis; class CartesianCoordinatePlane; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); private slots: void on_displayGrid1CB_toggled( bool checked ); void on_displayGrid2CB_toggled( bool checked ); private: void init(); KChart::Chart* m_chart; TableModel m_model; TableModel m_model2; KChart::DatasetProxyModel* m_datasetProxy; KChart::LineDiagram* m_lines; KChart::LineDiagram* m_lines2; KChart::CartesianCoordinatePlane* plane2; }; #endif /* MAINWINDOW_H */ diff --git a/examples/Plotter/BubbleChart/mainwindow.cpp b/examples/Plotter/BubbleChart/mainwindow.cpp index 153d998..7349f23 100644 --- a/examples/Plotter/BubbleChart/mainwindow.cpp +++ b/examples/Plotter/BubbleChart/mainwindow.cpp @@ -1,160 +1,160 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mainwindow.h" #include #include #include #include #include #include #include using namespace KChart; static const int nBubbles = 7; // we display seven bubbles using the following data structure: // 0: Y // 1: X // 2: size struct DataType{ DataType( qreal x_, qreal y_, qreal size_ ) : x(x_) , y(y_) , size(size_) {} qreal x; qreal y; qreal size; }; static const DataType bubblesData[nBubbles] = { DataType(0.5, 1.0, 100), DataType(1.0, 0.5, 60 ), DataType(1.6, 2.0, 28 ), DataType(0.7, 0.3, 55 ), DataType(1.3, 2.0, 95 ), DataType(2.0, 1.0, 75 ), DataType(1.4, 1.1, 85 ) }; #define ROLE_SIZE Qt::UserRole + 1 MainWindow::MainWindow( QWidget* parent ) : QWidget( parent ), - m_model(0) + m_model(nullptr) { setupUi( this ); QHBoxLayout* chartLayout = new QHBoxLayout( chartFrame ); m_chart = new Chart(); chartLayout->addWidget( m_chart ); // Set up the data initializeDataModel(); // Set up the diagram m_plotter = new Plotter(); // Register the data model at the diagram m_plotter->setModel( m_model ); // Add axes to the diagram CartesianAxis *xAxis = new CartesianAxis( m_plotter ); CartesianAxis *xAxis2 = new CartesianAxis( m_plotter ); CartesianAxis *yAxis = new CartesianAxis ( m_plotter ); CartesianAxis *yAxis2 = new CartesianAxis ( m_plotter ); xAxis->setPosition ( KChart::CartesianAxis::Bottom ); xAxis2->setPosition( KChart::CartesianAxis::Top ); yAxis->setPosition ( KChart::CartesianAxis::Left ); yAxis2->setPosition( KChart::CartesianAxis::Right ); m_plotter->addAxis( xAxis ); m_plotter->addAxis( xAxis2 ); m_plotter->addAxis( yAxis ); m_plotter->addAxis( yAxis2 ); connect( threeDEnabled, SIGNAL(toggled(bool)), this, SLOT(setMarkerAttributes()) ); m_chart->coordinatePlane()->replaceDiagram( m_plotter ); m_chart->setGlobalLeading( 20, 20, 20, 20 ); setMarkerAttributes(); } void MainWindow::initializeDataModel() { m_model = new QStandardItemModel( nBubbles, 2 ); m_model->setHeaderData(0, Qt::Horizontal, tr("Some Bubbles")); for ( int i=0; i < nBubbles; ++i ) { const QModelIndex indexX = m_model->index(i, 0); const QModelIndex indexY = m_model->index(i, 1); m_model->setData(indexX, QVariant( bubblesData[i].x ), Qt::DisplayRole); m_model->setData(indexY, QVariant( bubblesData[i].y ), Qt::DisplayRole); m_model->setData(indexY, bubblesData[i].size, ROLE_SIZE ); } } void MainWindow::setMarkerAttributes() { // disable the connecting line KChart::LineAttributes la = m_plotter->lineAttributes(); la.setVisible(false); m_plotter->setLineAttributes(la); for ( int iRow = 0; iRowmodel()->index(iRow, 0); const QModelIndex indexY = m_plotter->model()->index(iRow, 1); DataValueAttributes dva( m_plotter->dataValueAttributes( indexX ) ); dva.setVisible( true ); TextAttributes ta( dva.textAttributes() ); ta.setVisible( false ); MarkerAttributes ma( dva.markerAttributes() ); ma.setVisible( true ); ma.setMarkerStyle( MarkerAttributes::MarkerCircle ); ma.setThreeD( threeDEnabled->isChecked() ); // set the size const qreal d = m_model->data( indexY, ROLE_SIZE ).toReal(); ma.setMarkerSize( QSizeF(d,d) ); RelativePosition pos( dva.positivePosition() ); pos.setAlignment( Qt::AlignCenter ); pos.setHorizontalPadding(0); dva.setPositivePosition( pos ); dva.setMarkerAttributes( ma ); dva.setTextAttributes( ta ); // note: The KChart::Plotter looks at the X cell // for data value attributes, // any attrs set at the Y cell are ignored. m_plotter->setDataValueAttributes( indexX, dva ); } m_chart->update(); } diff --git a/examples/Plotter/BubbleChart/mainwindow.h b/examples/Plotter/BubbleChart/mainwindow.h index 31c10b3..53547ff 100644 --- a/examples/Plotter/BubbleChart/mainwindow.h +++ b/examples/Plotter/BubbleChart/mainwindow.h @@ -1,53 +1,53 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" QT_BEGIN_NAMESPACE class QStandardItemModel; QT_END_NAMESPACE namespace KChart { class Chart; class Plotter; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); private slots: void initializeDataModel(); void setMarkerAttributes(); private: QStandardItemModel* m_model; KChart::Chart* m_chart; KChart::Plotter* m_plotter; }; #endif /* MAINWINDOW_H */ diff --git a/examples/Plotter/Timeline/main.cpp b/examples/Plotter/Timeline/main.cpp index 99c4824..cee1435 100644 --- a/examples/Plotter/Timeline/main.cpp +++ b/examples/Plotter/Timeline/main.cpp @@ -1,148 +1,148 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include "timeaxis.h" #include "timechartmodel.h" class ChartWidget : public QWidget { Q_OBJECT public: - explicit ChartWidget(QWidget* parent = 0) : QWidget(parent), m_counter(0) { + explicit ChartWidget(QWidget* parent = nullptr) : QWidget(parent), m_counter(0) { QSplitter* splitter = new QSplitter(this); QHBoxLayout* l = new QHBoxLayout(this); setLayout(l); l->addWidget( splitter ); QWidget* leftWidget = new QWidget( splitter ); QVBoxLayout* leftLayout = new QVBoxLayout(leftWidget); leftWidget->setLayout(leftLayout); QPushButton* button = new QPushButton("Animate", leftWidget); leftLayout->addWidget( button ); button->setCheckable( true ); connect( button, SIGNAL(toggled(bool)), this, SLOT(buttonToggled(bool)) ); QTreeView* tv = new QTreeView( leftWidget ); leftLayout->addWidget( tv ); m_chart = new KChart::Chart( splitter ); m_model = new QStandardItemModel( 365, 2, this ); for ( int i = 0; i < 365; ++i ) { const QDateTime dt = QDateTime( QDate( 2010, 1, 1 ), QTime() ); m_model->setData( m_model->index( i, 0 ), dt.addDays( i ) ); m_model->setData( m_model->index( i, 1 ), sin( i / 10.0 ) * 10 ); } TimeChartModel* proxy = new TimeChartModel( this ); proxy->setSourceModel( m_model ); proxy->setVisibleRange( QDateTime( QDate( 2010, 2, 1 ), QTime() ), QDateTime( QDate( 2010, 3, 31 ), QTime() ) ); KChart::Plotter* plotter = new KChart::Plotter; m_chart->coordinatePlane()->replaceDiagram( plotter ); tv->setModel( proxy ); tv->show(); TimeAxis* axis = new TimeAxis( plotter ); axis->setPosition( TimeAxis::Bottom ); plotter->addAxis( axis ); plotter->setModel( proxy ); connect( proxy, SIGNAL(rowsInserted(QModelIndex,int,int)), m_chart->coordinatePlane(), SLOT(adjustRangesToData()) ); connect( proxy, SIGNAL(rowsRemoved(QModelIndex,int,int)), m_chart->coordinatePlane(), SLOT(adjustRangesToData()) ); proxy->setVisibleRange( QDateTime( QDate( 2010, 3, 15 ), QTime() ), QDateTime( QDate( 2010, 5, 18 ), QTime() ) ); qobject_cast< KChart::CartesianCoordinatePlane* >( m_chart->coordinatePlane() )->adjustRangesToData(); m_timer = new QTimer(this); connect( m_timer, SIGNAL(timeout()), this, SLOT(slotTimeout()) ); } private slots: void slotTimeout() { // An ugly hack to prevent the QAbstractItemModel from emitting dataChanged // for every single call to setData which would result in a full relayout // every time. That is horrible for performance and what we do is to prevent // QAbstractItemModel to emit dataChanged, collect what data changed and // emit the signal at the end ourself. m_model->blockSignals(true); QModelIndexList indexes; QVariant v1, v2; for ( int i = 0; i < 365; ++i ) { QModelIndex idx = m_model->index( i, 1 ); indexes.append(idx); m_model->setData( idx, sin( i / 10.0 + m_counter ) * 10 ); } m_model->blockSignals(false); if (!indexes.isEmpty()) { m_model->metaObject()->invokeMethod(m_model, "dataChanged", Qt::DirectConnection, Q_ARG(QModelIndex,indexes.first()), Q_ARG(QModelIndex,indexes.last())); } m_counter += 0.02; } void buttonToggled(bool checked) { if (checked) m_timer->start( 200 ); else m_timer->stop(); } private: KChart::Chart* m_chart; QStandardItemModel* m_model; QTimer* m_timer; qreal m_counter; }; /** * This example demonstrates how to use time-based plots with timestamp-value data points * based on seconds and how to use a proxy model for defining the plotted "window" of the * measurement data. */ int main( int argc, char* argv[] ) { QApplication app( argc, argv ); ChartWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/examples/Plotter/Timeline/timechartmodel.h b/examples/Plotter/Timeline/timechartmodel.h index 9e0b2cc..6c9db53 100644 --- a/examples/Plotter/Timeline/timechartmodel.h +++ b/examples/Plotter/Timeline/timechartmodel.h @@ -1,49 +1,49 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TIMECHARTMODEL_H #define TIMECHARTMODEL_H #include #include #include class TimeChartModel : public QSortFilterProxyModel { Q_OBJECT public: - explicit TimeChartModel( QObject* parent = 0 ); + explicit TimeChartModel( QObject* parent = nullptr ); QPair< QDateTime, QDateTime > visibleRange() const; public Q_SLOTS: void setVisibleRange( const QDateTime& start, const QDateTime& end ); void setVisibleStart( const QDateTime& start ); void setVisibleEnd( const QDateTime& end ); QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const Q_DECL_OVERRIDE; protected: bool filterAcceptsRow( int source_row, const QModelIndex& source_parent ) const Q_DECL_OVERRIDE; private: QPair< QDateTime, QDateTime > range; }; #endif diff --git a/examples/Polar/Advanced/mainwindow.h b/examples/Polar/Advanced/mainwindow.h index e506ebc..0b3b8a5 100644 --- a/examples/Polar/Advanced/mainwindow.h +++ b/examples/Polar/Advanced/mainwindow.h @@ -1,68 +1,68 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include namespace KChart { class Chart; class DatasetProxyModel; class PolarDiagram; class PolarCoordinatePlane; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - explicit MainWindow( QWidget* parent = 0 ); + explicit MainWindow( QWidget* parent = nullptr ); private slots: // start position of circular grid void on_startPositionSB_valueChanged( double pos ); void on_startPositionSL_valueChanged( int pos ); // show grid void on_circularGridCB_toggled( bool toggle ); void on_sagittalGridCB_toggled( bool toggle ); private: void initKChartClasses(); void wireUpKChartClasses(); void setItemModelData(); // For a change we do not use our special TableModel here, // but we just use the standard model that comes with Qt. QStandardItemModel m_model; KChart::Chart* m_chart; KChart::PolarDiagram* m_diagram; KChart::PolarCoordinatePlane* m_polarPlane; int m_currentFactor; int m_currentDirection; int m_currentSlice; }; #endif /* MAINWINDOW_H */ diff --git a/examples/Polar/Parameters/main.cpp b/examples/Polar/Parameters/main.cpp index 3dd1639..9e8b18b 100644 --- a/examples/Polar/Parameters/main.cpp +++ b/examples/Polar/Parameters/main.cpp @@ -1,160 +1,160 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include using namespace KChart; class ChartWidget : public QWidget { Q_OBJECT public: - explicit ChartWidget(QWidget* parent=0) + explicit ChartWidget(QWidget* parent = nullptr) : QWidget(parent) { // initialize the ItemModel and fill in some data m_model.insertRows( 0, 10 ); m_model.insertColumns( 0, 5 ); int value = 0; for ( int column = 0; column < m_model.columnCount(); ++column ) { for ( int row = 0; row < m_model.rowCount(); ++row ) { QModelIndex index = m_model.index( row, column ); m_model.setData( index, QVariant( value++ ) ); } } // We need a Polar plane for the Polar type PolarCoordinatePlane* polarPlane = new PolarCoordinatePlane( &m_chart ); // replace the default Cartesian plane with // our Polar plane m_chart.replaceCoordinatePlane( polarPlane ); // assign the model to our polar diagram PolarDiagram* diagram = new PolarDiagram; diagram->setModel(&m_model); // Configure the plane's Background BackgroundAttributes pba; pba.setBrush( QBrush(QColor(0x20,0x20,0x60)) ); pba.setVisible( true ); polarPlane->setBackgroundAttributes( pba ); // Configure some global / dataset / cell specific attributes: DataValueAttributes dva( diagram->dataValueAttributes() ); MarkerAttributes ma( dva.markerAttributes() ); ma.setVisible( true ); ma.setMarkerStyle( MarkerAttributes::MarkerSquare ); ma.setMarkerSize( QSize( 6,6 ) ); dva.setMarkerAttributes( ma ); // find a nicer place for the data value texts: // We want them to be centered on top of their respective markers. RelativePosition relativePosition( dva.positivePosition() ); relativePosition.setReferencePosition( Position::Center ); relativePosition.setAlignment( Qt::AlignBottom | Qt::AlignHCenter ); relativePosition.setHorizontalPadding( KChart::Measure( 0.0, KChartEnums::MeasureCalculationModeAbsolute ) ); relativePosition.setVerticalPadding( KChart::Measure( 0.0, KChartEnums::MeasureCalculationModeAbsolute ) ); dva.setPositivePosition( relativePosition ); diagram->setDataValueAttributes( dva ); // Display data values const QFont font(QFont( "Comic", 10 )); const int colCount = diagram->model()->columnCount(); for ( int iColumn = 0; iColumndataValueAttributes( iColumn ) ); TextAttributes ta( dva.textAttributes() ); ta.setRotation( 0 ); ta.setFont( font ); ta .setPen( QPen( Qt::gray ) ); ta.setVisible( true ); dva.setTextAttributes( ta ); dva.setVisible( true ); diagram->setDataValueAttributes( iColumn, dva); } // Set the marker of one single cell differently to show // how per-cell marker attributes can be used: const QModelIndex index = diagram->model()->index( 1, 2, QModelIndex() ); dva = diagram->dataValueAttributes( index ); ma = dva.markerAttributes(); ma.setMarkerStyle( MarkerAttributes::MarkerCircle ); ma.setMarkerSize( QSize( 40,40 ) ); // This is the canonical way to adjust a marker's color: // By default the color is invalid so we use an explicit fallback // here to make sure we are getting the right color, as it would // be used by KD Chart's built-in logic too: QColor semiTrans( ma.markerColor() ); if ( ! semiTrans.isValid() ) semiTrans = diagram->brush( index ).color(); semiTrans.setAlpha(164); ma.setMarkerColor( semiTrans.darker() ); dva.setMarkerAttributes( ma ); // While we are at it we also set the text alignment to centered // for this special point: relativePosition = dva.positivePosition(); relativePosition.setAlignment( Qt::AlignCenter ); dva.setPositivePosition( relativePosition ); diagram->setDataValueAttributes( index, dva); // Assign our diagram to the Chart m_chart.coordinatePlane()->replaceDiagram(diagram); // We want to have a nice gap around the polar diagram, // but we also want to have the coord. plane's background cover that area, // so we just use some zooming. // NOTE: Setting a zoom factor must not be done before // a diagram has been specified and assigned to the coordinate plane! polarPlane->setZoomFactorX(0.9); polarPlane->setZoomFactorY(0.9); QVBoxLayout* l = new QVBoxLayout(this); l->addWidget(&m_chart); m_chart.setGlobalLeadingTop( 5 ); m_chart.setGlobalLeadingBottom( 5 ); setLayout(l); } private: Chart m_chart; QStandardItemModel m_model; }; int main( int argc, char** argv ) { QApplication app( argc, argv ); ChartWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/examples/RealTime/main.cpp b/examples/RealTime/main.cpp index 104b44e..4ebab36 100644 --- a/examples/RealTime/main.cpp +++ b/examples/RealTime/main.cpp @@ -1,80 +1,80 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include class ChartWidget : public QWidget { Q_OBJECT public: - explicit ChartWidget(QWidget* parent=0) + explicit ChartWidget(QWidget* parent = nullptr) : QWidget(parent) { m_model.insertRows( 0, 2, QModelIndex() ); m_model.insertColumns( 0, 3, QModelIndex() ); for (int row = 0; row < 3; ++row) { for (int column = 0; column < 3; ++column) { QModelIndex index = m_model.index(row, column, QModelIndex()); m_model.setData(index, QVariant(row+1 * column) ); } } KChart::BarDiagram* diagram = new KChart::BarDiagram; diagram->setModel(&m_model); m_chart.coordinatePlane()->replaceDiagram(diagram); QVBoxLayout* l = new QVBoxLayout(this); l->addWidget(&m_chart); setLayout(l); m_timer = new QTimer(this); connect( m_timer, SIGNAL(timeout()), this, SLOT(slotTimeout()) ); m_timer->start( 200 ); } private slots: void slotTimeout() { QModelIndex index = m_model.index( 0, 1, QModelIndex()); qreal value = ( m_model.data( index ).toInt() % 24 ) +1; m_model.setData( index, value ); } private: KChart::Chart m_chart; QStandardItemModel m_model; QTimer *m_timer; }; int main( int argc, char** argv ) { QApplication app( argc, argv ); ChartWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/examples/SharedAbscissa/SeparateDiagrams/mainwindow.h b/examples/SharedAbscissa/SeparateDiagrams/mainwindow.h index 2694014..59a599a 100644 --- a/examples/SharedAbscissa/SeparateDiagrams/mainwindow.h +++ b/examples/SharedAbscissa/SeparateDiagrams/mainwindow.h @@ -1,57 +1,57 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include namespace KChart { class Chart; class DatasetProxyModel; class LineDiagram; class LineAttributes; class CartesianAxis; class CartesianCoordinatePlane; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); private: KChart::Chart* m_chart; TableModel m_model; TableModel m_model2; KChart::DatasetProxyModel* m_datasetProxy; KChart::LineDiagram* m_lines; KChart::LineDiagram* m_lines2; KChart::CartesianCoordinatePlane* plane2; // mutable KChart::CartesianAxis xAxis; //mutable KChart::CartesianAxis yAxis; }; #endif /* MAINWINDOW_H */ diff --git a/examples/Sql/main.cpp b/examples/Sql/main.cpp index 10c7aac..fcb49d3 100644 --- a/examples/Sql/main.cpp +++ b/examples/Sql/main.cpp @@ -1,146 +1,146 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KChart; /** * Proxymodel that transposes columns and rows. */ class TransposeProxyModel : public QAbstractProxyModel{ public: - explicit TransposeProxyModel(QObject* parent = 0) : QAbstractProxyModel(parent) {} + explicit TransposeProxyModel(QObject* parent = nullptr) : QAbstractProxyModel(parent) {} virtual ~TransposeProxyModel() {} QModelIndex mapFromSource ( const QModelIndex & sourceIndex ) const Q_DECL_OVERRIDE { return index(sourceIndex.column(), sourceIndex.row()); } QModelIndex mapToSource ( const QModelIndex & proxyIndex ) const Q_DECL_OVERRIDE { return sourceModel()->index(proxyIndex.column(), proxyIndex.row()); } QModelIndex index(int r, int c, const QModelIndex &ind=QModelIndex()) const Q_DECL_OVERRIDE { Q_UNUSED(ind) return createIndex(r,c); } QModelIndex parent(const QModelIndex&) const Q_DECL_OVERRIDE { return QModelIndex(); } int rowCount(const QModelIndex &) const Q_DECL_OVERRIDE { return sourceModel()->columnCount(); } int columnCount(const QModelIndex &) const Q_DECL_OVERRIDE { return sourceModel()->rowCount(); } QVariant data(const QModelIndex &ind, int role) const Q_DECL_OVERRIDE { return sourceModel()->data(mapToSource(ind), role); } }; /** * The example that creates the SQL-model, adds data to it and display the data in a model. */ class ChartWidget : public QWidget { Q_OBJECT public: - explicit ChartWidget(QWidget* parent=0) + explicit ChartWidget(QWidget* parent = nullptr) : QWidget(parent) - , m_model(0) + , m_model(nullptr) { QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); db.setHostName("localhost"); db.setDatabaseName(":memory:"); // in memory rather then in a file //db.setUserName(""); //db.setPassword(""); bool ok = db.open(); Q_ASSERT(ok); Q_UNUSED(ok) // release mode QSqlQuery createTableQuery = db.exec("CREATE TABLE IF NOT EXISTS MyTable (col1 INT NOT NULL PRIMARY KEY, col2 INT);"); Q_ASSERT(!createTableQuery.lastError().isValid()); m_model = new QSqlTableModel(this, db); m_model->setTable("MyTable"); m_model->setEditStrategy(QSqlTableModel::OnRowChange); m_model->setSort(0, Qt::AscendingOrder); ok = m_model->select(); Q_ASSERT(ok); m_model->setHeaderData(0, Qt::Horizontal, tr("Column 1")); m_model->setHeaderData(1, Qt::Horizontal, tr("Column 2")); for (int row = 0; row < 3; ++row) { QSqlRecord rec; for (int column = 0; column < 2; ++column) { QSqlField field(column==0?"col1":"col2",QVariant::Int); field.setValue(row+1 * column); rec.append(field); } ok = m_model->insertRecord(-1, rec); Q_ASSERT(ok); } BarDiagram* diagram = new BarDiagram; TransposeProxyModel* proxymodel = new TransposeProxyModel(this); proxymodel->setSourceModel(m_model); diagram->setModel(proxymodel); m_chart.coordinatePlane()->replaceDiagram(diagram); // Add at one Header and set it up HeaderFooter* header = new HeaderFooter( &m_chart ); header->setPosition( Position::North ); header->setText( "A Simple Bar Chart" ); m_chart.addHeaderFooter( header ); // Configure the plane Frame attributes FrameAttributes pfa; pfa.setPen( QPen ( QBrush( Qt::blue ), 2 ) ); pfa.setVisible( true ); diagram->coordinatePlane()->setFrameAttributes( pfa ); // Configure the header Frame attributes FrameAttributes hfa; hfa.setPen( QPen ( QBrush( Qt::darkGray ), 2 ) ); hfa.setPadding( 2 ); hfa.setVisible( true ); header->setFrameAttributes( hfa ); QVBoxLayout* l = new QVBoxLayout(this); l->addWidget(&m_chart); setLayout(l); } private: Chart m_chart; QSqlTableModel *m_model; }; int main( int argc, char** argv ) { QApplication app( argc, argv ); ChartWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/examples/Stock/Advanced/mainwindow.h b/examples/Stock/Advanced/mainwindow.h index 6b537cb..2c395aa 100644 --- a/examples/Stock/Advanced/mainwindow.h +++ b/examples/Stock/Advanced/mainwindow.h @@ -1,56 +1,56 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include #include #include #include class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget *parent = 0 ); + MainWindow( QWidget *parent = nullptr ); private slots: void on_threeDProperties_toggled( bool checked ); void on_perspectiveDepth_valueChanged( int value ); void on_perspectiveAngle_valueChanged( int value ); void on_useShadowColors_toggled( bool checked ); void on_stockTypeCB_currentIndexChanged( const QString &text ); void chooseColor(); private: void initValues(); void applyColor(const QColor &color); KChart::Chart *m_chart; TableModel m_HLCModel; TableModel m_OHLCModel; KChart::StockDiagram m_diagram; KChart::ThreeDBarAttributes m_threeDBarAttributes; }; #endif // MAINWINDOW_H diff --git a/examples/TernaryCharts/Advanced/mainwindow.cpp b/examples/TernaryCharts/Advanced/mainwindow.cpp index 64a62d4..2932aca 100644 --- a/examples/TernaryCharts/Advanced/mainwindow.cpp +++ b/examples/TernaryCharts/Advanced/mainwindow.cpp @@ -1,99 +1,99 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "mainwindow.h" MainWindow::MainWindow( QWidget* parent ) : QDialog( parent ) - , m_chart( 0 ) - , m_diagram( 0 ) - , m_ternaryPlane( 0 ) + , m_chart( nullptr ) + , m_diagram( nullptr ) + , m_ternaryPlane( nullptr ) { setupUi( this ); // make a chart m_chart = new KChart::Chart; // replace the default (cartesian) coordinate plane with a ternary one m_ternaryPlane = new KChart::TernaryCoordinatePlane; m_chart->replaceCoordinatePlane( m_ternaryPlane ); // make a ternary line diagram m_diagram = new KChart::TernaryPointDiagram; // and replace the default diagram with it m_ternaryPlane->replaceDiagram( m_diagram ); // add the three ternary axes, see the positions KChart::TernaryAxis* axisA = new KChart::TernaryAxis( m_diagram ); axisA->setPosition( KChartEnums::PositionSouth ); KChart::TernaryAxis* axisB = new KChart::TernaryAxis( m_diagram ); axisB->setPosition( KChartEnums::PositionWest ); KChart::TernaryAxis* axisC = new KChart::TernaryAxis( m_diagram ); axisC->setPosition( KChartEnums::PositionEast ); QHBoxLayout* chartLayout = new QHBoxLayout( frame ); chartLayout->addWidget( m_chart ); setupModel(); m_diagram->setModel( &m_model ); connect( m_diagram, SIGNAL(clicked(QModelIndex)), SLOT(indexClicked(QModelIndex)) ); } void MainWindow::setupModel() { m_model.insertRows( 0, 5 ); m_model.insertColumns( 0, 3 ); const float column0Share = 1.0f/3.0f; const float column1Share = 1.0f/3.0f; const float column2Share = 1.0f/3.0f; for ( int row = 0; row < m_model.rowCount(); ++row ) { const qreal SkewX = column0Share * ( 1 - 1.0/( 5 * row*row*row + 1 ) ); const qreal SkewY = SkewX; QModelIndex index; index = m_model.index( row, 0 ); m_model.setData( index, QVariant( column0Share - SkewX ) ); index = m_model.index( row, 1 ); m_model.setData( index, QVariant( column1Share + SkewX - SkewY) ); index = m_model.index( row, 2 ); m_model.setData( index, QVariant( column2Share + SkewY ) ); } } void MainWindow::indexClicked( const QModelIndex& index ) { qDebug() << "MainWindow::indexClicked:" << index; if ( index.isValid() ) { QString text; QTextStream stream( &text ); stream << "Row: " << index.row() << " - Column: " << index.column(); label->setText( text ); } } diff --git a/examples/TernaryCharts/Advanced/mainwindow.h b/examples/TernaryCharts/Advanced/mainwindow.h index cd1c4d8..e5cb233 100644 --- a/examples/TernaryCharts/Advanced/mainwindow.h +++ b/examples/TernaryCharts/Advanced/mainwindow.h @@ -1,58 +1,58 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include #include "ui_mainwindow.h" namespace KChart { class Chart; class TernaryLineDiagram; class TernaryPointDiagram; class TernaryCoordinatePlane; } class MainWindow : public QDialog, private Ui::MainWindow { Q_OBJECT public: - explicit MainWindow( QWidget* parent = 0 ); + explicit MainWindow( QWidget* parent = nullptr ); private slots: void indexClicked( const QModelIndex& index ); private: void setupModel(); KChart::Chart* m_chart; KChart::TernaryPointDiagram* m_diagram; KChart::TernaryCoordinatePlane* m_ternaryPlane; QStandardItemModel m_model; }; #endif diff --git a/examples/Widget/Advanced/mainwindow.h b/examples/Widget/Advanced/mainwindow.h index 87c827b..b3e237c 100644 --- a/examples/Widget/Advanced/mainwindow.h +++ b/examples/Widget/Advanced/mainwindow.h @@ -1,47 +1,47 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" namespace KChart{ class Widget; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = NULL ); + MainWindow( QWidget* parent = nullptr ); KChart::Widget* widget; public slots: void changeType(); void changeLeading( int leading ); void addDataset(); private: int datasetCount; }; #endif diff --git a/examples/Zoom/Keyboard/mainwindow.h b/examples/Zoom/Keyboard/mainwindow.h index ed8b737..b90c01f 100644 --- a/examples/Zoom/Keyboard/mainwindow.h +++ b/examples/Zoom/Keyboard/mainwindow.h @@ -1,41 +1,41 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" namespace KChart{ class Widget; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = NULL ); + MainWindow( QWidget* parent = nullptr ); KChart::Widget* widget; private: }; #endif diff --git a/examples/Zoom/ScrollBars/mainwindow.h b/examples/Zoom/ScrollBars/mainwindow.h index 5d58f2b..d920b4a 100644 --- a/examples/Zoom/ScrollBars/mainwindow.h +++ b/examples/Zoom/ScrollBars/mainwindow.h @@ -1,57 +1,57 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include namespace KChart { class Chart; class LineDiagram; class Legend; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); private slots: void on_zoomFactorSB_valueChanged( double factor ); void on_adjustGridCB_toggled( bool checked ); void on_rubberBandZoomCB_toggled( bool checked ); void on_hSBar_valueChanged( int value ); void on_vSBar_valueChanged( int value ); void applyNewZoomParameters(); private: KChart::Chart* m_chart; TableModel m_model; KChart::LineDiagram* m_lines; KChart::Legend* m_legend; }; #endif /* MAINWINDOW_H */ diff --git a/examples/demo/axissettings.cpp b/examples/demo/axissettings.cpp index 5f8d88e..596ad58 100644 --- a/examples/demo/axissettings.cpp +++ b/examples/demo/axissettings.cpp @@ -1,205 +1,205 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "axissettings.h" #include "ui_axissettings.h" #include #include #include #include #include #include #include #include #include using namespace KChart; class AxisSettings::Private : public QObject { Q_OBJECT public: Private( Chart* chart, AxisSettings *q ); void init(); CartesianAxis *m_currentAxis; QHash< CartesianCoordinatePlane*, QVector< CartesianAxis* > > m_axisCache; Chart *m_chart; Ui::AxisSettings ui; AxisSettings *qq; public Q_SLOTS: void currentIndexChanged( int index ); void setVisible( bool value ); void setShowMajorTickMarks( bool value ); void setShowMinorTickMarks( bool value ); private: void updateUiForCurrentAxis(); }; AxisSettings::Private::Private( Chart* chart, AxisSettings *q ) : m_chart( chart ) , qq( q ) { } void AxisSettings::Private::init() { ui.axisSelection->addItem( QIcon(), tr( "Left Y-Axis" ), CartesianAxis::Left ); ui.axisSelection->addItem( QIcon(), tr( "Right Y-Axis" ), CartesianAxis::Right ); ui.axisSelection->addItem( QIcon(), tr( "Bottom X-Axis" ), CartesianAxis::Bottom ); ui.axisSelection->addItem( QIcon(), tr( "Top X-Axis" ), CartesianAxis::Top ); connect( ui.axisSelection, SIGNAL(currentIndexChanged(int)), this, SLOT(currentIndexChanged(int)) ); connect( ui.axisVisibility, SIGNAL(toggled(bool)), this, SLOT(setVisible(bool)) ); connect( ui.majorTicks, SIGNAL(toggled(bool)), this, SLOT(setShowMajorTickMarks(bool)) ); connect( ui.minorTicks, SIGNAL(toggled(bool)), this, SLOT(setShowMinorTickMarks(bool)) ); ui.axisSelection->setCurrentIndex( 0 ); } void AxisSettings::Private::currentIndexChanged( int index ) { - m_currentAxis = 0; + m_currentAxis = nullptr; const CartesianAxis::Position pos = ( CartesianAxis::Position ) ui.axisSelection->itemData( index ).toInt(); CartesianCoordinatePlane *plane = qobject_cast< CartesianCoordinatePlane* >( m_chart->coordinatePlane() ); AbstractCartesianDiagram *diag = qobject_cast< AbstractCartesianDiagram* >( m_chart->coordinatePlane()->diagram() ); if ( plane && diag ) { QVector< CartesianAxis* > axes = diag->axes().toVector(); Q_FOREACH( CartesianAxis *axis, axes ) { if ( axis->position() == pos ) { m_currentAxis = axis; break; } } } updateUiForCurrentAxis(); } void AxisSettings::Private::updateUiForCurrentAxis() { const bool prevSignalsBlocked = ui.axisVisibility->blockSignals( true ); ui.axisVisibility->setChecked( m_currentAxis ); ui.axisVisibility->blockSignals( prevSignalsBlocked ); ui.majorTicks->setEnabled( m_currentAxis ); ui.minorTicks->setEnabled( m_currentAxis ); if ( m_currentAxis ) { const RulerAttributes rulerAttr = m_currentAxis->rulerAttributes(); ui.majorTicks->setChecked( rulerAttr.showMajorTickMarks() ); ui.minorTicks->setChecked( rulerAttr.showMinorTickMarks() ); } else { ui.majorTicks->setChecked( false ); ui.minorTicks->setChecked( false ); } } void AxisSettings::Private::setVisible( bool value ) { const CartesianAxis::Position pos = ( CartesianAxis::Position ) ui.axisSelection->itemData( ui.axisSelection->currentIndex() ).toInt(); CartesianCoordinatePlane *plane = qobject_cast< CartesianCoordinatePlane* >( m_chart->coordinatePlane() ); AbstractCartesianDiagram *diag = qobject_cast< AbstractCartesianDiagram* >( m_chart->coordinatePlane()->diagram() ); if ( plane && diag ) { QVector< CartesianAxis* > axes = m_axisCache[ plane ]; bool foundAxis = false; Q_FOREACH( CartesianAxis *axis, axes ) { if ( axis->position() == pos ) { foundAxis = true; if ( !value ) { diag->takeAxis( axis ); - m_currentAxis = 0; + m_currentAxis = nullptr; } else { diag->addAxis( axis ); m_currentAxis = axis; } break; } } if ( !foundAxis ) { Q_ASSERT( value ); CartesianAxis *axis = new CartesianAxis( diag ); axis->setPosition( pos ); diag->addAxis( axis ); m_axisCache[ plane ].append( axis ); m_currentAxis = axis; } updateUiForCurrentAxis(); m_chart->update(); } } void AxisSettings::Private::setShowMajorTickMarks( bool value ) { if ( m_currentAxis ) { RulerAttributes attr( m_currentAxis->rulerAttributes() ); attr.setShowMajorTickMarks( value ); m_currentAxis->setRulerAttributes( attr ); m_chart->update(); } } void AxisSettings::Private::setShowMinorTickMarks( bool value ) { if ( m_currentAxis ) { RulerAttributes attr( m_currentAxis->rulerAttributes() ); attr.setShowMinorTickMarks( value ); m_currentAxis->setRulerAttributes( attr ); m_chart->update(); } } AxisSettings::AxisSettings( Chart *chart, QWidget *parent ) : QWidget( parent ) , d( new Private( chart, this ) ) { d->ui.setupUi( this ); d->init(); } AxisSettings::~AxisSettings() { delete d; } void AxisSettings::diagramTypeChanged() { CartesianCoordinatePlane *plane = qobject_cast< CartesianCoordinatePlane* >( d->m_chart->coordinatePlane() ); setEnabled( plane ); if ( plane ) d->currentIndexChanged( d->ui.axisSelection->currentIndex() ); } #include "axissettings.moc" diff --git a/examples/demo/axissettings.h b/examples/demo/axissettings.h index 84c4f8b..a47b75b 100644 --- a/examples/demo/axissettings.h +++ b/examples/demo/axissettings.h @@ -1,46 +1,46 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef AXISSETTINGS_H #define AXISSETTINGS_H #include namespace KChart { class Chart; } class AxisSettings : public QWidget { Q_OBJECT public: - explicit AxisSettings( KChart::Chart *chart, QWidget *parent = 0 ); + explicit AxisSettings( KChart::Chart *chart, QWidget *parent = nullptr ); ~AxisSettings(); public Q_SLOTS: void diagramTypeChanged(); private: class Private; Private *d; }; #endif // AXISSETTINGS_H diff --git a/examples/demo/colorslider.h b/examples/demo/colorslider.h index c6191a7..b8b7739 100644 --- a/examples/demo/colorslider.h +++ b/examples/demo/colorslider.h @@ -1,56 +1,56 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef COLORSLIDER_H #define COLORSLIDER_H #include class ColorSlider : public QAbstractSlider { Q_OBJECT Q_PROPERTY( QColor startColor READ startColor WRITE setStartColor NOTIFY startColorChanged ) Q_PROPERTY( QColor endColor READ endColor WRITE setStartColor NOTIFY endColorChanged ) public: - explicit ColorSlider( QWidget *parent = 0 ); + explicit ColorSlider( QWidget *parent = nullptr ); ~ColorSlider(); QColor startColor() const; void setStartColor( const QColor &color ); QColor endColor() const; void setEndColor( const QColor &color ); QSize sizeHint() const Q_DECL_OVERRIDE; protected: void paintEvent( QPaintEvent *event ) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *event ) Q_DECL_OVERRIDE; void mouseReleaseEvent(QMouseEvent *event ) Q_DECL_OVERRIDE; void mouseMoveEvent( QMouseEvent *event ) Q_DECL_OVERRIDE; Q_SIGNALS: void startColorChanged(); void endColorChanged(); public Q_SLOTS: private: class Private; Private *d; }; #endif // COLORSLIDER_H diff --git a/examples/demo/datasetsettings.h b/examples/demo/datasetsettings.h index 649d4f9..67321d6 100644 --- a/examples/demo/datasetsettings.h +++ b/examples/demo/datasetsettings.h @@ -1,51 +1,51 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DATASETSETTINGS_H #define DATASETSETTINGS_H #include namespace KChart{ class Chart; } class DatasetSettings : public QWidget { Q_OBJECT Q_PROPERTY( int datasetCount READ datasetCount WRITE setDatasetCount NOTIFY datasetCountChanged() ) public: - explicit DatasetSettings( KChart::Chart *chart, QWidget *parent = 0 ); + explicit DatasetSettings( KChart::Chart *chart, QWidget *parent = nullptr ); ~DatasetSettings(); int datasetCount() const; public Q_SLOTS: void setDatasetCount( int value ); void indexChanged( int index ); void diagramTypeChanged(); Q_SIGNALS: void datasetCountChanged(); private: class Private; Private *d; }; #endif // DATASETSETTINGS_H diff --git a/examples/demo/datavaluesettings.h b/examples/demo/datavaluesettings.h index 9ef060a..b0043a7 100644 --- a/examples/demo/datavaluesettings.h +++ b/examples/demo/datavaluesettings.h @@ -1,48 +1,48 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DATAVALUESETTINGS_H #define DATAVALUESETTINGS_H #include namespace KChart { class Chart; } class DataValueSettings : public QWidget { Q_OBJECT public: - DataValueSettings( KChart::Chart *chart, QWidget* parent = 0 ); + DataValueSettings( KChart::Chart *chart, QWidget* parent = nullptr ); ~DataValueSettings(); public Q_SLOTS: void refresh(); private: class Private; Private *d; }; #endif /* MAINWINDOW_H */ diff --git a/examples/demo/diagramsettings.cpp b/examples/demo/diagramsettings.cpp index 03a7ff3..10441b8 100644 --- a/examples/demo/diagramsettings.cpp +++ b/examples/demo/diagramsettings.cpp @@ -1,257 +1,257 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "diagramsettings.h" #include "ui_diagramsettings.h" #include "gradientdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace KChart; class DiagramSettings::Private : public QObject { Q_OBJECT public: - Private( Chart *chart = 0, DiagramSettings *q = 0 ); + Private( Chart *chart = nullptr, DiagramSettings *q = nullptr ); ~Private(); void init(); void setThreeDData( const AbstractThreeDAttributes &attr ); Chart *m_chart; Ui::DiagramSettings *ui; DiagramSettings *qq; public Q_SLOTS: void changeThreeD(); void changeBackgroundColor(); void changeBackgroundVisibility(); void changeAutoGradient(); }; DiagramSettings::Private::Private( Chart *chart, DiagramSettings *q ) : QObject( q ) , m_chart( chart ) , ui(new Ui::DiagramSettings) , qq( q ) { } DiagramSettings::Private::~Private() { } void DiagramSettings::Private::changeAutoGradient() { changeThreeD(); } void DiagramSettings::Private::changeThreeD() { if ( m_chart && m_chart->coordinatePlane() && m_chart->coordinatePlane()->diagram() ) { BarDiagram *bars = qobject_cast< BarDiagram* >( m_chart->coordinatePlane()->diagram() ); LineDiagram *lines = qobject_cast< LineDiagram* >( m_chart->coordinatePlane()->diagram() ); PieDiagram *pie = qobject_cast< PieDiagram* >( m_chart->coordinatePlane()->diagram() ); if ( bars ) { ThreeDBarAttributes td( bars->threeDBarAttributes() ); td.setEnabled( ui->threeDSelector->checkState() == Qt::Checked ); td.setUseShadowColors( true ); td.setDepth( ui->barHeightInput->value() ); td.setThreeDBrushEnabled( ui->autoGradient->checkState() == Qt::Checked ); bars->setThreeDBarAttributes( td ); m_chart->update(); } else if ( lines ) { ThreeDLineAttributes td( lines->threeDLineAttributes() ); td.setEnabled( ui->threeDSelector->checkState() == Qt::Checked ); td.setDepth( ui->barHeightInput->value() ); td.setThreeDBrushEnabled( ui->autoGradient->checkState() == Qt::Checked ); lines->setThreeDLineAttributes( td ); m_chart->update(); } else if ( pie ) { ThreeDPieAttributes td( pie->threeDPieAttributes() ); td.setEnabled( ui->threeDSelector->checkState() == Qt::Checked ); td.setDepth( ui->barHeightInput->value() ); td.setThreeDBrushEnabled( ui->autoGradient->checkState() == Qt::Checked ); pie->setThreeDPieAttributes( td ); m_chart->update(); } } } void DiagramSettings::Private::changeBackgroundColor() { if ( m_chart && m_chart->coordinatePlane() && m_chart->coordinatePlane()->diagram() ) { BackgroundAttributes bat = m_chart->coordinatePlane()->backgroundAttributes(); bat.setVisible( true ); ui->visibleBtn->setChecked( true ); if ( ui->color->isChecked() ) { QBrush setBrush = bat.brush(); const QColor color = QColorDialog::getColor( setBrush.color(), qq, tr( "Choose new color" ) ); if ( !color.isValid() ) return; bat.setBrush( color ); QPalette palette = ui->diagramBackground->palette(); palette.setBrush( QPalette::Button, color ); ui->diagramBackground->setPalette( palette ); } else if ( ui->textureBtn->isChecked() ) { //QBrush setBrush = m_chart->coordinatePlane()->diagram()->brush( index ); QImage texture; const QString filename = QFileDialog::getOpenFileName( qq, tr( "Choose Texture" ), QString(), tr( "Images (*.png *.xpm *.jpg)" ) ); if ( filename.isEmpty() ) return; texture = QImage( filename ); bat.setBrush( texture ); QPalette palette = ui->diagramBackground->palette(); palette.setBrush( QPalette::Button, QBrush( texture ) ); ui->diagramBackground->setPalette( palette ); } else { QBrush setBrush = bat.brush(); QGradient grad; QLinearGradient lGrad; lGrad.setColorAt( 0, Qt::black ); lGrad.setColorAt( 1, setBrush.color() ); grad = lGrad; if ( setBrush.gradient() ) grad = *setBrush.gradient(); const QGradient &color = GradientDialog::getGradient( grad, qq, tr( "Choose new color" ) ); bat.setBrush( color ); QPalette palette = ui->diagramBackground->palette(); palette.setBrush( QPalette::Button, QBrush( color ) ); ui->diagramBackground->setPalette( palette ); } bat.setVisible( true ); m_chart->coordinatePlane()->setBackgroundAttributes( bat ); qq->update(); } } void DiagramSettings::Private::changeBackgroundVisibility() { if ( m_chart && m_chart->coordinatePlane() && m_chart->coordinatePlane()->diagram() ) { BackgroundAttributes bat = m_chart->coordinatePlane()->backgroundAttributes(); bat.setVisible( ui->visibleBtn->checkState() == Qt::Checked ); m_chart->coordinatePlane()->setBackgroundAttributes( bat ); m_chart->update(); } } void DiagramSettings::Private::init() { ui->setupUi( qq ); #ifdef Q_OS_LINUX ui->diagramBackground->setStyle( QStyleFactory::create( QStringLiteral( "cleanlooks" ) ) ); #endif connect( ui->threeDSelector, SIGNAL(toggled(bool)), this, SLOT(changeThreeD()) ); connect( ui->diagramBackground, SIGNAL(clicked()), this, SLOT(changeBackgroundColor()) ); connect( ui->visibleBtn, SIGNAL(toggled(bool)), this, SLOT(changeBackgroundVisibility()) ); connect( ui->barHeightInput, SIGNAL(valueChanged(int)), this, SLOT(changeThreeD()) ); connect( ui->autoGradient, SIGNAL(toggled(bool)), this, SLOT(changeAutoGradient()) ); qq->refreshSettings(); } void DiagramSettings::Private::setThreeDData( const AbstractThreeDAttributes &attr ) { ui->threeDSelector->setCheckState( attr.isEnabled() ? Qt::Checked : Qt::Unchecked ); ui->autoGradient->setCheckState( attr.isThreeDBrushEnabled() ? Qt::Checked : Qt::Unchecked ); ui->barHeightInput->setValue( attr.depth() ); } DiagramSettings::DiagramSettings( Chart *chart, QWidget *parent ) : QWidget( parent ) , d( new Private( chart, this ) ) { d->init(); } DiagramSettings::~DiagramSettings() { delete d; } void DiagramSettings::refreshSettings() { if ( d->m_chart && d->m_chart->coordinatePlane() && d->m_chart->coordinatePlane()->diagram() ) { BarDiagram *bars = qobject_cast< BarDiagram* >( d->m_chart->coordinatePlane()->diagram() ); LineDiagram *lines = qobject_cast< LineDiagram* >( d->m_chart->coordinatePlane()->diagram() ); PieDiagram *pie = qobject_cast< PieDiagram* >( d->m_chart->coordinatePlane()->diagram() ); if ( bars ) { const AbstractThreeDAttributes &td = bars->threeDBarAttributes(); d->setThreeDData( td ); } else if ( lines ) { const AbstractThreeDAttributes &td = lines->threeDLineAttributes(); d->setThreeDData( td ); } else if ( pie ) { const AbstractThreeDAttributes &td = pie->threeDPieAttributes(); d->setThreeDData( td ); } BackgroundAttributes bat = d->m_chart->coordinatePlane()->backgroundAttributes(); QBrush setBrush = bat.brush(); QPalette palette = d->ui->diagramBackground->palette(); if ( bat.isVisible() ) palette.setBrush( QPalette::Button, setBrush ); else palette.setBrush( QPalette::Button, this->palette().brush( QPalette::Button ) ); d->ui->diagramBackground->setPalette( palette ); } } #include "diagramsettings.moc" diff --git a/examples/demo/diagramsettings.h b/examples/demo/diagramsettings.h index eac5e29..79f6eaa 100644 --- a/examples/demo/diagramsettings.h +++ b/examples/demo/diagramsettings.h @@ -1,45 +1,45 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DIAGRAMSETTINGS_H #define DIAGRAMSETTINGS_H #include namespace KChart{ class Chart; } class DiagramSettings : public QWidget { Q_OBJECT public: - explicit DiagramSettings( KChart::Chart* chart, QWidget *parent = 0 ); + explicit DiagramSettings( KChart::Chart* chart, QWidget *parent = nullptr ); ~DiagramSettings(); public Q_SLOTS: void refreshSettings(); private: class Private; Private *d; }; #endif // DIAGRAMSETTINGS_H diff --git a/examples/demo/diagramtypedialog.h b/examples/demo/diagramtypedialog.h index 9a1a936..d8684e5 100644 --- a/examples/demo/diagramtypedialog.h +++ b/examples/demo/diagramtypedialog.h @@ -1,51 +1,51 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DIAGRAMTYPEDIALOG_H #define DIAGRAMTYPEDIALOG_H #include #include class QAbstractItemModel; namespace KChart { class Chart; } class DiagramTypeDialog : public QDialog { Q_OBJECT public: enum DiagramType{ Bar, LyingBar, Line, Plotter, Pie }; enum Subtype{ Normal, Percent, Stacked }; - explicit DiagramTypeDialog( KChart::Chart *chart, QWidget *parent = 0 ); + explicit DiagramTypeDialog( KChart::Chart *chart, QWidget *parent = nullptr ); ~DiagramTypeDialog(); void setDefaultModels( QHash< DiagramType, QAbstractItemModel* > models ); Q_SIGNALS: void diagramTypeChanged( DiagramType, Subtype ); private: class Private; Private *d; }; #endif // DIAGRAMTYPEDIALOG_H diff --git a/examples/demo/gradientdialog.h b/examples/demo/gradientdialog.h index caa2b48..1475abc 100644 --- a/examples/demo/gradientdialog.h +++ b/examples/demo/gradientdialog.h @@ -1,46 +1,46 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef GRADIENTDIALOG_H #define GRADIENTDIALOG_H #include #include class GradientDialog : public QDialog { Q_OBJECT Q_PROPERTY( QGradient gradient READ gradient WRITE setGradient NOTIFY gradientChanged() ) public: - explicit GradientDialog( QWidget *parent = 0 ); + explicit GradientDialog( QWidget *parent = nullptr ); ~GradientDialog(); QGradient gradient() const; void setGradient( const QGradient &gradient ); - static QGradient getGradient( const QGradient &gradient, QWidget *parent = 0, const QString &title = QString() ); + static QGradient getGradient( const QGradient &gradient, QWidget *parent = nullptr, const QString &title = QString() ); Q_SIGNALS: void gradientChanged(); private: class Private; Private *d; }; #endif // GRADIENTDIALOG_H diff --git a/examples/demo/mainwindow.cpp b/examples/demo/mainwindow.cpp index bee7c8a..ff60151 100644 --- a/examples/demo/mainwindow.cpp +++ b/examples/demo/mainwindow.cpp @@ -1,165 +1,165 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "datasetsettings.h" #include "diagramsettings.h" #include "datavaluesettings.h" #include "diagramtypedialog.h" #include "axissettings.h" using namespace KChart; class MainWindow::Private : public QObject { Q_OBJECT Q_PROPERTY( int datasetCount READ datasetCount NOTIFY datasetCountChanged ) public: int datasetCount() const; enum ChartType{ Bar, Line, Plotter, Polar, Pie }; - Private( MainWindow *q = 0 ); + Private( MainWindow *q = nullptr ); Chart *m_chartWin; CartesianCoordinatePlane *m_cartPlane; TableModel *m_model; AbstractDiagram *m_curDiagram; ChartType m_type; QHash< DiagramTypeDialog::DiagramType, QAbstractItemModel* > m_modelTable; MainWindow *qq; Q_SIGNALS: void datasetCountChanged( int ); public Q_SLOTS: void changedChartType(); }; MainWindow::Private::Private( MainWindow *q ) : m_chartWin( new Chart( q ) ) , m_cartPlane( new CartesianCoordinatePlane ) , m_model( new TableModel( q ) ) , m_curDiagram( new BarDiagram( q, m_cartPlane ) ) , m_type( Bar ) , qq( q ) { m_chartWin->replaceCoordinatePlane( m_cartPlane ); m_model->loadFromCSV( ":/data/barSimple.csv" ); m_cartPlane->replaceDiagram( m_curDiagram ); m_curDiagram->setModel( m_model ); m_modelTable[ DiagramTypeDialog::Bar ] = m_model; m_modelTable[ DiagramTypeDialog::LyingBar ] = m_model; TableModel *lineModel = new TableModel( q ); lineModel->loadFromCSV( ":/data/lineSimple.csv" ); m_modelTable[ DiagramTypeDialog::Line ] = lineModel; m_modelTable[ DiagramTypeDialog::Plotter ] = lineModel; TableModel *pieModel = new TableModel( q ); pieModel->loadFromCSV( ":/data/pieSimple.csv" ); m_modelTable[ DiagramTypeDialog::Pie ] = pieModel; DiagramTypeDialog *diagramTypeSettings = new DiagramTypeDialog( m_chartWin, qq ); diagramTypeSettings->setDefaultModels( m_modelTable ); QDockWidget *diagramTypeSettingsDock = new QDockWidget( tr( "DiagramType" ), qq ); diagramTypeSettingsDock->setWidget( diagramTypeSettings ); diagramTypeSettingsDock->setFloating( false ); connect( diagramTypeSettings, SIGNAL(diagramTypeChanged(DiagramType,Subtype)), this, SLOT(changedChartType()) ); q->addDockWidget( Qt::LeftDockWidgetArea, diagramTypeSettingsDock ); AxisSettings *axisSettings = new AxisSettings( m_chartWin, qq ); QDockWidget *axisSettingsDock = new QDockWidget( tr( "AxisSettings" ), qq ); axisSettingsDock->setWidget( axisSettings ); axisSettingsDock->setFloating( false ); q->addDockWidget( Qt::LeftDockWidgetArea, axisSettingsDock ); connect( diagramTypeSettings, SIGNAL(diagramTypeChanged(DiagramType,Subtype)), axisSettings, SLOT(diagramTypeChanged()) ); DatasetSettings *setSettings = new DatasetSettings( m_chartWin, qq ); QDockWidget *setSettingsDock = new QDockWidget( tr( "DatasetSettings" ), qq ); setSettingsDock->setWidget( setSettings ); setSettingsDock->setFloating( false ); q->addDockWidget( Qt::LeftDockWidgetArea, setSettingsDock ); connect( this, SIGNAL(datasetCountChanged(int)), setSettings, SLOT(setDatasetCount(int)) ); connect( diagramTypeSettings, SIGNAL(diagramTypeChanged(DiagramType,Subtype)), setSettings, SLOT(diagramTypeChanged()) ); setSettings->setDatasetCount( m_model->columnCount() ); DiagramSettings *diagSettings = new DiagramSettings( m_chartWin, qq ); QDockWidget *diagSettingsDock = new QDockWidget( tr( "DiagramSettings" ), qq ); diagSettingsDock->setWidget( diagSettings ); diagSettingsDock->setFloating( false ); q->addDockWidget( Qt::LeftDockWidgetArea, diagSettingsDock ); connect( diagramTypeSettings, SIGNAL(diagramTypeChanged(DiagramType,Subtype)), diagSettings, SLOT(refreshSettings()) ); DataValueSettings *dataValueSettings = new DataValueSettings( m_chartWin, qq ); QDockWidget *dataValueSettingsDock = new QDockWidget( tr( "DataValueSettings" ), qq ); dataValueSettingsDock->setWidget( dataValueSettings ); dataValueSettingsDock->setFloating( false ); q->addDockWidget( Qt::RightDockWidgetArea, dataValueSettingsDock ); connect( diagramTypeSettings, SIGNAL(diagramTypeChanged(DiagramType,Subtype)), dataValueSettings, SLOT(refresh()) ); } int MainWindow::Private::datasetCount() const { if ( m_chartWin->coordinatePlane() && m_chartWin->coordinatePlane()->diagram() ) { const AbstractDiagram *diag = m_chartWin->coordinatePlane()->diagram(); if ( !diag->model() ) return 0; return diag->model()->columnCount() / diag->datasetDimension(); } return 0; } void MainWindow::Private::changedChartType() { Q_EMIT datasetCountChanged( datasetCount() ); } MainWindow::MainWindow( QWidget *parent ) : QMainWindow( parent ) , d( new Private( this ) ) { setCentralWidget( d->m_chartWin ); } MainWindow::~MainWindow() { delete d; } #include "mainwindow.moc" diff --git a/examples/demo/mainwindow.h b/examples/demo/mainwindow.h index fdd7de4..fa1ea40 100644 --- a/examples/demo/mainwindow.h +++ b/examples/demo/mainwindow.h @@ -1,41 +1,41 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include class MainWindow : public QMainWindow { Q_OBJECT public: - explicit MainWindow( QWidget *parent = 0 ); + explicit MainWindow( QWidget *parent = nullptr ); ~MainWindow(); signals: public slots: private: class Private; Private *d; }; #endif // MAINWINDOW_H diff --git a/examples/tools/TableModel.h b/examples/tools/TableModel.h index 4653edd..835a6f3 100644 --- a/examples/tools/TableModel.h +++ b/examples/tools/TableModel.h @@ -1,149 +1,149 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TABLEMODEL_H #define TABLEMODEL_H #include #include #include #include "testtools_export.h" #include /** TableModel uses a simple rectangular vector of vectors to represent a data table that can be displayed in regular Qt Interview views. Additionally, it provides a method to load CSV files exported by OpenOffice Calc in the default configuration. This allows to prepare test data using spreadsheet software. It expects the CSV files in the subfolder ./modeldata. If the application is started from another location, it will ask for the location of the model data files. */ class TESTTOOLS_EXPORT TableModel : public QAbstractTableModel { Q_OBJECT public: - TableModel( QObject* parent = 0 ); + TableModel( QObject* parent = nullptr ); ~TableModel(); /** Return header data from the model. The model will use the first data row and the first data column of the physical data as source of column and row header data. This data is not exposed as model data, that means, the first model row and column will start at index (0, 0). */ QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const Q_DECL_OVERRIDE; int rowCount( const QModelIndex& parent = QModelIndex() ) const Q_DECL_OVERRIDE; int columnCount( const QModelIndex& parent = QModelIndex() ) const Q_DECL_OVERRIDE; QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const Q_DECL_OVERRIDE; bool setData( const QModelIndex& index, const QVariant& value, int role = Qt::EditRole ) Q_DECL_OVERRIDE; /** Load the table from a comma separated file. * * The files are supposed to be Unicode (UTF8), have commas (',') as * delimiters, and quotes ('"') as text delimiters. All lines are expected * to provide the same number of fields. HINT: This is the default way * OO.o-Calc exports CSV files. * The cell data is expected to be floating point values, except for the * first row and the first column, where string values are exected (those * will be used as axis descriptors). If values cannot be converted to * qreals, their string representation will be used. * * @returns true if successful, false otherwise * * @sa titleText */ bool loadFromCSV( const QString& filename ); /** * If both DataHasHorizontalHeaders and DataHasVerticalHeaders is * set true (that's the default setting) then loadFromCSV will interpret * the first field in the first row as title-text. * If no such field is found the loadFromCSV will set the title text to * an empty string. * * The text is stored and can be retrieved via titleText(), but the model * itself does nothing else with it: The calling application may use this * method and e.g. display the text as header or as title of the Legend * or as caption of the window ... * * @sa loadFromCSV */ const QString titleText() const { return m_titleText; } /** * Setting the title text has no effect except that the text * can then be retrieved via titleText. * * TableModel is just storing this data but it does nothing * else with it, nor does Qt's IndeView model make use of it. */ void setTitleText( const QString& txt ) { m_titleText = txt; } /** Make the model invalid, that is, provide no data. */ void clear(); /** * Set to false if the data has no horizontal header */ void setDataHasHorizontalHeaders( bool value ) { m_dataHasHorizontalHeaders = value; } /** * Set to false if the data has no vertical header */ void setDataHasVerticalHeaders( bool value ) { m_dataHasVerticalHeaders = value; } /** * setSupplyHeaderData(false) allows to prevent the model from supplying header data, * even if parsing found any */ void setSupplyHeaderData( bool value ) { m_supplyHeaderData = value; } protected: // the vector of rows: QVector< QVector > m_rows; private: // the header data: QStringList m_horizontalHeaderData; QStringList m_verticalHeaderData; QString m_titleText; bool m_dataHasHorizontalHeaders; bool m_dataHasVerticalHeaders; bool m_supplyHeaderData; }; #endif diff --git a/qtests/AttributesModel/main.cpp b/qtests/AttributesModel/main.cpp index 008c0fc..9b2d6c3 100644 --- a/qtests/AttributesModel/main.cpp +++ b/qtests/AttributesModel/main.cpp @@ -1,164 +1,164 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include using namespace KChart; class TestKChartAttributesModel : public QObject { Q_OBJECT private slots: void initTestCase() { TableModel *tableModel = new TableModel( this ); tableModel->loadFromCSV( ":/data" ); tableModel->setSupplyHeaderData( false ); m_model = tableModel; - m_plane = new CartesianCoordinatePlane(0); + m_plane = new CartesianCoordinatePlane(nullptr); m_bars = new BarDiagram(); m_bars->setModel( m_model ); m_lines = new LineDiagram(); m_lines->setModel( m_model ); } void testKChartAttributesModelSetSimpleData() { QModelIndex idx = m_model->index( 0, 2, QModelIndex() ); DataValueAttributes a = m_bars->dataValueAttributes( idx ); QCOMPARE( a.isVisible(), false ); a.setVisible( true ); m_bars->setDataValueAttributes( 2, a ); a = m_bars->dataValueAttributes( idx ); QCOMPARE( a.isVisible(), true ); } void testKChartAttributesModelTestPrivateModel() { // Private is now default //m_lines->usePrivateAttributes( true ); // now we should be getting defaults again QModelIndex idx = m_bars->model()->index( 0, 2, QModelIndex() ); DataValueAttributes a = m_lines->dataValueAttributes( idx ); QCOMPARE( a.isVisible(), false ); // set the defaults on the other model and make sure they have // an effect m_bars->setDataValueAttributes( 2, a ); DataValueAttributes b = m_bars->dataValueAttributes( idx ); QCOMPARE( b.isVisible(), false ); // now change a, set it on the lines, check that the bars // didn't change a.setVisible( true ); m_lines->setDataValueAttributes( 2, a ); QCOMPARE( a.isVisible(), true ); b = m_bars->dataValueAttributes( idx ); QCOMPARE( b.isVisible(), false ); } void testKChartAttributesModelTestSharedModel() { // Note: a SHARED atributes-model must be owned by the USER // but it may not be owned by any of the diagrams // see API docu of AbstractDiagram::setAttributesModel() - AttributesModel *attrsmodel = new AttributesModel( m_model, 0 ); + AttributesModel *attrsmodel = new AttributesModel( m_model, nullptr ); m_lines->setAttributesModel( attrsmodel ); m_bars->setAttributesModel( attrsmodel ); QModelIndex idx = m_model->index( 0, 2, QModelIndex() ); DataValueAttributes attrLin = m_lines->dataValueAttributes( idx ); attrLin.setVisible( false ); m_lines->setDataValueAttributes( idx, attrLin ); DataValueAttributes attrBar = m_bars->dataValueAttributes( idx ); QCOMPARE( attrBar.isVisible(), false ); attrLin.setVisible( true ); m_lines->setDataValueAttributes( idx, attrLin ); attrBar = m_bars->dataValueAttributes( idx ); QCOMPARE( attrBar.isVisible(), true ); } void testKChartAttributesModelTestSharedFromStart() { delete m_lines; delete m_bars; delete m_plane; - m_plane = new CartesianCoordinatePlane(0); + m_plane = new CartesianCoordinatePlane(nullptr); m_bars = new BarDiagram(); m_bars->setModel( m_model ); m_lines = new LineDiagram(); m_lines->setModel( m_model ); AttributesModel* attrsmodel = new AttributesModel( m_model, m_plane ); m_lines->setAttributesModel(attrsmodel); m_bars->setAttributesModel(attrsmodel); QModelIndex idx = m_bars->model()->index( 0, 2, QModelIndex() ); DataValueAttributes a = m_lines->dataValueAttributes( idx ); DataValueAttributes b = m_bars->dataValueAttributes( idx ); QCOMPARE( a.isVisible(), false ); QCOMPARE( b.isVisible(), false ); a.setVisible( true ); QCOMPARE( a.isVisible(), true ); m_lines->setDataValueAttributes( 2, a ); b = m_bars->dataValueAttributes( idx ); QCOMPARE( b.isVisible(), true ); // Should be true by sharing } void testKChartAttributesModelTestPrivate() { m_lines->setAttributesModel( new AttributesModel(m_model,m_lines) ); m_bars->setAttributesModel( new AttributesModel(m_model,m_bars) ); QModelIndex idx = m_lines->model()->index( 0, 2, QModelIndex() ); DataValueAttributes a = m_lines->dataValueAttributes( idx ); QCOMPARE( a.isVisible(), false ); // we got a default model again a.setVisible( true ); m_lines->setDataValueAttributes( 2, a ); // should now have propagated to the bars DataValueAttributes b = m_bars->dataValueAttributes( idx ); QCOMPARE( b.isVisible(), false ); // No sharing } void cleanupTestCase() { delete m_plane; } private: QAbstractItemModel *m_model; CartesianCoordinatePlane* m_plane; BarDiagram *m_bars; LineDiagram *m_lines; }; QTEST_MAIN(TestKChartAttributesModel) #include "main.moc" diff --git a/qtests/AxisOwnership/main.cpp b/qtests/AxisOwnership/main.cpp index 227995d..e354e72 100644 --- a/qtests/AxisOwnership/main.cpp +++ b/qtests/AxisOwnership/main.cpp @@ -1,163 +1,163 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include using namespace KChart; class TestAxisOwnership: public QObject { Q_OBJECT private slots: void initTestCase() { - m_chart = new Chart(0); + m_chart = new Chart(nullptr); m_bars = new BarDiagram(); m_lines = new LineDiagram(); m_tableModel = new TableModel( this ); m_tableModel->loadFromCSV( ":/data" ); m_bars->setModel( m_tableModel ); m_lines->setModel( m_tableModel ); m_chart->coordinatePlane()->replaceDiagram( m_bars ); m_chart->coordinatePlane()->addDiagram( m_lines ); } void testIntialOwnership() { m_axis = new CartesianAxis( m_bars ); QCOMPARE( m_axis->diagram(), static_cast( m_bars ) ); QVERIFY( m_axis->observedBy( m_bars ) ); } void testTakingOwnership() { m_axis = new CartesianAxis(); m_bars->addAxis( m_axis ); QCOMPARE( m_axis->diagram(), static_cast( m_bars ) ); QVERIFY( m_axis->observedBy( m_bars ) ); QVERIFY( !m_axis->observedBy( m_lines ) ); } void testGivingUpOwnership() { m_axis = new CartesianAxis(); m_bars->addAxis( m_axis ); m_lines->addAxis( m_axis ); QCOMPARE( m_axis->diagram(), static_cast( m_bars ) ); QVERIFY( m_axis->observedBy( m_bars ) ); QVERIFY( m_axis->observedBy( m_lines ) ); m_bars->takeAxis( m_axis ); QCOMPARE( m_axis->diagram(), static_cast( m_lines ) ); QVERIFY( m_axis->observedBy( m_lines ) ); QVERIFY( !m_axis->observedBy( m_bars ) ); } void testReplacing() { m_axis = new CartesianAxis; m_lines->addAxis( m_axis ); m_chart->coordinatePlane()->takeDiagram( m_bars ); QVERIFY( m_axis->observedBy( m_lines ) ); QVERIFY( !m_axis->observedBy( m_bars ) ); QPointer p( m_lines); m_chart->coordinatePlane()->replaceDiagram( m_bars, m_lines ); QVERIFY( !p ); - AbstractDiagram * nullDiagram = 0; + AbstractDiagram * nullDiagram = nullptr; QCOMPARE( m_axis->diagram(), static_cast( nullDiagram ) ); QVERIFY( !m_axis->observedBy( m_bars ) ); } void testReplacingWithPropagation() { initTestCase(); m_axis = new CartesianAxis; m_lines->addAxis( m_axis ); m_bars->addAxis( m_axis ); m_chart->coordinatePlane()->takeDiagram( m_bars ); QCOMPARE( m_axis->diagram(), static_cast( m_lines ) ); QVERIFY( m_axis->observedBy( m_bars ) ); QPointer p( m_lines); m_chart->coordinatePlane()->replaceDiagram( m_bars, m_lines ); QVERIFY( !p ); QCOMPARE( m_axis->diagram(), static_cast( m_bars ) ); QVERIFY( m_axis->observedBy( m_bars ) ); } void testAxisDeletion() { initTestCase(); m_axis = new CartesianAxis; m_lines->addAxis( m_axis ); CartesianAxisList list = m_lines->axes(); QVERIFY( !list.isEmpty() ); delete m_axis; list = m_lines->axes(); QVERIFY( list.isEmpty() ); } void testAxisDeletionAndUnregister() { initTestCase(); m_axis = new CartesianAxis; m_lines->addAxis( m_axis ); m_bars->addAxis( m_axis ); CartesianAxisList list = m_lines->axes(); QVERIFY( !list.isEmpty() ); delete m_axis; list = m_lines->axes(); QVERIFY( list.isEmpty() ); list = m_bars->axes(); QVERIFY( list.isEmpty() ); } void testFreeStanding() { initTestCase(); m_axis = new CartesianAxis; m_bars->addAxis( m_axis ); BarDiagram *bars2 = new BarDiagram; bars2->setModel( m_tableModel ); m_chart->coordinatePlane()->replaceDiagram( bars2 ); m_axis->sizeHint(); } void cleanupTestCase() { } private: Chart *m_chart; BarDiagram *m_bars; LineDiagram *m_lines; CartesianAxis *m_axis; TableModel *m_tableModel; }; QTEST_MAIN(TestAxisOwnership) #include "main.moc" diff --git a/qtests/BarDiagrams/main.cpp b/qtests/BarDiagrams/main.cpp index f7befa4..6622f6e 100644 --- a/qtests/BarDiagrams/main.cpp +++ b/qtests/BarDiagrams/main.cpp @@ -1,203 +1,203 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include using namespace KChart; class TestBarDiagrams: public QObject { Q_OBJECT private slots: void initTestCase() { - m_chart = new Chart(0); - m_model = new TableModel( 0 ); + m_chart = new Chart(nullptr); + m_model = new TableModel( nullptr ); m_model->loadFromCSV( ":/data" ); m_bars = new BarDiagram(); m_bars->setModel( m_model ); m_chart->coordinatePlane()->replaceDiagram( m_bars ); } void testBarDiagramType() { QCOMPARE( m_bars->type(), BarDiagram::Normal ); m_bars->setType( BarDiagram::Stacked ); QCOMPARE( m_bars->type(), BarDiagram::Stacked ); m_bars->setType( BarDiagram::Percent ); QCOMPARE( m_bars->type(), BarDiagram::Percent ); #ifdef TEMPORARILY_REMOVED m_bars->setType( BarDiagram::Rows ); QCOMPARE( m_bars->type(), BarDiagram::Rows ); #endif //reset to normal m_bars->setType( BarDiagram::Normal ); } void testBarAttributesLevelSettings() { //check segments const int rows = m_bars->model()->rowCount(); QCOMPARE( m_bars->numberOfAbscissaSegments(), rows ); const int cols = m_bars->model()->columnCount(); QCOMPARE( m_bars->numberOfOrdinateSegments(), cols ); QModelIndex idx = m_model->index(rows-3, cols-3, QModelIndex()); // create attribut BarAttributes ba( m_bars->barAttributes() ); BarAttributes baCol( m_bars->barAttributes() ); BarAttributes baIndex( m_bars->barAttributes() ); // modify at different level and compare baCol.setBarGapFactor(2 ); baIndex.setBarGapFactor( 3 ); m_bars->setBarAttributes( ba ); m_bars->setBarAttributes( cols-2, baCol ); m_bars->setBarAttributes( idx, baIndex ); QVERIFY( m_bars->barAttributes() != m_bars->barAttributes(cols-2) ); QVERIFY( m_bars->barAttributes() != m_bars->barAttributes(idx) ); QVERIFY( m_bars->barAttributes(cols-2) != m_bars->barAttributes(idx) ); QCOMPARE( m_bars->barAttributes(), ba ); QCOMPARE( m_bars->barAttributes( cols - 2 ), baCol ); QCOMPARE( m_bars->barAttributes( idx ), baIndex ); // try and override the cols and index level - should not work m_bars->setBarAttributes( ba ); QVERIFY( m_bars->barAttributes().barGapFactor() == 0.4 ); QVERIFY( m_bars->barAttributes( cols-2 ).barGapFactor() == 2 ); QVERIFY( m_bars->barAttributes( idx ).barGapFactor() == 3 ); } void testBarAttributesValueSettings() { BarAttributes ba( m_bars->barAttributes() ); // check default values QVERIFY( ba.fixedDataValueGap() == 6 ); QVERIFY( ba.useFixedDataValueGap() == false ); QVERIFY( ba.fixedValueBlockGap() == 24 ); QVERIFY( ba.useFixedValueBlockGap() == false ); QVERIFY( ba.fixedBarWidth() == -1 ); QVERIFY( ba.useFixedBarWidth() == false ); QVERIFY( ba.drawSolidExcessArrows() == false ); QVERIFY( ba.groupGapFactor() == 2.0 ); QVERIFY( ba.barGapFactor() == 0.4 ); //change settings ba.setFixedDataValueGap( 7 ); ba.setUseFixedDataValueGap( true ); ba.setFixedValueBlockGap( 25 ); ba.setUseFixedValueBlockGap( true ); ba.setFixedBarWidth( 1 ); ba.setUseFixedBarWidth( true ); ba.setDrawSolidExcessArrows( true ); //not implemented yet ba.setGroupGapFactor( 2 ); ba.setBarGapFactor( 1 ); m_bars->setBarAttributes( ba ); // get new values QVERIFY( m_bars->barAttributes().fixedDataValueGap() == 7 ); QVERIFY( m_bars->barAttributes().useFixedDataValueGap() == true ); QVERIFY( m_bars->barAttributes().fixedValueBlockGap() == 25 ); QVERIFY( m_bars->barAttributes().useFixedValueBlockGap() == true ); QVERIFY( m_bars->barAttributes().fixedBarWidth() == 1 ); QVERIFY( m_bars->barAttributes().useFixedBarWidth() == true ); QVERIFY( m_bars->barAttributes().drawSolidExcessArrows() == true ); QVERIFY( m_bars->barAttributes().groupGapFactor() == 2 ); QVERIFY( m_bars->barAttributes().barGapFactor() == 1 ); } void testThreeDBarAttributesLevelSettings() { //check segments const int rows = m_bars->model()->rowCount(); QCOMPARE( m_bars->numberOfAbscissaSegments(), rows ); const int cols = m_bars->model()->columnCount(); QCOMPARE( m_bars->numberOfOrdinateSegments(), cols ); QModelIndex idx = m_model->index(rows-3, cols-3, QModelIndex()); // create attribut ThreeDBarAttributes td( m_bars->threeDBarAttributes() ); ThreeDBarAttributes tdCol( m_bars->threeDBarAttributes() ); ThreeDBarAttributes tdIndex( m_bars->threeDBarAttributes() ); // modify at different level and compare tdCol.setDepth(25 ); tdIndex.setDepth( 30 ); m_bars->setThreeDBarAttributes( td ); m_bars->setThreeDBarAttributes( cols-2, tdCol ); m_bars->setThreeDBarAttributes( idx, tdIndex ); QVERIFY( m_bars->threeDBarAttributes() != m_bars->threeDBarAttributes(cols-2) ); QVERIFY( m_bars->threeDBarAttributes() != m_bars->threeDBarAttributes(idx) ); QVERIFY( m_bars->threeDBarAttributes(cols-2) != m_bars->threeDBarAttributes(idx) ); QCOMPARE( m_bars->threeDBarAttributes(), td ); QCOMPARE( m_bars->threeDBarAttributes( cols - 2 ), tdCol ); QCOMPARE( m_bars->threeDBarAttributes( idx ), tdIndex ); // try and override the cols and index level - should not work m_bars->setThreeDBarAttributes( td ); QVERIFY( m_bars->threeDBarAttributes().depth() == 20 ); QVERIFY( m_bars->threeDBarAttributes( cols-2 ).depth() == 25 ); QVERIFY( m_bars->threeDBarAttributes( idx ).depth() == 30 ); } void testThreeDBarAttributesValueSettings() { ThreeDBarAttributes td( m_bars->threeDBarAttributes() ); //check default values //generics == AbstractThreeD QVERIFY( td.isEnabled() == false ); QVERIFY( td.depth() == 20 ); QVERIFY( td.validDepth() == 0.0 ); //bars specifics QVERIFY( td.useShadowColors() == true ); // Not implemented QVERIFY( td.angle() == 45 ); // Not implemented //set new values td.setEnabled( true ); td.setDepth( 40 ); td.setUseShadowColors( false ); td.setAngle( 75 ); m_bars->setThreeDBarAttributes( td ); //get new values QVERIFY( m_bars->threeDBarAttributes().isEnabled() == true ); QVERIFY( m_bars->threeDBarAttributes().depth() == 40 ); QVERIFY( m_bars->threeDBarAttributes().validDepth() == 40 ); QVERIFY( m_bars->threeDBarAttributes().useShadowColors() == false ); QVERIFY( m_bars->threeDBarAttributes().angle() == 75 ); } void cleanupTestCase() { } private: Chart *m_chart; BarDiagram *m_bars; TableModel *m_model; }; QTEST_MAIN(TestBarDiagrams) #include "main.moc" diff --git a/qtests/CartesianPlanes/main.cpp b/qtests/CartesianPlanes/main.cpp index 54c4795..0def423 100644 --- a/qtests/CartesianPlanes/main.cpp +++ b/qtests/CartesianPlanes/main.cpp @@ -1,295 +1,295 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include using namespace KChart; class NumericDataModel : public QStandardItemModel { Q_OBJECT public: void setYValues( const QList< qreal > &values ) { min = QPointF( 0, 1e9 ); max = QPointF( values.size() - 1, -1e9 ); setRowCount( values.size() ); setColumnCount( 1 ); for ( int i = 0; i < values.size(); i++ ) { min.setY( qMin( min.y(), values[ i ] ) ); max.setY( qMax( max.y(), values[ i ] ) ); QModelIndex idx = index( i, 0 ); QStandardItemModel::setData( idx, values[ i ] ); } } void setXyValues( const QList< QPointF > &values ) { min = QPointF( 1e9, 1e9 ); max = QPointF( -1e9, -1e9 ); setRowCount( values.size() ); setColumnCount( 2 ); for ( int i = 0; i < values.size(); i++ ) { min.setX( qMin( min.x(), values[ i ].x() ) ); max.setX( qMax( max.x(), values[ i ].x() ) ); min.setY( qMin( min.y(), values[ i ].y() ) ); max.setY( qMax( max.y(), values[ i ].y() ) ); QModelIndex idx = index( i, 0 ); QStandardItemModel::setData( idx, values[ i ].x() ); idx = index( i, 1 ); QStandardItemModel::setData( idx, values[ i ].y() ); } } QPointF min; QPointF max; }; class TestCartesianPlanes : public QObject { Q_OBJECT private slots: void init(); void cleanup(); void testIntialOwnership(); void testDiagramOwnership(); void testIsometricScalingSettings(); void testZoomFactorsSettings(); void testRangeSettingsBars(); void testRangeSettingsPlotter(); void testGlobalGridAttributesSettings(); void testGridAttributesSettings(); void testAxesCalcModesSettings(); private: void doTestRangeSettings( AbstractCartesianDiagram *diagram, const QPointF &min, const QPointF &max ); Chart *m_chart; BarDiagram *m_bars; Plotter *m_plotter; CartesianCoordinatePlane *m_plane; NumericDataModel *m_model; }; void TestCartesianPlanes::init() { - m_chart = new Chart( 0 ); + m_chart = new Chart( nullptr ); m_model = new NumericDataModel(); m_model->setParent( m_chart ); m_bars = new BarDiagram( m_chart ); m_bars->setModel( m_model ); m_plotter = new Plotter( m_chart ); m_plotter->setModel( m_model ); m_plane = new CartesianCoordinatePlane( m_chart ); m_chart->addCoordinatePlane( m_plane ); m_plane->setReferenceCoordinatePlane( m_chart->coordinatePlane() ); qDebug() << m_plotter->datasetDimension(); } void TestCartesianPlanes::cleanup() { delete m_chart; } void TestCartesianPlanes::testIntialOwnership() { AbstractCoordinatePlane *plane = m_chart->coordinatePlane(); QCOMPARE( m_plane->referenceCoordinatePlane(), m_chart->coordinatePlane() ); - m_chart->takeCoordinatePlane( 0 ); + m_chart->takeCoordinatePlane( nullptr ); delete plane; - QCOMPARE( m_plane->referenceCoordinatePlane(), (AbstractCoordinatePlane*)0 ); + QCOMPARE( m_plane->referenceCoordinatePlane(), (AbstractCoordinatePlane*)nullptr ); } void TestCartesianPlanes::testDiagramOwnership() { m_plane->addDiagram( m_bars ); QCOMPARE( m_plane->diagrams().size(), 1 ); m_plane->addDiagram( m_plotter ); QCOMPARE( m_plane->diagrams().size(), 2 ); QCOMPARE( dynamic_cast< BarDiagram * >( m_plane->diagram() ), m_bars ); m_plane->takeDiagram( m_bars ); QCOMPARE( m_plane->diagrams().size(), 1 ); QCOMPARE( dynamic_cast< Plotter * >( m_plane->diagram() ), m_plotter ); m_plane->replaceDiagram( m_bars, m_plotter ); QCOMPARE( m_plane->diagrams().size(), 1 ); QCOMPARE( dynamic_cast< BarDiagram * >( m_plane->diagram() ), m_bars ); m_plane->takeDiagram( m_bars ); QCOMPARE( m_plane->diagrams().size(), 0 ); delete m_bars; } void TestCartesianPlanes::testIsometricScalingSettings() { QVERIFY( m_plane->doesIsometricScaling() == false ); m_plane->setIsometricScaling( true ); QVERIFY( m_plane->doesIsometricScaling() == true ); } void TestCartesianPlanes::testZoomFactorsSettings() { QCOMPARE( m_plane->zoomFactorX(), 1.0 ); QCOMPARE( m_plane->zoomFactorY(), 1.0 ); QCOMPARE( m_plane->zoomCenter(), QPointF( 0.5, 0.5 ) ); m_plane->setZoomFactorX( 1.5 ); m_plane->setZoomFactorY( 1.5 ); m_plane->setZoomCenter( QPointF( 1.0, 1.0 ) ); QCOMPARE( m_plane->zoomFactorX(), 1.5 ); QCOMPARE( m_plane->zoomFactorY(), 1.5 ); QCOMPARE( m_plane->zoomCenter(), QPointF( 1.0, 1.0 ) ); } void TestCartesianPlanes::doTestRangeSettings( AbstractCartesianDiagram *diagram, const QPointF &min, const QPointF &max ) { m_plane->addDiagram( diagram ); // the range is null when auto adjustment is turned off - check that... m_plane->setAutoAdjustHorizontalRangeToData( 100 ); m_plane->setAutoAdjustVerticalRangeToData( 100 ); { const QPair< qreal, qreal > hrange = m_plane->horizontalRange(); const QPair< qreal, qreal > vrange = m_plane->verticalRange(); QCOMPARE( hrange.first, qreal( 0.0 ) ); QCOMPARE( hrange.second, qreal( 0.0 ) ); QCOMPARE( vrange.first, qreal( 0.0 ) ); QCOMPARE( vrange.second, qreal( 0.0 ) ); } // now check that auto adjustment works when enabled m_plane->setAutoAdjustHorizontalRangeToData( 67 ); m_plane->setAutoAdjustVerticalRangeToData( 67 ); { const QPair< qreal, qreal > hrange = m_plane->horizontalRange(); const QPair< qreal, qreal > vrange = m_plane->verticalRange(); QCOMPARE( hrange.first, min.x() ); QCOMPARE( hrange.second, max.x() ); QCOMPARE( vrange.first, min.y() ); QCOMPARE( vrange.second, max.y() ); } { QPair< qreal, qreal> hboundaries( diagram->dataBoundaries().first.x(), diagram->dataBoundaries().second.x() ); QPair< qreal, qreal> vboundaries( int( diagram->dataBoundaries().first.y() + 0.5 ), int( diagram->dataBoundaries().second.y() + 0.5 ) ); m_plane->setHorizontalRange( hboundaries ); m_plane->setVerticalRange( vboundaries ); const QPair< qreal, qreal > newhb = m_plane->horizontalRange(); const QPair< qreal, qreal > newvb = m_plane->verticalRange(); QCOMPARE( newhb.first, hboundaries.first ); QCOMPARE( newhb.second, hboundaries.second ); QCOMPARE( newvb.first, vboundaries.first ); QCOMPARE( newvb.second, vboundaries.second ); } } void TestCartesianPlanes::testRangeSettingsBars() { QList< qreal > points; points << 40 << 45 << 42 << 34 << 34; m_model->setYValues( points ); // data point "0" is shown at "0.5" in bar diagrams, which requires some data range hackery. // we correct for that like so: m_model->max.rx() += 1; doTestRangeSettings( m_bars, m_model->min, m_model->max ); } void TestCartesianPlanes::testRangeSettingsPlotter() { QList< QPointF > points; points << QPointF( 0.0, 40.0 ) << QPointF( 1.0, 45.0 ) << QPointF( 2.0, 42.0 ) << QPointF( 3.0, 34.0 ) << QPointF( 4.0, 34.0 ); m_model->setXyValues( points ); doTestRangeSettings( m_plotter, m_model->min, m_model->max ); } void TestCartesianPlanes::testGlobalGridAttributesSettings() { GridAttributes ga = m_plane->globalGridAttributes(); QVERIFY( ga.isGridVisible() == true ); ga.setGridVisible( false ); m_plane->setGlobalGridAttributes( ga ); QVERIFY( m_plane->globalGridAttributes().isGridVisible() == false ); //reset to normal ga.setGridVisible( true ); QVERIFY( m_plane->globalGridAttributes().isGridVisible() == false ); m_plane->setGlobalGridAttributes( ga ); QVERIFY( m_plane->globalGridAttributes().isGridVisible() == true ); } void TestCartesianPlanes::testGridAttributesSettings() { GridAttributes gh = m_plane->gridAttributes( Qt::Horizontal ); GridAttributes gv = m_plane->gridAttributes( Qt::Vertical ); QVERIFY( gh.isGridVisible() == true ); QVERIFY( gv.isGridVisible() == true ); gh.setGridVisible( false ); m_plane->setGridAttributes( Qt::Horizontal, gh ); QVERIFY( m_plane->hasOwnGridAttributes( Qt::Horizontal ) == true ); QVERIFY( m_plane->hasOwnGridAttributes( Qt::Vertical ) == false ); QVERIFY( m_plane->gridAttributes( Qt::Horizontal ).isGridVisible() == false ); QVERIFY( m_plane->gridAttributes( Qt::Vertical ).isGridVisible() == true ); gv.setGridVisible( false ); m_plane->setGridAttributes( Qt::Vertical, gv ); QVERIFY( m_plane->hasOwnGridAttributes( Qt::Horizontal ) == true ); QVERIFY( m_plane->hasOwnGridAttributes( Qt::Vertical ) == true ); QVERIFY( m_plane->gridAttributes( Qt::Horizontal ).isGridVisible() == false ); QVERIFY( m_plane->gridAttributes( Qt::Vertical ).isGridVisible() == false ); m_plane->resetGridAttributes( Qt::Horizontal ); m_plane->resetGridAttributes( Qt::Vertical ); QVERIFY( m_plane->gridAttributes( Qt::Horizontal ).isGridVisible() == true ); QVERIFY( m_plane->gridAttributes( Qt::Vertical ).isGridVisible() == true ); QVERIFY( m_plane->hasOwnGridAttributes( Qt::Horizontal ) == false ); QVERIFY( m_plane->hasOwnGridAttributes( Qt::Vertical ) == false ); } void TestCartesianPlanes::testAxesCalcModesSettings() { QCOMPARE( m_plane->axesCalcModeX(), AbstractCoordinatePlane::Linear ); QCOMPARE( m_plane->axesCalcModeY(), AbstractCoordinatePlane::Linear ); m_plane->setAxesCalcModes( AbstractCoordinatePlane::Logarithmic ); QCOMPARE( m_plane->axesCalcModeX(), AbstractCoordinatePlane::Logarithmic ); QCOMPARE( m_plane->axesCalcModeY(), AbstractCoordinatePlane::Logarithmic ); m_plane->setAxesCalcModeX( AbstractCoordinatePlane::Linear ); QCOMPARE( m_plane->axesCalcModeX(), AbstractCoordinatePlane::Linear ); QCOMPARE( m_plane->axesCalcModeY(), AbstractCoordinatePlane::Logarithmic ); m_plane->setAxesCalcModeY( AbstractCoordinatePlane::Linear ); QCOMPARE( m_plane->axesCalcModeX(), AbstractCoordinatePlane::Linear ); QCOMPARE( m_plane->axesCalcModeY(), AbstractCoordinatePlane::Linear ); } QTEST_MAIN(TestCartesianPlanes) #include "main.moc" diff --git a/qtests/ChartElementOwnership/main.cpp b/qtests/ChartElementOwnership/main.cpp index bf14321..63a5a05 100644 --- a/qtests/ChartElementOwnership/main.cpp +++ b/qtests/ChartElementOwnership/main.cpp @@ -1,211 +1,211 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include using namespace KChart; class TestChartElementOwnership: public QObject { Q_OBJECT private slots: void initTestCase() { - m_chart = new Chart(0); + m_chart = new Chart(nullptr); } void testPlaneOwnership() { // check number plane AbstractCoordinatePlane*orig = m_chart->coordinatePlane(); QCOMPARE( m_chart->coordinatePlanes().size(), 1 ); // add and take CartesianCoordinatePlane *p = new CartesianCoordinatePlane(); m_chart->addCoordinatePlane( p ); QCOMPARE( m_chart->coordinatePlanes().size(), 2 ); m_chart->takeCoordinatePlane( orig ); QCOMPARE( m_chart->coordinatePlanes().size(), 1 ); QCOMPARE( dynamic_cast< CartesianCoordinatePlane * >(m_chart->coordinatePlane()), p ); m_chart->addCoordinatePlane( orig ); QCOMPARE( m_chart->coordinatePlanes().size(), 2 ); // replace abstract by polar PolarCoordinatePlane *po = new PolarCoordinatePlane(); m_chart->replaceCoordinatePlane( po, orig ); QCOMPARE( m_chart->coordinatePlanes().size(), 2 ); m_chart->takeCoordinatePlane( p ); QCOMPARE( m_chart->coordinatePlanes().size(), 1 ); QCOMPARE( dynamic_cast< PolarCoordinatePlane * >(m_chart->coordinatePlane()), po ); m_chart->addCoordinatePlane( p ); QCOMPARE( m_chart->coordinatePlanes().size(), 2 ); // delete delete po; QCOMPARE( m_chart->coordinatePlanes().size(), 1 ); QCOMPARE( dynamic_cast< CartesianCoordinatePlane * >(m_chart->coordinatePlane()), p ); // replace cartesian by polar PolarCoordinatePlane*polast = new PolarCoordinatePlane(); m_chart->replaceCoordinatePlane( polast ); QCOMPARE( m_chart->coordinatePlanes().size(), 1 ); QCOMPARE( dynamic_cast< PolarCoordinatePlane * >(m_chart->coordinatePlane()), polast ); // replace polar by cartesian CartesianCoordinatePlane* plast = new CartesianCoordinatePlane(); m_chart->replaceCoordinatePlane( plast, polast ); QCOMPARE( m_chart->coordinatePlanes().size(), 1 ); QCOMPARE( dynamic_cast< CartesianCoordinatePlane * >(m_chart->coordinatePlane()), plast ); } void testHeaderFooterOwnership() { QCOMPARE( m_chart->headerFooters().size(), 0 ); HeaderFooter * h = new HeaderFooter(); m_chart->addHeaderFooter( h ); QCOMPARE( m_chart->headerFooters().size(), 1 ); m_chart->takeHeaderFooter( h ); QCOMPARE( m_chart->headerFooters().size(), 0 ); m_chart->addHeaderFooter( h ); QCOMPARE( m_chart->headerFooters().size(), 1 ); delete h; QCOMPARE( m_chart->headerFooters().size(), 0 ); } void testHeaderFooterReplace() { QCOMPARE( m_chart->headerFooters().size(), 0 ); HeaderFooter * h = new HeaderFooter(); HeaderFooter * h1 = new HeaderFooter(); m_chart->addHeaderFooter( h ); QCOMPARE( m_chart->headerFooters().size(), 1 ); m_chart->addHeaderFooter( h1 ); QCOMPARE( m_chart->headerFooters().size(), 2 ); m_chart->takeHeaderFooter( h ); QCOMPARE( m_chart->headerFooters().size(), 1 ); QCOMPARE( m_chart->headerFooter(), h1 ); m_chart->replaceHeaderFooter( h, h1 ); QCOMPARE( m_chart->headerFooters().size(), 1 ); delete h; QCOMPARE( m_chart->headerFooters().size(), 0 ); } void testLegendOwnership() { // check no legend QCOMPARE( m_chart->legends().size(), 0 ); // check add legend - take legend - delete legend Legend * legend = new Legend( m_chart->coordinatePlane()->diagram() ); m_chart->addLegend( legend ); QCOMPARE( m_chart->legends().size(), 1 ); m_chart->takeLegend( legend ); QCOMPARE( m_chart->legends().size(), 0 ); m_chart->addLegend( legend ); QCOMPARE( m_chart->legends().size(), 1 ); delete legend; QCOMPARE( m_chart->legends().size(), 0 ); } void testLegendReplace() { // check no legend QCOMPARE( m_chart->legends().size(), 0 ); // check add several legends - take legend // replace legend - delete legend Legend * legend = new Legend( m_chart->coordinatePlane()->diagram() ); Legend * legend2 = new Legend( m_chart->coordinatePlane()->diagram() ); m_chart->addLegend( legend ); QCOMPARE( m_chart->legends().size(), 1 ); m_chart->addLegend( legend2 ); QCOMPARE( m_chart->legends().size(), 2 ); m_chart->takeLegend( legend ); QCOMPARE( m_chart->legends().size(), 1 ); QCOMPARE( m_chart->legend(), legend2 ); m_chart->replaceLegend( legend, legend2 ); QCOMPARE( m_chart->legends().size(), 1 ); delete legend; QCOMPARE( m_chart->legends().size(), 0 ); } void testPadding() { QVERIFY( m_chart->globalLeadingLeft() == false ); m_chart->setGlobalLeading( 2, 2, 2, 2 ); QCOMPARE( m_chart->globalLeadingLeft(), 2 ); QCOMPARE( m_chart->globalLeadingTop(), 2 ); QCOMPARE( m_chart->globalLeadingRight(), 2 ); QCOMPARE( m_chart->globalLeadingBottom(), 2 ); m_chart->setGlobalLeadingLeft( 5 ); QCOMPARE( m_chart->globalLeadingLeft(), 5 ); QCOMPARE( m_chart->globalLeadingTop(), 2 ); QCOMPARE( m_chart->globalLeadingRight(), 2 ); QCOMPARE( m_chart->globalLeadingBottom(), 2 ); m_chart->setGlobalLeadingTop( 5 ); QCOMPARE( m_chart->globalLeadingLeft(), 5 ); QCOMPARE( m_chart->globalLeadingTop(), 5 ); QCOMPARE( m_chart->globalLeadingRight(), 2 ); QCOMPARE( m_chart->globalLeadingBottom(), 2 ); m_chart->setGlobalLeadingRight( 5 ); QCOMPARE( m_chart->globalLeadingLeft(), 5 ); QCOMPARE( m_chart->globalLeadingTop(), 5 ); QCOMPARE( m_chart->globalLeadingRight(), 5 ); QCOMPARE( m_chart->globalLeadingBottom(), 2 ); m_chart->setGlobalLeadingBottom( 5 ); QCOMPARE( m_chart->globalLeadingLeft(), 5 ); QCOMPARE( m_chart->globalLeadingTop(), 5 ); QCOMPARE( m_chart->globalLeadingRight(), 5 ); QCOMPARE( m_chart->globalLeadingBottom(), 5 ); m_chart->setGlobalLeading( 2, 2, 2, 2 ); QCOMPARE( m_chart->globalLeadingLeft(), 2 ); QCOMPARE( m_chart->globalLeadingTop(), 2 ); QCOMPARE( m_chart->globalLeadingRight(), 2 ); QCOMPARE( m_chart->globalLeadingBottom(), 2 ); } void testChartDeletion() { delete m_chart; } void cleanupTestCase() { } private: Chart *m_chart; }; QTEST_MAIN(TestChartElementOwnership) #include "main.moc" diff --git a/qtests/Cloning/main.cpp b/qtests/Cloning/main.cpp index 984290a..3194438 100644 --- a/qtests/Cloning/main.cpp +++ b/qtests/Cloning/main.cpp @@ -1,187 +1,187 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include using namespace KChart; class TestCloning: public QObject { Q_OBJECT private slots: void initTestCase() { } void testCloningBarDiagram() { BarDiagram* diagram = new BarDiagram(); diagram->setType( BarDiagram::Stacked ); CartesianAxis *axis = new CartesianAxis; diagram->addAxis( axis ); QCOMPARE( diagram->axes().count(), 1 ); BarAttributes attrs; attrs.setFixedDataValueGap( 3.0 ); attrs.setFixedBarWidth( 30.0 ); attrs.setDrawSolidExcessArrows( false ); diagram->setBarAttributes( attrs ); attrs.setDrawSolidExcessArrows( true ); diagram->setBarAttributes( 0, attrs ); attrs.setDrawSolidExcessArrows( false ); ThreeDBarAttributes threeDAttrs; threeDAttrs.setUseShadowColors( false ); diagram->setThreeDBarAttributes( threeDAttrs ); BarDiagram* clone = diagram->clone(); QCOMPARE( diagram->type(), clone->type() ); // We do not clone the axes. QCOMPARE( clone->axes().count(), 0 ); // And neither the reference diagram. - QCOMPARE( clone->referenceDiagram(), (AbstractCartesianDiagram*)0 ); + QCOMPARE( clone->referenceDiagram(), (AbstractCartesianDiagram*)nullptr ); QCOMPARE( clone->referenceDiagramOffset(), QPointF() ); // And neither the plane. - QCOMPARE( clone->coordinatePlane(), (AbstractCoordinatePlane*)0 ); + QCOMPARE( clone->coordinatePlane(), (AbstractCoordinatePlane*)nullptr ); QCOMPARE( diagram->allowOverlappingDataValueTexts(), clone->allowOverlappingDataValueTexts() ); QCOMPARE( diagram->antiAliasing(), clone->antiAliasing() ); QCOMPARE( diagram->percentMode(), clone->percentMode() ); QCOMPARE( diagram->datasetDimension(), clone->datasetDimension() ); QCOMPARE( diagram->barAttributes(), clone->barAttributes() ); QCOMPARE( diagram->threeDBarAttributes(), clone->threeDBarAttributes() ); QVERIFY( diagram->attributesModel() != clone->attributesModel() ); } void testCloningLineDiagram() { LineDiagram* diagram = new LineDiagram(); diagram->setType( LineDiagram::Percent ); LineAttributes attrs; attrs.setMissingValuesPolicy( LineAttributes::MissingValuesShownAsZero ); diagram->setLineAttributes( attrs ); LineDiagram* clone = diagram->clone(); QCOMPARE( diagram->type(), clone->type() ); QCOMPARE( diagram->lineAttributes(), clone->lineAttributes() ); // the rest is already tested in testCloningBarDiagram() } void testCloningPieDiagram() { // commenting those tests - Deprecated method // will make new test for that in PolarCoordinatePlane // do we want the warning ? // if yes - we just need to un-comment PieDiagram* diagram = new PieDiagram(); //diagram->coordinatePlane()->setStartPosition( 15.0 ); diagram->setGranularity( 1.5 ); PieAttributes attrs; attrs.setExplode( true ); attrs.setExplodeFactor( 1.5 ); ThreeDPieAttributes threeDAttrs; threeDAttrs.setUseShadowColors( false ); PieDiagram* clone = diagram->clone(); //QCOMPARE( diagram->startPosition(), clone->startPosition() ); QCOMPARE( diagram->granularity(), clone->granularity() ); QCOMPARE( diagram->pieAttributes(), clone->pieAttributes() ); QCOMPARE( diagram->threeDPieAttributes(), clone->threeDPieAttributes() ); // the rest is already tested in testCloningBarDiagram() } void testCloningPolarDiagram() { // commenting those tests - Deprecated method // will make new test for that in PolarCoordinatePlane // do we want the warning ? // if yes - we just need to un-comment PolarDiagram* diagram = new PolarDiagram(); //diagram->setZeroDegreePosition( 5 ); diagram->setRotateCircularLabels( true ); diagram->setShowDelimitersAtPosition( Position::North, false ); diagram->setShowDelimitersAtPosition( Position::South, true ); diagram->setShowLabelsAtPosition( Position::North, true ); diagram->setShowLabelsAtPosition( Position::South, false ); PolarDiagram* clone = diagram->clone(); //QCOMPARE( diagram->zeroDegreePosition(), clone->zeroDegreePosition() ); QCOMPARE( diagram->rotateCircularLabels(), clone->rotateCircularLabels() ); QCOMPARE( diagram->showDelimitersAtPosition( Position::North ), clone->showDelimitersAtPosition( Position::North ) ); QCOMPARE( diagram->showDelimitersAtPosition( Position::South ), clone->showDelimitersAtPosition( Position::South ) ); QCOMPARE( diagram->showLabelsAtPosition( Position::North ), clone->showLabelsAtPosition( Position::North ) ); QCOMPARE( diagram->showLabelsAtPosition( Position::South ), clone->showLabelsAtPosition( Position::South ) ); // the rest is already tested in testCloningBarDiagram() } void testCloningRingDiagram() { RingDiagram* diagram = new RingDiagram(); diagram->setRelativeThickness( true ); RingDiagram* clone = diagram->clone(); QCOMPARE( diagram->relativeThickness(), clone->relativeThickness() ); // the rest is already tested in testCloningBarDiagram() // and testCloningPieDiagram() } void testCloningHeaderFooter() { HeaderFooter* headerFooter = new HeaderFooter(); headerFooter->setType( HeaderFooter::Footer ); TextAttributes attrs; attrs.setPen( QPen(Qt::red) ); headerFooter->setTextAttributes( attrs ); HeaderFooter* clone = headerFooter->clone(); QCOMPARE( headerFooter->type(), clone->type() ); QCOMPARE( headerFooter->textAttributes(), clone->textAttributes() ); } void testCloningLegends() { Legend* legend = new Legend(); TextAttributes attrs; attrs.setPen( QPen(Qt::red) ); legend->setTextAttributes( attrs ); legend->setShowLines( true ); legend->setPosition( Position::North ); Legend* clone = legend->clone(); QCOMPARE( legend->textAttributes(), clone->textAttributes() ); QCOMPARE( legend->showLines(), clone->showLines() ); QCOMPARE( legend->position(), clone->position() ); } void cleanupTestCase() { } private: }; QTEST_MAIN(TestCloning) #include "main.moc" diff --git a/qtests/DrawIntoPainter/framewidget.cpp b/qtests/DrawIntoPainter/framewidget.cpp index 0c353d7..a0512c7 100644 --- a/qtests/DrawIntoPainter/framewidget.cpp +++ b/qtests/DrawIntoPainter/framewidget.cpp @@ -1,77 +1,77 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "framewidget.h" #include #include #include FrameWidget::FrameWidget( QWidget* parent, Qt::WindowFlags f ) : QWidget( parent, f ) - , mChart( 0 ) + , mChart( nullptr ) { // this bloc left empty intentionally } void FrameWidget::setChart( KChart::Chart* chart ) { mChart = chart; // This is necessary because Chart can't automatically schedule somebody else (this object) to // call its custom paint method. connect( mChart, SIGNAL(propertiesChanged()), SLOT(update()) ); } void FrameWidget::paintEvent( QPaintEvent* e ) { if ( !mChart ) { QWidget::paintEvent( e ); } else { QPainter painter( this ); const int wid = 64; const QRect r( rect() ); const QPen oldPen( painter.pen() ); const QBrush oldBrush( painter.brush() ); // paint below the chart painter.setPen( QPen(Qt::NoPen) ); painter.setBrush( QBrush(QColor(0xd0,0xd0,0xff)) ); painter.drawEllipse(r.left(), r.top(), wid,wid); painter.drawEllipse(r.left()+r.width()-wid-1, r.top(), wid,wid); painter.drawEllipse(r.left(), r.top()+r.height()-wid-1, wid,wid); painter.drawEllipse(r.left()+r.width()-wid-1, r.top()+r.height()-wid-1, wid,wid); painter.setBrush( oldBrush ); painter.setPen( oldPen ); // paint the chart mChart->paint( &painter, QRect( r.left()+wid/2, r.top()+wid/2, r.width()-wid, r.height()-wid ) ); // paint over the chart painter.setPen( QPen(Qt::NoPen) ); painter.setBrush( QBrush(QColor(0xd0,0xff,0xff)) ); const int wid2=40; const int gap=(wid-wid2)/2; painter.drawEllipse(r.left()+gap, r.top()+gap, wid2,wid2); painter.drawEllipse(r.left()+r.width()-wid+gap-1, r.top()+gap, wid2,wid2); painter.drawEllipse(r.left()+gap, r.top()+r.height()-wid+gap-1, wid2,wid2); painter.drawEllipse(r.left()+r.width()-wid+gap-1, r.top()+r.height()-wid+gap-1, wid2,wid2); painter.setBrush( oldBrush ); painter.setPen( oldPen ); } } diff --git a/qtests/DrawIntoPainter/framewidget.h b/qtests/DrawIntoPainter/framewidget.h index 5623150..10d74f6 100644 --- a/qtests/DrawIntoPainter/framewidget.h +++ b/qtests/DrawIntoPainter/framewidget.h @@ -1,47 +1,47 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FRAMEWIDGET_H #define FRAMEWIDGET_H #include namespace KChart { class Chart; } class FrameWidget : public QWidget { Q_OBJECT public: - explicit FrameWidget( QWidget * parent = 0, Qt::WindowFlags f = Qt::WindowFlags() ); + explicit FrameWidget( QWidget * parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags() ); void paintEvent( QPaintEvent* ) Q_DECL_OVERRIDE; void setChart( KChart::Chart* chart ); private: KChart::Chart* mChart; }; #endif /* FRAMEWIDGET_H */ diff --git a/qtests/DrawIntoPainter/mainwindow.cpp b/qtests/DrawIntoPainter/mainwindow.cpp index 6ffd500..396aead 100644 --- a/qtests/DrawIntoPainter/mainwindow.cpp +++ b/qtests/DrawIntoPainter/mainwindow.cpp @@ -1,424 +1,424 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mainwindow.h" #include "framewidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KChart; static QPixmap drawIntoPixmap( const QSize& size, KChart::Chart* chart ) { QPixmap pix( size ); pix.fill( Qt::white ); QPainter painter( &pix ); chart->paint( &painter, QRect( 0, 0, size.width(), size.height() ) ); return pix; } // When set, this example uses FrameWidget which uses Chart::paint to paint itself. // When not set, this example uses a Chart widget directly. #define USE_FRAME_WIDGET 1 MainWindow::MainWindow( QWidget* parent ) : QWidget( parent ) { setupUi( this ); QHBoxLayout* chartLayout = new QHBoxLayout( chartFrame ); #ifdef USE_FRAME_WIDGET FrameWidget* chartFrameWidget = new FrameWidget(); chartLayout->addWidget( chartFrameWidget ); #endif hSBar->setVisible( false ); vSBar->setVisible( false ); m_model.loadFromCSV( ":/empty" ); // Set up the diagram m_lines = new LineDiagram(); m_lines->setModel( &m_model ); CartesianAxis *xAxis = new CartesianAxis( m_lines ); CartesianAxis *yAxis = new CartesianAxis ( m_lines ); CartesianAxis *axisTop = new CartesianAxis ( m_lines ); CartesianAxis *axisRight = new CartesianAxis ( m_lines ); xAxis->setPosition ( KChart::CartesianAxis::Bottom ); yAxis->setPosition ( KChart::CartesianAxis::Left ); axisTop->setPosition( KChart::CartesianAxis::Top ); axisRight->setPosition( KChart::CartesianAxis::Right ); m_lines->addAxis( xAxis ); m_lines->addAxis( yAxis ); m_lines->addAxis( axisTop ); m_lines->addAxis( axisRight ); m_chart = new Chart(); //m_chart->setGlobalLeading(10,10,10,10); // by default there is no leading #ifdef USE_FRAME_WIDGET chartFrameWidget->setChart( m_chart ); // make sure, we re-draw after changing one of the chart's properties connect( m_chart, SIGNAL(propertiesChanged()), chartFrameWidget, SLOT(update()) ) ; #else chartLayout->addWidget( m_chart ); #endif m_chart->coordinatePlane()->replaceDiagram( m_lines ); for ( int iColumn = 0; iColumnmodel()->columnCount(); ++iColumn ) { QPen pen(m_lines->pen( iColumn )); pen.setWidth(4); m_lines->setPen( iColumn, pen ); } FrameAttributes faChart( m_chart->frameAttributes() ); faChart.setVisible( true ); faChart.setPen( QPen(QColor(0x60,0x60,0xb0), 8) ); m_chart->setFrameAttributes( faChart ); BackgroundAttributes baChart( m_chart->backgroundAttributes() ); baChart.setVisible( true ); baChart.setBrush( QColor(0xd0,0xd0,0xff) ); m_chart->setBackgroundAttributes( baChart ); // Set up the legend m_legend = new Legend( m_lines, m_chart ); m_legend->setPosition( Position::South ); m_legend->setAlignment( Qt::AlignRight ); m_legend->setShowLines( false ); m_legend->setTitleText( tr( "Legend" ) ); m_legend->setOrientation( Qt::Horizontal ); // setting the legend frame and background to the same color: const QColor legendColor(0xff,0xe0,0x80); FrameAttributes faLegend( m_legend->frameAttributes() ); faLegend.setVisible( true ); faLegend.setPen( QPen(legendColor, 1) ); m_legend->setFrameAttributes( faLegend ); BackgroundAttributes baLegend( m_legend->backgroundAttributes() ); baLegend.setVisible( true ); baLegend.setBrush( legendColor ); m_legend->setBackgroundAttributes( baLegend ); m_chart->addLegend( m_legend ); // for illustration we paint the same chart at different sizes: QSize size1 = QSize( 200, 200 ); QSize size2 = QSize( 800, 800 ); m_pix1 = drawIntoPixmap( size1, m_chart ); m_pix2 = drawIntoPixmap( size2, m_chart ); m_pix2 = m_pix2.scaled( size1 ); m_smallChart1 = new QLabel( this ); m_smallChart1->setWindowTitle( "200x200" ); m_smallChart1->setPixmap( m_pix1 ); m_smallChart1->setFixedSize( m_pix1.size() ); m_smallChart1->move( width() - m_pix1.width()*2, height()/2 - m_pix1.height()-5 ); m_smallChart1->show(); m_smallChart2 = new QLabel( this ); m_smallChart2->setWindowTitle( "800x800 scaled down" ); m_smallChart2->setPixmap( m_pix2 ); m_smallChart2->setFixedSize( m_pix2.size() ); m_smallChart2->move( width() - m_pix2.width()*2, height()/2 + 5 ); m_smallChart2->show(); faChart.setPen( QPen(QColor(0xb0,0xb0,0xff), 8) ); m_chart->setFrameAttributes( faChart ); // initialize attributes; this is necessary because we need to enable data value attributes before // any of them (e.g. only markers) can be displayed. but if we enable data value attributs, a default // data value text is included, even if we only wanted to set markers. so we enable DVA and then // individually disable the parts we don't want. on_paintValuesCB_toggled( false ); on_paintMarkersCB_toggled( false ); } void MainWindow::updateData(QString data) { QTime t; t.start(); m_model.loadFromCSV( data ); qDebug("Time for loading data %s: %d ms", data.toLatin1().constData(), t.elapsed()); t.restart(); QSize size1 = QSize( 200, 200 ); QSize size2 = QSize( 800, 800 ); m_pix1 = drawIntoPixmap( size1, m_chart ); m_pix2 = drawIntoPixmap( size2, m_chart ); qDebug("Time for drawing pixmap %s: %d ms", data.toLatin1().constData(), t.elapsed()); t.restart(); m_lines->setModel( &m_model ); qDebug("Time for setting model %s: %d ms", data.toLatin1().constData(), t.elapsed()); t.restart(); m_smallChart1->setPixmap( m_pix1 ); m_smallChart2->setPixmap( m_pix2 ); m_smallChart1->show(); m_smallChart2->show(); qDebug("Time for setting pixmap %s: %d ms", data.toLatin1().constData(), t.elapsed()); t.restart(); } void MainWindow::on_lineTypeCB_currentIndexChanged( const QString & text ) { if ( text == "Normal" ) m_lines->setType( LineDiagram::Normal ); else if ( text == "Stacked" ) m_lines->setType( LineDiagram::Stacked ); else if ( text == "Percent" ) m_lines->setType( LineDiagram::Percent ); else qWarning (" Does not match any type"); } void MainWindow::on_paintLegendCB_toggled( bool checked ) { KChart::Legend* legend = m_chart->legend(); - if ( checked != ( legend != 0 ) ) { + if ( checked != ( legend != nullptr ) ) { if ( checked ) m_chart->addLegend( m_legend ); else m_chart->takeLegend( legend ); } } void MainWindow::on_paintValuesCB_toggled( bool checked ) { const int colCount = m_lines->model()->columnCount(); for ( int iColumn = 0; iColumndataValueAttributes( iColumn ); a.setVisible( true ); TextAttributes ta = a.textAttributes(); ta.setRotation( 0 ); ta.setFont( QFont( "Comic", 10 ) ); ta.setPen( m_lines->brush( iColumn ).color() ); ta.setVisible( checked ); a.setTextAttributes( ta ); m_lines->setDataValueAttributes( iColumn, a); } } void MainWindow::on_paintMarkersCB_toggled( bool checked ) { paintMarkers( checked, QSize() ); } void MainWindow::on_markersStyleCB_currentIndexChanged( const QString & text ) { Q_UNUSED( text ); on_paintMarkersCB_toggled( paintMarkersCB->isChecked() ); } void MainWindow::on_markersWidthSB_valueChanged( int i ) { Q_UNUSED( i ); markersHeightSB->setValue( markersWidthSB->value() ); on_paintMarkersCB_toggled( paintMarkersCB->isChecked() ); } void MainWindow::on_markersHeightSB_valueChanged( int i ) { Q_UNUSED( i ); markersWidthSB->setValue( markersHeightSB->value() ); on_paintMarkersCB_toggled( paintMarkersCB->isChecked() ); } void MainWindow::on_displayAreasCB_toggled( bool checked ) { const int colCount = m_lines->model()->columnCount(); for ( int iColumn = 0; iColumnlineAttributes( iColumn ) ); la.setDisplayArea( checked ); if ( checked ) la.setTransparency( transparencySB->value() ); m_lines->setLineAttributes( iColumn, la ); } } void MainWindow::on_transparencySB_valueChanged( int alpha ) { Q_UNUSED( alpha ); if ( !displayAreasCB->isChecked() ) displayAreasCB->setChecked( true ); else on_displayAreasCB_toggled( true ); } void MainWindow::on_zoomFactorSB_valueChanged( qreal factor ) { const bool isZoomedIn = factor > 1.0f; hSBar->setVisible( isZoomedIn ); vSBar->setVisible( isZoomedIn ); if ( !isZoomedIn ) { hSBar->setValue( 500 ); vSBar->setValue( 500 ); } m_chart->coordinatePlane()->setZoomFactorX( factor ); m_chart->coordinatePlane()->setZoomFactorY( factor ); } void MainWindow::on_hSBar_valueChanged( int hPos ) { m_chart->coordinatePlane()->setZoomCenter( QPointF(hPos/1000.0, vSBar->value()/1000.0) ); m_chart->update(); } void MainWindow::on_vSBar_valueChanged( int vPos ) { m_chart->coordinatePlane()->setZoomCenter( QPointF( hSBar->value()/1000.0, vPos/1000.0) ); } // since DataValue markers have no relative sizing mode we need to scale them for printing void MainWindow::paintMarkers( bool checked, const QSize& printSize ) { MarkerAttributes::MarkerStylesMap map; map.insert( 0, MarkerAttributes::MarkerSquare ); map.insert( 1, MarkerAttributes::MarkerCircle ); map.insert( 2, MarkerAttributes::MarkerRing ); map.insert( 3, MarkerAttributes::MarkerCross ); map.insert( 4, MarkerAttributes::MarkerDiamond ); // next: Specify column- / cell-specific attributes! const int colCount = m_lines->model()->columnCount(); for ( int iColumn = 0; iColumndataValueAttributes( iColumn ); dva.setVisible( true ); MarkerAttributes ma( dva.markerAttributes() ); switch ( markersStyleCB->currentIndex() ) { case 0: ma.setMarkerStyle( MarkerAttributes::MarkerSquare ); break; case 1: // Column-specific attributes ma.setMarkerStyle( map.value( iColumn ) ); break; case 2: ma.setMarkerStyle( MarkerAttributes::MarkerCircle ); break; case 3: ma.setMarkerStyle( MarkerAttributes::MarkerDiamond ); break; case 4: ma.setMarkerStyle( MarkerAttributes::Marker1Pixel ); break; case 5: ma.setMarkerStyle( MarkerAttributes::Marker4Pixels ); break; case 6: ma.setMarkerStyle( MarkerAttributes::MarkerRing ); break; case 7: ma.setMarkerStyle( MarkerAttributes::MarkerCross ); break; case 8: ma.setMarkerStyle( MarkerAttributes::MarkerFastCross ); break; default: Q_ASSERT( false ); } ma.setVisible( checked ); qreal factorWidth = printSize.isValid() ? ( printSize.width() / m_chart->rect().width() ) : 1.0f; qreal factorHeight = printSize.isValid() ? ( printSize.height() / m_chart->rect().height() ) : 1.0f; ma.setMarkerSize( QSize( markersWidthSB->value() * factorWidth, markersHeightSB->value() * factorHeight ) ); dva.setMarkerAttributes( ma ); m_lines->setDataValueAttributes( iColumn, dva ); // make a special one for certain values DataValueAttributes yellowAttributes( dva ); MarkerAttributes yellowMarker( yellowAttributes.markerAttributes() ); yellowMarker.setMarkerColor( Qt::yellow ); yellowAttributes.setMarkerAttributes( yellowMarker ); const int rowCount = m_lines->model()->rowCount(); for ( int j=0; j< rowCount; ++j ) { QModelIndex index = m_lines->model()->index( j, iColumn, QModelIndex() ); QBrush brush = m_lines->model()->headerData( iColumn, Qt::Vertical, DatasetBrushRole ).value(); qreal value = m_lines->model()->data( index ).toReal(); /* Set a specific color - marker for a specific value */ if ( value == 13 ) { m_lines->setDataValueAttributes( index, yellowAttributes ); } } } } void MainWindow::on_savePB_clicked() { qDebug() << "Painting into PNG"; QPixmap qpix(600,600); QPainter painter(&qpix); painter.setBrush(Qt::white); painter.fillRect( 0, 0, 600, 600, Qt::white); m_chart->paint( &painter, QRect(100, 100, 400, 400) ); painter.end(); qpix.save("kchart-demo.png", "PNG"); qDebug() << "Painting into PNG - done"; } void MainWindow::resizeEvent ( QResizeEvent * ) { m_smallChart1->move( width() - m_pix1.width()*2, height()/2 - m_pix1.height()-5 ); m_smallChart2->move( width() - m_pix2.width()*2, height()/2 + 5 ); } diff --git a/qtests/DrawIntoPainter/mainwindow.h b/qtests/DrawIntoPainter/mainwindow.h index 23abdc7..ee0ba33 100644 --- a/qtests/DrawIntoPainter/mainwindow.h +++ b/qtests/DrawIntoPainter/mainwindow.h @@ -1,81 +1,81 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include #include namespace KChart { class Chart; class DatasetProxyModel; class LineDiagram; class Legend; } class QLabel; class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); void updateData(QString data); protected: /** * Adjusts the two small charts when the window is resized. */ /* reimp */ void resizeEvent ( QResizeEvent * ) Q_DECL_OVERRIDE; private slots: void on_lineTypeCB_currentIndexChanged( const QString & text ); void on_paintLegendCB_toggled( bool checked ); void on_paintValuesCB_toggled( bool checked ); void on_paintMarkersCB_toggled( bool checked ); void on_markersStyleCB_currentIndexChanged( const QString & text ); void on_markersWidthSB_valueChanged( int i ); void on_markersHeightSB_valueChanged( int i); void on_displayAreasCB_toggled( bool checked ); void on_transparencySB_valueChanged( int i ); void on_zoomFactorSB_valueChanged( qreal factor ); void on_hSBar_valueChanged( int value ); void on_vSBar_valueChanged( int value ); void on_savePB_clicked(); private: void paintMarkers( bool checked, const QSize& printSize ); private: KChart::Chart* m_chart; TableModel m_model; KChart::DatasetProxyModel* m_datasetProxy; KChart::LineDiagram* m_lines; KChart::Legend* m_legend; QPixmap m_pix1, m_pix2; QLabel* m_smallChart1; QLabel* m_smallChart2; }; #endif /* MAINWINDOW_H */ diff --git a/qtests/Legends/main.cpp b/qtests/Legends/main.cpp index eeac35f..40d1e85 100644 --- a/qtests/Legends/main.cpp +++ b/qtests/Legends/main.cpp @@ -1,150 +1,150 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include using namespace KChart; class TestLegends: public QObject { Q_OBJECT private slots: void initTestCase() { - m_chart = new Chart(0); + m_chart = new Chart(nullptr); m_lines = new LineDiagram(); m_bars = new BarDiagram(); m_tableModel = new TableModel( this ); m_tableModel->loadFromCSV( "../../examples/tools/modeldata/KChart-Test-Datatables.csv" ); m_lines->setModel( m_tableModel ); m_bars->setModel( m_tableModel ); m_chart->coordinatePlane()->replaceDiagram( m_lines ); } void testIntialOwnership() { Legend* l = new Legend( m_chart ); m_chart->addLegend( l ); QCOMPARE( m_chart->legend(), l ); - QCOMPARE( l->diagram(), (AbstractDiagram*)0); + QCOMPARE( l->diagram(), (AbstractDiagram*)nullptr); l->setDiagram( m_lines ); QCOMPARE( dynamic_cast< LineDiagram * >(l->diagram()), m_lines ); } void testIntialOwnershipFromCtor() { Legend* l = new Legend( m_lines, m_chart ); m_chart->replaceLegend( l ); QCOMPARE( m_chart->legend(), l ); QCOMPARE( dynamic_cast< LineDiagram * >(l->diagram()), m_lines ); } void testReplacing() { Legend* l = new Legend( m_chart ); QPointer oldLegend = m_chart->legend(); QCOMPARE( dynamic_cast< LineDiagram * >(oldLegend->diagram()), m_lines ); m_chart->replaceLegend( l, oldLegend ); QVERIFY( oldLegend.isNull() ); - QCOMPARE( l->diagram(), (AbstractDiagram*)0 ); + QCOMPARE( l->diagram(), (AbstractDiagram*)nullptr ); l->setDiagram( m_lines ); QCOMPARE( dynamic_cast< LineDiagram * >(l->diagram()), m_lines ); } void testReferenceArea() { Legend* l = new Legend( ); - QCOMPARE( l->referenceArea(), ( const QWidget* )0 ); + QCOMPARE( l->referenceArea(), ( const QWidget* )nullptr ); l->setReferenceArea( m_chart ); QCOMPARE( dynamic_cast< const Chart * >(l->referenceArea()), const_cast< const Chart * >(m_chart) ); Legend* l1 = new Legend( m_chart ); QCOMPARE( dynamic_cast< const Chart * >(l1->referenceArea()), const_cast< const Chart * >(m_chart) ); Legend* l2 = new Legend( m_lines, m_chart ); QCOMPARE( dynamic_cast< const Chart * >(l2->referenceArea()), const_cast< const Chart * >(m_chart) ); } void testDiagramOwnership() { Legend* l = new Legend( m_chart ); QVERIFY( l->diagrams().size() == 0 ); l->addDiagram( m_lines ); QVERIFY( l->diagrams().size() == 1 ); l->addDiagram( m_bars ); QVERIFY( l->diagrams().size() == 2 ); QCOMPARE( dynamic_cast< LineDiagram * >(l->diagram()), m_lines ); l->removeDiagram( m_lines ); QVERIFY( l->diagrams().size() == 1 ); QCOMPARE( dynamic_cast< BarDiagram * >(l->diagram()), m_bars ); l->replaceDiagram( m_lines, m_bars ); QVERIFY( l->diagrams().size() == 1 ); QCOMPARE( dynamic_cast< LineDiagram * >(l->diagram()), m_lines ); } void testLegendSettings() { Legend* l = new Legend( m_lines, m_chart ); QVERIFY( l->position() == Position::NorthEast ); QVERIFY( l->alignment() == Qt::AlignCenter ); QVERIFY( l->orientation() == Qt::Vertical ); QVERIFY( l->showLines() == false ); QVERIFY( !l->titleText().isEmpty() ); QVERIFY( l->spacing() == 1 ); QVERIFY( l->legendStyle() == Legend::MarkersOnly ); l->setPosition( Position::North ); l->setAlignment( Qt::AlignLeft ); l->setOrientation( Qt::Horizontal ); l->setShowLines( true ); l->setTitleText( QString( tr( "Lines" ) ) ); l->setSpacing( 2 ); l->setLegendStyle( Legend::LinesOnly ); QVERIFY( l->position() == Position::North ); QVERIFY( l->alignment() == Qt::AlignLeft ); QVERIFY( l->orientation() == Qt::Horizontal ); QVERIFY( l->showLines() == true ); QVERIFY( l->titleText() == QString( tr( "Lines" ) ) ); QVERIFY( l->spacing() == 2 ); QVERIFY( l->legendStyle() == Legend::LinesOnly ); } void cleanupTestCase() { } private: Chart *m_chart; BarDiagram *m_bars; LineDiagram *m_lines; TableModel *m_tableModel; }; QTEST_MAIN(TestLegends) #include "main.moc" diff --git a/qtests/LineDiagrams/main.cpp b/qtests/LineDiagrams/main.cpp index e3fca71..15282e3 100644 --- a/qtests/LineDiagrams/main.cpp +++ b/qtests/LineDiagrams/main.cpp @@ -1,186 +1,186 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include using namespace KChart; class TestLineDiagrams: public QObject { Q_OBJECT private slots: void initTestCase() { - m_chart = new Chart(0); + m_chart = new Chart(nullptr); m_model = new TableModel( this ); m_model->loadFromCSV( ":/data" ); m_lines = new LineDiagram(); m_lines->setModel( m_model ); m_chart->coordinatePlane()->replaceDiagram( m_lines ); } void testLineDiagramType() { QCOMPARE( m_lines->type(), LineDiagram::Normal ); m_lines->setType( LineDiagram::Stacked ); QCOMPARE( m_lines->type(), LineDiagram::Stacked ); m_lines->setType( LineDiagram::Percent ); QCOMPARE( m_lines->type(), LineDiagram::Percent ); //reset to normal m_lines->setType( LineDiagram::Normal ); } void testLineAttributesLevelSettings() { //check segments const int rows = m_lines->model()->rowCount(); QCOMPARE( m_lines->numberOfAbscissaSegments(), rows ); const int cols = m_lines->model()->columnCount(); QCOMPARE( m_lines->numberOfOrdinateSegments(), cols ); QModelIndex idx = m_model->index(rows-3, cols-3, QModelIndex()); // create attribut LineAttributes la( m_lines->lineAttributes() ); LineAttributes laCol( m_lines->lineAttributes() ); LineAttributes laIndex( m_lines->lineAttributes() ); // modify at different level and compare laCol.setMissingValuesPolicy ( LineAttributes::MissingValuesHideSegments ); laIndex.setMissingValuesPolicy ( LineAttributes::MissingValuesShownAsZero ); m_lines->setLineAttributes( la ); m_lines->setLineAttributes( cols-2, laCol ); m_lines->setLineAttributes( idx, laIndex ); QVERIFY( m_lines->lineAttributes() != m_lines->lineAttributes(cols-2) ); QVERIFY( m_lines->lineAttributes() != m_lines->lineAttributes(idx) ); QVERIFY( m_lines->lineAttributes(cols-2) != m_lines->lineAttributes(idx) ); QCOMPARE( m_lines->lineAttributes(), la ); QCOMPARE( m_lines->lineAttributes( cols - 2 ), laCol ); QCOMPARE( m_lines->lineAttributes( idx ), laIndex ); // try and override the cols and index level - should not work m_lines->setLineAttributes( la ); QVERIFY( m_lines->lineAttributes().missingValuesPolicy() == LineAttributes::MissingValuesAreBridged ); QVERIFY( m_lines->lineAttributes( cols-2 ).missingValuesPolicy() == LineAttributes::MissingValuesHideSegments ); QVERIFY( m_lines->lineAttributes( idx ).missingValuesPolicy() == LineAttributes::MissingValuesShownAsZero ); } void testLineAttributesValueSettings() { LineAttributes la( m_lines->lineAttributes() ); // check default values QVERIFY( la.missingValuesPolicy() == LineAttributes::MissingValuesAreBridged ); QVERIFY( la.displayArea() == false ); QVERIFY( la.transparency() == 255 ); //change settings la.setMissingValuesPolicy ( LineAttributes::MissingValuesShownAsZero ); la.setDisplayArea( true ); la.setTransparency( 100 ); m_lines->setLineAttributes( la ); // get new values QVERIFY( m_lines->lineAttributes().missingValuesPolicy() == LineAttributes::MissingValuesShownAsZero ); QVERIFY( m_lines->lineAttributes().displayArea() == true ); QVERIFY( m_lines->lineAttributes().transparency() == 100 ); } void testThreeDLineAttributesLevelSettings() { //check segments const int rows = m_lines->model()->rowCount(); QCOMPARE( m_lines->numberOfAbscissaSegments(), rows ); const int cols = m_lines->model()->columnCount(); QCOMPARE( m_lines->numberOfOrdinateSegments(), cols ); QModelIndex idx = m_model->index(rows-3, cols-3, QModelIndex()); // create attribut ThreeDLineAttributes td( m_lines->threeDLineAttributes() ); ThreeDLineAttributes tdCol( m_lines->threeDLineAttributes() ); ThreeDLineAttributes tdIndex( m_lines->threeDLineAttributes() ); // modify at different level and compare tdCol.setDepth(25 ); tdIndex.setDepth( 30 ); m_lines->setThreeDLineAttributes( td ); m_lines->setThreeDLineAttributes( cols-2, tdCol ); m_lines->setThreeDLineAttributes( idx, tdIndex ); QVERIFY( m_lines->threeDLineAttributes() != m_lines->threeDLineAttributes(cols-2) ); QVERIFY( m_lines->threeDLineAttributes() != m_lines->threeDLineAttributes(idx) ); QVERIFY( m_lines->threeDLineAttributes(cols-2) != m_lines->threeDLineAttributes(idx) ); QCOMPARE( m_lines->threeDLineAttributes(), td ); QCOMPARE( m_lines->threeDLineAttributes( cols - 2 ), tdCol ); QCOMPARE( m_lines->threeDLineAttributes( idx ), tdIndex ); // try and override the cols and index level - should not work m_lines->setThreeDLineAttributes( td ); QVERIFY( m_lines->threeDLineAttributes().depth() == 20 ); QVERIFY( m_lines->threeDLineAttributes( cols-2 ).depth() == 25 ); QVERIFY( m_lines->threeDLineAttributes( idx ).depth() == 30 ); } void testThreeDLineAttributesValueSettings() { ThreeDLineAttributes td( m_lines->threeDLineAttributes() ); //check default values //generics == AbstractThreeD QVERIFY( td.isEnabled() == false ); QVERIFY( td.depth() == 20 ); QVERIFY( td.validDepth() == 0.0 ); //bars specifics QVERIFY( td.lineXRotation() == 15 ); QVERIFY( td.lineYRotation() == 15 ); //set new values td.setEnabled( true ); td.setDepth( 40 ); td.setLineXRotation( 20 ); td.setLineYRotation( 25 ); m_lines->setThreeDLineAttributes( td ); //get new values QVERIFY( m_lines->threeDLineAttributes().isEnabled() == true ); QVERIFY( m_lines->threeDLineAttributes().depth() == 40 ); QVERIFY( m_lines->threeDLineAttributes().validDepth() == 40 ); QVERIFY( m_lines->threeDLineAttributes().lineXRotation() == 20 ); QVERIFY( m_lines->threeDLineAttributes().lineYRotation() == 25 ); } void cleanupTestCase() { } private: Chart *m_chart; LineDiagram *m_lines; TableModel *m_model; }; QTEST_MAIN(TestLineDiagrams) #include "main.moc" diff --git a/qtests/Measure/main.cpp b/qtests/Measure/main.cpp index 9200040..d3f4dce 100644 --- a/qtests/Measure/main.cpp +++ b/qtests/Measure/main.cpp @@ -1,156 +1,156 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include using namespace KChart; class TestMeasure : public QObject { Q_OBJECT private slots: void initTestCase() { - m_chart = new Chart(0); + m_chart = new Chart(nullptr); m_model = new TableModel( this ); m_model->loadFromCSV( ":/data" ); m_bars = new BarDiagram(); m_bars->setModel( m_model ); m_chart->coordinatePlane()->replaceDiagram( m_bars ); m_chart->setGeometry(100,100, 300,200); m_chart->show(); } void testMeasureCalculationModeAbsolute() { KChart::Measure m( 100.0 ); m.setCalculationMode( KChartEnums::MeasureCalculationModeAbsolute ); QCOMPARE( - m.calculatedValue(0, KChartEnums::MeasureOrientationAuto), + m.calculatedValue(nullptr, KChartEnums::MeasureOrientationAuto), 100.0 ); } void testMeasureCalculationModeRelativeHorizontalOrientation() { KChart::Measure m( 100.0, KChartEnums::MeasureCalculationModeRelative, KChartEnums::MeasureOrientationHorizontal ); m.setReferenceArea( m_chart ); QCOMPARE( - m.calculatedValue(0, KChartEnums::MeasureOrientationAuto), + m.calculatedValue(nullptr, KChartEnums::MeasureOrientationAuto), m_chart->size().width() / 10.0 ); } void testMeasureCalculationModeRelativeVerticalOrientation() { KChart::Measure m( 100.0, KChartEnums::MeasureCalculationModeRelative, KChartEnums::MeasureOrientationVertical ); m.setReferenceArea( m_chart ); QCOMPARE( - m.calculatedValue(0, KChartEnums::MeasureOrientationAuto), + m.calculatedValue(nullptr, KChartEnums::MeasureOrientationAuto), m_chart->size().height() / 10.0 ); } void testMeasureCalculationModeAuto() { KChart::Measure m( 100.0 ); m.setCalculationMode( KChartEnums::MeasureCalculationModeAuto ); QCOMPARE( m.calculatedValue(m_chart, KChartEnums::MeasureOrientationHorizontal), m_chart->size().width() / 10.0 ); QCOMPARE( m.calculatedValue(m_chart, KChartEnums::MeasureOrientationVertical), m_chart->size().height() / 10.0 ); QCOMPARE( m.calculatedValue(m_chart, KChartEnums::MeasureOrientationMinimum), qMin( m_chart->size().width(), m_chart->size().height() ) / 10.0 ); QCOMPARE( m.calculatedValue(m_chart, KChartEnums::MeasureOrientationMaximum), qMax( m_chart->size().width(), m_chart->size().height() ) / 10.0 ); } void testMeasureCalculationModeAutoArea() { KChart::Measure m( 100.0, KChartEnums::MeasureCalculationModeAutoArea, KChartEnums::MeasureOrientationHorizontal ); QCOMPARE( m.calculatedValue(m_chart, KChartEnums::MeasureOrientationAuto), m_chart->size().width() / 10.0 ); m.setReferenceOrientation( KChartEnums::MeasureOrientationVertical ); QCOMPARE( m.calculatedValue(m_chart, KChartEnums::MeasureOrientationAuto), m_chart->size().height() / 10.0 ); m.setReferenceOrientation( KChartEnums::MeasureOrientationMinimum ); QCOMPARE( m.calculatedValue(m_chart, KChartEnums::MeasureOrientationAuto), qMin( m_chart->size().width(), m_chart->size().height() ) / 10.0 ); m.setReferenceOrientation( KChartEnums::MeasureOrientationMaximum ); QCOMPARE( m.calculatedValue(m_chart, KChartEnums::MeasureOrientationAuto), qMax( m_chart->size().width(), m_chart->size().height() ) / 10.0 ); } void testMeasureAutoOrientation() { KChart::Measure m( 100.0, KChartEnums::MeasureCalculationModeAutoOrientation, KChartEnums::MeasureOrientationAuto ); m.setReferenceArea( m_chart ); QCOMPARE( - m.calculatedValue(0, KChartEnums::MeasureOrientationHorizontal), + m.calculatedValue(nullptr, KChartEnums::MeasureOrientationHorizontal), m_chart->size().width() / 10.0 ); QCOMPARE( - m.calculatedValue(0, KChartEnums::MeasureOrientationVertical), + m.calculatedValue(nullptr, KChartEnums::MeasureOrientationVertical), m_chart->size().height() / 10.0 ); QCOMPARE( - m.calculatedValue(0, KChartEnums::MeasureOrientationMinimum), + m.calculatedValue(nullptr, KChartEnums::MeasureOrientationMinimum), qMin( m_chart->size().width(), m_chart->size().height() ) / 10.0 ); QCOMPARE( - m.calculatedValue(0, KChartEnums::MeasureOrientationMaximum), + m.calculatedValue(nullptr, KChartEnums::MeasureOrientationMaximum), qMax( m_chart->size().width(), m_chart->size().height() ) / 10.0 ); } void cleanupTestCase() { } private: Chart *m_chart; BarDiagram *m_bars; TableModel *m_model; }; QTEST_MAIN(TestMeasure) #include "main.moc" diff --git a/qtests/ParamVsParam/ModelParamVsParam.h b/qtests/ParamVsParam/ModelParamVsParam.h index 406066d..3cd4f3c 100644 --- a/qtests/ParamVsParam/ModelParamVsParam.h +++ b/qtests/ParamVsParam/ModelParamVsParam.h @@ -1,55 +1,55 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SUMGUI_MODELPARAMVSPARAM_H #define SUMGUI_MODELPARAMVSPARAM_H #include #include class ModelParamVsParam: public QStandardItemModel { Q_OBJECT public: ModelParamVsParam( - QObject *p_parent = 0); + QObject *p_parent = nullptr); void setTimeout( int p_timeout); void startSampling(); void stopSampling(); void populate( int p_nrOfParameters = 8, int p_nrOfSamples = 100); private slots: void timeout(); private: QTimer m_timer; }; // class ModelParamVsParam #endif // SUMGUI_MODELPARAMVSPARAM_H diff --git a/qtests/ParamVsParam/ModelParamVsParamPlot.h b/qtests/ParamVsParam/ModelParamVsParamPlot.h index 46826b8..1e39f3d 100644 --- a/qtests/ParamVsParam/ModelParamVsParamPlot.h +++ b/qtests/ParamVsParam/ModelParamVsParamPlot.h @@ -1,54 +1,54 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SUMGUI_MODELPARAMVSPARAMPLOT_H #define SUMGUI_MODELPARAMVSPARAMPLOT_H #include class ModelParamVsParamPlot: public QSortFilterProxyModel { public: ModelParamVsParamPlot( QAbstractItemModel *p_sourceModel, int p_column1, int p_column2, - QObject *p_parent = 0); + QObject *p_parent = nullptr); int columnCount( const QModelIndex &p_parent = QModelIndex()) const Q_DECL_OVERRIDE; protected: QVariant data( const QModelIndex &p_index, int p_role = Qt::DisplayRole) const Q_DECL_OVERRIDE; bool filterAcceptsColumn( int p_source_column, const QModelIndex &p_source_parent) const Q_DECL_OVERRIDE; private: int m_column1; int m_column2; }; // class ModelParamVsParamPlot #endif // SUMGUI_MODELPARAMVSPARAMPLOT_H diff --git a/qtests/ParamVsParam/mainwindow.cpp b/qtests/ParamVsParam/mainwindow.cpp index adc6980..48e00b9 100644 --- a/qtests/ParamVsParam/mainwindow.cpp +++ b/qtests/ParamVsParam/mainwindow.cpp @@ -1,208 +1,208 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mainwindow.h" #include "ModelParamVsParam.h" #include "ModelParamVsParamPlot.h" #include #include #include #include #include #include #include using namespace KChart; MainWindow::MainWindow( QWidget *p_parent) : QWidget(p_parent), m_sourceModel(new ModelParamVsParam), m_timeoutLineEdit(new QLineEdit("1")), m_nrOfParametersLineEdit(new QLineEdit("4")), m_nrOfSamplesLineEdit(new QLineEdit("4")), m_paramVsParamGridLayout(new QGridLayout) { connect(m_timeoutLineEdit, SIGNAL(editingFinished()), SLOT(timeoutEditingFinished())); connect(m_nrOfParametersLineEdit, SIGNAL(editingFinished()), SLOT(editingFinished())); connect(m_nrOfSamplesLineEdit, SIGNAL(editingFinished()), SLOT(editingFinished())); QVBoxLayout *vBoxLayout = new QVBoxLayout; setLayout(vBoxLayout); QGridLayout *gridLayout = new QGridLayout; vBoxLayout->addLayout(gridLayout); gridLayout->setColumnStretch(2, 1); int row(0); gridLayout->addWidget(new QLabel("Timeout [sec]:"), row, 0); gridLayout->addWidget(m_timeoutLineEdit, row++, 1); gridLayout->addWidget(new QLabel("Number of Parameters:"), row, 0); gridLayout->addWidget(m_nrOfParametersLineEdit, row++, 1); gridLayout->addWidget(new QLabel("Number of Samples:"), row, 0); gridLayout->addWidget(m_nrOfSamplesLineEdit, row++, 1); vBoxLayout->addLayout(m_paramVsParamGridLayout); m_sourceModel->setTimeout(m_timeoutLineEdit->text().toInt()); m_sourceModel->populate(m_nrOfParametersLineEdit->text().toInt(), m_nrOfSamplesLineEdit->text().toInt()); addPlots(); } // MainWindow::MainWindow() MainWindow::~MainWindow() { delete m_sourceModel; - m_sourceModel = NULL; + m_sourceModel = nullptr; } // MainWindow::~MainWindow() void MainWindow::timeoutEditingFinished() { if (m_timeoutLineEdit->isModified()) { m_timeoutLineEdit->setModified(false); m_sourceModel->setTimeout(m_timeoutLineEdit->text().toInt()); } } // MainWindow::timeoutEditingFinished() void MainWindow::editingFinished() { if (m_nrOfParametersLineEdit->isModified() || m_nrOfSamplesLineEdit->isModified()) { m_nrOfParametersLineEdit->setModified(false); m_nrOfSamplesLineEdit->setModified(false); m_sourceModel->populate(m_nrOfParametersLineEdit->text().toInt(), m_nrOfSamplesLineEdit->text().toInt()); m_sourceModel->stopSampling(); removePlots(); addPlots(); m_sourceModel->startSampling(); } } // MainWindow::editingFinished() void MainWindow::addPlots() { for (int r = 0; r < m_sourceModel->columnCount(); r++) { for (int c = 0; c < m_sourceModel->columnCount(); c++) { Chart * chart = new Chart; m_paramVsParamGridLayout->addWidget(chart, r, c); CartesianCoordinatePlane * plane = static_cast(chart->coordinatePlane()); // Hide grid. GridAttributes ga = plane->globalGridAttributes(); ga.setGridVisible(false); plane->setGlobalGridAttributes(ga); // Set axes fixed scale. qreal xoffset(c * 10); qreal yoffset(r * 10); QPair horizontalRange(xoffset, xoffset + 10); QPair verticalRange(yoffset, yoffset + 10); plane->setHorizontalRange(horizontalRange); plane->setVerticalRange(verticalRange); if (r == c) { } else { ModelParamVsParamPlot *modelParamVsParamPlot = new ModelParamVsParamPlot(m_sourceModel, c, r); LineDiagram *lineDiagram = new LineDiagram; lineDiagram->setDatasetDimension(2); lineDiagram->setModel(modelParamVsParamPlot); lineDiagram->setPen(Qt::NoPen); setMarkerAttributes(lineDiagram); CartesianAxis *xAxis = new CartesianAxis(lineDiagram); CartesianAxis *yAxis = new CartesianAxis(lineDiagram); xAxis->setPosition(CartesianAxis::Bottom); yAxis->setPosition(CartesianAxis::Left); xAxis->setTitleText('P' + QString::number(c)); yAxis->setTitleText('P' + QString::number(r)); lineDiagram->addAxis(xAxis); lineDiagram->addAxis(yAxis); chart->coordinatePlane()->replaceDiagram(lineDiagram); } } // for all rows } // for all columns } // MainWindow::addPlots() void MainWindow::removePlots() { while (m_paramVsParamGridLayout->count()) { Chart *chart = static_cast(m_paramVsParamGridLayout->itemAt(0)->widget()); m_paramVsParamGridLayout->removeWidget(chart); delete chart; } } // MainWindow::removePlots() void MainWindow::setMarkerAttributes( KChart::LineDiagram *p_lineDiagram) { QColor markerColor = Qt::green; MarkerAttributes::MarkerStyle markerStyle = MarkerAttributes::Marker4Pixels; QColor firstMarkerColor = Qt::red; MarkerAttributes::MarkerStyle firstMarkerStyle = MarkerAttributes::MarkerDiamond; DataValueAttributes dva = p_lineDiagram->dataValueAttributes(); MarkerAttributes ma = dva.markerAttributes(); TextAttributes ta = dva.textAttributes(); ma.setVisible(true); ma.setMarkerColor(markerColor); ma.setMarkerStyle(markerStyle); dva.setMarkerAttributes(ma); ta.setVisible(false); dva.setTextAttributes(ta); dva.setVisible(true); p_lineDiagram->setDataValueAttributes(0, dva); p_lineDiagram->setDataValueAttributes(1, dva); // Override for first row. ma.setMarkerColor(firstMarkerColor); ma.setMarkerStyle(firstMarkerStyle); dva.setMarkerAttributes(ma); QAbstractItemModel *model = p_lineDiagram->model(); p_lineDiagram->setDataValueAttributes(model->index(0, 0), dva); p_lineDiagram->setDataValueAttributes(model->index(0, 1), dva); } // MainWindow::setMarkerAttributes() diff --git a/qtests/ParamVsParam/mainwindow.h b/qtests/ParamVsParam/mainwindow.h index e2513ab..f2e99be 100644 --- a/qtests/ParamVsParam/mainwindow.h +++ b/qtests/ParamVsParam/mainwindow.h @@ -1,67 +1,67 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include class ModelParamVsParam; class QGridLayout; class QLineEdit; namespace KChart { class LineDiagram; } class MainWindow: public QWidget { Q_OBJECT public: MainWindow( - QWidget *p_parent = 0); + QWidget *p_parent = nullptr); virtual ~MainWindow(); private slots: void timeoutEditingFinished(); void editingFinished(); private: void addPlots(); void removePlots(); void setMarkerAttributes( KChart::LineDiagram *p_lineDiagram); private: ModelParamVsParam *m_sourceModel; QLineEdit *m_timeoutLineEdit; QLineEdit *m_nrOfParametersLineEdit; QLineEdit *m_nrOfSamplesLineEdit; QGridLayout *m_paramVsParamGridLayout; }; // class MainWindow #endif // MAINWINDOW_H diff --git a/qtests/PieDiagrams/main.cpp b/qtests/PieDiagrams/main.cpp index 9c9f856..253e358 100644 --- a/qtests/PieDiagrams/main.cpp +++ b/qtests/PieDiagrams/main.cpp @@ -1,158 +1,158 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include using namespace KChart; class TestPieDiagrams: public QObject { Q_OBJECT private slots: void initTestCase() { - m_chart = new Chart(0); + m_chart = new Chart(nullptr); PolarCoordinatePlane* polarPlane = new PolarCoordinatePlane( m_chart ); m_chart->replaceCoordinatePlane( polarPlane ); m_model = new TableModel( this ); m_model->loadFromCSV( ":/data" ); m_pie = new PieDiagram(); m_pie->setModel( m_model ); m_chart->coordinatePlane()->replaceDiagram( m_pie ); } void testPieDiagramsSettings() { QVERIFY( m_pie->granularity() == 1 ); m_pie->setGranularity( 2 ); QVERIFY( m_pie->granularity() == 2 ); } void testPieAttributesLevelSettings() { //check segments const int cols = m_pie->model()->columnCount(); QCOMPARE( m_pie->columnCount(), cols ); // create attribut PieAttributes pa( m_pie->pieAttributes() ); PieAttributes paCol( m_pie->pieAttributes() ); // modify at different level and compare paCol.setExplode ( true ); m_pie->setPieAttributes( pa ); m_pie->setPieAttributes( cols-2, paCol ); QVERIFY( m_pie->pieAttributes() != m_pie->pieAttributes(cols-2) ); QCOMPARE( m_pie->pieAttributes(), pa ); QCOMPARE( m_pie->pieAttributes( cols - 2 ), paCol ); // try and override the cols and index level - should not work m_pie->setPieAttributes( pa ); QVERIFY( m_pie->pieAttributes().explode() == false ); QVERIFY( m_pie->pieAttributes( cols-2 ).explode() == true ); } void testPieAttributesValueSettings() { PieAttributes pa( m_pie->pieAttributes() ); // check default values QVERIFY( pa.explode() == false ); QVERIFY( pa.explodeFactor() == 0.0 ); //change settings pa.setExplode ( true ); pa.setExplodeFactor( 0.2 ); m_pie->setPieAttributes( pa ); // get new values QVERIFY( m_pie->pieAttributes().explode() == true ); QVERIFY( m_pie->pieAttributes().explodeFactor() == 0.2 ); } void testThreeDPieAttributesLevelSettings() { //check segments const int cols = m_pie->model()->columnCount(); QCOMPARE( m_pie->columnCount(), cols ); // create attribut ThreeDPieAttributes td( m_pie->threeDPieAttributes() ); ThreeDPieAttributes tdCol( m_pie->threeDPieAttributes() ); // modify at different level and compare tdCol.setDepth(25 ); m_pie->setThreeDPieAttributes( td ); m_pie->setThreeDPieAttributes( cols-2, tdCol ); QVERIFY( m_pie->threeDPieAttributes() != m_pie->threeDPieAttributes(cols-2) ); QCOMPARE( m_pie->threeDPieAttributes(), td ); QCOMPARE( m_pie->threeDPieAttributes( cols - 2 ), tdCol ); // try and override the cols and index level - should not work m_pie->setThreeDPieAttributes( td ); QVERIFY( m_pie->threeDPieAttributes().depth() == -10 ); QVERIFY( m_pie->threeDPieAttributes( cols-2 ).depth() == 25 ); } void testThreeDPieAttributesValueSettings() { ThreeDPieAttributes td( m_pie->threeDPieAttributes() ); //check default values QVERIFY( td.isEnabled() == false ); QVERIFY( td.depth() == -10 ); QVERIFY( td.validDepth() == 0.0 ); //pie specifics QVERIFY( td.useShadowColors() == true ); //set new values td.setEnabled( true ); td.setDepth( 40 ); td.setUseShadowColors( false ); // not implemented yet m_pie->setThreeDPieAttributes( td ); //get new values QVERIFY( m_pie->threeDPieAttributes().isEnabled() == true ); QVERIFY( m_pie->threeDPieAttributes().depth() == 40 ); QVERIFY( m_pie->threeDPieAttributes().validDepth() == 40 ); QVERIFY( m_pie->threeDPieAttributes().useShadowColors() == false ); } void cleanupTestCase() { } private: Chart *m_chart; PieDiagram *m_pie; TableModel *m_model; }; QTEST_MAIN(TestPieDiagrams) #include "main.moc" diff --git a/qtests/PolarDiagrams/main.cpp b/qtests/PolarDiagrams/main.cpp index fcb43ed..79668ad 100644 --- a/qtests/PolarDiagrams/main.cpp +++ b/qtests/PolarDiagrams/main.cpp @@ -1,92 +1,92 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include using namespace KChart; class TestPolarDiagrams: public QObject { Q_OBJECT private slots: void initTestCase() { - m_chart = new Chart(0); + m_chart = new Chart(nullptr); PolarCoordinatePlane* polarPlane = new PolarCoordinatePlane( m_chart ); m_chart->replaceCoordinatePlane( polarPlane ); m_model = new TableModel( this ); m_model->loadFromCSV( ":/data" ); m_polar = new PolarDiagram(); m_polar->setModel( m_model ); m_chart->coordinatePlane()->replaceDiagram( m_polar ); } void testPolarDiagramsSettings() { QVERIFY( m_polar->rotateCircularLabels() == false ); QVERIFY( m_polar->showDelimitersAtPosition( Position::NorthWest ) == false ); QVERIFY( m_polar->showDelimitersAtPosition( Position::North ) == false ); QVERIFY( m_polar->showDelimitersAtPosition( Position::NorthEast ) == false ); QVERIFY( m_polar->showDelimitersAtPosition( Position::West ) == false ); QVERIFY( m_polar->showDelimitersAtPosition( Position::East ) == false ); QVERIFY( m_polar->showDelimitersAtPosition( Position::SouthWest ) == false ); QVERIFY( m_polar->showDelimitersAtPosition( Position::South ) == false ); QVERIFY( m_polar->showDelimitersAtPosition( Position::SouthEast ) == false ); QVERIFY( m_polar->showLabelsAtPosition( Position::NorthWest ) == false ); QVERIFY( m_polar->showLabelsAtPosition( Position::North ) == false ); QVERIFY( m_polar->showLabelsAtPosition( Position::NorthEast ) == false ); QVERIFY( m_polar->showLabelsAtPosition( Position::West ) == false ); QVERIFY( m_polar->showLabelsAtPosition( Position::East ) == false ); QVERIFY( m_polar->showLabelsAtPosition( Position::SouthWest ) == false ); QVERIFY( m_polar->showLabelsAtPosition( Position::South ) == false ); QVERIFY( m_polar->showLabelsAtPosition( Position::SouthEast ) == false ); qDebug() << "void PolarDiagram::init() is never called.....(?)"; m_polar->setRotateCircularLabels( true ); m_polar->setShowDelimitersAtPosition( Position::North, true ); m_polar->setShowDelimitersAtPosition( Position::South, true ); m_polar->setShowLabelsAtPosition( Position::North, true ); m_polar->setShowLabelsAtPosition( Position::South, true ); QVERIFY( m_polar->rotateCircularLabels() == true ); QVERIFY( m_polar->showDelimitersAtPosition( Position::North ) == true ); QVERIFY( m_polar->showDelimitersAtPosition( Position::South ) == true ); QVERIFY( m_polar->showLabelsAtPosition( Position::North ) == true ); QVERIFY( m_polar->showLabelsAtPosition( Position::South ) == true ); } void cleanupTestCase() { } private: Chart *m_chart; PolarDiagram *m_polar; TableModel *m_model; }; QTEST_MAIN(TestPolarDiagrams) #include "main.moc" diff --git a/qtests/PolarPlanes/main.cpp b/qtests/PolarPlanes/main.cpp index 2acc1cd..94392c4 100644 --- a/qtests/PolarPlanes/main.cpp +++ b/qtests/PolarPlanes/main.cpp @@ -1,161 +1,161 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include using namespace KChart; class TestPolarPlanes: public QObject { Q_OBJECT private slots: void initTestCase() { - m_chart = new Chart(0); + m_chart = new Chart(nullptr); m_tableModel = new TableModel( this ); m_tableModel->loadFromCSV( ":/data" ); m_pie = new PieDiagram(); m_pie->setModel( m_tableModel ); m_polar = new PolarDiagram(); m_polar->setModel( m_tableModel ); m_plane = new PolarCoordinatePlane(); m_chart->addCoordinatePlane( m_plane ); m_plane->setReferenceCoordinatePlane( m_chart->coordinatePlane() ); } void testIntialOwnership() { AbstractCoordinatePlane *plane = m_chart->coordinatePlane(); QCOMPARE( m_plane->referenceCoordinatePlane(), m_chart->coordinatePlane() ); - m_chart->takeCoordinatePlane( 0 ); + m_chart->takeCoordinatePlane( nullptr ); delete plane; - QCOMPARE( m_plane->referenceCoordinatePlane(), (AbstractCoordinatePlane*)0 ); + QCOMPARE( m_plane->referenceCoordinatePlane(), (AbstractCoordinatePlane*)nullptr ); } void testStartPositionSettings() { m_plane->addDiagram( m_pie ); QVERIFY( m_plane->startPosition() == 0.0 ); qreal pos = 45; m_plane->addDiagram( m_pie ); m_plane->setStartPosition( pos ); QVERIFY( m_plane->startPosition() == pos ); m_plane->takeDiagram( m_pie ); } void testZoomFactorsSettings() { m_plane->addDiagram( m_pie ); QCOMPARE( m_plane->zoomFactorX(), 1.0 ); QCOMPARE( m_plane->zoomFactorY(), 1.0 ); QCOMPARE( m_plane->zoomCenter(), QPointF( 0.5, 0.5 ) ); m_plane->setZoomFactorX( 1.5 ); m_plane->setZoomFactorY( 1.5 ); m_plane->setZoomCenter( QPointF ( 1.0, 1.0 ) ); QCOMPARE( m_plane->zoomFactorX(), 1.5 ); QCOMPARE( m_plane->zoomFactorY(), 1.5 ); QCOMPARE( m_plane->zoomCenter(), QPointF( 1.0, 1.0 ) ); m_plane->takeDiagram( m_pie ); } void testDiagramOwnership() { QCOMPARE( m_plane->diagrams().size(), 1 ); m_plane->addDiagram( m_polar ); QCOMPARE( m_plane->diagrams().size(), 2 ); QCOMPARE( dynamic_cast< PieDiagram * >(m_plane->diagram()), m_pie ); m_plane->takeDiagram( m_pie ); QCOMPARE( m_plane->diagrams().size(), 1 ); QCOMPARE( dynamic_cast< PolarDiagram * >(m_plane->diagram()), m_polar ); m_plane->replaceDiagram( m_pie, m_polar ); QCOMPARE( m_plane->diagrams().size(), 1 ); QCOMPARE( dynamic_cast< PieDiagram * >(m_plane->diagram()), m_pie ); m_plane->takeDiagram( m_pie ); QCOMPARE( m_plane->diagrams().size(), 0 ); delete m_pie; } void testGlobalGridAttributesSettings() { GridAttributes ga = m_plane->globalGridAttributes(); QVERIFY( ga.isGridVisible() == true ); ga.setGridVisible( false ); m_plane->setGlobalGridAttributes( ga ); QVERIFY( m_plane->globalGridAttributes().isGridVisible() == false ); //reset to normal ga.setGridVisible( true ); QVERIFY( m_plane->globalGridAttributes().isGridVisible() == false ); m_plane->setGlobalGridAttributes( ga ); QVERIFY( m_plane->globalGridAttributes().isGridVisible() == true ); } void testGridAttributesSettings() { GridAttributes gcircular = m_plane->gridAttributes( true ); GridAttributes gsagittal = m_plane->gridAttributes( false ); QVERIFY( gcircular.isGridVisible() == true ); gcircular.setGridVisible( false ); m_plane->setGridAttributes( true, gcircular ); QVERIFY( m_plane->hasOwnGridAttributes( true ) == true ); QVERIFY( m_plane->hasOwnGridAttributes( false ) == false ); QVERIFY( m_plane->gridAttributes( true ).isGridVisible() == false ); QVERIFY( m_plane->gridAttributes( false ).isGridVisible() == true ); gsagittal.setGridVisible( false ); m_plane->setGridAttributes( false, gsagittal ); QVERIFY( m_plane->hasOwnGridAttributes( true ) == true ); QVERIFY( m_plane->hasOwnGridAttributes( true ) == true ); QVERIFY( m_plane->gridAttributes( true ).isGridVisible() == false ); QVERIFY( m_plane->gridAttributes( false ).isGridVisible() == false ); m_plane->resetGridAttributes( true ); m_plane->resetGridAttributes( false ); QVERIFY( m_plane->gridAttributes( true ).isGridVisible() == true ); QVERIFY( m_plane->gridAttributes( false ).isGridVisible() == true ); QVERIFY( m_plane->hasOwnGridAttributes( true ) == false ); QVERIFY( m_plane->hasOwnGridAttributes( false ) == false ); } void cleanupTestCase() { } private: Chart *m_chart; PieDiagram *m_pie; PolarDiagram *m_polar; PolarCoordinatePlane *m_plane; TableModel *m_tableModel; }; QTEST_MAIN(TestPolarPlanes) #include "main.moc" diff --git a/qtests/QLayout/main.cpp b/qtests/QLayout/main.cpp index f176601..4d4b736 100644 --- a/qtests/QLayout/main.cpp +++ b/qtests/QLayout/main.cpp @@ -1,154 +1,154 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #define KDAB_REIMP class MyLegendWidget : public QWidget { Q_OBJECT public: explicit MyLegendWidget( QWidget* parent ) : QWidget( parent ) { } void makeSizeFixed() { setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); // like Legend setFixedSize( 50, 50 ); // hmm Legend has a layout instead. //QGridLayout* layout = new QGridLayout( this ); // ... } protected: KDAB_REIMP void resizeEvent( QResizeEvent* ) Q_DECL_OVERRIDE { // Note that this is never called unless the widget is shown. qDebug() << "resizeEvent " << size(); } }; class MyWidgetItem : public QWidgetItem { public: explicit MyWidgetItem(QWidget *w) : QWidgetItem(w) { } KDAB_REIMP bool isEmpty() const Q_DECL_OVERRIDE { return false; } }; class TestQLayout : public QObject { Q_OBJECT private slots: // This is very much like KChart::Chart does with legends void testBoxLayoutHiddenWidget() { QBoxLayout* vLayout = new QVBoxLayout; - MyLegendWidget* widget1 = new MyLegendWidget( 0 ); + MyLegendWidget* widget1 = new MyLegendWidget( nullptr ); widget1->resize( 10, 10 ); // Adding a hidden widget doesn't work, the layout ignores it // This is why we created MyWidgetItem above, then the layout works //vLayout->addWidget( widget1 ); MyWidgetItem* widgetItem = new MyWidgetItem( widget1 ); vLayout->addItem( widgetItem ); QRect geom( 100, 100, 800, 800 ); vLayout->setGeometry( geom ); //vLayout->activate(); // not needed QCOMPARE( vLayout->geometry(), geom ); qDebug() << "widget1: " << widget1->geometry(); QCOMPARE( widget1->geometry(), geom ); delete widget1; delete vLayout; } void testBoxLayoutChildWidget() { - QWidget* topLevelWidget = new QWidget( 0 ); + QWidget* topLevelWidget = new QWidget( nullptr ); // This time the layout is associated with a widget, like d->layout in KChart::Chart. QBoxLayout* vLayout = new QVBoxLayout( topLevelWidget ); MyLegendWidget* widget1 = new MyLegendWidget( topLevelWidget ); MyWidgetItem* widgetItem = new MyWidgetItem( widget1 ); vLayout->addItem( widgetItem ); //vLayout->activate(); QRect geom( 100, 100, 800, 800 ); vLayout->setGeometry( geom ); qDebug() << "widget1: " << widget1->geometry(); // int marg = topLevelWidget->style()->pixelMetric( QStyle::PM_DefaultTopLevelMargin ); int marg = vLayout->margin(); QCOMPARE( widget1->geometry(), geom.adjusted(marg,marg,-marg,-marg) ); geom = QRect( 10, 10, 80, 80 ); vLayout->setGeometry( geom ); qDebug() << "widget1: " << widget1->geometry(); QCOMPARE( widget1->geometry(), geom.adjusted(marg,marg,-marg,-marg) ); // And now let's show the widget for real geom = QRect( 0, 0, 500, 100 ); topLevelWidget->resize( geom.size() ); topLevelWidget->show(); QApplication::sendPostedEvents(); QRect expected = geom.adjusted(marg,marg,-marg,-marg); qDebug() << "widget1: " << widget1->frameGeometry() << "expecting" << expected; // this test is quite useless... //QCOMPARE( widget1->frameGeometry(), expected ); QVERIFY( widget1->isVisible() ); delete topLevelWidget; } void testSubGridLayout() { - QWidget* topLevelWidget = new QWidget( 0 ); + QWidget* topLevelWidget = new QWidget( nullptr ); QBoxLayout* vLayout = new QVBoxLayout( topLevelWidget ); QGridLayout* gridLayout = new QGridLayout(); QLineEdit* lineEdit = new QLineEdit( topLevelWidget ); MyWidgetItem* lineEditWidgetItem = new MyWidgetItem( lineEdit ); gridLayout->addItem( lineEditWidgetItem, 0, 0 ); MyLegendWidget* widget1 = new MyLegendWidget( topLevelWidget ); widget1->makeSizeFixed(); MyWidgetItem* widgetItem = new MyWidgetItem( widget1 ); gridLayout->addItem( widgetItem, 1, 1 ); vLayout->addLayout( gridLayout ); QRect geom( 100, 100, 800, 800 ); vLayout->setGeometry( geom ); qDebug() << "widget1: " << widget1->geometry(); QVERIFY( widget1->width() > 0 ); delete topLevelWidget; } }; QTEST_MAIN(TestQLayout) #include "main.moc" diff --git a/qtests/RelativePosition/main.cpp b/qtests/RelativePosition/main.cpp index a294968..039e5a5 100644 --- a/qtests/RelativePosition/main.cpp +++ b/qtests/RelativePosition/main.cpp @@ -1,108 +1,108 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include using namespace KChart; class TestRelativePosition: public QObject { Q_OBJECT private slots: void initTestCase() { - m_window = new QLabel(0); + m_window = new QLabel(nullptr); m_window->setGeometry(100,100, 300,200); m_window->show(); } void testRelativePositionHRelativeVAbsolute() { const qreal horizPerMille = 100.0; KChart::Measure mHoriz( horizPerMille, KChartEnums::MeasureCalculationModeRelative, KChartEnums::MeasureOrientationHorizontal ); mHoriz.setReferenceArea( m_window ); const qreal vertAbsolute = -50.0; KChart::Measure mVert( vertAbsolute, KChartEnums::MeasureCalculationModeAbsolute ); KChart::RelativePosition relPos; relPos.setReferenceArea( m_window ); relPos.setReferencePosition( KChart::Position::NorthWest ); relPos.setHorizontalPadding( mHoriz ); relPos.setVerticalPadding( mVert ); // no auto mode is active, so we pass an empty QSizeF: const QPointF pt( relPos.calculatedPoint( QSizeF() ) ); const QRectF geo( m_window->geometry() ); QCOMPARE( pt.x(), geo.topLeft().x() + geo.size().width() / 1000.0 * horizPerMille ); QCOMPARE( pt.y(), geo.topLeft().y() + vertAbsolute ); } void testRelativePositionHAbsoluteVRelative() { const qreal horizAbsolute = 100.0; KChart::Measure mHoriz( horizAbsolute, KChartEnums::MeasureCalculationModeAbsolute ); const qreal vertRelative = -50.0; KChart::Measure mVert( vertRelative, KChartEnums::MeasureCalculationModeRelative, KChartEnums::MeasureOrientationVertical ); mVert.setReferenceArea( m_window ); KChart::RelativePosition relPos; relPos.setReferenceArea( m_window ); relPos.setReferencePosition( KChart::Position::Center ); relPos.setHorizontalPadding( mHoriz ); relPos.setVerticalPadding( mVert ); // no auto mode is active, so we pass an empty QSizeF: const QPointF pt( relPos.calculatedPoint( QSizeF() ) ); const QRectF geo( m_window->geometry() ); QCOMPARE( pt.x(), geo.center().x() + horizAbsolute ); QCOMPARE( pt.y(), geo.center().y() + geo.size().height() / 1000.0 * vertRelative ); } void cleanupTestCase() { } private: QLabel *m_window; }; QTEST_MAIN(TestRelativePosition) #include "main.moc" diff --git a/qtests/WidgetElementOwnership/main.cpp b/qtests/WidgetElementOwnership/main.cpp index d2f2cbb..967157c 100644 --- a/qtests/WidgetElementOwnership/main.cpp +++ b/qtests/WidgetElementOwnership/main.cpp @@ -1,212 +1,212 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include //#include //#include //#include //#include #include //#include using namespace KChart; class TestWidgetElementOwnership: public QObject { Q_OBJECT private slots: void initTestCase() { - m_widget = new Widget(0); + m_widget = new Widget(nullptr); QVector< qreal > vec0; vec0.append( -2 ); vec0.append( -1 ); vec0.append( 0 ); vec0.append( 1 ); vec0.append( 2 ); m_widget->setDataset( 0, vec0, "Linear" ); QVector< qreal > vec1; vec1.append( -4 ); vec1.append( -2 ); vec1.append( 0 ); vec1.append( 2 ); vec1.append( 4 ); m_widget->setDataset( 1, vec1, "Quadratic" ); QVector< qreal > vec2; vec2.append( -8 ); vec2.append( -2 ); vec2.append( 0 ); vec2.append( 2 ); vec2.append( 8 ); m_widget->setDataset( 2, vec2, "Cubic" ); } void testPadding() { QVERIFY( m_widget->globalLeadingLeft() == false ); m_widget->setGlobalLeading( 2, 2, 2, 2 ); QCOMPARE( m_widget->globalLeadingLeft(), 2 ); QCOMPARE( m_widget->globalLeadingTop(), 2 ); QCOMPARE( m_widget->globalLeadingRight(), 2 ); QCOMPARE( m_widget->globalLeadingBottom(), 2 ); m_widget->setGlobalLeadingLeft( 5 ); QCOMPARE( m_widget->globalLeadingLeft(), 5 ); QCOMPARE( m_widget->globalLeadingTop(), 2 ); QCOMPARE( m_widget->globalLeadingRight(), 2 ); QCOMPARE( m_widget->globalLeadingBottom(), 2 ); m_widget->setGlobalLeadingTop( 5 ); QCOMPARE( m_widget->globalLeadingLeft(), 5 ); QCOMPARE( m_widget->globalLeadingTop(), 5 ); QCOMPARE( m_widget->globalLeadingRight(), 2 ); QCOMPARE( m_widget->globalLeadingBottom(), 2 ); m_widget->setGlobalLeadingRight( 5 ); QCOMPARE( m_widget->globalLeadingLeft(), 5 ); QCOMPARE( m_widget->globalLeadingTop(), 5 ); QCOMPARE( m_widget->globalLeadingRight(), 5 ); QCOMPARE( m_widget->globalLeadingBottom(), 2 ); m_widget->setGlobalLeadingBottom( 5 ); QCOMPARE( m_widget->globalLeadingLeft(), 5 ); QCOMPARE( m_widget->globalLeadingTop(), 5 ); QCOMPARE( m_widget->globalLeadingRight(), 5 ); QCOMPARE( m_widget->globalLeadingBottom(), 5 ); m_widget->setGlobalLeading( 2, 2, 2, 2 ); QCOMPARE( m_widget->globalLeadingLeft(), 2 ); QCOMPARE( m_widget->globalLeadingTop(), 2 ); QCOMPARE( m_widget->globalLeadingRight(), 2 ); QCOMPARE( m_widget->globalLeadingBottom(), 2 ); } void testHeaderFooterOwnership() { QCOMPARE( m_widget->allHeadersFooters().size(), 0 ); HeaderFooter * h = new HeaderFooter(); HeaderFooter * h1 = new HeaderFooter(); m_widget->addHeaderFooter( h ); m_widget->addHeaderFooter( h1 ); QCOMPARE( m_widget->allHeadersFooters().size(), 2 ); QCOMPARE( m_widget->firstHeaderFooter(), h ); m_widget->takeHeaderFooter( h ); QCOMPARE( m_widget->allHeadersFooters().size(), 1 ); QCOMPARE( m_widget->firstHeaderFooter(), h1 ); m_widget->replaceHeaderFooter( h , h1 ); QCOMPARE( m_widget->allHeadersFooters().size(), 1 ); delete h; QCOMPARE( m_widget->allHeadersFooters().size(), 0 ); } void testLegendOwnerShip() { // check no legend QCOMPARE( m_widget->allLegends().size(), 0 ); // check add legend - take legend - delete legend m_widget->addLegend( Position::North ); QCOMPARE( m_widget->allLegends().size(), 1 ); Legend* legend = m_widget->legend(); m_widget->takeLegend( legend ); QCOMPARE( m_widget->allLegends().size(), 0 ); m_widget->replaceLegend( legend ); QCOMPARE( m_widget->allLegends().size(), 1 ); delete legend; QCOMPARE( m_widget->allLegends().size(), 0 ); } void testRetrieveDiagram() { //set Cartesian type m_widget->setType( Widget::Line ); QCOMPARE( m_widget->type(), Widget::Line ); - QCOMPARE( m_widget->barDiagram(), static_cast(0) ); - QCOMPARE( m_widget->pieDiagram(), static_cast(0) ); - QCOMPARE( m_widget->ringDiagram(), static_cast(0) ); - QCOMPARE( m_widget->polarDiagram(), static_cast(0) ); + QCOMPARE( m_widget->barDiagram(), static_cast(nullptr) ); + QCOMPARE( m_widget->pieDiagram(), static_cast(nullptr) ); + QCOMPARE( m_widget->ringDiagram(), static_cast(nullptr) ); + QCOMPARE( m_widget->polarDiagram(), static_cast(nullptr) ); // set Polar type m_widget->setType( Widget::Polar ); QCOMPARE( m_widget->type(), Widget::Polar ); - QCOMPARE( m_widget->barDiagram(), static_cast(0) ); - QCOMPARE( m_widget->lineDiagram(), static_cast(0) ); - QCOMPARE( m_widget->ringDiagram(), static_cast(0) ); - QCOMPARE( m_widget->pieDiagram(), static_cast(0) ); + QCOMPARE( m_widget->barDiagram(), static_cast(nullptr) ); + QCOMPARE( m_widget->lineDiagram(), static_cast(nullptr) ); + QCOMPARE( m_widget->ringDiagram(), static_cast(nullptr) ); + QCOMPARE( m_widget->pieDiagram(), static_cast(nullptr) ); // reset default m_widget->setType( Widget::Line ); } void testTypeChangeWithLegend() { //set Cartesian type m_widget->addLegend( Position::North ); QCOMPARE( m_widget->allLegends().size(), 1 ); m_widget->setType( Widget::Bar, Widget::Stacked ); Legend *legend = m_widget->legend(); - QVERIFY( legend != 0 ); - QVERIFY( legend->diagram() != 0 ); + QVERIFY( legend != nullptr ); + QVERIFY( legend->diagram() != nullptr ); QCOMPARE( legend->diagram(), m_widget->diagram() ); QCOMPARE( m_widget->allLegends().size(), 1 ); m_widget->setType( Widget::Line ); } void testDiagramTypeSubType() { m_widget->setType( Widget::Line ); QCOMPARE( m_widget->type(), Widget::Line ); QCOMPARE( m_widget->subType(), Widget::Normal ); //check type subtype = default m_widget->setType( Widget::Bar ); QCOMPARE( m_widget->type(), Widget::Bar ); QCOMPARE( m_widget->subType(), Widget::Normal ); //check type subtype m_widget->setType( Widget::Line, Widget::Stacked ); QCOMPARE( m_widget->type(), Widget::Line ); QCOMPARE( m_widget->subType(), Widget::Stacked ); //check subtype m_widget->setSubType( Widget::Percent ); QCOMPARE( m_widget->subType(), Widget::Percent ); } void testPlaneOwnership() { // check plane AbstractCoordinatePlane*orig = m_widget->coordinatePlane(); QCOMPARE( m_widget->coordinatePlane(), orig ); } void testWidgetDeletion() { delete m_widget; } void cleanupTestCase() { } private: Widget *m_widget; }; QTEST_MAIN(TestWidgetElementOwnership) #include "main.moc" diff --git a/src/KChart/Cartesian/DiagramFlavors/KChartNormalBarDiagram_p.cpp b/src/KChart/Cartesian/DiagramFlavors/KChartNormalBarDiagram_p.cpp index 831c28b..c73d5d8 100644 --- a/src/KChart/Cartesian/DiagramFlavors/KChartNormalBarDiagram_p.cpp +++ b/src/KChart/Cartesian/DiagramFlavors/KChartNormalBarDiagram_p.cpp @@ -1,197 +1,197 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartNormalBarDiagram_p.h" #include #include "KChartBarDiagram.h" #include "KChartTextAttributes.h" #include "KChartAttributesModel.h" #include "KChartAbstractCartesianDiagram.h" using namespace KChart; using namespace std; NormalBarDiagram::NormalBarDiagram( BarDiagram* d ) : BarDiagramType( d ) { } BarDiagram::BarType NormalBarDiagram::type() const { return BarDiagram::Normal; } const QPair NormalBarDiagram::calculateDataBoundaries() const { const int rowCount = compressor().modelDataRows(); const int colCount = compressor().modelDataColumns(); const qreal xMin = 0.0; const qreal xMax = rowCount; qreal yMin = 0.0; qreal yMax = 0.0; qreal usedDepth = 0; bool isFirst = true; for ( int column = 0; column < colCount; ++column ) { for ( int row = 0; row < rowCount; ++row ) { const CartesianDiagramDataCompressor::CachePosition position( row, column ); const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); const qreal value = ISNAN( point.value ) ? 0.0 : point.value; QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( sourceIndex ); if ( threeDAttrs.isEnabled() ) usedDepth = qMax( usedDepth, threeDAttrs.depth() ); // this is always true yMin can be 0 in case all values // are the same // same for yMax it can be zero if all values are negative if ( isFirst ) { yMin = value; yMax = value; isFirst = false; } else { yMin = qMin( yMin, value ); yMax = qMax( yMax, value ); } } } // special cases if ( yMax == yMin ) { if ( yMin == 0.0 ) { yMax = 0.1; // we need at least a range } else if ( yMax < 0.0 ) { yMax = 0.0; // extend the range to zero } else if ( yMin > 0.0 ) { yMin = 0.0; // dito } } return QPair< QPointF, QPointF >( QPointF( xMin, yMin ), QPointF( xMax, yMax ) ); } void NormalBarDiagram::paint( PaintContext* ctx ) { reverseMapper().clear(); const QPair boundaries = diagram()->dataBoundaries(); // cached const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ; const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second ); const int rowCount = attributesModel()->rowCount(attributesModelRootIndex()); const int colCount = attributesModel()->columnCount(attributesModelRootIndex()); BarAttributes ba = diagram()->barAttributes(); ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes(); //we need some margin (hence the 2.5) for the three dimensional depth const qreal threeDepthMargin = ( threeDAttrs.isEnabled() ) ? 2.5 * threeDAttrs.depth() : 0; qreal barWidth = 0; qreal maxDepth = 0; qreal width = boundRight.x() - boundLeft.x() - threeDepthMargin; qreal groupWidth = width / rowCount; qreal spaceBetweenBars = 0; qreal spaceBetweenGroups = 0; if ( ba.useFixedBarWidth() ) { barWidth = ba.fixedBarWidth(); groupWidth += barWidth; // Pending Michel set a min and max value for the groupWidth // related to the area.width if ( groupWidth < 0 ) groupWidth = 0; if ( groupWidth * rowCount > width ) groupWidth = width / rowCount; } // maxLimit: allow the space between bars to be larger until area.width() // is covered by the groups. qreal maxLimit = rowCount * ( groupWidth + ( ( colCount - 1 ) * ba.fixedDataValueGap() ) ); //Pending Michel: FixMe if ( ba.useFixedDataValueGap() ) { if ( width > maxLimit ) { spaceBetweenBars += ba.fixedDataValueGap(); } else { spaceBetweenBars = ( ( width / rowCount ) - groupWidth ) / ( colCount - 1 ); } } if ( ba.useFixedValueBlockGap() ) { spaceBetweenGroups += ba.fixedValueBlockGap(); } calculateValueAndGapWidths( rowCount, colCount, groupWidth, barWidth, spaceBetweenBars, spaceBetweenGroups ); LabelPaintCache lpc; for ( int row = 0; row < rowCount; ++row ) { qreal offset = -groupWidth / 2 + spaceBetweenGroups / 2; if ( ba.useFixedDataValueGap() ) { if ( spaceBetweenBars > 0 ) { if ( width > maxLimit ) { offset -= ba.fixedDataValueGap(); } else { offset -= ( ( width / rowCount ) - groupWidth ) / ( colCount - 1 ); } } else { offset += barWidth / 2; } } for ( int column = 0; column < colCount; ++column ) { // paint one group const CartesianDiagramDataCompressor::CachePosition position( row, column ); const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); const qreal value = point.value;//attributesModel()->data( sourceIndex ).toReal(); if ( ! point.hidden && !ISNAN( value ) ) { QPointF topPoint = ctx->coordinatePlane()->translate( QPointF( point.key + 0.5, value ) ); QPointF bottomPoint = ctx->coordinatePlane()->translate( QPointF( point.key, 0 ) ); if ( threeDAttrs.isEnabled() ) { const qreal usedDepth = threeDAttrs.depth() / 4; topPoint.setY( topPoint.y() + usedDepth + 1.0 ); } const qreal barHeight = bottomPoint.y() - topPoint.y(); topPoint.setX( topPoint.x() + offset ); const QRectF rect( topPoint, QSizeF( barWidth, barHeight ) ); - m_private->addLabel( &lpc, sourceIndex, 0, PositionPoints( rect ), Position::North, + m_private->addLabel( &lpc, sourceIndex, nullptr, PositionPoints( rect ), Position::North, Position::South, point.value ); paintBars( ctx, sourceIndex, rect, maxDepth ); } offset += barWidth + spaceBetweenBars; } } m_private->paintDataValueTextsAndMarkers( ctx, lpc, false ); } diff --git a/src/KChart/Cartesian/DiagramFlavors/KChartNormalLyingBarDiagram_p.cpp b/src/KChart/Cartesian/DiagramFlavors/KChartNormalLyingBarDiagram_p.cpp index d2343ee..23e352a 100644 --- a/src/KChart/Cartesian/DiagramFlavors/KChartNormalLyingBarDiagram_p.cpp +++ b/src/KChart/Cartesian/DiagramFlavors/KChartNormalLyingBarDiagram_p.cpp @@ -1,183 +1,183 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartNormalLyingBarDiagram_p.h" #include #include "KChartBarDiagram.h" #include "KChartTextAttributes.h" #include "KChartAttributesModel.h" #include "KChartAbstractCartesianDiagram.h" using namespace KChart; using namespace std; NormalLyingBarDiagram::NormalLyingBarDiagram( BarDiagram* d ) : BarDiagramType( d ) { } BarDiagram::BarType NormalLyingBarDiagram::type() const { return BarDiagram::Normal; } // TODO there is a lot of duplication between this and the non-lying bar diagram, fix it someday... const QPair NormalLyingBarDiagram::calculateDataBoundaries() const { const int rowCount = compressor().modelDataRows(); const int colCount = compressor().modelDataColumns(); const qreal xMin = 0.0; const qreal xMax = rowCount; qreal yMin = 0.0; qreal yMax = 0.0; bool isFirst = true; for ( int column = 0; column < colCount; ++column ) { for ( int row = 0; row < rowCount; ++row ) { const CartesianDiagramDataCompressor::CachePosition position( row, column ); const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); const qreal value = ISNAN( point.value ) ? 0.0 : point.value; // this is always true yMin can be 0 in case all values // are the same // same for yMax it can be zero if all values are negative if ( isFirst ) { yMin = value; yMax = value; isFirst = false; } else { yMin = qMin( yMin, value ); yMax = qMax( yMax, value ); } } } // special cases if ( yMax == yMin ) { if ( yMin == 0.0 ) { yMax = 0.1; // we need at least a range } else if ( yMax < 0.0 ) { yMax = 0.0; // they are the same and negative } else if ( yMin > 0.0 ) { yMin = 0.0; // they are the same but positive } } const QPointF bottomLeft( QPointF( yMin, xMin ) ); const QPointF topRight( QPointF( yMax, xMax ) ); return QPair< QPointF, QPointF >( bottomLeft, topRight ); } void NormalLyingBarDiagram::paint( PaintContext* ctx ) { // FIXME: in all LyingBarDiagram types, the datasets are rendered top to bottom, but the abscissa // (in that case Y axis) ticks are still bottom to top. So tick labels are in reverse order. reverseMapper().clear(); const QPair boundaries = diagram()->dataBoundaries(); // cached const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ; const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second ); const int rowCount = attributesModel()->rowCount( attributesModelRootIndex() ); const int colCount = attributesModel()->columnCount( attributesModelRootIndex() ); BarAttributes ba = diagram()->barAttributes(); qreal barWidth = 0; qreal maxDepth = 0; qreal width = boundLeft.y() - boundRight.y(); qreal groupWidth = width / rowCount; qreal spaceBetweenBars = 0; qreal spaceBetweenGroups = 0; if ( ba.useFixedBarWidth() ) { barWidth = ba.fixedBarWidth(); groupWidth += barWidth; // Pending Michel set a min and max value for the groupWidth // related to the area.width if ( groupWidth < 0 ) groupWidth = 0; if ( groupWidth * rowCount > width ) groupWidth = width / rowCount; } // maxLimit: allow the space between bars to be larger until area.width() // is covered by the groups. qreal maxLimit = rowCount * ( groupWidth + ( colCount - 1 ) * ba.fixedDataValueGap() ); //Pending Michel: FixMe if ( ba.useFixedDataValueGap() ) { if ( width > maxLimit ) spaceBetweenBars += ba.fixedDataValueGap(); else spaceBetweenBars = ( width / rowCount - groupWidth ) / ( colCount - 1 ); } if ( ba.useFixedValueBlockGap() ) { spaceBetweenGroups += ba.fixedValueBlockGap(); } calculateValueAndGapWidths( rowCount, colCount,groupWidth, barWidth, spaceBetweenBars, spaceBetweenGroups ); LabelPaintCache lpc; for ( int row = 0; row < rowCount; row++ ) { qreal offset = -groupWidth / 2 + spaceBetweenGroups / 2; if ( ba.useFixedDataValueGap() ) { if ( spaceBetweenBars > 0 ) { if ( width > maxLimit ) { offset -= ba.fixedDataValueGap(); } else { offset -= ( width / rowCount - groupWidth ) / ( colCount - 1 ); } } else { offset += barWidth / 2; } } for ( int column = colCount-1; column >= 0; --column ) { // paint one group const CartesianDiagramDataCompressor::CachePosition position( row, column ); const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); QPointF dataPoint( 0, ( point.key + 0.5 ) ); const QPointF topLeft = ctx->coordinatePlane()->translate( dataPoint ); dataPoint.rx() += point.value; const QPointF bottomRight = ctx->coordinatePlane()->translate( dataPoint ) + QPointF( 0, barWidth ); const QRectF rect = QRectF( topLeft, bottomRight ).translated( 1.0, offset ); - m_private->addLabel( &lpc, sourceIndex, 0, PositionPoints( rect ), Position::North, + m_private->addLabel( &lpc, sourceIndex, nullptr, PositionPoints( rect ), Position::North, Position::South, point.value ); paintBars( ctx, sourceIndex, rect, maxDepth ); offset += barWidth + spaceBetweenBars; } } m_private->paintDataValueTextsAndMarkers( ctx, lpc, false ); } diff --git a/src/KChart/Cartesian/DiagramFlavors/KChartNormalPlotter_p.cpp b/src/KChart/Cartesian/DiagramFlavors/KChartNormalPlotter_p.cpp index 883e42c..a311f3d 100644 --- a/src/KChart/Cartesian/DiagramFlavors/KChartNormalPlotter_p.cpp +++ b/src/KChart/Cartesian/DiagramFlavors/KChartNormalPlotter_p.cpp @@ -1,190 +1,190 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartNormalPlotter_p.h" #include "KChartPlotter.h" #include "PaintingHelpers_p.h" #include using namespace KChart; using namespace std; NormalPlotter::NormalPlotter( Plotter* d ) : PlotterType( d ) { } Plotter::PlotType NormalPlotter::type() const { return Plotter::Normal; } const QPair< QPointF, QPointF > NormalPlotter::calculateDataBoundaries() const { if ( diagram()->useDataCompression() != Plotter::NONE ) return plotterCompressor().dataBoundaries(); else return compressor().dataBoundaries(); } void NormalPlotter::paint( PaintContext* ctx ) { reverseMapper().clear(); Q_ASSERT( dynamic_cast< CartesianCoordinatePlane* >( ctx->coordinatePlane() ) ); const CartesianCoordinatePlane* const plane = static_cast< CartesianCoordinatePlane* >( ctx->coordinatePlane() ); const int colCount = compressor().modelDataColumns(); const int rowCount = compressor().modelDataRows(); LabelPaintCache lpc; if ( diagram()->useDataCompression() != Plotter::NONE ) { for ( int dataset = 0; dataset < plotterCompressor().datasetCount(); ++dataset ) { LineAttributesInfoList lineList; PlotterDiagramCompressor::DataPoint lastPoint; for ( PlotterDiagramCompressor::Iterator it = plotterCompressor().begin( dataset ); it != plotterCompressor().end( dataset ); ++ it ) { const PlotterDiagramCompressor::DataPoint point = *it; const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); LineAttributes laCell = diagram()->lineAttributes( sourceIndex ); const LineAttributes::MissingValuesPolicy policy = laCell.missingValuesPolicy(); if ( ISNAN( point.key ) || ISNAN( point.value ) ) { switch ( policy ) { case LineAttributes::MissingValuesAreBridged: // we just bridge both values continue; case LineAttributes::MissingValuesShownAsZero: // fall-through since that attribute makes no sense for the plotter case LineAttributes::MissingValuesHideSegments: // fall-through since they're just hidden default: lastPoint = PlotterDiagramCompressor::DataPoint(); continue; } } // data area painting: a and b are prev / current data points, c and d are on the null line const QPointF b( plane->translate( QPointF( point.key, point.value ) ) ); if ( !point.hidden && PaintingHelpers::isFinite( b ) ) { const QPointF a( plane->translate( QPointF( lastPoint.key, lastPoint.value ) ) ); const QPointF c( plane->translate( QPointF( lastPoint.key, 0.0 ) ) ); const QPointF d( plane->translate( QPointF( point.key, 0.0 ) ) ); // data point label const PositionPoints pts = PositionPoints( b, a, d, c ); - m_private->addLabel( &lpc, sourceIndex, 0, pts, Position::NorthWest, + m_private->addLabel( &lpc, sourceIndex, nullptr, pts, Position::NorthWest, Position::NorthWest, point.value ); const bool lineValid = a.toPoint() != b.toPoint() && PaintingHelpers::isFinite( a ); if ( lineValid ) { // data line lineList.append( LineAttributesInfo( sourceIndex, a, b ) ); if ( laCell.displayArea() ) { // data area QList areas; QPolygonF polygon; polygon << a << b << d << c; areas << polygon; PaintingHelpers::paintAreas( m_private, ctx, attributesModel()->mapToSource( lastPoint.index ), areas, laCell.transparency() ); } } } lastPoint = point; } PaintingHelpers::paintElements( m_private, ctx, lpc, lineList ); } } else { if ( colCount == 0 || rowCount == 0 ) return; for ( int column = 0; column < colCount; ++column ) { LineAttributesInfoList lineList; CartesianDiagramDataCompressor::DataPoint lastPoint; for ( int row = 0; row < rowCount; ++row ) { const CartesianDiagramDataCompressor::CachePosition position( row, column ); const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); LineAttributes laCell = diagram()->lineAttributes( sourceIndex ); const LineAttributes::MissingValuesPolicy policy = laCell.missingValuesPolicy(); if ( ISNAN( point.key ) || ISNAN( point.value ) ) { switch ( policy ) { case LineAttributes::MissingValuesAreBridged: // we just bridge both values continue; case LineAttributes::MissingValuesShownAsZero: // fall-through since that attribute makes no sense for the plotter case LineAttributes::MissingValuesHideSegments: // fall-through since they're just hidden default: lastPoint = CartesianDiagramDataCompressor::DataPoint(); continue; } } // data area painting: a and b are prev / current data points, c and d are on the null line const QPointF b( plane->translate( QPointF( point.key, point.value ) ) ); if ( !point.hidden && PaintingHelpers::isFinite( b ) ) { const QPointF a( plane->translate( QPointF( lastPoint.key, lastPoint.value ) ) ); const QPointF c( plane->translate( QPointF( lastPoint.key, 0.0 ) ) ); const QPointF d( plane->translate( QPointF( point.key, 0.0 ) ) ); // data point label const PositionPoints pts = PositionPoints( b, a, d, c ); - m_private->addLabel( &lpc, sourceIndex, 0, pts, Position::NorthWest, + m_private->addLabel( &lpc, sourceIndex, nullptr, pts, Position::NorthWest, Position::NorthWest, point.value ); const bool lineValid = a.toPoint() != b.toPoint() && PaintingHelpers::isFinite( a ); if ( lineValid ) { // data line lineList.append( LineAttributesInfo( sourceIndex, a, b ) ); if ( laCell.displayArea() ) { // data area QList areas; QPolygonF polygon; polygon << a << b << d << c; areas << polygon; PaintingHelpers::paintAreas( m_private, ctx, attributesModel()->mapToSource( lastPoint.index ), areas, laCell.transparency() ); } } } lastPoint = point; } PaintingHelpers::paintElements( m_private, ctx, lpc, lineList ); } } } diff --git a/src/KChart/Cartesian/DiagramFlavors/KChartPercentBarDiagram_p.cpp b/src/KChart/Cartesian/DiagramFlavors/KChartPercentBarDiagram_p.cpp index ad41d60..929559f 100644 --- a/src/KChart/Cartesian/DiagramFlavors/KChartPercentBarDiagram_p.cpp +++ b/src/KChart/Cartesian/DiagramFlavors/KChartPercentBarDiagram_p.cpp @@ -1,200 +1,200 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartPercentBarDiagram_p.h" #include #include "KChartBarDiagram.h" #include "KChartTextAttributes.h" #include "KChartAttributesModel.h" #include "KChartAbstractCartesianDiagram.h" using namespace KChart; PercentBarDiagram::PercentBarDiagram( BarDiagram* d ) : BarDiagramType( d ) { } BarDiagram::BarType PercentBarDiagram::type() const { return BarDiagram::Percent; } const QPair PercentBarDiagram::calculateDataBoundaries() const { const int rowCount = diagram()->model() ? diagram()->model()->rowCount( diagram()->rootIndex() ) : 0; const int colCount = diagram()->model() ? diagram()->model()->columnCount( diagram()->rootIndex() ) : 0; const qreal xMin = 0.0; const qreal xMax = rowCount; const qreal yMin = 0.0; const qreal yMax = 100.0; qreal usedDepth = 0; for ( int row = 0; row < rowCount ; ++row ) { for ( int col = 0; col < colCount; ++col ) { const CartesianDiagramDataCompressor::CachePosition position( row, col ); const CartesianDiagramDataCompressor::DataPoint p = compressor().data( position ); QModelIndex sourceIndex = attributesModel()->mapToSource( p.index ); ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( sourceIndex ); if ( threeDAttrs.isEnabled() && threeDAttrs.depth() > usedDepth ) { usedDepth = threeDAttrs.depth(); } } } return QPair< QPointF, QPointF >( QPointF( xMin, yMin ), QPointF( xMax, yMax + usedDepth * 0.3 ) ); } void PercentBarDiagram::paint( PaintContext* ctx ) { reverseMapper().clear(); const QPair boundaries = diagram()->dataBoundaries(); // cached const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ; const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second ); const int rowCount = compressor().modelDataRows(); const int colCount = compressor().modelDataColumns(); BarAttributes ba = diagram()->barAttributes(); qreal barWidth = 0; qreal maxDepth = 0; qreal width = boundRight.x() - boundLeft.x(); qreal groupWidth = width / rowCount; qreal spaceBetweenBars = 0; qreal spaceBetweenGroups = 0; if ( ba.useFixedBarWidth() ) { barWidth = ba.fixedBarWidth(); groupWidth += barWidth; // Pending Michel set a min and max value for the groupWidth // related to the area.width if ( groupWidth < 0 ) groupWidth = 0; if ( groupWidth * rowCount > width ) groupWidth = width / rowCount; } // maxLimit: allow the space between bars to be larger until area.width() // is covered by the groups. qreal maxLimit = rowCount * (groupWidth + ((colCount-1) * ba.fixedDataValueGap()) ); //Pending Michel: FixMe if ( ba.useFixedDataValueGap() ) { if ( width > maxLimit ) spaceBetweenBars += ba.fixedDataValueGap(); else spaceBetweenBars = ((width/rowCount) - groupWidth)/(colCount-1); } if ( ba.useFixedValueBlockGap() ) spaceBetweenGroups += ba.fixedValueBlockGap(); calculateValueAndGapWidths( rowCount, colCount,groupWidth, barWidth, spaceBetweenBars, spaceBetweenGroups ); LabelPaintCache lpc; const qreal maxValue = 100; // always 100 % qreal sumValues = 0; QVector sumValuesVector; //calculate sum of values for each column and store for ( int row = 0; row < rowCount; ++row ) { for ( int col = 0; col < colCount; ++col ) { const CartesianDiagramDataCompressor::CachePosition position( row, col ); const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); //if ( point.value > 0 ) sumValues += qMax( point.value, -point.value ); if ( col == colCount - 1 ) { sumValuesVector << sumValues ; sumValues = 0; } } } // calculate stacked percent value for ( int col = 0; col < colCount; ++col ) { qreal offset = spaceBetweenGroups; if ( ba.useFixedBarWidth() ) offset -= ba.fixedBarWidth(); if ( offset < 0 ) offset = 0; for ( int row = 0; row < rowCount ; ++row ) { const CartesianDiagramDataCompressor::CachePosition position( row, col ); const CartesianDiagramDataCompressor::DataPoint p = compressor().data( position ); QModelIndex sourceIndex = attributesModel()->mapToSource( p.index ); ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( sourceIndex ); if ( threeDAttrs.isEnabled() ) { if ( barWidth > 0 ) barWidth = (width - ((offset+(threeDAttrs.depth()))*rowCount))/ rowCount; if ( barWidth <= 0 ) { barWidth = 0; maxDepth = offset - ( width/rowCount); } } else { barWidth = (width - (offset*rowCount))/ rowCount; } const qreal value = qMax( p.value, -p.value ); qreal stackedValues = 0.0; qreal key = 0.0; // calculate stacked percent value // we only take in account positives values for now. for ( int k = col; k >= 0 ; --k ) { const CartesianDiagramDataCompressor::CachePosition position( row, k ); const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); stackedValues += qMax( point.value, -point.value ); key = point.key; } QPointF point, previousPoint; if ( sumValuesVector.at( row ) != 0 && value > 0 ) { point = ctx->coordinatePlane()->translate( QPointF( key, stackedValues / sumValuesVector.at( row ) * maxValue ) ); point.rx() += offset / 2; previousPoint = ctx->coordinatePlane()->translate( QPointF( key, ( stackedValues - value)/sumValuesVector.at(row)* maxValue ) ); } const qreal barHeight = previousPoint.y() - point.y(); const QRectF rect( point, QSizeF( barWidth, barHeight ) ); - m_private->addLabel( &lpc, sourceIndex, 0, PositionPoints( rect ), Position::North, + m_private->addLabel( &lpc, sourceIndex, nullptr, PositionPoints( rect ), Position::North, Position::South, value ); paintBars( ctx, sourceIndex, rect, maxDepth ); } } m_private->paintDataValueTextsAndMarkers( ctx, lpc, false ); } diff --git a/src/KChart/Cartesian/DiagramFlavors/KChartPercentLyingBarDiagram_p.cpp b/src/KChart/Cartesian/DiagramFlavors/KChartPercentLyingBarDiagram_p.cpp index 10dca65..dccd407 100644 --- a/src/KChart/Cartesian/DiagramFlavors/KChartPercentLyingBarDiagram_p.cpp +++ b/src/KChart/Cartesian/DiagramFlavors/KChartPercentLyingBarDiagram_p.cpp @@ -1,213 +1,213 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartPercentLyingBarDiagram_p.h" #include #include "KChartBarDiagram.h" #include "KChartTextAttributes.h" #include "KChartAttributesModel.h" #include "KChartAbstractCartesianDiagram.h" using namespace KChart; PercentLyingBarDiagram::PercentLyingBarDiagram( BarDiagram* d ) : BarDiagramType( d ) { } BarDiagram::BarType PercentLyingBarDiagram::type() const { return BarDiagram::Percent; } const QPair PercentLyingBarDiagram::calculateDataBoundaries() const { //const int rowCount = compressor().modelDataRows(); //const int colCount = compressor().modelDataColumns(); const qreal xMin = 0; const qreal xMax = diagram()->model() ? diagram()->model()->rowCount( diagram()->rootIndex() ) : 0; qreal yMin = 0.0, yMax = 100.0; /*for ( int col = 0; col < colCount; ++col ) { for ( int row = 0; row < rowCount; ++row ) { // Ordinate should begin at 0 the max value being the 100% pos const QModelIndex idx = diagram()->model()->index( row, col, diagram()->rootIndex() ); // only positive values are handled qreal value = diagram()->model()->data( idx ).toReal(); if ( value > 0 ) yMax = qMax( yMax, value ); } }*/ // special cases if ( yMax == yMin ) { if ( yMin == 0.0 ) yMax = 0.1; //we need at least a range else yMax = 0.0; // they are the same but negative } const QPointF bottomLeft( QPointF( yMin, xMin ) ); const QPointF topRight( QPointF( yMax, xMax ) ); //qDebug() << "BarDiagram::calculateDataBoundaries () returns ( " << bottomLeft << topRight <<")"; return QPair< QPointF, QPointF >( bottomLeft, topRight ); } void PercentLyingBarDiagram::paint( PaintContext* ctx ) { reverseMapper().clear(); const QPair boundaries = diagram()->dataBoundaries(); // cached const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ; const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second ); const int rowCount = compressor().modelDataRows(); const int colCount = compressor().modelDataColumns(); BarAttributes ba = diagram()->barAttributes(); qreal barWidth = 0; qreal maxDepth = 0; qreal width = boundLeft.y() - boundRight.y(); qreal groupWidth = width / rowCount; qreal spaceBetweenBars = 0; qreal spaceBetweenGroups = 0; if ( ba.useFixedBarWidth() ) { barWidth = ba.fixedBarWidth(); groupWidth += barWidth; // Pending Michel set a min and max value for the groupWidth // related to the area.width if ( groupWidth < 0 ) groupWidth = 0; if ( groupWidth * rowCount > width ) groupWidth = width / rowCount; } // maxLimit: allow the space between bars to be larger until area.width() // is covered by the groups. qreal maxLimit = rowCount * (groupWidth + ((colCount-1) * ba.fixedDataValueGap()) ); //Pending Michel: FixMe if ( ba.useFixedDataValueGap() ) { if ( width > maxLimit ) spaceBetweenBars += ba.fixedDataValueGap(); else spaceBetweenBars = ((ctx->rectangle().width()/rowCount) - groupWidth)/(colCount-1); } if ( ba.useFixedValueBlockGap() ) spaceBetweenGroups += ba.fixedValueBlockGap(); calculateValueAndGapWidths( rowCount, colCount,groupWidth, barWidth, spaceBetweenBars, spaceBetweenGroups ); LabelPaintCache lpc; const qreal maxValue = 100.0; // always 100 % qreal sumValues = 0; QVector sumValuesVector; //calculate sum of values for each column and store for ( int row = 0; row < rowCount; ++row ) { for ( int col = 0; col < colCount; ++col ) { const CartesianDiagramDataCompressor::CachePosition position( row, col ); const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); //if ( point.value > 0 ) sumValues += qMax( point.value, -point.value ); if ( col == colCount - 1 ) { sumValuesVector << sumValues ; sumValues = 0; } } } // calculate stacked percent value for ( int curRow = rowCount - 1; curRow >= 0; --curRow ) { qreal offset = spaceBetweenGroups; if ( ba.useFixedBarWidth() ) offset -= ba.fixedBarWidth(); if ( offset < 0 ) offset = 0; for ( int col = 0; col < colCount ; ++col ) { qreal threeDOffset = 0.0; const CartesianDiagramDataCompressor::CachePosition position( curRow, col ); const CartesianDiagramDataCompressor::DataPoint p = compressor().data( position ); QModelIndex sourceIndex = attributesModel()->mapToSource( p.index ); ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( sourceIndex ); if ( threeDAttrs.isEnabled() ) { if ( barWidth > 0 ) { barWidth = (width - ((offset+(threeDAttrs.depth()))*rowCount))/ rowCount; threeDOffset = threeDAttrs.depth(); } if ( barWidth <= 0 ) { barWidth = 0.1; threeDOffset = (width - (offset*rowCount))/ rowCount; } } else { barWidth = (width - (offset*rowCount))/ rowCount; } const qreal value = qMax( p.value, -p.value ); qreal stackedValues = 0.0; qreal key = 0.0; // calculate stacked percent value // we only take in account positives values for now. for ( int k = col; k >= 0 ; --k ) { const CartesianDiagramDataCompressor::CachePosition position( curRow, k ); const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); stackedValues += qMax( point.value, -point.value ); key = point.key; } QPointF point, previousPoint; if ( sumValuesVector.at( curRow ) != 0 && value > 0 ) { QPointF dataPoint( ( stackedValues / sumValuesVector.at( curRow ) * maxValue ), key + 1 ); point = ctx->coordinatePlane()->translate( dataPoint ); point.ry() += offset / 2 + threeDOffset; previousPoint = ctx->coordinatePlane()->translate( QPointF( ( ( stackedValues - value) / sumValuesVector.at( curRow ) * maxValue ), key + 1 ) ); } const qreal barHeight = point.x() - previousPoint.x(); point.setX( point.x() - barHeight ); const QRectF rect = QRectF( point, QSizeF( barHeight, barWidth ) ).translated( 1, 0 ); - m_private->addLabel( &lpc, sourceIndex, 0, PositionPoints( rect ), Position::North, + m_private->addLabel( &lpc, sourceIndex, nullptr, PositionPoints( rect ), Position::North, Position::South, value ); paintBars( ctx, sourceIndex, rect, maxDepth ); } } m_private->paintDataValueTextsAndMarkers( ctx, lpc, false ); } diff --git a/src/KChart/Cartesian/DiagramFlavors/KChartPercentPlotter_p.cpp b/src/KChart/Cartesian/DiagramFlavors/KChartPercentPlotter_p.cpp index ae6eb4c..11440de 100644 --- a/src/KChart/Cartesian/DiagramFlavors/KChartPercentPlotter_p.cpp +++ b/src/KChart/Cartesian/DiagramFlavors/KChartPercentPlotter_p.cpp @@ -1,264 +1,264 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartPercentPlotter_p.h" #include "KChartPlotter.h" #include "PaintingHelpers_p.h" #include using namespace KChart; using namespace std; PercentPlotter::PercentPlotter( Plotter* d ) : PlotterType( d ) { } Plotter::PlotType PercentPlotter::type() const { return Plotter::Percent; } const QPair< QPointF, QPointF > PercentPlotter::calculateDataBoundaries() const { const int rowCount = compressor().modelDataRows(); const int colCount = compressor().modelDataColumns(); qreal xMin = std::numeric_limits< qreal >::quiet_NaN(); qreal xMax = std::numeric_limits< qreal >::quiet_NaN(); const qreal yMin = 0.0; const qreal yMax = 100.0; for ( int column = 0; column < colCount; ++column ) { for ( int row = 0; row < rowCount; ++row ) { const CartesianDiagramDataCompressor::CachePosition position( row, column ); const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); const qreal valueX = ISNAN( point.key ) ? 0.0 : point.key; if ( ISNAN( xMin ) ) { xMin = valueX; xMax = valueX; } else { xMin = qMin( xMin, valueX ); xMax = qMax( xMax, valueX ); } } } const QPointF bottomLeft( QPointF( xMin, yMin ) ); const QPointF topRight( QPointF( xMax, yMax ) ); return QPair< QPointF, QPointF >( bottomLeft, topRight ); } class Value { public: Value() : value( std::numeric_limits< qreal >::quiet_NaN() ) { } // allow implicit conversion Value( qreal value ) : value( value ) { } operator qreal() const { return value; } private: qreal value; }; void PercentPlotter::paint( PaintContext* ctx ) { reverseMapper().clear(); Q_ASSERT( dynamic_cast< CartesianCoordinatePlane* >( ctx->coordinatePlane() ) ); const CartesianCoordinatePlane* const plane = static_cast< CartesianCoordinatePlane* >( ctx->coordinatePlane() ); const int colCount = compressor().modelDataColumns(); const int rowCount = compressor().modelDataRows(); if ( colCount == 0 || rowCount == 0 ) return; LabelPaintCache lpc; // this map contains the y-values to each x-value QMap< qreal, QVector< QPair< Value, QModelIndex > > > diagramValues; for ( int col = 0; col < colCount; ++col ) { for ( int row = 0; row < rowCount; ++row ) { const CartesianDiagramDataCompressor::CachePosition position( row, col ); const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); diagramValues[ point.key ].resize( colCount ); diagramValues[ point.key ][ col ].first = point.value; diagramValues[ point.key ][ col ].second = point.index; } } // the sums of the y-values per x-value QMap< qreal, qreal > yValueSums; // the x-values QList< qreal > xValues = diagramValues.keys(); // make sure it's sorted qSort( xValues ); Q_FOREACH( const qreal xValue, xValues ) { // the y-values to the current x-value QVector< QPair< Value, QModelIndex > >& yValues = diagramValues[ xValue ]; Q_ASSERT( yValues.count() == colCount ); for ( int column = 0; column < colCount; ++column ) { QPair< Value, QModelIndex >& data = yValues[ column ]; // if the index is invalid, there was no value. Let's interpolate. if ( !data.second.isValid() ) { QPair< QPair< qreal, Value >, QModelIndex > left; QPair< QPair< qreal, Value >, QModelIndex > right; int xIndex = 0; // let's find the next lower value for ( xIndex = xValues.indexOf( xValue ); xIndex >= 0; --xIndex ) { if ( diagramValues[ xValues[ xIndex ] ][ column ].second.isValid() ) { left.first.first = xValues[ xIndex ]; left.first.second = diagramValues[ left.first.first ][ column ].first; left.second = diagramValues[ xValues[ xIndex ] ][ column ].second; break; } } // let's find the next higher value for ( xIndex = xValues.indexOf( xValue ); xIndex < xValues.count(); ++xIndex ) { if ( diagramValues[ xValues[ xIndex ] ][ column ].second.isValid() ) { right.first.first = xValues[ xIndex ]; right.first.second = diagramValues[ right.first.first ][ column ].first; right.second = diagramValues[ xValues[ xIndex ] ][ column ].second; break; } } // interpolate out of them (left and/or right might be invalid, but this doesn't matter here) const qreal leftX = left.first.first; const qreal rightX = right.first.first; const qreal leftY = left.first.second; const qreal rightY = right.first.second; data.first = leftY + ( rightY - leftY ) * ( xValue - leftX ) / ( rightX - leftX ); // if the result is a valid value, let's assign the index, too if ( !ISNAN( data.first.operator qreal() ) ) data.second = left.second; } // sum it up if ( !ISNAN( yValues[ column ].first.operator qreal() ) ) yValueSums[ xValue ] += yValues[ column ].first; } } for ( int column = 0; column < colCount; ++column ) { LineAttributesInfoList lineList; LineAttributes laPreviousCell; CartesianDiagramDataCompressor::CachePosition previousCellPosition; CartesianDiagramDataCompressor::DataPoint lastPoint; qreal lastExtraY = 0.0; qreal lastValue = 0.0; QMapIterator< qreal, QVector< QPair< Value, QModelIndex > > > i( diagramValues ); while ( i.hasNext() ) { i.next(); CartesianDiagramDataCompressor::DataPoint point; point.key = i.key(); const QPair< Value, QModelIndex >& data = i.value().at( column ); point.value = data.first; point.index = data.second; if ( ISNAN( point.key ) || ISNAN( point.value ) ) { previousCellPosition = CartesianDiagramDataCompressor::CachePosition(); continue; } qreal extraY = 0.0; for ( int col = column - 1; col >= 0; --col ) { const qreal y = i.value().at( col ).first; if ( !ISNAN( y ) ) extraY += y; } LineAttributes laCell; const qreal value = ( point.value + extraY ) / yValueSums[ i.key() ] * 100; const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index ); // area corners, a + b are the line ends: const QPointF a( plane->translate( QPointF( lastPoint.key, lastValue ) ) ); const QPointF b( plane->translate( QPointF( point.key, value ) ) ); const QPointF c( plane->translate( QPointF( lastPoint.key, lastExtraY / yValueSums[ i.key() ] * 100 ) ) ); const QPointF d( plane->translate( QPointF( point.key, extraY / yValueSums[ i.key() ] * 100 ) ) ); // add the line to the list: laCell = diagram()->lineAttributes( sourceIndex ); // add data point labels: const PositionPoints pts = PositionPoints( b, a, d, c ); // if necessary, add the area to the area list: QList areas; if ( laCell.displayArea() ) { QPolygonF polygon; polygon << a << b << d << c; areas << polygon; } // add the pieces to painting if this is not hidden: if ( !point.hidden /*&& !ISNAN( lastPoint.key ) && !ISNAN( lastPoint.value ) */) { - m_private->addLabel( &lpc, sourceIndex, 0, pts, Position::NorthWest, + m_private->addLabel( &lpc, sourceIndex, nullptr, pts, Position::NorthWest, Position::NorthWest, value ); if ( !ISNAN( lastPoint.key ) && !ISNAN( lastPoint.value ) ) { PaintingHelpers::paintAreas( m_private, ctx, attributesModel()->mapToSource( lastPoint.index ), areas, laCell.transparency() ); lineList.append( LineAttributesInfo( sourceIndex, a, b ) ); } } // wrap it up: laPreviousCell = laCell; lastPoint = point; lastExtraY = extraY; lastValue = value; } PaintingHelpers::paintElements( m_private, ctx, lpc, lineList ); } } diff --git a/src/KChart/Cartesian/DiagramFlavors/KChartStackedBarDiagram_p.cpp b/src/KChart/Cartesian/DiagramFlavors/KChartStackedBarDiagram_p.cpp index 86f3dd9..c6787f2 100644 --- a/src/KChart/Cartesian/DiagramFlavors/KChartStackedBarDiagram_p.cpp +++ b/src/KChart/Cartesian/DiagramFlavors/KChartStackedBarDiagram_p.cpp @@ -1,211 +1,211 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartStackedBarDiagram_p.h" #include #include "KChartBarDiagram.h" #include "KChartTextAttributes.h" #include "KChartAttributesModel.h" #include "KChartAbstractCartesianDiagram.h" using namespace KChart; StackedBarDiagram::StackedBarDiagram( BarDiagram* d ) : BarDiagramType( d ) { } BarDiagram::BarType StackedBarDiagram::type() const { return BarDiagram::Stacked; } const QPair StackedBarDiagram::calculateDataBoundaries() const { const int rowCount = compressor().modelDataRows(); const int colCount = compressor().modelDataColumns(); const qreal xMin = 0.0; const qreal xMax = rowCount; qreal yMin = 0.0; qreal yMax = 0.0; bool isFirst = true; for ( int row = 0; row < rowCount; ++row ) { // calculate sum of values per column - Find out stacked Min/Max qreal stackedValues = 0.0; qreal negativeStackedValues = 0.0; for ( int col = 0; col < colCount; ++col ) { const CartesianDiagramDataCompressor::CachePosition position( row, col ); const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); const double value = ISNAN( point.value ) ? 0.0 : point.value; if ( value > 0.0 ) { stackedValues += value; } else { negativeStackedValues += value; } // this is always true yMin can be 0 in case all values // are the same // same for yMax it can be zero if all values are negative if ( isFirst ) { yMin = negativeStackedValues < 0.0 ? negativeStackedValues : stackedValues; yMax = stackedValues > 0.0 ? stackedValues : negativeStackedValues; isFirst = false; } else { yMin = qMin( qMin( yMin, stackedValues ), negativeStackedValues ); yMax = qMax( qMax( yMax, stackedValues ), negativeStackedValues ); } } } // special cases if ( yMax == yMin ) { if ( yMin == 0.0 ) { yMax = 0.1; // we need at least a range } else if ( yMax < 0.0 ) { yMax = 0.0; // extend the range to zero } else if ( yMin > 0.0 ) { yMin = 0.0; // dito } } return QPair< QPointF, QPointF >( QPointF( xMin, yMin ), QPointF( xMax, yMax ) ); } void StackedBarDiagram::paint( PaintContext* ctx ) { reverseMapper().clear(); const QPair boundaries = diagram()->dataBoundaries(); // cached const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ; const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second ); const int rowCount = compressor().modelDataRows(); const int colCount = compressor().modelDataColumns(); BarAttributes ba = diagram()->barAttributes(); qreal barWidth = 0; qreal maxDepth = 0; qreal width = boundRight.x() - boundLeft.x(); qreal groupWidth = width / rowCount; qreal spaceBetweenBars = 0; qreal spaceBetweenGroups = 0; if ( ba.useFixedBarWidth() ) { barWidth = ba.fixedBarWidth(); groupWidth += barWidth; // Pending Michel set a min and max value for the groupWidth // related to the area.width if ( groupWidth < 0 ) groupWidth = 0; if ( groupWidth * rowCount > width ) groupWidth = width / rowCount; } // maxLimit: allow the space between bars to be larger until area.width() // is covered by the groups. qreal maxLimit = rowCount * (groupWidth + ((colCount-1) * ba.fixedDataValueGap()) ); //Pending Michel: FixMe if ( ba.useFixedDataValueGap() ) { if ( width > maxLimit ) spaceBetweenBars += ba.fixedDataValueGap(); else spaceBetweenBars = ((width/rowCount) - groupWidth)/(colCount-1); } if ( ba.useFixedValueBlockGap() ) spaceBetweenGroups += ba.fixedValueBlockGap(); calculateValueAndGapWidths( rowCount, colCount,groupWidth, barWidth, spaceBetweenBars, spaceBetweenGroups ); LabelPaintCache lpc; for ( int col = 0; col < colCount; ++col ) { qreal offset = spaceBetweenGroups; if ( ba.useFixedBarWidth() ) offset -= ba.fixedBarWidth(); if ( offset < 0 ) offset = 0; for ( int row = 0; row < rowCount; ++row ) { const CartesianDiagramDataCompressor::CachePosition position( row, col ); const CartesianDiagramDataCompressor::DataPoint p = compressor().data( position ); const QModelIndex index = attributesModel()->mapToSource( p.index ); ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( index ); const qreal value = p.value; qreal stackedValues = 0.0; qreal key = 0.0; if ( threeDAttrs.isEnabled() ) { if ( barWidth > 0 ) barWidth = (width - ((offset+(threeDAttrs.depth()))*rowCount))/ rowCount; if ( barWidth <= 0 ) { barWidth = 0; maxDepth = offset - (width/rowCount); } } else { barWidth = (width - (offset*rowCount))/ rowCount ; } for ( int k = col; k >= 0; --k ) { const CartesianDiagramDataCompressor::CachePosition position( row, k ); const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); if ( !ISNAN( point.value ) && (( p.value >= 0.0 && point.value >= 0.0 ) || ( p.value < 0.0 && point.value < 0.0 )) ) stackedValues += point.value; key = point.key; } if (!ISNAN( value )) { const qreal usedDepth = threeDAttrs.depth(); QPointF point = ctx->coordinatePlane()->translate( QPointF( key, stackedValues ) ); const qreal dy = point.y() - usedDepth; if ( dy < 0 ) { threeDAttrs.setDepth( point.y() - 1 ); diagram()->setThreeDBarAttributes( threeDAttrs ); } point.rx() += offset / 2; const QPointF previousPoint = ctx->coordinatePlane()->translate( QPointF( key, stackedValues - value ) ); const qreal barHeight = previousPoint.y() - point.y(); const QRectF rect( point, QSizeF( barWidth , barHeight ) ); - m_private->addLabel( &lpc, index, 0, PositionPoints( rect ), Position::North, + m_private->addLabel( &lpc, index, nullptr, PositionPoints( rect ), Position::North, Position::South, value ); paintBars( ctx, index, rect, maxDepth ); } } } m_private->paintDataValueTextsAndMarkers( ctx, lpc, false ); } diff --git a/src/KChart/Cartesian/DiagramFlavors/KChartStackedLyingBarDiagram_p.cpp b/src/KChart/Cartesian/DiagramFlavors/KChartStackedLyingBarDiagram_p.cpp index 495c6e9..f4803eb 100644 --- a/src/KChart/Cartesian/DiagramFlavors/KChartStackedLyingBarDiagram_p.cpp +++ b/src/KChart/Cartesian/DiagramFlavors/KChartStackedLyingBarDiagram_p.cpp @@ -1,200 +1,200 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartStackedLyingBarDiagram_p.h" #include #include "KChartBarDiagram.h" #include "KChartTextAttributes.h" #include "KChartAttributesModel.h" #include "KChartAbstractCartesianDiagram.h" using namespace KChart; StackedLyingBarDiagram::StackedLyingBarDiagram( BarDiagram* d ) : BarDiagramType( d ) { } BarDiagram::BarType StackedLyingBarDiagram::type() const { return BarDiagram::Stacked; } const QPair StackedLyingBarDiagram::calculateDataBoundaries() const { const int rowCount = compressor().modelDataRows(); const int colCount = compressor().modelDataColumns(); const qreal xMin = 0; const qreal xMax = rowCount; qreal yMin = 0; qreal yMax = 0; bool isFirst = true; for ( int row = 0; row < rowCount; ++row ) { // calculate sum of values per column - Find out stacked Min/Max qreal stackedValues = 0.0; qreal negativeStackedValues = 0.0; for ( int col = 0; col < colCount; ++col ) { const CartesianDiagramDataCompressor::CachePosition position( row, col ); const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); if ( point.value > 0.0 ) stackedValues += point.value; else negativeStackedValues += point.value; // this is always true yMin can be 0 in case all values // are the same // same for yMax it can be zero if all values are negative if ( isFirst ) { yMin = negativeStackedValues < 0.0 ? negativeStackedValues : stackedValues; yMax = stackedValues > 0.0 ? stackedValues : negativeStackedValues; isFirst = false; } else { yMin = qMin( qMin( yMin, stackedValues ), negativeStackedValues ); yMax = qMax( qMax( yMax, stackedValues ), negativeStackedValues ); } } } // special cases if ( yMax == yMin ) { if ( yMin == 0.0 ) { yMax = 0.1; // we need at least a range } else if ( yMax < 0.0 ) { yMax = 0.0; // extend the range to zero } else if ( yMin > 0.0 ) { yMin = 0.0; // dito } } return QPair< QPointF, QPointF >( QPointF( yMin, xMin ), QPointF( yMax, xMax ) ); } void StackedLyingBarDiagram::paint( PaintContext* ctx ) { reverseMapper().clear(); const QPair boundaries = diagram()->dataBoundaries(); // cached const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ; const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second ); const int rowCount = compressor().modelDataRows(); const int colCount = compressor().modelDataColumns(); BarAttributes ba = diagram()->barAttributes(); qreal barWidth = 0; qreal maxDepth = 0; qreal width = boundLeft.y() - boundRight.y(); qreal groupWidth = width / rowCount; qreal spaceBetweenBars = 0; qreal spaceBetweenGroups = 0; if ( ba.useFixedBarWidth() ) { barWidth = ba.fixedBarWidth(); groupWidth += barWidth; // Pending Michel set a min and max value for the groupWidth // related to the area.width if ( groupWidth < 0 ) groupWidth = 0; if ( groupWidth * rowCount > width ) groupWidth = width / rowCount; } // maxLimit: allow the space between bars to be larger until area.width() // is covered by the groups. qreal maxLimit = rowCount * (groupWidth + ((colCount-1) * ba.fixedDataValueGap()) ); //Pending Michel: FixMe if ( ba.useFixedDataValueGap() ) { if ( ctx->rectangle().width() > maxLimit ) spaceBetweenBars += ba.fixedDataValueGap(); else spaceBetweenBars = ((width/rowCount) - groupWidth)/(colCount-1); } if ( ba.useFixedValueBlockGap() ) spaceBetweenGroups += ba.fixedValueBlockGap(); calculateValueAndGapWidths( rowCount, colCount,groupWidth, barWidth, spaceBetweenBars, spaceBetweenGroups ); LabelPaintCache lpc; for ( int row = rowCount - 1; row >= 0; --row ) { qreal offset = spaceBetweenGroups; if ( ba.useFixedBarWidth() ) offset -= ba.fixedBarWidth(); if ( offset < 0 ) offset = 0; for ( int col = 0; col < colCount; ++col ) { qreal threeDOffset = 0.0; const CartesianDiagramDataCompressor::CachePosition position( row, col ); const CartesianDiagramDataCompressor::DataPoint p = compressor().data( position ); const QModelIndex index = attributesModel()->mapToSource( p.index ); ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( index ); const qreal value = p.value; qreal stackedValues = 0.0; qreal key = 0.0; if ( threeDAttrs.isEnabled() ) { if ( barWidth > 0 ) { barWidth = (width - ((offset+(threeDAttrs.depth()))*rowCount))/ rowCount; threeDOffset = threeDAttrs.depth(); } if ( barWidth <= 0 ) { barWidth = 0.1; threeDOffset = (width - (offset*rowCount))/ rowCount; } } else { barWidth = (width - (offset*rowCount))/ rowCount; } for ( int k = col; k >= 0; --k ) { const CartesianDiagramDataCompressor::CachePosition position( row, k ); const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); if ( ( p.value >= 0.0 && point.value >= 0.0 ) || ( p.value < 0.0 && point.value < 0.0 ) ) stackedValues += point.value; key = point.key; } QPointF point = ctx->coordinatePlane()->translate( QPointF( stackedValues, key + 1 ) ); point.ry() += offset / 2 + threeDOffset; const QPointF previousPoint = ctx->coordinatePlane()->translate( QPointF( stackedValues - value, key + 1 ) ); const qreal barHeight = point.x() - previousPoint.x(); point.rx() -= barHeight; const QRectF rect = QRectF( point, QSizeF( barHeight , barWidth ) ).translated( 1, 0 ); - m_private->addLabel( &lpc, index, 0, PositionPoints( rect ), Position::North, + m_private->addLabel( &lpc, index, nullptr, PositionPoints( rect ), Position::North, Position::South, value ); paintBars( ctx, index, rect, maxDepth ); } } m_private->paintDataValueTextsAndMarkers( ctx, lpc, false ); } diff --git a/src/KChart/Cartesian/KChartAbstractCartesianDiagram.cpp b/src/KChart/Cartesian/KChartAbstractCartesianDiagram.cpp index 32b959e..7f19e68 100644 --- a/src/KChart/Cartesian/KChartAbstractCartesianDiagram.cpp +++ b/src/KChart/Cartesian/KChartAbstractCartesianDiagram.cpp @@ -1,196 +1,196 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartAbstractCartesianDiagram.h" #include "KChartAbstractCartesianDiagram_p.h" #include "KChartMath_p.h" using namespace KChart; AbstractCartesianDiagram::Private::Private() - : referenceDiagram( 0 ) + : referenceDiagram( nullptr ) { } AbstractCartesianDiagram::Private::~Private() { } bool AbstractCartesianDiagram::compare( const AbstractCartesianDiagram* other ) const { if ( other == this ) return true; if ( ! other ) { return false; } return // compare the base class ( static_cast(this)->compare( other ) ) && // compare own properties (referenceDiagram() == other->referenceDiagram()) && ((! referenceDiagram()) || (referenceDiagramOffset() == other->referenceDiagramOffset())); } #define d d_func() AbstractCartesianDiagram::AbstractCartesianDiagram ( QWidget* parent, CartesianCoordinatePlane* plane ) : AbstractDiagram ( new Private(), parent, plane ) { init(); } KChart::AbstractCartesianDiagram::~AbstractCartesianDiagram() { Q_FOREACH( CartesianAxis* axis, d->axesList ) { axis->deleteObserver( this ); } d->axesList.clear(); } void AbstractCartesianDiagram::init() { d->compressor.setModel( attributesModel() ); connect( this, SIGNAL(layoutChanged(AbstractDiagram*)), &d->compressor, SLOT(slotDiagramLayoutChanged(AbstractDiagram*)) ); connect( this, SIGNAL(attributesModelAboutToChange(AttributesModel*,AttributesModel*)), this, SLOT(connectAttributesModel(AttributesModel*)) ); if ( d->plane ) { connect( d->plane, SIGNAL(viewportCoordinateSystemChanged()), this, SIGNAL(viewportCoordinateSystemChanged()) ); } } void AbstractCartesianDiagram::addAxis( CartesianAxis *axis ) { if ( !d->axesList.contains( axis ) ) { d->axesList.append( axis ); axis->createObserver( this ); layoutPlanes(); } } void AbstractCartesianDiagram::takeAxis( CartesianAxis *axis ) { const int idx = d->axesList.indexOf( axis ); if ( idx != -1 ) d->axesList.takeAt( idx ); axis->deleteObserver( this ); - axis->setParentWidget( 0 ); + axis->setParentWidget( nullptr ); layoutPlanes(); } KChart::CartesianAxisList AbstractCartesianDiagram::axes( ) const { return d->axesList; } void KChart::AbstractCartesianDiagram::layoutPlanes() { AbstractCoordinatePlane* plane = coordinatePlane(); if ( plane ) { plane->layoutPlanes(); } } void KChart::AbstractCartesianDiagram::setCoordinatePlane( AbstractCoordinatePlane* plane ) { if ( coordinatePlane() ) { disconnect( attributesModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)), coordinatePlane(), SLOT(relayout()) ); disconnect( attributesModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), coordinatePlane(), SLOT(relayout()) ); disconnect( attributesModel(), SIGNAL(columnsRemoved(QModelIndex,int,int)), coordinatePlane(), SLOT(relayout()) ); disconnect( attributesModel(), SIGNAL(columnsInserted(QModelIndex,int,int)), coordinatePlane(), SLOT(relayout()) ); disconnect( coordinatePlane() ); } AbstractDiagram::setCoordinatePlane(plane); if ( plane ) { // Readjust the layout when the dataset count changes connect( attributesModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)), plane, SLOT(relayout()) ); connect( attributesModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), plane, SLOT(relayout()) ); connect( attributesModel(), SIGNAL(columnsRemoved(QModelIndex,int,int)), plane, SLOT(relayout()) ); connect( attributesModel(), SIGNAL(columnsInserted(QModelIndex,int,int)), plane, SLOT(relayout()) ); connect( plane, SIGNAL(viewportCoordinateSystemChanged()), this, SIGNAL(viewportCoordinateSystemChanged()) ); connect( plane, SIGNAL(viewportCoordinateSystemChanged()), this, SLOT(update()) ); } } void AbstractCartesianDiagram::setReferenceDiagram( AbstractCartesianDiagram* diagram, const QPointF& offset ) { d->referenceDiagram = diagram; d->referenceDiagramOffset = offset; } AbstractCartesianDiagram* AbstractCartesianDiagram::referenceDiagram() const { return d->referenceDiagram; } QPointF AbstractCartesianDiagram::referenceDiagramOffset() const { return d->referenceDiagramOffset; } void AbstractCartesianDiagram::setRootIndex( const QModelIndex& index ) { d->compressor.setRootIndex( attributesModel()->mapFromSource( index ) ); AbstractDiagram::setRootIndex( index ); } void AbstractCartesianDiagram::setModel( QAbstractItemModel* m ) { if ( m == model() ) { return; } AbstractDiagram::setModel( m ); } void AbstractCartesianDiagram::setAttributesModel( AttributesModel* model ) { if ( model == attributesModel() ) { return; } AbstractDiagram::setAttributesModel( model ); } void AbstractCartesianDiagram::connectAttributesModel( AttributesModel* newModel ) { // The compressor must receive model signals before the diagram because the diagram will ask the // compressor for data upon receiving dataChanged() et al. from the model, at which point the // compressor must be up to date already. // Starting from Qt 4.6, the invocation order of slots is guaranteed to be equal to connection // order (and probably was before). // This is our opportunity to connect to the AttributesModel before the AbstractDiagram does. // ### A better design would be to properly recognize that the compressor is the real data interface // for Cartesian diagrams and make diagrams listen to updates from the *compressor*, not the model. // However, this would change the outside interface of AbstractCartesianDiagram which would be bad. // So we're stuck with the complication of this slot and the corresponding signal. d->compressor.setModel( newModel ); } diff --git a/src/KChart/Cartesian/KChartAbstractCartesianDiagram.h b/src/KChart/Cartesian/KChartAbstractCartesianDiagram.h index 8e17f0a..d4b5f24 100644 --- a/src/KChart/Cartesian/KChartAbstractCartesianDiagram.h +++ b/src/KChart/Cartesian/KChartAbstractCartesianDiagram.h @@ -1,129 +1,129 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTABSTRACTCARTESIANDIAGRAM_H #define KCHARTABSTRACTCARTESIANDIAGRAM_H #include "KChartCartesianCoordinatePlane.h" #include "KChartAbstractDiagram.h" #include "KChartCartesianAxis.h" namespace KChart { class GridAttributes; /** * @brief Base class for diagrams based on a cartesian coordianate system. * * The AbstractCartesianDiagram interface adds those elements that are * specific to diagrams based on a cartesian coordinate system to the * basic AbstractDiagram interface. */ class KCHART_EXPORT AbstractCartesianDiagram : public AbstractDiagram { Q_OBJECT Q_DISABLE_COPY( AbstractCartesianDiagram ) KCHART_DECLARE_DERIVED_DIAGRAM( AbstractCartesianDiagram, CartesianCoordinatePlane ) public: - explicit AbstractCartesianDiagram( QWidget* parent = 0, CartesianCoordinatePlane* plane = 0 ); + explicit AbstractCartesianDiagram( QWidget* parent = nullptr, CartesianCoordinatePlane* plane = nullptr ); virtual ~AbstractCartesianDiagram(); /** * Returns true if both diagrams have the same settings. */ bool compare( const AbstractCartesianDiagram* other ) const; #if defined(Q_COMPILER_MANGLES_RETURN_TYPE) virtual const int numberOfAbscissaSegments() const = 0; virtual const int numberOfOrdinateSegments() const = 0; #else virtual int numberOfAbscissaSegments() const = 0; virtual int numberOfOrdinateSegments() const = 0; #endif /** * Add the axis to the diagram. The diagram takes ownership of the axis * and will delete it. * * To gain back ownership (e.g. for assigning the axis to another diagram) * use the takeAxis method, before calling addAxis on the other diagram. * * \sa takeAxis */ virtual void addAxis( CartesianAxis * axis ); /** * Removes the axis from the diagram, without deleting it. * * The diagram no longer owns the axis, so it is * the caller's responsibility to delete the axis. * * \sa addAxis */ virtual void takeAxis( CartesianAxis * axis ); /** * @return a list of all axes added to the diagram */ virtual KChart::CartesianAxisList axes() const; /** * Triggers layouting of all coordinate planes on the current chart. * Normally you don't need to call this method. It's handled automatically for you. */ virtual void layoutPlanes(); /** \reimpl */ void setCoordinatePlane( AbstractCoordinatePlane* plane ) Q_DECL_OVERRIDE; /** * Makes this diagram use another diagram \a diagram as reference diagram with relative offset * \a offset. * To share cartesian axes between different diagrams there might be cases when you need that. * Normally you don't. * \sa examples/SharedAbscissa */ virtual void setReferenceDiagram( AbstractCartesianDiagram* diagram, const QPointF& offset = QPointF() ); /** * @return this diagram's reference diagram * \sa setReferenceDiagram */ virtual AbstractCartesianDiagram* referenceDiagram() const; /** * @return the relative offset of this diagram's reference diagram * \sa setReferenceDiagram */ virtual QPointF referenceDiagramOffset() const; /* reimpl */ void setModel( QAbstractItemModel* model ) Q_DECL_OVERRIDE; /* reimpl */ void setRootIndex( const QModelIndex& index ) Q_DECL_OVERRIDE; /* reimpl */ void setAttributesModel( AttributesModel* model ) Q_DECL_OVERRIDE; protected Q_SLOTS: void connectAttributesModel( AttributesModel* ); protected: /** @return the 3D item depth of the model index \a index */ virtual qreal threeDItemDepth( const QModelIndex& index ) const = 0; /** @return the 3D item depth of the data set \a column */ virtual qreal threeDItemDepth( int column ) const = 0; }; } #endif diff --git a/src/KChart/Cartesian/KChartAbstractCartesianDiagram_p.h b/src/KChart/Cartesian/KChartAbstractCartesianDiagram_p.h index 2ce5e01..3c46338 100644 --- a/src/KChart/Cartesian/KChartAbstractCartesianDiagram_p.h +++ b/src/KChart/Cartesian/KChartAbstractCartesianDiagram_p.h @@ -1,89 +1,89 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTABSTRACTCARTESIANDIAGRAM_P_H #define KCHARTABSTRACTCARTESIANDIAGRAM_P_H // // W A R N I N G // ------------- // // This file is not part of the KD Chart API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "KChartAbstractCartesianDiagram.h" #include #include #include #include "KChartMath_p.h" namespace KChart { class CartesianCoordinatePlane; class AbstractCartesianDiagram; /** * \internal */ class Q_DECL_HIDDEN AbstractCartesianDiagram::Private : public AbstractDiagram::Private { friend class AbstractCartesianDiagram; public: Private(); virtual ~Private(); Private( const Private& rhs ) : AbstractDiagram::Private( rhs ), // Do not copy axes and reference diagrams. axesList(), - referenceDiagram( 0 ), + referenceDiagram( nullptr ), referenceDiagramOffset() { } /** \reimpl */ CartesianDiagramDataCompressor::AggregatedDataValueAttributes aggregatedAttrs( const QModelIndex & index, const CartesianDiagramDataCompressor::CachePosition * position ) const Q_DECL_OVERRIDE { if ( position ) return compressor.aggregatedAttrs( diagram, index, *position ); CartesianDiagramDataCompressor::AggregatedDataValueAttributes allAttrs; allAttrs[index] = diagram->dataValueAttributes( index ); return allAttrs; } CartesianAxisList axesList; AbstractCartesianDiagram* referenceDiagram; QPointF referenceDiagramOffset; mutable CartesianDiagramDataCompressor compressor; }; KCHART_IMPL_DERIVED_DIAGRAM( AbstractCartesianDiagram, AbstractDiagram, CartesianCoordinatePlane ) } #endif /* KCHARTABSTRACTCARTESIANDIAGRAM_P_H */ diff --git a/src/KChart/Cartesian/KChartBarAttributes.cpp b/src/KChart/Cartesian/KChartBarAttributes.cpp index 6d088cb..17bbb4f 100644 --- a/src/KChart/Cartesian/KChartBarAttributes.cpp +++ b/src/KChart/Cartesian/KChartBarAttributes.cpp @@ -1,197 +1,197 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartBarAttributes.h" #include "KChartMath_p.h" #include #define d d_func() using namespace KChart; class Q_DECL_HIDDEN BarAttributes::Private { friend class BarAttributes; public: Private(); private: qreal datasetGap; bool useFixedDatasetGap; qreal valueBlockGap; bool useFixedValueBlockGap; qreal barWidth; bool useFixedBarWidth; bool drawSolidExcessArrows; qreal groupGapFactor; qreal barGapFactor; }; BarAttributes::Private::Private() :datasetGap( 6 ), useFixedDatasetGap( false ), valueBlockGap( 24 ), useFixedValueBlockGap( false ), barWidth( -1 ), useFixedBarWidth( false ), drawSolidExcessArrows( false ), groupGapFactor( 2.0 ), barGapFactor( 0.4 ) { } BarAttributes::BarAttributes() : _d( new Private() ) { } BarAttributes::BarAttributes( const BarAttributes& r ) : _d( new Private( *r.d ) ) { } BarAttributes& BarAttributes::operator= ( const BarAttributes& r ) { if ( this == &r ) return *this; *d = *r.d; return *this; } BarAttributes::~BarAttributes() { - delete _d; _d = 0; + delete _d; _d = nullptr; } bool BarAttributes::operator==( const BarAttributes& r ) const { if ( fixedDataValueGap() == r.fixedDataValueGap() && useFixedDataValueGap() == r.useFixedDataValueGap() && fixedValueBlockGap() == r.fixedValueBlockGap() && useFixedValueBlockGap() == r.useFixedValueBlockGap() && fixedBarWidth() == r.fixedBarWidth() && useFixedBarWidth() == r.useFixedBarWidth() && groupGapFactor() == r.groupGapFactor() && barGapFactor() == r.barGapFactor() && drawSolidExcessArrows() == r.drawSolidExcessArrows() ) return true; else return false; } void BarAttributes::setFixedDataValueGap( qreal gap ) { d->datasetGap = gap; } qreal BarAttributes::fixedDataValueGap() const { return d->datasetGap; } void BarAttributes::setUseFixedDataValueGap( bool gapIsFixed ) { d->useFixedDatasetGap = gapIsFixed; } bool BarAttributes::useFixedDataValueGap() const { return d->useFixedDatasetGap; } void BarAttributes::setFixedValueBlockGap( qreal gap ) { d->valueBlockGap = gap; } qreal BarAttributes::fixedValueBlockGap() const { return d->valueBlockGap; } void BarAttributes::setUseFixedValueBlockGap( bool gapIsFixed ) { d->useFixedValueBlockGap = gapIsFixed; } bool BarAttributes::useFixedValueBlockGap() const { return d->useFixedValueBlockGap; } void BarAttributes::setFixedBarWidth( qreal width ) { d->barWidth = width; } qreal BarAttributes::fixedBarWidth() const { return d->barWidth; } void BarAttributes::setUseFixedBarWidth( bool useFixedBarWidth ) { d->useFixedBarWidth = useFixedBarWidth; } bool BarAttributes::useFixedBarWidth() const { return d->useFixedBarWidth; } void BarAttributes::setGroupGapFactor( qreal gapFactor ) { d->groupGapFactor = gapFactor; } qreal BarAttributes::groupGapFactor() const { return d->groupGapFactor; } void BarAttributes::setBarGapFactor( qreal gapFactor ) { d->barGapFactor = gapFactor; } qreal BarAttributes::barGapFactor() const { return d->barGapFactor; } void BarAttributes::setDrawSolidExcessArrows( bool solidArrows ) { d->drawSolidExcessArrows = solidArrows; } bool BarAttributes::drawSolidExcessArrows() const { return d->drawSolidExcessArrows; } diff --git a/src/KChart/Cartesian/KChartBarDiagram.cpp b/src/KChart/Cartesian/KChartBarDiagram.cpp index bafb722..fd363ea 100644 --- a/src/KChart/Cartesian/KChartBarDiagram.cpp +++ b/src/KChart/Cartesian/KChartBarDiagram.cpp @@ -1,411 +1,411 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartBarDiagram.h" #include "KChartBarDiagram_p.h" #include "KChartThreeDBarAttributes.h" #include "KChartPosition.h" #include "KChartAttributesModel.h" #include "KChartAbstractGrid.h" #include "KChartPainterSaver_p.h" #include #include #include "KChartNormalBarDiagram_p.h" #include "KChartStackedBarDiagram_p.h" #include "KChartPercentBarDiagram_p.h" #include "KChartNormalLyingBarDiagram_p.h" #include "KChartStackedLyingBarDiagram_p.h" #include "KChartPercentLyingBarDiagram_p.h" #include "KChartMath_p.h" using namespace KChart; BarDiagram::Private::Private() : orientation( Qt::Vertical ) - , implementor( 0 ) - , normalDiagram( 0 ) - , stackedDiagram( 0 ) - , percentDiagram( 0 ) - , normalLyingDiagram( 0 ) - , stackedLyingDiagram( 0 ) - , percentLyingDiagram( 0 ) + , implementor( nullptr ) + , normalDiagram( nullptr ) + , stackedDiagram( nullptr ) + , percentDiagram( nullptr ) + , normalLyingDiagram( nullptr ) + , stackedLyingDiagram( nullptr ) + , percentLyingDiagram( nullptr ) { } BarDiagram::Private::~Private() { delete normalDiagram; delete stackedDiagram; delete percentDiagram; delete normalLyingDiagram; delete stackedLyingDiagram; delete percentLyingDiagram; } void BarDiagram::Private::setOrientationAndType( Qt::Orientation o, BarDiagram::BarType type ) { if ( orientation == o && implementor->type() == type ) { return; } BarDiagram *barDia = qobject_cast< BarDiagram * >( diagram ); orientation = o; if ( orientation == Qt::Vertical ) { switch ( type ) { case Normal: implementor = normalDiagram; break; case Stacked: implementor = stackedDiagram; break; case Percent: implementor = percentDiagram; break; default: Q_ASSERT_X( false, "BarDiagram::setType", "unknown diagram subtype" ); } } else { switch ( type ) { case Normal: implementor = normalLyingDiagram; break; case Stacked: implementor = stackedLyingDiagram; break; case Percent: implementor = percentLyingDiagram; break; default: Q_ASSERT_X( false, "BarDiagram::setType", "unknown diagram subtype" ); } } Q_ASSERT( implementor->type() == type ); // AbstractAxis settings - see AbstractDiagram and CartesianAxis barDia->setPercentMode( type == BarDiagram::Percent ); barDia->setDataBoundariesDirty(); emit barDia->layoutChanged( barDia ); emit barDia->propertiesChanged(); } #define d d_func() BarDiagram::BarDiagram( QWidget* parent, CartesianCoordinatePlane* plane ) : AbstractCartesianDiagram( new Private(), parent, plane ) { init(); } void BarDiagram::init() { d->normalDiagram = new NormalBarDiagram( this ); d->stackedDiagram = new StackedBarDiagram( this ); d->percentDiagram = new PercentBarDiagram( this ); d->normalLyingDiagram = new NormalLyingBarDiagram( this ); d->stackedLyingDiagram = new StackedLyingBarDiagram( this ); d->percentLyingDiagram = new PercentLyingBarDiagram( this ); d->implementor = d->normalDiagram; d->compressor.setModel( attributesModel() ); } BarDiagram::~BarDiagram() { } /** * Creates an exact copy of this diagram. */ BarDiagram * BarDiagram::clone() const { BarDiagram* newDiagram = new BarDiagram( new Private( *d ) ); newDiagram->setType( type() ); return newDiagram; } bool BarDiagram::compare( const BarDiagram* other ) const { if ( other == this ) return true; if ( ! other ) { return false; } return // compare the base class ( static_cast(this)->compare( other ) ) && // compare own properties (type() == other->type()); } /** * Sets the bar diagram's type to \a type * \sa BarDiagram::BarType */ void BarDiagram::setType( const BarType type ) { d->setOrientationAndType( d->orientation, type ); } /** * @return the type of the bar diagram */ BarDiagram::BarType BarDiagram::type() const { return d->implementor->type(); } /** * Sets the orientation of the bar diagram */ void BarDiagram::setOrientation( Qt::Orientation orientation ) { d->setOrientationAndType( orientation, d->implementor->type() ); } /** * @return the orientation of the bar diagram */ Qt::Orientation BarDiagram::orientation() const { return d->orientation; } /** * Sets the global bar attributes to \a ba */ void BarDiagram::setBarAttributes( const BarAttributes& ba ) { d->attributesModel->setModelData( qVariantFromValue( ba ), BarAttributesRole ); emit propertiesChanged(); } /** * Sets the bar attributes of data set \a column to \a ba */ void BarDiagram::setBarAttributes( int column, const BarAttributes& ba ) { d->setDatasetAttrs( column, qVariantFromValue( ba ), BarAttributesRole ); emit propertiesChanged(); } /** * Sets the line attributes for the model index \a index to \a ba */ void BarDiagram::setBarAttributes( const QModelIndex& index, const BarAttributes& ba ) { attributesModel()->setData( d->attributesModel->mapFromSource( index ), qVariantFromValue( ba ), BarAttributesRole ); emit propertiesChanged(); } /** * @return the global bar attribute set */ BarAttributes BarDiagram::barAttributes() const { return d->attributesModel->data( KChart::BarAttributesRole ).value(); } /** * @return the bar attribute set of data set \a column */ BarAttributes BarDiagram::barAttributes( int column ) const { const QVariant attrs( d->datasetAttrs( column, KChart::BarAttributesRole ) ); if ( attrs.isValid() ) return attrs.value(); return barAttributes(); } /** * @return the bar attribute set of the model index \a index */ BarAttributes BarDiagram::barAttributes( const QModelIndex& index ) const { return d->attributesModel->data( d->attributesModel->mapFromSource( index ), KChart::BarAttributesRole ).value(); } /** * Sets the global 3D bar attributes to \a threeDAttrs */ void BarDiagram::setThreeDBarAttributes( const ThreeDBarAttributes& threeDAttrs ) { setDataBoundariesDirty(); d->attributesModel->setModelData( qVariantFromValue( threeDAttrs ), ThreeDBarAttributesRole ); emit layoutChanged( this ); emit propertiesChanged(); } /** * Sets the 3D bar attributes of dataset \a column to \a threeDAttrs */ void BarDiagram::setThreeDBarAttributes( int column, const ThreeDBarAttributes& threeDAttrs ) { setDataBoundariesDirty(); d->setDatasetAttrs( column, qVariantFromValue( threeDAttrs ), ThreeDBarAttributesRole ); //emit layoutChanged( this ); emit propertiesChanged(); } /** * Sets the 3D line attributes of model index \a index to \a threeDAttrs */ void BarDiagram::setThreeDBarAttributes( const QModelIndex& index, const ThreeDBarAttributes& threeDAttrs ) { setDataBoundariesDirty(); d->attributesModel->setData( d->attributesModel->mapFromSource(index), qVariantFromValue( threeDAttrs ), ThreeDBarAttributesRole ); //emit layoutChanged( this ); emit propertiesChanged(); } /** * @return the global 3D bar attributes */ ThreeDBarAttributes BarDiagram::threeDBarAttributes() const { return d->attributesModel->data( KChart::ThreeDBarAttributesRole ).value(); } /** * @return the 3D bar attributes of data set \a column */ ThreeDBarAttributes BarDiagram::threeDBarAttributes( int column ) const { const QVariant attrs( d->datasetAttrs( column, KChart::ThreeDBarAttributesRole ) ); if ( attrs.isValid() ) return attrs.value(); return threeDBarAttributes(); } /** * @return the 3D bar attributes of the model index \a index */ ThreeDBarAttributes BarDiagram::threeDBarAttributes( const QModelIndex& index ) const { return d->attributesModel->data( d->attributesModel->mapFromSource(index), KChart::ThreeDBarAttributesRole ).value(); } qreal BarDiagram::threeDItemDepth( const QModelIndex& index ) const { return threeDBarAttributes( index ).validDepth(); } qreal BarDiagram::threeDItemDepth( int column ) const { return threeDBarAttributes( column ).validDepth(); } void BarDiagram::resizeEvent ( QResizeEvent*) { } const QPair BarDiagram::calculateDataBoundaries() const { d->compressor.setResolution( static_cast( this->size().width() * coordinatePlane()->zoomFactorX() ), static_cast( this->size().height() * coordinatePlane()->zoomFactorY() ) ); if ( !checkInvariants( true ) ) { return QPair< QPointF, QPointF >( QPointF( 0, 0 ), QPointF( 0, 0 ) ); } // note: calculateDataBoundaries() is ignoring the hidden flags. // That's not a bug but a feature: Hiding data does not mean removing them. // For totally removing data from KD Chart's view people can use e.g. a proxy model // calculate boundaries for different line types Normal - Stacked - Percent - Default Normal return d->implementor->calculateDataBoundaries(); } void BarDiagram::paintEvent ( QPaintEvent*) { QPainter painter ( viewport() ); PaintContext ctx; ctx.setPainter ( &painter ); ctx.setRectangle( QRectF ( 0, 0, width(), height() ) ); paint ( &ctx ); } void BarDiagram::paint( PaintContext* ctx ) { if ( !checkInvariants( true ) ) return; if ( !AbstractGrid::isBoundariesValid(dataBoundaries()) ) return; const PainterSaver p( ctx->painter() ); if ( model()->rowCount( rootIndex() ) == 0 || model()->columnCount( rootIndex() ) == 0 ) return; // nothing to paint for us AbstractCoordinatePlane* const plane = ctx->coordinatePlane(); ctx->setCoordinatePlane( plane->sharedAxisMasterPlane( ctx->painter() ) ); // This was intended as a fix for KDCH-515, however it caused KDCH-816 // and the original problem in KDCH-515 had by then been fixed in another way. // Bottom line is, this code is wrong because the above call to // plane->sharedAxisMasterPlane() performs a translation of the painter, which // also translates the clip rect, so if we set the old clip rect again afterwards, // we get a wrong clipping. // Also, this code is unnecessary because CartesianCoordinatePlane::paint() // already sets the clipping properly before calling this method. // ctx->painter()->setClipping( true ); // ctx->painter()->setClipRect( ctx->rectangle() ); // paint different bar types Normal - Stacked - Percent - Default Normal d->implementor->paint( ctx ); ctx->setCoordinatePlane( plane ); } void BarDiagram::resize( const QSizeF& size ) { d->compressor.setResolution( static_cast< int >( size.width() * coordinatePlane()->zoomFactorX() ), static_cast< int >( size.height() * coordinatePlane()->zoomFactorY() ) ); setDataBoundariesDirty(); AbstractCartesianDiagram::resize( size ); } #if defined(Q_COMPILER_MANGLES_RETURN_TYPE) const #endif int BarDiagram::numberOfAbscissaSegments () const { return d->attributesModel->rowCount(attributesModelRootIndex()); } #if defined(Q_COMPILER_MANGLES_RETURN_TYPE) const #endif int BarDiagram::numberOfOrdinateSegments () const { return d->attributesModel->columnCount(attributesModelRootIndex()); } //#undef d diff --git a/src/KChart/Cartesian/KChartBarDiagram.h b/src/KChart/Cartesian/KChartBarDiagram.h index 4f3fafa..df1d774 100644 --- a/src/KChart/Cartesian/KChartBarDiagram.h +++ b/src/KChart/Cartesian/KChartBarDiagram.h @@ -1,127 +1,127 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTBARDIAGRAM_H #define KCHARTBARDIAGRAM_H #include "KChartAbstractCartesianDiagram.h" #include "KChartBarAttributes.h" QT_BEGIN_NAMESPACE class QPainter; QT_END_NAMESPACE namespace KChart { class ThreeDBarAttributes; /** * @brief BarDiagram defines a common bar diagram. * * It provides different subtypes which are set using \a setType. */ class KCHART_EXPORT BarDiagram : public AbstractCartesianDiagram { Q_OBJECT Q_DISABLE_COPY( BarDiagram ) KCHART_DECLARE_DERIVED_DIAGRAM( BarDiagram, CartesianCoordinatePlane ) public: class BarDiagramType; friend class BarDiagramType; explicit BarDiagram( - QWidget* parent = 0, CartesianCoordinatePlane* plane = 0 ); + QWidget* parent = nullptr, CartesianCoordinatePlane* plane = nullptr ); virtual ~BarDiagram(); virtual BarDiagram * clone() const; /** * Returns true if both diagrams have the same settings. */ bool compare( const BarDiagram* other ) const; enum BarType { Normal, Stacked, Percent, Rows ///< @deprecated Use BarDiagram::setOrientation() instead }; void setType( const BarType type ); BarType type() const; void setOrientation( Qt::Orientation orientation ); Qt::Orientation orientation() const; void setBarAttributes( const BarAttributes & a ); void setBarAttributes( int column, const BarAttributes & a ); void setBarAttributes( const QModelIndex & index, const BarAttributes & a ); BarAttributes barAttributes() const; BarAttributes barAttributes( int column ) const; BarAttributes barAttributes( const QModelIndex & index ) const; void setThreeDBarAttributes( const ThreeDBarAttributes & a ); void setThreeDBarAttributes( int column, const ThreeDBarAttributes & a ); void setThreeDBarAttributes( const QModelIndex & index, const ThreeDBarAttributes & a ); ThreeDBarAttributes threeDBarAttributes() const; ThreeDBarAttributes threeDBarAttributes( int column ) const; ThreeDBarAttributes threeDBarAttributes( const QModelIndex & index ) const; #if defined(Q_COMPILER_MANGLES_RETURN_TYPE) // implement AbstractCartesianDiagram /** \reimpl */ const int numberOfAbscissaSegments () const; /** \reimpl */ const int numberOfOrdinateSegments () const; #else // implement AbstractCartesianDiagram /** \reimpl */ int numberOfAbscissaSegments () const Q_DECL_OVERRIDE; /** \reimpl */ int numberOfOrdinateSegments () const Q_DECL_OVERRIDE; #endif protected: void paint ( PaintContext* paintContext ) Q_DECL_OVERRIDE; public: void resize ( const QSizeF& area ) Q_DECL_OVERRIDE; protected: qreal threeDItemDepth( const QModelIndex & index ) const Q_DECL_OVERRIDE; qreal threeDItemDepth( int column ) const Q_DECL_OVERRIDE; /** \reimpl */ const QPair calculateDataBoundaries() const Q_DECL_OVERRIDE; void paintEvent ( QPaintEvent* ) Q_DECL_OVERRIDE; void resizeEvent ( QResizeEvent* ) Q_DECL_OVERRIDE; private: void calculateValueAndGapWidths( int rowCount, int colCount, qreal groupWidth, qreal& barWidth, qreal& spaceBetweenBars, qreal& spaceBetweenGroups ); }; // End of class BarDiagram } #endif // KCHARTBARDIAGRAM_H diff --git a/src/KChart/Cartesian/KChartCartesianAxis.cpp b/src/KChart/Cartesian/KChartCartesianAxis.cpp index 1ba6237..ca2d6f0 100644 --- a/src/KChart/Cartesian/KChartCartesianAxis.cpp +++ b/src/KChart/Cartesian/KChartCartesianAxis.cpp @@ -1,1173 +1,1173 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartCartesianAxis.h" #include "KChartCartesianAxis_p.h" #include #include #include #include #include #include #include "KChartPaintContext.h" #include "KChartChart.h" #include "KChartAbstractCartesianDiagram.h" #include "KChartAbstractDiagram_p.h" #include "KChartAbstractGrid.h" #include "KChartPainterSaver_p.h" #include "KChartLayoutItems.h" #include "KChartBarDiagram.h" #include "KChartStockDiagram.h" #include "KChartLineDiagram.h" #include "KChartPrintingParameters.h" using namespace KChart; #define d (d_func()) static qreal slightlyLessThan( qreal r ) { if ( r == 0.0 ) { // scale down the epsilon somewhat arbitrarily return r - std::numeric_limits< qreal >::epsilon() * 1e-6; } // scale the epsilon so that it (hopefully) changes at least the least significant bit of r qreal diff = qAbs( r ) * std::numeric_limits< qreal >::epsilon() * 2.0; return r - diff; } static int numSignificantDecimalPlaces( qreal floatNumber ) { static const int maxPlaces = 15; QString sample = QString::number( floatNumber, 'f', maxPlaces ).section( QLatin1Char('.'), 1, 2 ); int ret = maxPlaces; for ( ; ret > 0; ret-- ) { if ( sample[ ret - 1 ] != QLatin1Char( '0' ) ) { break; } } return ret; } // Feature idea: In case of numeric labels, consider limiting the possible values of majorThinningFactor // to something like {1, 2, 5} * 10^n. Or even better, something that achieves round values in the // remaining labels. // ensure we take the const-overload of any following function, esp. required for strict iterators template static const T& constify(T &v) { return v; } TickIterator::TickIterator( CartesianAxis* a, CartesianCoordinatePlane* plane, uint majorThinningFactor, bool omitLastTick ) : m_axis( a ), m_majorThinningFactor( majorThinningFactor ), m_majorLabelCount( 0 ), m_type( NoTick ) { // deal with the things that are specific to axes (like annotations), before the generic init(). const CartesianAxis::Private *axisPriv = CartesianAxis::Private::get( a ); XySwitch xy( axisPriv->isVertical() ); m_dimension = xy( plane->gridDimensionsList().first(), plane->gridDimensionsList().last() ); if ( omitLastTick ) { // In bar and stock charts the last X tick is a fencepost with no associated value, which is // convenient for grid painting. Here we have to manually exclude it to avoid overpainting. m_dimension.end -= m_dimension.stepWidth; } m_annotations = axisPriv->annotations; m_customTicks = axisPriv->customTicksPositions; const qreal inf = std::numeric_limits< qreal >::infinity(); if ( m_customTicks.count() ) { qSort( m_customTicks.begin(), m_customTicks.end() ); m_customTickIndex = 0; m_customTick = m_customTicks.at( m_customTickIndex ); } else { m_customTickIndex = -1; m_customTick = inf; } if ( m_majorThinningFactor > 1 && hasShorterLabels() ) { m_manualLabelTexts = m_axis->shortLabels(); } else { m_manualLabelTexts = m_axis->labels(); } m_manualLabelIndex = m_manualLabelTexts.isEmpty() ? -1 : 0; if ( !m_dimension.isCalculated ) { // ### depending on the data, it is difficult to impossible to choose anchors (where ticks // corresponding to the header labels are) on the ordinate or even the abscissa with // 2-dimensional data. this should be somewhat mitigated by isCalculated only being false // when header data labels should work, at least that seems to be what the code that sets up // the dimensions is trying to do. QStringList dataHeaderLabels; AbstractDiagram* const dia = plane->diagram(); dataHeaderLabels = dia->itemRowLabels(); if ( !dataHeaderLabels.isEmpty() ) { AttributesModel* model = dia->attributesModel(); const int anchorCount = model->rowCount( QModelIndex() ); if ( anchorCount == dataHeaderLabels.count() ) { for ( int i = 0; i < anchorCount; i++ ) { // ### ordinal number as anchor point generally only works for 1-dimensional data m_dataHeaderLabels.insert( qreal( i ), dataHeaderLabels.at( i ) ); } } } } bool hasMajorTicks = m_axis->rulerAttributes().showMajorTickMarks(); bool hasMinorTicks = m_axis->rulerAttributes().showMinorTickMarks(); init( xy.isY, hasMajorTicks, hasMinorTicks, plane ); } static QMap< qreal, QString > allAxisAnnotations( const AbstractCoordinatePlane *plane, bool isY ) { QMap< qreal, QString > annotations; Q_FOREACH( const AbstractDiagram *diagram, plane->diagrams() ) { const AbstractCartesianDiagram *cd = qobject_cast< const AbstractCartesianDiagram* >( diagram ); if ( !cd ) { continue; } Q_FOREACH( const CartesianAxis *axis, cd->axes() ) { const CartesianAxis::Private *axisPriv = CartesianAxis::Private::get( axis ); if ( axisPriv->isVertical() == isY ) { annotations.unite( axisPriv->annotations ); } } } return annotations; } TickIterator::TickIterator( bool isY, const DataDimension& dimension, bool useAnnotationsForTicks, bool hasMajorTicks, bool hasMinorTicks, CartesianCoordinatePlane* plane ) - : m_axis( 0 ), + : m_axis( nullptr ), m_dimension( dimension ), m_majorThinningFactor( 1 ), m_majorLabelCount( 0 ), m_customTickIndex( -1 ), m_manualLabelIndex( -1 ), m_type( NoTick ), m_customTick( std::numeric_limits< qreal >::infinity() ) { if ( useAnnotationsForTicks ) { m_annotations = allAxisAnnotations( plane, isY ); } init( isY, hasMajorTicks, hasMinorTicks, plane ); } void TickIterator::init( bool isY, bool hasMajorTicks, bool hasMinorTicks, CartesianCoordinatePlane* plane ) { Q_ASSERT( std::numeric_limits< qreal >::has_infinity ); m_isLogarithmic = m_dimension.calcMode == AbstractCoordinatePlane::Logarithmic; // sanity check against infinite loops hasMajorTicks = hasMajorTicks && ( m_dimension.stepWidth > 0 || m_isLogarithmic ); hasMinorTicks = hasMinorTicks && ( m_dimension.subStepWidth > 0 || m_isLogarithmic ); XySwitch xy( isY ); GridAttributes gridAttributes = plane->gridAttributes( xy( Qt::Horizontal, Qt::Vertical ) ); m_isLogarithmic = m_dimension.calcMode == AbstractCoordinatePlane::Logarithmic; if ( !m_isLogarithmic ) { // adjustedLowerUpperRange() is intended for use with linear scaling; specifically it would // round lower bounds < 1 to 0. const bool fixedRange = xy( plane->autoAdjustHorizontalRangeToData(), plane->autoAdjustVerticalRangeToData() ) >= 100; const bool adjustLower = gridAttributes.adjustLowerBoundToGrid() && !fixedRange; const bool adjustUpper = gridAttributes.adjustUpperBoundToGrid() && !fixedRange; m_dimension = AbstractGrid::adjustedLowerUpperRange( m_dimension, adjustLower, adjustUpper ); m_decimalPlaces = numSignificantDecimalPlaces( m_dimension.stepWidth ); } else { // the number of significant decimal places for each label naturally varies with logarithmic scaling m_decimalPlaces = -1; } const qreal inf = std::numeric_limits< qreal >::infinity(); // try to place m_position just in front of the first tick to be drawn so that operator++() // can be used to find the first tick if ( m_isLogarithmic ) { if ( ISNAN( m_dimension.start ) || ISNAN( m_dimension.end ) ) { // this can happen in a spurious paint operation before everything is set up; // just bail out to avoid an infinite loop in that case. m_dimension.start = 0.0; m_dimension.end = 0.0; m_position = inf; m_majorTick = inf; m_minorTick = inf; } else if ( m_dimension.start >= 0 ) { m_position = m_dimension.start ? pow( 10.0, floor( log10( m_dimension.start ) ) - 1.0 ) : 1e-6; m_majorTick = hasMajorTicks ? m_position : inf; m_minorTick = hasMinorTicks ? m_position * 20.0 : inf; } else { m_position = -pow( 10.0, ceil( log10( -m_dimension.start ) ) + 1.0 ); m_majorTick = hasMajorTicks ? m_position : inf; m_minorTick = hasMinorTicks ? m_position * 0.09 : inf; } } else { m_majorTick = hasMajorTicks ? m_dimension.start : inf; m_minorTick = hasMinorTicks ? m_dimension.start : inf; m_position = slightlyLessThan( m_dimension.start ); } ++( *this ); } bool TickIterator::areAlmostEqual( qreal r1, qreal r2 ) const { if ( !m_isLogarithmic ) { qreal span = m_dimension.end - m_dimension.start; if ( span == 0 ) { // When start == end, we still want to show one tick if possible, // which needs this function to perform a reasonable comparison. span = qFuzzyIsNull( m_dimension.start) ? 1 : qAbs( m_dimension.start ); } return qAbs( r2 - r1 ) < ( span ) * 1e-6; } else { return qAbs( r2 - r1 ) < qMax( qAbs( r1 ), qAbs( r2 ) ) * 0.01; } } bool TickIterator::isHigherPrecedence( qreal importantTick, qreal unimportantTick ) const { return importantTick != std::numeric_limits< qreal >::infinity() && ( importantTick <= unimportantTick || areAlmostEqual( importantTick, unimportantTick ) ); } void TickIterator::computeMajorTickLabel( int decimalPlaces ) { if ( m_manualLabelIndex >= 0 ) { m_text = m_manualLabelTexts[ m_manualLabelIndex++ ]; if ( m_manualLabelIndex >= m_manualLabelTexts.count() ) { // manual label texts repeat if there are less label texts than ticks on an axis m_manualLabelIndex = 0; } m_type = m_majorThinningFactor > 1 ? MajorTickManualShort : MajorTickManualLong; } else { // if m_axis is null, we are dealing with grid lines. grid lines never need labels. if ( m_axis && ( m_majorLabelCount++ % m_majorThinningFactor ) == 0 ) { QMap< qreal, QString >::ConstIterator it = constify(m_dataHeaderLabels).lowerBound( slightlyLessThan( m_position ) ); if ( it != m_dataHeaderLabels.constEnd() && areAlmostEqual( it.key(), m_position ) ) { m_text = it.value(); m_type = MajorTickHeaderDataLabel; } else { // 'f' to avoid exponential notation for large numbers, consistent with data value text if ( decimalPlaces < 0 ) { decimalPlaces = numSignificantDecimalPlaces( m_position ); } m_text = QString::number( m_position, 'f', decimalPlaces ); m_type = MajorTick; } } else { m_text.clear(); m_type = MajorTick; } } } void TickIterator::operator++() { if ( isAtEnd() ) { return; } const qreal inf = std::numeric_limits< qreal >::infinity(); // make sure to find the next tick at a value strictly greater than m_position if ( !m_annotations.isEmpty() ) { QMap< qreal, QString >::ConstIterator it = constify(m_annotations).upperBound( m_position ); if ( it != m_annotations.constEnd() ) { m_position = it.key(); m_text = it.value(); m_type = CustomTick; } else { m_position = inf; } } else if ( !m_isLogarithmic && m_dimension.stepWidth * 1e6 < qMax( qAbs( m_dimension.start ), qAbs( m_dimension.end ) ) ) { // If the step width is too small to increase m_position at all, we get an infinite loop. // This usually happens when m_dimension.start == m_dimension.end and both are very large. // When start == end, the step width defaults to 1, and it doesn't scale with start or end. // So currently, we bail and show no tick at all for empty ranges > 10^6, but at least we don't hang. m_position = inf; } else { // advance the calculated ticks if ( m_isLogarithmic ) { while ( m_majorTick <= m_position ) { m_majorTick *= m_position >= 0 ? 10 : 0.1; } while ( m_minorTick <= m_position ) { // the next major tick position should be greater than this m_minorTick += m_majorTick * ( m_position >= 0 ? 0.1 : 1.0 ); } } else { while ( m_majorTick <= m_position ) { m_majorTick += m_dimension.stepWidth; } while ( m_minorTick <= m_position ) { m_minorTick += m_dimension.subStepWidth; } } while ( m_customTickIndex >= 0 && m_customTick <= m_position ) { if ( ++m_customTickIndex >= m_customTicks.count() ) { m_customTickIndex = -1; m_customTick = inf; break; } m_customTick = m_customTicks.at( m_customTickIndex ); } // now see which kind of tick we'll have if ( isHigherPrecedence( m_customTick, m_majorTick ) && isHigherPrecedence( m_customTick, m_minorTick ) ) { m_position = m_customTick; computeMajorTickLabel( -1 ); // override the MajorTick type here because those tick's labels are collision-tested, which we don't want // for custom ticks. they may be arbitrarily close to other ticks, causing excessive label thinning. if ( m_type == MajorTick ) { m_type = CustomTick; } } else if ( isHigherPrecedence( m_majorTick, m_minorTick ) ) { m_position = m_majorTick; if ( m_minorTick != inf ) { // realign minor to major m_minorTick = m_majorTick; } computeMajorTickLabel( m_decimalPlaces ); } else if ( m_minorTick != inf ) { m_position = m_minorTick; m_text.clear(); m_type = MinorTick; } else { m_position = inf; } } if ( m_position > m_dimension.end || ISNAN( m_position ) ) { m_position = inf; // make isAtEnd() return true m_text.clear(); m_type = NoTick; } } CartesianAxis::CartesianAxis( AbstractCartesianDiagram* diagram ) : AbstractAxis ( new Private( diagram, this ), diagram ) { init(); } CartesianAxis::~CartesianAxis() { // when we remove the first axis it will unregister itself and // propagate the next one to the primary, thus the while loop while ( d->mDiagram ) { AbstractCartesianDiagram *cd = qobject_cast< AbstractCartesianDiagram* >( d->mDiagram ); cd->takeAxis( this ); } Q_FOREACH( AbstractDiagram *diagram, d->secondaryDiagrams ) { AbstractCartesianDiagram *cd = qobject_cast< AbstractCartesianDiagram* >( diagram ); cd->takeAxis( this ); } } void CartesianAxis::init() { d->customTickLength = 3; d->position = Bottom; setCachedSizeDirty(); connect( this, SIGNAL(coordinateSystemChanged()), SLOT(coordinateSystemChanged()) ); } bool CartesianAxis::compare( const CartesianAxis* other ) const { if ( other == this ) { return true; } if ( !other ) { return false; } return AbstractAxis::compare( other ) && ( position() == other->position() ) && ( titleText() == other->titleText() ) && ( titleTextAttributes() == other->titleTextAttributes() ); } void CartesianAxis::coordinateSystemChanged() { layoutPlanes(); } void CartesianAxis::setTitleText( const QString& text ) { d->titleText = text; setCachedSizeDirty(); layoutPlanes(); } QString CartesianAxis::titleText() const { return d->titleText; } void CartesianAxis::setTitleTextAttributes( const TextAttributes &a ) { d->titleTextAttributes = a; d->useDefaultTextAttributes = false; setCachedSizeDirty(); layoutPlanes(); } TextAttributes CartesianAxis::titleTextAttributes() const { if ( hasDefaultTitleTextAttributes() ) { TextAttributes ta( textAttributes() ); Measure me( ta.fontSize() ); me.setValue( me.value() * 1.5 ); ta.setFontSize( me ); return ta; } return d->titleTextAttributes; } void CartesianAxis::resetTitleTextAttributes() { d->useDefaultTextAttributes = true; setCachedSizeDirty(); layoutPlanes(); } bool CartesianAxis::hasDefaultTitleTextAttributes() const { return d->useDefaultTextAttributes; } void CartesianAxis::setPosition( Position p ) { if ( d->position == p ) { return; } d->position = p; // Invalidating size is not always necessary if both old and new positions are horizontal or both // vertical, but in practice there could be small differences due to who-knows-what, so always adapt // to the possibly new size. Changing position is expensive anyway. setCachedSizeDirty(); layoutPlanes(); } #if defined(Q_COMPILER_MANGLES_RETURN_TYPE) const #endif CartesianAxis::Position CartesianAxis::position() const { return d->position; } void CartesianAxis::layoutPlanes() { if ( ! d->diagram() || ! d->diagram()->coordinatePlane() ) { return; } AbstractCoordinatePlane* plane = d->diagram()->coordinatePlane(); if ( plane ) { plane->layoutPlanes(); } } static bool referenceDiagramIsBarDiagram( const AbstractDiagram * diagram ) { const AbstractCartesianDiagram * dia = qobject_cast< const AbstractCartesianDiagram * >( diagram ); if ( dia && dia->referenceDiagram() ) dia = dia->referenceDiagram(); - return qobject_cast< const BarDiagram* >( dia ) != 0; + return qobject_cast< const BarDiagram* >( dia ) != nullptr; } static bool referenceDiagramNeedsCenteredAbscissaTicks( const AbstractDiagram *diagram ) { const AbstractCartesianDiagram * dia = qobject_cast< const AbstractCartesianDiagram * >( diagram ); if ( dia && dia->referenceDiagram() ) dia = dia->referenceDiagram(); if ( qobject_cast< const BarDiagram* >( dia ) ) return true; if ( qobject_cast< const StockDiagram* >( dia ) ) return true; const LineDiagram * lineDiagram = qobject_cast< const LineDiagram* >( dia ); return lineDiagram && lineDiagram->centerDataPoints(); } bool CartesianAxis::isAbscissa() const { const Qt::Orientation diagramOrientation = referenceDiagramIsBarDiagram( d->diagram() ) ? ( ( BarDiagram* )( d->diagram() ) )->orientation() : Qt::Vertical; return diagramOrientation == Qt::Vertical ? position() == Bottom || position() == Top : position() == Left || position() == Right; } bool CartesianAxis::isOrdinate() const { return !isAbscissa(); } void CartesianAxis::paint( QPainter* painter ) { if ( !d->diagram() || !d->diagram()->coordinatePlane() ) { return; } PaintContext ctx; ctx.setPainter ( painter ); AbstractCoordinatePlane *const plane = d->diagram()->coordinatePlane(); ctx.setCoordinatePlane( plane ); ctx.setRectangle( QRectF( areaGeometry() ) ); PainterSaver painterSaver( painter ); // enable clipping only when required due to zoom, because it slows down painting // (the alternative to clipping when zoomed in requires much more work to paint just the right area) const qreal zoomFactor = d->isVertical() ? plane->zoomFactorY() : plane->zoomFactorX(); if ( zoomFactor > 1.0 ) { painter->setClipRegion( areaGeometry().adjusted( - d->amountOfLeftOverlap - 1, - d->amountOfTopOverlap - 1, d->amountOfRightOverlap + 1, d->amountOfBottomOverlap + 1 ) ); } paintCtx( &ctx ); } const TextAttributes CartesianAxis::Private::titleTextAttributesWithAdjustedRotation() const { TextAttributes titleTA( titleTextAttributes ); int rotation = titleTA.rotation(); if ( position == Left || position == Right ) { rotation += 270; } if ( rotation >= 360 ) { rotation -= 360; } // limit the allowed values to 0, 90, 180, 270 rotation = ( rotation / 90 ) * 90; titleTA.setRotation( rotation ); return titleTA; } QString CartesianAxis::Private::customizedLabelText( const QString& text, Qt::Orientation orientation, qreal value ) const { // ### like in the old code, using int( value ) as column number... QString withUnits = diagram()->unitPrefix( int( value ), orientation, true ) + text + diagram()->unitSuffix( int( value ), orientation, true ); return axis()->customizedLabel( withUnits ); } void CartesianAxis::setTitleSpace( qreal axisTitleSpace ) { d->axisTitleSpace = axisTitleSpace; } qreal CartesianAxis::titleSpace() const { return d->axisTitleSpace; } void CartesianAxis::setTitleSize( qreal value ) { Q_UNUSED( value ) // ### remove me } qreal CartesianAxis::titleSize() const { // ### remove me return 1.0; } void CartesianAxis::Private::drawTitleText( QPainter* painter, CartesianCoordinatePlane* plane, const QRect& geoRect ) const { const TextAttributes titleTA( titleTextAttributesWithAdjustedRotation() ); if ( titleTA.isVisible() ) { TextLayoutItem titleItem( titleText, titleTA, plane->parent(), KChartEnums::MeasureOrientationMinimum, Qt::AlignHCenter | Qt::AlignVCenter ); QPointF point; QSize size = titleItem.sizeHint(); switch ( position ) { case Top: point.setX( geoRect.left() + geoRect.width() / 2 ); point.setY( geoRect.top() + ( size.height() / 2 ) / axisTitleSpace ); size.setWidth( qMin( size.width(), axis()->geometry().width() ) ); break; case Bottom: point.setX( geoRect.left() + geoRect.width() / 2 ); point.setY( geoRect.bottom() - ( size.height() / 2 ) / axisTitleSpace ); size.setWidth( qMin( size.width(), axis()->geometry().width() ) ); break; case Left: point.setX( geoRect.left() + ( size.width() / 2 ) / axisTitleSpace ); point.setY( geoRect.top() + geoRect.height() / 2 ); size.setHeight( qMin( size.height(), axis()->geometry().height() ) ); break; case Right: point.setX( geoRect.right() - ( size.width() / 2 ) / axisTitleSpace ); point.setY( geoRect.top() + geoRect.height() / 2 ); size.setHeight( qMin( size.height(), axis()->geometry().height() ) ); break; } const PainterSaver painterSaver( painter ); painter->setClipping( false ); painter->translate( point ); titleItem.setGeometry( QRect( QPoint( -size.width() / 2, -size.height() / 2 ), size ) ); titleItem.paint( painter ); } } bool CartesianAxis::Private::isVertical() const { return axis()->isAbscissa() == AbstractDiagram::Private::get( diagram() )->isTransposed(); } void CartesianAxis::paintCtx( PaintContext* context ) { Q_ASSERT_X ( d->diagram(), "CartesianAxis::paint", "Function call not allowed: The axis is not assigned to any diagram." ); CartesianCoordinatePlane* plane = dynamic_cast( context->coordinatePlane() ); Q_ASSERT_X ( plane, "CartesianAxis::paint", "Bad function call: PaintContext::coordinatePlane() NOT a cartesian plane." ); // note: Not having any data model assigned is no bug // but we can not draw an axis then either. if ( !d->diagram()->model() ) { return; } const bool centerTicks = referenceDiagramNeedsCenteredAbscissaTicks( d->diagram() ) && isAbscissa(); XySwitch geoXy( d->isVertical() ); QPainter* const painter = context->painter(); // determine the position of the axis (also required for labels) and paint it qreal transversePosition = signalingNaN; // in data space // the next one describes an additional shift in screen space; it is unfortunately required to // make axis sharing work, which uses the areaGeometry() to override the position of the axis. qreal transverseScreenSpaceShift = signalingNaN; { // determine the unadulterated position in screen space DataDimension dimX = plane->gridDimensionsList().first(); DataDimension dimY = plane->gridDimensionsList().last(); QPointF start( dimX.start, dimY.start ); QPointF end( dimX.end, dimY.end ); // consider this: you can turn a diagonal line into a horizontal or vertical line on any // edge by changing just one of its four coordinates. switch ( position() ) { case CartesianAxis::Bottom: end.setY( dimY.start ); break; case CartesianAxis::Top: start.setY( dimY.end ); break; case CartesianAxis::Left: end.setX( dimX.start ); break; case CartesianAxis::Right: start.setX( dimX.end ); break; } transversePosition = geoXy( start.y(), start.x() ); QPointF transStart = plane->translate( start ); QPointF transEnd = plane->translate( end ); // an externally set areaGeometry() moves the axis position transversally; the shift is // nonzero only when this is a shared axis const QRect geo = areaGeometry(); switch ( position() ) { case CartesianAxis::Bottom: transverseScreenSpaceShift = geo.top() - transStart.y(); break; case CartesianAxis::Top: transverseScreenSpaceShift = geo.bottom() - transStart.y(); break; case CartesianAxis::Left: transverseScreenSpaceShift = geo.right() - transStart.x(); break; case CartesianAxis::Right: transverseScreenSpaceShift = geo.left() - transStart.x(); break; } geoXy.lvalue( transStart.ry(), transStart.rx() ) += transverseScreenSpaceShift; geoXy.lvalue( transEnd.ry(), transEnd.rx() ) += transverseScreenSpaceShift; if ( rulerAttributes().showRulerLine() ) { bool clipSaved = context->painter()->hasClipping(); painter->setClipping( false ); painter->drawLine( transStart, transEnd ); painter->setClipping( clipSaved ); } } // paint ticks and labels TextAttributes labelTA = textAttributes(); RulerAttributes rulerAttr = rulerAttributes(); int labelThinningFactor = 1; // TODO: label thinning also when grid line distance < 4 pixels, not only when labels collide TextLayoutItem *tickLabel = new TextLayoutItem( QString(), labelTA, plane->parent(), KChartEnums::MeasureOrientationMinimum, Qt::AlignLeft ); TextLayoutItem *prevTickLabel = new TextLayoutItem( QString(), labelTA, plane->parent(), KChartEnums::MeasureOrientationMinimum, Qt::AlignLeft ); QPointF prevTickLabelPos; enum { Layout = 0, Painting, Done }; for ( int step = labelTA.isVisible() ? Layout : Painting; step < Done; step++ ) { bool skipFirstTick = !rulerAttr.showFirstTick(); bool isFirstLabel = true; for ( TickIterator it( this, plane, labelThinningFactor, centerTicks ); !it.isAtEnd(); ++it ) { if ( skipFirstTick ) { skipFirstTick = false; continue; } const qreal drawPos = it.position() + ( centerTicks ? 0.5 : 0. ); QPointF onAxis = plane->translate( geoXy( QPointF( drawPos, transversePosition ) , QPointF( transversePosition, drawPos ) ) ); geoXy.lvalue( onAxis.ry(), onAxis.rx() ) += transverseScreenSpaceShift; const bool isOutwardsPositive = position() == Bottom || position() == Right; // paint the tick mark QPointF tickEnd = onAxis; qreal tickLen = it.type() == TickIterator::CustomTick ? d->customTickLength : tickLength( it.type() == TickIterator::MinorTick ); geoXy.lvalue( tickEnd.ry(), tickEnd.rx() ) += isOutwardsPositive ? tickLen : -tickLen; // those adjustments are required to paint the ticks exactly on the axis and of the right length if ( position() == Top ) { onAxis.ry() += 1; tickEnd.ry() += 1; } else if ( position() == Left ) { tickEnd.rx() += 1; } if ( step == Painting ) { painter->save(); if ( rulerAttr.hasTickMarkPenAt( it.position() ) ) { painter->setPen( rulerAttr.tickMarkPen( it.position() ) ); } else { painter->setPen( it.type() == TickIterator::MinorTick ? rulerAttr.minorTickMarkPen() : rulerAttr.majorTickMarkPen() ); } painter->drawLine( onAxis, tickEnd ); painter->restore(); } if ( it.text().isEmpty() || !labelTA.isVisible() ) { // the following code in the loop is only label painting, so skip it continue; } // paint the label QString text = it.text(); if ( it.type() == TickIterator::MajorTick ) { // add unit prefixes and suffixes, then customize text = d->customizedLabelText( text, geoXy( Qt::Horizontal, Qt::Vertical ), it.position() ); } else if ( it.type() == TickIterator::MajorTickHeaderDataLabel ) { // unit prefixes and suffixes have already been added in this case - only customize text = customizedLabel( text ); } tickLabel->setText( text ); QSizeF size = QSizeF( tickLabel->sizeHint() ); QPolygon labelPoly = tickLabel->boundingPolygon(); Q_ASSERT( labelPoly.count() == 4 ); // for alignment, find the label polygon edge "most parallel" and closest to the axis int axisAngle = 0; switch ( position() ) { case Bottom: axisAngle = 0; break; case Top: axisAngle = 180; break; case Right: axisAngle = 270; break; case Left: axisAngle = 90; break; default: Q_ASSERT( false ); } // the left axis is not actually pointing down and the top axis not actually pointing // left, but their corresponding closest edges of a rectangular unrotated label polygon are. int relAngle = axisAngle - labelTA.rotation() + 45; if ( relAngle < 0 ) { relAngle += 360; } int polyCorner1 = relAngle / 90; QPoint p1 = labelPoly.at( polyCorner1 ); QPoint p2 = labelPoly.at( polyCorner1 == 3 ? 0 : ( polyCorner1 + 1 ) ); QPointF labelPos = tickEnd; qreal labelMargin = rulerAttr.labelMargin(); if ( labelMargin < 0 ) { labelMargin = QFontMetricsF( tickLabel->realFont() ).height() * 0.5; } labelMargin -= tickLabel->marginWidth(); // make up for the margin that's already there switch ( position() ) { case Left: labelPos += QPointF( -size.width() - labelMargin, -0.45 * size.height() - 0.5 * ( p1.y() + p2.y() ) ); break; case Right: labelPos += QPointF( labelMargin, -0.45 * size.height() - 0.5 * ( p1.y() + p2.y() ) ); break; case Top: labelPos += QPointF( -0.45 * size.width() - 0.5 * ( p1.x() + p2.x() ), -size.height() - labelMargin ); break; case Bottom: labelPos += QPointF( -0.45 * size.width() - 0.5 * ( p1.x() + p2.x() ), labelMargin ); break; } tickLabel->setGeometry( QRect( labelPos.toPoint(), size.toSize() ) ); if ( step == Painting ) { tickLabel->paint( painter ); } // collision check the current label against the previous one // like in the old code, we don't shorten or decimate labels if they are already the // manual short type, or if they are the manual long type and on the vertical axis // ### they can still collide though, especially when they're rotated! if ( step == Layout ) { int spaceSavingRotation = geoXy( 270, 0 ); bool canRotate = labelTA.autoRotate() && labelTA.rotation() != spaceSavingRotation; const bool canShortenLabels = !geoXy.isY && it.type() == TickIterator::MajorTickManualLong && it.hasShorterLabels(); bool collides = false; if ( it.type() == TickIterator::MajorTick || it.type() == TickIterator::MajorTickHeaderDataLabel || canShortenLabels || canRotate ) { if ( isFirstLabel ) { isFirstLabel = false; } else { collides = tickLabel->intersects( *prevTickLabel, labelPos, prevTickLabelPos ); qSwap( prevTickLabel, tickLabel ); } prevTickLabelPos = labelPos; } if ( collides ) { // to make room, we try in order: shorten, rotate, decimate if ( canRotate && !canShortenLabels ) { labelTA.setRotation( spaceSavingRotation ); // tickLabel will be reused in the next round tickLabel->setTextAttributes( labelTA ); } else { labelThinningFactor++; } step--; // relayout break; } } } } delete tickLabel; - tickLabel = 0; + tickLabel = nullptr; delete prevTickLabel; - prevTickLabel = 0; + prevTickLabel = nullptr; if ( ! titleText().isEmpty() ) { d->drawTitleText( painter, plane, geometry() ); } } /* pure virtual in QLayoutItem */ bool CartesianAxis::isEmpty() const { return false; // if the axis exists, it has some (perhaps default) content } /* pure virtual in QLayoutItem */ Qt::Orientations CartesianAxis::expandingDirections() const { Qt::Orientations ret; switch ( position() ) { case Bottom: case Top: ret = Qt::Horizontal; break; case Left: case Right: ret = Qt::Vertical; break; default: Q_ASSERT( false ); break; }; return ret; } void CartesianAxis::setCachedSizeDirty() const { d->cachedMaximumSize = QSize(); } /* pure virtual in QLayoutItem */ QSize CartesianAxis::maximumSize() const { if ( ! d->cachedMaximumSize.isValid() ) d->cachedMaximumSize = d->calculateMaximumSize(); return d->cachedMaximumSize; } QSize CartesianAxis::Private::calculateMaximumSize() const { if ( !diagram() ) { return QSize(); } CartesianCoordinatePlane* plane = dynamic_cast< CartesianCoordinatePlane* >( diagram()->coordinatePlane() ); Q_ASSERT( plane ); QObject* refArea = plane->parent(); const bool centerTicks = referenceDiagramNeedsCenteredAbscissaTicks( diagram() ) && axis()->isAbscissa(); // we ignore: // - label thinning (expensive, not worst case and we want worst case) // - label autorotation (expensive, obscure feature(?)) // - axis length (it is determined by the plane / diagram / chart anyway) // - the title's influence on axis length; this one might be TODO. See KDCH-863. XySwitch geoXy( isVertical() ); qreal size = 0; // this is the size transverse to the axis direction // the following variables describe how much the first and last label stick out over the axis // area, so that the geometry of surrounding layout items can be adjusted to make room. qreal startOverhang = 0.0; qreal endOverhang = 0.0; if ( mAxis->textAttributes().isVisible() ) { // these four are used just to calculate startOverhang and endOverhang qreal lowestLabelPosition = signalingNaN; qreal highestLabelPosition = signalingNaN; qreal lowestLabelLongitudinalSize = signalingNaN; qreal highestLabelLongitudinalSize = signalingNaN; TextLayoutItem tickLabel( QString(), mAxis->textAttributes(), refArea, KChartEnums::MeasureOrientationMinimum, Qt::AlignLeft ); const RulerAttributes rulerAttr = mAxis->rulerAttributes(); bool showFirstTick = rulerAttr.showFirstTick(); for ( TickIterator it( axis(), plane, 1, centerTicks ); !it.isAtEnd(); ++it ) { const qreal drawPos = it.position() + ( centerTicks ? 0.5 : 0. ); if ( !showFirstTick ) { showFirstTick = true; continue; } qreal labelSizeTransverse = 0.0; qreal labelMargin = 0.0; QString text = it.text(); if ( !text.isEmpty() ) { QPointF labelPosition = plane->translate( QPointF( geoXy( drawPos, 1.0 ), geoXy( 1.0, drawPos ) ) ); highestLabelPosition = geoXy( labelPosition.x(), labelPosition.y() ); if ( it.type() == TickIterator::MajorTick ) { // add unit prefixes and suffixes, then customize text = customizedLabelText( text, geoXy( Qt::Horizontal, Qt::Vertical ), it.position() ); } else if ( it.type() == TickIterator::MajorTickHeaderDataLabel ) { // unit prefixes and suffixes have already been added in this case - only customize text = axis()->customizedLabel( text ); } tickLabel.setText( text ); QSize sz = tickLabel.sizeHint(); highestLabelLongitudinalSize = geoXy( sz.width(), sz.height() ); if ( ISNAN( lowestLabelLongitudinalSize ) ) { lowestLabelLongitudinalSize = highestLabelLongitudinalSize; lowestLabelPosition = highestLabelPosition; } labelSizeTransverse = geoXy( sz.height(), sz.width() ); labelMargin = rulerAttr.labelMargin(); if ( labelMargin < 0 ) { labelMargin = QFontMetricsF( tickLabel.realFont() ).height() * 0.5; } labelMargin -= tickLabel.marginWidth(); // make up for the margin that's already there } qreal tickLength = it.type() == TickIterator::CustomTick ? customTickLength : axis()->tickLength( it.type() == TickIterator::MinorTick ); size = qMax( size, tickLength + labelMargin + labelSizeTransverse ); } const DataDimension dimX = plane->gridDimensionsList().first(); const DataDimension dimY = plane->gridDimensionsList().last(); QPointF pt = plane->translate( QPointF( dimX.start, dimY.start ) ); const qreal lowestPosition = geoXy( pt.x(), pt.y() ); pt = plane->translate( QPointF( dimX.end, dimY.end ) ); const qreal highestPosition = geoXy( pt.x(), pt.y() ); // the geoXy( 1.0, -1.0 ) here is necessary because Qt's y coordinate is inverted startOverhang = qMax( 0.0, ( lowestPosition - lowestLabelPosition ) * geoXy( 1.0, -1.0 ) + lowestLabelLongitudinalSize * 0.5 ); endOverhang = qMax( 0.0, ( highestLabelPosition - highestPosition ) * geoXy( 1.0, -1.0 ) + highestLabelLongitudinalSize * 0.5 ); } amountOfLeftOverlap = geoXy( startOverhang, 0.0 ); amountOfRightOverlap = geoXy( endOverhang, 0.0 ); amountOfBottomOverlap = geoXy( 0.0, startOverhang ); amountOfTopOverlap = geoXy( 0.0, endOverhang ); const TextAttributes titleTA = titleTextAttributesWithAdjustedRotation(); if ( titleTA.isVisible() && !axis()->titleText().isEmpty() ) { TextLayoutItem title( axis()->titleText(), titleTA, refArea, KChartEnums::MeasureOrientationMinimum, Qt::AlignHCenter | Qt::AlignVCenter ); QFontMetricsF titleFM( title.realFont(), GlobalMeasureScaling::paintDevice() ); size += geoXy( titleFM.height() * 0.33, titleFM.averageCharWidth() * 0.55 ); // spacing size += geoXy( title.sizeHint().height(), title.sizeHint().width() ); } // the size parallel to the axis direction is not determined by us, so we just return 1 return QSize( geoXy( 1, int( size ) ), geoXy( int ( size ), 1 ) ); } /* pure virtual in QLayoutItem */ QSize CartesianAxis::minimumSize() const { return maximumSize(); } /* pure virtual in QLayoutItem */ QSize CartesianAxis::sizeHint() const { return maximumSize(); } /* pure virtual in QLayoutItem */ void CartesianAxis::setGeometry( const QRect& r ) { if ( d->geometry != r ) { d->geometry = r; setCachedSizeDirty(); } } /* pure virtual in QLayoutItem */ QRect CartesianAxis::geometry() const { return d->geometry; } void CartesianAxis::setCustomTickLength( int value ) { if ( d->customTickLength == value ) { return; } d->customTickLength = value; setCachedSizeDirty(); layoutPlanes(); } int CartesianAxis::customTickLength() const { return d->customTickLength; } int CartesianAxis::tickLength( bool subUnitTicks ) const { const RulerAttributes& rulerAttr = rulerAttributes(); return subUnitTicks ? rulerAttr.minorTickMarkLength() : rulerAttr.majorTickMarkLength(); } QMap< qreal, QString > CartesianAxis::annotations() const { return d->annotations; } void CartesianAxis::setAnnotations( const QMap< qreal, QString >& annotations ) { if ( d->annotations == annotations ) return; d->annotations = annotations; setCachedSizeDirty(); layoutPlanes(); } QList< qreal > CartesianAxis::customTicks() const { return d->customTicksPositions; } void CartesianAxis::setCustomTicks( const QList< qreal >& customTicksPositions ) { if ( d->customTicksPositions == customTicksPositions ) return; d->customTicksPositions = customTicksPositions; setCachedSizeDirty(); layoutPlanes(); } #if !defined(QT_NO_DEBUG_STREAM) QDebug operator<<(QDebug dbg, KChart::CartesianAxis::Position pos) { switch (pos) { case KChart::CartesianAxis::Bottom: dbg << "KChart::CartesianAxis::Bottom"; break; case KChart::CartesianAxis::Top: dbg << "KChart::CartesianAxis::Top"; break; case KChart::CartesianAxis::Left: dbg << "KChart::CartesianAxis::Left"; break; case KChart::CartesianAxis::Right: dbg << "KChart::CartesianAxis::Right"; break; default: dbg << "KChart::CartesianAxis::Invalid"; break; } return dbg; } #endif diff --git a/src/KChart/Cartesian/KChartCartesianAxis.h b/src/KChart/Cartesian/KChartCartesianAxis.h index b2770f2..03bac14 100644 --- a/src/KChart/Cartesian/KChartCartesianAxis.h +++ b/src/KChart/Cartesian/KChartCartesianAxis.h @@ -1,192 +1,192 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTCARTESIANAXIS_H #define KCHARTCARTESIANAXIS_H #include #include "KChartAbstractAxis.h" namespace KChart { class AbstractCartesianDiagram; /** * The class for cartesian axes. * * For being useful, axes need to be assigned to a diagram, see * AbstractCartesianDiagram::addAxis and AbstractCartesianDiagram::takeAxis. * * \sa PolarAxis, AbstractCartesianDiagram */ class KCHART_EXPORT CartesianAxis : public AbstractAxis { Q_OBJECT Q_DISABLE_COPY( CartesianAxis ) KCHART_DECLARE_PRIVATE_DERIVED_PARENT( CartesianAxis, AbstractDiagram* ) Q_ENUMS(Position) public: enum Position { Bottom, Top, Right, Left }; /** * C'tor of the class for cartesian axes. * * \note If you pass a null parent to the constructor, you need to call * your diagram's addAxis function to add your axis to the diagram. * Otherwise there is no need to call addAxis, since the constructor * does it already. * * \sa AbstractCartesianDiagram::addAxis */ - explicit CartesianAxis ( AbstractCartesianDiagram* diagram = 0 ); + explicit CartesianAxis ( AbstractCartesianDiagram* diagram = nullptr ); ~CartesianAxis(); /** * Returns true if both axes have the same settings. */ bool compare( const CartesianAxis* other ) const; /** reimpl */ void paint( QPainter* ) Q_DECL_OVERRIDE; /** reimpl */ void paintCtx( PaintContext* ) Q_DECL_OVERRIDE; /** * Sets the optional text displayed as chart title. */ void setTitleText( const QString& text ); QString titleText() const; /** * \deprecated * Sets the spacing between the title and the diagram. * Be aware that setting this value can lead to * collisions between axis labels and the title */ void setTitleSpace( qreal value ); /// \deprecated qreal titleSpace() const; /// \deprecated \brief use setTitleTextAttributes() instead void setTitleSize(qreal value); /// \deprecated qreal titleSize() const; void setTitleTextAttributes( const TextAttributes &a ); /** * Returns the text attributes that will be used for displaying the * title text. * This is either the text attributes as specified by setTitleTextAttributes, * or (if setTitleTextAttributes() was not called) the default text attributes. * \sa resetTitleTextAttributes, hasDefaultTitleTextAttributes */ TextAttributes titleTextAttributes() const; /** * Reset the title text attributes to the built-in default: * * Same font and pen as AbstractAxis::textAttributes() * and 1.5 times their size. */ void resetTitleTextAttributes(); bool hasDefaultTitleTextAttributes() const; virtual void setPosition ( Position p ); #if defined(Q_COMPILER_MANGLES_RETURN_TYPE) virtual const Position position () const; #else virtual Position position () const; #endif virtual void layoutPlanes(); virtual bool isAbscissa() const; virtual bool isOrdinate() const; /** * Sets the axis annotations to \a annotations. * Annotations are a QMap of qreals and QStrings defining special * markers and their position. * If you use annotations, the normal ticks and values will be invisible. * To unset the annotations, pass an empty QMap. */ void setAnnotations( const QMap< qreal, QString >& annotations ); /** * Returns the currently set axis annotations. */ QMap< qreal, QString > annotations() const; /** * Sets custom ticks on the axis. * Ticks are a QList of qreals defining their special position. */ void setCustomTicks( const QList< qreal >& ticksPostions ); /** * Returns the currently set custom ticks on the axis. */ QList< qreal > customTicks() const; /** * Sets the length of custom ticks on the axis. */ void setCustomTickLength(int value); /** * Returns the length of custom ticks on the axis. */ int customTickLength() const; /** pure virtual in QLayoutItem */ bool isEmpty() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize maximumSize() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize minimumSize() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize sizeHint() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QRect geometry() const Q_DECL_OVERRIDE; public Q_SLOTS: void setCachedSizeDirty() const; virtual int tickLength( bool subUnitTicks = false ) const; private Q_SLOTS: void coordinateSystemChanged(); }; typedef QList CartesianAxisList; } #if !defined(QT_NO_DEBUG_STREAM) KCHART_EXPORT QDebug operator<<(QDebug dbg, KChart::CartesianAxis::Position pos); #endif #endif diff --git a/src/KChart/Cartesian/KChartCartesianCoordinatePlane.cpp b/src/KChart/Cartesian/KChartCartesianCoordinatePlane.cpp index 056875d..0d40264 100644 --- a/src/KChart/Cartesian/KChartCartesianCoordinatePlane.cpp +++ b/src/KChart/Cartesian/KChartCartesianCoordinatePlane.cpp @@ -1,920 +1,920 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartCartesianCoordinatePlane.h" #include "KChartCartesianCoordinatePlane_p.h" #include "KChartAbstractDiagram.h" #include "KChartAbstractDiagram_p.h" #include "KChartAbstractCartesianDiagram.h" #include "CartesianCoordinateTransformation.h" #include "KChartGridAttributes.h" #include "KChartPaintContext.h" #include "KChartPainterSaver_p.h" #include "KChartBarDiagram.h" #include "KChartStockDiagram.h" #include #include #include #include #include #include using namespace KChart; #define d d_func() CartesianCoordinatePlane::Private::Private() : AbstractCoordinatePlane::Private() , bPaintIsRunning( false ) , hasOwnGridAttributesHorizontal( false ) , hasOwnGridAttributesVertical( false ) , isometricScaling( false ) , horizontalMin( 0 ) , horizontalMax( 0 ) , verticalMin( 0 ) , verticalMax( 0 ) , autoAdjustHorizontalRangeToData( 67 ) , autoAdjustVerticalRangeToData( 67 ) , autoAdjustGridToZoom( true ) , fixedDataCoordinateSpaceRelation( false ) , xAxisStartAtZero( true ) , reverseVerticalPlane( false ) , reverseHorizontalPlane( false ) { } CartesianCoordinatePlane::CartesianCoordinatePlane( Chart* parent ) : AbstractCoordinatePlane( new Private(), parent ) { // this bloc left empty intentionally } CartesianCoordinatePlane::~CartesianCoordinatePlane() { // this bloc left empty intentionally } void CartesianCoordinatePlane::init() { // this bloc left empty intentionally } void CartesianCoordinatePlane::addDiagram( AbstractDiagram* diagram ) { Q_ASSERT_X( dynamic_cast( diagram ), "CartesianCoordinatePlane::addDiagram", "Only cartesian " "diagrams can be added to a cartesian coordinate plane!" ); AbstractCoordinatePlane::addDiagram( diagram ); connect( diagram, SIGNAL(layoutChanged(AbstractDiagram*)), SLOT(slotLayoutChanged(AbstractDiagram*)) ); connect( diagram, SIGNAL(propertiesChanged()), this, SIGNAL(propertiesChanged()) ); } void CartesianCoordinatePlane::paint( QPainter* painter ) { // prevent recursive call: if ( d->bPaintIsRunning ) { return; } d->bPaintIsRunning = true; AbstractDiagramList diags = diagrams(); if ( !diags.isEmpty() ) { PaintContext ctx; ctx.setPainter ( painter ); ctx.setCoordinatePlane ( this ); const QRectF drawArea( drawingArea() ); ctx.setRectangle ( drawArea ); // enabling clipping so that we're not drawing outside PainterSaver painterSaver( painter ); QRect clipRect = drawArea.toRect().adjusted( -1, -1, 1, 1 ); QRegion clipRegion( clipRect ); painter->setClipRegion( clipRegion ); // paint the coordinate system rulers: d->grid->drawGrid( &ctx ); // paint the diagrams: for ( int i = 0; i < diags.size(); i++ ) { if ( diags[i]->isHidden() ) { continue; } bool doDumpPaintTime = AbstractDiagram::Private::get( diags[ i ] )->doDumpPaintTime; QTime stopWatch; if ( doDumpPaintTime ) { stopWatch.start(); } PainterSaver diagramPainterSaver( painter ); diags[i]->paint( &ctx ); if ( doDumpPaintTime ) { qDebug() << "Painting diagram" << i << "took" << stopWatch.elapsed() << "milliseconds"; } } } d->bPaintIsRunning = false; } void CartesianCoordinatePlane::slotLayoutChanged( AbstractDiagram* ) { layoutDiagrams(); } QRectF CartesianCoordinatePlane::getRawDataBoundingRectFromDiagrams() const { // determine unit of the rectangles of all involved diagrams: qreal minX = 0; qreal maxX = 0; qreal minY = 0; qreal maxY = 0; bool bStarting = true; Q_FOREACH( const AbstractDiagram* diagram, diagrams() ) { QPair dataBoundariesPair = diagram->dataBoundaries(); //qDebug() << "CartesianCoordinatePlane::getRawDataBoundingRectFromDiagrams()\ngets diagram->dataBoundaries: " << dataBoundariesPair.first << dataBoundariesPair.second; if ( bStarting || dataBoundariesPair.first.x() < minX ) minX = dataBoundariesPair.first.x(); if ( bStarting || dataBoundariesPair.first.y() < minY ) minY = dataBoundariesPair.first.y(); if ( bStarting || dataBoundariesPair.second.x() > maxX ) maxX = dataBoundariesPair.second.x(); if ( bStarting || dataBoundariesPair.second.y() > maxY ) maxY = dataBoundariesPair.second.y(); bStarting = false; } //qDebug() << "CartesianCoordinatePlane::getRawDataBoundingRectFromDiagrams()\nreturns data boundaries: " << QRectF( QPointF(minX, minY), QSizeF(maxX - minX, maxY - minY) ); QRectF dataBoundingRect; dataBoundingRect.setBottomLeft( QPointF( minX, minY ) ); dataBoundingRect.setTopRight( QPointF( maxX, maxY ) ); return dataBoundingRect; } QRectF CartesianCoordinatePlane::adjustedToMaxEmptyInnerPercentage( const QRectF& r, unsigned int percentX, unsigned int percentY ) const { QRectF ret = r; if ( ( axesCalcModeX() != Logarithmic || r.left() < 0.0 ) && percentX > 0 && percentX != 100 ) { const bool isPositive = r.left() >= 0; if ( ( r.right() >= 0 ) == isPositive ) { qreal upperBound = qMax( r.left(), r.right() ); qreal lowerBound = qMin( r.left(), r.right() ); qreal innerBound = isPositive ? lowerBound : upperBound; qreal outerBound = isPositive ? upperBound : lowerBound; if ( innerBound / outerBound * 100 <= percentX && d->xAxisStartAtZero ) { if ( isPositive ) { ret.setLeft( 0.0 ); } else { ret.setRight( 0.0 ); } } } } // ### this doesn't seem to take into account that Qt's y coordinate is inverted if ( ( axesCalcModeY() != Logarithmic || r.bottom() < 0.0 ) && percentY > 0 && percentY != 100 ) { const bool isPositive = r.bottom() >= 0; if ( ( r.top() >= 0 ) == isPositive ) { qreal upperBound = qMax( r.top(), r.bottom() ); qreal lowerBound = qMin( r.top(), r.bottom() ); const qreal innerBound = isPositive ? lowerBound : upperBound; const qreal outerBound = isPositive ? upperBound : lowerBound; if ( innerBound / outerBound * 100 <= percentY ) { if ( isPositive ) { ret.setBottom( 0.0 ); } else { ret.setTop( 0.0 ); } } } } return ret; } QRectF CartesianCoordinatePlane::calculateRawDataBoundingRect() const { // are manually set ranges to be applied? const bool bAutoAdjustHorizontalRange = d->autoAdjustHorizontalRangeToData < 100; const bool bAutoAdjustVerticalRange = d->autoAdjustVerticalRangeToData < 100; const bool bHardHorizontalRange = (!bAutoAdjustHorizontalRange) && (d->horizontalMin != d->horizontalMax || (ISNAN(d->horizontalMin) != ISNAN(d->horizontalMax))); const bool bHardVerticalRange = (!bAutoAdjustVerticalRange) && (d->verticalMin != d->verticalMax || (ISNAN(d->verticalMin) != ISNAN(d->verticalMax))); QRectF dataBoundingRect; // if custom boundaries are set on the plane, use them if ( bHardHorizontalRange && bHardVerticalRange ) { dataBoundingRect.setLeft( d->horizontalMin ); dataBoundingRect.setRight( d->horizontalMax ); dataBoundingRect.setBottom( d->verticalMin ); dataBoundingRect.setTop( d->verticalMax ); } else { // determine unit of the rectangles of all involved diagrams: dataBoundingRect = getRawDataBoundingRectFromDiagrams(); if ( bHardHorizontalRange ) { if (!ISNAN(d->horizontalMin)) dataBoundingRect.setLeft( d->horizontalMin ); if (!ISNAN(d->horizontalMax)) dataBoundingRect.setRight( d->horizontalMax ); } if ( bHardVerticalRange ) { if (!ISNAN(d->verticalMin)) dataBoundingRect.setBottom( d->verticalMin ); if (!ISNAN(d->verticalMax)) dataBoundingRect.setTop( d->verticalMax ); } } // recalculate the bounds, if automatic adjusting of ranges is desired AND // both bounds are at the same side of the zero line dataBoundingRect = adjustedToMaxEmptyInnerPercentage( dataBoundingRect, d->autoAdjustHorizontalRangeToData, d->autoAdjustVerticalRangeToData ); if ( bAutoAdjustHorizontalRange ) { const_cast( this )->d->horizontalMin = dataBoundingRect.left(); const_cast( this )->d->horizontalMax = dataBoundingRect.right(); } if ( bAutoAdjustVerticalRange ) { const_cast( this )->d->verticalMin = dataBoundingRect.bottom(); const_cast( this )->d->verticalMax = dataBoundingRect.top(); } // qDebug() << Q_FUNC_INFO << dataBoundingRect; return dataBoundingRect; } DataDimensionsList CartesianCoordinatePlane::getDataDimensionsList() const { - const AbstractCartesianDiagram* dgr = diagrams().isEmpty() ? 0 : + const AbstractCartesianDiagram* dgr = diagrams().isEmpty() ? nullptr : qobject_cast< const AbstractCartesianDiagram* >( diagrams().first() ); if ( dgr && dgr->referenceDiagram() ) { dgr = dgr->referenceDiagram(); } const BarDiagram *barDiagram = qobject_cast< const BarDiagram* >( dgr ); const StockDiagram *stockDiagram = qobject_cast< const StockDiagram* >( dgr ); // note: // It does make sense to retrieve the orientation from the first diagram. This is because // a coordinate plane can either be for horizontal *or* for vertical diagrams. Both at the // same time won't work, and thus the orientation for all diagrams is the same as for the first one. - const Qt::Orientation diagramOrientation = barDiagram != 0 ? barDiagram->orientation() : Qt::Vertical; + const Qt::Orientation diagramOrientation = barDiagram != nullptr ? barDiagram->orientation() : Qt::Vertical; const bool diagramIsVertical = diagramOrientation == Qt::Vertical; DataDimensionsList l; if ( dgr ) { const QRectF r( calculateRawDataBoundingRect() ); // We do not access d->gridAttributesHorizontal/Vertical here, but we use the getter function, // to get the global attrs, if no special ones have been set for the given orientation. const GridAttributes gaH( gridAttributes( Qt::Horizontal ) ); const GridAttributes gaV( gridAttributes( Qt::Vertical ) ); // append the first dimension: for Abscissa axes l.append( DataDimension( r.left(), r.right(), diagramIsVertical ? ( !stockDiagram && dgr->datasetDimension() > 1 ) : true, axesCalcModeX(), gaH.gridGranularitySequence(), gaH.gridStepWidth(), gaH.gridSubStepWidth() ) ); // append the second dimension: for Ordinate axes l.append( DataDimension( r.bottom(), r.top(), diagramIsVertical ? true : ( dgr->datasetDimension() > 1 ), axesCalcModeY(), gaV.gridGranularitySequence(), gaV.gridStepWidth(), gaV.gridSubStepWidth() ) ); } else { l.append( DataDimension() ); // This gets us the default 1..0 / 1..0 grid l.append( DataDimension() ); // shown, if there is no diagram on this plane. } return l; } QRectF CartesianCoordinatePlane::drawingArea() const { // the rectangle the diagrams cover in the *plane*: // We reserve 1px on each side for antialiased drawing, and respect the way QPainter calculates // the width of a painted rect (the size is the rectangle size plus the pen width). The latter // accounts for another pixel that we subtract from height and width. // This way, most clipping for regular pens should be avoided. When pens with a width larger // than 1 are used, this may not be sufficient. return QRectF( areaGeometry() ).adjusted( 1.0, 1.0, -2.0, -2.0 ); } QRectF CartesianCoordinatePlane::logicalArea() const { if ( d->dimensions.isEmpty() ) return QRectF(); const DataDimension dimX = d->dimensions.first(); const DataDimension dimY = d->dimensions.last(); const QPointF pt( qMin( dimX.start, dimX.end ), qMax( dimY.start, dimY.end ) ); const QSizeF siz( qAbs( dimX.distance() ), -qAbs( dimY.distance() ) ); const QRectF dataBoundingRect( pt, siz ); // determine logical top left, taking the "reverse" options into account const QPointF topLeft( d->reverseHorizontalPlane ? dataBoundingRect.right() : dataBoundingRect.left(), d->reverseVerticalPlane ? dataBoundingRect.bottom() : dataBoundingRect.top() ); const qreal width = dataBoundingRect.width() * ( d->reverseHorizontalPlane ? -1.0 : 1.0 ); const qreal height = dataBoundingRect.height() * ( d->reverseVerticalPlane ? -1.0 : 1.0 ); return QRectF( topLeft, QSizeF( width, height ) ); } QRectF CartesianCoordinatePlane::diagramArea() const { const QRectF logArea( logicalArea() ); QPointF physicalTopLeft = d->coordinateTransformation.translate( logArea.topLeft() ); QPointF physicalBottomRight = d->coordinateTransformation.translate( logArea.bottomRight() ); return QRectF( physicalTopLeft, physicalBottomRight ).normalized(); } QRectF CartesianCoordinatePlane::visibleDiagramArea() const { return diagramArea().intersected( drawingArea() ); } void CartesianCoordinatePlane::layoutDiagrams() { d->dimensions = gridDimensionsList(); Q_ASSERT_X ( d->dimensions.count() == 2, "CartesianCoordinatePlane::layoutDiagrams", "Error: gridDimensionsList() did not return exactly two dimensions." ); // physical area of the plane const QRectF physicalArea( drawingArea() ); // .. in contrast to the logical area const QRectF logArea( logicalArea() ); // TODO: isometric scaling for zooming? // the plane area might have changed, so the zoom values might also be changed handleFixedDataCoordinateSpaceRelation( physicalArea ); d->coordinateTransformation.updateTransform( logArea, physicalArea ); update(); } void CartesianCoordinatePlane::setFixedDataCoordinateSpaceRelation( bool fixed ) { d->fixedDataCoordinateSpaceRelation = fixed; d->fixedDataCoordinateSpaceRelationPinnedSize = QSize(); handleFixedDataCoordinateSpaceRelation( drawingArea() ); } bool CartesianCoordinatePlane::hasFixedDataCoordinateSpaceRelation() const { return d->fixedDataCoordinateSpaceRelation; } void CartesianCoordinatePlane::setXAxisStartAtZero(bool fixedStart) { if (d->xAxisStartAtZero == fixedStart) return; d->xAxisStartAtZero = fixedStart; } bool CartesianCoordinatePlane::xAxisStartAtZero() const { return d->xAxisStartAtZero; } void CartesianCoordinatePlane::handleFixedDataCoordinateSpaceRelation( const QRectF& geometry ) { if ( !d->fixedDataCoordinateSpaceRelation ) { return; } // is the new geometry ok? if ( !geometry.isValid() ) { return; } // note that the pinned size can be invalid even after setting it, if geometry wasn't valid. // this is relevant for the cooperation between this method, setFixedDataCoordinateSpaceRelation(), // and handleFixedDataCoordinateSpaceRelation(). if ( !d->fixedDataCoordinateSpaceRelationPinnedSize.isValid() ) { d->fixedDataCoordinateSpaceRelationPinnedSize = geometry.size(); d->fixedDataCoordinateSpaceRelationPinnedZoom = ZoomParameters( zoomFactorX(), zoomFactorY(), zoomCenter() ); return; } // if the plane size was changed, change zoom factors to keep the diagram size constant if ( d->fixedDataCoordinateSpaceRelationPinnedSize != geometry.size() ) { const qreal widthScaling = d->fixedDataCoordinateSpaceRelationPinnedSize.width() / geometry.width(); const qreal heightScaling = d->fixedDataCoordinateSpaceRelationPinnedSize.height() / geometry.height(); const qreal newZoomX = d->fixedDataCoordinateSpaceRelationPinnedZoom.xFactor * widthScaling; const qreal newZoomY = d->fixedDataCoordinateSpaceRelationPinnedZoom.yFactor * heightScaling; const QPointF newCenter = QPointF( d->fixedDataCoordinateSpaceRelationPinnedZoom.xCenter / widthScaling, d->fixedDataCoordinateSpaceRelationPinnedZoom.yCenter / heightScaling ); // Use these internal methods to avoid sending the propertiesChanged signal more than once bool changed = false; if ( doneSetZoomFactorY( newZoomY ) ) changed = true; if ( doneSetZoomFactorX( newZoomX ) ) changed = true; if ( doneSetZoomCenter( newCenter ) ) changed = true; if ( changed ) emit propertiesChanged(); } } const QPointF CartesianCoordinatePlane::translate( const QPointF& diagramPoint ) const { // Note: We do not test if the point lays inside of the data area, // but we just apply the transformation calculations to the point. // This allows for basic calculations done by the user, see e.g. // the file examples/Lines/BubbleChart/mainwindow.cpp return d->coordinateTransformation.translate( diagramPoint ); } const QPointF CartesianCoordinatePlane::translateBack( const QPointF& screenPoint ) const { return d->coordinateTransformation.translateBack( screenPoint ); } void CartesianCoordinatePlane::setIsometricScaling( bool isOn ) { if ( d->isometricScaling != isOn ) { d->isometricScaling = isOn; layoutDiagrams(); emit propertiesChanged(); } } bool CartesianCoordinatePlane::doesIsometricScaling() const { return d->isometricScaling; } bool CartesianCoordinatePlane::doneSetZoomFactorX( qreal factor ) { if ( d->coordinateTransformation.zoom.xFactor == factor ) { return false; } d->coordinateTransformation.zoom.xFactor = factor; if ( d->autoAdjustGridToZoom ) { d->grid->setNeedRecalculate(); } return true; } bool CartesianCoordinatePlane::doneSetZoomFactorY( qreal factor ) { if ( d->coordinateTransformation.zoom.yFactor == factor ) { return false; } d->coordinateTransformation.zoom.yFactor = factor; if ( d->autoAdjustGridToZoom ) { d->grid->setNeedRecalculate(); } return true; } bool CartesianCoordinatePlane::doneSetZoomCenter( const QPointF& point ) { if ( d->coordinateTransformation.zoom.center() == point ) { return false; } d->coordinateTransformation.zoom.setCenter( point ); if ( d->autoAdjustGridToZoom ) { d->grid->setNeedRecalculate(); } return true; } void CartesianCoordinatePlane::setZoomFactors( qreal factorX, qreal factorY ) { if ( doneSetZoomFactorX( factorX ) || doneSetZoomFactorY( factorY ) ) { d->coordinateTransformation.updateTransform( logicalArea(), drawingArea() ); emit propertiesChanged(); } } void CartesianCoordinatePlane::setZoomFactorX( qreal factor ) { if ( doneSetZoomFactorX( factor ) ) { d->coordinateTransformation.updateTransform( logicalArea(), drawingArea() ); emit propertiesChanged(); } } void CartesianCoordinatePlane::setZoomFactorY( qreal factor ) { if ( doneSetZoomFactorY( factor ) ) { d->coordinateTransformation.updateTransform( logicalArea(), drawingArea() ); emit propertiesChanged(); } } void CartesianCoordinatePlane::setZoomCenter( const QPointF& point ) { if ( doneSetZoomCenter( point ) ) { d->coordinateTransformation.updateTransform( logicalArea(), drawingArea() ); emit propertiesChanged(); } } QPointF CartesianCoordinatePlane::zoomCenter() const { return d->coordinateTransformation.zoom.center(); } qreal CartesianCoordinatePlane::zoomFactorX() const { return d->coordinateTransformation.zoom.xFactor; } qreal CartesianCoordinatePlane::zoomFactorY() const { return d->coordinateTransformation.zoom.yFactor; } CartesianCoordinatePlane::AxesCalcMode CartesianCoordinatePlane::axesCalcModeY() const { return d->coordinateTransformation.axesCalcModeY; } CartesianCoordinatePlane::AxesCalcMode CartesianCoordinatePlane::axesCalcModeX() const { return d->coordinateTransformation.axesCalcModeX; } void CartesianCoordinatePlane::setAxesCalcModes( AxesCalcMode mode ) { if ( d->coordinateTransformation.axesCalcModeY != mode || d->coordinateTransformation.axesCalcModeX != mode ) { d->coordinateTransformation.axesCalcModeY = mode; d->coordinateTransformation.axesCalcModeX = mode; emit propertiesChanged(); emit viewportCoordinateSystemChanged(); Q_FOREACH( AbstractDiagram* diag, diagrams() ) slotLayoutChanged( diag ); } } void CartesianCoordinatePlane::setAxesCalcModeY( AxesCalcMode mode ) { if ( d->coordinateTransformation.axesCalcModeY != mode ) { d->coordinateTransformation.axesCalcModeY = mode; emit propertiesChanged(); setGridNeedsRecalculate(); emit viewportCoordinateSystemChanged(); } } void CartesianCoordinatePlane::setAxesCalcModeX( AxesCalcMode mode ) { if ( d->coordinateTransformation.axesCalcModeX != mode ) { d->coordinateTransformation.axesCalcModeX = mode; emit propertiesChanged(); emit viewportCoordinateSystemChanged(); } } namespace { inline bool fuzzyCompare( qreal a, qreal b ) { if ( ISNAN(a) && ISNAN(b) ) return true; if ( qFuzzyIsNull(a) && qFuzzyIsNull(b) ) return true; return qFuzzyCompare( a, b ); } } void CartesianCoordinatePlane::setHorizontalRange( const QPair< qreal, qreal > & range ) { if ( !fuzzyCompare(d->horizontalMin, range.first) || !fuzzyCompare(d->horizontalMax, range.second) ) { d->autoAdjustHorizontalRangeToData = 100; d->horizontalMin = range.first; d->horizontalMax = range.second; layoutDiagrams(); emit propertiesChanged(); emit boundariesChanged(); } } void CartesianCoordinatePlane::setVerticalRange( const QPair< qreal, qreal > & range ) { if ( !fuzzyCompare(d->verticalMin, range.first) || !fuzzyCompare(d->verticalMax, range.second) ) { d->autoAdjustVerticalRangeToData = 100; d->verticalMin = range.first; d->verticalMax = range.second; layoutDiagrams(); emit propertiesChanged(); emit boundariesChanged(); } } QPair< qreal, qreal > CartesianCoordinatePlane::horizontalRange() const { return QPair( d->horizontalMin, d->horizontalMax ); } QPair< qreal, qreal > CartesianCoordinatePlane::verticalRange() const { return QPair( d->verticalMin, d->verticalMax ); } void CartesianCoordinatePlane::adjustRangesToData() { const QRectF dataBoundingRect( getRawDataBoundingRectFromDiagrams() ); d->horizontalMin = dataBoundingRect.left(); d->horizontalMax = dataBoundingRect.right(); d->verticalMin = dataBoundingRect.top(); d->verticalMax = dataBoundingRect.bottom(); layoutDiagrams(); emit propertiesChanged(); } void CartesianCoordinatePlane::adjustHorizontalRangeToData() { const QRectF dataBoundingRect( getRawDataBoundingRectFromDiagrams() ); d->horizontalMin = dataBoundingRect.left(); d->horizontalMax = dataBoundingRect.right(); layoutDiagrams(); emit propertiesChanged(); } void CartesianCoordinatePlane::adjustVerticalRangeToData() { const QRectF dataBoundingRect( getRawDataBoundingRectFromDiagrams() ); d->verticalMin = dataBoundingRect.bottom(); d->verticalMax = dataBoundingRect.top(); layoutDiagrams(); emit propertiesChanged(); } void CartesianCoordinatePlane::setAutoAdjustHorizontalRangeToData( unsigned int percentEmpty ) { if ( d->autoAdjustHorizontalRangeToData != percentEmpty ) { d->autoAdjustHorizontalRangeToData = percentEmpty; d->horizontalMin = 0.0; d->horizontalMax = 0.0; layoutDiagrams(); emit propertiesChanged(); } } void CartesianCoordinatePlane::setAutoAdjustVerticalRangeToData( unsigned int percentEmpty ) { if ( d->autoAdjustVerticalRangeToData != percentEmpty ) { d->autoAdjustVerticalRangeToData = percentEmpty; d->verticalMin = 0.0; d->verticalMax = 0.0; layoutDiagrams(); emit propertiesChanged(); } } unsigned int CartesianCoordinatePlane::autoAdjustHorizontalRangeToData() const { return d->autoAdjustHorizontalRangeToData; } unsigned int CartesianCoordinatePlane::autoAdjustVerticalRangeToData() const { return d->autoAdjustVerticalRangeToData; } void CartesianCoordinatePlane::setGridAttributes( Qt::Orientation orientation, const GridAttributes& a ) { if ( orientation == Qt::Horizontal ) d->gridAttributesHorizontal = a; else d->gridAttributesVertical = a; setHasOwnGridAttributes( orientation, true ); update(); emit propertiesChanged(); } void CartesianCoordinatePlane::resetGridAttributes( Qt::Orientation orientation ) { setHasOwnGridAttributes( orientation, false ); update(); } const GridAttributes CartesianCoordinatePlane::gridAttributes( Qt::Orientation orientation ) const { if ( hasOwnGridAttributes( orientation ) ) { if ( orientation == Qt::Horizontal ) return d->gridAttributesHorizontal; else return d->gridAttributesVertical; } else { return globalGridAttributes(); } } void CartesianCoordinatePlane::setHasOwnGridAttributes( Qt::Orientation orientation, bool on ) { if ( orientation == Qt::Horizontal ) d->hasOwnGridAttributesHorizontal = on; else d->hasOwnGridAttributesVertical = on; emit propertiesChanged(); } bool CartesianCoordinatePlane::hasOwnGridAttributes( Qt::Orientation orientation ) const { return orientation == Qt::Horizontal ? d->hasOwnGridAttributesHorizontal : d->hasOwnGridAttributesVertical; } void CartesianCoordinatePlane::setAutoAdjustGridToZoom( bool autoAdjust ) { if ( d->autoAdjustGridToZoom != autoAdjust ) { d->autoAdjustGridToZoom = autoAdjust; d->grid->setNeedRecalculate(); emit propertiesChanged(); } } #if defined(Q_COMPILER_MANGLES_RETURN_TYPE) const #endif bool CartesianCoordinatePlane::autoAdjustGridToZoom() const { return d->autoAdjustGridToZoom; } AbstractCoordinatePlane* CartesianCoordinatePlane::sharedAxisMasterPlane( QPainter* painter ) { CartesianCoordinatePlane* plane = this; AbstractCartesianDiagram* diag = dynamic_cast< AbstractCartesianDiagram* >( plane->diagram() ); - const CartesianAxis* sharedAxis = 0; - if ( diag != 0 ) + const CartesianAxis* sharedAxis = nullptr; + if ( diag != nullptr ) { const CartesianAxisList axes = diag->axes(); Q_FOREACH( const CartesianAxis* a, axes ) { CartesianCoordinatePlane* p = const_cast< CartesianCoordinatePlane* >( dynamic_cast< const CartesianCoordinatePlane* >( a->coordinatePlane() ) ); - if ( p != 0 && p != this ) + if ( p != nullptr && p != this ) { plane = p; sharedAxis = a; } } } - if ( plane == this || painter == 0 ) + if ( plane == this || painter == nullptr ) return plane; const QPointF zero = QPointF( 0, 0 ); const QPointF tenX = QPointF( 10, 0 ); const QPointF tenY = QPointF( 0, 10 ); if ( sharedAxis->isOrdinate() ) { painter->translate( translate( zero ).x(), 0.0 ); const qreal factor = (translate( tenX ) - translate( zero ) ).x() / ( plane->translate( tenX ) - plane->translate( zero ) ).x(); painter->scale( factor, 1.0 ); painter->translate( -plane->translate( zero ).x(), 0.0 ); } if ( sharedAxis->isAbscissa() ) { painter->translate( 0.0, translate( zero ).y() ); const qreal factor = (translate( tenY ) - translate( zero ) ).y() / ( plane->translate( tenY ) - plane->translate( zero ) ).y(); painter->scale( 1.0, factor ); painter->translate( 0.0, -plane->translate( zero ).y() ); } return plane; } void CartesianCoordinatePlane::setHorizontalRangeReversed( bool reverse ) { if ( d->reverseHorizontalPlane == reverse ) return; d->reverseHorizontalPlane = reverse; layoutDiagrams(); emit propertiesChanged(); } bool CartesianCoordinatePlane::isHorizontalRangeReversed() const { return d->reverseHorizontalPlane; } void CartesianCoordinatePlane::setVerticalRangeReversed( bool reverse ) { if ( d->reverseVerticalPlane == reverse ) return; d->reverseVerticalPlane = reverse; layoutDiagrams(); emit propertiesChanged(); } bool CartesianCoordinatePlane::isVerticalRangeReversed() const { return d->reverseVerticalPlane; } QRectF CartesianCoordinatePlane::visibleDataRange() const { QRectF result; const QRectF drawArea = drawingArea(); result.setTopLeft( translateBack( drawArea.topLeft() ) ); result.setBottomRight( translateBack( drawArea.bottomRight() ) ); return result; } void CartesianCoordinatePlane::setGeometry( const QRect& rectangle ) { if ( rectangle == geometry() ) { return; } d->geometry = rectangle; if ( d->isometricScaling ) { const int hfw = heightForWidth( rectangle.width() ); // same scaling for x and y means a fixed aspect ratio, which is enforced here // always shrink the too large dimension if ( hfw < rectangle.height() ) { d->geometry.setHeight( hfw ); } else { d->geometry.setWidth( qRound( qreal( rectangle.width() ) * qreal( rectangle.height() ) / qreal( hfw ) ) ); } } AbstractCoordinatePlane::setGeometry( d->geometry ); Q_FOREACH( AbstractDiagram* diagram, diagrams() ) { diagram->resize( d->geometry.size() ); } } Qt::Orientations CartesianCoordinatePlane::expandingDirections() const { // not completely sure why this is required for isometric scaling... return d->isometricScaling ? Qt::Horizontal : ( Qt::Horizontal | Qt::Vertical ); } bool CartesianCoordinatePlane::hasHeightForWidth() const { return d->isometricScaling; } int CartesianCoordinatePlane::heightForWidth( int w ) const { // ### using anything for dataRect that depends on geometry will close a feedback loop which // prevents the geometry from stabilizing. specifically, visibleDataRange() depends on // drawingArea(), and no good will come out of using it here. QRectF dataRect = logicalArea(); return qRound( qreal( w ) * qAbs( qreal( dataRect.height() ) / qreal( dataRect.width() ) ) ); } QSize CartesianCoordinatePlane::sizeHint() const { QSize sh = AbstractCoordinatePlane::sizeHint(); if ( d->isometricScaling ) { // not sure why the next line doesn't cause an infinite loop, but it improves initial size allocation sh = d->geometry.size(); sh.setHeight( heightForWidth( sh.width() ) ); } return sh; } diff --git a/src/KChart/Cartesian/KChartCartesianCoordinatePlane.h b/src/KChart/Cartesian/KChartCartesianCoordinatePlane.h index f186dd1..7e0038f 100644 --- a/src/KChart/Cartesian/KChartCartesianCoordinatePlane.h +++ b/src/KChart/Cartesian/KChartCartesianCoordinatePlane.h @@ -1,502 +1,502 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTCARTESIANCOORDINATEPLANE_H #define KCHARTCARTESIANCOORDINATEPLANE_H #include "KChartAbstractCoordinatePlane.h" namespace KChart { class Chart; class PaintContext; class AbstractDiagram; class CartesianAxis; class CartesianGrid; /** * @brief Cartesian coordinate plane */ class KCHART_EXPORT CartesianCoordinatePlane : public AbstractCoordinatePlane { Q_OBJECT Q_DISABLE_COPY( CartesianCoordinatePlane ) KCHART_DECLARE_PRIVATE_DERIVED_PARENT( CartesianCoordinatePlane, Chart* ) friend class CartesianAxis; friend class CartesianGrid; public: - explicit CartesianCoordinatePlane ( Chart* parent = 0 ); + explicit CartesianCoordinatePlane ( Chart* parent = nullptr ); ~CartesianCoordinatePlane(); void addDiagram ( AbstractDiagram* diagram ) Q_DECL_OVERRIDE; /** * If @p onOff is true, enforce that X and Y distances are scaled by the same factor. * This makes the plane's height a function of its width, and hasHeightForWidth() * will return true. */ void setIsometricScaling ( bool onOff ); bool doesIsometricScaling() const; const QPointF translate ( const QPointF& diagramPoint ) const Q_DECL_OVERRIDE; /** * \sa setZoomFactorX, setZoomCenter */ qreal zoomFactorX() const Q_DECL_OVERRIDE; /** * \sa setZoomFactorY, setZoomCenter */ qreal zoomFactorY() const Q_DECL_OVERRIDE; /** * \sa setZoomFactorX,setZoomFactorY */ void setZoomFactors( qreal factorX, qreal factorY ) Q_DECL_OVERRIDE; /** * \sa zoomFactorX, setZoomCenter */ void setZoomFactorX( qreal factor ) Q_DECL_OVERRIDE; /** * \sa zoomFactorY, setZoomCenter */ void setZoomFactorY( qreal factor ) Q_DECL_OVERRIDE; /** * \sa setZoomCenter, setZoomFactorX, setZoomFactorY */ QPointF zoomCenter() const Q_DECL_OVERRIDE; /** * \sa zoomCenter, setZoomFactorX, setZoomFactorY */ void setZoomCenter( const QPointF& center ) Q_DECL_OVERRIDE; /** * Allows to specify a fixed data-space / coordinate-space relation. If set * to true then fixed bar widths are used, so you see more bars as the window * is made wider. * * This allows to completely restrict the size of bars in a graph such that, * upon resizing a window, the graphs coordinate plane will grow (add more * ticks to x- and y-coordinates) rather than have the image grow. */ void setFixedDataCoordinateSpaceRelation( bool fixed ); bool hasFixedDataCoordinateSpaceRelation() const; /** * Allows to fix the lower bound of X axis to zero when diagram is in first quadrant. * * The default behavior is to lower x or y bound to be 0. If this behaviour is not wanted, * either \a CartesianCoordinatePlane::setHorizontalRange could be used instead of letting * KChart auto-adjust the ranges, or this method can be used to disable this behavior. */ void setXAxisStartAtZero(bool fixedStart); bool xAxisStartAtZero() const; /** * \brief Set the boundaries of the visible value space displayed in horizontal direction. * * This is also known as the horizontal viewport. * * By default the horizontal range is adjusted to the range covered by the model's data, * see setAutoAdjustHorizontalRangeToData for details. * Calling setHorizontalRange with a valid range disables this default automatic adjusting, * while on the other hand automatic adjusting will set these ranges. * * To disable use of this range you can either pass an empty pair by using the default * constructor QPair() or you can set both values to the same which constitutes * a null range. * * \note By default the visible data range often is larger than the * range calculated from the data model (or set by setHoriz.|Vert.Range(), resp.). * This is due to the built-in grid calculation feature: The visible start/end * values get adjusted so that they match a main-grid line. * You can turn this feature off for any of the four bounds by calling * GridAttributes::setAdjustBoundsToGrid() for either the global grid-attributes * or for the horizontal/vertical attrs separately. * * \note To set only one of the ends of the range to a fixed value while keeping * the other dynamically adjusted, use std::numeric_limits< qreal >::quiet_NaN() * for the dynamic value. * * \note If you use user defined vertical ranges together with logarithmic scale, only * positive values are supported. If you set it to negative values, the result is undefined. * * \param range a pair of values representing the smalles and the largest * horizontal value space coordinate displayed. * * \sa setAutoAdjustHorizontalRangeToData, setVerticalRange * \sa GridAttributes::setAdjustBoundsToGrid() */ void setHorizontalRange( const QPair & range ); /** * \brief Set the boundaries of the visible value space displayed in vertical direction. * * This is also known as the vertical viewport. * * By default the vertical range is adjusted to the range covered by the model's data, * see setAutoAdjustVerticalRangeToData for details. * Calling setVerticalRange with a valid range disables this default automatic adjusting, * while on the other hand automatic adjusting will set these ranges. * * To disable use of this range you can either pass an empty pair by using the default * constructor QPair() or you can set setting both values to the same which constitutes * a null range. * * \note By default the visible data range often is larger than the * range calculated from the data model (or set by setHoriz.|Vert.Range(), resp.). * This is due to the built-in grid calculation feature: The visible start/end * values get adjusted so that they match a main-grid line. * You can turn this feature off for any of the four bounds by calling * GridAttributes::setAdjustBoundsToGrid() for either the global grid-attributes * or for the horizontal/vertical attrs separately. * * \note To set only one of the ends of the range to a fixed value while keeping * the other dynamically adjusted, use std::numeric_limits< qreal >::quiet_NaN() * for the dynamic value. * * \note If you use user defined vertical ranges together with logarithmic scale, only * positive values are supported. If you set it to negative values, the result is undefined. * * \param range a pair of values representing the smalles and the largest * vertical value space coordinate displayed. * * \sa setAutoAdjustVerticalRangeToData, setHorizontalRange * \sa GridAttributes::setAdjustBoundsToGrid() */ void setVerticalRange( const QPair & range ); /** * @return The largest and smallest visible horizontal value space * value. If this is not explicitly set,or if both values are the same, * the plane will use the union of the dataBoundaries of all * associated diagrams. * \see KChart::AbstractDiagram::dataBoundaries */ QPair horizontalRange() const; /** * @return The largest and smallest visible horizontal value space * value. If this is not explicitly set, or if both values are the same, * the plane will use the union of the dataBoundaries of all * associated diagrams. * \see KChart::AbstractDiagram::dataBoundaries */ QPair verticalRange() const; /** * \brief Automatically adjust horizontal range settings to the ranges covered by * the model's values, when ever the data have changed, and then emit horizontalRangeAutomaticallyAdjusted. * * By default the horizontal range is adjusted automatically, if more than 67 percent of * the available horizontal space would be empty otherwise. * * Range setting is adjusted if more than \c percentEmpty percent of the horizontal * space covered by the coordinate plane would otherwise be empty. * Automatic range adjusting can happen, when either all of the data are positive or all are negative. * * Set percentEmpty to 100 to disable automatic range adjusting. * * \param percentEmpty The maximal percentage of horizontal space that may be empty. * * \sa horizontalRangeAutomaticallyAdjusted * \sa autoAdjustHorizontalRangeToData, adjustRangesToData * \sa setHorizontalRange, setVerticalRange * \sa setAutoAdjustVerticalRangeToData */ void setAutoAdjustHorizontalRangeToData( unsigned int percentEmpty = 67 ); /** * \brief Automatically adjust vertical range settings to the ranges covered by * the model's values, when ever the data have changed, and then emit verticalRangeAutomaticallyAdjusted. * * By default the vertical range is adjusted automatically, if more than 67 percent of * the available vertical space would be empty otherwise. * * Range setting is adjusted if more than \c percentEmpty percent of the horizontal * space covered by the coordinate plane would otherwise be empty. * Automatic range adjusting can happen, when either all of the data are positive or all are negative. * * Set percentEmpty to 100 to disable automatic range adjusting. * * \param percentEmpty The maximal percentage of horizontal space that may be empty. * * \sa verticalRangeAutomaticallyAdjusted * \sa autoAdjustVerticalRangeToData, adjustRangesToData * \sa setHorizontalRange, setVerticalRange * \sa setAutoAdjustHorizontalRangeToData */ void setAutoAdjustVerticalRangeToData( unsigned int percentEmpty = 67 ); /** * \brief Returns the maximal allowed percent of the horizontal * space covered by the coordinate plane that may be empty. * * \return A percent value indicating how much of the horizontal space may be empty. * If more than this is empty, automatic range adjusting is applied. * A return value of 100 indicates that no such automatic adjusting is done at all. * * \sa setAutoAdjustHorizontalRangeToData, adjustRangesToData */ unsigned int autoAdjustHorizontalRangeToData() const; /** * \brief Returns the maximal allowed percent of the vertical * space covered by the coordinate plane that may be empty. * * \return A percent value indicating how much of the vertical space may be empty. * If more than this is empty, automatic range adjusting is applied. * A return value of 100 indicates that no such automatic adjusting is done at all. * * \sa setAutoAdjustVerticalRangeToData, adjustRangesToData */ unsigned int autoAdjustVerticalRangeToData() const; /** * Set the attributes to be used for grid lines drawn in horizontal * direction (or in vertical direction, resp.). * * To disable horizontal grid painting, for example, your code should like this: * \code * GridAttributes ga = plane->gridAttributes( Qt::Horizontal ); * ga.setGridVisible( false ); * plane-setGridAttributes( Qt::Horizontal, ga ); * \endcode * * \note setGridAttributes overwrites the global attributes that * were set by AbstractCoordinatePlane::setGlobalGridAttributes. * To re-activate these global attributes you can call * resetGridAttributes. * * \sa resetGridAttributes, gridAttributes * \sa setAutoAdjustGridToZoom * \sa AbstractCoordinatePlane::setGlobalGridAttributes * \sa hasOwnGridAttributes */ void setGridAttributes( Qt::Orientation orientation, const GridAttributes & ); /** * Reset the attributes to be used for grid lines drawn in horizontal * direction (or in vertical direction, resp.). * By calling this method you specify that the global attributes set by * AbstractCoordinatePlane::setGlobalGridAttributes be used. * * \sa setGridAttributes, gridAttributes * \sa setAutoAdjustGridToZoom * \sa AbstractCoordinatePlane::globalGridAttributes * \sa hasOwnGridAttributes */ void resetGridAttributes( Qt::Orientation orientation ); /** * \return The attributes used for grid lines drawn in horizontal * direction (or in vertical direction, resp.). * * \note This function always returns a valid set of grid attributes: * If no special grid attributes were set foe this orientation * the global attributes are returned, as returned by * AbstractCoordinatePlane::globalGridAttributes. * * \sa setGridAttributes * \sa resetGridAttributes * \sa AbstractCoordinatePlane::globalGridAttributes * \sa hasOwnGridAttributes */ const GridAttributes gridAttributes( Qt::Orientation orientation ) const; /** * \return Returns whether the grid attributes have been set for the * respective direction via setGridAttributes( orientation ). * * If false, the grid will use the global attributes set * by AbstractCoordinatePlane::globalGridAttributes (or the default * attributes, resp.) * * \sa setGridAttributes * \sa resetGridAttributes * \sa AbstractCoordinatePlane::globalGridAttributes */ bool hasOwnGridAttributes( Qt::Orientation orientation ) const; /** * Disable / re-enable the built-in grid adjusting feature. * * By default additional lines will be drawn in a Linear grid when zooming in. * * \sa autoAdjustGridToZoom, setGridAttributes */ void setAutoAdjustGridToZoom( bool autoAdjust ); /** * Return the status of the built-in grid adjusting feature. * * \sa setAutoAdjustGridToZoom */ #if defined(Q_COMPILER_MANGLES_RETURN_TYPE) const bool autoAdjustGridToZoom() const; #else bool autoAdjustGridToZoom() const; #endif AxesCalcMode axesCalcModeY() const; AxesCalcMode axesCalcModeX() const; /** Specifies the calculation modes for all axes */ void setAxesCalcModes( AxesCalcMode mode ); /** Specifies the calculation mode for all Ordinate axes */ void setAxesCalcModeY( AxesCalcMode mode ); /** Specifies the calculation mode for all Abscissa axes */ void setAxesCalcModeX( AxesCalcMode mode ); /** reimpl */ void paint( QPainter* ) Q_DECL_OVERRIDE; /** reimpl */ - AbstractCoordinatePlane* sharedAxisMasterPlane( QPainter* p = 0 ) Q_DECL_OVERRIDE; + AbstractCoordinatePlane* sharedAxisMasterPlane( QPainter* p = nullptr ) Q_DECL_OVERRIDE; /** * Returns the currently visible data range. Might be greater than the * range of the grid. */ QRectF visibleDataRange() const; /** * Returns the logical area, i.e., the rectangle defined by the very top * left and very bottom right coordinate. */ QRectF logicalArea() const; /** * Returns the (physical) area occupied by the diagram. Unless zoom is applied * (which is also true when a fixed data coordinate / space relation is used), * \code diagramArea() == drawingArea() \endcode . * \sa setFixedDataCoordinateSpaceRelation * \sa drawingArea */ QRectF diagramArea() const; /** * Returns the visible part of the diagram area, i.e. * \code diagramArea().intersected( drawingArea() ) \endcode * \sa diagramArea */ QRectF visibleDiagramArea() const; /** * Sets whether the horizontal range should be reversed or not, i.e. * small values to the left and large values to the right (the default) * or vice versa. * \param reverse Whether the horizontal range should be reversed or not */ void setHorizontalRangeReversed( bool reverse ); /** * \return Whether the horizontal range is reversed or not */ bool isHorizontalRangeReversed() const; /** * Sets whether the vertical range should be reversed or not, i.e. * small values at the bottom and large values at the top (the default) * or vice versa. * \param reverse Whether the vertical range should be reversed or not */ void setVerticalRangeReversed( bool reverse ); /** * \return Whether the vertical range is reversed or not */ bool isVerticalRangeReversed() const; /** * reimplemented from AbstractCoordinatePlane */ void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; // reimplemented Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; public Q_SLOTS: /** * \brief Adjust both, horizontal and vertical range settings to the * ranges covered by the model's data values. * * \sa setHorizontalRange, setVerticalRange * \sa adjustHorizontalRangeToData, adjustVerticalRangeToData * \sa setAutoAdjustHorizontalRangeToData, setAutoAdjustVerticalRangeToData */ void adjustRangesToData(); /** * Adjust horizontal range settings to the ranges covered by the model's data values. * \sa adjustRangesToData */ void adjustHorizontalRangeToData(); /** * Adjust vertical range settings to the ranges covered by the model's data values. * \sa adjustRangesToData */ void adjustVerticalRangeToData(); protected: QRectF getRawDataBoundingRectFromDiagrams() const; QRectF adjustedToMaxEmptyInnerPercentage( const QRectF& r, unsigned int percentX, unsigned int percentY ) const; virtual QRectF calculateRawDataBoundingRect() const; DataDimensionsList getDataDimensionsList() const Q_DECL_OVERRIDE; // the whole drawing area, includes diagrams and axes, but maybe smaller // than (width, height): virtual QRectF drawingArea() const; public: const QPointF translateBack( const QPointF& screenPoint ) const; protected: void paintEvent ( QPaintEvent* ); void layoutDiagrams() Q_DECL_OVERRIDE; // the following three return true if the new value is different from the old bool doneSetZoomFactorX( qreal factor ); bool doneSetZoomFactorY( qreal factor ); bool doneSetZoomCenter( const QPointF& center ); void handleFixedDataCoordinateSpaceRelation( const QRectF& geometry ); // reimplemented from QLayoutItem, via AbstractLayoutItem, AbstractArea, AbstractCoordinatePlane bool hasHeightForWidth() const Q_DECL_OVERRIDE; int heightForWidth( int w ) const Q_DECL_OVERRIDE; QSize sizeHint() const Q_DECL_OVERRIDE; protected Q_SLOTS: void slotLayoutChanged( AbstractDiagram* ); private: void setHasOwnGridAttributes( Qt::Orientation orientation, bool on ); }; } #endif diff --git a/src/KChart/Cartesian/KChartCartesianCoordinatePlane_p.h b/src/KChart/Cartesian/KChartCartesianCoordinatePlane_p.h index cae7f78..368ffbc 100644 --- a/src/KChart/Cartesian/KChartCartesianCoordinatePlane_p.h +++ b/src/KChart/Cartesian/KChartCartesianCoordinatePlane_p.h @@ -1,141 +1,141 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTCARTESIANCOORDINATEPLANE_P_H #define KCHARTCARTESIANCOORDINATEPLANE_P_H // // W A R N I N G // ------------- // // This file is not part of the KD Chart API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "KChartAbstractCoordinatePlane_p.h" #include "CartesianCoordinateTransformation.h" #include "KChartCartesianGrid.h" #include "KChartZoomParameters.h" #include "KChartMath_p.h" namespace KChart { /** * \internal */ class Q_DECL_HIDDEN CartesianCoordinatePlane::Private : public AbstractCoordinatePlane::Private { friend class CartesianCoordinatePlane; public: explicit Private(); virtual ~Private() { } void initialize() Q_DECL_OVERRIDE { bPaintIsRunning = false; coordinateTransformation.axesCalcModeX = Linear; coordinateTransformation.axesCalcModeY = Linear; grid = new CartesianGrid(); } static Private *get( CartesianCoordinatePlane *plane ) { return static_cast< Private * >( plane->d_func() ); } bool isVisiblePoint( const AbstractCoordinatePlane * plane, const QPointF& point ) const Q_DECL_OVERRIDE { QPointF p = point; const CartesianCoordinatePlane* const ref = qobject_cast< const CartesianCoordinatePlane* >( const_cast< AbstractCoordinatePlane* >( plane )->sharedAxisMasterPlane() ); const CartesianCoordinatePlane* const cartPlane = dynamic_cast< const CartesianCoordinatePlane* >( plane ); - if ( ref != 0 && ref != cartPlane ) { + if ( ref != nullptr && ref != cartPlane ) { const QPointF logical = ref->translateBack( point ) - cartPlane->visibleDataRange().topLeft() + ref->visibleDataRange().topLeft(); p = ref->translate( logical ); } const QRectF geo( plane->geometry() ); return geo.contains( p ); } // the coordinate plane will calculate the coordinate transformation: CoordinateTransformation coordinateTransformation; bool bPaintIsRunning; // true after setGridAttributes( Qt::Orientation ) was used, // false if resetGridAttributes( Qt::Orientation ) was called bool hasOwnGridAttributesHorizontal; bool hasOwnGridAttributesVertical; // true after the first resize event came in // bool initialResizeEventReceived; // true if the coordinate plane scales isometrically // (same scaling ratio from data to screen space for both axes) bool isometricScaling; GridAttributes gridAttributesHorizontal; GridAttributes gridAttributesVertical; qreal horizontalMin; qreal horizontalMax; qreal verticalMin; qreal verticalMax; // autoAdjustHorizontalRangeToData determines if and how much the horizontal range is adjusted. // A value of 100 means that the fixed horizontal range will be used (e.g. set by the user), // otherwise the value will be the percentage of the diagram's horizontal range that is to be // left empty (i.e., it resembles the 'gap' between the horizontal extrema and the border of the // diagram). unsigned int autoAdjustHorizontalRangeToData; // autoAdjustVerticalRangeToData determines if and how much the vertical range is adjusted. // A value of 100 means that the fixed vertical range will be used (e.g. set by the user), // otherwise the value will be the percentage of the diagram's vertical range that is to be // left empty (i.e., it resembles the 'gap' between the vertical extrema and the border of the // diagram). unsigned int autoAdjustVerticalRangeToData; bool autoAdjustGridToZoom; bool fixedDataCoordinateSpaceRelation; bool xAxisStartAtZero; QSizeF fixedDataCoordinateSpaceRelationPinnedSize; ZoomParameters fixedDataCoordinateSpaceRelationPinnedZoom; DataDimensionsList dimensions; bool reverseVerticalPlane; bool reverseHorizontalPlane; }; KCHART_IMPL_DERIVED_PLANE(CartesianCoordinatePlane, AbstractCoordinatePlane) } #endif /* KCHARTBARDIAGRAM_P_H */ diff --git a/src/KChart/Cartesian/KChartCartesianDiagramDataCompressor_p.cpp b/src/KChart/Cartesian/KChartCartesianDiagramDataCompressor_p.cpp index 2644e12..51d69a2 100644 --- a/src/KChart/Cartesian/KChartCartesianDiagramDataCompressor_p.cpp +++ b/src/KChart/Cartesian/KChartCartesianDiagramDataCompressor_p.cpp @@ -1,660 +1,660 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartCartesianDiagramDataCompressor_p.h" #include #include #include "KChartAbstractCartesianDiagram.h" #include "KChartMath_p.h" using namespace KChart; using namespace std; CartesianDiagramDataCompressor::CartesianDiagramDataCompressor( QObject* parent ) : QObject( parent ) , m_mode( Precise ) , m_xResolution( 0 ) , m_yResolution( 0 ) , m_sampleStep( 0 ) , m_datasetDimension( 1 ) { calculateSampleStepWidth(); m_data.resize( 0 ); } static bool contains( const CartesianDiagramDataCompressor::AggregatedDataValueAttributes& aggregated, const DataValueAttributes& attributes ) { CartesianDiagramDataCompressor::AggregatedDataValueAttributes::const_iterator it = aggregated.constBegin(); for ( ; it != aggregated.constEnd(); ++it ) { if ( it.value() == attributes ) { return true; } } return false; } CartesianDiagramDataCompressor::AggregatedDataValueAttributes CartesianDiagramDataCompressor::aggregatedAttrs( const AbstractDiagram* diagram, const QModelIndex & index, const CachePosition& position ) const { // return cached attrs, if any DataValueAttributesCache::const_iterator i = m_dataValueAttributesCache.constFind( position ); if ( i != m_dataValueAttributesCache.constEnd() ) { return i.value(); } // aggregate attributes from all indices in the same CachePosition as index CartesianDiagramDataCompressor::AggregatedDataValueAttributes aggregated; Q_FOREACH( const QModelIndex& neighborIndex, mapToModel( position ) ) { DataValueAttributes attrs = diagram->dataValueAttributes( neighborIndex ); // only store visible and unique attributes if ( !attrs.isVisible() ) { continue; } if ( !contains( aggregated, attrs ) ) { aggregated[ neighborIndex ] = attrs; } } // if none of the attributes had the visible flag set, we just take the one set for the index // to avoid returning an empty list (### why not return an empty list?) if ( aggregated.isEmpty() ) { aggregated[index] = diagram->dataValueAttributes( index ); } m_dataValueAttributesCache[position] = aggregated; return aggregated; } bool CartesianDiagramDataCompressor::prepareDataChange( const QModelIndex& parent, bool isRows, int* start, int* end ) { if ( parent != m_rootIndex ) { return false; } Q_ASSERT( *start <= *end ); CachePosition startPos = isRows ? mapToCache( *start, 0 ) : mapToCache( 0, *start ); CachePosition endPos = isRows ? mapToCache( *end, 0 ) : mapToCache( 0, *end ); static const CachePosition nullPosition; if ( startPos == nullPosition ) { rebuildCache(); startPos = isRows ? mapToCache( *start, 0 ) : mapToCache( 0, *start ); endPos = isRows ? mapToCache( *end, 0 ) : mapToCache( 0, *end ); // The start position still isn't valid, // means that no resolution was set yet or we're about to add the first rows if ( startPos == nullPosition ) { return false; } } *start = isRows ? startPos.row : startPos.column; *end = isRows ? endPos.row : endPos.column; return true; } void CartesianDiagramDataCompressor::slotRowsAboutToBeInserted( const QModelIndex& parent, int start, int end ) { if ( !prepareDataChange( parent, true, &start, &end ) ) { return; } for ( int i = 0; i < m_data.size(); ++i ) { Q_ASSERT( start >= 0 && start <= m_data[ i ].size() ); m_data[ i ].insert( start, end - start + 1, DataPoint() ); } } void CartesianDiagramDataCompressor::slotRowsInserted( const QModelIndex& parent, int start, int end ) { if ( !prepareDataChange( parent, true, &start, &end ) ) { return; } for ( int i = 0; i < m_data.size(); ++i ) { for ( int j = start; j < m_data[i].size(); ++j ) { retrieveModelData( CachePosition( j, i ) ); } } } void CartesianDiagramDataCompressor::slotColumnsAboutToBeInserted( const QModelIndex& parent, int start, int end ) { if ( !prepareDataChange( parent, false, &start, &end ) ) { return; } const int rowCount = qMin( m_model ? m_model->rowCount( m_rootIndex ) : 0, m_xResolution ); Q_ASSERT( start >= 0 && start <= m_data.size() ); m_data.insert( start, end - start + 1, QVector< DataPoint >( rowCount ) ); } void CartesianDiagramDataCompressor::slotColumnsInserted( const QModelIndex& parent, int start, int end ) { if ( !prepareDataChange( parent, false, &start, &end ) ) { return; } for ( int i = start; i < m_data.size(); ++i ) { for (int j = 0; j < m_data[i].size(); ++j ) { retrieveModelData( CachePosition( j, i ) ); } } } void CartesianDiagramDataCompressor::slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end ) { if ( !prepareDataChange( parent, true, &start, &end ) ) { return; } for ( int i = 0; i < m_data.size(); ++i ) { m_data[ i ].remove( start, end - start + 1 ); } } void CartesianDiagramDataCompressor::slotRowsRemoved( const QModelIndex& parent, int start, int end ) { if ( parent != m_rootIndex ) return; Q_ASSERT( start <= end ); Q_UNUSED( end ) CachePosition startPos = mapToCache( start, 0 ); static const CachePosition nullPosition; if ( startPos == nullPosition ) { // Since we should already have rebuilt the cache, it won't help to rebuild it again. // Do not Q_ASSERT() though, since the resolution might simply not be set or we might now have 0 rows return; } for ( int i = 0; i < m_data.size(); ++i ) { for (int j = startPos.row; j < m_data[i].size(); ++j ) { retrieveModelData( CachePosition( j, i ) ); } } } void CartesianDiagramDataCompressor::slotColumnsAboutToBeRemoved( const QModelIndex& parent, int start, int end ) { if ( !prepareDataChange( parent, false, &start, &end ) ) { return; } m_data.remove( start, end - start + 1 ); } void CartesianDiagramDataCompressor::slotColumnsRemoved( const QModelIndex& parent, int start, int end ) { if ( parent != m_rootIndex ) return; Q_ASSERT( start <= end ); Q_UNUSED( end ); const CachePosition startPos = mapToCache( 0, start ); static const CachePosition nullPosition; if ( startPos == nullPosition ) { // Since we should already have rebuilt the cache, it won't help to rebuild it again. // Do not Q_ASSERT() though, since the resolution might simply not be set or we might now have 0 columns return; } for ( int i = startPos.column; i < m_data.size(); ++i ) { for ( int j = 0; j < m_data[i].size(); ++j ) { retrieveModelData( CachePosition( j, i ) ); } } } void CartesianDiagramDataCompressor::slotModelHeaderDataChanged( Qt::Orientation orientation, int first, int last ) { if ( orientation != Qt::Vertical ) return; if ( m_model->rowCount( m_rootIndex ) > 0 ) { const QModelIndex firstRow = m_model->index( 0, first, m_rootIndex ); // checked const QModelIndex lastRow = m_model->index( m_model->rowCount( m_rootIndex ) - 1, last, m_rootIndex ); // checked slotModelDataChanged( firstRow, lastRow ); } } void CartesianDiagramDataCompressor::slotModelDataChanged( const QModelIndex& topLeftIndex, const QModelIndex& bottomRightIndex ) { if ( topLeftIndex.parent() != m_rootIndex ) return; Q_ASSERT( topLeftIndex.parent() == bottomRightIndex.parent() ); Q_ASSERT( topLeftIndex.row() <= bottomRightIndex.row() ); Q_ASSERT( topLeftIndex.column() <= bottomRightIndex.column() ); CachePosition topleft = mapToCache( topLeftIndex ); CachePosition bottomright = mapToCache( bottomRightIndex ); for ( int row = topleft.row; row <= bottomright.row; ++row ) for ( int column = topleft.column; column <= bottomright.column; ++column ) invalidate( CachePosition( row, column ) ); } void CartesianDiagramDataCompressor::slotModelLayoutChanged() { rebuildCache(); calculateSampleStepWidth(); } void CartesianDiagramDataCompressor::slotDiagramLayoutChanged( AbstractDiagram* diagramBase ) { AbstractCartesianDiagram* diagram = qobject_cast< AbstractCartesianDiagram* >( diagramBase ); Q_ASSERT( diagram ); if ( diagram->datasetDimension() != m_datasetDimension ) { setDatasetDimension( diagram->datasetDimension() ); } } int CartesianDiagramDataCompressor::modelDataColumns() const { Q_ASSERT( m_datasetDimension != 0 ); // only operational if there is a model and a resolution if ( m_model ) { const int effectiveDimension = m_datasetDimension == 2 ? 2 : 1; const int columns = m_model->columnCount( m_rootIndex ) / effectiveDimension; Q_ASSERT( columns == m_data.size() ); return columns; } else { return 0; } } int CartesianDiagramDataCompressor::modelDataRows() const { // only operational if there is a model, columns, and a resolution if ( m_model && m_model->columnCount( m_rootIndex ) > 0 && m_xResolution > 0 ) { return m_data.isEmpty() ? 0 : m_data.first().size(); } else { return 0; } } void CartesianDiagramDataCompressor::setModel( QAbstractItemModel* model ) { if ( model == m_model ) { return; } - if ( m_model != 0 ) { + if ( m_model != nullptr ) { disconnect( m_model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), this, SLOT(slotModelHeaderDataChanged(Qt::Orientation,int,int)) ); disconnect( m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(slotModelDataChanged(QModelIndex,QModelIndex)) ); disconnect( m_model, SIGNAL(layoutChanged()), this, SLOT(slotModelLayoutChanged()) ); disconnect( m_model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(slotRowsAboutToBeInserted(QModelIndex,int,int)) ); disconnect( m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(slotRowsInserted(QModelIndex,int,int)) ); disconnect( m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)) ); disconnect( m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(slotRowsRemoved(QModelIndex,int,int)) ); disconnect( m_model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(slotColumnsAboutToBeInserted(QModelIndex,int,int)) ); disconnect( m_model, SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(slotColumnsInserted(QModelIndex,int,int)) ); disconnect( m_model, SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(slotColumnsRemoved(QModelIndex,int,int)) ); disconnect( m_model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(slotColumnsAboutToBeRemoved(QModelIndex,int,int)) ); disconnect( m_model, SIGNAL(modelReset()), this, SLOT(rebuildCache()) ); - m_model = 0; + m_model = nullptr; } m_modelCache.setModel( model ); - if ( model != 0 ) { + if ( model != nullptr ) { m_model = model; connect( m_model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), SLOT(slotModelHeaderDataChanged(Qt::Orientation,int,int)) ); connect( m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), SLOT(slotModelDataChanged(QModelIndex,QModelIndex)) ); connect( m_model, SIGNAL(layoutChanged()), SLOT(slotModelLayoutChanged()) ); connect( m_model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), SLOT(slotRowsAboutToBeInserted(QModelIndex,int,int)) ); connect( m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(slotRowsInserted(QModelIndex,int,int)) ); connect( m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)) ); connect( m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(slotRowsRemoved(QModelIndex,int,int)) ); connect( m_model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), SLOT(slotColumnsAboutToBeInserted(QModelIndex,int,int)) ); connect( m_model, SIGNAL(columnsInserted(QModelIndex,int,int)), SLOT(slotColumnsInserted(QModelIndex,int,int)) ); connect( m_model, SIGNAL(columnsRemoved(QModelIndex,int,int)), SLOT(slotColumnsRemoved(QModelIndex,int,int)) ); connect( m_model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), SLOT(slotColumnsAboutToBeRemoved(QModelIndex,int,int)) ); connect( m_model, SIGNAL(modelReset()), SLOT(rebuildCache()) ); } rebuildCache(); calculateSampleStepWidth(); } void CartesianDiagramDataCompressor::setRootIndex( const QModelIndex& root ) { if ( m_rootIndex != root ) { Q_ASSERT( root.model() == m_model || !root.isValid() ); m_rootIndex = root; m_modelCache.setRootIndex( root ); rebuildCache(); calculateSampleStepWidth(); } } void CartesianDiagramDataCompressor::recalcResolution() { setResolution( m_xResolution, m_yResolution ); } void CartesianDiagramDataCompressor::setResolution( int x, int y ) { if ( setResolutionInternal( x, y ) ) { rebuildCache(); calculateSampleStepWidth(); } } bool CartesianDiagramDataCompressor::setResolutionInternal( int x, int y ) { const int oldXRes = m_xResolution; const int oldYRes = m_yResolution; if ( m_datasetDimension != 1 ) { // just ignore the X resolution in that case m_xResolution = m_model ? m_model->rowCount( m_rootIndex ) : 0; } else { m_xResolution = qMax( 0, x ); } m_yResolution = qMax( 0, y ); return m_xResolution != oldXRes || m_yResolution != oldYRes; } void CartesianDiagramDataCompressor::clearCache() { for ( int column = 0; column < m_data.size(); ++column ) m_data[column].fill( DataPoint() ); } void CartesianDiagramDataCompressor::rebuildCache() { Q_ASSERT( m_datasetDimension != 0 ); m_data.clear(); setResolutionInternal( m_xResolution, m_yResolution ); const int columnDivisor = m_datasetDimension == 2 ? 2 : 1; const int columnCount = m_model ? m_model->columnCount( m_rootIndex ) / columnDivisor : 0; const int rowCount = qMin( m_model ? m_model->rowCount( m_rootIndex ) : 0, m_xResolution ); m_data.resize( columnCount ); for ( int i = 0; i < columnCount; ++i ) { m_data[i].resize( rowCount ); } // also empty the attrs cache m_dataValueAttributesCache.clear(); } const CartesianDiagramDataCompressor::DataPoint& CartesianDiagramDataCompressor::data( const CachePosition& position ) const { static DataPoint nullDataPoint; if ( ! mapsToModelIndex( position ) ) { return nullDataPoint; } if ( ! isCached( position ) ) { retrieveModelData( position ); } return m_data.at( position.column ).at( position.row ); } QPair< QPointF, QPointF > CartesianDiagramDataCompressor::dataBoundaries() const { const int colCount = modelDataColumns(); qreal xMin = std::numeric_limits< qreal >::quiet_NaN(); qreal xMax = std::numeric_limits< qreal >::quiet_NaN(); qreal yMin = std::numeric_limits< qreal >::quiet_NaN(); qreal yMax = std::numeric_limits< qreal >::quiet_NaN(); for ( int column = 0; column < colCount; ++column ) { const DataPointVector& data = m_data.at( column ); int row = 0; for ( DataPointVector::const_iterator it = data.begin(); it != data.end(); ++it, ++row ) { const DataPoint& p = *it; if ( !p.index.isValid() ) retrieveModelData( CachePosition( row, column ) ); if ( ISNAN( p.key ) || ISNAN( p.value ) ) { continue; } if ( ISNAN( xMin ) ) { xMin = p.key; xMax = p.key; yMin = p.value; yMax = p.value; } else { xMin = qMin( xMin, p.key ); xMax = qMax( xMax, p.key ); yMin = qMin( yMin, p.value ); yMax = qMax( yMax, p.value ); } } } const QPointF bottomLeft( xMin, yMin ); const QPointF topRight( xMax, yMax ); return qMakePair( bottomLeft, topRight ); } void CartesianDiagramDataCompressor::retrieveModelData( const CachePosition& position ) const { Q_ASSERT( mapsToModelIndex( position ) ); DataPoint result; result.hidden = true; switch ( m_mode ) { case Precise: { const QModelIndexList indexes = mapToModel( position ); if ( m_datasetDimension == 2 ) { Q_ASSERT( indexes.count() == 2 ); const QModelIndex& xIndex = indexes.at( 0 ); result.index = xIndex; result.key = m_modelCache.data( xIndex ); result.value = m_modelCache.data( indexes.at( 1 ) ); } else { if ( indexes.isEmpty() ) { break; } result.value = std::numeric_limits< qreal >::quiet_NaN(); result.key = 0.0; Q_FOREACH( const QModelIndex& index, indexes ) { const qreal value = m_modelCache.data( index ); if ( !ISNAN( value ) ) { result.value = ISNAN( result.value ) ? value : result.value + value; } result.key += index.row(); } result.index = indexes.at( 0 ); result.key /= indexes.size(); result.value /= indexes.size(); } Q_FOREACH( const QModelIndex& index, indexes ) { // the DataPoint point is visible if any of the underlying, aggregated points is visible if ( m_model->data( index, DataHiddenRole ).value() == false ) { result.hidden = false; } } break; } case SamplingSeven: break; } m_data[ position.column ][ position.row ] = result; Q_ASSERT( isCached( position ) ); } CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache( const QModelIndex& index ) const { Q_ASSERT( m_datasetDimension != 0 ); static const CachePosition nullPosition; if ( !index.isValid() ) { return nullPosition; } return mapToCache( index.row(), index.column() ); } CartesianDiagramDataCompressor::CachePosition CartesianDiagramDataCompressor::mapToCache( int row, int column ) const { Q_ASSERT( m_datasetDimension != 0 ); if ( m_data.size() == 0 || m_data.at( 0 ).size() == 0 ) { return mapToCache( QModelIndex() ); } // assumption: indexes per column == 1 if ( indexesPerPixel() == 0 ) { return mapToCache( QModelIndex() ); } return CachePosition( int( row / indexesPerPixel() ), column / m_datasetDimension ); } QModelIndexList CartesianDiagramDataCompressor::mapToModel( const CachePosition& position ) const { QModelIndexList indexes; if ( !mapsToModelIndex( position ) ) { return indexes; } Q_ASSERT( position.column < modelDataColumns() ); if ( m_datasetDimension == 2 ) { indexes << m_model->index( position.row, position.column * 2, m_rootIndex ); // checked indexes << m_model->index( position.row, position.column * 2 + 1, m_rootIndex ); // checked } else { // here, indexes per column is usually but not always 1 (e.g. stock diagrams can have three // or four dimensions: High-Low-Close or Open-High-Low-Close) const qreal ipp = indexesPerPixel(); const int baseRow = floor( position.row * ipp ); // the following line needs to work for the last row(s), too... const int endRow = floor( ( position.row + 1 ) * ipp ); for ( int row = baseRow; row < endRow; ++row ) { Q_ASSERT( row < m_model->rowCount( m_rootIndex ) ); const QModelIndex index = m_model->index( row, position.column, m_rootIndex ); if ( index.isValid() ) { indexes << index; } } } return indexes; } qreal CartesianDiagramDataCompressor::indexesPerPixel() const { if ( !m_model || m_data.size() == 0 || m_data.at( 0 ).size() == 0 ) { return 0; } return qreal( m_model->rowCount( m_rootIndex ) ) / qreal( m_data.at( 0 ).size() ); } bool CartesianDiagramDataCompressor::mapsToModelIndex( const CachePosition& position ) const { return m_model && m_data.size() > 0 && m_data.at( 0 ).size() > 0 && position.column >= 0 && position.column < m_data.size() && position.row >=0 && position.row < m_data.at( 0 ).size(); } void CartesianDiagramDataCompressor::invalidate( const CachePosition& position ) { if ( mapsToModelIndex( position ) ) { m_data[ position.column ][ position.row ] = DataPoint(); // Also invalidate the data value attributes at "position". // Otherwise the user overwrites the attributes without us noticing // it because we keep reading what's in the cache. m_dataValueAttributesCache.remove( position ); } } bool CartesianDiagramDataCompressor::isCached( const CachePosition& position ) const { Q_ASSERT( mapsToModelIndex( position ) ); const DataPoint& p = m_data.at( position.column ).at( position.row ); return p.index.isValid(); } void CartesianDiagramDataCompressor::calculateSampleStepWidth() { if ( m_mode == Precise ) { m_sampleStep = 1; return; } static unsigned int SomePrimes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 151, 211, 313, 401, 503, 607, 701, 811, 911, 1009, 10037, 12911, 16001, 20011, 50021, 100003, 137867, 199999, 500009, 707753, 1000003, 0 }; // ... after that, having a model at all becomes impractical // we want at least 17 samples per data point, using a prime step width const qreal WantedSamples = 17; if ( WantedSamples > indexesPerPixel() ) { m_sampleStep = 1; } else { int i; for ( i = 0; SomePrimes[i] != 0; ++i ) { if ( WantedSamples * SomePrimes[i+1] > indexesPerPixel() ) { break; } } m_sampleStep = SomePrimes[i]; if ( SomePrimes[i] == 0 ) { m_sampleStep = SomePrimes[i-1]; } else { m_sampleStep = SomePrimes[i]; } } } void CartesianDiagramDataCompressor::setDatasetDimension( int dimension ) { if ( dimension != m_datasetDimension ) { m_datasetDimension = dimension; rebuildCache(); calculateSampleStepWidth(); } } diff --git a/src/KChart/Cartesian/KChartCartesianDiagramDataCompressor_p.h b/src/KChart/Cartesian/KChartCartesianDiagramDataCompressor_p.h index 429dca2..31d76b4 100644 --- a/src/KChart/Cartesian/KChartCartesianDiagramDataCompressor_p.h +++ b/src/KChart/Cartesian/KChartCartesianDiagramDataCompressor_p.h @@ -1,216 +1,216 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTCARTESIANDIAGRAMDATACOMPRESSOR_H #define KCHARTCARTESIANDIAGRAMDATACOMPRESSOR_H // // W A R N I N G // ------------- // // This file is not part of the KD Chart API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include #include "KChartDataValueAttributes.h" #include "KChartModelDataCache_p.h" #include "kchart_export.h" class CartesianDiagramDataCompressorTests; QT_BEGIN_NAMESPACE class QAbstractItemModel; QT_END_NAMESPACE namespace KChart { class AbstractDiagram; // - transparently compress table model data if the diagram widget // size does not allow to display all data points in an acceptable way // - the class acts much like a proxy model, but is not // implemented as one, to avoid performance penalties by QVariant // conversions // - a wanted side effect is that the compressor will deliver // more precise values for more precise media, like paper // (a) this is absolutely strictly seriously private API of KChart // (b) if possible, this class is going to be templatized for // different diagram types // KCHART_EXPORT is needed as long there's a test using // this class directly class KCHART_EXPORT CartesianDiagramDataCompressor : public QObject { Q_OBJECT friend class ::CartesianDiagramDataCompressorTests; public: class DataPoint { public: DataPoint() : key( std::numeric_limits< qreal >::quiet_NaN() ), value( std::numeric_limits< qreal >::quiet_NaN() ), hidden( false ) {} qreal key; qreal value; bool hidden; QModelIndex index; }; typedef QVector DataPointVector; class CachePosition { public: CachePosition() : row( -1 ), column( -1 ) {} CachePosition( int row, int column ) : row( row ), column( column ) {} int row; int column; bool operator==( const CachePosition& rhs ) const { return row == rhs.row && column == rhs.column; } bool operator<( const CachePosition& rhs ) const { // This function is used to topologically sort all cache positions. // Think of them as entries in a matrix or table: // An entry comes before another entry if it is either above the other // entry, or in the same row and to the left of the other entry. return row < rhs.row || ( row == rhs.row && column < rhs.column ); } }; typedef QMap< QModelIndex, DataValueAttributes > AggregatedDataValueAttributes; typedef QMap< CartesianDiagramDataCompressor::CachePosition, AggregatedDataValueAttributes > DataValueAttributesCache; enum ApproximationMode { // do not approximate, interpolate by averaging all // datapoints for a pixel Precise, // approximate by averaging out over prime number distances SamplingSeven }; - explicit CartesianDiagramDataCompressor( QObject* parent = 0 ); + explicit CartesianDiagramDataCompressor( QObject* parent = nullptr ); // input: model, chart resolution, approximation mode void setModel( QAbstractItemModel* ); void setRootIndex( const QModelIndex& root ); void setResolution( int x, int y ); void recalcResolution(); void setApproximationMode( ApproximationMode mode ); void setDatasetDimension( int dimension ); // output: resulting model resolution, data points // FIXME (Mirko) rather stupid naming, Mirko! int modelDataColumns() const; int modelDataRows() const; const DataPoint& data( const CachePosition& ) const; QPair< QPointF, QPointF > dataBoundaries() const; AggregatedDataValueAttributes aggregatedAttrs( const AbstractDiagram* diagram, const QModelIndex & index, const CachePosition& position ) const; private Q_SLOTS: void slotRowsAboutToBeInserted( const QModelIndex&, int, int ); void slotRowsInserted( const QModelIndex&, int, int ); void slotRowsAboutToBeRemoved( const QModelIndex&, int, int ); void slotRowsRemoved( const QModelIndex&, int, int ); void slotColumnsAboutToBeInserted( const QModelIndex&, int, int ); void slotColumnsInserted( const QModelIndex&, int, int ); void slotColumnsAboutToBeRemoved( const QModelIndex&, int, int ); void slotColumnsRemoved( const QModelIndex&, int, int ); void slotModelHeaderDataChanged( Qt::Orientation, int, int ); void slotModelDataChanged( const QModelIndex&, const QModelIndex& ); void slotModelLayoutChanged(); // FIXME resolution changes and root index changes should all // be catchable with this method: void slotDiagramLayoutChanged( AbstractDiagram* ); // geometry has changed void rebuildCache(); // reset all cached values, without changing the cache geometry void clearCache(); private: // private version of setResolution() that does *not* call rebuildCache() bool setResolutionInternal( int x, int y ); // forget cached data at the position void invalidate( const CachePosition& ); // check if position is inside the dataset's index range bool mapsToModelIndex( const CachePosition& ) const; CachePosition mapToCache( const QModelIndex& ) const; CachePosition mapToCache( int row, int column ) const; // Note: returns only valid model indices QModelIndexList mapToModel( const CachePosition& ) const; qreal indexesPerPixel() const; // common logic for slot{Rows,Columns}[AboutToBe]{Inserted,Removed} bool prepareDataChange( const QModelIndex& parent, bool isRows, /* columns otherwise */ int* start, int* end); // retrieve data from the model, put it into the cache void retrieveModelData( const CachePosition& ) const; // check if a data point is in the cache: bool isCached( const CachePosition& ) const; // set sample step width according to settings: void calculateSampleStepWidth(); QPointer m_model; QModelIndex m_rootIndex; ApproximationMode m_mode; int m_xResolution; int m_yResolution; unsigned int m_sampleStep; mutable QVector m_data; // one per dataset ModelDataCache< qreal, Qt::DisplayRole > m_modelCache; mutable DataValueAttributesCache m_dataValueAttributesCache; int m_datasetDimension; }; } #endif diff --git a/src/KChart/Cartesian/KChartLeveyJenningsAxis.h b/src/KChart/Cartesian/KChartLeveyJenningsAxis.h index 5a06186..911557a 100644 --- a/src/KChart/Cartesian/KChartLeveyJenningsAxis.h +++ b/src/KChart/Cartesian/KChartLeveyJenningsAxis.h @@ -1,85 +1,85 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTLEVEYJENNINGSAXIS_H #define KCHARTLEVEYJENNINGSAXIS_H #include #include "KChartCartesianAxis.h" #include "KChartLeveyJenningsGridAttributes.h" namespace KChart { class LeveyJenningsDiagram; /** * The class for levey jennings axes. * * For being useful, axes need to be assigned to a diagram, see * LeveyJenningsDiagram::addAxis and LeveyJenningsDiagram::takeAxis. * * \sa PolarAxis, AbstractCartesianDiagram */ class KCHART_EXPORT LeveyJenningsAxis : public CartesianAxis { Q_OBJECT Q_DISABLE_COPY( LeveyJenningsAxis ) KCHART_DECLARE_PRIVATE_DERIVED_PARENT( LeveyJenningsAxis, AbstractDiagram* ) public: /** * C'tor of the class for levey jennings axes. * * \note If using a zero parent for the constructor, you need to call * your diagram's addAxis function to add your axis to the diagram. * Otherwise, there is no need to call addAxis, since the constructor * does that automatically for you, if you pass a diagram as parameter. * * \sa AbstractCartesianDiagram::addAxis */ - explicit LeveyJenningsAxis ( LeveyJenningsDiagram* diagram = 0 ); + explicit LeveyJenningsAxis ( LeveyJenningsDiagram* diagram = nullptr ); ~LeveyJenningsAxis(); LeveyJenningsGridAttributes::GridType type() const; void setType( LeveyJenningsGridAttributes::GridType type ); Qt::DateFormat dateFormat() const; void setDateFormat( Qt::DateFormat format ); /** * Returns true if both axes have the same settings. */ bool compare( const LeveyJenningsAxis* other ) const; /** reimpl */ void paintCtx( PaintContext* ) Q_DECL_OVERRIDE; protected: virtual void paintAsOrdinate( PaintContext* ); virtual void paintAsAbscissa( PaintContext* ); }; typedef QList LeveyJenningsAxisList; } #endif diff --git a/src/KChart/Cartesian/KChartLeveyJenningsCoordinatePlane.h b/src/KChart/Cartesian/KChartLeveyJenningsCoordinatePlane.h index 6d33787..5322679 100644 --- a/src/KChart/Cartesian/KChartLeveyJenningsCoordinatePlane.h +++ b/src/KChart/Cartesian/KChartLeveyJenningsCoordinatePlane.h @@ -1,64 +1,64 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTLEVEYJENNINGSCOORDINATEPLANE_H #define KCHARTLEVEYJENNINGSCOORDINATEPLANE_H #include "KChartCartesianCoordinatePlane.h" #include "KChartLeveyJenningsGridAttributes.h" namespace KChart { class LeveyJenningsGrid; /** * @brief Levey Jennings coordinate plane * This is actually nothing real more than a plain cartesian * coordinate plane. The difference is, that only Levey Jennings * Diagrams can be added to it. */ class KCHART_EXPORT LeveyJenningsCoordinatePlane : public CartesianCoordinatePlane { Q_OBJECT Q_DISABLE_COPY( LeveyJenningsCoordinatePlane ) KCHART_DECLARE_PRIVATE_DERIVED_PARENT( LeveyJenningsCoordinatePlane, Chart* ) friend class LeveyJenningsGrid; public: - explicit LeveyJenningsCoordinatePlane( Chart* parent = 0 ); + explicit LeveyJenningsCoordinatePlane( Chart* parent = nullptr ); ~LeveyJenningsCoordinatePlane(); void addDiagram( AbstractDiagram* diagram ) Q_DECL_OVERRIDE; LeveyJenningsGridAttributes gridAttributes() const; void setGridAttributes( const LeveyJenningsGridAttributes& attr ); protected: const QPointF translateBack( const QPointF& screenPoint ) const; private: LeveyJenningsGrid* grid() const; }; } #endif diff --git a/src/KChart/Cartesian/KChartLeveyJenningsDiagram.cpp b/src/KChart/Cartesian/KChartLeveyJenningsDiagram.cpp index a481d29..35b360d 100644 --- a/src/KChart/Cartesian/KChartLeveyJenningsDiagram.cpp +++ b/src/KChart/Cartesian/KChartLeveyJenningsDiagram.cpp @@ -1,732 +1,732 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartLeveyJenningsDiagram.h" #include "KChartLeveyJenningsDiagram_p.h" #include "KChartChart.h" #include "KChartTextAttributes.h" #include "KChartAbstractGrid.h" #include "KChartPainterSaver_p.h" #include #include #include #include #include using namespace KChart; using namespace std; LeveyJenningsDiagram::Private::Private() { } LeveyJenningsDiagram::Private::~Private() {} #define d d_func() LeveyJenningsDiagram::LeveyJenningsDiagram( QWidget* parent, LeveyJenningsCoordinatePlane* plane ) : LineDiagram( new Private(), parent, plane ) { init(); } void LeveyJenningsDiagram::init() { d->lotChangedPosition = Qt::AlignTop; d->fluidicsPackChangedPosition = Qt::AlignBottom; d->sensorChangedPosition = Qt::AlignBottom; d->scanLinePen = QPen( Qt::blue ); setPen( d->scanLinePen ); d->expectedMeanValue = 0.0; d->expectedStandardDeviation = 0.0; d->diagram = this; d->icons[ LotChanged ] = QString::fromLatin1( ":/KDE/kchart/LeveyJennings/karo_black.svg" ); d->icons[ SensorChanged ] = QString::fromLatin1( ":/KDE/kchart/LeveyJennings/karo_red.svg" ); d->icons[ FluidicsPackChanged ] = QString::fromLatin1( ":/KDE/kchart/LeveyJennings/karo_blue.svg" ); d->icons[ OkDataPoint ] = QString::fromLatin1( ":/KDE/kchart/LeveyJennings/circle_blue.svg" ); d->icons[ NotOkDataPoint ] = QString::fromLatin1( ":/KDE/kchart/LeveyJennings/circle_blue_red.svg" ); setSelectionMode( QAbstractItemView::SingleSelection ); } LeveyJenningsDiagram::~LeveyJenningsDiagram() { } /** * Creates an exact copy of this diagram. */ LineDiagram * LeveyJenningsDiagram::clone() const { LeveyJenningsDiagram* newDiagram = new LeveyJenningsDiagram( new Private( *d ) ); return newDiagram; } bool LeveyJenningsDiagram::compare( const LeveyJenningsDiagram* other ) const { if ( other == this ) return true; if ( ! other ) { return false; } /* qDebug() <<"\n LineDiagram::compare():"; // compare own properties qDebug() << (type() == other->type()); */ return // compare the base class ( static_cast(this)->compare( other ) ); } /** * Sets the position of the lot change symbol to \a pos. * Valid values are: Qt::AlignTop (default), Qt::AlignBottom. */ void LeveyJenningsDiagram::setLotChangedSymbolPosition( Qt::Alignment pos ) { if ( d->lotChangedPosition == pos ) return; d->lotChangedPosition = pos; update(); } /** * Returns the position of the lot change symbol. */ Qt::Alignment LeveyJenningsDiagram::lotChangedSymbolPosition() const { return d->lotChangedPosition; } /** * Sets the position of the fluidics pack changed symbol to \a pos. * Valid values are: Qt::AlignBottom (default), Qt::AlignTop. */ void LeveyJenningsDiagram::setFluidicsPackChangedSymbolPosition( Qt::Alignment pos ) { if ( d->fluidicsPackChangedPosition == pos ) return; d->fluidicsPackChangedPosition = pos; update(); } /** * Returns the position of the fluidics pack changed symbol. */ Qt::Alignment LeveyJenningsDiagram::fluidicsPackChangedSymbolPosition() const { return d->fluidicsPackChangedPosition; } /** * Sets the position of the sensor changed symbol to \a pos. * Valid values are: Qt::AlignBottom (default), Qt::AlignTop. */ void LeveyJenningsDiagram::setSensorChangedSymbolPosition( Qt::Alignment pos ) { if ( d->sensorChangedPosition == pos ) return; d->sensorChangedPosition = pos; update(); } /** * Returns the position of the sensor changed symbol. */ Qt::Alignment LeveyJenningsDiagram::sensorChangedSymbolPosition() const { return d->sensorChangedPosition; } /** * Sets the date/time of all fluidics pack changes to \a changes. */ void LeveyJenningsDiagram::setFluidicsPackChanges( const QVector< QDateTime >& changes ) { if ( d->fluidicsPackChanges == changes ) return; d->fluidicsPackChanges = changes; update(); } /** * Returns the list of all fluidics pack changes. */ QVector< QDateTime > LeveyJenningsDiagram::fluidicsPackChanges() const { return d->fluidicsPackChanges; } /** * Sets the date/time of all sensor changes to \a changes. */ void LeveyJenningsDiagram::setSensorChanges( const QVector< QDateTime >& changes ) { if ( d->sensorChanges == changes ) return; d->sensorChanges = changes; update(); } /** * Sets the pen used for drawing the scan line to \a pen */ void LeveyJenningsDiagram::setScanLinePen( const QPen& pen ) { if ( d->scanLinePen == pen ) return; d->scanLinePen = pen; update(); } /** * Returns the pen being used for drawing the scan line. */ QPen LeveyJenningsDiagram::scanLinePen() const { return d->scanLinePen; } /** * Returns the SVG file name usef for \a symbol */ QString LeveyJenningsDiagram::symbol( Symbol symbol ) const { return d->icons[ symbol ]; } /** * Sets the symbol being used for \a symbol to a SVG file \a filename. */ void LeveyJenningsDiagram::setSymbol( Symbol symbol, const QString& filename ) { if ( d->icons[ symbol ] == filename ) return; delete d->iconRenderer[ symbol ]; - d->iconRenderer[ symbol ] = 0; + d->iconRenderer[ symbol ] = nullptr; d->icons[ symbol ] = filename; update(); } /** * Returns the list of all sensor changes. */ QVector< QDateTime > LeveyJenningsDiagram::sensorChanges() const { return d->sensorChanges; } /** * Sets the expected mean value over all QC values to \a meanValue. */ void LeveyJenningsDiagram::setExpectedMeanValue( float meanValue ) { if ( d->expectedMeanValue == meanValue ) return; d->expectedMeanValue = meanValue; d->setYAxisRange(); update(); } /** * Returns the expected mean values over all QC values. */ float LeveyJenningsDiagram::expectedMeanValue() const { return d->expectedMeanValue; } /** * Sets the expected standard deviaction over all QC values to \a sd. */ void LeveyJenningsDiagram::setExpectedStandardDeviation( float sd ) { if ( d->expectedStandardDeviation == sd ) return; d->expectedStandardDeviation = sd; d->setYAxisRange(); update(); } /** * Returns the expected standard deviation over all QC values. */ float LeveyJenningsDiagram::expectedStandardDeviation() const { return d->expectedStandardDeviation; } /** * Returns the calculated mean values over all QC values. */ float LeveyJenningsDiagram::calculatedMeanValue() const { return d->calculatedMeanValue; } /** * Returns the calculated standard deviation over all QC values. */ float LeveyJenningsDiagram::calculatedStandardDeviation() const { return d->calculatedStandardDeviation; } void LeveyJenningsDiagram::setModel( QAbstractItemModel* model ) { - if ( this->model() != 0 ) + if ( this->model() != nullptr ) { disconnect( this->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(calculateMeanAndStandardDeviation()) ); disconnect( this->model(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(calculateMeanAndStandardDeviation()) ); disconnect( this->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(calculateMeanAndStandardDeviation()) ); disconnect( this->model(), SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(calculateMeanAndStandardDeviation()) ); disconnect( this->model(), SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(calculateMeanAndStandardDeviation()) ); disconnect( this->model(), SIGNAL(modelReset()), this, SLOT(calculateMeanAndStandardDeviation()) ); disconnect( this->model(), SIGNAL(layoutChanged()), this, SLOT(calculateMeanAndStandardDeviation()) ); } LineDiagram::setModel( model ); - if ( this->model() != 0 ) + if ( this->model() != nullptr ) { connect( this->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(calculateMeanAndStandardDeviation()) ); connect( this->model(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(calculateMeanAndStandardDeviation()) ); connect( this->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(calculateMeanAndStandardDeviation()) ); connect( this->model(), SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(calculateMeanAndStandardDeviation()) ); connect( this->model(), SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(calculateMeanAndStandardDeviation()) ); connect( this->model(), SIGNAL(modelReset()), this, SLOT(calculateMeanAndStandardDeviation()) ); connect( this->model(), SIGNAL(layoutChanged()), this, SLOT(calculateMeanAndStandardDeviation()) ); calculateMeanAndStandardDeviation(); } } // TODO: This is the 'easy' solution // evaluate whether this is enough or we need some better one or even boost here void LeveyJenningsDiagram::calculateMeanAndStandardDeviation() const { QVector< qreal > values; // first fetch all values const QAbstractItemModel& m = *model(); const int rowCount = m.rowCount( rootIndex() ); for ( int row = 0; row < rowCount; ++row ) { const QVariant var = m.data( m.index( row, 1, rootIndex() ) ); if ( !var.isValid() ) continue; const qreal value = var.toReal(); if ( ISNAN( value ) ) continue; values << value; } qreal sum = 0.0; qreal sumSquares = 0.0; Q_FOREACH( qreal value, values ) { sum += value; sumSquares += value * value; } const int N = values.count(); d->calculatedMeanValue = sum / N; d->calculatedStandardDeviation = sqrt( ( static_cast< qreal >( N ) * sumSquares - sum * sum ) / ( N * ( N - 1 ) ) ); } // calculates the largest QDate not greater than \a dt. static QDate floorDay( const QDateTime& dt ) { return dt.date(); } // calculates the smallest QDate not less than \a dt. static QDate ceilDay( const QDateTime& dt ) { QDate result = dt.date(); if ( QDateTime( result, QTime() ) < dt ) result = result.addDays( 1 ); return result; } // calculates the largest QDateTime like xx:00 not greater than \a dt. static QDateTime floorHour( const QDateTime& dt ) { return QDateTime( dt.date(), QTime( dt.time().hour(), 0 ) ); } // calculates the smallest QDateTime like xx:00 not less than \a dt. static QDateTime ceilHour( const QDateTime& dt ) { QDateTime result( dt.date(), QTime( dt.time().hour(), 0 ) ); if ( result < dt ) result = result.addSecs( 3600 ); return result; } /** \reimpl */ const QPair LeveyJenningsDiagram::calculateDataBoundaries() const { const qreal yMin = d->expectedMeanValue - 4 * d->expectedStandardDeviation; const qreal yMax = d->expectedMeanValue + 4 * d->expectedStandardDeviation; d->setYAxisRange(); // rounded down/up to the prev/next midnight (at least that's the default) const QPair< QDateTime, QDateTime > range = timeRange(); const unsigned int minTime = range.first.toTime_t(); const unsigned int maxTime = range.second.toTime_t(); const qreal xMin = minTime / static_cast< qreal >( 24 * 60 * 60 ); const qreal xMax = maxTime / static_cast< qreal >( 24 * 60 * 60 ) - xMin; const QPointF bottomLeft( QPointF( 0, yMin ) ); const QPointF topRight( QPointF( xMax, yMax ) ); return QPair< QPointF, QPointF >( bottomLeft, topRight ); } /** * Returns the timerange of the diagram's data. */ QPair< QDateTime, QDateTime > LeveyJenningsDiagram::timeRange() const { if ( d->timeRange != QPair< QDateTime, QDateTime >() ) return d->timeRange; const QAbstractItemModel& m = *model(); const int rowCount = m.rowCount( rootIndex() ); const QDateTime begin = m.data( m.index( 0, 3, rootIndex() ) ).toDateTime(); const QDateTime end = m.data( m.index( rowCount - 1, 3, rootIndex() ) ).toDateTime(); if ( begin.secsTo( end ) > 86400 ) { // if begin to end is more than 24h // round down/up to the prev/next midnight const QDate min = floorDay( begin ); const QDate max = ceilDay( end ); return QPair< QDateTime, QDateTime >( QDateTime( min ), QDateTime( max ) ); } else if ( begin.secsTo( end ) > 3600 ) { // more than 1h: rond down up to the prex/next hour // if begin to end is more than 24h const QDateTime min = floorHour( begin ); const QDateTime max = ceilHour( end ); return QPair< QDateTime, QDateTime >( min, max ); } return QPair< QDateTime, QDateTime >( begin, end ); } /** * Sets the \a timeRange visible on the x axis. Set it to QPair< QDateTime, QDateTime >() * to use the default auto calculation. */ void LeveyJenningsDiagram::setTimeRange( const QPair< QDateTime, QDateTime >& timeRange ) { if ( d->timeRange == timeRange ) return; d->timeRange = timeRange; update(); } /** * Draws the fluidics pack and sensor changed symbols. */ void LeveyJenningsDiagram::drawChanges( PaintContext* ctx ) { const unsigned int minTime = timeRange().first.toTime_t(); Q_FOREACH( const QDateTime& dt, d->fluidicsPackChanges ) { const qreal xValue = ( dt.toTime_t() - minTime ) / static_cast< qreal >( 24 * 60 * 60 ); const QPointF point( xValue, 0.0 ); drawFluidicsPackChangedSymbol( ctx, point ); } Q_FOREACH( const QDateTime& dt, d->sensorChanges ) { const qreal xValue = ( dt.toTime_t() - minTime ) / static_cast< qreal >( 24 * 60 * 60 ); const QPointF point( xValue, 0.0 ); drawSensorChangedSymbol( ctx, point ); } } /** \reimpl */ void LeveyJenningsDiagram::paint( PaintContext* ctx ) { d->reverseMapper.clear(); // note: Not having any data model assigned is no bug // but we can not draw a diagram then either. if ( !checkInvariants( true ) ) return; if ( !AbstractGrid::isBoundariesValid(dataBoundaries()) ) return; QPainter* const painter = ctx->painter(); const PainterSaver p( painter ); if ( model()->rowCount( rootIndex() ) == 0 || model()->columnCount( rootIndex() ) < 4 ) return; // nothing to paint for us AbstractCoordinatePlane* const plane = ctx->coordinatePlane(); ctx->setCoordinatePlane( plane->sharedAxisMasterPlane( painter ) ); const QAbstractItemModel& m = *model(); const int rowCount = m.rowCount( rootIndex() ); const unsigned int minTime = timeRange().first.toTime_t(); painter->setRenderHint( QPainter::Antialiasing, true ); int prevLot = -1; QPointF prevPoint; bool hadMissingValue = false; for ( int row = 0; row < rowCount; ++row ) { const QModelIndex lotIndex = m.index( row, 0, rootIndex() ); const QModelIndex valueIndex = m.index( row, 1, rootIndex() ); const QModelIndex okIndex = m.index( row, 2, rootIndex() ); const QModelIndex timeIndex = m.index( row, 3, rootIndex() ); const QModelIndex expectedMeanIndex = m.index( row, 4, rootIndex() ); const QModelIndex expectedSDIndex = m.index( row, 5, rootIndex() ); painter->setPen( pen( lotIndex ) ); QVariant vValue = m.data( valueIndex ); qreal value = vValue.toReal(); const int lot = m.data( lotIndex ).toInt(); const bool ok = m.data( okIndex ).toBool(); const QDateTime time = m.data( timeIndex ).toDateTime(); const qreal xValue = ( time.toTime_t() - minTime ) / static_cast< qreal >( 24 * 60 * 60 ); QVariant vExpectedMean = m.data( expectedMeanIndex ); const qreal expectedMean = vExpectedMean.toReal(); QVariant vExpectedSD = m.data( expectedSDIndex ); const qreal expectedSD = vExpectedSD.toReal(); QPointF point = ctx->coordinatePlane()->translate( QPointF( xValue, value ) ); if ( vValue.isNull() ) { hadMissingValue = true; } else { if ( !vExpectedMean.isNull() && !vExpectedSD.isNull() ) { // this calculates the 'logical' value relative to the expected mean and SD of this point value -= expectedMean; value /= expectedSD; value *= d->expectedStandardDeviation; value += d->expectedMeanValue; point = ctx->coordinatePlane()->translate( QPointF( xValue, value ) ); } if ( prevLot == lot ) { const QPen pen = painter->pen(); QPen newPen = pen; if ( hadMissingValue ) { newPen.setDashPattern( QVector< qreal >() << 4.0 << 4.0 ); } painter->setPen( newPen ); painter->drawLine( prevPoint, point ); painter->setPen( pen ); // d->reverseMapper.addLine( valueIndex.row(), valueIndex.column(), prevPoint, point ); } else if ( row > 0 ) { drawLotChangeSymbol( ctx, QPointF( xValue, value ) ); } if ( value <= d->expectedMeanValue + 4 * d->expectedStandardDeviation && value >= d->expectedMeanValue - 4 * d->expectedStandardDeviation ) { const QPointF location( xValue, value ); drawDataPointSymbol( ctx, location, ok ); d->reverseMapper.addCircle( valueIndex.row(), valueIndex.column(), ctx->coordinatePlane()->translate( location ), iconRect().size() ); } prevLot = lot; prevPoint = point; hadMissingValue = false; } const QModelIndex current = selectionModel()->currentIndex(); if ( selectionModel()->rowIntersectsSelection( lotIndex.row(), lotIndex.parent() ) || current.sibling( current.row(), 0 ) == lotIndex ) { const QPen pen = ctx->painter()->pen(); painter->setPen( d->scanLinePen ); painter->drawLine( ctx->coordinatePlane()->translate( QPointF( xValue, d->expectedMeanValue - 4 * d->expectedStandardDeviation ) ), ctx->coordinatePlane()->translate( QPointF( xValue, d->expectedMeanValue + 4 * d->expectedStandardDeviation ) ) ); painter->setPen( pen ); } } drawChanges( ctx ); ctx->setCoordinatePlane( plane ); } /** * Draws a data point symbol for the data point at \a pos. * @param ok True, when the data point is ok, false otherwise (different symbol) * @param ctx The PaintContext being used */ void LeveyJenningsDiagram::drawDataPointSymbol( PaintContext* ctx, const QPointF& pos, bool ok ) { const Symbol type = ok ? OkDataPoint : NotOkDataPoint; QPainter* const painter = ctx->painter(); const PainterSaver ps( painter ); const QPointF transPos = ctx->coordinatePlane()->translate( pos ).toPoint(); painter->translate( transPos ); painter->setClipping( false ); iconRenderer( type )->render( painter, iconRect() ); } /** * Draws a lot changed symbol for the data point at \a pos. * @param ctx The PaintContext being used * \sa lotChangedSymbolPosition */ void LeveyJenningsDiagram::drawLotChangeSymbol( PaintContext* ctx, const QPointF& pos ) { const QPointF transPos = ctx->coordinatePlane()->translate( QPointF( pos.x(), d->lotChangedPosition & Qt::AlignTop ? d->expectedMeanValue + 4 * d->expectedStandardDeviation : d->expectedMeanValue - 4 * d->expectedStandardDeviation ) ); QPainter* const painter = ctx->painter(); const PainterSaver ps( painter ); painter->setClipping( false ); painter->translate( transPos ); iconRenderer( LotChanged )->render( painter, iconRect() ); } /** * Draws a sensor changed symbol for the data point at \a pos. * @param ctx The PaintContext being used * \sa sensorChangedSymbolPosition */ void LeveyJenningsDiagram::drawSensorChangedSymbol( PaintContext* ctx, const QPointF& pos ) { const QPointF transPos = ctx->coordinatePlane()->translate( QPointF( pos.x(), d->sensorChangedPosition & Qt::AlignTop ? d->expectedMeanValue + 4 * d->expectedStandardDeviation : d->expectedMeanValue - 4 * d->expectedStandardDeviation ) ); QPainter* const painter = ctx->painter(); const PainterSaver ps( painter ); painter->setClipping( false ); painter->translate( transPos ); iconRenderer( SensorChanged )->render( painter, iconRect() ); } /** * Draws a fluidics pack changed symbol for the data point at \a pos. * @param ctx The PaintContext being used * \sa fluidicsPackChangedSymbolPosition */ void LeveyJenningsDiagram::drawFluidicsPackChangedSymbol( PaintContext* ctx, const QPointF& pos ) { const QPointF transPos = ctx->coordinatePlane()->translate( QPointF( pos.x(), d->fluidicsPackChangedPosition & Qt::AlignTop ? d->expectedMeanValue + 4 * d->expectedStandardDeviation : d->expectedMeanValue - 4 * d->expectedStandardDeviation ) ); QPainter* const painter = ctx->painter(); const PainterSaver ps( painter ); painter->setClipping( false ); painter->translate( transPos ); iconRenderer( FluidicsPackChanged )->render( painter, iconRect() ); } /** * Returns the rectangle being used for drawing the icons */ QRectF LeveyJenningsDiagram::iconRect() const { const Measure m( 12.5, KChartEnums::MeasureCalculationModeAuto, KChartEnums::MeasureOrientationAuto ); TextAttributes test; test.setFontSize( m ); const QFontMetrics fm( test.calculatedFont( coordinatePlane()->parent(), KChartEnums::MeasureOrientationAuto ) ); const qreal height = fm.height() / 1.2; return QRectF( -height / 2.0, -height / 2.0, height, height ); } /** * Returns the SVG icon renderer for \a symbol */ QSvgRenderer* LeveyJenningsDiagram::iconRenderer( Symbol symbol ) { - if ( d->iconRenderer[ symbol ] == 0 ) + if ( d->iconRenderer[ symbol ] == nullptr ) d->iconRenderer[ symbol ] = new QSvgRenderer( d->icons[ symbol ], this ); return d->iconRenderer[ symbol ]; } diff --git a/src/KChart/Cartesian/KChartLeveyJenningsDiagram.h b/src/KChart/Cartesian/KChartLeveyJenningsDiagram.h index 020d067..106b5a5 100644 --- a/src/KChart/Cartesian/KChartLeveyJenningsDiagram.h +++ b/src/KChart/Cartesian/KChartLeveyJenningsDiagram.h @@ -1,129 +1,129 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTLEVEYJENNINGSDIAGRAM_H #define KCHARTLEVEYJENNINGSDIAGRAM_H #include "KChartLineDiagram.h" #include "KChartLeveyJenningsCoordinatePlane.h" QT_BEGIN_NAMESPACE class QPainter; class QPolygonF; class QSvgRenderer; QT_END_NAMESPACE namespace KChart { class ThreeDLineAttributes; /** * @brief LeveyDiagram defines a Levey Jennings chart. * * It provides different subtypes which are set using \a setType. */ class KCHART_EXPORT LeveyJenningsDiagram : public LineDiagram { Q_OBJECT Q_DISABLE_COPY( LeveyJenningsDiagram ) // KCHART_DECLARE_PRIVATE_DERIVED_PARENT( LineDiagram, CartesianCoordinatePlane * ) KCHART_DECLARE_DERIVED_DIAGRAM( LeveyJenningsDiagram, LeveyJenningsCoordinatePlane ) public: - explicit LeveyJenningsDiagram( QWidget* parent = 0, LeveyJenningsCoordinatePlane* plane = 0 ); + explicit LeveyJenningsDiagram( QWidget* parent = nullptr, LeveyJenningsCoordinatePlane* plane = nullptr ); virtual ~LeveyJenningsDiagram(); LineDiagram * clone() const Q_DECL_OVERRIDE; enum Symbol { OkDataPoint, NotOkDataPoint, LotChanged, SensorChanged, FluidicsPackChanged }; /** * Returns true if both diagrams have the same settings. */ bool compare( const LeveyJenningsDiagram* other ) const; void setLotChangedSymbolPosition( Qt::Alignment pos ); Qt::Alignment lotChangedSymbolPosition() const; void setFluidicsPackChangedSymbolPosition( Qt::Alignment pos ); Qt::Alignment fluidicsPackChangedSymbolPosition() const; void setSensorChangedSymbolPosition( Qt::Alignment pos ); Qt::Alignment sensorChangedSymbolPosition() const; void setExpectedMeanValue( float meanValue ); float expectedMeanValue() const; void setExpectedStandardDeviation( float sd ); float expectedStandardDeviation() const; float calculatedMeanValue() const; float calculatedStandardDeviation() const; void setFluidicsPackChanges( const QVector< QDateTime >& changes ); QVector< QDateTime > fluidicsPackChanges() const; void setSensorChanges( const QVector< QDateTime >& changes ); QVector< QDateTime > sensorChanges() const; void setScanLinePen( const QPen& pen ); QPen scanLinePen() const; void setSymbol( Symbol symbol, const QString& filename ); QString symbol( Symbol symbol ) const; /* \reimpl */ void setModel( QAbstractItemModel* model ) Q_DECL_OVERRIDE; QPair< QDateTime, QDateTime > timeRange() const; void setTimeRange( const QPair< QDateTime, QDateTime >& timeRange ); protected: void paint( PaintContext* paintContext ) Q_DECL_OVERRIDE; void drawChanges( PaintContext* paintContext ); virtual void drawDataPointSymbol( PaintContext* paintContext, const QPointF& pos, bool ok ); virtual void drawLotChangeSymbol( PaintContext* paintContext, const QPointF& pos ); virtual void drawSensorChangedSymbol( PaintContext* paintContext, const QPointF& pos ); virtual void drawFluidicsPackChangedSymbol( PaintContext* paintContext, const QPointF& pos ); virtual QRectF iconRect() const; QSvgRenderer* iconRenderer( Symbol symbol ); /** \reimpl */ const QPair calculateDataBoundaries() const Q_DECL_OVERRIDE; protected Q_SLOTS: void calculateMeanAndStandardDeviation() const; }; // End of class KChartLineDiagram } #endif // KCHARTLINEDIAGRAM_H diff --git a/src/KChart/Cartesian/KChartLeveyJenningsDiagram_p.cpp b/src/KChart/Cartesian/KChartLeveyJenningsDiagram_p.cpp index df6748c..f64d101 100644 --- a/src/KChart/Cartesian/KChartLeveyJenningsDiagram_p.cpp +++ b/src/KChart/Cartesian/KChartLeveyJenningsDiagram_p.cpp @@ -1,49 +1,49 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartLeveyJenningsDiagram_p.h" #include "KChartLeveyJenningsDiagram.h" #include "KChartDataValueAttributes.h" using namespace KChart; LeveyJenningsDiagram::Private::Private( const Private& rhs ) : LineDiagram::Private( rhs ), lotChangedPosition( rhs.lotChangedPosition ), fluidicsPackChangedPosition( rhs.fluidicsPackChangedPosition ), sensorChangedPosition( rhs.sensorChangedPosition ), fluidicsPackChanges( rhs.fluidicsPackChanges ), sensorChanges( rhs.sensorChanges ), scanLinePen( rhs.scanLinePen ), icons( rhs.icons ), expectedMeanValue( rhs.expectedMeanValue ), expectedStandardDeviation( rhs.expectedStandardDeviation ) { } void LeveyJenningsDiagram::Private::setYAxisRange() const { CartesianCoordinatePlane* const plane = static_cast< CartesianCoordinatePlane* >( diagram->coordinatePlane() ); - if ( plane == 0 ) + if ( plane == nullptr ) return; plane->setVerticalRange( QPair< qreal, qreal >( expectedMeanValue - 4 * expectedStandardDeviation, expectedMeanValue + 4 * expectedStandardDeviation ) ); } diff --git a/src/KChart/Cartesian/KChartLeveyJenningsGridAttributes.cpp b/src/KChart/Cartesian/KChartLeveyJenningsGridAttributes.cpp index 26d044e..cee0fd3 100644 --- a/src/KChart/Cartesian/KChartLeveyJenningsGridAttributes.cpp +++ b/src/KChart/Cartesian/KChartLeveyJenningsGridAttributes.cpp @@ -1,123 +1,123 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartLeveyJenningsGridAttributes.h" #include "KChartMath_p.h" #include #include #include #define d d_func() using namespace KChart; class Q_DECL_HIDDEN LeveyJenningsGridAttributes::Private { friend class LeveyJenningsGridAttributes; public: Private(); private: QMap< GridType, bool > visible; QMap< GridType, QPen > pens; QMap< LeveyJenningsGridAttributes::Range, QBrush > rangeBrushes; }; LeveyJenningsGridAttributes::Private::Private() { pens[ Calculated ].setCapStyle( Qt::FlatCap ); pens[ Calculated ].setColor( Qt::blue ); pens[ Expected ].setCapStyle( Qt::FlatCap ); pens[ Expected ].setColor( Qt::black ); visible[ Calculated ] = true; visible[ Expected ] = true; rangeBrushes[ LeveyJenningsGridAttributes::CriticalRange ] = QBrush( QColor( 255, 255, 192 ) ); rangeBrushes[ LeveyJenningsGridAttributes::OutOfRange ] = QBrush( QColor( 255, 128, 128 ) ); } LeveyJenningsGridAttributes::LeveyJenningsGridAttributes() : _d( new Private() ) { // this bloc left empty intentionally } LeveyJenningsGridAttributes::LeveyJenningsGridAttributes( const LeveyJenningsGridAttributes& r ) : _d( new Private( *r.d ) ) { } LeveyJenningsGridAttributes & LeveyJenningsGridAttributes::operator=( const LeveyJenningsGridAttributes& r ) { if ( this == &r ) return *this; *d = *r.d; return *this; } LeveyJenningsGridAttributes::~LeveyJenningsGridAttributes() { - delete _d; _d = 0; + delete _d; _d = nullptr; } bool LeveyJenningsGridAttributes::operator==( const LeveyJenningsGridAttributes& r ) const { return isGridVisible( Expected ) == r.isGridVisible( Expected ) && isGridVisible( Calculated ) == r.isGridVisible( Calculated ) && gridPen( Expected ) == r.gridPen( Expected ) && gridPen( Calculated ) == r.gridPen( Calculated ); } void LeveyJenningsGridAttributes::setRangeBrush( Range range, const QBrush& brush ) { d->rangeBrushes[ range ] = brush; } QBrush LeveyJenningsGridAttributes::rangeBrush( Range range ) const { return d->rangeBrushes[ range ]; } void LeveyJenningsGridAttributes::setGridVisible( GridType type, bool visible ) { d->visible[ type ] = visible; } bool LeveyJenningsGridAttributes::isGridVisible( GridType type ) const { return d->visible[ type ]; } void LeveyJenningsGridAttributes::setGridPen( GridType type, const QPen& pen ) { d->pens[ type ] = pen; d->pens[ type ].setCapStyle( Qt::FlatCap ); } QPen LeveyJenningsGridAttributes::gridPen( GridType type ) const { return d->pens[ type ]; } diff --git a/src/KChart/Cartesian/KChartLineDiagram.h b/src/KChart/Cartesian/KChartLineDiagram.h index 3c8e4f9..fab034e 100644 --- a/src/KChart/Cartesian/KChartLineDiagram.h +++ b/src/KChart/Cartesian/KChartLineDiagram.h @@ -1,148 +1,148 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTLINEDIAGRAM_H #define KCHARTLINEDIAGRAM_H #include "KChartAbstractCartesianDiagram.h" #include "KChartLineAttributes.h" #include "KChartValueTrackerAttributes.h" QT_BEGIN_NAMESPACE class QPainter; class QPolygonF; QT_END_NAMESPACE namespace KChart { class ThreeDLineAttributes; /** * @brief LineDiagram defines a common line diagram. * * It provides different subtypes which are set using \a setType. */ class KCHART_EXPORT LineDiagram : public AbstractCartesianDiagram { Q_OBJECT Q_DISABLE_COPY( LineDiagram ) // KCHART_DECLARE_PRIVATE_DERIVED_PARENT( LineDiagram, CartesianCoordinatePlane * ) KCHART_DECLARE_DERIVED_DIAGRAM( LineDiagram, CartesianCoordinatePlane ) public: class LineDiagramType; friend class LineDiagramType; - explicit LineDiagram( QWidget* parent = 0, CartesianCoordinatePlane* plane = 0 ); + explicit LineDiagram( QWidget* parent = nullptr, CartesianCoordinatePlane* plane = nullptr ); virtual ~LineDiagram(); virtual LineDiagram * clone() const; /** * Returns true if both diagrams have the same settings. */ bool compare( const LineDiagram* other ) const; enum LineType { Normal = 0, Stacked = 1, Percent = 2 }; void setType( const LineType type ); LineType type() const; /** If centerDataPoints() is true, all data points are moved by an * offset of 0.5 to the right. This is useful in conjunction with * bar diagrams, since data points are then centered just like bars. * * \sa centerDataPoints() */ void setCenterDataPoints( bool center ); /** @return option set by setCenterDataPoints() */ bool centerDataPoints() const; /** With this property set to true, data sets in a normal line diagram * are drawn in reversed order. More clearly, the first (top-most) data set * in the source model will then appear in front. This is mostly due to * historical reasons. */ void setReverseDatasetOrder( bool reverse ); /** \see setReverseDatasetOrder */ bool reverseDatasetOrder() const; void setLineAttributes( const LineAttributes & a ); void setLineAttributes( int column, const LineAttributes & a ); void setLineAttributes( const QModelIndex & index, const LineAttributes & a ); void resetLineAttributes( int column ); void resetLineAttributes( const QModelIndex & index ); LineAttributes lineAttributes() const; LineAttributes lineAttributes( int column ) const; LineAttributes lineAttributes( const QModelIndex & index ) const; void setThreeDLineAttributes( const ThreeDLineAttributes & a ); void setThreeDLineAttributes( int column, const ThreeDLineAttributes & a ); void setThreeDLineAttributes( const QModelIndex & index, const ThreeDLineAttributes & a ); ThreeDLineAttributes threeDLineAttributes() const; ThreeDLineAttributes threeDLineAttributes( int column ) const; ThreeDLineAttributes threeDLineAttributes( const QModelIndex & index ) const; void setValueTrackerAttributes( const QModelIndex & index, const ValueTrackerAttributes & a ); ValueTrackerAttributes valueTrackerAttributes( const QModelIndex & index ) const; #if defined(Q_COMPILER_MANGLES_RETURN_TYPE) // implement AbstractCartesianDiagram /* reimpl */ const int numberOfAbscissaSegments () const; /* reimpl */ const int numberOfOrdinateSegments () const; #else // implement AbstractCartesianDiagram /* reimpl */ int numberOfAbscissaSegments () const Q_DECL_OVERRIDE; /* reimpl */ int numberOfOrdinateSegments () const Q_DECL_OVERRIDE; #endif protected: void paint ( PaintContext* paintContext ) Q_DECL_OVERRIDE; public: void resize ( const QSizeF& area ) Q_DECL_OVERRIDE; protected: qreal threeDItemDepth( const QModelIndex & index ) const Q_DECL_OVERRIDE; qreal threeDItemDepth( int column ) const Q_DECL_OVERRIDE; /** \reimpl */ const QPair calculateDataBoundaries() const Q_DECL_OVERRIDE; void paintEvent ( QPaintEvent* ) Q_DECL_OVERRIDE; void resizeEvent ( QResizeEvent* ) Q_DECL_OVERRIDE; }; // End of class KChartLineDiagram } #endif // KCHARTLINEDIAGRAM_H diff --git a/src/KChart/Cartesian/KChartPlotter.cpp b/src/KChart/Cartesian/KChartPlotter.cpp index 8c2709d..658136b 100644 --- a/src/KChart/Cartesian/KChartPlotter.cpp +++ b/src/KChart/Cartesian/KChartPlotter.cpp @@ -1,488 +1,488 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartPlotter.h" #include "KChartPlotter_p.h" #include "KChartAbstractGrid.h" #include "KChartPainterSaver_p.h" #include "KChartMath_p.h" #include "KChartNormalPlotter_p.h" #include "KChartPercentPlotter_p.h" #include "KChartStackedPlotter_p.h" using namespace KChart; Plotter::Private::Private() - : implementor( 0 ) - , normalPlotter( 0 ) - , percentPlotter( 0 ) - , stackedPlotter( 0 ) + : implementor( nullptr ) + , normalPlotter( nullptr ) + , percentPlotter( nullptr ) + , stackedPlotter( nullptr ) { } Plotter::Private::~Private() { delete normalPlotter; delete percentPlotter; delete stackedPlotter; } #define d d_func() Plotter::Plotter( QWidget* parent, CartesianCoordinatePlane* plane ) : AbstractCartesianDiagram( new Private(), parent, plane ) { init(); } void Plotter::init() { d->diagram = this; d->normalPlotter = new NormalPlotter( this ); d->percentPlotter = new PercentPlotter( this ); d->stackedPlotter = new StackedPlotter( this ); d->implementor = d->normalPlotter; QObject* test = d->implementor->plotterPrivate(); connect( this, SIGNAL(boundariesChanged()), test, SLOT(changedProperties()) ); // The signal is connected to the superclass's slot at this point because the connection happened // in its constructor when "its type was not Plotter yet". disconnect( this, SIGNAL(attributesModelAboutToChange(AttributesModel*,AttributesModel*)), this, SLOT(connectAttributesModel(AttributesModel*)) ); connect( this, SIGNAL(attributesModelAboutToChange(AttributesModel*,AttributesModel*)), this, SLOT(connectAttributesModel(AttributesModel*)) ); setDatasetDimensionInternal( 2 ); } Plotter::~Plotter() { } /** * Creates an exact copy of this diagram. */ Plotter* Plotter::clone() const { Plotter* newDiagram = new Plotter( new Private( *d ) ); newDiagram->setType( type() ); return newDiagram; } bool Plotter::compare( const Plotter* other ) const { if ( other == this ) return true; - if ( other == 0 ) + if ( other == nullptr ) return false; return // compare the base class ( static_cast< const AbstractCartesianDiagram* >( this )->compare( other ) ) && // compare own properties ( type() == other->type() ); } void Plotter::connectAttributesModel( AttributesModel* newModel ) { // Order of setting the AttributesModel in compressor and diagram is very important due to slot // invocation order. Refer to the longer comment in // AbstractCartesianDiagram::connectAttributesModel() for details. if ( useDataCompression() == Plotter::NONE ) { - d->plotterCompressor.setModel( 0 ); + d->plotterCompressor.setModel( nullptr ); AbstractCartesianDiagram::connectAttributesModel( newModel ); } else { - d->compressor.setModel( 0 ); + d->compressor.setModel( nullptr ); if ( attributesModel() != d->plotterCompressor.model() ) { d->plotterCompressor.setModel( attributesModel() ); connect( &d->plotterCompressor, SIGNAL(boundariesChanged()), this, SLOT(setDataBoundariesDirty()) ); if ( useDataCompression() != Plotter::SLOPE ) { connect( coordinatePlane(), SIGNAL(internal_geometryChanged(QRect,QRect)), this, SLOT(setDataBoundariesDirty()) ); connect( coordinatePlane(), SIGNAL(geometryChanged(QRect,QRect)), this, SLOT(setDataBoundariesDirty()) ); calcMergeRadius(); } } } } Plotter::CompressionMode Plotter::useDataCompression() const { return d->implementor->useCompression(); } void Plotter::setUseDataCompression( Plotter::CompressionMode value ) { if ( useDataCompression() != value ) { d->implementor->setUseCompression( value ); if ( useDataCompression() != Plotter::NONE ) { - d->compressor.setModel( NULL ); + d->compressor.setModel( nullptr ); if ( attributesModel() != d->plotterCompressor.model() ) d->plotterCompressor.setModel( attributesModel() ); } } } qreal Plotter::maxSlopeChange() const { return d->plotterCompressor.maxSlopeChange(); } void Plotter::setMaxSlopeChange( qreal value ) { d->plotterCompressor.setMaxSlopeChange( value ); } qreal Plotter::mergeRadiusPercentage() const { return d->mergeRadiusPercentage; } void Plotter::setMergeRadiusPercentage( qreal value ) { if ( d->mergeRadiusPercentage != value ) { d->mergeRadiusPercentage = value; //d->plotterCompressor.setMergeRadiusPercentage( value ); //update(); } } /** * Sets the plotter's type to \a type */ void Plotter::setType( const PlotType type ) { if ( d->implementor->type() == type ) { return; } if ( datasetDimension() != 2 ) { Q_ASSERT_X ( false, "setType()", "This line chart type can only be used with two-dimensional data." ); return; } switch ( type ) { case Normal: d->implementor = d->normalPlotter; break; case Percent: d->implementor = d->percentPlotter; break; case Stacked: d->implementor = d->stackedPlotter; break; default: Q_ASSERT_X( false, "Plotter::setType", "unknown plotter subtype" ); } bool connection = connect( this, SIGNAL(boundariesChanged()), d->implementor->plotterPrivate(), SLOT(changedProperties()) ); Q_ASSERT( connection ); Q_UNUSED( connection ); // d->lineType = type; Q_ASSERT( d->implementor->type() == type ); setDataBoundariesDirty(); emit layoutChanged( this ); emit propertiesChanged(); } /** * @return the type of the plotter */ Plotter::PlotType Plotter::type() const { return d->implementor->type(); } /** * Sets the global line attributes to \a la */ void Plotter::setLineAttributes( const LineAttributes& la ) { d->attributesModel->setModelData( qVariantFromValue( la ), LineAttributesRole ); emit propertiesChanged(); } /** * Sets the line attributes of data set \a column to \a la */ void Plotter::setLineAttributes( int column, const LineAttributes& la ) { d->setDatasetAttrs( column, qVariantFromValue( la ), LineAttributesRole ); emit propertiesChanged(); } /** * Resets the line attributes of data set \a column */ void Plotter::resetLineAttributes( int column ) { d->resetDatasetAttrs( column, LineAttributesRole ); emit propertiesChanged(); } /** * Sets the line attributes for the model index \a index to \a la */ void Plotter::setLineAttributes( const QModelIndex & index, const LineAttributes& la ) { d->attributesModel->setData( d->attributesModel->mapFromSource( index ), qVariantFromValue( la ), LineAttributesRole ); emit propertiesChanged(); } /** * Remove any explicit line attributes settings that might have been specified before. */ void Plotter::resetLineAttributes( const QModelIndex & index ) { d->attributesModel->resetData( d->attributesModel->mapFromSource(index), LineAttributesRole ); emit propertiesChanged(); } /** * @return the global line attribute set */ LineAttributes Plotter::lineAttributes() const { return d->attributesModel->data( KChart::LineAttributesRole ).value(); } /** * @return the line attribute set of data set \a column */ LineAttributes Plotter::lineAttributes( int column ) const { const QVariant attrs( d->datasetAttrs( column, LineAttributesRole ) ); if ( attrs.isValid() ) return attrs.value(); return lineAttributes(); } /** * @return the line attribute set of the model index \a index */ LineAttributes Plotter::lineAttributes( const QModelIndex& index ) const { return d->attributesModel->data( d->attributesModel->mapFromSource( index ), KChart::LineAttributesRole ).value(); } /** * Sets the global 3D line attributes to \a la */ void Plotter::setThreeDLineAttributes( const ThreeDLineAttributes& la ) { setDataBoundariesDirty(); d->attributesModel->setModelData( qVariantFromValue( la ), ThreeDLineAttributesRole ); emit propertiesChanged(); } /** * Sets the 3D line attributes of data set \a column to \a la */ void Plotter::setThreeDLineAttributes( int column, const ThreeDLineAttributes& la ) { setDataBoundariesDirty(); d->setDatasetAttrs( column, qVariantFromValue( la ), ThreeDLineAttributesRole ); emit propertiesChanged(); } /** * Sets the 3D line attributes of model index \a index to \a la */ void Plotter::setThreeDLineAttributes( const QModelIndex& index, const ThreeDLineAttributes& la ) { setDataBoundariesDirty(); d->attributesModel->setData( d->attributesModel->mapFromSource( index ), qVariantFromValue( la ), ThreeDLineAttributesRole ); emit propertiesChanged(); } /** * @return the global 3D line attributes */ ThreeDLineAttributes Plotter::threeDLineAttributes() const { return d->attributesModel->data( KChart::ThreeDLineAttributesRole ).value(); } /** * @return the 3D line attributes of data set \a column */ ThreeDLineAttributes Plotter::threeDLineAttributes( int column ) const { const QVariant attrs( d->datasetAttrs( column, ThreeDLineAttributesRole ) ); if ( attrs.isValid() ) { return attrs.value(); } return threeDLineAttributes(); } /** * @return the 3D line attributes of the model index \a index */ ThreeDLineAttributes Plotter::threeDLineAttributes( const QModelIndex& index ) const { return d->attributesModel->data( d->attributesModel->mapFromSource( index ), KChart::ThreeDLineAttributesRole ).value(); } qreal Plotter::threeDItemDepth( const QModelIndex & index ) const { return threeDLineAttributes( index ).validDepth(); } qreal Plotter::threeDItemDepth( int column ) const { return threeDLineAttributes( column ).validDepth(); } /** * Sets the value tracker attributes of the model index \a index to \a va */ void Plotter::setValueTrackerAttributes( const QModelIndex & index, const ValueTrackerAttributes & va ) { d->attributesModel->setData( d->attributesModel->mapFromSource( index ), qVariantFromValue( va ), KChart::ValueTrackerAttributesRole ); emit propertiesChanged(); } /** * Returns the value tracker attributes of the model index \a index */ ValueTrackerAttributes Plotter::valueTrackerAttributes( const QModelIndex & index ) const { return d->attributesModel->data( d->attributesModel->mapFromSource( index ), KChart::ValueTrackerAttributesRole ).value(); } void Plotter::resizeEvent ( QResizeEvent* ) { } const QPair< QPointF, QPointF > Plotter::calculateDataBoundaries() const { if ( !checkInvariants( true ) ) return QPair< QPointF, QPointF >( QPointF( 0, 0 ), QPointF( 0, 0 ) ); // note: calculateDataBoundaries() is ignoring the hidden flags. // That's not a bug but a feature: Hiding data does not mean removing them. // For totally removing data from KD Chart's view people can use e.g. a proxy model ... // calculate boundaries for different line types Normal - Stacked - Percent - Default Normal return d->implementor->calculateDataBoundaries(); } void Plotter::paintEvent ( QPaintEvent*) { QPainter painter ( viewport() ); PaintContext ctx; ctx.setPainter ( &painter ); ctx.setRectangle ( QRectF ( 0, 0, width(), height() ) ); paint ( &ctx ); } void Plotter::paint( PaintContext* ctx ) { // note: Not having any data model assigned is no bug // but we can not draw a diagram then either. if ( !checkInvariants( true ) ) return; AbstractCoordinatePlane* const plane = ctx->coordinatePlane(); if ( ! plane ) return; d->setCompressorResolution( size(), plane ); if ( !AbstractGrid::isBoundariesValid(dataBoundaries()) ) return; const PainterSaver p( ctx->painter() ); if ( model()->rowCount( rootIndex() ) == 0 || model()->columnCount( rootIndex() ) == 0 ) return; // nothing to paint for us ctx->setCoordinatePlane( plane->sharedAxisMasterPlane( ctx->painter() ) ); // paint different line types Normal - Stacked - Percent - Default Normal d->implementor->paint( ctx ); ctx->setCoordinatePlane( plane ); } void Plotter::resize ( const QSizeF& size ) { d->setCompressorResolution( size, coordinatePlane() ); if ( useDataCompression() == Plotter::BOTH || useDataCompression() == Plotter::DISTANCE ) { d->plotterCompressor.cleanCache(); calcMergeRadius(); } setDataBoundariesDirty(); AbstractCartesianDiagram::resize( size ); } void Plotter::setDataBoundariesDirty() { AbstractCartesianDiagram::setDataBoundariesDirty(); if ( useDataCompression() == Plotter::DISTANCE || useDataCompression() == Plotter::BOTH ) { calcMergeRadius(); //d->plotterCompressor.setMergeRadiusPercentage( d->mergeRadiusPercentage ); } } void Plotter::calcMergeRadius() { CartesianCoordinatePlane *plane = dynamic_cast< CartesianCoordinatePlane* >( coordinatePlane() ); Q_ASSERT( plane ); //Q_ASSERT( plane->translate( plane->translateBack( plane->visibleDiagramArea().topLeft() ) ) == plane->visibleDiagramArea().topLeft() ); QRectF range = plane->visibleDataRange(); //qDebug() << range; const qreal radius = std::sqrt( ( range.x() + range.width() ) * ( range.y() + range.height() ) ); //qDebug() << radius; //qDebug() << radius * d->mergeRadiusPercentage; //qDebug() << d->mergeRadiusPercentage; d->plotterCompressor.setMergeRadius( radius * d->mergeRadiusPercentage ); } #if defined(Q_COMPILER_MANGLES_RETURN_TYPE) const #endif int Plotter::numberOfAbscissaSegments () const { return d->attributesModel->rowCount( attributesModelRootIndex() ); } #if defined(Q_COMPILER_MANGLES_RETURN_TYPE) const #endif int Plotter::numberOfOrdinateSegments () const { return d->attributesModel->columnCount( attributesModelRootIndex() ); } diff --git a/src/KChart/Cartesian/KChartPlotter.h b/src/KChart/Cartesian/KChartPlotter.h index 92606db..b2e9f29 100644 --- a/src/KChart/Cartesian/KChartPlotter.h +++ b/src/KChart/Cartesian/KChartPlotter.h @@ -1,141 +1,141 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTPLOTTER_H #define KCHARTPLOTTER_H #include "KChartAbstractCartesianDiagram.h" #include "KChartLineAttributes.h" #include "KChartValueTrackerAttributes.h" namespace KChart { class ThreeDLineAttributes; /** * @brief Plotter defines a diagram type plotting two-dimensional data. */ class KCHART_EXPORT Plotter : public AbstractCartesianDiagram { Q_OBJECT Q_DISABLE_COPY( Plotter ) Q_ENUMS( CompressionMode ) KCHART_DECLARE_DERIVED_DIAGRAM( Plotter, CartesianCoordinatePlane ) Q_PROPERTY( CompressionMode useDataCompression READ useDataCompression WRITE setUseDataCompression ) Q_PROPERTY( qreal mergeRadiusPercentage READ mergeRadiusPercentage WRITE setMergeRadiusPercentage ) public: // SLOPE enables a compression based on minmal slope changes // DISTANCE is still buggy and can fail, same for BOTH, NONE is the default mode enum CompressionMode{ SLOPE, DISTANCE, BOTH, NONE }; class PlotterType; friend class PlotterType; - explicit Plotter( QWidget* parent = 0, CartesianCoordinatePlane* plane = 0 ); + explicit Plotter( QWidget* parent = nullptr, CartesianCoordinatePlane* plane = nullptr ); virtual ~Plotter(); virtual Plotter* clone() const; /** * Returns true if both diagrams have the same settings. */ bool compare( const Plotter* other ) const; enum PlotType { Normal = 0, Percent, Stacked }; void setType( const PlotType type ); PlotType type() const; void setLineAttributes( const LineAttributes & a ); void setLineAttributes( int column, const LineAttributes & a ); void setLineAttributes( const QModelIndex & index, const LineAttributes & a ); void resetLineAttributes( int column ); void resetLineAttributes( const QModelIndex & index ); LineAttributes lineAttributes() const; LineAttributes lineAttributes( int column ) const; LineAttributes lineAttributes( const QModelIndex & index ) const; void setThreeDLineAttributes( const ThreeDLineAttributes & a ); void setThreeDLineAttributes( int column, const ThreeDLineAttributes & a ); void setThreeDLineAttributes( const QModelIndex & index, const ThreeDLineAttributes & a ); ThreeDLineAttributes threeDLineAttributes() const; ThreeDLineAttributes threeDLineAttributes( int column ) const; ThreeDLineAttributes threeDLineAttributes( const QModelIndex & index ) const; void setValueTrackerAttributes( const QModelIndex & index, const ValueTrackerAttributes & a ); ValueTrackerAttributes valueTrackerAttributes( const QModelIndex & index ) const; CompressionMode useDataCompression() const; void setUseDataCompression( CompressionMode value ); qreal maxSlopeChange() const; void setMaxSlopeChange( qreal value ); qreal mergeRadiusPercentage() const; void setMergeRadiusPercentage( qreal value ); #if defined(Q_COMPILER_MANGLES_RETURN_TYPE) // implement AbstractCartesianDiagram /* reimpl */ const int numberOfAbscissaSegments () const; /* reimpl */ const int numberOfOrdinateSegments () const; #else // implement AbstractCartesianDiagram /* reimpl */ int numberOfAbscissaSegments () const Q_DECL_OVERRIDE; /* reimpl */ int numberOfOrdinateSegments () const Q_DECL_OVERRIDE; #endif protected Q_SLOTS: void connectAttributesModel( AttributesModel* ); protected: void paint ( PaintContext* paintContext ) Q_DECL_OVERRIDE; public: void resize ( const QSizeF& area ) Q_DECL_OVERRIDE; protected: qreal threeDItemDepth( const QModelIndex & index ) const Q_DECL_OVERRIDE; qreal threeDItemDepth( int column ) const Q_DECL_OVERRIDE; /** \reimpl */ const QPair calculateDataBoundaries() const Q_DECL_OVERRIDE; void paintEvent ( QPaintEvent* ) Q_DECL_OVERRIDE; void resizeEvent ( QResizeEvent* ) Q_DECL_OVERRIDE; protected Q_SLOTS: void setDataBoundariesDirty(); void calcMergeRadius(); }; // End of class KChart::Plotter } #endif // KCHARTLINEDIAGRAM_H diff --git a/src/KChart/Cartesian/KChartPlotterDiagramCompressor.cpp b/src/KChart/Cartesian/KChartPlotterDiagramCompressor.cpp index a4ef678..4140917 100644 --- a/src/KChart/Cartesian/KChartPlotterDiagramCompressor.cpp +++ b/src/KChart/Cartesian/KChartPlotterDiagramCompressor.cpp @@ -1,933 +1,933 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartPlotterDiagramCompressor.h" #include "KChartPlotterDiagramCompressor_p.h" #include "KChartMath_p.h" #include using namespace KChart; qreal calculateSlope( const PlotterDiagramCompressor::DataPoint &lhs, const PlotterDiagramCompressor::DataPoint & rhs ) { return ( rhs.value - lhs.value ) / ( rhs.key - lhs.key ); } PlotterDiagramCompressor::Iterator::Iterator( int dataSet, PlotterDiagramCompressor *parent ) : m_parent( parent ) , m_index( 0 ) , m_dataset( dataSet ) , m_bufferIndex( 0 ) , m_rebuffer( true ) { if ( m_parent ) { if ( parent->rowCount() > m_dataset && parent->rowCount() > 0 ) { m_buffer.append( parent->data( CachePosition( m_index, m_dataset ) ) ); } } else { m_dataset = - 1; m_index = - 1; } } PlotterDiagramCompressor::Iterator::Iterator( int dataSet, PlotterDiagramCompressor *parent, QVector< DataPoint > buffer ) : m_parent( parent ) , m_buffer( buffer ) , m_index( 0 ) , m_dataset( dataSet ) , m_bufferIndex( 0 ) , m_rebuffer( false ) , m_timeOfCreation( QDateTime::currentDateTime() ) { if ( !m_parent ) { m_dataset = -1 ; m_index = - 1; } else { // buffer needs to be filled if ( parent->datasetCount() > m_dataset && parent->rowCount() > 0 && m_buffer.isEmpty() ) { m_buffer.append( parent->data( CachePosition( m_index, m_dataset ) ) ); m_rebuffer = true; } } } PlotterDiagramCompressor::Iterator::~Iterator() { if ( m_parent ) { if ( m_parent.data()->d->m_timeOfLastInvalidation < m_timeOfCreation ) m_parent.data()->d->m_bufferlist[ m_dataset ] = m_buffer; } } bool PlotterDiagramCompressor::Iterator::isValid() const { - if ( m_parent == 0 ) + if ( m_parent == nullptr ) return false; return m_dataset >= 0 && m_index >= 0 && m_parent.data()->rowCount() > m_index; } //PlotterDiagramCompressor::Iterator& PlotterDiagramCompressor::Iterator::operator++() //{ // ++m_index; // ++m_bufferIndex; // // the version that checks dataBoundaries is separated here, this is to avoid the runtime cost // // of checking every time the boundaries if thats not necessary // if ( m_parent.data()->d->forcedBoundaries( Qt::Vertical ) || m_parent.data()->d->forcedBoundaries( Qt::Vertical ) ) // { // if ( m_bufferIndex >= m_buffer.count() && m_rebuffer ) // { // if ( m_index < m_parent.data()->rowCount() ) // { // PlotterDiagramCompressor::DataPoint dp = m_parent.data()->data( CachePosition( m_index, m_dataset ) ); // if ( m_parent.data()->d->inBoundaries( Qt::Vertical, dp ) && m_parent.data()->d->inBoundaries( Qt::Horizontal, dp ) ) // { // m_buffer.append( dp ); // } // else // { // if ( m_index + 1 < m_parent.data()->rowCount() ) // { // PlotterDiagramCompressor::DataPoint dp1 = m_parent.data()->data( CachePosition( m_index, m_dataset ) ); // if ( m_parent.data()->d->inBoundaries( Qt::Vertical, dp1 ) && m_parent.data()->d->inBoundaries( Qt::Horizontal, dp1 ) ) // { // m_buffer.append( dp ); // } // } // } // } // } // else // { // if ( m_bufferIndex == m_buffer.count() ) // m_index = - 1; // return *this; // } // PlotterDiagramCompressor::DataPoint dp; // if ( isValid() ) // dp = m_parent.data()->data( CachePosition( m_index - 1, m_dataset ) ); // if ( m_parent ) // { // if ( m_index >= m_parent.data()->rowCount() ) // m_index = -1; // else // { // const qreal mergeRadius = m_parent.data()->d->m_mergeRadius; // PlotterDiagramCompressor::DataPoint newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) ); // while ( dp.distance( newdp ) <= mergeRadius // || !( m_parent.data()->d->inBoundaries( Qt::Vertical, dp ) || m_parent.data()->d->inBoundaries( Qt::Horizontal, dp ) ) ) // { // ++m_index; // if ( m_index >= m_parent.data()->rowCount() ) // { // m_index = - 1; // break; // } // newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) ); // } // } // } // } // else // { // // we have a new point in the buffer // if ( m_bufferIndex >= m_buffer.count() && m_rebuffer ) // { // if ( m_index < m_parent.data()->rowCount() ) // m_buffer.append( m_parent.data()->data( CachePosition( m_index, m_dataset ) ) ); // } // else // { // if ( m_bufferIndex == m_buffer.count() ) // m_index = - 1; // return *this; // } // PlotterDiagramCompressor::DataPoint dp; // if ( isValid() ) // dp = m_parent.data()->data( CachePosition( m_index - 1, m_dataset ) ); // // make sure we switch to the next point which would be in the buffer // if ( m_parent ) // { // PlotterDiagramCompressor *parent = m_parent.data(); // if ( m_index >= parent->rowCount() ) // m_index = -1; // else // { // switch ( parent->d->m_mode ) // { // case( PlotterDiagramCompressor::DISTANCE ): // { // const qreal mergeRadius = m_parent.data()->d->m_mergeRadius; // PlotterDiagramCompressor::DataPoint newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) ); // while ( dp.distance( newdp ) <= mergeRadius ) // { // ++m_index; // if ( m_index >= m_parent.data()->rowCount() ) // { // m_index = - 1; // break; // } // newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) ); // } // } // break; // case( PlotterDiagramCompressor::BOTH ): // { // const qreal mergeRadius = m_parent.data()->d->m_mergeRadius; // PlotterDiagramCompressor::DataPoint newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) ); // while ( dp.distance( newdp ) <= mergeRadius ) // { // ++m_index; // if ( m_index >= m_parent.data()->rowCount() ) // { // m_index = - 1; // break; // } // newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) ); // } // } // break; // case ( PlotterDiagramCompressor::SLOPE ): // { // const qreal mergedist = parent->d->m_maxSlopeRadius; // qreal oldSlope = 0; // qreal newSlope = 0; // PlotterDiagramCompressor::DataPoint newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) ); // PlotterDiagramCompressor::DataPoint olddp = PlotterDiagramCompressor::DataPoint(); // if ( m_bufferIndex > 1 ) // { // oldSlope = calculateSlope( m_buffer[ m_bufferIndex - 2 ], m_buffer[ m_bufferIndex - 1 ] ); // newSlope = calculateSlope( m_buffer[ m_bufferIndex - 1 ], newdp ); // } // bool first = true; // while ( qAbs( newSlope - oldSlope ) < mergedist ) // { // ++m_index; // if ( m_index >= m_parent.data()->rowCount() ) // { // m_index = - 1; // break; // } // if ( first ) // { // oldSlope = newSlope; // first = false; // } // olddp = newdp; // newdp = m_parent.data()->data( CachePosition( m_index, m_dataset ) ); // newSlope = calculateSlope( olddp, newdp ); // } // } // break; // default: // Q_ASSERT( false ); // } // } // } // } // return *this; //} void PlotterDiagramCompressor::Iterator::handleSlopeForward( const DataPoint &dp ) { PlotterDiagramCompressor* parent = m_parent.data(); const qreal mergedist = parent->d->m_maxSlopeRadius; qreal oldSlope = 0; qreal newSlope = 0; PlotterDiagramCompressor::DataPoint newdp = dp; PlotterDiagramCompressor::DataPoint olddp = PlotterDiagramCompressor::DataPoint(); if ( m_bufferIndex > 1 ) { //oldSlope = calculateSlope( m_buffer[ m_bufferIndex - 2 ], m_buffer[ m_bufferIndex - 1 ] ); //newSlope = calculateSlope( m_buffer[ m_bufferIndex - 1 ], newdp ); oldSlope = calculateSlope( parent->data( CachePosition( m_index - 2, m_dataset ) ) , parent->data( CachePosition( m_index - 1, m_dataset ) ) ); newSlope = calculateSlope( parent->data( CachePosition( m_index - 1, m_dataset ) ), newdp ); qreal accumulatedDist = qAbs( newSlope - oldSlope ); qreal olddist = accumulatedDist; qreal newdist; int counter = 0; while ( accumulatedDist < mergedist ) { ++m_index; if ( m_index >= m_parent.data()->rowCount() ) { m_index = - 1; if ( m_buffer.last() != parent->data( CachePosition( parent->rowCount() -1, m_dataset ) ) ) m_index = parent->rowCount(); break; } oldSlope = newSlope; olddp = newdp; newdp = parent->data( CachePosition( m_index, m_dataset ) ); newSlope = calculateSlope( olddp, newdp ); newdist = qAbs( newSlope - oldSlope ); if ( olddist == newdist ) { ++counter; } else { if ( counter > 10 ) break; } accumulatedDist += newdist; olddist = newdist; } m_buffer.append( newdp ); } else m_buffer.append( dp ); } PlotterDiagramCompressor::Iterator& PlotterDiagramCompressor::Iterator::operator++() { PlotterDiagramCompressor* parent = m_parent.data(); Q_ASSERT( parent ); const int count = parent->rowCount(); //increment the indexes ++m_index; ++m_bufferIndex; //if the index reached the end of the datamodel make this iterator an enditerator //and make sure the buffer was not already build, if thats the case its not necessary //to rebuild it and it would be hard to extend it as we had to know where m_index was if ( m_index >= count || ( !m_rebuffer && m_bufferIndex == m_buffer.count() ) ) { if ( m_bufferIndex == m_buffer.count() ) { if ( m_buffer.last() != parent->data( CachePosition( parent->rowCount() -1, m_dataset ) ) ) m_index = parent->rowCount(); else m_index = - 1; ++m_bufferIndex; } else m_index = -1; } //if we reached the end of the buffer continue filling the buffer if ( m_bufferIndex == m_buffer.count() && m_index >= 0 && m_rebuffer ) { PlotterDiagramCompressor::DataPoint dp = parent->data( CachePosition( m_index, m_dataset ) ); if ( parent->d->inBoundaries( Qt::Vertical, dp ) && parent->d->inBoundaries( Qt::Horizontal, dp ) ) { if ( parent->d->m_mode == PlotterDiagramCompressor::SLOPE ) handleSlopeForward( dp ); } else { m_index = -1; } } return *this; } PlotterDiagramCompressor::Iterator PlotterDiagramCompressor::Iterator::operator++( int ) { Iterator result = *this; ++result; return result; } PlotterDiagramCompressor::Iterator& PlotterDiagramCompressor::Iterator::operator += ( int value ) { for ( int index = m_index; index + value != m_index; ++( *this ) ) {}; return *this; } PlotterDiagramCompressor::Iterator& PlotterDiagramCompressor::Iterator::operator--() { --m_index; --m_bufferIndex; return *this; } PlotterDiagramCompressor::Iterator PlotterDiagramCompressor::Iterator::operator--( int ) { Iterator result = *this; --result; return result; } PlotterDiagramCompressor::Iterator& PlotterDiagramCompressor::Iterator::operator-=( int value ) { m_index -= value; return *this; } PlotterDiagramCompressor::DataPoint PlotterDiagramCompressor::Iterator::operator*() { if ( !m_parent ) return PlotterDiagramCompressor::DataPoint(); Q_ASSERT( m_parent ); if ( m_index == m_parent.data()->rowCount() ) return m_parent.data()->data( CachePosition( m_parent.data()->rowCount() - 1 , m_dataset ) ); return m_buffer[ m_bufferIndex ]; } bool PlotterDiagramCompressor::Iterator::operator==( const PlotterDiagramCompressor::Iterator &other ) const { return m_parent.data() == other.m_parent.data() && m_index == other.m_index && m_dataset == other.m_dataset; } bool PlotterDiagramCompressor::Iterator::operator!=( const PlotterDiagramCompressor::Iterator &other ) const { return ! ( *this == other ); } void PlotterDiagramCompressor::Iterator::invalidate() { m_dataset = - 1; } PlotterDiagramCompressor::Private::Private( PlotterDiagramCompressor *parent ) : m_parent( parent ) - , m_model( 0 ) + , m_model( nullptr ) , m_mergeRadius( 0.1 ) , m_maxSlopeRadius( 0.1 ) , m_boundary( qMakePair( QPointF( std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN() ) , QPointF( std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN() ) ) ) , m_forcedXBoundaries( qMakePair( std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN() ) ) , m_forcedYBoundaries( qMakePair( std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN() ) ) , m_mode( PlotterDiagramCompressor::SLOPE ) { } void PlotterDiagramCompressor::Private::setModelToZero() { - m_model = 0; + m_model = nullptr; } inline bool inBoundary( const QPair< qreal, qreal > &bounds, qreal value ) { return bounds.first <= value && value <= bounds.second; } bool PlotterDiagramCompressor::Private::inBoundaries( Qt::Orientation orient, const PlotterDiagramCompressor::DataPoint &dp ) const { if ( orient == Qt::Vertical && forcedBoundaries( Qt::Vertical ) ) { return inBoundary( m_forcedYBoundaries, dp.value ); } else if ( forcedBoundaries( Qt::Horizontal ) ) { return inBoundary( m_forcedXBoundaries, dp.key ); } return true; } //// TODO this is not threadsafe do never try to invoke the painting in a different thread than this //// method //void PlotterDiagramCompressor::Private::rowsInserted( const QModelIndex& /*parent*/, int start, int end ) //{ // if ( m_bufferlist.count() > 0 && !m_bufferlist[ 0 ].isEmpty() && start < m_bufferlist[ 0 ].count() ) // { // calculateDataBoundaries(); // clearBuffer(); // return; // } // // we are handling appends only here, a prepend might be added, insert is expensive if not needed // qreal minX = std::numeric_limits< qreal >::max(); // qreal minY = std::numeric_limits< qreal >::max(); // qreal maxX = std::numeric_limits< qreal >::min(); // qreal maxY = std::numeric_limits< qreal >::min(); // for ( int dataset = 0; dataset < m_bufferlist.size(); ++dataset ) // { // PlotterDiagramCompressor::DataPoint predecessor = m_bufferlist[ dataset ].isEmpty() ? DataPoint() : m_bufferlist[ dataset ].last(); // qreal oldSlope = 0; // qreal newSlope = 0; // PlotterDiagramCompressor::DataPoint newdp = m_parent->data( CachePosition( start, dataset ) ); // PlotterDiagramCompressor::DataPoint olddp = PlotterDiagramCompressor::DataPoint(); // const int datacount = m_bufferlist[ dataset ].count(); // if ( m_mode != PlotterDiagramCompressor::DISTANCE && m_bufferlist[ dataset ].count() > 1 ) // { // oldSlope = calculateSlope( m_bufferlist[ dataset ][ datacount - 2 ], m_bufferlist[ dataset ][ datacount - 1 ] ); // newSlope = calculateSlope( m_bufferlist[ dataset ][ datacount - 1 ], newdp ); // } // bool first = true; // for ( int row = start; row <= end; ++row ) // { // PlotterDiagramCompressor::DataPoint curdp = m_parent->data( CachePosition( row, dataset ) ); // const bool checkcur = inBoundaries( Qt::Vertical, curdp ) && inBoundaries( Qt::Horizontal, curdp ); // const bool checkpred = inBoundaries( Qt::Vertical, predecessor ) && inBoundaries( Qt::Horizontal, predecessor ); // const bool check = checkcur || checkpred; // switch ( m_mode ) // { // case( PlotterDiagramCompressor::BOTH ): // { // if ( predecessor.distance( curdp ) > m_mergeRadius && check ) // { // if ( start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() ) // { // m_bufferlist[ dataset ].append( curdp ); // } // else if ( !m_bufferlist[ dataset ].isEmpty() ) // { // m_bufferlist[ dataset ].insert( row, curdp ); // } // predecessor = curdp; // minX = qMin( curdp.key, m_boundary.first.x() ); // minY = qMin( curdp.value, m_boundary.first.y() ); // maxX = qMax( curdp.key, m_boundary.second.x() ); // maxY = qMax( curdp.value, m_boundary.second.y() ); // } // } // break; // case ( PlotterDiagramCompressor::DISTANCE ): // { // if ( predecessor.distance( curdp ) > m_mergeRadius && check ) // { // if ( start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() ) // { // m_bufferlist[ dataset ].append( curdp ); // } // else if ( !m_bufferlist[ dataset ].isEmpty() ) // { // m_bufferlist[ dataset ].insert( row, curdp ); // } // predecessor = curdp; // minX = qMin( curdp.key, m_boundary.first.x() ); // minY = qMin( curdp.value, m_boundary.first.y() ); // maxX = qMax( curdp.key, m_boundary.second.x() ); // maxY = qMax( curdp.value, m_boundary.second.y() ); // } // } // break; // case( PlotterDiagramCompressor::SLOPE ): // { // if ( check && qAbs( newSlope - oldSlope ) >= m_maxSlopeRadius ) // { // if ( start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() ) // { // m_bufferlist[ dataset ].append( curdp ); // oldSlope = newSlope; // } // else if ( !m_bufferlist[ dataset ].isEmpty() ) // { // m_bufferlist[ dataset ].insert( row, curdp ); // oldSlope = newSlope; // } // predecessor = curdp; // minX = qMin( curdp.key, m_boundary.first.x() ); // minY = qMin( curdp.value, m_boundary.first.y() ); // maxX = qMax( curdp.key, m_boundary.second.x() ); // maxY = qMax( curdp.value, m_boundary.second.y() ); // if ( first ) // { // oldSlope = newSlope; // first = false; // } // olddp = newdp; // newdp = m_parent->data( CachePosition( row, dataset ) ); // newSlope = calculateSlope( olddp, newdp ); // } // } // break; // } // } // } // setBoundaries( qMakePair( QPointF( minX, minY ), QPointF( maxX, maxY ) ) ); // emit m_parent->rowCountChanged(); //} #include // TODO this is not threadsafe do never try to invoke the painting in a different thread than this // method void PlotterDiagramCompressor::Private::rowsInserted( const QModelIndex& /*parent*/, int start, int end ) { //Q_ASSERT( std::numeric_limits::quiet_NaN() < 5 || std::numeric_limits::quiet_NaN() > 5 ); //Q_ASSERT( 5 == qMin( std::numeric_limits::quiet_NaN(), 5.0 ) ); //Q_ASSERT( 5 == qMax( 5.0, std::numeric_limits::quiet_NaN() ) ); if ( m_bufferlist.count() > 0 && !m_bufferlist[ 0 ].isEmpty() && start < m_bufferlist[ 0 ].count() ) { calculateDataBoundaries(); clearBuffer(); return; } // we are handling appends only here, a prepend might be added, insert is expensive if not needed qreal minX = m_boundary.first.x(); qreal minY = m_boundary.first.y(); qreal maxX = m_boundary.second.x(); qreal maxY = m_boundary.second.y(); for ( int dataset = 0; dataset < m_bufferlist.size(); ++dataset ) { if ( m_mode == PlotterDiagramCompressor::SLOPE ) { PlotterDiagramCompressor::DataPoint predecessor = m_bufferlist[ dataset ].isEmpty() ? DataPoint() : m_bufferlist[ dataset ].last(); qreal oldSlope = 0; qreal newSlope = 0; int counter = 0; PlotterDiagramCompressor::DataPoint newdp = m_parent->data( CachePosition( start, dataset ) ); PlotterDiagramCompressor::DataPoint olddp = PlotterDiagramCompressor::DataPoint(); if ( start > 1 ) { oldSlope = calculateSlope( m_parent->data( CachePosition( start - 2, dataset ) ), m_parent->data( CachePosition( start - 1, dataset ) ) ); olddp = m_parent->data( CachePosition( start - 1, dataset ) ); } else { m_bufferlist[ dataset ].append( newdp ); minX = qMin( minX, newdp.key ); minY = qMin( minY, newdp.value ); maxX = qMax( newdp.key, maxX ); maxY = qMax( newdp.value, maxY ); continue; } qreal olddist = 0; qreal newdist = 0; for ( int row = start; row <= end; ++row ) { PlotterDiagramCompressor::DataPoint curdp = m_parent->data( CachePosition( row, dataset ) ); newdp = curdp; newSlope = calculateSlope( olddp, newdp ); olddist = newdist; newdist = qAbs( newSlope - oldSlope ); m_accumulatedDistances[ dataset ] += newdist; const bool checkcur = inBoundaries( Qt::Vertical, curdp ) && inBoundaries( Qt::Horizontal, curdp ); const bool checkpred = inBoundaries( Qt::Vertical, predecessor ) && inBoundaries( Qt::Horizontal, predecessor ); const bool check = checkcur || checkpred; if ( m_accumulatedDistances[ dataset ] >= m_maxSlopeRadius && check ) { if ( start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() ) { m_bufferlist[ dataset ].append( curdp ); } else if ( !m_bufferlist[ dataset ].isEmpty() ) { m_bufferlist[ dataset ].insert( row, curdp ); } predecessor = curdp; m_accumulatedDistances[ dataset ] = 0; } minX = qMin( minX, curdp.key ); minY = qMin( minY, curdp.value ); maxX = qMax( curdp.key, maxX ); maxY = qMax( curdp.value, maxY ); oldSlope = newSlope; olddp = newdp; if ( olddist == newdist ) { ++counter; } else { if ( counter > 10 ) { m_bufferlist[ dataset ].append( curdp ); predecessor = curdp; m_accumulatedDistances[ dataset ] = 0; } } } setBoundaries( qMakePair( QPointF( minX, minY ), QPointF( maxX, maxY ) ) ); } else { PlotterDiagramCompressor::DataPoint predecessor = m_bufferlist[ dataset ].isEmpty() ? DataPoint() : m_bufferlist[ dataset ].last(); for ( int row = start; row <= end; ++row ) { PlotterDiagramCompressor::DataPoint curdp = m_parent->data( CachePosition( row, dataset ) ); const bool checkcur = inBoundaries( Qt::Vertical, curdp ) && inBoundaries( Qt::Horizontal, curdp ); const bool checkpred = inBoundaries( Qt::Vertical, predecessor ) && inBoundaries( Qt::Horizontal, predecessor ); const bool check = checkcur || checkpred; if ( predecessor.distance( curdp ) > m_mergeRadius && check ) { if ( start > m_bufferlist[ dataset ].count() && !m_bufferlist[ dataset ].isEmpty() ) { m_bufferlist[ dataset ].append( curdp ); } else if ( !m_bufferlist[ dataset ].isEmpty() ) { m_bufferlist[ dataset ].insert( row, curdp ); } predecessor = curdp; qreal minX = qMin( curdp.key, m_boundary.first.x() ); qreal minY = qMin( curdp.value, m_boundary.first.y() ); qreal maxX = qMax( curdp.key, m_boundary.second.x() ); qreal maxY = qMax( curdp.value, m_boundary.second.y() ); setBoundaries( qMakePair( QPointF( minX, minY ), QPointF( maxX, maxY ) ) ); } } } } emit m_parent->rowCountChanged(); } void PlotterDiagramCompressor::setCompressionModel( CompressionMode value ) { Q_ASSERT( d ); if ( d->m_mode != value ) { d->m_mode = value; d->clearBuffer(); emit rowCountChanged(); } } void PlotterDiagramCompressor::Private::setBoundaries( const Boundaries & bound ) { if ( bound != m_boundary ) { m_boundary = bound; emit m_parent->boundariesChanged(); } } void PlotterDiagramCompressor::Private::calculateDataBoundaries() { if ( !forcedBoundaries( Qt::Vertical ) || !forcedBoundaries( Qt::Horizontal ) ) { qreal minX = std::numeric_limits::quiet_NaN(); qreal minY = std::numeric_limits::quiet_NaN(); qreal maxX = std::numeric_limits::quiet_NaN(); qreal maxY = std::numeric_limits::quiet_NaN(); for ( int dataset = 0; dataset < m_parent->datasetCount(); ++dataset ) { for ( int row = 0; row < m_parent->rowCount(); ++ row ) { PlotterDiagramCompressor::DataPoint dp = m_parent->data( CachePosition( row, dataset ) ); minX = qMin( minX, dp.key ); minY = qMin( minY, dp.value ); maxX = qMax( dp.key, maxX ); maxY = qMax( dp.value, maxY ); Q_ASSERT( !ISNAN( minX ) ); Q_ASSERT( !ISNAN( minY ) ); Q_ASSERT( !ISNAN( maxX ) ); Q_ASSERT( !ISNAN( maxY ) ); } } if ( forcedBoundaries( Qt::Vertical ) ) { minY = m_forcedYBoundaries.first; maxY = m_forcedYBoundaries.second; } if ( forcedBoundaries( Qt::Horizontal ) ) { minX = m_forcedXBoundaries.first; maxX = m_forcedXBoundaries.second; } setBoundaries( qMakePair( QPointF( minX, minY ), QPointF( maxX, maxY ) ) ); } } QModelIndexList PlotterDiagramCompressor::Private::mapToModel( const CachePosition &pos ) { QModelIndexList indexes; QModelIndex index; index = m_model->index( pos.first, pos.second * 2, QModelIndex() ); Q_ASSERT( index.isValid() ); indexes << index; index = m_model->index( pos.first, pos.second * 2 + 1, QModelIndex() ); Q_ASSERT( index.isValid() ); indexes << index; return indexes; } bool PlotterDiagramCompressor::Private::forcedBoundaries( Qt::Orientation orient ) const { if ( orient == Qt::Vertical ) return !ISNAN( m_forcedYBoundaries.first ) && !ISNAN( m_forcedYBoundaries.second ); else return !ISNAN( m_forcedXBoundaries.first ) && !ISNAN( m_forcedXBoundaries.second ); } void PlotterDiagramCompressor::Private::clearBuffer() { //TODO all iterator have to be invalid after this operation //TODO make sure there are no regressions, the timeOfLastInvalidation should stop iterators from // corrupting the cache m_bufferlist.clear(); m_bufferlist.resize( m_parent->datasetCount() ); m_accumulatedDistances.clear(); m_accumulatedDistances.resize( m_parent->datasetCount() ); m_timeOfLastInvalidation = QDateTime::currentDateTime(); } PlotterDiagramCompressor::PlotterDiagramCompressor(QObject *parent) : QObject(parent) , d( new Private( this ) ) { } PlotterDiagramCompressor::~PlotterDiagramCompressor() { delete d; - d = 0; + d = nullptr; } void PlotterDiagramCompressor::setForcedDataBoundaries( const QPair< qreal, qreal > &bounds, Qt::Orientation direction ) { if ( direction == Qt::Vertical ) { d->m_forcedYBoundaries = bounds; } else { d->m_forcedXBoundaries = bounds; } d->clearBuffer(); emit boundariesChanged(); } QAbstractItemModel* PlotterDiagramCompressor::model() const { Q_ASSERT( d ); return d->m_model; } void PlotterDiagramCompressor::setModel( QAbstractItemModel *model ) { Q_ASSERT( d ); if ( d->m_model ) { d->m_model->disconnect( this ); d->m_model->disconnect( d ); } d->m_model = model; if ( d->m_model) { d->m_bufferlist.resize( datasetCount() ); d->m_accumulatedDistances.resize( datasetCount() ); d->calculateDataBoundaries(); connect( d->m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), d, SLOT(rowsInserted(QModelIndex,int,int)) ); connect( d->m_model, SIGNAL(modelReset()), d, SLOT(clearBuffer()) ); connect( d->m_model, SIGNAL(destroyed(QObject*)), d, SLOT(setModelToZero()) ); } } PlotterDiagramCompressor::DataPoint PlotterDiagramCompressor::data( const CachePosition& pos ) const { DataPoint point; QModelIndexList indexes = d->mapToModel( pos ); Q_ASSERT( indexes.count() == 2 ); QVariant yValue = d->m_model->data( indexes.last() ); QVariant xValue = d->m_model->data( indexes.first() ); Q_ASSERT( xValue.isValid() ); Q_ASSERT( yValue.isValid() ); bool ok = false; point.key = xValue.toReal( &ok ); Q_ASSERT( ok ); ok = false; point.value = yValue.toReal( &ok ); Q_ASSERT( ok ); point.index = indexes.first(); return point; } void PlotterDiagramCompressor::setMergeRadius( qreal radius ) { if ( d->m_mergeRadius != radius ) { d->m_mergeRadius = radius; if ( d->m_mode != PlotterDiagramCompressor::SLOPE ) emit rowCountChanged(); } } void PlotterDiagramCompressor::setMaxSlopeChange( qreal value ) { if ( d->m_maxSlopeRadius != value ) { d->m_maxSlopeRadius = value; emit boundariesChanged(); } } qreal PlotterDiagramCompressor::maxSlopeChange() const { return d->m_maxSlopeRadius; } void PlotterDiagramCompressor::setMergeRadiusPercentage( qreal radius ) { Boundaries bounds = dataBoundaries(); const qreal width = radius * ( bounds.second.x() - bounds.first.x() ); const qreal height = radius * ( bounds.second.y() - bounds.first.y() ); const qreal realRadius = std::sqrt( width * height ); setMergeRadius( realRadius ); } int PlotterDiagramCompressor::rowCount() const { return d->m_model ? d->m_model->rowCount() : 0; } void PlotterDiagramCompressor::cleanCache() { d->clearBuffer(); } int PlotterDiagramCompressor::datasetCount() const { if ( d->m_model && d->m_model->columnCount() == 0 ) return 0; return d->m_model ? ( d->m_model->columnCount() + 1 ) / 2 : 0; } QPair< QPointF, QPointF > PlotterDiagramCompressor::dataBoundaries() const { Boundaries bounds = d->m_boundary; if ( d->forcedBoundaries( Qt::Vertical ) ) { bounds.first.setY( d->m_forcedYBoundaries.first ); bounds.second.setY( d->m_forcedYBoundaries.second ); } if ( d->forcedBoundaries( Qt::Horizontal ) ) { bounds.first.setX( d->m_forcedXBoundaries.first ); bounds.second.setX( d->m_forcedXBoundaries.second ); } return bounds; } PlotterDiagramCompressor::Iterator PlotterDiagramCompressor::begin( int dataSet ) { Q_ASSERT( dataSet >= 0 && dataSet < d->m_bufferlist.count() ); return Iterator( dataSet, this, d->m_bufferlist[ dataSet ] ); } PlotterDiagramCompressor::Iterator PlotterDiagramCompressor::end( int dataSet ) { Iterator it( dataSet, this ); it.m_index = -1; return it; } diff --git a/src/KChart/Cartesian/KChartPlotterDiagramCompressor.h b/src/KChart/Cartesian/KChartPlotterDiagramCompressor.h index d6850db..db0a215 100644 --- a/src/KChart/Cartesian/KChartPlotterDiagramCompressor.h +++ b/src/KChart/Cartesian/KChartPlotterDiagramCompressor.h @@ -1,151 +1,151 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PLOTTERDIAGRAMCOMPRESSOR_H #define PLOTTERDIAGRAMCOMPRESSOR_H #include #include #include #include #include #include #include namespace KChart { class PlotterDiagramCompressor : public QObject { Q_OBJECT Q_ENUMS( CompressionMode ) public: enum CompressionMode{ SLOPE = 0, DISTANCE, BOTH }; class DataPoint { public: DataPoint() : key( std::numeric_limits< qreal >::quiet_NaN() ), value( std::numeric_limits< qreal >::quiet_NaN() ), hidden( false ) {} inline qreal distance( const DataPoint &other ) { const qreal dx = key - other.key; const qreal dy = value - other.value; return std::sqrt( dx * dx + dy * dy ); } inline bool operator==( const DataPoint &other ) { return key == other.key && value == other.value; } inline bool operator!=( const DataPoint &other ) { return !( *this == other ); } qreal key; qreal value; bool hidden; QModelIndex index; }; class Iterator { friend class PlotterDiagramCompressor; public: Iterator( int dataSet, PlotterDiagramCompressor *parent ); ~Iterator(); bool isValid() const; Iterator& operator++(); Iterator operator++( int ); Iterator& operator += ( int value ); Iterator& operator--(); Iterator operator--( int ); Iterator& operator-=( int value ); DataPoint operator*(); bool operator==( const Iterator &other ) const; bool operator!=( const Iterator &other ) const; void invalidate(); protected: Iterator( int dataSet, PlotterDiagramCompressor *parent, QVector< DataPoint > buffer ); private: void handleSlopeForward( const DataPoint &dp ); QPointer< PlotterDiagramCompressor > m_parent; QVector< DataPoint > m_buffer; int m_index; int m_dataset; int m_bufferIndex; int m_rebuffer; QDateTime m_timeOfCreation; }; typedef QVector DataPointVector; class CachePosition { public: CachePosition() : first( -1 ), second( -1 ) {} CachePosition( int first, int second ) : first( first ), second( second ) {} int first; int second; bool operator==( const CachePosition& rhs ) const { return first == rhs.first && second == rhs.second; } }; - explicit PlotterDiagramCompressor(QObject *parent = 0); + explicit PlotterDiagramCompressor(QObject *parent = nullptr); ~PlotterDiagramCompressor(); Iterator begin( int dataSet ); Iterator end( int dataSet ); void setMergeRadius( qreal radius ); void setMergeRadiusPercentage( qreal radius ); void setModel( QAbstractItemModel *model ); QAbstractItemModel* model() const; DataPoint data( const CachePosition& pos ) const; int rowCount() const; int datasetCount() const; void setCompressionModel( CompressionMode value ); void setMaxSlopeChange( qreal value ); qreal maxSlopeChange() const; void cleanCache(); QPair< QPointF, QPointF > dataBoundaries() const; void setForcedDataBoundaries( const QPair< qreal, qreal > &bounds, Qt::Orientation direction ); Q_SIGNALS: void boundariesChanged(); void rowCountChanged(); private: class Private; Private *d; }; } #endif // PLOTTERDIAGRAMCOMPRESSOR_H diff --git a/src/KChart/Cartesian/KChartStockDiagram.h b/src/KChart/Cartesian/KChartStockDiagram.h index caed769..c7d3650 100644 --- a/src/KChart/Cartesian/KChartStockDiagram.h +++ b/src/KChart/Cartesian/KChartStockDiagram.h @@ -1,116 +1,116 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHART_STOCK_DIAGRAM_H #define KCHART_STOCK_DIAGRAM_H #include "KChartAbstractCartesianDiagram.h" #include "KChartCartesianCoordinatePlane.h" #include "KChartStockBarAttributes.h" #include "KChartThreeDBarAttributes.h" namespace KChart { class PaintContext; class KCHART_EXPORT StockDiagram : public AbstractCartesianDiagram { Q_OBJECT Q_DISABLE_COPY( StockDiagram ) KCHART_DECLARE_DERIVED_DIAGRAM( StockDiagram, CartesianCoordinatePlane ) public: enum Type { HighLowClose, OpenHighLowClose, Candlestick }; - explicit StockDiagram( QWidget *parent = 0, CartesianCoordinatePlane *plane = 0 ); + explicit StockDiagram( QWidget *parent = nullptr, CartesianCoordinatePlane *plane = nullptr ); ~StockDiagram(); void setType( Type type ); Type type() const; void setStockBarAttributes( const StockBarAttributes &attr ); StockBarAttributes stockBarAttributes() const; void setStockBarAttributes( int column, const StockBarAttributes &attr ); StockBarAttributes stockBarAttributes( int column ) const; void setThreeDBarAttributes( const ThreeDBarAttributes &attr ); ThreeDBarAttributes threeDBarAttributes() const; void setThreeDBarAttributes( int column, const ThreeDBarAttributes &attr ); ThreeDBarAttributes threeDBarAttributes( int column ) const; void setLowHighLinePen( const QPen &pen ); QPen lowHighLinePen() const; void setLowHighLinePen( int column, const QPen &pen ); QPen lowHighLinePen( int column ) const; void setUpTrendCandlestickBrush( const QBrush &brush ); QBrush upTrendCandlestickBrush() const; void setDownTrendCandlestickBrush( const QBrush &brush ); QBrush downTrendCandlestickBrush() const; void setUpTrendCandlestickBrush( int column, const QBrush &brush ); QBrush upTrendCandlestickBrush( int column ) const; void setDownTrendCandlestickBrush( int column, const QBrush &brush ); QBrush downTrendCandlestickBrush( int column ) const; void setUpTrendCandlestickPen( const QPen &pen ); QPen upTrendCandlestickPen() const; void setDownTrendCandlestickPen( const QPen &pen ); QPen downTrendCandlestickPen() const; void setUpTrendCandlestickPen( int column, const QPen &pen ); QPen upTrendCandlestickPen( int column ) const; void setDownTrendCandlestickPen( int column, const QPen &pen ); QPen downTrendCandlestickPen( int column ) const; #if defined(Q_COMPILER_MANGLES_RETURN_TYPE) virtual const int numberOfAbscissaSegments() const; virtual const int numberOfOrdinateSegments() const; #else int numberOfAbscissaSegments() const Q_DECL_OVERRIDE; int numberOfOrdinateSegments() const Q_DECL_OVERRIDE; #endif void paint( PaintContext *paintContext ) Q_DECL_OVERRIDE; void resize( const QSizeF &size ) Q_DECL_OVERRIDE; qreal threeDItemDepth( int column ) const Q_DECL_OVERRIDE; qreal threeDItemDepth( const QModelIndex &index ) const Q_DECL_OVERRIDE; protected: const QPair calculateDataBoundaries() const Q_DECL_OVERRIDE; }; } // namespace KChart #endif // KCHART_STOCK_DIAGRAM_H diff --git a/src/KChart/Cartesian/KChartStockDiagram_p.cpp b/src/KChart/Cartesian/KChartStockDiagram_p.cpp index 8dce0bd..7d96606 100644 --- a/src/KChart/Cartesian/KChartStockDiagram_p.cpp +++ b/src/KChart/Cartesian/KChartStockDiagram_p.cpp @@ -1,537 +1,537 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartStockDiagram_p.h" #include "KChartPainterSaver_p.h" using namespace KChart; class Q_DECL_HIDDEN StockDiagram::Private::ThreeDPainter { public: struct ThreeDProperties { qreal depth; qreal angle; bool useShadowColors; }; ThreeDPainter( QPainter *p ) : painter( p ) {}; QPolygonF drawTwoDLine( const QLineF &line, const QPen &pen, const ThreeDProperties &props ); QPolygonF drawThreeDLine( const QLineF &line, const QBrush &brush, const QPen &pen, const ThreeDProperties &props ); QPolygonF drawThreeDRect( const QRectF &rect, const QBrush &brush, const QPen &pen, const ThreeDProperties &props ); private: QPointF projectPoint( const QPointF &point, qreal depth, qreal angle ) const; QColor calcShadowColor( const QColor &color, qreal angle ) const; QPainter *painter; }; /** * Projects a point in 3D space * * @param depth The distance from the point and the projected point * @param angle The angle the projected point is rotated by around the original point */ QPointF StockDiagram::Private::ThreeDPainter::projectPoint( const QPointF &point, qreal depth, qreal angle ) const { const qreal angleInRad = DEGTORAD( angle ); const qreal distX = depth * cos( angleInRad ); // Y coordinates are reversed on our coordinate plane const qreal distY = depth * -sin( angleInRad ); return QPointF( point.x() + distX, point.y() + distY ); } /** * Returns the shadow color for a given color, depending on the angle of rotation * * @param color The color to calculate the shadow color for * @param angle The angle that the colored area is rotated by */ QColor StockDiagram::Private::ThreeDPainter::calcShadowColor( const QColor &color, qreal angle ) const { // The shadow factor determines to how many percent the brightness // of the color can be reduced. That is, the darkest shadow color // is color * shadowFactor. const qreal shadowFactor = 0.5; const qreal sinAngle = 1.0 - qAbs( sin( DEGTORAD( angle ) ) ) * shadowFactor; return QColor( qRound( color.red() * sinAngle ), qRound( color.green() * sinAngle ), qRound( color.blue() * sinAngle ) ); } /** * Draws a 2D line in 3D space by painting it with a z-coordinate of props.depth / 2.0 * * @param line The line to draw * @param pen The pen to use to draw the line * @param props The 3D properties to draw the line with * @return The drawn line, but with a width of 2px, as a polygon */ QPolygonF StockDiagram::Private::ThreeDPainter::drawTwoDLine( const QLineF &line, const QPen &pen, const ThreeDProperties &props ) { // Restores the painting properties when destroyed PainterSaver painterSaver( painter ); // The z coordinate to use (i.e., at what depth to draw the line) const qreal z = props.depth / 2.0; // Projec the 2D points of the line in 3D const QPointF deepP1 = projectPoint( line.p1(), z, props.angle ); const QPointF deepP2 = projectPoint( line.p2(), z, props.angle ); // The drawn line with a width of 2px QPolygonF threeDArea; // The offset of the line "borders" from the center to each side const QPointF offset( 0.0, 1.0 ); threeDArea << deepP1 - offset << deepP2 - offset << deepP1 + offset << deepP2 + offset << deepP1 - offset; painter->setPen( pen ); painter->drawLine( QLineF( deepP1, deepP2 ) ); return threeDArea; } /** * Draws an ordinary line in 3D by expanding it in the z-axis by the given depth. * * @param line The line to draw * @param brush The brush to fill the resulting polygon with * @param pen The pen to paint the borders of the resulting polygon with * @param props The 3D properties to draw the line with * @return The 3D shape drawn */ QPolygonF StockDiagram::Private::ThreeDPainter::drawThreeDLine( const QLineF &line, const QBrush &brush, const QPen &pen, const ThreeDProperties &props ) { // Restores the painting properties when destroyed PainterSaver painterSaver( painter ); const QPointF p1 = line.p1(); const QPointF p2 = line.p2(); // Project the 2D points of the line in 3D const QPointF deepP1 = projectPoint( p1, props.depth, props.angle ); const QPointF deepP2 = projectPoint( p2, props.depth, props.angle ); // The result is a 3D representation of the 2D line QPolygonF threeDArea; threeDArea << p1 << p2 << deepP2 << deepP1 << p1; // Use shadow colors if ThreeDProperties::useShadowColors is set // Note: Setting a new color on a brush or pen does not effect gradients or textures if ( props.useShadowColors ) { QBrush shadowBrush( brush ); QPen shadowPen( pen ); shadowBrush.setColor( calcShadowColor( brush.color(), props.angle ) ); shadowPen.setColor( calcShadowColor( pen.color(), props.angle ) ); painter->setBrush( shadowBrush ); painter->setPen( shadowPen ); } else { painter->setBrush( brush ); painter->setPen( pen ); } painter->drawPolygon( threeDArea ); return threeDArea; } /** * Draws a 3D cuboid by extending a 2D rectangle in the z-axis * * @param rect The rectangle to draw * @param brush The brush fill the surfaces of the cuboid with * @param pen The pen to draw the edges with * @param props The 3D properties to use for drawing the cuboid * @return The drawn cuboid as a polygon */ QPolygonF StockDiagram::Private::ThreeDPainter::drawThreeDRect( const QRectF &rect, const QBrush &brush, const QPen &pen, const ThreeDProperties &props ) { // Restores the painting properties when destroyed PainterSaver painterSaver( painter ); // Make sure that the top really is the top const QRectF normalizedRect = rect.normalized(); // Calculate all the four sides of the rectangle const QLineF topSide = QLineF( normalizedRect.topLeft(), normalizedRect.topRight() ); const QLineF bottomSide = QLineF( normalizedRect.bottomLeft(), normalizedRect.bottomRight() ); const QLineF leftSide = QLineF( normalizedRect.topLeft(), normalizedRect.bottomLeft() ); const QLineF rightSide = QLineF( normalizedRect.topRight(), normalizedRect.bottomRight() ); QPolygonF drawnPolygon; // Shorter names are easier on the eyes const qreal angle = props.angle; // Only top and right side is visible if ( angle >= 0.0 && angle < 90.0 ) { drawnPolygon = drawnPolygon.united( drawThreeDLine( topSide, brush, pen, props ) ); drawnPolygon = drawnPolygon.united( drawThreeDLine( rightSide, brush, pen, props ) ); // Only top and left side is visible } else if ( angle >= 90.0 && angle < 180.0 ) { drawnPolygon = drawnPolygon.united( drawThreeDLine( topSide, brush, pen, props ) ); drawnPolygon = drawnPolygon.united( drawThreeDLine( leftSide, brush, pen, props ) ); // Only bottom and left side is visible } else if ( angle >= 180.0 && angle < 270.0 ) { drawnPolygon = drawnPolygon.united( drawThreeDLine( bottomSide, brush, pen, props ) ); drawnPolygon = drawnPolygon.united( drawThreeDLine( leftSide, brush, pen, props ) ); // Only bottom and right side is visible } else if ( angle >= 270.0 && angle <= 360.0 ) { drawnPolygon = drawnPolygon.united( drawThreeDLine( bottomSide, brush, pen, props ) ); drawnPolygon = drawnPolygon.united( drawThreeDLine( rightSide, brush, pen, props ) ); } // Draw the front side painter->setPen( pen ); painter->setBrush( brush ); painter->drawRect( normalizedRect ); return drawnPolygon; } StockDiagram::Private::Private() : AbstractCartesianDiagram::Private() { } StockDiagram::Private::Private( const Private& r ) : AbstractCartesianDiagram::Private( r ) { } StockDiagram::Private::~Private() { } /** * Projects a point onto the coordinate plane * * @param context The context to paint the point in * @point The point to project onto the coordinate plane * @return The projected point */ QPointF StockDiagram::Private::projectPoint( PaintContext *context, const QPointF &point ) const { return context->coordinatePlane()->translate( QPointF( point.x() + 0.5, point.y() ) ); } /** * Projects a candlestick onto the coordinate plane * * @param context The context to paint the candlestick in * @param low The */ QRectF StockDiagram::Private::projectCandlestick( PaintContext *context, const QPointF &open, const QPointF &close, qreal width ) const { const QPointF leftHighPoint = context->coordinatePlane()->translate( QPointF( close.x() + 0.5 - width / 2.0, close.y() ) ); const QPointF rightLowPoint = context->coordinatePlane()->translate( QPointF( open.x() + 0.5 + width / 2.0, open.y() ) ); const QPointF rightHighPoint = context->coordinatePlane()->translate( QPointF( close.x() + 0.5 + width / 2.0, close.y() ) ); return QRectF( leftHighPoint, QSizeF( rightHighPoint.x() - leftHighPoint.x(), rightLowPoint.y() - leftHighPoint.y() ) ); } void StockDiagram::Private::drawOHLCBar( int dataset, const CartesianDiagramDataCompressor::DataPoint &open, const CartesianDiagramDataCompressor::DataPoint &high, const CartesianDiagramDataCompressor::DataPoint &low, const CartesianDiagramDataCompressor::DataPoint &close, PaintContext *context ) { // Note: A row in the model is a column in a StockDiagram const int col = low.index.row(); StockBarAttributes attr = stockDiagram()->stockBarAttributes( col ); ThreeDBarAttributes threeDAttr = stockDiagram()->threeDBarAttributes( col ); const qreal tickLength = attr.tickLength(); const QPointF leftOpenPoint( open.key + 0.5 - tickLength, open.value ); const QPointF rightOpenPoint( open.key + 0.5, open.value ); const QPointF highPoint( high.key + 0.5, high.value ); const QPointF lowPoint( low.key + 0.5, low.value ); const QPointF leftClosePoint( close.key + 0.5, close.value ); const QPointF rightClosePoint( close.key + 0.5 + tickLength, close.value ); bool reversedOrder = false; // If 3D mode is enabled, we have to make sure the z-order is right if ( threeDAttr.isEnabled() ) { const int angle = threeDAttr.angle(); // Z-order is from right to left if ( ( angle >= 0 && angle < 90 ) || ( angle >= 180 && angle < 270 ) ) reversedOrder = true; // Z-order is from left to right if ( ( angle >= 90 && angle < 180 ) || ( angle >= 270 && angle <= 360 ) ) reversedOrder = false; } if ( reversedOrder ) { if ( !open.hidden ) drawLine( dataset, col, leftOpenPoint, rightOpenPoint, context ); // Open marker if ( !low.hidden && !high.hidden ) drawLine( dataset, col, lowPoint, highPoint, context ); // Low-High line if ( !close.hidden ) drawLine( dataset, col, leftClosePoint, rightClosePoint, context ); // Close marker } else { if ( !close.hidden ) drawLine( dataset, col, leftClosePoint, rightClosePoint, context ); // Close marker if ( !low.hidden && !high.hidden ) drawLine( dataset, col, lowPoint, highPoint, context ); // Low-High line if ( !open.hidden ) drawLine( dataset, col, leftOpenPoint, rightOpenPoint, context ); // Open marker } LabelPaintCache lpc; if ( !open.hidden ) { - addLabel( &lpc, diagram->attributesModel()->mapToSource( open.index ), 0, + addLabel( &lpc, diagram->attributesModel()->mapToSource( open.index ), nullptr, PositionPoints( leftOpenPoint ), Position::South, Position::South, open.value ); } if ( !high.hidden ) { - addLabel( &lpc, diagram->attributesModel()->mapToSource( high.index ), 0, + addLabel( &lpc, diagram->attributesModel()->mapToSource( high.index ), nullptr, PositionPoints( highPoint ), Position::South, Position::South, high.value ); } if ( !low.hidden ) { - addLabel( &lpc, diagram->attributesModel()->mapToSource( low.index ), 0, + addLabel( &lpc, diagram->attributesModel()->mapToSource( low.index ), nullptr, PositionPoints( lowPoint ), Position::South, Position::South, low.value ); } if ( !close.hidden ) { - addLabel( &lpc, diagram->attributesModel()->mapToSource( close.index ), 0, + addLabel( &lpc, diagram->attributesModel()->mapToSource( close.index ), nullptr, PositionPoints( rightClosePoint ), Position::South, Position::South, close.value ); } paintDataValueTextsAndMarkers( context, lpc, false ); } /** * Draws a line connecting the low and the high value of an OHLC chart * * @param low The low data point * @param high The high data point * @param context The context to draw the candlestick in */ void StockDiagram::Private::drawCandlestick( int /*dataset*/, const CartesianDiagramDataCompressor::DataPoint &open, const CartesianDiagramDataCompressor::DataPoint &high, const CartesianDiagramDataCompressor::DataPoint &low, const CartesianDiagramDataCompressor::DataPoint &close, PaintContext *context ) { PainterSaver painterSaver( context->painter() ); // Note: A row in the model is a column in a StockDiagram, and the other way around const int row = low.index.row(); const int col = low.index.column(); QPointF bottomCandlestickPoint; QPointF topCandlestickPoint; QBrush brush; QPen pen; bool drawLowerLine; bool drawCandlestick = !open.hidden && !close.hidden; bool drawUpperLine; // Find out if we need to paint a down-trend or up-trend candlestick // and set brush and pen accordingly // Also, determine what the top and bottom points of the candlestick are if ( open.value <= close.value ) { pen = stockDiagram()->upTrendCandlestickPen( row ); brush = stockDiagram()->upTrendCandlestickBrush( row ); bottomCandlestickPoint = QPointF( open.key, open.value ); topCandlestickPoint = QPointF( close.key, close.value ); drawLowerLine = !low.hidden && !open.hidden; drawUpperLine = !low.hidden && !close.hidden; } else { pen = stockDiagram()->downTrendCandlestickPen( row ); brush = stockDiagram()->downTrendCandlestickBrush( row ); bottomCandlestickPoint = QPointF( close.key, close.value ); topCandlestickPoint = QPointF( open.key, open.value ); drawLowerLine = !low.hidden && !close.hidden; drawUpperLine = !low.hidden && !open.hidden; } StockBarAttributes attr = stockDiagram()->stockBarAttributes( col ); ThreeDBarAttributes threeDAttr = stockDiagram()->threeDBarAttributes( col ); const QPointF lowPoint = projectPoint( context, QPointF( low.key, low.value ) ); const QPointF highPoint = projectPoint( context, QPointF( high.key, high.value ) ); const QLineF lowerLine = QLineF( lowPoint, projectPoint( context, bottomCandlestickPoint ) ); const QLineF upperLine = QLineF( projectPoint( context, topCandlestickPoint ), highPoint ); // Convert the data point into coordinates on the coordinate plane QRectF candlestick = projectCandlestick( context, bottomCandlestickPoint, topCandlestickPoint, attr.candlestickWidth() ); // Remember the drawn polygon to add it to the ReverseMapper later QPolygonF drawnPolygon; // Use the ThreeDPainter class to draw a 3D candlestick if ( threeDAttr.isEnabled() ) { ThreeDPainter threeDPainter( context->painter() ); ThreeDPainter::ThreeDProperties threeDProps; threeDProps.depth = threeDAttr.depth(); threeDProps.angle = threeDAttr.angle(); threeDProps.useShadowColors = threeDAttr.useShadowColors(); // If the perspective angle is within [0,180], we paint from bottom to top, // otherwise from top to bottom to ensure the correct z order if ( threeDProps.angle > 0.0 && threeDProps.angle < 180.0 ) { if ( drawLowerLine ) drawnPolygon = threeDPainter.drawTwoDLine( lowerLine, pen, threeDProps ); if ( drawCandlestick ) drawnPolygon = threeDPainter.drawThreeDRect( candlestick, brush, pen, threeDProps ); if ( drawUpperLine ) drawnPolygon = threeDPainter.drawTwoDLine( upperLine, pen, threeDProps ); } else { if ( drawUpperLine ) drawnPolygon = threeDPainter.drawTwoDLine( upperLine, pen, threeDProps ); if ( drawCandlestick ) drawnPolygon = threeDPainter.drawThreeDRect( candlestick, brush, pen, threeDProps ); if ( drawLowerLine ) drawnPolygon = threeDPainter.drawTwoDLine( lowerLine, pen, threeDProps ); } } else { QPainter *const painter = context->painter(); painter->setBrush( brush ); painter->setPen( pen ); if ( drawLowerLine ) painter->drawLine( lowerLine ); if ( drawUpperLine ) painter->drawLine( upperLine ); if ( drawCandlestick ) painter->drawRect( candlestick ); // The 2D representation is the projected candlestick itself drawnPolygon = candlestick; // FIXME: Add lower and upper line to reverse mapper } LabelPaintCache lpc; if ( !low.hidden ) - addLabel( &lpc, diagram->attributesModel()->mapToSource( low.index ), 0, + addLabel( &lpc, diagram->attributesModel()->mapToSource( low.index ), nullptr, PositionPoints( lowPoint ), Position::South, Position::South, low.value ); if ( drawCandlestick ) { // Both, the open as well as the close value are represented by this candlestick reverseMapper.addPolygon( row, openValueColumn(), drawnPolygon ); reverseMapper.addPolygon( row, closeValueColumn(), drawnPolygon ); - addLabel( &lpc, diagram->attributesModel()->mapToSource( open.index ), 0, + addLabel( &lpc, diagram->attributesModel()->mapToSource( open.index ), nullptr, PositionPoints( candlestick.bottomRight() ), Position::South, Position::South, open.value ); - addLabel( &lpc, diagram->attributesModel()->mapToSource( close.index ), 0, + addLabel( &lpc, diagram->attributesModel()->mapToSource( close.index ), nullptr, PositionPoints( candlestick.topRight() ), Position::South, Position::South, close.value ); } if ( !high.hidden ) - addLabel( &lpc, diagram->attributesModel()->mapToSource( high.index ), 0, + addLabel( &lpc, diagram->attributesModel()->mapToSource( high.index ), nullptr, PositionPoints( highPoint ), Position::South, Position::South, high.value ); paintDataValueTextsAndMarkers( context, lpc, false ); } /** * Draws a line connecting two points * * @param col The column of the diagram to paint the line in * @param point1 The first point * @param point2 The second point * @param context The context to draw the low-high line in */ void StockDiagram::Private::drawLine( int dataset, int col, const QPointF &point1, const QPointF &point2, PaintContext *context ) { PainterSaver painterSaver( context->painter() ); // A row in the model is a column in the diagram const int modelRow = col; const int modelCol = 0; const QPen pen = diagram->pen( dataset ); const QBrush brush = diagram->brush( dataset ); const ThreeDBarAttributes threeDBarAttr = stockDiagram()->threeDBarAttributes( col ); QPointF transP1 = context->coordinatePlane()->translate( point1 ); QPointF transP2 = context->coordinatePlane()->translate( point2 ); QLineF line = QLineF( transP1, transP2 ); if ( threeDBarAttr.isEnabled() ) { ThreeDPainter::ThreeDProperties threeDProps; threeDProps.angle = threeDBarAttr.angle(); threeDProps.depth = threeDBarAttr.depth(); threeDProps.useShadowColors = threeDBarAttr.useShadowColors(); ThreeDPainter painter( context->painter() ); reverseMapper.addPolygon( modelCol, modelRow, painter.drawThreeDLine( line, brush, pen, threeDProps ) ); } else { context->painter()->setPen( pen ); //context->painter()->setBrush( brush ); reverseMapper.addLine( modelCol, modelRow, transP1, transP2 ); context->painter()->drawLine( line ); } } /** * Returns the column of the open value in the model * * @return The column of the open value */ int StockDiagram::Private::openValueColumn() const { // Return an invalid column if diagram has no open values return type == HighLowClose ? -1 : 0; } /** * Returns the column of the high value in the model * * @return The column of the high value */ int StockDiagram::Private::highValueColumn() const { return type == HighLowClose ? 0 : 1; } /** * Returns the column of the low value in the model * * @return The column of the low value */ int StockDiagram::Private::lowValueColumn() const { return type == HighLowClose ? 1 : 2; } /** * Returns the column of the close value in the model * * @return The column of the close value */ int StockDiagram::Private::closeValueColumn() const { return type == HighLowClose ? 2 : 3; } diff --git a/src/KChart/KChartAbstractAreaBase.cpp b/src/KChart/KChartAbstractAreaBase.cpp index 1576443..769849b 100644 --- a/src/KChart/KChartAbstractAreaBase.cpp +++ b/src/KChart/KChartAbstractAreaBase.cpp @@ -1,235 +1,235 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartAbstractAreaBase.h" #include "KChartAbstractAreaBase_p.h" #include #include #include #include "KChartPainterSaver_p.h" #include "KChartPrintingParameters.h" #include "KChartMath_p.h" #include using namespace KChart; AbstractAreaBase::Private::Private() : visible( true ) { init(); } AbstractAreaBase::Private::~Private() {} void AbstractAreaBase::Private::init() { } AbstractAreaBase::AbstractAreaBase() : _d( new Private() ) { } AbstractAreaBase::~AbstractAreaBase() { - delete _d; _d = 0; + delete _d; _d = nullptr; } void AbstractAreaBase::init() { } #define d d_func() bool AbstractAreaBase::compare( const AbstractAreaBase* other ) const { if ( other == this ) return true; if ( !other ) { return false; } return (frameAttributes() == other->frameAttributes()) && (backgroundAttributes() == other->backgroundAttributes()); } void AbstractAreaBase::alignToReferencePoint( const RelativePosition& position ) { Q_UNUSED( position ); // PENDING(kalle) FIXME qWarning( "Sorry, not implemented: void AbstractAreaBase::alignToReferencePoint( const RelativePosition& position )" ); } void AbstractAreaBase::setFrameAttributes( const FrameAttributes &a ) { if ( d->frameAttributes == a ) return; d->frameAttributes = a; positionHasChanged(); } FrameAttributes AbstractAreaBase::frameAttributes() const { return d->frameAttributes; } void AbstractAreaBase::setBackgroundAttributes( const BackgroundAttributes &a ) { if ( d->backgroundAttributes == a ) return; d->backgroundAttributes = a; positionHasChanged(); } BackgroundAttributes AbstractAreaBase::backgroundAttributes() const { return d->backgroundAttributes; } /* static */ void AbstractAreaBase::paintBackgroundAttributes( QPainter& painter, const QRect& rect, const KChart::BackgroundAttributes& attributes ) { if ( !attributes.isVisible() ) return; /* first draw the brush (may contain a pixmap)*/ if ( Qt::NoBrush != attributes.brush().style() ) { KChart::PainterSaver painterSaver( &painter ); painter.setPen( Qt::NoPen ); const QPointF newTopLeft( painter.deviceMatrix().map( rect.topLeft() ) ); painter.setBrushOrigin( newTopLeft ); painter.setBrush( attributes.brush() ); painter.drawRect( rect.adjusted( 0, 0, -1, -1 ) ); } /* next draw the backPixmap over the brush */ if ( !attributes.pixmap().isNull() && attributes.pixmapMode() != BackgroundAttributes::BackgroundPixmapModeNone ) { QPointF ol = rect.topLeft(); if ( BackgroundAttributes::BackgroundPixmapModeCentered == attributes.pixmapMode() ) { ol.setX( rect.center().x() - attributes.pixmap().width() / 2 ); ol.setY( rect.center().y() - attributes.pixmap().height()/ 2 ); painter.drawPixmap( ol, attributes.pixmap() ); } else { QMatrix m; qreal zW = (qreal)rect.width() / (qreal)attributes.pixmap().width(); qreal zH = (qreal)rect.height() / (qreal)attributes.pixmap().height(); switch ( attributes.pixmapMode() ) { case BackgroundAttributes::BackgroundPixmapModeScaled: { qreal z; z = qMin( zW, zH ); m.scale( z, z ); } break; case BackgroundAttributes::BackgroundPixmapModeStretched: m.scale( zW, zH ); break; default: ; // Cannot happen, previously checked } QPixmap pm = attributes.pixmap().transformed( m ); ol.setX( rect.center().x() - pm.width() / 2 ); ol.setY( rect.center().y() - pm.height()/ 2 ); painter.drawPixmap( ol, pm ); } } } /* static */ void AbstractAreaBase::paintFrameAttributes( QPainter& painter, const QRect& rect, const KChart::FrameAttributes& attributes ) { if ( !attributes.isVisible() ) return; // Note: We set the brush to NoBrush explicitly here. // Otherwise we might get a filled rectangle, so any // previously drawn background would be overwritten by that area. const QPen oldPen( painter.pen() ); const QBrush oldBrush( painter.brush() ); painter.setPen( PrintingParameters::scalePen( attributes.pen() ) ); painter.setBrush( Qt::NoBrush ); painter.drawRoundedRect( rect.adjusted( 0, 0, -1, -1 ), attributes.cornerRadius(), attributes.cornerRadius() ); painter.setBrush( oldBrush ); painter.setPen( oldPen ); } void AbstractAreaBase::paintBackground( QPainter& painter, const QRect& rect ) { - Q_ASSERT_X ( d != 0, "AbstractAreaBase::paintBackground()", + Q_ASSERT_X ( d != nullptr, "AbstractAreaBase::paintBackground()", "Private class was not initialized!" ); PainterSaver painterSaver( &painter ); const qreal radius = d->frameAttributes.cornerRadius(); QPainterPath path; path.addRoundedRect( rect.adjusted( 0, 0, -1, -1 ), radius, radius ); painter.setClipPath(path); paintBackgroundAttributes( painter, rect, d->backgroundAttributes ); } void AbstractAreaBase::paintFrame( QPainter& painter, const QRect& rect ) { - Q_ASSERT_X ( d != 0, "AbstractAreaBase::paintFrame()", + Q_ASSERT_X ( d != nullptr, "AbstractAreaBase::paintFrame()", "Private class was not initialized!" ); paintFrameAttributes( painter, rect, d->frameAttributes ); } void AbstractAreaBase::getFrameLeadings(int& left, int& top, int& right, int& bottom ) const { int padding = 0; if ( d && d->frameAttributes.isVisible() ) { padding = qMax( d->frameAttributes.padding(), 0 ); } left = padding; top = padding; right = padding; bottom = padding; } QRect AbstractAreaBase::innerRect() const { int left; int top; int right; int bottom; getFrameLeadings( left, top, right, bottom ); return QRect ( QPoint( 0, 0 ), areaGeometry().size() ).adjusted( left, top, -right, -bottom ); } void AbstractAreaBase::positionHasChanged() { // this bloc left empty intentionally } diff --git a/src/KChart/KChartAbstractAreaWidget.h b/src/KChart/KChartAbstractAreaWidget.h index dedfe88..f65232e 100644 --- a/src/KChart/KChartAbstractAreaWidget.h +++ b/src/KChart/KChartAbstractAreaWidget.h @@ -1,110 +1,110 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTABSTRACTAREAWIDGET_H #define KCHARTABSTRACTAREAWIDGET_H #include #include #include #include #include "KChartAbstractAreaBase.h" namespace KChart { /** * @class AbstractAreaWidget KChartAbstractArea.h * @brief An area in the chart with a background, a frame, etc. * * AbstractAreaWidget is the base for all widget classes that have * a set of background attributes and frame attributes, such as * KChart::Chart and KChart::Legend. */ class KCHART_EXPORT AbstractAreaWidget : public QWidget, public AbstractAreaBase { Q_OBJECT Q_DISABLE_COPY( AbstractAreaWidget ) KCHART_DECLARE_PRIVATE_DERIVED_QWIDGET( AbstractAreaWidget ) public: - explicit AbstractAreaWidget( QWidget* parent = 0 ); + explicit AbstractAreaWidget( QWidget* parent = nullptr ); /** * @brief Draws the background and frame, then calls paint(). * * In most cases there is no need to overwrite this method in a derived * class, but you would overwrite paint() instead. * @sa paint */ void paintEvent( QPaintEvent* event ) Q_DECL_OVERRIDE; /** * @brief Draws the background and frame, then calls paint(). * * In most cases there is no need to overwrite this method in a derived * class, but you would overwrite paint() instead. */ virtual void paintIntoRect( QPainter& painter, const QRect& rect ); /** * Overwrite this to paint the inner contents of your widget. * * @note When overriding this method, please let your widget draw * itself at the top/left corner of the painter. You should call rect() * (or width(), height(), resp.) to find the drawable area's size: * While the paint() method is being executed the frame of the widget * is outside of its rect(), so you can use all of rect() for * your custom drawing! * @sa paint, paintIntoRect */ virtual void paint( QPainter* painter ) = 0; /** * Call paintAll, if you want the background and the frame to be drawn * before the normal paint() is invoked automatically. */ void paintAll( QPainter& painter ); /** * Call this to trigger an unconditional re-building of the widget's internals. */ virtual void forceRebuild(); /** * Call this to trigger an conditional re-building of the widget's internals. * * e.g. AbstractAreaWidget call this, before calling layout()->setGeometry() */ virtual void needSizeHint(); virtual void resizeLayout( const QSize& ); Q_SIGNALS: void positionChanged( AbstractAreaWidget * ); protected: virtual ~AbstractAreaWidget() ; QRect areaGeometry() const Q_DECL_OVERRIDE; void positionHasChanged() Q_DECL_OVERRIDE; }; } #endif // KCHARTABSTRACTAREAWIDGET_H diff --git a/src/KChart/KChartAbstractAxis.cpp b/src/KChart/KChartAbstractAxis.cpp index 2380d21..62762a6 100644 --- a/src/KChart/KChartAbstractAxis.cpp +++ b/src/KChart/KChartAbstractAxis.cpp @@ -1,259 +1,259 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartAbstractAxis.h" #include "KChartAbstractAxis_p.h" #include "KChartAbstractDiagram.h" #include "KChartAbstractCartesianDiagram.h" #include "KChartEnums.h" #include "KChartMeasure.h" #include "KChartMath_p.h" using namespace KChart; #define d d_func() AbstractAxis::Private::Private( AbstractDiagram* diagram, AbstractAxis* axis ) - : observer( 0 ) + : observer( nullptr ) , mDiagram( diagram ) , mAxis( axis ) { // Note: We do NOT call setDiagram( diagram, axis ); // but it is called in AbstractAxis::delayedInit() instead! } AbstractAxis::Private::~Private() { delete observer; - observer = 0; + observer = nullptr; } bool AbstractAxis::Private::setDiagram( AbstractDiagram* diagram_, bool delayedInit ) { AbstractDiagram* diagram = delayedInit ? mDiagram : diagram_; if ( delayedInit ) { - mDiagram = 0; + mDiagram = nullptr; } // do not set a diagram again that was already set if ( diagram && ((diagram == mDiagram) || secondaryDiagrams.contains( diagram )) ) return false; bool bNewDiagramStored = false; if ( ! mDiagram ) { mDiagram = diagram; delete observer; if ( mDiagram ) { observer = new DiagramObserver( mDiagram, mAxis ); const bool con = connect( observer, SIGNAL(diagramDataChanged(AbstractDiagram*)), mAxis, SIGNAL(coordinateSystemChanged()) ); Q_UNUSED( con ) Q_ASSERT( con ); bNewDiagramStored = true; } else { - observer = 0; + observer = nullptr; } } else { if ( diagram ) secondaryDiagrams.enqueue( diagram ); } return bNewDiagramStored; } void AbstractAxis::Private::unsetDiagram( AbstractDiagram* diagram ) { if ( diagram == mDiagram ) { - mDiagram = 0; + mDiagram = nullptr; delete observer; - observer = 0; + observer = nullptr; } else { secondaryDiagrams.removeAll( diagram ); } if ( !secondaryDiagrams.isEmpty() ) { AbstractDiagram *nextDiagram = secondaryDiagrams.dequeue(); setDiagram( nextDiagram ); } } bool AbstractAxis::Private::hasDiagram( AbstractDiagram* diagram ) const { return diagram == mDiagram || secondaryDiagrams.contains( diagram ); } void AbstractAxis::Private::updateLayouts() { if ( CartesianAxis* cartesianAxis = qobject_cast< CartesianAxis* >( mAxis ) ) { cartesianAxis->layoutPlanes(); } else { mAxis->update(); } } AbstractAxis::AbstractAxis ( AbstractDiagram* diagram ) : AbstractArea( new Private( diagram, this ) ) { init(); QTimer::singleShot(0, this, SLOT(delayedInit())); } AbstractAxis::~AbstractAxis() { - d->mDiagram = 0; + d->mDiagram = nullptr; d->secondaryDiagrams.clear(); } void AbstractAxis::init() { Measure m( 14, KChartEnums::MeasureCalculationModeAuto, KChartEnums::MeasureOrientationAuto ); d->textAttributes.setFontSize( m ); m.setValue( 6 ); m.setCalculationMode( KChartEnums::MeasureCalculationModeAbsolute ); d->textAttributes.setMinimalFontSize( m ); if ( d->diagram() ) createObserver( d->diagram() ); } void AbstractAxis::delayedInit() { // We call setDiagram() here, because the c'tor of Private // only has stored the pointers, but it did not call setDiagram(). if ( d ) - d->setDiagram( 0, true /* delayedInit */ ); + d->setDiagram( nullptr, true /* delayedInit */ ); } bool AbstractAxis::compare( const AbstractAxis* other ) const { if ( other == this ) { return true; } if ( !other ) { return false; } return ( static_cast(this)->compare( other ) ) && (textAttributes() == other->textAttributes()) && (labels() == other->labels()) && (shortLabels() == other->shortLabels()); } const QString AbstractAxis::customizedLabel( const QString& label ) const { return label; } void AbstractAxis::createObserver( AbstractDiagram* diagram ) { d->setDiagram( diagram ); } void AbstractAxis::deleteObserver( AbstractDiagram* diagram ) { d->unsetDiagram( diagram ); } void AbstractAxis::connectSignals() { if ( d->observer ) { const bool con = connect( d->observer, SIGNAL(diagramDataChanged(AbstractDiagram*)), this, SIGNAL(coordinateSystemChanged()) ); Q_UNUSED( con ); Q_ASSERT( con ); } } void AbstractAxis::setTextAttributes( const TextAttributes &a ) { if ( d->textAttributes == a ) return; d->textAttributes = a; d->updateLayouts(); } TextAttributes AbstractAxis::textAttributes() const { return d->textAttributes; } void AbstractAxis::setRulerAttributes( const RulerAttributes &a ) { d->rulerAttributes = a; d->updateLayouts(); } RulerAttributes AbstractAxis::rulerAttributes() const { return d->rulerAttributes; } void AbstractAxis::setLabels( const QStringList& list ) { if ( d->hardLabels == list ) return; d->hardLabels = list; d->updateLayouts(); } QStringList AbstractAxis::labels() const { return d->hardLabels; } void AbstractAxis::setShortLabels( const QStringList& list ) { if ( d->hardShortLabels == list ) return; d->hardShortLabels = list; d->updateLayouts(); } QStringList AbstractAxis::shortLabels() const { return d->hardShortLabels; } const AbstractCoordinatePlane* AbstractAxis::coordinatePlane() const { if ( d->diagram() ) return d->diagram()->coordinatePlane(); - return 0; + return nullptr; } const AbstractDiagram * KChart::AbstractAxis::diagram() const { return d->diagram(); } bool KChart::AbstractAxis::observedBy( AbstractDiagram * diagram ) const { return d->hasDiagram( diagram ); } void KChart::AbstractAxis::update() { if ( d->diagram() ) d->diagram()->update(); } diff --git a/src/KChart/KChartAbstractAxis.h b/src/KChart/KChartAbstractAxis.h index 9871552..a9b50ce 100644 --- a/src/KChart/KChartAbstractAxis.h +++ b/src/KChart/KChartAbstractAxis.h @@ -1,252 +1,252 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTABSTRACTAXIS_H #define KCHARTABSTRACTAXIS_H // #include // #include // #include #include "kchart_export.h" #include "KChartGlobal.h" #include "KChartAbstractArea.h" #include "KChartTextAttributes.h" #include "KChartRulerAttributes.h" QT_BEGIN_NAMESPACE class QPainter; class QSizeF; QT_END_NAMESPACE namespace KChart { class Area; class AbstractCoordinatePlane; class PaintContext; class AbstractDiagram; /** * The base class for axes. * * For being useful, axes need to be assigned to a diagram, see * AbstractCartesianDiagram::addAxis and AbstractCartesianDiagram::takeAxis. * * \sa PolarAxis, AbstractCartesianDiagram */ class KCHART_EXPORT AbstractAxis : public AbstractArea { Q_OBJECT Q_DISABLE_COPY( AbstractAxis ) KCHART_DECLARE_PRIVATE_DERIVED_PARENT( AbstractAxis, AbstractDiagram* ) public: - explicit AbstractAxis( AbstractDiagram* diagram = 0 ); + explicit AbstractAxis( AbstractDiagram* diagram = nullptr ); virtual ~AbstractAxis(); // FIXME implement when code os ready for it: // virtual Area* clone() const = 0; // FIXME (Mirko) readd when needed // void copyRelevantDetailsFrom( const KChartAxis* axis ); /* virtual void paint( PaintContext* ) const = 0; virtual QSize sizeHint() const = 0;*/ //virtual void paintEvent( QPaintEvent* event) = 0; /** * \brief Reimplement this method if you want to adjust axis labels * before they are printed. * * KChart is calling this method immediately before drawing the * text, this means: What you return here will be drawn without * further modifications. * * \param label The text of the label as KChart has calculated it * automatically (or as it was taken from a QStringList provided * by you, resp.) * * \note If you reimplement this method in a subclass of KChart::CartesianAxis, * and your reimplementation's return value depends on data other than @p label * (so KChart will not know when it changes), you must manually ensure that * layouts are adapted to any changed sizes of the axis labels. To do that, * call KChart::CartesianAxis::layoutPlanes() from your reimplementation when * you know that the external data changed and it will change label sizes - * or when you cannot exclude that. * * \return The text to be drawn. By default this is the same as \c label. */ virtual const QString customizedLabel( const QString& label ) const; /** * Returns true if both axes have the same settings. */ bool compare( const AbstractAxis* other ) const; /** * \internal * * Method invoked by AbstractCartesianDiagram::addAxis(). * * You should not call this function, unless you know exactly, * what you are doing. * * \sa connectSignals(), AbstractCartesianDiagram::addAxis() */ void createObserver( AbstractDiagram* diagram ); /** * \internal * * Method invoked by AbstractCartesianDiagram::takeAxis(). * * You should not call this function, unless you know exactly, * what you are doing. * * \sa AbstractCartesianDiagram::takeAxis() */ void deleteObserver( AbstractDiagram* diagram ); const AbstractDiagram* diagram() const; bool observedBy( AbstractDiagram* diagram ) const; /** * Wireing the signal/slot connections. * * This method gets called automatically, each time, when you assign * the axis to a diagram, either by passing a diagram* to the c'tor, * or by calling the diagram's setAxis method, resp. * * If overwriting this method in derived classes, make sure to call * this base method AbstractAxis::connectSignals(), so your axis * gets connected to the diagram's built-in signals. * * \sa AbstractCartesianDiagram::addAxis() */ virtual void connectSignals(); /** \brief Use this to specify the text attributes to be used for axis labels. By default, the reference area will be set at painting time. It will be the then-valid coordinate plane's parent widget, so normally, it will be the KChart::Chart. Thus the labels of all of your axes in all of your diagrams within that Chart will be drawn in same font size, by default. \sa textAttributes, setLabels */ void setTextAttributes( const TextAttributes &a ); /** \brief Returns the text attributes to be used for axis labels. \sa setTextAttributes */ TextAttributes textAttributes() const; /** \brief Use this to specify the attributes used to paint the axis ruler Every axis has a default set of ruler attributes that is exactly the same among them. Use this method to specify your own attributes. \sa rulerAttributes */ void setRulerAttributes( const RulerAttributes &a ); /** \brief Returns the attributes to be used for painting the rulers \sa setRulerAttributes */ RulerAttributes rulerAttributes() const; /** \brief Use this to specify your own set of strings, to be used as axis labels. Labels specified via setLabels take precedence: If a non-empty list is passed, KChart will use these strings as axis labels, instead of calculating them. If you pass a smaller number of strings than the number of labels drawn at this axis, KChart will repeat the strings until all labels are drawn. As an example you could specify the seven days of the week as abscissa labels, which would be repeatedly used then. By passing an empty QStringList you can reset the default behaviour. \sa labels, setShortLabels */ void setLabels( const QStringList& list ); /** Returns a list of strings, that are used as axis labels, as set via setLabels. \sa setLabels */ QStringList labels() const; /** \brief Use this to specify your own set of strings, to be used as axis labels, in case the normal labels are too long. \note Setting done via setShortLabels will be ignored, if you did not pass a non-empty string list via setLabels too! By passing an empty QStringList you can reset the default behaviour. \sa shortLabels, setLabels */ void setShortLabels( const QStringList& list ); /** Returns a list of strings, that are used as axis labels, as set via setShortLabels. \note Setting done via setShortLabels will be ignored, if you did not pass a non-empty string list via setLabels too! \sa setShortLabels */ QStringList shortLabels() const; void setGeometry( const QRect& rect ) Q_DECL_OVERRIDE = 0; QRect geometry() const Q_DECL_OVERRIDE = 0; /** \brief Convenience function, returns the coordinate plane, in which this axis is used. If the axis is not used in a coordinate plane, the return value is Zero. */ const AbstractCoordinatePlane* coordinatePlane() const; protected Q_SLOTS: /** called for initializing after the c'tor has completed */ virtual void delayedInit(); public Q_SLOTS: void update(); Q_SIGNALS: void coordinateSystemChanged(); }; } #endif // KCHARTABSTRACTAXIS_H diff --git a/src/KChart/KChartAbstractCoordinatePlane.cpp b/src/KChart/KChartAbstractCoordinatePlane.cpp index 94d556e..3904fb8 100644 --- a/src/KChart/KChartAbstractCoordinatePlane.cpp +++ b/src/KChart/KChartAbstractCoordinatePlane.cpp @@ -1,450 +1,450 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartAbstractCoordinatePlane.h" #include "KChartAbstractCoordinatePlane_p.h" #include "KChartChart.h" #include "KChartGridAttributes.h" #include #include #include #include using namespace KChart; #define d d_func() AbstractCoordinatePlane::Private::Private() : AbstractArea::Private() - , parent( 0 ) - , grid( 0 ) - , referenceCoordinatePlane( 0 ) + , parent( nullptr ) + , grid( nullptr ) + , referenceCoordinatePlane( nullptr ) , enableCornerSpacers( true ) , enableRubberBandZooming( false ) - , rubberBand( 0 ) + , rubberBand( nullptr ) { // this bloc left empty intentionally } AbstractCoordinatePlane::AbstractCoordinatePlane ( KChart::Chart* parent ) : AbstractArea ( new Private() ) { d->parent = parent; d->init(); } AbstractCoordinatePlane::~AbstractCoordinatePlane() { emit destroyedCoordinatePlane( this ); } void AbstractCoordinatePlane::init() { d->initialize(); // virtual method to init the correct grid: cartesian, polar, ... connect( this, SIGNAL(internal_geometryChanged(QRect,QRect)), this, SIGNAL(geometryChanged(QRect,QRect)), Qt::QueuedConnection ); } void AbstractCoordinatePlane::addDiagram ( AbstractDiagram* diagram ) { // diagrams are invisible and paint through their paint() method diagram->hide(); d->diagrams.append( diagram ); diagram->setParent( d->parent ); diagram->setCoordinatePlane( this ); layoutDiagrams(); layoutPlanes(); // there might be new axes, etc connect( diagram, SIGNAL(modelsChanged()), this, SLOT(layoutPlanes()) ); connect( diagram, SIGNAL(modelDataChanged()), this, SLOT(update()) ); connect( diagram, SIGNAL(modelDataChanged()), this, SLOT(relayout()) ); connect( this, SIGNAL(boundariesChanged()), diagram, SIGNAL(boundariesChanged()) ); update(); emit boundariesChanged(); } /*virtual*/ void AbstractCoordinatePlane::replaceDiagram ( AbstractDiagram* diagram, AbstractDiagram* oldDiagram_ ) { if ( diagram && oldDiagram_ != diagram ) { AbstractDiagram* oldDiagram = oldDiagram_; if ( d->diagrams.count() ) { if ( ! oldDiagram ) { oldDiagram = d->diagrams.first(); if ( oldDiagram == diagram ) return; } takeDiagram( oldDiagram ); } delete oldDiagram; addDiagram( diagram ); layoutDiagrams(); layoutPlanes(); // there might be new axes, etc update(); } } /*virtual*/ void AbstractCoordinatePlane::takeDiagram ( AbstractDiagram* diagram ) { const int idx = d->diagrams.indexOf( diagram ); if ( idx != -1 ) { d->diagrams.removeAt( idx ); - diagram->setParent( 0 ); - diagram->setCoordinatePlane( 0 ); + diagram->setParent( nullptr ); + diagram->setCoordinatePlane( nullptr ); disconnect( diagram, SIGNAL(modelsChanged()), this, SLOT(layoutPlanes()) ); disconnect( diagram, SIGNAL(modelDataChanged()), this, SLOT(update()) ); disconnect( diagram, SIGNAL(modelDataChanged()), this, SLOT(relayout()) ); layoutDiagrams(); update(); } } AbstractDiagram* AbstractCoordinatePlane::diagram() { if ( d->diagrams.isEmpty() ) { - return 0; + return nullptr; } else { return d->diagrams.first(); } } AbstractDiagramList AbstractCoordinatePlane::diagrams() { return d->diagrams; } ConstAbstractDiagramList AbstractCoordinatePlane::diagrams() const { ConstAbstractDiagramList list; #ifndef QT_NO_STL qCopy( d->diagrams.begin(), d->diagrams.end(), std::back_inserter( list ) ); #else Q_FOREACH( AbstractDiagram * a, d->diagrams ) list.push_back( a ); #endif return list; } void KChart::AbstractCoordinatePlane::setGlobalGridAttributes( const GridAttributes& a ) { d->gridAttributes = a; update(); } GridAttributes KChart::AbstractCoordinatePlane::globalGridAttributes() const { return d->gridAttributes; } KChart::DataDimensionsList KChart::AbstractCoordinatePlane::gridDimensionsList() { return d->grid->updateData( this ); } void KChart::AbstractCoordinatePlane::setGridNeedsRecalculate() { d->grid->setNeedRecalculate(); } void KChart::AbstractCoordinatePlane::setReferenceCoordinatePlane( AbstractCoordinatePlane * plane ) { d->referenceCoordinatePlane = plane; } AbstractCoordinatePlane * KChart::AbstractCoordinatePlane::referenceCoordinatePlane( ) const { return d->referenceCoordinatePlane; } void KChart::AbstractCoordinatePlane::setParent( KChart::Chart* parent ) { d->parent = parent; } const KChart::Chart* KChart::AbstractCoordinatePlane::parent() const { return d->parent; } KChart::Chart* KChart::AbstractCoordinatePlane::parent() { return d->parent; } /* pure virtual in QLayoutItem */ bool KChart::AbstractCoordinatePlane::isEmpty() const { return false; // never empty! // coordinate planes with no associated diagrams // are showing a default grid of ()1..10, 1..10) stepWidth 1 } /* pure virtual in QLayoutItem */ Qt::Orientations KChart::AbstractCoordinatePlane::expandingDirections() const { return Qt::Vertical | Qt::Horizontal; } /* pure virtual in QLayoutItem */ QSize KChart::AbstractCoordinatePlane::maximumSize() const { // No maximum size set. Especially not parent()->size(), we are not layouting // to the parent widget's size when using Chart::paint()! return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX); } /* pure virtual in QLayoutItem */ QSize KChart::AbstractCoordinatePlane::minimumSize() const { return QSize(60, 60); // this default can be overwritten by derived classes } /* pure virtual in QLayoutItem */ QSize KChart::AbstractCoordinatePlane::sizeHint() const { // we return our maxiumu (which is the full size of the Chart) // even if we know the plane will be smaller return maximumSize(); } /* pure virtual in QLayoutItem */ void KChart::AbstractCoordinatePlane::setGeometry( const QRect& r ) { if ( d->geometry != r ) { // inform the outside word by Signal geometryChanged() // via a queued connection to internal_geometryChanged() emit internal_geometryChanged( d->geometry, r ); d->geometry = r; // Note: We do *not* call update() here // because it would invoke KChart::update() recursively. } } /* pure virtual in QLayoutItem */ QRect KChart::AbstractCoordinatePlane::geometry() const { return d->geometry; } void KChart::AbstractCoordinatePlane::update() { //qDebug("KChart::AbstractCoordinatePlane::update() called"); emit needUpdate(); } void KChart::AbstractCoordinatePlane::relayout() { //qDebug("KChart::AbstractCoordinatePlane::relayout() called"); emit needRelayout(); } void KChart::AbstractCoordinatePlane::layoutPlanes() { //qDebug("KChart::AbstractCoordinatePlane::relayout() called"); emit needLayoutPlanes(); } void KChart::AbstractCoordinatePlane::setRubberBandZoomingEnabled( bool enable ) { d->enableRubberBandZooming = enable; - if ( !enable && d->rubberBand != 0 ) + if ( !enable && d->rubberBand != nullptr ) { delete d->rubberBand; - d->rubberBand = 0; + d->rubberBand = nullptr; } } bool KChart::AbstractCoordinatePlane::isRubberBandZoomingEnabled() const { return d->enableRubberBandZooming; } void KChart::AbstractCoordinatePlane::setCornerSpacersEnabled( bool enable ) { if ( d->enableCornerSpacers == enable ) return; d->enableCornerSpacers = enable; emit needRelayout(); } bool KChart::AbstractCoordinatePlane::isCornerSpacersEnabled() const { return d->enableCornerSpacers; } void KChart::AbstractCoordinatePlane::mousePressEvent( QMouseEvent* event ) { if ( event->button() == Qt::LeftButton ) { - if ( d->enableRubberBandZooming && d->rubberBand == 0 ) + if ( d->enableRubberBandZooming && d->rubberBand == nullptr ) d->rubberBand = new QRubberBand( QRubberBand::Rectangle, qobject_cast< QWidget* >( parent() ) ); - if ( d->rubberBand != 0 ) + if ( d->rubberBand != nullptr ) { d->rubberBandOrigin = event->pos(); d->rubberBand->setGeometry( QRect( event->pos(), QSize() ) ); d->rubberBand->show(); event->accept(); } } else if ( event->button() == Qt::RightButton ) { if ( d->enableRubberBandZooming && !d->rubberBandZoomConfigHistory.isEmpty() ) { // restore the last config from the stack ZoomParameters config = d->rubberBandZoomConfigHistory.pop(); setZoomFactorX( config.xFactor ); setZoomFactorY( config.yFactor ); setZoomCenter( config.center() ); QWidget* const p = qobject_cast< QWidget* >( parent() ); - if ( p != 0 ) + if ( p != nullptr ) p->update(); event->accept(); } } Q_FOREACH( AbstractDiagram * a, d->diagrams ) { a->mousePressEvent( event ); } } void KChart::AbstractCoordinatePlane::mouseDoubleClickEvent( QMouseEvent* event ) { if ( event->button() == Qt::RightButton ) { // othewise the second click gets lost // which is pretty annoying when zooming out fast mousePressEvent( event ); } Q_FOREACH( AbstractDiagram * a, d->diagrams ) { a->mouseDoubleClickEvent( event ); } } void KChart::AbstractCoordinatePlane::mouseReleaseEvent( QMouseEvent* event ) { - if ( d->rubberBand != 0 ) + if ( d->rubberBand != nullptr ) { // save the old config on the stack d->rubberBandZoomConfigHistory.push( ZoomParameters( zoomFactorX(), zoomFactorY(), zoomCenter() ) ); // this is the height/width of the rubber band in pixel space const qreal rubberWidth = static_cast< qreal >( d->rubberBand->width() ); const qreal rubberHeight = static_cast< qreal >( d->rubberBand->height() ); if ( rubberWidth > 0.0 && rubberHeight > 0.0 ) { // this is the center of the rubber band in pixel space const qreal centerX = qFloor( d->rubberBand->geometry().width() / 2.0 + d->rubberBand->geometry().x() ); const qreal centerY = qCeil( d->rubberBand->geometry().height() / 2.0 + d->rubberBand->geometry().y() ); const qreal rubberCenterX = static_cast< qreal >( centerX - geometry().x() ); const qreal rubberCenterY = static_cast< qreal >( centerY - geometry().y() ); // this is the height/width of the plane in pixel space const qreal myWidth = static_cast< qreal >( geometry().width() ); const qreal myHeight = static_cast< qreal >( geometry().height() ); // this describes the new center of zooming, relative to the plane pixel space const qreal newCenterX = rubberCenterX / myWidth / zoomFactorX() + zoomCenter().x() - 0.5 / zoomFactorX(); const qreal newCenterY = rubberCenterY / myHeight / zoomFactorY() + zoomCenter().y() - 0.5 / zoomFactorY(); // this will be the new zoom factor const qreal newZoomFactorX = zoomFactorX() * myWidth / rubberWidth; const qreal newZoomFactorY = zoomFactorY() * myHeight / rubberHeight; // and this the new center const QPointF newZoomCenter( newCenterX, newCenterY ); setZoomFactorX( newZoomFactorX ); setZoomFactorY( newZoomFactorY ); setZoomCenter( newZoomCenter ); } d->rubberBand->parentWidget()->update(); delete d->rubberBand; - d->rubberBand = 0; + d->rubberBand = nullptr; event->accept(); } Q_FOREACH( AbstractDiagram * a, d->diagrams ) { a->mouseReleaseEvent( event ); } } void KChart::AbstractCoordinatePlane::mouseMoveEvent( QMouseEvent* event ) { - if ( d->rubberBand != 0 ) + if ( d->rubberBand != nullptr ) { const QRect normalized = QRect( d->rubberBandOrigin, event->pos() ).normalized(); d->rubberBand->setGeometry( normalized & geometry() ); event->accept(); } Q_FOREACH( AbstractDiagram * a, d->diagrams ) { a->mouseMoveEvent( event ); } } #if defined(Q_COMPILER_MANGLES_RETURN_TYPE) const #endif bool KChart::AbstractCoordinatePlane::isVisiblePoint( const QPointF& point ) const { return d->isVisiblePoint( this, point ); } AbstractCoordinatePlane* KChart::AbstractCoordinatePlane::sharedAxisMasterPlane( QPainter* p ) { Q_UNUSED( p ); return this; } #if !defined(QT_NO_DEBUG_STREAM) #include "KChartEnums.h" QDebug KChart::operator<<( QDebug stream, const DataDimension& r ) { stream << "DataDimension(" << " start=" << r.start << " end=" << r.end << " sequence=" << KChartEnums::granularitySequenceToString( r.sequence ) << " isCalculated=" << r.isCalculated << " calcMode=" << ( r.calcMode == AbstractCoordinatePlane::Logarithmic ? "Logarithmic" : "Linear" ) << " stepWidth=" << r.stepWidth << " subStepWidth=" << r.subStepWidth << " )"; return stream; } #endif #undef d diff --git a/src/KChart/KChartAbstractCoordinatePlane.h b/src/KChart/KChartAbstractCoordinatePlane.h index 2ec6885..9bc6a9a 100644 --- a/src/KChart/KChartAbstractCoordinatePlane.h +++ b/src/KChart/KChartAbstractCoordinatePlane.h @@ -1,443 +1,443 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTABSTRACTCOORDINATEPLANE_H #define KCHARTABSTRACTCOORDINATEPLANE_H #include #include #include "KChartAbstractArea.h" #include "KChartAbstractDiagram.h" #include "KChartEnums.h" namespace KChart { class Chart; class GridAttributes; class DataDimension; typedef QList DataDimensionsList; /** * @brief Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane, TernaryCoordinatePlane */ class KCHART_EXPORT AbstractCoordinatePlane : public AbstractArea { Q_OBJECT KCHART_DECLARE_PRIVATE_DERIVED_PARENT( AbstractCoordinatePlane, Chart* ) friend class AbstractGrid; public: enum AxesCalcMode { Linear, Logarithmic }; protected: - explicit AbstractCoordinatePlane( Chart* parent = 0 ); + explicit AbstractCoordinatePlane( Chart* parent = nullptr ); public: virtual ~AbstractCoordinatePlane(); /** * Adds a diagram to this coordinate plane. * @param diagram The diagram to add. * * \sa replaceDiagram, takeDiagram */ virtual void addDiagram( AbstractDiagram* diagram ); /** * Replaces the old diagram, or appends the * diagram, it there is none yet. * * @param diagram The diagram to be used instead of the old diagram. * This parameter must not be zero, or the method will do nothing. * * @param oldDiagram The diagram to be removed by the new diagram. This * diagram will be deleted automatically. If the parameter is omitted, * the very first diagram will be replaced. In case, there was no * diagram yet, the new diagram will just be added. * * \note If you want to re-use the old diagram, call takeDiagram and * addDiagram, instead of using replaceDiagram. * * \sa addDiagram, takeDiagram */ - virtual void replaceDiagram( AbstractDiagram* diagram, AbstractDiagram* oldDiagram = 0 ); + virtual void replaceDiagram( AbstractDiagram* diagram, AbstractDiagram* oldDiagram = nullptr ); /** * Removes the diagram from the plane, without deleting it. * * The plane no longer owns the diagram, so it is * the caller's responsibility to delete the diagram. * * \sa addDiagram, replaceDiagram */ virtual void takeDiagram( AbstractDiagram* diagram ); /** * @return The first diagram associated with this coordinate plane. */ AbstractDiagram* diagram(); /** * @return The list of diagrams associated with this coordinate plane. */ AbstractDiagramList diagrams(); /** * @return The list of diagrams associated with this coordinate plane. */ ConstAbstractDiagramList diagrams() const; /** * Distribute the available space among the diagrams and axes. */ virtual void layoutDiagrams() = 0; /** * Translate the given point in value space coordinates to a position * in pixel space. * @param diagramPoint The point in value coordinates. * @returns The translated point. */ virtual const QPointF translate( const QPointF& diagramPoint ) const = 0; /** * @return Whether zooming with a rubber band using the mouse is enabled. */ bool isRubberBandZoomingEnabled() const; /** * Enables or disables zooming with a rubber band using the mouse. */ void setRubberBandZoomingEnabled( bool enable ); /** * @return The zoom factor in horizontal direction, that is applied * to all coordinate transformations. */ virtual qreal zoomFactorX() const { return 1.0; } /** * @return The zoom factor in vertical direction, that is applied * to all coordinate transformations. */ virtual qreal zoomFactorY() const { return 1.0; } /** * Sets both zoom factors in one go. * \sa setZoomFactorX,setZoomFactorY */ virtual void setZoomFactors( qreal factorX, qreal factorY ) { Q_UNUSED( factorX ); Q_UNUSED( factorY ); } /** * Sets the zoom factor in horizontal direction, that is applied * to all coordinate transformations. * @param factor The new zoom factor */ virtual void setZoomFactorX( qreal factor ) { Q_UNUSED( factor ); } /** * Sets the zoom factor in vertical direction, that is applied * to all coordinate transformations. * @param factor The new zoom factor */ virtual void setZoomFactorY( qreal factor ) { Q_UNUSED( factor ); } /** * @return The center point (in value coordinates) of the * coordinate plane, that is used for zoom operations. */ virtual QPointF zoomCenter() const { return QPointF(0.0, 0.0); } /** * Set the point (in value coordinates) to be used as the * center point in zoom operations. * @param center The point to use. */ virtual void setZoomCenter( const QPointF& center ) { Q_UNUSED( center ); } /** * Set the grid attributes to be used by this coordinate plane. * To disable grid painting, for example, your code should like this: * \code * GridAttributes ga = plane->globalGridAttributes(); * ga.setGlobalGridVisible( false ); * plane->setGlobalGridAttributes( ga ); * \endcode * \sa globalGridAttributes * \sa CartesianCoordinatePlane::setGridAttributes */ void setGlobalGridAttributes( const GridAttributes & ); /** * @return The grid attributes used by this coordinate plane. * \sa setGlobalGridAttributes * \sa CartesianCoordinatePlane::gridAttributes */ GridAttributes globalGridAttributes() const; /** * Returns the dimensions used for drawing the grid lines. * * Returned data is the result of (cached) grid calculations, * so - if you need that information for your own tasks - make sure to * call again this function after every data modification that has changed * the data range, since grid calculation is based upon the data range, * thus the grid start/end might have changed if the data was changed. * * @note Returned list will contain different numbers of DataDimension, * depending on the kind of coordinate plane used. * For CartesianCoordinatePlane two DataDimension are returned: the first * representing grid lines in X direction (matching the Abscissa axes) * and the second indicating vertical grid lines (or Ordinate axes, resp.). * * @return The dimensions used for drawing the grid lines. * @sa DataDimension */ DataDimensionsList gridDimensionsList(); /** * Set another coordinate plane to be used as the reference plane * for this one. * @param plane The coordinate plane to be used the reference plane * for this one. * @see referenceCoordinatePlane */ void setReferenceCoordinatePlane( AbstractCoordinatePlane * plane ); /** * There are two ways, in which planes can be caused to interact, in * where they are put layouting wise: The first is the reference plane. If * such a reference plane is set, on a plane, it will use the same cell in the * layout as that one. In addition to this, planes can share an axis. In that case * they will be laid out in relation to each other as suggested by the position * of the axis. If, for example Plane1 and Plane2 share an axis at position Left, * that will result in the layout: Axis Plane1 Plane 2, vertically. If Plane1 * also happens to be Plane2's reference plane, both planes are drawn over each * other. The reference plane concept allows two planes to share the same space * even if neither has any axis, and in case there are shared axis, it is used * to decided, whether the planes should be painted on top of each other or * laid out vertically or horizontally next to each other. * @return The reference coordinate plane associated with this one. */ AbstractCoordinatePlane * referenceCoordinatePlane() const; /** * @return Whether this plane should have spacers in the corners * formed by the presence of axes. */ bool isCornerSpacersEnabled() const; /** * Enables or disables the use of spacers in the plane corners. */ void setCornerSpacersEnabled( bool enable ); - virtual AbstractCoordinatePlane* sharedAxisMasterPlane( QPainter* p = 0 ); // KChart 3: const method? + virtual AbstractCoordinatePlane* sharedAxisMasterPlane( QPainter* p = nullptr ); // KChart 3: const method? /** pure virtual in QLayoutItem */ bool isEmpty() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize maximumSize() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize minimumSize() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize sizeHint() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem * * \note Do not call this function directly, unless you know * exactly what you are doing. Geometry management is done * by KChart's internal layouting measures. */ void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QRect geometry() const Q_DECL_OVERRIDE; virtual void mousePressEvent( QMouseEvent* event ); virtual void mouseDoubleClickEvent( QMouseEvent* event ); virtual void mouseMoveEvent( QMouseEvent* event ); virtual void mouseReleaseEvent( QMouseEvent* event ); /** * Called internally by KChart::Chart */ void setParent( Chart* parent ); Chart* parent(); const Chart* parent() const; /** * Tests, if a point is visible on the coordinate plane. * * \note Before calling this function the point must have been translated into coordinate plane space. */ #if defined(Q_COMPILER_MANGLES_RETURN_TYPE) const bool isVisiblePoint( const QPointF& point ) const; #else bool isVisiblePoint( const QPointF& point ) const; #endif public Q_SLOTS: /** * Calling update() on the plane triggers the global KChart::Chart::update() */ void update(); /** * Calling relayout() on the plane triggers the global KChart::Chart::slotRelayout() */ void relayout(); /** * Calling layoutPlanes() on the plane triggers the global KChart::Chart::slotLayoutPlanes() */ void layoutPlanes(); /** * Used by the chart to clear the cached grid data. */ void setGridNeedsRecalculate(); Q_SIGNALS: /** Emitted when this coordinate plane is destroyed. */ void destroyedCoordinatePlane( AbstractCoordinatePlane* ); /** Emitted when plane needs to update its drawings. */ void needUpdate(); /** Emitted when plane needs to trigger the Chart's layouting. */ void needRelayout(); /** Emitted when plane needs to trigger the Chart's layouting of the coord. planes. */ void needLayoutPlanes(); /** Emitted upon change of a property of the Coordinate Plane or any of its components. */ void propertiesChanged(); void boundariesChanged(); /** Emitted after the geometry of the Coordinate Plane has been changed. * and control has returned to the event loop. * * Parameters are the old geometry, the new geometry. */ void geometryChanged( QRect, QRect ); private: Q_SIGNALS: // Emitted from inside the setGeometry() // This is connected via QueuedConnection to the geometryChanged() Signal // that users can connect to safely then. void internal_geometryChanged( QRect, QRect ); /** Emitted upon change of the view coordinate system */ void viewportCoordinateSystemChanged(); protected: virtual DataDimensionsList getDataDimensionsList() const = 0; //KCHART_DECLARE_PRIVATE_DERIVED( AbstractCoordinatePlane ) }; /** * \brief Helper class for one dimension of data, e.g. for the rows in a data model, * or for the labels of an axis, or for the vertical lines in a grid. * * isCalculated specifies whether this dimension's values are calculated or counted. * (counted == "Item 1", "Item 2", "Item 3" ...) * * sequence is the GranularitySequence, as specified at for the respective * coordinate plane. * * Step width is an optional parameter, to be omitted (or set to Zero, resp.) * if the step width is unknown. * * The default c'tor just gets you counted values from 1..10, using step width 1, * used by the CartesianGrid, when showing an empty plane without any diagrams. */ class DataDimension{ public: DataDimension() : start( 1.0 ) , end( 10.0 ) , isCalculated( false ) , calcMode( AbstractCoordinatePlane::Linear ) , sequence( KChartEnums::GranularitySequence_10_20 ) , stepWidth( 1.0 ) , subStepWidth( 0.0 ) {} DataDimension( qreal start_, qreal end_, bool isCalculated_, AbstractCoordinatePlane::AxesCalcMode calcMode_, KChartEnums::GranularitySequence sequence_, qreal stepWidth_=0.0, qreal subStepWidth_=0.0 ) : start( start_ ) , end( end_ ) , isCalculated( isCalculated_ ) , calcMode( calcMode_ ) , sequence( sequence_ ) , stepWidth( stepWidth_ ) , subStepWidth( subStepWidth_ ) {} /** * Returns the size of the distance, * equivalent to the width() (or height(), resp.) of a QRectF. * * Note that this value can be negative, e.g. indicating axis labels * going in reversed direction. */ qreal distance() const { return end-start; } bool operator==( const DataDimension& r ) const { return (start == r.start) && (end == r.end) && (sequence == r.sequence) && (isCalculated == r.isCalculated) && (calcMode == r.calcMode) && (stepWidth == r.stepWidth) && (subStepWidth == r.subStepWidth); } bool operator!=( const DataDimension& other ) const { return !operator==( other ); } qreal start; qreal end; bool isCalculated; AbstractCoordinatePlane::AxesCalcMode calcMode; KChartEnums::GranularitySequence sequence; qreal stepWidth; qreal subStepWidth; }; #if !defined(QT_NO_DEBUG_STREAM) QDebug operator<<( QDebug stream, const DataDimension& r ); #endif } #endif diff --git a/src/KChart/KChartAbstractDiagram.h b/src/KChart/KChartAbstractDiagram.h index 0b3b5fc..5aae945 100644 --- a/src/KChart/KChartAbstractDiagram.h +++ b/src/KChart/KChartAbstractDiagram.h @@ -1,735 +1,735 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTABSTRACTDIAGRAM_H #define KCHARTABSTRACTDIAGRAM_H #include #include #include #include "KChartGlobal.h" #include "KChartMarkerAttributes.h" #include "KChartAttributesModel.h" namespace KChart { class AbstractCoordinatePlane; class AttributesModel; class DataValueAttributes; class PaintContext; /** * @brief AbstractDiagram defines the interface for diagram classes * * AbstractDiagram is the base class for diagram classes ("chart types"). * * It defines the interface, that needs to be implemented for the diagram, * to function within the KChart framework. It extends Interview's * QAbstractItemView. */ class KCHART_EXPORT AbstractDiagram : public QAbstractItemView { Q_OBJECT Q_DISABLE_COPY( AbstractDiagram ) KCHART_DECLARE_PRIVATE_BASE_POLYMORPHIC( AbstractDiagram ) friend class AbstractCoordinatePlane; friend class CartesianCoordinatePlane; friend class PolarCoordinatePlane; protected: explicit inline AbstractDiagram( Private *p, QWidget* parent, AbstractCoordinatePlane* plane ); explicit AbstractDiagram ( - QWidget* parent = 0, AbstractCoordinatePlane* plane = 0 ); + QWidget* parent = nullptr, AbstractCoordinatePlane* plane = nullptr ); public: virtual ~AbstractDiagram(); /** * Returns true if both diagrams have the same settings. */ bool compare( const AbstractDiagram* other ) const; /** * @brief Return the bottom left and top right data point, that the * diagram will display (unless the grid adjusts these values). * * This method returns a cached result of calculations done by * calculateDataBoundaries. * Classes derived from AbstractDiagram must implement the * calculateDataBoundaries function, to specify their own * way of calculating the data boundaries. * If derived classes want to force recalculation of the * data boundaries, they can call setDataBoundariesDirty() * * Returned value is in diagram coordinates. */ const QPair dataBoundaries() const; // protected: // FIXME: why should that be private? (Mirko) /** * Draw the diagram contents to the rectangle and painter, that are * passed in as part of the paint context. * * @param paintContext All information needed for painting. */ virtual void paint ( PaintContext* paintContext ) = 0; /** * Called by the widget's sizeEvent. Adjust all internal structures, * that are calculated, dependending on the size of the widget. * * @param area */ virtual void resize ( const QSizeF& area ); /** Associate a model with the diagram. */ void setModel ( QAbstractItemModel * model ) Q_DECL_OVERRIDE; /** Associate a seleection model with the diagrom. */ void setSelectionModel( QItemSelectionModel* selectionModel ) Q_DECL_OVERRIDE; /** * Associate an AttributesModel with this diagram. Note that * the diagram does _not_ take ownership of the AttributesModel. * This should thus only be used with AttributesModels that * have been explicitly created by the user, and are owned * by her. Setting an AttributesModel that is internal to * another diagram is an error. * * Correct: * * \code * AttributesModel *am = new AttributesModel( model, 0 ); * diagram1->setAttributesModel( am ); * diagram2->setAttributesModel( am ); * * \endcode * * Wrong: * * \code * * diagram1->setAttributesModel( diagram2->attributesModel() ); * * \endcode * * @param model The AttributesModel to use for this diagram. * @see AttributesModel, usesExternalAttributesModel */ virtual void setAttributesModel( AttributesModel* model ); /** * Returns whether the diagram is using its own built-in attributes model * or an attributes model that was set via setAttributesModel. * * @see setAttributesModel */ virtual bool usesExternalAttributesModel() const; /** * Returns the AttributesModel, that is used by this diagram. * By default each diagram owns its own AttributesModel, which * should never be deleted. Only if a user-supplied AttributesModel * has been set does the pointer returned here not belong to the * diagram. * * @return The AttributesModel associated with the diagram. * @see setAttributesModel */ virtual AttributesModel* attributesModel() const; /** Set the root index in the model, where the diagram starts * referencing data for display. */ void setRootIndex ( const QModelIndex& idx ) Q_DECL_OVERRIDE; /** \reimpl */ QRect visualRect(const QModelIndex &index) const Q_DECL_OVERRIDE; /** \reimpl */ void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) Q_DECL_OVERRIDE; /** \reimpl */ QModelIndex indexAt(const QPoint &point) const Q_DECL_OVERRIDE; /** \reimpl */ QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) Q_DECL_OVERRIDE; /** \reimpl */ int horizontalOffset() const Q_DECL_OVERRIDE; /** \reimpl */ int verticalOffset() const Q_DECL_OVERRIDE; /** \reimpl */ bool isIndexHidden(const QModelIndex &index) const Q_DECL_OVERRIDE; /** \reimpl */ void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command) Q_DECL_OVERRIDE; /** \reimpl */ QRegion visualRegionForSelection(const QItemSelection &selection) const Q_DECL_OVERRIDE; virtual QRegion visualRegion(const QModelIndex &index) const; /** \reimpl */ void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles = QVector()) Q_DECL_OVERRIDE; /** \reimpl */ void doItemsLayout() Q_DECL_OVERRIDE; /** * The coordinate plane associated with the diagram. This determines * how coordinates in value space are mapped into pixel space. By default * this is a CartesianCoordinatePlane. * @return The coordinate plane associated with the diagram. */ AbstractCoordinatePlane* coordinatePlane() const; /** * Set the coordinate plane associated with the diagram. This determines * how coordinates in value space are mapped into pixel space. The chart * takes ownership. * @return The coordinate plane associated with the diagram. */ virtual void setCoordinatePlane( AbstractCoordinatePlane* plane ); /** * Hide (or unhide, resp.) a data cell. * * \note Hidden data are still taken into account by the coordinate plane, * so neither the grid nor your axes' ranges will change, when you hide data. * For totally removing data from KChart's view you can use another approach: * e.g. you could define a proxy model on top of your data model, and register * the proxy model calling setModel() instead of registering your real data model. * * @param index The datapoint to set the hidden status for. With a dataset dimension * of two, this is the index of the key of each key/value pair. * @param hidden The hidden status to set. */ void setHidden( const QModelIndex & index, bool hidden ); /** * Hide (or unhide, resp.) a dataset. * * \note Hidden data are still taken into account by the coordinate plane, * so neither the grid nor your axes' ranges will change, when you hide data. * For totally removing data from KChart's view you can use another approach: * e.g. you could define a proxy model on top of your data model, and register * the proxy model calling setModel() instead of registering your real data model. * * @param dataset The dataset to set the hidden status for. * @param hidden The hidden status to set. */ void setHidden( int dataset, bool hidden ); /** * Hide (or unhide, resp.) all datapoints in the model. * * \note Hidden data are still taken into account by the coordinate plane, * so neither the grid nor your axes' ranges will change, when you hide data. * For totally removing data from KChart's view you can use another approach: * e.g. you could define a proxy model on top of your data model, and register * the proxy model calling setModel() instead of registering your real data model. * * @param hidden The hidden status to set. */ void setHidden( bool hidden ); /** * Retrieve the hidden status specified globally. This will fall * back automatically to the default settings ( = not hidden), if there * are no specific settings. * @return The global hidden status. */ bool isHidden() const; /** * Retrieve the hidden status for the given dataset. This will fall * back automatically to what was set at diagram level, if there * are no dataset specific settings. * @param dataset The dataset to retrieve the hidden status for. * @return The hidden status for the given dataset. */ bool isHidden( int dataset ) const; /** * Retrieve the hidden status for the given index. This will fall * back automatically to what was set at dataset or diagram level, if there * are no datapoint specific settings. * @param index The datapoint to retrieve the hidden status for. * @return The hidden status for the given index. */ bool isHidden( const QModelIndex & index ) const; /** * Set the DataValueAttributes for the given index. * @param index The datapoint to set the attributes for. With a dataset dimension * of two, this is the index of the key of each key/value pair. * @param a The attributes to set. */ void setDataValueAttributes( const QModelIndex & index, const DataValueAttributes & a ); /** * Set the DataValueAttributes for the given dataset. * @param dataset The dataset to set the attributes for. * @param a The attributes to set. */ void setDataValueAttributes( int dataset, const DataValueAttributes & a ); /** * Set the DataValueAttributes for all datapoints in the model. * @param a The attributes to set. */ void setDataValueAttributes( const DataValueAttributes & a ); /** * Retrieve the DataValueAttributes specified globally. This will fall * back automatically to the default settings, if there * are no specific settings. * @return The global DataValueAttributes. */ DataValueAttributes dataValueAttributes() const; /** * Retrieve the DataValueAttributes for the given dataset. This will fall * back automatically to what was set at model level, if there * are no dataset specific settings. * @param dataset The dataset to retrieve the attributes for. * @return The DataValueAttributes for the given dataset. */ DataValueAttributes dataValueAttributes( int dataset ) const; /** * Retrieve the DataValueAttributes for the given index. This will fall * back automatically to what was set at dataset or model level, if there * are no datapoint specific settings. * @param index The datapoint to retrieve the attributes for. With a dataset dimension * of two, this is the index of the key of each key/value pair. * @return The DataValueAttributes for the given index. */ DataValueAttributes dataValueAttributes( const QModelIndex & index ) const; /** * Set the pen to be used, for painting the datapoint at the given index. * @param index The datapoint's index in the model. With a dataset dimension * of two, this is the index of the key of each key/value pair. * @param pen The pen to use. */ void setPen( const QModelIndex& index, const QPen& pen ); /** * Set the pen to be used, for painting the given dataset. * @param dataset The dataset to set the pen for. * @param pen The pen to use. */ void setPen( int dataset, const QPen& pen ); /** * Set the pen to be used, for painting all datasets in the model. * @param pen The pen to use. */ void setPen( const QPen& pen ); /** * Retrieve the pen to be used for painting datapoints globally. This will fall * back automatically to the default settings, if there * are no specific settings. * @return The pen to use for painting. */ QPen pen() const; /** * Retrieve the pen to be used for the given dataset. This will fall * back automatically to what was set at model level, if there * are no dataset specific settings. * @param dataset The dataset to retrieve the pen for. * @return The pen to use for painting. */ QPen pen( int dataset ) const; /** * Retrieve the pen to be used, for painting the datapoint at the given * index in the model. * @param index The index of the datapoint in the model. With a dataset dimension * of two, this is the index of the key of each key/value pair. * @return The pen to use for painting. */ QPen pen( const QModelIndex& index ) const; /** * Set the brush to be used, for painting the datapoint at the given index. * @param index The datapoint's index in the model. With a dataset dimension * of two, this is the index of the key of each key/value pair. * @param brush The brush to use. */ void setBrush( const QModelIndex& index, const QBrush& brush); /** * Set the brush to be used, for painting the given dataset. * @param dataset The dataset to set the brush for. * @param brush The brush to use. */ void setBrush( int dataset, const QBrush& brush ); /** * Set the brush to be used, for painting all datasets in the model. * @param brush The brush to use. */ void setBrush( const QBrush& brush); /** * Retrieve the brush to be used for painting datapoints globally. This will fall * back automatically to the default settings, if there * are no specific settings. * @return The brush to use for painting. */ QBrush brush() const; /** * Retrieve the brush to be used for the given dataset. This will fall * back automatically to what was set at model level, if there * are no dataset specific settings. * @param dataset The dataset to retrieve the brush for. * @return The brush to use for painting. */ QBrush brush( int dataset ) const; /** * Retrieve the brush to be used, for painting the datapoint at the given * index in the model. * @param index The index of the datapoint in the model. With a dataset dimension * of two, this is the index of the key of each key/value pair. * @return The brush to use for painting. */ QBrush brush( const QModelIndex& index ) const; /** * Set the unit prefix to be used on axes for one specific column. * @param prefix The prefix to be used. * @param column The column which should be set. * @param orientation The orientation of the axis to use. */ void setUnitPrefix( const QString& prefix, int column, Qt::Orientation orientation ); /** * Set the unit prefix to be used on axes for all columns. * @param prefix The prefix to be used. * @param orientation The orientation of the axis to use. */ void setUnitPrefix( const QString& prefix, Qt::Orientation orientation ); /** * Set the unit prefix to be used on axes for one specific column. * @param suffix The suffix to be used. * @param column The column which should be set. * @param orientation The orientation of the axis to use. */ void setUnitSuffix( const QString& suffix, int column, Qt::Orientation orientation ); /** * Set the unit prefix to be used on axes for all columns. * @param suffix The suffix to be used. * @param orientation The orientation of the axis to use. */ void setUnitSuffix( const QString& suffix, Qt::Orientation orientation ); /** * Retrieves the axis unit prefix for a specific column. * @param column The column whose prefix should be retrieved. * @param orientation The orientation of the axis. * @param fallback If true, the prefix for all columns is returned, when * none is set for the selected column. * @return The axis unit prefix. */ QString unitPrefix( int column, Qt::Orientation orientation, bool fallback = false ) const; /** * Retrieves the axis unit prefix. * @param orientation The orientation of the axis. * @return The axis unit prefix. */ QString unitPrefix( Qt::Orientation orientation ) const; /** * Retrieves the axis unit suffix for a specific column. * @param column The column whose prefix should be retrieved. * @param orientation The orientation of the axis. * @param fallback If true, the suffix for all columns is returned, when * none is set for the selected column. * @return The axis unit suffix. */ QString unitSuffix( int column, Qt::Orientation orientation, bool fallback = false ) const; /** * Retrieves the axis unit suffix. * @param orientation The orientation of the axis. * @return The axis unit suffix. */ QString unitSuffix( Qt::Orientation orientation ) const; /** * Set whether data value labels are allowed to overlap. * @param allow True means that overlapping labels are allowed. */ void setAllowOverlappingDataValueTexts( bool allow ); /** * @return Whether data value labels are allowed to overlap. */ bool allowOverlappingDataValueTexts() const; /** * Set whether anti-aliasing is to be used while rendering * this diagram. * @param enabled True means that AA is enabled. */ void setAntiAliasing( bool enabled ); /** * @return Whether anti-aliasing is to be used for rendering * this diagram. */ bool antiAliasing() const; /** * Set the palette to be used, for painting datasets to the default * palette. * @see KChart::Palette. * FIXME: fold into one usePalette (KChart::Palette&) method */ void useDefaultColors(); /** * Set the palette to be used, for painting datasets to the rainbow * palette. * @see KChart::Palette. */ void useRainbowColors(); /** * Set the palette to be used, for painting datasets to the subdued * palette. * @see KChart::Palette. */ void useSubduedColors(); /** * The set of item row labels currently displayed, for use in Abscissa axes, etc. * @return The set of item row labels currently displayed. */ QStringList itemRowLabels() const; /** * The set of dataset labels currently displayed, for use in legends, etc. * @return The set of dataset labels currently displayed. */ QStringList datasetLabels() const; /** * The set of dataset brushes currently used, for use in legends, etc. * * @note Cell-level override brushes, if set, take precedence over the * dataset values, so you might need to check these too, in order to find * the brush, that is used for a single cell. * * @return The current set of dataset brushes. */ QList datasetBrushes() const; /** * The set of dataset pens currently used, for use in legends, etc. * * @note Cell-level override pens, if set, take precedence over the * dataset values, so you might need to check these too, in order to find * the pens, that is used for a single cell. * * @return The current set of dataset pens. */ QList datasetPens() const; /** * The set of dataset markers currently used, for use in legends, etc. * * @note Cell-level override markers, if set, take precedence over the * dataset values, so you might need to check these too, in order to find * the marker, that is shown for a single cell. * * @return The current set of dataset brushes. */ QList datasetMarkers() const; /** * \deprecated * * \brief Deprecated method that turns the percent mode of this diagram on or off. * * This method is deprecated. Use the setType() method of a supporting diagram implementation * instead, e.g. BarDiagram::setType(). * * \see percentMode */ void setPercentMode( bool percent ); /** * \brief Returns whether this diagram is drawn in percent mode. * * If true, all data points in the same column of a diagram will * be be drawn at the same X coordinate and stacked up so that the distance from the * last data point (or the zero line) to a data point P is always the ratio of (Y-Value of P)/ * (sum of all Y-Values in same column as P) relative to the diagrams height * (or width, if abscissa and ordinate are swapped). * * Note that this property is not applicable to all diagram types. */ bool percentMode() const; virtual void paintMarker( QPainter* painter, const MarkerAttributes& markerAttributes, const QBrush& brush, const QPen&, const QPointF& point, const QSizeF& size ); /** * The dataset dimension of a diagram determines how many value dimensions * it expects each datapoint to have. * For each dimension and data series it will expect one column of values in the model. * If the dimension is 1, automatic values will be used for X. * * For example, a diagram with the default dimension of 1 will have one column * per data series (the Y values) and will use automatic values for X * (1, 2, 3, ... n). * If the dimension is 2, the diagram will use the first, (and the third, * fifth, etc) columns as X values, and the second, (and the fourth, sixth, * etc) column as Y values. * @return The dataset dimension of the diagram. */ int datasetDimension() const; /** * \deprecated * * Sets the dataset dimension of the diagram. Using this method * is deprecated. Use the specific diagram types instead. */ void setDatasetDimension( int dimension ); protected: void setDatasetDimensionInternal( int dimension ); public: void update() const; void paintMarker( QPainter* painter, const DataValueAttributes& a, const QModelIndex& index, const QPointF& pos ); void paintMarker( QPainter* painter, const QModelIndex& index, const QPointF& pos ); void paintDataValueText( QPainter* painter, const QModelIndex& index, const QPointF& pos, qreal value ); // reverse mapping: /** This method is added alongside with indexAt from QAIM, since in KChart multiple indexes can be displayed at the same spot. */ QModelIndexList indexesAt( const QPoint& point ) const; QModelIndexList indexesIn( const QRect& rect ) const; protected: virtual bool checkInvariants( bool justReturnTheStatus=false ) const; virtual const QPair calculateDataBoundaries() const = 0; protected Q_SLOTS: void setDataBoundariesDirty() const; protected: /** * \deprecated * This method is deprecated and provided for backward-compatibility only. * Your own diagram classes should call * d->paintDataValueTextsAndMarkers() instead * which also is taking care for showing your cell-specific comments, if any, */ virtual void paintDataValueTexts( QPainter* painter ); /** * \deprecated * This method is deprecated and provided for backward-compatibility only. * Your own diagram classes should call * d->paintDataValueTextsAndMarkers() instead * which also is taking care for showing your cell-specific comments, if any, */ virtual void paintMarkers( QPainter* painter ); void setAttributesModelRootIndex( const QModelIndex& ); QModelIndex attributesModelRootIndex() const; /** * Helper method, retrieving the data value (DisplayRole) for a given row and column * @param row The row to query. * @param column The column to query. * @return The value of the display role at the given row and column as a qreal. * @deprecated */ qreal valueForCell( int row, int column ) const; Q_SIGNALS: /** Diagrams are supposed to emit this signal, when the layout of one of their element changes. Layouts can change, for example, when axes are added or removed, or when the configuration was changed in a way that the axes or the diagram itself are displayed in a different geometry. Changes in the diagrams coordinate system also result in the layoutChanged() signal being emitted. */ void layoutChanged( AbstractDiagram* ); /** * This signal is emitted when this diagram is being destroyed, but before all the * data, i.e. the attributes model, is invalidated. */ void aboutToBeDestroyed(); /** This signal is emitted when either the model or the AttributesModel is replaced. */ void modelsChanged(); /** This signal is emitted just before the new attributes model is connected internally. It gives you a chance to connect to its signals first or perform other setup work. */ void attributesModelAboutToChange( AttributesModel* newModel, AttributesModel* oldModel ); /** This signal is emitted, when the model data is changed. */ void modelDataChanged(); /** This signal is emitted, when the hidden status of at least one data cell was (un)set. */ void dataHidden(); /** Emitted upon change of a property of the Diagram. */ void propertiesChanged(); /** Emitted upon change of a data boundary */ void boundariesChanged(); /** Emitted upon change of the view coordinate system */ void viewportCoordinateSystemChanged(); private: QModelIndex conditionallyMapFromSource( const QModelIndex & sourceIndex ) const; }; typedef QList AbstractDiagramList; typedef QList ConstAbstractDiagramList; /** * @brief Internally used class just adding a special constructor used by AbstractDiagram */ class PrivateAttributesModel : public AttributesModel { Q_OBJECT public: - explicit PrivateAttributesModel( QAbstractItemModel* model, QObject * parent = 0 ) + explicit PrivateAttributesModel( QAbstractItemModel* model, QObject * parent = nullptr ) : AttributesModel(model,parent) {} }; } #endif diff --git a/src/KChart/KChartAbstractDiagram_p.cpp b/src/KChart/KChartAbstractDiagram_p.cpp index ccef30b..c0cdf0e 100644 --- a/src/KChart/KChartAbstractDiagram_p.cpp +++ b/src/KChart/KChartAbstractDiagram_p.cpp @@ -1,678 +1,678 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ // // W A R N I N G // ------------- // // This file is not part of the KD Chart API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "KChartAbstractDiagram_p.h" #include "KChartBarDiagram.h" #include "KChartFrameAttributes.h" #include "KChartPainterSaver_p.h" #include #include #include using namespace KChart; LabelPaintInfo::LabelPaintInfo() : isValuePositive( false ) { } LabelPaintInfo::LabelPaintInfo( const QModelIndex& _index, const DataValueAttributes& _attrs, const QPainterPath& _labelArea, const QPointF& _markerPos, bool _isValuePositive, const QString& _value ) : index( _index ) , attrs( _attrs ) , labelArea( _labelArea ) , markerPos( _markerPos ) , isValuePositive( _isValuePositive ) , value( _value ) { } LabelPaintInfo::LabelPaintInfo( const LabelPaintInfo& other ) : index( other.index ) , attrs( other.attrs ) , labelArea( other.labelArea ) , markerPos( other.markerPos ) , isValuePositive( other.isValuePositive ) , value( other.value ) { } AbstractDiagram::Private::Private() - : diagram( 0 ) + : diagram( nullptr ) , doDumpPaintTime( false ) - , plane( 0 ) - , attributesModel( new PrivateAttributesModel(0,0) ) + , plane( nullptr ) + , attributesModel( new PrivateAttributesModel(nullptr,nullptr) ) , allowOverlappingDataValueTexts( false ) , antiAliasing( true ) , percent( false ) , datasetDimension( 1 ) , databoundariesDirty( true ) , mCachedFontMetrics( QFontMetrics( qApp->font() ) ) { } AbstractDiagram::Private::~Private() { if ( attributesModel && qobject_cast(attributesModel) ) delete attributesModel; } void AbstractDiagram::Private::init() { } void AbstractDiagram::Private::init( AbstractCoordinatePlane* newPlane ) { plane = newPlane; } bool AbstractDiagram::Private::usesExternalAttributesModel() const { return ( ! attributesModel.isNull() ) && ( ! qobject_cast(attributesModel) ); } void AbstractDiagram::Private::setAttributesModel( AttributesModel* amodel ) { if ( attributesModel == amodel ) { return; } if ( !attributesModel.isNull() ) { if ( qobject_cast< PrivateAttributesModel* >( attributesModel ) ) { delete attributesModel; } else { disconnect( attributesModel, SIGNAL(rowsInserted(QModelIndex,int,int)), diagram, SLOT(setDataBoundariesDirty()) ); disconnect( attributesModel, SIGNAL(columnsInserted(QModelIndex,int,int)), diagram, SLOT(setDataBoundariesDirty()) ); disconnect( attributesModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), diagram, SLOT(setDataBoundariesDirty()) ); disconnect( attributesModel, SIGNAL(columnsRemoved(QModelIndex,int,int)), diagram, SLOT(setDataBoundariesDirty()) ); disconnect( attributesModel, SIGNAL(modelReset()), diagram, SLOT(setDataBoundariesDirty()) ); disconnect( attributesModel, SIGNAL(layoutChanged()), diagram, SLOT(setDataBoundariesDirty()) ); disconnect( attributesModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), diagram, SIGNAL(modelDataChanged())); } } emit diagram->attributesModelAboutToChange( amodel, attributesModel ); connect( amodel, SIGNAL(rowsInserted(QModelIndex,int,int)), diagram, SLOT(setDataBoundariesDirty()) ); connect( amodel, SIGNAL(columnsInserted(QModelIndex,int,int)), diagram, SLOT(setDataBoundariesDirty()) ); connect( amodel, SIGNAL(rowsRemoved(QModelIndex,int,int)), diagram, SLOT(setDataBoundariesDirty()) ); connect( amodel, SIGNAL(columnsRemoved(QModelIndex,int,int)), diagram, SLOT(setDataBoundariesDirty()) ); connect( amodel, SIGNAL(modelReset()), diagram, SLOT(setDataBoundariesDirty()) ); connect( amodel, SIGNAL(layoutChanged()), diagram, SLOT(setDataBoundariesDirty()) ); connect( amodel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), diagram, SIGNAL(modelDataChanged())); attributesModel = amodel; } AbstractDiagram::Private::Private( const AbstractDiagram::Private& rhs ) : - diagram( 0 ), + diagram( nullptr ), doDumpPaintTime( rhs.doDumpPaintTime ), // Do not copy the plane - plane( 0 ), + plane( nullptr ), attributesModelRootIndex( QModelIndex() ), attributesModel( rhs.attributesModel ), allowOverlappingDataValueTexts( rhs.allowOverlappingDataValueTexts ), antiAliasing( rhs.antiAliasing ), percent( rhs.percent ), datasetDimension( rhs.datasetDimension ), mCachedFontMetrics( rhs.cachedFontMetrics() ) { - attributesModel = new PrivateAttributesModel( 0, 0); + attributesModel = new PrivateAttributesModel( nullptr, nullptr); attributesModel->initFrom( rhs.attributesModel ); } // FIXME: Optimize if necessary qreal AbstractDiagram::Private::calcPercentValue( const QModelIndex & index ) const { qreal sum = 0.0; for ( int col = 0; col < attributesModel->columnCount( QModelIndex() ); col++ ) sum += attributesModel->data( attributesModel->index( index.row(), col, QModelIndex() ) ).toReal(); // checked if ( sum == 0.0 ) return 0.0; return attributesModel->data( attributesModel->mapFromSource( index ) ).toReal() / sum * 100.0; } void AbstractDiagram::Private::addLabel( LabelPaintCache* cache, const QModelIndex& index, const CartesianDiagramDataCompressor::CachePosition* position, const PositionPoints& points, const Position& autoPositionPositive, const Position& autoPositionNegative, const qreal value, qreal favoriteAngle /* = 0.0 */ ) { CartesianDiagramDataCompressor::AggregatedDataValueAttributes allAttrs( aggregatedAttrs( index, position ) ); QMap::const_iterator it; for ( it = allAttrs.constBegin(); it != allAttrs.constEnd(); ++it ) { DataValueAttributes dva = it.value(); if ( !dva.isVisible() ) { continue; } const bool isPositive = ( value >= 0.0 ); RelativePosition relPos( dva.position( isPositive ) ); relPos.setReferencePoints( points ); if ( relPos.referencePosition().isUnknown() ) { relPos.setReferencePosition( isPositive ? autoPositionPositive : autoPositionNegative ); } // Rotate the label position (not the label itself) if the diagram is rotated so that the defaults still work if ( isTransposed() ) { KChartEnums::PositionValue posValue = relPos.referencePosition().value(); if ( posValue >= KChartEnums::PositionNorthWest && posValue <= KChartEnums::PositionWest ) { // rotate 90 degrees clockwise posValue = static_cast< KChartEnums::PositionValue >( posValue + 2 ); if ( posValue > KChartEnums::PositionWest ) { // wraparound posValue = static_cast< KChartEnums::PositionValue >( posValue - ( KChartEnums::PositionWest - KChartEnums::PositionNorthWest ) ); } relPos.setReferencePosition( Position( posValue ) ); } } const QPointF referencePoint = relPos.referencePoint(); if ( !diagram->coordinatePlane()->isVisiblePoint( referencePoint ) ) { continue; } const qreal fontHeight = cachedFontMetrics( dva.textAttributes(). calculatedFont( plane, KChartEnums::MeasureOrientationMinimum ), diagram )->height(); // Note: When printing data value texts and padding's Measure is using automatic reference area // detection, the font height is used as reference size for both horizontal and vertical // padding. QSizeF relativeMeasureSize( fontHeight, fontHeight ); if ( !dva.textAttributes().hasRotation() ) { TextAttributes ta = dva.textAttributes(); ta.setRotation( favoriteAngle ); dva.setTextAttributes( ta ); } // get the size of the label text using a subset of the information going into the final layout const QString text = formatDataValueText( dva, index, value ); QTextDocument doc; doc.setDocumentMargin( 0 ); if ( Qt::mightBeRichText( text ) ) { doc.setHtml( text ); } else { doc.setPlainText( text ); } const QFont calculatedFont( dva.textAttributes() .calculatedFont( plane, KChartEnums::MeasureOrientationMinimum ) ); doc.setDefaultFont( calculatedFont ); const QRectF plainRect = doc.documentLayout()->frameBoundingRect( doc.rootFrame() ); /** * A few hints on how the positioning of the text frame is done: * * Let's assume we have a bar chart, a text for a positive value * to be drawn, and "North" as attrs.positivePosition(). * * The reference point (pos) is then set to the top center point * of a bar. The offset now depends on the alignment: * * Top: text is centered horizontally to the bar, bottom of * text frame starts at top of bar * * Bottom: text is centered horizontally to the bar, top of * text frame starts at top of bar * * Center: text is centered horizontally to the bar, center * line of text frame is same as top of bar * * TopLeft: right edge of text frame is horizontal center of * bar, bottom of text frame is top of bar. * * ... * * Positive and negative value labels are treated equally, "North" * also refers to the top of a negative bar, and *not* to the bottom. * * * "NorthEast" likewise refers to the top right edge of the bar, * "NorthWest" to the top left edge of the bar, and so on. * * In other words, attrs.positivePosition() always refers to a * position of the *bar*, and relPos.alignment() always refers * to an alignment of the text frame relative to this position. */ QTransform transform; { // move to the general area where the label should be QPointF calcPoint = relPos.calculatedPoint( relativeMeasureSize ); transform.translate( calcPoint.x(), calcPoint.y() ); // align the text rect; find out by how many half-widths / half-heights to move. int dx = -1; if ( relPos.alignment() & Qt::AlignLeft ) { dx -= 1; } else if ( relPos.alignment() & Qt::AlignRight ) { dx += 1; } int dy = -1; if ( relPos.alignment() & Qt::AlignTop ) { dy -= 1; } else if ( relPos.alignment() & Qt::AlignBottom ) { dy += 1; } transform.translate( qreal( dx ) * plainRect.width() * 0.5, qreal( dy ) * plainRect.height() * 0.5 ); // rotate the text rect around its center transform.translate( plainRect.center().x(), plainRect.center().y() ); int rotation = dva.textAttributes().rotation(); if ( !isPositive && dva.mirrorNegativeValueTextRotation() ) { rotation *= -1; } transform.rotate( rotation ); transform.translate( -plainRect.center().x(), -plainRect.center().y() ); } QPainterPath labelArea; //labelArea.addPolygon( transform.mapToPolygon( plainRect.toRect() ) ); //labelArea.closeSubpath(); // Not doing that because QTransform has a special case for 180° that gives a different than // usual ordering of the points in the polygon returned by mapToPolygon( const QRect & ). // We expect a particular ordering in paintDataValueTextsAndMarkers() by using elementAt( 0 ), // and similar things might happen elsewhere. labelArea.addPolygon( transform.map( QPolygon( plainRect.toRect(), true ) ) ); // store the label geometry and auxiliary data cache->paintReplay.append( LabelPaintInfo( it.key(), dva, labelArea, referencePoint, value >= 0.0, text ) ); } } const QFontMetrics* AbstractDiagram::Private::cachedFontMetrics( const QFont& font, const QPaintDevice* paintDevice) const { if ( ( font != mCachedFont ) || ( paintDevice != mCachedPaintDevice ) ) { mCachedFontMetrics = QFontMetrics( font, const_cast( paintDevice ) ); // TODO what about setting mCachedFont and mCachedPaintDevice? } return &mCachedFontMetrics; } const QFontMetrics AbstractDiagram::Private::cachedFontMetrics() const { return mCachedFontMetrics; } QString AbstractDiagram::Private::formatNumber( qreal value, int decimalDigits ) const { const int digits = qMax(decimalDigits, 0); const qreal roundingEpsilon = pow( 0.1, digits ) * ( value >= 0.0 ? 0.5 : -0.5 ); QString asString = QString::number( value + roundingEpsilon, 'f' ); const int decimalPos = asString.indexOf( QLatin1Char( '.' ) ); if ( decimalPos < 0 ) { return asString; } int last = qMin( decimalPos + digits, asString.length() - 1 ); // remove trailing zeros (and maybe decimal dot) while ( last > decimalPos && asString[ last ] == QLatin1Char( '0' ) ) { last--; } if ( last == decimalPos ) { last--; } asString.chop( asString.length() - last - 1 ); return asString; } void AbstractDiagram::Private::forgetAlreadyPaintedDataValues() { alreadyDrawnDataValueTexts.clear(); prevPaintedDataValueText.clear(); } void AbstractDiagram::Private::paintDataValueTextsAndMarkers( PaintContext* ctx, const LabelPaintCache &cache, bool paintMarkers, bool justCalculateRect /* = false */, QRectF* cumulatedBoundingRect /* = 0 */ ) { if ( justCalculateRect && !cumulatedBoundingRect ) { qWarning() << Q_FUNC_INFO << "Neither painting nor finding the bounding rect, what are we doing?"; } const PainterSaver painterSaver( ctx->painter() ); ctx->painter()->setClipping( false ); if ( paintMarkers && !justCalculateRect ) { Q_FOREACH ( const LabelPaintInfo& info, cache.paintReplay ) { diagram->paintMarker( ctx->painter(), info.index, info.markerPos ); } } TextAttributes ta; { Measure m( 18.0, KChartEnums::MeasureCalculationModeRelative, KChartEnums::MeasureOrientationMinimum ); m.setReferenceArea( ctx->coordinatePlane() ); ta.setFontSize( m ); m.setAbsoluteValue( 6.0 ); ta.setMinimalFontSize( m ); } forgetAlreadyPaintedDataValues(); Q_FOREACH ( const LabelPaintInfo& info, cache.paintReplay ) { const QPointF pos = info.labelArea.elementAt( 0 ); paintDataValueText( ctx->painter(), info.attrs, pos, info.isValuePositive, info.value, justCalculateRect, cumulatedBoundingRect ); const QString comment = info.index.data( KChart::CommentRole ).toString(); if ( comment.isEmpty() ) { continue; } TextBubbleLayoutItem item( comment, ta, ctx->coordinatePlane()->parent(), KChartEnums::MeasureOrientationMinimum, Qt::AlignHCenter | Qt::AlignVCenter ); const QRect rect( pos.toPoint(), item.sizeHint() ); if (cumulatedBoundingRect) { (*cumulatedBoundingRect) |= rect; } if ( !justCalculateRect ) { item.setGeometry( rect ); item.paint( ctx->painter() ); } } if ( cumulatedBoundingRect ) { *cumulatedBoundingRect = ctx->painter()->transform().inverted().mapRect( *cumulatedBoundingRect ); } } QString AbstractDiagram::Private::formatDataValueText( const DataValueAttributes &dva, const QModelIndex& index, qreal value ) const { if ( !dva.isVisible() ) { return QString(); } if ( dva.usePercentage() ) { value = calcPercentValue( index ); } QString ret; if ( dva.dataLabel().isNull() ) { ret = formatNumber( value, dva.decimalDigits() ); } else { ret = dva.dataLabel(); } ret.prepend( dva.prefix() ); ret.append( dva.suffix() ); return ret; } void AbstractDiagram::Private::paintDataValueText( QPainter* painter, const QModelIndex& index, const QPointF& pos, qreal value, bool justCalculateRect /* = false */, QRectF* cumulatedBoundingRect /* = 0 */ ) { const DataValueAttributes dva( diagram->dataValueAttributes( index ) ); const QString text = formatDataValueText( dva, index, value ); paintDataValueText( painter, dva, pos, value >= 0.0, text, justCalculateRect, cumulatedBoundingRect ); } void AbstractDiagram::Private::paintDataValueText( QPainter* painter, const DataValueAttributes& attrs, const QPointF& pos, bool valueIsPositive, const QString& text, bool justCalculateRect /* = false */, QRectF* cumulatedBoundingRect /* = 0 */ ) { if ( !attrs.isVisible() ) { return; } const TextAttributes ta( attrs.textAttributes() ); if ( !ta.isVisible() || ( !attrs.showRepetitiveDataLabels() && prevPaintedDataValueText == text ) ) { return; } prevPaintedDataValueText = text; QTextDocument doc; doc.setDocumentMargin( 0.0 ); if ( Qt::mightBeRichText( text ) ) { doc.setHtml( text ); } else { doc.setPlainText( text ); } const QFont calculatedFont( ta.calculatedFont( plane, KChartEnums::MeasureOrientationMinimum ) ); const PainterSaver painterSaver( painter ); painter->setPen( PrintingParameters::scalePen( ta.pen() ) ); doc.setDefaultFont( calculatedFont ); QAbstractTextDocumentLayout::PaintContext context; context.palette = diagram->palette(); context.palette.setColor( QPalette::Text, ta.pen().color() ); QAbstractTextDocumentLayout* const layout = doc.documentLayout(); layout->setPaintDevice( painter->device() ); painter->translate( pos.x(), pos.y() ); int rotation = ta.rotation(); if ( !valueIsPositive && attrs.mirrorNegativeValueTextRotation() ) { rotation *= -1; } painter->rotate( rotation ); // do overlap detection "as seen by the painter" QTransform transform = painter->worldTransform(); bool drawIt = true; // note: This flag can be set differently for every label text! // In theory a user could e.g. have some small red text on one of the // values that she wants to have written in any case - so we just // do not test if such texts would cover some of the others. if ( !attrs.showOverlappingDataLabels() ) { const QRectF br( layout->frameBoundingRect( doc.rootFrame() ) ); QPolygon pr = transform.mapToPolygon( br.toRect() ); // Using QPainterPath allows us to use intersects() (which has many early-exits) // instead of QPolygon::intersected (which calculates a slow and precise intersection polygon) QPainterPath path; path.addPolygon( pr ); // iterate backwards because recently added items are more likely to overlap, so we spend // less time checking irrelevant items when there is overlap for ( int i = alreadyDrawnDataValueTexts.count() - 1; i >= 0; i-- ) { if ( alreadyDrawnDataValueTexts.at( i ).intersects( path ) ) { // qDebug() << "not painting this label due to overlap"; drawIt = false; break; } } if ( drawIt ) { alreadyDrawnDataValueTexts << path; } } if ( drawIt ) { QRectF rect = layout->frameBoundingRect( doc.rootFrame() ); if ( cumulatedBoundingRect ) { (*cumulatedBoundingRect) |= transform.mapRect( rect ); } if ( !justCalculateRect ) { bool paintBack = false; BackgroundAttributes back( attrs.backgroundAttributes() ); if ( back.isVisible() ) { paintBack = true; painter->setBrush( back.brush() ); } else { painter->setBrush( QBrush() ); } qreal radius = 0.0; FrameAttributes frame( attrs.frameAttributes() ); if ( frame.isVisible() ) { paintBack = true; painter->setPen( frame.pen() ); radius = frame.cornerRadius(); } if ( paintBack ) { QRectF borderRect( QPointF( 0, 0 ), rect.size() ); painter->drawRoundedRect( borderRect, radius, radius ); } layout->draw( painter, context ); } } } QModelIndex AbstractDiagram::Private::indexAt( const QPoint& point ) const { QModelIndexList l = indexesAt( point ); qSort( l ); if ( !l.isEmpty() ) return l.first(); else return QModelIndex(); } QModelIndexList AbstractDiagram::Private::indexesAt( const QPoint& point ) const { return reverseMapper.indexesAt( point ); // which could be empty } QModelIndexList AbstractDiagram::Private::indexesIn( const QRect& rect ) const { return reverseMapper.indexesIn( rect ); } CartesianDiagramDataCompressor::AggregatedDataValueAttributes AbstractDiagram::Private::aggregatedAttrs( const QModelIndex& index, const CartesianDiagramDataCompressor::CachePosition* position ) const { Q_UNUSED( position ); // used by cartesian diagrams only CartesianDiagramDataCompressor::AggregatedDataValueAttributes allAttrs; allAttrs[index] = diagram->dataValueAttributes( index ); return allAttrs; } void AbstractDiagram::Private::setDatasetAttrs( int dataset, const QVariant& data, int role ) { // To store attributes for a dataset, we use the first column // that's associated with it. (i.e., with a dataset dimension // of two, the column of the keys). In most cases however, there's // only one data dimension, and thus also only one column per data set. int column = dataset * datasetDimension; // For DataHiddenRole, also store the flag in the other data points that belong to this data set, // otherwise it's impossible to hide data points in a plotter diagram because there will always // be one model index that belongs to this data point that is not hidden. // For more details on how hiding works, see the data compressor. // Also see KDCH-503 for which this is a workaround. int columnSpan = role == DataHiddenRole ? datasetDimension : 1; for ( int i = 0; i < columnSpan; i++ ) { attributesModel->setHeaderData( column + i, Qt::Horizontal, data, role ); } } QVariant AbstractDiagram::Private::datasetAttrs( int dataset, int role ) const { // See setDataSetAttrs for explanation of column int column = dataset * datasetDimension; return attributesModel->headerData( column, Qt::Horizontal, role ); } void AbstractDiagram::Private::resetDatasetAttrs( int dataset, int role ) { // See setDataSetAttrs for explanation of column int column = dataset * datasetDimension; attributesModel->resetHeaderData( column, Qt::Horizontal, role ); } bool AbstractDiagram::Private::isTransposed() const { // Determine the diagram that specifies the orientation. // That diagram is the reference diagram, if it exists, or otherwise the diagram itself. // Note: In KChart 2.3 or earlier, only a bar diagram can be transposed. const AbstractCartesianDiagram* refDiagram = qobject_cast< const AbstractCartesianDiagram * >( diagram ); if ( !refDiagram ) { return false; } if ( refDiagram->referenceDiagram() ) { refDiagram = refDiagram->referenceDiagram(); } const BarDiagram* barDiagram = qobject_cast< const BarDiagram* >( refDiagram ); if ( !barDiagram ) { return false; } return barDiagram->orientation() == Qt::Horizontal; } LineAttributesInfo::LineAttributesInfo() { } LineAttributesInfo::LineAttributesInfo( const QModelIndex& _index, const QPointF& _value, const QPointF& _nextValue ) : index( _index ) , value ( _value ) , nextValue ( _nextValue ) { } diff --git a/src/KChart/KChartAbstractDiagram_p.h b/src/KChart/KChartAbstractDiagram_p.h index c746278..3d9014d 100644 --- a/src/KChart/KChartAbstractDiagram_p.h +++ b/src/KChart/KChartAbstractDiagram_p.h @@ -1,249 +1,249 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTABSTRACTDIAGRAM_P_H #define KCHARTABSTRACTDIAGRAM_P_H // // W A R N I N G // ------------- // // This file is not part of the KD Chart API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "KChartAbstractDiagram.h" #include "KChartAbstractCoordinatePlane.h" #include "KChartDataValueAttributes.h" #include "KChartBackgroundAttributes.h" #include "KChartRelativePosition.h" #include "KChartPosition.h" #include "KChartPaintContext.h" #include "KChartPrintingParameters.h" #include "KChartChart.h" #include #include "ReverseMapper.h" #include #include #include #include #include #include #include namespace KChart { class LabelPaintInfo { public: LabelPaintInfo(); LabelPaintInfo( const QModelIndex& _index, const DataValueAttributes& _attrs, const QPainterPath& _labelArea, const QPointF& _markerPos, bool _isValuePositive, const QString& _value ); LabelPaintInfo( const LabelPaintInfo& other ); QModelIndex index; DataValueAttributes attrs; QPainterPath labelArea; QPointF markerPos; bool isValuePositive; // could (ab)use attrs.dataLabel() instead QString value; }; class LabelPaintCache { public: LabelPaintCache() {} ~LabelPaintCache() { clear(); } void clear() { paintReplay.clear(); } QVector paintReplay; private: LabelPaintCache( LabelPaintCache& other ); // no copies }; /** * \internal */ class AttributesModel; class Q_DECL_HIDDEN KChart::AbstractDiagram::Private { friend class AbstractDiagram; public: explicit Private(); virtual ~Private(); Private( const Private& rhs ); void setAttributesModel( AttributesModel* ); bool usesExternalAttributesModel() const; // FIXME: Optimize if necessary virtual qreal calcPercentValue( const QModelIndex & index ) const; // this should possibly be virtual so it can be overridden void addLabel( LabelPaintCache* cache, const QModelIndex& index, const CartesianDiagramDataCompressor::CachePosition* position, const PositionPoints& points, const Position& autoPositionPositive, const Position& autoPositionNegative, const qreal value, qreal favoriteAngle = 0.0 ); const QFontMetrics* cachedFontMetrics( const QFont& font, const QPaintDevice* paintDevice) const; const QFontMetrics cachedFontMetrics() const; QString formatNumber( qreal value, int decimalDigits ) const; QString formatDataValueText( const DataValueAttributes &dva, const QModelIndex& index, qreal value ) const; void forgetAlreadyPaintedDataValues(); void paintDataValueTextsAndMarkers( PaintContext* ctx, const LabelPaintCache& cache, bool paintMarkers, bool justCalculateRect=false, - QRectF* cumulatedBoundingRect=0 ); + QRectF* cumulatedBoundingRect = nullptr ); void paintDataValueText( QPainter* painter, const QModelIndex& index, const QPointF& pos, qreal value, bool justCalculateRect=false, - QRectF* cumulatedBoundingRect=0 ); + QRectF* cumulatedBoundingRect = nullptr ); void paintDataValueText( QPainter* painter, const DataValueAttributes& attrs, const QPointF& pos, bool valueIsPositive, const QString& text, bool justCalculateRect=false, - QRectF* cumulatedBoundingRect=0 ); + QRectF* cumulatedBoundingRect = nullptr ); inline int datasetCount() const { return attributesModel->columnCount( attributesModelRootIndex ) / datasetDimension; } virtual QModelIndex indexAt( const QPoint& point ) const; QModelIndexList indexesAt( const QPoint& point ) const; QModelIndexList indexesIn( const QRect& rect ) const; virtual CartesianDiagramDataCompressor::AggregatedDataValueAttributes aggregatedAttrs( const QModelIndex & index, const CartesianDiagramDataCompressor::CachePosition * position ) const; /** * Sets arbitrary attributes of a data set. */ void setDatasetAttrs( int dataset, const QVariant& data, int role ); /** * Retrieves arbitrary attributes of a data set. */ QVariant datasetAttrs( int dataset, int role ) const; /** * Resets an attribute of a dataset back to its default. */ void resetDatasetAttrs( int dataset, int role ); /** * Whether the diagram is transposed (X and Y swapped), which has the same effect as rotating * the diagram 90° clockwise and inverting the (then vertical) X coordinate. */ bool isTransposed() const; static Private* get( AbstractDiagram *diagram ) { return diagram->_d; } AbstractDiagram* diagram; ReverseMapper reverseMapper; /// The size of the diagram set by AbstractDiagram::resize() QSizeF diagramSize; bool doDumpPaintTime; // for use in performance testing code protected: void init(); void init( AbstractCoordinatePlane* plane ); QPointer plane; mutable QModelIndex attributesModelRootIndex; QPointer attributesModel; bool allowOverlappingDataValueTexts; bool antiAliasing; bool percent; int datasetDimension; mutable QPair databoundaries; mutable bool databoundariesDirty; QMap< Qt::Orientation, QString > unitSuffix; QMap< Qt::Orientation, QString > unitPrefix; QMap< int, QMap< Qt::Orientation, QString > > unitSuffixMap; QMap< int, QMap< Qt::Orientation, QString > > unitPrefixMap; QList< QPainterPath > alreadyDrawnDataValueTexts; private: QString prevPaintedDataValueText; mutable QFontMetrics mCachedFontMetrics; mutable QFont mCachedFont; mutable QPaintDevice* mCachedPaintDevice; }; inline AbstractDiagram::AbstractDiagram( Private * p ) : _d( p ) { init(); } inline AbstractDiagram::AbstractDiagram( Private * p, QWidget* parent, AbstractCoordinatePlane* plane ) : QAbstractItemView( parent ), _d( p ) { _d->init( plane ); init(); } class LineAttributesInfo { public: LineAttributesInfo(); LineAttributesInfo( const QModelIndex& _index, const QPointF& _value, const QPointF& _nextValue ); QModelIndex index; QPointF value; QPointF nextValue; }; typedef QVector LineAttributesInfoList; typedef QVectorIterator LineAttributesInfoListIterator; } #endif /* KCHARTDIAGRAM_P_H */ diff --git a/src/KChart/KChartAbstractGrid.cpp b/src/KChart/KChartAbstractGrid.cpp index 03026b1..614adae 100644 --- a/src/KChart/KChartAbstractGrid.cpp +++ b/src/KChart/KChartAbstractGrid.cpp @@ -1,116 +1,116 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartAbstractGrid.h" #include "KChartPaintContext.h" #include "KChartMath_p.h" #include using namespace KChart; using namespace std; static qreal _trunc( qreal v ) { return (( v > 0.0 ) ? floor( v ) : ceil( v )); } AbstractGrid::AbstractGrid () - : mPlane( 0 ) + : mPlane( nullptr ) { //this bloc left empty intentionally } AbstractGrid::~AbstractGrid() { //this bloc left empty intentionally } void AbstractGrid::setNeedRecalculate() { mCachedRawDataDimensions.clear(); } DataDimensionsList AbstractGrid::updateData( AbstractCoordinatePlane* plane ) { if ( plane ) { const DataDimensionsList rawDataDimensions( plane->getDataDimensionsList() ); // ### this could be dangerous becaus calculateGrid() looks at some data we are not checking // for changes here. if ( mCachedRawDataDimensions.empty() || ( rawDataDimensions != mCachedRawDataDimensions ) ) { mCachedRawDataDimensions = rawDataDimensions; mPlane = plane; mDataDimensions = calculateGrid( rawDataDimensions ); } } return mDataDimensions; } bool AbstractGrid::isBoundariesValid(const QRectF& r ) { return isBoundariesValid( qMakePair( r.topLeft(), r.bottomRight() ) ); } bool AbstractGrid::isBoundariesValid(const QPair& b ) { return isValueValid( b.first.x() ) && isValueValid( b.first.y() ) && isValueValid( b.second.x() ) && isValueValid( b.second.y() ); } bool AbstractGrid::isBoundariesValid(const DataDimensionsList& l ) { for (int i = 0; i < l.size(); ++i) if ( ! isValueValid( l.at(i).start ) || ! isValueValid( l.at(i).end ) ) return false; return true; } bool AbstractGrid::isValueValid(const qreal& r ) { return !(ISNAN(r) || ISINF(r)); } void AbstractGrid::adjustLowerUpperRange( qreal& start, qreal& end, qreal stepWidth, bool adjustLower, bool adjustUpper ) { const qreal startAdjust = ( start >= 0.0 ) ? 0.0 : -1.0; const qreal endAdjust = ( end >= 0.0 ) ? 1.0 : 0.0; if ( adjustLower && !qFuzzyIsNull( fmod( start, stepWidth ) ) ) start = stepWidth * (_trunc( start / stepWidth ) + startAdjust); if ( adjustUpper && !qFuzzyIsNull( fmod( end, stepWidth ) ) ) end = stepWidth * (_trunc( end / stepWidth ) + endAdjust); } const DataDimension AbstractGrid::adjustedLowerUpperRange( const DataDimension& dim, bool adjustLower, bool adjustUpper ) { DataDimension result( dim ); adjustLowerUpperRange( result.start, result.end, result.stepWidth, adjustLower, adjustUpper ); return result; } diff --git a/src/KChart/KChartAbstractProxyModel.h b/src/KChart/KChartAbstractProxyModel.h index 38118e5..6d3a498 100644 --- a/src/KChart/KChartAbstractProxyModel.h +++ b/src/KChart/KChartAbstractProxyModel.h @@ -1,51 +1,51 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTABSTRACTPROXYMODEL_H #define KCHARTABSTRACTPROXYMODEL_H #include #include "KChartGlobal.h" namespace KChart { /** * @brief Base class for all proxy models used inside KChart * \internal */ class KCHART_EXPORT AbstractProxyModel : public QAbstractProxyModel { Q_OBJECT public: - explicit AbstractProxyModel( QObject* parent = 0 ); + explicit AbstractProxyModel( QObject* parent = nullptr ); /*! \reimpl */ QModelIndex mapFromSource( const QModelIndex & sourceIndex ) const Q_DECL_OVERRIDE; /*! \reimpl */ QModelIndex mapToSource( const QModelIndex &proxyIndex ) const Q_DECL_OVERRIDE; /*! \reimpl */ QModelIndex index( int row, int col, const QModelIndex& index ) const Q_DECL_OVERRIDE; /*! \reimpl */ QModelIndex parent( const QModelIndex& index ) const Q_DECL_OVERRIDE; }; } #endif /* KCHARTABSTRACTPROXYMODEL_H */ diff --git a/src/KChart/KChartAbstractThreeDAttributes.cpp b/src/KChart/KChartAbstractThreeDAttributes.cpp index d4fc2ae..1f6f77c 100644 --- a/src/KChart/KChartAbstractThreeDAttributes.cpp +++ b/src/KChart/KChartAbstractThreeDAttributes.cpp @@ -1,137 +1,137 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartAbstractThreeDAttributes.h" #include "KChartAbstractThreeDAttributes_p.h" #include "KChartMath_p.h" #include #include #define d d_func() using namespace KChart; AbstractThreeDAttributes::Private::Private() : enabled( false ), depth( 20 ), threeDBrushEnabled( false ) { } AbstractThreeDAttributes::AbstractThreeDAttributes() : _d( new Private() ) { } AbstractThreeDAttributes::AbstractThreeDAttributes( const AbstractThreeDAttributes& r ) : _d( new Private( *r.d ) ) { } AbstractThreeDAttributes& AbstractThreeDAttributes::operator= ( const AbstractThreeDAttributes& r ) { if ( this == &r ) return *this; *d = *r.d; return *this; } AbstractThreeDAttributes::~AbstractThreeDAttributes() { - delete _d; _d = 0; + delete _d; _d = nullptr; } bool AbstractThreeDAttributes::operator==( const AbstractThreeDAttributes& r ) const { return isEnabled() == r.isEnabled() && depth() == r.depth() && isThreeDBrushEnabled() == r.isThreeDBrushEnabled(); } void AbstractThreeDAttributes::init( ) { } void AbstractThreeDAttributes::setEnabled( bool enabled ) { d->enabled = enabled; } bool AbstractThreeDAttributes::isEnabled() const { return d->enabled; } void AbstractThreeDAttributes::setDepth( qreal depth ) { d->depth = depth; } qreal AbstractThreeDAttributes::depth() const { return d->depth; } qreal AbstractThreeDAttributes::validDepth() const { return isEnabled() ? d->depth : 0.0; } bool AbstractThreeDAttributes::isThreeDBrushEnabled() const { return d->threeDBrushEnabled; } void AbstractThreeDAttributes::setThreeDBrushEnabled( bool enabled ) { d->threeDBrushEnabled = enabled; } QBrush AbstractThreeDAttributes::threeDBrush( const QBrush& brush, const QRectF& rect ) const { if ( isThreeDBrushEnabled() ) { QLinearGradient gr(rect.topLeft(), rect.bottomRight()); gr.setColorAt(0.0, brush.color()); gr.setColorAt(0.5, brush.color().lighter(180)); gr.setColorAt(1.0, brush.color()); return QBrush(gr); } return brush; } #if !defined(QT_NO_DEBUG_STREAM) QDebug operator<<(QDebug dbg, const KChart::AbstractThreeDAttributes& a) { dbg << "enabled="< #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KChart; class Q_DECL_HIDDEN AttributesModel::Private { public: Private(); QMap< int, QMap< int, QMap< int, QVariant > > > dataMap; QMap< int, QMap< int, QVariant > > horizontalHeaderDataMap; QMap< int, QMap< int, QVariant > > verticalHeaderDataMap; QMap< int, QVariant > modelDataMap; QMap< int, QVariant > defaultsMap; int dataDimension; AttributesModel::PaletteType paletteType; Palette palette; }; AttributesModel::Private::Private() : dataDimension( 1 ), paletteType( AttributesModel::PaletteTypeDefault ), palette( Palette::defaultPalette() ) { } #define d d_func() AttributesModel::AttributesModel( QAbstractItemModel* model, QObject * parent/* = 0 */ ) : AbstractProxyModel( parent ), _d( new Private ) { setSourceModel( model ); setDefaultForRole( KChart::DataValueLabelAttributesRole, DataValueAttributes::defaultAttributesAsVariant() ); } AttributesModel::~AttributesModel() { delete _d; - _d = 0; + _d = nullptr; } void AttributesModel::initFrom( const AttributesModel* other ) { *d = *other->d; } bool AttributesModel::compareHeaderDataMaps( const QMap< int, QMap< int, QVariant > >& mapA, const QMap< int, QMap< int, QVariant > >& mapB ) const { if ( mapA.count() != mapB.count() ) { return false; } QMap< int, QMap< int, QVariant > >::const_iterator itA = mapA.constBegin(); QMap< int, QMap< int, QVariant > >::const_iterator itB = mapB.constBegin(); for ( ; itA != mapA.constEnd(); ++itA, ++itB ) { if ( itA->count() != itB->count() ) { return false; } QMap< int, QVariant >::const_iterator it2A = itA->constBegin(); QMap< int, QVariant >::const_iterator it2B = itB->constBegin(); for ( ; it2A != itA->constEnd(); ++it2A, ++it2B ) { if ( it2A.key() != it2B.key() ) { return false; } if ( !compareAttributes( it2A.key(), it2A.value(), it2B.value() ) ) { return false; } } } return true; } bool AttributesModel::compare( const AttributesModel* other ) const { if ( other == this ) { return true; } if ( !other || d->paletteType != other->d->paletteType ) { return false; } { if ( d->dataMap.count() != other->d->dataMap.count() ) { return false; } QMap< int, QMap< int, QMap > >::const_iterator itA = d->dataMap.constBegin(); QMap< int, QMap< int, QMap > >::const_iterator itB = other->d->dataMap.constBegin(); for ( ; itA != d->dataMap.constEnd(); ++itA, ++itB ) { if ( itA->count() != itB->count() ) { return false; } QMap< int, QMap< int, QVariant > >::const_iterator it2A = itA->constBegin(); QMap< int, QMap< int, QVariant > >::const_iterator it2B = itB->constBegin(); for ( ; it2A != itA->constEnd(); ++it2A, ++it2B ) { if ( it2A->count() != it2B->count() ) { return false; } QMap< int, QVariant >::const_iterator it3A = it2A->constBegin(); QMap< int, QVariant >::const_iterator it3B = it2B->constBegin(); for ( ; it3A != it2A->constEnd(); ++it3A, ++it3B ) { if ( it3A.key() != it3B.key() ) { return false; } if ( !compareAttributes( it3A.key(), it3A.value(), it3B.value() ) ) { return false; } } } } } if ( !compareHeaderDataMaps( d->horizontalHeaderDataMap, other->d->horizontalHeaderDataMap ) || !compareHeaderDataMaps( d->verticalHeaderDataMap, other->d->verticalHeaderDataMap ) ) { return false; } { if ( d->modelDataMap.count() != other->d->modelDataMap.count() ) { return false; } QMap< int, QVariant >::const_iterator itA = d->modelDataMap.constBegin(); QMap< int, QVariant >::const_iterator itB = other->d->modelDataMap.constBegin(); for ( ; itA != d->modelDataMap.constEnd(); ++itA, ++itB ) { if ( itA.key() != itB.key() ) { return false; } if ( !compareAttributes( itA.key(), itA.value(), itB.value() ) ) { return false; } } } return true; } bool AttributesModel::compareAttributes( int role, const QVariant& a, const QVariant& b ) const { if ( isKnownAttributesRole( role ) ) { switch ( role ) { case DataValueLabelAttributesRole: return (a.value() == b.value()); case DatasetBrushRole: return (a.value() == b.value()); case DatasetPenRole: return (a.value() == b.value()); case ThreeDAttributesRole: // As of yet there is no ThreeDAttributes class, // and the AbstractThreeDAttributes class is pure virtual, // so we ignore this role for now. // (khz, 04.04.2007) /* return (qVariantValue( a ) == qVariantValue( b )); */ break; case LineAttributesRole: return (a.value() == b.value()); case ThreeDLineAttributesRole: return (a.value() == b.value()); case BarAttributesRole: return (a.value() == b.value()); case StockBarAttributesRole: return (a.value() == b.value()); case ThreeDBarAttributesRole: return (a.value() == b.value()); case PieAttributesRole: return (a.value() == b.value()); case ThreeDPieAttributesRole: return (a.value() == b.value()); case ValueTrackerAttributesRole: return (a.value() == b.value()); case DataHiddenRole: return (a.value() == b.value()); default: Q_ASSERT( false ); // all of our own roles need to be handled break; } } else { return (a == b); } return true; } QVariant AttributesModel::headerData( int section, Qt::Orientation orientation, int role/* = Qt::DisplayRole */ ) const { if ( sourceModel() ) { const QVariant sourceData = sourceModel()->headerData( section, orientation, role ); if ( sourceData.isValid() ) { return sourceData; } } // the source model didn't have data set, let's use our stored values const QMap< int, QMap< int, QVariant> >& map = orientation == Qt::Horizontal ? d->horizontalHeaderDataMap : d->verticalHeaderDataMap; QMap< int, QMap< int, QVariant > >::const_iterator mapIt = map.find( section ); if ( mapIt != map.constEnd() ) { const QMap< int, QVariant >& dataMap = mapIt.value(); QMap< int, QVariant >::const_iterator dataMapIt = dataMap.find( role ); if ( dataMapIt != dataMap.constEnd() ) { return dataMapIt.value(); } } return defaultHeaderData( section, orientation, role ); } QVariant AttributesModel::defaultHeaderData( int section, Qt::Orientation orientation, int role ) const { // Default values if nothing else matches const int dataset = section / d->dataDimension; switch ( role ) { case Qt::DisplayRole: //TODO for KChart 3.0: return QString::number( dataset + 1 ); return QVariant( (orientation == Qt::Vertical ? QStringLiteral("Series ") : QStringLiteral("Item ")) + QString::number( dataset )) ; case KChart::DatasetBrushRole: return d->palette.getBrush( dataset ); case KChart::DatasetPenRole: // if no per model override was set, use the (possibly default) color set for the brush if ( !modelData( role ).isValid() ) { QBrush brush = headerData( section, orientation, DatasetBrushRole ).value< QBrush >(); return QPen( brush.color() ); } default: break; } return QVariant(); } QVariant AttributesModel::data( int role ) const { if ( isKnownAttributesRole( role ) ) { // check if there is something set at global level QVariant v = modelData( role ); // else return the default setting, if any if ( !v.isValid() ) v = defaultsForRole( role ); return v; } return QVariant(); } QVariant AttributesModel::data( int column, int role ) const { if ( isKnownAttributesRole( role ) ) { // check if there is something set for the column (dataset) QVariant v; v = headerData( column, Qt::Horizontal, role ); // check if there is something set at global level if ( !v.isValid() ) v = data( role ); // includes automatic fallback to default return v; } return QVariant(); } QVariant AttributesModel::data( const QModelIndex& index, int role ) const { if ( index.isValid() ) { Q_ASSERT( index.model() == this ); } if ( !sourceModel() ) { return QVariant(); } if ( index.isValid() ) { const QVariant sourceData = sourceModel()->data( mapToSource( index ), role ); if ( sourceData.isValid() ) { return sourceData; } } // check if we are storing a value for this role at this cell index if ( d->dataMap.contains( index.column() ) ) { const QMap< int, QMap< int, QVariant > >& colDataMap = d->dataMap[ index.column() ]; if ( colDataMap.contains( index.row() ) ) { const QMap< int, QVariant >& dataMap = colDataMap[ index.row() ]; if ( dataMap.contains( role ) ) { const QVariant v = dataMap[ role ]; if ( v.isValid() ) { return v; } } } } // check if there is something set for the column (dataset), or at global level if ( index.isValid() ) { return data( index.column(), role ); // includes automatic fallback to default } return QVariant(); } bool AttributesModel::isKnownAttributesRole( int role ) const { switch ( role ) { // fallthrough intended case DataValueLabelAttributesRole: case DatasetBrushRole: case DatasetPenRole: case ThreeDAttributesRole: case LineAttributesRole: case ThreeDLineAttributesRole: case BarAttributesRole: case StockBarAttributesRole: case ThreeDBarAttributesRole: case PieAttributesRole: case ThreeDPieAttributesRole: case ValueTrackerAttributesRole: case DataHiddenRole: return true; default: return false; } } QVariant AttributesModel::defaultsForRole( int role ) const { // returns default-constructed QVariant if not found return d->defaultsMap.value( role ); } bool AttributesModel::setData ( const QModelIndex & index, const QVariant & value, int role ) { if ( !isKnownAttributesRole( role ) ) { return sourceModel()->setData( mapToSource(index), value, role ); } else { QMap< int, QMap< int, QVariant> > &colDataMap = d->dataMap[ index.column() ]; QMap< int, QVariant > &dataMap = colDataMap[ index.row() ]; dataMap.insert( role, value ); emit attributesChanged( index, index ); return true; } } bool AttributesModel::resetData ( const QModelIndex & index, int role ) { return setData( index, QVariant(), role ); } bool AttributesModel::setHeaderData ( int section, Qt::Orientation orientation, const QVariant & value, int role ) { if ( sourceModel() && headerData( section, orientation, role ) == value ) { return true; } if ( !isKnownAttributesRole( role ) ) { return sourceModel()->setHeaderData( section, orientation, value, role ); } else { QMap< int, QMap > §ionDataMap = orientation == Qt::Horizontal ? d->horizontalHeaderDataMap : d->verticalHeaderDataMap; QMap< int, QVariant > &dataMap = sectionDataMap[ section ]; dataMap.insert( role, value ); if ( sourceModel() ) { int numRows = rowCount( QModelIndex() ); int numCols = columnCount( QModelIndex() ); if ( orientation == Qt::Horizontal && numRows > 0 ) emit attributesChanged( index( 0, section, QModelIndex() ), index( numRows - 1, section, QModelIndex() ) ); else if ( orientation == Qt::Vertical && numCols > 0 ) emit attributesChanged( index( section, 0, QModelIndex() ), index( section, numCols - 1, QModelIndex() ) ); emit headerDataChanged( orientation, section, section ); // FIXME: This only makes sense for orientation == Qt::Horizontal, // but what if orientation == Qt::Vertical? if ( section != -1 && numRows > 0 ) emit dataChanged( index( 0, section, QModelIndex() ), index( numRows - 1, section, QModelIndex() ) ); } return true; } } bool AttributesModel::resetHeaderData ( int section, Qt::Orientation orientation, int role ) { return setHeaderData ( section, orientation, QVariant(), role ); } void AttributesModel::setPaletteType( AttributesModel::PaletteType type ) { if ( d->paletteType == type ) { return; } d->paletteType = type; switch ( type ) { case PaletteTypeDefault: d->palette = Palette::defaultPalette(); break; case PaletteTypeSubdued: d->palette = Palette::subduedPalette(); break; case PaletteTypeRainbow: d->palette = Palette::rainbowPalette(); break; default: qWarning( "Unknown palette type!" ); } } AttributesModel::PaletteType AttributesModel::paletteType() const { return d->paletteType; } bool KChart::AttributesModel::setModelData( const QVariant value, int role ) { d->modelDataMap.insert( role, value ); int numRows = rowCount( QModelIndex() ); int numCols = columnCount( QModelIndex() ); if ( sourceModel() && numRows > 0 && numCols > 0 ) { emit attributesChanged( index( 0, 0, QModelIndex() ), index( numRows - 1, numCols - 1, QModelIndex() ) ); beginResetModel(); endResetModel(); } return true; } QVariant KChart::AttributesModel::modelData( int role ) const { return d->modelDataMap.value( role, QVariant() ); } int AttributesModel::rowCount( const QModelIndex& index ) const { if ( sourceModel() ) { return sourceModel()->rowCount( mapToSource(index) ); } else { return 0; } } int AttributesModel::columnCount( const QModelIndex& index ) const { if ( sourceModel() ) { return sourceModel()->columnCount( mapToSource(index) ); } else { return 0; } } void AttributesModel::setSourceModel( QAbstractItemModel* sourceModel ) { - if ( this->sourceModel() != 0 ) + if ( this->sourceModel() != nullptr ) { disconnect( this->sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(slotDataChanged(QModelIndex,QModelIndex))); disconnect( this->sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(slotRowsInserted(QModelIndex,int,int)) ); disconnect( this->sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(slotRowsRemoved(QModelIndex,int,int)) ); disconnect( this->sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(slotRowsAboutToBeInserted(QModelIndex,int,int)) ); disconnect( this->sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)) ); disconnect( this->sourceModel(), SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(slotColumnsInserted(QModelIndex,int,int)) ); disconnect( this->sourceModel(), SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(slotColumnsRemoved(QModelIndex,int,int)) ); disconnect( this->sourceModel(), SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(slotColumnsAboutToBeInserted(QModelIndex,int,int)) ); disconnect( this->sourceModel(), SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(slotColumnsAboutToBeRemoved(QModelIndex,int,int)) ); disconnect( this->sourceModel(), SIGNAL(modelReset()), this, SIGNAL(modelReset()) ); disconnect( this->sourceModel(), SIGNAL(layoutChanged()), this, SIGNAL(layoutChanged()) ); } QAbstractProxyModel::setSourceModel( sourceModel ); - if ( this->sourceModel() != NULL ) + if ( this->sourceModel() != nullptr ) { connect( this->sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(slotDataChanged(QModelIndex,QModelIndex))); connect( this->sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(slotRowsInserted(QModelIndex,int,int)) ); connect( this->sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(slotRowsRemoved(QModelIndex,int,int)) ); connect( this->sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(slotRowsAboutToBeInserted(QModelIndex,int,int)) ); connect( this->sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)) ); connect( this->sourceModel(), SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(slotColumnsInserted(QModelIndex,int,int)) ); connect( this->sourceModel(), SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(slotColumnsRemoved(QModelIndex,int,int)) ); connect( this->sourceModel(), SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(slotColumnsAboutToBeInserted(QModelIndex,int,int)) ); connect( this->sourceModel(), SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(slotColumnsAboutToBeRemoved(QModelIndex,int,int)) ); connect( this->sourceModel(), SIGNAL(modelReset()), this, SIGNAL(modelReset()) ); connect( this->sourceModel(), SIGNAL(layoutChanged()), this, SIGNAL(layoutChanged()) ); } } void AttributesModel::slotRowsAboutToBeInserted( const QModelIndex& parent, int start, int end ) { beginInsertRows( mapFromSource( parent ), start, end ); } void AttributesModel::slotColumnsAboutToBeInserted( const QModelIndex& parent, int start, int end ) { beginInsertColumns( mapFromSource( parent ), start, end ); } void AttributesModel::slotRowsInserted( const QModelIndex& parent, int start, int end ) { Q_UNUSED( parent ); Q_UNUSED( start ); Q_UNUSED( end ); endInsertRows(); } void AttributesModel::slotColumnsInserted( const QModelIndex& parent, int start, int end ) { Q_UNUSED( parent ); Q_UNUSED( start ); Q_UNUSED( end ); endInsertColumns(); } void AttributesModel::slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end ) { beginRemoveRows( mapFromSource( parent ), start, end ); } void AttributesModel::slotColumnsAboutToBeRemoved( const QModelIndex& parent, int start, int end ) { beginRemoveColumns( mapFromSource( parent ), start, end ); } void AttributesModel::slotRowsRemoved( const QModelIndex& parent, int start, int end ) { Q_UNUSED( parent ); Q_UNUSED( start ); Q_UNUSED( end ); endRemoveRows(); } void AttributesModel::removeEntriesFromDataMap( int start, int end ) { QMap< int, QMap< int, QMap< int, QVariant > > >::iterator it = d->dataMap.find( end ); // check that the element was found if ( it != d->dataMap.end() ) { ++it; QVector< int > indexesToDel; for ( int i = start; i < end && it != d->dataMap.end(); ++i ) { d->dataMap[ i ] = it.value(); indexesToDel << it.key(); ++it; } if ( indexesToDel.isEmpty() ) { for ( int i = start; i < end; ++i ) { indexesToDel << i; } } for ( int i = 0; i < indexesToDel.count(); ++i ) { d->dataMap.remove( indexesToDel[ i ] ); } } } void AttributesModel::removeEntriesFromDirectionDataMaps( Qt::Orientation dir, int start, int end ) { QMap > §ionDataMap = dir == Qt::Horizontal ? d->horizontalHeaderDataMap : d->verticalHeaderDataMap; QMap >::iterator it = sectionDataMap.upperBound( end ); // check that the element was found if ( it != sectionDataMap.end() ) { QVector< int > indexesToDel; for ( int i = start; i < end && it != sectionDataMap.end(); ++i ) { sectionDataMap[ i ] = it.value(); indexesToDel << it.key(); ++it; } if ( indexesToDel.isEmpty() ) { for ( int i = start; i < end; ++i ) { indexesToDel << i; } } for ( int i = 0; i < indexesToDel.count(); ++i ) { sectionDataMap.remove( indexesToDel[ i ] ); } } } void AttributesModel::slotColumnsRemoved( const QModelIndex& parent, int start, int end ) { Q_UNUSED( parent ); Q_UNUSED( start ); Q_UNUSED( end ); Q_ASSERT_X( sourceModel(), "removeColumn", "This should only be triggered if a valid source Model exists!" ); for ( int i = start; i <= end; ++i ) { d->verticalHeaderDataMap.remove( start ); } removeEntriesFromDataMap( start, end ); removeEntriesFromDirectionDataMaps( Qt::Horizontal, start, end ); removeEntriesFromDirectionDataMaps( Qt::Vertical, start, end ); endRemoveColumns(); } void AttributesModel::slotDataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ) { emit dataChanged( mapFromSource( topLeft ), mapFromSource( bottomRight ) ); } void AttributesModel::setDefaultForRole( int role, const QVariant& value ) { if ( value.isValid() ) { d->defaultsMap.insert( role, value ); } else { // erase the possibily existing value to not let the map grow: QMap::iterator it = d->defaultsMap.find( role ); if ( it != d->defaultsMap.end() ) { d->defaultsMap.erase( it ); } } Q_ASSERT( defaultsForRole( role ).value() == value.value() ); } void AttributesModel::setDatasetDimension( int dimension ) { //### need to "reformat" or throw away internal data? d->dataDimension = dimension; } int AttributesModel::datasetDimension() const { return d->dataDimension; } diff --git a/src/KChart/KChartAttributesModel.h b/src/KChart/KChartAttributesModel.h index 06de446..1ca4a12 100644 --- a/src/KChart/KChartAttributesModel.h +++ b/src/KChart/KChartAttributesModel.h @@ -1,149 +1,149 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __KCHART_ATTRIBUTES_MODEL_H__ #define __KCHART_ATTRIBUTES_MODEL_H__ #include "KChartAbstractProxyModel.h" #include #include #include "KChartGlobal.h" namespace KChart { /** * @brief A proxy model used for decorating data with attributes. * * An AttributesModel forwards data from and to the source model and adds attributes, * data that influences the graphical rendering of the source model data. * The attributes are distinguished from the source model's data by their @p role values. * Therefore this class does not need to, and does not, change the data layout from the * source model's; indexes that refer to the same data have the same row and column * values in both models. * Attribute changes, that is changes to data with the attribute role, via the interface * of this class (including setData()) are stored internally and not forwarded to the source model. */ class KCHART_EXPORT AttributesModel : public AbstractProxyModel { Q_OBJECT KCHART_DECLARE_PRIVATE_BASE_POLYMORPHIC( AttributesModel ) public: enum PaletteType { PaletteTypeDefault = 0, PaletteTypeRainbow = 1, PaletteTypeSubdued = 2 }; - explicit AttributesModel( QAbstractItemModel* model, QObject * parent = 0 ); + explicit AttributesModel( QAbstractItemModel* model, QObject * parent = nullptr ); ~AttributesModel(); /** Copies the internal data (maps and palette) of another * AttributesModel* into this one. */ void initFrom( const AttributesModel* other ); /** Returns true if both, all of the attributes set, and * the palette set is equal in both of the AttributeModels. */ bool compare( const AttributesModel* other ) const; bool compareAttributes( int role, const QVariant& a, const QVariant& b ) const; /* Attributes Model specific API */ bool setModelData( const QVariant value, int role ); QVariant modelData( int role ) const; /** Returns whether the given role corresponds to one of the known * internally used ones. */ bool isKnownAttributesRole( int role ) const; /** Sets the palettetype used by this attributesmodel */ void setPaletteType( PaletteType type ); PaletteType paletteType() const; /** Returns the data that were specified at global level, * or the default data, or QVariant(). */ QVariant data(int role) const; /** Returns the data that were specified at per column level, * or the globally set data, or the default data, or QVariant(). */ QVariant data(int column, int role) const; /** \reimpl */ QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const Q_DECL_OVERRIDE; /** \reimpl */ int rowCount(const QModelIndex& ) const Q_DECL_OVERRIDE; /** \reimpl */ int columnCount(const QModelIndex& ) const Q_DECL_OVERRIDE; /** \reimpl */ QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; /** \reimpl */ bool setData ( const QModelIndex & index, const QVariant & value, int role = Qt::DisplayRole) Q_DECL_OVERRIDE; /** Remove any explicit attributes settings that might have been specified before. */ bool resetData ( const QModelIndex & index, int role = Qt::DisplayRole); /** \reimpl */ bool setHeaderData ( int section, Qt::Orientation orientation, const QVariant & value, int role = Qt::DisplayRole) Q_DECL_OVERRIDE; /** Returns default values for the header data. */ virtual QVariant defaultHeaderData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; /** Remove any explicit attributes settings that might have been specified before. */ bool resetHeaderData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole); /** \reimpl */ void setSourceModel ( QAbstractItemModel* sourceModel ) Q_DECL_OVERRIDE; /** Define the default value for a certain role. Passing a default-constructed QVariant is equivalent to removing the default. */ void setDefaultForRole( int role, const QVariant& value ); /** Set the dimension of the dataset in the source model. \sa AbstractDiagram::setDatasetDimension */ void setDatasetDimension( int dimension ); int datasetDimension() const; Q_SIGNALS: void attributesChanged( const QModelIndex&, const QModelIndex& ); private Q_SLOTS: void slotRowsAboutToBeInserted( const QModelIndex& parent, int start, int end ); void slotColumnsAboutToBeInserted( const QModelIndex& parent, int start, int end ); void slotRowsInserted( const QModelIndex& parent, int start, int end ); void slotColumnsInserted( const QModelIndex& parent, int start, int end ); void slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end ); void slotColumnsAboutToBeRemoved( const QModelIndex& parent, int start, int end ); void slotRowsRemoved( const QModelIndex& parent, int start, int end ); void slotColumnsRemoved( const QModelIndex& parent, int start, int end ); void slotDataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ); private: // helper QVariant defaultsForRole( int role ) const; bool compareHeaderDataMaps( const QMap< int, QMap< int, QVariant > >& mapA, const QMap< int, QMap< int, QVariant > >& mapB ) const; void removeEntriesFromDataMap( int start, int end ); void removeEntriesFromDirectionDataMaps( Qt::Orientation dir, int start, int end ); }; } #endif diff --git a/src/KChart/KChartBackgroundAttributes.cpp b/src/KChart/KChartBackgroundAttributes.cpp index 6f93c6e..7ab910a 100644 --- a/src/KChart/KChartBackgroundAttributes.cpp +++ b/src/KChart/KChartBackgroundAttributes.cpp @@ -1,155 +1,155 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartBackgroundAttributes.h" #include "KChartMath_p.h" #include #define d d_func() using namespace KChart; class Q_DECL_HIDDEN BackgroundAttributes::Private { friend class KChart::BackgroundAttributes; public: Private(); private: bool visible; QBrush brush; BackgroundPixmapMode pixmapMode; QPixmap pixmap; }; BackgroundAttributes::Private::Private() : visible( false ), pixmapMode( BackgroundAttributes::BackgroundPixmapModeNone ) { } BackgroundAttributes::BackgroundAttributes() : _d( new Private() ) { } BackgroundAttributes::BackgroundAttributes( const BackgroundAttributes& r ) : _d( new Private( *r.d ) ) { } BackgroundAttributes & BackgroundAttributes::operator=( const BackgroundAttributes& r ) { if ( this == &r ) return *this; *d = *r.d; return *this; } bool BackgroundAttributes::operator==( const BackgroundAttributes& r ) const { return isEqualTo( r ); } bool BackgroundAttributes::isEqualTo( const BackgroundAttributes& other, bool ignorePixmap ) const { /* qDebug() << "BackgroundAttributes::operator=="; qDebug() << "isVisible" << (isVisible() == other.isVisible()); qDebug() << "brush" << (brush() == other.brush()); qDebug() << "pixmapMode"<< (pixmapMode() == other.pixmapMode()); qDebug() << "pixmap" << (pixmap().serialNumber() == other.pixmap().serialNumber()); */ return ( isVisible() == other.isVisible() && brush() == other.brush() && pixmapMode() == other.pixmapMode() && (ignorePixmap || pixmap().cacheKey() == other.pixmap().cacheKey()) ); } BackgroundAttributes::~BackgroundAttributes() { - delete _d; _d = 0; + delete _d; _d = nullptr; } void BackgroundAttributes::setVisible( bool visible ) { d->visible = visible; } bool BackgroundAttributes::isVisible() const { return d->visible; } void BackgroundAttributes::setBrush( const QBrush &brush ) { d->brush = brush; } QBrush BackgroundAttributes::brush() const { return d->brush; } void BackgroundAttributes::setPixmapMode( BackgroundPixmapMode mode ) { d->pixmapMode = mode; } BackgroundAttributes::BackgroundPixmapMode BackgroundAttributes::pixmapMode() const { return d->pixmapMode; } void BackgroundAttributes::setPixmap( const QPixmap &backPixmap ) { d->pixmap = backPixmap; } QPixmap BackgroundAttributes::pixmap() const { return d->pixmap; } #if !defined(QT_NO_DEBUG_STREAM) QDebug operator<<(QDebug dbg, const KChart::BackgroundAttributes& ba) { dbg << "KChart::BackgroundAttributes(" << "visible="<. */ #include "KChartChart.h" #include "KChartChart_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "KChartCartesianCoordinatePlane.h" #include "KChartAbstractCartesianDiagram.h" #include "KChartHeaderFooter.h" #include "KChartEnums.h" #include "KChartLegend.h" #include "KChartLayoutItems.h" #include #include #include "KChartPainterSaver_p.h" #include "KChartPrintingParameters.h" #include #if defined KDAB_EVAL #include "../evaldialog/evaldialog.h" #endif #if 0 // dumpLayoutTree dumps a QLayout tree in a hopefully easy to read format to stderr - feel free to // use, improve and extend; it is very useful for looking at any layout problem. #include // this is this different from both QRect::isEmpty() and QRect::isNull() for "wrong" QRects, // i.e. those where topLeft() is actually below and / or right of bottomRight(). static bool isZeroArea(const QRect &r) { return !r.width() || !r.height(); } static QString lineProlog(int nestingDepth, int lineno) { QString numbering(QString::number(lineno).rightJustified(5).append(QChar::fromAscii(':'))); QString indent(nestingDepth * 4, QLatin1Char(' ')); return numbering + indent; } static void dumpLayoutTreeRecurse(QLayout *l, int *counter, int depth) { const QLatin1String colorOn(isZeroArea(l->geometry()) ? "\033[0m" : "\033[32m"); const QLatin1String colorOff("\033[0m"); QString prolog = lineProlog(depth, *counter); (*counter)++; qDebug() << colorOn + prolog << l->metaObject()->className() << l->geometry() << "hint" << l->sizeHint() << l->hasHeightForWidth() << "min" << l->minimumSize() << "max" << l->maximumSize() << l->expandingDirections() << l->alignment() << colorOff; for (int i = 0; i < l->count(); i++) { QLayoutItem *child = l->itemAt(i); if (QLayout *childL = child->layout()) { dumpLayoutTreeRecurse(childL, counter, depth + 1); } else { // The isZeroArea check culls usually largely useless output - you might want to remove it in // some debugging situations. Add a boolean parameter to this and dumpLayoutTree() if you do. if (!isZeroArea(child->geometry())) { prolog = lineProlog(depth + 1, *counter); (*counter)++; qDebug() << colorOn + prolog << typeid(*child).name() << child->geometry() << "hint" << child->sizeHint() << child->hasHeightForWidth() << "min" << child->minimumSize() << "max" << child->maximumSize() << child->expandingDirections() << child->alignment() << colorOff; } } } } static void dumpLayoutTree(QLayout *l) { int counter = 0; dumpLayoutTreeRecurse(l, &counter, 0); } #endif static const Qt::Alignment s_gridAlignments[ 3 ][ 3 ] = { // [ row ][ column ] { Qt::AlignTop | Qt::AlignLeft, Qt::AlignTop | Qt::AlignHCenter, Qt::AlignTop | Qt::AlignRight }, { Qt::AlignVCenter | Qt::AlignLeft, Qt::AlignVCenter | Qt::AlignHCenter, Qt::AlignVCenter | Qt::AlignRight }, { Qt::AlignBottom | Qt::AlignLeft, Qt::AlignBottom | Qt::AlignHCenter, Qt::AlignBottom | Qt::AlignRight } }; static void getRowAndColumnForPosition(KChartEnums::PositionValue pos, int* row, int* column) { switch ( pos ) { case KChartEnums::PositionNorthWest: *row = 0; *column = 0; break; case KChartEnums::PositionNorth: *row = 0; *column = 1; break; case KChartEnums::PositionNorthEast: *row = 0; *column = 2; break; case KChartEnums::PositionEast: *row = 1; *column = 2; break; case KChartEnums::PositionSouthEast: *row = 2; *column = 2; break; case KChartEnums::PositionSouth: *row = 2; *column = 1; break; case KChartEnums::PositionSouthWest: *row = 2; *column = 0; break; case KChartEnums::PositionWest: *row = 1; *column = 0; break; case KChartEnums::PositionCenter: *row = 1; *column = 1; break; default: *row = -1; *column = -1; break; } } using namespace KChart; // Layout widgets even if they are not visible (that's why isEmpty() is overridden) - at least that // was the original reason... class MyWidgetItem : public QWidgetItem { public: explicit MyWidgetItem(QWidget *w, Qt::Alignment alignment = Qt::Alignment()) : QWidgetItem( w ) { setAlignment( alignment ); } // All of the methods are reimplemented from QWidgetItem, and work around some oddity in QLayout and / or // KD Chart - I forgot the details between writing this code as an experiment and committing it, very // sorry about that! // Feel free to comment out any of them and then try the line-breaking feature in horizontal legends in // the Legends/Advanced example. It will not work well in various ways - won't get enough space and look // very broken, will inhibit resizing the window etc. QSize sizeHint() const Q_DECL_OVERRIDE { QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); return w->sizeHint(); } QSize minimumSize() const Q_DECL_OVERRIDE { QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); return w->minimumSize(); } QSize maximumSize() const Q_DECL_OVERRIDE { // Not just passing on w->maximumSize() fixes that the size policy of Legend is disregarded, making // Legend take all available space, which makes both the Legend internal layout and the overall // layout of chart + legend look bad. QWidget::maximumSize() is not a virtual method, it's a // property, so "overriding" that one would be even uglier, and prevent user-set property // values from doing anything. QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); QSize ret = w->maximumSize(); const QSize hint = w->sizeHint(); const QSizePolicy::Policy hPolicy = w->sizePolicy().horizontalPolicy(); if (hPolicy == QSizePolicy::Fixed || hPolicy == QSizePolicy::Maximum) { ret.rwidth() = hint.width(); } const QSizePolicy::Policy vPolicy = w->sizePolicy().verticalPolicy(); if (vPolicy == QSizePolicy::Fixed || vPolicy == QSizePolicy::Maximum) { ret.rheight() = hint.height(); } return ret; } Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE { QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); if ( isEmpty() ) { return Qt::Orientations(0); } Qt::Orientations e = w->sizePolicy().expandingDirections(); return e; } void setGeometry(const QRect &g) Q_DECL_OVERRIDE { QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); w->setGeometry(g); } QRect geometry() const Q_DECL_OVERRIDE { QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); return w->geometry(); } bool hasHeightForWidth() const Q_DECL_OVERRIDE { QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); bool ret = !isEmpty() && qobject_cast< Legend* >( w )->hasHeightForWidth(); return ret; } int heightForWidth( int width ) const Q_DECL_OVERRIDE { QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); int ret = w->heightForWidth( width ); return ret; } bool isEmpty() const Q_DECL_OVERRIDE { QWidget* w = const_cast< MyWidgetItem * >( this )->widget(); // legend->hide() should indeed hide the legend, // but a legend in a chart that hasn't been shown yet isn't hidden // (as can happen when using Chart::paint() without showing the chart) return w->isHidden() && w->testAttribute( Qt::WA_WState_ExplicitShowHide ); } }; // When "abusing" QLayouts to lay out items with different geometry from the backing QWidgets, // some manual work is required to correctly update all the sublayouts. // This is because all the convenient ways to deal with QLayouts assume QWidgets somewhere. // What this does is somewhat similar to QLayout::activate(), but it never refers to the parent // QWidget which has the wrong geometry. static void invalidateLayoutTree( QLayoutItem *item ) { QLayout *layout = item->layout(); if ( layout ) { const int count = layout->count(); for ( int i = 0; i < count; i++ ) { invalidateLayoutTree( layout->itemAt( i ) ); } } item->invalidate(); } void Chart::Private::slotUnregisterDestroyedLegend( Legend *l ) { chart->takeLegend( l ); } void Chart::Private::slotUnregisterDestroyedHeaderFooter( HeaderFooter* hf ) { chart->takeHeaderFooter( hf ); } void Chart::Private::slotUnregisterDestroyedPlane( AbstractCoordinatePlane* plane ) { coordinatePlanes.removeAll( plane ); Q_FOREACH ( AbstractCoordinatePlane* p, coordinatePlanes ) { if ( p->referenceCoordinatePlane() == plane) { - p->setReferenceCoordinatePlane( 0 ); + p->setReferenceCoordinatePlane( nullptr ); } } plane->layoutPlanes(); } Chart::Private::Private( Chart* chart_ ) : chart( chart_ ) , useNewLayoutSystem( false ) - , layout( 0 ) - , vLayout( 0 ) - , planesLayout( 0 ) - , headerLayout( 0 ) - , footerLayout( 0 ) - , dataAndLegendLayout( 0 ) - , leftOuterSpacer( 0 ) - , rightOuterSpacer( 0 ) - , topOuterSpacer( 0 ) - , bottomOuterSpacer( 0 ) + , layout(nullptr) + , vLayout(nullptr) + , planesLayout(nullptr) + , headerLayout(nullptr) + , footerLayout(nullptr) + , dataAndLegendLayout(nullptr) + , leftOuterSpacer(nullptr) + , rightOuterSpacer(nullptr) + , topOuterSpacer(nullptr) + , bottomOuterSpacer(nullptr) , isFloatingLegendsLayoutDirty( true ) , isPlanesLayoutDirty( true ) - , globalLeadingLeft( 0 ) - , globalLeadingRight( 0 ) - , globalLeadingTop( 0 ) - , globalLeadingBottom( 0 ) + , globalLeadingLeft(0) + , globalLeadingRight(0) + , globalLeadingTop(0) + , globalLeadingBottom(0) { for ( int row = 0; row < 3; ++row ) { for ( int column = 0; column < 3; ++column ) { for ( int i = 0; i < 2; i++ ) { - innerHdFtLayouts[ i ][ row ][ column ] = 0; + innerHdFtLayouts[ i ][ row ][ column ] = nullptr; } } } } Chart::Private::~Private() { } enum VisitorState{ Visited, Unknown }; struct ConnectedComponentsComparator{ bool operator()( const LayoutGraphNode *lhs, const LayoutGraphNode *rhs ) const { return lhs->priority < rhs->priority; } }; static QVector< LayoutGraphNode* > getPrioritySortedConnectedComponents( QVector< LayoutGraphNode* > &nodeList ) { QVector< LayoutGraphNode* >connectedComponents; QHash< LayoutGraphNode*, VisitorState > visitedComponents; Q_FOREACH ( LayoutGraphNode* node, nodeList ) visitedComponents[ node ] = Unknown; for ( int i = 0; i < nodeList.size(); ++i ) { LayoutGraphNode *curNode = nodeList[ i ]; LayoutGraphNode *representativeNode = curNode; if ( visitedComponents[ curNode ] != Visited ) { QStack< LayoutGraphNode* > stack; stack.push( curNode ); while ( !stack.isEmpty() ) { curNode = stack.pop(); Q_ASSERT( visitedComponents[ curNode ] != Visited ); visitedComponents[ curNode ] = Visited; if ( curNode->bottomSuccesor && visitedComponents[ curNode->bottomSuccesor ] != Visited ) stack.push( curNode->bottomSuccesor ); if ( curNode->leftSuccesor && visitedComponents[ curNode->leftSuccesor ] != Visited ) stack.push( curNode->leftSuccesor ); if ( curNode->sharedSuccesor && visitedComponents[ curNode->sharedSuccesor ] != Visited ) stack.push( curNode->sharedSuccesor ); if ( curNode->priority < representativeNode->priority ) representativeNode = curNode; } connectedComponents.append( representativeNode ); } } std::sort( connectedComponents.begin(), connectedComponents.end(), ConnectedComponentsComparator() ); return connectedComponents; } struct PriorityComparator{ public: PriorityComparator( QHash< AbstractCoordinatePlane*, LayoutGraphNode* > mapping ) : m_mapping( mapping ) {} bool operator() ( AbstractCoordinatePlane *lhs, AbstractCoordinatePlane *rhs ) const { const LayoutGraphNode *lhsNode = m_mapping[ lhs ]; Q_ASSERT( lhsNode ); const LayoutGraphNode *rhsNode = m_mapping[ rhs ]; Q_ASSERT( rhsNode ); return lhsNode->priority < rhsNode->priority; } const QHash< AbstractCoordinatePlane*, LayoutGraphNode* > m_mapping; }; void checkExistingAxes( LayoutGraphNode* node ) { if ( node && node->diagramPlane && node->diagramPlane->diagram() ) { AbstractCartesianDiagram *diag = qobject_cast< AbstractCartesianDiagram* >( node->diagramPlane->diagram() ); if ( diag ) { Q_FOREACH( const CartesianAxis* axis, diag->axes() ) { switch ( axis->position() ) { case( CartesianAxis::Top ): node->topAxesLayout = true; break; case( CartesianAxis::Bottom ): node->bottomAxesLayout = true; break; case( CartesianAxis::Left ): node->leftAxesLayout = true; break; case( CartesianAxis::Right ): node->rightAxesLayout = true; break; } } } } } static void mergeNodeAxisInformation( LayoutGraphNode* lhs, LayoutGraphNode* rhs ) { lhs->topAxesLayout |= rhs->topAxesLayout; rhs->topAxesLayout = lhs->topAxesLayout; lhs->bottomAxesLayout |= rhs->bottomAxesLayout; rhs->bottomAxesLayout = lhs->bottomAxesLayout; lhs->leftAxesLayout |= rhs->leftAxesLayout; rhs->leftAxesLayout = lhs->leftAxesLayout; lhs->rightAxesLayout |= rhs->rightAxesLayout; rhs->rightAxesLayout = lhs->rightAxesLayout; } static CoordinatePlaneList findSharingAxisDiagrams( AbstractCoordinatePlane* plane, const CoordinatePlaneList& list, Chart::Private::AxisType type, QVector< CartesianAxis* >* sharedAxes ) { if ( !plane || !plane->diagram() ) return CoordinatePlaneList(); Q_ASSERT( plane ); Q_ASSERT( plane->diagram() ); CoordinatePlaneList result; AbstractCartesianDiagram* diagram = qobject_cast< AbstractCartesianDiagram* >( plane->diagram() ); if ( !diagram ) return CoordinatePlaneList(); QList< CartesianAxis* > axes; Q_FOREACH( CartesianAxis* axis, diagram->axes() ) { if ( ( type == Chart::Private::Ordinate && ( axis->position() == CartesianAxis::Left || axis->position() == CartesianAxis::Right ) ) || ( type == Chart::Private::Abscissa && ( axis->position() == CartesianAxis::Top || axis->position() == CartesianAxis::Bottom ) ) ) { axes.append( axis ); } } Q_FOREACH( AbstractCoordinatePlane *curPlane, list ) { AbstractCartesianDiagram* diagram = qobject_cast< AbstractCartesianDiagram* > ( curPlane->diagram() ); if ( !diagram ) continue; Q_FOREACH( CartesianAxis* curSearchedAxis, axes ) { Q_FOREACH( CartesianAxis* curAxis, diagram->axes() ) { if ( curSearchedAxis == curAxis ) { result.append( curPlane ); if ( !sharedAxes->contains( curSearchedAxis ) ) sharedAxes->append( curSearchedAxis ); } } } } return result; } /** * this method determines the needed layout of the graph * taking care of the sharing problematic * its NOT allowed to have a diagram that shares * more than one axis in the same direction */ QVector< LayoutGraphNode* > Chart::Private::buildPlaneLayoutGraph() { QHash< AbstractCoordinatePlane*, LayoutGraphNode* > planeNodeMapping; QVector< LayoutGraphNode* > allNodes; // create all nodes and a mapping between plane and nodes Q_FOREACH( AbstractCoordinatePlane* curPlane, coordinatePlanes ) { if ( curPlane->diagram() ) { allNodes.append( new LayoutGraphNode ); allNodes[ allNodes.size() - 1 ]->diagramPlane = curPlane; allNodes[ allNodes.size() - 1 ]->priority = allNodes.size(); checkExistingAxes( allNodes[ allNodes.size() - 1 ] ); planeNodeMapping[ curPlane ] = allNodes[ allNodes.size() - 1 ]; } } // build the graph connections Q_FOREACH( LayoutGraphNode* curNode, allNodes ) { QVector< CartesianAxis* > sharedAxes; CoordinatePlaneList xSharedPlanes = findSharingAxisDiagrams( curNode->diagramPlane, coordinatePlanes, Abscissa, &sharedAxes ); Q_ASSERT( sharedAxes.size() < 2 ); // TODO duplicated code make a method out of it if ( sharedAxes.size() == 1 && xSharedPlanes.size() > 1 ) { //xSharedPlanes.removeAll( sharedAxes.first()->diagram()->coordinatePlane() ); //std::sort( xSharedPlanes.begin(), xSharedPlanes.end(), PriorityComparator( planeNodeMapping ) ); for ( int i = 0; i < xSharedPlanes.size() - 1; ++i ) { LayoutGraphNode *tmpNode = planeNodeMapping[ xSharedPlanes[ i ] ]; Q_ASSERT( tmpNode ); LayoutGraphNode *tmpNode2 = planeNodeMapping[ xSharedPlanes[ i + 1 ] ]; Q_ASSERT( tmpNode2 ); tmpNode->bottomSuccesor = tmpNode2; } // if ( sharedAxes.first()->diagram() && sharedAxes.first()->diagram()->coordinatePlane() ) // { // LayoutGraphNode *lastNode = planeNodeMapping[ xSharedPlanes.last() ]; // Q_ASSERT( lastNode ); // Q_ASSERT( sharedAxes.first()->diagram()->coordinatePlane() ); // LayoutGraphNode *ownerNode = planeNodeMapping[ sharedAxes.first()->diagram()->coordinatePlane() ]; // Q_ASSERT( ownerNode ); // lastNode->bottomSuccesor = ownerNode; // } //merge AxisInformation, needs a two pass run LayoutGraphNode axisInfoNode; for ( int count = 0; count < 2; ++count ) { for ( int i = 0; i < xSharedPlanes.size(); ++i ) { mergeNodeAxisInformation( &axisInfoNode, planeNodeMapping[ xSharedPlanes[ i ] ] ); } } } sharedAxes.clear(); CoordinatePlaneList ySharedPlanes = findSharingAxisDiagrams( curNode->diagramPlane, coordinatePlanes, Ordinate, &sharedAxes ); Q_ASSERT( sharedAxes.size() < 2 ); if ( sharedAxes.size() == 1 && ySharedPlanes.size() > 1 ) { //ySharedPlanes.removeAll( sharedAxes.first()->diagram()->coordinatePlane() ); //std::sort( ySharedPlanes.begin(), ySharedPlanes.end(), PriorityComparator( planeNodeMapping ) ); for ( int i = 0; i < ySharedPlanes.size() - 1; ++i ) { LayoutGraphNode *tmpNode = planeNodeMapping[ ySharedPlanes[ i ] ]; Q_ASSERT( tmpNode ); LayoutGraphNode *tmpNode2 = planeNodeMapping[ ySharedPlanes[ i + 1 ] ]; Q_ASSERT( tmpNode2 ); tmpNode->leftSuccesor = tmpNode2; } // if ( sharedAxes.first()->diagram() && sharedAxes.first()->diagram()->coordinatePlane() ) // { // LayoutGraphNode *lastNode = planeNodeMapping[ ySharedPlanes.last() ]; // Q_ASSERT( lastNode ); // Q_ASSERT( sharedAxes.first()->diagram()->coordinatePlane() ); // LayoutGraphNode *ownerNode = planeNodeMapping[ sharedAxes.first()->diagram()->coordinatePlane() ]; // Q_ASSERT( ownerNode ); // lastNode->bottomSuccesor = ownerNode; // } //merge AxisInformation, needs a two pass run LayoutGraphNode axisInfoNode; for ( int count = 0; count < 2; ++count ) { for ( int i = 0; i < ySharedPlanes.size(); ++i ) { mergeNodeAxisInformation( &axisInfoNode, planeNodeMapping[ ySharedPlanes[ i ] ] ); } } } sharedAxes.clear(); if ( curNode->diagramPlane->referenceCoordinatePlane() ) curNode->sharedSuccesor = planeNodeMapping[ curNode->diagramPlane->referenceCoordinatePlane() ]; } return allNodes; } QHash Chart::Private::buildPlaneLayoutInfos() { /* There are two ways in which planes can be caused to interact in * where they are put layouting wise: The first is the reference plane. If * such a reference plane is set, on a plane, it will use the same cell in the * layout as that one. In addition to this, planes can share an axis. In that case * they will be laid out in relation to each other as suggested by the position * of the axis. If, for example Plane1 and Plane2 share an axis at position Left, * that will result in the layout: Axis Plane1 Plane 2, vertically. If Plane1 * also happens to be Plane2's referece plane, both planes are drawn over each * other. The reference plane concept allows two planes to share the same space * even if neither has any axis, and in case there are shared axis, it is used * to decided, whether the planes should be painted on top of each other or * laid out vertically or horizontally next to each other. */ QHash axisInfos; QHash planeInfos; Q_FOREACH(AbstractCoordinatePlane* plane, coordinatePlanes ) { PlaneInfo p; // first check if we share space with another plane p.referencePlane = plane->referenceCoordinatePlane(); planeInfos.insert( plane, p ); Q_FOREACH( AbstractDiagram* abstractDiagram, plane->diagrams() ) { AbstractCartesianDiagram* diagram = qobject_cast ( abstractDiagram ); if ( !diagram ) { continue; } Q_FOREACH( CartesianAxis* axis, diagram->axes() ) { if ( !axisInfos.contains( axis ) ) { /* If this is the first time we see this axis, add it, with the * current plane. The first plane added to the chart that has * the axis associated with it thus "owns" it, and decides about * layout. */ AxisInfo i; i.plane = plane; axisInfos.insert( axis, i ); } else { AxisInfo i = axisInfos[axis]; if ( i.plane == plane ) { continue; // we don't want duplicates, only shared } /* The user expects diagrams to be added on top, and to the right * so that horizontally we need to move the new diagram, vertically * the reference one. */ PlaneInfo pi = planeInfos[plane]; // plane-to-plane linking overrides linking via axes if ( !pi.referencePlane ) { // we're not the first plane to see this axis, mark us as a slave pi.referencePlane = i.plane; if ( axis->position() == CartesianAxis::Left || axis->position() == CartesianAxis::Right ) { pi.horizontalOffset += 1; } planeInfos[plane] = pi; pi = planeInfos[i.plane]; if ( axis->position() == CartesianAxis::Top || axis->position() == CartesianAxis::Bottom ) { pi.verticalOffset += 1; } planeInfos[i.plane] = pi; } } } } // Create a new grid layout for each plane that has no reference. p = planeInfos[plane]; - if ( p.referencePlane == 0 ) { + if ( p.referencePlane == nullptr ) { p.gridLayout = new QGridLayout(); p.gridLayout->setMargin( 0 ); planeInfos[plane] = p; } } return planeInfos; } void Chart::Private::slotLayoutPlanes() { /*TODO make sure this is really needed */ const QBoxLayout::Direction oldPlanesDirection = planesLayout ? planesLayout->direction() : QBoxLayout::TopToBottom; if ( planesLayout && dataAndLegendLayout ) dataAndLegendLayout->removeItem( planesLayout ); - const bool hadPlanesLayout = planesLayout != 0; + const bool hadPlanesLayout = planesLayout != nullptr; int left, top, right, bottom; if ( hadPlanesLayout ) planesLayout->getContentsMargins(&left, &top, &right, &bottom); Q_FOREACH( AbstractLayoutItem* plane, planeLayoutItems ) { plane->removeFromParentLayout(); } //TODO they should get a correct parent, but for now it works Q_FOREACH( AbstractLayoutItem* plane, planeLayoutItems ) { if ( dynamic_cast< AutoSpacerLayoutItem* >( plane ) ) delete plane; } planeLayoutItems.clear(); delete planesLayout; //hint: The direction is configurable by the user now, as // we are using a QBoxLayout rather than a QVBoxLayout. (khz, 2007/04/25) planesLayout = new QBoxLayout( oldPlanesDirection ); isPlanesLayoutDirty = true; // here we create the layouts; we need to "run" them before painting if ( useNewLayoutSystem ) { gridPlaneLayout = new QGridLayout; planesLayout->addLayout( gridPlaneLayout ); if (hadPlanesLayout) planesLayout->setContentsMargins(left, top, right, bottom); planesLayout->setObjectName( QString::fromLatin1( "planesLayout" ) ); /* First go through all planes and all axes and figure out whether the planes * need to coordinate. If they do, they share a grid layout, if not, each * get their own. See buildPlaneLayoutInfos() for more details. */ QVector< LayoutGraphNode* > vals = buildPlaneLayoutGraph(); //qDebug() << Q_FUNC_INFO << "GraphNodes" << vals.size(); QVector< LayoutGraphNode* > connectedComponents = getPrioritySortedConnectedComponents( vals ); //qDebug() << Q_FUNC_INFO << "SubGraphs" << connectedComponents.size(); int row = 0; int col = 0; QSet< CartesianAxis* > laidOutAxes; for ( int i = 0; i < connectedComponents.size(); ++i ) { LayoutGraphNode *curComponent = connectedComponents[ i ]; for ( LayoutGraphNode *curRowComponent = curComponent; curRowComponent; curRowComponent = curRowComponent->bottomSuccesor ) { col = 0; for ( LayoutGraphNode *curColComponent = curRowComponent; curColComponent; curColComponent = curColComponent->leftSuccesor ) { Q_ASSERT( curColComponent->diagramPlane->diagrams().size() == 1 ); Q_FOREACH( AbstractDiagram* diagram, curColComponent->diagramPlane->diagrams() ) { const int planeRowOffset = 1;//curColComponent->topAxesLayout ? 1 : 0; const int planeColOffset = 1;//curColComponent->leftAxesLayout ? 1 : 0; //qDebug() << Q_FUNC_INFO << row << col << planeRowOffset << planeColOffset; //qDebug() << Q_FUNC_INFO << row + planeRowOffset << col + planeColOffset; planeLayoutItems << curColComponent->diagramPlane; AbstractCartesianDiagram *cartDiag = qobject_cast< AbstractCartesianDiagram* >( diagram ); if ( cartDiag ) { gridPlaneLayout->addItem( curColComponent->diagramPlane, row + planeRowOffset, col + planeColOffset, 2, 2 ); curColComponent->diagramPlane->setParentLayout( gridPlaneLayout ); - QHBoxLayout *leftLayout = 0; - QHBoxLayout *rightLayout = 0; - QVBoxLayout *topLayout = 0; - QVBoxLayout *bottomLayout = 0; + QHBoxLayout *leftLayout = nullptr; + QHBoxLayout *rightLayout = nullptr; + QVBoxLayout *topLayout = nullptr; + QVBoxLayout *bottomLayout = nullptr; if ( curComponent->sharedSuccesor ) { gridPlaneLayout->addItem( curColComponent->sharedSuccesor->diagramPlane, row + planeRowOffset, col + planeColOffset, 2, 2 ); curColComponent->sharedSuccesor->diagramPlane->setParentLayout( gridPlaneLayout ); planeLayoutItems << curColComponent->sharedSuccesor->diagramPlane; } Q_FOREACH( CartesianAxis* axis, cartDiag->axes() ) { if ( axis->isAbscissa() ) { if ( curColComponent->bottomSuccesor ) continue; } if ( laidOutAxes.contains( axis ) ) continue; // if ( axis->diagram() != diagram ) // continue; switch ( axis->position() ) { case( CartesianAxis::Top ): if ( !topLayout ) topLayout = new QVBoxLayout; topLayout->addItem( axis ); axis->setParentLayout( topLayout ); break; case( CartesianAxis::Bottom ): if ( !bottomLayout ) bottomLayout = new QVBoxLayout; bottomLayout->addItem( axis ); axis->setParentLayout( bottomLayout ); break; case( CartesianAxis::Left ): if ( !leftLayout ) leftLayout = new QHBoxLayout; leftLayout->addItem( axis ); axis->setParentLayout( leftLayout ); break; case( CartesianAxis::Right ): if ( !rightLayout ) { rightLayout = new QHBoxLayout; } rightLayout->addItem( axis ); axis->setParentLayout( rightLayout ); break; } planeLayoutItems << axis; laidOutAxes.insert( axis ); } if ( leftLayout ) gridPlaneLayout->addLayout( leftLayout, row + planeRowOffset, col, 2, 1, Qt::AlignRight | Qt::AlignVCenter ); if ( rightLayout ) gridPlaneLayout->addLayout( rightLayout, row, col + planeColOffset + 2, 2, 1, Qt::AlignLeft | Qt::AlignVCenter ); if ( topLayout ) gridPlaneLayout->addLayout( topLayout, row, col + planeColOffset, 1, 2, Qt::AlignBottom | Qt::AlignHCenter ); if ( bottomLayout ) gridPlaneLayout->addLayout( bottomLayout, row + planeRowOffset + 2, col + planeColOffset, 1, 2, Qt::AlignTop | Qt::AlignHCenter ); } else { gridPlaneLayout->addItem( curColComponent->diagramPlane, row, col, 4, 4 ); curColComponent->diagramPlane->setParentLayout( gridPlaneLayout ); } col += planeColOffset + 2 + ( 1 ); } } int axisOffset = 2;//curRowComponent->topAxesLayout ? 1 : 0; //axisOffset += curRowComponent->bottomAxesLayout ? 1 : 0; const int rowOffset = axisOffset + 2; row += rowOffset; } // if ( planesLayout->direction() == QBoxLayout::TopToBottom ) // ++row; // else // ++col; } qDeleteAll( vals ); // re-add our grid(s) to the chart's layout if ( dataAndLegendLayout ) { dataAndLegendLayout->addLayout( planesLayout, 1, 1 ); dataAndLegendLayout->setRowStretch( 1, 1000 ); dataAndLegendLayout->setColumnStretch( 1, 1000 ); } slotResizePlanes(); #ifdef NEW_LAYOUT_DEBUG for ( int i = 0; i < gridPlaneLayout->rowCount(); ++i ) { for ( int j = 0; j < gridPlaneLayout->columnCount(); ++j ) { if ( gridPlaneLayout->itemAtPosition( i, j ) ) qDebug() << Q_FUNC_INFO << "item at" << i << j << gridPlaneLayout->itemAtPosition( i, j )->geometry(); else qDebug() << Q_FUNC_INFO << "item at" << i << j << "no item present"; } } //qDebug() << Q_FUNC_INFO << "Relayout ended"; #endif } else { if ( hadPlanesLayout ) { planesLayout->setContentsMargins( left, top, right, bottom ); } planesLayout->setMargin( 0 ); planesLayout->setSpacing( 0 ); planesLayout->setObjectName( QString::fromLatin1( "planesLayout" ) ); /* First go through all planes and all axes and figure out whether the planes * need to coordinate. If they do, they share a grid layout, if not, each * gets their own. See buildPlaneLayoutInfos() for more details. */ QHash planeInfos = buildPlaneLayoutInfos(); QHash axisInfos; Q_FOREACH( AbstractCoordinatePlane* plane, coordinatePlanes ) { Q_ASSERT( planeInfos.contains(plane) ); PlaneInfo& pi = planeInfos[ plane ]; const int column = pi.horizontalOffset; const int row = pi.verticalOffset; //qDebug() << "processing plane at column" << column << "and row" << row; QGridLayout *planeLayout = pi.gridLayout; if ( !planeLayout ) { PlaneInfo& refPi = pi; // if this plane is sharing an axis with another one, recursively check for the original plane and use // the grid of that as planeLayout. while ( !planeLayout && refPi.referencePlane ) { refPi = planeInfos[refPi.referencePlane]; planeLayout = refPi.gridLayout; } Q_ASSERT_X( planeLayout, "Chart::Private::slotLayoutPlanes()", "Invalid reference plane. Please check that the reference plane has been added to the Chart." ); } else { planesLayout->addLayout( planeLayout ); } /* Put the plane in the center of the layout. If this is our own, that's * the middle of the layout, if we are sharing, it's a cell in the center * column of the shared grid. */ planeLayoutItems << plane; plane->setParentLayout( planeLayout ); planeLayout->addItem( plane, row, column, 1, 1 ); //qDebug() << "Chart slotLayoutPlanes() calls planeLayout->addItem("<< row << column << ")"; planeLayout->setRowStretch( row, 2 ); planeLayout->setColumnStretch( column, 2 ); Q_FOREACH( AbstractDiagram* abstractDiagram, plane->diagrams() ) { AbstractCartesianDiagram* diagram = qobject_cast< AbstractCartesianDiagram* >( abstractDiagram ); if ( !diagram ) { continue; // FIXME what about polar ? } - if ( pi.referencePlane != 0 ) + if ( pi.referencePlane != nullptr ) { pi.topAxesLayout = planeInfos[ pi.referencePlane ].topAxesLayout; pi.bottomAxesLayout = planeInfos[ pi.referencePlane ].bottomAxesLayout; pi.leftAxesLayout = planeInfos[ pi.referencePlane ].leftAxesLayout; pi.rightAxesLayout = planeInfos[ pi.referencePlane ].rightAxesLayout; } // collect all axes of a kind into sublayouts - if ( pi.topAxesLayout == 0 ) + if ( pi.topAxesLayout == nullptr ) { pi.topAxesLayout = new QVBoxLayout; pi.topAxesLayout->setMargin( 0 ); pi.topAxesLayout->setObjectName( QString::fromLatin1( "topAxesLayout" ) ); } - if ( pi.bottomAxesLayout == 0 ) + if ( pi.bottomAxesLayout == nullptr ) { pi.bottomAxesLayout = new QVBoxLayout; pi.bottomAxesLayout->setMargin( 0 ); pi.bottomAxesLayout->setObjectName( QString::fromLatin1( "bottomAxesLayout" ) ); } - if ( pi.leftAxesLayout == 0 ) + if ( pi.leftAxesLayout == nullptr ) { pi.leftAxesLayout = new QHBoxLayout; pi.leftAxesLayout->setMargin( 0 ); pi.leftAxesLayout->setObjectName( QString::fromLatin1( "leftAxesLayout" ) ); } - if ( pi.rightAxesLayout == 0 ) + if ( pi.rightAxesLayout == nullptr ) { pi.rightAxesLayout = new QHBoxLayout; pi.rightAxesLayout->setMargin( 0 ); pi.rightAxesLayout->setObjectName( QString::fromLatin1( "rightAxesLayout" ) ); } - if ( pi.referencePlane != 0 ) + if ( pi.referencePlane != nullptr ) { planeInfos[ pi.referencePlane ].topAxesLayout = pi.topAxesLayout; planeInfos[ pi.referencePlane ].bottomAxesLayout = pi.bottomAxesLayout; planeInfos[ pi.referencePlane ].leftAxesLayout = pi.leftAxesLayout; planeInfos[ pi.referencePlane ].rightAxesLayout = pi.rightAxesLayout; } //pi.leftAxesLayout->setSizeConstraint( QLayout::SetFixedSize ); Q_FOREACH( CartesianAxis* axis, diagram->axes() ) { if ( axisInfos.contains( axis ) ) { continue; // already laid out this one } Q_ASSERT ( axis ); axis->setCachedSizeDirty(); //qDebug() << "--------------- axis added to planeLayoutItems -----------------"; planeLayoutItems << axis; switch ( axis->position() ) { case CartesianAxis::Top: axis->setParentLayout( pi.topAxesLayout ); pi.topAxesLayout->addItem( axis ); break; case CartesianAxis::Bottom: axis->setParentLayout( pi.bottomAxesLayout ); pi.bottomAxesLayout->addItem( axis ); break; case CartesianAxis::Left: axis->setParentLayout( pi.leftAxesLayout ); pi.leftAxesLayout->addItem( axis ); break; case CartesianAxis::Right: axis->setParentLayout( pi.rightAxesLayout ); pi.rightAxesLayout->addItem( axis ); break; default: Q_ASSERT_X( false, "Chart::paintEvent", "unknown axis position" ); break; }; axisInfos.insert( axis, AxisInfo() ); } /* Put each stack of axes-layouts in the cells surrounding the * associated plane. We are laying out in the oder the planes * were added, and the first one gets to lay out shared axes. * Private axes go here as well, of course. */ if ( !pi.topAxesLayout->parent() ) { planeLayout->addLayout( pi.topAxesLayout, row - 1, column ); } if ( !pi.bottomAxesLayout->parent() ) { planeLayout->addLayout( pi.bottomAxesLayout, row + 1, column ); } if ( !pi.leftAxesLayout->parent() ) { planeLayout->addLayout( pi.leftAxesLayout, row, column - 1 ); } if ( !pi.rightAxesLayout->parent() ) { planeLayout->addLayout( pi.rightAxesLayout,row, column + 1 ); } } // use up to four auto-spacer items in the corners around the diagrams: #define ADD_AUTO_SPACER_IF_NEEDED( \ spacerRow, spacerColumn, hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ) \ { \ if ( hLayout || vLayout ) { \ AutoSpacerLayoutItem * spacer \ = new AutoSpacerLayoutItem( hLayoutIsAtTop, hLayout, vLayoutIsAtLeft, vLayout ); \ planeLayout->addItem( spacer, spacerRow, spacerColumn, 1, 1 ); \ spacer->setParentLayout( planeLayout ); \ planeLayoutItems << spacer; \ } \ } if ( plane->isCornerSpacersEnabled() ) { ADD_AUTO_SPACER_IF_NEEDED( row - 1, column - 1, false, pi.leftAxesLayout, false, pi.topAxesLayout ) ADD_AUTO_SPACER_IF_NEEDED( row + 1, column - 1, true, pi.leftAxesLayout, false, pi.bottomAxesLayout ) ADD_AUTO_SPACER_IF_NEEDED( row - 1, column + 1, false, pi.rightAxesLayout, true, pi.topAxesLayout ) ADD_AUTO_SPACER_IF_NEEDED( row + 1, column + 1, true, pi.rightAxesLayout, true, pi.bottomAxesLayout ) } } // re-add our grid(s) to the chart's layout if ( dataAndLegendLayout ) { dataAndLegendLayout->addLayout( planesLayout, 1, 1 ); dataAndLegendLayout->setRowStretch( 1, 1000 ); dataAndLegendLayout->setColumnStretch( 1, 1000 ); } slotResizePlanes(); } } void Chart::Private::createLayouts() { // The toplevel layout provides the left and right global margins layout = new QHBoxLayout( chart ); layout->setMargin( 0 ); layout->setObjectName( QString::fromLatin1( "Chart::Private::layout" ) ); layout->addSpacing( globalLeadingLeft ); leftOuterSpacer = layout->itemAt( layout->count() - 1 )->spacerItem(); // The vLayout provides top and bottom global margins and lays // out headers, footers and the diagram area. vLayout = new QVBoxLayout(); vLayout->setMargin( 0 ); vLayout->setObjectName( QString::fromLatin1( "vLayout" ) ); layout->addLayout( vLayout, 1000 ); layout->addSpacing( globalLeadingRight ); rightOuterSpacer = layout->itemAt( layout->count() - 1 )->spacerItem(); // 1. the spacing above the header area vLayout->addSpacing( globalLeadingTop ); topOuterSpacer = vLayout->itemAt( vLayout->count() - 1 )->spacerItem(); // 2. the header area headerLayout = new QGridLayout(); headerLayout->setMargin( 0 ); vLayout->addLayout( headerLayout ); // 3. the area containing coordinate planes, axes, and legends dataAndLegendLayout = new QGridLayout(); dataAndLegendLayout->setMargin( 0 ); dataAndLegendLayout->setObjectName( QString::fromLatin1( "dataAndLegendLayout" ) ); vLayout->addLayout( dataAndLegendLayout, 1000 ); // 4. the footer area footerLayout = new QGridLayout(); footerLayout->setMargin( 0 ); footerLayout->setObjectName( QString::fromLatin1( "footerLayout" ) ); vLayout->addLayout( footerLayout ); // 5. Prepare the header / footer layout cells: // Each of the 9 header cells (the 9 footer cells) // contain their own QVBoxLayout // since there can be more than one header (footer) per cell. for ( int row = 0; row < 3; ++row ) { for ( int column = 0; column < 3; ++ column ) { const Qt::Alignment align = s_gridAlignments[ row ][ column ]; for ( int headOrFoot = 0; headOrFoot < 2; headOrFoot++ ) { QVBoxLayout* innerLayout = new QVBoxLayout(); innerLayout->setMargin( 0 ); innerLayout->setAlignment( align ); innerHdFtLayouts[ headOrFoot ][ row ][ column ] = innerLayout; QGridLayout* outerLayout = headOrFoot == 0 ? headerLayout : footerLayout; outerLayout->addLayout( innerLayout, row, column, align ); } } } // 6. the spacing below the footer area vLayout->addSpacing( globalLeadingBottom ); bottomOuterSpacer = vLayout->itemAt( vLayout->count() - 1 )->spacerItem(); // the data+axes area dataAndLegendLayout->addLayout( planesLayout, 1, 1 ); dataAndLegendLayout->setRowStretch( 1, 1 ); dataAndLegendLayout->setColumnStretch( 1, 1 ); } void Chart::Private::slotResizePlanes() { if ( !dataAndLegendLayout ) { return; } if ( !overrideSize.isValid() ) { // activate() takes the size from the layout's parent QWidget, which is not updated when overrideSize // is set. So don't let the layout grab the wrong size in that case. // When overrideSize *is* set, we call layout->setGeometry() in paint( QPainter*, const QRect& ), // which also "activates" the layout in the sense that it distributes space internally. layout->activate(); } // Adapt diagram drawing to the new size Q_FOREACH (AbstractCoordinatePlane* plane, coordinatePlanes ) { plane->layoutDiagrams(); } } void Chart::Private::updateDirtyLayouts() { if ( isPlanesLayoutDirty ) { Q_FOREACH ( AbstractCoordinatePlane* p, coordinatePlanes ) { p->setGridNeedsRecalculate(); p->layoutPlanes(); p->layoutDiagrams(); } } if ( isPlanesLayoutDirty || isFloatingLegendsLayoutDirty ) { chart->reLayoutFloatingLegends(); } isPlanesLayoutDirty = false; isFloatingLegendsLayoutDirty = false; } void Chart::Private::reapplyInternalLayouts() { QRect geo = layout->geometry(); invalidateLayoutTree( layout ); layout->setGeometry( geo ); slotResizePlanes(); } void Chart::Private::paintAll( QPainter* painter ) { updateDirtyLayouts(); QRect rect( QPoint( 0, 0 ), overrideSize.isValid() ? overrideSize : chart->size() ); //qDebug() << this<<"::paintAll() uses layout size" << currentLayoutSize; // Paint the background (if any) AbstractAreaBase::paintBackgroundAttributes( *painter, rect, backgroundAttributes ); // Paint the frame (if any) AbstractAreaBase::paintFrameAttributes( *painter, rect, frameAttributes ); chart->reLayoutFloatingLegends(); Q_FOREACH( AbstractLayoutItem* planeLayoutItem, planeLayoutItems ) { planeLayoutItem->paintAll( *painter ); } Q_FOREACH( TextArea* textLayoutItem, textLayoutItems ) { textLayoutItem->paintAll( *painter ); } Q_FOREACH( Legend *legend, legends ) { const bool hidden = legend->isHidden() && legend->testAttribute( Qt::WA_WState_ExplicitShowHide ); if ( !hidden ) { //qDebug() << "painting legend at " << legend->geometry(); legend->paintIntoRect( *painter, legend->geometry() ); } } } // ******** Chart interface implementation *********** #define d d_func() Chart::Chart ( QWidget* parent ) : QWidget ( parent ) , _d( new Private( this ) ) { #if defined KDAB_EVAL EvalDialog::checkEvalLicense( "KD Chart" ); #endif FrameAttributes frameAttrs; // no frame per default... // frameAttrs.setVisible( true ); frameAttrs.setPen( QPen( Qt::black ) ); frameAttrs.setPadding( 1 ); setFrameAttributes( frameAttrs ); addCoordinatePlane( new CartesianCoordinatePlane ( this ) ); d->createLayouts(); } Chart::~Chart() { delete d; } void Chart::setFrameAttributes( const FrameAttributes &a ) { d->frameAttributes = a; } FrameAttributes Chart::frameAttributes() const { return d->frameAttributes; } void Chart::setBackgroundAttributes( const BackgroundAttributes &a ) { d->backgroundAttributes = a; } BackgroundAttributes Chart::backgroundAttributes() const { return d->backgroundAttributes; } //TODO KChart 3.0; change QLayout into QBoxLayout::Direction void Chart::setCoordinatePlaneLayout( QLayout * layout ) { if (layout == d->planesLayout) return; if (d->planesLayout) { // detach all QLayoutItem's the previous planesLayout has cause // otherwise deleting the planesLayout would delete them too. for(int i = d->planesLayout->count() - 1; i >= 0; --i) { d->planesLayout->takeAt(i); } delete d->planesLayout; } d->planesLayout = qobject_cast( layout ); d->slotLayoutPlanes(); } QLayout* Chart::coordinatePlaneLayout() { return d->planesLayout; } AbstractCoordinatePlane* Chart::coordinatePlane() { if ( d->coordinatePlanes.isEmpty() ) { qWarning() << "Chart::coordinatePlane: warning: no coordinate plane defined."; - return 0; + return nullptr; } else { return d->coordinatePlanes.first(); } } CoordinatePlaneList Chart::coordinatePlanes() { return d->coordinatePlanes; } void Chart::addCoordinatePlane( AbstractCoordinatePlane* plane ) { // Append insertCoordinatePlane( d->coordinatePlanes.count(), plane ); } void Chart::insertCoordinatePlane( int index, AbstractCoordinatePlane* plane ) { if ( index < 0 || index > d->coordinatePlanes.count() ) { return; } connect( plane, SIGNAL(destroyedCoordinatePlane(AbstractCoordinatePlane*)), d, SLOT(slotUnregisterDestroyedPlane(AbstractCoordinatePlane*)) ); connect( plane, SIGNAL(needUpdate()), this, SLOT(update()) ); connect( plane, SIGNAL(needRelayout()), d, SLOT(slotResizePlanes()) ) ; connect( plane, SIGNAL(needLayoutPlanes()), d, SLOT(slotLayoutPlanes()) ) ; connect( plane, SIGNAL(propertiesChanged()),this, SIGNAL(propertiesChanged()) ); d->coordinatePlanes.insert( index, plane ); plane->setParent( this ); d->slotLayoutPlanes(); } void Chart::replaceCoordinatePlane( AbstractCoordinatePlane* plane, AbstractCoordinatePlane* oldPlane_ ) { if ( plane && oldPlane_ != plane ) { AbstractCoordinatePlane* oldPlane = oldPlane_; if ( d->coordinatePlanes.count() ) { if ( ! oldPlane ) { oldPlane = d->coordinatePlanes.first(); if ( oldPlane == plane ) return; } takeCoordinatePlane( oldPlane ); } delete oldPlane; addCoordinatePlane( plane ); } } void Chart::takeCoordinatePlane( AbstractCoordinatePlane* plane ) { const int idx = d->coordinatePlanes.indexOf( plane ); if ( idx != -1 ) { d->coordinatePlanes.takeAt( idx ); - disconnect( plane, 0, d, 0 ); - disconnect( plane, 0, this, 0 ); + disconnect( plane, nullptr, d, nullptr ); + disconnect( plane, nullptr, this, nullptr ); plane->removeFromParentLayout(); - plane->setParent( 0 ); + plane->setParent( nullptr ); d->mouseClickedPlanes.removeAll(plane); } d->slotLayoutPlanes(); // Need to emit the signal: In case somebody has connected the signal // to her own slot for e.g. calling update() on a widget containing the chart. emit propertiesChanged(); } void Chart::setGlobalLeading( int left, int top, int right, int bottom ) { setGlobalLeadingLeft( left ); setGlobalLeadingTop( top ); setGlobalLeadingRight( right ); setGlobalLeadingBottom( bottom ); } void Chart::setGlobalLeadingLeft( int leading ) { d->globalLeadingLeft = leading; d->leftOuterSpacer->changeSize( leading, 0, QSizePolicy::Fixed, QSizePolicy::Minimum ); d->reapplyInternalLayouts(); } int Chart::globalLeadingLeft() const { return d->globalLeadingLeft; } void Chart::setGlobalLeadingTop( int leading ) { d->globalLeadingTop = leading; d->topOuterSpacer->changeSize( 0, leading, QSizePolicy::Minimum, QSizePolicy::Fixed ); d->reapplyInternalLayouts(); } int Chart::globalLeadingTop() const { return d->globalLeadingTop; } void Chart::setGlobalLeadingRight( int leading ) { d->globalLeadingRight = leading; d->rightOuterSpacer->changeSize( leading, 0, QSizePolicy::Fixed, QSizePolicy::Minimum ); d->reapplyInternalLayouts(); } int Chart::globalLeadingRight() const { return d->globalLeadingRight; } void Chart::setGlobalLeadingBottom( int leading ) { d->globalLeadingBottom = leading; d->bottomOuterSpacer->changeSize( 0, leading, QSizePolicy::Minimum, QSizePolicy::Fixed ); d->reapplyInternalLayouts(); } int Chart::globalLeadingBottom() const { return d->globalLeadingBottom; } void Chart::paint( QPainter* painter, const QRect& target ) { if ( target.isEmpty() || !painter ) { return; } QPaintDevice* prevDevice = GlobalMeasureScaling::paintDevice(); GlobalMeasureScaling::setPaintDevice( painter->device() ); // Output on a widget - if ( dynamic_cast< QWidget* >( painter->device() ) != 0 ) { + if ( dynamic_cast< QWidget* >( painter->device() ) != nullptr ) { GlobalMeasureScaling::setFactors( qreal( target.width() ) / qreal( geometry().size().width() ), qreal( target.height() ) / qreal( geometry().size().height() ) ); } else { // Output onto a QPixmap PrintingParameters::setScaleFactor( qreal( painter->device()->logicalDpiX() ) / qreal( logicalDpiX() ) ); const qreal resX = qreal( logicalDpiX() ) / qreal( painter->device()->logicalDpiX() ); const qreal resY = qreal( logicalDpiY() ) / qreal( painter->device()->logicalDpiY() ); GlobalMeasureScaling::setFactors( qreal( target.width() ) / qreal( geometry().size().width() ) * resX, qreal( target.height() ) / qreal( geometry().size().height() ) * resY ); } const QPoint translation = target.topLeft(); painter->translate( translation ); // the following layout logic has the disadvantage that repeatedly calling this method can // cause a relayout every time, but since this method's main use seems to be printing, the // gratuitous relayouts shouldn't be much of a performance problem. const bool differentSize = target.size() != size(); QRect oldGeometry; if ( differentSize ) { oldGeometry = geometry(); d->isPlanesLayoutDirty = true; d->isFloatingLegendsLayoutDirty = true; invalidateLayoutTree( d->dataAndLegendLayout ); d->dataAndLegendLayout->setGeometry( QRect( QPoint(), target.size() ) ); } d->overrideSize = target.size(); d->paintAll( painter ); d->overrideSize = QSize(); if ( differentSize ) { invalidateLayoutTree( d->dataAndLegendLayout ); d->dataAndLegendLayout->setGeometry( oldGeometry ); d->isPlanesLayoutDirty = true; d->isFloatingLegendsLayoutDirty = true; } // for debugging // painter->setPen( QPen( Qt::blue, 8 ) ); // painter->drawRect( target ); painter->translate( -translation.x(), -translation.y() ); GlobalMeasureScaling::instance()->resetFactors(); PrintingParameters::resetScaleFactor(); GlobalMeasureScaling::setPaintDevice( prevDevice ); } void Chart::resizeEvent ( QResizeEvent* event ) { d->isPlanesLayoutDirty = true; d->isFloatingLegendsLayoutDirty = true; QWidget::resizeEvent( event ); } void Chart::reLayoutFloatingLegends() { Q_FOREACH( Legend *legend, d->legends ) { const bool hidden = legend->isHidden() && legend->testAttribute( Qt::WA_WState_ExplicitShowHide ); if ( legend->position().isFloating() && !hidden ) { // resize the legend const QSize legendSize( legend->sizeHint() ); legend->setGeometry( QRect( legend->geometry().topLeft(), legendSize ) ); // find the legends corner point (reference point plus any paddings) const RelativePosition relPos( legend->floatingPosition() ); QPointF pt( relPos.calculatedPoint( size() ) ); //qDebug() << pt; // calculate the legend's top left point const Qt::Alignment alignTopLeft = Qt::AlignBottom | Qt::AlignLeft; if ( (relPos.alignment() & alignTopLeft) != alignTopLeft ) { if ( relPos.alignment() & Qt::AlignRight ) pt.rx() -= legendSize.width(); else if ( relPos.alignment() & Qt::AlignHCenter ) pt.rx() -= 0.5 * legendSize.width(); if ( relPos.alignment() & Qt::AlignBottom ) pt.ry() -= legendSize.height(); else if ( relPos.alignment() & Qt::AlignVCenter ) pt.ry() -= 0.5 * legendSize.height(); } //qDebug() << pt << endl; legend->move( static_cast(pt.x()), static_cast(pt.y()) ); } } } void Chart::paintEvent( QPaintEvent* ) { QPainter painter( this ); d->paintAll( &painter ); emit finishedDrawing(); } void Chart::addHeaderFooter( HeaderFooter* hf ) { Q_ASSERT( hf->type() == HeaderFooter::Header || hf->type() == HeaderFooter::Footer ); int row; int column; getRowAndColumnForPosition( hf->position().value(), &row, &column ); if ( row == -1 ) { qWarning( "Unknown header/footer position" ); return; } d->headerFooters.append( hf ); d->textLayoutItems.append( hf ); connect( hf, SIGNAL(destroyedHeaderFooter(HeaderFooter*)), d, SLOT(slotUnregisterDestroyedHeaderFooter(HeaderFooter*)) ); connect( hf, SIGNAL(positionChanged(HeaderFooter*)), d, SLOT(slotHeaderFooterPositionChanged(HeaderFooter*)) ); // set the text attributes (why?) TextAttributes textAttrs( hf->textAttributes() ); Measure measure( textAttrs.fontSize() ); measure.setRelativeMode( this, KChartEnums::MeasureOrientationMinimum ); measure.setValue( 20 ); textAttrs.setFontSize( measure ); hf->setTextAttributes( textAttrs ); // add it to the appropriate layout int innerLayoutIdx = hf->type() == HeaderFooter::Header ? 0 : 1; QVBoxLayout* headerFooterLayout = d->innerHdFtLayouts[ innerLayoutIdx ][ row ][ column ]; hf->setParentLayout( headerFooterLayout ); hf->setAlignment( s_gridAlignments[ row ][ column ] ); headerFooterLayout->addItem( hf ); d->slotResizePlanes(); } void Chart::replaceHeaderFooter( HeaderFooter* headerFooter, HeaderFooter* oldHeaderFooter_ ) { if ( headerFooter && oldHeaderFooter_ != headerFooter ) { HeaderFooter* oldHeaderFooter = oldHeaderFooter_; if ( d->headerFooters.count() ) { if ( ! oldHeaderFooter ) { oldHeaderFooter = d->headerFooters.first(); if ( oldHeaderFooter == headerFooter ) return; } takeHeaderFooter( oldHeaderFooter ); } delete oldHeaderFooter; addHeaderFooter( headerFooter ); } } void Chart::takeHeaderFooter( HeaderFooter* headerFooter ) { const int idx = d->headerFooters.indexOf( headerFooter ); if ( idx == -1 ) { return; } disconnect( headerFooter, SIGNAL(destroyedHeaderFooter(HeaderFooter*)), d, SLOT(slotUnregisterDestroyedHeaderFooter(HeaderFooter*)) ); d->headerFooters.takeAt( idx ); headerFooter->removeFromParentLayout(); - headerFooter->setParentLayout( 0 ); + headerFooter->setParentLayout( nullptr ); d->textLayoutItems.remove( d->textLayoutItems.indexOf( headerFooter ) ); d->slotResizePlanes(); } void Chart::Private::slotHeaderFooterPositionChanged( HeaderFooter* hf ) { chart->takeHeaderFooter( hf ); chart->addHeaderFooter( hf ); } HeaderFooter* Chart::headerFooter() { if ( d->headerFooters.isEmpty() ) { - return 0; + return nullptr; } else { return d->headerFooters.first(); } } HeaderFooterList Chart::headerFooters() { return d->headerFooters; } void Chart::Private::slotLegendPositionChanged( AbstractAreaWidget* aw ) { Legend* legend = qobject_cast< Legend* >( aw ); Q_ASSERT( legend ); chart->takeLegend( legend ); chart->addLegendInternal( legend, false ); } void Chart::addLegend( Legend* legend ) { legend->show(); addLegendInternal( legend, true ); emit propertiesChanged(); } void Chart::addLegendInternal( Legend* legend, bool setMeasures ) { if ( !legend ) { return; } KChartEnums::PositionValue pos = legend->position().value(); if ( pos == KChartEnums::PositionCenter ) { qWarning( "Not showing legend because PositionCenter is not supported for legends." ); } int row; int column; getRowAndColumnForPosition( pos, &row, &column ); if ( row < 0 && pos != KChartEnums::PositionFloating ) { qWarning( "Not showing legend because of unknown legend position." ); return; } d->legends.append( legend ); legend->setParent( this ); // set text attributes (why?) if ( setMeasures ) { TextAttributes textAttrs( legend->textAttributes() ); Measure measure( textAttrs.fontSize() ); measure.setRelativeMode( this, KChartEnums::MeasureOrientationMinimum ); measure.setValue( 20 ); textAttrs.setFontSize( measure ); legend->setTextAttributes( textAttrs ); textAttrs = legend->titleTextAttributes(); measure.setRelativeMode( this, KChartEnums::MeasureOrientationMinimum ); measure.setValue( 24 ); textAttrs.setFontSize( measure ); legend->setTitleTextAttributes( textAttrs ); legend->setReferenceArea( this ); } // add it to the appropriate layout if ( pos != KChartEnums::PositionFloating ) { legend->needSizeHint(); // in each edge and corner of the outer layout, there's a grid for the different alignments that we create // on demand. we don't remove it when empty. QLayoutItem* edgeItem = d->dataAndLegendLayout->itemAtPosition( row, column ); QGridLayout* alignmentsLayout = dynamic_cast< QGridLayout* >( edgeItem ); Q_ASSERT( !edgeItem || alignmentsLayout ); // if it exists, it must be a QGridLayout if ( !alignmentsLayout ) { alignmentsLayout = new QGridLayout; d->dataAndLegendLayout->addLayout( alignmentsLayout, row, column ); alignmentsLayout->setMargin( 0 ); } // in case there are several legends in the same edge or corner with the same alignment, they are stacked // vertically using a QVBoxLayout. it is created on demand as above. row = 1; column = 1; for ( int i = 0; i < 3; i++ ) { for ( int j = 0; j < 3; j++ ) { Qt::Alignment align = s_gridAlignments[ i ][ j ]; if ( align == legend->alignment() ) { row = i; column = j; break; } } } QLayoutItem* alignmentItem = alignmentsLayout->itemAtPosition( row, column ); QVBoxLayout* sameAlignmentLayout = dynamic_cast< QVBoxLayout* >( alignmentItem ); Q_ASSERT( !alignmentItem || sameAlignmentLayout ); // if it exists, it must be a QVBoxLayout if ( !sameAlignmentLayout ) { sameAlignmentLayout = new QVBoxLayout; alignmentsLayout->addLayout( sameAlignmentLayout, row, column ); sameAlignmentLayout->setMargin( 0 ); } sameAlignmentLayout->addItem( new MyWidgetItem( legend, legend->alignment() ) ); } connect( legend, SIGNAL(destroyedLegend(Legend*)), d, SLOT(slotUnregisterDestroyedLegend(Legend*)) ); connect( legend, SIGNAL(positionChanged(AbstractAreaWidget*)), d, SLOT(slotLegendPositionChanged(AbstractAreaWidget*)) ); connect( legend, SIGNAL(propertiesChanged()), this, SIGNAL(propertiesChanged()) ); d->slotResizePlanes(); } void Chart::replaceLegend( Legend* legend, Legend* oldLegend_ ) { if ( legend && oldLegend_ != legend ) { Legend* oldLegend = oldLegend_; if ( d->legends.count() ) { if ( ! oldLegend ) { oldLegend = d->legends.first(); if ( oldLegend == legend ) return; } takeLegend( oldLegend ); } delete oldLegend; addLegend( legend ); } } void Chart::takeLegend( Legend* legend ) { const int idx = d->legends.indexOf( legend ); if ( idx == -1 ) { return; } d->legends.takeAt( idx ); - disconnect( legend, 0, d, 0 ); - disconnect( legend, 0, this, 0 ); + disconnect( legend, nullptr, d, nullptr ); + disconnect( legend, nullptr, this, nullptr ); // the following removes the legend from its layout and destroys its MyWidgetItem (the link to the layout) - legend->setParent( 0 ); + legend->setParent( nullptr ); d->slotResizePlanes(); emit propertiesChanged(); } Legend* Chart::legend() { - return d->legends.isEmpty() ? 0 : d->legends.first(); + return d->legends.isEmpty() ? nullptr : d->legends.first(); } LegendList Chart::legends() { return d->legends; } void Chart::mousePressEvent( QMouseEvent* event ) { const QPoint pos = mapFromGlobal( event->globalPos() ); Q_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) { if ( plane->geometry().contains( event->pos() ) && plane->diagrams().size() > 0 ) { QMouseEvent ev( QEvent::MouseButtonPress, pos, event->globalPos(), event->button(), event->buttons(), event->modifiers() ); plane->mousePressEvent( &ev ); d->mouseClickedPlanes.append( plane ); } } } void Chart::mouseDoubleClickEvent( QMouseEvent* event ) { const QPoint pos = mapFromGlobal( event->globalPos() ); Q_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) { if ( plane->geometry().contains( event->pos() ) && plane->diagrams().size() > 0 ) { QMouseEvent ev( QEvent::MouseButtonPress, pos, event->globalPos(), event->button(), event->buttons(), event->modifiers() ); plane->mouseDoubleClickEvent( &ev ); } } } void Chart::mouseMoveEvent( QMouseEvent* event ) { QSet< AbstractCoordinatePlane* > eventReceivers = QSet< AbstractCoordinatePlane* >::fromList( d->mouseClickedPlanes ); Q_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) { if ( plane->geometry().contains( event->pos() ) && plane->diagrams().size() > 0 ) { eventReceivers.insert( plane ); } } const QPoint pos = mapFromGlobal( event->globalPos() ); Q_FOREACH( AbstractCoordinatePlane* plane, eventReceivers ) { QMouseEvent ev( QEvent::MouseMove, pos, event->globalPos(), event->button(), event->buttons(), event->modifiers() ); plane->mouseMoveEvent( &ev ); } } void Chart::mouseReleaseEvent( QMouseEvent* event ) { QSet< AbstractCoordinatePlane* > eventReceivers = QSet< AbstractCoordinatePlane* >::fromList( d->mouseClickedPlanes ); Q_FOREACH( AbstractCoordinatePlane* plane, d->coordinatePlanes ) { if ( plane->geometry().contains( event->pos() ) && plane->diagrams().size() > 0 ) { eventReceivers.insert( plane ); } } const QPoint pos = mapFromGlobal( event->globalPos() ); Q_FOREACH( AbstractCoordinatePlane* plane, eventReceivers ) { QMouseEvent ev( QEvent::MouseButtonRelease, pos, event->globalPos(), event->button(), event->buttons(), event->modifiers() ); plane->mouseReleaseEvent( &ev ); } d->mouseClickedPlanes.clear(); } bool Chart::event( QEvent* event ) { if ( event->type() == QEvent::ToolTip ) { const QHelpEvent* const helpEvent = static_cast< QHelpEvent* >( event ); Q_FOREACH( const AbstractCoordinatePlane* const plane, d->coordinatePlanes ) { // iterate diagrams in reverse, so that the top-most painted diagram is // queried first for a tooltip before the diagrams behind it const ConstAbstractDiagramList& diagrams = plane->diagrams(); for (int i = diagrams.size() - 1; i >= 0; --i) { const AbstractDiagram* diagram = diagrams[i]; if (diagram->isHidden()) { continue; } const QModelIndex index = diagram->indexAt( helpEvent->pos() ); const QVariant toolTip = index.data( Qt::ToolTipRole ); if ( toolTip.isValid() ) { QPoint pos = mapFromGlobal( helpEvent->pos() ); QRect rect( pos - QPoint( 1, 1 ), QSize( 3, 3 ) ); QToolTip::showText( QCursor::pos(), toolTip.toString(), this, rect ); return true; } } } } return QWidget::event( event ); } bool Chart::useNewLayoutSystem() const { return d_func()->useNewLayoutSystem; } void Chart::setUseNewLayoutSystem( bool value ) { if ( d_func()->useNewLayoutSystem != value ) d_func()->useNewLayoutSystem = value; } diff --git a/src/KChart/KChartChart.h b/src/KChart/KChartChart.h index 67ae7a0..352fec1 100644 --- a/src/KChart/KChartChart.h +++ b/src/KChart/KChartChart.h @@ -1,573 +1,573 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTCHART_H #define KCHARTCHART_H #include #include "kchart_export.h" #include "KChartGlobal.h" /* Simplified(*) overview of object ownership in a chart: Chart is-a QWidget | n CoordinatePlanes is-a AbstractArea is-a AbstractLayoutItem is-a QLayoutItem | n Diagrams is-a QAbstractItemView is-a QWidget / | \ AbstractGrid | Axes (can be shared between diagrams) is-a AbstractArea is-a... QLayoutItem (no base class) | Legends is-a AbstractAreaWidget is-a QWidget (*) less important classes, including base classes, removed. Layout rules: In principle, every size or existence change in one of the objects listed above must be propagated to all other objects. This could change their size. There are also settings changes that invalidate the size of other components, where the size changes are detected and propagated. Painting call tree (simplified): Chart::paint() (from users) / paintEvent() (from framework) ChartPrivate::paintAll()-----------------------------------------------\ CoordinatePlane::paintAll() (from AbstractArea)--------\ Axis::paintAll()-\ CoordinatePlane::paint() (from AbstractLayoutItem) Grid::drawGrid() Axis::paint() Diagram::paint( PaintContext* paintContext ) Note that grids are painted from the coordinate plane, not from the diagram as ownership would suggest. */ namespace KChart { class BackgroundAttributes; class FrameAttributes; class AbstractDiagram; class AbstractCoordinatePlane; class HeaderFooter; class Legend; typedef QList CoordinatePlaneList; typedef QList HeaderFooterList; typedef QList LegendList; /** * @class Chart KChartChart.h KChartChart * @brief A chart with one or more diagrams. * * The Chart class represents a drawing consisting of one or more diagrams * and various optional elements such as legends, axes, text boxes, headers * or footers. It takes ownership of all these elements when they are assigned * to it. Each diagram is associated with a coordinate plane, of which the chart * can have more than one. The coordinate planes (and thus the associated diagrams) * can be laid out in various ways. * * The Chart class makes heavy use of the Qt Interview framework for model/view * programming, and thus requires data to be presented to it in a QAbstractItemModel * compatible way. For many simple charts, especially if the visualized data is * static, KChart::Widget provides an abstracted interface, that hides the complexity * of Interview to a large extent. */ class KCHART_EXPORT Chart : public QWidget { Q_OBJECT // KD Chart 3.0: leading is inter-line distance of text. this here is MARGIN or SPACING. Q_PROPERTY( int globalLeadingTop READ globalLeadingTop WRITE setGlobalLeadingTop ) Q_PROPERTY( int globalLeadingBottom READ globalLeadingBottom WRITE setGlobalLeadingBottom ) Q_PROPERTY( int globalLeadingLeft READ globalLeadingLeft WRITE setGlobalLeadingLeft ) Q_PROPERTY( int globalLeadingRight READ globalLeadingRight WRITE setGlobalLeadingRight ) Q_PROPERTY( bool useNewLayoutSystem READ useNewLayoutSystem WRITE setUseNewLayoutSystem ) KCHART_DECLARE_PRIVATE_BASE_POLYMORPHIC_QWIDGET( Chart ) public: - explicit Chart ( QWidget* parent = 0 ); + explicit Chart ( QWidget* parent = nullptr ); ~Chart(); /** * @brief useNewLayoutSystem * Be very careful activating the new layout system, * its still experimental and works only if the user knows * what he is doing. The reason is that the system does not prevent * the user from creating sharing graphs that are not layoutable in a * plane and still needs assistance from the user. */ bool useNewLayoutSystem() const; void setUseNewLayoutSystem( bool value ); /** \brief Specify the frame attributes to be used, by default is it a thin black line. To hide the frame line, you could do something like this: \verbatim KChart::FrameAttributes frameAttrs( my_chart->frameAttributes() ); frameAttrs.setVisible( false ); my_chart->setFrameAttributes( frameAttrs ); \endverbatim \sa setBackgroundAttributes */ void setFrameAttributes( const FrameAttributes &a ); FrameAttributes frameAttributes() const; /** \brief Specify the background attributes to be used, by default there is no background. To set a light blue background, you could do something like this: \verbatim KChart::BackgroundAttributes backgroundAttrs( my_chart->backgroundAttributes() ); backgroundAttrs.setVisible( true ); backgroundAttrs.setBrush( QColor(0xd0,0xd0,0xff) ); my_chart->setBackgroundAttributes( backgroundAttrs ); \endverbatim \sa setFrameAttributes */ void setBackgroundAttributes( const BackgroundAttributes &a ); BackgroundAttributes backgroundAttributes() const; /** * Each chart must have at least one coordinate plane. * Initially a default CartesianCoordinatePlane is created. * Use replaceCoordinatePlane() to replace it with a different * one, such as a PolarCoordinatePlane. * @return The first coordinate plane of the chart. */ AbstractCoordinatePlane* coordinatePlane(); /** * The list of coordinate planes. * @return The list of coordinate planes. */ CoordinatePlaneList coordinatePlanes(); /** * Adds a coordinate plane to the chart. The chart takes ownership. * @param plane The coordinate plane to add. * * \sa replaceCoordinatePlane, takeCoordinatePlane */ void addCoordinatePlane( AbstractCoordinatePlane* plane ); /** * Inserts a coordinate plane to the chart at index @p index. * The chart takes ownership. * * @param index The index where to add the plane * @param plane The coordinate plane to add. * * \sa replaceCoordinatePlane, takeCoordinatePlane */ void insertCoordinatePlane( int index, AbstractCoordinatePlane* plane ); /** * Replaces the old coordinate plane, or appends the * plane, it there is none yet. * * @param plane The coordinate plane to be used instead of the old plane. * This parameter must not be zero, or the method will do nothing. * * @param oldPlane The coordinate plane to be removed by the new plane. This * plane will be deleted automatically. If the parameter is omitted, * the very first coordinate plane will be replaced. In case, there was no * plane yet, the new plane will just be added. * * \note If you want to re-use the old coordinate plane, call takeCoordinatePlane and * addCoordinatePlane, instead of using replaceCoordinatePlane. * * \sa addCoordinatePlane, takeCoordinatePlane */ void replaceCoordinatePlane( AbstractCoordinatePlane* plane, - AbstractCoordinatePlane* oldPlane = 0 ); + AbstractCoordinatePlane* oldPlane = nullptr ); /** * Removes the coordinate plane from the chart, without deleting it. * * The chart no longer owns the plane, so it is * the caller's responsibility to delete the plane. * * \sa addCoordinatePlane, takeCoordinatePlane */ void takeCoordinatePlane( AbstractCoordinatePlane* plane ); /** * Set the coordinate plane layout that should be used as model for * the internal used layout. The layout needs to be an instance of * QHBoxLayout or QVBoxLayout. */ void setCoordinatePlaneLayout( QLayout * layout ); QLayout* coordinatePlaneLayout(); /** * The first header or footer of the chart. By default there is none. * @return The first header or footer of the chart or 0 if there was none * added to the chart. */ HeaderFooter* headerFooter(); /** * The list of headers and footers associated with the chart. * @return The list of headers and footers associated with the chart. */ HeaderFooterList headerFooters(); /** * Adds a header or a footer to the chart. The chart takes ownership. * @param headerFooter The header (or footer, resp.) to add. * * \sa replaceHeaderFooter, takeHeaderFooter */ void addHeaderFooter( HeaderFooter* headerFooter ); /** * Replaces the old header (or footer, resp.), or appends the * new header or footer, it there is none yet. * * @param headerFooter The header or footer to be used instead of the old one. * This parameter must not be zero, or the method will do nothing. * * @param oldHeaderFooter The header or footer to be removed by the new one. This * header or footer will be deleted automatically. If the parameter is omitted, * the very first header or footer will be replaced. In case, there was no * header and no footer yet, the new header or footer will just be added. * * \note If you want to re-use the old header or footer, call takeHeaderFooter and * addHeaderFooter, instead of using replaceHeaderFooter. * * \sa addHeaderFooter, takeHeaderFooter */ void replaceHeaderFooter ( HeaderFooter* headerFooter, - HeaderFooter* oldHeaderFooter = 0 ); + HeaderFooter* oldHeaderFooter = nullptr ); /** * Removes the header (or footer, resp.) from the chart, without deleting it. * * The chart no longer owns the header or footer, so it is * the caller's responsibility to delete the header or footer. * * \sa addHeaderFooter, replaceHeaderFooter */ void takeHeaderFooter( HeaderFooter* headerFooter ); /** * The first legend of the chart or 0 if there was none added to the chart. * @return The first legend of the chart or 0 if none exists. */ Legend* legend(); /** * The list of all legends associated with the chart. * @return The list of all legends associated with the chart. */ LegendList legends(); /** * Add the given legend to the chart. The chart takes ownership. * @param legend The legend to add. * * \sa replaceLegend, takeLegend */ void addLegend( Legend* legend ); /** * Replaces the old legend, or appends the * new legend, it there is none yet. * * @param legend The legend to be used instead of the old one. * This parameter must not be zero, or the method will do nothing. * * @param oldLegend The legend to be removed by the new one. This * legend will be deleted automatically. If the parameter is omitted, * the very first legend will be replaced. In case, there was no * legend yet, the new legend will just be added. * * If you want to re-use the old legend, call takeLegend and * addLegend, instead of using replaceLegend. * * \note Whenever addLegend is called the font sizes used by the * Legend are set to relative and they get coupled to the Chart's size, * with their relative values being 20 for the item texts and 24 to the * title text. So if you want to use custom font sizes for the Legend * make sure to set them after calling addLegend. * * \sa addLegend, takeLegend */ - void replaceLegend ( Legend* legend, Legend* oldLegend = 0 ); + void replaceLegend ( Legend* legend, Legend* oldLegend = nullptr ); /** * Removes the legend from the chart, without deleting it. * * The chart no longer owns the legend, so it is * the caller's responsibility to delete the legend. * * \sa addLegend, takeLegend */ void takeLegend( Legend* legend ); /** * Set the padding between the margin of the widget and the area that * the contents are drawn into. * @param left The padding on the left side. * @param top The padding at the top. * @param right The padding on the left hand side. * @param bottom The padding on the bottom. * * \note Using previous versions of KD Chart you might have called * setGlobalLeading() to make room for long Abscissa labels (or for an * overlapping top label of an Ordinate axis, resp.) that would not fit * into the normal axis area. This is \em no \em longer \em needed * because KD Chart now is using hidden auto-spacer items reserving * as much free space as is needed for axes with overlaping content * at the respective sides. * * \sa setGlobalLeadingTop, setGlobalLeadingBottom, setGlobalLeadingLeft, setGlobalLeadingRight * \sa globalLeadingTop, globalLeadingBottom, globalLeadingLeft, globalLeadingRight */ void setGlobalLeading( int left, int top, int right, int bottom ); /** * Set the padding between the start of the widget and the start * of the area that is used for drawing on the left. * @param leading The padding value. * * \sa setGlobalLeading */ void setGlobalLeadingLeft( int leading ); /** * The padding between the start of the widget and the start * of the area that is used for drawing on the left. * @return The padding between the start of the widget and the start * of the area that is used for drawing on the left. * * \sa setGlobalLeading */ int globalLeadingLeft() const; /** * Set the padding between the start of the widget and the start * of the area that is used for drawing at the top. * @param leading The padding value. * * \sa setGlobalLeading */ void setGlobalLeadingTop( int leading ); /** * The padding between the start of the widget and the start * of the area that is used for drawing at the top. * @return The padding between the start of the widget and the start * of the area that is used for drawing at the top. * * \sa setGlobalLeading */ int globalLeadingTop() const; /** * Set the padding between the start of the widget and the start * of the area that is used for drawing on the right. * @param leading The padding value. * * \sa setGlobalLeading */ void setGlobalLeadingRight( int leading ); /** * The padding between the start of the widget and the start * of the area that is used for drawing on the right. * @return The padding between the start of the widget and the start * of the area that is used for drawing on the right. * * \sa setGlobalLeading */ int globalLeadingRight() const; /** * Set the padding between the start of the widget and the start * of the area that is used for drawing on the bottom. * @param leading The padding value. * * \sa setGlobalLeading */ void setGlobalLeadingBottom( int leading ); /** * The padding between the start of the widget and the start * of the area that is used for drawing at the bottom. * @return The padding between the start of the widget and the start * of the area that is used for drawing at the bottom. * * \sa setGlobalLeading */ int globalLeadingBottom() const; /** * Paints all the contents of the chart. Use this method to make KChart * draw into your QPainter. * * \note Any global leading settings will be used by the paint method too, * so make sure to set them to zero, if you want the drawing to have the exact * size of the target rectangle. * * \param painter The painter to be drawn into. * \param target The rectangle to be filled by the Chart's drawing. * * \sa setGlobalLeading */ void paint( QPainter* painter, const QRect& target ); void reLayoutFloatingLegends(); Q_SIGNALS: /** Emitted upon change of a property of the Chart or any of its components. */ void propertiesChanged(); void finishedDrawing(); protected: /** * Adjusts the internal layout when the chart is resized. */ /* reimp */ void resizeEvent ( QResizeEvent * event ) Q_DECL_OVERRIDE; /** * @brief Draws the background and frame, then calls paint(). * * In most cases there is no need to override this method in a derived * class, but if you do, do not forget to call paint(). * @sa paint */ /* reimp */ void paintEvent( QPaintEvent* event ) Q_DECL_OVERRIDE; /** reimp */ void mousePressEvent( QMouseEvent* event ) Q_DECL_OVERRIDE; /** reimp */ void mouseDoubleClickEvent( QMouseEvent* event ) Q_DECL_OVERRIDE; /** reimp */ void mouseMoveEvent( QMouseEvent* event ) Q_DECL_OVERRIDE; /** reimp */ void mouseReleaseEvent( QMouseEvent* event ) Q_DECL_OVERRIDE; /** reimp */ bool event( QEvent* event ) Q_DECL_OVERRIDE; private: // TODO move this to the private class void addLegendInternal( Legend *legend, bool setMeasures ); }; // Here we have a few docu block to be included into the API documentation: /** * \dir src * \brief Implementation directory of KChart. * * This directory contains the header files and the source files of both, * the private and the public classes. * * \note Only classes that have an include wrapper in the \c $KCHARTDIR/include * directory are part of the supported API. * All other classes are to be considered as implemntation details, they * could be changed in future versions of KChart without notice. * * In other words: No class that is not mentioned in the \c $KCHARTDIR/include * directory may be directly used by your application. * * The recommended way to include classes of the KChart API is including * them by class name, so instead of including KChartChart.h you would say: * \verbatim #include \endverbatim * * When following this there is no reason to include the \c $KCHARTDIR/src * directory, it is sufficient to include \c $KCHARTDIR/include */ } /** * @class QAbstractItemView "(do not include)" * @brief Class only listed here to document inheritance of some KChart classes. * * Please consult the respective Qt documentation for details: * http://doc.trolltech.com/ */ /** * @class QAbstractProxyModel "(do not include)" * @brief Class only listed here to document inheritance of some KChart classes. * * Please consult the respective Qt documentation for details: * http://doc.trolltech.com/ */ /** * @class QFrame "(do not include)" * @brief Class only listed here to document inheritance of some KChart classes. * * Please consult the respective Qt documentation for details: * http://doc.trolltech.com/ */ /** * @class QObject "(do not include)" * @brief Class only listed here to document inheritance of some KChart classes. * * Please consult the respective Qt documentation for details: * http://doc.trolltech.com/ */ /** * @class QSortFilterProxyModel "(do not include)" * @brief Class only listed here to document inheritance of some KChart classes. * * Please consult the respective Qt documentation for details: * http://doc.trolltech.com/ */ /** * @class QWidget "(do not include)" * @brief Class only listed here to document inheritance of some KChart classes. * * Please consult the respective Qt documentation for details: * http://doc.trolltech.com/ */ /** * @class QTextDocument "(do not include)" * @brief Class only listed here to document inheritance of some KChart classes. * * Please consult the respective Qt documentation for details: * http://doc.trolltech.com/ */ /** * @class QLayoutItem "(do not include)" * @brief Class only listed here to document inheritance of some KChart classes. * * Please consult the respective Qt documentation for details: * http://doc.trolltech.com/ */ /** * @class QGraphicsPolygonItem "(do not include)" * @brief Class only listed here to document inheritance of some KChart classes. * * Please consult the respective Qt documentation for details: * http://doc.trolltech.com/ */ #endif diff --git a/src/KChart/KChartChart_p.h b/src/KChart/KChartChart_p.h index 8a31685..7e2c966 100644 --- a/src/KChart/KChartChart_p.h +++ b/src/KChart/KChartChart_p.h @@ -1,195 +1,195 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTCHART_P_H #define KCHARTCHART_P_H // // W A R N I N G // ------------- // // This file is not part of the KD Chart API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include "KChartChart.h" #include "KChartAbstractArea.h" #include "KChartTextArea.h" #include "KChartFrameAttributes.h" #include "KChartBackgroundAttributes.h" #include "KChartLayoutItems.h" #include "KChartMath_p.h" namespace KChart { class AbstractAreaWidget; class CartesianAxis; /* struct PlaneInfo can't be declared inside Chart::Private, otherwise MSVC.net says: qhash.h(195) : error C2248: 'KChart::Chart::Private' : cannot access protected class declared in class 'KChart::Chart' KChartChart_p.h(58) : see declaration of 'KChart::Chart::Private' KChartChart.h(61) : see declaration of 'KChart::Chart' KChartChart.cpp(262) : see reference to class template instantiation 'QHash' being compiled, with Key=KChart::AbstractCoordinatePlane *, T=KChart::Chart::Private::PlaneInfo */ /** * \internal */ struct PlaneInfo { PlaneInfo() - : referencePlane( 0 ), + : referencePlane( nullptr ), horizontalOffset( 1 ), verticalOffset( 1 ), - gridLayout( 0 ), - topAxesLayout( 0 ), - bottomAxesLayout( 0 ), - leftAxesLayout( 0 ), - rightAxesLayout( 0 ) + gridLayout( nullptr ), + topAxesLayout( nullptr ), + bottomAxesLayout( nullptr ), + leftAxesLayout( nullptr ), + rightAxesLayout( nullptr ) {} AbstractCoordinatePlane *referencePlane; int horizontalOffset; int verticalOffset; QGridLayout* gridLayout; QVBoxLayout* topAxesLayout; QVBoxLayout* bottomAxesLayout; QHBoxLayout* leftAxesLayout; QHBoxLayout* rightAxesLayout; }; struct LayoutGraphNode { LayoutGraphNode() - : diagramPlane( 0 ) - , leftSuccesor( 0 ) - , bottomSuccesor( 0 ) - , sharedSuccesor( 0 ) - , gridLayout( 0 ) + : diagramPlane( nullptr ) + , leftSuccesor( nullptr ) + , bottomSuccesor( nullptr ) + , sharedSuccesor( nullptr ) + , gridLayout( nullptr ) , topAxesLayout( false ) , bottomAxesLayout( false ) , leftAxesLayout( false ) , rightAxesLayout( false ) , priority( -1 ) {} AbstractCoordinatePlane* diagramPlane; LayoutGraphNode* leftSuccesor; LayoutGraphNode* bottomSuccesor; LayoutGraphNode* sharedSuccesor; QGridLayout* gridLayout; bool topAxesLayout; bool bottomAxesLayout; bool leftAxesLayout; bool rightAxesLayout; int priority; bool operator<( const LayoutGraphNode &other ) const { return priority < other.priority; } }; /** * \internal */ class Q_DECL_HIDDEN Chart::Private : public QObject { Q_OBJECT public: Chart* chart; enum AxisType { Abscissa, Ordinate }; bool useNewLayoutSystem; CoordinatePlaneList coordinatePlanes; HeaderFooterList headerFooters; LegendList legends; QHBoxLayout* layout; QVBoxLayout* vLayout; QBoxLayout* planesLayout; QGridLayout* gridPlaneLayout; QGridLayout* headerLayout; QGridLayout* footerLayout; QGridLayout* dataAndLegendLayout; QSpacerItem* leftOuterSpacer; QSpacerItem* rightOuterSpacer; QSpacerItem* topOuterSpacer; QSpacerItem* bottomOuterSpacer; QVBoxLayout* innerHdFtLayouts[2][3][3]; QVector textLayoutItems; QVector planeLayoutItems; QVector legendLayoutItems; QSize overrideSize; bool isFloatingLegendsLayoutDirty; bool isPlanesLayoutDirty; // since we do not want to derive Chart from AbstractAreaBase, we store the attributes // here and call two static painting methods to draw the background and frame. KChart::FrameAttributes frameAttributes; KChart::BackgroundAttributes backgroundAttributes; // ### wrong names, "leading" means inter-line distance of text. spacing? margin? int globalLeadingLeft, globalLeadingRight, globalLeadingTop, globalLeadingBottom; QList< AbstractCoordinatePlane* > mouseClickedPlanes; Qt::LayoutDirection layoutDirection; Private( Chart* ); virtual ~Private(); void createLayouts(); void updateDirtyLayouts(); void reapplyInternalLayouts(); // TODO: see if this can be merged with updateDirtyLayouts() void paintAll( QPainter* painter ); struct AxisInfo { AxisInfo() - :plane(0) + :plane(nullptr) {} AbstractCoordinatePlane *plane; }; QHash buildPlaneLayoutInfos(); QVector< LayoutGraphNode* > buildPlaneLayoutGraph(); public Q_SLOTS: void slotLayoutPlanes(); void slotResizePlanes(); void slotLegendPositionChanged( AbstractAreaWidget* legend ); void slotHeaderFooterPositionChanged( HeaderFooter* hf ); void slotUnregisterDestroyedLegend( Legend * legend ); void slotUnregisterDestroyedHeaderFooter( HeaderFooter* headerFooter ); void slotUnregisterDestroyedPlane( AbstractCoordinatePlane* plane ); }; } #endif diff --git a/src/KChart/KChartDataValueAttributes.cpp b/src/KChart/KChartDataValueAttributes.cpp index ecc8f7d..6efc85b 100644 --- a/src/KChart/KChartDataValueAttributes.cpp +++ b/src/KChart/KChartDataValueAttributes.cpp @@ -1,343 +1,343 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartDataValueAttributes.h" #include #include #include "KChartRelativePosition.h" #include "KChartPosition.h" #include "KChartMath_p.h" #include #include #include #include // FIXME till #define KCHART_DATA_VALUE_AUTO_DIGITS 4 #define d d_func() using namespace KChart; class Q_DECL_HIDDEN DataValueAttributes::Private { friend class DataValueAttributes; public: Private(); private: TextAttributes textAttributes; FrameAttributes frameAttributes; BackgroundAttributes backgroundAttributes; MarkerAttributes markerAttributes; QString prefix; QString suffix; QString dataLabel; RelativePosition negativeRelPos; RelativePosition positiveRelPos; qint16 decimalDigits; qint16 powerOfTenDivisor; bool visible : 1; bool showInfinite : 1; bool showRepetitiveDataLabels : 1; bool showOverlappingDataLabels : 1; bool usePercentage : 1; bool mirrorNegativeValueTextRotation : 1; }; DataValueAttributes::Private::Private() : decimalDigits( KCHART_DATA_VALUE_AUTO_DIGITS ), powerOfTenDivisor( 0 ), visible( false ), showInfinite( true ) { Measure me( 20.0, KChartEnums::MeasureCalculationModeAuto, KChartEnums::MeasureOrientationAuto ); textAttributes.setFontSize( me ); me.setValue( 8.0 ); me.setCalculationMode( KChartEnums::MeasureCalculationModeAbsolute ); textAttributes.setMinimalFontSize( me ); textAttributes.setRotation( -45 ); // we set the Position to unknown: so the diagrams can take their own decisions positiveRelPos.setReferencePosition( Position::Unknown ); negativeRelPos.setReferencePosition( Position::Unknown ); positiveRelPos.setAlignment( Qt::AlignTop | Qt::AlignRight ); negativeRelPos.setAlignment( Qt::AlignBottom | Qt::AlignRight ); showRepetitiveDataLabels = false; showOverlappingDataLabels = false; usePercentage = false; mirrorNegativeValueTextRotation = false; } DataValueAttributes::DataValueAttributes() : _d( new Private() ) { } DataValueAttributes::DataValueAttributes( const DataValueAttributes& r ) : _d( new Private( *r.d ) ) { } DataValueAttributes & DataValueAttributes::operator=( const DataValueAttributes& r ) { if ( this == &r ) return *this; *d = *r.d; return *this; } DataValueAttributes::~DataValueAttributes() { - delete _d; _d = 0; + delete _d; _d = nullptr; } bool DataValueAttributes::operator==( const DataValueAttributes& r ) const { return isVisible() == r.isVisible() && textAttributes() == r.textAttributes() && frameAttributes() == r.frameAttributes() && backgroundAttributes() == r.backgroundAttributes() && markerAttributes() == r.markerAttributes() && decimalDigits() == r.decimalDigits() && prefix() == r.prefix() && suffix() == r.suffix() && dataLabel() == r.dataLabel() && powerOfTenDivisor() == r.powerOfTenDivisor() && showInfinite() == r.showInfinite() && negativePosition() == r.negativePosition() && positivePosition() == r.positivePosition() && showRepetitiveDataLabels() == r.showRepetitiveDataLabels() && showOverlappingDataLabels() == r.showOverlappingDataLabels() && usePercentage() == r.usePercentage() && mirrorNegativeValueTextRotation() == r.mirrorNegativeValueTextRotation(); } /*static*/ const DataValueAttributes& DataValueAttributes::defaultAttributes() { static const DataValueAttributes theDefaultDataValueAttributes; return theDefaultDataValueAttributes; } /*static*/ const QVariant& DataValueAttributes::defaultAttributesAsVariant() { static const QVariant theDefaultDataValueAttributesVariant = qVariantFromValue(defaultAttributes()); return theDefaultDataValueAttributesVariant; } void DataValueAttributes::setVisible( bool visible ) { d->visible = visible; } bool DataValueAttributes::isVisible() const { return d->visible; } void DataValueAttributes::setTextAttributes( const TextAttributes &a ) { d->textAttributes = a; } TextAttributes DataValueAttributes::textAttributes() const { return d->textAttributes; } void DataValueAttributes::setFrameAttributes( const FrameAttributes &a ) { d->frameAttributes = a; } FrameAttributes DataValueAttributes::frameAttributes() const { return d->frameAttributes; } void DataValueAttributes::setBackgroundAttributes( const BackgroundAttributes &a ) { d->backgroundAttributes = a; } BackgroundAttributes DataValueAttributes::backgroundAttributes() const { return d->backgroundAttributes; } void DataValueAttributes::setMarkerAttributes( const MarkerAttributes &a ) { d->markerAttributes = a; } MarkerAttributes DataValueAttributes::markerAttributes() const { return d->markerAttributes; } void DataValueAttributes::setMirrorNegativeValueTextRotation( bool enable ) { d->mirrorNegativeValueTextRotation = enable; } bool DataValueAttributes::mirrorNegativeValueTextRotation() const { return d->mirrorNegativeValueTextRotation; } void DataValueAttributes::setUsePercentage( bool enable ) { d->usePercentage = enable; } bool DataValueAttributes::usePercentage() const { return d->usePercentage; } void DataValueAttributes::setDecimalDigits( int digits ) { d->decimalDigits = digits; } int DataValueAttributes::decimalDigits() const { return d->decimalDigits; } void DataValueAttributes::setPrefix( const QString prefixString ) { d->prefix = prefixString; } QString DataValueAttributes::prefix() const { return d->prefix; } void DataValueAttributes::setSuffix( const QString suffixString ) { d->suffix = suffixString; } QString DataValueAttributes::suffix() const { return d->suffix; } void DataValueAttributes::setDataLabel( const QString label ) { d->dataLabel = label; } QString DataValueAttributes::dataLabel() const { return d->dataLabel; } bool DataValueAttributes::showRepetitiveDataLabels() const { return d->showRepetitiveDataLabels; } void DataValueAttributes::setShowRepetitiveDataLabels( bool showRepetitiveDataLabels ) { d->showRepetitiveDataLabels = showRepetitiveDataLabels; } bool DataValueAttributes::showOverlappingDataLabels() const { return d->showOverlappingDataLabels; } void DataValueAttributes::setShowOverlappingDataLabels( bool showOverlappingDataLabels ) { d->showOverlappingDataLabels = showOverlappingDataLabels; } void DataValueAttributes::setPowerOfTenDivisor( int powerOfTenDivisor ) { d->powerOfTenDivisor = powerOfTenDivisor; } int DataValueAttributes::powerOfTenDivisor() const { return d->powerOfTenDivisor; } void DataValueAttributes::setShowInfinite( bool infinite ) { d->showInfinite = infinite; } bool DataValueAttributes::showInfinite() const { return d->showInfinite; } void DataValueAttributes::setNegativePosition( const RelativePosition& relPosition ) { d->negativeRelPos = relPosition; } const RelativePosition DataValueAttributes::negativePosition() const { return d->negativeRelPos; } void DataValueAttributes::setPositivePosition( const RelativePosition& relPosition ) { d->positiveRelPos = relPosition; } const RelativePosition DataValueAttributes::positivePosition() const { return d->positiveRelPos; } #if !defined(QT_NO_DEBUG_STREAM) QDebug operator<<(QDebug dbg, const KChart::DataValueAttributes& val ) { dbg << "RelativePosition DataValueAttributes(" << "visible="<. */ #ifndef KCHARTDATASETPROXYMODEL_H #define KCHARTDATASETPROXYMODEL_H #include #include #include "kchart_export.h" namespace KChart { class IndexOutOfBoundsException; typedef QVector DatasetDescriptionVector; /** DatasetProxyModel takes a KChart dataset configuration and translates it into a filtering proxy model. The resulting model will only contain the part of the model that is selected by the dataset, and the according row and column header data. Currently, this model is implemented for table models only. The way it would work with models representing a tree is to be decided. The column selection is configured by passing a dataset description vector to the model. This vector (of integers) is supposed to have one value for each column of the original model. If the value at position x is -1, column x of the original model is not included in the dataset. If it is between 0 and (columnCount() -1), it is the column the source column is mapped to in the resulting model. Any other value is an error. */ class KCHART_EXPORT DatasetProxyModel : public QSortFilterProxyModel { Q_OBJECT public: /** Create a DatasetProxyModel. Without further configuration, this model is invalid. @see setDatasetDescriptionVector */ - explicit DatasetProxyModel ( QObject* parent = 0 ); + explicit DatasetProxyModel ( QObject* parent = nullptr ); QModelIndex buddy( const QModelIndex& index ) const Q_DECL_OVERRIDE; Qt::ItemFlags flags( const QModelIndex& index ) const Q_DECL_OVERRIDE; QModelIndex index( int row, int column, const QModelIndex &parent = QModelIndex() ) const Q_DECL_OVERRIDE; QModelIndex parent(const QModelIndex &child ) const Q_DECL_OVERRIDE; /** Implements the mapping from the source to the proxy indexes. */ QModelIndex mapFromSource ( const QModelIndex & sourceIndex ) const Q_DECL_OVERRIDE; /** Implements the mapping from the proxy to the source indexes. */ QModelIndex mapToSource ( const QModelIndex& proxyIndex ) const Q_DECL_OVERRIDE; /** Overloaded from base class. */ QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; /** Overloaded from base class. */ bool setData( const QModelIndex& index, const QVariant& value, int role ) Q_DECL_OVERRIDE; /** Overloaded from base class. */ QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const Q_DECL_OVERRIDE; /** Overloaded from base class. */ void setSourceModel(QAbstractItemModel *sourceModel) Q_DECL_OVERRIDE; /** Set the root index of the table in the source model */ void setSourceRootIndex(const QModelIndex& rootIdx); public Q_SLOTS: /** Reset all dataset description. After that, the result of the proxying is an empty model (a new dataset description needs to be set to achieve a non-empty result). */ void resetDatasetDescriptions(); /** Configure the dataset selection for the columns. Every call to this method resets the previous dataset description. */ void setDatasetColumnDescriptionVector ( const DatasetDescriptionVector& columnConfig ); /** Configure the dataset selection for the rows. Every call to this method resets the previous dataset description. */ void setDatasetRowDescriptionVector ( const DatasetDescriptionVector& rowConfig ); /** Convenience method to configure rows and columns in one step. */ void setDatasetDescriptionVectors ( const DatasetDescriptionVector& rowConfig, const DatasetDescriptionVector& columnConfig ); // FIXME: add convenience methods to configure common dataset // selections (like rectangular areas etc) protected: /** Decide whether the column is accepted. */ bool filterAcceptsColumn ( int sourceColumn, const QModelIndex & ) const Q_DECL_OVERRIDE; /** Decide whether the row is accepted. */ bool filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const Q_DECL_OVERRIDE; private: /** Map a proxy column to a source column. */ int mapProxyColumnToSource ( const int& proxyColumn ) const; /** Map a source column to a proxy column. */ int mapSourceColumnToProxy ( const int& sourceColumn ) const; /** Map a proxy row to a source row. */ int mapProxyRowToSource ( const int& proxyRow ) const; /** Map a source row to a proxy row. */ int mapSourceRowToProxy ( const int& sourceRow ) const; /** Initialize the transformation vectors from the dataset description. The input parameter "Configuration" is a vector that specifies what srce column will be mapped to what proxy column. Example: position: [0][1][2] value: [2][0][1] This will map the source column 2 to proxy column 0, source 0 to proxy 1, and source 1 to proxy 2. Source needs to have at least 2 column. The source-to-proxy mapping looks the same, except that it may contain values of -1, which means this column is not part of the resulting model. The values in the configuration vector must be unique (otherwise, a 1-to-1 mapping in both directions is impossible). sourceCount is the number of columns in the source model. The proxy-to-source map has as many elements as the proxy has columns, the source-to-proxy map has as many elements as the source has columns. Same goes for rows (the mapping logic is the same). */ void initializeDatasetDecriptors ( const DatasetDescriptionVector& inConfiguration, int sourceCount, DatasetDescriptionVector& outSourceToProxyMap, DatasetDescriptionVector& outProxyToSourceMap ); DatasetDescriptionVector mColSrcToProxyMap; DatasetDescriptionVector mColProxyToSrcMap; DatasetDescriptionVector mRowSrcToProxyMap; DatasetDescriptionVector mRowProxyToSrcMap; int mProxyRowCount; int mProxyColumnCount; QModelIndex mRootIndex; }; } #endif diff --git a/src/KChart/KChartDatasetSelector.h b/src/KChart/KChartDatasetSelector.h index 0a5c3f6..c48f99b 100644 --- a/src/KChart/KChartDatasetSelector.h +++ b/src/KChart/KChartDatasetSelector.h @@ -1,77 +1,77 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTDATASETSELECTOR_H #define KCHARTDATASETSELECTOR_H #include #include "KChartDatasetProxyModel.h" /** * \cond PRIVATE_API_DOCU * * ( This class is used internally by DatasetSelectorWidget. ) */ QT_BEGIN_NAMESPACE namespace Ui { class DatasetSelector; } QT_END_NAMESPACE /** * \endcond */ namespace KChart { class KCHART_EXPORT DatasetSelectorWidget : public QFrame { Q_OBJECT public: - explicit DatasetSelectorWidget ( QWidget* parent = 0 ); + explicit DatasetSelectorWidget ( QWidget* parent = nullptr ); ~DatasetSelectorWidget(); public Q_SLOTS: void setSourceRowCount ( const int& rowCount ); void setSourceColumnCount ( const int& columnCount ); Q_SIGNALS: void configureDatasetProxyModel ( const DatasetDescriptionVector& rowConfig, const DatasetDescriptionVector& columnConfig ); void mappingDisabled (); private Q_SLOTS: void updateState ( bool ); void calculateMapping(); private: void resetDisplayValues (); Ui::DatasetSelector* mUi; int mSourceRowCount; int mSourceColumnCount; }; } #endif diff --git a/src/KChart/KChartDiagramObserver.cpp b/src/KChart/KChartDiagramObserver.cpp index 7dd169c..1e832ec 100644 --- a/src/KChart/KChartDiagramObserver.cpp +++ b/src/KChart/KChartDiagramObserver.cpp @@ -1,152 +1,152 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "KChartMath_p.h" #include using namespace KChart; DiagramObserver::DiagramObserver( AbstractDiagram * diagram, QObject* parent ) : QObject( parent ), m_diagram( diagram ) { if ( m_diagram ) { connect( m_diagram, SIGNAL(destroyed(QObject*)), SLOT(slotDestroyed(QObject*))); connect( m_diagram, SIGNAL(aboutToBeDestroyed()), SLOT(slotAboutToBeDestroyed())); connect( m_diagram, SIGNAL(modelsChanged()), SLOT(slotModelsChanged())); } init(); } DiagramObserver::~DiagramObserver() { } const AbstractDiagram* DiagramObserver::diagram() const { return m_diagram; } AbstractDiagram* DiagramObserver::diagram() { return m_diagram; } void DiagramObserver::init() { if ( !m_diagram ) return; if ( m_model ) disconnect(m_model); if ( m_attributesmodel ) disconnect(m_attributesmodel); const bool con = connect( m_diagram, SIGNAL(viewportCoordinateSystemChanged()), this, SLOT(slotDataChanged()) ); Q_ASSERT( con ); Q_UNUSED( con ) connect( m_diagram, SIGNAL(dataHidden()), SLOT(slotDataHidden()) ); if ( m_diagram->model() ) { connect( m_diagram->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), SLOT(slotDataChanged(QModelIndex,QModelIndex))); connect( m_diagram->model(), SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(slotDataChanged())); connect( m_diagram->model(), SIGNAL(columnsInserted(QModelIndex,int,int)), SLOT(slotDataChanged())); connect( m_diagram->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(slotDataChanged())); connect( m_diagram->model(), SIGNAL(columnsRemoved(QModelIndex,int,int)), SLOT(slotDataChanged())); connect( m_diagram->model(), SIGNAL(modelReset()), SLOT(slotDataChanged())); connect( m_diagram->model(), SIGNAL(headerDataChanged(Qt::Orientation,int,int)), SLOT(slotHeaderDataChanged(Qt::Orientation,int,int))); } if ( m_diagram->attributesModel() ) connect( m_diagram->attributesModel(), SIGNAL(attributesChanged(QModelIndex,QModelIndex)), SLOT(slotAttributesChanged(QModelIndex,QModelIndex))); m_model = m_diagram->model(); m_attributesmodel = m_diagram->attributesModel(); } void DiagramObserver::slotDestroyed(QObject*) { //qDebug() << this << "emits signal\n" // " emit diagramDestroyed(" << m_diagram << ")"; AbstractDiagram* diag = m_diagram; - disconnect( m_diagram, 0, this, 0); - m_diagram = 0; + disconnect( m_diagram, nullptr, this, nullptr); + m_diagram = nullptr; emit diagramDestroyed( diag ); } void DiagramObserver::slotAboutToBeDestroyed() { emit diagramAboutToBeDestroyed( m_diagram ); } void DiagramObserver::slotModelsChanged() { init(); slotDataChanged(); slotAttributesChanged(); } void DiagramObserver::slotHeaderDataChanged(Qt::Orientation,int,int) { //qDebug() << "DiagramObserver::slotHeaderDataChanged()"; emit diagramDataChanged( m_diagram ); } void DiagramObserver::slotDataChanged(QModelIndex,QModelIndex) { slotDataChanged(); } void DiagramObserver::slotDataChanged() { //qDebug() << "DiagramObserver::slotDataChanged()"; emit diagramDataChanged( m_diagram ); } void DiagramObserver::slotDataHidden() { //qDebug() << "DiagramObserver::slotDataHidden()"; emit diagramDataHidden( m_diagram ); } void DiagramObserver::slotAttributesChanged(QModelIndex,QModelIndex) { slotAttributesChanged(); } void DiagramObserver::slotAttributesChanged() { //qDebug() << "DiagramObserver::slotAttributesChanged()"; emit diagramAttributesChanged( m_diagram ); } diff --git a/src/KChart/KChartDiagramObserver.h b/src/KChart/KChartDiagramObserver.h index 18cee7e..7ce129f 100644 --- a/src/KChart/KChartDiagramObserver.h +++ b/src/KChart/KChartDiagramObserver.h @@ -1,87 +1,87 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __KCHARTDIAGRAMOBSERVER_H_ #define __KCHARTDIAGRAMOBSERVER_H_ #include "KChartGlobal.h" #include #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; QT_END_NAMESPACE namespace KChart { class AbstractDiagram; /** * \brief A DiagramObserver watches the associated diagram for * changes and deletion and emits corresponsing signals. */ class KCHART_EXPORT DiagramObserver : public QObject { Q_OBJECT public: /** * Constructs a new observer observing the given diagram. */ - explicit DiagramObserver( AbstractDiagram * diagram, QObject* parent = 0 ); + explicit DiagramObserver( AbstractDiagram * diagram, QObject* parent = nullptr ); ~DiagramObserver(); const AbstractDiagram* diagram() const; AbstractDiagram* diagram(); Q_SIGNALS: /** This signal is emitted immediately before the diagram is * being destroyed. */ void diagramDestroyed( AbstractDiagram* diagram ); /** Emitted when a diagram is being destroyed, but before its data is invalidated **/ void diagramAboutToBeDestroyed( AbstractDiagram* diagram ); /** This signal is emitted whenever the data of the diagram changes. */ void diagramDataChanged( AbstractDiagram* diagram ); /** This signal is emitted whenever any of the data of the diagram was set (un)hidden. */ void diagramDataHidden( AbstractDiagram* diagram ); /** This signal is emitted whenever the attributes of the diagram change. */ void diagramAttributesChanged( AbstractDiagram* diagram ); private Q_SLOTS: void slotDestroyed(QObject*); void slotAboutToBeDestroyed(); void slotHeaderDataChanged(Qt::Orientation,int,int); void slotDataChanged(QModelIndex,QModelIndex); void slotDataChanged(); void slotDataHidden(); void slotAttributesChanged(); void slotAttributesChanged(QModelIndex,QModelIndex); void slotModelsChanged(); private: void init(); AbstractDiagram* m_diagram; QPointer m_model; QPointer m_attributesmodel; }; } #endif // KChartDiagramObserver_H diff --git a/src/KChart/KChartFrameAttributes.cpp b/src/KChart/KChartFrameAttributes.cpp index 9c71b2c..5ccdbf5 100644 --- a/src/KChart/KChartFrameAttributes.cpp +++ b/src/KChart/KChartFrameAttributes.cpp @@ -1,136 +1,136 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartFrameAttributes.h" #include "KChartMath_p.h" #define d d_func() using namespace KChart; class Q_DECL_HIDDEN FrameAttributes::Private { friend class FrameAttributes; public: Private(); private: bool visible; QPen pen; qreal cornerRadius; int padding; }; FrameAttributes::Private::Private() : visible( false ), cornerRadius( 0 ), padding( 0 ) { } FrameAttributes::FrameAttributes() : _d( new Private() ) { } FrameAttributes::FrameAttributes( const FrameAttributes& r ) : _d( new Private( *r.d ) ) { } FrameAttributes & FrameAttributes::operator=( const FrameAttributes& r ) { if ( this == &r ) return *this; *d = *r.d; return *this; } FrameAttributes::~FrameAttributes() { - delete _d; _d = 0; + delete _d; _d = nullptr; } bool FrameAttributes::operator==( const FrameAttributes& r ) const { return ( isVisible() == r.isVisible() && pen() == r.pen() && cornerRadius() == r.cornerRadius() && padding() == r.padding() ); } void FrameAttributes::setVisible( bool visible ) { d->visible = visible; } bool FrameAttributes::isVisible() const { return d->visible; } void FrameAttributes::setPen( const QPen & pen ) { d->pen = pen; } QPen FrameAttributes::pen() const { return d->pen; } void FrameAttributes::setCornerRadius(qreal radius) { d->cornerRadius = radius; } qreal FrameAttributes::cornerRadius() const { return d->cornerRadius; } void FrameAttributes::setPadding( int padding ) { d->padding = padding; } int FrameAttributes::padding() const { return d->padding; } #if !defined(QT_NO_DEBUG_STREAM) QDebug operator<<(QDebug dbg, const KChart::FrameAttributes& fa) { dbg << "KChart::FrameAttributes(" << "visible="<. */ #include "KChartGridAttributes.h" #include "KChartMath_p.h" #include #include #define d d_func() using namespace KChart; class Q_DECL_HIDDEN GridAttributes::Private { friend class GridAttributes; public: Private(); private: bool visible; KChartEnums::GranularitySequence sequence; bool linesOnAnnotations; qreal stepWidth; qreal subStepWidth; bool adjustLower; bool adjustUpper; QPen pen; bool subVisible; QPen subPen; bool outerVisible; QPen zeroPen; }; GridAttributes::Private::Private() : visible( true ), sequence( KChartEnums::GranularitySequence_10_20 ), linesOnAnnotations( false ), stepWidth( 0.0 ), subStepWidth( 0.0 ), adjustLower( true ), adjustUpper( true ), pen( QColor(0xa0, 0xa0, 0xa0 ) ), subVisible( true ), subPen( QColor(0xd0, 0xd0, 0xd0 ) ), outerVisible( true ), zeroPen( QColor( 0x00, 0x00, 0x80 ) ) { pen.setCapStyle( Qt::FlatCap ); subPen.setCapStyle( Qt::FlatCap ); zeroPen.setCapStyle( Qt::FlatCap ); } GridAttributes::GridAttributes() : _d( new Private() ) { // this bloc left empty intentionally } GridAttributes::GridAttributes( const GridAttributes& r ) : _d( new Private( *r.d ) ) { } GridAttributes & GridAttributes::operator=( const GridAttributes& r ) { if ( this == &r ) return *this; *d = *r.d; return *this; } GridAttributes::~GridAttributes() { - delete _d; _d = 0; + delete _d; _d = nullptr; } bool GridAttributes::operator==( const GridAttributes& r ) const { return isGridVisible() == r.isGridVisible() && gridGranularitySequence() == r.gridGranularitySequence() && linesOnAnnotations() == r.linesOnAnnotations() && adjustLowerBoundToGrid() == r.adjustLowerBoundToGrid() && adjustUpperBoundToGrid() == r.adjustUpperBoundToGrid() && gridPen() == r.gridPen() && isSubGridVisible() == r.isSubGridVisible() && subGridPen() == r.subGridPen() && isOuterLinesVisible() == r.isOuterLinesVisible() && zeroLinePen() == r.zeroLinePen(); } void GridAttributes::setGridVisible( bool visible ) { d->visible = visible; } bool GridAttributes::isGridVisible() const { return d->visible; } void GridAttributes::setLinesOnAnnotations( bool b ) { d->linesOnAnnotations = b; } bool GridAttributes::linesOnAnnotations() const { return d->linesOnAnnotations; } /** * Specifies the step width to be used for calculating * the grid lines. * * \note Step with can be set for Linear axis calculation mode only, * there is no way to specify a step width for Logarithmic axes. * * By default the GridAttributes class does not use a fixed step width, * but it uses KChartEnums::GranularitySequence_10_20. * * \param stepWidth the step width to be used. * If this parameter is omitted (or set to Zero, resp.) * the automatic step width calculation will be done, * using the granularity sequence specified. * This is the default. * * \sa gridStepWidth, setGranularitySequence */ void GridAttributes::setGridStepWidth( qreal stepWidth ) { d->stepWidth = stepWidth; } /** * Returns the step width to be used for calculating * the grid lines. * * \sa setGridStepWidth */ qreal GridAttributes::gridStepWidth() const { return d->stepWidth; } /** * Specifies the sub-step width to be used for calculating * the grid sub-lines. * * * \param subStepWidth the sub-step width to be used. * If this parameter is omitted (or set to Zero, resp.) * the automatic calculation will be done, using the * granularity sequence specified. * This is the default. * * \sa gridSubStepWidth */ void GridAttributes::setGridSubStepWidth( qreal subStepWidth ) { d->subStepWidth = subStepWidth; } /** * Returns the sub-step width to be used for calculating * the sub-grid lines. * * \sa setGridStepWidth */ qreal GridAttributes::gridSubStepWidth() const { return d->subStepWidth; } /** * Specifies the granularity sequence to be used for calculating * the grid lines. * * By default the GridAttributes class uses KChartEnums::GranularitySequence_10_20. * * \note Granularity can be set for Linear axis calculation mode only, * there is no way to specify a step width for Logarithmic axes. * * \note The sequence specified by this method is ignored, if * a fixed step width was specified via setStepWidth. * * \param sequence one of the sequences declared in * KChartEnums::GranularitySequence. * * \sa gridGranularitySequence, setStepWidth */ void GridAttributes::setGridGranularitySequence( KChartEnums::GranularitySequence sequence ) { d->sequence = sequence; } /** * Returns the granularity sequence to be used for calculating * the grid lines. * * \sa setGridGranularitySequence */ KChartEnums::GranularitySequence GridAttributes::gridGranularitySequence() const { return d->sequence; } void GridAttributes::setAdjustBoundsToGrid( bool adjustLower, bool adjustUpper ) { d->adjustLower = adjustLower; d->adjustUpper = adjustUpper; } bool GridAttributes::adjustLowerBoundToGrid() const { return d->adjustLower; } bool GridAttributes::adjustUpperBoundToGrid() const { return d->adjustUpper; } void GridAttributes::setGridPen( const QPen & pen ) { d->pen = pen; d->pen.setCapStyle( Qt::FlatCap ); } QPen GridAttributes::gridPen() const { return d->pen; } void GridAttributes::setSubGridVisible( bool visible ) { d->subVisible = visible; } bool GridAttributes::isSubGridVisible() const { return d->subVisible; } void GridAttributes::setSubGridPen( const QPen & pen ) { d->subPen = pen; d->subPen.setCapStyle( Qt::FlatCap ); } QPen GridAttributes::subGridPen() const { return d->subPen; } void GridAttributes::setOuterLinesVisible( bool visible ) { d->outerVisible = visible; } bool GridAttributes::isOuterLinesVisible() const { return d->outerVisible; } void GridAttributes::setZeroLinePen( const QPen & pen ) { d->zeroPen = pen; d->zeroPen.setCapStyle( Qt::FlatCap ); } QPen GridAttributes::zeroLinePen() const { return d->zeroPen; } #if !defined(QT_NO_DEBUG_STREAM) QDebug operator<<(QDebug dbg, const KChart::GridAttributes& a) { dbg << "KChart::GridAttributes(" << "visible="<. */ #include "KChartHeaderFooter.h" #include "KChartHeaderFooter_p.h" #include "KChartChart.h" #include #include "KTextDocument.h" #include "KChartMath_p.h" #include #include #include #include #include #include #include using namespace KChart; HeaderFooter::Private::Private() : type( Header ), position( Position::North ) { } HeaderFooter::Private::~Private() { } #define d d_func() HeaderFooter::HeaderFooter( Chart* parent ) : TextArea( new Private() ) { setParent( parent ); init(); } HeaderFooter::~HeaderFooter() { emit destroyedHeaderFooter( this ); } void HeaderFooter::setParent( QObject* parent ) { QObject::setParent( parent ); setParentWidget( qobject_cast( parent ) ); if ( parent && ! autoReferenceArea() ) setAutoReferenceArea( parent ); } void HeaderFooter::init() { TextAttributes ta; ta.setPen( QPen(Qt::black) ); ta.setFont( QFont( QLatin1String( "helvetica" ), 10, QFont::Bold, false ) ); Measure m( 35.0 ); m.setRelativeMode( autoReferenceArea(), KChartEnums::MeasureOrientationMinimum ); ta.setFontSize( m ); m.setValue( 8.0 ); m.setCalculationMode( KChartEnums::MeasureCalculationModeAbsolute ); ta.setMinimalFontSize( m ); setTextAttributes( ta ); } /** * Creates an exact copy of this header/footer. */ HeaderFooter * HeaderFooter::clone() const { - HeaderFooter* headerFooter = new HeaderFooter( new Private( *d ), 0 ); + HeaderFooter* headerFooter = new HeaderFooter( new Private( *d ), nullptr ); headerFooter->setType( type() ); headerFooter->setPosition( position() ); headerFooter->setText( text() ); headerFooter->setTextAttributes( textAttributes() ); return headerFooter; } bool HeaderFooter::compare( const HeaderFooter& other ) const { return (type() == other.type()) && (position() == other.position()) && // also compare members inherited from the base class: (autoReferenceArea() == other.autoReferenceArea()) && (text() == other.text()) && (textAttributes() == other.textAttributes()); } void HeaderFooter::setType( HeaderFooterType type ) { if ( d->type != type ) { d->type = type; emit positionChanged( this ); } } HeaderFooter::HeaderFooterType HeaderFooter::type() const { return d->type; } void HeaderFooter::setPosition( Position position ) { if ( d->position != position ) { d->position = position; emit positionChanged( this ); } } Position HeaderFooter::position() const { return d->position; } diff --git a/src/KChart/KChartHeaderFooter.h b/src/KChart/KChartHeaderFooter.h index 76a3ed9..d0b2f3a 100644 --- a/src/KChart/KChartHeaderFooter.h +++ b/src/KChart/KChartHeaderFooter.h @@ -1,68 +1,68 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTHEADERFOOTER_H #define KCHARTHEADERFOOTER_H #include "KChartTextArea.h" #include "KChartPosition.h" namespace KChart { class Chart; class TextAttributes; /** * @brief A header or footer displaying text above or below charts */ class KCHART_EXPORT HeaderFooter : public TextArea { Q_OBJECT KCHART_DECLARE_PRIVATE_DERIVED_PARENT( HeaderFooter, Chart* ) public: - HeaderFooter( Chart* parent = 0 ); + HeaderFooter( Chart* parent = nullptr ); virtual ~HeaderFooter(); virtual HeaderFooter * clone() const; bool compare( const HeaderFooter& other ) const; enum HeaderFooterType{ Header, Footer }; void setType( HeaderFooterType type ); HeaderFooterType type() const; void setPosition( Position position ); Position position() const; void setParent( QObject* parent ); Q_SIGNALS: void destroyedHeaderFooter( HeaderFooter* ); void positionChanged( HeaderFooter* ); }; // End of class HeaderFooter } #endif // KCHARTHEADERFOOTER_H diff --git a/src/KChart/KChartLayoutItems.cpp b/src/KChart/KChartLayoutItems.cpp index b69fde4..a5d7d5f 100644 --- a/src/KChart/KChartLayoutItems.cpp +++ b/src/KChart/KChartLayoutItems.cpp @@ -1,1026 +1,1026 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartLayoutItems.h" #include "KTextDocument.h" #include "KChartAbstractArea.h" #include "KChartAbstractDiagram.h" #include "KChartBackgroundAttributes.h" #include "KChartFrameAttributes.h" #include "KChartPaintContext.h" #include "KChartPainterSaver_p.h" #include "KChartPrintingParameters.h" #include "KChartMath_p.h" #include #include #include #include #include #include #include #include #include #include #include //#define DEBUG_ITEMS_PAINT /** Inform the item about its widget: This enables the item, to trigger that widget's update, whenever the size of the item's contents has changed. Thus, you need to call setParentWidget on every item, that has a non-fixed size. */ void KChart::AbstractLayoutItem::setParentWidget( QWidget* widget ) { mParent = widget; } void KChart::AbstractLayoutItem::paintAll( QPainter& painter ) { paint( &painter ); } /** * Default impl: Paint the complete item using its layouted position and size. */ void KChart::AbstractLayoutItem::paintCtx( PaintContext* context ) { if ( context ) paint( context->painter() ); } /** Report changed size hint: ask the parent widget to recalculate the layout. */ void KChart::AbstractLayoutItem::sizeHintChanged() const { // This is exactly like what QWidget::updateGeometry does. // qDebug("KChart::AbstractLayoutItem::sizeHintChanged() called"); if ( mParent ) { if ( mParent->layout() ) mParent->layout()->invalidate(); else QApplication::postEvent( mParent, new QEvent( QEvent::LayoutRequest ) ); } } KChart::TextBubbleLayoutItem::TextBubbleLayoutItem( const QString& text, const KChart::TextAttributes& attributes, const QObject* area, KChartEnums::MeasureOrientation orientation, Qt::Alignment alignment ) : AbstractLayoutItem( alignment ), m_text( new TextLayoutItem( text, attributes, area, orientation, alignment ) ) { } KChart::TextBubbleLayoutItem::TextBubbleLayoutItem() : AbstractLayoutItem( Qt::AlignLeft ), m_text( new TextLayoutItem() ) { } KChart::TextBubbleLayoutItem::~TextBubbleLayoutItem() { delete m_text; } void KChart::TextBubbleLayoutItem::setAutoReferenceArea( const QObject* area ) { m_text->setAutoReferenceArea( area ); } const QObject* KChart::TextBubbleLayoutItem::autoReferenceArea() const { return m_text->autoReferenceArea(); } void KChart::TextBubbleLayoutItem::setText( const QString& text ) { m_text->setText( text ); } QString KChart::TextBubbleLayoutItem::text() const { return m_text->text(); } void KChart::TextBubbleLayoutItem::setTextAttributes( const TextAttributes& a ) { m_text->setTextAttributes( a ); } KChart::TextAttributes KChart::TextBubbleLayoutItem::textAttributes() const { return m_text->textAttributes(); } bool KChart::TextBubbleLayoutItem::isEmpty() const { return m_text->isEmpty(); } Qt::Orientations KChart::TextBubbleLayoutItem::expandingDirections() const { return m_text->expandingDirections(); } QSize KChart::TextBubbleLayoutItem::maximumSize() const { const int border = borderWidth(); return m_text->maximumSize() + QSize( 2 * border, 2 * border ); } QSize KChart::TextBubbleLayoutItem::minimumSize() const { const int border = borderWidth(); return m_text->minimumSize() + QSize( 2 * border, 2 * border ); } QSize KChart::TextBubbleLayoutItem::sizeHint() const { const int border = borderWidth(); return m_text->sizeHint() + QSize( 2 * border, 2 * border ); } void KChart::TextBubbleLayoutItem::setGeometry( const QRect& r ) { const int border = borderWidth(); m_text->setGeometry( r.adjusted( border, border, -border, -border ) ); } QRect KChart::TextBubbleLayoutItem::geometry() const { const int border = borderWidth(); return m_text->geometry().adjusted( -border, -border, border, border ); } void KChart::TextBubbleLayoutItem::paint( QPainter* painter ) { const QPen oldPen = painter->pen(); const QBrush oldBrush = painter->brush(); painter->setPen( Qt::black ); painter->setBrush( QColor( 255, 255, 220 ) ); painter->drawRoundRect( geometry(), 10 ); painter->setPen( oldPen ); painter->setBrush( oldBrush ); m_text->paint( painter ); } int KChart::TextBubbleLayoutItem::borderWidth() const { return 1; } KChart::TextLayoutItem::TextLayoutItem( const QString& text, const KChart::TextAttributes& attributes, const QObject* area, KChartEnums::MeasureOrientation orientation, Qt::Alignment alignment ) : AbstractLayoutItem( alignment ) , mText( text ) , mTextAlignment( alignment ) , mAttributes( attributes ) , mAutoReferenceArea( area ) , mAutoReferenceOrientation( orientation ) , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint() , cachedFontSize( 0.0 ) , cachedFont( mAttributes.font() ) { } KChart::TextLayoutItem::TextLayoutItem() : AbstractLayoutItem( Qt::AlignLeft ) , mText() , mTextAlignment( Qt::AlignLeft ) , mAttributes() - , mAutoReferenceArea( 0 ) + , mAutoReferenceArea( nullptr ) , mAutoReferenceOrientation( KChartEnums::MeasureOrientationHorizontal ) , cachedSizeHint() // default this to invalid to force just-in-time calculation before first use of sizeHint() , cachedFontSize( 0.0 ) , cachedFont( mAttributes.font() ) { } void KChart::TextLayoutItem::setAutoReferenceArea( const QObject* area ) { mAutoReferenceArea = area; cachedSizeHint = QSize(); sizeHint(); } const QObject* KChart::TextLayoutItem::autoReferenceArea() const { return mAutoReferenceArea; } void KChart::TextLayoutItem::setText(const QString & text) { mText = text; cachedSizeHint = QSize(); sizeHint(); if ( mParent ) mParent->update(); } QString KChart::TextLayoutItem::text() const { return mText; } void KChart::TextLayoutItem::setTextAlignment( Qt::Alignment alignment) { if ( mTextAlignment == alignment ) return; mTextAlignment = alignment; if ( mParent ) mParent->update(); } Qt::Alignment KChart::TextLayoutItem::textAlignment() const { return mTextAlignment; } /** \brief Use this to specify the text attributes to be used for this item. \sa textAttributes */ void KChart::TextLayoutItem::setTextAttributes( const TextAttributes &a ) { mAttributes = a; cachedFont = a.font(); cachedSizeHint = QSize(); // invalidate size hint sizeHint(); if ( mParent ) mParent->update(); } /** Returns the text attributes to be used for this item. \sa setTextAttributes */ KChart::TextAttributes KChart::TextLayoutItem::textAttributes() const { return mAttributes; } Qt::Orientations KChart::TextLayoutItem::expandingDirections() const { return Qt::Orientations(); // Grow neither vertically nor horizontally } QRect KChart::TextLayoutItem::geometry() const { return mRect; } bool KChart::TextLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KChart::TextLayoutItem::maximumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } QSize KChart::TextLayoutItem::minimumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } void KChart::TextLayoutItem::setGeometry( const QRect& r ) { mRect = r; } // returns the bounding box of rect rotated around its center QRectF rotatedRect( const QRectF& rect, qreal rotation ) { QTransform t; QPointF center = rect.center(); t.translate( center.x(), center.y() ); t.rotate( rotation ); t.translate( -center.x(), -center.y() ); return t.mapRect( rect ); } qreal KChart::TextLayoutItem::fitFontSizeToGeometry() const { QFont f = realFont(); const qreal origResult = f.pointSizeF(); qreal result = origResult; const qreal minSize = mAttributes.minimalFontSize().value(); const QSize mySize = geometry().size(); if ( mySize.isNull() ) { return result; } QFontMetrics fm( f ); while ( true ) { const QSizeF textSize = rotatedRect( fm.boundingRect( mText ), mAttributes.rotation() ).normalized().size(); if ( textSize.height() <= mySize.height() && textSize.width() <= mySize.width() ) { return result; } result -= 0.5; if ( minSize > 0 && result < minSize ) { return result + 0.5; } else if ( result <= 0.0 ) { return origResult; } f.setPointSizeF( result ); fm = QFontMetrics( f ); } } qreal KChart::TextLayoutItem::realFontSize() const { return mAttributes.calculatedFontSize( mAutoReferenceArea, mAutoReferenceOrientation ); } bool KChart::TextLayoutItem::maybeUpdateRealFont() const { const qreal fntSiz = realFontSize(); const bool doUpdate = !cachedSizeHint.isValid() || cachedFontSize != fntSiz; if ( doUpdate && fntSiz > 0.0 ) { cachedFontSize = fntSiz; cachedFont.setPointSizeF( fntSiz ); } return doUpdate; // "didUpdate" by now } QFont KChart::TextLayoutItem::realFont() const { maybeUpdateRealFont(); return cachedFont; } QPolygon KChart::TextLayoutItem::boundingPolygon() const { // should probably call sizeHint() here, but that one is expensive (see TODO there) return mCachedBoundingPolygon; } bool KChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPointF& myPos, const QPointF& otherPos ) const { return intersects( other, myPos.toPoint(), otherPos.toPoint() ); } bool KChart::TextLayoutItem::intersects( const TextLayoutItem& other, const QPoint& myPos, const QPoint& otherPos ) const { QRegion myRegion( boundingPolygon().translated( myPos - otherPos ) ); QRegion otherRegion( other.boundingPolygon() ); return myRegion.intersects( otherRegion ); } QSize KChart::TextLayoutItem::sizeHint() const { // ### we only really need to recalculate the size hint when mAttributes.rotation has *changed* if ( maybeUpdateRealFont() || mAttributes.rotation() || !cachedSizeHint.isValid() ) { const QSize newSizeHint( calcSizeHint( cachedFont ) ); Q_ASSERT( newSizeHint.isValid() ); if ( newSizeHint != cachedSizeHint ) { cachedSizeHint = newSizeHint; sizeHintChanged(); } } return cachedSizeHint; } QSize KChart::TextLayoutItem::sizeHintUnrotated() const { maybeUpdateRealFont(); // make sure the cached font is up to date return unrotatedSizeHint( cachedFont ); } // PENDING(kalle) Support auto shrink QSize KChart::TextLayoutItem::unrotatedTextSize( QFont fnt ) const { if ( fnt == QFont() ) { fnt = realFont(); // this is the cached font in most cases } const QFontMetricsF fm( fnt, GlobalMeasureScaling::paintDevice() ); QRect veryLarge( 0, 0, 100000, 100000 ); // this overload of boundingRect() interprets \n as line breaks, not as regular characters. return fm.boundingRect( veryLarge, Qt::AlignLeft | Qt::AlignTop, mText ).size().toSize(); } int KChart::TextLayoutItem::marginWidth() const { return marginWidth( unrotatedTextSize() ); } int KChart::TextLayoutItem::marginWidth( const QSize& textSize ) const { - return qMin ( QApplication::style()->pixelMetric( QStyle::PM_ButtonMargin, 0, 0 ), + return qMin ( QApplication::style()->pixelMetric( QStyle::PM_ButtonMargin, nullptr, nullptr ), // decrease frame size if the text is small textSize.height() * 2 / 3 ); } QSize KChart::TextLayoutItem::unrotatedSizeHint( const QFont& fnt ) const { QSize ret = unrotatedTextSize( fnt ); const int margin = marginWidth( ret ); ret += QSize( margin, margin ); return ret; } QSize KChart::TextLayoutItem::calcSizeHint( const QFont& font ) const { const QSize size = unrotatedSizeHint( font ); QPoint topLeft( -size.width() * 0.5, -size.height() * 0.5 ); if ( !mAttributes.rotation() ) { mCachedBoundingPolygon.resize( 4 ); // using the same winding order as returned by QPolygon QTransform::mapToPolygon(const QRect&), // which is: 0-1: top edge, 1-2: right edge, 2-3: bottom edge, 3-0: left edge (of input rect) mCachedBoundingPolygon[ 0 ] = topLeft; mCachedBoundingPolygon[ 1 ] = topLeft + QPoint( size.width(), 0 ); // top right mCachedBoundingPolygon[ 2 ] = topLeft + QPoint( size.width(), size.height() ); // bottom right mCachedBoundingPolygon[ 3 ] = topLeft + QPoint( 0, size.height() ); // bottom left return size; } const QRect rect( topLeft, size ); QTransform t; t.rotate( mAttributes.rotation() ); mCachedBoundingPolygon = t.mapToPolygon( rect ); return mCachedBoundingPolygon.boundingRect().size(); } void KChart::TextLayoutItem::paint( QPainter* painter ) { if ( !mRect.isValid() ) { return; } const PainterSaver painterSaver( painter ); QFont f = realFont(); if ( mAttributes.autoShrink() ) { f.setPointSizeF( fitFontSizeToGeometry() ); } painter->setFont( f ); QSize innerSize = unrotatedTextSize(); QRectF rect = QRectF( QPointF( 0, 0 ), innerSize ); rect.translate( -rect.center() ); painter->translate( mRect.center() ); painter->rotate( mAttributes.rotation() ); #ifdef DEBUG_ITEMS_PAINT painter->setPen( Qt::red ); painter->drawRect( rect ); #endif painter->setPen( PrintingParameters::scalePen( mAttributes.pen() ) ); QTextDocument* document = mAttributes.textDocument(); if ( document ) { document->setPageSize( rect.size() ); document->setHtml( mText ); QAbstractTextDocumentLayout::PaintContext paintcontext; // ### this doesn't work for rotated painting because clip does not translate the painting // TODO translate the painting either using a QTransform or one of QPainter's transform stages paintcontext.clip = rect; document->documentLayout()->draw( painter, paintcontext ); } else { painter->drawText( rect, mTextAlignment, mText ); } } KChart::HorizontalLineLayoutItem::HorizontalLineLayoutItem() : AbstractLayoutItem( Qt::AlignCenter ) { } Qt::Orientations KChart::HorizontalLineLayoutItem::expandingDirections() const { return Qt::Horizontal; } QRect KChart::HorizontalLineLayoutItem::geometry() const { return mRect; } bool KChart::HorizontalLineLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KChart::HorizontalLineLayoutItem::maximumSize() const { return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); } QSize KChart::HorizontalLineLayoutItem::minimumSize() const { return QSize( 0, 0 ); } void KChart::HorizontalLineLayoutItem::setGeometry( const QRect& r ) { mRect = r; } QSize KChart::HorizontalLineLayoutItem::sizeHint() const { return QSize( -1, 3 ); // see qframe.cpp } void KChart::HorizontalLineLayoutItem::paint( QPainter* painter ) { if ( !mRect.isValid() ) return; painter->drawLine( QPointF( mRect.left(), mRect.center().y() ), QPointF( mRect.right(), mRect.center().y() ) ); } KChart::VerticalLineLayoutItem::VerticalLineLayoutItem() : AbstractLayoutItem( Qt::AlignCenter ) { } Qt::Orientations KChart::VerticalLineLayoutItem::expandingDirections() const { return Qt::Vertical; } QRect KChart::VerticalLineLayoutItem::geometry() const { return mRect; } bool KChart::VerticalLineLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KChart::VerticalLineLayoutItem::maximumSize() const { return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); } QSize KChart::VerticalLineLayoutItem::minimumSize() const { return QSize( 0, 0 ); } void KChart::VerticalLineLayoutItem::setGeometry( const QRect& r ) { mRect = r; } QSize KChart::VerticalLineLayoutItem::sizeHint() const { return QSize( 3, -1 ); // see qframe.cpp } void KChart::VerticalLineLayoutItem::paint( QPainter* painter ) { if ( !mRect.isValid() ) return; painter->drawLine( QPointF( mRect.center().x(), mRect.top() ), QPointF( mRect.center().x(), mRect.bottom() ) ); } KChart::MarkerLayoutItem::MarkerLayoutItem( KChart::AbstractDiagram* diagram, const MarkerAttributes& marker, const QBrush& brush, const QPen& pen, Qt::Alignment alignment ) : AbstractLayoutItem( alignment ) , mDiagram( diagram ) , mMarker( marker ) , mBrush( brush ) , mPen( pen ) { } Qt::Orientations KChart::MarkerLayoutItem::expandingDirections() const { return Qt::Orientations(); // Grow neither vertically nor horizontally } QRect KChart::MarkerLayoutItem::geometry() const { return mRect; } bool KChart::MarkerLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KChart::MarkerLayoutItem::maximumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } QSize KChart::MarkerLayoutItem::minimumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } void KChart::MarkerLayoutItem::setGeometry( const QRect& r ) { mRect = r; } QSize KChart::MarkerLayoutItem::sizeHint() const { //qDebug() << "KChart::MarkerLayoutItem::sizeHint() returns:"<(( rect.width() - siz.width()) / 2.0 ), static_cast(( rect.height() - siz.height()) / 2.0 ) ); #ifdef DEBUG_ITEMS_PAINT QPointF oldPos = pos; #endif // And finally, drawMarker() assumes the position to be the center // of the marker, adjust again. pos += QPointF( static_cast( siz.width() ) / 2.0, static_cast( siz.height() )/ 2.0 ); diagram->paintMarker( painter, marker, brush, pen, pos.toPoint(), siz ); #ifdef DEBUG_ITEMS_PAINT const QPen oldPen( painter->pen() ); painter->setPen( Qt::red ); painter->drawRect( QRect( oldPos.toPoint(), siz ) ); painter->setPen( oldPen ); #endif } KChart::LineLayoutItem::LineLayoutItem( KChart::AbstractDiagram* diagram, int length, const QPen& pen, Qt::Alignment legendLineSymbolAlignment, Qt::Alignment alignment ) : AbstractLayoutItem( alignment ) , mDiagram( diagram ) , mLength( length ) , mPen( pen ) , mLegendLineSymbolAlignment(legendLineSymbolAlignment) { // enforce a minimum pen width if ( pen.width() < 2 ) mPen.setWidth( 2 ); } Qt::Orientations KChart::LineLayoutItem::expandingDirections() const { return Qt::Orientations(); // Grow neither vertically nor horizontally } QRect KChart::LineLayoutItem::geometry() const { return mRect; } bool KChart::LineLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KChart::LineLayoutItem::maximumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } QSize KChart::LineLayoutItem::minimumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } void KChart::LineLayoutItem::setGeometry( const QRect& r ) { mRect = r; } QSize KChart::LineLayoutItem::sizeHint() const { return QSize( mLength, mPen.width() + 2 ); } void KChart::LineLayoutItem::setLegendLineSymbolAlignment(Qt::Alignment legendLineSymbolAlignment) { if (mLegendLineSymbolAlignment == legendLineSymbolAlignment) return; mLegendLineSymbolAlignment = legendLineSymbolAlignment; } Qt::Alignment KChart::LineLayoutItem::legendLineSymbolAlignment() const { return mLegendLineSymbolAlignment; } void KChart::LineLayoutItem::paint( QPainter* painter ) { paintIntoRect( painter, mRect, mPen, mLegendLineSymbolAlignment ); } void KChart::LineLayoutItem::paintIntoRect( QPainter* painter, const QRect& rect, const QPen& pen, Qt::Alignment lineAlignment) { if ( ! rect.isValid() ) return; const QPen oldPen = painter->pen(); painter->setPen( PrintingParameters::scalePen( pen ) ); qreal y = 0; if (lineAlignment == Qt::AlignTop) y = rect.top(); else if (lineAlignment == Qt::AlignBottom) y = rect.bottom(); else y = rect.center().y(); painter->drawLine( QPointF( rect.left(), y ), QPointF( rect.right(), y ) ); painter->setPen( oldPen ); } KChart::LineWithMarkerLayoutItem::LineWithMarkerLayoutItem( KChart::AbstractDiagram* diagram, int lineLength, const QPen& linePen, int markerOffs, const MarkerAttributes& marker, const QBrush& markerBrush, const QPen& markerPen, Qt::Alignment alignment ) : AbstractLayoutItem( alignment ) , mDiagram( diagram ) , mLineLength( lineLength ) , mLinePen( linePen ) , mMarkerOffs( markerOffs ) , mMarker( marker ) , mMarkerBrush( markerBrush ) , mMarkerPen( markerPen ) { } Qt::Orientations KChart::LineWithMarkerLayoutItem::expandingDirections() const { return Qt::Orientations(); // Grow neither vertically nor horizontally } QRect KChart::LineWithMarkerLayoutItem::geometry() const { return mRect; } bool KChart::LineWithMarkerLayoutItem::isEmpty() const { return false; // never empty, otherwise the layout item would not exist } QSize KChart::LineWithMarkerLayoutItem::maximumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } QSize KChart::LineWithMarkerLayoutItem::minimumSize() const { return sizeHint(); // PENDING(kalle) Review, quite inflexible } void KChart::LineWithMarkerLayoutItem::setGeometry( const QRect& r ) { mRect = r; } QSize KChart::LineWithMarkerLayoutItem::sizeHint() const { const QSize lineSize( mLineLength, mLinePen.width() + 2 ); return lineSize.expandedTo( mMarker.markerSize().toSize() ); } void KChart::LineWithMarkerLayoutItem::paint( QPainter* painter ) { // paint the line over the full width, into the vertical middle of the rect LineLayoutItem::paintIntoRect( painter, mRect, mLinePen, Qt::AlignCenter ); // paint the marker with the given offset from the left side of the line const QRect r( QPoint( mRect.x()+mMarkerOffs, mRect.y() ), QSize( mMarker.markerSize().toSize().width(), mRect.height() ) ); MarkerLayoutItem::paintIntoRect( painter, r, mDiagram, mMarker, mMarkerBrush, mMarkerPen ); } KChart::AutoSpacerLayoutItem::AutoSpacerLayoutItem( bool layoutIsAtTopPosition, QHBoxLayout *rightLeftLayout, bool layoutIsAtLeftPosition, QVBoxLayout *topBottomLayout ) : AbstractLayoutItem( Qt::AlignCenter ) , mLayoutIsAtTopPosition( layoutIsAtTopPosition ) , mRightLeftLayout( rightLeftLayout ) , mLayoutIsAtLeftPosition( layoutIsAtLeftPosition ) , mTopBottomLayout( topBottomLayout ) { } Qt::Orientations KChart::AutoSpacerLayoutItem::expandingDirections() const { return Qt::Orientations(); // Grow neither vertically nor horizontally } QRect KChart::AutoSpacerLayoutItem::geometry() const { return mRect; } bool KChart::AutoSpacerLayoutItem::isEmpty() const { return true; // never empty, otherwise the layout item would not exist } QSize KChart::AutoSpacerLayoutItem::maximumSize() const { return sizeHint(); } QSize KChart::AutoSpacerLayoutItem::minimumSize() const { return sizeHint(); } void KChart::AutoSpacerLayoutItem::setGeometry( const QRect& r ) { mRect = r; } static void updateCommonBrush( QBrush& commonBrush, bool& bStart, const KChart::AbstractArea& area ) { const KChart::BackgroundAttributes ba( area.backgroundAttributes() ); const bool hasSimpleBrush = ( ! area.frameAttributes().isVisible() && ba.isVisible() && ba.pixmapMode() == KChart::BackgroundAttributes::BackgroundPixmapModeNone && - ba.brush().gradient() == 0 ); + ba.brush().gradient() == nullptr ); if ( bStart ) { bStart = false; commonBrush = hasSimpleBrush ? ba.brush() : QBrush(); } else { if ( ! hasSimpleBrush || ba.brush() != commonBrush ) { commonBrush = QBrush(); } } } QSize KChart::AutoSpacerLayoutItem::sizeHint() const { QBrush commonBrush; bool bStart=true; // calculate the maximal overlap of the top/bottom axes: int topBottomOverlap = 0; if ( mTopBottomLayout ) { for (int i = 0; i < mTopBottomLayout->count(); ++i) { AbstractArea* area = dynamic_cast(mTopBottomLayout->itemAt(i)); if ( area ) { //qDebug() << "AutoSpacerLayoutItem testing" << area; topBottomOverlap = qMax( topBottomOverlap, mLayoutIsAtLeftPosition ? area->rightOverlap() : area->leftOverlap() ); updateCommonBrush( commonBrush, bStart, *area ); } } } // calculate the maximal overlap of the left/right axes: int leftRightOverlap = 0; if ( mRightLeftLayout ) { for (int i = 0; i < mRightLeftLayout->count(); ++i) { AbstractArea* area = dynamic_cast(mRightLeftLayout->itemAt(i)); if ( area ) { //qDebug() << "AutoSpacerLayoutItem testing" << area; leftRightOverlap = qMax( leftRightOverlap, mLayoutIsAtTopPosition ? area->bottomOverlap() : area->topOverlap() ); updateCommonBrush( commonBrush, bStart, *area ); } } } if ( topBottomOverlap > 0 && leftRightOverlap > 0 ) mCommonBrush = commonBrush; else mCommonBrush = QBrush(); mCachedSize = QSize( topBottomOverlap, leftRightOverlap ); //qDebug() << mCachedSize; return mCachedSize; } void KChart::AutoSpacerLayoutItem::paint( QPainter* painter ) { if ( mParentLayout && mRect.isValid() && mCachedSize.isValid() && mCommonBrush.style() != Qt::NoBrush ) { QPoint p1( mRect.topLeft() ); QPoint p2( mRect.bottomRight() ); if ( mLayoutIsAtLeftPosition ) p1.rx() += mCachedSize.width() - mParentLayout->spacing(); else p2.rx() -= mCachedSize.width() - mParentLayout->spacing(); if ( mLayoutIsAtTopPosition ) { p1.ry() += mCachedSize.height() - mParentLayout->spacing() - 1; p2.ry() -= 1; } else p2.ry() -= mCachedSize.height() - mParentLayout->spacing() - 1; //qDebug() << mLayoutIsAtTopPosition << mLayoutIsAtLeftPosition; //qDebug() << mRect; //qDebug() << mParentLayout->margin(); //qDebug() << QRect( p1, p2 ); const QPoint oldBrushOrigin( painter->brushOrigin() ); const QBrush oldBrush( painter->brush() ); const QPen oldPen( painter->pen() ); const QPointF newTopLeft( painter->deviceMatrix().map( p1 ) ); painter->setBrushOrigin( newTopLeft ); painter->setBrush( mCommonBrush ); painter->setPen( Qt::NoPen ); painter->drawRect( QRect( p1, p2 ) ); painter->setBrushOrigin( oldBrushOrigin ); painter->setBrush( oldBrush ); painter->setPen( oldPen ); } // debug code: #if 0 //qDebug() << "KChart::AutoSpacerLayoutItem::paint()"; if ( !mRect.isValid() ) return; painter->drawRect( mRect ); painter->drawLine( QPointF( mRect.topLeft(), mRect.bottomRight() ) ); painter->drawLine( QPointF( mRect.topRight(), mRect.bottomLeft() ) ); #endif } diff --git a/src/KChart/KChartLayoutItems.h b/src/KChart/KChartLayoutItems.h index 2437457..6130a43 100644 --- a/src/KChart/KChartLayoutItems.h +++ b/src/KChart/KChartLayoutItems.h @@ -1,476 +1,476 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTLAYOUTITEMS_H #define KCHARTLAYOUTITEMS_H #include #include #include #include #include #include #include "KChartTextAttributes.h" #include "KChartMarkerAttributes.h" QT_BEGIN_NAMESPACE class QPainter; class KTextDocument; QT_END_NAMESPACE // TODO remove QRectF rotatedRect( const QRectF& pt, qreal rotation ); namespace KChart { class AbstractDiagram; class PaintContext; /** * Base class for all layout items of KChart * \internal */ class KCHART_EXPORT AbstractLayoutItem : public QLayoutItem { public: AbstractLayoutItem( Qt::Alignment itemAlignment = Qt::Alignment() ) : QLayoutItem( itemAlignment ), - mParent( 0 ), - mParentLayout( 0 ) {} + mParent( nullptr ), + mParentLayout( nullptr ) {} /** * Default impl: just call paint. * * Derived classes like KChart::AbstractArea are providing * additional action here. */ virtual void paintAll( QPainter& painter ); virtual void paint( QPainter* ) = 0; virtual void paintCtx( PaintContext* context ); virtual void setParentWidget( QWidget* widget ); virtual void sizeHintChanged() const; void setParentLayout( QLayout* lay ) { mParentLayout = lay; } QLayout* parentLayout() { return mParentLayout; } void removeFromParentLayout() { if ( mParentLayout ) { if ( widget() ) mParentLayout->removeWidget( widget() ); else mParentLayout->removeItem( this ); } } protected: QWidget* mParent; QLayout* mParentLayout; }; /** * Layout item showing a text *\internal */ class KCHART_EXPORT TextLayoutItem : public AbstractLayoutItem { public: TextLayoutItem(); TextLayoutItem( const QString& text, const TextAttributes& attributes, const QObject* autoReferenceArea, KChartEnums::MeasureOrientation autoReferenceOrientation, Qt::Alignment alignment = Qt::Alignment() ); void setAutoReferenceArea( const QObject* area ); const QObject* autoReferenceArea() const; void setText(const QString & text); QString text() const; void setTextAlignment( Qt::Alignment ); Qt::Alignment textAlignment() const; void setTextAttributes( const TextAttributes& a ); TextAttributes textAttributes() const; /** pure virtual in QLayoutItem */ bool isEmpty() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize maximumSize() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize minimumSize() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize sizeHint() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QRect geometry() const Q_DECL_OVERRIDE; virtual int marginWidth() const; virtual QSize sizeHintUnrotated() const; virtual bool intersects( const TextLayoutItem& other, const QPointF& myPos, const QPointF& otherPos ) const; virtual bool intersects( const TextLayoutItem& other, const QPoint& myPos, const QPoint& otherPos ) const; virtual qreal realFontSize() const; virtual QFont realFont() const; void paint( QPainter* ) Q_DECL_OVERRIDE; QPolygon boundingPolygon() const; private: bool maybeUpdateRealFont() const; QSize unrotatedSizeHint( const QFont& fnt = QFont() ) const; QSize unrotatedTextSize( QFont fnt = QFont() ) const; QSize calcSizeHint( const QFont& font ) const; int marginWidth( const QSize& textSize ) const; qreal fitFontSizeToGeometry() const; QRect mRect; QString mText; Qt::Alignment mTextAlignment; TextAttributes mAttributes; const QObject* mAutoReferenceArea; KChartEnums::MeasureOrientation mAutoReferenceOrientation; mutable QSize cachedSizeHint; mutable QPolygon mCachedBoundingPolygon; mutable qreal cachedFontSize; mutable QFont cachedFont; }; class KCHART_EXPORT TextBubbleLayoutItem : public AbstractLayoutItem { public: TextBubbleLayoutItem(); TextBubbleLayoutItem( const QString& text, const TextAttributes& attributes, const QObject* autoReferenceArea, KChartEnums::MeasureOrientation autoReferenceOrientation, Qt::Alignment alignment = Qt::Alignment() ); ~TextBubbleLayoutItem(); void setAutoReferenceArea( const QObject* area ); const QObject* autoReferenceArea() const; void setText(const QString & text); QString text() const; void setTextAttributes( const TextAttributes& a ); TextAttributes textAttributes() const; /** pure virtual in QLayoutItem */ bool isEmpty() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize maximumSize() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize minimumSize() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QSize sizeHint() const Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; /** pure virtual in QLayoutItem */ QRect geometry() const Q_DECL_OVERRIDE; void paint( QPainter* painter ) Q_DECL_OVERRIDE; protected: int borderWidth() const; private: TextLayoutItem* const m_text; }; /** * Layout item showing a data point marker * \internal */ class KCHART_EXPORT MarkerLayoutItem : public AbstractLayoutItem { public: MarkerLayoutItem( AbstractDiagram* diagram, const MarkerAttributes& marker, const QBrush& brush, const QPen& pen, Qt::Alignment alignment = Qt::Alignment() ); Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; QRect geometry() const Q_DECL_OVERRIDE; bool isEmpty() const Q_DECL_OVERRIDE; QSize maximumSize() const Q_DECL_OVERRIDE; QSize minimumSize() const Q_DECL_OVERRIDE; void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; QSize sizeHint() const Q_DECL_OVERRIDE; void paint( QPainter* ) Q_DECL_OVERRIDE; static void paintIntoRect( QPainter* painter, const QRect& rect, AbstractDiagram* diagram, const MarkerAttributes& marker, const QBrush& brush, const QPen& pen ); private: AbstractDiagram* mDiagram; QRect mRect; MarkerAttributes mMarker; QBrush mBrush; QPen mPen; }; /** * Layout item showing a coloured line * \internal */ class KCHART_EXPORT LineLayoutItem : public AbstractLayoutItem { public: LineLayoutItem( AbstractDiagram* diagram, int length, const QPen& pen, Qt::Alignment mLegendLineSymbolAlignment, Qt::Alignment alignment = Qt::Alignment() ); Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; QRect geometry() const Q_DECL_OVERRIDE; bool isEmpty() const Q_DECL_OVERRIDE; QSize maximumSize() const Q_DECL_OVERRIDE; QSize minimumSize() const Q_DECL_OVERRIDE; void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; QSize sizeHint() const Q_DECL_OVERRIDE; void setLegendLineSymbolAlignment(Qt::Alignment legendLineSymbolAlignment); virtual Qt::Alignment legendLineSymbolAlignment() const; void paint( QPainter* ) Q_DECL_OVERRIDE; static void paintIntoRect( QPainter* painter, const QRect& rect, const QPen& pen, Qt::Alignment lineAlignment); private: AbstractDiagram* mDiagram; //TODO: not used. remove it int mLength; QPen mPen; QRect mRect; Qt::Alignment mLegendLineSymbolAlignment; }; /** * Layout item showing a coloured line and a data point marker * \internal */ class KCHART_EXPORT LineWithMarkerLayoutItem : public AbstractLayoutItem { public: LineWithMarkerLayoutItem( AbstractDiagram* diagram, int lineLength, const QPen& linePen, int markerOffs, const MarkerAttributes& marker, const QBrush& markerBrush, const QPen& markerPen, Qt::Alignment alignment = Qt::Alignment() ); Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; QRect geometry() const Q_DECL_OVERRIDE; bool isEmpty() const Q_DECL_OVERRIDE; QSize maximumSize() const Q_DECL_OVERRIDE; QSize minimumSize() const Q_DECL_OVERRIDE; void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; QSize sizeHint() const Q_DECL_OVERRIDE; void paint( QPainter* ) Q_DECL_OVERRIDE; private: AbstractDiagram* mDiagram; QRect mRect; int mLineLength; QPen mLinePen; int mMarkerOffs; MarkerAttributes mMarker; QBrush mMarkerBrush; QPen mMarkerPen; }; /** * Layout item showing a horizontal line * \internal */ class KCHART_EXPORT HorizontalLineLayoutItem : public AbstractLayoutItem { public: HorizontalLineLayoutItem(); Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; QRect geometry() const Q_DECL_OVERRIDE; bool isEmpty() const Q_DECL_OVERRIDE; QSize maximumSize() const Q_DECL_OVERRIDE; QSize minimumSize() const Q_DECL_OVERRIDE; void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; QSize sizeHint() const Q_DECL_OVERRIDE; void paint( QPainter* ) Q_DECL_OVERRIDE; private: QRect mRect; }; /** * Layout item showing a vertial line * \internal */ class KCHART_EXPORT VerticalLineLayoutItem : public AbstractLayoutItem { public: VerticalLineLayoutItem(); Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; QRect geometry() const Q_DECL_OVERRIDE; bool isEmpty() const Q_DECL_OVERRIDE; QSize maximumSize() const Q_DECL_OVERRIDE; QSize minimumSize() const Q_DECL_OVERRIDE; void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; QSize sizeHint() const Q_DECL_OVERRIDE; void paint( QPainter* ) Q_DECL_OVERRIDE; private: QRect mRect; }; /** * @brief An empty layout item * \internal * * The AutoSpacerLayoutItem is automatically put into each corner cell of * the planeLayout grid: one of its reference-layouts is a QVBoxLayout (for * the top, or bottom axes resp.), the other one is a QHBoxLayout (for the * left/right sided axes). * * The spacer reserves enough space so all of the AbstractAreas contained * in the two reference-layouts can display not only their in-bounds * content but also their overlapping content reaching out of their area. * * KChart's layouting is applying this schema: \verbatim +------------------+-------------------------+-----------------+ | +--------------+ | +---------------------+ | +-------------+ | | | | | | QVBoxLayout for | | | | | | | AUTO | | | the top axis/axes | | | AUTO | | | | SPACER | | +---------------------+ | | SPACER | | | | ITEM | | | | | | ITEM | | | | | | | | | | | | | +--------------+ | +---------------------+ | +-------------+ | +------------------+-------------------------+-----------------+ | +--------+-----+ | +---------------------+ | +-------+-----+ | | | | | | | | | | | | | | | | | | | | | | | | | | | QHBox- | | | | | | | Right | | | | | Layout | | | | | | | | | | | | | | | | | | | axes | | | | | for | | | | | | | | | | | | | | | | | | | layout| | | | | the | | | | DIAGRAM(s) | | | | | | | | | | | | | | | | | | | | left | | | | | | | | | | | | | | | | | | | | | | | | axis | | | | | | | | | | | | or | | | | | | | | | | | | axes | | | | | | | | | | | | | | | | | | | | | | | +--------+-----+ | +---------------------+ | +-------+-----+ | +------------------+-------------------------+-----------------+ | +--------------+ | +---------------------+ | +-------------+ | | | | | | QVBoxLayout for | | | | | | | AUTO | | | the bottom axes | | | AUTO | | | | SPACER | | +---------------------+ | | SPACER | | | | ITEM | | | | | | ITEM | | | | | | | | | | | | | +--------------+ | +---------------------+ | +-------------+ | +------------------+-------------------------+-----------------+ \endverbatim * * A typical use case is an Abscissa axis with long labels: \verbatim 2 -| | 1 -| | 0 -+------------------------------------ | | | | | Monday Tuesday Wednesday Thursday Friday \endverbatim * The last letters of the word "Friday" would have been * cut off in previous versions of KChart - that is * if you did not call KChart::Chart::setGlobalLeading(). * * Now the word will be shown completely because there * is an auto-spacer-item taking care for the additional * space needed in the lower/right corner. */ class KCHART_EXPORT AutoSpacerLayoutItem : public AbstractLayoutItem { public: AutoSpacerLayoutItem( bool layoutIsAtTopPosition, QHBoxLayout *rightLeftLayout, bool layoutIsAtLeftPosition, QVBoxLayout *topBottomLayout ); Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; QRect geometry() const Q_DECL_OVERRIDE; bool isEmpty() const Q_DECL_OVERRIDE; QSize maximumSize() const Q_DECL_OVERRIDE; QSize minimumSize() const Q_DECL_OVERRIDE; void setGeometry( const QRect& r ) Q_DECL_OVERRIDE; QSize sizeHint() const Q_DECL_OVERRIDE; void paint( QPainter* ) Q_DECL_OVERRIDE; private: QRect mRect; bool mLayoutIsAtTopPosition; QHBoxLayout *mRightLeftLayout; bool mLayoutIsAtLeftPosition; QVBoxLayout *mTopBottomLayout; mutable QBrush mCommonBrush; mutable QSize mCachedSize; }; } #endif /* KCHARTLAYOUTITEMS_H */ diff --git a/src/KChart/KChartLegend.cpp b/src/KChart/KChartLegend.cpp index a964f47..9623f1f 100644 --- a/src/KChart/KChartLegend.cpp +++ b/src/KChart/KChartLegend.cpp @@ -1,1259 +1,1259 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartLegend.h" #include "KChartLegend_p.h" #include #include #include #include #include "KTextDocument.h" #include #include "KChartLayoutItems.h" #include #include #include #include #include #include #include #include #include #include #include using namespace KChart; Legend::Private::Private() : - referenceArea( 0 ), + referenceArea( nullptr ), position( Position::East ), alignment( Qt::AlignCenter ), textAlignment( Qt::AlignCenter ), relativePosition( RelativePosition() ), orientation( Qt::Vertical ), order( Qt::AscendingOrder ), showLines( false ), titleText( QObject::tr( "Legend" ) ), spacing( 1 ), useAutomaticMarkerSize( true ), legendStyle( MarkersOnly ) { // By default we specify a simple, hard point as the 'relative' position's ref. point, // since we can not be sure that there will be any parent specified for the legend. relativePosition.setReferencePoints( PositionPoints( QPointF( 0.0, 0.0 ) ) ); relativePosition.setReferencePosition( Position::NorthWest ); relativePosition.setAlignment( Qt::AlignTop | Qt::AlignLeft ); relativePosition.setHorizontalPadding( Measure( 4.0, KChartEnums::MeasureCalculationModeAbsolute ) ); relativePosition.setVerticalPadding( Measure( 4.0, KChartEnums::MeasureCalculationModeAbsolute ) ); } Legend::Private::~Private() { // this bloc left empty intentionally } #define d d_func() Legend::Legend( QWidget* parent ) : AbstractAreaWidget( new Private(), parent ) { d->referenceArea = parent; init(); } Legend::Legend( AbstractDiagram* diagram, QWidget* parent ) : AbstractAreaWidget( new Private(), parent ) { d->referenceArea = parent; init(); setDiagram( diagram ); } Legend::~Legend() { emit destroyedLegend( this ); } void Legend::init() { setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); d->layout = new QGridLayout( this ); d->layout->setMargin( 2 ); d->layout->setSpacing( d->spacing ); const Measure normalFontSizeTitle( 12, KChartEnums::MeasureCalculationModeAbsolute ); const Measure normalFontSizeLabels( 10, KChartEnums::MeasureCalculationModeAbsolute ); const Measure minimalFontSize( 4, KChartEnums::MeasureCalculationModeAbsolute ); TextAttributes textAttrs; textAttrs.setPen( QPen( Qt::black ) ); textAttrs.setFont( QFont( QLatin1String( "helvetica" ), 10, QFont::Normal, false ) ); textAttrs.setFontSize( normalFontSizeLabels ); textAttrs.setMinimalFontSize( minimalFontSize ); setTextAttributes( textAttrs ); TextAttributes titleTextAttrs; titleTextAttrs.setPen( QPen( Qt::black ) ); titleTextAttrs.setFont( QFont( QLatin1String( "helvetica" ), 12, QFont::Bold, false ) ); titleTextAttrs.setFontSize( normalFontSizeTitle ); titleTextAttrs.setMinimalFontSize( minimalFontSize ); setTitleTextAttributes( titleTextAttrs ); FrameAttributes frameAttrs; frameAttrs.setVisible( true ); frameAttrs.setPen( QPen( Qt::black ) ); frameAttrs.setPadding( 1 ); setFrameAttributes( frameAttrs ); d->position = Position::NorthEast; d->alignment = Qt::AlignCenter; } QSize Legend::minimumSizeHint() const { return sizeHint(); } //#define DEBUG_LEGEND_PAINT QSize Legend::sizeHint() const { #ifdef DEBUG_LEGEND_PAINT qDebug() << "Legend::sizeHint() started"; #endif Q_FOREACH( AbstractLayoutItem* paintItem, d->paintItems ) { paintItem->sizeHint(); } return AbstractAreaWidget::sizeHint(); } void Legend::needSizeHint() { buildLegend(); } void Legend::resizeLayout( const QSize& size ) { #ifdef DEBUG_LEGEND_PAINT qDebug() << "Legend::resizeLayout started"; #endif if ( d->layout ) { d->reflowHDatasetItems( this ); d->layout->setGeometry( QRect(QPoint( 0,0 ), size) ); activateTheLayout(); } #ifdef DEBUG_LEGEND_PAINT qDebug() << "Legend::resizeLayout done"; #endif } void Legend::activateTheLayout() { if ( d->layout && d->layout->parent() ) { d->layout->activate(); } } void Legend::setLegendStyle( LegendStyle style ) { if ( d->legendStyle == style ) { return; } d->legendStyle = style; setNeedRebuild(); } Legend::LegendStyle Legend::legendStyle() const { return d->legendStyle; } /** * Creates an exact copy of this legend. */ Legend* Legend::clone() const { - Legend* legend = new Legend( new Private( *d ), 0 ); + Legend* legend = new Legend( new Private( *d ), nullptr ); legend->setTextAttributes( textAttributes() ); legend->setTitleTextAttributes( titleTextAttributes() ); legend->setFrameAttributes( frameAttributes() ); legend->setUseAutomaticMarkerSize( useAutomaticMarkerSize() ); legend->setPosition( position() ); legend->setAlignment( alignment() ); legend->setTextAlignment( textAlignment() ); legend->setLegendStyle( legendStyle() ); return legend; } bool Legend::compare( const Legend* other ) const { if ( other == this ) { return true; } if ( !other ) { return false; } return ( AbstractAreaBase::compare( other ) ) && (isVisible() == other->isVisible()) && (position() == other->position()) && (alignment() == other->alignment())&& (textAlignment() == other->textAlignment())&& (floatingPosition() == other->floatingPosition()) && (orientation() == other->orientation())&& (showLines() == other->showLines())&& (texts() == other->texts())&& (brushes() == other->brushes())&& (pens() == other->pens())&& (markerAttributes() == other->markerAttributes())&& (useAutomaticMarkerSize() == other->useAutomaticMarkerSize()) && (textAttributes() == other->textAttributes()) && (titleText() == other->titleText())&& (titleTextAttributes() == other->titleTextAttributes()) && (spacing() == other->spacing()) && (legendStyle() == other->legendStyle()); } void Legend::paint( QPainter* painter ) { #ifdef DEBUG_LEGEND_PAINT qDebug() << "entering Legend::paint( QPainter* painter )"; #endif if ( !diagram() ) { return; } activateTheLayout(); Q_FOREACH( AbstractLayoutItem* paintItem, d->paintItems ) { paintItem->paint( painter ); } #ifdef DEBUG_LEGEND_PAINT qDebug() << "leaving Legend::paint( QPainter* painter )"; #endif } uint Legend::datasetCount() const { int modelLabelsCount = 0; Q_FOREACH ( DiagramObserver* observer, d->observers ) { AbstractDiagram* diagram = observer->diagram(); Q_ASSERT( diagram->datasetLabels().count() == diagram->datasetBrushes().count() ); modelLabelsCount += diagram->datasetLabels().count(); } return modelLabelsCount; } void Legend::setReferenceArea( const QWidget* area ) { if ( area == d->referenceArea ) { return; } d->referenceArea = area; setNeedRebuild(); } const QWidget* Legend::referenceArea() const { return d->referenceArea ? d->referenceArea : qobject_cast< const QWidget* >( parent() ); } AbstractDiagram* Legend::diagram() const { if ( d->observers.isEmpty() ) { - return 0; + return nullptr; } return d->observers.first()->diagram(); } DiagramList Legend::diagrams() const { DiagramList list; for ( int i = 0; i < d->observers.size(); ++i ) { list << d->observers.at(i)->diagram(); } return list; } ConstDiagramList Legend::constDiagrams() const { ConstDiagramList list; for ( int i = 0; i < d->observers.size(); ++i ) { list << d->observers.at(i)->diagram(); } return list; } void Legend::addDiagram( AbstractDiagram* newDiagram ) { if ( newDiagram ) { DiagramObserver* observer = new DiagramObserver( newDiagram, this ); DiagramObserver* oldObs = d->findObserverForDiagram( newDiagram ); if ( oldObs ) { delete oldObs; d->observers[ d->observers.indexOf( oldObs ) ] = observer; } else { d->observers.append( observer ); } connect( observer, SIGNAL(diagramAboutToBeDestroyed(AbstractDiagram*)), SLOT(resetDiagram(AbstractDiagram*))); connect( observer, SIGNAL(diagramDataChanged(AbstractDiagram*)), SLOT(setNeedRebuild())); connect( observer, SIGNAL(diagramDataHidden(AbstractDiagram*)), SLOT(setNeedRebuild())); connect( observer, SIGNAL(diagramAttributesChanged(AbstractDiagram*)), SLOT(setNeedRebuild())); setNeedRebuild(); } } void Legend::removeDiagram( AbstractDiagram* oldDiagram ) { int datasetBrushOffset = 0; QList< AbstractDiagram * > diagrams = this->diagrams(); for ( int i = 0; i datasetBrushes().count(); i++ ) { d->brushes.remove(datasetBrushOffset + i); d->texts.remove(datasetBrushOffset + i); } for ( int i = 0; i < oldDiagram->datasetPens().count(); i++ ) { d->pens.remove(datasetBrushOffset + i); } break; } datasetBrushOffset += diagrams.at(i)->datasetBrushes().count(); } if ( oldDiagram ) { DiagramObserver *oldObs = d->findObserverForDiagram( oldDiagram ); if ( oldObs ) { delete oldObs; d->observers.removeAt( d->observers.indexOf( oldObs ) ); } setNeedRebuild(); } } void Legend::removeDiagrams() { // removeDiagram() may change the d->observers list. So, build up the list of // diagrams to remove first and then remove them one by one. QList< AbstractDiagram * > diagrams; for ( int i = 0; i < d->observers.size(); ++i ) { diagrams.append( d->observers.at( i )->diagram() ); } for ( int i = 0; i < diagrams.count(); ++i ) { removeDiagram( diagrams[ i ] ); } } void Legend::replaceDiagram( AbstractDiagram* newDiagram, AbstractDiagram* oldDiagram ) { AbstractDiagram* old = oldDiagram; if ( !d->observers.isEmpty() && !old ) { old = d->observers.first()->diagram(); if ( !old ) { d->observers.removeFirst(); // first entry had a 0 diagram } } if ( old ) { removeDiagram( old ); } if ( newDiagram ) { addDiagram( newDiagram ); } } uint Legend::dataSetOffset( AbstractDiagram* diagram ) { uint offset = 0; for ( int i = 0; i < d->observers.count(); ++i ) { if ( d->observers.at(i)->diagram() == diagram ) { return offset; } AbstractDiagram* diagram = d->observers.at(i)->diagram(); if ( !diagram->model() ) { continue; } offset = offset + diagram->model()->columnCount(); } return offset; } void Legend::setDiagram( AbstractDiagram* newDiagram ) { replaceDiagram( newDiagram ); } void Legend::resetDiagram( AbstractDiagram* oldDiagram ) { removeDiagram( oldDiagram ); } void Legend::setVisible( bool visible ) { // do NOT bail out if visible == isVisible(), because the return value of isVisible() also depends // on the visibility of the parent. QWidget::setVisible( visible ); emitPositionChanged(); } void Legend::setNeedRebuild() { buildLegend(); sizeHint(); } void Legend::setPosition( Position position ) { if ( d->position == position ) { return; } d->position = position; emitPositionChanged(); } void Legend::emitPositionChanged() { emit positionChanged( this ); emit propertiesChanged(); } Position Legend::position() const { return d->position; } void Legend::setAlignment( Qt::Alignment alignment ) { if ( d->alignment == alignment ) { return; } d->alignment = alignment; emitPositionChanged(); } Qt::Alignment Legend::alignment() const { return d->alignment; } void Legend::setTextAlignment( Qt::Alignment alignment ) { if ( d->textAlignment == alignment ) { return; } d->textAlignment = alignment; emitPositionChanged(); } Qt::Alignment Legend::textAlignment() const { return d->textAlignment; } void Legend::setLegendSymbolAlignment( Qt::Alignment alignment ) { if ( d->legendLineSymbolAlignment == alignment ) { return; } d->legendLineSymbolAlignment = alignment; emitPositionChanged(); } Qt::Alignment Legend::legendSymbolAlignment() const { return d->legendLineSymbolAlignment ; } void Legend::setFloatingPosition( const RelativePosition& relativePosition ) { d->position = Position::Floating; if ( d->relativePosition != relativePosition ) { d->relativePosition = relativePosition; emitPositionChanged(); } } const RelativePosition Legend::floatingPosition() const { return d->relativePosition; } void Legend::setOrientation( Qt::Orientation orientation ) { if ( d->orientation == orientation ) { return; } d->orientation = orientation; setNeedRebuild(); emitPositionChanged(); } Qt::Orientation Legend::orientation() const { return d->orientation; } void Legend::setSortOrder( Qt::SortOrder order ) { if ( d->order == order ) { return; } d->order = order; setNeedRebuild(); emitPositionChanged(); } Qt::SortOrder Legend::sortOrder() const { return d->order; } void Legend::setShowLines( bool legendShowLines ) { if ( d->showLines == legendShowLines ) { return; } d->showLines = legendShowLines; setNeedRebuild(); emitPositionChanged(); } bool Legend::showLines() const { return d->showLines; } void Legend::setUseAutomaticMarkerSize( bool useAutomaticMarkerSize ) { d->useAutomaticMarkerSize = useAutomaticMarkerSize; setNeedRebuild(); emitPositionChanged(); } bool Legend::useAutomaticMarkerSize() const { return d->useAutomaticMarkerSize; } /** \brief Removes all legend texts that might have been set by setText. This resets the Legend to default behaviour: Texts are created automatically. */ void Legend::resetTexts() { if ( !d->texts.count() ) { return; } d->texts.clear(); setNeedRebuild(); } void Legend::setText( uint dataset, const QString& text ) { if ( d->texts[ dataset ] == text ) { return; } d->texts[ dataset ] = text; setNeedRebuild(); } QString Legend::text( uint dataset ) const { if ( d->texts.find( dataset ) != d->texts.end() ) { return d->texts[ dataset ]; } else { return d->modelLabels[ dataset ]; } } const QMap Legend::texts() const { return d->texts; } void Legend::setColor( uint dataset, const QColor& color ) { if ( d->brushes[ dataset ] != color ) { d->brushes[ dataset ] = color; setNeedRebuild(); update(); } } void Legend::setBrush( uint dataset, const QBrush& brush ) { if ( d->brushes[ dataset ] != brush ) { d->brushes[ dataset ] = brush; setNeedRebuild(); update(); } } QBrush Legend::brush( uint dataset ) const { if ( d->brushes.contains( dataset ) ) { return d->brushes[ dataset ]; } else { return d->modelBrushes[ dataset ]; } } const QMap Legend::brushes() const { return d->brushes; } void Legend::setBrushesFromDiagram( AbstractDiagram* diagram ) { bool changed = false; QList datasetBrushes = diagram->datasetBrushes(); for ( int i = 0; i < datasetBrushes.count(); i++ ) { if ( d->brushes[ i ] != datasetBrushes[ i ] ) { d->brushes[ i ] = datasetBrushes[ i ]; changed = true; } } if ( changed ) { setNeedRebuild(); update(); } } void Legend::setPen( uint dataset, const QPen& pen ) { if ( d->pens[dataset] == pen ) { return; } d->pens[dataset] = pen; setNeedRebuild(); update(); } QPen Legend::pen( uint dataset ) const { if ( d->pens.find( dataset ) != d->pens.end() ) { return d->pens[ dataset ]; } else { return d->modelPens[ dataset ]; } } const QMap Legend::pens() const { return d->pens; } void Legend::setMarkerAttributes( uint dataset, const MarkerAttributes& markerAttributes ) { if ( d->markerAttributes[dataset] == markerAttributes ) { return; } d->markerAttributes[ dataset ] = markerAttributes; setNeedRebuild(); update(); } MarkerAttributes Legend::markerAttributes( uint dataset ) const { if ( d->markerAttributes.find( dataset ) != d->markerAttributes.end() ) { return d->markerAttributes[ dataset ]; } else if ( static_cast( d->modelMarkers.count() ) > dataset ) { return d->modelMarkers[ dataset ]; } else { return MarkerAttributes(); } } const QMap Legend::markerAttributes() const { return d->markerAttributes; } void Legend::setTextAttributes( const TextAttributes &a ) { if ( d->textAttributes == a ) { return; } d->textAttributes = a; setNeedRebuild(); } TextAttributes Legend::textAttributes() const { return d->textAttributes; } void Legend::setTitleText( const QString& text ) { if ( d->titleText == text ) { return; } d->titleText = text; setNeedRebuild(); } QString Legend::titleText() const { return d->titleText; } void Legend::setTitleTextAttributes( const TextAttributes &a ) { if ( d->titleTextAttributes == a ) { return; } d->titleTextAttributes = a; setNeedRebuild(); } TextAttributes Legend::titleTextAttributes() const { return d->titleTextAttributes; } void Legend::forceRebuild() { #ifdef DEBUG_LEGEND_PAINT qDebug() << "entering Legend::forceRebuild()"; #endif buildLegend(); #ifdef DEBUG_LEGEND_PAINT qDebug() << "leaving Legend::forceRebuild()"; #endif } void Legend::setSpacing( uint space ) { if ( d->spacing == space && d->layout->spacing() == int( space ) ) { return; } d->spacing = space; d->layout->setSpacing( space ); setNeedRebuild(); } uint Legend::spacing() const { return d->spacing; } void Legend::setDefaultColors() { Palette pal = Palette::defaultPalette(); for ( int i = 0; i < pal.size(); i++ ) { setBrush( i, pal.getBrush( i ) ); } } void Legend::setRainbowColors() { Palette pal = Palette::rainbowPalette(); for ( int i = 0; i < pal.size(); i++ ) { setBrush( i, pal.getBrush( i ) ); } } void Legend::setSubduedColors( bool ordered ) { Palette pal = Palette::subduedPalette(); if ( ordered ) { for ( int i = 0; i < pal.size(); i++ ) { setBrush( i, pal.getBrush( i ) ); } } else { static const int s_subduedColorsCount = 18; Q_ASSERT( pal.size() >= s_subduedColorsCount ); static const int order[ s_subduedColorsCount ] = { 0, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14, 1, 6, 11, 16, 3, 8, 13 }; for ( int i = 0; i < s_subduedColorsCount; i++ ) { setBrush( i, pal.getBrush( order[i] ) ); } } } void Legend::resizeEvent( QResizeEvent * event ) { Q_UNUSED( event ); #ifdef DEBUG_LEGEND_PAINT qDebug() << "Legend::resizeEvent() called"; #endif forceRebuild(); sizeHint(); QTimer::singleShot( 0, this, SLOT(emitPositionChanged()) ); } void Legend::Private::fetchPaintOptions( Legend *q ) { modelLabels.clear(); modelBrushes.clear(); modelPens.clear(); modelMarkers.clear(); // retrieve the diagrams' settings for all non-hidden datasets for ( int i = 0; i < observers.size(); ++i ) { const AbstractDiagram* diagram = observers.at( i )->diagram(); if ( !diagram ) { continue; } const QStringList diagramLabels = diagram->datasetLabels(); const QList diagramBrushes = diagram->datasetBrushes(); const QList diagramPens = diagram->datasetPens(); const QList diagramMarkers = diagram->datasetMarkers(); const bool ascend = q->sortOrder() == Qt::AscendingOrder; int dataset = ascend ? 0 : diagramLabels.count() - 1; const int end = ascend ? diagramLabels.count() : -1; for ( ; dataset != end; dataset += ascend ? 1 : -1 ) { if ( diagram->isHidden( dataset ) || q->datasetIsHidden( dataset ) ) { continue; } modelLabels += diagramLabels[ dataset ]; modelBrushes += diagramBrushes[ dataset ]; modelPens += diagramPens[ dataset ]; modelMarkers += diagramMarkers[ dataset ]; } } Q_ASSERT( modelLabels.count() == modelBrushes.count() ); } QSizeF Legend::Private::markerSize( Legend *q, int dataset, qreal fontHeight ) const { QSizeF suppliedSize = q->markerAttributes( dataset ).markerSize(); if ( q->useAutomaticMarkerSize() || !suppliedSize.isValid() ) { return QSizeF( fontHeight, fontHeight ); } else { return suppliedSize; } } QSizeF Legend::Private::maxMarkerSize( Legend *q, qreal fontHeight ) const { QSizeF ret( 1.0, 1.0 ); if ( q->legendStyle() != LinesOnly ) { for ( int dataset = 0; dataset < modelLabels.count(); ++dataset ) { ret = ret.expandedTo( markerSize( q, dataset, fontHeight ) ); } } return ret; } HDatasetItem::HDatasetItem() - : markerLine(0), - label(0), - separatorLine(0), - spacer(0) + : markerLine(nullptr), + label(nullptr), + separatorLine(nullptr), + spacer(nullptr) {} static void updateToplevelLayout(QWidget *w) { while ( w ) { if ( w->isTopLevel() ) { // The null check has proved necessary during destruction of the Legend / Chart if ( w->layout() ) { w->layout()->update(); } break; } else { w = qobject_cast< QWidget * >( w->parent() ); Q_ASSERT( w ); } } } void Legend::buildLegend() { /* Grid layout partitioning (horizontal orientation): row zero is the title, row one the divider line between title and dataset items, row two for each item: line, marker, text label and separator line in that order. In a vertically oriented legend, row pairs (2, 3), ... contain a possible separator line (first row) and (second row) line, marker, text label each. */ d->destroyOldLayout(); if ( orientation() == Qt::Vertical ) { d->layout->setColumnStretch( 6, 1 ); } else { d->layout->setColumnStretch( 6, 0 ); } d->fetchPaintOptions( this ); const KChartEnums::MeasureOrientation measureOrientation = orientation() == Qt::Vertical ? KChartEnums::MeasureOrientationMinimum : KChartEnums::MeasureOrientationHorizontal; // legend caption if ( !titleText().isEmpty() && titleTextAttributes().isVisible() ) { TextLayoutItem* titleItem = new TextLayoutItem( titleText(), titleTextAttributes(), referenceArea(), measureOrientation, d->textAlignment ); titleItem->setParentWidget( this ); d->paintItems << titleItem; d->layout->addItem( titleItem, 0, 0, 1, 5, Qt::AlignCenter ); // The line between the title and the legend items, if any. if ( showLines() && d->modelLabels.count() ) { HorizontalLineLayoutItem* lineItem = new HorizontalLineLayoutItem; d->paintItems << lineItem; d->layout->addItem( lineItem, 1, 0, 1, 5, Qt::AlignCenter ); } } qreal fontHeight = textAttributes().calculatedFontSize( referenceArea(), measureOrientation ); { QFont tmpFont = textAttributes().font(); tmpFont.setPointSizeF( fontHeight ); if ( GlobalMeasureScaling::paintDevice() ) { fontHeight = QFontMetricsF( tmpFont, GlobalMeasureScaling::paintDevice() ).height(); } else { fontHeight = QFontMetricsF( tmpFont ).height(); } } const QSizeF maxMarkerSize = d->maxMarkerSize( this, fontHeight ); // If we show a marker on a line, we paint it after 8 pixels // of the line have been painted. This allows to see the line style // at the right side of the marker without the line needing to // be too long. // (having the marker in the middle of the line would require longer lines) const int lineLengthLeftOfMarker = 8; int maxLineLength = 18; { bool hasComplexPenStyle = false; for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ) { const QPen pn = pen( dataset ); const Qt::PenStyle ps = pn.style(); if ( ps != Qt::NoPen ) { maxLineLength = qMin( pn.width() * 18, maxLineLength ); if ( ps != Qt::SolidLine ) { hasComplexPenStyle = true; } } } if ( legendStyle() != LinesOnly ) { if ( hasComplexPenStyle ) maxLineLength += lineLengthLeftOfMarker; maxLineLength += int( maxMarkerSize.width() ); } } // for all datasets: add (line)marker items and text items to the layout; // actual layout happens in flowHDatasetItems() for horizontal layout, here for vertical for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ) { const int vLayoutRow = 2 + dataset * 2; HDatasetItem dsItem; // It is possible to set the marker brush through markerAttributes as well as // the dataset brush set in the diagram - the markerAttributes have higher precedence. MarkerAttributes markerAttrs = markerAttributes( dataset ); markerAttrs.setMarkerSize( d->markerSize( this, dataset, fontHeight ) ); const QBrush markerBrush = markerAttrs.markerColor().isValid() ? QBrush( markerAttrs.markerColor() ) : brush( dataset ); switch ( legendStyle() ) { case MarkersOnly: dsItem.markerLine = new MarkerLayoutItem( diagram(), markerAttrs, markerBrush, markerAttrs.pen(), Qt::AlignLeft | Qt::AlignVCenter ); break; case LinesOnly: dsItem.markerLine = new LineLayoutItem( diagram(), maxLineLength, pen( dataset ), d->legendLineSymbolAlignment, Qt::AlignCenter ); break; case MarkersAndLines: dsItem.markerLine = new LineWithMarkerLayoutItem( diagram(), maxLineLength, pen( dataset ), lineLengthLeftOfMarker, markerAttrs, markerBrush, markerAttrs.pen(), Qt::AlignCenter ); break; default: Q_ASSERT( false ); } dsItem.label = new TextLayoutItem( text( dataset ), textAttributes(), referenceArea(), measureOrientation, d->textAlignment ); dsItem.label->setParentWidget( this ); // horizontal layout is deferred to flowDatasetItems() if ( orientation() == Qt::Horizontal ) { d->hLayoutDatasets << dsItem; continue; } // (actual) vertical layout here if ( dsItem.markerLine ) { d->layout->addItem( dsItem.markerLine, vLayoutRow, 1, 1, 1, Qt::AlignCenter ); d->paintItems << dsItem.markerLine; } d->layout->addItem( dsItem.label, vLayoutRow, 3, 1, 1, Qt::AlignLeft | Qt::AlignVCenter ); d->paintItems << dsItem.label; // horizontal separator line, only between items if ( showLines() && dataset != d->modelLabels.count() - 1 ) { HorizontalLineLayoutItem* lineItem = new HorizontalLineLayoutItem; d->layout->addItem( lineItem, vLayoutRow + 1, 0, 1, 5, Qt::AlignCenter ); d->paintItems << lineItem; } } if ( orientation() == Qt::Horizontal ) { d->flowHDatasetItems( this ); } // vertical line (only in vertical mode) if ( orientation() == Qt::Vertical && showLines() && d->modelLabels.count() ) { VerticalLineLayoutItem* lineItem = new VerticalLineLayoutItem; d->paintItems << lineItem; d->layout->addItem( lineItem, 2, 2, d->modelLabels.count() * 2, 1 ); } updateToplevelLayout( this ); emit propertiesChanged(); #ifdef DEBUG_LEGEND_PAINT qDebug() << "leaving Legend::buildLegend()"; #endif } int HDatasetItem::height() const { return qMax( markerLine->sizeHint().height(), label->sizeHint().height() ); } void Legend::Private::reflowHDatasetItems( Legend *q ) { if (hLayoutDatasets.isEmpty()) { return; } paintItems.clear(); // Dissolve exactly the QHBoxLayout(s) created as "currentLine" in flowHDatasetItems - don't remove the // caption and line under the caption! Those are easily identified because they aren't QLayouts. for ( int i = layout->count() - 1; i >= 0; i-- ) { QLayoutItem *const item = layout->itemAt( i ); QLayout *const hbox = item->layout(); if ( !hbox ) { AbstractLayoutItem *alItem = dynamic_cast< AbstractLayoutItem * >( item ); Q_ASSERT( alItem ); paintItems << alItem; continue; } Q_ASSERT( dynamic_cast< QHBoxLayout * >( hbox ) ); layout->takeAt( i ); // detach children so they aren't deleted with the parent for ( int j = hbox->count() - 1; j >= 0; j-- ) { hbox->takeAt( j ); } delete hbox; } flowHDatasetItems( q ); } // this works pretty much like flow layout for text, and it is only applicable to dataset items // laid out horizontally void Legend::Private::flowHDatasetItems( Legend *q ) { const int separatorLineWidth = 3; // hardcoded in VerticalLineLayoutItem::sizeHint() const int allowedWidth = q->areaGeometry().width(); QHBoxLayout *currentLine = new QHBoxLayout; int columnSpan = 5; int mainLayoutColumn = 0; int row = 0; if ( !titleText.isEmpty() && titleTextAttributes.isVisible() ) { ++row; if (q->showLines()){ ++row; } } layout->addItem( currentLine, row, mainLayoutColumn, /*rowSpan*/1 , columnSpan, Qt::AlignLeft | Qt::AlignVCenter ); mainLayoutColumn += columnSpan; for ( int dataset = 0; dataset < hLayoutDatasets.size(); dataset++ ) { HDatasetItem *hdsItem = &hLayoutDatasets[ dataset ]; bool spacerUsed = false; bool separatorUsed = false; if ( !currentLine->isEmpty() ) { const int separatorWidth = ( q->showLines() ? separatorLineWidth : 0 ) + q->spacing(); const int payloadWidth = hdsItem->markerLine->sizeHint().width() + hdsItem->label->sizeHint().width(); if ( currentLine->sizeHint().width() + separatorWidth + payloadWidth > allowedWidth ) { // too wide, "line break" #ifdef DEBUG_LEGEND_PAINT qDebug() << Q_FUNC_INFO << "break" << mainLayoutColumn << currentLine->sizeHint().width() << currentLine->sizeHint().width() + separatorWidth + payloadWidth << allowedWidth; #endif currentLine = new QHBoxLayout; layout->addItem( currentLine, row, mainLayoutColumn, /*rowSpan*/1 , columnSpan, Qt::AlignLeft | Qt::AlignVCenter ); mainLayoutColumn += columnSpan; } else { // > 1 dataset item in line, put spacing and maybe a separator between them if ( !hdsItem->spacer ) { hdsItem->spacer = new QSpacerItem( q->spacing(), 1 ); } currentLine->addItem( hdsItem->spacer ); spacerUsed = true; if ( q->showLines() ) { if ( !hdsItem->separatorLine ) { hdsItem->separatorLine = new VerticalLineLayoutItem; } paintItems << hdsItem->separatorLine; currentLine->addItem( hdsItem->separatorLine ); separatorUsed = true; } } } // those have no parents in the current layout, so they wouldn't get cleaned up otherwise if ( !spacerUsed ) { delete hdsItem->spacer; - hdsItem->spacer = 0; + hdsItem->spacer = nullptr; } if ( !separatorUsed ) { delete hdsItem->separatorLine; - hdsItem->separatorLine = 0; + hdsItem->separatorLine = nullptr; } currentLine->addItem( hdsItem->markerLine ); paintItems << hdsItem->markerLine; currentLine->addItem( hdsItem->label ); paintItems << hdsItem->label; } } bool Legend::hasHeightForWidth() const { // this is better than using orientation() because, for layout purposes, we're not height-for-width // *yet* before buildLegend() has been called, and the layout logic might get upset if we say // something that will only be true in the future return !d->hLayoutDatasets.isEmpty(); } int Legend::heightForWidth( int width ) const { if ( d->hLayoutDatasets.isEmpty() ) { return -1; } int ret = 0; // space for caption and line under caption (if any) for (int i = 0; i < 2; i++) { if ( QLayoutItem *item = d->layout->itemAtPosition( i, 0 ) ) { ret += item->sizeHint().height(); } } const int separatorLineWidth = 3; // ### hardcoded in VerticalLineLayoutItem::sizeHint() int currentLineWidth = 0; int currentLineHeight = 0; Q_FOREACH( const HDatasetItem &hdsItem, d->hLayoutDatasets ) { const int payloadWidth = hdsItem.markerLine->sizeHint().width() + hdsItem.label->sizeHint().width(); if ( !currentLineWidth ) { // first iteration currentLineWidth = payloadWidth; } else { const int separatorWidth = ( showLines() ? separatorLineWidth : 0 ) + spacing(); currentLineWidth += separatorWidth + payloadWidth; if ( currentLineWidth > width ) { // too wide, "line break" #ifdef DEBUG_LEGEND_PAINT qDebug() << Q_FUNC_INFO << "heightForWidth break" << currentLineWidth << currentLineWidth + separatorWidth + payloadWidth << width; #endif ret += currentLineHeight + spacing(); currentLineWidth = payloadWidth; currentLineHeight = 0; } } currentLineHeight = qMax( currentLineHeight, hdsItem.height() ); } ret += currentLineHeight; // one less spacings than lines return ret; } void Legend::Private::destroyOldLayout() { // in the horizontal layout case, the QHBoxLayout destructor also deletes child layout items // (it isn't documented that QLayoutItems delete their children) for ( int i = layout->count() - 1; i >= 0; i-- ) { delete layout->takeAt( i ); } Q_ASSERT( !layout->count() ); hLayoutDatasets.clear(); paintItems.clear(); } void Legend::setHiddenDatasets( const QList hiddenDatasets ) { d->hiddenDatasets = hiddenDatasets; } const QList Legend::hiddenDatasets() const { return d->hiddenDatasets; } void Legend::setDatasetHidden( uint dataset, bool hidden ) { if ( hidden && !d->hiddenDatasets.contains( dataset ) ) { d->hiddenDatasets.append( dataset ); } else if ( !hidden && d->hiddenDatasets.contains( dataset ) ) { d->hiddenDatasets.removeAll( dataset ); } } bool Legend::datasetIsHidden( uint dataset ) const { return d->hiddenDatasets.contains( dataset ); } diff --git a/src/KChart/KChartLegend.h b/src/KChart/KChartLegend.h index a4ed75d..6ed09be 100644 --- a/src/KChart/KChartLegend.h +++ b/src/KChart/KChartLegend.h @@ -1,408 +1,408 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTLEGEND_H #define KCHARTLEGEND_H #include "KChartAbstractAreaWidget.h" #include "KChartPosition.h" #include "KChartMarkerAttributes.h" class QTextTable; namespace KChart { class AbstractDiagram; typedef QList DiagramList; typedef QList ConstDiagramList; /** * @brief Legend defines the interface for the legend drawing class. * * Legend is the class for drawing legends for all kinds of diagrams ("chart types"). * * Legend is drawn on chart level, not per diagram, but you can have more than one * legend per chart, using KChart::Chart::addLegend(). * * \note Legend is different from all other classes ofd KChart, since it can be * displayed outside of the Chart's area. If you want to, you can embedd the legend * into your own widget, or into another part of a bigger layout, into which you might * have inserted the Chart. * * On the other hand, please note that you MUST call Chart::addLegend to get your * legend positioned into the correct place of your chart - if you want to have * the legend shown inside of the chart (that's probably true for most cases). */ class KCHART_EXPORT Legend : public AbstractAreaWidget { Q_OBJECT Q_DISABLE_COPY( Legend ) KCHART_DECLARE_PRIVATE_DERIVED_QWIDGET( Legend ) public: - explicit Legend( QWidget* parent = 0 ); - explicit Legend( KChart::AbstractDiagram* diagram, QWidget* parent = 0 ); + explicit Legend( QWidget* parent = nullptr ); + explicit Legend( KChart::AbstractDiagram* diagram, QWidget* parent = nullptr ); virtual ~Legend(); enum LegendStyle { MarkersOnly = 0, LinesOnly = 1, MarkersAndLines = 2 }; void setLegendStyle( LegendStyle style ); LegendStyle legendStyle() const; virtual Legend * clone() const; /** * Returns true if both legends have the same settings. */ bool compare( const Legend* other ) const; void resizeEvent( QResizeEvent * event ) Q_DECL_OVERRIDE; // TODO: should be protected virtual void paint( QPainter* painter ) Q_DECL_OVERRIDE; void setVisible( bool visible ) Q_DECL_OVERRIDE; /** Specifies the reference area for font size of title text, and for font size of the item texts, IF automatic area detection is set. \note This parameter is ignored, if the Measure given for setTitleTextAttributes (or setTextAttributes, resp.) is not specifying automatic area detection. If no reference area is specified, but automatic area detection is set, then the size of the legend's parent widget will be used. \sa KChart::Measure, KChartEnums::MeasureCalculationMode */ void setReferenceArea( const QWidget* area ); /** Returns the reference area, that is used for font size of title text, and for font size of the item texts, IF automatic area detection is set. \sa setReferenceArea */ const QWidget* referenceArea() const; /** * The first diagram of the legend or 0 if there was none added to the legend. * @return The first diagram of the legend or 0. * * \sa diagrams, addDiagram, removeDiagram, removeDiagrams, replaceDiagram, setDiagram */ KChart::AbstractDiagram* diagram() const; /** * The list of all diagrams associated with the legend. * @return The list of all diagrams associated with the legend. * * \sa diagram, addDiagram, removeDiagram, removeDiagrams, replaceDiagram, setDiagram */ DiagramList diagrams() const; /** * @return The list of diagrams associated with this legend. */ ConstDiagramList constDiagrams() const; /** * Add the given diagram to the legend. * @param newDiagram The diagram to add. * * \sa diagram, diagrams, removeDiagram, removeDiagrams, replaceDiagram, setDiagram */ void addDiagram( KChart::AbstractDiagram* newDiagram ); /** * Removes the diagram from the legend's list of diagrams. * * \sa diagram, diagrams, addDiagram, removeDiagrams, replaceDiagram, setDiagram */ void removeDiagram( KChart::AbstractDiagram* oldDiagram ); /** * Removes all diagrams from the legend's list of diagrams. * * \sa diagram, diagrams, addDiagram, removeDiagram, replaceDiagram, setDiagram */ void removeDiagrams(); /** * Replaces the old diagram, or appends the * new diagram, it there is none yet. * * @param newDiagram The diagram to be used instead of the old one. * If this parameter is zero, the first diagram will just be removed. * * @param oldDiagram The diagram to be removed by the new one. This * diagram will be deleted automatically. If the parameter is omitted, * the very first diagram will be replaced. In case, there was no * diagram yet, the new diagram will just be added. * * \sa diagram, diagrams, addDiagram, removeDiagram, removeDiagrams, setDiagram */ void replaceDiagram( KChart::AbstractDiagram* newDiagram, - KChart::AbstractDiagram* oldDiagram = 0 ); + KChart::AbstractDiagram* oldDiagram = nullptr ); /** * Returns the offset of the first dataset of \c diagram. * */ uint dataSetOffset( KChart::AbstractDiagram* diagram ); /** * @brief A convenience method doing the same as replaceDiagram( newDiagram, 0 ); * * Replaces the first diagram by the given diagram. * If the legend's list of diagram is empty the given diagram is added to the list. * * \sa diagram, diagrams, addDiagram, removeDiagram, removeDiagrams, replaceDiagram */ void setDiagram( KChart::AbstractDiagram* newDiagram ); /** * \brief Specify the position of a non-floating legend. * * Use setFloatingPosition to set position and alignment * if your legend is floating. * * \sa setAlignment, setFloatingPosition */ void setPosition( Position position ); /** * Returns the position of a non-floating legend. * \sa setPosition */ Position position() const; /** * \brief Specify the alignment of a non-floating legend. * * Use setFloatingPosition to set position and alignment * if your legend is floating. * * \sa alignment, setPosition, setFloatingPosition */ void setAlignment( Qt::Alignment ); /** * Returns the alignment of a non-floating legend. * \sa setAlignment */ Qt::Alignment alignment() const; /** * \brief Specify the alignment of the text elements within the legend * * \sa textAlignment() */ void setTextAlignment( Qt::Alignment ); /** * \brief Returns the alignment used while rendering text elements within the legend. * * \sa setTextAlignment() */ Qt::Alignment textAlignment() const; /** * \brief Specify the alignment of the legend symbol( alignment of Legend::LinesOnly) * within the legend * * \sa legendSymbolAlignment() */ void setLegendSymbolAlignment(Qt::Alignment); /** * \brief Returns the alignment used while drawing legend symbol(alignment of Legend::LinesOnly) * within the legend. * * \sa setLegendSymbolAlignment() */ Qt::Alignment legendSymbolAlignment() const; /** * \brief Specify the position and alignment of a floating legend. * * Use setPosition and setAlignment to set position and alignment * if your legend is non-floating. * * \note When setFloatingPosition is called, the Legend's position value is set to * KChart::Position::Floating automatically. * * If your Chart is pointed to by m_chart, your could have the floating legend * aligned exactly to the chart's coordinate plane's top-right corner * with the following commands: \verbatim KChart::RelativePosition relativePosition; relativePosition.setReferenceArea( m_chart->coordinatePlane() ); relativePosition.setReferencePosition( Position::NorthEast ); relativePosition.setAlignment( Qt::AlignTop | Qt::AlignRight ); relativePosition.setHorizontalPadding( KChart::Measure( -1.0, KChartEnums::MeasureCalculationModeAbsolute ) ); relativePosition.setVerticalPadding( KChart::Measure( 0.0, KChartEnums::MeasureCalculationModeAbsolute ) ); m_legend->setFloatingPosition( relativePosition ); \endverbatim * * To have the legend positioned at a fixed point, measured from the QPainter's top left corner, * you could use the following code code: * \verbatim KChart::RelativePosition relativePosition; relativePosition.setReferencePoints( PositionPoints( QPointF( 0.0, 0.0 ) ) ); relativePosition.setReferencePosition( Position::NorthWest ); relativePosition.setAlignment( Qt::AlignTop | Qt::AlignLeft ); relativePosition.setHorizontalPadding( KChart::Measure( 4.0, KChartEnums::MeasureCalculationModeAbsolute ) ); relativePosition.setVerticalPadding( KChart::Measure( 4.0, KChartEnums::MeasureCalculationModeAbsolute ) ); m_legend->setFloatingPosition( relativePosition ); \endverbatim * Actually that's exactly the code KChart is using as default position for any floating legends, * so if you just say setPosition( KChart::Position::Floating ) without calling setFloatingPosition * your legend will be positioned at point 4/4. * * \sa setPosition, setAlignment */ void setFloatingPosition( const RelativePosition& relativePosition ); /** * Returns the position of a floating legend. * \sa setFloatingPosition */ const RelativePosition floatingPosition() const; void setOrientation( Qt::Orientation orientation ); Qt::Orientation orientation() const; void setSortOrder( Qt::SortOrder order ); Qt::SortOrder sortOrder() const; void setShowLines( bool legendShowLines ); bool showLines() const; void resetTexts(); void setText( uint dataset, const QString& text ); QString text( uint dataset ) const; const QMap texts() const; /** * Sets a list of datasets that are to be hidden in the legend. * * By passing an empty list, you show all datasets. * All datasets are shown by default, which means * that hiddenDatasets() returns an empty list. */ void setHiddenDatasets( const QList hiddenDatasets ); const QList hiddenDatasets() const; void setDatasetHidden( uint dataset, bool hidden ); bool datasetIsHidden( uint dataset ) const; uint datasetCount() const; void setDefaultColors(); void setRainbowColors(); void setSubduedColors( bool ordered = false ); void setBrushesFromDiagram( KChart::AbstractDiagram* diagram ); /** * Note: there is no color() getter method, since setColor * just sets a QBrush with the respective color, so the * brush() getter method is sufficient. */ void setColor( uint dataset, const QColor& color ); void setBrush( uint dataset, const QBrush& brush ); QBrush brush( uint dataset ) const; const QMap brushes() const; void setPen( uint dataset, const QPen& pen ); QPen pen( uint dataset ) const; const QMap pens() const; /** * Note that any sizes specified via setMarkerAttributes are ignored, * unless you disable the automatic size calculation, by saying * setUseAutomaticMarkerSize( false ) */ void setMarkerAttributes( uint dataset, const MarkerAttributes& ); MarkerAttributes markerAttributes( uint dataset ) const; const QMap markerAttributes() const; /** * This option is on by default, it means that Marker sizes in the Legend * will be the same as the font height used for their respective label texts. * * Set this to false, if you want to specify the marker sizes via setMarkerAttributes * or if you want the Legend to use the same marker sizes as they are used in the Diagrams. */ void setUseAutomaticMarkerSize( bool useAutomaticMarkerSize ); bool useAutomaticMarkerSize() const; void setTextAttributes( const TextAttributes &a ); TextAttributes textAttributes() const; void setTitleText( const QString& text ); QString titleText() const; void setTitleTextAttributes( const TextAttributes &a ); TextAttributes titleTextAttributes() const; void setSpacing( uint space ); uint spacing() const; // called internally by KChart::Chart, when painting into a custom QPainter void forceRebuild() Q_DECL_OVERRIDE; QSize minimumSizeHint() const Q_DECL_OVERRIDE; QSize sizeHint() const Q_DECL_OVERRIDE; bool hasHeightForWidth() const Q_DECL_OVERRIDE; int heightForWidth( int width ) const Q_DECL_OVERRIDE; void needSizeHint() Q_DECL_OVERRIDE; void resizeLayout( const QSize& size ) Q_DECL_OVERRIDE; Q_SIGNALS: void destroyedLegend( Legend* ); /** Emitted upon change of a property of the Legend or any of its components. */ void propertiesChanged(); private Q_SLOTS: void emitPositionChanged(); void resetDiagram( AbstractDiagram* ); void activateTheLayout(); void setNeedRebuild(); void buildLegend(); }; // End of class Legend } #endif // KCHARTLEGEND_H diff --git a/src/KChart/KChartLegend_p.h b/src/KChart/KChartLegend_p.h index b4cfb2b..a85fd2d 100644 --- a/src/KChart/KChartLegend_p.h +++ b/src/KChart/KChartLegend_p.h @@ -1,145 +1,145 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTLEGEND_P_H #define KCHARTLEGEND_P_H // // W A R N I N G // ------------- // // This file is not part of the KD Chart API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "KChartLegend.h" #include #include "KChartAbstractAreaWidget_p.h" #include #include #include "KChartMath_p.h" #include #include #include #include QT_BEGIN_NAMESPACE class QGridLayout; class KTextDocument; class QTextDocument; QT_END_NAMESPACE namespace KChart { class AbstractDiagram; struct DatasetItem; class DiagramObserver; class AbstractLayoutItem; struct HDatasetItem { HDatasetItem(); int height() const; AbstractLayoutItem *markerLine; TextLayoutItem *label; VerticalLineLayoutItem *separatorLine; QSpacerItem *spacer; }; class DiagramsObserversList : public QList {}; /** * \internal */ class Q_DECL_HIDDEN Legend::Private : public AbstractAreaWidget::Private { friend class Legend; public: Private(); ~Private(); DiagramObserver* findObserverForDiagram( AbstractDiagram* diagram ) { for (int i = 0; i < observers.size(); ++i) { DiagramObserver * obs = observers.at(i); if ( obs->diagram() == diagram ) return obs; } - return 0; + return nullptr; } void fetchPaintOptions( Legend *q ); QSizeF markerSize( Legend *q, int dataset, qreal fontHeight ) const; QSizeF maxMarkerSize( Legend *q, qreal fontHeight ) const; void reflowHDatasetItems( Legend *q ); void flowHDatasetItems( Legend *q ); void destroyOldLayout(); private: // user-settable const QWidget* referenceArea; Position position; Qt::Alignment alignment; Qt::Alignment textAlignment; Qt::Alignment legendLineSymbolAlignment; RelativePosition relativePosition; Qt::Orientation orientation; Qt::SortOrder order; bool showLines; QMap texts; QMap brushes; QMap pens; QMap markerAttributes; QList hiddenDatasets; TextAttributes textAttributes; QString titleText; TextAttributes titleTextAttributes; uint spacing; bool useAutomaticMarkerSize; LegendStyle legendStyle; // internal mutable QStringList modelLabels; mutable QList modelBrushes; mutable QList modelPens; mutable QList< MarkerAttributes > modelMarkers; mutable QSize cachedSizeHint; QVector< AbstractLayoutItem* > paintItems; QGridLayout* layout; QList< HDatasetItem > hLayoutDatasets; DiagramsObserversList observers; }; inline Legend::Legend( Private* p, QWidget* parent ) : AbstractAreaWidget( p, parent ) { init(); } inline Legend::Private * Legend::d_func() { return static_cast( AbstractAreaWidget::d_func() ); } inline const Legend::Private * Legend::d_func() const { return static_cast( AbstractAreaWidget::d_func() ); } } #endif /* KCHARTLEGEND_P_H */ diff --git a/src/KChart/KChartLineAttributes.cpp b/src/KChart/KChartLineAttributes.cpp index a471ca4..03b1e03 100644 --- a/src/KChart/KChartLineAttributes.cpp +++ b/src/KChart/KChartLineAttributes.cpp @@ -1,156 +1,156 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartLineAttributes.h" #include "KChartMath_p.h" #include #define d d_func() using namespace KChart; class Q_DECL_HIDDEN LineAttributes::Private { friend class LineAttributes; public: Private(); private: //Areas MissingValuesPolicy missingValuesPolicy; bool displayArea; bool visible; uint transparency; int areaBoundingDataset; }; LineAttributes::Private::Private() : missingValuesPolicy( MissingValuesAreBridged ) , displayArea( false ) , visible( true ) , transparency( 255 ) , areaBoundingDataset( -1 ) { } LineAttributes::LineAttributes() : _d( new Private() ) { } LineAttributes::LineAttributes( const LineAttributes& r ) : _d( new Private( *r.d ) ) { } LineAttributes& LineAttributes::operator= ( const LineAttributes& r ) { if ( this == &r ) return *this; *d = *r.d; return *this; } LineAttributes::~LineAttributes() { - delete _d; _d = 0; + delete _d; _d = nullptr; } bool LineAttributes::operator==( const LineAttributes& r ) const { return missingValuesPolicy() == r.missingValuesPolicy() && displayArea() == r.displayArea() && isVisible() == r.isVisible() && transparency() == r.transparency() && areaBoundingDataset() == r.areaBoundingDataset(); } void LineAttributes::setMissingValuesPolicy( MissingValuesPolicy policy ) { d->missingValuesPolicy = policy; } LineAttributes::MissingValuesPolicy LineAttributes::missingValuesPolicy() const { return d->missingValuesPolicy; } void LineAttributes::setDisplayArea( bool display ) { d->displayArea = display; } bool LineAttributes::displayArea() const { return d->displayArea; } void LineAttributes::setTransparency( uint alpha ) { if ( alpha > 255 ) alpha = 255; d->transparency = alpha; } uint LineAttributes::transparency() const { return d->transparency; } void LineAttributes::setAreaBoundingDataset( int dataset ) { d->areaBoundingDataset = dataset; } int LineAttributes::areaBoundingDataset() const { return d->areaBoundingDataset; } void LineAttributes::setVisible( bool visible ) { d->visible = visible; } bool LineAttributes::isVisible() const { return d->visible; } #if !defined(QT_NO_DEBUG_STREAM) QDebug operator<<(QDebug dbg, const KChart::LineAttributes& a) { dbg << "KChart::LineAttributes(" // MissingValuesPolicy missingValuesPolicy; << "bool="<. */ #include "KChartMarkerAttributes.h" #include "KChartMath_p.h" #include #include #include #include #include #include #include using namespace KChart; class Q_DECL_HIDDEN MarkerAttributes::Private { friend class ::KChart::MarkerAttributes; public: Private(); private: bool visible; bool threeD; QMap markerStylesMap; uint markerStyle; MarkerSizeMode markerSizeMode; QSizeF markerSize; QColor markerColor; QPainterPath customMarkerPath; QPen markerPen; }; MarkerAttributes::Private::Private() : visible( false ), threeD( false ), markerStyle( MarkerSquare ), markerSizeMode( AbsoluteSize ), markerSize( 10, 10 ), markerPen( Qt::black ) { } MarkerAttributes::MarkerAttributes() : _d( new Private ) { } MarkerAttributes::MarkerAttributes( const MarkerAttributes& r ) : _d( new Private( *r._d ) ) { } MarkerAttributes & MarkerAttributes::operator=( const MarkerAttributes& r ) { MarkerAttributes copy( r ); copy.swap( *this ); return *this; } MarkerAttributes::~MarkerAttributes() { - delete _d; _d = 0; + delete _d; _d = nullptr; } #define d d_func() bool MarkerAttributes::operator==( const MarkerAttributes& r ) const { /* qDebug() << "MarkerAttributes::operator== finds" << "b" << (isVisible() == r.isVisible()) << "c" << (markerStylesMap() == r.markerStylesMap()) << "d" << (markerStyle() == r.markerStyle()) << markerStyle() <visible = visible; } bool MarkerAttributes::isVisible() const { return d->visible; } void MarkerAttributes::setThreeD( bool value ) { d->threeD = value; } bool MarkerAttributes::threeD() const { return d->threeD; } void MarkerAttributes::setMarkerStylesMap( const MarkerStylesMap & map ) { d->markerStylesMap = map; } MarkerAttributes::MarkerStylesMap MarkerAttributes::markerStylesMap() const { return d->markerStylesMap; } void MarkerAttributes::setMarkerStyle( uint style ) { d->markerStyle = style; } uint MarkerAttributes::markerStyle() const { return d->markerStyle; } void MarkerAttributes::setMarkerSize( const QSizeF& size ) { d->markerSize = size; } QSizeF MarkerAttributes::markerSize() const { return d->markerSize; } void MarkerAttributes::setMarkerSizeMode( MarkerSizeMode mode ) { d->markerSizeMode = mode; } MarkerAttributes::MarkerSizeMode MarkerAttributes::markerSizeMode() const { return d->markerSizeMode; } void MarkerAttributes::setMarkerColor( const QColor& color ) { d->markerColor = color; } QColor MarkerAttributes::markerColor() const { return d->markerColor; } void MarkerAttributes::setCustomMarkerPath( const QPainterPath& path ) { d->customMarkerPath = path; } QPainterPath MarkerAttributes::customMarkerPath() const { return d->customMarkerPath; } void MarkerAttributes::setPen( const QPen& pen ) { d->markerPen = pen; } QPen MarkerAttributes::pen() const { return d->markerPen; } #undef d #ifndef QT_NO_DEBUG_STREAM QDebug operator<<( QDebug dbg, const MarkerAttributes & ma ) { return dbg << "KChart::MarkerAttributes(" << "visible=" << ma.isVisible() << "markerStylesMap=" << ma.markerStylesMap() << "markerStyle=" << ma.markerStyle() << "markerColor=" << ma.markerColor() << "customMarkerPath=" << ma.customMarkerPath() << "pen=" << ma.pen() << ")"; } #endif diff --git a/src/KChart/KChartMeasure.cpp b/src/KChart/KChartMeasure.cpp index e7e7630..906a422 100644 --- a/src/KChart/KChartMeasure.cpp +++ b/src/KChart/KChartMeasure.cpp @@ -1,240 +1,240 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartMeasure.h" #include #include #include #include #include #include "KChartMath_p.h" #include namespace KChart { Measure::Measure() : mValue( 0.0 ), mMode( KChartEnums::MeasureCalculationModeAuto ), - mArea( 0 ), + mArea( nullptr ), mOrientation( KChartEnums::MeasureOrientationAuto ) { // this bloc left empty intentionally } Measure::Measure( qreal value, KChartEnums::MeasureCalculationMode mode, KChartEnums::MeasureOrientation orientation ) : mValue( value ), mMode( mode ), - mArea( 0 ), + mArea( nullptr ), mOrientation( orientation ) { // this bloc left empty intentionally } Measure::Measure( const Measure& r ) : mValue( r.value() ), mMode( r.calculationMode() ), mArea( r.referenceArea() ), mOrientation( r.referenceOrientation() ) { // this bloc left empty intentionally } Measure & Measure::operator=( const Measure& r ) { if ( this != &r ) { mValue = r.value(); mMode = r.calculationMode(); mArea = r.referenceArea(); mOrientation = r.referenceOrientation(); } return *this; } qreal Measure::calculatedValue( const QSizeF& autoSize, KChartEnums::MeasureOrientation autoOrientation) const { if ( mMode == KChartEnums::MeasureCalculationModeAbsolute ) { return mValue; } else { qreal value = 0.0; const QObject theAutoArea; const QObject* autoArea = &theAutoArea; const QObject* area = mArea ? mArea : autoArea; KChartEnums::MeasureOrientation orientation = mOrientation; switch ( mMode ) { case KChartEnums::MeasureCalculationModeAuto: area = autoArea; orientation = autoOrientation; break; case KChartEnums::MeasureCalculationModeAutoArea: area = autoArea; break; case KChartEnums::MeasureCalculationModeAutoOrientation: orientation = autoOrientation; break; case KChartEnums::MeasureCalculationModeAbsolute: // fall through intended case KChartEnums::MeasureCalculationModeRelative: break; } if ( area ) { QSizeF size; if ( area == autoArea ) size = autoSize; else size = sizeOfArea( area ); //qDebug() << ( area == autoArea ) << "size" << size; qreal referenceValue = 0; switch ( orientation ) { case KChartEnums::MeasureOrientationAuto: // fall through intended case KChartEnums::MeasureOrientationMinimum: referenceValue = qMin( size.width(), size.height() ); break; case KChartEnums::MeasureOrientationMaximum: referenceValue = qMax( size.width(), size.height() ); break; case KChartEnums::MeasureOrientationHorizontal: referenceValue = size.width(); break; case KChartEnums::MeasureOrientationVertical: referenceValue = size.height(); break; } value = mValue / 1000.0 * referenceValue; } return value; } } qreal Measure::calculatedValue( const QObject* autoArea, KChartEnums::MeasureOrientation autoOrientation) const { return calculatedValue( sizeOfArea( autoArea ), autoOrientation); } const QSizeF Measure::sizeOfArea( const QObject* area ) const { QSizeF size; const CartesianCoordinatePlane* plane = dynamic_cast( area ); if ( false ) { size = plane->visibleDiagramArea().size(); } else { const AbstractArea* kdcArea = dynamic_cast(area); if ( kdcArea ) { size = kdcArea->geometry().size(); //qDebug() << "Measure::sizeOfArea() found kdcArea with size" << size; } else { const QWidget* widget = dynamic_cast(area); if ( widget ) { /* ATTENTION: Using the layout does not work: The Legend will never get the right size then! const QLayout * layout = widget->layout(); if ( layout ) { size = layout->geometry().size(); //qDebug() << "Measure::sizeOfArea() found widget with layout size" << size; } else*/ { size = widget->geometry().size(); //qDebug() << "Measure::sizeOfArea() found widget with size" << size; } } else if ( mMode != KChartEnums::MeasureCalculationModeAbsolute ) { size = QSizeF(1.0, 1.0); //qDebug("Measure::sizeOfArea() got no valid area."); } } } const QPair< qreal, qreal > factors = GlobalMeasureScaling::instance()->currentFactors(); return QSizeF(size.width() * factors.first, size.height() * factors.second); } bool Measure::operator==( const Measure& r ) const { return( mValue == r.value() && mMode == r.calculationMode() && mArea == r.referenceArea() && mOrientation == r.referenceOrientation() ); } GlobalMeasureScaling::GlobalMeasureScaling() : - m_paintDevice( 0 ) + m_paintDevice( nullptr ) { mFactors.push( qMakePair(qreal(1.0), qreal(1.0)) ); } GlobalMeasureScaling::~GlobalMeasureScaling() { // this space left empty intentionally } GlobalMeasureScaling* GlobalMeasureScaling::instance() { static GlobalMeasureScaling instance; return &instance; } void GlobalMeasureScaling::setFactors(qreal factorX, qreal factorY) { instance()->mFactors.push( qMakePair(factorX, factorY) ); } void GlobalMeasureScaling::resetFactors() { // never remove the initial (1.0. 1.0) setting if ( instance()->mFactors.count() > 1 ) instance()->mFactors.pop(); } const QPair< qreal, qreal > GlobalMeasureScaling::currentFactors() { return instance()->mFactors.top(); } void GlobalMeasureScaling::setPaintDevice( QPaintDevice* paintDevice ) { instance()->m_paintDevice = paintDevice; } QPaintDevice* GlobalMeasureScaling::paintDevice() { return instance()->m_paintDevice; } } #if !defined(QT_NO_DEBUG_STREAM) QDebug operator<<(QDebug dbg, const KChart::Measure& m) { dbg << "KChart::Measure(" << "value="<. */ #include "KChartModelDataCache_p.h" #include using namespace KChart::ModelDataCachePrivate; ModelSignalMapperConnector::ModelSignalMapperConnector( ModelSignalMapper& mapper ) - : QObject( 0 ), + : QObject( nullptr ), m_mapper( mapper ) { } ModelSignalMapperConnector::~ModelSignalMapperConnector() { } void ModelSignalMapperConnector::connectSignals( QAbstractItemModel* model ) { connect( model, SIGNAL(destroyed()), this, SLOT(resetModel()) ); connect( model, SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(columnsInserted(QModelIndex,int,int)) ); connect( model, SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(columnsRemoved(QModelIndex,int,int)) ); connect( model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex)) ); connect( model, SIGNAL(layoutChanged()), this, SLOT(layoutChanged()) ); connect( model, SIGNAL(modelReset()), this, SLOT(modelReset()) ); connect( model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int)) ); connect( model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved(QModelIndex,int,int)) ); } void ModelSignalMapperConnector::disconnectSignals( QAbstractItemModel* model ) { disconnect( model, SIGNAL(destroyed()), this, SLOT(resetModel()) ); disconnect( model, SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(columnsInserted(QModelIndex,int,int)) ); disconnect( model, SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(columnsRemoved(QModelIndex,int,int)) ); disconnect( model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex)) ); disconnect( model, SIGNAL(layoutChanged()), this, SLOT(layoutChanged()) ); disconnect( model, SIGNAL(modelReset()), this, SLOT(modelReset()) ); disconnect( model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int)) ); disconnect( model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved(QModelIndex,int,int)) ); } void ModelSignalMapperConnector::resetModel() { m_mapper.resetModel(); } void ModelSignalMapperConnector::columnsInserted( const QModelIndex& parent, int start, int end ) { m_mapper.columnsInserted( parent, start, end ); } void ModelSignalMapperConnector::columnsRemoved( const QModelIndex& parent, int start, int end ) { m_mapper.columnsRemoved( parent, start, end ); } void ModelSignalMapperConnector::dataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ) { m_mapper.dataChanged( topLeft, bottomRight ); } void ModelSignalMapperConnector::layoutChanged() { m_mapper.layoutChanged(); } void ModelSignalMapperConnector::modelReset() { m_mapper.modelReset(); } void ModelSignalMapperConnector::rowsInserted( const QModelIndex& parent, int start, int end ) { m_mapper.rowsInserted( parent, start, end ); } void ModelSignalMapperConnector::rowsRemoved( const QModelIndex& parent, int start, int end ) { m_mapper.rowsRemoved( parent, start, end ); } diff --git a/src/KChart/KChartModelDataCache_p.h b/src/KChart/KChartModelDataCache_p.h index 9b42e9b..234b1d3 100644 --- a/src/KChart/KChartModelDataCache_p.h +++ b/src/KChart/KChartModelDataCache_p.h @@ -1,351 +1,351 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTMODELDATACACHE_H #define KCHARTMODELDATACACHE_H #include #include #include #include #include "kchart_export.h" QT_BEGIN_NAMESPACE class QAbstractItemModel; QT_END_NAMESPACE namespace KChart { namespace ModelDataCachePrivate { class ModelSignalMapper { protected: ModelSignalMapper() {} public: virtual ~ModelSignalMapper() {} virtual void resetModel() = 0; virtual void columnsInserted( const QModelIndex&, int, int ) = 0; virtual void columnsRemoved( const QModelIndex&, int, int ) = 0; virtual void dataChanged( const QModelIndex&, const QModelIndex& ) = 0; virtual void layoutChanged() = 0; virtual void modelReset() = 0; virtual void rowsInserted( const QModelIndex&, int, int ) = 0; virtual void rowsRemoved( const QModelIndex&, int, int ) = 0; }; // this class maps slots to a non-QObject instantiating ModelSignalMapper class KCHART_EXPORT ModelSignalMapperConnector : public QObject { Q_OBJECT public: explicit ModelSignalMapperConnector( ModelSignalMapper& mapper ); ~ModelSignalMapperConnector(); void connectSignals( QAbstractItemModel* model ); void disconnectSignals( QAbstractItemModel* model ); protected Q_SLOTS: void resetModel(); void columnsInserted( const QModelIndex&, int, int ); void columnsRemoved( const QModelIndex&, int, int ); void dataChanged( const QModelIndex&, const QModelIndex& ); void layoutChanged(); void modelReset(); void rowsInserted( const QModelIndex&, int, int ); void rowsRemoved( const QModelIndex&, int, int ); private: ModelSignalMapper& m_mapper; }; template< class T> T nan() { return T(); } template<> inline qreal nan< qreal >() { return std::numeric_limits< qreal >::quiet_NaN(); } } template< class T, int ROLE > class ModelDataCache : public ModelDataCachePrivate::ModelSignalMapper { public: ModelDataCache() - : m_model( 0 ), + : m_model( nullptr ), m_connector( *this ) { } virtual ~ModelDataCache() { } T data( const QModelIndex& index ) const { if ( !index.isValid() || index.parent() != m_rootIndex || index.row() >= m_model->rowCount(m_rootIndex) || index.column() >= m_model->columnCount(m_rootIndex) ) return ModelDataCachePrivate::nan< T >(); if ( index.row() >= m_data.count() ) { qWarning( "KChart didn't receive signal rowsInserted, resetModel or layoutChanged, " "but an index with a row outside of the known bounds." ); // apparently, data were added behind our back (w/o signals) const_cast< ModelDataCache< T, ROLE >* >( this )->rowsInserted( m_rootIndex, m_data.count(), m_model->rowCount( m_rootIndex ) - 1 ); Q_ASSERT( index.row() < m_data.count() ); } if ( index.column() >= m_data.first().count() ) { qWarning( "KChart didn't got signal columnsInserted, resetModel or layoutChanged, " "but an index with a column outside of the known bounds." ); // apparently, data were added behind our back (w/o signals) const_cast< ModelDataCache< T, ROLE >* >( this )->columnsInserted( m_rootIndex, m_data.first().count(), m_model->columnCount( m_rootIndex ) - 1 ); Q_ASSERT( index.column() < m_data.first().count() ); } return data( index.row(), index.column() ); } T data( int row, int column ) const { if ( row < 0 || column < 0 ) return ModelDataCachePrivate::nan< T >(); Q_ASSERT( row < m_model->rowCount(m_rootIndex) ); Q_ASSERT( column < m_model->columnCount(m_rootIndex) ); Q_ASSERT( row < m_data.count() ); Q_ASSERT( column < m_data.first().count() ); if ( isCached( row, column ) ) return m_data.at( row ).at( column ); return fetchFromModel( row, column, ROLE ); } void setModel( QAbstractItemModel* model ) { - if ( m_model != 0 ) + if ( m_model != nullptr ) m_connector.disconnectSignals( m_model ); m_model = model; - if ( m_model != 0 ) + if ( m_model != nullptr ) m_connector.connectSignals( m_model ); modelReset(); } QAbstractItemModel* model() const { return m_model; } void setRootIndex( const QModelIndex& rootIndex ) { Q_ASSERT( rootIndex.model() == m_model || !rootIndex.isValid() ); m_rootIndex = rootIndex; modelReset(); } QModelIndex rootIndex() const { return m_rootIndex; } protected: bool isCached( int row, int column ) const { return m_cacheValid.at( row ).at( column ); } T fetchFromModel( int row, int column, int role ) const { - Q_ASSERT( m_model != 0 ); + Q_ASSERT( m_model != nullptr ); const QModelIndex index = m_model->index( row, column, m_rootIndex ); const QVariant data = index.data( role ); const T value = data.isNull() ? ModelDataCachePrivate::nan< T >() : ( data.value< T >() ); m_data[ row ][ column ] = value; m_cacheValid[ row ][ column ] = true; return value; } void columnsInserted( const QModelIndex& parent, int start, int end ) Q_DECL_OVERRIDE { - Q_ASSERT( m_model != 0 ); + Q_ASSERT( m_model != nullptr ); Q_ASSERT( parent.model() == m_model || !parent.isValid() ); if ( parent != m_rootIndex ) return; Q_ASSERT( start <= end ); Q_ASSERT( start <= m_model->columnCount(m_rootIndex) ); const int rowCount = m_data.count(); for ( int row = 0; row < rowCount; ++row ) { m_data[ row ].insert( start, end - start + 1, T() ); m_cacheValid[ row ].insert( start, end - start + 1, false ); Q_ASSERT( m_data.at( row ).count() == m_model->columnCount( m_rootIndex ) ); Q_ASSERT( m_cacheValid.at( row ).count() == m_model->columnCount( m_rootIndex ) ); } } void columnsRemoved( const QModelIndex& parent, int start, int end ) Q_DECL_OVERRIDE { - Q_ASSERT( m_model != 0 ); + Q_ASSERT( m_model != nullptr ); Q_ASSERT( parent.model() == m_model || !parent.isValid() ); if ( parent != m_rootIndex ) return; Q_ASSERT( start <= end ); const int rowCount = m_data.count(); for ( int row = 0; row < rowCount; ++row ) { m_data[ row ].remove( start, end - start + 1 ); m_cacheValid[ row ].remove( start, end - start + 1 ); Q_ASSERT( m_data.at( row ).count() == m_model->columnCount( m_rootIndex ) ); Q_ASSERT( m_cacheValid.at( row ).count() == m_model->columnCount( m_rootIndex ) ); } } void dataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ) Q_DECL_OVERRIDE { if ( !m_model ) return; - Q_ASSERT( m_model != 0 ); + Q_ASSERT( m_model != nullptr ); Q_ASSERT( topLeft.parent() == bottomRight.parent() ); if ( !topLeft.isValid() || !bottomRight.isValid() || topLeft.parent() != m_rootIndex ) return; Q_ASSERT( topLeft.model() == m_model && bottomRight.model() == m_model ); const int minRow = qMax( 0, topLeft.row() ); const int maxRow = bottomRight.row(); const int minCol = qMax( 0, topLeft.column() ); const int maxCol = bottomRight.column(); Q_ASSERT( minRow <= maxRow ); Q_ASSERT( minCol <= maxCol ); Q_ASSERT( maxRow < m_model->rowCount( m_rootIndex ) ); Q_ASSERT( maxCol < m_model->columnCount( m_rootIndex ) ); for ( int row = minRow; row <= maxRow; ++row ) { for ( int col = minCol; col <= maxCol; ++col ) { m_cacheValid[ row ][ col ] = false; Q_ASSERT( !isCached( row, col ) ); } } } void layoutChanged() Q_DECL_OVERRIDE { modelReset(); } void modelReset() Q_DECL_OVERRIDE { m_data.clear(); m_cacheValid.clear(); - if ( m_model == 0 ) + if ( m_model == nullptr ) return; m_data.fill( QVector< T >( m_model->columnCount( m_rootIndex ) ), m_model->rowCount( m_rootIndex ) ); m_cacheValid.fill( QVector< bool >( m_model->columnCount( m_rootIndex ), false ), m_model->rowCount( m_rootIndex ) ); Q_ASSERT( m_data.count() == m_model->rowCount( m_rootIndex ) ); Q_ASSERT( m_cacheValid.count() == m_model->rowCount( m_rootIndex ) ); } void rowsInserted( const QModelIndex& parent, int start, int end ) Q_DECL_OVERRIDE { - Q_ASSERT( m_model != 0 ); + Q_ASSERT( m_model != nullptr ); Q_ASSERT( parent.model() == m_model || !parent.isValid() ); if ( parent != m_rootIndex || start >= m_model->rowCount(m_rootIndex) ) return; Q_ASSERT( start <= end ); Q_ASSERT( end - start + 1 <= m_model->rowCount(m_rootIndex) ); m_data.insert( start, end - start + 1, QVector< T >( m_model->columnCount( m_rootIndex ) ) ); m_cacheValid.insert( start, end - start + 1, QVector< bool >( m_model->columnCount( m_rootIndex ), false ) ); Q_ASSERT( m_data.count() == m_model->rowCount( m_rootIndex ) ); Q_ASSERT( m_cacheValid.count() == m_model->rowCount( m_rootIndex ) ); } void rowsRemoved( const QModelIndex& parent, int start, int end ) Q_DECL_OVERRIDE { - Q_ASSERT( m_model != 0 ); + Q_ASSERT( m_model != nullptr ); Q_ASSERT( parent.model() == m_model || !parent.isValid() ); if ( parent != m_rootIndex || start >= m_data.count() ) return; Q_ASSERT( start <= end ); m_data.remove( start, end - start + 1 ); m_cacheValid.remove( start, end - start + 1 ); Q_ASSERT( m_data.count() == m_model->rowCount( m_rootIndex ) ); Q_ASSERT( m_cacheValid.count() == m_model->rowCount( m_rootIndex ) ); } void resetModel() Q_DECL_OVERRIDE { // no need to disconnect, this is a response to SIGNAL( destroyed() ) - m_model = 0; + m_model = nullptr; modelReset(); } private: QAbstractItemModel* m_model; QModelIndex m_rootIndex; ModelDataCachePrivate::ModelSignalMapperConnector m_connector; mutable QVector< QVector< T > > m_data; mutable QVector< QVector< bool > > m_cacheValid; }; } #endif diff --git a/src/KChart/KChartPaintContext.cpp b/src/KChart/KChartPaintContext.cpp index c77f61e..a6eb41c 100644 --- a/src/KChart/KChartPaintContext.cpp +++ b/src/KChart/KChartPaintContext.cpp @@ -1,83 +1,83 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartPaintContext.h" #include "KChartAbstractCoordinatePlane.h" #include "KChartMath_p.h" #include #include using namespace KChart; #define d (d_func()) class Q_DECL_HIDDEN PaintContext::Private { public: QPainter* painter; QRectF rect; AbstractCoordinatePlane* plane; Private() - : painter( 0 ) - , plane ( 0 ) + : painter( nullptr ) + , plane ( nullptr ) {} }; PaintContext::PaintContext() : _d ( new Private() ) { } PaintContext::~PaintContext() { delete _d; } const QRectF PaintContext::rectangle() const { return d->rect; } void PaintContext::setRectangle ( const QRectF& rect ) { d->rect = rect; } QPainter* PaintContext::painter() const { return d->painter; } void PaintContext::setPainter( QPainter* painter ) { d->painter = painter; } AbstractCoordinatePlane* PaintContext::coordinatePlane() const { return d->plane; } void PaintContext::setCoordinatePlane( AbstractCoordinatePlane* plane) { d->plane = plane; } diff --git a/src/KChart/KChartPalette.cpp b/src/KChart/KChartPalette.cpp index d86082b..94a8ed5 100644 --- a/src/KChart/KChartPalette.cpp +++ b/src/KChart/KChartPalette.cpp @@ -1,182 +1,182 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartPalette.h" #include "KChartMath_p.h" #include #include using namespace KChart; namespace { static Palette makeDefaultPalette() { Palette p; p.addBrush( Qt::red ); p.addBrush( Qt::green ); p.addBrush( Qt::blue ); p.addBrush( Qt::cyan ); p.addBrush( Qt::magenta ); p.addBrush( Qt::yellow ); p.addBrush( Qt::darkRed ); p.addBrush( Qt::darkGreen ); p.addBrush( Qt::darkBlue ); p.addBrush( Qt::darkCyan ); p.addBrush( Qt::darkMagenta ); p.addBrush( Qt::darkYellow ); return p; } static Palette makeSubduedPalette() { Palette p; p.addBrush( QColor( 0xe0,0x7f,0x70 ) ); p.addBrush( QColor( 0xe2,0xa5,0x6f ) ); p.addBrush( QColor( 0xe0,0xc9,0x70 ) ); p.addBrush( QColor( 0xd1,0xe0,0x70 ) ); p.addBrush( QColor( 0xac,0xe0,0x70 ) ); p.addBrush( QColor( 0x86,0xe0,0x70 ) ); p.addBrush( QColor( 0x70,0xe0,0x7f ) ); p.addBrush( QColor( 0x70,0xe0,0xa4 ) ); p.addBrush( QColor( 0x70,0xe0,0xc9 ) ); p.addBrush( QColor( 0x70,0xd1,0xe0 ) ); p.addBrush( QColor( 0x70,0xac,0xe0 ) ); p.addBrush( QColor( 0x70,0x86,0xe0 ) ); p.addBrush( QColor( 0x7f,0x70,0xe0 ) ); p.addBrush( QColor( 0xa4,0x70,0xe0 ) ); p.addBrush( QColor( 0xc9,0x70,0xe0 ) ); p.addBrush( QColor( 0xe0,0x70,0xd1 ) ); p.addBrush( QColor( 0xe0,0x70,0xac ) ); p.addBrush( QColor( 0xe0,0x70,0x86 ) ); return p; } static Palette makeRainbowPalette() { Palette p; p.addBrush( QColor(255, 0,196) ); p.addBrush( QColor(255, 0, 96) ); p.addBrush( QColor(255, 128,64) ); p.addBrush( Qt::yellow ); p.addBrush( Qt::green ); p.addBrush( Qt::cyan ); p.addBrush( QColor( 96, 96,255) ); p.addBrush( QColor(160, 0,255) ); for ( int i = 8 ; i < 16 ; ++i ) { p.addBrush( p.getBrush( i - 8 ).color().light(), i ); } return p; } } #define d d_func() class Q_DECL_HIDDEN Palette::Private { public: explicit Private() {} ~Private() {} QVector brushes; }; const Palette& Palette::defaultPalette() { static const Palette palette = makeDefaultPalette(); return palette; } const Palette& Palette::subduedPalette() { static const Palette palette = makeSubduedPalette(); return palette; } const Palette& Palette::rainbowPalette() { static const Palette palette = makeRainbowPalette(); return palette; } Palette::Palette( QObject *parent ) : QObject( parent ), _d( new Private ) { } Palette::~Palette() { - delete _d; _d = 0; + delete _d; _d = nullptr; } Palette::Palette( const Palette& r ) : QObject(), _d( new Private( *r.d ) ) { } Palette& Palette::operator=( const Palette& r ) { Palette copy( r ); copy.swap( *this ); // emit changed() ? return *this; } bool Palette::isValid() const { return d->brushes.size() >= 1; } int Palette::size() const { return d->brushes.size(); } void Palette::addBrush( const QBrush& brush, int position ) { if ( position < 0 || position >= size() ) { d->brushes.append( brush ); } else { d->brushes.insert( position, brush ); } emit changed(); } QBrush Palette::getBrush( int position ) const { if ( !isValid() ) return QBrush(); return d->brushes.at( position % size() ); } void Palette::removeBrush( int position ) { if ( position < 0 || position >= size() ) return; d->brushes.remove( position ); emit changed(); } diff --git a/src/KChart/KChartPalette.h b/src/KChart/KChartPalette.h index 9a1f44e..d0070a0 100644 --- a/src/KChart/KChartPalette.h +++ b/src/KChart/KChartPalette.h @@ -1,96 +1,96 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __KCHART_PALETTE_H__ #define __KCHART_PALETTE_H__ #include #include #include "KChartGlobal.h" namespace KChart { /** * \brief A Palette is a set of brushes (or colors) to be used * for painting data sets. * * The palette class encapsulates a colletion of brushes, which in * the simplest case are colors, to be used for painting a series of * data sets. When asked for the m-th color, a palette of size n will * wrap around and thus cycle through the available colors. * * Three builtin palettes are provided for convenience, one with a default * set of colors, one with a subdued color selection, one with rainbow * colors. * * When a palette changes, it emits a changed() signal. Hook up to it, if * you want to repaint when the color selection changes. */ class KCHART_EXPORT Palette: public QObject { Q_OBJECT public: - explicit Palette( QObject *parent = 0 ); + explicit Palette( QObject *parent = nullptr ); Palette( const Palette& ); Palette &operator= ( const Palette & ); ~Palette(); /** Provide access to the three builtin palettes, one with standard bright * colors, one with more subdued colors, and one with rainbow colors. */ static const Palette& defaultPalette(); static const Palette& subduedPalette(); static const Palette& rainbowPalette(); /** @return whether this represents a valid palette. For a palette to be * valid it needs to have at least one brush associated. */ bool isValid() const; /** @return the number of brushed in the palette. */ int size() const; /** Adds \a brush to the palette. If no \a position is specified, the * brush is appended. */ void addBrush( const QBrush & brush, int position = -1 ); /** * Query the palette for a brush at the specified position. If the * position exceeds the size of the palette, it wraps around. */ QBrush getBrush( int position ) const; /** Remove the brush at position \a position, if there is one. */ void removeBrush( int position ); Q_SIGNALS: /** Emitted whenever the palette changes. Views listen to this and * repaing. */ void changed(); private: KCHART_DECLARE_PRIVATE_BASE_VALUE( Palette ) }; } KCHART_DECLARE_SWAP_SPECIALISATION( KChart::Palette ) #endif diff --git a/src/KChart/KChartRelativePosition.cpp b/src/KChart/KChartRelativePosition.cpp index 7d1cc7f..2e29215 100644 --- a/src/KChart/KChartRelativePosition.cpp +++ b/src/KChart/KChartRelativePosition.cpp @@ -1,229 +1,229 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartRelativePosition.h" #include "KChartEnums.h" #include "KChartMeasure.h" #include "KChartPosition.h" #include "KChartAbstractArea.h" #include "KChartMath_p.h" #include #include using namespace KChart; class Q_DECL_HIDDEN RelativePosition::Private { friend class ::KChart::RelativePosition; public: Private(); ~Private(); private: QObject* area; PositionPoints points; Position position; Qt::Alignment alignment; Measure horizontalPadding; Measure verticalPadding; qreal rotation; }; RelativePosition::Private::Private() - : area( 0 ), + : area( nullptr ), alignment( Qt::AlignCenter ), rotation( 0 ) { } RelativePosition::Private::~Private() {} RelativePosition::RelativePosition() : _d( new Private ) { } RelativePosition::RelativePosition( const RelativePosition& r ) : _d( new Private( *r._d ) ) { } RelativePosition & RelativePosition::operator=( const RelativePosition & other ) { RelativePosition copy( other ); copy.swap( *this ); return *this; } RelativePosition::~RelativePosition() { delete _d; } #define d d_func() void RelativePosition::setReferenceArea( QObject * area ) { d->area = area; if ( area ) setReferencePoints( PositionPoints() ); } QObject * RelativePosition::referenceArea() const { return d->area; } void RelativePosition::setReferencePoints( const PositionPoints& points ) { d->points = points; if ( !points.isNull() ) - setReferenceArea( 0 ); + setReferenceArea( nullptr ); } const PositionPoints RelativePosition::referencePoints() const{ return d->points; } void RelativePosition::setReferencePosition( Position pos ) { d->position = pos; } void RelativePosition::resetReferencePosition() { d->position = Position::Unknown; } Position RelativePosition::referencePosition() const { return d->position; } void RelativePosition::setAlignment( Qt::Alignment align ) { d->alignment = align; } Qt::Alignment RelativePosition::alignment() const { return d->alignment; } void RelativePosition::setHorizontalPadding( const Measure & pad ) { d->horizontalPadding = pad; } Measure RelativePosition::horizontalPadding() const { return d->horizontalPadding; } void RelativePosition::setVerticalPadding( const Measure & pad ) { d->verticalPadding = pad; } Measure RelativePosition::verticalPadding() const { return d->verticalPadding; } void RelativePosition::setRotation( qreal rot ) { d->rotation = rot; } qreal RelativePosition::rotation() const { return d->rotation; } const QPointF RelativePosition::referencePoint( qreal* polarDegrees ) const { - bool useRect = ( d->area != 0 ); + bool useRect = ( d->area != nullptr ); QRect rect; if ( useRect ) { if ( const QWidget* widget = qobject_cast< const QWidget* >( d->area ) ) { const QLayout* layout = widget->layout(); rect = layout ? layout->geometry() : widget->geometry(); } else if ( const AbstractArea* kdcArea = qobject_cast< const AbstractArea* >( d->area ) ) { rect = kdcArea->geometry(); } else { useRect = false; } } QPointF pt; qreal angle = 0.0; if ( useRect ) { pt = PositionPoints( rect ).point( d->position ); } else { pt = d->points.point( d->position ); angle = d->points.degrees( d->position.value() ); } if ( polarDegrees ) { *polarDegrees = angle; } return pt; } const QPointF RelativePosition::calculatedPoint( const QSizeF& autoSize ) const { const qreal dx = horizontalPadding().calculatedValue( autoSize, KChartEnums::MeasureOrientationHorizontal ); const qreal dy = verticalPadding().calculatedValue( autoSize, KChartEnums::MeasureOrientationVertical ); qreal polarDegrees; QPointF pt( referencePoint( &polarDegrees ) ); if ( polarDegrees == 0.0 ) { pt += QPointF( dx, dy ); } else { const qreal rad = DEGTORAD( polarDegrees); const qreal sinDeg = sin(rad); const qreal cosDeg = cos(rad); pt.setX( pt.x() + dx * cosDeg + dy * sinDeg ); pt.setY( pt.y() - dx * sinDeg + dy * cosDeg ); } return pt; } bool RelativePosition::operator==( const RelativePosition& r ) const { return d->area == r.referenceArea() && d->position == r.referencePosition() && d->alignment == r.alignment() && d->horizontalPadding == r.horizontalPadding() && d->verticalPadding == r.verticalPadding() && d->rotation == r.rotation() ; } #undef d #if !defined(QT_NO_DEBUG_STREAM) QDebug operator<<(QDebug dbg, const KChart::RelativePosition& rp) { dbg << "KChart::RelativePosition(" << "referencearea="<. */ #ifndef KCHARTRELATIVEPOSITION_H #define KCHARTRELATIVEPOSITION_H #include #include #include #include #include #include "KChartGlobal.h" namespace KChart { class Position; class PositionPoints; class Measure; /** \class RelativePosition KChartRelativePosition.h \brief Defines relative position information: reference area, position in this area (reference position), horizontal / vertical padding, and rotation. See detailed description of \a KChart::Position for an illustration of the different possible reference positions. Using RelativePosition you can specify the relative parts of some position information, and you can specify the absolute parts: the reference area, and the position in this area. \note To get an absolute position, you have three options: \li either you declare both, the relative and the absolute parts, using setReferenceArea for the latter, \li or you specify a set of points, using setReferencePoints, \li or you don't use either, leaving it to KChart to find a suitable reference area. */ class KCHART_EXPORT RelativePosition { public: RelativePosition(); RelativePosition( const RelativePosition& ); RelativePosition & operator=( const RelativePosition & other ); ~RelativePosition(); /** * \brief Set the reference area to be used to find the anchor point. * * The reference area's type can be either a QWidget subclass or a KChart::AbstractArea subclass. * * \note Usage of reference area and reference points is mutually exclusive: * Only one can be used, so any previously set reference points are cleared * when you call setReferenceArea. * * Also note: In a few cases KChart will ignore your area (or points, resp.) settings! * Relative positioning of data value texts is an example: For these * the reference area is always taken to be the data area. * * \sa setReferencePosition, setAlignment, setHorizontalPadding, setVerticalPadding */ void setReferenceArea( QObject* area ); QObject* referenceArea() const; /** * \brief Set a set of points from which the anchor point will be selected. * * \note Usage of reference area and reference points is mutually exclusive: * Only one can be used, so any previously set reference area is cleared * when you call setReferencePoints. * * Also note: In a few cases KChart will ignore your points (or area, resp.) settings! * Relative positioning of data value texts is an example: For these * the reference area is always taken to be the data area. * * \sa setReferenceArea, setReferencePosition, setAlignment, setHorizontalPadding, setVerticalPadding */ void setReferencePoints( const PositionPoints& points ); const PositionPoints referencePoints() const; /** * \brief Set the position of the anchor point. * * The anchor point of a RelativePosition may be one of the pre-defined * points of it's reference area - for details see KChart::Position. * * See detailed description of \a KChart::Position for an illustration of the * different possible reference positions. * * \sa resetReferencePosition, setReferenceArea, setAlignment, setHorizontalPadding, setVerticalPadding, KChart::Position */ void setReferencePosition( Position position ); /** * \brief Resets the position of the anchor point to the built-in default. * * If the anchor point of a RelativePosition is reset (or never changed from the * default setting) KChart will choose an appropriate Position at run-time. * * e.g. BarDiagrams will use Position::North / Position::South for positive / negative values. * * \sa setReferencePosition, setReferenceArea, setAlignment, setHorizontalPadding, setVerticalPadding, KChart::Position */ void resetReferencePosition(); Position referencePosition() const; /** * Set the alignment of the content placed by this RelativePosition. * * Padding is applied first to obtain the final reference point * for the content's alignment * * \note To print centered content, besides calling setAlignment( Qt::AlignCenter ) * you might also want to set zero padding to have your text centered more precisely. * * \sa setReferencePosition, setReferenceArea, setHorizontalPadding, setVerticalPadding */ void setAlignment( Qt::Alignment flags ); Qt::Alignment alignment() const; /** * Set the width of the horizontal padding between the anchor point and the content * placed by this RelativePosition. * * \note When printing data value texts this Measure is used to find the alignment * point for this text, then alignment() is use to determine how to align the text * relative to that point. * The font height is used as reference size for both horizontal and vertical padding * if the respective padding's Measure is using automatic reference area detection. * * \sa setVerticalPadding, setReferencePosition, setReferenceArea */ void setHorizontalPadding( const Measure& padding ); Measure horizontalPadding() const; /** * Set the height of the vertical padding between the anchor point and the content * placed by this RelativePosition. * * \note When printing data value texts this Measure is used to find the alignment * point for this text, then alignment() is use to determine how to align the text * relative to that point. * The font height is used as reference size for both horizontal and vertical padding * if the respective padding's Measure is using automatic reference area detection. * * \sa setHorizontalPadding, setReferencePosition, setReferenceArea */ void setVerticalPadding( const Measure& padding ); Measure verticalPadding() const; void setRotation( qreal rot ); qreal rotation() const; /** * \brief Return the reference point, according to the reference area/position, and ignoring padding. * * This method is called at drawing time. * The returned point is used to test if the label of a data value is to be printed: a label * is printed only if its reference point is inside or touching the coordinate plane. * * If polarDegrees is set, the degree information will be returned that was stored for the * respective point. This is used by the PieDiagram class to determine how vertical/horizontal * padding settings should affect the position of the data value texts' reference points. * \sa calculatedPoint, setReferenceArea, setReferencePosition, setHorizontalPadding, setVerticalPadding */ - const QPointF referencePoint(qreal* polarDegrees=0) const; + const QPointF referencePoint(qreal* polarDegrees = nullptr) const; /** * \brief Calculate a point, accordin to the reference area/position and the padding. * * This method is called at drawing time: The returned point is used as anchor point. * Note that it is the task of the calling code to place the content, taking the alignment * property into account. This class does not know the size of the content so it * cannot place it. * * \sa referencePoint, setReferenceArea, setReferencePosition, setHorizontalPadding, setVerticalPadding */ const QPointF calculatedPoint( const QSizeF& autoSize ) const; bool operator==( const RelativePosition& ) const; bool operator!=( const RelativePosition & other ) const; private: KCHART_DECLARE_PRIVATE_BASE_VALUE( RelativePosition ) }; inline bool RelativePosition::operator!=( const RelativePosition & other ) const { return !operator==( other ); } } #if !defined(QT_NO_DEBUG_STREAM) KCHART_EXPORT QDebug operator<<(QDebug, const KChart::RelativePosition& ); #endif /* QT_NO_DEBUG_STREAM */ KCHART_DECLARE_SWAP_SPECIALISATION( KChart::RelativePosition ) QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO( KChart::RelativePosition, Q_MOVABLE_TYPE ); QT_END_NAMESPACE Q_DECLARE_METATYPE( KChart::RelativePosition ) #endif // KCHARTRELATIVEPOSITION_H diff --git a/src/KChart/KChartRulerAttributes.cpp b/src/KChart/KChartRulerAttributes.cpp index f27fb01..8b0ac0a 100644 --- a/src/KChart/KChartRulerAttributes.cpp +++ b/src/KChart/KChartRulerAttributes.cpp @@ -1,319 +1,319 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartRulerAttributes.h" #include "KChartMath_p.h" #include #include #define d d_func() using namespace KChart; class Q_DECL_HIDDEN RulerAttributes::Private { friend class RulerAttributes; public: Private(); private: QPen tickMarkPen; QPen majorTickMarkPen; QPen minorTickMarkPen; bool majorTickMarkPenIsSet : 1; bool minorTickMarkPenIsSet : 1; bool showMajorTickMarks : 1; bool showMinorTickMarks : 1; bool showRulerLine : 1; bool majorTickLengthIsSet : 1; bool minorTickLengthIsSet : 1; bool showFirstTick : 1; int labelMargin; int majorTickLength; int minorTickLength; RulerAttributes::TickMarkerPensMap customTickMarkPens; }; RulerAttributes::Private::Private() : tickMarkPen( Qt::black ) , majorTickMarkPen( Qt::black ) , minorTickMarkPen( Qt::black ) , majorTickMarkPenIsSet( false ) , minorTickMarkPenIsSet( false ) , showMajorTickMarks( true ) , showMinorTickMarks( true ) , showRulerLine( false ) , majorTickLengthIsSet( false ) , minorTickLengthIsSet( false ) , showFirstTick( true ) , labelMargin( -1 ) , majorTickLength( 3 ) , minorTickLength( 2 ) { tickMarkPen.setCapStyle( Qt::FlatCap ); majorTickMarkPen.setCapStyle( Qt::FlatCap ); minorTickMarkPen.setCapStyle( Qt::FlatCap ); } RulerAttributes::RulerAttributes() : _d( new Private() ) { // this bloc left empty intentionally } RulerAttributes::RulerAttributes( const RulerAttributes& r ) : _d( new Private( *r.d ) ) { } void RulerAttributes::setTickMarkPen( const QPen& pen ) { d->tickMarkPen = pen; } QPen RulerAttributes::tickMarkPen() const { return d->tickMarkPen; } void RulerAttributes::setMajorTickMarkPen( const QPen& pen ) { d->majorTickMarkPen = pen; d->majorTickMarkPenIsSet = true; } bool RulerAttributes::majorTickMarkPenIsSet() const { return d->majorTickMarkPenIsSet; } QPen RulerAttributes::majorTickMarkPen() const { return d->majorTickMarkPenIsSet ? d->majorTickMarkPen : d->tickMarkPen; } void RulerAttributes::setMinorTickMarkPen( const QPen& pen ) { d->minorTickMarkPen = pen; d->minorTickMarkPenIsSet = true; } bool RulerAttributes::minorTickMarkPenIsSet() const { return d->minorTickMarkPenIsSet; } QPen RulerAttributes::minorTickMarkPen() const { return d->minorTickMarkPenIsSet ? d->minorTickMarkPen : d->tickMarkPen; } void RulerAttributes::setTickMarkPen( qreal value, const QPen& pen ) { if ( !d->customTickMarkPens.contains( value ) ) d->customTickMarkPens.insert( value, pen ); } QPen RulerAttributes::tickMarkPen( qreal value ) const { QMapIterator it( d->customTickMarkPens ); while ( it.hasNext() ) { it.next(); if ( qAbs( value - it.key() ) < std::numeric_limits< float >::epsilon() ) return it.value(); } return d->tickMarkPen; } RulerAttributes::TickMarkerPensMap RulerAttributes::tickMarkPens() const { return d->customTickMarkPens; } bool RulerAttributes::hasTickMarkPenAt( qreal value ) const { QMapIterator it( d->customTickMarkPens ); while ( it.hasNext() ) { it.next(); if ( qAbs( value - it.key() ) < std::numeric_limits< float >::epsilon() ) return true; } return false; } void RulerAttributes::setTickMarkColor( const QColor& color ) { d->tickMarkPen.setColor( color ); } QColor RulerAttributes::tickMarkColor() const { return d->tickMarkPen.color(); } void RulerAttributes::setShowMajorTickMarks( bool show ) { d->showMajorTickMarks = show; } bool RulerAttributes::showMajorTickMarks() const { return d->showMajorTickMarks; } void RulerAttributes::setShowMinorTickMarks( bool show ) { d->showMinorTickMarks = show; } bool RulerAttributes::showMinorTickMarks() const { return d->showMinorTickMarks; } void RulerAttributes::setLabelMargin(int margin) { d->labelMargin = margin; } int RulerAttributes::labelMargin() const { return d->labelMargin; } void RulerAttributes::setMajorTickMarkLength( int length ) { d->majorTickLength = length; d->majorTickLengthIsSet = true; } int RulerAttributes::majorTickMarkLength() const { return d->majorTickLength; } bool RulerAttributes::majorTickMarkLengthIsSet() const { return d->majorTickLengthIsSet; } void RulerAttributes::setMinorTickMarkLength( int length ) { d->minorTickLength = length; d->minorTickLengthIsSet = true; } int RulerAttributes::minorTickMarkLength() const { return d->minorTickLength; } bool RulerAttributes::minorTickMarkLengthIsSet() const { return d->minorTickLengthIsSet; } void RulerAttributes::setShowFirstTick( bool show ) { d->showFirstTick = show; } bool RulerAttributes::showFirstTick() const { return d->showFirstTick; } RulerAttributes & RulerAttributes::operator=( const RulerAttributes& r ) { if ( this == &r ) return *this; *d = *r.d; return *this; } RulerAttributes::~RulerAttributes() { - delete _d; _d = 0; + delete _d; _d = nullptr; } bool RulerAttributes::operator==( const RulerAttributes& r ) const { bool isEqual = tickMarkPen() == r.tickMarkPen() && majorTickMarkPen() == r.majorTickMarkPen() && minorTickMarkPen() == r.minorTickMarkPen() && majorTickMarkPenIsSet() == r.majorTickMarkPenIsSet() && minorTickMarkPenIsSet() == r.minorTickMarkPenIsSet() && showMajorTickMarks() == r.showMajorTickMarks() && showMinorTickMarks() == r.showMinorTickMarks() && showRulerLine() == r.showRulerLine() && majorTickMarkLengthIsSet() == r.majorTickMarkLengthIsSet() && minorTickMarkLengthIsSet() == r.minorTickMarkLengthIsSet() && showFirstTick() == r.showFirstTick() && d->customTickMarkPens.size() == r.d->customTickMarkPens.size(); if ( !isEqual ) { return false; } QMap< qreal, QPen >::ConstIterator it = d->customTickMarkPens.constBegin(); QMap< qreal, QPen >::ConstIterator it2 = r.d->customTickMarkPens.constBegin(); for ( ; it != d->customTickMarkPens.constEnd(); ++it, ++it2 ) { if ( it.key() != it2.key() || it.value() != it2.value() ) { return false; } } return true; } void RulerAttributes::setShowRulerLine( bool show ) { d->showRulerLine = show; } bool RulerAttributes::showRulerLine() const { return d->showRulerLine; } #if !defined( QT_NO_DEBUG_STREAM ) QDebug operator << ( QDebug dbg, const KChart::RulerAttributes& a ) { dbg << "KChart::RulerAttributes(" << "tickMarkPen=" << a.tickMarkPen() << "majorTickMarkPen=" << a.majorTickMarkPen() << "minorTickMarkPen=" << a.minorTickMarkPen(); const RulerAttributes::TickMarkerPensMap pens( a.tickMarkPens() ); QMapIterator it( pens ); while ( it.hasNext() ) { it.next(); dbg << "customTickMarkPen=(" << it.value() << " : " << it.key() << ")"; } dbg << ")"; return dbg; } #endif /* QT_NO_DEBUG_STREAM */ diff --git a/src/KChart/KChartTextAttributes.cpp b/src/KChart/KChartTextAttributes.cpp index 41bf15d..76cf0c4 100644 --- a/src/KChart/KChartTextAttributes.cpp +++ b/src/KChart/KChartTextAttributes.cpp @@ -1,288 +1,288 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartTextAttributes.h" #include #include #include "KChartMath_p.h" #include #include #include #include #include #include #define d d_func() using namespace KChart; class Q_DECL_HIDDEN TextAttributes::Private { friend class TextAttributes; public: Private(); private: bool visible; QFont font; mutable QFont cachedFont; mutable qreal cachedFontSize; Measure fontSize; Measure minimalFontSize; bool autoRotate; bool autoShrink; bool hasRotation; int rotation; QPen pen; QSharedPointer document; }; TextAttributes::Private::Private() : visible( true ), font( QApplication::font() ), cachedFontSize( -1.0 ), autoRotate( false ), autoShrink( false ), hasRotation( false ), rotation( 0 ), pen( Qt::black ) { } TextAttributes::TextAttributes() : _d( new Private() ) { } TextAttributes::TextAttributes( const TextAttributes& r ) : _d( new Private( *r.d ) ) { } TextAttributes & TextAttributes::operator=( const TextAttributes& r ) { if ( this == &r ) return *this; *d = *r.d; return *this; } TextAttributes::~TextAttributes() { - delete _d; _d = 0; + delete _d; _d = nullptr; } bool TextAttributes::operator==( const TextAttributes& r ) const { // the following works around a bug in gcc 4.3.2 // causing StyleHint to be set to Zero when copying a QFont const QFont myFont( font() ); QFont r_font( r.font() ); r_font.setStyleHint( myFont.styleHint(), myFont.styleStrategy() ); return ( isVisible() == r.isVisible() && myFont == r_font && fontSize() == r.fontSize() && minimalFontSize() == r.minimalFontSize() && autoRotate() == r.autoRotate() && autoShrink() == r.autoShrink() && rotation() == r.rotation() && pen() == r.pen() && textDocument() == r.textDocument() ); } void TextAttributes::setVisible( bool visible ) { d->visible = visible; } bool TextAttributes::isVisible() const { return d->visible; } void TextAttributes::setFont( const QFont& font ) { d->font = font; d->cachedFont = font; // note: we do not set the font's size here, but in calculatedFont() d->cachedFontSize = -1.0; } QFont TextAttributes::font() const { return d->font; } void TextAttributes::setFontSize( const Measure & measure ) { d->fontSize = measure; } Measure TextAttributes::fontSize() const { return d->fontSize; } void TextAttributes::setMinimalFontSize( const Measure & measure ) { d->minimalFontSize = measure; } Measure TextAttributes::minimalFontSize() const { return d->minimalFontSize; } bool TextAttributes::hasAbsoluteFontSize() const { return d->fontSize.calculationMode() == KChartEnums::MeasureCalculationModeAbsolute && d->minimalFontSize.calculationMode() == KChartEnums::MeasureCalculationModeAbsolute; } qreal TextAttributes::calculatedFontSize( const QSizeF &referenceSize, KChartEnums::MeasureOrientation autoReferenceOrientation ) const { const qreal normalSize = fontSize().calculatedValue( referenceSize, autoReferenceOrientation ); const qreal minimalSize = minimalFontSize().calculatedValue( referenceSize, autoReferenceOrientation ); return qMax( normalSize, minimalSize ); } #if defined(Q_COMPILER_MANGLES_RETURN_TYPE) const #endif qreal TextAttributes::calculatedFontSize( const QObject* autoReferenceArea, KChartEnums::MeasureOrientation autoReferenceOrientation ) const { const qreal normalSize = fontSize().calculatedValue( autoReferenceArea, autoReferenceOrientation ); const qreal minimalSize = minimalFontSize().calculatedValue( autoReferenceArea, autoReferenceOrientation ); return qMax( normalSize, minimalSize ); } const QFont TextAttributes::calculatedFont( const QObject* autoReferenceArea, KChartEnums::MeasureOrientation autoReferenceOrientation ) const { qreal size = NaN; const CartesianCoordinatePlane* plane = qobject_cast< const CartesianCoordinatePlane* >( autoReferenceArea ); if ( plane && plane->hasFixedDataCoordinateSpaceRelation() ) { // HACK // if hasFixedDataCoordinateSpaceRelation, we use a zoom trick to keep the diagram at a constant size // even when the plane size changes. calculatedFontSize() usually uses the plane size, not the diagram // size, to determine the font size. here we need to give it the diagram size in order to make the font // size constant, too. see KDCHDEV-219. CartesianCoordinatePlane::Private *priv = CartesianCoordinatePlane::Private::get( const_cast< CartesianCoordinatePlane * >( plane ) ); size = calculatedFontSize( priv->fixedDataCoordinateSpaceRelationPinnedSize, autoReferenceOrientation ); } else { size = calculatedFontSize( autoReferenceArea, autoReferenceOrientation ); } if ( size > 0.0 && d->cachedFontSize != size ) { d->cachedFontSize = size; d->cachedFont.setPointSizeF( d->cachedFontSize ); } return d->cachedFont; } void TextAttributes::setAutoRotate( bool autoRotate ) { d->autoRotate = autoRotate; } bool TextAttributes::autoRotate() const { return d->autoRotate; } void TextAttributes::setAutoShrink( bool autoShrink ) { d->autoShrink = autoShrink; } bool TextAttributes::autoShrink() const { return d->autoShrink; } void TextAttributes::setRotation( int rotation ) { d->hasRotation = true; d->rotation = rotation; } int TextAttributes::rotation() const { return d->rotation; } void TextAttributes::resetRotation() { d->hasRotation = false; d->rotation = 0; } bool TextAttributes::hasRotation() const { return d->hasRotation; } void TextAttributes::setPen( const QPen& pen ) { d->pen = pen; } QPen TextAttributes::pen() const { return d->pen; } QTextDocument* TextAttributes::textDocument() const { return d->document.data(); } void TextAttributes::setTextDocument(QTextDocument* document) { d->document = QSharedPointer(document); } #if !defined(QT_NO_DEBUG_STREAM) QDebug operator<<(QDebug dbg, const KChart::TextAttributes& ta) { dbg << "KChart::TextAttributes(" << "visible=" << ta.isVisible() << "font=" << ta.font().toString() /* What? No QDebug for QFont? */ << "fontsize=" << ta.fontSize() << "minimalfontsize=" << ta.minimalFontSize() << "autorotate=" << ta.autoRotate() << "autoshrink=" << ta.autoShrink() << "rotation=" << ta.rotation() << "pen=" << ta.pen() << ")"; return dbg; } #endif /* QT_NO_DEBUG_STREAM */ diff --git a/src/KChart/KChartValueTrackerAttributes.cpp b/src/KChart/KChartValueTrackerAttributes.cpp index b1745ae..9955a92 100644 --- a/src/KChart/KChartValueTrackerAttributes.cpp +++ b/src/KChart/KChartValueTrackerAttributes.cpp @@ -1,202 +1,202 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartValueTrackerAttributes.h" #include "KChartMath_p.h" #include #include #include #define d d_func() using namespace KChart; class Q_DECL_HIDDEN ValueTrackerAttributes::Private { friend class ValueTrackerAttributes; public: Private(); private: QPen linePen; QPen markerPen; QBrush markerBrush; QBrush arrowBrush; QSizeF markerSize; bool enabled; QBrush areaBrush; Qt::Orientations orientations; }; ValueTrackerAttributes::Private::Private() : linePen( QPen( QColor( 80, 80, 80, 200 ) ) ), markerSize( QSizeF( 6.0, 6.0 ) ), enabled( false ), areaBrush( QBrush() ), orientations(Qt::Vertical|Qt::Horizontal) { markerPen = linePen; arrowBrush = linePen.color(); } ValueTrackerAttributes::ValueTrackerAttributes() : _d( new Private() ) { } ValueTrackerAttributes::ValueTrackerAttributes( const ValueTrackerAttributes& r ) : _d( new Private( *r.d ) ) { } ValueTrackerAttributes & ValueTrackerAttributes::operator=( const ValueTrackerAttributes& r ) { if ( this == &r ) return *this; *d = *r.d; return *this; } ValueTrackerAttributes::~ValueTrackerAttributes() { - delete _d; _d = 0; + delete _d; _d = nullptr; } bool ValueTrackerAttributes::operator==( const ValueTrackerAttributes& r ) const { return ( linePen() == r.linePen() && markerPen() == r.markerPen() && markerBrush() == r.markerBrush() && arrowBrush() == r.arrowBrush() && areaBrush() == r.areaBrush() && markerSize() == r.markerSize() && isEnabled() == r.isEnabled() ); } void ValueTrackerAttributes::setPen( const QPen& pen ) { d->linePen = pen; d->markerPen = pen; d->markerBrush = QBrush(); d->arrowBrush = pen.color(); } QPen ValueTrackerAttributes::pen() const { return d->linePen; } void ValueTrackerAttributes::setLinePen( const QPen &pen ) { d->linePen = pen; } QPen ValueTrackerAttributes::linePen() const { return d->linePen; } void ValueTrackerAttributes::setMarkerPen( const QPen &pen ) { d->markerPen = pen; } QPen ValueTrackerAttributes::markerPen() const { return d->markerPen; } void ValueTrackerAttributes::setMarkerBrush( const QBrush &brush ) { d->markerBrush = brush; } QBrush ValueTrackerAttributes::markerBrush() const { return d->markerBrush; } void ValueTrackerAttributes::setArrowBrush( const QBrush &brush ) { d->arrowBrush = brush; } QBrush ValueTrackerAttributes::arrowBrush() const { return d->arrowBrush; } void ValueTrackerAttributes::setAreaBrush( const QBrush& brush ) { d->areaBrush = brush; } QBrush ValueTrackerAttributes::areaBrush() const { return d->areaBrush; } void ValueTrackerAttributes::setMarkerSize( const QSizeF& size ) { d->markerSize = size; } QSizeF ValueTrackerAttributes::markerSize() const { return d->markerSize; } Qt::Orientations ValueTrackerAttributes::orientations() const { return d->orientations; } void ValueTrackerAttributes::setOrientations( Qt::Orientations orientations ) { d->orientations = orientations; } void ValueTrackerAttributes::setEnabled( bool enabled ) { d->enabled = enabled; } bool ValueTrackerAttributes::isEnabled() const { return d->enabled; } #if !defined(QT_NO_DEBUG_STREAM) QDebug operator<<(QDebug dbg, const KChart::ValueTrackerAttributes& va) { dbg << "KChart::ValueTrackerAttributes(" << "linePen="<. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "KChartMath_p.h" #include #define d d_func() using namespace KChart; Widget::Private::Private( Widget * qq ) : q( qq ), layout( q ), m_model( q ), m_chart( q ), m_cartPlane( &m_chart ), m_polPlane( &m_chart ), usedDatasetWidth( 0 ) { KDAB_SET_OBJECT_NAME( layout ); KDAB_SET_OBJECT_NAME( m_model ); KDAB_SET_OBJECT_NAME( m_chart ); layout.addWidget( &m_chart ); } Widget::Private::~Private() {} /** * \class Widget KChartWidget.h * \brief The KChart widget for usage without Interwiev. * * If you want to use KChart with Interview, use KChart::Chart instead. */ /** * Constructor. Creates a new widget with all data initialized empty. * * \param parent the widget parent; passed on to QWidget */ Widget::Widget( QWidget* parent ) : QWidget(parent), _d( new Private( this ) ) { // as default we have a cartesian coordinate plane ... // ... and a line diagram setType( Line ); } /** * Destructor. */ Widget::~Widget() { - delete _d; _d = 0; + delete _d; _d = nullptr; } void Widget::init() { } void Widget::setDataset( int column, const QVector< qreal > & data, const QString& title ) { if ( ! checkDatasetWidth( 1 ) ) return; QStandardItemModel & model = d->m_model; justifyModelSize( data.size(), column + 1 ); for ( int i = 0; i < data.size(); ++i ) { const QModelIndex index = model.index( i, column ); model.setData( index, QVariant( data[i] ), Qt::DisplayRole ); } if ( ! title.isEmpty() ) model.setHeaderData( column, Qt::Horizontal, QVariant( title ) ); } void Widget::setDataset( int column, const QVector< QPair< qreal, qreal > > & data, const QString& title ) { if ( ! checkDatasetWidth( 2 )) return; QStandardItemModel & model = d->m_model; justifyModelSize( data.size(), (column + 1) * 2 ); for ( int i = 0; i < data.size(); ++i ) { QModelIndex index = model.index( i, column * 2 ); model.setData( index, QVariant( data[i].first ), Qt::DisplayRole ); index = model.index( i, column * 2 + 1 ); model.setData( index, QVariant( data[i].second ), Qt::DisplayRole ); } if ( ! title.isEmpty() ) { model.setHeaderData( column, Qt::Horizontal, QVariant( title ) ); } } void Widget::setDataCell( int row, int column, qreal data ) { if ( ! checkDatasetWidth( 1 ) ) return; QStandardItemModel & model = d->m_model; justifyModelSize( row + 1, column + 1 ); const QModelIndex index = model.index( row, column ); model.setData( index, QVariant( data ), Qt::DisplayRole ); } void Widget::setDataCell( int row, int column, QPair< qreal, qreal > data ) { if ( ! checkDatasetWidth( 2 )) return; QStandardItemModel & model = d->m_model; justifyModelSize( row + 1, (column + 1) * 2 ); QModelIndex index = model.index( row, column * 2 ); model.setData( index, QVariant( data.first ), Qt::DisplayRole ); index = model.index( row, column * 2 + 1 ); model.setData( index, QVariant( data.second ), Qt::DisplayRole ); } /* * Resets all data. */ void Widget::resetData() { d->m_model.clear(); d->usedDatasetWidth = 0; } /** * Sets all global leadings (borders). */ void Widget::setGlobalLeading( int left, int top, int right, int bottom ) { d->m_chart.setGlobalLeading( left, top, right, bottom ); } /** * Sets the left leading (border). */ void Widget::setGlobalLeadingLeft( int leading ) { d->m_chart.setGlobalLeadingLeft( leading ); } /** * Returns the left leading (border). */ int Widget::globalLeadingLeft() const { return d->m_chart.globalLeadingLeft(); } /** * Sets the top leading (border). */ void Widget::setGlobalLeadingTop( int leading ) { d->m_chart.setGlobalLeadingTop( leading ); } /** * Returns the top leading (border). */ int Widget::globalLeadingTop() const { return d->m_chart.globalLeadingTop(); } /** * Sets the right leading (border). */ void Widget::setGlobalLeadingRight( int leading ) { d->m_chart.setGlobalLeadingRight( leading ); } /** * Returns the right leading (border). */ int Widget::globalLeadingRight() const { return d->m_chart.globalLeadingRight(); } /** * Sets the bottom leading (border). */ void Widget::setGlobalLeadingBottom( int leading ) { d->m_chart.setGlobalLeadingBottom( leading ); } /** * Returns the bottom leading (border). */ int Widget::globalLeadingBottom() const { return d->m_chart.globalLeadingBottom(); } /** * Returns the first of all headers. */ KChart::HeaderFooter* Widget::firstHeaderFooter() { return d->m_chart.headerFooter(); } /** * Returns a list with all headers. */ QList Widget::allHeadersFooters() { return d->m_chart.headerFooters(); } /** * Adds a new header/footer with the given text to the position. */ void Widget::addHeaderFooter( const QString& text, HeaderFooter::HeaderFooterType type, Position position) { HeaderFooter* newHeader = new HeaderFooter( &d->m_chart ); newHeader->setType( type ); newHeader->setPosition( position ); newHeader->setText( text ); d->m_chart.addHeaderFooter( newHeader ); // we need this explicit call ! } /** * Adds an existing header / footer object. */ void Widget::addHeaderFooter( HeaderFooter* header ) { header->setParent( &d->m_chart ); d->m_chart.addHeaderFooter( header ); // we need this explicit call ! } void Widget::replaceHeaderFooter( HeaderFooter* header, HeaderFooter* oldHeader ) { header->setParent( &d->m_chart ); d->m_chart.replaceHeaderFooter( header, oldHeader ); } void Widget::takeHeaderFooter( HeaderFooter* header ) { d->m_chart.takeHeaderFooter( header ); } /** * Returns the first of all legends. */ KChart::Legend* Widget::legend() { return d->m_chart.legend(); } /** * Returns a list with all legends. */ QList Widget::allLegends() { return d->m_chart.legends(); } /** * Adds an empty legend on the given position. */ void Widget::addLegend( Position position ) { Legend* legend = new Legend( diagram(), &d->m_chart ); legend->setPosition( position ); d->m_chart.addLegend( legend ); } /** * Adds a new, already existing, legend. */ void Widget::addLegend( Legend* legend ) { legend->setDiagram( diagram() ); legend->setParent( &d->m_chart ); d->m_chart.addLegend( legend ); } void Widget::replaceLegend( Legend* legend, Legend* oldLegend ) { legend->setDiagram( diagram() ); legend->setParent( &d->m_chart ); d->m_chart.replaceLegend( legend, oldLegend ); } void Widget::takeLegend( Legend* legend ) { d->m_chart.takeLegend( legend ); } AbstractDiagram* Widget::diagram() { - if ( coordinatePlane() == 0 ) + if ( coordinatePlane() == nullptr ) qDebug() << "diagram(): coordinatePlane() was NULL"; return coordinatePlane()->diagram(); } BarDiagram* Widget::barDiagram() { return dynamic_cast( diagram() ); } LineDiagram* Widget::lineDiagram() { return dynamic_cast( diagram() ); } Plotter* Widget::plotter() { return dynamic_cast( diagram() ); } PieDiagram* Widget::pieDiagram() { return dynamic_cast( diagram() ); } RingDiagram* Widget::ringDiagram() { return dynamic_cast( diagram() ); } PolarDiagram* Widget::polarDiagram() { return dynamic_cast( diagram() ); } AbstractCoordinatePlane* Widget::coordinatePlane() { return d->m_chart.coordinatePlane(); } static bool isCartesian( KChart::Widget::ChartType type ) { return (type == KChart::Widget::Bar) || (type == KChart::Widget::Line); } static bool isPolar( KChart::Widget::ChartType type ) { return (type == KChart::Widget::Pie) || (type == KChart::Widget::Ring) || (type == KChart::Widget::Polar); } void Widget::setType( ChartType chartType, SubType chartSubType ) { - AbstractDiagram* diag = 0; + AbstractDiagram* diag = nullptr; const ChartType oldType = type(); if ( chartType != oldType ) { if ( chartType != NoType ) { if ( isCartesian( chartType ) && ! isCartesian( oldType ) ) { if ( coordinatePlane() == &d->m_polPlane ) { d->m_chart.takeCoordinatePlane( &d->m_polPlane ); d->m_chart.addCoordinatePlane( &d->m_cartPlane ); } else { d->m_chart.replaceCoordinatePlane( &d->m_cartPlane ); } } else if ( isPolar( chartType ) && ! isPolar( oldType ) ) { if ( coordinatePlane() == &d->m_cartPlane ) { d->m_chart.takeCoordinatePlane( &d->m_cartPlane ); d->m_chart.addCoordinatePlane( &d->m_polPlane ); } else { d->m_chart.replaceCoordinatePlane( &d->m_polPlane ); } } } switch ( chartType ) { case Bar: diag = new BarDiagram( &d->m_chart, &d->m_cartPlane ); break; case Line: diag = new LineDiagram( &d->m_chart, &d->m_cartPlane ); break; case Plot: diag = new Plotter( &d->m_chart, &d->m_cartPlane ); break; case Pie: diag = new PieDiagram( &d->m_chart, &d->m_polPlane ); break; case Polar: diag = new PolarDiagram( &d->m_chart, &d->m_polPlane ); break; case Ring: diag = new RingDiagram( &d->m_chart, &d->m_polPlane ); break; case NoType: break; } - if ( diag != NULL ) { + if ( diag != nullptr ) { if ( isCartesian( oldType ) && isCartesian( chartType ) ) { AbstractCartesianDiagram *oldDiag = qobject_cast( coordinatePlane()->diagram() ); AbstractCartesianDiagram *newDiag = qobject_cast( diag ); Q_FOREACH( CartesianAxis* axis, oldDiag->axes() ) { oldDiag->takeAxis( axis ); newDiag->addAxis ( axis ); } } Q_FOREACH( Legend* l, d->m_chart.legends() ) { l->setDiagram( diag ); } diag->setModel( &d->m_model ); coordinatePlane()->replaceDiagram( diag ); //checkDatasetWidth( d->usedDatasetWidth ); } //coordinatePlane()->setGridNeedsRecalculate(); } if ( chartType != NoType ) { if ( chartType != oldType || chartSubType != subType() ) setSubType( chartSubType ); d->m_chart.resize( size() ); // triggering immediate update } } template< class DiagramType, class Subtype > void setSubtype( AbstractDiagram *_dia, Subtype st) { if ( DiagramType *dia = qobject_cast< DiagramType * >( _dia ) ) { dia->setType( st ); } } void Widget::setSubType( SubType subType ) { // ### at least PieDiagram, PolarDiagram and RingDiagram are unhandled here AbstractDiagram *dia = diagram(); switch ( subType ) { case Normal: setSubtype< BarDiagram >( dia, BarDiagram::Normal ); setSubtype< LineDiagram >( dia, LineDiagram::Normal ); setSubtype< Plotter >( dia, Plotter::Normal ); break; case Stacked: setSubtype< BarDiagram >( dia, BarDiagram::Stacked ); setSubtype< LineDiagram >( dia, LineDiagram::Stacked ); // setSubtype< Plotter >( dia, Plotter::Stacked ); break; case Percent: setSubtype< BarDiagram >( dia, BarDiagram::Percent ); setSubtype< LineDiagram >( dia, LineDiagram::Percent ); setSubtype< Plotter >( dia, Plotter::Percent ); break; case Rows: setSubtype< BarDiagram >( dia, BarDiagram::Rows ); break; default: Q_ASSERT_X ( false, "Widget::setSubType", "Sub-type not supported!" ); break; } } /** * Returns the type of the chart. */ Widget::ChartType Widget::type() const { // PENDING(christoph) save the type out-of-band: AbstractDiagram * const dia = const_cast( this )->diagram(); if ( qobject_cast< BarDiagram* >( dia ) ) return Bar; else if ( qobject_cast< LineDiagram* >( dia ) ) return Line; else if ( qobject_cast< Plotter* >( dia ) ) return Plot; else if ( qobject_cast< PieDiagram* >( dia ) ) return Pie; else if ( qobject_cast< PolarDiagram* >( dia ) ) return Polar; else if ( qobject_cast< RingDiagram* >( dia ) ) return Ring; else return NoType; } Widget::SubType Widget::subType() const { // PENDING(christoph) save the type out-of-band: Widget::SubType retVal = Normal; AbstractDiagram * const dia = const_cast( this )->diagram(); BarDiagram* barDia = qobject_cast< BarDiagram* >( dia ); LineDiagram* lineDia = qobject_cast< LineDiagram* >( dia ); Plotter* plotterDia = qobject_cast< Plotter* >( dia ); //FIXME(khz): Add the impl for these chart types - or remove them from here: // PieDiagram* pieDia = qobject_cast< PieDiagram* >( diagram() ); // PolarDiagram* polarDia = qobject_cast< PolarDiagram* >( diagram() ); // RingDiagram* ringDia = qobject_cast< RingDiagram* >( diagram() ); #define TEST_SUB_TYPE(DIAGRAM, INTERNALSUBTYPE, SUBTYPE) \ { \ if ( DIAGRAM && DIAGRAM->type() == INTERNALSUBTYPE ) \ retVal = SUBTYPE; \ } const Widget::ChartType mainType = type(); switch ( mainType ) { case Bar: TEST_SUB_TYPE( barDia, BarDiagram::Normal, Normal ); TEST_SUB_TYPE( barDia, BarDiagram::Stacked, Stacked ); TEST_SUB_TYPE( barDia, BarDiagram::Percent, Percent ); TEST_SUB_TYPE( barDia, BarDiagram::Rows, Rows ); break; case Line: TEST_SUB_TYPE( lineDia, LineDiagram::Normal, Normal ); TEST_SUB_TYPE( lineDia, LineDiagram::Stacked, Stacked ); TEST_SUB_TYPE( lineDia, LineDiagram::Percent, Percent ); break; case Plot: TEST_SUB_TYPE( plotterDia, Plotter::Normal, Normal ); TEST_SUB_TYPE( plotterDia, Plotter::Percent, Percent ); break; case Pie: // no impl. yet break; case Polar: // no impl. yet break; case Ring: // no impl. yet break; default: Q_ASSERT_X ( false, "Widget::subType", "Chart type not supported!" ); break; } return retVal; } /** * Checks whether the given width matches with the one used until now. */ bool Widget::checkDatasetWidth( int width ) { if ( width == diagram()->datasetDimension() ) { d->usedDatasetWidth = width; return true; } qDebug() << "The current diagram type doesn't support this data dimension."; return false; /* if ( d->usedDatasetWidth == width || d->usedDatasetWidth == 0 ) { d->usedDatasetWidth = width; diagram()->setDatasetDimension( width ); return true; } qDebug() << "It's impossible to mix up the different setDataset() methods on the same widget."; return false;*/ } /** * Justifies the model, so that the given rows and columns fit into it. */ void Widget::justifyModelSize( int rows, int columns ) { QAbstractItemModel & model = d->m_model; const int currentRows = model.rowCount(); const int currentCols = model.columnCount(); if ( currentCols < columns ) if ( ! model.insertColumns( currentCols, columns - currentCols )) qDebug() << "justifyModelSize: could not increase model size."; if ( currentRows < rows ) if ( ! model.insertRows( currentRows, rows - currentRows )) qDebug() << "justifyModelSize: could not increase model size."; Q_ASSERT( model.rowCount() >= rows ); Q_ASSERT( model.columnCount() >= columns ); } diff --git a/src/KChart/KChartWidget.h b/src/KChart/KChartWidget.h index e1e822a..d550b52 100644 --- a/src/KChart/KChartWidget.h +++ b/src/KChart/KChartWidget.h @@ -1,239 +1,239 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __KCHARTWIDGET_H__ #define __KCHARTWIDGET_H__ #include "KChartGlobal.h" #include #include "KChartEnums.h" #include "KChartHeaderFooter.h" QT_BEGIN_NAMESPACE template class QVector; template struct QPair; QT_END_NAMESPACE namespace KChart { // some forward declarations class AbstractDiagram; class Chart; class AbstractCoordinatePlane; class TableModel; class BarDiagram; class LineDiagram; class Plotter; class PieDiagram; class RingDiagram; class PolarDiagram; class Legend; class Position; /** * \class Widget KChartWidget.h * \brief The KChart widget for usage without Model/View. * * If you want to use KChart with Model/View, use KChart::Chart instead. */ class KCHART_EXPORT Widget : public QWidget { Q_OBJECT Q_DISABLE_COPY( Widget ) KCHART_DECLARE_PRIVATE_BASE_POLYMORPHIC_QWIDGET( Widget ) public: /** * Standard Qt-style Constructor * * Creates a new widget with all data initialized empty. * * \param parent the widget parent; passed on to QWidget */ - explicit Widget( QWidget* parent = 0 ); + explicit Widget( QWidget* parent = nullptr ); /** Destructor. */ ~Widget(); /** Sets the data in the given column using a QVector of qreal for the Y values. */ void setDataset( int column, const QVector< qreal > & data, const QString& title = QString() ); /** Sets the data in the given column using a QVector of QPairs * of qreal for the (X, Y) values. */ void setDataset( int column, const QVector< QPair< qreal, qreal > > & data, const QString& title = QString() ); /** Sets the Y value data for a given cell. */ void setDataCell( int row, int column, qreal data ); /** Sets the data for a given column using an (X, Y) QPair of qreals. */ void setDataCell( int row, int column, QPair< qreal, qreal > data ); /** Resets all data. */ void resetData(); public Q_SLOTS: /** Sets all global leadings (borders). */ void setGlobalLeading( int left, int top, int right, int bottom ); /** Sets the left leading (border). */ void setGlobalLeadingLeft( int leading ); /** Sets the top leading (border). */ void setGlobalLeadingTop( int leading ); /** Sets the right leading (border). */ void setGlobalLeadingRight( int leading ); /** Sets the bottom leading (border). */ void setGlobalLeadingBottom( int leading ); public: /** Returns the left leading (border). */ int globalLeadingLeft() const; /** Returns the top leading (border). */ int globalLeadingTop() const; /** Returns the right leading (border). */ int globalLeadingRight() const; /** Returns the bottom leading (border). */ int globalLeadingBottom() const; /** Returns the first of all headers. */ HeaderFooter* firstHeaderFooter(); /** Returns a list with all headers. */ QList allHeadersFooters(); /** Adds a new header/footer with the given text to the position. */ void addHeaderFooter( const QString& text, HeaderFooter::HeaderFooterType type, Position position ); /** * Adds the existing header / footer object \a header. * \sa replaceHeaderFooter, takeHeaderFooter */ void addHeaderFooter( HeaderFooter* header ); /** * Replaces the old header (or footer, resp.), or appends the * new header or footer, it there is none yet. * * @param header The header or footer to be used instead of the old one. * This parameter must not be zero, or the method will do nothing. * * @param oldHeader The header or footer to be removed by the new one. This * header or footer will be deleted automatically. If the parameter is omitted, * the very first header or footer will be replaced. In case, there was no * header and no footer yet, the new header or footer will just be added. * * \note If you want to re-use the old header or footer, call takeHeaderFooter and * addHeaderFooter, instead of using replaceHeaderFooter. * * \sa addHeaderFooter, takeHeaderFooter */ void replaceHeaderFooter( HeaderFooter* header, - HeaderFooter* oldHeader = 0 ); + HeaderFooter* oldHeader = nullptr ); /** Remove the header (or footer, resp.) from the widget, * without deleting it. * The chart no longer owns the header or footer, so it is * the caller's responsibility to delete the header or footer. * * \sa addHeaderFooter, replaceHeaderFooter */ void takeHeaderFooter( HeaderFooter* header ); /** Returns the first of all legends. */ Legend* legend(); /** Returns a list with all legends. */ QList allLegends(); /** Adds an empty legend on the given position. */ void addLegend( Position position ); /** Adds a new, already existing, legend. */ void addLegend (Legend* legend ); - void replaceLegend( Legend* legend, Legend* oldLegend = 0 ); + void replaceLegend( Legend* legend, Legend* oldLegend = nullptr ); void takeLegend( Legend* legend ); /** Returns a pointer to the current diagram. */ AbstractDiagram* diagram(); /** If the current diagram is a BarDiagram, it is returnd; otherwise 0 is returned. * This function provides type-safe casting. */ BarDiagram* barDiagram(); /** If the current diagram is a LineDiagram, it is returnd; otherwise 0 is returned. * This function provides type-safe casting. */ LineDiagram* lineDiagram(); /** If the current diagram is a LineDiagram, it is returnd; otherwise 0 is returned. * This function provides type-safe casting. * * \note Do not use lineDiagram for multi-dimensional diagrams, but use plotter instead * * \sa plotter */ Plotter* plotter(); /** If the current diagram is a Plotter, it is returnd; otherwise 0 is returned. * This function provides type-safe casting. */ PieDiagram* pieDiagram(); /** If the current diagram is a RingDiagram, it is returnd; otherwise 0 is returned. * This function provides type-safe casting. */ RingDiagram* ringDiagram(); /** If the current diagram is a PolarDiagram, it is returnd; otherwise 0 is returned. * This function provides type-safe casting. */ PolarDiagram* polarDiagram(); /** Returns a pointer to the current coordinate plane. */ AbstractCoordinatePlane* coordinatePlane(); enum ChartType { NoType, Bar, Line, Plot, Pie, Ring, Polar }; /** Returns the type of the chart. */ ChartType type() const; /** Sub type values, matching the values defines for the respective Diagram classes. */ enum SubType { Normal, Stacked, Percent, Rows }; /** Returns the sub-type of the chart. */ SubType subType() const; public Q_SLOTS: /** Sets the type of the chart. */ void setType( ChartType chartType, SubType subType=Normal ); /** \brief Sets the type of the chart without changing the main type. * * Make sure to use a sub-type that matches the main type, * so e.g. setting sub-type Rows makes sense for Bar charts only, * and it will be ignored for all other chart types. * * \sa KChart::BarDiagram::BarType, KChart::LineDiagram::LineType * \sa KChart::PieDiagram::PieType, KChart::RingDiagram::RingType * \sa KChart::PolarDiagram::PolarType */ void setSubType( SubType subType ); private: /** Justifies the model, so that the given rows and columns fit into it. */ void justifyModelSize( int rows, int columns ); /** Checks whether the given width matches with the one used until now. */ bool checkDatasetWidth( int width ); }; } #endif // KChartWidget_H diff --git a/src/KChart/KTextDocument.h b/src/KChart/KTextDocument.h index 0c483ec..6da0717 100644 --- a/src/KChart/KTextDocument.h +++ b/src/KChart/KTextDocument.h @@ -1,64 +1,64 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KTEXTDOCUMENT_H #define KTEXTDOCUMENT_H // // W A R N I N G // ------------- // // This file is not part of the KD Chart API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include /** * KTextDocument is an internally used enhanced QTextDocument * \internal */ class KTextDocument : public QTextDocument { Q_OBJECT public: - explicit KTextDocument( QObject* parent = 0 ); - explicit KTextDocument( const QString& text, QObject* parent = 0 ); + explicit KTextDocument( QObject* parent = nullptr ); + explicit KTextDocument( const QString& text, QObject* parent = nullptr ); ~KTextDocument(); QSize sizeHint(); QSize minimumSizeHint(); private: QSize sizeForWidth( int width ); private: bool mHintValid; QSize mSizeHint; QSize mMinimumSizeHint; }; #endif /* KTEXTDOCUMENT_H */ diff --git a/src/KChart/Polar/KChartAbstractPieDiagram.h b/src/KChart/Polar/KChartAbstractPieDiagram.h index d31c010..c061287 100644 --- a/src/KChart/Polar/KChartAbstractPieDiagram.h +++ b/src/KChart/Polar/KChartAbstractPieDiagram.h @@ -1,93 +1,93 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTABSTRACTPIEDIAGRAM_H #define KCHARTABSTRACTPIEDIAGRAM_H #include "KChartAbstractPolarDiagram.h" namespace KChart { class PieAttributes; class ThreeDPieAttributes; /** * @brief Base class for any diagram type */ class KCHART_EXPORT AbstractPieDiagram : public AbstractPolarDiagram { Q_OBJECT Q_DISABLE_COPY( AbstractPieDiagram ) KCHART_DECLARE_DERIVED_DIAGRAM( AbstractPieDiagram, PolarCoordinatePlane ) public: explicit AbstractPieDiagram( - QWidget* parent = 0, PolarCoordinatePlane* plane = 0 ); + QWidget* parent = nullptr, PolarCoordinatePlane* plane = nullptr ); virtual ~AbstractPieDiagram(); /** * Returns true if both diagrams have the same settings. */ bool compare( const AbstractPieDiagram* other ) const; /** Set the granularity: the smaller the granularity the more your diagram * segments will show facettes instead of rounded segments. * \param value the granularity value between 0.05 (one twentieth of a degree) * and 36.0 (one tenth of a full circle), other values will be interpreted as 1.0. */ void setGranularity( qreal value ); /** @return the granularity. */ qreal granularity() const; /** \deprecated Use PolarCoordinatePlane::setStartPosition( qreal degrees ) instead. */ void setStartPosition( int degrees ); /** \deprecated Use qreal PolarCoordinatePlane::startPosition instead. */ int startPosition() const; /** If this property is set, and if a pie's TextAttributes have no rotation set, its labels will * automatically be rotated according to the pie's angle. */ void setAutoRotateLabels( bool autoRotate ); /** \see setAutoRotateLabels( bool autoRotate ) */ bool autoRotateLabels() const; void setPieAttributes( const PieAttributes & a ); void setPieAttributes( int column, const PieAttributes & a ); void setPieAttributes( const QModelIndex & index, const PieAttributes & a ); PieAttributes pieAttributes() const; PieAttributes pieAttributes( int column ) const; PieAttributes pieAttributes( const QModelIndex & index ) const; void setThreeDPieAttributes( const ThreeDPieAttributes & a ); void setThreeDPieAttributes( int column, const ThreeDPieAttributes & a ); void setThreeDPieAttributes( const QModelIndex & index, const ThreeDPieAttributes & a ); ThreeDPieAttributes threeDPieAttributes() const; ThreeDPieAttributes threeDPieAttributes( int column ) const; ThreeDPieAttributes threeDPieAttributes( const QModelIndex & index ) const; }; // End of class KChartAbstractPieDiagram } #endif // KCHARTABSTACTPIEDIAGRAM_H diff --git a/src/KChart/Polar/KChartAbstractPolarDiagram.h b/src/KChart/Polar/KChartAbstractPolarDiagram.h index 40c77fe..22d0f27 100644 --- a/src/KChart/Polar/KChartAbstractPolarDiagram.h +++ b/src/KChart/Polar/KChartAbstractPolarDiagram.h @@ -1,57 +1,57 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTABSTRACTPOLARDIAGRAM_H #define KCHARTABSTRACTPOLARDIAGRAM_H #include "KChartPolarCoordinatePlane.h" #include "KChartAbstractDiagram.h" namespace KChart { class GridAttributes; /** * @brief Base class for diagrams based on a polar coordinate system. */ class KCHART_EXPORT AbstractPolarDiagram : public AbstractDiagram { Q_OBJECT Q_DISABLE_COPY( AbstractPolarDiagram ) KCHART_DECLARE_DERIVED_DIAGRAM( AbstractPolarDiagram, PolarCoordinatePlane ) public: explicit AbstractPolarDiagram ( - QWidget* parent = 0, PolarCoordinatePlane* plane = 0 ); + QWidget* parent = nullptr, PolarCoordinatePlane* plane = nullptr ); virtual ~AbstractPolarDiagram() {} virtual qreal valueTotals () const = 0; virtual qreal numberOfValuesPerDataset() const = 0; virtual qreal numberOfDatasets() const { return 1; }; virtual qreal numberOfGridRings() const = 0; const PolarCoordinatePlane * polarCoordinatePlane() const; int columnCount() const; int rowCount() const; }; } #endif diff --git a/src/KChart/Polar/KChartPieAttributes.cpp b/src/KChart/Polar/KChartPieAttributes.cpp index 76227b5..5cbe263 100644 --- a/src/KChart/Polar/KChartPieAttributes.cpp +++ b/src/KChart/Polar/KChartPieAttributes.cpp @@ -1,122 +1,122 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartPieAttributes.h" #include "KChartPieAttributes_p.h" #include "KChartMath_p.h" #include #define d d_func() using namespace KChart; PieAttributes::Private::Private() : explodeFactor( 0.0 ) , tangentialGapFactor( 0.0 ) , radialGapFactor( 0.0 ) { } PieAttributes::PieAttributes() : _d( new Private() ) { } PieAttributes::PieAttributes( const PieAttributes& r ) : _d( new Private( *r.d ) ) { } PieAttributes& PieAttributes::operator= ( const PieAttributes& r ) { if ( this == &r ) return *this; *d = *r.d; return *this; } PieAttributes::~PieAttributes() { - delete _d; _d = 0; + delete _d; _d = nullptr; } bool PieAttributes::operator==( const PieAttributes& r ) const { return explodeFactor() == r.explodeFactor() && gapFactor( true ) == r.gapFactor( true ) && gapFactor( false) == r.gapFactor( false); } void PieAttributes::init( ) { } void PieAttributes::setExplode( bool enabled ) { d->explodeFactor = (enabled ? 0.1 : 0.0); } bool PieAttributes::explode() const { return (d->explodeFactor != 0.0); } void PieAttributes::setExplodeFactor( qreal factor ) { d->explodeFactor = factor; } qreal PieAttributes::explodeFactor() const { return d->explodeFactor; } void PieAttributes::setGapFactor( bool circular, qreal factor ) { if ( circular ) d->tangentialGapFactor = factor; else d->radialGapFactor = factor; } qreal PieAttributes::gapFactor( bool circular ) const { return circular ? d->tangentialGapFactor : d->radialGapFactor; } #if !defined(QT_NO_DEBUG_STREAM) QDebug operator<<(QDebug dbg, const KChart::PieAttributes& a) { dbg << "KChart::PieAttributes("; dbg << "explodeFactor="<< a.explodeFactor() << ")"; return dbg; } #endif /* QT_NO_DEBUG_STREAM */ diff --git a/src/KChart/Polar/KChartPieDiagram.cpp b/src/KChart/Polar/KChartPieDiagram.cpp index 792df34..bbaea7f 100644 --- a/src/KChart/Polar/KChartPieDiagram.cpp +++ b/src/KChart/Polar/KChartPieDiagram.cpp @@ -1,966 +1,966 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartPieDiagram.h" #include "KChartPieDiagram_p.h" #include "KChartPaintContext.h" #include "KChartPieAttributes.h" #include "KChartPolarCoordinatePlane_p.h" #include "KChartThreeDPieAttributes.h" #include "KChartPainterSaver_p.h" #include "KChartMath_p.h" #include #include #include using namespace KChart; PieDiagram::Private::Private() : labelDecorations( PieDiagram::NoDecoration ), isCollisionAvoidanceEnabled( false ) { } PieDiagram::Private::~Private() {} #define d d_func() PieDiagram::PieDiagram( QWidget* parent, PolarCoordinatePlane* plane ) : AbstractPieDiagram( new Private(), parent, plane ) { init(); } PieDiagram::~PieDiagram() { } void PieDiagram::init() { } /** * Creates an exact copy of this diagram. */ PieDiagram * PieDiagram::clone() const { return new PieDiagram( new Private( *d ) ); } void PieDiagram::setLabelDecorations( LabelDecorations decorations ) { d->labelDecorations = decorations; } PieDiagram::LabelDecorations PieDiagram::labelDecorations() const { return d->labelDecorations; } void PieDiagram::setLabelCollisionAvoidanceEnabled( bool enabled ) { d->isCollisionAvoidanceEnabled = enabled; } bool PieDiagram::isLabelCollisionAvoidanceEnabled() const { return d->isCollisionAvoidanceEnabled; } const QPair PieDiagram::calculateDataBoundaries () const { if ( !checkInvariants( true ) || model()->rowCount() < 1 ) return QPair( QPointF( 0, 0 ), QPointF( 0, 0 ) ); const PieAttributes attrs( pieAttributes() ); QPointF bottomLeft( QPointF( 0, 0 ) ); QPointF topRight; // If we explode, we need extra space for the slice that has the largest explosion distance. if ( attrs.explode() ) { const int colCount = columnCount(); qreal maxExplode = 0.0; for ( int j = 0; j < colCount; ++j ) { const PieAttributes columnAttrs( pieAttributes( model()->index( 0, j, rootIndex() ) ) ); // checked maxExplode = qMax( maxExplode, columnAttrs.explodeFactor() ); } topRight = QPointF( 1.0 + maxExplode, 1.0 + maxExplode ); } else { topRight = QPointF( 1.0, 1.0 ); } return QPair ( bottomLeft, topRight ); } void PieDiagram::paintEvent( QPaintEvent* ) { QPainter painter ( viewport() ); PaintContext ctx; ctx.setPainter ( &painter ); ctx.setRectangle( QRectF ( 0, 0, width(), height() ) ); paint ( &ctx ); } void PieDiagram::resizeEvent( QResizeEvent* ) { } void PieDiagram::resize( const QSizeF& size ) { AbstractPieDiagram::resize(size); } void PieDiagram::paint( PaintContext* ctx ) { // Painting is a two stage process // In the first stage we figure out how much space is needed // for text labels. // In the second stage, we make use of that information and // perform the actual painting. placeLabels( ctx ); paintInternal( ctx ); } void PieDiagram::calcSliceAngles() { // determine slice positions and sizes const qreal sum = valueTotals(); const qreal sectorsPerValue = 360.0 / sum; const PolarCoordinatePlane* plane = polarCoordinatePlane(); qreal currentValue = plane ? plane->startPosition() : 0.0; const int colCount = columnCount(); d->startAngles.resize( colCount ); d->angleLens.resize( colCount ); bool atLeastOneValue = false; // guard against completely empty tables for ( int iColumn = 0; iColumn < colCount; ++iColumn ) { bool isOk; const qreal cellValue = qAbs( model()->data( model()->index( 0, iColumn, rootIndex() ) ) // checked .toReal( &isOk ) ); // toReal() returns 0.0 if there was no value or a non-numeric value atLeastOneValue = atLeastOneValue || isOk; d->startAngles[ iColumn ] = currentValue; d->angleLens[ iColumn ] = cellValue * sectorsPerValue; currentValue = d->startAngles[ iColumn ] + d->angleLens[ iColumn ]; } // If there was no value at all, this is the sign for other code to bail out if ( !atLeastOneValue ) { d->startAngles.clear(); d->angleLens.clear(); } } void PieDiagram::calcPieSize( const QRectF &contentsRect ) { d->size = qMin( contentsRect.width(), contentsRect.height() ); // if any slice explodes, the whole pie needs additional space so we make the basic size smaller qreal maxExplode = 0.0; const int colCount = columnCount(); for ( int j = 0; j < colCount; ++j ) { const PieAttributes columnAttrs( pieAttributes( model()->index( 0, j, rootIndex() ) ) ); // checked maxExplode = qMax( maxExplode, columnAttrs.explodeFactor() ); } d->size /= ( 1.0 + 1.0 * maxExplode ); if ( d->size < 0.0 ) { d->size = 0; } } // this is the rect of the top surface of the pie, i.e. excluding the "3D" rim effect. QRectF PieDiagram::twoDPieRect( const QRectF &contentsRect, const ThreeDPieAttributes& threeDAttrs ) const { QRectF pieRect; if ( !threeDAttrs.isEnabled() ) { qreal x = ( contentsRect.width() - d->size ) / 2.0; qreal y = ( contentsRect.height() - d->size ) / 2.0; pieRect = QRectF( contentsRect.left() + x, contentsRect.top() + y, d->size, d->size ); } else { // threeD: width is the maximum possible width; height is 1/2 of that qreal sizeFor3DEffect = 0.0; qreal x = ( contentsRect.width() - d->size ) / 2.0; qreal height = d->size; // make sure that the height plus the threeDheight is not more than the // available size if ( threeDAttrs.depth() >= 0.0 ) { // positive pie height: absolute value sizeFor3DEffect = threeDAttrs.depth(); height = d->size - sizeFor3DEffect; } else { // negative pie height: relative value sizeFor3DEffect = - threeDAttrs.depth() / 100.0 * height; height = d->size - sizeFor3DEffect; } qreal y = ( contentsRect.height() - height - sizeFor3DEffect ) / 2.0; pieRect = QRectF( contentsRect.left() + x, contentsRect.top() + y, d->size, height ); } return pieRect; } void PieDiagram::placeLabels( PaintContext* paintContext ) { if ( !checkInvariants(true) || model()->rowCount() < 1 ) { return; } if ( paintContext->rectangle().isEmpty() || valueTotals() == 0.0 ) { return; } const ThreeDPieAttributes threeDAttrs( threeDPieAttributes() ); const int colCount = columnCount(); d->reverseMapper.clear(); // on first call, this sets up the internals of the ReverseMapper. calcSliceAngles(); if ( d->startAngles.isEmpty() ) { return; } calcPieSize( paintContext->rectangle() ); // keep resizing the pie until the labels and the pie fit into paintContext->rectangle() bool tryAgain = true; while ( tryAgain ) { tryAgain = false; QRectF pieRect = twoDPieRect( paintContext->rectangle(), threeDAttrs ); d->forgetAlreadyPaintedDataValues(); d->labelPaintCache.clear(); for ( int slice = 0; slice < colCount; slice++ ) { if ( d->angleLens[ slice ] != 0.0 ) { const QRectF explodedPieRect = explodedDrawPosition( pieRect, slice ); addSliceLabel( &d->labelPaintCache, explodedPieRect, slice ); } } QRectF textBoundingRect; d->paintDataValueTextsAndMarkers( paintContext, d->labelPaintCache, false, true, &textBoundingRect ); if ( d->isCollisionAvoidanceEnabled ) { shuffleLabels( &textBoundingRect ); } if ( !textBoundingRect.isEmpty() && d->size > 0.0 ) { const QRectF &clipRect = paintContext->rectangle(); // see by how many pixels the text is clipped on each side qreal right = qMax( qreal( 0.0 ), textBoundingRect.right() - clipRect.right() ); qreal left = qMax( qreal( 0.0 ), clipRect.left() - textBoundingRect.left() ); // attention here - y coordinates in Qt are inverted compared to the convention in maths qreal top = qMax( qreal( 0.0 ), clipRect.top() - textBoundingRect.top() ); qreal bottom = qMax( qreal( 0.0 ), textBoundingRect.bottom() - clipRect.bottom() ); qreal maxOverhang = qMax( qMax( right, left ), qMax( top, bottom ) ); if ( maxOverhang > 0.0 ) { // subtract 2x as much because every side only gets half of the total diameter reduction // and we have to make up for the overhang on one particular side. d->size -= qMin( d->size, maxOverhang * 2.0 ); tryAgain = true; } } } } static int wraparound( int i, int size ) { while ( i < 0 ) { i += size; } while ( i >= size ) { i -= size; } return i; } //#define SHUFFLE_DEBUG void PieDiagram::shuffleLabels( QRectF* textBoundingRect ) { // things that could be improved here: // - use a variable number (chosen using angle information) of neighbors to check // - try harder to arrange the labels to look nice // ideas: // - leave labels that don't collide alone (only if they their offset is zero) // - use a graphics view for collision detection LabelPaintCache& lpc = d->labelPaintCache; const int n = lpc.paintReplay.size(); bool modified = false; qreal direction = 5.0; QVector< qreal > offsets; offsets.fill( 0.0, n ); for ( bool lastRoundModified = true; lastRoundModified; ) { lastRoundModified = false; for ( int i = 0; i < n; i++ ) { const int neighborsToCheck = qMax( 10, lpc.paintReplay.size() - 1 ); const int minComp = wraparound( i - neighborsToCheck / 2, n ); const int maxComp = wraparound( i + ( neighborsToCheck + 1 ) / 2, n ); QPainterPath& path = lpc.paintReplay[ i ].labelArea; for ( int j = minComp; j != maxComp; j = wraparound( j + 1, n ) ) { if ( i == j ) { continue; } QPainterPath& otherPath = lpc.paintReplay[ j ].labelArea; while ( ( offsets[ i ] + direction > 0 ) && otherPath.intersects( path ) ) { #ifdef SHUFFLE_DEBUG qDebug() << "collision involving" << j << "and" << i << " -- n =" << n; TextAttributes ta = lpc.paintReplay[ i ].attrs.textAttributes(); ta.setPen( QPen( Qt::white ) ); lpc.paintReplay[ i ].attrs.setTextAttributes( ta ); #endif uint slice = lpc.paintReplay[ i ].index.column(); qreal angle = DEGTORAD( d->startAngles[ slice ] + d->angleLens[ slice ] / 2.0 ); qreal dx = cos( angle ) * direction; qreal dy = -sin( angle ) * direction; offsets[ i ] += direction; path.translate( dx, dy ); lastRoundModified = true; } } } direction *= -1.07; // this can "overshoot", but avoids getting trapped in local minimums modified = modified || lastRoundModified; } if ( modified ) { for ( int i = 0; i < lpc.paintReplay.size(); i++ ) { *textBoundingRect |= lpc.paintReplay[ i ].labelArea.boundingRect(); } } } static QPolygonF polygonFromPainterPath( const QPainterPath &pp ) { QPolygonF ret; for ( int i = 0; i < pp.elementCount(); i++ ) { const QPainterPath::Element& el = pp.elementAt( i ); Q_ASSERT( el.type == QPainterPath::MoveToElement || el.type == QPainterPath::LineToElement ); ret.append( el ); } return ret; } // you can call it "normalizedProjectionLength" if you like static qreal normProjection( const QLineF &l1, const QLineF &l2 ) { const qreal dotProduct = l1.dx() * l2.dx() + l1.dy() * l2.dy(); return qAbs( dotProduct / ( l1.length() * l2.length() ) ); } static QLineF labelAttachmentLine( const QPointF ¢er, const QPointF &start, const QPainterPath &label ) { Q_ASSERT ( label.elementCount() == 5 ); // start is assumed to lie on the outer rim of the slice(!), making it possible to derive the // radius of the pie const qreal pieRadius = QLineF( center, start ).length(); // don't draw a line at all when the label is connected to its slice due to at least one of its // corners falling inside the slice. for ( int i = 0; i < 4; i++ ) { // point 4 is just a duplicate of point 0 if ( QLineF( label.elementAt( i ), center ).length() < pieRadius ) { return QLineF(); } } // find the closest edge in the polygon, and its two neighbors QPointF closeCorners[3]; { QPointF closest = QPointF( 1000000, 1000000 ); int closestIndex = 0; // better misbehave than crash for ( int i = 0; i < 4; i++ ) { // point 4 is just a duplicate of point 0 QPointF p = label.elementAt( i ); if ( QLineF( p, center ).length() < QLineF( closest, center ).length() ) { closest = p; closestIndex = i; } } closeCorners[ 0 ] = label.elementAt( wraparound( closestIndex - 1, 4 ) ); closeCorners[ 1 ] = closest; closeCorners[ 2 ] = label.elementAt( wraparound( closestIndex + 1, 4 ) ); } QLineF edge1 = QLineF( closeCorners[ 0 ], closeCorners[ 1 ] ); QLineF edge2 = QLineF( closeCorners[ 1 ], closeCorners[ 2 ] ); QLineF connection1 = QLineF( ( closeCorners[ 0 ] + closeCorners[ 1 ] ) / 2.0, center ); QLineF connection2 = QLineF( ( closeCorners[ 1 ] + closeCorners[ 2 ] ) / 2.0, center ); QLineF ret; // prefer the connecting line meeting its edge at a more perpendicular angle if ( normProjection( edge1, connection1 ) < normProjection( edge2, connection2 ) ) { ret = connection1; } else { ret = connection2; } // This tends to look a bit better than not doing it *shrug* ret.setP2( ( start + center ) / 2.0 ); // make the line end at the rim of the slice (not 100% accurate because the line is not precisely radial) qreal p1Radius = QLineF( ret.p1(), center ).length(); ret.setLength( p1Radius - pieRadius ); return ret; } void PieDiagram::paintInternal( PaintContext* paintContext ) { // note: Not having any data model assigned is no bug // but we can not draw a diagram then either. if ( !checkInvariants( true ) || model()->rowCount() < 1 ) { return; } if ( d->startAngles.isEmpty() || paintContext->rectangle().isEmpty() || valueTotals() == 0.0 ) { return; } const ThreeDPieAttributes threeDAttrs( threeDPieAttributes() ); const int colCount = columnCount(); // Paint from back to front ("painter's algorithm") - first draw the backmost slice, // then the slices on the left and right from back to front, then the frontmost one. QRectF pieRect = twoDPieRect( paintContext->rectangle(), threeDAttrs ); const int backmostSlice = findSliceAt( 90, colCount ); const int frontmostSlice = findSliceAt( 270, colCount ); int currentLeftSlice = backmostSlice; int currentRightSlice = backmostSlice; drawSlice( paintContext->painter(), pieRect, backmostSlice ); if ( backmostSlice == frontmostSlice ) { const int rightmostSlice = findSliceAt( 0, colCount ); const int leftmostSlice = findSliceAt( 180, colCount ); if ( backmostSlice == leftmostSlice ) { currentLeftSlice = findLeftSlice( currentLeftSlice, colCount ); } if ( backmostSlice == rightmostSlice ) { currentRightSlice = findRightSlice( currentRightSlice, colCount ); } } while ( currentLeftSlice != frontmostSlice ) { if ( currentLeftSlice != backmostSlice ) { drawSlice( paintContext->painter(), pieRect, currentLeftSlice ); } currentLeftSlice = findLeftSlice( currentLeftSlice, colCount ); } while ( currentRightSlice != frontmostSlice ) { if ( currentRightSlice != backmostSlice ) { drawSlice( paintContext->painter(), pieRect, currentRightSlice ); } currentRightSlice = findRightSlice( currentRightSlice, colCount ); } // if the backmost slice is not the frontmost slice, we draw the frontmost one last if ( backmostSlice != frontmostSlice || ! threeDPieAttributes().isEnabled() ) { drawSlice( paintContext->painter(), pieRect, frontmostSlice ); } d->paintDataValueTextsAndMarkers( paintContext, d->labelPaintCache, false, false ); // it's safer to do this at the beginning of placeLabels, but we can save some memory here. d->forgetAlreadyPaintedDataValues(); // ### maybe move this into AbstractDiagram, also make ReverseMapper deal better with multiple polygons const QPointF center = paintContext->rectangle().center(); const PainterSaver painterSaver( paintContext->painter() ); paintContext->painter()->setBrush( Qt::NoBrush ); Q_FOREACH( const LabelPaintInfo &pi, d->labelPaintCache.paintReplay ) { // we expect the PainterPath to be a rectangle if ( pi.labelArea.elementCount() != 5 ) { continue; } paintContext->painter()->setPen( pen( pi.index ) ); if ( d->labelDecorations & LineFromSliceDecoration ) { paintContext->painter()->drawLine( labelAttachmentLine( center, pi.markerPos, pi.labelArea ) ); } if ( d->labelDecorations & FrameDecoration ) { paintContext->painter()->drawPath( pi.labelArea ); } d->reverseMapper.addPolygon( pi.index.row(), pi.index.column(), polygonFromPainterPath( pi.labelArea ) ); } d->labelPaintCache.clear(); d->startAngles.clear(); d->angleLens.clear(); } #if defined ( Q_OS_WIN) #define trunc(x) ((int)(x)) #endif QRectF PieDiagram::explodedDrawPosition( const QRectF& drawPosition, uint slice ) const { const QModelIndex index( model()->index( 0, slice, rootIndex() ) ); // checked const PieAttributes attrs( pieAttributes( index ) ); QRectF adjustedDrawPosition = drawPosition; if ( attrs.explode() ) { qreal startAngle = d->startAngles[ slice ]; qreal angleLen = d->angleLens[ slice ]; qreal explodeAngle = ( DEGTORAD( startAngle + angleLen / 2.0 ) ); qreal explodeDistance = attrs.explodeFactor() * d->size / 2.0; adjustedDrawPosition.translate( explodeDistance * cos( explodeAngle ), explodeDistance * - sin( explodeAngle ) ); } return adjustedDrawPosition; } /** Internal method that draws one of the slices in a pie chart. \param painter the QPainter to draw in \param dataset the dataset to draw the pie for \param slice the slice to draw \param threeDPieHeight the height of the three dimensional effect */ void PieDiagram::drawSlice( QPainter* painter, const QRectF& drawPosition, uint slice) { // Is there anything to draw at all? if ( d->angleLens[ slice ] == 0.0 ) { return; } const QRectF adjustedDrawPosition = explodedDrawPosition( drawPosition, slice ); draw3DEffect( painter, adjustedDrawPosition, slice ); drawSliceSurface( painter, adjustedDrawPosition, slice ); } /** Internal method that draws the surface of one of the slices in a pie chart. \param painter the QPainter to draw in \param dataset the dataset to draw the slice for \param slice the slice to draw */ void PieDiagram::drawSliceSurface( QPainter* painter, const QRectF& drawPosition, uint slice ) { // Is there anything to draw at all? const qreal angleLen = d->angleLens[ slice ]; const qreal startAngle = d->startAngles[ slice ]; const QModelIndex index( model()->index( 0, slice, rootIndex() ) ); // checked const PieAttributes attrs( pieAttributes( index ) ); const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( index ) ); painter->setRenderHint ( QPainter::Antialiasing ); QBrush br = brush( index ); if ( threeDAttrs.isEnabled() ) { br = threeDAttrs.threeDBrush( br, drawPosition ); } painter->setBrush( br ); QPen pen = this->pen( index ); if ( threeDAttrs.isEnabled() ) { pen.setColor( Qt::black ); } painter->setPen( pen ); if ( angleLen == 360 ) { // full circle, avoid nasty line in the middle painter->drawEllipse( drawPosition ); //Add polygon to Reverse mapper for showing tool tips. QPolygonF poly( drawPosition ); d->reverseMapper.addPolygon( index.row(), index.column(), poly ); } else { // draw the top of this piece // Start with getting the points for the arc. const int arcPoints = static_cast(trunc( angleLen / granularity() )); QPolygonF poly( arcPoints + 2 ); qreal degree = 0.0; int iPoint = 0; bool perfectMatch = false; while ( degree <= angleLen ) { poly[ iPoint ] = pointOnEllipse( drawPosition, startAngle + degree ); //qDebug() << degree << angleLen << poly[ iPoint ]; perfectMatch = ( degree == angleLen ); degree += granularity(); ++iPoint; } // if necessary add one more point to fill the last small gap if ( !perfectMatch ) { poly[ iPoint ] = pointOnEllipse( drawPosition, startAngle + angleLen ); // add the center point of the piece poly.append( drawPosition.center() ); } else { poly[ iPoint ] = drawPosition.center(); } //find the value and paint it //fix value position d->reverseMapper.addPolygon( index.row(), index.column(), poly ); painter->drawPolygon( poly ); } } // calculate the position points for the label and pass them to addLabel() void PieDiagram::addSliceLabel( LabelPaintCache* lpc, const QRectF& drawPosition, uint slice ) { const qreal angleLen = d->angleLens[ slice ]; const qreal startAngle = d->startAngles[ slice ]; const QModelIndex index( model()->index( 0, slice, rootIndex() ) ); // checked const qreal sum = valueTotals(); // Position points are calculated relative to the slice. // They are calculated as if the slice was 'standing' on its tip and the rim was up, // so North is the middle (also highest part) of the rim and South is the tip of the slice. const QPointF south = drawPosition.center(); const QPointF southEast = south; const QPointF southWest = south; const QPointF north = pointOnEllipse( drawPosition, startAngle + angleLen / 2.0 ); const QPointF northEast = pointOnEllipse( drawPosition, startAngle ); const QPointF northWest = pointOnEllipse( drawPosition, startAngle + angleLen ); QPointF center = ( south + north ) / 2.0; const QPointF east = ( south + northEast ) / 2.0; const QPointF west = ( south + northWest ) / 2.0; PositionPoints points( center, northWest, north, northEast, east, southEast, south, southWest, west ); qreal topAngle = startAngle - 90; if ( topAngle < 0.0 ) { topAngle += 360.0; } points.setDegrees( KChartEnums::PositionEast, topAngle ); points.setDegrees( KChartEnums::PositionNorthEast, topAngle ); points.setDegrees( KChartEnums::PositionWest, topAngle + angleLen ); points.setDegrees( KChartEnums::PositionNorthWest, topAngle + angleLen ); points.setDegrees( KChartEnums::PositionCenter, topAngle + angleLen / 2.0 ); points.setDegrees( KChartEnums::PositionNorth, topAngle + angleLen / 2.0 ); qreal favoriteTextAngle = 0.0; if ( autoRotateLabels() ) { favoriteTextAngle = - ( startAngle + angleLen / 2 ) + 90.0; while ( favoriteTextAngle <= 0.0 ) { favoriteTextAngle += 360.0; } // flip the label when upside down if ( favoriteTextAngle > 90.0 && favoriteTextAngle < 270.0 ) { favoriteTextAngle = favoriteTextAngle - 180.0; } // negative angles can have special meaning in addLabel; otherwise they work fine if ( favoriteTextAngle <= 0.0 ) { favoriteTextAngle += 360.0; } } - d->addLabel( lpc, index, 0, points, Position::Center, Position::Center, + d->addLabel( lpc, index, nullptr, points, Position::Center, Position::Center, angleLen * sum / 360, favoriteTextAngle ); } static bool doSpansOverlap( qreal s1Start, qreal s1End, qreal s2Start, qreal s2End ) { if ( s1Start < s2Start ) { return s1End >= s2Start; } else { return s1Start <= s2End; } } static bool doArcsOverlap( qreal a1Start, qreal a1End, qreal a2Start, qreal a2End ) { Q_ASSERT( a1Start >= 0 && a1Start <= 360 && a1End >= 0 && a1End <= 360 && a2Start >= 0 && a2Start <= 360 && a2End >= 0 && a2End <= 360 ); // all of this could probably be done better... if ( a1End < a1Start ) { a1End += 360; } if ( a2End < a2Start ) { a2End += 360; } if ( doSpansOverlap( a1Start, a1End, a2Start, a2End ) ) { return true; } if ( a1Start > a2Start ) { return doSpansOverlap( a1Start - 360.0, a1End - 360.0, a2Start, a2End ); } else { return doSpansOverlap( a1Start + 360.0, a1End + 360.0, a2Start, a2End ); } } /** Internal method that draws the shadow creating the 3D effect of a pie \param painter the QPainter to draw in \param drawPosition the position to draw at \param slice the slice to draw the shadow for */ void PieDiagram::draw3DEffect( QPainter* painter, const QRectF& drawPosition, uint slice ) { const QModelIndex index( model()->index( 0, slice, rootIndex() ) ); // checked const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( index ) ); if ( ! threeDAttrs.isEnabled() ) { return; } // NOTE: We cannot optimize away drawing some of the effects (even // when not exploding), because some of the pies might be left out // in future versions which would make some of the normally hidden // pies visible. Complex hidden-line algorithms would be much more // expensive than just drawing for nothing. // No need to save the brush, will be changed on return from this // method anyway. const QBrush brush = this->brush( model()->index( 0, slice, rootIndex() ) ); // checked if ( threeDAttrs.useShadowColors() ) { painter->setBrush( QBrush( brush.color().darker() ) ); } else { painter->setBrush( brush ); } qreal startAngle = d->startAngles[ slice ]; qreal endAngle = startAngle + d->angleLens[ slice ]; // Normalize angles while ( startAngle >= 360 ) startAngle -= 360; while ( endAngle >= 360 ) endAngle -= 360; Q_ASSERT( startAngle >= 0 && startAngle <= 360 ); Q_ASSERT( endAngle >= 0 && endAngle <= 360 ); // positive pie height: absolute value // negative pie height: relative value const int depth = threeDAttrs.depth() >= 0.0 ? threeDAttrs.depth() : -threeDAttrs.depth() / 100.0 * drawPosition.height(); if ( startAngle == endAngle || startAngle == endAngle - 360 ) { // full circle draw3dOuterRim( painter, drawPosition, depth, 180, 360 ); } else { if ( doArcsOverlap( startAngle, endAngle, 180, 360 ) ) { draw3dOuterRim( painter, drawPosition, depth, startAngle, endAngle ); } if ( startAngle >= 270 || startAngle <= 90 ) { draw3dCutSurface( painter, drawPosition, depth, startAngle ); } if ( endAngle >= 90 && endAngle <= 270 ) { draw3dCutSurface( painter, drawPosition, depth, endAngle ); } } } /** Internal method that draws the cut surface of a slice (think of a real pie cut into slices) in 3D mode, for surfaces that are facing the observer. \param painter the QPainter to draw in \param rect the position to draw at \param threeDHeight the height of the shadow \param angle the angle of the segment */ void PieDiagram::draw3dCutSurface( QPainter* painter, const QRectF& rect, qreal threeDHeight, qreal angle ) { QPolygonF poly( 4 ); const QPointF center = rect.center(); const QPointF circlePoint = pointOnEllipse( rect, angle ); poly[0] = center; poly[1] = circlePoint; poly[2] = QPointF( circlePoint.x(), circlePoint.y() + threeDHeight ); poly[3] = QPointF( center.x(), center.y() + threeDHeight ); // TODO: add polygon to ReverseMapper painter->drawPolygon( poly ); } /** Internal method that draws the outer rim of a slice when the rim is facing the observer. \param painter the QPainter to draw in \param rect the position to draw at \param threeDHeight the height of the shadow \param startAngle the starting angle of the segment \param endAngle the ending angle of the segment */ void PieDiagram::draw3dOuterRim( QPainter* painter, const QRectF& rect, qreal threeDHeight, qreal startAngle, qreal endAngle ) { // Start with getting the points for the inner arc. if ( endAngle < startAngle ) { endAngle += 360; } startAngle = qMax( startAngle, qreal( 180.0 ) ); endAngle = qMin( endAngle, qreal( 360.0 ) ); int numHalfPoints = trunc( ( endAngle - startAngle ) / granularity() ) + 1; if ( numHalfPoints < 2 ) { return; } QPolygonF poly( numHalfPoints ); qreal degree = endAngle; int iPoint = 0; bool perfectMatch = false; while ( degree >= startAngle ) { poly[ numHalfPoints - iPoint - 1 ] = pointOnEllipse( rect, degree ); perfectMatch = (degree == startAngle); degree -= granularity(); ++iPoint; } // if necessary add one more point to fill the last small gap if ( !perfectMatch ) { poly.prepend( pointOnEllipse( rect, startAngle ) ); ++numHalfPoints; } poly.resize( numHalfPoints * 2 ); // Now copy these arcs again into the final array, but in the // opposite direction and moved down by the 3D height. for ( int i = numHalfPoints - 1; i >= 0; --i ) { QPointF pointOnFirstArc( poly[ i ] ); pointOnFirstArc.setY( pointOnFirstArc.y() + threeDHeight ); poly[ numHalfPoints * 2 - i - 1 ] = pointOnFirstArc; } // TODO: Add polygon to ReverseMapper painter->drawPolygon( poly ); } /** Internal method that finds the slice that is located at the position specified by \c angle. \param angle the angle at which to search for a slice \return the number of the slice found */ uint PieDiagram::findSliceAt( qreal angle, int colCount ) { for ( int i = 0; i < colCount; ++i ) { qreal endseg = d->startAngles[ i ] + d->angleLens[ i ]; if ( d->startAngles[ i ] <= angle && endseg >= angle ) { return i; } } // If we have not found it, try wrap around // but only if the current searched angle is < 360 degree if ( angle < 360 ) return findSliceAt( angle + 360, colCount ); // otherwise - what ever went wrong - we return 0 return 0; } /** Internal method that finds the slice that is located to the left of \c slice. \param slice the slice to start the search from \return the number of the pie to the left of \c pie */ uint PieDiagram::findLeftSlice( uint slice, int colCount ) { if ( slice == 0 ) { if ( colCount > 1 ) { return colCount - 1; } else { return 0; } } else { return slice - 1; } } /** Internal method that finds the slice that is located to the right of \c slice. \param slice the slice to start the search from \return the number of the slice to the right of \c slice */ uint PieDiagram::findRightSlice( uint slice, int colCount ) { int rightSlice = slice + 1; if ( rightSlice == colCount ) { rightSlice = 0; } return rightSlice; } /** * Auxiliary method returning a point to a given boundary * rectangle of the enclosed ellipse and an angle. */ QPointF PieDiagram::pointOnEllipse( const QRectF& boundingBox, qreal angle ) { qreal angleRad = DEGTORAD( angle ); qreal cosAngle = cos( angleRad ); qreal sinAngle = -sin( angleRad ); qreal posX = cosAngle * boundingBox.width() / 2.0; qreal posY = sinAngle * boundingBox.height() / 2.0; return QPointF( posX + boundingBox.center().x(), posY + boundingBox.center().y() ); } /*virtual*/ qreal PieDiagram::valueTotals() const { if ( !model() ) return 0; const int colCount = columnCount(); qreal total = 0.0; // non-empty models need a row with data Q_ASSERT( colCount == 0 || model()->rowCount() >= 1 ); for ( int j = 0; j < colCount; ++j ) { total += qAbs(model()->data( model()->index( 0, j, rootIndex() ) ).toReal()); // checked } return total; } /*virtual*/ qreal PieDiagram::numberOfValuesPerDataset() const { return model() ? model()->columnCount( rootIndex() ) : 0.0; } /*virtual*/ qreal PieDiagram::numberOfGridRings() const { return 1; } diff --git a/src/KChart/Polar/KChartPieDiagram.h b/src/KChart/Polar/KChartPieDiagram.h index 7df00a6..53b73ee 100644 --- a/src/KChart/Polar/KChartPieDiagram.h +++ b/src/KChart/Polar/KChartPieDiagram.h @@ -1,123 +1,123 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTPIEDIAGRAM_H #define KCHARTPIEDIAGRAM_H #include "KChartAbstractPieDiagram.h" namespace KChart { class LabelPaintCache; /** * @brief PieDiagram defines a common pie diagram */ class KCHART_EXPORT PieDiagram : public AbstractPieDiagram { Q_OBJECT Q_DISABLE_COPY( PieDiagram ) KCHART_DECLARE_DERIVED_DIAGRAM( PieDiagram, PolarCoordinatePlane ) public: explicit PieDiagram( - QWidget* parent = 0, PolarCoordinatePlane* plane = 0 ); + QWidget* parent = nullptr, PolarCoordinatePlane* plane = nullptr ); virtual ~PieDiagram(); protected: // Implement AbstractDiagram /** \reimpl */ void paint( PaintContext* paintContext ) Q_DECL_OVERRIDE; public: /** * Describes which decorations are painted around data labels. */ enum LabelDecoration { NoDecoration = 0, ///< No decoration FrameDecoration = 1, ///< A rectangular frame is painted around the label text LineFromSliceDecoration = 2 ///< A line is drawn from the pie slice to its label }; Q_DECLARE_FLAGS( LabelDecorations, LabelDecoration ) /// Set the decorations to be painted around data labels according to @p decorations. void setLabelDecorations( LabelDecorations decorations ); /// Return the decorations to be painted around data labels. LabelDecorations labelDecorations() const; /// If @p enabled is set to true, labels that would overlap will be shuffled to avoid overlap. /// \note Collision avoidance may allow labels to be closer than AbstractDiagram with /// allowOverlappingDataValueTexts() == false, so you should usually also call /// setAllowOverlappingDataValueTexts( true ) if you enable this feature. void setLabelCollisionAvoidanceEnabled( bool enabled ); /// Return whether overlapping labels will be moved to until they don't overlap anymore. bool isLabelCollisionAvoidanceEnabled() const; /** \reimpl */ void resize ( const QSizeF& area ) Q_DECL_OVERRIDE; // Implement AbstractPolarDiagram /** \reimpl */ qreal valueTotals () const Q_DECL_OVERRIDE; /** \reimpl */ qreal numberOfValuesPerDataset() const Q_DECL_OVERRIDE; /** \reimpl */ qreal numberOfGridRings() const Q_DECL_OVERRIDE; virtual PieDiagram * clone() const; protected: /** \reimpl */ const QPair calculateDataBoundaries() const Q_DECL_OVERRIDE; void paintEvent( QPaintEvent* ) Q_DECL_OVERRIDE; void resizeEvent( QResizeEvent* ) Q_DECL_OVERRIDE; private: // ### move to private class? void placeLabels( PaintContext* paintContext ); // Solve problems with label overlap by changing label positions inside d->labelPaintCache. void shuffleLabels( QRectF* textBoundingRect ); void paintInternal( PaintContext* paintContext ); void drawSlice( QPainter* painter, const QRectF& drawPosition, uint slice ); void drawSliceSurface( QPainter* painter, const QRectF& drawPosition, uint slice ); void addSliceLabel( LabelPaintCache* lpc, const QRectF& drawPosition, uint slice ); void draw3DEffect( QPainter* painter, const QRectF& drawPosition, uint slice ); void draw3dCutSurface( QPainter* painter, const QRectF& rect, qreal threeDHeight, qreal angle ); void draw3dOuterRim( QPainter* painter, const QRectF& rect, qreal threeDHeight, qreal startAngle, qreal endAngle ); void calcSliceAngles(); void calcPieSize( const QRectF &contentsRect ); QRectF twoDPieRect( const QRectF &contentsRect, const ThreeDPieAttributes& threeDAttrs ) const; QRectF explodedDrawPosition( const QRectF& drawPosition, uint slice ) const; uint findSliceAt( qreal angle, int columnCount ); uint findLeftSlice( uint slice, int columnCount ); uint findRightSlice( uint slice, int columnCount ); QPointF pointOnEllipse( const QRectF& boundingBox, qreal angle ); }; // End of class KChartPieDiagram Q_DECLARE_OPERATORS_FOR_FLAGS( PieDiagram::LabelDecorations ) } #endif // KCHARTPIEDIAGRAM_H diff --git a/src/KChart/Polar/KChartPolarCoordinatePlane.cpp b/src/KChart/Polar/KChartPolarCoordinatePlane.cpp index ded8aca..bf41edf 100644 --- a/src/KChart/Polar/KChartPolarCoordinatePlane.cpp +++ b/src/KChart/Polar/KChartPolarCoordinatePlane.cpp @@ -1,376 +1,376 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartPolarCoordinatePlane.h" #include "KChartPolarCoordinatePlane_p.h" #include "KChartPainterSaver_p.h" #include "KChartChart.h" #include "KChartPaintContext.h" #include "KChartAbstractDiagram.h" #include "KChartAbstractPolarDiagram.h" #include "KChartPolarDiagram.h" #include "KChartMath_p.h" #include #include #include #include #include using namespace KChart; #define d d_func() PolarCoordinatePlane::PolarCoordinatePlane ( Chart* parent ) : AbstractCoordinatePlane ( new Private(), parent ) { // this bloc left empty intentionally } PolarCoordinatePlane::~PolarCoordinatePlane() { // this bloc left empty intentionally } void PolarCoordinatePlane::init() { // this bloc left empty intentionally } void PolarCoordinatePlane::addDiagram ( AbstractDiagram* diagram ) { Q_ASSERT_X ( dynamic_cast ( diagram ), "PolarCoordinatePlane::addDiagram", "Only polar" "diagrams can be added to a polar coordinate plane!" ); AbstractCoordinatePlane::addDiagram ( diagram ); connect ( diagram, SIGNAL (layoutChanged(AbstractDiagram*)), SLOT (slotLayoutChanged(AbstractDiagram*)) ); } void PolarCoordinatePlane::paint ( QPainter* painter ) { AbstractDiagramList diags = diagrams(); if ( d->coordinateTransformations.size() != diags.size() ) { // diagrams have not been set up yet return; } // need at least one so d->currentTransformation can be a valid pointer Q_ASSERT( !d->coordinateTransformations.isEmpty() ); PaintContext ctx; ctx.setPainter ( painter ); ctx.setCoordinatePlane ( this ); ctx.setRectangle ( geometry() /*d->contentRect*/ ); // 1. ask (only!) PolarDiagrams if they need additional space for data labels / data comments const qreal oldZoomX = zoomFactorX(); const qreal oldZoomY = zoomFactorY(); d->newZoomX = oldZoomX; d->newZoomY = oldZoomY; for ( int i = 0; i < diags.size(); i++ ) { d->currentTransformation = & ( d->coordinateTransformations[i] ); qreal zoomX; qreal zoomY; PolarDiagram* polarDia = dynamic_cast ( diags[i] ); if ( polarDia ) { polarDia->paint( &ctx, true, zoomX, zoomY ); d->newZoomX = qMin( d->newZoomX, zoomX ); d->newZoomY = qMin( d->newZoomY, zoomY ); } } if ( d->newZoomX != oldZoomX || d->newZoomY != oldZoomY ) { //qDebug() << "new zoom:" << d->newZoomY << " old zoom:" << oldZoomY; - d->currentTransformation = 0; // not painting anymore until we get called again + d->currentTransformation = nullptr; // not painting anymore until we get called again QMetaObject::invokeMethod( this, "adjustZoomAndRepaint", Qt::QueuedConnection ); return; } // 2. there was room enough for the labels, so start drawing // paint the coordinate system rulers: d->currentTransformation = &d->coordinateTransformations.first(); d->grid->drawGrid( &ctx ); // paint the diagrams which will re-use their DataValueTextInfoList(s) filled in step 1: for ( int i = 0; i < diags.size(); i++ ) { d->currentTransformation = & ( d->coordinateTransformations[i] ); PainterSaver painterSaver( painter ); PolarDiagram* polarDia = dynamic_cast( diags[i] ); if ( polarDia ) { qreal dummy1, dummy2; polarDia->paint( &ctx, false, dummy1, dummy2 ); } else { diags[i]->paint( &ctx ); } } - d->currentTransformation = 0; + d->currentTransformation = nullptr; } void PolarCoordinatePlane::adjustZoomAndRepaint() { const qreal newZoom = qMin(d->newZoomX, d->newZoomY); setZoomFactors(newZoom, newZoom); update(); } void PolarCoordinatePlane::resizeEvent ( QResizeEvent* ) { d->initialResizeEventReceived = true; layoutDiagrams(); } void PolarCoordinatePlane::layoutDiagrams() { // the rectangle the diagrams cover in the *plane*: // (Why -3? We save 1px on each side for the antialiased drawing, and // respect the way QPainter calculates the width of a painted rect (the // size is the rectangle size plus the pen width). This way, most clipping // for regular pens should be avoided. When pens with a penWidth or larger // than 1 are used, this may not b sufficient. const QRect rect( areaGeometry() ); d->contentRect = QRectF ( 1, 1, rect.width() - 3, rect.height() - 3 ); const ZoomParameters zoom = d->coordinateTransformations.isEmpty() ? ZoomParameters() : d->coordinateTransformations.front().zoom; // FIXME distribute space according to options: const qreal oldStartPosition = startPosition(); d->coordinateTransformations.clear(); Q_FOREACH( AbstractDiagram* diagram, diagrams() ) { AbstractPolarDiagram *polarDiagram = dynamic_cast( diagram ); Q_ASSERT( polarDiagram ); QPair dataBoundariesPair = polarDiagram->dataBoundaries(); const qreal angleUnit = 360 / polarDiagram->valueTotals(); //qDebug() << "--------------------------------------------------------"; const qreal radius = qAbs( dataBoundariesPair.first.y() ) + dataBoundariesPair.second.y(); //qDebug() << radius <<"="<contentRect.width(); const qreal planeHeight = d->contentRect.height(); const qreal radiusUnit = qMin( planeWidth, planeHeight ) / diagramWidth; //qDebug() << radiusUnit <<"=" << "qMin( "<contentRect.topLeft(); CoordinateTransformation diagramTransposition; diagramTransposition.originTranslation = coordinateOrigin; diagramTransposition.radiusUnit = radiusUnit; diagramTransposition.angleUnit = angleUnit; diagramTransposition.startPosition = oldStartPosition; diagramTransposition.zoom = zoom; diagramTransposition.minValue = dataBoundariesPair.first.y() < 0 ? dataBoundariesPair.first.y() : 0.0; d->coordinateTransformations.append( diagramTransposition ); } update(); } const QPointF PolarCoordinatePlane::translate( const QPointF& diagramPoint ) const { - Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::translate", + Q_ASSERT_X ( d->currentTransformation != nullptr, "PolarCoordinatePlane::translate", "Only call translate() from within paint()." ); return d->currentTransformation->translate ( diagramPoint ); } const QPointF PolarCoordinatePlane::translatePolar( const QPointF& diagramPoint ) const { - Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::translate", + Q_ASSERT_X ( d->currentTransformation != nullptr, "PolarCoordinatePlane::translate", "Only call translate() from within paint()." ); return d->currentTransformation->translatePolar ( diagramPoint ); } qreal PolarCoordinatePlane::angleUnit() const { - Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::angleUnit", + Q_ASSERT_X ( d->currentTransformation != nullptr, "PolarCoordinatePlane::angleUnit", "Only call angleUnit() from within paint()." ); return d->currentTransformation->angleUnit; } qreal PolarCoordinatePlane::radiusUnit() const { - Q_ASSERT_X ( d->currentTransformation != 0, "PolarCoordinatePlane::radiusUnit", + Q_ASSERT_X ( d->currentTransformation != nullptr, "PolarCoordinatePlane::radiusUnit", "Only call radiusUnit() from within paint()." ); return d->currentTransformation->radiusUnit; } void PolarCoordinatePlane::slotLayoutChanged ( AbstractDiagram* ) { if ( d->initialResizeEventReceived ) layoutDiagrams(); } void PolarCoordinatePlane::setStartPosition( qreal degrees ) { Q_ASSERT_X ( diagram(), "PolarCoordinatePlane::setStartPosition", "setStartPosition() needs a diagram to be associated to the plane." ); for ( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin(); it != d->coordinateTransformations.end(); ++it ) { CoordinateTransformation& trans = *it; trans.startPosition = degrees; } } qreal PolarCoordinatePlane::startPosition() const { return d->coordinateTransformations.isEmpty() ? 0.0 : d->coordinateTransformations.first().startPosition; } qreal PolarCoordinatePlane::zoomFactorX() const { return d->coordinateTransformations.isEmpty() ? 1.0 : d->coordinateTransformations.first().zoom.xFactor; } qreal PolarCoordinatePlane::zoomFactorY() const { return d->coordinateTransformations.isEmpty() ? 1.0 : d->coordinateTransformations.first().zoom.yFactor; } void PolarCoordinatePlane::setZoomFactors( qreal factorX, qreal factorY ) { setZoomFactorX( factorX ); setZoomFactorY( factorY ); } void PolarCoordinatePlane::setZoomFactorX( qreal factor ) { for ( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin(); it != d->coordinateTransformations.end(); ++it ) { CoordinateTransformation& trans = *it; trans.zoom.xFactor = factor; } } void PolarCoordinatePlane::setZoomFactorY( qreal factor ) { for ( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin(); it != d->coordinateTransformations.end(); ++it ) { CoordinateTransformation& trans = *it; trans.zoom.yFactor = factor; } } QPointF PolarCoordinatePlane::zoomCenter() const { return d->coordinateTransformations.isEmpty() ? QPointF( 0.5, 0.5 ) : QPointF( d->coordinateTransformations.first().zoom.xCenter, d->coordinateTransformations.first().zoom.yCenter ); } void PolarCoordinatePlane::setZoomCenter( const QPointF& center ) { for ( CoordinateTransformationList::iterator it = d->coordinateTransformations.begin(); it != d->coordinateTransformations.end(); ++it ) { CoordinateTransformation& trans = *it; trans.zoom.xCenter = center.x(); trans.zoom.yCenter = center.y(); } } DataDimensionsList PolarCoordinatePlane::getDataDimensionsList() const { DataDimensionsList l; //FIXME(khz): do the real calculation return l; } void KChart::PolarCoordinatePlane::setGridAttributes( bool circular, const GridAttributes& a ) { if ( circular ) d->gridAttributesCircular = a; else d->gridAttributesSagittal = a; setHasOwnGridAttributes( circular, true ); update(); emit propertiesChanged(); } void KChart::PolarCoordinatePlane::resetGridAttributes( bool circular ) { setHasOwnGridAttributes( circular, false ); update(); } const GridAttributes KChart::PolarCoordinatePlane::gridAttributes( bool circular ) const { if ( hasOwnGridAttributes( circular ) ) { if ( circular ) return d->gridAttributesCircular; else return d->gridAttributesSagittal; } else { return globalGridAttributes(); } } QRectF KChart::PolarCoordinatePlane::Private::contentsRect( const KChart::PolarCoordinatePlane* plane ) { QRectF contentsRect; QPointF referencePointAtTop = plane->translate( QPointF( 1, 0 ) ); QPointF temp = plane->translate( QPointF( 0, 0 ) ) - referencePointAtTop; const qreal offset = temp.y(); referencePointAtTop.setX( referencePointAtTop.x() - offset ); contentsRect.setTopLeft( referencePointAtTop ); contentsRect.setBottomRight( referencePointAtTop + QPointF( 2.0 * offset, 2.0 * offset) ); return contentsRect; } void KChart::PolarCoordinatePlane::setHasOwnGridAttributes( bool circular, bool on ) { if ( circular ) d->hasOwnGridAttributesCircular = on; else d->hasOwnGridAttributesSagittal = on; emit propertiesChanged(); } bool KChart::PolarCoordinatePlane::hasOwnGridAttributes( bool circular ) const { return ( circular ) ? d->hasOwnGridAttributesCircular : d->hasOwnGridAttributesSagittal; } diff --git a/src/KChart/Polar/KChartPolarCoordinatePlane.h b/src/KChart/Polar/KChartPolarCoordinatePlane.h index 229f54d..13defa9 100644 --- a/src/KChart/Polar/KChartPolarCoordinatePlane.h +++ b/src/KChart/Polar/KChartPolarCoordinatePlane.h @@ -1,164 +1,164 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHART_POLAR_COORDINATEPLANE_H #define KCHART_POLAR_COORDINATEPLANE_H #include "KChartAbstractCoordinatePlane.h" namespace KChart { class Chart; class PaintContext; /** * @brief Polar coordinate plane */ class KCHART_EXPORT PolarCoordinatePlane : public AbstractCoordinatePlane { Q_OBJECT Q_DISABLE_COPY( PolarCoordinatePlane ) KCHART_DECLARE_PRIVATE_DERIVED_PARENT( PolarCoordinatePlane, Chart* ) public: struct CoordinateTransformation; typedef QList CoordinateTransformationList; - explicit PolarCoordinatePlane ( Chart* parent = 0 ); + explicit PolarCoordinatePlane ( Chart* parent = nullptr ); ~PolarCoordinatePlane(); void addDiagram ( AbstractDiagram* diagram ) Q_DECL_OVERRIDE; const QPointF translate ( const QPointF& diagramPoint ) const Q_DECL_OVERRIDE; const QPointF translatePolar ( const QPointF& diagramPoint ) const; /** \brief Specify the rotation of the coordinate plane. * * In a pie diagram this indicates the position where the first pie starts, * in a polar diagram it specifies the Zero position of the circular axis: * * \image html polar-plane-start-position.png "Illustration of \"start position\" property" * * \sa startPosition */ void setStartPosition( qreal degrees ); /** Retrieve the rotation of the coordinate plane. * \sa setStartPosition */ qreal startPosition() const; qreal zoomFactorX() const Q_DECL_OVERRIDE; qreal zoomFactorY() const Q_DECL_OVERRIDE; void setZoomFactors( qreal factorX, qreal factorY ) Q_DECL_OVERRIDE; void setZoomFactorX( qreal factor ) Q_DECL_OVERRIDE; void setZoomFactorY( qreal factor ) Q_DECL_OVERRIDE; QPointF zoomCenter() const Q_DECL_OVERRIDE; void setZoomCenter( const QPointF& center ) Q_DECL_OVERRIDE; /** * Set the attributes to be used for grid lines drawn in circular * direction (or in sagittal direction, resp.). * * To disable circular grid painting, for example, your code should like this: * \code * GridAttributes ga = plane->gridAttributes( bool ); * ga.setGridVisible( false ); * plane-setGridAttributes( bool, ga ); * \endcode * * \note setGridAttributes overwrites the global attributes that * were set by AbstractCoordinatePlane::setGlobalGridAttributes. * To re-activate these global attributes you can call * resetGridAttributes. * * \sa resetGridAttributes, gridAttributes * \sa AbstractCoordinatePlane::setGlobalGridAttributes * \sa hasOwnGridAttributes */ void setGridAttributes( bool circular, const GridAttributes & ); /** * Reset the attributes to be used for grid lines drawn in circular * direction (or in sagittal direction, resp.). * By calling this method you specify that the global attributes set by * AbstractCoordinatePlane::setGlobalGridAttributes be used. * * \sa setGridAttributes, gridAttributes * \sa AbstractCoordinatePlane::globalGridAttributes * \sa hasOwnGridAttributes */ void resetGridAttributes( bool circular ); /** * \return The attributes used for grid lines drawn in circular * direction (or in sagittal direction, resp.). * * \note This function always returns a valid set of grid attributes: * If no special grid attributes were set for this direction * the global attributes are returned, as returned by * AbstractCoordinatePlane::globalGridAttributes. * * \sa setGridAttributes * \sa resetGridAttributes * \sa AbstractCoordinatePlane::globalGridAttributes * \sa hasOwnGridAttributes */ const GridAttributes gridAttributes( bool circular ) const; /** * \return Returns whether the grid attributes have been set for the * respective direction via setGridAttributes( bool circular ). * * If false, the grid will use the global attributes set * by AbstractCoordinatePlane::globalGridAttributes (or the default * attributes, resp.) * * \sa setGridAttributes * \sa resetGridAttributes * \sa AbstractCoordinatePlane::globalGridAttributes */ bool hasOwnGridAttributes( bool circular ) const; qreal angleUnit() const; qreal radiusUnit() const; /** reimpl */ void paint( QPainter* ) Q_DECL_OVERRIDE; protected: DataDimensionsList getDataDimensionsList() const Q_DECL_OVERRIDE; void paintEvent ( QPaintEvent* ); void resizeEvent ( QResizeEvent* ); void layoutDiagrams() Q_DECL_OVERRIDE; protected Q_SLOTS: void slotLayoutChanged( AbstractDiagram* diagram ); void adjustZoomAndRepaint(); private: void setHasOwnGridAttributes( bool circular, bool on ); }; } #endif diff --git a/src/KChart/Polar/KChartPolarCoordinatePlane_p.h b/src/KChart/Polar/KChartPolarCoordinatePlane_p.h index 5ef262d..ac23385 100644 --- a/src/KChart/Polar/KChartPolarCoordinatePlane_p.h +++ b/src/KChart/Polar/KChartPolarCoordinatePlane_p.h @@ -1,138 +1,138 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTPOLARCOORDINATEPLANE_P_H #define KCHARTPOLARCOORDINATEPLANE_P_H // // W A R N I N G // ------------- // // This file is not part of the KD Chart API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "KChartAbstractCoordinatePlane_p.h" #include "KChartZoomParameters.h" #include "KChartPolarGrid.h" #include "KChartMath_p.h" namespace KChart { /** * \internal */ struct PolarCoordinatePlane::CoordinateTransformation { // represents the distance of the diagram coordinate origin to the // origin of the coordinate plane space: QPointF originTranslation; qreal radiusUnit; qreal angleUnit; qreal minValue; qreal startPosition; ZoomParameters zoom; static QPointF polarToCartesian( qreal R, qreal theta ) { // de-inline me return QPointF( R * cos( DEGTORAD( theta ) ), R * sin( DEGTORAD( theta ) ) ); } inline const QPointF translate( const QPointF& diagramPoint ) const { // ### de-inline me // calculate the polar coordinates const qreal x = (diagramPoint.x() * radiusUnit) - (minValue * radiusUnit); //qDebug() << x << "=" << diagramPoint.x() << "*" << radiusUnit << " startPosition: " << startPosition; const qreal y = ( diagramPoint.y() * -angleUnit) - 90.0 - startPosition; // convert to cartesian coordinates QPointF cartesianPoint = polarToCartesian( x, y ); cartesianPoint.setX( cartesianPoint.x() * zoom.xFactor ); cartesianPoint.setY( cartesianPoint.y() * zoom.yFactor ); QPointF newOrigin = originTranslation; qreal minOrigin = qMin( newOrigin.x(), newOrigin.y() ); newOrigin.setX( newOrigin.x() + minOrigin * ( 1 - zoom.xCenter * 2 ) * zoom.xFactor ); newOrigin.setY( newOrigin.y() + minOrigin * ( 1 - zoom.yCenter * 2 ) * zoom.yFactor ); return newOrigin + cartesianPoint; } inline const QPointF translatePolar( const QPointF& diagramPoint ) const { // ### de-inline me return QPointF( diagramPoint.x() * angleUnit, diagramPoint.y() * radiusUnit ); } }; class Q_DECL_HIDDEN PolarCoordinatePlane::Private : public AbstractCoordinatePlane::Private { friend class PolarCoordinatePlane; public: explicit Private() - : currentTransformation(0) + : currentTransformation(nullptr) , initialResizeEventReceived(false ) , hasOwnGridAttributesCircular ( false ) , hasOwnGridAttributesSagittal ( false ) {} virtual ~Private() { } void initialize() Q_DECL_OVERRIDE { grid = new PolarGrid(); } static QRectF contentsRect( const PolarCoordinatePlane* plane ); // the coordinate plane will calculate coordinate transformations for all // diagrams and store them here: CoordinateTransformationList coordinateTransformations; // when painting, this pointer selects the coordinate transformation for // the current diagram: CoordinateTransformation* currentTransformation; // the reactangle occupied by the diagrams, in plane coordinates QRectF contentRect; // true after the first resize event came in bool initialResizeEventReceived; // true after setGridAttributes( Qt::Orientation ) was used, // false if resetGridAttributes( Qt::Orientation ) was called bool hasOwnGridAttributesCircular; bool hasOwnGridAttributesSagittal; GridAttributes gridAttributesCircular; GridAttributes gridAttributesSagittal; qreal newZoomX, newZoomY; }; KCHART_IMPL_DERIVED_PLANE(PolarCoordinatePlane, AbstractCoordinatePlane) } #endif /* KCHARTBARDIAGRAM_P_H */ diff --git a/src/KChart/Polar/KChartPolarDiagram.cpp b/src/KChart/Polar/KChartPolarDiagram.cpp index c2d00a9..1480a39 100644 --- a/src/KChart/Polar/KChartPolarDiagram.cpp +++ b/src/KChart/Polar/KChartPolarDiagram.cpp @@ -1,306 +1,306 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartPolarDiagram.h" #include "KChartPolarDiagram_p.h" #include "KChartPaintContext.h" #include "KChartPainterSaver_p.h" #include "KChartMath_p.h" #include using namespace KChart; PolarDiagram::Private::Private() : rotateCircularLabels( false ), closeDatasets( false ) { } PolarDiagram::Private::~Private() {} #define d d_func() PolarDiagram::PolarDiagram( QWidget* parent, PolarCoordinatePlane* plane ) : AbstractPolarDiagram( new Private( ), parent, plane ) { //init(); } PolarDiagram::~PolarDiagram() { } void PolarDiagram::init() { setShowDelimitersAtPosition( Position::Unknown, false ); setShowDelimitersAtPosition( Position::Center, false ); setShowDelimitersAtPosition( Position::NorthWest, false ); setShowDelimitersAtPosition( Position::North, true ); setShowDelimitersAtPosition( Position::NorthEast, false ); setShowDelimitersAtPosition( Position::West, false ); setShowDelimitersAtPosition( Position::East, false ); setShowDelimitersAtPosition( Position::SouthWest, false ); setShowDelimitersAtPosition( Position::South, true ); setShowDelimitersAtPosition( Position::SouthEast, false ); setShowDelimitersAtPosition( Position::Floating, false ); setShowLabelsAtPosition( Position::Unknown, false ); setShowLabelsAtPosition( Position::Center, false ); setShowLabelsAtPosition( Position::NorthWest, false ); setShowLabelsAtPosition( Position::North, true ); setShowLabelsAtPosition( Position::NorthEast, false ); setShowLabelsAtPosition( Position::West, false ); setShowLabelsAtPosition( Position::East, false ); setShowLabelsAtPosition( Position::SouthWest, false ); setShowLabelsAtPosition( Position::South, true ); setShowLabelsAtPosition( Position::SouthEast, false ); setShowLabelsAtPosition( Position::Floating, false ); } /** * Creates an exact copy of this diagram. */ PolarDiagram * PolarDiagram::clone() const { PolarDiagram* newDiagram = new PolarDiagram( new Private( *d ) ); // This needs to be copied after the fact newDiagram->d->showDelimitersAtPosition = d->showDelimitersAtPosition; newDiagram->d->showLabelsAtPosition = d->showLabelsAtPosition; newDiagram->d->rotateCircularLabels = d->rotateCircularLabels; newDiagram->d->closeDatasets = d->closeDatasets; return newDiagram; } const QPair PolarDiagram::calculateDataBoundaries () const { if ( !checkInvariants(true) ) return QPair( QPointF( 0, 0 ), QPointF( 0, 0 ) ); const int rowCount = model()->rowCount(rootIndex()); const int colCount = model()->columnCount(rootIndex()); qreal xMin = 0.0; qreal xMax = colCount; qreal yMin = 0, yMax = 0; for ( int iCol=0; iColdata( model()->index( iRow, iCol, rootIndex() ) ).toReal(); // checked yMax = qMax( yMax, value ); yMin = qMin( yMin, value ); } } QPointF bottomLeft ( QPointF( xMin, yMin ) ); QPointF topRight ( QPointF( xMax, yMax ) ); return QPair ( bottomLeft, topRight ); } void PolarDiagram::paintEvent ( QPaintEvent*) { QPainter painter ( viewport() ); PaintContext ctx; ctx.setPainter ( &painter ); ctx.setRectangle( QRectF ( 0, 0, width(), height() ) ); paint ( &ctx ); } void PolarDiagram::resizeEvent ( QResizeEvent*) { } void PolarDiagram::paintPolarMarkers( PaintContext* ctx, const QPolygonF& polygon ) { Q_UNUSED(ctx); Q_UNUSED(polygon); // obsolete, since we are using real markers now! } void PolarDiagram::paint( PaintContext* ctx ) { qreal dummy1, dummy2; paint( ctx, true, dummy1, dummy2 ); paint( ctx, false, dummy1, dummy2 ); } void PolarDiagram::paint( PaintContext* ctx, bool calculateListAndReturnScale, qreal& newZoomX, qreal& newZoomY ) { // note: Not having any data model assigned is no bug // but we can not draw a diagram then either. if ( !checkInvariants(true) ) return; d->reverseMapper.clear(); const int rowCount = model()->rowCount( rootIndex() ); const int colCount = model()->columnCount( rootIndex() ); if ( calculateListAndReturnScale ) { // Check if all of the data value texts / data comments fit into the available space... d->labelPaintCache.clear(); for ( int iCol = 0; iCol < colCount; ++iCol ) { for ( int iRow=0; iRow < rowCount; ++iRow ) { QModelIndex index = model()->index( iRow, iCol, rootIndex() ); // checked const qreal value = model()->data( index ).toReal(); QPointF point = coordinatePlane()->translate( QPointF( value, iRow ) ) + ctx->rectangle().topLeft(); //qDebug() << point; - d->addLabel( &d->labelPaintCache, index, 0, PositionPoints( point ), + d->addLabel( &d->labelPaintCache, index, nullptr, PositionPoints( point ), Position::Center, Position::Center, value ); } } newZoomX = coordinatePlane()->zoomFactorX(); newZoomY = coordinatePlane()->zoomFactorY(); if ( d->labelPaintCache.paintReplay.count() ) { // ...and zoom out if necessary const qreal oldZoomX = newZoomX; const qreal oldZoomY = newZoomY; QRectF txtRectF; d->paintDataValueTextsAndMarkers( ctx, d->labelPaintCache, true, true, &txtRectF ); const QRect txtRect = txtRectF.toRect(); const QRect curRect = coordinatePlane()->geometry(); const qreal gapX = qMin( txtRect.left() - curRect.left(), curRect.right() - txtRect.right() ); const qreal gapY = qMin( txtRect.top() - curRect.top(), curRect.bottom() - txtRect.bottom() ); if ( gapX < 0.0 ) { newZoomX = oldZoomX * ( 1.0 + ( gapX - 1.0 ) / curRect.width() ); } if ( gapY < 0.0 ) { newZoomY = oldZoomY * ( 1.0 + ( gapY - 1.0 ) / curRect.height() ); } } } else { // Paint the data sets for ( int iCol = 0; iCol < colCount; ++iCol ) { //TODO(khz): As of yet PolarDiagram can not show per-segment line attributes // but it draws every polyline in one go - using one color. // This needs to be enhanced to allow for cell-specific settings // in the same way as LineDiagram does it. QBrush brush = d->datasetAttrs( iCol, KChart::DatasetBrushRole ).value(); QPolygonF polygon; for ( int iRow = 0; iRow < rowCount; ++iRow ) { QModelIndex index = model()->index( iRow, iCol, rootIndex() ); // checked const qreal value = model()->data( index ).toReal(); QPointF point = coordinatePlane()->translate( QPointF( value, iRow ) ) + ctx->rectangle().topLeft(); polygon.append( point ); //qDebug() << point; } if ( closeDatasets() && !polygon.isEmpty() ) { // close the circle by connecting the last data point to the first polygon.append( polygon.first() ); } PainterSaver painterSaver( ctx->painter() ); ctx->painter()->setRenderHint ( QPainter::Antialiasing ); ctx->painter()->setBrush( brush ); QPen p = d->datasetAttrs( iCol, KChart::DatasetPenRole ).value< QPen >(); if ( p.style() != Qt::NoPen ) { ctx->painter()->setPen( PrintingParameters::scalePen( p ) ); ctx->painter()->drawPolyline( polygon ); } } d->paintDataValueTextsAndMarkers( ctx, d->labelPaintCache, true ); } } void PolarDiagram::resize ( const QSizeF& size ) { AbstractPolarDiagram::resize(size); } /*virtual*/ qreal PolarDiagram::valueTotals () const { return model()->rowCount(rootIndex()); } /*virtual*/ qreal PolarDiagram::numberOfValuesPerDataset() const { return model() ? model()->rowCount(rootIndex()) : 0.0; } /*virtual*/ qreal PolarDiagram::numberOfGridRings() const { return 5; // FIXME } void PolarDiagram::setZeroDegreePosition( int degrees ) { Q_UNUSED( degrees ); qWarning() << "Deprecated PolarDiagram::setZeroDegreePosition() called, setting ignored."; } int PolarDiagram::zeroDegreePosition() const { qWarning() << "Deprecated PolarDiagram::zeroDegreePosition() called."; return 0; } void PolarDiagram::setRotateCircularLabels( bool rotateCircularLabels ) { d->rotateCircularLabels = rotateCircularLabels; } bool PolarDiagram::rotateCircularLabels() const { return d->rotateCircularLabels; } void PolarDiagram::setCloseDatasets( bool closeDatasets ) { d->closeDatasets = closeDatasets; } bool PolarDiagram::closeDatasets() const { return d->closeDatasets; } void PolarDiagram::setShowDelimitersAtPosition( Position position, bool showDelimiters ) { d->showDelimitersAtPosition[position.value()] = showDelimiters; } void PolarDiagram::setShowLabelsAtPosition( Position position, bool showLabels ) { d->showLabelsAtPosition[position.value()] = showLabels; } bool PolarDiagram::showDelimitersAtPosition( Position position ) const { return d->showDelimitersAtPosition[position.value()]; } bool PolarDiagram::showLabelsAtPosition( Position position ) const { return d->showLabelsAtPosition[position.value()]; } diff --git a/src/KChart/Polar/KChartPolarDiagram.h b/src/KChart/Polar/KChartPolarDiagram.h index 9d3042d..ff91845 100644 --- a/src/KChart/Polar/KChartPolarDiagram.h +++ b/src/KChart/Polar/KChartPolarDiagram.h @@ -1,106 +1,106 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTPOLARDIAGRAM_H #define KCHARTPOLARDIAGRAM_H #include "KChartPosition.h" #include "KChartAbstractPolarDiagram.h" QT_BEGIN_NAMESPACE class QPolygonF; QT_END_NAMESPACE namespace KChart { /** * @brief PolarDiagram defines a common polar diagram */ class KCHART_EXPORT PolarDiagram : public AbstractPolarDiagram { Q_OBJECT Q_DISABLE_COPY( PolarDiagram ) KCHART_DECLARE_DERIVED_DIAGRAM( PolarDiagram, PolarCoordinatePlane ) public: explicit PolarDiagram( - QWidget* parent = 0, PolarCoordinatePlane* plane = 0 ); + QWidget* parent = nullptr, PolarCoordinatePlane* plane = nullptr ); virtual ~PolarDiagram(); protected: // Implement AbstractDiagram /** \reimpl */ void paint ( PaintContext* paintContext ) Q_DECL_OVERRIDE; public: /** \reimpl */ void resize ( const QSizeF& area ) Q_DECL_OVERRIDE; // Implement AbstractPolarDiagram /** \reimpl */ qreal valueTotals () const Q_DECL_OVERRIDE; /** \reimpl */ qreal numberOfValuesPerDataset() const Q_DECL_OVERRIDE; /** \reimpl */ qreal numberOfGridRings() const Q_DECL_OVERRIDE; virtual PolarDiagram * clone() const; /** \deprecated Use PolarCoordinatePlane::setStartPosition( qreal degrees ) instead. */ void setZeroDegreePosition( int degrees ); /** \deprecated Use qreal PolarCoordinatePlane::startPosition instead. */ int zeroDegreePosition() const; void setRotateCircularLabels( bool rotateCircularLabels ); bool rotateCircularLabels() const; /** Close each of the data series by connecting the last point to its * respective start point */ void setCloseDatasets( bool closeDatasets ); bool closeDatasets() const; void setShowDelimitersAtPosition( Position position, bool showDelimiters ); void setShowLabelsAtPosition( Position position, bool showLabels ); bool showDelimitersAtPosition( Position position ) const; bool showLabelsAtPosition( Position position ) const; virtual void paint ( PaintContext* paintContext, bool calculateListAndReturnScale, qreal& newZoomX, qreal& newZoomY ); // KChart 3: references -> pointers protected: /** \reimpl */ const QPair calculateDataBoundaries() const Q_DECL_OVERRIDE; void paintEvent ( QPaintEvent* ) Q_DECL_OVERRIDE; void resizeEvent ( QResizeEvent* ) Q_DECL_OVERRIDE; virtual void paintPolarMarkers( PaintContext* ctx, const QPolygonF& polygon ); }; // End of class PolarDiagram } #endif // KCHARTPOLARDIAGRAM_H diff --git a/src/KChart/Polar/KChartRadarCoordinatePlane.h b/src/KChart/Polar/KChartRadarCoordinatePlane.h index e0aa10b..c2ac2fd 100644 --- a/src/KChart/Polar/KChartRadarCoordinatePlane.h +++ b/src/KChart/Polar/KChartRadarCoordinatePlane.h @@ -1,72 +1,72 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHART_RADAR_COORDINATEPLANE_H #define KCHART_RADAR_COORDINATEPLANE_H #include "KChartPolarCoordinatePlane.h" namespace KChart { class Chart; /** * @brief Radar coordinate plane */ class KCHART_EXPORT RadarCoordinatePlane : public PolarCoordinatePlane { Q_OBJECT Q_DISABLE_COPY( RadarCoordinatePlane ) KCHART_DECLARE_PRIVATE_DERIVED_PARENT( RadarCoordinatePlane, Chart* ) public: - explicit RadarCoordinatePlane ( Chart* parent = 0 ); + explicit RadarCoordinatePlane ( Chart* parent = nullptr ); ~RadarCoordinatePlane(); /** * Set the attributes to be used for axis captions. * * To disable axis captions, for example, your code should like this: * \code * TextAttributes ta = plane->textAttributes(); * ta.setVisible( false ); * plane-setTextAttributes( ta ); * \endcode */ void setTextAttributes( const TextAttributes & attr ); /** * \return The attributes used for axis. * * \note This function always returns a valid set of text attributes: * If no special text attributes was set a default one is * returned. * * \sa setTextAttributes */ const TextAttributes textAttributes() const; }; } #endif diff --git a/src/KChart/Polar/KChartRadarDiagram.cpp b/src/KChart/Polar/KChartRadarDiagram.cpp index f587568..e4851f0 100644 --- a/src/KChart/Polar/KChartRadarDiagram.cpp +++ b/src/KChart/Polar/KChartRadarDiagram.cpp @@ -1,335 +1,335 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartRadarDiagram.h" #include "KChartRadarDiagram_p.h" #include "KChartPaintContext.h" #include "KChartPainterSaver_p.h" #include "KChartMath_p.h" #include using namespace KChart; RadarDiagram::Private::Private() : closeDatasets( false ), reverseData( false ), fillAlpha( 0.0 ) { } RadarDiagram::Private::~Private() {} #define d d_func() RadarDiagram::RadarDiagram( QWidget* parent, RadarCoordinatePlane* plane ) : AbstractPolarDiagram( new Private( ), parent, plane ) { //init(); } RadarDiagram::~RadarDiagram() { } void RadarDiagram::init() { } /** * Creates an exact copy of this diagram. */ RadarDiagram * RadarDiagram::clone() const { RadarDiagram* newDiagram = new RadarDiagram( new Private( *d ) ); // This needs to be copied after the fact newDiagram->d->closeDatasets = d->closeDatasets; return newDiagram; } const QPair RadarDiagram::calculateDataBoundaries () const { if ( !checkInvariants(true) ) return QPair( QPointF( 0, 0 ), QPointF( 0, 0 ) ); const int rowCount = model()->rowCount(rootIndex()); const int colCount = model()->columnCount(rootIndex()); qreal xMin = 0.0; qreal xMax = colCount; qreal yMin = 0, yMax = 0; for ( int iCol=0; iColdata( model()->index( iRow, iCol, rootIndex() ) ).toReal(); // checked yMax = qMax( yMax, value ); yMin = qMin( yMin, value ); } } QPointF bottomLeft ( QPointF( xMin, yMin ) ); QPointF topRight ( QPointF( xMax, yMax ) ); return QPair ( bottomLeft, topRight ); } void RadarDiagram::paintEvent ( QPaintEvent*) { QPainter painter ( viewport() ); PaintContext ctx; ctx.setPainter ( &painter ); ctx.setRectangle( QRectF ( 0, 0, width(), height() ) ); paint ( &ctx ); } void RadarDiagram::paint( PaintContext* ctx ) { qreal dummy1, dummy2; paint( ctx, true, dummy1, dummy2 ); paint( ctx, false, dummy1, dummy2 ); } static qreal fitFontSizeToGeometry( const QString& text, const QFont& font, const QRectF& geometry, const TextAttributes& ta ) { QFont f = font; const qreal origResult = f.pointSizeF(); qreal result = origResult; const QSizeF mySize = geometry.size(); if ( mySize.isNull() ) return result; const QString t = text; QFontMetrics fm( f ); while ( true ) { const QSizeF textSize = rotatedRect( fm.boundingRect( t ), ta.rotation() ).normalized().size(); if ( textSize.height() <= mySize.height() && textSize.width() <= mySize.width() ) return result; result -= 0.5; if ( result <= 0.0 ) return origResult; f.setPointSizeF( result ); fm = QFontMetrics( f ); } } static QPointF scaleToRealPosition( const QPointF& origin, const QRectF& sourceRect, const QRectF& destRect, const AbstractCoordinatePlane& plane ) { QPointF result = plane.translate( origin ); result -= sourceRect.topLeft(); result.setX( result.x() / sourceRect.width() * destRect.width() ); result.setY( result.y() / sourceRect.height() * destRect.height() ); result += destRect.topLeft(); return result; } void RadarDiagram::setReverseData( bool val ) { d->reverseData = val; } bool RadarDiagram::reverseData() { return d->reverseData; } // local structure to remember the settings of a polygon inclusive the used color and pen. struct Polygon { QPolygonF polygon; QBrush brush; QPen pen; Polygon(const QPolygonF &polygon, const QBrush &brush, const QPen &pen) : polygon(polygon), brush(brush), pen(pen) {} }; void RadarDiagram::paint( PaintContext* ctx, bool calculateListAndReturnScale, qreal& newZoomX, qreal& newZoomY ) { // note: Not having any data model assigned is no bug // but we can not draw a diagram then either. if ( !checkInvariants(true) ) return; d->reverseMapper.clear(); const int rowCount = model()->rowCount( rootIndex() ); const int colCount = model()->columnCount( rootIndex() ); int iRow, iCol; const qreal min = dataBoundaries().first.y(); const qreal r = qAbs( min ) + dataBoundaries().second.y(); const qreal step = ( r - qAbs( min ) ) / ( numberOfGridRings() ); RadarCoordinatePlane* plane = dynamic_cast(ctx->coordinatePlane()); TextAttributes ta = plane->textAttributes(); QRectF fontRect = ctx->rectangle(); fontRect.setSize( QSizeF( fontRect.width(), step / 2.0 ) ); const qreal labelFontSize = fitFontSizeToGeometry( QString::fromLatin1( "TestXYWQgqy" ), ta.font(), fontRect, ta ); QFont labelFont = ta.font(); ctx->painter()->setPen( ta.pen() ); labelFont.setPointSizeF( labelFontSize ); const QFontMetricsF metric( labelFont ); const qreal labelHeight = metric.height(); QPointF offset; QRectF destRect = ctx->rectangle(); if ( ta.isVisible() ) { destRect.setY( destRect.y() + 2 * labelHeight ); destRect.setHeight( destRect.height() - 4 * labelHeight ); } if ( calculateListAndReturnScale ) { ctx->painter()->save(); // Check if all of the data value texts / data comments will fit // into the available space: d->labelPaintCache.clear(); ctx->painter()->save(); for ( iCol=0; iCol < colCount; ++iCol ) { for ( iRow=0; iRow < rowCount; ++iRow ) { QModelIndex index = model()->index( iRow, iCol, rootIndex() ); // checked const qreal value = model()->data( index ).toReal(); QPointF point = scaleToRealPosition( QPointF( value, iRow ), ctx->rectangle(), destRect, *ctx->coordinatePlane() ); - d->addLabel( &d->labelPaintCache, index, 0, PositionPoints( point ), + d->addLabel( &d->labelPaintCache, index, nullptr, PositionPoints( point ), Position::Center, Position::Center, value ); } } ctx->painter()->restore(); const qreal oldZoomX = coordinatePlane()->zoomFactorX(); const qreal oldZoomY = coordinatePlane()->zoomFactorY(); newZoomX = oldZoomX; newZoomY = oldZoomY; if ( d->labelPaintCache.paintReplay.count() ) { QRectF txtRectF; d->paintDataValueTextsAndMarkers( ctx, d->labelPaintCache, true, true, &txtRectF ); const QRect txtRect = txtRectF.toRect(); const QRect curRect = coordinatePlane()->geometry(); const qreal gapX = qMin( txtRect.left() - curRect.left(), curRect.right() - txtRect.right() ); const qreal gapY = qMin( txtRect.top() - curRect.top(), curRect.bottom() - txtRect.bottom() ); newZoomX = oldZoomX; newZoomY = oldZoomY; if ( gapX < 0.0 ) newZoomX *= 1.0 + (gapX-1.0) / curRect.width(); if ( gapY < 0.0 ) newZoomY *= 1.0 + (gapY-1.0) / curRect.height(); } ctx->painter()->restore(); } else { // Iterate through data sets and create a list of polygons out of them. QList polygons; for ( iCol=0; iCol < colCount; ++iCol ) { //TODO(khz): As of yet RadarDiagram can not show per-segment line attributes // but it draws every polyline in one go - using one color. // This needs to be enhanced to allow for cell-specific settings // in the same way as LineDiagram does it. QPolygonF polygon; QPointF point0; for ( iRow=0; iRow < rowCount; ++iRow ) { QModelIndex index = model()->index( iRow, iCol, rootIndex() ); // checked const qreal value = model()->data( index ).toReal(); QPointF point = scaleToRealPosition( QPointF( value, d->reverseData ? ( rowCount - iRow ) : iRow ), ctx->rectangle(), destRect, *ctx->coordinatePlane() ); polygon.append( point ); if ( ! iRow ) point0= point; } if ( closeDatasets() && rowCount ) polygon.append( point0 ); QBrush brush = d->datasetAttrs( iCol, KChart::DatasetBrushRole ).value(); QPen p = d->datasetAttrs( iCol, KChart::DatasetPenRole ).value< QPen >(); if ( p.style() != Qt::NoPen ) { polygons.append( Polygon( polygon, brush, PrintingParameters::scalePen( p ) ) ); } } // first fill the areas with the brush-color and the defined alpha-value. if (d->fillAlpha > 0.0) { Q_FOREACH(const Polygon& p, polygons) { PainterSaver painterSaver( ctx->painter() ); ctx->painter()->setRenderHint ( QPainter::Antialiasing ); QBrush br = p.brush; QColor c = br.color(); c.setAlphaF(d->fillAlpha); br.setColor(c); ctx->painter()->setBrush( br ); ctx->painter()->setPen( p.pen ); ctx->painter()->drawPolygon( p.polygon ); } } // then draw the poly-lines. Q_FOREACH(const Polygon& p, polygons) { PainterSaver painterSaver( ctx->painter() ); ctx->painter()->setRenderHint ( QPainter::Antialiasing ); ctx->painter()->setBrush( p.brush ); ctx->painter()->setPen( p.pen ); ctx->painter()->drawPolyline( p.polygon ); } d->paintDataValueTextsAndMarkers( ctx, d->labelPaintCache, true ); } } void RadarDiagram::resize ( const QSizeF& size ) { AbstractPolarDiagram::resize( size ); } /*virtual*/ qreal RadarDiagram::valueTotals () const { return model()->rowCount(rootIndex()); } /*virtual*/ qreal RadarDiagram::numberOfValuesPerDataset() const { return model() ? model()->rowCount(rootIndex()) : 0.0; } /*virtual*/ qreal RadarDiagram::numberOfGridRings() const { return 5; // FIXME } void RadarDiagram::setCloseDatasets( bool closeDatasets ) { d->closeDatasets = closeDatasets; } bool RadarDiagram::closeDatasets() const { return d->closeDatasets; } qreal RadarDiagram::fillAlpha() const { return d->fillAlpha; } void RadarDiagram::setFillAlpha(qreal alphaF) { d->fillAlpha = alphaF; } void RadarDiagram::resizeEvent ( QResizeEvent*) { } diff --git a/src/KChart/Polar/KChartRadarDiagram.h b/src/KChart/Polar/KChartRadarDiagram.h index 51e77d0..2efb125 100644 --- a/src/KChart/Polar/KChartRadarDiagram.h +++ b/src/KChart/Polar/KChartRadarDiagram.h @@ -1,98 +1,98 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTRADARDIAGRAM_H #define KCHARTRADARDIAGRAM_H #include "KChartPosition.h" #include "KChartPolarDiagram.h" #include "KChartRadarCoordinatePlane.h" QT_BEGIN_NAMESPACE class QPolygonF; QT_END_NAMESPACE namespace KChart { /** * @brief RadarDiagram defines a common radar diagram */ class KCHART_EXPORT RadarDiagram : public AbstractPolarDiagram { Q_OBJECT Q_DISABLE_COPY( RadarDiagram ) KCHART_DECLARE_DERIVED_DIAGRAM( RadarDiagram, RadarCoordinatePlane ) public: explicit RadarDiagram( - QWidget* parent = 0, RadarCoordinatePlane* plane = 0 ); + QWidget* parent = nullptr, RadarCoordinatePlane* plane = nullptr ); virtual ~RadarDiagram(); virtual void paint ( PaintContext* paintContext, bool calculateListAndReturnScale, qreal& newZoomX, qreal& newZoomY ); /** \reimpl */ void resize ( const QSizeF& area ) Q_DECL_OVERRIDE; /** \reimpl */ qreal valueTotals () const Q_DECL_OVERRIDE; /** \reimpl */ qreal numberOfValuesPerDataset() const Q_DECL_OVERRIDE; /** \reimpl */ qreal numberOfGridRings() const Q_DECL_OVERRIDE; /** * if val is true the diagram will mirror the diagram datapoints */ void setReverseData( bool val ); bool reverseData(); virtual RadarDiagram * clone() const; /** * Close each of the data series by connecting the last point to its * respective start point */ void setCloseDatasets( bool closeDatasets ); bool closeDatasets() const; /** * Fill the areas of the radar chart with there respective color defined * via KChart::DatasetBrushRole. The value defines the alpha of the * color to use. If set to 0.0 (the default) then the radar areas will * not be filled with any color. If set to 1.0 then the areas will be * solid filled and are not transparent. */ qreal fillAlpha() const; void setFillAlpha(qreal alphaF); protected: /** \reimpl */ const QPair calculateDataBoundaries() const Q_DECL_OVERRIDE; void paintEvent ( QPaintEvent* ) Q_DECL_OVERRIDE; void resizeEvent ( QResizeEvent* ) Q_DECL_OVERRIDE; void paint ( PaintContext* paintContext ) Q_DECL_OVERRIDE; }; // End of class RadarDiagram } #endif // KCHARTRADARDIAGRAM_H diff --git a/src/KChart/Polar/KChartRingDiagram.h b/src/KChart/Polar/KChartRingDiagram.h index 70207fc..3710c4f 100644 --- a/src/KChart/Polar/KChartRingDiagram.h +++ b/src/KChart/Polar/KChartRingDiagram.h @@ -1,89 +1,89 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTRINGDIAGRAM_H #define KCHARTRINGDIAGRAM_H #include "KChartAbstractPieDiagram.h" namespace KChart { /** * @brief RingDiagram defines a common ring diagram */ class KCHART_EXPORT RingDiagram : public AbstractPieDiagram { Q_OBJECT Q_DISABLE_COPY( RingDiagram ) KCHART_DECLARE_DERIVED_DIAGRAM( RingDiagram, PolarCoordinatePlane ) public: explicit RingDiagram( - QWidget* parent = 0, PolarCoordinatePlane* plane = 0 ); + QWidget* parent = nullptr, PolarCoordinatePlane* plane = nullptr ); virtual ~RingDiagram(); protected: // Implement AbstractDiagram /** \reimpl */ void paint( PaintContext* paintContext ) Q_DECL_OVERRIDE; public: /** \reimpl */ void resize( const QSizeF& area ) Q_DECL_OVERRIDE; // Implement AbstractPolarDiagram /** \reimpl */ qreal valueTotals() const Q_DECL_OVERRIDE; /** \reimpl */ qreal numberOfValuesPerDataset() const Q_DECL_OVERRIDE; qreal numberOfDatasets() const Q_DECL_OVERRIDE; /** \reimpl */ qreal numberOfGridRings() const Q_DECL_OVERRIDE; qreal valueTotals( int dataset ) const; virtual RingDiagram * clone() const; /** * Returns true if both diagrams have the same settings. */ bool compare( const RingDiagram* other ) const; void setRelativeThickness( bool relativeThickness ); bool relativeThickness() const; virtual void setExpandWhenExploded( bool expand ); virtual bool expandWhenExploded() const; protected: /** \reimpl */ const QPair calculateDataBoundaries() const Q_DECL_OVERRIDE; void paintEvent( QPaintEvent* ) Q_DECL_OVERRIDE; void resizeEvent( QResizeEvent* ) Q_DECL_OVERRIDE; private: void drawOneSlice( QPainter* painter, uint dataset, uint slice, qreal granularity ); void drawPieSurface( QPainter* painter, uint dataset, uint slice, qreal granularity ); QPointF pointOnEllipse( const QRectF& rect, int dataset, int slice, bool outer, qreal angle, qreal totalGapFactor, qreal totalExplodeFactor ); }; // End of class RingDiagram } #endif // KCHARTRINGDIAGRAM_H diff --git a/src/KChart/ReverseMapper.cpp b/src/KChart/ReverseMapper.cpp index d59344d..38fa9b9 100644 --- a/src/KChart/ReverseMapper.cpp +++ b/src/KChart/ReverseMapper.cpp @@ -1,178 +1,178 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "ReverseMapper.h" #include #include #include #include #include #include #include "KChartAbstractDiagram.h" #include "ChartGraphicsItem.h" using namespace KChart; ReverseMapper::ReverseMapper() - : m_scene( 0 ) - , m_diagram( 0 ) + : m_scene( nullptr ) + , m_diagram( nullptr ) { } ReverseMapper::ReverseMapper( AbstractDiagram* diagram ) - : m_scene( 0 ) + : m_scene( nullptr ) , m_diagram( diagram ) { } ReverseMapper::~ReverseMapper() { - delete m_scene; m_scene = 0; + delete m_scene; m_scene = nullptr; } void ReverseMapper::setDiagram( AbstractDiagram* diagram ) { m_diagram = diagram; } void ReverseMapper::clear() { m_itemMap.clear(); delete m_scene; m_scene = new QGraphicsScene(); } QModelIndexList ReverseMapper::indexesIn( const QRect& rect ) const { Q_ASSERT( m_diagram ); if ( m_scene && m_scene->sceneRect().intersects( rect ) ) { QList items = m_scene->items( rect ); QModelIndexList indexes; Q_FOREACH( QGraphicsItem* item, items ) { ChartGraphicsItem* i = qgraphicsitem_cast( item ); if ( i ) { QModelIndex index ( m_diagram->model()->index( i->row(), i->column(), m_diagram->rootIndex() ) ); // checked indexes << index; } } return indexes; } else { return QModelIndexList(); } } QModelIndexList ReverseMapper::indexesAt( const QPointF& point ) const { Q_ASSERT( m_diagram ); if ( m_scene && m_scene->sceneRect().contains( point ) ) { QList items = m_scene->items( point ); QModelIndexList indexes; Q_FOREACH( QGraphicsItem* item, items ) { ChartGraphicsItem* i = qgraphicsitem_cast( item ); if ( i ) { QModelIndex index ( m_diagram->model()->index( i->row(), i->column(), m_diagram->rootIndex() ) ); // checked if ( !indexes.contains(index) ) indexes << index; } } return indexes; } else { return QModelIndexList(); } } QPolygonF ReverseMapper::polygon( int row, int column ) const { if ( !m_diagram->model()->hasIndex( row, column, m_diagram->rootIndex() ) ) return QPolygon(); const QModelIndex index = m_diagram->model()->index( row, column, m_diagram->rootIndex() ); // checked return m_itemMap.contains( index ) ? m_itemMap[ index ]->polygon() : QPolygon(); } QRectF ReverseMapper::boundingRect( int row, int column ) const { if ( !m_diagram->model()->hasIndex( row, column, m_diagram->rootIndex() ) ) return QRectF(); const QModelIndex index = m_diagram->model()->index( row, column, m_diagram->rootIndex() ); // checked return m_itemMap.contains( index ) ? m_itemMap[ index ]->polygon().boundingRect() : QRectF(); } void ReverseMapper::addItem( ChartGraphicsItem* item ) { Q_ASSERT( m_scene ); m_scene->addItem( item ); m_itemMap.insert( m_diagram->model()->index( item->row(), item->column(), m_diagram->rootIndex() ), item ); // checked } void ReverseMapper::addRect( int row, int column, const QRectF& rect ) { addPolygon( row, column, QPolygonF( rect ) ); } void ReverseMapper::addPolygon( int row, int column, const QPolygonF& polygon ) { ChartGraphicsItem* item = new ChartGraphicsItem( row, column ); item->setPolygon( polygon ); addItem( item ); } void ReverseMapper::addCircle( int row, int column, const QPointF& location, const QSizeF& diameter ) { QPainterPath path; QPointF ossfet( -0.5*diameter.width(), -0.5*diameter.height() ); path.addEllipse( QRectF( location + ossfet, diameter ) ); addPolygon( row, column, QPolygonF( path.toFillPolygon() ) ); } void ReverseMapper::addLine( int row, int column, const QPointF& from, const QPointF& to ) { // that's no line, dude... make a small circle around that point, instead if ( from == to ) { addCircle( row, column, from, QSizeF( 1.5, 1.5 ) ); return; } // lines do not make good polygons to click on. we calculate a 2 // pixel wide rectangle, where the original line is excatly // centered in. // make a 3 pixel wide polygon from the line: static const QPointF pixel( 1.0, 1.0 ); QPointF left, right; if ( from.x() < to.x() ) { left = from; right = to; } else { right = from; left = to; } const QPointF lineVector( right - left ); const qreal lineVectorLength = sqrt( lineVector.x() * lineVector.x() + lineVector.y() * lineVector.y() ); const QPointF lineVectorUnit( lineVector / lineVectorLength ); const QPointF normOfLineVectorUnit( -lineVectorUnit.y(), lineVectorUnit.x() ); // now the four polygon end points: const QPointF one( left - lineVectorUnit + normOfLineVectorUnit ); const QPointF two( left - lineVectorUnit - normOfLineVectorUnit ); const QPointF three( right + lineVectorUnit - normOfLineVectorUnit ); const QPointF four( right + lineVectorUnit + normOfLineVectorUnit ); addPolygon( row, column, QPolygonF() << one << two << three << four ); } diff --git a/src/KChart/Ternary/KChartAbstractTernaryDiagram.h b/src/KChart/Ternary/KChartAbstractTernaryDiagram.h index 204bdfc..a1f5525 100644 --- a/src/KChart/Ternary/KChartAbstractTernaryDiagram.h +++ b/src/KChart/Ternary/KChartAbstractTernaryDiagram.h @@ -1,59 +1,59 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTABSTRACTTERNARYDIAGRAM_H #define KCHARTABSTRACTTERNARYDIAGRAM_H #include "KChartAbstractDiagram.h" #include "KChartTernaryAxis.h" namespace KChart { class TernaryCoordinatePlane; class TernaryAxis; /** * @brief Base class for diagrams based on a ternary coordinate plane. */ class KCHART_EXPORT AbstractTernaryDiagram : public AbstractDiagram { Q_OBJECT Q_DISABLE_COPY( AbstractTernaryDiagram ) KCHART_DECLARE_DERIVED_DIAGRAM( AbstractTernaryDiagram, TernaryCoordinatePlane ) public: - explicit AbstractTernaryDiagram ( QWidget* parent = 0, - TernaryCoordinatePlane* plane = 0 ); + explicit AbstractTernaryDiagram ( QWidget* parent = nullptr, + TernaryCoordinatePlane* plane = nullptr ); virtual ~AbstractTernaryDiagram(); void paint (PaintContext *paintContext) Q_DECL_OVERRIDE; virtual void addAxis( TernaryAxis* axis ); virtual void takeAxis( TernaryAxis* axis ); virtual TernaryAxisList axes () const; protected: const QPair< QPointF, QPointF > calculateDataBoundaries () const Q_DECL_OVERRIDE = 0; }; } #endif diff --git a/src/KChart/Ternary/KChartAbstractTernaryDiagram_p.h b/src/KChart/Ternary/KChartAbstractTernaryDiagram_p.h index b8fe995..6a7ce80 100644 --- a/src/KChart/Ternary/KChartAbstractTernaryDiagram_p.h +++ b/src/KChart/Ternary/KChartAbstractTernaryDiagram_p.h @@ -1,102 +1,102 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTABSTRACTTERNARYDIAGRAM_P_H #define KCHARTABSTRACTTERNARYDIAGRAM_P_H // // W A R N I N G // ------------- // // This file is not part of the KD Chart API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "KChartAbstractTernaryDiagram.h" #include "KChartTernaryCoordinatePlane.h" #include #include #include #include "KChartPainterSaver_p.h" #include "KChartMath_p.h" #include "ReverseMapper.h" #include "ChartGraphicsItem.h" namespace KChart { /** * \internal */ class Q_DECL_HIDDEN AbstractTernaryDiagram::Private : public AbstractDiagram::Private { friend class AbstractTernaryDiagram; public: Private(); ~Private() {} Private( const Private& rhs ) : AbstractDiagram::Private( rhs ), // Do not copy axes and reference diagrams. axesList(), - referenceDiagram( 0 ), + referenceDiagram( nullptr ), referenceDiagramOffset() { } TernaryAxisList axesList; AbstractTernaryDiagram* referenceDiagram; QPointF referenceDiagramOffset; void drawPoint( QPainter* p, int row, int column, const QPointF& widgetLocation ) { // Q_ASSERT( false ); // unused, to be removed static const qreal Diameter = 5.0; static const qreal Radius = Diameter / 2.0; QRectF ellipseRect( widgetLocation - QPointF( Radius, Radius ), QSizeF( Diameter, Diameter ) ); p->drawEllipse( ellipseRect ); reverseMapper.addRect( row, column, ellipseRect ); } virtual void paint( PaintContext* paintContext ) { paintContext->painter()->setRenderHint( QPainter::Antialiasing, antiAliasing ); if ( !axesList.isEmpty() ) { Q_FOREACH( TernaryAxis* axis, axesList ) { PainterSaver s( paintContext->painter() ); axis->paintCtx( paintContext ); } } } }; KCHART_IMPL_DERIVED_DIAGRAM( AbstractTernaryDiagram, AbstractDiagram, TernaryCoordinatePlane ) } #endif /* KCHARTABSTRACTTERNARYDIAGRAM_P_H */ diff --git a/src/KChart/Ternary/KChartTernaryAxis.cpp b/src/KChart/Ternary/KChartTernaryAxis.cpp index b91885d..d7e3210 100644 --- a/src/KChart/Ternary/KChartTernaryAxis.cpp +++ b/src/KChart/Ternary/KChartTernaryAxis.cpp @@ -1,280 +1,280 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartTernaryAxis.h" #include #include #include #include "TernaryConstants.h" #include "KChartTernaryCoordinatePlane.h" #include "KChartAbstractTernaryDiagram.h" #include "KChartLayoutItems.h" #include "KChartTextLabelCache.h" using namespace KChart; // m_label and m_fifty do not have to be pointers, once the class is // pimpled (PrerenderedLabel is not published API) TernaryAxis::TernaryAxis ( AbstractTernaryDiagram* diagram) : AbstractAxis( diagram ) , m_position( KChartEnums::PositionUnknown ) , m_label( new PrerenderedLabel ) , m_fifty( new PrerenderedLabel ) { resetTitleTextAttributes(); setPosition( KChartEnums::PositionSouth ); // arbitrary m_fifty->setText( QObject::tr( "50%" ) ); // const // FIXME is this consistent with other diagram/axis/plane implementations? diagram->addAxis( this ); } TernaryAxis::~TernaryAxis() { - delete m_label; m_label = 0; - delete m_fifty; m_fifty = 0; + delete m_label; m_label = nullptr; + delete m_fifty; m_fifty = nullptr; } void TernaryAxis::paintAll (QPainter &) { // not used } void TernaryAxis::paint (QPainter *) { // not used } void TernaryAxis::paintCtx (PaintContext * paintContext) { QPainter* p = paintContext->painter(); TernaryCoordinatePlane* plane = (TernaryCoordinatePlane*) paintContext->coordinatePlane(); // QObject* refArea = plane->parent(); QRectF titleArea; // paint the axis label (across the triangle, that one): QList labels; labels << m_label << m_fifty; Q_FOREACH( PrerenderedLabel* label, labels ) { const QPixmap& pixmap = label->pixmap(); QPointF point = plane->translate( label->position() ) - label->referencePointLocation(); p->drawPixmap( point, pixmap ); } } bool TernaryAxis::isEmpty() const { // todo: what's this method for? return false; } QRect TernaryAxis::geometry () const { return m_geometry; } void TernaryAxis::setGeometry (const QRect &rect) { m_geometry = rect; } QSize TernaryAxis::minimumSize () const { // todo: return realistic sizes return QSize( 100, 100 ); } QSize TernaryAxis::maximumSize () const { return QSize( 300, 200 ); } QSize TernaryAxis::sizeHint () const { return QSize( 150, 100 ); } Qt::Orientations TernaryAxis::expandingDirections () const { return Qt::Vertical | Qt::Horizontal; } const Position TernaryAxis::position () const { return m_position; } void TernaryAxis::setPosition (Position p) { if ( p == position() ) return; if ( p != KChartEnums::PositionWest && p != KChartEnums::PositionEast && p != KChartEnums::PositionSouth ) { qDebug() << "TernaryAxis::setPosition: only south, east and west are supported " "positions for ternary axes."; return; } if ( m_title.isEmpty() ) switch ( p.value() ) { case KChartEnums::PositionSouth: m_label->setText( tr( "A" ) ); break; case KChartEnums::PositionWest: m_label->setText( tr( "C" ) ); break; case KChartEnums::PositionEast: m_label->setText( tr( "B" ) ); break; default: break; } m_position = p; updatePrerenderedLabels(); // position has changed } void TernaryAxis::setTitleText( const QString& text ) { m_title = text; // do not remove m_label->setText( text ); } QString TernaryAxis::titleText() const { return m_label->text(); } void TernaryAxis::setTitleTextAttributes( const TextAttributes &a ) { m_titleAttributes = a; updatePrerenderedLabels(); } TextAttributes TernaryAxis::titleTextAttributes() const { return m_titleAttributes; } void TernaryAxis::resetTitleTextAttributes() { TextAttributes a; m_titleAttributes = a; updatePrerenderedLabels(); } bool TernaryAxis::hasDefaultTitleTextAttributes() const { TextAttributes a; return m_titleAttributes == a; } void TernaryAxis::updatePrerenderedLabels() { TextAttributes attributes = titleTextAttributes(); qreal axisLabelAngle = 0.0; qreal fiftyMarkAngle = 0.0; QPointF axisLabelPosition; QPointF fiftyMarkPosition; KChartEnums::PositionValue fiftyMarkReferencePoint = KChartEnums::PositionUnknown; switch ( position().value() ) { case KChartEnums::PositionSouth: // this is the axis on the other side of A axisLabelAngle = 0.0; fiftyMarkAngle = 0.0; axisLabelPosition = TriangleTop; fiftyMarkPosition = 0.5 * AxisVector_B_C - RelMarkerLength * Norm_B_C; fiftyMarkReferencePoint = KChartEnums::PositionNorth; break; case KChartEnums::PositionEast: // this is the axis on the other side of B axisLabelAngle = 240.0; fiftyMarkAngle = 60; axisLabelPosition = TriangleBottomLeft; fiftyMarkPosition = AxisVector_B_C + 0.5 * AxisVector_C_A - RelMarkerLength * Norm_C_A; fiftyMarkReferencePoint = KChartEnums::PositionSouth; break; case KChartEnums::PositionWest: // this is the axis on the other side of C axisLabelAngle = 120.0; fiftyMarkAngle = 300.0; axisLabelPosition = TriangleBottomRight; fiftyMarkPosition = 0.5 * AxisVector_B_A + RelMarkerLength * Norm_B_A; fiftyMarkReferencePoint = KChartEnums::PositionSouth; break; case KChartEnums::PositionUnknown: break; // initial value default: qDebug() << "TernaryAxis::updatePrerenderedLabel: unknown location"; }; m_label->setFont( attributes.font() ); // m_label->setText( titleText() ); // done by setTitleText() m_label->setAngle( axisLabelAngle ); m_label->setPosition( axisLabelPosition ); m_label->setReferencePoint( KChartEnums::PositionSouth ); QFont font = attributes.font(); font.setPointSizeF( 0.85 * font.pointSizeF() ); m_fifty->setFont( font ); m_fifty->setAngle( fiftyMarkAngle ); m_fifty->setPosition( fiftyMarkPosition ); m_fifty->setReferencePoint( fiftyMarkReferencePoint ); } QPair TernaryAxis::requiredMargins() const { QSizeF topleft( 0.0, 0.0 ); QSizeF bottomRight( 0.0, 0.0 ); switch ( position().value() ) { case KChartEnums::PositionSouth: // the label of the south axis is, in fact, up north. topleft.setHeight( m_label->pixmap().height() ); bottomRight.setHeight( m_fifty->pixmap().height() ); break; case KChartEnums::PositionWest: bottomRight.setWidth( m_label->pixmap().width() - m_label->referencePointLocation().x() ); bottomRight.setHeight( m_label->pixmap().height() - m_label->referencePointLocation().y() ); break; case KChartEnums::PositionEast: topleft.setWidth( m_label->pixmap().width() - ( m_label->pixmap().width() - m_label->referencePointLocation().x() ) ); bottomRight.setHeight( m_label->pixmap().height() - ( m_label->pixmap().height() - m_label->referencePointLocation().y() ) ); break; default: qDebug() << "TernaryAxis::requiredMargins: unknown location"; } // qDebug() << "TernaryAxis::requiredMargins:" << topleft << bottomRight; return QPair( topleft, bottomRight ); } diff --git a/src/KChart/Ternary/KChartTernaryAxis.h b/src/KChart/Ternary/KChartTernaryAxis.h index eaa11d0..3f77c62 100644 --- a/src/KChart/Ternary/KChartTernaryAxis.h +++ b/src/KChart/Ternary/KChartTernaryAxis.h @@ -1,93 +1,93 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTTERNARYAXIS_H #define KCHARTTERNARYAXIS_H #include #include #include class PrerenderedLabel; namespace KChart { class AbstractTernaryDiagram; /** * The class for ternary axes */ class KCHART_EXPORT TernaryAxis : public AbstractAxis { Q_OBJECT Q_DISABLE_COPY( TernaryAxis ) KCHART_DECLARE_PRIVATE_DERIVED_PARENT( TernaryAxis, AbstractDiagram* ) public: - explicit TernaryAxis ( AbstractTernaryDiagram* diagram = 0 ); + explicit TernaryAxis ( AbstractTernaryDiagram* diagram = nullptr ); ~TernaryAxis(); void paintAll( QPainter &) Q_DECL_OVERRIDE; void paint (QPainter *) Q_DECL_OVERRIDE; void paintCtx (PaintContext *) Q_DECL_OVERRIDE; QRect geometry () const Q_DECL_OVERRIDE; void setGeometry (const QRect &rect) Q_DECL_OVERRIDE; bool isEmpty () const Q_DECL_OVERRIDE; QSize minimumSize () const Q_DECL_OVERRIDE; QSize maximumSize () const Q_DECL_OVERRIDE; QSize sizeHint () const Q_DECL_OVERRIDE; Qt::Orientations expandingDirections () const Q_DECL_OVERRIDE; virtual const Position position () const; virtual void setPosition (Position p); void setTitleText( const QString& text ); QString titleText() const; void setTitleTextAttributes( const TextAttributes &a ); TextAttributes titleTextAttributes() const; void resetTitleTextAttributes(); bool hasDefaultTitleTextAttributes() const; QPair requiredMargins() const; private: void updatePrerenderedLabels(); // TODO, move class variables to private class QRect m_geometry; Position m_position; QString m_title; TextAttributes m_titleAttributes; // FIXME (Mirko): Move axis labels from grid to here, do not // expose them, just paint them. Use title text for text. Make // a function to allow the coordinate plane to calculate the // necessary margins, like this: PrerenderedLabel* m_label; PrerenderedLabel* m_fifty; }; typedef QList TernaryAxisList; } #endif diff --git a/src/KChart/Ternary/KChartTernaryCoordinatePlane.cpp b/src/KChart/Ternary/KChartTernaryCoordinatePlane.cpp index fcc6095..6945af8 100644 --- a/src/KChart/Ternary/KChartTernaryCoordinatePlane.cpp +++ b/src/KChart/Ternary/KChartTernaryCoordinatePlane.cpp @@ -1,190 +1,190 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartTernaryCoordinatePlane.h" #include "KChartTernaryCoordinatePlane_p.h" #include #include #include "KChartPaintContext.h" #include "KChartPainterSaver_p.h" #include "KChartTernaryAxis.h" #include "KChartAbstractTernaryDiagram.h" #include "TernaryConstants.h" using namespace KChart; #define d d_func() TernaryCoordinatePlane::Private::Private() : AbstractCoordinatePlane::Private() { } TernaryCoordinatePlane::TernaryCoordinatePlane( Chart* parent ) : AbstractCoordinatePlane( new Private(), parent ) { } TernaryCoordinatePlane::~TernaryCoordinatePlane() { } void TernaryCoordinatePlane::init() { } void TernaryCoordinatePlane::addDiagram( AbstractDiagram* diagram ) { Q_ASSERT_X ( dynamic_cast( diagram ), "TernaryCoordinatePlane::addDiagram", "Only ternary " "diagrams can be added to a ternary coordinate plane!" ); AbstractCoordinatePlane::addDiagram ( diagram ); } void TernaryCoordinatePlane::layoutDiagrams() { // this is our "resize event": // all diagrams always take the same space, nothing to be done here // the "inner" margin (adjustments to diagram coordinates) QRectF diagramNativeRectangle ( QPointF( 0.0, 0.0 ), QSizeF( TriangleWidth, TriangleHeight ) ); QPair margins = grid()->requiredMargins(); d->diagramRect = areaGeometry(); diagramNativeRectangle.adjust (-margins.first.width(), -margins.first.height(), margins.second.width(), margins.second.height() ); // the "outer" margin (distance between diagram contents and area, // determined by axis label overlap { QSizeF topleft( 0.0, 0.0 ); QSizeF bottomRight( 0.0, 0.0 ); Q_FOREACH( AbstractDiagram* abstractDiagram, diagrams() ) { AbstractTernaryDiagram* diagram = qobject_cast( abstractDiagram ); Q_ASSERT( diagram ); Q_FOREACH( TernaryAxis* axis, diagram->axes() ) { QPair margin = axis->requiredMargins(); topleft = topleft.expandedTo( margin.first ); bottomRight = bottomRight.expandedTo( margin.second ); } } d->diagramRectContainer = d->diagramRect.adjusted( topleft.width(), topleft.height(), -bottomRight.width(), -bottomRight.height() ); } // now calculate isometric projection, x and y widget coordinate // units, and location of (0.0, 0.0) in diagram coordinates QPointF zeroZeroPoint = d->diagramRectContainer.bottomLeft(); qreal w = d->diagramRectContainer.width(); qreal h = d->diagramRectContainer.height(); qreal usableWidth; qreal usableHeight; if ( TriangleHeight * w > h ) { // shorten width: usableWidth = h / diagramNativeRectangle.height(); usableHeight = h; zeroZeroPoint.setX( zeroZeroPoint.x() + ( w - usableWidth ) / 2 ); } else { // reduce height: usableWidth = w; usableHeight = diagramNativeRectangle.height() * w; zeroZeroPoint.setY( zeroZeroPoint.y() - ( h - usableHeight ) / 2 ); } // the rectangle has 1 as it's width, and TriangleHeight as it's // height - so this is how we translate that to widget coordinates: d->xUnit = usableWidth / diagramNativeRectangle.width(); // only because we normalize the values to [0..1] d->yUnit = -usableHeight / diagramNativeRectangle.height(); // now move zeroZeroPoint so that it does not include the tick marks { qreal descent = diagramNativeRectangle.height() - TriangleHeight; qreal rightShift = -diagramNativeRectangle.x(); zeroZeroPoint += QPointF( rightShift * d->xUnit, descent * d->yUnit ); } d->diagramRect.setBottomLeft( zeroZeroPoint ); d->diagramRect.setTopRight( QPointF( usableWidth, -usableHeight ) + zeroZeroPoint ); } const QPointF TernaryCoordinatePlane::translate( const QPointF& point ) const { return QPointF( d->diagramRect.bottomLeft().x() + point.x() * d->xUnit, d->diagramRect.bottomLeft().y() + point.y() * d->yUnit ); } QSize TernaryCoordinatePlane::minimumSizeHint() const { // FIXME temp return QSize(); } QSizePolicy TernaryCoordinatePlane::sizePolicy() const { return QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding ); } void TernaryCoordinatePlane::paint( QPainter* painter ) { PainterSaver s( painter ); // FIXME: this is not a good location for that: painter->setRenderHint(QPainter::Antialiasing, true ); AbstractDiagramList diags = diagrams(); if ( !diags.isEmpty() ) { PaintContext ctx; ctx.setPainter ( painter ); ctx.setCoordinatePlane ( this ); const QRectF drawArea( areaGeometry() ); ctx.setRectangle ( drawArea ); // paint the coordinate system rulers: - Q_ASSERT( d->grid != 0 ); + Q_ASSERT( d->grid != nullptr ); d->grid->drawGrid( &ctx ); // paint the diagrams: for ( int i = 0; i < diags.size(); i++ ) { PainterSaver diagramPainterSaver( painter ); diags[i]->paint ( &ctx ); } } } DataDimensionsList TernaryCoordinatePlane::getDataDimensionsList() const { // not needed return DataDimensionsList(); } TernaryGrid* TernaryCoordinatePlane::grid() const { TernaryGrid* ternaryGrid = static_cast( d->grid ); Q_ASSERT( dynamic_cast( d->grid ) ); return ternaryGrid; } #undef d diff --git a/src/KChart/Ternary/KChartTernaryCoordinatePlane.h b/src/KChart/Ternary/KChartTernaryCoordinatePlane.h index 929aeae..eab912b 100644 --- a/src/KChart/Ternary/KChartTernaryCoordinatePlane.h +++ b/src/KChart/Ternary/KChartTernaryCoordinatePlane.h @@ -1,63 +1,63 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTTERNARYCOORDINATEPLANE_H #define KCHARTTERNARYCOORDINATEPLANE_H #include "KChartAbstractCoordinatePlane.h" namespace KChart { class TernaryGrid; /** * @brief Ternary coordinate plane */ class KCHART_EXPORT TernaryCoordinatePlane : public AbstractCoordinatePlane { Q_OBJECT Q_DISABLE_COPY( TernaryCoordinatePlane ) KCHART_DECLARE_PRIVATE_DERIVED_PARENT( TernaryCoordinatePlane, Chart* ) public: - explicit TernaryCoordinatePlane( Chart* parent = 0 ); + explicit TernaryCoordinatePlane( Chart* parent = nullptr ); ~TernaryCoordinatePlane(); void addDiagram( AbstractDiagram* diagram ) Q_DECL_OVERRIDE; void layoutDiagrams() Q_DECL_OVERRIDE; const QPointF translate ( const QPointF& diagramPoint ) const Q_DECL_OVERRIDE; void paint( QPainter* ) Q_DECL_OVERRIDE; DataDimensionsList getDataDimensionsList() const Q_DECL_OVERRIDE; /** \reimpl */ QSize minimumSizeHint() const; /** \reimpl */ QSizePolicy sizePolicy() const; private: TernaryGrid* grid() const; }; } #endif diff --git a/src/KChart/Ternary/KChartTernaryLineDiagram.cpp b/src/KChart/Ternary/KChartTernaryLineDiagram.cpp index 2422d3d..49f524f 100644 --- a/src/KChart/Ternary/KChartTernaryLineDiagram.cpp +++ b/src/KChart/Ternary/KChartTernaryLineDiagram.cpp @@ -1,155 +1,155 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartTernaryLineDiagram.h" #include "KChartTernaryLineDiagram_p.h" #include #include #include #include "KChartLineAttributes.h" #include "KChartDataValueAttributes.h" #include "KChartMarkerAttributes.h" #include "TernaryPoint.h" #include "TernaryConstants.h" #include "KChartPainterSaver_p.h" using namespace KChart; #define d d_func() TernaryLineDiagram::Private::Private() : AbstractTernaryDiagram::Private() { } TernaryLineDiagram::TernaryLineDiagram ( QWidget* parent, TernaryCoordinatePlane* plane ) : AbstractTernaryDiagram( new Private(), parent, plane ) { init(); setDatasetDimensionInternal( 3 ); // the third column is implicit DataValueAttributes dataValueAttributes; dataValueAttributes.setVisible( true ); MarkerAttributes markerAttributes; markerAttributes.setMarkerStyle( MarkerAttributes::MarkerCircle ); markerAttributes.setVisible( true ); dataValueAttributes.setMarkerAttributes( markerAttributes ); attributesModel()->setDefaultForRole( KChart::DataValueLabelAttributesRole, qVariantFromValue( dataValueAttributes ) ); } TernaryLineDiagram::~TernaryLineDiagram() { } void TernaryLineDiagram::init() { } void TernaryLineDiagram::resize (const QSizeF& area) { AbstractTernaryDiagram::resize( area ); } void TernaryLineDiagram::paint (PaintContext *paintContext) { d->reverseMapper.clear(); d->paint( paintContext ); // sanity checks: - if ( model() == 0 ) return; + if ( model() == nullptr ) return; QPainter* p = paintContext->painter(); PainterSaver s( p ); TernaryCoordinatePlane* plane = (TernaryCoordinatePlane*) paintContext->coordinatePlane(); Q_ASSERT( plane ); qreal x, y, z; // for some reason(?) TernaryPointDiagram is using per-diagram DVAs only: const DataValueAttributes attrs( dataValueAttributes() ); d->forgetAlreadyPaintedDataValues(); int columnCount = model()->columnCount( rootIndex() ); QPointF start; for (int column=0; columnrowCount( rootIndex() ); for ( int row = 0; row < numrows; row++ ) { // see if there is data otherwise skip QModelIndex base = model()->index( row, column ); // checked if ( ! model()->data( base ).isNull() ) { p->setPen( PrintingParameters::scalePen( pen( base ) ) ); p->setBrush( brush( base ) ); // retrieve data x = qMax( model()->data( model()->index( row, column, rootIndex() ) ).toReal(), // checked 0.0 ); y = qMax( model()->data( model()->index( row, column+1, rootIndex() ) ).toReal(), // checked 0.0 ); z = qMax( model()->data( model()->index( row, column+2, rootIndex() ) ).toReal(), // checked 0.0 ); qreal total = x + y + z; if ( fabs( total ) > 3 * std::numeric_limits::epsilon() ) { TernaryPoint tPunkt( x / total, y / total ); QPointF diagramLocation = translate( tPunkt ); QPointF widgetLocation = plane->translate( diagramLocation ); if ( row > 0 ) { p->drawLine( start, widgetLocation ); } paintMarker( p, model()->index( row, column, rootIndex() ), widgetLocation ); // checked start = widgetLocation; // retrieve text and data value attributes // FIXME use data model DisplayRole text QString text = tr( "(%1, %2, %3)", "(x, y, z) values of the data point" ) .arg( x * 100, 0, 'f', 0 ) .arg( y * 100, 0, 'f', 0 ) .arg( z * 100, 0, 'f', 0 ); d->paintDataValueText( p, attrs, widgetLocation, true, text, true ); } else { // ignore and do not paint this point, garbage data qDebug() << "TernaryPointDiagram::paint: data point x/y/z:" << x << "/" << y << "/" << z << "ignored, unusable."; } } } } } const QPair< QPointF, QPointF > TernaryLineDiagram::calculateDataBoundaries () const { // this is a constant, because we defined it to be one: static QPair Boundaries( TriangleBottomLeft, QPointF( TriangleBottomRight.x(), TriangleHeight ) ); return Boundaries; } diff --git a/src/KChart/Ternary/KChartTernaryLineDiagram.h b/src/KChart/Ternary/KChartTernaryLineDiagram.h index 743ba02..d85c750 100644 --- a/src/KChart/Ternary/KChartTernaryLineDiagram.h +++ b/src/KChart/Ternary/KChartTernaryLineDiagram.h @@ -1,51 +1,51 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTTERNARYLINEDIAGRAM_H #define KCHARTTERNARYLINEDIAGRAM_H #include "KChartTernaryCoordinatePlane.h" #include "KChartAbstractTernaryDiagram.h" namespace KChart { /** * @brief A TernaryLineDiagram is a line diagram with a ternary coordinate plane */ class KCHART_EXPORT TernaryLineDiagram : public AbstractTernaryDiagram { Q_OBJECT Q_DISABLE_COPY( TernaryLineDiagram ) KCHART_DECLARE_DERIVED_DIAGRAM( TernaryLineDiagram, TernaryCoordinatePlane ) public: - explicit TernaryLineDiagram ( QWidget* parent = 0, TernaryCoordinatePlane* plane = 0 ); + explicit TernaryLineDiagram ( QWidget* parent = nullptr, TernaryCoordinatePlane* plane = nullptr ); virtual ~TernaryLineDiagram(); void resize (const QSizeF &area) Q_DECL_OVERRIDE; void paint (PaintContext *paintContext) Q_DECL_OVERRIDE; protected: const QPair< QPointF, QPointF > calculateDataBoundaries () const Q_DECL_OVERRIDE; }; } #endif diff --git a/src/KChart/Ternary/KChartTernaryPointDiagram.cpp b/src/KChart/Ternary/KChartTernaryPointDiagram.cpp index 8bfb916..56ae082 100644 --- a/src/KChart/Ternary/KChartTernaryPointDiagram.cpp +++ b/src/KChart/Ternary/KChartTernaryPointDiagram.cpp @@ -1,139 +1,139 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "KChartTernaryPointDiagram.h" #include "KChartTernaryPointDiagram_p.h" #include #include #include #include "TernaryPoint.h" #include "TernaryConstants.h" using namespace KChart; #define d d_func() TernaryPointDiagram::Private::Private() : AbstractTernaryDiagram::Private() { } TernaryPointDiagram::TernaryPointDiagram ( QWidget* parent, TernaryCoordinatePlane* plane ) : AbstractTernaryDiagram( new Private(), parent, plane ) { init(); setDatasetDimensionInternal( 3 ); // the third column is implicit } TernaryPointDiagram::~TernaryPointDiagram() { } void TernaryPointDiagram::init() { d->reverseMapper.setDiagram( this ); } void TernaryPointDiagram::resize (const QSizeF& area) { AbstractTernaryDiagram::resize( area ); } void TernaryPointDiagram::paint (PaintContext *paintContext) { d->reverseMapper.clear(); d->paint( paintContext ); // sanity checks: - if ( model() == 0 ) return; + if ( model() == nullptr ) return; QPainter* p = paintContext->painter(); PainterSaver s( p ); TernaryCoordinatePlane* plane = static_cast< TernaryCoordinatePlane* >( paintContext->coordinatePlane() ); Q_ASSERT( plane ); qreal x, y, z; // for some reason(?) TernaryPointDiagram is using per-diagram DVAs only: const DataValueAttributes attrs( dataValueAttributes() ); d->forgetAlreadyPaintedDataValues(); int columnCount = model()->columnCount( rootIndex() ); for (int column=0; columnrowCount( rootIndex() ); for ( int row = 0; row < numrows; row++ ) { QModelIndex base = model()->index( row, column, rootIndex() ); // checked // see if there is data otherwise skip if ( ! model()->data( base ).isNull() ) { p->setPen( PrintingParameters::scalePen( pen( base ) ) ); p->setBrush( brush( base ) ); // retrieve data x = qMax( model()->data( model()->index( row, column+0, rootIndex() ) ).toReal(), // checked 0.0 ); y = qMax( model()->data( model()->index( row, column+1, rootIndex() ) ).toReal(), // checked 0.0 ); z = qMax( model()->data( model()->index( row, column+2, rootIndex() ) ).toReal(), // checked 0.0 ); // fix messed up data values (paint as much as possible) qreal total = x + y + z; if ( fabs( total ) > 3 * std::numeric_limits::epsilon() ) { TernaryPoint tPunkt( x / total, y / total ); QPointF diagramLocation = translate( tPunkt ); QPointF widgetLocation = plane->translate( diagramLocation ); paintMarker( p, model()->index( row, column, rootIndex() ), widgetLocation ); // checked QString text = tr( "(%1, %2, %3)", "(x, y, z) values of the data point" ) .arg( x * 100, 0, 'f', 0 ) .arg( y * 100, 0, 'f', 0 ) .arg( z * 100, 0, 'f', 0 ); d->paintDataValueText( p, attrs, widgetLocation, true, text, true ); } else { // ignore and do not paint this point, garbage data qDebug() << "TernaryPointDiagram::paint: data point x/y/z:" << x << "/" << y << "/" << z << "ignored, unusable."; } } } } } const QPair< QPointF, QPointF > TernaryPointDiagram::calculateDataBoundaries () const { // this is a constant, because we defined it to be one: static QPair Boundaries( TriangleBottomLeft, QPointF( TriangleBottomRight.x(), TriangleHeight ) ); return Boundaries; } diff --git a/src/KChart/Ternary/KChartTernaryPointDiagram.h b/src/KChart/Ternary/KChartTernaryPointDiagram.h index 9aceea7..01cbc9e 100644 --- a/src/KChart/Ternary/KChartTernaryPointDiagram.h +++ b/src/KChart/Ternary/KChartTernaryPointDiagram.h @@ -1,50 +1,50 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KCHARTTERNARYPOINTDIAGRAM_H #define KCHARTTERNARYPOINTDIAGRAM_H #include "KChartTernaryCoordinatePlane.h" #include "KChartAbstractTernaryDiagram.h" namespace KChart { /** * @brief A TernaryPointDiagram is a point diagram within a ternary coordinate plane */ class KCHART_EXPORT TernaryPointDiagram : public AbstractTernaryDiagram { Q_OBJECT Q_DISABLE_COPY( TernaryPointDiagram ) KCHART_DECLARE_DERIVED_DIAGRAM( TernaryPointDiagram, TernaryCoordinatePlane ) public: - explicit TernaryPointDiagram ( QWidget* parent = 0, TernaryCoordinatePlane* plane = 0 ); + explicit TernaryPointDiagram ( QWidget* parent = nullptr, TernaryCoordinatePlane* plane = nullptr ); virtual ~TernaryPointDiagram(); void resize (const QSizeF &area) Q_DECL_OVERRIDE; void paint (PaintContext *paintContext) Q_DECL_OVERRIDE; protected: const QPair< QPointF, QPointF > calculateDataBoundaries () const Q_DECL_OVERRIDE; }; } #endif diff --git a/src/KGantt/kganttabstractgrid.h b/src/KGantt/kganttabstractgrid.h index a65d36a..9bd2abb 100644 --- a/src/KGantt/kganttabstractgrid.h +++ b/src/KGantt/kganttabstractgrid.h @@ -1,73 +1,73 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KGANTTABSTRACTGRID_H #define KGANTTABSTRACTGRID_H #include "kganttglobal.h" #include "kganttconstraint.h" QT_BEGIN_NAMESPACE class QPainter; class QRectF; class QAbstractItemModel; class QModelIndex; QT_END_NAMESPACE namespace KGantt { class AbstractRowController; class Span; class KGANTT_EXPORT AbstractGrid : public QObject { Q_OBJECT KGANTT_DECLARE_PRIVATE_BASE_POLYMORPHIC( AbstractGrid ) friend class GraphicsScene; public: - AbstractGrid(QObject* parent = 0); + AbstractGrid(QObject* parent = nullptr); virtual ~AbstractGrid(); QAbstractItemModel* model() const; QModelIndex rootIndex() const; virtual Span mapToChart( const QModelIndex& idx ) const = 0; virtual bool mapFromChart( const Span& span, const QModelIndex& idx, const QList& constraints=QList() ) const = 0; virtual qreal mapToChart( const QVariant &value ) const; virtual QVariant mapFromChart( qreal x ) const; bool isSatisfiedConstraint( const Constraint& c ) const; virtual void paintGrid( QPainter* painter, const QRectF& sceneRect, const QRectF& exposedRect, - AbstractRowController* rowController = 0, QWidget* widget=0 ) = 0; + AbstractRowController* rowController = nullptr, QWidget* widget = nullptr ) = 0; virtual void paintHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, - qreal offset, QWidget* widget=0 ) = 0; + qreal offset, QWidget* widget = nullptr ) = 0; public Q_SLOTS: /*internal*/ virtual void setModel( QAbstractItemModel* model ); /*internal*/ virtual void setRootIndex( const QModelIndex& idx ); Q_SIGNALS: void gridChanged(); protected: virtual void drawBackground(QPainter* paint, const QRectF& rect); virtual void drawForeground(QPainter* paint, const QRectF& rect); }; } #endif /* KGANTTABSTRACTGRID_H */ diff --git a/src/KGantt/kganttconstraintgraphicsitem.h b/src/KGantt/kganttconstraintgraphicsitem.h index 31fb97a..67cf266 100644 --- a/src/KGantt/kganttconstraintgraphicsitem.h +++ b/src/KGantt/kganttconstraintgraphicsitem.h @@ -1,64 +1,64 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KGANTTCONSTRAINTGRAPHICSITEM_H #define KGANTTCONSTRAINTGRAPHICSITEM_H #include #include "kganttconstraint.h" namespace KGantt { class GraphicsScene; class KGANTT_EXPORT ConstraintGraphicsItem : public QGraphicsItem { public: enum { Type = UserType + 43 }; explicit ConstraintGraphicsItem( const Constraint& c, - QGraphicsItem* parent = 0, GraphicsScene* scene = 0 ); + QGraphicsItem* parent = nullptr, GraphicsScene* scene = nullptr ); virtual ~ConstraintGraphicsItem(); /*reimp*/ int type() const Q_DECL_OVERRIDE; /*reimp (non virtual)*/GraphicsScene* scene() const; /*reimp*/ QString ganttToolTip() const; /*reimp*/ QRectF boundingRect() const Q_DECL_OVERRIDE; /*reimp*/ void paint( QPainter* painter, const QStyleOptionGraphicsItem* option, - QWidget* widget = 0 ) Q_DECL_OVERRIDE; + QWidget* widget = nullptr ) Q_DECL_OVERRIDE; inline const Constraint& constraint() const { return m_constraint; } Constraint proxyConstraint() const; void setStart( const QPointF& start ); inline QPointF start() const { return m_start; } void setEnd( const QPointF& end ); inline QPointF end() const { return m_end; } void updateItem( const QPointF& start,const QPointF& end ); private: Constraint m_constraint; QPointF m_start; QPointF m_end; }; } #endif /* KGANTTCONSTRAINTGRAPHICSITEM_H */ diff --git a/src/KGantt/kganttconstraintmodel.h b/src/KGantt/kganttconstraintmodel.h index 75c8368..5f2d31f 100644 --- a/src/KGantt/kganttconstraintmodel.h +++ b/src/KGantt/kganttconstraintmodel.h @@ -1,82 +1,82 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KGANTTCONSTRAINTMODEL_H #define KGANTTCONSTRAINTMODEL_H #include #include #include "kganttglobal.h" #include "kganttconstraint.h" namespace KGantt { class KGANTT_EXPORT ConstraintModel : public QObject { Q_OBJECT KGANTT_DECLARE_PRIVATE_DERIVED_PARENT( ConstraintModel, QObject* ) public: - explicit ConstraintModel( QObject* parent=0 ); + explicit ConstraintModel( QObject* parent = nullptr ); virtual ~ConstraintModel(); /** * Subclassing ConstraintModel and overriding addConstraint() and removeConstraint() can provide * re-entrancy issues in the ConstraintModel<->ConstraintProxy interaction. Therefore it is recommed * to better subclass GraphicsView and override addConstraint() there. */ virtual void addConstraint( const Constraint& c ); virtual bool removeConstraint( const Constraint& c ); void clear(); void cleanup(); QList constraints() const; bool hasConstraint( const Constraint& c ) const; inline bool hasConstraint( const QModelIndex& s, const QModelIndex& e ) const; QList constraintsForIndex( const QModelIndex& ) const; Q_SIGNALS: void constraintAdded(const KGantt::Constraint&); void constraintRemoved(const KGantt::Constraint&); private: Private* _d; }; inline const ConstraintModel::Private* ConstraintModel::d_func() const { return _d; } inline ConstraintModel::Private* ConstraintModel::d_func() { return _d; } inline bool ConstraintModel::hasConstraint( const QModelIndex& s, const QModelIndex& e ) const { return hasConstraint( Constraint( s, e ) ); } } #ifndef QT_NO_DEBUG_STREAM QDebug KGANTT_EXPORT operator<<( QDebug dbg, const KGantt::ConstraintModel& model ); inline QDebug operator<<( QDebug dbg, KGantt::ConstraintModel* model ) { return operator<<(dbg,*model); } #endif /* QT_NO_DEBUG_STREAM */ #endif /* KGANTTCONSTRAINTMODEL_H */ diff --git a/src/KGantt/kganttconstraintproxy.h b/src/KGantt/kganttconstraintproxy.h index 245869c..e9e5e5b 100644 --- a/src/KGantt/kganttconstraintproxy.h +++ b/src/KGantt/kganttconstraintproxy.h @@ -1,70 +1,70 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KGANTTCONSTRAINTPROXY_H #define KGANTTCONSTRAINTPROXY_H #include "kganttglobal.h" #include QT_BEGIN_NAMESPACE class QAbstractProxyModel; QT_END_NAMESPACE namespace KGantt { class Constraint; class ConstraintModel; class KGANTT_EXPORT ConstraintProxy : public QObject { Q_OBJECT public: - explicit ConstraintProxy( QObject* parent = 0 ); + explicit ConstraintProxy( QObject* parent = nullptr ); virtual ~ConstraintProxy(); void setSourceModel( ConstraintModel* src ); void setDestinationModel( ConstraintModel* dest ); void setProxyModel( QAbstractProxyModel* proxy ); ConstraintModel* sourceModel() const; ConstraintModel* destinationModel() const; QAbstractProxyModel* proxyModel() const; private Q_SLOTS: void slotSourceConstraintAdded( const KGantt::Constraint& ); void slotSourceConstraintRemoved( const KGantt::Constraint& ); void slotDestinationConstraintAdded( const KGantt::Constraint& ); void slotDestinationConstraintRemoved( const KGantt::Constraint& ); void slotLayoutChanged(); private: void copyFromSource(); QPointer m_proxy; QPointer m_source; QPointer m_destination; }; } #endif /* KGANTTCONSTRAINTPROXY_H */ diff --git a/src/KGantt/kganttdatetimegrid.h b/src/KGantt/kganttdatetimegrid.h index df832fd..c28876c 100644 --- a/src/KGantt/kganttdatetimegrid.h +++ b/src/KGantt/kganttdatetimegrid.h @@ -1,165 +1,165 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KGANTTDATETIMEGRID_H #define KGANTTDATETIMEGRID_H #include "kganttabstractgrid.h" #include #include namespace KGantt { class DateTimeScaleFormatter; class KGANTT_EXPORT DateTimeGrid : public AbstractGrid { Q_OBJECT KGANTT_DECLARE_PRIVATE_DERIVED( DateTimeGrid ) public: enum Scale { ScaleAuto, ScaleHour, ScaleDay, ScaleWeek, ScaleMonth, ScaleUserDefined }; DateTimeGrid(); virtual ~DateTimeGrid(); QDateTime startDateTime() const; void setStartDateTime( const QDateTime& dt ); qreal dayWidth() const; void setDayWidth( qreal ); qreal mapFromDateTime( const QDateTime& dt) const; QDateTime mapToDateTime( qreal x ) const; void setWeekStart( Qt::DayOfWeek ); Qt::DayOfWeek weekStart() const; void setFreeDays( const QSet& fd ); QSet freeDays() const; void setFreeDaysBrush(const QBrush brush); QBrush freeDaysBrush() const; void setScale( Scale s ); Scale scale() const; void setUserDefinedLowerScale( DateTimeScaleFormatter* lower ); void setUserDefinedUpperScale( DateTimeScaleFormatter* upper ); DateTimeScaleFormatter* userDefinedLowerScale() const; DateTimeScaleFormatter* userDefinedUpperScale() const; bool rowSeparators() const; void setRowSeparators( bool enable ); void setNoInformationBrush( const QBrush& brush ); QBrush noInformationBrush() const; /*reimp*/ Span mapToChart( const QModelIndex& idx ) const Q_DECL_OVERRIDE; /*reimp*/ bool mapFromChart( const Span& span, const QModelIndex& idx, const QList& constraints=QList() ) const Q_DECL_OVERRIDE; /*reimp*/ qreal mapToChart( const QVariant& value ) const Q_DECL_OVERRIDE; /*reimp*/ QVariant mapFromChart( qreal x ) const Q_DECL_OVERRIDE; /*reimp*/ void paintGrid( QPainter* painter, const QRectF& sceneRect, const QRectF& exposedRect, - AbstractRowController* rowController = 0, - QWidget* widget=0 ) Q_DECL_OVERRIDE; + AbstractRowController* rowController = nullptr, + QWidget* widget = nullptr ) Q_DECL_OVERRIDE; /*reimp*/ void paintHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, - qreal offset, QWidget* widget=0 ) Q_DECL_OVERRIDE; + qreal offset, QWidget* widget = nullptr ) Q_DECL_OVERRIDE; protected: virtual void paintHourScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, - qreal offset, QWidget* widget=0 ); + qreal offset, QWidget* widget = nullptr ); virtual void paintDayScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, - qreal offset, QWidget* widget=0 ); + qreal offset, QWidget* widget = nullptr ); virtual void paintWeekScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, - qreal offset, QWidget* widget=0 ); + qreal offset, QWidget* widget = nullptr ); virtual void paintMonthScaleHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, - qreal offset, QWidget* widget=0 ); + qreal offset, QWidget* widget = nullptr ); virtual void paintUserDefinedHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, const DateTimeScaleFormatter* formatter, - QWidget* widget = 0 ); + QWidget* widget = nullptr ); virtual void drawDayBackground(QPainter* painter, const QRectF& rect, const QDate& date); virtual void drawDayForeground(QPainter* painter, const QRectF& rect, const QDate& date); QRectF computeRect(const QDateTime& from, const QDateTime& to, const QRectF& rect) const; QPair dateTimeRange(const QRectF& rect) const; /* reimp */ void drawBackground(QPainter* paint, const QRectF& rect) Q_DECL_OVERRIDE; /* reimp */ void drawForeground(QPainter* paint, const QRectF& rect) Q_DECL_OVERRIDE; }; class KGANTT_EXPORT DateTimeScaleFormatter { KGANTT_DECLARE_PRIVATE_BASE_POLYMORPHIC( DateTimeScaleFormatter ) public: enum Range { Second, Minute, Hour, Day, Week, Month, Year }; DateTimeScaleFormatter( Range range, const QString& formatString, Qt::Alignment alignment = Qt::AlignCenter ); DateTimeScaleFormatter( Range range, const QString& formatString, const QString& templ, Qt::Alignment alignment = Qt::AlignCenter ); DateTimeScaleFormatter( const DateTimeScaleFormatter& other ); virtual ~DateTimeScaleFormatter(); DateTimeScaleFormatter& operator=( const DateTimeScaleFormatter& other ); QString format() const; Range range() const; Qt::Alignment alignment() const; virtual QDateTime nextRangeBegin( const QDateTime& datetime ) const; virtual QDateTime currentRangeBegin( const QDateTime& datetime ) const; QString format( const QDateTime& datetime ) const; virtual QString text( const QDateTime& datetime ) const; }; } #ifndef QT_NO_DEBUG_STREAM QDebug KGANTT_EXPORT operator<<( QDebug dbg, KGantt::DateTimeScaleFormatter::Range ); #endif #endif /* KGANTTDATETIMEGRID_H */ diff --git a/src/KGantt/kganttdatetimegrid_p.h b/src/KGantt/kganttdatetimegrid_p.h index 604fb5a..300f282 100644 --- a/src/KGantt/kganttdatetimegrid_p.h +++ b/src/KGantt/kganttdatetimegrid_p.h @@ -1,166 +1,166 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KGANTTDATETIMEGRID_P_H #define KGANTTDATETIMEGRID_P_H #include "kganttdatetimegrid.h" #include "kganttabstractgrid_p.h" #include #include namespace KGantt { class Q_DECL_HIDDEN DateTimeScaleFormatter::Private { public: Private( DateTimeScaleFormatter::Range _range, const QString& _format, const QString& _templ, Qt::Alignment _alignment ) : range( _range ), format( _format ), templ( _templ ), alignment( _alignment ) { } const DateTimeScaleFormatter::Range range; const QString format; const QString templ; const Qt::Alignment alignment; }; class Q_DECL_HIDDEN DateTimeGrid::Private : public AbstractGrid::Private { public: Private() : startDateTime( QDateTime::currentDateTime().addDays( -3 ) ), dayWidth( 100. ), scale( ScaleAuto ), weekStart( Qt::Monday ), freeDays( QSet() << Qt::Saturday << Qt::Sunday ), rowSeparators( false ), noInformationBrush( Qt::red, Qt::DiagCrossPattern ), freeDaysBrush(QBrush()), upper( new DateTimeScaleFormatter( DateTimeScaleFormatter::Week, QString::fromLatin1( "w" ) ) ), lower( new DateTimeScaleFormatter( DateTimeScaleFormatter::Day, QString::fromLatin1( "ddd" ) ) ), year_upper( DateTimeScaleFormatter::Year, QString::fromLatin1("yyyy" ) ), year_lower( DateTimeScaleFormatter::Month, QString::fromLatin1("MMM" ) ), month_upper( DateTimeScaleFormatter::Month, QString::fromLatin1("MMMM" ) ), month_lower( DateTimeScaleFormatter::Week, QString::fromLatin1("w" ) ), week_upper( DateTimeScaleFormatter::Week, QString::fromLatin1("w" ) ), week_lower( DateTimeScaleFormatter::Day, QString::fromLatin1("ddd" ) ), day_upper( DateTimeScaleFormatter::Day, QString::fromLatin1("dddd" ) ), day_lower( DateTimeScaleFormatter::Hour, QString::fromLatin1("hh" ) ), hour_upper( DateTimeScaleFormatter::Hour, QString::fromLatin1("hh" ) ), hour_lower( DateTimeScaleFormatter::Minute, QString::fromLatin1("m" ) ), minute_upper( DateTimeScaleFormatter::Minute, QString::fromLatin1("m" ) ), minute_lower( DateTimeScaleFormatter::Second, QString::fromLatin1("s" ) ) { } ~Private() { delete lower; delete upper; } qreal dateTimeToChartX( const QDateTime& dt ) const; QDateTime chartXtoDateTime( qreal x ) const; - int tabHeight( const QString& txt, QWidget* widget = 0 ) const; + int tabHeight( const QString& txt, QWidget* widget = nullptr ) const; void getAutomaticFormatters( DateTimeScaleFormatter** lower, DateTimeScaleFormatter** upper); class DateTextFormatter { public: virtual ~DateTextFormatter() {} virtual QString format( const QDateTime& dt ) = 0; virtual QRect textRect( qreal x, qreal offset, qreal dayWidth, const QRectF& headerRect, const QDateTime& dt ) = 0; }; /*! * We need this because we have a header type for a year, but no such scale. */ enum HeaderType { HeaderHour, HeaderDay, HeaderWeek, HeaderMonth, HeaderYear }; HeaderType headerTypeForScale( DateTimeGrid::Scale scale ); void paintHeader( QPainter* painter, const QRectF& headerRect, const QRectF& exposedRect, qreal offset, QWidget* widget, HeaderType headerType, DateTextFormatter *formatter ); void paintVerticalLines( QPainter* painter, const QRectF& sceneRect, const QRectF& exposedRect, QWidget* widget, HeaderType headerType ); void paintVerticalUserDefinedLines( QPainter* painter, const QRectF& sceneRect, const QRectF& exposedRect, const DateTimeScaleFormatter* formatter, QWidget* widget ); Qt::PenStyle gridLinePenStyle( QDateTime dt, HeaderType headerType ) const; QDateTime adjustDateTimeForHeader( QDateTime dt, HeaderType headerType ) const; QDateTime startDateTime; QDateTime endDateTime; qreal dayWidth; Scale scale; Qt::DayOfWeek weekStart; QSet freeDays; bool rowSeparators; QBrush noInformationBrush; QBrush freeDaysBrush; DateTimeScaleFormatter* upper; DateTimeScaleFormatter* lower; DateTimeScaleFormatter year_upper; DateTimeScaleFormatter year_lower; DateTimeScaleFormatter month_upper; DateTimeScaleFormatter month_lower; DateTimeScaleFormatter week_upper; DateTimeScaleFormatter week_lower; DateTimeScaleFormatter day_upper; DateTimeScaleFormatter day_lower; DateTimeScaleFormatter hour_upper; DateTimeScaleFormatter hour_lower; DateTimeScaleFormatter minute_upper; DateTimeScaleFormatter minute_lower; }; inline DateTimeGrid::DateTimeGrid( DateTimeGrid::Private* d ) : AbstractGrid( d ) {} inline DateTimeGrid::Private* DateTimeGrid::d_func() { return static_cast( AbstractGrid::d_func() ); } inline const DateTimeGrid::Private* DateTimeGrid::d_func() const { return static_cast( AbstractGrid::d_func() ); } } #endif /* KGANTTDATETIMEGRID_P_H */ diff --git a/src/KGantt/kganttforwardingproxymodel.h b/src/KGantt/kganttforwardingproxymodel.h index 4a9a0af..d280921 100644 --- a/src/KGantt/kganttforwardingproxymodel.h +++ b/src/KGantt/kganttforwardingproxymodel.h @@ -1,71 +1,71 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KGANTTFORWARDINGPROXYMODEL_H #define KGANTTFORWARDINGPROXYMODEL_H #include #include "kganttglobal.h" namespace KGantt { class KGANTT_EXPORT ForwardingProxyModel : public QAbstractProxyModel { Q_OBJECT Q_DISABLE_COPY(ForwardingProxyModel) public: - explicit ForwardingProxyModel( QObject* parent=0 ); + explicit ForwardingProxyModel( QObject* parent = nullptr ); virtual ~ForwardingProxyModel(); /*reimp*/ QModelIndex mapFromSource ( const QModelIndex & sourceIndex ) const Q_DECL_OVERRIDE; /*reimp*/ QModelIndex mapToSource ( const QModelIndex & proxyIndex ) const Q_DECL_OVERRIDE; /*reimp*/ void setSourceModel( QAbstractItemModel* model ) Q_DECL_OVERRIDE; /*reimp*/ QModelIndex index( int row, int column, const QModelIndex& parent = QModelIndex() ) const Q_DECL_OVERRIDE; /*reimp*/ QModelIndex parent( const QModelIndex& idx ) const Q_DECL_OVERRIDE; /*reimp*/ int rowCount( const QModelIndex& idx = QModelIndex() ) const Q_DECL_OVERRIDE; /*reimp*/ int columnCount( const QModelIndex& idx = QModelIndex() ) const Q_DECL_OVERRIDE; /*reimp*/ bool setData( const QModelIndex& index, const QVariant& value, int role = Qt::EditRole ) Q_DECL_OVERRIDE; /*reimp*/ QMimeData *mimeData(const QModelIndexList &indexes) const Q_DECL_OVERRIDE; /*reimp*/ bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) Q_DECL_OVERRIDE; /*reimp*/ QStringList mimeTypes() const Q_DECL_OVERRIDE; /*reimp*/ Qt::DropActions supportedDropActions() const Q_DECL_OVERRIDE; protected Q_SLOTS: virtual void sourceModelAboutToBeReset(); virtual void sourceModelReset(); virtual void sourceLayoutAboutToBeChanged(); virtual void sourceLayoutChanged(); virtual void sourceDataChanged( const QModelIndex& from, const QModelIndex& to ); virtual void sourceColumnsAboutToBeInserted( const QModelIndex& idx, int start, int end ); virtual void sourceColumnsInserted( const QModelIndex& idx, int start, int end ); virtual void sourceColumnsAboutToBeRemoved( const QModelIndex& idx, int start, int end ); virtual void sourceColumnsRemoved( const QModelIndex& idx, int start, int end ); virtual void sourceRowsAboutToBeInserted( const QModelIndex& idx, int start, int end ); virtual void sourceRowsInserted( const QModelIndex& idx, int start, int end ); virtual void sourceRowsAboutToBeRemoved( const QModelIndex&, int start, int end ); virtual void sourceRowsRemoved( const QModelIndex&, int start, int end ); }; } #endif /* KGANTTFORWARDINGPROXYMODEL_H */ diff --git a/src/KGantt/kganttgraphicsitem.cpp b/src/KGantt/kganttgraphicsitem.cpp index c1484f5..a317422 100644 --- a/src/KGantt/kganttgraphicsitem.cpp +++ b/src/KGantt/kganttgraphicsitem.cpp @@ -1,580 +1,580 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "kganttgraphicsitem.h" #include "kganttgraphicsscene.h" #include "kganttgraphicsview.h" #include "kganttitemdelegate.h" #include "kganttconstraintgraphicsitem.h" #include "kganttconstraintmodel.h" #include "kganttconstraint.h" #include "kganttabstractgrid.h" #include "kganttabstractrowcontroller.h" #include #include #include #include #include #include #include #include #include #include #include /*!\class KGantt::GraphicsItem * \internal */ using namespace KGantt; typedef QGraphicsItem BASE; namespace { class Updater { bool *u_ptr; bool oldval; public: Updater( bool* u ) : u_ptr( u ), oldval( *u ) { *u=true; } ~Updater() { *u_ptr = oldval; } }; } GraphicsItem::GraphicsItem( QGraphicsItem* parent, GraphicsScene* scene ) : BASE( parent ), m_isupdating( false ) { if ( scene ) scene->addItem( this ); init(); } GraphicsItem::GraphicsItem( const QModelIndex& idx, QGraphicsItem* parent, GraphicsScene* scene ) : BASE( parent ), m_index( idx ), m_isupdating( false ) { init(); if ( scene ) scene->addItem( this ); } GraphicsItem::~GraphicsItem() { } void GraphicsItem::init() { setCacheMode( QGraphicsItem::DeviceCoordinateCache ); setFlags( ItemIsMovable|ItemIsSelectable|ItemIsFocusable ); setAcceptHoverEvents( true ); setHandlesChildEvents( true ); setZValue( 100. ); - m_dragline = 0; + m_dragline = nullptr; } int GraphicsItem::type() const { return Type; } StyleOptionGanttItem GraphicsItem::getStyleOption() const { StyleOptionGanttItem opt; if (!m_index.isValid()) { // TODO: find out why we get invalid indexes //qDebug()<<"GraphicsItem::getStyleOption: Invalid index"; return opt; } opt.itemRect = rect(); opt.boundingRect = boundingRect(); QVariant tp = m_index.model()->data( m_index, TextPositionRole ); if (tp.isValid()) { opt.displayPosition = static_cast(tp.toInt()); } else { #if 0 qDebug() << "Item" << m_index.model()->data( m_index, Qt::DisplayRole ).toString() << ", ends="<data( m_index, Qt::TextAlignmentRole ); if ( da.isValid() ) { opt.displayAlignment = static_cast< Qt::Alignment >( da.toInt() ); } else { switch ( opt.displayPosition ) { case StyleOptionGanttItem::Left: opt.displayAlignment = Qt::AlignLeft|Qt::AlignVCenter; break; case StyleOptionGanttItem::Right: opt.displayAlignment = Qt::AlignRight|Qt::AlignVCenter; break; case StyleOptionGanttItem::Hidden: // fall through case StyleOptionGanttItem::Center: opt.displayAlignment = Qt::AlignCenter; break; } } opt.grid = scene()->grid(); opt.text = m_index.model()->data( m_index, Qt::DisplayRole ).toString(); if ( isEnabled() ) opt.state |= QStyle::State_Enabled; if ( isSelected() ) opt.state |= QStyle::State_Selected; if ( hasFocus() ) opt.state |= QStyle::State_HasFocus; return opt; } GraphicsScene* GraphicsItem::scene() const { return qobject_cast( QGraphicsItem::scene() ); } void GraphicsItem::setRect( const QRectF& r ) { #if 0 qDebug() << "GraphicsItem::setRect("<isReadOnly() && m_index.isValid() && m_index.model()->flags( m_index ) & Qt::ItemIsEditable; } void GraphicsItem::paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget ) { Q_UNUSED( widget ); if ( boundingRect().isValid() && scene() ) { StyleOptionGanttItem opt = getStyleOption(); *static_cast(&opt) = *static_cast( option ); //opt.fontMetrics = painter->fontMetrics(); scene()->itemDelegate()->paintGanttItem( painter, opt, index() ); } } void GraphicsItem::setIndex( const QPersistentModelIndex& idx ) { m_index=idx; update(); } QString GraphicsItem::ganttToolTip() const { return scene()->itemDelegate()->toolTip( index() ); } QRectF GraphicsItem::boundingRect() const { return m_boundingrect; } QPointF GraphicsItem::startConnector( int relationType ) const { switch ( relationType ) { case Constraint::StartStart: case Constraint::StartFinish: return mapToScene( m_rect.left(), m_rect.top()+m_rect.height()/2. ); default: break; } return mapToScene( m_rect.right(), m_rect.top()+m_rect.height()/2. ); } QPointF GraphicsItem::endConnector( int relationType ) const { switch ( relationType ) { case Constraint::FinishFinish: case Constraint::StartFinish: return mapToScene( m_rect.right(), m_rect.top()+m_rect.height()/2. ); default: break; } return mapToScene( m_rect.left(), m_rect.top()+m_rect.height()/2. ); } void GraphicsItem::constraintsChanged() { if ( !scene() || !scene()->itemDelegate() ) return; const Span bs = scene()->itemDelegate()->itemBoundingSpan( getStyleOption(), index() ); const QRectF br = boundingRect(); setBoundingRect( QRectF( bs.start(), 0., bs.length(), br.height() ) ); } void GraphicsItem::addStartConstraint( ConstraintGraphicsItem* item ) { assert( item ); m_startConstraints << item; item->setStart( startConnector( item->constraint().relationType() ) ); constraintsChanged(); } void GraphicsItem::addEndConstraint( ConstraintGraphicsItem* item ) { assert( item ); m_endConstraints << item; item->setEnd( endConnector( item->constraint().relationType() ) ); constraintsChanged(); } void GraphicsItem::removeStartConstraint( ConstraintGraphicsItem* item ) { assert( item ); m_startConstraints.removeAll( item ); constraintsChanged(); } void GraphicsItem::removeEndConstraint( ConstraintGraphicsItem* item ) { assert( item ); m_endConstraints.removeAll( item ); constraintsChanged(); } void GraphicsItem::updateConstraintItems() { { // Workaround for multiple definition error with MSVC6 Q_FOREACH( ConstraintGraphicsItem* item, m_startConstraints ) { QPointF s = startConnector( item->constraint().relationType() ); item->setStart( s ); }} {// Workaround for multiple definition error with MSVC6 Q_FOREACH( ConstraintGraphicsItem* item, m_endConstraints ) { QPointF e = endConnector( item->constraint().relationType() ); item->setEnd( e ); }} } void GraphicsItem::updateItem( const Span& rowGeometry, const QPersistentModelIndex& idx ) { //qDebug() << "GraphicsItem::updateItem("<grid()->mapToChart( static_cast(idx) ); setPos( QPointF( s.start(), rowGeometry.start() ) ); setRect( QRectF( 0., 0., s.length(), rowGeometry.length() ) ); setIndex( idx ); const Span bs = scene()->itemDelegate()->itemBoundingSpan( getStyleOption(), index() ); //qDebug() << "boundingSpan for" << getStyleOption().text << rect() << "is" << bs; setBoundingRect( QRectF( bs.start(), 0., bs.length(), rowGeometry.length() ) ); const int maxh = scene()->rowController()->maximumItemHeight(); if ( maxh < rowGeometry.length() ) { QRectF r = rect(); const Qt::Alignment align = getStyleOption().displayAlignment; if ( align & Qt::AlignTop ) { // Do nothing } else if ( align & Qt::AlignBottom ) { r.setY( rowGeometry.length()-maxh ); } else { // Center r.setY( ( rowGeometry.length()-maxh ) / 2. ); } r.setHeight( maxh ); setRect( r ); } //scene()->setSceneRect( scene()->sceneRect().united( mapToScene( boundingRect() ).boundingRect() ) ); //updateConstraintItems(); } QVariant GraphicsItem::itemChange( GraphicsItemChange change, const QVariant& value ) { if ( !isUpdating() && change==ItemPositionChange && scene() ) { QPointF newPos=value.toPointF(); if ( isEditable() ) { newPos.setY( pos().y() ); return newPos; } else { return pos(); } } else if ( change==QGraphicsItem::ItemSelectedChange ) { if ( index().isValid() && !( index().model()->flags( index() ) & Qt::ItemIsSelectable ) ) { // Reject selection attempt return qVariantFromValue( false ); } if ( value.toBool() ) { scene()->selectionModel()->select( index(), QItemSelectionModel::Select ); } else { scene()->selectionModel()->select( index(), QItemSelectionModel::Deselect ); } } return QGraphicsItem::itemChange( change, value ); } void GraphicsItem::focusInEvent( QFocusEvent* event ) { Q_UNUSED( event ); scene()->selectionModel()->select( index(), QItemSelectionModel::SelectCurrent ); } void GraphicsItem::updateModel() { //qDebug() << "GraphicsItem::updateModel()"; if ( isEditable() ) { QAbstractItemModel* model = const_cast( index().model() ); #if !defined(NDEBUG) ConstraintModel* cmodel = scene()->constraintModel(); #endif assert( model ); assert( cmodel ); if ( model ) { //ItemType typ = static_cast( model->data( index(), // ItemTypeRole ).toInt() ); QList constraints; for ( QList::iterator it1 = m_startConstraints.begin() ; it1 != m_startConstraints.end() ; ++it1 ) constraints.push_back((*it1)->proxyConstraint()); for ( QList::iterator it2 = m_endConstraints.begin() ; it2 != m_endConstraints.end() ; ++it2 ) constraints.push_back((*it2)->proxyConstraint()); if ( scene()->grid()->mapFromChart( Span( scenePos().x(), rect().width() ), index(), constraints ) ) { scene()->updateRow( index().parent() ); } } } } void GraphicsItem::hoverMoveEvent( QGraphicsSceneHoverEvent* event ) { if ( !isEditable() ) return; StyleOptionGanttItem opt = getStyleOption(); ItemDelegate::InteractionState istate = scene()->itemDelegate()->interactionStateFor( event->pos(), opt, index() ); switch ( istate ) { case ItemDelegate::State_ExtendLeft: #ifndef QT_NO_CURSOR setCursor( Qt::SizeHorCursor ); #endif scene()->itemEntered( index() ); break; case ItemDelegate::State_ExtendRight: #ifndef QT_NO_CURSOR setCursor( Qt::SizeHorCursor ); #endif scene()->itemEntered( index() ); break; case ItemDelegate::State_Move: #ifndef QT_NO_CURSOR setCursor( Qt::SplitHCursor ); #endif scene()->itemEntered( index() ); break; default: #ifndef QT_NO_CURSOR unsetCursor(); #endif break; }; } void GraphicsItem::hoverLeaveEvent( QGraphicsSceneHoverEvent* ) { #ifndef QT_NO_CURSOR unsetCursor(); #endif } void GraphicsItem::mousePressEvent( QGraphicsSceneMouseEvent* event ) { //qDebug() << "GraphicsItem::mousePressEvent("<itemDelegate()->interactionStateFor( event->pos(), opt, index() ); // If State_None is returned by interactionStateFor(), we ignore this event so that // it can get forwarded to another item that's below this one. Needed, for example, // to allow items to be moved that are placed below the label of another item. if ( istate != ItemDelegate::State_None ) { m_istate = istate; m_presspos = event->pos(); m_pressscenepos = event->scenePos(); scene()->itemPressed( index() ); switch ( m_istate ) { case ItemDelegate::State_ExtendLeft: case ItemDelegate::State_ExtendRight: default: /* State_Move */ BASE::mousePressEvent( event ); break; } } else { event->ignore(); } } void GraphicsItem::mouseReleaseEvent( QGraphicsSceneMouseEvent* event ) { //qDebug() << "GraphicsItem::mouseReleaseEvent("<itemClicked( index() ); } - delete m_dragline; m_dragline = 0; + delete m_dragline; m_dragline = nullptr; if ( scene()->dragSource() ) { // Create a new constraint GraphicsItem* other = qgraphicsitem_cast( scene()->itemAt( event->scenePos(), QTransform() ) ); if ( other && scene()->dragSource()!=other && other->index().data(KGantt::ItemTypeRole) == KGantt::TypeEvent ) { // The code below fixes bug KDCH-696. // Modified the code to add constraint even if the user drags and drops // constraint on left part of the TypeEvent symbol(i.e diamond symbol) QRectF itemRect = other->rect().adjusted(-other->rect().height()/2.0, 0, 0, 0 ); if ( other->mapToScene( itemRect ).boundingRect().contains( event->scenePos() )) { GraphicsView* view = qobject_cast( event->widget()->parentWidget() ); if ( view ) { view->addConstraint( scene()->summaryHandlingModel()->mapToSource( scene()->dragSource()->index() ), scene()->summaryHandlingModel()->mapToSource( other->index() ), event->modifiers() ); } } } else { if ( other && scene()->dragSource()!=other && other->mapToScene( other->rect() ).boundingRect().contains( event->scenePos() )) { GraphicsView* view = qobject_cast( event->widget()->parentWidget() ); if ( view ) { view->addConstraint( scene()->summaryHandlingModel()->mapToSource( scene()->dragSource()->index() ), scene()->summaryHandlingModel()->mapToSource( other->index() ), event->modifiers() ); } } } - scene()->setDragSource( 0 ); + scene()->setDragSource( nullptr ); //scene()->update(); } else { if ( isEditable() ) { updateItemFromMouse(event->scenePos()); // It is important to set m_presspos to null here because // when the sceneRect updates because we move the item // a MouseMoveEvent will be delivered, and we have to // protect against that m_presspos = QPointF(); updateModel(); // without this command we sometimes get a white area at the left side of a task item // after we moved that item right-ways into a grey weekend section of the scene: scene()->update(); } } m_presspos = QPointF(); BASE::mouseReleaseEvent( event ); } void GraphicsItem::mouseDoubleClickEvent( QGraphicsSceneMouseEvent* event ) { const int typ = static_cast( index().model()->data( index(), ItemTypeRole ).toInt() ); StyleOptionGanttItem opt = getStyleOption(); ItemDelegate::InteractionState istate = scene()->itemDelegate()->interactionStateFor( event->pos(), opt, index() ); if ( (istate != ItemDelegate::State_None) || (typ == TypeSummary)) { scene()->itemDoubleClicked( index() ); } BASE::mouseDoubleClickEvent( event ); } void GraphicsItem::updateItemFromMouse( const QPointF& scenepos ) { //qDebug() << "GraphicsItem::updateItemFromMouse("< #include #include QT_BEGIN_NAMESPACE class QGraphicsLineItem; QT_END_NAMESPACE namespace KGantt { class GraphicsScene; class ConstraintGraphicsItem; /* Can we fit every kind of item into one gfxitem class? */ class GraphicsItem : public QGraphicsItem { public: enum { Type = UserType + 42 }; - explicit GraphicsItem( QGraphicsItem* parent = 0, GraphicsScene* scene = 0 ); - explicit GraphicsItem( const QModelIndex& idx, QGraphicsItem* parent = 0, GraphicsScene* scene = 0 ); + explicit GraphicsItem( QGraphicsItem* parent = nullptr, GraphicsScene* scene = nullptr ); + explicit GraphicsItem( const QModelIndex& idx, QGraphicsItem* parent = nullptr, GraphicsScene* scene = nullptr ); virtual ~GraphicsItem(); /*reimp*/int type() const Q_DECL_OVERRIDE; /*reimp (non-virtual)*/ GraphicsScene* scene() const; void updateItem( const Span& rowgeometry, const QPersistentModelIndex& idx ); //virtual ItemType itemType() const = 0; //qreal dateTimeToSceneX( const QDateTime& dt ) const; //QDateTime sceneXtoDateTime( qreal x ) const; QRectF rect() const { return m_rect; } void setRect( const QRectF& r ); void setBoundingRect( const QRectF& r ); virtual QString ganttToolTip() const; const QPersistentModelIndex& index() const { return m_index; } void setIndex( const QPersistentModelIndex& idx ); bool isEditable() const; bool isUpdating() const { return m_isupdating; } void addStartConstraint( ConstraintGraphicsItem* ); void addEndConstraint( ConstraintGraphicsItem* ); void removeStartConstraint( ConstraintGraphicsItem* ); void removeEndConstraint( ConstraintGraphicsItem* ); QList startConstraints() const { return m_startConstraints; } QList endConstraints() const { return m_endConstraints; } /*reimp*/ QRectF boundingRect() const Q_DECL_OVERRIDE; /*reimp*/ void paint( QPainter* painter, const QStyleOptionGraphicsItem* option, - QWidget* widget = 0 ) Q_DECL_OVERRIDE; + QWidget* widget = nullptr ) Q_DECL_OVERRIDE; /*reimp*/ QVariant itemChange( GraphicsItemChange, const QVariant& value ) Q_DECL_OVERRIDE; protected: /*reimp*/ void focusInEvent( QFocusEvent* event ) Q_DECL_OVERRIDE; /*reimp*/ void hoverMoveEvent( QGraphicsSceneHoverEvent* ) Q_DECL_OVERRIDE; /*reimp*/ void hoverLeaveEvent( QGraphicsSceneHoverEvent* ) Q_DECL_OVERRIDE; /*reimp*/ void mousePressEvent( QGraphicsSceneMouseEvent* ) Q_DECL_OVERRIDE; /*reimp*/ void mouseReleaseEvent( QGraphicsSceneMouseEvent* ) Q_DECL_OVERRIDE; /*reimp*/ void mouseDoubleClickEvent( QGraphicsSceneMouseEvent* ) Q_DECL_OVERRIDE; /*reimp*/ void mouseMoveEvent( QGraphicsSceneMouseEvent* ) Q_DECL_OVERRIDE; private: void init(); QPointF startConnector( int relationType ) const; QPointF endConnector( int relationType ) const; void updateConstraintItems(); StyleOptionGanttItem getStyleOption() const; void updateModel(); void updateItemFromMouse( const QPointF& scenepos ); void constraintsChanged(); QRectF m_rect; QRectF m_boundingrect; QPersistentModelIndex m_index; bool m_isupdating; int m_istate; QPointF m_presspos; QPointF m_pressscenepos; QGraphicsLineItem* m_dragline; GraphicsItem* m_dragtarget; QList m_startConstraints; QList m_endConstraints; }; } #endif /* KGANTTGRAPHICSITEM_H */ diff --git a/src/KGantt/kganttgraphicsscene.cpp b/src/KGantt/kganttgraphicsscene.cpp index f231422..83a5a53 100644 --- a/src/KGantt/kganttgraphicsscene.cpp +++ b/src/KGantt/kganttgraphicsscene.cpp @@ -1,954 +1,954 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "kganttgraphicsscene.h" #include "kganttgraphicsscene_p.h" #include "kganttgraphicsitem.h" #include "kganttconstraint.h" #include "kganttconstraintgraphicsitem.h" #include "kganttitemdelegate.h" #include "kganttabstractrowcontroller.h" #include "kganttdatetimegrid.h" #include "kganttsummaryhandlingproxymodel.h" #include #include #include #include #include #include #include #include #include #include #include // defines HAVE_PRINTER if support for printing should be included #ifdef _WIN32_WCE // There is no printer support under wince even if QT_NO_PRINTER is not set #else #ifndef QT_NO_PRINTER #define HAVE_PRINTER #endif #endif /*!\class KGantt::GraphicsScene * \internal */ using namespace KGantt; GraphicsScene::Private::Private( GraphicsScene* _q ) : q( _q ), - dragSource( 0 ), + dragSource( nullptr ), itemDelegate( new ItemDelegate( _q ) ), - rowController( 0 ), + rowController( nullptr ), grid( &default_grid ), readOnly( false ), isPrinting( false ), drawColumnLabels( true ), labelsWidth( 0.0 ), summaryHandlingModel( new SummaryHandlingProxyModel( _q ) ), - selectionModel( 0 ) + selectionModel( nullptr ) { default_grid.setStartDateTime( QDateTime::currentDateTime().addDays( -1 ) ); } void GraphicsScene::Private::clearConstraintItems() { for(ConstraintGraphicsItem *citem : constraintItems) { // remove constraint from items first for(GraphicsItem *item : items) { item->removeStartConstraint(citem); item->removeEndConstraint(citem); } q->removeItem(citem); delete citem; } constraintItems.clear(); } void GraphicsScene::Private::resetConstraintItems() { clearConstraintItems(); if ( constraintModel.isNull() ) return; QList clst = constraintModel->constraints(); Q_FOREACH( const Constraint& c, clst ) { createConstraintItem( c ); } q->updateItems(); } void GraphicsScene::Private::createConstraintItem( const Constraint& c ) { GraphicsItem* sitem = q->findItem( summaryHandlingModel->mapFromSource( c.startIndex() ) ); GraphicsItem* eitem = q->findItem( summaryHandlingModel->mapFromSource( c.endIndex() ) ); if ( sitem && eitem ) { ConstraintGraphicsItem* citem = new ConstraintGraphicsItem( c ); sitem->addStartConstraint( citem ); eitem->addEndConstraint( citem ); constraintItems.append( citem ); q->addItem( citem ); } //q->insertConstraintItem( c, citem ); } // Delete the constraint item, and clean up pointers in the start- and end item void GraphicsScene::Private::deleteConstraintItem( ConstraintGraphicsItem *citem ) { //qDebug()<<"GraphicsScene::Private::deleteConstraintItem citem="<constraint(); - GraphicsItem* item = items.value( summaryHandlingModel->mapFromSource( c.startIndex() ), 0 ); + GraphicsItem* item = items.value( summaryHandlingModel->mapFromSource( c.startIndex() ), nullptr ); if ( item ) { item->removeStartConstraint( citem ); } - item = items.value( summaryHandlingModel->mapFromSource( c.endIndex() ), 0 ); + item = items.value( summaryHandlingModel->mapFromSource( c.endIndex() ), nullptr ); if ( item ) { item->removeEndConstraint( citem ); } constraintItems.removeAt(constraintItems.indexOf(citem)); delete citem; } void GraphicsScene::Private::deleteConstraintItem( const Constraint& c ) { deleteConstraintItem( findConstraintItem( c ) ); } ConstraintGraphicsItem* GraphicsScene::Private::findConstraintItem( const Constraint& c ) const { - GraphicsItem* item = items.value( summaryHandlingModel->mapFromSource( c.startIndex() ), 0 ); + GraphicsItem* item = items.value( summaryHandlingModel->mapFromSource( c.startIndex() ), nullptr ); if ( item ) { const QList clst = item->startConstraints(); QList::const_iterator it = clst.begin(); for ( ; it != clst.end() ; ++it ) { if ( c.compareIndexes((*it)->constraint()) ) break; } if ( it != clst.end() ) { return *it; } } - item = items.value( summaryHandlingModel->mapFromSource( c.endIndex() ), 0 ); + item = items.value( summaryHandlingModel->mapFromSource( c.endIndex() ), nullptr ); if ( item ) { const QList clst = item->endConstraints(); QList::const_iterator it = clst.begin(); for ( ; it != clst.end() ; ++it ) { if ( c.compareIndexes( (*it)->constraint() ) ) break; } if ( it != clst.end() ) { return *it; } } - return 0; + return nullptr; } // NOTE: we might get here after indexes are invalidated, so cannot do any controlled cleanup void GraphicsScene::Private::clearItems() { for(GraphicsItem *item : items) { q->removeItem(item); delete item; } items.clear(); // do last to avoid cleaning up items clearConstraintItems(); } GraphicsScene::GraphicsScene( QObject* parent ) : QGraphicsScene( parent ), _d( new Private( this ) ) { init(); } GraphicsScene::~GraphicsScene() { qDeleteAll( items() ); delete _d; } #define d d_func() void GraphicsScene::init() { setItemIndexMethod( QGraphicsScene::NoIndex ); setConstraintModel( new ConstraintModel( this ) ); connect( d->grid, SIGNAL(gridChanged()), this, SLOT(slotGridChanged()) ); } /* NOTE: The delegate should really be a property * of the view, but that doesn't really fit at * this time */ void GraphicsScene::setItemDelegate( ItemDelegate* delegate ) { if ( !d->itemDelegate.isNull() && d->itemDelegate->parent()==this ) delete d->itemDelegate; d->itemDelegate = delegate; update(); } ItemDelegate* GraphicsScene::itemDelegate() const { return d->itemDelegate; } QAbstractItemModel* GraphicsScene::model() const { assert(!d->summaryHandlingModel.isNull()); return d->summaryHandlingModel->sourceModel(); } void GraphicsScene::setModel( QAbstractItemModel* model ) { assert(!d->summaryHandlingModel.isNull()); d->summaryHandlingModel->setSourceModel(model); d->grid->setModel( d->summaryHandlingModel ); setSelectionModel( new QItemSelectionModel( model, this ) ); } QAbstractProxyModel* GraphicsScene::summaryHandlingModel() const { return d->summaryHandlingModel; } void GraphicsScene::setSummaryHandlingModel( QAbstractProxyModel* proxyModel ) { proxyModel->setSourceModel( model() ); d->summaryHandlingModel = proxyModel; } void GraphicsScene::setRootIndex( const QModelIndex& idx ) { d->grid->setRootIndex( idx ); } QModelIndex GraphicsScene::rootIndex() const { return d->grid->rootIndex(); } ConstraintModel* GraphicsScene::constraintModel() const { return d->constraintModel; } void GraphicsScene::setConstraintModel( ConstraintModel* cm ) { if ( !d->constraintModel.isNull() ) { d->constraintModel->disconnect( this ); d->clearConstraintItems(); } d->constraintModel = cm; connect( cm, SIGNAL(constraintAdded(KGantt::Constraint)), this, SLOT(slotConstraintAdded(KGantt::Constraint)) ); connect( cm, SIGNAL(constraintRemoved(KGantt::Constraint)), this, SLOT(slotConstraintRemoved(KGantt::Constraint)) ); d->resetConstraintItems(); } void GraphicsScene::setSelectionModel( QItemSelectionModel* smodel ) { d->selectionModel = smodel; // TODO: update selection from model and connect signals } QItemSelectionModel* GraphicsScene::selectionModel() const { return d->selectionModel; } void GraphicsScene::setRowController( AbstractRowController* rc ) { d->rowController = rc; } AbstractRowController* GraphicsScene::rowController() const { return d->rowController; } void GraphicsScene::setGrid( AbstractGrid* grid ) { - QAbstractItemModel* model = 0; - if ( grid == 0 ) grid = &d->default_grid; + QAbstractItemModel* model = nullptr; + if ( grid == nullptr ) grid = &d->default_grid; if ( d->grid ) { d->grid->disconnect( this ); model = d->grid->model(); } d->grid = grid; connect( d->grid, SIGNAL(gridChanged()), this, SLOT(slotGridChanged()) ); d->grid->setModel( model ); slotGridChanged(); } AbstractGrid* GraphicsScene::grid() const { return d->grid; } void GraphicsScene::setReadOnly( bool ro ) { d->readOnly = ro; } bool GraphicsScene::isReadOnly() const { return d->readOnly; } /* Returns the index with column=0 fromt the * same row as idx and with the same parent. * This is used to traverse the tree-structure * of the model */ QModelIndex GraphicsScene::mainIndex( const QModelIndex& idx ) { #if 0 if ( idx.isValid() ) { return idx.model()->index( idx.row(), 0,idx.parent() ); } else { return QModelIndex(); } #else return idx; #endif } /*! Returns the index pointing to the last column * in the same row as idx. This can be thought of * as in "inverse" of mainIndex() */ QModelIndex GraphicsScene::dataIndex( const QModelIndex& idx ) { #if 0 if ( idx.isValid() ) { const QAbstractItemModel* model = idx.model(); return model->index( idx.row(), model->columnCount( idx.parent() )-1,idx.parent() ); } else { return QModelIndex(); } #else return idx; #endif } /*! Creates a new item of type type. * TODO: If the user should be allowed to override * this in any way, it needs to be in View! */ GraphicsItem* GraphicsScene::createItem( ItemType type ) const { #if 0 switch ( type ) { case TypeEvent: return 0; case TypeTask: return new TaskItem; case TypeSummary: return new SummaryItem; default: return 0; } #endif //qDebug() << "GraphicsScene::createItem("<findItem( idx ); const int itemtype = summaryHandlingModel->data( idx, ItemTypeRole ).toInt(); if (!item) { item = q->createItem( static_cast( itemtype ) ); item->setIndex( idx ); q->insertItem( idx, item); } item->updateItem( span, idx ); QModelIndex child; int cr = 0; while ( ( child = idx.child( cr, 0 ) ).isValid() ) { recursiveUpdateMultiItem( span, child ); ++cr; } } void GraphicsScene::updateRow( const QModelIndex& rowidx ) { //qDebug() << "GraphicsScene::updateRow("<mapToSource( rowidx ); Span rg = rowController()->rowGeometry( sidx ); for ( QModelIndex treewalkidx = sidx; treewalkidx.isValid(); treewalkidx = treewalkidx.parent() ) { if ( treewalkidx.data( ItemTypeRole ).toInt() == TypeMulti && !rowController()->isRowExpanded( treewalkidx )) { rg = rowController()->rowGeometry( treewalkidx ); } } bool blocked = blockSignals( true ); for ( int col = 0; col < summaryHandlingModel()->columnCount( rowidx.parent() ); ++col ) { const QModelIndex idx = summaryHandlingModel()->index( rowidx.row(), col, rowidx.parent() ); const QModelIndex sidx = summaryHandlingModel()->mapToSource( idx ); const int itemtype = summaryHandlingModel()->data( idx, ItemTypeRole ).toInt(); const bool isExpanded = rowController()->isRowExpanded( sidx ); if ( itemtype == TypeNone ) { removeItem( idx ); continue; } if ( itemtype == TypeMulti && !isExpanded ) { d->recursiveUpdateMultiItem( rg, idx ); } else { if ( summaryHandlingModel()->data( rowidx.parent(), ItemTypeRole ).toInt() == TypeMulti && !isExpanded ) { //continue; } GraphicsItem* item = findItem( idx ); if (!item) { item = createItem( static_cast( itemtype ) ); item->setIndex( idx ); insertItem(idx, item); } const Span span = rowController()->rowGeometry( sidx ); item->updateItem( span, idx ); } } blockSignals( blocked ); } void GraphicsScene::insertItem( const QPersistentModelIndex& idx, GraphicsItem* item ) { if ( !d->constraintModel.isNull() ) { // Create items for constraints const QModelIndex sidx = summaryHandlingModel()->mapToSource( idx ); const QList clst = d->constraintModel->constraintsForIndex( sidx ); Q_FOREACH( const Constraint& c, clst ) { QModelIndex other_idx; if ( c.startIndex() == sidx ) { other_idx = c.endIndex(); - GraphicsItem* other_item = d->items.value(summaryHandlingModel()->mapFromSource( other_idx ),0); + GraphicsItem* other_item = d->items.value(summaryHandlingModel()->mapFromSource( other_idx ),nullptr); if ( !other_item ) continue; ConstraintGraphicsItem* citem = new ConstraintGraphicsItem( c ); item->addStartConstraint( citem ); other_item->addEndConstraint( citem ); d->constraintItems.append( citem ); addItem( citem ); } else if ( c.endIndex() == sidx ) { other_idx = c.startIndex(); - GraphicsItem* other_item = d->items.value(summaryHandlingModel()->mapFromSource( other_idx ),0); + GraphicsItem* other_item = d->items.value(summaryHandlingModel()->mapFromSource( other_idx ),nullptr); if ( !other_item ) continue; ConstraintGraphicsItem* citem = new ConstraintGraphicsItem( c ); other_item->addStartConstraint( citem ); item->addEndConstraint( citem ); d->constraintItems.append( citem ); addItem( citem ); } else { assert( 0 ); // Impossible } } } d->items.insert( idx, item ); addItem( item ); } void GraphicsScene::removeItem( const QModelIndex& idx ) { //qDebug() << "GraphicsScene::removeItem("<::iterator it = d->items.find( idx ); if ( it != d->items.end() ) { GraphicsItem* item = *it; assert( item ); // We have to remove the item from the list first because // there is a good chance there will be reentrant calls d->items.erase( it ); { // Remove any constraintitems attached const QSet clst = QSet::fromList( item->startConstraints() ) + QSet::fromList( item->endConstraints() ); Q_FOREACH( ConstraintGraphicsItem* citem, clst ) { d->deleteConstraintItem( citem ); } } // Get rid of the item delete item; } } GraphicsItem* GraphicsScene::findItem( const QModelIndex& idx ) const { - if ( !idx.isValid() ) return 0; + if ( !idx.isValid() ) return nullptr; assert( idx.model() == summaryHandlingModel() ); QHash::const_iterator it = d->items.find( idx ); - return ( it != d->items.end() )?*it:0; + return ( it != d->items.end() )?*it:nullptr; } GraphicsItem* GraphicsScene::findItem( const QPersistentModelIndex& idx ) const { - if ( !idx.isValid() ) return 0; + if ( !idx.isValid() ) return nullptr; assert( idx.model() == summaryHandlingModel() ); QHash::const_iterator it = d->items.find( idx ); - return ( it != d->items.end() )?*it:0; + return ( it != d->items.end() )?*it:nullptr; } void GraphicsScene::clearItems() { d->clearItems(); } void GraphicsScene::updateItems() { for ( QHash::iterator it = d->items.begin(); it != d->items.end(); ++it ) { GraphicsItem* const item = it.value(); const QPersistentModelIndex& idx = it.key(); item->updateItem( Span( item->pos().y(), item->rect().height() ), idx ); } invalidate( QRectF(), QGraphicsScene::BackgroundLayer ); } void GraphicsScene::deleteSubtree( const QModelIndex& _idx ) { QModelIndex idx = dataIndex( _idx ); if ( !idx.model() ) return; const QModelIndex parent( idx.parent() ); const int colcount = idx.model()->columnCount( parent ); {for ( int i = 0; i < colcount; ++i ) { removeItem( parent.child( idx.row(), i ) ); }} const int rowcount = summaryHandlingModel()->rowCount( _idx ); {for ( int i = 0; i < rowcount; ++i ) { deleteSubtree( summaryHandlingModel()->index( i, summaryHandlingModel()->columnCount(_idx)-1, _idx ) ); }} } ConstraintGraphicsItem* GraphicsScene::findConstraintItem( const Constraint& c ) const { return d->findConstraintItem( c ); } void GraphicsScene::slotConstraintAdded( const KGantt::Constraint& c ) { d->createConstraintItem( c ); } void GraphicsScene::slotConstraintRemoved( const KGantt::Constraint& c ) { d->deleteConstraintItem( c ); } void GraphicsScene::slotGridChanged() { updateItems(); update(); emit gridChanged(); } void GraphicsScene::helpEvent( QGraphicsSceneHelpEvent *helpEvent ) { #ifndef QT_NO_TOOLTIP QGraphicsItem *item = itemAt( helpEvent->scenePos(), QTransform() ); if ( GraphicsItem* gitem = qgraphicsitem_cast( item ) ) { QToolTip::showText(helpEvent->screenPos(), gitem->ganttToolTip()); } else if ( ConstraintGraphicsItem* citem = qgraphicsitem_cast( item ) ) { QToolTip::showText(helpEvent->screenPos(), citem->ganttToolTip()); } else { QGraphicsScene::helpEvent( helpEvent ); } #endif /* QT_NO_TOOLTIP */ } void GraphicsScene::drawBackground( QPainter* painter, const QRectF& _rect ) { QRectF scn( sceneRect() ); QRectF rect( _rect ); if ( d->isPrinting && d->drawColumnLabels ) { QRectF headerRect( scn.topLeft()+QPointF( d->labelsWidth, 0 ), QSizeF( scn.width()-d->labelsWidth, d->rowController->headerHeight() )); - d->grid->paintHeader( painter, headerRect, rect, 0, 0 ); + d->grid->paintHeader( painter, headerRect, rect, 0, nullptr ); #if 0 /* We have to blank out the part of the header that is invisible during * normal rendering when we are printing. */ QRectF labelsTabRect( scn.topLeft(), QSizeF( d->labelsWidth, headerRect.height() ) ); QStyleOptionHeader opt; opt.rect = labelsTabRect.toRect(); opt.text = QLatin1String(""); opt.textAlignment = Qt::AlignCenter; style()->drawControl(QStyle::CE_Header, &opt, painter, 0); #endif scn.setTop( headerRect.bottom() ); scn.setLeft( headerRect.left() ); rect = rect.intersected( scn ); } d->grid->paintGrid( painter, scn, rect, d->rowController ); d->grid->drawBackground(painter, rect); } void GraphicsScene::drawForeground( QPainter* painter, const QRectF& rect ) { d->grid->drawForeground(painter, rect); } void GraphicsScene::itemEntered( const QModelIndex& idx ) { emit entered( idx ); } void GraphicsScene::itemPressed( const QModelIndex& idx ) { emit pressed( idx ); } void GraphicsScene::itemClicked( const QModelIndex& idx ) { emit clicked( idx ); } void GraphicsScene::itemDoubleClicked( const QModelIndex& idx ) { emit qrealClicked( idx ); } void GraphicsScene::setDragSource( GraphicsItem* item ) { d->dragSource = item; } GraphicsItem* GraphicsScene::dragSource() const { return d->dragSource; } /*! Print the Gantt chart using \a printer. If \a drawRowLabels * is true (the default), each row will have it's label printed * on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. * * This version of print() will print multiple pages. */ void GraphicsScene::print( QPrinter* printer, bool drawRowLabels, bool drawColumnLabels ) { #ifndef HAVE_PRINTER Q_UNUSED( printer ); Q_UNUSED( drawRowLabels ); Q_UNUSED( drawColumnLabels ); #else QPainter painter( printer ); doPrint( &painter, printer->pageRect(), sceneRect().left(), sceneRect().right(), printer, drawRowLabels, drawColumnLabels ); #endif } /*! Print part of the Gantt chart from \a start to \a end using \a printer. * If \a drawRowLabels is true (the default), each row will have it's * label printed on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. * * This version of print() will print multiple pages. * * To print a certain range of a chart with a DateTimeGrid, use * qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const * to figure out the values for \a start and \a end. */ void GraphicsScene::print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels ) { #ifndef HAVE_PRINTER Q_UNUSED( printer ); Q_UNUSED( start ); Q_UNUSED( end ); Q_UNUSED( drawRowLabels ); Q_UNUSED( drawColumnLabels ); #else QPainter painter( printer ); doPrint( &painter, printer->pageRect(), start, end, printer, drawRowLabels, drawColumnLabels ); #endif } /*! Render the GanttView inside the rectangle \a target using the painter \a painter. * If \a drawRowLabels is true (the default), each row will have it's * label printed on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. */ void GraphicsScene::print( QPainter* painter, const QRectF& _targetRect, bool drawRowLabels, bool drawColumnLabels ) { QRectF targetRect( _targetRect ); if ( targetRect.isNull() ) { targetRect = sceneRect(); } - doPrint( painter, targetRect, sceneRect().left(), sceneRect().right(), 0, drawRowLabels, drawColumnLabels ); + doPrint( painter, targetRect, sceneRect().left(), sceneRect().right(), nullptr, drawRowLabels, drawColumnLabels ); } /*! Render the GanttView inside the rectangle \a target using the painter \a painter. * If \a drawRowLabels is true (the default), each row will have it's * label printed on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. * * To print a certain range of a chart with a DateTimeGrid, use * qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const * to figure out the values for \a start and \a end. */ void GraphicsScene::print( QPainter* painter, qreal start, qreal end, const QRectF& _targetRect, bool drawRowLabels, bool drawColumnLabels ) { QRectF targetRect( _targetRect ); if ( targetRect.isNull() ) { targetRect = sceneRect(); } - doPrint( painter, targetRect, start, end, 0, drawRowLabels, drawColumnLabels ); + doPrint( painter, targetRect, start, end, nullptr, drawRowLabels, drawColumnLabels ); } /*!\internal */ void GraphicsScene::doPrint( QPainter* painter, const QRectF& targetRect, qreal start, qreal end, QPrinter* printer, bool drawRowLabels, bool drawColumnLabels ) { assert( painter ); d->isPrinting = true; d->drawColumnLabels = drawColumnLabels; d->labelsWidth = 0.0; QFont sceneFont( font() ); #ifdef HAVE_PRINTER if ( printer ) { sceneFont = QFont( font(), printer ); if ( font().pointSizeF() >= 0.0 ) sceneFont.setPointSizeF( font().pointSizeF() ); else if ( font().pointSize() >= 0 ) sceneFont.setPointSize( font().pointSize() ); else sceneFont.setPixelSize( font().pixelSize() ); } #endif QGraphicsTextItem dummyTextItem( QLatin1String("X") ); dummyTextItem.adjustSize(); QFontMetrics fm(dummyTextItem.font()); sceneFont.setPixelSize( fm.height() ); const QRectF oldScnRect( sceneRect() ); QRectF scnRect( oldScnRect ); scnRect.setLeft( start ); scnRect.setRight( end ); bool b = blockSignals( true ); /* column labels */ if ( d->drawColumnLabels ) { QRectF headerRect( scnRect ); headerRect.setHeight( - d->rowController->headerHeight() ); scnRect.setTop(scnRect.top() - d->rowController->headerHeight()); } /* row labels */ QVector textLabels; if ( drawRowLabels ) { qreal textWidth = 0.; qreal charWidth = QFontMetricsF(sceneFont).width( QString::fromLatin1( "X" ) ); QModelIndex sidx = summaryHandlingModel()->mapToSource( summaryHandlingModel()->index( 0, 0, rootIndex()) ); do { QModelIndex idx = summaryHandlingModel()->mapFromSource( sidx ); const Span rg=rowController()->rowGeometry( sidx ); const QString txt = idx.data( Qt::DisplayRole ).toString(); QGraphicsTextItem* item = new QGraphicsTextItem( txt ); addItem( item ); textLabels << item; item->setTextWidth( QFontMetricsF(sceneFont).width( txt ) + charWidth ); textWidth = qMax( item->textWidth(), textWidth ); item->setPos( 0, rg.start() ); } while ( ( sidx = rowController()->indexBelow( sidx ) ).isValid() ); Q_FOREACH( QGraphicsTextItem* item, textLabels ) { item->setPos( scnRect.left()-textWidth, item->y() ); item->show(); } scnRect.setLeft( scnRect.left()-textWidth ); d->labelsWidth = textWidth; } setSceneRect( scnRect ); painter->save(); painter->setClipRect( targetRect ); qreal yratio = targetRect.height()/scnRect.height(); /* If we're not printing multiple pages, * check if the span fits and adjust: */ if ( !printer && targetRect.width()/scnRect.width() < yratio ) { yratio = targetRect.width()/scnRect.width(); } qreal offset = scnRect.left(); int pagecount = 0; while ( offset < scnRect.right() ) { painter->setFont( sceneFont ); render( painter, targetRect, QRectF( QPointF( offset, scnRect.top()), QSizeF( targetRect.width()/yratio, scnRect.height() ) ) ); offset += targetRect.width()/yratio; ++pagecount; if ( printer && offset < scnRect.right() ) { #ifdef HAVE_PRINTER printer->newPage(); #endif } else { break; } } d->isPrinting = false; d->drawColumnLabels = true; d->labelsWidth = 0.0; qDeleteAll( textLabels ); blockSignals( b ); setSceneRect( oldScnRect ); painter->restore(); } #include "moc_kganttgraphicsscene.cpp" #ifndef KDAB_NO_UNIT_TESTS #include "unittest/test.h" #include #include #include #include "kganttgraphicsview.h" class SceneTestRowController : public KGantt::AbstractRowController { private: static const int ROW_HEIGHT; QPointer m_model; public: SceneTestRowController() { } void setModel( QAbstractItemModel* model ) { m_model = model; } /*reimp*/int headerHeight() const Q_DECL_OVERRIDE { return 40; } /*reimp*/ bool isRowVisible( const QModelIndex& ) const Q_DECL_OVERRIDE { return true;} /*reimp*/ bool isRowExpanded( const QModelIndex& ) const Q_DECL_OVERRIDE { return false; } /*reimp*/ KGantt::Span rowGeometry( const QModelIndex& idx ) const Q_DECL_OVERRIDE { return KGantt::Span( idx.row() * ROW_HEIGHT, ROW_HEIGHT ); } /*reimp*/ int maximumItemHeight() const Q_DECL_OVERRIDE { return ROW_HEIGHT/2; } /*reimp*/int totalHeight() const Q_DECL_OVERRIDE { return m_model->rowCount()* ROW_HEIGHT; } /*reimp*/ QModelIndex indexAt( int height ) const Q_DECL_OVERRIDE { return m_model->index( height/ROW_HEIGHT, 0 ); } /*reimp*/ QModelIndex indexBelow( const QModelIndex& idx ) const Q_DECL_OVERRIDE { if ( !idx.isValid() )return QModelIndex(); return idx.model()->index( idx.row()+1, idx.column(), idx.parent() ); } /*reimp*/ QModelIndex indexAbove( const QModelIndex& idx ) const Q_DECL_OVERRIDE { if ( !idx.isValid() )return QModelIndex(); return idx.model()->index( idx.row()-1, idx.column(), idx.parent() ); } }; class TestLineItem : public QGraphicsLineItem { public: TestLineItem( bool *destroyedFlag ) : QGraphicsLineItem( 0, 0, 10, 10 ), // geometry doesn't matter m_destroyedFlag( destroyedFlag ) {} ~TestLineItem() { *m_destroyedFlag = true; } private: bool *m_destroyedFlag; }; const int SceneTestRowController::ROW_HEIGHT = 30; KDAB_SCOPED_UNITTEST_SIMPLE( KGantt, GraphicsView, "test" ) { QStandardItemModel model; QStandardItem* item = new QStandardItem(); item->setData( KGantt::TypeTask, KGantt::ItemTypeRole ); item->setData( QString::fromLatin1( "Decide on new product" ) ); item->setData( QDateTime( QDate( 2007, 3, 1 ) ), KGantt::StartTimeRole ); item->setData( QDateTime( QDate( 2007, 3, 3 ) ), KGantt::EndTimeRole ); QStandardItem* item2 = new QStandardItem(); item2->setData( KGantt::TypeTask, KGantt::ItemTypeRole ); item2->setData( QString::fromLatin1( "Educate personnel" ) ); item2->setData( QDateTime( QDate( 2007, 3, 3 ) ), KGantt::StartTimeRole ); item2->setData( QDateTime( QDate( 2007, 3, 6 ) ), KGantt::EndTimeRole ); model.appendRow( item ); model.appendRow( item2 ); SceneTestRowController rowController; rowController.setModel( &model ); KGantt::GraphicsView graphicsView; graphicsView.setRowController( &rowController ); graphicsView.setModel( &model ); // Now the interesting stuff - the items above are just for a "realistic environment" bool foreignItemDestroyed = false; TestLineItem *foreignItem = new TestLineItem( &foreignItemDestroyed ); graphicsView.scene()->addItem( foreignItem ); assertFalse( foreignItemDestroyed ); graphicsView.updateScene(); assertFalse( foreignItemDestroyed ); } #endif /* KDAB_NO_UNIT_TESTS */ diff --git a/src/KGantt/kganttgraphicsscene.h b/src/KGantt/kganttgraphicsscene.h index caef217..29863a4 100644 --- a/src/KGantt/kganttgraphicsscene.h +++ b/src/KGantt/kganttgraphicsscene.h @@ -1,138 +1,138 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KGANTTGRAPHICSSCENE_H #define KGANTTGRAPHICSSCENE_H #include #include #include #include #include "kganttglobal.h" QT_BEGIN_NAMESPACE class QAbstractProxyModel; class QItemSelectionModel; class QPrinter; QT_END_NAMESPACE namespace KGantt { class AbstractGrid; class AbstractRowController; class GraphicsItem; class Constraint; class ConstraintModel; class ConstraintGraphicsItem; class ItemDelegate; class KGANTT_EXPORT GraphicsScene : public QGraphicsScene { Q_OBJECT KGANTT_DECLARE_PRIVATE_BASE_POLYMORPHIC( GraphicsScene ) public: - explicit GraphicsScene( QObject* parent=0 ); + explicit GraphicsScene( QObject* parent = nullptr ); virtual ~GraphicsScene(); //qreal dateTimeToSceneX( const QDateTime& dt ) const; //QDateTime sceneXtoDateTime( qreal x ) const; static QModelIndex mainIndex( const QModelIndex& idx ); static QModelIndex dataIndex( const QModelIndex& idx ); QAbstractItemModel* model() const; QAbstractProxyModel* summaryHandlingModel() const; QModelIndex rootIndex() const; ConstraintModel* constraintModel() const; QItemSelectionModel* selectionModel() const; void insertItem( const QPersistentModelIndex&, GraphicsItem* ); void removeItem( const QModelIndex& ); using QGraphicsScene::removeItem; GraphicsItem* findItem( const QModelIndex& ) const; GraphicsItem* findItem( const QPersistentModelIndex& ) const; void updateItems(); void clearItems(); void deleteSubtree( const QModelIndex& ); ConstraintGraphicsItem* findConstraintItem( const Constraint& ) const; QList findConstraintItems( const QModelIndex& idx ) const; void setItemDelegate( ItemDelegate* ); ItemDelegate* itemDelegate() const; void setRowController( AbstractRowController* rc ); AbstractRowController* rowController() const; void setGrid( AbstractGrid* grid ); AbstractGrid* grid() const; bool isReadOnly() const; void updateRow( const QModelIndex& idx ); GraphicsItem* createItem( ItemType type ) const; /* used by GraphicsItem */ void itemEntered( const QModelIndex& ); void itemPressed( const QModelIndex& ); void itemClicked( const QModelIndex& ); void itemDoubleClicked( const QModelIndex& ); void setDragSource( GraphicsItem* item ); GraphicsItem* dragSource() const; /* Printing */ void print( QPrinter* printer, bool drawRowLabels = true, bool drawColumnLabels = true ); void print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels = true, bool drawColumnLabels = true ); void print( QPainter* painter, const QRectF& target = QRectF(), bool drawRowLabels=true, bool drawColumnLabels = true ); void print( QPainter* painter, qreal start, qreal end, const QRectF& target = QRectF(), bool drawRowLabels=true, bool drawColumnLabels = true ); Q_SIGNALS: void gridChanged(); void clicked( const QModelIndex & index ); void qrealClicked( const QModelIndex & index ); void entered( const QModelIndex & index ); void pressed( const QModelIndex & index ); protected: /*reimp*/ void helpEvent( QGraphicsSceneHelpEvent *helpEvent ) Q_DECL_OVERRIDE; /*reimp*/ void drawBackground( QPainter* painter, const QRectF& rect ) Q_DECL_OVERRIDE; /*reimp*/ void drawForeground( QPainter* painter, const QRectF& rect ) Q_DECL_OVERRIDE; public Q_SLOTS: void setModel( QAbstractItemModel* ); void setSummaryHandlingModel( QAbstractProxyModel* ); void setConstraintModel( ConstraintModel* ); void setRootIndex( const QModelIndex& idx ); void setSelectionModel( QItemSelectionModel* selectionmodel ); void setReadOnly( bool ); private Q_SLOTS: /* slots for ConstraintModel */ void slotConstraintAdded( const KGantt::Constraint& ); void slotConstraintRemoved( const KGantt::Constraint& ); void slotGridChanged(); private: void doPrint( QPainter* painter, const QRectF& targetRect, qreal start, qreal end, QPrinter* printer, bool drawRowLabels, bool drawColumnLabels ); }; } #endif /* KGANTTGRAPHICSSCENE_H */ diff --git a/src/KGantt/kganttgraphicsview.cpp b/src/KGantt/kganttgraphicsview.cpp index 5d920e5..9fdf2a1 100644 --- a/src/KGantt/kganttgraphicsview.cpp +++ b/src/KGantt/kganttgraphicsview.cpp @@ -1,795 +1,795 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "kganttgraphicsview.h" #include "kganttgraphicsview_p.h" #include "kganttabstractrowcontroller.h" #include "kganttgraphicsitem.h" #include "kganttconstraintmodel.h" #include #include #include #include #include #include #include #include #if defined KDAB_EVAL #include "../evaldialog/evaldialog.h" #endif /*!\class KGantt::HeaderWidget * \internal */ using namespace KGantt; HeaderWidget::HeaderWidget( GraphicsView* parent ) : QWidget( parent ), m_offset( 0. ) { assert( parent ); // Parent must be set } HeaderWidget::~HeaderWidget() { } void HeaderWidget::scrollTo( int v ) { m_offset = v; // QWidget::scroll() wont work properly for me on Mac //scroll( static_cast( old-v ), 0 ); update(); } void HeaderWidget::paintEvent( QPaintEvent* ev ) { QPainter p( this ); view()->grid()->paintHeader( &p, rect(), ev->rect(), m_offset, this ); } bool HeaderWidget::event( QEvent* event ) { if ( event->type() == QEvent::ToolTip ) { DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() ); if ( grid ) { QHelpEvent *e = static_cast( event ); QDateTime dt = grid->mapFromChart( view()->mapToScene( e->x(), 0 ).x() ).toDateTime(); setToolTip( dt.toString() ); } } return QWidget::event( event ); } void HeaderWidget::contextMenuEvent( QContextMenuEvent* event ) { QMenu contextMenu; DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() ); - QAction* actionScaleAuto = 0; - QAction* actionScaleMonth = 0; - QAction* actionScaleWeek = 0; - QAction* actionScaleDay = 0; - QAction* actionScaleHour = 0; - QAction* actionZoomIn = 0; - QAction* actionZoomOut = 0; - if ( grid != 0 ) + QAction* actionScaleAuto = nullptr; + QAction* actionScaleMonth = nullptr; + QAction* actionScaleWeek = nullptr; + QAction* actionScaleDay = nullptr; + QAction* actionScaleHour = nullptr; + QAction* actionZoomIn = nullptr; + QAction* actionZoomOut = nullptr; + if ( grid != nullptr ) { QMenu* menuScale = new QMenu( tr( "Scale", "@title:menu" ), &contextMenu ); QActionGroup* scaleGroup = new QActionGroup( &contextMenu ); scaleGroup->setExclusive( true ); actionScaleAuto = new QAction( tr( "Auto", "@item:inmenu Automatic scale" ), menuScale ); actionScaleAuto->setCheckable( true ); actionScaleAuto->setChecked( grid->scale() == DateTimeGrid::ScaleAuto ); actionScaleMonth = new QAction( tr( "Month", "@item:inmenu" ), menuScale ); actionScaleMonth->setCheckable( true ); actionScaleMonth->setChecked( grid->scale() == DateTimeGrid::ScaleMonth ); actionScaleWeek = new QAction( tr( "Week", "@item:inmenu" ), menuScale ); actionScaleWeek->setCheckable( true ); actionScaleWeek->setChecked( grid->scale() == DateTimeGrid::ScaleWeek ); actionScaleDay = new QAction( tr( "Day", "@item:inmenu" ), menuScale ); actionScaleDay->setCheckable( true ); actionScaleDay->setChecked( grid->scale() == DateTimeGrid::ScaleDay ); actionScaleHour = new QAction( tr( "Hour", "@item:inmenu" ), menuScale ); actionScaleHour->setCheckable( true ); actionScaleHour->setChecked( grid->scale() == DateTimeGrid::ScaleHour ); scaleGroup->addAction( actionScaleAuto ); menuScale->addAction( actionScaleAuto ); scaleGroup->addAction( actionScaleMonth ); menuScale->addAction( actionScaleMonth ); scaleGroup->addAction( actionScaleWeek ); menuScale->addAction( actionScaleWeek ); scaleGroup->addAction( actionScaleDay ); menuScale->addAction( actionScaleDay ); scaleGroup->addAction( actionScaleHour ); menuScale->addAction( actionScaleHour ); contextMenu.addMenu( menuScale ); contextMenu.addSeparator(); actionZoomIn = new QAction( tr( "Zoom In", "@action:inmenu" ), &contextMenu ); contextMenu.addAction( actionZoomIn ); actionZoomOut = new QAction( tr( "Zoom Out", "@action:inmenu" ), &contextMenu ); contextMenu.addAction( actionZoomOut ); } if ( contextMenu.isEmpty() ) { event->ignore(); return; } const QAction* const action = contextMenu.exec( event->globalPos() ); - if ( action == 0 ) {} + if ( action == nullptr ) {} else if ( action == actionScaleAuto ) { - assert( grid != 0 ); + assert( grid != nullptr ); grid->setScale( DateTimeGrid::ScaleAuto ); } else if ( action == actionScaleMonth ) { - assert( grid != 0 ); + assert( grid != nullptr ); grid->setScale( DateTimeGrid::ScaleMonth ); } else if ( action == actionScaleWeek ) { - assert( grid != 0 ); + assert( grid != nullptr ); grid->setScale( DateTimeGrid::ScaleWeek ); } else if ( action == actionScaleDay ) { - assert( grid != 0 ); + assert( grid != nullptr ); grid->setScale( DateTimeGrid::ScaleDay ); } else if ( action == actionScaleHour ) { - assert( grid != 0 ); + assert( grid != nullptr ); grid->setScale( DateTimeGrid::ScaleHour ); } else if ( action == actionZoomIn ) { - assert( grid != 0 ); + assert( grid != nullptr ); grid->setDayWidth( grid->dayWidth() * 1.25 ); } else if ( action == actionZoomOut ) { - assert( grid != 0 ); + assert( grid != nullptr ); // daywidth *MUST NOT* go below 1.0, it is used as an integer later on grid->setDayWidth( qMax( 1.0, grid->dayWidth() * 0.8 ) ); } event->accept(); } GraphicsView::Private::Private( GraphicsView* _q ) - : q( _q ), rowcontroller(0), headerwidget( _q ) + : q( _q ), rowcontroller(nullptr), headerwidget( _q ) { } void GraphicsView::Private::updateHeaderGeometry() { q->setViewportMargins(0,rowcontroller->headerHeight(),0,0); headerwidget.setGeometry( q->viewport()->x(), q->viewport()->y() - rowcontroller->headerHeight(), q->viewport()->width(), rowcontroller->headerHeight() ); } void GraphicsView::Private::slotGridChanged() { updateHeaderGeometry(); headerwidget.update(); q->updateSceneRect(); q->update(); } void GraphicsView::Private::slotHorizontalScrollValueChanged( int val ) { const QRectF viewRect = q->transform().mapRect( q->sceneRect() ); headerwidget.scrollTo( val-q->horizontalScrollBar()->minimum()+static_cast( viewRect.left() ) ); } void GraphicsView::Private::slotColumnsInserted( const QModelIndex& parent, int start, int end ) { Q_UNUSED( start ); Q_UNUSED( end ); QModelIndex idx = scene.model()->index( 0, 0, scene.summaryHandlingModel()->mapToSource( parent ) ); do { scene.updateRow( scene.summaryHandlingModel()->mapFromSource( idx ) ); } while ( ( idx = rowcontroller->indexBelow( idx ) ) != QModelIndex() && rowcontroller->isRowVisible( idx ) ); //} while ( ( idx = d->treeview.indexBelow( idx ) ) != QModelIndex() && d->treeview.visualRect(idx).isValid() ); q->updateSceneRect(); } void GraphicsView::Private::slotColumnsRemoved( const QModelIndex& parent, int start, int end ) { // TODO Q_UNUSED( start ); Q_UNUSED( end ); Q_UNUSED( parent ); q->updateScene(); } void GraphicsView::Private::slotDataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ) { //qDebug() << "GraphicsView::slotDataChanged("<index( row, 0, parent ) ); } } void GraphicsView::Private::slotLayoutChanged() { //qDebug() << "slotLayoutChanged()"; q->updateScene(); } void GraphicsView::Private::slotModelReset() { //qDebug() << "slotModelReset()"; q->updateScene(); } void GraphicsView::Private::slotRowsInserted( const QModelIndex& parent, int start, int end ) { Q_UNUSED( parent ); Q_UNUSED( start ); Q_UNUSED( end ); q->updateScene(); // TODO: This might be optimised } void GraphicsView::Private::removeConstraintsRecursive( QAbstractProxyModel *summaryModel, const QModelIndex& index ) { if ( summaryModel->hasChildren( index ) ) { //qDebug() << "removing constraints from children of"<rowCount( index ); ++row ) { const QModelIndex child = summaryModel->index( row, index.column(), index ); removeConstraintsRecursive( summaryModel, child ); } } //qDebug() << "removing constraints from"<sourceModel() QList clst = scene.constraintModel()->constraintsForIndex( summaryModel->mapToSource( index ) ); Q_FOREACH( Constraint c, clst ) { scene.constraintModel()->removeConstraint( c ); } } void GraphicsView::Private::slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end ) { //qDebug() << "GraphicsView::Private::slotRowsAboutToBeRemoved("<columnCount( parent ); ++col ) { const QModelIndex idx = summaryModel->index( row, col, parent ); removeConstraintsRecursive( summaryModel, idx ); scene.removeItem( idx ); } } } void GraphicsView::Private::slotRowsRemoved( const QModelIndex& parent, int start, int end ) { //qDebug() << "GraphicsView::Private::slotRowsRemoved("<updateScene(); } void GraphicsView::Private::slotItemClicked( const QModelIndex& idx ) { QModelIndex sidx = idx;//scene.summaryHandlingModel()->mapToSource( idx ); emit q->clicked( sidx ); - if (q->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, q)) + if (q->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, q)) emit q->activated( sidx ); } void GraphicsView::Private::slotItemDoubleClicked( const QModelIndex& idx ) { QModelIndex sidx = idx;//scene.summaryHandlingModel()->mapToSource( idx ); emit q->qrealClicked( sidx ); - if (!q->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, q)) + if (!q->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, q)) emit q->activated( sidx ); } void GraphicsView::Private::slotHeaderContextMenuRequested( const QPoint& pt ) { emit q->headerContextMenuRequested( headerwidget.mapToGlobal( pt ) ); } /*!\class KGantt::GraphicsView kganttgraphicsview.h KGanttGraphicsView * \ingroup KGantt * \brief The GraphicsView class provides a model/view implementation of a gantt chart. * * */ /*! \fn void GraphicsView::activated( const QModelIndex & index ) */ /*! \fn void GraphicsView::clicked( const QModelIndex & index ); */ /*! \fn void GraphicsView::qrealClicked( const QModelIndex & index ); */ /*! \fn void GraphicsView::entered( const QModelIndex & index ); */ /*! \fn void GraphicsView::pressed( const QModelIndex & index ); */ /*! \fn void GraphicsView::headerContextMenuRequested( const QPoint& pt ) * This signal is emitted when the header has contextMenuPolicy Qt::CustomContextMenu * and the widget wants to show a context menu for the header. Unlike in * QWidget::customContextMenuRequested() signal, \a pt is here in global coordinates. */ /*! Constructor. Creates a new KGantt::GraphicsView with parent * \a parent. */ GraphicsView::GraphicsView( QWidget* parent ) : QGraphicsView( parent ), _d( new Private( this ) ) { #if defined KDAB_EVAL EvalDialog::checkEvalLicense( "KD Gantt" ); #endif connect( horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotHorizontalScrollValueChanged(int)) ); connect( &_d->scene, SIGNAL(gridChanged()), this, SLOT(slotGridChanged()) ); connect( &_d->scene, SIGNAL(entered(QModelIndex)), this, SIGNAL(entered(QModelIndex)) ); connect( &_d->scene, SIGNAL(pressed(QModelIndex)), this, SIGNAL(pressed(QModelIndex)) ); connect( &_d->scene, SIGNAL(clicked(QModelIndex)), this, SLOT(slotItemClicked(QModelIndex)) ); connect( &_d->scene, SIGNAL(qrealClicked(QModelIndex)), this, SLOT(slotItemDoubleClicked(QModelIndex)) ); connect( &_d->scene, SIGNAL(sceneRectChanged(QRectF)), this, SLOT(updateSceneRect()) ); connect( &_d->headerwidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotHeaderContextMenuRequested(QPoint)) ); setScene( &_d->scene ); // HACK! setSummaryHandlingModel( _d->scene.summaryHandlingModel() ); // So that AbstractGrid::drawBackground() and AbstractGrid::drawForeground() // works properly setViewportUpdateMode(QGraphicsView::FullViewportUpdate); //setCacheMode( CacheBackground ); } /*! Destroys this view. */ GraphicsView::~GraphicsView() { delete _d; } #define d d_func() /*! Sets the model to be displayed in this view to * \a model. The view does not take ownership of the model. * * To make a model work well with GraphicsView it must * have a certain layout. Whether the model is flat or has a * treestrucure is not important, as long as an * AbstractRowController is provided that can navigate the * model. * * GraphicsView operates per row in the model. The data is always * taken from the _last_ item in the row. The ItemRoles used are * Qt::DisplayRole and the roles defined in KGantt::ItemDataRole. * * Note: This model is not returned by \a model() * * \see GraphicsView::model */ void GraphicsView::setModel( QAbstractItemModel* model ) { if ( d->scene.model() ) { disconnect( d->scene.model() ); } d->scene.setModel( model ); connect( model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(updateSceneRect()) ); updateScene(); } /*! \returns the current model displayed by this view * * Note: The returned model is not the model set with \a setModel() * * \see GraphicsView::setModel */ QAbstractItemModel* GraphicsView::model() const { return d->scene.model(); } void GraphicsView::setSummaryHandlingModel( QAbstractProxyModel* proxyModel ) { disconnect( d->scene.summaryHandlingModel() ); d->scene.setSummaryHandlingModel( proxyModel ); /* Connections. We have to rely on the treeview * to receive the signals before we do(!) */ connect( proxyModel, SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(slotColumnsInserted(QModelIndex,int,int)) ); connect( proxyModel, SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(slotColumnsRemoved(QModelIndex,int,int)) ); connect( proxyModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(slotDataChanged(QModelIndex,QModelIndex)) ); connect( proxyModel, SIGNAL(layoutChanged()), this, SLOT(slotLayoutChanged()) ); connect( proxyModel, SIGNAL(modelReset()), this, SLOT(slotModelReset()) ); connect( proxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(slotRowsInserted(QModelIndex,int,int)) ); connect( proxyModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)) ); connect( proxyModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(slotRowsRemoved(QModelIndex,int,int)) ); updateScene(); } /*! Sets the constraintmodel displayed by this view. * \see KGantt::ConstraintModel. */ void GraphicsView::setConstraintModel( ConstraintModel* cmodel ) { d->scene.setConstraintModel( cmodel ); } /*! \returns the KGantt::ConstraintModel displayed by this view. */ ConstraintModel* GraphicsView::constraintModel() const { return d->scene.constraintModel(); } /*! \returns the KGantt::SummaryHandlingProxyModel used by this view. */ QAbstractProxyModel* GraphicsView::summaryHandlingModel() const { return d->scene.summaryHandlingModel(); } /*! Sets the root index of the model displayed by this view. * Similar to QAbstractItemView::setRootIndex, default is QModelIndex(). */ void GraphicsView::setRootIndex( const QModelIndex& idx ) { d->scene.setRootIndex( idx ); } /*! \returns the rootindex for this view. */ QModelIndex GraphicsView::rootIndex() const { return d->scene.rootIndex(); } /*! Sets the QItemSelectionModel used by this view to manage * selections. Similar to QAbstractItemView::setSelectionModel */ void GraphicsView::setSelectionModel( QItemSelectionModel* model ) { d->scene.setSelectionModel( model ); } /*! \returns the QItemSelectionModel used by this view */ QItemSelectionModel* GraphicsView::selectionModel() const { return d->scene.selectionModel(); } /*! Sets the KGantt::ItemDelegate used for rendering items on this * view. \see ItemDelegate and QAbstractItemDelegate. */ void GraphicsView::setItemDelegate( ItemDelegate* delegate ) { d->scene.setItemDelegate( delegate ); } /*! \returns the ItemDelegate used by this view to render items */ ItemDelegate* GraphicsView::itemDelegate() const { return d->scene.itemDelegate(); } /*! Sets the AbstractRowController used by this view. The * AbstractRowController deals with the height and position * of each row and with which parts of the model are * displayed. \see AbstractRowController */ void GraphicsView::setRowController( AbstractRowController* rowcontroller ) { d->rowcontroller = rowcontroller; d->scene.setRowController( rowcontroller ); updateScene(); } /*! \returns the AbstractRowController * for this view. \see setRowController */ AbstractRowController* GraphicsView::rowController() const { return d->rowcontroller; } /*! Sets the AbstractGrid for this view. The grid is an * object that controls how QModelIndexes are mapped * to and from the view and how the background and header * is rendered. \see AbstractGrid and DateTimeGrid. */ void GraphicsView::setGrid( AbstractGrid* grid ) { d->scene.setGrid( grid ); d->slotGridChanged(); } /*! \returns the AbstractGrid used by this view. */ AbstractGrid* GraphicsView::grid() const { return d->scene.grid(); } /*! Sets the view to read-only mode if \a to is true. The default is * read/write if the model permits it. */ void GraphicsView::setReadOnly( bool ro ) { d->scene.setReadOnly( ro ); } /*!\returns true iff the view is in read-only mode */ bool GraphicsView::isReadOnly() const { return d->scene.isReadOnly(); } /*! Sets the context menu policy for the header. The default value * Qt::DefaultContextMenu results in a standard context menu on the header * that allows the user to set the scale and zoom. * * Setting this to Qt::CustomContextMenu will cause the signal * headerContextMenuRequested(const QPoint& pt) to be emitted instead. * * \see QWidget::setContextMenuPolicy( Qt::ContextMenuPolicy ) */ void GraphicsView::setHeaderContextMenuPolicy( Qt::ContextMenuPolicy p ) { d->headerwidget.setContextMenuPolicy( p ); } /*! \returns the context menu policy for the header */ Qt::ContextMenuPolicy GraphicsView::headerContextMenuPolicy() const { return d->headerwidget.contextMenuPolicy(); } /*! Adds a constraint from \a from to \a to. \a modifiers are the * keyboard modifiers pressed by the user when the action is invoked. * * Override this to control how contraints are added. The default * implementation adds a soft constraint unless the Shift key is pressed, * in that case it adds a hard constraint. If a constraint is already * present, it is removed and nothing is added. */ void GraphicsView::addConstraint( const QModelIndex& from, const QModelIndex& to, Qt::KeyboardModifiers modifiers ) { if ( isReadOnly() ) return; ConstraintModel* cmodel = constraintModel(); assert( cmodel ); Constraint c( from, to, ( modifiers&Qt::ShiftModifier )?Constraint::TypeHard:Constraint::TypeSoft ); if ( cmodel->hasConstraint( c ) ) cmodel->removeConstraint( c ); else cmodel->addConstraint( c ); } void GraphicsView::resizeEvent( QResizeEvent* ev ) { d->updateHeaderGeometry(); QRectF r = scene()->itemsBoundingRect(); // To scroll more to the left than the actual item start, bug #4516 r.setLeft( qMin( 0.0, r.left() ) ); // TODO: take scrollbars into account (if not always on) // The scene should be at least the size of the viewport QSizeF size = viewport()->size(); //TODO: why -2 below? size should be ex. frames etc? if ( size.width() > r.width() ) { r.setWidth( size.width() - 2 ); } if ( size.height() > r.height() ) { r.setHeight( size.height() - 2 ); } const int totalh = rowController()->totalHeight(); if ( r.height() < totalh ) { r.setHeight( totalh ); } scene()->setSceneRect( r ); QGraphicsView::resizeEvent( ev ); } /*!\returns The QModelIndex for the item located at * position \a pos in the view or an invalid index * if no item was present at that position. * * This is useful for for example contextmenus. */ QModelIndex GraphicsView::indexAt( const QPoint& pos ) const { QGraphicsItem* item = itemAt( pos ); if ( GraphicsItem* gitem = qgraphicsitem_cast( item ) ) { return d->scene.summaryHandlingModel()->mapToSource( gitem->index() ); } else { return QModelIndex(); } } /*! \internal */ void GraphicsView::clearItems() { d->scene.clearItems(); } /*! \internal */ void GraphicsView::updateRow( const QModelIndex& idx ) { d->scene.updateRow( d->scene.summaryHandlingModel()->mapFromSource( idx ) ); } /*! \internal * Adjusts the bounding rectangle of the scene. */ void GraphicsView::updateSceneRect() { /* What to do with this? We need to shrink the view to * make collapsing items work */ qreal range = horizontalScrollBar()->maximum()-horizontalScrollBar()->minimum(); const qreal hscroll = horizontalScrollBar()->value()/( range>0?range:1 ); QRectF r = d->scene.itemsBoundingRect(); // To scroll more to the left than the actual item start, bug #4516 r.setTop( 0. ); r.setLeft( qMin( 0.0, r.left() ) ); r.setSize( r.size().expandedTo( viewport()->size() ) ); const int totalh = rowController()->totalHeight(); if ( r.height() < totalh ) r.setHeight( totalh ); d->scene.setSceneRect( r ); /* set scrollbar to keep the same time in view */ range = horizontalScrollBar()->maximum()-horizontalScrollBar()->minimum(); if ( range>0 ) horizontalScrollBar()->setValue( qRound( hscroll*range ) ); /* We have to update here to adjust for any rows with no * information because they are painted with a different * background brush */ d->scene.invalidate( QRectF(), QGraphicsScene::BackgroundLayer ); } /*! \internal * Resets the state of the view. */ void GraphicsView::updateScene() { clearItems(); if ( !model()) return; if ( !rowController()) return; QModelIndex idx = model()->index( 0, 0, rootIndex() ); do { updateRow( idx ); } while ( ( idx = rowController()->indexBelow( idx ) ) != QModelIndex() && rowController()->isRowVisible(idx) ); //constraintModel()->cleanup(); //qDebug() << constraintModel(); updateSceneRect(); if ( scene() ) scene()->invalidate( QRectF(), QGraphicsScene::BackgroundLayer ); } /*! \internal */ GraphicsItem* GraphicsView::createItem( ItemType type ) const { return d->scene.createItem( type ); } /*! \internal */ void GraphicsView::deleteSubtree( const QModelIndex& idx ) { d->scene.deleteSubtree( d->scene.summaryHandlingModel()->mapFromSource( idx ) ); } /*! Print the Gantt chart using \a printer. If \a drawRowLabels * is true (the default), each row will have it's label printed * on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. * * This version of print() will print multiple pages. */ void GraphicsView::print( QPrinter* printer, bool drawRowLabels, bool drawColumnLabels ) { d->scene.print( printer, drawRowLabels, drawColumnLabels ); } /*! Print part of the Gantt chart from \a start to \a end using \a printer. * If \a drawRowLabels is true (the default), each row will have it's * label printed on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. * * This version of print() will print multiple pages. * * To print a certain range of a chart with a DateTimeGrid, use * qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const * to figure out the values for \a start and \a end. */ void GraphicsView::print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels ) { d->scene.print( printer, start, end, drawRowLabels, drawColumnLabels ); } /*! Render the GanttView inside the rectangle \a target using the painter \a painter. * If \a drawRowLabels is true (the default), each row will have it's * label printed on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. */ void GraphicsView::print( QPainter* painter, const QRectF& targetRect, bool drawRowLabels, bool drawColumnLabels ) { d->scene.print(painter, targetRect, drawRowLabels, drawColumnLabels); } /*! Render the GanttView inside the rectangle \a target using the painter \a painter. * If \a drawRowLabels is true (the default), each row will have it's * label printed on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. * * To print a certain range of a chart with a DateTimeGrid, use * qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const * to figure out the values for \a start and \a end. */ void GraphicsView::print( QPainter* painter, qreal start, qreal end, const QRectF& targetRect, bool drawRowLabels, bool drawColumnLabels ) { d->scene.print(painter, start, end, targetRect, drawRowLabels, drawColumnLabels); } #include "moc_kganttgraphicsview.cpp" diff --git a/src/KGantt/kganttgraphicsview.h b/src/KGantt/kganttgraphicsview.h index 5dc1ff5..62987e5 100644 --- a/src/KGantt/kganttgraphicsview.h +++ b/src/KGantt/kganttgraphicsview.h @@ -1,133 +1,133 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KGANTTGRAPHICSVIEW_H #define KGANTTGRAPHICSVIEW_H #include #include "kganttglobal.h" QT_BEGIN_NAMESPACE class QPrinter; class QModelIndex; class QAbstractItemModel; class QAbstractProxyModel; class QItemSelectionModel; QT_END_NAMESPACE namespace KGantt { class AbstractRowController; class AbstractGrid; class GraphicsItem; class ConstraintModel; class ItemDelegate; class KGANTT_EXPORT GraphicsView : public QGraphicsView { Q_OBJECT KGANTT_DECLARE_PRIVATE_BASE_POLYMORPHIC(GraphicsView) Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly ) Q_PRIVATE_SLOT( d, void slotGridChanged() ) Q_PRIVATE_SLOT( d, void slotHorizontalScrollValueChanged( int ) ) Q_PRIVATE_SLOT( d, void slotHeaderContextMenuRequested( const QPoint& ) ) /* slots for QAbstractItemModel signals */ Q_PRIVATE_SLOT( d, void slotColumnsInserted( const QModelIndex& parent, int start, int end ) ) Q_PRIVATE_SLOT( d, void slotColumnsRemoved( const QModelIndex& parent, int start, int end ) ) Q_PRIVATE_SLOT( d, void slotDataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ) ) Q_PRIVATE_SLOT( d, void slotLayoutChanged() ) Q_PRIVATE_SLOT( d, void slotModelReset() ) Q_PRIVATE_SLOT( d, void slotRowsInserted( const QModelIndex& parent, int start, int end ) ) Q_PRIVATE_SLOT( d, void slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end ) ) Q_PRIVATE_SLOT( d, void slotRowsRemoved( const QModelIndex& parent, int start, int end ) ) Q_PRIVATE_SLOT( d, void slotItemClicked( const QModelIndex& idx ) ) Q_PRIVATE_SLOT( d, void slotItemDoubleClicked( const QModelIndex& idx ) ) public: - explicit GraphicsView( QWidget* parent=0 ); + explicit GraphicsView( QWidget* parent = nullptr ); virtual ~GraphicsView(); QAbstractItemModel* model() const; QAbstractProxyModel* summaryHandlingModel() const; ConstraintModel* constraintModel() const; QModelIndex rootIndex() const; QItemSelectionModel* selectionModel() const; AbstractRowController* rowController() const; AbstractGrid* grid() const; ItemDelegate* itemDelegate() const; bool isReadOnly() const; void setHeaderContextMenuPolicy( Qt::ContextMenuPolicy ); Qt::ContextMenuPolicy headerContextMenuPolicy() const; QModelIndex indexAt( const QPoint& pos ) const; virtual void addConstraint( const QModelIndex& from, const QModelIndex& to, Qt::KeyboardModifiers modifiers ); void updateRow( const QModelIndex& ); void updateScene(); public Q_SLOTS: void updateSceneRect(); public: void deleteSubtree( const QModelIndex& ); void print( QPrinter* printer, bool drawRowLabels = true, bool drawColumnLabels = true ); void print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels = true, bool drawColumnLabels = true ); void print( QPainter* painter, const QRectF& target = QRectF(), bool drawRowLabels = true, bool drawColumnLabels = true ); void print( QPainter* painter, qreal start, qreal end, const QRectF& target = QRectF(), bool drawRowLabels = true, bool drawColumnLabels = true ); public Q_SLOTS: void setModel( QAbstractItemModel* ); void setSummaryHandlingModel( QAbstractProxyModel* model ); void setConstraintModel( ConstraintModel* ); void setRootIndex( const QModelIndex& ); void setSelectionModel( QItemSelectionModel* ); void setRowController( AbstractRowController* ); void setGrid( AbstractGrid* ); void setItemDelegate( ItemDelegate* delegate ); void setReadOnly( bool ); Q_SIGNALS: void activated( const QModelIndex & index ); void clicked( const QModelIndex & index ); void qrealClicked( const QModelIndex & index ); void entered( const QModelIndex & index ); void pressed( const QModelIndex & index ); void headerContextMenuRequested( const QPoint& pt ); protected: void clearItems(); /*reimp*/void resizeEvent( QResizeEvent* ) Q_DECL_OVERRIDE; private: friend class View; GraphicsItem* createItem( ItemType type ) const; }; } #endif /* KGANTTGRAPHICSVIEW_H */ diff --git a/src/KGantt/kganttitemdelegate.h b/src/KGantt/kganttitemdelegate.h index 2cc65f2..e224014 100644 --- a/src/KGantt/kganttitemdelegate.h +++ b/src/KGantt/kganttitemdelegate.h @@ -1,96 +1,96 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KGANTTITEMDELEGATE_H #define KGANTTITEMDELEGATE_H #include #include #include #include #include "kganttglobal.h" namespace KGantt { class StyleOptionGanttItem; class Constraint; class KGANTT_EXPORT ItemDelegate : public QItemDelegate { Q_OBJECT KGANTT_DECLARE_PRIVATE_BASE_POLYMORPHIC( ItemDelegate ) public: enum InteractionState { State_None = 0, State_Move, State_ExtendLeft, State_ExtendRight, State_DragConstraint }; - explicit ItemDelegate( QObject* parent = 0 ); + explicit ItemDelegate( QObject* parent = nullptr ); virtual ~ItemDelegate(); void setDefaultBrush( ItemType type, const QBrush& brush ); QBrush defaultBrush( ItemType type ) const; void setDefaultPen( ItemType type, const QPen& pen ); QPen defaultPen( ItemType type ) const; virtual Span itemBoundingSpan(const StyleOptionGanttItem& opt, const QModelIndex& idx) const; virtual QRectF constraintBoundingRect( const QPointF& start, const QPointF& end, const Constraint &constraint ) const; virtual InteractionState interactionStateFor( const QPointF& pos, const StyleOptionGanttItem& opt, const QModelIndex& idx ) const; virtual void paintGanttItem( QPainter* p, const StyleOptionGanttItem& opt, const QModelIndex& idx ); virtual void paintConstraintItem( QPainter* p, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint ); virtual QString toolTip( const QModelIndex &idx ) const; protected: void paintFinishStartConstraint( QPainter* p, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint ); QPolygonF finishStartLine( const QPointF& start, const QPointF& end ) const; QPolygonF finishStartArrow( const QPointF& start, const QPointF& end ) const; void paintFinishFinishConstraint( QPainter* p, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint ); QPolygonF finishFinishLine( const QPointF& start, const QPointF& end ) const; QPolygonF finishFinishArrow( const QPointF& start, const QPointF& end ) const; void paintStartStartConstraint( QPainter* p, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint ); QPolygonF startStartLine( const QPointF& start, const QPointF& end ) const; QPolygonF startStartArrow( const QPointF& start, const QPointF& end ) const; void paintStartFinishConstraint( QPainter* p, const QStyleOptionGraphicsItem& opt, const QPointF& start, const QPointF& end, const Constraint &constraint ); QPolygonF startFinishLine( const QPointF& start, const QPointF& end ) const; QPolygonF startFinishArrow( const QPointF& start, const QPointF& end ) const; }; } #ifndef QT_NO_DEBUG_STREAM QDebug operator<<( QDebug dbg, KGantt::ItemDelegate::InteractionState ); #endif #endif /* KGANTTITEMDELEGATE_H */ diff --git a/src/KGantt/kganttlegend.cpp b/src/KGantt/kganttlegend.cpp index 127c9d7..0a04a1e 100644 --- a/src/KGantt/kganttlegend.cpp +++ b/src/KGantt/kganttlegend.cpp @@ -1,208 +1,208 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "kganttlegend.h" #include "kganttlegend_p.h" #include "kganttitemdelegate.h" #include #include #include using namespace KGantt; /*!\class KGantt::Legend kganttlegend.h KGanttLegend * \ingroup KGantt * \brief Legend showing an image and a description for Gantt items * * This is an item view class showing a small Gantt item and it's * text defined by LegendRole. */ /*! Constructor. Creates a Legend with parent \a parent. * The QObject parent is not used for anything internally. */ Legend::Legend( QWidget* parent ) : QAbstractItemView( parent ), _d( new Private ) { setItemDelegate( new ItemDelegate( this ) ); setFrameStyle( QFrame::NoFrame ); } /*! Destructor. Does nothing */ Legend::~Legend() { delete _d; } #define d d_func() QModelIndex Legend::indexAt( const QPoint& point ) const { Q_UNUSED( point ); return QModelIndex(); } QRect Legend::visualRect( const QModelIndex& index ) const { Q_UNUSED( index ); return QRect(); } QSize Legend::sizeHint() const { return measureItem( rootIndex() ); } QSize Legend::minimumSizeHint() const { return measureItem( rootIndex() ); } void Legend::setModel( QAbstractItemModel* model ) { - if ( this->model() != 0 ) + if ( this->model() != nullptr ) { disconnect( this->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(modelDataChanged()) ); disconnect( this->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(modelDataChanged()) ); disconnect( this->model(), SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(modelDataChanged()) ); } QAbstractItemView::setModel( model ); d->proxyModel.setSourceModel( model ); - if ( this->model() != 0 ) + if ( this->model() != nullptr ) { connect( this->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(modelDataChanged()) ); connect( this->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(modelDataChanged()) ); connect( this->model(), SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(modelDataChanged()) ); } } /*! Triggers repainting of the legend. */ void Legend::modelDataChanged() { updateGeometry(); viewport()->update(); } void Legend::paintEvent( QPaintEvent* event ) { Q_UNUSED( event ); // no model, no legend... - if ( model() == 0 ) + if ( model() == nullptr ) return; QPainter p( viewport() ); p.fillRect( viewport()->rect(), palette().color( QPalette::Window ) ); drawItem( &p, rootIndex() ); } /*! Creates a StyleOptionGanttItem with all style options filled in * except the target rectangles. */ StyleOptionGanttItem Legend::getStyleOption( const QModelIndex& index ) const { StyleOptionGanttItem opt; opt.displayPosition = StyleOptionGanttItem::Right; opt.displayAlignment = Qt::Alignment( d->proxyModel.data( index, Qt::TextAlignmentRole ).toInt() ); opt.text = index.model()->data( index, LegendRole ).toString(); opt.font = ( index.model()->data( index, Qt::FontRole ) ).value< QFont >(); return opt; } /*! Draws the legend item at \a index and all of it's children recursively * at \a pos onto \a painter. * Reimplement this if you want to draw items in an user defined way. * \returns the rectangle drawn. */ QRect Legend::drawItem( QPainter* painter, const QModelIndex& index, const QPoint& pos ) const { int xPos = pos.x(); int yPos = pos.y(); if ( index.isValid() && index.model() == &d->proxyModel ) { ItemDelegate* const delegate = qobject_cast< ItemDelegate* >( itemDelegate( index ) ); - assert( delegate != 0 ); + assert( delegate != nullptr ); const QRect r( pos, measureItem( index, false ) ); StyleOptionGanttItem opt = getStyleOption( index ); opt.rect = r; opt.rect.setWidth( r.height() ); const ItemType typ = static_cast( index.model()->data( index, ItemTypeRole ).toInt() ); const int dx = (typ == TypeEvent) ? (r.height() / 2) : 0; opt.itemRect = opt.rect.adjusted(dx,0,dx,0); opt.boundingRect = r; opt.boundingRect.setWidth( r.width() + r.height() ); if ( !opt.text.isNull() ) delegate->paintGanttItem( painter, opt, index ); xPos = r.right(); yPos = r.bottom(); } const int rowCount = d->proxyModel.rowCount( index ); for ( int row = 0; row < rowCount; ++row ) { const QRect r = drawItem( painter, d->proxyModel.index( row, 0, index ), QPoint( pos.x(), yPos ) ); xPos = qMax( xPos, r.right() ); yPos = qMax( yPos, r.bottom() ); } return QRect( pos, QPoint( xPos, yPos ) ); } /*! Calculates the needed space for the legend item at \a index and, if \a recursive is true, * all child items. */ QSize Legend::measureItem( const QModelIndex& index, bool recursive ) const { - if ( model() == 0 ) + if ( model() == nullptr ) return QSize(); QSize baseSize; - if ( index.model() != 0 ) + if ( index.model() != nullptr ) { QFontMetrics fm( ( index.model()->data( index, Qt::FontRole ) ).value< QFont >() ); const QString text = index.model()->data( index, LegendRole ).toString(); if ( !text.isEmpty() ) baseSize += QSize( fm.width( text ) + fm.height() + 2, fm.height() + 2 ); } if ( !recursive ) return baseSize; QSize childrenSize; const int rowCount = d->proxyModel.rowCount( index ); for ( int row = 0; row < rowCount; ++row ) { const QSize childSize = measureItem( d->proxyModel.index( row, 0, index ) ); childrenSize.setWidth( qMax( childrenSize.width(), childSize.width() ) ); childrenSize.rheight() += childSize.height(); } return baseSize + childrenSize; } diff --git a/src/KGantt/kganttlegend.h b/src/KGantt/kganttlegend.h index 4f2ca19..c92a6b5 100644 --- a/src/KGantt/kganttlegend.h +++ b/src/KGantt/kganttlegend.h @@ -1,67 +1,67 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KGANTTLEGEND_H #define KGANTTLEGEND_H #include #include "kganttglobal.h" #include "kganttstyleoptionganttitem.h" namespace KGantt { class KGANTT_EXPORT Legend : public QAbstractItemView { Q_OBJECT KGANTT_DECLARE_PRIVATE_BASE_POLYMORPHIC( Legend ) public: - explicit Legend( QWidget* parent = 0 ); + explicit Legend( QWidget* parent = nullptr ); virtual ~Legend(); /*reimp*/ QModelIndex indexAt( const QPoint& point ) const Q_DECL_OVERRIDE; /*reimp*/ QRect visualRect( const QModelIndex& index ) const Q_DECL_OVERRIDE; /*reimp*/ void scrollTo( const QModelIndex&, ScrollHint = EnsureVisible ) Q_DECL_OVERRIDE {} /*reimp*/ QSize sizeHint() const Q_DECL_OVERRIDE; /*reimp*/ QSize minimumSizeHint() const Q_DECL_OVERRIDE; /*reimp*/ void setModel( QAbstractItemModel* model ) Q_DECL_OVERRIDE; protected: virtual QRect drawItem( QPainter* painter, const QModelIndex& index, const QPoint& pos = QPoint() ) const; virtual QSize measureItem( const QModelIndex& index, bool recursive = true ) const; virtual StyleOptionGanttItem getStyleOption( const QModelIndex& index ) const; /*reimp*/ void paintEvent( QPaintEvent* event ) Q_DECL_OVERRIDE; /*reimp*/ int horizontalOffset() const Q_DECL_OVERRIDE { return 0; } /*reimp*/ bool isIndexHidden( const QModelIndex& ) const Q_DECL_OVERRIDE { return false; } /*reimp*/ QModelIndex moveCursor( CursorAction, Qt::KeyboardModifiers ) Q_DECL_OVERRIDE { return QModelIndex(); } /*reimp*/ void setSelection( const QRect&, QItemSelectionModel::SelectionFlags ) Q_DECL_OVERRIDE {} /*reimp*/ int verticalOffset() const Q_DECL_OVERRIDE { return 0; } /*reimp*/ QRegion visualRegionForSelection( const QItemSelection& ) const Q_DECL_OVERRIDE { return QRegion(); } protected Q_SLOTS: virtual void modelDataChanged(); }; } #endif diff --git a/src/KGantt/kganttlistviewrowcontroller.cpp b/src/KGantt/kganttlistviewrowcontroller.cpp index 3c6db1f..9d9e1c8 100644 --- a/src/KGantt/kganttlistviewrowcontroller.cpp +++ b/src/KGantt/kganttlistviewrowcontroller.cpp @@ -1,105 +1,105 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "kganttlistviewrowcontroller.h" #include "kganttlistviewrowcontroller_p.h" #include #include #include using namespace KGantt; /*!\class TreeViewRowController * This is an implementation of AbstractRowController that * aligns a gantt view with a QListView. Provided for * convenience for users who want to use View with QListView * instead of QTreeView. */ ListViewRowController::ListViewRowController( QListView* lv, QAbstractProxyModel* proxy ) : _d( new Private(lv,proxy) ) { } ListViewRowController::~ListViewRowController() { - delete _d; _d = 0; + delete _d; _d = nullptr; } #define d d_func() int ListViewRowController::headerHeight() const { return d->listview->viewport()->y()-d->listview->frameWidth(); } int ListViewRowController::maximumItemHeight() const { return d->listview->fontMetrics().height(); } int ListViewRowController::totalHeight() const { return d->listview->verticalScrollBar()->maximum()+d->listview->viewport()->height(); } bool ListViewRowController::isRowVisible( const QModelIndex& _idx ) const { const QModelIndex idx = d->proxy->mapToSource( _idx ); assert( idx.isValid() ? ( idx.model() == d->listview->model() ):( true ) ); return d->listview->visualRect(idx).isValid(); } bool ListViewRowController::isRowExpanded( const QModelIndex& _idx ) const { Q_UNUSED(_idx); return false; } Span ListViewRowController::rowGeometry( const QModelIndex& _idx ) const { const QModelIndex idx = d->proxy->mapToSource( _idx ); assert( idx.isValid() ? ( idx.model() == d->listview->model() ):( true ) ); QRect r = d->listview->visualRect(idx).translated( QPoint( 0, static_cast(d->listview)->verticalOffset() ) ); return Span( r.y(), r.height() ); } QModelIndex ListViewRowController::indexAt( int height ) const { return d->proxy->mapFromSource( d->listview->indexAt( QPoint( 1,height ) ) ); } QModelIndex ListViewRowController::indexAbove( const QModelIndex& _idx ) const { const QModelIndex idx = d->proxy->mapToSource( _idx ); return d->proxy->mapFromSource( idx.sibling( idx.row()-1, idx.column()) ); } QModelIndex ListViewRowController::indexBelow( const QModelIndex& _idx ) const { const QModelIndex idx = d->proxy->mapToSource( _idx ); if ( !idx.isValid() || idx.column()!=0 ) return QModelIndex(); if ( idx.model()->rowCount(idx.parent())proxy->mapFromSource( idx.sibling( idx.row()+1, idx.column()) ); } diff --git a/src/KGantt/kganttproxymodel.cpp b/src/KGantt/kganttproxymodel.cpp index 4c64598..294d1c0 100644 --- a/src/KGantt/kganttproxymodel.cpp +++ b/src/KGantt/kganttproxymodel.cpp @@ -1,200 +1,200 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "kganttproxymodel.h" #include "kganttproxymodel_p.h" using namespace KGantt; typedef ForwardingProxyModel BASE; ProxyModel::Private::Private( ProxyModel* _q ) #if 0 : calendarMode( false ) #endif { Q_UNUSED( _q ); // for now columnMap[Qt::DisplayRole] = 0; columnMap[ItemTypeRole] = 1; columnMap[StartTimeRole] = 2; columnMap[EndTimeRole] = 3; columnMap[TaskCompletionRole] = 4; columnMap[LegendRole] = 5; roleMap[Qt::DisplayRole] = Qt::DisplayRole; roleMap[ItemTypeRole] = Qt::DisplayRole; roleMap[StartTimeRole] = StartTimeRole; roleMap[EndTimeRole] = EndTimeRole; roleMap[TaskCompletionRole] = Qt::DisplayRole; roleMap[LegendRole] = Qt::DisplayRole; } ProxyModel::ProxyModel( QObject* parent ) : BASE( parent ), _d( new Private( this ) ) { init(); } ProxyModel::~ProxyModel() { - delete _d; _d = 0; + delete _d; _d = nullptr; } #define d d_func() void ProxyModel::init() { } QModelIndex ProxyModel::mapFromSource( const QModelIndex& sourceIdx ) const { #if 0 if ( sourceIdx.isValid() ) { if ( calendarMode() ) { const QAbstractItemModel* model = sourceIdx.model(); if ( model->hasChildren( sourceIdx ) ) { return BASE::mapFromSource( model->index( sourceIdx.row(),0,sourceIdx.parent())); } else { // Map children to columns return BASE::mapFromSource( model->index( sourceIdx.row(),0,sourceIdx.parent())) .child( 0, sourceIdx.column() ); } } return BASE::mapFromSource( sourceIdx.model()->index( sourceIdx.row(),0,sourceIdx.parent())); } else return QModelIndex(); #else // danders: // this was: // return BASE::mapFromSource( sourceIdx.model()?sourceIdx.model()->index( sourceIdx.row(),0,sourceIdx.parent()):QModelIndex()); // with column hardcoded to 0. // Please notify if this *really* needs to be hardcoded, afaics it makes it impossible to get at anything else // than column 0 from e.g. an item delegate. return BASE::mapFromSource( sourceIdx.model()?sourceIdx.model()->index( sourceIdx.row(),sourceIdx.column(),sourceIdx.parent()):QModelIndex()); #endif } QModelIndex ProxyModel::mapToSource( const QModelIndex& proxyIdx ) const { #if 0 if ( proxyIdx.isValid() ) { if ( calendarMode() && proxyIdx.column() > 0 ) { return BASE::mapToSource( proxyIdx.model()->index( proxyIdx.column(), 0, proxyIdx ) ); } return BASE::mapToSource( proxyIdx ); } else return QModelIndex(); #else return BASE::mapToSource( proxyIdx ); #endif } void ProxyModel::setColumn( int ganttrole, int col ) { d->columnMap[ganttrole] = col; } void ProxyModel::removeColumn( int ganttrole ) { d->columnMap.remove( ganttrole ); } int ProxyModel::column( int ganttrole ) const { return d->columnMap[ganttrole]; } void ProxyModel::removeRole( int ganttrole ) { d->roleMap.remove( ganttrole ); } void ProxyModel::setRole( int ganttrole, int role ) { d->roleMap[ganttrole] = role; } int ProxyModel::role( int ganttrole ) const { return d->roleMap[ganttrole]; } #if 0 void ProxyModel::setCalendarMode( bool enable ) { if ( d->calendarMode != enable ) { d->calendarMode = enable; reset(); } } bool ProxyModel::calendarMode() const { return d->calendarMode; } #endif int ProxyModel::rowCount( const QModelIndex& proxyIndex ) const { // TODO return BASE::rowCount( proxyIndex ); } int ProxyModel::columnCount( const QModelIndex& proxyIndex ) const { return qMin( sourceModel()->columnCount( mapToSource( proxyIndex ) ), 1 ); } QVariant ProxyModel::data( const QModelIndex& proxyIdx, int role ) const { int srole = role; int scol = proxyIdx.column(); QHash::const_iterator it = d->roleMap.find( role ); if ( it != d->roleMap.end() ) srole = *it; it = d->columnMap.find( role ); if ( it != d->columnMap.end() ) scol = *it; #if 0 qDebug() << "mapping "<(role)<<", "< " << static_cast(srole)<<", " << scol << "value=" << sourceModel()->data( sourceModel()->index( proxyIdx.row(), scol, mapToSource( proxyIdx.parent() ) ), srole ); #endif const QAbstractItemModel* model = sourceModel(); return model->data( model->index( proxyIdx.row(), scol, mapToSource( proxyIdx.parent() ) ), srole ); } bool ProxyModel::setData( const QModelIndex& proxyIdx, const QVariant& value, int role ) { int srole = role; int scol = proxyIdx.column(); QHash::const_iterator it = d->roleMap.constFind( role ); if ( it != d->roleMap.constEnd() ) srole = *it; it = d->columnMap.constFind( role ); if ( it != d->columnMap.constEnd() ) scol = *it; QAbstractItemModel* model = sourceModel(); return model->setData( model->index( proxyIdx.row(), scol, mapToSource( proxyIdx.parent() ) ), value, srole ); } #include "moc_kganttproxymodel.cpp" diff --git a/src/KGantt/kganttproxymodel.h b/src/KGantt/kganttproxymodel.h index 437b412..dceeb4e 100644 --- a/src/KGantt/kganttproxymodel.h +++ b/src/KGantt/kganttproxymodel.h @@ -1,59 +1,59 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KGANTTPROXYMODEL_H #define KGANTTPROXYMODEL_H #include "kganttforwardingproxymodel.h" namespace KGantt { class KGANTT_EXPORT ProxyModel : public ForwardingProxyModel { Q_OBJECT Q_DISABLE_COPY(ProxyModel) KGANTT_DECLARE_PRIVATE_BASE_POLYMORPHIC( ProxyModel ) public: - explicit ProxyModel( QObject* parent=0 ); + explicit ProxyModel( QObject* parent = nullptr ); virtual ~ProxyModel(); void setColumn( int ganttrole, int col ); void removeColumn( int ganttrole ); void setRole( int ganttrole, int role ); void removeRole( int ganttrole ); int column( int ganttrole ) const; int role( int ganttrole ) const; #if 0 void setCalendarMode( bool enable ); bool calendarMode() const; #endif /*reimp*/ QModelIndex mapFromSource( const QModelIndex& idx) const Q_DECL_OVERRIDE; /*reimp*/ QModelIndex mapToSource( const QModelIndex& proxyIdx ) const Q_DECL_OVERRIDE; /*reimp*/ int rowCount( const QModelIndex& idx ) const Q_DECL_OVERRIDE; /*reimp*/ int columnCount( const QModelIndex& idx ) const Q_DECL_OVERRIDE; /*reimp*/ QVariant data( const QModelIndex& idx, int role = Qt::DisplayRole ) const Q_DECL_OVERRIDE; /*reimp*/ bool setData( const QModelIndex& idx, const QVariant& value, int role=Qt::EditRole ) Q_DECL_OVERRIDE; }; } #endif /* KGANTTPROXYMODEL_H */ diff --git a/src/KGantt/kganttstyleoptionganttitem.cpp b/src/KGantt/kganttstyleoptionganttitem.cpp index b31a953..33a2315 100644 --- a/src/KGantt/kganttstyleoptionganttitem.cpp +++ b/src/KGantt/kganttstyleoptionganttitem.cpp @@ -1,111 +1,111 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "kganttstyleoptionganttitem.h" using namespace KGantt; /*!\class KGantt::StyleOptionGanttItem kganttstyleoptionganttitem.h KGanttStyleOptionGanttItem * \ingroup KGantt * \brief QStyleOption subclass for gantt items. */ typedef QStyleOptionViewItem BASE; /*! Constructor. Sets grid to 0. */ StyleOptionGanttItem::StyleOptionGanttItem() : BASE(), displayPosition( Left ), - grid( 0 ) + grid( nullptr ) { type = QStyleOption::SO_CustomBase+89; version = 1; } /*! Copy constructor. Creates a copy of \a other */ StyleOptionGanttItem::StyleOptionGanttItem( const StyleOptionGanttItem& other ) : BASE(other) { operator=( other ); } /*! Assignment operator */ StyleOptionGanttItem& StyleOptionGanttItem::operator=( const StyleOptionGanttItem& other ) { BASE::operator=( other ); boundingRect = other.boundingRect; itemRect = other.itemRect; displayPosition = other.displayPosition; grid = other.grid; text = other.text; return *this; } #ifndef QT_NO_DEBUG_STREAM QDebug operator<<( QDebug dbg, KGantt::StyleOptionGanttItem::Position p) { switch ( p ) { case KGantt::StyleOptionGanttItem::Left: dbg << "KGantt::StyleOptionGanttItem::Left"; break; case KGantt::StyleOptionGanttItem::Right: dbg << "KGantt::StyleOptionGanttItem::Right"; break; case KGantt::StyleOptionGanttItem::Center: dbg << "KGantt::StyleOptionGanttItem::Center"; break; case KGantt::StyleOptionGanttItem::Hidden: dbg << "KGantt::StyleOptionGanttItem::Hidden"; break; default: dbg << static_cast( p ); } return dbg; } QDebug operator<<( QDebug dbg, const KGantt::StyleOptionGanttItem& s ) { dbg << "KGantt::StyleOptionGanttItem[ boundingRect="<. */ #ifndef KGANTTSUMMARYHANDLINGPROXYMODEL_H #define KGANTTSUMMARYHANDLINGPROXYMODEL_H #include "kganttforwardingproxymodel.h" namespace KGantt { class KGANTT_EXPORT SummaryHandlingProxyModel : public ForwardingProxyModel { Q_OBJECT KGANTT_DECLARE_PRIVATE_BASE_POLYMORPHIC( SummaryHandlingProxyModel ) public: - explicit SummaryHandlingProxyModel( QObject* parent=0 ); + explicit SummaryHandlingProxyModel( QObject* parent = nullptr ); virtual ~SummaryHandlingProxyModel(); /*reimp*/ void setSourceModel( QAbstractItemModel* model ) Q_DECL_OVERRIDE; /*reimp*/ QVariant data( const QModelIndex& proxyIndex, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; /*reimp*/ bool setData( const QModelIndex& index, const QVariant& value, int role = Qt::EditRole ) Q_DECL_OVERRIDE; /*reimp*/ Qt::ItemFlags flags( const QModelIndex& idx ) const Q_DECL_OVERRIDE; protected: /*reimp*/ void sourceModelReset() Q_DECL_OVERRIDE; /*reimp*/ void sourceLayoutChanged() Q_DECL_OVERRIDE; /*reimp*/ void sourceDataChanged( const QModelIndex& from, const QModelIndex& to ) Q_DECL_OVERRIDE; /*reimp*/ void sourceColumnsAboutToBeInserted( const QModelIndex& idx, int start, int end ) Q_DECL_OVERRIDE; /*reimp*/ void sourceColumnsAboutToBeRemoved( const QModelIndex& idx, int start, int end ) Q_DECL_OVERRIDE; /*reimp*/ void sourceRowsAboutToBeInserted( const QModelIndex& idx, int start, int end ) Q_DECL_OVERRIDE; /*reimp*/ void sourceRowsAboutToBeRemoved( const QModelIndex&, int start, int end ) Q_DECL_OVERRIDE; }; } #endif /* KGANTTSUMMARYHANDLINGPROXYMODEL_H */ diff --git a/src/KGantt/kgantttreeviewrowcontroller.cpp b/src/KGantt/kgantttreeviewrowcontroller.cpp index 7f19e98..10bc463 100644 --- a/src/KGantt/kgantttreeviewrowcontroller.cpp +++ b/src/KGantt/kgantttreeviewrowcontroller.cpp @@ -1,120 +1,120 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "kgantttreeviewrowcontroller.h" #include "kgantttreeviewrowcontroller_p.h" #include #include #include #include using namespace KGantt; /*!\class TreeViewRowController * This is an implementation of AbstractRowController that * aligns a gantt view with a QTreeView. */ TreeViewRowController::TreeViewRowController( QTreeView* tv, QAbstractProxyModel* proxy ) : _d( new Private ) { _d->treeview = static_cast(tv); _d->proxy = proxy; } TreeViewRowController::~TreeViewRowController() { - delete _d; _d=0; + delete _d; _d = nullptr; } #define d d_func() int TreeViewRowController::headerHeight() const { //return d->treeview->header()->sizeHint().height(); return d->treeview->viewport()->y()-d->treeview->frameWidth(); } int TreeViewRowController::maximumItemHeight() const { return d->treeview->fontMetrics().height(); } int TreeViewRowController::totalHeight() const { return d->treeview->verticalScrollBar()->maximum()+d->treeview->viewport()->height(); } bool TreeViewRowController::isRowVisible( const QModelIndex& _idx ) const { //qDebug() << _idx.model()<proxy << d->treeview->model(); const QModelIndex idx = d->proxy->mapToSource( _idx ); assert( idx.isValid() ? ( idx.model() == d->treeview->model() ):( true ) ); return d->treeview->visualRect(idx).isValid(); } bool TreeViewRowController::isRowExpanded( const QModelIndex& _idx ) const { const QModelIndex idx = d->proxy->mapToSource( _idx ); assert( idx.isValid() ? ( idx.model() == d->treeview->model() ):( true ) ); return d->treeview->isExpanded( idx ); } Span TreeViewRowController::rowGeometry( const QModelIndex& _idx ) const { const QModelIndex idx = d->proxy->mapToSource( _idx ); assert( idx.isValid() ? ( idx.model() == d->treeview->model() ):( true ) ); QRect r = d->treeview->visualRect(idx).translated( QPoint( 0, d->treeview->verticalOffset() ) ); return Span( r.y(), r.height() ); } QModelIndex TreeViewRowController::indexAt( int height ) const { /* using indexAt( QPoint ) wont work here, since it does hit detection * against the actual item text/icon, so we would return wrong values * for items with no text etc. * * The code below could cache for performance, but currently it doesn't * seem to be the performance bottleneck at all. */ if ( !d->treeview->model() ) return QModelIndex(); int y = d->treeview->verticalOffset(); QModelIndex idx = d->treeview->model()->index( 0, 0, d->treeview->rootIndex() ); do { if ( y >= height ) break; y += d->treeview->rowHeight( idx ); idx = d->treeview->indexBelow( idx ); } while ( idx.isValid() ); return d->proxy->mapFromSource( idx ); } QModelIndex TreeViewRowController::indexAbove( const QModelIndex& _idx ) const { const QModelIndex idx = d->proxy->mapToSource( _idx ); return d->proxy->mapFromSource( d->treeview->indexAbove( idx ) ); } QModelIndex TreeViewRowController::indexBelow( const QModelIndex& _idx ) const { const QModelIndex idx = d->proxy->mapToSource( _idx ); return d->proxy->mapFromSource( d->treeview->indexBelow( idx ) ); } diff --git a/src/KGantt/kganttview.cpp b/src/KGantt/kganttview.cpp index c9edab4..f84e2d3 100644 --- a/src/KGantt/kganttview.cpp +++ b/src/KGantt/kganttview.cpp @@ -1,665 +1,665 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "kganttview.h" #include "kganttview_p.h" #include "kganttitemdelegate.h" #include "kganttgraphicsitem.h" #include "kganttsummaryhandlingproxymodel.h" #include #include #include #include #include #include #include #include #include #if defined KDAB_EVAL #include "../evaldialog/evaldialog.h" #endif using namespace KGantt; namespace { class HeaderView : public QHeaderView { public: - explicit HeaderView( QWidget* parent=0 ) : QHeaderView( Qt::Horizontal, parent ) { + explicit HeaderView( QWidget* parent=nullptr ) : QHeaderView( Qt::Horizontal, parent ) { } QSize sizeHint() const Q_DECL_OVERRIDE { QSize s = QHeaderView::sizeHint(); s.rheight() *= 2; return s; } }; } KGanttTreeView::KGanttTreeView( QAbstractProxyModel* proxy, QWidget* parent ) : QTreeView( parent ), m_controller( this, proxy ) { setHeader( new HeaderView ); } KGanttTreeView::~KGanttTreeView() { } void KGanttTreeView::expandAll(QModelIndex index) { for (int i = 0; i < model()->rowCount(index); i++) { QModelIndex indexAt = model()->index(i, 0, index); if (model()->hasChildren(indexAt)) expandAll(indexAt); if (isExpanded(indexAt)) continue; expand(indexAt); } } void KGanttTreeView::collapseAll(QModelIndex index) { for (int i = 0; i < model()->rowCount(index); i++) { QModelIndex indexAt = model()->index(i, 0, index); if (model()->hasChildren(indexAt)) collapseAll(indexAt); if (!isExpanded(indexAt)) continue; collapse(indexAt); } } View::Private::Private(View* v) : q(v), splitter(v), - rowController(0), + rowController(nullptr), gfxview( new GraphicsView( &splitter ) ), - model(0) + model(nullptr) { //init(); } View::Private::~Private() { delete gfxview; } void View::Private::init() { KGanttTreeView* tw = new KGanttTreeView( &ganttProxyModel, &splitter ); tw->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); tw->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); q->setLeftView( tw ); q->setRowController( tw->rowController() ); //gfxview.setRenderHints( QPainter::Antialiasing ); tw->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); QVBoxLayout* layout = new QVBoxLayout(q); layout->setMargin(0); layout->addWidget(&splitter); q->setLayout(layout); constraintProxy.setProxyModel( &ganttProxyModel ); constraintProxy.setDestinationModel( &mappedConstraintModel ); setupGraphicsView(); } void View::Private::setupGraphicsView() { gfxview->setParent( &splitter ); gfxview->setAlignment(Qt::AlignTop|Qt::AlignLeft); gfxview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); gfxview->setSelectionModel( leftWidget->selectionModel() ); gfxview->setConstraintModel( &mappedConstraintModel ); q->setLeftView( leftWidget ); q->setRowController( rowController ); updateScene(); } void View::Private::updateScene() { gfxview->clearItems(); if ( !model) return; if ( QTreeView* tw = qobject_cast(leftWidget)) { QModelIndex idx = ganttProxyModel.mapFromSource( model->index( 0, 0, leftWidget->rootIndex() ) ); do { gfxview->updateRow( idx ); } while ( ( idx = tw->indexBelow( idx ) ) != QModelIndex() && gfxview->rowController()->isRowVisible(idx) ); gfxview->updateSceneRect(); } else { const QModelIndex rootidx = ganttProxyModel.mapFromSource( leftWidget->rootIndex() ); for ( int r = 0; r < ganttProxyModel.rowCount(rootidx); ++r ) { gfxview->updateRow( ganttProxyModel.index( r, 0, rootidx ) ); } } } void View::Private::slotCollapsed(const QModelIndex& _idx) { QTreeView* tw = qobject_cast(leftWidget); if (!tw) return; bool blocked = gfxview->blockSignals( true ); QModelIndex idx( _idx ); const QAbstractItemModel* model = leftWidget->model(); const QModelIndex pidx = ganttProxyModel.mapFromSource(idx); bool isMulti = false; for ( QModelIndex treewalkidx = pidx; treewalkidx.isValid(); treewalkidx = treewalkidx.parent() ) { if ( treewalkidx.data( ItemTypeRole ).toInt() == TypeMulti && !gfxview->rowController()->isRowExpanded( treewalkidx ) ) { isMulti = true; break; } } if ( !isMulti ) { for ( int i = 0; i < model->rowCount( idx ); ++i ) { gfxview->deleteSubtree( ganttProxyModel.index( i, 0, pidx ) ); } } else { gfxview->updateRow(pidx); } //qDebug() << "Looking to update from " << idx; while ( ( idx=tw->indexBelow( idx ) ) != QModelIndex() && gfxview->rowController()->isRowVisible( ganttProxyModel.mapFromSource(idx) ) ) { const QModelIndex proxyidx( ganttProxyModel.mapFromSource( idx ) ); gfxview->updateRow(proxyidx); } gfxview->blockSignals( blocked ); gfxview->updateSceneRect(); } void View::Private::slotExpanded(const QModelIndex& _idx) { QModelIndex idx( ganttProxyModel.mapFromSource( _idx ) ); do { //qDebug() << "Updating row" << idx << idx.data( Qt::DisplayRole ).toString(); gfxview->updateRow(idx); } while ( ( idx=gfxview->rowController()->indexBelow( idx ) ) != QModelIndex() && gfxview->rowController()->isRowVisible( idx ) ); gfxview->updateSceneRect(); } void View::Private::slotVerticalScrollValueChanged( int val ) { #if 0 qDebug() << "View::Private::slotVerticalScrollValueChanged("<verticalScrollBar()->singleStep(); #endif leftWidget->verticalScrollBar()->setValue( val/gfxview->verticalScrollBar()->singleStep() ); } void View::Private::slotLeftWidgetVerticalRangeChanged(int min, int max ) { //qDebug() << "View::Private::slotLeftWidgetVerticalRangeChanged("<verticalScrollBar()->setRange( min, max ); gfxview->updateSceneRect(); } } void View::Private::slotGfxViewVerticalRangeChanged( int min, int max ) { //qDebug() << "View::Private::slotGfxViewVerticalRangeChanged("<verticalScrollBar()->minimum(); int leftMax = leftWidget->verticalScrollBar()->maximum(); bool blocked = gfxview->verticalScrollBar()->blockSignals( true ); gfxview->verticalScrollBar()->setRange( qMax( min, leftMin ), qMax( max, leftMax ) ); gfxview->verticalScrollBar()->blockSignals( blocked ); } } /*!\class KGantt::View kganttview.h KGanttView * \ingroup KGantt * \brief This widget that consists of a QTreeView and a GraphicsView * * This is the easy to use, complete gantt chart widget. It * consists of a QTreeView on the left and a KGantt::GraphicsView * on the right separated by a QSplitter. The two views share the same * model. */ /*! Constructor. Creates a View with parent \a parent, * a DateTimeGrid as default grid implementaion and no model etc. */ View::View(QWidget* parent) : QWidget(parent), _d(new Private(this)) { #if defined KDAB_EVAL EvalDialog::checkEvalLicense( "KD Gantt" ); #endif _d->init(); } View::~View() { delete _d; } #define d d_func() /*! Replaces the left widget with a custom QAbstractItemView. * * \param aiv The view to be used to the left, instead of the default tree view * \sa setRowController() */ void View::setLeftView( QAbstractItemView* aiv ) { assert( aiv ); if ( aiv==d->leftWidget ) return; if ( !d->leftWidget.isNull() ) { d->leftWidget->disconnect( this ); d->leftWidget->hide(); d->leftWidget->verticalScrollBar()->disconnect( d->gfxview->verticalScrollBar() ); d->gfxview->verticalScrollBar()->disconnect( d->leftWidget->verticalScrollBar() ); } d->leftWidget = aiv; d->splitter.insertWidget( 0, d->leftWidget ); if ( qobject_cast(d->leftWidget) ) { connect( d->leftWidget, SIGNAL(collapsed(QModelIndex)), this, SLOT(slotCollapsed(QModelIndex)) ); connect( d->leftWidget, SIGNAL(expanded(QModelIndex)), this, SLOT(slotExpanded(QModelIndex)) ); } connect( d->gfxview->verticalScrollBar(), SIGNAL(valueChanged(int)), d->leftWidget->verticalScrollBar(), SLOT(setValue(int)) ); connect( d->leftWidget->verticalScrollBar(), SIGNAL(valueChanged(int)), d->gfxview->verticalScrollBar(), SLOT(setValue(int)) ); connect( d->leftWidget->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(slotLeftWidgetVerticalRangeChanged(int,int)) ); connect( d->gfxview->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(slotGfxViewVerticalRangeChanged(int,int)) ); } /*! Sets \a ctrl to be the rowcontroller used by this View. * The default rowcontroller is owned by KGantt::View and is * suitable for the default treeview in the left part of the view. * You probably only want to change this if you replace the treeview. */ void View::setRowController( AbstractRowController* ctrl ) { if ( ctrl == d->rowController && d->gfxview->rowController() == ctrl ) return; d->rowController = ctrl; d->gfxview->setRowController( d->rowController ); } /*! \returns a pointer to the current rowcontroller. * \see AbstractRowController */ AbstractRowController* View::rowController() { return d->rowController; } /*! \overload AbstractRowController* KGantt::View::rowController() */ const AbstractRowController* View::rowController() const { return d->rowController; } /*! * \returns a pointer to the QAbstractItemView in the left * part of the widget. * */ const QAbstractItemView* View::leftView() const { return d->leftWidget; } /*! * \overload const QAbstractItemView* KGantt::View::leftView() const */ QAbstractItemView* View::leftView() { return d->leftWidget; } /*! Set the GraphicsView to be used for this View. It only makes sense to call this * if you need to subclass GraphicsView. * * NOTE: _Only_ call this right after creating the View, before setting a model or any other * attributes. */ void View::setGraphicsView( GraphicsView* gv ) { if ( gv != d->gfxview ) { GraphicsView* old = d->gfxview; d->gfxview = gv; d->gfxview->setModel(old->model()); // use the old ForwardingProxyModel d->setupGraphicsView(); d->gfxview->setGrid( old->grid() ); delete old; } } /*! * \returns a pointer to the GraphicsView */ const GraphicsView* View::graphicsView() const { return d->gfxview; } /*! * \overload const GraphicsView* KGantt::View::graphicsView() const */ GraphicsView* View::graphicsView() { return d->gfxview; } /*! * \returns a pointer to the QSplitter that manages the left view and graphicsView */ const QSplitter* View::splitter() const { return &d->splitter; } /*! * \overload const QSplitter* KGantt::View::splitter() const */ QSplitter* View::splitter() { return &d->splitter; } /*! \returns the current model displayed by this view */ QAbstractItemModel* View::model() const { return leftView()->model(); } /*! Sets the QAbstractItemModel to be displayed in this view * to \a model. * * \see GraphicsView::setModel */ void View::setModel( QAbstractItemModel* model ) { leftView()->setModel( model ); d->ganttProxyModel.setSourceModel( model ); d->gfxview->setModel( &d->ganttProxyModel ); } /*! \returns the QItemSelectionModel used by this view */ QItemSelectionModel* View::selectionModel() const { return leftView()->selectionModel(); } /*! Sets the QItemSelectionModel used by this view to manage * selections. Similar to QAbstractItemView::setSelectionModel */ void View::setSelectionModel( QItemSelectionModel* smodel ) { leftView()->setSelectionModel( smodel ); d->gfxview->setSelectionModel( new QItemSelectionModel( &( d->ganttProxyModel ),this ) ); } /*! Sets the AbstractGrid for this view. The grid is an * object that controls how QModelIndexes are mapped * to and from the view and how the background and header * is rendered. \see AbstractGrid and DateTimeGrid. */ void View::setGrid( AbstractGrid* grid ) { d->gfxview->setGrid( grid ); } void View::expandAll( QModelIndex index ) { // FIXME: // It is legal to call setLeftView() with any QAbstractItemView, // so expandAll should be reimplemented to work with that. KGanttTreeView* tw = qobject_cast(leftView()); if (tw) { tw->expandAll(index); } } void View::collapseAll( QModelIndex index ) { // FIXME: // It is legal to call setLeftView() with any QAbstractItemView, // so expandAll should be reimplemented to work with that. KGanttTreeView* tw = qobject_cast(leftView()); if (tw) { tw->collapseAll(index); } } /*! \returns the AbstractGrid used by this view. */ AbstractGrid* View::grid() const { return d->gfxview->grid(); } /*! \returns the rootindex for this view. */ QModelIndex View::rootIndex() const { return leftView()->rootIndex(); } /*! Sets the root index of the model displayed by this view. * Similar to QAbstractItemView::setRootIndex, default is QModelIndex(). */ void View::setRootIndex( const QModelIndex& idx ) { leftView()->setRootIndex( idx ); d->gfxview->setRootIndex( idx ); } /*! \returns the ItemDelegate used by this view to render items */ ItemDelegate* View::itemDelegate() const { return d->gfxview->itemDelegate(); } /*! Sets the KGantt::ItemDelegate used for rendering items on this * view. \see ItemDelegate and QAbstractItemDelegate. */ void View::setItemDelegate( ItemDelegate* delegate ) { leftView()->setItemDelegate( delegate ); d->gfxview->setItemDelegate( delegate ); } /*! Sets the constraintmodel displayed by this view. * \see KGantt::ConstraintModel. */ void View::setConstraintModel( ConstraintModel* cm ) { d->constraintProxy.setSourceModel( cm ); d->gfxview->setConstraintModel( &d->mappedConstraintModel ); } /*! \returns the KGantt::ConstraintModel displayed by this view. */ ConstraintModel* View::constraintModel() const { return d->constraintProxy.sourceModel(); } const QAbstractProxyModel* View::ganttProxyModel() const { return &( d->ganttProxyModel ); } QAbstractProxyModel* View::ganttProxyModel() { return &( d->ganttProxyModel ); } void View::ensureVisible(const QModelIndex& index) { QGraphicsView* view = graphicsView(); KGantt::GraphicsScene* scene = static_cast(view->scene()); if (!scene) return; KGantt::SummaryHandlingProxyModel* model = static_cast(scene->summaryHandlingModel()); const QModelIndex pidx = d->ganttProxyModel.mapFromSource(index); const QModelIndex idx = model->mapFromSource( pidx ); QGraphicsItem* item = scene->findItem(idx); view->ensureVisible(item); } void View::resizeEvent(QResizeEvent*ev) { QWidget::resizeEvent(ev); } /*!\returns The QModelIndex for the item located at * position \a pos in the view or an invalid index * if no item was present at that position. * * \see GraphicsView::indexAt */ QModelIndex View::indexAt( const QPoint& pos ) const { return d->gfxview->indexAt( pos ); } /*! Print the Gantt chart using \a printer. If \a drawRowLabels * is true (the default), each row will have it's label printed * on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. * * This version of print() will print multiple pages. */ void View::print( QPrinter* printer, bool drawRowLabels, bool drawColumnLabels ) { graphicsView()->print( printer, drawRowLabels, drawColumnLabels ); } /*! Print part of the Gantt chart from \a start to \a end using \a printer. * If \a drawRowLabels is true (the default), each row will have it's * label printed on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. * * This version of print() will print multiple pages. * * To print a certain range of a chart with a DateTimeGrid, use * qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const * to figure out the values for \a start and \a end. */ void View::print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels ) { graphicsView()->print( printer, start, end, drawRowLabels, drawColumnLabels ); } /*! Render the GanttView inside the rectangle \a target using the painter \a painter. * If \a drawRowLabels is true (the default), each row will have it's * label printed on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. */ void View::print( QPainter* painter, const QRectF& target, bool drawRowLabels, bool drawColumnLabels) { d->gfxview->print( painter, target, drawRowLabels, drawColumnLabels); } /*! Render the GanttView inside the rectangle \a target using the painter \a painter. * If \a drawRowLabels is true (the default), each row will have it's * label printed on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. * * To print a certain range of a chart with a DateTimeGrid, use * qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const * to figure out the values for \a start and \a end. */ void View::print( QPainter* painter, qreal start, qreal end, const QRectF& target, bool drawRowLabels, bool drawColumnLabels) { d->gfxview->print( painter, start, end, target, drawRowLabels, drawColumnLabels); } #include "moc_kganttview.cpp" #ifndef KDAB_NO_UNIT_TESTS #include "unittest/test.h" #include "kganttlistviewrowcontroller.h" #include #include #include #include KDAB_SCOPED_UNITTEST_SIMPLE( KGantt, View, "test" ) { - View view( 0 ); + View view( nullptr ); #if 0 // GUI tests do not work well on the server QTimer::singleShot( 1000, qApp, SLOT(quit()) ); view.show(); qApp->exec(); QPixmap screenshot1 = QPixmap::grabWidget( &view ); QTreeView* tv = new QTreeView; view.setLeftView( tv ); view.setRowController( new TreeViewRowController(tv,view.ganttProxyModel()) ); QTimer::singleShot( 1000, qApp, SLOT(quit()) ); qApp->exec(); QPixmap screenshot2 = QPixmap::grabWidget( &view ); assertEqual( screenshot1.toImage(), screenshot2.toImage() ); QListView* lv = new QListView; view.setLeftView(lv); view.setRowController( new ListViewRowController(lv,view.ganttProxyModel())); view.show(); QTimer::singleShot( 1000, qApp, SLOT(quit()) ); qApp->exec(); #endif } #endif /* KDAB_NO_UNIT_TESTS */ diff --git a/src/KGantt/kganttview.h b/src/KGantt/kganttview.h index f0bfd27..fc7ec7d 100644 --- a/src/KGantt/kganttview.h +++ b/src/KGantt/kganttview.h @@ -1,107 +1,107 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KGANTTVIEW_H #define KGANTTVIEW_H #include #include #include "kganttglobal.h" QT_BEGIN_NAMESPACE class QAbstractItemModel; class QAbstractProxyModel; class QAbstractItemView; class QItemSelectionModel; class QPrinter; class QSplitter; QT_END_NAMESPACE namespace KGantt { class ItemDelegate; class Constraint; class ConstraintModel; class AbstractGrid; class GraphicsView; class AbstractRowController; class KGANTT_EXPORT View : public QWidget { Q_OBJECT KGANTT_DECLARE_PRIVATE_BASE_POLYMORPHIC_QWIDGET(View) Q_PRIVATE_SLOT( d, void slotCollapsed(const QModelIndex&) ) Q_PRIVATE_SLOT( d, void slotExpanded(const QModelIndex&) ) Q_PRIVATE_SLOT( d, void slotVerticalScrollValueChanged( int ) ) Q_PRIVATE_SLOT( d, void slotLeftWidgetVerticalRangeChanged( int, int ) ) Q_PRIVATE_SLOT( d, void slotGfxViewVerticalRangeChanged( int, int ) ) public: - explicit View(QWidget* parent=0); + explicit View(QWidget* parent = nullptr); virtual ~View(); QAbstractItemModel* model() const; QItemSelectionModel* selectionModel() const; ItemDelegate* itemDelegate() const; ConstraintModel* constraintModel() const; AbstractGrid* grid() const; QModelIndex rootIndex() const; QModelIndex indexAt( const QPoint& pos ) const; void setLeftView( QAbstractItemView* ); const QAbstractItemView* leftView() const; QAbstractItemView* leftView(); const QSplitter* splitter() const; QSplitter* splitter(); void setRowController( AbstractRowController* ); AbstractRowController* rowController(); const AbstractRowController* rowController() const; void setGraphicsView( GraphicsView* ); const GraphicsView* graphicsView() const; GraphicsView* graphicsView(); const QAbstractProxyModel* ganttProxyModel() const; QAbstractProxyModel* ganttProxyModel(); void ensureVisible(const QModelIndex& index); void print( QPrinter* printer, bool drawRowLabels=true, bool drawColumnLabels=true ); void print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels=true, bool drawColumnLabels=true ); void print( QPainter* painter, const QRectF& target = QRectF(), bool drawRowLabels=true, bool drawColumnLabels=true); void print( QPainter* painter, qreal start, qreal end, const QRectF& target = QRectF(), bool drawRowLabels=true, bool drawColumnLabels=true); public Q_SLOTS: void setModel(QAbstractItemModel* model); void setRootIndex( const QModelIndex& idx ); void setSelectionModel( QItemSelectionModel* smodel ); void setItemDelegate( ItemDelegate* ); void setConstraintModel( ConstraintModel* ); void setGrid( AbstractGrid* ); void expandAll( QModelIndex index = QModelIndex() ); void collapseAll( QModelIndex index = QModelIndex() ); protected: /*reimp*/ void resizeEvent(QResizeEvent*) Q_DECL_OVERRIDE; }; } #endif /* KGANTTVIEW_H */ diff --git a/src/KGantt/kganttview_p.h b/src/KGantt/kganttview_p.h index 0d22529..9302412 100644 --- a/src/KGantt/kganttview_p.h +++ b/src/KGantt/kganttview_p.h @@ -1,109 +1,109 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KGANTTVIEW_P_H #define KGANTTVIEW_P_H #include "kganttview.h" #include "kganttgraphicsscene.h" #include "kgantttreeviewrowcontroller.h" #include "kganttconstraintmodel.h" #include "kganttconstraintproxy.h" #include "kganttgraphicsview.h" #include "kganttdatetimegrid.h" #include "kganttproxymodel.h" #include #include #include #include #include QT_BEGIN_NAMESPACE class QAbstractProxyModel; class QGraphicsView; class QGraphicsScene; class QGraphicsRectItem; QT_END_NAMESPACE namespace KGantt { class GraphicsItem; class KGanttTreeView; /*! \internal */ class KGanttTreeView : public QTreeView { Q_OBJECT public: - explicit KGanttTreeView( QAbstractProxyModel* proxy, QWidget* parent=0 ); + explicit KGanttTreeView( QAbstractProxyModel* proxy, QWidget* parent = nullptr ); virtual ~KGanttTreeView(); AbstractRowController* rowController() { return &m_controller; } public Q_SLOTS: void expandAll(QModelIndex index = QModelIndex()); void collapseAll(QModelIndex index = QModelIndex()); private: TreeViewRowController m_controller; }; /*! \internal */ class Q_DECL_HIDDEN View::Private { public: explicit Private(View*); virtual ~Private(); void init(); GraphicsItem* createItem( ItemType type ) const; void updateScene(); void setupGraphicsView(); // slots void slotCollapsed(const QModelIndex&); void slotExpanded(const QModelIndex&); void slotVerticalScrollValueChanged( int ); void slotLeftWidgetVerticalRangeChanged( int, int ); void slotGfxViewVerticalRangeChanged( int, int ); View* q; QSplitter splitter; /* TODO: Refine/subclass */ //KGanttTreeView treeview; QPointer leftWidget; AbstractRowController* rowController; QPointer gfxview; //KGanttHeaderWidget headerwidget; QPointer model; ProxyModel ganttProxyModel; //KGanttTreeViewRowController rowController; ConstraintModel mappedConstraintModel; ConstraintProxy constraintProxy; }; } #endif /* KGANTTVIEW_P_H */ diff --git a/src/KGantt/test/TestKGanttView.cpp b/src/KGantt/test/TestKGanttView.cpp index 8110654..c6f97fe 100644 --- a/src/KGantt/test/TestKGanttView.cpp +++ b/src/KGantt/test/TestKGanttView.cpp @@ -1,340 +1,340 @@ /* * Copyright (C) 2018 Dag Andersen * * This file is part of the KGantt library. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #undef QT_NO_CAST_FROM_ASCII #include "TestKGanttView.h" #include "kganttglobal.h" #include "kganttgraphicsview.h" #include "kganttgraphicsscene.h" #include "kganttgraphicsitem.h" #include "kganttconstraintmodel.h" #include "kgantttreeviewrowcontroller.h" #include "kganttlistviewrowcontroller.h" #include "kganttforwardingproxymodel.h" #include "kganttitemdelegate.h" #include "kganttdatetimegrid.h" #include #include using namespace KGantt; void TestKGanttView::init() { view = new KGantt::View(); itemModel = new QStandardItemModel(); view->setModel(itemModel); view->setConstraintModel(new KGantt::ConstraintModel()); QCOMPARE(view->model(), itemModel); QCOMPARE(itemModel->rowCount(), 0); } void TestKGanttView::cleanup() { delete view; delete itemModel; } void TestKGanttView::testApi() { // this sould never change KGantt::GraphicsScene *scene = qobject_cast(view->graphicsView()->scene()); QVERIFY(scene); QTreeView *treeview = new QTreeView(); QVERIFY(view->leftView() != treeview); view->setLeftView(treeview); QVERIFY(view->leftView() == treeview); QStandardItemModel *model = new QStandardItemModel(); QVERIFY(view->model() != model); view->setModel(model); QVERIFY(view->model() == model); QVERIFY(view->leftView()->model() == model); QEXPECT_FAIL("", "model() returns different model than set with setModel()! This should be changed in next major release", Continue); QVERIFY(view->graphicsView()->model() == model); KGantt::ForwardingProxyModel *fpm = qobject_cast(view->graphicsView()->model()); QVERIFY(fpm); QVERIFY(fpm->sourceModel() == model); QVERIFY(view->ganttProxyModel()->sourceModel() == model); QVERIFY(view->ganttProxyModel() == fpm); QItemSelectionModel *smodel = new QItemSelectionModel(model); QVERIFY(view->selectionModel() != smodel); view->setSelectionModel(smodel); QVERIFY(view->selectionModel() == smodel); QVERIFY(view->leftView()->selectionModel() == smodel); QVERIFY(view->graphicsView()->selectionModel() != smodel); // grapgicsView() gets its own selection model KGantt::ItemDelegate *delegate = new KGantt::ItemDelegate(); QVERIFY(view->itemDelegate() != delegate); view->setItemDelegate(delegate); QVERIFY(view->itemDelegate() == delegate); QVERIFY(view->graphicsView()->itemDelegate() == delegate); QVERIFY(scene->itemDelegate() == delegate); KGantt::ConstraintModel *cmodel = new KGantt::ConstraintModel(); QVERIFY(view->constraintModel() != cmodel); view->setConstraintModel(cmodel); QVERIFY(view->constraintModel() == cmodel); // grapgicsView and graphicsScene returns a different model // (it is an internal model handled by the view) QVERIFY(view->graphicsView()->constraintModel() != cmodel); QVERIFY(scene->constraintModel() != cmodel); QVERIFY(view->graphicsView()->constraintModel() == scene->constraintModel()); KGantt::TreeViewRowController *tvr = new KGantt::TreeViewRowController(treeview, view->ganttProxyModel()); QVERIFY(view->rowController() != tvr); view->setRowController(tvr); QVERIFY(view->rowController() == tvr); QVERIFY(view->graphicsView()->rowController() == tvr); KGantt::DateTimeGrid *grid = new KGantt::DateTimeGrid(); QVERIFY(view->grid() != grid); view->setGrid(grid); QVERIFY(view->grid() == grid); QVERIFY(view->graphicsView()->grid() == grid); QVERIFY(scene->grid() == grid); // TODO: rootIndex KGantt::ConstraintModel *old_cmodel = view->graphicsView()->constraintModel(); KGantt::GraphicsView *gv = new KGantt::GraphicsView(); QVERIFY(view->graphicsView() != gv); view->setGraphicsView(gv); QVERIFY(view->graphicsView() == gv); - QVERIFY(view->graphicsView()->parent() != 0); + QVERIFY(view->graphicsView()->parent() != nullptr); QVERIFY(view->graphicsView()->rowController() == tvr); KGantt::ForwardingProxyModel *new_fpm = qobject_cast(view->graphicsView()->model()); - QVERIFY(new_fpm != 0); + QVERIFY(new_fpm != nullptr); QVERIFY(view->graphicsView()->model() == new_fpm); QVERIFY(new_fpm->sourceModel() == model); QVERIFY(view->ganttProxyModel()->sourceModel() == model); QVERIFY(view->ganttProxyModel() == new_fpm); QVERIFY(view->graphicsView()->constraintModel() == old_cmodel); - QVERIFY(treeview->parent() != 0); // QSplitter takes ownership + QVERIFY(treeview->parent() != nullptr); // QSplitter takes ownership - QVERIFY(model->parent() == 0); // nobody takes ownership + QVERIFY(model->parent() == nullptr); // nobody takes ownership model->deleteLater(); - QVERIFY(smodel->parent() != 0); // item model takes ownership + QVERIFY(smodel->parent() != nullptr); // item model takes ownership - QVERIFY(delegate->parent() == 0); // nobody takes ownership + QVERIFY(delegate->parent() == nullptr); // nobody takes ownership delegate->deleteLater(); - QVERIFY(cmodel->parent() == 0); // nobody takes ownership + QVERIFY(cmodel->parent() == nullptr); // nobody takes ownership cmodel->deleteLater(); delete tvr; // does not have a parent delete grid; // does not have a parent } void TestKGanttView::initTreeModel() { if (itemModel->columnCount() == 0) { itemModel->setHorizontalHeaderLabels(QStringList()<< "Title"<<"Type"<<"Start"<<"End"); } QList items; QStandardItem *sum = new QStandardItem("Summary 1"); items << sum; QStandardItem *item = new QStandardItem(QString::number((int)KGantt::TypeSummary)); items << item; itemModel->appendRow(items); items.clear(); item = new QStandardItem("T1"); items << item; item = new QStandardItem(QString::number((int)KGantt::TypeTask)); items << item; QDateTime now = QDateTime::currentDateTime(); item = new QStandardItem(now.toString()); items << item; item = new QStandardItem(now.addDays(1).toString()); items << item; sum->appendRow(items); items.clear(); item = new QStandardItem("T2"); items << item; item = new QStandardItem(QString::number((int)KGantt::TypeTask)); items << item; item = new QStandardItem(now.addDays(1).toString()); items << item; item = new QStandardItem(now.addDays(2).toString()); items << item; sum->appendRow(items); } void TestKGanttView::testDefaultView() { initTreeModel(); QCOMPARE(itemModel->rowCount(), 1); QCOMPARE(view->graphicsView()->scene()->items().count(), 1); view->expandAll(); QCOMPARE(view->graphicsView()->scene()->items().count(), 3); view->collapseAll(); QCOMPARE(view->graphicsView()->scene()->items().count(), 1); } void TestKGanttView::testTreeView() { QTreeView *treeview = new QTreeView(view); view->setLeftView(treeview); view->setModel(itemModel); // must be set again initTreeModel(); QCOMPARE(itemModel->rowCount(), 1); QCOMPARE(view->graphicsView()->scene()->items().count(), 1); view->expandAll(); QEXPECT_FAIL("", "expandAll() only works with the default treeview", Continue); QCOMPARE(view->graphicsView()->scene()->items().count(), 3); view->collapseAll(); QCOMPARE(view->graphicsView()->scene()->items().count(), 1); treeview->expandAll(); QCOMPARE(view->graphicsView()->scene()->items().count(), 3); treeview->collapseAll(); QCOMPARE(view->graphicsView()->scene()->items().count(), 1); } void TestKGanttView::initListModel() { QList items; QStandardItem *item = new QStandardItem("T1"); items << item; item = new QStandardItem(QString::number((int)KGantt::TypeTask)); items << item; QDateTime now = QDateTime::currentDateTime(); item = new QStandardItem(now.toString()); items << item; item = new QStandardItem(now.addDays(1).toString()); items << item; itemModel->appendRow(items); QCOMPARE(view->graphicsView()->scene()->items().count(), 1); items.clear(); item = new QStandardItem("T2"); items << item; item = new QStandardItem(QString::number((int)KGantt::TypeTask)); items << item; item = new QStandardItem(now.addDays(1).toString()); items << item; item = new QStandardItem(now.addDays(2).toString()); items << item; itemModel->appendRow(items); } void TestKGanttView::testListView() { QListView *listview = new QListView(view); view->setLeftView(listview); view->setRowController(new KGantt::ListViewRowController(listview, view->ganttProxyModel())); view->setModel(itemModel); // must be set again initListModel(); } void TestKGanttView::testConstraints() { initTreeModel(); ConstraintModel *model = view->constraintModel(); QPersistentModelIndex idx1 = itemModel->index(0, 0, itemModel->index(0, 0)); QPersistentModelIndex idx2 = itemModel->index(1, 0, itemModel->index(0, 0)); model->addConstraint(Constraint(idx1, idx2)); QCOMPARE(model->constraints().count(), 1); QVERIFY(model->hasConstraint(Constraint(idx1, idx2))); QCOMPARE(view->graphicsView()->scene()->items().count(), 1); view->expandAll(); QCOMPARE(view->graphicsView()->scene()->items().count(), 4); view->collapseAll(); QCOMPARE(view->graphicsView()->scene()->items().count(), 1); // constraint item also removed QCOMPARE(model->constraints().count(), 1); // the constraint is not removed view->expandAll(); model->removeConstraint(model->constraints().first()); QCOMPARE(view->graphicsView()->scene()->items().count(), 3); // constraint item also removed QCOMPARE(model->constraints().count(), 0); // the constraint is removed model->addConstraint(Constraint(idx1, idx2)); QCOMPARE(view->graphicsView()->scene()->items().count(), 4); QVERIFY(itemModel->removeRows(idx1.row(), 1, idx1.parent())); QCOMPARE(view->graphicsView()->scene()->items().count(), 2); // constraint item also removed QCOMPARE(model->constraints().count(), 0); // the constraint is also removed itemModel->clear(); QCOMPARE(view->graphicsView()->scene()->items().count(), 0); initTreeModel(); idx1 = itemModel->index(0, 0, itemModel->index(0, 0)); idx2 = itemModel->index(1, 0, itemModel->index(0, 0)); model->addConstraint(Constraint(idx1, idx2)); QCOMPARE(model->constraints().count(), 1); QVERIFY(model->hasConstraint(Constraint(idx1, idx2))); // adding new items shall not affect existing constraints initTreeModel(); // add more items, will also collapseAll() QCOMPARE(view->graphicsView()->scene()->items().count(), 2); view->expandAll(); QCOMPARE(view->graphicsView()->scene()->items().count(), 7); // removing items wo constraints shall not affect existing constraints QVERIFY(itemModel->removeRows(0, 1, itemModel->index(1, 0))); QCOMPARE(view->graphicsView()->scene()->items().count(), 6); // must be possible to add constraints between items with different parent QPersistentModelIndex idx3 = itemModel->index(0, 0, itemModel->index(1, 0)); model->addConstraint(Constraint(idx2, idx3)); QCOMPARE(model->constraints().count(), 2); QVERIFY(model->hasConstraint(Constraint(idx2, idx3))); QCOMPARE(view->graphicsView()->scene()->items().count(), 7); // removing summary item shall also remove child item + any constraints to the child item QVERIFY(itemModel->removeRows(1, 1)); QCOMPARE(model->constraints().count(), 1); QCOMPARE(view->graphicsView()->scene()->items().count(), 4); } QTEST_MAIN(TestKGanttView) diff --git a/src/KGantt/unittest/test.h b/src/KGantt/unittest/test.h index a94ad2f..6997fbd 100644 --- a/src/KGantt/unittest/test.h +++ b/src/KGantt/unittest/test.h @@ -1,200 +1,200 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __KDAB__UNITTEST__TEST_H__ #define __KDAB__UNITTEST__TEST_H__ #ifndef KDAB_NO_UNIT_TESTS #include "kganttglobal.h" #include #include #include namespace KDAB { namespace UnitTest { #define assertNotNull( x ) _assertNotNull( ( x ), #x, __FILE__, __LINE__ ) #define assertNull( x ) _assertNull( ( x ), #x, __FILE__, __LINE__ ) #define assertTrue( x ) _assertTrue( (x), #x, __FILE__, __LINE__ ) #define assertFalse( x ) _assertFalse( (x), #x, __FILE__, __LINE__ ) #define assertEqual( x, y ) _assertEqual( (x), (y), #x, #y, __FILE__, __LINE__ ) #define assertNotEqual( x, y ) _assertNotEqual( (x), (y), #x, #y, __FILE__, __LINE__ ) // to be phased out: #define assertNearEqual( x, y, z ) #define assertEqualWithEpsilons( x, y, z ) _assertEqualWithEpsilons( (x), (y), (z), #x, #y, #z, __FILE__, __LINE__ ) #if 0 #define assertIsNaN( x ) _assertIsNaN( (x), #x, __FILE__, __LINE__ ) #define assertIsNotNaN( x ) _assertIsNotNaN( (x), #x, __FILE__, __LINE__ ) #endif #define assertThrowsExceptionWithCode( x, E, code ) \ do { \ try { \ x; \ fail( __FILE__, __LINE__ ) \ << "\"" #x "\" didn't throw \"" #E "\"" << std::endl; \ } catch ( E & ppq_ut_thrown ) { \ success(); \ ( void )ppq_ut_thrown; \ code; \ } catch ( ... ) { \ fail( __FILE__, __LINE__ ) \ << "\"" #x "\" threw something, but it wasn't \"" #E "\"" << std::endl; \ } \ } while ( false ) #define assertThrowsException( x, E ) assertThrowsExceptionWithCode( x, E, do{}while (0) ) #define assertDoesNotThrowException( x, E ) \ do { \ try { \ x; \ success(); \ } catch ( E & ) { \ fail( __FILE__, __LINE__ ) \ << "\"" #x "\" threw \"" #E "\", but shouldn't" << std::endl; \ } catch ( ... ) { \ fail( __FILE__, __LINE__ ) \ << "\"" #x "\" threw something, but it wasn't \"" #E "\"" << std::endl; \ } \ } while ( false ) class Test { const std::string mName; unsigned int mFailed, mSucceeded; public: Test( const std::string & name ); virtual ~Test() {} const std::string & name() const { return mName; } unsigned int failed() const { return mFailed; } unsigned int succeeded() const { return mSucceeded; } virtual void run() = 0; protected: void _assertNotNull( const void * x, const char * expression, const char * file, unsigned int line ); void _assertNull( const void * x, const char * expression, const char * file, unsigned int line ); #if 0 void _assertIsNaN( qreal v, const char * expression, const char * file, unsigned int line ); void _assertIsNotNaN( qreal v, const char * expression, const char * file, unsigned int line ); #endif void _assertTrue( bool x, const char * expression, const char * file, unsigned int line ); void _assertFalse( bool x, const char * expression, const char * file, unsigned int line ); void _assertEqualWithEpsilons( float x1, float x2, int prec, const char * expr1, const char * expr2, const char * exprPrec, const char * file, unsigned int line ); void _assertEqualWithEpsilons( qreal x1, qreal x2, int prec, const char * expr1, const char * expr2, const char * exprPrec, const char * file, unsigned int line ); void _assertEqualWithEpsilons( long double x1, long double x2, int prec, const char * expr1, const char * expr2, const char * exprPrec, const char * file, unsigned int line ); template void _assertEqual( const T & x1, const S & x2, const char * expr1, const char * expr2, const char * file, unsigned int line ) { if ( x1 == x2 ) this->success(); else { this->fail( file, line ) << '"' << expr1 << "\" yielded " << x1 << "; expected: " << x2 << "(\"" << expr2 << "\")" << std::endl; } } template void _assertNotEqual( const T & x1, const S & x2, const char * expr1, const char * expr2, const char * file, unsigned int line ) { if ( x1 != x2 ) this->success(); else { this->fail( file, line ) << '"' << expr1 << "\" yielded " << x1 << "; expected something not equal to: " << x2 << "(\"" << expr2 << "\")" << std::endl; } } protected: std::ostream & fail( const char * file, unsigned int line ); void success() { ++mSucceeded; } }; class TestFactory { public: virtual ~TestFactory() {} virtual Test * create() const = 0; }; } } #include "testregistry.h" namespace KDAB { namespace UnitTest { template class GenericFactory : public TestFactory { public: - GenericFactory( const char * group=0 ) { + GenericFactory( const char * group = nullptr ) { TestRegistry::instance()->registerTestFactory( this, group ); } Test * create() const Q_DECL_OVERRIDE { return new T_Test(); } }; } } #include "libutil.h" // Use these macros to export your UnitTest class so that it gets executed by the test runner. // Use the second macro if your class is within a namespace. // Arguments : // - Namespace (unquoted) : the namespace in which the test class in contained // - Class (unquoted) : the test class, without namespace // - Group (quoted) : the name of the group this unit test belongs to #define KDAB_EXPORT_UNITTEST( Class, Group ) \ static const KDAB::UnitTest::GenericFactory< Class > __##Class##_unittest( Group ); \ KDAB_EXPORT_STATIC_SYMBOLS( Class ) #define KDAB_EXPORT_SCOPED_UNITTEST( Namespace, Class, Group ) \ static const KDAB::UnitTest::GenericFactory< Namespace::Class > __##Class##_unittest( Group ); \ KDAB_EXPORT_STATIC_SYMBOLS( Class ) // Use this macro to import the test explicitly (for windows static libs only) #define KDAB_IMPORT_UNITTEST( Class ) KDAB_IMPORT_STATIC_SYMBOLS( Class ) // Convenience macros that create a simple test class for a single test and export it. // Usage : KDAB_UNITTEST_SIMPLE( MyClass, "mygroup" ) { doSomething(); assertEqual(...); } #define KDAB_UNITTEST_SIMPLE( Class, Group ) \ class Class##Test : public KDAB::UnitTest::Test { \ public: \ Class##Test() : Test( #Class ) {} \ void run(); \ }; \ KDAB_EXPORT_UNITTEST( Class##Test, Group ) \ void Class##Test::run() #define KDAB_SCOPED_UNITTEST_SIMPLE( Namespace, Class, Group ) \ namespace Namespace { \ class Class##Test : public KDAB::UnitTest::Test { \ public: \ Class##Test() : Test( #Namespace "::" #Class ) {} \ void run() Q_DECL_OVERRIDE; \ }; \ } \ KDAB_EXPORT_SCOPED_UNITTEST( Namespace, Class##Test, Group ) \ void Namespace::Class##Test::run() #endif // KDAB_NO_UNIT_TESTS #endif // __KDAB__UNITTEST__TEST_H__ diff --git a/src/KGantt/unittest/testregistry.cpp b/src/KGantt/unittest/testregistry.cpp index 3203a1d..575f41c 100644 --- a/src/KGantt/unittest/testregistry.cpp +++ b/src/KGantt/unittest/testregistry.cpp @@ -1,111 +1,111 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KDAB_NO_UNIT_TESTS #include "testregistry.h" #include "test.h" #include #include #include #include KDAB::UnitTest::TestRegistry::TestRegistry() : mTests() { } KDAB::UnitTest::TestRegistry::~TestRegistry() {} -KDAB::UnitTest::TestRegistry * KDAB::UnitTest::TestRegistry::mSelf = 0; +KDAB::UnitTest::TestRegistry * KDAB::UnitTest::TestRegistry::mSelf = nullptr; // static KDAB::UnitTest::TestRegistry * KDAB::UnitTest::TestRegistry::instance() { if ( !mSelf ) mSelf = new TestRegistry; return mSelf; } // static void KDAB::UnitTest::TestRegistry::deleteInstance() { - delete mSelf; mSelf = 0; + delete mSelf; mSelf = nullptr; } void KDAB::UnitTest::TestRegistry::registerTestFactory( const TestFactory * tf, const char * group ) { assert( tf ); mTests[group].push_back( tf ); } unsigned int KDAB::UnitTest::TestRegistry::run() const { unsigned int failed = 0; for ( std::map< std::string, std::vector >::const_iterator g = mTests.begin() ; g != mTests.end() ; ++g ) { std::cerr << "===== GROUP \"" << g->first << "\" =========" << std::endl; for ( std::vector::const_iterator it = g->second.begin() ; it != g->second.end() ; ++it ) { // once ported to unique_ptr, remove special flag -Wno-deprecated-declarations for this file std::auto_ptr t( (*it)->create() ); assert( t.get() ); std::cerr << " === \"" << t->name() << "\" ===" << std::endl; t->run(); std::cerr << " Succeeded: " << std::setw( 4 ) << t->succeeded() << "; failed: " << std::setw( 4 ) << t->failed() << std::endl; failed += t->failed(); } } return failed; } unsigned int KDAB::UnitTest::TestRegistry::run( const char * group ) const { assert( group ); assert( *group ); unsigned int failed = 0; const std::map< std::string, std::vector >::const_iterator g = mTests.find( group ); if ( g == mTests.end() ) { std::cerr << "ERROR: No such group \"" << group << "\"" << std::endl; return 1; } std::cerr << "===== GROUP \"" << g->first << "\" =========" << std::endl; for ( std::vector::const_iterator it = g->second.begin() ; it != g->second.end() ; ++it ) { std::auto_ptr t( (*it)->create() ); assert( t.get() ); std::cerr << " === \"" << t->name() << "\" ===" << std::endl; t->run(); std::cerr << " Succeeded: " << t->succeeded() << "; failed: " << t->failed() << std::endl; failed += t->failed(); } return failed; } KDAB::UnitTest::Runner::~Runner() { TestRegistry::deleteInstance(); } unsigned int KDAB::UnitTest::Runner::run( const char * group ) const { if ( group && *group ) return TestRegistry::instance()->run( group ); else return TestRegistry::instance()->run(); } #endif // KDAB_NO_UNIT_TESTS diff --git a/src/KGantt/unittest/testregistry.h b/src/KGantt/unittest/testregistry.h index 0daeea2..d690e03 100644 --- a/src/KGantt/unittest/testregistry.h +++ b/src/KGantt/unittest/testregistry.h @@ -1,71 +1,71 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __KDAB__UNITTEST__TESTREGISTRY_H__ #define __KDAB__UNITTEST__TESTREGISTRY_H__ #ifndef KDAB_NO_UNIT_TESTS #include "../kganttglobal.h" #include #include #include #include namespace KDAB { namespace UnitTest { class Test; class TestFactory; class KGANTT_EXPORT TestRegistry { friend class ::KDAB::UnitTest::TestFactory; static TestRegistry * mSelf; TestRegistry(); ~TestRegistry(); public: static TestRegistry * instance(); static void deleteInstance(); void registerTestFactory( const TestFactory * tf, const char * group ); /*! runs all tests in all groups. @return the number of failed tests (if any) */ unsigned int run() const; /*! runs only tests in group \a group @return the number of failed tests (if any) */ unsigned int run( const char * group ) const; private: std::map< std::string, std::vector > mTests; }; class KGANTT_EXPORT Runner { public: ~Runner(); - unsigned int run( const char * group=0 ) const; + unsigned int run( const char * group = nullptr ) const; }; } } #endif // KDAB_NO_UNIT_TESTS #endif // __KDAB__UNITTEST__TESTREGISTRY_H__ diff --git a/tests/DelayedData/main.cpp b/tests/DelayedData/main.cpp index 321dbda..1d9bfa8 100644 --- a/tests/DelayedData/main.cpp +++ b/tests/DelayedData/main.cpp @@ -1,111 +1,111 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include class ChartWidget : public QWidget { Q_OBJECT public: - explicit ChartWidget(QWidget* parent=0) + explicit ChartWidget(QWidget* parent = nullptr) : QWidget(parent) { m_model.insertRows( 0, 0, QModelIndex() ); KChart::BarDiagram* diagram = new KChart::BarDiagram; diagram->setModel(&m_model); KChart::Legend* legend = new KChart::Legend(diagram,diagram); m_chart.addLegend(legend); // The code below doesn't work, but it would // be nice if it did: #if 0 KChart::Legend* legend = new KChart::Legend; legend->addDiagram(diagram1); legend->addDiagram(diagram2); ... m_chart.addLegend(legend); #endif KChart::CartesianAxis* abcissa = new KChart::CartesianAxis(diagram); abcissa->setPosition( KChart::CartesianAxis::Bottom ); KChart::CartesianAxis* ordinate = new KChart::CartesianAxis(diagram); ordinate->setPosition( KChart::CartesianAxis::Left ); diagram->addAxis(abcissa); diagram->addAxis(ordinate); // NOTE: If this is done before adding axes, // the axes don't show up at all m_chart.coordinatePlane()->replaceDiagram(diagram); m_rowbutton.setText( tr("Add rows") ); m_colbutton.setText( tr("Add columns") ); connect( &m_rowbutton, SIGNAL(clicked()), this, SLOT(addRows())); connect( &m_colbutton, SIGNAL(clicked()), this, SLOT(addCols())); QVBoxLayout* l = new QVBoxLayout(this); l->addWidget(&m_chart); l->addWidget(&m_rowbutton); l->addWidget(&m_colbutton); setLayout(l); } private slots: void addRows() { m_model.insertRows(m_model.rowCount(),1); for ( int i = 0; i < m_model.columnCount(); ++i ) { m_model.setData( m_model.index(m_model.rowCount()-1,i), i ); } } void addCols() { m_model.insertColumns(m_model.columnCount(),1); for ( int i = 0; i < m_model.rowCount(); ++i ) { m_model.setData( m_model.index(i,m_model.columnCount()-1), i ); } } private: KChart::Chart m_chart; QPushButton m_rowbutton; QPushButton m_colbutton; QStandardItemModel m_model; }; int main( int argc, char** argv ) { QApplication app( argc, argv ); ChartWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/tests/Gantt/apireview/entrydelegate.h b/tests/Gantt/apireview/entrydelegate.h index f21df11..b0d5b6d 100644 --- a/tests/Gantt/apireview/entrydelegate.h +++ b/tests/Gantt/apireview/entrydelegate.h @@ -1,42 +1,42 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ENTRYDELEGATE_H #define ENTRYDELEGATE_H #include namespace KGantt { class ConstraintModel; } class EntryDelegate : public QItemDelegate { public: - EntryDelegate( KGantt::ConstraintModel* constraintModel, QObject* parent = 0 ); + EntryDelegate( KGantt::ConstraintModel* constraintModel, QObject* parent = nullptr ); bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ) Q_DECL_OVERRIDE; private: void addConstraint(const QModelIndex & index1, const QModelIndex & index2); void setReadOnly(const QModelIndex & index, bool readOnly); KGantt::ConstraintModel* constraintModel; }; #endif /* ENTRYDELEGATE_H */ diff --git a/tests/Gantt/apireview/entrydialog.h b/tests/Gantt/apireview/entrydialog.h index d6c03df..ba5948a 100644 --- a/tests/Gantt/apireview/entrydialog.h +++ b/tests/Gantt/apireview/entrydialog.h @@ -1,67 +1,67 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ENTRYDIALOG_H #define ENTRYDIALOG_H #include #include #include QT_BEGIN_NAMESPACE class QAbstractItemModel; namespace Ui { class EntryDialog; } QT_END_NAMESPACE namespace KGantt { class ConstraintModel; } class EntryDialog : public QDialog { Q_OBJECT public: - EntryDialog( const QAbstractItemModel* model, QWidget* parent = 0, Qt::WindowFlags f = Qt::WindowFlags() ); + EntryDialog( const QAbstractItemModel* model, QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags() ); void initFrom( const QModelIndex& index, const KGantt::ConstraintModel* constraintModel ); QString name() const; int type() const; QDateTime startDate() const; QDateTime endDate() const; int completion() const; bool readOnly() const; QModelIndex depends() const; QString legend() const; private slots: void updateEndDate( const QDateTime& startDate ); void disableEditing( bool disable ); private: void init(); void addDependItem( const QAbstractItemModel* model, const QModelIndex& index, int indent = 0 ); QList indexList; const QAbstractItemModel* model; Ui::EntryDialog* ui; }; #endif /* ENTRYDIALOG_H */ diff --git a/tests/Gantt/apireview/mainwindow.h b/tests/Gantt/apireview/mainwindow.h index 1657126..874ec55 100644 --- a/tests/Gantt/apireview/mainwindow.h +++ b/tests/Gantt/apireview/mainwindow.h @@ -1,83 +1,83 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include QT_BEGIN_NAMESPACE class QStandardItem; class QStandardItemModel; namespace Ui { class MainWindow; } QT_END_NAMESPACE namespace KGantt { class ConstraintModel; class DateTimeGrid; } class MainWindow : public QMainWindow { Q_OBJECT public: - MainWindow( QWidget * parent = 0, Qt::WindowFlags flags = Qt::WindowFlags() ); + MainWindow( QWidget * parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags() ); private slots: void addNewEntry(); void removeEntry(); void addDemoEntry(); void printPreview(); void showContextMenu( const QPoint& ); void enableActions( const QItemSelection& selected ); void zoomIn(); void zoomOut(); void slotClicked( const QModelIndex& ); void slotDoubleClicked( const QModelIndex& ); private: void initModel(); void initActions(); void initItemDelegate(); void initGrid(); void setReadOnly( const QModelIndex& index, bool readOnly ); void addConstraint( const QModelIndex& index1, const QModelIndex& index2 ); void addConstraint( const QStandardItem* item1, const QStandardItem* item2 ); QStandardItemModel* model; KGantt::ConstraintModel* constraintModel; KGantt::DateTimeGrid* grid; int dayWidth; QAction* newEntryAction; QAction* removeEntryAction; QAction* demoAction; QAction* printAction; QAction* zoomInAction; QAction* zoomOutAction; Ui::MainWindow* ui; }; #endif /* MAINWINDOW_H */ diff --git a/tests/Gantt/customconstraints/main.cpp b/tests/Gantt/customconstraints/main.cpp index ce3ad2f..85b3cb9 100644 --- a/tests/Gantt/customconstraints/main.cpp +++ b/tests/Gantt/customconstraints/main.cpp @@ -1,136 +1,136 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class MyGraphicsView : public KGantt::GraphicsView { Q_OBJECT public: - MyGraphicsView( QWidget* parent=0 ) : KGantt::GraphicsView( parent ) + MyGraphicsView( QWidget* parent = nullptr ) : KGantt::GraphicsView( parent ) { } /*reimp*/void addConstraint( const QModelIndex& from, const QModelIndex& to, Qt::KeyboardModifiers modifiers ) Q_DECL_OVERRIDE { qDebug() << "MyGraphicsView::addConstraint()"; if ( isReadOnly() ) return; KGantt::ConstraintModel* cmodel = constraintModel(); KGantt::Constraint c( from, to, ( modifiers&Qt::ShiftModifier )?KGantt::Constraint::TypeHard:KGantt::Constraint::TypeSoft ); c.setData( KGantt::Constraint::ValidConstraintPen, QPen( QColor( Qt::green ), 3, Qt::DashLine ) ); c.setData( KGantt::Constraint::InvalidConstraintPen, QPen( QColor( Qt::blue ), 3, Qt::DashLine ) ); if ( cmodel->hasConstraint( c ) ) cmodel->removeConstraint( c ); else cmodel->addConstraint( c ); } }; class MyStandardItem : public QStandardItem { public: MyStandardItem( const QVariant& v ) : QStandardItem() { setData( v, Qt::DisplayRole ); } MyStandardItem( const QString& v ) : QStandardItem() { setData( v, Qt::DisplayRole ); } MyStandardItem( const QDateTime& dt, int role ) : QStandardItem() { setData( QVariant::fromValue( dt ), role ); } }; class MyWidget : public QWidget { Q_OBJECT public: - MyWidget() : QWidget( 0 ) + MyWidget() : QWidget( nullptr ) { view.setGraphicsView( new MyGraphicsView ); qDebug() << "Building data"; //proxyModel.setSourceModel( &model ); for ( int h = 0; h < 2; ++h ) { QList items; items << new MyStandardItem( QString::fromLatin1( "Item %1" ).arg( h ) ) << new MyStandardItem( KGantt::TypeTask ) << new MyStandardItem( QDateTime::currentDateTime().addDays( h ), KGantt::StartTimeRole ) << new MyStandardItem( QDateTime::currentDateTime().addDays( h+1 ), KGantt::EndTimeRole ) << new MyStandardItem( 50 ); model.appendRow( items ); } qDebug() << "Creating view"; slider.setOrientation( Qt::Horizontal ); slider.setRange( 1, 10000 ); slider.setValue( 100 ); QVBoxLayout* l = new QVBoxLayout( this ); l->addWidget( &view ); l->addWidget( &slider ); grid.setStartDateTime( QDateTime::currentDateTime().addDays( -3 ) ); grid.setDayWidth( 100 ); //grid.setNoInformationBrush( Qt::NoBrush ); view.setGrid( &grid ); view.setModel(&model); connect( &slider, SIGNAL(valueChanged(int)), this, SLOT(slotZoom(int)) ); } public slots: void slotZoom( int z ) { grid.setDayWidth( z ); } private: KGantt::View view; KGantt::DateTimeGrid grid; QSlider slider; QStandardItemModel model; }; int main( int argc, char** argv ) { QApplication app( argc, argv ); //QPixmapCache::setCacheLimit( 30*1024 ); MyWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/tests/Gantt/headers/main.cpp b/tests/Gantt/headers/main.cpp index 22af03f..053d70d 100644 --- a/tests/Gantt/headers/main.cpp +++ b/tests/Gantt/headers/main.cpp @@ -1,117 +1,117 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include using namespace KGantt; /* A custom formatter that displays tabs for every 10 minutes. */ class MyDateTimeScaleFormatter : public DateTimeScaleFormatter { public: MyDateTimeScaleFormatter(); /*reimp*/QDateTime nextRangeBegin( const QDateTime& datetime ) const Q_DECL_OVERRIDE; /*reimp*/QDateTime currentRangeBegin( const QDateTime& datetime ) const Q_DECL_OVERRIDE; /*reimp*/QString text( const QDateTime& dt ) const Q_DECL_OVERRIDE; }; MyDateTimeScaleFormatter::MyDateTimeScaleFormatter() : DateTimeScaleFormatter( Hour, "hh" ) { } QDateTime MyDateTimeScaleFormatter::nextRangeBegin( const QDateTime& datetime ) const { return currentRangeBegin( datetime ).addSecs( 60*10 ); } QDateTime MyDateTimeScaleFormatter::currentRangeBegin( const QDateTime& datetime ) const { QDateTime dt( datetime ); dt.setTime( QTime( dt.time().hour(), ( dt.time().minute()/10 ) * 10, 0, 0 ) ); return dt; } QString MyDateTimeScaleFormatter::text( const QDateTime& dt ) const { return QObject::tr( ":%1\nXX" ).arg( dt.time().toString( "mm" ) ); } /* A custom headerview that is taller than standard * so we can fit more lines into it on the graphicsview * side. */ class MyHeaderView : public QHeaderView { public: - explicit MyHeaderView( QWidget* parent=0 ) + explicit MyHeaderView( QWidget* parent = nullptr ) : QHeaderView( Qt::Horizontal, parent ) { } /*reimp*/QSize sizeHint() const Q_DECL_OVERRIDE { QSize s = QHeaderView::sizeHint(); s.rheight() *= 3; return s; } }; int main( int argc, char** argv ) { QApplication app( argc, argv ); QStandardItemModel model( 1, 1 ); model.setHeaderData( 0, Qt::Horizontal, QObject::tr( "Task" ) ); /*** A view with some alternative header labels ***/ View view1; DateTimeGrid grid1; grid1.setUserDefinedUpperScale( new DateTimeScaleFormatter( DateTimeScaleFormatter::Year, QString::fromLatin1( "yyyy" ), QString::fromLatin1( "In the year %1." ), Qt::AlignLeft ) ); grid1.setUserDefinedLowerScale( new DateTimeScaleFormatter( DateTimeScaleFormatter::Month, QString::fromLatin1( "MMMM" ), QString::fromLatin1( "In the month %1." ), Qt::AlignRight ) ); grid1.setScale( DateTimeGrid::ScaleUserDefined ); grid1.setDayWidth( 6. ); view1.setGrid( &grid1 ); view1.setModel( &model ); view1.show(); /*** A view with header and vertical grid lines for every 10 minutes */ View view2; QTreeView* tw = qobject_cast( view2.leftView() ); if ( tw ) tw->setHeader( new MyHeaderView ); DateTimeGrid grid2; grid2.setDayWidth( 5000 ); grid2.setUserDefinedUpperScale( new DateTimeScaleFormatter( DateTimeScaleFormatter::Hour, QString::fromLatin1( "hh" ) ) ); grid2.setUserDefinedLowerScale( new MyDateTimeScaleFormatter ); grid2.setScale( DateTimeGrid::ScaleUserDefined ); view2.setGrid( &grid2 ); view2.setModel( &model ); view2.show(); return app.exec(); } diff --git a/tests/Gantt/listview/main.cpp b/tests/Gantt/listview/main.cpp index c5118b6..a82d851 100644 --- a/tests/Gantt/listview/main.cpp +++ b/tests/Gantt/listview/main.cpp @@ -1,141 +1,141 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include class MyStandardItem : public QStandardItem { public: MyStandardItem( const QVariant& v, int role = Qt::DisplayRole ) : QStandardItem() { setData( v, role ); } MyStandardItem( const QString& v ) : QStandardItem() { setData( v, Qt::DisplayRole ); } }; class MyListView : public QListView { public: - MyListView(QWidget* parent=0) : QListView(parent) + MyListView(QWidget* parent = nullptr) : QListView(parent) { // Make some space for the header setViewportMargins(0, fontMetrics().height()*2+4*frameWidth(), 0, 0); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); } }; class MyWidget : public QWidget { Q_OBJECT public: - MyWidget() : QWidget( 0 ) + MyWidget() : QWidget( nullptr ) { qDebug() << "Building data"; //proxyModel.setSourceModel( &model ); for ( int i = 0; i < 10; ++i ) { QStandardItem* item = new MyStandardItem( QString::fromLatin1("Multi Item %1").arg(i) ); for ( int j=0; j < 20; j+=3 ) { item->appendRow( QList() << new MyStandardItem( QString::fromLatin1( "Item %1" ).arg( j ) ) << new MyStandardItem( KGantt::TypeTask ) << new MyStandardItem( QDateTime::currentDateTime().addDays( j ), KGantt::StartTimeRole ) << new MyStandardItem( QDateTime::currentDateTime().addDays( j+1+i/7 ), KGantt::EndTimeRole ) << new MyStandardItem( 50 ) ); } item->appendRow( QList() << new MyStandardItem( QString::fromLatin1( "Event" ) ) << new MyStandardItem( KGantt::TypeEvent ) << new MyStandardItem( QDateTime::currentDateTime(), KGantt::StartTimeRole ) << new MyStandardItem( QDateTime(), KGantt::EndTimeRole ) << new MyStandardItem( QString() ) ); model.appendRow( QList() << item << new MyStandardItem( KGantt::TypeMulti ) << new MyStandardItem( QString() ) << new MyStandardItem( QString() ) << new MyStandardItem( QString() ) ); } //cmodel.addConstraint( KGantt::Constraint( proxyModel.index( 0, 3 ), proxyModel.index( 10, 3 ) ) ); //cmodel.addConstraint( KGantt::Constraint( proxyModel.index( 10, 3 ), proxyModel.index( 5, 3 ) ) ); QModelIndex pidx = model.index( 0, 0 ); pidx = model.index( 0, 0, pidx ); cmodel.addConstraint( KGantt::Constraint( model.index( 0, 0, pidx), model.index( 1, 0, pidx ) ) ); cmodel.addConstraint( KGantt::Constraint( model.index( 1, 0, pidx), model.index( 0, 0, pidx ) ) ); cmodel.addConstraint( KGantt::Constraint( model.index( 1, 0, pidx), model.index( 10, 0, pidx ) ) ); cmodel.addConstraint( KGantt::Constraint( model.index( 3, 0, pidx), model.index( 5, 0, pidx ) ) ); cmodel.addConstraint( KGantt::Constraint( model.index( 7, 0, pidx), model.index( 4, 0, pidx ) ) ); qDebug() << "Creating view"; slider.setOrientation( Qt::Horizontal ); slider.setRange( 1, 1000 ); slider.setValue( 100 ); QVBoxLayout* l = new QVBoxLayout( this ); l->addWidget( &view ); l->addWidget( &slider ); grid.setStartDateTime( QDateTime::currentDateTime().addDays( -3 ) ); grid.setDayWidth( 100 ); grid.setFreeDays( QSet() << Qt::Saturday << Qt::Sunday ); grid.setFreeDaysBrush( QBrush( Qt::red ) ); QListView* lv = new MyListView; view.setLeftView(lv); view.setRowController( new KGantt::ListViewRowController(lv,view.ganttProxyModel())); view.setGrid( &grid ); view.setModel(&model); //view.setConstraintModel( &cmodel ); connect( &slider, SIGNAL(valueChanged(int)), this, SLOT(slotZoom(int)) ); } public slots: void slotZoom( int z ) { grid.setDayWidth( z ); } private: KGantt::View view; KGantt::DateTimeGrid grid; QSlider slider; QStandardItemModel model; KGantt::ConstraintModel cmodel; }; int main( int argc, char** argv ) { QApplication app( argc, argv ); MyWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/tests/Gantt/reorder/main.cpp b/tests/Gantt/reorder/main.cpp index 27fa7dd..83efec4 100644 --- a/tests/Gantt/reorder/main.cpp +++ b/tests/Gantt/reorder/main.cpp @@ -1,173 +1,173 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include /* Test model that uses beginMoveRows() */ class MyTaskModel : public QAbstractTableModel { struct Task { QString title; QDateTime start, end; }; QList m_tasks; public: - MyTaskModel( QObject* parent=0 ) + MyTaskModel( QObject* parent = nullptr ) : QAbstractTableModel( parent ) { Task task; task.title = tr( "Item 1" ); task.start = QDateTime::currentDateTime(); task.end = task.start.addDays( 1 ); m_tasks.push_back( task ); task.title = tr( "Item 2" ); task.start = QDateTime::currentDateTime().addDays( 2 ); task.end = task.start.addDays( 1 ); m_tasks.push_back( task ); task.title = tr( "Item 3" ); task.start = QDateTime::currentDateTime().addDays( 4 ); task.end = task.start.addDays( 1 ); m_tasks.push_back( task ); task.title = tr( "Item 4" ); task.start = QDateTime::currentDateTime().addDays( 6 ); task.end = task.start.addDays( 1 ); m_tasks.push_back( task ); } /*reimp*/int rowCount( const QModelIndex& index = QModelIndex() ) const Q_DECL_OVERRIDE { return index.isValid()?0:m_tasks.size(); } /*reimp*/int columnCount( const QModelIndex& index = QModelIndex() ) const Q_DECL_OVERRIDE { return index.isValid()?0:4; } /*reimp*/QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const Q_DECL_OVERRIDE { if ( index.isValid() && index.row() < rowCount() && index.column() < columnCount() ) { switch ( index.column() ) { case 0: return ( role == Qt::DisplayRole ) ?QVariant::fromValue( m_tasks[index.row()].title ) :QVariant(); case 1: return ( role == Qt::DisplayRole ) ?QVariant::fromValue( ( int )KGantt::TypeTask ) :QVariant(); case 2: return ( role == KGantt::StartTimeRole || role == Qt::DisplayRole ) ?QVariant::fromValue( m_tasks[index.row()].start ) :QVariant(); case 3: return ( role == KGantt::EndTimeRole || role == Qt::DisplayRole) ?QVariant::fromValue( m_tasks[index.row()].end ) :QVariant(); } } return QVariant(); } void moveRow( int from, int to ) { if ( from == to ) return; if ( from >= m_tasks.size() || to >= m_tasks.size()+1 ) return; if ( beginMoveRows( QModelIndex(), from, from, QModelIndex(), to ) ) { m_tasks.move( from, to ); endMoveRows(); } else { assert( 0 ); } } }; /* Test class to see the effect of moving rows */ class MoveHelper : public QObject { public: MoveHelper( MyTaskModel* model, KGantt::ConstraintModel*, KGantt::View*, #if 0 KGantt::ConstraintModel* constraints, KGantt::View* view, #endif int row1, int row2 ) : QObject( model ), m_model( model ), #if 0 m_constraints( constraints ), m_view( view ), #endif m_row1( row1 ), m_row2( row2 ) { } void showContraints( const QString& /*pfx*/ ) { #if 0 qDebug() << pfx << *m_constraints; qDebug() << "0:" << m_constraints->constraintsForIndex( m_model->index( 0, 0 ) ); qDebug() << "1:"<< m_constraints->constraintsForIndex( m_model->index( 1, 0 ) ); qDebug() << "2:"<< m_constraints->constraintsForIndex( m_model->index( 2, 0 ) ); qDebug() << "3:"<< m_constraints->constraintsForIndex( m_model->index( 3, 0 ) ); #endif } ~MoveHelper() { qDebug() << "Moving row" << m_row1 << "to" << m_row2; showContraints( "Before:" ); m_model->moveRow( m_row1, m_row2 ); showContraints( "After:" ); // Hack until KGantt supports this: //m_view->setConstraintModel( m_constraints ); } private: MyTaskModel* m_model; #if 0 KGantt::ConstraintModel* m_constraints; KGantt::View* m_view; #endif int m_row1, m_row2; }; int main( int argc, char** argv ) { QApplication app( argc, argv ); KGantt::View* view = new KGantt::View; MyTaskModel model; KGantt::ConstraintModel constraints; constraints.addConstraint( KGantt::Constraint( model.index( 0, 0 ), model.index( 1, 0 ) ) ); constraints.addConstraint( KGantt::Constraint( model.index( 0, 0 ), model.index( 2, 0 ) ) ); constraints.addConstraint( KGantt::Constraint( model.index( 1, 0 ), model.index( 3, 0 ) ) ); view->setModel( &model ); view->setConstraintModel( &constraints ); view->show(); /* After 5 seconds, move row 1 to pos 0: */ QTimer::singleShot( 5000, new MoveHelper( &model, &constraints, view, 1, 0 ), SLOT(deleteLater()) ); return app.exec(); } diff --git a/tests/Gantt/view/main.cpp b/tests/Gantt/view/main.cpp index ccad291..d036a6f 100644 --- a/tests/Gantt/view/main.cpp +++ b/tests/Gantt/view/main.cpp @@ -1,203 +1,203 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class MyStandardItem : public QStandardItem { public: MyStandardItem( const QVariant& v ) : QStandardItem() { setData( v, Qt::DisplayRole ); //setFlags( flags() & ~Qt::ItemIsEditable ); } MyStandardItem( const QString& v ) : QStandardItem() { setData( v, Qt::DisplayRole ); //setFlags( flags() & ~Qt::ItemIsEditable ); } MyStandardItem( const QDateTime& dt, int role ) : QStandardItem() { setData( QVariant::fromValue( dt ), role ); } }; class MyWidget : public QWidget { Q_OBJECT public: - MyWidget() : QWidget( 0 ) + MyWidget() : QWidget( nullptr ) { qDebug() << "Building data"; //proxyModel.setSourceModel( &model ); for ( int h = 0; h < 20; ++h ) { QStandardItem* topitem = new MyStandardItem( QString::fromLatin1("Top Item %1").arg( h )); for ( int i = 0; i < 20; ++i ) { QStandardItem* item = new MyStandardItem( QString::fromLatin1("Multi Item %1").arg( i )); for ( int j=0; j < 30; j+=3 ) { item->appendRow( QList() << new MyStandardItem( QString::fromLatin1( "Item %1" ).arg( j ) ) << new MyStandardItem( KGantt::TypeTask ) << new MyStandardItem( QDateTime::currentDateTime().addDays( j ), KGantt::StartTimeRole ) << new MyStandardItem( QDateTime::currentDateTime().addDays( j+1+i/7 ), KGantt::EndTimeRole ) << new MyStandardItem( 50 ) ); } item->appendRow( QList() << new MyStandardItem( QString::fromLatin1( "Event" ) ) << new MyStandardItem( KGantt::TypeEvent ) << new MyStandardItem( QDateTime::currentDateTime(), KGantt::StartTimeRole ) << new MyStandardItem( QDateTime(), KGantt::EndTimeRole ) << new MyStandardItem( QString() ) ); topitem->appendRow( QList() << item << new MyStandardItem( KGantt::TypeMulti ) << new MyStandardItem( QString() ) << new MyStandardItem( QString() ) << new MyStandardItem( QString() ) ); } model.appendRow( QList() << topitem << new MyStandardItem(KGantt::TypeMulti/*Summary*/ ) << new MyStandardItem(QString()) << new MyStandardItem(QString()) << new MyStandardItem(QString()) ); } model.appendRow( QList() << new MyStandardItem( QString::fromLatin1( "No data" ) ) ); //cmodel.addConstraint( KGantt::Constraint( proxyModel.index( 0, 3 ), proxyModel.index( 10, 3 ) ) ); //cmodel.addConstraint( KGantt::Constraint( proxyModel.index( 10, 3 ), proxyModel.index( 5, 3 ) ) ); QModelIndex pidx = model.index( 0, 0 ); pidx = model.index( 0, 0, pidx ); cmodel.addConstraint( KGantt::Constraint( model.index( 0, 0, pidx), model.index( 1, 0, pidx ) ) ); cmodel.addConstraint( KGantt::Constraint( model.index( 1, 0, pidx), model.index( 0, 0, pidx ) ) ); cmodel.addConstraint( KGantt::Constraint( model.index( 1, 0, pidx), model.index( 10, 0, pidx ) ) ); cmodel.addConstraint( KGantt::Constraint( model.index( 3, 0, pidx), model.index( 5, 0, pidx ) ) ); cmodel.addConstraint( KGantt::Constraint( model.index( 7, 0, pidx), model.index( 4, 0, pidx ) ) ); qDebug() << "Creating view"; slider.setOrientation( Qt::Horizontal ); slider.setRange( 1, 10000 ); slider.setValue( 100 ); QVBoxLayout* l = new QVBoxLayout( this ); l->addWidget( &view ); l->addWidget( &slider ); grid.setStartDateTime( QDateTime::currentDateTime().addDays( -3 ) ); grid.setDayWidth( 100 ); //grid.setNoInformationBrush( Qt::NoBrush ); view.setGrid( &grid ); view.setModel(&model); //view.setConstraintModel( &cmodel ); connect( &slider, SIGNAL(valueChanged(int)), this, SLOT(slotZoom(int)) ); QPushButton* pb1 = new QPushButton( tr("Print Preview...")); QPushButton* pb2 = new QPushButton( tr("Print...")); l->addWidget(pb1); l->addWidget(pb2); connect(pb1,SIGNAL(clicked()),this,SLOT(slotPrintPreview())); connect(pb2,SIGNAL(clicked()),this,SLOT(slotPrint())); view.graphicsView()->setHeaderContextMenuPolicy( Qt::CustomContextMenu ); connect( view.graphicsView(), SIGNAL(headerContextMenuRequested(QPoint)), this, SLOT(slotHeaderMenu(QPoint)) ); } public slots: void slotZoom( int z ) { grid.setDayWidth( z ); } void slotPrintPreview() { QPixmap pix(1000,200); pix.fill(Qt::white); QPainter painter(&pix); view.print(&painter, pix.rect()); painter.end(); QLabel* label = new QLabel; label->setPixmap(pix); label->show(); } void slotPrint() { QPrinter printer( QPrinter::HighResolution ); QPrintDialog pd( &printer, this ); if ( pd.exec() == QDialog::Accepted ) { /* printer.setResolution( 600 ); QPainter p( &printer ); QFont f; f.setPixelSize( 9 ); p.setFont( f ); view.print( &p ); */ QRectF r = view.graphicsView()->mapToScene( view.graphicsView()->viewport()->rect() ).boundingRect(); view.print( &printer, r.left(), r.right() ); } } void slotHeaderMenu( const QPoint& pt ) { QMenu menu; menu.addAction( tr( "This" ) ); menu.addAction( tr( "is" ) ); menu.addAction( tr( "just" ) ); menu.addAction( tr( "a" ) ); menu.addAction( tr( "test" ) ); menu.exec( pt ); } private: KGantt::View view; KGantt::DateTimeGrid grid; QSlider slider; QStandardItemModel model; KGantt::ConstraintModel cmodel; }; int main( int argc, char** argv ) { QApplication app( argc, argv ); QPixmapCache::setCacheLimit( 30*1024 ); MyWidget w; w.show(); return app.exec(); } #include "main.moc" diff --git a/tests/RootIndex/mainwindow.h b/tests/RootIndex/mainwindow.h index 54f83ee..ee472f1 100644 --- a/tests/RootIndex/mainwindow.h +++ b/tests/RootIndex/mainwindow.h @@ -1,58 +1,58 @@ /** * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KD Chart library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include namespace KChart { class Chart; class DatasetProxyModel; class LineDiagram; class BarDiagram; class LineAttributes; class CartesianAxis; class CartesianCoordinatePlane; } class MainWindow : public QWidget, private Ui::MainWindow { Q_OBJECT public: - MainWindow( QWidget* parent = 0 ); + MainWindow( QWidget* parent = nullptr ); private: KChart::Chart* m_chart; QStandardItemModel m_model; QStandardItemModel m_model2; KChart::DatasetProxyModel* m_datasetProxy; KChart::LineDiagram* m_lines; KChart::BarDiagram* m_bars; KChart::CartesianCoordinatePlane* plane; // mutable KChart::CartesianAxis xAxis; //mutable KChart::CartesianAxis yAxis; }; #endif /* MAINWINDOW_H */