diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-07-04 22:38:03 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-07-04 22:38:03 +0000 |
commit | dadc34655c3ab961b0b0b94a10eaaba710f0b5e8 (patch) | |
tree | 99e72842fe687baea16376a147619b6048d7e441 /libkdchart/KDChartHiLoPainter.cpp | |
download | kmymoney-dadc34655c3ab961b0b0b94a10eaaba710f0b5e8.tar.gz kmymoney-dadc34655c3ab961b0b0b94a10eaaba710f0b5e8.zip |
Added kmymoney
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kmymoney@1239792 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'libkdchart/KDChartHiLoPainter.cpp')
-rw-r--r-- | libkdchart/KDChartHiLoPainter.cpp | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/libkdchart/KDChartHiLoPainter.cpp b/libkdchart/KDChartHiLoPainter.cpp new file mode 100644 index 0000000..c95d482 --- /dev/null +++ b/libkdchart/KDChartHiLoPainter.cpp @@ -0,0 +1,390 @@ +/* -*- Mode: C++ -*- + KDChart - a multi-platform charting engine + */ + +/**************************************************************************** + ** Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB. All rights reserved. + ** + ** This file is part of the KDChart library. + ** + ** This file may be distributed and/or modified under the terms of the + ** GNU General Public License version 2 as published by the Free Software + ** Foundation and appearing in the file LICENSE.GPL included in the + ** packaging of this file. + ** + ** Licensees holding valid commercial KDChart licenses may use this file in + ** accordance with the KDChart Commercial License Agreement provided with + ** the Software. + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ** See http://www.klaralvdalens-datakonsult.se/?page=products for + ** information about KDChart Commercial License Agreements. + ** + ** Contact [email protected] if any conditions of this + ** licensing are not clear to you. + ** + **********************************************************************/ +#include "KDChartHiLoPainter.h" +#include <KDChartParams.h> +#include "KDChartTextPiece.h" + +#include <qpainter.h> + +#include <stdlib.h> + +/** + \class KDChartHiLoPainter KDChartHiLoPainter.h + + \brief A chart painter implementation that can paint HiLo charts. + */ + +/** + Constructor. Sets up internal data structures as necessary. + + \param params the KDChartParams structure that defines the chart + */ + KDChartHiLoPainter::KDChartHiLoPainter( KDChartParams* params ) : +KDChartAxesPainter( params ) +{ + // This constructor intentionally left blank so far; we cannot setup the + // geometry yet since we do not know the size of the painter. +} + + +/** + Destructor. + */ +KDChartHiLoPainter::~KDChartHiLoPainter() +{ + // intentionally left blank +} + + +/** + This method is a specialization that returns a fallback legend text + appropriate for HiLo that do not have the same notion of a dataset like + e.g. bars. + + This method is only used when automatic legends are used, because + manual and first-column legends do not need fallback texts. + + \param uint dataset the dataset number for which to generate a + fallback text + \return the fallback text to use for describing the specified + dataset in the legend + */ +QString KDChartHiLoPainter::fallbackLegendText( uint dataset ) const +{ + return QObject::tr( "Value " ) + QString::number( dataset + 1 ); +} + + +/** + This methods returns the number of elements to be shown in the + legend in case fallback texts are used. + + This method is only used when automatic legends are used, because + manual and first-column legends do not need fallback texts. + + \return the number of fallback texts to use + */ +uint KDChartHiLoPainter::numLegendFallbackTexts( KDChartTableDataBase* data ) const +{ + return data->usedRows(); +} + + +bool KDChartHiLoPainter::isNormalMode() const +{ + return KDChartParams::HiLoNormal == params()->hiLoChartSubType(); +} + +int KDChartHiLoPainter::clipShiftUp( bool, double ) const +{ + return 0; +} + +void KDChartHiLoPainter::specificPaintData( QPainter* painter, + const QRect& ourClipRect, + KDChartTableDataBase* data, + KDChartDataRegionList* /*regions*/, + const KDChartAxisParams* axisPara, + bool /*bNormalMode*/, + uint /*chart*/, + double logWidth, + double areaWidthP1000, + double logHeight, + double axisYOffset, + double /*minColumnValue*/, + double /*maxColumnValue*/, + double /*columnValueDistance*/, + uint chartDatasetStart, + uint chartDatasetEnd, + uint datasetStart, + uint datasetEnd ) +{ + double areaHeightP1000 = logHeight / 1000.0; + double averageValueP1000 = ( areaWidthP1000 + areaHeightP1000 ) / 2.0; + int datasetNum=abs(static_cast<int>(chartDatasetEnd-chartDatasetStart))+1; + + painter->setPen( params()->outlineDataColor() ); + + // Number of values: If -1, use all values, otherwise use the + // specified number of values. + int numValues = 0; + if ( params()->numValues() != -1 ) + numValues = params()->numValues(); + else + numValues = data->usedCols(); + + // We need to make sure that we have a certain number of + // cells in the dataset(s), depending on the sub type to display. + if( (numValues < 2) || + ((params()->hiLoChartSubType() == KDChartParams::HiLoClose) && (numValues < 3)) || + ((params()->hiLoChartSubType() == KDChartParams::HiLoOpenClose) && (numValues < 4)) ){ + qDebug( "\nNot enough data to display a High/Low Chart!\n" ); + qDebug( "type requiring" ); + qDebug( "---- ---------" ); + qDebug( "High/Low 2 data cells per series" ); + qDebug( "High/Low/Close 3 data cells per series" ); + qDebug( "High/Low/open/Close 4 data cells per series\n" ); + return; // PENDING(kalle) Throw exception? + } + + double pixelsPerUnit = 0.0; + if( 0.0 != axisPara->trueAxisHigh() - axisPara->trueAxisLow() ) + pixelsPerUnit = logHeight / (axisPara->trueAxisHigh() - axisPara->trueAxisLow()); + else + pixelsPerUnit = logHeight / 10; + + // Distance between the individual "stocks" + double pointDist = logWidth / (double)datasetNum; + + // compute the position of the 0 axis + double zeroXAxisI = axisPara->axisZeroLineStartY() - _dataRect.y(); + + const int nLineWidth = params()->lineWidth(); + + // Loop over the datasets, draw one "stock" line for each series. + for ( uint dataset = chartDatasetStart; + dataset <= chartDatasetEnd; + ++dataset ) { + // The first and the second col are always high and low; we sort them + // accordingly. + QVariant valueA; + QVariant valueB; + if( dataset >= datasetStart && + dataset <= datasetEnd && + data->cellCoord( dataset, 0, valueA, 1 ) && + data->cellCoord( dataset, 1, valueB, 1 ) && + QVariant::Double == valueA.type() && + QVariant::Double == valueB.type() ){ + const double cellValue1 = valueA.toDouble(); + const double cellValue2 = valueB.toDouble(); + const double lowValue = QMIN( cellValue1, cellValue2 ); + const double highValue = QMAX( cellValue1, cellValue2 ); + const double lowDrawValue = lowValue * pixelsPerUnit; + const double highDrawValue = highValue * pixelsPerUnit; + + painter->setPen( QPen( params()->dataColor( dataset ), + nLineWidth ) ); + // draw line from low to high + int xpos = static_cast<int>( + pointDist * ( (double)(dataset-chartDatasetStart) + 0.5 ) ); + int lowYPos = static_cast<int>( zeroXAxisI - lowDrawValue ); + int highYPos = static_cast<int>( zeroXAxisI - highDrawValue ); + + painter->drawLine( xpos, lowYPos, xpos, highYPos ); + + // Find out how long open/close lines need to be in case we + // need them. We make them 1/10 of the space available for one + // "stock". + int openCloseTickLength = static_cast<int>( pointDist * 0.1 ); + // we need these here because we might need to consider these + // later when drawing the low and high labels + bool hasOpen = false, hasClose = false; + double openValue = 0.0, openDrawValue = 0.0, + closeValue = 0.0, closeDrawValue = 0.0; + + // if we have an open/close chart, show the open value + if( params()->hiLoChartSubType() == KDChartParams::HiLoOpenClose ) { + // Only do this if there is a value in the third col. + if( data->cellCoord( dataset, 2, valueA, 1 ) && + QVariant::Double == valueA.type() ) { + hasOpen = true; + openValue = valueA.toDouble(); + openDrawValue = openValue * pixelsPerUnit; + painter->drawLine( xpos - openCloseTickLength, + static_cast<int>( zeroXAxisI - openDrawValue ), + xpos, + static_cast<int>( zeroXAxisI - openDrawValue ) ); + } + } + + // If we have an open/close chart or a close chart, show the + // close value, but only if there is a value in the + // corresponding column (2 for HiLoClose, 3 for + // HiLoOpenClose). + if( ( params()->hiLoChartSubType() == KDChartParams::HiLoClose && + data->cellCoord( dataset, 2, valueA, 1 ) && + QVariant::Double == valueA.type() ) || + ( params()->hiLoChartSubType() == KDChartParams::HiLoOpenClose && + data->cellCoord( dataset, 3, valueB, 1 ) && + QVariant::Double == valueB.type() ) ) { + hasClose = true; + closeValue = ( params()->hiLoChartSubType() == KDChartParams::HiLoClose ) + ? valueA.toDouble() + : valueB.toDouble(); + closeDrawValue = closeValue * pixelsPerUnit; + painter->drawLine( xpos, + static_cast<int>( zeroXAxisI - closeDrawValue ), + xpos + openCloseTickLength, + static_cast<int>( zeroXAxisI - closeDrawValue ) ); + } + + // Draw the low value, if requested. + if( params()->hiLoChartPrintLowValues() ) { + // PENDING(kalle) Number formatting? + QFont theFont( params()->hiLoChartLowValuesFont() ); + if ( params()->hiLoChartLowValuesUseFontRelSize() ) { + int nTxtHeight = + static_cast < int > ( params()->hiLoChartLowValuesFontRelSize() + * averageValueP1000 ); + theFont.setPointSizeFloat( nTxtHeight ); + } + KDChartTextPiece lowText( painter, QString::number( lowValue ), + theFont ); + int width = lowText.width(); + int height = lowText.height(); + + // Check whether there is enough space below the data display + int valX = 0, valY = 0; + //qDebug("\nzeroXAxisI %f lowDrawValue %f height %i logHeight %f _dataRect.y() %i axisYOffset %f",zeroXAxisI,highDrawValue,height,logHeight,_dataRect.y(),axisYOffset); + //qDebug("zeroXAxisI - lowDrawValue + height %f < axisYOffset + logHeight %f", + //zeroXAxisI - lowDrawValue + height, axisYOffset+logHeight); + if( zeroXAxisI - lowDrawValue + height < axisYOffset+logHeight ) { + // enough space + valX = xpos - ( width / 2 ); + valY = (int)lowDrawValue - lowText.fontLeading(); + } else { + // not enough space - move to left + if( !hasOpen || height < openDrawValue ) { + // Either there is no open value or it is high enough + // that we can put the low value to the left. + valX = xpos - width - nLineWidth; + valY = static_cast<int>(zeroXAxisI) + - lowYPos + + height/2 + + nLineWidth/2; + }// else + ; // no way to draw it (really?) + } + lowText.draw( painter, + valX, static_cast<int>( zeroXAxisI - valY ), + ourClipRect, + params()->hiLoChartLowValuesColor() ); + } + + // Draw the high value, if requested. + if( params()->hiLoChartPrintHighValues() ) { + // PENDING(kalle) Number formatting? + QFont theFont( params()->hiLoChartHighValuesFont() ); + if ( params()->hiLoChartHighValuesUseFontRelSize() ) { + int nTxtHeight = + static_cast < int > ( params()->hiLoChartHighValuesFontRelSize() + * averageValueP1000 ); + theFont.setPointSizeFloat( nTxtHeight ); + } + KDChartTextPiece highText( painter, QString::number( highValue ), + theFont ); + int width = highText.width(); + int height = highText.height(); + + // Check whether there is enough space above the data display + int valX = 0, valY = 0; + if( zeroXAxisI - highDrawValue - height > axisYOffset ) { + // enough space + valX = xpos - ( width / 2 ); + valY = (int)highDrawValue + highText.fontLeading() + height; + } else { + // not enough space - move to right + if( !hasClose || + height < ( _dataRect.height() - closeDrawValue ) ) { + // Either there is no close value or it is low enough + // that we can put the high value to the right. + valX = xpos + nLineWidth; + valY = static_cast<int>(zeroXAxisI) + - highYPos + + height/2 + - nLineWidth/2; + } //else + ; // no way to draw it (really?) + } + highText.draw( painter, + valX, static_cast<int>( zeroXAxisI - valY ), + ourClipRect, + params()->hiLoChartHighValuesColor() ); + } + + // Draw the open value, if requested. + if( params()->hiLoChartPrintOpenValues() && + params()->hiLoChartSubType() == KDChartParams::HiLoOpenClose ) { + // PENDING(kalle) Number formatting? + QFont theFont( params()->hiLoChartOpenValuesFont() ); + if ( params()->hiLoChartOpenValuesUseFontRelSize() ) { + int nTxtHeight = + static_cast < int > ( params()->hiLoChartOpenValuesFontRelSize() + * averageValueP1000 ); + theFont.setPointSizeFloat( nTxtHeight ); + } + KDChartTextPiece openText( painter, QString::number( openValue ), + theFont ); + int width = openText.width(); + int height = openText.height(); + + // We can pretty safely assume that there is always enough + // space to the left and right of the data display. + int valX = 0, valY = 0; + valX = xpos - openCloseTickLength - width; + valY = (int)openDrawValue + ( height / 2 ); + openText.draw( painter, + valX, static_cast<int>( zeroXAxisI - valY ), + ourClipRect, + params()->hiLoChartOpenValuesColor() ); + } + + // Draw the close value, if requested. + if( params()->hiLoChartPrintCloseValues() && + ( params()->hiLoChartSubType() == KDChartParams::HiLoOpenClose + || + params()->hiLoChartSubType() == KDChartParams::HiLoClose ) ) { + // PENDING(kalle) Number formatting? + QFont theFont( params()->hiLoChartCloseValuesFont() ); + if ( params()->hiLoChartCloseValuesUseFontRelSize() ) { + int nTxtHeight = + static_cast < int > ( params()->hiLoChartCloseValuesFontRelSize() + * averageValueP1000 ); + theFont.setPointSizeFloat( nTxtHeight ); + } + KDChartTextPiece closeText( painter, QString::number( closeValue ), + theFont ); + //int width = closeText.width(); + int height = closeText.height(); + + // We can pretty safely assume that there is always enough + // space to the left and right of the data display. + int valX = 0, valY = 0; + valX = xpos + openCloseTickLength; + valY = (int)closeDrawValue + ( height / 2 ); + closeText.draw( painter, + valX, static_cast<int>( zeroXAxisI - valY ), + ourClipRect, + params()->hiLoChartCloseValuesColor() ); + } + + } else + continue; // we cannot display this value + } +} |