File indexing completed on 2024-12-15 04:02:33

0001 /*
0002  * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
0003  *
0004  * This file is part of the KD Chart library.
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "KChartStockDiagram.h"
0010 #include "KChartStockDiagram_p.h"
0011 
0012 #include "KChartPaintContext.h"
0013 #include "KChartPainterSaver_p.h"
0014 
0015 using namespace KChart;
0016 
0017 #define d d_func()
0018 
0019 StockDiagram::StockDiagram( QWidget *parent, CartesianCoordinatePlane *plane )
0020     : AbstractCartesianDiagram( new Private(), parent, plane )
0021 {
0022     init();
0023 }
0024 
0025 StockDiagram::~StockDiagram()
0026 {
0027 }
0028 
0029 /*
0030   * Initializes the diagram
0031   */
0032 void StockDiagram::init()
0033 {
0034     d->diagram = this;
0035     d->compressor.setModel( attributesModel() );
0036 
0037     // Set properties to defaults
0038     d->type = HighLowClose;
0039     d->upTrendCandlestickBrush = QBrush( Qt::white );
0040     d->downTrendCandlestickBrush = QBrush( Qt::black );
0041     d->upTrendCandlestickPen = QPen( Qt::black );
0042     d->downTrendCandlestickPen = QPen( Qt::black );
0043 
0044     d->lowHighLinePen = QPen( Qt::black );
0045     setDatasetDimensionInternal( 3 );
0046     //setDatasetDimension( 3 );
0047 
0048     setPen( QPen( Qt::black ) );
0049 }
0050 
0051 void StockDiagram::setType( Type type )
0052 {
0053     d->type = type;
0054     Q_EMIT propertiesChanged();
0055 }
0056 
0057 StockDiagram::Type StockDiagram::type() const
0058 {
0059    return d->type;
0060 }
0061 
0062 void StockDiagram::setStockBarAttributes( const StockBarAttributes &attr )
0063 {
0064     attributesModel()->setModelData(
0065             QVariant::fromValue( attr ),
0066             StockBarAttributesRole );
0067     Q_EMIT propertiesChanged();
0068 }
0069 
0070 StockBarAttributes StockDiagram::stockBarAttributes() const
0071 {
0072     return attributesModel()->modelData( StockBarAttributesRole ).value<StockBarAttributes>();
0073 }
0074 
0075 void StockDiagram::setStockBarAttributes( int column, const StockBarAttributes &attr )
0076 {
0077     d->setDatasetAttrs( column, QVariant::fromValue( attr ), StockBarAttributesRole );
0078     Q_EMIT propertiesChanged();
0079 }
0080 
0081 StockBarAttributes StockDiagram::stockBarAttributes( int column ) const
0082 {
0083     const QVariant attr( d->datasetAttrs( column, StockBarAttributesRole ) );
0084     if ( attr.isValid() )
0085         return attr.value<StockBarAttributes>();
0086     return stockBarAttributes();
0087 }
0088 
0089 void StockDiagram::setThreeDBarAttributes( const ThreeDBarAttributes &attr )
0090 {
0091     attributesModel()->setModelData(
0092             QVariant::fromValue( attr ),
0093             ThreeDBarAttributesRole );
0094     Q_EMIT propertiesChanged();
0095 }
0096 
0097 ThreeDBarAttributes StockDiagram::threeDBarAttributes() const
0098 {
0099     return attributesModel()->modelData( ThreeDBarAttributesRole ).value<ThreeDBarAttributes>();
0100 }
0101 
0102 void StockDiagram::setThreeDBarAttributes( int column, const ThreeDBarAttributes &attr )
0103 {
0104     d->setDatasetAttrs( column, QVariant::fromValue( attr ), StockBarAttributesRole );
0105     Q_EMIT propertiesChanged();
0106 }
0107 
0108 ThreeDBarAttributes StockDiagram::threeDBarAttributes( int column ) const
0109 {
0110     const QVariant attr( d->datasetAttrs( column, ThreeDBarAttributesRole ) );
0111     if ( attr.isValid() )
0112         return attr.value<ThreeDBarAttributes>();
0113     return threeDBarAttributes();
0114 }
0115 
0116 
0117 void StockDiagram::setLowHighLinePen( const QPen &pen )
0118 {
0119     d->lowHighLinePen = pen;
0120 }
0121 
0122 QPen StockDiagram::lowHighLinePen() const
0123 {
0124     return d->lowHighLinePen;
0125 }
0126 
0127 void StockDiagram::setLowHighLinePen( int column, const QPen &pen )
0128 {
0129     d->lowHighLinePens[column] = pen;
0130 }
0131 
0132 QPen StockDiagram::lowHighLinePen( int column ) const
0133 {
0134     if ( d->lowHighLinePens.contains( column ) )
0135         return d->lowHighLinePens[column];
0136     return d->lowHighLinePen;
0137 }
0138 
0139 void StockDiagram::setUpTrendCandlestickBrush( const QBrush &brush )
0140 {
0141     d->upTrendCandlestickBrush = brush;
0142 }
0143 
0144 QBrush StockDiagram::upTrendCandlestickBrush() const
0145 {
0146     return d->upTrendCandlestickBrush;
0147 }
0148 
0149 void StockDiagram::setDownTrendCandlestickBrush( const QBrush &brush )
0150 {
0151     d->downTrendCandlestickBrush = brush;
0152 }
0153 
0154 QBrush StockDiagram::downTrendCandlestickBrush() const
0155 {
0156     return d->downTrendCandlestickBrush;
0157 }
0158 
0159 void StockDiagram::setUpTrendCandlestickBrush( int column, const QBrush &brush )
0160 {
0161     d->upTrendCandlestickBrushes[column] = brush;
0162 }
0163 
0164 QBrush StockDiagram::upTrendCandlestickBrush( int column ) const
0165 {
0166     if ( d->upTrendCandlestickBrushes.contains( column ) )
0167         return d->upTrendCandlestickBrushes[column];
0168     return d->upTrendCandlestickBrush;
0169 }
0170 
0171 void StockDiagram::setDownTrendCandlestickBrush( int column, const QBrush &brush )
0172 {
0173     d->downTrendCandlestickBrushes[column] = brush;
0174 }
0175 
0176 QBrush StockDiagram::downTrendCandlestickBrush( int column ) const
0177 {
0178     if ( d->downTrendCandlestickBrushes.contains( column ) )
0179         return d->downTrendCandlestickBrushes[column];
0180     return d->downTrendCandlestickBrush;
0181 }
0182 
0183 
0184 void StockDiagram::setUpTrendCandlestickPen( const QPen &pen )
0185 {
0186     d->upTrendCandlestickPen = pen;
0187 }
0188 
0189 QPen StockDiagram::upTrendCandlestickPen() const
0190 {
0191     return d->upTrendCandlestickPen;
0192 }
0193 
0194 void StockDiagram::setDownTrendCandlestickPen( const QPen &pen )
0195 {
0196     d->downTrendCandlestickPen = pen;
0197 }
0198 
0199 QPen StockDiagram::downTrendCandlestickPen() const
0200 {
0201     return d->downTrendCandlestickPen;
0202 }
0203 
0204 void StockDiagram::setUpTrendCandlestickPen( int column, const QPen &pen )
0205 {
0206     d->upTrendCandlestickPens[column] = pen;
0207 }
0208 
0209 QPen StockDiagram::upTrendCandlestickPen( int column ) const
0210 {
0211     if ( d->upTrendCandlestickPens.contains( column ) )
0212         return d->upTrendCandlestickPens[column];
0213     return d->upTrendCandlestickPen;
0214 }
0215 
0216 void StockDiagram::setDownTrendCandlestickPen( int column, const QPen &pen )
0217 {
0218     d->downTrendCandlestickPens[column] = pen;
0219 }
0220 
0221 QPen StockDiagram::downTrendCandlestickPen( int column ) const
0222 {
0223     if ( d->downTrendCandlestickPens.contains( column ) )
0224         return d->downTrendCandlestickPens[column];
0225     return d->downTrendCandlestickPen;
0226 }
0227 
0228 #if defined(Q_COMPILER_MANGLES_RETURN_TYPE)
0229 const
0230 #endif
0231 int StockDiagram::numberOfAbscissaSegments() const { return 1; }
0232 
0233 #if defined(Q_COMPILER_MANGLES_RETURN_TYPE)
0234 const
0235 #endif
0236 int StockDiagram::numberOfOrdinateSegments() const { return 1; }
0237 
0238 void StockDiagram::paint( PaintContext *context )
0239 {
0240     // Clear old reverse mapping data and create new
0241     // reverse mapping scene
0242     d->reverseMapper.clear();
0243 
0244     PainterSaver painterSaver( context->painter() );
0245     const int rowCount = attributesModel()->rowCount( attributesModelRootIndex() );
0246     const int divisor = ( d->type == OpenHighLowClose || d->type == Candlestick ) ? 4 : 3;
0247     const int colCount = attributesModel()->columnCount( attributesModelRootIndex() ) / divisor;
0248     for ( int col = 0; col < colCount; ++col )
0249     {
0250         for ( int row = 0; row < rowCount; row++ ) {
0251             CartesianDiagramDataCompressor::DataPoint low;
0252             CartesianDiagramDataCompressor::DataPoint high;
0253             CartesianDiagramDataCompressor::DataPoint open;
0254             CartesianDiagramDataCompressor::DataPoint close;
0255             CartesianDiagramDataCompressor::DataPoint volume;
0256 
0257             if ( d->type == HighLowClose ) {
0258                 const CartesianDiagramDataCompressor::CachePosition highPos( row, col * divisor );
0259                 const CartesianDiagramDataCompressor::CachePosition lowPos( row, col * divisor + 1 );
0260                 const CartesianDiagramDataCompressor::CachePosition closePos( row, col * divisor + 2 );
0261                 low = d->compressor.data( lowPos );
0262                 high = d->compressor.data( highPos );
0263                 close = d->compressor.data( closePos );
0264             } else if ( d->type == OpenHighLowClose || d->type == Candlestick ) {
0265                 const CartesianDiagramDataCompressor::CachePosition openPos( row, col * divisor );
0266                 const CartesianDiagramDataCompressor::CachePosition highPos( row, col * divisor + 1 );
0267                 const CartesianDiagramDataCompressor::CachePosition lowPos( row, col * divisor + 2 );
0268                 const CartesianDiagramDataCompressor::CachePosition closePos( row, col * divisor + 3 );
0269                 open = d->compressor.data( openPos );
0270                 low = d->compressor.data( lowPos );
0271                 high = d->compressor.data( highPos );
0272                 close = d->compressor.data( closePos );
0273             }
0274 
0275 
0276             switch ( d->type ) {
0277             case HighLowClose:
0278                 open.hidden = true;
0279                 Q_FALLTHROUGH();
0280                 // Fall-through intended!
0281             case OpenHighLowClose:
0282                 if ( close.index.isValid() && low.index.isValid() && high.index.isValid() )
0283                 d->drawOHLCBar( col, open, high, low, close, context );
0284                 break;
0285             case Candlestick:
0286                 d->drawCandlestick( col, open, high, low, close, context );
0287                 break;
0288             }
0289         }
0290     }
0291 }
0292 
0293 void StockDiagram::resize( const QSizeF &size )
0294 {
0295     d->compressor.setResolution( static_cast< int >( size.width() * coordinatePlane()->zoomFactorX() ),
0296                                  static_cast< int >( size.height() * coordinatePlane()->zoomFactorY() ) );
0297     setDataBoundariesDirty();
0298     AbstractCartesianDiagram::resize( size );
0299 }
0300 
0301 qreal StockDiagram::threeDItemDepth( int column ) const
0302 {
0303     Q_UNUSED( column );
0304     //FIXME: Implement threeD functionality
0305     return 1.0;
0306 }
0307 
0308 qreal StockDiagram::threeDItemDepth( const QModelIndex &index ) const
0309 {
0310     Q_UNUSED( index );
0311     //FIXME: Implement threeD functionality
0312     return 1.0;
0313 }
0314 
0315 const QPair<QPointF, QPointF> StockDiagram::calculateDataBoundaries() const
0316 {
0317     const int rowCount = attributesModel()->rowCount( attributesModelRootIndex() );
0318     const int colCount = attributesModel()->columnCount( attributesModelRootIndex() );
0319     qreal xMin = 0.0;
0320     qreal xMax = rowCount;
0321     qreal yMin = 0.0;
0322     qreal yMax = 0.0;
0323     for ( int row = 0; row < rowCount; row++ ) {
0324         for ( int col = 0; col < colCount; col++ ) {
0325             const CartesianDiagramDataCompressor::CachePosition pos( row, col );
0326             const CartesianDiagramDataCompressor::DataPoint point = d->compressor.data( pos );
0327             yMax = qMax( yMax, point.value );
0328             yMin = qMin( yMin, point.value ); // FIXME: Can stock charts really have negative values?
0329         }
0330     }
0331     return QPair<QPointF, QPointF>( QPointF( xMin, yMin ), QPointF( xMax, yMax ) );
0332 }
0333