kchart

KDChartHiLoPainter.cpp

00001 /* -*- Mode: C++ -*-
00002    KDChart - a multi-platform charting engine
00003    */
00004 
00005 /****************************************************************************
00006  ** Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB.  All rights reserved.
00007  **
00008  ** This file is part of the KDChart library.
00009  **
00010  ** This file may be distributed and/or modified under the terms of the
00011  ** GNU General Public License version 2 as published by the Free Software
00012  ** Foundation and appearing in the file LICENSE.GPL included in the
00013  ** packaging of this file.
00014  **
00015  ** Licensees holding valid commercial KDChart licenses may use this file in
00016  ** accordance with the KDChart Commercial License Agreement provided with
00017  ** the Software.
00018  **
00019  ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020  ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021  **
00022  ** See http://www.klaralvdalens-datakonsult.se/?page=products for
00023  **   information about KDChart Commercial License Agreements.
00024  **
00025  ** Contact info@klaralvdalens-datakonsult.se if any conditions of this
00026  ** licensing are not clear to you.
00027  **
00028  **********************************************************************/
00029 #include "KDChartHiLoPainter.h"
00030 #include <KDChartParams.h>
00031 #include "KDChartTextPiece.h"
00032 
00033 #include <qpainter.h>
00034 
00035 #include <stdlib.h>
00036 
00048     KDChartHiLoPainter::KDChartHiLoPainter( KDChartParams* params ) :
00049 KDChartAxesPainter( params )
00050 {
00051     // This constructor intentionally left blank so far; we cannot setup the
00052     // geometry yet since we do not know the size of the painter.
00053 }
00054 
00055 
00059 KDChartHiLoPainter::~KDChartHiLoPainter()
00060 {
00061     // intentionally left blank
00062 }
00063 
00064 
00078 QString KDChartHiLoPainter::fallbackLegendText( uint dataset ) const
00079 {
00080     return QObject::tr( "Value " ) + QString::number( dataset + 1 );
00081 }
00082 
00083 
00093 uint KDChartHiLoPainter::numLegendFallbackTexts( KDChartTableDataBase* data ) const
00094 {
00095     return data->usedRows();
00096 }
00097 
00098 
00099 bool KDChartHiLoPainter::isNormalMode() const
00100 {
00101     return KDChartParams::HiLoNormal == params()->hiLoChartSubType();
00102 }
00103 
00104 int KDChartHiLoPainter::clipShiftUp( bool, double ) const
00105 {
00106     return 0;
00107 }
00108 
00109 void KDChartHiLoPainter::specificPaintData( QPainter* painter,
00110         const QRect& ourClipRect,
00111         KDChartTableDataBase* data,
00112         KDChartDataRegionList* /*regions*/,
00113         const KDChartAxisParams* axisPara,
00114         bool /*bNormalMode*/,
00115         uint /*chart*/,
00116         double logWidth,
00117         double areaWidthP1000,
00118         double logHeight,
00119         double axisYOffset,
00120         double /*minColumnValue*/,
00121         double /*maxColumnValue*/,
00122         double /*columnValueDistance*/,
00123         uint chartDatasetStart,
00124         uint chartDatasetEnd,
00125         uint datasetStart,
00126         uint datasetEnd )
00127 {
00128     double areaHeightP1000 = logHeight / 1000.0;
00129     double averageValueP1000 = ( areaWidthP1000 + areaHeightP1000 ) / 2.0;
00130     int datasetNum=abs(static_cast<int>(chartDatasetEnd-chartDatasetStart))+1;
00131 
00132     painter->setPen( params()->outlineDataColor() );
00133 
00134     // Number of values: If -1, use all values, otherwise use the
00135     // specified number of values.
00136     int numValues = 0;
00137     if ( params()->numValues() != -1 )
00138         numValues = params()->numValues();
00139     else
00140         numValues = data->usedCols();
00141 
00142     // We need to make sure that we have a certain number of
00143     // cells in the dataset(s), depending on the sub type to display.
00144     if( (numValues < 2) ||
00145         ((params()->hiLoChartSubType() == KDChartParams::HiLoClose)     && (numValues < 3)) ||
00146         ((params()->hiLoChartSubType() == KDChartParams::HiLoOpenClose) && (numValues < 4)) ){
00147         qDebug( "\nNot enough data to display a High/Low Chart!\n" );
00148         qDebug( "type                 requiring" );
00149         qDebug( "----                 ---------" );
00150         qDebug( "High/Low             2 data cells per series" );
00151         qDebug( "High/Low/Close       3 data cells per series" );
00152         qDebug( "High/Low/open/Close  4 data cells per series\n" );
00153         return; // PENDING(kalle) Throw exception?
00154     }
00155 
00156     double pixelsPerUnit = 0.0;
00157     if( 0.0 != axisPara->trueAxisHigh() - axisPara->trueAxisLow() )
00158         pixelsPerUnit = logHeight / (axisPara->trueAxisHigh() - axisPara->trueAxisLow());
00159     else
00160         pixelsPerUnit = logHeight / 10;
00161 
00162     // Distance between the individual "stocks"
00163     double pointDist = logWidth / (double)datasetNum;
00164 
00165     // compute the position of the 0 axis
00166     double zeroXAxisI = axisPara->axisZeroLineStartY() - _dataRect.y();
00167 
00168     const int nLineWidth = params()->lineWidth();
00169 
00170     // Loop over the datasets, draw one "stock" line for each series.
00171     for ( uint dataset  = chartDatasetStart;
00172             dataset <= chartDatasetEnd;
00173             ++dataset ) {
00174         // The first and the second col are always high and low; we sort them
00175         // accordingly.
00176         QVariant valueA;
00177         QVariant valueB;
00178         if( dataset >= datasetStart &&
00179             dataset <= datasetEnd &&
00180             data->cellCoord( dataset, 0, valueA, 1 ) &&
00181             data->cellCoord( dataset, 1, valueB, 1 ) &&
00182             QVariant::Double == valueA.type() &&
00183             QVariant::Double == valueB.type() ){
00184             const double cellValue1 = valueA.toDouble();
00185             const double cellValue2 = valueB.toDouble();
00186             const double lowValue  = QMIN( cellValue1, cellValue2 );
00187             const double highValue = QMAX( cellValue1, cellValue2 );
00188             const double lowDrawValue = lowValue * pixelsPerUnit;
00189             const double highDrawValue = highValue * pixelsPerUnit;
00190 
00191             painter->setPen( QPen( params()->dataColor( dataset ),
00192                              nLineWidth ) );
00193             // draw line from low to high
00194             int xpos = static_cast<int>(
00195                         pointDist * ( (double)(dataset-chartDatasetStart) + 0.5 ) );
00196             int lowYPos  = static_cast<int>( zeroXAxisI - lowDrawValue );
00197             int highYPos = static_cast<int>( zeroXAxisI - highDrawValue );
00198 
00199             painter->drawLine( xpos, lowYPos, xpos, highYPos );
00200 
00201             // Find out how long open/close lines need to be in case we
00202             // need them. We make them 1/10 of the space available for one
00203             // "stock".
00204             int openCloseTickLength = static_cast<int>( pointDist * 0.1 );
00205             // we need these here because we might need to consider these
00206             // later when drawing the low and high labels
00207             bool hasOpen = false, hasClose = false;
00208             double openValue = 0.0, openDrawValue = 0.0,
00209             closeValue = 0.0, closeDrawValue = 0.0;
00210 
00211             // if we have an open/close chart, show the open value
00212             if( params()->hiLoChartSubType() == KDChartParams::HiLoOpenClose ) {
00213                 // Only do this if there is a value in the third col.
00214                 if( data->cellCoord( dataset, 2, valueA, 1 ) &&
00215                     QVariant::Double == valueA.type() ) {
00216                     hasOpen = true;
00217                     openValue = valueA.toDouble();
00218                     openDrawValue = openValue * pixelsPerUnit;
00219                     painter->drawLine( xpos - openCloseTickLength,
00220                             static_cast<int>( zeroXAxisI - openDrawValue ),
00221                             xpos,
00222                             static_cast<int>( zeroXAxisI - openDrawValue ) );
00223                 }
00224             }
00225 
00226             // If we have an open/close chart or a close chart, show the
00227             // close value, but only if there is a value in the
00228             // corresponding column (2 for HiLoClose, 3 for
00229             // HiLoOpenClose).
00230             if( ( params()->hiLoChartSubType() == KDChartParams::HiLoClose &&
00231                   data->cellCoord( dataset, 2, valueA, 1 ) &&
00232                   QVariant::Double == valueA.type() ) ||
00233                 ( params()->hiLoChartSubType() == KDChartParams::HiLoOpenClose &&
00234                   data->cellCoord( dataset, 3, valueB, 1 ) &&
00235                   QVariant::Double == valueB.type() ) ) {
00236                 hasClose = true;
00237                 closeValue = ( params()->hiLoChartSubType() == KDChartParams::HiLoClose )
00238                            ? valueA.toDouble() 
00239                            : valueB.toDouble();
00240                 closeDrawValue = closeValue * pixelsPerUnit;
00241                 painter->drawLine( xpos,
00242                         static_cast<int>( zeroXAxisI - closeDrawValue ),
00243                         xpos + openCloseTickLength,
00244                         static_cast<int>( zeroXAxisI - closeDrawValue ) );
00245             }
00246 
00247             // Draw the low value, if requested.
00248             if( params()->hiLoChartPrintLowValues() ) {
00249                 // PENDING(kalle) Number formatting?
00250                 QFont theFont( params()->hiLoChartLowValuesFont() );
00251                 if ( params()->hiLoChartLowValuesUseFontRelSize() ) {
00252                     int nTxtHeight =
00253                         static_cast < int > ( params()->hiLoChartLowValuesFontRelSize()
00254                                 * averageValueP1000 );
00255                     theFont.setPointSizeFloat( nTxtHeight );
00256                 }
00257                 KDChartTextPiece lowText( painter, QString::number( lowValue ),
00258                         theFont );
00259                 int width = lowText.width();
00260                 int height = lowText.height();
00261 
00262                 // Check whether there is enough space below the data display
00263                 int valX = 0, valY = 0;
00264                 //qDebug("\nzeroXAxisI %f   lowDrawValue %f   height %i   logHeight %f   _dataRect.y() %i   axisYOffset %f",zeroXAxisI,highDrawValue,height,logHeight,_dataRect.y(),axisYOffset);
00265                 //qDebug("zeroXAxisI - lowDrawValue + height %f    <   axisYOffset + logHeight %f",
00266                 //zeroXAxisI - lowDrawValue + height, axisYOffset+logHeight);
00267                 if( zeroXAxisI - lowDrawValue + height < axisYOffset+logHeight ) {
00268                     // enough space
00269                     valX = xpos - ( width / 2 );
00270                     valY = (int)lowDrawValue - lowText.fontLeading();
00271                 } else {
00272                     // not enough space - move to left
00273                     if( !hasOpen || height < openDrawValue ) {
00274                         // Either there is no open value or it is high enough
00275                         // that we can put the low value to the left.
00276                         valX = xpos - width - nLineWidth;
00277                         valY = static_cast<int>(zeroXAxisI)
00278                                 - lowYPos
00279                                 + height/2
00280                                 + nLineWidth/2;
00281                     }
00282                 }
00283                 lowText.draw( painter,
00284                               valX, static_cast<int>( zeroXAxisI - valY ),
00285                               ourClipRect,
00286                               params()->hiLoChartLowValuesColor() );
00287             }
00288 
00289             // Draw the high value, if requested.
00290             if( params()->hiLoChartPrintHighValues() ) {
00291                 // PENDING(kalle) Number formatting?
00292                 QFont theFont( params()->hiLoChartHighValuesFont() );
00293                 if ( params()->hiLoChartHighValuesUseFontRelSize() ) {
00294                     int nTxtHeight =
00295                         static_cast < int > ( params()->hiLoChartHighValuesFontRelSize()
00296                                 * averageValueP1000 );
00297                     theFont.setPointSizeFloat( nTxtHeight );
00298                 }
00299                 KDChartTextPiece highText( painter, QString::number( highValue ),
00300                         theFont );
00301                 int width = highText.width();
00302                 int height = highText.height();
00303 
00304                 // Check whether there is enough space above the data display
00305                 int valX = 0, valY = 0;
00306                 if( zeroXAxisI - highDrawValue - height > axisYOffset ) {
00307                     // enough space
00308                     valX = xpos - ( width / 2 );
00309                     valY = (int)highDrawValue + highText.fontLeading() + height;
00310                 } else {
00311                     // not enough space - move to right
00312                     if( !hasClose ||
00313                         height < ( _dataRect.height() - closeDrawValue ) ) {
00314                         // Either there is no close value or it is low enough
00315                         // that we can put the high value to the right.
00316                         valX = xpos + nLineWidth;
00317                         valY = static_cast<int>(zeroXAxisI)
00318                                 - highYPos
00319                                 + height/2
00320                                 - nLineWidth/2;
00321                     }
00322                 }
00323                 highText.draw( painter,
00324                                valX, static_cast<int>( zeroXAxisI - valY ),
00325                                ourClipRect,
00326                                params()->hiLoChartHighValuesColor() );
00327             }
00328 
00329             // Draw the open value, if requested.
00330             if( params()->hiLoChartPrintOpenValues() &&
00331                     params()->hiLoChartSubType() == KDChartParams::HiLoOpenClose ) {
00332                 // PENDING(kalle) Number formatting?
00333                 QFont theFont( params()->hiLoChartOpenValuesFont() );
00334                 if ( params()->hiLoChartOpenValuesUseFontRelSize() ) {
00335                     int nTxtHeight =
00336                         static_cast < int > ( params()->hiLoChartOpenValuesFontRelSize()
00337                                 * averageValueP1000 );
00338                     theFont.setPointSizeFloat( nTxtHeight );
00339                 }
00340                 KDChartTextPiece openText( painter, QString::number( openValue ),
00341                         theFont );
00342                 int width = openText.width();
00343                 int height = openText.height();
00344 
00345                 // We can pretty safely assume that there is always enough
00346                 // space to the left and right of the data display.
00347                 int valX = 0, valY = 0;
00348                 valX = xpos - openCloseTickLength - width;
00349                 valY = (int)openDrawValue + ( height / 2 );
00350                 openText.draw( painter,
00351                         valX, static_cast<int>( zeroXAxisI - valY ),
00352                         ourClipRect,
00353                         params()->hiLoChartOpenValuesColor() );
00354             }
00355 
00356             // Draw the close value, if requested.
00357             if( params()->hiLoChartPrintCloseValues() &&
00358                     ( params()->hiLoChartSubType() == KDChartParams::HiLoOpenClose
00359                       ||
00360                       params()->hiLoChartSubType() == KDChartParams::HiLoClose ) ) {
00361                 // PENDING(kalle) Number formatting?
00362                 QFont theFont( params()->hiLoChartCloseValuesFont() );
00363                 if ( params()->hiLoChartCloseValuesUseFontRelSize() ) {
00364                     int nTxtHeight =
00365                         static_cast < int > ( params()->hiLoChartCloseValuesFontRelSize()
00366                                 * averageValueP1000 );
00367                     theFont.setPointSizeFloat( nTxtHeight );
00368                 }
00369                 KDChartTextPiece closeText( painter, QString::number( closeValue ),
00370                         theFont );
00371                 //int width = closeText.width();
00372                 int height = closeText.height();
00373 
00374                 // We can pretty safely assume that there is always enough
00375                 // space to the left and right of the data display.
00376                 int valX = 0, valY = 0;
00377                 valX = xpos + openCloseTickLength;
00378                 valY = (int)closeDrawValue + ( height / 2 );
00379                 closeText.draw( painter,
00380                         valX, static_cast<int>( zeroXAxisI - valY ),
00381                         ourClipRect,
00382                         params()->hiLoChartCloseValuesColor() );
00383             }
00384 
00385         } else
00386             continue; // we cannot display this value
00387     }
00388 }
KDE Home | KDE Accessibility Home | Description of Access Keys