diff options
Diffstat (limited to 'libkdchart/KDChart.cpp')
-rw-r--r-- | libkdchart/KDChart.cpp | 491 |
1 files changed, 491 insertions, 0 deletions
diff --git a/libkdchart/KDChart.cpp b/libkdchart/KDChart.cpp new file mode 100644 index 0000000..5a4b471 --- /dev/null +++ b/libkdchart/KDChart.cpp @@ -0,0 +1,491 @@ +/* -*- 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 info@klaralvdalens-datakonsult.se if any conditions of this + ** licensing are not clear to you. + ** + **********************************************************************/ +#if defined KDAB_EVAL +#include "../evaldialog/evaldialog.h" +#endif + +/** + \dontinclude KDChartPainter.h + */ +#include <KDChart.h> +#include <KDChartPainter.h> +#include <KDChartParams.h> +#include <KDChartGlobal.h> +#include <KDChartAxisParams.h> + +#include <qglobal.h> +#include <qpainter.h> +#include <qpaintdevice.h> +#include <qpaintdevicemetrics.h> + +#ifdef QSA +#if 0 // Disabled by ingwa to make it compile +#include <qsinterpreter.h> +#include "KDChartWrapperFactory.h" +#include "KDChartObjectFactory.h" +#endif +#endif + +/** + \class KDChart KDChart.h + + \brief Provides a single entry-point to the charting engine for + applications that wish to provide their own QPainter. + + It is not useful to instantiate this class as it contains + static methods only. + + \note If for some reason you are NOT using the + KDChartWidget class but calling the painting methods of KDChart directly, + you probably will also use the KDChartDataRegionList class: + This class is derived from QPtrList, so all of the Qt documentation + for this class is valid for KDChartDataRegionList too, e.g. freeing + of the pointers stored can either be done automatically or + manually - so PLEASE take the time to read the reference information for this class! + + \sa KDChartWidget, KDChartDataRegionList + */ + +KDChartParams* KDChart::oldParams = 0; +KDChartPainter* KDChart::cpainter = 0; +KDChartPainter* KDChart::cpainter2 = 0; +KDChartParams::ChartType KDChart::cpainterType = KDChartParams::NoType; +KDChartParams::ChartType KDChart::cpainterType2 = KDChartParams::NoType; + +/** + A global function that cleans up possible KDChartPainter objects at + application shutdown. + */ +void cleanupPainter(); + + +bool hasCartesianAxes( KDChartParams::ChartType chartType ) +{ + switch( chartType ){ + case KDChartParams::NoType: return false; + case KDChartParams::Bar: return true; + case KDChartParams::Line: return true; + case KDChartParams::Area: return true; + case KDChartParams::Pie: return false; + case KDChartParams::HiLo: return true; + case KDChartParams::Ring: return false; + case KDChartParams::Polar: return false; // Polar axes are NO cartesian axes! + case KDChartParams::BoxWhisker: return true; + default: + qDebug("\n\n\n\nKDCHART ERROR: Type missing in KDChart.cpp hasCartesianAxes()\n" + "=============================================================\n" + "=============================================================\n\n\n\n"); + } + return false; +} + + +/** + Calculates the drawing area from a given QPainter. + + Use this function to get a QRect that you may pass to + KDChart::setupGeometry() if you need to know the positions and + sizes of the axis areas and/or the data area *before* drawing + the chart. After calling KDChart::setupGeometry() you may use + KDChartParams::axisArea() and/or KDChartParams::dataArea() + to retrieve the desired information. + + \return True if the painter was valid and the drawing area + could be calculated successfully, else false. + */ +bool KDChart::painterToDrawRect( QPainter* painter, QRect& drawRect ) +{ + if( painter ){ + QPaintDeviceMetrics painterMetrics( painter->device() ); + drawRect = QRect( 0, 0, painterMetrics.width(), painterMetrics.height() ); + drawRect.setWidth( drawRect.width() -2 ); + drawRect.setHeight( drawRect.height()-2 ); + return true; + }else{ + drawRect = QRect( QPoint(0,0), QSize(0,0) ); + qDebug("ERROR: KDChartPainter::painterToDrawRect() was called with *no* painter."); + return false; + } +} + + +/** + Calculates the axis and data area rects of a chart with the + specified parameters on the specified painter. + + \note Call this function if you need to know the positions and + sizes of the axis areas and/or the data area *before* drawing + the chart. After calling this function you may use + KDChartParams::axisArea() and/or KDChartParams::dataArea() + to retrieve the desired information. + + To get the right drawing area from a given QPainter please + use the static method KDChart::painterToDrawRect(). + + \param painter the painter that is eventually to be used for drawing + \param params the parameters defining the chart + \param data the data that should be displayed as a chart + \param drawRect the position and size of the drawing area to be used + */ +bool KDChart::setupGeometry( QPainter* painter, + KDChartParams* params, + KDChartTableDataBase* data, + const QRect& drawRect ) +{ +//qDebug("INVOKING: KDChart::setupGeometry()"); + if( !params ){ + qDebug("ERROR: setupGeometry::paint() was called with *no* params."); + return false; + } + if( !data ){ + qDebug("ERROR: setupGeometry::paint() was called with *no* data."); + return false; + } + // don't crash due to memory problems when running on windows +#ifdef Q_WS_WIN + QPixmap::setDefaultOptimization(QPixmap::MemoryOptim); +#endif + + // Install a cleanup routine that is called when the Qt + // application shuts down and cleans up any potentially still + // existing painters. Only do this once. + static bool bFirstCleanUpInstall = true; + if( bFirstCleanUpInstall ) { + bFirstCleanUpInstall = false; + qAddPostRoutine( cleanupPainter ); + } + + // Check whether last call of this methode gave us the same params pointer. + // If params changed we must create new painter(s). + bool paramsHasChanged = ( params != oldParams ); + if( paramsHasChanged ) + oldParams = params; + + // Check whether there already is painter and, if that is the + // case, whether the painter still has the correct type (the chart + // type might have changed in the meantime). + if ( paramsHasChanged || !cpainter || cpainterType != params->chartType() ) + { + delete cpainter; /* save, since always 0 if there was not yet + a chart painter */ + // create a new painter + cpainter = KDChartPainter::create( params, false ); + cpainterType = params->chartType(); + } + + // Check whether there already is a 2nd painter and, if that is the + // case, whether the painter still has the correct type (the + // additional chart type might have changed in the meantime). + if ( paramsHasChanged || !cpainter2 || cpainterType2 != params->additionalChartType() ) + { + delete cpainter2; /* save, since always 0 if there was not yet + a chart painter */ + // create a new painter + if( hasCartesianAxes( params->chartType() ) + && hasCartesianAxes( params->additionalChartType() ) ){ + cpainter2 = KDChartPainter::create( params, true ); + cpainterType2 = params->additionalChartType(); + }else{ + cpainter2 = 0; + cpainterType2 = KDChartParams::NoType; + } + } + + if ( cpainter ){ // can be 0 if no exceptions are used + cpainter->setupGeometry( painter, data, drawRect ); + } + + if ( cpainter2 ){ // can be 0 if no exceptions are used + cpainter2->setupGeometry( painter, data, drawRect ); + } + + return true; +} + +/** + Paints a chart with the specified parameters on the specified + painter. + + \note If you are passing \c regions pointer, KD Chart will call + the \c clear() method on it, to delete any regions that might + still be registered from previous painting. + Make sure to copy any regions information into your own, private + data structure, in case you need to keep track of region information, + that was valid for such previous times. + + \param painter the QPainter onto which the chart should be painted + \param params the parameters defining the chart + \param data the data that should be displayed as a chart + \param regions if not null, this points to a + KDChartDataRegionList that will be filled with the regions + of the data segments. This information is needed internally for both + recognizing the data segment when reporting mouse clicks and + for finding the correct position to draw the respective data value texts. + \param rect the position and size of the drawing area to be used, + if this parameter is zero the painter's device metrics will be used. + \param mustCalculateGeometry may be set to false if paint() is called + immediately after a previous call of setupGeometry() to save some + time in case you have specified a lot of data cells. + */ +void KDChart::paint( QPainter* painter, + KDChartParams* paraParams, + KDChartTableDataBase* paraData, + KDChartDataRegionList* regions, + const QRect* rect, + bool mustCalculateGeometry ) +{ +//qDebug("KDChart::paint() mustCalculateGeometry: "+QString(mustCalculateGeometry?"TRUE":"FALSE") ); +#if defined KDAB_EVAL + EvalDialog::checkEvalLicense( "KD Chart" ); +#endif + + // delete old contents, to avoid the region from constantly growing + if( regions ) + regions->clear(); + + KDChartParams* params = paraParams; + KDChartTableDataBase* data = paraData; + if( !paraParams && !paraData ){ + qDebug("-----"); + qDebug("Note: KDChart::paint() was called without \"params\" and without \"data\"."); + qDebug("----- Showing a default bar chart."); + params = new KDChartParams(); + params->setDatasetGap(3 * params->valueBlockGap()); + params->setPrintDataValues( false ); + params->setLegendPosition( KDChartParams::NoLegend ); + params->setAxisLabelsVisible( KDChartAxisParams::AxisPosBottom, false ); + params->setAxisShowGrid( KDChartAxisParams::AxisPosBottom, false ); + params->setHeader1Text( "KDChartWidget" ); + data = new KDChartTableData( 3, 1 ); + // 1st series + data->setCell( 0, 0, 12.5 ); + // 2nd series + data->setCell( 1, 0, 8.0 ); + // 3rd series + data->setCell( 2, 0, 15.0 ); + } + + QRect drawRect; + bool bOk = true; + if( mustCalculateGeometry || !cpainter || cpainter->outermostRect().isNull() ){ + if( rect ) + drawRect = *rect; + else if( !painterToDrawRect( painter, drawRect ) ){ + qDebug("ERROR: KDChart::paint() could not calculate a drawing area."); + bOk = false; + } + //qDebug("xxx" ); + if( (params || data) && !setupGeometry( painter, params, data, drawRect ) ){ + qDebug("ERROR: KDChart::paint() could not calculate the chart geometry."); + bOk = false; + } + }else{ + drawRect = cpainter->outermostRect(); + } + + //qDebug("yyy" ); + + if( bOk ){ + // Note: the following *must* paint the main-chart first + // and the additional chart afterwards + // since all axes computations are only done when + // the first chart is painted but will be needed for both of course. + // + bool paintFirst = true; + bool paintLast = ! ( cpainter && cpainter2 ); + if ( cpainter ) { // can be 0 if no exceptions are used + //qDebug("zzz" ); + cpainter->paint( painter, data, paintFirst, paintLast, regions, &drawRect, false ); + + paintFirst = false; + } + paintLast = true; + if ( cpainter2 ) // can be 0 if no exceptions are used + cpainter2->paint( painter, data, paintFirst, paintLast, regions, &drawRect, false ); + } + + if( !paraParams && !paraData ){ + delete params; + delete data; + } + KDChartAutoColor::freeInstance(); // stuff that memory leak +} + + +/** + Paints a chart with the specified parameters on the specified + painter which should use a QPrinter as it's output device. + + This method is provided for your convenience, it behaves + like the paint() method described above but additionally + it takes care for the output mode flag: Before painting is + started the internal optimizeOutputForScreen flag is set + to FALSE and after painting is done it is restored to + it's previous value. + + \sa paint + */ +void KDChart::print( QPainter* painter, KDChartParams* params, + KDChartTableDataBase* data, + KDChartDataRegionList* regions, + const QRect* rect, + bool mustCalculateGeometry ) +{ + bool oldOpt=true; + if( params ){ + oldOpt = params->optimizeOutputForScreen(); + params->setOptimizeOutputForScreen( false ); + } + paint( painter, params, data, regions, rect, mustCalculateGeometry ); + if( params ) + params->setOptimizeOutputForScreen( oldOpt ); +} + + +/* + This method is called at application shut-down and cleans up the + last created painter. + */ +void cleanupPainter() +{ + delete KDChart::cpainter; + delete KDChart::cpainter2; + KDChart::oldParams = 0; +} + +#ifdef QSA +void KDChart::initInterpreter( QSInterpreter* interpreter ) +{ + privateInitInterpreter( interpreter ); + interpreter->evaluate( globals() ); +} + +void KDChart::initProject( QSProject* project ) +{ + project->createScript( QString::fromLatin1( "KDCHART_Globals" ), globals() ); + privateInitInterpreter( project->interpreter() ); +} + +QString KDChart::globals() +{ + QString globals; + QMap<char*, double> intMap; + + intMap.insert( "KDCHART_POS_INFINITE", KDCHART_POS_INFINITE ); + intMap.insert( "KDCHART_NEG_INFINITE", KDCHART_NEG_INFINITE ); + intMap.insert( "KDCHART_AlignAuto", KDCHART_AlignAuto ); + intMap.insert( "KDCHART_ALL_AXES", KDCHART_ALL_AXES ); + intMap.insert( "KDCHART_NO_AXIS", KDCHART_NO_AXIS ); + intMap.insert( "KDCHART_ALL_DATASETS", KDCHART_ALL_DATASETS ); + intMap.insert( "KDCHART_NO_DATASET", KDCHART_NO_DATASET ); + intMap.insert( "KDCHART_UNKNOWN_CHART", KDCHART_UNKNOWN_CHART ); + intMap.insert( "KDCHART_ALL_CHARTS", KDCHART_ALL_CHARTS ); + intMap.insert( "KDCHART_NO_CHART", KDCHART_NO_CHART ); + intMap.insert( "KDCHART_GLOBAL_LINE_STYLE", KDCHART_GLOBAL_LINE_STYLE ); + intMap.insert( "KDCHART_AUTO_SIZE", KDCHART_AUTO_SIZE ); + intMap.insert( "KDCHART_DATA_VALUE_AUTO_DIGITS", KDCHART_DATA_VALUE_AUTO_DIGITS ); + intMap.insert( "KDCHART_SAGITTAL_ROTATION", KDCHART_SAGITTAL_ROTATION ); + intMap.insert( "KDCHART_TANGENTIAL_ROTATION", KDCHART_TANGENTIAL_ROTATION ); + intMap.insert( "KDCHART_PROPSET_NORMAL_DATA", KDCHART_PROPSET_NORMAL_DATA ); + intMap.insert( "KDCHART_PROPSET_TRANSPARENT_DATA", KDCHART_PROPSET_TRANSPARENT_DATA ); + intMap.insert( "KDCHART_PROPSET_HORI_LINE", KDCHART_PROPSET_HORI_LINE ); + intMap.insert( "KDCHART_PROPSET_VERT_LINE", KDCHART_PROPSET_VERT_LINE ); + intMap.insert( "KDCHART_SAGGITAL_ROTATION", KDCHART_SAGGITAL_ROTATION ); + intMap.insert( "KDCHART_CNT_ORDINATES", KDCHART_CNT_ORDINATES ); + intMap.insert( "KDCHART_MAX_POLAR_DELIMS_AND_LABELS_POS", KDCHART_MAX_POLAR_DELIMS_AND_LABELS_POS ); + intMap.insert( "KDCHART_MAX_AXES", KDCHART_MAX_AXES ); + intMap.insert( "KDCHART_AXIS_LABELS_AUTO_DELTA", KDCHART_AXIS_LABELS_AUTO_DELTA ); + intMap.insert( "KDCHART_AXIS_LABELS_AUTO_LEAVEOUT", KDCHART_AXIS_LABELS_AUTO_LEAVEOUT ); + intMap.insert( "KDCHART_AXIS_LABELS_AUTO_DIGITS", KDCHART_AXIS_LABELS_AUTO_DIGITS ); + intMap.insert( "KDCHART_AXIS_GRID_AUTO_LINEWIDTH", KDCHART_AXIS_GRID_AUTO_LINEWIDTH ); + intMap.insert( "KDCHART_AXIS_IGNORE_EMPTY_INNER_SPAN", KDCHART_AXIS_IGNORE_EMPTY_INNER_SPAN ); + intMap.insert( "KDCHART_DONT_CHANGE_EMPTY_INNER_SPAN_NOW", KDCHART_DONT_CHANGE_EMPTY_INNER_SPAN_NOW ); + intMap.insert( "DBL_MIN", DBL_MIN ); + intMap.insert( "DBL_MAX", DBL_MAX ); + + for( QMapIterator<char*,double> it= intMap.begin(); it != intMap.end(); ++it ) { + // This is written this way to be efficient + globals += QString::fromLatin1( "const " ); + globals += it.key(); + globals += " = "; + globals += QString::number( it.data() ); + globals += ";\n"; + } + + globals += QString::fromLatin1( "const KDCHART_AXIS_LABELS_AUTO_DATETIME_FORMAT=\"%1\";\n" ) + .arg( QString::fromLatin1( KDCHART_AXIS_LABELS_AUTO_DATETIME_FORMAT ) ); + globals += QString::fromLatin1( "const KDCHART_AXIS_LABELS_AUTO_LIMIT = 140319.64;\n" ); + globals += QString::fromLatin1( "const KDCHART_DEFAULT_AXIS_GRID_COLOR = new Color(\"%1\");\n" ) + .arg(KDCHART_DEFAULT_AXIS_GRID_COLOR.name()); + globals += QString::fromLatin1( "const KDCHART_DATA_VALUE_AUTO_COLOR = new Color(\"%1\");\n" ) + .arg( (KDCHART_DATA_VALUE_AUTO_COLOR)->name()); + + + QMap<char*,QColor> colorMap; + colorMap.insert( "Qt.color0", Qt::color0 ); + colorMap.insert( "Qt.color1", Qt::color1 ); + colorMap.insert( "Qt.black", Qt::black ); + colorMap.insert( "Qt.white", Qt::white ); + colorMap.insert( "Qt.darkGray", Qt::darkGray ); + colorMap.insert( "Qt.gray", Qt::gray ); + colorMap.insert( "Qt.lightGray", Qt::lightGray ); + colorMap.insert( "Qt.red", Qt::red ); + colorMap.insert( "Qt.green", Qt::green ); + colorMap.insert( "Qt.blue", Qt::blue ); + colorMap.insert( "Qt.cyan", Qt::cyan ); + colorMap.insert( "Qt.magenta", Qt::magenta ); + colorMap.insert( "Qt.yellow", Qt::yellow ); + colorMap.insert( "Qt.darkRed", Qt::darkRed ); + colorMap.insert( "Qt.darkGreen", Qt::darkGreen ); + colorMap.insert( "Qt.darkBlue", Qt::darkBlue ); + colorMap.insert( "Qt.darkCyan", Qt::darkCyan ); + colorMap.insert( "Qt.darkMagenta", Qt::darkMagenta ); + colorMap.insert( "Qt.darkYellow", Qt::darkYellow ); + for( QMapIterator<char*,QColor> it2= colorMap.begin(); it2 != colorMap.end(); ++it2 ) { + // This is written this way to be efficient + globals += QString::fromLatin1( it2.key() ); + globals += QString::fromLatin1( " = new Color( " ); + globals += QString::number( it2.data().red() ); + globals += ','; + globals += QString::number( it2.data().green() ); + globals += ','; + globals += QString::number( it2.data().blue() ); + globals += QString::fromLatin1( " );\n" ); + } + //qDebug( "%s",globals.latin1() ); + return globals; +} + +void KDChart::privateInitInterpreter( QSInterpreter* interpreter ) +{ + interpreter->addWrapperFactory( new KDChartWrapperFactory ); + interpreter->addObjectFactory ( new KDChartObjectFactory ); +} + +#endif |