diff options
Diffstat (limited to 'kchart/kdchart')
83 files changed, 48363 insertions, 0 deletions
diff --git a/kchart/kdchart/KDChart.cpp b/kchart/kdchart/KDChart.cpp new file mode 100644 index 00000000..5a4b4719 --- /dev/null +++ b/kchart/kdchart/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 diff --git a/kchart/kdchart/KDChart.h b/kchart/kdchart/KDChart.h new file mode 100644 index 00000000..4d4944e7 --- /dev/null +++ b/kchart/kdchart/KDChart.h @@ -0,0 +1,97 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHART_H__ +#define __KDCHART_H__ + +#ifdef QSA +#include <qsproject.h> +#endif // QSA + +#include "KDChartGlobal.h" +#include "KDChartTable.h" +#include "KDChartUnknownTypeException.h" +#include "KDChartParams.h" +#include "KDChartDataRegion.h" + +/** \file KDChart.h + \brief Definition of a single entry-point to the charting engine for + applications that wish to provide their own QPainter. + + It is not useful to instantiate the KDChart class as it only contains + static methods. + */ + +class KDChartPainter; +class QPainter; +class QSInterpreter; + + +class KDCHART_EXPORT KDChart +{ + friend void cleanupPainter(); + + public: + static bool setupGeometry( QPainter* painter, + KDChartParams* params, + KDChartTableDataBase* data, + const QRect& drawRect ); + + static bool painterToDrawRect( QPainter* painter, QRect& drawRect ); + + static void paint( QPainter*, KDChartParams*, KDChartTableDataBase*, + KDChartDataRegionList* regions = 0, + const QRect* rect = 0, + bool mustCalculateGeometry = true ); + + static void print( QPainter*, KDChartParams*, KDChartTableDataBase*, + KDChartDataRegionList* regions = 0, + const QRect* rect = 0, + bool mustCalculateGeometry = true ); + +#ifdef QSA + static void initInterpreter( QSInterpreter* ); + static void initProject( QSProject* ); +#endif + + private: + KDChart(); // prevent instantiations + + static KDChartParams* oldParams; + static KDChartPainter* cpainter; + static KDChartPainter* cpainter2; + static KDChartParams::ChartType cpainterType; + static KDChartParams::ChartType cpainterType2; + +#ifdef QSA + static QString globals(); + static void privateInitInterpreter( QSInterpreter* interpreter ); +#endif +}; + +#endif diff --git a/kchart/kdchart/KDChartAreaPainter.cpp b/kchart/kdchart/KDChartAreaPainter.cpp new file mode 100644 index 00000000..19a8123e --- /dev/null +++ b/kchart/kdchart/KDChartAreaPainter.cpp @@ -0,0 +1,80 @@ +/* -*- 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. + ** + **********************************************************************/ +#include "KDChartAreaPainter.h" + +/** + \class KDChartAreaPainter KDChartAreaPainter.h + + \brief Implements a chart painter that draws area charts. + */ + +/** + Constructor. Sets up internal data structures as necessary. + + \param params the KDChartParams structure that defines the chart + */ + KDChartAreaPainter::KDChartAreaPainter( KDChartParams* params ) : +KDChartLinesPainter( 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. + */ +KDChartAreaPainter::~KDChartAreaPainter() +{ + // intentionally left blank +} + + +/** + Paints the actual data area. + + \param painter the QPainter onto which the chart should be painted + \param data the data that will be displayed as a chart + \param paint2nd specifies whether the main chart or the additional chart is to be drawn now + \param regions a pointer to a list of regions that will be filled + with regions representing the data segments, if not null + */ +void KDChartAreaPainter::paintData( QPainter* painter, + KDChartTableDataBase* data, + bool paint2nd, + KDChartDataRegionList* regions ) +{ + paintDataInternal( painter, data, + false, // non-centered points + false, // no line markers + true, // an area + paint2nd, + regions ); +} + diff --git a/kchart/kdchart/KDChartAreaPainter.h b/kchart/kdchart/KDChartAreaPainter.h new file mode 100644 index 00000000..f3347836 --- /dev/null +++ b/kchart/kdchart/KDChartAreaPainter.h @@ -0,0 +1,50 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTAREAPAINTER_H__ +#define __KDCHARTAREAPAINTER_H__ + +#include "KDChartLinesPainter.h" +#include <KDChartTable.h> + +class KDChartParams; + +class KDChartAreaPainter : public KDChartLinesPainter +{ + friend class KDChartPainter; + protected: + KDChartAreaPainter( KDChartParams* params ); + virtual ~KDChartAreaPainter(); + + virtual void paintData( QPainter* painter, + KDChartTableDataBase* data, + bool paint2nd, + KDChartDataRegionList* regions = 0 ); +}; + +#endif diff --git a/kchart/kdchart/KDChartAxesPainter.cpp b/kchart/kdchart/KDChartAxesPainter.cpp new file mode 100644 index 00000000..68e70af1 --- /dev/null +++ b/kchart/kdchart/KDChartAxesPainter.cpp @@ -0,0 +1,4525 @@ +/* -*- Mode: C++ -*- + KDChart - a multi-platform charting engine + */ + +/**************************************************************************** + ** Copyright (C) 2001-2003 Klar�vdalens 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. + ** + **********************************************************************/ +#include <qpainter.h> +#include <qlabel.h> + +#include <KDDrawText.h> +#include "KDChartAxesPainter.h" +#include "KDChartAxisParams.h" +#include "KDChartParams.h" + +#include <stdlib.h> + + +/** + Little helper function returning the number of seconds + between UTC start date 1970/01/01 00:00 and a given date \c dt. + The return value is negative for \c dt < 1970/01/01. + */ +int secondsSinceUTCStart( const QDateTime& dt ) +{ + QDateTime dtStart( QDate( 1970, 1, 1 ) ); + return dtStart.secsTo( dt ); +} + + +/** + \class KDChartAxesPainter KDChartAxesPainter.h + + \brief A common base class for classes that implement chart + painters for chart types ith axes. + */ + +/** + Constructor. Sets up internal data structures as necessary. + + \param params the KDChartParams structure that defines the chart + */ + KDChartAxesPainter::KDChartAxesPainter( KDChartParams* params ) : +KDChartPainter( params ) +{ + // Intentionally left blank. + // We cannot setup the geometry yet + // since we do not know the size of the painter. +} + +/** + Destructor. + */ +KDChartAxesPainter::~KDChartAxesPainter() +{ + // intentionally left blank +} + + +#if COMPAT_QT_VERSION < 0x030000 +QDateTime dateTimeFromString( const QString& s ) // only ISODate is allowed +{ + int year( s.mid( 0, 4 ).toInt() ); + int month( s.mid( 5, 2 ).toInt() ); + int day( s.mid( 8, 2 ).toInt() ); + QString t( s.mid( 11 ) ); + int hour( t.mid( 0, 2 ).toInt() ); + int minute( t.mid( 3, 2 ).toInt() ); + int second( t.mid( 6, 2 ).toInt() ); + int msec( t.mid( 9, 3 ).toInt() ); + if ( year && month && day ) + return QDateTime( QDate( year, month, day ), + QTime( hour, minute, second, msec ) ); + else + return QDateTime(); +} +QString dateTimeToString( const QDateTime& dt ) // ISODate is returned +{ + QString date; + QString month( + QString::number( dt.date().month() ).rightJustify( 2, '0' ) ); + QString day( + QString::number( dt.date().day() ).rightJustify( 2, '0' ) ); + date = QString::number( dt.date().year() ) + "-" + month + "-" + day; + QString time; + time.sprintf( "%.2d:%.2d:%.2d", + dt.time().hour(), dt.time().minute(), dt.time().second() ); + return date + "T" + time; +} +#endif + + +/** + ReCalculate the labels based upon given nDelta and nDeltaPix. + + This is necessary to build isometric axes. + */ +void reCalculateLabelTexts( + QPainter* painter, + const KDChartTableDataBase& data, + const KDChartParams& params, + uint axisNumber, + double averageValueP1000, + double delimLen, + internal__KDChart__CalcValues& cv ) +{ + KDChartAxesPainter::calculateLabelTexts( + painter, + data, + params, + axisNumber, + averageValueP1000, + delimLen, + // start of reference parameters + cv.basicPos, + cv.orig, + cv.dest, + cv.pXDeltaFactor, + cv.pYDeltaFactor, + cv.pXDelimDeltaFaktor, + cv.pYDelimDeltaFaktor, + cv.nSubDelimFactor, + cv.pDelimDelta, + cv.nTxtHeight, + cv.pTextsX, + cv.pTextsY, + cv.pTextsW, + cv.pTextsH, + cv.textAlign, + cv.bLogarithmic, + cv.isDateTime, + cv.autoDtLabels, + cv.dtLow, + cv.dtHigh, + cv.dtDeltaScale, + true, + cv.nDelta, + cv.nDeltaPix ); + const KDChartAxisParams & para = params.axisParams( axisNumber ); + cv.bSteadyCalc = para.axisSteadyValueCalc(); + cv.bDecreasing = para.axisValuesDecreasing(); + cv.nLow = para.trueAxisLow(); + cv.nHigh = para.trueAxisHigh(); +} + + +bool KDChartAxesPainter::calculateAllAxesLabelTextsAndCalcValues( + QPainter* painter, + KDChartTableDataBase* data, + double areaWidthP1000, + double areaHeightP1000, + double& delimLen) +{ + uint iAxis; + double averageValueP1000 = QMIN(areaWidthP1000, areaHeightP1000);//( areaWidthP1000 + areaHeightP1000 ) / 2.0; + //qDebug("KChart::KDChartAxesPainter::calculateAllAxesLabelTextsAndCalcValues() averageValueP1000: %f", averageValueP1000); + // length of little delimiter-marks indicating axis scaling + delimLen = 20.0 * averageValueP1000; // per mille of area + + // Determine axes calculation values and labels before drawing the axes. + + // step #1: calculate all values independendly from the other axes' values + for( iAxis = 0; iAxis < KDCHART_MAX_AXES; ++iAxis ) + { + internal__KDChart__CalcValues& cv = calcVal[iAxis]; + cv.processThisAxis = ( params()->axisParams( iAxis ).axisVisible() + && KDChartAxisParams::AxisTypeUnknown + != params()->axisParams( iAxis ).axisType() ); + if( cv.processThisAxis ){ + cv.nSubDelimFactor = 0.0; + cv.pDelimDelta = 0.0; + cv.nTxtHeight = 0.0; + cv.pTextsX = 0.0; + cv.pTextsY = 0.0; + cv.pTextsW = 0.0; + cv.pTextsH = 0.0; + cv.textAlign = Qt::AlignHCenter | Qt::AlignVCenter; + cv.isDateTime = false; + cv.autoDtLabels = false; + calculateLabelTexts( painter, + *data, + *params(), + iAxis, + averageValueP1000, + delimLen, + // start of reference parameters + cv.basicPos, + cv.orig, + cv.dest, + cv.pXDeltaFactor, + cv.pYDeltaFactor, + cv.pXDelimDeltaFaktor, + cv.pYDelimDeltaFaktor, + cv.nSubDelimFactor, + cv.pDelimDelta, + cv.nTxtHeight, + cv.pTextsX, + cv.pTextsY, + cv.pTextsW, + cv.pTextsH, + cv.textAlign, + cv.bLogarithmic, + cv.isDateTime, + cv.autoDtLabels, + cv.dtLow, + cv.dtHigh, + cv.dtDeltaScale ); + const KDChartAxisParams & para = params()->axisParams( iAxis ); + cv.bSteadyCalc = para.axisSteadyValueCalc(); + cv.bDecreasing = para.axisValuesDecreasing(); + cv.nLow = para.trueAxisLow(); + cv.nHigh = para.trueAxisHigh(); + cv.nDelta = para.trueAxisDelta(); + cv.nDeltaPix = para.trueAxisDeltaPixels(); + cv.pLastX = cv.dest.x(); + cv.pLastY = cv.dest.y(); + } + } + + // step #2: if isometric axes are desired adjust/re-calculate some values + for ( iAxis = 0; iAxis < KDCHART_MAX_AXES; ++iAxis ){ + internal__KDChart__CalcValues& cv = calcVal[iAxis]; + if( cv.processThisAxis + && cv.bSteadyCalc ){ + const KDChartAxisParams & para = params()->axisParams( iAxis ); + const uint isoRef = para.isometricReferenceAxis(); + if( KDCHART_NO_AXIS != isoRef + && iAxis != isoRef + && ( KDCHART_MAX_AXES > isoRef + || KDCHART_ALL_AXES == isoRef ) ){ + if( KDCHART_ALL_AXES == isoRef ){ + uint iAxis2; + // first find the axis values to be taken as reference + double nDelta = cv.nDelta; + double nDeltaPix = cv.nDeltaPix; + double nSubDelimFactor = cv.nSubDelimFactor; + for ( iAxis2 = 0; + iAxis2 < KDCHART_MAX_AXES; + ++iAxis2 ){ + internal__KDChart__CalcValues& cv2 = calcVal[iAxis2]; + if( cv2.processThisAxis + && cv2.bSteadyCalc + && (0.0 != cv2.nDelta) + && (fabs(cv2.nDeltaPix / cv2.nDelta) < fabs(nDeltaPix / nDelta)) ){ + if( (nDelta >= 0.0) == (cv2.nDelta >= 0.0) ) + nDelta = cv2.nDelta; + else + nDelta = cv2.nDelta * -1.0; + if( (nDeltaPix >= 0.0) == (cv2.nDeltaPix >= 0.0) ) + nDeltaPix = cv2.nDeltaPix; + else + nDeltaPix = cv2.nDeltaPix * -1.0; + if( (nSubDelimFactor >= 0.0) == (cv2.nSubDelimFactor >= 0.0) ) + nSubDelimFactor = cv2.nSubDelimFactor; + else + nSubDelimFactor = cv2.nSubDelimFactor * -1.0; + } + } + // now adjust all axes (if necessary) + for ( iAxis2 = 0; + iAxis2 < KDCHART_MAX_AXES; + ++iAxis2 ){ + internal__KDChart__CalcValues& cv2 = calcVal[iAxis2]; + if( cv2.processThisAxis + && cv2.bSteadyCalc + && ( fabs(cv2.nDelta) != fabs(nDelta) + || fabs(cv2.nDeltaPix) != fabs(nDeltaPix) ) ){ + //qDebug("\nrecalculating scale for axis %x", iAxis2); + //qDebug("cv2.nDelta %f cv2.nDeltaPix %f nDelta %f nDeltaPix %f\n", + // cv2.nDelta,cv2.nDeltaPix,nDelta,nDeltaPix); + if( (cv2.nDelta >= 0.0) == (nDelta >= 0.0) ) + cv2.nDelta = nDelta; + else + cv2.nDelta = nDelta * -1.0; + if( (cv2.nDeltaPix >= 0.0) == (nDeltaPix >= 0.0) ) + cv2.nDeltaPix = nDeltaPix; + else + cv2.nDeltaPix = nDeltaPix * -1.0; + reCalculateLabelTexts( painter, + *data, + *params(), + iAxis2, + averageValueP1000, + delimLen, + cv2 ); + if( (cv2.nSubDelimFactor >= 0.0) == (nSubDelimFactor >= 0.0) ) + cv2.nSubDelimFactor = nSubDelimFactor; + else + cv2.nSubDelimFactor = nSubDelimFactor * -1.0; + } + } + }else{ + internal__KDChart__CalcValues& cv2 = calcVal[isoRef]; + // adjust this axis or the other axis (if necessary) + if( cv2.processThisAxis + && cv2.bSteadyCalc + && ( cv2.nDelta != cv.nDelta + || cv2.nDeltaPix != cv.nDeltaPix ) ){ + if( cv2.nDelta > cv.nDelta + || ( cv2.nDelta == cv.nDelta + && cv2.nDeltaPix < cv.nDeltaPix ) ){ + // adjust this axis + //qDebug("recalculating scale for this axis %x", iAxis); + cv.nDelta = cv2.nDelta; + cv.nDeltaPix = cv2.nDeltaPix; + reCalculateLabelTexts( + painter, + *data, + *params(), + iAxis, + averageValueP1000, + delimLen, + cv ); + cv.nSubDelimFactor = cv2.nSubDelimFactor; + }else{ + // adjust the other axis + //qDebug("\nrecalculating scale for other axis %x", isoRef); + //qDebug("cv2.nDelta %f cv2.nDeltaPix %f cv.nDelta %f cv.nDeltaPix %f", + // cv2.nDelta,cv2.nDeltaPix,cv.nDelta,cv.nDeltaPix); + cv2.nDelta = cv.nDelta; + cv2.nDeltaPix = cv.nDeltaPix; + reCalculateLabelTexts( + painter, + *data, + *params(), + isoRef, + averageValueP1000, + delimLen, + cv2 ); + cv2.nSubDelimFactor = cv.nSubDelimFactor; + } + } + } + } + } + } + return true; +} + + +/** + Paints the actual axes areas. + + \param painter the QPainter onto which the chart should be painted + \param data the data that will be displayed as a chart + */ +void KDChartAxesPainter::paintAxes( QPainter* painter, + KDChartTableDataBase* data ) +{ + if ( !painter || !data || 0 == params() ) + return ; + + const bool bMultiRowBarChart = KDChartParams::Bar == params()->chartType() && + KDChartParams::BarMultiRows == params()->barChartSubType(); + + double areaWidthP1000 = _logicalWidth / 1000.0; + double areaHeightP1000 = _logicalHeight / 1000.0; + double averageValueP1000 = QMIN(areaWidthP1000, areaHeightP1000);//( areaWidthP1000 + areaHeightP1000 ) / 2.0; + // length of little delimiter-marks indicating axis scaling + double delimLen; + +//qDebug("-------------------------------------------------------------------------------------"); + + calculateAllAxesLabelTextsAndCalcValues( painter, data, areaWidthP1000, areaHeightP1000, delimLen ); + + + // Now the labels are known, so let us paint the axes... + painter->save(); + painter->setPen( Qt::NoPen ); + + bool screenOutput = params()->optimizeOutputForScreen(); + uint iAxis; + + for ( iAxis = 0; iAxis < KDCHART_MAX_AXES; ++iAxis ){ + internal__KDChart__CalcValues& cv = calcVal[iAxis]; + if( cv.processThisAxis ){ + + const KDChartAxisParams & para = params()->axisParams( iAxis ); + + internal__KDChart__CalcValues& cv = calcVal[iAxis]; + + const QColor labelsColor( para.axisLabelsColor() ); + + // Debugging axis areas: + //painter->fillRect(para.axisTrueAreaRect(), Qt::yellow); + + uint lineWidth = 0 <= para.axisLineWidth() + ? para.axisLineWidth() + : -1 * static_cast < int > ( para.axisLineWidth() + * averageValueP1000 ); + ( ( KDChartAxisParams& ) para ).setAxisTrueLineWidth( lineWidth ); + + uint gridLineWidth + = ( KDCHART_AXIS_GRID_AUTO_LINEWIDTH + == para.axisGridLineWidth() ) + ? lineWidth + : ( ( 0 <= para.axisGridLineWidth() ) + ? para.axisGridLineWidth() + : -1 * static_cast < int > ( para.axisGridLineWidth() + * averageValueP1000 ) ); + + uint gridSubLineWidth + = ( KDCHART_AXIS_GRID_AUTO_LINEWIDTH + == para.axisGridSubLineWidth() ) + ? lineWidth + : ( ( 0 <= para.axisGridSubLineWidth() ) + ? para.axisGridSubLineWidth() + : -1 * static_cast < int > ( para.axisGridSubLineWidth() + * averageValueP1000 ) ); + + // Magic to find out axis scaling factors and labels text height + // ============================================================= + // - khz, 02/24/2001 + // + // 1st Calculate the axis label texts height regarding to + // user-defined per-axis settings. + // + // 2nd This height is given to calculateLabelTexts() to + // calculate the delimiter and sub-delimiter distances as + // well as the axis scaling factors. + // If neccessary and possible the short replacement strings + // are taken that might have been specified by the user. + // - see KDChartAxisParams::setAxisLabelStringLists() - + // + // 3rd Before displaying the texts we make sure they fit into + // their space, if needed we will do the following + // in order to avoid clipping of text parts: + // + // (a) ABSCISSA axes only: rotate the texts in 5 steps + // until they are drawn vertically + // + // (b) further reduce the texts' font height down to 6pt + // . + // + // If the texts *still* don't fit into their space, we are lost + // and they will be clipped. Such is live. + // + // Why all this? + // + // Because I do not believe in axis areas growing and shrinking + // regarding to long or short label texts: start such behaviour + // and become mad. + // + // Better plan: ask the user to specify a way how to abbreviate + // label texts (e.g. by writing "200" instead + // of that wide and unreadable "200,000.00") + // + // + // F E A T U R E P L A N N E D F O R F U T U R E . . . + // + // + + // Note: The labels-touch-edges flag may have been set to true + // inside the calculateLabelTexts() function. + bool bTouchEdges = para.axisLabelsTouchEdges(); + + // NOTE: The steady-value-calc flag may have been set to true + // inside the calculateLabelTexts() function + // by a special setAxisLabelTextParams() call, + // therefor we do not store its value before calling that function. + if( cv.bLogarithmic ) + cv.nSubDelimFactor = 0.1; + + const double nUsableAxisHeight = cv.pTextsH; + const double nUsableAxisWidth = cv.pTextsW; + + const bool isHorizontalAxis + = (KDChartAxisParams::AxisPosBottom == cv.basicPos) || + (KDChartAxisParams::AxisPosTop == cv.basicPos); + + QStringList* labelTexts = ( QStringList* ) para.axisLabelTexts(); + uint nLabels = ( 0 != labelTexts ) + ? labelTexts->count() + : 0; + // start point of 1st delimiter on the axis-line == grid-start + QPoint p1( cv.orig ); + // end point of 1st delimiter near the label text + QPoint p2( cv.orig ); + // end point of small sub-delimiter + QPoint p2a( cv.orig ); + // start point of 1st grid-line (beginnig at the axis) + QPoint pGA( cv.orig ); + // end point of 1st grid-line at the other side of the chart + QPoint pGZ( cv.orig ); + // start point of zero-line, this is often identical with p1 + // but will be different in case of shifted zero-line + double axisZeroLineStartX = p1.x(); + double axisZeroLineStartY = p1.y(); + + p2.setX( p2.x() + static_cast < int > ( cv.pXDelimDeltaFaktor * delimLen ) ); + p2.setY( p2.y() + static_cast < int > ( cv.pYDelimDeltaFaktor * delimLen ) ); + p2a.setX( p2a.x() + static_cast < int > ( cv.pXDelimDeltaFaktor * delimLen * 2.0 / 3.0 ) ); + p2a.setY( p2a.y() + static_cast < int > ( cv.pYDelimDeltaFaktor * delimLen * 2.0 / 3.0 ) ); + pGZ.setX( pGZ.x() - static_cast < int > ( cv.pXDelimDeltaFaktor * (_dataRect.width() - 1) ) ); + pGZ.setY( pGZ.y() - static_cast < int > ( cv.pYDelimDeltaFaktor * (_dataRect.height() - 1) ) ); + + if ( nLabels ) { + // Sometimes the first or last labels partially reach out of + // their axis area: we allow this + const bool oldClippingFlag = painter->hasClipping(); + painter->setClipping( false ); + + if( para.hasAxisFirstLabelText() ) + labelTexts->first() = para.axisFirstLabelText(); + if( para.hasAxisLastLabelText() ) + labelTexts->last() = para.axisLastLabelText(); + + const double pXDelta = cv.pXDeltaFactor * cv.pDelimDelta; + const double pYDelta = cv.pYDeltaFactor * cv.pDelimDelta; + + // draw label texts and delimiters and grid + painter->setPen( QPen( para.axisLineColor(), + lineWidth ) ); + + const QString formatDT = cv.isDateTime + ? para.axisLabelsDateTimeFormat() + : QString(); + + // calculate font size + const double minTextHeight = para.axisLabelsFontMinSize(); + //qDebug("KChart::KDChartAxesPainter::paintAxes() cv.nTxtHeight: %f minTextHeight: %f", cv.nTxtHeight, minTextHeight); + if ( minTextHeight > cv.nTxtHeight ) + cv.nTxtHeight = minTextHeight; + QFont actFont( para.axisLabelsFont() ); + if ( para.axisLabelsFontUseRelSize() ) { + actFont.setPixelSize( static_cast < int > ( cv.nTxtHeight ) ); + } + painter->setFont( actFont ); + QFontMetrics fm( painter->fontMetrics() ); + + int nLeaveOut = 0; + int nRotation = 0; + + // Draw simple string labels + // or calculate and draw nice Date/Time ruler? + QString commonDtHeader; + if( cv.autoDtLabels ){ + cv.textAlign = Qt::AlignCenter; + //qDebug(dtLow.toString("\nd.MM.yyyy - h:mm:ss" )); + //qDebug(dtHigh.toString( "d.MM.yyyy - h:mm:ss" )); + const QDate& dLow = cv.dtLow.date(); + const QTime& tLow = cv.dtLow.time(); + const QDate& dHigh = cv.dtHigh.date(); + const QTime& tHigh = cv.dtHigh.time(); + bool sameYear = dLow.year() == dHigh.year(); + bool sameMonth = sameYear && (dLow.month() == dHigh.month() ); + bool sameDay = sameMonth && (dLow.day() == dHigh.day() ); + bool sameHour = sameDay && (tLow.hour() == tHigh.hour() ); + bool sameMinute = sameHour && (tLow.minute() == tHigh.minute()); + bool sameSecond = sameMinute && (tLow.second() == tHigh.second()); + if( sameDay ){ + commonDtHeader = QString::number( dLow.day() ) + + ". " +#if COMPAT_QT_VERSION >= 0x030000 + + QDate::longMonthName( dLow.month() ) +#else + + dLow.monthName( dLow.month() ) +#endif + + ' ' + + QString::number( dLow.year() ); + if( sameHour ){ + commonDtHeader += " / " + + QString::number( tLow.hour() ) + + ':'; + if( sameMinute ){ + if( 10 > tLow.minute() ) + commonDtHeader += '0'; + commonDtHeader += QString::number( tLow.minute() ) + + ':'; + if( sameSecond ){ + if( 10 > tLow.second() ) + commonDtHeader += '0'; + commonDtHeader += QString::number( tLow.second() ); + // + // " Huston, we have a problem! " + // + // Currently we don't support milli secs + // since they will not fit into a double + // when looking at years... + // + // This will be improved in release 2.0. + // (khz, 2002/07/12) + } + else + commonDtHeader += "00"; + } + else + commonDtHeader += "00"; + } + }else if( sameMonth ) +#if COMPAT_QT_VERSION >= 0x030000 + commonDtHeader = QDate::longMonthName( dLow.month() ) +#else + commonDtHeader = dLow.monthName( dLow.month() ) +#endif + + ' ' + + QString::number( dLow.year() ); + else if( sameYear ) + commonDtHeader = QString::number( dLow.year() ); + //if( !commonDtHeader.isEmpty() ) + // qDebug(commonDtHeader); + }else{ + // make sure all label texts fit into their space + // by rotating and/or shrinking the texts + // or by leaving out some of the labels + QRegion unitedRegions; + + const bool tryLeavingOut = + ( para.axisValueLeaveOut() == KDCHART_AXIS_LABELS_AUTO_LEAVEOUT ) + || ( 0 < para.axisValueLeaveOut() ); + if( tryLeavingOut ) { + if( para.axisValueLeaveOut() + == KDCHART_AXIS_LABELS_AUTO_LEAVEOUT ) + nLeaveOut = 0; + else + nLeaveOut = para.axisValueLeaveOut(); + + } + else + nLeaveOut = 0; + int stepWidthLeaveOut = nLeaveOut+1; + int iStepsLeaveOut = 0; + + const bool tryShrinking = !para.axisLabelsDontShrinkFont(); + const double nInitialTxtHeight = cv.nTxtHeight; + + const bool tryRotating = isHorizontalAxis + && !para.axisLabelsDontAutoRotate(); + const int nInitialRotation = ( (360 > para.axisLabelsRotation()) + && (270 <= para.axisLabelsRotation()) ) + ? para.axisLabelsRotation() + : 0; + nRotation = nInitialRotation; + + bool textsDontFitIntoArea; + bool textsOverlapping; + bool textsMatching; + do { + textsDontFitIntoArea = false; + textsOverlapping = false; + textsMatching = true; + // test if all texts match without mutually overlapping + unitedRegions = QRegion(); + int align = nRotation + ? (Qt::AlignRight | Qt::AlignVCenter) // adjusting for rotation + : cv.textAlign; + QPoint anchor(200,200); + int iLeaveOut = 0; + double iLabel=0.0; + for ( QStringList::Iterator it = labelTexts->begin(); + it != labelTexts->end(); + ++it ) { + iLabel += 1.0; + if( iLeaveOut < nLeaveOut ) { + ++iLeaveOut; + } else { + iLeaveOut = 0; + anchor.setX( p2.x() + static_cast < int > ( pXDelta * (iLabel - 0.5) ) ); + anchor.setY( p2.y() + static_cast < int > ( pYDelta * (iLabel - 0.5) ) ); + + // allow for shearing and/or scaling of the painter + anchor = painter->worldMatrix().map( anchor ); + + QString text; + if( cv.isDateTime ){ +#if COMPAT_QT_VERSION >= 0x030000 + QDateTime dt( QDateTime::fromString( *it, + Qt::ISODate ) ); + text = dt.toString( formatDT ); +#else + QDateTime dt( dateTimeFromString( *it ) ); + text = dt.toString(); +#endif + }else{ + text = *it; + } + KDDrawTextRegionAndTrueRect infosKDD = + KDDrawText::measureRotatedText( painter, + nRotation, + anchor, + text, + 0, + align, + &fm, + false, + false, + 15 ); + if( infosKDD.region.boundingRect().left() + < params()->globalLeadingLeft()+1 ){ + textsMatching = false; + textsDontFitIntoArea = true; + //qDebug("too wide"); + } + //qDebug("nRotation: %i",nRotation); + QRegion sectReg; + if( nRotation ){ + //qDebug("test 1"); + sectReg = infosKDD.region.intersect( unitedRegions ); + }else{ + //qDebug("test 2"); + QRect rect( infosKDD.region.boundingRect() ); + rect.addCoords(-2,-2,2,2); + QRegion biggerRegion( rect ); + sectReg = biggerRegion.intersect( unitedRegions ); + } + if ( sectReg.isEmpty() ) + unitedRegions = unitedRegions.unite( infosKDD.region ); + else { + textsMatching = false; + textsOverlapping = true; + //qDebug("label regions are intersecting"); + break; + } + } + } +/* + if(!iAxis){ + + qDebug("nTxtHeight: "+QString::number(cv.nTxtHeight)+" nRotation: "+QString::number(nRotation)+ + " matching: "+QString(textsMatching ? "TRUE":"FALSE")); + qDebug("nUsableAxisHeight: %f, unitedRegions.boundingRect().height(): %i ", + nUsableAxisHeight, unitedRegions.boundingRect().height()); + } +*/ + if( isHorizontalAxis ) { + if( nUsableAxisHeight < unitedRegions.boundingRect().height() ){ + //textsMatching = false; + textsDontFitIntoArea = true; + } + } else { + if( nUsableAxisWidth < unitedRegions.boundingRect().width() ){ + //qDebug("textsMatching: %s",textsMatching ? "TRUE" : "FALSE"); + textsMatching = false; + textsDontFitIntoArea = true; + //qDebug("too wide"); + } + //else qDebug("not too wide"); + } + /* + if(textsMatching && !iAxis){ + qDebug("--------------------------"); + qDebug("nTxtHeight: "+QString::number(cv.nTxtHeight)+" nRotation: "+QString::number(nRotation)); + qDebug("matching"); + } + */ + if( !textsMatching ) { + bool rotatingDoesNotHelp = false; + // step 1: In case of labels being too wide + // to fit into the available space + // we try to rotate the texts in 5 steps. + // This is done for Abscissa axes only. + if ( tryRotating ) { + //qDebug("try rotating"); + // The following is designed for horizontal axes + // since we currently don't support label rotating + // on vertical axes. (khz, 2002/08/15) + if( textsDontFitIntoArea ){ + if( nRotation != nInitialRotation ){ + //textsDontFitIntoArea = false; + nRotation = nInitialRotation; + } + rotatingDoesNotHelp = true; + //qDebug("rotating does not help (a)"); + } + else{ + if( nRotation ) { + if( 270 < nRotation ) { + nRotation -= 5; + if( 270 > nRotation ) + nRotation = 270; // drawing vertically now + } else { + if( nInitialRotation ) + nRotation = nInitialRotation; + else + nRotation = 0; // reset rotation to ZERO + rotatingDoesNotHelp = true; + //qDebug("rotating does not help (b)"); + } + } else { + if( nInitialRotation ) + nRotation = nInitialRotation; + else + nRotation = 350; // (re-)start rotating with -10 + } + } + } + if ( !tryRotating || rotatingDoesNotHelp ) { + + // step 2: In case of labels being too wide and + // rotating them did not help or is forbidden + // we try to reduce the font size. + if ( tryShrinking && (minTextHeight < cv.nTxtHeight) ) { + //qDebug("try shrinking"); + cv.nTxtHeight -= 1.0; + if ( minTextHeight > cv.nTxtHeight ) + cv.nTxtHeight = minTextHeight; + } else { + + // step 3: In case reducing the font size is not possible + // any further (or is not allowed at all) we try + // to leave out some of the labels. + if( tryLeavingOut + && textsOverlapping + && (nLeaveOut+1 < static_cast < int > ( nLabels ) ) ) { + //qDebug("try leaving out"); + ++iStepsLeaveOut; + //if(!iAxis)qDebug("iStepsLeaveOut: %i", iStepsLeaveOut); + nLeaveOut = + iStepsLeaveOut*stepWidthLeaveOut - 1; + if( tryShrinking ) + cv.nTxtHeight = nInitialTxtHeight; + } + else + break; + } + if( tryShrinking ) { + actFont.setPixelSize( static_cast < int > ( cv.nTxtHeight ) ); + //qDebug("axis: cv.nTxtHeight: %f", iAxis, cv.nTxtHeight); + painter->setFont( actFont ); + fm = painter->fontMetrics(); + } + } + } +//qDebug("nLeaveOut: %i",nLeaveOut); + } while( !textsMatching ); + + if( nRotation ){ + // The following is designed for horizontal axes + // since we currently don't support label rotating + // on vertical axes. (khz, 2002/08/15) + //int oldVert = textAlign & (Qt::AlignTop | Qt::AlignBottom); + //int steepness = abs(270-nRotation); + //bool steep = (30 > steepness); + cv.textAlign = Qt::AlignRight | Qt::AlignVCenter; // adjusting for rotation + //cv.textAlign = Qt::AlignRight | Qt::AlignVCenter; + /* ( steep ? Qt::AlignVCenter : oldVert);*/ + //int dx = pXDelta / 2 - steep ? (nTxtHeight / 4) : 0; + double dx = (pXDelta / 2) - (cv.nTxtHeight / 4); + double dy = /*steep ? 0 : */(cv.nTxtHeight / 2.0); + cv.pTextsX += dx; + cv.pTextsY += dy; + } + /* + QBrush oldBrush = painter->brush(); + QRegion oldReg = painter->clipRegion();//QPainter::CoordPainter); + painter->setBrush(Qt::Dense4Pattern); + painter->setClipRegion(unitedRegions);//,QPainter::CoordPainter); + painter->drawRect(0,0,2000,1500); + painter->setClipRegion(oldReg);//,QPainter::CoordPainter); + painter->setBrush(oldBrush); + */ + /*if(!iAxis){ + qDebug("=========================="); + qDebug("nTxtHeight: "+QString::number(nTxtHeight)+" nRotation: "+QString::number(nRotation)); + qDebug(textsMatching ? "matching":"not matching"); + }*/ + } + + painter->setFont( actFont ); + fm = QFontMetrics( painter->fontMetrics() ); + + // set colour of grid pen + QPen gridPen, leaveOutGridPen; + if( para.axisShowGrid() && !bMultiRowBarChart ) + gridPen.setColor( para.axisGridColor() ); + + const int pXDeltaDiv2 = static_cast < int > ( pXDelta / 2.0 ); + const int pYDeltaDiv2 = static_cast < int > ( pYDelta / 2.0 ); + + bool bDrawAdditionalSubGridLine = false; + double pGXMicroAdjust = 0.0; + double pGYMicroAdjust = 0.0; + if ( !bTouchEdges ) { + // adjust the data values pos + p1.setX( p1.x() + pXDeltaDiv2 ); + p1.setY( p1.y() + pYDeltaDiv2 ); + p2.setX( p2.x() + pXDeltaDiv2 ); + p2.setY( p2.y() + pYDeltaDiv2 ); + // adjust the short delimiter lines pos + p2a.setX( p2a.x() + pXDeltaDiv2 ); + p2a.setY( p2a.y() + pYDeltaDiv2 ); + // adjust grid lines pos + bDrawAdditionalSubGridLine = + isHorizontalAxis && ! + params()->axisParams( + KDChartAxisParams::AxisPosRight ).axisVisible() && + !bMultiRowBarChart; + pGA.setX( pGA.x() + pXDeltaDiv2 ); + pGA.setY( pGA.y() + pYDeltaDiv2 ); + pGZ.setX( pGZ.x() + pXDeltaDiv2 ); + pGZ.setY( pGZ.y() + pYDeltaDiv2 ); + // fine-tune grid line pos for grid of vertical axis + if( KDChartAxisParams::AxisTypeNORTH == para.axisType() ) { + pGXMicroAdjust = cv.pXDeltaFactor * lineWidth / 2.0; + pGYMicroAdjust = cv.pYDeltaFactor * lineWidth / 2.0; + } + } + double x1, y1, x2, y2, xGA, yGA, xGZ, yGZ, + p1X, p1Y, p2X, p2Y, pGAX, pGAY, pGZX, pGZY, xT, yT; + + double pXSubDelimDelta = pXDelta * cv.nSubDelimFactor; + double pYSubDelimDelta = pYDelta * cv.nSubDelimFactor; + + if ( !cv.autoDtLabels + && 0.0 != cv.nSubDelimFactor + && para.axisShowSubDelimiters() + && para.axisLabelsVisible() + && !nLeaveOut ) { + QPen pen( para.axisLineColor(), static_cast < int > ( 0.5 * lineWidth ) ); + uint penWidth = pen.width(); + bool bOk = true; + + if( cv.bLogarithmic ) + cv.nSubDelimFactor = 0.1; + + while ( fabs( ( pXDelta + pYDelta ) * cv.nSubDelimFactor / 6.0 ) + <= 1.0 + penWidth + && bOk ) { + if ( 0 < penWidth ) { + --penWidth; + pen.setWidth( penWidth ); + }else{ + if( cv.bLogarithmic ){ + break; // there is nothing we can do: we allways + // want 10 sub-delims per logarithmic step + }else{ + if ( 0.5 != cv.nSubDelimFactor ) { + // emercency: reduce number of sub-scaling + cv.nSubDelimFactor = 0.5; + + pXSubDelimDelta = pXDelta * cv.nSubDelimFactor; + pYSubDelimDelta = pYDelta * cv.nSubDelimFactor; + } else + bOk = false; + } + } + } + if ( bOk ) { + x1 = p1.x(); + y1 = p1.y(); + x2 = p2a.x(); + y2 = p2a.y(); + xGA = pGA.x(); + yGA = pGA.y(); + xGZ = pGZ.x(); + yGZ = pGZ.y(); + p1X = x1; + p1Y = y1; + p2X = x2; + p2Y = y2; + pGAX = xGA; + pGAY = yGA; + pGZX = xGZ; + pGZY = yGZ; + + // set up grid pen for drawing the sub-grid lines + const QPen oldGridPen( gridPen ); + if ( para.axisShowGrid() ) { + gridPen.setColor( para.axisGridSubColor() ); + gridPen.setWidth( gridSubLineWidth ); + gridPen.setStyle( para.axisGridSubStyle() ); + } + const QPen oldPen( painter->pen() ); + painter->setPen( pen ); + double nSubDelim = ( labelTexts->count() - 1 ) + / cv.nSubDelimFactor; + + //qDebug("subDelim: %f", + modf( nSubDelim, &nSubDelim ); + + int logarithCnt = 1; + double xLogarithOffs = 0; + double yLogarithOffs = 0; + double dDummy; + double mainDelim = 0.0; + bool paint = true; + + for ( double iDelim = 1.0; + iDelim <= nSubDelim + 1.0; + iDelim += 1.0, logarithCnt++ ) { + // test if it is a sub or a main delimiter + if ( mainDelim > 0.0 ) + paint = true; + else + paint = false; + + if ( cv.bLogarithmic ) + { + if ( logarithCnt == 11 ) + { + xLogarithOffs += + pXDelta * log10( 10*cv.nSubDelimFactor*10 ); + yLogarithOffs += + pYDelta * log10( 10*cv.nSubDelimFactor*10 ); + logarithCnt=1; + } + + pXSubDelimDelta = + pXDelta * log10( 10*cv.nSubDelimFactor*logarithCnt ); + pYSubDelimDelta = + pYDelta * log10( 10*cv.nSubDelimFactor*logarithCnt ); + } + + if ( para.axisShowGrid() && !bMultiRowBarChart) { + // draw the sub grid line + if( 0.0 != modf((iDelim-1.0) * cv.nSubDelimFactor, &dDummy) ) + + saveDrawLine( *painter, + QPoint( static_cast<int>( pGAX - pGXMicroAdjust ), + static_cast<int>( pGAY - pGYMicroAdjust ) ), + QPoint( static_cast<int>( pGZX - pGXMicroAdjust ), + static_cast<int>( pGZY - pGYMicroAdjust ) ), + gridPen ); + + if( cv.bLogarithmic ){ + pGAX = xGA + pXSubDelimDelta + xLogarithOffs; + pGAY = yGA + pYSubDelimDelta + yLogarithOffs; + pGZX = xGZ + pXSubDelimDelta + xLogarithOffs; + pGZY = yGZ + pYSubDelimDelta + yLogarithOffs; + }else{ + pGAX = xGA + iDelim * pXSubDelimDelta; + pGAY = yGA + iDelim * pYSubDelimDelta; + pGZX = xGZ + iDelim * pXSubDelimDelta; + pGZY = yGZ + iDelim * pYSubDelimDelta; + /* + if( !modf(iDelim * cv.nSubDelimFactor, &dDummy) ){ + pGAX = xGA + (iDelim * cv.nSubDelimFactor) * pXDelta; + pGAY = yGA + (iDelim * cv.nSubDelimFactor) * pYDelta; + pGZX = xGZ + (iDelim * cv.nSubDelimFactor) * pXDelta; + pGZY = yGZ + (iDelim * cv.nSubDelimFactor) * pYDelta; + } + */ + } + } + + + // draw the short delimiter line + // PENDING: Michel - make sure not to draw the sub-delimiters over the main ones. + // by testing if it is a sub delimiter or a main one + if ( paint ) + painter->drawLine( QPoint( static_cast<int>( p1X ), static_cast<int>( p1Y ) ), + QPoint( static_cast<int>( p2X ), static_cast<int>( p2Y ) ) ); + + mainDelim += 1.0; + + + if( cv.bLogarithmic ){ + p1X = x1 + pXSubDelimDelta + xLogarithOffs; + p1Y = y1 + pYSubDelimDelta + yLogarithOffs; + p2X = x2 + pXSubDelimDelta + xLogarithOffs; + p2Y = y2 + pYSubDelimDelta + yLogarithOffs; + }else{ + p1X = x1 + iDelim * pXSubDelimDelta; + p1Y = y1 + iDelim * pYSubDelimDelta; + p2X = x2 + iDelim * pXSubDelimDelta; + p2Y = y2 + iDelim * pYSubDelimDelta; + } + + if ( mainDelim >= nSubDelim/(labelTexts->count() -1) ) + mainDelim = 0.0; + + + } // for + // draw additional sub grid line + if( bDrawAdditionalSubGridLine + && para.axisShowGrid() ) { + + saveDrawLine( *painter, + QPoint( static_cast<int>( pGAX - pGXMicroAdjust ), + static_cast<int>( pGAY - pGYMicroAdjust ) ), + QPoint( static_cast<int>( pGZX - pGXMicroAdjust ), + static_cast<int>( pGZY - pGYMicroAdjust ) ), + gridPen ); + + } + painter->setPen( oldPen ); + gridPen = oldGridPen; + } + } + x1 = p1.x(); + y1 = p1.y(); + x2 = p2.x(); + y2 = p2.y(); + xGA = pGA.x(); + yGA = pGA.y(); + xGZ = pGZ.x(); + yGZ = pGZ.y(); + p1X = x1; + p1Y = y1; + p2X = x2; + p2Y = y2; + pGAX = xGA; + pGAY = yGA; + pGZX = xGZ; + pGZY = yGZ; + xT = cv.pTextsX; + yT = cv.pTextsY; + // set up grid pen for drawing the normal grid lines + if ( para.axisShowGrid() ) { + gridPen.setWidth( gridLineWidth ); + gridPen.setStyle( para.axisGridStyle() ); + // if axis not visible draw the 1st grid line too + if( !para.axisLineVisible() ) + saveDrawLine( *painter, cv.orig, cv.dest, gridPen ); + } + if( nLeaveOut ) { + leaveOutGridPen = gridPen; + leaveOutGridPen.setWidth( gridLineWidth / 2 ); + leaveOutGridPen.setStyle( Qt::DotLine ); + } + // ========================================================= + // || The labels and delimiters and grid printing loops || + // ========================================================= + // + double iLabel = 0.0; + if( cv.autoDtLabels ) + { + /* + qDebug("\ndtLow: %i %i %i %i:%i:%i", + dtLow.date().year(), + dtLow.date().month(), + dtLow.date().day(), + dtLow.time().hour(), + dtLow.time().minute(), + dtLow.time().second()); + qDebug("dtHigh: %i %i %i %i:%i:%i", + dtHigh.date().year(), + dtHigh.date().month(), + dtHigh.date().day(), + dtHigh.time().hour(), + dtHigh.time().minute(), + dtHigh.time().second()); + */ + int pXD = static_cast <int> (cv.pXDelimDeltaFaktor * 1.25 * (cv.nTxtHeight+4)); + int pYD = static_cast <int> (cv.pYDelimDeltaFaktor * 1.25 * (cv.nTxtHeight+4)); + int orgXD = pXD; + int orgYD = pYD; + cv.pTextsW = fabs( (0.0 == pXDelta) ? pXD : pXDelta ); + cv.pTextsH = fabs( (0.0 == pYDelta) ? pYD : pYDelta ); + + double pSecX = x1; + double pSecY = y1; + bool secPaint= false; + double pMinX = x1; + double pMinY = y1; + bool minPaint= false; + double pHourX = x1; + double pHourY = y1; + bool hourPaint= false; + double pDayX = x1; + double pDayY = y1; + bool dayPaint= false; + /* khz: currently not used + double pWeekX = x1; + double pWeekY = y1; + bool weekPaint= false; + */ + double pMonthX = x1; + double pMonthY = y1; + bool monthPaint= false; + /*double pQuarterX = x1; + double pQuarterY = y1; + bool minPaint= false; + */ + double pYearX = x1; + double pYearY = y1; + bool yearPaint= false; + + double pXYDelta = fabs( pXDelta ) + fabs( pYDelta ); + + if( 0.0 == para.trueAxisDeltaPixels() ) + ( ( KDChartAxisParams& ) para ).setTrueAxisDeltaPixels( QMIN(_logicalWidth, _logicalHeight) / 150 ); + + bool dtGoDown = cv.dtLow > cv.dtHigh; + int mult = dtGoDown ? -1 : 1; + const QDateTime& startDt = dtGoDown ? cv.dtHigh : cv.dtLow; + + ( ( KDChartAxisParams& ) para ).setAxisDtLowPos( x1, y1 ); + // adjust stored dt-low and scale settings + ( ( KDChartAxisParams& ) para ).setTrueAxisDtLow( startDt ); + ( ( KDChartAxisParams& ) para ).setTrueAxisDtScale( cv.dtDeltaScale ); + + int gridDX = pGZ.x() - pGA.x(); + int gridDY = pGZ.y() - pGA.y(); + if ( para.axisShowGrid() ) { + gridPen.setColor( para.axisGridColor() ); + gridPen.setWidth( gridLineWidth ); + gridPen.setStyle( para.axisGridStyle() ); + } + QPen subGridPen( gridPen.color(), 1, para.axisGridStyle() ); + QPen subSubGridPen( gridPen.color(), 1, para.axisGridSubStyle() ); + QPen pen = subGridPen; + + QDateTime dt( startDt ); + QDateTime newDt( startDt ); + for( uint i=1; i <= nLabels; ++i ){ + switch( cv.dtDeltaScale ) { + case KDChartAxisParams::ValueScaleSecond: + dtAddSecs( dt, 1 * mult, newDt ); + break; + case KDChartAxisParams::ValueScaleMinute: + dtAddSecs( dt, 60 * mult, newDt ); + break; + case KDChartAxisParams::ValueScaleHour: + dtAddSecs( dt, 3600 * mult, newDt ); + break; + case KDChartAxisParams::ValueScaleDay: + dtAddDays( dt, 1 * mult, newDt ); + break; + case KDChartAxisParams::ValueScaleWeek: + dtAddDays( dt, 7 * mult, newDt ); + break; + case KDChartAxisParams::ValueScaleMonth: + dtAddMonths( dt,1 * mult, newDt ); + break; + case KDChartAxisParams::ValueScaleQuarter: + dtAddMonths( dt,3 * mult, newDt ); + break; + case KDChartAxisParams::ValueScaleYear: + dtAddYears( dt, 1 * mult, newDt ); + break; + default: + dtAddDays( dt, 1 * mult, newDt ); + break; + } + const QDateTime& testDt + = dtGoDown + ? ( ( newDt < cv.dtLow ) + ? cv.dtLow + : newDt ) + : ( ( newDt > cv.dtHigh ) + ? cv.dtHigh + : newDt ); + /* + qDebug(" dt: %i %i %i %i:%i:%i", + newDt.date().year(),newDt.date().month(),newDt.date().day(), + newDt.time().hour(),newDt.time().minute(),newDt.time().second()); + qDebug("testDt: %i %i %i %i:%i:%i", + testDt.date().year(),testDt.date().month(),testDt.date().day(), + testDt.time().hour(),testDt.time().minute(),testDt.time().second()); + */ + bool endLoop = (i == nLabels) || (&testDt != &newDt); + + secPaint = ( KDChartAxisParams::ValueScaleSecond >= cv.dtDeltaScale ) && + ( testDt.time().second() != dt.time().second() || + ( endLoop && ((pSecX != x1) || (pSecY != y1)))); + minPaint = ( KDChartAxisParams::ValueScaleMinute >= cv.dtDeltaScale ) && + ( testDt.time().minute() != dt.time().minute() || + ( endLoop && ((pMinX != x1) || (pMinY != y1)))); + hourPaint = ( KDChartAxisParams::ValueScaleHour >= cv.dtDeltaScale ) && + ( testDt.time().hour() != dt.time().hour() || + ( endLoop && ((pHourX != x1) || (pHourY != y1)))); + dayPaint = ( KDChartAxisParams::ValueScaleDay >= cv.dtDeltaScale ) && + ( testDt.date().day() != dt.date().day() || + ( endLoop && ((pDayX != x1) || (pDayY != y1)))); + /* khz: currently not used + weekPaint = ( KDChartAxisParams::ValueScaleWeek >= cv.dtDeltaScale ) && + ( testDt.date().week() != dt.date().week() || + ( endLoop && ((pWeekX != x1) || (pWeekY != y1)))); + */ + monthPaint = ( KDChartAxisParams::ValueScaleMonth >= cv.dtDeltaScale ) && + ( testDt.date().month() != dt.date().month() || + ( endLoop && ((pMonthX != x1) || (pMonthY != y1)))); + yearPaint = ( KDChartAxisParams::ValueScaleYear >= cv.dtDeltaScale ) && + ( testDt.date().year() != dt.date().year() || + ( endLoop && ((pYearX != x1) || (pYearY != y1)))); + + p1X = x1 + iLabel * pXDelta; + p1Y = y1 + iLabel * pYDelta; + p2X = p1X + pXDelta; + p2Y = p1Y + pYDelta; + pXD = orgXD; + pYD = orgYD; + + if( endLoop ){ + ( ( KDChartAxisParams& ) para ).setAxisDtHighPos( p1X, p1Y ); + // adjust stored dt-high settings + ( ( KDChartAxisParams& ) para ).setTrueAxisDtHigh( dt ); + } + pen = subGridPen; + /* + // old code: just draw the seconds without any tests + // (not wise to do that when supporting sec1000 + // and the like some day...) + if( newDt.time().second() != dt.time().second() ){ + painter->drawLine( QPoint( p1X, p1Y ), QPoint( p1X+pXD, p1Y+pYD ) ); + painter->drawLine( QPoint( p1X+pXD, p1Y+pYD ), + QPoint( p1X+pXD + pXDelta, p1Y+pYD + pYDelta ) ); + painter->drawText( p1X+pXD-orgXD, p1Y+pYD-orgYD, + pTextsW, pTextsH, + textAlign | Qt::DontClip, + QString::number( dt.time().second() ) ); + pXD += orgXD; + pYD += orgYD; + } + */ + if( secPaint ){ + painter->drawLine( QPoint( static_cast<int>( pSecX+pXD ), + static_cast<int>( pSecY+pYD ) ), + QPoint( static_cast<int>( p2X + pXD ), + static_cast<int>( p2Y + pYD ) ) ); + if( (pXDelta/2.0 < p2X - pSecX) || (pYDelta/2.0 < p2Y - pSecY) ){ + QPen oldPen( painter->pen() ); + painter->setPen( QPen( labelsColor ) ); + painter->drawText( static_cast<int>( pSecX+pXD-orgXD ), + static_cast<int>( pSecY+pYD-orgYD ), + static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pSecX))), + static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pSecY))), + cv.textAlign | Qt::DontClip, + QString::number( dt.time().second() ) ); + painter->setPen( oldPen ); + if ( para.axisShowGrid() ){ + + saveDrawLine( *painter, + QPoint( static_cast<int>( pSecX ), + static_cast<int>( pSecY ) ), + QPoint( static_cast<int>( pSecX + gridDX ), + static_cast<int>( pSecY + gridDY ) ), + pen ); + pen = gridPen; + } + if( !minPaint || pMinX != pSecX || pMinY != pSecY ){ + painter->drawLine( QPoint( static_cast<int>( pSecX ), + static_cast<int>( pSecY ) ), + QPoint( static_cast<int>( pSecX+pXD ), + static_cast<int>( pSecY+pYD ) ) ); + } + } + if( endLoop && !minPaint ) + painter->drawLine( QPoint( static_cast<int>( p2X ), + static_cast<int>( p2Y ) ), + QPoint( static_cast<int>( p2X+pXD ), + static_cast<int>( p2Y+pYD ) ) ); + pSecX = p1X + pXDelta; + pSecY = p1Y + pYDelta; + pXD += orgXD; + pYD += orgYD; + } + if( minPaint ){ + painter->drawLine( QPoint( static_cast<int>( pMinX+pXD ), + static_cast<int>( pMinY+pYD ) ), + QPoint( static_cast<int>( p2X + pXD ), + static_cast<int>( p2Y + pYD ) ) ); + if( (pXDelta/2.0 < p2X - pMinX) || (pYDelta/2.0 < p2Y - pMinY) ){ + QPen oldPen( painter->pen() ); + painter->setPen( QPen( labelsColor ) ); + painter->drawText( static_cast<int>( pMinX+pXD-orgXD ), + static_cast<int>( pMinY+pYD-orgYD ), + static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pMinX)) ), + static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pMinY)) ), + cv.textAlign | Qt::DontClip, + QString::number( dt.time().minute() ) ); + painter->setPen( oldPen ); + if ( para.axisShowGrid() ){ + if( !secPaint && 10 < pXYDelta ){ + saveDrawLine( *painter, + QPoint( static_cast<int>( pMinX+pXDelta/2 ), + static_cast<int>( pMinY+pYDelta/2 ) ), + QPoint( static_cast<int>( pMinX+pXDelta/2 + gridDX ), + static_cast<int>( pMinY+pYDelta/2 + gridDY ) ), + subSubGridPen ); + } + saveDrawLine( *painter, + QPoint( static_cast<int>( pMinX ), + static_cast<int>( pMinY ) ), + QPoint( static_cast<int>( pMinX + gridDX ), + static_cast<int>( pMinY + gridDY ) ), + pen ); + pen = gridPen; + } + if( !hourPaint || pHourX != pMinX || pHourY != pMinY ){ + painter->drawLine( QPoint( static_cast<int>( pMinX ), + static_cast<int>( pMinY ) ), + QPoint( static_cast<int>( pMinX+pXD ), + static_cast<int>( pMinY+pYD ) ) ); + } + } + if( endLoop && !hourPaint ) + painter->drawLine( QPoint( static_cast<int>( p2X ), + static_cast<int>( p2Y ) ), + QPoint( static_cast<int>( p2X+pXD ), + static_cast<int>( p2Y+pYD ) ) ); + pMinX = p1X + pXDelta; + pMinY = p1Y + pYDelta; + pXD += orgXD; + pYD += orgYD; + } + if( hourPaint ){ + painter->drawLine( QPoint( static_cast<int>( pHourX+pXD ), + static_cast<int>( pHourY+pYD ) ), + QPoint( static_cast<int>( p2X + pXD ), + static_cast<int>( p2Y + pYD ) ) ); + /* + qDebug("line"); + qDebug("pXDelta / 2.0 : %f", pXDelta/2.0); + qDebug("p2X - pHourX : %f", p2X - pHourX); + */ + if( (pXDelta/2.0 < p2X - pHourX) || (pYDelta/2.0 < p2Y - pHourY) ){ + /* + qDebug("pHourX %f", pHourX ); + qDebug(" +pXD %i", pXD ); + qDebug(" -orgXD %i", orgXD); + qDebug("pHourY %f", pHourY ); + qDebug(" +pYD %i", pYD ); + qDebug(" -orgYD %i", orgYD); + */ + QPen oldPen( painter->pen() ); + painter->setPen( QPen( labelsColor ) ); + painter->drawText( static_cast<int>( pHourX+pXD-orgXD ), + static_cast<int>( pHourY+pYD-orgYD ), + static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pHourX))), + static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pHourY))), + cv.textAlign | Qt::DontClip, + QString::number( dt.time().hour() ) ); + painter->setPen( oldPen ); + if ( para.axisShowGrid() ){ + if( !minPaint && 10 < pXYDelta ){ + saveDrawLine( *painter, + QPoint( static_cast<int>( pHourX+pXDelta/2 ), + static_cast<int>( pHourY+pYDelta/2 ) ), + QPoint( static_cast<int>( pHourX+pXDelta/2 + gridDX ), + static_cast<int>( pHourY+pYDelta/2 + gridDY ) ), + subSubGridPen ); + } + saveDrawLine( *painter, + QPoint( static_cast<int>( pHourX ), + static_cast<int>( pHourY ) ), + QPoint( static_cast<int>( pHourX + gridDX ), + static_cast<int>( pHourY + gridDY ) ), + pen ); + pen = gridPen; + } + if( !dayPaint || pDayX != pHourX || pDayY != pHourY ){ + painter->drawLine( QPoint( static_cast<int>( pHourX ), + static_cast<int>( pHourY ) ), + QPoint( static_cast<int>( pHourX+pXD ), + static_cast<int>( pHourY+pYD ) ) ); + } + } + if( endLoop && !dayPaint ) + painter->drawLine( QPoint( static_cast<int>( p2X ), + static_cast<int>( p2Y ) ), + QPoint( static_cast<int>( p2X+pXD ), + static_cast<int>( p2Y+pYD ) ) ); + pHourX = p1X + pXDelta; + pHourY = p1Y + pYDelta; + pXD += orgXD; + pYD += orgYD; + } + if( dayPaint ){ + painter->drawLine( QPoint( static_cast<int>( pDayX+pXD ), + static_cast<int>( pDayY+pYD ) ), + QPoint( static_cast<int>( p2X + pXD ), + static_cast<int>( p2Y + pYD ) ) ); + if( (pXDelta/2.0 < p2X - pDayX) || (pYDelta/2.0 < p2Y - pDayY) ){ + QPen oldPen( painter->pen() ); + painter->setPen( QPen( labelsColor ) ); + painter->drawText( static_cast<int>( pDayX+pXD-orgXD ), + static_cast<int>( pDayY+pYD-orgYD ), + static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pDayX)) ), + static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pDayY)) ), + cv.textAlign | Qt::DontClip, + QString::number( dt.date().day() ) ); + painter->setPen( oldPen ); + /* khz: currently not used + if( !weekPaint || pWeekX != pDayX || pWeekY != pDayY ) + */ + if ( para.axisShowGrid() ){ + if( !hourPaint && 10 < pXYDelta ){ + saveDrawLine( *painter, + QPoint( static_cast<int>( pDayX+pXDelta/2 ), + static_cast<int>( pDayY+pYDelta/2 ) ), + QPoint( static_cast<int>( pDayX+pXDelta/2 + gridDX ), + static_cast<int>( pDayY+pYDelta/2 + gridDY ) ), + subSubGridPen ); + } + saveDrawLine( *painter, + QPoint( static_cast<int>( pDayX ), + static_cast<int>( pDayY ) ), + QPoint( static_cast<int>( pDayX + gridDX ), + static_cast<int>( pDayY + gridDY ) ), + pen ); + pen = gridPen; + } + if( !monthPaint || pMonthX != pDayX || pMonthY != pDayY ){ + painter->drawLine( QPoint( static_cast<int>( pDayX ), + static_cast<int>( pDayY ) ), + QPoint( static_cast<int>( pDayX+pXD ), + static_cast<int>( pDayY+pYD ) ) ); + } + } + /* khz: currently not used + if( endLoop && !weekPaint ) + */ + if( endLoop && !monthPaint ) + painter->drawLine( QPoint( static_cast<int>( p2X ), + static_cast<int>( p2Y ) ), + QPoint( static_cast<int>( p2X+pXD ), + static_cast<int>( p2Y+pYD ) ) ); + pDayX = p1X + pXDelta; + pDayY = p1Y + pYDelta; + pXD += orgXD; + pYD += orgYD; + } + /* khz: currently unused + if( weekPaint ){ + painter->drawLine( QPoint( pWeekX+pXD, pWeekY+pYD ), + QPoint( p2X + pXD, p2Y + pYD ) ); + if( (pXDelta/2.0 < p2X - pWeekX) || (pYDelta/2.0 < p2Y - pWeekY) ){ + QPen oldPen( painter->pen() ); + painter->setPen( QPen( labelsColor ) ); + painter->drawText( pWeekX+pXD-orgXD, pWeekY+pYD-orgYD, + painter->setPen( oldPen ); + fabs((0.0 == pXDelta) ? pTextsW : (p2X - pWeekX)), + fabs((0.0 == pYDelta) ? pTextsH : (p2Y - pWeekY)), + textAlign | Qt::DontClip, + QString::number( dt.date().week() ) ); + if ( para.axisShowGrid() ){ + if( !dayPaint && 40 < pXYDelta ){ + // draw 7 lines: + //saveDrawLine( *painter, + // QPoint( pWeekX+pXDelta/2, + // pWeekY+pYDelta/2 ), + // QPoint( pWeekX+pXDelta/2 + gridDX, + // pWeekY+pYDelta/2 + gridDY ), + // subSubGridPen ); + } + saveDrawLine( *painter, + QPoint( pWeekX, + pWeekY ), + QPoint( pWeekX + gridDX, + pWeekY + gridDY ), + pen ); + pen = gridPen; + } + if( !monthPaint || pMonthX != pDayX || pMonthY != pDayY ){ + painter->drawLine( QPoint( pWeekX, pWeekY ), QPoint( pWeekX+pXD, pWeekY+pYD ) ); + } + } + if( endLoop && !monthPaint ) + painter->drawLine( QPoint( p2X, p2Y ), QPoint( p2X+pXD, p2Y+pYD ) ); + pWeekX = p1X + pXDelta; + pWeekY = p1Y + pYDelta; + pXD += orgXD; + pYD += orgYD; + } + */ + if( monthPaint ){ + painter->drawLine( QPoint( static_cast<int>( pMonthX+pXD ), + static_cast<int>( pMonthY+pYD ) ), + QPoint( static_cast<int>( p2X + pXD ), + static_cast<int>( p2Y + pYD ) ) ); + if( (pXDelta/2.0 < p2X - pMonthX) || (pYDelta/2.0 < p2Y - pMonthY) ){ + QPen oldPen( painter->pen() ); + painter->setPen( QPen( labelsColor ) ); + painter->drawText( static_cast<int>( pMonthX+pXD-orgXD ), + static_cast<int>( pMonthY+pYD-orgYD ), + static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pMonthX)) ), + static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pMonthY)) ), + cv.textAlign | Qt::DontClip, + QString::number( dt.date().month() ) ); + painter->setPen( oldPen ); + if ( para.axisShowGrid() ){ + /* khz: currently unused + if( !weekPaint && + && 10 < pXYDelta ){ + saveDrawLine( *painter, + QPoint( pMonthX+pXDelta/2, + pMonthY+pYDelta/2 ), + QPoint( pMonthX+pXDelta/2 + gridDX, + pMonthY+pYDelta/2 + gridDY ), + subSubGridPen ); + } + */ + saveDrawLine( *painter, + QPoint( static_cast<int>( pMonthX ), + static_cast<int>( pMonthY ) ), + QPoint( static_cast<int>( pMonthX + gridDX ), + static_cast<int>( pMonthY + gridDY ) ), + pen ); + pen = gridPen; + } + if( !yearPaint || pYearX != pMonthX || pYearY != pMonthY ){ + painter->drawLine( QPoint( static_cast<int>( pMonthX ), + static_cast<int>( pMonthY ) ), + QPoint( static_cast<int>( pMonthX+pXD ), + static_cast<int>( pMonthY+pYD ) ) ); + } + } + if( endLoop && !yearPaint ) + painter->drawLine( QPoint( static_cast<int>( p2X ), + static_cast<int>( p2Y ) ), + QPoint( static_cast<int>( p2X+pXD ), + static_cast<int>( p2Y+pYD ) ) ); + pMonthX = p1X + pXDelta; + pMonthY = p1Y + pYDelta; + pXD += orgXD; + pYD += orgYD; + } + if( yearPaint ){ + painter->drawLine( QPoint( static_cast<int>( pYearX+pXD ), + static_cast<int>( pYearY+pYD ) ), + QPoint( static_cast<int>( p2X + pXD ), + static_cast<int>( p2Y + pYD ) ) ); + if( (pXDelta/2.0 < p2X - pYearX) || (pYDelta/2.0 < p2Y - pYearY) ){ + QPen oldPen( painter->pen() ); + painter->setPen( QPen( labelsColor ) ); + painter->drawText( static_cast<int>( pYearX+pXD-orgXD ), + static_cast<int>( pYearY+pYD-orgYD ), + static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pYearX)) ), + static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pYearY)) ), + cv.textAlign | Qt::DontClip, + QString::number( dt.date().year() ) ); + painter->setPen( oldPen ); + if ( para.axisShowGrid() ){ + if( !monthPaint && 10 < pXYDelta ){ + saveDrawLine( *painter, + QPoint( static_cast<int>( pYearX+pXDelta/2 ), + static_cast<int>( pYearY+pYDelta/2 ) ), + QPoint( static_cast<int>( pYearX+pXDelta/2 + gridDX ), + static_cast<int>( pYearY+pYDelta/2 + gridDY ) ), + subSubGridPen ); + } + saveDrawLine( *painter, + QPoint( static_cast<int>( pYearX ), + static_cast<int>( pYearY ) ), + QPoint( static_cast<int>( pYearX + gridDX ), + static_cast<int>( pYearY + gridDY ) ), + pen ); + pen = gridPen; + } + painter->drawLine( QPoint( static_cast<int>( pYearX ), + static_cast<int>( pYearY ) ), + QPoint( static_cast<int>( pYearX+pXD ), + static_cast<int>( pYearY+pYD ) ) ); + } + if( endLoop ) + painter->drawLine( QPoint( static_cast<int>( p2X ), + static_cast<int>( p2Y ) ), + QPoint( static_cast<int>( p2X+pXD ), + static_cast<int>( p2Y+pYD ) ) ); + pYearX = p1X + pXDelta; + pYearY = p1Y + pYDelta; + pXD += orgXD; + pYD += orgYD; + } + if( &testDt != &newDt ) + break; + dt = newDt; + iLabel += 1.0; + } + if( !commonDtHeader.isEmpty() ){ + QPen oldPen( painter->pen() ); + painter->setPen( QPen( labelsColor ) ); + painter->drawText( static_cast<int>( x1 + pXD ), static_cast<int>( y1 + pYD ), + commonDtHeader ); + painter->setPen( oldPen ); + } + }else{ + int iLeaveOut = nLeaveOut; + QString label; + for ( QStringList::Iterator labelIter = labelTexts->begin(); + labelIter != labelTexts->end(); + ++labelIter ) { + QDateTime dt; + if( cv.isDateTime ){ +#if COMPAT_QT_VERSION >= 0x030000 + dt = QDateTime::fromString( *labelIter, + Qt::ISODate ); + label = dt.toString( formatDT ); +#else + dt = dateTimeFromString( *labelIter ); + label = dt.toString(); +#endif + }else{ + label = *labelIter; + } + + if( iLeaveOut < nLeaveOut ) + ++iLeaveOut; + else + iLeaveOut = 0; + //Pending Michel: test if the user implicitely wants to get rid + //of the non fractional values delimiters and grid lines. + // axisDigitsBehindComma == 0 and the user implicitely + // setAxisShowFractionalValuesDelimiters() to false + bool showDelim = para.axisShowFractionalValuesDelimiters(); + if ( para.axisShowGrid() && !bMultiRowBarChart ) { + if ( !label.isNull() || showDelim ){ + if( !iLeaveOut ) + // draw the main grid line + + saveDrawLine( *painter, + QPoint( static_cast<int>( pGAX - pGXMicroAdjust ), + static_cast<int>( pGAY - pGYMicroAdjust ) ), + QPoint( static_cast<int>( pGZX - pGXMicroAdjust ), + static_cast<int>( pGZY - pGYMicroAdjust ) ), + gridPen ); + + else if( para.axisShowSubDelimiters() ) + // draw a thin sub grid line instead of main line + saveDrawLine( *painter, + QPoint( static_cast<int>( pGAX - pGXMicroAdjust ), + static_cast<int>( pGAY - pGYMicroAdjust ) ), + QPoint( static_cast<int>( pGZX - pGXMicroAdjust ), + static_cast<int>( pGZY - pGYMicroAdjust ) ), + leaveOutGridPen ); + } + } + if ( para.axisLabelsVisible() ) { + if( !iLeaveOut ) { + /*PENDING Michel: those points should not be redrawn if sub-delimiters are drawn + *drawing the submarkers + * make it visible or not + *In the case we have a null label - axisDigitsBehindComma is implicitely set to 0 - + *also paint or dont paint the delimiter corresponding to this label - default is paint. + */ + if ( !label.isNull() || showDelim ) + painter->drawLine( QPoint( static_cast<int>( p1X ), + static_cast<int>( p1Y ) ), + QPoint( static_cast<int>( p2X ), + static_cast<int>( p2Y ) ) ); + + cv.pLastX = p1X; + cv.pLastY = p1Y; + QPen oldPen( painter->pen() ); + painter->setPen( QPen( labelsColor ) ); + if( para.axisLabelsDontShrinkFont() + && isHorizontalAxis + && (Qt::AlignHCenter == (cv.textAlign & Qt::AlignHCenter)) ) { + double w = fm.width( label ) + 4.0; + double x0 = cv.pTextsX + cv.pTextsW / 2.0; + + painter->drawText( static_cast<int>( x0 - w / 2.0 ), + static_cast<int>( cv.pTextsY ), + static_cast<int>( w ), + static_cast<int>( cv.pTextsH ), + cv.textAlign, label ); + } else { + if( nRotation ){ + KDDrawText::drawRotatedText( + painter, + nRotation, + painter->worldMatrix().map( + QPoint( static_cast<int>( cv.pTextsX ), + static_cast<int>( cv.pTextsY ) ) ), + label, + 0, + cv.textAlign, + false, + &fm, + screenOutput,screenOutput,0, + screenOutput ); + } else { + // Pending Michel draw the axis labels + painter->drawText( static_cast<int>( cv.pTextsX ), + static_cast<int>( cv.pTextsY ), + static_cast<int>( cv.pTextsW ), + static_cast<int>( cv.pTextsH ), + cv.textAlign | Qt::DontClip, + label ); + + // debugging text rect + /* + painter->drawRect(static_cast <int>(cv.pTextsX), + static_cast <int>(cv.pTextsY), + static_cast <int> (nUsableAxisWidth), + static_cast <int> (nUsableAxisHeight)); + */ + } + } + painter->setPen( oldPen ); + } + } + + + if( cv.isDateTime ){ + if( labelTexts->begin() == labelIter ){ + ((KDChartAxisParams&)para).setAxisDtLowPos( + pGAX - pGXMicroAdjust, + pGAY - pGYMicroAdjust ); + // adjust stored dt-low settings + ( ( KDChartAxisParams& ) para ).setTrueAxisDtLow( dt ); + }else{ + ((KDChartAxisParams&)para).setAxisDtHighPos( + pGAX - pGXMicroAdjust, + pGAY - pGYMicroAdjust ); + // adjust stored dt-high settings + ( ( KDChartAxisParams& ) para ).setTrueAxisDtHigh( dt ); + } + } + + iLabel += 1.0; + p1X = x1 + iLabel * pXDelta; + p1Y = y1 + iLabel * pYDelta; + p2X = x2 + iLabel * pXDelta; + p2Y = y2 + iLabel * pYDelta; + cv.pTextsX = xT + iLabel * pXDelta; + cv.pTextsY = yT + iLabel * pYDelta; + + pGAX = xGA + iLabel * pXDelta; + pGAY = yGA + iLabel * pYDelta; + pGZX = xGZ + iLabel * pXDelta; + pGZY = yGZ + iLabel * pYDelta; + /* + pGAX = xGA + iLabel * pXSubDelimDelta / cv.nSubDelimFactor; + pGAY = yGA + iLabel * pYSubDelimDelta / cv.nSubDelimFactor; + pGZX = xGZ + iLabel * pXSubDelimDelta / cv.nSubDelimFactor; + pGZY = yGZ + iLabel * pYSubDelimDelta / cv.nSubDelimFactor; + */ + } + } + + + // adjust zero-line start, if not starting at origin + if ( cv.bSteadyCalc && + ( para.axisValuesDecreasing() || + (0.0 != para.trueAxisLow()) ) ) { + //double x = p1.x(); + double x = 0.0; + /* we have to find the *real* X axis position, + this is NOT always the p1.x() as it is the + case for left2 or right2 axes. [cmw, 12/01/2005] */ + if (cv.basicPos==KDChartAxisParams::AxisPosRight) + x = static_cast<double>(_dataRect.right()); + else + x = static_cast<double>(_dataRect.left()); + double y = p1.y(); + double mult = para.trueAxisLow() / para.trueAxisDelta(); + x -= mult * pXDelta; + y -= mult * pYDelta; + axisZeroLineStartX = x; + axisZeroLineStartY = y; + //qDebug( "axisZeroLineStartX %f, axisZeroLineStartY %f", + // axisZeroLineStartX, axisZeroLineStartY ); + } + + painter->setClipping( oldClippingFlag ); + } // if( nLabels ) + + // draw zero-line (Ok, this might be overwritten by axes + // cause those are drawn after all labels and grid and + // zero-line(s) has been painted, see code below, starting + // with "// draw all the axes". + if ( cv.bSteadyCalc && !cv.isDateTime ) { + ( ( KDChartAxisParams& ) para ).setAxisZeroLineStart( axisZeroLineStartX, axisZeroLineStartY ); + double axisZeroLineStart; + int minCoord, maxCoord; + double xFactor, yFactor; + switch( cv.basicPos ){ + case KDChartAxisParams::AxisPosLeft: + xFactor = 1.0; + yFactor = 0.0; + axisZeroLineStart = axisZeroLineStartY; + minCoord = QMIN( cv.orig.y(), cv.dest.y() ); + maxCoord = QMAX( cv.orig.y(), cv.dest.y() ); + + break; + case KDChartAxisParams::AxisPosRight: + xFactor = -1.0; + yFactor = 0.0; + axisZeroLineStart = axisZeroLineStartY; + minCoord = QMIN( cv.orig.y(), cv.dest.y() ); + maxCoord = QMAX( cv.orig.y(), cv.dest.y() ); + break; + case KDChartAxisParams::AxisPosTop: + xFactor = 0.0; + yFactor = 1.0; + axisZeroLineStart = axisZeroLineStartX; + minCoord = QMIN( cv.orig.x(), cv.dest.x() ); + maxCoord = QMAX( cv.orig.x(), cv.dest.x() ); + break; + case KDChartAxisParams::AxisPosBottom: + xFactor = 0.0; + yFactor = -1.0; + axisZeroLineStart = axisZeroLineStartX; + minCoord = QMIN( cv.orig.x(), cv.dest.x() ); + maxCoord = QMAX( cv.orig.x(), cv.dest.x() ); + break; + default: + xFactor = 0.0; + yFactor = 0.0; + axisZeroLineStart = 0.0; + minCoord = 0; + maxCoord = 0; + } + if( axisZeroLineStart >= minCoord && + axisZeroLineStart <= maxCoord ){ + QPoint pZ0( static_cast<int>( para.axisZeroLineStartX() ), + static_cast<int>( para.axisZeroLineStartY() ) ); + QPoint pZ( static_cast<int>( para.axisZeroLineStartX() + + xFactor * _dataRect.width() ), + static_cast<int>( para.axisZeroLineStartY() + + yFactor * _dataRect.height() ) ); + //qDebug("------"); + saveDrawLine( *painter, + pZ0, + pZ, + QPen( para.axisZeroLineColor(), + lineWidth ) ); + } + } + + } + + } + + // Drawing all the axes lines: +/* + // 1. test if the standard axes are share one or several corner points + // if yes, we first draw a polyline using a "Qt::MiterJoin" PenJoinStyle + // to make sure the corners are filled + internal__KDChart__CalcValues& cv1 = calcVal[ KDChartAxisParams::AxisPosLeft ]; + internal__KDChart__CalcValues& cv2 = calcVal[ KDChartAxisParams::AxisPosBottom ]; + const KDChartAxisParams& pa1 = params()->axisParams( KDChartAxisParams::AxisPosLeft ); + const KDChartAxisParams& pa2 = params()->axisParams( KDChartAxisParams::AxisPosBottom ); +qDebug("\n\nx1: %i y1: %i w1: %i\nx2: %i y2: %i w2: %i", +cv1.orig.x(), cv1.orig.y(), pa1.axisTrueLineWidth(), +cv2.orig.x(), cv2.orig.y(), pa2.axisTrueLineWidth() ); + if( cv1.orig == cv2.orig ){ + const QColor c1( pa1.axisLineColor() ); + const QColor c2( pa2.axisLineColor() ); + const QPoint pA( cv1.dest ); + const QPoint pB( cv1.orig ); + const QPoint pC( cv2.dest ); + QPen pen( QColor( (c1.red() + c2.red()) /2, + (c1.green() + c2.green())/2, + (c1.blue() + c2.blue()) /2 ), + QMIN(pa1.axisTrueLineWidth(), pa2.axisTrueLineWidth()) ); + pen.setJoinStyle( Qt::MiterJoin ); + painter->setPen( pen ); + QPointArray a; + a.putPoints( 0, 3, pA.x(),pA.y(), pB.x(),pB.y(), pC.x(),pC.y() ); + painter->drawPolyline( a ); +qDebug("done\n" ); + } +*/ + // 2. draw the axes using their normal color + for( iAxis = 0; iAxis < KDCHART_MAX_AXES; ++iAxis ){ + internal__KDChart__CalcValues& cv = calcVal[iAxis]; + const KDChartAxisParams & para = params()->axisParams( iAxis ); + if( cv.processThisAxis && para.axisLineVisible() ){ + painter->setPen( QPen( para.axisLineColor(), + para.axisTrueLineWidth() ) ); + int x = cv.dest.x(); + if( 2.0 >= QABS(cv.pLastX - x) ) + x = static_cast < int > ( cv.pLastX ); + int y = cv.dest.y(); + if( 2.0 >= QABS(cv.pLastY - y) ) + y = static_cast < int > ( cv.pLastY ); + painter->drawLine( cv.orig, QPoint(x,y) ); + } + } + + painter->restore(); +} + + +double fastPow10( int x ) +{ + double res = 1.0; + if( 0 <= x ){ + for( int i = 1; i <= x; ++i ) + res *= 10.0; + }else{ + for( int i = -1; i >= x; --i ) + res /= 10.0; + } + return res; +} +double fastPow10( double x ) +{ + return pow(10.0, x); +} + + +/** + Calculates the actual label texts for one axis. + + \note When calling this function the actual area size for this + axis must be set, this means you may only call it when + \c KDChartPainter::setupGeometry() has been called before. + + \param painter the QPainter onto which the chart should be painted + \param data the data that will be displayed as a chart + \param params the KDChartParams that were specified globally + \param axisNumber the number of this axis (used in some params structures) + \param averageValueP1000 (average height+width of the prtbl. area) / 1000 + \param basicPos the basic axis position returned by + KDChartAxisParams::basicAxisPos() + \param orig the axis start point + \param delimLen the length of one delimiter mark + \param (all others) the reference parameters to be returned + by this function + */ +/**** static ****/ +void KDChartAxesPainter::calculateLabelTexts( + QPainter* painter, + const KDChartTableDataBase& data, + const KDChartParams& params, + uint axisNumber, + double averageValueP1000, + double delimLen, + // start of return parameters + KDChartAxisParams::AxisPos& basicPos, + QPoint& orig, + QPoint& dest, + double& pXDeltaFactor, + double& pYDeltaFactor, + double& pXDelimDeltaFaktor, + double& pYDelimDeltaFaktor, + double& nSubDelimFactor, + double& pDelimDelta, + double& nTxtHeight, + double& pTextsX, + double& pTextsY, + double& pTextsW, + double& pTextsH, + int& textAlign, + bool& isLogarithmic, + bool& isDateTime, + bool& autoDtLabels, + QDateTime& dtLow, + QDateTime& dtHigh, + KDChartAxisParams::ValueScale& dtDeltaScale, + bool adjustTheValues, + double trueDelta, + double trueDeltaPix ) +{ +//qDebug("\nentering KDChartAxesPainter::calculateLabelTexts() : nTxtHeight: "+QString::number(nTxtHeight)); + const KDChartAxisParams & para = params.axisParams( axisNumber ); + + // store whether the labels are to be drawn in reverted order + const bool bDecreasing = para.axisValuesDecreasing(); + + basicPos = KDChartAxisParams::basicAxisPos( axisNumber ); + + pXDeltaFactor = 0.0; + pYDeltaFactor = 0.0; + pXDelimDeltaFaktor = 0.0; + pYDelimDeltaFaktor = 0.0; + int axisLength; + switch ( basicPos ) { + case KDChartAxisParams::AxisPosBottom: { + axisLength = para.axisTrueAreaRect().width(); + orig = bDecreasing + ? para.axisTrueAreaRect().topRight() + : para.axisTrueAreaRect().topLeft(); + dest = bDecreasing + ? para.axisTrueAreaRect().topLeft() + : para.axisTrueAreaRect().topRight(); + pYDelimDeltaFaktor = 1.0; + pXDeltaFactor = bDecreasing ? -1.0 : 1.0; + //qDebug("\nsetting pXDeltaFactor for axis %x", axisNumber); + //qDebug(bDecreasing ? "bDecreasing = TRUE" : "bDecreasing = FALSE"); + //qDebug("pXDeltaFactor = %f\n",pXDeltaFactor); + } + break; + case KDChartAxisParams::AxisPosLeft: { + axisLength = para.axisTrueAreaRect().height(); + orig = bDecreasing + ? para.axisTrueAreaRect().topRight() + : para.axisTrueAreaRect().bottomRight(); + dest = bDecreasing + ? para.axisTrueAreaRect().bottomRight() + : para.axisTrueAreaRect().topRight(); + pXDelimDeltaFaktor = -1.0; + pYDeltaFactor = bDecreasing ? 1.0 : -1.0; + } + break; + case KDChartAxisParams::AxisPosTop: { + axisLength = para.axisTrueAreaRect().width(); + orig = bDecreasing + ? para.axisTrueAreaRect().bottomRight() + : para.axisTrueAreaRect().bottomLeft(); + dest = bDecreasing + ? para.axisTrueAreaRect().bottomLeft() + : para.axisTrueAreaRect().bottomRight(); + pYDelimDeltaFaktor = -1.0; + pXDeltaFactor = bDecreasing ? -1.0 : 1.0; + } + break; + case KDChartAxisParams::AxisPosRight: { + axisLength = para.axisTrueAreaRect().height(); + orig = bDecreasing + ? para.axisTrueAreaRect().topLeft() + : para.axisTrueAreaRect().bottomLeft(); + dest = bDecreasing + ? para.axisTrueAreaRect().bottomLeft() + : para.axisTrueAreaRect().topLeft(); + pXDelimDeltaFaktor = 1.0; + pYDeltaFactor = bDecreasing ? 1.0 : -1.0; + } + break; + default: { + axisLength = 0; + qDebug( "IMPLEMENTATION ERROR: KDChartAxesPainter::paintAxes() unhandled enum value." ); + } + break; + } + + // which dataset(s) is/are represented by this axis? + uint dataset, dataset2, chart; + if ( !params.axisDatasets( axisNumber, dataset, dataset2, chart ) ) { + dataset = KDCHART_ALL_DATASETS; + dataset2 = KDCHART_ALL_DATASETS; + chart = 0; + //qDebug("\nautomatic set values: chart: %u,\ndataset: %u, dataset2: %u", + //chart, dataset, dataset2); + } + // which dataset(s) with mode DataEntry (or mode ExtraLinesAnchor, resp.) + // is/are represented by this axis? + uint dataDataset, dataDataset2; + if( params.findDatasets( KDChartParams::DataEntry, + KDChartParams::ExtraLinesAnchor, + dataDataset, + dataDataset2, + chart ) ) { + // adjust dataDataset in case MORE THAN ONE AXIS + // is representing THIS CHART + if( ( KDCHART_ALL_DATASETS != dataset + && KDCHART_NO_DATASET != dataset ) + || ( KDCHART_ALL_DATASETS != dataDataset + && KDCHART_NO_DATASET != dataDataset ) ){ + int ds = (KDCHART_ALL_DATASETS != dataset) + ? dataset + : 0; + int dds = (KDCHART_ALL_DATASETS != dataDataset) + ? dataDataset + : 0; + dataDataset = QMAX( ds, dds ); + } + if( ( KDCHART_ALL_DATASETS != dataset2 + && KDCHART_NO_DATASET != dataset2 ) + || ( KDCHART_ALL_DATASETS != dataDataset2 + && KDCHART_NO_DATASET != dataDataset2 ) ){ + int ds2 = (KDCHART_ALL_DATASETS != dataset2) + ? dataset2 + : KDCHART_MAX_AXES-1; + int dds2 = (KDCHART_ALL_DATASETS != dataDataset2) + ? dataDataset2 + : KDCHART_MAX_AXES-1; + dataDataset2 = QMIN( ds2, dds2 ); + } + } + else { + // Should not happen + qDebug( "IMPLEMENTATION ERROR: findDatasets( DataEntry, ExtraLinesAnchor ) should *always* return true. (b)" ); + dataDataset = KDCHART_ALL_DATASETS; + } + //qDebug("\naxisNumber: %x\nchart: %x\ndataset: %x, dataset2: %x,\ndataDataset: %x, dataDataset2: %x", + //axisNumber, chart, dataset, dataset2, dataDataset, dataDataset2); + + if ( para.axisLabelsFontUseRelSize() ){ + nTxtHeight = para.axisLabelsFontRelSize() + * averageValueP1000; +//qDebug("using rel. size in KDChartAxesPainter::calculateLabelTexts() : nTxtHeight: "+QString::number(nTxtHeight)); + }else { + QFontInfo info( para.axisLabelsFont() ); + nTxtHeight = info.pointSize(); +//qDebug("using FIXED size in KDChartAxesPainter::calculateLabelTexts() : nTxtHeight: "+QString::number(nTxtHeight)); + } + + const KDChartEnums::NumberNotation notation = para.axisLabelsNotation(); + const int behindComma = para.axisDigitsBehindComma(); + const int divPow10 = para.axisLabelsDivPow10(); + const QString decimalPoint = para.axisLabelsDecimalPoint(); + const QString thousandsPoint = para.axisLabelsThousandsPoint(); + const QString prefix = para.axisLabelsPrefix(); + const QString postfix = para.axisLabelsPostfix(); + const int totalLen = para.axisLabelsTotalLen(); + const QChar padFill = para.axisLabelsPadFill(); + const bool blockAlign = para.axisLabelsBlockAlign(); + + QStringList labelTexts; + int colNum = para.labelTextsDataRow(); + bool bDone = true; + switch ( para.axisLabelTextsFormDataRow() ) { + case KDChartAxisParams::LabelsFromDataRowYes: { + // Take whatever is in the specified column (even if not a string) + int trueBehindComma = -1; + QVariant value; + for ( uint iDataset = 0; iDataset < data.usedRows(); iDataset++ ) { + if( data.cellCoord( iDataset, colNum, value, 1 ) ){ + if( QVariant::String == value.type() ) + labelTexts.append( value.toString() ); + else { + labelTexts.append( applyLabelsFormat( value.toDouble(), + divPow10, + behindComma, + para.axisValueDelta(), + trueBehindComma, + notation, + decimalPoint, + thousandsPoint, + prefix, + postfix, + totalLen, + padFill, + blockAlign ) ); + + } + } + } + break; + } + case KDChartAxisParams::LabelsFromDataRowGuess: { + QVariant value; + for ( uint iDataset = 0; iDataset < data.usedRows(); iDataset++ ) { + if( data.cellCoord( iDataset, colNum, value, 1 ) ){ + if( QVariant::String == value.type() ){ + const QString sVal( value.toString() ); + if( !sVal.isEmpty() && !sVal.isNull() ) + labelTexts.append( sVal ); + } + }else{ + labelTexts.clear(); + bDone = false; + break; + } + } + break; + } + case KDChartAxisParams::LabelsFromDataRowNo: { + bDone = false; + break; + } + default: + // Should not happen + qDebug( "KDChart: Unknown label texts source" ); + } + + // if necessary adjust text params *including* the steady value calc setting + const bool dataCellsHaveSeveralCoordinates = + (KDCHART_ALL_DATASETS == dataDataset) + ? data.cellsHaveSeveralCoordinates() + : data.cellsHaveSeveralCoordinates( dataDataset, dataDataset2 ); + if( dataCellsHaveSeveralCoordinates && !para.axisSteadyValueCalc() ) + ((KDChartParams&)params).setAxisLabelTextParams( + axisNumber, + true, + KDCHART_AXIS_LABELS_AUTO_LIMIT, + KDCHART_AXIS_LABELS_AUTO_LIMIT, + KDCHART_AXIS_LABELS_AUTO_DELTA, + para.axisLabelsDigitsBehindComma() );// NOTE: This sets MANY other params to default values too! + + + const KDChartParams::ChartType params_chartType + = ( 0 == chart ) + ? params.chartType() + : params.additionalChartType(); + + + // store whether we are calculating Ordinate-like axis values + const bool bSteadyCalc = para.axisSteadyValueCalc(); + + // store whether logarithmic calculation is wanted + isLogarithmic = bSteadyCalc && + (KDChartParams::Line == params_chartType) && + (KDChartAxisParams::AxisCalcLogarithmic == para.axisCalcMode()); + + //qDebug(bSteadyCalc ? "bSteadyCalc":"NOT bSteadyCalc"); + //qDebug(isLogarithmic? "isLogarithmic":"NOT isLogarithmic"); + + // store whether this is a vertical axis or a horizontal axis + const bool bVertAxis = (KDChartAxisParams::AxisPosLeft == basicPos) || + (KDChartAxisParams::AxisPosRight == basicPos); + + // store the coordinate number to be used for this axis + const int coordinate = bVertAxis ? 1 : 2; + + // store whether our coordinates are double or QDateTime values + const QVariant::Type valueType = + (KDCHART_ALL_DATASETS == dataDataset) + ? data.cellsValueType( coordinate ) + : data.cellsValueType( dataDataset, dataDataset2, coordinate ); + isDateTime = valueType == QVariant::DateTime; + bool bIsDouble = valueType == QVariant::Double; + + autoDtLabels = isDateTime && ( KDCHART_AXIS_LABELS_AUTO_DATETIME_FORMAT + == para.axisLabelsDateTimeFormat() ); + + if( autoDtLabels || bSteadyCalc ) + ( ( KDChartAxisParams& ) para ).setAxisLabelsTouchEdges( true ); + + bool bStatistical = KDChartParams::HiLo == params_chartType + || KDChartParams::BoxWhisker == params_chartType; + + if ( !bVertAxis && KDChartParams::BoxWhisker == params_chartType + && ! para.axisLabelStringCount() ) { + uint ds1 = (KDCHART_ALL_DATASETS == dataDataset) + ? 0 + : dataDataset; + uint ds2 = (KDCHART_ALL_DATASETS == dataDataset) + ? data.usedRows() - 1 + : dataDataset2; + for (uint i = ds1; i <= ds2; ++i) + labelTexts.append( + QObject::tr( "Series " ) + QString::number( i + 1 ) ); + bDone = true; + } + + double nLow = 1.0 + bSteadyCalc;// ? 0.0 : data.colsScrolledBy(); + double nHigh = 10.0; + double nDelta = 1.0; + if ( !bDone ) { + bDone = true; + + // look if exact label specification was made via limits and delta + if ( ! isLogarithmic + && ! para.axisLabelStringCount() + && ! ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueStart() ) + && ! ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() ) + && ! ( para.axisValueStart() == para.axisValueEnd() ) + && ! ( KDCHART_AXIS_LABELS_AUTO_DELTA == para.axisValueDelta() ) + && ! ( 0.0 == para.axisValueDelta() ) ) { + nLow = para.axisValueStart().toDouble(); + nHigh = para.axisValueEnd().toDouble(); + nDelta = para.axisValueDelta(); + int behindComma = para.axisDigitsBehindComma(); + int trueBehindComma = -1; + bool upwards = (nLow < nHigh); + if( upwards != (0.0 < nDelta) ) + nDelta *= -1.0; + double nVal = nLow; + bDone = false; + bool bShowVeryLastLabel = false; + //qDebug("\n nLow: %f, nHigh: %f, nDelta: %f", nLow, nHigh, nDelta ); + while( bShowVeryLastLabel || (upwards ? (nVal <= nHigh) : (nVal >= nHigh)) ){ + //qDebug("nVal : %f", nVal ); + labelTexts.append( applyLabelsFormat( nVal, + divPow10, + behindComma, + nDelta, + trueBehindComma, + notation, + decimalPoint, + thousandsPoint, + prefix, + postfix, + totalLen, + padFill, + blockAlign ) ); + nVal += nDelta; + //qDebug("nVal-neu: %f", nVal ); + if( ! (upwards ? (nVal <= nHigh) : (nVal >= nHigh)) ){ + // work around a rounding error: show the last label, even if not nVal == nHigh is not matching exactly + if( bShowVeryLastLabel ) + bShowVeryLastLabel = false; + else{ + QString sHigh( applyLabelsFormat( nHigh, + divPow10, + behindComma, + nDelta, + trueBehindComma, + notation, + decimalPoint, + thousandsPoint, + prefix, + postfix, + totalLen, + padFill, + blockAlign ) ); + QString sValue( applyLabelsFormat( nVal, + divPow10, + behindComma, + nDelta, + trueBehindComma, + notation, + decimalPoint, + thousandsPoint, + prefix, + postfix, + totalLen, + padFill, + blockAlign ) ); + bShowVeryLastLabel = (sValue == sHigh); + //qDebug("test: sHigh: "+sHigh+" sValue: "+sValue ); + } + } + bDone = true; + } + ( ( KDChartAxisParams& ) para ).setTrueAxisLowHighDelta( nLow, nHigh, nDelta ); + //qDebug("\n[Z-0] nLow: %f, nHigh: %f, nDelta: %f", nLow, nHigh, nDelta ); + } // look if a string list was specified + else if ( para.axisLabelStringCount() ) { + int nLabels = bSteadyCalc + ? para.axisLabelStringCount() + : bStatistical ? data.usedRows() : data.usedCols(); + calculateBasicTextFactors( nTxtHeight, para, averageValueP1000, + basicPos, orig, delimLen, nLabels, + // start of return parameters + pDelimDelta, + pTextsX, pTextsY, pTextsW, pTextsH, + textAlign ); + bool useShortLabels = false; + QStringList tmpList( para.axisLabelStringList() ); + + // find start- and/or end-entry + int iStart = 0; + int iEnd = para.axisLabelStringCount() - 1; + if( ! ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueStart() ) + || ! ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() ) ) { + const bool testStart = !( QVariant::String == para.axisValueStart().type() ); + const bool testEnd = !( QVariant::String == para.axisValueEnd().type() ); + QString sStart = testStart + ? para.axisValueStart().toString().upper() + : QString::null; + QString sEnd = testEnd + ? para.axisValueEnd().toString().upper() + : QString::null; + + uint i = 0; + for ( QStringList::Iterator it = tmpList.begin(); + it != tmpList.end(); ++it, ++i ) { + if ( 0 == iStart && + 0 == QString::compare( sStart, ( *it ).upper() ) ) { + iStart = i; + } + if ( 0 == QString::compare( sEnd, ( *it ).upper() ) ) { + iEnd = i; + } + } + } + + // check text widths to ensure all the entries will fit + // into the available space + if ( para.axisLabelStringCount() + && para.axisShortLabelsStringCount() + && para.axisLabelStringList() != para.axisShortLabelsStringList() ) { + QFont font( para.axisLabelsFont() ); + if ( para.axisLabelsFontUseRelSize() ) + font.setPixelSize( static_cast < int > ( nTxtHeight ) ); + painter->setFont( font ); + QFontMetrics fm( painter->fontMetrics() ); + + QStringList::Iterator it = tmpList.begin(); + for ( int i = 0; i < nLabels; ++i ) { + if ( it != tmpList.end() ) { + if ( fm.width( *it ) > pTextsW ) { + useShortLabels = true; + break; + } + ++it; + } + } + } + if ( useShortLabels ) + tmpList = para.axisShortLabelsStringList(); + else + tmpList = para.axisLabelStringList(); + + // prepare transfering the strings into the labelTexts list + double ddelta + = ( KDCHART_AXIS_LABELS_AUTO_DELTA == para.axisValueDelta() ) + ? 1.0 + : para.axisValueDelta(); + modf( ddelta, &ddelta ); + bool positive = ( 0.0 <= ddelta ); + int delta = static_cast < int > ( fabs( ddelta ) ); + // find 1st significant entry + QStringList::Iterator it = positive + ? tmpList.begin() + : tmpList.fromLast(); + if ( positive ) + for ( int i = 0; i < (int)tmpList.count(); ++i ) { + if ( i >= iStart ) + break; + ++it; + } + else + for ( int i = tmpList.count() - 1; i >= 0; --i ) { + if ( i <= iEnd ) + break; + --it; + } + // transfer the strings + int meter = delta; + int i2 = positive ? iStart : iEnd; + for ( int iLabel = 0; iLabel < nLabels; ) { + if ( positive ) { + if ( it == tmpList.end() ) { + it = tmpList.begin(); + i2 = 0; + } + } else { + if ( it == tmpList.begin() ) { + it = tmpList.end(); + i2 = tmpList.count(); + } + } + if ( ( positive && i2 >= iStart ) + || ( !positive && i2 <= iEnd ) ) { + if ( meter >= delta ) { + labelTexts << *it; + ++iLabel; + meter = 1; + } else { + meter += 1; + } + } + if ( positive ) { + if ( i2 == iEnd ) { + it = tmpList.begin(); + i2 = 0; + } else { + ++it; + ++i2; + } + } else { + if ( i2 == iStart ) { + it = tmpList.end(); + i2 = tmpList.count(); + } else { + --it; + --i2; + } + } + } + } else { + // find out if the associated dataset contains only strings + // if yes, we will take these as label texts + uint dset = ( dataset == KDCHART_ALL_DATASETS ) ? 0 : dataset; + //qDebug("\ndset: %u", dset); + bDone = false; + QVariant value; + for ( uint col = 0; col < data.usedCols(); ++col ) { + if( data.cellCoord( dset, col, value, coordinate ) ){ + if( QVariant::String == value.type() ){ + const QString sVal = value.toString(); + if( !sVal.isEmpty() && !sVal.isNull() ){ + labelTexts.append( sVal ); + bDone = true; + } + }else{ + labelTexts.clear(); + bDone = false; + break; + } + } + } + } + } + + + if ( bDone ) { + // Some strings were found, now let us see which of them are + // actually to be taken right now. + // + // + // F E A T U R E P L A N N E D F O R F U T U R E . . . + // + // + + } + else { + // No strings were found, so let us either calculate the texts + // based upon the numerical values of the associated dataset(s) + // or just compose some default texts... + if ( data.usedCols() && bSteadyCalc ) { + // double values for numerical coordinates + double nLow = 0.01; + double nHigh = 0.0; + double orgLow = 0.0; + double orgHigh = 0.0; + double nDelta = 0.0; + double nDist = 0.0; + + // VERTICAL axes support three modes: + enum { Normal, Stacked, Percent } mode; + + if( bVertAxis ){ + switch ( params_chartType ) { + case KDChartParams::Bar: + if ( KDChartParams::BarStacked + == params.barChartSubType() ) + mode = Stacked; + else if ( KDChartParams::BarPercent + == params.barChartSubType() ) + mode = Percent; + else + mode = Normal; + break; + case KDChartParams::Line: + if ( KDChartParams::LineStacked + == params.lineChartSubType() ) + mode = Stacked; + else if ( KDChartParams::LinePercent + == params.lineChartSubType() ) + mode = Percent; + else + mode = Normal; + break; + case KDChartParams::Area: + if ( KDChartParams::AreaStacked + == params.areaChartSubType() ) + mode = Stacked; + else if ( KDChartParams::AreaPercent + == params.areaChartSubType() ) + mode = Percent; + else + mode = Normal; + break; + case KDChartParams::HiLo: + case KDChartParams::BoxWhisker: + mode = Normal; + break; + case KDChartParams::Polar: + if ( KDChartParams::PolarStacked + == params.polarChartSubType() ) + mode = Stacked; + else if ( KDChartParams::PolarPercent + == params.polarChartSubType() ) + mode = Percent; + else + mode = Normal; + break; + default: { + // Should not happen + qDebug( "IMPLEMENTATION ERROR: Unknown params_chartType in calculateLabelTexts()" ); + mode = Normal; + } + } + }else + mode = Normal; // this axis is not a vertical axis + + uint nLabels = 200; + + // find highest and lowest value of associated dataset(s) + bool bOrdFactorsOk = false; + + if( adjustTheValues ){ + nDelta = fabs( trueDelta ); + pDelimDelta = trueDeltaPix; + nLow = QMIN( para.trueAxisLow(), para.trueAxisHigh() ); + //qDebug("\nsearching: para.trueAxisLow() %f para.trueAxisHigh() %f",para.trueAxisLow(),para.trueAxisHigh()); + double orgLow( nLow ); + modf( nLow / nDelta, &nLow ); + nLow *= nDelta; + if ( nLow > orgLow ) + nLow -= nDelta; + if ( 0.0 < nLow && 0.0 >= orgLow ) + nLow = 0.0; + nHigh = nLow; + double dx = fabs( pXDeltaFactor * pDelimDelta ); + double dy = fabs( pYDeltaFactor * pDelimDelta ); + double x = 0.0; + double y = 0.0; + nLabels = 1; + if( axisLength ){ + do{ + ++nLabels; + nHigh += nDelta; + x += dx; + y += dy; + }while( x < axisLength && y < axisLength ); + nHigh -= nDelta; + --nLabels; + } + nDist = nHigh - nLow; + bOrdFactorsOk = true; + + } + + if( !bOrdFactorsOk ){ + const bool bAutoCalcStart = + ( Percent != mode ) + && ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueStart() ); + const bool bAutoCalcEnd = + ( Percent != mode ) + && ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() ); + + if( !bIsDouble && !isDateTime ){ + // no data at all: let us use our default 0..10 range + nLow = 0.0; + nHigh = 10.0; + nDist = 10.0; + nDelta = 1.0; + nSubDelimFactor = 0.5; + bIsDouble = true; + bOrdFactorsOk = true; + }else if( mode == Percent ){ + // precentage mode: we use a 0..100 range + nLow = 0.0; + nHigh = 100.0; + nDist = 100.0; + nDelta = 10.0; + nSubDelimFactor = 0.25; + bOrdFactorsOk = true; + }else{ + //qDebug("\ngo: nLow: %f nHigh: %f", nLow, nHigh ); + // get the raw start value + const bool bStackedMode = (mode == Stacked); + if( bAutoCalcStart ){ + if ( dataDataset == KDCHART_ALL_DATASETS ) { + if( bIsDouble ){ + nLow = bStackedMode + ? QMIN( data.minColSum(), 0.0 ) + : data.minValue( coordinate, + isLogarithmic ); + //qDebug("\n1: nLow: %f", nLow ); + + }else{ + dtLow = data.minDtValue( coordinate ); + } + } else { + if( bIsDouble ){ + nLow = bStackedMode + ? QMIN( data.minColSum( dataDataset, dataDataset2 ), + 0.0 ) + : data.minInRows( dataDataset,dataDataset2, + coordinate, + isLogarithmic ); + }else{ + dtLow = data.minDtInRows( dataDataset,dataDataset2, + coordinate ); + } + } + }else{ + if( bIsDouble ){ + if( QVariant::Double == para.axisValueStart().type() ) + nLow = para.axisValueStart().toDouble(); + }else{ + if( QVariant::DateTime == para.axisValueStart().type() ) + dtLow = para.axisValueStart().toDateTime(); + } + } + + // get the raw end value + if( bAutoCalcEnd ){ + if ( dataDataset == KDCHART_ALL_DATASETS ) { + if( bIsDouble ){ + nHigh = bStackedMode + ? QMAX( data.maxColSum(), 0.0 ) + : data.maxValue( coordinate ); + }else{ + dtHigh = data.maxDtValue( coordinate ); + } + } else { + if( bIsDouble ) + nHigh = bStackedMode + ? QMAX( data.maxColSum( dataDataset, dataDataset2 ), + 0.0 ) + : data.maxInRows( dataDataset,dataDataset2, + coordinate ); + else + dtHigh = data.maxDtInRows( dataDataset,dataDataset2, + coordinate ); + } + //qDebug("\nbAutoCalcEnd:\n nLow: %f\n nHigh: %f", nLow, nHigh ); + }else{ + if( bIsDouble ){ + if( QVariant::Double == para.axisValueEnd().type() ) + nHigh = para.axisValueEnd().toDouble(); + }else{ + if( QVariant::DateTime == para.axisValueEnd().type() ) + dtHigh = para.axisValueEnd().toDateTime(); + } + } + } + + + //qDebug("\nnew: nLow: %f nHigh: %f", nLow, nHigh ); + + if( bIsDouble ) { + if( DBL_MAX == nLow + || ( ( 0.0 == nHigh || 0 == nHigh ) + && ( 0.0 == nLow || 0 == nLow ) ) ) { + // qDebug("NO values or all values have ZERO value, showing 0.0 - 10.0 span"); + nLow = 0.0; + nHigh = 10.0; + nDist = 10.0; + nDelta = 1.0; + nSubDelimFactor = 0.5; + bOrdFactorsOk = true; + //qDebug("nLow: %f, nHigh: %f", nLow, nHigh); + }else if( nLow == nHigh ){ + // if both values are equal, but NOT Zero + // -> move the appropriate one to Zero + if( nLow < 0.0 ) + nHigh = 0.0; + else + nLow = 0.0; + //qDebug("nLow: %f, nHigh: %f", nLow, nHigh); + }else if( nHigh < nLow ){ + // make sure nLow is <= nHigh + double nTmp = nLow; + nLow = nHigh; + nHigh = nTmp; + } + } else if( isDateTime ){ + bool toggleDts = dtLow > dtHigh; + if( toggleDts ) { + QDateTime dt( dtLow ); + dtLow = dtHigh; + dtHigh = dt; + } + double secDist = dtLow.secsTo( dtHigh ); + secDist += (dtHigh.time().msec() - dtLow.time().msec()) / 1000.0; + const double aDist = fabs( secDist ); + const double secMin = 60.0; + const double secHour = 60.0 * secMin; + const double secDay = 24.0 * secHour; + // + // we temporarily disable week alignment until bug + // is fixed (1st week of year must not start in the + // preceeding year but rather be shown incompletely) + // + // (khz, 2003/10/10) + // + //const int secWeek = 7 * secDay; + const double secMonth = 30 * secDay; // approx. + const double secYear = 12 * secMonth; // approx. + if( 2.0*secMin > aDist ) + dtDeltaScale = KDChartAxisParams::ValueScaleSecond; + else if( 2.0*secHour > aDist ) + dtDeltaScale = KDChartAxisParams::ValueScaleMinute; + else if( 2.0*secDay > aDist ) + dtDeltaScale = KDChartAxisParams::ValueScaleHour; + // khz: else if( 2*secWeek > aDist ) + // khz: dtDeltaScale = KDChartAxisParams::ValueScaleDay; + else if( 2.0*secMonth > aDist ) + dtDeltaScale = KDChartAxisParams::ValueScaleDay; + // khz: dtDeltaScale = KDChartAxisParams::ValueScaleWeek; + + else if( 2.0*secYear > aDist ) + dtDeltaScale = KDChartAxisParams::ValueScaleMonth; + else if( 10.0*secYear > aDist ) + dtDeltaScale = KDChartAxisParams::ValueScaleQuarter; + else + dtDeltaScale = KDChartAxisParams::ValueScaleYear; + + + //const int yearLow = dtLow.date().year(); + const int monthLow = dtLow.date().month(); + // khz: currently unused: const int dowLow = dtLow.date().dayOfWeek(); + const int dayLow = dtLow.date().day(); + const int hourLow = dtLow.time().hour(); + const int minuteLow = dtLow.time().minute(); + const int secondLow = dtLow.time().second(); + + //const int yearHigh = dtHigh.date().year(); + const int monthHigh = dtHigh.date().month(); + // khz: currently unused: const int dowHigh = dtHigh.date().dayOfWeek(); + const int hourHigh = dtHigh.time().hour(); + const int minuteHigh = dtHigh.time().minute(); + const int secondHigh = dtHigh.time().second(); + int yearLowD = 0; + int monthLowD = 0; + int dayLowD = 0; + int hourLowD = 0; + int minuteLowD = 0; + int secondLowD = 0; + int yearHighD = 0; + int monthHighD = 0; + int dayHighD = 0; + int hourHighD = 0; + int minuteHighD = 0; + int secondHighD = 0; + bool gotoEndOfMonth = false; + switch( dtDeltaScale ) { + case KDChartAxisParams::ValueScaleSecond: + //qDebug("\nKDChartAxisParams::ValueScaleSecond"); + if( 5.0 < aDist ){ + secondLowD = secondLow % 5; + if( secondHigh % 5 ) + secondHighD = 5 - secondHigh % 5; + } + break; + case KDChartAxisParams::ValueScaleMinute: + //qDebug("\nKDChartAxisParams::ValueScaleMinute"); + secondLowD = secondLow; + secondHighD = 59-secondHigh; + break; + case KDChartAxisParams::ValueScaleHour: + //qDebug("\nKDChartAxisParams::ValueScaleHour"); + minuteLowD = minuteLow; + secondLowD = secondLow; + minuteHighD = 59-minuteHigh; + secondHighD = 59-secondHigh; + break; + case KDChartAxisParams::ValueScaleDay: + //qDebug("\nKDChartAxisParams::ValueScaleDay"); + hourLowD = hourLow; + minuteLowD = minuteLow; + secondLowD = secondLow; + hourHighD = 23-hourHigh; + minuteHighD = 59-minuteHigh; + secondHighD = 59-secondHigh; + break; + case KDChartAxisParams::ValueScaleWeek: + //qDebug("\nKDChartAxisParams::ValueScaleWeek"); + // khz: week scaling is disabled at the moment + /* + dayLowD = dowLow - 1; + hourLowD = hourLow; + minuteLowD = minuteLow; + secondLowD = secondLow; + if( 7 > dowHigh ) + dayHighD = 7 - dowHigh + 1; + */ + break; + case KDChartAxisParams::ValueScaleMonth: + //qDebug("\nKDChartAxisParams::ValueScaleMonth"); + if( 1 < dayLow ) + dayLowD = dayLow - 1; + hourLowD = hourLow; + minuteLowD = minuteLow; + secondLowD = secondLow; + gotoEndOfMonth = true; + break; + case KDChartAxisParams::ValueScaleQuarter: + //qDebug("\nKDChartAxisParams::ValueScaleQuarter"); + monthLowD = ( monthLow - 1 ) % 3; + dayLowD = dayLow; + hourLowD = hourLow; + minuteLowD = minuteLow; + secondLowD = secondLow; + if( ( monthHigh - 1 ) % 3 ) + monthHighD = 3 - ( monthHigh - 1 ) % 3; + gotoEndOfMonth = true; + break; + case KDChartAxisParams::ValueScaleYear: + //qDebug("\nKDChartAxisParams::ValueScaleYear"); + monthLowD = monthLow; + dayLowD = dayLow; + hourLowD = hourLow; + minuteLowD = minuteLow; + secondLowD = secondLow; + if( 12 > monthHigh ) + monthHighD = 12 - monthHigh; + gotoEndOfMonth = true; + break; + default: + /* NOOP */ + break; + } + dtLow = dtLow.addSecs( -1 * (secondLowD + 60*minuteLowD + 3600*hourLowD) ); + dtLow = dtLow.addDays( -1 * dayLowD ); + dtAddMonths( dtLow, -1 * monthLowD, dtLow ); + dtAddYears( dtLow, -1 * yearLowD, dtLow ); + dtHigh = dtHigh.addSecs( secondHighD + 60*minuteHighD + 3600* hourHighD ); + dtHigh = dtHigh.addDays( dayHighD ); + dtAddMonths( dtHigh, monthHighD, dtHigh ); + dtAddYears( dtHigh, yearHighD, dtHigh ); + if( gotoEndOfMonth ){ + dtHigh.setDate( QDate( dtHigh.date().year(), + dtHigh.date().month(), + dtHigh.date().daysInMonth() ) ); + dtHigh.setTime( QTime( 23, 59, 59 ) ); + } + if( toggleDts ) { + QDateTime dt( dtLow ); + dtLow = dtHigh; + dtHigh = dt; + } + // secDist = dtLow.secsTo( dtHigh ); + + // NOTE: nSubDelimFactor is not set here since it + // cannot be used for QDateTime values. + nSubDelimFactor = 0.0; + bOrdFactorsOk = true; + } + + + if( !bOrdFactorsOk ) { + // adjust one or both of our limit values + // according to max-empty-inner-span settings + nDist = nHigh - nLow; + if( !isLogarithmic ){ + // replace nLow (or nHigh, resp.) by zero if NOT ALL OF + // our values are located outside of the 'max. empty + // inner space' (i.e. percentage of the y-axis range + // that may to contain NO data entries) + int maxEmpty = para.axisMaxEmptyInnerSpan(); + if( bAutoCalcStart ) { + //qDebug("\nbAutoCalcStart:\n nLow: %f\n nHigh: %f", nLow, nHigh ); + if( 0.0 < nLow ) { + if( maxEmpty == KDCHART_AXIS_IGNORE_EMPTY_INNER_SPAN + || maxEmpty > ( nLow / nHigh * 100.0 ) ) + nLow = 0.0; + else if( nDist / 100.0 < nLow ) + nLow -= nDist / 100.0; // shift lowest value + } + else if( nDist / 100.0 < fabs( nLow ) ) + nLow -= nDist / 100.0; // shift lowest value + nDist = nHigh - nLow; + //qDebug("* nLow: %f\n nHigh: %f", nLow, nHigh ); + } + if( bAutoCalcEnd ) { + //qDebug("\nbAutoCalcEnd:\n nLow: %f\n nHigh: %f", nLow, nHigh ); + if( 0.0 > nHigh ) { + if( maxEmpty == KDCHART_AXIS_IGNORE_EMPTY_INNER_SPAN + || maxEmpty > ( nHigh / nLow * 100.0 ) ) + nHigh = 0.0; + else if( nDist / 100.0 > nHigh ) + nHigh += nDist / 100.0; // shift highest value + } + else if( nDist / 100.0 < fabs( nHigh ) ) + nHigh += nDist / 100.0; // shift highest value + nDist = nHigh - nLow; + //qDebug("* nLow: %f\n nHigh: %f\n\n", nLow, nHigh ); + } + } + } + + + if( isLogarithmic ){ + if( bIsDouble ) { + //qDebug("\n[L--] nLow: %f, nHigh: %f, nDelta: %f", nLow, nHigh, nDelta ); + if( 0.0 == QABS( nLow ) ) + nLow = -5; + else{ + // find the Low / High values for the log. axis + nLow = log10( QABS( nLow ) ); + //if( 0.0 >= nLow ){ + //nLow = fastPow10( -nLow ); + //} + } + nHigh = log10( QABS( nHigh ) ); + + //qDebug("[L-0] nLow: %f, nHigh: %f", nLow, nHigh ); + double intPart=0.0; // initialization necessary for Borland C++ + double fractPart = modf( nLow, &intPart ); + //qDebug(" intPart: %f\nfractPart: %f", intPart, fractPart ); + if( 0.0 > nLow && 0.0 != fractPart ) + nLow = intPart - 1.0; + else + nLow = intPart; + fractPart = modf( nHigh, &intPart ); + if( 0.0 != fractPart ) + nHigh = intPart + 1.0; + + nDist = nHigh - nLow; + nDelta = 1.0; + nSubDelimFactor = 0.1; + //qDebug("\n[L-1] nLow: %f, nHigh: %f, nDelta: %f", nLow, nHigh, nDelta ); + bOrdFactorsOk = true; + } + } + + + if ( !bOrdFactorsOk ) { + // adjust one or both of our limit values + // according to first two digits of (nHigh - nLow) delta + double nDivisor; + double nRound; + nDist = nHigh - nLow; + //qDebug("* nLow: %f\n nHigh: %f nDist: %f\n\n", nLow, nHigh, nDist ); + // find out factors and adjust nLow and nHigh + orgLow = nLow; + orgHigh = nHigh; + calculateOrdinateFactors( para, isLogarithmic, + nDist, nDivisor, nRound, + nDelta, nSubDelimFactor, + nLow, nHigh ); + nLabels = params.roundVal( nDist / nDelta ); + + //qDebug("\n0. nOrgHigh: %f\n nOrgLow: %f", + // orgHigh, orgLow); + //qDebug("\n nDist: %f\n nHigh: %f\n nLow: %f", + // nDist, nHigh, nLow); + //qDebug(" nDelta: %f", nDelta); + //qDebug(" nRound: %f", nRound); + //qDebug(" nLabels: %u", nLabels); + + if( para.axisSteadyValueCalc() ) { + ++nLabels; + //qDebug("* nLabels: %u", nLabels ); + } + } + + + // calculate the amount of nLabels to be written we could take + // based on the space we have for writing the label texts + if( ! ( KDCHART_AXIS_LABELS_AUTO_DELTA + == para.axisValueDelta() ) ){ + nDist = nHigh - nLow; + nDelta = para.axisValueDelta(); + nLabels = params.roundVal( nDist / nDelta ); + + //qDebug("\nI nLow: %f\n nHigh: %f\n nDelta: %f\n nLabels: %u", + // nLow, nHigh, nDelta, nLabels ); + + if( para.axisSteadyValueCalc() ) { + ++nLabels; + + //qDebug("* nLabels: %u", nLabels ); + + } + } + + // make sure labels fit into available height, if vertical axis + if( bVertAxis ) { + //Pending Michel + //find out the width + const KDChartAxisParams & xpara = params.axisParams( KDChartAxisParams::AxisPosBottom ); + double areaWidth = xpara.axisTrueAreaRect().width(); + //make sure to avoid inf + double areaHeight = para.axisTrueAreaRect().height()>0?para.axisTrueAreaRect().height():1.0; + double widthHeight = areaWidth / areaHeight; + //qDebug( "widthHeight %f, nDelta %f", widthHeight, nDelta); + //qDebug( "maxValue %f", data.maxValue()); + //qDebug( "maxColSum %f", data.maxColSum()); + //qDebug( "axisValueEnd() %f", para.axisValueEnd().toDouble()); + double nDivisor; + double nRound; + orgLow = nLow; + orgHigh = nHigh; + + //check if there are axis limitation - if not (auto calculation): + //adjust the axis for 3dbars in order to display the whole top of the bar + //in relation to the with and the height of the area. + // add conditions for multirows here + if ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd().toDouble()) { + if (params.threeDBars() ) { + if ( KDChartParams::BarPercent != params.barChartSubType()) { + if ( widthHeight > 1.5 ) + orgHigh += nDelta * widthHeight; + else + orgHigh += widthHeight * 0.5; + } + } + } else { + orgHigh = nHigh = para.axisValueEnd().toDouble(); + } + //qDebug("\ncalc ordinate 0. nDist: %f\n nLow: %f\n nHigh: %f\n nDelta: %f\n nLabels: %u", nDist, nLow, nHigh, nDelta, nLabels ); + bool bTryNext = false; + uint minLabels = para.axisSteadyValueCalc() ? 3 : 2; + // the following must be processed at least twice - to avoid rounding errors + int pass = 0; + do{ + nDist = nHigh - nLow; + nLow = orgLow; + nHigh = orgHigh; + /* + qDebug("\n=============================================================================\ncalc ordinate 1. nDist: %f\n nLow: %f\n nHigh: %f\n nDelta: %f\n nLabels: %u", + nDist, nLow, nHigh, nDelta, nLabels ); + */ + calculateOrdinateFactors( para, isLogarithmic, + nDist, nDivisor, nRound, + nDelta, + nSubDelimFactor, nLow, nHigh, + bTryNext ); + nLabels = params.roundVal( nDist / nDelta ); + + //qDebug("\ncalc ordinate 2. nDist: %f\n+ nLow: %f\n nHigh: %f\n nDelta: %f\n nLabels: %u", + //nDist, nLow, nHigh, nDelta, nLabels ); + //QString sDelta;sDelta.setNum( nDelta, 'f', 24 ); + //QString sLow; sLow.setNum( nLow, 'f', 24 ); + //qDebug("nLow: %f, sLow: %s, sDelta: %s", nLow, sLow.latin1(), sDelta.latin1()); + + // special case: End values was set by the user, but no Detla values was set. + if( !bAutoCalcEnd && orgHigh > nLow + nLabels * nDelta ) { + ++nLabels; + //qDebug("\nnLabels: %u\n", nLabels ); + } + if( para.axisSteadyValueCalc() ) { + ++nLabels; + //qDebug("\nnLabels: %u\n", nLabels ); + } + //qDebug("calc ordinate n. nDist = nHigh - nLow: %f = %f - %f",nDist, nHigh, nLow); + //qDebug(" nRound: %f\n", nRound); + bTryNext = true; + ++pass; + }while ( ( pass < 2 ) + || ( ( minLabels < nLabels ) + && ( areaHeight < ( nTxtHeight * 1.5 ) * nLabels ) ) ); + } + } + + // finally we can build the texts + if( bIsDouble ) { + int trueBehindComma = -1; + double nVal = nLow; + for ( uint i = 0; i < nLabels; ++i ) { + if( isLogarithmic ) { + labelTexts.append( applyLabelsFormat( + fastPow10( static_cast < int > ( nVal ) ), + divPow10, + behindComma, + 1.0 == nDelta ? KDCHART_AXIS_LABELS_AUTO_DELTA : nDelta, + trueBehindComma, + notation, + decimalPoint, + thousandsPoint, + prefix, + postfix, + totalLen, + padFill, + blockAlign ) ); + } else { + labelTexts.append( applyLabelsFormat( nVal, + divPow10, + behindComma, + nDelta, + trueBehindComma, + notation, + decimalPoint, + thousandsPoint, + prefix, + postfix, + totalLen, + padFill, + blockAlign ) ); + } + nVal += nDelta; + } + + // save our true Low and High value + //qDebug(para.axisSteadyValueCalc()?"\ntrue " : "\nfalse"); + //qDebug("nVal: %f, nDelta: %f", nVal, nDelta ); + if ( para.axisSteadyValueCalc() ) { + nHigh = nVal - nDelta; + } + ( ( KDChartAxisParams& ) para ).setTrueAxisLowHighDelta( nLow, nHigh, nDelta ); + //qDebug("[Z] nLow: %f, nHigh: %f, nDelta: %f", nLow, nHigh, nDelta ); + + } else { + bool goDown = dtLow > dtHigh; + int mult = goDown ? -1 : 1; + QDateTime dt( dtLow ); + nLabels = 0; + /* + qDebug("dtLow: "); + qDebug(dtLow.toString( Qt::ISODate )); + qDebug("dtHigh: "); + qDebug(dtHigh.toString( Qt::ISODate )); + */ + bool bDone=false; + while( !bDone ) { + /* + qDebug("dtLow: %i %i %i %i:%i:%i", + dtLow.date().year(), + dtLow.date().month(), + dtLow.date().day(), + dtLow.time().hour(), + dtLow.time().minute(), + dtLow.time().second()); + qDebug("dtHigh: %i %i %i %i:%i:%i", + dtHigh.date().year(), + dtHigh.date().month(), + dtHigh.date().day(), + dtHigh.time().hour(), + dtHigh.time().minute(), + dtHigh.time().second()); + qDebug("dt: %i %i %i %i:%i:%i", + dt.date().year(), + dt.date().month(), + dt.date().day(), + dt.time().hour(), + dt.time().minute(), + dt.time().second()); + */ + ++nLabels; + if( autoDtLabels ) + labelTexts.append( "x" ); + else +#if COMPAT_QT_VERSION >= 0x030000 + labelTexts.append( dt.toString( Qt::ISODate ) ); +#else + labelTexts.append( dateTimeToString( dt ) ); +#endif + bDone = (goDown ? (dt < dtLow ) : (dt > dtHigh)); + /*if( bDone ){ + dtHigh = dt; + }else*/{ + switch( dtDeltaScale ) { + case KDChartAxisParams::ValueScaleSecond: + dtAddSecs( dt, 1 * mult, dt ); + break; + case KDChartAxisParams::ValueScaleMinute: + dtAddSecs( dt, 60 * mult, dt ); + break; + case KDChartAxisParams::ValueScaleHour: + dtAddSecs( dt, 3600 * mult, dt ); + break; + case KDChartAxisParams::ValueScaleDay: + dtAddDays( dt, 1 * mult, dt ); + break; + case KDChartAxisParams::ValueScaleWeek: + dtAddDays( dt, 7 * mult, dt ); + break; + case KDChartAxisParams::ValueScaleMonth: + dtAddMonths( dt,1 * mult, dt ); + break; + case KDChartAxisParams::ValueScaleQuarter: + dtAddMonths( dt,3 * mult, dt ); + break; + case KDChartAxisParams::ValueScaleYear: + dtAddYears( dt, 1 * mult, dt ); + break; + default: + dtAddDays( dt, 1 * mult, dt ); + break; + } + } + } + //if( autoDtLabels ) + // labelTexts.append( "x" ); + ( ( KDChartAxisParams& ) para ).setTrueAxisDtLowHighDeltaScale( + dtLow, dtHigh, + dtDeltaScale ); + // note: pDelimDelta will be calculated below, + // look for "COMMOM CALC OF NLABELS, DELIM DELTA..." + } + bDone = true; + } + + // let's generate some strings + if ( !bDone ) { + // default scenario for abscissa axes + uint count = bStatistical + ? (data.usedRows() ? data.usedRows() : 1) + : (data.usedCols() ? data.usedCols() : 1); + //double start( 1.0 ); + double start( 1.0 + (bSteadyCalc ? 0.0 : static_cast < double >(data.colsScrolledBy())) ); +//qDebug("colsScrolledBy: %i", data.colsScrolledBy()); +//if(bVertAxis) +//qDebug("vert nVal starting: %f",start); +//else +//qDebug("horz nVal starting: %f",start); +//if(bSteadyCalc) +//qDebug("bSteadyCalc"); +//else +//qDebug("not bSteadyCalc"); + double delta( 1.0 ); + double finis( start + delta * ( count - 1 ) ); + const bool startIsDouble = QVariant::Double == para.axisValueStart().type(); + const bool endIsDouble = QVariant::Double == para.axisValueEnd().type(); + + bool deltaIsAuto = true; + if ( !( KDCHART_AXIS_LABELS_AUTO_DELTA == para.axisValueDelta() ) ) { + delta = para.axisValueDelta(); + deltaIsAuto = false; + } + if ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueStart() ) { + if ( ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() ) ) { + finis = start + delta * ( count - 1 ); + } else { + if( endIsDouble ){ + finis = para.axisValueEnd().toDouble(); + start = finis - delta * ( count - 1 ); +//qDebug("1 changing: start: %f",start); + } else { + // + // + // F E A T U R E P L A N N E D F O R F U T U R E . . . + // + // + } + } + }else{ + if ( startIsDouble ) { + start = para.axisValueStart().toDouble() + (bSteadyCalc ? 0.0 : static_cast < double >(data.colsScrolledBy())); +//qDebug("2 changing: start: %f",start); + } else { + // + // + // F E A T U R E P L A N N E D F O R F U T U R E . . . + // + // + } + if ( !( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() ) ) { + if (endIsDouble ) { + finis = para.axisValueEnd().toDouble(); + if ( deltaIsAuto ) { + delta = ( finis - start ) / count; + } else { + count = static_cast < uint > ( + ( finis - start ) / delta ); + } + } else { + // auto-rows like + // sunday, monday, tuesday, ... + // + // + // F E A T U R E P L A N N E D F O R F U T U R E . . . + // + // + } + } + else { + finis = start + delta * ( count - 1 ); + } + } + QString prefix( QObject::tr( "Item " ) ); + QString postfix; + + + if ( startIsDouble && endIsDouble ) { + int precis = + KDCHART_AXIS_LABELS_AUTO_DIGITS == para.axisDigitsBehindComma() + ? 0 + : para.axisDigitsBehindComma(); + double s = start; + double f = finis; +//qDebug("label loop: s: %f f: %f",s,f); + bool up = ( 0.0 < delta ); + // check the text widths of one large(?) entry + // and hope all the entries will + // fit into the available space + double value = up ? s : f; + uint nLabels = 0; + while ( up ? ( value <= f ) : ( value >= s ) ) { + ++nLabels; + value += delta * up ? 1.0 : -1.0; + } + calculateBasicTextFactors( nTxtHeight, para, + averageValueP1000, + basicPos, orig, delimLen, nLabels, + // start of return parameters + pDelimDelta, + pTextsX, pTextsY, pTextsW, pTextsH, + textAlign ); + QFont font( para.axisLabelsFont() ); + if ( para.axisLabelsFontUseRelSize() ) + font.setPixelSize( static_cast < int > ( nTxtHeight ) ); + painter->setFont( font ); + QFontMetrics fm( painter->fontMetrics() ); + + if ( fm.width( prefix + + QString::number( -fabs( ( s + f ) / 2.0 + delta ), + 'f', precis ) ) + > pTextsW ) { + prefix = ""; + postfix = ""; + } + // now transfer the strings into labelTexts + value = up ? s : f; + while ( up ? ( value <= f ) : ( value >= s ) ) { + labelTexts.append( + prefix + QString::number( value, 'f', precis ) + + postfix ); + value += delta * up ? 1.0 : -1.0; + } + } else { + // pending(KHZ): make sure this branch will ever be reached + // check the text widths largest entry + // to make sure it will fit into the available space + calculateBasicTextFactors( nTxtHeight, para, + averageValueP1000, + basicPos, orig, delimLen, + count, + // start of return parameters + pDelimDelta, + pTextsX, pTextsY, pTextsW, pTextsH, + textAlign ); + QFont font( para.axisLabelsFont() ); + if ( para.axisLabelsFontUseRelSize() ) + font.setPixelSize( static_cast < int > ( nTxtHeight ) ); + painter->setFont( font ); + QFontMetrics fm( painter->fontMetrics() ); + + if ( fm.width( prefix + QString::number( count - 1 ) ) + > pTextsW ) { + prefix = ""; + postfix = ""; + } + // now transfer the strings into labelTexts + for ( uint i = 1; i <= count; ++i ) + labelTexts.append( + prefix + QString::number( i ) + postfix ); + } + } + } + + /* + finishing: COMMOM CALC OF NLABELS, DELIM DELTA... + */ + uint nLabels = labelTexts.count() + ? labelTexts.count() + : 0; + ( ( KDChartAxisParams& ) para ).setAxisLabelTexts( &labelTexts ); + + if( !adjustTheValues ){ + + calculateBasicTextFactors( nTxtHeight, para, averageValueP1000, + basicPos, orig, delimLen, nLabels, + // start of return parameters + pDelimDelta, + pTextsX, pTextsY, pTextsW, pTextsH, + textAlign ); + } + + ( ( KDChartAxisParams& ) para ).setTrueAxisDeltaPixels( pDelimDelta ); + + //qDebug("\nsetting: para.trueAxisLow() %f para.trueAxisHigh() %f",para.trueAxisLow(),para.trueAxisHigh()); + //qDebug("\npDelimDelta: %f", pDelimDelta ); + + /* + qDebug( "Found label texts:" ); + for ( QStringList::Iterator it = labelTexts.begin(); + it != labelTexts.end(); ++it ) + qDebug( ">>> %s", (*it).latin1() ); + qDebug( "\n" ); + */ +//qDebug("\nleaving KDChartAxesPainter::calculateLabelTexts() : nTxtHeight: "+QString::number(nTxtHeight)); +} + + +/** + Calculates some label text factors needed + by function \c calculateLabelTexts() + + \note When calling this function the actual area size for this + axis must be set, this means you may only call it when + \c KDChartPainter::setupGeometry() has been called before. + + \param nTxtHeight the text height to be used for calculating + the return values + \param para the KDChartAxisParams that were specified for this axis + \param averageValueP1000 (average height+width of the prtbl. area) / 1000 + \param basicPos the basic axis position returned by + KDChartAxisParams::basicAxisPos() + \param orig the axis start point + \param delimLen the length of one delimiter mark + \param nLabels the number of labels to be shown at this axis + \param (all others) the reference parameters to be returned + by this function + */ +/**** static ****/ +void KDChartAxesPainter::calculateBasicTextFactors( double nTxtHeight, + const KDChartAxisParams& para, + double /*averageValueP1000*/, + KDChartAxisParams::AxisPos basicPos, + const QPoint& orig, + double delimLen, + uint nLabels, + // start of return params + double& pDelimDelta, + double& pTextsX, + double& pTextsY, + double& pTextsW, + double& pTextsH, + int& textAlign ) +{ + switch ( basicPos ) { + case KDChartAxisParams::AxisPosBottom: { + bool bTouchEdges = para.axisLabelsTouchEdges(); + double wid = para.axisTrueAreaRect().width(); + double divi = bTouchEdges + ? ( 1 < nLabels ? nLabels - 1 : 1 ) + : ( nLabels ? nLabels : 10 ); + pDelimDelta = wid / divi; + + pTextsW = pDelimDelta - 4.0; + pTextsX = orig.x() + 2.0 + - ( bTouchEdges + ? pDelimDelta / 2.0 + : 0.0 ); + pTextsH = para.axisTrueAreaRect().height() - delimLen * 1.33; + pTextsY = orig.y() + + delimLen * 1.33; + textAlign = Qt::AlignHCenter | Qt::AlignTop; + /* + qDebug("pTextsW %f wid %f nLabels %u", pTextsW, wid, nLabels ); + */ + } + break; + case KDChartAxisParams::AxisPosLeft: { + double hig = para.axisTrueAreaRect().height(); + pDelimDelta = hig / ( 1 < nLabels ? nLabels - 1 : 1 ); + + pTextsX = para.axisTrueAreaRect().bottomLeft().x() + + 2.0; + pTextsY = orig.y() - nTxtHeight / 2; + pTextsW = para.axisTrueAreaRect().width() + - delimLen * 1.33 - 2.0; + pTextsH = nTxtHeight; + textAlign = Qt::AlignRight | Qt::AlignVCenter; + } + break; + case KDChartAxisParams::AxisPosTop: { + bool bTouchEdges = para.axisLabelsTouchEdges(); + double wid = para.axisTrueAreaRect().width(); + double divi = bTouchEdges + ? ( 1 < nLabels ? nLabels - 1 : 1 ) + : ( nLabels ? nLabels : 10 ); + pDelimDelta = wid / divi; + + pTextsW = pDelimDelta - 4.0; + pDelimDelta = wid / divi; + + pTextsX = orig.x() + 2.0 + - ( bTouchEdges + ? pDelimDelta / 2.0 + : 0.0 ); + pTextsH = para.axisTrueAreaRect().height() - delimLen * 1.33; + pTextsY = para.axisTrueAreaRect().topLeft().y(); + textAlign = Qt::AlignHCenter | Qt::AlignBottom; + } + break; + case KDChartAxisParams::AxisPosRight: { + double hig = para.axisTrueAreaRect().height(); + pDelimDelta = hig / ( 1 < nLabels ? nLabels - 1 : 1 ); + + pTextsX = para.axisTrueAreaRect().bottomLeft().x() + + delimLen * 1.33; + pTextsY = orig.y() - nTxtHeight / 2; + pTextsW = para.axisTrueAreaRect().width() + - delimLen * 1.33 - 2.0; + pTextsH = nTxtHeight; + textAlign = Qt::AlignLeft | Qt::AlignVCenter; + } + break; + default: { + qDebug( "IMPLEMENTATION ERROR: KDChartAxesPainter::calculateBasicTextFactors() unhandled enum value." ); + // NOOP since the 'basicPos' does not support more that these four values. + } + break; + } +} + + +/** + Takes double \c nVal and returns a QString showing the amount of digits + behind the comma that was specified by \c behindComma (or calculated + automatically by removing trailing zeroes, resp.). + To make sure the resulting string looks fine together with other strings + of the same label row please specify \c nDelta indicating the step width + from one label text to the other. + To prevent the function from having to re-calculate the number of + digits to keep behind the comma, provide it with a temporary helper + variable "trueBehindComma" that has to be initialized with a value + smaller than zero. + + \note This function is reserved for internal use. + */ +QString KDChartAxesPainter::truncateBehindComma( const double nVal, + const int behindComma, + const double nDelta, + int& trueBehindComma ) +{ + const int nTrustedPrecision = 6; // when using 15 we got 1.850000 rounded to 1.849999999999999 + + const bool bUseAutoDigits = KDCHART_AXIS_LABELS_AUTO_DIGITS == behindComma; + const bool bAutoDelta = KDCHART_AXIS_LABELS_AUTO_DELTA == nDelta; + QString sVal; + sVal.setNum( nVal, 'f', bUseAutoDigits ? nTrustedPrecision + : QMIN(behindComma, nTrustedPrecision) ); + //qDebug("nVal: %f sVal: "+sVal, nVal ); + //qDebug( QString(" %1").arg(sVal)); + if ( bUseAutoDigits ) { + int comma = sVal.find( '.' ); + if ( -1 < comma ) { + if ( bAutoDelta ) { + int i = sVal.length(); + while ( 1 < i + && '0' == sVal[ i - 1 ] ) + --i; + sVal.truncate( i ); + if ( '.' == sVal[ i - 1 ] ) + sVal.truncate( i - 1 ); + } else { + if ( 0 > trueBehindComma ) { + QString sDelta = QString::number( nDelta, 'f', nTrustedPrecision ); + int i = sDelta.length(); + while ( 1 < i + && '0' == sDelta[ i - 1 ] ) + --i; + sDelta.truncate( i ); + if ( '.' == sDelta[ i - 1 ] ) + trueBehindComma = 0; + else { + int deltaComma = sDelta.find( '.' ); + if ( -1 < deltaComma ) + trueBehindComma = sDelta.length() - deltaComma - 1; + else + trueBehindComma = 0; + } + } + // now we cut off the too-many digits behind the comma + int nPos = comma + ( trueBehindComma ? trueBehindComma + 1 : 0 ); + sVal.truncate( nPos ); + } + } + } + //qDebug( QString(" - %1").arg(trueBehindComma)); + return sVal; +} + +#if 0 + +#if defined ( Q_WS_WIN) +#define trunc(x) ((int)(x)) +#endif + +#else + +// Ugly hack because Solaris (among others?) doesn't have trunc(), +// since the libs are not C99. I could do a complex #if expression, +// but instead I will just define trunc() here. + +double trunc(double x) +{ + return x >= 0.0 ? floor(x) : -floor(-x); +} + +#endif + + +QString KDChartAxesPainter::applyLabelsFormat( const double nVal_, + int divPow10, + int behindComma, + double nDelta_, + int& trueBehindComma, + KDChartEnums::NumberNotation notation, + const QString& decimalPoint, + const QString& thousandsPoint, + const QString& prefix, + const QString& postfix, + int totalLen, + const QChar& padFill, + bool blockAlign ) +{ + double nVal = nVal_ / fastPow10( divPow10 ); + double nDelta = nDelta_; + + double valLog10 = 0.0; + if( notation == KDChartEnums::NumberNotationScientific || + notation == KDChartEnums::NumberNotationScientificBig ){ + valLog10 = (nVal != 0.0) ? trunc( log10(QABS(nVal)) ) : 0.0; + //qDebug("nVal old: %f valLog10: %f",nVal,valLog10); + nVal /= fastPow10( valLog10 ); + nDelta /= fastPow10( valLog10 ); + //qDebug("nVal new: %f",nVal); + } + QString sVal = truncateBehindComma( nVal, + behindComma, + nDelta, + trueBehindComma ); + //qDebug("sVal : "+sVal+" behindComma: %i",behindComma); + + int posComma = sVal.find( '.' ); + if( 0 <= posComma ){ + sVal.replace( posComma, 1, decimalPoint); + }else{ + posComma = sVal.length(); + } + if( notation == KDChartEnums::NumberNotationScientific || + notation == KDChartEnums::NumberNotationScientificBig ){ + if( notation == KDChartEnums::NumberNotationScientific ) + sVal.append( "e" ); + else + sVal.append( "E" ); + sVal.append( QString::number( valLog10, 'f', 0 ) ); + } else { + if( thousandsPoint.length() ){ + const int minLen = (0 < sVal.length() && '-' == sVal[0]) + ? 4 + : 3; + int n = posComma; // number of digits at the left side of the comma + while( minLen < n ){ + n -= 3; + sVal.insert(n, thousandsPoint); + } + } + } + sVal.append( postfix ); + int nFill = totalLen - (sVal.length() + prefix.length()); + if( 0 > nFill ) + nFill = 0; + if( !blockAlign ) + sVal.prepend( prefix ); + for(int i=0; i < nFill; ++i) + sVal.prepend( padFill ); + if( blockAlign ) + sVal.prepend( prefix ); + if ( totalLen > 0 ) + sVal.truncate( totalLen ); + /*Pending Michel: Force non fractional values + *In case it is a fractional value + *and the user has set axisLabelsDigitsBehindComma() == 0 + *return an empty string + */ + if ( behindComma == 0 && QString::number(nVal).find('.') > 0 ) + sVal = QString::null;//sVal = ""; + return sVal; +} + +/** + Calculates the factors to be used for calculating ordinate labels texts. + + \note This function is reserved for internal use. + */ +void KDChartAxesPainter::calculateOrdinateFactors( + const KDChartAxisParams& para, + bool isLogarithmic, + double& nDist, + double& nDivisor, + double& nRound, + double& nDelta, + double& nSubDelimFactor, + double& nLow, + double& nHigh, + bool findNextRound ) +{ + if ( findNextRound ) { + if ( 1.0 > nRound ) + nRound = 1.0; + else + if ( 2.0 > nRound ) + nRound = 2.0; + else + if ( 2.5 > nRound ) + nRound = 2.5; + else + if ( 5.0 > nRound ) + nRound = 5.0; + else { + nDivisor *= 10.0; + nRound = 1.0; + } + } else { + nDivisor = 1.0; + QString sDistDigis2; + sDistDigis2.setNum( nDist, 'f', 24); + if ( 1.0 > nDist ) { + sDistDigis2.remove( 0, 2 ); + nDivisor = 0.01; + while ( 0 < sDistDigis2.length() + && '0' == sDistDigis2[ 0 ] ) { + nDivisor *= 0.1; + sDistDigis2.remove( 0, 1 ); + } + } else { + if ( 10.0 > nDist ) { + nDivisor = 0.1; + // remove comma, if present + sDistDigis2.remove( 1, 1 ); + } else + if ( 100.0 > nDist ) + nDivisor = 1.0; + else { + int comma = sDistDigis2.find( '.' ); + if ( -1 < comma ) + sDistDigis2.truncate( comma ); + nDivisor = fastPow10( (int)sDistDigis2.length() - 2 ); + } + } + sDistDigis2.truncate( 2 ); + bool bOk; + double nDistDigis2( sDistDigis2.toDouble( &bOk ) ); + if ( !bOk ) + nDistDigis2 = 10.0; + if ( 75.0 <= nDistDigis2 ) + nRound = 5.0; + else + if ( 40.0 <= nDistDigis2 ) + nRound = 2.5; + else + if ( 20.0 <= nDistDigis2 ) + nRound = 2.0; + else + nRound = 1.0; + } + + nDelta = nRound * nDivisor; + + // make sure its a whole number > 0 if its a log axis. Just round up. + if( isLogarithmic ) + nDelta = static_cast < int > ( nDelta ) < nDelta + ? static_cast < int > ( nDelta ) + 1 + : static_cast < int > ( nDelta ); + + bool bInvertedAxis = ( 0.0 > nDist ); + if( bInvertedAxis ) + nDelta *= -1.0; + + /* + qDebug(" n D i s t : %f", nDist ); + qDebug(" n D i v i s o r : %f", nDivisor); + qDebug(" n R o u n d : %f", nRound ); + qDebug(" n D e l t a : %f", nDelta ); + qDebug(" nHigh : %f", nHigh ); + qDebug(" nLow : %f", nLow ); + */ + if( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueStart() + || !para.axisValueStartIsExact() ) { + double orgLow( nLow ); + modf( nLow / nDelta, &nLow ); + nLow *= nDelta; + if( bInvertedAxis ){ + if ( nLow < orgLow ) + nLow += nDelta; + if ( 0.0 > nLow && 0.0 <= orgLow ) + nLow = 0.0; + }else{ + if ( nLow > orgLow ) + nLow -= nDelta; + if ( 0.0 < nLow && 0.0 >= orgLow ) + nLow = 0.0; + } + } + if ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() ) { + double orgHigh( nHigh ); + modf( nHigh / nDelta, &nHigh ); + nHigh *= nDelta; + if( bInvertedAxis ){ + if ( nHigh > orgHigh ) + nHigh -= nDelta; + if ( 0.0 < nHigh && 0.0 >= orgHigh ) + nHigh = 0.0; + }else{ + if ( nHigh < orgHigh ) + nHigh += nDelta; + if ( 0.0 > nHigh && 0.0 <= orgHigh ) + nHigh = 0.0; + } + } + + //qDebug(" n H i g h : %f", nHigh ); + //qDebug(" n L o w : %f\n\n", nLow ); + + if ( 1.0 == nRound ) + nSubDelimFactor = 0.5; + else + if ( 2.0 == nRound ) + nSubDelimFactor = 0.25; + else + if ( 2.5 == nRound ) + nSubDelimFactor = 0.2; + else + if ( 5.0 == nRound ) + nSubDelimFactor = 0.2; + else { + // Should not happen + qDebug( "IMPLEMENTATION ERROR: Unknown nRound in calculateOrdinateFactors()" ); + nSubDelimFactor = 1.0; + } + + nDist = nHigh - nLow; +} + +/**** static ****/ +void KDChartAxesPainter::saveDrawLine( QPainter& painter, + QPoint pA, + QPoint pZ, + QPen pen ) +{ + const QPen oldPen( painter.pen() ); + bool bNice = ( pen.color() == oldPen.color() ) + && ( pen.width() == oldPen.width() ) + && ( pen.style() == oldPen.style() ); + if ( !bNice ) + painter.setPen( pen ); + painter.drawLine( pA, pZ ); + if ( !bNice ) + painter.setPen( oldPen ); +} + +/**** static ****/ +void KDChartAxesPainter::dtAddSecs( const QDateTime& org, const int secs, QDateTime& dest ) +{ + //qDebug("entering KDChartAxesPainter::dtAddSecs() .."); + int s = org.time().second(); + int m = org.time().minute(); + int h = org.time().hour(); + int days = 0; + if( -1 < secs ){ + int mins = (s + secs) / 60; + if( 0 == mins ) + s += secs; + else{ + s = (s + secs) % 60; + int hours = (m + mins) / 60; + if( 0 == hours ) + m += mins; + else{ + m = (m + mins) % 60; + days = (h + hours) / 24; + if( 0 == days ) + h += hours; + else{ + h = (h + hours) % 24; + } + } + } + } + dest.setTime( QTime(h,m,s) ); + dest.setDate( org.date() ); + if( days ) + dtAddDays( dest, days, dest ); + //qDebug(".. KDChartAxesPainter::dtAddSecs() done."); +} + +/**** static ****/ +void KDChartAxesPainter::dtAddDays( const QDateTime& org, const int days, QDateTime& dest ) +{ + //qDebug("entering KDChartAxesPainter::dtAddDays() .."); + int d = org.date().day(); + int m = org.date().month(); + int y = org.date().year(); + int dd = (-1 < days) ? 1 : -1; + int di = 0; + while( di != days ){ + d += dd; + // underrunning day? + if( 1 > d ){ + if( 1 < m ){ + --m; + d = QDate( y,m,1 ).daysInMonth(); + } + else{ + --y; + m = 12; + d = 31; + } + // overrunning day? + }else if( QDate( y,m,1 ).daysInMonth() < d ){ + if( 12 > m ) + ++m; + else{ + ++y; + m = 1; + } + d = 1; + } + di += dd; + } + dest = QDateTime( QDate( y,m,d ), org.time() ); + //qDebug(".. KDChartAxesPainter::dtAddDays() done."); +} + +/**** static ****/ +void KDChartAxesPainter::dtAddMonths( const QDateTime& org, const int months, QDateTime& dest ) +{ + //qDebug("entering KDChartAxesPainter::dtAddMonths() .."); + int d = org.date().day(); + int m = org.date().month(); + int y = org.date().year(); + int md = (-1 < months) ? 1 : -1; + int mi = 0; + while( mi != months ){ + m += md; + if( 1 > m ){ + --y; + m = 12; + }else if( 12 < m ){ + ++y; + m = 1; + } + mi += md; + } + // QMIN takes care for intercalary day + dest = QDateTime( QDate( y,m,QMIN( d, QDate( y,m,1 ).daysInMonth() ) ), + org.time() ); + //qDebug(".. KDChartAxesPainter::dtAddMonths() done."); +} + +/**** static ****/ +void KDChartAxesPainter::dtAddYears( const QDateTime& org, const int years, QDateTime& dest ) +{ + //qDebug("entering KDChartAxesPainter::dtAddYears() .."); + int d = org.date().day(); + int m = org.date().month(); + int y = org.date().year() + years; + dest.setTime( org.time() ); + // QMIN takes care for intercalary day + dest = QDateTime( QDate( y,m,QMIN( d, QDate( y,m,d ).daysInMonth() ) ), + org.time() ); + //qDebug(".. KDChartAxesPainter::dtAddYears() done."); +} + + + +void KDChartAxesPainter::calculateAbscissaInfos( const KDChartParams& params, + const KDChartTableDataBase& data, + uint datasetStart, + uint datasetEnd, + double logWidth, + const QRect& dataRect, + abscissaInfos& infos ) +{ + if( params.axisParams( KDChartAxisParams::AxisPosBottom ).axisVisible() + && ( KDChartAxisParams::AxisTypeUnknown + != params.axisParams( KDChartAxisParams::AxisPosBottom ).axisType() ) ) + infos.abscissaPara = ¶ms.axisParams( KDChartAxisParams::AxisPosBottom ); + else + if( params.axisParams( KDChartAxisParams::AxisPosBottom2 ).axisVisible() + && ( KDChartAxisParams::AxisTypeUnknown + != params.axisParams( KDChartAxisParams::AxisPosBottom2 ).axisType() ) ) + infos.abscissaPara = ¶ms.axisParams( KDChartAxisParams::AxisPosBottom2 ); + else + if( params.axisParams( KDChartAxisParams::AxisPosTop ).axisVisible() + && ( KDChartAxisParams::AxisTypeUnknown + != params.axisParams( KDChartAxisParams::AxisPosTop ).axisType() ) ) + infos.abscissaPara = ¶ms.axisParams( KDChartAxisParams::AxisPosTop ); + else + if( params.axisParams( KDChartAxisParams::AxisPosTop2 ).axisVisible() + && ( KDChartAxisParams::AxisTypeUnknown + != params.axisParams( KDChartAxisParams::AxisPosTop2 ).axisType() ) ) + infos.abscissaPara = ¶ms.axisParams( KDChartAxisParams::AxisPosTop2 ); + else + // default is bottom axis: + infos.abscissaPara = ¶ms.axisParams( KDChartAxisParams::AxisPosBottom ); + + if( infos.abscissaPara->axisLabelsTouchEdges() ) + infos.bCenterThePoints = false; + + infos.bAbscissaDecreasing = infos.abscissaPara->axisValuesDecreasing(); + infos.bAbscissaIsLogarithmic + = KDChartAxisParams::AxisCalcLogarithmic == infos.abscissaPara->axisCalcMode(); + + + // Number of values: If -1, use all values, otherwise use the + // specified number of values. + infos.numValues = 0; + if ( params.numValues() > -1 ) + infos.numValues = params.numValues(); + else + infos.numValues = data.usedCols(); + + QVariant::Type type2Ref = QVariant::Invalid; + infos.bCellsHaveSeveralCoordinates = + data.cellsHaveSeveralCoordinates( datasetStart, datasetEnd, + &type2Ref ); + + infos.numLabels = (infos.abscissaPara && + infos.abscissaPara->axisLabelTexts()) + ? infos.abscissaPara->axisLabelTexts()->count() + : infos.numValues; + if( 0 >= infos.numLabels ) + infos.numLabels = 1; + + infos.bAbscissaHasTrueAxisValues = + infos.abscissaPara && (0.0 != infos.abscissaPara->trueAxisDelta()); + infos.abscissaStart = infos.bAbscissaHasTrueAxisValues + ? infos.abscissaPara->trueAxisLow() + : 0.0; + infos.abscissaEnd = infos.bAbscissaHasTrueAxisValues + ? infos.abscissaPara->trueAxisHigh() + : 1.0 * (infos.numLabels - 1); + infos.abscissaSpan = fabs( infos.abscissaEnd - infos.abscissaStart ); + infos.abscissaDelta = infos.bAbscissaHasTrueAxisValues + ? infos.abscissaPara->trueAxisDelta() + : ( ( 0.0 != infos.abscissaSpan ) + ? ( infos.abscissaSpan / infos.numLabels ) + : infos.abscissaSpan ); + + //qDebug( bAbscissaDecreasing ? "bAbscissaDecreasing = TRUE" : "bAbscissaDecreasing = FALSE"); + //qDebug( abscissaHasTrueAxisValues ? "abscissaHasTrueAxisValues = TRUE" : "abscissaHasTrueAxisValues = FALSE"); + //qDebug( "abscissaDelta = %f", abscissaDelta); + + infos.bAbscissaHasTrueAxisDtValues = + (QVariant::DateTime == type2Ref) && + infos.abscissaPara && + infos.abscissaPara->trueAxisDtLow().isValid(); + if( infos.bAbscissaHasTrueAxisDtValues ){ + infos.numLabels = 200; + infos.bCenterThePoints = false; + } + + infos.dtLowPos = infos.bAbscissaHasTrueAxisDtValues + ? infos.abscissaPara->axisDtLowPosX() - dataRect.x() + : 0.0; + infos.dtHighPos = infos.bAbscissaHasTrueAxisDtValues + ? infos.abscissaPara->axisDtHighPosX() - dataRect.x() + : logWidth; + infos.abscissaDtStart = infos.bAbscissaHasTrueAxisDtValues + ? infos.abscissaPara->trueAxisDtLow() + : QDateTime(); + infos.abscissaDtEnd = infos.bAbscissaHasTrueAxisDtValues + ? infos.abscissaPara->trueAxisDtHigh() + : QDateTime(); + + //Pending Michel case when same hh:mm:ss but different msecs + infos.bScaleLessThanDay = ( infos.bAbscissaHasTrueAxisDtValues + ? infos.abscissaPara->trueAxisDtDeltaScale() + : KDChartAxisParams::ValueScaleDay ) + < KDChartAxisParams::ValueScaleDay; + if ( infos.abscissaDtStart.time() == infos.abscissaDtEnd.time() && infos.bScaleLessThanDay ) + infos.dtHighPos = logWidth; + + // adjust the milli seconds: + infos.abscissaDtStart.setTime( + QTime( infos.abscissaDtStart.time().hour(), + infos.abscissaDtStart.time().minute(), + infos.abscissaDtStart.time().second(), + 0 ) ); + infos.abscissaDtEnd.setTime( + QTime( infos.abscissaDtEnd.time().hour(), + infos.abscissaDtEnd.time().minute(), + infos.abscissaDtEnd.time().second(), + 999 ) ); + //qDebug( infos.abscissaPara->trueAxisDtLow().toString("yyyy-MM-dd-hh:mm:ss.zzz")); + //qDebug( infos.abscissaPara->trueAxisDtHigh().toString("yyyy-MM-dd-hh:mm:ss.zzz")); + //qDebug(infos.abscissaDtStart.toString("yyyy-MM-dd-hh:mm:ss.zzz")); + //qDebug(infos.abscissaDtEnd.toString("yyyy-MM-dd-hh:mm:ss.zzz")); +/* + infos.bScaleLessThanDay = ( infos.bAbscissaHasTrueAxisDtValues + ? infos.abscissaPara->trueAxisDtDeltaScale() + : KDChartAxisParams::ValueScaleDay ) + < KDChartAxisParams::ValueScaleDay; +*/ + if( infos.bAbscissaHasTrueAxisDtValues ){ + if( infos.bScaleLessThanDay ){ + infos.abscissaDtSpan = infos.abscissaDtStart.secsTo( infos.abscissaDtEnd ); + + /* NOTE: We do *not* add the milli seconds because they aren't covered + by the span indicated by infos.dtHighPos - infos.dtLowPos. + if( infos.abscissaDtStart.time().msec() || infos.abscissaDtEnd.time().msec() ) + infos.abscissaDtSpan = + ( infos.abscissaDtEnd.time().msec() - + infos.abscissaDtStart.time().msec() ) / 1000.0; + */ + } + else{ + infos.abscissaDtSpan = infos.abscissaDtStart.daysTo( infos.abscissaDtEnd ); + if( infos.abscissaDtStart.time().msec() || infos.abscissaDtEnd.time().msec() ) + infos.abscissaDtSpan += + ( infos.abscissaDtEnd.time().msec() - + infos.abscissaDtStart.time().msec() ) / (86400.0 * 1000.0); + if( infos.abscissaDtEnd.time().second() ) + infos.abscissaDtSpan += infos.abscissaDtEnd.time().second() / 86400.0; + if( infos.abscissaDtEnd.time().minute() ) + infos.abscissaDtSpan += infos.abscissaDtEnd.time().minute() / 1440.0; + if( infos.abscissaDtEnd.time().hour() ) + infos.abscissaDtSpan += infos.abscissaDtEnd.time().hour() / 24.0; + } + }else + infos.abscissaDtSpan = 10.0; + if( 0 == infos.abscissaDtSpan || 0.0 == infos.abscissaDtSpan ) + infos.abscissaDtSpan = 1.0; + + infos.abscissaDtPixelsPerScaleUnit = (infos.dtHighPos - infos.dtLowPos)/ infos.abscissaDtSpan; + + if( infos.bAbscissaHasTrueAxisDtValues ) + infos.abscissaDelta = 20.0; + + infos.pointDist + = ( infos.abscissaPara && (0.0 != infos.abscissaPara->trueAxisDeltaPixels()) ) + ? infos.abscissaPara->trueAxisDeltaPixels() + : ( logWidth / + ( + (1 > ((double)(infos.numLabels) - (infos.bCenterThePoints ? 0.0 : 1.0))) + ? ((double)(infos.numLabels) - (infos.bCenterThePoints ? 0.0 : 1.0)) + : 1 ) ); + + infos.abscissaPixelsPerUnit = ( 0.0 != infos.abscissaDelta ) + ? ( infos.pointDist / infos.abscissaDelta ) + : infos.pointDist; + + //const double infos.abscissaZeroPos2 = -1.0 * infos.abscissaPixelsPerUnit * infos.abscissaStart; + infos.abscissaZeroPos = infos.abscissaPara->axisZeroLineStartX() - dataRect.x(); + //qDebug("abscissaZeroPos %f abscissaZeroPos2 %f",abscissaZeroPos,abscissaZeroPos2); + + /* + qDebug(abscissaPara ? + "\nabscissaPara: OK" : + "\nabscissaPara: leer"); + qDebug(abscissaHasTrueAxisValues ? + "abscissaHasTrueAxisValues: TRUE" : + "abscissaHasTrueAxisValues: FALSE"); + qDebug("abscissaStart: %f", abscissaStart); + qDebug("abscissaEnd : %f", abscissaEnd); + qDebug("abscissaPara->trueAxisDelta(): %f", abscissaPara->trueAxisDelta()); + qDebug("numValues : %u, numLabels : %u", numValues, numLabels); + */ +} + + + +bool KDChartAxesPainter::calculateAbscissaAxisValue( const QVariant& value, + abscissaInfos& ai, + int colNumber, + double& xValue ) +{ + if( ai.bCellsHaveSeveralCoordinates ) { + if( QVariant::Double == value.type() ) { + double dVal = value.toDouble(); + if( ai.bAbscissaIsLogarithmic ){ + if( 0.0 < dVal ) + xValue = ai.abscissaPixelsPerUnit * log10( dVal ); + else + xValue = -10250.0; + }else{ + xValue = ai.abscissaPixelsPerUnit * dVal; + } + xValue *= ai.bAbscissaDecreasing ? -1.0 : 1.0; + xValue += ai.abscissaZeroPos; + } + else if( ai.bAbscissaHasTrueAxisDtValues && + QVariant::DateTime == value.type() ) { + const QDateTime dtVal = value.toDateTime(); + double dT = ( ai.bScaleLessThanDay ) + ? ai.abscissaDtStart.secsTo( dtVal ) + : ai.abscissaDtStart.daysTo( dtVal ); + /* + qDebug("abscissaDtStart: %i %i %i %i:%i:%i.%i", + ai.abscissaDtStart.date().year(), + ai.abscissaDtStart.date().month(), + ai.abscissaDtStart.date().day(), + ai.abscissaDtStart.time().hour(), + ai.abscissaDtStart.time().minute(), + ai.abscissaDtStart.time().second(), + ai.abscissaDtStart.time().msec()); + */ + //qDebug("days to = %f",dT); + + /* + qDebug(" dtVal: %i %i %i %i:%i:%i.%i", + dtVal.date().year(), + dtVal.date().month(), + dtVal.date().day(), + dtVal.time().hour(), + dtVal.time().minute(), + dtVal.time().second(), + dtVal.time().msec()); + */ + xValue = ai.abscissaDtPixelsPerScaleUnit * dT; + if( dtVal.time().msec() ) + xValue += (ai.abscissaDtPixelsPerScaleUnit * dtVal.time().msec()) + / ( ai.bScaleLessThanDay + ? 1000.0 + : (1000.0 * 86400.0) ); + //qDebug("xValue: %f",xValue); + if( !ai.bScaleLessThanDay ){ + if( dtVal.time().second() ) + xValue += (ai.abscissaDtPixelsPerScaleUnit * dtVal.time().second()) + / 86400.0; + if( dtVal.time().minute() ) + xValue += (ai.abscissaDtPixelsPerScaleUnit * dtVal.time().minute()) + / 1440.0; + if( dtVal.time().hour() ) + xValue += (ai.abscissaDtPixelsPerScaleUnit * dtVal.time().hour()) + / 24.0; + } + xValue *= ai.bAbscissaDecreasing ? -1.0 : 1.0; + xValue += ai.dtLowPos; + // qDebug("xValue = dtLowPos + abscissaDtPixelsPerScaleUnit * dT\n%f = %f + %f * %f", + // xValue, dtLowPos, abscissaDtPixelsPerScaleUnit, dT); + } + else + return false; + } else + xValue = ai.pointDist * ( double ) colNumber; + return true; +} + + + +/*************************************************************************/ +/*************************************************************************/ +/*************************************************************************/ +/*** ***/ +/*** Framework for data drawing using cartesian axes (Bar, Line, ...) ***/ +/*** ***/ +/*************************************************************************/ +/*************************************************************************/ +/*************************************************************************/ + +/** + Paints the actual data area and registers the region for the data + points if \a regions is not 0. + + \param painter the QPainter onto which the chart should be painted + \param data the data that will be displayed as a chart + \param paint2nd specifies whether the main chart or the additional chart is to be drawn now + \param regions a pointer to a list of regions that will be filled + with regions representing the data segments, if not null + */ +void KDChartAxesPainter::paintData( QPainter* painter, + KDChartTableDataBase* data, + bool paint2nd, + KDChartDataRegionList* regions ) +{ + bool bNormalMode = isNormalMode(); + + uint chart = paint2nd ? 1 : 0; + + // find out the ordinate axis (or axes, resp.) belonging to this chart + // (up to 4 ordinates might be in use: 2 left ones and 2 right ones) + uint axesCount; + KDChartParams::AxesArray ordinateAxes; + ordinateAxes.resize( KDCHART_CNT_ORDINATES ); + if( !params()->chartAxes( chart, axesCount, ordinateAxes ) ) { + // no axis - no fun! + return; + // We cannot draw data without an axis having calculated high/low + // values and position of the zero line before. + + // PENDING(khz) Allow drawing without having a visible axis! + } + + //const KDChartParams::ChartType params_chartType + // = paint2nd ? params()->additionalChartType() : params()->chartType(); + + double logWidth = _dataRect.width(); + double areaWidthP1000 = logWidth / 1000.0; + + int nClipShiftUp = clipShiftUp(bNormalMode, areaWidthP1000); + QRect ourClipRect( _dataRect ); + if ( 0 < ourClipRect.top() ) { + ourClipRect.setTop( ourClipRect.top() - nClipShiftUp ); + ourClipRect.setHeight( ourClipRect.height() + nClipShiftUp - 1 ); + } else + ourClipRect.setHeight( ourClipRect.height() + nClipShiftUp / 2 - 1 ); + + // protect axes ? + //ourClipRect.setBottom( ourClipRect.bottom() - 1 ); + //ourClipRect.setLeft( ourClipRect.left() + 1 ); + //ourClipRect.setRight( ourClipRect.right() - 1 ); + + const QWMatrix & world = painter->worldMatrix(); + ourClipRect = +#if COMPAT_QT_VERSION >= 0x030000 + world.mapRect( ourClipRect ); +#else + world.map( ourClipRect ); +#endif + painter->setClipRect( ourClipRect ); + painter->translate( _dataRect.x(), _dataRect.y() ); + + painter->setPen( params()->outlineDataColor() ); + + // find out which datasets are to be represented by this chart + uint chartDatasetStart, chartDatasetEnd; + findChartDatasets( data, paint2nd, chart, chartDatasetStart, chartDatasetEnd ); + + // Note: 'aI' is *not* the axis number! + for( uint aI = 0; aI < axesCount; ++aI ) { + // 'axis' is the REAL axis number! + uint axis = ordinateAxes.at( aI ); + + const KDChartAxisParams* axisPara = ¶ms()->axisParams( axis ); + + uint datasetStart, datasetEnd; + uint axisDatasetStart, axisDatasetEnd; + uint dummy; + if( params()->axisDatasets( axis, + axisDatasetStart, + axisDatasetEnd, dummy ) + && ( KDCHART_ALL_DATASETS != axisDatasetStart ) ) { + + if( KDCHART_NO_DATASET == axisDatasetStart ){ + //========== + continue; // NO DATASETS --> STOP PROCESSING THIS AXIS + //========== + } + + if( axisDatasetStart >= chartDatasetStart + && axisDatasetStart <= chartDatasetEnd ) + datasetStart = QMAX( axisDatasetStart, chartDatasetStart ); + else if( axisDatasetStart <= chartDatasetStart + && axisDatasetEnd >= chartDatasetStart ) + datasetStart = chartDatasetStart; + else + datasetStart = 20; + if( axisDatasetEnd >= chartDatasetStart + && axisDatasetEnd <= chartDatasetEnd ) + datasetEnd = QMIN( axisDatasetEnd, chartDatasetEnd ); + else if( axisDatasetEnd >= chartDatasetEnd + && axisDatasetStart <= chartDatasetEnd ) + datasetEnd = chartDatasetEnd; + else + datasetEnd = 0; + } else { + datasetStart = chartDatasetStart; + datasetEnd = chartDatasetEnd; + } + + //qDebug("\n==========================================================" + // "\naxis %u axisDatasetStart %u axisDatasetEnd %u / chartDatasetStart %u chartDatasetEnd %u", + //axis, axisDatasetStart, axisDatasetEnd, chartDatasetStart, chartDatasetEnd ); + + double logHeight = axisPara->axisTrueAreaRect().height(); + double axisYOffset = axisPara->axisTrueAreaRect().y() - _dataRect.y(); + + //qDebug("\n==========================================================\naxis %u logHeight %f axisDatasetStart %u chartDatasetStart %u axisDatasetEnd %u chartDatasetEnd %u", + //axis, logHeight, axisDatasetStart, chartDatasetStart, axisDatasetEnd, chartDatasetEnd ); + //if( KDCHART_ALL_DATASETS == axisDatasetStart ) + // qDebug(" ALL DATASETS"); + //if( KDCHART_NO_DATASET == axisDatasetStart ) + // qDebug(" N O DATESETS"); + + double maxColumnValue = axisPara->trueAxisHigh(); + double minColumnValue = axisPara->trueAxisLow(); + double columnValueDistance = maxColumnValue - minColumnValue; + + + // call the chart type specific data painter: + specificPaintData( painter, + ourClipRect, + data, + regions, + axisPara, + bNormalMode, + chart, + logWidth, + areaWidthP1000, + logHeight, + axisYOffset, + minColumnValue, + maxColumnValue, + columnValueDistance, + chartDatasetStart, + chartDatasetEnd, + datasetStart, + datasetEnd ); + } + painter->translate( - _dataRect.x(), - _dataRect.y() ); +} diff --git a/kchart/kdchart/KDChartAxesPainter.h b/kchart/kdchart/KDChartAxesPainter.h new file mode 100644 index 00000000..29e955b4 --- /dev/null +++ b/kchart/kdchart/KDChartAxesPainter.h @@ -0,0 +1,220 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTAXESPAINTER_H__ +#define __KDCHARTAXESPAINTER_H__ + +#include <qpen.h> +#include <qpainter.h> + +#include <KDChartPainter.h> +#include <KDChartTable.h> +#include <KDChartAxisParams.h> + +class QDateTime; +class KDChartParams; + +class KDChartAxesPainter : public KDChartPainter +{ + protected: + KDChartAxesPainter( KDChartParams* params ); + virtual ~KDChartAxesPainter(); + + virtual void paintAxes( QPainter* painter, + KDChartTableDataBase* data ); + + virtual void paintData( QPainter* painter, + KDChartTableDataBase* data, + bool paint2nd, + KDChartDataRegionList* regions ); + + // note: Classes inheriting from KDChartAxesPainter + // MUST implement the following abstract functions: + + // Returns whether a normal chart is to be drawn + // or a stacked or percentage (or ... ) chart . + virtual bool isNormalMode() const = 0; + + // Returns the number of pixels to be added to the clipping area's height + // to make room for special arrows indicating oversized, cut data entries. + // Chart classes not drawing such indicators must return 0. + virtual int clipShiftUp( bool normalMode, double areaWidthP1000 ) const = 0; + + // The actual, chart type specific drawing of the data. + virtual void 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 ) = 0; + + virtual bool calculateAllAxesLabelTextsAndCalcValues( + QPainter* painter, + KDChartTableDataBase* data, + double areaWidthP1000, + double areaHeightP1000, + double& delimLen ); + + private: + public: + // special infos needed by the different painters' implementations + // of the specificPaintData() function + struct abscissaInfos{ + const KDChartAxisParams* abscissaPara; + bool bCenterThePoints; + bool bAbscissaDecreasing; + bool bAbscissaIsLogarithmic; + bool bCellsHaveSeveralCoordinates; + int numValues; + int numLabels; + bool bAbscissaHasTrueAxisValues; + double abscissaStart; + double abscissaEnd; + double abscissaSpan; + double abscissaDelta; + bool bAbscissaHasTrueAxisDtValues; + double dtLowPos; + double dtHighPos; + QDateTime abscissaDtStart; + QDateTime abscissaDtEnd; + bool bScaleLessThanDay; + double abscissaDtSpan; + double abscissaDtPixelsPerScaleUnit; + double pointDist; + double abscissaPixelsPerUnit; + double abscissaZeroPos; + }; + static void calculateAbscissaInfos( + const KDChartParams& params, + const KDChartTableDataBase& data, + uint datasetStart, + uint datasetEnd, + double logWidth, + const QRect& dataRect, + abscissaInfos& infos ); + static bool calculateAbscissaAxisValue( const QVariant& value, + abscissaInfos& ai, + int colNumber, + double& xValue ); + + static void saveDrawLine( QPainter& painter, + QPoint pA, + QPoint pZ, + QPen pen ); + static void calculateLabelTexts( + QPainter* painter, + const KDChartTableDataBase& data, + const KDChartParams& params, + uint axisNumber, + double averageValueP1000, + double delimLen, + // start of return parameters + KDChartAxisParams::AxisPos& basicPos, + QPoint& orig, + QPoint& dest, + double& pXDeltaFactor, + double& pYDeltaFactor, + double& pXDelimDeltaFaktor, + double& pYDelimDeltaFaktor, + double& nSubDelimFactor, + double& pDelimDelta, + double& nTxtHeight, + double& pTextsX, + double& pTextsY, + double& pTextsW, + double& pTextsH, + int& textAlign, + bool& isLogarithmic, + bool& isDateTime, + bool& autoDtLabels, + QDateTime& dtLow, + QDateTime& dtHigh, + KDChartAxisParams::ValueScale& dtDeltaScale, + bool adjustTheValues = false, + double trueDelta = 0.0, + double trueDeltaPix = 0.0 ); + static void calculateBasicTextFactors( double nTxtHeight, + const KDChartAxisParams& para, + double averageValueP1000, + KDChartAxisParams::AxisPos basicPos, + const QPoint& orig, + double delimLen, + uint nLabels, + // start of return parameters + double& pDelimDelta, + double& pTextsX, + double& pTextsY, + double& pTextsW, + double& pTextsH, + int& textAlign ); + static QString truncateBehindComma( const double nVal, + const int behindComma, + const double nDelta, + int& trueBehindComma ); + static QString applyLabelsFormat( const double nVal, + int divPow10, + int behindComma, + double nDelta, + int& trueBehindComma, + KDChartEnums::NumberNotation notation, + const QString& decimalPoint, + const QString& thousandsPoint, + const QString& prefix, + const QString& postfix, + int totalLen, + const QChar& padFill, + bool blockAlign ); + static void calculateOrdinateFactors( const KDChartAxisParams& para, + bool bLogarithmic, + double& nDist, + double& nDivisor, + double& nRound, + double& nDelta, + double& nSubDelimFactor, + double& nLow, + double& nHigh, + bool findNextRound = false ); + static void dtAddSecs( const QDateTime& org, const int secs, QDateTime& dest ); + static void dtAddDays( const QDateTime& org, const int days, QDateTime& dest ); + static void dtAddMonths( const QDateTime& org, const int months, QDateTime& dest ); + static void dtAddYears( const QDateTime& org, const int years, QDateTime& dest ); +}; + +#endif diff --git a/kchart/kdchart/KDChartAxisParams.cpp b/kchart/kdchart/KDChartAxisParams.cpp new file mode 100644 index 00000000..158ea1a3 --- /dev/null +++ b/kchart/kdchart/KDChartAxisParams.cpp @@ -0,0 +1,3318 @@ +/* -*- Mode: C++ -*- + KDChart - a multi-platform charting engine + */ + +/**************************************************************************** + ** Copyright (C) 2001-2003 Klarävdalens 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. + ** + **********************************************************************/ +#include <KDChartAxisParams.h> +#ifndef KDCHART_MASTER_CVS +#include "KDChartAxisParams.moc" +#endif + + +/** + \class KDChartAxisParams KDChartAxisParams.h + \brief access the chart axis parameters. + + To be used in combination with the axis access methods in + class KDChartParams. + + + \see KDChartParams.h + \see KDChartParams.cpp + + Bundles all parameters of one axis including the type except the + actual data displayed. Serializing an object of this type plus the + data displayed is enough to be able to recreate the chart later. + +*/ + + + + + +/** + \c enum KDChartAxisParams::Axistype { AxisTypeUnknown, + AxisTypeEAST, + AxisTypeNORTH, + AxisUP }; + + The axis type - the kind of the axis (x-, y- or z-axis). + + \li \c AxisTypeEAST axis of abscisses, the so-called \em X-axis. + In world coordinates this is the EAST axis as used + on topographical maps. + This axis normally is assigned to the 1st dimension + of the represented data, to change this use + KDChartAxesPainter::setAxisAssignment(). + + \li \c AxisTypeNORTH axis of ordinates, the so-called \em Y-axis. + when are represented by the chart. + In world coordinates this is the NORTH axis as used + on topographical maps for 2-dimensional data, + or the NORTH axis as used on bird's-eys views of + buildings etc for 3-dimensional data, resp.. + This axis normally is assigned to the 2nd dimension + of the represented data. + + \li \c AxisTypeUP axis of the 3rd dimension, the so-called \em Z-axis. + In world coordinates this is the UP axis as used + on bird's-eys views of buildings etc. + This axis normally is assigned to the 3rd dimension + of the represented data. + + The following picture shows the default positions of the + different axes: + \verbatim + + +-----------+ + /| | + n +------------+ / | | + . | | / | | + NORTH, y . | 2-dim. | / | | + . | | / +-----------+ + . | chart | / / / n + . | | n + / 3-dim. / . + . | | . | / / . NORTH, y + . | | UP, z . | / chart / . + . | | . | / / . + . | | . |/ / . + 0 +------------+ 0 +-----------+ . + 0 + 0............n 0...........n + EAST, x EAST, x + \endverbatim + + \note Normally you will not have to specify the AxisType since it + will be set automatically in KDChartAxesPainter::setDefaultAxesTypes() + called by c'tor of class KDChartAxesPainter + + \sa setAxisType, axisType, KDChartAxesPainter::setAxisAssignment() + */ + + +/** + Constructor. Define default values: + + - do not show this axis at all, + - use circa 100 per thousand of the printable area + size for drawing this axis, + - calc mode linear, + - line is visible, + - line width: 3 per mille of the printable area size + - line color: black, + - labels are visible, + - labels font: helvetica + - labels font size: 20 / 1000 of the printable area size + - labels color: black, + - this axis will be used for an ordinate + - label value limits will be calculated automatically, + */ +KDChartAxisParams::KDChartAxisParams() +{ + _axisType = AxisTypeUnknown; + _axisVisible = false; + _axisAreaMode = AxisAreaModeAutoSize; + _axisUseAvailableSpaceFrom = 0; + _axisUseAvailableSpaceTo = -1000; + _axisAreaMin = -100; + _axisAreaMax = 0; + _axisCalcMode = AxisCalcLinear; + _axisIsoRefAxis = UINT_MAX; // == KDCHART_NO_AXIS, see KDChartParams.cpp + _axisTrueAreaSize = 0; + _axisZeroLineStartX = 0.0; + _axisZeroLineStartY = 0.0; + _axisDtLowPosX = 0.0; + _axisDtLowPosY = 0.0; + _axisDtHighPosX = 0.0; + _axisDtHighPosY = 0.0; + + _axisLineWidth = -3; // == 3/1000 + _axisTrueLineWidth = 1; + _axisLineVisible = true; + _axisLineColor = QColor( 0, 0, 0 ); + + _axisShowFractionalValuesDelimiters = true; + _axisShowGrid = false; + _axisGridColor = KDCHART_DEFAULT_AXIS_GRID_COLOR; + _axisGridLineWidth = KDCHART_AXIS_GRID_AUTO_LINEWIDTH; + _axisGridStyle = Qt::SolidLine; + + _axisShowSubDelimiters = true; + _axisGridSubColor = KDCHART_DEFAULT_AXIS_GRID_COLOR; + _axisGridSubLineWidth = KDCHART_AXIS_GRID_AUTO_LINEWIDTH; + _axisGridSubStyle = Qt::DotLine; + + _axisZeroLineColor = QColor( 0x00, 0x00, 0x80 ); + + _axisLabelsVisible = true; + _axisLabelsFont = QFont( "helvetica", 12, + QFont::Normal, false ); + _axisLabelsFontUseRelSize = true; + _axisLabelsFontRelSize = 32; + _axisLabelsFontMinSize = 6; + _axisLabelsDontShrinkFont = false; + _axisLabelsDontAutoRotate = false; + _axisLabelsRotation = 0; + _axisLabelsColor = QColor( 0, 0, 0 ); + + _axisSteadyValueCalc = true; + _axisValueStartIsExact = true; + _axisValueStart = KDCHART_AXIS_LABELS_AUTO_LIMIT; + _axisValueEnd = KDCHART_AXIS_LABELS_AUTO_LIMIT; + _axisValueDelta = KDCHART_AXIS_LABELS_AUTO_DELTA; + _axisValueDeltaScale = ValueScaleNumerical; + _axisValueLeaveOut = KDCHART_AXIS_LABELS_AUTO_LEAVEOUT; + _axisValuesDecreasing = false; + + // First we used "h:mm:ss\nd.MM.yyyy" but now we calculate the + // format automatically - based on the time span to be displayed. + _axisLabelsDateTimeFormat = KDCHART_AXIS_LABELS_AUTO_DATETIME_FORMAT; + + _axisMaxEmptyInnerSpan = 90; + _takeLabelsFromDataRow = LabelsFromDataRowNo; + _labelTextsDataRow = 0; + _axisLabelStringList.clear(); + _axisShortLabelsStringList.clear(); + + setAxisLabelTexts( 0 ); + setAxisFirstLabelText(); + setAxisLastLabelText(); + setTrueAxisDeltaPixels( 0.0 ); + setTrueAxisLowHighDelta( 0.0, 0.0, 0.0 ); + setTrueAxisDtLowHighDeltaScale( QDateTime(), QDateTime(), ValueScaleDay ); + + _axisLabelsDivPow10 = 0; + _axisDigitsBehindComma = KDCHART_AXIS_LABELS_AUTO_DIGITS; + _axisLabelsNotation = KDChartEnums::NumberNotationDecimal; + _axisLabelsDecimalPoint = "."; + _axisLabelsThousandsPoint = ","; + _axisLabelsPrefix = ""; + _axisLabelsPostfix = ""; + _axisLabelsTotalLen = 0; + _axisLabelsPadFill = ' '; + _axisLabelsBlockAlign = true; +} + +/** + Destructor. Only defined to have it virtual. + +*/ + +KDChartAxisParams::~KDChartAxisParams() +{ + // Intentionally left blank for now. +} + + +/** + Specifies the axis type. The default is unknown (AxisTypeUnknown). + + \note Normally you will not have to specify the AxisType since it + will be set automatically in KDChartAxesPainter::setDefaultAxesTypes() + called by c'tor of class KDChartAxesPainter + + \param axisType the axis type to use + \sa axisType, AxisType + */ +void KDChartAxisParams::setAxisType( AxisType axisType ) +{ + _axisType = axisType; + emit changed(); +} + +/** + \fn AxisType KDChartAxisParams::axisType() const + + \return the axis type configured in this params object. + + \sa setAxisType, AxisType + */ + + +/** + + \c enum KDChartAxisParams::AxisPos { AxisPosSTART = 0, + + AxisPosBottom = 0, + AxisPosSagittal = 0, // <-- for POLAR charts + AxisPosLeft = 1, + AxisPosCircular = 1, // <-- for POLAR charts + AxisPosLowerRightEdge = 2, + + AxisPosTop = 3, + AxisPosRight = 4, + AxisPosLowerLeftEdge = 5, + +// diese Markierung muss jeweils mitgepflegt werden, +// wenn AxisPos erweitert werden sollte. +AxisPosAdditionalsSTART = 6, + +AxisPosBottom2 = 6, +AxisPosLeft2 = 7, +AxisPosLowerRightEdge2 = 8, + +AxisPosTop2 = 9, +AxisPosRight2 = 10, +AxisPosLowerLeftEdge2 = 11, + +// auch diese Markierung muss jeweils mitgepflegt werden, +// wenn AxisPos erweitert werden sollte. +AxisPosEND = 11 }; + +Since the chart area is defined by subtracting the space consumed by +the axes from the printable area there are two options that are +mutually exclusive. + +- Define the chart by precisely specifying (in absolute or in +relative numbers) how much space may be used by each axis. +and/or define start and end values for the axes' labels. + +-> This produces results that are comparable to each other +since the chart will have an exactly fixed size and its position +on the paper will remain the same even if the labels of its axes +become wider or more narrow. +Also the graphical representation of the date will be comparable +since all your charts will use the same scale if you wish this. + +- Let the program calculate the chart layout based upon the kind +of and width of the labels to be drawn at the axes. +Also the program can calculate the labels for you and find the +boundary values for start and end of the scale automatically. +-> This will produce good looking results without you having +to fiddle around with the settings. +Unfortunately these charts will not allways be comparable to +each other if the axes' labels change since both the size of +the area reserved for the chart and the scaling of the +scales will vary if the data values do so. + +With KDChart both ways are possible, see hints given with +setAxisAreaMode(), setAxisAreaMin() and setAxisAreaMax(). + +To optimize your results you might want to use setAxisLineWidth() and +setAxisLabelsFontPointSize(): negative values will specify it +in per thousand of the printable area size - producing a result +looking fine even if the chart is +printed at very large dimensions. + +\sa setAxisAreaMode, setAxisAreaMin, setAxisAreaMax +\sa setAxisLineWidth(), setAxisLabelsFontPointSize() +\sa setAxisLabelsLimitsAuto(), setAxisLabelsLimitsLow() +\sa setAxisLabelsLimitsHeigh() +*/ + +/** + The axis position. + Use AxisPosBottom for X-axis and AxisPosLeft for traditional Y-axis. + When showing 3-dimensional charts you may use AxisPosLowerRightEdge + to have an Y-axis on the lower right side going into the 'depth' of + the picture and a z-axis going 'upwards' at the left edge. + The other, optional values (AxisPosRight, AxisPosTop and + AxisPosLowerLeftEdge, resp.) could be used to show a second + axis on the other side of the picture - useful in case you want + to show two data sets in one chart, like two curves showing data sets + sharing the same abscissa axis but having differently scaled ordinates. + The additional values (AxisPosBottom2, AxisPosTop2 ..) may be used + to specify composed charts having two ordinates at the same side of + the drawing and the like... + + These AxisPos values are used to specify which axis' settings are + to be modified or retrieved, resp. + + \note Use special values \c AxisPossagittal and \c AxisPosCircular + to set delimiter/label/grid parameters for polar charts. + + \sa setAxisAreaMode, axisAreaMode, setAxisCalcMode, axisCalcMode + \sa KDChartParams::setAxisParams, KDChartParams::axisParams + */ + + +/** + The basic axis type. + + \param pos the axis type to be analyzed + \return the basic axis type (Bottom, Left, Top or Right) + */ +KDChartAxisParams::AxisPos KDChartAxisParams::basicAxisPos( uint pos ) +{ + AxisPos ret = AxisPos( pos ); + switch ( pos ) { + case AxisPosBottom: + case AxisPosLeft: + case AxisPosTop: + case AxisPosRight: + break; + case AxisPosLowerRightEdge: + ret = AxisPosRight; + break; + case AxisPosLowerLeftEdge: + ret = AxisPosLeft; + break; + case AxisPosBottom2: + ret = AxisPosBottom; + break; + case AxisPosLeft2: + ret = AxisPosLeft; + break; + case AxisPosTop2: + ret = AxisPosTop; + break; + case AxisPosRight2: + ret = AxisPosRight; + break; + case AxisPosLowerRightEdge2: + ret = AxisPosRight; + break; + case AxisPosLowerLeftEdge2: + ret = AxisPosLeft; + break; + default: { + qDebug( "IMPLEMENTATION ERROR: type missing in KDChartAxisParams::basicAxisPos()" ); + Q_ASSERT( ret != AxisPos( pos ) ); + } + } + return ret; +} + +/** + Specifies if the axis will be drawn. The default is false. + + \param axisVisible set true to make visible the respective axis. + \sa axisVisible, AxisVisible + */ +void KDChartAxisParams::setAxisVisible( bool axisVisible ) +{ + _axisVisible = axisVisible; + emit changed(); +} + +/** + Returns weither the axis will be drawn or not. + + \return if the axis is visible or not. + \sa setAxisVisible, AxisVisible + */ +bool KDChartAxisParams::axisVisible() const +{ + return _axisVisible; +} + + +/** + \c enum AxisAreaMode { AxisAreaModeFixedSize, + AxisAreaModeAutoSize, + AxisAreaModeMinMaxSize }; + + The axis size, determines how to calculate the size of area used + by the axis - i.e. the width of left axis area (or the right + one, resp.) or the height of the top axis area (or the bottom one, + resp.). + \li \c AxisAreaModeFixedSize (default) value will be taken + from \c AxisAreaMinSize() or \c AxisAreaMaxSize() - whichever + returns the smaller value + \li \c AxisAreaModeAutoSize (default) will be calculated + automatically based on the size of the labels to be drawn + \li \c AxisAreaModeMinMaxSize will be calculated automatically but + bearing user defined limits in mind (this is not implemented yet) + + \sa setAxisAreaMode, axisAreaMode, AxisAreaMode + \sa setAxisAreaMin, setAxisAreaMax, setAxisArea + */ + +/** + Specifies the axis size mode. + The default is auto size (AxisAreaModeAutoSize). + + \param axisAreaMode the way how to find out the + size of the area where the axis shall be drawn. + \sa axisAreaMode, AxisAreaMode + \sa setAxisAreaMin, setAxisAreaMax, setAxisArea + */ +void KDChartAxisParams::setAxisAreaMode( AxisAreaMode axisAreaMode ) +{ + _axisAreaMode = axisAreaMode; + emit changed(); +} + + + +/** + Returns the axis size mode configured in this params object. + + \return the axis size mode configured in this params object. + \sa setAxisAreaMode, AxisAreaMode, setAxisAreaMin, setAxisAreaMax + \sa setAxisArea + */ +KDChartAxisParams::AxisAreaMode KDChartAxisParams::axisAreaMode() const +{ + return _axisAreaMode; +} + +/** + Converts the specified axis area mode enum to a string representation. + + \param mode the axis area mode enum to convert + \return the string representation of the mode enum + */ +QString KDChartAxisParams::axisAreaModeToString( AxisAreaMode mode ) { + switch( mode ) { + case AxisAreaModeFixedSize: + return "FixedSize"; + case AxisAreaModeAutoSize: + return "AutoSize"; + case AxisAreaModeMinMaxSize: + return "MinMaxSize"; + default: // should not happen + qDebug( "Unknown axis area mode" ); + return "AxisAreaMode"; + } +} + +/** + Converts the specified string to an axis area mode enum value. + + \param string the string to convert + \return the axis area mode enum value + */ +KDChartAxisParams::AxisAreaMode KDChartAxisParams::stringToAxisAreaMode( const QString& type ) { + if( type == "FixedSize" ) + return AxisAreaModeFixedSize; + else if( type == "AutoSize" ) + return AxisAreaModeAutoSize; + else if( type == "MinMaxSize" ) + return AxisAreaModeMinMaxSize; + else // should not happen + return AxisAreaModeAutoSize; +} + +/** + Specifies the axis area minimum width (or height, resp.). + + \param axisAreaMin the axis area minimum width (or height, resp.) + If value is negative, the absolute value is per thousand + of the size of the printable area to + be used. This will make the axis look the same even if scaled + to very different size. + + Note: It AxisAreaModeFixedSize is set the smaller value of + axisAreaMax and axisAreaMin is used for the area size. + +\sa axisAreaMin, axisAreaMax, setAxisAreaMode, setAxisAreaMax +\sa setAxisArea +*/ +void KDChartAxisParams::setAxisAreaMin( int axisAreaMin ) +{ + _axisAreaMin = axisAreaMin; + emit changed(); +} + +/** + \fn int KDChartAxisParams::axisAreaMin() const + Returns the axis area minimum width (or height, resp.). + + \return the axis area minimum width (or height, resp.). + \sa setAxisAreaMin, setAxisAreaMax, setAxisArea + */ + + + +/** + Specifies how the axis will make use of the available space. + + Use this function to specify how large the area of the axis + will be and where it will be positioned. + + \note This function is to be used for (vertical) ordinate axes <b>only</b>, + available space usage specified for abscissa axes will be ignored. + +example: setAxisUseAvailableSpace( 0, -499 ) will make the +axis occupy the first half of the available space, +so there could be a second axis (for chart #2 having the same +x-axis as chart #1) using the remaining +part of the available space, this one would be specified +by setAxisUseAvailableSpace( -500, -1000 ). + +See also the examples given with \c KDChartParams::setAdditionalChartType(). + +\param axisUseAvailableSpaceFrom the beginning offset +of the space to be covered by this axis. +Set this to 0 to have your axis start at the very beginning +of the avaliable space - this is the default setting. +Use negative values to specify an offset in per mil +of the available space, or use positive values to specify +it in pixels. + +\param axisUseAvailableSpaceTo the ending offset +of the space to be covered by this axis. +Set this to -1000 (== 1000 per mille) to let the axis +end at the very end of the available space, this is the default setting. +to a smaller negative value to specify the percantage +Use negative values to specify an offset in per mil +of the available space, or use positive values to specify +it in pixels. + +\sa axisUseAvailableSpaceFrom, axisUseAvailableSpaceTo +*/ +void KDChartAxisParams::setAxisUseAvailableSpace( int axisUseAvailableSpaceFrom, + int axisUseAvailableSpaceTo ) +{ + _axisUseAvailableSpaceFrom = axisUseAvailableSpaceFrom; + _axisUseAvailableSpaceTo = axisUseAvailableSpaceTo; + emit changed(); +} + + +/** + \fn int KDChartAxisParams::axisUseAvailableSpaceFrom() const + Returns the beginning offset of the space used by this + axis in comparison to the available space that could + be used by this axis. + + \sa setAxisUseAvailableSpace, axisUseAvailableSpaceTo + */ + + +/** + \fn int KDChartAxisParams::axisUseAvailableSpaceTo() const + Returns the ending offset of the space used by this + axis in comparison to the available space that could + be used by this axis. + + \sa setAxisUseAvailableSpace, axisUseAvailableSpaceFrom + */ + +/** + Specifies the axis area maximum width (or height, resp.). + + \param axisAreaMax the axis area maximum width (or height, resp.) + If value is negative, the absolute value is per thousand + of the size of the printable area to + be used. This will make the axis look the same even if scaled + to very different size. + + Note: If AxisAreaModeFixedSize is set the smaller value of + axisAreaMax and axisAreaMin is used for the area size. + +\sa axisAreaMax, axisAreaMin, setAxisAreaMode, setAxisAreaMin +\sa setAxisArea +*/ +void KDChartAxisParams::setAxisAreaMax( int axisAreaMax ) +{ + _axisAreaMax = axisAreaMax; + emit changed(); +} +/** + \fn int KDChartAxisParams::axisAreaMax() const + Returns the axis area maximum width (or height, resp.). + + \return the axis area maximum width (or height, resp.). + \sa setAxisAreaMax, setAxisAreaMin, setAxisArea + */ + +/** + Specifies the axis area size mode and the + minimum and maximum width (or height, resp.) of the area. + This method is here for convenience, see \c setAxisAreaMode, + \c setAreaMin and \c setAreaMax for details. + + Note: Is AxisAreaModeFixedSize is set the smaller value of + axisAreaMax and axisAreaMin is used for the area size. + + \param axisAreaMode the way how to find out the + size of the area where the axis shall be drawn. + \param axisAreaMin the axis area minimum width (or height, resp.) + \param axisAreaMax the axis area maximum width (or height, resp.) + + \sa setAxisAreaMode, setAxisAreaMin, setAxisAreaMax + */ +void KDChartAxisParams::setAxisArea( AxisAreaMode axisAreaMode, + int axisAreaMin, + int axisAreaMax ) +{ + _axisAreaMode = axisAreaMode; + _axisAreaMin = axisAreaMin; + _axisAreaMax = axisAreaMax; + emit changed(); +} + +/** + \c enum AxisCalcMode { AxisCalcLinear, AxisCalcLogarithmic }; + The axis calculation mode. + + \sa setAxisCalcMode, axisCalcMode, AxisCalcMode + */ + +/** + Specifies the axis calculation mode. + The default is linear calculation (AxisCalcLinear). + + \note Specifying an AxisCalcLogarithmic calculation mode + also results in the axis's label parameters being adjusted, + in detail these settings will become valid: + + \li steady-value-calculation mode is activated + \li automatic limit calculation will be set for the begin and the end of the axis + \li logarithmical step width will be calculated automatically: 0.001, 0.01, 0.1, 1, 10, .. + \li number of Digits shown behind the comma will be calculated automatically. + + This is done by implicitely calling setAxisValues(), + so you don't need to explicitely call that function, + actually the following is what happens inside: + + \verbatim + if( AxisCalcLogarithmic == axisCalcMode ) + setAxisValues( true, + KDCHART_AXIS_LABELS_AUTO_LIMIT, + KDCHART_AXIS_LABELS_AUTO_LIMIT, + 1.0, + KDCHART_AXIS_LABELS_AUTO_DIGITS ); + \endverbatim + + You may override these setting by making an extra setAxisValues() + call AFTER calling setAxisCalcMode( AxisCalcLogarithmic ). + + \param axisCalcMode the axis calculation mode to be used. + \sa axisCalcMode, AxisCalcMode + \sa setAxisSteadyValueCalc + */ +void KDChartAxisParams::setAxisCalcMode( AxisCalcMode axisCalcMode ) +{ + _axisCalcMode = axisCalcMode; + if( AxisCalcLogarithmic == axisCalcMode ){ + setAxisValues( true, + KDCHART_AXIS_LABELS_AUTO_LIMIT, + KDCHART_AXIS_LABELS_AUTO_LIMIT, + 1.0, + KDCHART_AXIS_LABELS_AUTO_DIGITS ); + } + emit changed(); +} +/** + \fn AxisCalcMode KDChartAxisParams::axisCalcMode() const + Returns the axis calculation mode configured in this params object. + + \return the axis calculation mode configured in this params object. + \sa setAxisCalcMode, AxisCalcMode + */ + +/** + Converts the specified axis calc mode enum to a string representation. + + \param mode the axis calc mode enum to convert + \return the string representation of the mode enum + */ +QString KDChartAxisParams::axisCalcModeToString( AxisCalcMode mode ) { + switch( mode ) { + case AxisCalcLinear: + return "Linear"; + case AxisCalcLogarithmic: + return "Logarithmic"; + default: // should not happen + qDebug( "Unknown axis calc mode" ); + return "Linear"; + } +} + +/** + Converts the specified string to an axis calc mode enum value. + + \param string the string to convert + \return the axis calc mode enum value + */ +KDChartAxisParams::AxisCalcMode KDChartAxisParams::stringToAxisCalcMode( const QString& type ) { + if( type == "Linear" ) + return AxisCalcLinear; + else if( type == "Logarithmic" ) + return AxisCalcLogarithmic; + else // should not happen + return AxisCalcLinear; +} + +/** + Specifies another axis which this axis shall be isometric with. + + \param isoRefAxis axis which this axis shall be isometric with. + + Normally all axes' step widths are calculated independently from + each other. By specifying a reference axis for one axis you make + KDChart use the same scale for both axes. + + For example to have the left axis using the same scale as the + right axis you could invoke this: + + \verbatim + KDChartAxisParams pa( + _p->axisParams( KDChartAxisParams::AxisPosLeft ) ); + pa.setIsometricReferenceAxis( KDChartAxisParams::AxisPosBottom ); + _p->setAxisParams( KDChartAxisParams::AxisPosLeft, pa ); + \endverbatim + + These commands are equivalent to the following ones: + + \verbatim + KDChartAxisParams pa( + _p->axisParams( KDChartAxisParams::AxisPosBottom ) ); + pa.setIsometricReferenceAxis( KDChartAxisParams::AxisPosLeft ); + _p->setAxisParams( KDChartAxisParams::AxisPosBottom, pa ); + \endverbatim + + In any case both axes will use the same scale so - unless you are + using more axes than these two ones - the resulting chart will be true + to scale. + + \note Use special value KDCHART_ALL_AXES if your chart + has got more than two axes and all of them shall use the same scale, specifying + this for one of the axes is enough, there is no need to set it several times. + + Use special value KDCHART_NO_AXIS to undo any previous setting + that was specified for this axis, this has to be called for any axis that was + modified by previous calls. + + \sa isometricReferenceAxis + */ +void KDChartAxisParams::setIsometricReferenceAxis( uint isoRefAxis ) +{ + _axisIsoRefAxis = isoRefAxis; + emit changed(); +} + +/** + \fn uint KDChartAxisParams::isometricReferenceAxis() const + Returns which axis this axis shall be isometric with, this will + be either the axis position - see KDChartAxisParams::AxisPos - or one of the special + values KDCHART_ALL_AXES and KDCHART_NO_AXIS. + + \return which axis this axis shall be isometric with. + + \sa setIsometricReferenceAxis + */ + + +/** + \fn void KDChartAxisParams::setAxisTrueAreaSize( int axisTrueAreaSize ) + Specifies the true axis area width (or height, resp.). + + \param axisAreaMax the true axis area width (or height, resp.) + as it was calculated and drawn. + This is allways an absolute value. + + \note Do <b>not call</b> this function unless you are knowing + exactly what you are doing. \c setAxisTrueAreaSize is normally + reserved for internal usage by methods calculating the area + size based upon \c axisAreaMin and \c axisAreaMax. Thus the + signal \c changed() is not sended here. + + \sa axisAreaMax, axisAreaMin, setAxisAreaMode, setAxisAreaMin + \sa setAxisArea + */ + +/** + \fn int KDChartAxisParams::axisTrueAreaSize() const + Returns the true axis area width (or height, resp.) + as calculated and drawn. + + \return the true axis area width (or height, resp.). + \sa setAxisAreaMax, setAxisAreaMin, setAxisArea + */ + +/** + \fn void KDChartAxisParams::setAxisTrueAreaRect( const QRect& axisTrueAreaRect ) + Specifies the true axis area rectangle. + + \param axisAreaMax the true axis area rectangle + as it was calculated and drawn. + + \note Do <b>not call</b> this function unless you are knowing + exactly what you are doing. \c setAxisTrueAreaRect is normally + reserved for internal usage by methods calculating the area + size based upon \c axisAreaMin and \c axisAreaMax. Thus the + signal \c changed() is not sended here. + + \sa axisAreaMax, axisAreaMin, setAxisAreaMode, setAxisAreaMin + \sa setAxisArea + */ + +/** + \fn QRect KDChartAxisParams::axisTrueAreaRect() const + Returns the true axis area rectangle + as calculated and drawn. + + \return the true axis area rectangle + \sa setAxisAreaMax, setAxisAreaMin, setAxisArea + */ + +/** + Specifies whether the axis sub-delimiters should be drawn. + + \note If true and axisShowGrid is also true the grid on the + chart data area will show a thin dotted line for each sub-delimiter + (or a line with a pattern defined by \c setAxisGridSubStyle, resp.) + + \param axisShowSubDelimiters if true sub-delimiters will be drawn at this axis. + \sa axisShowSubDelimiters, setAxisShowGrid, setAxisGridSubStyle + */ +void KDChartAxisParams::setAxisShowSubDelimiters( bool axisShowSubDelimiters ) +{ + _axisShowSubDelimiters = axisShowSubDelimiters; + emit changed(); +} + +/** + Specifies whether the delimiters should be drawn at the position for the fractional values. + + \note This can be useful if you want to force only painting non fractional values on the axis and + do not want the delimiters and grid lines to be drawn at the position where some fractional values + (auto calculation) were meant to be displayed. It will have no effect in case fractional values labels + are painted. + In order to force painting only non fractional values you need to call setAxisBehindDigitsComma(0). + \param axisShowFracValDelim if false delimiters and grid line will not be drawn on this axis at the positon + where fractional values were meant to be drawn. + \sa setAxisDigitBehindComma +*/ + +void KDChartAxisParams::setAxisShowFractionalValuesDelimiters( bool axisShowFracValDelim ) +{ + _axisShowFractionalValuesDelimiters = axisShowFracValDelim; + emit changed(); +} + + +/** + \fn bool KDChartAxisParams::axisShowSubDelimiters() const + Returns whether the axis sub-delimiters will be drawn. + + \return whether the axis sub-delimiters will be drawn. + \sa setAxisShowSubDelimiters + */ + +/** + Specifies whether the axis line should be drawn. + + \param axisLineVisible if true the line of this axis will be drawn. + \sa axisLineVisible + */ +void KDChartAxisParams::setAxisLineVisible( bool axisLineVisible ) +{ + _axisLineVisible = axisLineVisible; + emit changed(); +} + + +/** + \fn bool KDChartAxisParams::axisLineVisible() const + Returns whether the axis line should be drawn. + + \return whether the axis line should be drawn. + \sa setAxisLineVisible + */ + +/** + Specifies the axis line width. + + \param axisLineWidth the axis line width. + If value is negative, the absolute value is per thousand + of the printable area size to be used. This will make the + axis look the same even if scaled to very different size. + + \sa axisLineWidth + */ +void KDChartAxisParams::setAxisLineWidth( int axisLineWidth ) +{ + _axisLineWidth = axisLineWidth; + emit changed(); +} + + +/** + \fn int KDChartAxisParams::axisLineWidth() const + Returns the axis line width. + + \return the axis line width. + \sa setAxisLineWidth + */ + + +/** + \fn void s KDChartAxisParams::setAxisTrueLineWidth( int axisTrueLineWidth ) + Specifies the actual axis line width, as calculated and drawn. + + \Note You may not use this internal function. + + \param axisTrueLineWidth the actual axis line width, + as calculated and drawn. + + \sa axisTrueLineWidth + */ + +/** + \fn int KDChartAxisParams::axisTrueLineWidth() const + Returns the axis true line width, as calculated and drawn. + + \return the axis true line width, as calculated and drawn. + \sa setAxisTrueLineWidth + */ + + +/** + Specifies the axis line colour. + + \param axisLineColor the axis line colour. + \sa axisLineColor + */ +void KDChartAxisParams::setAxisLineColor( QColor axisLineColor ) +{ + _axisLineColor = axisLineColor; + emit changed(); +} + +/** + \fn QColor KDChartAxisParams::axisLineColor() const + Returns the axis line colour. + + \return the axis line colour. + \sa setAxisLineColor + */ + + +/** + Specifies whether a grid should be drawn at the chart data area. + By default the grid will be drawn based on the left + ordinate axis and on the bottom abscissa axis. + + The grid will show a solid line for each delimiter. + (or a line with a pattern defined by \c setAxisGridStyle, resp.) + + \note If true and axisShowSubDelimiters is also true the grid + will show a thin dotted line for each sub-delimiter. + (or a line with a pattern defined by \c setAxisGridSubStyle, resp.) + + \param axisShowGrid if true a grid will be drawn on the chart data area. + \sa axisShowGrid, setAxisGridStyle, setAxisGridSubStyle + */ +void KDChartAxisParams::setAxisShowGrid( bool axisShowGrid ) +{ + _axisShowGrid = axisShowGrid; + emit changed(); +} + +/** + \fn bool KDChartAxisParams::axisShowGrid() const + Returns whether a grid should be drawn at the chart data area. + + \return whether a grid should be drawn at the chart data area. + \sa setAxisShowGrid, setAxisShowSubDelimiters + */ + + + +/** + Specifies the axis grid colour. + + To reset the color to the built-in default value + please call \c setAxisGridColor( KDCHART_DEFAULT_AXIS_GRID_COLOR ) + + \param axisGridColor the axis grid color. + \sa axisGridColor, setAxisShowGrid + */ +void KDChartAxisParams::setAxisGridColor( QColor axisGridColor ) +{ + _axisGridColor = axisGridColor; + emit changed(); +} + +/** + \fn QColor KDChartAxisParams::axisGridColor() const + Returns the axis grid color. + + \return the axis grid color. + \sa setAxisGridColor, setAxisShowGrid + */ + + +/** + Specifies the colour to be used for the thin lines between the + normal axis grid lines. + + To reset the color to the built-in default value + please call \c setAxisGridSubColor( KDCHART_DEFAULT_AXIS_GRID_COLOR ) + + \param axisGridSubColor the axis grid sub color. + \sa axisGridSubColor, setAxisGridColor, setAxisShowGrid, setAxisShowSubDelimiters + */ +void KDChartAxisParams::setAxisGridSubColor( QColor axisGridSubColor ) +{ + _axisGridSubColor = axisGridSubColor; + emit changed(); +} + + +/** + \fn QColor KDChartAxisParams::axisGridSubColor() const + Returns the axis grid sub color. + + \return the axis grid sub color. + \sa setAxisGridSubColor + */ + +/** + Specifies the width of the axis grid lines. + + \note Normally you would <b>not</b> call this function since + grid lines in most cases look best in their default line +width: the same width as the axis line they belong to. +However when combining multiple datasets or multiple charts +sharing the same abscissa axes but having their ordinates +differently scaled you might want to reduce the line width +of the respective grid lines and use different grid colours +to show two grids at the same time. In such cases it might +also be a good idea to deactivate \c setAxisShowSubDelimiters +thus avoiding the dotted sub-grid lines or to set their +style to Qt::NoPen to get sub-delimiters on the axis +but no sub-grid lines. + +You may use setAxisGridLineWidth( KDCHART_AXIS_GRID_AUTO_LINEWIDTH ) +to reset the value to its default: being automatically +adjusted to the width of the axis line. + +\param axisGridLineWidth the width of the axis grid lines. +If value is negative, the absolute value is per thousand +of the printable area size to be used. This will make the +grid look the same even if scaled to very different size. +\sa axisGridLineWidth, setAxisGridColor, setAxisGridStyle +\sa setAxisShowGrid, setAxisShowSubDelimiters +*/ +void KDChartAxisParams::setAxisGridLineWidth( int axisGridLineWidth ) +{ + _axisGridLineWidth = axisGridLineWidth; + emit changed(); +} + +/** + \fn int KDChartAxisParams::axisGridLineWidth() const + Returns the width of the axis grid lines. + (see explanation given with \c setAxisGridLineWidth ) + + \return the width of the axis grid lines. + \sa setAxisGridLineWidth, setAxisShowGrid + */ + +/** + Specifies the width of the thin lines between the + normal axis grid lines. + + You may use setAxisGridSubLineWidth( KDCHART_AXIS_GRID_AUTO_LINEWIDTH ) + to reset the value to its default: being automatically + adjusted to the width of the axis line. + + \param axisGridSubLineWidth the axis grid sub line width. + \sa axisGridSubLineWidth, setAxisGridLineWidth, setAxisShowGrid, setAxisShowSubDelimiters + */ +void KDChartAxisParams::setAxisGridSubLineWidth( int axisGridSubLineWidth ) +{ + _axisGridSubLineWidth = axisGridSubLineWidth; + emit changed(); +} + + +/** + \fn QColor KDChartAxisParams::axisGridSubLineWidth() const + Returns the axis grid sub line width. + + \return the axis grid sub line width. + \sa setAxisGridSubLineWidth + */ + + +/** + Specifies the axis grid line pattern. + + \param axisGridStyle the axis grid line pattern. + \sa axisGridStyle, setAxisShowGrid + */ +void KDChartAxisParams::setAxisGridStyle( PenStyle axisGridStyle ) +{ + _axisGridStyle = axisGridStyle; + emit changed(); +} + +/** + \fn PenStyle KDChartAxisParams::axisGridStyle() const + Returns the axis grid line pattern. + + \return the axis grid line pattern. + \sa setAxisGridStyle, setAxisShowGrid + */ + + +/** + Specifies the axis grid line pattern for the thin lines + showing the sub-delimiter values. + + \param axisGridStyle the axis grid line pattern for the thin lines + showing the sub-delimiter values. + \sa axisGridSubStyle, setAxisGridStyle, axisGridStyle + \sa setAxisShowGrid + */ +void KDChartAxisParams::setAxisGridSubStyle( PenStyle axisGridSubStyle ) +{ + _axisGridSubStyle = axisGridSubStyle; + emit changed(); +} + +/** + \fn PenStyle KDChartAxisParams::axisGridSubStyle() const + Returns the axis grid line pattern for the thin lines + showing the sub-delimiter values. + + \return the axis grid line pattern for the thin lines + showing the sub-delimiter values. + \sa setAxisGridSubStyle + \sa setAxisGridStyle, axisGridStyle, setAxisShowGrid + */ + +/** + Specifies the colour of the zero-line + that is drawn if zero is not at the lower + edge of the chart. + + \param axisZeroLineColor the zero-line colour. + \sa axisZeroLineColor + */ +void KDChartAxisParams::setAxisZeroLineColor( QColor axisZeroLineColor ) +{ + _axisZeroLineColor = axisZeroLineColor; + emit changed(); +} + +/** + \fn QColor KDChartAxisParams::axisZeroLineColor() const + Returns the colour used for the zero-value line + that is drawn if zero is not at the lower + edge of the chart. + + \return the zero-line colour. + \sa setAxisZeroLineColor + */ + +/** + Specifies whether the axis labels should be drawn. + + \param axisLabelsVisible if true the labels of this axis will be + drawn. + \sa axisLabelsVisible + */ +void KDChartAxisParams::setAxisLabelsVisible( bool axisLabelsVisible ) +{ + _axisLabelsVisible = axisLabelsVisible; + emit changed(); +} + + +/** + \fn bool KDChartAxisParams::axisLabelsVisible() const + Returns whether the axis labels should be drawn. + + \return whether the axis labels should be drawn. + \sa setAxisLabelsVisible + */ + + +/** + \fn void KDChartAxisParams::setAxisLabelsFontMinSize( int axisLabelsFontMinSize ) + + Specifies the minimal font size to be used for displaying the axis labels. + + Use this to specify the minimal font size to be used for axis labels, + in case KD Chart is calculating the axis labels fonts dynamically. Default value is 10 points. + + \sa setAxisLabelsFont, setAxisLabelsFontUseRelSize, setAxisLabelsFontRelSize + */ + + + +/** + Specifies whether the axis labels start and end at the + edges of the charts instead being positioned in the + middle of the first data point (or the last one, resp.) + + \param axisLabelsTouchEdges if the axis labels start and end at the + edges of the charts instead being positioned in the + middle of the first data point (or the last one, resp.) + + \sa axisLabelsTouchEdges + */ +void KDChartAxisParams::setAxisLabelsTouchEdges( bool axisLabelsTouchEdges ) +{ + _axisLabelsTouchEdges = axisLabelsTouchEdges; + emit changed(); +} + +/** + \fn bool KDChartAxisParams::axisLabelsTouchEdges() const + Returns whether the axis labels start and end at the + edges of the charts instead being positioned in the + middle of the first data point (or the last one, resp.) + + \return whether the axis labels start and end at the + edges of the charts instead being positioned in the + middle of the first data point (or the last one, resp.) + \sa setAxisLabelsTouchEdges + */ + +/** + Specifies the axis labels font. + + \note The font size will be ignored if \c useFontSize is false, + in this case the font size will be calculated dynamically using + the value stored by you calling setAxisLabelsFontRelSize(). + + \param axisLabelsFont the font to be used for the axis' labels. + \param useFontSize set ti true if the fixed font size of + the \c axisLabelsFont is to be used, otherwise the font size + will be calculated dynamically. + + \sa setAxisLabelsFontRelSize, setAxisLabelsFontUseRelSize + \sa axisLabelsFont, axisLabelsFontRelSize + */ +void KDChartAxisParams::setAxisLabelsFont( QFont axisLabelsFont, bool useFontSize ) +{ + _axisLabelsFont = axisLabelsFont; + _axisLabelsFontUseRelSize = ! useFontSize; + emit changed(); +} +/** + \fn QFont KDChartAxisParams::axisLabelsFont() const + Returns the axis labels font. + + \return the axis labels font. + \sa setAxisLabelsFont, setAxisLabelsFontRelSize + \sa axisLabelsFontRelSize + */ + +/** + Specifies whether axis labels shall be drawn + using relative font size. + + \param axisLabelsFontUseRelSize whether axis labels shall be drawn + using relative font size. + If true the absolute value of the value set by \c + setAxisLabelsFontRelSize is per thousand + of of the printable area size + to be used. This will make the axis look the same even if scaled + to very different size. + + \sa setAxisLabelsFontRelSize, setAxisLabelsFont + */ +void KDChartAxisParams::setAxisLabelsFontUseRelSize( bool axisLabelsFontUseRelSize ) +{ + _axisLabelsFontUseRelSize = axisLabelsFontUseRelSize; + emit changed(); +} + +/** + \fn bool KDChartAxisParams::axisLabelsFontUseRelSize() const + Returns whether the fix axis font size is used. + + \return whether the fix axis labels font size is used. + \sa setAxisLabelsFontRelSize, setAxisLabelsFont + */ + + +/** + Specifies the axis labels relative font size. + + \param axisLabelsFontRelSize the relative axis font size. + If this value unequals zero the absolute value is per thousand + of the printable area width size + to be used. This will make the axis look the same even if scaled + to very different size. + + \sa setAxisLabelsFontUseRelSize, setAxisLabelsFont + */ +void KDChartAxisParams::setAxisLabelsFontRelSize( int axisLabelsFontRelSize ) +{ + _axisLabelsFontRelSize = axisLabelsFontRelSize; + emit changed(); +} + + +/** + \fn int KDChartAxisParams::axisLabelsFontRelSize() const + Returns the axis labels relative font size. + + \return the axis labels relative font size. + \sa setAxisLabelsFontRelSize, setAxisLabelsFontUseRelSize + */ + +/** + \fn bool KDChartAxisParams::axisLabelsDontShrinkFont() const + Specifies whether the axis labels' font size may be shrinked + to avoid overwriting neighboring areas. + + \sa axisLabelsDontShrinkFont + \sa setAxisLabelsDontAutoRotate, setAxisLabelsRotation + \sa setAxisLabelsFontUseRelSize, setAxisLabelsFont + */ + + +/** + \fn void KDChartAxisParams::setAxisLabelsDontAutoRotate( bool labelsDontAutoRotate ) + + Specifies whether the axis labels may be rotated + to avoid overwriting neighboring areas. + + \sa axisLabelsDontAutoRotate + \sa setAxisLabelsDontShrinkFont, setAxisLabelsRotation + \sa setAxisLabelsFontUseRelSize, setAxisLabelsFont + */ + +/** + \fn bool KDChartAxisParams::axisLabelsDontAutoRotate() const + Returns whether the axis labels may not be rotated + to avoid overwriting neighboring areas. + + \return whether the axis labels may not be rotated + to avoid overwriting neighboring areas. + \sa setAxisLabelsDontAutoRotate + \sa axisLabelsDontShrinkFont, setAxisLabelsRotation + \sa setAxisLabelsFontRelSize, setAxisLabelsFont + */ + +/** + \fn void KDChartAxisParams::setAxisLabelsRotation( int rotation ) + Specifies by how many degrees the axis labels shall be rotated. + + \param rotation The rotation of the labels - value must be either + zero or between 360 and 270. 360 degrees means don't rotate. + + \note This setting specifies the <b>minimum</b> rotation of + the labels. Rotation may be increased to fit labels into + available space unless you explicitely called the + setAxisLabelsDontAutoRotate() function. + + \sa axisLabelsDontAutoRotate + \sa setAxisLabelsDontShrinkFont, setAxisLabelsRotation + \sa setAxisLabelsFontUseRelSize, setAxisLabelsFont + */ + + +/** + \fn int KDChartAxisParams::axisLabelsRotation() const + Returns by how many degrees the axis labels will be rotated. + + \sa setAxisLabelsDontAutoRotate + \sa axisLabelsDontShrinkFont, setAxisLabelsRotation + \sa setAxisLabelsFontRelSize, setAxisLabelsFont + */ + + +/** + Specifies the axis labels colour. + + \param axisLabelsColor the axis labels colour. + \sa axisLabelsColor + */ +void KDChartAxisParams::setAxisLabelsColor( QColor axisLabelsColor ) +{ + _axisLabelsColor = axisLabelsColor; + emit changed(); +} + +/** + \fn QColor KDChartAxisParams::axisLabelsColor() const + Returns the axis labels colour. + + \return the axis labels colour. + \sa setAxisLabelsColor + */ + +/** + Specifies the calculations to be applied to the axis labels. + + \param divPow10 The power of 10 which the data value is to be divided by. + A value of 2 means divide by 100, a value of -3 means multiply by 1000, + and 0 (by definition) would result in multiplying by 1. + \param digitsBehindComma The number of digits to show behind the comma, + to have this calculated automatically just use the default value + KDCHART_DATA_VALUE_AUTO_DIGITS. + \sa setAxisLabelsFormat + \sa setAxisLabelsNotation + \sa axisLabelsDivPow10, axisLabelsDigitsBehindComma + */ +void KDChartAxisParams::setAxisLabelsCalc( int divPow10, + int digitsBehindComma ) +{ + _axisLabelsDivPow10 = divPow10; + _axisDigitsBehindComma = digitsBehindComma; + emit changed(); +} + +/** + Specifies the way how the axis label strings will be formatted. Will be ignored for non-numerical axis labels. + + \param decimalPoint The 'radix character' (or text, resp.) to be inserted + into the string (default is '.'). + \param thousandsPoint The character (or text, resp.) to be used as delimiter + between the hundred digit and the thousand digit and between the + 100.000 and the 1.000.000 digit (default is ','). + + \sa setAxisLabelsFormat, setAxisLabelsCalc + \sa setAxisLabelsNotation + \sa axisLabelsDecimalPoint, axisLabelsThousandsPoint + */ +void KDChartAxisParams::setAxisLabelsRadix( const QString& decimalPoint, + const QString& thousandsPoint ) +{ + _axisLabelsDecimalPoint = decimalPoint; + _axisLabelsThousandsPoint = thousandsPoint; +} + + +/** + Specifies the way how the number part of the axis label strings will be formatted. + Will be ignored for non-numerical axis labels. + + \param notation The way of notation to be used for the number part. + + \note If you need exponential notation with a common magnitude, just use + setAxisLabelsCalc to declare the divisor, and then add a KDChartCustomBox + to the end of your axis's area, informing the user about the magnitude to + be added to each of the values, e.g. by saying "x 1e3". + + \sa KDChartEnums::NumberNotation, setAxisLabelsCalc, setAxisLabelsRadix, setAxisLabelsFormat + */ +void KDChartAxisParams::setAxisLabelsNotation( KDChartEnums::NumberNotation notation ) +{ + _axisLabelsNotation = notation; +} + +/** + Specifies the way how the axis label strings will be formatted. Will be ignored for non-numerical axis labels. + + \param prefix The character (or text, resp.) to be prepended before the string + after inserting the decimalPoint and the thousandsPoint. + \param postfix The character (or text, resp.) to be appended to the string + after adding decimalPoint, thousandsPoint, prefix. + \param totalLen The forced size of the string after adding decimalPoint, + thousandsPoint, prefix, postfix. If this parameter is set to zero + (default) no padding will be performed. + If the string has more characters than the (non-zero) value of totalLen, + the respective number of characters will be cut off at the right side. + \param padFill The padding character to be prepended before the string + (or inserted into the string, resp.) when increasing its length until + the totalLen is reached. + \param blockAlign If set to true (default) the padFill character(s) will be + inserted between the number and the prefix, if set to false they will be + prepended before the prefix. + + \sa setAxisLabelsRadix, setAxisLabelsCalc + \sa setAxisLabelsNotation + \sa axisLabelsPrefix, axisLabelsPostfix, axisLabelsTotalLen + \sa axisLabelsPadFill, axisLabelsBlockAlign + */ +void KDChartAxisParams::setAxisLabelsFormat( const QString& prefix, + const QString& postfix, + const int& totalLen, + const QChar& padFill, + const bool& blockAlign ) +{ + _axisLabelsPrefix = prefix; + _axisLabelsPostfix = postfix; + _axisLabelsTotalLen = totalLen; + _axisLabelsPadFill = padFill; + _axisLabelsBlockAlign = blockAlign; +} + + + +/** + \c enum LabelsFromDataRow { LabelsFromDataRowYes, + LabelsFromDataRowNo, + LabelsFromDataRowGuess }; + + Are the axis labels stored in a data row? + If \c LabelsFromDataRowGuess we assume yes only if + all the entries of that data row contain strings - no numbers. + + \li \c LabelsFromDataRowYes = label texts are to be taken from data row + \li \c LabelsFromDataRowNo = do not take label texts from data row + \li \c LabelsFromDataRowGuess = take label texts from data row if all + entries in that row are strings (no numerical values!) + + \sa setAxisValues, setAxisValueStart, setAxisValueEnd, setAxisValueDelta + \sa setLabelTextsFormDataRow + \sa axisLabelTextsFormDataRow + */ + + +/** + Converts the specified labels from data row enum to a string + representation. + + \param mode the enum to convert + \return the string representation of the mode enum + */ +QString KDChartAxisParams::labelsFromDataRowToString( LabelsFromDataRow mode ) { + switch( mode ) { + case LabelsFromDataRowYes: + return "Yes"; + case LabelsFromDataRowNo: + return "No"; + case LabelsFromDataRowGuess: + return "Guess"; + default: // should not happen + qDebug( "Unknown labels from data row mode" ); + return "Guess"; + } +} + +/** + Converts the specified string to a data row enum value. + + \param string the string to convert + \return the data row mode enum value + */ +KDChartAxisParams::LabelsFromDataRow KDChartAxisParams::stringToLabelsFromDataRow( const QString& type ) { + if( type == "Yes" ) + return LabelsFromDataRowYes; + else if( type == "No" ) + return LabelsFromDataRowNo; + else if( type == "Guess" ) + return LabelsFromDataRowGuess; + else // should not happen + return LabelsFromDataRowGuess; +} + +/** + + \c enum ValueScale { ValueScaleNumerical = 0, ( have gaps here to allow specifying of additional scaling steps in between ) + ValueScaleSecond = 20, + ValueScaleMinute = 30, + ValueScaleHour = 40, + ValueScaleDay = 50, + ValueScaleWeek = 60, + ValueScaleMonth = 70, + ValueScaleQuarter = 80, + ValueScaleYear = 90 }; + + Are axis labels scaled mumerically or fixing a time period? + If \c ValueScaleNumerical axis labels are just numbers like "item 1", "item 2"... + + \li \c ValueScaleSecond = seconds + \li \c ValueScaleMinute = minutes + .. + \li \c ValueScaleYear = years + + \sa setAxisValues, setAxisValueStart, setAxisValueEnd, setAxisValueDelta + \sa setLabelTextsFormDataRow + \sa axisLabelTextsFormDataRow + */ + + +/** + +Note: The following text is to become part of the setAxisValues() doku +you see below. +It shall be added once the automatic string support has been added! + + +Currently the following strings are supported by the automatical +numbering feature. (These strings are implemented for your convenience, +you may specify any other set of label texts by passing a \c QStringList +pointer to \c axisLabelStringList.) + +\li Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday +\li January, February, March, May, June, July, August, September, October, November, December +\li Spring, Summer, Autumn, Winter + +... + +To specify the start value and the way the label values +are calculated you may + +\li \b either pass one of the <b>above strings</b> as start value +<br> +In this case the label texts will be taken from the respective +list restarting at the beginnig whenever the end is reached. +<br> +In case you specify a non-zero integer value for +\c axisValueDelta the width and direction +of steps will be performed accordingly: +<br> +A value of <b>1.0</b> +(specifying a step-length of 1 ) will cause every entry to be +taken. +<br> +A <b>4.0</b> would take the start entry, skip 3 entries, take +the 4th entry and so forth... +<br> +By using negative numbers you can specify backward stepping. +<br> +(Of course all non-integer values will be ignored here.) +*/ + +/** + <B>General axis texts setup routine.</B><BR> + Specifies how the axis limits shall be calculated + and specifies the lower and the upper limit, + the step-width and the list of strings + (if such is to be used). + + \note Start and end values are \c KDChartData so you may either + specify numerical data or pass a string. <br> + However if passing a string make sure to also specify a + valid QStringList* for \c axisLabelStringList. + In that case it could be wise to also also specify a second + QStringList* for \c axisShortLabelsStringsList to be used in + case the axes area is not wide enough to display the + label texts in their full length. + + <P> + + To specify the start value and the way the label values + are calculated you may + + \li \b either pass a <b>string</b> as start value + <br> + In this case the label texts will be taken from the string + list passed to the function via \c axisLabelStringList + starting with the string you specified as start value and + restarting at the beginnig whenever the end is reached. + In case the start value is not contained in the + \c axisLabelStringList list it will be ignored and labelling + will be done as if you had specified + KDCHART_AXIS_LABELS_AUTO_LIMIT as start value (see below). + <br> + In case you specify a non-zero integer value for + \c axisValueDelta the width and direction + of steps will be performed accordingly: + <br> + A value of <b>1.0</b> + (specifying a step-length of 1 ) will cause every entry + of the list to be taken. + <br> + A <b>4.0</b> would take the start entry, skip 3 entries, take + the 4th entry and so forth... + <br> + By using negative numbers you can specify backward stepping. + <br> + (Of course all non-integer values will be ignored here.) + \li \b or pass <b>KDCHART_AXIS_LABELS_AUTO_LIMIT</b> as start value + <br> + In this case the first label text <br> + <i>either</i> will be calculated + based upon the lowest value of the associated + datasets row number \c labelTextsDataRow if + \c axisLabelsFromDataRow is set to true, <br> + <i>or</i> the first entry of the texts list will be taken + if \c axisLabelStringList is set properly, <br> + <i>otherwise</i> it will be set to "1". + + \li \b or pass a <b>numerical value</b> as start value + <br> + In this case the first label text will be set to that value. + <br> + <b>In either case</b> (KDCHART_AXIS_LABELS_AUTO_LIMIT or numerical value) + the \c axisValueDelta may be used to specify the value to be + added to obtain the next values: + <br> + A value of <b>1.0</b> will cause 1.0 to be added to the current + value to make the next value. + <br> + A <b>-0.25</b> would cause 0.25 to be subtracted from the current value. + <br> + (Negative or non-integer numbers are allowed here.) + +<P> + +Examples: + +\verbatim +setAxisValues(); +\endverbatim +This would specify a default ordinate-axis obtaining its values from the +attached dataset. Looks nice but it is difficult to compare it to another +chart representing slightly different min/max values since it neither starts +at zero nor ends at an allways same value. + +\verbatim +setAxisValues( true, KDChartData( 0.0 ) ); +\endverbatim +This would specify a half-automatical ordinate-axis obtaining its values from +the attached dataset. Looks nice and can be slightly easier compared to +another chart since it allways starts at zero, causing negative values to be +ignored completely. + +\verbatim +setAxisValues( true, KDChartData( -2.0 ), + KDChartData( 7.5 ), + 0.5, 1 ); +\endverbatim +This would specify an ordinate-axis \not obtaining its values +from the attached dataset. +<b>Since both the start value and the end value are specified the range +of the dataset values are ignored completely.</b> +It will show one digit behind the comma. +The resulting chart can be perfectly compared to +another chart since it allways starts and ends at the very same level - no +matter what the dataset values are about actually. + +\verbatim +setAxisValues( false, KDChartData( 0.0 ), + KDChartData( 3.5), + 0.25, 2 ); +\endverbatim +This would specify a default abscissa-axis starting with value 0.0 and +adding 0.25 to get the next value, will count until 3.5. +It will show two digits behind the comma. + +\verbatim +setAxisValues( false, + KDChartData( 1964.0 ), KDCHART_AXIS_LABELS_AUTO_LIMIT, 1, 0 ); +\endverbatim +This would specify a default abscissa-axis starting with value 1964 and +adding 1 to get the next value. +It will show no digits behind the comma. + +\verbatim +KDChartParams p; +KDChartAxisParams pa( p.axisParams( KDChartAxisParams::AxisPosBottom ) ); + +QStringList abscissaNames; +abscissaNames << "Sunday" << "Monday" << "Tuesday" << "Wednesday" +<< "Thursday" << "Friday" << "Saturday"; + +QStringList abscissaShortNames; +abscissaShortNames << "Sun" << "Mon" << "Tue" << "Wed" +<< "Thu" << "Fri" << "Sat"; + +pa.setAxisValues( false, + KDChartData( "Monday" ), + KDCHART_AXIS_LABELS_AUTO_LIMIT, + KDCHART_AXIS_LABELS_AUTO_DELTA, + KDCHART_AXIS_LABELS_AUTO_DIGITS, + KDChartAxisParams::LabelsFromDataRowNo, + 0, + &abscissaNames, + &abscissaShortNames ); +p.setAxisParams( KDChartAxisParams::AxisPosBottom, pa ); +\endverbatim +This would specify a default abscissa-axis starting with 'Monday' and +counting the days of the week as far as neccessary to represent +all the entries in the associated dataset. +Note this \c LabelsFromDataRowNo indicating that the texts are <b>not</b> to +be taken from a data row and note \c &abscissaNames indicating the +\c QStringList where to take the texts from instead. (In case the axis area + is not wide enough to display the strings in their full length their + counterparts stored in abscissaShortNames will be displayed instead.) +<b>Note also:</b> The strings in those two QStringList are <b>not</b> +copied into the \c KDChartAxisParams nor into the \c KDChartParams +so please make sure the Lists are alive and valid till the end of +the param objects. Otherwise you will not be able to display the +texts. +<P> + +\param axisSteadyValueCalc specifies whether label values shall be calculataed based upon the associated dataset values (normally this is true for ordinate axes) or based upon some string list (as you might expect it for abscissa labels). +\param axisValueStart specifies first label value to be written. +\param axisValueEnd specifies the last label value to be written. +\param axisValueDelta specifies the length of the steps to be taken from one label text to the next one. +\param axisDigitsBehindComma specifies how many digits are to be shown behind the axis label texts comma. +\param axisMaxEmptyInnerSpan specifies the percentage of the y-axis range that may to contain NO data entries, if - and only if - axisValueStart (or axisValueEnd, resp.) is set to KDCHART_AXIS_LABELS_AUTO_LIMIT. To prevent \c setAxisValues from changing the current setting you may specify KDCHART_DONT_CHANGE_EMPTY_INNER_SPAN_NOW here, to deactivate taking into account the inner span entirely just use KDCHART_AXIS_IGNORE_EMPTY_INNER_SPAN. +\param takeLabelsFromDataRow specifies whether the labels texts shall be taken from a special row (reserved for this in each dataset) or not. +\param axisLabelStringList points to a \c QStringList containing the label texts to be used. +\param axisShortLabelsStringList points to a \c QStringList containing the label texts to be used in case their full-size counterparts cannot be shown due to limited axis area size. +\param axisValueLeaveOut is used for horizontal (top or bottom) axes only; it specifies whether some of the axis labels are to be skipped if there is not enough room for drawing them all without overlapping - this parameter may be set to KDCHART_AXIS_LABELS_AUTO_LEAVEOUT or to zero or to another positive value. +\param axisValueDeltaScale is used to specify the scaling mode of \c axisValueDelta - either just ValueScaleNumbers of a special time scale indicator. +\sa setAxisValueStart, setAxisValueEnd, setAxisValueDelta, setAxisValuesDecreasing +\sa axisValueStart axisValueEnd, axisValueDelta, ValueScale +\sa LabelsFromDataRow, axisLabelTextsFormDataRow, axisLabelTexts +\sa axisSteadyValueCalc, setAxisValueLeaveOut +*/ +void KDChartAxisParams::setAxisValues( bool axisSteadyValueCalc, + const QVariant& axisValueStart, + const QVariant& axisValueEnd, + double axisValueDelta, + int axisDigitsBehindComma, + int axisMaxEmptyInnerSpan, + LabelsFromDataRow takeLabelsFromDataRow, + int labelTextsDataRow, + QStringList* axisLabelStringList, + QStringList* axisShortLabelsStringList, + int axisValueLeaveOut, + ValueScale axisValueDeltaScale ) +{ + _axisSteadyValueCalc = axisSteadyValueCalc; + _axisValueStart = axisValueStart; + _axisValueEnd = axisValueEnd; + _axisValueLeaveOut = axisValueLeaveOut; + _axisValueDelta = axisValueDelta; + _axisValueDeltaScale = axisValueDeltaScale; + _axisDigitsBehindComma = axisDigitsBehindComma; + if ( KDCHART_DONT_CHANGE_EMPTY_INNER_SPAN_NOW != axisMaxEmptyInnerSpan ) { + if ( 100 < axisMaxEmptyInnerSpan + || 1 > axisMaxEmptyInnerSpan ) + _axisMaxEmptyInnerSpan = KDCHART_AXIS_IGNORE_EMPTY_INNER_SPAN; + else + _axisMaxEmptyInnerSpan = axisMaxEmptyInnerSpan; + } + _takeLabelsFromDataRow = takeLabelsFromDataRow; + _labelTextsDataRow = labelTextsDataRow; + if( axisLabelStringList ) + _axisLabelStringList = *axisLabelStringList; + else + _axisLabelStringList.clear(); + if( axisShortLabelsStringList ) + _axisShortLabelsStringList = *axisShortLabelsStringList; + else + _axisShortLabelsStringList.clear(); + // label texts must be recalculated? + setAxisLabelTexts( 0 ); + setTrueAxisDeltaPixels( 0.0 ); + setTrueAxisLowHighDelta( 0.0, 0.0, 0.0 ); + setTrueAxisDtLowHighDeltaScale( QDateTime(), QDateTime(), ValueScaleDay ); + emit changed(); +} + +/** + \fn void KDChartAxisParams::setAxisValueStart( const KDChartData axisValueStart ) + Specifies the lower limit for the axis labels: the start value. + + \param axisValueStart the lower limit for the axis labels: the start + value. + \sa setAxisValues, setAxisValueStartIsExact + \sa setAxisValueEnd, setAxisValueDelta + \sa axisValueStart, axisValueEnd, axisValueDelta + \sa axisLabelsFromDataRow, axisLabelTexts + \sa axisLabelStringList, axisShortLabelsStringList + */ + +/** + \fn KDChartData KDChartAxisParams::axisValueStart() const + Returns the lower limit for the axis labels: the start value. + + \return the lower limit for the axis labels: the start value. + \sa setAxisValues, setAxisValueStart, setAxisValueStartIsExact + \sa setAxisValueEnd, setAxisValueDelta + \sa axisValueEnd, axisValueDelta + \sa axisLabelsFromDataRow, axisLabelTexts + \sa axisLabelStringList, axisShortLabelsStringList + */ + +/** + \fn void setAxisValueStartIsExact( bool isExactValue ) + Specifies whether the lower limit for the axis labels that + is specified via setAxisValueStart() is to be used as the + axis start value even if this will not look very nice: this is + the default, KD Chart just takes the value specified by you, e.g. + if you specify 75003.00 as start value you will get exactly this. + + By setting this flag to FALSE you let KD Chart find a better + value for you: if your value is not Zero the axis will start + with the next value lower than your start value that can be + divided by the delta factor. + + \param isExactValue set this to FALSE if KD Chart shall find + a better value than the one you have specified by setAxisValueStart() + \sa setAxisValues, setAxisValueEnd, setAxisValueDelta + \sa axisValueStartIsExact, axisValueStart + \sa axisValueEnd, axisValueDelta + \sa axisLabelsFromDataRow, axisLabelTexts + \sa axisLabelStringList, axisShortLabelsStringList + */ + +/** + \fn bool axisValueStartIsExact() const + Returns whether the lower limit for the axis labels that + is specified via setAxisValueStart() is to be used as the + axis start value even if this will not look very nice: this is + the default, KD Chart just takes the value specified by you, e.g. + if you specify 75003.00 as start value you will get exactly this. + + \return whether the lower limit for the axis labels that + is specified via setAxisValueStart() is to be used as the + axis start value even if this will not look very nice. + \sa setAxisValues, setAxisValueStartIsExact, setAxisValueStart + \sa setAxisValueEnd, setAxisValueDelta + \sa axisValueEnd, axisValueDelta + \sa axisLabelsFromDataRow, axisLabelTexts + \sa axisLabelStringList, axisShortLabelsStringList + */ + + +/** + \fn void KDChartAxisParams::setAxisValueEnd( const KDChartData axisValueEnd ) + Specifies the upper limit for the axis labels: the end value. + + \param axisValueStart the upper limit for the axis labels: the end + value. + \sa setAxisValues, setAxisValueStart, setAxisValueStartIsExact + \sa setAxisValueDelta + \sa axisValueStart, axisValueEnd, axisValueDelta + \sa axisLabelsFromDataRow, axisLabelTexts + \sa axisLabelStringList, axisShortLabelsStringList + */ + +/** + \fn KDChartData KDChartAxisParams::axisValueEnd() const + Returns the upper limit for the axis labels: the end value. + + \return the upper limit for the axis labels: the end value. + \sa setAxisValues, setAxisValueStart, setAxisValueStartIsExact + \sa setAxisValueEnd, setAxisValueDelta + \sa axisValueStart, axisValueDelta + \sa axisLabelsFromDataRow, axisLabelTexts + \sa axisLabelStringList, axisShortLabelsStringList + */ + + +/** + Specifies the DELTA value for the axis labels: the distance + between two labels. + + \param axisValueDelta the DELTA value for the axis labels: the distance + between two labels. + \param scale the scaling of the DELTA value + \sa ValueScale + \sa setAxisValues + \sa setAxisValueStart, setAxisValueStartIsExact + \sa setAxisValueEnd, setAxisValueDelta + \sa axisValueStart, axisValueEnd, axisValueDelta + \sa axisLabelsFromDataRow, axisLabelTexts + \sa axisLabelStringList, axisShortLabelsStringList + */ +void KDChartAxisParams::setAxisValueDelta( const double axisValueDelta, + ValueScale scale ) +{ + _axisValueDelta = axisValueDelta; + _axisValueDeltaScale = scale; +} + +/** + \fn double KDChartAxisParams::axisValueDelta() const + Returns the DELTA value for the axis labels: the distance + between two labels. + + \return the DELTA value for the axis labels: the distance + between two labels. + \sa setAxisValueDelta + */ + + +/** + \fn ValueScale KDChartAxisParams::axisValueDeltaScale() const + Returns the DELTA value scaling mode for the axis labels + + \sa setAxisValueDelta + */ + + + +/** + \fn void KDChartAxisParams::setAxisValueLeaveOut( const int leaveOut ) + Specifies how many axis labels are to be skipped + if there is not enough space for displaying all of them. + This is usefull in case you have lots of entries in one dataset. + + \sa setAxisValues + */ +/** + \fn int KDChartAxisParams::axisValueLeaveOut() const + Returns how many axis labels are to be skipped + if there is not enough space for displaying all of them. + + \sa setAxisValueLeaveOut + */ + +/** + \fn void KDChartAxisParams::setAxisValuesDecreasing( bool valuesDecreasing ) + Specifies whether axis values should be printed in reverted order: starting + with the highest label and decreasing until the lowest label is reached. + + \note This feature is supported for LINE charts only. + + \sa setAxisValues +*/ + +/** + \fn bool KDChartAxisParams::axisValuesDecreasing() const + Returns whether axis values should be printed in reverted order: starting + with the highest label and decreasing until the lowest label is reached. + + \note This feature is supported for LINE charts only. + + \sa setAxisValuesDecreasing + */ + +/** + \fn void KDChartAxisParams::setTrueAxisDeltaPixels( double nDeltaPixels ) + Specifies the true axis pixel distance between two label delimiters. + + \param nDeltaPixels the true value as it was calculated. + + \note Do <b>not call</b> this function unless you are knowing + exactly what you are doing. \c setTrueAxisDeltaPixels is normally + reserved for internal usage by methods calculating the axis + label texts. Thus the signal \c changed() is not sended here. + + \sa trueAxisDeltaPixels, trueAxisLow, trueAxisHigh, trueAxisDelta + \sa setAxisArea + */ + +/** + \fn double KDChartAxisParams::trueAxisDeltaPixels() const + Returns the <b>true</b> delimiter delta pixel value of the axis labels + as is was calculated and set by \c setTrueAxisDeltaPixels. + + \return the true delimiter delta pixel value of the axis labels + \sa setAxisValues + \sa trueAxisLow, trueAxisHigh, trueAxisDelta + */ + + + +/** + Specifies the true axis lower and upper limit values of the axis + and the exact Delta value between the axis delimiters. + + \param nLow/nHigh/nDelta the true values as they were calculated. + + \note Do <b>not call</b> this function unless you are knowing + exactly what you are doing. \c setAxisTrueAreaSize is normally + reserved for internal usage by methods calculating the axis + label texts. Thus the signal \c changed() is not sended here. + + \sa trueAxisLow, trueAxisHigh, trueAxisDelta, trueAxisDeltaPixels + \sa setAxisArea + */ +void KDChartAxisParams::setTrueAxisLowHighDelta( double nLow, double nHigh, double nDelta ) +{ + _trueLow = nLow; + _trueHigh = nHigh; + _trueDelta = nDelta; + +} + + +/** + \fn double KDChartAxisParams::trueAxisLow() const + Returns the <b>true</b> start value of the ordinate axis labels + as is was calculated and set by \c setTrueAxisLowHighDelta. + + \return the true lower limit of the axis labels + \sa setAxisValues + \sa trueAxisHigh, trueAxisDelta, trueAxisDeltaPixels + */ + +/** + \fn double KDChartAxisParams::trueAxisHigh() const + Returns the <b>true</b> end value of the ordinate axis labels + as is was calculated and set by \c setTrueAxisLowHighDelta. + + \return the true upper limit of the axis labels + \sa setAxisValues + \sa trueAxisLow, trueAxisDelta, trueAxisDeltaPixels + */ + +/** + \fn double KDChartAxisParams::trueAxisDelta() const + Returns the <b>true</b> delta value of the ordinate axis labels + as is was calculated and set by \c setTrueAxisLowHighDelta. + + \return the true delta value of the axis labels + \sa setAxisValues + \sa trueAxisLow, trueAxisHigh, trueAxisDeltaPixels + */ + + + +void KDChartAxisParams::setTrueAxisDtLowHighDeltaScale( QDateTime dtLow, QDateTime dtHigh, + ValueScale dtDeltaScale ) +{ + _trueDtLow = dtLow; + _trueDtHigh = dtHigh; + _trueDtDeltaScale = dtDeltaScale; +} + +/** + \fn void KDChartAxisParams::setTrueAxisDtLow( QDateTime dtLow ) + +*/ + +/** + \fn void KDChartAxisParams::void setTrueAxisDtHigh( QDateTime dtHigh ) + +*/ + +/** + \fn void KDChartAxisParams::void setTrueAxisDtScale( ValueScale scale ) + +*/ + +/** + \fn void KDChartAxisParams::QDateTime trueAxisDtLow() const + +*/ + +/** + \fn void KDChartAxisParams::QDateTime trueAxisDtHigh() const + +*/ + +/** + \fn void KDChartAxisParams::ValueScale trueAxisDtDeltaScale() const + +*/ + + +/** + Specifies the not-rounded screen positions where drawing of + this axis zero line started. + + \Note You may not call this internal function - it is reserved for + internal usage of methodes needing to know the zero-line offsets + + \param Pt the not-rounded screen positions where drawing of + this axis zero line started. + + \sa axisZeroLineStartX, axisZeroLineStartY + */ +void KDChartAxisParams::setAxisZeroLineStart( double x, double y ) +{ + _axisZeroLineStartX = x; + _axisZeroLineStartY = y; +} + +/** + \fn double KDChartAxisParams::axisZeroLineStartX() const + Returns the not-rounded x-position where drawing of + this axis zero line started. This function needed + when painting the data of isometric axes (bars, lines, dots...). + + \return the unrounded x-position where drawing of + this axis zero line started. + + \sa setAxisZeroLineStart, axisZeroLineStartY + */ + +/** + \fn double KDChartAxisParams::axisZeroLineStartY() const + Returns the not-rounded y-position where drawing of + this axis zero line started. This function needed + when painting the data of isometric axes (bars, lines, dots...). + + \return the unrounded y-position where drawing of + this axis zero line started. + + \sa setAxisZeroLineStart, axisZeroLineStartX + */ + +/** + Specifies the not-rounded screen positions where drawing of + this axis low date/time value could be done. + + \Note You may not call this internal function - it is reserved for + internal usage of methodes needing to know the zero-line offsets + + \param Pt the not-rounded screen positions where drawing of + this axis low date/time value could be done. + + \sa axisDtLowPosX, axisDtLowPosY + */ +void KDChartAxisParams::setAxisDtLowPos( double x, double y ) +{ + _axisDtLowPosX = x; + _axisDtLowPosY = y; +} + +/** + \fn double void KDChartAxisParams::axisDtLowPosX() const + Returns the not-rounded x-position where drawing of + this axis low date/time value could be done. This function needed + when painting the data of isometric axes (bars, lines, dots...). + + \return the unrounded x-position where drawing of + this axis low date/time value could be done. + + \sa setAxisDtLowPos, axisDtLowPosY + */ + +/** + \fn double KDChartAxisParams::axisDtLowPosY() const + Returns the not-rounded y-position where drawing of + this axis low date/time value could be done. This function needed + when painting the data of isometric axes (bars, lines, dots...). + + \return the unrounded y-position where drawing of + this axis low date/time value could be done. + + \sa setAxisDtLowPos, axisDtLowPosX + */ + + +/** + Specifies the not-rounded screen positions where drawing of + this axis high date/time value could be done. + + \Note You may not call this internal function - it is reserved for + internal usage of methodes needing to know the zero-line offsets + + \param Pt the not-rounded screen positions where drawing of + this axis high date/time value could be done. + + \sa axisDtHighPosX, axisDtHighPosY + */ +void KDChartAxisParams::setAxisDtHighPos( double x, double y ) +{ + _axisDtHighPosX = x; + _axisDtHighPosY = y; +} + +/** + \fn double KDChartAxisParams::axisDtHighPosX() const + Returns the not-rounded x-position where drawing of + this axis high date/time value could be done. This function needed + when painting the data of isometric axes (bars, lines, dots...). + + \return the unrounded x-position where drawing of + this axis high date/time value could be done. + + \sa setAxisDtHighPos, axisDtHighPosY + */ + +/** + \fn double KDChartAxisParams::axisDtHighPosY() const + Returns the not-rounded y-position where drawing of + this axis high date/time value could be done. This function needed + when painting the data of isometric axes (bars, lines, dots...). + + \return the unrounded y-position where drawing of + this axis high date/time value could be done. + + \sa setAxisDtHighPos, axisDtHighPosX + */ + + +/** + \fn void KDChartAxisParams::setAxisDigitsBehindComma( const int digits ) + Specifies the number of digits to be printed behind the comma + on the axis labels. + + \param digits the number of digits to be printed behind the comma + on the axis labels. + + \sa axisDigitsBehindComma + */ + +/** + \fn int KDChartAxisParams::axisDigitsBehindComma() const + Returns the number of digits to be printed behind the comma + on the axis labels. + + \return the number of digits to be printed behind the comma + on the axis labels. + \sa setAxisValues + \sa axisValueStart + \sa axisLabelsFromDataRow, axisLabelTexts + \sa axisLabelStringList, axisShortLabelsStringList + \sa setAxisLabelStringLists + */ + +/** + \fn void KDChartAxisParams::setAxisLabelsDateTimeFormat( const QString& format ) + Specifies the format to be used for displaying abscissa axis + QDateTime item labels. + + \note This feature is only available when using Qt 3.0 or newer, + previous versions use a non changable format. + + To calculate the format automatically (based on the + time span to be displayed) use the special value + \c KDCHART_AXIS_LABELS_AUTO_DATETIME_FORMAT - this is the default setting. + + See Qt documentation on the format to be used here: + + $QTDIR/doc/html/qdatetime.html#toString-2 + + \note Insert a '\n' character if you want to print the labels in two rows, e.g. "h:mm:ss\nd.MM.yyyy" would do that. + + \sa axisLabelsDateTimeFormat + */ + +/** + \fn QString KDChartAxisParams::axisLabelsDateTimeFormat() const + Returns the format to be used for displaying abscissa axis + QDateTime item labels. + + \sa setAxisLabelsDateTimeFormat + */ + + +/** + \fn void KDChartAxisParams::setAxisMaxEmptyInnerSpan( const int maxEmpty ) + Specifies the percentage of the y-axis range that may to contain NO + data entries, if - and only if - axisValueStart (or axisValueEnd, + resp.) is set to KDCHART_AXIS_LABELS_AUTO_LIMIT. + + \param maxEmpty the percentage of the y-axis range that may to contain NO + data entries, if - and only if - axisValueStart (or axisValueEnd, + resp.) is set to KDCHART_AXIS_LABELS_AUTO_LIMIT. + + \sa axisMaxEmptyInnerSpan + */ + +/** + \fn int KDChartAxisParams::axisMaxEmptyInnerSpan() const + Returns the percentage of the y-axis range that may to contain NO + data entries, if - and only if - axisValueStart (or axisValueEnd, + resp.) is set to KDCHART_AXIS_LABELS_AUTO_LIMIT. + + \note If more space is empty the zero-line will not be included info the chart but the lowest (or highest, resp.) entry of the axis will be shifted accordingly. + \sa setAxisValues + */ + + +/** + Specifies whether the axis labels are stored in a data row. + If \c LabelsFromDataRowGuess we assume yes only if + all the entries of that data row contain strings - no numbers. + + \note Calling this function results in overwriting the information + that you might have set by previous calls of that function. + Only <b>one</b> data row can be specified as containing label texts. + To specify a data row that contains (or might contain) axis label texts just + call this function with \c LabelsFromDataRowYes (or \c LabelsFromDataRowGuess, + resp.) specifying this row but do <b>not</b> call the function n times with + the \c LabelsFromDataRowNo parameter to 'deactivate' the other rows. + The \c LabelsFromDataRowNo should be used to specify that <b>none</b> of + the data rows is containing the axis label texts (this is the default + setting). + + \param row the data row number that contains (or might contain, resp.) the labels + \param mode the state of our information concerning that row (see: \c LabelsFromDataRow) + + \sa LabelsFromDataRow, axisLabelTextsFormDataRow, setAxisValues + */ +void KDChartAxisParams::setLabelTextsFormDataRow( int row, LabelsFromDataRow mode ) +{ + _labelTextsDataRow = row; + _takeLabelsFromDataRow = mode; +} + + +/** + \fn void void KDChartAxisParams::setLabelTextsFormDataRow( int row, LabelsFromDataRow mode ); + Returns whether the axis labels will be taken from the associated dataset. + + \return whether the axis limits will be taken from the associated dataset. + \sa setAxisValues + \sa axisValueStart, axisValueEnd + \sa axisLabelsFromDataRow, axisLabelTexts + */ + + +/** + \fn int KDChartAxisParams::labelTextsDataRow() const + Returns the number of the data row that contains (or might contain, + resp.) the texts to be taken for the axis labels. + <br> + Use \c axisLabelTextsFormDataRow to make sure the texts are + to be taken from that row. + <br> + Use \c axisLabelStringList to get a QStringList* of texts from + which the texts to be drawn will be taken. + + Use \c axisShortLabelsStringList to get a QStringList* of texts from + which the texts to be drawn will be taken in case the axis area size + is too small to display their full-size counterparts stored in + \c axisLabelStringList. + + Use \c axisLabelTexts to get a QStringList* containing the label + texts that are <b>actually</b> drawn at the axis. + + \return the number of the data row that contains (or might contain, + resp.) the texts to be taken for the axis labels. + \sa setAxisValues + \sa axisValueStart, axisValueEnd + \sa axisLabelsFromDataRow, axisLabelTexts + \sa axisLabelStringList, axisShortLabelsStringList + */ + + +/** + Specifies a \c QStringList which the axis label texts are to + be taken from, the second parameter (if not zero) specifies an + alternative list of strings that are to be displayed in case + the axis area size is too small for showing the full-length names. + + \note Normally axis labeling starts with the list's first string + and end with its last string, but by specifying a start and an + end value as additional parameters you can make KDChart repeat + the strings between these two values only, as shown here: + + \verbatim + QStringList abscissaNames; + abscissaNames << "Sunday" << "Monday" << "Tuesday" << "Wednesday" + << "Thursday" << "Friday" << "Saturday"; + QStringList abscissaShortNames; + abscissaShortNames << "Sun" << "Mon" << "Tue" << "Wed" + << "Thu" << "Fri" << "Sat"; + + KDChartAxisParams pa( _p->axisParams( + KDChartAxisParams::AxisPosBottom ) ); + + setAxisLabelStringParams( &abscissaNames, + &abscissaShortNames, + "Monday", + "Friday") + + _p->setAxisParams( KDChartAxisParams::AxisPosBottom, pa ); + \endverbatim + + + \param QStringList* axisLabelStringList points to the list of labels to be displayed + \param QStringList* axisShortLabelStringList points to + an alternative list of short names to be displayed if the long labels take too much place + \param QString valueStart ( default null ) the label to begin with + \param QString valueEnd ( default null ) the label to end with + + \sa KDChartParams::setAxisLabelStringParams + \sa axisLabelStringList, axisShortLabelsStringList + \sa setAxisValues, setLabelTextsFormDataRow + \sa axisLabelTexts + */ +void KDChartAxisParams::setAxisLabelStringLists( QStringList* axisLabelStringList, + QStringList* axisShortLabelStringList, + const QString& valueStart, + const QString& valueEnd ) +{ + QVariant axisValueStart, axisValueEnd; + + if( valueStart.isNull() ) + axisValueStart = KDCHART_AXIS_LABELS_AUTO_LIMIT; + else + axisValueStart = valueStart; + + if( valueEnd.isNull() ) + axisValueEnd = KDCHART_AXIS_LABELS_AUTO_LIMIT; + else + axisValueEnd = valueEnd; + + setAxisValues( false, + axisValueStart, + axisValueEnd, + KDCHART_AXIS_LABELS_AUTO_DELTA, + KDCHART_AXIS_LABELS_AUTO_DIGITS, + KDCHART_AXIS_IGNORE_EMPTY_INNER_SPAN, + LabelsFromDataRowNo, + 0, + axisLabelStringList, + axisShortLabelStringList, + KDCHART_AXIS_LABELS_AUTO_LEAVEOUT ); + +} + + +/** + \fn QStringList KDChartAxisParams::axisLabelStringList() const + Returns a \c QStringList containing the label texts to be used. + + Calling \c axisShortLabelsStringList() instead will return + another \c QStringList containing the label texts to be displayed + in case the axis area size is too small to show the full-size names. + + \note This is the list of texts you specified by \c setAxisValues + or by \c setAxisLabelStringLists. + The texts <b>actually</b> drawn at the axis are <b>not neccessarily</b> the + same as the ones in this list since (regarding Start and/or End and/or + Delta value) they might be only a subset of this list. Whenever label texts + are calculated automatically the resulting labels are also stored in a + second list that you may access via \c axisLabelTexts(). + + \return a \c QStringList containing the label texts to be used. + \sa axisShortLabelsStringList + \sa setAxisLabelStringLists + \sa setAxisValues + \sa axisValueStart, axisValueEnd, axisLabelTexts + \sa axisLabelsFromDataRow, setLabelTextsFormDataRow + */ + +/** + \fn uint KDChartAxisParams::axisLabelStringCount() const + Returns the number of strings stored as label texts, + the texts can be retrieved by calling \c axisLabelStringList(). + + \sa axisShortLabelsStringCount + \sa axisLabelStringList, axisShortLabelsStringList + */ + +/** + \fn QStringList KDChartAxisParams::axisShortLabelsStringList() const + Returns a \c QStringList containing the label texts to be used + in case the axis area size is too small to show the full-size + label texts. + + Calling \c axisLabelStringList() instead will return + another \c QStringList containing their full-size counterparts. + + \note This is the list of texts you specified by \c setAxisValues + or by \c setAxisShortLabelsStringList. + The texts <b>actually</b> drawn at the axis are <b>not neccessarily</b> the + same as the ones in this list since (regarding Start and/or End and/or + Delta value) they might be only a subset of this list. Whenever label texts + are calculated automatically the resulting labels are also stored in a + second list that you may access via \c axisLabelTexts(). + + \return a \c QStringList containing the label texts to be used + in case the axis area size is too small to show the full-size + label texts. + \sa axisLabelStringList + \sa setAxisLabelStringLists + \sa setAxisValues + \sa axisValueStart, axisValueEnd, axisLabelTexts + \sa axisLabelsFromDataRow, setLabelTextsFormDataRow + */ + +/** + \fn uint KDChartAxisParamsaxisShortLabelsStringCount() const + Returns the number of strings stored as label texts, + the texts can be retrieved by calling \c axisLabelStringList(). + + \sa axisLabelStringCount + \sa axisLabelStringList, axisShortLabelsStringList + */ + + +/** + \fn const QStringList* KDChartAxisParamsaxisLabelTexts() const + Returns a \c QStringList containing the label texts + that are <b>actually</b> drawn at the axis. + + In case the texts are unknown returns zero. + + \note This is the list of texts <b>actually</b> drawn at the axis. + This strings are not neccessarily the same as the ones in the list given by + \c setAxisValues since (regarding Start and/or End and/or Delta value) it + might be only a subset of that list. Whenever labels text are calculated + automatically the resulting labels also stored in this list - it will + allways be a true copy of the texts painted at the axis. + + \return a \c QStringList containing the label texts actually being used. + \sa setAxisValues + \sa axisValueStart, axisValueEnd, axisLabelStringList + \sa axisLabelsFromDataRow, setLabelTextsFormDataRow + \sa setAxisLabelStringLists, setAxisValues + */ + + + +/** + Specifies the label texts that are <b>actually</b> drawn + at the axis labels. + + \note Do not call this function unless you know what you are + doing. It is used internally whenever the texts to be drawn + have been re-calculated or the charts parameters have changed. + For specifying another list of strings to be used as label texts + you might rather want to call \c setAxisLabelStringLists() or + \c setLabelTextsFormDataRow() depending from whether your texts + are stored in a \c QStringList or in a data row. + + \param axisLabelTexts specifies the texts that are + <b>actually</b> drawn at the axis labels are unknown. + + \sa setAxisLabelStringLists, setLabelTextsFormDataRow, setAxisValues + \sa setAxisFirstLabelText, setAxisLastLabelText + */ +void KDChartAxisParams::setAxisLabelTexts( const QStringList* axisLabelTexts ) +{ + _axisLabelTexts.clear(); + _axisLabelTextsDirty = ( 0 == axisLabelTexts ); + if ( !_axisLabelTextsDirty ) + _axisLabelTexts = *axisLabelTexts; +} + +/** + \fn void KDChartAxisParamssetAxisLabelTextsDirty( bool axisLabelTextsDirty ) + Specifies whether the label texts are <b>actually</b> drawn + at the axis labels are unknown. If \c false, they could + successfully be retrieved by \c axisLabelTexts(). + + \note Do not call this function unless you know what you are + doing. It is used internally whenever the texts to be drawn + have been re-calculated or the charts parameters have changed. + For specifying another list of strings to be used as label texts + you might rather want to call \c setAxisLabelStringLists() or + \c setLabelTextsFormDataRow() depending from whether your texts + are stored in a \c QStringList or in a data row. + + \param axisLabelTextsDirty specifies whether the texts are + <b>actually</b> drawn at the axis labels are unknown. + + \sa setAxisLabelStringLists, setLabelTextsFormDataRow, setAxisValues + */ + +/** + \fn bool KDChartAxisParams::axisLabelTextsDirty() const + Returns whether the label texts that are <b>actually</b> drawn + at the axis labels are unknown. If \c false, they could + successfully be retrieved by \c axisLabelTexts(). + + \return whether the texts that are <b>actually</b> drawn at + the axis labels are unknown. + + \sa setAxisLabelStringLists, setLabelTextsFormDataRow, setAxisValues + */ + + +/** + Sets a special text that is to be displayed _instead_of_ + the first axis label IF the parameter is not a NULL string. + + \note This function does not affect axis label and axis range + calculation but it replaces the first label after all calculation + is done. This may be used in case you want to replace the first + label by some special text string, e.g. you might want to display + the text "origo" instead of the start value. + + To remove a first label string that was set by a previous call of this + function just call it again, with no parameter. + + \param axisFirstLabelText specifies the text that is + <b>actually</b> drawn as the first label: a NULL string + ( as produced by QString() ) will be ignored, to suppress + the first label please specify an EMPTY but NOT NULL string + by passing "" as parameter. + + + \sa setAxisLastLabelText + \sa setAxisLabelStringLists, setLabelTextsFormDataRow, setAxisValues + */ +void KDChartAxisParams::setAxisFirstLabelText( const QString& axisFirstLabelText ) +{ + _axisFirstLabelText = axisFirstLabelText; +} + +/** + Sets a special text that is to be displayed _instead_of_ + the last axis label IF the parameter is not a NULL string. + + \note This function does not affect axis label and axis range + calculation but it replaces the last label after all calculation + is done. This may be used in case you want to replace the last + label by some special text string, e.g. you might want to display + the text "maximum" instead of the end value. + + To remove a last label string that was set by a previous call of this + function just call it again, with no parameter. + + \param axisFirstLabelText specifies the text that is + <b>actually</b> drawn as the last label: a NULL string + ( as produced by QString() ) will be ignored, to suppress + the first label please specify an EMPTY but NOT NULL string + by passing "" as parameter. + + \sa setAxisFirstLabelText + \sa setAxisLabelStringLists, setLabelTextsFormDataRow, setAxisValues + */ +void KDChartAxisParams::setAxisLastLabelText( const QString& axisLastLabelText ) +{ + _axisLastLabelText = axisLastLabelText; +} + + + +/** + \fn void KDChartAxisParams::setAxisSteadyValueCalc( bool axisSteadyValueCalc ) + Specifies whether label values shall be calculated based upon the + associated dataset values (normally this is true for ordinate axes) + or based upon some string list (as you might expect it for abscissa + labels). + + \sa setAxisValues + */ + +/** + \fn bool KDChartAxisParams::axisSteadyValueCalc() const + Returns whether label values shall be calculataed based upon the associated + dataset values (normally this is true for ordinate axes) or based upon some + string list (as you might expect it for abscissa labels). + + \sa setAxisValues + */ + +/** + \fn KDChartAxisParams::KDChartAxisParams( const KDChartAxisParams& R ) : QObject() + Copy-constructor: By calling the copy method, + see also the assignment operator. + */ + + +/** + Assignment operator: By calling the copy method, + see also the copy constructor. + */ + +KDChartAxisParams& KDChartAxisParams::operator=( const KDChartAxisParams& R ) +{ + if ( this != &R ) + deepCopy( *this, R ); + return *this; +} + +void KDChartAxisParams::deepCopy( KDChartAxisParams& D, const KDChartAxisParams& R ) +{ + D._axisType = R._axisType; + D._axisVisible = R._axisVisible; + D._axisAreaMode = R._axisAreaMode; + D._axisUseAvailableSpaceFrom = R._axisUseAvailableSpaceFrom; + D._axisUseAvailableSpaceTo = R._axisUseAvailableSpaceTo; + D._axisAreaMin = R._axisAreaMin; + D._axisAreaMax = R._axisAreaMax; + D._axisCalcMode = R._axisCalcMode; + D._axisIsoRefAxis = R._axisIsoRefAxis; + D._axisTrueAreaSize = R._axisTrueAreaSize; + D._axisTrueAreaRect = R._axisTrueAreaRect; + D._axisZeroLineStartX = R._axisZeroLineStartX; + D._axisZeroLineStartY = R._axisZeroLineStartY; + D._axisDtLowPosX = R._axisDtLowPosX; + D._axisDtLowPosY = R._axisDtLowPosY; + D._axisDtHighPosX = R._axisDtHighPosX; + D._axisDtHighPosY = R._axisDtHighPosY; + D._axisLineVisible = R._axisLineVisible; + D._axisLineWidth = R._axisLineWidth; + D._axisTrueLineWidth = R._axisTrueLineWidth; + D._axisLineColor = R._axisLineColor; + // main grid: + D._axisShowFractionalValuesDelimiters = R._axisShowFractionalValuesDelimiters; + D._axisShowGrid = R._axisShowGrid; + D._axisGridColor = R._axisGridColor; + D._axisGridLineWidth = R._axisGridLineWidth; + D._axisGridStyle = R._axisGridStyle; + // sub grid: + D._axisShowSubDelimiters = R._axisShowSubDelimiters; + D._axisGridSubColor = R._axisGridSubColor; + D._axisGridSubLineWidth = R._axisGridSubLineWidth; + D._axisGridSubStyle = R._axisGridSubStyle; + + D._axisZeroLineColor = R._axisZeroLineColor; + D._axisLabelsVisible = R._axisLabelsVisible; + D._axisLabelsFont = R._axisLabelsFont; + D._axisLabelsFontUseRelSize = R._axisLabelsFontUseRelSize; + D._axisLabelsDontShrinkFont = R._axisLabelsDontShrinkFont; + D._axisLabelsDontAutoRotate = R._axisLabelsDontAutoRotate; + D._axisLabelsRotation = R._axisLabelsRotation; + D._axisLabelsFontRelSize = R._axisLabelsFontRelSize; + D._axisLabelsFontMinSize = R._axisLabelsFontMinSize; + D._axisLabelsColor = R._axisLabelsColor; + + D._axisSteadyValueCalc = R._axisSteadyValueCalc; + D._axisValueStartIsExact = R._axisValueStartIsExact; + D._axisValueStart = R._axisValueStart; + D._axisValueEnd = R._axisValueEnd; + D._axisValueDelta = R._axisValueDelta; + D._axisValueDeltaScale = R._axisValueDeltaScale; + D._axisValueLeaveOut = R._axisValueLeaveOut; + D._axisValuesDecreasing = R._axisValuesDecreasing; + D._axisDigitsBehindComma = R._axisDigitsBehindComma; + D._axisLabelsDateTimeFormat = R._axisLabelsDateTimeFormat; + D._axisMaxEmptyInnerSpan = R._axisMaxEmptyInnerSpan; + D._takeLabelsFromDataRow = R._takeLabelsFromDataRow; + D._labelTextsDataRow = R._labelTextsDataRow; + D._axisLabelStringList = R._axisLabelStringList; + D._axisShortLabelsStringList = R._axisShortLabelsStringList; + D._axisLabelTextsDirty = R._axisLabelTextsDirty; + + D._axisLabelsDivPow10 = R._axisLabelsDivPow10; + D._axisLabelsDecimalPoint = R._axisLabelsDecimalPoint; + D._axisLabelsNotation = R._axisLabelsNotation; + D._axisLabelsThousandsPoint= R._axisLabelsThousandsPoint; + D._axisLabelsPrefix = R._axisLabelsPrefix; + D._axisLabelsPostfix = R._axisLabelsPostfix; + D._axisLabelsTotalLen = R._axisLabelsTotalLen; + D._axisLabelsPadFill = R._axisLabelsPadFill; + D._axisLabelsBlockAlign = R._axisLabelsBlockAlign; + + D._axisFirstLabelText = R._axisFirstLabelText; + D._axisLastLabelText = R._axisLastLabelText; + + D._axisLabelTexts = R._axisLabelTexts; + D._trueAxisDeltaPixels = R._trueAxisDeltaPixels; + D._trueHigh = R._trueHigh; + D._trueLow = R._trueLow; + D._trueDelta = R._trueDelta; + D._trueDtLow = R._trueDtLow; + D._trueDtHigh = R._trueDtHigh; + D._trueDtDeltaScale = R._trueDtDeltaScale; +} + + +/** + Converts the specified axis type enum to a string representation. + + \param type the axis type enum to convert + \return the string representation of the axis type enum + */ +QString KDChartAxisParams::axisTypeToString( AxisType type ) { + switch( type ) { + case AxisTypeUnknown: + return "Unknown"; + case AxisTypeEAST: + return "East"; + case AxisTypeNORTH: + return "North"; + case AxisUP: + return "Up"; + default: // should not happen + qDebug( "Unknown axis type" ); + return "Unknown"; + } +} + +/** + Converts the specified string to an axis type enum value. + + \param string the string to convert + \return the axis type enum value + */ +KDChartAxisParams::AxisType KDChartAxisParams::stringToAxisType( const QString& type ) { + if( type == "Unknown" ) + return AxisTypeUnknown; + else if( type == "East" ) + return AxisTypeEAST; + else if( type == "North" ) + return AxisTypeNORTH; + else if( type == "Up" ) + return AxisUP; + else // should not happen + return AxisTypeUnknown; +} + + + +/** + \fn void KDChartAxisParams::changed() + \c Signals: + This signal is emitted when any of the chart axis + parameters have changed. + */ + +/** + \var AxisType _axisType + \c private: + + Specifies the axis type. + + \sa setAxisType + */ + +/** + \var bool _axisVisible + \c private: + Specifies whether this axis is to be drawn. False by default. + + \sa setAxisVisible + */ + + +/** + \var bool _axisLabelsTouchEdges + \private: + Specifies whether the axis labels start and end at the + edges of the charts instead being positioned in the + middle of the first data point (or the last one, resp.) + + \sa axisLabelsTouchEdges + */ + + +/** + \var AxisAreaMode _axisAreaMode + \private: + Specifies how to find out the size of the area to be + used by this axis. + + \sa setAxisAreaMode, setAxisAreaMin, setAxisAreaMax, setAxisArea + */ + +/** + \var int _axisUseAvailableSpaceFrom + \private: + Specifies the beginning offset of the space used by this axis + in comparison to the space that could be used by this axis. + + \sa setAxisUseAvailableSpace + */ + +/** + \var int _axisUseAvailableSpaceTo + \private: + Specifies the ending offset of the space used by this axis + in comparison to the space that could be used by this axis. + + \sa setAxisUseAvailableSpace + */ + +/** + \var int _axisAreaMin + Specifies the minimum axis area width (or height, resp.). + \sa setAxisAreaMin, setAxisAreaMode, setAxisAreaMax, setAxisArea + */ + +/** + \var int _axisAreaMax + Specifies the maximum axis area width (or height, resp.). + + \sa setAxisAreaMax, setAxisAreaMode, setAxisAreaMin, setAxisArea + */ + +/** + \var AxisCalcMode _axisCalcMode + Specifies the axis calculation mode. + + \sa setAxisCalcMode + */ +/** + \var uint _axisIsoRefAxis + Specifies which axis this axis shall be isometric with. + + \sa setIsometricReferenceAxis + */ + +/** + \var int _axisTrueAreaSize + Specifies the axis area width (or height, resp.) + as it was calculated and drawn. + + \sa setAxisAreaMax, setAxisAreaMode, setAxisAreaMin, setAxisArea + */ + +/** + \var QRect _axisTrueAreaRect + Specifies the true axis area rectangle + as it was calculated and drawn. + + \sa setAxisAreaMax, setAxisAreaMin, setAxisArea + */ + +/** + \var bool _axisShowSubDelimiters + Specifies whether the axis sub-delimiters will be drawn. + + \sa setAxisShowSubDelimiters + */ +/** + \var bool _axisLineVisible + Specifies whether the axis line is visible or not. + + \sa setAxisLineVisible + */ + +/** + \var int _axisLineWidth + Specifies the axis line width. + + \sa setAxisLineWidth + */ +/** + \var int _axisTrueLineWidth + Specifies the actual axis line width, as calculated and drawn. + + \sa setAxisTrueLineWidth + */ + +/** + \var QColor _axisLineColor + Specifies the axis line colour. + + \sa setAxisLineColor + */ + +/** + \var bool _axisShowGrid + Specifies whether a grid will be drawn at the chart data area. + + \sa setAxisShowGrid + */ + +/** + \var QColor _axisGridColor + Specifies the axis grid colour. + + \sa setAxisGridColor, setAxisShowGrid + */ + + +/** + \var int _axisGridLineWidth + Specifies the width of the axis grid lines. + + \sa setAxisGridLineWidth + */ + +/** + \var QColor _axisGridSubColor + Specifies the axis grid sub colour. + + \sa setAxisGridSubColor, setAxisShowGrid, setAxisShowSubDelimiters + */ + + +/** + \var int _axisGridSubLineWidth + Specifies the width of the axis grid sub lines. + + \sa setAxisGridSubLineWidth, setAxisShowGrid, setAxisShowSubDelimiters + */ + +/** + \var PenStyle _axisGridStyle + Specifies the axis grid line pattern for main grid lines. + + \sa setAxisGridStyle, setAxisShowGrid + */ + +/** + \var PenStyle _axisGridSubStyle + Specifies the axis grid line pattern for sub-delimiter grid lines. + + \sa setAxisGridSubStyle, setAxisGridStyle, setAxisShowGrid + */ + +/** + \var QColor _axisZeroLineColor + Specifies the zero-line colour. + + \sa setAxisZeroLineColor + */ + +/** + \var bool _axisLabelsVisible + Specifies whether the axis' labels are visible or not. + + \sa setAxisLabelsVisible + */ + +/** + \var QFont _axisLabelsFont + Specifies the axis labels font. + + \sa setAxisLabelsFont + \sa setAxisLabelsFontUseRelSize, setAxisLabelsFontRelSize + */ + +/** + \var bool _axisLabelsFontUseRelSize + Specifies whether the size of the label font is to be calculated + on a relative basis. + + \sa setAxisLabelsFontUseRelSize, setAxisLabelsFontRelSize + \sa setAxisLabelsFont + */ + +/** + \var int _axisLabelsFontRelSize + Specifies the per mille basis for calculating the relative + axis labels font size. + + \sa setAxisLabelsFontRelSize, setAxisLabelsFontUseRelSize + \sa setAxisLabelsFont + */ + +/** + \var QColor _axisLabelsColor + Specifies the axis labels color. + + \sa setAxisLabelsColor + */ + +/** + \var bool _axisSteadyValueCalc + Specifies whether label values shall be calculataed based upon the associated dataset values (normally this is true for ordinate axes) or based upon some string list (as you might expect it for abscissa labels). + + \sa setAxisValues + */ + +/** + \var KDChartData _axisValueStart; + Specifies the lower limit for the axis labels: the start value. + + \sa setAxisValues + */ + +/** + \var bool _axisValueStartIsExact; + Specifies whether the lower limit for the axis labels is + to be used the start value even if this might not look good. + + \sa setAxisValueStartIsExact + */ + +/** + \var KDChartData _axisValueEnd + Specifies the higher limit for the axis labels: the end value. + + \sa setAxisValues + */ + +/** + \var double _axisValueDelta + Stores the DELTA value for the axis labels: the distance + between two labels. + + \sa setAxisValues + */ + +/** + \var ValueScale _axisValueDeltaScale + Stores the scaling mode for axis labels: either just numbers + or a specified time scale (e.g. milliseconds or hours or months...) + */ + +/** + \var int _axisValueLeaveOut + Stores how many axis labels are to be skipped after drawing one. + This is usefull in case you have lots of entries in one dataset. + + \sa setAxisValues + */ + +/** + \var bool _axisValuesDecreasing; + Stores whether the axis labels are printed in reverted order. + + \sa setAxisValuesDecreasing + */ + +/** + \var double _trueAxisDeltaPixels + Stores the <b>true</b> delimiter delta pixel value of the axis labels + as is was calculated and set by \c setTrueAxisDeltaPixels. + + \sa setAxisValues + \sa trueAxisDeltaPixels + */ + +/** + \var double _trueLow + Specifies the <b>true</b> start value of the axis labels + as is was calculated and set by \c setTrueAxisLowHighDelta. + + \sa setAxisValues + \sa trueAxisLow, trueAxisHigh, trueAxisDelta + */ + +/** + \var double _trueHigh + Specifies the <b>true</b> end value of the axis labels + as is was calculated and set by \c setTrueAxisLowHighDelta. + + \sa setAxisValues + \sa trueAxisLow, trueAxisHigh, trueAxisDelta + */ + +/** + \var double _trueDelta + Specifies the <b>true</b> delta value of the axis labels + as is was calculated and set by \c setTrueAxisLowHighDelta. + + \sa setAxisValues + \sa trueAxisLow, trueAxisHigh, trueAxisDelta + */ +/** + \var double _axisZeroLineStartX + Specifies the not-rounded screen x-position where drawing of + this axis zero line started. + */ + +/** + \var double _axisZeroLineStartY + Specifies the not-rounded screen y-position where drawing of + this axis zero line started. + */ + +/** + \var int _axisDigitsBehindComma + Specifies the number of digits to be printed behind the comma + on the axis labels. + + \sa setAxisValues + */ + +/** + \var int _axisMaxEmptyInnerSpan + Specifies the percentage of the y-axis range that may to contain NO + data entries, if - and only if - axisValueStart (or axisValueEnd, + resp.) is set to KDCHART_AXIS_LABELS_AUTO_LIMIT. + + \sa setAxisValues + */ +/** + \var LabelsFromDataRow _takeLabelsFromDataRow + Specifies whether the axis labels shall be taken directly + from the entries of a data row. + + \sa setAxisValues + */ + + +/** + \var int _labelTextsDataRow + Specifies a data row which the axis labels shall be taken from. + + \sa setAxisValues + */ + +/** + \var QStringList _axisLabelStringList + Specifies a QStringList containing the label texts to be used. + + \sa _axisShortLabelsStringList + \sa setAxisValues, _axisLabelTexts, _axisLabelTextsDirty + */ + + +/** + \var QStringList _axisShortLabelsStringList + Specifies a QStringList containing the label texts to be used + in case the axis area is not wide enough to show their full-size + counterparts. + + \sa _axisLabelStringList + \sa setAxisValues, _axisLabelTexts, _axisLabelTextsDirty + */ + +/** + \var QStringList _axisLabelTexts + Contains the label texts <b>actually</b> being used. + + \sa setAxisValues, _axisLabelStringList, _axisLabelTextsDirty + */ + +/** + \var bool _axisLabelTextsDirty + Specifies whether the QStringList _axisLabelTexts content is invalid. + + \sa setAxisValues, _axisLabelTexts + */ diff --git a/kchart/kdchart/KDChartAxisParams.h b/kchart/kdchart/KDChartAxisParams.h new file mode 100644 index 00000000..5817b372 --- /dev/null +++ b/kchart/kdchart/KDChartAxisParams.h @@ -0,0 +1,515 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTAXISPARAMS_H__ +#define __KDCHARTAXISPARAMS_H__ + +#include <qfont.h> +#include <qcolor.h> +#include <qmap.h> +#include <qobject.h> +#include <qtextstream.h> +#include <qstringlist.h> +#include <qdatetime.h> +#include <qvariant.h> + +#include "KDChartGlobal.h" +#include "KDChartEnums.h" + +class KDChartParams; + +/** + Use this to specify that the axis label limits + are to be calculated automatically. + + \sa setAxisValues + */ +#define KDCHART_AXIS_LABELS_AUTO_LIMIT QVariant( 140319.64 ) +// If you edit the above, also edit KD Chart.cpp + +/** + Use this to specify that the step-width from one label + to the other shall be calculated automatically. + + \sa setAxisValues + */ +#define KDCHART_AXIS_LABELS_AUTO_DELTA 140319.64 + +/** + Use this to specify that an automatically computed amount of + axis labels are to be skipped if there is not enough space + for displaying all of them. + This is usefull in case you have lots of entries in one dataset. + + \sa setAxisValues + */ +#define KDCHART_AXIS_LABELS_AUTO_LEAVEOUT 14364 + +/** + Use this to specify that date/time format to be used for + displaying the axis labels is to be determined automatically. + */ +#define KDCHART_AXIS_LABELS_AUTO_DATETIME_FORMAT "AUTO" + +/** + Use this to specify that the number of digits to be shown + on the axis labels behind the comma is to be calculated + automatically. + + \sa setAxisValues + */ +#define KDCHART_AXIS_LABELS_AUTO_DIGITS 14364 + +/** + Use this to reset the grid colour to its default value + for re-doing changes made by calling \c setAxisGridColor(). + + \sa setAxisGridColor + */ + + +#define KDCHART_DEFAULT_AXIS_GRID_COLOR QColor( 0xA0, 0xA0, 0xA0 ) + +/** + Use this to specify that the width of the axis grid lines + shall be equal to the width of the axis line. + + \sa setAxisGridLineWidth + */ +#define KDCHART_AXIS_GRID_AUTO_LINEWIDTH 14364 + +/** + Use this to specify that the zero-line shall NOT be omitted + in case all of the data are grouped far away from zero. + + \sa setAxisValues + */ +#define KDCHART_AXIS_IGNORE_EMPTY_INNER_SPAN 1 + +/** + Use this to prevent \c setAxisValues from changing the current + setting of _axisMaxEmptyInnerSpan. + + \sa setAxisValues + */ + +#define KDCHART_DONT_CHANGE_EMPTY_INNER_SPAN_NOW -2 + + + + +/** \file KDChartAxisParams.h + \brief Provide access to the chart axis parameters. + + Use the KDChartAxisParams class to modify parameters of one axis each. + */ + +class KDCHART_EXPORT KDChartAxisParams : public QObject +{ + Q_OBJECT + Q_ENUMS(AxisType) + Q_ENUMS(AxisPos) + Q_ENUMS(AxisAreaMode) + Q_ENUMS(AxisCalcMode) + Q_ENUMS(LabelsFromDataRow) + Q_ENUMS(ValueScale) + + // Neede by QSA + Q_ENUMS( PenStyle ) + +public: + + enum AxisType { AxisTypeUnknown, + AxisTypeEAST, + AxisTypeNORTH, + AxisUP }; + +public slots: // PENDING(blackie) Go through this file and reorder, so all slots are in one section + void setAxisType( AxisType axisType ); + + + AxisType axisType() const { return _axisType; } + static QString axisTypeToString( AxisType type ); + static AxisType stringToAxisType( const QString& type ); + +public: + + // Attention: In case you extent AxisPos, make sure to also + // increase MAX_AXES and AxisPosEND. + enum AxisPos { AxisPosSTART = 0, + + AxisPosBottom = 0, + AxisPosSagittal = 0, // <-- for POLAR charts + AxisPosSaggital = 0, // <-- wrong spelling kept for backward compatibility + AxisPosLeft = 1, + AxisPosCircular = 1, // <-- for POLAR charts + AxisPosLowerRightEdge = 2, + + AxisPosTop = 3, + AxisPosRight = 4, + AxisPosLowerLeftEdge = 5, + + // diese Markierung muss jeweils mitgepflegt werden, + // wenn AxisPos erweitert werden sollte. + AxisPosAdditionalsSTART = 6, + + AxisPosBottom2 = 6, + AxisPosLeft2 = 7, + AxisPosLowerRightEdge2 = 8, + + AxisPosTop2 = 9, + AxisPosRight2 = 10, + AxisPosLowerLeftEdge2 = 11, + // auch diese Markierung muss jeweils mitgepflegt werden, + // wenn AxisPos erweitert werden sollte. + AxisPosEND = 11 }; + +public slots: + static AxisPos basicAxisPos( uint pos ); + void setAxisVisible( bool axisVisible ); + bool axisVisible() const; + +public: + enum AxisAreaMode { AxisAreaModeFixedSize, + AxisAreaModeAutoSize, + AxisAreaModeMinMaxSize }; + +public slots: + void setAxisAreaMode( AxisAreaMode axisAreaMode ); + AxisAreaMode axisAreaMode() const; + static QString axisAreaModeToString( AxisAreaMode mode ); + static AxisAreaMode stringToAxisAreaMode( const QString& type ); + void setAxisAreaMin( int axisAreaMin ); + + //Returns the axis area minimum width (or height, resp.). + int axisAreaMin() const { return _axisAreaMin; } + void setAxisUseAvailableSpace( int axisUseAvailableSpaceFrom, + int axisUseAvailableSpaceTo ); + int axisUseAvailableSpaceFrom() const { return _axisUseAvailableSpaceFrom; } + int axisUseAvailableSpaceTo() const { return _axisUseAvailableSpaceTo; } + void setAxisAreaMax( int axisAreaMax ); + int axisAreaMax() const { return _axisAreaMax; } + void setAxisArea( AxisAreaMode axisAreaMode, + int axisAreaMin, + int axisAreaMax ); + +public: + enum AxisCalcMode { AxisCalcLinear, AxisCalcLogarithmic }; + +public slots: + void setAxisCalcMode( AxisCalcMode axisCalcMode ); + AxisCalcMode axisCalcMode() const { return _axisCalcMode; } + static QString axisCalcModeToString( AxisCalcMode mode ); + static AxisCalcMode stringToAxisCalcMode( const QString& type ); + void setIsometricReferenceAxis( uint isoRefAxis ); + uint isometricReferenceAxis() const { return _axisIsoRefAxis; } + void setAxisTrueAreaSize( int axisTrueAreaSize ) { _axisTrueAreaSize = axisTrueAreaSize; } + int axisTrueAreaSize() const { return _axisTrueAreaSize; } + void setAxisTrueAreaRect( const QRect& axisTrueAreaRect ) { _axisTrueAreaRect = axisTrueAreaRect; } + QRect axisTrueAreaRect() const { return _axisTrueAreaRect; } + void setAxisLineVisible( bool axisLineVisible ); + bool axisLineVisible() const { return _axisLineVisible; } + void setAxisLineWidth( int axisLineWidth ); + int axisLineWidth() const { return _axisLineWidth; } + void setAxisTrueLineWidth( int axisTrueLineWidth ) { _axisTrueLineWidth = axisTrueLineWidth; } + int axisTrueLineWidth() const { return _axisTrueLineWidth; } + void setAxisLineColor( QColor axisLineColor ); + QColor axisLineColor() const { return _axisLineColor; } + // main grid: + void setAxisShowFractionalValuesDelimiters( bool axisShowFractValDelim ); + bool axisShowFractionalValuesDelimiters() const { return _axisShowFractionalValuesDelimiters; } + void setAxisShowGrid( bool axisShowGrid ); + bool axisShowGrid() const { return _axisShowGrid; } + void setAxisGridStyle( PenStyle axisGridStyle ); + PenStyle axisGridStyle() const { return _axisGridStyle; } + void setAxisGridColor( QColor axisGridColor ); + QColor axisGridColor() const { return _axisGridColor; } + void setAxisGridLineWidth( int axisGridLineWidth ); + int axisGridLineWidth() const { return _axisGridLineWidth; } + // sub grid: + void setAxisShowSubDelimiters( bool axisShowSubDelimiters ); + bool axisShowSubDelimiters() const { return _axisShowSubDelimiters; } + void setAxisGridSubStyle( PenStyle axisGridSubStyle ); + PenStyle axisGridSubStyle() const { return _axisGridSubStyle; } + void setAxisGridSubColor( QColor axisGridSubColor ); + QColor axisGridSubColor() const { return _axisGridSubColor; } + void setAxisGridSubLineWidth( int axisGridSubLineWidth ); + int axisGridSubLineWidth() const { return _axisGridSubLineWidth; } + + void setAxisZeroLineColor( QColor axisZeroLineColor ); + QColor axisZeroLineColor() const { return _axisZeroLineColor; } + + void setAxisLabelsVisible( bool axisLabelsVisible ); + bool axisLabelsVisible() const { return _axisLabelsVisible; } + void setAxisLabelsTouchEdges( bool axisLabelsTouchEdges ); + bool axisLabelsTouchEdges() const { return _axisLabelsTouchEdges; } + void setAxisLabelsFont( QFont axisLabelsFont, bool useFontSize ); + QFont axisLabelsFont() const { return _axisLabelsFont; } + void setAxisLabelsFontUseRelSize( bool axisLabelsFontUseRelSize ); + bool axisLabelsFontUseRelSize() const { return _axisLabelsFontUseRelSize; } + void setAxisLabelsFontRelSize( int axisLabelsFontRelSize ); + int axisLabelsFontRelSize() const { return _axisLabelsFontRelSize; } + void setAxisLabelsFontMinSize( int axisLabelsFontMinSize ) { _axisLabelsFontMinSize = axisLabelsFontMinSize; } + int axisLabelsFontMinSize() const { return _axisLabelsFontMinSize; } + void setAxisLabelsDontShrinkFont( bool labelsDontShrinkFont ) { _axisLabelsDontShrinkFont = labelsDontShrinkFont; } + bool axisLabelsDontShrinkFont() const { return _axisLabelsDontShrinkFont; } + void setAxisLabelsDontAutoRotate( bool labelsDontAutoRotate ) { _axisLabelsDontAutoRotate = labelsDontAutoRotate; } + bool axisLabelsDontAutoRotate() const { return _axisLabelsDontAutoRotate; } + void setAxisLabelsRotation( int rotation ) { _axisLabelsRotation = rotation; } + int axisLabelsRotation() const { return _axisLabelsRotation; } + void setAxisLabelsColor( QColor axisLabelsColor ); + QColor axisLabelsColor() const { return _axisLabelsColor; } + + void setAxisLabelsCalc( int divPow10 = 0, + int digitsBehindComma = KDCHART_AXIS_LABELS_AUTO_DIGITS ); + int axisLabelsDivPow10() const { return _axisLabelsDivPow10; } + int axisLabelsDigitsBehindComma() const { return _axisDigitsBehindComma; } + void setAxisLabelsRadix( const QString& decimalPoint, + const QString& thousandsPoint ); + QString axisLabelsDecimalPoint() const { return _axisLabelsDecimalPoint; } + QString axisLabelsThousandsPoint() const { return _axisLabelsThousandsPoint; } + + void setAxisLabelsNotation( KDChartEnums::NumberNotation notation ); + KDChartEnums::NumberNotation axisLabelsNotation() const { return _axisLabelsNotation; } + + void setAxisLabelsFormat( const QString& prefix = "", + const QString& postfix = "", + const int& totalLen = 0, + const QChar& padFill = ' ', + const bool& blockAlign = true ); + QString axisLabelsPrefix() const { return _axisLabelsPrefix; } + QString axisLabelsPostfix() const { return _axisLabelsPostfix; } + int axisLabelsTotalLen() const { return _axisLabelsTotalLen; } + QChar axisLabelsPadFill() const { return _axisLabelsPadFill; } + bool axisLabelsBlockAlign() const { return _axisLabelsBlockAlign; } + +public: + enum LabelsFromDataRow { LabelsFromDataRowYes, + LabelsFromDataRowNo, + LabelsFromDataRowGuess }; + +public slots: + static QString labelsFromDataRowToString( LabelsFromDataRow mode ); + static LabelsFromDataRow stringToLabelsFromDataRow( const QString& type ); + +public: + enum ValueScale { ValueScaleNumerical = 0, // have gaps here to allow specifying of additional scaling steps in between + ValueScaleSecond = 20, + ValueScaleMinute = 30, + ValueScaleHour = 40, + ValueScaleDay = 50, + ValueScaleWeek = 60, + ValueScaleMonth = 70, + ValueScaleQuarter = 80, + ValueScaleYear = 90 }; + +public slots: + void setAxisValues( bool axisSteadyValueCalc = true, + const QVariant& axisValueStart = KDCHART_AXIS_LABELS_AUTO_LIMIT, + const QVariant& axisValueEnd = KDCHART_AXIS_LABELS_AUTO_LIMIT, + double axisValueDelta = KDCHART_AXIS_LABELS_AUTO_DELTA, + int axisDigitsBehindComma = KDCHART_AXIS_LABELS_AUTO_DIGITS, + int axisMaxEmptyInnerSpan = KDCHART_DONT_CHANGE_EMPTY_INNER_SPAN_NOW, + LabelsFromDataRow takeLabelsFromDataRow = LabelsFromDataRowNo, + int labelTextsDataRow = 0, + QStringList* axisLabelStringList = 0, + QStringList* axisShortLabelsStringList = 0, + int axisValueLeaveOut = KDCHART_AXIS_LABELS_AUTO_LEAVEOUT, + ValueScale axisValueDeltaScale = ValueScaleNumerical ); + + + void setAxisValueStartIsExact( bool isExactValue=true ) { _axisValueStartIsExact = isExactValue; } + bool axisValueStartIsExact() const { return _axisValueStartIsExact; } + void setAxisValueStart( const QVariant& axisValueStart ) { _axisValueStart = axisValueStart; } + QVariant axisValueStart() const { return _axisValueStart; } + void setAxisValueEnd( const QVariant& axisValueEnd ) {_axisValueEnd = axisValueEnd; } + QVariant axisValueEnd() const { return _axisValueEnd; } + void setAxisValueDelta( const double axisValueDelta, + ValueScale scale = ValueScaleNumerical ); + double axisValueDelta() const { return _axisValueDelta; } + ValueScale axisValueDeltaScale() const { return _axisValueDeltaScale; } + void setAxisValueLeaveOut( const int leaveOut ) { _axisValueLeaveOut = leaveOut; } + int axisValueLeaveOut() const { return _axisValueLeaveOut; } + void setAxisValuesDecreasing( bool valuesDecreasing = true ) + { + _axisValuesDecreasing = valuesDecreasing; + } + bool axisValuesDecreasing() const { return _axisValuesDecreasing; } + void setTrueAxisDeltaPixels( double nDeltaPixels ) { _trueAxisDeltaPixels = nDeltaPixels; } + double trueAxisDeltaPixels() const { return _trueAxisDeltaPixels; } + void setTrueAxisLowHighDelta( double nLow, double nHigh, double nDelta ); + double trueAxisLow() const { return _trueLow; } + double trueAxisHigh() const { return _trueHigh; } + double trueAxisDelta() const { return _trueDelta; } + void setTrueAxisDtLowHighDeltaScale( QDateTime dtLow, QDateTime dtHigh, + ValueScale dtDeltaScale ); + void setTrueAxisDtLow( QDateTime dtLow ) { _trueDtLow = dtLow; } + void setTrueAxisDtHigh( QDateTime dtHigh ) { _trueDtHigh = dtHigh; } + void setTrueAxisDtScale( ValueScale scale ) { _trueDtDeltaScale = scale; } + QDateTime trueAxisDtLow() const { return _trueDtLow; } + QDateTime trueAxisDtHigh() const { return _trueDtHigh; } + ValueScale trueAxisDtDeltaScale() const { return _trueDtDeltaScale; } + void setAxisZeroLineStart( double x, double y ); + double axisZeroLineStartX() const { return _axisZeroLineStartX; } + double axisZeroLineStartY() const { return _axisZeroLineStartY; } + void setAxisDtLowPos( double x, double y ); + double axisDtLowPosX() const { return _axisDtLowPosX; } + double axisDtLowPosY() const { return _axisDtLowPosY; } + void setAxisDtHighPos( double x, double y ); + double axisDtHighPosX() const { return _axisDtHighPosX; } + double axisDtHighPosY() const { return _axisDtHighPosY; } + void setAxisDigitsBehindComma( const int digits ) { _axisDigitsBehindComma = digits; } + int axisDigitsBehindComma() const { return _axisDigitsBehindComma; } + void setAxisLabelsDateTimeFormat( const QString& format ) { _axisLabelsDateTimeFormat = format; } + QString axisLabelsDateTimeFormat() const { return _axisLabelsDateTimeFormat; } + void setAxisMaxEmptyInnerSpan( const int maxEmpty ) { _axisMaxEmptyInnerSpan = maxEmpty; } + int axisMaxEmptyInnerSpan() const { return _axisMaxEmptyInnerSpan; } + void setLabelTextsFormDataRow( int row, LabelsFromDataRow mode ); + LabelsFromDataRow axisLabelTextsFormDataRow() const { return _takeLabelsFromDataRow; } + int labelTextsDataRow() const { return _labelTextsDataRow; } + void setAxisLabelStringLists( QStringList* axisLabelStringList, + QStringList* axisShortLabelStringList, + const QString& valueStart = QString::null, + const QString& valueEnd = QString::null ); + QStringList axisLabelStringList() const { return _axisLabelStringList; } + uint axisLabelStringCount() const { return _axisLabelStringList.count(); } + QStringList axisShortLabelsStringList() const { return _axisShortLabelsStringList; } + uint axisShortLabelsStringCount() const { return _axisShortLabelsStringList.count(); } + const QStringList* axisLabelTexts() const { return _axisLabelTextsDirty ? 0 : &_axisLabelTexts; } + void setAxisLabelTexts( const QStringList* axisLabelTexts ); + void setAxisLabelTextsDirty( bool axisLabelTextsDirty ) { _axisLabelTextsDirty = axisLabelTextsDirty; } + bool axisLabelTextsDirty() const { return _axisLabelTextsDirty; } + void setAxisFirstLabelText( const QString& axisFirstLabelText = QString() ); + void setAxisLastLabelText( const QString& axisLastLabelText = QString() ); + QString axisFirstLabelText() const { return _axisFirstLabelText; } + QString axisLastLabelText() const { return _axisLastLabelText; } + bool hasAxisFirstLabelText() const { return !_axisFirstLabelText.isNull(); } + bool hasAxisLastLabelText() const { return !_axisLastLabelText.isNull(); } + void setAxisSteadyValueCalc( bool axisSteadyValueCalc ) { _axisSteadyValueCalc = axisSteadyValueCalc; } + bool axisSteadyValueCalc() const { return _axisSteadyValueCalc; } + +public: + KDChartAxisParams(); + virtual ~KDChartAxisParams(); + KDChartAxisParams& operator=( const KDChartAxisParams& R ); + static void deepCopy( KDChartAxisParams& D, const KDChartAxisParams& R ); + friend QTextStream& operator<<( QTextStream& s, const KDChartParams& p ); + friend QTextStream& operator>>( QTextStream& s, KDChartParams& p ); + friend class KDChartParams; + +signals: + + // emitted when any of the chart axis parameters have changed. + void changed(); + +private: + + AxisType _axisType; + bool _axisVisible; + bool _axisLabelsTouchEdges; + AxisAreaMode _axisAreaMode; + int _axisUseAvailableSpaceFrom; + int _axisUseAvailableSpaceTo; + int _axisAreaMin; + int _axisAreaMax; + AxisCalcMode _axisCalcMode; + uint _axisIsoRefAxis; + int _axisTrueAreaSize; + QRect _axisTrueAreaRect; + bool _axisLineVisible; + int _axisLineWidth; + int _axisTrueLineWidth; + QColor _axisLineColor; + + bool _axisShowFractionalValuesDelimiters; + bool _axisShowGrid; + QColor _axisGridColor; + int _axisGridLineWidth; + PenStyle _axisGridStyle; + + bool _axisShowSubDelimiters; + QColor _axisGridSubColor; + int _axisGridSubLineWidth; + PenStyle _axisGridSubStyle; + + QColor _axisZeroLineColor; + bool _axisLabelsVisible; + QFont _axisLabelsFont; + bool _axisLabelsFontUseRelSize; + int _axisLabelsFontRelSize; + int _axisLabelsFontMinSize; + bool _axisLabelsDontShrinkFont; + bool _axisLabelsDontAutoRotate; + int _axisLabelsRotation; + QColor _axisLabelsColor; + + bool _axisSteadyValueCalc; + bool _axisValueStartIsExact; + QVariant _axisValueStart; + QVariant _axisValueEnd; + double _axisValueDelta; + ValueScale _axisValueDeltaScale; + int _axisValueLeaveOut; + bool _axisValuesDecreasing; + double _trueAxisDeltaPixels; + double _trueLow; + double _trueHigh; + double _trueDelta; + QDateTime _trueDtLow; + QDateTime _trueDtHigh; + ValueScale _trueDtDeltaScale; + double _axisZeroLineStartX; + double _axisZeroLineStartY; + double _axisDtLowPosX; + double _axisDtLowPosY; + double _axisDtHighPosX; + double _axisDtHighPosY; + int _axisDigitsBehindComma; + QString _axisLabelsDateTimeFormat; + int _axisMaxEmptyInnerSpan; + LabelsFromDataRow _takeLabelsFromDataRow; + int _labelTextsDataRow; + QStringList _axisLabelStringList; + QStringList _axisShortLabelsStringList; + QStringList _axisLabelTexts; + bool _axisLabelTextsDirty; + QString _axisFirstLabelText; + QString _axisLastLabelText; + + int _axisLabelsDivPow10; + QString _axisLabelsDecimalPoint; + QString _axisLabelsThousandsPoint; + KDChartEnums::NumberNotation _axisLabelsNotation; + QString _axisLabelsPrefix; + QString _axisLabelsPostfix; + int _axisLabelsTotalLen; + QChar _axisLabelsPadFill; + bool _axisLabelsBlockAlign; +}; + +#endif diff --git a/kchart/kdchart/KDChartAxisParamsWrapper.cpp b/kchart/kdchart/KDChartAxisParamsWrapper.cpp new file mode 100644 index 00000000..5250a713 --- /dev/null +++ b/kchart/kdchart/KDChartAxisParamsWrapper.cpp @@ -0,0 +1,73 @@ +#include "KDChartAxisParamsWrapper.h" +#include <KDChartAxisParams.h> + +KDChartAxisParamsWrapper::KDChartAxisParamsWrapper( KDChartAxisParams* data ) :QObject(0), _data(data) +{ +} + +void KDChartAxisParamsWrapper::setAxisValueStart( double axisValueStart ) +{ + _data->setAxisValueStart( axisValueStart ); +} + +void KDChartAxisParamsWrapper::setAxisValueEnd( double axisValueEnd ) +{ + _data->setAxisValueEnd( axisValueEnd); +} + +void KDChartAxisParamsWrapper::setAxisValueStart( const QVariant axisValueStart ) +{ + _data->setAxisValueStart( axisValueStart ); +} + +void KDChartAxisParamsWrapper::setAxisValueEnd( const QVariant axisValueEnd ) +{ + _data->setAxisValueEnd( axisValueEnd ); +} + +void KDChartAxisParamsWrapper::setAxisValues( bool axisSteadyValueCalc, + QVariant axisValueStart, + QVariant axisValueEnd, + double axisValueDelta, + int axisDigitsBehindComma, + int axisMaxEmptyInnerSpan, + int takeLabelsFromDataRow, + int labelTextsDataRow, + const QVariant& axisLabelStringList, + const QVariant& axisShortLabelsStringList, + int axisValueLeaveOut, + int axisValueDeltaScale ) +{ + QStringList l1 = axisLabelStringList.toStringList(); + QStringList l2 = axisShortLabelsStringList.toStringList(); + _data->setAxisValues( axisSteadyValueCalc, + axisValueStart, + axisValueEnd, + axisValueDelta, + axisDigitsBehindComma, + axisMaxEmptyInnerSpan, + (KDChartAxisParams::LabelsFromDataRow) takeLabelsFromDataRow, + labelTextsDataRow, + &l1, + &l2, + axisValueLeaveOut, + (KDChartAxisParams::ValueScale) axisValueDeltaScale ); +} + +void KDChartAxisParamsWrapper::setAxisLabelStringLists( const QVariant& axisLabelStringList, + const QVariant& axisShortLabelStringList, + const QString& valueStart, + const QString& valueEnd ) +{ + QStringList l1 = axisLabelStringList.toStringList(); + QStringList l2 = axisShortLabelStringList.toStringList(); + _data->setAxisLabelStringLists( &l1, &l2, valueStart, valueEnd ); +} + +void KDChartAxisParamsWrapper::setAxisLabelTexts( const QVariant& axisLabelTexts ) +{ + QStringList l = axisLabelTexts.toStringList(); + _data->setAxisLabelTexts( &l ); +} + +#include "KDChartAxisParamsWrapper.moc" diff --git a/kchart/kdchart/KDChartAxisParamsWrapper.h b/kchart/kdchart/KDChartAxisParamsWrapper.h new file mode 100644 index 00000000..de8dbcc3 --- /dev/null +++ b/kchart/kdchart/KDChartAxisParamsWrapper.h @@ -0,0 +1,50 @@ +#ifndef KDCHARTAXISPARAMSWRAPPER_H +#define KDCHARTAXISPARAMSWRAPPER_H +#include <qobject.h> +#include <KDChartEnums.h> +#include <KDChartAxisParams.h> +#include <qstringlist.h> +#include <qvariant.h> +class KDChartAxisParams; + +class KDChartAxisParamsWrapper :public QObject +{ + Q_OBJECT + +public: + KDChartAxisParamsWrapper( KDChartAxisParams* ); + +public slots: + // These overload are only for convenience. + void setAxisValueStart( double axisValueStart ); + void setAxisValueEnd( double axisValueEnd ); + void setAxisValueStart( const QVariant axisValueStart ); + void setAxisValueEnd( const QVariant axisValueEnd ); + +public slots: + void setAxisValues( bool axisSteadyValueCalc = true, + QVariant axisValueStart = KDCHART_AXIS_LABELS_AUTO_LIMIT, + QVariant axisValueEnd = KDCHART_AXIS_LABELS_AUTO_LIMIT, + double axisValueDelta = KDCHART_AXIS_LABELS_AUTO_DELTA, + int axisDigitsBehindComma = KDCHART_AXIS_LABELS_AUTO_DIGITS, + int axisMaxEmptyInnerSpan = KDCHART_DONT_CHANGE_EMPTY_INNER_SPAN_NOW, + int takeLabelsFromDataRow = KDChartAxisParams::LabelsFromDataRowNo, + int labelTextsDataRow = 0, + const QVariant& axisLabelStringList = QVariant( QStringList() ), + const QVariant& axisShortLabelsStringList = QVariant( QStringList() ), + int axisValueLeaveOut = KDCHART_AXIS_LABELS_AUTO_LEAVEOUT, + int axisValueDeltaScale = KDChartAxisParams::ValueScaleNumerical ); + + void setAxisLabelStringLists( const QVariant& axisLabelStringList, + const QVariant& axisShortLabelStringList, + const QString& valueStart = QString::null, + const QString& valueEnd = QString::null ); + + void setAxisLabelTexts( const QVariant& axisLabelTexts ); +private: + KDChartAxisParams* _data; +}; + + +#endif /* KDCHARTAXISPARAMSWRAPPER_H */ + diff --git a/kchart/kdchart/KDChartBWPainter.cpp b/kchart/kdchart/KDChartBWPainter.cpp new file mode 100644 index 00000000..96bd2bc7 --- /dev/null +++ b/kchart/kdchart/KDChartBWPainter.cpp @@ -0,0 +1,483 @@ +/* -*- 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. + ** + **********************************************************************/ +#include "KDChartBWPainter.h" +#include <KDChartParams.h> +#include "KDChartTextPiece.h" + +#include <qpainter.h> +#if COMPAT_QT_VERSION >= 0x030000 +#include <qmemarray.h> +#else +#include <qarray.h> +#define QMemArray QArray +#endif + +#include <stdlib.h> + +/** + \class KDChartBWPainter KDChartBWPainter.h + + \brief A chart painter implementation that can paint Box&Whisker charts. + */ + +/** + Constructor. Sets up internal data structures as necessary. + + \param params the KDChartParams structure that defines the chart + */ + KDChartBWPainter::KDChartBWPainter( 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. + */ +KDChartBWPainter::~KDChartBWPainter() +{ + // intentionally left blank +} + + +void quicksort( QMemArray<double>& a, int lo, int hi ) +{ + int i=lo, j=hi; + double h; + double x=a[(lo+hi)/2]; + do + { + while (a[i]<x) i++; + while (a[j]>x) j--; + if (i<=j) + { + h=a[i]; a[i]=a[j]; a[j]=h; + i++; j--; + } + } while (i<=j); + if (lo<j) quicksort(a, lo, j); + if (i<hi) quicksort(a, i, hi); +} + + +// The following function returns the number of used cells containing a double. +int KDChartBWPainter::calculateStats( KDChartTableDataBase& data, + uint dataset ) +{ + const uint nMax = data.usedCols(); + int nUsed = 0; + QMemArray<double> values( nMax ); + double sum = 0.0; + QVariant vVal; + if( data.sorted() ){ + for( uint i=0; i<nMax; ++i){ + if( data.cellCoord( dataset, i, vVal, 1 ) && + QVariant::Double == vVal.type() ) { + values[nUsed] = vVal.toDouble(); + sum += values[nUsed]; + ++nUsed; + } + } + }else{ + // make copy of the dataset and look if it is sorted + bool sorted = true; + double last = 0.0; // <-- avoids an annoying compile-time warning + for( uint i=0; i<nMax; ++i){ + if( data.cellCoord( dataset, i, vVal, 1 ) && + QVariant::Double == vVal.type() ) { + values[nUsed] = vVal.toDouble(); + if( nUsed // skip 1st value + && last > values[nUsed] ) + sorted = false; + last = values[nUsed]; + sum += last; + ++nUsed; + } + } + if( !sorted ){ + // sort our copy of the dataset + quicksort( values, 0, nUsed-1 ); + } + } + + // Values now contains all used values without empty gaps. + // nUsed contains their number, so values[nUsed-1] is the last value. + + if( nUsed ){ + // store some values + stats[ KDChartParams::MaxValue ] = values[nUsed-1]; + stats[ KDChartParams::MinValue ] = values[0]; + + stats[ KDChartParams::MeanValue ] = sum / nUsed; + // calculate statistics + bool bOdd = 1 == nUsed % 2; + // find median + int nUd2 = nUsed/2; + if( bOdd ) + stats[ KDChartParams::Median ] = values[ nUd2 ]; + else + stats[ KDChartParams::Median ] = + (values[ QMAX(nUd2-1, 0) ] + values[ nUd2 ]) /2; + // find last value of lower quartile + nLastQ1 = QMAX( nUd2-1, 0 ); + // find 1st value of lower quartile + nFirstQ1 = nLastQ1 / 2; + + // determine how many values are below the median ( == how many are above it) + int nLowerCount = nLastQ1 - nFirstQ1 + 1; + + // find 1st value of upper quartile + nFirstQ3 = bOdd ? QMIN( nUd2+1, nUsed-1 ) : nUd2; + // find last value of upper quartile + nLastQ3 = nFirstQ3 + nLowerCount - 1; + + // find quartiles + bool bOdd2 = 1 == nLowerCount % 2; + // find lower quartile + if( bOdd2 ) + stats[ KDChartParams::Quartile1 ] = values[ nFirstQ1 ]; + else + stats[ KDChartParams::Quartile1 ] = + (values[ QMAX(nFirstQ1-1, 0) ] + values[ nFirstQ1 ]) /2; + // find upper quartile + if( bOdd2 ){ + stats[ KDChartParams::Quartile3 ] = values[ nLastQ3 ]; +} + else{ + //qDebug(" "+QString::number(nLastQ3)+" "+QString::number(KDChartParams::Quartile3) + // +" "+QString::number(nUsed)+" "+QString::number(QMIN(nLastQ3+1, nUsed-1))); + stats[ KDChartParams::Quartile3 ] = + (values[ nLastQ3 ] + values[ QMIN(nLastQ3+1, nUsed-1) ]) /2; +} + // find the interquartile range (IQR) + double iqr = stats[ KDChartParams::Quartile3 ] - stats[ KDChartParams::Quartile1 ]; + + // calculate the fences + double upperInner, lowerInner, upperOuter, lowerOuter; + params()->bWChartFences( upperInner, lowerInner, + upperOuter, lowerOuter ); + stats[ KDChartParams::UpperInnerFence ] = + stats[ KDChartParams::Quartile3 ] + iqr * upperInner; + stats[ KDChartParams::LowerInnerFence ] = + stats[ KDChartParams::Quartile1 ] - iqr * lowerInner; + stats[ KDChartParams::UpperOuterFence ] = + stats[ KDChartParams::Quartile3 ] + iqr * upperOuter; + stats[ KDChartParams::LowerOuterFence ] = + stats[ KDChartParams::Quartile1 ] - iqr * lowerOuter; + } + return nUsed; +} + + +/** + This method is a specialization that returns a fallback legend text + appropriate for BW 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 KDChartBWPainter::fallbackLegendText( uint dataset ) const +{ + return QObject::tr( "Series " ) + 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 KDChartBWPainter::numLegendFallbackTexts( KDChartTableDataBase* data ) const +{ + return data->usedRows(); +} + + +bool KDChartBWPainter::isNormalMode() const +{ + return KDChartParams::BWNormal == params()->bWChartSubType(); +} + +int KDChartBWPainter::clipShiftUp( bool, double ) const +{ + return 0; +} + +/** + Paints the actual data area and registers the region for the data + points if \a regions is not 0. + + \param painter the QPainter onto which the chart should be painted + \param data the data that will be displayed as a chart + \param paint2nd specifies whether the main chart or the additional chart is to be drawn now + \param regions a pointer to a list of regions that will be filled + with regions representing the data segments, if not null + */ +void KDChartBWPainter::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; + + uint datasetNum = ( chartDatasetEnd - chartDatasetStart ) + 1; + + 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 lineWidth = static_cast<int>( pointDist / 66.0 ) * QMAX(params()->lineWidth(), 1); + const int lineWidthD2 = lineWidth * 2 / 3; + + const bool noBrush = Qt::NoBrush == params()->bWChartBrush().style(); + + // Loop over the datasets, draw one box and whisker shape for each series. + for ( uint dataset = chartDatasetStart; + dataset <= chartDatasetEnd; + ++dataset ) { + + if( dataset >= datasetStart && + dataset <= datasetEnd && + 0 < calculateStats( *data, dataset ) ) { + const QColor color( params()->dataColor( dataset ) ); + // transform calculated values + double drawUOF = stats[ KDChartParams::UpperOuterFence ] * pixelsPerUnit; + double drawUIF = stats[ KDChartParams::UpperInnerFence ] * pixelsPerUnit; + double drawQu3 = stats[ KDChartParams::Quartile3 ] * pixelsPerUnit; + double drawMed = stats[ KDChartParams::Median ] * pixelsPerUnit; + double drawQu1 = stats[ KDChartParams::Quartile1 ] * pixelsPerUnit; + double drawLIF = stats[ KDChartParams::LowerInnerFence ] * pixelsPerUnit; + double drawLOF = stats[ KDChartParams::LowerOuterFence ] * pixelsPerUnit; + double drawMax = stats[ KDChartParams::MaxValue ] * pixelsPerUnit; + double drawMin = stats[ KDChartParams::MinValue ] * pixelsPerUnit; + double drawMean= stats[ KDChartParams::MeanValue ] * pixelsPerUnit; + // get whisker values + double drawUWhisker = QMIN(drawUIF, drawMax); + double drawLWhisker = QMAX(drawLIF, drawMin); + // get the box width + const int boxWidth = QMAX( 6, static_cast<int>( pointDist * 0.2 ) ); + // get marker size (for the outliers and/or for the median value) + int markWidth = params()->bWChartOutValMarkerSize(); + bool drawOutliers = ( 0 != markWidth ); + if( drawOutliers ){ + if( 0 > markWidth) + markWidth = QMAX( 4, markWidth * boxWidth / -100 ); + else + markWidth = QMAX( 4, markWidth ); + } + else + markWidth = boxWidth * 25 / 100; // use the default for the Median marker + + painter->setPen( QPen( color, lineWidth ) ); + painter->setBrush( params()->bWChartBrush() ); + // draw the box + int boxWidthD2 = boxWidth / 2; + int xPos = static_cast<int>( + pointDist * ( (double)(dataset - chartDatasetStart) + 0.5 ) + - lineWidth / 2); + painter->drawRect( xPos - boxWidthD2, + static_cast<int>( zeroXAxisI - drawQu3 ), + boxWidth, + static_cast<int>( drawQu3 - drawQu1) + 1 ); + // draw the median + painter->drawLine( xPos - boxWidthD2, + static_cast<int>( zeroXAxisI - drawMed ), + xPos - boxWidthD2 + boxWidth, + static_cast<int>( zeroXAxisI - drawMed ) ); + // draw the whisker + painter->drawLine( xPos - boxWidthD2, + static_cast<int>( zeroXAxisI - drawUWhisker ), + xPos - boxWidthD2 + boxWidth, + static_cast<int>( zeroXAxisI - drawUWhisker ) ); + painter->drawLine( xPos, + static_cast<int>( zeroXAxisI - drawUWhisker ), + xPos, + static_cast<int>( zeroXAxisI - drawQu3 ) ); + painter->drawLine( xPos - boxWidthD2, + static_cast<int>( zeroXAxisI - drawLWhisker ), + xPos - boxWidthD2 + boxWidth, + static_cast<int>( zeroXAxisI - drawLWhisker ) ); + painter->drawLine( xPos, + static_cast<int>( zeroXAxisI - drawLWhisker ), + xPos, + static_cast<int>( zeroXAxisI - drawQu1 ) ); + // draw the values + int xPos2 = static_cast<int>( + pointDist * ( (double)(dataset - chartDatasetStart) + 0.5 ) + - lineWidthD2 / 2); + int markWidthD2 = QMAX(markWidth / 2, 2); + int markWidthD25 = QMAX(static_cast<int>( 0.85 * markWidth / 2.0), 2); + int markWidthD35 = QMAX(static_cast<int>( 0.85 * markWidth / 3.0), 2); + // draw the outliers + if( drawOutliers ){ + const uint nMax = data->usedCols(); + int drawVal; + QVariant vVal; + for( uint i=0; i<nMax; ++i) + if( data->cellCoord( dataset, i, vVal, 1 ) && + QVariant::Double == vVal.type() ) { + drawVal = static_cast<int>( pixelsPerUnit * vVal.toDouble() ); + if( drawLOF > drawVal || drawUOF < drawVal ) { + painter->setPen( Qt::NoPen ); + painter->drawChord( xPos2 - markWidthD2, + static_cast<int>(zeroXAxisI - drawVal) - markWidthD2, + markWidth, + markWidth, + 0, 5760 ); + painter->setPen( QPen( color, lineWidthD2 ) ); + painter->drawArc( xPos2 - markWidthD2, + static_cast<int>(zeroXAxisI - drawVal) - markWidthD2, + markWidth, + markWidth, + 0, 5760 ); + } else if( drawLIF > drawVal || drawUIF < drawVal ) { + painter->setPen( Qt::NoPen ); + painter->drawChord( xPos2 - markWidthD2, + static_cast<int>( zeroXAxisI - drawVal) - markWidthD2, + markWidth, + markWidth, + 0, 5760 ); + painter->setPen( QPen( color, lineWidthD2 ) ); + painter->drawLine( xPos2, + static_cast<int>(zeroXAxisI - drawVal) - markWidthD2, + xPos2, + static_cast<int>(zeroXAxisI - drawVal) + markWidthD2 ); + painter->drawLine( xPos2 - markWidthD25, + static_cast<int>(zeroXAxisI - drawVal) - markWidthD35, + xPos2 + markWidthD25, + static_cast<int>(zeroXAxisI - drawVal) + markWidthD35 ); + painter->drawLine( xPos2 + markWidthD25, + static_cast<int>(zeroXAxisI - drawVal) - markWidthD35, + xPos2 - markWidthD25, + static_cast<int>(zeroXAxisI - drawVal) + markWidthD35 ); + } + } + } + // draw the mean value + bool evenLineWidthD2 = floor( ((double)lineWidthD2)/2.0 ) == ((double)lineWidthD2)/2.0; + painter->setPen( params()->bWChartBrush().color() ); + painter->drawChord( xPos2 - markWidthD2-1 - (evenLineWidthD2 ? 0 : 1), + static_cast<int>( zeroXAxisI - drawMean ) - markWidthD2 - 1, + markWidthD2*2 + (evenLineWidthD2 ? 2 : 3), + markWidthD2*2 + (evenLineWidthD2 ? 2 : 3), + 0, 5760 ); + if( noBrush ) { + // use different color brightness for the Mean marker + int h,s,v; + color.hsv(&h,&s,&v); + painter->setPen( QPen( 128 > v ? color.light(150) : color.dark(150), + lineWidthD2 ) ); + } else + painter->setPen( QPen( color, lineWidthD2 ) ); + painter->drawLine( xPos2 - markWidthD2 - (evenLineWidthD2 ? 0 : 1), + static_cast<int>( zeroXAxisI - drawMean ), + xPos2 + markWidthD2, + static_cast<int>( zeroXAxisI - drawMean ) ); + painter->drawLine( xPos2 - (evenLineWidthD2 ? 0 : 1), + static_cast<int>( zeroXAxisI - drawMean ) - markWidthD2, + xPos2 - (evenLineWidthD2 ? 0 : 1), + static_cast<int>( zeroXAxisI - drawMean ) + markWidthD2 + (evenLineWidthD2 ? 0 : 1) ); + + // draw the statistical value texts + painter->setPen( Qt::NoPen ); + for( int ii = KDChartParams::BWStatValSTART; + ii <= KDChartParams::BWStatValEND; + ++ii ){ + KDChartParams::BWStatVal i = (KDChartParams::BWStatVal)ii; + if( params()->bWChartPrintStatistics( i ) ) { + QFont statFont( params()->bWChartStatisticsFont( i ) ); + float nTxtHeight = statFont.pointSizeFloat(); + if ( params()->bWChartStatisticsUseRelSize( i ) ) { + nTxtHeight = params()->bWChartStatisticsFontRelSize( i ) + * boxWidth / 100; + statFont.setPointSizeFloat( nTxtHeight ); + } + double drawStat = pixelsPerUnit * stats[i]; + KDChartTextPiece statText( painter, QString::number( stats[i] ), + statFont ); + int tw = statText.width(); + int xDelta = ( KDChartParams::MaxValue == i + || KDChartParams::MeanValue == i + || KDChartParams::MinValue == i ) + ? -1 * (tw + static_cast<int>( 1.3*boxWidthD2 )) + : static_cast<int>( 1.3*boxWidthD2 ); + QBrush brush( params()->bWChartStatisticsBrush( i ) ); + painter->setBrush( brush ); + int y = static_cast<int>( zeroXAxisI - drawStat - nTxtHeight/2); + painter->drawRect( xPos + xDelta - 1, + y, + tw + 2, + QMAX(static_cast < int > ( nTxtHeight ), 8) + 1 ); + statText.draw( painter, + xPos + xDelta, + y, + ourClipRect, + params()->bWChartStatisticsColor( i ), + 0 ); + } + } + + } else + continue; // we cannot display this value + } +} diff --git a/kchart/kdchart/KDChartBWPainter.h b/kchart/kdchart/KDChartBWPainter.h new file mode 100644 index 00000000..3dd9ad77 --- /dev/null +++ b/kchart/kdchart/KDChartBWPainter.h @@ -0,0 +1,76 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTBWPAINTER_H__ +#define __KDCHARTBWPAINTER_H__ + +#include "KDChartAxesPainter.h" +#include <KDChartParams.h> +#include <KDChartTable.h> + +#include <qnamespace.h> + +class KDChartParams; + +class KDChartBWPainter : public KDChartAxesPainter, public Qt +{ + friend class KDChartPainter; + protected: + KDChartBWPainter( KDChartParams* params ); + virtual ~KDChartBWPainter(); + + virtual bool isNormalMode() const; + virtual int clipShiftUp( bool, double ) const; + virtual void 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 ); + + virtual QString fallbackLegendText( uint dataset ) const; + virtual uint numLegendFallbackTexts( KDChartTableDataBase* data ) const; + private: + int calculateStats( KDChartTableDataBase&, uint );// returns number of used cells containing a double + double stats[ KDChartParams::BWStatValOffEndValue]; // y cordinates + int nFirstQ1, nLastQ1, nFirstQ3, nLastQ3; +}; + +#endif diff --git a/kchart/kdchart/KDChartBarPainter.cpp b/kchart/kdchart/KDChartBarPainter.cpp new file mode 100644 index 00000000..b41fa34e --- /dev/null +++ b/kchart/kdchart/KDChartBarPainter.cpp @@ -0,0 +1,1707 @@ +/* -*- 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. + ** + **********************************************************************/ +#include "KDChartBarPainter.h" +#include <KDChartParams.h> + +#include <qpainter.h> + +#include <stdlib.h> + +/** + \class KDChartBarPainter KDChartBarPainter.h + + \brief A chart painter implementation that can paint bar charts. +*/ + +/** + Constructor. Sets up internal data structures as necessary. + + \param params the KDChartParams structure that defines the chart +*/ +KDChartBarPainter::KDChartBarPainter( 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. +*/ +KDChartBarPainter::~KDChartBarPainter() +{ + // intentionally left blank +} + +bool KDChartBarPainter::isNormalMode() const +{ + + return KDChartParams::BarNormal == params()->barChartSubType(); +} + +int KDChartBarPainter::clipShiftUp( bool normalMode, double areaWidthP1000 ) const +{ + + const bool bThreeDBars = params()->threeDBars() || (KDChartParams::BarMultiRows == params()->barChartSubType()); + return ( normalMode && !bThreeDBars ) + ? static_cast < int > ( areaWidthP1000 * 16.0 ) + : 0; +} + + +void KDChartBarPainter::initMyPainter( QPainter* painter ) +{ + _myPainter = painter; + _painterDX = 0.0; + _painterDY = 0.0; +} + +void KDChartBarPainter::shiftMyPainter( double dx, double dy ) +{ + if( dx != 0.0 || dy != 0.0 ){ + _myPainter->translate(dx, dy); + _painterDX += dx; + _painterDY += dy; + } +} + +void KDChartBarPainter::shiftMyPainterBack() +{ + if( _painterDX != 0.0 || _painterDY != 0.0 ){ + _myPainter->translate(-_painterDX, -_painterDY); + _painterDX = 0.0; + _painterDY = 0.0; + } +} + + +void KDChartBarPainter::calculateXFront1_2( bool bNormalMode, bool bIsVeryFirstBar, bool bIsFirstDataset, bool _bThreeDBars, + double xpos, double valueBlockGap, double datasetGap, double frontBarWidth, + int& frontX1, int& frontX2, int& prevFrontX2 ){ + + if( _bThreeDBars || bIsVeryFirstBar || !bNormalMode ) + frontX1 = static_cast<int>( xpos ); + else if( bIsFirstDataset ) + frontX1 = prevFrontX2 + static_cast <int> ( valueBlockGap ) + 1; + else + frontX1 = prevFrontX2 + static_cast < int > ( datasetGap ) + 1; + + // Note: frontX2 is calculated by using xpos but NOT by using frontX1. + // So we make sure that (a) the gaps between all bars of one dataset + // are exactly the same size, and that (b) the bars are automatically + // adjusted in their width: to match their very position within the abscissa range. + // Adjusting will result in them being ONE pixel wider sometimes. + frontX2 = static_cast < int > ( xpos + frontBarWidth ); + prevFrontX2 = frontX2; +} + + +QPointArray rectToPointArray( const QRect& r ) +{ + QPointArray a(4); + a.putPoints( 0, 4, r.left(),r.top(), r.right(),r.top(), r.right(),r.bottom(), r.left(),r.bottom() ); + return a; +} + + +void KDChartBarPainter::specificPaintData( QPainter* painter, + const QRect& ourClipRect, + KDChartTableDataBase* data, + KDChartDataRegionList* regions, + const KDChartAxisParams* ordinatePara, + 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 ) +{ + /****************Pending Michel*********************** + *Rectangles are painted in the following order: * + *front , right side, top. * + *Store the painted rectangles in order to paint * + *them in relation to each other for more precision. * + ****************************************************/ + QRect frontRectPositive,frontRectNegative, + rightRectPositive, rightRectNegative, + excessRectPositive,excessRectNegative; + + _areaP1000 = areaWidthP1000; + + + + if( !data ) return; + // qDebug("datasetStart: %i datasetEnd: %i", datasetStart, datasetEnd); + const QPen defaultOutlinePen( params()->outlineDataColor(), + params()->outlineDataLineWidth(), + params()->outlineDataLineStyle() ); + abscissaInfos ai; + ai.bCenterThePoints = true; + calculateAbscissaInfos( *params(), *data, + datasetStart, datasetEnd, + logWidth, _dataRect, + ai ); + + const QRect devRect( painter->window() ); + + initMyPainter( painter ); + + const bool ordinateIsLogarithmic + = KDChartAxisParams::AxisCalcLogarithmic == ordinatePara->axisCalcMode(); + + const bool bMultiRows = KDChartParams::BarMultiRows == params()->barChartSubType(); + _bThreeDBars = params()->threeDBars() || bMultiRows; + int numChartDataEntryDatasets = 0; + uint myLastDataEntryDataset = 0; + for( uint iD = chartDatasetStart; iD <= chartDatasetEnd; ++iD ){ + if( params()->chartSourceMode( iD ) == KDChartParams::DataEntry ){ + ++numChartDataEntryDatasets; + myLastDataEntryDataset = iD; + } + } + + const bool bHadClipping = painter->hasClipping(); + + //this allow the height/size of the bar to be painted fully + if( bMultiRows ) { + painter->setClipping( false ); + } + // Number of columns in one dataset: 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(); + + double datasetGap = bMultiRows + ? 0.0 + : params()->datasetGap() + * ( params()->datasetGapIsRelative() + ? areaWidthP1000 + : 1.0 ); + double valueBlockGap = bMultiRows + ? 0.0 + : params()->valueBlockGap() + * ( params()->valueBlockGapIsRelative() + ? areaWidthP1000 + : 1.0 ); + + // This is the same for all three bar types except for multi-bar Surface charts. + double spaceBetweenValueBlocks = bMultiRows + ? 0.0 + : static_cast<int>( valueBlockGap ) * numValues; + + // Set some geometry values that apply to bar charts only + double totalNumberOfBars = 0.0; + double spaceBetweenDatasets = 0.0; + switch ( params()->barChartSubType() ) { + case KDChartParams::BarNormal: { + totalNumberOfBars = numChartDataEntryDatasets * numValues; + spaceBetweenDatasets = datasetGap + * ( totalNumberOfBars - numValues ); + break; + } + case KDChartParams::BarStacked: + case KDChartParams::BarPercent: + case KDChartParams::BarMultiRows: + totalNumberOfBars = numValues; + spaceBetweenDatasets = 0; // always 0 when stacked/percent/multi-rows + break; + default: + qFatal( "Unsupported bar chart type" ); + }; + + double barWidth = 0.0; + if( params()->barWidth() == KDCHART_AUTO_SIZE ) { + + //Default auto calc. to optimize when many bars datasetGap == blockGap/2 == barWidth/2 + //unless valueBlockGap or datasetGap are different from default values == users value + barWidth = logWidth / totalNumberOfBars; + do { + barWidth -= 0.1; + params()->valueBlockGap() != 24 ? spaceBetweenValueBlocks = params()->valueBlockGap() * numValues + : spaceBetweenValueBlocks = ( barWidth/2 ) * numValues; + if ( spaceBetweenDatasets != 0 ) + params()->datasetGap()!= 6 ? spaceBetweenDatasets = params()->datasetGap() * ( totalNumberOfBars - numValues ) + : spaceBetweenDatasets = ( barWidth/4 ) * ( totalNumberOfBars - numValues ); + } + while ( barWidth*totalNumberOfBars + spaceBetweenValueBlocks + spaceBetweenDatasets > logWidth ); + + valueBlockGap = ( spaceBetweenValueBlocks )/ numValues; + if ( spaceBetweenDatasets != 0 ) + datasetGap = spaceBetweenDatasets / ( totalNumberOfBars - numValues ); + + barWidth = (logWidth - ( valueBlockGap * numValues ) - ( datasetGap * ( totalNumberOfBars - numValues ) ) ) / totalNumberOfBars; + ; + + /* Debug space available + qDebug ( " logWidth %f" , logWidth ); + qDebug ( "compare value %f", barWidth*totalNumberOfBars + spaceBetweenValueBlocks + datasetGap * ( totalNumberOfBars - numValues ) ); + */ + } else if( 0 > params()->barWidth() ) + barWidth = params()->barWidth() * -areaWidthP1000; + else + barWidth = params()->barWidth(); + + //sideBarWidth: pre-calculate in order to have a reference + //we will correct according to the available space later. + //This in order to center the labels in relation to the front width + //when painting 3DBars + double sideBarWidth = _bThreeDBars + ? ( barWidth - barWidth / (1.0 + params()->cosThreeDBarAngle()) ) * + params()->threeDBarDepth() + : 0.0; + + const double frontBarWidth = _bThreeDBars && !bMultiRows + ? barWidth - sideBarWidth + : barWidth; + + //correct the width for the side of the bar + double totalThreeDBarWidth = totalNumberOfBars*barWidth + sideBarWidth; + double totalSpaceOccupied = totalThreeDBarWidth + spaceBetweenDatasets + spaceBetweenValueBlocks; + if ( logWidth < totalSpaceOccupied) { + sideBarWidth -= (totalSpaceOccupied /*+ spaceBetweenDatasets + spaceBetweenValueBlocks*/ - logWidth)/totalNumberOfBars; + + //qDebug ( "logWidth %f", logWidth ); + //qDebug ( "totalSpaceOccupied %f", totalSpaceOccupied ); + } + + const double sideBarHeight = sideBarWidth; + + + double pixelsPerUnit = 0.0; + + if ( params()->barChartSubType() != KDChartParams::BarPercent ) // not used for percent + pixelsPerUnit = logHeight / ( columnValueDistance ? columnValueDistance : 10 ); + + //qDebug("\nordinatePara->trueAxisLow() = %f", ordinatePara->trueAxisLow()); + //qDebug( "ordinatePara->trueAxisHigh() = %f", ordinatePara->trueAxisHigh()); + //qDebug( "sideBarHeight = %f", sideBarHeight); + //qDebug( "pixelsPerUnit = %f", pixelsPerUnit); + + double zeroXAxisI; + if ( params()->barChartSubType() == KDChartParams::BarPercent ) { + if ( minColumnValue == 0.0 ) + zeroXAxisI = 0.0; + else if ( maxColumnValue == 0.0 ) + zeroXAxisI = logHeight - sideBarHeight; + else + zeroXAxisI = ( logHeight - sideBarHeight ) / 2.0; + + } else { + zeroXAxisI = logHeight + - ordinatePara->axisZeroLineStartY() + + _dataRect.y(); + } + + double shiftUpperBars = (params()->barChartSubType() != KDChartParams::BarPercent) + && (ordinatePara->axisTrueLineWidth() % 2) + ? 1.0 + : 0.0; + + // Initializing drawing positions + double yposPositivesStart = logHeight; + double yposNegativesStart = logHeight; + if( params()->barChartSubType() == KDChartParams::BarPercent ){ + yposPositivesStart += axisYOffset; + yposNegativesStart += axisYOffset; + } + + for ( int iPaintExtraLinesOrTheData = 0; + iPaintExtraLinesOrTheData < 3; + ++iPaintExtraLinesOrTheData ) + { + + const bool bDrawExtraLines = (1 != iPaintExtraLinesOrTheData); + const bool bDrawExtraLinesInFront = (2 == iPaintExtraLinesOrTheData); + + double xpos = 0.0; + + if ( _bThreeDBars && !bMultiRows ) + xpos = 0.0 + (barWidth/2) + (valueBlockGap/2) - (frontBarWidth/2); + else + xpos = 0.0 + (valueBlockGap / 2.0); + + double yposPositives = yposPositivesStart; + double yposNegatives = yposNegativesStart; + + /* Pending Michel: no need for this anymore */ + //double totalThreeDBarWidth = bMultiRows + //? barWidth + sideBarWidth + // : barWidth; + + double nShiftX = bMultiRows + ? sideBarWidth + : 0.0; + double nShiftY = bMultiRows + ? sideBarHeight + : 0.0; + + double valueTotal = 0.0; // valueTotal is used for percent bars only + + // iterate over all columns: item1, item2, item3 ... + int prevFrontX2 = 0; + bool bIsVeryFirstBar = true; + for ( int value = 0; value < numValues; ++value ) { + + bool bFirstValidValueUnknown = true; + uint firstValidValue = 0; + uint lastValidPositiveValue = 0; + double maxValueInThisColumn = 0.0, minValueInThisColumn = 0.0; + if ( params()->barChartSubType() == KDChartParams::BarStacked || + params()->barChartSubType() == KDChartParams::BarPercent) { + valueTotal = 0.0; + // iterate over datasets of this axis only: + for ( uint dataset = datasetStart; + dataset <= datasetEnd; + ++dataset ) { + + QVariant vVal; + if( data->cellCoord( dataset, value, vVal, 1 ) + && params()->chartSourceMode( dataset ) == KDChartParams::DataEntry + && QVariant::Double == vVal.type() ){ + + const double cellValue + = ordinateIsLogarithmic + ? log10( vVal.toDouble() ) + : vVal.toDouble(); + //qDebug("value %u dataset %u logHeight %f", value,dataset,logHeight); + + if( bFirstValidValueUnknown ){ + firstValidValue = dataset; + bFirstValidValueUnknown = false; + } + if( 0.0 <= cellValue ) + lastValidPositiveValue = dataset; + + maxValueInThisColumn = QMAX( maxValueInThisColumn, cellValue ); + minValueInThisColumn = QMIN( minValueInThisColumn, cellValue ); + if( params()->barChartSubType() == KDChartParams::BarPercent /*|| + params()->barChartSubType() == KDChartParams::BarStacked*/ ) + valueTotal += cellValue; + } + } + } + + //qDebug("shiftMyPainter( (numChartDataEntryDatasets-1)*nShiftX, (numChartDataEntryDatasets-1)*-nShiftY );"); + shiftMyPainter( (numChartDataEntryDatasets-1)*nShiftX, (numChartDataEntryDatasets-1)*-nShiftY ); + + // iterate over all datasets of this chart: + // (but draw only the bars of this axis) + bool bIsFirstDataset = true; + for ( uint dataset = bMultiRows + ? chartDatasetEnd + : chartDatasetStart; + dataset >= chartDatasetStart && dataset <= chartDatasetEnd; + bMultiRows + ? --dataset + : ++dataset ) { + //qDebug("value %u dataset %u logHeight %f", value,dataset,logHeight); + + const bool bDataEntrySourceMode + = (params()->chartSourceMode( dataset ) == KDChartParams::DataEntry); + + + QVariant coord1; + QVariant coord2; + int propID; + if( data->cellContent( dataset, value, coord1, coord2, propID ) + && QVariant::Double == coord1.type() ){ + + const double cellValue + = ordinateIsLogarithmic + ? log10( coord1.toDouble() ) + : coord1.toDouble(); + //qDebug("b"); + // there is a numeric value + + double barHeight; + + if ( params()->barChartSubType() == KDChartParams::BarPercent ) + barHeight = ( cellValue / valueTotal ) * fabs(zeroXAxisI - logHeight ); + else { + barHeight = pixelsPerUnit * cellValue; + + if( 0.0 <= barHeight ) + //barHeight = QMAX(0.0, barHeight - sideBarHeight); + barHeight = barHeight - sideBarHeight; + else + barHeight -= sideBarHeight; + } + + // This little adjustment avoids a crash when trying + // to retrieve the bounding rect of a zero size region... + if( 0 == barHeight || 0.0 == barHeight ) { + + barHeight = 1.0; // makes sense to have a min size anyway + } + + // draw only the bars belonging to the axis + // which we are processing currently + if( dataset >= datasetStart && dataset <= datasetEnd ) { + //qDebug("b2"); + + // calculate Abscissa axis value, if there are X coordinates + // --------------------------------------------------------- + bool skipMe = false; + if( ai.bCellsHaveSeveralCoordinates ){ + skipMe = !calculateAbscissaAxisValue( coord2, + ai, 0, xpos ); + // adjust bar position to have it horizontally centered to the point + if( ai.bAbscissaHasTrueAxisDtValues && + QVariant::DateTime == coord2.type() ) + xpos -= frontBarWidth / 2.0; + } + + if( !skipMe ){ + // Configure colors + QColor myBarColor( params()->dataColor( dataset ) ); + QColor myShadow1Color( params()->dataShadow1Color( dataset ) ); + QColor myShadow2Color( params()->dataShadow2Color( dataset ) ); + + // Get default values for extra lines and their markers + const KDChartParams::LineMarkerStyle + defaultMarkerStyle = params()->lineMarkerStyle( dataset ); + const QPen defaultPen( params()->lineColor().isValid() + ? params()->lineColor() + : params()->dataColor( dataset ), + params()->lineWidth(), + params()->lineStyle( dataset ) ); + + // -------------------------------------------------------- + // determine any 'extra' properties assigned to this cell + // by traversing the property set chain (if necessary) + // -------------------------------------------------------- + KDChartPropertySet propSet; + bool bCellPropertiesFound = + params()->calculateProperties( propID, + propSet ); + bool bShowThisBar = bDataEntrySourceMode; + if( bCellPropertiesFound ){ + if( bShowThisBar && !bDrawExtraLines ){ + int iDummy; + if( propSet.hasOwnShowBar( iDummy, bShowThisBar ) ){ + // NOP + } + if( propSet.hasOwnBarColor( iDummy, myBarColor ) ){ + // adjust the shadow colors + params()->calculateShadowColors( myBarColor, + myShadow1Color, + myShadow2Color ); + } + } + } + + if( !bDrawExtraLines || bCellPropertiesFound ){ + + //QRegion* region = 0; + KDChartDataRegion::PointArrayList * pointArrayList = 0; + if( bDataEntrySourceMode && !bDrawExtraLines ){ + painter->setPen( defaultOutlinePen ); + if ( myBarColor.isValid() ) + painter->setBrush( myBarColor ); + else + painter->setBrush( NoBrush ); + + // Prepare region for detection of mouse clicks + // and for finding anchor positions of data value texts + if ( regions ) + pointArrayList = new KDChartDataRegion::PointArrayList(); + } + + // Start drawing + int frontX1 = 0; + int frontX2 = 0; + bool tooLow = false; + bool tooHigh = false; + + if ( cellValue < 0 || cellValue < minColumnValue ) { + double maxValueYPos = maxColumnValue * pixelsPerUnit; + double yZero = yposNegatives - zeroXAxisI - sideBarHeight; + + tooLow = (barHeight - yZero + logHeight + axisYOffset) < 0; + + /*we need to handle those values in case we have a negative view*/ + if ( cellValue <= 0 && cellValue > maxColumnValue && minColumnValue < 0 && maxColumnValue < 0 ) + tooLow = true; + + if ( tooLow && ( bNormalMode || bMultiRows) ) { + double delta = 0.0125 * logHeight; + double height = -1*(-1.0 * (yZero + sideBarHeight) - 2 * delta); + double height1 = height - 3.0 * delta; + + int yArrowGap = static_cast < int > ( 2.5 * delta ); + calculateXFront1_2( bNormalMode, bIsVeryFirstBar, bIsFirstDataset, _bThreeDBars, + xpos, valueBlockGap, datasetGap, frontBarWidth, + frontX1, frontX2, prevFrontX2 ); + + const int xm = static_cast < int > ( (frontX1 + frontX2) / 2.0 ); + QRect rect( ourClipRect ); + + rect.setHeight( static_cast<int>( rect.height() + 3.0 * delta ) ); + painter->setClipRect( rect ); + + //Pending Michel: Make sure the point of the arrow is always at the same distance + //from the X axis reference to the point of the arrow. + int arrowXAxisGap; + QPoint arrowTop( xm,static_cast<int>( yZero + height1 + 2 * yArrowGap) ); + + if ( arrowTop.y()== yposNegatives ) + arrowXAxisGap = -2; + else + arrowXAxisGap = static_cast <int> (yposNegatives - arrowTop.y() - 2); + + if( bDrawExtraLines ){ + drawExtraLinesAndMarkers( + propSet, + defaultPen, + defaultMarkerStyle, + xm, static_cast<int>( yZero + height1 ), + painter, + ai.abscissaPara, + ordinatePara, + areaWidthP1000, + logHeight/1000.0, + bDrawExtraLinesInFront ); + }else if( bShowThisBar ){ + if( params()->drawSolidExcessArrows() ) { + + /* PENDING Michel: + * Here we have two situations. + * The value is too low because over the Min negative value + * or it is not within the configured view. + */ + // Draw solid excess arrows negatives + + QPointArray points( 5 ); + /*this works in a positive view -> 200 500*/ + points.setPoint( 0, frontX1, cellValue < 0 ? + static_cast<int>(yZero+height1 - 3.0*delta)+(2*yArrowGap)+ arrowXAxisGap: + static_cast<int>( yZero + height1)+(2*yArrowGap)+ arrowXAxisGap); + points.setPoint( 1, frontX2, cellValue < 0 ? + static_cast<int>(yZero+height1 - 3.0*delta)+(2*yArrowGap)+ arrowXAxisGap: + static_cast<int>( yZero + height1)+(2*yArrowGap)+ arrowXAxisGap ); + points.setPoint( 2, frontX2, cellValue < 0 ? + static_cast<int>(yZero+height1 - 3.0*delta)+(2*yArrowGap)+ arrowXAxisGap: + static_cast<int>( yZero + height1)+(2*yArrowGap)+ arrowXAxisGap); + points.setPoint( 3, xm, cellValue < 0 ? + static_cast<int>( yZero + height1)+(2*yArrowGap)+ arrowXAxisGap: + static_cast<int>(yZero+height1 - 3.0*delta)+(2*yArrowGap)+ arrowXAxisGap); + points.setPoint( 4, frontX1, cellValue < 0 ? + static_cast<int>(yZero+height1 - 3.0*delta)+(2*yArrowGap)+ arrowXAxisGap: + static_cast<int>( yZero + height1)+(2*yArrowGap)+ arrowXAxisGap); + + /* 0 between start and end -> -500 500*/ + if ( minColumnValue < 0 && maxColumnValue > 0 ) { + points.setPoint(0, points.point(0).x(), static_cast <int> (yposNegatives - zeroXAxisI) ); + points.setPoint(1, points.point(1).x(), static_cast <int> (yposNegatives - zeroXAxisI) ); + } + + /* negative view -> -200 -500 */ + if ( minColumnValue < 0 && maxColumnValue < 0 ) { + /*value negative or zero > maxColumnValue*/ + if ( cellValue > maxColumnValue ) { + // the view is under Yaxis 0 level + // we need to show a symbol for the bars which are over the Yaxis. + // calculate the coordinate and direction for the arrow. + // arrow downward for negative values and upward for positives value + int diffArrowBase = points.point(2).y() - points.point(3).y(); + double maxValueYPos = maxColumnValue * pixelsPerUnit; + double minValueYPos = minColumnValue * pixelsPerUnit; + double adjustedArrow = (cellValue == 0 ? minValueYPos - maxValueYPos + diffArrowBase + 2: + minValueYPos - maxValueYPos - diffArrowBase + 1); + points.setPoint( 0, frontX1, points.point(0).y() + static_cast <int> (adjustedArrow)); + points.setPoint( 1, frontX2, points.point(1).y() + static_cast <int> (adjustedArrow )); + points.setPoint( 2, frontX2, points.point(2).y() + static_cast <int> (adjustedArrow)); + points.setPoint( 3, xm, points.point(3).y() + static_cast <int> (adjustedArrow)); + points.setPoint( 4, frontX1, points.point(4).y() + static_cast <int> (adjustedArrow)); + } + /*value < mincolumn value*/ + if ( cellValue < minColumnValue ) { + points.setPoint( 0, frontX1, static_cast <int> ( yZero - maxValueYPos ) ); + points.setPoint( 1, frontX2, static_cast <int> ( yZero - maxValueYPos ) ); + } + + } + + /*Pending Michel: case setbarWidth */ + //adjust the painting in case we have a user given Width allow it + //to be larger than the auto calculated width in case we want to overlap + + if ( params()->userWidth() != 0 ) { + int userwidth = 0; + if ( params()->userWidth() < 0 ) + userwidth = static_cast <int> (params()->userWidth() * -areaWidthP1000); + else + userwidth = static_cast <int> (params()->userWidth() * areaWidthP1000); + //if ( userwidth < frontBarWidth ) { + QRect tmpRect ( points.point(0), points.point(2)); + points.setPoint(0, static_cast <int>(tmpRect.center().x() - userwidth/2), + points.point(0).y()); + points.setPoint(1, static_cast <int>(tmpRect.center().x() + userwidth/2), + points.point(1).y()); + points.setPoint(2, static_cast <int>(tmpRect.center().x() + userwidth/2), + points.point(2).y()); + points.setPoint(4, static_cast <int>(tmpRect.center().x() - userwidth/2), + points.point(4).y()); + //} + } + if ( bMultiRows )painter->setClipping( false ); + painter->drawPolygon( points ); + + //correct the y position: displaying position for the value label + QPoint tpLeft (points.point(4).x(), points.point(4).y() - 2 * yArrowGap ); + QPoint tpRight(points.point(2).x(), points.point(2).y() - 2 * yArrowGap ); + + //store the front rectangle + excessRectNegative.setTopLeft(tpLeft); + excessRectNegative.setTopRight(tpRight); + excessRectNegative.setBottomRight(points.point(1)); + excessRectNegative.setBottomLeft(points.point(0)); + + // Don't use points for drawing after this! + if ( pointArrayList ) { + if ( cellValue < 0 ) { + //correction for labels vertical position + int size = static_cast <int> ( ( points.point( 3 ).y() - tpRight.y() + excessRectNegative.width() )/2 ); + points.setPoint( 4 ,tpLeft ); + points.setPoint( 2, tpRight ); + if ( cellValue < maxColumnValue ) + points.translate( _dataRect.x(), -_dataRect.y() - size ); + else + points.translate( _dataRect.x(), _dataRect.y() ); + } else + points.translate( _dataRect.x(), _dataRect.y() ); + + pointArrayList->append( points ); + } + + } else { + // Draw split excess arrows negatives + + /* PENDING Michel: + * Here we have two situations. + * The value is too low because over the Min negative value + * The value is not within the configured view.. + */ + QPointArray points( 5 ); + /*this works in a positive view -> 200 500*/ + points.setPoint( 0, frontX1, cellValue < 0 ? + static_cast<int>(yZero+height1 - 3.0*delta) + arrowXAxisGap: + static_cast<int>( yZero + height1)+(2 * yArrowGap)+ arrowXAxisGap); + points.setPoint( 1, frontX2, cellValue < 0 ? + static_cast<int>(yZero+height1 - 3.0*delta) + arrowXAxisGap: + static_cast<int>( yZero + height1)+(2 * yArrowGap)+ arrowXAxisGap); + points.setPoint( 2, frontX2, cellValue < 0 ? + static_cast<int>(yZero+height1 - 3.0*delta) + arrowXAxisGap: + static_cast<int>( yZero + height1)+(2 * yArrowGap)+ arrowXAxisGap); + points.setPoint( 3, xm, cellValue < 0 ? + static_cast<int>( yZero + height1) + arrowXAxisGap: + static_cast<int>(yZero+height1 - 3.0*delta)+(2 * yArrowGap)+ arrowXAxisGap); + points.setPoint( 4, frontX1, cellValue < 0 ? + static_cast<int>(yZero+height1 - 3.0*delta) + arrowXAxisGap: + static_cast<int>( yZero + height1)+(2 * yArrowGap)+ arrowXAxisGap); + + /* 0 between start and end -> -500 500*/ + if ( minColumnValue < 0 && maxColumnValue > 0 ) { + points.setPoint(0, points.point(0).x(), static_cast <int> (yposNegatives - zeroXAxisI) ); + points.setPoint(1, points.point(1).x(), static_cast <int> (yposNegatives - zeroXAxisI) ); + } + + /* negative view -> -200 -500 */ + if ( minColumnValue < 0 && maxColumnValue < 0 ) { + /*value negative or zero > maxColumnValue*/ + if ( cellValue > maxColumnValue ) { + // the view is under Yaxis 0 level + // we need to show a symbol for the bars which are over the Yaxis. + // calculate the coordinate and direction for the arrow. + // arrow downward for negative values and upward for positives value + int diffArrowBase = points.point(2).y() - points.point(3).y(); + double maxValueYPos = maxColumnValue * pixelsPerUnit; + + points.setPoint( 0, frontX1, static_cast <int> ( yZero - maxValueYPos) ); + points.setPoint( 1, frontX2, static_cast <int> ( yZero - maxValueYPos) ); + points.setPoint( 2, frontX2, static_cast <int> ( yZero - maxValueYPos) ); + points.setPoint( 3, xm, static_cast <int> ( yZero - maxValueYPos - diffArrowBase ) ); + points.setPoint( 4, frontX1, static_cast <int> ( yZero - maxValueYPos) ); + } + /*value < mincolumn value*/ + if ( cellValue < minColumnValue ) { + points.setPoint( 0, frontX1, static_cast <int> ( yZero - maxValueYPos) ); + points.setPoint( 1, frontX2, static_cast <int> ( yZero - maxValueYPos) ); + } + } + + //Pending Michel adjust the painting in case we have a user given Width + //allow it to be larger than the auto calculated width in case we want + //to overlap + if ( params()->userWidth() != 0 ) { + int userwidth = 0; + if ( params()->userWidth() < 0 ) + userwidth = static_cast <int> (params()->userWidth() * -areaWidthP1000); + else + userwidth = static_cast <int> (params()->userWidth() * areaWidthP1000); + //if ( userwidth < frontBarWidth ) { + QRect tmpRect ( points.point(0), points.point(2)); + points.setPoint(0, static_cast <int>(tmpRect.center().x() - userwidth/2), + points.point(0).y()); + points.setPoint(1, static_cast <int>(tmpRect.center().x() + userwidth/2), + points.point(1).y()); + points.setPoint(2, static_cast <int>(tmpRect.center().x() + userwidth/2), + points.point(2).y()); + points.setPoint(4, static_cast <int>(tmpRect.center().x() - userwidth/2), + points.point(4).y()); + } + if ( bMultiRows )painter->setClipping( false ); + painter->drawPolygon( points ); + + //correct the y position: displaying position for the value label + QPoint tpLeft (points.point(4).x(), points.point(4).y() - 2 * yArrowGap ); + QPoint tpRight(points.point(2).x(), points.point(2).y() - 2 * yArrowGap ); + + //store the excess front rectangle + excessRectNegative.setTopLeft(tpLeft); + excessRectNegative.setTopRight(tpRight); + excessRectNegative.setBottomRight(points.point(1)); + excessRectNegative.setBottomLeft(points.point(0)); + + // Don't use points for drawing after this! + if ( pointArrayList ) { + if ( cellValue < 0 ) { + //calculate correction for labels vertical position + int size = static_cast <int> ( ( points.point( 3 ).y() - tpRight.y() + excessRectNegative.width() )/2 ); + if ( cellValue < maxColumnValue ) + points.translate( _dataRect.x(), -_dataRect.y() - size ); + else + points.translate( _dataRect.x(), _dataRect.y() + ( 2 * yArrowGap ) ); + } else + points.translate( _dataRect.x(), -_dataRect.y() ); + + pointArrayList->append( points ); + } + + QPointArray points2( 6 ); + points2.setPoint( 0, frontX1, cellValue < 0 ? + static_cast<int>( yZero + height1 - 3.0 * delta ) + arrowXAxisGap: + static_cast<int>(yZero + height1) + arrowXAxisGap); + points2.setPoint( 1, xm, cellValue < 0 ? + static_cast<int>(yZero + height1) + arrowXAxisGap: + static_cast<int>( yZero + height1 - 3.0 * delta ) + arrowXAxisGap); + points2.setPoint( 2, frontX2, cellValue < 0 ? + static_cast<int>(yZero + height1 - 3.0 * delta) + arrowXAxisGap: + static_cast<int>(yZero + height1) + arrowXAxisGap); + points2.setPoint( 3, frontX2, cellValue < 0 ? + static_cast<int>(yZero + height1 - 3.75 * delta) + arrowXAxisGap : + static_cast<int>(yZero + height1 - 0.75 * delta) + arrowXAxisGap); + points2.setPoint( 4, xm, cellValue < 0 ? + static_cast<int>(yZero + height1 - 0.75 * delta) + arrowXAxisGap: + static_cast<int>(yZero + height1 - 3.75 * delta) + arrowXAxisGap); + points2.setPoint( 5, frontX1, cellValue < 0 ? + static_cast<int>(yZero + height1 - 3.75 * delta) + arrowXAxisGap: + static_cast<int>(yZero + height1 - 0.75 * delta) + arrowXAxisGap); + points2.translate( 0, yArrowGap ); + + if ( minColumnValue < 0 && maxColumnValue < 0 && cellValue > maxColumnValue ) { + // the view is under Yaxis 0 level + // we need to show a symbol for the bars which are over the Yaxis. + // calculate the coordinate and direction for the arrow. + // arrow downward for negative values and upward for positives value + int diffArrowBase = points.point(2).y() - points.point(3).y(); + double maxValueYPos = maxColumnValue * pixelsPerUnit; + double minValueYPos = minColumnValue * pixelsPerUnit; + double adjustedArrow = cellValue == 0 ? minValueYPos - maxValueYPos + diffArrowBase: + minValueYPos - maxValueYPos - diffArrowBase + 1; + + points2.setPoint( 0, frontX1, points2.point(0).y() + + static_cast <int> (adjustedArrow - diffArrowBase + yArrowGap) ); + points2.setPoint( 1, xm, points2.point(1).y() + + static_cast <int> (adjustedArrow - diffArrowBase + yArrowGap) ); + points2.setPoint( 2, frontX2, points2.point(2).y() + + static_cast <int> (adjustedArrow - diffArrowBase + yArrowGap) ); + points2.setPoint( 3, frontX2, points2.point(3).y() + + static_cast <int> (adjustedArrow - diffArrowBase + yArrowGap) ); + points2.setPoint( 4, xm, points2.point(4).y() + + static_cast <int> (adjustedArrow - diffArrowBase + yArrowGap) ); + points2.setPoint( 5, frontX1, points2.point(5).y() + + static_cast <int> (adjustedArrow - diffArrowBase + yArrowGap) ); + } + + //Pending Michel adjust the painting in case we have a user given Width + //allow it to be larger than the auto calculated width in case we want + //to overlap + if ( params()->userWidth() != 0 ) { + int userwidth = 0; + if ( params()->userWidth() < 0 ) + userwidth = static_cast <int> (params()->userWidth() * -areaWidthP1000); + else + userwidth = static_cast <int> (params()->userWidth() * areaWidthP1000); + //if ( userwidth < frontBarWidth ) { + + points2.setPoint(0, excessRectNegative.topLeft().x(),points2.point(0).y()); + points2.setPoint(2, excessRectNegative.topRight().x(),points2.point(2).y()); + points2.setPoint(3, excessRectNegative.topRight().x(),points2.point(3).y()); + points2.setPoint(5, excessRectNegative.topLeft().x(),points2.point(5).y()); + } + + painter->drawPolygon( points2 ); + + /* + NOTE: We do NOT want to store the region here, but + we use the front rectangle above in order to display the + data value labels inside the front rectangle. + Disadvantage: clicking onto these excess arrows, will not + result in a signal being sent, because KDChartWidget will + not notice, that the user has clicked onto the bar. + That's a minor drawback, compared to the gain of being + able to position the labels correctly. + */ + + if ( cellValue < 0 ) + points2.translate( 0, yArrowGap ); + else + points2.translate( 0, -yArrowGap ); + + painter->drawPolygon( points2 ); + + + /* + NOTE: We do NOT want to store the region here + (see comment above) + */ + } + } + painter->setClipRect( ourClipRect ); + } /*if (tooLow && bNormalMode)*/ + else { + // + // old code (sometimes not touching the grid): + //QRect rec( xpos, yZero, frontBarWidth, -barHeight ); + //painter->drawRect( rec ); + // + + int pt1Y = static_cast < int > ( yZero - barHeight /*- sideBarsHeight*/ ); + + /* + if ( cellValue != 0 ) { + pt1Y = static_cast <int> (cellValue * pixelsPerUnit * -2); + qDebug( "value %s",QString::number(static_cast <int> (cellValue)).latin1()); + qDebug( "pt1Y %s", QString::number(static_cast <int> (cellValue * pixelsPerUnit * -2)).latin1()); + } + else + pt1Y = static_cast < int > ( yZero - barHeight); + */ + calculateXFront1_2( bNormalMode, bIsVeryFirstBar, bIsFirstDataset, _bThreeDBars, + xpos, valueBlockGap, datasetGap, frontBarWidth, + frontX1, frontX2, prevFrontX2 ); + + QPoint pt1( frontX1, pt1Y ); + QPoint pt2( frontX2, + static_cast < int > ( yZero + sideBarHeight) ); + + if( 0.0 > maxColumnValue ){ + pt2.setY(pt2.y() - static_cast < int > (pixelsPerUnit * maxColumnValue)); + } + if( pt2.y() < pt1Y ) { + pt1.setY( pt2.y() ); + pt2.setY( pt1Y ); + } + if( pt2.x() < frontX1 ) { + pt1.setX( frontX2 ); + pt2.setX( frontX1 ); + } + if( bDrawExtraLines ){ + int y = pt2.y(); + if( _bThreeDBars ) + y -= static_cast < int > ( sideBarHeight ); + drawExtraLinesAndMarkers( + propSet, + defaultPen, + defaultMarkerStyle, + (frontX1+frontX2)/2, y, + painter, + ai.abscissaPara, + ordinatePara, + areaWidthP1000, + logHeight/1000.0, + bDrawExtraLinesInFront ); + }else if( bShowThisBar ){ + + QSize siz( pt2.x() - pt1.x(), + pt2.y() - pt1.y() ); + QRect rect( pt1, siz ); + + if( 1.5 > frontBarWidth ){ + QPen oldPen( painter->pen() ); + painter->setPen( QPen(painter->brush().color(), 0) ); + painter->drawLine(pt1, QPoint(pt1.x(),pt2.y())); + painter->setPen( oldPen ); + }else{ + // store the front rect negative + if ( tooLow || cellValue < minColumnValue) { + frontRectNegative.setTopLeft(excessRectNegative.bottomLeft()); + frontRectNegative.setTopRight(excessRectNegative.bottomRight()); + frontRectNegative.setBottomRight(excessRectNegative.topRight()); + frontRectNegative.setBottomLeft(excessRectNegative.topLeft()); + + } else { + frontRectNegative.setTopLeft(rect.topLeft()); + frontRectNegative.setTopRight(rect.topRight()); + frontRectNegative.setBottomRight(rect.bottomRight()); + frontRectNegative.setBottomLeft(rect.bottomLeft()); + + if ( cellValue == 0 && params()->barChartSubType() == KDChartParams::BarPercent) + rect.setTop( rect.bottom()); + } + + //debug lines and points + /* + painter->drawLine( rect.topLeft(), rect.topRight()); + painter->drawLine( rect.topRight(), rect.bottomRight()); + painter->drawLine( rect.bottomRight(), rect.bottomLeft()); + painter->drawLine( rect.bottomLeft(), rect.topLeft()); + + painter->drawText( frontRectNegative.topLeft(), "0f"); + painter->drawText( frontRectNegative.topRight(), "1f"); + painter->drawText( frontRectNegative.bottomRight(), "2f"); + painter->drawText( frontRectNegative.bottomLeft(), "3f"); + */ + + //Pending Michel adjust the painting in case we have a user given Width + //and it is not larger than the auto calculated width + + if ( params()->userWidth() != 0 ) { + int userwidth = 0; + if ( params()->userWidth() < 0 ) + userwidth = static_cast <int> (params()->userWidth() * -areaWidthP1000); + else + userwidth = static_cast <int> (params()->userWidth() * areaWidthP1000); + //if ( userwidth < frontBarWidth ) { + rect.setLeft( static_cast <int> (rect.center().x() - (userwidth/2))); + rect.setRight( static_cast <int> (rect.center().x() + (userwidth/2))); + rect.setWidth( static_cast <int> (userwidth) ); + frontRectNegative.setLeft( tooLow || cellValue < minColumnValue ? + static_cast <int> (excessRectNegative.center().x() - (userwidth/2)):rect.left()); + frontRectNegative.setRight( tooHigh ? static_cast <int> (excessRectNegative.center().x() + (userwidth/2)):rect.right()); + } + //drawing the front size negative values + if ( cellValue != 0 && params()->barChartSubType() != KDChartParams::BarPercent) { + painter->setClipping( false ); + painter->drawRect( rect ); + } + } + // Don't use rect for drawing after this! + if ( pointArrayList ) { + rect.moveBy( _dataRect.x(), _dataRect.y() ); + pointArrayList->append( rectToPointArray( rect ) ); + } + } + } + } else { + // + // Positive values: + // + /*Pending Michel: all values under the min value are handled as negative*/ + + double maxValueYPos = maxColumnValue * pixelsPerUnit; + double minValueYPos = minColumnValue * pixelsPerUnit; + double minDataValueYPos = maxValueYPos - minValueYPos; + double yZero = yposPositives - zeroXAxisI; + //qDebug( "yposPositives %f - zeroXAxisI %f = %f" , yposPositives, zeroXAxisI, yZero ); + //qDebug( "yZero %s", QString::number( yZero ).latin1()); + //qDebug( "minDataValueYPos = %s", QString::number( minDataValueYPos).latin1()); + //qDebug( "positive value %s", QString::number( cellValue).latin1()); + //Pending Michel: draw the default split excess arrows + //when the front top of the 3d chart reach the max Y value + + if(!_bThreeDBars) + tooHigh = ( barHeight > maxValueYPos*1.001 ) || ( cellValue < minColumnValue ); + else { + //calculate the Y position for the top front line + //if it is over the max height pos - tooHigh becomes true + if ( params()->barChartSubType()!= KDChartParams::BarStacked ) { + int dataValueYPos = static_cast <int>( ( cellValue * pixelsPerUnit ) ); + tooHigh = dataValueYPos > maxValueYPos; + } else { + tooHigh = maxValueInThisColumn > maxColumnValue; + } + } + + if ( tooHigh && bNormalMode || + tooHigh && params()->barChartSubType()== KDChartParams::BarStacked + || tooHigh && bMultiRows ) { + + double delta = -0.0125 * logHeight; + double height = -1.0 * yZero + - 2.0 * delta; + double height1 = height + -3.0 * delta; + + int yArrowGap = static_cast < int > ( 2.5 * delta ); + calculateXFront1_2( bNormalMode, bIsVeryFirstBar, bIsFirstDataset, _bThreeDBars, + xpos, valueBlockGap, datasetGap, frontBarWidth, + frontX1, frontX2, prevFrontX2 ); + + const int xm = static_cast < int > ( ( frontX1 + frontX2 ) / 2.0 ); + QRect rect( ourClipRect ); + + rect.setTop( static_cast<int>( rect.top() + 3 * delta ) ); + rect.setHeight( static_cast<int>( rect.height() - 3 * delta ) ); + painter->setClipRect( rect ); + + if( bDrawExtraLines ){ + drawExtraLinesAndMarkers( + propSet, + defaultPen, + defaultMarkerStyle, + xm, static_cast<int>( yZero + height1 ), + painter, + ai.abscissaPara, + ordinatePara, + areaWidthP1000, + logHeight/1000.0, + bDrawExtraLinesInFront ); + }else if( bShowThisBar ){ + if( params()->drawSolidExcessArrows() ) { + // Draw solid excess arrows + QPointArray points( 5 ); + /*this works for positive config and 0 include config*/ + points.setPoint( 0, frontX1, + (minDataValueYPos < static_cast <int> (yZero))? + static_cast <int> (minDataValueYPos-1):static_cast <int>(yZero)); + points.setPoint( 1, frontX2, + (minDataValueYPos < static_cast <int> (yZero))? + static_cast <int> (minDataValueYPos-1):static_cast <int>(yZero)); + points.setPoint( 2, frontX2, static_cast<int>( yZero + height1 - 3.0 * delta ) + + 2 * yArrowGap ); + points.setPoint( 3, xm, static_cast<int>( yZero + height1 ) + + 2 * yArrowGap ); + points.setPoint( 4, frontX1, static_cast<int>( yZero + height1 - 3.0 * delta ) + + 2 * yArrowGap ); + + /*case where start and end value are negatives */ + if ( cellValue > maxColumnValue && 0 >= maxColumnValue ) { + points.setPoint( 0, frontX1,static_cast<int>( yZero + height1 - 3.0 * delta ) + 2 * yArrowGap); + points.setPoint( 1, frontX2,static_cast<int>( yZero + height1 - 3.0 * delta ) + 2 * yArrowGap); + } + + //Pending Michel adjust the painting in case we have a user given Width + //allow it to be larger than the auto calculated width in case we want + //to overlap + if ( params()->userWidth() != 0 ) { + int userwidth = 0; + if ( params()->userWidth() < 0 ) + userwidth = static_cast <int> (params()->userWidth() * -areaWidthP1000); + else + userwidth = static_cast <int> (params()->userWidth() * areaWidthP1000); + //if ( userwidth < frontBarWidth ) { + QRect tmpRect ( points.point(0), points.point(2)); + points.setPoint(0, static_cast <int>(tmpRect.center().x() - userwidth/2), + points.point(0).y()); + points.setPoint(1, static_cast <int>(tmpRect.center().x() + userwidth/2), + points.point(1).y()); + points.setPoint(2, static_cast <int>(tmpRect.center().x() + userwidth/2), + points.point(2).y()); + points.setPoint(4, static_cast <int>(tmpRect.center().x() - userwidth/2), + points.point(4).y()); + //} + } + if ( params()->barChartSubType() != KDChartParams::BarStacked || + params()->barChartSubType() == KDChartParams::BarStacked && dataset != datasetEnd ) { + //drawing a single front in case it is too high + painter->setClipping( false ); + painter->drawPolygon( points ); + } + //correct the y position: displaying position for the value label + QPoint tpLeft (points.point(4).x(), static_cast <int> (points.point(4).y() - yArrowGap)); + QPoint tpRight(points.point(2).x(), static_cast <int> (points.point(2).y() - yArrowGap)); + + //debugging points + /* + painter->drawText( points.point(0), "p0"); + painter->drawText( points.point(1), "p1"); + painter->drawText( points.point(2), "p2"); + painter->drawText( points.point(3), "p3"); + */ + //store the front rectangle + excessRectPositive.setTopLeft(tpLeft); + excessRectPositive.setTopRight(tpRight); + excessRectPositive.setBottomRight(points.point(1)); + excessRectPositive.setBottomLeft(points.point(0)); + + // Don't use points for drawing after this! + if ( pointArrayList && params()->barChartSubType() != KDChartParams::BarStacked ) { + points.translate( _dataRect.x(), _dataRect.y() + excessRectPositive.top() - yArrowGap ); + pointArrayList->append( points ); + } else if ( params()->barChartSubType() == KDChartParams::BarStacked ) { + if ( dataset != datasetEnd ) { + points.translate( _dataRect.x(), _dataRect.y() + excessRectPositive.top() ); + pointArrayList->append( points ); + } else { + //adjust the display of the value label under Y max value level + points.translate( _dataRect.x(), _dataRect.y() - excessRectPositive.bottom() - yArrowGap); + pointArrayList->append( points ); + } + } + } else { + + // Draw split excess arrows (default) + /* PENDING Michel: + * Here we have two situations. + * The value is too high because over the Max positive value + * or it is not within the configured view. + */ + QPointArray points( 5 ); + /*this works for positive config and 0 include config*/ + points.setPoint( 0, frontX1, + (minDataValueYPos < static_cast <int> (yZero))? + static_cast <int> (minDataValueYPos - 1) : static_cast <int>(yZero)); + points.setPoint( 1, frontX2, + (minDataValueYPos < static_cast <int> (yZero))? + static_cast<int> ( minDataValueYPos - 1) : static_cast <int>(yZero)); + points.setPoint( 2, frontX2, static_cast<int>( yZero + height1 - 3.0 * delta ) ); + points.setPoint( 3, xm, static_cast<int>( yZero + height1 ) ); + points.setPoint( 4, frontX1, static_cast<int>( yZero + height1 - 3.0 * delta ) ); + + /*case where start and end value are negatives */ + if ( cellValue > maxColumnValue && 0 >= maxColumnValue || cellValue == 0) { + points.setPoint( 0, frontX1, static_cast<int>( yZero + height1 - 3.0 * delta )); + points.setPoint( 1, frontX2, static_cast<int>( yZero + height1 - 3.0 * delta )); + } + + //Pending Michel adjust the painting in case we have a user given Width + //allow it to be larger than the auto calculated width in case we want + //to overlap + if ( params()->userWidth() != 0 ) { + int userwidth = 0; + if ( params()->userWidth() < 0 ) + userwidth = static_cast <int> (params()->userWidth() * -areaWidthP1000); + else + userwidth = static_cast <int> (params()->userWidth() * areaWidthP1000); + //if ( userwidth < frontBarWidth ) { + QRect tmpRect ( points.point(0), points.point(2)); + points.setPoint(0, static_cast <int>(tmpRect.center().x() - userwidth/2), + points.point(0).y()); + points.setPoint(1, static_cast <int>(tmpRect.center().x() + userwidth/2), + points.point(1).y()); + points.setPoint(2, static_cast <int>(tmpRect.center().x() + userwidth/2), + points.point(2).y()); + points.setPoint(4, static_cast <int>(tmpRect.center().x() - userwidth/2), + points.point(4).y()); + //} + } + if ( params()->barChartSubType() != KDChartParams::BarStacked || + params()->barChartSubType() == KDChartParams::BarStacked && dataset != datasetEnd ) { + painter->setClipping( false ); + painter->drawPolygon( points ); + } + + //store the front rectangle + excessRectPositive.setTopLeft(points.point(4)); + excessRectPositive.setTopRight(points.point(2)); + excessRectPositive.setBottomRight(points.point(1)); + excessRectPositive.setBottomLeft(points.point(0)); + + // Don't use points for drawing after this! + if ( pointArrayList && params()->barChartSubType() != KDChartParams::BarStacked ) { + points.translate( _dataRect.x(), _dataRect.y() + excessRectPositive.top() ); + pointArrayList->append( points ); + } else if ( params()->barChartSubType() == KDChartParams::BarStacked ) { + if ( dataset != datasetEnd ) { + points.translate( _dataRect.x(), _dataRect.y() + excessRectPositive.top() ); + pointArrayList->append( points ); + } else { + //adjust the display of the value label under Y max value level + points.translate( _dataRect.x(), _dataRect.y() - excessRectPositive.bottom() - yArrowGap); + pointArrayList->append( points ); + } + } + + QPointArray points2( 6 ); + points2.setPoint( 0, frontX1, static_cast<int>( yZero + height1 - 3.0 * delta ) ); + points2.setPoint( 1, xm, static_cast<int>( yZero + height1 ) ); + points2.setPoint( 2, frontX2, static_cast<int>( yZero + height1 - 3.0 * delta ) ); + points2.setPoint( 3, frontX2, static_cast<int>( yZero + height1 - 3.75 * delta ) ); + points2.setPoint( 4, xm, static_cast<int>( yZero + height1 - 0.75 * delta ) ); + points2.setPoint( 5, frontX1, static_cast<int>( yZero + height1 - 3.75 * delta ) ); + points2.translate( 0, yArrowGap ); + + //Pending Michel adjust the painting in case we have a user given Width + //allow it to be larger than the auto calculated width in case we want + //to overlap + if ( params()->userWidth() != 0 ) { + int userwidth = 0; + if ( params()->userWidth() < 0 ) + userwidth = static_cast <int> (params()->userWidth() * -areaWidthP1000); + else + userwidth = static_cast <int> (params()->userWidth() * areaWidthP1000); + //if ( userwidth < frontBarWidth ) { + points2.setPoint(0, excessRectPositive.topLeft().x(),points2.point(0).y()); + points2.setPoint(2, excessRectPositive.topRight().x(),points2.point(2).y()); + points2.setPoint(3, excessRectPositive.topRight().x(),points2.point(3).y()); + points2.setPoint(5, excessRectPositive.topLeft().x(),points2.point(5).y()); + //} + } + + painter->drawPolygon( points2 ); + /* + NOTE: We do NOT want to store the region here, but + we use the front rectangle above in order to display the + data value labels inside the front rectangle. + Disadvantage: clicking onto these excess arrows, will not + result in a signal being sent, because KDChartWidget will + not notice, that the user has clicked onto the bar. + That's a minor drawback, compared to the gain of being + able to position the labels correctly. + */ + points2.translate( 0, yArrowGap ); + painter->drawPolygon( points2 ); + /* + NOTE: We do NOT want to store the region here + (see comment above) + */ + } // draw split excess arrow + } //if( bShowThisBar ) + + painter->setClipRect( ourClipRect ); + } // not tooLow && bNormalMode ) + else { + //bool fromBottom = bNormalMode && !_bThreeDBars; + double y0 = yposPositives - zeroXAxisI; + + int pt1Y = static_cast < int > ( y0 - barHeight - sideBarHeight); + + calculateXFront1_2( bNormalMode, bIsVeryFirstBar, bIsFirstDataset, _bThreeDBars, + xpos, valueBlockGap, datasetGap, frontBarWidth, + frontX1, frontX2, prevFrontX2 ); + + QPoint pt1( frontX1, pt1Y ); + QPoint pt2( frontX2, + static_cast < int > ( y0 + shiftUpperBars ) ); + + if( 0.0 < minColumnValue ) + pt2.setY(pt2.y() - static_cast < int > ( pixelsPerUnit * minColumnValue )); + + if( pt2.y() < pt1Y ) { + pt1.setY( pt2.y() ); + pt2.setY( pt1Y ); + } + if( pt2.x() < frontX1 ) { + pt1.setX( frontX2 ); + pt2.setX( frontX1 ); + } + if( bDrawExtraLines ){ + int y = pt1.y(); + if( _bThreeDBars ) + y -= static_cast < int > ( sideBarHeight ); + + drawExtraLinesAndMarkers( + propSet, + defaultPen, + defaultMarkerStyle, + (frontX1+frontX2)/2, y, + painter, + ai.abscissaPara, + ordinatePara, + areaWidthP1000, + logHeight/1000.0, + bDrawExtraLinesInFront ); + }else if( bShowThisBar ){ + + QSize siz( pt2.x() - pt1.x(), + pt2.y() - pt1.y()); + + QRect rect( pt1, siz ); + + //adjust display for the 3d percent bars - last dataset. + if ( params()->barChartSubType() == KDChartParams::BarPercent && cellValue != 0) + rect.setTop( rect.top() + static_cast <int> (sideBarHeight) - 1); + + // store the front rect + if( tooHigh ) { + frontRectPositive.setTopLeft(excessRectPositive.topLeft()); + frontRectPositive.setTopRight(excessRectPositive.topRight()); + frontRectPositive.setBottomRight(excessRectPositive.bottomRight()); + frontRectPositive.setBottomLeft(excessRectPositive.bottomLeft()); + } else { + frontRectPositive.setTopLeft(rect.topLeft()); + frontRectPositive.setTopRight(rect.topRight()); + frontRectPositive.setBottomRight(rect.bottomRight()); + frontRectPositive.setBottomLeft(rect.bottomLeft()); + if( _bThreeDBars && cellValue == 0 ) + frontRectNegative = frontRectPositive; + } + + if( 1.5 > frontBarWidth ){ + //qDebug("1.5 > frontBarWidth "); + QPen oldPen( painter->pen() ); + painter->setPen( QPen(painter->brush().color(), 0) ); + painter->drawLine(pt1, QPoint(pt1.x(),pt2.y())); + painter->setPen( oldPen ); + }else{ + //debugging points and lines + /* + painter->drawText( rect.topLeft(), QString::number(cellValue).latin1()); + painter->drawText( rect.topRight(), "1f"); + painter->drawText( rect.bottomRight(), "2f"); + painter->drawText( rect.bottomLeft(), "3f"); + painter->drawLine( rect.topLeft(), rect.topRight()); + painter->drawLine( rect.topRight(), rect.bottomRight()); + painter->drawLine( rect.bottomRight(), rect.bottomLeft()); + painter->drawLine( rect.bottomLeft(), rect.topLeft()); + */ + + //Pending Michel adjust the painting in case we have a user given Width + //allow it to be larger than the auto calculated width in case we want + //to overlap + if ( params()->userWidth() != 0 ) { + int userwidth = 0; + if ( params()->userWidth() < 0 ) + userwidth = static_cast <int> (params()->userWidth() * -areaWidthP1000); + else + userwidth = static_cast <int> (params()->userWidth() * areaWidthP1000); + //if ( userwidth < frontBarWidth ) { + rect.setLeft( static_cast <int> (rect.center().x() - (userwidth/2))); + rect.setRight( static_cast <int> (rect.center().x() + (userwidth/2))); + rect.setWidth( static_cast <int> (userwidth) ); + //adjust the front rect + frontRectPositive.setLeft( tooHigh ? static_cast <int> (excessRectPositive.center().x() - (userwidth/2)):rect.left()); + frontRectPositive.setRight( tooHigh ? static_cast <int> (excessRectPositive.center().x() + (userwidth/2)):rect.right()); + } + + // drawing the front side + if (!tooHigh && !tooLow || params()->barChartSubType() == KDChartParams::BarPercent ) { + if ( bMultiRows ) + painter->setClipping( false ); + else + painter->setClipping( true ); + painter->drawRect( rect ); + } + // Don't use rect for drawing after this + if ( pointArrayList ) { + rect.moveBy( _dataRect.x(), _dataRect.y()); + pointArrayList->append( rectToPointArray( rect ) ); + } + } + } // bShowThisBar + } // positive values + } + if ( bShowThisBar && _bThreeDBars && !bDrawExtraLines ) { + //Pending Michel: no need to use that anymore + //const int maxY = 2*devRect.height(); + QPointArray points( 4 ); + if (cellValue <= 0 || cellValue < minColumnValue) { + if ( tooLow || cellValue < minColumnValue ) { + points.setPoint(0, excessRectNegative.topRight()); + points.setPoint(1, excessRectNegative.topRight().x() + static_cast<int>(sideBarHeight), + excessRectNegative.top() - static_cast<int>(sideBarHeight)); + points.setPoint(2, excessRectNegative.bottomRight().x() + static_cast<int>(sideBarHeight), + excessRectNegative.bottom() - static_cast<int>(sideBarHeight)); + points.setPoint(3, excessRectNegative.bottomRight()); + } else { + points.setPoint( 0, frontRectNegative.bottomRight()); + points.setPoint( 1, frontRectNegative.bottomRight().x() + static_cast<int>(sideBarHeight), + frontRectNegative.bottom() - static_cast<int>(sideBarHeight) ); + points.setPoint(2, frontRectNegative.bottomRight().x() + static_cast<int>(sideBarHeight), + frontRectNegative.top() - static_cast<int>(sideBarHeight)); + points.setPoint(3, frontRectNegative.topRight() ); + } + + rightRectNegative.setTopLeft( points.point(0)); + rightRectNegative.setTopRight( points.point(2)); + rightRectNegative.setBottomRight(points.point(1)); + rightRectNegative.setBottomLeft(points.point(3)); + + } else { + // Pending Michel + // Make sure to align the right side top and bottom points + // to the front side points + if ( tooHigh ) { + points.setPoint(0, excessRectPositive.topRight()); + points.setPoint(1, excessRectPositive.topRight().x() + static_cast <int> (sideBarHeight), + excessRectPositive.top() - static_cast <int> (sideBarHeight) ); + points.setPoint(2, excessRectPositive.bottomRight().x() + static_cast <int> (sideBarHeight), + excessRectPositive.bottom() - static_cast <int> (sideBarHeight)); + points.setPoint(3, excessRectPositive.bottomRight()); + } else { + points.setPoint(0, frontRectPositive.topRight()); + points.setPoint(1, frontRectPositive.topRight().x() + static_cast <int> (sideBarHeight), + frontRectPositive.top() - static_cast<int>(sideBarHeight)); + points.setPoint(2, frontRectPositive.bottomRight().x() + static_cast <int> (sideBarHeight), + frontRectPositive.bottom() - static_cast<int>(sideBarHeight)); + points.setPoint(3, frontRectPositive.bottomRight()); + } + //register the right rect + rightRectPositive.setTopLeft( points.point(0)); + rightRectPositive.setTopRight( points.point(1)); + rightRectPositive.setBottomLeft( points.point(3)); + rightRectPositive.setBottomRight(points.point(2)); + } + + if ( myShadow2Color.isValid() ) + painter->setBrush( QBrush( myShadow2Color, params()->shadowPattern() ) ); + else + painter->setBrush( NoBrush ); + + //debug points and lines + /* + painter->drawText( points.point(0), "0r"); + painter->drawText( points.point(1), "1r"); + painter->drawText( points.point(2), "2r"); + painter->drawText( points.point(3), "3r"); + + painter->drawLine( points.point(0), points.point(1)); + painter->drawLine( points.point(1), points.point(2)); + painter->drawLine( points.point(2), points.point(3)); + painter->drawLine( points.point(3), points.point(0)); + */ + + //drawing the right side + if( (!tooHigh && !tooLow) || (tooHigh && cellValue <= 0 ) ) { + if (( cellValue != 0 && params()->barChartSubType() == KDChartParams::BarPercent ) || + ( cellValue != 0 && params()->barChartSubType() == KDChartParams::BarStacked ) || + ( cellValue != 0 && bNormalMode || + cellValue != 0 && bMultiRows)) { + painter->setClipping( false ); + painter->drawPolygon( points ); + } + } + + // Dont override the region stored in case of excess values or barPercent this in order to display + // the value labels closer to the corner of the front bar. + if ( pointArrayList && !tooHigh && !tooLow && params()->barChartSubType() != KDChartParams::BarPercent ) { + QPointArray points2cpy( points.copy() ); + //qDebug("g2"); + points2cpy.translate( _dataRect.x(), _dataRect.y()); + //qDebug("g3"); + pointArrayList->append( points2cpy ); + //qDebug("g4"); + } + + // drawing the top, but only for the topmost piece for stacked and percent + if ( bNormalMode || bMultiRows || + // For stacked and percent bars, there are three ways to determine + // the top: + // 1. all values are negative: the top is the one in the first dataset + ( maxValueInThisColumn <= 0.0 && dataset == firstValidValue ) || + // 2. all values are positive: the top is the one in the last dataset + ( minValueInThisColumn >= 0.0 && dataset == lastValidPositiveValue ) || + // 3. some values are positive, some negative: + // the top is the one in the last positive + // dataset value + ( dataset == lastValidPositiveValue ) ) { + if (cellValue <= 0 || cellValue < minColumnValue) { + if ( tooLow ) { + points.setPoint(0,excessRectNegative.bottomLeft()); + points.setPoint(1,excessRectNegative.topLeft().x() + static_cast <int> (sideBarHeight), + excessRectNegative.bottom() - static_cast <int> (sideBarHeight)); + points.setPoint(2,excessRectNegative.bottomRight().x() + static_cast <int> (sideBarHeight), + excessRectNegative.bottom() - static_cast <int> (sideBarHeight)); + points.setPoint(3,excessRectNegative.bottomRight()); + }else { + // Align the top to the front and the right side + points.setPoint(0,frontRectNegative.topLeft() ); + points.setPoint(1,frontRectNegative.topLeft().x() + static_cast <int> (sideBarHeight), rightRectNegative.top()); + points.setPoint(2,rightRectNegative.topRight() ); + points.setPoint(3,rightRectNegative.topRight().x() - static_cast <int> (sideBarHeight), + frontRectNegative.topRight().y() ); + //make sure the top rect color is the right one - barStacked - Noll values + if ( (params()->barChartSubType() == KDChartParams::BarStacked && cellValue == 0 && maxValueInThisColumn != 0 ) ) + points.translate(0, maxValueInThisColumn <= 0?-(static_cast <int> (1*pixelsPerUnit) + 1): + -static_cast<int>(maxValueInThisColumn*pixelsPerUnit)); + + if ( params()->barChartSubType() == KDChartParams::BarPercent && cellValue == 0 ) { + if ( dataset == datasetEnd && maxValueInThisColumn != 0) + points.translate(0, -static_cast<int>( logHeight - sideBarHeight )); + else if ( maxValueInThisColumn == 0) + points.translate(0, static_cast <int> (logHeight + (sideBarHeight - sideBarWidth))); + } + } + } else { + if ( tooHigh ) { + points.setPoint(0, excessRectPositive.topLeft()); + points.setPoint(1, excessRectPositive.topLeft().x() + static_cast <int> (sideBarHeight), + excessRectPositive.top() - static_cast <int> (sideBarHeight) ); + points.setPoint(2, excessRectPositive.topRight().x() + static_cast <int> (sideBarHeight), + excessRectPositive.top() - static_cast <int> (sideBarHeight)); + points.setPoint(3, excessRectPositive.topRight()); + } else { + // Pending Michel + // Align the top to the front and the right side + points.setPoint(0, frontRectPositive.topLeft()); + points.setPoint(1, frontRectPositive.topLeft().x() + static_cast <int> (sideBarHeight), + rightRectPositive.top() ); + points.setPoint(2, rightRectPositive.topRight()); + points.setPoint(3, rightRectPositive.topRight().x() - static_cast <int> (sideBarHeight), + frontRectPositive.topRight().y()); + } + } + + if (cellValue < 0.0 && maxValueInThisColumn < 0) + painter->setBrush( bMultiRows ? myBarColor : black ); + else + painter->setBrush( QBrush( myShadow1Color, params()->shadowPattern() ) ); + + if ( !myShadow1Color.isValid() ) + painter->setBrush( NoBrush ); // override prev. setting + // debug points and lines + /* + painter->drawText( points.point(0), "0t"); + painter->drawText( points.point(1), "1t"); + painter->drawText( points.point(2), "2t"); + painter->drawText( points.point(3), "3t"); + + painter->drawLine( points.point(0), points.point(1) ); + painter->drawLine( points.point(1),points.point(2) ); + painter->drawLine( points.point(2),points.point(3) ); + painter->drawLine( points.point(3),points.point(0) ); + */ + + // drawing the top side + if (!tooHigh && !tooLow || (tooHigh && cellValue <= 0) ) + painter->drawPolygon( points ); + + // dont override the region stored in case of excess values. + if (pointArrayList && !tooHigh && !tooLow + && params()->barChartSubType() != KDChartParams::BarPercent + && params()->barChartSubType() != KDChartParams::BarStacked) { + points.translate( _dataRect.x(), _dataRect.y()); + pointArrayList->append( points ); + } + } + }//if ( _bThreeDBars ) + + if( regions && pointArrayList && ! pointArrayList->empty() ) { + if( bShowThisBar && !bDrawExtraLines ){ + KDChartDataRegion * region; + if( _bThreeDBars ){ + region = new KDChartDataRegion( dataset, value, chart, + pointArrayList, true ); + } else { + // just store a rectangle if NOT in 3-D bar mode + region = new KDChartDataRegion( dataset, value, chart, + pointArrayList->first().boundingRect() ); + delete pointArrayList; + } + /*qDebug("KDChartDataRegion stored! x: %i y: %i w: %i h: %i", + region->rect().x(),region->rect().y(), + region->rect().width(),region->rect().height());*/ + regions->append( region ); + } else { + delete pointArrayList; + } + } + }// if( !bDrawExtraLines || bCellPropertiesFound ) + }// if( !skipMe ) + }else{ + // Do not paint a bar, but update the position + // variable: to find the exact x1 position for + // the next bar that will be drawn. + int iDummy1, iDummy2; + calculateXFront1_2( bNormalMode, bIsVeryFirstBar, bIsFirstDataset, _bThreeDBars, + xpos, valueBlockGap, datasetGap, frontBarWidth, + iDummy1, iDummy2, prevFrontX2 ); + }// if( dataset >= datasetStart && dataset <= datasetEnd ) + + // Vertical advancement in stacked or percent only if there was a value + // Pending Michel add sideBarHeight in case we are in 3D mode but not for Percent + // where we need to display the top rect but cant resize the YAxis. + if ( params()->barChartSubType() == KDChartParams::BarStacked || + params()->barChartSubType() == KDChartParams::BarPercent ) + if ( /*barHeight*/ cellValue < 0 ) + (_bThreeDBars && params()->barChartSubType() != KDChartParams::BarPercent)?yposNegatives -= sideBarHeight + barHeight: + yposNegatives -= barHeight; + else + (_bThreeDBars && params()->barChartSubType() != KDChartParams::BarPercent)?yposPositives -= sideBarHeight + barHeight: + yposPositives -= barHeight; + + } else { + // Do not paint a bar, but update the position + // variable: to find the exact x1 position for + // the next bar that will be drawn. + int iDummy1, iDummy2; + calculateXFront1_2( bNormalMode, bIsVeryFirstBar, bIsFirstDataset, _bThreeDBars, + xpos, valueBlockGap, datasetGap, frontBarWidth, + iDummy1, iDummy2, prevFrontX2 ); + } + + + // advance only if the next dataset has DataEntry mode + bool bAdvanceToNextValue = + ( bMultiRows ? (dataset == chartDatasetStart) : (dataset == chartDatasetEnd) + || ( params()->chartSourceMode( bMultiRows ? dataset-1 : dataset+1 ) + == KDChartParams::DataEntry ) ); + // Advance to next value; only for normal bars + if ( bNormalMode ) { + if( bAdvanceToNextValue ) + xpos += barWidth; + // skip gap between datasets, unless last dataset + if ( dataset < myLastDataEntryDataset ) + xpos += datasetGap; + } + if( bAdvanceToNextValue || bMultiRows ){ + //qDebug("shiftMyPainter( -nShiftX, nShiftY );"); + shiftMyPainter( -nShiftX, nShiftY ); + } + bIsVeryFirstBar = false; + bIsFirstDataset = false; + } + + + // Advancement between value blocks + if ( bNormalMode ){ + // skip gap between value blocks, don't worry about last one here + xpos += valueBlockGap; + //qDebug("**************************** xpos: %f + valueBlockGap: %f = %f", xpos, valueBlockGap, xpos+valueBlockGap); + }else{ + // skip gap between value blocks + xpos += valueBlockGap + barWidth; + //qDebug ( "2/ barWidth %f", barWidth ); + //qDebug ( " valueBlockGap %f", valueBlockGap ); + // start at bottom with next value group + yposPositives = yposPositivesStart; + yposNegatives = yposNegativesStart; + } + //qDebug("shiftMyPainterBack"); + shiftMyPainterBack(); + } + + } + + + if( bMultiRows ) + painter->setClipping( bHadClipping ); +} diff --git a/kchart/kdchart/KDChartBarPainter.h b/kchart/kdchart/KDChartBarPainter.h new file mode 100644 index 00000000..10b0e05e --- /dev/null +++ b/kchart/kdchart/KDChartBarPainter.h @@ -0,0 +1,86 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTBARPAINTER_H__ +#define __KDCHARTBARPAINTER_H__ + +#include "KDChartAxesPainter.h" +#include <KDChartTable.h> + +#include <qnamespace.h> + +class KDChartParams; + +class KDChartBarPainter : public KDChartAxesPainter, public Qt +{ + friend class KDChartPainter; + protected: + KDChartBarPainter( KDChartParams* params ); + virtual ~KDChartBarPainter(); + + /* + virtual void paintData( QPainter* painter, + KDChartTableDataBase* data, + bool paint2nd, + KDChartDataRegionList* regions ); + */ + virtual bool isNormalMode() const; + virtual int clipShiftUp( bool normalMode, double areaWidthP1000 ) const; + virtual void 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 ); + virtual void calculateXFront1_2( bool bNormalMode, bool bIsVeryFirstBar, bool bIsFirstDataset, bool _bThreeDBars, + double xpos, double valueBlockGap, double datasetGap, double frontBarWidth, + int& frontX1, int& frontX2, int& previousFrontX2 ); + private: + void initMyPainter( QPainter* painter ); + void shiftMyPainter( double dx, double dy ); + void shiftMyPainterBack(); + QPainter* _myPainter; + double _painterDX; + double _painterDY; + bool _bThreeDBars; + double _areaP1000; +}; + +#endif diff --git a/kchart/kdchart/KDChartBaseSeries.cpp b/kchart/kdchart/KDChartBaseSeries.cpp new file mode 100644 index 00000000..c2c8d6dd --- /dev/null +++ b/kchart/kdchart/KDChartBaseSeries.cpp @@ -0,0 +1,39 @@ +/* -*- 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. + ** + **********************************************************************/ + + +#include "KDChartBaseSeries.h" + +/** + Destructor. Does nothing, only defined to have it virtual. + */ +KDChartBaseSeries::~KDChartBaseSeries() +{ + // this bloc left empty intentionally +} diff --git a/kchart/kdchart/KDChartBaseSeries.h b/kchart/kdchart/KDChartBaseSeries.h new file mode 100644 index 00000000..5b615ee6 --- /dev/null +++ b/kchart/kdchart/KDChartBaseSeries.h @@ -0,0 +1,60 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTBASESERIES_H__ +#define __KDCHARTBASESERIES_H__ + +// A single data series abstracted. +// Is included in a DataSeriesBag. +// Will be a base class for other series objects, such as DataVectorSeries, +// and DataQuerySeries. +// +// Requirements: +// - Able to handle its own parameters, colours, legend texts, etc. +// - Able to hide/show itself. +// - Almost completely abstract, so we can inherit it from other classes. +// Implement things like hide and show here tho. + +#include "KDChartDataIntern.h" + +class KDCHART_EXPORT KDChartBaseSeries +{ + public: + virtual ~KDChartBaseSeries(); + virtual uint rows() const = 0; + virtual const KDChartData& cell( uint row ) const = 0; + virtual void setCell( uint row, const KDChartData& element) = 0; + virtual void expand( uint rows ) = 0; + + // methods modelled on the TableBase methods. + virtual double maxValue( int coordinate, bool &ok ) const = 0; + virtual double minValue( int coordinate, bool &ok ) const = 0; +}; + + +#endif diff --git a/kchart/kdchart/KDChartCustomBox.cpp b/kchart/kdchart/KDChartCustomBox.cpp new file mode 100644 index 00000000..4f765f0a --- /dev/null +++ b/kchart/kdchart/KDChartCustomBox.cpp @@ -0,0 +1,495 @@ +/* -*- 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. + ** + **********************************************************************/ +#include <qpainter.h> +#include <qregion.h> +#include <qpalette.h> +#include <qpoint.h> +#include <qsimplerichtext.h> +#include <qpaintdevicemetrics.h> + +#include <KDChartCustomBox.h> +#include <KDXMLTools.h> +#include <KDFrame.h> + + + +KDChartCustomBox::~KDChartCustomBox() +{ + // Intentionally left blank for now. +} + + +void KDChartCustomBox::deepCopy( const KDChartCustomBox* source ) +{ + if( !source || this == source ) + return; + _rotation = source->_rotation; + _content.deepCopy( &source->_content); + _fontSize = source->_fontSize; + _fontScaleGlobal = source->_fontScaleGlobal; + _deltaX = source->_deltaX; + _deltaY = source->_deltaY; + _width = source->_width; + _height = source->_height; + _color = source->_color; + _paper = source->_paper; + _anchorArea = source->_anchorArea; + _anchorPos = source->_anchorPos; + _anchorAlign = source->_anchorAlign; + _dataRow = source->_dataRow; + _dataCol = source->_dataCol; + _data3rd = source->_data3rd; + _deltaAlign = source->_deltaAlign; + _deltaScaleGlobal = source->_deltaScaleGlobal; + _anchorBeingCalculated = source->_anchorBeingCalculated; + _parentAxisArea = source->_parentAxisArea; +} + +const KDChartCustomBox* KDChartCustomBox::clone() const +{ + KDChartCustomBox* newBox = new KDChartCustomBox(); + newBox->deepCopy( this ); + return newBox; +} + + +float KDChartCustomBox::trueFontSize( double areaWidthP1000, + double areaHeightP1000, + int rectHeight ) const +{ + float size; + if( 0 > _fontSize ) { + if( _fontScaleGlobal ) { + + size = _fontSize * QMIN(areaWidthP1000, areaHeightP1000) * -1.0;//(areaWidthP1000 + areaHeightP1000) / -2.0; + + } else { + + // calculate the exact size: + float targetLineSpacing = (_fontSize * rectHeight)/ -1000; + size = targetLineSpacing; + // step #1 - try to make the right font size: + QFont font( content().font() ); + font.setPointSizeFloat( size ); + QFontMetrics fm( font ); + //qDebug(QString("\nsize : ")+QString::number(size)); + //qDebug(QString("(float)rectHeight : ")+QString::number((float)rectHeight)); + //qDebug(QString("(float)fm.lineSpacing(): ")+QString::number((float)fm.lineSpacing())); + size *= targetLineSpacing / fm.lineSpacing(); + //qDebug(QString("size : ")+QString::number(size)); + // step #2 - make sure the font size is not too big: + font.setPointSizeFloat( size ); + QFontMetrics fm2( font ); + size *= targetLineSpacing / fm2.lineSpacing(); + //qDebug(QString("(float)rectHeight : ")+QString::number((float)rectHeight)); + //qDebug(QString("(float)fm.lineSpacing(): ")+QString::number((float)fm.lineSpacing())); + //qDebug(QString("size : ")+QString::number(size)); + } + } + else { +//qDebug(QString("\n_fontsize: ")+QString::number(_fontSize)); + if( _fontSize ) + size = _fontSize; + else{ + size = content().font().pointSize(); + if( -1 == size ) + size = content().font().pixelSize(); + if( -1 == size ) + size = 10; + } + } + return size; +} + + +int KDChartCustomBox::trueFontLineSpacing( double areaWidthP1000, + double areaHeightP1000, + int rectHeight ) const +{ + QFont font( content().font() ); + font.setPointSizeFloat( trueFontSize( areaWidthP1000,areaHeightP1000, rectHeight ) ); + QFontMetrics fm( font ); + return fm.lineSpacing(); +} + + +void KDChartCustomBox::getTrueShift( double areaWidthP1000, + double areaHeightP1000, + int rectHeight, + int& dX, + int& dY ) const +{ + int x, y; + if( _deltaScaleGlobal ){ + x = (0 > _deltaX) ? static_cast < int > ( -areaWidthP1000 * _deltaX ) : _deltaX; + y = (0 > _deltaY) ? static_cast < int > ( -areaHeightP1000 * _deltaY ) : _deltaY; + }else{ + int fontHeight = trueFontLineSpacing( areaWidthP1000, areaHeightP1000, rectHeight ); + //qDebug("\nfontHeight %i",fontHeight); + x = (0 > _deltaX) ? static_cast < int > ( fontHeight * _deltaX / -100.0 ) : _deltaX; + y = (0 > _deltaY) ? static_cast < int > ( fontHeight * _deltaY / -100.0 ) : _deltaY; + //qDebug("y %i",y); + } + uint deltaAlign = (KDCHART_AlignAuto == _deltaAlign) ? _anchorAlign : _deltaAlign; + if ( Qt::AlignLeft == (Qt::AlignLeft & deltaAlign) ) + dX = x; + else if ( Qt::AlignRight == (Qt::AlignRight & deltaAlign) ) + dX = -x; + else + dX = 0; // <-- so the _deltaX value becomes ineffective! + + if ( Qt::AlignTop == (Qt::AlignTop & deltaAlign) ) + dY = y; + else if ( Qt::AlignBottom == (Qt::AlignBottom & deltaAlign) ) + dY = -y; + else + dY = 0; // <-- so the _deltaY value becomes ineffective! +} + +QRect KDChartCustomBox::trueRect( QPainter * /*painter*/, QPoint /*anchor*/, double /*areaWidthP1000*/, double /*areaHeightP1000*/ ) const +{ + //temporary allow KDC_Presentation to compile + qDebug( "Sorry, not implemented yet: KDChartCustomBox::trueRect()" ); + return QRect( 1, 1, 2, 2 ); +} + +//static QPainter* pppainter=0; +//static int pdWidth =1280; +//static int pdHeight =1024; + +QRect KDChartCustomBox::trueRect( QPoint anchor, double areaWidthP1000, double areaHeightP1000 ) const +{ + int w = (0 > _width ) ? static_cast < int > ( -areaWidthP1000 * _width ) : _width; + int h = (0 > _height) ? static_cast < int > ( -areaHeightP1000 * _height ) : _height; + + //qDebug("w: %i h: %i", w,h ); + + if( _fontScaleGlobal && 0 == w && 0 == h ){ + //Now calculate the size of the box based upon the content! + QFont font( content().font() ); + if ( _fontSize ) { + font.setPointSizeFloat( + (0 > _fontSize) + ? (_fontSize * QMIN(areaWidthP1000, areaHeightP1000) * -1.0)//(areaWidthP1000 + areaHeightP1000) / -2.0 + : _fontSize ); + //qDebug("\n_fontSize * QMIN(areaWidthP1000, areaHeightP1000) %i * QMIN(%f, %f) text: %s", _fontSize , areaWidthP1000, areaHeightP1000, content().text().latin1()); + } + QString txt( content().text() ); + QString txtTest( txt.stripWhiteSpace().lower() ); +#if QT_VERSION >= 200 && QT_VERSION < 300 +// version 2.x + if( !(txtTest.left(4) == "<qt>") ) + txt.prepend( "<qt>" ); + if( !(txtTest.right(5)== "</qt>")) + txt.append( "</qt>"); +#else + if( !txtTest.startsWith("<qt>" ) ) + txt.prepend( "<qt>" ); + if( !txtTest.endsWith( "</qt>") ) + txt.append( "</qt>"); +#endif + + //qDebug("\nw: %i h: %i", w,h ); + QSimpleRichText tmpContent( txt, font ); +// tmpContent.setWidth(pdWidth); +// tmpContent.setHeight(pdHeight); +// tmpContent.adjustSize(); +// if( pppainter ){ +// tmpContent.setWidth(pppainter, 2000); +// tmpContent.adjustSize(); +// } + w = tmpContent.widthUsed(); + h = tmpContent.height(); + //qDebug("pdWidth: %i w: %i h %i",pdWidth,w,h); + //qDebug("w: %i h: %i", w,h ); + } + + //qDebug("\nw: %i h: %i", w,h ); + int x,y; + if ( Qt::AlignLeft == (Qt::AlignLeft & _anchorAlign) ) + x = 0; + else if ( Qt::AlignRight == (Qt::AlignRight & _anchorAlign) ) + x = - w + 1; + else + x = - w / 2; + + if ( Qt::AlignTop == (Qt::AlignTop & _anchorAlign) ) + y = 0; + else if ( Qt::AlignBottom == (Qt::AlignBottom & _anchorAlign) ) + y = - h + 1; + else + y = - h / 2; + int dX,dY; + getTrueShift( areaWidthP1000, areaHeightP1000, h, + dX, dY ); + //qDebug("trueRect: x %i y %i w %i h %i text: %s", anchor.x()+x+dX, anchor.y()+y+dY, w,h, content().text().latin1()); + + return QRect( anchor.x()+x+dX, anchor.y()+y+dY, w, h ); +} + + +int KDChartCustomBox::trueRectAlignX(const QRect& rect) const +{ + int ret = rect.center().x(); + if ( Qt::AlignLeft == (Qt::AlignLeft & _anchorAlign) ) + ret -= rect.width(); + else if ( Qt::AlignRight == (Qt::AlignRight & _anchorAlign) ) + ret += rect.width(); + return ret; +} +int KDChartCustomBox::trueRectAlignY(const QRect& rect) const +{ + int ret = rect.center().y(); + if ( Qt::AlignTop == (Qt::AlignTop & _anchorAlign) ) + ret -= rect.height(); + else if ( Qt::AlignBottom == (Qt::AlignBottom & _anchorAlign) ) + ret += rect.height(); + return ret; +} + +void KDChartCustomBox::paint( QPainter* painter, + QPoint anchor, + double areaWidthP1000, + double areaHeightP1000, + const KDFrame* frame, + const QRect& frameRect, + const QColor * color, + const QBrush * paper ) const +{ + painter->save(); + int rotDX = 0; + int rotDY = 0; +/* +//pppainter=painter; +const QPaintDeviceMetrics metrics(painter->device()); +pdWidth = metrics.width(); +const int aWidthP1000 = metrics.width() /1000; +const int aHeightP1000 = metrics.height()/1000; +//pdHeight = metrics.height(); +*/ + + QRect myRect( trueRect( anchor, areaWidthP1000, areaHeightP1000 ) ); + + + /* +QPaintDeviceMetrics metrics(painter->device()); +int pdWidth = metrics.width(); +int dpiY = metrics.logicalDpiY(); +int dpiX = metrics.logicalDpiX(); +qDebug("dpiY: %i dpiX: %i",dpiY,dpiX); +qDebug("pdWidth: %i box myRect w: %i h %i",pdWidth,myRect.width(),myRect.height()); + */ +//myRect.setSize(myRect.size()*6); + + + QRect myFrameRect( frameRect ); + if ( myRect.isValid() ) { +//qDebug("box myRect center: x %i y %i",myRect.center().x(),myRect.center().y()); + if( _rotation ){ + getTrueShift( areaWidthP1000, areaHeightP1000, myRect.height(), + rotDX, rotDY ); + myRect.moveBy( -rotDX, -rotDY ); + if( frame ) + myFrameRect.moveBy( -rotDX, -rotDY ); +//qDebug("\nrotDelta: x %i y %i",rotDX,rotDY); +//qDebug("\nbox myRect center: x %i y %i",myRect.center().x(),myRect.center().y()); + myRect.moveCenter( QPoint( anchor.x() - trueRectAlignX(myRect), + anchor.y() - trueRectAlignY(myRect) ) ); + if( frame ) + myFrameRect.moveCenter( QPoint( anchor.x() - trueRectAlignX(myFrameRect), + anchor.y() - trueRectAlignY(myFrameRect) ) ); +//qDebug("box myRect center: x %i y %i",myRect.center().x(),myRect.center().y()); + painter->translate( anchor.x(), anchor.y() ); + painter->rotate( _rotation ); + painter->translate( rotDX, rotDY ); + } + if( frame ) + frame->paint( painter, KDFrame::PaintAll, myFrameRect ); + if ( _fontSize ) { + QFont font( content().font() ); + + float trueSize = trueFontSize(areaWidthP1000,areaHeightP1000, myRect.height() ); + font.setPointSizeFloat( trueSize ); + //adjust the area height related to the font size + myRect.setHeight( (int)(trueSize )+ (int)(trueSize*0.5)); + const KDChartTextPiece tmpTextPiece( painter, content().text(), font ); + + tmpTextPiece.draw( painter, myRect.x(), myRect.y(), myRect, + color ? *color : _color, + paper ? paper : &_paper ); + }else{ + + content().draw( painter, myRect.x(), myRect.y(), myRect, + color ? *color : _color, + paper ? paper : &_paper ); + } + } + painter->restore(); +} + + +void KDChartCustomBox::createCustomBoxNode( QDomDocument& document, + QDomNode& parent, + const QString& elementName, + const KDChartCustomBox* custombox ) +{ + QDomElement customBoxElement = document.createElement( elementName ); + parent.appendChild( customBoxElement ); + KDXML::createIntNode( document, customBoxElement, "Rotation", custombox->_rotation ); + KDXML::createStringNode( document, customBoxElement, "ContentText", + custombox->_content.text() ); + KDXML::createFontNode( document, customBoxElement, "ContentFont", + custombox->_content.font() ); + KDXML::createIntNode( document, customBoxElement, "FontSize", custombox->_fontSize ); + KDXML::createBoolNode( document, customBoxElement, "FontScaleGlobal", + custombox->_fontScaleGlobal ); + KDXML::createIntNode( document, customBoxElement, "DeltaX", custombox->_deltaX ); + KDXML::createIntNode( document, customBoxElement, "DeltaY", custombox->_deltaY ); + KDXML::createIntNode( document, customBoxElement, "Width", custombox->_width ); + KDXML::createIntNode( document, customBoxElement, "Height", custombox->_height ); + KDXML::createColorNode( document, customBoxElement, "Color", custombox->_color ); + KDXML::createBrushNode( document, customBoxElement, "Paper", custombox->_paper ); + KDXML::createIntNode( document, customBoxElement, "AnchorArea", + custombox->_anchorArea ); + KDXML::createStringNode( document, customBoxElement, "AnchorPos", + KDChartEnums::positionFlagToString( custombox->_anchorPos ) ); + KDXML::createIntNode( document, customBoxElement, "AnchorAlign", + custombox->_anchorAlign ); + KDXML::createIntNode( document, customBoxElement, "DataRow", + custombox->_dataRow ); + KDXML::createIntNode( document, customBoxElement, "DataCol", + custombox->_dataCol ); + KDXML::createIntNode( document, customBoxElement, "Data3rd", + custombox->_data3rd ); + KDXML::createIntNode( document, customBoxElement, "DeltaAlign", + custombox->_deltaAlign ); + KDXML::createBoolNode( document, customBoxElement, "DeltaScaleGlobal", + custombox->_deltaScaleGlobal ); + KDXML::createIntNode( document, customBoxElement, "ParentAxisArea", + custombox->_parentAxisArea ); +} + + +bool KDChartCustomBox::readCustomBoxNode( const QDomElement& element, + KDChartCustomBox& custombox ) +{ + bool ok = true; + QString tempContentText; + QFont tempContentFont; + int tempRotation = 0; // must be initialized: new parameter, not present in old versions of KDChart + int tempDeltaAlign = KDCHART_AlignAuto; // must be initialized too: new parameter + bool tempDeltaScaleGlobal = true; // must be initialized too: new parameter + int tempFontSize, tempDeltaX, tempDeltaY, + tempWidth, tempHeight, tempAnchorArea, tempParentAxisArea, tempAnchorAlign, + tempDataRow, tempDataCol, tempData3rd; + bool tempFontScaleGlobal; + QColor tempColor; + QBrush tempPaper; + KDChartEnums::PositionFlag tempAnchorPos = KDChartEnums::PosTopLeft; + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Rotation" ) { + ok = ok & KDXML::readIntNode( element, tempRotation ); + } else if( tagName == "ContentText" ) { + ok = ok & KDXML::readStringNode( element, tempContentText ); + } else if( tagName == "ContentFont" ) { + ok = ok & KDXML::readFontNode( element, tempContentFont ); + } else if( tagName == "FontSize" ) { + ok = ok & KDXML::readIntNode( element, tempFontSize ); + } else if( tagName == "FontScaleGlobal" ) { + ok = ok & KDXML::readBoolNode( element, tempFontScaleGlobal ); + } else if( tagName == "DeltaX" ) { + ok = ok & KDXML::readIntNode( element, tempDeltaX ); + } else if( tagName == "DeltaY" ) { + ok = ok & KDXML::readIntNode( element, tempDeltaY ); + } else if( tagName == "Width" ) { + ok = ok & KDXML::readIntNode( element, tempWidth ); + } else if( tagName == "Height" ) { + ok = ok & KDXML::readIntNode( element, tempHeight ); + } else if( tagName == "Color" ) { + ok = ok & KDXML::readColorNode( element, tempColor ); + } else if( tagName == "Paper" ) { + ok = ok & KDXML::readBrushNode( element, tempPaper ); + } else if( tagName == "AnchorArea" ) { + ok = ok & KDXML::readIntNode( element, tempAnchorArea ); + } else if( tagName == "AnchorPos" ) { + QString value; + ok = ok & KDXML::readStringNode( element, value ); + tempAnchorPos = KDChartEnums::stringToPositionFlag( value ); + } else if( tagName == "AnchorAlign" ) { + ok = ok & KDXML::readIntNode( element, tempAnchorAlign ); + } else if( tagName == "DataRow" ) { + ok = ok & KDXML::readIntNode( element, tempDataRow ); + } else if( tagName == "DataCol" ) { + ok = ok & KDXML::readIntNode( element, tempDataCol ); + } else if( tagName == "Data3rd" ) { + ok = ok & KDXML::readIntNode( element, tempData3rd ); + } else if( tagName == "DeltaAlign" ) { + ok = ok & KDXML::readIntNode( element, tempDeltaAlign ); + } else if( tagName == "DeltaScaleGlobal" ) { + ok = ok & KDXML::readBoolNode( element, tempDeltaScaleGlobal ); + } else if ( tagName == "ParentAxisArea" ) { + ok = ok & KDXML::readIntNode( element, tempParentAxisArea ); + } else { + qDebug( "Unknown tag in custom box" ); + } + } + node = node.nextSibling(); + } + + if( ok ) { + const KDChartTextPiece piece( 0, tempContentText, tempContentFont ); + custombox._content.deepCopy( &piece ); + custombox._rotation = tempRotation; + custombox._fontSize = tempFontSize; + custombox._fontScaleGlobal = tempFontScaleGlobal; + custombox._deltaX = tempDeltaX; + custombox._deltaY = tempDeltaY; + custombox._width = tempWidth; + custombox._height = tempHeight; + custombox._color = tempColor; + custombox._paper = tempPaper; + custombox._anchorArea = tempAnchorArea; + custombox._anchorPos = tempAnchorPos; + custombox._anchorAlign = tempAnchorAlign; + custombox._dataRow = tempDataRow; + custombox._dataCol = tempDataCol; + custombox._data3rd = tempData3rd; + custombox._deltaAlign = tempDeltaAlign; + custombox._deltaScaleGlobal = tempDeltaScaleGlobal; + custombox._parentAxisArea = tempParentAxisArea; + } + + return ok; +} + +#include "KDChartCustomBox.moc" diff --git a/kchart/kdchart/KDChartCustomBox.h b/kchart/kdchart/KDChartCustomBox.h new file mode 100644 index 00000000..f4f86944 --- /dev/null +++ b/kchart/kdchart/KDChartCustomBox.h @@ -0,0 +1,1043 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTCUSTOMBOX_H__ +#define __KDCHARTCUSTOMBOX_H__ + +#include <qobject.h> +#include <qtextstream.h> +#include <qfont.h> +#include <qdom.h> +#include "KDChartGlobal.h" +#include "KDChartEnums.h" +#include "KDChartTextPiece.h" + + +class KDFrame; + +// needed because there was no AlignAuto before Qt 3.0 +#define KDCHART_AlignAuto 0 + + +/** \file KDChartCustomBox.h + \brief Definition of a class for specifying and drawing custom boxes. + */ + +/** + Class for specifying and drawing custom boxes. + */ +class KDCHART_EXPORT KDChartCustomBox :public QObject +{ + Q_OBJECT + friend class KDChartPainter; + friend class KDChartParams; + +public: + + /** + Constructor. + Set default values. + */ + KDChartCustomBox() : + _rotation( 0 ), + _fontSize( -10 ), + _fontScaleGlobal( true ), + _deltaX( 0 ), + _deltaY( 0 ), + _width( 0 ), + _height( 0 ), + _color( Qt::black ), + _paper( Qt::NoBrush ), + _anchorArea( KDChartEnums::AreaInnermost ), + _anchorPos( KDChartEnums::PosTopLeft ), + _anchorAlign( Qt::AlignTop + Qt::AlignLeft ), + _dataRow( 0 ), + _dataCol( 0 ), + _data3rd( 0 ), + _deltaAlign( KDCHART_AlignAuto ), + _deltaScaleGlobal( true ), + _anchorBeingCalculated( false ), + _parentAxisArea( -1 ) + { + const KDChartTextPiece piece( 0, "", QFont( "helvetica", 8, QFont::Normal, false ) ); + _content.deepCopy( &piece ); + } + + /** + Constructor. + Set content and font size but no position/size parameters. + + \note If \c fontScaleGlobal is set to true it is not necessary to + specify the size of the box: if both \c width and \c height are zero + and \c fontScaleGlobal is true the size of the box will be calculated + automatically based upon the content size. In this case the content + size is interpreted as rich text string - even if the text is NOT + framed by <tt><qt></tt> and <tt></qt></tt>. + + \param content The string or rich text string to be written into the box. + \param fontSize The size of the font to be used, see explanation above. + \param fontScaleGlobal If true the font size will be calculated based + upon the the size of the drawing area, otherwise it will be calculated + based upon the height of the box. + */ + KDChartCustomBox( const KDChartTextPiece & content, + int fontSize, + bool fontScaleGlobal = true ) : + _rotation( 0 ), + _fontSize( fontSize ), + _fontScaleGlobal( fontScaleGlobal ), + _deltaX( 0 ), + _deltaY( 0 ), + _width( 0 ), + _height( 0 ), + _color( Qt::black ), + _paper( Qt::NoBrush ), + _anchorArea( KDChartEnums::AreaInnermost ), + _anchorPos( KDChartEnums::PosTopLeft ), + _anchorAlign( Qt::AlignTop + Qt::AlignLeft ), + _dataRow( 0 ), + _dataCol( 0 ), + _data3rd( 0 ), + _deltaAlign( KDCHART_AlignAuto ), + _deltaScaleGlobal( true ), + _anchorBeingCalculated( false ), + _parentAxisArea( -1 ) + { + _content.deepCopy( &content ); + } + + + /** + Constructor. + Set content and position/size parameters and the color and brush to be used. + + \note If \c fontScaleGlobal is set to true it is not necessary to + specify the size of the box: if both \c width and \c height are zero + and \c fontScaleGlobal is true the size of the box will be calculated + automatically based upon the content size. In this case the content + size is interpreted as rich text string - even if the text is NOT + framed by <tt><qt></tt> and <tt></qt></tt>. + + \param content The text piece to be displayed. + \param fontSize If \c fontSize value is greater 0, the value is taken as exact size, + if \c fontSize is less than 0 it is interpreted as being a per-mille value + of the size of the drawing area (or of the height of the box in case + \c fontScaleGlobal is set to false, resp.). + Normally the actual font size is calculated dynamically in methode paint. + <b>However</b> if fontSize is zero no calculating will take place but the + size of the content font is used. + \param fontScaleGlobal If true the font size will be calculated based + upon the the size of the drawing area, otherwise it will be calculated + based upon the height of the box. + \param deltaX The X distance between the box and its anchor. + <b>Note: </b> If greater 0, the value is taken as exact offset, + if less than 0, it is interpreted as being a per-mille value of the + size of the drawing area (or as percent value of the actual font size + (as returned by QFontMetrics::lineSpacing() ) if deltaScaleGlobal is FALSE, resp.). + Actual drawing position/size is calculated dynamically in methode trueRect. + \param deltaY The Y distance between the box and its anchor. + \param width The width of the box. + \param height The height of the box. + \param color The text color. + \param paper The brush to be used for the background. + \param area The area to which the box is to be aligned. + \param position The anchor position. This is the edge (or + the corner, resp.) of the area to which + the box is to be aligned. + \param align The way how the box is to be aligned to its anchor. + \param dataRow The row number of the KDChart data region that is to be used + as anchor area. This parameter is ignored if \c area is not \c AreaChartDataRegion. + \param dataCol The column number of the KDChart data region that is to be used + as anchor area. This parameter is ignored if \c area is not \c AreaChartDataRegion. + \param data3rd The third dimensions number of the KDChart data region that is to be used + as anchor area. This parameter is ignored if \c area is not \c AreaChartDataRegion + or if there is no 3-dimensional data structure. + \param deltaAlign The way how \c deltaX and \deltaY affect the position of the box. + Leave this parameter to its default value KDCHART_AlignAuto to have the delta values + used according to the box's main \c align settings, otherwise specify your own + alignment settings: e.g. right means there will be a gap between the right side of + the box and its anchor point - if the main \c align parameter is set to right too + the anchor point will to be outside of the box / if \c align is set to left + (but the \c deltaAlign to right) the anchor point will be inside the box. + Possible values for \c deltaAlign are: + \li \c KDCHART_AlignAuto + \li \c Qt::AlignLeft | Qt::AlignTop + \li \c Qt::AlignLeft | Qt::AlignBottom + \li \c Qt::AlignRight | Qt::AlignTop + \li \c Qt::AlignRight | Qt::AlignBottom + Using AlignVCenter or AlignHCenter or AlignCenter does not make sense here: + center delta alignment will cause KDChart to ignore the respective delta + settings: deltaX or deltaY or both will become ineffective. + \param deltaScaleGlobal If true the actual delta X and delta Y values will + be calculated by \c deltaX and \c deltaY based upon the size of the + drawing area, otherwise they will be calculated based upon the actual + font size. + */ + KDChartCustomBox( const KDChartTextPiece & content, + int fontSize, + bool fontScaleGlobal, + int deltaX, + int deltaY, + int width, + int height, + const QColor & color = QColor( Qt::black ), + const QBrush & paper = QBrush( Qt::NoBrush ), + uint area = KDChartEnums::AreaInnermost, + KDChartEnums::PositionFlag position = KDChartEnums::PosTopLeft, + uint align = Qt::AlignTop + Qt::AlignLeft, + uint dataRow = 0, + uint dataCol = 0, + uint data3rd = 0, + uint deltaAlign = KDCHART_AlignAuto, + bool deltaScaleGlobal = true, + int parentAxis = -1 ) + : _rotation( 0 ), + _fontSize( fontSize ), + _fontScaleGlobal( fontScaleGlobal ), + _deltaX( deltaX ), + _deltaY( deltaY ), + _width( width ), + _height( height ), + _color( color ), + _paper( paper ), + _anchorArea( area ), + _anchorPos( position ), + _anchorAlign( align ), + _dataRow( dataRow ), + _dataCol( dataCol ), + _data3rd( data3rd ), + _deltaAlign( deltaAlign ), + _deltaScaleGlobal( deltaScaleGlobal ), + _anchorBeingCalculated( false ), + _parentAxisArea( parentAxis ) + { + _content.deepCopy( &content ); + } + + + /** + Constructor. + Use this special constructor to specify a <b>rotated</b> box, reference + point of the rotation is the anchor specified by the \c area and the + \c position parameters. + + \note If \c fontScaleGlobal is set to true it is not necessary to + specify the size of the box: if both \c width and \c height are zero + and \c fontScaleGlobal is true the size of the box will be calculated + automatically based upon the content size. In this case the content + size is interpreted as rich text string - even if the text is NOT + framed by <tt><qt></tt> and <tt></qt></tt>. + + \param rotation The box's rotation angle in degrees (0 .. 360). + \param content The text piece to be displayed. + \param fontSize If \c fontSize value is greater 0, the value is taken as exact size, + if \c fontSize is less than 0 it is interpreted as being a per-mille value + of the size of the drawing area (or of the height of the box in case + \c fontScaleGlobal is set to false, resp.). + Normally the actual font size is calculated dynamically in methode paint. + <b>However</b> if fontSize is zero no calculating will take place but the + size of the content font is used. + \param fontScaleGlobal If true the font size will be calculated based + upon the the size of the drawing area, otherwise it will be calculated + based upon the height of the box. + \param deltaX The X distance between the box and its anchor. + <b>Note: </b> If greater 0, the value is taken as exact offset, + if less than 0, it is interpreted as being a per-mille value of the + width of the drawing area (or as percent value of the actual font size + (as returned by QFontMetrics::lineSpacing() ) if deltaScaleGlobal is FALSE, resp.). + Actual drawing position/size is calculated dynamically in method trueRect. + \param deltaY The Y distance between the box and its anchor. + <b>Note: </b> If greater 0, the value is taken as exact offset, + if less than 0, it is interpreted as being a per-mille value of the + height of the drawing area (or as percent value of the actual font size + (as returned by QFontMetrics::lineSpacing() ) if deltaScaleGlobal is FALSE, resp.). + Actual drawing position/size is calculated dynamically in method trueRect. + \param width The width of the box. + \param height The height of the box. + \param color The text color. + \param paper The brush to be used for the background. + \param area The area to which the box is to be aligned. + \param position The anchor position. This is the edge (or + the corner, resp.) of the area to which the box is to be aligned + and it is also used as the reference point of the rotation. + \param align The way how the box is to be aligned to its anchor. + \param dataRow The row number of the KDChart data region that is to be used + as anchor area. This parameter is ignored if \c area is not \c AreaChartDataRegion. + \param dataCol The column number of the KDChart data region that is to be used + as anchor area. This parameter is ignored if \c area is not \c AreaChartDataRegion. + \param data3rd The third dimensions number of the KDChart data region that is to be used + as anchor area. This parameter is ignored if \c area is not \c AreaChartDataRegion + or if there is no 3-dimensional data structure. + \param deltaAlign The way how \c deltaX and \deltaY affect the position of the box. + Leave this parameter to its default value KDCHART_AlignAuto to have the delta values + used according to the box's main \c align settings, otherwise specify your own + alignment settings: e.g. Qt::AlignRight means the box will be moved to the left + (by the amount calculated using the \c deltaX value), so there will be a gap + between the right side of the box and its anchor point IF the main \c align flag + is set to Qt::AlignRight too, so the anchor point will to be outside of the + box then. However if the main \c align flag is set to Qt::AlignLeft the anchor + point will be inside the box. + Possible values for \c deltaAlign are: + \li \c KDCHART_AlignAuto + \li \c Qt::AlignLeft | Qt::AlignTop + \li \c Qt::AlignLeft | Qt::AlignBottom + \li \c Qt::AlignRight | Qt::AlignTop + \li \c Qt::AlignRight | Qt::AlignBottom + Using AlignVCenter or AlignHCenter or AlignCenter does not make sense here: + center delta alignment will cause KDChart to ignore the respective delta + settings: deltaX or deltaY or both will become ineffective. + \note Moving of the box due to \c deltaAlign settings is applied after + the box is rotated: e.g. this means a gap specified by \c deltaAlign = Qt::AlignTop + gap will actually result in a left gap if the box is rotated by 90 degrees. + \param deltaScaleGlobal If true the actual delta X and delta Y values will + be calculated by \c deltaX and \c deltaY based upon the size of the + drawing area, otherwise they will be calculated based upon the actual + font size. + */ + KDChartCustomBox( int rotation, + const KDChartTextPiece & content, + int fontSize, + bool fontScaleGlobal, + int deltaX, + int deltaY, + int width, + int height, + const QColor & color = QColor( Qt::black ), + const QBrush & paper = QBrush( Qt::NoBrush ), + uint area = KDChartEnums::AreaInnermost, + KDChartEnums::PositionFlag position = KDChartEnums::PosTopLeft, + uint align = Qt::AlignTop + Qt::AlignLeft, + uint dataRow = 0, + uint dataCol = 0, + uint data3rd = 0, + uint deltaAlign = KDCHART_AlignAuto, + bool deltaScaleGlobal = true, + int parentAxis = -1 ) + : _rotation( rotation ), + _fontSize( fontSize ), + _fontScaleGlobal( fontScaleGlobal ), + _deltaX( deltaX ), + _deltaY( deltaY ), + _width( width ), + _height( height ), + _color( color ), + _paper( paper ), + _anchorArea( area ), + _anchorPos( position ), + _anchorAlign( align ), + _dataRow( dataRow ), + _dataCol( dataCol ), + _data3rd( data3rd ), + _deltaAlign( deltaAlign ), + _deltaScaleGlobal( deltaScaleGlobal ), + _anchorBeingCalculated( false ), + _parentAxisArea( parentAxis ) + { + _content.deepCopy( &content ); + } + + + /** + Creates a DOM element node that represents a custom box for use + in a DOM document. + + \param document the DOM document to which the node will belong + \param parent the parent node to which the new node will be appended + \param elementName the name of the new node + \param custombox the custom box to be represented + */ + static void createCustomBoxNode( QDomDocument& document, + QDomNode& parent, + const QString& elementName, + const KDChartCustomBox* custombox ); + + /** + Reads data from a DOM element node that represents a custom box + object and fills a KDChartCustomBox object with the data. + + \param element the DOM element to read from + \param settings the custom box object to read the data into + */ + static bool readCustomBoxNode( const QDomElement& element, + KDChartCustomBox& custombox ); + +public slots: // PENDING(blackie) merge slots sections. + + float trueFontSize( double areaWidthP1000, + double areaHeightP1000, + int rectHeight ) const; + int trueFontLineSpacing( double areaWidthP1000, + double areaHeightP1000, + int rectHeight ) const; + int trueRectAlignX(const QRect& rect) const; + int trueRectAlignY(const QRect& rect) const; + void getTrueShift( double areaWidthP1000, + double areaHeightP1000, + int rectHeight, + int& dX, + int& dY )const; + + + /** + Return the actual rectangle which to draw box into. + + \note When ever possible, use this method, instead of the other trueRect(), that + has no \c QPainter parameter. Passing a QPainter will give you more exact results. + + \param painter The QPainter to be used. + \param anchor The anchor point which the box is to be aligned to. + This can be any point within the painter drawing area but you + will probably compute a point using anchorArea(), anchorPosition(), anchorAlign() + (and dataRow(), dataCol(), data3rd() when dealing with KDChart data regions, resp.) + \param areaWidthP1000 The thousands part of the logical width + of the area to be used for drawing. + \param areaHeightP1000 The thousands part of the logical height + of the area to be used for drawing. + */ + QRect trueRect( QPainter * painter, + QPoint anchor, double areaWidthP1000, double areaHeightP1000 ) const ; + + /** + Return the actual rectangle which to draw box into. + + \param anchor The anchor point which the box is to be aligned to. + This can be any point within the painter drawing area but you + will probably compute a point using anchorArea(), anchorPosition(), anchorAlign() + (and dataRow(), dataCol(), data3rd() when dealing with KDChart data regions, resp.) + \param areaWidthP1000 The thousands part of the logical width + of the area to be used for drawing. + \param areaHeightP1000 The thousands part of the logical height + of the area to be used for drawing. + */ + virtual QRect trueRect( QPoint anchor, + double areaWidthP1000, + double areaHeightP1000 ) const ; + + /** + Paints the box. + + \param painter The QPainter to be used for drawing. + \param anchor The anchor point which the box is to be aligned to. + This can be any point within the painter drawing area but you + will probably compute a point using anchorArea(), anchorPosition(), anchorAlign() + (and dataRow(), dataCol(), data3rd() when dealing with KDChart data regions, resp.) + \param areaWidthP1000 The thousands part of the drawing area width. + \param areaHeightP1000 The thousands part of the drawing area height. + \param color The text color to be used. + \param paper The brush to be used for the background. + \param rect The rectangle to be drawn into. If empty the rectangle will be + calculated dynamically based upon the \c x, \c y, \c width, \c height values + (that were set via Constructor or via setPosAndSize) and based upon the + logical height (or width, resp.) of the painters drawing area. + */ + virtual void paint( QPainter* painter, + QPoint anchor, + double areaWidthP1000, + double areaHeightP1000, + const KDFrame* frame = 0, + const QRect& frameRect = QRect(), + const QColor * color = 0, + const QBrush * paper = 0 ) const ; + + /** + Specifies the rotation angle of the box in degrees (0..360). + */ + void setRotation( int rotation ) + { + _rotation = rotation; + } + + /** + Specifies the text piece content to be drawn. + */ + void setContent( const KDChartTextPiece & content ) + { + _content.deepCopy( &content ); + } + + /** + Specifies the font size to be used. + + \note If \c fontScaleGlobal is set to true it is not necessary to + specify the size of the box: if both \c width and \c height are zero + and \c fontScaleGlobal is true the size of the box will be calculated + automatically based upon the content size. In this case the content + size is interpreted as rich text string - even if the text is NOT + framed by <tt><qt></tt> and <tt></qt></tt>. + + \param fontSize If \c fontSize value is greater 0, the value is taken as exact size, + if \c fontSize is less than 0 it is interpreted as being a per-mille value + of the size of the drawing area (or of the height of the box in case + \c fontScaleGlobal is set to false, resp.). + Normally the actual font size is calculated dynamically in methode paint. + <b>However</b> if fontSize is zero no calculating will take place but the + size of the content font is used. + \param fontScaleGlobal If true the font size will be calculated based + upon the the size of the drawing area, otherwise it will be calculated + based upon the height of the box. + */ + void setFontSize( int fontSize, bool fontScaleGlobal ) + { + _fontSize = fontSize; + _fontScaleGlobal = fontScaleGlobal; + } + + /** + Specifies the area to which the box is to be aligned. + */ + void setAnchorArea( uint area ) + { + _anchorArea = area; + } + + /** + Specifies the anchor position. + This is the edge (or the corner, resp.) of the area + to which the box is to be aligned. + */ + void setAnchorPosition( KDChartEnums::PositionFlag position ) + { + // Note if you change the parameters here, then you must also change them in wrappers/KDChartCustomBoxWrapper.h + _anchorPos = position; + } + + /** + Specifies the way how the box is to be aligned to its anchor. + */ + void setAnchorAlign( uint align ) + { + _anchorAlign = align; + } + + /** + Specifies the row number of the KDChart data region that is to be used + as anchor area. This value is ignored if anchorArea is not \c AreaChartDataRegion. + */ + void setDataRow( uint dataRow ) + { + _dataRow = dataRow; + } + + /** + Specifies the column number of the KDChart data region that is to be used + as anchor area. This value is ignored if anchorArea is not \c AreaChartDataRegion. + */ + void setDataCol( uint dataCol ) + { + _dataCol = dataCol; + } + + /** + Specifies the third dimensions number of the KDChart data region that is to be used + as anchor area. This value is ignored if anchorArea is not \c AreaChartDataRegion + or if there is no 3-dimensional data structure. + */ + void setData3rd( uint data3rd ) + { + _data3rd = data3rd; + } + + /** + Specifies the distance between the box and the anchor point and + specifies the size of the box. + + \param deltaX The X distance between the box and its anchor. + <b>Note: </b> If greater 0, the value is taken as exact offset, + if less than 0, it is interpreted as being a per-mille value of the + size of the drawing area (or as percent value of the actual font size + (as returned by QFontMetrics::lineSpacing() ) if deltaScaleGlobal is FALSE, resp.). + Actual drawing position/size is calculated dynamically in methode trueRect. + \param deltaY The Y distance between the box and its anchor. + \param width The width of the drawing region. + \param height The height of the drawing region. + \param deltaAlign the way how the values specified for deltaX and/or deltaY + affect the position of the box. + \param deltaScaleGlobal If true the actual delta X and delta Y values will + be calculated by \c deltaX and \c deltaY based upon the size of the + drawing area, otherwise they will be calculated based upon the actual + font size. + + \sa setDistance, setSize, setDeltaAlign, setDeltaScale + */ + void setDistanceAndSize( int deltaX, + int deltaY, + int width, + int height, + uint deltaAlign = KDCHART_AlignAuto, + bool deltaScaleGlobal = true ) + { + _deltaX = deltaX; + _deltaY = deltaY; + _width = width; + _height = height; + _deltaAlign = deltaAlign; + _deltaScaleGlobal = deltaScaleGlobal; + } + + /** + Specifies the distance between the box and the anchor point. + + \note If \c fontScaleGlobal is set to true it is not necessary to + specify the size of the box: if both \c width and \c height are zero + and \c fontScaleGlobal is true the size of the box will be calculated + automatically based upon the content size. In this case the content + size is interpreted as rich text string - even if the text is NOT + framed by <tt><qt></tt> and <tt></qt></tt>. + + \param deltaX The X distance between the box and its anchor. + <b>Note: </b> If greater 0, the value is taken as exact offset, + if less than 0, it is interpreted as being a per-mille value of the + size of the drawing area (or as percent value of the actual font size + (as returned by QFontMetrics::lineSpacing() ) if deltaScaleGlobal is FALSE, resp.). + Actual drawing position/size is calculated dynamically in methode trueRect. + \param deltaY The Y distance between the box and its anchor. + \param align the way how the values specified for deltaX and/or deltaY + affect the position of the box. + \param deltaScaleGlobal If true the actual delta X and delta Y values will + be calculated by \c deltaX and \c deltaY based upon the size of the + drawing area, otherwise they will be calculated based upon the actual + font size. + + \sa setDistanceAndSize, setSize, setDeltaAlign, setDeltaScale + */ + void setDistance( int deltaX, + int deltaY, + uint align = KDCHART_AlignAuto, + bool deltaScaleGlobal = true ) + { + _deltaX = deltaX; + _deltaY = deltaY; + _deltaAlign = align; + _deltaScaleGlobal = deltaScaleGlobal; + } + + /** + Specifies the way how the values specified for deltaX and/or deltaY + affect the position of the box. + + Set this to KDHART_KDCHART_AlignAuto to have the delta values + used according to the box's main \c align settings, otherwise specify your own + alignment settings: e.g. right means there will be a gap between the right side of + the box and its anchor point - if the main \c align parameter is set to right too + the anchor point will to be outside of the box / if \c align is set to left + (but the \c deltaAlign to right) the anchor point will be inside the box. + Possible values for \c deltaAlign are: + \li \c KDCHART_AlignAuto + \li \c Qt::AlignLeft | Qt::AlignTop + \li \c Qt::AlignLeft | Qt::AlignBottom + \li \c Qt::AlignRight | Qt::AlignTop + \li \c Qt::AlignRight | Qt::AlignBottom + Using AlignVCenter or AlignHCenter or AlignCenter does not make sense here: + center delta alignment will cause KDChart to ignore the respective delta + settings: deltaX or deltaY or both will become ineffective. + \note Moving of the box due to \c deltaAlign settings is applied after + the box is rotated: e.g. this means a gap specified by \c deltaAlign = Qt::AlignTop + gap will actually result in a left gap if the box is rotated by 90 degrees. + \param deltaScaleGlobal If true the actual delta X and delta Y values will + be calculated by \c deltaX and \c deltaY based upon the size of the + drawing area, otherwise they will be calculated based upon the actual + font size. + + \sa setDeltaScale, setDistance, setDistanceAndSize, deltaAlign + */ + void setDeltaAlign( uint align, + bool deltaScaleGlobal = true ) + { + _deltaAlign = align; + _deltaScaleGlobal = deltaScaleGlobal; + } + + /** + Specifies the way how the distance between the box and its anchor + will be calculated. + + \param deltaScaleGlobal If true the actual delta X and delta Y values will + be calculated by \c deltaX and \c deltaY based upon the size of the + drawing area, otherwise they will be calculated based upon the actual + font size. + + \sa setDeltaAlign, setDistance, setDistanceAndSize, deltaAlign + */ + void setDeltaScale( bool deltaScaleGlobal ) + { + _deltaScaleGlobal = deltaScaleGlobal; + } + + /** + Specifies the size of the box. + + \note If \c fontScaleGlobal is set to true it is not necessary to + specify the size of the box: if both \c width and \c height are zero + and \c fontScaleGlobal is true the size of the box will be calculated + automatically based upon the content size. In this case the content + size is interpreted as rich text string - even if the text is NOT + framed by <tt><qt></tt> and <tt></qt></tt>. + + \param width The width of the box. + <b>Note:</b> If greater 0, the value is taken as exact offset, + if less than 0, it is interpreted as being a per-mille value + of the logical height (or width, resp.) of the area to be used for drawing. + Actual drawing position/size is calculated dynamically in methode trueRect. + \param height The height of the box. + + \sa setDistance, setDistanceAndSize, setDeltaAlign + */ + void setSize( int width, int height ) + { + _width = width; + _height = height; + } + + /** + Specifies the text color to be used. + + \param color The text color. + */ + void setColor( QColor color ) + { + _color = color; + } + + /** + Specifies the brush to be used for the background. + + \param paper The brush to be used for the background. + */ + void setPaper( const QBrush & paper ) + { + _paper = paper; + } + + /** + Returns the rotation angle of the box in degrees (0..360). + */ + int rotation() const + { + return _rotation; + } + + /** + Returns the text piece content that is to be drawn. + */ + const KDChartTextPiece & content() const + { + return _content; + } + + /** + Returns the font size to be used. + + \note If fontSize value is greater 0, the value is taken as exact size, + if less than 0, it is interpreted as being a per-mille value of the logical + height (or width, resp.) of the area to be used for drawing. + Normally the actual font size is calculated dynamically in methode paint. + <b>However</b> if fontSize is zero no calculating will take place but the + size of the content font is used. + */ + int fontSize() const + { + return _fontSize; + } + + /** + Returns the way how the font size is calculated <b>if</b> fontSize() is negative. + + If true the font size will be calculated based upon the the size of the drawing + area, otherwise it will be calculated based upon the height of the box. + */ + bool fontScaleGlobal() const + { + return _fontScaleGlobal; + } + + /** + Returns the area to which the box is to be aligned. + */ + uint anchorArea() const + { + return _anchorArea; + } + + /** + Returns the anchor position. + This is the edge (or the corner, resp.) of the area + to which the box is to be aligned. + */ + KDChartEnums::PositionFlag anchorPosition() const + { + return _anchorPos; + } + + /** + Returns the way how the box is to be aligned to its anchor. + */ + uint anchorAlign() const + { + return _anchorAlign; + } + + /** + Returns the row number of the KDChart data region that is to be used + as anchor area. You should use this if anchorArea is \c AreaChartDataRegion + to find out the data region which the box is to be aligned to. + */ + uint dataRow() const + { + return _dataRow; + } + + /** + Returns the column number of the KDChart data region that is to be used + as anchor area. You should use this if anchorArea is \c AreaChartDataRegion + to find out the data region which the box is to be aligned to. + */ + uint dataCol() const + { + return _dataCol; + } + + /** + Returns the third dimensions number of the KDChart data region that is to be used + as anchor area. You should use this if anchorArea is \c AreaChartDataRegion + and if there is a 3-dimensional data structure + to find out the data region which the box is to be aligned to. + */ + uint data3rd() const + { + return _data3rd; + } + + /** + Returns the X distance between the box and its anchor. + <b>Note: </b> If greater 0, the value is taken as exact offset, + if less than 0, it is interpreted as being a per-mille value of the + size of the drawing area (or as percent value of the actual font size + (as returned by QFontMetrics::lineSpacing() ) if deltaScaleGlobal is FALSE, resp.). + Actual drawing position/size is calculated dynamically in methode trueRect. + + \sa deltaY, deltaAlign, deltaScaleGlobal + */ + int deltaX() const + { + return _deltaX; + } + + /** + Returns the Y distance between the box and its anchor. + <b>Note: </b> If greater 0, the value is taken as exact offset, + if less than 0, it is interpreted as being a per-mille value of the + size of the drawing area (or as percent value of the actual font size + (as returned by QFontMetrics::lineSpacing() ) if deltaScaleGlobal is FALSE, resp.). + Actual drawing position/size is calculated dynamically in methode trueRect. + + \sa deltaX, deltaAlign, deltaScaleGlobal + */ + int deltaY() const + { + return _deltaY; + } + + /** + Returns the way how the values specified for deltaX and/or deltaY + affect the position of the box. + + \sa setDeltaAlign, deltaX, deltaY, deltaScaleGlobal + */ + uint deltaAlign() const + { + return _deltaAlign; + } + + /** + Returns the way how the distance between the box and its anchor + is calculated. + + \sa setDeltaScaleGlobal, deltaX, deltaY, deltaAlign + */ + bool deltaScaleGlobal() const + { + return _deltaScaleGlobal; + } + + /** + Returns the width of the region where + to draw the box. <b>Note:</b> If greater 0, the value is the exact offset, + if less than 0, it is interpreted as being a per-mille value + of the logical height (or width, resp.) of the area to be used for drawing. + Actual drawing position/size is calculated dynamically in methode trueRect. + */ + int width() const + { + return _width; + } + + /** + Returns the height of the region where + to draw the box. <b>Note:</b> If greater 0, the value is the exact offset, + if less than 0, it is interpreted as being a per-mille value + of the logical height (or width, resp.) of the area to be used for drawing. + Actual drawing position/size is calculated dynamically in methode trueRect. + */ + int height() const + { + return _height; + } + + /** + Returns the text color. + */ + QColor color() const + { + return _color; + } + + /** + Returns the background brush. + */ + const QBrush & paper() const + { + return _paper; + } + +public: + /** + Destructor. Only defined to have it virtual. + */ + virtual ~KDChartCustomBox(); + + /** + Copy the settings of box \c source into this box. + + \note Use this method instead of using the assignment operator. + + \sa clone + */ + void deepCopy( const KDChartCustomBox* source ); + + + /** + Create a new box on the heap, copy the settings stored by + this box into the newly created box and return + the pointer to the new box. + + \note Use this method instead of using the copy constructor. + + \sa deepCopy + */ + const KDChartCustomBox* clone() const; + +private: + KDChartCustomBox( const KDChartCustomBox& ) : QObject(0) {} + +protected: + /** + Internal routine for recursion handling. + Note: This is a const methode changing NO VITAL information + of the box but setting an internal, temporary flag. + */ + void setInternalFlagAnchorBeingCalculated( bool flag ) const + { + KDChartCustomBox* that = const_cast<KDChartCustomBox*>(this); + that->_anchorBeingCalculated = flag; + } + /** + Internal routine for recursion handling. + */ + bool anchorBeingCalculated() const + { + return _anchorBeingCalculated; + } + + void setParentAxisArea( int parentAxis ) const + { + KDChartCustomBox* that = const_cast<KDChartCustomBox*>(this); + that->_parentAxisArea = parentAxis; + } + + int parentAxisArea() const + { + return _parentAxisArea; + } + +private: + int _rotation; + KDChartTextPiece _content; + int _fontSize; + bool _fontScaleGlobal; + // Values to be transformed into a real rect at painting time. + // If greater 0, values are exact, if less than 0, values are in per-mille + // of the size of the drawing area. + int _deltaX; + int _deltaY; + int _width; + int _height; + + QColor _color; + QBrush _paper; + + uint _anchorArea; + KDChartEnums::PositionFlag _anchorPos; + uint _anchorAlign; + uint _dataRow; + uint _dataCol; + uint _data3rd; + uint _deltaAlign; + bool _deltaScaleGlobal; + // + // The following flag is NOT to be saved/restored in a file. + // + // Being a TEMPORARY flag preventing circular recursion + // it must be set to f a l s e + // after loading a KDChartCustomBox from a file. + bool _anchorBeingCalculated; + int _parentAxisArea; +}; + + +/** + Writes the KDChartCustomBox object p as an XML document to the text stream s. + + \param s the text stream to write to + \param p the KDChartCustomBox object to write + \return the text stream after the write operation + */ +//QTextStream& operator<<( QTextStream& s, const KDChartCustomBox& p ); + + +/** + Reads the an XML document from the text stream s into the + KDChartCustomBox object p + + \param s the text stream to read from + \param p the KDChartCustomBox object to read into + \return the text stream after the read operation + */ +//QTextStream& operator>>( QTextStream& s, KDChartCustomBox& p ); + + +#endif diff --git a/kchart/kdchart/KDChartCustomBoxWrapper.cpp b/kchart/kdchart/KDChartCustomBoxWrapper.cpp new file mode 100644 index 00000000..2ee28eba --- /dev/null +++ b/kchart/kdchart/KDChartCustomBoxWrapper.cpp @@ -0,0 +1,13 @@ +#include "KDChartCustomBoxWrapper.h" +KDChartCustomBoxWrapper::KDChartCustomBoxWrapper( KDChartCustomBox* data) + :QObject(0), _data(data) +{ +} + +const KDChartTextPiece* KDChartCustomBoxWrapper::content() const +{ + return &_data->content(); +} + + +#include "KDChartCustomBoxWrapper.moc" diff --git a/kchart/kdchart/KDChartCustomBoxWrapper.h b/kchart/kdchart/KDChartCustomBoxWrapper.h new file mode 100644 index 00000000..b294a249 --- /dev/null +++ b/kchart/kdchart/KDChartCustomBoxWrapper.h @@ -0,0 +1,33 @@ +#ifndef KDCHARTCUSTOMBOX_H +#define KDCHARTCUSTOMBOX_H + +#include <qobject.h> +#include "KDChartCustomBox.h" +class KDChartCustomBox; +class KDChartTextPiece; +class KDChartCustomBoxWrapper :public QObject +{ + Q_OBJECT + +public: + KDChartCustomBoxWrapper( KDChartCustomBox* ); + +public slots: + const KDChartTextPiece* content() const; + + + // These methods need to be here, for the enums to work. + void setAnchorPosition( int position ) + { + _data->setAnchorPosition( (KDChartEnums::PositionFlag) position ); + } + + + +private: + KDChartCustomBox* _data; +}; + + +#endif /* KDCHARTCUSTOMBOX_H */ + diff --git a/kchart/kdchart/KDChartDataIntern.cpp b/kchart/kdchart/KDChartDataIntern.cpp new file mode 100644 index 00000000..21640f40 --- /dev/null +++ b/kchart/kdchart/KDChartDataIntern.cpp @@ -0,0 +1,221 @@ +/* -*- 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. + ** + **********************************************************************/ +#include <KDChartDataIntern.h> + +/** + \class KDChartData KDChartData.h + + \brief Encapsulates one data cell in a chart. + + Data cells either hold one value for the ordinate axis + or they hold two values: one for the ordinate axis and + an extra one for the abscissa axis. + + Ordinate values can either be of type double or non-existant, + abscissa values can either be of type double or QDateTime or non-existant. + + \note All methods returning one of the data values (or one of the data + types, resp.) expect a valNo parameter with either a 1 for the first value + (the Y or ordinate value) + or a 2 for the second value (the X or abscissa value). + */ + + +/** + \fn KDChartData::KDChartData() + + Constructor. Creates a KDChartData object that represents no value. + */ + +/** + \fn KDChartData::KDChartData( double value ) + + Constructor. Assigns a double value to this object. + + \param value the double value which this object should represent. + */ + +/** + \fn KDChartData::KDChartData( double yValue, double xValue ) + + Constructor. Assigns two double values to this object. + + \param yValue the double value which this object should represent. + \param xValue the position on the abscissa axis which this object + should be assigned to. + */ + + +/** + \fn KDChartData::KDChartData( double yValue, QDateTime xValue ) + + Constructor. Assigns a double value and a QDateTime value to this object. + + \param yValue the double value which this object should represent. + \param xValue the position on the abscissa axis which this object + should be assigned to. + */ + + +/** + \fn KDChartData::KDChartData( const QString& value ) + + Constructor. Assigns a string value to this object. + + This data type may be used to store axis labels or legend texts + in extra data cells. + + \param value the string value which this object should represent + */ + + +/** + \fn bool KDChartData::hasValue(int) const + + Returns true if the object represents a value (double or QString) + and false otherwise. + + \return true if the object represents a value + */ + +/** + \fn bool KDChartData::isDateTime(int) const + + Returns true if the object represents a QDateTime value and false + otherwise. + + \return true if the object represents a QDateTime value + */ + +/** + \fn bool KDChartData::isString(int) const + + Returns true if the object represents a QString value and false + otherwise. + + \return true if the object represents a QString value + */ + +/** + \fn bool KDChartData::isDouble(int) const + + Returns true if the object represents a double value and false + otherwise. + + \return true if the object represents a double value + */ + +/** + \fn bool KDChartData::isNormalDouble(int) const + + Returns true if the object represents a double value that + is neither KDCHART_POS_INFINITE nor KDCHART_NEG_INFINITE. + + \return true if the object represents a normal double value + */ + +/** + \fn bool KDChartData::isPosInfiniteDouble(int) const + + Returns true if the object represents a double value that + is KDCHART_POS_INFINITE. + + \return true if the object represents a KDCHART_POS_INFINITE value + */ + +/** + \fn bool KDChartData::isNegInfiniteDouble(int) const + + Returns true if the object represents a double value that + is KDCHART_NEG_INFINITE. + + \return true if the object represents a KDCHART_NEG_INFINITE value + */ + + +/** + \fn double KDChartData::doubleValue( int valNo=1 ) const + + Returns the double value represented by this object, + or DBL_MIN if this object does not represent a double value. + + You may use isDouble() or isNormalDouble() to check if it + is appropriate to call this function. + + \return the double value represented by this object, + or DBL_MIN if this object does not represent a double value + */ + +/** + \fn QDateTime KDChartData::dateTimeValue( int valNo=1 ) const + + Returns the QDateTime value represented by this object, + or QDateTime() if this object does not represent a QDateTime value. + + You may use isDateTime() to check if it + is appropriate to call this function. + + \return the QDateTime value represented by this object, + or QDateTime() if this object does not represent a QDateTime value + */ + +/** + \fn QString KDChartData::stringValue( int valNo=1 ) const + + Returns the string value represented by this object, + or QString::null if this object does not represent a string value. + + You may use isString() to check if it + is appropriate to call this function. + + \note String values may be used to store axis labels or legend texts + in extra data cells. + + \return the string value represented by this object, + or QString::null if this object does not represent a string value + */ + + +/** + \fn KDChartData::ValueType KDChartData::valueType( int valNo ) const + + Returns the type of the respective value. + + \param valNo specifies the value number: 1 means the Y value, 2 means the X value + + \return the type of the respective value + */ + + +/** + \fn void KDChartData::clearValue() + + Resets both of the data types of this cell to KDChartData::NoValue + and sets the property set ID to KDCHART_PROPSET_NORMAL_DATA. + */ diff --git a/kchart/kdchart/KDChartDataIntern.h b/kchart/kdchart/KDChartDataIntern.h new file mode 100644 index 00000000..cb41b5cb --- /dev/null +++ b/kchart/kdchart/KDChartDataIntern.h @@ -0,0 +1,432 @@ +/* -*- Mode: C++ -*- + KDChart - a multi-platform charting engine +*/ + +/**************************************************************************** + ** Copyright (C) 2001-2003 Klar�vdalens 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. + ** + **********************************************************************/ +#ifndef __KDCHARTDATAINTERN_H__ +#define __KDCHARTDATAINTERN_H__ + +#include <qvariant.h> +#include <qstring.h> +#include <qdatetime.h> +#include "KDChartGlobal.h" + +/** + \file KDChartData.h + + \brief Provides a class to encapsulate one data value in a chart. +*/ + + + +// Please leave all methods in this class inline! +// It's necessary since it's part of the interface provided by KDChart +class KDCHART_EXPORT KDChartData +{ +public: + // 0. default c'tor: initializing all values as undefined + + KDChartData() : + _valueType( QVariant::Invalid ), + _valueType2( QVariant::Invalid ), + _propSetID( 0 ) + {} + + // 1. simple c'tors: used for 1-coordinate data + + KDChartData( double value ) : + _valueType( QVariant::Double ), dValue( value ), + _valueType2( QVariant::Invalid ), + _propSetID( 0 ) + {} + /* string values are only supported for legend texts or axis labels */ + KDChartData( const QString& value ) : + _valueType( QVariant::String ), sValue( value ), + _valueType2( QVariant::Invalid ), + _propSetID( 0 ) + {} + /* date/time values for /ordinate/ axes are not implemented yet + KDChartData( QDateTime value ) : + _valueType( QVariant::DateTime ), dtValue( value ), + _valueType2( QVariant::Invalid ), + _propSetID( 0 ) + {}*/ + + // 2. complex c'tors: used for 2-coordinate data + + // 2.a) with additional Date/Time: normally used when Date on x-axis + // e.g. for time related index numbers like water level measurements + KDChartData( double yValue, QDateTime xValue ) : + _valueType( QVariant::Double ), dValue( yValue ), + _valueType2( QVariant::DateTime ), dtValue2( xValue ), + _propSetID( 0 ) + {} + + + KDChartData( const QVariant& _value1, + const QVariant& _value2 ) + { + switch( _value1.type() ){ + case QVariant::Int: + case QVariant::UInt: + case QVariant::Double: + _valueType = QVariant::Double; + dValue = _value1.toDouble(); + break; + case QVariant::DateTime: + _valueType = QVariant::DateTime; + dtValue = _value1.toDateTime(); + break; + case QVariant::String: + _valueType = QVariant::String; + sValue = _value1.toString(); + break; + default: + _valueType = QVariant::Invalid; + } + switch( _value2.type() ){ + case QVariant::Int: + case QVariant::UInt: + case QVariant::Double: + _valueType2 = QVariant::Double; + dValue2 = _value2.toDouble(); + break; + case QVariant::DateTime: + _valueType2 = QVariant::DateTime; + dtValue2 = _value2.toDateTime(); + break; + default: + _valueType2 = QVariant::Invalid; + } + _propSetID = 0; + } + + /* date/time values for /ordinate/ axes are not implemented yet + KDChartData( QDateTime yValue, QDateTime xValue ) : + _valueType( DateTime ), dtValue( yValue ), + _valueType2( DateTime ), dtValue2( xValue ), + _propSetID( 0 ) + {}*/ + // 2.b) with additional Double: may be used for mathematical data... + KDChartData( double yValue, double xValue ) : + _valueType( QVariant::Double ), dValue( yValue ), + _valueType2( QVariant::Double ), dValue2( xValue ), + _propSetID( 0 ) + {} + /* date/time values for /ordinate/ axes are not implemented yet + KDChartData( QDateTime yValue, double xValue ) : + _valueType( DateTime ), dtValue( yValue ), + _valueType2( Double ), dValue2( xValue ), + _propSetID( 0 ) + {}*/ + +/* + /-** + Copy constructor. + + \sa setData + * / + KDChartData( const KDChartData& other ) : + { + setData( other ); + } + + /-** + Assignment operator. + + \sa setData + * / + KDChartData& operator=( const KDChartData& R ) + { + setData( R ); + return *this; + } +*/ + /** + Compare operator. + + \sa isEqual + */ + bool operator==( const KDChartData& it ) const + { + return isEqual(*this, it); + } + + /** + Compare operator. + + \sa isEqual + */ + bool operator!=( const KDChartData& it ) const + { + return !isEqual(*this, it); + } + + /** + Compare method. + + \note The property set ID is <b>not</b> taken into account while comparing. + Two KDChartData are considered equal if their data values are equal - the + property set ID is ignored. + */ + bool isEqual( const KDChartData& a, const KDChartData& b ) const + { + bool bRet = (a.hasValue( 1 ) == b.hasValue( 1 )) && + (a.hasValue( 2 ) == b.hasValue( 2 )); + if ( bRet && a.hasValue( 1 ) ) { + bRet = a.valueType( 1 ) == b.valueType( 1 ); + if ( bRet ) { + switch ( a.valueType( 1 ) ) { + case QVariant::String: + bRet = a.stringValue( 1 ) == b.stringValue( 1 ); + break; + case QVariant::Double: + bRet = a.doubleValue( 1 ) == b.doubleValue( 1 ); + break; + case QVariant::DateTime: + bRet = a.dateTimeValue( 1 ) == b.dateTimeValue( 1 ); + break; + default: + bRet = false; + } + } + if ( bRet && a.hasValue( 2 ) ) { + bRet = a.valueType( 2 ) == b.valueType( 2 ); + if ( bRet ) { + switch ( a.valueType( 2 ) ) { + // note: the 2nd value can not be a string + // - must be a date or a number! + case QVariant::Double: + bRet = a.doubleValue( 2 ) == b.doubleValue( 2 ); + break; + case QVariant::DateTime: + bRet = a.dateTimeValue( 2 ) == b.dateTimeValue( 2 ); + break; + default: + bRet = false; + } + } + } + // Note: We do *not* compare the _propSetID here since it contains + // no values but is used to handle some layout information... + } + return bRet; + } + + /** + Method setAll. + + \note The property set ID is <b>also</b> changed by the setAll method. + + \sa setData + */ + void setAll( const KDChartData& R ) + { + setData( R ); + setPropertySet( R.propertySet() ); + } + + + /** + Method setData. + + \note The property set ID is <b>not</b> changed by the setData method. + If you want to set it please call \c KDChartData::setPropertySet() explicitely. + + \sa setAll + */ + void setData( const KDChartData& R ) + { + if( &R != this ){ + _valueType = R._valueType; + _valueType2 = R._valueType2; + switch ( valueType( 1 ) ) { + case QVariant::String: + sValue = R.sValue; + break; + case QVariant::Double: + dValue = R.dValue; + break; + case QVariant::DateTime: + dtValue = R.dtValue; + break; + default: + /* NOOP */; + } + switch ( valueType( 2 ) ) { + // note: the 2nd value can not be a string + // - must be a date or a number! + case QVariant::Double: + dValue2 = R.dValue2; + break; + case QVariant::DateTime: + dtValue2 = R.dtValue2; + break; + default: + /* NOOP */; + } + // Note: We do *not* copy the _propSetID here since it contains + // no values but is used to handle some layout information... + } + } + + QVariant::Type valueType( int valNo=1 ) const + { + return (1 == valNo) + ? _valueType + : _valueType2; + } + bool hasValue( int valNo=1 ) const + { + return (1 == valNo) + ? (_valueType != QVariant::Invalid) + : ((_valueType2 == QVariant::Double) || (_valueType2 == QVariant::DateTime)); + } + /* string values are only supported for legend texts or axis labels */ + bool isString( int valNo=1 ) const + { + return (1 == valNo) + ? (_valueType == QVariant::String) + : false; + } + bool isDouble( int valNo=1 ) const + { + return (1 == valNo) + ? (_valueType == QVariant::Double) + : (_valueType2 == QVariant::Double); + } + bool isDateTime( int valNo=1 ) const + { + return (1 == valNo) + ? (_valueType == QVariant::DateTime) + : (_valueType2 == QVariant::DateTime); + } + + + void clearValue() + { + _valueType = QVariant::Invalid; + _valueType2 = QVariant::Invalid; + _propSetID = 0; + } + + QVariant value( int valNo=1 ) const + { + if( 1 == valNo ) + switch ( valueType( 1 ) ) { + case QVariant::String: + return sValue; + case QVariant::Double: + return dValue; + case QVariant::DateTime: + return dtValue; + default: + return QVariant(); + } + else if( 2 == valNo ) + switch ( valueType( 2 ) ) { + case QVariant::Double: + return dValue2; + case QVariant::DateTime: + return dtValue2; + default: + return QVariant(); + } + else + return QVariant(); + } + + /* string values are only supported for legend texts or axis labels */ + QString stringValue( int valNo=1 ) const + { + // note: the 2nd value can not be a string + // - must be a date or a number! + if ((1 == valNo) && isString( valNo )) + return sValue; + else + return QString::null; + } + double doubleValue( int valNo=1 ) const + { + return isDouble( valNo ) + ? ((1 == valNo) ? dValue : dValue2) + : DBL_MIN; + } + QDateTime dateTimeValue( int valNo=1 ) const + { + return isDateTime( valNo ) + ? ((1 == valNo) ? dtValue : dtValue2) + : QDateTime(); + } + + /** + Assign a property set to a data cell. + + \param propSetID The ID of the property set to be assigned to this data cell. + This ID can either be one of the built-in IDs documented + at KDChartPropertySet::BuiltinDataPropertySetIDs or + a special ID that was given back by a + KDChartParams::registerProperties function call. + + \sa propertySet + \sa KDCHART_PROPSET_NORMAL_DATA, KDCHART_PROPSET_TRANSPARENT_DATA + */ + void setPropertySet( int propSetID = 0 ) + { + _propSetID = propSetID; + } + /** + Return the ID of the property set that is assigned to this data cell. + + Use KDChartParams::properties( int ID ) for accessing the respective property set information. + + \sa setPropertySet + \sa KDCHART_PROPSET_NORMAL_DATA, KDCHART_PROPSET_TRANSPARENT_DATA + */ + int propertySet() const + { + return _propSetID; + } + + +private: + // OBLIGATORY 1st value: usually used for ordinate axis + QVariant::Type _valueType; + QDateTime dtValue; + double dValue; // Sorry, d(t)Value and sValue cannot be a union, + QString sValue; // since QString has a non-default constructor. + + // OPTIONAL 2nd value: if valid, normally used for abscissa axis + // note: this 2nd value can not be a string - must be a date or a number! + QVariant::Type _valueType2; + QDateTime dtValue2; + double dValue2; + + // ID number of the property set assigned to this cell + int _propSetID; +}; + +#endif diff --git a/kchart/kdchart/KDChartDataRegion.h b/kchart/kdchart/KDChartDataRegion.h new file mode 100644 index 00000000..a1d83032 --- /dev/null +++ b/kchart/kdchart/KDChartDataRegion.h @@ -0,0 +1,226 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTDATAREGION__ +#define __KDCHARTDATAREGION__ + +#include <qregion.h> +#include <qpointarray.h> + +#include "KDChartGlobal.h" + +/*! + \internal + */ +struct KDCHART_EXPORT KDChartDataRegion +{ + typedef QValueList<QPointArray> PointArrayList; + + KDChartDataRegion() + { + init(); + } + + KDChartDataRegion( QRegion region, uint r, uint c, uint ch ) + { + init(); + pRegion = new QRegion( region ); + row = r; + col = c; + chart = ch; + } + + KDChartDataRegion( uint r, uint c, uint ch, QPointArray array ) + { + init(); + pArray = new QPointArray( array ); + row = r; + col = c; + chart = ch; + } + + KDChartDataRegion( uint r, uint c, uint ch, QRect rect ) + { + init(); + pRect = new QRect( rect ); + row = r; + col = c; + chart = ch; + } + KDChartDataRegion( uint r, uint c, uint ch, PointArrayList * list, bool takeOwnership ) + { + init(); + if( takeOwnership ) + pPointArrayList = list; + else + pPointArrayList = new PointArrayList( * list ); + row = r; + col = c; + chart = ch; + } + + ~KDChartDataRegion() + { + //qDebug ("~KDChartDataRegion"); + if( pPointArrayList ) + delete pPointArrayList; + if( pRegion ) + delete pRegion; + if( pArray ) + delete pArray; + if( pRect ) + delete pRect; + if( pTextRegion ) + delete pTextRegion; + } + + QRegion region() const + { + if( pPointArrayList && ! pPointArrayList->empty() ){ + QRegion region; + PointArrayList::iterator it; + for ( it = pPointArrayList->begin(); it != pPointArrayList->end(); ++it ){ + region += QRegion( *it ); + } + return region; + } + if( pRegion ) + return *pRegion; + if( pArray ) + return QRegion( *pArray ); + if( pRect ) + return QRegion( *pRect ); + return QRegion(); + } + + QRect rect() const + { + if( pPointArrayList && ! pPointArrayList->empty() ){ + QRect rect; + PointArrayList::iterator it; + for ( it = pPointArrayList->begin(); it != pPointArrayList->end(); ++it ){ + rect = rect.unite( (*it).boundingRect() ); + } + return rect; + } + if( pRegion ) + return pRegion->boundingRect(); + if( pArray ) + return pArray->boundingRect(); + if( pRect ) + return *pRect; + return QRect(); + } + + bool contains(const QPoint & p) const + { + if( pPointArrayList && ! pPointArrayList->empty() ){ + PointArrayList::iterator it; + for ( it = pPointArrayList->begin(); it != pPointArrayList->end(); ++it ){ + QRegion region( *it ); + if( region.contains( p ) ) + return true; + } + return false; + } + if( pRegion ) + return pRegion->contains( p ); + if( pArray ) + return QRegion( *pArray ).contains( p ); + if( pRect ) + return pRect->contains( p ); + return false; + } + + void init() + { + pRegion = 0; + pArray = 0; + pRect = 0; + pTextRegion = 0; + pPointArrayList = 0; + row = 0; + col = 0; + chart = 0; + negative = false; // default value (useful if value is a string) + points.resize( 9 ); + startAngle = 1440; + angleLen = 1; + } + + QRegion* pRegion; + QPointArray* pArray; + QRect* pRect; + QRegion* pTextRegion; // for the data values text + + // for three-dimensional bars, and for comparable structures, needing + // more than one QPointArray, we use this list: + PointArrayList* pPointArrayList; + + // For rectangular data representation (bar, line, area, point, ...) + // we use the above declared 'pRect'. + // For curved data representations (pie slice, ring segment, ...) + // we store the following additional anchor information: + + // store 9 elements: one for each value of KDChartEnums::PositionFlag + QPointArray points; + + int startAngle; // Note: 5760 makes a full circle, 2880 is left 'corner'. + int angleLen; + + uint row; + uint col; + // members needed for calculation of data values texts + uint chart; + QString text; // the data values text + bool negative; // stores whether the data value is less than zero +}; + + +/** + \class KDChartDataRegionList KDChartDataRegion.h + + \brief The collection class used by KD Chart to store data region information. + + This class is derived from QPtrList, so all of the Qt documentation for this class + is valid for KDChartDataRegionList too. + + \note Normally there is no need to use this class yourself, since it + is instantiated by the KDChartWidget. If however you are not using the + KDChartWidget class but calling the painting methods of KDChart directly, + make sure to either free the pointer stored in KDChartDataRegionList manually, + or to call setAutoDelete( true ) to let your KDChartDataRegionList own these pointers: + in this case please also make sure to call the clear() method whenever you want + your KDChartDataRegionList to free these pointers. + Note that all of this ONLY applies in case of NOT using the KDChartWidget. + + \sa KDChart, KDChartWidget + */ +typedef QPtrList < KDChartDataRegion > KDChartDataRegionList; + +#endif diff --git a/kchart/kdchart/KDChartEnums.cpp b/kchart/kdchart/KDChartEnums.cpp new file mode 100644 index 00000000..b94f16d3 --- /dev/null +++ b/kchart/kdchart/KDChartEnums.cpp @@ -0,0 +1,98 @@ +#include "KDChartEnums.h" + +QString KDChartEnums::positionFlagToString( PositionFlag type ) +{ + switch( type ) { + case PosTopLeft: + return "TopLeft"; + case PosTopCenter: + return "TopCenter"; + case PosTopRight: + return "TopRight"; + case PosCenterLeft: + return "CenterLeft"; + case PosCenter: + return "Center"; + case PosCenterRight: + return "CenterRight"; + case PosBottomLeft: + return "BottomLeft"; + case PosBottomCenter: + return "BottomCenter"; + case PosBottomRight: + return "BottomRight"; + default: // should not happen + qDebug( "Unknown content position" ); + return "TopLeft"; + } +} + +KDChartEnums::PositionFlag KDChartEnums::stringToPositionFlag( const QString& string ) +{ + if( string == "TopLeft" ) + return PosTopLeft; + else if( string == "TopCenter" ) + return PosTopCenter; + else if( string == "TopRight" ) + return PosTopRight; + else if( string == "CenterLeft" ) + return PosCenterLeft; + else if( string == "Center" ) + return PosCenter; + else if( string == "CenterRight" ) + return PosCenterRight; + else if( string == "BottomLeft" ) + return PosBottomLeft; + else if( string == "BottomCenter" ) + return PosBottomCenter; + else if( string == "BottomRight" ) + return PosBottomRight; + else // default, should not happen + return PosTopLeft; +} + +QPoint KDChartEnums::positionFlagToPoint( const QRect& rect, + PositionFlag pos ) +{ + QPoint pt; + if( rect.isValid() ) { + switch( pos ) { + case KDChartEnums::PosTopLeft: + pt = rect.topLeft(); + break; + case KDChartEnums::PosTopCenter: + pt.setY( rect.top() ); + pt.setX( rect.center().x() ); + break; + case KDChartEnums::PosTopRight: + pt = rect.topRight(); + break; + case KDChartEnums::PosCenterLeft: + pt.setY( rect.center().y() ); + pt.setX( rect.left() ); + break; + case KDChartEnums::PosCenter: + pt = rect.center(); + break; + case KDChartEnums::PosCenterRight: + pt.setY( rect.center().y() ); + pt.setX( rect.right() ); + break; + case KDChartEnums::PosBottomLeft: + pt = rect.bottomLeft(); + break; + case KDChartEnums::PosBottomCenter: + pt.setY( rect.bottom() ); + pt.setX( rect.center().x() ); + break; + case KDChartEnums::PosBottomRight: + pt = rect.bottomRight(); + break; + } + } + return pt; +} + +#ifndef KDCHART_MASTER_CVS +#include "KDChartEnums.moc" +#endif diff --git a/kchart/kdchart/KDChartEnums.h b/kchart/kdchart/KDChartEnums.h new file mode 100644 index 00000000..530a66ea --- /dev/null +++ b/kchart/kdchart/KDChartEnums.h @@ -0,0 +1,305 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTENUMS_H__ +#define __KDCHARTENUMS_H__ + +#include <qrect.h> +#include <qpointarray.h> + +#include "KDChartGlobal.h" +#include <qobject.h> + +/** \file KDChartEnums.h + \brief Definition of global enums. + */ + +/** + Project global class providing some enums needed both by KDChartParams + and by KDChartCustomBox. + */ +class KDCHART_EXPORT KDChartEnums :public QObject +{ + Q_OBJECT + Q_ENUMS( TextLayoutPolicy ) + Q_ENUMS( AreaName ) + Q_ENUMS( PositionFlag ) + +public: + /** + Text layout policy: what to do if text that is to be drawn would + cover neighboring text or neighboring areas. + + \li \c LayoutJustOverwrite Just ignore the layout collision and write the text nevertheless. + \li \c LayoutPolicyRotate Try counter-clockwise rotation to make the text fit into the space. + \li \c LayoutPolicyShiftVertically Shift the text baseline upwards (or downwards, resp.) and draw a connector line between the text and its anchor. + \li \c LayoutPolicyShiftHorizontally Shift the text baseline to the left (or to the right, resp.) and draw a connector line between the text and its anchor. + \li \c LayoutPolicyShrinkFontSize Reduce the text font size. + + \sa KDChartParams::setPrintDataValues + */ + enum TextLayoutPolicy { LayoutJustOverwrite, + LayoutPolicyRotate, + LayoutPolicyShiftVertically, + LayoutPolicyShiftHorizontally, + LayoutPolicyShrinkFontSize }; + + /** + Converts the specified text layout policy enum to a + string representation. + + \param type the text layout policy to convert + \return the string representation of the text layout policy enum + */ + static QString layoutPolicyToString( TextLayoutPolicy type ) { + switch( type ) { + case LayoutJustOverwrite: + return "JustOverwrite"; + case LayoutPolicyRotate: + return "Rotate"; + case LayoutPolicyShiftVertically: + return "ShiftVertically"; + case LayoutPolicyShiftHorizontally: + return "ShiftHorizontally"; + case LayoutPolicyShrinkFontSize: + return "ShrinkFontSize"; + default: // should not happen + qDebug( "Unknown text layout policy" ); + return "JustOverwrite"; + } + } + + + /** + Number notation specifies the general way, how a number is to be shown. + + \li \c NumberNotationDecimal Traditional way of writing a decimal number. + \li \c NumberNotationScientific Exponential notation, with exactly one non-zero digit to the left of the decimal. + \li \c NumberNotationScientificBig Same as \c NumberNotationScientific, but using 'E' instead of 'e'. + + \sa KDChartAxisParams::setAxisLabelsNotation + */ + enum NumberNotation { NumberNotationDecimal, + NumberNotationScientific, + NumberNotationScientificBig }; + + /** + Converts the specified number notation enum to a + string representation. + + \param notation the number notation to convert + \return the string representation of the number notation enum + */ + static QString numberNotationToString( NumberNotation notation ) { + switch( notation ) { + case NumberNotationDecimal: + return "NumberNotationDecimal"; + case NumberNotationScientific: + return "NumberNotationScientific"; + case NumberNotationScientificBig: + return "NumberNotationScientificBig"; + default: // should not happen + qDebug( "Unknown text number notation" ); + return "NumberNotationDecimal"; + } + } + + + /** + Converts the specified string to a number notation enum value. + + \param string the string to convert + \return the number notation enum value + */ + static NumberNotation stringToNumberNotation( const QString& notation ) { + if( notation == "NumberNotationDecimal" ) + return NumberNotationDecimal; + else if( notation == "NumberNotationScientific" ) + return NumberNotationScientific; + else if( notation == "NumberNotationScientificBig" ) + return NumberNotationScientificBig; + else // default, should not happen + return NumberNotationDecimal; + } + + + /** + Converts the specified string to a text layout policy enum value. + + \param string the string to convert + \return the text layout policy enum value + */ + static TextLayoutPolicy stringToLayoutPolicy( const QString& string ) { + if( string == "JustOverwrite" ) + return LayoutJustOverwrite; + else if( string == "Rotate" ) + return LayoutPolicyRotate; + else if( string == "ShiftVertically" ) + return LayoutPolicyShiftVertically; + else if( string == "ShiftHorizontally" ) + return LayoutPolicyShiftHorizontally; + else if( string == "ShrinkFontSize" ) + return LayoutPolicyShrinkFontSize; + else // default, should not happen + return LayoutJustOverwrite; + } + + /** + Areas of the chart that may have their own backgrounds + and/or may be surrounded by a simple or complex border. + + \li \c AreaData surrounding the data area + \li \c AreaAxes surrounding the axes but leaving out the data area + \li \c AreaDataAxes surrounding the data+axes area + \li \c AreaLegend surrounding the legend area + \li \c AreaDataAxesLegend surrounding the data+axes+legend area + \li \c AreaHeaders surrounding the headers area + \li \c AreaFooters surrounding the footers area + \li \c AreaDataAxesLegendHeadersFooters surrounding the data+axes+legend+headers+footers area + \li \c AreaInnermost covering the complete drawing area but <b>not</b> covering the global left/top/right/bottom leading + \li \c AreaOutermost covering the complete drawing area including the global left/top/right/bottom leading + + \li \c AreaChartDataRegion covering the area used to display one data entry (i.e. one point, bar, line, pie slice,...). + The respective data coordinates are specified by additional parameters, this is used by + KDChartCustomBox where you have the parameters \c dataRow, \c dataCol, \c data3rd. + + In addition there is a special value specifying a <b>list</b> of regions: + + \li \c AreasCustomBoxes specifies many small areas surrounding all the custom boxes that you might have added to the chart, + this is useful in case you want to specify some default frame settings to be used for all custom boxes + not having frame settings of their own. + + Finally there are three special values that you may use to specify + a single axis area (or a header/footer area, or a custom box area resp.). + Just add the number of the axis (or header/footer, or custom box resp.) + to the respective base value: + + \li \c AreaAxisBASE value to be added to the axis number in case you want to specify a single axis area, + e.g. for specifying the area of the left ordinate axis just type <b>AreaAxisBASE + AxisPosLeft</b>. + \li \c AreaHdFtBASE value to be added to the header/footer number in case you want to specify a single header (or footer, resp.) area, + e.g. for specifying the area of the main header just type <b>AreaHdFtBASE + HdFtPosHeader</b>. + \li \c AreaCustomBoxBASE value to be added to the number of a custom box that you might have added to your chart, + e.g. for specifying the area a custom box you have added to the chart + (let us assume the index of that box is in \c boxIdx1) just type <b>AreaCustBoxBASE + boxIdx1</b>. + + \sa KDChartParams::setSimpleFrame, KDChartParams::setFrame + \sa KDChartParams::insertCustomBox, KDChartCustomBox + */ + enum AreaName { AreaUNKNOWN = 0x0000, + AreaData = 0x0001, + AreaAxes = 0x0002, + AreaDataAxes = 0x0003, + AreaLegend = 0x0004, + AreaDataAxesLegend = 0x0005, + AreaHeaders = 0x0006, + AreaFooters = 0x0007, + AreaDataAxesLegendHeadersFooters = 0x0008, + AreaInnermost = 0x0009, + AreaOutermost = 0x000a, + AreaChartDataRegion = 0x000b, + AreasCustomBoxes = 0x000d, + AreaAxisBASE = 0x1000, + AreaHdFtBASE = 0x2000, + AreaCustomBoxesBASE = 0x4000, + AreaBASEMask = 0xF000 }; + + + /** + The general position flag to specify a point of + an area, for example this could be the anchor point + which an annotation box should be aligned to. + + The following picture shows the different positions: + + \image html "../refman_images/positions.png" + \image latex "../refman_images/positions.png" "the PositionFlag enum" width=4in + + \note The position and alignment of content to be printed at (or + inside of, resp.) an area or a point -- like for printing data value texts next + to their graphical representations (which might be a bar, line, pie slice,...) -- + is specified by two parameters: a \c PositionFlag and a uint holding a combination of \c Qt::AlignmentFlags. + Remember that Qt::AlignmentFlags are used to specify <b>with which edge</b> something + is to be aligned to its anchor, e.g. \c AlignLeft means align with the left edge. + + The position of content and the way it is aligned to this + position is shown in the following drawing, note that annotation #2 and annotation #3 + share the same PositionFlag but have different alignment flags set: + + \image html "../refman_images/alignment.png" + \image latex "../refman_images/alignment.png" "positioning and aligning" width=4in + + \sa KDChartParams::setPrintDataValues + */ + enum PositionFlag { PosTopLeft =0, PosTopCenter =1, PosTopRight =2, + PosCenterLeft=3, PosCenter =4, PosCenterRight=5, + PosBottomLeft=6, PosBottomCenter=7, PosBottomRight=8 }; + + + /** + Returns the point representing a position of a rectangle. + */ + static QPoint positionFlagToPoint( const QRect& rect, + PositionFlag pos ); + + /** + Returns the point representing a position of a corresponding + QPointArray. + + \note The array \c points <b>must</b> have at least nine elements. + */ + static QPoint positionFlagToPoint( const QPointArray& points, + PositionFlag pos ) + { + QPoint pt; + if( 9 <= points.size() ) + pt = points[ pos ]; + return pt; + } + + + /** + Converts the specified content position enum to a + string representation. + + \param type the content position to convert + \return the string representation of the type enum + */ + static QString positionFlagToString( PositionFlag type ); + + + /** + Converts the specified string to a content position enum value. + + \param string the string to convert + \return the content position enum value + */ + static PositionFlag stringToPositionFlag( const QString& string ); +}; + + +#endif diff --git a/kchart/kdchart/KDChartGlobal.h b/kchart/kdchart/KDChartGlobal.h new file mode 100644 index 00000000..4a5f9e77 --- /dev/null +++ b/kchart/kdchart/KDChartGlobal.h @@ -0,0 +1,203 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTGLOBAL_H__ +#define __KDCHARTGLOBAL_H__ + +#include <qglobal.h> + +#include <float.h> + +#if defined(unix) || defined(__unix__) || defined(Q_WS_MAC) || defined(Q_WS_LINUX) +#include <limits.h> +#else +#define MINDOUBLE DBL_MIN +#define MAXDOUBLE DBL_MAX +#endif + +#if defined(_MSC_VER) +#pragma warning(disable: 4251) +#endif +#include <kdchart_export.h> + + +/* \file KDChartGlobal.h + \brief Definition of global macros. + */ + + + +#if defined(_MSC_VER) +#ifndef QT_NO_STL +#define QT_NO_STL // Avoid a name clash +#endif +#endif + + + +// important: make the old format of QT_VERSION comparable +// to the new format that is used since Qt 3.0.0 +#if QT_VERSION == 141 +#define COMPAT_QT_VERSION 0x010401 +#endif +#if QT_VERSION == 142 +#define COMPAT_QT_VERSION 0x010402 +#endif +#if QT_VERSION == 143 +#define COMPAT_QT_VERSION 0x010403 +#endif +#if QT_VERSION == 144 +#define COMPAT_QT_VERSION 0x010404 +#endif +#if QT_VERSION == 145 +#define COMPAT_QT_VERSION 0x010405 +#endif +#if QT_VERSION == 200 +#define COMPAT_QT_VERSION 0x020000 +#endif +#if QT_VERSION == 201 +#define COMPAT_QT_VERSION 0x020001 +#endif +#if QT_VERSION == 202 +#define COMPAT_QT_VERSION 0x020002 +#endif +#if QT_VERSION == 210 +#define COMPAT_QT_VERSION 0x020100 +#endif +#if QT_VERSION == 211 +#define COMPAT_QT_VERSION 0x020101 +#endif +#if QT_VERSION == 220 +#define COMPAT_QT_VERSION 0x020200 +#endif +#if QT_VERSION == 221 +#define COMPAT_QT_VERSION 0x020201 +#endif +#if QT_VERSION == 222 +#define COMPAT_QT_VERSION 0x020202 +#endif +#if QT_VERSION == 223 +#define COMPAT_QT_VERSION 0x020203 +#endif +#if QT_VERSION == 224 +#define COMPAT_QT_VERSION 0x020204 +#endif +#if QT_VERSION == 230 +#define COMPAT_QT_VERSION 0x020300 +#endif +#if QT_VERSION == 231 +#define COMPAT_QT_VERSION 0x020301 +#endif +#if QT_VERSION == 232 +#define COMPAT_QT_VERSION 0x020302 +#if defined(_MSC_VER) +#pragma message( "using Qt 2.3.2" ) +#endif +#endif +#if QT_VERSION == 300 +#define COMPAT_QT_VERSION 0x030000 +#endif +#if QT_VERSION == 301 +#define COMPAT_QT_VERSION 0x030001 +#endif +#if QT_VERSION == 302 +#define COMPAT_QT_VERSION 0x030002 +#endif +#if QT_VERSION == 303 +#define COMPAT_QT_VERSION 0x030003 +#endif +#if QT_VERSION == 304 +#define COMPAT_QT_VERSION 0x030004 +#endif +#if QT_VERSION == 305 +#define COMPAT_QT_VERSION 0x030005 +#endif +#if QT_VERSION == 310 +#define COMPAT_QT_VERSION 0x030100 +#endif +// --- new format since Qt 3.0.0 or since 3.0.4 ??? +#ifndef COMPAT_QT_VERSION +#if defined(_MSC_VER) +#pragma message( "using Qt > 2.3.2" ) +#endif +#define COMPAT_QT_VERSION QT_VERSION +#endif + +// since Qt 3.1 they do /not/ include limits.h or climits on windows anymore +// so we must include that manually +#if COMPAT_QT_VERSION > 0x030099 +#if defined(_MSC_VER) +#include <climits> +#endif +#endif + + +#if COMPAT_QT_VERSION < 0x030000 + +#if !defined(Q_ASSERT) +#if defined(QT_CHECK_STATE) +#if defined(QT_FATAL_ASSERT) +#define Q_ASSERT(x) ((x) ? (void)0 : qFatal("ASSERT: \"%s\" in %s (%d)",#x,__FILE__,__LINE__)) +#else +#define Q_ASSERT(x) ((x) ? (void)0 : qWarning("ASSERT: \"%s\" in %s (%d)",#x,__FILE__,__LINE__)) +#endif +#else +#define Q_ASSERT(x) +#endif +#endif + +#if !defined(QT_NO_COMPAT) +// source compatibility with Qt 2.x +#ifndef Q_OS_TEMP +# if !defined(ASSERT) +# define ASSERT(x) Q_ASSERT(x) +# endif +#endif // Q_OS_TEMP +#endif // QT_NO_COMPAT + +#define QPtrList QList +#define QPtrListIterator QListIterator +#define QPtrVector QVector +#define QMemArray QArray + +#include <qlist.h> +#include <qvector.h> +#include <qarray.h> + +#else + +#include <qptrlist.h> +#include <qptrvector.h> +#include <qmemarray.h> + +#endif +// end of #if COMPAT_QT_VERSION < 0x030000 + + + +#endif diff --git a/kchart/kdchart/KDChartHiLoPainter.cpp b/kchart/kdchart/KDChartHiLoPainter.cpp new file mode 100644 index 00000000..6cebb723 --- /dev/null +++ b/kchart/kdchart/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 info@klaralvdalens-datakonsult.se 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 + } +} diff --git a/kchart/kdchart/KDChartHiLoPainter.h b/kchart/kdchart/KDChartHiLoPainter.h new file mode 100644 index 00000000..79dd09c7 --- /dev/null +++ b/kchart/kdchart/KDChartHiLoPainter.h @@ -0,0 +1,71 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTHILOPAINTER_H__ +#define __KDCHARTHILOPAINTER_H__ + +#include "KDChartAxesPainter.h" +#include <KDChartTable.h> + +#include <qnamespace.h> + +class KDChartParams; + +class KDChartHiLoPainter : public KDChartAxesPainter, public Qt +{ + friend class KDChartPainter; + protected: + KDChartHiLoPainter( KDChartParams* params ); + virtual ~KDChartHiLoPainter(); + + virtual bool isNormalMode() const; + virtual int clipShiftUp( bool, double ) const; + virtual void 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 ); + + virtual QString fallbackLegendText( uint dataset ) const; + virtual uint numLegendFallbackTexts( KDChartTableDataBase* data ) const; +}; + +#endif diff --git a/kchart/kdchart/KDChartLinesPainter.cpp b/kchart/kdchart/KDChartLinesPainter.cpp new file mode 100644 index 00000000..2d6181d4 --- /dev/null +++ b/kchart/kdchart/KDChartLinesPainter.cpp @@ -0,0 +1,944 @@ +/* -*- 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. + ** + **********************************************************************/ +#include "KDChartLinesPainter.h" +#include <KDChartParams.h> +#include <KDChartPropertySet.h> + +#include <qpainter.h> + +#if COMPAT_QT_VERSION >= 0x030000 +#include <qvaluevector.h> +#else +#include <qarray.h> +#endif + +#include <stdlib.h> + +/** + \class KDChartLinesPainter KDChartLinesPainter.h + + \brief A chart painter implementation that can paint line charts. +*/ + +/** + Constructor. Sets up internal data structures as necessary. + + \param params the KDChartParams structure that defines the chart + \param data the data that will be displayed as a chart +*/ +KDChartLinesPainter::KDChartLinesPainter( 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. +*/ +KDChartLinesPainter::~KDChartLinesPainter() +{ + // intentionally left blank +} + + +/** + Paints the actual data area. Data regions will only be added if \a + regions is not 0. + + \param painter the QPainter onto which the chart should be painted + \param data the data that will be displayed as a chart + \param paint2nd specifies whether the main chart or the additional chart is to be drawn now + \param regions a pointer to a list of regions that will be filled + with regions representing the data segments, if not null +*/ +void KDChartLinesPainter::paintData( QPainter* painter, + KDChartTableDataBase* data, + bool paint2nd, + KDChartDataRegionList* regions ) +{ + paintDataInternal( painter, data, + true, // center points + params()->lineMarker() && !params()->threeDLines(), // line markers yes/no, 3D lines have no markers + false, // not an area + paint2nd, + regions ); +} + +/** + \internal + + Does the actual painting of a line or an area chart and is provided + with the appropriate parameters from \c + KDChartLinesPainter::paintData() and + KDChartAreaPainter::paintData(). + + \param painter the QPainter onto which the chart should be painted + \param data the data that will be displayed as a chart + \param centerThePoints if true, the value points will be centered in + their segment (typically used for line charts), if false, the first + value point will be on the ordinate, the second one at the right + margin of the chart. + \param isArea true if an area will be drawn, false for lines. Using + this parameter should be avoided. + \param paint2nd specifies whether the main chart or the additional chart is to be drawn now + \param regions a pointer to a list of regions that will be filled + with regions representing the data segments, if not null +*/ +void KDChartLinesPainter::paintDataInternal( QPainter* painter, + KDChartTableDataBase* data, + bool centerThePoints, + bool drawMarkers, + bool isArea, + bool paint2nd, + KDChartDataRegionList* regions ) +{ + mCenterThePoints = centerThePoints; + mDrawMarkers = drawMarkers; + mIsArea = isArea; + mChartType = paint2nd ? params()->additionalChartType() + : params()->chartType(); + + KDChartAxesPainter::paintData( painter, data, paint2nd, regions ); +} + + +#define DEGTORAD(d) (d)*M_PI/180 + +/*! + Projects a point in a space defined by its x, y, and z coordinates + into a point onto a plane, given two rotation angles around the x + resp. y axis. +*/ +QPoint KDChartLinesPainter::project( int x, int y, int z ) +{ + + double xrad = DEGTORAD( params()->threeDLineXRotation() ); + double yrad = DEGTORAD( params()->threeDLineYRotation() ); + QPoint ret( static_cast<int>( x*cos( yrad ) + z * sin( yrad ) ), + static_cast<int>( y*cos( xrad ) - z * sin( xrad ) ) ); + return ret; +} + +bool KDChartLinesPainter::isNormalMode() const +{ + return KDChartParams::LineNormal == params()->lineChartSubType(); +} + +int KDChartLinesPainter::clipShiftUp( bool, double ) const +{ + return 0; +} + + +class MyPoint +{ +public: + MyPoint() : bValid( false ), bSkipThis( false ), cellValue( 0.0 ) {} + void set( int x, int y, double value ) { + bValid = true; + p.setX( x ); + p.setY( y ); + cellValue = value; + } + void setSkipThis( bool skipThis ) { + bSkipThis = skipThis; + } + QPoint p; + bool bValid; + bool bSkipThis; + double cellValue; +}; + + +void KDChartLinesPainter::specificPaintData( QPainter* painter, + const QRect& /*ourClipRect*/, + KDChartTableDataBase* data, + KDChartDataRegionList* regions, + const KDChartAxisParams* ordinatePara, + 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 ) +{ + if( !data ) return; + + + abscissaInfos ai; + ai.bCenterThePoints = mCenterThePoints; + calculateAbscissaInfos( *params(), *data, + datasetStart, datasetEnd, + logWidth, _dataRect, + ai ); + mCenterThePoints = ai.bCenterThePoints; + + bool bOrdinateDecreasing = ordinatePara + ? ordinatePara->axisValuesDecreasing() + : false; + bool bOrdinateIsLogarithmic + = ordinatePara + ? (KDChartAxisParams::AxisCalcLogarithmic == ordinatePara->axisCalcMode()) + : false; + + //const double ordinatePixelsPerUnit = logHeight / columnValueDistance; + const double ordinatePixelsPerUnit + = ( ordinatePara + && (0.0 != ordinatePara->trueAxisDeltaPixels()) + && (0.0 != ordinatePara->trueAxisDelta())) + ? ordinatePara->trueAxisDeltaPixels() / ordinatePara->trueAxisDelta() + : logHeight / columnValueDistance;; + //qDebug("ordinatePixelsPerUnit: %f",ordinatePixelsPerUnit); + + + const bool showThreeDLines = !mIsArea && params()->threeDLines(); + + enum { Normal, Stacked, Percent } mode = Normal; + if ( ( ( mChartType == KDChartParams::Line ) + && ( params()->lineChartSubType() == KDChartParams::LineNormal ) ) + || ( ( mChartType == KDChartParams::Area ) + && ( params()->areaChartSubType() == KDChartParams::AreaNormal ) ) ) + mode = Normal; + else if ( ( ( mChartType == KDChartParams::Line ) + && ( params()->lineChartSubType() == KDChartParams::LineStacked ) ) + || ( ( mChartType == KDChartParams::Area ) + && ( params()->areaChartSubType() == KDChartParams::AreaStacked ) ) ) + mode = Stacked; + else if ( ( ( mChartType == KDChartParams::Line ) + && ( params()->lineChartSubType() == KDChartParams::LinePercent ) ) + || ( ( mChartType == KDChartParams::Area ) + && ( params()->areaChartSubType() == KDChartParams::AreaPercent ) ) ) + mode = Percent; + else + qDebug( "Internal error in KDChartLinesPainter::paintDataInternal(): Unknown subtype" ); + + + QMap < int, double > currentValueSums; + if ( mode == Stacked || mode == Percent ) { + // this array is only used for stacked and percent lines, no need + // to waste time initializing it for normal types + for ( int value = 0; value < ai.numValues; ++value ) + currentValueSums[ value ] = 0.0; + } + QMap < int, double > totalValueSums; + + // compute the position of the 0 axis + double zeroXAxisI; + if ( mode == Percent ) { + if ( minColumnValue == 0.0 ) + zeroXAxisI = logHeight + axisYOffset; + else if( maxColumnValue == 0.0 ) + zeroXAxisI = _dataRect.y() + axisYOffset; + else + zeroXAxisI = logHeight / 2.0 + _dataRect.y(); + } else + zeroXAxisI = ordinatePara->axisZeroLineStartY() - _dataRect.y(); + + + // compute how to shift of the points in case we want them in the + // middle of their respective columns + int xShift = mCenterThePoints ? static_cast < int > ( ai.pointDist * 0.5 ) : 0; + + + // calculate all points' positions + // =============================== + int arrayNumDatasets = 0; + int arrayNumValues = ai.bAbscissaHasTrueAxisDtValues + ? data->cols() + : ai.numValues; + int dataset; + for( dataset = datasetEnd; + ( dataset >= static_cast < int > ( datasetStart ) && dataset >= 0 ); + --dataset ) + ++arrayNumDatasets; +#if COMPAT_QT_VERSION >= 0x030000 + QValueVector<MyPoint> allPoints( +#else + QArray<MyPoint> allPoints( +#endif + arrayNumDatasets * arrayNumValues ); + + KDChartPropertySet curPropSet; + int curPropSetId = KDChartPropertySet::UndefinedID; + + for( dataset = datasetEnd; ( dataset >= (int)datasetStart && dataset >= 0 ); --dataset ) { + + int prevPointX = -1; + int prevPointY = -1; + + const KDChartParams::LineMarkerStyle + defaultMarkerStyle = params()->lineMarkerStyle( dataset ); + const QPen default2DPen( params()->lineColor().isValid() + ? params()->lineColor() + : params()->dataColor( dataset ), + params()->lineWidth(), + params()->lineStyle( dataset ) ); + + if( ai.bAbscissaHasTrueAxisDtValues ) + ai.numValues = data->cols(); + + QVariant vValY; + QVariant vValX; + int cellPropID; + for( int value = 0; value < ai.numValues; ++value ) { + //if ( mode == Percent ) + // valueTotal = data->colAbsSum( value ); + double valueTotal = 0.0; // Will only be used for Percent + if( mode == Percent ) { + valueTotal = 0.0; + // iterate over datasets of this axis only: + for ( uint dataset2 = datasetStart; + dataset2 <= datasetEnd; + ++dataset2 ) { + if( data->cellCoord( dataset2, value, vValY, 1 ) && + QVariant::Double == vValY.type() ) + valueTotal += vValY.toDouble(); + } + } + + if( data->cellContent( dataset, value, vValY, vValX, cellPropID ) && + QVariant::Double == vValY.type() && + ( !ai.bCellsHaveSeveralCoordinates || QVariant::Invalid != vValX.type() ) ){ + //qDebug("a. cellPropID: %i",cellPropID); + + // calculate Ordinate axis value + // ----------------------------- + double cellValue = vValY.toDouble(); + double drawValue = 0.0; + // PENDING(kalle) This does not work for AreaPercent yet + if ( mode == Stacked ) + drawValue = ( cellValue + currentValueSums[ value ] ) * ordinatePixelsPerUnit; + else if ( mode == Percent ) + drawValue = ( ( cellValue + currentValueSums[ value ] ) / valueTotal ) * 100.0 * ordinatePixelsPerUnit; + else { + // LineNormal or AreaNormal + if( bOrdinateIsLogarithmic ){ + if( 0.0 < cellValue ) + drawValue = log10( cellValue ) * ordinatePixelsPerUnit; + else + drawValue = -10250.0; + //qDebug("\nlogarithmic calc - cellValue: %f drawValue: %f", + // cellValue, drawValue ); + }else{ + drawValue = cellValue * ordinatePixelsPerUnit * (bOrdinateDecreasing ? -1.0 : 1.0); + //qDebug("\nlinear calc - cellValue: %f\n - drawValue: %f", + // cellValue, drawValue ); + } + } + + + // calculate Abscissa axis value + // ----------------------------- + double xValue; + bool skipMe = !calculateAbscissaAxisValue( vValX, ai, value, + xValue ); + + + // calculate and store the point and region / draw the marker + // ---------------------------------------------------------- + if( !skipMe ){ + // prevent the point from being toooo far + // below the bottom (or above the top, resp.) + // of the cliprect + double pY = QMIN( zeroXAxisI - drawValue, + (logHeight + axisYOffset) * 3 ); + pY = QMAX( pY, -(logHeight + axisYOffset) * 3 ); + // specify the Point + int myPointX = static_cast < int > ( xValue ) + xShift; + int myPointY = static_cast < int > ( pY ); + + if( cellPropID == curPropSetId && + myPointX == prevPointX && + myPointY == prevPointY ){ + allPoints[ static_cast < int > ( datasetEnd-dataset ) + * arrayNumValues + value ].setSkipThis( true ); + skipMe = true; + //qDebug("skipped"); + }else{ + // use typecast to make it compile on windows using qt232 + allPoints[ static_cast < int > ( datasetEnd-dataset ) + * arrayNumValues + value ].set( myPointX, myPointY, cellValue ); + //qDebug("ok"); + } + if( !skipMe ){ + // -------------------------------------------------------- + // determine any 'extra' properties assigned to this cell + // by traversing the property set chain (if necessary) + // -------------------------------------------------------- + if( cellPropID != curPropSetId ){ + //qDebug("b. ( curPropSetId: %i )",curPropSetId); + //qDebug("b. cellPropID: %i",cellPropID); + //qDebug(curPropSet.name().latin1()); + if( cellPropID != KDChartPropertySet::UndefinedID && + params()->calculateProperties( cellPropID, + curPropSet ) ){ + curPropSetId = cellPropID; + //qDebug("c. curPropSetId: %i",curPropSetId); + //qDebug(curPropSet.name().latin1()); + }else{ + curPropSetId = KDChartPropertySet::UndefinedID; + } + } + // make sure any extra horiz. and/or vert. lines and/or markers + // are drawn *before* the data lines and/or markers are painted + if( mChartType == KDChartParams::Line ){ + if( curPropSetId != KDChartPropertySet::UndefinedID ){ + drawExtraLinesAndMarkers( + curPropSet, + default2DPen, + defaultMarkerStyle, + myPointX, myPointY, + painter, + ai.abscissaPara, + ordinatePara, + logWidth/1000.0, + logHeight/1000.0, + false ); + } + } + prevPointX = myPointX; + prevPointY = myPointY; + } + } + // calculate running sum for stacked and percent + if ( mode == Stacked || mode == Percent ) { + if( cellValue == KDCHART_POS_INFINITE ) + currentValueSums[ value ] = KDCHART_POS_INFINITE; + else if( currentValueSums[ value ] != KDCHART_POS_INFINITE ) + currentValueSums[ value ] += cellValue; + } + } + } + } + + + + QPointArray previousPoints; // no vector since only areas need it, + // and these do not support 3d yet + + // Store some (dataset-independend) default values + // to be used unless other properties + // have been specified for the respective data cell: + // + const bool defaultDrawMarkers = mDrawMarkers; + + for ( dataset = datasetEnd; ( dataset >= (int)datasetStart && dataset >= 0 ); --dataset ) { + + // Store some (dataset-dependend) default values + // to be used unless other properties + // have been specified for the respective data cell: + // + const QPen default2DPen( params()->lineColor().isValid() + ? params()->lineColor() + : params()->dataColor( dataset ), + params()->lineWidth(), + params()->lineStyle( dataset ) ); + bool currentDrawMarkers = defaultDrawMarkers; + const KDChartParams::LineMarkerStyle markerStyle = params()->lineMarkerStyle( dataset ); + + // the +2 is for the areas (if any) + QPtrVector< QPointArray > points( 2 ); + points.setAutoDelete( true ); + /* Pending Michel - we need to keep track of the + * non rotated points for 3D lines + */ + QPtrVector< QPointArray > oripoints( 2 ); + oripoints.setAutoDelete( true ); + + int i = 0; + for( i = 0; i < 2; ++i ) { + points.insert( i, new QPointArray( ai.numValues + 2 ) ); + oripoints.insert( i, new QPointArray( ai.numValues + 2 ) ); + } + + if( ai.bAbscissaHasTrueAxisDtValues ) + ai.numValues = data->cols(); + + int point = 0; + + for ( int value = 0; value < ai.numValues; ++value ) { + + // determine and store marker properties assigned to this cell + // ----------------------------------------------------------- + currentDrawMarkers = defaultDrawMarkers; + int cellPropID; + if( data->cellProp( dataset, value, cellPropID ) && + cellPropID != curPropSetId ){ + if( cellPropID != KDChartPropertySet::UndefinedID && + params()->calculateProperties( cellPropID, + curPropSet ) ) + curPropSetId = cellPropID; + else + curPropSetId = KDChartPropertySet::UndefinedID; + } + if( curPropSetId != KDChartPropertySet::UndefinedID ){ + // we can safely call the following functions and ignore their + // return values since they will touch the parameters' values + // if the propSet *contains* corresponding own values only. + int iDummy; + curPropSet.hasOwnShowMarker( iDummy, currentDrawMarkers ); + } + + + int iVec = static_cast < int > ( datasetEnd-dataset ) * arrayNumValues + value; + if( allPoints[ iVec ].bValid && !allPoints[ iVec ].bSkipThis ){ + const MyPoint& mp = allPoints[iVec]; + + + //qDebug("\np.x() %i p.y() %i", p.x(), p.y() ); + // For 3D lines, we need two points (that lie + // behind each other on the Z axis). For 2D lines and + // areas, we need only one point. + if( showThreeDLines ) { + points[0]->setPoint( point, project( mp.p.x(), mp.p.y(), + (datasetStart+dataset)*params()->threeDLineDepth() ) ); + points[1]->setPoint( point, project( mp.p.x(), mp.p.y(), + (datasetStart+dataset + 1)*params()->threeDLineDepth() ) ); + oripoints[0]->setPoint( point, mp.p.x(), mp.p.y() ); + oripoints[1]->setPoint( point, mp.p.x() - (datasetStart+dataset + 1)*params()->threeDLineDepth(), + mp.p.y() - (datasetStart+dataset + 1)*params()->threeDLineDepth() ); + + } else + // 2D lines or areas + points[0]->setPoint( point, mp.p ); + ++point; + + int x = mp.p.x(); + int y = QMAX(QMIN(mp.p.y(), + static_cast < int > (logHeight +axisYOffset)), + 0); + bool markerIsOutside = y != mp.p.y(); + // draw the marker and store the region + if ( currentDrawMarkers ){ + uint theAlignment = Qt::AlignCenter; + bool hasOwnSize = false; + int theWidth = 0; + int theHeight = 0; + QColor theColor(params()->dataColor( dataset )); + int theStyle = markerStyle; + if( curPropSetId != KDChartPropertySet::UndefinedID ){ + // we can safely call the following functions and ignore their + // return values since they will touch the parameters' values + // if the propSet *contains* corresponding own values only. + int iDummy; + curPropSet.hasOwnMarkerAlign( iDummy, theAlignment ); + curPropSet.hasOwnMarkerColor( iDummy, theColor ); + curPropSet.hasOwnMarkerStyle( iDummy, theStyle ); + QSize size(theWidth, theHeight); + hasOwnSize = curPropSet.hasOwnMarkerSize(iDummy, size); + if( hasOwnSize ){ + theWidth = size.width(); + theHeight = size.height(); + } + } + + drawMarker( painter, + params(), + _areaWidthP1000, _areaHeightP1000, + _dataRect.x(), + _dataRect.y(), + markerIsOutside + ? KDChartParams::LineMarker1Pixel + : theStyle, + theColor, + QPoint(x,y), + dataset, value, chart, regions, + hasOwnSize ? &theWidth : 0, + hasOwnSize ? &theHeight : 0, + theAlignment ); + + } + // store the region + else if( regions ) { + QRect rect( QPoint( x-1, y-1 ), QPoint( x+1, y+1 ) ); + rect.moveBy( _dataRect.x(), _dataRect.y() ); + regions->append( + new KDChartDataRegion(dataset, value, chart, rect) ); + } + + } + } + if ( point ) { + bool bDrawLines = (0 != params()->lineWidth()); + + if ( mIsArea ) { + // first draw with the fill brush, no pen, with the + // zero axis points or upper border points added for the first + // dataset or with the previous points reversed for all other + // datasets. + painter->setPen( QPen( Qt::NoPen ) ); + const QBrush datasetBrush( params()->dataColor( dataset ), Qt::SolidPattern ); + painter->setBrush( datasetBrush ); + QBrush currentBrush( datasetBrush ); + + if ( mode == Normal || dataset == (int)datasetEnd ) { + /// first dataset (or any dataset in normal mode, where + /// the datasets overwrite each other) + + // no 3d handling for areas yet + QPoint lastPoint = points[0]->point( point - 1 ); + + // zeroXAxisI can be too far below the abscissa, but it's + // the only thing we have. Likewise can 0 be too far above + // the upper boundary, but again it's the only thing we + // have, at the rest is clipped anyway. + int yCoord; + if ( params()->areaLocation() == KDChartParams::AreaBelow || + mode == Percent ) + yCoord = static_cast<int>(zeroXAxisI); + else + yCoord = static_cast<int>(axisYOffset); + + // old: draw the complete area in on go: + /* + // no 3d handling for areas yet + points[0]->setPoint( point, lastPoint.x(), yCoord ); + point++; + + QPoint firstPoint = points[0]->point( 0 ); + points[0]->setPoint( point, firstPoint.x(), yCoord ); + point++; + + painter->drawPolygon( *points[0], false, 0, point ); + */ + + // new: draw individual area segments: + curPropSetId = KDChartPropertySet::UndefinedID; + for( int value = 0; value < point-1; ++value ) { + + int cellPropID; + if( data->cellProp( dataset, value, cellPropID ) && + cellPropID != curPropSetId ){ + + if( cellPropID != KDChartPropertySet::UndefinedID && + params()->calculateProperties( cellPropID, + curPropSet ) ){ + curPropSetId = cellPropID; + }else{ + curPropSetId = KDChartPropertySet::UndefinedID; + } + // preset with default value + QBrush theAreaBrush = datasetBrush; + + if( curPropSetId != KDChartPropertySet::UndefinedID ){ + // we can safely call the following functions and ignore their + // return values since they will touch the parameters' values + // if the propSet *contains* corresponding own values only. + int iDummy; + curPropSet.hasOwnAreaBrush( iDummy, theAreaBrush ); + } + painter->setBrush( theAreaBrush ); + + } + QPointArray segment( 4 ); + segment.setPoint( 0, points[0]->point( value ) ); + segment.setPoint( 1, points[0]->point( value+1 ) ); + segment.setPoint( 2, points[0]->point( value+1 ).x(), yCoord ); + segment.setPoint( 3, points[0]->point( value ).x(), yCoord ); + painter->drawPolygon( segment ); + } + + // old: draw the complete area in on go: + /* + // remove the last two points added + point -= 2; + */ + //qDebug("\n111"); + } // if ( mode == Normal || dataset == (int)datasetEnd ) + else { + // don't mess around with the original array; we'll need + // that for the next time through. + + //qDebug("222"); + // no 3d handling for areas yet + QPointArray thisSection = points[0]->copy(); + + thisSection.resize( point + previousPoints.size() ); + // append the previous array (there is guaranteed to be + // one because we are at least the second time through + // here) in reverse order + for ( unsigned int i = 0; i < previousPoints.size(); ++i ) { + thisSection.setPoint( point + i, + previousPoints.point( previousPoints.size() - i - 1 ) ); + //qDebug("\nx: %i",previousPoints.point( previousPoints.size() - i - 1 ).x()); + //qDebug("y: %i",previousPoints.point( previousPoints.size() - i - 1 ).y()); + } + painter->drawPolygon( thisSection ); + } + // draw the line with no brush and outline color + painter->setBrush( Qt::NoBrush ); + painter->setPen( QPen( params()->outlineDataColor(), + params()->outlineDataLineWidth() ) ); + } else { + // line + if( showThreeDLines ) { + // This is a 3D line: + // We draw the line with the data color brush + // and the outline data pen. + painter->setBrush( params()->dataColor( dataset ) ); + painter->setPen( QPen( params()->outlineDataColor(), + params()->outlineDataLineWidth() ) ); + } else { + // This is a 2D line: + // We draw the line with the no brush + // and the data color if no special line color was specified. + painter->setBrush( Qt::NoBrush ); + painter->setPen( default2DPen ); + } + } + + // Neither draw the contour line if this is a pure Point chart + // nor draw it for the last row of a percent area chart. + + if( bDrawLines && + ( (mode != Percent) || !mIsArea || (dataset != (int)datasetEnd) ) ){ + if( showThreeDLines ) { + + // A 3D line needs to be drawn piece-wise + for ( int value = 0; value < point-1; ++value ) { + // if( data->cell( dataset, value ).hasValue() && + // data->cell( dataset, value+1 ).hasValue() ) { + // qDebug( "Draw a segment in dataset %d from %d to %d", dataset, value, value+1 ); + + //store the rotated points ( see project() ) + QPointArray rotatedSegment( 4 ); + rotatedSegment.setPoint( 0, points[0]->point( value )); + rotatedSegment.setPoint( 1, points[0]->point( value+1 ) ); + rotatedSegment.setPoint( 2, points[1]->point( value+1 ) ); + rotatedSegment.setPoint( 3, points[1]->point( value ) ); + + //store the true points without rotation + QPointArray trueSegment( 4 ); + trueSegment.setPoint( 0, oripoints[0]->point( value )); + trueSegment.setPoint( 1, oripoints[0]->point( value+1 ) ); + trueSegment.setPoint( 2, oripoints[1]->point( value+1 ) ); + trueSegment.setPoint( 3, oripoints[1]->point( value ) ); + + // calculate the rotated points position relative to each other + // we will then be able to keep the rotation ( see: project () ) + // by reporting this position relative to the true segment line + //left side pt3 and pt0 + int dx30 = rotatedSegment.point(3).x() - rotatedSegment.point(0).x(); + int dy30 = rotatedSegment.point(3).y() - rotatedSegment.point(0).y(); + //right side pt1 and pt2 + int dx12 = rotatedSegment.point(2).x() - rotatedSegment.point(1).x(); + int dy12 = rotatedSegment.point(2).y() - rotatedSegment.point(1).y(); + + // store and paint the "3D" segment + QPointArray segment( 4 ); + segment.setPoint( 0, trueSegment.point(0) ); + segment.setPoint( 1, trueSegment.point(1) ); + segment.setPoint( 2, trueSegment.point(1).x() + dx12, trueSegment.point(1).y() + dy12 ); + segment.setPoint( 3, trueSegment.point(0).x() + dx30, trueSegment.point(0).y() + dy30); + + + //PENDING Michel 3dlines drawing a segment with showThreeDLines + painter->drawPolygon( segment ); + + + // } else + // qDebug( "Can't draw a segment in dataset %d from %d to %d", dataset, value, value+1 ); + } + } else { + QPoint p1, p2; + // Note: If markers are drawn very near to each other + // and tiny markers are used + // we don't draw the connecting lines. + bool b4PMarkers = KDChartParams::LineMarker4Pixels == markerStyle; + bool bTinyMarkers = + KDChartParams::LineMarker1Pixel == markerStyle || b4PMarkers; + curPropSetId = KDChartPropertySet::UndefinedID; + painter->setPen( default2DPen ); + for ( int value = 0; value < point-1; ++value ) { + p1 = points[0]->point( value ); + p2 = points[0]->point( value+1 ); + + // Determine properties assigned to this cell + // and change the painter if necessarry: + currentDrawMarkers = defaultDrawMarkers; + int cellPropID; + if( data->cellProp( dataset, value, cellPropID ) && + cellPropID != curPropSetId ){ + if( cellPropID != KDChartPropertySet::UndefinedID && + params()->calculateProperties( cellPropID, + curPropSet ) ){ + curPropSetId = cellPropID; + }else{ + curPropSetId = KDChartPropertySet::UndefinedID; + } + // preset with default values + int theLineWidth = default2DPen.width(); + QColor theLineColor = default2DPen.color(); + Qt::PenStyle theLineStyle = default2DPen.style(); + if( curPropSetId != KDChartPropertySet::UndefinedID ){ + // we can safely call the following functions and ignore their + // return values since they will touch the parameters' values + // if the propSet *contains* corresponding own values only. + int iDummy; + curPropSet.hasOwnLineWidth ( iDummy, theLineWidth ); + curPropSet.hasOwnLineColor ( iDummy, theLineColor ); + curPropSet.hasOwnLineStyle ( iDummy, theLineStyle ); + curPropSet.hasOwnShowMarker( iDummy, currentDrawMarkers ); + } + painter->setPen( QPen( theLineColor, + theLineWidth, + theLineStyle ) ); + } + + if( !currentDrawMarkers ){ + //PENDING Michel: drawing a line - not currentMarkers + painter->drawLine( p1, p2 ); + }else{ + int dx = p2.x() - p1.x(); + int dy = p2.y() - p1.y(); + if( !bTinyMarkers || (abs(dx) > 4) || (abs(dy) > 4) ){ + if( bTinyMarkers ) { + double m = !dx ? 100.0 + : !dy ? 0.01 + : ((double)dy / (double)dx); + double am = fabs(m); + int dxx; + int dyy; + if( 0.25 > am ){ + dxx = 3; + dyy = 0; + }else if( 0.67 > am ){ + dxx = 3; + dyy = 1; + }else if( 1.33 > am ){ + dxx = 2; + dyy = 2; + }else if( 4.0 > am ){ + dxx = 1; + dyy = 3; + }else{ + dxx = 0; + dyy = 3; + } + if( 0 > dx ) + dxx *= -1; + if( 0 > dy ) + dyy *= -1; + if( b4PMarkers ){ + if( 0 < dx ) + ++p1.rx(); + else if( 0 > dx ) + ++p2.rx(); + if( 0 < dy ) + ++p1.ry(); + else if( 0 > dy ) + ++p2.ry(); + } + p1.rx() += dxx; p1.ry() += dyy; + p2.rx() -= dxx; p2.ry() -= dyy; + } + //PENDING Michel: drawing a line - currentMarkers + painter->drawLine( p1, p2 ); + } + } + } + } + } + } + + // Save point array for next way through (needed for e.g. stacked + // areas), not for 3D currently + points[0]->resize( point ); + previousPoints = points[0]->copy(); + } + + + // Now draw any extra lines (and/or their markers, resp.) that + // are to be printed IN FRONT of the normal lines: + if( mChartType == KDChartParams::Line ){ + for( dataset = datasetEnd; ( dataset >= (int)datasetStart && dataset >= 0 ); --dataset ) { + + const KDChartParams::LineMarkerStyle + defaultMarkerStyle = params()->lineMarkerStyle( dataset ); + const QPen default2DPen( params()->lineColor().isValid() + ? params()->lineColor() + : params()->dataColor( dataset ), + params()->lineWidth(), + params()->lineStyle( dataset ) ); + + if( ai.bAbscissaHasTrueAxisDtValues ) + ai.numValues = data->cols(); + + for ( int value = 0; value < ai.numValues; ++value ) { + int iVec = static_cast < int > ( datasetEnd-dataset ) * arrayNumValues + value; + if( allPoints[ iVec ].bValid ){ + const MyPoint& mp = allPoints[iVec]; + //qDebug("\np.x() %i p.y() %i", p.x(), p.y() ); + + // -------------------------------------------------------- + // determine any 'extra' properties assigned to this cell + // by traversing the property set chain (if necessary) + // -------------------------------------------------------- + int cellPropID; + if( data->cellProp( dataset, value, cellPropID ) && + cellPropID != curPropSetId ){ + if( cellPropID != KDChartPropertySet::UndefinedID && + params()->calculateProperties( cellPropID, + curPropSet ) ) + curPropSetId = cellPropID; + else + curPropSetId = KDChartPropertySet::UndefinedID; + } + if( curPropSetId != KDChartPropertySet::UndefinedID ){ + drawExtraLinesAndMarkers( + curPropSet, + default2DPen, + defaultMarkerStyle, + mp.p.x(), mp.p.y(), + painter, + ai.abscissaPara, + ordinatePara, + logWidth/1000.0, + logHeight/1000.0, + true ); + } + } + } + } + } + //qDebug(const_cast < KDChartParams* > ( params() )->properties( KDCHART_PROPSET_NORMAL_DATA )->name().latin1()); + //qDebug(const_cast < KDChartParams* > ( params() )->properties( KDCHART_PROPSET_TRANSPARENT_DATA )->name().latin1()); + //qDebug(const_cast < KDChartParams* > ( params() )->properties( KDCHART_PROPSET_HORI_LINE )->name().latin1()); + //qDebug(const_cast < KDChartParams* > ( params() )->properties( KDCHART_PROPSET_VERT_LINE )->name().latin1()); + //qDebug("--"); + } diff --git a/kchart/kdchart/KDChartLinesPainter.h b/kchart/kdchart/KDChartLinesPainter.h new file mode 100644 index 00000000..cd2c40f6 --- /dev/null +++ b/kchart/kdchart/KDChartLinesPainter.h @@ -0,0 +1,86 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTLINESPAINTER_H__ +#define __KDCHARTLINESPAINTER_H__ + +#include "KDChartAxesPainter.h" +#include <KDChartTable.h> +#include <KDChartParams.h> + +class QSize; + +class KDChartLinesPainter : public KDChartAxesPainter +{ + friend class KDChartPainter; + + protected: + KDChartLinesPainter( KDChartParams* params ); + virtual ~KDChartLinesPainter(); + + virtual void paintData( QPainter* painter, + KDChartTableDataBase* data, + bool paint2nd, + KDChartDataRegionList* regions = 0 ); + virtual bool isNormalMode() const; + virtual int clipShiftUp( bool, double ) const; + virtual void 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 ); + + void paintDataInternal( QPainter* painter, + KDChartTableDataBase* data, + bool centerThePoints, + bool drawMarkers, + bool isArea, + bool paint2nd, + KDChartDataRegionList* regions = 0 ); + QPoint project( int x, int y, int z ); + private: + KDChartParams::ChartType mChartType; + bool mCenterThePoints; + bool mDrawMarkers; + bool mIsArea; +}; + +#endif diff --git a/kchart/kdchart/KDChartListTable.h b/kchart/kdchart/KDChartListTable.h new file mode 100644 index 00000000..a11c44e8 --- /dev/null +++ b/kchart/kdchart/KDChartListTable.h @@ -0,0 +1,484 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTLISTTABLE_H__ +#define __KDCHARTLISTTABLE_H__ + +#include <qvaluelist.h> +#include <qshared.h> +#include <qtable.h> + +#include <KDChartDataIntern.h> +#include <KDChartTableBase.h> + +class KDCHART_EXPORT KDChartListTablePrivate : public QShared +{ + public: + KDChartListTablePrivate() : QShared() { + row_count = 0; + col_count = 0; + } + + KDChartListTablePrivate( uint _rows, uint _cols ) : QShared() { + for ( uint i = 0; i < _rows; i++ ) + row_list.append( int() ); + for ( uint j = 0; j < _cols; j++ ) + col_list.append( int() ); + for ( uint k = 0; k < _rows * _cols; k++ ) + matrix.append( KDChartData() ); + col_count = _cols; + row_count = _rows; + } + + KDChartListTablePrivate( const KDChartListTablePrivate& _t ) : + QShared(), + matrix( _t.matrix ), + row_list( _t.row_list ), + col_list( _t.col_list ), + col_count( _t.col_count ), + row_count( _t.row_count ) {} + ~KDChartListTablePrivate() {} + + void expand( uint _rows, uint _cols ) { + if ( _rows > row_count ) { + for ( uint r = 0; r < _rows - row_count; ++r ) { + row_list.append( int() ); + for ( uint i = 0; i < col_count; ++i ) + matrix.append( KDChartData() ); + } + row_count = _rows; + } + if ( _cols > col_count ) { + uint old = col_count; + col_count = _cols; + for ( uint c = 0; c < _cols - old; ++c ) { + col_list.append( int() ); + for ( uint i = 0; i < row_count; ++i ) + matrix.insert( matrix.at( i * col_count + old + c ), KDChartData() ); + } + } + } + + const KDChartData& cell( uint _row, uint _col ) const + { + Q_ASSERT( _row < row_count && _col < col_count ); + return matrix[ static_cast < int > ( _row * col_count + _col ) ]; + } + KDChartData& cell( uint _row, uint _col ) + { + Q_ASSERT( _row < row_count && _col < col_count ); + return matrix[ static_cast < int > ( _row * col_count + _col ) ]; + } + + void setCell( uint _row, uint _col, const KDChartData& _element ) + { + Q_ASSERT( _row < row_count && _col < col_count ); + matrix[ static_cast < int > ( _row * col_count + _col ) ].setAll( _element ); + } + + void clearCell( uint _row, uint _col ) { + Q_ASSERT( _row < row_count && _col < col_count ); + matrix[ static_cast < int > ( _row * col_count + _col ) ].clearValue(); + } + + void clearAllCells() { + for ( uint r = 0; r < row_count; ++r ) + for ( uint c = 0; c < col_count; ++c ) + matrix[ static_cast < int > ( r * col_count + c ) ].clearValue(); + } + + int& row( uint _row ) { + Q_ASSERT( _row < row_count ); + return row_list[ _row ]; + } + + const int& row( uint _row ) const { + Q_ASSERT( _row < row_count ); + return row_list[ _row ]; + } + + void setRow( uint _row, const int& _v ) { + Q_ASSERT( _row < row_count ); + row_list[ _row ] = _v; + } + + int& col( uint _col ) { + Q_ASSERT( _col < col_count ); + return col_list[ _col ]; + } + + + const int& col( uint _col ) const { + Q_ASSERT( _col < col_count ); + return col_list[ _col ]; + } + + void setCol( uint _col, const int& _v ) { + Q_ASSERT( _col < col_count ); + col_list[ _col ] = _v; + } + + void insertColumn( uint _c ) { + Q_ASSERT( _c <= col_count ); + ++col_count; + QValueList < KDChartData > ::Iterator it; + for ( uint i = 0; i < row_count; ++i ) { + it = matrix.at( i * col_count + _c ); + matrix.insert( it, KDChartData() ); + } + + QValueList < int > ::Iterator it2 = col_list.at( _c ); + col_list.insert( it2, int() ); + } + + void insertRow( uint _r ) { + Q_ASSERT( _r <= row_count ); + ++row_count; + QValueList < KDChartData > ::Iterator it = matrix.at( _r * col_count ); + for ( uint i = 0; i < col_count; ++i ) + matrix.insert( it, KDChartData() ); + + QValueList < int > ::Iterator it2 = row_list.at( _r ); + row_list.insert( it2, int() ); + } + + void removeColumn( uint _c ) { + Q_ASSERT( _c < col_count ); + --col_count; + QValueList < KDChartData > ::Iterator it; + for ( uint i = 0; i < row_count; ++i ) { + it = matrix.at( i * col_count + _c ); + matrix.remove( it ); + } + + QValueList < int > ::Iterator it2 = col_list.at( _c ); + col_list.remove( it2 ); + } + + void removeRow( uint _r ) { + Q_ASSERT( _r < row_count ); + --row_count; + QValueList < KDChartData > ::Iterator it = matrix.at( _r * col_count ); + for ( uint i = 0; i < col_count; ++i ) + it = matrix.remove( it ); + + QValueList < int > ::Iterator it2 = row_list.at( _r ); + row_list.remove( it2 ); + } + + QValueList < KDChartData > matrix; + QValueList < int > row_list; + QValueList < int > col_list; + + uint col_count; + uint row_count; +}; + + +class KDChartListTableData : public KDChartTableDataBase +{ + private: + typedef KDChartListTablePrivate Priv; + uint _usedRows, _usedCols; + + public: + /** + * Typedefs + */ + typedef QValueList < KDChartData > ::Iterator Iterator; + typedef QValueList < KDChartData > ::ConstIterator ConstIterator; + + typedef QValueList < int > ::Iterator RowIterator; + typedef QValueList < int > ::ConstIterator ConstRowIterator; + + typedef QValueList < int > ::Iterator ColIterator; + typedef QValueList < int > ::ConstIterator ConstColIterator; + + /** + * API + */ + KDChartListTableData() : + KDChartTableDataBase() + { + sh = new Priv; + _usedCols = 0; + _usedRows = 0; + } + KDChartListTableData( uint _rows, uint _cols ) : + KDChartTableDataBase() + { + sh = new Priv( _rows, _cols ); + _usedRows = _rows; + _usedCols = _cols; + } + + KDChartListTableData( const KDChartListTableData& _t ) : + KDChartTableDataBase( _t ) { + _useUsedRows = _t._useUsedRows; + _useUsedCols = _t._useUsedCols; + _usedRows = _t._usedRows; + _usedCols = _t._usedCols; + sh = _t.sh; + sh->ref(); + setSorted( _t.sorted() ); + } + + virtual ~KDChartListTableData() { + if ( sh->deref() ) + delete sh; + } + + KDChartListTableData& operator=( const KDChartListTableData& t ) { + if ( &t == this ) + return * this; + _useUsedRows = t._useUsedRows; + _useUsedCols = t._useUsedCols; + _usedRows = t._usedRows; + _usedCols = t._usedCols; + t.sh->ref(); + if ( sh->deref() ) + delete sh; + sh = t.sh; + setSorted( t.sorted() ); + return *this; + } + + Iterator begin() { + return sh->matrix.begin(); + } + + ConstIterator begin() const { + return sh->matrix.begin(); + } + + Iterator end() { + return sh->matrix.end(); + } + + ConstIterator end() const { + return sh->matrix.end(); + } + + ColIterator colBegin() { + return sh->col_list.begin(); + } + + ConstColIterator colBegin() const { + return sh->col_list.begin(); + } + + ColIterator colEnd() { + return sh->col_list.end(); + } + + ConstColIterator colEnd() const + { + return sh->col_list.end(); + } + + RowIterator rowBegin() { + return sh->row_list.begin(); + } + + ConstRowIterator rowBegin() const { + return sh->row_list.begin(); + } + + RowIterator rowEnd() { + return sh->row_list.end(); + } + + ConstRowIterator rowEnd() const { + return sh->row_list.end(); + } + + bool isEmpty() const { + return ( sh->col_count == 0 && sh->row_count == 0 ); + } + + uint cols() const { + return sh->col_count; + } + + uint rows() const { + return sh->row_count; + } + + virtual bool cellCoord( uint _row, uint _col, + QVariant& _value, + int coordinate=1 ) const + { + if( _row >= sh->row_count || _col >= sh->col_count ) + return false; + _value = sh->cell( _row, _col ).value( coordinate ); + return true; + } + + virtual bool cellProp( uint _row, uint _col, + int& _prop ) const + { + if( _row >= sh->row_count || _col >= sh->col_count ) + return false; + _prop = sh->cell( _row, _col ).propertySet(); + return true; + } + + virtual void setCell( uint _row, uint _col, + const QVariant& _value1, + const QVariant& _value2=QVariant() ) + { + detach(); + const KDChartData element( _value1, _value2 ); + sh->setCell( _row, _col, element ); + } + + virtual void setProp( uint _row, uint _col, + int _propSet=0 ) + { + sh->cell( _row, _col ).setPropertySet( _propSet ); + } + + void clearCell( uint _row, uint _col ) { + detach(); + sh->clearCell( _row, _col ); + } + + void clearAllCells() { + detach(); + sh->clearAllCells(); + } + + int& row( uint _row ) { + detach(); + return sh->row( _row ); + } + + const int& row( uint _row ) const { + return sh->row( _row ); + } + + void setRow( uint _row, const int& _v ) { + detach(); + sh->setRow( _row, _v ); + } + + int& col( uint _col ) { + detach(); + return sh->col( _col ); + } + + const int& col( uint _col ) const { + return sh->col( _col ); + } + + void setCol( uint _col, const int& _v ) { + detach(); + sh->setCol( _col, _v ); + } + + void insertColumn( uint _c ) { + detach(); + sh->insertColumn( _c ); + ++_usedCols; + } + + void insertRow( uint _r ) { + detach(); + sh->insertRow( _r ); + ++_usedRows; + } + + void removeColumn( uint _c ) { + detach(); + sh->removeColumn( _c ); + if( _usedCols ) + --_usedCols; + } + + void removeRow( uint _r ) { + detach(); + sh->removeRow( _r ); + if( _usedRows ) + --_usedRows; + } + + void expand( uint _rows, uint _cols ) { + detach(); + sh->expand( _rows, _cols ); + // adjust the usedRows / usedCols, if they had been set before + if( _useUsedCols ) + setUsedCols( QMIN( _usedCols, _cols ) ); + if( _useUsedRows ) + setUsedRows( QMIN( _usedRows, _rows ) ); + } + + void setUsedRows( uint _rows ) { + Q_ASSERT( _rows <= rows() ); + if( _usedRows < _rows ) + setSorted( false ); + _usedRows = _rows; + _useUsedRows = true; + } + + uint usedRows() const { + return _useUsedRows ? _usedRows : rows(); + } + + void setUsedCols( uint _cols ) { + Q_ASSERT( _cols <= cols() ); + if( _usedCols < _cols ) + setSorted( false ); + _usedCols = _cols; + _useUsedCols = true; + } + + uint usedCols() const { + return _useUsedCols ? _usedCols : cols(); + } + + private: + /** + * Helpers + */ + void detach() { + if ( sh->count > 1 ) { + sh->deref(); + sh = new Priv( *sh ); + } + setSorted( false ); + } + + /** + * Variables + */ + Priv* sh; +}; + +#endif +// __KDCHARTLISTTABLE_H__ + diff --git a/kchart/kdchart/KDChartNotEnoughSpaceException.h b/kchart/kdchart/KDChartNotEnoughSpaceException.h new file mode 100644 index 00000000..b97adde7 --- /dev/null +++ b/kchart/kdchart/KDChartNotEnoughSpaceException.h @@ -0,0 +1,50 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTNOTENOUGHSPACEEXCEPTION_H__ +#define __KDCHARTNOTENOUGHSPACEEXCEPTION_H__ + +#ifdef USE_EXCEPTIONS + +#include <qstring.h> + +#include "KDChartGlobal.h" + +/** + This exception is thrown when there is not enough space for + drawing all selected parts of a chart. + */ +class KDCHART_EXPORT KDChartNotEnoughSpaceException +{ + public: + KDChartNotEnoughSpaceException(); +}; + +#endif + +#endif diff --git a/kchart/kdchart/KDChartObjectFactory.cpp b/kchart/kdchart/KDChartObjectFactory.cpp new file mode 100644 index 00000000..61063901 --- /dev/null +++ b/kchart/kdchart/KDChartObjectFactory.cpp @@ -0,0 +1,531 @@ +#include "KDChartObjectFactory.h" +#include <KDChartParams.h> +#include <KDChartTable.h> +#include <qsargument.h> +#include <qdatetime.h> +#include <KDChartEnums.h> +#include <qfont.h> +#include <KDChartTextPiece.h> +#include <qcolor.h> +#include <KDChartWidget.h> +#include <KDChartPropertySet.h> +#include "factories/QtFactory.h" +#include "factories/QFontFactory.h" + +// PENDING(blackie) Clean up code in this file, so it uses the correct getter methods like getBool. +KDChartObjectFactory::KDChartObjectFactory() +{ + registerClass( QString::fromLatin1( "KDChartWidget" ) ); + registerClass( QString::fromLatin1( "KDChartParams" ), new KDChartParams ); + registerClass( QString::fromLatin1( "KDChartTableData" ) ); + registerClass( QString::fromLatin1( "KDChartAxisParams" ), new KDChartAxisParams ); + registerClass( QString::fromLatin1( "KDChartEnums" ), 0, new KDChartEnums ); + registerClass( QString::fromLatin1( "KDChartTextPiece" ) ); + registerClass( QString::fromLatin1( "KDChartCustomBox" ), new KDChartCustomBox ); + registerClass( QString::fromLatin1( "KDChartPropertySet" ), new KDChartPropertySet ); + registerClass( QString::fromLatin1( "KDFrame" ), QString::null, new KDFrame ); + + registerClass( QString::fromLatin1( "Qt" ), QString::null, new QtFactory() ); + registerClass( QString::fromLatin1( "QFont" ), QString::null, new QFontFactory ); +} + +QObject* KDChartObjectFactory::create( const QString& className, const QSArgumentList& args, QObject* /*context*/ ) +{ + if ( className == QString::fromLatin1( "KDChartWidget" ) ) + return createKDChartWidget( args ); + + if ( className == QString::fromLatin1("KDChartParams") ) + return new KDChartParams(); + + else if ( className == QString::fromLatin1("KDChartTableData") ) + return createKDChartTableData( args ); + + else if ( className == QString::fromLatin1("KDChartAxisParams") ) + return new KDChartAxisParams(); + + + else if ( className == QString::fromLatin1( "KDChartTextPiece" ) ) + return createKDChartTextPiece( args ); + + else if ( className == QString::fromLatin1( "KDChartCustomBox" ) ) + return createKDChartCustomBox( args ); + + else if ( className == QString::fromLatin1( "KDChartPropertySet" ) ) + return createKDChartPropertySet( args ); + + else + return 0; +} + +QObject* KDChartObjectFactory::createKDChartWidget( const QSArgumentList& args ) +{ + if ( !checkArgCount( "KDChartWidget", args.count(), 2, 2 ) ) return 0; + if ( !checkArgsIsQtClass( args, 1, "KDChartParams", "KDChartWidget" ) ) return 0; + if ( !checkArgsIsQtClass( args, 2, "KDChartTableDataBase", "KDChartWidget" ) ) return 0; + KDChartParams* params = static_cast<KDChartParams*>( args[0].qobject() ); + KDChartTableDataBase* data = static_cast<KDChartTableDataBase*>( args[1].qobject() ); + + return new KDChartWidget( params, data ); + +} + +QObject* KDChartObjectFactory::createKDChartTableData( const QSArgumentList& args ) +{ + if ( args.count() != 2 ) { + throwError( QObject::tr( "wrong number of arguments to KDChartTableData" ) ); + return 0; + } + QSArgument arg1 = args[0]; + QSArgument arg2 = args[1]; + + if ( arg1.type() != QSArgument::Variant || !isNumber(arg1.variant()) ) { + throwError( QObject::tr( "wrong type for argument 1 of KDChartTableData" ) ); + return 0; + } + if ( arg2.type() != QSArgument::Variant || !isNumber(arg2.variant() ) ) { + throwError( QObject::tr( "wrong type for argument 2 of KDChartTableData" ) ); + return 0; + } + return new KDChartTableData( arg1.variant().toUInt(), arg2.variant().toUInt() ); +} + + +QObject* KDChartObjectFactory::createKDChartTextPiece( const QSArgumentList& args ) +{ + if ( !checkArgCount( "KDChartTextPiece", args.count(), 2, 2 ) ) return 0; + QSArgument arg1 = args[0]; + QSArgument arg2 = args[1]; + + QString str; + if ( !getString( args, 1, &str, "KDChartTextPiece" ) ) return 0; + if ( !checkIsQtVariant( args, 2, QVariant::Font, QString::fromLatin1( "QFont" ), "KDChartTextPiece" ) ) return 0; + QFont font = args[1].variant().toFont(); + + return new KDChartTextPiece( str, font ); +} + +QObject* KDChartObjectFactory::createKDChartCustomBox( const QSArgumentList& args ) +{ + int tmp; + + if ( args.count() == 0 ) + return new KDChartCustomBox(); + + + if ( args[0].type() == QSArgument::Variant && isNumber( args[0].variant() ) ) { + if ( args.count() < 8 ) { + throwError( QObject::tr( "Too few arguments to KDChartCustomBox" ) ); + return 0; + } + + int rotation; + if ( !getNumber( args, 1, &rotation, "KDChartCustomBox" ) ) return 0; + + if (!checkArgsIsQtClass( args, 2, "KDChartTextPiece", "KDChartCustomBox" )) return 0; + KDChartTextPiece* content = static_cast<KDChartTextPiece*>( args[1].qobject() ); + + int fontSize; + if ( !getNumber( args, 3, &fontSize, "KDChartCustomBox" ) ) return 0; + + + bool fontScaleGlobal; + if ( !getBool( args, 4, &fontScaleGlobal, "KDChartCustomBox" ) ) return 0; + + int deltaX; + if ( !getNumber( args, 5, &deltaX, "KDChartCustomBox" ) ) return 0; + + int deltaY; + if ( !getNumber( args, 6, &deltaY, "KDChartCustomBox" ) ) return 0; + + int width; + if ( !getNumber( args, 7, &width, "KDChartCustomBox" ) ) return 0; + + int height; + if ( !getNumber( args, 8, &height, "KDChartCustomBox" ) ) return 0; + + QColor color = Qt::black; + if ( args.count() >= 9 ) { + if ( !checkIsQtVariant( args, 9, QVariant::Color, QString::fromLatin1( "QColor" ), "KDChartCustomBox" ) ) return 0; + color = args[8].variant().toColor(); + } + + QBrush paper = Qt::NoBrush; + if ( args.count() >= 10 ) { + if ( !checkIsQtVariant( args, 10, QVariant::Brush, QString::fromLatin1( "QBrush" ), "KDChartCustomBox" ) ) return 0; + paper = args[9].variant().toBrush(); + } + + uint area = KDChartEnums::AreaInnermost; + if ( args.count() >= 11 ) { + if ( !getNumber( args, 11, &area, "KDChartCustomBox" ) ) return 0; + } + + KDChartEnums::PositionFlag position = KDChartEnums::PosTopLeft; + if ( args.count() >= 12 ) { + if ( !getNumber( args, 12, &tmp, "KDChartCustomBox" ) ) return 0; + position = (KDChartEnums::PositionFlag) tmp; + } + + uint align = Qt::AlignTop + Qt::AlignLeft; + if ( args.count() >= 13 ) { + if ( !getNumber( args, 13, &align, "KDChartCustomBox" ) ) return 0; + } + + uint dataRow = 0; + if ( args.count() >= 14 ) { + if ( !getNumber( args, 14, &dataRow, "KDChartCustomBox" ) ) return 0; + } + + uint dataCol = 0; + if ( args.count() >= 15 ) { + if ( !getNumber( args, 15, &dataCol, "KDChartCustomBox" ) ) return 0; + } + + uint data3rd = 0; + if ( args.count() >= 16 ) { + if ( !getNumber( args, 16, &data3rd, "KDChartCustomBox" ) ) return 0; + } + + uint deltaAlign = KDCHART_AlignAuto; + if ( args.count() >= 17 ) { + if ( !getNumber( args, 17, &deltaAlign, "KDChartCustomBox" ) ) return 0; + } + + bool deltaScaleGlobal = true; + if ( args.count() >= 18 ) { + if ( !getBool( args, 18, &deltaScaleGlobal, "KDChartCustomBox" ) ) return 0; + } + + return new KDChartCustomBox( rotation, *content, fontSize, fontScaleGlobal, deltaX, deltaY, + width, height, color, paper, area, position, align, dataRow, + dataCol, data3rd, deltaAlign, deltaScaleGlobal ); + } + + if (!checkArgsIsQtClass( args, 1, "KDChartTextPiece", "KDChartCustomBox" )) return 0; + KDChartTextPiece* content = static_cast<KDChartTextPiece*>( args[0].qobject() ); + + if ( args.count() < 2 ) { + throwError( QObject::tr( "Too few arguments to KDChartCustomBox" ) ); + return 0; + } + + int fontSize; + if ( ! getNumber( args, 2, &fontSize, "KDChartCustomBox" ) ) return 0; + + bool fontScaleGlobal = true; + if ( args.count() > 2 && !getBool( args, 3, &fontScaleGlobal, "KDChartCustomBox" ) ) return 0; + + if ( args.count() <= 3 ) + return new KDChartCustomBox( *content, fontSize, fontScaleGlobal ); + + checkArgCount( "KDChartCustomBox", args.count(), 7, 17 ); + + int deltaX; + if ( !getNumber( args, 4, &deltaX, "KDChartCustomBox" ) ) return 0; + + int deltaY; + if ( !getNumber( args, 5, &deltaY, "KDChartCustomBox" ) ) return 0; + + int width; + if ( !getNumber( args, 6, &width, "KDChartCustomBox" ) ) return 0; + + int height; + if ( !getNumber( args, 7, &height, "KDChartCustomBox" ) ) return 0; + + + QColor color = Qt::black; + if ( args.count() >= 8 ) { + if ( !checkIsQtVariant( args, 8, QVariant::Color, QString::fromLatin1( "QColor" ), "KDChartCustomBox" ) ) return 0; + color = args[7].variant().toColor(); + } + + QBrush paper = Qt::NoBrush; + if ( args.count() >= 9 ) { + if ( !checkIsQtVariant( args, 9, QVariant::Brush, QString::fromLatin1( "QBrush" ), "KDChartCustomBox" ) ) return 0; + paper = args[8].variant().toBrush(); + } + + uint area = KDChartEnums::AreaInnermost; + if ( args.count() >= 10 ) { + if ( !getNumber( args, 10, &area, "KDChartCustomBox" ) ) return 0; + } + + KDChartEnums::PositionFlag position = KDChartEnums::PosTopLeft; + if ( args.count() >= 11 ) { + if ( !getNumber( args, 11, &tmp, "KDChartCustomBox" ) ) return 0; + position = (KDChartEnums::PositionFlag) tmp; + } + + uint align = Qt::AlignTop + Qt::AlignLeft; + if ( args.count() >= 12 ) { + if ( !getNumber( args, 12, &align, "KDChartCustomBox" ) ) return 0; + } + + uint dataRow = 0; + if ( args.count() >= 13 ) { + if ( !getNumber( args, 13, &dataRow, "KDChartCustomBox" ) ) return 0; + } + + uint dataCol = 0; + if ( args.count() >= 14 ) { + if ( !getNumber( args, 14, &dataCol, "KDChartCustomBox" ) ) return 0; + } + + uint data3rd = 0; + if ( args.count() >= 15 ) { + if ( !getNumber( args, 15, &data3rd, "KDChartCustomBox" ) ) return 0; + } + + uint deltaAlign = KDCHART_AlignAuto; + if ( args.count() >= 16 ) { + if ( !getNumber( args, 16, &deltaAlign, "KDChartCustomBox" ) ) return 0; + } + + bool deltaScaleGlobal = true; + if ( args.count() >= 17 ) { + if ( !getBool( args, 17, &deltaScaleGlobal, "KDChartCustomBox" ) ) return 0; + } + + + return new KDChartCustomBox( *content, fontSize, fontScaleGlobal, deltaX, deltaY, width, height, color, + paper, area, position, align, dataRow, dataCol, data3rd, deltaAlign, deltaScaleGlobal ); +} + +QObject* KDChartObjectFactory::createKDChartPropertySet(const QSArgumentList& args ) +{ + if ( args.count() == 0 ) + return new KDChartPropertySet(); + + QString name; + if ( !getString( args, 1, &name, "KDChartPropertySet" ) ) return 0; + + if ( args.count() == 1 ) + return new KDChartPropertySet( name ); + + int i; + if ( !getNumber( args, 2, &i, "KDChartPropertySet" ) ) return 0; + if ( args.count() == 2 ) + return new KDChartPropertySet( name, i ); + + if ( !checkArgCount( "KDChartPropertySet", args.count(), 33, 33 ) ) return 0; + + int idLineWidth = i; + + int lineWidth; + if ( !getNumber( args, 3,&lineWidth, "KDChartPropertySet" ) ) return 0; + + int idLineColor; + if ( !getNumber( args, 4,&idLineColor, "KDChartPropertySet" ) ) return 0; + + + if ( !checkIsQtVariant( args, 5, QVariant::Color, QString::fromLatin1( "QColor" ), "KDChartPropertySet" ) ) return 0; + QColor lineColor = args[4].variant().toColor(); + + int idLineStyle; + if ( !getNumber( args, 6,&idLineStyle, "KDChartPropertySet" ) ) return 0; + + + + if ( !getNumber( args, 7,&i, "KDChartPropertySet" ) ) return 0; + Qt::PenStyle lineStyle = (Qt::PenStyle) i; + + int idShowMarker; + if ( !getNumber( args, 8,&idShowMarker, "KDChartPropertySet" ) ) return 0; + + bool showMarker; + if ( !getBool( args, 9, &showMarker, "KDChartPropertySet" ) ) return 0; + + int idExtraLinesAlign; + if ( !getNumber( args, 10,&idExtraLinesAlign, "KDChartPropertySet" ) ) return 0; + + uint extraLinesAlign; + if ( !getNumber( args, 11,&extraLinesAlign, "KDChartPropertySet" ) ) return 0; + + int idExtraLinesInFront; + if ( !getNumber( args, 12,&idExtraLinesInFront, "KDChartPropertySet" ) ) return 0; + + bool extraLinesInFront; + if ( !getBool( args, 13, &extraLinesInFront, "KDChartPropertySet" ) ) return 0; + + int idExtraLinesLength; + if ( !getNumber( args, 14,&idExtraLinesLength, "KDChartPropertySet" ) ) return 0; + + int extraLinesLength; + if ( !getNumber( args, 15,&extraLinesLength, "KDChartPropertySet" ) ) return 0; + + int idExtraLinesWidth; + if ( !getNumber( args, 16,&idExtraLinesWidth, "KDChartPropertySet" ) ) return 0; + + int extraLinesWidth; + if ( !getNumber( args, 17,&extraLinesWidth, "KDChartPropertySet" ) ) return 0; + + int idExtraLinesColor; + if ( !getNumber( args, 18,&idExtraLinesColor, "KDChartPropertySet" ) ) return 0; + + if ( !checkIsQtVariant( args, 19, QVariant::Color, QString::fromLatin1( "QColor" ), "KDChartPropertySet" ) ) return 0; + QColor extraLinesColor = args[18].variant().toColor(); + + int idExtraLinesStyle; + if ( !getNumber( args, 20,&idExtraLinesStyle, "KDChartPropertySet" ) ) return 0; + + if ( !getNumber( args, 21,&i, "KDChartPropertySet" ) ) return 0; + Qt::PenStyle extraLinesStyle = (Qt::PenStyle) i; + + int idExtraMarkersAlign; + if ( !getNumber( args, 22,&idExtraMarkersAlign, "KDChartPropertySet" ) ) return 0; + + uint extraMarkersAlign; + if ( !getNumber( args, 23,&extraMarkersAlign, "KDChartPropertySet" ) ) return 0; + + int idExtraMarkersSize; + if ( !getNumber( args, 24,&idExtraMarkersSize, "KDChartPropertySet" ) ) return 0; + + if ( !checkIsQtVariant( args, 25, QVariant::Size, QString::fromLatin1( "QSize" ), "KDChartPropertySet" ) ) return 0; + QSize extraMarkersSize = args[24].variant().toSize(); + + int idExtraMarkersColor; + if ( !getNumber( args, 26,&idExtraMarkersColor, "KDChartPropertySet" ) ) return 0; + + if ( !checkIsQtVariant( args, 27, QVariant::Color, QString::fromLatin1( "QColor" ), "KDChartPropertySet" ) ) return 0; + QColor extraMarkersColor = args[26].variant().toColor(); + + int idExtraMarkersStyle; + if ( !getNumber( args, 28,&idExtraMarkersStyle, "KDChartPropertySet" ) ) return 0; + + int extraMarkersStyle; + if ( !getNumber( args, 29,&extraMarkersStyle, "KDChartPropertySet" ) ) return 0; + + int idShowBar; + if ( !getNumber( args, 30,&idShowBar, "KDChartPropertySet" ) ) return 0; + + bool showBar; + if ( !getBool( args, 31, &showBar, "KDChartPropertySet" ) ) return 0; + + int idBarColor; + if ( !getNumber( args, 32,&idBarColor, "KDChartPropertySet" ) ) return 0; + + if ( !checkIsQtVariant( args, 33, QVariant::Color, QString::fromLatin1( "QColor" ), "KDChartPropertySet" ) ) return 0; + QColor barColor = args[32].variant().toColor(); + + KDChartPropertySet* set = new KDChartPropertySet; + set->setName( name ); + set->setLineWidth( idLineWidth, lineWidth ); + set->setLineColor( idLineColor, lineColor ); + set->setLineStyle( idLineStyle, lineStyle ); + set->setShowMarker( idShowMarker, showMarker ); + set->setExtraLinesAlign( idExtraLinesAlign, extraLinesAlign ); + set->setExtraLinesInFront( idExtraLinesInFront, extraLinesInFront ); + set->setExtraLinesLength( idExtraLinesLength, extraLinesLength ); + set->setExtraLinesWidth( idExtraLinesWidth, extraLinesWidth ); + set->setExtraLinesColor( idExtraLinesColor, extraLinesColor ); + set->setExtraLinesStyle( idExtraLinesStyle, extraLinesStyle ); + set->setExtraMarkersAlign( idExtraMarkersAlign, extraMarkersAlign ); + set->setExtraMarkersSize( idExtraMarkersSize, extraMarkersSize ); + set->setExtraMarkersColor( idExtraMarkersColor, extraMarkersColor ); + set->setExtraMarkersStyle( idExtraMarkersStyle, extraMarkersStyle ); + set->setShowBar( idShowMarker, showBar ); + set->setBarColor( idBarColor, barColor ); + return set; +} + + +bool KDChartObjectFactory::isNumber( const QVariant& v ) +{ + return ( v.type() == QVariant::Int || v.type() == QVariant::UInt || v.type() == QVariant::Double ); +} + + // PENDING(blackie) rework order of parameters so it matches the other methods +bool KDChartObjectFactory::checkArgCount( const QString& className, int count, int min, int max ) +{ + if ( count < min ) { + throwError( QObject::tr( "Too few arguments when creating %1 object." ).arg( className ) ); + return false; + } + if ( count > max ) { + throwError( QObject::tr( "Too many arguments when creating %1 object." ).arg( className ) ); + return false; + } + return true; +} + + +bool KDChartObjectFactory::checkArgsIsQtClass( const QSArgumentList& args, int index, const char* expected, const char* constructing ) +{ + const QSArgument& arg = args[index-1]; + if ( arg.type() != QSArgument::QObjectPtr || !arg.qobject()->inherits( expected ) ) { + throwError( QObject::tr( "Invalid type for argument no %1 to %2, must be a %3" ).arg(index).arg(constructing).arg(expected) ); + return false; + } + return true; +} + + +bool KDChartObjectFactory::getString( const QSArgumentList& args, int index, QString* str, const char* constructing ) +{ + const QSArgument& arg = args[index-1]; + if ( arg.type() != QSArgument::Variant || arg.variant().type() != QVariant::String ) { + throwError( QObject::tr( "Invalid type for argument %1 to %2, must be a string" ).arg(index).arg(constructing) ); + return false; + } + else { + *str = arg.variant().toString(); + return true; + } +} + +bool KDChartObjectFactory::getNumber( const QSArgumentList& args, int index, double* number, const char* constructing ) +{ + const QSArgument& arg = args[index-1]; + if ( arg.type() != QSArgument::Variant || !isNumber(arg.variant()) ) { + throwError( QObject::tr( "Invalid type for argument %1 to %2, must be a number" ).arg(index).arg( constructing ) ); + return false; + } + else { + *number = arg.variant().toDouble(); + return true; + } +} + +bool KDChartObjectFactory::getNumber( const QSArgumentList& args, int index, int* number, const char* constructing ) +{ + double tmp; + bool ok = getNumber( args, index, &tmp, constructing ); + *number = static_cast<int>( tmp ); + return ok; +} + +bool KDChartObjectFactory::getNumber( const QSArgumentList& args, int index, uint* number, const char* constructing ) +{ + double tmp; + bool ok = getNumber( args, index, &tmp, constructing ); + *number = static_cast<uint>( tmp ); + return ok; +} + + + +bool KDChartObjectFactory::getBool( const QSArgumentList& args, int index, bool* b, const char* constructing ) +{ + const QSArgument& arg = args[index-1]; + if ( arg.type() != QSArgument::Variant || arg.variant().type() != QVariant::Bool ) { + throwError( QObject::tr( "Invalid type for argument %1 to %2, must be a boolean" ).arg(index).arg( constructing ) ); + return false; + } + else { + *b = arg.variant().toBool(); + return true; + } +} + +bool KDChartObjectFactory::checkIsQtVariant( const QSArgumentList& args, int index, QVariant::Type expected, const QString& variantName, const char* constructing ) +{ + const QSArgument& arg = args[index-1]; + if ( arg.type() != QSArgument::Variant || arg.variant().type() != expected ) { + throwError( QObject::tr( "Invalid type for argument %1 to %2, must be a %3").arg(index).arg(constructing).arg(variantName) ); + return false; + } + else + return true; +} + + diff --git a/kchart/kdchart/KDChartObjectFactory.h b/kchart/kdchart/KDChartObjectFactory.h new file mode 100644 index 00000000..15fdb943 --- /dev/null +++ b/kchart/kdchart/KDChartObjectFactory.h @@ -0,0 +1,37 @@ +#ifndef KDCHARTQSA_H +#define KDCHARTQSA_H +#include <qsobjectfactory.h> + +class KDChartObjectFactory :public QSObjectFactory { + +public: + KDChartObjectFactory(); + virtual QObject* create( const QString& className, const QSArgumentList& args, QObject* context ); +protected: + QObject* createKDChartWidget( const QSArgumentList& args ); + QObject* createKDChartTableData( const QSArgumentList& args ); + QObject* createQDateTime( const QSArgumentList& args ); + QObject* createQDate( const QSArgumentList& args ); + QObject* createQTime( const QSArgumentList& args ); + QObject* createKDChartTextPiece( const QSArgumentList& args ); + QObject* createQFont( const QSArgumentList& args ); + QObject* createKDChartCustomBox( const QSArgumentList& args ); + QObject* createQColor(const QSArgumentList& args ); + QObject* createKDChartPropertySet(const QSArgumentList& args ); + + bool isNumber( const QVariant& ); + bool checkArgCount( const QString& className, int count, int min, int max ); + bool checkArgsIsQtClass( const QSArgumentList& args, int index, const char* expected, const char* constructing ); + bool getString( const QSArgumentList& arg, int index, QString* str, const char* constructing ); + + bool getNumber( const QSArgumentList& args, int index, double* number, const char* constructing ); + bool getNumber( const QSArgumentList& args, int index, int* number, const char* constructing ); + bool getNumber( const QSArgumentList& args, int index, uint* number, const char* constructing ); + + bool getBool( const QSArgumentList& arg, int index, bool* b, const char* constructing ); + bool checkIsQtVariant( const QSArgumentList& arg, int index, QVariant::Type expected, const QString& variantName, const char* constructing ); +}; + + +#endif /* KDCHARTQSA_H */ + diff --git a/kchart/kdchart/KDChartPainter.cpp b/kchart/kdchart/KDChartPainter.cpp new file mode 100644 index 00000000..7643589c --- /dev/null +++ b/kchart/kdchart/KDChartPainter.cpp @@ -0,0 +1,2984 @@ +/* -*- 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. + ** + **********************************************************************/ +#include <KDChartParams.h> +#if defined ( SUN7 ) || defined (_SGIAPI) || defined ( Q_WS_WIN) + #include <math.h> +#else + #include <cmath> + #include <stdlib.h> +#endif + +#include <KDDrawText.h> +#include <KDChartPainter.h> +#include <KDChartEnums.h> +#include <KDChartParams.h> +#include <KDChartCustomBox.h> +#include <KDChartTableBase.h> +#include <KDChartDataRegion.h> +#include <KDChartUnknownTypeException.h> +#include <KDChartNotEnoughSpaceException.h> +#include <KDChartBarPainter.h> +#include <KDChartAreaPainter.h> +#include <KDChartLinesPainter.h> +#include <KDChartPiePainter.h> +#include <KDChartPolarPainter.h> +#include <KDChartRingPainter.h> +#include <KDChartHiLoPainter.h> +#include <KDChartBWPainter.h> +#include <KDChartTextPiece.h> + +#include <KDChart.h> // for static method KDChart::painterToDrawRect() + +#include <qpainter.h> +#include <qpaintdevice.h> +#include <qpaintdevicemetrics.h> + +#define DEGTORAD(d) (d)*M_PI/180 + + +/** + \class KDChartPainter KDChartPainter.h + + \brief An abstract base class that defines an interface for classes + that implement chart drawing. + + Applications don't use this class directly (except for + registering/unregistering, see below) new chart implementations, + but instead use the method KDChart::paint() which takes care of the + correct creation and deletion of the painter implementation + used. Or they use KDChartWidget which handles everything + automatically. + + This class cannot be instantiated directly. Even the concrete + subclasses are not instantiated directly, but are instantiated via + KDChartPainter::create() which creates a subclass according to the + parameters passed. + + Application developers can provide their own chart implementations + by subclassing from KDChartPainter, instantiating their subclass + and registering their implementation with + KDChartPainter::registerPainter(). These registrations can be + removed with KDChartPainter::unregisterPainter(). + */ + +/** + Constructor. Will only be called by subclass constructors since + this class can never be instantiated directly. + + \param params the parameters of the chart to be drawn + */ +KDChartPainter::KDChartPainter( KDChartParams* params ) : +_outermostRect( QRect(QPoint(0,0), QSize(0,0))), +_legendTitle( 0 ), +_params( params ), +_legendNewLinesStartAtLeft( true ), +_legendTitleHeight( 0 ), +_legendTitleWidth( 0 ), +_legendTitleMetricsHeight( 0 ) +{ + // This constructor intentionally left blank so far; we cannot setup the + // geometry yet since we do not know the size of the painter. +} + +/** + Destructor. Cleans up any data structures that might have been allocated in + the meantime. + */ +KDChartPainter::~KDChartPainter() +{ + delete _legendTitle; +} + +bool KDChartPainter::calculateAllAxesLabelTextsAndCalcValues( + QPainter*, + KDChartTableDataBase*, + double, + double, + double& ) +{ + // This function intentionally returning false; it is implemented + // by the KDChartAxesPainter class only. + return false; +} + +/** + Creates an object of a concrete subclass of KDChartPainter that + KDChart::paint() (and consequently, the application) can use to + have charts painted. The subclass is determined on the base of the + params parameter which among other things indicates the type of the + chart. + + \param params the parameter set which is used to determine the + painter implementation to be used + \return a pointer to an object of a subclass of KDChartPainter that + can be used to draw charts as defined by the \a params + parameter. Returns 0 if there is no registered + KDChartPainter subclass for the type specified in \a params. This + can only happen with user-defined chart types. + */ +KDChartPainter* KDChartPainter::create( KDChartParams* params, bool make2nd ) +{ + KDChartParams::ChartType cType = make2nd + ? params->additionalChartType() + : params->chartType(); + switch ( cType ) + { + case KDChartParams::Bar: + return new KDChartBarPainter( params ); + case KDChartParams::Line: + return new KDChartLinesPainter( params ); + case KDChartParams::Area: + return new KDChartAreaPainter( params ); + case KDChartParams::Pie: + return new KDChartPiePainter( params ); + case KDChartParams::Ring: + return new KDChartRingPainter( params ); + case KDChartParams::HiLo: + return new KDChartHiLoPainter( params ); + case KDChartParams::BoxWhisker: + return new KDChartBWPainter( params ); + case KDChartParams::Polar: + return new KDChartPolarPainter( params ); + case KDChartParams::NoType: + default: + return 0; + } +} + + +/** + Registers a user-defined painter implementation which is identified + by a string. If there is already a painter implementation + registered under that name, the old registration will be deleted. + + KDChartPainter does not assume ownership of the registered painter, + but you should unregister a painter before deleting an + implementation object to avoid that that object is called after its + deletion. + + \param painterName the name under which the painter implementation + should be registered. This will be matched against the user-defined + chart type name in the KDChartParams structure. + \param painter an implementation object of a user-defined chart + implementation + */ +void KDChartPainter::registerPainter( const QString& /*painterName*/, + KDChartPainter* /*painter*/ ) +{ + // PENDING(kalle) Implement this + qDebug( "Sorry, not implemented: KDChartPainter::registerPainter()" ); +} + + +/** + Unregisters a user-defined painter implementation. Does not delete + the implementation object. If no implementation has been registered + under this name, an exception is thrown if KDChart is compiled with + exceptions, otherwise nothing happens. + + \param the name under which the painter implementation is + registered + */ +void KDChartPainter::unregisterPainter( const QString& /*painterName*/ ) +{ + // PENDING(kalle) Implement this + qDebug( "Sorry, not implemented: KDChartPainter::unregisterPainter()" ); +} + + +/** + Paints the chart for which this chart painter is configured on a + QPainter. This is the method that bundles all the painting + functions that paint specific parts of the chart like axes or + legends. Subclasses can override this method, but should rarely + need to do so. + + \param painter the QPainter onto which the chart should be drawn + \param data the data which will be displayed as a chart + \param regions a pointer to a region list that will be filled with + regions representing the data segments if not null + */ +void KDChartPainter::paint( QPainter* painter, + KDChartTableDataBase* data, + bool paintFirst, + bool paintLast, + KDChartDataRegionList* regions, + const QRect* rect, + bool mustCalculateGeometry ) +{ + + + if( paintFirst && regions ) + regions->clear(); + + // Protect against non-existing data + if( data->usedCols() == 0 && data->usedRows() == 0 ) + return ; + + QRect drawRect; + //Pending Michel: at this point we have to setupGeometry + if( mustCalculateGeometry || _outermostRect.isNull() ){ + if( rect ) + drawRect = *rect; + else if( !KDChart::painterToDrawRect( painter, drawRect ) ){ + qDebug("ERROR: KDChartPainter::paint() could not calculate the drawing area."); + return; + } + setupGeometry( painter, data, drawRect ); + } + else + drawRect = _outermostRect; + + //qDebug("A2: _legendRect:\n%i,%i\n%i,%i", _legendRect.left(),_legendRect.top(),_legendRect.right(),_legendRect.bottom() ); + + + // Note: In addition to the below paintArea calls there might be several + // other paintArea calls regarding to the BASE areas (AreaAxisBASE, + // AreaHdFtBASE, AreaCustomBoxesBASE). + // These additional calls result in smaller areas being drawn inside + // on the larger ones specifies here. + if ( paintFirst ) { + paintArea( painter, KDChartEnums::AreaOutermost ); + paintArea( painter, KDChartEnums::AreaInnermost ); + + paintArea( painter, KDChartEnums::AreaDataAxesLegendHeadersFooters ); + + paintArea( painter, KDChartEnums::AreaHeaders ); + paintArea( painter, KDChartEnums::AreaFooters ); + // header areas are drawn in the following order: + // 1st center: main header, left main header, right main header + // 2nd above: header #0, left header #0, right header #0 + // 3rd below: header #2, left header #2, right header #2 + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeader ); + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeaderL ); + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeaderR ); + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeader0 ); + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeader0L ); + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeader0R ); + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeader2 ); + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeader2L ); + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeader2R ); + // footer areas are drawn in the same order as the header areas: + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooter ); + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooterL ); + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooterR ); + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooter0 ); + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooter0L ); + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooter0R ); + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooter2 ); + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooter2L ); + paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooter2R ); + + paintHeaderFooter( painter, data ); + + paintArea( painter, KDChartEnums::AreaDataAxesLegend ); + paintArea( painter, KDChartEnums::AreaDataAxes ); + paintArea( painter, KDChartEnums::AreaAxes ); + for( int axis = KDChartAxisParams::AxisPosSTART; + KDChartAxisParams::AxisPosEND >= axis; ++axis ) + paintArea( painter, KDChartEnums::AreaAxisBASE + axis ); + paintArea( painter, KDChartEnums::AreaData ); + paintAxes( painter, data ); + } + + painter->save(); + paintData( painter, data, !paintFirst, regions ); + painter->restore(); + + if ( paintLast ) { + // paint the frame lines of all little data region areas + // on top of all data representations + paintDataRegionAreas( painter, regions ); + if( KDChartParams::Bar != params()->chartType() || + KDChartParams::BarMultiRows != params()->barChartSubType() ) + paintDataValues( painter, data, regions ); + if (params()->legendPosition()!=KDChartParams::NoLegend) + paintArea( painter, KDChartEnums::AreaLegend ); + paintLegend( painter, data ); + paintCustomBoxes( painter, regions ); + } +} + + +/** + Paints an area frame. + */ +void KDChartPainter::paintArea( QPainter* painter, + uint area, + KDChartDataRegionList* regions, + uint dataRow, + uint dataCol, + uint data3rd ) +{ + if( KDChartEnums::AreaCustomBoxesBASE != (KDChartEnums::AreaBASEMask & area) ){ + bool bFound; + const KDChartParams::KDChartFrameSettings* settings = + params()->frameSettings( area, bFound ); + if( bFound ) { + bool allCustomBoxes; + QRect rect( calculateAreaRect( allCustomBoxes, + area, + dataRow, dataCol, data3rd, regions ) ); + + if( !allCustomBoxes ) + paintAreaWithGap( painter, rect, *settings ); + } + } +} + + +void KDChartPainter::paintDataRegionAreas( QPainter* painter, + KDChartDataRegionList* regions ) +{ + if( regions ){ + int iterIdx; + bool bFound; + const KDChartParams::KDChartFrameSettings* settings = + params()->frameSettings( KDChartEnums::AreaChartDataRegion, bFound, &iterIdx ); + while( bFound ) { + bool bDummy; + QRect rect( calculateAreaRect( bDummy, + KDChartEnums::AreaChartDataRegion, + settings->dataRow(), + settings->dataCol(), + settings->data3rd(), + regions ) ); + // NOTE: we can *not* draw any background behind the + // data representations. + // reason: for being able to do that we would have to + // know the respective regions _before_ the + // data representations are drawn; since that + // is impossible, we just draw the borders only + // ( == the corners and the edges ) and ignore the background + // + // (actually: Since the respective interface function does not allow + // specifying a background there is nothing to be ignored anyway.) + settings->frame().paint( painter, + KDFrame::PaintBorder, + trueFrameRect( rect, settings ) ); + settings = params()->nextFrameSettings( bFound, &iterIdx ); + } + } +} + + +QRect KDChartPainter::trueFrameRect( const QRect& orgRect, + const KDChartParams::KDChartFrameSettings* settings ) const +{ + QRect rect( orgRect ); + if( settings ){ + rect.moveBy( -settings->innerGapX(), -settings->innerGapY() ); + rect.setWidth( rect.width() + 2*settings->innerGapX() ); + rect.setHeight( rect.height() + 2*settings->innerGapY() ); + } + return rect; +} + + +/** + Paints an area frame. + This methode is called internally by KDChartPainter::paintArea. + NOTE: areas around KDChartCustomBoxes are _not_ drawn here but + in KDChartCustomBox::paint() which is called by paintCustomBoxes(). + */ +void KDChartPainter::paintAreaWithGap( QPainter* painter, + QRect rect, + const KDChartParams::KDChartFrameSettings& settings ) +{ + if( painter && rect.isValid() ) + settings.frame().paint( painter, + KDFrame::PaintAll, + trueFrameRect( rect, &settings ) ); +} + + +/** + Paints the data value texts near the data representations. + */ +void KDChartPainter::paintDataValues( QPainter* painter, + KDChartTableDataBase* data, + KDChartDataRegionList* regions ) +{ + KDChartDataRegion* region; + if ( painter + && data + && regions + && regions->count() + && params() + && ( params()->printDataValues( 0 ) + || params()->printDataValues( 1 ) ) ) { + + // out of loop status saving + painter->save(); + + QFont font0( params()->dataValuesFont( 0 ) ); + + if( params()->dataValuesUseFontRelSize( 0 ) ) { + float size = QMIN(_areaWidthP1000, _areaHeightP1000) * abs(params()->dataValuesFontRelSize( 0 )); + if ( 9.0 > size ) + size = 9.0; + font0.setPixelSize( static_cast < int > ( size ) ); + } + painter->setFont( font0 ); + QFontMetrics fm0( painter->fontMetrics() ); + double fm0HeightP100( fm0.height() / 100.0 ); + QFont font1( params()->dataValuesFont( 1 ) ); + + if( params()->dataValuesUseFontRelSize( 1 ) ) { + float size = QMIN(_areaWidthP1000, _areaHeightP1000) * abs(params()->dataValuesFontRelSize( 1 )); + if ( 9.0 > size ) + size = 9.0; + font1.setPixelSize( static_cast < int > ( size ) ); + } else + font1.setPixelSize( font0.pixelSize()); + painter->setFont( font1 ); + QFontMetrics fm1( painter->fontMetrics() ); + double fm1HeightP100( fm1.height() / 100.0 ); + + bool lastDigitIrrelevant0 = true; + bool lastDigitIrrelevant1 = true; + // get and format the texts + for ( region=regions->first(); + region != 0; + region = regions->next() ) { + QVariant vValY; + if( data->cellCoord( region->row, region->col, vValY, 1 ) ){ + if( QVariant::String == vValY.type() ){ + const QString sVal( vValY.toString() ); + if( !sVal.isEmpty() ) + region->text = sVal; + }else if( QVariant::Double == vValY.type() ){ + double value( vValY.toDouble() ); + region->negative = 0.0 > value; + double divi( pow( 10.0, params()->dataValuesDivPow10( region->chart ) ) ); + if ( 1.0 != divi ) + value /= divi; + int digits( params()->dataValuesDigitsBehindComma( region->chart ) ); + bool autoDigits( KDCHART_DATA_VALUE_AUTO_DIGITS == digits ); + if( autoDigits ) { + if( 10 < digits ) + digits = 10; + } else + ( region->chart + ? lastDigitIrrelevant1 + : lastDigitIrrelevant0 ) = false; + if( value == KDCHART_NEG_INFINITE ) + region->text = "-LEMNISKATE"; + else if( value == KDCHART_POS_INFINITE ) + region->text = "+LEMNISKATE"; + else { + region->text.setNum( value, 'f', digits ); + if ( autoDigits && region->text.contains( '.' ) ) { + int len = region->text.length(); + while ( 3 < len + && '0' == region->text[ len-1 ] + && '.' != region->text[ len-2 ] ) { + --len; + region->text.truncate( len ); + } + if( '0' != region->text[ len-1 ] ) + ( region->chart + ? lastDigitIrrelevant1 + : lastDigitIrrelevant0 ) = false; + } + } + } + } + } + + if ( lastDigitIrrelevant0 || lastDigitIrrelevant1 ) + for ( region=regions->first(); + region != 0; + region = regions->next() ) + if ( ( ( lastDigitIrrelevant0 && !region->chart ) + || ( lastDigitIrrelevant1 && region->chart ) ) + && region->text.contains( '.' ) + && ( 2 < region->text.length() ) ) + region->text.truncate ( region->text.length() - 2 ); + + + // draw the Data Value Texts and calculate the text regions + painter->setPen( Qt::black ); + + bool allowOverlapping = params()->allowOverlappingDataValueTexts(); + bool drawThisOne; + QRegion lastRegionDone; + + QFontMetrics actFM( painter->fontMetrics() ); + + QFont* oldFont = 0; + int oldRotation = 0; + uint oldChart = UINT_MAX; + uint oldDatacolorNo = UINT_MAX; + for ( region=regions->first(); + region != 0; + region = regions->next() ) { + + // in loop status saving + painter->save(); + + if ( region->text.length() ) { + + QVariant vValY; + bool zero = + data->cellCoord( region->row, region->col, vValY, 1 ) && + QVariant::Double == vValY.type() && + ( 0.0 == vValY.toDouble() || 0 == vValY.toDouble() ); + uint align( params()->dataValuesAnchorAlign( region->chart, + region->negative ) ); + KDChartParams::ChartType cType = region->chart + ? params()->additionalChartType() + : params()->chartType(); + + + // these use the bounding rect of region-region: + bool bIsAreaChart = KDChartParams::Area == cType; + bool rectangular = ( KDChartParams::Bar == cType + || KDChartParams::Line == cType + || bIsAreaChart + || KDChartParams::HiLo == cType + || KDChartParams::BoxWhisker == cType ); + + // these use the nine anchor points stored in region->points + bool circular = ( KDChartParams::Pie == cType + || KDChartParams::Ring == cType + || KDChartParams::Polar == cType ); + + + KDChartEnums::PositionFlag anchorPos( + params()->dataValuesAnchorPosition( region->chart, region->negative ) ); + + QPoint anchor( + rectangular + ? KDChartEnums::positionFlagToPoint( region->rect(), anchorPos ) + : KDChartEnums::positionFlagToPoint( region->points, anchorPos ) ); + + double & fmHeightP100 = region->chart ? fm1HeightP100 : fm0HeightP100; + + int angle = region->startAngle; + switch ( anchorPos ) { + case KDChartEnums::PosTopLeft: + case KDChartEnums::PosCenterLeft: + case KDChartEnums::PosBottomLeft: + angle += region->angleLen; + break; + case KDChartEnums::PosTopCenter: + case KDChartEnums::PosCenter: + case KDChartEnums::PosBottomCenter: + angle += region->angleLen / 2; + break; + /* + case KDChartEnums::PosTopRight: + case KDChartEnums::PosCenterRight: + case KDChartEnums::PosBottomRight: + angle += 0; + break; + */ + default: + break; + } + double anchorDX( params()->dataValuesAnchorDeltaX( region->chart, region->negative ) + * fmHeightP100 ); + double anchorDY( params()->dataValuesAnchorDeltaY( region->chart, region->negative ) + * fmHeightP100 ); + if ( circular ) { + if ( 0.0 != anchorDY ) { + double normAngle = angle / 16; + double normAngleRad = DEGTORAD( normAngle ); + double sinAngle = sin( normAngleRad ); + QPoint& pM = region->points[ KDChartEnums::PosCenter ]; + double dX( pM.x() - anchor.x() ); + double dY( pM.y() - anchor.y() ); + double radialLen( sinAngle ? dY / sinAngle : dY ); + double radialFactor( ( radialLen == 0.0 ) ? 0.0 : ( ( radialLen - anchorDY ) / radialLen ) ); + anchor.setX( static_cast < int > ( pM.x() - dX * radialFactor ) ); + anchor.setY( static_cast < int > ( pM.y() - dY * radialFactor ) ); + } + } else { + anchor.setX( anchor.x() + static_cast < int > ( anchorDX ) ); + anchor.setY( anchor.y() + static_cast < int > ( anchorDY ) ); + } + + + if(anchor.x() < -250){ + anchor.setX(-250); + //qDebug("!! bad negative x position in KDChartPainter::paintDataValues() !!"); + } + if(anchor.y() < -2500){ + anchor.setY(-2500); + //qDebug("!! bad negative y position in KDChartPainter::paintDataValues() !!"); + } + + int rotation( params()->dataValuesRotation( region->chart, + region->negative ) ); + bool incRotationBy90 = false; + if( region->text == "-LEMNISKATE" || + region->text == "+LEMNISKATE" ){ + if( params()->dataValuesShowInfinite( region->chart ) ){ + //bool bIsLineChart = KDChartParams::Line == cType; + if( region->text == "-LEMNISKATE" ) + align = Qt::AlignRight + Qt::AlignVCenter; + else + align = Qt::AlignLeft + Qt::AlignVCenter; + if( !rotation ) + rotation = 90; + else + incRotationBy90 = true; + region->text = " 8 "; + }else{ + region->text = ""; + } + } + + if ( rotation ) { + anchor = painter->worldMatrix().map( anchor ); + + // Temporary solution for fixing the data labels size + // bug when in QPrinter::HighResolution mode: + // There seem to be no backdraws by acting like this, + // but further investigation is required to detect the + // real error in the previous code/ + if ( KDCHART_SAGGITAL_ROTATION == rotation + || KDCHART_TANGENTIAL_ROTATION == rotation ) { + rotation = ( KDCHART_TANGENTIAL_ROTATION == rotation + ? -1440 + : 0 ) + + angle; + rotation /= 16; + if( incRotationBy90 ) + rotation += 90; + if ( 360 <= rotation ) + rotation -= 360; + else if ( 0 > rotation ) + rotation += 360; + rotation = 360 - rotation; + }else if( incRotationBy90 ) + rotation = (rotation + 90) % 360; + + + if( rotation != oldRotation ) { + painter->rotate( rotation - oldRotation ); + // Comment this out - zooming and scrolling + // oldRotation = rotation; + } + + QFont* actFont = region->chart ? &font1 : &font0; + if( oldFont != actFont ) { + painter->setFont( *actFont ); + actFM = QFontMetrics( painter->fontMetrics() ); + // Comment this out - zooming and scrolling + //oldFont = actFont; + } + + KDDrawTextRegionAndTrueRect infosKDD = + KDDrawText::measureRotatedText( painter, + rotation, + anchor, + region->text, + 0, + align, + &actFM, + true, + true, + 5 ); + //anchor = painter->worldMatrix().map( anchor ); + + if( allowOverlapping ) { + drawThisOne = true; + }else { + QRegion sectReg( infosKDD.region.intersect( lastRegionDone ) ); + drawThisOne = sectReg.isEmpty(); + } + if( drawThisOne ) { + lastRegionDone = lastRegionDone.unite( infosKDD.region ); + region->pTextRegion = new QRegion( infosKDD.region ); + + if( params()->dataValuesAutoColor( region->chart ) ) { + if( bIsAreaChart ){ + QColor color( params()->dataColor( region->row ) ); + /* + if( ( (0.0 > anchorDY) && region->negative ) + || ( (0.0 < anchorDY) && !region->negative ) ) + painter->setPen( + QColor( static_cast < int > ( 255- color.red() ), + static_cast < int > ( 255- color.green() ), + static_cast < int > ( 255- color.blue() ) ) ); + else + */ + painter->setPen( color.dark() ); + }else{ + if( zero ) { + if( oldDatacolorNo != UINT_MAX ) { + painter->setPen( Qt::black ); + oldDatacolorNo = UINT_MAX; + } + } + else { + uint datacolorNo = ( KDChartParams::Pie == cType + || KDChartParams::Ring == cType ) + ? region->col + : region->row; + if( oldDatacolorNo != datacolorNo ) { + oldDatacolorNo = datacolorNo; + QColor color( params()->dataColor( datacolorNo ) ); + painter->setPen( QColor( + static_cast < int > (255-color.red() ), + static_cast < int > (255-color.green()), + static_cast < int > (255-color.blue() ))); + } + } + } + } + else if( oldChart != region->chart ) { + oldChart = region->chart; + painter->setPen( params()->dataValuesColor( region->chart ) ); + } + + if( params()->optimizeOutputForScreen() ){ + painter->rotate( -oldRotation ); + oldRotation = 0; + if ( anchor.y() < 0 ) + anchor.setY( -anchor.y() ); + + KDDrawText::drawRotatedText( painter, + rotation, + anchor, + region->text, + region->chart ? &font1 : &font0, + align, + false, // bool showAnchor + 0, // const QFontMetrics* fontMet + false, // bool noFirstrotate + false, // bool noBackrotate + 0, // KDDrawTextRegionAndTrueRect* infos + true ); // bool optimizeOutputForScreen + }else{ + painter->setPen( params()->dataValuesColor( region->chart ) ); + //Pending Michel Painting data value labels rotated. + painter->drawText( infosKDD.x , infosKDD.y , + infosKDD.width, infosKDD.height, + Qt::AlignHCenter | Qt::AlignVCenter | Qt::SingleLine, + region->text ); + + } + + + } // if not intersect + + } else { + + // no rotation: + painter->rotate( -oldRotation ); + oldRotation = 0; + QFontMetrics & fm = region->chart ? fm1 : fm0; + int boundingRectWidth = fm.boundingRect( region->text ).width(); + int leftBearing = fm.leftBearing( region->text[ 0 ] ); + const QChar c = region->text.at( region->text.length() - 1 ); + int rightBearing = fm.rightBearing( c ); + int w = boundingRectWidth + leftBearing + rightBearing + 1; + int h = fm.height(); // ascent + descent + 1 + int dx = 0; + int dy = 0; + switch( align & ( Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter ) ) { + case Qt::AlignRight: + dx = -w+1; + break; + case Qt::AlignHCenter: + // Center on the middle of the bounding rect, not + // the painted area, because numbers appear centered then + dx = -( ( boundingRectWidth / 2 ) + leftBearing ); + break; + } + switch( align & ( Qt::AlignTop | Qt::AlignBottom | Qt::AlignVCenter ) ) { + case Qt::AlignBottom: + dy = -h+1; + break; + case Qt::AlignVCenter: + dy = -h / 2; + break; + } + + QRegion thisRegion( + QRect( anchor.x() + dx, anchor.y() + dy, w, h ) ); + if( allowOverlapping ) + drawThisOne = true; + else { + QRegion sectReg( thisRegion.intersect( lastRegionDone ) ); + drawThisOne = sectReg.isEmpty(); + } + if( drawThisOne ) { + lastRegionDone = lastRegionDone.unite( thisRegion ); + region->pTextRegion = new QRegion( thisRegion ); +#ifdef DEBUG_TEXT_PAINTING + // for testing: + QRect rect( region->pTextRegion->boundingRect() ); + painter->drawRect( rect ); + painter->setPen( Qt::red ); + rect.setLeft( rect.left() + leftBearing ); + rect.setTop( rect.top() + ( fm.height()-fm.boundingRect( region->text ).height() ) /2 ); + rect.setWidth( fm.boundingRect( region->text ).width() ); + rect.setHeight( fm.boundingRect( region->text ).height() ); + painter->drawRect( rect ); + painter->setPen( Qt::black ); +#endif + /* + +NOTE: The following will be REMOVED again once +the layout policy feature is implemented !!! + +*/ + QRect textRect( region->pTextRegion->boundingRect() ); + if( bIsAreaChart ){ + QBrush brush( params()->dataValuesBackground( region->chart ) ); + painter->setBrush( brush ); + painter->setPen( Qt::NoPen ); + QRect rect( textRect ); + rect.moveBy( -2, 0 ); + rect.setWidth( rect.width() + 4 ); + painter->drawRect( rect ); + } + painter->setFont( region->chart ? font1 : font0 ); + if( params()->dataValuesAutoColor( region->chart ) ) { + if( bIsAreaChart ){ + QColor color( params()->dataColor( region->row ) ); + /* + if( ( (0.0 > anchorDY) && region->negative ) + || ( (0.0 < anchorDY) && !region->negative ) ) + painter->setPen( + QColor( static_cast < int > ( 255- color.red() ), + static_cast < int > ( 255- color.green() ), + static_cast < int > ( 255- color.blue() ) ) ); + else + */ + painter->setPen( color.dark() ); + }else{ + if( zero ) + painter->setPen( Qt::black ); + else { + QColor color( params()->dataColor( + ( KDChartParams::Pie == params()->chartType() + || KDChartParams::Ring == params()->chartType() ) + ? region->col + : region->row ) ); + painter->setPen( QColor( static_cast < int > ( 255- color.red() ), + static_cast < int > ( 255- color.green() ), + static_cast < int > ( 255- color.blue() ) ) ); + } + } + }else{ + painter->setPen( params()->dataValuesColor( region->chart ) ); + } + + painter->drawText( textRect.left(), textRect.top(), + textRect.width()+1, textRect.height()+1, + Qt::AlignLeft | Qt::AlignTop, region->text ); + + } + + + } + } + // + painter->restore(); + + } + painter->restore(); + } + +} + + +/** + Paints all custom boxes. + */ +void KDChartPainter::paintCustomBoxes( QPainter* painter, + KDChartDataRegionList* regions ) +{ + // paint all of the custom boxes AND their surrounding frames+background (if any) + bool bGlobalFound; + const KDChartParams::KDChartFrameSettings* globalFrameSettings + = params()->frameSettings( KDChartEnums::AreasCustomBoxes, bGlobalFound ); + + uint idx; + for( idx = 0; idx <= params()->maxCustomBoxIdx(); ++idx ) { + const KDChartCustomBox * box = params()->customBox( idx ); + if( box ) { + // paint border and background + paintArea( painter, + KDChartEnums::AreaCustomBoxesBASE + idx, + regions, + box->dataRow(), + box->dataCol(), + box->data3rd() ); + // retrieve frame information + bool bIndividualFound; + const KDChartParams::KDChartFrameSettings * individualFrameSettings + = params()->frameSettings( KDChartEnums::AreaCustomBoxesBASE + idx, + bIndividualFound ); + const KDChartParams::KDChartFrameSettings * settings + = bIndividualFound ? individualFrameSettings + : bGlobalFound ? globalFrameSettings : 0; + // paint content + const QPoint anchor( calculateAnchor( *box, regions ) ); + box->paint( painter, + anchor, + _areaWidthP1000, + _areaHeightP1000, + settings ? settings->framePtr() : 0, + trueFrameRect( box->trueRect( anchor, _areaWidthP1000, _areaHeightP1000 ), + settings ) ); + } + } +} + + +/** + Calculated the top left corner of a custom box. + */ +QPoint KDChartPainter::calculateAnchor( const KDChartCustomBox & box, + KDChartDataRegionList* regions ) const +{ + QPoint pt(0,0); + + // Recursion handling: + // + // * calculateAnchor() normally calls calculateAreaRect() + // + // * calculateAreaRect() will in turn calls calculateAnchor() in case of + // box.anchorArea() being based on KDChartEnums::AreaCustomBoxesBASE + // + // This is Ok as long as the recursive call of calculateAnchor() is NOT + // intend examination the same box as a previous call. + // + // Rule: + // + // A box may be aligned to another box (and the 2nd box may again be + // aligned to a 3rd box and so on) but NO CIRCULAR alignment is allowed. + // + if( !box.anchorBeingCalculated() ) { + + box.setInternalFlagAnchorBeingCalculated( true ); + + bool allCustomBoxes; + QRect rect( calculateAreaRect( allCustomBoxes, + box.anchorArea(), + box.dataRow(), + box.dataCol(), + box.data3rd(), + regions ) ); + if( allCustomBoxes ) { + // + // Dear user of this library. + // + // You faced the above error during program runtime? + // + // The reason for this is that you may NOT use AreasCustomBoxes + // as a value for the KDChartCustomBox anchor area. + // + // This is due to the fact that an anchor area allways must specify one AREA + // or some contiguous areas that form an area when combined. + // The flag AreasCustomBoxes however specifies a list of custom boxes + // that normally do not form a contiguos ares, so they cannot be used as anchor area. + // + // In order to specify a SINGLE custom box please use AreaCustomBoxBASE+boxId. + // + } + pt = KDChartEnums::positionFlagToPoint( rect, box.anchorPosition() ); + + box.setInternalFlagAnchorBeingCalculated( false ); + } + + return pt; +} + + +/** + Calculated the rectangle covered by an area. + NOTE: KDChartCustomBox areas are _not_ calculated here. + */ +QRect KDChartPainter::calculateAreaRect( bool & allCustomBoxes, + uint area, + uint dataRow, + uint dataCol, + uint /*data3rd*/, + KDChartDataRegionList* regions ) const +{ + QRect rect(0,0, 0,0); + allCustomBoxes = false; + uint pos; + switch( area ) { + case KDChartEnums::AreaData: + rect = _dataRect; + break; + case KDChartEnums::AreaAxes: + break; + case KDChartEnums::AreaLegend: + rect = _legendRect; + break; + case KDChartEnums::AreaDataAxes: + rect = _axesRect; + break; + case KDChartEnums::AreaDataAxesLegend: + rect = _axesRect; + if( _legendRect.isValid() ) { + if( rect.isValid() ) + rect = rect.unite( _legendRect ); + else + rect = _legendRect; + } + break; + case KDChartEnums::AreaHeaders: { + bool bStart = true; + for( pos = KDChartParams::HdFtPosHeadersSTART; + KDChartParams::HdFtPosHeadersEND >= pos; + ++pos ) { + const QRect& r = params()->headerFooterRect( pos ); + if( r.isValid() ) { + if( bStart ) + rect = r; + else + rect = rect.unite( r ); + bStart = false; + } + } + } + break; + case KDChartEnums::AreaFooters: { + bool bStart = true; + for( pos = KDChartParams::HdFtPosFootersSTART; + KDChartParams::HdFtPosFootersEND >= pos; + ++pos ) { + const QRect& r = params()->headerFooterRect( pos ); + if( r.isValid() ) { + if( bStart ) + rect = r; + else + rect = rect.unite( r ); + bStart = false; + } + } + } + break; + case KDChartEnums::AreaDataAxesLegendHeadersFooters: { + rect = _axesRect; + bool bStart = !rect.isValid(); + if( _legendRect.isValid() ) { + if( bStart ) + rect = _legendRect; + else + rect = rect.unite( _legendRect ); + bStart = false; + } + for( pos = KDChartParams::HdFtPosSTART; + KDChartParams::HdFtPosEND >= pos; + ++pos ) { + const QRect& r = params()->headerFooterRect( pos ); + if( r.isValid() ) { + if( bStart ) + rect = r; + else + rect = rect.unite( r ); + bStart = false; + } + } + } + break; + case KDChartEnums::AreaOutermost: + rect = _outermostRect; + break; + case KDChartEnums::AreaInnermost: + rect = _innermostRect; + break; + case KDChartEnums::AreasCustomBoxes: + allCustomBoxes = true; + break; + case KDChartEnums::AreaChartDataRegion: + if( regions ) { + KDChartDataRegion* current; + for ( current = regions->first(); + current != 0; + current = regions->next() ) { + if ( current->row == dataRow + && current->col == dataCol + // + // the line below prepared for true 3-dimensional data charts + // + /* && current->region.thirdDimension == data3rd */ ) { + rect = current->rect(); + break; + } + } + } + break; + case KDChartEnums::AreaUNKNOWN: + break; + + default: { + uint maskBASE = KDChartEnums::AreaBASEMask & area; + pos = area - maskBASE; + if ( KDChartEnums::AreaAxisBASE == maskBASE ) { + rect = params()->axisParams( pos ).axisTrueAreaRect(); + } else if ( KDChartEnums::AreaHdFtBASE == maskBASE ) { + rect = params()->headerFooterRect( pos ); + } else if ( KDChartEnums::AreaCustomBoxesBASE == maskBASE ) { + const KDChartCustomBox * box = params()->customBox( pos ); + if( box ) { + rect = box->trueRect( calculateAnchor( *box, regions ), + _areaWidthP1000, + _areaHeightP1000 ); + } + } + } + } + return rect; +} + + +QPoint KDChartPainter::pointOnCircle( const QRect& rect, double angle ) +{ + // There are two ways of computing this: The simple, but slow one + // is to use QPointArray.makeArc() and take the first point. The + // more advanced, but faster one is to do the trigonometric + // computionations ourselves. Since the comments in + // QPointArray::makeArc() very often say that the code there is + // "poor", we'd better do it outselves... + + double normAngle = angle / 16.0; + double normAngleRad = DEGTORAD( normAngle ); + double cosAngle = cos( normAngleRad ); + double sinAngle = -sin( normAngleRad ); + double posX = floor( cosAngle * ( double ) rect.width() / 2.0 + 0.5 ); + double posY = floor( sinAngle * ( double ) rect.height() / 2.0 + 0.5 ); + return QPoint( static_cast<int>(posX) + rect.center().x(), + static_cast<int>(posY) + rect.center().y() ); + +} + +void KDChartPainter::makeArc( QPointArray& points, + const QRect& rect, + double startAngle, double angles ) +{ + double endAngle = startAngle + angles; + int rCX = rect.center().x(); + int rCY = rect.center().y(); + double rWid2 = ( double ) rect.width() / 2.0; + double rHig2 = ( double ) rect.height() / 2.0; + int numSteps = static_cast<int>(angles); + if( floor( angles ) < angles ) + ++numSteps; + points.resize( numSteps ); + double angle = startAngle; + if( angle < 0.0 ) + angle += 5760.0; + else if( angle >= 5760.0 ) + angle -= 5760.0; + for(int i = 0; i < numSteps; ++i){ + double normAngle = angle / 16.0; + double normAngleRad = DEGTORAD( normAngle ); + double cosAngle = cos( normAngleRad ); + double sinAngle = -sin( normAngleRad ); + double posX = floor( cosAngle * rWid2 + 0.5 ); + double posY = floor( sinAngle * rHig2 + 0.5 ); + points[i] = QPoint( ( int ) posX + rCX, + ( int ) posY + rCY ); + if( i+1 >= numSteps-1 ) + angle = endAngle; // the very last step width may be smaller than 1.0 + else + angle += 1.0; + if( angle >= 5760.0 ) + angle -= 5760.0; + } +} + +/** + Paints the axes for the chart. The implementation in KDChartPainter + does nothing; subclasses for chart types that have axes will + provide the appropriate drawing code here. This method serves as a + fallback for chart types that do not have axes (like pies). + + \param painter the QPainter onto which the chart should be drawn + \param data the data that will be displayed as a chart + */ +void KDChartPainter::paintAxes( QPainter* /*painter*/, KDChartTableDataBase* /*data*/ ) +{ + // This method intentionally left blank. +} + + +int KDChartPainter::legendTitleVertGap() const +{ + return _legendTitleHeight + + static_cast < int > ( _legendTitleMetricsHeight * 0.20 ); +} + + +QFont KDChartPainter::trueLegendFont() const +{ + QFont trueFont = params()->legendFont(); + if ( params()->legendFontUseRelSize() ) { + const double averageValueP1000 = QMIN(_areaWidthP1000, _areaHeightP1000);//( _areaWidthP1000 + _areaHeightP1000 ) / 2.0; + trueFont.setPixelSize( + static_cast < int > ( params()->legendFontRelSize() * averageValueP1000 ) ); + } + return trueFont; +} + + +/** + Calculates the size of the rectangle for horizontal legend orientation. + + \param painter the QPainter onto which the chart should be drawn + */ +void KDChartPainter::calculateHorizontalLegendSize( QPainter* painter, + QSize& size, + bool& legendNewLinesStartAtLeft ) const +{ + + legendNewLinesStartAtLeft = false; + QRect legendRect( _legendRect ); + /* + * Pending Michel reset the left side before calculating + *the new legend position calculation + *otherwise we occasionally reach the edge and get a wrong + *result + */ + + legendRect.setLeft( _innermostRect.left() ); + + const int em2 = 2 * _legendEMSpace; + const int em4 = 4 * _legendEMSpace; + const int emDiv2 = static_cast < int > ( _legendEMSpace / 2.0 ); + + const int xposHori0 = legendRect.left() + _legendEMSpace; + + int xpos = xposHori0; + + int ypos = legendRect.top() + emDiv2; + + // first paint the title, if any + if( _legendTitle ) + xpos += _legendTitleWidth + em4; + + int maxX = _legendTitleWidth + _legendEMSpace; + + // save the x position: here start the item texts if in horizontal mode + int xposHori1 = xpos; + + // add the space of the box plus the space between the box and the text + int x2 = xpos + em2; + + // loop over all the datasets, each one has one row in the legend + // if its data are to be used in at least one of the charts drawn + // *but* only if there is a legend text for it! + const int rightEdge = _innermostRect.right()-_legendEMSpace; + bool bFirstLFWithTitle = _legendTitle; + painter->setFont( trueLegendFont() ); + QFontMetrics txtMetrics( painter->fontMetrics() ); + int dataset; + for ( dataset = 0; dataset < _numLegendTexts; ++dataset ) { + /* + if( KDChartParams::DataEntry == params()->chartSourceMode( dataset ) ) { + */ + if( !_legendTexts[ dataset ].isEmpty() ){ + int txtWidth = txtMetrics.width( _legendTexts[ dataset ] ) + 1; + if( x2 + txtWidth > rightEdge ){ + if( xposHori1 + em2 + txtWidth > rightEdge){ + xposHori1 = xposHori0; + legendNewLinesStartAtLeft = true; + } + xpos = xposHori1; + x2 = xpos + em2; + ypos += bFirstLFWithTitle ? legendTitleVertGap() : _legendSpacing; + bFirstLFWithTitle = false; + } + maxX = QMAX(maxX, x2+txtWidth+_legendEMSpace); + + xpos += txtWidth + em4; + x2 += txtWidth + em4; + } + } + if( bFirstLFWithTitle ) + ypos += _legendTitleHeight; + else + ypos += txtMetrics.height(); + + size.setWidth( maxX - legendRect.left() ); + size.setHeight( ypos + emDiv2 - _legendRect.top() ); +} + + +bool KDChartPainter::mustDrawVerticalLegend() const +{ + return + params()->legendOrientation() == Qt::Vertical || + params()->legendPosition() == KDChartParams::LegendLeft || + params()->legendPosition() == KDChartParams::LegendRight || + params()->legendPosition() == KDChartParams::LegendTopLeft || + params()->legendPosition() == KDChartParams::LegendTopLeftLeft || + params()->legendPosition() == KDChartParams::LegendTopRight || + params()->legendPosition() == KDChartParams::LegendTopRightRight || + params()->legendPosition() == KDChartParams::LegendBottomLeft || + params()->legendPosition() == KDChartParams::LegendBottomLeftLeft || + params()->legendPosition() == KDChartParams::LegendBottomRight || + params()->legendPosition() == KDChartParams::LegendBottomRightRight; +} + +QFont KDChartPainter::trueLegendTitleFont() const +{ + const double averageValueP1000 = QMIN(_areaWidthP1000, _areaHeightP1000);//( _areaWidthP1000 + _areaHeightP1000 ) / 2.0; + QFont font( params()->legendTitleFont() ); + if ( params()->legendTitleFontUseRelSize() ) { + int nTxtHeight = + static_cast < int > ( params()->legendTitleFontRelSize() + * averageValueP1000 ); + font.setPixelSize( nTxtHeight ); + // qDebug("l-t-height %i",nTxtHeight); + } + return font; +} + +/** + Paints the legend for the chart. The implementation in KDChartPainter + draws a standard legend that should be suitable for most chart + types. Subclasses can provide their own implementations. + + \param painter the QPainter onto which the chart should be drawn + \param data the data that will be displayed as a chart + */ +void KDChartPainter::paintLegend( QPainter* painter, + KDChartTableDataBase* /*data*/ ) +{ + if ( params()->legendPosition() == KDChartParams::NoLegend ) + return ; // do not draw legend + + const bool bVertical = mustDrawVerticalLegend(); + painter->save(); + + + bool bFrameFound; + params()->frameSettings( KDChartEnums::AreaLegend, bFrameFound ); + + // start out with a rectangle around the legend + //painter->setPen( QPen( Qt::black, 1 ) ); + //painter->setBrush( QBrush::NoBrush ); + //Pending Michel: let us paint the frame at the end of the drawmarker + //and draw text process, in case we need to resize it then + /* + if( !bFrameFound ) { + painter->drawRect( _legendRect ); + } + */ + //qDebug("B: _legendRect:\n %i,%i\n %i,%i", _legendRect.left(),_legendRect.top(),_legendRect.right(),_legendRect.bottom() ); + //qDebug("B: legendArea():\n %i,%i\n %i,%i\n", _params->legendArea().left(),_params->legendArea().top(),_params->legendArea().right(),_params->legendArea().bottom() ); + + const int em2 = 2 * _legendEMSpace; + const int em4 = 4 * _legendEMSpace; + const int emDiv2 = static_cast < int > ( _legendEMSpace / 2.0 ); + + const int xposHori0 = _legendRect.left() + _legendEMSpace; + + int xpos = xposHori0; + + int ypos = _legendRect.top() + emDiv2; + + + + + // first paint the title, if any + if( _legendTitle ) { + painter->setFont( trueLegendTitleFont() ); + _legendTitle->draw( painter, + xpos, + ypos, + QRegion( xpos, + ypos , + _legendTitleWidth, + _legendTitleHeight ), + params()->legendTitleTextColor() ); + if( bVertical ) + ypos += legendTitleVertGap(); + + else + xpos += _legendTitleWidth + em4; + + } + + // save the x position: here start the item texts if in horizontal mode + const int xposHori1 = _legendNewLinesStartAtLeft ? xposHori0 : xpos; + + // add the space of the box plus the space between the box and the text + int x2 = xpos + em2; + + // loop over all the datasets, each one has one row in the legend + // if its data are to be used in at least one of the charts drawn + // *but* only if there is a legend text for it! + const int rightEdge = _legendRect.right(); + bool bFirstLF = true; + painter->setFont( trueLegendFont() ); + QFontMetrics txtMetrics( painter->fontMetrics() ); + int dataset; + for ( dataset = 0; dataset < _numLegendTexts; ++dataset ) { + /* + if( KDChartParams::DataEntry == params()->chartSourceMode( dataset ) ) { + */ + if( !_legendTexts[ dataset ].isEmpty() ){ + int txtWidth = txtMetrics.width( _legendTexts[ dataset ] ) + 1; + + // calculate the width and height for the marker, relative to the font height + // we need the legend text to be aligned to the marker + // substract a gap. + int legHeight = static_cast <int>((txtMetrics.height() - (int)(txtMetrics.height() * 0.1))*0.85); + + //int legHeight = static_cast <int> (_legendRect.height()*0.8); + + if( !bVertical && x2 + txtWidth >= rightEdge ){ + _legendRect.setHeight( _legendRect.height() + _legendSpacing ); + xpos = xposHori1; + x2 = xpos + em2; + ypos += bFirstLF ? legendTitleVertGap() : _legendSpacing; + bFirstLF = false; + } + painter->setBrush( QBrush( params()->dataColor( dataset ), + QBrush::SolidPattern ) ); + + if( params()->legendShowLines() ){ + painter->setPen( QPen( params()->dataColor( dataset ), 2, + params()->lineStyle( dataset ) ) ); + painter->drawLine( + xpos - emDiv2, + ypos + emDiv2 + 1, + xpos + static_cast < int > ( _legendEMSpace * 1.5 ), + ypos + emDiv2 + 1); + } + + /* + // draw marker if we have a marker, OR we have no marker and no line + if ( params()->lineMarker() || + params()->lineStyle( dataset ) == Qt::NoPen )*/ + drawMarker( painter, + params(), + _areaWidthP1000, _areaHeightP1000, + _dataRect.x(), _dataRect.y(), + params()->lineMarker() + ? params()->lineMarkerStyle( dataset ) + : KDChartParams::LineMarkerSquare, + params()->dataColor(dataset), + QPoint(xpos + emDiv2, + bVertical? ypos + emDiv2: !bFirstLF ?ypos + _legendSpacing:_legendRect.center().y() - (legHeight / 2 ))/*ypos + emDiv2*/ , + 0, 0, 0, NULL, // these params are deadweight here. TODO + &legHeight /*&_legendEMSpace*/, &legHeight /*&_legendEMSpace*/, + bVertical ? Qt::AlignCenter : (Qt::AlignTop | Qt::AlignHCenter) ); + /* + painter->drawText(_legendRect.topLeft(), "topLeft" ); + painter->drawText(_legendRect.topLeft().x(), _legendRect.center().y(), "center" ); + painter->drawText(_legendRect.bottomLeft(), "bottomLeft" ); + */ + /* old: + painter->setPen( Qt::black ); + painter->drawRect( xpos, + ypos + ( _legendHeight - _legendEMSpace ) / 2, + _legendEMSpace, + _legendEMSpace ); + */ + painter->setPen( params()->legendTextColor() ); + painter->drawText( x2, + bVertical ? ypos : !bFirstLF ? ypos + _legendSpacing : _legendRect.center().y() - (legHeight / 2 ), + txtWidth, + legHeight, + Qt::AlignLeft | Qt::AlignVCenter, + _legendTexts[ dataset ] ); + + if( bVertical ) + ypos += _legendSpacing; + else { + xpos += txtWidth + em4; + x2 += txtWidth + em4; + } + } + } + + painter->setPen( QPen( Qt::black, 1 ) ); + painter->setBrush( QBrush::NoBrush ); + if( !bFrameFound ) + painter->drawRect( _legendRect ); + + + painter->restore(); +} + + +void adjustFromTo(int& from, int& to) +{ + if( abs(from) > abs(to) ){ + int n = from; + from = to; + to = n; + } +} + + +bool KDChartPainter::axesOverlapping( int axis1, int axis2 ) +{ + KDChartAxisParams::AxisPos basicPos = KDChartAxisParams::basicAxisPos( axis1 ); + if( basicPos != KDChartAxisParams::basicAxisPos( axis2 ) ) + // Only axes of the same position can be compared. (e.g. 2 left axes) + return false; + + if( KDChartAxisParams::AxisPosLeft != basicPos && + KDChartAxisParams::AxisPosRight != basicPos ) + // Available space usage only possible for (vertical) ordinate axes. + return false; + + int f1 = params()->axisParams( axis1 ).axisUseAvailableSpaceFrom(); + int t1 = params()->axisParams( axis1 ).axisUseAvailableSpaceTo(); + int f2 = params()->axisParams( axis2 ).axisUseAvailableSpaceFrom(); + int t2 = params()->axisParams( axis2 ).axisUseAvailableSpaceTo(); + adjustFromTo(f1,t1); + adjustFromTo(f2,t2); + // give these values some meaning + // to be able to compare mixed fixed and/or relative figures: + const double guessedAxisHeightP1000 = _areaHeightP1000 * 80.0 / 100.0; + if(f1 < 0) f1 = static_cast < int > ( f1 * -guessedAxisHeightP1000 ); + if(t1 < 0) t1 = static_cast < int > ( t1 * -guessedAxisHeightP1000 ); + if(f2 < 0) f2 = static_cast < int > ( f2 * -guessedAxisHeightP1000 ); + if(t2 < 0) t2 = static_cast < int > ( t2 * -guessedAxisHeightP1000 ); + const bool res = (f1 >= f2 && f1 < t2) || (f2 >= f1 && f2 < t1); + return res; +} + + +void internSetAxisArea( KDChartParams* params, int axis, + int x0, int y0, int w0, int h0 ) +{ + // axis may never occupy more than 1000 per mille of the available space + int nFrom = QMAX(-1000, params->axisParams( axis ).axisUseAvailableSpaceFrom()); + int nTo = QMAX(-1000, params->axisParams( axis ).axisUseAvailableSpaceTo()); + adjustFromTo(nFrom,nTo); + + KDChartAxisParams::AxisPos basicPos = KDChartAxisParams::basicAxisPos( axis ); + int x, y, w, h; + if( KDChartAxisParams::AxisPosBottom == basicPos || + KDChartAxisParams::AxisPosTop == basicPos ){ + + // Note: available space usage is ignored for abscissa axes! + // + //if( nFrom < 0 ) + // x = x0 + w0*nFrom/-1000; + //else + // x = x0 + nFrom; + //y = y0; + //if( nTo < 0 ) + // w = x0 + w0*nTo/-1000 - x; + //else + // w = x0 + nTo - x; + //h = h0; + + x = x0; + y = y0; + w = w0; + h = h0; + + }else{ + x = x0; + if( nTo < 0 ) + y = y0 + h0 - h0*nTo/-1000; + else + y = y0 + h0 - nTo; + w = w0; + if( nFrom < 0 ) + h = y0 + h0 - h0*nFrom/-1000 - y; + else + h = y0 + h0 - nFrom - y; + } + + params->setAxisArea( axis, + QRect( x, + y, + w, + h ) ); +} + + +/** + Paints the header and footers for the chart. The implementation + in KDChartPainter draws a standard header that should be suitable + for most chart types. Subclasses can provide their own implementations. + + \param painter the QPainter onto which the chart should be drawn + \param data the data that will be displayed as a chart + */ +void KDChartPainter::paintHeaderFooter( QPainter* painter, + KDChartTableDataBase* /*data*/ ) +{ + const double averageValueP1000 = QMIN(_areaWidthP1000, _areaHeightP1000);//( _areaWidthP1000 + _areaHeightP1000 ) / 2.0; + + painter->save(); + + for( int iHdFt = KDChartParams::HdFtPosSTART; + iHdFt <= KDChartParams::HdFtPosEND; ++iHdFt ){ + QString txt( params()->headerFooterText( iHdFt ) ); + if ( !txt.isEmpty() ) { + QFont actFont( params()->headerFooterFont( iHdFt ) ); + if ( params()->headerFooterFontUseRelSize( iHdFt ) ) + actFont.setPixelSize( static_cast < int > ( + params()->headerFooterFontRelSize( iHdFt ) * averageValueP1000 ) ); + painter->setPen( params()->headerFooterColor( iHdFt ) ); + painter->setFont( actFont ); + // Note: The alignment flags used here match the rect calculation + // done in KDChartPainter::setupGeometry(). + // AlignTop is done to ensure that the hd/ft texts of the same + // group (e.g. Hd2L and Hd2 and Hd2R) have the same baselines. + + QRect rect( params()->headerFooterRect( iHdFt ) ); + int dXY = iHdFt < KDChartParams::HdFtPosFootersSTART + ? _hdLeading/3 + : _ftLeading/3; + rect.moveBy(dXY, dXY); + rect.setWidth( rect.width() -2*dXY +1 ); + rect.setHeight( rect.height()-2*dXY +1 ); + painter->drawText( rect, + Qt::AlignLeft | Qt::AlignTop | Qt::SingleLine, + txt ); + } + } + painter->restore(); +} + + +int KDChartPainter::calculateHdFtRects( + QPainter* painter, + double averageValueP1000, + int xposLeft, + int xposRight, + bool bHeader, + int& yposTop, + int& yposBottom ) +{ + int& leading = (bHeader ? _hdLeading : _ftLeading); + leading = 0; // pixels between the header (or footer, resp.) text + // and the border of the respective Hd/Ft area + + const int rangesCnt = 3; + const int ranges[ rangesCnt ] + = { bHeader ? KDChartParams::HdFtPosHeaders0START : KDChartParams::HdFtPosFooters0START, + bHeader ? KDChartParams::HdFtPosHeaders1START : KDChartParams::HdFtPosFooters1START, + bHeader ? KDChartParams::HdFtPosHeaders2START : KDChartParams::HdFtPosFooters2START }; + const int rangeSize = 3; + QFontMetrics* metrics[rangesCnt * rangeSize]; + + int i; + for( i = 0; i < rangesCnt*rangeSize; ++i ) + metrics[ i ] = 0; + + int iRange; + int iHdFt; + for( iRange = 0; iRange < rangesCnt; ++iRange ){ + for( i = 0; i < rangeSize; ++i ){ + iHdFt = ranges[iRange] + i; + QString txt( params()->headerFooterText( iHdFt ) ); + if ( !txt.isEmpty() ) { + QFont actFont( params()->headerFooterFont( iHdFt ) ); + if ( params()->headerFooterFontUseRelSize( iHdFt ) ) { + actFont.setPixelSize( static_cast < int > ( + params()->headerFooterFontRelSize( iHdFt ) * averageValueP1000 ) ); + } + painter->setFont( actFont ); + metrics[ iRange*rangeSize + i ] = new QFontMetrics( painter->fontMetrics() ); + leading = QMAX( leading, metrics[ iRange*rangeSize + i ]->lineSpacing() / 2 ); + } + } + } + + if( bHeader ) + ++yposTop;//yposTop += leading/3; + //else + //--yposBottom;//yposBottom -= leading/3; + + int leading23 = leading*2/3 +1; + + for( iRange = + bHeader ? 0 : rangesCnt-1; + bHeader ? iRange < rangesCnt : iRange >= 0; + bHeader ? ++iRange : --iRange ){ + // Ascents and heights must be looked at to ensure that the hd/ft texts + // of the same group (e.g. Hd2L and Hd2 and Hd2R) have equal baselines. + int ascents[rangeSize]; + int heights[rangeSize]; + int widths[ rangeSize]; + int maxAscent = 0; + int maxHeight = 0; + for( i = 0; i < rangeSize; ++i ){ + iHdFt = ranges[iRange] + i; + if ( metrics[ iRange*rangeSize + i ] ) { + QFontMetrics& m = *metrics[ iRange*rangeSize + i ]; + ascents[i] = m.ascent(); + heights[i] = m.height() + leading23; + + // the following adds two spaces to work around a bug in Qt: + // bounding rect sometimes is too small, if using italicized fonts + widths[ i] = m.boundingRect( params()->headerFooterText( iHdFt )+" " ).width() + leading23; + + maxAscent = QMAX( maxAscent, ascents[i] ); + maxHeight = QMAX( maxHeight, heights[i] ); + }else{ + heights[i] = 0; + } + } + + if( !bHeader ) + yposBottom -= maxHeight; + + for( i = 0; i < rangeSize; ++i ){ + if( heights[i] ){ + iHdFt = ranges[iRange] + i; + int x1; + switch( i ){ + case 1: x1 = xposLeft+1; + break; + case 2: x1 = xposRight-widths[i]-1; + break; + default: x1 = xposLeft + (xposRight-xposLeft-widths[i]) / 2; + } + ((KDChartParams*)params())->__internalStoreHdFtRect( iHdFt, + QRect( x1, + bHeader + ? yposTop + maxAscent - ascents[i] + : yposBottom + maxAscent - ascents[i], + widths[ i], + heights[i] - 1 ) ); + } + } + if( bHeader ) + yposTop += leading + maxHeight; + else + yposBottom -= leading; + } + for( i = 0; i < rangesCnt*rangeSize; ++i ) + if( metrics[ i ] ) + delete metrics[ i ]; + return leading; +} + + + +void KDChartPainter::findChartDatasets( KDChartTableDataBase* data, + bool paint2nd, + uint chart, + uint& chartDatasetStart, + uint& chartDatasetEnd ) +{ + chartDatasetStart = 0; + chartDatasetEnd = 0; + if( params()->neverUsedSetChartSourceMode() + || !params()->findDatasets( KDChartParams::DataEntry, + KDChartParams::ExtraLinesAnchor, + chartDatasetStart, + chartDatasetEnd, + chart ) ) { + uint maxRow, maxRowMinus1; + switch ( data->usedRows() ) { + case 0: + return ; + case 1: + maxRow = 0; + maxRowMinus1 = 0; + break; + default: + maxRow = data->usedRows() - 1; + maxRowMinus1 = maxRow; + } + chartDatasetStart = paint2nd ? maxRow : 0; + chartDatasetEnd = paint2nd + ? maxRow + : ( ( KDChartParams::NoType == params()->additionalChartType() ) + ? maxRow + : maxRowMinus1 ); + + } +} + + +void KDChartPainter::calculateAllAxesRects( + QPainter* painter, + bool finalPrecision, + KDChartTableDataBase* data + ) +{ + const bool bIsAreaChart = KDChartParams::Area == params()->chartType(); + const bool bMultiRows = KDChartParams::Bar == params()->chartType() && + KDChartParams::BarMultiRows == params()->barChartSubType(); + + const int trueWidth = _outermostRect.width(); + const int trueHeight = _outermostRect.height(); + const double averageValueP1000 = QMIN(_areaWidthP1000, _areaHeightP1000);//( _areaWidthP1000 + _areaHeightP1000 ) / 2.0; + + // store the axes' 0 offsets + int nAxesLeft0 = _axesRect.left() - _outermostRect.left(); + int nAxesRight0 = _outermostRect.right() - _axesRect.right(); + int nAxesTop0 = _axesRect.top() - _outermostRect.top(); + int nAxesBottom0 = _outermostRect.bottom() - _axesRect.bottom(); + if( bMultiRows ){ + uint chartDatasetStart, chartDatasetEnd; + findChartDatasets( data, false, 0, chartDatasetStart, chartDatasetEnd ); + const int datasets = chartDatasetEnd - chartDatasetStart + 1; + int numValues = 0; + if ( params()->numValues() != -1 ) + numValues = params()->numValues(); + else + numValues = data->usedCols(); + if( datasets ){ + const int additionalGapWidth = static_cast < int > ( 1.0 * _axesRect.width() / (9.75*numValues + 4.0*datasets) * 4.0*datasets ); + nAxesRight0 += additionalGapWidth; + nAxesTop0 += static_cast < int > ( additionalGapWidth * 0.52 ); + //const double widthFactor = additionalGapWidth*1.0 / _axesRect.width(); + //nAxesTop0 += static_cast < int > ( _axesRect.height() * widthFactor ); + } + } + // store the distances to be added to the axes' 0 offsets + int nAxesLeftADD =0; + int nAxesRightADD =0; + int nAxesTopADD =0; + int nAxesBottomADD=0; + + // determine whether the axes widths of one side should be added + // or their maximum should be used + bool bAddLeft = axesOverlapping( KDChartAxisParams::AxisPosLeft, + KDChartAxisParams::AxisPosLeft2 ); + bool bAddRight = axesOverlapping( KDChartAxisParams::AxisPosRight, + KDChartAxisParams::AxisPosRight2 ); + bool bAddTop = axesOverlapping( KDChartAxisParams::AxisPosTop, + KDChartAxisParams::AxisPosTop2 ); + bool bAddBottom = axesOverlapping( KDChartAxisParams::AxisPosBottom, + KDChartAxisParams::AxisPosBottom2 ); + // iterate over all axes + uint iAxis; + for ( iAxis = 0; iAxis < KDCHART_MAX_AXES; ++iAxis ) { + //qDebug( "iAxis %i", iAxis ); + const KDChartAxisParams& para = params()->axisParams( iAxis ); + int areaSize = 0; + + if ( para.axisVisible() + && KDChartAxisParams::AxisTypeUnknown != para.axisType() ) { + + const KDChartAxisParams::AxisPos + basicPos( KDChartAxisParams::basicAxisPos( iAxis ) ); + + int areaMin = para.axisAreaMin(); + int areaMax = para.axisAreaMax(); + if ( 0 > areaMin ) + areaMin = static_cast < int > ( -1.0 * averageValueP1000 * areaMin ); + if ( 0 > areaMax ) + areaMax = static_cast < int > ( -1.0 * averageValueP1000 * areaMax ); + + // make sure areaMin will not be too small + // for the label texts and check if there is an axis Title + switch ( basicPos ) { + case KDChartAxisParams::AxisPosBottom: + case KDChartAxisParams::AxisPosTop: + if ( para.axisLabelsVisible() ) { + int fntHeight; + if ( para.axisLabelsFontUseRelSize() ) + fntHeight = QMAX(static_cast < int > ( para.axisLabelsFontRelSize() * averageValueP1000 ), + para.axisLabelsFontMinSize() ); + else { + painter->setFont( para.axisLabelsFont() ); + QFontMetrics metrics( painter->fontMetrics() ); + fntHeight = metrics.height(); + } + // adjust text height in case of formatted Date/Time values + uint dataDataset, dataDataset2; + if( !params()->findDataset( KDChartParams::DataEntry, + dataDataset, + dataDataset2, + KDCHART_ALL_CHARTS ) ) { + qDebug( "IMPLEMENTATION ERROR: findDataset( DataEntry, ... ) should *always* return true. (a)" ); + dataDataset = KDCHART_ALL_DATASETS; + } + QVariant::Type valType = QVariant::Invalid; + const bool dataCellsHaveSeveralCoordinates = + (KDCHART_ALL_DATASETS == dataDataset) + ? data->cellsHaveSeveralCoordinates( &valType ) + : data->cellsHaveSeveralCoordinates( dataDataset, dataDataset2, &valType ); + QString format( para.axisLabelsDateTimeFormat() ); + if( dataCellsHaveSeveralCoordinates + && QVariant::DateTime == valType ){ + if( KDCHART_AXIS_LABELS_AUTO_DATETIME_FORMAT == format ) + areaMin = QMAX( areaMin, static_cast < int > ( fntHeight * 6.75 ) ); + else + areaMin = QMAX( areaMin, fntHeight * ( 3 + format.contains("\n") ) ); + } + else + areaMin = QMAX( areaMin, fntHeight * 3 ); + } + break; + case KDChartAxisParams::AxisPosLeft: + case KDChartAxisParams::AxisPosRight: + default: + break; + } + + + switch ( para.axisAreaMode() ) { + case KDChartAxisParams::AxisAreaModeAutoSize: + { + areaSize = areaMin; + switch ( basicPos ) { + case KDChartAxisParams::AxisPosBottom: + case KDChartAxisParams::AxisPosTop: + break; + case KDChartAxisParams::AxisPosLeft: + case KDChartAxisParams::AxisPosRight: + if( finalPrecision ){ + internal__KDChart__CalcValues& cv = calcVal[iAxis]; + const int nUsableAxisWidth = static_cast < int > (cv.pTextsW); + const KDChartAxisParams & para = params()->axisParams( iAxis ); + QFont axisLabelsFont( para.axisLabelsFont() ); + if ( para.axisLabelsFontUseRelSize() ) { + axisLabelsFont.setPixelSize( static_cast < int > ( cv.nTxtHeight ) ); + } + painter->setFont( para.axisLabelsFont() ); + QFontMetrics axisLabelsFontMetrics( painter->fontMetrics() ); + const int lenEM( axisLabelsFontMetrics.boundingRect("M").width() ); + const QStringList* labelTexts = para.axisLabelTexts(); + uint nLabels = ( 0 != labelTexts ) + ? labelTexts->count() + : 0; + int maxLabelsWidth = 0; + for ( uint i = 0; i < nLabels; ++i ) + maxLabelsWidth = + QMAX( maxLabelsWidth, + axisLabelsFontMetrics.boundingRect(*labelTexts->at(i)).width() ); + if( nUsableAxisWidth < maxLabelsWidth ) + areaSize = maxLabelsWidth + + (para.axisTrueAreaRect().width() - nUsableAxisWidth) + + lenEM; + } + break; + default: + break; + } + } + break; + case KDChartAxisParams::AxisAreaModeMinMaxSize: + { + qDebug( "Sorry, not implemented: AxisAreaModeMinMaxSize" ); + } + + // + // + // F E A T U R E P L A N N E D F O R F U T U R E . . . + // + // + + // break; + + case KDChartAxisParams::AxisAreaModeFixedSize: + { + areaSize = areaMax ? QMIN( areaMin, areaMax ) : areaMin; + } + break; + } + + //find out if there is a title box + uint idx; + int boxSize = 0; + for( idx = 0; idx <= params()->maxCustomBoxIdx(); ++idx ) { + const KDChartCustomBox * box = params()->customBox( idx ); + if ( box ) + if ( box->parentAxisArea() == KDChartAxisParams::AxisPosBottom + || box->parentAxisArea() == KDChartAxisParams::AxisPosLeft + || box->parentAxisArea() == KDChartAxisParams::AxisPosTop + || box->parentAxisArea() == KDChartAxisParams::AxisPosRight ) + boxSize = box->trueRect(QPoint( 0,0 ), _areaWidthP1000, _areaHeightP1000 ).height(); + } + + areaSize += boxSize; + + switch ( basicPos ) { + case KDChartAxisParams::AxisPosBottom: + if( bAddBottom ) { + //areaSize += boxSize; + nAxesBottomADD += areaSize; + } + else{ + // areaSize += boxSize; + nAxesBottomADD = QMAX( nAxesBottomADD + boxSize, areaSize ); + } + break; + case KDChartAxisParams::AxisPosLeft: + if( bAddLeft ) + nAxesLeftADD += areaSize; + else + nAxesLeftADD = QMAX( nAxesLeftADD + boxSize, areaSize ); + break; + case KDChartAxisParams::AxisPosTop: + if( bAddTop ) + nAxesTopADD += areaSize; + else + nAxesTopADD = QMAX( nAxesTopADD + boxSize, areaSize ); + break; + case KDChartAxisParams::AxisPosRight: + if( bAddRight ) + nAxesRightADD += areaSize; + else + nAxesRightADD = QMAX( nAxesRightADD + boxSize, areaSize ); + break; + default: + break; + } + } + // Note: to prevent users from erroneously calling this + // function we do *not* provide a wrapper for it + // in the KDChartParams class but rather call it + // *directly* using a dirty typecast. + ( ( KDChartAxisParams& ) para ).setAxisTrueAreaSize( areaSize ); + } + int nMinDistance = static_cast < int > ( 30.0 * averageValueP1000 ); + + int nAxesBottom = QMAX( nAxesBottom0 + nAxesBottomADD, nMinDistance ); + + // for micro alignment with the X axis, we adjust the Y axis - but not for Area Charts: + // otherwise the areas drawn would overwrite the Y axis line. + int nAxesLeft = QMAX( nAxesLeft0 + nAxesLeftADD, nMinDistance ) + - (bIsAreaChart ? 0 : 1); + + int nAxesTop = QMAX( nAxesTop0 + nAxesTopADD, nMinDistance ); + + int nAxesRight = QMAX( nAxesRight0 + nAxesRightADD, nMinDistance ); + + int nBottom = params()->axisParams( KDChartAxisParams::AxisPosBottom ).axisTrueAreaSize(); + int nLeft = params()->axisParams( KDChartAxisParams::AxisPosLeft ).axisTrueAreaSize(); + int nTop = params()->axisParams( KDChartAxisParams::AxisPosTop ).axisTrueAreaSize(); + int nRight = params()->axisParams( KDChartAxisParams::AxisPosRight ).axisTrueAreaSize(); + int nBottom2 = params()->axisParams( KDChartAxisParams::AxisPosBottom2 ).axisTrueAreaSize(); + int nLeft2 = params()->axisParams( KDChartAxisParams::AxisPosLeft2 ).axisTrueAreaSize(); + int nTop2 = params()->axisParams( KDChartAxisParams::AxisPosTop2 ).axisTrueAreaSize(); + int nRight2 = params()->axisParams( KDChartAxisParams::AxisPosRight2 ).axisTrueAreaSize(); + + internSetAxisArea( _params, + KDChartAxisParams::AxisPosBottom, + _outermostRect.left() + nAxesLeft, + _outermostRect.top() + trueHeight - nAxesBottom, + trueWidth - nAxesLeft - nAxesRight + 1, + nBottom ); + internSetAxisArea( _params, + KDChartAxisParams::AxisPosLeft, + _outermostRect.left() + (bAddLeft ? nAxesLeft0 + nLeft2 : nAxesLeft0), + _outermostRect.top() + nAxesTop, + nLeft, + trueHeight - nAxesTop - nAxesBottom + 1 ); + + internSetAxisArea( _params, + KDChartAxisParams::AxisPosTop, + _outermostRect.left() + nAxesLeft, + _outermostRect.top() + (bAddTop ? nAxesTop0 + nTop2 : nAxesTop0), + trueWidth - nAxesLeft - nAxesRight + 1, + nTop ); + internSetAxisArea( _params, + KDChartAxisParams::AxisPosRight, + _outermostRect.left() + trueWidth - nAxesRight, + _outermostRect.top() + nAxesTop, + nRight, + trueHeight - nAxesTop - nAxesBottom + 1 ); + + internSetAxisArea( _params, + KDChartAxisParams::AxisPosBottom2, + _outermostRect.left() + nAxesLeft, + _outermostRect.top() + trueHeight - nAxesBottom + (bAddBottom ? nBottom : 0), + trueWidth - nAxesLeft - nAxesRight + 1, + nBottom2 ); + internSetAxisArea( _params, + KDChartAxisParams::AxisPosLeft2, + _outermostRect.left() + nAxesLeft0, + _outermostRect.top() + nAxesTop, + nLeft2, + trueHeight - nAxesTop - nAxesBottom + 1 ); + + internSetAxisArea( _params, + KDChartAxisParams::AxisPosTop2, + _outermostRect.left() + nAxesLeft, + _outermostRect.top() + nAxesTop0, + trueWidth - nAxesLeft - nAxesRight + 1, + nTop2 ); + internSetAxisArea( _params, + KDChartAxisParams::AxisPosRight2, + _outermostRect.left() + trueWidth - nAxesRight + (bAddRight ? nRight : 0), + _outermostRect.top() + nAxesTop, + nRight2, + trueHeight - nAxesTop - nAxesBottom + 1 ); + + _dataRect = QRect( _outermostRect.left() + nAxesLeft, + _outermostRect.top() + nAxesTop, + trueWidth - nAxesLeft - nAxesRight + 1, + trueHeight - nAxesTop - nAxesBottom + 1 ); +} + + + +/** + This method will be called whenever any parameters that affect + geometry have been changed. It will compute the appropriate + positions for the various parts of the chart (legend, axes, data + area etc.). The implementation in KDChartPainter computes a + standard geometry that should be suitable for most chart + types. Subclasses can provide their own implementations. + + \param data the data that will be displayed as a chart + \param drawRect the position and size of the area where the chart + is to be displayed in + */ +void KDChartPainter::setupGeometry( QPainter* painter, + KDChartTableDataBase* data, + const QRect& drawRect ) +{ + //qDebug("INVOKING: KDChartPainter::setupGeometry()"); + // avoid recursion from repaint() being called due to params() changed signals... + const bool oldBlockSignalsState = params()->signalsBlocked(); + const_cast < KDChartParams* > ( params() )->blockSignals( true ); + + _outermostRect = drawRect; + + int yposTop = _outermostRect.topLeft().y(); + int xposLeft = _outermostRect.topLeft().x(); + int yposBottom = _outermostRect.bottomRight().y(); + int xposRight = _outermostRect.bottomRight().x(); + + const int trueWidth = _outermostRect.width(); + const int trueHeight = _outermostRect.height(); + + // Temporary values used to calculate start values xposLeft, yposTop, xposRight, yposBottom. + // They will be replaced immediately after these calculations. + _areaWidthP1000 = trueWidth / 1000.0; + _areaHeightP1000 = trueHeight / 1000.0; + + + xposLeft += 0 < params()->globalLeadingLeft() + ? params()->globalLeadingLeft() + : static_cast < int > ( params()->globalLeadingLeft() * -_areaWidthP1000 ); + yposTop += 0 < params()->globalLeadingTop() + ? params()->globalLeadingTop() + : static_cast < int > ( params()->globalLeadingTop() * -_areaHeightP1000 ); + xposRight -= 0 < params()->globalLeadingRight() + ? params()->globalLeadingRight() + : static_cast < int > ( params()->globalLeadingRight() * -_areaWidthP1000 ); + yposBottom -= 0 < params()->globalLeadingBottom() + ? params()->globalLeadingBottom() + : static_cast < int > ( params()->globalLeadingBottom()* -_areaHeightP1000 ); + + _innermostRect = QRect( QPoint(xposLeft, yposTop), + QPoint(xposRight, yposBottom) ); + + _logicalWidth = xposRight - xposLeft; + _logicalHeight = yposBottom - yposTop; + + // true values (having taken the global leadings into account) + // to be used by all following functions + _areaWidthP1000 = _logicalWidth / 1000.0; + _areaHeightP1000 = _logicalHeight / 1000.0; + + double averageValueP1000 = QMIN(_areaWidthP1000, _areaHeightP1000);//( _areaWidthP1000 + _areaHeightP1000 ) / 2.0; + + // new code design: + // 1. now min-header-leading is text height/2 + // 2. leading or legendSpacing (whichever is larger) + // will be added if legend is below the header(s) + // 3. leading will be added between header and data area + // in case there is no top legend but grid is to be shown. + int headerLineLeading = calculateHdFtRects( + painter, + averageValueP1000, + xposLeft, xposRight, + false, + yposTop, yposBottom ); + calculateHdFtRects( + painter, + averageValueP1000, + xposLeft, xposRight, + true, + yposTop, yposBottom ); + + // Calculate legend position. First check whether there is going + // to be a legend at all: + if ( params()->legendPosition() != KDChartParams::NoLegend ) { + // Now calculate the size needed for the legend + findLegendTexts( data ); + + bool hasLegendTitle = false; + if ( !params()->legendTitleText().isEmpty() ) + hasLegendTitle = true; + + _legendTitleWidth = 0; + if( _legendTitle ) + delete _legendTitle; + _legendTitle = 0; + if ( hasLegendTitle ) { + const QFont font( trueLegendTitleFont() ); + painter->setFont( font ); + QFontMetrics legendTitleMetrics( painter->fontMetrics() ); + _legendTitleMetricsHeight = legendTitleMetrics.height(); + + _legendTitle = new KDChartTextPiece( painter, + params()->legendTitleText(), + font ); + _legendTitleWidth = _legendTitle->width(); + _legendTitleHeight = _legendTitle->height(); + // qDebug("1. _legendTitleHeight %i",_legendTitleHeight); + } + + painter->setFont( trueLegendFont() ); + QFontMetrics legendMetrics( painter->fontMetrics() ); + _legendSpacing = legendMetrics.lineSpacing(); + _legendHeight = legendMetrics.height(); + _legendLeading = legendMetrics.leading(); + + _legendEMSpace = legendMetrics.width( 'M' ); + + int sizeX = 0; + int sizeY = 0; + + for ( int dataset = 0; dataset < _numLegendTexts; dataset++ ) { + sizeX = QMAX( sizeX, legendMetrics.width( _legendTexts[ dataset ] ) ); + if( !_legendTexts[ dataset ].isEmpty() ) + sizeY += _legendSpacing; + } + // add space below the legend's bottom line + sizeY += _legendEMSpace - _legendLeading; + // add space for the legend title if any was set + if ( hasLegendTitle ) + sizeY += legendTitleVertGap(); + + // assume 4 em spaces: before the color box, the color box, after the + // color box and after the legend text + sizeX += ( _legendEMSpace * 4 ); + + // We cannot setup the title width earlier as the title does + // not have a color box. The two em spaces are before the + // color box (where the title does not start yet, it is + // left-aligned with the color boxes) and after the title (to + // have some space before the boundary line comes). + sizeX = QMAX( sizeX, _legendTitleWidth + _legendEMSpace*2 ); + + //qDebug("setupGeometry mustDrawVerticalLegend: %s", mustDrawVerticalLegend() ? "YES":"NO "); + + // PENDING Michel: do that after having calculated the position + if( !mustDrawVerticalLegend() ){ + QSize size; + calculateHorizontalLegendSize( painter, + size, + _legendNewLinesStartAtLeft ); + sizeX = size.width(); + sizeY = size.height(); + } + + switch ( params()->legendPosition() ) { + case KDChartParams::LegendTop: + if ( headerLineLeading ) + yposTop += QMAX( (int)params()->legendSpacing(), headerLineLeading ); + _legendRect = QRect( xposLeft + ( (xposRight-xposLeft) - sizeX ) / 2, + yposTop, sizeX, sizeY ); + yposTop = _legendRect.bottom() + params()->legendSpacing(); + //qDebug("A: _legendRect:\n%i,%i\n%i,%i", _legendRect.left(),_legendRect.top(),_legendRect.right(),_legendRect.bottom() ); + break; + case KDChartParams::LegendBottom: + if ( params()->showGrid() ) + yposTop += headerLineLeading; + _legendRect = QRect( xposLeft + ( (xposRight-xposLeft) - sizeX ) / 2, + yposBottom - sizeY, + sizeX, sizeY ); + yposBottom = _legendRect.top() - params()->legendSpacing(); + break; + case KDChartParams::LegendLeft: + if ( params()->showGrid() ) + yposTop += headerLineLeading; + _legendRect = QRect( xposLeft + 1, ( yposBottom - yposTop - sizeY ) / 2 + + yposTop, + sizeX, sizeY ); + xposLeft = _legendRect.right() + params()->legendSpacing(); + break; + case KDChartParams::LegendRight: + if ( params()->showGrid() ) + yposTop += headerLineLeading; + _legendRect = QRect( xposRight - sizeX - 1, + ( yposBottom - yposTop - sizeY ) / 2 + yposTop, + sizeX, sizeY ); + xposRight = _legendRect.left() - params()->legendSpacing(); + break; + case KDChartParams::LegendTopLeft: + if ( headerLineLeading ) + yposTop += QMAX( (int)params()->legendSpacing(), headerLineLeading ); + _legendRect = QRect( xposLeft + 1, yposTop, sizeX, sizeY ); + yposTop = _legendRect.bottom() + params()->legendSpacing(); + xposLeft = _legendRect.right() + params()->legendSpacing(); + break; + case KDChartParams::LegendTopLeftTop: + if ( headerLineLeading ) + yposTop += QMAX( (int)params()->legendSpacing(), headerLineLeading ); + _legendRect = QRect( xposLeft + 1, yposTop, sizeX, sizeY ); + yposTop = _legendRect.bottom() + params()->legendSpacing(); + break; + case KDChartParams::LegendTopLeftLeft: + if ( headerLineLeading ) + yposTop += QMAX( (int)params()->legendSpacing(), headerLineLeading ); + _legendRect = QRect( xposLeft + 1, yposTop, sizeX, sizeY ); + xposLeft = _legendRect.right() + params()->legendSpacing(); + break; + case KDChartParams::LegendTopRight: + if ( headerLineLeading ) + yposTop += QMAX( (int)params()->legendSpacing(), headerLineLeading ); + _legendRect = QRect( xposRight - sizeX - 1, + yposTop, sizeX, sizeY ); + yposTop = _legendRect.bottom() + params()->legendSpacing(); + xposRight = _legendRect.left() - params()->legendSpacing(); + break; + case KDChartParams::LegendTopRightTop: + if ( headerLineLeading ) + yposTop += QMAX( (int)params()->legendSpacing(), headerLineLeading ); + _legendRect = QRect( xposRight - sizeX - 1, + yposTop, sizeX, sizeY ); + yposTop = _legendRect.bottom() + params()->legendSpacing(); + break; + case KDChartParams::LegendTopRightRight: + if ( headerLineLeading ) + yposTop += QMAX( (int)params()->legendSpacing(), headerLineLeading ); + _legendRect = QRect( xposRight - sizeX - 1, + yposTop, sizeX, sizeY ); + xposRight = _legendRect.left() - params()->legendSpacing(); + break; + case KDChartParams::LegendBottomLeft: + if ( params()->showGrid() ) + yposTop += headerLineLeading; + _legendRect = QRect( xposLeft + 1, yposBottom - sizeY, sizeX, sizeY ); + yposBottom = _legendRect.top() - params()->legendSpacing(); + xposLeft = _legendRect.right() + params()->legendSpacing(); + break; + case KDChartParams::LegendBottomLeftBottom: + if ( params()->showGrid() ) + yposTop += headerLineLeading; + _legendRect = QRect( xposLeft + 1, yposBottom - sizeY, sizeX, sizeY ); + yposBottom = _legendRect.top() - params()->legendSpacing(); + break; + case KDChartParams::LegendBottomLeftLeft: + if ( params()->showGrid() ) + yposTop += headerLineLeading; + _legendRect = QRect( xposLeft + 1, yposBottom - sizeY, sizeX, sizeY ); + xposLeft = _legendRect.right() + params()->legendSpacing(); + break; + case KDChartParams::LegendBottomRight: + if ( params()->showGrid() ) + yposTop += headerLineLeading; + _legendRect = QRect( xposRight - sizeX - 1, + yposBottom - sizeY, sizeX, sizeY ); + yposBottom = _legendRect.top() - params()->legendSpacing(); + xposRight = _legendRect.left() - params()->legendSpacing(); + break; + case KDChartParams::LegendBottomRightBottom: + if ( params()->showGrid() ) + yposTop += headerLineLeading; + _legendRect = QRect( xposRight - sizeX - 1, + yposBottom - sizeY, sizeX, sizeY ); + yposBottom = _legendRect.top() - params()->legendSpacing(); + break; + case KDChartParams::LegendBottomRightRight: + if ( params()->showGrid() ) + yposTop += headerLineLeading; + _legendRect = QRect( xposRight - sizeX - 1, + yposBottom - sizeY, sizeX, sizeY ); + xposRight = _legendRect.left() - params()->legendSpacing(); + break; + default: + // Should not be able to happen + qDebug( "KDChart: Unknown legend position" ); + } + _params->setLegendArea( _legendRect ); + + }else{ + _params->setLegendArea( QRect(QPoint(0,0), QSize(0,0)) ); + } + + + _axesRect = QRect( QPoint(xposLeft, yposTop), QPoint(xposRight, yposBottom) ); + + // important rule: do *not* calculate axes areas for Polar charts! + // (even if left and bottom axes might be set active) + if( KDChartParams::Polar == params()->chartType() ) { + _dataRect = _axesRect; + } else { + // 1st step: make a preliminary approximation of the axes sizes, + // as a basis of following label texts calculation + calculateAllAxesRects( painter, false, data ); + // 2nd step: calculate all labels (preliminary data, will be + // overwritten by KDChartAxesPainter) + // to find out the longest possible axis labels + double dblDummy; + if( calculateAllAxesLabelTextsAndCalcValues( + painter, + data, + _areaWidthP1000, + _areaHeightP1000, + dblDummy ) ) + // 3rd step: calculate the _true_ axes rects based upon + // the preliminary axes labels + calculateAllAxesRects( painter, true, data ); + } + _params->setDataArea( _dataRect ); + + const_cast < KDChartParams* > ( params() )->blockSignals( oldBlockSignalsState ); +} + + +/** + This method implements the algorithm to find the texts for the legend. + */ +void KDChartPainter::findLegendTexts( KDChartTableDataBase* data ) +{ + uint dataset; + QVariant vValY; + switch ( params()->legendSource() ) { + case KDChartParams::LegendManual: { + // The easiest case: Take manually set strings, no matter whether any + // have been set. + _numLegendTexts = numLegendFallbackTexts( data ); + for ( dataset = 0; dataset < static_cast<uint>(_numLegendTexts); dataset++ ) + _legendTexts[ dataset ] = params()->legendText( dataset ); + break; + } + case KDChartParams::LegendFirstColumn: { + // Take whatever is in the first column + for ( dataset = 0; dataset < data->usedRows(); dataset++ ){ + if( data->cellCoord( dataset, 0, vValY, 1 ) ){ + if( QVariant::String == vValY.type() ) + _legendTexts[ dataset ] = vValY.toString(); + else + _legendTexts[ dataset ] = ""; + } + } + _numLegendTexts = data->usedRows(); + break; + } + case KDChartParams::LegendAutomatic: { + // First, try the first row + bool notfound = false; + _numLegendTexts = numLegendFallbackTexts( data ); // assume this for cleaner + // code below + for ( dataset = 0; dataset < data->usedRows(); dataset++ ) { + if( data->cellCoord( dataset, 0, vValY, 1 ) ){ + if( QVariant::String == vValY.type() ) + _legendTexts[ dataset ] = vValY.toString(); + else + _legendTexts[ dataset ] = ""; + if( _legendTexts[ dataset ].isEmpty() ){ + notfound = true; + break; + } + } + } + + // If there were no entries for all the datasets, use the manually set + // texts, and resort to Series 1, Series 2, ... where nothing has been + // set. + if ( notfound ) { + for ( dataset = 0; dataset < numLegendFallbackTexts( data ); + dataset++ ) { + _legendTexts[ dataset ] = params()->legendText( dataset ); + if ( _legendTexts[ dataset ].isEmpty() || _legendTexts[ dataset ].isNull() ) { + _legendTexts[ dataset ] = fallbackLegendText( dataset ); + // there + _numLegendTexts = numLegendFallbackTexts( data ); + } + } + } + break; + } + default: + // Should not happen + qDebug( "KDChart: Unknown legend source" ); + } +} + + +/** + This method provides a fallback legend text for the specified + dataset, if there was no other way to determine a legend text, but + a legend should be shown nevertheless. The default is to return + "Series" plus a dataset number (with datasets starting at 1 for + this purpose; inherited painter implementations can override this. + + 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 KDChartPainter::fallbackLegendText( uint dataset ) const +{ + return QObject::tr( "Series " ) + QString::number( dataset + 1 ); +} + + +/** + This methods returns the number of elements to be shown in the + legend in case fallback texts are used. By default, this will be + the number of datasets, but specialized painters can override this + (e.g., painters that draw charts that can only display one dataset + will return the number of values instead). + + 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 KDChartPainter::numLegendFallbackTexts( KDChartTableDataBase* data ) const +{ + return data->usedRows(); +} + + +/** + Draws the marker for one data point according to the specified style, color, size. + + \param painter the painter to draw on + \param style what kind of marker is drawn (square, diamond, circle, ...) + \param color the color in which to draw the marker + \param p the center of the marker + \param size the width and height of the marker: both values must be positive. + */ +void KDChartPainter::drawMarker( QPainter* painter, + int style, + const QColor& color, + const QPoint& p, + const QSize& size, + uint align ) +{ + int width = size.width(); + int height = size.height(); + drawMarker( painter, + 0, + 0.0, 0.0, + 0,0, + style, + color, + p, + 0,0,0, + 0, + &width, + &height, + align ); +} + + +/** + Draws the marker for one data point according to the specified style. + + \param painter the painter to draw on + \param style what kind of marker is drawn (square, diamond, circle, ...) + \param color the color in which to draw the marker + \param p the center of the marker + \param dataset the dataset which this marker represents + \param value the value which this marker represents + \param regions a list of regions for data points, a new region for the new + marker will be appended to this list if it is not 0 + + \return pointer to the KDChartDataRegion that was appended to the regions list, + or zero if if parameter regions was zero + */ +KDChartDataRegion* KDChartPainter::drawMarker( QPainter* painter, + const KDChartParams* params, + double areaWidthP1000, + double areaHeightP1000, + int deltaX, + int deltaY, + int style, + const QColor& color, + const QPoint& _p, + uint dataset, uint value, uint chart, + KDChartDataRegionList* regions, + int* width, + int* height, + uint align ) +{ + KDChartDataRegion* datReg = 0; + const double areaSizeP1000 = QMIN(areaWidthP1000, areaHeightP1000); + int xsize = width ? *width : (params ? params->lineMarkerSize().width() : 12); + if( 0 > xsize ) + xsize = static_cast < int > (xsize * -areaSizeP1000); + int ysize = height ? *height : (params ? params->lineMarkerSize().height() : 12); + if( 0 > ysize ) + ysize = static_cast < int > (ysize * -areaSizeP1000); + if( KDChartParams::LineMarkerCross != style ){ + xsize = QMAX( xsize, 4 ); + ysize = QMAX( ysize, 4 ); + } + uint xsize2 = xsize / 2; + uint ysize2 = ysize / 2; + uint xsize4 = xsize / 4; + uint ysize4 = ysize / 4; + uint xsize6 = xsize / 6; + uint ysize6 = ysize / 6; + painter->setPen( color ); + const uint xysize2 = QMIN( xsize2, ysize2 ); + + int x = _p.x(); + int y = _p.y(); + if( align & Qt::AlignLeft ) + x += xsize2; + else if( align & Qt::AlignRight ) + x -= xsize2; + if( align & Qt::AlignTop ) + y += ysize2; + else if( align & Qt::AlignBottom ) + y -= ysize2; + const QPoint p(x, y); + + switch ( style ) { + case KDChartParams::LineMarkerSquare: { + const QPen oldPen( painter->pen() ); + const QBrush oldBrush( painter->brush() ); + painter->setBrush( color ); + painter->setPen( color ); + QRect rect( QPoint( p.x() - xsize2, p.y() - ysize2 ), QPoint( p.x() + xsize2, p.y() + ysize2 ) ); + painter->drawRect( rect ); + // Don't use rect for drawing after this! + rect.moveBy( deltaX, deltaY ); + if ( regions ){ + datReg = + new KDChartDataRegion( + dataset, value, + chart, rect ); + regions->append( datReg ); + } + painter->setPen( oldPen ); + painter->setBrush( oldBrush ); + break; + } + case KDChartParams::LineMarkerDiamond:{ + const QBrush oldBrush( painter->brush() ); + painter->setBrush( color ); + QPointArray points( 4 ); + points.setPoint( 0, p.x() - xsize2, p.y() ); + points.setPoint( 1, p.x(), p.y() - ysize2 ); + points.setPoint( 2, p.x() + xsize2, p.y() ); + points.setPoint( 3, p.x(), p.y() + ysize2 ); + painter->drawPolygon( points ); + // Don't use points for drawing after this! + points.translate( deltaX, deltaY ); + if ( regions ){ + datReg = new KDChartDataRegion( + dataset, value, + chart, points ); + regions->append( datReg ); + } + painter->setBrush( oldBrush ); + break; + } + case KDChartParams::LineMarker1Pixel: { + QRect rect( p, p ); + painter->drawRect( rect ); + // Don't use rect for drawing after this! + rect.moveBy( deltaX, deltaY ); + if ( regions ){ + datReg = new KDChartDataRegion( + dataset, value, + chart, rect ); + regions->append( datReg ); + } + break; + } + case KDChartParams::LineMarker4Pixels:{ + QRect rect( p, QPoint( p.x()+1, p.y()+1 ) ); + painter->drawRect( rect ); + // Don't use rect for drawing after this! + rect.moveBy( deltaX, deltaY ); + if ( regions ){ + datReg = new KDChartDataRegion( + dataset, value, + chart, rect ); + regions->append( datReg ); + } + break; + } + case KDChartParams::LineMarkerRing: { + const QPen oldPen( painter->pen() ); + painter->setPen( QPen( color, QMIN(xsize4, ysize4) ) ); + const QBrush oldBrush( painter->brush() ); + painter->setBrush( Qt::NoBrush ); + painter->drawEllipse( p.x() - xsize2, p.y() - ysize2, xsize, ysize ); + if ( regions ) { + QPointArray points; + points.makeEllipse( p.x() - xsize2, p.y() - ysize2, xsize, ysize ); + // Don't use points for drawing after this! + points.translate( deltaX, deltaY ); + if( points.size() > 0 ){ + datReg = new KDChartDataRegion( + dataset, value, + chart, points ); + regions->append( datReg ); + } + } + painter->setBrush( oldBrush ); + painter->setPen( oldPen ); + break; + } + case KDChartParams::LineMarkerCross: { + const QPen oldPen( painter->pen() ); + painter->setPen( color ); + const QBrush oldBrush( painter->brush() ); + painter->setBrush( color ); + int numPoints = (ysize && xsize) ? 12 : 4; + QPointArray points( numPoints ); + if( ysize && xsize ){ + points.setPoint( 0, p.x() - xsize6, p.y() - ysize6 ); + points.setPoint( 1, p.x() - xsize6, p.y() - ysize2 ); + points.setPoint( 2, p.x() + xsize6, p.y() - ysize2 ); + points.setPoint( 3, p.x() + xsize6, p.y() - ysize6 ); + points.setPoint( 4, p.x() + xsize2, p.y() - ysize6 ); + points.setPoint( 5, p.x() + xsize2, p.y() + ysize6 ); + points.setPoint( 6, p.x() + xsize6, p.y() + ysize6 ); + points.setPoint( 7, p.x() + xsize6, p.y() + ysize2 ); + points.setPoint( 8, p.x() - xsize6, p.y() + ysize2 ); + points.setPoint( 9, p.x() - xsize6, p.y() + ysize6 ); + points.setPoint(10, p.x() - xsize2, p.y() + ysize6 ); + points.setPoint(11, p.x() - xsize2, p.y() - ysize6 ); + }else if( ysize ){ + points.setPoint( 0, p.x() - ysize6, p.y() - ysize2 ); + points.setPoint( 1, p.x() + ysize6, p.y() - ysize2 ); + points.setPoint( 2, p.x() + ysize6, p.y() + ysize2 ); + points.setPoint( 3, p.x() - ysize6, p.y() + ysize2 ); + }else{ + points.setPoint( 0, p.x() - xsize2, p.y() - xsize6 ); + points.setPoint( 1, p.x() + xsize2, p.y() - xsize6 ); + points.setPoint( 2, p.x() + xsize2, p.y() + xsize6 ); + points.setPoint( 3, p.x() - xsize2, p.y() + xsize6 ); + } + painter->drawPolygon( points ); + // Don't use points for drawing after this! + points.translate( deltaX, deltaY ); + if( regions ){ + datReg = new KDChartDataRegion( + dataset, value, + chart, points ); + regions->append( datReg ); + } + painter->setBrush( oldBrush ); + painter->setPen( oldPen ); + break; + } + case KDChartParams::LineMarkerFastCross: { + const QPen oldPen( painter->pen() ); + painter->setPen( color ); + painter->drawLine( QPoint(p.x() - xysize2, p.y()), + QPoint(p.x() + xysize2, p.y()) ); + painter->drawLine( QPoint(p.x(), p.y() - xysize2), + QPoint(p.x(), p.y() + xysize2) ); + QRect rect( QPoint( p.x() - 2, p.y() - 2 ), + QPoint( p.x() + 2, p.y() + 2 ) ); + // Don't use rect for drawing after this! + rect.moveBy( deltaX, deltaY ); + if ( regions ){ + datReg = + new KDChartDataRegion( + dataset, value, + chart, rect ); + regions->append( datReg ); + } + painter->setPen( oldPen ); + break; + } + case KDChartParams::LineMarkerCircle: + default: { + const QBrush oldBrush( painter->brush() ); + painter->setBrush( color ); + painter->drawEllipse( p.x() - xsize2, p.y() - ysize2, xsize, ysize ); + if ( regions ) { + QPointArray points; + points.makeEllipse( p.x() - xsize2, p.y() - ysize2, xsize, ysize ); + // Don't use points for drawing after this! + points.translate( deltaX, deltaY ); + if( points.size() > 0 ){ + datReg = new KDChartDataRegion( + dataset, value, + chart, points ); + regions->append( datReg ); + } + } + painter->setBrush( oldBrush ); + } + } + return datReg; +} + + +void KDChartPainter::drawExtraLinesAndMarkers( + KDChartPropertySet& propSet, + const QPen& defaultPen, + const KDChartParams::LineMarkerStyle& defaultMarkerStyle, + int myPointX, + int myPointY, + QPainter* painter, + const KDChartAxisParams* abscissaPara, + const KDChartAxisParams* ordinatePara, + const double areaWidthP1000, + const double areaHeightP1000, + bool bDrawInFront ) +{ + + // we can safely call the following functions and ignore their + // return values since they will touch the parameters' values + // if the propSet *contains* corresponding own values only. + int iDummy; + uint extraLinesAlign = 0; + if( propSet.hasOwnExtraLinesAlign( iDummy, extraLinesAlign ) + && ( extraLinesAlign + & ( Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter | + Qt::AlignTop | Qt::AlignBottom | Qt::AlignVCenter ) ) ){ + bool extraLinesInFront = false; + propSet.hasOwnExtraLinesInFront( iDummy, extraLinesInFront ); + if( bDrawInFront == extraLinesInFront ){ + const double areaSizeP1000 = QMIN(areaWidthP1000, areaHeightP1000); + int extraLinesLength = -20; + int extraLinesWidth = defaultPen.width(); + QColor extraLinesColor = defaultPen.color(); + Qt::PenStyle extraLinesStyle = defaultPen.style(); + uint extraMarkersAlign = 0; + propSet.hasOwnExtraLinesLength( iDummy, extraLinesLength ); + propSet.hasOwnExtraLinesWidth( iDummy, extraLinesWidth ); + propSet.hasOwnExtraLinesColor( iDummy, extraLinesColor ); + propSet.hasOwnExtraLinesStyle( iDummy, extraLinesStyle ); + const int horiLenP2 = (0 > extraLinesLength) + ? static_cast<int>(areaWidthP1000 * extraLinesLength) / 2 + : extraLinesLength / 2; + const int vertLenP2 = (0 > extraLinesLength) + ? static_cast<int>(areaHeightP1000 * extraLinesLength) / 2 + : extraLinesLength / 2; + // draw the extra line(s) + QPoint pL( (Qt::AlignLeft == (extraLinesAlign & Qt::AlignLeft)) + ? 0 + : (Qt::AlignHCenter == (extraLinesAlign & Qt::AlignHCenter)) + ? myPointX - horiLenP2 + : myPointX, + myPointY ); + QPoint pR( (Qt::AlignRight == (extraLinesAlign & Qt::AlignRight)) + ? abscissaPara->axisTrueAreaRect().width() + : (Qt::AlignHCenter == (extraLinesAlign & Qt::AlignHCenter)) + ? myPointX + horiLenP2 + : myPointX, + myPointY ); + QPoint pT( myPointX, + (Qt::AlignTop == (extraLinesAlign & Qt::AlignTop)) + ? 0 + : (Qt::AlignVCenter == (extraLinesAlign & Qt::AlignVCenter)) + ? myPointY - vertLenP2 + : myPointY ); + QPoint pB( myPointX, + (Qt::AlignBottom == (extraLinesAlign & Qt::AlignBottom)) + ? ordinatePara->axisTrueAreaRect().height() + : (Qt::AlignVCenter == (extraLinesAlign & Qt::AlignVCenter)) + ? myPointY + vertLenP2 + : myPointY ); + const QPen extraPen( extraLinesColor, + 0 > extraLinesWidth + ? static_cast < int > ( areaSizeP1000 * -extraLinesWidth ) + : extraLinesWidth, + extraLinesStyle ); + const QPen oldPen( painter->pen() ); + painter->setPen( extraPen ); + if( extraLinesAlign & ( Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter ) ) + painter->drawLine( pL, pR ); + if( extraLinesAlign & ( Qt::AlignTop | Qt::AlignBottom | Qt::AlignVCenter ) ) + painter->drawLine( pT, pB ); + painter->setPen( oldPen ); + // draw the marker(s) of the extra line(s) + propSet.hasOwnExtraMarkersAlign( iDummy, extraMarkersAlign ); + if( extraMarkersAlign + & ( Qt::AlignLeft | Qt::AlignRight | + Qt::AlignTop | Qt::AlignBottom ) ){ + QSize extraMarkersSize = params()->lineMarkerSize(); + QColor extraMarkersColor = extraLinesColor; + int extraMarkersStyle = defaultMarkerStyle; + propSet.hasOwnExtraMarkersSize( iDummy, extraMarkersSize ); + propSet.hasOwnExtraMarkersColor( iDummy, extraMarkersColor ); + propSet.hasOwnExtraMarkersStyle( iDummy, extraMarkersStyle ); + // draw the extra marker(s) + int w = extraMarkersSize.width(); + int h = extraMarkersSize.height(); + if( w < 0 ) + w = static_cast < int > (w * -areaSizeP1000); + if( h < 0 ) + h = static_cast < int > (h * -areaSizeP1000); + if( extraMarkersAlign & Qt::AlignLeft ) + drawMarker( painter, + params(), + _areaWidthP1000, _areaHeightP1000, + _dataRect.x(), _dataRect.y(), + (KDChartParams::LineMarkerStyle)extraMarkersStyle, + extraMarkersColor, + pL, + 0, 0, 0, 0, + &w, &h, + Qt::AlignCenter ); + if( extraMarkersAlign & Qt::AlignRight ) + drawMarker( painter, + params(), + _areaWidthP1000, _areaHeightP1000, + _dataRect.x(), _dataRect.y(), + (KDChartParams::LineMarkerStyle)extraMarkersStyle, + extraMarkersColor, + pR, + 0, 0, 0, 0, + &w, &h, + Qt::AlignCenter ); + if( extraMarkersAlign & Qt::AlignTop ) + drawMarker( painter, + params(), + _areaWidthP1000, _areaHeightP1000, + _dataRect.x(), _dataRect.y(), + (KDChartParams::LineMarkerStyle)extraMarkersStyle, + extraMarkersColor, + pT, + 0, 0, 0, 0, + &w, &h, + Qt::AlignCenter ); + if( extraMarkersAlign & Qt::AlignBottom ) + drawMarker( painter, + params(), + _areaWidthP1000, _areaHeightP1000, + _dataRect.x(), _dataRect.y(), + (KDChartParams::LineMarkerStyle)extraMarkersStyle, + extraMarkersColor, + pB, + 0, 0, 0, 0, + &w, &h, + Qt::AlignCenter ); + } + } + } +} + + diff --git a/kchart/kdchart/KDChartPainter.h b/kchart/kdchart/KDChartPainter.h new file mode 100644 index 00000000..a93ef7d8 --- /dev/null +++ b/kchart/kdchart/KDChartPainter.h @@ -0,0 +1,296 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTPAINTER_H__ +#define __KDCHARTPAINTER_H__ + +#include <qmap.h> +#include <qrect.h> +#include <qdatetime.h> +#include <qregion.h> +#include <qstring.h> + +#include "KDChartGlobal.h" +#include "KDChartUnknownTypeException.h" +#include "KDChartNotEnoughSpaceException.h" +#include "KDChartTable.h" +#include "KDChartDataRegion.h" +#include "KDChartParams.h" + +class KDChartTableDataBase; +class KDChartCustomBox; +class KDChartTextPiece; +class KDChartPropertySet; +class KDChartAxisParams; +class QPainter; + + +struct internal__KDChart__CalcValues { + bool processThisAxis; + bool bSteadyCalc; + bool bLogarithmic; + bool bDecreasing; + KDChartAxisParams::AxisPos basicPos; + QPoint orig; + QPoint dest; + double pXDeltaFactor; + double pYDeltaFactor; + double pXDelimDeltaFaktor; + double pYDelimDeltaFaktor; + double nSubDelimFactor; + double pDelimDelta; + double nTxtHeight; + double pTextsX; + double pTextsY; + double pTextsW; + double pTextsH; + int textAlign; + bool isDateTime; + bool autoDtLabels; + QDateTime dtLow; + QDateTime dtHigh; + KDChartAxisParams::ValueScale dtDeltaScale; + double nLow; + double nHigh; + double nDelta; + double nDeltaPix; + double pLastX; + double pLastY; +}; + + +class KDCHART_EXPORT KDChartPainter +{ + public: + static KDChartPainter* create( KDChartParams* params, + bool make2nd = false ); + + virtual ~KDChartPainter(); + + static void registerPainter( const QString& painterName, + KDChartPainter* painter ); + static void unregisterPainter( const QString& painterName ); + + virtual void setupGeometry ( QPainter* painter, + KDChartTableDataBase* data, + const QRect& rect ); + + virtual void paint( QPainter* painter, KDChartTableDataBase* data, + bool paintFirst, + bool paintLast, + KDChartDataRegionList* regions = 0, + const QRect* rect = 0, + bool mustCalculateGeometry = true ); + + virtual QRect outermostRect() const { return _outermostRect; } + + static void drawMarker( QPainter* painter, + int style, + const QColor& color, + const QPoint& p, + const QSize& size, + uint align = Qt::AlignCenter ); + + protected: + KDChartPainter( KDChartParams* ); + + // Note: dataRow, dataCol (and data3rd, resp.) + // must only be set if area == KDChartEnums::AreaChartDataRegion + virtual void paintArea( QPainter* painter, + uint area, + KDChartDataRegionList* regions = 0, + uint dataRow = 0, + uint dataCol = 0, + uint data3rd = 0 ); + + virtual void paintDataRegionAreas( QPainter* painter, + KDChartDataRegionList* regions ); + + virtual void paintAreaWithGap( QPainter* painter, + QRect rect, + const KDChartParams::KDChartFrameSettings& settings ); + virtual void paintCustomBoxes( QPainter* painter, + KDChartDataRegionList* regions ); + + virtual void paintData( QPainter* painter, KDChartTableDataBase* data, + bool paint2nd, + KDChartDataRegionList* regions = 0 ) = 0; + virtual void paintDataValues( QPainter* painter, + KDChartTableDataBase* data, + KDChartDataRegionList* regions ); + virtual void paintAxes( QPainter* painter, + KDChartTableDataBase* data ); + virtual void paintLegend( QPainter* painter, + KDChartTableDataBase* data ); + virtual void paintHeaderFooter( QPainter* painter, + KDChartTableDataBase* data ); + virtual bool axesOverlapping( int axis1, int axis2 ); + + virtual void findChartDatasets( KDChartTableDataBase* data, + bool paint2nd, + uint chart, + uint& chartDatasetStart, + uint& chartDatasetEnd ); + + virtual void calculateAllAxesRects( QPainter* painter, + bool finalPrecision, + KDChartTableDataBase* data ); + + virtual QPoint calculateAnchor( const KDChartCustomBox & box, + KDChartDataRegionList* regions = 0 ) const; + virtual QRect calculateAreaRect( bool & allCustomBoxes, + uint area, + uint dataRow, + uint dataCol, + uint data3rd, + KDChartDataRegionList* regions ) const; + + virtual QString fallbackLegendText( uint dataset ) const; + virtual uint numLegendFallbackTexts( KDChartTableDataBase* data ) const; + + static QPoint pointOnCircle( const QRect& rect, double angle ); + static void makeArc( QPointArray& points, + const QRect& rect, + double startAngle, double angles ); + + const KDChartParams* params() const + { + return _params; + } + + QRect _outermostRect; /* The Outermost rect covers the complete + area of the painter. */ + + QRect _innermostRect; /* The Innermost rect covers the area of + the painter MINUS the the global + left/top/right/bottom leading. + ALL following ones are INSIDE the Innermost. + */ + + QRect _dataRect; + + QRect _axesRect; /* The Axes rect contains the Data rect. Up to 4 + axes might be at the left and bottom as well + as at the right and/or at the to top of the + chart. + */ + + QRect _legendRect; /* The legend position depends on the parameter + settings. If it is not directly to the left or + to the right of the data display, it will be + below the headers and on top of the footers. + */ + int _legendEMSpace; // an em space in the legend font + int _legendSpacing; // the line spacing in the legend font + int _legendHeight; // the font height in the legend font + int _legendLeading; // the font leading in the legend font + // int _legendTitleSpacing; // the line spacing in the legend title font + // int _legendTitleLeading; // the font leading in the legend title font + KDChartTextPiece* _legendTitle; + + int _numLegendTexts; // the number of legend items to show + + int _logicalWidth; + int _logicalHeight; + double _areaWidthP1000; + double _areaHeightP1000; + + QMap < int, QString > _legendTexts; // precomputed legend texts + + internal__KDChart__CalcValues calcVal[ KDCHART_MAX_AXES ]; + virtual bool calculateAllAxesLabelTextsAndCalcValues( + QPainter* painter, + KDChartTableDataBase* data, + double areaWidthP1000, + double areaHeightP1000, + double& delimLen ); + + virtual void drawExtraLinesAndMarkers( + KDChartPropertySet& propSet, + const QPen& defaultPen, + const KDChartParams::LineMarkerStyle& defaultMarkerStyle, + int myPointX, + int myPointY, + QPainter* painter, + const KDChartAxisParams* abscissaPara, + const KDChartAxisParams* ordinatePara, + const double areaWidthP1000, + const double areaHeightP1000, + bool bDrawInFront = FALSE ); + + static KDChartDataRegion* drawMarker( QPainter* painter, + const KDChartParams* params, + double areaWidthP1000, + double areaHeightP1000, + int deltaX, + int deltaY, + int style, + const QColor& color, + const QPoint& p, + uint dataset, uint value, uint chart, + KDChartDataRegionList* regions = 0, + int* width = 0, + int* height = 0, + uint align = Qt::AlignCenter ); + + private: + // disallow copy-construction and assignment + KDChartPainter( const KDChartPainter& ); + KDChartPainter& operator=( const KDChartPainter& ); + + + QMap < QString, KDChartPainter* > _customPainters; + KDChartParams* _params; + + QRect trueFrameRect( const QRect& orgRect, + const KDChartParams::KDChartFrameSettings* settings ) const; + + int legendTitleVertGap() const; + QFont trueLegendTitleFont() const; + QFont trueLegendFont() const; + void calculateHorizontalLegendSize( QPainter* painter, + QSize& size, + bool& legendNewLinesStartAtLeft ) const; + bool mustDrawVerticalLegend() const; + void findLegendTexts( KDChartTableDataBase* ); + int calculateHdFtRects( QPainter* painter, + double averageValueP1000, + int xposLeft, + int xposRight, + bool bHeader, + int& yposTop, + int& yposBottom ); + bool _legendNewLinesStartAtLeft; + int _legendTitleHeight; + int _legendTitleWidth; + int _legendTitleMetricsHeight; // the font height in the legend title font + int _hdLeading; + int _ftLeading; +}; + +#endif diff --git a/kchart/kdchart/KDChartParams.cpp b/kchart/kdchart/KDChartParams.cpp new file mode 100644 index 00000000..94fd117e --- /dev/null +++ b/kchart/kdchart/KDChartParams.cpp @@ -0,0 +1,9564 @@ +/* -*- 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. + ** + **********************************************************************/ +#include <KDChartParams.h> +#include <KDChartAxisParams.h> +#include <KDChartEnums.h> +#include <KDFrame.h> +#include <KDChartCustomBox.h> +#include <KDChartTextPiece.h> +#include <KDXMLTools.h> +#ifndef KDCHART_MASTER_CVS +#include "KDChartParams.moc" +#endif + +class KDChartData; + +//#include <qdom.h> + +/** + \class KDChartParams KDChartParams.h + \brief Bundles the display parameters of a chart. + + Bundles all parameters of a chart including the type except the + actual data displayed. Serializing an object of this type plus the + data displayed is enough to be able to recreate the chart later. + */ + + + +//QColor KDChartParams::_internalPointer_DataValueAutoColor = QColor( 0,1,0 ); + +KDChartAutoColor* KDChartAutoColor::mInstance = NULL; + +KDChartAutoColor::KDChartAutoColor() +{ +} + +KDChartAutoColor::KDChartAutoColor( KDChartAutoColor const& ) +{ +} + +KDChartAutoColor::~KDChartAutoColor() +{ +} + +const KDChartAutoColor* KDChartAutoColor::instance() +{ + if( mInstance == 0 ) + mInstance = new KDChartAutoColor(); + return mInstance; +} + +void KDChartAutoColor::freeInstance() +{ + if( mInstance ) + delete mInstance; + mInstance = 0; +} + + +/* +static QColor defaultColor; +QT_STATIC_CONST_IMPL QColor & KDChartParams_AutoColor = defaultColor; +*/ + +/** + Constructor. Defines default values. + */ +KDChartParams::KDChartParams() +{ + tempPropSetA = new KDChartPropertySet(); + tempPropSetB = new KDChartPropertySet(); + + // GENERAL + + // Avoid Purify UMR + _maxDatasetColor = 1; + _maxDatasetSourceMode = 0; + _setChartSourceModeWasUsed = false; + + _customBoxDictMayContainHoles = false; + + // Set the default font params flag for data value texts + // but do *not* call setPrintDataValues() there since + // this will be called internally by setChartType() below. + setPrintDataValuesWithDefaultFontParams( KDCHART_ALL_CHARTS, false ); + _printDataValuesSettings._dataValuesShowInfinite = true; + _printDataValuesSettings2._dataValuesShowInfinite = true; + + setAllowOverlappingDataValueTexts( false ); + +#if COMPAT_QT_VERSION >= 0x030100 + setOptimizeOutputForScreen( false ); +#else + setOptimizeOutputForScreen( true ); +#endif + + setGlobalLeading( 0,0,0,0 ); + + + // Default type is bar charts + setChartType( Bar ); + + // By default, there is no additional chart type + setAdditionalChartType( NoType ); + + // Default is to show all values. + setNumValues( static_cast<unsigned int>( -1 ) ); + + _defaultFont = QFont( "helvetica", 8, QFont::Normal, false ); + + // The default frame settings: no border, no corners, no background + _noFrame.clearAll(); + + // The default frame settings: no inner gap, no outer gap and use the _noFrame + // *** no need to initialize _noFrameSettings: it is initialized by a default c'tor! + + // The default brightness of shadow colors (needs to be set + // before the data colors to avoid an UMR). + setShadowBrightnessFactor( 1.0 ); + + // The default shadow fill style. + setShadowPattern( Qt::SolidPattern ); + + // Some default colors for the data. + setDataDefaultColors(); + + // Default color for data display outlines. + setOutlineDataColor( black ); + + // Default line width for data display outlines. + setOutlineDataLineWidth( 1 ); + + // Default line style for data display outlines. + setOutlineDataLineStyle( Qt::SolidLine ); + + // END GENERAL + + + setDataArea( QRect( QPoint(0,0), QSize(0,0)) ); + setLegendArea(QRect( QPoint(0,0), QSize(0,0)) ); + + + // BAR CHART-SPECIFIC + + // Default bar subtype is normal + setBarChartSubType( BarNormal ); + + // Default is not to draw 3D bars + setThreeDBars( false ); + + // Default is to used shadowed colors for 3D bar effects + setThreeDBarsShadowColors( true ); + + // Default angle for 3D bars is 45 degrees. + setThreeDBarAngle( 45 ); + + // Default depth of 3D bars is 1.0 + setThreeDBarDepth( 1.0 ); + + // Default gap between datasets is 6 per mille of chart data area. + setDatasetGap( 6 ); + setDatasetGapIsRelative( true ); + + // Default gap between value blocks is 24 per mille of chart data area. + setValueBlockGap( 24 ); + setValueBlockGapIsRelative( true ); + + // Default is to have the bar width's calculated indiidually + setBarWidth( KDCHART_AUTO_SIZE ); + // reset the special indicator members storing the number + // of bars that were drawn last time / could not be drawn + // due to too less horizontal space + setBarsDisplayed( 0 ); + setBarsLeft( 0 ); + + // By default, excess arrows are drawn in a split fashion + setDrawSolidExcessArrows( false ); + + // END BAR CHART-SPECIFIC + + + // LINE/AREA CHART-SPECIFIC + // Normal lines by default + setLineChartSubType( LineNormal ); + + // No markers by default + setLineMarker( false ); + + // Lines have a width of 1 pixel by default + setLineWidth( 1 ); + + // Lines are solid by default + setLineStyle( Qt::SolidLine ); + + // Lines have the same color as their + // respective data points by default + setLineColor(); + + + // Default line marker styles and size + _maxDatasetLineMarkerStyle = 0; // avoid UMR + setLineMarkerStyle( 0, LineMarkerCircle ); + setLineMarkerStyle( 1, LineMarkerSquare ); + setLineMarkerStyle( 2, LineMarkerDiamond ); + setLineMarkerSize( QSize( 6, 6 ) ); + + // 3D line settings + setThreeDLines( false ); + setThreeDLineDepth( 10 ); + /* temporary disabled: + setThreeDLineXRotation( 30 ); + setThreeDLineYRotation( 30 ); + */ + setThreeDLineXRotation( 15 ); + setThreeDLineYRotation( 15 ); + + // Normal areas by default + setAreaChartSubType( AreaNormal ); + + // Areas are filled below the value points by default. + setAreaLocation( AreaBelow ); + + // END LINE/AREA CHART-SPECIFIC + + // POLAR CHART-SPECIFIC + // Normal polar charts by default + setPolarChartSubType( PolarNormal ); + + // Have markers by default + setPolarMarker( true ); + + // Polar charts show the zero point at the right side of the circle + setPolarZeroDegreePos( 0 ); + + // Lines have a width of 3/1000 of the chart's min size + // (either width or height) by default + setPolarLineWidth( -3 ); + + // Default polar marker styles and size + _maxDatasetPolarMarkerStyle = 0; // avoid UMR + setPolarMarkerStyle( 0, PolarMarkerCircle ); + setPolarMarkerStyle( 1, PolarMarkerSquare ); + setPolarMarkerStyle( 2, PolarMarkerDiamond ); + setPolarMarkerSize( QSize( -40,-40 ) ); + + // default circular axes delimiters + setPolarRotateCircularLabels( false ); + setPolarDelimsAndLabelsAtPos( KDChartEnums::PosTopLeft, false, false ); + setPolarDelimsAndLabelsAtPos( KDChartEnums::PosTopCenter, true, true ); + setPolarDelimsAndLabelsAtPos( KDChartEnums::PosTopRight, false, false ); + + setPolarDelimsAndLabelsAtPos( KDChartEnums::PosCenterLeft, false, false ); + setPolarDelimsAndLabelsAtPos( KDChartEnums::PosCenterRight, false, false ); + + setPolarDelimsAndLabelsAtPos( KDChartEnums::PosBottomLeft, false, false ); + setPolarDelimsAndLabelsAtPos( KDChartEnums::PosBottomCenter, true, true ); + setPolarDelimsAndLabelsAtPos( KDChartEnums::PosBottomRight, false, false ); + + + /* for test: + setPolarDelimsAndLabelsAtPos( KDChartEnums::PosTopLeft, true, true ); + setPolarDelimsAndLabelsAtPos( KDChartEnums::PosTopCenter, true, true ); + setPolarDelimsAndLabelsAtPos( KDChartEnums::PosTopRight, true, true ); + setPolarDelimsAndLabelsAtPos( KDChartEnums::PosCenterRight, true, true ); + setPolarDelimsAndLabelsAtPos( KDChartEnums::PosBottomRight, true, true ); + setPolarDelimsAndLabelsAtPos( KDChartEnums::PosBottomCenter, true, true ); + setPolarDelimsAndLabelsAtPos( KDChartEnums::PosBottomLeft, true, true ); + setPolarDelimsAndLabelsAtPos( KDChartEnums::PosCenterLeft, true, true ); + */ + + + // END POLAR CHART-SPECIFIC + + // PIE/RING CHART-SPECIFIC + // Pie/ring charts do not explode by default. + setExplode( false ); + // But if they do, the explode factor is 10% + setExplodeFactor( 0.1 ); + // setExplodeValues() is not called for initialization, the default + // empty list is the default. + // Flat pies by default + setThreeDPies( false ); + // Height of 3D effect + setThreeDPieHeight( 20 ); + // Start of pie circle + setPieStart( 0 ); + // Start of ring circle + setRingStart( 0 ); + // Ring thickness is constant by default + setRelativeRingThickness( false ); + // END PIE/RING CHART-SPECIFIC + + + // HILO CHART-SPECIFIC + // Simple HiLo charts by default + setHiLoChartSubType( KDChartParams::HiLoSimple ); + setHiLoChartPrintLowValues( false ); + setHiLoChartPrintHighValues( false ); + setHiLoChartPrintOpenValues( false ); + setHiLoChartPrintCloseValues( false ); + + + // BOX+WHISKER CHART-SPECIFIC + // Simple Box-and-Whisker charts by default + setBWChartSubType( KDChartParams::BWSimple ); + setBWChartFences(1.5, 1.5, 3.0, 3.0); + setBWChartOutValMarkerSize( -25 ); + setBWChartPrintStatistics( BWStatValALL, false ); + setBWChartBrush( Qt::white ); + + // LEGEND + // Distance between legend and data. + setLegendSpacing( 20 ); + // Position of the legend + setLegendPosition( LegendRight ); + // Orientation of the legend + setLegendOrientation( Qt::Vertical ); + // Whether the legend shall show lines or just + // show the markers (or squares, resp.) + setLegendShowLines( false ); + // Where the legend labels come from + setLegendSource( LegendAutomatic ); + // legend texts are drawn in black by default + setLegendTextColor( Qt::black ); + // legend font size is calculated dynamically, but ignore the font size + setLegendFont( QFont( "helvetica", 10, QFont::Normal, false ), false ); + // legend font size is calculated dynamically: + // 40 / 1000 of the minimal value of + // the printable area height and width + setLegendFontRelSize( 40 ); + // the default legend title is "Legend" + setLegendTitleText( tr( "Legend" ) ); + // legend title is drawn in black by default + setLegendTitleTextColor( Qt::black ); + // legend title font size is calculated dynamically, but ignore + // the font size + setLegendTitleFont( QFont( "helvetica", 12, QFont::Normal, false ), false ); + // legend title font size is calculated dynamically: + // 50 / 1000 of the minimal value of + // the printable area height and width + setLegendTitleFontRelSize( 50 ); + // END LEGEND + + + // AXES + + setDefaultAxesTypes(); + // activate bottom (ordinate) and left (abcissa) axis + activateDefaultAxes(); + + // END AXES + + + // HEADERS/FOOTERS + + // Set a default font for all sections not taking the build-in + // defaults from c'tor KDChartParams::HdFtParams::HdFtParams() + QFont defaultHdFtFont( "helvetica", 14, QFont::Normal, false ); + int relHd0Size = 15; + int relHdSize = 22; + int relHd2Size = 19; + + int relFt0Size = 15; + int relFtSize = 19; + int relFt2Size = 12; + setHeaderFooterFont( KDChartParams::HdFtPosHeader0, + defaultHdFtFont, true, relHd0Size ); + setHeaderFooterFont( KDChartParams::HdFtPosHeader0L, + defaultHdFtFont, true, relHd0Size ); + setHeaderFooterFont( KDChartParams::HdFtPosHeader0R, + defaultHdFtFont, true, relHd0Size ); + setHeaderFooterFont( KDChartParams::HdFtPosHeader, + defaultHdFtFont, true, relHdSize ); + setHeaderFooterFont( KDChartParams::HdFtPosHeaderL, + defaultHdFtFont, true, relHdSize ); + setHeaderFooterFont( KDChartParams::HdFtPosHeaderR, + defaultHdFtFont, true, relHdSize ); + setHeaderFooterFont( KDChartParams::HdFtPosHeader2, + defaultHdFtFont, true, relHd2Size ); + setHeaderFooterFont( KDChartParams::HdFtPosHeader2L, + defaultHdFtFont, true, relHd2Size ); + setHeaderFooterFont( KDChartParams::HdFtPosHeader2R, + defaultHdFtFont, true, relHd2Size ); + + setHeaderFooterFont( KDChartParams::HdFtPosFooter0, + defaultHdFtFont, true, relFt0Size ); + setHeaderFooterFont( KDChartParams::HdFtPosFooter0L, + defaultHdFtFont, true, relFt0Size ); + setHeaderFooterFont( KDChartParams::HdFtPosFooter0R, + defaultHdFtFont, true, relFt0Size ); + setHeaderFooterFont( KDChartParams::HdFtPosFooter, + defaultHdFtFont, true, relFtSize ); + setHeaderFooterFont( KDChartParams::HdFtPosFooterL, + defaultHdFtFont, true, relFtSize ); + setHeaderFooterFont( KDChartParams::HdFtPosFooterR, + defaultHdFtFont, true, relFtSize ); + setHeaderFooterFont( KDChartParams::HdFtPosFooter2, + defaultHdFtFont, true, relFt2Size ); + setHeaderFooterFont( KDChartParams::HdFtPosFooter2L, + defaultHdFtFont, true, relFt2Size ); + setHeaderFooterFont( KDChartParams::HdFtPosFooter2R, + defaultHdFtFont, true, relFt2Size ); + // END HEADERS/FOOTERS + + // PROPERTY SETS + tempPropSetA->fullReset("normal data"); + setProperties(KDCHART_PROPSET_NORMAL_DATA, *tempPropSetA); + // don't show the line, don't show the marker + tempPropSetA->setName("transparent data"); + tempPropSetA->setLineStyle( KDChartPropertySet::OwnID, Qt::NoPen ); + tempPropSetA->setShowMarker( KDChartPropertySet::OwnID, false ); + setProperties(KDCHART_PROPSET_TRANSPARENT_DATA, *tempPropSetA); + // don't show line nor marker, but do show the horizontal line + tempPropSetA->setName("horizontal line"); + tempPropSetA->setExtraLinesAlign( KDChartPropertySet::OwnID, Qt::AlignLeft | Qt::AlignRight ); + setProperties(KDCHART_PROPSET_HORI_LINE, *tempPropSetA); + // don't show line nor marker, but do show the vertical line + tempPropSetA->setName("vertical line"); + tempPropSetA->setExtraLinesAlign( KDChartPropertySet::OwnID, Qt::AlignTop | Qt::AlignBottom ); + setProperties(KDCHART_PROPSET_VERT_LINE, *tempPropSetA); + // END PROPERTY SETS +} + + +/** + Destructor. Only defined to have it virtual. + */ +KDChartParams::~KDChartParams() +{ + KDChartAutoColor::freeInstance(); + delete tempPropSetA; + delete tempPropSetB; +} + + +// GENERAL +/** @name General parameters. + + These methods set general parameters that apply to several or all chart types. + */ +//@{ + + +/** + Stores a new property set: data are stored by value so you may + use your \c rSet instance for other purpose later... + + \returns The property set's ID to be used for later retrieving + the property information by calling the properties() function + or for assigning these properties to a data cell - either in the + KDChartData constructor or by calling KDChartData::setPropertySet(). + + \note The property set's ID may also be retrieved by calling + <b>set.id()</b> after calling registerProperties( set ) + + \note The predefined (build-in) property ids (like KDCHART_PROPSET_NORMAL_DATA + and KDCHART_PROPSET_TRANSPARENT_DATA) must not be registered + but should be used without further initialization. + + \sa KDCHART_PROPSET_NORMAL_DATA, KDCHART_PROPSET_TRANSPARENT_DATA + \sa KDCHART_PROPSET_HORI_LINE, KDCHART_PROPSET_VERT_LINE + \sa KDChartData::setPropertySet + \sa removeProperties + */ +int KDChartParams::registerProperties( KDChartPropertySet& rSet ) +{ + _propertySetList.setAutoDelete( true ); + rSet.mOwnID = _propertySetList.count(); + _propertySetList.insert( rSet.mOwnID, rSet.clone() ); + return rSet.mOwnID; +} + +/** + Overwrites a property set with a new property set. + + \note This function might also be used to initialy store + a property set using a specific ID number, but note + that another property set having the same number + would be replaced by this property set automatically. Therefor + in general it is better to use the registerProperties function + to initially obtain a unique ID number for your new property set. + + \returns TRUE if the property set had been stored before, + or FALSE if the set was now stored initially. + + \sa KDCHART_PROPSET_TRANSPARENT_DATA + \sa KDCHART_PROPSET_HORI_LINE, KDCHART_PROPSET_VERT_LINE + \sa KDChartData::setPropertySet + \sa removeProperties + */ +void KDChartParams::setProperties( int id, KDChartPropertySet& rSet ) +{ + _propertySetList.setAutoDelete( true ); + rSet.mOwnID = id; + _propertySetList.replace( rSet.mOwnID, rSet.clone() ); +} + +/** + Removes a property set from the registry + that was registered via registerProperties(). + + \note It is not possible to remove the build-in default property set: + function calls using KDCHART_PROPSET_NORMAL_DATA as ID + will be ignored. + + \returns TRUE if the property set was found and removed, or FALSE + if the set was not found or KDCHART_PROPSET_NORMAL_DATA + was specified as ID value. + + \sa registerProperties, setProperties + */ +bool KDChartParams::removeProperties( int id ) +{ + _propertySetList.setAutoDelete( true ); + // Removing the default property set is not allowed! + if( KDCHART_PROPSET_NORMAL_DATA == id ) + return false; + return _propertySetList.remove( id ); +} + + +/** + Retrieves the values specified for a property set that + was stored by registerProperties(). + + Use this function to retrieve the exact specification + of a property set. + + Don't use this function to retrieve the properties that are + valid for a given data cell but use KDChartParams::calculateProperties() + instead. + + \note This function does <b>not</b> return a pointer + to the property set itself but a copy of the data + stored therein. To change a stored property set + you may use the setProperties() function. + + \returns TRUE if the property set was found, FALSE + if it no property set was registred with this ID. + + \sa registerProperties, KDChartData::setPropertySet + */ +bool KDChartParams::properties( int id, KDChartPropertySet& rSet ) const +{ + const KDChartPropertySet* R = _propertySetList.find( id ); + const bool bFound = (0 != R); + if( bFound ) + rSet.deepCopy( R ); + return bFound; +} + + +/** + Retrieves the internal property set object that + created when you called registerProperties(). + + Use this function to access a property set for modifying it directly. + + Don't use this function to retrieve the properties that are + valid for a given data cell but use KDChartParams::calculateProperties() + instead. + + \note This function <b>does</b> return a pointer + to the property set itself, so you can directly modify its contents. + <b>However</b> you have to make sure NOT to delete this pointer, + since KDChartParams is owning it, so it would try to free + the pointer again later, most likely resulting in a program crash. + To delete a stored property set you may use the removeProperties() function. + + \returns A pointer to the internally stored property set if the property set was found, + Zero if no property set was registred with this ID. + + \sa registerProperties, removeProperties, KDChartData::setPropertySet + */ +KDChartPropertySet* KDChartParams::properties( int id ) +{ + return _propertySetList.find( id ); +} + + +/** + Retrieves the values that correspond to a property set by + reading this set's properties and obtaining other property + set's values if necessary (according to ID numbers that might + be stored in the property set). + + Use this function to retrieve the properties that are + valid for a given data cell. + + Don't use this function to retrieve the exact specification + of a property set but use KDChartParams::properties() instead. + + \returns TRUE if the property set was found, FALSE + if it no property set was registred with this ID. + + \sa registerProperties, KDChartData::setPropertySet + */ +bool KDChartParams::calculateProperties( int startId, KDChartPropertySet& rSet ) const +{ + KDChartPropertySet& startSet = *tempPropSetA; + startSet.quickReset(""); + + rSet.deepCopy( &startSet ); // reset all properties of rSet to the default !! + +//qDebug("in KDChartParams::calculateProperties():"); +//qDebug(" startId: %i",startId); +//qDebug(" startSet: %s",startSet.name().latin1()); + + bool bOk = properties(startId, startSet); + if( bOk ){ + int lineWidth; + QColor lineColor; + Qt::PenStyle lineStyle; + bool showMarker; + uint markerAlign; + QSize markerSize; + QColor markerColor; + int markerStyle; + uint extraLinesAlign; + bool extraLinesInFront; + int extraLinesLength; + int extraLinesWidth; + QColor extraLinesColor; + Qt::PenStyle extraLinesStyle; + uint extraMarkersAlign; + QSize extraMarkersSize; + QColor extraMarkersColor; + int extraMarkersStyle; + bool showBar; + QColor barColor; + QBrush areaBrush; + // c'tor sets all IDs to unknown by default + KDChartPropertySet& propSet = *tempPropSetB; + propSet.quickReset(""); + // PENDING(khz) replace the rustic depth counter i by a smart way + // to quickly and safely recognize forbidden circular ID dependencies + // *without* using this artificial maximal tree depth limitation. + const int maxDepth = 1000; + int i; + int id; + // retrieve lineWidth + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnLineWidth( id, lineWidth ) ){ + rSet.setLineWidth( KDChartPropertySet::OwnID, lineWidth ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + // retrieve lineColor + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnLineColor( id, lineColor ) ){ + rSet.setLineColor( KDChartPropertySet::OwnID, lineColor ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + // retrieve lineStyle + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnLineStyle( id, lineStyle ) ){ + rSet.setLineStyle( KDChartPropertySet::OwnID, lineStyle ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + + // markers at cell value position: + + // retrieve showMarker + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnShowMarker( id, showMarker ) ){ + rSet.setShowMarker( KDChartPropertySet::OwnID, showMarker ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + // retrieve marker alignment + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnMarkerAlign( id, markerAlign ) ){ + rSet.setMarkerAlign( KDChartPropertySet::OwnID, markerAlign ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + // retrieve marker size + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnMarkerSize( id, markerSize ) ){ + rSet.setMarkerSize( KDChartPropertySet::OwnID, markerSize ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + // retrieve marker color + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnMarkerColor( id, markerColor ) ){ + rSet.setMarkerColor( KDChartPropertySet::OwnID, markerColor ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + // retrieve marker style + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnMarkerStyle( id, markerStyle ) ){ + rSet.setMarkerStyle( KDChartPropertySet::OwnID, markerStyle ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + + // extra lines: + + // retrieve alignment of extra lines + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnExtraLinesAlign( id, extraLinesAlign ) ){ + rSet.setExtraLinesAlign( KDChartPropertySet::OwnID, extraLinesAlign ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + // retrieve whether the extra lines shall be printed in front of the normal lines + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnExtraLinesInFront( id, extraLinesInFront ) ){ + rSet.setExtraLinesInFront( KDChartPropertySet::OwnID, extraLinesInFront ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + // retrieve lineLength + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnExtraLinesLength( id, extraLinesLength ) ){ + rSet.setExtraLinesLength( KDChartPropertySet::OwnID, extraLinesLength ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + // retrieve lineWidth + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnExtraLinesWidth( id, extraLinesWidth ) ){ + rSet.setExtraLinesWidth( KDChartPropertySet::OwnID, extraLinesWidth ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + // retrieve lineColor + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnExtraLinesColor( id, extraLinesColor ) ){ + rSet.setExtraLinesColor( KDChartPropertySet::OwnID, extraLinesColor ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + // retrieve lineStyle + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnExtraLinesStyle( id, extraLinesStyle ) ){ + rSet.setExtraLinesStyle( KDChartPropertySet::OwnID, extraLinesStyle ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + + // markers at the ends of the extra lines: + + // retrieve marker alignment + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnExtraMarkersAlign( id, extraMarkersAlign ) ){ + rSet.setExtraMarkersAlign( KDChartPropertySet::OwnID, extraMarkersAlign ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + // retrieve marker size + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnExtraMarkersSize( id, extraMarkersSize ) ){ + rSet.setExtraMarkersSize( KDChartPropertySet::OwnID, extraMarkersSize ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + // retrieve marker color + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnExtraMarkersColor( id, extraMarkersColor ) ){ + rSet.setExtraMarkersColor( KDChartPropertySet::OwnID, extraMarkersColor ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + // retrieve marker style + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnExtraMarkersStyle( id, extraMarkersStyle ) ){ + rSet.setExtraMarkersStyle( KDChartPropertySet::OwnID, extraMarkersStyle ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + + // retrieve showBar + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnShowBar( id, showBar ) ){ + rSet.setShowBar( KDChartPropertySet::OwnID, showBar ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + // retrieve barColor + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnBarColor( id, barColor ) ){ + rSet.setBarColor( KDChartPropertySet::OwnID, barColor ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + + // retrieve areaBrush + propSet.deepCopy( &startSet ); i=0; + do{ + if( propSet.hasOwnAreaBrush( id, areaBrush ) ){ + rSet.setAreaBrush( KDChartPropertySet::OwnID, areaBrush ); + break; + }else if( KDChartPropertySet::UndefinedID == id || maxDepth < i ) + break; + ++i; + }while( properties(id, propSet) ); + + } + return bOk; +} + + +/** + \fn int KDChartParams::roundVal(double) + + Returns the parameter \c d rounded to the nearest integer. + */ + + + +/** + Specifies if and how a chart will print the data value texts near + their respective entries. + + Data value texts will be printed immediately after drawing all of + the chart data points (or bars, lines,...) but before drawing the + legend and before drawing any custom boxes. If more than one chart + ist to be drawn (sharing the same data area) printing of the data + text values will take place after drawing all of the last charts + data points, this enables us to see the texts even if their + respective data representations are covered by the second charts + drawings. The same covering/colliding problem might occur with Area + charts if one area is (partially) covering another area. In such + cases you might either want to specify an appropriate + TextLayoutPolicy for getting a better looking result or specify an + other text color for data value texts of the second chart since by + default the first chart has black texts while the second chart + shows its data value texts in dark blue color. + + \note Only used if chartType() is <b>neither HiLo nor + BoxWhisker</b>. To specify printing of data values in a HiLo chart + please use setHiLoChartPrintLowValues, setHiLoChartPrintHighValues, + setHiLoChartPrintOpenValues, setHiLoChartPrintCloseValues. To + specify printing of data values in a BoxWhisker chart please use + setBWChartPrintStatistics. + + Calling <b>setPrintDataValues( false )</b> will <b>deactivate</b> + printing of the values. + + Calling setPrintDataValuesWithDefaultFontParams( chart ) will + <b>reset</b> the respective font size and colour and position + parameters (but not the QFont itself) and <b>activate</b> printing + of the values for the \c chart speficied (or for all charts by + using \c KDCHART_ALL_CHARTS, resp.). + + \param active specifies whether the value texts are to be printed or not. + \param chart The number of the chart: 0 for the first chart, 1 for + the second chart in case there are two charts to be drawn sharing the + same data area. Use the special value KDCHART_ALL_CHARTS + to specify that your settings are to be taken for both charts. + \param divPow10 The power of 10 which the data value is to be divided by. + A value of 2 means divide by 100, a value of -3 means multiply by 1000, + and 0 (by definition) would result in multiplying by 1. + \param digitsBehindComma The number of digits to show behind the comma, + to have this calculated automatically just use the default value + KDCHART_DATA_VALUE_AUTO_DIGITS. + \param font a Pointer to the font to be used, if zero the default data value + texts font will be taken (this is a Times font since small Times digits are + clearer than small Helvetica digits). + + Changing of one or more of <b>the following parameters</b> + automatically de-activates all future font parameter adjustments + that would otherwise take place after each call of setChartType (or + setAdditionalChartType, resp.). To re-enable this usefull feature + you may call setPrintDataValuesWithDefaultFontParams at any time + afterwards. + + \param size (in per mille of the chart width) the dynamic size of + the font to be used. If this parameter is zero the size of the + \c font is used instead - regardless of the size of the chart! + You may use setPrintDataValuesFontRelSize to change this parameter setting + without affecting the other ones. + \param color the color to be used when printing the values. + To have the color calculated automatically - useful when printing + inside the bars (or pie slices, areas, ... resp.) - please use + \c KDCHART_DATA_VALUE_AUTO_COLOR instead of a QColor*. + You may use setPrintDataValuesColor to change this parameter setting + without affecting the other ones. + + The following parameters apply to values less than zero only: + + \param negativePosition The anchor position which the text is to be +aligned to. +\param negativeAlign The way how the text is to be aligned to the anchor. +This must be a reasonable combination of Qt::AlignmentFlags. +\param negativeDeltaX The X distance between the <b>anchor +point</b> -- specified by \c negativePosition (or \c + positivePosition, resp.) -- and the internal <b>alignment point</b> +of the text -- specified by \c negativeAlign (or \c positiveAlign, + resp.). <b>Note: </b> For better compatibility to the dynamic font +size this parameter is interpreted as being a per-cent value of the +used font height. If greater 0, the X position is increased, if +less than 0, it is reduced. Actual font size and thus also this +delta value are calculated dynamically before painting based on the +size of the chart and the specification made via parameter \c size. + +\param negativeDeltaY The Y distance between the <b>anchor +point</b> -- specified by \c negativePosition (or \c + positivePosition, resp.) -- and the internal <b>alignment point</b> +of the text -- specified by \c negativeAlign (or \c positiveAlign, + resp.). <b>Note: </b> For better compatibility to the dynamic font +size this parameter is interpreted as being a per-cent value of the +used font height. If greater 0, the Y position is increased, if +less than 0, it is reduced. Actual font size and thus also this +delta value are calculated dynamically before painting based on the +size of the chart and the specification made via parameter \c size. +\param negativeRotation The amount of degrees (using a circle of + 360 degrees) taken to rotate the text. Positive values rotate +clockwise, negative values rotate counter-clockwise. There are two +special values that you might find usefull for Pie charts or for +Ring charts: \c KDCHART_SAGGITAL_ROTATION and \c +KDCHART_TANGENTIAL_ROTATION both leading to individual +calculation of appropriate rotation for each data value. Rotation +will be performed around the internal <b>alignment point</b> of the +text -- specified by \c negativeAlign (or \c positiveAlign, resp.). + +The following parameters apply to values greater than zero or equal zero: + +\param positivePosition The anchor position which the text is to be +aligned to. +\param positiveAlign The way how the text is to be aligned to the anchor. +This must be a reasonable combination of Qt::AlignmentFlags. +\param negativeDeltaX The X distance between the <b>anchor +point</b> -- specified by \c negativePosition (or \c + positivePosition, resp.) -- and the internal <b>alignment point</b> +of the text -- specified by \c negativeAlign (or \c positiveAlign, + resp.). <b>Note: </b> For better compatibility to the dynamic font +size this parameter is interpreted as being a per-cent value of the +used font height. If greater 0, the X position is increased, if +less than 0, it is reduced. Actual font size and thus also this +delta value are calculated dynamically before painting based on the +size of the chart and the specification made via parameter \c size. +\param positiveDeltaY The Y distance between the <b>anchor +point</b> -- specified by \c negativePosition (or \c + positivePosition, resp.) -- and the internal <b>alignment point</b> +of the text -- specified by \c negativeAlign (or \c positiveAlign, + resp.). <b>Note: </b> For better compatibility to the dynamic font +size this parameter is interpreted as being a per-cent value of the +used font height. If greater 0, the Y position is increased, if +less than 0, it is reduced. Actual font size and thus also this +delta value are calculated dynamically before painting based on the +size of the chart and the specification made via parameter \c size. +\param positiveRotation The amount of degrees (using a circle of + 360 degrees) taken to rotate the text. Positive values rotate +clockwise, negative values rotate counter-clockwise. There are two +special values that you might find usefull for Pie charts or for +Ring charts: \c KDCHART_SAGGITAL_ROTATION and \c +KDCHART_TANGENTIAL_ROTATION both leading to individual +calculation of appropriate rotation for each data value. Rotation +will be performed around the internal <b>alignment point</b> of the +text -- specified by \c negativeAlign (or \c positiveAlign, resp.). + +\param layoutPolicy The way to handle too narrow space conflicts: +what to do if a data text covers a neighboring data text (or a + neighboring data area, resp.). + +\sa printDataValues +\sa setPrintDataValuesWithDefaultFontParams, printDataValuesWithDefaultFontParams +\sa setPrintDataValuesFontRelSize, setPrintDataValuesColor +\sa dataValuesDivPow10 +\sa dataValuesDigitsBehindComma +\sa dataValuesFontUseRelSize +\sa dataValuesFontRelSize +\sa dataValuesFontColor +\sa dataValuesAnchorPosition +\sa dataValuesAnchorAlign +\sa dataValuesAnchorDeltaX +\sa dataValuesAnchorDeltaY +\sa dataValuesRotation +\sa dataValuesLayoutPolicy +*/ +void KDChartParams::setPrintDataValues( bool active, + uint chart, + int divPow10, + int digitsBehindComma, + QFont* font, + uint size, + const QColor* color, + KDChartEnums::PositionFlag negativePosition, + uint negativeAlign, + int negativeDeltaX, + int negativeDeltaY, + int negativeRotation, + KDChartEnums::PositionFlag positivePosition, + uint positiveAlign, + int positiveDeltaX, + int positiveDeltaY, + int positiveRotation, + KDChartEnums::TextLayoutPolicy policy ) +{ + uint count = (KDCHART_ALL_CHARTS == chart) ? 2 : 1; + PrintDataValuesSettings * settings = (( 1 < count ) || ( 0 == chart )) + ? &_printDataValuesSettings + : &_printDataValuesSettings2; + for ( uint i = 0; i < count; ++i ) { + settings->_printDataValues = active; + settings->_divPow10 = divPow10; + settings->_digitsBehindComma = digitsBehindComma; + + const ChartType cType + = ( ( 1 < count && i ) + || ( 1 == count && 0 < chart && chart < 1000 ) ) + ? additionalChartType() + : chartType(); + + bool finished( false ); + if ( UINT_MAX == size ) { + finished = true; + switch ( cType ) { + case NoType: + case Bar: { + if ( font ) + settings->_dataValuesFont = *font; + else + settings->_dataValuesFont = QFont( "times", 1, QFont::Bold ); + settings->_dataValuesUseFontRelSize = true; + settings->_dataValuesFontRelSize = 16; + settings->_dataValuesAutoColor = false; // !!! + settings->_dataValuesColor = QColor( Qt::darkBlue ); + settings->_dataValuesBrush = QBrush( Qt::NoBrush ); + // for values below zero: + settings->_dataValuesAnchorNegativePosition = KDChartEnums::PosBottomRight; + settings->_dataValuesAnchorNegativeAlign = Qt::AlignBottom + Qt::AlignRight; + settings->_dataValuesAnchorNegativeDeltaX = 20; + settings->_dataValuesAnchorNegativeDeltaY = 55; + settings->_dataValuesNegativeRotation = 300; + // for values greater/equal zero: + settings->_dataValuesAnchorPositivePosition = KDChartEnums::PosTopLeft; + settings->_dataValuesAnchorPositiveAlign = Qt::AlignTop + Qt::AlignLeft; + settings->_dataValuesAnchorPositiveDeltaX = - 20; + settings->_dataValuesAnchorPositiveDeltaY = - 65; + settings->_dataValuesPositiveRotation = 300; + + settings->_dataValuesLayoutPolicy = KDChartEnums::LayoutPolicyRotate; + } + break; + case Line: { + if ( font ) + settings->_dataValuesFont = *font; + else + settings->_dataValuesFont = QFont( "times", 1, QFont::Normal ); + settings->_dataValuesUseFontRelSize = true; + settings->_dataValuesFontRelSize = 16; + settings->_dataValuesAutoColor = false; // !!! + settings->_dataValuesColor = QColor( Qt::darkBlue ); + settings->_dataValuesBrush = QBrush( Qt::NoBrush ); + // for values below zero: + settings->_dataValuesAnchorNegativePosition = KDChartEnums::PosBottomCenter; + settings->_dataValuesAnchorNegativeAlign = Qt::AlignTop + Qt::AlignHCenter; + settings->_dataValuesAnchorNegativeDeltaX = 0; + settings->_dataValuesAnchorNegativeDeltaY = 0; + settings->_dataValuesNegativeRotation = 0; + // for values greater/equal zero: + settings->_dataValuesAnchorPositivePosition = KDChartEnums::PosTopCenter; + settings->_dataValuesAnchorPositiveAlign = Qt::AlignBottom + Qt::AlignHCenter; + settings->_dataValuesAnchorPositiveDeltaX = 0; + settings->_dataValuesAnchorPositiveDeltaY = 0; + settings->_dataValuesPositiveRotation = 0; + + settings->_dataValuesLayoutPolicy = KDChartEnums::LayoutPolicyRotate; + } + break; + case Area: { + if ( font ) + settings->_dataValuesFont = *font; + else + settings->_dataValuesFont = QFont( "times", 1, QFont::Bold ); + settings->_dataValuesUseFontRelSize = true; + settings->_dataValuesFontRelSize = 21; + settings->_dataValuesAutoColor = true; // !!! + settings->_dataValuesColor = QColor( Qt::black ); + settings->_dataValuesBrush = QBrush( Qt::white ); + + bool bShowOutside = areaChartSubType() == AreaNormal; + // for values below zero: + settings->_dataValuesAnchorNegativePosition = KDChartEnums::PosBottomCenter; + settings->_dataValuesAnchorNegativeAlign = Qt::AlignHCenter + + (bShowOutside ? Qt::AlignTop : Qt::AlignBottom); + settings->_dataValuesAnchorNegativeDeltaX = 0; + settings->_dataValuesAnchorNegativeDeltaY = bShowOutside ? 20 : -35; + settings->_dataValuesNegativeRotation = 0; + // for values greater/equal zero: + settings->_dataValuesAnchorPositivePosition = KDChartEnums::PosTopCenter; + settings->_dataValuesAnchorPositiveAlign = Qt::AlignHCenter + + (bShowOutside ? Qt::AlignBottom : Qt::AlignTop); + settings->_dataValuesAnchorPositiveDeltaX = 0; + settings->_dataValuesAnchorPositiveDeltaY = bShowOutside ? -20 : 35; + settings->_dataValuesPositiveRotation = 0; + + settings->_dataValuesLayoutPolicy = KDChartEnums::LayoutPolicyRotate; + } + break; + case HiLo: + case BoxWhisker: + // settings are not defined here because HiLo and BW charts + // are *not* set up using setPrintDataValues() + // but by using their own methods + break; + case Pie: { + if ( font ) + settings->_dataValuesFont = *font; + else + settings->_dataValuesFont = QFont( "times", 1, QFont::Bold ); + settings->_dataValuesUseFontRelSize = true; + settings->_dataValuesFontRelSize = 25; + settings->_dataValuesAutoColor = true; // !!! + settings->_dataValuesColor = QColor( Qt::black ); + settings->_dataValuesBrush = QBrush( Qt::NoBrush ); + // for values below zero: + settings->_dataValuesAnchorNegativePosition = KDChartEnums::PosTopCenter; + settings->_dataValuesAnchorNegativeAlign = Qt::AlignTop + Qt::AlignHCenter; + settings->_dataValuesAnchorNegativeDeltaX = 0; + settings->_dataValuesAnchorNegativeDeltaY = 50; + settings->_dataValuesNegativeRotation = KDCHART_TANGENTIAL_ROTATION; + // for values greater/equal zero: + settings->_dataValuesAnchorPositivePosition = KDChartEnums::PosTopCenter; + settings->_dataValuesAnchorPositiveAlign = Qt::AlignTop + Qt::AlignHCenter; + settings->_dataValuesAnchorPositiveDeltaX = 0; + settings->_dataValuesAnchorPositiveDeltaY = 50; + settings->_dataValuesPositiveRotation = KDCHART_TANGENTIAL_ROTATION; + + settings->_dataValuesLayoutPolicy = KDChartEnums::LayoutPolicyRotate; + } + break; + case Ring: { + if ( font ) + settings->_dataValuesFont = *font; + else + settings->_dataValuesFont = QFont( "times", 1, QFont::Bold ); + settings->_dataValuesUseFontRelSize = true; + settings->_dataValuesFontRelSize = 25; + settings->_dataValuesAutoColor = true; // !!! + settings->_dataValuesColor = QColor( Qt::black ); + settings->_dataValuesBrush = QBrush( Qt::NoBrush ); + // for values below zero: + settings->_dataValuesAnchorNegativePosition = KDChartEnums::PosCenter; + settings->_dataValuesAnchorNegativeAlign = Qt::AlignCenter; + settings->_dataValuesAnchorNegativeDeltaX = 0; + settings->_dataValuesAnchorNegativeDeltaY = 10; + settings->_dataValuesNegativeRotation = KDCHART_TANGENTIAL_ROTATION; + // for values greater/equal zero: + settings->_dataValuesAnchorPositivePosition = KDChartEnums::PosCenter; + settings->_dataValuesAnchorPositiveAlign = Qt::AlignCenter; + settings->_dataValuesAnchorPositiveDeltaX = 0; + settings->_dataValuesAnchorPositiveDeltaY = 10; + settings->_dataValuesPositiveRotation = KDCHART_TANGENTIAL_ROTATION; + + settings->_dataValuesLayoutPolicy = KDChartEnums::LayoutPolicyRotate; + } + break; + + case Polar: { + settings->_dataValuesFontRelSize = 18; + if ( font ) + settings->_dataValuesFont = *font; + else + settings->_dataValuesFont = QFont( "times", 1, QFont::Bold ); + settings->_dataValuesUseFontRelSize = true; + settings->_dataValuesFontRelSize = 26; + settings->_dataValuesAutoColor = polarMarker(); // !!! + settings->_dataValuesColor = QColor( Qt::black ); + settings->_dataValuesBrush = QBrush( Qt::NoBrush ); + + // for values below zero: + settings->_dataValuesAnchorNegativePosition = KDChartEnums::PosCenter; + settings->_dataValuesAnchorNegativeAlign = Qt::AlignCenter; + settings->_dataValuesAnchorNegativeDeltaX = 0; + settings->_dataValuesAnchorNegativeDeltaY = 0; + settings->_dataValuesNegativeRotation = 0; + // for values greater/equal zero: + settings->_dataValuesAnchorNegativePosition = KDChartEnums::PosCenter; + settings->_dataValuesAnchorNegativeAlign = Qt::AlignCenter; + settings->_dataValuesAnchorNegativeDeltaX = 0; + settings->_dataValuesAnchorNegativeDeltaY = 0; + settings->_dataValuesNegativeRotation = 0; + + //settings->_dataValuesLayoutPolicy = KDChartEnums::LayoutPolicyShrinkFontSize; + //settings->_dataValuesFontRelSize = 26; + //setDefaultAxesTypes(); + //finished = false; // use build-in default params, see KDChartParams.h::setPrintDataValues() + } + break; + + default: { + qDebug( "IMPLEMENTATION ERROR: Unknown chartType in setPrintDataValues()" ); + finished = false; // use build-in default params, see KDChartParams.h::setPrintDataValues() + } + } + } + if ( !finished ) { + settings->_useDefaultFontParams = false; + + if ( font ) + settings->_dataValuesFont = *font; + else + settings->_dataValuesFont = QFont( "times", 1, QFont::Bold ); + + uint theSize( UINT_MAX == size ? 14 : size ); + settings->_dataValuesUseFontRelSize = ( 0 < theSize ); + settings->_dataValuesFontRelSize = theSize; + if ( KDCHART_DATA_VALUE_AUTO_COLOR == color + && ( Polar != cType || polarMarker() ) ) { + settings->_dataValuesAutoColor = true; // !!! + settings->_dataValuesColor = QColor( Qt::black ); + } + else { + settings->_dataValuesAutoColor = false; + if ( 0 == color ) + settings->_dataValuesColor + = QColor( i ? Qt::darkBlue : Qt::black ); + else + settings->_dataValuesColor = *color; + } + settings->_dataValuesBrush = Qt::NoBrush; + // for values below zero: + settings->_dataValuesAnchorNegativePosition = negativePosition; + settings->_dataValuesAnchorNegativeAlign = negativeAlign; + settings->_dataValuesAnchorNegativeDeltaX = negativeDeltaX; + settings->_dataValuesAnchorNegativeDeltaY = negativeDeltaY; + settings->_dataValuesNegativeRotation = negativeRotation; + // for values greater/equal zero: + settings->_dataValuesAnchorPositivePosition = positivePosition; + settings->_dataValuesAnchorPositiveAlign = positiveAlign; + settings->_dataValuesAnchorPositiveDeltaX = positiveDeltaX; + settings->_dataValuesAnchorPositiveDeltaY = positiveDeltaY; + settings->_dataValuesPositiveRotation = positiveRotation; + + settings->_dataValuesLayoutPolicy = policy; + } + if ( 0 < chart ) + settings = &_printDataValuesSettings2; + } + emit changed(); +} + + +/** + \enum KDChartParams::SourceMode + + The chart source mode. + Use \c setChartSourceMode to specify if and how any data + should be taken into account for a given chart. + + \sa setChartSourceMode, chartSourceMode, setAdditionalChartType + */ + + +/** + Specifies the chart source mode of one or more datasets. + Use this to specify if and how any data should be taken + into account for a given chart. You may call this function + multiple times to specify how the different datasets are to + be used. Number of datasets is only limited by the data that + you actually provide to the chart widget. Usage of this method + is mandatory when specifying combinations of more than one + chart (e.g. via \c setAdditionalChartType() ). + + \note If specifying <b>more than one dataset</b> for the very + same chart and the same source mode make sure they form + a contiguous <b>series</b>.<br> + It is not possible to use dataset 0..3 as DataEntry, dataset + 4 as AxisLabel and again dataset 5..6 as DataEntry for the + very same chart!<br> + Of course you could specify 0..3 as DataEntry for chart 0 + while 5..6 would contain the data entries for chart 1 + when specifying a widget showing two charts. + + <b>Also note:</b><br> + Whenever you use \c setChartSourceMode() make sure not to + forget any of the datasets which you want to define: not + specifying a dataset would result in it being ignored.<br> + So the rule is: either don't call setChartSourceMode() at all + or call it for each dataset! + + To quickly clear all such settings just might want to call + <b>setChartSourceMode( UnknownMode, KDCHART_ALL_DATASETS );</b> + without specifying a dataset number and without specifying + a chart number.<br> + However this KDCHART_ALL_DATASETS may only be used to + <b>clear</b> SourceMode settings - but not for defining the + SourceMode for all datasets - the above mentioned combination + with <b>UnknownMode</b> is the only way how to use + <b>KDCHART_ALL_DATASETS</b> here! + + <b>Examples:</b> + + \verbatim + setChartSourceMode( DataEntry, 2 ); + \endverbatim + This would specify a simple chart obtaining its values from dataset 2. + + \verbatim + setChartSourceMode( DataEntry, 0, 0, 0 ); + setChartSourceMode( DataEntry, 3, 3, 1 ); + \endverbatim + This would specify a two chart widget - the 1st obtaining its values + from dataset 0 and the 2nd taking the values from dataset 3. + + \verbatim + setChartSourceMode( LegendText, 1, 1, 0 ); + setChartSourceMode( AxisLabel, 2, 2, 0 ); + setChartSourceMode( DataEntry, 3, 6, 0 ); + setChartSourceMode( ExtraLinesAnchor, 7, 7, 0 ); + setChartSourceMode( LegendText, 8, 8, 1 ); + setChartSourceMode( AxisLabel, 9, 9, 1 ); + setChartSourceMode( DataEntry, 10, 13, 1 ); + \endverbatim + This would specify a two chart widget - the 1st obtaining its values + from datasets 3..6 and the 2nd taking the values from datasets 10..13. + Their respective legends and axes would take their values from + datasets 1 and 2 (or 8 and 9, resp.). + Additionally the 1st chart would use dataset 7 to determine the + horizontal (or vertical, resp.) position of any extra lines + that might be specified by KDChartPropertySet IDs assigned to + the cells in dataset 7. + + \note If source mode is ExtraLinesAnchor all cells + of the respective datset will be ignored when the normal + cell representations (e.g. Bars or Lines) are drawn - these + cells will <em>only</em> be used as anchor points for any + extra lines that are specified with these cell's property sets. + + When specifying \c ExtraLinesAnchor for more than one dataset + of the same chart make sure they form a contiguous row + <b>together</b> with the \c DataEntry dataset(s): e.g + you may use datasets 3..8 for DataEntry cells and datasets + 9..10 for ExtraLinesAnchor cells. + All datasets with mode DataEntry or ExtraLinesAnchor are taken + into account for calculation of the axes ranges and delimiters. + + <P> + + \param mode the way how to use information stored in this dataset. + \param dataset the dataset number to be used (or ignored, resp.). + Use \c KDCHART_ALL_DATASETS to indicate that information given + applies to all of the datasets. + \param dataset2 a second dataset number to be taken into account + (or ignored, resp.). Use this one together with the \dataset +param to specify a range of datasets. +\param chart the chart taking into account this dataset. +Use this parameter only if more than one chart is to be displayed. +By using KDCHART_ALL_CHARTS here you may specify that the dataset +(or the range of datasets, resp.) is to be taken into account for +all of the charts displayed. + +\sa SourceMode, chartSourceMode, findDataset +\sa maxDatasetSourceMode, setAdditionalChartType +*/ +void KDChartParams::setChartSourceMode( SourceMode mode, + uint dataset, + uint dataset2, + uint chart ) +{ + if ( KDCHART_NO_DATASET != dataset + && KDCHART_ALL_DATASETS != dataset + && KDCHART_ALL_DATASETS != dataset2 ) { + uint i; + uint last = ( KDCHART_NO_DATASET == dataset2 ) ? dataset : dataset2; + for ( i = dataset; i <= last; ++i ) + _dataSourceModeAndChart[ i ] = ModeAndChart( mode, chart ); + _maxDatasetSourceMode = QMAX( _maxDatasetSourceMode, --i ); + _setChartSourceModeWasUsed = true; + } else if ( UnknownMode == mode && dataset == KDCHART_ALL_DATASETS ) { + _dataSourceModeAndChart.clear(); + _setChartSourceModeWasUsed = false; + } + + emit changed(); +} + + +/** + Retrieves usage information of a given dataset or a range of + datasets. + + \note If you specify a range of datasets and not all of them + share the same SourceMode the return value will be \c UnknownMode + whereas in case of the dataset(s) not being used it will be \c DontUse. + Accordingly if you provide a chart pointer and not all the + datasets share the same chart the chart pointer will receive + the value KDCHART_UNKNOWN_CHART.<br> + + <b>Also note:</b> + If \c setChartSourceMode() was not used at all + chartSourceMode() will return <b>DataEntry</b> no matter what + dataset number is specified. + + <p> + + \param dataset the dataset number to be looked at. + Use \c KDCHART_ALL_DATASETS to find out if all of the datasets share + the very same SourceMode. + \param dataset2 a second dataset number. + Use this one together with the \dataset param to look at a range of + datasets and find out if all of them share the very same SourceMode. + \param chart If this parameter points to an int the method will + provide you with the chart number that this dataset (or this range + of datasets, resp.) is taken into account for. + + \sa SourceMode, setChartSourceMode, setAdditionalChartType + \sa maxDatasetSourceMode, findDataset + */ +KDChartParams::SourceMode KDChartParams::chartSourceMode( uint dataset, + uint dataset2, + uint* pChart ) const +{ + uint chart = KDCHART_UNKNOWN_CHART; + SourceMode mode = UnknownMode; + + if ( _setChartSourceModeWasUsed ) { + if ( dataset <= _maxDatasetSourceMode + && ( KDCHART_NO_DATASET == dataset2 + || dataset2 <= _maxDatasetSourceMode ) ) { + + uint a, b; + if ( KDCHART_ALL_DATASETS == dataset ) { + a = 0; + b = UINT_MAX; + } else { + a = dataset; + b = KDCHART_NO_DATASET == dataset2 ? a : dataset2; + } + + bool bStart = true; + ModeAndChartMap::ConstIterator it; + for( it = _dataSourceModeAndChart.find( a ); + ( it != _dataSourceModeAndChart.end() ) && ( it.key() <= b ); + ++it ){ + if ( bStart ) { + mode = it.data().mode(); + chart = it.data().chart(); + bStart = false; + } else { + if ( mode != it.data().mode() ) + mode = UnknownMode; + if ( chart != it.data().chart() ) + chart = KDCHART_UNKNOWN_CHART; + } + } + } + } else { + mode = DataEntry; + chart = 0; + } + if ( pChart ) + * pChart = chart; + return mode; +} + + +/** + Retrieves the dataset number or a range of datasets + being used with a given SourceMode for a given chart + or KDCHART_NO_DATASET if no dataset was specified for + that mode and that chart. + + \note If \c setChartSourceMode() was not used at all + findDataset() will return true and set the dataset + to KDCHART_ALL_DATASETS when called for SourceMode + <b>DataEntry</b>. All other SourceModes will return false then. + + \return True if at least one dataset was found. + + \param mode the SourceMode to search for. + \param dataset the dataset number found (or the first + dataset number in case a series of datasets was found, resp.) + \param dataset2 the dataset number found (or the last + dataset number in case a series of datasets was found, resp.) + <b>Hint:</b> You may give the same variable both for + dataset and for dataset2, e.g. this makes sense in case you + are sure that findDataset will find only one dataset anyway. + \param chart the chart number that the dataset (or this range + of datasets, resp.) must have been defined for. If this is missing + findDataset assumes you are looking for datasets of the first chart. + + \sa findDatasets, SourceMode, setChartSourceMode, chartSourceMode + \sa maxDatasetSourceMode, setAdditionalChartType + */ +bool KDChartParams::findDataset( SourceMode mode, + uint& dataset, + uint& dataset2, + uint chart ) const +{ + bool res = false; + dataset = KDCHART_NO_DATASET; + dataset2 = KDCHART_NO_DATASET; + if ( _setChartSourceModeWasUsed ) { + bool bStart = true; + ModeAndChartMap::ConstIterator it; + for ( it = _dataSourceModeAndChart.begin(); + it != _dataSourceModeAndChart.end(); ++it ) { + if ( ( it.data().mode() == mode ) + && ( ( KDCHART_ALL_CHARTS == chart ) + || ( it.data().chart() == chart ) ) ) { + if ( bStart ) { + dataset = it.key(); + bStart = false; + } + dataset2 = it.key(); + res = true; + } else if ( !bStart ) + return res; + } + } else if ( DataEntry == mode ) { + dataset = KDCHART_ALL_DATASETS; + dataset2 = KDCHART_ALL_DATASETS; + res = true; + } + return res; +} + + +/** + Retrieves the dataset number or a range of datasets + being used with one of two given SourceModes + for a given chart or KDCHART_NO_DATASET + if no dataset was specified for that mode and that chart. + + If \c setChartSourceMode() was not used at all + findDataset() will return true and set the dataset + to KDCHART_ALL_DATASETS when either \c modeA or \c modeB + is <b>DataEntry</b>. All other SourceModes will return + false then. + + \note If the found datasets do <em>not</em> form a + <b>contiguous series</b> the return value will be false + and the value of \c dataset will be KDCHART_NO_DATASET + and a comment will be written to stderr + (if KDChart was compiled in debug mode). + + \return True if at least one dataset was found. + + \param modeA one of the SourceModes to search for. + \param modeB the other one of the two SourceModes to search for. + \param dataset the dataset number found (or the first + dataset number in case a series of datasets was found, resp.) + \param dataset2 the dataset number found (or the last + dataset number in case a series of datasets was found, resp.) + <b>Hint:</b> You may give the same variable both for + dataset and for dataset2, e.g. this makes sense in case you + are sure that findDataset will find only one dataset anyway. + \param chart the chart number that the dataset (or this range + of datasets, resp.) must have been defined for. If this is missing + findDataset assumes you are looking for datasets of the first chart. + + \sa findDataset, SourceMode, setChartSourceMode, chartSourceMode + \sa maxDatasetSourceMode, setAdditionalChartType + */ +bool KDChartParams::findDatasets( SourceMode modeA, + SourceMode modeB, + uint& dataset, + uint& dataset2, + uint chart ) const +{ + bool res = false; + dataset = KDCHART_NO_DATASET; + dataset2 = KDCHART_NO_DATASET; + uint dsA1, dsA2, dsB1, dsB2; + bool foundA = findDataset( modeA, dsA1, dsA2, chart ); + bool foundB = findDataset( modeB, dsB1, dsB2, chart ); + if( foundA || foundB ){ + if( dsA1 == KDCHART_ALL_DATASETS || dsB1 == KDCHART_ALL_DATASETS ){ + dataset = KDCHART_ALL_DATASETS; + dataset2 = KDCHART_ALL_DATASETS; + res = true; + }else{ + if( foundA && foundB ){ + if( QMIN(dsA2, dsB2) + 1 == QMAX(dsA1, dsB1) ){ + dataset = QMIN(dsA1, dsB1); + dataset2 = QMAX(dsA2, dsB2); + res = true; + }else{ + qDebug("ERROR in KDChartParams::findDatasets(): Datasets found are *not* a contiguous series."); + } + }else{ + dataset = foundA ? dsA1 : dsB1; + dataset2 = foundA ? dsA2 : dsB2; + res = true; + } + } + } + return res; +} + + +/** + Specifies a color for a dataset. Note that if you define a color for a + dataset but not for a dataset with a lower number (and there is neither + a default value), the color for that dataset with the lower number will + be undefined. If you do not want any color, pass an invalid color + (i.e. a default-constructed QColor object). This is only useful + for chart types that have an outline (like bars or pies), + because you would not see anything otherwise.<br> + In charts that can only display one dataset (like pies), the specified + colors are used for the different values instead. + + \param dataset the number of the dataset (or value for pies and similar + charts) for which the color is specified + \param color the color to use for this dataset/value + \sa setDataDefaultColors, setDataRainbowColors, setDataSubduedColors + \sa dataColor, maxDataColor + */ +void KDChartParams::setDataColor( uint dataset, QColor color ) +{ + QColor shadow1; + QColor shadow2; + calculateShadowColors( color, shadow1, shadow2 ); + _dataColors[ dataset ] = color; + _dataColorsShadow1[ dataset ] = shadow1; + _dataColorsShadow2[ dataset ] = shadow2; + _maxDatasetColor = QMAX( _maxDatasetColor, dataset ); + + emit changed(); +} + +/** + Specifies KD Chart's default colors for the datasets 0..11. + + This method may be usefull to re-set the colors after changing the + colors of the datasets. + + \sa setDataColor, setDataRainbowColors, setDataSubduedColors + \sa dataColor, maxDataColor + */ +void KDChartParams::setDataDefaultColors() +{ + setDataColor( 0, red ); + setDataColor( 1, green ); + setDataColor( 2, blue ); + setDataColor( 3, cyan ); + setDataColor( 4, magenta ); + setDataColor( 5, yellow ); + setDataColor( 6, darkRed ); + setDataColor( 7, darkGreen ); + setDataColor( 8, darkBlue ); + setDataColor( 9, darkCyan ); + setDataColor( 10, darkMagenta ); + setDataColor( 11, darkYellow ); +} + +/** + Specifies a set of eight rainbow-like colors for the datasets 0..7 + and sets the lighter versions of this colors to datasets 8..15. + + This is a convenience method setting colors that look best on + a dark gray or black background. + + \sa setDataColor, setDataDefaultColors, setDataSubduedColors + \sa dataColor, maxDataColor + */ +void KDChartParams::setDataRainbowColors() +{ + setDataColor( 0, QColor(255, 0,196) ); + setDataColor( 1, QColor(255, 0, 96) ); + setDataColor( 2, QColor(255, 128,64) ); + setDataColor( 3, Qt::yellow ); + setDataColor( 4, Qt::green ); + setDataColor( 5, Qt::cyan ); + setDataColor( 6, QColor( 96, 96,255) ); + setDataColor( 7, QColor(160, 0,255) ); + for( int i=8; i<16; ++i ) + setDataColor( i, dataColor(i-8).light() ); +} + +/** + Specifies a set of eighteen subdued colors for the datasets 0..17. + + This is a convenience method setting colors that is ideal for + area charts since the dark data value texts can be easily + read when printed onto such background colors. + + \param ordered if true all eighteen colors will be set according + to their appearance in the color circle, if false they will be + arranged in a logical order making it very easy ti distinguish + two neighboring colors. Set this parameter to true if you want + your neighboring colors look quite similiar, the default is false. + + \sa setDataColor, setDataDefaultColors, setDataRainbowColors + \sa dataColor, maxDataColor + */ +void KDChartParams::setDataSubduedColors( bool ordered ) +{ +static const int NUM_SUBDUEDCOLORS = 18; +static const QColor SUBDUEDCOLORS[ NUM_SUBDUEDCOLORS ] = { + QColor( 0xe0,0x7f,0x70 ), + QColor( 0xe2,0xa5,0x6f ), + QColor( 0xe0,0xc9,0x70 ), + QColor( 0xd1,0xe0,0x70 ), + QColor( 0xac,0xe0,0x70 ), + QColor( 0x86,0xe0,0x70 ), + QColor( 0x70,0xe0,0x7f ), + QColor( 0x70,0xe0,0xa4 ), + QColor( 0x70,0xe0,0xc9 ), + QColor( 0x70,0xd1,0xe0 ), + QColor( 0x70,0xac,0xe0 ), + QColor( 0x70,0x86,0xe0 ), + QColor( 0x7f,0x70,0xe0 ), + QColor( 0xa4,0x70,0xe0 ), + QColor( 0xc9,0x70,0xe0 ), + QColor( 0xe0,0x70,0xd1 ), + QColor( 0xe0,0x70,0xac ), + QColor( 0xe0,0x70,0x86 ), +}; + if( ordered ) + for(int i=0; i<NUM_SUBDUEDCOLORS; ++i) + setDataColor( i, SUBDUEDCOLORS[i] ); + else{ + setDataColor( 0, SUBDUEDCOLORS[ 0] ); + setDataColor( 1, SUBDUEDCOLORS[ 5] ); + setDataColor( 2, SUBDUEDCOLORS[10] ); + setDataColor( 3, SUBDUEDCOLORS[15] ); + setDataColor( 4, SUBDUEDCOLORS[ 2] ); + setDataColor( 5, SUBDUEDCOLORS[ 7] ); + setDataColor( 6, SUBDUEDCOLORS[12] ); + setDataColor( 7, SUBDUEDCOLORS[17] ); + setDataColor( 8, SUBDUEDCOLORS[ 4] ); + setDataColor( 9, SUBDUEDCOLORS[ 9] ); + setDataColor(10, SUBDUEDCOLORS[14] ); + setDataColor(11, SUBDUEDCOLORS[ 1] ); + setDataColor(12, SUBDUEDCOLORS[ 6] ); + setDataColor(13, SUBDUEDCOLORS[11] ); + setDataColor(14, SUBDUEDCOLORS[16] ); + setDataColor(15, SUBDUEDCOLORS[ 3] ); + setDataColor(16, SUBDUEDCOLORS[ 8] ); + setDataColor(17, SUBDUEDCOLORS[13] ); + } +} + + +void KDChartParams::calculateShadowColors( QColor color, + QColor& shadow1, + QColor& shadow2 ) const +{ + if ( !color.isValid() ) { // no fill color + shadow1 = QColor(); + shadow2 = QColor(); + } else { + int hue, saturation, value; + color.hsv( &hue, &saturation, &value ); + double v = value; + v = v * 2.0 / 3.0 * shadowBrightnessFactor(); + if ( 255.0 < v ) + v = 255.0; + else if ( 1.0 > v ) + v = 0.0; + shadow1.setHsv( hue, saturation, static_cast < int > ( v ) ); + v = value; + v = v / 3.0 * shadowBrightnessFactor(); + if ( 255.0 < v ) + v = 255.0; + else if ( 1.0 > v ) + v = 0.0; + shadow2.setHsv( hue, saturation, static_cast < int > ( v ) ); + } +} + + +/** + Returns the color for a dataset. If no color has been defined for this + dataset and none for a higher dataset either, the number will be mapped + to the range of defined colors. If no color has been defined for this + dataset, but at least one for a higher dataset, the return value of + this method is undefined. + + \param dataset the number of the dataset for which to return the color + \return the color for this dataset + \sa setDataColor, maxDataColor + */ +QColor KDChartParams::dataColor( uint dataset ) const +{ + uint index = dataset % (_maxDatasetColor+1); + if( _dataColors.find( index ) != _dataColors.end() ) + return _dataColors[ index ]; + else + return QColor(); // documentation says undefined +} + + +QString KDChartParams::dataRegionFrameAreaName( uint dataRow, + uint dataCol, + uint data3rd ) +{ + return QString( "%1/%2/%3/%4" ) + .arg( KDChartEnums::AreaChartDataRegion, 5 ) + .arg( dataRow, 5 ) + .arg( dataCol, 5 ) + .arg( data3rd, 5 ); +} + + +/** + Recomputes the shadow colors by iterating over all configured + data colors and reassigning the data colors with exactly the + same values which in turn triggers computation of the shadow + colors. Expensive if many data colors are set, but performance + is OK for typical cases. + */ +void KDChartParams::recomputeShadowColors() +{ + // Simply reassign the available colors; this will trigger + // recomputation of the shadow colors. + for( QMap<uint,QColor>::Iterator it = _dataColors.begin(); + it != _dataColors.end(); ++it ) { + setDataColor( it.key(), it.data() ); + } +} + + + + +/** + Returns the first shadow color for a dataset. This is the color that + is used to draw the top bars with 3D effects. It is somewhat + darker than the original data color. If no color has been defined for this + dataset and none for a higher dataset either, the number will be mapped + to the range of defined colors. If no color has been defined for this + dataset, but at least one for a higher dataset, the return value of + this method is undefined. + + \param dataset the number of the dataset for which to return the color + \return the color for this dataset + \sa setDataColor, maxDataColor, dataShadow2Color + */ +QColor KDChartParams::dataShadow1Color( uint dataset ) const +{ + uint index = dataset % _maxDatasetColor; + if ( _threeDShadowColors ) + if( _dataColorsShadow1.find( index ) != _dataColorsShadow1.end() ) + return _dataColorsShadow1[ index ]; + else + return QColor(); // documentation says undefined + else + if( _dataColors.find( index ) != _dataColors.end() ) + return _dataColors[ index ]; + else + return QColor(); // documentation says undefined +} + + +/** + Returns the second shadow color for a dataset. This is the color that + is used to draw the sides of bars with 3D effects. It is + darker than the original data color. If no color has been defined for this + dataset and none for a higher dataset either, the number will be mapped + to the range of defined colors. If no color has been defined for this + dataset, but at least one for a higher dataset, the return value of + this method is undefined. + + \param dataset the number of the dataset for which to return the color + \return the color for this dataset + \sa setDataColor, maxDataColor, dataShadow1Color + */ +QColor KDChartParams::dataShadow2Color( uint dataset ) const +{ + uint index = dataset % _maxDatasetColor; + if ( _threeDShadowColors ) + if( _dataColorsShadow2.find( index ) != _dataColorsShadow2.end() ) + return _dataColorsShadow2[ index ]; + else + return QColor(); // documentation says undefined + else + if( _dataColors.find( index ) != _dataColors.end() ) + return _dataColors[ index ]; + else + return QColor(); // documentation says undefined +} + + +/** + Set the default axis types for all the axes the user might activate for this chart: + \li Pie charts by default have no axes at all + \li Bar/Line/Area charts may have up to 8 axes (up to 2 at each side of the chart) + \li charts representing 3-dimensional data may have up to 12 axes + + \note This function also specifies the default way how to calculate + the axis labels: abscissa starts with zero and counts by 1, ordinate + is calculating the labels automatically based upon the values found in + the associated dataset(s). + */ +void KDChartParams::setDefaultAxesTypes() +{ + // reset types of all axes + uint i = 0; + for ( i = 0; i < KDCHART_MAX_AXES; ++i ) + setAxisType( i, KDChartAxisParams::AxisTypeUnknown ); + + // Note that abscissa axes should start labeling at the very + // first position and end at the last position when drawing + // area charts. + // Bar charts and line charts look better with their abscissa labels + // in the respective middle positions below each bar (or point, resp.) + for ( i = KDChartAxisParams::AxisPosSTART; + i <= KDChartAxisParams::AxisPosEND; ++i ) + switch ( i ) { + // abscissa axes: + case KDChartAxisParams::AxisPosBottom: + case KDChartAxisParams::AxisPosTop: + case KDChartAxisParams::AxisPosBottom2: + case KDChartAxisParams::AxisPosTop2: + setAxisLabelsTouchEdges( i, Area == chartType() ); + break; + // ordinate axes: + case KDChartAxisParams::AxisPosLeft: + case KDChartAxisParams::AxisPosRight: + case KDChartAxisParams::AxisPosLeft2: + case KDChartAxisParams::AxisPosRight2: + setAxisLabelsTouchEdges( i, true ); //Polar != chartType() ); + break; + // additional axes for charts representing 3-dimensional data: + case KDChartAxisParams::AxisPosLowerRightEdge: + case KDChartAxisParams::AxisPosLowerLeftEdge: + case KDChartAxisParams::AxisPosLowerRightEdge2: + case KDChartAxisParams::AxisPosLowerLeftEdge2: + setAxisLabelsTouchEdges( i, false ); + break; + default: { + qDebug( "IMPLEMENTATION ERROR: axis type missing in KDChartParams::setDefaultAxesTypes()" ); + Q_ASSERT( !this ); + } + } + + // set default axis types according to chart type + switch ( chartType() ) { + case NoType: + break; + case Bar: + case Line: + case Area: + // default axes + setAxisType( KDChartAxisParams::AxisPosBottom, + KDChartAxisParams::AxisTypeEAST ); + setAxisShowGrid( KDChartAxisParams::AxisPosBottom, + true ); + setAxisType( KDChartAxisParams::AxisPosLeft, + KDChartAxisParams::AxisTypeNORTH ); + setAxisShowGrid( KDChartAxisParams::AxisPosLeft, + true ); + // 'other side' axes + setAxisType( KDChartAxisParams::AxisPosTop, + KDChartAxisParams::AxisTypeEAST ); + setAxisType( KDChartAxisParams::AxisPosRight, + KDChartAxisParams::AxisTypeNORTH ); + // additional, 2nd axes + setAxisType( KDChartAxisParams::AxisPosBottom2, + KDChartAxisParams::AxisTypeEAST ); + setAxisType( KDChartAxisParams::AxisPosLeft2, + KDChartAxisParams::AxisTypeNORTH ); + // additional, 2nd axes for 'other' sides + setAxisType( KDChartAxisParams::AxisPosTop2, + KDChartAxisParams::AxisTypeEAST ); + setAxisType( KDChartAxisParams::AxisPosRight2, + KDChartAxisParams::AxisTypeNORTH ); + + // Specify default numbering information + // for all 'non-ordinate' axes (this are the X axes): + // + // axisSteadyValueCalc flag is set to false + // Start value 1 + // End value following the number of entries + // in the associated dataset(s) + // Delta value 1.0 + // and dont show any Digits behind the comma. + setAxisLabelTextParams( KDChartAxisParams::AxisPosBottom, false, + 1.0, + KDCHART_AXIS_LABELS_AUTO_LIMIT, + 1.0, 0 ); + setAxisLabelTextParams( KDChartAxisParams::AxisPosTop, false, + 1.0, + KDCHART_AXIS_LABELS_AUTO_LIMIT, + 1.0, 0 ); + setAxisLabelTextParams( KDChartAxisParams::AxisPosBottom2, false, + 1.0, + KDCHART_AXIS_LABELS_AUTO_LIMIT, + 1.0, 0 ); + setAxisLabelTextParams( KDChartAxisParams::AxisPosTop2, false, + 1.0, + KDCHART_AXIS_LABELS_AUTO_LIMIT, + 1.0, 0 ); + + // no need to specify numbering information for + // the ordinate-axes since the default auto-calc + // feature is fine for here. + + break; + + // Code for charts representing 3-dimensional data. + + /* + + // ( not active since there are no such charts yet ) + + case KDChartParams::BarMatrix: + // default axes + setAxisType( KDChartAxisParams::AxisPosBottom, + KDChartAxisParams::AxisTypeEAST ); + setAxisType( KDChartAxisParams::AxisPosLowerRightEdge, + KDChartAxisParams::AxisTypeNORTH ); + setAxisType( KDChartAxisParams::AxisPosLeft, + KDChartAxisParams::AxisTypeUP ); + // 'other side' axes + setAxisType( KDChartAxisParams::AxisPosTop, + KDChartAxisParams::AxisTypeEAST ); + setAxisType( KDChartAxisParams::AxisPosLowerLeftEdge, + KDChartAxisParams::AxisTypeNORTH ); + setAxisType( KDChartAxisParams::AxisPosRight, + KDChartAxisParams::AxisTypeUP ); + // additional, 2nd axes + setAxisType( KDChartAxisParams::AxisPosBottom2, + KDChartAxisParams::AxisTypeEAST ); + setAxisType( KDChartAxisParams::AxisPosLowerRightEdge2, + KDChartAxisParams::AxisTypeNORTH ); + setAxisType( KDChartAxisParams::AxisPosLeft2, + KDChartAxisParams::AxisTypeUP ); + // additional, 2nd axes for 'other' sides + setAxisType( KDChartAxisParams::AxisPosTop2, + KDChartAxisParams::AxisTypeEAST ); + setAxisType( KDChartAxisParams::AxisPosLowerLeftEdge2, + KDChartAxisParams::AxisTypeNORTH ); + setAxisType( KDChartAxisParams::AxisPosRight2, + KDChartAxisParams::AxisTypeUP ); + + // Specify default numbering information + // for all 'non-ordinate' axes (this are the X and the Y axes): + // Start vaule 1 + // End value following the number of entries + // in the associated dataset(s) + // Delta value 1.0 + // and don't show any Digits behind the comma. + setAxisLabelTextParams( KDChartAxisParams::AxisPosBottom, false, + 1.0, KDCHART_AXIS_LABELS_AUTO_LIMIT, 1.0, 0 ); + setAxisLabelTextParams( KDChartAxisParams::AxisPosTop, false, + 1.0, KDCHART_AXIS_LABELS_AUTO_LIMIT, 1.0, 0 ); + setAxisLabelTextParams( KDChartAxisParams::AxisPosLowerRightEdge, + false, + 1.0, KDCHART_AXIS_LABELS_AUTO_LIMIT, 1.0, 0 ); + setAxisLabelTextParams( KDChartAxisParams::AxisPosLowerLeftEdge, + false, + 1.0, KDCHART_AXIS_LABELS_AUTO_LIMIT, 1.0, 0 ); + setAxisLabelTextParams( KDChartAxisParams::AxisPosBottom2, false, + 1.0, KDCHART_AXIS_LABELS_AUTO_LIMIT, 1.0, 0 ); + setAxisLabelTextParams( KDChartAxisParams::AxisPosTop2, false, + 1.0, KDCHART_AXIS_LABELS_AUTO_LIMIT, 1.0, 0 ); + setAxisLabelTextParams(KDChartAxisParams::AxisPosLowerRightEdge2, + false, + 1.0, KDCHART_AXIS_LABELS_AUTO_LIMIT, 1.0, 0 ); + setAxisLabelTextParams( KDChartAxisParams::AxisPosLowerLeftEdge2, + false, + 1.0, KDCHART_AXIS_LABELS_AUTO_LIMIT, 1.0, 0 ); + + false, + 1.0, KDCHART_AXIS_LABELS_AUTO_LIMIT, 1.0, 0 ); + + // no need to specify numbering information for + // the ordinate-axes since the default auto-calc + // feature is fine for here. + + break; + */ + + case KDChartParams::Pie: + case KDChartParams::Ring: + // by default there are no axes defined for pie and ring charts + break; + case KDChartParams::Polar: + setAxisType( KDChartAxisParams::AxisPosSaggital, + KDChartAxisParams::AxisTypeEAST ); + setAxisType( KDChartAxisParams::AxisPosCircular, + KDChartAxisParams::AxisTypeNORTH ); + setAxisLabelsVisible( KDChartAxisParams::AxisPosSaggital, true ); + setAxisLabelsFont( KDChartAxisParams::AxisPosSaggital, + QFont( "helvetica", 1, QFont::Bold ), + -30, + Qt::darkBlue ); + setAxisLabelsVisible( KDChartAxisParams::AxisPosCircular, true ); + setAxisLabelsFont( KDChartAxisParams::AxisPosCircular, + QFont( "helvetica", 1, QFont::Bold ), + -22, + Qt::darkBlue ); + setPolarRotateCircularLabels( false ); + break; + default: { + qDebug( "IMPLEMENTATION ERROR: type missing in KDChartParams::setDefaultAxesTypes()" ); + Q_ASSERT( !this ); + } + } + + emit changed(); +} + + +/** + activate the default axis types for this chart: + \li Pie charts by default have no axes at all + \li Bar/Line/Area by default have 2 axes: the abscissa and the ordinate + \li charts representing 3-dimensional data by default have 3 axes + */ +void KDChartParams::activateDefaultAxes() +{ + // deactivate all axes: specifying KDCHART_NO_DATASET will + // also remove their visability flag + for ( uint i = 0; i < KDCHART_MAX_AXES; ++i ) + setAxisDatasets( i, KDCHART_NO_DATASET ); + + switch ( chartType() ) { + case NoType: + break; + case Bar: + case Line: + case Area: + setAxisVisible( KDChartAxisParams::AxisPosBottom, true ); + setAxisDatasets( KDChartAxisParams::AxisPosBottom, KDCHART_ALL_DATASETS ); + setAxisVisible( KDChartAxisParams::AxisPosLeft, true ); + setAxisDatasets( KDChartAxisParams::AxisPosLeft, KDCHART_ALL_DATASETS ); + /* test: + setAxisVisible( KDChartAxisParams::AxisPosTop, true ); + setAxisDatasets(KDChartAxisParams::AxisPosTop, KDCHART_ALL_DATASETS ); + setAxisVisible( KDChartAxisParams::AxisPosRight, true ); + setAxisDatasets(KDChartAxisParams::AxisPosRight, KDCHART_ALL_DATASETS ); + + setAxisVisible( KDChartAxisParams::AxisPosBottom2, true ); + setAxisDatasets(KDChartAxisParams::AxisPosBottom2, KDCHART_ALL_DATASETS ); + setAxisVisible( KDChartAxisParams::AxisPosLeft2, true ); + setAxisDatasets(KDChartAxisParams::AxisPosLeft2, KDCHART_ALL_DATASETS ); + + setAxisVisible( KDChartAxisParams::AxisPosTop2, true ); + setAxisDatasets(KDChartAxisParams::AxisPosTop2, KDCHART_ALL_DATASETS ); + setAxisVisible( KDChartAxisParams::AxisPosRight2, true ); + setAxisDatasets(KDChartAxisParams::AxisPosRight2, KDCHART_ALL_DATASETS ); + */ + break; + // Code for charts representing 3-dimensional data. + + /* + + // ( not active since there are no such charts yet ) + + case KDChartParams::BarMatrix: + setAxisVisible( KDChartAxisParams::AxisPosBottom, true ); + setAxisDatasets(KDChartAxisParams::AxisPosBottom, KDCHART_ALL_DATASETS ); + setAxisVisible( KDChartAxisParams::AxisPosLeft, true ); + setAxisDatasets(KDChartAxisParams::AxisPosLeft, KDCHART_ALL_DATASETS ); + setAxisVisible( KDChartAxisParams::AxisPosLowerRightEdge, true ); + setAxisDatasets(KDChartAxisParams::AxisPosLowerRightEdge, + KDCHART_ALL_DATASETS); + break; + */ + case KDChartParams::Pie: + case KDChartParams::Ring: + case KDChartParams::Polar: + // by default there are no axes defined for pie, ring, and polar charts + break; + default: { + qDebug( "IMPLEMENTATION ERROR: type missing in KDChartParams::activateDefaultAxes()" ); + Q_ASSERT( !this ); + } + } +} + + + + + + + + +/** + \fn KDChartParams::setOptimizeOutputForScreen(bool) + + Specify whether drawing should be optimized for (low resolution) + screen output or whether other routines should be used to obtain + best quality when using a printer. + + \note The default of this flag is TRUE, so make sure to deactivate + it before painting your KDChart onto a QPrinter device! + + \note This setting is NOT stored together with the other parameters, + it is recommended to set the flag each time when the drawing targed + (printer or screen, resp) is changed. + + \sa optimizeOutputForScreen + */ + + +/** + \fn bool KDChartParams::optimizeOutputForScreen() const + Returns whether drawing is optimized for (low resolution) + screen output (this is the default) or whether other routines + are used to obtain best quality when using a printer. + + \sa setOptimizeOutputForScreen + */ + + +/** + Specifies the distance between the chart and the left border of the + painter area. If \c leading is a negative value it is interpreted + as per-mille value of the painter area, the true offset will then + be calculated dynamically at drawing time. + + \sa setGlobalLeading, setGlobalLeadingTop, setGlobalLeadingRight, + setGlobalLeadingBottom + \sa globalLeadingLeft, globalLeadingTop, globalLeadingRight, + globalLeadingBottom + */ +void KDChartParams::setGlobalLeading( int left, int top, int right, int bottom ) +{ + _globalLeadingLeft = left; + _globalLeadingTop = top; + _globalLeadingRight = right; + _globalLeadingBottom = bottom; + emit changed(); +} + + +/** + \fn void KDChartParams::setGlobalLeadingLeft( int ) + + Specifies the distance between the chart and the left border of the + painter area. If \c leading is a negative value it is interpreted + as per-mille value of the painter area, the true offset will then + be calculated dynamically at drawing time. + + \sa setGlobalLeading, setGlobalLeadingTop, setGlobalLeadingRight, + setGlobalLeadingBottom + \sa globalLeadingLeft, globalLeadingTop, globalLeadingRight, + globalLeadingBottom + */ + + +/** + \fn void KDChartParams::setGlobalLeadingTop( int ) + + Specifies the distance between the chart and the upper border of + the painter area. If \c leading is a negative value it is + interpreted as per-mille value of the painter area, the true offset + will then be calculated dynamically at drawing time. + + \sa setGlobalLeading, setGlobalLeadingLeft, setGlobalLeadingRight, + setGlobalLeadingBottom + \sa globalLeadingLeft, globalLeadingTop, globalLeadingRight, + globalLeadingBottom + */ + + +/** + \fn void KDChartParams::setGlobalLeadingRight( int ) + + Specifies the distance between the chart and the right border + of the painter area. If \c leading is a negative value it is + interpreted as per-mille value of the painter area, the true + offset will then be calculated dynamically at drawing time. + + \sa setGlobalLeading, setGlobalLeadingLeft, setGlobalLeadingTop, + setGlobalLeadingBottom + \sa globalLeadingLeft, globalLeadingTop, globalLeadingRight, + globalLeadingBottom + */ + + +/** + \fn void KDChartParams::setGlobalLeadingBottom( int ) + + Specifies the distance between the chart and the lower border of + the painter area. If \c leading is a negative value it is + interpreted as per-mille value of the painter area, the true offset + will then be calculated dynamically at drawing time. + + \sa setGlobalLeading, setGlobalLeadingLeft, setGlobalLeadingTop, + setGlobalLeadingRight + \sa globalLeadingLeft, globalLeadingTop, globalLeadingRight, + globalLeadingBottom + */ + + +/** + \fn int KDChartParams::globalLeadingLeft() const + + Return the distance between the chart and the left border of the + painter area. If \c leading is a negative value it is interpreted + as per-mille value of the painter area, the true offset will then + be calculated dynamically at drawing time. + + \sa setGlobalLeading, setGlobalLeadingLeft, setGlobalLeadingTop, + setGlobalLeadingRight, setGlobalLeadingBottom + \sa globalLeadingTop, globalLeadingRight, globalLeadingBottom + */ + + +/** + \fn KDChartParams::globalLeadingTop() const + + Return the distance between the chart and the upper border of the + painter area. If \c leading is a negative value it is interpreted + as per-mille value of the painter area, the true offset will then + be calculated dynamically at drawing time. + + \sa setGlobalLeading, setGlobalLeadingLeft, setGlobalLeadingTop, + setGlobalLeadingRight, setGlobalLeadingBottom + \sa globalLeadingLeft, globalLeadingRight, globalLeadingBottom + */ + + +/** + \fn KDChartParams::globalLeadingRight() const + + Return the distance between the chart and the right border of the + painter area. If \c leading is a negative value it is interpreted + as per-mille value of the painter area, the true offset will then + be calculated dynamically at drawing time. + + \sa setGlobalLeading, setGlobalLeadingLeft, setGlobalLeadingTop, + setGlobalLeadingRight, setGlobalLeadingBottom + \sa globalLeadingLeft, globalLeadingTop, globalLeadingBottom + */ + + +/** + \fn int KDChartParams::globalLeadingBottom() const + + Return the distance between the chart and the lower border of the + painter area. If \c leading is a negative value it is interpreted + as per-mille value of the painter area, the true offset will then + be calculated dynamically at drawing time. + + \sa setGlobalLeading, setGlobalLeadingLeft, setGlobalLeadingTop, + setGlobalLeadingRight, setGlobalLeadingBottom + \sa globalLeadingLeft, globalLeadingTop, globalLeadingRight + */ + + +/** + \fn void KDChartParams::setFrame( uint, const KDFrame&, int, int, + int, int, bool, bool ) + + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Specify the frame settings to be used for one of the chart areas. + + The names and the meaning of the areas are explained with the enum + AreaName. The KDFrame class is explained in detail here: KDFrame + <b>Note however:</b> The \c innerRect settings of this KDFrame will + be ignored since position and size of this frame will be calculated + dynamically based upon the AreaName! + + \param area The area to be surrounded by a frame. + \param outerGap The distance between the frame and the surrounding + parts of the chart. + \param innerGap The distance between the frame and the inner area. + \param addFrameWidthToLayout Specifies whether the inner area will + shrink the area AND its frame will occupy the same space of the + chart as the area would occupy if no frame were drawn. If false, + the frame is drawn around the area without taking care not to + override other content of the chart. + + \note An easier way to specify a frame setting is selecting a pre-defined + setting using the setSimpleFrame methode. + + \sa setSimpleFrame, frameSettings, removeFrame + \endif + */ + +/** + \fn void KDChartParams::setSimpleFrame( uint, int, int, int, int, + bool, bool, KDFrame::SimpleFrame, int, int, QPen, QBrush, const + QPixmap*, KDFrame::BackPixmapMode, int, KDFrame::CornerName ) + + Select a pre-defined frame setting to be used for one of the chart areas. + + \param area The area to be surrounded by a frame. + \param outerGap The distance between the frame and the surrounding + parts of the chart. + \param innerGap The distance between the frame and the inner area. + \if v200 + \param addFrameWidthToLayout Specifies whether the inner area will + shrink the area AND its frame will occupy the same space of the + chart as the area would occupy if no frame were drawn. If false, + the frame is drawn around the area without taking care not to + override other content of the chart. + + The parameters specifying the pre-defined frame settings are + defined here: KDFrame::setSimpleFrame. + \sa setFrame, frameSettings + \else + \param addFrameWidthToLayout For internal use - must be \c true. + \param addFrameHeightToLayout For internal use - must be \c true. + \param simpleFrame For internal use - must be \c KDFrame::FrameFlat. + \param lineWidth For internal use - must be \c 1. + \param midLineWidth For internal use - must be \c 0. + \param pen The pen to be used for drawing the four frame border lines. + \param background The brush to be used for painting the frame background. + \param backPixmap The picture to be displayed as background image + of the frame. + \param backPixmapMode The way how backPixmap is to be modified to + fill the frame, these are possible: + \li \c KDFrame::PixCentered Image will not be scaled nor stretched + but shown in original size. + \li \c KDFrame::PixScaled Image will be scaled until it's height or + it's width match the frame's height (or width, resp.) - whichever + comes first - but image proportions will be preserved. + \li \c KDFrame::PixStretched Image will be stretched to have the + same height and the same width as the frame - normally this results + in changing the original image proportions. + \param shadowWidth For internal use - do not set this parameter or + set it to \c 0. + \param sunPos For internal use - do not set this parameter or set + it to \c KDFrame::CornerTopLeft. + \endif + + \sa removeFrame + */ + + +/** + \fn void KDChartParams::setDataRegionFrame( uint, uint, uint, int, int, int, int, + bool, bool, KDFrame::SimpleFrame, int, int, QPen, QBrush, const + QPixmap*, KDFrame::BackPixmapMode, int, KDFrame::CornerName ) + + Select a pre-defined frame setting to be used for one of the data representations + (e.g. to have the frame surrounding one of the bars in a Bar chart). + + \note KDChart currently supports specifying data frames without background only, + it is not possible to draw a special background behind a data representation. + + \param dataRow The table row of the cell to be surrounded by a frame. + \param dataCol The table column of the cell. + \param data3rd The third table coordinate of the cell (parameter not used, its value will be ignored, set to 0, reserved for future use). + \param innerGap The distance between the frame and the inner area. + \if v200 + \param addFrameWidthToLayout Specifies whether the inner area will + shrink the area AND its frame will occupy the same space of the + chart as the area would occupy if no frame were drawn. If false, + the frame is drawn around the area without taking care not to + override other content of the chart. + + The parameters specifying the pre-defined frame settings are + defined here: KDFrame::setSimpleFrame. + \sa setFrame, frameSettings + \else + \param addFrameWidthToLayout For internal use - must be \c true. + \param addFrameHeightToLayout For internal use - must be \c true. + \param simpleFrame For internal use - must be \c KDFrame::FrameFlat. + \param lineWidth For internal use - must be \c 1. + \param midLineWidth For internal use - must be \c 0. + \param pen The pen to be used for drawing the four frame border lines. + \param shadowWidth For internal use - do not set this parameter or + set it to \c 0. + \param sunPos For internal use - do not set this parameter or set + it to \c KDFrame::CornerTopLeft. + \endif + */ + + +/** + \fn bool KDChartParams::moveDataRegionFrame( uint, uint, uint, uint, uint, uint ) + + Move a frame that was previously specified using setDataRegionFrame + to another location: moves the frame from cell[ oldDataRow, oldDataCol ] + to cell[ newDataRow, newDataCol ] without changing looking of the frame, + or just removes the frame entirely. + + \param oldDataRow The table row of the frame to be removed. + \param oldDataCol The table column of the frame to be removed. + \param oldData3rd The third table coordinate of the old cell (parameter not used, its value will be ignored, set to 0, reserved for future use). + \param newDataRow The table row of the cell to be framed instead, or KDCHART_NO_DATASET if the frame is to be removed + without framing another cell then. + \param newDataCol The table column of the cell to be framed instead. + \param newData3rd The third table coordinate of the new cell (parameter not used, its value will be ignored, set to 0, reserved for future use). + + \note Using KDCHART_NO_DATASET for the newDataRow parameter will not + result in returning FALSE because it is an allowed action: the frame is just removed then. + + \return TRUE if the frame could be moved or was removed; FALSE if either there was no frame around the old cell or the target cell does not exist. + */ +bool KDChartParams::moveDataRegionFrame( uint oldDataRow, + uint oldDataCol, + uint, // important: we ignore the data3rd parameter for now! + uint newDataRow, + uint newDataCol, + uint// important: we ignore the data3rd parameter for now! + ) +{ + const QString oldKey( dataRegionFrameAreaName( oldDataRow, oldDataCol, 0 ) ); // oldData3rd ) ); + KDChartFrameSettings* it = _areaDict.find( oldKey ); + bool bFound = ( it != 0 ); + if( bFound ){ + if( KDCHART_NO_DATASET != newDataRow ){ + KDChartFrameSettings* frame = new KDChartFrameSettings; + KDChartFrameSettings::deepCopy( *frame, *it ); + frame->setDataRow( newDataRow ); + frame->setDataCol( newDataCol ); + frame->setData3rd( 0 ); // newData3rd ); + _areaDict.setAutoDelete( TRUE ); + _areaDict.replace( + dataRegionFrameAreaName( newDataRow, newDataCol, 0 ), //data3rd 5 ), + frame ); + } + _areaDict.remove( oldKey ); + emit changed(); + } + return bFound; +} + +/** + \var KDChartParams::CustomBoxDict + + The type that stores the custom boxes in a chart. + */ + +/** + Return the frame settings of one of the chart areas. + + \sa nextFrameSettings, setSimpleFrame, setFrame, removeFrame + */ +const KDChartParams::KDChartFrameSettings* KDChartParams::frameSettings( uint area, + bool& bFound, + int* pIterIdx ) const +{ + if( pIterIdx ) + *pIterIdx = 0; + const QString key( QString( "%1/-----/-----/-----" ).arg( area, 5 ) ); + KDChartFrameSettings* it = _areaDict.find( key ); + bFound = ( it != 0 ); + if( bFound ) + return it; + else if( pIterIdx ){ + QString keyStart( key.left(6) ); + QDictIterator<KDChartFrameSettings> it2( _areaDict ); + for( ; it2.current(); ++it2 ){ + if( it2.currentKey().startsWith( keyStart ) ){ + bFound = true; + return it2.current(); + } + ++*pIterIdx; + } + } + return &_noFrameSettings; +} + + +/** + Remove the last frame, that was specified for this chart area. + If you have more than one frame attached to the same area, + make sure to call this method repeatedly, if you want + to remove all of them. + + \note This method can be used to remove frames, that were specified + via setFrame (or via setSimpleFrame, resp.), but if can not be used + to remove frames, that were specified using setDataRegionFrame. + + \sa nextFrameSettings, setSimpleFrame, setFrame + */ +bool KDChartParams::removeFrame( uint area ) +{ + return _areaDict.remove( QString( "%1/-----/-----/-----" ).arg( area, 5 ) ); +} + + +/** + Return the next frame settings specified for the same area type + as the frame that was retrieved by previous calls of \c frameSettings() + or \c nextFrameSettings(). + + \sa setDataRegionFrame, frameSettings + */ +const KDChartParams::KDChartFrameSettings* KDChartParams::nextFrameSettings( bool& bFound, + int* pIterIdx ) const +{ + bFound = false; + if( pIterIdx ){ + int i=0; + QDictIterator<KDChartFrameSettings> it( _areaDict ); + for( ; it.current(); ++it ){ + if( *pIterIdx == i ) + break; + ++i; + } + if( *pIterIdx == i ){ + QString keyStart( it.currentKey().left(6) ); + ++it; + for( ; it.current(); ++it ){ + ++*pIterIdx; + if( it.currentKey().startsWith( keyStart ) ){ + bFound = true; + return it.current(); + } + } + } + } + return &_noFrameSettings; +} + + +/** + Add a new custom box to the list of boxes. + To remove this box from the list lateron just call removeCustomBox + with the index that was returned by insertCustomBox. + + \sa removeCustomBox, removeAllCustomBoxes, customBox, maxCustomBoxIdx + */ +uint KDChartParams::insertCustomBox( const KDChartCustomBox & box ) +{ + _customBoxDict.setAutoDelete( true ); + uint newIdx; + if( _customBoxDictMayContainHoles ){ + _customBoxDictMayContainHoles = false; + const uint maxIndex = maxCustomBoxIdx(); + newIdx = 1 + maxIndex; + for( uint idx = 0; idx <= maxIndex; ++idx ) { + if( ! _customBoxDict.find( idx ) ) { + newIdx = idx; + _customBoxDictMayContainHoles = true; // we found a hole, so there might be more of them + break; + } + } + }else{ + newIdx = _customBoxDict.count(); + } + _customBoxDict.insert( newIdx, box.clone() ); + emit changed(); + return newIdx; +} + + +/** + Remove a custom box from the list of boxes. + Please fill in the index parameter with the value + that was returned by insertCustomBox. + + \note This method also removes the last frame, that was attached to this + box (if any), so the only reason for calling removeFrame() manually would + be, that you have specified more than one frame for this box. + + + \sa removeAllCustomBoxes, insertCustomBox, customBox, maxCustomBoxIdx + */ +bool KDChartParams::removeCustomBox( const uint & idx ) +{ + const bool bFound = _customBoxDict.remove( idx ); + if( bFound ){ + // also remove any frame, that was attached to this box + removeFrame( KDChartEnums::AreaCustomBoxesBASE + idx ); + } + _customBoxDictMayContainHoles = true; + emit changed(); + return bFound; +} + + +/** + \fn void KDChartParams::removeAllCustomBoxes() + Remove all custom boxes from the list of boxes. + + \sa removeCustomBox, insertCustomBox, customBox, maxCustomBoxIdx + */ + + +/** + Retrieve a const pointer to a custom box from the list. + You may fill in the index parameter with the value + that was returned by insertCustomBox, or you may use a numerical value, + e.g. when iterating from zero up to maxCustomBoxIdx(). + + \return Zero if the custom box has been removed by calling removeCustomBox. + + \sa insertCustomBox customBoxRef, removeCustomBox, removeAllCustomBoxes, maxCustomBoxIdx + */ +const KDChartCustomBox* KDChartParams::customBox( uint box ) const +{ + return _customBoxDict.find( box ); +} + +/** + Retrieve a non-const pointer to a custom box from the list. + You may fill in the index parameter with the value + that was returned by insertCustomBox, or you may use a numerical value, + e.g. when iterating from zero up to maxCustomBoxIdx(). + + \note The reference returned by this method may be used to directly + modify the properties of the respective box. + + \return Zero if the custom box has been removed by calling removeCustomBox. + + \sa insertCustomBox customBox, removeCustomBox, removeAllCustomBoxes, maxCustomBoxIdx + */ +KDChartCustomBox* KDChartParams::customBoxRef( uint box ) +{ + return _customBoxDict.find( box ); +} + +/** + Retrieve the biggest custom boxes ID used. + + \sa insertCustomBox, removeCustomBox, removeAllCustomBoxes, customBox + */ +uint KDChartParams::maxCustomBoxIdx() const +{ + uint cnt( _customBoxDict.count() ); + if( cnt ) { + int maxIndex = cnt-1; + QIntDictIterator<KDChartCustomBox> it( _customBoxDict ); + for( ; it.current(); ++it ) + if( it.currentKey() > maxIndex ) + maxIndex = it.currentKey(); + return maxIndex; + } + return 0; +} + +/** + \enum KDChartParams::ChartType + + The chart type. Covers only built-in chart types. + + \sa setChartType, chartType + \sa setAdditionalChartType, additionalChartType, + \sa setChartSourceMode + */ + + +/** + Specifies the chart type. The default is bar charts (Bar). + + \note Allways call this <b>before</b> specifying other + axis properties and before calling \c setAdditionalChartType().<br> + \c setChartType() deactivates the right axis and also sets the + axisLabelsTouchEdges back to their default for all bottom and + top axes. + + \note Calling setChartType() results in changing the default data value + texts font settings (i.e. the font size, colour, position, but not the + QFont itself) <b>if</b> these parameters were not changed by + setPrintDataValues(). + In order to re-activate this automatical adjustment of font settings + even after you have changed them manually you may call + setPrintDataValuesWithDefaultFontParams(). + \li When calling setPrintDataValuesWithDefaultFontParams() + <b>before</b> calling setChartType() make sure you set the + \c callSetPrintDataValues parameter to \c false. + \li When calling setPrintDataValuesWithDefaultFontParams() + <b>after</b> calling setChartType() you may set the \c + callSetPrintDataValues parameter to \c true <b>or</b> you may call + setPrintDataValues() yourself after calling + setPrintDataValuesWithDefaultFontParams() to specify additional + parameters. + + \param chartType the chart type to use + \sa chartType, ChartType + \sa setAdditionalChartType, additionalChartType, + \sa setBarChartSubType, barChartSubType + \sa setLineChartSubType, lineChartSubType + \sa setPolarChartSubType, polarChartSubType + \sa setChartSourceMode, chartSourceMode + \sa setAxisLabelsTouchEdges + */ +void KDChartParams::setChartType( ChartType chartType ) +{ + _chartType = chartType; + + // de-activate 2nd axis that might have been set automatically + // by a previous call of \c setAdditionalChartType() + setAxisVisible( KDChartAxisParams::AxisPosRight, false ); + + // Make sure abscissa axes start their labeling at the very + // first position and end at the last position when drawing + // area charts. + // Bar charts and line charts look better with their abscissa labels + // in the respective middle positions below each bar (or point, resp.) + bool bAbscissaAxisLabelsTouchEdges = ( Area == chartType ); + setAxisLabelsTouchEdges( KDChartAxisParams::AxisPosBottom, + bAbscissaAxisLabelsTouchEdges ); + setAxisLabelsTouchEdges( KDChartAxisParams::AxisPosTop, + bAbscissaAxisLabelsTouchEdges ); + setAxisLabelsTouchEdges( KDChartAxisParams::AxisPosBottom2, + bAbscissaAxisLabelsTouchEdges ); + setAxisLabelsTouchEdges( KDChartAxisParams::AxisPosTop2, + bAbscissaAxisLabelsTouchEdges ); + // activate default data value text settings for this chart type + if ( printDataValues( 0 ) + && printDataValuesWithDefaultFontParams( 0 ) ) + setPrintDataValues( true, 0 ); + emit changed(); +} + + +/** + \fn KDChartParams::ChartType KDChartParams::chartType() const + + Returns the chart type configured in this params object. + + \return the chart type configured in this params object. + \sa setChartType, ChartType + \sa setAdditionalChartType, additionalChartType, + \sa setBarChartSubType, barChartSubType + \sa setLineChartSubType, lineChartSubType + \sa setPolarChartSubType, polarChartSubType + \sa setChartSourceMode, chartSourceMode + */ + + + +/** + Specifies the additional chart type. + The default is no additional chart (NoType). + The additional chart will have <b>no grid</b> so either make sure you + specify the chart that should have a grid via \c setChartType() and + the other one as additional chart or specify the grid manually if you + need one for the additional chart too. + + The additional chart uses the same abscissa axis as the main chart, + but cannot change the abscissa settings: the abscissa range and step width + is determined according to the main chart's values. + + You may combine all different types of cartesian charts + (== Bar, Line, HiLo, ..., but not Polar, Pie, ...) + so e.g. the following combinations are possible:<br> + + \verbatim + setChartType( KDChartParams::Bar ); + setAdditionalChartType( KDChartParams::Line ); + \endverbatim + or + \verbatim + setChartType( KDChartParams::Line ); + setAdditionalChartType( KDChartParams::HiLo ); + \endverbatim + or + \verbatim + setChartType( KDChartParams::Bar ); + setAdditionalChartType( KDChartParams::Bar ); + ... + \endverbatim + + \note When defining multiple charts it is mandatory to + call \c setChartSourceMode() for specifying the dataset(s) + and their respective charts after calling \c setAdditionalChartType(), + also it is recommended to call \c setAxisDatasets() for all + axes to be used by the different datasets - otherwise the + default setting will be used (all datasets except the last + one match the left axis, the last dataset matches the right axis), + so e.g. your code to have a bar chart for datasets 0..2 (using the + left axis) and a line chart for datasets 3..4 (using the right axis) + could look like this: + + \verbatim + setChartType( KDChartParams::Bar ); + setAdditionalChartType( KDChartParams::Line ); + setChartSourceMode( KDChartParams::DataEntry, 0,2, 0 ); + setChartSourceMode( KDChartParams::DataEntry, 3,4, 1 ); + setAxisDatasets( KDChartAxisParams::AxisPosLeft, 0,2, 0 ); + setAxisDatasets( KDChartAxisParams::AxisPosRight, 3,4, 1 ); + \endverbatim + + \note In case you DON'T want to use the right axis but the left axis #2 + make sure to reset the dataset assignment that is automatically done for + the right axis. Doing this will also clear the visibility flag of the + right axis that is automatically set by setAdditionalChartType() + implicitely assuming that the additional chart will use the right axis, + so there is no need to explicitely call setAxisVisible( false ) for + the right axis: specifying KDCHART_NO_DATASET is enough to disable it. + e.g. in the example shown above you would replace the + last line by these ones: + + \verbatim + setAxisDatasets( KDChartAxisParams::AxisPosLeft2, 3,4, 1 ); + setAxisDatasets( KDChartAxisParams::AxisPosRight, + KDCHART_NO_DATASET ); + \endverbatim + + Often your additional chart will look better when not overlapping with + the first chart, specifying different y-positions for the charts + is possible by \c KDChartAxisParams::setAxisUseAvailableSpace(), + e.g. like this: + +\verbatim +// specify which amount of the available space +// is to be used by the first y-axis: + +KDChartAxisParams pa( + _p->axisParams( KDChartAxisParams::AxisPosLeft ) ); +pa.setAxisUseAvailableSpace( 0, -499 ); +_p->setAxisParams( KDChartAxisParams::AxisPosLeft, pa ); + +// specify which amount of the available space +// is to be used by the second y-axis: + +pa = _p->axisParams( KDChartAxisParams::AxisPosLeft2 ); +pa.setAxisUseAvailableSpace( -750, -1000 ); +_p->setAxisParams( KDChartAxisParams::AxisPosLeft2, pa ); +\endverbatim + +\param chartType the additional chart type to use + +\sa additionalChartType, setChartSourceMode, setAxisDatasets +\sa <br>chartType, ChartType +\sa setBarChartSubType, barChartSubType +\sa setLineChartSubType, lineChartSubType + */ +void KDChartParams::setAdditionalChartType( ChartType chartType ) +{ + _additionalChartType = chartType; + if ( KDChartParams::NoType == chartType ) { + setAxisDatasets( KDChartAxisParams::AxisPosRight, + KDCHART_NO_DATASET ); + } else { + setAxisDatasets( KDChartAxisParams::AxisPosRight, + KDCHART_ALL_DATASETS, + KDCHART_ALL_DATASETS, + 1 ); + if( printDataValues( 1 ) + && printDataValuesWithDefaultFontParams( 1 ) ) + setPrintDataValues( true, 1 ); + } + emit changed(); +} + + +/** + \fn void KDChartParams::ChartType KDChartParams::additionalChartType() const + + Returns the additional chart type configured in this params object. + + \return the additional chart type configured in this params object. + \sa setAdditionalChartType, setChartSourceMode, chartSourceMode + \sa <br>setChartType, ChartType + \sa setBarChartSubType, barChartSubType + \sa setLineChartSubType, lineChartSubType + */ + + + +/** + \fn bool KDChartParams::neverUsedSetChartSourceMode() const + + Retrieves whether setChartSourceMode() was called or not + */ + + + + +/** + \fn uint KDChartParams::maxDatasetSourceMode() const + + Returns the number of the highest dataset for which a SourceMode has been + defined. + + \return the number of the highest dataset for which a SourceMode has been + defined. + \sa setChartSourceMode, chartSourceMode, findDataset + */ + +/** + \fn void KDChartParams::setNumValues( uint ) + + Specifies how many of the values should be shown. -1 means all + available values. + + \param numValues the number of values to be shown + \sa numValues + */ + +/** + \fn int KDChartParams::numValues() const + + Returns how many of the values should be shown. -1 means all + available values. + + \return the number of values to be shown + \sa setNumValues + */ + + +/** + \fn void KDChartParams::setShadowBrightnessFactor( double factor ) + + Specifies a factor to be used to adjust the + built-in brightness of shadow colors in + 3-dimensional drawings like e.g. 3D Bar charts. + + \param factor a factor that is to be taken into account for internal + calculation of shadow colors. By specifying values greater 1.0 you + may lighten the shadows until the most intensive brightness is + reached, while values smaller than 1.0 will darken the shadows until + the respective colors are black. + + \sa shadowBrightnessFactor + */ + + +/** + \fn double KDChartParams::shadowBrightnessFactor() const + + Returns a factor to be used to adjust the + built-in brightness of shadow colors in + 3-dimensional drawings like e.g. 3D Bar charts. + + \return a factor that is to be taken into account for internal + calculation of shadow colors. + + \sa setShadowBrightnessFactor + */ + + +/** + \fn void KDChartParams::setShadowPattern( Qt::BrushStyle style ) + + Specifies a filling style for filling the shadow areas in + 3-dimensional drawings like 3D bar charts. The default is to + fill with a solid color, the color is determined with \a + setThreeDShadowColors. + + \param style the fill style to use + \sa shadowPattern(), setThreeDShadowColors(), + threeDShadowColors() + */ + + +/** + \fn Qt::BrushStyle KDChartParams::shadowPattern() const + + Returns the filling style used for filling the shadow areas in + 3-dimensional drawings like 3D bar charts. The default is to + fill with a solid color, the fill color can be queried with \a + threeDShadowColors(). + + \return the fill style used + \sa setShadowPattern(), setThreeDShadowColors(), + threeDShadowColors() + */ + +/** + \fn void KDChartParams::setOutlineDataColor( QColor color ) + + Specifies a color for the outlines of data displays. The default is + black. + + \note Use setOutlineDataLineStyle( Qt::NoPen ) to hide the line. + + \param color the color to use for the outlines + \sa outlineDataColor, setOutlineDataLineStyle, setOutlineDataLineWidth + */ + + +/** + \fn QColor KDChartParams::outlineDataColor() const + + Returns the color for the outlines of data displays. + + \return the outline color + \sa setOutlineDataColor + */ + + +/** + \fn void KDChartParams::setOutlineDataLineWidth( uint width ) + + Specifies the width of the outlines of data displays. The default is 1 + pixel. + + \note Use setOutlineDataLineStyle( Qt::NoPen ) to hide the line. + + \param width the line width to use for the outlines + \sa outlineDataLineWidth, setOutlineDataLineStyle, setOutlineDataColor + */ + + +/** + \fn uint KDChartParams::outlineDataLineWidth() const + + Returns the width of the outlines of data displays. + + \param style the line width that is used for outlines + \sa setOutlineDataLineWidth + */ + + +/** + \fn void KDChartParams::setOutlineDataLineStyle( PenStyle style ) + + Specifies the line style of the outlines of data displays. The default + is a solid line. Warning: On Windows 95/98, the style setting (other + than NoPen and SolidLine) has no effect for lines with width greater + than 1, due to a bug in the operating system. + + \param width the line style to use for the outlines + \sa outlineDataLineStyle, setOutlineDataColor, setOutlineDataLineWidth + */ + + +/** + \fn PenStyle KDChartParams::outlineDataLineStyle() const + + Returns the style of the outlines of data displays. + + \param style the line style that is used for outlines + \sa setOutlineDataLineStyle + */ + + +/** + \fn uint KDChartParams::maxDataColor() const + + Returns the number of the highest dataset for which a color has been + defined. + + \return the number of the highest dataset for which a color has been + defined. + \sa setDataColor, dataColor + */ + + +/** + \fn void KDChartParams::setThreeDShadowColors( bool shadow ) + + Specifies whether the engine should draw the sides and tops of + 3D effects in shadowed versions of the data colors (the + default) or in the data colors themselves. Only used with 3D + effects in charts that support these. + + \param shadow true for shadowed colors, false for original colors + \sa threeDShadowColors + */ + + +/** + \fn bool KDChartParams::threeDShadowColors() const + + Returns whether the engine should draw the sides and tops of 3D effects in + shadowed versions of the data colors or in the data colors + themselves. Only used with 3D effects in charts that + support these. The default is true. + + \return true if shadowed colors are used for 3D effects + \sa setThreeDShadowColors + */ + + +/** + \fn void KDChartParams::setAllowOverlappingDataValueTexts( bool allow ) + + Specifies whether a data value text may be drawn even if it's + region intersects with another data value text's region. + + By default this is FALSE to prevent ugly mutual overwriting of + data value texts and to speed up drawing of cahrts containing + thousands of data points. + */ + + +/** + \fn bool KDChartParams::allowOverlappingDataValueTexts() const + + Returns whether a data value text may be drawn even if it's region + intersects with another data value text's region. + + By default this is FALSE to prevent ugly mutual overwriting of data + value texts and to speed up drawing of cahrts containing thousands + of data points. + */ + + +/** + Specifies how the data value text figures are modified before printing. + + This methode is provided for your convenience, to learn how to set the + other text parameters please have a look at setPrintDataValues. + + \sa printDataValuesWithDefaultFontParams, setPrintDataValues + \sa dataValuesColor + \sa setPrintDataValuesFontRelSize setPrintDataValuesBackground + */ +void KDChartParams::setDataValuesCalc( int divPow10, + int digitsBehindComma, + uint chart ) +{ + uint count = (KDCHART_ALL_CHARTS == chart) ? 2 : 1; + PrintDataValuesSettings * settings = (( 1 < count ) || ( 0 == chart )) + ? &_printDataValuesSettings + : &_printDataValuesSettings2; + for ( uint i = 0; i < count; ++i ) { + settings->_divPow10 = divPow10; + settings->_digitsBehindComma = digitsBehindComma; + if ( 0 < chart ) + settings = &_printDataValuesSettings2; + } + emit changed(); +} + +/** + Specifies the font to be used for printing the data value texts. + + This methode is provided for your convenience, to learn how to set the + other text parameters please have a look at setPrintDataValues. + + \sa printDataValuesWithDefaultFontParams, setPrintDataValues + \sa dataValuesColor + \sa setPrintDataValuesFontRelSize setPrintDataValuesBackground + */ +void KDChartParams::setDataValuesFont( QFont* font, + uint size, + uint chart ) +{ + uint count = (KDCHART_ALL_CHARTS == chart) ? 2 : 1; + PrintDataValuesSettings * settings = (( 1 < count ) || ( 0 == chart )) + ? &_printDataValuesSettings + : &_printDataValuesSettings2; + for ( uint i = 0; i < count; ++i ) { + settings->_useDefaultFontParams = false; + if( font ) + settings->_dataValuesFont = *font; + if( UINT_MAX != size ){ + settings->_dataValuesUseFontRelSize = ( 0 < size ); + settings->_dataValuesFontRelSize = size; + } + if ( 0 < chart ) + settings = &_printDataValuesSettings2; + } + emit changed(); +} + +/** + Specifies the position and rotation of the data value texts to be printed. + + This methode is provided for your convenience, to learn how to set the + other text parameters please have a look at setPrintDataValues. + + \sa printDataValuesWithDefaultFontParams, setPrintDataValues + \sa dataValuesColor + \sa setPrintDataValuesFontRelSize setPrintDataValuesBackground + */ +void KDChartParams::setDataValuesPlacing( KDChartEnums::PositionFlag position, + uint align, + int deltaX, + int deltaY, + int rotation, + bool specifyingPositiveValues, + uint chart ) +{ + uint count = (KDCHART_ALL_CHARTS == chart) ? 2 : 1; + PrintDataValuesSettings * settings = (( 1 < count ) || ( 0 == chart )) + ? &_printDataValuesSettings + : &_printDataValuesSettings2; + for ( uint i = 0; i < count; ++i ) { + if( specifyingPositiveValues ){ + // for values greater/equal zero: + settings->_dataValuesAnchorPositivePosition = position; + settings->_dataValuesAnchorPositiveAlign = align; + settings->_dataValuesAnchorPositiveDeltaX = deltaX; + settings->_dataValuesAnchorPositiveDeltaY = deltaY; + settings->_dataValuesPositiveRotation = rotation; + }else{ + // for values below zero: + settings->_dataValuesAnchorNegativePosition = position; + settings->_dataValuesAnchorNegativeAlign = align; + settings->_dataValuesAnchorNegativeDeltaX = deltaX; + settings->_dataValuesAnchorNegativeDeltaY = deltaY; + settings->_dataValuesNegativeRotation = rotation; + } + if ( 0 < chart ) + settings = &_printDataValuesSettings2; + } + emit changed(); +} + +/** + Specifies the text color and background brush to be used for + printing the data value texts. + + To have the color calculated automatically - useful when printing + inside the bars (or pie slices, areas, ... resp.) - please use + \c KDCHART_DATA_VALUE_AUTO_COLOR instead of a QColor*. + + Setting the background is normally not needed since reasonable + settings are done by default: Area charts have a white background + behind the data value texts while all other chart types use no + special background (the value is Qt::NoBrush then). + + In case you want to use it to remove the extra background from + your Area chart's data value texts you should consider changing + the dataset colors as well: an idea might be trying the subdued + colorset by calling \c setSubduedColors(). + + \sa printDataValuesWithDefaultFontParams + \sa setPrintDataValues + \sa dataValuesColor + \sa setPrintDataValuesFontRelSize + */ +void KDChartParams::setDataValuesColors( const QColor* color, + const QBrush& background, + uint chart ) +{ + // first store the color + if( color ) + setPrintDataValuesColor( chart, color ); + // now store the background + uint count = (KDCHART_ALL_CHARTS == chart) ? 2 : 1; + PrintDataValuesSettings * settings = (( 1 < count ) || ( 0 == chart )) + ? &_printDataValuesSettings + : &_printDataValuesSettings2; + for ( uint i = 0; i < count; ++i ) { + settings->_dataValuesBrush = background; + if ( 0 < chart ) + settings = &_printDataValuesSettings2; + } + emit changed(); +} + + +/* sorry, but policy handling is not implemnted yet: */ +void KDChartParams::setDataValuesPolicy( + KDChartEnums::TextLayoutPolicy policy, + uint chart ) +{ + uint count = (KDCHART_ALL_CHARTS == chart) ? 2 : 1; + PrintDataValuesSettings * settings = (( 1 < count ) || ( 0 == chart )) + ? &_printDataValuesSettings + : &_printDataValuesSettings2; + for ( uint i = 0; i < count; ++i ) { + settings->_dataValuesLayoutPolicy = policy; + if ( 0 < chart ) + settings = &_printDataValuesSettings2; + } + emit changed(); +} + + +/** + Specifies whether data value texts should be printed even if the + value is KDCHART_POS_INFINITE (or KDCHART_NEG_INFINITE). + + Printing of an infinite symbol (lemniskate) is done by default, + ou may use this function to disable it. + + \sa printDataValuesWithDefaultFontParams + \sa setPrintDataValues + \sa dataValuesColor + \sa setPrintDataValuesFontRelSize + */ +void KDChartParams::setDataValuesShowInfinite( bool dataValuesShowInfinite, + uint chart) +{ + uint count = (KDCHART_ALL_CHARTS == chart) ? 2 : 1; + PrintDataValuesSettings * settings = (( 1 < count ) || ( 0 == chart )) + ? &_printDataValuesSettings + : &_printDataValuesSettings2; + for ( uint i = 0; i < count; ++i ) { + settings->_dataValuesShowInfinite = dataValuesShowInfinite; + if ( 0 < chart ) + settings = &_printDataValuesSettings2; + } + emit changed(); +} + + +/* function only there for backward compatibility */ +void KDChartParams::setPrintDataValuesColor( uint chart, const QColor* color ) +{ + uint count = (KDCHART_ALL_CHARTS == chart) ? 2 : 1; + PrintDataValuesSettings * settings = (( 1 < count ) || ( 0 == chart )) + ? &_printDataValuesSettings + : &_printDataValuesSettings2; + for ( uint i = 0; i < count; ++i ) { + if ( KDCHART_DATA_VALUE_AUTO_COLOR == color ) { + settings->_dataValuesAutoColor = true; // !!! + settings->_dataValuesColor = QColor( Qt::black ); + } + else { + settings->_dataValuesAutoColor = false; + if ( 0 == color ) + settings->_dataValuesColor + = QColor( i ? Qt::darkBlue : Qt::black ); + else + settings->_dataValuesColor = *color; + } + if ( 0 < chart ) + settings = &_printDataValuesSettings2; + } + emit changed(); +} + + +/** + Specifies the dynamic font size to be used for printing the data + value texts. To change settings for all charts specify \c + KDCHART_ALL_CHARTS as \chart parameter. + + This methode is provided for your convenience, to learn how to set the + other text parameters please have a look at setPrintDataValues. + + \sa printDataValuesWithDefaultFontParams, setPrintDataValues + \sa setPrintdataValuesColor, dataValuesFontRelSize + */ +void KDChartParams::setPrintDataValuesFontRelSize( uint chart, uint size ) +{ + uint count = (KDCHART_ALL_CHARTS == chart) ? 2 : 1; + PrintDataValuesSettings * settings = (( 1 < count ) || ( 0 == chart )) + ? &_printDataValuesSettings + : &_printDataValuesSettings2; + uint theSize( UINT_MAX == size ? 16 : size ); + for ( uint i = 0; i < count; ++i ) { + settings->_dataValuesUseFontRelSize = ( 0 < theSize ); + settings->_dataValuesFontRelSize = theSize; + if ( 0 < chart ) + settings = &_printDataValuesSettings2; + } + emit changed(); +} + + +/** + Specifies that data value texts are to be printed with default font + parameters. Calling this methode results in resetting the + respective font size and colour and position parameters but not the + QFont itself. By setting \c callSetPrintDataValues to true you + select general enabling of text printing, when doing so it is + <b>not</b> neccessary to call setPrintDataValues() after calling + setPrintDataValuesWithDefaultFontParams(). + + \note If you want to call setChartType() after calling + setPrintDataValuesWithDefaultFontParams() you should set the \c + callSetPrintDataValues parameter to false to prevent + setPrintDataValues() from being called twice since it is called + internally each time you call setChartType() <b>if</b> the default + font params are to be set. + + \sa printDataValuesWithDefaultFontParams, setPrintDataValues + */ +void KDChartParams::setPrintDataValuesWithDefaultFontParams( uint chart, + bool callSetPrintDataValues ) +{ + uint count = (KDCHART_ALL_CHARTS == chart) ? 2 : 1; + PrintDataValuesSettings * settings = (( 1 < count ) || ( 0 == chart )) + ? &_printDataValuesSettings + : &_printDataValuesSettings2; + for ( uint i = 0; i < count; ++i ) { + settings->_printDataValues = true; + settings->_useDefaultFontParams = true; + if ( 0 < chart ) + settings = &_printDataValuesSettings2; + } + if ( callSetPrintDataValues ) + setPrintDataValues( true, chart ); +} + + +/** + \fn bool KDChartParams::printDataValuesWithDefaultFontParams( uint chart ) const + + Retrieves whether data value texts are to be printed with + non-default font parameters or not - <b>when</b> text printing is + active. + + \note You might also want to call printDataValues to see if text + printing is active since this default flag remains set in + background even when printing has been de-activated. + + \sa setPrintDataValuesWithDefaultFontParams, printDataValues + */ + + +/** + \fn bool KDChartParams::printDataValues( uint chart ) const + + Returns whether the data values will be printed near their + respective entries. + + \param chart The number of the chart: 0 for the first chart, 1 for + the second chart in case there are two charts to be drawn sharing the + same data area. + + \return whether the data values will be printed near their + respective entries. + + \sa setPrintDataValues + */ + + +/** + \fn QFont KDChartParams::dataValuesFont( uint chart ) const + + Returns the font to be used for printing the data values + + \param chart The number of the chart: 0 for the first chart, 1 for + the second chart in case there are two charts to be drawn sharing the + same data area. + + \returns the font to be used for printing the data values + + \sa setPrintDataValues + */ + + +/** + \fn bool KDChartParams::dataValuesUseFontRelSize( uint chart ) const + + Returns whether the font size to be used for printing the + data values is calculated dynamically. + + \param chart The number of the chart: 0 for the first chart, 1 for + the second chart in case there are two charts to be drawn sharing the + same data area. + + \return whether the font size to be used for printing the + data values is calculated dynamically. + + \sa setPrintDataValues, setPrintDataValuesFontRelSize + */ + + +/** + \fn int KDChartParams::dataValuesFontRelSize( uint chart ) const + + Returns the relative size (in per mille of the chart width) + of font size to be used for printing the + data values. + + \param chart The number of the chart: 0 for the first chart, 1 for + the second chart in case there are two charts to be drawn sharing the + same data area. + + \return the relative size (in per mille of the chart width) + of font size to be used for printing the + data values. + + \sa setPrintDataValues, setPrintDataValuesFontRelSize + */ + + +/** + \fn QColor KDChartParams::dataValuesColor( uint chart ) const + + Returns the colour of the font to be used for printing the + data values. + + \param chart The number of the chart: 0 for the first chart, 1 for + the second chart in case there are two charts to be drawn sharing the + same data area. + + \return the colour of the font to be used for printing the + data values. + + \sa setPrintDataValues, setPrintDataValuesColor + */ + + +/** + \fn bool KDChartParams::dataValuesAutoColor( uint chart ) const + + Returns whether the font to be used for printing the data values + texts shall have automatically calculated colors fitting to their + respectivs data representations. + + \param chart The number of the chart: 0 for the first chart, 1 for + the second chart in case there are two charts to be drawn sharing the + same data area. + + \return whether the font to be used for printing the data values texts + shall have automatically calculated colors fitting to their respectivs data representations. + + \sa setPrintDataValues + */ + + +/** + Returns the anchor position which the text is to be aligned to. + + \param chart The number of the chart: 0 for the first chart, 1 for + the second chart in case there are two charts to be drawn sharing the + same data area. + + \param negative If true the return value is only valid for data + values less than zero, if false it applies to data values greater + or equal to zero. + + \returns the anchor position which the text is to be aligned to in case of + the value being less than zero. + + \sa setPrintDataValues + */ +KDChartEnums::PositionFlag KDChartParams::dataValuesAnchorPosition( uint chart, bool negative ) const +{ + if ( negative ) + return chart ? _printDataValuesSettings2._dataValuesAnchorNegativePosition + : _printDataValuesSettings._dataValuesAnchorNegativePosition; + else + return chart ? _printDataValuesSettings2._dataValuesAnchorPositivePosition + : _printDataValuesSettings._dataValuesAnchorPositivePosition; +} + + +/** + Returns the way how the text is to be aligned to the anchor. + + This must be a reasonable combination of Qt::AlignmentFlags. + + \param chart The number of the chart: 0 for the first chart, 1 for + the second chart in case there are two charts to be drawn sharing the + same data area. + + \param negative If true the return value is only valid for data + values less than zero, if false it applies to data values greater + or equal to zero. + + \returns the way how the text is to be aligned to the anchor in case of + the value being less than zero. + + \sa setPrintDataValues + */ +uint KDChartParams::dataValuesAnchorAlign( uint chart, bool negative ) const +{ + if ( negative ) + return chart ? _printDataValuesSettings2._dataValuesAnchorNegativeAlign + : _printDataValuesSettings._dataValuesAnchorNegativeAlign; + else + return chart ? _printDataValuesSettings2._dataValuesAnchorPositiveAlign + : _printDataValuesSettings._dataValuesAnchorPositiveAlign; +} + + +/** + Returns the X distance between the text and its anchor. + + \note For better compatibility to the dynamic font size, + this parameter is <b>always</b> interpreted as being a per-mille + value of the logical width of the drawing area. If greater 0, the X + position is increased, if less than 0, it is reduced, this is + calculated dynamically before painting. + + \param chart The number of the chart: 0 for the first chart, 1 for + the second chart in case there are two charts to be drawn sharing the + same data area. + + \param negative If true the return value is only valid for data + values less than zero, if false it applies to data values greater + or equal to zero. + + \returns the X distance between the text and its anchor. + + \sa setPrintDataValues + */ +int KDChartParams::dataValuesAnchorDeltaX( uint chart, bool negative ) const +{ + if ( negative ) + return chart ? _printDataValuesSettings2._dataValuesAnchorNegativeDeltaX + : _printDataValuesSettings._dataValuesAnchorNegativeDeltaX; + else + return chart ? _printDataValuesSettings2._dataValuesAnchorPositiveDeltaX + : _printDataValuesSettings._dataValuesAnchorPositiveDeltaX; +} + + +/** + Returns the Y distance between the text and its anchor. + + \note For better compatibility to the dynamic font size this + parameter is <b>always</b> interpreted as being a per-mille value + of the logical width of the drawing area. If greater 0, the Y + position is increased, if less than 0, it is reduced, this is + calculated dynamically before painting. + + \param chart The number of the chart: 0 for the first chart, 1 for + the second chart in case there are two charts to be drawn sharing the + same data area. + + \param negative If true the return value is only valid for data + values less than zero, if false it applies to data values greater + or equal to zero. + + \returns the Y distance between the text and its anchor. + + \sa setPrintDataValues + */ +int KDChartParams::dataValuesAnchorDeltaY( uint chart, bool negative ) const +{ + if ( negative ) + return chart ? _printDataValuesSettings2._dataValuesAnchorNegativeDeltaY + : _printDataValuesSettings._dataValuesAnchorNegativeDeltaY; + else + return chart ? _printDataValuesSettings2._dataValuesAnchorPositiveDeltaY + : _printDataValuesSettings._dataValuesAnchorPositiveDeltaY; +} + + +/** + Returns the amount of degrees (using a circle of 360 degrees) taken to + rotate the text. Positive values rotate clockwise, negative values rotate counter-clockwise. + + \param chart The number of the chart: 0 for the first chart, 1 for + the second chart in case there are two charts to be drawn sharing the + same data area. + + \param negative If true the return value is only valid for data + values less than zero, if false it applies to data values greater + or equal to zero. + + \returns the amount of degrees (using a circle of 360 degrees) taken to + rotate the text. + + \sa setPrintDataValues + */ +int KDChartParams::dataValuesRotation( uint chart, bool negative ) const +{ + if ( negative ) + return chart ? _printDataValuesSettings2._dataValuesNegativeRotation + : _printDataValuesSettings._dataValuesNegativeRotation; + else + return chart ? _printDataValuesSettings2._dataValuesPositiveRotation + : _printDataValuesSettings._dataValuesPositiveRotation; +} + + +/** + \fn KDChartEnums::TextLayoutPolicy KDChartParams::dataValuesLayoutPolicy( uint chart ) const + + Returns the way to handle too narrow space conflicts: what to do if + a data text covers a neighboring data text (or a neighboring data + area, resp.). + + \note A layout policy different from LayoutJustOverwrite from does + not mean that this policy is followed in any case. Rather than + giving up when the selected policy does not result in a good layout + the program will automatically try the next policy: if + LayoutPolicyRotate did not succeed LayoutPolicyShiftVertically will + be tried, if this did not succeed either + LayoutPolicyShiftHorizontally will be tried. + + \param chart The number of the chart: 0 for the first chart, 1 for + the second chart in case there are two charts to be drawn sharing the + same data area. + + \returns the way to handle too narrow space conflicts. + + \sa setPrintDataValues + */ + +/** + \fn void KDChartParams::changed() + + This signal is emitted when any of the chart parameters has changed. + */ + +/** + \var const uint KDCHART_MAX_AXES + + Charts have up to 12 axes: + up to two axes on both sides for X, Y + and Z (if data is 3-dimensional). This constant is located here and + in KDChartAxisParams, because one KDChartAxisParams object + describes only one axis. + */ + +/** + \var static const int KDCHART_PROPSET_NORMAL_DATA; + + Default (built-in) property ID, used for data cells + without any special properties. + + Use this IDs <b>without</b> previously defining a respective + set of properties via KDChartParams::registerProperties(...). + + \note While (in theory) it <b>is</b> possible to assign special + property values to this build-in property set this normally might not + a very good idea since these values would apply to every(!) data cell + that has no other property set assigned. Such general changes + would rather be done calling KDChartParam's main functions, e.g. you + would invoke KDChartParams::setLineMarker(true) instead of changing + the normal-data property set. + + \sa KDCHART_PROPSET_TRANSPARENT_DATA + \sa KDCHART_PROPSET_HORI_LINE, KDCHART_PROPSET_VERT_LINE + \sa KDChartData::setPropertySet + \sa KDChartParams::registerProperties, KDChartParams::setProperties, KDChartParams::properties + */ + +/** + \var static const int KDCHART_PROPSET_TRANSPARENT_DATA; + + Predefined (build-in) property ID. used to specify a cell that should be displayed + using a null pen: neither the data representation nor + the connecting line to the next point will be shown, + but the line will be drawn using line style Qt::NoPen. + + Use this IDs <b>without</b> previously defining a respective + set of properties via KDChartParams::registerProperties(...). + + \note: Assigning this property to a data cell will result in the + cell being invisible: there will be no marker for this point and + there will be a 'gap' in this dataset's line from the point to the next one. + In case you want to change this default behavior, + use KDChartParams::setProperties(), + e.g. the following piece of code still has an invisible line while now + enabeling showing of the marker - but only if it was set active by + KDChartParams::setLineMarker(true). + + \verbatim + KDChartPropertySet transpProps; + bool bDummy; + if( properties(KDCHART_PROPSET_TRANSPARENT_DATA, + transpProps) ){ +// build-in property was found, let's modify it a bit: +transpProps.setShowMarker( +KDCHART_PROPSET_NORMAL_DATA, bDummy ); +setProperties(KDCHART_PROPSET_TRANSPARENT_DATA, +transpProps); +}else{ +// Ooops? The build-in property was NOT found! +// OK, never mind, let's specify it from scratch: +transpProps.setName("transparent data"); +transpProps.setLineStyle(KDChartPropertySet::OwnID, Qt::NoPen); +transpProps.setShowMarker( +KDCHART_PROPSET_NORMAL_DATA, bDummy); +setProperties(KDCHART_PROPSET_TRANSPARENT_DATA, +transpProps); +} +\endverbatim + +\note The example above uses setProperties (in the last line) to +define a property set. This is the right way for build-in property sets, +but it is not recomended for your additional user-defined property sets: +these should be registered with the registerProperties function +to initially obtain a unique ID number for your new property set, +see the second example given with \c KDCHART_PROPSET_VERT_LINE. + +\sa KDCHART_PROPSET_NORMAL_DATA +\sa KDCHART_PROPSET_HORI_LINE, KDCHART_PROPSET_VERT_LINE +\sa KDChartData::setPropertySet +\sa KDChartParams::registerProperties, KDChartParams::setProperties, KDChartParams::properties +*/ + +/** + \var static const int KDCHART_PROPSET_HORI_LINE; + + Predefined (build-in) property ID. may be used to specify a special cell + which is not part of the normal data but to be used for positioning + a separate horizontal line. + Data display will be surpressed for this point: neither the data + representation nor the connecting line to the next point will + be shown, actually the point will remain invisible but a horizontal + line will be drawn instead: using line style Qt::DotLine and the + respective dataset's line color. + + Use this IDs <b>without</b> previously defining a respective + set of properties via KDChartParams::registerProperties(...). + + In case you want to change this default behavior, + use KDChartParams::setProperties(), + e.g. the following piece of code still has an invisible line and + also does not show the point's marker while now showing two + separate markers at the horizontal line's start and end. + + \verbatim + KDChartPropertySet horiProps; + if( properties(KDCHART_PROPSET_HORI_LINE, + horiProps) ){ +// build-in property was found, let's modify it a bit: +horiProps.setExtraMarkersAlign( +KDChartPropertySet::OwnID, +Qt::AlignLeft | Qt::AlignRight ); +setProperties(KDCHART_PROPSET_HORI_LINE, +horiProps); +}else{ +// Ooops? The build-in property was NOT found! +// OK, never mind, let's specify it from scratch: +horiProps.setName("horizontal line"); +horiProps.setLineStyle(KDChartPropertySet::OwnID, Qt::NoPen); +horiProps.setShowMarker( KDChartPropertySet::OwnID, false ); +horiProps.setExtraMarkersAlign( KDChartPropertySet::OwnID, +Qt::AlignLeft | Qt::AlignRight ); +horiProps.setExtraMarkersAlign( +KDChartPropertySet::OwnID, +Qt::AlignLeft | Qt::AlignRight ); +setProperties(KDCHART_PROPSET_HORI_LINE, +horiProps); +} +\endverbatim + +\note The example above uses setProperties (in the last line) to +define a property set. This is the right way for build-in property sets, +but it is not recomended for your additional user-defined property sets: +these should be registered with the registerProperties function +to initially obtain a unique ID number for your new property set, +see the second example given with \c KDCHART_PROPSET_VERT_LINE. + +\sa KDCHART_PROPSET_NORMAL_DATA +\sa KDCHART_PROPSET_VERT_LINE +\sa KDChartData::setPropertySet +\sa KDChartParams::registerProperties, KDChartParams::setProperties, KDChartParams::properties +*/ + +/** + \var static const int KDCHART_PROPSET_VERT_LINE; + + Predefined (build-in) property ID. may be used to specify a special cell + which is not part of the normal data but to be used for positioning + a separate vertical line. + Data display will be surpressed for this point: neither the data + representation nor the connecting line to the next point will + be shown, actually the point will remain invisible but a vertical + line will be drawn instead: using line style Qt::DotLine and the + respective dataset's line color. + + Use this IDs <b>without</b> previously defining a respective + set of properties via KDChartParams::registerProperties(...). + + In case you want to change this default behavior, + use KDChartParams::setProperties(), + e.g. the following piece of code still has an invisible line and + also does not show the point's marker while now showing a + separate marker at the top end of the vertical line which + will <b>not</b> run across the complete data area but start + on the respective point's Y position and go to the top edge + of the chart's area. + + \verbatim + KDChartPropertySet vertProps; + if( properties(KDCHART_PROPSET_VERT_LINE, + vertProps) ){ +// build-in property was found, let's modify it a bit: +vertProps.setExtraMarkersAlign( +KDChartPropertySet::OwnID, Qt::AlignTop ); +setProperties(KDCHART_PROPSET_VERT_LINE, +vertProps); +}else{ +// Ooops? The build-in property was NOT found! +// OK, never mind, let's specify it from scratch: +vertProps.setName("vertical line"); +vertProps.setLineStyle(KDChartPropertySet::OwnID, Qt::NoPen); +vertProps.setShowMarker( KDChartPropertySet::OwnID, false ); +vertProps.setExtraMarkersAlign( KDChartPropertySet::OwnID, +Qt::AlignTop ); +vertProps.setExtraMarkersAlign( +KDChartPropertySet::OwnID, +Qt::AlignTop ); +setProperties(KDCHART_PROPSET_VERT_LINE, +vertProps); +} +\endverbatim + +\note The example above uses setProperties (in the last line) to +define a property set. This is the right way for build-in property sets, +but it is not recomended for your additional user-defined property sets: +these should be registered with the registerProperties function +to initially obtain a unique ID number for your new property set, +see the following example: + +The code shown below demonstrates how to specify a <b>new</b> +property set which then can be used for adding a red vertical +line with marker to some of your normal data points: + +\verbatim +// 1. specify and register the new property set +KDChartPropertySet dataWithTopLineProps; +dataWithTopLineProps.setName( +"normal data with red vertical line on top"); +dataWithTopLineProps.setExtraLinesAlign( +KDChartPropertySet::OwnID, +Qt::AlignTop ); +dataWithTopLineProps.setExtraLinesWidth( +KDChartPropertySet::OwnID, +-2 ); +dataWithTopLineProps.setExtraLinesColor( +KDChartPropertySet::OwnID, + Qt::red ); +dataWithTopLineProps.setExtraLinesStyle( + KDChartPropertySet::OwnID, + Qt::SolidLine ); +dataWithTopLineProps.setExtraMarkersAlign( + KDChartPropertySet::OwnID, + Qt::AlignTop ); +dataWithTopLineProps.setExtraMarkersSize( + KDChartPropertySet::OwnID, + QSize(-10, -10) ); +dataWithTopLineProps.setExtraMarkersColor( + KDChartPropertySet::OwnID, + Qt::darkRed ); +dataWithTopLineProps.setExtraMarkersStyle( + KDChartPropertySet::OwnID, + KDChartParams::LineMarkerCircle ); +int idDataWithTopLineProps += registerProperties( dataWithTopLineProps ); +// 2. assign this property set +// to some of your data cells +d = new KDChartTableData( 5, 10 ); +// ... +d->cell( 0, 2 ).setPropertySet( idDataWithTopLineProps ); +d->cell( 2, 5 ).setPropertySet( idDataWithTopLineProps ); +\endverbatim + +\sa KDCHART_PROPSET_NORMAL_DATA +\sa KDCHART_PROPSET_HORI_LINE +\sa KDChartData::setPropertySet +\sa KDChartParams::registerProperties, KDChartParams::setProperties, KDChartParams::properties +*/ + + + +//@} +// END GENERAL + +// START BARCHART +/** @name Bar chart-specific methods. + + These methods query and set barchart-specific parameters. + */ +//@{ + +/** + \enum KDChartParams::BarChartSubType + + The bar subtype. Only used when chartType() == Bar + + \sa setBarChartSubType, barChartSubType + */ + + +/** + \fn void KDChartParams::setBarChartSubType( BarChartSubType barChartSubType ) + Specifies the bar chart subtype. + + This setting only has an effect in bar charts. + + \param barChartSubType the bar chart subtype + \sa barChartSubType, BarChartSubType, setChartType, chartType + */ + + +/** + \fn KDChartParams::BarChartSubType KDChartParams::barChartSubType() const + + Returns the bar chart subtype. + + This setting only has an effect in bar charts. + + \return the bar chart sub type + \sa setBarChartSubType, BarChartSubType, setChartType, chartType + */ + + + +/** + \fn void KDChartParams::setThreeDBars( bool threeDBars ) + + Specifies whether the engine should draw the bars in 3D. + + This setting only has an effect in bar charts. + + \param threeDBars true if bars should be drawn with a 3D effect + \sa threeDBars, setThreeDBarAngle, threeDBarAngle + */ + + +/** + \fn bool KDChartParams::threeDBars() const + + Returns whether the engine should draw any bars in 3D. + + This setting only has an effect in bar charts. + + \return true if bars should be draws with a 3D effect, false + otherwise + \sa setThreeDBars, threeDBarAngle, setThreeDBarAngle + */ + +/** + \fn void KDChartParams::setThreeDBarsShadowColors( bool shadow ) + \obsolete + Specifies whether the engine should draw the sides and tops of 3D bars + in shadowed versions of the data colors or in the data colors + themselves. Only used if threeDBars() == true. The default is true. + + This setting only has an effect in bar charts. + + This method is obsolete; use setThreeDShadowColors instead + + \param shadow true for shadowed colors, false for original colors + \sa setThreeDShadowColors + */ + +/** + \fn bool KDChartParams::threeDBarsShadowColors() const + + \obsolete + Returns whether the engine should draw the sides and tops of 3D bars in + shadowed versions of the data colors or in the data colors + themselves. Only used if threeDBars() == true. The default is true. + + This setting only has an effect in bar charts. + + This method is obsolete; use threeDShadowColors instead + + \return true if bars use shadowed colors for 3D effects + \sa setThreeDBarsShadowColors + */ + + +/** + Specifies the angle used for 3D bars. Only used if threeDBars() == true. + + This setting only has an effect in bar charts. + + \param angle the angle in degrees. The default (and most useful + value) is 45. Angle can be between 0 and 90, all other values + are ignored. Values close to 0 or close to 90 may look + strange, depending on the resolution of the output device. + \sa setThreeDBars, threeDBars + \sa threeDBarAngle, cosThreeDBarAngle + \sa setThreeDBarDepth, threeDBarDepth + */ +void KDChartParams::setThreeDBarAngle( uint angle ) +{ + if ( angle > 90 ) /* since angle is an uint, we do not need to + test for < 0 */ + return ; + _threeDBarAngle = angle; + + // cache the cosine of this value + _cosThreeDBarAngle = cos( static_cast < double > ( _threeDBarAngle ) * M_PI / 180.0 ); + emit changed(); +} + + +/** + \fn uint KDChartParams::threeDBarAngle() const + + Returns the angle in degrees used for 3D bars. Only used if threeDBars() == true. + + This setting only has an effect in bar charts. + + \return the angle in degrees used for 3D bars, always between 0 and 90. + \sa setThreeDBars, threeDBars + \sa setThreeDBarAngle, cosThreeDBarAngle + \sa setThreeDBarDepth, threeDBarDepth + */ + + +/** + \fn double KDChartParams::cosThreeDBarAngle() const + + Returns the cosine in rad of the angle used for 3D bars. Only used + if threeDBars() == true. + + This setting only has an effect in bar charts. + + \return the cosine in rad of the angle used for 3D bars, always + between 0 and 90. \sa setThreeDBars, threeDBars \sa + setThreeDBarAngle, threeDBarAngle \sa setThreeDBarDepth, + threeDBarDepth + */ + + +/** + \fn void KDChartParams::setThreeDBarDepth( double depth ) + + Specifies the depth of the 3D Effect used for 3D bars. + Only used if threeDBars() == true. + + This setting only has an effect in bar charts. + + \param depth the depth of the 3D Effect in relation to + the bar width. The default (and most useful) value of + 1.0 may be used to specify bars with square basis. + \sa threeDBarDepth + \sa setThreeDBars, threeDBars + \sa setThreeDBarAngle, threeDBarAngle, cosThreeDBarAngle + */ + + +/** + \fn double KDChartParams::threeDBarDepth() const + + Returns the depth of the 3D Effect used for 3D bars. + Only used if threeDBars() == true. + + This setting only has an effect in bar charts. + + \return the depth of the 3D Effect in relation to the bar width. + \sa setThreeDBarDepth + \sa setThreeDBars, threeDBars + \sa setThreeDBarAngle, threeDBarAngle, cosThreeDBarAngle + */ + + +/** + \fn void KDChartParams::setDatasetGap( int gap ) + + Specifies the number of pixels between two dataset values. + In addition you might want to use \c setOutlineDataLineStyle( Qt::NoPen ) to hide the line. + + Also the method \c setValueBlockGap might be usefull, please read the information given there ... + + This setting only has an effect in bar charts: if there is more than one dataset shown by your chart. + + \note Use negative values for overlaps, use \c + setDatasetGapIsRelative to specify that the \c gap + value is a per mille value of the chart data area width. + + The default is 6 per mille of the data area of the chart. + + \param gap the number of pixels between two dataset values. + \sa setValueBlockGap + \sa datasetGap + \sa datasetGapIsRelative, setDatasetGapIsRelative + \sa setOutlineDataLineStyle + */ + + +/** + \fn int KDChartParams::datasetGap() const + + Returns the number of pixels between two dataset values. + + This setting only has an effect in bar charts. + + \note Negative values signify overlaps, use \c datasetGapIsRelative + to find out if the \datasetGap value is a per mille value of the + chart data area width. + + \return the number of pixels between two dataset values. + \sa setDatasetGap + \sa datasetGapIsRelative, setDatasetGapIsRelative + */ + + +/** + \fn void KDChartParams::setDatasetGapIsRelative( bool gapIsRelative ) + + Specifies if the value set by \c setDatasetGap is a + per mille value of the chart data area width. + + This setting only has an effect in bar charts. + + \param gapIsRelative specifies if the value set by \c setDatasetGap + is a per mille value of the chart data area width. + \sa datasetGapIsRelative, datasetGap, setDatasetGap + */ + + +/** + \fn bool KDChartParams::datasetGapIsRelative() const + + Returns if the value set by \c setDatasetGap + is a per mille value of the chart data area width. + + This setting only has an effect in bar charts. + + \return if the value set by \c setDatasetGap + is a per mille value of the chart data area width. + \sa setDatasetGap, setDatasetGapIsRelative, datasetGap, setDatasetGap + */ + + +/** + \fn void KDChartParams::setValueBlockGap( int gap ) + + Specifies the number of pixels between each value block. + If there is only one dataset shown this gap is appearing between each of the bars, otherwise it is appearing between each of the bar blocks. + In addition you might want to use \c setOutlineDataLineStyle( Qt::NoPen ) to hide the line. + + Also the method \c setDatasetGap might be usefull, please read the information given there ... + + This setting only has an effect in bar charts. + + Use negative values for overlaps (which might look strange), + use \c setValueBlockGapIsRelative to specify that the \c gap + value is a per mille value of the chart data area width. + + The default is 15 per mille of the data area of the chart. + + \note Specifying a value block gap width AND a bar width + may result in KD Chart not being able to display all bars + if the data area is not wide enough. To make your + application notice when this + happens you should consider connecting the + barsDisplayed() signal emitted by your KDChartWidget object + to an appropriate slot function. Additionally (or in case + you are not using the KDChartWidget class, resp.) + you may access the number of actually displayed/not displayed + bars by the KDChartParams functions numBarsDisplayed + and numBarsLeft, the return value of these functions + are not not defined if they are called before drawing is done. + + \param gap the number of pixels between each value block. + \sa setDatasetGap + \sa setBarWidth, valueBlockGap + \sa KDChartWidget::barsDisplayed, KDChart::barsDisplayed + \sa numBarsDisplayed, numBarsLeft + \sa valueBlockGapIsRelative, setValueBlockGapIsRelative + \sa setOutlineDataLineStyle + */ + + +/** + \fn int KDChartParams::valueBlockGap() const + + Returns the number of pixels between each value block. + + This setting only has an effect in bar charts. + + \note Negative values signify overlaps, use \c valueBlockGapIsRelative + to find out if the \valueBlockGap value is a per mille value of the + chart data area width. + + \return the number of pixels between each value block. + \sa valueBlockGap + \sa valueBlockGapIsRelative, setValueBlockGapIsRelative + */ + + +/** + \fn void KDChartParams::setBarWidth( int width ) + + Specifies the width of one bar: makes sure that all of + the bars have exactly the same width. + + This setting only has an effect in bar charts. + + \note Use negative values for to specify that the \c width + value is a per mille value of the chart data area width, + use no value or the default KDCHART_AUTO_SIZE + to (re)activate normal calculation mode making sure that all + bars fit into the available space but might not look so nice + if many bars are there. + + \note Specifying a bar width AND a value block gap width + may result in KD Chart not being able to display all bars + if the data area is not wide enough. To make your + application notice when this + happens you should consider connecting the + barsDisplayed() signal emitted by your KDChartWidget object + to an appropriate slot function. Additionally (or in case + you are not using the KDChartWidget class) + you may access the number of actually displayed/not displayed + bars by the KDChartParams functions numBarsDisplayed + and numBarsLeft, the return value of these functions + are not not defined if they are called before drawing is done. + + \param width the width of one bar. + \sa KDChartWidget::barsDisplayed, KDChart::barsDisplayed + \sa numBarsDisplayed, numBarsLeft + \sa setValueBlockGap + */ + +/** + \fn int KDChartParams::barWidth() const + + Returns the width of one bar as set by setBarWidth + or KDCHART_AUTO_SIZE if setBarWidth + was never called. + + This setting only has an effect in bar charts. + + \note Negative values signify that the bar width is + a per mille value of the chart data area width. + + \return the width of one bar as set by setBarWidth + or KDCHART_AUTO_SIZE if setBarWidth + was never called. + \sa setBarWidth + */ + +/** + \fn int KDChartParams::numBarsDisplayed() const + + Returns the number of bars that were displayed last + time your bar chart was drawn. + + This setting only has an effect in bar charts. + + This can be different from the number of bars you + actually wanted to display: by specifying both + the bar width and the value block gap width you + may controll your chart very well but take the risk + of not getting all bars drawn if there is not enough + horizontal space to display them with the given width + and gap width. + + To quickly determine if all bars were drawn just check + if the numBarsLeft() function returns a zero value. + + \return the number of bars that were drawn. + \sa KDChartWidget::barsDisplayed, KDChart::barsDisplayed + \sa numBarsLeft + \sa setBarWidth, setValueBlockGap + */ + +/** + \fn int KDChartParams::numBarsLeft() const + + Returns the number of bars that could NOT be painted + last time your bar chart was drawn. + + This setting only has an effect in bar charts. + + If this value is greater than zero, the bar chart + shows less bars than you + actually wanted to display: by specifying both + the bar width and the value block gap width you + may controll your chart very well but take the risk + of not getting all bars drawn if there is not enough + horizontal space to display them with the given width + and gap width. + + You may call the numBarsDisplayed() function to see + how many bars were drawn. + + \return the number of bars that could not be drawn. + \sa KDChartWidget::barsDisplayed, KDChart::barsDisplayed + \sa numBarsDisplayed + \sa setBarWidth, setValueBlockGap + */ + +/** + \fn void KDChartParams::setValueBlockGapIsRelative( bool gapIsRelative ) + + Specifies if the value set by \c setValueBlockGap is a + per mille value of the chart data area width. + + This setting only has an effect in bar charts. + + \param gapIsRelative specifies if the value set by \c setValueBlockGap + is a per mille value of the chart data area width. + \sa valueBlockGapIsRelative, valueBlockGap, setValueBlockGap + */ + + +/** + \fn bool KDChartParams::valueBlockGapIsRelative() const + + Returns if the value set by \c setValueBlockGap + is a per mille value of the chart data area width. + + This setting only has an effect in bar charts. + + \return if the value set by \c setValueBlockGap + is a per mille value of the chart data area width. + \sa setValueBlockGapIsRelative, setValueBlockGap, valueBlockGap + \sa setValueBlockGap + */ + + +/** + \fn void KDChartParams::setDrawSolidExcessArrows( bool solidArrows ) + + Specifies whether arrows showing excess values in bar charts should + be drawn solidly or split. + + This setting only has an effect in bar charts. + + If \a solidArrows is true, the bars with excess values (like + infinity, or any other value that exceeds the y-axis labelling) will + have an integrated arrow head. If \a solidArrows is false, they will + still have an integrated arrow head at a lower position, but also + two flat arrows on top of them to better indicate that there could + be a large gap. It is recommended to set this value to false (this + is also the default), as this makes the excess value situation much + more obvious. + + This setting only has an effect in bar charts. + + \param solidArrows whether excess arrows shown be drawn solidly or + split + \sa drawSolidExcessArrows +*/ + + +/** + \fn bool KDChartParams::drawSolidExcessArrows() const + + Returns whether arrows showing excess values in bar charts should + be drawn solidly or split. + + This setting only has an effect in bar charts. + + \return true if excess arrows are drawn solidly, false if they are + split + \sa setDrawSolidExcessArrows +*/ + + +//@} +// END BARCHART + +// START LINECHART +/** @name Line and area chart-specific methods. + + These methods query and set line and area chart-specific parameters. + */ +//@{ + +/** + \enum KDChartParams::LineChartSubType + + The line subtype. Only used when chartType == Line + + \sa setLineChartSubType, lineChartSubType + */ + + +/** + \fn void KDChartParams::setLineChartSubType( LineChartSubType lineChartSubType ) + + Specifies the line chart subtype. Only used if chartType() == + Line. The default is LineNormal. + + \param lineChartSubType the line chart subtype + \sa lineChartSubType, LineChartSubType, setChartType, chartType + */ + + +/** + \fn KDChartParams::LineChartSubType KDChartParams::lineChartSubType() const + + Returns the line chart subtype. Only used if chartType() == + Line. + + \return the line chart sub type + \sa setLineChartSubType, LineChartSubType, setChartType, chartType + */ + + + +/** + \fn void KDChartParams::setLineMarker( bool marker ) + + Specifies whether there should be a marker at each data + point. Only used if chartType() == Line and if threeDLines() == + false. The default is not to draw markers. + + \note Use the setLineMarkerStyle function to specify the shape + of the markers, use the setLineWidth function to set the + width of the lines connecting the markers (or to surpress + drawing of such lines, resp.) + + \param marker true if markers should be drawn + \sa setLineMarker, setLineMarkerSize + \sa setLineMarkerStyle, setLineColor, setLineWidth, setLineStyle + */ + + +/** + \fn bool KDChartParams::lineMarker() const + + Returns whether line markers should be drawn at each data + point. Only used if chartType() == Line and if threeDLines() == + false. + + \return true if markers should be drawn. + */ + + +/** + \enum KDChartParams::LineMarkerStyle + + The available line marker styles. + */ + + +/** + Specifies the line marker to be used for a dataset. Only used if + chartType() == Line and lineMarker() == true. If you specify a + marker for a dataset, but not for a dataset with a lower + value, then the marker for the dataset with the lower value + will be undefined unless it was previously defined. The default + is a circle for the first dataset, a square for the second, a + diamond for the third and undefined for all subsequent + datasets. + + \note Extra marker styles LineMarker1Pixel and LineMarker4Pixels + are intended for drawing charts representing large numbers + of data cells, when using these styles it might be a good + idea to set the line width to zero. This would surpress + drawing of the lines resulting in drawing the markers only. + + \param dataset the dataset for which to set the line marker + \param style the style to set for the specified dataset + \sa LineMarkerStyle, lineMarkerStyle, setLineMarkerSize + \sa setLineColor, setLineWidth, setLineStyle + */ +void KDChartParams::setLineMarkerStyle( uint dataset, LineMarkerStyle style ) +{ + _lineMarkerStyles[ dataset ] = style; + _maxDatasetLineMarkerStyle = QMAX( dataset, + _maxDatasetLineMarkerStyle ); + emit changed(); +} + +/** + Returns the marker to be used for a dataset. Only used if + chartType() == Line and lineMarker() == true. + + \param dataset the dataset for which to return the line marker + \return the line marker for the specified data set + \sa LineMarkerStyle, setLineMarkerStyle + */ +KDChartParams::LineMarkerStyle KDChartParams::lineMarkerStyle( uint dataset ) const +{ + if( _lineMarkerStyles.find( dataset ) != _lineMarkerStyles.end() ) + return _lineMarkerStyles[ dataset ]; + else + return LineMarkerCircle; // default +} + + + + +/** + \var KDChartParams::LineMarkerStyleMap; + + The type of the storage structure for line marker styles. You + should normally not need to use this. + */ + + +/** + Sets a whole map of line marker styles. You can use this for + assigning many line marker styles at the same time, but + typically it is better to set them individually with \a + setLineMarkerStyle(). + + \param map the map of styles + \sa lineMarkerStyles, setLineMarker, setLineMarkerStyle, setLineMarkerSize + \sa setLineColor, setLineWidth, setLineStyle + */ +void KDChartParams::setLineMarkerStyles( LineMarkerStyleMap map ) { + _lineMarkerStyles = map; + // update _maxDatasetLineMarkerStyle + uint maxDataset = 0; + for( LineMarkerStyleMap::ConstIterator it = _lineMarkerStyles.begin(); + it != _lineMarkerStyles.end(); ++it ) + maxDataset = QMAX( maxDataset, it.key() ); + _maxDatasetLineMarkerStyle = maxDataset; +} + + +/** + \fn KDChartParams::LineMarkerStyleMap KDChartParams::lineMarkerStyles() const + + Returns the whole map of line marker styles. You will typically + not need this. You can query individual line marker styles by + calling \a lineMarkerStyle(). + + \return the map of styles + \sa lineMarkerStyle, setLineMarkerStyles + */ + + +/** + \fn uint KDChartParams::maxDatasetLineMarkerStyle() const + + + Returns the highest dataset for which a line marker style has been + defined. Not all datasets with a lower number necessarily have + a defined line marker. + + \return the highest dataset with a defined line marker + \sa LineMarkerStyle, setLineMarkerStyle, lineMarkerStyle + */ + + +/** + \fn void KDChartParams::setLineMarkerSize( QSize size ) + + Specifies the sizes of line markers. Only used if chartType() == Line + and lineMarker() == true. The default is 6x6. + + Negative values of \c size.width() and/or \c size.height() will + be interpreted as per-mille value of the chart's drawing area, + the true marker width (or height, resp.) will then be calculated + dynamically depending on the actual chart size. + + \param size the size of the line marker in pixels + \sa lineMarkerSize, setLineMarker, setLineMarkerStyle + \sa setLineColor, setLineWidth, setLineStyle + */ + + +/** + \fn QSize KDChartParams::lineMarkerSize() const + + Returns the sizes of line markers. Only used if chartType() == Line and + lineMarker() == true. + + \return the size of the line marker in pixels + \sa setLineMarkerSize + */ + + +/** + \fn void KDChartParams::setLineWidth( int width ) + + Specifies the width for lines in line charts. Default is 1. + Only used if threeDLines() == false. + + If \c width is negative it is interpreted as per-mille + value of the chart's drawing area, the true line width + will then be calculated dynamically depending on the + actual chart size. + + \note Set the line width to zero to surpress drawing of the lines + but do not forget to call setLineMarker( true ) and specify an + appropriate marker style to have the data points drawn. + + \param width the new width + \sa lineWidth, setLineColor, setLineStyle + \sa setLineMarker, setLineMarkerSize, setLineMarkerStyle + */ + + +/** + \fn int KDChartParams::lineWidth() const + + Returns the line width of the lines in line charts. + + \return the line width of lines in line charts + \sa lineWidth + */ + + +/** + \fn void KDChartParams::setLineColor( QColor color = QColor() ) + + Specifies the color of the lines in line charts. By default + line chart lines are drawn in the color specified by the + respective setDataColor function, to reset your chart to + this default just call setLineColor() without specifying a + parameter. + + \note Setting the line color to a special value is usefull to + visualize gaps in a normally very narrow series of data values. + + e.g. You might want to specify setLineMarkerStyle( i, LineMarker4Pixels ) + or even setLineMarkerStyle( i, LineMarker1Pixel ) and have your + marker dots normally form a contiguous line without the need of + any connecting lines except at the places where missing data + values would interrupt the 'line'. Using setLineColor( Qt::darkGray) + could make your chart more readable in such cases. + + \param color of the lines in line charts + \sa lineColor, setLineWidth, setLineStyle + \sa setLineMarker, setLineMarkerSize, setLineMarkerStyle + */ + + +/** + \fn QColor KDChartParams::lineColor() const + + Returns the color of the lines in line charts. + + \return the color of the lines in line charts + \sa setLineColor + */ + + +/** + Specifies a line style to be used for line charts. + + The dataset parameter is optional, by default saying + KDCHART_GLOBAL_LINE_STYLE, you may use it to + set a dataset specific line style for one the lines. + + Global and dataset specific line styles may be used + simultaneously: If no dataset specific style is + defined the global style is used which by default + is Qt::SolidLine. + + Warning: On Windows 95/98, the style setting (other + than NoPen and SolidLine) has no effect for lines with width greater + than 1, due to a bug in the operating system. + + \param style the line style of the line + \param dataset the number of the dataset controlling the respective line + \sa lineStyle, setLineWidth, setLineColor + \sa setLineMarker, setLineMarkerSize, setLineMarkerStyle + */ +void KDChartParams::setLineStyle( Qt::PenStyle style, uint dataset ) +{ + if( KDCHART_GLOBAL_LINE_STYLE == dataset ) + _lineStyle = style; + else + _datasetLineStyles[ dataset ] = style; + emit changed(); +} + +/** + Returns the line style of one of the lines in a line chart + or the global line style if no style was specified for the + dataset or if the optional dataset parameter has its default + value KDCHART_GLOBAL_LINE_STYLE. + + Warning: On Windows 95/98, the style setting (other than NoPen and + SolidLine) has no effect for lines with width greater than 1, due + to a bug in the operating system. + + \param dataset the dataset for which to return the line style + \return the line style for the specified data set + \sa setLineStyle, setLineMarkerStyle + */ +Qt::PenStyle KDChartParams::lineStyle( uint dataset ) const +{ + if( KDCHART_GLOBAL_LINE_STYLE == dataset ) + // global line style + return _lineStyle; + else if( _datasetLineStyles.find( dataset ) == _datasetLineStyles.end() ) + return lineStyle(); + else + return _datasetLineStyles[ dataset ]; +} + + +/** + \fn void KDChartParams::setThreeDLines( bool threeD ) + + Specifies whether lines should be drawn three-dimensionally or + not. The default is to draw two-dimensionally. Only used if + chartType() == Line. + + \param threeD if true, draw three-dimensionally, otherwise draw + two-dimensionally. + \sa threeDLines(), setThreeDLineDepth(), threeDLineDepth(), +// setThreeDLineXRotation(), threeDLineXRotation(), +// setThreeDLineYRotation(), threeDLineYRotation() +*/ + + +/** + \fn bool KDChartParams::threeDLines() const + + Returns whether lines are drawn three-dimensionally or not. The + default is to draw two-dimensionally. Only used if chartType() == + Line. + + \return true if lines are drawn three-dimensionally, false + otherwise. + \sa setThreeDLines(), setThreeDLineDepth(), threeDLineDepth(), +// setThreeDLineXRotation(), threeDLineXRotation(), +// setThreeDLineYRotation(), threeDLineYRotation() +*/ + + +/** + \fn void KDChartParams::setThreeDLineDepth( int depth ) + + Specifies the depth of 3D lines (the "width" in Z + direction). Only used if chartType() == Line and threeDLines() == + true. The default is 20 pixels. + + \param depth the depth in pixels + \sa setThreeDLines(), threeDLines(), threeDLineDepth() + */ + + +/** + \fn int KDChartParams::threeDLineDepth() const + + Returns the depth of 3D lines (the "width" in Z direction). Only + used if chartType() == Line and threeDLines() == true. The + default is 20 pixels. + + \return the depth in pixels + \sa setThreeDLines(), threeDLines(), setThreeDLineDepth() + */ + + +/** + \fn void KDChartParams::setThreeDLineXRotation( int degrees ) + + \deprecated Feature scheduled for future release, at present not implemented. + */ +/* + \fn void KDChartParams::setThreeDLineXRotation( int degrees ) + + Specifies the rotation around the X axis in degrees. The value + may be between 0 and 90. Only used if chartType() == Line and + threeDLines() == true. The default is 30 degrees. If 0 degrees is + specified for both the X and the Y rotation, the lines will look + like 2D lines. + + \param rotation the rotation in degrees. Must be between 0 and + 90. + \sa setThreeDLines(), threeDLines(), threeDLineXRotation() + */ + + + +/** + \fn int KDChartParams::threeDLineXRotation() const + + \deprecated Feature scheduled for future release, at present not implemented. + */ +/* + \fn int KDChartParams::threeDLineXRotation() const + + Returns the rotation around the X axis in degrees. The value may + be between 0 and 90. Only used if chartType() == Line and + threeDLines() == true. The default is 30 degrees. + + \return the rotation in degrees. Is always between 0 and 90. + */ + + +/** + \fn void KDChartParams::setThreeDLineYRotation( int degrees ) + + \deprecated Feature scheduled for future release, at present not implemented. + */ +/* + \fn void KDChartParams::setThreeDLineYRotation( int degrees ) + + Specifies the rotation around the Y axis in degrees. The value + may be between 0 and 90. Only used if chartType() == Line and + threeDLines() == true. The default is 30 degrees. If 0 degrees is + specified for both the X and the Y rotation, the lines will look + like 2D lines. + + \param rotation the rotation in degrees. Must be between 0 and + 90. + \sa setThreeDLines(), threeDLines(), threeDLineYRotation() + */ + +/** + \fn int KDChartParams::threeDLineYRotation() const + + \deprecated Feature scheduled for future release, at present not implemented. + */ +/* + \fn int KDChartParams::threeDLineYRotation() const + + Returns the rotation around the X axis in degrees. The value may + be between 0 and 90. Only used if chartType() == Line and + threeDLines() == true. The default is 30 degrees. + + \return the rotation in degrees. Is always between 0 and 90. + */ + + +/** + \enum KDChartParams::AreaChartSubType + + The area subtype. Only used when chartType == Area + + \sa setAreaChartSubType, areaChartSubType + */ + + +/** + \fn void KDChartParams::setAreaChartSubType( AreaChartSubType areaChartSubType ) + + Specifies the area chart subtype. Only used if chartType() == + Area. The default is AreaNormal. + + \param areaChartSubType the area chart subtype + \sa areaChartSubType, AreaChartSubType, setChartType, chartType + */ + + +/** + \fn KDChartParams::AreaChartSubType KDChartParams::areaChartSubType() const + + + Returns the area chart subtype. Only used if chartType() == + Area. + + \return the area chart sub type + \sa setAreaChartSubType, AreaChartSubType, setChartType, chartType + */ + + +/** + \enum KDChartParams::AreaLocation + + Specifies whether the area above the value points or below the + value points should be filled. The default is to fill below the + value points. + + \sa setAreaLocation(), areaLocation() + */ + + +/** + \fn void KDChartParams::setAreaLocation( AreaLocation location ) + + Specifies whether the area above or below the value points + should be filled. The default is to fill below the value + points. + + This setting is <em>not</em> used with percent areas + (i.e. areaChartSubType() == AreaPercent), because these always + cover the whole chart. + + \param location whether to fill above or below the value points + \sa AreaLocation, areaLocation() + */ + + +/** + \fn KDChartParams::AreaLocation KDChartParams::areaLocation() const + + Returns whether the area above or below the value points is + filled. + + \return whether the area above or below the value points is + filled. + \sa AreaLocation, setAreaLocation() + */ + + +//@} +// END LINECHART + +// START POLARCHART +/** @name Polar chart-specific methods. + + These methods query and set polar chart-specific parameters. + */ +//@{ + + +/** + \enum KDChartParams::PolarChartSubType + + The polar subtype. Only used when chartType == Polar + + \sa setPolarChartSubType, polarChartSubType + */ + + +/** + \fn void KDChartParams::setPolarChartSubType( PolarChartSubType polarChartSubType ) + + Specifies the polar chart subtype. Only used if chartType() == + Polar. The default is PolarNormal. + + \param polarChartSubType the polar chart subtype + \sa polarChartSubType, PolarChartSubType, setChartType, chartType + */ + + +/** + \fn KDChartParams::PolarChartSubType KDChartParams::polarChartSubType() const + + Returns the polar chart subtype. Only used if chartType() == + Polar. + + \return the polar chart sub type + \sa setPolarChartSubType, PolarChartSubType, setChartType, chartType + */ + + +/** + \fn void KDChartParams::setPolarMarker( bool marker ) + + Specifies whether there should be a marker at each data + point. Only used if chartType() == Polar. The default is not to + draw markers. + + \param marker true if markers should be drawn + */ + + +/** + \fn bool KDChartParams::polarMarker() const + + Returns whether polar markers should be drawn at each data + point. Only used if chartType() == Polar. + + \return true if markers should be drawn. + */ + + +/** + \enum KDChartParams::PolarMarkerStyle + + The available polar marker styles. + */ + + +/** + Specifies the polar marker to be used for a dataset. Only used if + chartType() == Polar and polarMarker() == true. If you specify a + marker for a dataset, but not for a dataset with a lower + value, then the marker for the dataset with the lower value + will be undefined unless it was previously defined. The default + is a circle for the first dataset, a square for the second, a + diamond for the third and undefined for all subsequent + datasets. + + \param dataset the dataset for which to set the polar marker + \param style the style to set for the specified dataset + \sa PolarMarkerStyle, polarMarkerStyle + */ +void KDChartParams::setPolarMarkerStyle( uint dataset, PolarMarkerStyle style ) +{ + _polarMarkerStyles[ dataset ] = style; + _maxDatasetPolarMarkerStyle = QMAX( dataset, + _maxDatasetPolarMarkerStyle ); + emit changed(); +} + +/** + Returns the marker to be used for a dataset. Only used if + chartType() == Polar and polarMarker() == true. + + \param dataset the dataset for which to return the polar marker + \return the polar marker for the specified data set + \sa PolarMarkerStyle, setPolarMarkerStyle + */ +KDChartParams::PolarMarkerStyle KDChartParams::polarMarkerStyle( uint dataset ) const +{ + if( _polarMarkerStyles.find( dataset ) != _polarMarkerStyles.end() ) + return _polarMarkerStyles[ dataset ]; + else + return PolarMarkerCircle; // default +} + + +/** + \var KDChartParams::PolarMarkerStyleMap + + The type of the storage structure for polar marker styles. You + should normally not need to use this. + */ + + + +/** + Sets a whole map of polar marker styles. You can use this for + assigning many polar marker styles at the same time, but + typically it is better to set them individually with \a + setPolarMarkerStyle(). + + \param map the map of styles + \sa polarMarkerStyles(), setPolarMarkerStyle() + */ +void KDChartParams::setPolarMarkerStyles( PolarMarkerStyleMap map ) { + _polarMarkerStyles = map; + // update _maxDatasetPolarMarkerStyle + uint maxDataset = 0; + for( PolarMarkerStyleMap::ConstIterator it = _polarMarkerStyles.begin(); + it != _polarMarkerStyles.end(); ++it ) + maxDataset = QMAX( maxDataset, it.key() ); + _maxDatasetPolarMarkerStyle = maxDataset; +} + + +/** + \fn KDChartParams::PolarMarkerStyleMap KDChartParams::polarMarkerStyles() const + + Returns the whole map of polar marker styles. You will usually + not need this. You can query individual polar marker styles by + calling \a polarMarkerStyle(). + + \return the map of styles + \sa polarMarkerStyle(), setPolarMarkerStyles() + */ + + +/** + \fn uint KDChartParams::maxDatasetPolarMarkerStyle() const + + Returns the highest dataset for which a polar marker style has been + defined. Not all datasets with a lower number necessarily have + a defined polar marker. + + \return the highest dataset with a defined polar marker + \sa PolarMarkerStyle, setPolarMarkerStyle, polarMarkerStyle + */ + + +/** + \fn void KDChartParams::setPolarMarkerSize( QSize ) + + Specifies the sizes of polar markers. Only used if chartType() == Polar + and polarMarker() == true. The default is -40 x -40. + + \param size the size of the polar marker in pixels, if negative this is a + per mille value of the chart min. size (the chart width or height, depending + what is smaller), if positive the value is taken as absolute number of pixels. + + \sa polarMarkerSize + */ + + +/** + \fn QSize KDChartParams::polarMarkerSize() const + + Returns the sizes of polar markers. Only used if chartType() == + Polar and polarMarker() == true. + + \return the size of the polar marker in pixels + \sa setPolarMarkerSize + */ + + +/** + \fn void KDChartParams::setPolarLineWidth( int width ) + + Specifies the width for lines in polar charts. Default is -3. + + \param width the new width + \sa polarLineWidth + */ + + +/** + \fn int KDChartParams::polarLineWidth() const + + Returns the line width of the lines in polar charts. + + \return the line width of lines in polar charts + \sa setPolarLineWidth + */ + + +/** + \fn void KDChartParams::setPolarZeroDegreePos( int degrees ) + + Specifies the zero degree position in polar charts. + + Use this function to have the zero degree point on a position + different from the right side of the circle. Valid parameters + are -359..359. + + \sa polarZeroDegreePos + */ + + +/** + \fn int KDChartParams::polarZeroDegreePos() const + + Returns the zero degree position in polar charts. + + \sa setPolarZeroDegreePos + */ + + +/** + \fn void KDChartParams::setPolarRotateCircularLabels( bool rotateCircularLabels ) + + Specifies whether to rotate circular labels in polar charts. + + \sa polarRotateCircularLabels, setPolarDelimsAndLabelsAtPos + */ + + +/** + \fn bool KDChartParams::polarRotateCircularLabels() const + + Returns whether circular labels will be rotated in polar charts. + + \sa setPolarRotateCircularLabels, polarDelimAtPos, polarLabelsAtPos + */ + + +/** + Specifies whether to show circular delimiters and labels at a given + position in polar charts. + + \note Value \c KDChartEnums::PosCenter will be ignored. + + \sa setPolarRotateCircularLabels, polarDelimAtPos, polarLabelsAtPos + */ +void KDChartParams::setPolarDelimsAndLabelsAtPos( KDChartEnums::PositionFlag pos, + bool showDelimiters, + bool showLabels ) +{ + if( KDCHART_MAX_POLAR_DELIMS_AND_LABELS_POS >= pos ) { + _polarDelimsAndLabels[ pos ].showDelimiters = showDelimiters; + _polarDelimsAndLabels[ pos ].showLabels = showLabels; + } +} + +/** + Returns whether to show circular delimiters at a given + position in polar charts. + + \sa polarRotateCircularLabels, setPolarDelimAndLabelsPos, polarLabelsAtPos + */ +bool KDChartParams::polarDelimAtPos( KDChartEnums::PositionFlag pos ) const +{ + if( KDCHART_MAX_POLAR_DELIMS_AND_LABELS_POS >= pos ) + return _polarDelimsAndLabels[ pos ].showDelimiters; + else + return false; +} + +/** + Returns whether to show circular labels at a given + position in polar charts. + + \sa polarRotateCircularLabels, setPolarDelimAndLabelsPos, polarDelimAtPos + */ +bool KDChartParams::polarLabelsAtPos( KDChartEnums::PositionFlag pos ) const +{ + if( KDCHART_MAX_POLAR_DELIMS_AND_LABELS_POS >= pos ) + return _polarDelimsAndLabels[ pos ].showLabels; + else + return false; +} + + + +//@} +// END LINECHART + +// START RING/PIECHART +/** @name Ring and pie chart-specific methods. + + These methods query and set ring and pie chart-specific parameters. + */ +//@{ + +/** + \fn void KDChartParams::setExplode( bool explode ) + + Specifies whether the pie chart or ring chart should be + exploding (all pies or ring segments are slightly displaced + from the center) or not. The default is not to explode. Only + used if chartType() == Pie or chartType() == Ring. + + Note that calling this method by passing true turns on + exploding for all segments. You can turn exploding on and off + for individual segments by calling setExplodeValues() and also + change the explode factors by calling setExplodeFactor() or + setExplodeFactors(). + + Note that in ring charts, only the outermost ring is + exploded. Also note that exploding rings are only useful if + none of the values in the outermost dataset covers more than + 1/4 of the ring, otherwise the exploded ring will look funny. + + \param explode true if the pie or ring should be exploding, + false otherwise + \sa exploding(), setExplodeFactor(), explodeFactor(), + setExplodeFactors(), explodeFactors(), setExplodeValues(), + explodeValues() + */ + + +/** + \fn bool KDChartParams::explode() const + + Returns whether the pie or ring chart should be exploding or not + + \return true if the pie should be exploding + \sa setExplode, setExplodeFactor(), explodeFactor(), + setExplodeFactors(), explodeFactors(), setExplodeValues(), + explodeValues() + */ + + +/** + \fn void KDChartParams::setExplodeValues( QValueList<int> explodeList ) + + Specifies which values to explode. Explosion of values only + happens if setExplode( true ) has been called. Normally, all + values are exploded (all values on the outer ring in the case of a + ring chart). With this function, you have a finer control about + the explosion. You can specify the values that should be exploded + by their position. Passing an empty list here turns on explosion + for all pies or ring segments (but only if explosion is turned on + in general). + + To repeat: Setting anything here has no effect if setExplode( true ) + is not called. You can, however, set any values here even if explosion is + not turned on; they will be saved in case explosion will be turned on in + the future and then be valid automatically. + + The explode factor, i.e., the amount with which a segment is moved + from the center can either be set for all segments with + \a setExplodeFactor() or for individual segments with + \a setExplodeFactors(). The default is 10%. + + \param explodeList the list of positions in the displayed dataset that + should be drawn in an exploded position. Pass an empty list here to + explode all values. + \sa setExplode(), explode(), setExplodeFactor(), explodeFactor(), + explodeValues(), setExplodeFactors(), explodeFactors() + */ + + +/** + \fn QValueList<int> KDChartParams::explodeValues() const + + Returns which values are exploded in a pie or ring chart. For a detailed + explanation of this feature, please see \a setExplodeValues(). + + \return the list of values that are exploded + \sa setExplode(), explode(), setExplodeFactor(), explodeFactor(), + setExplodeValues() + */ + + +/** + \fn void KDChartParams::setExplodeFactors( QMap<int,double> factors ) + + Specifies the explode factors for each segment in percent, + i.e. how much an exploded pie or ring segment is displaced from + the center. The factor is given as a double value between 0 and 1; + 0.1 means 10%. Only used if chartType() == Pie or chartType() == + Ring and explode() == true. + + Segments that are not contained in the map specified here will + have the default explode factor of 10%, if exploding is turned on + for them at all. This also means that passing an empty list to + this method does not turn off exploding in general; use \a + setExplode( false ) for that. + +Note: This method has no immediate effect if setExplode( true ) +has not been called. It is, however, possible to preset explode +factors and then turn on exploding later. + +Note: Besides giving a segment an explode factor and turning on +exploding in general, you also need to specify to explode a +certain segment by calling \a setExplodeValues(). This gives maximum +flexibility as it allows you to preset explode factors and then +explode or not not explode a segment at leisure - at the expense +of one more method call. + +\param factors the list of explode factors +\sa setExplode(), explode(), setExplodeValues(), explodeValues(), +setExplodeFactor(), explodeFactor(), explodeFactors() +*/ + + +/** + \fn QMap<int,double> KDChartParams::explodeFactors() const { + + Returns the list of explode factors. Each explode factor in the + list corresponds to the segment at the same position. + + \return the list of explode factors + \sa setExplode(), explode(), setExplodeValues(), explodeValues(), + setExplodeFactor(), explodeFactor(), setExplodeFactors() + */ + + +/** + \fn void KDChartParams::setExplodeFactor( double factor ) + + Specifies the explode factor in percent, i.e. how much an + exploded pie or ring segment is displaced from the center. The + factor is given as a double value between 0 and 1; 0.1 means + 10% and is the default. Only used if + chartType() == Pie or chartType() == Ring and explode() == true. + + Note that this method sets the explode factor for all segments + that are exploded but for which no more specific explode factor + has been set with \a setExplodeFactors(). Thus, to make the value + specified here the default value, you can pass an empty list to + \a setExplodeFactors() (which also is the default). + + \param factor the explode factor + \sa setExplode(), explode(), explodeFactor(), + setExplodeValues(), explodeValues(), setExplodeFactors(), + explodeFactors() + */ + + +/** + \fn double KDChartParams::explodeFactor() const + + Returns the default explode factor, i.e., the explode factor + used for those segments which should be exploded but for which + no more specific explode factor has been set. See \a + setExplodeFactor() for an explanation of the value. + + \return the explode factor + \sa setExplodeFactor(), setExplode(), explode() + */ + + +/** + \fn void KDChartParams::setThreeDPies( bool threeDPies ) + + Specifies whether the engine should draw the pies in 3D. Only + used if chartType() == Pie. + + \param threeDPies true if pies should be drawn with a 3D effect + \sa threeDPies(), setThreeDPieHeight(), threeDPieHeight() + */ + + +/** + \fn bool KDChartParams::threeDPies() const + + Returns whether the engine should draw any pies in 3D. Only + used if chartType() == Pie. + + \return true if pies should be drawn with a 3D effect, false + otherwise + \sa setThreeDPies(), threeDPieHeight(), setThreeDPieHeight() + */ + + +/** + \fn void KDChartParams::setThreeDPieHeight( int pixels ) + + Specifies the height of the 3D effect for pies in pixels. Only used if + chartType() == Pie and threeDPies() == true. Negative values are + interpreted relative to the height of the pie where -100 means + that the 3D effect has the same height as the pie itself; values + smaller than -25 are hardly useful. Positive values are + interpreted as absolute sizes in pixels. The default is an + absolute value of 20 pixels. + + \param height the height of the 3D effect in pixels + \sa threeDHeight(), setThreeDPies(), threeDPies() + */ + + +/** + \fn int KDChartParams::threeDPieHeight() const + + Returns the height of the 3D effect for pies in pixels. See \a + setThreeDPieHeight for the interpretation of the value. + + \return the height of the 3D effect in pixels + \sa setThreeDPieHeight(), setThreeDPies(), threeDPies() + */ + + +/** + \fn void KDChartParams::setPieStart( int degrees ) + + Specifies the starting point of the pie circle. The default is the 3 o' + clock position which equals to 0. Positive values are angles in degrees + counterclockwise, negative values are angles in degrees + clockwise. All values will be normalized to [0;360[. + + \param degrees the starting point in degrees + \sa pieStart() + */ + + +/** + \fn int KDChartParams::pieStart() const + + Returns the starting point of the pie circle. See \a setPieStart() for + the interpretation of these values. The return value is + always in the interval [0;360[ + + \return the starting point of the pie circle in degrees + \sa setPieStart() + */ + + +/** + \fn void KDChartParams::setRingStart( int degrees ) + + Specifies the starting point of the ring circle. The default is the 3 o' + clock position which equals to 0. Positive values are angles in degrees + counterclockwise, negative values are angles in degrees + clockwise. All values will be normalized to [0;360[. + + \param degrees the starting point in degrees + \sa pieStart() + */ + + +/** + \fn int KDChartParams::ringStart() const + + Returns the starting point of the ring circle. See \a + setRingStart() for the interpretation of these values. The + return value is always in the interval [0;360[ + + \return the starting point of the ring circle in degrees + \sa setRingStart() + */ + + +/** + \fn void KDChartParams::setRelativeRingThickness( bool relativeThickness ) + + Specifies whether the ring thickness should be relative to the sum of the + values in the dataset that the ring represents. The default is to have all + the rings with the same thickness which itself depends on the size of the + chart and the number of rings. + + \param relativeThickness if true, ring thickness is relative, if false, it + is constant. + \sa relativeThickness() + */ + + +/** + \fn bool KDChartParams::relativeRingThickness() const + + Returns whether the ring thickness is relative to the sum of values in the + dataset that the ring represents, or whether the ring thickness should be + constant. + + \return true if thickness is relative, false if it is constant + \sa setRelativeRingThickness() + */ + + + +//@} +// END RING/PIECHART + +// START HILO CHART +/** @name Hi-lo chart-specific methods. + + These methods query and set hi-lo chart-specific parameters. + */ +//@{ + +/** + \enum KDChartParams::HiLoChartSubType + + The HiLo subtype. Only used when chartType == HiLo + + \sa setHiLoChartSubType, hiLoChartSubType + */ + +/** + \fn void KDChartParams::setHiLoChartSubType( HiLoChartSubType hiLoChartSubType ) + + Specifies the HiLo chart subtype. Only used if chartType() == + HiLo + + \param hiLoChartSubType the HiLo chart subtype + \sa hiLoChartSubType, HiLoChartSubType, setChartType, chartType + */ + + +/** + \fn KDChartParams::HiLoChartSubType KDChartParams::hiLoChartSubType() const + + Returns the HiLo chart subtype. Only used if chartType() == + HiLo. + + \return the HiLo chart sub type + \sa setHiLoChartSubType, HiLoChartSubType, setChartType, chartType + */ + + +/** + Specifies if and how a HiLo chart will print the Low + values below their respective entries. Only used if chartType() == HiLo + + \note <b>setHiLoChartPrintLowValues( false )</b> will + deactivate printing of Low values. + + \param active specifies whether the values are to be printed or not. + \param font a Pointer to the font to be used. + \param size (in per mille of the chart width) the dynamic size of + the font to be used. If this parameter is zero the size of the + \c font is used instead - regardless of the size of the chart! + \param color the color to be used when printing the values. + \sa setChartType, chartType + \sa setHiLoChartSubType, hiLoChartSubType + \sa setHiLoChartPrintHighValues + \sa hiLoChartPrintLowValues, hiLoChartPrintLowValues + \sa hiLoChartLowValuesFontColor, hiLoChartLowValuesFontColor + \sa hiLoChartLowValuesFontUseRelSize, hiLoChartLowValuesFontUseRelSize + \sa hiLoChartLowValuesFontRelSize, hiLoChartLowValuesFontRelSize + \sa hiLoChartPrintHighValues, hiLoChartPrintHighValues + \sa hiLoChartHighValuesFontColor, hiLoChartHighValuesFontColor + \sa hiLoChartHighValuesFontUseRelSize, hiLoChartHighValuesFontUseRelSize + \sa hiLoChartHighValuesFontRelSize, hiLoChartHighValuesFontRelSize + */ +void KDChartParams::setHiLoChartPrintLowValues( bool active, QFont* font, + int size, QColor* color ) +{ + _hiLoChartPrintLowValues = active; + if ( font ) + _hiLoChartLowValuesFont = *font; + else + _hiLoChartLowValuesFont = _defaultFont; + _hiLoChartLowValuesUseFontRelSize = ( 0 < size ); + _hiLoChartLowValuesFontRelSize = size; + if ( 0 == color ) + _hiLoChartLowValuesColor = QColor( 0, 0, 0 ); + else + _hiLoChartLowValuesColor = *color; + emit changed(); +} + + +/** + \fn bool KDChartParams::hiLoChartPrintLowValues() const + + Returns whether the low values + will be printed under their respective entries. + + \return whether the low values + will be printed under their respective entries. + + \sa setHiLoChartPrintLowValues, setHiLoChartPrintHighValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + \fn QFont KDChartParams::hiLoChartLowValuesFont() const + + Returns the font to be used for printing the + low values + + \returns the font to be used for printing the + low values + + \sa setHiLoChartPrintLowValues, setHiLoChartPrintHighValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + \fn bool KDChartParams::hiLoChartLowValuesUseFontRelSize() const + + Returns whether the font size to be used for printing the + low values is calculated dynamically. + + \return whether the font size to be used for printing the + low values is calculated dynamically. + + \sa setHiLoChartPrintLowValues, setHiLoChartPrintHighValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + \fn int KDChartParams::hiLoChartLowValuesFontRelSize() const + + Returns the relative size (in per mille of the chart width) + of font size to be used for printing the + low values. + + \return the relative size (in per mille of the chart width) + of font size to be used for printing the + low values. + + \sa setHiLoChartPrintLowValues, setHiLoChartPrintHighValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + \fn QColor KDChartParams::hiLoChartLowValuesColor() const + + Returns the colour of the font size to be used for printing the + low values. + + \return the colour of the font size to be used for printing the + low values. + + \sa setHiLoChartPrintLowValues, setHiLoChartPrintHighValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + Specifies if and how a HiLo chart will print the High + values below their respective entries. + Only used if chartType() == HiLo + + \note <b>setHiLoChartPrintHighValues( false )</b> will + deactivate printing of High values. + + \param active specifies whether the values are to be printed or not. + \param font specifies a Pointer to the font to be used. + \param size (in per mille of the chart width) specifies the + dynamic size of the font to be used. If this parameter is zero + the size of the + \c font is used instead - regardless of the size of the chart! + \param specifies the color the color to be used when printing + the values. + \sa setHiLoChartPrintLowValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + \sa hiLoChartPrintHighValues, hiLoChartPrintHighValues + \sa hiLoChartHighValuesFontColor, hiLoChartHighValuesFontColor + \sa hiLoChartHighValuesFontUseRelSize, hiLoChartHighValuesFontUseRelSize + \sa hiLoChartHighValuesFontRelSize, hiLoChartHighValuesFontRelSize + \sa hiLoChartPrintLowValues, hiLoChartPrintLowValues + \sa hiLoChartLowValuesFontColor, hiLoChartLowValuesFontColor + \sa hiLoChartLowValuesFontUseRelSize, hiLoChartLowValuesFontUseRelSize + \sa hiLoChartLowValuesFontRelSize, hiLoChartLowValuesFontRelSize + */ +void KDChartParams::setHiLoChartPrintHighValues( bool active, QFont* font, + int size, QColor* color ) +{ + _hiLoChartPrintHighValues = active; + if ( font ) + _hiLoChartHighValuesFont = *font; + else + _hiLoChartHighValuesFont = _defaultFont; + _hiLoChartHighValuesUseFontRelSize = ( 0 < size ); + _hiLoChartHighValuesFontRelSize = size; + if ( 0 == color ) + _hiLoChartHighValuesColor = QColor( 0, 0, 0 ); + else + _hiLoChartHighValuesColor = *color; + emit changed(); +} + + +/** + \fn bool KDChartParams::hiLoChartPrintHighValues() const + + Returns whether the high values + will be printed under their respective entries. + + \return whether the high values + will be printed under their respective entries. + + \sa setHiLoChartPrintHighValues, setHiLoChartPrintLowValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + \fn QFont KDChartParams::hiLoChartHighValuesFont() const + + Returns the font to be used for printing the + high values. + + \returns the font to be used for printing the + high values. + + \sa setHiLoChartPrintHighValues, setHiLoChartPrintLowValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + \fn bool KDChartParams::hiLoChartHighValuesUseFontRelSize() const + + Returns whether the font size to be used for printing the + high values is calculated dynamically. + + \return whether the font size to be used for printing the + high values is calculated dynamically. + + \sa setHiLoChartPrintHighValues, setHiLoChartPrintLowValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + \fn int KDChartParams::hiLoChartHighValuesFontRelSize() const + + Returns the relative size (in per mille of the chart width) + of font size to be used for printing the + high values. + + \return the relative size (in per mille of the chart width) + of font size to be used for printing the + high values. + + \sa setHiLoChartPrintHighValues, setHiLoChartPrintHighValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + \fn QColor KDChartParams::hiLoChartHighValuesColor() const + + Returns the colour of the font size to be used for printing the + high values. + + \return the colour of the font size to be used for printing the + high values. + + \sa setHiLoChartPrintHighValues, setHiLoChartPrintLowValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + Specifies if and how a HiLo chart will print the Open values + below their respective entries. + Only used if chartType() == HiLo and if the HiLo chart sub type + shows open values. + + \note <b>setHiLoChartPrintOpenValues( false )</b> will + deactivate printing of Open values. + + \param active specifies whether the values are to be printed or not. + \param font a Pointer to the font to be used. + \param size (in per mille of the chart width) the dynamic size of + the font to be used. If this parameter is zero the size of the + \c font is used instead - regardless of the size of the chart! + \param color the color to be used when printing the values. + \sa setHiLoChartPrintCloseValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + \sa hiLoChartPrintOpenValues, hiLoChartPrintOpenValues + \sa hiLoChartOpenValuesFontColor, hiLoChartOpenValuesFontColor + \sa hiLoChartOpenValuesFontUseRelSize, hiLoChartOpenValuesFontUseRelSize + \sa hiLoChartOpenValuesFontRelSize, hiLoChartOpenValuesFontRelSize + \sa hiLoChartPrintCloseValues, hiLoChartPrintCloseValues + \sa hiLoChartCloseValuesFontColor, hiLoChartCloseValuesFontColor + \sa hiLoChartCloseValuesFontUseRelSize, hiLoChartCloseValuesFontUseRelSize + \sa hiLoChartCloseValuesFontRelSize, hiLoChartCloseValuesFontRelSize + */ +void KDChartParams::setHiLoChartPrintOpenValues( bool active, QFont* font, + uint size, QColor* color ) +{ + _hiLoChartPrintOpenValues = active; + if ( font ) + _hiLoChartOpenValuesFont = *font; + else + _hiLoChartOpenValuesFont = _defaultFont; + _hiLoChartOpenValuesUseFontRelSize = ( 0 < size ); + _hiLoChartOpenValuesFontRelSize = size; + if ( 0 == color ) + _hiLoChartOpenValuesColor = QColor( 0, 0, 0 ); + else + _hiLoChartOpenValuesColor = *color; + emit changed(); +} + + +/** + \fn bool KDChartParams::hiLoChartPrintOpenValues() const + + Returns whether the open values will be printed under their + respective entries. + + \return whether the open values will be printed under their + respective entries. + + \sa setHiLoChartPrintOpenValues, setHiLoChartPrintCloseValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + \fn QFont KDChartParams::hiLoChartOpenValuesFont() const + + Returns the font to be used for printing the + open values. + + \returns the font to be used for printing the + open values. + + \sa setHiLoChartPrintOpenValues, setHiLoChartPrintCloseValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + \fn bool KDChartParams::hiLoChartOpenValuesUseFontRelSize() const + + Returns whether the font size to be used for printing the + open values is calculated dynamically. + + \return whether the font size to be used for printing the + open values is calculated dynamically. + + \sa setHiLoChartPrintOpenValues, setHiLoChartPrintCloseValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + \fn int KDChartParams::hiLoChartOpenValuesFontRelSize() const + + Returns the relative size (in per mille of the chart width) + of font size to be used for printing the open values. + + \return the relative size (in per mille of the chart width) of + font size to be used for printing the open values. + + \sa setHiLoChartPrintOpenValues, setHiLoChartPrintCloseValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + \fn QColor KDChartParams::hiLoChartOpenValuesColor() const + + Returns the colour of the font size to be used for printing the + open values. + + \return the colour of the font size to be used for printing the + open values. + + \sa setHiLoChartPrintOpenValues, setHiLoChartPrintCloseValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + Specifies if and how a HiLo chart will print the Close + values next to their respective entries. + Only used if chartType() == HiLo and the HiLo chart sub type + contains Close values. + + \note <b>setHiLoChartPrintCloseValues( false )</b> will + deactivate printing of Close values. + + \param active specifies whether the values are to be printed or not. + \param font specifies a Pointer to the font to be used. + \param size (in per mille of the chart width) specifies the dynamic size of + the font to be used. If this parameter is zero the size of the + \c font is used instead - regardless of the size of the chart! + \param specifies the color the color to be used when printing the values. + \sa setHiLoChartPrintOpenValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + \sa hiLoChartPrintCloseValues, hiLoChartPrintCloseValues + \sa hiLoChartCloseValuesFontColor, hiLoChartCloseValuesFontColor + \sa hiLoChartCloseValuesFontUseRelSize, hiLoChartCloseValuesFontUseRelSize + \sa hiLoChartCloseValuesFontRelSize, hiLoChartCloseValuesFontRelSize + \sa hiLoChartPrintOpenValues, hiLoChartPrintOpenValues + \sa hiLoChartOpenValuesFontColor, hiLoChartOpenValuesFontColor + \sa hiLoChartOpenValuesFontUseRelSize, hiLoChartOpenValuesFontUseRelSize + \sa hiLoChartOpenValuesFontRelSize, hiLoChartOpenValuesFontRelSize + */ +void KDChartParams::setHiLoChartPrintCloseValues( bool active, QFont* font, + int size, QColor* color ) +{ + _hiLoChartPrintCloseValues = active; + if ( font ) + _hiLoChartCloseValuesFont = *font; + else + _hiLoChartCloseValuesFont = _defaultFont; + _hiLoChartCloseValuesUseFontRelSize = ( 0 < size ); + _hiLoChartCloseValuesFontRelSize = size; + if ( 0 == color ) + _hiLoChartCloseValuesColor = QColor( 0, 0, 0 ); + else + _hiLoChartCloseValuesColor = *color; + emit changed(); +} + +/** + \fn bool KDChartParams::hiLoChartPrintCloseValues() const + + Returns whether the close values will be printed under their + respective entries. + + \return whether the close values will be printed under their + respective entries. + + \sa setHiLoChartPrintCloseValues, setHiLoChartPrintOpenValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + \fn QFont KDChartParams::hiLoChartCloseValuesFont() const + + Returns the font to be used for printing the close values. + + \returns the font to be used for printing the close values. + + \sa setHiLoChartPrintCloseValues, setHiLoChartPrintOpenValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + \fn bool KDChartParams::hiLoChartCloseValuesUseFontRelSize() const + + Returns whether the font size to be used for printing the close + values is calculated dynamically. + + \return whether the font size to be used for printing the close + values is calculated dynamically. + + \sa setHiLoChartPrintCloseValues, setHiLoChartPrintOpenValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + \fn int KDChartParams::hiLoChartCloseValuesFontRelSize() const + Returns the relative size (in per mille of the chart width) of + font size to be used for printing the close values. + + \return the relative size (in per mille of the chart width) of + font size to be used for printing the close values. + + \sa setHiLoChartPrintCloseValues, setHiLoChartPrintOpenValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + \fn QColor KDChartParams::hiLoChartCloseValuesColor() const + + Returns the colour of the font size to be used for printing the + close values. + + \return the colour of the font size to be used for printing the + close values. + + \sa setHiLoChartPrintCloseValues, setHiLoChartPrintOpenValues + \sa setHiLoChartSubType, hiLoChartSubType + \sa setChartType, chartType + */ + + +/** + \fn void KDChartParams::setHiLoChartPrintFirstValues( bool, QFont*, uint, QColor* ) + + \obsolete + + This method is obsolete; use \a setHiLoChartPrintOpenValues(). + */ + + +/** + \fn bool KDChartParams::hiLoChartPrintFirstValues() const + + \obsolete + + This method is obsolete; use \a hiLoChartPrintOpenValues() + instead. + */ + + +/** + \fn QFont KDChartParams::hiLoChartFirstValuesFont() const + + \obsolete + + This method is obsolete; use \a hiLoChartOpenValuesFont() + instead. + */ + + +/** + \fn bool KDChartParams::hiLoChartFirstValuesUseFontRelSize() const + + \obsolete + + This method is obsolete; use \a + hiLoChartOpenValuesUseFontRelSize() instead. + */ + + +/** + \fn int KDChartParams::hiLoChartFirstValuesFontRelSize() const + + \obsolete + + This method is obsolete; use \a + hiLoChartOpenValuesFontRelSize() instead. + */ + + +/** + \fn QColor KDChartParams::hiLoChartFirstValuesColor() const + + \obsolete + + This method is obsolete; use \a hiLoChartOpenValuesColor() + instead. + */ + + +/** + \fn void KDChartParams::setHiLoChartPrintLastValues( bool, QFont*, int, QColor* ) + + \obsolete + + This method is obsolete; use \a setHiLoChartPrintCloseValues() + instead. + */ + + +/** + \fn bool KDChartParams::hiLoChartPrintLastValues() const + + \obsolete + + This method is obsolete; use \a hiLoChartPrintCloseValues() + instead. + */ + + +/** + \fn QFont KDChartParams::hiLoChartLastValuesFont() const + + \obsolete + + This method is obsolete; use \a hiLoChartCloseValuesFont() + instead. + */ + + +/** + \fn bool KDChartParams::hiLoChartLastValuesUseFontRelSize() const + + \obsolete + + This method is obsolete; use \a hiLoChartCloseValuesUseFontRelSize() + instead. + */ + + +/** + \fn int KDChartParams::hiLoChartLastValuesFontRelSize() const + + \obsolete + + This method is obsolete; use \a hiLoChartCloseValuesFontRelSize() + instead. + */ + + +/** + \fn QColor KDChartParams::hiLoChartLastValuesColor() const + + \obsolete + + This method is obsolete; use \a hiLoChartCloseValuesColor() + instead. + */ + + + +//@} +// END HILO CHART + +// START BOX/WHISKER CHART +/** @name Box-and-whisker chart-specific methods. + + These methods query and set box-and-whisker chart-specific parameters. + */ +//@{ + +/** + \enum KDChartParams::BWChartSubType + + The BoxWhisker subtype. Only used when chartType == BoxWhisker + + \sa setBWChartSubType, bWChartSubType + */ + + +/** + \enum KDChartParams::BWStatVal + + The different statistical values that can be printed into or + asked from a Box and Whisker chart. Only used when chartType == BoxWhisker + + \sa setBWChartSubType, bWChartSubType + */ // use the following syntax to avoid warnings: + + +/** + \fn void KDChartParams::setBWChartSubType( BWChartSubType bWChartSubType ) + + Specifies the BoxWhisker chart subtype. Only used if chartType() == + BoxWhisker + + \param bWChartSubType the BoxWhisker chart subtype + \sa bWChartSubType, BWChartSubType, setChartType, chartType + */ + + +/** + \fn KDChartParams::BWChartSubType KDChartParams::bWChartSubType() const + + Returns the BoxWhisker chart subtype. Only used if chartType() == + BoxWhisker. + + \return the BoxWhisker chart sub type + \sa setBWChartSubType, BWChartSubType, setChartType, chartType + */ + + + +/** + Specifies the factors to be used to calculate the position of + the inner (or outer, resp.) fences. + + Default values for both inner fences are 1.5 times the + interquartile range from the corresponding quartile, + for both outer fences default is 3.0 times the IQR. + + \sa bWChartFences + \sa setBWChartOutValMarkerSize, setBWChartPrintStatistics + \sa setBWChartSubType, bWChartSubType + \sa setChartType, chartType + */ +void KDChartParams::setBWChartFences( double upperInner, double lowerInner, + double upperOuter, double lowerOuter ) +{ + _BWChartFenceUpperInner = upperInner; + _BWChartFenceLowerInner = lowerInner; + _BWChartFenceUpperOuter = upperOuter; + _BWChartFenceLowerOuter = lowerOuter; +} +/** + Retrievs the factors to be used to calculate the position of + the inner (or outer, resp.) fences. + + \sa setBWChartFences + \sa setBWChartOutValMarkerSize, setBWChartPrintStatistics + \sa setBWChartSubType, bWChartSubType + \sa setChartType, chartType + */ +void KDChartParams::bWChartFences( double& upperInner, double& lowerInner, + double& upperOuter, double& lowerOuter ) const +{ + upperInner = _BWChartFenceUpperInner; + lowerInner = _BWChartFenceLowerInner; + upperOuter = _BWChartFenceUpperOuter; + lowerOuter = _BWChartFenceLowerOuter; +} + + +/** + \fn void KDChartParams::setBWChartBrush( const QBrush& bWChartBrush ) + + Specifies the brush to be used to fill the BoxWhisker charts. + + \param bWChartBrush the brush to be used + \sa bWChartBrush, setChartType, chartType + */ + + +/** + \fn QBrush KDChartParams::bWChartBrush() const + + Returns the brush to be used to fill the BoxWhisker charts. + + \return the brush to be used + \sa setBWChartBrush, setChartType, chartType + */ + + +/** + \fn void KDChartParams::setBWChartOutValMarkerSize( int size ) + + Specifies how big outlier markers (representing the values outside + the inner fences) will be drawn. + + When specifying a value different from 0 the chart will show the outlier + markers in the respective size, use zero to disable showing the outliers, + use negative values to have the marker size calculated dynamically + (in percent of the current boxes' width). Thus marker sizes will be + recalculated after modification of either the number of boxes + or the size of the entire chart. + + \note Shapes of the outlier markers depend on their distance to the + respective fence: circles are used to represent near outside values + while asteriscs indicate far outside values (the ones outside the + outer fence), you may use setBWChartFences() to specify the factor + to be used to calculate the value of the inner and outer fence. + + \sa bWChartOutValMarkerSize + \sa setBWChartFences, setBWChartPrintStatistics + \sa setBWChartSubType, bWChartSubType + \sa setChartType, chartType + */ + + +/** + \fn int KDChartParams::bWChartOutValMarkerSize() const + + Returns how big outlier markers (representing the values outside + the inner fences) will be drawn. + + \return how big outside value markers will be drawn, zero means + they will be invisible, negative values indicate that the size + is calculated dynamically based upon the current boxes' width. + + \sa setBWChartOutValMarkerSize + \sa setBWChartFences, setBWChartPrintStatistics + \sa setBWChartSubType, bWChartSubType + \sa setChartType, chartType + */ + + +/** + Specifies if and how a BoxWhisker chart will print the + respective statistical values near to the drawing. + Only used if chartType() == BoxWhisker + + \note <b>setBWChartPrintStatistics( BWStatVal, false )</b> will + deactivate printing of the respective statistical values. + + \param statValue specifies the statistical value to be set, use + BWStatValALL to define equal settings for all + of the statistical values. + \param active specifies whether the values are to be printed or not. + \param font a Pointer to the font to be used. + \param size (relative to the boxes' width) the dynamic size of + the font to be used. If this parameter is zero the size of the + \c font is used instead - regardless of the size of the chart + and regardless of the number of boxes! + \param color the color to be used when printing the values. + \param brush the brush to be used to fill the text's area before printing + \sa setChartType + \sa setBWChartSubType + \sa bWChartPrintStatistics + \sa bWChartStatisticsColor, + \sa bWChartStatisticsBrush, + \sa bWChartStatisticsFontUseRelSize + \sa bWChartStatisticsFontRelSize + */ +void KDChartParams::setBWChartPrintStatistics( BWStatVal statValue, + bool active, + QFont* font, + int size, + QColor* color, + QBrush* brush ) +{ + BWStatVal statValA = (BWStatValALL == statValue) ? BWStatValSTART : statValue; + BWStatVal statValZ = (BWStatValALL == statValue) ? BWStatValEND : statValue; + for( int i = statValA; i <= statValZ; ++i ){ + _BWChartStatistics[ i ].active = active; + if ( font ) + _BWChartStatistics[ i ].font = *font; + else + _BWChartStatistics[ i ].font = _defaultFont; + _BWChartStatistics[ i ].useRelSize = ( 0 < size ); + _BWChartStatistics[ i ].relSize = size; + if ( 0 == color ) + _BWChartStatistics[ i ].color = QColor( 0, 0, 0 ); + else + _BWChartStatistics[ i ].color = *color; + if ( 0 == brush ) + _BWChartStatistics[ i ].brush = QBrush( Qt::white ); + else + _BWChartStatistics[ i ].brush = *brush; + } + emit changed(); +} + + +/** + \fn bool KDChartParams::bWChartPrintStatistics( BWStatVal statValue ) const + + Returns whether the respective statistical values + will be printed near to the drawing. + + \return whether the respective statistical values + will be printed near to the drawing. + + \sa setBWChartPrintStatistics + \sa setBWChartSubType, bWChartSubType + \sa setChartType, chartType + */ + + +/** + \fn QFont KDChartParams::bWChartStatisticsFont( BWStatVal statValue ) const + + Returns the font to be used for printing the respective statistical values + + \returns the font to be used for printing the respective statistical values + + \sa setBWChartPrintStatistics + \sa setBWChartSubType, bWChartSubType + \sa setChartType, chartType + */ + + +/** + \fn bool KDChartParams::bWChartStatisticsUseRelSize( BWStatVal statValue ) const + + Returns whether the font size to be used for printing the respective statistical values + is calculated dynamically. + + \returns whether the font size to be used for printing the respective statistical values + is calculated dynamically. + + \sa setBWChartPrintStatistics + \sa setBWChartSubType, bWChartSubType + \sa setChartType, chartType + */ + + +/** + \fn int KDChartParams::bWChartStatisticsFontRelSize( BWStatVal statValue ) const + + Returns the relative size (in per mille of the chart width) + of the font to be used for printing the respective statistical values. + + \returns the relative size (in per mille of the chart width) + of the font to be used for printing the respective statistical values. + + \sa setBWChartPrintStatistics + \sa setBWChartSubType, bWChartSubType + \sa setChartType, chartType + */ + + +/** + \fn QColor KDChartParams::bWChartStatisticsColor( BWStatVal statValue ) const + + Returns the colour + of the font to be used for printing the respective statistical values. + + \returns the colour + of the font to be used for printing the respective statistical values. + + \sa setBWChartPrintStatistics + \sa setBWChartSubType, bWChartSubType + \sa setChartType, chartType + */ + + +/** + \fn QBrush KDChartParams::bWChartStatisticsBrush( BWStatVal statValue ) const + + Returns the brush used + to fill the text's area before printing the respective statistical values. + + \returns the brush used + to fill the text's area before printing the respective statistical values. + + \sa setBWChartPrintStatistics + \sa setBWChartSubType, bWChartSubType + \sa setChartType, chartType + */ + + +//@} +// END BOX/WHISKER CHART + +// START LEGENDS +/** @name Legend methods. + + These methods query and set legend parameters. + */ +//@{ + +/** + \enum KDChartParams::LegendPosition + + The possible positions for a legend. NoLegend turns legend display + off. + + \sa setLegendPosition, legendPosition + */ + + +/** + \fn void KDChartParams::setLegendPosition( LegendPosition position ) + + Specifies where the legend should be shown. NoLegend turns legend + display off. The default is LegendRight which positions the legend + right to the data display. + + \param the position for the legend + \sa LegendPosition, legendPosition, setLegendOrientation, setLegendShowLines + */ + + +/** + \fn KDChartParams::LegendPosition KDChartParams::legendPosition() const + + Returns where the legend will be shown. + + \return where the legend will be shown + \sa LegendPosition, setLegendPosition, setLegendShowLines + */ + + +/** + \fn void KDChartParams::setLegendOrientation( Qt::Orientation orientation ) + + Specifies how the legend should be printed. Qt::Vertical (the default) + prints the legend entries below each other, Qt::Horizontal prints them + aside each other. + + \note Horizontal orientation is only possible if the chart is NOT making + room in horizontal direction, this means you may specify horizontal + orientation if the legend position in one of the following values + only: LegendTop, LegendBottom, LegendTopLeftTop, LegendTopRightTop, + LegendBottomLeftBottom, LegendBottomRightBottom + + \param the orientation for the legend + \sa legendOrientation, setLegendPosition, setLegendShowLines + */ + + +/** + \fn Qt::Orientation KDChartParams::legendOrientation() const + + Returns how the legend will be printed. + + \return how the legend will be printed. + \sa setLegendOrientation, setLegendPosition, setLegendShowLines + */ + +/** + \fn void KDChartParams::setLegendShowLines( bool legendShowLines ) + + Specifies whether the legend shall show lines or just + show the markers (or squares, resp.). + + \note By default no lines are drawn but just the markers are shown, + for Line charts you might want to set this flag to true. + + \param flag whether lines are drawn or only the markers + \sa legendShowLines, setLegendOrientation, setLegendPosition + */ + + +/** + \fn bool KDChartParams::legendShowLines() const + + Returns whether the legend shows lines or just + the markers (or squares, resp.). + + \return whether the legend shows lines or just the markers (or squares, resp.). + \sa setLegendShowLines, setLegendOrientation, setLegendPosition + */ + + + +/** + \enum KDChartParams::LegendSource + + The possible ways for getting the legend text. Available are: + <ul> + <li>Manual - Uses texts set with setLegendText(); if no text is set for + a dataset, the legend text will be empty. + <li>FirstColumn - Uses values from the first column, no matter what + this contains. + <li>Automatic - Tries first to use values from the first column; if + this does not contain any string values, tries to use values set + manually with setLegendText(). If there are no values set manually + either, resolves to standard texts like Series 1, Series 2, .... This + is the default. + </ul> + \sa setLegendSource, legendSource, setLegendText, legendText + */ + + +/** + \fn void KDChartParams::setLegendSource( LegendSource source ) + + Specifies where the legend text is coming from. See LegendSource for an + explanation of the possible values. + + \param source the source of the legend texts + \sa legendSource, LegendSource, setLegendText, legendText + */ + + +/** + \fn LegendSource KDChartParams::legendSource() const + + Returns where the legend text is coming from. See LegendSource for an + explanation of the possible return values. + + \return where the legend text is coming from + \sa LegendSource, setLegendSource, setLegendText, legendText + */ + + + +/** + \fn void KDChartParams::setLegendText( uint dataset, const QString& text ) + + Manually specifies a legend text for a certain dataset. This is only + used if legendSource() == Manual or if legendSource() == Automatic and + it is not possible to retrieve values from the first column. + + \param dataset the dataset for which to set the legend text + \param the legend text to be set for a given dataset + \sa LegendSource, setLegendSource, legendSource, legendText + */ + + +/** + \fn QString KDChartParams::legendText( uint dataset ) const + + Returns the manually specified legend text for a given dataset. + + \return the manually specified legend text for the specified dataset + \sa setLegendText, LegendSource, setLegendSource, legendSource + */ + + +/** + \fn void KDChartParams::setLegendTextColor( const QColor& color ) + + Specifies the color to be used for the legend texts. The + default is black. + + \param color the color to be used for the legend texts + \sa legendTextColor(), setLegendTitleTextColor() + */ + + +/** + \fn QColor KDChartParams::legendTextColor() const + + Returns the color in which the legend texts are drawn. The + default is black. + + \return the color in which the legend texts are drawn + \sa setLegendTextColor(), legendTitleTextColor() + */ + + +/** + \fn void KDChartParams::setLegendFont( const QFont& font, bool useFontSize ) + + Specifies the font in which the legend texts are drawn. The default is a + 10pt helvetica font. + + \note The font size will be ignored if \c useFontSize is false, + in this case the font size will be calculated dynamically using + the value stored by you calling setLegendFontRelSize(). + + \param font the font to draw the legend texts in + \sa setLegendFontUseRelSize, legendFont + */ + + +/** + \fn QFont KDChartParams::legendFont() const + + Returns the font in which the legend texts are drawn. + + \return the font in which the legend texts are drawn + \sa setLegendFont, setLegendFontUseRelSize + */ + + +/** + \fn void KDChartParams::setLegendFontUseRelSize( bool legendFontUseRelSize ) + + Specifies whether the legend shall be drawn + using relative font size. + + \param legendFontUseRelSize whether legend shall be drawn + using relative font size. + If true the absolute value of the value set by \c + setLegendFontRelSize is per thousand + of of the average value of the printable area height and width. + This will make the legend look nice even if scaled to very + different size. + + \sa setLegendFontRelSize, setAxisLabelsFont + */ + + +/** + \fn bool KDChartParams::legendFontUseRelSize() const + + Returns whether the legend font size is used. + + \return whether the fix legend font size is used. + \sa setLegendFontRelSize, setLegendFont + */ + + +/** + \fn void KDChartParams::setLegendFontRelSize( int legendFontRelSize ) + + Specifies the legend relative font size. + + \param legendFontRelSize the relative legend size. + If this value unequals zero the absolute value is per thousand + of of the average value of the printable area height and width. + This will make the legend look nice even if scaled to very + different size. + + \sa setLegendFontUseRelSize, setLegendFont + */ + + +/** + \fn int KDChartParams::legendFontRelSize() const + + Returns the relative legend font size. + + \return the relative legend font size. + \sa setLegendFontRelSize, setLegendFontUseRelSize + */ + + +/** + \fn void KDChartParams::setLegendTitleText( const QString& text ) + + Specifies a text to be shown as the title of the legend. The + default is "Legend" (possibly localized). To turn off the + legend title completely, pass an empty string. + + \param text the text to use as the legend title + \sa legendTitleText(), setLegendTitleFont(), + setLegendTitleFontUseRelSize() + */ + + + + +/** + \fn QString KDChartParams::legendTitleText() const + + Returns the text that is shown as the title of the legend. + + \return the text that is used as the legend title + \sa setLegendTitleText(), legendTitleFont(), + legendTitleFontUseRelSize() + */ + + +/** + \fn void KDChartParams::setLegendTitleTextColor( const QColor& color ) + + Specifies the color to be used for the legend title. The + default is black. Note that this color is only the default when + rich text is used; tags in the rich text might change the color. + + \param color the color to be used for the legend title + \sa legendTitleTextColor(), setLegendTextColor() + */ + + +/** + \fn QColor KDChartParams::legendTitleTextColor() const + + Returns the color in which the legend title is drawn. The + default is black. + + \return the color in which the legend title is drawn + \sa setLegendTitleTextColor(), legendTextColor() + */ + + +/** + \fn void KDChartParams::setLegendTitleFont( const QFont& font, bool useFontSize ) + + Specifies the font in which the legend title (if any) is drawn. The + default is a 12pt helvetica font. + + \note The font size will be ignored if \c useFontSize is false, + in this case the font size will be calculated dynamically using + the value stored by you calling setLegendFontRelSize(). + + \param font the font to draw the legend texts in + \sa setLegendTitleFontUseRelSize, legendTitleFont + */ + + +/** + \fn QFont KDChartParams::legendTitleFont() const + + Returns the font in which the legend title (if any) is drawn. + + \return the font in which the legend texts are drawn + \sa setLegendTitleFont, setLegendTitleFontUseRelSize + */ + + +/** + \fn void KDChartParams::setLegendTitleFontUseRelSize( bool legendTitleFontUseRelSize ) + + + Specifies whether the legend title (if any) shall be drawn using + relative font size. + + \param legendTitleFontUseRelSize whether the legend title shall + be drawn using relative font size. + If true the absolute value of the value set by \c + setLegendTitleFontRelSize is per thousandth of the average + value of the printable area height and width. + This will make the legend title look nice even if scaled to a very + different size. + + \sa setLegendTitleFontRelSize, setAxisLabelsFont + */ + + +/** + \fn bool KDChartParams::legendTitleFontUseRelSize() const + + Returns whether the legend title font size is used. + + \return whether the fixed legend title font size is used. + \sa setLegendTitleFontRelSize, setLegendTitleFont + */ + + +/** + \fn void KDChartParams::setLegendTitleFontRelSize( int legendTitleFontRelSize ) + + + Specifies the legend title relative font size. + + \param legendTitleFontRelSize the relative legend size. + If this value unequals zero the absolute value is per thousand + of of the average value of the printable area height and width. + This will make the legend title look nice even if scaled to a very + different size. + + \sa setLegendTitleFontUseRelSize, setLegendTitleFont + */ + + +/** + \fn int KDChartParams::legendTitleFontRelSize() const + + Returns the relative legend title font size. + + \return the relative legend title font size. + \sa setLegendTitleFontRelSize, setLegendTitleFontUseRelSize + */ + + +/** + \fn void KDChartParams::setLegendSpacing( uint space ) + + Specifies how much space in pixels there should be between the legend + and the data display. The default is 20 pixels. + + \param space the space in pixels between the legend and the data + display + \sa legendSpacing + */ + + +/** + \fn uint KDChartParams::legendSpacing() const + + Returns how much space in pixels there is between the legend and the + data display. + + \return the space in pixels between the legend and the data display + \sa setLegendSpacing + */ + +//@} +// END LEGENDS + +// START AXES +/** @name Axis methods. + + These methods query and set axis parameters. + */ +//@{ + +/** + \fn void KDChartParams::setAxisType( uint n, const KDChartAxisParams::AxisType axisType ) + + Specifies the axis type. + + \param n the number of the axis settings to be modified + \param axisType the type of the axis. + \sa axisVisible + */ + + +/** + \fn void KDChartParams::setAxisVisible( uint n, const bool axisVisible ) + + Specifies if the axis will be drawn. The default is false. + + \param n the number of the axis settings to be modified + \param axisVisible set true to make visible the respective axis. + \sa axisVisible + */ + + +/** + \fn bool KDChartParams::axisVisible( uint n ) const + + Returns if the axis will be drawn. + + \return if the axis is visible or not. + \sa setAxisVisible + */ + + +/** + Specifies whether a grid should be drawn at the chart data area. + + The grid will show a solid line for each delimiter. + (or a line with a pattern defined by \c KDChartAxisParams::setAxisGridStyle(), resp.) + + \note If true and axisShowSubDelimiters is also true the grid + will show a thin dotted line for each sub-delimiter. + (or a line with a pattern defined by \c KDChartAxisParams::setAxisGridSubStyle(), resp.) + + \param n the number of the axis for which to specify whether a grid should be drawn. + \param axisShowGrid if true a grid will be drawn on the chart data area. + \sa axisShowGrid, setAxisGridStyle, setAxisGridSubStyle + */ +void KDChartParams::setAxisShowGrid( uint n, bool axisShowGrid ) +{ + if ( n < KDCHART_MAX_AXES ) { + _axisSettings[ n ].params.setAxisShowGrid( axisShowGrid ); + emit changed(); + } +} + +/** + Returns if any grid will be drawn at the data area. + + \return if any grid will be drawn at the data area. + \sa KDChartAxisParams::setAxisShowGrid + */ +bool KDChartParams::showGrid() const +{ + for ( uint i = 0; i < KDCHART_MAX_AXES; ++i ) { + if ( _axisSettings[ i ].params.axisVisible() + && _axisSettings[ i ].params.axisShowGrid() ) + return true; + } + return false; +} + +/** + Specifies which data are to be represented by a given axis.<br> + If you specify a valid dataset-number for parameter dataset + you may use the same number or a greater number for + parameter dataset2 to specify a range of contiguous series + of datasets.<br> + If you specify <b>KDCHART_ALL_DATASETS</b> for dataset + the value of dataset2 does not matter.<br> + By setting the \c chart parameter you may specify which chart is + represented by the axis, you may use this when displaying + more than one chart in the same widget. + + Calling setAxisDataset() with a dataset number will automatically set + the visability flag of the respective axis (or axes, resp.) while calling + it with KDCHART_NO_DATASET will clear the visibility flag so there is no + need to explicitely call \c setAxisVisible() after calling setAxisDatasets(). + + \note If you specify \c KDCHART_ALL_DATASETS the axis will + represent all the datasets with SourceMode DataEntry + (see \c setChartSourceMode for explanation) that have been + specified for this chart.<br> + + \param n the number of the axis for which to specify the dataset(s) + \param dataset the dataset represented by axis \c n + + \sa axisDatasets, chartAxes, setChartSourceMode, setAdditionalChartType + */ +void KDChartParams::setAxisDatasets( uint n, uint dataset, + uint dataset2, + uint chart ) +{ + uint a1 = ( KDCHART_ALL_AXES == n ) + ? 0 + : QMIN( n, KDCHART_MAX_AXES-1 ); + uint a2 = ( KDCHART_ALL_AXES == n ) + ? KDCHART_MAX_AXES-1 + : QMIN( n, KDCHART_MAX_AXES-1 ); + for( uint i = a1; i <= a2; ++i ) { + _axisSettings[ i ].params.setAxisVisible( KDCHART_NO_DATASET != dataset ); + _axisSettings[ i ].dataset = dataset; + _axisSettings[ i ].dataset2 = + ( KDCHART_ALL_DATASETS == dataset + || KDCHART_NO_DATASET == dataset + || KDCHART_ALL_DATASETS == dataset2 + || KDCHART_NO_DATASET == dataset2 ) + ? dataset + : dataset2; + _axisSettings[ i ].chart = chart; + } + emit changed(); +} + +/** + Returns which data are to be represented by a given axis. + ( see explanation given with \c setAxisDatasets() ) + + \param n the axis for which to return the dataset number(s) + \param dataset the starting number of the series of datasets represented by axis \c n or KDCHART_ALL_DATASETS + \param dataset2 the end number of the series of datasets + \param chart the chart represented by axis \c n + \return true if a valid axis number was specified by parameter n + \sa setAxisDataset, chartAxes + */ +bool KDChartParams::axisDatasets( uint n, uint& dataset, + uint& dataset2, uint& chart ) const +{ + bool bOk = ( n < KDCHART_MAX_AXES ); + if ( bOk ) { + dataset = _axisSettings[ n ].dataset; + dataset2 = _axisSettings[ n ].dataset2; + chart = _axisSettings[ n ].chart; + } + return bOk; +} + + +/** + Returns which ORDINATE axis(axes) is(are) representing a given chart. + This will look for the following axes: AxisPosLeft, AxisPosRight, + AxisPosLeft2, AxisPosRight2. + + It is allowed to specify the same variable as axis1, axis2, axis3, + axis4 - when doing this you will get the last axis number that was + found for this chart. In case you are sure there is only one axis + per chart you may act that way. + + \param chart the chart for which to return the axis number(s) + \param cnt the count of axis numbers returned + \param axes on return, a vector with the numbers of the axes + \return true if at least one axis is representing the chart \c chart + \sa setAxisDataset, axisDataset + */ +bool KDChartParams::chartAxes( uint chart, uint& cnt, AxesArray& axes ) const +{ + cnt = 0; + axes.resize( KDCHART_CNT_ORDINATES ); + for ( int i2 = 0; i2 < KDCHART_CNT_ORDINATES; ++i2 ) { + axes[ i2 ] = KDCHART_NO_AXIS; + } + for ( uint i = 0; i < KDCHART_MAX_AXES; ++i ) { + if ( chart == _axisSettings[ i ].chart + && ( KDChartAxisParams::AxisPosLeft == i + || KDChartAxisParams::AxisPosRight == i + || KDChartAxisParams::AxisPosLeft2 == i + || KDChartAxisParams::AxisPosRight2 == i ) ) { + for( int j = 0; j < KDCHART_CNT_ORDINATES; ++j ) { + if( KDCHART_NO_AXIS == axes[ j ] || axes[ j ] == i ) { + if( KDCHART_NO_AXIS == axes[ j ] ) { + ++cnt; + axes[ j ] = i; + } + break; + } + } + } + } + return (0 < cnt); +} + + + + +/** + \fn QRect KDChartParams::axisArea( const uint n ) const + + Returns the true axis area rectangle as it was was calculate + by KD Chart. + + \param n the number of the axis + + \note This special function may be called *after* calling + KDChart::setupGeometry(). Normally you don't need to call + it at all, its only purpose is to provide you with a way to + retrieve the true position and size of an axis area. + + \sa dataArea + */ + +/** + \fn QRect KDChartParams::legendArea() const + + Returns the true legend area rectangle as it was was calculate + by KD Chart. + + \note This special function may be called *after* calling + KDChart::setupGeometry(). Normally you don't need to call + it at all, its only purpose is to provide you with a way to + retrieve the true position and size of an legend area. + + \sa dataArea + */ + +/** + \fn QRect KDChartParams::dataArea() const + + Returns the true data area rectangle as it was was calculate + by KD Chart. + + \note This special function may be called *after* calling + KDChart::setupGeometry(). Normally you don't need to call + it at all, its only purpose is to provide you with a way to + retrieve the true position and size of the data area. + + \sa axisArea + */ + + +/** + \fn void KDChartParams::setAxisArea( const uint n, const QRect& areaRect ) + + Specifies the true axis area rectangle. + + \note Do <b>not call</b> this function unless you are knowing + exactly what you are doing. \c setAxisTrueAreaRect is normally + reserved for internal usage by methods calculating the area + size based upon \c axisAreaMin and \c axisAreaMax. Thus the + signal \c changed() is not sended here. + + \sa axisAreaMax, axisAreaMin, setAxisAreaMode, setAxisAreaMin + \sa setAxisArea + */ + + +/** + \fn void KDChartParams::setAxisLabelsTouchEdges( uint n, bool axisLabelsTouchEdges ) + + Specifies whether the axis labels start and end at the + edges of the charts instead being positioned in the + middle of the first data point (or the last one, resp.) + + \note If you decide to call setAxisLabelsTouchEdges() to + override the default settings of abscissa axes + (AxisPosBottom, AxisPosTop, AxisPosBottom2, AxisPosTop2) + make sure to call it again each time you have called + setChartType() since your settings will be overwritten + when changing the chart type. + + \param axisLabelsTouchEdges if the axis labels start and end at + the edges of the charts instead being positioned in the + middle of the first data point (or the last one, resp.) + + \sa axisLabelsTouchEdges + */ + + +/** + \fn void KDChartParams::setAxisLabelsVisible( uint n, bool axisLabelsVisible ) + + Specifies whether the axis labels of an axis are to be shown. + + \param axisLabelsVisible if true the labels of this axis will be drawn. + + \sa setAxisLabelsFont + */ + + +/** + Specifies the axis labels font for one axis. + + \note The font size will be ignored if \c axisLabelsFontSize is not zero, + in this case the font size will be calculated dynamically using + the value axisLabelsFontSize. + + \param axisLabelsFont the font to be used for the axis' labels. + \param axisLabelsFontSize the (fixed or relative) axis font size. + If this value is less than zero the absolute value is per thousand + of the average value of the printable area height and width + to be used. This will make the axis look the same even if scaled + to very different size. + \param axisLabelsColor the axis labels colour. + + \sa setAxisLabelsVisible + \sa setAxisLabelsFontUseRelSize, setAxisLabelsFontRelSize + \sa KDChartAxisParams::setAxisLabelsFontMinSize + */ +void KDChartParams::setAxisLabelsFont( uint n, + QFont axisLabelsFont, + int axisLabelsFontSize, + QColor axisLabelsColor ) +{ + if ( n < KDCHART_MAX_AXES ) { + bool extraSize = (0 != axisLabelsFontSize); + QFont theFont( axisLabelsFont ); + bool useFontFixedSize = true; + if ( extraSize ){ + if( 0 > axisLabelsFontSize ){ + useFontFixedSize = false; + _axisSettings[ n ].params.setAxisLabelsFontRelSize( -axisLabelsFontSize ); + }else{ + theFont.setPointSize( axisLabelsFontSize ); + } + } + _axisSettings[ n ].params.setAxisLabelsFont( theFont, useFontFixedSize ); + _axisSettings[ n ].params.setAxisLabelsColor( axisLabelsColor ); + } +} + + +/** + \fn void KDChartParams::setAxisLabelTextParams( uint n, bool axisSteadyValueCalc, + KDChartData axisValueStart, KDChartData axisValueEnd, + double axisValueDelta, int axisDigitsBehindComma, + int axisMaxEmptyInnerSpan, + KDChartAxisParams::LabelsFromDataRow takeLabelsFromDataRow, + int labelTextsDataRow, QStringList* axisLabelStringList, + QStringList* axisShortLabelsStringList ) + + Specifies the label text parameters for one axis. + + \sa KDChartAxisParams::setAxisLabelTextParams + */ + + +/** + Specifies a \c QStringList which the axis label texts for one + axis are to be taken from, the optional short-labels parameter + (if not zero) may be used to specify an alternative list of + strings that are to be displayed in case the axis area size + is too small for showing the full-length names. + + \note Normally axis labeling starts with the list's first string + and end with its last string, but by specifying a start and an + end value as additional parameters you can make KDChart repeat + the strings between these two values only, as shown here: + + \verbatim + QStringList abscissaNames; + abscissaNames << "Sunday" << "Monday" << "Tuesday" << "Wednesday" + << "Thursday" << "Friday" << "Saturday"; + QStringList abscissaShortNames; + abscissaShortNames << "Sun" << "Mon" << "Tue" << "Wed" + << "Thu" << "Fri" << "Sat"; + + setAxisLabelStringParams( KDChartAxisParams::AxisPosBottom, + &names, &shortNames, + "Monday", "Friday") + \endverbatim + + + \param n the ID of the axis + \param QStringList* axisLabelStringList points to the list of labels to be displayed + \param QStringList* axisShortLabelStringList points to + an alternative list of short names to be displayed if the long labels take too much place + \param QString valueStart ( default null ) the label to begin with + \param QString valueEnd ( default null ) the label to end with + + \sa KDChartAxisParams::setAxisLabelStringLists + */ +void KDChartParams::setAxisLabelStringParams( uint n, + QStringList* axisLabelStringList, + QStringList* axisShortLabelStringList, + const QString& valueStart, + const QString& valueEnd ) +{ + if ( n < KDCHART_MAX_AXES ) { + _axisSettings[ n ].params.setAxisLabelStringLists( axisLabelStringList, + axisShortLabelStringList, + valueStart, + valueEnd ); + emit changed(); + } +} + + +/** + Specifies the parameters for one axis. + + \param axisParams the axis parameters to be used. + \sa axisParams, AxisParams + */ +void KDChartParams::setAxisParams( uint n, + const KDChartAxisParams& axisParams ) +{ + if ( n < KDCHART_MAX_AXES ) { + _axisSettings[ n ].params = axisParams; + emit changed(); + } +} + + +/** + \fn const KDChartAxisParams& KDChartParams::axisParams( uint n ) const + + Returns the parameters that are currently defined for axis n. + + \return all the parameters defined for axis n. If called with invalid + value for \c n it returns the default axis parameters. + \sa setAxisParams, AxisParams + */ + + +//@} +// END AXES + +// START HEADERFOOTER + +/** @name Header and footer methods. + + These methods query and set header and footer parameters. + + The names of the Header and Footer sections. + + You may use up to 9 header and/or up to 9 footer sections. + + \li Up to three <b>leading headers</b> ( \c HdFtPosHeader0 , \c + HdFtPosHeader0L , \c HdFtPosHeader0R ) may be drawn at the very upper + edge of the printable area. <br> + You might want to use those sections to show some marginal information + like department name, print date, page number... <br> + <b>Note:</b> Those headers <b>share the same area</b> so make sure to + specify propper horizontal alignment for each section when using more than + one of them. By default \c HdFtPosHeader0 has centered alignement, + \c HdFtPosHeader0L is aligned to the left and \c HdFtPosHeader0R to the + right side. All of them are vertically aligned to the bottom, you may + change this at your own risk: the resulting output might look strange. <br> + <b>also note:</b> Alignment to bottom here means their baselines are + horizontally adjusted to each other when more than one of them is used - it + does <b>not</b> mean the <b>lowest pixel positions</b> of their output will + be at the very same y coordinate since those might vary depending from the + font size and or the font size you have specified for each of the sections. + + \li Up to three <b>main headers</b> ( \c HdFtPosHeader , \c + HdFtPosHeaderL , \c HdFtPosHeaderR ) may be drawn under the + leading header(s). <br> + You could use this headers to show the main information such as project name, + chart title or period of time (e.g. census year). <br> + Like their counterparts they share the same part of the printable area so the + restrictions regarding alignment mentioned above apply also to these three + sections. + + \li Up to three <b>additional headers</b> ( \c HdFtPosHeader2 , \c + HdFtPosHeader2L , \c HdFtPosHeader2R ) may be drawn under the main + header(s). <br> + This headers could show additional information such as project phase, chart + sub-title or sub-period of time (e.g. census quarter-year). <br> + Like their counterparts they share the same part of the printable area so the + restrictions regarding alignment mentioned above apply also to these three + sections. + + <p> + + \li Up to three <b>main footers</b> ( \c HdFtPosFooter , \c + HdFtPosFooterL , \c HdFtPosFooterR ) may be drawn under the chart data + area (or the legend that might be shown below that area, resp.). <br> + You might want to use these footers instead of (or additional to) the + main header(s) for showing the main information... <br> + Like their header-counterparts they share the same part of the printable area + so the restrictions regarding alignment mentioned above apply also to these + three sections. + + \li Up to three <b>additional footers</b> ( \c HdFtPosFooter2 , \c + HdFtPosFooter2L , \c HdFtPosHeader2R ) may be drawn under the main + footer(s). <br> + This footers could show additional information instead of (or additional to) + the additional header(s). <br> + Like their counterparts they share the same part of the printable area so the + restrictions regarding alignment mentioned above apply also to these three + sections. + + \li Up to three <b>trailing footers</b> ( \c HdFtPosFooter0 , \c + HdFtPosFooter0L , \c HdFtPosFooter0R ) may be drawn at the very lower + edge of the printable area: <b>under</b> the other footer sections <br> + You might want to use those sections to show some marginal information + instead of (or additional to) the leading header(s). <br> + Like their counterparts they share the same part of the printable area so the + restrictions regarding alignment mentioned above apply also to these three + sections. + +\note The names \c HdFtPosHeader or \c HdFtPosFooter are the basic names also returned by \c basicAxisPos. + +<p> +<b>Usage:</b> Please see example code at \c setHeaderFooterText function explanation. + +\sa setHeaderFooterText, headerFooterText +\sa setHeaderFooterFont, headerFooterFont +\sa setHeaderFooterFontUseRelSize, headerFooterFontUseRelSize +\sa setHeaderFooterFontRelSize, headerFooterFontRelSize +*/ +//@{ + + +/** + \enum KDChartParams::HdFtPos + + This enum lists the positions that headers and footers can take. + */ + + +/** + \enum KDChartParams::HdFtPosSTART, + + start of all sections + */ + +/** + \enum KDChartParams::HdFtPosHeadersSTART + + start of header sections + */ + +/** + \enum KDChartParams::HdFtPosHeader0 + + leading header (at the very top of the printable area) + */ + +/** + \enum KDChartParams::HdFtPosHeader0L + + another leading header, by default left-aligned + */ + +/** + \enum KDChartParams::HdFtPosHeader0R + + yet another leading header, by default right-aligned + */ + +/** + \enum KDChartParams::HdFtPosHeader + + main header (below the leading header) + */ + +/** + \enum KDChartParams::HdFtPosHeaderL + + another main header, by default left-aligned + */ + +/** + \enum KDChartParams::HdFtPosHeaderR + + yet another main header, by default right-aligned + */ + +/** + \enum KDChartParams::HdFtPosHeader2 + + additional header (below the main header) + */ + +/** + \enum KDChartParams::HdFtPosHeader2L + + another additional header, by default left-aligned + */ + +/** + \enum KDChartParams::HdFtPosHeader2R + + yet another additional header, by default right-aligned + */ + +/** + \enum KDChartParams::HdFtPosHeadersEND + + end of header sections + */ + +/** + \enum KDChartParams::HdFtPosFootersSTART + + start of footer sections + */ + + +/** + \enum KDChartParams::HdFtPosFooter + + main footer (below the data area or the bottom legend (if any), resp.) + */ + +/** + \enum KDChartParams::HdFtPosFooterL + + another main footer, by default left-aligned + */ + +/** + \enum KDChartParams::HdFtPosFooterR + + yet another main footer, by default right-aligned + */ + + +/** + \enum KDChartParams::HdFtPosFooter2 + + additional footer (below the main footer) + */ + +/** + \enum KDChartParams::HdFtPosFooter2L + + another additional footer, by default left-aligned + */ + +/** + \enum KDChartParams::HdFtPosFooter2R + + yet another additional footer, by default right-aligned + */ + + +/** + \enum KDChartParams::HdFtPosFooter0 + + trailing footer (below the additional footer) + */ + +/** + \enum KDChartParams::HdFtPosFooter0L + + another trailing footer, by default left-aligned + */ + +/** + \enum KDChartParams::HdFtPosFooter0R + + yet another trailing footer, by default right-aligned + */ + +/** + \enum KDChartParams::HdFtPosFootersEND + + end of footer sections + */ + +/** + \enum KDChartParams::HdFtPosEND + + end of all sections + */ + + +/** + Specifies the text to be displayed in the header (or footer, resp.) + section specified by \c pos. + The default is not to display any header text nor footer text. + + \note To learn about the different header (or footer, resp.) + sections please see information given at \c HdFtPos enum explanation. + + \param pos the section in which the text is to be shown + \param text the text to be shown + \sa HdFtPos, headerFooterText() + \sa setHeaderFooterFont, headerFooterFont + \sa setHeaderFooterFontUseRelSize, headerFooterFontUseRelSize + \sa setHeaderFooterFontRelSize, headerFooterFontRelSize + \sa setHeader1Text(), header1Text() + \sa setHeader2Text(), header2Text() + \sa setFooterText(), footerText() + */ +void KDChartParams::setHeaderFooterText( uint pos, const QString& text ) +{ + if ( HdFtPosEND >= pos ) { + _hdFtParams[ pos ]._text = text; + emit changed(); + } +} + + +/** + Returns the text that is displayed in the header (or footer, resp.) + section specified by \c pos. + + \note To learn about the different header (or footer, resp.) + sections please see information given at \c HdFtPos enum explanation. + + \param pos the section for which to return the text + \return the displayed text + \sa HdFtPos, setHeaderFooterText() + \sa setHeaderFooterFont, headerFooterFont + \sa setHeaderFooterFontUseRelSize, headerFooterFontUseRelSize + \sa setHeaderFooterFontRelSize, headerFooterFontRelSize + \sa setHeader1Text(), header1Text() + \sa setHeader2Text(), header2Text() + \sa setFooterText(), footerText() + */ +QString KDChartParams::headerFooterText( uint pos ) const +{ + if ( HdFtPosEND >= pos ) + return _hdFtParams[ pos ]._text; + else + return QString::null; +} + + + +/** + \note This methode for internal use. + Return the position and size of one header/footer area. + <b>Will return valid data only if called after KDChartPainter::setupGeometry()!</b> + */ +const QRect& KDChartParams::headerFooterRect( uint pos ) const +{ + if ( HdFtPosEND >= pos ) + return _hdFtParams[ pos ].rect(); + else + return _noRect; +} + + +/** + Specifies the colour to be used for the header text (or footer text, resp.) + of the header/footer section specified by \c pos. + The default colour is <b>QColor( Qt::black )</b>. + + \note To learn about the different header (or footer, resp.) + sections please see information given at \c HdFtPos enum explanation. + + \param pos the section for which the colour is to be changed + \param color the text colour to be used + \sa HdFtPos, headerFooterText() + \sa setHeaderFooterFont, headerFooterFont + \sa setHeaderFooterFontUseRelSize, headerFooterFontUseRelSize + \sa setHeaderFooterFontRelSize, headerFooterFontRelSize + \sa setHeader1Text(), header1Text() + \sa setHeader2Text(), header2Text() + \sa setFooterText(), footerText() + */ +void KDChartParams::setHeaderFooterColor( uint pos, const QColor color ) +{ + if ( HdFtPosEND >= pos ) { + _hdFtParams[ pos ]._color = color; + emit changed(); + } +} + + +/** + Returns the colour used for the header text (or footer text, resp.) + of the header/footer section specified by \c pos. + + \note To learn about the different header (or footer, resp.) + sections please see information given at \c HdFtPos enum explanation. + + \param pos the section for which to return the text colour + \return the text colour + \sa HdFtPos, setHeaderFooterText() + \sa setHeaderFooterFont, headerFooterFont + \sa setHeaderFooterFontUseRelSize, headerFooterFontUseRelSize + \sa setHeaderFooterFontRelSize, headerFooterFontRelSize + \sa setHeader1Text(), header1Text() + \sa setHeader2Text(), header2Text() + \sa setFooterText(), footerText() + */ +QColor KDChartParams::headerFooterColor( uint pos ) const +{ + if ( HdFtPosEND >= pos ) + return _hdFtParams[ pos ]._color; + else + return QColor( Qt::black ); +} + + +/** + Specifies the font to be used in the header (or footer, + resp.) section specified by \c pos.. + The default is a 14pt Helvetica font. + + \note To learn about the different header (or footer, resp.) + sections please see information given at \c HdFtPos enum explanation. + + \param pos the section in which the font is to be used + \param font the font to use + \sa HdFtPos, setHeaderFooterText(), headerFooterText() + \sa headerFooterFont() + \sa setHeaderFooterFontUseRelSize, headerFooterFontUseRelSize + \sa setHeaderFooterFontRelSize, headerFooterFontRelSize + \sa setHeader1Font(), header1Font() + \sa setHeader2Font(), header2Font() + \sa setFooterFont(), footerFont() + \sa setHeader1Text(), header1Text() + \sa setHeader2Text(), header2Text() + \sa setFooterText(), footerText() + */ +void KDChartParams::setHeaderFooterFont( uint pos, const QFont& font, + bool fontUseRelSize, + int fontRelSize ) +{ + if ( HdFtPosEND >= pos ) { + _hdFtParams[ pos ]._font = font; + _hdFtParams[ pos ]._fontUseRelSize = fontUseRelSize; + _hdFtParams[ pos ]._fontRelSize = fontRelSize; + emit changed(); + } +} + + +/** + Returns the font used in the header (or footer, resp.) + section specified by \c pos. + + \note To learn about the different header (or footer, resp.) + sections please see information given at \c HdFtPos enum explanation. + + \param pos the section for which to return the font + \return the used font + \sa HdFtPos, setHeaderFooterText(), headerFooterText() + \sa setHeaderFooterFont() + \sa setHeaderFooterFontUseRelSize, headerFooterFontUseRelSize + \sa setHeaderFooterFontRelSize, headerFooterFontRelSize + \sa setHeader1Font(), header1Font() + \sa setHeader2Font(), header2Font() + \sa setFooterFont(), footerFont() + \sa setHeader1Text(), header1Text() + \sa setHeader2Text(), header2Text() + \sa setFooterText(), footerText() + */ +QFont KDChartParams::headerFooterFont( uint pos ) const +{ + if ( HdFtPosEND >= pos ) + return _hdFtParams[ pos ]._font; + else + return QApplication::font(); +} + + +/** + Returns is the font size used in the header (or footer, resp.) + section specified by \c pos will be calculated dynamically. + + \note To learn about the different header (or footer, resp.) + sections please see information given at \c HdFtPos enum explanation. + + \param pos the section for which to return the font calculation flag + \return the font calculation flag + \sa HdFtPos, setHeaderFooterText(), headerFooterText() + \sa setHeaderFooterFont() + \sa setHeaderFooterFontUseRelSize, headerFooterFontUseRelSize + \sa setHeaderFooterFontRelSize, headerFooterFontRelSize + \sa setHeader1Font(), header1Font() + \sa setHeader2Font(), header2Font() + \sa setFooterFont(), footerFont() + \sa setHeader1Text(), header1Text() + \sa setHeader2Text(), header2Text() + \sa setFooterText(), footerText() + */ +bool KDChartParams::headerFooterFontUseRelSize( uint pos ) const +{ + if ( HdFtPosEND >= pos ) + return _hdFtParams[ pos ]._fontUseRelSize; + else + return false; +} + + +/** + Returns the factor used for dynamical calculation of + the font size of the text in the header (or footer, resp.) + section specified by \c pos. + + \note To learn about the different header (or footer, resp.) + sections please see information given at \c HdFtPos enum explanation. + + \param pos the section for which to return the font size factor + \return the font size factor + \sa HdFtPos, setHeaderFooterText(), headerFooterText() + \sa setHeaderFooterFont() + \sa setHeaderFooterFontUseRelSize, headerFooterFontUseRelSize + \sa setHeaderFooterFontRelSize, headerFooterFontRelSize + \sa setHeader1Font(), header1Font() + \sa setHeader2Font(), header2Font() + \sa setFooterFont(), footerFont() + \sa setHeader1Text(), header1Text() + \sa setHeader2Text(), header2Text() + \sa setFooterText(), footerText() + */ +int KDChartParams::headerFooterFontRelSize( uint pos ) const +{ + if ( HdFtPosEND >= pos ) + return _hdFtParams[ pos ]._fontRelSize; + else + return 10; +} + + +/** + \fn void KDChartParams::setHeader1Text( const QString& text ) + + Specifies the text to be displayed as the first header line. The + default is not to display any header text. + + \note This function provided for convenience only. <br> + To gain the <b>full</b> benefits of the header/footer management + please consider learning about the different header (or footer, + resp.) sections, see information given at \c HdFtPos enum + explanation. <br> + The 'better' way to specify content and settings of headers or + footers is to call \c setHeaderFooterText ( or \c setHeaderFooterFont + , \c setHeaderFooterFontUseRelSize , \c setHeaderFooterFontRelSize , + resp.) while specifying the appropriate \c HdFtPos. <br> + The canonical way to retrieve settings and content of headers or + footers is to call \c headerFooterText (or \c headerFooterFont , + \c headerFooterFontUseRelSize , \c headerFooterFontRelSize, resp.). + + \param text the text to display + \sa HdFtPos, setHeaderFooterText(), headerFooterText() + \sa header2Text(), setHeader1Text(), header1Text(), setFooterText(), + footerText() + */ + + +/** + \fn QString KDChartParams::header1Text() const + + Returns the text that is displayed as the first header line. + + \note This function provided for convenience only. <br> + To gain the <b>full</b> benefits of the header/footer management + please consider learning about the different header (or footer, + resp.) sections, see information given at \c HdFtPos enum + explanation. <br> + The 'better' way to specify content and settings of headers or + footers is to call \c setHeaderFooterText ( or \c setHeaderFooterFont + , \c setHeaderFooterFontUseRelSize , \c setHeaderFooterFontRelSize , + resp.) while specifying the appropriate \c HdFtPos. <br> + The canonical way to retrieve settings and content of headers or + footers is to call \c headerFooterText (or \c headerFooterFont , + \c headerFooterFontUseRelSize , \c headerFooterFontRelSize, resp.). + + \return the displayed text + \sa HdFtPos, setHeaderFooterText(), headerFooterText() + \sa setHeader2Text(), setHeader1Text(), header1Text() + \sa setFooterText(), footerText() + */ + + +/** + \fn void KDChartParams::setHeader1Font( const QFont& font ) + + Specifies the font to be used for displaying the first header line. + The default is a 14pt Helvetica font. + + \note This function provided for convenience only. <br> + To gain the <b>full</b> benefits of the header/footer management + please consider learning about the different header (or footer, + resp.) sections, see information given at \c HdFtPos enum + explanation. <br> + The 'better' way to specify content and settings of headers or + footers is to call \c setHeaderFooterText ( or \c setHeaderFooterFont + , \c setHeaderFooterFontUseRelSize , \c setHeaderFooterFontRelSize , + resp.) while specifying the appropriate \c HdFtPos. <br> + The canonical way to retrieve settings and content of headers or + footers is to call \c headerFooterText (or \c headerFooterFont , + \c headerFooterFontUseRelSize , \c headerFooterFontRelSize, resp.). + + \param font the font to use + \sa HdFtPos, setHeaderFooterFont(), headerFooterFont() + \sa setHeaderFooterFontUseRelSize(), headerFooterFontUseRelSize() + \sa setHeaderFooterFontRelSize(), headerFooterFontRelSize() + \sa header2Font(), setHeader2Text(), header2Text() + \sa setHeader1Font(), header1Font() + \sa setFooterFont(), footerFont() + */ + + +/** + \fn QFont KDChartParams::header1Font() const + + Returns the font used for displaying the second header line. + + \note This function provided for convenience only. <br> + To gain the <b>full</b> benefits of the header/footer management + please consider learning about the different header (or footer, + resp.) sections, see information given at \c HdFtPos enum + explanation. <br> + The 'better' way to specify content and settings of headers or + footers is to call \c setHeaderFooterText ( or \c setHeaderFooterFont + , \c setHeaderFooterFontUseRelSize , \c setHeaderFooterFontRelSize , + resp.) while specifying the appropriate \c HdFtPos. <br> + The canonical way to retrieve settings and content of headers or + footers is to call \c headerFooterText (or \c headerFooterFont , + \c headerFooterFontUseRelSize , \c headerFooterFontRelSize, resp.). + + \return the used font + \sa HdFtPos, setHeaderFooterFont(), headerFooterFont() + \sa setHeaderFooterFontUseRelSize(), headerFooterFontUseRelSize() + \sa setHeaderFooterFontRelSize(), headerFooterFontRelSize() + \sa setHeader2Font(), setHeader2Text(), header2Text(), + setHeader1Font(), header1Font(), setFooterFont(), footerFont() + */ + + +/** + \fn void KDChartParams::setHeader2Text( const QString& text ) + + Specifies the text to be displayed as the second header line. The + default is not to display any header text. + + \note This function provided for convenience only. <br> + To gain the <b>full</b> benefits of the header/footer management + please consider learning about the different header (or footer, + resp.) sections, see information given at \c HdFtPos enum + explanation. <br> + The 'better' way to specify content and settings of headers or + footers is to call \c setHeaderFooterText ( or \c setHeaderFooterFont + , \c setHeaderFooterFontUseRelSize , \c setHeaderFooterFontRelSize , + resp.) while specifying the appropriate \c HdFtPos. <br> + The canonical way to retrieve settings and content of headers or + footers is to call \c headerFooterText (or \c headerFooterFont , + \c headerFooterFontUseRelSize , \c headerFooterFontRelSize, resp.). + + \param text the text to display + \sa HdFtPos, setHeaderFooterText(), headerFooterText() + \sa header2Text(), setHeader1Text(), header1Text(), setFooterText(), + footerText() + */ + + +/** + \fn QString KDChartParams::header2Text() const + + Returns the text that is displayed as the second header line. + + \note This function provided for convenience only. <br> + To gain the <b>full</b> benefits of the header/footer management + please consider learning about the different header (or footer, + resp.) sections, see information given at \c HdFtPos enum + explanation. <br> + The 'better' way to specify content and settings of headers or + footers is to call \c setHeaderFooterText ( or \c setHeaderFooterFont + , \c setHeaderFooterFontUseRelSize , \c setHeaderFooterFontRelSize , + resp.) while specifying the appropriate \c HdFtPos. <br> + The canonical way to retrieve settings and content of headers or + footers is to call \c headerFooterText (or \c headerFooterFont , + \c headerFooterFontUseRelSize , \c headerFooterFontRelSize, resp.). + + \return the displayed text + \sa HdFtPos, setHeaderFooterText(), headerFooterText() + \sa setHeader2Text(), setHeader1Text(), header1Text(), setFooterText(), + footerText() + */ + + +/** + \fn void KDChartParams::setHeader2Font( const QFont& font ) + + Specifies the font to be used for displaying the second header line. The + default is a 12pt Helvetica font. + + \note This function provided for convenience only. <br> + To gain the <b>full</b> benefits of the header/footer management + please consider learning about the different header (or footer, + resp.) sections, see information given at \c HdFtPos enum + explanation. <br> + The 'better' way to specify content and settings of headers or + footers is to call \c setHeaderFooterText ( or \c setHeaderFooterFont + , \c setHeaderFooterFontUseRelSize , \c setHeaderFooterFontRelSize , + resp.) while specifying the appropriate \c HdFtPos. <br> + The canonical way to retrieve settings and content of headers or + footers is to call \c headerFooterText (or \c headerFooterFont , + \c headerFooterFontUseRelSize , \c headerFooterFontRelSize, resp.). + + \param font the font to use + \sa HdFtPos, setHeaderFooterFont(), headerFooterFont() + \sa setHeaderFooterFontUseRelSize(), headerFooterFontUseRelSize() + \sa setHeaderFooterFontRelSize(), headerFooterFontRelSize() + \sa header2Font(), setHeader2Text(), header2Text(), setHeader1Font(), header1Font(), + setFooterFont(), footerFont() + */ + + + +/** + \fn QFont KDChartParams::header2Font() const + + Returns the font used for displaying the second header line. + + \note This function provided for convenience only. <br> + To gain the <b>full</b> benefits of the header/footer management + please consider learning about the different header (or footer, + resp.) sections, see information given at \c HdFtPos enum + explanation. <br> + The 'better' way to specify content and settings of headers or + footers is to call \c setHeaderFooterText ( or \c setHeaderFooterFont + , \c setHeaderFooterFontUseRelSize , \c setHeaderFooterFontRelSize , + resp.) while specifying the appropriate \c HdFtPos. <br> + The canonical way to retrieve settings and content of headers or + footers is to call \c headerFooterText (or \c headerFooterFont , + \c headerFooterFontUseRelSize , \c headerFooterFontRelSize, resp.). + + \return the used font + \sa HdFtPos, setHeaderFooterFont(), headerFooterFont() + \sa setHeaderFooterFontUseRelSize(), headerFooterFontUseRelSize() + \sa setHeaderFooterFontRelSize(), headerFooterFontRelSize() + \sa setHeader2Font(), setHeader2Text(), header2Text(), + setHeader1Font(), header1Font(), setFooterFont(), footerFont() + */ + + +/** + \fn void KDChartParams::setFooterText( const QString& text ) + + Specifies the text to be displayed as the footer line. The + default is not to display any footer text. + + \note This function provided for convenience only. <br> + To gain the <b>full</b> benefits of the header/footer management + please consider learning about the different header (or footer, + resp.) sections, see information given at \c HdFtPos enum + explanation. <br> + The 'better' way to specify content and settings of headers or + footers is to call \c setHeaderFooterText ( or \c setHeaderFooterFont + , \c setHeaderFooterFontUseRelSize , \c setHeaderFooterFontRelSize , + resp.) while specifying the appropriate \c HdFtPos. <br> + The canonical way to retrieve settings and content of headers or + footers is to call \c headerFooterText (or \c headerFooterFont , + \c headerFooterFontUseRelSize , \c headerFooterFontRelSize, resp.). + + \param text the text to display + \sa HdFtPos, setHeaderFooterText(), headerFooterText() + \sa footerText(), setHeader1Text(), header1Text(), setHeader2Text(), + header2Text() + */ + + +/** + \fn QString KDChartParams::footerText() const + + Returns the text that is displayed as the footer line. + + \note This function provided for convenience only. <br> + To gain the <b>full</b> benefits of the header/footer management + please consider learning about the different header (or footer, + resp.) sections, see information given at \c HdFtPos enum + explanation. <br> + The 'better' way to specify content and settings of headers or + footers is to call \c setHeaderFooterText ( or \c setHeaderFooterFont + , \c setHeaderFooterFontUseRelSize , \c setHeaderFooterFontRelSize , + resp.) while specifying the appropriate \c HdFtPos. <br> + The canonical way to retrieve settings and content of headers or + footers is to call \c headerFooterText (or \c headerFooterFont , + \c headerFooterFontUseRelSize , \c headerFooterFontRelSize, resp.). + + \return the displayed text + \sa HdFtPos, setHeaderFooterText(), headerFooterText() + \sa setFooterText(), setHeader1Text(), header1Text(), setHeader2Text(), + header2Text() + */ + + +/** + \fn void KDChartParams::setFooterFont( const QFont& font ) + + Specifies the font to be used for displaying the footer line. The + default is a 12pt Helvetica font. + + \note This function provided for convenience only. <br> + To gain the <b>full</b> benefits of the header/footer management + please consider learning about the different header (or footer, + resp.) sections, see information given at \c HdFtPos enum + explanation. <br> + The 'better' way to specify content and settings of headers or + footers is to call \c setHeaderFooterText ( or \c setHeaderFooterFont + , \c setHeaderFooterFontUseRelSize , \c setHeaderFooterFontRelSize , + resp.) while specifying the appropriate \c HdFtPos. <br> + The canonical way to retrieve settings and content of headers or + footers is to call \c headerFooterText (or \c headerFooterFont , + \c headerFooterFontUseRelSize , \c headerFooterFontRelSize, resp.). + + \param font the font to use + \sa HdFtPos, setHeaderFooterFont(), headerFooterFont() + \sa setHeaderFooterFontUseRelSize(), headerFooterFontUseRelSize() + \sa setHeaderFooterFontRelSize(), headerFooterFontRelSize() + \sa footerFont(), setFooterText(), footerText(), setHeader1Font(), header1Font(), + setHeader2Font(), header2Font() + */ + + +/** + \fn QFont KDChartParams::footerFont() const + + Returns the font used for displaying the footer line. + + \note This function provided for convenience only. <br> + To gain the <b>full</b> benefits of the header/footer management + please consider learning about the different header (or footer, + resp.) sections, see information given at \c HdFtPos enum + explanation. <br> + The 'better' way to specify content and settings of headers or + footers is to call \c setHeaderFooterText ( or \c setHeaderFooterFont + , \c setHeaderFooterFontUseRelSize , \c setHeaderFooterFontRelSize , + resp.) while specifying the appropriate \c HdFtPos. <br> + The canonical way to retrieve settings and content of headers or + footers is to call \c headerFooterText (or \c headerFooterFont , + \c headerFooterFontUseRelSize , \c headerFooterFontRelSize, resp.). + + \return the used font + \sa HdFtPos, setHeaderFooterFont(), headerFooterFont() + \sa setHeaderFooterFontUseRelSize(), headerFooterFontUseRelSize() + \sa setHeaderFooterFontRelSize(), headerFooterFontRelSize() + \sa setFooterFont(), setFooterText(), footerText(), + setHeader1Font(), header1Font(), setHeader2Font(), header2Font() + */ + + +//@} +// END HEADERFOOTER + + + +// PRIVATE VARIABLES +/** + \var int KDChartParams::_globalLeadingLeft; + Stores the distance between the chart and the left border of the painter area. + */ + +/** + \var int KDChartParams::_globalLeadingTop; + + Stores the distance between the chart and the upper border of the painter area. + */ + + +/** + \var int KDChartParams::_globalLeadingRight; + + Stores the distance between the chart and the left border of the painter area. + */ + + +/** + \var int KDChartParams::_globalLeadingBottom; + + Stores the distance between the chart and the right border of the painter area. + */ + +/** + \var ChartType KDChartParams::_chartType; + + Stores the chart type. + */ + +/** + \var ChartType KDChartParams::_additionalChartType; + + Stores the additional chart type. + */ + +/** + \var int KDChartParams::_numValues; + + Stores how many of the values should be shown. -1 means all + available values. + */ + +/** + \var QFont KDChartParams::_defaultFont; + + Stores our default font used for many different purposes. + */ + +/** + \var KDChartParams::AreaMap + + The type used for storing the frame settings for all of the chart areas. + */ + +/** + \var AreaMap KDChartParams::_areaMap; + Stores the frame settings for all of the chart areas. + */ + +/** + \var CustomBoxMap KDChartParams::_customBoxDict; + + Stores the settings for all of the custom boxes. + */ + + +/** + \var QRect KDChartParams::_noRect; + + Stores an empty rectangle. + */ + +/** + \var KDFrame KDChartParams::_noFrame; + + Stores the default frame settings: no border, no corners, no background. + */ + +/** + \var KDChartFrameSettings KDChartParams::_noFrameSettings; + + Stores the default area settings: no frame, no inner gap, no outer gap. + */ + + +/** + \class KDChartParams::ModeAndChart + + Stores the SourceMode and the chart(s) for one dataset. + */ + + +/** + \var ModeAndChartMap KDChartParams::_dataSourceModeAndChart; + + Stores the SourceMode and the chart(s) for the datasets. + */ + +/** + \var typedef KDChartParams::ModeAndChartMap + + The type used for storing the source mode and charts of the datasets + */ + + +/** + \var bool KDChartParams::_setChartSourceModeWasUsed; + + Stores whether setChartSourceMode() was called or not + */ + +/** + \var QMap < uint, QColor > KDChartParams::_dataColors; + + Stores the colors for the datasets. + */ + +/** + \var uint KDChartParams::_maxDatasetSourceMode; + + Stores the highest dataset number for which a SourceMode has been + defined. Note that if you define a value for a dataset but not for a + dataset with a lower number (and there is neither a default value), the + source mode for that dataset with the lower number will be undefined. + */ + + +/** + \var KDChartPropertySetList KDChartParams::_propertySetList; + + Stores all property-sets that were defined. + Each of these sets may be assigned to an individual cell. + */ + + +/** + \var double KDChartParams::_shadowBrightnessFactor; + + Stores a factor to be used to adjust the + built-in brightness of shadow colors in + 3-dimensional drawings like e.g. 3D bar charts. + */ + + +/** + \var Qt::BrushStyle KDChartParams::_shadowPattern; + + Stores a fill style to be used for filling shadow area in + 3-dimensional drawings like e.g. 3D bar charts. + */ + +/** + \var bool KDChartParams::_threeDShadowColors; + + Stores whether shadowed colors are used for 3D effects. Only used + for 3D effects in charts that support these. + */ + + +/** + \var uint KDChartParams::_maxDatasetColor; + + Stores the highest dataset number for which a color has been + defined. Note that if you define a value for a dataset but not for a + dataset with a lower number (and there is neither a default value), the + color for that dataset with the lower number will be undefined. + */ + +/** + \var QMap < uint, QColor > KDChartParams::_dataColorsShadow1; + + Stores the shadow colors that are somewhat darker than the colors in + _dataColors. + */ + +/** + \var QMap < uint, QColor > KDChartParams::_dataColorsShadow2; + + Stores the shadow colors that are even more darker than the colors in + _dataColorsShadow1; + */ + +/** + \var QColor KDChartParams::_outlineDataColor; + + Stores the color to be used for outlines of data displays. + */ + +/** + \var uint KDChartParams::_outlineDataLineWidth; + + Stores the line width to be used for outlines of data displays. + */ + +/** + \var PenStyle KDChartParams::_outlineDataLineStyle; + + Stores the line style to be used for outlines of data displays. + */ + + + +/** + \struct PrintValueDataSettings KDChartParams.h + + Stores if and how a chart will print the data values near their + respective entries. + */ + + +/** + \var PrintDataValuesSettings KDChartParams::_printDataValuesSettings; + + Stores if and how the FIRST chart will print the data values + near their respective entries. + */ + +/** + \var PrintDataValuesSettings KDChartParams::_printDataValuesSettings2; + + Stores if and how the SECOND chart will print the data values + near their respective entries, in case two charts are to be + drawn sharing the same data area. + */ + +/** + \var bool KDChartParams::_allowOverlappingDataValueTexts; + + Stores whether a data value may be drawn near it's respective entry + even if it's region intersects with another data value text's region. + + By default this is FALSE to prevent ugly mutual overwriting of data + value texts and to speed up drawing of cahrts containing thousands + of data points. + */ + + +/** + \var BarChartSubType KDChartParams::_barChartSubType; + + Stores the bar chart subtype. Only used when chartType() == + Bar + */ + +/** + \var bool KDChartParams::_threeDBars; + + Stores whether the engine should draw the bars in 3D. Only + used if chartType() == Bar. + */ + +/** + \var int KDChartParams::_threeDBarAngle; + + Stores the angle used for 3D display. Only used if + threeDBars == true. + */ + +/** + \var double KDChartParams::_threeDBarDepth; + + Stores the depth of the 3D Effect used for 3D bars + in relation to the bar width. + Only used if chartType() == Bar and threeDBars() == true. + */ + + +/** + \var double KDChartParams::_cosThreeDBarAngle; + + The cosine in rad of the angle for 3D display. Only used if + threeDBars == true. + */ + +/** + \var int KDChartParams::_datasetGap; + + Stores the number of pixels between two dataset values. + */ + +/** + \var bool KDChartParams::_datasetGapIsRelative; + + Stores if the value set by \c setDatasetGap + is a per mille value of the chart data area width. + */ + +/** + \var int KDChartParams::_valueBlockGap; + + Stores the number of pixels between each value block. + */ + +/** + \var bool KDChartParams::_valueBlockGapIsRelative; + + Stores if the value set by \c setValueBlockGap + is a per mille value of the chart data area width. + */ + +/// LINES/AREAS-specific +/** + \var LineChartSubType KDChartParams::_lineChartSubType; + + Stores the line chart subtype. Only used when chartType() == + Line + */ + +/** + \var bool KDChartParams::_threeDLines; + + Stores whether 3D lines should be drawn. + */ + + +/** + \var int KDChartParams::_threeDLineDepth; + + Stores the depth of 3D lines. + */ + + +// documentation of this member variable temporary disabled: +// Feature is currently not supported, will be implemented +// by future versions of KDChart +/* + \var int KDChartParams::_threeDLineXRotation; + + Stores the X rotation of 3D lines. + */ + + +// documentation of this member variable temporary disabled: +// Feature is currently not supported, will be implemented +// by future versions of KDChart +/* + \var int KDChartParams::_threeDLineYRotation; + + Stores the Y rotation of 3D lines. + */ + + +/** + \var bool KDChartParams::_lineMarker; + + Stores whether line markers should be drawn. Only used when + chartType() == Line. + */ + + +/** + \var LineMarkerStyleMap KDChartParams::_lineMarkerStyles; + + Stores the styles of the line makers + */ + +/** + \var uint KDChartParams::_maxDatasetLineMarkerStyle; + + Stores the highest dataset number for which a line marker has been + defined. Note that if you define a value for a dataset but not for a + dataset with a lower number (and there is neither a default value), the + line marker for that dataset with the lower number will be undefined. + */ + +/** + \var QSize KDChartParams::_lineMarkerSize; + + Stores the size of the line markers. + */ + + +/** + \var QColor KDChartParams::_lineColor; + + Stores the color of lines in line charts. + */ + +/** + \var int KDChartParams::_lineWidth; + + Stores the line width of lines in line charts. + */ + +/** + \var PenStyle KDChartParams::_lineStyle; + + Stores the line style of lines in line charts. + */ + +/** + \var AreaChartSubType KDChartParams::_areaChartSubType; + + Stores the area chart subtype. Only used when chartType() == + Area + */ + + +/** + \var AreaLocation KDChartParams::_areaLocation; + + Stores whether the area above or below the value points should + be filled. + */ + + +/// POLAR-specific +/** + \var PolarChartSubType KDChartParams::_polarChartSubType; + + Stores the polar chart subtype. Only used when chartType() == + Polar + */ + +/** + \var bool KDChartParams::_polarMarker; + + Stores whether polar markers should be drawn. Only used when + chartType() == Polar. + */ + +/** + \var PolarMarkerStyleMap KDChartParams::_polarMarkerStyles; + + Stores the styles of the markers in a polar char. + */ + +/** + \var uint KDChartParams::_maxDatasetPolarMarkerStyle; + + Stores the highest dataset number for which a polar marker has been + defined. Note that if you define a value for a dataset but not for a + dataset with a lower number (and there is neither a default value), the + polar marker for that dataset with the lower number will be undefined. + */ + +/** + \var QSize KDChartParams::_polarMarkerSize; + + Stores the size of the polar markers. + */ + + +/** + \var int KDChartParams::_polarLineWidth; + + Stores the line width of lines in polar charts. + */ + + +/** + \var int KDChartParams::_polarZeroDegreePos; + + Stores the zero degree position in polar charts. + */ + + +/** + \var bool KDChartParams::_polarRotateCircularLabels; + + Stores whether to rotate circular labels in polar charts. + */ + +/** + \struct KDChartParams::_polarDelimsAndLabelStruct KDChartParams.h + + Stores whether to show circular delimiters and/or labels + at a given position in polar charts. + */ + +/** + \var _polarDelimsAndLabelStruct KDChartParams::_polarDelimsAndLabels[ 1 + KDCHART_MAX_POLAR_DELIMS_AND_LABELS_POS ]; + + Stores where to show circular delimiters and labels in polar charts. + */ + + + +// PIES/RINGS +/** + \var bool KDChartParams::_explode; + + Stores whether pies or ring segments are drawn exploding. + */ + +/** + \var double KDChartParams::_explodeFactor; + + Stores the default explode factor of pie or ring segments. + */ + +/** + \var QMap<int,double> KDChartParams::_explodeFactors; + + Stores the list of segment-specific explode factors of pie or + ring segments. + */ + +/** + \var QValueList<int> KDChartParams::_explodeList; + + Stores the list of pies or ring segments to explode. + */ + +/** + \var bool KDChartParams::_threeDPies; + + Stores whether pies will have a 3D effect. + */ + +/** + \var int KDChartParams::_threeDPieHeight; + + Stores the height of the pie 3D effect. + */ + +/** + \var int KDChartParams::_pieStart; + + Stores the starting point of the pie circle. + */ + +/** + \var int KDChartParams::_ringStart; + + Stores the starting point of the ring circle. + */ + +/** + \var bool KDChartParams::_relativeRingThickness; + + Stores whether the ring thickness is relativ or constant. + */ + + +// HI-LO CHARTS + +/** + \var HiLoChartSubType KDChartParams::_hiLoChartSubType; + + Stores the HiLo subtype. + */ + +/** + \var bool KDChartParams::_hiLoChartPrintLowValues; + + Stores whether the low values + will be printed under their respective entries. + */ + + +/** + \var QFont KDChartParams::_hiLoChartLowValuesFont; + + Stores the font to be used for printing the low values. + */ + + +/** + \var bool KDChartParams::_hiLoChartLowValuesUseFontRelSize; + + Stores whether the font size to be used for printing the low + values is calculated dynamically. + */ + + +/** + \var int KDChartParams::_hiLoChartLowValuesFontRelSize; + + Stores the relative size (in per mille of the chart width) of + font size to be used for printing the low values. + */ + + +/** + \var QColor KDChartParams::_hiLoChartLowValuesColor; + + Stores the colour of the font size to be used for printing the + low values. + */ + +/** + \var bool KDChartParams::_hiLoChartPrintHighValues; + + Stores whether the high values will be printed under their + respective entries. + */ + + +/** + \var QFont KDChartParams::_hiLoChartHighValuesFont; + + Stores the font to be used for printing the high values. + */ + + +/** + \var bool KDChartParams::_hiLoChartHighValuesUseFontRelSize; + + Stores whether the font size to be used for printing the high + values is calculated dynamically. + */ + + +/** + \var int KDChartParams::_hiLoChartHighValuesFontRelSize; + + Stores the relative size (in per mille of the chart width) of + font size to be used for printing the high values. + */ + + +/** + \var QColor KDChartParams::_hiLoChartHighValuesColor; + + Stores the colour of the font size to be used for printing the + high values. + */ + +/** + \var bool KDChartParams::_hiLoChartPrintOpenValues; + + Stores whether the open values + will be printed under their respective entries. + */ + + +/** + \var QFont KDChartParams::_hiLoChartOpenValuesFont; + + Stores the font to be used for printing the open values. + */ + + +/** + \var bool KDChartParams::_hiLoChartOpenValuesUseFontRelSize; + + Stores whether the font size to be used for printing the open + values is calculated dynamically. + */ + + +/** + \var int KDChartParams::_hiLoChartOpenValuesFontRelSize; + + Stores the relative size (in per mille of the chart width) of + font size to be used for printing the open values. + */ + + +/** + \var QColor KDChartParams::_hiLoChartOpenValuesColor; + + Stores the colour of the font size to be used for printing the + open values. + */ + +/** + \var bool KDChartParams::_hiLoChartPrintCloseValues; + + Stores whether the close values will be printed under their + respective entries. + */ + + +/** + \var QFont KDChartParams::_hiLoChartCloseValuesFont; + + Stores the font to be used for printing the close values. + */ + + +/** + \var bool KDChartParams::_hiLoChartCloseValuesUseFontRelSize; + + Stores whether the font size to be used for printing the close + values is calculated dynamically. + */ + + +/** + \var int KDChartParams::_hiLoChartCloseValuesFontRelSize; + + Stores the relative size (in per mille of the chart width) of + font size to be used for printing the close values. + */ + + +/** + \var QColor KDChartParams::_hiLoChartCloseValuesColor; + + Stores the colour of the font size to be used for printing the + close values. + */ + + +/** + \var BWChartSubType KDChartParams::_BWChartSubType; + + Stores the Box And Whisker subtype. + */ + +/** + \struct BWChartStatistics KDChartParams.h + + Stores the statistical data for a box-and-whisker chart + */ + +/** + \var BWChartStatistics KDChartParams::_BWChartStatistics[ BWStatValOffEndValue ]; + + Stores whether and how the statistical data + will be printed near the drawing. + */ + +/** + \var double KDChartParams::_BWChartFenceUpperInner; + \var double KDChartParams::_BWChartFenceLowerInner; + \var double KDChartParams::_BWChartFenceUpperOuter; + \var double KDChartParams::_BWChartFenceLowerOuter; + + Stores the factors to be used to calculate the width of + the inner and outer fences. + */ + +/** + \var int KDChartParams::_BWChartOutValMarkerSize; + + Stores the size (or the negative size factor, resp.) of + the markers representing the outliers. + */ + + +/** + \var QBrush KDChartParams::_BWChartBrush; + + Stores the brush to be used. + */ + +/** + \var LegendPosition KDChartParams::_legendPosition; + + Stores the legend position. + */ + +/** + \var LegendSource KDChartParams::_legendSource; + + Stores the legend source. + */ + +/** + \var QMap < int, QString > KDChartParams::_legendText; + + Stores the manually set legend texts. + */ + + +/** + \var QColor KDChartParams::_legendTextColor; + + Stores the color for drawing the legend texts. + */ + +/** + \var QFont KDChartParams::_legendFont; + + Stores the font for drawing the legends. + */ + +/** + \var bool KDChartParams::_legendFontUseRelSize; + + Stores whether the size of the legend font is to be calculated + on a relative basis. + + \sa setLegendFontUseRelSize, setLegendFontRelSize + \sa setLegendFont + */ + +/** + \var int KDChartParams::_legendFontRelSize; + + Stores the per mille basis for calculating the relative + legend font size. + + \sa setLegendFontRelSize, setLegendFontUseRelSize + \sa setLegendFont + */ + + +/** + \var QString KDChartParams::_legendTitleText; + + Stores the legend title text. + */ + + +/** + \var QColor KDChartParams::_legendTitleTextColor; + + Stores the color for drawing the legend title. + */ + + +/** + \var QFont KDChartParams::_legendTitleFont; + + Stores the font for drawing the legend title. + */ + +/** + \var bool KDChartParams::_legendTitleFontUseRelSize; + + Stores whether the size of the legend title font is to be + calculated on a relative basis. + + \sa setLegendTitleFontUseRelSize, setLegendTitleFontRelSize + \sa setLegendTitleFont + */ + +/** + \var int KDChartParams::_legendTitleFontRelSize; + + Stores the per mille basis for calculating the relative + legend title font size. + + \sa setLegendTitleFontRelSize, setLegendTitleFontUseRelSize + \sa setLegendTitleFont + */ + + +/** + \var uint KDChartParams::_legendSpacing; + + Stores the number of pixels between the legend and the data display + */ + + +/** + \struct KDChartParams::AxisSettings KDChartParams.h + + Stores all the settings of all the axis \em plus one more + parameter set containing the build-in defauls axis settings. + */ + +/** + \var KDChartParams::_axisSettings + + Stores the settings for each of the possible thirteen axes + */ + + + +/** + \struct HdFtParams KDChartParams.h + + Stores all the settings of all the header sections + and all the footer sections. + */ + + + +/** + Retrieves the ID of the first KDChartCustomBox that is anchored at axis \c n. + + By default this box is used for the default axis title text: it is created + (or modified, resp.) by calling one of the axis title convenience functions + setAxisTitleColor or setAxisTitleFont or setAxisTitleFontUseRelSize or setAxisTitleFontRelSize. + + \note If you have specified another custom box manually that is anchored to this axis + before (or instead of, resp.) calling one of the convenience functions mentioned above + your custom box will become the default axis title text box: you then can access/modify + its properties using these convenience functions. + + \return TRUE if a KDChartCustomBox was found that is anchored to axis \c n. + \param n the axis number, this could be KDChartAxisParams::AxisPosLeft or KDChartAxisParams::AxisPosBottom... + \param boxID will obtain the respective KDChartCustomBox's ID if return value is TRUE, otherwise the parameter's value remains unchanged. + \sa setAxisTitle, setAxisTitleColor, setAxisTitleFont, setAxisTitleFontUseRelSize, setAxisTitleFontRelSize + */ +bool KDChartParams::findFirstAxisCustomBoxID( uint n, uint& boxID ) const +{ + QIntDictIterator<KDChartCustomBox> it( _customBoxDict ); + for( ; it.current(); ++it ){ + if( (*it).anchorArea() == KDChartEnums::AreaAxisBASE + n ){ + boxID = it.currentKey(); + return true; + } + } + return false; +} + +void KDChartParams::insertDefaultAxisTitleBox( uint n, + bool setTitle, const QString& axisTitle, + bool setColor, const QColor& axisTitleColor, + bool setFont, const QFont& axisTitleFont, + bool setFontUseRelSize, bool axisTitleFontUseRelSize, + bool setFontRelSize, int axisTitleFontRelSize ) +{ + bool bVert = false; + bool bHorz = false; + bool b3rd = false; + switch( KDChartAxisParams::basicAxisPos( n ) ){ + case KDChartAxisParams::AxisPosLeft: + case KDChartAxisParams::AxisPosRight: + case KDChartAxisParams::AxisPosLeft2: + case KDChartAxisParams::AxisPosRight2: + bVert = true; + break; + case KDChartAxisParams::AxisPosTop: + case KDChartAxisParams::AxisPosBottom: + case KDChartAxisParams::AxisPosTop2: + case KDChartAxisParams::AxisPosBottom2: + bHorz = true; + break; + default: + b3rd = true; + break; + } + const QFont defaultFont( "helvetica", 6, QFont::Normal, false ); + + // SGI IRIX: Compiling error. + // QString titleString( setTitle ? axisTitle : "<qt><center> </center></qt>" ); + QString titleString; + if( setTitle ) + titleString = axisTitle; + else + titleString = "<qt><center> </center></qt>"; + + + const QString stripTitleString( titleString.simplifyWhiteSpace().upper() ); + if( setTitle ){ + if( !stripTitleString.startsWith("<QT>" ) ) + titleString.prepend("<qt><center>"); + if( !stripTitleString.endsWith("</QT>" ) ) + titleString.append("</center></qt>"); + } + + KDChartTextPiece textPiece( titleString, setFont ? axisTitleFont : defaultFont ); + int fixedFontSize = textPiece.font().pointSize(); + if( -1 == fixedFontSize ) + fixedFontSize = textPiece.font().pixelSize(); + if( -1 == fixedFontSize ) + fixedFontSize = 15; + int relFontSize = setFontRelSize ? -axisTitleFontRelSize : -18; + + KDChartCustomBox customBox( bVert ? -90 : 0, + textPiece, + setFontUseRelSize + ? ( axisTitleFontUseRelSize ? relFontSize : fixedFontSize ) + : relFontSize, + true, + 0, 0, + 0, 0, + setColor ? axisTitleColor : Qt::darkBlue, + Qt::NoBrush, + KDChartEnums::AreaAxisBASE + n, + bVert ? KDChartEnums::PosCenterLeft : KDChartEnums::PosBottomCenter, bVert ? (Qt::AlignTop + Qt::AlignHCenter) : (Qt::AlignBottom + Qt::AlignHCenter), + 0,0,0, + bVert ? (Qt::AlignBottom + Qt::AlignRight) : (Qt::AlignTop + Qt::AlignHCenter), + false, n ); + + customBox.setParentAxisArea( n ); + const uint id = insertCustomBox( customBox ); + /*debugging Title box*/ + /* + setSimpleFrame( + KDChartEnums::AreaCustomBoxesBASE + + id, + 0,0, 0,0, + true, + true, + KDFrame::FrameFlat, + 1, + 0, + Qt::red, + QBrush( QColor ( 255,248,222 ) ) ); + */ +} + +/** + Specifies the default axis title text. + + \note This is a convenience function accessing the first KDChartCustomBox that is anchored at axis \c n. + + \param axisTitle the axis title text + \sa setAxisTitleColor,setAxisTitleFont,setAxisTitleFontUseRelSize, setAxisTitleFontRelSize + \sa axisTitle, axisTitleColor, axisTitleFont, axisTitleFontUseRelSize, axisTitleFontRelSize + */ +void KDChartParams::setAxisTitle( uint n, const QString& axisTitle ) +{ + bool bDone = false; + uint boxID; + if( findFirstAxisCustomBoxID( n, boxID ) ){ + KDChartCustomBox* box = (KDChartCustomBox*)customBox( boxID ); + if( box ){ + QString title = axisTitle; + const QString stripTitleString( title.simplifyWhiteSpace().upper() ); + if( !stripTitleString.startsWith("<QT>" ) ) + title.prepend("<qt><center>"); + if( !stripTitleString.endsWith("</QT>" ) ) + title.append("</center></qt>"); + + KDChartTextPiece textPiece( 0, title, + box->content().font() ); + + box->setContent( textPiece ); + //qDebug ("old Axis Title updated"); + bDone = true; + } + } + if( !bDone ){ + insertDefaultAxisTitleBox( n, + true, axisTitle, + false, QColor(), + false, QFont(), + false, false, + false, 0 ); + //qDebug("new Axis Title Box inserted"); + } + emit changed(); +} +/** + Returns the default axis title text + + \return the axis title text. + \sa setAxisTitle + */ +QString KDChartParams::axisTitle( uint n ) const +{ + uint boxID; + if( findFirstAxisCustomBoxID( n, boxID ) ){ + const KDChartCustomBox* box = customBox( boxID ); + if( box ) + return box->content().text(); + } + return QString(); +} + +/** + Specifies the color of the default axis title text. + + \param axisTitleColor the color of the axis title text + \sa setAxisTitle, setAxisTitleFont,setAxisTitleFontUseRelSize, setAxisTitleFontRelSize + \sa axisTitle, axisTitleColor, axisTitleFont, axisTitleFontUseRelSize, axisTitleFontRelSize + */ +void KDChartParams::setAxisTitleColor( uint n, QColor axisTitleColor ) +{ + bool bDone = false; + uint boxID; + if( findFirstAxisCustomBoxID( n, boxID ) ){ + KDChartCustomBox* box = (KDChartCustomBox*)customBox( boxID ); + if( box ){ + box->setColor( axisTitleColor ); + bDone = true; + } + } + if( !bDone ) + insertDefaultAxisTitleBox( n, + false, QString(), + true, axisTitleColor, + false, QFont(), + false, false, + false, 0 ); + emit changed(); +} +/** + Returns the color of the default axis title text + + \return the color of the axis title text. + \sa setAxisTitleColor + */ +QColor KDChartParams::axisTitleColor( uint n ) const +{ + uint boxID; + if( findFirstAxisCustomBoxID( n, boxID ) ){ + const KDChartCustomBox* box = customBox( boxID ); + if( box ) + return box->color(); + } + return Qt::darkBlue; +} + +/** + Specifies the font and the size of the default axis title text. + + \param axisTitleFont the font of the axis title text + \param useFontSize flag indicating whether the font's size is to be used + \param axisTitleFontRelSize the size to be used if \c useFontSize is false, this is interpreted as per mil value of the printable area size + \sa setAxisTitle, setAxisTitleColor,setAxisTitleFontUseRelSize, setAxisTitleFontRelSize + \sa axisTitle, axisTitleColor, axisTitleFont, axisTitleFontUseRelSize, axisTitleFontRelSize + */ +void KDChartParams::setAxisTitleFont( uint n, + QFont axisTitleFont ) +{ + bool bDone = false; + uint boxID; + if( findFirstAxisCustomBoxID( n, boxID ) ){ + KDChartCustomBox* box = (KDChartCustomBox*)customBox( boxID ); + if( box ){ + KDChartTextPiece textPiece( 0, box->content().text(), + axisTitleFont ); + box->setContent( textPiece ); + bDone = true; + } + } + if( !bDone ) + insertDefaultAxisTitleBox( n, + false, QString(), + false, QColor(), + true, axisTitleFont, + false, false, + false, 0 ); + emit changed(); +} + +/** + Specifies the font and the size of the default axis title text. + \param axisTitleFont the font of the axis title text - by default the font will be relative + \param useFixedFontSize flag indicating whether the font's size is to be used as fixed or calculated as per mil value. + \param axisTitleFontRelSize the size to be used if \c useFixedFontSize is false, this is interpreted as per mil value of the printable area size + \sa setAxisTitle, setAxisTitleColor, setAxisTitleFont, setAxisTitleFontUseRelSize, setAxisTitleFontRelSize + \sa axisTitle, axisTitleColor, axisTitleFont, axisTitleFontUseRelSize, axisTitleFontRelSize + */ + +void KDChartParams::setAxisTitleFont( uint n, + QFont axisTitleFont, + bool useFixedFontSize ) +{ + + bool bDone = false; + uint boxID; + if( findFirstAxisCustomBoxID( n, boxID ) ){ + KDChartCustomBox* box = (KDChartCustomBox*)customBox( boxID ); + if( box ){ + KDChartTextPiece textPiece( 0, box->content().text(), + axisTitleFont ); + int fixedFontSize = textPiece.font().pointSize(); + setAxisTitleFontRelSize( n, fixedFontSize ); + box->setContent( textPiece ); + bDone = true; + } + } + if( !bDone ) + insertDefaultAxisTitleBox( n, + false, QString(), + false, QColor(), + true, axisTitleFont, + false, false, + false, 0 ); + + emit changed(); + + if ( useFixedFontSize ) + setAxisTitleFontUseRelSize( n, false); + +} + + +/** + Returns the font of the default axis title text. + + \return the axis title's font. + \sa setAxisTitleFont + */ +QFont KDChartParams::axisTitleFont( uint n ) const +{ + uint boxID; + if( findFirstAxisCustomBoxID( n, boxID ) ){ + const KDChartCustomBox* box = customBox( boxID ); + if( box ) + return box->content().font(); + } + return QFont( "helvetica", 6, QFont::Normal, false ); +} + +/** + Specifies whether the fixed size of the the default axis title text's font. + + \param axisTitleFontUseRelSize flag indicating whether the font's size is to be used or the size specified via setAxisTitleFontRelSize. + \sa setAxisTitle, setAxisTitleColor,setAxisTitleFont, setAxisTitleFontRelSize + \sa axisTitle, axisTitleColor, axisTitleFont, axisTitleFontUseRelSize, axisTitleFontRelSize + */ +void KDChartParams::setAxisTitleFontUseRelSize( uint n, + bool axisTitleFontUseRelSize ) +{ + bool bDone = false; + uint boxID; + if( findFirstAxisCustomBoxID( n, boxID ) ){ + KDChartCustomBox* box = (KDChartCustomBox*)customBox( boxID ); + if( box ){ + if ( !axisTitleFontUseRelSize ) { + if ( box->fontSize() < 0 ) + box->setFontSize( -(box->fontSize()), true ); + } else { + if( 0 <= box->fontSize() ) { + box->setFontSize( -(box->fontSize()), true ); + } else + box->setFontSize( box->fontSize(), true); + } + bDone = true; + } + } + if( !bDone ) + insertDefaultAxisTitleBox( n, + false, QString(), + false, QColor(), + false, QFont(), + true, axisTitleFontUseRelSize, + false, 0 ); + emit changed(); +} +/** + Returns whether the fix font size is used for the default axis title text. + + \return whether the fix axis title font size is used. + \sa setAxisTitleFontUseRelSize + */ +bool KDChartParams::axisTitleFontUseRelSize( uint n ) const +{ + uint boxID; + if( findFirstAxisCustomBoxID( n, boxID ) ){ + const KDChartCustomBox* box = customBox( boxID ); + if( box ) + return ( 0 > box->fontSize() ); + } + return true; +} + +/** + Specifies whether a relative size to be used for the default axis title text instead of the fixed size of the font. + + Calling this function implicitely sets this axis's \c axisTitleFontUseRelSize flag to TRUE. + + \param axisTitleFontRelSize the size to be used, this is interpreted as per mil value of the printable area size. + \sa setAxisTitle, setAxisTitleColor,setAxisTitleFont, setAxisTitleFontUseRelSize + \sa axisTitle, axisTitleColor, axisTitleFont, axisTitleFontUseRelSize, axisTitleFontRelSize + */ +void KDChartParams::setAxisTitleFontRelSize( uint n, + int axisTitleFontRelSize ) +{ + bool bDone = false; + uint boxID; + if( findFirstAxisCustomBoxID( n, boxID ) ){ + KDChartCustomBox* box = (KDChartCustomBox*)customBox( boxID ); + if( box ){ + box->setFontSize( -axisTitleFontRelSize, true ); + bDone = true; + } + } + if( !bDone ) + insertDefaultAxisTitleBox( n, + false, QString(), + false, QColor(), + false, QFont(), + true, true, + true, axisTitleFontRelSize ); + emit changed(); +} +/** + Returns the relative size of the default axis title text, should + be ignored if \c axisTitleFontUseRelSize returns false. + + \return the axis title's relative font size. + \sa setAxisTitleFontRelSize + */ +int KDChartParams::axisTitleFontRelSize( uint n ) const +{ + uint boxID; + if( findFirstAxisCustomBoxID( n, boxID ) ){ + const KDChartCustomBox* box = customBox( boxID ); + if( box ){ + int i = box->fontSize(); + return (0 > i) ? -i : i; + } + } + return 18; +} diff --git a/kchart/kdchart/KDChartParams.h b/kchart/kdchart/KDChartParams.h new file mode 100644 index 00000000..06673488 --- /dev/null +++ b/kchart/kdchart/KDChartParams.h @@ -0,0 +1,2787 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTPARAMS_H__ +#define __KDCHARTPARAMS_H__ + +#include <qapplication.h> +#include <qfont.h> +#include <qcolor.h> +#include <qpen.h> +#include <qmap.h> +#include <qdict.h> +#include <qintdict.h> +#include <qobject.h> +#include <qtextstream.h> +#include <qsimplerichtext.h> +#include <qdom.h> +#include <limits.h> + +#include <math.h> +#ifdef Q_WS_WIN +#define M_PI 3.14159265358979323846 +#endif + +#include "KDChartGlobal.h" +#include "KDChartEnums.h" +#include "KDChartCustomBox.h" +#include "KDFrame.h" +#include "KDChartAxisParams.h" +#include "KDChartPropertySet.h" + +#if COMPAT_QT_VERSION >= 0x030000 +#include <qvaluevector.h> +#else +#include <qarray.h> +#endif + +/** \file KDChartParams.h + \brief Header for all common chart parameters. + + This file is used to access all chart parameters except of the + axis settings which you will find in \c KDChartAxisParams.h + + \see KDChartAxisParams.h +*/ + + +typedef QIntDict<KDChartPropertySet> KDChartPropertySetList; + +#define KDCHART_ALL_AXES UINT_MAX-1 + +// PENDING(blackie) KHZ, please clean up here. I guess this defined can be removed now. +// Note: The following specification matches the UINT_MAX value used +// in KDChartAxisParams::KDChartAxisParams() to initialize the +// axis' _axisIsoRefAxis member. +// OK, this is a dirty hack but is avoids circular dependency +// between KDChartParams and KDChartAxisParams +#define KDCHART_NO_AXIS UINT_MAX +#define KDCHART_ALL_DATASETS UINT_MAX - 1 + +#define KDCHART_NO_DATASET UINT_MAX +#define KDCHART_UNKNOWN_CHART UINT_MAX - 2 +#define KDCHART_ALL_CHARTS UINT_MAX - 1 +#define KDCHART_NO_CHART UINT_MAX +#define KDCHART_GLOBAL_LINE_STYLE UINT_MAX +#define KDCHART_AUTO_SIZE INT_MAX +#define KDCHART_DATA_VALUE_AUTO_DIGITS INT_MAX +#define KDCHART_SAGITTAL_ROTATION INT_MAX +#define KDCHART_TANGENTIAL_ROTATION INT_MAX - 1 +#define KDCHART_PROPSET_NORMAL_DATA 0 +#define KDCHART_PROPSET_TRANSPARENT_DATA 1 +#define KDCHART_PROPSET_HORI_LINE 2 +#define KDCHART_PROPSET_VERT_LINE 3 + +// #define KDCHART_DATA_VALUE_AUTO_COLOR KDChartAutoColor::instance()->color() + +// PENDING(blackie) Can we delete this now? +/* we must keep this wrongly spelled value for backward compatibility reasons */ +#define KDCHART_SAGGITAL_ROTATION INT_MAX + +/** + Our charts may have up to 4 ordinate axes: + 2 left ones and 2 right ones + */ +#define KDCHART_CNT_ORDINATES 4 + +#define KDCHART_MAX_POLAR_DELIMS_AND_LABELS_POS 8 +#define KDCHART_MAX_AXES 12 + + +#define KDCHART_DATA_VALUE_AUTO_COLOR (KDChartAutoColor::instance()->color()) + +class KDCHART_EXPORT KDChartAutoColor { +public: + static const KDChartAutoColor* instance(); + static void freeInstance(); + const QColor* color() const + { + return &mColor; + } +private: + KDChartAutoColor(); + KDChartAutoColor( KDChartAutoColor const& ); + ~KDChartAutoColor(); + QColor mColor; + static KDChartAutoColor *mInstance; +}; + +/* +// #define KDCHART_DATA_VALUE_AUTO_COLOR KDChartParams_KDChartAutoColor +*/ + + +class KDCHART_EXPORT KDChartParams : public QObject +{ + + Q_OBJECT + Q_ENUMS(ChartType) + Q_ENUMS(SourceMode) + Q_ENUMS(BarChartSubType) + Q_ENUMS(LineChartSubType) + Q_ENUMS(LineMarkerStyle) + Q_ENUMS(AreaChartSubType) + Q_ENUMS(AreaLocation) + Q_ENUMS(PolarChartSubType) + Q_ENUMS(PolarMarkerStyle) + Q_ENUMS(HiLoChartSubType) + Q_ENUMS(BWChartSubType) + Q_ENUMS(BWStatVal) + Q_ENUMS(LegendPosition) + Q_ENUMS(LegendSource) + Q_ENUMS(HdFtPos) + + // Needed for QSA + Q_ENUMS( BrushStyle ) + Q_ENUMS( PenStyle ) + Q_ENUMS( Orientation ) + + +public slots: // PENDING(blackie) merge public slots into one section. + void setOptimizeOutputForScreen( bool screenOutput ) + { + _optimizeOutputForScreen = screenOutput; + } + + + bool optimizeOutputForScreen() const + { + return _optimizeOutputForScreen; + } + + void setGlobalLeading( int left, int top, int right, int bottom ); + + void setGlobalLeadingLeft( int leading ) + { + _globalLeadingLeft = leading; + emit changed(); + } + void setGlobalLeadingTop( int leading ) + { + _globalLeadingTop = leading; + emit changed(); + } + void setGlobalLeadingRight( int leading ) + { + _globalLeadingRight = leading; + emit changed(); + } + void setGlobalLeadingBottom( int leading ) + { + _globalLeadingBottom = leading; + emit changed(); + } + int globalLeadingLeft() const + { + return _globalLeadingLeft; + } + int globalLeadingTop() const + { + return _globalLeadingTop; + } + int globalLeadingRight() const + { + return _globalLeadingRight; + } + int globalLeadingBottom() const + { + return _globalLeadingBottom; + } + + int registerProperties( KDChartPropertySet& rSet ); + void setProperties( int id, KDChartPropertySet& rSet ); + bool removeProperties( int id ); + bool properties( int id, KDChartPropertySet& rSet ) const; + KDChartPropertySet* properties( int id ); + bool calculateProperties( int startId, KDChartPropertySet& rSet ) const; + +public: + +// MOC_SKIP_BEGIN + class KDCHART_EXPORT KDChartFrameSettings + { + // Q_OBJECT + public: + KDChartFrameSettings(); + KDChartFrameSettings( uint dataRow, + uint dataCol, + uint data3rd, + const KDFrame& frame, + int outerGapX, + int outerGapY, + int innerGapX, + int innerGapY, + bool addFrameWidthToLayout = true, + bool addFrameHeightToLayout = true ); + + // public slots: + uint dataRow() const + { + return _dataRow; + } + uint dataCol() const + { + return _dataCol; + } + uint data3rd() const + { + return _data3rd; + } + + void setDataRow( uint dataRow ) + { + _dataRow = dataRow; + } + void setDataCol( uint dataCol ) + { + _dataCol = dataCol; + } + void setData3rd( uint data3rd ) + { + _data3rd = data3rd; + } + + + void resetFrame() + { + if( _frame ) + delete _frame; + _frame = new KDFrame(); + } + + + const KDFrame* framePtr() const + { + return _frame; + } + + + const KDFrame& frame() const + { + return *_frame; + } + + + int innerGapX() const + { + return _innerGapX; + } + + + int innerGapY() const + { + return _innerGapY; + } + + + int outerGapX() const + { + return _outerGapX; + } + + + int outerGapY() const + { + return _outerGapY; + } + + + bool addFrameWidthToLayout() const + { + return _addFrameWidthToLayout; + } + + + bool addFrameHeightToLayout() const + { + return _addFrameHeightToLayout; + } + + // public: + virtual ~KDChartFrameSettings(); + + + static void createFrameSettingsNode( QDomDocument& document, + QDomNode& parent, + const QString& elementName, + const KDChartFrameSettings* settings, + uint areaId ); + + static bool readFrameSettingsNode( const QDomElement& element, + KDChartFrameSettings& settings, + uint& areaId ); + + + static void deepCopy( KDChartFrameSettings& D, const KDChartFrameSettings& R ) { + D._dataRow = R._dataRow; + D._dataCol = R._dataCol; + D._data3rd = R._data3rd; + + delete D._frame; + if( R._frame ){ + D._frame = new KDFrame(); + KDFrame::deepCopy(*D._frame, *R._frame); + }else{ + D._frame = 0; + } + + D._outerGapX = R._outerGapX; + D._outerGapY = R._outerGapY; + D._innerGapX = R._innerGapX; + D._innerGapY = R._innerGapY; + D._addFrameWidthToLayout = R._addFrameWidthToLayout; + D._addFrameHeightToLayout = R._addFrameHeightToLayout; + } + + private: + // KDChartFrameSettings( const KDChartFrameSettings& other ) :QObject( 0 ) { + KDChartFrameSettings( const KDChartFrameSettings& ) {} + KDChartFrameSettings& operator=( const KDChartFrameSettings& ){return *this;} + + + private: + uint _dataRow; + uint _dataCol; + uint _data3rd; + KDFrame* _frame; + int _outerGapX; + int _outerGapY; + int _innerGapX; + int _innerGapY; + bool _addFrameWidthToLayout; + bool _addFrameHeightToLayout; + }; + +// MOC_SKIP_END + +public slots: + + void setFrame( uint area, + const KDFrame& frame, + int outerGapX, + int outerGapY, + int innerGapX, + int innerGapY, + bool addFrameWidthToLayout = true, + bool addFrameHeightToLayout = true ) + { + _areaDict.setAutoDelete( TRUE ); + _areaDict.replace( QString( "%1/-----/-----/-----" ).arg( area, 5 ), + new KDChartFrameSettings(0,0,0, + frame, + outerGapX, + outerGapY, + innerGapX, + innerGapY, + addFrameWidthToLayout, + addFrameHeightToLayout ) ); + emit changed(); + } + + + // Note if you change the parameters here, then you must also change them in wrappers/KDChartParametersWrapper.h + void setSimpleFrame( uint area, + int outerGapX = 0, + int outerGapY = 0, + int innerGapX = 0, + int innerGapY = 0, + bool addFrameWidthToLayout = true, + bool addFrameHeightToLayout = true, + KDFrame::SimpleFrame simpleFrame = KDFrame::FrameFlat, + int lineWidth = 1, + int midLineWidth = 0, + QPen pen = QPen(), + QBrush background = QBrush( Qt::NoBrush ), + const QPixmap* backPixmap = 0, // no pixmap + KDFrame::BackPixmapMode backPixmapMode = KDFrame::PixStretched, + int shadowWidth = 0, + KDFrame::CornerName sunPos = KDFrame::CornerTopLeft ) + { + _areaDict.setAutoDelete( TRUE ); + KDFrame frame( QRect(0,0,0,0), + simpleFrame, + lineWidth, + midLineWidth, + pen, + background, + backPixmap, + backPixmapMode, + shadowWidth, + sunPos ); + + _areaDict.replace( QString( "%1/-----/-----/-----" ).arg( area, 5 ), + new KDChartFrameSettings( 0,0,0, frame, + outerGapX, + outerGapY, + innerGapX, + innerGapY, + addFrameWidthToLayout, + addFrameHeightToLayout ) ); + emit changed(); + } + + bool removeFrame( uint area ); + + + // Note if you change the parameters here, then you must also change them in wrappers/KDChartParametersWrapper.h + void setDataRegionFrame( uint dataRow, + uint dataCol, + uint, // important: we ignore the data3rd parameter for now! + int innerGapX = 0, + int innerGapY = 0, + bool addFrameWidthToLayout = true, + bool addFrameHeightToLayout = true, + KDFrame::SimpleFrame simpleFrame = KDFrame::FrameFlat, + int lineWidth = 1, + int midLineWidth = 0, + QPen pen = QPen(), + int shadowWidth = 0, + KDFrame::CornerName sunPos = KDFrame::CornerTopLeft ) + { + _areaDict.setAutoDelete( TRUE ); + KDFrame frame( QRect(0,0,0,0), + simpleFrame, + lineWidth, + midLineWidth, + pen, + QBrush( Qt::NoBrush ), + 0, + KDFrame::PixStretched, + shadowWidth, + sunPos ); + + _areaDict.replace( + dataRegionFrameAreaName( dataRow, dataCol, 0 ), //data3rd 5 ), + new KDChartFrameSettings( dataRow, + dataCol, + 0, // important: we ignore the data3rd parameter for now! + frame, + 0, + 0, + innerGapX, + innerGapY, + addFrameWidthToLayout, + addFrameHeightToLayout ) ); + emit changed(); + } + + + // Note if you change the parameters here, then you must also change them in wrappers/KDChartParametersWrapper.h + bool moveDataRegionFrame( uint oldDataRow, + uint oldDataCol, + uint, // important: we ignore the data3rd parameter for now! + uint newDataRow, + uint newDataCol, + uint // important: we ignore the data3rd parameter for now! + ); + + + const KDChartFrameSettings* frameSettings( uint area, + bool& bFound, + int* pIterIdx=0 ) const; + + const KDChartFrameSettings* nextFrameSettings( bool& bFound, + int* pIterIdx ) const; + + +public: + typedef QIntDict <KDChartCustomBox > CustomBoxDict; + +public slots: + uint insertCustomBox( const KDChartCustomBox & box ); + bool removeCustomBox( const uint & idx ); + + void removeAllCustomBoxes() + { + emit changed(); + _customBoxDict.setAutoDelete( true ); + _customBoxDict.clear(); + } + + KDChartCustomBox* customBoxRef( uint box ); + const KDChartCustomBox* customBox( uint box ) const; + uint maxCustomBoxIdx() const; + + +public: + enum ChartType { NoType, Bar, Line, Area, Pie, HiLo, Ring, Polar, BoxWhisker }; + +public slots: + void setChartType( ChartType chartType ); + ChartType chartType() const + { + return _chartType; + } + + + static QString chartTypeToString( ChartType type ); + static ChartType stringToChartType( const QString& string ); + + void setAdditionalChartType( ChartType chartType ); + ChartType additionalChartType() const + { + return _additionalChartType; + } +public: + + enum SourceMode { + UnknownMode = 0, + DontUse = 1, + DataEntry = 2, + AxisLabel = 3, + LegendText = 4, + ExtraLinesAnchor = 5, + Last_SourceMode = ExtraLinesAnchor }; + +public slots: + void setChartSourceMode( SourceMode mode, + uint dataset, + uint dataset2 = KDCHART_NO_DATASET, + uint chart = 0 ); + + static QString chartSourceModeToString( const SourceMode& mode ); + static SourceMode stringToChartSourceMode( const QString& string ); + + + bool neverUsedSetChartSourceMode() const + { + return !_setChartSourceModeWasUsed; + } + + SourceMode chartSourceMode( uint dataset, + uint dataset2 = KDCHART_NO_DATASET, + uint* chart = 0 ) const; + + bool findDataset( SourceMode mode, + uint& dataset, + uint& dataset2, + uint chart = 0 ) const; + + bool findDatasets( SourceMode mode1, + SourceMode mode2, + uint& dataset, + uint& dataset2, + uint chart = 0 ) const; + + uint maxDatasetSourceMode() const + { + return _maxDatasetSourceMode; + } + + + void setDefaultAxesTypes(); + void activateDefaultAxes(); + + + void setNumValues( uint numValues ) + { + _numValues = numValues; + emit changed(); + } + + + int numValues() const + { + return _numValues; + } + + + void calculateShadowColors( QColor color, + QColor& shadow1, + QColor& shadow2 ) const; + + void setDataColor( uint dataset, QColor color ); + QColor dataColor( uint dataset ) const; + + void setDataDefaultColors(); + void setDataRainbowColors(); + void setDataSubduedColors( bool ordered = false ); + + + void setShadowBrightnessFactor( double factor ) + { + _shadowBrightnessFactor = factor; + recomputeShadowColors(); + emit changed(); + } + + double shadowBrightnessFactor() const + { + return _shadowBrightnessFactor; + } + + + void setShadowPattern( BrushStyle style ) { + _shadowPattern = style; + emit changed(); + } + + Qt::BrushStyle shadowPattern() const { + return _shadowPattern; + } + + + void setOutlineDataColor( QColor color ) + { + _outlineDataColor = color; + emit changed(); + } + + + QColor outlineDataColor() const + { + return _outlineDataColor; + } + + + void setOutlineDataLineWidth( uint width ) + { + _outlineDataLineWidth = width; + emit changed(); + } + + uint outlineDataLineWidth() const + { + return _outlineDataLineWidth; + } + + + void setOutlineDataLineStyle( PenStyle style ) + { + _outlineDataLineStyle = style; + emit changed(); + } + + PenStyle outlineDataLineStyle() const + { + return _outlineDataLineStyle; + } + + + uint maxDataColor() const + { + return _maxDatasetColor; + } + + + void setThreeDShadowColors( bool shadow ) + { + _threeDShadowColors = shadow; + emit changed(); + } + + + bool threeDShadowColors() const + { + return _threeDShadowColors; + } + + + static int roundVal( double d ) + { + double fr; + double i=0.0; // initialization necessary for Borland C++ + fr = modf( d, &i ); + int ret = static_cast < int > ( i ); + if( 0.49999 <= fabs( fr ) ) + ret += ( 0.0 < d ) ? 1 : -1; + return ret; + } + + // Note if you change the parameters here, then you must also change them in wrappers/KDChartParametersWrapper.h + void setPrintDataValues( bool active, + uint chart = KDCHART_ALL_CHARTS, + int divPow10 = 0, + int digitsBehindComma = KDCHART_DATA_VALUE_AUTO_DIGITS, + QFont* font = 0, + uint size = UINT_MAX, // <-- makes us use the *default* font params + // by IGNORING settings of + // the following parameters! + const QColor* color = KDCHART_DATA_VALUE_AUTO_COLOR, + KDChartEnums::PositionFlag negativePosition = KDChartEnums::PosCenter, + uint negativeAlign = Qt::AlignCenter, + int negativeDeltaX = 0, + int negativeDeltaY = 0, + int negativeRotation = 0, + KDChartEnums::PositionFlag positivePosition = KDChartEnums::PosCenter, + uint positiveAlign = Qt::AlignCenter, + int positiveDeltaX = 0, + int positiveDeltaY = 0, + int positiveRotation = 0, + KDChartEnums::TextLayoutPolicy policy = KDChartEnums::LayoutPolicyRotate ); + + void setDataValuesCalc( int divPow10 = 0, + int digitsBehindComma = KDCHART_DATA_VALUE_AUTO_DIGITS, + uint chart = KDCHART_ALL_CHARTS ); + void setDataValuesFont( QFont* font, + uint size = UINT_MAX, + uint chart = KDCHART_ALL_CHARTS ); + + // Note if you change the parameters here, then you must also change them in wrappers/KDChartParametersWrapper.h + void setDataValuesPlacing( KDChartEnums::PositionFlag position, + uint align, + int deltaX, + int deltaY, + int rotation, + bool specifyingPositiveValues = true, + uint chart = KDCHART_ALL_CHARTS ); + + void setDataValuesColors( const QColor* color = KDCHART_DATA_VALUE_AUTO_COLOR, + const QBrush& background = Qt::NoBrush, + uint chart = KDCHART_ALL_CHARTS ); + + // Note if you change the parameters here, then you must also change them in wrappers/KDChartParametersWrapper.h + void setDataValuesPolicy( KDChartEnums::TextLayoutPolicy policy = KDChartEnums::LayoutPolicyRotate, + uint chart = KDCHART_ALL_CHARTS ); + + void setDataValuesShowInfinite( bool dataValuesShowInfinite = true, + uint chart = KDCHART_ALL_CHARTS ); + + void setPrintDataValuesWithDefaultFontParams( uint chart = KDCHART_ALL_CHARTS, + bool callSetPrintDataValues = true ); + + void setAllowOverlappingDataValueTexts( bool allow ) + { + _allowOverlappingDataValueTexts = allow; + } + + + bool allowOverlappingDataValueTexts() const + { + return _allowOverlappingDataValueTexts; + } + + bool printDataValuesWithDefaultFontParams( uint chart ) const + { + return chart ? _printDataValuesSettings2._useDefaultFontParams + : _printDataValuesSettings._useDefaultFontParams; + } + + bool printDataValues( uint chart ) const + { + return chart ? _printDataValuesSettings2._printDataValues + : _printDataValuesSettings._printDataValues; + } + + + int dataValuesDivPow10( uint chart ) const + { + return chart ? _printDataValuesSettings2._divPow10 + : _printDataValuesSettings._divPow10; + } + + + int dataValuesDigitsBehindComma( uint chart ) const + { + return chart ? _printDataValuesSettings2._digitsBehindComma + : _printDataValuesSettings._digitsBehindComma; + } + + + QFont dataValuesFont( uint chart ) const + { + return chart ? _printDataValuesSettings2._dataValuesFont + : _printDataValuesSettings._dataValuesFont; + } + + + bool dataValuesUseFontRelSize( uint chart ) const + { + return chart ? _printDataValuesSettings2._dataValuesUseFontRelSize + : _printDataValuesSettings._dataValuesUseFontRelSize; + } + + + int dataValuesFontRelSize( uint chart ) const + { + return chart ? _printDataValuesSettings2._dataValuesFontRelSize + : _printDataValuesSettings._dataValuesFontRelSize; + } + + + QColor dataValuesColor( uint chart ) const + { + return chart ? _printDataValuesSettings2._dataValuesColor + : _printDataValuesSettings._dataValuesColor; + } + + + QBrush dataValuesBackground( uint chart ) const + { + return chart ? _printDataValuesSettings2._dataValuesBrush + : _printDataValuesSettings._dataValuesBrush; + } + + + bool dataValuesAutoColor( uint chart ) const + { + return chart ? _printDataValuesSettings2._dataValuesAutoColor + : _printDataValuesSettings._dataValuesAutoColor; + } + + + KDChartEnums::PositionFlag dataValuesAnchorPosition( uint chart, + bool negative ) const; + uint dataValuesAnchorAlign( uint chart, bool negative ) const; + int dataValuesAnchorDeltaX( uint chart, bool negative ) const; + int dataValuesAnchorDeltaY( uint chart, bool negative ) const; + int dataValuesRotation( uint chart, bool negative ) const; + + KDChartEnums::TextLayoutPolicy dataValuesLayoutPolicy( uint chart ) const + { + return chart ? _printDataValuesSettings2._dataValuesLayoutPolicy + : _printDataValuesSettings._dataValuesLayoutPolicy; + } + + + bool dataValuesShowInfinite( uint chart ) const + { + return chart ? _printDataValuesSettings2._dataValuesShowInfinite + : _printDataValuesSettings._dataValuesShowInfinite; + } + + + // for backward compatibility we may NOT remove these functions: + void setPrintDataValuesColor( uint chart = KDCHART_ALL_CHARTS, + const QColor* color = KDCHART_DATA_VALUE_AUTO_COLOR ); + void setPrintDataValuesFontRelSize( uint chart, uint size ); + + + static QString markerStyleToString( int style ); + static QString markerStyleToStringTr( int style ); + static int stringToMarkerStyle( const QString& string ); + static int stringToMarkerStyleTr( const QString& string ); + + // END GENERAL + + +public: + + // BAR CHART-SPECIFIC + enum BarChartSubType { BarNormal, BarStacked, BarPercent, BarMultiRows }; + +public slots: + void setBarChartSubType( BarChartSubType barChartSubType ) + { + _barChartSubType = barChartSubType; + emit changed(); + } + + BarChartSubType barChartSubType() const + { + return _barChartSubType; + } + + + + + + static QString barChartSubTypeToString( BarChartSubType type ); + static BarChartSubType stringToBarChartSubType( const QString& string ); + + + void setThreeDBars( bool threeDBars ) + { + _threeDBars = threeDBars; + emit changed(); + } + + bool threeDBars() const + { + return _threeDBars; + } + + + void setThreeDBarsShadowColors( bool shadow ) + { + _threeDShadowColors = shadow; + emit changed(); + } + + + bool threeDBarsShadowColors() const + { + return _threeDShadowColors; + } + + + QColor dataShadow1Color( uint dataset ) const; + QColor dataShadow2Color( uint dataset ) const; + + + void setThreeDBarAngle( uint angle ); + uint threeDBarAngle() const + { + return _threeDBarAngle; + } + + + double cosThreeDBarAngle() const + { + return _cosThreeDBarAngle; + } + + + void setThreeDBarDepth( double depth ) + { + _threeDBarDepth = depth; + emit changed(); + } + + + double threeDBarDepth() const + { + return _threeDBarDepth; + } + + + void setDatasetGap( int gap ) + { + _datasetGap = gap; + emit changed(); + } + + int datasetGap() const + { + return _datasetGap; + } + + + void setDatasetGapIsRelative( bool gapIsRelative ) + { + _datasetGapIsRelative = gapIsRelative; + emit changed(); + } + + bool datasetGapIsRelative() const + { + return _datasetGapIsRelative; + } + + + void setValueBlockGap( int gap ) + { + _valueBlockGap = gap; + emit changed(); + } + + int valueBlockGap() const + { + return _valueBlockGap; + } + + + void setValueBlockGapIsRelative( bool gapIsRelative ) + { + _valueBlockGapIsRelative = gapIsRelative; + emit changed(); + } + + bool valueBlockGapIsRelative() const + { + return _valueBlockGapIsRelative; + } + + + void setBarWidth( int width = KDCHART_AUTO_SIZE ) + { + if ( width == KDCHART_AUTO_SIZE ) { + _barWidth = width; + _userWidth = 0; + } + else + _userWidth = width; + + emit changed(); + } + + + int barWidth() const + { + return _barWidth; + } + + int userWidth() const + { + return _userWidth; + } + + int numBarsDisplayed() const + { + return _barsDisplayed; + } + + int numBarsLeft() const + { + return _barsLeft; + } + + void setDrawSolidExcessArrows( bool solidArrows ) { + _solidExcessArrows = solidArrows; + emit changed(); + } + + bool drawSolidExcessArrows() const + { + return _solidExcessArrows; + } + + // END BAR CHART-SPECIFIC + +public: + // LINE/AREA CHART-SPECIFIC + enum LineChartSubType { LineNormal, LineStacked, LinePercent }; + +public slots: + void setLineChartSubType( LineChartSubType lineChartSubType ) + { + _lineChartSubType = lineChartSubType; + emit changed(); + } + + LineChartSubType lineChartSubType() const + { + return _lineChartSubType; + } + + + static LineChartSubType stringToLineChartSubType( const QString& string ); + static QString lineChartSubTypeToString( LineChartSubType type ); + + + void setLineMarker( bool marker ) + { + _lineMarker = marker; + emit changed(); + } + + bool lineMarker() const + { + return _lineMarker; + } + +public: + enum LineMarkerStyle { LineMarkerCircle = 0, + LineMarkerSquare = 1, + LineMarkerDiamond = 2, + LineMarker1Pixel = 3, + LineMarker4Pixels = 4, + LineMarkerRing = 5, + LineMarkerCross = 6, + LineMarkerFastCross = 7 }; + + // PENDING(blackie) Add a wrapper for this + typedef QMap<uint,LineMarkerStyle> LineMarkerStyleMap; +public slots: + + void setLineMarkerStyle( uint dataset, LineMarkerStyle style ); + void setLineMarkerStyles( LineMarkerStyleMap map ); + LineMarkerStyle lineMarkerStyle( uint dataset ) const; + + static QString lineMarkerStyleToString( LineMarkerStyle style ); + static QString lineMarkerStyleToStringTr( LineMarkerStyle style ); + static LineMarkerStyle stringToLineMarkerStyle( const QString& string ); + static LineMarkerStyle stringToLineMarkerStyleTr( const QString& string ); + + LineMarkerStyleMap lineMarkerStyles() const { + return _lineMarkerStyles; + } + + + uint maxDatasetLineMarkerStyle() const + { + return _maxDatasetLineMarkerStyle; + } + + + void setLineMarkerSize( QSize size ) + { + _lineMarkerSize = size; + emit changed(); + } + + QSize lineMarkerSize() const + { + return _lineMarkerSize; + } + + void setLineWidth( int width ) + { + _lineWidth = width; + emit changed(); + } + + int lineWidth() const + { + return _lineWidth; + } + + + void setLineColor( QColor color = QColor() ) + { + _lineColor = color; + emit changed(); + } + + QColor lineColor() const + { + return _lineColor; + } + + void setLineStyle( PenStyle style, uint dataset=KDCHART_GLOBAL_LINE_STYLE ); + + PenStyle lineStyle( uint dataset=KDCHART_GLOBAL_LINE_STYLE ) const; + + + void setThreeDLines( bool threeD ) { + _threeDLines = threeD; + emit changed(); + } + + + bool threeDLines() const { + return _threeDLines; + } + + + void setThreeDLineDepth( int depth ) { + _threeDLineDepth = depth; + emit changed(); + } + + + int threeDLineDepth() const { + return _threeDLineDepth; + } + + + // NOTE: documentation of this function is temporary disabled. + // Feature is currently not supported, will be implemented + // by future versions of KDChart + void setThreeDLineXRotation( int degrees ) { + _threeDLineXRotation = degrees; + emit changed(); + } + + + // NOTE: documentation of this function is temporary disabled. + // Feature is currently not supported, will be implemented + // by future versions of KDChart + int threeDLineXRotation() const { + return _threeDLineXRotation; + } + + + // NOTE: documentation of this function is temporary disabled. + // Feature is currently not supported, will be implemented + // by future versions of KDChart + void setThreeDLineYRotation( int degrees ) { + _threeDLineYRotation = degrees; + emit changed(); + } + + + // NOTE: documentation of this function is temporary disabled. + // Feature is currently not supported, will be implemented + // by future versions of KDChart + int threeDLineYRotation() const { + return _threeDLineYRotation; + } + +public: + enum AreaChartSubType { AreaNormal, AreaStacked, AreaPercent }; + +public slots: + void setAreaChartSubType( AreaChartSubType areaChartSubType ) + { + _areaChartSubType = areaChartSubType; + // activate default data value text settings for this chart type + if( printDataValues( 0 ) + && printDataValuesWithDefaultFontParams( 0 ) ) + setPrintDataValues( true, 0 ); + emit changed(); + } + + + AreaChartSubType areaChartSubType() const + { + return _areaChartSubType; + } + + + static QString areaChartSubTypeToString( AreaChartSubType type ); + static AreaChartSubType stringToAreaChartSubType( const QString& string ); + +public: + enum AreaLocation { AreaAbove, AreaBelow }; +public slots: + + void setAreaLocation( AreaLocation location ) + { + _areaLocation = location; + emit changed(); + } + + AreaLocation areaLocation() const + { + return _areaLocation; + } + + + static QString areaLocationToString( AreaLocation type ); + static AreaLocation stringToAreaLocation( const QString& string ); + + // END LINE/AREA CHART-SPECIFIC + +public: + // POLAR CHART-SPECIFIC + enum PolarChartSubType { PolarNormal, PolarStacked, PolarPercent }; + +public slots: + void setPolarChartSubType( PolarChartSubType polarChartSubType ) + { + _polarChartSubType = polarChartSubType; + emit changed(); + } + + PolarChartSubType polarChartSubType() const + { + return _polarChartSubType; + } + + + static PolarChartSubType stringToPolarChartSubType( const QString& string ); + static QString polarChartSubTypeToString( PolarChartSubType type ); + + void setPolarMarker( bool marker ) + { + _polarMarker = marker; + emit changed(); + } + + bool polarMarker() const + { + return _polarMarker; + } + +public: + enum PolarMarkerStyle { PolarMarkerCircle = 0, + PolarMarkerSquare = 1, + PolarMarkerDiamond = 2, + PolarMarker1Pixel = 3, + PolarMarker4Pixels = 4, + PolarMarkerRing = 5, + PolarMarkerCross = 6 }; + +public slots: + void setPolarMarkerStyle( uint dataset, PolarMarkerStyle style ); + PolarMarkerStyle polarMarkerStyle( uint dataset ) const; + static QString polarMarkerStyleToString( PolarMarkerStyle style ); + static QString polarMarkerStyleToStringTr( PolarMarkerStyle style ); + static PolarMarkerStyle stringToPolarMarkerStyle( const QString& string ); + static PolarMarkerStyle stringToPolarMarkerStyleTr( const QString& string ); + +public: + typedef QMap<uint,PolarMarkerStyle> PolarMarkerStyleMap; + +public slots: + void setPolarMarkerStyles( PolarMarkerStyleMap map ); + + PolarMarkerStyleMap polarMarkerStyles() const { + return _polarMarkerStyles; + } + + uint maxDatasetPolarMarkerStyle() const + { + return _maxDatasetPolarMarkerStyle; + } + + void setPolarMarkerSize( QSize size = QSize( -40, -40 ) ) + { + _polarMarkerSize = size; + emit changed(); + } + + QSize polarMarkerSize() const + { + return _polarMarkerSize; + } + + void setPolarLineWidth( int width = -3 ) + { + _polarLineWidth = width; + emit changed(); + } + + int polarLineWidth() const + { + return _polarLineWidth; + } + + + void setPolarZeroDegreePos( int degrees ) + { + _polarZeroDegreePos = degrees; + } + + int polarZeroDegreePos() const + { + return _polarZeroDegreePos; + } + + + void setPolarRotateCircularLabels( bool rotateCircularLabels ) + { + _polarRotateCircularLabels = rotateCircularLabels; + } + + bool polarRotateCircularLabels() const + { + return _polarRotateCircularLabels; + } + + + // Note if you change the parameters here, then you must also change them in wrappers/KDChartParametersWrapper.h + void setPolarDelimsAndLabelsAtPos( KDChartEnums::PositionFlag pos, + bool showDelimiters, + bool showLabels ); + + // Note if you change the parameters here, then you must also change them in wrappers/KDChartParametersWrapper.h + bool polarDelimAtPos( KDChartEnums::PositionFlag pos ) const; + + // Note if you change the parameters here, then you must also change them in wrappers/KDChartParametersWrapper.h + bool polarLabelsAtPos( KDChartEnums::PositionFlag pos ) const; + + // END POLAR CHART-SPECIFIC + + + // PIE/RING CHART-SPECIFIC + + void setExplode( bool explode ) + { + _explode = explode; + emit changed(); + } + + bool explode() const + { + return _explode; + } + + + void setExplodeValues( QValueList<int> explodeList ) { + _explodeList = explodeList; + emit changed(); + } + + + // Unfortunately this is not avaialble from QSA- + QValueList<int> explodeValues() const { + return _explodeList; + } + +public: + typedef QMap<int,double> ExplodeFactorsMap; + +public slots: + void setExplodeFactors( ExplodeFactorsMap factors ) { + _explodeFactors = factors; + emit changed(); + } + + + ExplodeFactorsMap explodeFactors() const { + return _explodeFactors; + } + + + void setExplodeFactor( double factor ) + { + _explodeFactor = factor; + emit changed(); + } + + double explodeFactor() const + { + return _explodeFactor; + } + + + void setThreeDPies( bool threeDPies ) + { + _threeDPies = threeDPies; + emit changed(); + } + + bool threeDPies() const + { + return _threeDPies; + } + + + void setThreeDPieHeight( int pixels ) + { + _threeDPieHeight = pixels; + emit changed(); + } + + int threeDPieHeight() const + { + return _threeDPieHeight; + } + + void setPieStart( int degrees ) + { + while ( degrees < 0 ) + degrees += 360; + while ( degrees >= 360 ) + degrees -= 360; + _pieStart = degrees; + + emit changed(); + } + + + int pieStart() const + { + return _pieStart; + } + + void setRingStart( int degrees ) + { + while ( degrees < 0 ) + degrees += 360; + while ( degrees >= 360 ) + degrees -= 360; + _ringStart = degrees; + + emit changed(); + } + + + int ringStart() const + { + return _ringStart; + } + + void setRelativeRingThickness( bool relativeThickness ) { + _relativeRingThickness = relativeThickness; + + emit changed(); + } + + + bool relativeRingThickness() const { + return _relativeRingThickness; + } + + // END PIE/RING CHART-SPECIFIC + +public: + // HI/LO CHART-SPECIFIC + enum HiLoChartSubType { HiLoNormal, HiLoSimple = HiLoNormal, + HiLoClose, HiLoOpenClose }; + +public slots: + void setHiLoChartSubType( HiLoChartSubType hiLoChartSubType ) + { + _hiLoChartSubType = hiLoChartSubType; + emit changed(); + } + + HiLoChartSubType hiLoChartSubType() const + { + return _hiLoChartSubType; + } + + static QString hiLoChartSubTypeToString( HiLoChartSubType type ); + static HiLoChartSubType stringToHiLoChartSubType( const QString& string ); + void setHiLoChartPrintLowValues( bool active, + QFont* font = 0, + int size = 14, + QColor* color = 0 ); + + bool hiLoChartPrintLowValues() const + { + return _hiLoChartPrintLowValues; + } + + + QFont hiLoChartLowValuesFont() const + { + return _hiLoChartLowValuesFont; + } + + + bool hiLoChartLowValuesUseFontRelSize() const + { + return _hiLoChartLowValuesUseFontRelSize; + } + + + int hiLoChartLowValuesFontRelSize() const + { + return _hiLoChartLowValuesFontRelSize; + } + + + QColor hiLoChartLowValuesColor() const + { + return _hiLoChartLowValuesColor; + } + + void setHiLoChartPrintHighValues( bool active, + QFont* font = 0, + int size = 14, + QColor* color = 0 ); + + + bool hiLoChartPrintHighValues() const + { + return _hiLoChartPrintHighValues; + } + + + QFont hiLoChartHighValuesFont() const + { + return _hiLoChartHighValuesFont; + } + + + bool hiLoChartHighValuesUseFontRelSize() const + { + return _hiLoChartHighValuesUseFontRelSize; + } + + + int hiLoChartHighValuesFontRelSize() const + { + return _hiLoChartHighValuesFontRelSize; + } + + + QColor hiLoChartHighValuesColor() const + { + return _hiLoChartHighValuesColor; + } + + void setHiLoChartPrintOpenValues( bool active, + QFont* font = 0, + uint size = 14, + QColor* color = 0 ); + + bool hiLoChartPrintOpenValues() const + { + return _hiLoChartPrintOpenValues; + } + + + QFont hiLoChartOpenValuesFont() const + { + return _hiLoChartOpenValuesFont; + } + + + bool hiLoChartOpenValuesUseFontRelSize() const + { + return _hiLoChartOpenValuesUseFontRelSize; + } + + + int hiLoChartOpenValuesFontRelSize() const + { + return _hiLoChartOpenValuesFontRelSize; + } + + + QColor hiLoChartOpenValuesColor() const + { + return _hiLoChartOpenValuesColor; + } + + void setHiLoChartPrintCloseValues( bool active, + QFont* font = 0, + int size = 14, + QColor* color = 0 ); + + bool hiLoChartPrintCloseValues() const + { + return _hiLoChartPrintCloseValues; + } + + + QFont hiLoChartCloseValuesFont() const + { + return _hiLoChartCloseValuesFont; + } + + + bool hiLoChartCloseValuesUseFontRelSize() const + { + return _hiLoChartCloseValuesUseFontRelSize; + } + + + int hiLoChartCloseValuesFontRelSize() const + { + return _hiLoChartCloseValuesFontRelSize; + } + + + QColor hiLoChartCloseValuesColor() const + { + return _hiLoChartCloseValuesColor; + } + + + void setHiLoChartPrintFirstValues( bool active, + QFont* font = 0, + uint size = 14, + QColor* color = 0 ) + { + setHiLoChartPrintOpenValues( active, font, size, color ); + } + + bool hiLoChartPrintFirstValues() const + { + return hiLoChartPrintOpenValues(); + } + + + QFont hiLoChartFirstValuesFont() const + { + return hiLoChartOpenValuesFont(); + } + + + bool hiLoChartFirstValuesUseFontRelSize() const + { + return hiLoChartOpenValuesUseFontRelSize(); + } + + int hiLoChartFirstValuesFontRelSize() const + { + return hiLoChartOpenValuesFontRelSize(); + } + + QColor hiLoChartFirstValuesColor() const + { + return hiLoChartOpenValuesColor(); + } + + void setHiLoChartPrintLastValues( bool active, + QFont* font = 0, + int size = 14, + QColor* color = 0 ) + { + setHiLoChartPrintCloseValues( active, font, size, color ); + } + + bool hiLoChartPrintLastValues() const + { + return hiLoChartPrintCloseValues(); + } + + QFont hiLoChartLastValuesFont() const + { + return hiLoChartCloseValuesFont(); + } + + bool hiLoChartLastValuesUseFontRelSize() const + { + return hiLoChartCloseValuesUseFontRelSize(); + } + + int hiLoChartLastValuesFontRelSize() const + { + return hiLoChartCloseValuesFontRelSize(); + } + + QColor hiLoChartLastValuesColor() const + { + return hiLoChartCloseValuesColor(); + } + + + // END HI/LO CHART-SPECIFIC + + +public: + // BOX WHISKER CHART-SPECIFIC + enum BWChartSubType { BWNormal, BWSimple = BWNormal }; + + enum BWStatVal { BWStatValSTART, + UpperOuterFence = BWStatValSTART, // first value stored in array + UpperInnerFence, + Quartile3, + Median, + Quartile1, + LowerInnerFence, + LowerOuterFence, + MaxValue, + MeanValue, + MinValue, + BWStatValEND = MinValue, // last value stored in array + // extra values - not stored in the array: + BWStatValOffEndValue, // the number of array values + BWStatValALL, // special flag reprsenting *all* array values + BWStatValUNKNOWN }; // to be returned when value is undefined +public slots: + + void setBWChartSubType( BWChartSubType bWChartSubType ) + { + _BWChartSubType = bWChartSubType; + emit changed(); + } + + BWChartSubType bWChartSubType() const + { + return _BWChartSubType; + } + + static QString bWChartSubTypeToString( BWChartSubType type ); + static BWChartSubType stringToBWChartSubType( const QString& string ); + static QString bWChartStatValToString( BWStatVal type ); + static BWStatVal stringToBWChartStatVal( const QString& string ); + + void setBWChartFences( double upperInner, double lowerInner, + double upperOuter, double lowerOuter ); + void bWChartFences( double& upperInner, double& lowerInner, + double& upperOuter, double& lowerOuter ) const; + + void setBWChartBrush( const QBrush& bWChartBrush ) + { + _BWChartBrush = bWChartBrush; + emit changed(); + } + + QBrush bWChartBrush() const + { + return _BWChartBrush; + } + + void setBWChartOutValMarkerSize( int size ) + { + _BWChartOutValMarkerSize = size; + } + + + int bWChartOutValMarkerSize() const + { + return _BWChartOutValMarkerSize; + } + + void setBWChartPrintStatistics( BWStatVal statValue, + bool active, + QFont* font = 0, + int size = 24, + QColor* color = 0, + QBrush* brush = 0 ); + + bool bWChartPrintStatistics( BWStatVal statValue ) const + { + return _BWChartStatistics[ statValue ].active; + } + + + QFont bWChartStatisticsFont( BWStatVal statValue ) const + { + return _BWChartStatistics[ statValue ].font; + } + + + bool bWChartStatisticsUseRelSize( BWStatVal statValue ) const + { + return _BWChartStatistics[ statValue ].useRelSize; + } + + + int bWChartStatisticsFontRelSize( BWStatVal statValue ) const + { + return _BWChartStatistics[ statValue ].relSize; + } + + + QColor bWChartStatisticsColor( BWStatVal statValue ) const + { + return _BWChartStatistics[ statValue ].color; + } + + QBrush bWChartStatisticsBrush( BWStatVal statValue ) const + { + return _BWChartStatistics[ statValue ].brush; + } + + +public: + // LEGENDS + + enum LegendPosition { NoLegend, LegendTop, LegendBottom, + LegendLeft, LegendRight, + LegendTopLeft, + LegendTopLeftTop, + LegendTopLeftLeft, + LegendTopRight, + LegendTopRightTop, + LegendTopRightRight, + LegendBottomLeft, + LegendBottomLeftBottom, + LegendBottomLeftLeft, + LegendBottomRight, + LegendBottomRightBottom, + LegendBottomRightRight + }; + +public slots: + void setLegendPosition( LegendPosition position ) + { + _legendPosition = position; + emit changed(); + } + + LegendPosition legendPosition() const + { + return _legendPosition; + } + + void setLegendOrientation( Orientation orientation ) + { + _legendOrientation = orientation; + emit changed(); + } + + Orientation legendOrientation() const + { + return _legendOrientation; + } + + + void setLegendShowLines( bool legendShowLines ) + { + _legendShowLines = legendShowLines; + emit changed(); + } + + bool legendShowLines() const + { + return _legendShowLines; + } + + + static QString legendPositionToString( LegendPosition pos ); + static LegendPosition stringToLegendPosition( const QString& string ); + +public: + enum LegendSource { LegendManual, LegendFirstColumn, LegendAutomatic }; + +public slots: + void setLegendSource( LegendSource source ) + { + _legendSource = source; + emit changed(); + } + + LegendSource legendSource() const + { + return _legendSource; + } + + static QString legendSourceToString( LegendSource source ); + static LegendSource stringToLegendSource( const QString& string ); + + void setLegendText( uint dataset, const QString& text ) + { + _legendText[ dataset ] = text; + emit changed(); + } + + QString legendText( uint dataset ) const + { + if( _legendText.find( dataset ) != _legendText.end() ) + return _legendText[ dataset ]; + else + return QString::null; + } + + + void setLegendTextColor( const QColor& color ) + { + _legendTextColor = color; + emit changed(); + } + + + QColor legendTextColor() const + { + return _legendTextColor; + } + + + void setLegendFont( const QFont& font, bool useFontSize ) + { + _legendFont = font; + _legendFontUseRelSize = ! useFontSize; + emit changed(); + } + + QFont legendFont() const + { + return _legendFont; + } + + + void setLegendFontUseRelSize( bool legendFontUseRelSize ) + { + _legendFontUseRelSize = legendFontUseRelSize; + emit changed(); + } + + bool legendFontUseRelSize() const + { + return _legendFontUseRelSize; + } + + void setLegendFontRelSize( int legendFontRelSize ) + { + _legendFontRelSize = legendFontRelSize; + emit changed(); + } + + int legendFontRelSize() const + { + return _legendFontRelSize; + } + + + void setLegendTitleText( const QString& text ) + { + _legendTitleText = text; + emit changed(); + } + + + QString legendTitleText() const + { + return _legendTitleText; + } + + + void setLegendTitleTextColor( const QColor& color ) + { + _legendTitleTextColor = color; + emit changed(); + } + + + QColor legendTitleTextColor() const + { + return _legendTitleTextColor; + } + + + + void setLegendTitleFont( const QFont& font, bool useFontSize ) + { + _legendTitleFont = font; + _legendTitleFontUseRelSize = ! useFontSize; + emit changed(); + } + + QFont legendTitleFont() const + { + return _legendTitleFont; + } + + + void setLegendTitleFontUseRelSize( bool legendTitleFontUseRelSize ) + { + _legendTitleFontUseRelSize = legendTitleFontUseRelSize; + emit changed(); + } + + bool legendTitleFontUseRelSize() const + { + return _legendTitleFontUseRelSize; + } + + void setLegendTitleFontRelSize( int legendTitleFontRelSize ) + { + _legendTitleFontRelSize = legendTitleFontRelSize; + emit changed(); + } + + int legendTitleFontRelSize() const + { + return _legendTitleFontRelSize; + } + + + void setLegendSpacing( uint space ) + { + _legendSpacing = space; + } + + + uint legendSpacing() const + { + return _legendSpacing; + } + + // END LEGENDS + + + + // AXES + + // Note if you change the parameters here, then you must also change them in wrappers/KDChartParametersWrapper.h + void setAxisType( uint n, const KDChartAxisParams::AxisType axisType ) + { + if ( n < KDCHART_MAX_AXES ) { + _axisSettings[ n ].params.setAxisType( axisType ); + emit changed(); + } + } + + void setAxisVisible( uint n, + const bool axisVisible ) + { + if ( n < KDCHART_MAX_AXES ) { + _axisSettings[ n ].params.setAxisVisible( axisVisible ); + emit changed(); + } + } + + bool axisVisible( uint n ) const + { + return n < KDCHART_MAX_AXES ? _axisSettings[ n ].params.axisVisible() + : false; + } + + void setAxisShowGrid( uint n, + bool axisShowGrid ); + bool showGrid() const; + void setAxisDatasets( uint n, + uint dataset, + uint dataset2 = KDCHART_NO_DATASET, + uint chart = 0 ); + bool axisDatasets( uint n, + uint& dataset, + uint& dataset2, + uint& chart ) const; + +public: +#if COMPAT_QT_VERSION >= 0x030000 + typedef QValueVector<uint> AxesArray; +#else + typedef QArray<uint> AxesArray; +#endif + +public slots: + bool chartAxes( uint chart, uint& cnt, AxesArray& axes ) const; + + void setAxisArea( const uint n, const QRect& areaRect ) + { + if ( n < KDCHART_MAX_AXES ) { + _axisSettings[ n ].params.setAxisTrueAreaRect( areaRect ); + // Do not do emit changed() here! + } + } + + void setAxisLabelsTouchEdges( uint n, bool axisLabelsTouchEdges ) + { + _axisSettings[ n ].params.setAxisLabelsTouchEdges( + axisLabelsTouchEdges ); + emit changed(); + } + + void setAxisLabelsVisible( uint n, + bool axisLabelsVisible ) + { + if ( n < KDCHART_MAX_AXES ) + _axisSettings[ n ].params.setAxisLabelsVisible( axisLabelsVisible ); + } + + void setAxisLabelsFont( uint n, + QFont axisLabelsFont, + int axisLabelsFontSize = 0, + QColor axisLabelsColor = Qt::black ); + + // Note if you change the parameters here, then you must also change them in wrappers/KDChartParametersWrapper.h + void setAxisLabelTextParams( uint n, + bool axisSteadyValueCalc = true, + QVariant axisValueStart = KDCHART_AXIS_LABELS_AUTO_LIMIT, + QVariant axisValueEnd = KDCHART_AXIS_LABELS_AUTO_LIMIT, + double axisValueDelta = KDCHART_AXIS_LABELS_AUTO_DELTA, + int axisDigitsBehindComma = KDCHART_AXIS_LABELS_AUTO_DIGITS, + int axisMaxEmptyInnerSpan = 67, + KDChartAxisParams::LabelsFromDataRow takeLabelsFromDataRow + = KDChartAxisParams::LabelsFromDataRowNo, + int labelTextsDataRow = 0, + QStringList* axisLabelStringList = 0, + QStringList* axisShortLabelsStringList = 0, + int axisValueLeaveOut = KDCHART_AXIS_LABELS_AUTO_LEAVEOUT, + KDChartAxisParams::ValueScale axisValueDeltaScale = KDChartAxisParams::ValueScaleNumerical ) + { + _axisSettings[ n ].params.setAxisValues( + axisSteadyValueCalc, + axisValueStart, + axisValueEnd, + axisValueDelta, + axisDigitsBehindComma, + axisMaxEmptyInnerSpan, + takeLabelsFromDataRow, + labelTextsDataRow, + axisLabelStringList, + axisShortLabelsStringList, + axisValueLeaveOut, + axisValueDeltaScale ); + emit changed(); + } + + + void setAxisLabelStringParams( uint n, + QStringList* axisLabelStringList, + QStringList* axisShortLabelStringList, + const QString& valueStart = QString::null, + const QString& valueEnd = QString::null ); + + + void setAxisParams( uint n, + const KDChartAxisParams& axisParams ); + + const KDChartAxisParams& axisParams( uint n ) const + { + return n < KDCHART_MAX_AXES ? _axisSettings[ n ].params + : _axisSettings[ KDCHART_MAX_AXES ].params; + } + + // some convenience functions refering to the first KDChartCustomBox + // that is anchored to the axis n. + bool findFirstAxisCustomBoxID( uint n, uint& boxID ) const; + void setAxisTitle( uint n, const QString& axisTitle ); + + QString axisTitle( uint n ) const; + void setAxisTitleColor( uint n, QColor axisTitleColor ); + QColor axisTitleColor( uint n ) const; + void setAxisTitleFont( uint n, QFont axisTitleFont ); + void setAxisTitleFont( uint n, QFont axisTitleFont, bool useFixedFontSize ); + QFont axisTitleFont( uint n ) const; + void setAxisTitleFontUseRelSize( uint n, bool useRelSize ); + bool axisTitleFontUseRelSize( uint n ) const; + void setAxisTitleFontRelSize( uint n, int axisTitleFontRelSize ); + int axisTitleFontRelSize( uint n ) const; + + QRect axisArea( const uint n ) const + { + if ( n < KDCHART_MAX_AXES ) { + return _axisSettings[ n ].params.axisTrueAreaRect(); + // Do not do emit changed() here! + }else{ + return QRect(QPoint(0,0),QSize(0,0)); + } + } + // END AXES + + + + QRect dataArea() const { return _dataAreaRect; } + QRect legendArea() const { return _legendAreaRect; } + + + + // HEADERS/FOOTERS + +public: + + /* + READ before changing/enhancing the following enum + ================================================= + + Please note: The following enum is made in a way that + sections with smaller Y values come first. + Thus one can construct a loop itterating over theHdFtPos values + and so going from the top of the screen toiwards the bottom... + + Also please note that extending this enum shound NOT break the + tripple groups (numbers 0/1/2, 3/4/5, and 6/7/8) since the + algorithms in + KDChartPainter::setupGeometry() and + KDChartPainter::paintHeaderFooter() rely on this schema. + Also don't forget to always update the auxiliary values + HdFtPosSTART, HdFtPosHeadersSTART, + HdFtPosHeadersEND, HdFtPosFootersSTART, + HdFtPosFootersEND, and HdFtPosEND muessen - these are used + fpr other itarators... + */ + enum HdFtPos{ // start of all sections + HdFtPosSTART = 0, + + // headers: + HdFtPosHeadersSTART = 0, + // top-most headers + HdFtPosHeaders0START = 0, + HdFtPosHeader0 = 0, + HdFtPosHeader0L = 1, + HdFtPosHeader0R = 2, + HdFtPosHeaders0END = 2, + // normal ( == middle ) headers + HdFtPosHeaders1START = 3, + HdFtPosHeader = 3, // center + HdFtPosHeaderL = 4, // left + HdFtPosHeaderR = 5, // right + HdFtPosHeaders1END = 5, + // bottom headers + HdFtPosHeaders2START = 6, + HdFtPosHeader2 = 6, + HdFtPosHeader2L = 7, + HdFtPosHeader2R = 8, + HdFtPosHeaders2END = 8, + + HdFtPosHeadersEND = 8, + + // footers: + HdFtPosFootersSTART = 9, + // normal ( == middle ) footers + HdFtPosFooters1START = 9, + HdFtPosFooter = 9, + HdFtPosFooterL = 10, + HdFtPosFooterR = 11, + HdFtPosFooters1END = 11, + // bottom footers + HdFtPosFooters2START = 12, + HdFtPosFooter2 = 12, + HdFtPosFooter2L = 13, + HdFtPosFooter2R = 14, + HdFtPosFooters2END = 14, + // top-most footers + HdFtPosFooters0START = 15, + HdFtPosFooter0 = 15, + HdFtPosFooter0L = 16, + HdFtPosFooter0R = 17, + HdFtPosFooters0END = 17, + + HdFtPosFootersEND = 17, + + HdFtPosEND = 17 }; + +public slots: + + void setHeaderFooterText( uint pos, const QString& text ); + QString headerFooterText( uint pos ) const; + const QRect& headerFooterRect( uint pos ) const; + void setHeaderFooterColor( uint pos, const QColor color ); + QColor headerFooterColor( uint pos ) const; + void setHeaderFooterFont( uint pos, const QFont& font, + bool fontUseRelSize, + int fontRelSize ); + QFont headerFooterFont( uint pos ) const; + bool headerFooterFontUseRelSize( uint pos ) const; + int headerFooterFontRelSize( uint pos ) const; + + + // quick&dirty functions: access Header, Header2 and Footer + // without having to specify the HdFtPos + // + // This may be convenient for specifying simple charts with + // up to two centered header(s) and up to one centered footer. + + + void setHeader1Text( const QString& text ) + { + _hdFtParams[ HdFtPosHeader ]._text = text; + emit changed(); + } + + + QString header1Text() const + { + return _hdFtParams[ HdFtPosHeader ]._text; + } + + void setHeader1Font( const QFont& font ) + { + _hdFtParams[ HdFtPosHeader ]._font = font; + emit changed(); + } + + + QFont header1Font() const + { + return _hdFtParams[ HdFtPosHeader ]._font; + } + + void setHeader2Text( const QString& text ) + { + _hdFtParams[ HdFtPosHeader2 ]._text = text; + emit changed(); + } + + + QString header2Text() const + { + return _hdFtParams[ HdFtPosHeader2 ]._text; + } + + void setHeader2Font( const QFont& font ) + { + _hdFtParams[ HdFtPosHeader2 ]._font = font; + emit changed(); + } + + + QFont header2Font() const + { + return _hdFtParams[ HdFtPosHeader2 ]._font; + } + + + + void setFooterText( const QString& text ) + { + _hdFtParams[ HdFtPosFooter ]._text = text; + emit changed(); + } + + + QString footerText() const + { + return _hdFtParams[ HdFtPosFooter ]._text; + } + + void setFooterFont( const QFont& font ) + { + _hdFtParams[ HdFtPosFooter ]._font = font; + emit changed(); + } + + + QFont footerFont() const + { + return _hdFtParams[ HdFtPosFooter ]._font; + } + + + /** + \internal + */ + void __internalStoreHdFtRect( int pos, QRect rect ) + { + if ( 0 <= pos && HdFtPosEND >= pos ) + _hdFtParams[ pos ].setRect( rect ); + } + + + // END HEADERS/FOOTERS + + + +public: + KDChartParams(); + virtual ~KDChartParams(); + +public slots: + void loadAxesFormXML(int& curAxisSettings, QDomElement& element); + bool loadXML( const QDomDocument& doc ); + + void saveAxesToXML(QDomDocument& doc, QDomElement& docRoot) const; + QDomDocument saveXML( bool withPI = true ) const; + +public: + friend QTextStream& operator<<( QTextStream& s, const KDChartParams& p ); + friend QTextStream& operator>>( QTextStream& s, KDChartParams& p ); + +public slots: + static void createChartValueNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, + const QVariant& valY, + const QVariant& valX, + const int& propID ); + static void createColorMapNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, + const QMap< uint, QColor >& map ); + static void createDoubleMapNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, + const QMap< int, double >& map ); + static void createChartFontNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, + const QFont& font, bool useRelFont, + int relFont, + int minFont=-1 ); + static bool readColorMapNode( const QDomElement& element, + QMap<uint,QColor>* map ); + static bool readDoubleMapNode( const QDomElement& element, + QMap<int,double>* map ); + static bool readChartFontNode( const QDomElement& element, QFont& font, + bool& useRelFont, int& relFontSize, + int* minFontSize=0 ); + static bool readChartValueNode( const QDomElement& element, + QVariant& valY, + QVariant& valX, + int& propID ); + + + // do *not* call this function manually: it is used by KDChartPainter::setupGeometry() + void setDataArea( const QRect& areaRect ) + { + _dataAreaRect = areaRect; + // Do not do emit changed() here! + } + // do *not* call this function manually: it is used by KDChartPainter::setupGeometry() + void setLegendArea( const QRect& areaRect ) + { + _legendAreaRect = areaRect; + // Do not do emit changed() here! + } + + +signals: + void changed(); + + +protected: + void setBarsDisplayed( int barsDisplayed ) + { + _barsDisplayed = barsDisplayed; + } + void setBarsLeft( int barsLeft ) + { + _barsLeft = barsLeft; + } + + +private: + QString dataRegionFrameAreaName( uint dataRow, + uint dataCol, + uint data3rd ); + void recomputeShadowColors(); + void insertDefaultAxisTitleBox( uint n, + bool setTitle, const QString& axisTitle, + bool setColor, const QColor& axisTitleColor, + bool setFont, const QFont& axisTitleFont, + bool setFontUseRel, bool useFontSize, + bool setFontRelSize, int axisTitleFontRelSize ); + + static QColor _internalPointer_DataValueAutoColor; + + KDChartPropertySet* tempPropSetA; + KDChartPropertySet* tempPropSetB; + + bool _optimizeOutputForScreen; + int _globalLeadingLeft; + int _globalLeadingTop; + int _globalLeadingRight; + int _globalLeadingBottom; + ChartType _chartType; + ChartType _additionalChartType; + int _numValues; + QFont _defaultFont; + + typedef QDict < KDChartFrameSettings > AreaDict; + AreaDict _areaDict; + + CustomBoxDict _customBoxDict; + bool _customBoxDictMayContainHoles; + QRect _noRect; + KDFrame _noFrame; + + KDChartFrameSettings _noFrameSettings; + + class ModeAndChart + { + public: + ModeAndChart() + : _mode( UnknownMode ), _chart( KDCHART_NO_CHART ) + {} + ModeAndChart( SourceMode mode, uint chart ) + : _mode( mode ), _chart( chart ) + {} + + SourceMode mode() const + { + return _mode; + }; + uint chart() const + { + return _chart; + }; + + void setMode( SourceMode mode ) + { + _mode = mode; + }; + void setChart( uint chart ) + { + _chart = chart; + }; + private: + SourceMode _mode; + uint _chart; + }; + + typedef QMap < uint, ModeAndChart > ModeAndChartMap; + ModeAndChartMap _dataSourceModeAndChart; + bool _setChartSourceModeWasUsed; + QMap < uint, QColor > _dataColors; + uint _maxDatasetSourceMode; + KDChartPropertySetList _propertySetList; + double _shadowBrightnessFactor; + Qt::BrushStyle _shadowPattern; + bool _threeDShadowColors; + uint _maxDatasetColor; + QMap < uint, QColor > _dataColorsShadow1; + QMap < uint, QColor > _dataColorsShadow2; + QColor _outlineDataColor; + uint _outlineDataLineWidth; + PenStyle _outlineDataLineStyle; + + + struct PrintDataValuesSettings { + bool _printDataValues; + int _divPow10; + int _digitsBehindComma; + QFont _dataValuesFont; + bool _dataValuesUseFontRelSize; + int _dataValuesFontRelSize; + QColor _dataValuesColor; + QBrush _dataValuesBrush; + bool _dataValuesAutoColor; + KDChartEnums::PositionFlag _dataValuesAnchorNegativePosition; + uint _dataValuesAnchorNegativeAlign; + int _dataValuesAnchorNegativeDeltaX; + int _dataValuesAnchorNegativeDeltaY; + int _dataValuesNegativeRotation; + KDChartEnums::PositionFlag _dataValuesAnchorPositivePosition; + uint _dataValuesAnchorPositiveAlign; + int _dataValuesAnchorPositiveDeltaX; + int _dataValuesAnchorPositiveDeltaY; + int _dataValuesPositiveRotation; + KDChartEnums::TextLayoutPolicy _dataValuesLayoutPolicy; + bool _dataValuesShowInfinite; + + bool _useDefaultFontParams; // internal flag, do NOT store it! + }; + PrintDataValuesSettings _printDataValuesSettings; + PrintDataValuesSettings _printDataValuesSettings2; + + bool _allowOverlappingDataValueTexts; + BarChartSubType _barChartSubType; + bool _threeDBars; + int _threeDBarAngle; + double _threeDBarDepth; + double _cosThreeDBarAngle; + int _datasetGap; + bool _datasetGapIsRelative; + int _valueBlockGap; + bool _valueBlockGapIsRelative; + int _barWidth; + int _userWidth; + bool _solidExcessArrows; + // volatile bar chart information, not saved in the stream + QRect _dataAreaRect; + QRect _legendAreaRect; + int _barsDisplayed; + int _barsLeft; + + // LINES/AREAS-specific + LineChartSubType _lineChartSubType; + bool _threeDLines; + int _threeDLineDepth; + int _threeDLineXRotation; + int _threeDLineYRotation; + bool _lineMarker; + LineMarkerStyleMap _lineMarkerStyles; + uint _maxDatasetLineMarkerStyle; + QSize _lineMarkerSize; + QColor _lineColor; + int _lineWidth; + PenStyle _lineStyle; + typedef QMap<uint, PenStyle> LineStyleMap; + LineStyleMap _datasetLineStyles; + AreaChartSubType _areaChartSubType; + AreaLocation _areaLocation; + + + // POLAR-specific + PolarChartSubType _polarChartSubType; + bool _polarMarker; + PolarMarkerStyleMap _polarMarkerStyles; + uint _maxDatasetPolarMarkerStyle; + QSize _polarMarkerSize; + int _polarLineWidth; + int _polarZeroDegreePos; + bool _polarRotateCircularLabels; + struct _polarDelimsAndLabelStruct { + bool showDelimiters; + bool showLabels; + }; + _polarDelimsAndLabelStruct _polarDelimsAndLabels[ 1 + KDCHART_MAX_POLAR_DELIMS_AND_LABELS_POS ]; + + + + // PIES/RINGS + bool _explode; + double _explodeFactor; + ExplodeFactorsMap _explodeFactors; + QValueList<int> _explodeList; + bool _threeDPies; + int _threeDPieHeight; + int _pieStart; + int _ringStart; + bool _relativeRingThickness; + + + // HI-LO CHARTS + + HiLoChartSubType _hiLoChartSubType; + bool _hiLoChartPrintLowValues; + QFont _hiLoChartLowValuesFont; + bool _hiLoChartLowValuesUseFontRelSize; + int _hiLoChartLowValuesFontRelSize; + QColor _hiLoChartLowValuesColor; + bool _hiLoChartPrintHighValues; + QFont _hiLoChartHighValuesFont; + bool _hiLoChartHighValuesUseFontRelSize; + int _hiLoChartHighValuesFontRelSize; + QColor _hiLoChartHighValuesColor; + bool _hiLoChartPrintOpenValues; + QFont _hiLoChartOpenValuesFont; + bool _hiLoChartOpenValuesUseFontRelSize; + int _hiLoChartOpenValuesFontRelSize; + QColor _hiLoChartOpenValuesColor; + bool _hiLoChartPrintCloseValues; + QFont _hiLoChartCloseValuesFont; + bool _hiLoChartCloseValuesUseFontRelSize; + int _hiLoChartCloseValuesFontRelSize; + QColor _hiLoChartCloseValuesColor; + + + + // BOX AND WHISKER CHARTS + + BWChartSubType _BWChartSubType; + + struct BWChartStatistics { + double value; + int y; + bool active; + QFont font; + bool useRelSize; + int relSize; + QColor color; + QBrush brush; + }; + BWChartStatistics _BWChartStatistics[ BWStatValOffEndValue ]; + double _BWChartFenceUpperInner; + double _BWChartFenceLowerInner; + double _BWChartFenceUpperOuter; + double _BWChartFenceLowerOuter; + int _BWChartOutValMarkerSize; + QBrush _BWChartBrush; + + + // LEGENDS + + LegendPosition _legendPosition; + Qt::Orientation _legendOrientation; + bool _legendShowLines; + LegendSource _legendSource; + QMap < int, QString > _legendText; + QColor _legendTextColor; + QFont _legendFont; + bool _legendFontUseRelSize; + int _legendFontRelSize; + QString _legendTitleText; + QColor _legendTitleTextColor; + QFont _legendTitleFont; + bool _legendTitleFontUseRelSize; + int _legendTitleFontRelSize; + uint _legendSpacing; + + + // AXES (private) + // Must be exported because of the inline methods + struct KDCHART_EXPORT AxisSettings + { + uint dataset; + uint dataset2; + uint chart; + KDChartAxisParams params; + AxisSettings() + { + dataset = KDCHART_NO_DATASET; + dataset2 = KDCHART_NO_DATASET; + chart = KDCHART_NO_CHART; + } + }; + + // 13 == KDCHART_MAX_AXES + 1 + AxisSettings _axisSettings[ 13 ]; + // END AXES (private) + + + // HEADER/FOOTER (private) + // Must be exported because of the inline methods + struct KDCHART_EXPORT HdFtParams + { + QString _text; + QColor _color; + QFont _font; + bool _fontUseRelSize; + int _fontRelSize; + HdFtParams() + { + _color = QColor( Qt::black ); + _font = QFont( "helvetica", 10, QFont::Normal, false ); + _fontUseRelSize = true; + _fontRelSize = 8; // per default quite small + } + void setRect( QRect rect ) + { + _rect = rect; + } + const QRect& rect() const + { + return _rect; + } + private: + // temporary data that are NOT to be stored within sessions: + QRect _rect; + }; + + HdFtParams _hdFtParams[ HdFtPosEND + 1 ]; + // END HEADER/FOOTER (private) +}; + + +QTextStream& operator<<( QTextStream& s, const KDChartParams& p ); + + +QTextStream& operator>>( QTextStream& s, KDChartParams& p ); + + +#endif diff --git a/kchart/kdchart/KDChartParamsWrapper.cpp b/kchart/kdchart/KDChartParamsWrapper.cpp new file mode 100644 index 00000000..49e51668 --- /dev/null +++ b/kchart/kdchart/KDChartParamsWrapper.cpp @@ -0,0 +1,73 @@ +#include "KDChartParamsWrapper.h" +#include <KDChartParams.h> + +KDChartParamsWrapper::KDChartParamsWrapper( KDChartParams* data ) :QObject(0), _data(data) +{ +} + +const KDChartAxisParams* KDChartParamsWrapper::axisParams( uint n ) const +{ + const KDChartAxisParams& a = _data->axisParams( n ); + return &a; +} + +void KDChartParamsWrapper::setExplodeValues( QVariant explodeList ) +{ + if ( !explodeList.type() == QVariant::List ) + return; + QValueList<QVariant> list = explodeList.toList(); + + QValueList<int> res; + for( QValueList<QVariant>::Iterator it = list.begin(); it != list.end(); ++it ) { + int x = (*it).toInt(); + res.append(x); + } + _data->setExplodeValues( res ); +} + +void KDChartParamsWrapper::setExplodeFactors( QVariant factors ) +{ + if ( !factors.type() == QVariant::List ) + return; + + QValueList<QVariant> list = factors.toList(); + + KDChartParams::ExplodeFactorsMap res; + int i = 0; + for( QValueList<QVariant>::Iterator it = list.begin(); it != list.end(); ++it, ++i ) { + res[i] = (*it).toDouble(); + } + _data->setExplodeFactors( res ); +} + +void KDChartParamsWrapper::setAxisLabelStringParams( uint n, QVariant axisLabelStringList, QVariant axisShortLabelStringList, + const QString& valueStart, const QString& valueEnd ) +{ + QStringList l1 = axisLabelStringList.toStringList(); + QStringList l2 = axisShortLabelStringList.toStringList(); + _data->setAxisLabelStringParams( n, &l1, &l2, valueStart, valueEnd ); +} + +void KDChartParamsWrapper::setAxisLabelTextParams( uint n, + bool axisSteadyValueCalc, + QVariant axisValueStart, + QVariant axisValueEnd, + double axisValueDelta, + int axisDigitsBehindComma, + int axisMaxEmptyInnerSpan, + int takeLabelsFromDataRow, + int labelTextsDataRow, + const QVariant& axisLabelStringList, + const QVariant& axisShortLabelsStringList, + int axisValueLeaveOut, + int axisValueDeltaScale ) { + + QStringList l1 = axisLabelStringList.toStringList(); + QStringList l2 = axisShortLabelsStringList.toStringList(); + + _data->setAxisLabelTextParams( n, axisSteadyValueCalc, axisValueStart, axisValueEnd, axisValueDelta, axisDigitsBehindComma, axisMaxEmptyInnerSpan, + (KDChartAxisParams::LabelsFromDataRow) takeLabelsFromDataRow, labelTextsDataRow, &l1, &l2, + axisValueLeaveOut, (KDChartAxisParams::ValueScale) axisValueDeltaScale ); +} + +#include "KDChartParamsWrapper.moc" diff --git a/kchart/kdchart/KDChartParamsWrapper.h b/kchart/kdchart/KDChartParamsWrapper.h new file mode 100644 index 00000000..827cef11 --- /dev/null +++ b/kchart/kdchart/KDChartParamsWrapper.h @@ -0,0 +1,175 @@ +#ifndef KDCHARTPARAMSWRAPPER_H +#define KDCHARTPARAMSWRAPPER_H +#include <qobject.h> +#include <KDFrame.h> +#include <KDChartParams.h> +#include <qvariant.h> +class KDChartAxisParams; + +class KDChartParamsWrapper :public QObject +{ + Q_OBJECT + +public: + KDChartParamsWrapper( KDChartParams* ); +public slots: + const KDChartAxisParams* axisParams( uint n ) const; + void setExplodeValues( QVariant explodeList ); + void setExplodeFactors( QVariant factors ); + void setAxisLabelStringParams( uint n, + QVariant axisLabelStringList, + QVariant axisShortLabelStringList, + const QString& valueStart = QString::null, + const QString& valueEnd = QString::null ); + + // These methods need to be here, for the enums to work. + void setSimpleFrame( uint area, + int outerGapX = 0, + int outerGapY = 0, + int innerGapX = 0, + int innerGapY = 0, + bool addFrameWidthToLayout = true, + bool addFrameHeightToLayout = true, + int simpleFrame = KDFrame::FrameFlat, + int lineWidth = 1, + int midLineWidth = 0, + QPen pen = QPen(), + QBrush background = QBrush( Qt::NoBrush ), + const QPixmap* backPixmap = 0, // no pixmap + int backPixmapMode = KDFrame::PixStretched, + int shadowWidth = 0, + int sunPos = KDFrame::CornerTopLeft ) + { + _data->setSimpleFrame(area, outerGapX, outerGapY, innerGapX, innerGapY, addFrameWidthToLayout, addFrameHeightToLayout, + (KDFrame::SimpleFrame) simpleFrame, lineWidth, midLineWidth, pen, background, backPixmap, + (KDFrame::BackPixmapMode) backPixmapMode, shadowWidth, (KDFrame::CornerName) sunPos ); + } + + void setDataRegionFrame( uint dataRow, + uint dataCol, + uint data3rd, // important: we ignore the data3rd parameter for now! + int innerGapX = 0, + int innerGapY = 0, + bool addFrameWidthToLayout = true, + bool addFrameHeightToLayout = true, + int simpleFrame = KDFrame::FrameFlat, + int lineWidth = 1, + int midLineWidth = 0, + QPen pen = QPen(), + int shadowWidth = 0, + int sunPos = KDFrame::CornerTopLeft ) + { + _data->setDataRegionFrame( dataRow, dataCol, + data3rd, innerGapX, innerGapY, addFrameWidthToLayout, addFrameHeightToLayout, + (KDFrame::SimpleFrame) simpleFrame, lineWidth, midLineWidth, pen, shadowWidth, (KDFrame::CornerName) sunPos ); + } + + bool moveDataRegionFrame( uint oldDataRow, + uint oldDataCol, + uint oldData3rd, // important: we ignore the data3rd parameter for now! + uint newDataRow, + uint newDataCol, + uint newData3rd// important: we ignore the data3rd parameter for now! + ) + { + return _data->moveDataRegionFrame( + oldDataRow, oldDataCol, oldData3rd, newDataRow, newDataCol, newData3rd ); + } + + void setPrintDataValues( bool active, + uint chart = KDCHART_ALL_CHARTS, + int divPow10 = 0, + int digitsBehindComma = KDCHART_DATA_VALUE_AUTO_DIGITS, + QFont* font = 0, + uint size = UINT_MAX, // <-- makes us use the *default* font params + // by IGNORING settings of + // the following parameters! + const QColor* color = KDCHART_DATA_VALUE_AUTO_COLOR, + int negativePosition = KDChartEnums::PosCenter, + uint negativeAlign = Qt::AlignCenter, + int negativeDeltaX = 0, + int negativeDeltaY = 0, + int negativeRotation = 0, + int positivePosition = KDChartEnums::PosCenter, + uint positiveAlign = Qt::AlignCenter, + int positiveDeltaX = 0, + int positiveDeltaY = 0, + int positiveRotation = 0, + int policy = KDChartEnums::LayoutPolicyRotate ) + { + _data->setPrintDataValues( active, chart, divPow10, digitsBehindComma,font, size, color, (KDChartEnums::PositionFlag) negativePosition, + negativeAlign, negativeDeltaX, negativeDeltaY, negativeRotation, (KDChartEnums::PositionFlag) positivePosition, + positiveAlign, positiveDeltaX, positiveDeltaY, positiveRotation, (KDChartEnums::TextLayoutPolicy) policy ); + } + + + void setDataValuesPlacing( int position, + uint align, + int deltaX, + int deltaY, + int rotation, + bool specifyingPositiveValues = true, + uint chart = KDCHART_ALL_CHARTS ) + { + _data->setDataValuesPlacing( (KDChartEnums::PositionFlag) position, align, deltaX, deltaY, rotation, specifyingPositiveValues, chart ); + } + + + void setDataValuesPolicy( int policy = KDChartEnums::LayoutPolicyRotate, + uint chart = KDCHART_ALL_CHARTS ) + { + _data->setDataValuesPolicy( (KDChartEnums::TextLayoutPolicy) policy, chart ); + } + + + void setPolarDelimsAndLabelsAtPos( int pos, + bool showDelimiters, + bool showLabels ) + { + _data->setPolarDelimsAndLabelsAtPos( (KDChartEnums::PositionFlag) pos, showDelimiters, showLabels ); + } + + + bool polarDelimAtPos( int pos ) const + { + return _data->polarDelimAtPos( (KDChartEnums::PositionFlag) pos ); + } + + + + bool polarLabelsAtPos( int pos ) const + { + return _data->polarLabelsAtPos( (KDChartEnums::PositionFlag) pos ); + } + + + void setAxisType( uint n, int axisType ) + { + _data->setAxisType( n, (KDChartAxisParams::AxisType) axisType ); + } + + + void setAxisLabelTextParams( uint n, + bool axisSteadyValueCalc = true, + QVariant axisValueStart = KDCHART_AXIS_LABELS_AUTO_LIMIT, + QVariant axisValueEnd = KDCHART_AXIS_LABELS_AUTO_LIMIT, + double axisValueDelta = KDCHART_AXIS_LABELS_AUTO_DELTA, + int axisDigitsBehindComma = KDCHART_AXIS_LABELS_AUTO_DIGITS, + int axisMaxEmptyInnerSpan = 67, + int takeLabelsFromDataRow = KDChartAxisParams::LabelsFromDataRowNo, + int labelTextsDataRow = 0, + const QVariant& axisLabelStringList = QStringList(), + const QVariant& axisShortLabelsStringList = QStringList(), + int axisValueLeaveOut = KDCHART_AXIS_LABELS_AUTO_LEAVEOUT, + int axisValueDeltaScale = KDChartAxisParams::ValueScaleNumerical ); + + + +private: + KDChartParams* _data; +}; + + + +#endif /* KDCHARTPARAMSWRAPPER_H */ + diff --git a/kchart/kdchart/KDChartParams_frame.cpp b/kchart/kdchart/KDChartParams_frame.cpp new file mode 100644 index 00000000..c873a0a1 --- /dev/null +++ b/kchart/kdchart/KDChartParams_frame.cpp @@ -0,0 +1,288 @@ +/* -*- 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. + ** + **********************************************************************/ + +#include "KDChartParams.h" +#include <KDXMLTools.h> + + +/** + \class KDChartParams::KDChartFrameSettings KDChartParams.h + + Stores the frame settings for one of the chart areas: + \li frame (consisting of edges, corners, background, shadow) + \li distance of frame to inner area + \li distance of frame to surrounding parts of the chart + + \sa setSimpleFrame, setFrame, frameSettings + */ + + +/** + Constructor. Setting default values. + */ +KDChartParams::KDChartFrameSettings::KDChartFrameSettings() +{ + _dataRow = 0; + _dataCol = 0; + _data3rd = 0; + _frame = 0; + resetFrame(); + _outerGapX = 0; + _outerGapY = 0; + _innerGapX = 0; + _innerGapY = 0; + _addFrameWidthToLayout = true; + _addFrameHeightToLayout = true; +} + + +/** + Constructor. + + \param outerGap Distance between the frame and the surrounding parts of the chart. + \param innerGap Distance between the frame and inner area. + \param addFrameWidthToLayout If true, shrink inner area so the area AND its frame will occupy + the same space of the chart as the area would occupy if no frame were drawn. + If false, the frame is drawn around the area without taking care not to override other + content of the chart. + \param frame The frame settings to be used for this area. The values of this parameter + will be copied into a KDFrame object stored internally in this KDChartFrameSettings so + it is save to use the same KDFrame object for specifying the settings of more than one area. + */ +KDChartParams::KDChartFrameSettings::KDChartFrameSettings( + uint dataRow, + uint dataCol, + uint data3rd, + const KDFrame& frame, + int outerGapX, + int outerGapY, + int innerGapX, + int innerGapY, + bool addFrameWidthToLayout, + bool addFrameHeightToLayout ) + : _dataRow( dataRow ), + _dataCol( dataCol ), + _data3rd( data3rd ), + _frame( 0 ), + _outerGapX( outerGapX ), + _outerGapY( outerGapY ), + _innerGapX( innerGapX ), + _innerGapY( innerGapY ), + _addFrameWidthToLayout( addFrameWidthToLayout ), +_addFrameHeightToLayout( addFrameHeightToLayout ) +{ + resetFrame(); + KDFrame::deepCopy(*_frame, frame); +} + + + +/** + Destructor. Only defined to have it virtual. + */ +KDChartParams::KDChartFrameSettings::~KDChartFrameSettings() +{ + delete _frame; +} + +/** + Reads data from a DOM element node that represents a frame + settings object and fills a KDChartFrameSettings object + with the data. + + \param element the DOM element to read from + \param settings the frame settings object to read the data into + */ +bool KDChartParams::KDChartFrameSettings::readFrameSettingsNode( const QDomElement& element, + KDChartFrameSettings& settings, + uint& areaId ) +{ + bool ok = true; + KDFrame tempFrame; + int tempId = KDChartEnums::AreaUNKNOWN; + int tempDataRow = 0; // these 3 need initialization since they were not + int tempDataCol = 0;// existent in previous releases of KDChart + int tempData3rd = 0; + + int tempOuterGapX, tempOuterGapY, tempInnerGapX, tempInnerGapY; + bool tempAddFrameWidthToLayout, tempAddFrameHeightToLayout; + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Frame" ) { + ok = ok & KDFrame::readFrameNode( element, tempFrame ); + } else if( tagName == "AreaId" ) { + ok = ok & KDXML::readIntNode( element, tempId ); + } else if( tagName == "DataRow" ) { + ok = ok & KDXML::readIntNode( element, tempDataRow ); + } else if( tagName == "DataCol" ) { + ok = ok & KDXML::readIntNode( element, tempDataCol ); + } else if( tagName == "Data3rd" ) { + ok = ok & KDXML::readIntNode( element, tempData3rd ); + } else if( tagName == "OuterGapX" ) { + ok = ok & KDXML::readIntNode( element, tempOuterGapX ); + } else if( tagName == "OuterGapY" ) { + ok = ok & KDXML::readIntNode( element, tempOuterGapY ); + } else if( tagName == "InnerGapX" ) { + ok = ok & KDXML::readIntNode( element, tempInnerGapX ); + } else if( tagName == "InnerGapY" ) { + ok = ok & KDXML::readIntNode( element, tempInnerGapY ); + } else if( tagName == "AddFrameWidthToLayout" ) { + ok = ok & KDXML::readBoolNode( element, tempAddFrameWidthToLayout ); + } else if( tagName == "AddFrameHeightToLayout" ) { + ok = ok & KDXML::readBoolNode( element, tempAddFrameHeightToLayout ); + } else { + qDebug( "Unknown tag in frame settings" ); + } + } + node = node.nextSibling(); + } + + if( ok ) { + settings.resetFrame(); + KDFrame::deepCopy(*settings._frame, tempFrame); + + settings._dataRow = tempDataRow; + settings._dataCol = tempDataCol; + settings._data3rd = tempData3rd; + settings._outerGapX = tempOuterGapX; + settings._outerGapY = tempOuterGapY; + settings._innerGapX = tempInnerGapX; + settings._innerGapY = tempInnerGapY; + settings._addFrameWidthToLayout = tempAddFrameWidthToLayout; + settings._addFrameHeightToLayout = tempAddFrameHeightToLayout; + areaId = tempId; + } + + return ok; +} + + + +/** + \fn const KDFrame& KDChartParams::KDChartFrameSettings::frame() const + + Return the KDFrame object used for drawing this areas frame. + */ + + +/** + \fn int KDChartParams::KDChartFrameSettings::innerGapX() const + + Return the X-distance between the frame and the inner area. + */ + +/** + \fn int KDChartParams::KDChartFrameSettings::innerGapY() const + + Return the Y-distance between the frame and the inner area. + */ + +/** + \fn int KDChartParams::KDChartFrameSettings::outerGapX() const + + Return the X-distance between the frame and the surrounding parts of the chart. + */ + +/** + \fn int KDChartParams::KDChartFrameSettings::outerGapY() const + + Return the Y-distance between the frame and the surrounding parts of the chart. + */ + +/** + \fn bool KDChartParams::KDChartFrameSettings::addFrameWidthToLayout() const + + Return whether the inner area will shrink the area AND its frame will occupy + the same space of the chart as the area would occupy if no frame were drawn. + If false, the frame is drawn around the area without taking care not to override other + content of the chart. + + \sa addFrameHeightToLayout + */ + + +/** + \fn bool KDChartParams::KDChartFrameSettings::addFrameHeightToLayout() const + + Return whether the inner area will shrink the area AND its frame will occupy + the same space of the chart as the area would occupy if no frame were drawn. + If false, the frame is drawn around the area without taking care not to override other + content of the chart. + + \sa addFrameWidthToLayout + */ + + + +/** + Creates a DOM element node that represents a frame settings + object for use in a DOM document. + + \param document the DOM document to which the node will belong + \param parent the parent node to which the new node will be appended + \param elementName the name of the new node + \param settings the frame settings to be represented + */ +void KDChartParams::KDChartFrameSettings::createFrameSettingsNode( QDomDocument& document, + QDomNode& parent, + const QString& elementName, + const KDChartParams::KDChartFrameSettings* settings, + uint areaId ) +{ + QDomElement frameSettingsElement = document.createElement( elementName ); + parent.appendChild( frameSettingsElement ); + if( settings->_frame ) + KDFrame::createFrameNode( document, frameSettingsElement, "Frame", + *settings->_frame ); + KDXML::createIntNode( document, frameSettingsElement, "AreaId", + areaId ); + KDXML::createIntNode( document, frameSettingsElement, "DataRow", + settings->_dataRow ); + KDXML::createIntNode( document, frameSettingsElement, "DataCol", + settings->_dataCol ); + KDXML::createIntNode( document, frameSettingsElement, "Data3rd", + settings->_data3rd ); + KDXML::createIntNode( document, frameSettingsElement, "OuterGapX", + settings->_outerGapX ); + KDXML::createIntNode( document, frameSettingsElement, "OuterGapY", + settings->_outerGapY ); + KDXML::createIntNode( document, frameSettingsElement, "InnerGapX", + settings->_innerGapX ); + KDXML::createIntNode( document, frameSettingsElement, "InnerGapY", + settings->_innerGapY ); + KDXML::createBoolNode( document, frameSettingsElement, + "AddFrameWidthToLayout", + settings->_addFrameWidthToLayout ); + KDXML::createBoolNode( document, frameSettingsElement, + "AddFrameHeightToLayout", + settings->_addFrameHeightToLayout ); +} diff --git a/kchart/kdchart/KDChartParams_io.cpp b/kchart/kdchart/KDChartParams_io.cpp new file mode 100644 index 00000000..092229ea --- /dev/null +++ b/kchart/kdchart/KDChartParams_io.cpp @@ -0,0 +1,3333 @@ +/* -*- 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. + ** + **********************************************************************/ +#include "KDChartParams.h" +#include <KDXMLTools.h> + +#include <qintdict.h> + +/** + \fn QTextStream& operator<<( QTextStream& s, const KDChartParams& p ); + + Writes the KDChartParams object p as an XML document to the text stream s. + + \param s the text stream to write to + \param p the KDChartParams object to write + \return the text stream after the write operation + */ +QTextStream& operator<<( QTextStream& s, const KDChartParams& p ) +{ + QDomDocument document = p.saveXML(); + s << document.toString(); + + return s; +} + +/** + Helper method saving the axis parameters to an XML file, + this is called by saveXML() only. +*/ +void KDChartParams::saveAxesToXML(QDomDocument& doc, QDomElement& docRoot) const +{ + // the AxisSettings elements + for( int axis = 0; axis < 13; axis++ ) { + QDomElement axisSettingsElement = + doc.createElement( "AxisSettings" ); + docRoot.appendChild( axisSettingsElement ); + axisSettingsElement.setAttribute( "Dataset", + _axisSettings[axis].dataset ); + axisSettingsElement.setAttribute( "Dataset2", + _axisSettings[axis].dataset2 ); + axisSettingsElement.setAttribute( "Chart", + _axisSettings[axis].chart ); + { + // the Type element + KDXML::createStringNode( doc, axisSettingsElement, "Type", + KDChartAxisParams::axisTypeToString( _axisSettings[axis].params._axisType ) ); + + // the Visible element + KDXML::createBoolNode( doc, axisSettingsElement, "Visible", + _axisSettings[axis].params._axisVisible ); + + // the LabelsTouchEdges element + KDXML::createBoolNode( doc, axisSettingsElement, "LabelsTouchEdges", + _axisSettings[axis].params._axisLabelsTouchEdges ); + + // the AreaMode element + KDXML::createStringNode( doc, axisSettingsElement, "AreaMode", + KDChartAxisParams::axisAreaModeToString( _axisSettings[axis].params._axisAreaMode ) ); + + // the UseAvailableSpaceFrom element + KDXML::createIntNode( doc, axisSettingsElement, "UseAvailableSpaceFrom", + _axisSettings[axis].params._axisUseAvailableSpaceFrom ); + + // the UseAvailableSpaceTo element + KDXML::createIntNode( doc, axisSettingsElement, "UseAvailableSpaceTo", + _axisSettings[axis].params._axisUseAvailableSpaceTo ); + + // the IsometricReferenceAxis element + KDXML::createIntNode( doc, axisSettingsElement, "IsometricReferenceAxis", + _axisSettings[axis].params._axisIsoRefAxis ); + + // the AreaMin element + KDXML::createIntNode( doc, axisSettingsElement, "AreaMin", + _axisSettings[axis].params._axisAreaMin ); + + // the AreaMax element + KDXML::createIntNode( doc, axisSettingsElement, "AreaMax", + _axisSettings[axis].params._axisAreaMax ); + + // the CalcMode element + KDXML::createStringNode( doc, axisSettingsElement, "CalcMode", + KDChartAxisParams::axisCalcModeToString( _axisSettings[axis].params._axisCalcMode ) ); + + // the TrueAreaSize element + KDXML::createIntNode( doc, axisSettingsElement, "TrueAreaSize", + _axisSettings[axis].params._axisTrueAreaSize ); + + // the TrueAreaRect element + KDXML::createRectNode( doc, axisSettingsElement, "TrueAreaRect", + _axisSettings[axis].params._axisTrueAreaRect ); + + // the ShowSubDelimiters element + KDXML::createBoolNode( doc, axisSettingsElement, "ShowSubDelimiters", + _axisSettings[axis].params._axisShowSubDelimiters ); + + // the LineVisible element + KDXML::createBoolNode( doc, axisSettingsElement, "LineVisible", + _axisSettings[axis].params._axisLineVisible ); + + // the LineWidth element + KDXML::createIntNode( doc, axisSettingsElement, "LineWidth", + _axisSettings[axis].params._axisLineWidth ); + + // the TrueLineWidth element + KDXML::createIntNode( doc, axisSettingsElement, "TrueLineWidth", + _axisSettings[axis].params._axisTrueLineWidth ); + + // the LineColor element + KDXML::createColorNode( doc, axisSettingsElement, "LineColor", + _axisSettings[axis].params._axisLineColor ); + + // the ShowGrid element + KDXML::createBoolNode( doc, axisSettingsElement, "ShowGrid", + _axisSettings[axis].params._axisShowGrid ); + + // the GridColor element + KDXML::createColorNode( doc, axisSettingsElement, "GridColor", + _axisSettings[axis].params._axisGridColor ); + + // the GridLineWidth element + KDXML::createIntNode( doc, axisSettingsElement, "GridLineWidth", + _axisSettings[axis].params._axisGridLineWidth ); + + // the GridStyle element + KDXML::createStringNode( doc, axisSettingsElement, "GridStyle", + KDXML::penStyleToString( _axisSettings[axis].params._axisGridStyle ) ); + + // the GridSubColor element + KDXML::createColorNode( doc, axisSettingsElement, "GridSubColor", + _axisSettings[axis].params._axisGridSubColor ); + + // the GridSubLineWidth element + KDXML::createIntNode( doc, axisSettingsElement, "GridSubLineWidth", + _axisSettings[axis].params._axisGridSubLineWidth ); + + // the GridSubStyle element + KDXML::createStringNode( doc, axisSettingsElement, "GridSubStyle", + KDXML::penStyleToString( _axisSettings[axis].params._axisGridSubStyle ) ); + + // the ZeroLineColor element + KDXML::createColorNode( doc, axisSettingsElement, "ZeroLineColor", + _axisSettings[axis].params._axisZeroLineColor ); + + // the LabelsVisible element + KDXML::createBoolNode( doc, axisSettingsElement, "LabelsVisible", + _axisSettings[axis].params._axisLabelsVisible ); + + // the LabelsFont element + createChartFontNode( doc, axisSettingsElement, "LabelsFont", + _axisSettings[axis].params._axisLabelsFont, + _axisSettings[axis].params._axisLabelsFontUseRelSize, + _axisSettings[axis].params._axisLabelsFontRelSize, + _axisSettings[axis].params._axisLabelsFontMinSize ); + + // the LabelsDontShrinkFont element + KDXML::createBoolNode( doc, axisSettingsElement, "LabelsDontShrinkFont", + _axisSettings[axis].params._axisLabelsDontShrinkFont ); + + // the LabelsDontAutoRotate element + KDXML::createBoolNode( doc, axisSettingsElement, "LabelsDontAutoRotate", + _axisSettings[axis].params._axisLabelsDontAutoRotate ); + + // the LabelsRotation element + KDXML::createIntNode( doc, axisSettingsElement, "LabelsRotation", + _axisSettings[axis].params._axisLabelsRotation ); + + // the LabelsLeaveOut element + KDXML::createIntNode( doc, axisSettingsElement, "LabelsLeaveOut", + _axisSettings[axis].params._axisValueLeaveOut ); + + // the LabelsColor element + KDXML::createColorNode( doc, axisSettingsElement, "LabelsColor", + _axisSettings[axis].params._axisLabelsColor ); + + // the SteadyValueCalc element + KDXML::createBoolNode( doc, axisSettingsElement, "SteadyValueCalc", + _axisSettings[axis].params._axisSteadyValueCalc ); + + // the ValueStart element + if( ! ( KDCHART_AXIS_LABELS_AUTO_LIMIT == _axisSettings[axis].params._axisValueStart )) + createChartValueNode( doc, axisSettingsElement, "ValueStart", + _axisSettings[axis].params._axisValueStart, + 0.0, + 0 ); + + // the ValueStartIsExact element + KDXML::createBoolNode( doc, axisSettingsElement, "ValueStartIsExact", + _axisSettings[axis].params._axisValueStartIsExact ); + + // the ValueEnd element + if( ! ( KDCHART_AXIS_LABELS_AUTO_LIMIT == _axisSettings[axis].params._axisValueEnd )) + createChartValueNode( doc, axisSettingsElement, "ValueEnd", + _axisSettings[axis].params._axisValueEnd, + 0.0, + 0 ); + + // the ValueDelta element + if( ! ( KDCHART_AXIS_LABELS_AUTO_DELTA == _axisSettings[axis].params._axisValueDelta )) + KDXML::createDoubleNode( doc, axisSettingsElement, "ValueDelta", + _axisSettings[axis].params._axisValueDelta ); + KDXML::createIntNode( doc, axisSettingsElement, "ValueDeltaScale", + _axisSettings[axis].params._axisValueDeltaScale ); + + // the ValuesDecreasing element + KDXML::createBoolNode( doc, axisSettingsElement, "ValuesDecreasing", + _axisSettings[axis].params._axisValuesDecreasing ); + + // the TrueLow element + KDXML::createDoubleNode( doc, axisSettingsElement, "TrueLow", + _axisSettings[axis].params._trueLow ); + + // the TrueHigh element + KDXML::createDoubleNode( doc, axisSettingsElement, "TrueHigh", + _axisSettings[axis].params._trueHigh ); + + // the TrueDelta element + KDXML::createDoubleNode( doc, axisSettingsElement, "TrueDelta", + _axisSettings[axis].params._trueDelta ); + + // the ZeroLineStart element + QDomElement zeroLineStartElement = doc.createElement( "ZeroLineStart" ); + axisSettingsElement.appendChild( zeroLineStartElement ); + zeroLineStartElement.setAttribute( "X", _axisSettings[axis].params._axisZeroLineStartX ); + zeroLineStartElement.setAttribute( "Y", _axisSettings[axis].params._axisZeroLineStartY ); + + // the DigitsBehindComma element + KDXML::createIntNode( doc, axisSettingsElement, "DigitsBehindComma", + _axisSettings[axis].params._axisDigitsBehindComma ); + + // the LabelsDateTimeFormat element + KDXML::createStringNode( doc, axisSettingsElement, "LabelsDateTimeFormat", + _axisSettings[axis].params._axisLabelsDateTimeFormat ); + + // the MaxEmptyInnerSpan element + KDXML::createIntNode( doc, axisSettingsElement, "MaxEmptyInnerSpan", + _axisSettings[axis].params._axisMaxEmptyInnerSpan ); + + // the LabelsFromDataRow element + KDXML::createStringNode( doc, axisSettingsElement, "LabelsFromDataRow", + KDChartAxisParams::labelsFromDataRowToString( _axisSettings[axis].params._takeLabelsFromDataRow ) ); + + // the TextsDataRow element + KDXML::createIntNode( doc, axisSettingsElement, "TextsDataRow", + _axisSettings[axis].params._labelTextsDataRow ); + + // the LabelString elements + KDXML::createStringListNodes( doc, axisSettingsElement, "LabelString", + &_axisSettings[axis].params._axisLabelStringList ); + + // the ShortLabelString elements + KDXML::createStringListNodes( doc, axisSettingsElement, "ShortLabelString", + &_axisSettings[axis].params._axisShortLabelsStringList ); + + // the LabelText elements + KDXML::createStringListNodes( doc, axisSettingsElement, "LabelText", + &_axisSettings[axis].params._axisLabelTexts ); + + // the LabelTextsDirty element + KDXML::createBoolNode( doc, axisSettingsElement, "LabelTextsDirty", + _axisSettings[axis].params._axisLabelTextsDirty ); + + // labels formatting: + + // the extra FirstLabelText element + KDXML::createStringNode( doc, axisSettingsElement, "FirstLabelReplacementText", + _axisSettings[axis].params._axisFirstLabelText ); + + // the extra LastLabelText element + KDXML::createStringNode( doc, axisSettingsElement, "LastLabelReplacementText", + _axisSettings[axis].params._axisLastLabelText ); + + // the LabelsDivPow10 element + KDXML::createIntNode( doc, axisSettingsElement, "LabelsDivPow10", + _axisSettings[axis].params._axisLabelsDivPow10 ); + + // the LabelsDecimalPoint element + KDXML::createStringNode( doc, axisSettingsElement, "LabelsDecimalPoint", + _axisSettings[axis].params._axisLabelsDecimalPoint ); + + // the LabelsThousandsPoint element + KDXML::createStringNode( doc, axisSettingsElement, "LabelsThousandsPoint", + _axisSettings[axis].params._axisLabelsThousandsPoint ); + + // the LabelsNotation element + KDXML::createStringNode( doc, axisSettingsElement, "LabelsNotation", + KDChartEnums::numberNotationToString( _axisSettings[axis].params._axisLabelsNotation ) ); + + // the LabelsPrefix element + KDXML::createStringNode( doc, axisSettingsElement, "LabelsPrefix", + _axisSettings[axis].params._axisLabelsPrefix ); + + // the LabelsPostfix element + KDXML::createStringNode( doc, axisSettingsElement, "LabelsPostfix", + _axisSettings[axis].params._axisLabelsPostfix ); + + // the LabelsTotalLen element + KDXML::createIntNode( doc, axisSettingsElement, "LabelsTotalLen", + _axisSettings[axis].params._axisLabelsTotalLen ); + + // the LabelsPadFill element + KDXML::createStringNode( doc, axisSettingsElement, "LabelsPadFill", + _axisSettings[axis].params._axisLabelsPadFill ); + + // the LabelsBlockAlign element + KDXML::createBoolNode( doc, axisSettingsElement, "LabelsBlockAlign", + _axisSettings[axis].params._axisLabelsBlockAlign ); + } + } +} + + +/** + Saves the parameters to an XML document. + + \return the XML document that represents the parameters + \sa loadXML + */ +QDomDocument KDChartParams::saveXML( bool withPI ) const +{ + // Create an inital DOM document + QString docstart = "<ChartParams/>"; + + QDomDocument doc( "ChartParams" ); + doc.setContent( docstart ); + if( withPI ) + doc.appendChild( doc.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" ) ); + + QDomElement docRoot = doc.documentElement(); + docRoot.setAttribute( "xmlns", "http://www.klaralvdalens-datakonsult.se/kdchart" ); + docRoot.setAttribute( "xmlns:xsi", "http://www.w3.org/2000/10/XMLSchema-instance" ); + docRoot.setAttribute( "xsi:schemaLocation", "http://www.klaralvdalens-datakonsult.se/kdchart" ); + + // the ChartType element + QDomElement chartTypeElement = doc.createElement( "ChartType" ); + docRoot.appendChild( chartTypeElement ); + chartTypeElement.setAttribute( "primary", + chartTypeToString( _chartType ) ); + chartTypeElement.setAttribute( "secondary", + chartTypeToString( _additionalChartType ) ); + + + // the NumValues element + KDXML::createIntNode( doc, docRoot, "NumValues", _numValues ); + + // the ModeAndChartMap element + QDomElement modeAndChartMapElement = + doc.createElement( "ModeAndChartMap" ); + docRoot.appendChild( modeAndChartMapElement ); + for( QMap<uint,KDChartParams::ModeAndChart>::ConstIterator it = _dataSourceModeAndChart.begin(); + it != _dataSourceModeAndChart.end(); ++it ) { + // Dataset element + QDomElement datasetElement = doc.createElement( "Dataset" ); + QDomText datasetContent = + doc.createTextNode( QString::number( it.key() ) ); + datasetElement.appendChild( datasetContent ); + modeAndChartMapElement.appendChild( datasetElement ); + // ModeAndChart element + QDomElement modeAndChartElement = doc.createElement( "ModeAndChart" ); + modeAndChartElement.setAttribute( "Mode", chartSourceModeToString( it.data().mode() ) ); + modeAndChartElement.setAttribute( "Chart", it.data().chart() ); + modeAndChartMapElement.appendChild( modeAndChartElement ); + } + + // the PropertySetMap element + QDomElement propertySetMapElement = + doc.createElement( "PropertySetMap" ); + docRoot.appendChild( propertySetMapElement ); + QIntDictIterator<KDChartPropertySet> it2( _propertySetList ); + for( ; it2.current(); ++it2 ) + propertySetMapElement.appendChild( it2.current()->saveXML( doc ) ); + + KDXML::createBoolNode( doc, docRoot, "ChartSourceModeWasUsed", + _setChartSourceModeWasUsed ); + + // the MaxDatasetSourceMode element + KDXML::createIntNode( doc, docRoot, "MaxDatasetSourceMode", + _maxDatasetSourceMode ); + + // the ColorSettings element + QDomElement colorSettingsElement = + doc.createElement( "ColorSettings" ); + docRoot.appendChild( colorSettingsElement ); + + { + // the DataColors element + createColorMapNode( doc, colorSettingsElement, + "DataColors", _dataColors ); + + // the MaxDatasetColor element + KDXML::createIntNode( doc, colorSettingsElement, "MaxDatasetColor", + _maxDatasetColor ); + + // the ShadowBrightnessFactor element + KDXML::createDoubleNode( doc, colorSettingsElement, + "ShadowBrightnessFactor", + _shadowBrightnessFactor ); + + // the ShadowPattern element + KDXML::createStringNode( doc, colorSettingsElement, + "ShadowPattern", + KDXML::brushStyleToString(_shadowPattern ) ); + + // the ThreeDShadowColors element + KDXML::createBoolNode( doc, colorSettingsElement, + "ThreeDShadowColors", + _threeDShadowColors ); + + // the DataColorsShadow1 element + createColorMapNode( doc, colorSettingsElement, + "DataColorsShadow1", + _dataColorsShadow1 ); + + // the DataColorsShadow2 element + createColorMapNode( doc, colorSettingsElement, + "DataColorsShadow2", + _dataColorsShadow2 ); + + // the OutlineDataColor element + KDXML::createColorNode( doc, colorSettingsElement, + "OutlineDataColor", + _outlineDataColor ); + + // the OutlineDataLineWidth element + KDXML::createIntNode( doc, colorSettingsElement, + "OutlineDataLineWidth", + _outlineDataLineWidth ); + + // the OutlineDataLineStyle element + QDomElement outlineDataLineStyleElement = + doc.createElement( "OutlineDataLineStyle" ); + colorSettingsElement.appendChild( outlineDataLineStyleElement ); + outlineDataLineStyleElement.setAttribute( "Style", + KDXML::penStyleToString( _outlineDataLineStyle ) ); + } + + + // the BarSettings elemenet + QDomElement barSettingsElement = + doc.createElement( "BarSettings" ); + docRoot.appendChild( barSettingsElement ); + + { + // the SubType element + KDXML::createStringNode( doc, barSettingsElement, + "SubType", KDChartParams::barChartSubTypeToString( _barChartSubType ) ); + + KDXML::createBoolNode( doc, barSettingsElement, + "ThreeDBars", _threeDBars ); + + KDXML::createDoubleNode( doc, barSettingsElement, + "ThreeDBarDepth", _threeDBarDepth ); + + KDXML::createIntNode( doc, barSettingsElement, + "DatasetGap", _datasetGap ); + + KDXML::createBoolNode( doc, barSettingsElement, + "DatasetGapIsRelative", _datasetGapIsRelative ); + + KDXML::createIntNode( doc, barSettingsElement, + "ValueBlockGap", _valueBlockGap ); + + KDXML::createBoolNode( doc, barSettingsElement, + "ValueBlockGapIsRelative", + _valueBlockGapIsRelative ); + + KDXML::createIntNode( doc, barSettingsElement, + "BarWidth", _barWidth ); + + KDXML::createBoolNode( doc, barSettingsElement, + "SolidExcessArrows", _solidExcessArrows ); + } + + + // the LineSettings element + QDomElement lineSettingsElement = + doc.createElement( "LineSettings" ); + docRoot.appendChild( lineSettingsElement ); + + { + // the SubType element + KDXML::createStringNode( doc, lineSettingsElement, + "SubType", KDChartParams::lineChartSubTypeToString( _lineChartSubType ) ); + + // the Marker element + KDXML::createBoolNode( doc, lineSettingsElement, + "Marker", _lineMarker ); + + // the MarkerStyles elements + {for( QMap<uint,KDChartParams::LineMarkerStyle>::ConstIterator it = _lineMarkerStyles.begin(); + it != _lineMarkerStyles.end(); ++it ) { + QDomElement markerStyleElement = doc.createElement( "MarkerStyle" ); + lineSettingsElement.appendChild( markerStyleElement ); + markerStyleElement.setAttribute( "Dataset", it.key() ); + markerStyleElement.setAttribute( "Style", KDChartParams::lineMarkerStyleToString( it.data() ) ); + }} + + // the MarkerSize element + KDXML::createSizeNode( doc, lineSettingsElement, + "MarkerSize", _lineMarkerSize ); + + // the LineWidth element + KDXML::createIntNode( doc, lineSettingsElement, + "LineWidth", _lineWidth ); + + // the LineColor element + KDXML::createColorNode( doc, lineSettingsElement, + "LineColor", + _lineColor ); + + // the LineStyle element + QDomElement lineStyleElement = + doc.createElement( "LineStyle" ); + lineSettingsElement.appendChild( lineStyleElement ); + lineStyleElement.setAttribute( "Style", + KDXML::penStyleToString( _lineStyle ) ); + + // the DatasetLineStyles elements + {for( QMap<uint, PenStyle>::ConstIterator it = _datasetLineStyles.begin(); + it != _datasetLineStyles.end(); ++it ) { + QDomElement lineStyleElement = doc.createElement( "DatasetLineStyle" ); + lineSettingsElement.appendChild( lineStyleElement ); + lineStyleElement.setAttribute( "Dataset", it.key() ); + lineStyleElement.setAttribute( "Style", KDXML::penStyleToString( it.data() ) ); + }} + + // the ThreeD element + KDXML::createBoolNode( doc, lineSettingsElement, + "ThreeD", _threeDLines ); + + // the ThreeDDepth element + KDXML::createIntNode( doc, lineSettingsElement, + "ThreeDDepth", _threeDLineDepth ); + + // the ThreeDXRotation element + KDXML::createIntNode( doc, lineSettingsElement, + "ThreeDXRotation", _threeDLineXRotation ); + + // the ThreeDYRotation element + KDXML::createIntNode( doc, lineSettingsElement, + "ThreeDYRotation", _threeDLineYRotation ); + } + + + // the AreaSettings element + QDomElement areaSettingsElement = + doc.createElement( "AreaSettings" ); + docRoot.appendChild( areaSettingsElement ); + + { + // the SubType element + KDXML::createStringNode( doc, areaSettingsElement, "SubType", + KDChartParams::areaChartSubTypeToString( _areaChartSubType ) ); + + // the Location elemenet + KDXML::createStringNode( doc, areaSettingsElement, "Location", + KDChartParams::areaLocationToString( _areaLocation ) ); + } + + + // the PieRingSettings element + QDomElement pieRingSettingsElement = + doc.createElement( "PieRingSettings" ); + docRoot.appendChild( pieRingSettingsElement ); + + { + // the Explode element + KDXML::createBoolNode( doc, pieRingSettingsElement, "Explode", + _explode ); + + // the DefaultExplodeFactor element + KDXML::createDoubleNode( doc, pieRingSettingsElement, "DefaultExplodeFactor", + _explodeFactor ); + + // the ExplodeFactors element + createDoubleMapNode( doc, pieRingSettingsElement, "ExplodeFactors", + _explodeFactors ); + + // the ExplodeSegment element + for( QValueList<int>::ConstIterator it = _explodeList.begin(); + it != _explodeList.end(); ++it ) + KDXML::createIntNode( doc, pieRingSettingsElement, + "ExplodeSegment", *it ); + + // the ThreeDPies element + KDXML::createBoolNode( doc, pieRingSettingsElement, "ThreeDPies", + _threeDPies ); + + // the ThreeDPieHeight element + KDXML::createIntNode( doc, pieRingSettingsElement, "ThreeDPieHeight", + _threeDPieHeight ); + + // the PieStart element + KDXML::createIntNode( doc, pieRingSettingsElement, "PieStart", + _pieStart ); + + // the RingStart element + KDXML::createIntNode( doc, pieRingSettingsElement, "RingStart", + _ringStart ); + + // the RelativeRingThickness element + KDXML::createBoolNode( doc, pieRingSettingsElement, + "RelativeRingThickness", _relativeRingThickness ); + } + + // the HiLoSettings element + QDomElement hiLoSettingsElement = + doc.createElement( "HiLoSettings" ); + docRoot.appendChild( hiLoSettingsElement ); + { + // the SubType element + KDXML::createStringNode( doc, hiLoSettingsElement, "SubType", + KDChartParams::hiLoChartSubTypeToString( _hiLoChartSubType ) ); + + // the PrintLowValues element + KDXML::createBoolNode( doc, hiLoSettingsElement, "PrintLowValues", + _hiLoChartPrintLowValues ); + + // the LowValuesFont element + createChartFontNode( doc, hiLoSettingsElement, "LowValuesFont", + _hiLoChartLowValuesFont, + _hiLoChartLowValuesUseFontRelSize, + _hiLoChartLowValuesFontRelSize ); + + // the LowValuesColor element + KDXML::createColorNode( doc, hiLoSettingsElement, "LowValuesColor", + _hiLoChartLowValuesColor ); + + // the PrintHighValues element + KDXML::createBoolNode( doc, hiLoSettingsElement, "PrintHighValues", + _hiLoChartPrintHighValues ); + + // the HighValuesFont element + createChartFontNode( doc, hiLoSettingsElement, "HighValuesFont", + _hiLoChartHighValuesFont, + _hiLoChartHighValuesUseFontRelSize, + _hiLoChartHighValuesFontRelSize ); + + // the HighValuesColor element + KDXML::createColorNode( doc, hiLoSettingsElement, "HighValuesColor", + _hiLoChartHighValuesColor ); + + // the PrintOpenValues element + KDXML::createBoolNode( doc, hiLoSettingsElement, "PrintOpenValues", + _hiLoChartPrintOpenValues ); + + // the OpenValuesFont element + createChartFontNode( doc, hiLoSettingsElement, "OpenValuesFont", + _hiLoChartOpenValuesFont, + _hiLoChartOpenValuesUseFontRelSize, + _hiLoChartOpenValuesFontRelSize ); + + // the OpenValuesColor element + KDXML::createColorNode( doc, hiLoSettingsElement, "OpenValuesColor", + _hiLoChartOpenValuesColor ); + + // the PrintCloseValues element + KDXML::createBoolNode( doc, hiLoSettingsElement, "PrintCloseValues", + _hiLoChartPrintCloseValues ); + + // the CloseValuesFont element + createChartFontNode( doc, hiLoSettingsElement, "CloseValuesFont", + _hiLoChartCloseValuesFont, + _hiLoChartCloseValuesUseFontRelSize, + _hiLoChartCloseValuesFontRelSize ); + + // the CloseValuesColor element + KDXML::createColorNode( doc, hiLoSettingsElement, "CloseValuesColor", + _hiLoChartCloseValuesColor ); + } + + + + // the BoxAndWhiskerSettings element + QDomElement bWSettingsElement = + doc.createElement( "BoxAndWhiskerSettings" ); + docRoot.appendChild( bWSettingsElement ); + { + // the SubType element + KDXML::createStringNode( doc, bWSettingsElement, "SubType", + KDChartParams::bWChartSubTypeToString( _BWChartSubType ) ); + + // the fences elements + KDXML::createDoubleNode( doc, bWSettingsElement, "FenceUpperInner", + _BWChartFenceUpperInner ); + KDXML::createDoubleNode( doc, bWSettingsElement, "FenceLowerInner", + _BWChartFenceLowerInner ); + KDXML::createDoubleNode( doc, bWSettingsElement, "FenceUpperOuter", + _BWChartFenceUpperInner ); + KDXML::createDoubleNode( doc, bWSettingsElement, "FenceLowerOuter", + _BWChartFenceLowerOuter ); + + // the brush to be used + KDXML::createBrushNode( doc, bWSettingsElement, "Brush", + _BWChartBrush ); + + // the size of the outlier markers + KDXML::createIntNode( doc, bWSettingsElement, "OutlierSize", + _BWChartOutValMarkerSize ); + + // the PrintStatistics elements + for( int i = BWStatValSTART; i <= BWStatValEND; ++i ){ + QDomElement printStatElement = + doc.createElement( "PrintStatistics"+bWChartStatValToString( (BWStatVal)i ) ); + KDXML::createBoolNode( doc, printStatElement, "Active", + _BWChartStatistics[ i ].active ); + createChartFontNode( doc, printStatElement, "Font", + _BWChartStatistics[ i ].font, + _BWChartStatistics[ i ].useRelSize, + _BWChartStatistics[ i ].relSize ); + KDXML::createColorNode( doc, printStatElement, "Color", + _BWChartStatistics[ i ].color ); + KDXML::createBrushNode( doc, printStatElement, "Brush", + _BWChartStatistics[ i ].brush ); + } + } + + + + // the PolarSettings element + QDomElement polarSettingsElement = + doc.createElement( "PolarSettings" ); + docRoot.appendChild( polarSettingsElement ); + + { + // the SubType element + KDXML::createStringNode( doc, polarSettingsElement, + "SubType", KDChartParams::polarChartSubTypeToString( _polarChartSubType ) ); + + // the Marker element + KDXML::createBoolNode( doc, polarSettingsElement, + "Marker", _polarMarker ); + + // the MarkerStyles elements + for( QMap<uint,KDChartParams::PolarMarkerStyle>::ConstIterator it = _polarMarkerStyles.begin(); + it != _polarMarkerStyles.end(); ++it ) { + QDomElement markerStyleElement = doc.createElement( "MarkerStyle" ); + polarSettingsElement.appendChild( markerStyleElement ); + markerStyleElement.setAttribute( "Dataset", it.key() ); + markerStyleElement.setAttribute( "Style", KDChartParams::polarMarkerStyleToString( it.data() ) ); + } + + // the MarkerSize element + KDXML::createSizeNode( doc, polarSettingsElement, + "MarkerSize", _polarMarkerSize ); + + // the PolarLineWidth element + KDXML::createIntNode( doc, polarSettingsElement, + "PolarLineWidth", _polarLineWidth ); + } + + + + // the LegendSettings element + QDomElement legendSettingsElement = + doc.createElement( "LegendSettings" ); + docRoot.appendChild( legendSettingsElement ); + { + // the Position element + KDXML::createStringNode( doc, legendSettingsElement, "Position", + KDChartParams::legendPositionToString( _legendPosition ) ); + + // the Orientation element + KDXML::createOrientationNode( doc, legendSettingsElement, + "Orientation", _legendOrientation ); + + // the ShowLines element + KDXML::createBoolNode( doc, legendSettingsElement, + "ShowLines", _legendShowLines ); + + // the Source element + KDXML::createStringNode( doc, legendSettingsElement, "Source", + KDChartParams::legendSourceToString( _legendSource ) ); + + // the Text elements + for( QMap<int,QString>::ConstIterator it = _legendText.begin(); + it != _legendText.end(); ++it ) { + QDomElement legendTextElement = doc.createElement( "LegendText" ); + legendSettingsElement.appendChild( legendTextElement ); + legendTextElement.setAttribute( "Dataset", it.key() ); + legendTextElement.setAttribute( "Text", it.data() ); + } + + // the TextColor element + KDXML::createColorNode( doc, legendSettingsElement, "TextColor", + _legendTextColor ); + + // the TextFont element + createChartFontNode( doc, legendSettingsElement, "TextFont", + _legendFont, + _legendFontUseRelSize, + _legendFontRelSize ); + + // the TitleText element + KDXML::createStringNode( doc, legendSettingsElement, "TitleText", + _legendTitleText ); + + // the TitleColor element + KDXML::createColorNode( doc, legendSettingsElement, "TitleColor", + _legendTitleTextColor ); + + // the TitleFont element + createChartFontNode( doc, legendSettingsElement, "TitleFont", + _legendTitleFont, + _legendTitleFontUseRelSize, + _legendTitleFontRelSize ); + + // the Spacing element + KDXML::createIntNode( doc, legendSettingsElement, "Spacing", + _legendSpacing ); + } + + saveAxesToXML(doc, docRoot); + + // the HeaderFooterSettings elements + for( int hf = 0; hf < 18; hf++ ) { + QDomElement hfSettingsElement = + doc.createElement( "HeaderFooterSettings" ); + docRoot.appendChild( hfSettingsElement ); + { + KDXML::createStringNode( doc, hfSettingsElement, "Text", + _hdFtParams[hf]._text ); + createChartFontNode( doc, hfSettingsElement, "Font", + _hdFtParams[hf]._font, + _hdFtParams[hf]._fontUseRelSize, + _hdFtParams[hf]._fontRelSize ); + KDXML::createColorNode( doc, hfSettingsElement, "Color", + _hdFtParams[hf]._color ); + } + } + + + // the GlobalLeading element + QDomElement globalLeadingElement = + doc.createElement( "GlobalLeading" ); + docRoot.appendChild( legendSettingsElement ); + { + KDXML::createIntNode( doc, globalLeadingElement, "Left", + _globalLeadingLeft ); + KDXML::createIntNode( doc, globalLeadingElement, "Top", + _globalLeadingTop ); + KDXML::createIntNode( doc, globalLeadingElement, "Right", + _globalLeadingRight ); + KDXML::createIntNode( doc, globalLeadingElement, "Bottom", + _globalLeadingBottom ); + } + + // the DataValuesSettings1 element + QDomElement dataValuesSettings1Element = + doc.createElement( "DataValuesSettings1" ); + docRoot.appendChild( dataValuesSettings1Element ); + { + KDXML::createBoolNode( doc, dataValuesSettings1Element, "PrintDataValues", + _printDataValuesSettings._printDataValues ); + KDXML::createIntNode( doc, dataValuesSettings1Element, "DivPow10", + _printDataValuesSettings._divPow10 ); + KDXML::createIntNode( doc, dataValuesSettings1Element, "DigitsBehindComma", + _printDataValuesSettings._digitsBehindComma ); + createChartFontNode( doc, dataValuesSettings1Element, "Font", + _printDataValuesSettings._dataValuesFont, + _printDataValuesSettings._dataValuesUseFontRelSize, + _printDataValuesSettings._dataValuesFontRelSize ); + KDXML::createColorNode( doc, dataValuesSettings1Element, "Color", + _printDataValuesSettings._dataValuesColor ); + KDXML::createBrushNode( doc, dataValuesSettings1Element, "Background", + _printDataValuesSettings._dataValuesBrush ); + KDXML::createBoolNode( doc, dataValuesSettings1Element, "AutoColor", + _printDataValuesSettings._dataValuesAutoColor ); + KDXML::createStringNode( doc, dataValuesSettings1Element, + "AnchorNegativePosition", + KDChartEnums::positionFlagToString( _printDataValuesSettings._dataValuesAnchorNegativePosition ) ); + KDXML::createIntNode( doc, dataValuesSettings1Element, + "AnchorNegativeAlign", + _printDataValuesSettings._dataValuesAnchorNegativeAlign ); + KDXML::createIntNode( doc, dataValuesSettings1Element, + "AnchorNegativeDeltaX", + _printDataValuesSettings._dataValuesAnchorNegativeDeltaX ); + KDXML::createIntNode( doc, dataValuesSettings1Element, + "AnchorNegativeDeltaY", + _printDataValuesSettings._dataValuesAnchorNegativeDeltaY ); + KDXML::createIntNode( doc, dataValuesSettings1Element, + "NegativeRotation", + _printDataValuesSettings._dataValuesNegativeRotation ); + KDXML::createStringNode( doc, dataValuesSettings1Element, + "AnchorPositivePosition", + KDChartEnums::positionFlagToString( _printDataValuesSettings._dataValuesAnchorPositivePosition ) ); + KDXML::createIntNode( doc, dataValuesSettings1Element, + "AnchorPositiveAlign", + _printDataValuesSettings._dataValuesAnchorPositiveAlign ); + KDXML::createIntNode( doc, dataValuesSettings1Element, + "AnchorPositiveDeltaX", + _printDataValuesSettings._dataValuesAnchorPositiveDeltaX ); + KDXML::createIntNode( doc, dataValuesSettings1Element, + "AnchorPositiveDeltaY", + _printDataValuesSettings._dataValuesAnchorPositiveDeltaY ); + KDXML::createIntNode( doc, dataValuesSettings1Element, + "PositiveRotation", + _printDataValuesSettings._dataValuesPositiveRotation ); + + KDXML::createStringNode( doc, dataValuesSettings1Element, + "LayoutPolicy", + KDChartEnums::layoutPolicyToString( _printDataValuesSettings._dataValuesLayoutPolicy ) ); + + KDXML::createBoolNode( doc, dataValuesSettings1Element, "ShowInfinite", + _printDataValuesSettings._dataValuesShowInfinite ); + } + + // the DataValuesSettings2 element + QDomElement dataValuesSettings2Element = + doc.createElement( "DataValuesSettings2" ); + docRoot.appendChild( dataValuesSettings2Element ); + { + KDXML::createBoolNode( doc, dataValuesSettings2Element, "PrintDataValues", + _printDataValuesSettings2._printDataValues ); + KDXML::createIntNode( doc, dataValuesSettings2Element, "DivPow10", + _printDataValuesSettings2._divPow10 ); + KDXML::createIntNode( doc, dataValuesSettings2Element, "DigitsBehindComma", + _printDataValuesSettings2._digitsBehindComma ); + createChartFontNode( doc, dataValuesSettings2Element, "Font", + _printDataValuesSettings2._dataValuesFont, + _printDataValuesSettings2._dataValuesUseFontRelSize, + _printDataValuesSettings2._dataValuesFontRelSize ); + KDXML::createColorNode( doc, dataValuesSettings2Element, "Color", + _printDataValuesSettings2._dataValuesColor ); + KDXML::createBrushNode( doc, dataValuesSettings2Element, "Background", + _printDataValuesSettings2._dataValuesBrush ); + KDXML::createBoolNode( doc, dataValuesSettings2Element, "AutoColor", + _printDataValuesSettings2._dataValuesAutoColor ); + KDXML::createStringNode( doc, dataValuesSettings2Element, + "AnchorNegativePosition", + KDChartEnums::positionFlagToString( _printDataValuesSettings2._dataValuesAnchorNegativePosition ) ); + KDXML::createIntNode( doc, dataValuesSettings2Element, + "AnchorNegativeAlign", + _printDataValuesSettings2._dataValuesAnchorNegativeAlign ); + KDXML::createIntNode( doc, dataValuesSettings2Element, + "AnchorNegativeDeltaX", + _printDataValuesSettings2._dataValuesAnchorNegativeDeltaX ); + KDXML::createIntNode( doc, dataValuesSettings2Element, + "AnchorNegativeDeltaY", + _printDataValuesSettings2._dataValuesAnchorNegativeDeltaY ); + KDXML::createIntNode( doc, dataValuesSettings2Element, + "NegativeRotation", + _printDataValuesSettings2._dataValuesNegativeRotation ); + KDXML::createStringNode( doc, dataValuesSettings2Element, + "AnchorPositivePosition", + KDChartEnums::positionFlagToString( _printDataValuesSettings2._dataValuesAnchorPositivePosition ) ); + KDXML::createIntNode( doc, dataValuesSettings2Element, + "AnchorPositiveAlign", + _printDataValuesSettings2._dataValuesAnchorPositiveAlign ); + KDXML::createIntNode( doc, dataValuesSettings2Element, + "AnchorPositiveDeltaX", + _printDataValuesSettings2._dataValuesAnchorPositiveDeltaX ); + KDXML::createIntNode( doc, dataValuesSettings2Element, + "AnchorPositiveDeltaY", + _printDataValuesSettings2._dataValuesAnchorPositiveDeltaY ); + KDXML::createIntNode( doc, dataValuesSettings2Element, + "PositiveRotation", + _printDataValuesSettings2._dataValuesPositiveRotation ); + + KDXML::createStringNode( doc, dataValuesSettings2Element, + "LayoutPolicy", + KDChartEnums::layoutPolicyToString( _printDataValuesSettings2._dataValuesLayoutPolicy ) ); + + KDXML::createBoolNode( doc, dataValuesSettings2Element, "ShowInfinite", + _printDataValuesSettings2._dataValuesShowInfinite ); + } + + // global settings for data value settings of _all_ charts + QDomElement dataValuesGlobalSettingsElement = + doc.createElement( "DataValuesGlobalSettings" ); + docRoot.appendChild( dataValuesGlobalSettingsElement ); + { + KDXML::createBoolNode( doc, dataValuesGlobalSettingsElement, + "allowOverlappingTexts", + _allowOverlappingDataValueTexts ); + } + + // the AreaMap element + QDomElement areaMapElement = + doc.createElement( "AreaMap" ); + docRoot.appendChild( areaMapElement ); + { + QDictIterator<KDChartFrameSettings> it( _areaDict ); + for( ; it.current(); ++it ){ + KDChartFrameSettings::createFrameSettingsNode( doc, areaMapElement, + "FrameSettings", + it.current(), + it.currentKey().left(5).stripWhiteSpace().toUInt() ); + } + } + + // the CustomBoxMap element + QDomElement customBoxMapElement = + doc.createElement( "CustomBoxMap" ); + docRoot.appendChild( customBoxMapElement ); + { + QIntDictIterator<KDChartCustomBox> it( _customBoxDict ); + for( ; it.current(); ++it ){ + KDXML::createIntNode( doc, customBoxMapElement, "Number", it.currentKey() ); + KDChartCustomBox::createCustomBoxNode( doc, customBoxMapElement, + "CustomBox", it ); + } + } + + + return doc; +} + + +/** + \fn QTextStream& operator>>( QTextStream& s, KDChartParams& p ); + + Reads the an XML document from the text stream s into the + KDChartParams object p + + \param s the text stream to read from + \param p the KDChartParams object to read into + \return the text stream after the read operation + */ +QTextStream& operator>>( QTextStream& s, KDChartParams& p ) +{ + QDomDocument doc( "ChartParams" ); + // would be nicer if QDomDocument could read from a QTextStream... + QString docString = s.read(); + doc.setContent( docString ); + + p.loadXML( doc ); + + return s; +} + + +/** + Helper method called by loadXML() only. + */ +void KDChartParams::loadAxesFormXML(int& curAxisSettings, QDomElement& element) +{ + KDChartAxisParams* axisSettings = + &( _axisSettings[ curAxisSettings ].params ); + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Type" ) { + QString string; + if( KDXML::readStringNode( element, string ) ) + axisSettings->_axisType = KDChartAxisParams::stringToAxisType( string ); + } else if( tagName == "Visible" ) { + bool visible; + if( KDXML::readBoolNode( element, visible ) ) + axisSettings->_axisVisible = visible; + } else if( tagName == "LabelsTouchEdges" ) { + bool labelsTouchEdges; + if( KDXML::readBoolNode( element, labelsTouchEdges ) ) + axisSettings->_axisLabelsTouchEdges = labelsTouchEdges; + } else if( tagName == "AreaMode" ) { + QString string; + if( KDXML::readStringNode( element, string ) ) + axisSettings->_axisAreaMode = KDChartAxisParams::stringToAxisAreaMode( string ); + } else if( tagName == "UseAvailableSpaceFrom" ) { + int spaceFrom; + if( KDXML::readIntNode( element, spaceFrom ) ) + axisSettings->_axisUseAvailableSpaceFrom = spaceFrom; + } else if( tagName == "UseAvailableSpaceTo" ) { + int spaceTo; + if( KDXML::readIntNode( element, spaceTo ) ) + axisSettings->_axisUseAvailableSpaceTo = spaceTo; + } else if( tagName == "IsometricReferenceAxis" ) { + int isoRefAxis; + if( KDXML::readIntNode( element, isoRefAxis ) ) + axisSettings->_axisIsoRefAxis = isoRefAxis; + } else if( tagName == "AreaMin" ) { + int areaMin; + if( KDXML::readIntNode( element, areaMin ) ) + axisSettings->_axisAreaMin = areaMin; + } else if( tagName == "AreaMax" ) { + int areaMax; + if( KDXML::readIntNode( element, areaMax ) ) + axisSettings->_axisAreaMax = areaMax; + } else if( tagName == "CalcMode" ) { + QString string; + if( KDXML::readStringNode( element, string ) ) + axisSettings->_axisCalcMode = KDChartAxisParams::stringToAxisCalcMode( string ); + } else if( tagName == "TrueAreaSize" ) { + int trueAreaSize; + if( KDXML::readIntNode( element, trueAreaSize ) ) + axisSettings->_axisTrueAreaSize = trueAreaSize; + } else if( tagName == "TrueAreaRect" ) { + QRect trueAreaRect; + if( KDXML::readRectNode( element, trueAreaRect ) ) + axisSettings->_axisTrueAreaRect = trueAreaRect; + } else if( tagName == "ShowSubDelimiters" ) { + bool showSubDelimiters; + if( KDXML::readBoolNode( element, showSubDelimiters ) ) + axisSettings->_axisShowSubDelimiters = showSubDelimiters; + } else if( tagName == "LineVisible" ) { + bool lineVisible; + if( KDXML::readBoolNode( element, lineVisible ) ) + axisSettings->_axisLineVisible = lineVisible; + } else if( tagName == "LineWidth" ) { + int lineWidth; + if( KDXML::readIntNode( element, lineWidth ) ) + axisSettings->_axisLineWidth = lineWidth; + } else if( tagName == "TrueLineWidth" ) { + int trueLineWidth; + if( KDXML::readIntNode( element, trueLineWidth ) ) + axisSettings->_axisTrueLineWidth = trueLineWidth; + } else if( tagName == "LineColor" ) { + QColor color; + if( KDXML::readColorNode( element, color ) ) + axisSettings->_axisLineColor = color; + } else if( tagName == "ShowGrid" ) { + bool showGrid; + if( KDXML::readBoolNode( element, showGrid ) ) + axisSettings->_axisShowGrid = showGrid; + } else if( tagName == "GridColor" ) { + QColor color; + if( KDXML::readColorNode( element, color ) ) + axisSettings->_axisGridColor = color; + } else if( tagName == "GridLineWidth" ) { + int gridLineWidth; + if( KDXML::readIntNode( element, gridLineWidth ) ) + axisSettings->_axisGridLineWidth = gridLineWidth; + } else if( tagName == "GridStyle" ) { + if( element.hasAttribute( "Style" ) ) + axisSettings->_axisGridStyle = KDXML::stringToPenStyle( element.attribute( "Style" ) ); + } else if( tagName == "GridSubColor" ) { + QColor color; + if( KDXML::readColorNode( element, color ) ) + axisSettings->_axisGridSubColor = color; + } else if( tagName == "GridSubLineWidth" ) { + int gridSubLineWidth; + if( KDXML::readIntNode( element, gridSubLineWidth ) ) + axisSettings->_axisGridSubLineWidth = gridSubLineWidth; + } else if( tagName == "GridSubStyle" ) { + if( element.hasAttribute( "Style" ) ) + axisSettings->_axisGridSubStyle = KDXML::stringToPenStyle( element.attribute( "Style" ) ); + } else if( tagName == "ZeroLineColor" ) { + QColor color; + if( KDXML::readColorNode( element, color ) ) + axisSettings->_axisZeroLineColor = color; + } else if( tagName == "LabelsVisible" ) { + bool labelsVisible; + if( KDXML::readBoolNode( element, labelsVisible ) ) + axisSettings->_axisLabelsVisible = labelsVisible; + } else if( tagName == "LabelsFont" ) { + readChartFontNode( element, + axisSettings->_axisLabelsFont, + axisSettings->_axisLabelsFontUseRelSize, + axisSettings->_axisLabelsFontRelSize, + &axisSettings->_axisLabelsFontMinSize ); + } else if( tagName == "LabelsDontShrinkFont" ) { + bool dontShrink; + if( KDXML::readBoolNode( element, dontShrink ) ) + axisSettings->_axisLabelsDontShrinkFont = dontShrink; + } else if( tagName == "LabelsDontAutoRotate" ) { + bool dontRotate; + if( KDXML::readBoolNode( element, dontRotate ) ) + axisSettings->_axisLabelsDontAutoRotate = dontRotate; + } else if( tagName == "LabelsRotation" ) { + int rotation; + if( KDXML::readIntNode( element, rotation ) ) + axisSettings->_axisLabelsRotation = rotation; + } else if( tagName == "LabelsLeaveOut" ) { + int leaveOut; + if( KDXML::readIntNode( element, leaveOut ) ) + axisSettings->_axisValueLeaveOut = leaveOut; + } else if( tagName == "LabelsColor" ) { + QColor color; + if( KDXML::readColorNode( element, color ) ) + axisSettings->_axisLabelsColor = color; + } else if( tagName == "SteadyValueCalc" ) { + bool steadyValueCalc; + if( KDXML::readBoolNode( element, steadyValueCalc ) ) + axisSettings->_axisSteadyValueCalc = steadyValueCalc; + } else if( tagName == "ValueStart" ) { + QVariant valX, valY; + int propID; + if( readChartValueNode( element, valY, valX, propID ) && + QVariant::Double == valY.type() ) + axisSettings->_axisValueStart = valY.toDouble(); + } else if( tagName == "ValueStartIsExact" ) { + bool isExactValue; + if( KDXML::readBoolNode( element, isExactValue ) ) + axisSettings->_axisValueStartIsExact = isExactValue; + } else if( tagName == "ValueEnd" ) { + QVariant valX, valY; + int propID; + if( readChartValueNode( element, valY, valX, propID ) && + QVariant::Double == valY.type() ) + axisSettings->_axisValueEnd = valY.toDouble(); + } else if( tagName == "ValueDelta" ) { + double valueDelta; + if( KDXML::readDoubleNode( element, valueDelta ) ) + axisSettings->_axisValueDelta = valueDelta; + } else if( tagName == "ValueDeltaScale" ) { + int valueDeltaScale; + if( KDXML::readIntNode( element, valueDeltaScale ) ) + axisSettings->_axisValueDeltaScale = (KDChartAxisParams::ValueScale)valueDeltaScale; + } else if( tagName == "ValuesDecreasing" ) { + bool decreasing; + if( KDXML::readBoolNode( element, decreasing ) ) + axisSettings->_axisValuesDecreasing = decreasing; + } else if( tagName == "TrueLow" ) { + double trueLow; + if( KDXML::readDoubleNode( element, trueLow ) ) + axisSettings->_trueLow = trueLow; + } else if( tagName == "TrueHigh" ) { + double trueHigh; + if( KDXML::readDoubleNode( element, trueHigh ) ) + axisSettings->_trueHigh = trueHigh; + } else if( tagName == "TrueDelta" ) { + double trueDelta; + if( KDXML::readDoubleNode( element, trueDelta ) ) + axisSettings->_trueDelta = trueDelta; + } else if( tagName == "ZeroLineStart" ) { + double x = 0.0; + double y = 0.0; + bool ok = true; + if( element.hasAttribute( "X" ) && + element.hasAttribute( "Y" ) ) { + x = element.attribute( "X" ).toDouble( &ok ); + if( ok ) + y = element.attribute( "Y" ).toDouble( &ok ); + } else + ok = false; + + if( ok ) { + axisSettings->_axisZeroLineStartX = x; + axisSettings->_axisZeroLineStartY = y; + } + } else if( tagName == "DigitsBehindComma" ) { + int digitsBehindComma; + if( KDXML::readIntNode( element, digitsBehindComma ) ) + axisSettings->_axisDigitsBehindComma = digitsBehindComma; + } else if( tagName == "LabelsDateTimeFormat" ) { + QString string; + if( KDXML::readStringNode( element, string ) ) + axisSettings->_axisLabelsDateTimeFormat = string; + } else if( tagName == "MaxEmptyInnerSpan" ) { + int maxEmptyInnerSpan; + if( KDXML::readIntNode( element, maxEmptyInnerSpan ) ) + axisSettings->_axisMaxEmptyInnerSpan = maxEmptyInnerSpan; + } else if( tagName == "LabelsFromDataRow" ) { + QString string; + if( KDXML::readStringNode( element, string ) ) + axisSettings->_labelTextsDataRow = KDChartAxisParams::stringToLabelsFromDataRow( string ); + } else if( tagName == "TextsDataRow" ) { + int textsDataRow; + if( KDXML::readIntNode( element, textsDataRow ) ) + axisSettings->_labelTextsDataRow = textsDataRow; + } else if( tagName == "LabelString" ) { + QString string; + if( KDXML::readStringNode( element, string ) ) + axisSettings->_axisLabelStringList << string; + } else if( tagName == "ShortLabelString" ) { + QString string; + if( KDXML::readStringNode( element, string ) ) + axisSettings->_axisShortLabelsStringList << string; + } else if( tagName == "LabelText" ) { + QString string; + if( KDXML::readStringNode( element, string ) ) + axisSettings->_axisLabelTexts = string; + } else if( tagName == "LabelTextsDirty" ) { + bool labelTextsDirty; + if( KDXML::readBoolNode( element, labelTextsDirty ) ) + axisSettings->_axisLabelTextsDirty = labelTextsDirty; + } else if( tagName == "FirstLabelReplacementText" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + axisSettings->_axisFirstLabelText = value; + } else if( tagName == "LastLabelReplacementText" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + axisSettings->_axisLastLabelText = value; + } else if( tagName == "LabelsDivPow10" ) { + int divPow10; + if( KDXML::readIntNode( element, divPow10 ) ) + axisSettings->_axisLabelsDivPow10 = divPow10; + } else if( tagName == "LabelsDecimalPoint" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + axisSettings->_axisLabelsDecimalPoint = value; + } else if( tagName == "LabelsThousandsPoint" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + axisSettings->_axisLabelsThousandsPoint = value; + } else if( tagName == "LabelsNotation" ) { + QString string; + if( KDXML::readStringNode( element, string ) ) + axisSettings->_axisLabelsNotation = KDChartEnums::stringToNumberNotation( string ); + } else if( tagName == "LabelsPrefix" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + axisSettings->_axisLabelsPrefix = value; + } else if( tagName == "LabelsPostfix" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + axisSettings->_axisLabelsPostfix = value; + } else if( tagName == "LabelsTotalLen" ) { + int totalLen; + if( KDXML::readIntNode( element, totalLen ) ) + axisSettings->_axisLabelsTotalLen = totalLen; + } else if( tagName == "LabelsPadFill" ) { + QString value; + if( KDXML::readStringNode( element, value ) ){ + if( value.isEmpty() ) + axisSettings->_axisLabelsPadFill = ' '; + else + axisSettings->_axisLabelsPadFill = value.at(0); + } + } else if( tagName == "LabelsBlockAlign" ) { + bool blockAlign; + if( KDXML::readBoolNode( element, blockAlign ) ) + axisSettings->_axisLabelsBlockAlign = blockAlign; + } else { + qDebug( "Unknown subelement of AxisSettings found: %s", tagName.latin1() ); + } + } + node = node.nextSibling(); + } + curAxisSettings++; // one axis settings entry finished +} + + +/** + Reads the parameters from an XML document. + \param doc the XML document to read from + \return true if the parameters could be read, false if a file + format error occurred + \sa saveXML + */ +bool KDChartParams::loadXML( const QDomDocument& doc ) +{ + int curAxisSettings = 0; + int curHFSettings = 0; + + QDomElement docRoot = doc.documentElement(); // ChartParams element + QDomNode node = docRoot.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "ChartType" ) { + if( element.hasAttribute( "primary" ) ) + _chartType = KDChartParams::stringToChartType( element.attribute( "primary" ) ); + if( element.hasAttribute( "secondary" ) ) + _additionalChartType = KDChartParams::stringToChartType( element.attribute( "secondary" ) ); + } else if( tagName == "NumValues" ) { + int numValues; + if( KDXML::readIntNode( element, numValues ) ) + _numValues = numValues; + } else if( tagName == "ModeAndChartMap" ) { + int dataset = -1; + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Dataset" ) { + KDXML::readIntNode( element, dataset ); + } else if( tagName == "ModeAndChart" ) { + Q_ASSERT( dataset != -1 ); // there must have been a dataset tag before + if( element.hasAttribute( "Mode" ) && + element.hasAttribute( "Chart" ) ) { + KDChartParams::SourceMode sourceMode = KDChartParams::stringToChartSourceMode( element.attribute( "Mode" ) ); + bool ok = false; + uint chart = element.attribute( "Chart" ).toUInt( &ok ); + if( ok ) + _dataSourceModeAndChart[dataset] = KDChartParams::ModeAndChart( sourceMode, chart ); + } + } else { + qDebug( "Unknown subelement of ModeAndChartMap found: %s", tagName.latin1() ); + } + } + node = node.nextSibling(); + } + } else if( tagName == "PropertySetMap" ) { + // the PropertySetMap element + QDomNode node = element.firstChild(); + KDChartPropertySet set; + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() // was really an element + && KDChartPropertySet::loadXML( element, set ) ) // parsed OK + setProperties( set.id(), set ); + node = node.nextSibling(); + } + } else if( tagName == "ChartSourceModeWasUsed" ) { + bool chartSourceModeWasUsed; + if( KDXML::readBoolNode( element, chartSourceModeWasUsed ) ) + _setChartSourceModeWasUsed = chartSourceModeWasUsed; + } else if( tagName == "MaxDatasetSourceMode" ) { + int maxDatasetSourceMode; + if( KDXML::readIntNode( element, maxDatasetSourceMode ) ) + _maxDatasetSourceMode = maxDatasetSourceMode; + } else if( tagName == "ColorSettings" ) { + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "DataColors" ) { + QMap<uint,QColor>* map = &_dataColors; + readColorMapNode( element, map ); + } else if( tagName == "MaxDatasetColor" ) { + int maxDatasetColor; + if( KDXML::readIntNode( element, maxDatasetColor ) ) + _maxDatasetColor = maxDatasetColor; + } else if( tagName == "ShadowBrightnessFactor" ) { + double shadowBrightnessFactor; + if( KDXML::readDoubleNode( element, shadowBrightnessFactor ) ) + _shadowBrightnessFactor = shadowBrightnessFactor; + } else if( tagName == "ShadowPattern" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + _shadowPattern = KDXML::stringToBrushStyle( value ); + } else if( tagName == "ThreeDShadowColors" ) { + bool threeDShadowColors; + if( KDXML::readBoolNode( element, threeDShadowColors ) ) + _threeDShadowColors = threeDShadowColors; + } else if( tagName == "DataColorsShadow1" ) { + QMap<uint,QColor>* map = &_dataColorsShadow1; + readColorMapNode( element, map ); + } else if( tagName == "DataColorsShadow2" ) { + QMap<uint,QColor>* map = &_dataColorsShadow2; + readColorMapNode( element, map ); + } else if( tagName == "OutlineDataColor" ) { + QColor color; + if( KDXML::readColorNode( element, color ) ) + _outlineDataColor = color; + } else if( tagName == "OutlineDataLineWidth" ) { + int outlineDataLineWidth; + if( KDXML::readIntNode( element, outlineDataLineWidth ) ) + _outlineDataLineWidth = outlineDataLineWidth; + } else if( tagName == "OutlineDataLineStyle" ) { + if( element.hasAttribute( "Style" ) ) + _outlineDataLineStyle = KDXML::stringToPenStyle( element.attribute( "Style" ) ); + } else { + qDebug( "!!!Unknown subelement of ColorSettings found: %s", tagName.latin1() ); + } + } + node = node.nextSibling(); + } + } else if( tagName == "BarSettings" ) { + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "SubType" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + _barChartSubType = KDChartParams::stringToBarChartSubType( value ); + } else if( tagName == "ThreeDBars" ) { + bool threeDBars; + if( KDXML::readBoolNode( element, threeDBars ) ) + _threeDBars = threeDBars; + } else if( tagName == "ThreeDBarDepth" ) { + double threeDBarDepth; + if( KDXML::readDoubleNode( element, threeDBarDepth ) ) + _threeDBarDepth = threeDBarDepth; + } else if( tagName == "DatasetGap" ) { + int datasetGap; + if( KDXML::readIntNode( element, datasetGap ) ) + _datasetGap = datasetGap; + } else if( tagName == "DatasetGapIsRelative" ) { + bool datasetGapIsRelative; + if( KDXML::readBoolNode( element, datasetGapIsRelative ) ) + _datasetGapIsRelative = datasetGapIsRelative; + } else if( tagName == "ValueBlockGap" ) { + int valueBlockGap; + if( KDXML::readIntNode( element, valueBlockGap ) ) + _valueBlockGap = valueBlockGap; + } else if( tagName == "ValueBlockGapIsRelative" ) { + bool valueBlockGapIsRelative; + if( KDXML::readBoolNode( element, valueBlockGapIsRelative ) ) + _valueBlockGapIsRelative = valueBlockGapIsRelative; + } else if( tagName == "BarWidth" ) { + int barWidth; + if( KDXML::readIntNode( element, barWidth ) ) + _barWidth = barWidth; + } else if( tagName == "SolidExcessArrows" ) { + bool solidExcessArrows; + if( KDXML::readBoolNode( element, solidExcessArrows ) ) + _solidExcessArrows = solidExcessArrows; + } else { + qDebug( "Unknown subelement of BarSettings found: %s", tagName.latin1() ); + } + } + node = node.nextSibling(); + } + } else if( tagName == "LineSettings" ) { + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "SubType" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + _lineChartSubType = KDChartParams::stringToLineChartSubType( value ); + } else if( tagName == "Marker" ) { + bool marker; + if( KDXML::readBoolNode( element, marker ) ) + _lineMarker = marker; + } else if( tagName == "MarkerStyle" ) { + bool ok = true; + uint dataset; + KDChartParams::LineMarkerStyle style = LineMarkerCircle; + if( element.hasAttribute( "Dataset" ) && + element.hasAttribute( "Style" ) ) { + dataset = element.attribute( "Dataset" ).toUInt( &ok ); + if( ok ) + style = KDChartParams::stringToLineMarkerStyle( element.attribute( "Style" ) ); + } else + ok = false; + if( ok ) + _lineMarkerStyles[dataset] = style; + } else if( tagName == "MarkerSize" ) { + int width = 1; + int height= 1; + bool ok = true; + if( element.hasAttribute( "Width" ) && + element.hasAttribute( "Height" ) ) { + width = element.attribute( "Width" ).toInt( &ok ); + if( ok ) + height = element.attribute( "Height" ).toInt( &ok ); + } else + ok = false; + + if( ok ) { + _lineMarkerSize.setWidth( width ); + _lineMarkerSize.setHeight( height ); + } + } else if( tagName == "LineWidth" ) { + int lineWidth; + if( KDXML::readIntNode( element, lineWidth ) ) + _lineWidth = lineWidth; + } else if( tagName == "LineColor" ) { + QColor color; + if( KDXML::readColorNode( element, color ) ) + _lineColor = color; + } else if( tagName == "LineStyle" ) { + if( element.hasAttribute( "Style" ) ) + _lineStyle = KDXML::stringToPenStyle( element.attribute( "Style" ) ); + } else if( tagName == "DatasetLineStyle" ) { + bool ok = true; + uint dataset; + PenStyle style = Qt::SolidLine; + if( element.hasAttribute( "Dataset" ) && + element.hasAttribute( "Style" ) ) { + dataset = element.attribute( "Dataset" ).toUInt( &ok ); + if( ok ) + style = KDXML::stringToPenStyle( element.attribute( "Style" ) ); + } else + ok = false; + if( ok ) + _datasetLineStyles[dataset] = style; + } else if( tagName == "ThreeD" ) { + bool threeD; + if( KDXML::readBoolNode( element, threeD ) ) + _threeDLines = threeD; + } else if( tagName == "ThreeDDepth" ) { + int depth; + if( KDXML::readIntNode( element, depth ) ) + _threeDLineDepth = depth; + } else if( tagName == "ThreeDXRotation" ) { + int rotation; + if( KDXML::readIntNode( element, rotation ) ) + _threeDLineXRotation = rotation; + } else if( tagName == "ThreeDYRotation" ) { + int rotation; + if( KDXML::readIntNode( element, rotation ) ) + _threeDLineYRotation = rotation; + } else { + qDebug( "Unknown subelement of LineSettings found: %s", tagName.latin1() ); + } + } + node = node.nextSibling(); + } + } else if( tagName == "AreaSettings" ) { + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "SubType" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + _areaChartSubType = KDChartParams::stringToAreaChartSubType( value ); + } else if( tagName == "Location" ) { + QString string; + if( KDXML::readStringNode( element, string ) ) + _areaLocation = KDChartParams::stringToAreaLocation( string ); + } else { + qDebug( "Unknown subelement of AreaSettings found: %s", tagName.latin1() ); + } + } + node = node.nextSibling(); + } + } else if( tagName == "PieRingSettings" ) { + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Explode" ) { + bool explode; + if( KDXML::readBoolNode( element, explode ) ) + _explode = explode; + } else if( tagName == "DefaultExplodeFactor" ) { + double defaultExplodeFactor; + if( KDXML::readDoubleNode( element, defaultExplodeFactor ) ) + _explodeFactor = defaultExplodeFactor; + } else if( tagName == "ExplodeFactors" ) { + QMap<int,double>* map = &_explodeFactors; + readDoubleMapNode( element, map ); + } else if( tagName == "ExplodeSegment" ) { + int explodeSegment; + if( KDXML::readIntNode( element, explodeSegment ) ) + _explodeList << explodeSegment; + } else if( tagName == "ThreeDPies" ) { + bool threeDPies; + if( KDXML::readBoolNode( element, threeDPies ) ) + _threeDPies = threeDPies; + } else if( tagName == "ThreeDPieHeight" ) { + int threeDPieHeight; + if( KDXML::readIntNode( element, threeDPieHeight ) ) + _threeDPieHeight = threeDPieHeight; + } else if( tagName == "PieStart" ) { + int pieStart; + if( KDXML::readIntNode( element, pieStart ) ) + _pieStart = pieStart; + } else if( tagName == "RingStart" ) { + int ringStart; + if( KDXML::readIntNode( element, ringStart ) ) + _ringStart = ringStart; + } else if( tagName == "RelativeRingThickness" ) { + bool relativeRingThickness; + if( KDXML::readBoolNode( element, relativeRingThickness ) ) + _relativeRingThickness = relativeRingThickness; + } else { + qDebug( "Unknown subelement of PieRingSettings found: %s", tagName.latin1() ); + } + } + node = node.nextSibling(); + } + } else if( tagName == "HiLoSettings" ) { + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "SubType" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + _hiLoChartSubType = KDChartParams::stringToHiLoChartSubType( value ); + } else if( tagName == "PrintLowValues" ) { + bool printLowValues; + if( KDXML::readBoolNode( element, printLowValues ) ) + _hiLoChartPrintLowValues = printLowValues; + } else if( tagName == "LowValuesFont" ) { + readChartFontNode( element, + _hiLoChartLowValuesFont, + _hiLoChartLowValuesUseFontRelSize, + _hiLoChartLowValuesFontRelSize ); + } else if( tagName == "LowValuesColor" ) { + QColor color; + if( KDXML::readColorNode( element, color ) ) + _hiLoChartLowValuesColor = color; + } else if( tagName == "PrintHighValues" ) { + bool printHighValues; + if( KDXML::readBoolNode( element, printHighValues ) ) + _hiLoChartPrintHighValues = printHighValues; + } else if( tagName == "HighValuesFont" ) { + readChartFontNode( element, + _hiLoChartHighValuesFont, + _hiLoChartHighValuesUseFontRelSize, + _hiLoChartHighValuesFontRelSize ); + } else if( tagName == "HighValuesColor" ) { + QColor color; + if( KDXML::readColorNode( element, color ) ) + _hiLoChartHighValuesColor = color; + } else if( tagName == "PrintOpenValues" ) { + bool printOpenValues; + if( KDXML::readBoolNode( element, printOpenValues ) ) + _hiLoChartPrintOpenValues = printOpenValues; + } else if( tagName == "OpenValuesFont" ) { + readChartFontNode( element, + _hiLoChartOpenValuesFont, + _hiLoChartOpenValuesUseFontRelSize, + _hiLoChartOpenValuesFontRelSize ); + } else if( tagName == "OpenValuesColor" ) { + QColor color; + if( KDXML::readColorNode( element, color ) ) + _hiLoChartOpenValuesColor = color; + } else if( tagName == "PrintCloseValues" ) { + bool printCloseValues; + if( KDXML::readBoolNode( element, printCloseValues ) ) + _hiLoChartPrintCloseValues = printCloseValues; + } else if( tagName == "CloseValuesFont" ) { + readChartFontNode( element, + _hiLoChartCloseValuesFont, + _hiLoChartCloseValuesUseFontRelSize, + _hiLoChartCloseValuesFontRelSize ); + } else if( tagName == "CloseValuesColor" ) { + QColor color; + if( KDXML::readColorNode( element, color ) ) + _hiLoChartCloseValuesColor = color; + } else { + qDebug( "Unknown subelement of HiLoSettings found: %s", tagName.latin1() ); + } + } + node = node.nextSibling(); + } + + + } else if( tagName == "BoxAndWhiskerSettings" ) { + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "SubType" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + _BWChartSubType = KDChartParams::stringToBWChartSubType( value ); + } else if( tagName == "FenceUpperInner" ) { + double fence; + if( KDXML::readDoubleNode( element, fence ) ) + _BWChartFenceUpperInner = fence; + } else if( tagName == "FenceLowerInner" ) { + double fence; + if( KDXML::readDoubleNode( element, fence ) ) + _BWChartFenceUpperOuter = fence; + } else if( tagName == "FenceUpperOuter" ) { + double fence; + if( KDXML::readDoubleNode( element, fence ) ) + _BWChartFenceUpperOuter = fence; + } else if( tagName == "FenceLowerOuter" ) { + double fence; + if( KDXML::readDoubleNode( element, fence ) ) + _BWChartFenceUpperOuter = fence; + } else if( tagName == "Brush" ) { + QBrush brush; + if( KDXML::readBrushNode( element, brush ) ) + _BWChartBrush = brush; + } else if( tagName == "OutlierSize" ) { + int size; + if( KDXML::readIntNode( element, size ) ) + _BWChartOutValMarkerSize = size; + } else if( tagName.startsWith("PrintStatistics") ) { + // 012345678901234 + QString statName(tagName.mid( 15 )); + BWStatVal i( stringToBWChartStatVal( statName ) ); + if( BWStatValSTART <= i && i <= BWStatValEND ) { + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Active" ) { + bool active; + if( KDXML::readBoolNode( element, active ) ) + _BWChartStatistics[ i ].active = active; + } else if( tagName == "Font" ) { + readChartFontNode( element, + _BWChartStatistics[ i ].font, + _BWChartStatistics[ i ].useRelSize, + _BWChartStatistics[ i ].relSize ); + } else if( tagName == "Color" ) { + QColor color; + if( KDXML::readColorNode( element, color ) ) + _BWChartStatistics[ i ].color = color; + } else if( tagName == "Brush" ) { + QBrush brush; + if( KDXML::readBrushNode( element, brush ) ) + _BWChartStatistics[ i ].brush = brush; + } else { + qDebug( "Unknown subelement of BoxAndWhiskerSettings found: %s", tagName.latin1() ); + } + } + } + } else { + qDebug( "Unknown subelement of BoxAndWhiskerSettings found: %s", tagName.latin1() ); + } + } else { + qDebug( "Unknown subelement of BoxAndWhiskerSettings found: %s", tagName.latin1() ); + } + } + node = node.nextSibling(); + } + } else if( tagName == "PolarSettings" ) { + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "SubType" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + _polarChartSubType = KDChartParams::stringToPolarChartSubType( value ); + } else if( tagName == "Marker" ) { + bool marker; + if( KDXML::readBoolNode( element, marker ) ) + _polarMarker = marker; + } else if( tagName == "MarkerStyle" ) { + bool ok = true; + uint dataset; + KDChartParams::PolarMarkerStyle style = PolarMarkerCircle; + if( element.hasAttribute( "Dataset" ) && + element.hasAttribute( "Style" ) ) { + dataset = element.attribute( "Dataset" ).toUInt( &ok ); + if( ok ) + style = KDChartParams::stringToPolarMarkerStyle( element.attribute( "Style" ) ); + } else + ok = false; + if( ok ) + _polarMarkerStyles[dataset] = style; + } else if( tagName == "MarkerSize" ) { + int width = 1; + int height= 1; + bool ok = true; + if( element.hasAttribute( "Width" ) && + element.hasAttribute( "Height" ) ) { + width = element.attribute( "Width" ).toInt( &ok ); + if( ok ) + height = element.attribute( "Height" ).toInt( &ok ); + } else + ok = false; + + if( ok ) { + _polarMarkerSize.setWidth( width ); + _polarMarkerSize.setHeight( height ); + } + } else if( tagName == "PolarLineWidth" ) { + int polarLineWidth; + if( KDXML::readIntNode( element, polarLineWidth ) ) + _polarLineWidth = polarLineWidth; + } else { + qDebug( "Unknown subelement of PolarSettings found: %s", tagName.latin1() ); + } + } + node = node.nextSibling(); + } + } else if( tagName == "LegendSettings" ) { + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Position" ) { + QString string; + if( KDXML::readStringNode( element, string ) ) + _legendPosition = KDChartParams::stringToLegendPosition( string ); + } else if( tagName == "Orientation" ) { + Qt::Orientation value=Qt::Vertical; + if( KDXML::readOrientationNode( element, value ) ) + _legendOrientation = value; + } else if( tagName == "ShowLines" ) { + bool showLines; + if( KDXML::readBoolNode( element, showLines ) ) + _legendShowLines = showLines; + } else if( tagName == "Source" ) { + QString string; + if( KDXML::readStringNode( element, string ) ) + _legendSource = KDChartParams::stringToLegendSource( string ); + } else if( tagName == "LegendText" ) { + bool ok = true; + uint dataset = 0; + QString text; + if( element.hasAttribute( "Dataset" ) && + element.hasAttribute( "Text" ) ) { + dataset = element.attribute( "Dataset" ).toUInt( &ok ); + if( ok ) + text = element.attribute( "Text" ); + } else + ok = false; + if( ok ) + _legendText[dataset] = text; + } else if( tagName == "TextColor" ) { + QColor color; + if( KDXML::readColorNode( element, color ) ) + _legendTextColor = color; + } else if( tagName == "TextFont" ) { + readChartFontNode( element, + _legendFont, + _legendFontUseRelSize, + _legendFontRelSize ); + } else if( tagName == "TitleText" ) { + QString string; + if( KDXML::readStringNode( element, string ) ) + _legendTitleText = string; + } else if( tagName == "TitleColor" ) { + QColor color; + if( KDXML::readColorNode( element, color ) ) + _legendTitleTextColor = color; + } else if( tagName == "TitleFont" ) { + readChartFontNode( element, + _legendTitleFont, + _legendTitleFontUseRelSize, + _legendTitleFontRelSize ); + } else if( tagName == "Spacing" ) { + int spacing; + if( KDXML::readIntNode( element, spacing ) ) + _legendSpacing = spacing; + } else { + qDebug( "Unknown subelement of LegendSettings found: %s", tagName.latin1() ); + } + } + node = node.nextSibling(); + } + } else if( tagName == "AxisSettings" ) { + + loadAxesFormXML(curAxisSettings, element); + + } else if( tagName == "HeaderFooterSettings" ) { + KDChartParams::HdFtParams* hfSettings = + &( _hdFtParams[ curHFSettings ] ); + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Text" ) { + QString string; + if( KDXML::readStringNode( element, string ) ) + hfSettings->_text = string; + } else if( tagName == "Font" ) { + readChartFontNode( element, + hfSettings->_font, + hfSettings->_fontUseRelSize, + hfSettings->_fontRelSize ); + } else if( tagName == "Color" ) { + QColor color; + if( KDXML::readColorNode( element, color ) ) + hfSettings->_color = color; + } else { + qDebug( "Unknown subelement of HeaderFooterSettings found: %s", tagName.latin1() ); + } + } + node = node.nextSibling(); + } + curHFSettings++; // one header/footer setting finished + } else if( tagName == "GlobalLeading" ) { + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an elemente + QString tagName = element.tagName(); + if( tagName == "Left" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _globalLeadingLeft = value; + } + else if( tagName == "Top" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _globalLeadingTop = value; + } + else if( tagName == "Right" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _globalLeadingRight = value; + } + else if( tagName == "Bottom" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _globalLeadingBottom = value; + } else { + qDebug( "Unknown subelement of GlobalLeading found: %s", tagName.latin1() ); + } + } + node = node.nextSibling(); + } + } else if( tagName == "DataValuesSettings1" ) { + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "PrintDataValues" ) { + bool value; + if( KDXML::readBoolNode( element, value ) ) + _printDataValuesSettings._printDataValues = value; + } else if( tagName == "DivPow10" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings._divPow10 = value; + } else if( tagName == "DigitsBehindComma" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings._digitsBehindComma = value; + } else if( tagName == "Font" ) { + readChartFontNode( element, + _printDataValuesSettings._dataValuesFont, + _printDataValuesSettings._dataValuesUseFontRelSize, + _printDataValuesSettings._dataValuesFontRelSize ); + } else if( tagName == "Color" ) { + KDXML::readColorNode( element, _printDataValuesSettings._dataValuesColor ); + } else if( tagName == "Background" ) { + KDXML::readBrushNode( element, _printDataValuesSettings._dataValuesBrush ); + } else if( tagName == "AutoColor" ) { + KDXML::readBoolNode( element, + _printDataValuesSettings._dataValuesAutoColor ); + } else if( tagName == "AnchorNegativePosition" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + _printDataValuesSettings._dataValuesAnchorNegativePosition = KDChartEnums::stringToPositionFlag( value ); + } else if( tagName == "AnchorNegativeAlign" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings._dataValuesAnchorNegativeAlign = value; + } else if( tagName == "AnchorNegativeDeltaX" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings._dataValuesAnchorNegativeDeltaX = value; + } else if( tagName == "AnchorNegativeDeltaY" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings._dataValuesAnchorNegativeDeltaY = value; + } else if( tagName == "NegativeRotation" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings._dataValuesNegativeRotation = value; + } else if( tagName == "AnchorPositivePosition" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + _printDataValuesSettings._dataValuesAnchorPositivePosition = KDChartEnums::stringToPositionFlag( value ); + } else if( tagName == "AnchorPositiveAlign" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings._dataValuesAnchorPositiveAlign = value; + } else if( tagName == "AnchorPositiveDeltaX" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings._dataValuesAnchorPositiveDeltaX = value; + } else if( tagName == "AnchorPositiveDeltaY" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings._dataValuesAnchorPositiveDeltaY = value; + } else if( tagName == "PositiveRotation" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings._dataValuesPositiveRotation = value; + } else if( tagName == "LayoutPolicy" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + _printDataValuesSettings._dataValuesLayoutPolicy = KDChartEnums::stringToLayoutPolicy( value ); + } else if( tagName == "ShowInfinite" ) { + KDXML::readBoolNode( element, _printDataValuesSettings._dataValuesShowInfinite ); + } else { + qDebug( "Unknown subelement of DataValuesSettings1 found: %s", tagName.latin1() ); + } + } + node = node.nextSibling(); + } + } else if( tagName == "DataValuesSettings2" ) { + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "PrintDataValues" ) { + bool value; + if( KDXML::readBoolNode( element, value ) ) + _printDataValuesSettings2._printDataValues = value; + } else if( tagName == "DivPow10" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings2._divPow10 = value; + } else if( tagName == "DigitsBehindComma" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings2._digitsBehindComma = value; + } else if( tagName == "Font" ) { + readChartFontNode( element, + _printDataValuesSettings2._dataValuesFont, + _printDataValuesSettings2._dataValuesUseFontRelSize, + _printDataValuesSettings2._dataValuesFontRelSize ); + } else if( tagName == "Color" ) { + KDXML::readColorNode( element, _printDataValuesSettings2._dataValuesColor ); + } else if( tagName == "Background" ) { + KDXML::readBrushNode( element, _printDataValuesSettings2._dataValuesBrush ); + } else if( tagName == "AutoColor" ) { + KDXML::readBoolNode( element, + _printDataValuesSettings2._dataValuesAutoColor ); + } else if( tagName == "AnchorNegativePosition" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + _printDataValuesSettings2._dataValuesAnchorNegativePosition = KDChartEnums::stringToPositionFlag( value ); + } else if( tagName == "AnchorNegativeAlign" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings2._dataValuesAnchorNegativeAlign = value; + } else if( tagName == "AnchorNegativeDeltaX" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings2._dataValuesAnchorNegativeDeltaX = value; + } else if( tagName == "AnchorNegativeDeltaY" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings2._dataValuesAnchorNegativeDeltaY = value; + } else if( tagName == "NegativeRotation" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings2._dataValuesNegativeRotation = value; + } else if( tagName == "AnchorPositivePosition" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + _printDataValuesSettings2._dataValuesAnchorPositivePosition = KDChartEnums::stringToPositionFlag( value ); + } else if( tagName == "AnchorPositiveAlign" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings2._dataValuesAnchorPositiveAlign = value; + } else if( tagName == "AnchorPositiveDeltaX" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings2._dataValuesAnchorPositiveDeltaX = value; + } else if( tagName == "AnchorPositiveDeltaY" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings2._dataValuesAnchorPositiveDeltaY = value; + } else if( tagName == "PositiveRotation" ) { + int value; + if( KDXML::readIntNode( element, value ) ) + _printDataValuesSettings2._dataValuesPositiveRotation = value; + } else if( tagName == "LayoutPolicy" ) { + QString value; + if( KDXML::readStringNode( element, value ) ) + _printDataValuesSettings2._dataValuesLayoutPolicy = KDChartEnums::stringToLayoutPolicy( value ); + } else if( tagName == "ShowInfinite" ) { + KDXML::readBoolNode( element, _printDataValuesSettings2._dataValuesShowInfinite ); + } else { + qDebug( "Unknown subelement of DataValuesSettings2 found: %s", tagName.latin1() ); + } + } + node = node.nextSibling(); + } + } else if( tagName == "DataValuesGlobalSettings" ) { + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "allowOverlappingTexts" ) { + bool value; + if( KDXML::readBoolNode( element, value ) ) + _allowOverlappingDataValueTexts = value; + } + else + qDebug( "Unknown subelement of DataValuesGlobalSettings found: %s", tagName.latin1() ); + // do _not_ return false here (to enable future extentions) + } + node = node.nextSibling(); + } + } else if( tagName == "AreaMap" ) { + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "FrameSettings" ) { + KDChartFrameSettings* frameSettings = new KDChartFrameSettings; + uint areaId = KDChartEnums::AreaUNKNOWN; + if( KDChartFrameSettings::readFrameSettingsNode( + element, *frameSettings, areaId ) ) { + QString str; + if(areaId == KDChartEnums::AreaChartDataRegion) + str = QString( "%1/%2/%3/%4" ) + .arg( areaId, 5 ) + .arg( frameSettings->dataRow(), 5 ) + .arg( frameSettings->dataCol(), 5 ) + .arg( 0, 5 );//frameSettings->data3rd(), 5 ); + else + str = QString( "%1/-----/-----/-----" ).arg( areaId, 5 ); + _areaDict.replace( str, frameSettings ); + } + } + else + qDebug( "Unknown tag in AreaMap found: %s", tagName.latin1() ); + // do _not_ return false here (to enable future extentions) + } + node = node.nextSibling(); + } + } else if( tagName == "CustomBoxMap" ) { + QDomNode node = element.firstChild(); + int curNumber = -1; + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Number" ) { + KDXML::readIntNode( element, curNumber ); + } else if( tagName == "CustomBox" ) { + Q_ASSERT( curNumber != -1 ); // there was a Dataset tag before + KDChartCustomBox customBox; + KDChartCustomBox::readCustomBoxNode( element, + customBox ); + _customBoxDict.insert( curNumber, customBox.clone() ); + } + else + qDebug( "Unknown tag in CustomBoxMap found: %s", tagName.latin1() ); + } + node = node.nextSibling(); + } + } else { + qDebug( "Unknown second-level element found: %s", tagName.latin1() ); + // NOTE: We do *not* 'return false' here but continue normal operation + // since additional elements might have been added in future versions + } + } + node = node.nextSibling(); + } + return true; +} + + +/** + Creates a DOM element node that represents a map of QColor objects + for use in a DOM document. + + \param doc the DOM document to which the node will belong + \param parent the parent node to which the new node will be appended + \param elementName the name of the new node + \param map the map of colors to be represented + */ +void KDChartParams::createColorMapNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, + const QMap< uint, QColor >& map ) +{ + QDomElement mapElement = + doc.createElement( elementName ); + parent.appendChild( mapElement ); + for( QMap<uint,QColor>::ConstIterator it = map.begin(); + it != map.end(); ++it ) { + // Dataset element + QDomElement datasetElement = doc.createElement( "Dataset" ); + mapElement.appendChild( datasetElement ); + QDomText datasetContent = + doc.createTextNode( QString::number( it.key() ) ); + datasetElement.appendChild( datasetContent ); + // Color element + KDXML::createColorNode( doc, mapElement, "Color", it.data() ); + } +} + + +/** + Creates a DOM element node that represents a map of doubles + for use in a DOM document. + + \param doc the DOM document to which the node will belong + \param parent the parent node to which the new node will be appended + \param elementName the name of the new node + \param map the map of doubles to be represented + */ +void KDChartParams::createDoubleMapNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, + const QMap< int, double >& map ) +{ + QDomElement mapElement = + doc.createElement( elementName ); + parent.appendChild( mapElement ); + for( QMap<int,double>::ConstIterator it = map.begin(); + it != map.end(); ++it ) { + // Dataset element + QDomElement valueElement = doc.createElement( "Value" ); + mapElement.appendChild( valueElement ); + QDomText valueContent = + doc.createTextNode( QString::number( it.key() ) ); + valueElement.appendChild( valueContent ); + // Color element + KDXML::createDoubleNode( doc, mapElement, "Factor", it.data() ); + } +} + + +void dataCoordToElementAttr(const QVariant& val, QDomElement& element, const QString& postfix) +{ + if( QVariant::Double == val.type() ) + element.setAttribute( "DoubleValue"+postfix, + QString::number( val.toDouble() ) ); + else if( QVariant::String == val.type() ) + element.setAttribute( "StringValue"+postfix, val.toString() ); + else if( QVariant::DateTime == val.type() ) + element.setAttribute( "DateTimeValue"+postfix, + val.toDateTime().toString( Qt::ISODate ) ); + else + element.setAttribute( "NoValue"+postfix, "true" ); +} + +/** + Creates a DOM element node that represents a + chart value for use in a DOM document. + + \param doc the DOM document to which the node will belong + \param parent the parent node to which the new node will be appended + \param elementName the name of the new node + \param data the chart value to be represented + */ +void KDChartParams::createChartValueNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, + const QVariant& valY, + const QVariant& valX, + const int& propID ) +{ + QDomElement element = doc.createElement( elementName ); + parent.appendChild( element ); + dataCoordToElementAttr( valY, element, "" ); // no postfix for Y value: backwards compat. + dataCoordToElementAttr( valX, element, "X" ); + element.setAttribute( "PropertySetID", + QString::number( propID ) ); +} + + + +/** + Creates a DOM element node that represents a font used in a + chart for use in a DOM document. + + \param doc the DOM document to which the node will belong + \param parent the parent node to which the new node will be appended + \param elementName the name of the new node + \param font the font to be resented + \param useRelFont the specification whether the font size + is relative + \param relFont the relative font size + \param minFont the minimal font size in points, leave this parameter out if not needed + */ +void KDChartParams::createChartFontNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, + const QFont& font, bool useRelFont, + int relFont, + int minFont ) +{ + QDomElement chartFontElement = doc.createElement( elementName ); + parent.appendChild( chartFontElement ); + KDXML::createFontNode( doc, chartFontElement, "Font", font ); + KDXML::createBoolNode( doc, chartFontElement, "UseRelFontSize", + useRelFont ); + KDXML::createIntNode( doc, chartFontElement, "RelFontSize", relFont ); + if( 0 <= minFont ) + KDXML::createIntNode( doc, chartFontElement, "MinFontSize", minFont ); +} + + + + + +/** + Creates a DOM element node that represents a color map + for use in a DOM document. + + \param doc the DOM document to which the node will belong + \param parent the parent node to which the new node will be appended + \param elementName the name of the new node + \param map the color map to be represented + */ +bool KDChartParams::readColorMapNode( const QDomElement& element, + QMap<uint,QColor>* value ) +{ + QDomNode node = element.firstChild(); + int curDataset = -1; + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Dataset" ) { + KDXML::readIntNode( element, curDataset ); + } else if( tagName == "Color" ) { + Q_ASSERT( curDataset != -1 ); // there was a Dataset tag before + QColor color; + KDXML::readColorNode( element, color ); + value->insert( curDataset, color ); + } else { + qDebug( "Unknown tag in color map" ); + } + } + node = node.nextSibling(); + } + + return true; +} + + +/** + Reads data from a DOM element node that represents a double + map and fills a double map with the data. + + \param element the DOM element to read from + \param map the frame settings object to read the data into + */ +bool KDChartParams::readDoubleMapNode( const QDomElement& element, + QMap<int,double>* value ) +{ + QDomNode node = element.firstChild(); + int curValue = -1; + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Value" ) { + KDXML::readIntNode( element, curValue ); + } else if( tagName == "Factor" ) { + Q_ASSERT( curValue != -1 ); // there was a Value tag before + double doubleValue; + KDXML::readDoubleNode( element, doubleValue ); + value->insert( curValue, doubleValue ); + } else { + qDebug( "Unknown tag in double map" ); + } + } + node = node.nextSibling(); + } + + return true; +} + + + +/** + Reads data from a DOM element node that represents a font + uses in a chart and fills the reference parameters + with the data. + + \param element the DOM element to read from + \param font the represented font + \param useRelFont whether the font size is relative + \param relFontSize the relative font size + */ +bool KDChartParams::readChartFontNode( const QDomElement& element, + QFont& font, + bool& useRelFont, + int& relFontSize, + int* minFontSize ) +{ + bool ok = true; + QFont tempFont; + bool tempRelFont; + int tempRelFontSize; + int tempMinFontSize=-1; + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Font" ) { + ok = ok & KDXML::readFontNode( element, tempFont ); + } else if( tagName == "UseRelFontSize" ) { + ok = ok & KDXML::readBoolNode( element, tempRelFont ); + } else if( tagName == "RelFontSize" ) { + ok = ok & KDXML::readIntNode( element, tempRelFontSize ); + } else if( tagName == "MinFontSize" ) { + ok = ok & KDXML::readIntNode( element, tempMinFontSize ); + } else { + qDebug( "Unknown tag in color map" ); + } + } + node = node.nextSibling(); + } + + if( ok ) { + font = tempFont; + useRelFont = tempRelFont; + relFontSize = tempRelFontSize; + if( minFontSize && 0 <= tempMinFontSize ) + *minFontSize = tempMinFontSize; + } + + return ok; +} + + +bool foundCoordAttribute( const QDomElement& element, const QString& postfix, + QVariant& val ) +{ + if( element.hasAttribute( "NoValue"+postfix ) ) + val = QVariant(); + else if( element.hasAttribute( "DoubleValue"+postfix ) ) + val = element.attribute( "DoubleValue"+postfix ); + else if( element.hasAttribute( "DateTimeValue"+postfix ) ) + val = element.attribute( "DateTimeValue"+postfix ); + else if( element.hasAttribute( "StringValue"+postfix ) ) + val = element.attribute( "StringValue"+postfix ); + else + return false; + + return true; +} + +// PENDING(kalle) Support DateTime values, even when writing. +/** + Reads data from a DOM element node that represents a chart + value and fills a KDChartData object with the data. + + \param element the DOM element to read from + \param value the chart data object to read the data into + */ +bool KDChartParams::readChartValueNode( const QDomElement& element, + QVariant& valY, + QVariant& valX, + int& propID ) +{ + if( foundCoordAttribute( element, "", valY ) || + foundCoordAttribute( element, "Y", valY ) ){ // valY must be there + if( !foundCoordAttribute( element, "X", valX ) ){ + valX = QVariant(); + } + propID = 0; + if( element.hasAttribute( "PropertySetID" ) ) { + bool ok; + int i = element.attribute( "PropertySetID" ).toInt( &ok ); + if( ok ) + propID = i; + } + return true; // if Y value found everything is OK + } + + return false; +} + + +/** + Converts the specified chart type enum to a string representation. + + \param type the type enum to convert + \return the string representation of the type enum + */ +QString KDChartParams::chartTypeToString( ChartType type ) +{ + switch( type ) { + case NoType: + return "NoType"; + case Bar: + return "Bar"; + case Line: + return "Line"; + case Area: + return "Area"; + case Pie: + return "Pie"; + case HiLo: + return "HiLo"; + case BoxWhisker: + return "BoxWhisker"; + case Ring: + return "Ring"; + case Polar: + return "Polar"; + default: // should not happen + return "NoType"; + } +} + + +/** + Converts the specified string to a chart type enum value. + + \param string the string to convert + \return the chart type enum value + */ +KDChartParams::ChartType KDChartParams::stringToChartType( const QString& string ) +{ + if( string == "NoType" ) + return NoType; + else if( string == "Bar" ) + return Bar; + else if( string == "Line" ) + return Line; + else if( string == "Area" ) + return Area; + else if( string == "Pie" ) + return Pie; + else if( string == "HiLo" ) + return HiLo; + else if( string == "BoxWhisker" ) + return BoxWhisker; + else if( string == "Ring" ) + return Ring; + else if( string == "Polar" ) + return Polar; + else // default, should not happen + return NoType; +} + + + +QString KDChartParams::markerStyleToString( int style ) +{ + switch( style ) { + case LineMarkerSquare: + return "Square"; + case LineMarkerDiamond: + return "Diamond"; + case LineMarkerCircle: + return "Circle"; + case LineMarker1Pixel: + return "one Pixel"; + case LineMarker4Pixels: + return "four Pixels"; + case LineMarkerRing: + return "Ring"; + case LineMarkerCross: + return "Cross"; + case LineMarkerFastCross: + return "fast Cross"; + default: // should not happen + qDebug( "Unknown marker style" ); + return "Circle"; + } +} +QString KDChartParams::markerStyleToStringTr( int style ) +{ + switch( style ) { + case LineMarkerSquare: + return tr( "Square" ); + case LineMarkerDiamond: + return tr( "Diamond" ); + case LineMarkerCircle: + return tr( "Circle" ); + case LineMarker1Pixel: + return tr( "One pixel" ); + case LineMarker4Pixels: + return tr( "Four pixels" ); + case LineMarkerRing: + return tr( "Ring" ); + case LineMarkerCross: + return tr( "Cross" ); + case LineMarkerFastCross: + return tr( "fast Cross" ); + default: // should not happen + qDebug( "Unknown line marker style!" ); + return tr( "Circle" ); + } +} +int KDChartParams::stringToMarkerStyle( const QString& string ) +{ + if( string == "Square" ) + return LineMarkerSquare; + else if( string == "Diamond" ) + return LineMarkerDiamond; + else if( string == "Circle" ) + return LineMarkerCircle; + else if( string == "one Pixel" ) + return LineMarker1Pixel; + else if( string == "four Pixels" ) + return LineMarker4Pixels; + else if( string == "Ring" ) + return LineMarkerRing; + else if( string == "Cross" ) + return LineMarkerCross; + else if( string == "fast Cross" ) + return LineMarkerFastCross; + else // default, should not happen + return LineMarkerCircle; +} +int KDChartParams::stringToMarkerStyleTr( const QString& string ) +{ + if( string == tr( "Square" ) ) + return LineMarkerSquare; + else if( string == tr( "Diamond" ) ) + return LineMarkerDiamond; + else if( string == tr( "Circle" ) ) + return LineMarkerCircle; + else if( string == tr( "One pixel" ) ) + return LineMarker1Pixel; + else if( string == tr( "Four pixels" ) ) + return LineMarker4Pixels; + else if( string == tr( "Ring" ) ) + return LineMarkerRing; + else if( string == tr( "Cross" ) ) + return LineMarkerCross; + else if( string == tr( "fast Cross" ) ) + return LineMarkerFastCross; + else // default, should not happen + return LineMarkerCircle; +} + + + +/** + Converts the specified bar chart subtype enum to a string representation. + + \param type the type enum to convert + \return the string representation of the type enum + */ +QString KDChartParams::barChartSubTypeToString( BarChartSubType type ) { + switch( type ) { + case BarNormal: + return "BarNormal"; + case BarStacked: + return "BarStacked"; + case BarPercent: + return "BarPercent"; + case BarMultiRows: + return "BarMultiRows"; + default: // should not happen + qDebug( "Unknown bar type" ); + return "BarNormal"; + } +} + + +/** + Converts the specified string to a bar chart subtype enum value. + + \param string the string to convert + \return the bar chart subtype enum value + */ +KDChartParams::BarChartSubType KDChartParams::stringToBarChartSubType( const QString& string ) { + if( string == "BarNormal" ) + return BarNormal; + else if( string == "BarStacked" ) + return BarStacked; + else if( string == "BarPercent" ) + return BarPercent; + else if( string == "BarMultiRows" ) + return BarMultiRows; + else // should not happen + return BarNormal; +} + + + +/** + Converts the specified string to a line chart subtype enum value. + + \param string the string to convert + \return the line chart subtype enum value + */ +KDChartParams::LineChartSubType KDChartParams::stringToLineChartSubType( const QString& string ) { + if( string == "LineNormal" ) + return LineNormal; + else if( string == "LineStacked" ) + return LineStacked; + else if( string == "LinePercent" ) + return LinePercent; + else // should not happen + return LineNormal; +} + + + +/** + Converts the specified line chart subtype enum to a string representation. + + \param type the type enum to convert + \return the string representation of the type enum + */ +QString KDChartParams::lineChartSubTypeToString( LineChartSubType type ) { + switch( type ) { + case LineNormal: + return "LineNormal"; + case LineStacked: + return "LineStacked"; + case LinePercent: + return "LinePercent"; + default: // should not happen + qDebug( "Unknown bar type" ); + return "LineNormal"; + } +} + + +/** + Converts the specified line marker style enum to a string + representation. + + \param type the type enum to convert + \return the string representation of the type enum + */ +QString KDChartParams::lineMarkerStyleToString( LineMarkerStyle style ) +{ + return markerStyleToString( style ); +} + + +/** + Converts the specified line marker style enum to a localized + string representation that can be used for string output. + + \param type the type enum to convert + \return the localized string representation of the type enum + */ +QString KDChartParams::lineMarkerStyleToStringTr( LineMarkerStyle style ) +{ + return markerStyleToStringTr( style ); +} + + +/** + Converts the specified string to a line marker style value. + + \param string the string to convert + \return the line marker style enum value + */ +KDChartParams::LineMarkerStyle KDChartParams::stringToLineMarkerStyle( const QString& string ) +{ + return static_cast<KDChartParams::LineMarkerStyle>(stringToMarkerStyle( string )); +} + +/** + Converts the specified localized string to a line marker style + value. + + \param string the string to convert + \return the line marker style enum value + */ +KDChartParams::LineMarkerStyle KDChartParams::stringToLineMarkerStyleTr( const QString& string ) +{ + return static_cast<KDChartParams::LineMarkerStyle>(stringToMarkerStyleTr( string )); +} + + +/** + Converts the specified area chart subtype enum to a string representation. + + \param type the subtype enum to convert + \return the string representation of the type enum + */ +QString KDChartParams::areaChartSubTypeToString( AreaChartSubType type ) { + switch( type ) { + case AreaNormal: + return "AreaNormal"; + case AreaStacked: + return "AreaStacked"; + case AreaPercent: + return "AreaPercent"; + default: // should not happen + qDebug( "Unknown area chart subtype" ); + return "AreaNormal"; + } +} + + +/** + Converts the specified string to a area chart subtype enum value. + + \param string the string to convert + \return the area chart subtype enum value + */ +KDChartParams::AreaChartSubType KDChartParams::stringToAreaChartSubType( const QString& string ) { + if( string == "AreaNormal" ) + return AreaNormal; + else if( string == "AreaStacked" ) + return AreaStacked; + else if( string == "AreaPercent" ) + return AreaPercent; + else // should not happen + return AreaNormal; +} + + +/** + Converts the specified area location enum to a string representation. + + \param type the location enum to convert + \return the string representation of the type enum + */ +QString KDChartParams::areaLocationToString( AreaLocation type ) { + switch( type ) { + case AreaAbove: + return "Above"; + case AreaBelow: + return "Below"; + default: // should not happen + qDebug( "Unknown area location" ); + return "Below"; + } +} + + +/** + Converts the specified string to an area location enum value. + + \param string the string to convert + \return the aration location enum value + */ +KDChartParams::AreaLocation KDChartParams::stringToAreaLocation( const QString& string ) { + if( string == "Above" ) + return AreaAbove; + else if( string == "Below" ) + return AreaBelow; + else // default, should not happen + return AreaBelow; +} + + +/** + Converts the specified string to a polar chart subtype enum value. + + \param string the string to convert + \return the polar chart subtype enum value + */ +KDChartParams::PolarChartSubType KDChartParams::stringToPolarChartSubType( const QString& string ) { + if( string == "PolarNormal" ) + return PolarNormal; + else if( string == "PolarStacked" ) + return PolarStacked; + else if( string == "PolarPercent" ) + return PolarPercent; + else // should not happen + return PolarNormal; +} + + +/** + Converts the specified polar chart subtype enum to a string representation. + + \param type the type enum to convert + \return the string representation of the type enum + */ +QString KDChartParams::polarChartSubTypeToString( PolarChartSubType type ) { + switch( type ) { + case PolarNormal: + return "PolarNormal"; + case LineStacked: + return "PolarStacked"; + case LinePercent: + return "PolarPercent"; + default: // should not happen + qDebug( "Unknown polar type" ); + return "PolarNormal"; + } +} + + +/** + Converts the specified polar marker style enum to a string + representation. + + \param type the type enum to convert + \return the string representation of the type enum + */ +QString KDChartParams::polarMarkerStyleToString( PolarMarkerStyle style ) +{ + return markerStyleToString( style ); +} + + +/** + Converts the specified polar marker style enum to a localized + string representation that can be used for string output. + + \param type the type enum to convert + \return the localized string representation of the type enum + */ +QString KDChartParams::polarMarkerStyleToStringTr( PolarMarkerStyle style ) +{ + return markerStyleToStringTr( style ); +} + + +/** + Converts the specified string to a polar marker style value. + + \param string the string to convert + \return the polar marker style enum value + */ +KDChartParams::PolarMarkerStyle KDChartParams::stringToPolarMarkerStyle( const QString& string ) +{ + return static_cast<KDChartParams::PolarMarkerStyle>(stringToMarkerStyle( string )); +} + + +/** + Converts the specified localized string to a polar marker style + value. + + \param string the string to convert + \return the polar marker style enum value + */ +KDChartParams::PolarMarkerStyle KDChartParams::stringToPolarMarkerStyleTr( const QString& string ) +{ + return static_cast<KDChartParams::PolarMarkerStyle>(stringToMarkerStyle( string )); +} + + +/** + Converts the specified HiLo chart subtype enum to a string representation. + + \param type the subtype enum to convert + \return the string representation of the type enum + */ +QString KDChartParams::hiLoChartSubTypeToString( HiLoChartSubType type ) { + switch( type ) { + case HiLoSimple: + return "HiLoSimple"; + case HiLoClose: + return "HiLoClose"; + case HiLoOpenClose: + return "HiLoOpenClose"; + default: // should not happen + qDebug( "Unknown HiLo chart subtype" ); + return "HiLoNormal"; + } +} + + +/** + Converts the specified string to a HiLo chart subtype enum value. + + \param string the string to convert + \return the HiLo chart subtype enum value + */ +KDChartParams::HiLoChartSubType KDChartParams::stringToHiLoChartSubType( const QString& string ) { + if( string == "HiLoSimple" ) + return HiLoSimple; + else if( string == "HiLoClose" ) + return HiLoClose; + else if( string == "HiLoOpenClose" ) + return HiLoOpenClose; + else // should not happen + return HiLoNormal; +} + + +/** + Converts the specified string to a BoxWhisker chart subtype enum value. + + \param string the string to convert + \return the BoxWhisker chart subtype enum value + */ +KDChartParams::BWChartSubType KDChartParams::stringToBWChartSubType( const QString& string ) { + if( string == "BWSimple" ) + return BWSimple; + else // should not happen + return BWNormal; +} + +/** + Converts the specified BWStatVal enum to a string representation. + + \param type the BWStatVal enum to convert + \return the string representation of the type BWStatVal + */ +QString KDChartParams::bWChartStatValToString( BWStatVal type ) { + switch( type ) { + case UpperOuterFence: + return "UpperOuterFence"; + case UpperInnerFence: + return "UpperInnerFence"; + case Quartile3: + return "Quartile3"; + case Median: + return "Median"; + case Quartile1: + return "Quartile1"; + case LowerInnerFence: + return "LowerInnerFence"; + case LowerOuterFence: + return "LowerOuterFence"; + case MaxValue: + return "MaxValue"; + case MeanValue: + return "MeanValue"; + case MinValue: + return "MinValue"; + default: // should not happen + qDebug( "Unknown BoxWhisker statistical value type" ); + return "unknown"; + } +} + +/** + Converts the specified string to a BWStatVal enum value. + + \param string the string to convert + \return the BWStatVal enum value + */ +KDChartParams::BWStatVal KDChartParams::stringToBWChartStatVal( const QString& string ) { + if( string == "UpperOuterFence" ) + return UpperOuterFence; + else if( string == "UpperInnerFence" ) + return UpperInnerFence; + else if( string == "Quartile3" ) + return Quartile3; + else if( string == "Median" ) + return Median; + else if( string == "Quartile1" ) + return Quartile1; + else if( string == "LowerInnerFence" ) + return LowerInnerFence; + else if( string == "LowerOuterFence" ) + return LowerOuterFence; + else if( string == "MaxValue" ) + return MaxValue; + else if( string == "MeanValue" ) + return MeanValue; + else if( string == "MinValue" ) + return MinValue; + else // should not happen + return BWStatValUNKNOWN; +} + + +/** + Converts the specified legend position enum to a string representation. + + \param type the legend position enum to convert + \return the string representation of the type enum + */ +QString KDChartParams::legendPositionToString( LegendPosition pos ) { + switch( pos ) { + case NoLegend: + return "NoLegend"; + case LegendTop: + return "LegendTop"; + case LegendBottom: + return "LegendBottom"; + case LegendLeft: + return "LegendLeft"; + case LegendRight: + return "LegendRight"; + case LegendTopLeft: + return "LegendTopLeft"; + case LegendTopLeftTop: + return "LegendTopLeftTop"; + case LegendTopLeftLeft: + return "LegendTopLeftLeft"; + case LegendBottomLeft: + return "LegendBottomLeft"; + case LegendBottomLeftBottom: + return "LegendBottomLeftBottom"; + case LegendBottomLeftLeft: + return "LegendBottomLeftLeft"; + case LegendTopRight: + return "LegendTopRight"; + case LegendTopRightTop: + return "LegendTopRightTop"; + case LegendTopRightRight: + return "LegendTopRightRight"; + case LegendBottomRight: + return "LegendBottomRight"; + case LegendBottomRightBottom: + return "LegendBottomRightBottom"; + case LegendBottomRightRight: + return "LegendBottomRightRight"; + default: // should not happen + qDebug( "Unknown legend position" ); + return "LegendLeft"; + } +} + + +/** + Converts the specified string to a legend position enum value. + + \param string the string to convert + \return the legend position enum value + */ +KDChartParams::LegendPosition KDChartParams::stringToLegendPosition( const QString& string ) { + if( string == "NoLegend" ) + return NoLegend; + else if( string == "LegendTop" ) + return LegendTop; + else if( string == "LegendBottom" ) + return LegendBottom; + else if( string == "LegendLeft" ) + return LegendLeft; + else if( string == "LegendRight" ) + return LegendRight; + else if( string == "LegendTopLeft" ) + return LegendTopLeft; + else if( string == "LegendTopLeftTop" ) + return LegendTopLeftTop; + else if( string == "LegendTopLeftLeft" ) + return LegendTopLeftLeft; + else if( string == "LegendBottomLeft" ) + return LegendBottomLeft; + else if( string == "LegendBottomLeftBottom" ) + return LegendBottomLeftBottom; + else if( string == "LegendBottomLeftLeft" ) + return LegendBottomLeftLeft; + else if( string == "LegendTopRight" ) + return LegendTopRight; + else if( string == "LegendTopRightTop" ) + return LegendTopRightTop; + else if( string == "LegendTopRightRight" ) + return LegendTopRightRight; + else if( string == "LegendBottomRight" ) + return LegendBottomRight; + else if( string == "LegendBottomRightBottom" ) + return LegendBottomRightBottom; + else if( string == "LegendBottomRightRight" ) + return LegendBottomRightRight; + else // default, should not happen + return LegendLeft; +} + +/** + Converts the specified legend source enum to a string representation. + + \param source the legend source enum to convert + \return the string representation of the type enum + */ +QString KDChartParams::legendSourceToString( LegendSource source ) { + switch( source ) { + case LegendManual: + return "Manual"; + case LegendFirstColumn: + return "FirstColumn"; + case LegendAutomatic: + return "Automatic"; + default: // should not happen + qDebug( "Unknown legend source" ); + return "Automatic"; + } +} + + +/** + Converts the specified string to a legend source enum value. + + \param string the string to convert + \return the legend source enum value + */ +KDChartParams::LegendSource KDChartParams::stringToLegendSource( const QString& string ) { + if( string == "Manual" ) + return LegendManual; + else if( string == "FirstColumn" ) + return LegendFirstColumn; + else if( string == "Automatic" ) + return LegendAutomatic; + else // default, should not happen + return LegendAutomatic; +} + + +/** + Converts the specified chart source mode enum value to a string. + + \param mode the chart source mode enum value to convert + \return the string + */ +QString KDChartParams::chartSourceModeToString( const SourceMode& mode ) +{ + switch( mode ){ + case UnknownMode: + return "UnknownMode"; + case DontUse: + return "DontUse"; + case DataEntry: + return "DataEntry"; + case AxisLabel: + return "AxisLabel"; + case LegendText: + return "LegendText"; + case ExtraLinesAnchor: + return "ExtraLinesAnchor"; + default: // should not happen + return "UnknownMode"; + } +} + + +/** + Converts the specified string to a chart source mode enum value. + + \param string the string to convert + \return the chart source mode enum value + */ +KDChartParams::SourceMode KDChartParams::stringToChartSourceMode( const QString& string ) +{ + if( string.isEmpty() ) + return UnknownMode; + // compatibility with pre-1.0 KDChart stream format: + bool bOk; + int mode = string.toInt( &bOk ); + if( bOk && mode >= 0 && mode <= Last_SourceMode ) + return (KDChartParams::SourceMode)mode; + // new (KDChart 1.0...) stream format: + if( string == "UnknownMode" ) + return UnknownMode; + if( string == "DontUse" ) + return DontUse; + if( string == "DataEntry" ) + return DataEntry; + if( string == "AxisLabel" ) + return AxisLabel; + if( string == "LegendText" ) + return LegendText; + if( string == "ExtraLinesAnchor" ) + return ExtraLinesAnchor; + // should not happen + return UnknownMode; +} + + +/** + Converts the specified BoxWhisker chart subtype enum to a string + representation. + + \param type the subtype enum to convert + \return the string representation of the type enum + */ +QString KDChartParams::bWChartSubTypeToString( BWChartSubType type ) { + switch( type ) { + case BWSimple: + return "BWSimple"; + default: // should not happen + qDebug( "Unknown BoxWhisker chart subtype" ); + return "BWNormal"; + } +} + diff --git a/kchart/kdchart/KDChartPiePainter.cpp b/kchart/kdchart/KDChartPiePainter.cpp new file mode 100644 index 00000000..f1715f1b --- /dev/null +++ b/kchart/kdchart/KDChartPiePainter.cpp @@ -0,0 +1,831 @@ +/* -*- 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. + ** + **********************************************************************/ +#include "KDChartEnums.h" +#include "KDChartPiePainter.h" +#include "KDChartParams.h" + +#include <qpainter.h> +#include <qvaluestack.h> +#include <qmessagebox.h> + +#define DEGTORAD(d) (d)*M_PI/180 + +#include <math.h> + +/** + \class KDChartPiePainter KDChartPiePainter.h + + \brief A chart painter implementation that can paint pie charts. + + PENDING(kalle) Write more documentation. + */ + +/** + Constructor. Sets up internal data structures as necessary. + + \param params the KDChartParams structure that defines the chart + */ + KDChartPiePainter::KDChartPiePainter( KDChartParams* params ) : +KDChartPainter( 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. + */ +KDChartPiePainter::~KDChartPiePainter() +{ + // intentionally left blank +} + +//static bool bHelp=true; + +/** + Paints the actual data area. + + \param painter the QPainter onto which the chart should be painted + \param data the data that will be displayed as a chart + \param paint2nd specifies whether the main chart or the additional chart is to be drawn now + \param regions a pointer to a list of regions that will be filled + with regions representing the data segments, if not null + */ +void KDChartPiePainter::paintData( QPainter* painter, + KDChartTableDataBase* data, + bool paint2nd, + KDChartDataRegionList* regions ) +{ +//bHelp=true; + uint chart = paint2nd ? 1 : 0; + + QRect ourClipRect( _dataRect ); + ourClipRect.addCoords( -1,-1,1,1 ); + + const QWMatrix & world = painter->worldMatrix(); + ourClipRect = +#if COMPAT_QT_VERSION >= 0x030000 + world.mapRect( ourClipRect ); +#else + world.map( ourClipRect ); +#endif + + painter->setClipRect( ourClipRect ); + + // find which dataset to paint + uint dataset; + if ( !params()->findDataset( KDChartParams::DataEntry + , + dataset, dataset ) ) { + return ; // nothing to draw + } + + if ( dataset == KDCHART_ALL_DATASETS ) + // setChartSourceMode() has not been used (or all datasets have been + // configured to be used); use the first dataset by + // default + dataset = 0; + + + // Number of values: If -1, use all values, otherwise use the + // specified number of values. + if ( params()->numValues() != -1 ) + _numValues = params()->numValues(); + else + _numValues = data->usedCols(); + + _startAngles.resize( _numValues ); + _angleLens.resize( _numValues ); + + // compute position + _size = QMIN( _dataRect.width(), _dataRect.height() ); // initial size + // if the pies explode, we need to give them additional space => + // make the basic size smaller + if ( params()->explode() ) { + double doubleSize = ( double ) _size; + doubleSize /= ( 1.0 + params()->explodeFactor() * 2 ); + _size = ( int ) doubleSize; + } + + int sizeFor3DEffect = 0; + if ( !params()->threeDPies() ) { + + int x = ( _dataRect.width() == _size ) ? 0 : ( ( _dataRect.width() - _size ) / 2 ); + int y = ( _dataRect.height() == _size ) ? 0 : ( ( _dataRect.height() - _size ) / 2 ); + _position = QRect( x, y, _size, _size ); + _position.moveBy( _dataRect.left(), _dataRect.top() ); + } else { + // threeD: width is the maximum possible width; height is 1/2 of that + int x = ( _dataRect.width() == _size ) ? 0 : ( ( _dataRect.width() - _size ) / 2 ); + int height = _size; + // make sure that the height plus the threeDheight is not more than the + // available size + if ( params()->threeDPieHeight() >= 0 ) { + // positive pie height: absolute value + sizeFor3DEffect = params()->threeDPieHeight(); + height = _size - sizeFor3DEffect; + } else { + // negative pie height: relative value + sizeFor3DEffect = -( int ) ( ( ( double ) params()->threeDPieHeight() / 100.0 ) * ( double ) height ); + height = _size - sizeFor3DEffect; + } + int y = ( _dataRect.height() == height ) ? 0 : ( ( _dataRect.height() - height - sizeFor3DEffect ) / 2 ); + + _position = QRect( _dataRect.left() + x, _dataRect.top() + y, + _size, height ); + // _position.moveBy( _dataRect.left(), _dataRect.top() ); + } + + double sum = data->rowAbsSum( dataset ); + if( sum==0 ) //nothing to draw + return; + double sectorsPerValue = 5760.0 / sum; // 5760 == 16*360, number of sections in Qt circle + + int currentValue = params()->pieStart() * 16; + bool atLeastOneValue = false; // guard against completely empty tables + QVariant vValY; + for ( int value = 0; value < _numValues; value++ ) { + // is there anything at all at this value + /* see above for meaning of 16 */ + + if( data->cellCoord( dataset, value, vValY, 1 ) && + QVariant::Double == vValY.type() ){ + _startAngles[ value ] = currentValue; + const double cellValue = fabs( vValY.toDouble() ); + _angleLens[ value ] = ( int ) floor( cellValue * sectorsPerValue + 0.5 ); + atLeastOneValue = true; + } else { // mark as non-existent + _angleLens[ value ] = 0; + if ( value > 0 ) + _startAngles[ value ] = _startAngles[ value - 1 ]; + else + _startAngles[ value ] = currentValue; + } + + currentValue = _startAngles[ value ] + _angleLens[ value ]; + } + + // If there was no value at all, bail out, to avoid endless loops + // later on (e.g. in findPieAt()). + if( !atLeastOneValue ) + return; + + + // Find the backmost pie which is at +90° and needs to be drawn + // first + int backmostpie = findPieAt( 90 * 16 ); + // Find the frontmost pie (at -90°/+270°) that should be drawn last + int frontmostpie = findPieAt( 270 * 16 ); + // and put the backmost pie on the TODO stack to initialize it, + // but only if it is not the frontmostpie + QValueStack < int > todostack; + if ( backmostpie != frontmostpie ) + todostack.push( backmostpie ); + else { + // Otherwise, try to find something else + int leftOfCurrent = findLeftPie( backmostpie ); + if ( leftOfCurrent != frontmostpie ) { + todostack.push( leftOfCurrent ); + } else { + int rightOfCurrent = findRightPie( backmostpie ); + if ( rightOfCurrent != frontmostpie ) { + todostack.push( rightOfCurrent ); + } + } + // If we get here, there was nothing else, and we will bail + // out of the while loop below. + } + + // The list with pies that have already been drawn + + QValueList < int > donelist; + + // Draw pies until the todostack is empty or only the frontmost + // pie is there + while ( !todostack.isEmpty() && + !( ( todostack.count() == 1 ) && + ( ( todostack.top() == frontmostpie ) ) ) ) { + // The while loop cannot be cancelled if frontmostpie is on + // top of the stack, but this is also backmostpie (can happen + // when one of the pies covers more than 1/2 of the circle. In + // this case, we need to find something else to put on the + // stack to get things going. + + // take one pie from the stack + int currentpie = todostack.pop(); + // if this pie was already drawn, ignore it + if ( donelist.find( currentpie ) != donelist.end() ) + continue; + + // If this pie is the frontmost pie, put it back, but at the + // second position (otherwise, there would be an endless + // loop). If this pie is the frontmost pie, there must be at + // least one other pie, otherwise the loop would already have + // been terminated by the loop condition. + if ( currentpie == frontmostpie ) { + Q_ASSERT( !todostack.isEmpty() ); + // QValueStack::exchange() would be nice here... + int secondpie = todostack.pop(); + if ( currentpie == secondpie ) + // no need to have the second pie twice on the stack, + // forget about one instance and take the third + // instead + if ( todostack.isEmpty() ) + break; // done anyway + else + secondpie = todostack.pop(); + todostack.push( currentpie ); + todostack.push( secondpie ); + continue; + } + + // When we get here, we can just draw the pie and proceed. + drawOnePie( painter, data, dataset, currentpie, chart, + sizeFor3DEffect, + regions ); + + // Mark the pie just drawn as done. + donelist.append( currentpie ); + + // Now take the pie to the left and to the right, check + // whether these have not been painted already, and put them + // on the stack. + int leftOfCurrent = findLeftPie( currentpie ); + if ( donelist.find( leftOfCurrent ) == donelist.end() ) + todostack.push( leftOfCurrent ); + int rightOfCurrent = findRightPie( currentpie ); + if ( donelist.find( rightOfCurrent ) == donelist.end() ) + todostack.push( rightOfCurrent ); + } + + // now only the frontmost pie is left to draw + drawOnePie( painter, data, dataset, frontmostpie, chart, + sizeFor3DEffect, + regions ); +} + + +/** + Internal method that draws one of the pies in a pie chart. + + \param painter the QPainter to draw in + \param dataset the dataset to draw the pie for + \param pie the pie to draw + \param the chart to draw the pie in + \param regions a pointer to a list of regions that will be filled + with regions representing the data segments, if not null + */ +void KDChartPiePainter::drawOnePie( QPainter* painter, + KDChartTableDataBase* /*data*/, + uint dataset, uint pie, uint chart, + uint threeDPieHeight, + KDChartDataRegionList* regions ) +{ + // Is there anything to draw at all? + int angleLen = _angleLens[ ( int ) pie ]; + if ( angleLen ) { + int startAngle = _startAngles[ ( int ) pie ]; + + KDChartDataRegion* datReg = 0; + QRegion* region = 0; + bool mustDeleteRegion = false; + if ( regions ){ + region = new QRegion(); + mustDeleteRegion = true; + } + + QRect drawPosition = _position; + if ( params()->explode() ) { + // need to compute a new position for each or some of the pie + QValueList<int> explodeList = params()->explodeValues(); + if( explodeList.count() == 0 || // nothing on list, explode all + explodeList.find( pie ) != explodeList.end() ) { + double explodeAngle = ( startAngle + angleLen / 2 ) / 16; + double explodeAngleRad = DEGTORAD( explodeAngle ); + double cosAngle = cos( explodeAngleRad ); + double sinAngle = -sin( explodeAngleRad ); + + // find the explode factor for this particular pie + double explodeFactor = 0.0; + QMap<int,double> explodeFactors = params()->explodeFactors(); + if( !explodeFactors.contains( pie ) ) // not on factors list, use default + explodeFactor = params()->explodeFactor(); + else // on factors list, use segment-specific value + explodeFactor = explodeFactors[pie]; + + double explodeX = explodeFactor * _size * cosAngle; + double explodeY = explodeFactor * _size * sinAngle; + drawPosition.moveBy( static_cast<int>( explodeX ), static_cast<int>( explodeY ) ); + } else + drawPosition = _position; + } else + drawPosition = _position; + + // The 3D effect needs to be drawn first because it could + // otherwise partly hide the pie itself. + if ( params()->threeDPies() ) { + draw3DEffect( painter, drawPosition, dataset, pie, chart, + threeDPieHeight, + params()->explode(), region ); + } + + painter->setBrush( params()->dataColor( pie ) ); + if ( angleLen == 5760 ) { + // full circle, avoid nasty line in the middle + painter->drawEllipse( drawPosition ); + if ( regions ) { + QPointArray hitregion; + hitregion.makeEllipse( drawPosition.x(), drawPosition.y(), + drawPosition.width(), + drawPosition.height() ); + datReg = new KDChartDataRegion( region->unite( QRegion( hitregion ) ), + dataset, + pie, + chart ); + datReg->points[ KDChartEnums::PosCenter ] + = drawPosition.center(); + datReg->points[ KDChartEnums::PosCenterRight ] + = pointOnCircle( drawPosition, 0 ); + datReg->points[ KDChartEnums::PosTopRight ] + = pointOnCircle( drawPosition, 720 ); + datReg->points[ KDChartEnums::PosTopCenter ] + = pointOnCircle( drawPosition, 1440 ); + datReg->points[ KDChartEnums::PosTopLeft ] + = pointOnCircle( drawPosition, 2160 ); + datReg->points[ KDChartEnums::PosCenterLeft ] + = pointOnCircle( drawPosition, 2880 ); + datReg->points[ KDChartEnums::PosBottomLeft ] + = pointOnCircle( drawPosition, 3600 ); + datReg->points[ KDChartEnums::PosBottomCenter ] + = pointOnCircle( drawPosition, 4320 ); + datReg->points[ KDChartEnums::PosBottomRight ] + = pointOnCircle( drawPosition, 5040 ); + datReg->startAngle = 2880; + datReg->angleLen = 5760; + regions->append( datReg ); + } + } else { + // draw the top of this piece + // Start with getting the points for the arc. + const int arcPoints = angleLen; + QPointArray collect(arcPoints+2); + int i=0; + for ( ; i<=angleLen; ++i){ + collect.setPoint(i, pointOnCircle( drawPosition, startAngle+i )); + } + // Adding the center point of the piece. + collect.setPoint(i, drawPosition.center() ); + + + + painter->drawPolygon( collect ); + +//if( bHelp ){ +// painter->drawPolyline( collect ); +//bHelp=false; +//} + + + + if ( regions ) { + QPointArray hitregion; + hitregion.makeArc( drawPosition.x(), drawPosition.y(), + drawPosition.width(), + drawPosition.height(), + ( int ) startAngle, ( int ) angleLen ); + hitregion.resize( hitregion.size() + 1 ); + hitregion.setPoint( hitregion.size() - 1, + drawPosition.center() ); + datReg = new KDChartDataRegion( region->unite( QRegion( hitregion ) ), + dataset, + pie, + chart ); + + datReg->points[ KDChartEnums::PosTopLeft ] + = pointOnCircle( drawPosition, startAngle + angleLen ); + datReg->points[ KDChartEnums::PosTopCenter ] + = pointOnCircle( drawPosition, startAngle + angleLen / 2 ); + datReg->points[ KDChartEnums::PosTopRight ] + = pointOnCircle( drawPosition, startAngle ); + + datReg->points[ KDChartEnums::PosBottomLeft ] = drawPosition.center(); + datReg->points[ KDChartEnums::PosBottomCenter ] + = datReg->points[ KDChartEnums::PosBottomLeft ]; + datReg->points[ KDChartEnums::PosBottomRight ] + = datReg->points[ KDChartEnums::PosBottomLeft ]; + + datReg->points[ KDChartEnums::PosCenterLeft ] + = QPoint( ( datReg->points[ KDChartEnums::PosTopLeft ].x() + + datReg->points[ KDChartEnums::PosBottomLeft ].x() ) / 2, + ( datReg->points[ KDChartEnums::PosTopLeft ].y() + + datReg->points[ KDChartEnums::PosBottomLeft ].y() ) / 2 ); + datReg->points[ KDChartEnums::PosCenter ] + = QPoint( ( datReg->points[ KDChartEnums::PosTopCenter ].x() + + datReg->points[ KDChartEnums::PosBottomCenter ].x() ) / 2, + ( datReg->points[ KDChartEnums::PosTopCenter ].y() + + datReg->points[ KDChartEnums::PosBottomCenter ].y() ) / 2 ); + datReg->points[ KDChartEnums::PosCenterRight ] + = QPoint( ( datReg->points[ KDChartEnums::PosTopRight ].x() + + datReg->points[ KDChartEnums::PosBottomRight ].x() ) / 2, + ( datReg->points[ KDChartEnums::PosTopRight ].y() + + datReg->points[ KDChartEnums::PosBottomRight ].y() ) / 2 ); + + datReg->startAngle = startAngle; + datReg->angleLen = angleLen; + regions->append( datReg ); + } + } + if( mustDeleteRegion ) + delete region; + } +} + + +/** + Internal method that draws the shadow creating the 3D effect of a pie + + \param painter the QPainter to draw in + \param rect the position to draw at + \param dataset the dataset to draw the pie for + \param pie the pie to draw the shadow for + \param the chart to draw the pie in + \param threeDHeight the height of the shadow + \param regions a pointer to a list of regions that will be filled + with regions representing the data segments, if not null + */ +void KDChartPiePainter::draw3DEffect( QPainter* painter, + const QRect& drawPosition, + uint dataset, uint pie, uint chart, + uint threeDHeight, + bool /*explode*/, + QRegion* region ) +{ + // 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. + painter->setBrush( QBrush( params()->dataShadow1Color( pie ), + params()->shadowPattern() ) ); + + int startAngle = _startAngles[ ( int ) pie ]; + int endAngle = startAngle + _angleLens[ ( int ) pie ]; + // Normalize angles + while ( startAngle >= 5760 ) + startAngle -= 5760; + while ( endAngle >= 5760 ) + endAngle -= 5760; + Q_ASSERT( startAngle >= 0 && startAngle <= 360 * 16 ); + Q_ASSERT( endAngle >= 0 && endAngle <= 360 * 16 ); + + //int centerY = drawPosition.center().y(); + + if ( startAngle == endAngle || + startAngle == endAngle - 5760 ) { // full circle + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, 2880, 5760, region ); + } else if ( startAngle <= 90 * 16 ) { + if ( endAngle <= 90 * 16 ) { + if ( startAngle <= endAngle ) { + /// starts and ends in first quadrant, less than 1/4 + drawStraightEffectSegment( painter, drawPosition, dataset, + pie, chart, threeDHeight, startAngle, + region ); + } else { + /// starts and ends in first quadrant, more than 3/4 + drawStraightEffectSegment( painter, drawPosition, dataset, + pie, chart, threeDHeight, startAngle, + region ); + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, 2880, 5760, region ); + } + } else if ( endAngle <= 180 * 16 ) { + /// starts in first quadrant, ends in second quadrant, + /// less than 1/2 + drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, startAngle, region ); + drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, endAngle, region ); + } else if ( endAngle <= 270 * 16 ) { + /// starts in first quadrant, ends in third quadrant + drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, startAngle, + region ); + drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, endAngle, region ); + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, 2880, endAngle, region ); + } else { // 270*16 < endAngle < 360*16 + /// starts in first quadrant, ends in fourth quadrant, + /// more than 3/4 + drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, startAngle, region ); + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, 2880, endAngle, region ); + } + } else if ( startAngle <= 180 * 16 ) { + if ( endAngle <= 90 * 16 ) { + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, 2880, 5760, region ); + } else if ( endAngle <= 180 * 16 ) { + if ( startAngle <= endAngle ) { + /// starts in second quadrant, ends in second + /// quadrant, less than 1/4 + drawStraightEffectSegment( painter, drawPosition, dataset, + pie, chart, threeDHeight, endAngle, + region ); + } else { + /// starts in second quadrant, ends in second + /// quadrant, more than 1/4 + drawStraightEffectSegment( painter, drawPosition, dataset, + pie, chart, threeDHeight, endAngle, + region ); + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, 2880, 5760, region ); + } + } else if ( endAngle <= 270 * 16 ) { + drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, endAngle, region ); + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, 2880, endAngle, region ); + } else { // 270*16 < endAngle < 360*16 + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, 2880, endAngle, region ); + } + } else if ( startAngle <= 270 * 16 ) { + if ( endAngle <= 90 * 16 ) { + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, startAngle, 5760, region ); + } else if ( endAngle <= 180 * 16 ) { + drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, endAngle, region ); + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, startAngle, 5760, region ); + } else if ( endAngle <= 270 * 16 ) { + if ( startAngle <= endAngle ) { + /// starts in third quadrant, ends in third quadrant, + /// less than 1/4 + drawStraightEffectSegment( painter, drawPosition, dataset, + pie, chart, threeDHeight, endAngle, + region ); + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, startAngle, endAngle, + region ); + } else { + /// starts in third quadrant, ends in third quadrant, + /// more than 3/4 + drawStraightEffectSegment( painter, drawPosition, dataset, + pie, chart, threeDHeight, endAngle, + region ); + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, 2880, endAngle, + region ); + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, startAngle, 5760, + region ); + } + } else { // 270*16 < endAngle < 360*16 + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, startAngle, endAngle, + region ); + } + } else { // 270*16 < startAngle < 360*16 + if ( endAngle <= 90 * 16 ) { + drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, startAngle, region ); + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, startAngle, 5760, region ); + } else if ( endAngle <= 180 * 16 ) { + drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, startAngle, region ); + drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, endAngle, region ); + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, startAngle, 5760, region ); + } else if ( endAngle <= 270 * 16 ) { + drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, startAngle, region ); + drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, endAngle, region ); + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, 2880, endAngle, region ); + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, startAngle, 5760, region ); + } else { // 270*16 < endAngle < 360*16 + if ( startAngle <= endAngle ) { + /// starts in fourth quadrant, ends in fourth + /// quadrant, less than 1/4 + drawStraightEffectSegment( painter, drawPosition, dataset, + pie, chart, threeDHeight, startAngle, + region ); + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, startAngle, endAngle, + region ); + } else { + /// starts in fourth quadrant, ends in fourth + /// quadrant, more than 3/4 + drawStraightEffectSegment( painter, drawPosition, dataset, + pie, chart, threeDHeight, startAngle, + region ); + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, startAngle, 5760, + region ); + drawArcEffectSegment( painter, drawPosition, dataset, pie, chart, + threeDHeight, 2880, endAngle, region ); + } + } + } +} + + +/** + Internal method that draws a segment with a straight 3D effect + + \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 + \param regions a pointer to a list of regions that will be filled + with regions representing the data segments, if not null + */ +void KDChartPiePainter::drawStraightEffectSegment( QPainter* painter, + const QRect& rect, + uint /*dataset*/, uint /*pie*/, uint /*chart*/, + int threeDHeight, + int angle, + QRegion* region ) +{ + QPoint center = rect.center(); + QPointArray points( 4 ); + QPoint circlePoint = pointOnCircle( rect, angle ); + points.setPoint( 0, center ); + points.setPoint( 1, circlePoint ); + points.setPoint( 2, circlePoint.x(), circlePoint.y() + threeDHeight ); + points.setPoint( 3, center.x(), + center.y() + threeDHeight ); + painter->drawPolygon( points ); + if ( region ) + *region += QRegion( points ); +} + + +/** + Internal method that draws a segment with an arc 3D effect + + \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 + \param regions a pointer to a list of regions that will be filled + with regions representing the data segments, if not null + */ +void KDChartPiePainter::drawArcEffectSegment( QPainter* painter, + const QRect& rect, + uint /*dataset*/, uint /*pie*/, uint /*chart*/, + int threeDHeight, + int startAngle, + int endAngle, + QRegion* region ) +{ + // Start with getting the points for the inner arc. + const int startA = QMIN(startAngle, endAngle); + const int endA = QMAX(startAngle, endAngle); + const int arcPoints = endA-startA+1; + QPointArray collect(arcPoints * 2); + for ( int angle=endA; angle>=startA; --angle){ + collect.setPoint(endA-angle, pointOnCircle( rect, angle )); + } + + // Now copy these arcs again into the same array, but in the + // opposite direction and moved down by the 3D height. + for ( int i = arcPoints - 1; i >= 0; --i ) { + QPoint pointOnFirstArc = collect.point( i ); + pointOnFirstArc.setY( pointOnFirstArc.y() + threeDHeight ); + collect.setPoint( arcPoints * 2 - i - 1, pointOnFirstArc ); + } + painter->drawPolygon( collect ); + if ( region ) + *region += QRegion( collect ); +} + + +/** + Internal method that finds the pie that is located at the position + specified by \c angle. + + \param angle the angle at which to search for a pie + \return the number of the pie found + */ +uint KDChartPiePainter::findPieAt( int angle ) +{ + for ( int i = 0; i < _numValues; i++ ) { + int endseg = _startAngles[ i ] + _angleLens[ i ]; + if ( ( _startAngles[ i ] <= angle ) && + ( endseg >= angle ) ) + // found! + return i; + } + + // If we have not found it, try wrap around + return findPieAt( angle + 5760 ); +} + + +/** + Internal method that finds the pie that is located to the left of + the pie specified by \c pie. + + \param pie the pie to start the search from + \return the number of the pie to the left of \c pie + */ +uint KDChartPiePainter::findLeftPie( uint pie ) +{ + if ( pie == 0 ) + if ( _numValues > 1 ) + return _numValues - 1; + else + return 0; + else { + return pie - 1; + } +} + + +/** + Internal method that finds the pie that is located to the right of + the pie specified by \c pie. + + \param pie the pie to start the search from + \return the number of the pie to the right of \c pie + */ +uint KDChartPiePainter::findRightPie( uint pie ) +{ + int rightpie = pie + 1; + if ( rightpie == _numValues ) + rightpie = 0; + return rightpie; +} + + +/** + This method is a specialization that returns a fallback legend text + appropriate for pies that do not have more than one dataset + + 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 KDChartPiePainter::fallbackLegendText( uint dataset ) const +{ + return QObject::tr( "Item " ) + 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 KDChartPiePainter::numLegendFallbackTexts( KDChartTableDataBase* data ) const +{ + return data->usedCols(); +} diff --git a/kchart/kdchart/KDChartPiePainter.h b/kchart/kdchart/KDChartPiePainter.h new file mode 100644 index 00000000..047aa334 --- /dev/null +++ b/kchart/kdchart/KDChartPiePainter.h @@ -0,0 +1,84 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTPIEPAINTER_H__ +#define __KDCHARTPIEPAINTER_H__ + +#include <KDChartPainter.h> +#include <KDChartTable.h> + +class KDChartParams; + +class KDChartPiePainter : public KDChartPainter +{ + friend class KDChartPainter; + protected: + KDChartPiePainter( KDChartParams* params ); + virtual ~KDChartPiePainter(); + + virtual void paintData( QPainter* painter, + KDChartTableDataBase* data, + bool paint2nd, + KDChartDataRegionList* regions = 0 ); + virtual void drawOnePie( QPainter* painter, + KDChartTableDataBase* data, + uint dataset, uint pie, uint chart, + uint threeDPieHeight, + KDChartDataRegionList* regions = 0 ); + virtual void draw3DEffect( QPainter* painter, const QRect& drawPosition, + uint dataset, uint pie, uint chart, + uint threeDPieHeight, + bool explode, + QRegion* region = 0 ); + void drawStraightEffectSegment( QPainter* painter, const QRect& rect, + uint dataset, uint pie, uint chart, + int threeDHeight, int angle, + QRegion* region = 0 ); + void drawArcEffectSegment( QPainter* painter, const QRect& rect, + uint dataset, uint pie, uint chart, + int threeDHeight, int startAngle, + int endAngle, + QRegion* region = 0 ); + + virtual QString fallbackLegendText( uint dataset ) const; + virtual uint numLegendFallbackTexts( KDChartTableDataBase* data ) const; + + uint findPieAt( int angle ); + uint findLeftPie( uint pie ); + uint findRightPie( uint pie ); + + QMemArray < int > _startAngles; + QMemArray < int > _angleLens; + + QRect _position; + int _size; + int _numValues; // PENDING(kalle) Move to base class +} +; + +#endif diff --git a/kchart/kdchart/KDChartPlaneSeries.cpp b/kchart/kdchart/KDChartPlaneSeries.cpp new file mode 100644 index 00000000..319302cd --- /dev/null +++ b/kchart/kdchart/KDChartPlaneSeries.cpp @@ -0,0 +1,138 @@ +/* -*- 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. + ** + **********************************************************************/ + + +#include "KDChartPlaneSeries.h" + +KDChartPlaneSeries::KDChartPlaneSeries( bool isX, double location ) +{ + setXAxis(isX); + setLocation(location); +} + +KDChartPlaneSeries::~KDChartPlaneSeries() +{ +} + + +uint KDChartPlaneSeries::rows() const +{ + return 2; +} + + +const KDChartData& KDChartPlaneSeries::cell( uint row ) const +{ + switch (row) + { + case 0: return _start; + case 1: return _stop; + default: Q_ASSERT(0); + return _start; // won't get here + } +} + +void KDChartPlaneSeries::setCell( uint row, const KDChartData& element) +{ + Q_ASSERT(0); // not possible + // avoid compiler warnings + row = (uint)element.doubleValue(); +} + +void KDChartPlaneSeries::expand( uint rows ) +{ + Q_ASSERT(0); // not possible + // avoid compiler warnings + rows = 0; +} + + + +// NOW for our special API. +bool KDChartPlaneSeries::isXAxis() const +{ + return _isX; +} + +double KDChartPlaneSeries::location() const +{ + return _location; +} + +void KDChartPlaneSeries::setXAxis( bool isX ) +{ + _isX = isX; + update(); +} + +void KDChartPlaneSeries::setLocation( double location ) +{ + _location = location; + update(); +} + +// this is the magic part of the class. +// draw a line from DBL_MIN to DBL_MAX. +void KDChartPlaneSeries::update() +{ + if ( _isX ) + { + _start = KDChartData( DBL_MIN, _location ); + _stop = KDChartData( DBL_MAX, _location ); + } + else + { + _start = KDChartData( _location, DBL_MIN ); + _stop = KDChartData( _location, DBL_MAX ); + } +} + + + +// we return !ok if its on the infinite axis +double KDChartPlaneSeries::maxValue( int coordinate, bool &ok ) const +{ + // coordinate==0 is the x value... + // is not ok + if ( _isX == (coordinate==0) ) + { + ok = false; + return 0; + } + + ok = true; + return _location; +} + + + +double KDChartPlaneSeries::minValue( int coordinate, bool &ok ) const +{ + return maxValue(coordinate,ok); +} diff --git a/kchart/kdchart/KDChartPlaneSeries.h b/kchart/kdchart/KDChartPlaneSeries.h new file mode 100644 index 00000000..5974d436 --- /dev/null +++ b/kchart/kdchart/KDChartPlaneSeries.h @@ -0,0 +1,69 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTPLANESERIES_H__ +#define __KDCHARTPLANESERIES_H__ + + +#include "KDChartBaseSeries.h" + + +class KDCHART_EXPORT KDChartPlaneSeries : public KDChartBaseSeries +{ + public: + KDChartPlaneSeries( bool isX = false, double location = 0 ); + virtual ~KDChartPlaneSeries(); + + virtual uint rows() const; + virtual const KDChartData& cell( uint row ) const; + virtual void setCell( uint row, const KDChartData& element); + virtual void expand( uint rows ); + + + // methods modelled on the TableBase methods, but these + // inherit from BaseSeries. + virtual double maxValue( int coordinate, bool &ok ) const; + virtual double minValue( int coordinate, bool &ok ) const; + + + // NOW for our special API. + virtual bool isXAxis() const; + virtual double location() const; + + virtual void setXAxis( bool isX ); // if false, its a y axis plane + virtual void setLocation( double location ); + + protected: + bool _isX; + double _location; + KDChartData _start, _stop; + virtual void update(); +}; + + +#endif diff --git a/kchart/kdchart/KDChartPolarPainter.cpp b/kchart/kdchart/KDChartPolarPainter.cpp new file mode 100644 index 00000000..41009a10 --- /dev/null +++ b/kchart/kdchart/KDChartPolarPainter.cpp @@ -0,0 +1,805 @@ +/* -*- 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. + ** + **********************************************************************/ +#include "KDChartPolarPainter.h" +#include <KDChartParams.h> +#include <KDChartAxisParams.h> +#include "KDChartAxesPainter.h" +#include "KDDrawText.h" + +#include <qpainter.h> + +/** + \class KDChartPolarPainter KDChartPolarPainter.h + + \brief A chart painter implementation that can paint polar charts. +*/ + +/** + Constructor. Sets up internal data structures as necessary. + + \param params the KDChartParams structure that defines the chart + \param data the data that will be displayed as a chart +*/ +KDChartPolarPainter::KDChartPolarPainter( KDChartParams* params ) : + KDChartPainter( 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. +*/ +KDChartPolarPainter::~KDChartPolarPainter() +{ + // intentionally left blank +} + + +/** + Paints the actual data area. Data regions will only be added if \a + regions is not 0 and the chart is configured to be drawn with + markers. + + \param painter the QPainter onto which the chart should be painted + \param data the data that will be displayed as a chart + \param paint2nd specifies whether the main chart or the additional chart is to be drawn now + \param regions a pointer to a list of regions that will be filled + with regions representing the data segments, if not null +*/ +void KDChartPolarPainter::paintData( QPainter* painter, + KDChartTableDataBase* data, + bool paint2nd, + KDChartDataRegionList* regions ) +{ + uint chart = paint2nd ? 1 : 0; + + QRect ourClipRect( _dataRect ); + ourClipRect.setBottom( ourClipRect.bottom() - 1 ); // protect axes + ourClipRect.setLeft( ourClipRect.left() + 1 ); + ourClipRect.setRight( ourClipRect.right() - 1 ); + // + // PENDING(khz) adjust the clip rect if neccessary... + // + + const QWMatrix & world = painter->worldMatrix(); + ourClipRect = +#if COMPAT_QT_VERSION >= 0x030000 + world.mapRect( ourClipRect ); +#else + world.map( ourClipRect ); +#endif + + painter->setClipRect( ourClipRect ); + + + uint datasetStart, datasetEnd; + findChartDatasets( data, paint2nd, chart, datasetStart, datasetEnd ); + + + painter->translate( _dataRect.x(), _dataRect.y() ); + + + // 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(); + + // compute position + int size = QMIN( _dataRect.width(), _dataRect.height() ); // initial size + + const double minSizeP1000 = size / 1000.0; + + int x = ( _dataRect.width() == size ) ? 0 : ( ( _dataRect.width() - size ) / 2 ); + int y = ( _dataRect.height() == size ) ? 0 : ( ( _dataRect.height() - size ) / 2 ); + QRect position( x, y, size, size ); + + QPoint center( position.width() / 2 + position.x(), + position.height() / 2 + position.y() ); + + + double maxValue; + switch ( params()->polarChartSubType() ) { + case KDChartParams::PolarNormal: + maxValue = data->maxValue(); + break; + case KDChartParams::PolarPercent: + maxValue = 100.0; + break; + default: + maxValue = QMAX( data->maxColSum(), 0.0 ); + } + + double pixelsPerUnit = 0.0; + // the / 2 in the next line is there because we need the space in + // both directions + pixelsPerUnit = (position.height() / maxValue / 2) * 1000 / 1250; + + QMap < int, double > currentValueSums; + if ( params()->polarChartSubType() == KDChartParams::PolarStacked + || params()->polarChartSubType() == KDChartParams::PolarPercent ) + // this array is only used for stacked and percent polar + // charts, no need to waste time initializing it for normal + // ones + for ( int value = 0; value < numValues; value++ ) + currentValueSums[ value ] = 0.0; + QMap < int, double > totalValueSums; + + + /* + axes schema: use AxisPosSagittal for sagittal 'axis' lines + use AxisPosCircular for circular 'axis' + */ + const KDChartAxisParams & paraSagittal = params()->axisParams( KDChartAxisParams::AxisPosSagittal ); + const KDChartAxisParams & paraCircular = params()->axisParams( KDChartAxisParams::AxisPosCircular ); + + int sagittalLineWidth = 0 <= paraSagittal.axisLineWidth() + ? paraSagittal.axisLineWidth() + : -1 * static_cast < int > ( paraSagittal.axisLineWidth() + * minSizeP1000 ); + ( ( KDChartAxisParams& ) paraSagittal ).setAxisTrueLineWidth( sagittalLineWidth ); + int sagittalGridLineWidth + = ( KDCHART_AXIS_GRID_AUTO_LINEWIDTH + == paraSagittal.axisGridLineWidth() ) + ? sagittalLineWidth + : ( ( 0 <= paraSagittal.axisGridLineWidth() ) + ? paraSagittal.axisGridLineWidth() + : -1 * static_cast < int > ( paraSagittal.axisGridLineWidth() + * minSizeP1000 ) ); + + int circularLineWidth = 0 <= paraCircular.axisLineWidth() + ? paraCircular.axisLineWidth() + : -1 * static_cast < int > ( paraCircular.axisLineWidth() + * minSizeP1000 ); + ( ( KDChartAxisParams& ) paraCircular ).setAxisTrueLineWidth( circularLineWidth ); + int circularGridLineWidth + = ( KDCHART_AXIS_GRID_AUTO_LINEWIDTH + == paraCircular.axisGridLineWidth() ) + ? circularLineWidth + : ( ( 0 <= paraCircular.axisGridLineWidth() ) + ? paraCircular.axisGridLineWidth() + : -1 * static_cast < int > ( paraCircular.axisGridLineWidth() + * minSizeP1000 ) ); + + QFont actFont; + int labels = 0; + double currentRadiusPPU = position.height() / 2.0; + + // draw the "axis" circles + if( paraCircular.axisShowGrid() + || paraCircular.axisVisible() + || paraCircular.axisLabelsVisible() ) { + + double radiusPPU = maxValue * pixelsPerUnit; + double pDelimDelta = 0.0; + + // calculate label texts + QStringList* labelTexts = 0; + ((KDChartParams*)params())->setAxisArea( KDChartAxisParams::AxisPosCircular, + QRect( 0, + 0, + static_cast<int>( radiusPPU ), + static_cast<int>( radiusPPU ) ) ); + + double delimLen = 20.0 * minSizeP1000; // per mille of area + KDChartAxisParams::AxisPos basicPos; + QPoint orig, dest; + double dDummy; + double nSubDelimFactor = 0.0; + double nTxtHeight = 0.0; + double pTextsX = 0.0; + double pTextsY = 0.0; + double pTextsW = 0.0; + double pTextsH = 0.0; + int textAlign = Qt::AlignHCenter | Qt::AlignVCenter; + bool isLogarithmic = false; + bool isDateTime = false; + bool autoDtLabels = false; + QDateTime dtLow; + QDateTime dtHigh; + KDChartAxisParams::ValueScale dtDeltaScale; + KDChartAxesPainter::calculateLabelTexts( + painter, + *data, + *params(), + KDChartAxisParams::AxisPosCircular, + minSizeP1000, + delimLen, + // start of reference parameters + basicPos, + orig, + dest, + dDummy,dDummy,dDummy,dDummy, + nSubDelimFactor, + pDelimDelta, + nTxtHeight, + pTextsX, + pTextsY, + pTextsW, + pTextsH, + textAlign, + isLogarithmic, + isDateTime, + autoDtLabels, + dtLow, + dtHigh, + dtDeltaScale ); + labelTexts = ( QStringList* ) paraCircular.axisLabelTexts(); + if( paraCircular.axisLabelsVisible() ) { +//qDebug("\nnTxtHeight: "+QString::number(nTxtHeight)); + // calculate font size + actFont = paraCircular.axisLabelsFont(); + if ( paraCircular.axisLabelsFontUseRelSize() ) { +//qDebug("paraCircular.axisLabelsFontUseRelSize() is TRUE"); + actFont.setPointSizeFloat( nTxtHeight ); + } + QFontMetrics fm( actFont ); + QString strMax; + int maxLabelsWidth = 0; + for ( QStringList::Iterator it = labelTexts->begin(); + it != labelTexts->end(); + ++it ) { + if ( fm.width( *it ) > maxLabelsWidth ) { + maxLabelsWidth = fm.width( *it ); + strMax = *it; + } + } + while ( fm.width( strMax ) > pTextsW + && 6.0 < nTxtHeight ) { + nTxtHeight -= 0.5; + actFont.setPointSizeFloat( nTxtHeight ); + fm = QFontMetrics( actFont ); + } + painter->setFont( actFont ); + } + + double radiusDelta = pDelimDelta; + + labels = labelTexts + ? labelTexts->count() + : 0; + if( labels ) + currentRadiusPPU = -radiusDelta; + for( int iLabel = 0; iLabel < labels; ++iLabel ) { + //while( currentRadius < maxValue ) { + //double currentRadiusPPU = currentRadius; + currentRadiusPPU += radiusDelta; + double currentRadiusPPU2 = currentRadiusPPU * 2; + int circularAxisAngle = ( currentRadiusPPU != 0.0 ) ? ( static_cast < int > (4.0 * radiusPPU / currentRadiusPPU) ) : 0; + if( paraCircular.axisShowGrid() ) { + painter->setPen( QPen( paraCircular.axisGridColor(), + circularGridLineWidth ) ); + painter->drawEllipse( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ) ); + } + if( paraCircular.axisVisible() ) { + painter->setPen( QPen( paraCircular.axisLineColor(), + circularLineWidth ) ); + if( params()->polarDelimAtPos( KDChartEnums::PosTopCenter ) ) + painter->drawArc( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ), + (90 - circularAxisAngle/2) * 16, + circularAxisAngle * 16 ); + if( params()->polarDelimAtPos( KDChartEnums::PosBottomCenter ) ) + painter->drawArc( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ), + (270 - circularAxisAngle/2) * 16, + circularAxisAngle * 16 ); + + if( params()->polarDelimAtPos( KDChartEnums::PosCenterRight ) ) + painter->drawArc( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ), + (0 - circularAxisAngle/2) * 16, + circularAxisAngle * 16 ); + if( params()->polarDelimAtPos( KDChartEnums::PosCenterLeft ) ) + painter->drawArc( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ), + (180 - circularAxisAngle/2) * 16, + circularAxisAngle * 16 ); + + if( params()->polarDelimAtPos( KDChartEnums::PosTopRight ) ) + painter->drawArc( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ), + (45 - circularAxisAngle/2) * 16, + circularAxisAngle * 16 ); + if( params()->polarDelimAtPos( KDChartEnums::PosBottomLeft ) ) + painter->drawArc( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ), + (225 - circularAxisAngle/2) * 16, + circularAxisAngle * 16 ); + + if( params()->polarDelimAtPos( KDChartEnums::PosBottomRight ) ) + painter->drawArc( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ), + (315 - circularAxisAngle/2) * 16, + circularAxisAngle * 16 ); + if( params()->polarDelimAtPos( KDChartEnums::PosTopLeft ) ) + painter->drawArc( static_cast<int>( center.x() - currentRadiusPPU ), + static_cast<int>( center.y() - currentRadiusPPU ), + static_cast<int>( currentRadiusPPU2 ), static_cast<int>( currentRadiusPPU2 ), + (135 - circularAxisAngle/2) * 16, + circularAxisAngle * 16 ); + } + if( paraCircular.axisLabelsVisible() ) { + const bool rotate = params()->polarRotateCircularLabels(); + painter->setPen( QPen( paraCircular.axisLabelsColor(), + circularLineWidth ) ); + const QString& txt = (*labelTexts)[ iLabel ]; + if( params()->polarLabelsAtPos( KDChartEnums::PosTopCenter ) ) + paintCircularAxisLabel( painter, rotate, 90, center, currentRadiusPPU, txt, + Qt::AlignBottom | Qt::AlignHCenter, iLabel ); + + if( params()->polarLabelsAtPos( KDChartEnums::PosBottomCenter ) ) + paintCircularAxisLabel( painter, rotate, 270, center, currentRadiusPPU, txt, + Qt::AlignTop | Qt::AlignHCenter, iLabel ); + + if( params()->polarLabelsAtPos( KDChartEnums::PosCenterRight ) ) + paintCircularAxisLabel( painter, rotate, 0, center, currentRadiusPPU, txt, + Qt::AlignVCenter | Qt::AlignRight, iLabel ); + + if( params()->polarLabelsAtPos( KDChartEnums::PosCenterLeft ) ) + paintCircularAxisLabel( painter, rotate, 180, center, currentRadiusPPU, txt, + Qt::AlignVCenter | Qt::AlignLeft, iLabel ); + + if( params()->polarLabelsAtPos( KDChartEnums::PosTopRight ) ) + paintCircularAxisLabel( painter, rotate, 45, center, currentRadiusPPU, txt, + Qt::AlignBottom | Qt::AlignRight, iLabel ); + + if( params()->polarLabelsAtPos( KDChartEnums::PosBottomLeft ) ) + paintCircularAxisLabel( painter, rotate, 225, center, currentRadiusPPU, txt, + Qt::AlignTop | Qt::AlignLeft, iLabel ); + + if( params()->polarLabelsAtPos( KDChartEnums::PosBottomRight ) ) + paintCircularAxisLabel( painter, rotate, 315, center, currentRadiusPPU, txt, + Qt::AlignTop | Qt::AlignRight, iLabel ); + + if( params()->polarLabelsAtPos( KDChartEnums::PosTopLeft ) ) + paintCircularAxisLabel( painter, rotate, 135, center, currentRadiusPPU, txt, + Qt::AlignBottom | Qt::AlignLeft, iLabel ); + } + } + } + + + double circularSpan = params()->polarChartSubType() == KDChartParams::PolarPercent + ? 100.0 + : paraCircular.trueAxisHigh() - paraCircular.trueAxisLow(); + double radius = currentRadiusPPU; + if( !labels + || params()->polarChartSubType() == KDChartParams::PolarPercent ) + radius = (position.width() / 2.0) * 1000.0 / 1250.0; + + if( params()->polarChartSubType() != KDChartParams::PolarPercent ) + pixelsPerUnit = labels ? currentRadiusPPU / circularSpan + : (position.height() / maxValue / 2.0) * 1000.0 / 1250.0; + else + pixelsPerUnit = (position.height() / 100.0 / 2.0) * 1000.0 / 1250.0; + + // draw the sagittal grid and axis lines + if( paraSagittal.axisShowGrid() + || paraSagittal.axisVisible() + || paraSagittal.axisLabelsVisible() ) { + + // calculate label texts + QStringList* labelTexts = 0; + bool onlyDefaultLabels = true; + if( paraSagittal.axisLabelsVisible() ) { + ((KDChartParams*)params())->setAxisArea( KDChartAxisParams::AxisPosSagittal, + QRect( 0, + 0, + static_cast < int > ( 2.0 * M_PI * radius ), + static_cast < int > ( 0.5 * radius ) ) ); + double delimLen = 20.0 * minSizeP1000; // per mille of area + KDChartAxisParams::AxisPos basicPos; + QPoint orig, dest; + double dDummy; + double nSubDelimFactor = 0.0; + double pDelimDelta = 0.0; + double nTxtHeight = 0.0; + double pTextsX = 0.0; + double pTextsY = 0.0; + double pTextsW = 0.0; + double pTextsH = 0.0; + int textAlign = Qt::AlignCenter; + bool isLogarithmic = false; + bool isDateTime = false; + bool autoDtLabels = false; + QDateTime dtLow; + QDateTime dtHigh; + KDChartAxisParams::ValueScale dtDeltaScale; + KDChartAxesPainter::calculateLabelTexts( + painter, + *data, + *params(), + KDChartAxisParams::AxisPosSagittal, + minSizeP1000, + delimLen, + // start of reference parameters + basicPos, + orig, + dest, + dDummy,dDummy,dDummy,dDummy, + nSubDelimFactor, + pDelimDelta, + nTxtHeight, + pTextsX, + pTextsY, + pTextsW, + pTextsH, + textAlign, + isLogarithmic, + isDateTime, + autoDtLabels, + dtLow, + dtHigh, + dtDeltaScale ); + labelTexts = ( QStringList* ) paraSagittal.axisLabelTexts(); + // calculate font size + actFont = paraSagittal.axisLabelsFont(); + if ( paraSagittal.axisLabelsFontUseRelSize() ) { + actFont.setPointSizeFloat( nTxtHeight ); + } + QFontMetrics fm( actFont ); + QString strMax; + int maxLabelsWidth = 0; + for ( QStringList::Iterator it = labelTexts->begin(); + it != labelTexts->end(); + ++it ) { + if ( fm.width( *it ) > maxLabelsWidth ) { + maxLabelsWidth = fm.width( *it ); + strMax = *it; + } + if ( !(*it).startsWith( "Item ") ) + onlyDefaultLabels = false; + } + while ( fm.width( strMax ) > pTextsW && 6.0 < nTxtHeight ) { + nTxtHeight -= 0.5; + actFont.setPointSizeFloat( nTxtHeight ); + fm = QFontMetrics( actFont ); + } + painter->setFont( actFont ); + } + + int currentAngle = params()->polarZeroDegreePos(); + if( -360 > currentAngle + || 360 < currentAngle ) + currentAngle = 0; + if( 0 > currentAngle ) + currentAngle += 360; + int r1 = static_cast < int > ( radius * 1050 / 1000 ); + int r2 = static_cast < int > ( radius * 1100 / 1000 ); + int r3 = static_cast < int > ( radius * 1175 / 1000 ); + QPoint pt1, pt2, pt3; + uint nLabels = labelTexts->count(); + int angleBetweenRays = 360 / nLabels; + for( uint value = 0; value < nLabels; ++value ) { + pt1 = center + polarToXY( r1, currentAngle ); + pt2 = center + polarToXY( r2, currentAngle ); + pt3 = center + polarToXY( r3, currentAngle ); + + //pt3 = painter->worldMatrix().map( pt3 ); + + if( paraSagittal.axisShowGrid() ) { + painter->setPen( QPen( paraSagittal.axisGridColor(), + sagittalGridLineWidth ) ); + painter->drawLine( center, pt1 ); + } + if( paraSagittal.axisVisible() ) { + painter->setPen( QPen( paraSagittal.axisLineColor(), + sagittalLineWidth ) ); + painter->drawLine( pt1, pt2 ); + } + if( paraSagittal.axisLabelsVisible() + && labelTexts + && labelTexts->count() > value ) { + painter->setPen( QPen( paraSagittal.axisLabelsColor(), + sagittalLineWidth ) ); + QString label( onlyDefaultLabels + ? QString::number( currentAngle ) + : (*labelTexts)[ value ] ); + + KDDrawText::drawRotatedText( painter, + currentAngle+90, + painter->worldMatrix().map(pt3), + label, + 0, + Qt::AlignCenter ); + } + currentAngle += angleBetweenRays; + } + } + + + // Now draw the data + int dataLinesWidth = 0 <= params()->polarLineWidth() + ? params()->polarLineWidth() + : -1 * static_cast < int > ( params()->polarLineWidth() + * minSizeP1000 ); + painter->setBrush( Qt::NoBrush ); + for ( unsigned int dataset = datasetStart; dataset <= datasetEnd; dataset++ ) { + painter->setPen( QPen( params()->dataColor( dataset ), + dataLinesWidth ) ); + QPointArray points( numValues ); + int totalPoints = 0; + double valueTotal = 0.0; // Will only be used for Percent + int angleBetweenRays = 360 / numValues; + QVariant vValY; + for ( int value = 0; value < numValues; value++ ) { + if( params()->polarChartSubType() == KDChartParams::PolarPercent ) + valueTotal = data->colAbsSum( value ); + // the value determines the angle, the dataset only the color + if( data->cellCoord( dataset, value, vValY, 1 ) && + QVariant::Double == vValY.type() ){ + const double cellValue = vValY.toDouble(); + double drawValue; + if ( params()->polarChartSubType() == KDChartParams::PolarStacked ) + drawValue = ( cellValue + currentValueSums[ value ] ) * pixelsPerUnit; + else if( params()->polarChartSubType() == KDChartParams::PolarPercent ) { + drawValue = ( ( cellValue + currentValueSums[ value ] ) + / valueTotal * static_cast<double>( radius ) ); + } else + drawValue = cellValue * pixelsPerUnit; + + // record the point for drawing the polygon later + int drawAngle = value * angleBetweenRays; + QPoint drawPoint( center + polarToXY( static_cast<int>( drawValue ), + drawAngle ) ); + points.setPoint( totalPoints, drawPoint ); + totalPoints++; + KDChartDataRegion* datReg = 0; + // the marker can be drawn now + if( params()->polarMarker() ) { + int xsize = params()->polarMarkerSize().width(); + int ysize = params()->polarMarkerSize().height(); + datReg = drawMarker( painter, + params(), + _areaWidthP1000, _areaHeightP1000, + _dataRect.x(), _dataRect.y(), + params()->polarMarkerStyle( dataset ), + params()->dataColor( dataset ), + drawPoint, + dataset, value, chart, + regions, + xsize ? &xsize : 0, + ysize ? &ysize : 0 ); + painter->setPen( QPen( params()->dataColor( dataset ), + dataLinesWidth ) ); + } + if ( regions ) { + bool bMustAppendDatReg = 0 == datReg; + if( bMustAppendDatReg ){ + QRect rect( QPoint( drawPoint.x() - 1, + drawPoint.y() - 1 ), + QSize( 3, 3 ) ); + datReg = new KDChartDataRegion( dataset, + value, + chart, + rect ); + } + datReg->points[ KDChartEnums::PosTopLeft ] = + drawPoint + _dataRect.topLeft(); + + datReg->points[ KDChartEnums::PosTopCenter ] = + datReg->points[ KDChartEnums::PosTopLeft ]; + datReg->points[ KDChartEnums::PosTopRight ] = + datReg->points[ KDChartEnums::PosTopLeft ]; + datReg->points[ KDChartEnums::PosBottomLeft ] = + datReg->points[ KDChartEnums::PosTopLeft ]; + datReg->points[ KDChartEnums::PosBottomCenter ] = + datReg->points[ KDChartEnums::PosTopLeft ]; + datReg->points[ KDChartEnums::PosBottomRight ] = + datReg->points[ KDChartEnums::PosTopLeft ]; + datReg->points[ KDChartEnums::PosCenterLeft ] = + datReg->points[ KDChartEnums::PosTopLeft ]; + datReg->points[ KDChartEnums::PosCenter ] = + datReg->points[ KDChartEnums::PosTopLeft ]; + datReg->points[ KDChartEnums::PosCenterRight ] = + datReg->points[ KDChartEnums::PosTopLeft ]; + /* + // test the center positions: + painter->drawEllipse( datReg->points[ KDChartEnums::PosCenterLeft ].x() - 2, + datReg->points[ KDChartEnums::PosCenterLeft ].y() - 2, 5, 5); + */ + datReg->startAngle = drawAngle; + datReg->angleLen = drawAngle; + if( bMustAppendDatReg ) + regions->append( datReg ); + } + // calculate running sum for stacked and percent + if ( params()->polarChartSubType() == KDChartParams::PolarStacked || + params()->polarChartSubType() == KDChartParams::PolarPercent ) + currentValueSums[ value ] += cellValue; + } + } + painter->drawPolygon( points ); + } + + painter->translate( -_dataRect.x(), -_dataRect.y() ); +} + + +/* + Helper methode being called by KDChartPolarPainter::paintData() +*/ +void KDChartPolarPainter::paintCircularAxisLabel( QPainter* painter, + bool rotate, + int txtAngle, + QPoint center, + double currentRadiusPPU, + const QString& txt, + int align, + int step ) +{ + if( !rotate && (0 != (align & (Qt::AlignLeft | Qt::AlignRight) ) ) ) + currentRadiusPPU += center.x()*0.01; + KDDrawText::drawRotatedText( + painter, + rotate ? txtAngle - 90 : 0, + painter->worldMatrix().map(center - polarToXY( static_cast<int>( currentRadiusPPU ), txtAngle )), + txt, + 0, + step + ? (rotate ? Qt::AlignBottom | Qt::AlignHCenter : align) + : Qt::AlignCenter, + false,0,false, + false ); +} + + +/*! + Draws the marker for one data point according to the specified style. + + \param painter the painter to draw on + \param style what kind of marker is drawn (square, diamond or circle) + \param color the color in which to draw the marker + \param p the center of the marker + \param dataset the dataset which this marker represents + \param value the value which this marker represents + \param regions a list of regions for data points, a new region for the new + marker will be appended to this list if it is not 0 +*//* +void KDChartPolarPainter::drawMarker( QPainter* painter, + KDChartParams::PolarMarkerStyle style, + const QColor& color, + const QPoint& p, + uint, //dataset, + uint, //value, + uint, //chart, + double minSizeP1000, + QRegion& region ) +{ + int xsize = params()->polarMarkerSize().width(); + if ( 0 > xsize ) + xsize = -1 * static_cast < int > ( xsize * minSizeP1000 ); + int ysize = params()->polarMarkerSize().height(); + if ( 0 > ysize ) + ysize = -1 * static_cast < int > ( ysize * minSizeP1000 ); + int xsize2 = xsize / 2; + int ysize2 = ysize / 2; + painter->setPen( color ); + switch ( style ) { + case KDChartParams::PolarMarkerSquare: { + painter->save(); + painter->setBrush( color ); + QRect rect( QPoint( p.x() - xsize2, p.y() - ysize2 ), QPoint( p.x() + xsize2, p.y() + ysize2 ) ); + painter->drawRect( rect ); + // Don't use rect for drawing after this! + rect.moveBy( _dataRect.x(), _dataRect.y() ); + region = QRegion( rect ); + painter->restore(); + break; + } + case KDChartParams::PolarMarkerDiamond: { + painter->save(); + painter->setBrush( color ); + QPointArray points( 4 ); + points.setPoint( 0, p.x() - xsize2, p.y() ); + points.setPoint( 1, p.x(), p.y() - ysize2 ); + points.setPoint( 2, p.x() + xsize2, p.y() ); + points.setPoint( 3, p.x(), p.y() + ysize2 ); + painter->drawPolygon( points ); + // Don't use points for drawing after this! + points.translate( _dataRect.x(), _dataRect.y() ); + region = QRegion( points ); + painter->restore(); + break; + } + case KDChartParams::PolarMarkerCircle: + default: { + painter->save(); + painter->setBrush( color ); + painter->drawEllipse( p.x() - xsize2, p.y() - ysize2, xsize, ysize ); + QPointArray points; + points.makeEllipse( p.x() - xsize2, p.y() - ysize2, xsize, ysize ); + // Don't use points for drawing after this! + points.translate( _dataRect.x(), _dataRect.y() ); + if( points.size() > 0 ) + region = QRegion( points ); + else + region = QRegion(); + painter->restore(); + } + }; +}*/ + +#define DEGTORAD(d) (d)*M_PI/180 + +QPoint KDChartPolarPainter::polarToXY( int radius, int angle ) +{ + double anglerad = DEGTORAD( static_cast<double>( angle ) ); + QPoint ret( static_cast<int>( cos( anglerad ) * radius ), + static_cast<int>( sin( anglerad ) * radius ) ); + return ret; +} + + +/** + This method is a specialization that returns a fallback legend text + appropriate for polar charts where the fallbacks should come from + the values, not from the datasets. + + 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 KDChartPolarPainter::fallbackLegendText( uint dataset ) const +{ + return QObject::tr( "Series " ) + 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 KDChartPolarPainter::numLegendFallbackTexts( KDChartTableDataBase* data ) const +{ + return data->usedRows(); +} diff --git a/kchart/kdchart/KDChartPolarPainter.h b/kchart/kdchart/KDChartPolarPainter.h new file mode 100644 index 00000000..52a264a6 --- /dev/null +++ b/kchart/kdchart/KDChartPolarPainter.h @@ -0,0 +1,78 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTPOLARPAINTER_H__ +#define __KDCHARTPOLARPAINTER_H__ + +#include "KDChartPainter.h" +#include <KDChartTable.h> +#include <KDChartParams.h> + +class KDChartPolarPainter : public KDChartPainter +{ + friend class KDChartPainter; + + protected: + KDChartPolarPainter( KDChartParams* params ); + virtual ~KDChartPolarPainter(); + + virtual void paintData( QPainter* painter, + KDChartTableDataBase* data, + bool paint2nd, + KDChartDataRegionList* regions = 0 ); +/* void drawMarker( QPainter* painter, + KDChartParams::PolarMarkerStyle style, + const QColor& color, const QPoint& p, + uint dataset, uint value, uint chart, + double minSizeP1000, + QRegion & region );*/ + virtual QString fallbackLegendText( uint dataset ) const; + virtual uint numLegendFallbackTexts( KDChartTableDataBase* data ) const; + + private: + QPoint polarToXY( int radius, int angle ); + void paintAxisLabels( QPainter* painter, + QStringList& labelTexts, + double radiusDelta, + const QRect& position, + const QPoint& center, + double radiusPPU, + double& currentRadiusPPU, + const KDChartAxisParams & paraCircular, + double minSizeP1000 ); + void paintCircularAxisLabel( QPainter* painter, + bool rotate, + int txtAngle, + QPoint center, + double currentRadiusPPU, + const QString& txt, + int align, + int step ); +}; + +#endif diff --git a/kchart/kdchart/KDChartPropertySet.cpp b/kchart/kdchart/KDChartPropertySet.cpp new file mode 100644 index 00000000..6abec756 --- /dev/null +++ b/kchart/kdchart/KDChartPropertySet.cpp @@ -0,0 +1,384 @@ +/* -*- 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. + ** + **********************************************************************/ + +#include "KDChartPropertySet.h" +#include "KDChartParams.h" +#include <KDXMLTools.h> + +#include <qpainter.h> +#include <qvaluestack.h> + +#include <stdlib.h> + +/** + \file KDChartPropertySet.cpp + + \brief Implementation of property settings of a single KDChart data cell. + */ + +void KDChartPropertySet::deepCopy( const KDChartPropertySet* source ) +{ + if( !source || this == source ) + return; + mOwnID = source->mOwnID; + mName = source->mName; + mIdShowBar = source->mIdShowBar; mShowBar = source->mShowBar; + mIdBarColor = source->mIdBarColor; mBarColor = source->mBarColor; + mIdAreaBrush = source->mIdAreaBrush; mAreaBrush = source->mAreaBrush; + mIdLineWidth = source->mIdLineWidth; mLineWidth = source->mLineWidth; + mIdLineColor = source->mIdLineColor; mLineColor = source->mLineColor; + mIdLineStyle = source->mIdLineStyle; mLineStyle = source->mLineStyle; + mIdShowMarker = source->mIdShowMarker; mShowMarker = source->mShowMarker; + mIdMarkerSize = source->mIdMarkerSize; mMarkerSize = source->mMarkerSize; + mIdMarkerColor = source->mIdMarkerColor; mMarkerColor = source->mMarkerColor; + mIdMarkerStyle = source->mIdMarkerStyle; mMarkerStyle = source->mMarkerStyle; + mIdMarkerAlign = source->mIdMarkerAlign; mMarkerAlign = source->mMarkerAlign; + mIdExtraLinesAlign = source->mIdExtraLinesAlign; mExtraLinesAlign = source->mExtraLinesAlign; + mIdExtraLinesInFront = source->mIdExtraLinesInFront; mExtraLinesInFront = source->mExtraLinesInFront; + mIdExtraLinesLength = source->mIdExtraLinesLength; mExtraLinesLength = source->mExtraLinesLength; + mIdExtraLinesWidth = source->mIdExtraLinesWidth; mExtraLinesWidth = source->mExtraLinesWidth; + mIdExtraLinesColor = source->mIdExtraLinesColor; mExtraLinesColor = source->mExtraLinesColor; + mIdExtraLinesStyle = source->mIdExtraLinesStyle; mExtraLinesStyle = source->mExtraLinesStyle; + mIdExtraMarkersAlign = source->mIdExtraMarkersAlign; mExtraMarkersAlign = source->mExtraMarkersAlign; + mIdExtraMarkersSize = source->mIdExtraMarkersSize; mExtraMarkersSize = source->mExtraMarkersSize; + mIdExtraMarkersColor = source->mIdExtraMarkersColor; mExtraMarkersColor = source->mExtraMarkersColor; + mIdExtraMarkersStyle = source->mIdExtraMarkersStyle; mExtraMarkersStyle = source->mExtraMarkersStyle; +} + +const KDChartPropertySet* KDChartPropertySet::clone() const +{ + KDChartPropertySet* newSet = new KDChartPropertySet(); + newSet->deepCopy( this ); + return newSet; +} + +void KDChartPropertySet::quickReset( const QString& name, int idParent ) +{ + // set the name + mName = name; + // fill with idParent + mOwnID = idParent; + mIdLineWidth = idParent; + mIdLineColor = idParent; + mIdLineStyle = idParent; + mIdShowMarker = idParent; + mIdMarkerSize = idParent; + mIdMarkerColor = idParent; + mIdMarkerStyle = idParent; + mIdMarkerAlign = idParent; + mIdExtraLinesAlign = idParent; + mIdExtraLinesInFront = idParent; + mIdExtraLinesLength = idParent; + mIdExtraLinesWidth = idParent; + mIdExtraLinesColor = idParent; + mIdExtraLinesStyle = idParent; + mIdExtraMarkersAlign = idParent; + mIdExtraMarkersSize = idParent; + mIdExtraMarkersColor = idParent; + mIdExtraMarkersStyle = idParent; + mIdShowBar = idParent; + mIdBarColor = idParent; + mIdAreaBrush = idParent; +} + +void KDChartPropertySet::fullReset( const QString& name, int idParent ) +{ + quickReset( name, idParent ); + fillValueMembersWithDummyValues(); +} + +void KDChartPropertySet::fillValueMembersWithDummyValues() +{ + // fill with dummy values to avoid problems when saving us into a stream + mLineWidth = 1; + mLineColor = Qt::black; + mLineStyle = Qt::SolidLine; + mShowMarker = true; + mMarkerAlign = Qt::AlignCenter; + mMarkerSize = QSize(6,6); + mMarkerColor = Qt::black; + mMarkerStyle = 0; + mExtraLinesAlign = Qt::AlignLeft|Qt::AlignTop; + mExtraLinesInFront = false; + mExtraLinesLength = -20; + mExtraLinesWidth = 1; + mExtraLinesColor = Qt::black; + mExtraLinesStyle = Qt::SolidLine; + mExtraMarkersAlign = Qt::AlignLeft|Qt::AlignTop; + mExtraMarkersSize = QSize(6,6); + mExtraMarkersColor = Qt::black; + mExtraMarkersStyle = 0; + mShowBar = true; + mBarColor = Qt::black; + mAreaBrush = QBrush( Qt::blue ); +} + + +QDomElement KDChartPropertySet::saveXML(QDomDocument& doc) const +{ + QDomElement propertySetElement = doc.createElement( "PropertySet" ); + propertySetElement.setAttribute( "OwnID", mOwnID ); + KDXML::createStringNode( doc, propertySetElement, "Name", mName ); + // normal bar properties: + KDXML::createIntNode( doc, propertySetElement, "IDShowBar", mIdShowBar ); + KDXML::createBoolNode( doc, propertySetElement, "ShowBar", mShowBar ); + KDXML::createIntNode( doc, propertySetElement, "IDBarColor", mIdBarColor ); + KDXML::createColorNode( doc, propertySetElement, "BarColor", mBarColor ); + // normal area properties: + KDXML::createIntNode( doc, propertySetElement, "IDAreaBrush", mIdAreaBrush ); + KDXML::createBrushNode( doc, propertySetElement, "AreaBrush", mAreaBrush ); + // normal line properties: + KDXML::createIntNode( doc, propertySetElement, "IDLineWidth", mIdLineWidth ); + KDXML::createIntNode( doc, propertySetElement, "LineWidth", mLineWidth ); + KDXML::createIntNode( doc, propertySetElement, "IDLineColor", mIdLineColor ); + KDXML::createColorNode( doc, propertySetElement, "LineColor", mLineColor ); + KDXML::createIntNode( doc, propertySetElement, "IDLineStyle", mIdLineStyle ); + QDomElement lineStyleElement = doc.createElement( "LineStyle" ); + propertySetElement.appendChild( lineStyleElement ); + lineStyleElement.setAttribute( "Style", KDXML::penStyleToString( mLineStyle)); + // normal marker properties: + KDXML::createIntNode( doc, propertySetElement, "IDShowMarker", mIdShowMarker); + KDXML::createBoolNode( doc, propertySetElement, "ShowMarker", mShowMarker); + KDXML::createIntNode( doc, propertySetElement, "IDMarkerAlign", mIdMarkerAlign ); + KDXML::createIntNode( doc, propertySetElement, "MarkerAlign", mMarkerAlign ); + KDXML::createIntNode( doc, propertySetElement, "IDMarkerSize", mIdMarkerSize ); + KDXML::createSizeNode( doc, propertySetElement, "MarkerSize", mMarkerSize ); + KDXML::createIntNode( doc, propertySetElement, "IDMarkerColor", mIdMarkerColor ); + KDXML::createColorNode( doc, propertySetElement, "MarkerColor", mMarkerColor ); + KDXML::createIntNode( doc, propertySetElement, "IDMarkerStyle", mIdMarkerStyle ); + QDomElement markerStElem = doc.createElement( "MarkerStyle" ); + propertySetElement.appendChild( markerStElem ); + markerStElem.setAttribute("Style", + KDChartParams::lineMarkerStyleToString( (KDChartParams::LineMarkerStyle)mMarkerStyle)); + // extra lines: + KDXML::createIntNode( doc, propertySetElement, "IDExtraLinesAlign", mIdExtraLinesAlign ); + KDXML::createIntNode( doc, propertySetElement, "ExtraLinesAlign", mExtraLinesAlign ); + KDXML::createIntNode( doc, propertySetElement, "IDExtraLinesInFront",mIdExtraLinesInFront ); + KDXML::createBoolNode( doc, propertySetElement, "ExtraLinesInFront", mExtraLinesInFront ); + KDXML::createIntNode( doc, propertySetElement, "IDExtraLinesLength", mIdExtraLinesLength ); + KDXML::createIntNode( doc, propertySetElement, "ExtraLinesLength", mExtraLinesLength ); + KDXML::createIntNode( doc, propertySetElement, "IDExtraLinesWidth", mIdExtraLinesWidth ); + KDXML::createIntNode( doc, propertySetElement, "ExtraLinesWidth", mExtraLinesWidth ); + KDXML::createIntNode( doc, propertySetElement, "IDExtraLinesColor", mIdExtraLinesColor ); + KDXML::createColorNode( doc, propertySetElement, "ExtraLinesColor", mExtraLinesColor ); + KDXML::createIntNode( doc, propertySetElement, "IDExtraLinesStyle", mIdExtraLinesStyle ); + QDomElement specLineStElem = doc.createElement( "ExtraLinesStyle" ); + propertySetElement.appendChild( specLineStElem ); + specLineStElem.setAttribute( "Style", KDXML::penStyleToString( mExtraLinesStyle)); + // extra markers: + KDXML::createIntNode( doc, propertySetElement, "IDExtraMarkersAlign", mIdExtraMarkersAlign ); + KDXML::createIntNode( doc, propertySetElement, "ExtraMarkersAlign", mExtraMarkersAlign ); + KDXML::createIntNode( doc, propertySetElement, "IDExtraMarkersSize", mIdExtraMarkersSize ); + KDXML::createSizeNode( doc, propertySetElement, "ExtraMarkersSize", mExtraMarkersSize ); + KDXML::createIntNode( doc, propertySetElement, "IDExtraMarkersColor", mIdExtraMarkersColor ); + KDXML::createColorNode( doc, propertySetElement, "ExtraMarkersColor", mExtraMarkersColor ); + KDXML::createIntNode( doc, propertySetElement, "IDExtraMarkersStyle", mIdExtraMarkersStyle ); + QDomElement specMarkerStElem = doc.createElement( "ExtraMarkersStyle" ); + propertySetElement.appendChild( specMarkerStElem ); + specMarkerStElem.setAttribute("Style", + KDChartParams::lineMarkerStyleToString( (KDChartParams::LineMarkerStyle)mExtraMarkersStyle)); + return propertySetElement; +} + +bool KDChartPropertySet::loadXML( const QDomElement& element, KDChartPropertySet& set ) +{ + bool bOwnIDFound = false; + QString s; + QColor color; + QBrush brush; + QSize size; + bool bValue; + int i; + // initialize the property set with default values + set.fillValueMembersWithDummyValues(); + // parse the element + if( element.hasAttribute("OwnID") ){ + i = element.attribute( "OwnID" ).toInt( &bOwnIDFound ); + if( bOwnIDFound ){ + set.mOwnID = i; + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Name" ) { + if( KDXML::readStringNode( element, s ) ) + set.mName = s; + } else + // normal bar properties: + if( tagName == "IDShowBar" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdShowBar = i; + } else if( tagName == "ShowBar" ) { + if( KDXML::readBoolNode( element, bValue ) ) + set.mShowBar = bValue; + } else if( tagName == "IDBarColor" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdBarColor = i; + } else if( tagName == "BarColor" ) { + if( KDXML::readColorNode( element, color ) ) + set.mBarColor = color; + } else + // normal area properties: + if( tagName == "IDAreaBrush" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdAreaBrush = i; + } else if( tagName == "AreaBrush" ) { + if( KDXML::readBrushNode( element, brush ) ) + set.mAreaBrush = color; + } else + // normal line properties: + if( tagName == "IDLineWidth" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdLineWidth = i; + } else if( tagName == "LineWidth" ) { + if( KDXML::readIntNode( element, i ) ) + set.mLineWidth = i; + } else if( tagName == "IDLineColor" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdLineColor = i; + } else if( tagName == "LineColor" ) { + if( KDXML::readColorNode( element, color ) ) + set.mLineColor = color; + } else if( tagName == "IDLineStyle" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdLineStyle = i; + } else if( tagName == "LineStyle" ) { + if( element.hasAttribute( "Style" ) ) + set.mLineStyle = KDXML::stringToPenStyle( element.attribute( "Style" ) ); + } else + // normal marker properties: + if( tagName == "IDShowMarker" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdShowMarker = i; + } else if( tagName == "ShowMarker" ) { + if( KDXML::readBoolNode( element, bValue ) ) + set.mShowMarker = bValue; + } else if( tagName == "IDMarkerAlign" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdMarkerAlign = i; + } else if( tagName == "MarkerAlign" ) { + if( KDXML::readIntNode( element, i ) ) + set.mMarkerAlign = i; + } else if( tagName == "IDMarkerSize" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdMarkerSize = i; + } else if( tagName == "MarkerSize" ) { + if( KDXML::readSizeNode( element, size ) ) + set.mMarkerSize = size; + } else if( tagName == "IDMarkerColor" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdMarkerColor = i; + } else if( tagName == "MarkerColor" ) { + if( KDXML::readColorNode( element, color ) ) + set.mMarkerColor = color; + } else if( tagName == "IDMarkerStyle" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdMarkerStyle = i; + } else if( tagName == "MarkerStyle" ) { + if( element.hasAttribute( "Style" ) ) + set.mMarkerStyle + = KDChartParams::stringToLineMarkerStyle( element.attribute( "Style" ) ); + } else + // extra lines: + if( tagName == "IDExtraLinesAlign" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdExtraLinesAlign = i; + } else if( tagName == "ExtraLinesAlign" ) { + if( KDXML::readIntNode( element, i ) ) + set.mExtraLinesAlign = i; + } else if( tagName == "IDExtraLinesInFront" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdExtraLinesInFront = i; + } else if( tagName == "ExtraLinesInFront" ) { + if( KDXML::readBoolNode( element, bValue ) ) + set.mExtraLinesInFront = bValue; + } else if( tagName == "IDExtraLinesLength" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdExtraLinesLength = i; + } else if( tagName == "ExtraLinesLength" ) { + if( KDXML::readIntNode( element, i ) ) + set.mExtraLinesLength = i; + } else if( tagName == "IDExtraLinesWidth" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdExtraLinesWidth = i; + } else if( tagName == "ExtraLinesWidth" ) { + if( KDXML::readIntNode( element, i ) ) + set.mExtraLinesWidth = i; + } else if( tagName == "IDExtraLinesColor" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdExtraLinesColor = i; + } else if( tagName == "ExtraLinesColor" ) { + if( KDXML::readColorNode( element, color ) ) + set.mExtraLinesColor = color; + } else if( tagName == "IDExtraLinesStyle" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdExtraLinesStyle = i; + } else if( tagName == "ExtraLinesStyle" ) { + if( element.hasAttribute( "Style" ) ) + set.mExtraLinesStyle = KDXML::stringToPenStyle( element.attribute( "Style" ) ); + } else + // extra markers: + if( tagName == "IDExtraMarkersAlign" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdExtraMarkersAlign = i; + } else if( tagName == "ExtraMarkersAlign" ) { + if( KDXML::readIntNode( element, i ) ) + set.mExtraMarkersAlign = i; + } else if( tagName == "IDExtraMarkersSize" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdExtraMarkersSize = i; + } else if( tagName == "ExtraMarkersSize" ) { + if( KDXML::readSizeNode( element, size ) ) + set.mExtraMarkersSize = size; + } else if( tagName == "IDExtraMarkersColor" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdExtraMarkersColor = i; + } else if( tagName == "ExtraMarkersColor" ) { + if( KDXML::readColorNode( element, color ) ) + set.mExtraMarkersColor = color; + } else if( tagName == "IDExtraMarkersStyle" ) { + if( KDXML::readIntNode( element, i ) ) + set.mIdExtraMarkersStyle = i; + } else if( tagName == "ExtraMarkersStyle" ) { + if( element.hasAttribute( "Style" ) ) + set.mExtraMarkersStyle + = KDChartParams::stringToLineMarkerStyle( element.attribute( "Style" ) ); + } else { + qDebug( "Unknown subelement of KDChartPropertySet found: %s", tagName.latin1() ); + } + } + node = node.nextSibling(); + } + } + } + return bOwnIDFound; +} + +#include "KDChartPropertySet.moc" diff --git a/kchart/kdchart/KDChartPropertySet.h b/kchart/kdchart/KDChartPropertySet.h new file mode 100644 index 00000000..306109cd --- /dev/null +++ b/kchart/kdchart/KDChartPropertySet.h @@ -0,0 +1,985 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTPROPERTYSET__ +#define __KDCHARTPROPERTYSET__ + + +#include <KDChartEnums.h> +#include <kdchart_export.h> +#include <qdom.h> +#include <qcolor.h> +#include <qbrush.h> + +/** + \file KDChartPropertySet.h + + \brief Property settings of a single KDChart data cell. + */ + +/** + \class KDChartPropertySet KDChartPropertySet.h + \brief Property settings of a single KDChart data cell. + + \note The property settings feature currently is supported by + normal (== not 3-dimensional!) Line Charts only. Future releases + of KDChart will offer a rich set of options to use cell-specific + properties for other chart types as well... + + The KDChartPropertySet class stores several settings to be + used by the painter classes to decide how to visualize the + respective data cell. + Each of the settings may be specified either by giving a + value for it or by using the ID of another KDChartPropertySet +instance: If the later the value of that instance is used +instead of the value that is stored locally. + +\sa KDChartData::setPropertySet +\sa KDCHART_PROPSET_NORMAL_DATA, KDCHART_PROPSET_TRANSPARENT_DATA +\sa KDChartParams::registerProperties +*/ +class KDCHART_EXPORT KDChartPropertySet :public QObject +{ + Q_OBJECT + Q_ENUMS( SpecialDataPropertyID ) + + // Required by QSA + Q_ENUMS( PenStyle ) + + friend class KDChartParams; + + public: + /** + Special property IDs. + + \li \c UndefinedID (the default value) + used to indicate that <b>no</b> ID was set for + members \c mIdLineWidth, \c mIdLineColor, + \c mIdLineStyle, \c mIdShowMarker and the like... + + \li \c OwnID used for referencing a property set's *own* ID. + + \sa KDChartPropertySet + */ + enum SpecialDataPropertyID { + UndefinedID = -2, + OwnID = -1 + }; + + /** + default constructor setting all values to undefined + and name to empty string + */ + KDChartPropertySet() : + mOwnID( UndefinedID ) + { fullReset(""); } + + + /** + Constructor setting all Property Set IDs to the same ID value. + + This constructor may be used to initialize a property set + and let it have all property IDs set to a specific value, + e.g. you might pass KDCHART_PROPSET_NORMAL_DATA + as ID to make the default property set the parent of all + values. + + \param name (may be empty) a name describing this property set. + \param idParent the ID of the parent property set. Skip this paramter + to define a property set without specifying a parent. + */ + KDChartPropertySet( const QString& name, int idParent=KDChartPropertySet::UndefinedID ) : + mOwnID( UndefinedID ) + { fullReset( name, idParent ); } + + + /** + Copy the settings stored by property set \c source into this property set. + + \note Use this method instead of using the assignment operator. + + \sa clone, quickReset, fullReset + */ + void deepCopy( const KDChartPropertySet* source ); + + + /** + Create a new property set on the heap, copy the settings stored by + this property set into the newly created property set and return + the pointer to the new property set. + + \note Use this method instead of using the copy constructor. + + \sa deepCopy, quickReset, fullReset + */ + const KDChartPropertySet* clone() const; + + + /** + Set the name, + set all of the ID settings to idParent, + but do NOT change the value settings, + and do NOT modify mOwnID. + + \note Use this to quickly reset the ID settings: in most cases this should + be sufficient for resetting the property set. + + \sa clone, fullReset + */ + void quickReset( const QString& name, int idParent=KDChartPropertySet::UndefinedID ); + + + /** + Set the name, + set all of the ID settings to idParent, + set all of the value settings back to their default value, + but do NOT modify mOwnID. + + \note Use this to entirely reset both the ID values and the value settings: one of + the very few reasons why you might want to do that might be your saving this property set + into a data stream. In most other cases just calling quickReset should be sufficient. + + \sa clone, quickReset + */ + void fullReset( const QString& name, int idParent=KDChartPropertySet::UndefinedID ); + + + /** + Save this property set's settings in a stream, + this stores the own property set ID as well. + + \sa loadXML + */ + QDomElement saveXML(QDomDocument& doc) const; + + + /** + Retrieves a property set and stores it in parameter set. + + \note Since the property set's internal <b>ID</b> is also read + make sure to call KDChartParams::setProperties( set.id(), set ) + after calling \c load() if you want to use the loaded + property set in the context of KDChartParam settings. + + \returns TRUE if the property set could be read. + + \sa id, saveXLM + */ + static bool loadXML( const QDomElement& element, KDChartPropertySet& set ); + +public slots: + /** + Returns the name of this property set. + */ + int id() const { return mOwnID; } + + /** + Change the descriptive name of this property set. + */ + void setName( const QString& name ) + { + mName = name; + } + + /** + Returns the name of this property set. + */ + QString name() const { return mName; } + + + + /** + Specify the ID of the property set specifying a boolean flag indicating + whether this cell's bar is to be painted + <b>or</b> specifying this flag directly. + + \note This function should be used for Bar Charts only, otherwise + the settings specified here will be ignored. + + \param idShowBar ID of the property set specifying the flag + indicating whether this cell's bar is to be painted. + Use special value KDChartPropertySet::UndefinedID + to specify neither another property set's ID + nor an own value for this flag. + Use special value KDChartPropertySet::OwnID + if you do NOT want to inherit another property set's + settings but want to specify the flag by using + the following parameter. + \param showBar Flag indicating whether this cell's bar is to be painted. + This parameter is stored but ignored if the previous parameter + is not set to KDChartPropertySet::OwnID. + + \sa hasOwnShowBar + \sa setBarColor + \sa hasOwnBarColor + */ + void setShowBar( int idShowBar, bool showBar ) + { + mIdShowBar = idShowBar; + mShowBar = showBar; + } + + /** + Returns whether this property set is specifying it's own flag indicating + whether this cell's bar is to be painted. + + \note This function should be used for Bar Charts only, otherwise + the settings specified here will be ignored. + + \returns TRUE if this property set is specifying it's own showBar flag, + FALSE if the settings of another property set are to be used instead. + + \note The return value will also be FALSE if the 'default' properties + are to be used: in this case idShowBar will be KDChartParams::NormalData + + \param idShowBar to be ignored if return value is TRUE. + If idShowBar is KDChartPropertySet::UndefinedID + then neither a property set ID + nor an own flag were specified (so no special + enabeling/disabeling of bars is associated to the + respective data cell), else idShowBar contains the + ID of another property set that is specifying the flag + to be used. + \param showBar this parameter's value is not modified if return value is FALSE. + Parameter showBar contains the showBar flag + associated with the respective data cell. + If return value is FALSE the showBar + flag is not set (so the parameter keeps its previous value) + but this is to be overridden by the respective value + of another property set that is indicated + by the idShowBar parameter - unless this has the special + value KDChartPropertySet::UndefinedID as decribed above. + + \sa setShowBar + \sa setBarColor + \sa hasOwnBarColor + */ + bool hasOwnShowBar( int& idShowBar, bool& showBar ) + { + idShowBar = mIdShowBar; + if( OwnID == idShowBar ){ + showBar = mShowBar; + return true; + } + return false; + } + + /** + Specify the ID of the property set specifying the bar color + to be used for this cell + <b>or</b> specify the bar color directly. + + \note This function should be used for Bar Charts only, otherwise + the settings specified here will be ignored. + + \param idBarColor ID of the property set specifying the bar color. + Use special value KDChartPropertySet::UndefinedID + to specify neither another property set's ID + nor an own value for the bar color. + Use special value KDChartPropertySet::OwnID + if you do NOT want to inherit another property set's + settings but want to specify the bar color by using + the following parameter. + \param barColor The bar color to be used. + This parameter is stored but ignored if the previous parameter + is not set to KDChartPropertySet::OwnID. + + \sa hasOwnBarColor + */ + void setBarColor( int idBarColor, const QColor& barColor ) + { + mIdBarColor = idBarColor; + mBarColor = barColor; + } + + /** + Returns whether this property set is specifying it's own bar color settings. + + \note This function should be used for Bar Charts only, otherwise + the settings specified here will be ignored. + + \returns TRUE if this property set is specifying it's own bar color settings, + FALSE if the settings of another property set are to be used instead. + + \note The return value will also be FALSE if the 'default' properties + are to be used: in this case idBarColor will be KDChartParams::NormalData + + \param idBarColor to be ignored if return value is TRUE. + If idBarColor is KDChartPropertySet::UndefinedID + then neither a property set ID + nor an own bar color were specified (so no special + Bar color is associated to the respective data cell), + else idBarColor contains the ID of another property set + that is specifying the Bar color to be used. + \param barColor this parameter's value is not modified if return value is FALSE. + Parameter barColor contains the bar color value + associated with the respective data cell. + If return value is FALSE the barColor + value is not set (so the parameter keeps its previous value) + but this is to be overridden by the respective value + of another property set that is indicated + by the idBarColor parameter - unless this has the special + value KDChartPropertySet::UndefinedID as decribed above. + + \sa setBarColor + */ + bool hasOwnBarColor( int& idBarColor, QColor& barColor ) + { + idBarColor = mIdBarColor; + if( OwnID == mIdBarColor ){ + barColor = mBarColor; + return true; + } + return false; + } + + + /** + Specify the ID of the property set specifying the line width + to be used for drawing a line from this data point to the next one + <b>or</b> specify the line width directly. + + \note This function should be used for Line Charts only, otherwise + the settings specified here will be ignored. + + \param idLineWidth ID of the property set specifying the line width. + Use special value KDChartPropertySet::UndefinedID + to specify neither another property set's ID + nor an own value for the line width. + Use special value KDChartPropertySet::OwnID + if you do NOT want to inherit another property set's + settings but want to specify the line width by using + the following parameter. + \param lineWidth The line width to be used. + This parameter is stored but ignored if the previous parameter + is not set to KDChartPropertySet::OwnID. + + \sa setLineWidth, setLineColor, setLineStyle, setShowMarker + \sa hasOwnLineWidth, hasOwnLineColor, hasOwnLineStyle, hasOwnShowMarker + */ + void setLineWidth( int idLineWidth, int lineWidth ) + { + mIdLineWidth = idLineWidth; + mLineWidth = lineWidth; + } + + /** + Returns whether this property set is specifying it's own line width settings. + + \note This function should be used for Line Charts only. + + \returns TRUE if this property set is specifying it's own line width settings, + FALSE if the settings of another property set are to be used instead. + + \param idLineWidth to be ignored if return value is TRUE. + If idLineWidth is KDChartPropertySet::UndefinedID + then neither a property set ID + nor an own line width were specified (so no special + line width is associated to the respective data cell), + else idLineWidth contains the ID of another property set + that is specifying the line width to be used. + \param lineWidth this parameter's value is not modified if return value is FALSE. + Parameter lineWidth contains the line width value + associated with the respective data cell. + If return value is FALSE the lineWidth + value is not set (so the parameter keeps its previous value) + but this is to be overridden by the respective value + of another property set that is indicated + by the idLineWidth parameter - unless this has the special + value KDChartPropertySet::UndefinedID as decribed above. + + \sa setLineWidth, setLineColor, setLineStyle, setShowMarker + \sa hasOwnLineColor, hasOwnLineStyle, hasOwnShowMarker + */ + bool hasOwnLineWidth( int& idLineWidth, int& lineWidth ) + { + idLineWidth = mIdLineWidth; + if( OwnID == mIdLineWidth ){ + lineWidth = mLineWidth; + return true; + } + return false; + } + + /** + Specify the ID of the property set specifying the line color + to be used for drawing a line from this data point to the next one + <b>or</b> specify the line color directly. + + \note This function should be used for Line Charts only, otherwise + the settings specified here will be ignored. + + \param idLineColor ID of the property set specifying the line color. + Use special value KDChartPropertySet::UndefinedID + to specify neither another property set's ID + nor an own value for the line color. + Use special value KDChartPropertySet::OwnID + if you do NOT want to inherit another property set's + settings but want to specify the line color by using + the following parameter. + \param lineColor The line color to be used. + This parameter is stored but ignored if the previous parameter + is not set to KDChartPropertySet::OwnID. + + \sa setLineWidth, setLineStyle, setShowMarker + \sa hasOwnLineWidth, hasOwnLineColor, hasOwnLineStyle, hasOwnShowMarker + */ + void setLineColor( int idLineColor, const QColor& lineColor ) + { + mIdLineColor = idLineColor; + mLineColor = lineColor; + } + + /** + Returns whether this property set is specifying it's own line color settings. + + \note This function should be used for Line Charts only, otherwise + the settings specified here will be ignored. + + \returns TRUE if this property set is specifying it's own line color settings, + FALSE if the settings of another property set are to be used instead. + + \note The return value will also be FALSE if the 'default' properties + are to be used: in this case idLineColor will be KDChartParams::NormalData + + \param idLineColor to be ignored if return value is TRUE. + If idLineColor is KDChartPropertySet::UndefinedID + then neither a property set ID + nor an own line color were specified (so no special + line color is associated to the respective data cell), + else idLineColor contains the ID of another property set + that is specifying the line color to be used. + \param lineColor this parameter's value is not modified if return value is FALSE. + Parameter lineColor contains the line color value + associated with the respective data cell. + If return value is FALSE the lineColor + value is not set (so the parameter keeps its previous value) + but this is to be overridden by the respective value + of another property set that is indicated + by the idLineColor parameter - unless this has the special + value KDChartPropertySet::UndefinedID as decribed above. + + \sa setLineWidth, setLineColor, setLineStyle, setShowMarker + \sa hasOwnLineWidth, hasOwnLineStyle, hasOwnShowMarker + */ + bool hasOwnLineColor( int& idLineColor, QColor& lineColor ) + { + idLineColor = mIdLineColor; + if( OwnID == mIdLineColor ){ + lineColor = mLineColor; + return true; + } + return false; + } + + /** + Specify the ID of the property set specifying the line style + to be used for drawing a line from this data point to the next one + <b>or</b> specify the line width directly. + + \note This function should be used for Line Charts only, otherwise + the settings specified here will be ignored. + + \param idLineStyle ID of the property set specifying the line style. + Use special value KDChartPropertySet::UndefinedID + to specify neither another property set's ID + nor an own value for the line style. + Use special value KDChartPropertySet::OwnID + if you do NOT want to inherit another property set's + settings but want to specify the line style by using + the following parameter. + \param lineStyle The line style to be used. + This parameter is stored but ignored if the previous parameter + is not set to KDChartPropertySet::OwnID. + + \sa setLineWidth, setLineColor, setShowMarker + \sa hasOwnLineWidth, hasOwnLineColor, hasOwnLineStyle, hasOwnShowMarker + */ + void setLineStyle( int idLineStyle, const PenStyle& lineStyle ) + { + mIdLineStyle = idLineStyle; + mLineStyle = lineStyle; + } + + /** + Returns whether this property set is specifying it's own line style settings. + + \note This function should be used for Line Charts only, otherwise + the settings specified here will be ignored. + + \returns TRUE if this property set is specifying it's own line style settings, + FALSE if the settings of another property set are to be used instead. + + \note The return value will also be FALSE if the 'default' properties + are to be used: in this case idLineStyle will be KDChartParams::NormalData + + \param idLineStyle to be ignored if return value is TRUE. + If idLineStyle is KDChartPropertySet::UndefinedID + then neither a property set ID + nor an own line style were specified (so no special + line style is associated to the respective data cell), + else idLineStyle contains the ID of another property set + that is specifying the line style to be used. + \param lineStyle this parameter's value is not modified if return value is FALSE. + Parameter lineStyle contains the line style value + associated with the respective data cell. + If return value is FALSE the lineStyle + value is not set (so the parameter keeps its previous value) + but this is to be overridden by the respective value + of another property set that is indicated + by the idLineStyle parameter - unless this has the special + value KDChartPropertySet::UndefinedID as decribed above. + + \sa setLineWidth, setLineColor, setLineStyle, setShowMarker + \sa hasOwnLineWidth, hasOwnLineColor, hasOwnShowMarker + */ + bool hasOwnLineStyle( int& idLineStyle, PenStyle& lineStyle ) + { + idLineStyle = mIdLineStyle; + if( OwnID == mIdLineStyle ){ + lineStyle = mLineStyle; + return true; + } + return false; + } + + + + /** + Specify the ID of the property set specifying the area brush + to be used for this cell + <b>or</b> specify the area brush directly. + + \note This function should be used for Area Charts in <b>Normal</b> mode + only, otherwise the settings specified here will be ignored. + + \param idAreaBrush ID of the property set specifying the area brush. + Use special value KDChartPropertySet::UndefinedID + to specify neither another property set's ID + nor an own value for the area brush. + Use special value KDChartPropertySet::OwnID + if you do NOT want to inherit another property set's + settings but want to specify the area brush by using + the following parameter. + \param areaBrush The area brush to be used. + This parameter is stored but ignored if the previous parameter + is not set to KDChartPropertySet::OwnID. + + \sa hasOwnAreaBrush + */ + void setAreaBrush( int idAreaBrush, const QBrush& areaBrush ) + { + mIdAreaBrush = idAreaBrush; + mAreaBrush = areaBrush; + } + + /** + Returns whether this property set is specifying it's own area brush settings. + + \note This function should be used for Area Charts in <b>Normal</b> mode + only, otherwise the settings specified here will be ignored. + + \returns TRUE if this property set is specifying it's own area brush settings, + FALSE if the settings of another property set are to be used instead. + + \note The return value will also be FALSE if the 'default' properties + are to be used: in this case idAreaBrush will be KDChartParams::NormalData + + \param idAreaBrush to be ignored if return value is TRUE. + If idAreaBrush is KDChartPropertySet::UndefinedID + then neither a property set ID + nor an own area brush were specified (so no special + area brush is associated to the respective data cell), + else idAreaBrush contains the ID of another property set + that is specifying the area brush to be used. + \param areaBrush this parameter's value is not modified if return value is FALSE. + Parameter areaBrush contains the area brush value + associated with the respective data cell. + If return value is FALSE the areaBrush + value is not set (so the parameter keeps its previous value) + but this is to be overridden by the respective value + of another property set that is indicated + by the idAreaBrush parameter - unless this has the special + value KDChartPropertySet::UndefinedID as decribed above. + + \sa setAreaBrush + */ + bool hasOwnAreaBrush( int& idAreaBrush, QBrush& areaBrush ) + { + idAreaBrush = mIdAreaBrush; + if( OwnID == mIdAreaBrush ){ + areaBrush = mAreaBrush; + return true; + } + return false; + } + + + + + + /** + Specify the ID of the property set specifying a boolean flag indicating + whether a Marker is to be displayed for this data value + <b>or</b> specifying this flag directly. + + \note This function should be used for Line Charts only, otherwise + the settings specified here will be ignored. + + \param idShowMarker ID of the property set specifying the flag + indicating whether a marker is to be shown. + Use special value KDChartPropertySet::UndefinedID + to specify neither another property set's ID + nor an own value for this flag. + Use special value KDChartPropertySet::OwnID + if you do NOT want to inherit another property set's + settings but want to specify the flag by using + the following parameter. + \param showMarker Flag indicating whether a marker is to be shown. + This parameter is stored but ignored if the previous parameter + is not set to KDChartPropertySet::OwnID. + + \sa hasOwnShowMarker + \sa setMarkerAlign, setMarkerSize, setMarkerColor, setMarkerStyle + \sa setLineWidth, setLineColor, setLineStyle + */ + void setShowMarker( int idShowMarker, bool showMarker ) + { + mIdShowMarker = idShowMarker; + mShowMarker = showMarker; + } + + /** + Returns whether this property set is specifying it's own flag indicating + whether a Marker is to be displayed. + + \note This function should be used for Line Charts only, otherwise + the settings specified here will be ignored. + + \returns TRUE if this property set is specifying it's own showMarker flag, + FALSE if the settings of another property set are to be used instead. + + \note The return value will also be FALSE if the 'default' properties + are to be used: in this case idShowMarker will be KDChartParams::NormalData + + \param idShowMarker to be ignored if return value is TRUE. + If idShowMarker is KDChartPropertySet::UndefinedID + then neither a property set ID + nor an own flag were specified (so no special + enabeling/disabeling of markers is associated to the + respective data cell), else idShowMarker contains the + ID of another property set that is specifying the flag + to be used. + \param showMarker this parameter's value is not modified if return value is FALSE. + Parameter showMarker contains the showMarker flag + associated with the respective data cell. + If return value is FALSE the showMarker + flag is not set (so the parameter keeps its previous value) + but this is to be overridden by the respective value + of another property set that is indicated + by the idShowMarker parameter - unless this has the special + value KDChartPropertySet::UndefinedID as decribed above. + + \sa setShowMarker + \sa hasOwnMarkerAlign, hasOwnMarkerSize, hasOwnMarkerColor, hasOwnMarkerStyle + \sa hasOwnLineWidth, hasOwnLineColor, hasOwnLineStyle + */ + bool hasOwnShowMarker( int& idShowMarker, bool& showMarker ) + { + idShowMarker = mIdShowMarker; + if( OwnID == idShowMarker ){ + showMarker = mShowMarker; + return true; + } + return false; + } + + /** + Specify the ID of the property set specifying the alignment of the + Marker to be displayed for this data value + <b>or</b> specifying this flag directly. + + \note This function should be used for Line Charts only, otherwise + the settings specified here will be ignored. + + \param idMarkerAlign ID of the property set specifying the alignment + of the Marker to be shown. + Use special value KDChartPropertySet::UndefinedID + to specify neither another property set's ID + nor an own value for this flag. + Use special value KDChartPropertySet::OwnID + if you do NOT want to inherit another property set's + settings but want to specify the flag by using + the following parameter. + \param markerAlign The alignment of the marker to be shown. + This parameter is stored but ignored if the previous parameter + is not set to KDChartPropertySet::OwnID. + + \sa hasOwnShowMarker + \sa setMarkerAlign, setMarkerSize, setMarkerColor, setMarkerStyle + \sa setLineWidth, setLineColor, setLineStyle + */ + void setMarkerAlign( int idMarkerAlign, uint markerAlign ) + { + mIdMarkerAlign = idMarkerAlign; + mMarkerAlign = markerAlign; + } + bool hasOwnMarkerAlign( int& idMarkerAlign, uint& markerAlign ) + { + idMarkerAlign = mIdMarkerAlign; + if( OwnID == idMarkerAlign ){ + markerAlign = mMarkerAlign; + return true; + } + return false; + } + void setMarkerSize( int idMarkerSize, const QSize& markerSize ) + { + mIdMarkerSize = idMarkerSize; + mMarkerSize = markerSize; + } + bool hasOwnMarkerSize( int& idMarkerSize, QSize& markerSize ) + { + idMarkerSize = mIdMarkerSize; + if( OwnID == idMarkerSize ){ + markerSize = mMarkerSize; + return true; + } + return false; + } + void setMarkerColor( int idMarkerColor, const QColor& markerColor ) + { + mIdMarkerColor = idMarkerColor; + mMarkerColor = markerColor; + } + bool hasOwnMarkerColor( int& idMarkerColor, QColor& markerColor ) + { + idMarkerColor = mIdMarkerColor; + if( OwnID == idMarkerColor ){ + markerColor = mMarkerColor; + return true; + } + return false; + } + void setMarkerStyle( int idMarkerStyle, int markerStyle ) + { + mIdMarkerStyle = idMarkerStyle; + mMarkerStyle = markerStyle; + } + bool hasOwnMarkerStyle( int& idMarkerStyle, int& markerStyle ) + { + idMarkerStyle = mIdMarkerStyle; + if( OwnID == idMarkerStyle ){ + markerStyle = mMarkerStyle; + return true; + } + return false; + } + + + + + + + void setExtraLinesAlign( int idExtraLinesAlign, uint extraLinesAlign ) + { + mIdExtraLinesAlign = idExtraLinesAlign; + mExtraLinesAlign = extraLinesAlign; + } + bool hasOwnExtraLinesAlign( int& idExtraLinesAlign, uint& extraLinesAlign ) + { + idExtraLinesAlign = mIdExtraLinesAlign; + if( OwnID == idExtraLinesAlign ){ + extraLinesAlign = mExtraLinesAlign; + return true; + } + return false; + } + void setExtraLinesInFront( int idExtraLinesInFront, bool extraLinesInFront ) + { + mIdExtraLinesInFront = idExtraLinesInFront; + mExtraLinesInFront = extraLinesInFront; + } + bool hasOwnExtraLinesInFront( int& idExtraLinesInFront, bool& extraLinesInFront ) + { + idExtraLinesInFront = mIdExtraLinesInFront; + if( OwnID == idExtraLinesInFront ){ + extraLinesInFront = mExtraLinesInFront; + return true; + } + return false; + } + void setExtraLinesLength( int idExtraLinesLength, int extraLinesLength ) + { + mIdExtraLinesLength = idExtraLinesLength; + mExtraLinesLength = extraLinesLength; + } + bool hasOwnExtraLinesLength( int& idExtraLinesLength, int& extraLinesLength ) + { + idExtraLinesLength = mIdExtraLinesLength; + if( OwnID == idExtraLinesLength ){ + extraLinesLength = mExtraLinesLength; + return true; + } + return false; + } + void setExtraLinesWidth( int idExtraLinesWidth, int extraLinesWidth ) + { + mIdExtraLinesWidth = idExtraLinesWidth; + mExtraLinesWidth = extraLinesWidth; + } + bool hasOwnExtraLinesWidth( int& idExtraLinesWidth, int& extraLinesWidth ) + { + idExtraLinesWidth = mIdExtraLinesWidth; + if( OwnID == idExtraLinesWidth ){ + extraLinesWidth = mExtraLinesWidth; + return true; + } + return false; + } + void setExtraLinesColor( int idExtraLinesColor, const QColor& extraLinesColor ) + { + mIdExtraLinesColor = idExtraLinesColor; + mExtraLinesColor = extraLinesColor; + } + bool hasOwnExtraLinesColor( int& idExtraLinesColor, QColor& extraLinesColor ) + { + idExtraLinesColor = mIdExtraLinesColor; + if( OwnID == idExtraLinesColor ){ + extraLinesColor = mExtraLinesColor; + return true; + } + return false; + } + void setExtraLinesStyle( int idExtraLinesStyle, const PenStyle extraLinesStyle ) + { + mIdExtraLinesStyle = idExtraLinesStyle; + mExtraLinesStyle = extraLinesStyle; + } + bool hasOwnExtraLinesStyle( int& idExtraLinesStyle, PenStyle& extraLinesStyle ) + { + idExtraLinesStyle = mIdExtraLinesStyle; + if( OwnID == idExtraLinesStyle ){ + extraLinesStyle = mExtraLinesStyle; + return true; + } + return false; + } + + void setExtraMarkersAlign( int idExtraMarkersAlign, uint extraMarkersAlign ) + { + mIdExtraMarkersAlign = idExtraMarkersAlign; + mExtraMarkersAlign = extraMarkersAlign; + } + bool hasOwnExtraMarkersAlign( int& idExtraMarkersAlign, uint& extraMarkersAlign ) + { + idExtraMarkersAlign = mIdExtraMarkersAlign; + if( OwnID == idExtraMarkersAlign ){ + extraMarkersAlign = mExtraMarkersAlign; + return true; + } + return false; + } + void setExtraMarkersSize( int idExtraMarkersSize, const QSize& extraMarkersSize ) + { + mIdExtraMarkersSize = idExtraMarkersSize; + mExtraMarkersSize = extraMarkersSize; + } + bool hasOwnExtraMarkersSize( int& idExtraMarkersSize, QSize& extraMarkersSize ) + { + idExtraMarkersSize = mIdExtraMarkersSize; + if( OwnID == idExtraMarkersSize ){ + extraMarkersSize = mExtraMarkersSize; + return true; + } + return false; + } + void setExtraMarkersColor( int idExtraMarkersColor, const QColor& extraMarkersColor ) + { + mIdExtraMarkersColor = idExtraMarkersColor; + mExtraMarkersColor = extraMarkersColor; + } + bool hasOwnExtraMarkersColor( int& idExtraMarkersColor, QColor& extraMarkersColor ) + { + idExtraMarkersColor = mIdExtraMarkersColor; + if( OwnID == idExtraMarkersColor ){ + extraMarkersColor = mExtraMarkersColor; + return true; + } + return false; + } + void setExtraMarkersStyle( int idExtraMarkersStyle, int extraMarkersStyle ) + { + mIdExtraMarkersStyle = idExtraMarkersStyle; + mExtraMarkersStyle = extraMarkersStyle; + } + bool hasOwnExtraMarkersStyle( int& idExtraMarkersStyle, int& extraMarkersStyle ) + { + idExtraMarkersStyle = mIdExtraMarkersStyle; + if( OwnID == idExtraMarkersStyle ){ + extraMarkersStyle = mExtraMarkersStyle; + return true; + } + return false; + } + +protected: + // the following member only to be set internally by KDChartParams::registerProperties + // and by KDChartParams::setProperties + int mOwnID; + +private: + KDChartPropertySet( const KDChartPropertySet& ) : QObject(0) {} + + QString mName; + // IDs: values used if ID == OwnID: + int mIdLineWidth; int mLineWidth; + int mIdLineColor; QColor mLineColor; + int mIdLineStyle; Qt::PenStyle mLineStyle; + int mIdShowMarker; bool mShowMarker; + int mIdMarkerSize; QSize mMarkerSize; + int mIdMarkerColor; QColor mMarkerColor; + int mIdMarkerStyle; int mMarkerStyle; + int mIdMarkerAlign; uint mMarkerAlign; + int mIdExtraLinesAlign; uint mExtraLinesAlign; + int mIdExtraLinesInFront; bool mExtraLinesInFront; + int mIdExtraLinesLength; int mExtraLinesLength; + int mIdExtraLinesWidth; int mExtraLinesWidth; + int mIdExtraLinesColor; QColor mExtraLinesColor; + int mIdExtraLinesStyle; Qt::PenStyle mExtraLinesStyle; + int mIdExtraMarkersAlign; uint mExtraMarkersAlign; + int mIdExtraMarkersSize; QSize mExtraMarkersSize; + int mIdExtraMarkersColor; QColor mExtraMarkersColor; + int mIdExtraMarkersStyle; int mExtraMarkersStyle; + int mIdShowBar; bool mShowBar; + int mIdBarColor; QColor mBarColor; + int mIdAreaBrush; QBrush mAreaBrush; + void fillValueMembersWithDummyValues(); +}; + +#endif diff --git a/kchart/kdchart/KDChartRingPainter.cpp b/kchart/kdchart/KDChartRingPainter.cpp new file mode 100644 index 00000000..20870662 --- /dev/null +++ b/kchart/kdchart/KDChartRingPainter.cpp @@ -0,0 +1,400 @@ +/* -*- 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. + ** + **********************************************************************/ +#include "KDChartRingPainter.h" +#include "KDChartParams.h" + +#include <qpainter.h> +#include <qvaluestack.h> + +#include <stdlib.h> + +#define DEGTORAD(d) (d)*M_PI/180 + +/** + \class KDChartRingPainter KDChartRingPainter.h + + \brief A chart painter implementation that can paint pie charts. + + PENDING(kalle) Write more documentation. + */ + +/** + Constructor. Sets up internal data structures as necessary. + + \param params the KDChartParams structure that defines the chart + */ + KDChartRingPainter::KDChartRingPainter( KDChartParams* params ) : +KDChartPainter( 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. + */ +KDChartRingPainter::~KDChartRingPainter() +{ + // intentionally left blank +} + + +/** + Paints the actual data area. + + \param painter the QPainter onto which the chart should be painted + \param data the data that will be displayed as a chart + \param paint2nd specifies whether the main chart or the additional chart is to be drawn now + \param regions a pointer to a list of regions that will be filled + with regions representing the data segments, if not null + */ +void KDChartRingPainter::paintData( QPainter* painter, + KDChartTableDataBase* data, + bool paint2nd, + KDChartDataRegionList* regions ) +{ + uint chart = paint2nd ? 1 : 0; + + QRect ourClipRect( _dataRect ); + + const QWMatrix & world = painter->worldMatrix(); + ourClipRect = +#if COMPAT_QT_VERSION >= 0x030000 + world.mapRect( ourClipRect ); +#else + world.map( ourClipRect ); +#endif + + ourClipRect.setTop(ourClipRect.top()-1); + ourClipRect.setLeft(ourClipRect.left()-1); + ourClipRect.setBottom(ourClipRect.bottom()+1); + ourClipRect.setRight(ourClipRect.right()+1); + painter->setClipRect( ourClipRect ); + + uint datasetStart, datasetEnd; + if ( params()->neverUsedSetChartSourceMode() + || !params()->findDataset( KDChartParams::DataEntry, + datasetStart, + datasetEnd, + chart ) ) { + uint maxRow, maxRowMinus1; + switch ( data->usedRows() ) { + case 0: + return ; + case 1: + maxRow = 0; + maxRowMinus1 = 0; + break; + default: + maxRow = data->usedRows() - 1; + maxRowMinus1 = maxRow - 1; + } + datasetStart = paint2nd ? maxRow + : 0; + datasetEnd = paint2nd ? maxRow + : ( ( KDChartParams::NoType + == params()->additionalChartType() ) + ? maxRow + : maxRowMinus1 ); + } + uint datasetNum = abs( (int)( datasetEnd - datasetStart ) + 1 ); + + + // Number of values: If -1, use all values, otherwise use the + // specified number of values. + if ( params()->numValues() != -1 ) + _numValues = params()->numValues(); + else + _numValues = data->usedCols(); + + // compute position + _size = QMIN( _dataRect.width(), _dataRect.height() ); // initial size + // if the rings explode, we need to give them additional space => + // make the basic size smaller + if ( params()->explode() ) { + double doubleSize = ( double ) _size; + doubleSize /= ( 1.0 + params()->explodeFactor() * 2 ); + _size = ( int ) doubleSize; + } + + int x = ( _dataRect.width() == _size ) ? 0 : ( ( _dataRect.width() - _size ) / 2 ); + int y = ( _dataRect.height() == _size ) ? 0 : ( ( _dataRect.height() - _size ) / 2 ); + _position = QRect( x, y, _size, _size ); + _position.moveBy( _dataRect.left(), _dataRect.top() ); + + // We need the row sums anyway later, so we can just as well compute them + // here, because we need them in case of relative ring thicknesses. + QMemArray<double> rowsums; + double totalSum = 0.0; + rowsums.resize( datasetEnd+1 ); // not datasetNum! + for( int d1 = (int)datasetStart; d1 <= (int)datasetEnd; d1++ ) { + rowsums[d1] = data->rowAbsSum( d1 ); + totalSum += rowsums[d1]; + } + + QMemArray<int> ringthicknesses; + ringthicknesses.resize( datasetEnd+1 ); // not datasetNum! + + // constant ring thickness + int ringthickness = _size / ( datasetNum * 2 ); + // Never let the ring thickness be more than 1/10 of the size to + // ensure "ringness" + if( ringthickness > ( _size/10 ) ) + ringthickness = _size / 10; + + for( int d2 = (int)datasetStart; d2 <= (int)datasetEnd; d2++ ) + if( params()->relativeRingThickness() ) { + // 50% should be the same thickness as the one used when ring + // thickness is constant. + ringthicknesses[d2] = (uint)floor( (rowsums[d2] / totalSum) * + ( 2.0 * (double)ringthickness ) + 0.5 ); + } else { + ringthicknesses[d2] = ringthickness; + } + + int currentouterradius = _size/2; + + // Loop through all the displayable datasets; each dataset is one ring + for( int dataset = (int)datasetStart; dataset <= (int)datasetEnd; dataset++ ) { + double sectorsPerValue = 5760.0 / rowsums[dataset]; // 5760 == 16*360, number of sections in Qt circle + //int sectorsPerValueI = static_cast<int>( sectorsPerValue ); + double currentstartpos = (double)params()->ringStart() * 16.0; + // Loop through all the values; each value is one piece on the ring. + QVariant vValY; + for( int value = 0; value < _numValues; value++ ) { + // is there anything at all at this value? + double cellValue = 0.0; + if( data->cellCoord( dataset, value, vValY, 1 ) && + QVariant::Double == vValY.type() ){ + cellValue = fabs( vValY.toDouble() ); + // Explosion: Only explode if explosion is turned on generally + // and we are on the first ring. Besides, if there is a list + // of explodeable values, the current value must be on this + // list. + + QValueList<int> explodeList = params()->explodeValues(); + bool explode = params()->explode() && // explosion is on at all + ( dataset == (int)datasetStart ) && // outermost ring + ( ( explodeList.count() == 0 ) || // either nothing on explode list + ( explodeList.find( value ) != explodeList.end() ) ); // or pie is on it + + drawOneSegment( painter, + currentouterradius, + currentouterradius-ringthicknesses[dataset], + currentstartpos, + sectorsPerValue * cellValue, + dataset, value, chart, explode, regions ); + } + currentstartpos += sectorsPerValue * cellValue; + } + currentouterradius -= ringthicknesses[dataset]; + } +} + + + +void KDChartRingPainter::drawOneSegment( QPainter* painter, + uint outerRadius, + uint innerRadius, + double startAngle, + double angles, + uint dataset, + uint value, + uint chart, + bool explode, + KDChartDataRegionList* regions ) +{ + // special case for full circle + if( angles == 5760.0 ) + startAngle = 0.0; + + painter->setPen( QPen( params()->outlineDataColor(), + params()->outlineDataLineWidth() ) ); + painter->setBrush( params()->dataColor( value ) ); + + uint outerRadius2 = outerRadius * 2; + uint innerRadius2 = innerRadius * 2; + + QRect drawPosition = _position; + if ( explode ) { + // need to compute a new position for each pie + double explodeAngle = ( startAngle + angles / 2.0 ) / 16.0; + double explodeAngleRad = DEGTORAD( explodeAngle ); + double cosAngle = cos( explodeAngleRad ); + double sinAngle = -sin( explodeAngleRad ); + + // find the explode factor for this particular ring segment + double explodeFactor = 0.0; + QMap<int,double> explodeFactors = params()->explodeFactors(); + if( !explodeFactors.contains( value ) ) // not on factors list, use default + explodeFactor = params()->explodeFactor(); + else // on factors list, use segment-specific value + explodeFactor = explodeFactors[value]; + + double explodeX = explodeFactor * _size * cosAngle; + double explodeY = explodeFactor * _size * sinAngle; + drawPosition.moveBy( static_cast<int>( explodeX ), static_cast<int>( explodeY ) ); + } + + QRect outerRect( drawPosition.x() + + ( drawPosition.width() - outerRadius2 ) / 2, + drawPosition.y() + + ( drawPosition.height() - outerRadius2 ) / 2, + outerRadius2, outerRadius2 ); + QRect innerRect( drawPosition.x() + + ( drawPosition.width() - innerRadius2 ) / 2, + drawPosition.y() + + ( drawPosition.height() - innerRadius2 ) / 2, + innerRadius2, innerRadius2 ); + + // Start with getting the points for the inner arc. + QPointArray innerArc; + makeArc( innerArc, innerRect, startAngle, angles ); + + // And the points for the outer arc + QPointArray outerArc; + makeArc( outerArc, outerRect, startAngle, angles ); + + // Now copy the points from the outer arc in the reverse order onto the + // inner arc array and draw that. + uint innerArcPoints = innerArc.size(); + uint outerArcPoints = outerArc.size(); + innerArc.resize( innerArcPoints + outerArcPoints ); + for ( int i = outerArcPoints - 1; i >= 0; i-- ) { + innerArc.setPoint( innerArcPoints+outerArcPoints-i-1, + outerArc.point( i ) ); + } + + painter->drawPolygon( innerArc ); + if ( regions /* && ( innerArc.size() > 2 )*/ ) { + KDChartDataRegion* datReg = new KDChartDataRegion( dataset, + value, + chart, + innerArc ); + + const int aA = static_cast<int>( startAngle ); + const int aM = static_cast<int>( startAngle + angles / 2.0 ); + const int aZ = static_cast<int>( startAngle + angles ); + + datReg->points[ KDChartEnums::PosTopLeft ] + = pointOnCircle( outerRect, aZ ); + datReg->points[ KDChartEnums::PosTopCenter ] + = pointOnCircle( outerRect, aM ); + datReg->points[ KDChartEnums::PosTopRight ] + = pointOnCircle( outerRect, aA ); + + datReg->points[ KDChartEnums::PosBottomLeft ] + = pointOnCircle( innerRect, aZ ); + datReg->points[ KDChartEnums::PosBottomCenter ] + = pointOnCircle( innerRect, aM ); + datReg->points[ KDChartEnums::PosBottomRight ] + = pointOnCircle( innerRect, aA ); + + datReg->points[ KDChartEnums::PosCenterLeft ] + = QPoint( ( datReg->points[ KDChartEnums::PosTopLeft ].x() + + datReg->points[ KDChartEnums::PosBottomLeft ].x() ) / 2, + ( datReg->points[ KDChartEnums::PosTopLeft ].y() + + datReg->points[ KDChartEnums::PosBottomLeft ].y() ) / 2 ); + datReg->points[ KDChartEnums::PosCenter ] + = QPoint( ( datReg->points[ KDChartEnums::PosTopCenter ].x() + + datReg->points[ KDChartEnums::PosBottomCenter ].x() ) / 2, + ( datReg->points[ KDChartEnums::PosTopCenter ].y() + + datReg->points[ KDChartEnums::PosBottomCenter ].y() ) / 2 ); + datReg->points[ KDChartEnums::PosCenterRight ] + = QPoint( ( datReg->points[ KDChartEnums::PosTopRight ].x() + + datReg->points[ KDChartEnums::PosBottomRight ].x() ) / 2, + ( datReg->points[ KDChartEnums::PosTopRight ].y() + + datReg->points[ KDChartEnums::PosBottomRight ].y() ) / 2 ); + + // test the 9 positions: + /* + painter->drawEllipse( datReg->points[ KDChartEnums::PosTopLeft ].x() - 2, + datReg->points[ KDChartEnums::PosTopLeft ].y() - 2, 5, 5); + painter->drawEllipse( datReg->points[ KDChartEnums::PosCenterLeft ].x() - 2, + datReg->points[ KDChartEnums::PosCenterLeft ].y() - 2, 5, 5); + painter->drawEllipse( datReg->points[ KDChartEnums::PosBottomLeft ].x() - 2, + datReg->points[ KDChartEnums::PosBottomLeft ].y() - 2, 5, 5); + + qDebug( "\ncenter: (%i, %i)", + datReg->points[ KDChartEnums::PosCenter ].x(), + datReg->points[ KDChartEnums::PosCenter ].y() ); + painter->drawEllipse( datReg->points[ KDChartEnums::PosTopCenter ].x() - 2, + datReg->points[ KDChartEnums::PosTopCenter ].y() - 2, 5, 5); + painter->drawEllipse( datReg->points[ KDChartEnums::PosCenter ].x() - 2, + datReg->points[ KDChartEnums::PosCenter ].y() - 2, 5, 5); + painter->drawEllipse( datReg->points[ KDChartEnums::PosBottomCenter].x() - 2, + datReg->points[ KDChartEnums::PosBottomCenter].y() - 2, 5, 5); + + painter->drawRect( datReg->points[ KDChartEnums::PosCenterRight ].x() - 2, + datReg->points[ KDChartEnums::PosCenterRight ].y() - 2, 5, 5); + //painter->drawRect( datReg->points[ KDChartEnums::PosTopRight ].x() - 2, + // datReg->points[ KDChartEnums::PosTopRight ].y() - 2, 5, 5); + painter->drawRect( datReg->points[ KDChartEnums::PosBottomRight ].x() - 2, + datReg->points[ KDChartEnums::PosBottomRight ].y() - 2, 5, 5); + */ + datReg->startAngle = static_cast<int>( startAngle ); + datReg->angleLen = static_cast<int>( angles ); + regions->append( datReg ); + } +} + + +/** + This method is a specialization that returns a fallback legend text + appropriate for rings where the fallbacks should come from the values, not + from the datasets. + + 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 KDChartRingPainter::fallbackLegendText( uint dataset ) const +{ + return QObject::tr( "Item " ) + 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 KDChartRingPainter::numLegendFallbackTexts( KDChartTableDataBase* data ) const +{ + return data->usedCols(); +} diff --git a/kchart/kdchart/KDChartRingPainter.h b/kchart/kdchart/KDChartRingPainter.h new file mode 100644 index 00000000..be70f0ce --- /dev/null +++ b/kchart/kdchart/KDChartRingPainter.h @@ -0,0 +1,64 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTRINGPAINTER_H__ +#define __KDCHARTRINGPAINTER_H__ + +#include <KDChartPainter.h> +#include <KDChartTable.h> + +class KDChartParams; + +class KDChartRingPainter : public KDChartPainter +{ + friend class KDChartPainter; + protected: + KDChartRingPainter( KDChartParams* params ); + virtual ~KDChartRingPainter(); + + virtual void paintData( QPainter* painter, + KDChartTableDataBase* data, + bool paint2nd, + KDChartDataRegionList* regions = 0 ); + void drawOneSegment( QPainter* painter, + uint outerRadius, uint innerRadius, + double startAngle, double angles, + uint dataset, uint value, uint chart, + bool explode, + KDChartDataRegionList* regions = 0 ); + + virtual QString fallbackLegendText( uint dataset ) const; + virtual uint numLegendFallbackTexts( KDChartTableDataBase* data ) const; + + QRect _position; + int _size; + int _numValues; // PENDING(kalle) Move to base class +} +; + +#endif diff --git a/kchart/kdchart/KDChartSeriesCollection.cpp b/kchart/kdchart/KDChartSeriesCollection.cpp new file mode 100644 index 00000000..185625e1 --- /dev/null +++ b/kchart/kdchart/KDChartSeriesCollection.cpp @@ -0,0 +1,240 @@ +/* -*- 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. + ** + **********************************************************************/ + + +#include "KDChartSeriesCollection.h" +#include "KDChartParams.h" + + +KDChartSeriesCollection::KDChartSeriesCollection( KDChartParams *params ) +{ + _params = params; +} + + +KDChartSeriesCollection::~KDChartSeriesCollection() +{ + for ( int i = 0; i < static_cast < int > ( (*this).size() ); i ++ ) + delete (*this)[i]; +} + + +// both rows return the same amount +uint KDChartSeriesCollection::rows() const +{ + return usedRows(); +} +uint KDChartSeriesCollection::usedRows() const +{ + return (*this).size(); +} + + +uint KDChartSeriesCollection::cols() const +{ + return usedCols(); +} + + +uint KDChartSeriesCollection::usedCols() const +{ + uint result = 0; + + // find the maximum number of rows in all the visible series + + for ( int i = 0; i < static_cast < int > ( (*this).size() ); i ++ ) + if ( result < (*this)[i]->rows()) + result = (*this)[i]->rows(); + + return result; +} + +// Don't really know how to handle these yet, I have no need yet. +// It appears to be only used to load QTables. +// ASSERT if used. +void KDChartSeriesCollection::setUsedRows( uint ) +{ + Q_ASSERT(0); +} +void KDChartSeriesCollection::setUsedCols( uint ) +{ + Q_ASSERT(0); +} + + +void KDChartSeriesCollection::setCell( uint row, uint col, + const KDChartData& element ) +{ + Q_ASSERT( row < (*this).size() ); + this->at(row)->setCell(col, element); +} + +const KDChartData& KDChartSeriesCollection::cell( uint row, uint col ) const +{ + Q_ASSERT( row < (*this).size() ); + // Put this back in if/when KHZ adds the performance improvements + // re usedCols( uint row ) Q_ASSERT( col < (*this)[row]->rows() ); + + if ( col < this->at(row)->rows() ) + return this->at(row)->cell(col); + else + return _blank; +} + +void KDChartSeriesCollection::expand( uint cols, uint rows ) +{ + // first expand ourselves - cols-wise + (*this).resize(rows); + + // now expand our babies + for ( int i = 0; i < static_cast < int > ( (*this).size() ); i ++ ) + (*this)[i]->expand(cols); +} + + + +// coordinate is the first or second value in a data point +double KDChartSeriesCollection::maxValue( int coordinate ) const +{ + // IF there are no series to read from, then just return zero. + // KHZ: perhaps this should assert? + + bool ok; // the ok is required in case we check a PlaneSeries, which + // cannot possibly have a min or max on one of the axis. + + double result = 0; // if no valid min/max, then this is the default + bool first_max = true; + + // find the first max +#if COMPAT_QT_VERSION >= 0x030000 + QValueVector<KDChartBaseSeries *>::const_iterator i; +#else + QArray<KDChartBaseSeries *>::ConstIterator i; +#endif + for ( i = (*this).begin(); i != (*this).end(); i ++ ) + { + double temp = (*i)->maxValue(coordinate, ok); + if ( ok && (first_max || temp > result) ) + { + first_max = false; + result = temp; + } + } + + return result; +} + + + +double KDChartSeriesCollection::minValue( int coordinate ) const +{ + // IF there are no series to read from, then just return zero. + // KHZ: perhaps this should assert? + + bool ok = false; // the ok is required in case we check a PlaneSeries, which + // cannot possibly have a min or max on one of the axis. + + double result = 0; // if no valid min/max, then this is the default + + // find the first min +#if COMPAT_QT_VERSION >= 0x030000 + QValueVector<KDChartBaseSeries *>::const_iterator i; +#else + QArray<KDChartBaseSeries *>::ConstIterator i; +#endif + for ( i = (*this).begin(); !ok && i != (*this).end(); i ++ ) + result = (*i)->minValue(coordinate, ok); + + if ( ok ) + for ( ; i != (*this).end(); i ++ ) + { + double temp = (*i)->minValue(coordinate, ok); + if (ok) + result = QMIN( result, temp ); + } + + return result; +} + + +unsigned int KDChartSeriesCollection::indexOf( KDChartBaseSeries *series ) +{ + unsigned int index = 0; +#if COMPAT_QT_VERSION >= 0x030000 + QValueVector<KDChartBaseSeries *>::const_iterator i; +#else + QArray<KDChartBaseSeries *>::ConstIterator i; +#endif + for ( i = (*this).begin(); i != (*this).end(); i ++, index ++ ) + if ( *i == series ) + break; + + // must find it + Q_ASSERT( index < (*this).size() ); + + return index; +} + + +void KDChartSeriesCollection::setLegendText( KDChartBaseSeries *series, QString text ) +{ + _params->setLegendText( indexOf( series ), text ); +} + + + +void KDChartSeriesCollection::setYaxis( KDChartBaseSeries *series, + KDChartAxisParams::AxisPos axis ) +{ + unsigned int index = indexOf( series ); + _params->setAxisDatasets( axis, index, index, 0 ); +} + + + + +QString KDChartSeriesCollection::legendText( KDChartBaseSeries *series ) +{ + return _params->legendText( indexOf(series) ); +} + + +KDChartAxisParams::AxisPos KDChartSeriesCollection::yAxis( KDChartBaseSeries *series ) +{ + unsigned int index = indexOf( series ); + unsigned int tempchart = 0; // needed cause for some reason KHZ wants a reference. + + // now we have to look through + for ( int i = 0; i < KDCHART_MAX_AXES; i ++ ) + if ( _params->axisDatasets( i, index, index, tempchart ) ) + return (KDChartAxisParams::AxisPos) i; + + Q_ASSERT(0); // should find it + return (KDChartAxisParams::AxisPos) 0; +} diff --git a/kchart/kdchart/KDChartSeriesCollection.h b/kchart/kdchart/KDChartSeriesCollection.h new file mode 100644 index 00000000..ac0a1c9b --- /dev/null +++ b/kchart/kdchart/KDChartSeriesCollection.h @@ -0,0 +1,93 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTSERIESCOLLECTION_H__ +#define __KDCHARTSERIESCOLLECTION_H__ + +// to avoid problems in TableBase - it doesn't include this - bug! +#include <qtable.h> + +#include "KDChartTableBase.h" +#include "KDChartBaseSeries.h" +#if COMPAT_QT_VERSION >= 0x030000 +#include <qvaluevector.h> +#else +#include <qarray.h> +#endif +class KDChartParams; +#include "KDChartAxisParams.h" + +// takes ownership of any series passed to it, and will delete +// them when it is deleted. +class KDCHART_EXPORT KDChartSeriesCollection : public KDChartTableDataBase, +#if COMPAT_QT_VERSION >= 0x030000 + public QValueVector<KDChartBaseSeries *> +#else + public QArray<KDChartBaseSeries *> +#endif +{ + // Standard KDChartTableDataBase interface + public: + KDChartSeriesCollection( KDChartParams *params ); + virtual ~KDChartSeriesCollection(); + + virtual uint rows() const; + virtual uint cols() const; + virtual void setUsedRows( uint _rows ); + virtual uint usedRows() const; + virtual void setUsedCols( uint _cols ); + virtual uint usedCols() const; + virtual void setCell( uint _row, uint _col, + const KDChartData& _element ); + virtual const KDChartData& cell( uint _row, uint _col ) const; + virtual void expand( uint _rows, uint _cols ); + + + // Methods we need KHZ to modify in HIS code so we can use our code + // needs to become virtual! + virtual double minValue( int coordinate=1 ) const; + virtual double maxValue( int coordinate=1 ) const; + + + // extra functions for our use + virtual void setLegendText( KDChartBaseSeries *series, QString text ); + virtual QString legendText( KDChartBaseSeries *series ); + + virtual void setYaxis( KDChartBaseSeries *series, + KDChartAxisParams::AxisPos axis );// PENDING(blackie) possible enum problem + virtual KDChartAxisParams::AxisPos yAxis( KDChartBaseSeries *series );// PENDING(blackie) possible enum problem + + virtual unsigned int indexOf( KDChartBaseSeries *series ); + + protected: + KDChartData _blank; + KDChartParams *_params; +}; + + +#endif diff --git a/kchart/kdchart/KDChartTable.h b/kchart/kdchart/KDChartTable.h new file mode 100644 index 00000000..297cb45e --- /dev/null +++ b/kchart/kdchart/KDChartTable.h @@ -0,0 +1,47 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTTABLE_H__ +#define __KDCHARTTABLE_H__ + +// *must* include this one as first one: +#include <KDChartGlobal.h> + +#include <KDChartListTable.h> +#if COMPAT_QT_VERSION >= 0x030000 +#include <KDChartVectorTable.h> +#endif + +#if COMPAT_QT_VERSION >= 0x030000 +typedef KDChartVectorTableData KDChartTableData; +#else +typedef KDChartListTableData KDChartTableData; +#endif + + +#endif diff --git a/kchart/kdchart/KDChartTableBase.cpp b/kchart/kdchart/KDChartTableBase.cpp new file mode 100644 index 00000000..d25e13fc --- /dev/null +++ b/kchart/kdchart/KDChartTableBase.cpp @@ -0,0 +1,779 @@ +/* -*- 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. + ** + **********************************************************************/ +#include <math.h> +#include <limits.h> +#include <qtable.h> + +#include <KDChartTableBase.h> + + +/** + \class KDChartTableDataBase KDChartTableBase.h + \brief Encapsulates all data values that are to be used in a chart. + + \note To create your data table you would <em>not</em> use a + \c KDChartTableDataBase but instantiate the class \c KDChartTableData. + The \c KDChartTableData class is an auxiliary class: depending on your + Qt version it will be mapped onto a \c KDChartVectorTableData or onto + a \c KDChartListTableData both of which are derived from + \c KDChartTableDataBase and implement all of its functions. + Thus you would create a table of 3 datasets with 25 cells each like this: + \verbatim + + KDChartTableData myData( 3, 25 ); + + \endverbatim + + Data values may be specified via \c setCell(). + Cell specific properties may be specified via \c setCellProp(). + + You may adjust or modify your table like this: + + \li Entering the data can be done either manually using \c setCell() + or by passing a QTable to the \c importFromQTable() function. + + \li Performance of KD Chart can be increased by specifying the number + of rows and or the number of columns actually used: \c setUsedRows() + and/or \c setUsedCols() prevents KD Chart from iterating over thousands + of empty rows/cols that might follow your data cells in case your + table is much bigger than needed. + + \li In case you want to increase your table's size without using the + data stored in it please call the \c expand() function with the new + total number of rows and cells. + + \li Accessing one data cell is possible via \c cellCoord() + and via \c cellProp(), + e.g. you might assign a special property set ID to all cells with a + future absicssa axis value: + \verbatim + + const QDateTime currentTime( QDateTime::currentDateTime() ); + for( int iCell = 0; iCell < usedValues; ++iCell ){ + KDChartData& cell = myData.cell( 0, iCell ); + // assign special property set ID if X value is in the future + if( cell.isDateTime( 2 ) && cell.dateTimeValue( 2 ) > currentTime ) + cell.setPropertySet( idProp_FutureValues ); + } + + \endverbatim + + \note All of the other functions provided by KDChartTableDataBase are + either used internally by KD Chart or they are const methods + returning some usefull figures like the sum of all values in a row... +*/ + + +void KDChartTableDataBase::setUsedRows( uint _rows ) { + _usedRows = _rows; + _useUsedRows = true; +} +uint KDChartTableDataBase::usedRows() const { + return _useUsedRows ? _usedRows : rows(); +} +void KDChartTableDataBase::setUsedCols( uint _cols ) { + _usedCols = _cols; + _useUsedCols = true; +} +uint KDChartTableDataBase::usedCols() const { + return _useUsedCols ? _usedCols : cols(); +} + + +bool KDChartTableDataBase::cellsHaveSeveralCoordinates( + QVariant::Type* type2Ref ) const +{ + return cellsHaveSeveralCoordinates( 0, UINT_MAX, type2Ref ); +} + + +bool KDChartTableDataBase::cellsHaveSeveralCoordinates( + uint row1, + uint row2, + QVariant::Type* type2Ref ) const +{ + // return true if all wanted datasets have at least two coordinates + // stored in all of their cells - BUT only if these coordinates are + // of equal type for each of the cells + // note: We skip cells that are empty, this means having + // set neither coordinate #1 nor coordinate #2. + bool severalCoordinates = row1 < usedRows(); + if( severalCoordinates ) { + severalCoordinates = false; + QVariant::Type testType = QVariant::Invalid; + const uint r2 = (UINT_MAX == row2) + ? usedRows() + : QMIN( row2+1, usedRows() ); + QVariant value1; + QVariant value2; + for ( uint row = row1; row < r2; ++row ){ + for ( uint col = 0; col < usedCols(); ++col ){ + if( cellCoords( row, col, value1, value2 ) ){ + if( QVariant::Invalid != value2.type() ){ + if( (QVariant::Invalid != testType) && + (value2.type() != testType) ){ + severalCoordinates = false; + break; + }else{ + testType = value2.type(); + if( NULL != type2Ref ) + *type2Ref = testType; + severalCoordinates = true; + } + }else if( QVariant::Invalid != value1.type() ){ + severalCoordinates = false; + break; + } + } + } + } + } + return severalCoordinates; +} + + +QVariant::Type KDChartTableDataBase::cellsValueType( + uint row1, + uint row2, + int coordinate ) const +{ + QVariant::Type res = QVariant::Invalid; + const uint r2 = (UINT_MAX == row2) + ? usedRows() + : QMIN( row2+1, usedRows() ); + + QVariant value; + for ( uint row = row1; row < r2; ++row ) + for ( uint col = 0; col < usedCols(); ++col ) + if( cellCoord( row, col, value, coordinate ) ) + if( QVariant::Invalid != value.type() ) + res = value.type(); + return res; +} + + +QVariant::Type KDChartTableDataBase::cellsValueType( + int coordinate ) const +{ + return cellsValueType( 0, UINT_MAX, coordinate ); +} + + +double KDChartTableDataBase::maxValue( int coordinate ) const +{ + double maxValue = 0.0; + bool bStart = true; + QVariant value; + double dVal; + for ( uint row = 0; row < usedRows(); row++ ) { + for ( uint col = 0; col < usedCols(); col++ ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::Double == value.type() ) { + dVal = value.toDouble(); + if( isNormalDouble( dVal ) ){ + if ( bStart ) { + maxValue = dVal; + bStart = false; + } else + maxValue = QMAX( maxValue, dVal ); + } + } + } + } + return maxValue; +} + + + +double KDChartTableDataBase::minValue( int coordinate, bool bOnlyGTZero ) const +{ + double minValue = 0.0; + bool bStart = true; + QVariant value; + double dVal; + for ( uint row = 0; row < usedRows(); row++ ) { + for ( uint col = 0; col < usedCols(); col++ ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::Double == value.type() ) { + dVal = value.toDouble(); + if( !bOnlyGTZero || 0.0 < dVal ){ + if ( bStart ) { + minValue = dVal; + bStart = false; + }else{ + minValue = QMIN( minValue, dVal ); + } + } + } + } + } + return minValue; +} + + +QDateTime KDChartTableDataBase::maxDtValue( int coordinate ) const +{ + QDateTime maxValue = QDateTime( QDate(1970,1,1) ); + bool bStart = true; + QVariant value; + QDateTime dtVal; + for ( uint row = 0; row < usedRows(); row++ ) { + for ( uint col = 0; col < usedCols(); col++ ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::DateTime == value.type() ) { + dtVal = value.toDateTime(); + if ( bStart ) { + maxValue = dtVal; + bStart = false; + } else + maxValue = QMAX(maxValue, dtVal); + } + } + } + return maxValue; +} + + + +QDateTime KDChartTableDataBase::minDtValue( int coordinate ) const +{ + QDateTime minValue = QDateTime( QDate(1970,1,1) ); + bool bStart = true; + QVariant value; + QDateTime dtVal; + for ( uint row = 0; row < usedRows(); row++ ) { + for ( uint col = 0; col < usedCols(); col++ ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::DateTime == value.type() ) { + dtVal = value.toDateTime(); + if ( bStart ) { + minValue = dtVal; + bStart = false; + } else + minValue = QMIN(minValue, dtVal); + } + } + } + return minValue; +} + + +double KDChartTableDataBase::maxColSum( int coordinate ) const +{ + double maxValue = 0.0; + bool bStart = true; + for ( uint col = 0; col < usedCols(); col++ ) { + double colValue = colSum( col, coordinate ); + if ( bStart ) { + maxValue = colValue; + bStart = false; + } else + maxValue = QMAX( maxValue, colValue ); + } + return maxValue; +} + + +double KDChartTableDataBase::minColSum( int coordinate ) const +{ + double minValue = 0.0; + bool bStart = true; + for ( uint col = 0; col < usedCols(); col++ ) { + double colValue = colSum( col, coordinate ); + if ( bStart ) { + minValue = colValue; + bStart = false; + } else + minValue = QMIN( minValue, colValue ); + } + + return minValue; +} + + +double KDChartTableDataBase::maxColSum( uint row, uint row2, int coordinate ) const +{ + double maxValue = 0; + bool bStart = true; + if ( 0 < usedRows() ) { + uint a = row; + uint z = row2; + if ( usedRows() <= a ) + a = usedRows() - 1; + if ( usedRows() <= z ) + z = usedRows() - 1; + for ( uint col = 0; col < usedCols(); col++ ) { + double valueValue = 0.0; + QVariant value; + double dVal; + for ( uint row = a; row <= z; row++ ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::Double == value.type() ) { + dVal = value.toDouble(); + if( isNormalDouble( dVal ) ) + valueValue += dVal; + } + } + if ( bStart ) { + maxValue = valueValue; + bStart = false; + } else + maxValue = QMAX( maxValue, valueValue ); + } + } + return maxValue; +} + + +double KDChartTableDataBase::minColSum( uint row, uint row2, int coordinate ) const +{ + double minValue = 0; + bool bStart = true; + if ( 0 < usedRows() ) { + uint a = row; + uint z = row2; + if ( usedRows() <= a ) + a = usedRows() - 1; + if ( usedRows() <= z ) + z = usedRows() - 1; + for ( uint col = 0; col < usedCols(); col++ ) { + double valueValue = 0.0; + QVariant value; + double dVal; + for ( uint row = a; row <= z; row++ ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::Double == value.type() ) { + dVal = value.toDouble(); + if( isNormalDouble( dVal ) ) + valueValue += dVal; + } + } + if ( bStart ) { + minValue = valueValue; + bStart = false; + } else + minValue = QMIN( minValue, valueValue ); + } + } + return minValue; +} + + +double KDChartTableDataBase::colSum( uint col, int coordinate ) const +{ + double sum = 0.0; + QVariant value; + double dVal; + for ( uint row = 0; row < usedRows(); row++ ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::Double == value.type() ) { + dVal = value.toDouble(); + if( isNormalDouble( dVal ) ) + sum += dVal; + } + } + + return sum; +} + + +double KDChartTableDataBase::colAbsSum( uint col, int coordinate ) const +{ + double sum = 0.0; + QVariant value; + double dVal; + for ( uint row = 0; row < usedRows(); row++ ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::Double == value.type() ) { + dVal = value.toDouble(); + if( isNormalDouble( dVal ) ) + sum += fabs( dVal ); + } + } + + return sum; +} + + +double KDChartTableDataBase::maxRowSum( int coordinate ) const +{ + double maxValue = 0.0; + bool bStart = true; + for ( uint row = 0; row < usedRows(); row++ ) { + double rowValue = rowSum( row, coordinate ); + if ( bStart ) { + maxValue = rowValue; + bStart = false; + } else + maxValue = QMAX( maxValue, rowValue ); + } + return maxValue; +} + + +double KDChartTableDataBase::minRowSum( int coordinate ) const +{ + double minValue = 0.0; + bool bStart = true; + for ( uint row = 0; row < usedRows(); row++ ) { + double rowValue = rowSum( row, coordinate ); + if ( bStart ) { + minValue = rowValue; + bStart = false; + } else + minValue = QMIN( minValue, rowValue ); + } + + return minValue; +} + + +double KDChartTableDataBase::rowSum( uint row, int coordinate ) const +{ + double sum = 0.0; + QVariant value; + double dVal; + for ( uint col = 0; col < usedCols(); col++ ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::Double == value.type() ) { + dVal = value.toDouble(); + if( isNormalDouble( dVal ) ) + sum += dVal; + } + } + return sum; +} + + +double KDChartTableDataBase::rowAbsSum( uint row, int coordinate ) const +{ + double sum = 0.0; + QVariant value; + double dVal; + for ( uint col = 0; col < usedCols(); col++ ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::Double == value.type() ) { + dVal = value.toDouble(); + if( isNormalDouble( dVal ) ) + sum += fabs( dVal ); + } + } + return sum; +} + + +double KDChartTableDataBase::maxInColumn( uint col, int coordinate ) const +{ + double maxValue = 0.0; + bool bStart = true; + QVariant value; + double dVal; + for ( uint row = 0; row < usedRows(); row++ ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::Double == value.type() ) { + dVal = value.toDouble(); + if( isNormalDouble( dVal ) ){ + if ( bStart ) { + maxValue = dVal; + bStart = false; + } else + maxValue = QMAX( maxValue, dVal ); + } + } + } + + return maxValue; +} + + +double KDChartTableDataBase::minInColumn( uint col, int coordinate ) const +{ + double minValue = 0.0; + bool bStart = true; + QVariant value; + double dVal; + for ( uint row = 0; row < usedRows(); row++ ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::Double == value.type() ) { + dVal = value.toDouble(); + if( isNormalDouble( dVal ) ){ + if ( bStart ) { + minValue = dVal; + bStart = false; + } else + minValue = QMIN( minValue, dVal ); + } + } + } + + return minValue; +} + + +double KDChartTableDataBase::maxInRow( uint row, int coordinate ) const +{ + double maxValue = DBL_MIN; + bool bStart = true; + QVariant value; + double dVal; + if ( UINT_MAX > row ) { + for ( uint col = 0; col < usedCols(); col++ ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::Double == value.type() ) { + dVal = value.toDouble(); + if( isNormalDouble( dVal ) ){ + if ( bStart ) { + maxValue = dVal; + bStart = false; + } else + maxValue = QMAX( maxValue, dVal ); + } + } + } + } + return maxValue; +} + + +double KDChartTableDataBase::minInRow( uint row, int coordinate ) const +{ + double minValue = DBL_MAX; + bool bStart = true; + QVariant value; + double dVal; + if ( UINT_MAX > row ) { + for ( uint col = 0; col < usedCols(); col++ ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::Double == value.type() ) { + dVal = value.toDouble(); + if( isNormalDouble( dVal ) ){ + if ( bStart ) { + minValue = dVal; + bStart = false; + } else + minValue = QMIN( minValue, dVal ); + } + } + } + } + return minValue; +} + + +double KDChartTableDataBase::maxInRows( uint row, uint row2, int coordinate ) const +{ + double maxValue = 0.0; + bool bStart = true; + if ( 0 < usedRows() ) { + uint a = row; + uint z = row2; + // qDebug("KDChartTableDataBase::maxInRows() (1) a: %u z: %u", a, z); + if ( usedRows() <= a ) + a = usedRows() - 1; + if ( usedRows() <= z ) + z = usedRows() - 1; + // qDebug("KDChartTableDataBase::maxInRows() (2) a: %u z: %u", a, z); + for ( uint row = a; row <= z; ++row ) { + QVariant value; + double dVal; + for ( uint col = 0; col < usedCols(); ++col ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::Double == value.type() ) { + dVal = value.toDouble(); + if( isNormalDouble( dVal ) ){ + if ( bStart ) { + maxValue = dVal; + bStart = false; + } else + maxValue = QMAX( maxValue, dVal ); + } + } + } + } + } + return maxValue; +} + + +double KDChartTableDataBase::minInRows( uint row, uint row2, int coordinate, bool bOnlyGTZero ) const +{ + double minValue = 0.0; + bool bStart = true; + if ( 0 < usedRows() ) { + uint a = row; + uint z = row2; + // qDebug("KDChartTableDataBase::minInRows() (1) a: %u z: %u", a, z); + if ( usedRows() <= a ) + a = usedRows() - 1; + if ( usedRows() <= z ) + z = usedRows() - 1; + //qDebug("KDChartTableDataBase::minInRows() (2) a: %u z: %u", a, z); + for ( uint row = a; row <= z; ++row ) { + QVariant value; + double dVal; + for ( uint col = 0; col < usedCols(); ++col ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::Double == value.type() ) { + dVal = value.toDouble(); + if( isNormalDouble( dVal ) ){ + if( !bOnlyGTZero || 0.0 < dVal ){ + if ( bStart ) { + minValue = dVal; + bStart = false; + }else{ + minValue = QMIN( minValue, dVal ); + } + } + } + } + } + } + } + return minValue; +} + + +QDateTime KDChartTableDataBase::maxDtInRows( uint row, uint row2, + int coordinate ) const +{ + QDateTime maxValue = QDateTime( QDate(1970,1,1) ); + bool bStart = true; + if ( 0 < usedRows() ) { + uint a = row; + uint z = row2; + if ( usedRows() <= a ) + a = usedRows() - 1; + if ( usedRows() <= z ) + z = usedRows() - 1; + for ( uint row = a; row <= z; ++row ) { + QVariant value; + QDateTime dtVal; + for ( uint col = 0; col < usedCols(); ++col ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::DateTime == value.type() ) { + dtVal = value.toDateTime(); + if ( bStart ) { + maxValue = dtVal; + bStart = false; + } else + maxValue = QMAX( maxValue, dtVal ); + } + } + } + } + return maxValue; +} + + +QDateTime KDChartTableDataBase::minDtInRows( uint row, uint row2, + int coordinate ) const +{ + QDateTime minValue = QDateTime( QDate(1970,1,1) ); + bool bStart = true; + if ( 0 < usedRows() ) { + uint a = row; + uint z = row2; + if ( usedRows() <= a ) + a = usedRows() - 1; + if ( usedRows() <= z ) + z = usedRows() - 1; + for ( uint row = a; row <= z; ++row ) { + QVariant value; + QDateTime dtVal; + for ( uint col = 0; col < usedCols(); ++col ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::DateTime == value.type() ) { + dtVal = value.toDateTime(); + if ( bStart ) { + minValue = dtVal; + bStart = false; + } else + minValue = QMIN( minValue, dtVal ); + } + } + } + } + return minValue; +} + + +uint KDChartTableDataBase::lastPositiveCellInColumn( uint col, int coordinate ) const +{ + uint ret = UINT_MAX; + QVariant value; + double dVal; + for ( uint row = 0; row < usedRows(); row++ ) { + if( cellCoord( row, col, value, coordinate ) && + QVariant::Double == value.type() ) { + dVal = value.toDouble(); + if( isNormalDouble( dVal ) && 0 < dVal ) + ret = row; + } + } + return ret; +} + + +void KDChartTableDataBase::importFromQTable( QTable* table ) +{ + if( table->numRows() > (int)rows() || + table->numCols() > (int)cols() ) + expand( table->numRows(), table->numCols() ); + setUsedRows( table->numRows() ); + setUsedCols( table->numCols() ); + for( int row = 0; row < table->numRows(); row++ ) + for( int col = 0; col < table->numCols(); col++ ) { + QString cellContents = table->text( row, col ); + if( !cellContents.isEmpty() ) { + // First try to parse a double + bool ok = false; + double value = cellContents.toDouble( &ok ); + if( ok ) { + // there was a double + setCell( row, col, value ); + } else { + // no double, but at least a string + setCell( row, col, cellContents ); + } + } // don't do anything if no contents + } + setSorted( false ); +} + + +void KDChartTableDataBase::setSorted(bool sorted) +{ + _sorted = sorted; +} +bool KDChartTableDataBase::sorted() const +{ + return _sorted; +} + +#include "KDChartTableBase.moc" diff --git a/kchart/kdchart/KDChartTableBase.h b/kchart/kdchart/KDChartTableBase.h new file mode 100644 index 00000000..50314491 --- /dev/null +++ b/kchart/kdchart/KDChartTableBase.h @@ -0,0 +1,478 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTTABLEINTERFACE_H__ +#define __KDCHARTTABLEINTERFACE_H__ + + +class QTable; + +#include <qvariant.h> +#include <qobject.h> +#include <qdatetime.h> + +#include <kdchart_export.h> + +#if defined( SUN7 ) || defined (_SGIAPI) + #include <float.h> + #include <limits.h> +#else + #include <cfloat> + #include <climits> +#endif + + +/** + Use special value KDCHART_POS_INFINITE to indicate positive infinite values. + + If your own table class (derived from KDChartTableDataBase) does + not store your positive infinite values as KDCHART_POS_INFINITE + please make sure to reimplement \sa isPosInfinite() accordingly. + + \sa isPosInfinite, isNegInfinite +*/ +#define KDCHART_POS_INFINITE DBL_MAX + +/** + Use special value KDCHART_NEG_INFINITE to indicate negative infinite values. + + If your own table class (derived from KDChartTableDataBase) does + not store your negative infinite values as KDCHART_NEG_INFINITE + please make sure to reimplement \sa isNegInfinite() accordingly. + + \sa isNegInfinite, isPosInfinite +*/ +#define KDCHART_NEG_INFINITE -DBL_MAX + + +/** + \file KDChartTableBase.h + + \brief Provides a table class holding all data values + that are to be used in a chart. + + By subclassing KDChartTableDataBase you may provide your own + methods to access data stored somewhere else instead of + using the setCell function to move them into KD Chart's cells. + + \note See the files in doc/tutorial/step07/ for a sample implementation you might want to use as starting-point for your own data handling class. +*/ + +///KD Chart's build-in table data for an easy way of storing data values. +class KDCHART_EXPORT KDChartTableDataBase :public QObject +{ + Q_OBJECT +public: + /** + Default constructor. + + Creates an empty table and sets the sorted flag to false. + */ + KDChartTableDataBase() : + QObject( 0 ), + _sorted(false), + _useUsedRows(false), + _useUsedCols(false) {} + /** + Default copy constructor. + + Just initializes the QObject part of this class and copies the sorted flag. + */ + KDChartTableDataBase( const KDChartTableDataBase& other ) :QObject(0) + { + _sorted = other._sorted; + _useUsedRows = other._useUsedRows; + _useUsedCols = other._useUsedCols; + _usedRows = other._usedRows; + _usedCols = other._usedCols; + } + /** + Default destructor. + + Does nothing, only defined to have it virtual. + */ + virtual ~KDChartTableDataBase() {} + +public slots: + /** + Returns the number of rows in the table. + + \note This pure-virtual function has to be implemented by + each class derived from KDChartTableDataBase. + + \returns the number of rows in the table. + + \sa setRows, usedRows, cols + */ + virtual uint rows() const = 0; + /** + Returns the number of cols in the table. + + \note This pure-virtual function has to be implemented by + each class derived from KDChartTableDataBase. + + \returns the number of cols in the table. + + \sa setCols, usedCols, rows + */ + virtual uint cols() const = 0; + + /** + Stores data in a cell. + + \note This pure-virtual function has to be implemented by + each class derived from KDChartTableDataBase. + + \param _row the row number of the cell to store the data object into. + \param _col the column number of the cell to store the data object into. + \param _value1 the first value to be stored, normally the Y value, possible types: int, double, QString + QString might be used in case you want to use this cell's content for axis label + \param _value2 the second value to be stored, normally the X value (if any), possible types: int, double, QDateTime + + \sa cellCoords, cellContent, setProp + */ + virtual void setCell( uint _row, uint _col, + const QVariant& _value1, + const QVariant& _value2=QVariant() ) = 0; + /** + Specifies the property set ID for a cell. + + \note This pure-virtual function has to be implemented by + each class derived from KDChartTableDataBase. + + \param _row the row number of the cell. + \param _col the column number of the cell. + \param _propSet the property set ID to be stored for this data cell, defaults to zero for normal data. + + \sa cellProp, cellContent, setCell + */ + virtual void setProp( uint _row, uint _col, + int _propSet=0 ) = 0; + + /** + Returns one of the coordinate data value(s) stored in a cell. + + \note This pure-virtual function has to be implemented by + each class derived from KDChartTableDataBase. + + \param _row the row number of the cell to be retrieved. + \param _col the column number of the cell to be retrieved. + \param _value the coordinate variable to be filled by this method. + \param coordinate the number of the coordinate to be retrieved, normally + 1 is the Y value and 2 is the X value. + + \returns TRUE if the row and col are addressing a cell in the table. + + \sa cellCoords, cellProp, cellContent, cellVal, setCell, setProp + */ + virtual bool cellCoord( uint _row, uint _col, + QVariant& _value, + int coordinate=1 ) const = 0; + + /** + Returns one of the coordinate data value(s) stored in a cell. + + This convenience method calls the bool cellCoord() function and returns + the result if it returned successfully - otherwise it returns an invalid QVariant. + + \note If you \em know that a cell is containing valid double data + you may quickly access them like this: +\verbatim const double yVal = data.cellVal( r, c ).toDouble(); +const double xVal = data.cellVal( r, c, 2 ).toDouble(); \endverbatim + + \param _row the row number of the cell to be retrieved. + \param _col the column number of the cell to be retrieved. + \param coordinate the number of the coordinate to be retrieved, normally + 1 is the Y value and 2 is the X value. + + \returns cell contens if the row and col are addressing a cell in the + table, otherwise an invalid QVariant is returned. + + \sa cellCoords, cellProp, cellContent, setCell, setProp + */ + virtual QVariant cellVal( uint _row, uint _col, int coordinate=1 ) const { + QVariant value; + if( cellCoord( _row, _col, value, coordinate ) ) + return value; + else + return QVariant(); + } + + /** + Returns the property set ID stored in a cell. + + \note This pure-virtual function has to be implemented by + each class derived from KDChartTableDataBase. + + \param _prop the property set ID of the cell to be retrieved. + + \returns TRUE if the row and col are addressing a cell in the table. + + \sa cellCoord, cellCoords, cellContent, setCell, setProp + */ + virtual bool cellProp( uint _row, uint _col, + int& _prop ) const = 0; + + /** + Increases the number of rows (and/or columns, resp.) stored in this table. + + \note This pure-virtual function has to be implemented by + each class derived from KDChartTableDataBase. + + \note The old content of the table must be preserved (e.g. by copying + the data into the cells of the new table). + + \param _row the new number of rows. + \param _col the new number of columns. + + \sa cell + */ + virtual void expand( uint _rows, uint _cols ) = 0; + + + + // E N D O F pure-virtual function declarations + + + + /** + \note To improve runtime speed this virtual function + may be reimplemented by classes derived from KDChartTableDataBase. + + \sa cellCoords, cellProp, setCell, setProp + */ + virtual bool cellContent( uint _row, uint _col, + QVariant& _value1, + QVariant& _value2, + int& _prop ) const + { + return cellCoords(_row,_col, _value1,_value2) && + cellProp(_row,_col, _prop); + } + /** + \note To improve runtime speed this virtual function + may be reimplemented by classes derived from KDChartTableDataBase. + + \sa cellCoord, cellProp, cellContent, setCell, setProp + */ + virtual bool cellCoords( uint _row, uint _col, + QVariant& _value1, + QVariant& _value2 ) const + { + return cellCoord(_row,_col, _value1, 1) && + cellCoord(_row,_col, _value2, 2); + } + + + /** + Sets the number of rows in the table that actually contain data. + + \note You might want to re-implement this function in derived classes, + the default implementation just stores the new number of used rows. + + \param _rows the number of rows in the table that actually contain data. + + \sa usedRows, rows, cols + */ + virtual void setUsedRows( uint _rows ); + /** + Returns the number of rows in the table that actually contain data. + + \returns the number of rows in the table that actually contain data. + + \sa setUsedRows, rows, cols + */ + virtual uint usedRows() const; + + /** + Sets the number of cols in the table that actually contain data. + + \note You might want to re-implement this function in derived classes, + the default implementation just stores the new number of used cols. + + \param _cols the number of cols in the table that actually contain data. + + \sa usedCols, rows, cols + */ + virtual void setUsedCols( uint _cols ); + /** + Returns the number of cols in the table that actually contain data. + + \returns the number of cols in the table that actually contain data. + + \sa setUsedCols, rows, cols + */ + virtual uint usedCols() const; + + + /** + Returns the number of cols the table has been scrolled by. + + Default implementation of this always returns zero, so make sure + to return the appropriate value if your class derived from KDChartTableDataBase + is supporting internal data scrolling technics. + + See the KD Chart Programmers Manual for details described in the + Data Scrolling chapter. + + \returns the number of cols the table has been scrolled by. + + \sa cols, rows + */ + virtual uint colsScrolledBy() const + { + return 0; + } + + + /** + Specified whether the table is sorted. + + \sa sorted + */ + virtual void setSorted(bool sorted); + /** + Returns whether the table is sorted. + + \sa setSorted + */ + virtual bool sorted() const; + + /** + Returns true if the given value represents a positive infinite value. + + \note This virtual function may be implemented by + classes derived from KDChartTableDataBase. + This should be done if your data are not stored as + special value KDCHART_POS_INFINITE + to indicate positive infinite values + + \returns TRUE if the value given is a positive infinite value. + */ + virtual bool isPosInfinite( double value ) const + { + return value == KDCHART_POS_INFINITE; + } + + /** + Returns true if the given value represents a negative infinite value. + + \note This virtual function may be implemented by + classes derived from KDChartTableDataBase. + This should be done if your data are not stored as + special value KDCHART_NEG_INFINITE + to indicate negative infinite values + + \returns TRUE if the value given is a negative infinite value. + */ + virtual bool isNegInfinite( double value ) const + { + return value == KDCHART_NEG_INFINITE; + } + + /** + Returns true if the given value represents a normal double value. + + Normal double values are defined as values that are neither positive infinite + nor negative infinite. This method is provided to let derived classed use + their own way to determine when a double value is to be threated as normal. + + \note To improve runtime speed this virtual function + may be reimplemented by classes derived from KDChartTableDataBase. + + \returns TRUE if the value given is neither positive infinite nor negativr infinite. + */ + virtual bool isNormalDouble( double value ) const + { + return !isPosInfinite( value ) && !isNegInfinite( value ); + } + + /** + Returns true if the given QVariant value represents a normal double value. + + This method tests if \c value has type QVariant::Double: if no, it returns false; + if yes, it sets \c dVal accordingly and calls the virtual method + isNormalDouble( double value ). + + \param value The QVariant value to be tested and converted. + \param dVal Points to the double variable to be filled with the converted value. + + \returns TRUE if the value given is neither positive infinite nor negative + infinite, \c value is set to the converted value if the type of \c value + is QVariant::Double, otherwise it is not modified. + */ + bool isNormalDouble( QVariant value, double& dVal ) const + { + if( QVariant::Double != value.type() ) + return false; + dVal = value.toDouble(); + return isNormalDouble( dVal ); + } + + virtual void importFromQTable( QTable* table ); + + virtual double maxValue( int coordinate=1 ) const; + virtual double minValue( int coordinate=1, bool bOnlyGTZero=false ) const; + + virtual QDateTime maxDtValue( int coordinate=1 ) const; + virtual QDateTime minDtValue( int coordinate=1 ) const; + + virtual double maxColSum( int coordinate=1 ) const; + virtual double minColSum( int coordinate=1 ) const; + virtual double maxColSum( uint row, uint row2, int coordinate=1 ) const; + virtual double minColSum( uint row, uint row2, int coordinate=1 ) const; + virtual double colSum( uint col, int coordinate=1 ) const; + virtual double colAbsSum( uint col, int coordinate=1 ) const; + virtual double maxRowSum( int coordinate=1 ) const; + virtual double minRowSum( int coordinate=1 ) const; + virtual double rowSum( uint row, int coordinate=1 ) const; + virtual double rowAbsSum( uint row, int coordinate=1 ) const; + virtual double maxInColumn( uint col, int coordinate=1 ) const; + virtual double minInColumn( uint col, int coordinate=1 ) const; + virtual double maxInRow( uint row, int coordinate=1 ) const; + virtual double minInRow( uint row, int coordinate=1 ) const; + virtual double maxInRows( uint row, uint row2, int coordinate=1 ) const; + virtual double minInRows( uint row, uint row2, int coordinate=1, bool bOnlyGTZero=false ) const; + virtual QDateTime maxDtInRows( uint row, uint row2, int coordinate=1 ) const; + virtual QDateTime minDtInRows( uint row, uint row2, int coordinate=1 ) const; + virtual uint lastPositiveCellInColumn( uint col, int coordinate=1 ) const; + virtual bool cellsHaveSeveralCoordinates(QVariant::Type* type2Ref) const; + virtual bool cellsHaveSeveralCoordinates(uint row1=0, uint row2=UINT_MAX, + QVariant::Type* type2Ref=NULL) const; + virtual QVariant::Type cellsValueType( uint row1, uint row2=UINT_MAX, + int coordinate=1 ) const; + virtual QVariant::Type cellsValueType( int coordinate=1 ) const; +private: + bool _sorted; +protected: + bool _useUsedRows, _useUsedCols; +private: + uint _usedRows, _usedCols; +}; + +#endif diff --git a/kchart/kdchart/KDChartTableDataWrapper.cpp b/kchart/kdchart/KDChartTableDataWrapper.cpp new file mode 100644 index 00000000..6cae581f --- /dev/null +++ b/kchart/kdchart/KDChartTableDataWrapper.cpp @@ -0,0 +1,36 @@ +#include "KDChartTableDataWrapper.h" + +KDChartTableDataWrapper::KDChartTableDataWrapper( KDChartTableData* data ) :QObject(0), _data(data) +{ +} +/* +KDChartData* KDChartTableDataWrapper::cell( uint row, uint col ) +{ + KDChartData& item = _data->cell( row, col ); + return &item; +} + +void KDChartTableDataWrapper::setCell( uint _row, uint _col, double _element ) +{ + // Without this, the user has to wrap the element in a KDChartData + // In C++ this is handled bu default constructors. + _data->setCell( _row, _col, _element ); + +} + +void KDChartTableDataWrapper::setCell( uint _row, uint _col, QString _element ) +{ + // Without this, the user has to wrap the element in a KDChartData + // In C++ this is handled bu default constructors. + _data->setCell( _row, _col, _element ); +} + +void KDChartTableDataWrapper::setCell( uint _row, uint _col, const KDChartData& _element ) +{ + // When overriding setCell above, I also had to override this one. I believe that must be due to a bug in QSA + _data->setCell( _row, _col, _element ); +} + +*/ + +#include "KDChartTableDataWrapper.moc" diff --git a/kchart/kdchart/KDChartTableDataWrapper.h b/kchart/kdchart/KDChartTableDataWrapper.h new file mode 100644 index 00000000..90b557d5 --- /dev/null +++ b/kchart/kdchart/KDChartTableDataWrapper.h @@ -0,0 +1,29 @@ +#ifndef KDCHARTTABLEDATAWRAPPER_H +#define KDCHARTTABLEDATAWRAPPER_H +#include <qobject.h> +#include <KDChartTable.h> +class KDChartData; + +class KDChartTableDataWrapper :public QObject +{ + Q_OBJECT +public: + KDChartTableDataWrapper( KDChartTableData* ); +public slots: +/* + QVariant cell( uint _row, uint _col ); + void setCell( uint _row, uint _col, + const QVariant& _value1, + const QVariant& _value2=QVariant() ); + void setCell( uint _row, uint _col, double _element ); + + // PENDING(blackie) This one do not work, due to QSA bug regarding function overloading. + void setCell( uint _row, uint _col, QString _element ); +*/ +private: + KDChartTableData* _data; +}; + + +#endif /* KDCHARTTABLEDATAWRAPPER_H */ + diff --git a/kchart/kdchart/KDChartTextPiece.cpp b/kchart/kdchart/KDChartTextPiece.cpp new file mode 100644 index 00000000..2b451c2e --- /dev/null +++ b/kchart/kdchart/KDChartTextPiece.cpp @@ -0,0 +1,292 @@ +/* -*- 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. + ** + **********************************************************************/ +#include "KDChartTextPiece.h" + +#include <qstylesheet.h> +#include <qsimplerichtext.h> +#include <qfontmetrics.h> +#include <qpainter.h> +#include <qapplication.h> +#include <qrect.h> + +KDChartTextPiece::KDChartTextPiece() + :QObject(0) +{ + _isRichText = false; + _richText = 0; + + _font = QApplication::font(); + // These three are needed for both + _metrics = new QFontMetrics( _font ); + _dirtyMetrics = true; + _text = QString(""); +} + + +KDChartTextPiece::KDChartTextPiece( const QString& text, const QFont& font ) + :QObject(0) +{ + if( QStyleSheet::mightBeRichText( text ) ) { + _isRichText = true; + _richText = new QSimpleRichText( text, font ); + _richText->adjustSize(); + } else { + _isRichText = false; + _richText = 0; + } + + // These three are needed for both + _metrics = new QFontMetrics( font ); + _dirtyMetrics = true; + _text = text; + _font = font; +} + + +KDChartTextPiece::KDChartTextPiece( QPainter *p, const QString& text, const QFont& font ) + :QObject(0) +{ + + if( QStyleSheet::mightBeRichText( text ) ) { + _isRichText = true; + _richText = new QSimpleRichText( text, font ); + //qDebug( "richtext width %s", QString::number(_richText->width()).latin1()); + //qDebug( "richtext height %s", QString::number(_richText->height()).latin1()); + _richText->adjustSize(); + //qDebug( "richtext width %s", QString::number(_richText->width()).latin1()); + //qDebug( "richtext height %s", QString::number(_richText->height()).latin1()); + + } else { + _isRichText = false; + _richText = 0; + } + + // These three are needed for both + _dirtyMetrics = (p == 0); + if( _dirtyMetrics ) { + _metrics = new QFontMetrics( font ); + //qDebug("dirty metrics text: %s", text.latin1()); + } + else{ + p->save(); + p->setFont( font ); + _metrics = new QFontMetrics( p->fontMetrics() ); + //qDebug ( "drawing metrics text: %s", text.latin1() ); + //p->drawRect( _metrics->boundingRect( text) ); + //p->drawText( _metrics->boundingRect(text).bottomRight(), text); + p->restore(); + } + _text = text; + _font = font; +} + + +void KDChartTextPiece::deepCopy( const KDChartTextPiece* source ) +{ + if( !source || this == source ) + return; + if( _richText ) + delete _richText; + _isRichText = source->_isRichText; + if( source->_richText ) { + _richText = new QSimpleRichText( source->_text, source->_font ); + _richText->adjustSize(); + } + else + _richText = 0; + + // used for both + if( _metrics ) + delete _metrics; + _metrics = new QFontMetrics( *source->_metrics ); + _dirtyMetrics = source->_dirtyMetrics; + _text = source->_text; + _font = source->_font; +} + +const KDChartTextPiece* KDChartTextPiece::clone() const +{ + KDChartTextPiece* newPiece = new KDChartTextPiece(); + newPiece->deepCopy( this ); + return newPiece; +} + + +KDChartTextPiece::~KDChartTextPiece() +{ + if( _richText ) + delete _richText; + if( _metrics ) + delete _metrics; +} + + +int KDChartTextPiece::width() const +{ + if( _isRichText ) + return _richText->widthUsed(); + else + return _metrics->width( _text ); +} + + +int KDChartTextPiece::height() const +{ + + if( _isRichText ) { + //qDebug ("_richText height %s", QString::number(_richText->height()).latin1()); + return _richText->height(); + } + else { + + //qDebug ("_metrics height %s", QString::number(_metrics->height()).latin1()); + return _metrics->height(); + } +} + + +int KDChartTextPiece::fontLeading() const +{ + return _metrics->leading(); +} + +QRect KDChartTextPiece::rect( QPainter *p, const QRect& clipRect) const +{ + QRect rect( clipRect ); + QFont font( _font ); + + if( _isRichText ) { + + // Pending Michel make sure the fonts are not too large + if ( _richText->height() > clipRect.height() || _richText->width() > clipRect.width() ) + font.setPixelSize( QMIN( (int)clipRect.width(),(int)clipRect.height() ) ); + + _richText->setDefaultFont( font ); + _richText->setWidth( p, clipRect.width() ); + rect.setWidth( _richText->width() ); + rect.setHeight( _richText->height() ); + } else + rect = clipRect; + + return rect; +} + +void KDChartTextPiece::draw( QPainter *p, int x, int y, + const QRect& clipRect, + const QColor& color, + const QBrush* paper ) const +{ + + if( _isRichText ) { + + QColorGroup cg; + //calculate the text area before drawing + QRect txtArea = rect( p,clipRect); + cg.setColor( QColorGroup::Text, color ); + _richText->draw( p, txtArea.x(), txtArea.y(),txtArea , cg, paper ); + } else { + p->save(); + p->setFont( _font ); + if( paper ) + p->setBrush( *paper ); + p->setPen( color ); + //dont clip to avoid truncated text + //p->setClipRect( txtArea ); + if( _dirtyMetrics ){ + if( _metrics ) + delete _metrics; + KDChartTextPiece* meNotConst = const_cast<KDChartTextPiece*>(this); + //KDChartTextPiece* meNotConst(const_cast<KDChartTextPiece*>(this)); + meNotConst->_metrics = new QFontMetrics( p->fontMetrics() ); + meNotConst->_dirtyMetrics = false; + } + + p->drawText( x, y + _metrics->ascent(), _text ); + p->restore(); + } +} + + +void KDChartTextPiece::draw( QPainter *p, int x, int y, + const QRegion& clipRegion, + const QColor& color, + const QBrush* paper ) const +{ + if( _isRichText ) { + QColorGroup cg; + cg.setColor( QColorGroup::Text, color ); + _richText->setDefaultFont( _font ); + _richText->setWidth( p, clipRegion.boundingRect().width() ); + _richText->draw( p, x, y, clipRegion, cg, paper ); + } else { + p->save(); + p->setFont( _font ); + if( paper ) + p->setBrush( *paper ); + p->setPen( color ); + p->setClipRegion( clipRegion ); + + if( _dirtyMetrics ){ + if( _metrics ) + delete _metrics; + + // this line does not compile with MSVC++: + // KDChartTextPiece* meNotConst( const_cast<KDChartTextPiece*>(this) ); + KDChartTextPiece* meNotConst = const_cast<KDChartTextPiece*>(this); + + meNotConst->_metrics = new QFontMetrics( p->fontMetrics() ); + meNotConst->_dirtyMetrics = false; + } + + p->drawText( x, y + _metrics->ascent(), _text ); + p->restore(); + } +} + + +QString KDChartTextPiece::text() const +{ + return _text; +} + + +QFont KDChartTextPiece::font() const +{ + return _font; +} + + +bool KDChartTextPiece::isRichText() const +{ + return _isRichText; +} + + + +#include "KDChartTextPiece.moc" diff --git a/kchart/kdchart/KDChartTextPiece.h b/kchart/kdchart/KDChartTextPiece.h new file mode 100644 index 00000000..16d5a1ba --- /dev/null +++ b/kchart/kdchart/KDChartTextPiece.h @@ -0,0 +1,101 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTTEXTPIECE_H__ +#define __KDCHARTTEXTPIECE_H__ + +#include <qstring.h> +#include <qfont.h> +#include <qregion.h> +#include <qpalette.h> +#include <qobject.h> +#include <kdchart_export.h> + +class QBrush; +class QPainter; +class QSimpleRichText; +class QFontMetrics; + +class KDCHART_EXPORT KDChartTextPiece :public QObject +{ + Q_OBJECT + + public: + KDChartTextPiece(); + KDChartTextPiece( const QString& text, const QFont& font ); + KDChartTextPiece( QPainter* painter, const QString& text, const QFont& font ); + virtual ~KDChartTextPiece(); + /** + Copy the settings of text piece \c source into this box. + + \note Use this method instead of using the assignment operator. + + \sa clone + */ + void deepCopy( const KDChartTextPiece* source ); + + + /** + Create a new text piece on the heap, copy the settings stored by + this text piece into the newly created text piece and return + the pointer to the new text piece. + + \note Use this method instead of using the copy constructor. + + \sa deepCopy + */ + const KDChartTextPiece* clone() const; + + private: + KDChartTextPiece( const KDChartTextPiece& ) : QObject(0) {} + + public slots: + virtual int width() const; + virtual int height() const; + virtual int fontLeading() const; + virtual QRect rect( QPainter *p, const QRect& clipRect) const; + virtual void draw( QPainter *p, int x, int y, const QRect& clipRect, + const QColor& color, const QBrush* paper = 0 ) const; + virtual void draw( QPainter *p, int x, int y, const QRegion& clipRegion, + const QColor& color, const QBrush* paper = 0 ) const; + + QString text() const; + QFont font() const; + bool isRichText() const; + + + protected: + bool _isRichText; + QSimpleRichText* _richText; // used when _isRichText == true + QString _text; // used when _isRichText == false + QFont _font; // used when _isRichText == false + QFontMetrics* _metrics; + bool _dirtyMetrics; +}; + +#endif diff --git a/kchart/kdchart/KDChartUnknownTypeException.h b/kchart/kdchart/KDChartUnknownTypeException.h new file mode 100644 index 00000000..8f36a767 --- /dev/null +++ b/kchart/kdchart/KDChartUnknownTypeException.h @@ -0,0 +1,60 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTUNKNOWNTYPEEXCEPTION_H__ +#define __KDCHARTUNKNOWNTYPEEXCEPTION_H__ + +#ifdef USE_EXCEPTIONS + +#include <qstring.h> + +#include "KDChartGlobal.h" + +/** + This exception is thrown when it is attempted to create a chart of + non-registered user-defined type. + */ +class KDCHART_EXPORT KDChartUnknownTypeException +{ + public: + KDChartUnknownTypeException( const QString& type ) : + _type( type ) + {} + + QString type() const + { + return _type; + } + + private: + QString _type; +}; + +#endif + +#endif diff --git a/kchart/kdchart/KDChartVectorSeries.cpp b/kchart/kdchart/KDChartVectorSeries.cpp new file mode 100644 index 00000000..79b43742 --- /dev/null +++ b/kchart/kdchart/KDChartVectorSeries.cpp @@ -0,0 +1,126 @@ +/* -*- 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. + ** + **********************************************************************/ + +#include "KDChartVectorSeries.h" +#include "KDChartSeriesCollection.h" + + +KDChartVectorSeries::~KDChartVectorSeries() +{ +} + + + +uint KDChartVectorSeries::rows() const +{ + return size(); +} + +const KDChartData& KDChartVectorSeries::cell( uint row ) const +{ + Q_ASSERT( row < size() ); + // make it compile on windows using qt232 + return this->at(row); +} + +void KDChartVectorSeries::setCell( uint row, const KDChartData& element) +{ + Q_ASSERT( row < size() ); + // make it compile on windows using qt232 + this->at(row) = element; +} + +void KDChartVectorSeries::expand( uint rows ) +{ + resize(rows); +} + + +// we return !ok if its on the infinite axis +double KDChartVectorSeries::maxValue( int coordinate, bool &ok ) const +{ + double maxValue = 0.0; + bool bStart = true; + +#if COMPAT_QT_VERSION >= 0x030000 + KDChartVectorSeries::const_iterator i; +#else + KDChartVectorSeries::ConstIterator i; +#endif + + for ( i = begin(); i != end(); i ++ ) + { + const KDChartData& d = *i; + if ( d.isDouble( coordinate ) ) + { + if ( bStart ) + { + maxValue = d.doubleValue( coordinate ); + bStart = false; + } + else + maxValue = QMAX( maxValue, d.doubleValue( coordinate ) ); + } + } + + ok = !bStart; + return maxValue; +} + + + +double KDChartVectorSeries::minValue( int coordinate, bool &ok ) const +{ + double minValue = 0.0; + bool bStart = true; + +#if COMPAT_QT_VERSION >= 0x030000 + KDChartVectorSeries::const_iterator i; +#else + KDChartVectorSeries::ConstIterator i; +#endif + + for ( i = begin(); i != end(); i ++ ) + { + const KDChartData& d = *i; + if ( d.isDouble( coordinate ) ) + { + if ( bStart ) + { + minValue = d.doubleValue( coordinate ); + bStart = false; + } + else + minValue = QMIN( minValue, d.doubleValue( coordinate ) ); + } + } + + ok = !bStart; + return minValue; +} diff --git a/kchart/kdchart/KDChartVectorSeries.h b/kchart/kdchart/KDChartVectorSeries.h new file mode 100644 index 00000000..6e6f72e5 --- /dev/null +++ b/kchart/kdchart/KDChartVectorSeries.h @@ -0,0 +1,71 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTVECTORSERIES_H__ +#define __KDCHARTVECTORSERIES_H__ + +// A single data series abstracted. +// Is included in a DataSeriesBag. +// Will be a base class for other series objects, such as DataVectorSeries, +// and my DataQuerySeries. +// +// Requirements: +// - Implement a QValueVector interface + +#include "KDChartBaseSeries.h" +#if COMPAT_QT_VERSION >= 0x030000 +#include <qvaluevector.h> +#else +#include <qarray.h> +#endif +class KDChartSeriesCollection; + + + +class KDCHART_EXPORT KDChartVectorSeries : public KDChartBaseSeries, +#if COMPAT_QT_VERSION >= 0x030000 + public QValueVector<KDChartData> +#else + public QArray<KDChartData> +#endif +{ + public: + virtual ~KDChartVectorSeries(); + + virtual uint rows() const; + virtual const KDChartData& cell( uint row ) const; + virtual void setCell( uint row, const KDChartData& element); + virtual void expand( uint rows ); + + // methods modelled on the TableBase methods, but these + // inherit from BaseSeries. + virtual double maxValue( int coordinate, bool &ok ) const; + virtual double minValue( int coordinate, bool &ok ) const; +}; + +#endif diff --git a/kchart/kdchart/KDChartVectorTable.cpp b/kchart/kdchart/KDChartVectorTable.cpp new file mode 100644 index 00000000..40e5c5bd --- /dev/null +++ b/kchart/kdchart/KDChartVectorTable.cpp @@ -0,0 +1,33 @@ +/* -*- Mode: C++ -*- + KDChart - a multi-platform charting engine +*/ + +/**************************************************************************** + ** Copyright (C) 2006 Inge Wallin. All rights reserved. + ** + ** This file is an addition to 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. + ** + **********************************************************************/ + + +#include "KDChartVectorTable.moc" + +#if 0 // Already defined in the .h file +KDChartVectorTableData::~KDChartVectorTableData() +{ + if ( sh->deref() ) + delete sh; +} +#endif diff --git a/kchart/kdchart/KDChartVectorTable.h b/kchart/kdchart/KDChartVectorTable.h new file mode 100644 index 00000000..b31b0811 --- /dev/null +++ b/kchart/kdchart/KDChartVectorTable.h @@ -0,0 +1,319 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTVECTORTABLE_H__ +#define __KDCHARTVECTORTABLE_H__ + +#include <qvaluevector.h> +#include <qshared.h> +#include <qtable.h> + +#include <KDChartDataIntern.h> +#include <KDChartTableBase.h> + +class KDCHART_EXPORT KDChartVectorTablePrivate : public QShared +{ +public: + KDChartVectorTablePrivate() : QShared() { + row_count = 0; + col_count = 0; + } + + KDChartVectorTablePrivate( uint _rows, uint _cols ) : QShared() { + matrix.resize( _rows * _cols, KDChartData() ); + col_count = _cols; + row_count = _rows; + } + + KDChartVectorTablePrivate( const KDChartVectorTablePrivate& _t ) : + QShared(), + matrix( _t.matrix ), + col_count( _t.col_count ), + row_count( _t.row_count ) {} + + ~KDChartVectorTablePrivate() {} + + void expand( uint _rows, uint _cols ) { + // Save the old table + QValueVector<KDChartData> save( matrix ); + + // Delete old data, then resize + matrix.resize( 0 ); + matrix.resize( _rows * _cols, KDChartData() ); + + // Copy over the old data + for( uint row = 0; row < QMIN( row_count, _rows ); row++ ) + for( uint col = 0; col < QMIN( col_count, _cols ); col++ ) + matrix[ row * _cols + col ].setAll( save[ row * col_count + col ] ); + + // set the new counts + col_count = _cols; + row_count = _rows; + } + + KDChartData& cell( uint _row, uint _col ) { + Q_ASSERT( _row < row_count ); + Q_ASSERT( _col < col_count ); + return matrix[ static_cast < int > ( _row * col_count + _col ) ]; + } + const KDChartData& cell( uint _row, uint _col ) const { + Q_ASSERT( _row < row_count ); + Q_ASSERT( _col < col_count ); + return matrix[ static_cast < int > ( _row * col_count + _col ) ]; + } + void setCell( uint _row, uint _col, const KDChartData& _element ) { + Q_ASSERT( _row < row_count ); + Q_ASSERT( _col < col_count ); + matrix[ static_cast < int > ( _row * col_count + _col ) ].setAll( _element ); + } + + void clearCell( uint _row, uint _col ) { + Q_ASSERT( _row < row_count ); + Q_ASSERT( _col < col_count ); + matrix[ static_cast < int > ( _row * col_count + _col ) ].clearValue(); + } + + void clearAllCells() { + for ( uint r = 0; r < row_count; ++r ) + for ( uint c = 0; c < col_count; ++c ) + matrix[ r * col_count + c ].clearValue(); + } + + QValueVector<KDChartData> matrix; + + uint col_count; + uint row_count; +}; + + +class KDCHART_EXPORT KDChartVectorTableData : public KDChartTableDataBase +{ + Q_OBJECT + +private: + typedef KDChartVectorTablePrivate Priv; + uint _usedRows, _usedCols; + +public: + /** + * Typedefs + */ + typedef QValueVector<KDChartData>::iterator Iterator; + typedef QValueVector<KDChartData>::const_iterator ConstIterator; + + typedef QValueVector<int>::iterator RowIterator; + typedef QValueVector<int>::const_iterator ConstRowIterator; + + typedef QValueVector<int>::iterator ColIterator; + typedef QValueVector<int>::const_iterator ConstColIterator; + + /** + * API + */ + KDChartVectorTableData() : + KDChartTableDataBase() + { + sh = new Priv; + _usedCols = 0; + _usedRows = 0; + } + KDChartVectorTableData( uint _rows, uint _cols ) : + KDChartTableDataBase() + { + sh = new Priv( _rows, _cols ); + _usedRows = _rows; + _usedCols = _cols; + } + + KDChartVectorTableData( const KDChartVectorTableData& _t ) : + KDChartTableDataBase( _t ) { + _useUsedRows = _t._useUsedRows; + _useUsedCols = _t._useUsedCols; + _usedRows = _t._usedRows; + _usedCols = _t._usedCols; + sh = _t.sh; + sh->ref(); + setSorted( _t.sorted() ); + } + + virtual ~KDChartVectorTableData() { + if ( sh->deref() ) + delete sh; + } + + KDChartVectorTableData& operator=( const KDChartVectorTableData& t ) { + if ( &t == this ) + return * this; + _useUsedRows = t._useUsedRows; + _useUsedCols = t._useUsedCols; + _usedRows = t._usedRows; + _usedCols = t._usedCols; + t.sh->ref(); + if ( sh->deref() ) + delete sh; + sh = t.sh; + setSorted( t.sorted() ); + return *this; + } + +public slots: + Iterator begin() { + return sh->matrix.begin(); + } + + ConstIterator begin() const { + return sh->matrix.begin(); + } + + Iterator end() { + return sh->matrix.end(); + } + + ConstIterator end() const { + return sh->matrix.end(); + } + + bool isEmpty() const { + return ( sh->col_count == 0 && sh->row_count == 0 ); + } + + uint cols() const { + return sh->col_count; + } + + uint rows() const { + return sh->row_count; + } + // WARNING: The KDChartData class is an internal class now, + // and nobody supposed to use it any longer. + // Instead, please use cellCoord(), cellProp(), setCell(), setProp(), ... + // (khz, 2006-05-23) +/* + KDChartData& cell( uint _row, uint _col ) { + detach(); + return sh->cell( _row, _col ); + } +*/ + virtual bool cellCoord( uint _row, uint _col, + QVariant& _value, + int coordinate=1 ) const + { + if( _row >= sh->row_count || _col >= sh->col_count ) + return false; + _value = sh->cell( _row, _col ).value( coordinate ); + return true; + } + + virtual bool cellProp( uint _row, uint _col, + int& _prop ) const + { + if( _row >= sh->row_count || _col >= sh->col_count ) + return false; + _prop = sh->cell( _row, _col ).propertySet(); + return true; + } + + virtual void setCell( uint _row, uint _col, + const QVariant& _value1, + const QVariant& _value2=QVariant() ) { + detach(); + const KDChartData element( _value1, _value2 ); + sh->setCell( _row, _col, element ); + } + + virtual void setProp( uint _row, uint _col, + int _propSet=0 ) + { + sh->cell( _row, _col ).setPropertySet( _propSet ); + } + + void clearCell( uint _row, uint _col ) { + detach(); + sh->clearCell( _row, _col ); + } + + void clearAllCells() { + detach(); + sh->clearAllCells(); + } + + void expand( uint _rows, uint _cols ) { + detach(); + sh->expand( _rows, _cols ); + // adjust the usedRows / usedCols, if they had been set before + if( _useUsedCols ) + setUsedCols( QMIN( _usedCols, _cols ) ); + if( _useUsedRows ) + setUsedRows( QMIN( _usedRows, _rows ) ); + } + + void setUsedRows( uint _rows ) { + Q_ASSERT( _rows <= rows() ); + if( _usedRows < _rows ) + setSorted( false ); + _usedRows = _rows; + _useUsedRows = true; + } + + uint usedRows() const { + return _useUsedRows ? _usedRows : rows(); + } + + void setUsedCols( uint _cols ) { + Q_ASSERT( _cols <= cols() ); + if( _usedCols < _cols ) + setSorted( false ); + _usedCols = _cols; + _useUsedCols = true; + } + + uint usedCols() const { + return _useUsedCols ? _usedCols : cols(); + } + +private: + /** + * Helpers + */ + void detach() { + if ( sh->count > 1 ) { + sh->deref(); + sh = new Priv( *sh ); + } + setSorted( false ); + } + + /** + * Variables + */ + Priv* sh; +}; + +#endif +// __KDCHARTLISTTABLE_H__ + diff --git a/kchart/kdchart/KDChartWidget.cpp b/kchart/kdchart/KDChartWidget.cpp new file mode 100644 index 00000000..81164ead --- /dev/null +++ b/kchart/kdchart/KDChartWidget.cpp @@ -0,0 +1,438 @@ +/* -*- 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. + ** + **********************************************************************/ +#include <KDChartWidget.h> +#include <KDChart.h> +#include <KDChartParams.h> +#include <KDChartTableBase.h> +#ifndef KDCHART_MASTER_CVS +#include "KDChartWidget.moc" +#endif + +#include <qpainter.h> + +/** + \class KDChartWidget KDChartWidget.h + + \brief The entry point into the charting that most people will want + to use. + + Simply create a KChartWidget in your application and put it where + you want in your widget hierarchy and create a KChartParams object + that specifies how the chart should be drawn. + + \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 KDChart, KDChartDataRegionList + */ + + +/** + Default Constructor. + + Sets params and data pointers to zero, you should call setParams + and setData before using this chart otherwise only a simple + default bar chart will be shown. + + \param parent the widget parent; passed on to QWidget + \param name the widget name; passed on to QWidget + */ + +KDChartWidget::KDChartWidget( QWidget* parent, const char* name ) : +QWidget( parent, name ), + _params( 0 ), + _data( 0 ), + _activeData( false ), +_mousePressedOnRegion( 0 ) +{ + _dataRegions.setAutoDelete( true ); + setDoubleBuffered( true ); + setBackgroundMode( Qt::NoBackground ); +} + + +/** + Constructor. Stores the chart parameters. + + \param params the specification of the chart + \param data the data to be displayed as a chart + \param parent the widget parent; passed on to QWidget + \param name the widget name; passed on to QWidget + */ + +KDChartWidget::KDChartWidget( KDChartParams* params, + KDChartTableDataBase* data, + QWidget* parent, const char* name ) : +QWidget( parent, name ), + _params( params ), + _data( data ), + _activeData( false ), +_mousePressedOnRegion( 0 ) +{ + _dataRegions.setAutoDelete( true ); + setDoubleBuffered( true ); + setBackgroundMode( Qt::NoBackground ); +} + + +/** + Destructor. + */ +KDChartWidget::~KDChartWidget() +{ + // delete any regions that might still be registered + _dataRegions.clear(); + KDChartAutoColor::freeInstance(); +} + +void KDChartWidget::paintTo( QPainter& painter, + const QRect* rect ) +{ + KDChart::paint( &painter, _params, _data, &_dataRegions, rect ); +} + +void KDChartWidget::print( QPainter& painter, + const QRect* rect ) +{ + bool oldOpt=true; + if( _params ){ + oldOpt = _params->optimizeOutputForScreen(); + _params->setOptimizeOutputForScreen( false ); + } + bool bOldBuf = _doubleBuffered; + _doubleBuffered = false; + paintTo( painter, rect ); + _doubleBuffered = bOldBuf; + if( _params ) + _params->setOptimizeOutputForScreen( oldOpt ); +} + +void KDChartWidget::paintEvent( QPaintEvent* event ) +{ + if( _doubleBuffered ) { + // if double-buffering, paint onto the pixmap and copy + // afterwards + _buffer.fill( backgroundColor() ); + QPainter painter( &_buffer ); + paintTo( painter ); + bitBlt( this, event->rect().topLeft(), &_buffer, event->rect() ); + } else { + // if not double-buffering, paint directly into the window + QPainter painter( this ); + paintTo( painter ); + } +} + + +/** + \internal + */ +void KDChartWidget::mousePressEvent( QMouseEvent* event ) +{ + if ( !_activeData ) + return ; + + _mousePressedOnRegion = 0; + KDChartDataRegion* current = 0; + //QPtrListIterator < KDChartDataRegion > it( _dataRegions ); + for( current = _dataRegions.last(); current; current = _dataRegions.prev() ){ + //while ( ( current = it.current() ) ) { + if ( current->contains( event->pos() ) ) { + _mousePressedOnRegion = current; + if ( event->button() == LeftButton ){ + emit dataLeftPressed( current->row, current->col ); + emit dataLeftPressed( event->pos() ); + }else if ( event->button() == MidButton ){ + emit dataMiddlePressed( current->row, current->col ); + emit dataMiddlePressed( event->pos() ); + }else{ + emit dataRightPressed( current->row, current->col ); + emit dataRightPressed( event->pos() ); + } + return; + } + } +} + + +/** + \internal + */ +void KDChartWidget::mouseReleaseEvent( QMouseEvent* event ) +{ + if ( !_activeData ) + return ; + + KDChartDataRegion* current = 0; + QPtrListIterator < KDChartDataRegion > it( _dataRegions ); + while ( ( current = it.current() ) ) { + ++it; + if ( current->contains( event->pos() ) ) { + if ( event->button() == LeftButton ) { + emit dataLeftReleased( current->row, current->col ); + emit dataLeftReleased( event->pos() ); + if ( _mousePressedOnRegion == current ){ + emit dataLeftClicked( current->row, current->col ); + emit dataLeftClicked( event->pos() ); + } + } else if ( event->button() == MidButton ) { + emit dataMiddleReleased( current->row, current->col ); + emit dataMiddleReleased( event->pos() ); + if ( _mousePressedOnRegion == current ){ + emit dataMiddleClicked( current->row, current->col ); + emit dataMiddleClicked( event->pos() ); + } + } else { + emit dataRightReleased( current->row, current->col ); + emit dataRightReleased( event->pos() ); + if ( _mousePressedOnRegion == current ){ + emit dataRightClicked( current->row, current->col ); + emit dataRightClicked( event->pos() ); + } + } + } + } +} + + +/** + \internal + */ +void KDChartWidget::resizeEvent( QResizeEvent* /*event*/ ) +{ + // if we use double-buffering, resize the buffer to the new size, + // otherwise leave it alone + if( _doubleBuffered ) + _buffer.resize( size() ); +} + + +/** + If \a active is true, this widget reports mouse presses, releases + and clicks on the data segments it displays. This can slow down the + display process, so this is turned off by default. + + If active data reporting is turned on when the widget is already + shown, data will be reported after the next repaint(). Call + repaint() explicitly if necessary. + + Active data is currently supported for bar, pie, and line charts + (the latter only with markers, as trying to hit the line would be + too difficult for the user anyway). + + \param active if true, the widget reports mouse events + \sa isActiveData() + */ +void KDChartWidget::setActiveData( bool active ) +{ + _activeData = active; +} + + +/** + Returns true if the widget is configured to report mouse + events. The default is not to report mouse events. + + \return true if the widget is configured to report mouse events, + false otherwise + \sa setActiveData() + */ +bool KDChartWidget::isActiveData() const +{ + return _activeData; +} + + +/** + If \a doublebuffered is true, the widget will double-buffer + everything while drawing which reduces flicker a lot, but requires + more memory as an off-screen buffer of the same size as the widget + needs to be kept around. However, in most cases, it is worth + spending the extra memory. Double-buffering is on by + default. Turning double-buffering on or off does not trigger a + repaint. + + \param doublebuffered if true, turns double-buffering on, if false, + turns double-buffering off + \sa isDoubleBuffered + */ +void KDChartWidget::setDoubleBuffered( bool doublebuffered ) +{ + _doubleBuffered = doublebuffered; + if( doublebuffered ) { + // turn double-buffering on + // resize the buffer to the size of the widget + _buffer.resize( size() ); + } else { + // turn double-buffering off + // minimize the buffer so that it does not take any memory + _buffer.resize( 0, 0 ); + } +} + + +/** + Returns whether the widget uses double-buffering for drawing. See + \a setDoubleBuffered() for an explanation of double-buffering. + + \return true if double-buffering is turned on, false otherwise + */ +bool KDChartWidget::isDoubleBuffered() const +{ + return _doubleBuffered; +} + + +/** + Set an entire new parameter set. + (Normally you might prefer modifying the existing parameters + rather than specifying a new set.) + */ +void KDChartWidget::setParams( KDChartParams* params ) +{ + _params = params; +} + +/** + Set an entire new data table. + */ +void KDChartWidget::setData( KDChartTableDataBase* data ) +{ + _data = data; +} + +/** + Returns a pointer to the current parameter set. + */ +KDChartParams* KDChartWidget::params() const +{ + return _params; +} + +/** + Returns a pointer to the current data table. + */ +KDChartTableDataBase* KDChartWidget::data() const +{ + return _data; +} + +/** + \fn void KDChartWidget::barsDisplayed( int barsDisplayed, int barsLeft ) + + This signal is emitted when drawing of a bar chart is done. + Use it to determine if all bars have been drawn: in case + you specified both the bar width and the value block gap width + it might be that KD Chart is not able to display all bars since + they do not fit into the available horizontal space. + + The value of barsLeft indicates how many bars could not be + drawn because the data area was not wide enough. + + \sa KDChartParams::numBarsDisplayed, KDChartParams::numBarsLeft + */ + +/** + \fn void KDChartWidget::dataLeftClicked( uint row, uint col ) + + This signal is emitted when a data item was clicked onto with the left mouse button. + + The values of row / col indicate the respective dataset (row) and item (col). + + \note There is another signal sent simultaneously: reporting the screen coordinates clicked onto. + + \sa dataLeftReleased + \sa dataRightClicked, dataMiddleClicked + */ +/** + \fn void KDChartWidget::dataRightClicked( uint row, uint col ) + + This signal is emitted when a data item was clicked onto with the right mouse button. + + The values of row / col indicate the respective dataset (row) and item (col). + + \note There is another signal sent simultaneously: reporting the screen coordinates clicked onto. + + \sa dataRightReleased + \sa dataLeftClicked, dataMiddleClicked + */ +/** + \fn void KDChartWidget::dataMiddleClicked( uint row, uint col ) + + This signal is emitted when a data item was clicked onto with the middle mouse button. + + The values of row / col indicate the respective dataset (row) and item (col). + + \note There is another signal sent simultaneously: reporting the screen coordinates clicked onto. + + \sa dataMiddleReleased + \sa dataLeftClicked, dataRightClicked + */ + +/** + \fn void KDChartWidget::dataLeftClicked( const QPoint & pnt ) + + This signal is emitted when a data item was clicked onto with the left mouse button. + + The value of pnt indicates the screen coordinates in relation to the origin of the data area. + + \note There is another signal sent simultaneously: reporting which data item was clicked onto. + + \sa dataLeftReleased + \sa dataRightClicked, dataMiddleClicked + */ +/** + \fn void KDChartWidget::dataRightClicked( const QPoint & pnt ) + + This signal is emitted when a data item was clicked onto with the right mouse button. + + The values of row / col indicate the screen coordinates in relation to the origin of the data area. + + \note There is another signal sent simultaneously: reporting which data item was clicked onto. + + \sa dataRightReleased + \sa dataLeftClicked, dataMiddleClicked + */ +/** + \fn void KDChartWidget::dataMiddleClicked( const QPoint & pnt ) + + This signal is emitted when a data item was clicked onto with the middle mouse button. + + The values of row / col indicate the screen coordinates in relation to the origin of the data area. + + \note There is another signal sent simultaneously: reporting which data item was clicked onto. + + \sa dataMiddleReleased + \sa dataLeftClicked, dataRightClicked + */ diff --git a/kchart/kdchart/KDChartWidget.h b/kchart/kdchart/KDChartWidget.h new file mode 100644 index 00000000..d117daa0 --- /dev/null +++ b/kchart/kdchart/KDChartWidget.h @@ -0,0 +1,120 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDCHARTWIDGET_H__ +#define __KDCHARTWIDGET_H__ + +#include <KDChartGlobal.h> +#include <KDChartTable.h> +#include <KDChartDataRegion.h> +#include <qwidget.h> +#include <qscrollview.h> +#include <qpixmap.h> + +/** + \file KDChartWidget.h + + \brief Provids the entry point into the charting that most + people will want to use. + */ + +class KDChartParams; + +class KDCHART_EXPORT KDChartWidget : public QWidget +{ + Q_OBJECT + Q_PROPERTY( bool activeData READ isActiveData WRITE setActiveData ) + Q_PROPERTY( bool doubleBuffered READ isDoubleBuffered WRITE setDoubleBuffered ) + +public: + KDChartWidget( QWidget* parent = 0, const char* name = 0 ); + KDChartWidget( KDChartParams* params, + KDChartTableDataBase* data, + QWidget* parent = 0, const char* name = 0 ); + ~KDChartWidget(); + +public slots: + bool isActiveData() const; + bool isDoubleBuffered() const; + + KDChartParams* params() const; + KDChartTableDataBase* data() const; + + const KDChartDataRegionList* dataRegions() const { return &_dataRegions; } + + void setActiveData( bool active ); + void setDoubleBuffered( bool doublebuffered ); + void setParams( KDChartParams* params ); + void setData( KDChartTableDataBase* data ); + // use this method to paint to low resolution devices + void paintTo( QPainter& painter, + const QRect* rect = 0 ); + // use this method to paint to high res devices like printers... + void print( QPainter& painter, + const QRect* rect = 0 ); + + + +signals: + void dataLeftClicked( uint row, uint col ); + void dataLeftClicked( const QPoint & pnt ); + void dataMiddleClicked( uint row, uint col ); + void dataMiddleClicked( const QPoint & pnt ); + void dataRightClicked( uint row, uint col ); + void dataRightClicked( const QPoint & pnt ); + void dataLeftPressed( uint row, uint col ); + void dataLeftPressed( const QPoint & pnt ); + void dataMiddlePressed( uint row, uint col ); + void dataMiddlePressed( const QPoint & pnt ); + void dataRightPressed( uint row, uint col ); + void dataRightPressed( const QPoint & pnt ); + void dataLeftReleased( uint row, uint col ); + void dataLeftReleased( const QPoint & pnt ); + void dataMiddleReleased( uint row, uint col ); + void dataMiddleReleased( const QPoint & pnt ); + void dataRightReleased( uint row, uint col ); + void dataRightReleased( const QPoint & pnt ); + void barsDisplayed( int barsDisplayed, int barsLeft ); + +protected: + virtual void paintEvent( QPaintEvent* event ); + virtual void mousePressEvent( QMouseEvent* event ); + virtual void mouseReleaseEvent( QMouseEvent* event ); + virtual void resizeEvent( QResizeEvent* event ); + +private: + KDChartParams* _params; + KDChartTableDataBase* _data; + bool _activeData; + bool _doubleBuffered; + QPixmap _buffer; + KDChartDataRegion* _mousePressedOnRegion; + KDChartDataRegionList _dataRegions; +}; + +#endif diff --git a/kchart/kdchart/KDChartWrapperFactory.cpp b/kchart/kdchart/KDChartWrapperFactory.cpp new file mode 100644 index 00000000..430ef3a4 --- /dev/null +++ b/kchart/kdchart/KDChartWrapperFactory.cpp @@ -0,0 +1,33 @@ +#include "KDChartWrapperFactory.h" +#include <KDChartTable.h> +#include <qfont.h> +#include "wrappers/KDChartParamsWrapper.h" +#include "wrappers/KDChartTableDataWrapper.h" +#include "wrappers/KDChartCustomBoxWrapper.h" +#include <KDChartCustomBox.h> +#include "wrappers/KDChartAxisParamsWrapper.h" + +KDChartWrapperFactory::KDChartWrapperFactory() +{ + registerWrapper( "KDChartParams", "KDChartParamsWrapper" ); + registerWrapper( "KDChartVectorTableData", "KDChartTableDataWrapper" ); + registerWrapper( "KDChartCustomBox", "KDChartCustomBoxWrapper" ); + registerWrapper( "KDChartAxisParams", "KDChartAxisParamsWrapper" ); +} + +QObject* KDChartWrapperFactory::create( const QString& className, void* ptr ) +{ + if ( className == QString::fromLatin1( "KDChartParams" ) ) + return new KDChartParamsWrapper( static_cast<KDChartParams*>( ptr ) ); + + if ( className == QString::fromLatin1( "KDChartVectorTableData" ) ) + return new KDChartTableDataWrapper( static_cast<KDChartTableData*>( ptr ) ); + + if ( className == QString::fromLatin1( "KDChartCustomBox" ) ) + return new KDChartCustomBoxWrapper( static_cast<KDChartCustomBox*>( ptr ) ); + + if ( className == QString::fromLatin1( "KDChartAxisParams" ) ) + return new KDChartAxisParamsWrapper( static_cast<KDChartAxisParams*>( ptr ) ); + + return 0; +} diff --git a/kchart/kdchart/KDChartWrapperFactory.h b/kchart/kdchart/KDChartWrapperFactory.h new file mode 100644 index 00000000..aa072770 --- /dev/null +++ b/kchart/kdchart/KDChartWrapperFactory.h @@ -0,0 +1,13 @@ +#ifndef KDCHARTWRAPPERFACTORY_H +#define KDCHARTWRAPPERFACTORY_H +#include <qswrapperfactory.h> + +class KDChartWrapperFactory :public QSWrapperFactory +{ +public: + KDChartWrapperFactory(); + virtual QObject* create ( const QString& className, void* ptr ); +}; + +#endif /* KDCHARTWRAPPERFACTORY_H */ + diff --git a/kchart/kdchart/KDDrawText.cpp b/kchart/kdchart/KDDrawText.cpp new file mode 100644 index 00000000..4b9467f7 --- /dev/null +++ b/kchart/kdchart/KDDrawText.cpp @@ -0,0 +1,483 @@ +/* -*- 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. + ** + **********************************************************************/ +#include <qpainter.h> +#include <qbitmap.h> +#include <qpixmap.h> +#include <math.h> +#include <limits.h> + +#include <KDDrawText.h> + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +void KDDrawText::drawRotatedText( QPainter* painter, + float degrees, + QPoint anchor, + const QString& text, + const QFont* font, + int align, + bool showAnchor, + const QFontMetrics* fontMet, + bool noFirstrotate, + bool noBackrotate, + KDDrawTextRegionAndTrueRect* infos, + bool optimizeOutputForScreen ) +{ + drawRotatedTxt( painter, + optimizeOutputForScreen, + degrees, + anchor, + text, + font, + align, + showAnchor, + INT_MAX, + INT_MAX, + fontMet, + false, + 0 != infos, + noFirstrotate, + noBackrotate, + infos ); +} + + +KDDrawTextRegionAndTrueRect KDDrawText::measureRotatedText( + QPainter* painter, + float degrees, + QPoint anchor, + const QString& text, + const QFont* font, + int align, + const QFontMetrics* fontMet, + bool noFirstrotate, + bool noBackrotate, + int addPercentOfHeightToRegion ) +{ + KDDrawTextRegionAndTrueRect infos; + drawRotatedTxt( painter, + false, + degrees, + anchor, + text, + font, + align, + false, + INT_MAX, + INT_MAX, + fontMet, + true, + false, + noFirstrotate, + noBackrotate, + &infos, + addPercentOfHeightToRegion ); + return infos; +} + + +void KDDrawText::drawRotatedTxt( QPainter* painter, + bool optimizeOutputForScreen, + float degrees, + QPoint anchor, + const QString& text, + const QFont* font, + int align, + bool showAnchor, + int txtWidth, + int txtHeight, + const QFontMetrics* fontMet, + bool calculateOnly, + bool doNotCalculate, + bool noFirstrotate, + bool noBackrotate, + KDDrawTextRegionAndTrueRect* infos, + int addPercentOfHeightToRegion ) +{ + +// showAnchor=true; + //qDebug("\nanchor: "+ text + " / "+QString::number(anchor.x()) + // +" / "+QString::number(anchor.y())); + bool useInfos = doNotCalculate && infos; + bool fontChanged = ( 0 != font ); + QFont oldFont; + if( fontChanged ) { + oldFont = painter->font(); + painter->setFont( *font ); + } + else + font = &painter->font(); + + bool mustBackrotate = false; + if( !optimizeOutputForScreen && !noFirstrotate ){ + painter->rotate( degrees ); + if( !noBackrotate ) + mustBackrotate = true; + } + + QPoint pos = useInfos ? infos->pos : painter->xFormDev( anchor ); + + if( useInfos ) + { + txtWidth = infos->width; + txtHeight = infos->height; + } + else + { + int newHeight=0; + + // a bug in the AIX 5.2 compiler means using (?:) syntax doesn't work here + // therefor we do it the following way: + QFontMetrics* pFM=0; + if( ! pFM ) { + pFM = new QFontMetrics( painter->fontMetrics() ); + } else { + pFM = const_cast<QFontMetrics*>(fontMet); + } + + int nLF = text.contains('\n'); + if( INT_MAX == txtWidth ) { + if( nLF ){ + int tw; + txtWidth = 0; + int i0 = 0; + int iLF = text.find('\n'); + while( -1 != iLF ){ + const QRect r(pFM->boundingRect( text.mid(i0, iLF-i0) )); + tw = r.width()+ 2; + newHeight = r.height(); + if( tw > txtWidth ) + txtWidth = tw; + i0 = iLF+1; + iLF = text.find('\n', i0); + } + if( iLF < (int)text.length() ){ + const QRect r(pFM->boundingRect( text.mid( i0 ) )); + tw = r.width()+2; + newHeight = r.height(); + if( tw > txtWidth ) + txtWidth = tw; + i0 = iLF+1; + } + }else{ + const QRect r(painter->boundingRect( 0,0,1,1, Qt::AlignAuto, text )); + // correct width and height before painting with 2 unit to avoid truncating. + // PENDING Michel - improve + txtWidth = r.width()+2; + newHeight = r.height()+2; + } + } + if( INT_MAX == txtWidth || INT_MAX == txtHeight ) { + txtHeight = newHeight ? newHeight : pFM->height() * (1+nLF); + } + if( pFM != fontMet ) + delete pFM; + if( infos ) { + infos->pos = pos; + // PENDING infos + infos->width = txtWidth; + infos->height = txtHeight; + } + } + if( showAnchor ) { + int d = txtHeight/4; + QPen savePen = painter->pen(); + painter->setPen( QColor( Qt::darkRed ) ); + painter->drawLine( pos.x(), pos.y()-d, + pos.x(), pos.y()+d ); + painter->drawLine( pos.x()-d, pos.y(), + pos.x()+d, pos.y() ); + painter->setPen( savePen ); + } + int x = useInfos ? infos->x : pos.x(); + int y = useInfos ? infos->y : pos.y(); + //qDebug("1.: (x / y) :" + text + " / "+QString::number(x) + // +" / "+QString::number(y)); + //qDebug("2.: (posx / posy) :" + text ); + // qDebug ( "%d", pos.x() ); qDebug ( "%d", pos.y() ); + //qDebug("3.: (infosx / infosy) :" + text + " / "+QString::number(infos->x) + // +" / "+QString::number(infos->y)); + + if( !useInfos && !optimizeOutputForScreen ) { + switch( align & ( Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter ) ) { + case Qt::AlignLeft: + break; + case Qt::AlignRight: +//qDebug( QPaintDeviceMetrics::logicalDpiX() ); + x -= txtWidth; + break; + case Qt::AlignHCenter: + x -= txtWidth - txtWidth/2; + break; + } + switch( align & ( Qt::AlignTop | Qt::AlignBottom | Qt::AlignVCenter ) ) { + case Qt::AlignTop: + break; + case Qt::AlignBottom: + y -= txtHeight; + break; + case Qt::AlignVCenter: + y -= txtHeight/2; + break; + } + } + if( infos && !useInfos ) { + painter->xForm( pos ); + infos->x = x - 4; + infos->y = y - 4; + //PENDING Michel updating info using x , y from pos + //qDebug("4.: (infosx / infosy) :" + text + " / "+QString::number(infos->x) + //+" / "+QString::number(infos->y)); + //qDebug("5.: (x / y) :" + text + " / "+QString::number(x) + // +" / "+QString::number(y)); + //qDebug("6.: (anchorx /anchory) :" + text + " / "+QString::number(x) + // +" / "+QString::number(y)); + QRect rect( painter->boundingRect( x, y, + txtWidth, txtHeight, + Qt::AlignLeft + Qt::AlignTop, + text ) ); + //painter->fillRect (rect, Qt::blue ); + + QPoint topLeft( painter->xForm( rect.topLeft() ) ); + QPoint topRight( painter->xForm( rect.topRight() ) ); + QPoint bottomRight( painter->xForm( rect.bottomRight() ) ); + QPoint bottomLeft( painter->xForm( rect.bottomLeft() ) ); + + int additor = addPercentOfHeightToRegion * txtHeight / 100; + QPointArray points; + points.setPoints( 4, topLeft.x()-additor, topLeft.y()-additor, + topRight.x()+additor, topRight.y()-additor, + bottomRight.x()+additor, bottomRight.y()+additor, + bottomLeft.x()-additor, bottomLeft.y()+additor ); + infos->region = QRegion( points ); + } + + // When the Qt initialization bug is fixed the following scope + // will be put into an "if( showAnchor )" entirely. + { + int d = txtHeight/4; + QPen savePen = painter->pen(); + if( showAnchor ) { + painter->setPen( QColor( Qt::blue ) ); + painter->drawLine( x, y-d, + x, y+d ); + painter->drawLine( x-d, y, + x+d, y ); + painter->setPen( QColor( Qt::darkGreen ) ); + painter->drawRect(x,y,txtWidth,txtHeight); + //painter->drawText( x, y-d, text); + +/* + }else{ + // Working around a strange Qt bug: Rotated painter must be + // initialized by drawing before text can be painted there. + painter->setPen( QColor( Qt::white ) ); + painter->drawLine( 30000,0,30001,0 ); +*/ + } + painter->setPen( savePen ); + } + + if( mustBackrotate && optimizeOutputForScreen ){ + painter->rotate( -degrees ); + mustBackrotate = false; + } + + if( !calculateOnly ){ + //qDebug("txtWidth: %i txtHeight: %i", txtWidth, txtHeight); + if( !optimizeOutputForScreen ){ +/* + painter->drawText( x, y, + txtWidth, txtHeight, + Qt::AlignLeft + Qt::AlignTop, + text ); +*/ + painter->drawText( x, y, + txtWidth, txtHeight, + Qt::AlignLeft + Qt::AlignTop, + text ); +/* + painter->drawText( x, y, + text, + -1, + Qt::AlignRight + Qt::AlignTop ); +*/ + }else{ + // new code (rotating the text ourselves for better quality on screens) + QPixmap pm( txtWidth+2, txtHeight+2, 1 ); + // note: When using colored axis labels it will be necessary + // to change this code and use a 256 color pixmap instead + // of a monochrome one. (khz, 2002/08/15) + pm.fill(Qt::color0); + QPainter p; + p.begin( &pm ); + if( showAnchor ){ + p.drawRect(0,0, txtWidth,txtHeight); + p.drawLine(0,0, txtWidth,txtHeight); + p.drawLine(0,txtHeight, txtWidth,0); + } + p.setFont(painter->font()); + + p.drawText( 0, 0, txtWidth, txtHeight, + Qt::AlignLeft + Qt::AlignTop, + text ); +/* + p.drawText( 0,0, + text, + -1, + Qt::AlignLeft + Qt::AlignTop ); +*/ + + QBitmap mask; + mask = pm; + pm.setMask( mask ); + QWMatrix m; + m.rotate( degrees ); + QPixmap theRotatedPixmap = pm.xForm(m); + + // where are our four corner points now: + double degreesRad = degrees; + while( degreesRad > 360 ) + degreesRad -= 360; + degreesRad *= M_PI / 180.0; + double cosA = cos( degreesRad ); + double sinA = sin( degreesRad ); + QPoint pTopLeft( 0, + 0 ); + QPoint pBotLeft( static_cast < int > ( 0 * cosA - txtHeight * sinA ), + static_cast < int > ( txtHeight * cosA + 0 * sinA ) ); + QPoint pTopRight( static_cast < int > ( txtWidth * cosA - 0 * sinA ), + static_cast < int > ( 0 * cosA + txtWidth * sinA ) ); + QPoint pBotRight( static_cast < int > ( txtWidth * cosA - txtHeight * sinA ), + static_cast < int > ( txtHeight * cosA + txtWidth * sinA ) ); + + // make our four corner points relative + // to the bounding rect of the rotated pixmap + { + QPoint pDeltaTL( QMIN(0, QMIN(pBotLeft.x(), QMIN(pTopRight.x(), pBotRight.x()))), + QMIN(0, QMIN(pBotLeft.y(), QMIN(pTopRight.y(), pBotRight.y()))) ); + pTopLeft -= pDeltaTL; + pBotLeft -= pDeltaTL; + pTopRight -= pDeltaTL; + pBotRight -= pDeltaTL; + } + + /* + painter->setPen( QColor( Qt::black ) ); + painter->drawLine( x-13, y, x+13, y ); + painter->drawLine( x, y-13, x, y+13 ); + painter->setPen( QColor( Qt::blue ) ); + painter->drawLine( x+pTopLeft.x()-3, y+pTopLeft.y(), x+pTopLeft.x()+3, y+pTopLeft.y() ); + painter->drawLine( x+pTopLeft.x(), y+pTopLeft.y()-3, x+pTopLeft.x(), y+pTopLeft.y()+3 ); + painter->setPen( QColor( Qt::red ) ); + painter->drawLine( x+pTopRight.x()-3, y+pTopRight.y(), x+pTopRight.x()+3, y+pTopRight.y() ); + painter->drawLine( x+pTopRight.x(), y+pTopRight.y()-3, x+pTopRight.x(), y+pTopRight.y()+3 ); + painter->setPen( QColor( Qt::green ) ); + painter->drawLine( x+pBotLeft.x()-3, y+pBotLeft.y(), x+pBotLeft.x()+3, y+pBotLeft.y() ); + painter->drawLine( x+pBotLeft.x(), y+pBotLeft.y()-3, x+pBotLeft.x(), y+pBotLeft.y()+3 ); + painter->setPen( QColor( Qt::yellow ) ); + painter->drawLine( x+pBotRight.x()-3, y+pBotRight.y(), x+pBotRight.x()+3, y+pBotRight.y() ); + painter->drawLine( x+pBotRight.x(), y+pBotRight.y()-3, x+pBotRight.x(), y+pBotRight.y()+3 ); + */ + + // The horizontal and vertical alignment together define one of + // NINE possible points: this point must be moved on the anchor. + int hAlign = align & ( Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter ); + int vAlign = align & ( Qt::AlignTop | Qt::AlignBottom | Qt::AlignVCenter ); + + QPoint pixPoint; + switch( hAlign ) { + case Qt::AlignLeft: + switch( vAlign ) { + case Qt::AlignTop: + pixPoint = pTopLeft; + break; + case Qt::AlignBottom: + pixPoint = pBotLeft; + break; + case Qt::AlignVCenter: + default: + pixPoint = QPoint( (pTopLeft.x() + pBotLeft.x()) / 2, + (pTopLeft.y() + pBotLeft.y()) / 2 ); + break; + } + break; + case Qt::AlignRight: + switch( vAlign ) { + case Qt::AlignTop: + pixPoint = pTopRight; + break; + case Qt::AlignBottom: + pixPoint = pBotRight; + break; + case Qt::AlignVCenter: + default: + pixPoint = QPoint( (pTopRight.x() + pBotRight.x()) / 2, + (pTopRight.y() + pBotRight.y()) / 2 ); + break; + } + break; + case Qt::AlignHCenter: + default: + switch( vAlign ) { + case Qt::AlignTop: + pixPoint = QPoint( (pTopLeft.x() + pTopRight.x()) / 2, + (pTopLeft.y() + pTopRight.y()) / 2 ); + break; + case Qt::AlignBottom: + pixPoint = QPoint( (pBotLeft.x() + pBotRight.x()) / 2, + (pBotLeft.y() + pBotRight.y()) / 2 ); + break; + case Qt::AlignVCenter: + default: + pixPoint = QPoint( (pTopLeft.x() + pBotRight.x()) / 2, + (pTopLeft.y() + pBotRight.y()) / 2 ); + break; + } + break; + } + //qDebug("2.: (x / y) : "+QString::number(x) + // +" / "+QString::number(y)); + painter->drawPixmap( QPoint( x - pixPoint.x(), + y - pixPoint.y() ), + theRotatedPixmap ); + p.end(); + } + } + + if( mustBackrotate ) + painter->rotate( -degrees ); + + if( fontChanged ) + painter->setFont( oldFont ); +} + + diff --git a/kchart/kdchart/KDDrawText.h b/kchart/kdchart/KDDrawText.h new file mode 100644 index 00000000..64337e5a --- /dev/null +++ b/kchart/kdchart/KDDrawText.h @@ -0,0 +1,102 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDDRAWTEXT_H__ +#define __KDDRAWTEXT_H__ + +#include <qpainter.h> +#ifdef linux +#include <values.h> +#endif +#include "KDChartGlobal.h" + + +struct KDDrawTextRegionAndTrueRect +{ + QRegion region; + QPoint pos; + int x,y,width,height; +}; + + +class KDDrawText +{ + public: + static void drawRotatedText( QPainter* painter, + float degrees, + QPoint anchor, + const QString& text, + const QFont* font = 0, + int align = Qt::AlignLeft | Qt::AlignTop, + // useful for debugging of your layout: + bool showAnchor = false, + const QFontMetrics* fontMet = 0, + bool noFirstrotate = false, + bool noBackrotate = false, + KDDrawTextRegionAndTrueRect* infos = 0, + bool optimizeOutputForScreen = false ); + + static KDDrawTextRegionAndTrueRect measureRotatedText( + QPainter* painter, + float degrees, + QPoint anchor, + const QString& text, + const QFont* font, + int align, + const QFontMetrics* fontMet, + bool noFirstrotate, + bool noBackrotate, + int addPercentOfHeightToRegion ); + + private: + static void drawRotatedTxt( QPainter* painter, + bool optimizeOutputForScreen, + float degrees, + QPoint anchor, + const QString& text, + const QFont* font = 0, + int align = Qt::AlignLeft | Qt::AlignTop, + // usefull for debugging of your layout: + bool showAnchor = false, + // speed-up parameters + // used internally by drawCircleText() + // to avoid duplicate calculation + int txtWidth = INT_MAX, + int txtHeight = INT_MAX, + const QFontMetrics* fontMet = 0, + // additional speed-up parameters used by KDChart + bool calculateOnly = false, + bool doNotCalculate= false, + bool noFirstrotate = false, + bool noBackrotate = false, + KDDrawTextRegionAndTrueRect* infos = 0, + int addPercentOfHeightToRegion = 0 ); + +}; + +#endif diff --git a/kchart/kdchart/KDFrame.cpp b/kchart/kdchart/KDFrame.cpp new file mode 100644 index 00000000..a8a18339 --- /dev/null +++ b/kchart/kdchart/KDFrame.cpp @@ -0,0 +1,618 @@ +/* -*- Mode: C++ -*- + KD Tools - a set of useful widgets for Qt +*/ + +/**************************************************************************** +** Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools 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 KD Tools licenses may use this file in +** accordance with the KD Tools 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 KD Tools Commercial License Agreements. +** +** Contact info@klaralvdalens-datakonsult.se if any conditions of this +** licensing are not clear to you. +** +**********************************************************************/ + +#include <qpainter.h> +#include <qbrush.h> + +#include <KDFrame.h> +#include <KDFrameProfileSection.h> +#include <KDXMLTools.h> + +#if defined( SUN7 ) || defined( _SGIAPI ) +#include <math.h> +#else +#include <cmath> +#endif +#ifdef Q_WS_WIN +#define M_PI 3.14159265358979323846 +#endif + + +KDFrame::~KDFrame() +{ + _profileSections.clear(); // is ignored if auto-deletion is disabled +} + +KDFrame::KDFrameCorner::~KDFrameCorner() +{ + // Intentionally left blank for now. +} + + + + + +void KDFrame::paintBackground( QPainter& painter, const QRect& innerRect ) const +{ + /* first draw the brush (may contain a pixmap)*/ + if( Qt::NoBrush != _background.style() ) { + QPen oldPen( painter.pen() ); + QPoint oldOrig( painter.brushOrigin() ); + QBrush oldBrush( painter.brush() ); + painter.setPen( Qt::NoPen ); + const QPoint newTopLeft( painter.xForm( innerRect.topLeft() ) ); + painter.setBrushOrigin( newTopLeft.x(), newTopLeft.y() ); + painter.setBrush( _background ); + painter.drawRect( innerRect.x(), innerRect.y(), + innerRect.width(), innerRect.height() ); + painter.setPen( oldPen ); + painter.setBrushOrigin( oldOrig ); + painter.setBrush( oldBrush ); + } + /* next draw the backPixmap over the brush */ + if( ! _backPixmap.isNull() ) { + QPoint ol = innerRect.topLeft(); + if( PixCentered == _backPixmapMode ) + { + ol.setX( innerRect.center().x() - _backPixmap.width() / 2 ); + ol.setY( innerRect.center().y() - _backPixmap.height()/ 2 ); + bitBlt( painter.device(), ol, &_backPixmap ); + } else { + QWMatrix m; + double zW = (double)innerRect.width() / (double)_backPixmap.width(); + double zH = (double)innerRect.height() / (double)_backPixmap.height(); + switch ( _backPixmapMode ) { + case PixCentered: + break; + case PixScaled: { + double z; + z = QMIN( zW, zH ); + m.scale( z, z ); + } + break; + case PixStretched: + m.scale( zW, zH ); + break; + } + QPixmap pm = _backPixmap.xForm( m ); + ol.setX( innerRect.center().x() - pm.width() / 2 ); + ol.setY( innerRect.center().y() - pm.height()/ 2 ); + bitBlt( painter.device(), ol, &pm ); + } + } +} + + +void KDFrame::paintEdges( QPainter& painter, const QRect& innerRect ) const + { + +/* + Note: The following code OF COURSE is only dummy-code and will be replaced. + + At the moment it is used to draw the simple frames which were set by + the setSimpleFrame() function. +*/ + if( !_topProfile.isEmpty() ){ + + KDFrameProfileSection* section; + for ( section = ((KDFrameProfile)_topProfile).last(); section; section = ((KDFrameProfile)_topProfile).prev() ){ + const QPen oldPen = painter.pen(); + const QBrush oldBrush = painter.brush(); + QPen thePen; + thePen = section->pen(); + int penWidth = QMAX(thePen.width(), 1) * QMAX(section->width(), 1); +//qDebug("paintEdges: thePen.width() = %i", thePen.width()); +//qDebug("paintEdges: section->width() = %i", section->width()); +//qDebug("paintEdges: penWidth = %i", penWidth); + thePen.setWidth( penWidth ); + painter.setPen( thePen ); + painter.setBrush( Qt::NoBrush ); + painter.drawRect( innerRect.x()-penWidth, + innerRect.y()-penWidth, + innerRect.width() +2*penWidth, + innerRect.height() +2*penWidth ); + painter.setBrush( oldBrush ); + painter.setPen( oldPen ); + } + } +} + + +void KDFrame::paintCorners( QPainter& /*painter*/, const QRect& /*innerRect*/ ) const +{ + // At the moment nothing is done here since the setSimpleFrame() function + // does not allow specifying corners: all predefined frames have normal + // (default) corners. + ; +} + + +void KDFrame::paint( QPainter* painter, + KDFramePaintSteps steps, + QRect innerRect ) const +{ + if( painter ) { + const QRect& rect = innerRect.isValid() ? innerRect : _innerRect; + switch( steps ) { + case PaintBackground: + paintBackground( *painter, rect ); + break; + case PaintEdges: + paintEdges( *painter, rect ); + break; + case PaintCorners: + paintCorners( *painter, rect ); + break; + case PaintBorder: + paintEdges( *painter, rect ); + paintCorners( *painter, rect ); + break; + case PaintAll: + paintBackground( *painter, rect ); + paintEdges( *painter, rect ); + paintCorners( *painter, rect ); + break; + } + } +} + + +void KDFrame::clearProfile( ProfileName name ) +{ + switch( name ) { + case ProfileTop: _topProfile.clear(); + break; + case ProfileRight: _rightProfile.clear(); + break; + case ProfileBottom:_bottomProfile.clear(); + break; + case ProfileLeft: _leftProfile.clear(); + break; + } +} + +void KDFrame::addProfileSection( ProfileName name, + int wid, + QPen pen, + KDFrameProfileSection::Direction dir, + KDFrameProfileSection::Curvature curv ) +{ + switch( name ) { + case ProfileTop: _topProfile.append( new KDFrameProfileSection( dir, curv, wid, pen ) ); + break; + case ProfileRight: _rightProfile.append( new KDFrameProfileSection( dir, curv, wid, pen ) ); + break; + case ProfileBottom:_bottomProfile.append(new KDFrameProfileSection( dir, curv, wid, pen ) ); + break; + case ProfileLeft: _leftProfile.append( new KDFrameProfileSection( dir, curv, wid, pen ) ); + break; + } +} + +void KDFrame::setProfile( ProfileName name, const KDFrameProfile& profile ) +{ + switch( name ) { + case ProfileTop: _topProfile = profile; + break; + case ProfileRight: _rightProfile = profile; + break; + case ProfileBottom:_bottomProfile = profile; + break; + case ProfileLeft: _leftProfile = profile; + break; + } +} + +const KDFrameProfile& KDFrame::profile( ProfileName name ) const +{ + switch( name ) { + case ProfileTop: return _topProfile; + break; + case ProfileRight: return _rightProfile; + break; + case ProfileBottom:return _bottomProfile; + break; + default: return _leftProfile; + } + + return _leftProfile; +} + + +void KDFrame::setSimpleFrame( SimpleFrame frame, + int lineWidth, + int midLineWidth, + QPen pen, + QBrush background, + const QPixmap* backPixmap, + BackPixmapMode backPixmapMode ) +{ + _profileSections.clear(); + _topProfile.clear(); + _rightProfile.clear(); + _bottomProfile.clear(); + _leftProfile.clear(); + _background = background; + _backPixmap = backPixmap ? *backPixmap : QPixmap(); + _backPixmapMode = backPixmapMode; + if( FrameFlat == frame ) { + //qDebug("_profileSections.count() before = %i", _profileSections.count()); + KDFrameProfileSection* newsection = + new KDFrameProfileSection( KDFrameProfileSection::DirPlain, + KDFrameProfileSection::CvtPlain, + lineWidth, pen ); + _profileSections.append( newsection ); + //qDebug( "_profileSections.count() after = %i, lineWidth = %i", + // _profileSections.count(), + // lineWidth ); + _topProfile.append( newsection ); + _rightProfile = _topProfile; + _bottomProfile = _topProfile; + _leftProfile = _topProfile; + } + else { + switch( frame ) { + case FrameElegance: { + int line1 = lineWidth / 8; + int line2 = lineWidth / 16; + int line3 = line2; + int gap2 = line2 * 4; + int gap1 = lineWidth - line1 - line2 - line3 - gap2; + QPen noP( Qt::NoPen ); + KDFrameProfileSection* newsection; + newsection = new KDFrameProfileSection( KDFrameProfileSection::DirPlain, + KDFrameProfileSection::CvtPlain, + line1, pen ); + _profileSections.append( newsection ); + _topProfile.append( newsection ); + newsection = new KDFrameProfileSection( KDFrameProfileSection::DirPlain, + KDFrameProfileSection::CvtPlain, + gap1, noP ); + _profileSections.append( newsection ); + _topProfile.append( newsection ); + newsection = new KDFrameProfileSection( KDFrameProfileSection::DirPlain, + KDFrameProfileSection::CvtPlain, + line2, pen ); + _profileSections.append( newsection ); + _topProfile.append( newsection ); + newsection = new KDFrameProfileSection( KDFrameProfileSection::DirPlain, + KDFrameProfileSection::CvtPlain, + gap2, noP ); + _profileSections.append( newsection ); + _topProfile.append( newsection ); + newsection = new KDFrameProfileSection( KDFrameProfileSection::DirPlain, + KDFrameProfileSection::CvtPlain, + line3, pen ); + _profileSections.append( newsection ); + _topProfile.append( newsection ); + } + break; + case FrameBoxRaized: + { + KDFrameProfileSection* newsection; + newsection = new KDFrameProfileSection( KDFrameProfileSection::DirRaising, + KDFrameProfileSection::CvtPlain, + lineWidth, pen ); + _profileSections.append( newsection ); + _topProfile.append( newsection ); + newsection = new KDFrameProfileSection( KDFrameProfileSection::DirPlain, + KDFrameProfileSection::CvtPlain, + midLineWidth, pen ); + _profileSections.append( newsection ); + _topProfile.append( newsection ); + newsection = new KDFrameProfileSection( KDFrameProfileSection::DirSinking, + KDFrameProfileSection::CvtPlain, + lineWidth, pen ); + _profileSections.append( newsection ); + _topProfile.append( newsection ); + break; + } + case FrameBoxSunken: + { + KDFrameProfileSection* newsection; + newsection = new KDFrameProfileSection( KDFrameProfileSection::DirSinking, + KDFrameProfileSection::CvtPlain, + lineWidth, pen ); + _profileSections.append( newsection ); + _topProfile.append( newsection ); + newsection = new KDFrameProfileSection( KDFrameProfileSection::DirPlain, + KDFrameProfileSection::CvtPlain, + midLineWidth, pen ); + _profileSections.append( newsection ); + _topProfile.append( newsection ); + newsection = new KDFrameProfileSection( KDFrameProfileSection::DirRaising, + KDFrameProfileSection::CvtPlain, + lineWidth, pen ); + _profileSections.append( newsection ); + _topProfile.append( newsection ); + } + break; + case FramePanelRaized: + { + KDFrameProfileSection* newsection; + newsection = new KDFrameProfileSection( KDFrameProfileSection::DirRaising, + KDFrameProfileSection::CvtPlain, + lineWidth, pen ); + _profileSections.append( newsection ); + _topProfile.append( newsection ); + break; + } + case FramePanelSunken: + { + KDFrameProfileSection* newsection; + newsection = new KDFrameProfileSection( KDFrameProfileSection::DirSinking, + KDFrameProfileSection::CvtPlain, + lineWidth, pen ); + _profileSections.append( newsection ); + _topProfile.append( newsection ); + } + break; + case FrameSemicircular: + { + KDFrameProfileSection* newsection; + newsection = new KDFrameProfileSection( KDFrameProfileSection::DirRaising, + KDFrameProfileSection::CvtConvex, + lineWidth, pen ); + _profileSections.append( newsection ); + _topProfile.append( newsection ); + newsection = new KDFrameProfileSection( KDFrameProfileSection::DirPlain, + KDFrameProfileSection::CvtPlain, + midLineWidth, pen ); + _profileSections.append( newsection ); + _topProfile.append( newsection ); + newsection = new KDFrameProfileSection( KDFrameProfileSection::DirSinking, + KDFrameProfileSection::CvtConcave, + lineWidth, pen ); + _profileSections.append( newsection ); + _topProfile.append( newsection ); + } + break; + default: + break; + } + } + _rightProfile = _topProfile; + _bottomProfile = _topProfile; + _leftProfile = _topProfile; + setCorners( CornerNormal, 0 ); +} + + +void KDFrame::createFrameNode( QDomDocument& document, QDomNode& parent, + const QString& elementName, + const KDFrame& frame ) +{ + QDomElement frameElement = document.createElement( elementName ); + parent.appendChild( frameElement ); + KDXML::createIntNode( document, frameElement, "ShadowWidth", + frame._shadowWidth ); + KDXML::createStringNode( document, frameElement, "CornerName", + cornerNameToString( frame._sunPos ) ); + + KDXML::createBrushNode( document, frameElement, "Background", + frame._background ); + KDXML::createPixmapNode( document, frameElement, "BackPixmap", + frame._backPixmap ); + KDXML::createStringNode( document, frameElement, "BackPixmapMode", + backPixmapModeToString( frame._backPixmapMode ) ); + + KDXML::createRectNode( document, frameElement, "InnerRect", + frame._innerRect ); + createFrameProfileNode( document, frameElement, "TopProfile", + frame._topProfile ); + createFrameProfileNode( document, frameElement, "RightProfile", + frame._rightProfile ); + createFrameProfileNode( document, frameElement, "BottomProfile", + frame._bottomProfile ); + createFrameProfileNode( document, frameElement, "LeftProfile", + frame._leftProfile ); + KDFrameCorner::createFrameCornerNode( document, frameElement, "CornerTL", + frame._cornerTL ); + KDFrameCorner::createFrameCornerNode( document, frameElement, "CornerTR", + frame._cornerTR ); + KDFrameCorner::createFrameCornerNode( document, frameElement, "CornerBL", + frame._cornerBL ); + KDFrameCorner::createFrameCornerNode( document, frameElement, "CornerBR", + frame._cornerBR ); +} + +void KDFrame::createFrameProfileNode( QDomDocument& document, QDomNode& parent, + const QString& elementName, + KDFrameProfile profile ) +{ + QDomElement profileElement = document.createElement( elementName ); + parent.appendChild( profileElement ); + for( const KDFrameProfileSection* section = profile.first(); section != 0; + section = profile.next() ) + KDFrameProfileSection::createFrameProfileSectionNode( document, + profileElement, + "ProfileSection", + section ); +} + + +void KDFrame::KDFrameCorner::createFrameCornerNode( QDomDocument& document, + QDomNode& parent, + const QString& elementName, + const KDFrameCorner& corner ) +{ + QDomElement cornerElement = document.createElement( elementName ); + parent.appendChild( cornerElement ); + KDXML::createStringNode( document, cornerElement, "Style", + KDFrame::cornerStyleToString( corner._style ) ); + KDXML::createIntNode( document, cornerElement, "Width", + corner._width ); + createFrameProfileNode( document, cornerElement, "Profile", + corner._profile ); +} + +bool KDFrame::readFrameNode( const QDomElement& element, KDFrame& frame ) +{ + bool ok = true; + int tempShadowWidth; + CornerName tempCornerName=CornerTopLeft; + QBrush tempBackground; + QPixmap tempBackPixmap; + BackPixmapMode tempBackPixmapMode=PixStretched; + QRect tempInnerRect; + KDFrameProfile tempTopProfile, tempRightProfile, + tempBottomProfile, tempLeftProfile; + KDFrameCorner tempCornerTL, tempCornerTR, tempCornerBL, tempCornerBR; + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "ShadowWidth" ) { + ok = ok & KDXML::readIntNode( element, tempShadowWidth ); + } else if( tagName == "CornerName" ) { + QString value; + ok = ok & KDXML::readStringNode( element, value ); + tempCornerName = stringToCornerName( value ); + } else if( tagName == "Background" ) { + ok = ok & KDXML::readBrushNode( element, tempBackground ); + } else if( tagName == "BackPixmap" ) { + ok = ok & KDXML::readPixmapNode( element, tempBackPixmap ); + } else if( tagName == "BackPixmapMode" ) { + QString value; + ok = ok & KDXML::readStringNode( element, value ); + tempBackPixmapMode = stringToBackPixmapMode( value ); + } else if( tagName == "InnerRect" ) { + ok = ok & KDXML::readRectNode( element, tempInnerRect ); + } else if( tagName == "TopProfile" ) { + ok = ok & readFrameProfileNode( element, tempTopProfile ); + } else if( tagName == "RightProfile" ) { + ok = ok & readFrameProfileNode( element, tempRightProfile ); + } else if( tagName == "BottomProfile" ) { + ok = ok & readFrameProfileNode( element, tempBottomProfile ); + } else if( tagName == "LeftProfile" ) { + ok = ok & readFrameProfileNode( element, tempLeftProfile ); + } else if( tagName == "CornerTL" ) { + ok = ok & KDFrameCorner::readFrameCornerNode( element, + tempCornerTL ); + } else if( tagName == "CornerTR" ) { + ok = ok & KDFrameCorner::readFrameCornerNode( element, + tempCornerTR ); + } else if( tagName == "CornerBL" ) { + ok = ok & KDFrameCorner::readFrameCornerNode( element, + tempCornerBL ); + } else if( tagName == "CornerBR" ) { + ok = ok & KDFrameCorner::readFrameCornerNode( element, + tempCornerBR ); + } else { + qDebug( "Unknown tag in frame" ); + } + } + node = node.nextSibling(); + } + + if( ok ) { + frame._shadowWidth = tempShadowWidth; + frame._sunPos = tempCornerName; + frame._background = tempBackground; + frame._backPixmap = tempBackPixmap; + frame._backPixmapMode = tempBackPixmapMode; + frame._innerRect = tempInnerRect; + frame._topProfile = tempTopProfile; + frame._rightProfile = tempRightProfile; + frame._bottomProfile = tempBottomProfile; + frame._leftProfile = tempLeftProfile; + frame._cornerTL = tempCornerTL; + frame._cornerTR = tempCornerTR; + frame._cornerBL = tempCornerBL; + frame._cornerBR = tempCornerBR; + } + + return ok; +} + + +bool KDFrame::readFrameProfileNode( const QDomElement& element, + KDFrameProfile& profile ) +{ + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "ProfileSection" ) { + KDFrameProfileSection* section = new KDFrameProfileSection(); + KDFrameProfileSection::readFrameProfileSectionNode( element, + section ); + profile.append( section ); + } else { + qDebug( "Unknown tag in double map" ); + return false; + } + } + node = node.nextSibling(); + } + + return true; +} + + +bool KDFrame::KDFrameCorner::readFrameCornerNode( const QDomElement& element, + KDFrameCorner& corner ) +{ + bool ok = true; + CornerStyle tempStyle=CornerNormal; + int tempWidth; + KDFrameProfile tempProfile; + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Style" ) { + QString value; + ok = ok & KDXML::readStringNode( element, value ); + tempStyle = stringToCornerStyle( value ); + } else if( tagName == "Width" ) { + ok = ok & KDXML::readIntNode( element, tempWidth ); + } else if( tagName == "Profile" ) { + KDFrameProfile profile; + ok = ok & readFrameProfileNode( element, profile ); + } else { + qDebug( "Unknown tag in frame" ); + } + } + node = node.nextSibling(); + } + + if( ok ) { + corner._style = tempStyle; + corner._width = tempWidth; + corner._profile = tempProfile; + } + + return ok; +} + +#include "KDFrame.moc" diff --git a/kchart/kdchart/KDFrame.h b/kchart/kdchart/KDFrame.h new file mode 100644 index 00000000..bbc49155 --- /dev/null +++ b/kchart/kdchart/KDFrame.h @@ -0,0 +1,1167 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDFRAME_H__ +#define __KDFRAME_H__ + +#include <qapplication.h> +#include <qfont.h> +#include <qcolor.h> +#include <qpixmap.h> +#include <qpen.h> +#include <qmap.h> +#include <qobject.h> +#include <qtextstream.h> +#include <qdom.h> + +#include <KDFrameProfileSection.h> + + +/** \file KDFrame.h + \brief Header of the KDFrame class providing highly configurable rectangular frame drawing. + + \ifnot v200 + This class is work in progress, at present only single line frame borders + and (scaled/streched) background pictures are available. + See KDChartParams::setSimpleFrame() to learn how to use them. + \else + A frame may consist of an (optional) border and/or an (optional) background. + The border may consist of up to four edges and/or up to four border corners. + + \note Each of the edges and each of the corners may use their own user-definable profile. + + The frame corners ( see types defined in \c CornerStyle ) are set up via setCorner() + + Some commonly used frame profiles (e.g. raised box, sunken panel) are predefined in \c SimpleFrame, see setSimpleFrame(). + To learn how to specify your frame profiles have a look at example code given with setProfile() + \endif + */ + + +/** + \brief The main class of KDFrame. + + KDFrame is made for highly configurable rectangular frame drawing. + + A frame may consist of an (optional) border and/or an (optional) background. + \ifndef v200 + This class is work in progress, at present only single line frame borders + and (scaled/streched) background pictures are available. + See KDChartParams::setSimpleFrame() to learn how to use them. + \else + The border may consist of up to four edges and/or up to four border corners. + + \note Each of the edges and each of the corners may use their own user-definable profile. + + The frame corners ( see types defined in CornerStyle ) are set up via setCorner() + + Some commonly used frame profiles (e.g. raised box, sunken panel) are predefined in SimpleFrame, see setSimpleFrame(). + To learn how to specify your frame profiles have a look at example code given with setProfile() + \endif + */ +class KDCHART_EXPORT KDFrame : public QObject +{ + Q_OBJECT + Q_ENUMS( BackPixmapMode ) + Q_ENUMS( SimpleFrame ) + Q_ENUMS( ProfileName ) + Q_ENUMS( CornerName ) + Q_ENUMS( CornerStyle ) + Q_ENUMS( KDFramePaintSteps ) + +public: + //// GENERAL + + + /** + These are ways how to display a pixmap that might + be painted into the inner area. + + \Note To have a 'tiled' background image do not use a background + pixmap but use a background <b>brush</b> holding this pixmap. + */ + enum BackPixmapMode { PixCentered, PixScaled, PixStretched }; + +public slots: + + /** + Converts the specified background pixmap mode enum to a string + representation. + + \param type the background pixmap mode to convert + \return the string representation of the background pixmap mode enum + */ + static QString backPixmapModeToString( BackPixmapMode type ) { + switch( type ) { + case PixCentered: + return "Centered"; + case PixScaled: + return "Scaled"; + case PixStretched: + return "Stretched"; + } + + return "Stretched"; + } + + + /** + Converts the specified string to a background pixmap mode enum value. + + \param string the string to convert + \return the background pixmap mode enum value + */ + static BackPixmapMode stringToBackPixmapMode( const QString& string ) { + if( string == "Centered" ) + return PixCentered; + else if( string == "Scaled" ) + return PixScaled; + else if( string == "Stretched" ) + return PixStretched; + + return PixStretched; + } + + +public: + + + /** + These simple frames are pre-defined for your convenience + + \li \c FrameFlat a flat rectangular frame + \li \c FrameElegance a flat frame consisting of three lines + \li \c FrameBoxRaized a raised box + \li \c FrameBoxSunken a sunken box + \li \c FramePanelRaized a raised panel + \li \c FramePanelSunken a sunken panel + \li \c FrameSemicircular a raised box with round edges + + \ifnot v200 + All pre-defined frames have normal corners. + \else + All pre-defined frames have normal corners but + of course you may specify differently looking corners by + calling setCorners() (or setCorner(), resp.) after having + called setSimpleFrame() + + \sa setSimpleFrame + \endif + */ + enum SimpleFrame { FrameFlat, FrameElegance, + FrameBoxRaized, FrameBoxSunken, + FramePanelRaized, FramePanelSunken, + FrameSemicircular }; + +public slots: + + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Select a pre-defined frame. + This methode is provided for convenience, the same results can be obtained by calling + addProfileSection multiple times specifying the appropriate parameters and finally + setting the corners to \c CornerNormal + + \note When using FrameElegance the midLineWidth value will be ignored since + in this special case the lineValue will indicate the <b>total width</b> of the frame. + FrameElegance frames look best when lineWidth is 16 or the multiple of 16. + + \Note To have a 'tiled' background image do not use a background + pixmap but use a background <b>brush</b> holding this pixmap. + + See setProfile for a short example using this method. + + \param frame one of the values defined for enum \c SimpleFrame + \param lineWidth for FrameFlat and FrameElegance: the complete frame width; + for frames with 2-line edges (FramePanelRaized, FramePanelSunken): the width of each of the lines; + for frames with 3-line edges (FrameBoxRaized, FrameBoxSunken, FrameSemicircular): the width of the outer and the + width of the inner line. + \param midLineWidth only used for frames with 3-line edges: the width + of the middle line. + \param pen the basic pen to be used for this frame's lines + \param background the brush to be used for the frame's background or QBrush() if the background is not to be filled by a brush + \param backPixmap if not zero points to a pixmap to be used for the background + \param backPixmapMode determines how the size pixmap is adjusted to the frame size, + see \c BackPixmapMode for the possible values. + + \sa SimpleFrame, BackPixmapMode, addProfileSection, setProfile, profile, setCorners, setCorner, cornerStyle, cornerWidth + \endif + */ + void setSimpleFrame( SimpleFrame frame, + int lineWidth, + int midLineWidth, + QPen pen, + QBrush background = QBrush(), + const QPixmap* backPixmap = 0, + BackPixmapMode backPixmapMode = PixStretched ); + +public: + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Names of the four frame edges + + \li \c ProfileTop + \li \c ProfileRight + \li \c ProfileBottom + \li \c ProfileLeft + + \sa setSimpleFrame, clearProfile, addProfileSection, setProfile, profile + \endif + */ + enum ProfileName { ProfileTop, ProfileRight, + ProfileBottom, ProfileLeft }; + +public slots: + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Add another section to one of the frames profiles. + + \note The sections will be drawn in the order in which they were added to the profile + beginning at the outside and ending with the inner section of the frame + + See setProfile for a short example using this method. + + \sa ProfileName, setSimpleFrame, addProfileSection, setProfile, profile + \endif + */ + void clearProfile( ProfileName name ); + + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Add another section to one of the frames profiles. + + \note The sections will be drawn in the order in which they were added to the profile + beginning at the outside and ending with the inner section of the frame. + + Adding a gap between two sections can be done by specifying a <b>QPen( Qt::NoPen )</b>. + + See setProfile for a short example using this method. + + \sa ProfileName, setSimpleFrame, clearProfile, setProfile, profile + \endif + */ + void addProfileSection( ProfileName name, + int wid, + QPen pen, + KDFrameProfileSection::Direction dir = KDFrameProfileSection::DirPlain, // PENDING(blackie) possible enum problem + KDFrameProfileSection::Curvature curv = KDFrameProfileSection::CvtPlain ); + + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Specify one of the frames profiles by copying another KDFrameProfile. + Use this function if a profile shall look the same as another one. + + <b>Example:</b> + + \verbatim + // instantiate a frame around an inner rectangle 50/20, 250/20 + KDFrame myFrame( 50,20, 250,120 ); + + // select a very simple rectangular frame with normal corners, black border, white background + myFrame.setSimpleFrame( KDFrame::FrameFlat, 1, 0, QPen( Qt::Black ), QBrush( Qt::White ) ); + + // make the top profile look more nice + myFrame.clearProfile( KDFrame::ProfileTop ); + myFrame.addProfileSection( KDFrame::ProfileTop, 2, QPen( Qt::Black ), + KDFrameProfileSection::DirPlain, + KDFrameProfileSection::CvtPlain ); + myFrame.addProfileSection( KDFrame::ProfileTop, 5, QPen( Qt::NoPen ), + KDFrameProfileSection::DirPlain, + KDFrameProfileSection::CvtPlain ); + myFrame.addProfileSection( KDFrame::ProfileTop, 1, QPen( Qt::Black ), + KDFrameProfileSection::DirPlain, + KDFrameProfileSection::CvtPlain ); + + // copy the top profile settings into the bottom profile + myFrame.setProfile( KDFrame::ProfileBottom, + myFrame.profile( KDFrame::ProfileTop ) ); + \endverbatim + + + \sa ProfileName, setSimpleFrame, clearProfile, addProfileSection, profile + \endif + */ + void setProfile( ProfileName name, const KDFrameProfile& profile ); + + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Return one of the frames profiles. + + \sa ProfileName, setSimpleFrame, clearProfile, addProfileSection, setProfile + \endif + */ + const KDFrameProfile& profile( ProfileName name ) const; + +public: + + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Names of the frame corners: + + \li \c CornerTopLeft + \li \c CornerTopRight + \li \c CornerBottomLeft + \li \c CornerBottomRight + + \sa setCorners, setCorner, setSunPos, cornerStyle, cornerWidth, sunPos + \endif + */ + enum CornerName { CornerTopLeft, + CornerTopRight, + CornerBottomLeft, + CornerBottomRight, + CornerUNKNOWN }; + +public slots: + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Converts the specified corner name enum to a string representation. + + \param type the corner name to convert + \return the string representation of the corner name enum + \endif + */ + static QString cornerNameToString( CornerName type ) { + switch( type ) { + case CornerTopLeft: + return "TopLeft"; + case CornerTopRight: + return "TopRight"; + case CornerBottomLeft: + return "BottomLeft"; + case CornerBottomRight: + return "BottomRight"; + case CornerUNKNOWN: + return "UNKNOWN"; + } + + return "UNKNOWN"; + } + + + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Converts the specified string to a corner name enum value. + + \param string the string to convert + \return the corner name enum value + \endif + */ + static CornerName stringToCornerName( const QString& string ) { + if( string == "TopLeft" ) + return CornerTopLeft; + else if( string == "TopRight" ) + return CornerTopRight; + else if( string == "BottomLeft" ) + return CornerBottomLeft; + else if( string == "BottomRight" ) + return CornerBottomRight; + else if( string == "UNKNOWN" ) + return CornerUNKNOWN; + + return CornerUNKNOWN; + } + +public: + + + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Look of the frame corners: + + \li \c CornerNormal a square angle corner. + \li \c CornerRound a quarter of a circle. + \li \c CornerOblique corner cut-off by 45 degrees. + + \sa setCorners, setCorner, cornerStyle, cornerWidth + \endif + */ + enum CornerStyle { CornerNormal, + CornerRound, + CornerOblique }; + +public slots: + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Converts the specified corner style enum to a string representation. + + \param type the corner style enum to convert + \return the string representation of the corner style enum + \endif + */ + static QString cornerStyleToString( CornerStyle type ) { + switch( type ) { + case CornerNormal: + return "Normal"; + case CornerRound: + return "Round"; + case CornerOblique: + return "Oblique"; + } + + return "Normal"; + } + + + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Converts the specified string to a corner stylye enum value. + + \param string the string to convert + \return the corner style enum value + \endif + */ + static CornerStyle stringToCornerStyle( const QString& string ) { + if( string == "Normal" ) + return CornerNormal; + else if( string == "Round" ) + return CornerRound; + else if( string == "Oblique" ) + return CornerOblique; + + return CornerNormal; + } + + +public: + +// MOC_SKIP_BEGIN + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Helper class storing settings for one corner of the frame. + + To set/retrieve information stored in this class please use + the appropriate methods of the KDFrame class. + + \sa setCorners, setCorner, cornerStyle, cornerWidth, cornerProfile, CornerName, CornerStyle + \endif + */ + class KDCHART_EXPORT KDFrameCorner + { + friend class KDFrame; + public: + KDFrameCorner( CornerStyle style = CornerNormal, + int width = 0, + KDFrameProfile* const profile = 0 ) + : _style( style ), + _width( width ) + { + if( profile ) + _profile = *profile; + else + _profile.clear(); + } + /** + Destructor. Only defined to have it virtual. + */ + virtual ~KDFrameCorner(); + + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Creates a DOM element node that represents a frame corner for use + in a DOM document. + + \param document the DOM document to which the node will belong + \param parent the parent node to which the new node will be appended + \param elementName the name of the new node + \param corner the corner to be represented + \endif + */ + static void createFrameCornerNode( QDomDocument& document, + QDomNode& parent, + const QString& elementName, + const KDFrameCorner& corner ); + + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Reads data from a DOM element node that represents a frame + corner and fills a KDFrameCorner object with the data. + + \param element the DOM element to read from + \param corner the frame corner object to read the data into + \endif + */ + static bool readFrameCornerNode( const QDomElement& element, + KDFrameCorner& corner ); + + protected: + void setAll( CornerStyle style, + int width, + KDFrameProfile* const profile = 0 ) + { + _style = style; + _width = width; + if( profile ) + _profile = *profile; + else + _profile.clear(); + } + CornerStyle style() const + { + return _style; + } + int width() const + { + return _width; + } + const KDFrameProfile& profile() const + { + return _profile; + } + private: + CornerStyle _style; + int _width; + KDFrameProfile _profile; + }; + +// MOC_SKIP_END + +public slots: + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Specify the look and the width and (optionally) the profile + of one of the frame corners. + + \sa setCorners, cornerStyle, cornerWidth, cornerWidth, CornerName, CornerStyle + \endif + */ + void setCorner( CornerName name, + CornerStyle style, + int width, + KDFrameProfile* const profile = 0 ) + { + switch( name ) { + case CornerTopLeft: _cornerTL.setAll( style, width, profile ); + break; + case CornerTopRight: _cornerTR.setAll( style, width, profile ); + break; + case CornerBottomLeft: _cornerBL.setAll( style, width, profile ); + break; + case CornerBottomRight: _cornerBR.setAll( style, width, profile ); + break; + case CornerUNKNOWN: + break; + } + } + + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Specify the look and the width and (optionally) the profile + of all of the frame corners. + + \sa setCorner, cornerStyle, cornerWidth, CornerName, CornerStyle + \endif + */ + void setCorners( CornerStyle style, + int width, + KDFrameProfile* const profile = 0 ) + { + _cornerTL.setAll( style, width, profile ); + _cornerTR.setAll( style, width, profile ); + _cornerBL.setAll( style, width, profile ); + _cornerBR.setAll( style, width, profile ); + } + + + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Returns the look of one of the frame corners. + + \sa setCorners, cornerWidth, CornerName, CornerStyle + \endif + */ + CornerStyle cornerStyle( CornerName name ) const + { + switch( name ) { + case CornerTopLeft: return _cornerTL.style(); + case CornerTopRight: return _cornerTR.style(); + case CornerBottomLeft: return _cornerBL.style(); + case CornerBottomRight: return _cornerBR.style(); + case CornerUNKNOWN: + default: return CornerNormal; + } + } + + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Returns the width of one of the frame corners. + + \sa setCorners, cornerStyle, CornerName, CornerStyle + \endif + */ + int cornerWidth( CornerName name ) const + { + switch( name ) { + case CornerTopLeft: return _cornerTL.width(); + case CornerTopRight: return _cornerTR.width(); + case CornerBottomLeft: return _cornerBL.width(); + case CornerBottomRight: return _cornerBR.width(); + case CornerUNKNOWN: + default: return 0; + } + } + + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Returns the profile of one of the frame corners. + + \sa setCorners, cornerStyle, CornerName, CornerStyle + \endif + */ + const KDFrameProfile& cornerProfile( CornerName name ) const + { + switch( name ) { + case CornerUNKNOWN: + case CornerTopLeft: return _cornerTL.profile(); + case CornerTopRight: return _cornerTR.profile(); + case CornerBottomLeft: return _cornerBL.profile(); + case CornerBottomRight: return _cornerBR.profile(); + default: return _cornerTL.profile(); + } + } + + + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Specifies the position of the sun, normally this is the upper left corner. + + \sa sunPos + \endif + */ + void setSunPos( CornerName sunPos ) + { + _sunPos = sunPos; + } + + /** + \ifnot v200 + \deprecated Feature scheduled for future release, at present not implemented. + \else + Returns the position of the sun. + + \sa setSunPos + \endif + */ + CornerName sunPos() const + { + return _sunPos; + } + + + /** + Specifies the brush to be used to fill the inner area of this frame, + calling this methode without passing in a parameter re-sets the background brush + to \c QBrush( \cQt::NoBrush \c). + + \Note To have a 'tiled' background image just use a brush + holding this pixmap - for other ways to show background + images please use setBackPixmap. + + \sa setBackPixmap, background + */ + void setBackground( QBrush background = QBrush( Qt::NoBrush ) ) + { + _background = background; + } + + /** + Specifies a pixmap to be used to fill the inner area of this frame, + calling this methode without passing in a parameter removes the background pixmap. + + \Note To have a 'tiled' background image do not use setBackPixmap + but use setBackground specifying a brush holding the pixmap. + + \sa setBackground, background + */ + void setBackPixmap( const QPixmap* backPixmap = 0, + BackPixmapMode backPixmapMode = PixStretched ) + { + _backPixmap = backPixmap ? *backPixmap : QPixmap(); + _backPixmapMode = backPixmapMode; + } + + /** + Returns the brush that is used to fill the inner area of this frame, + or a QBrush( \cNoBrush \c) if no background is to be drawn. + + \param backPixmap receives the pixmap used for drawing the background or + a null pixmap, test this by calling backPixmap.isNull() + + \Note If a 'tiled' background image is shown the respective pixmap + may be found by calling the <b>brush's</b> pixmap() function. + + \sa setBackground, setBackPixmap + */ + const QBrush& background( const QPixmap*& backPixmap, + BackPixmapMode& backPixmapMode ) const + { + backPixmap = &_backPixmap; + backPixmapMode = _backPixmapMode; + return _background; + } + + + /** + Specifies the position and the size of area that is surrounded by the frame. + + \note The rectangle applies to the <b>inner</b> area of the frame. + The Frame is drawn around this area: touching it but not covering it. + The outer size of the frame and the position of its outer left corner + depends from the frame profile width(s). + + \sa innerRect, setCorners, setSimpleFrame, setProfile + */ + void setInnerRect( QRect innerRect ) + { + _innerRect = innerRect; + } + + /** + Returns the position and the size of the frame. + + \note The rectangle returns to the <b>inner</b> area of the frame. + The Frame is drawn around this area: touching it but not covering it. + The outer size of the frame and the position of its outer left corner + depends from the frame profile width. + + \sa setInnerRect, setProfile + */ + QRect innerRect() const + { + return _innerRect; + } + + + /** + Paint methode drawing the background (if any) of the frame. + + \note Under normal circumstances you will <b>never</b> have to + call this methode since it is called internally by paint() if you + start it with \c PaintBackground (or with \c PaintAll, resp.) for the \c steps parameter. + */ + virtual void paintBackground( QPainter& painter, const QRect& innerRect ) const; + /** + Paint methode drawing the edges (if any) of the frame. + + \note Under normal circumstances you will <b>never</b> have to + call this methode since it is called internally by paint() if you + start it with \c PaintEdges (or with \c PaintAll, \c PaintBorder, resp.) for the \c steps parameter. + */ + virtual void paintEdges( QPainter& painter, const QRect& innerRect ) const; + /** + Paint methode drawing the corners (if any) of the frame. + + \note Under normal circumstances you will <b>never</b> have to + call this methode since it is called internally by paint() if you + start it with \c PaintCorners (or with \c PaintAll, \c PaintBorder, resp.) for the \c steps parameter. + */ + virtual void paintCorners( QPainter& painter, const QRect& innerRect ) const; + + +public: + /** + The different steps taken to paint the frame: + first paint the background then the edges then the corners. + By choosing \c PaintAll all the parts of the frame will be drawn. + + \li \c PaintBackground -- first paints the brush, then paints the pixmap if any + \li \c PaintEdges + \li \c PaintCorners + \li \c PaintBorder -- paint the edges and the corners but not the background + \li \c PaintAll + + \sa paint + */ + enum KDFramePaintSteps { PaintBackground, PaintEdges, PaintCorners, PaintBorder, PaintAll }; + +public slots: + + /** + Paint methode actually drawing the frame. + + This method must be called from inside the \c paint() methode of your widget. + + In order not to override the inner contents of the frame you normally would + first call <b>paint( painter, PaintBackground );</b> then do all the inside + drawing and finally call <b>paint( painter, PaintBorder );</b>. In case the + inner contents <b>are</b> allowed to (partially) override the frame border + you could do the following: First call <b>paint( painter, PaintBackground );</b> + immediately followed by <b>paint( painter, PaintEdges );</b> then do all your + inside work and finally draw <b>paint( painter, PaintCorners );</b>. + + \param painter The QPainter to be used for drawing. + \param steps The part of the frame to be drawn, use KDFrame::PaintAll to draw the entire frame and the background, + use KDFrame::PaintBackground to draw only the background, use KDFrame::PaintEdges to draw just the edges, + use KDFrame::PaintCorners to draw only the corners. + \param innerRect The area inside the frame. Use this parameter to temporarily override the \c innerRect set by + the constructor of KDFrame or by setInnerRect(). This approach can be usefull if you want to draw several + frames that differ only regarding to their position and size but share the same edges/corners/background settings. + In this case you could decide to instantiate only one KDFrame set up the desired settings and just call + its paint() methode several time - giving it the appropriate innerRect for each frame. This would result in + less memory usage since you could use that single KDFrame object as kind of a shared ressource. + */ + virtual void paint( QPainter* painter, + KDFramePaintSteps steps = PaintAll, + QRect innerRect = QRect(0,0,0,0) ) const; + + + /** + Remove all settings and specify no border, no edges, no background. + */ + void clearAll() + { + _background = QBrush(); + _backPixmap = QPixmap(); + _backPixmapMode = PixStretched; + _shadowWidth = 0; + _sunPos = CornerTopLeft; + _innerRect = QRect( 0,0, 0,0 ); + _topProfile.clear(); + _rightProfile.clear(); + _bottomProfile.clear(); + _leftProfile.clear(); + _cornerTL.setAll( CornerNormal, 0, 0 ); + _cornerTR.setAll( CornerNormal, 0, 0 ); + _cornerBL.setAll( CornerNormal, 0, 0 ); + _cornerBR.setAll( CornerNormal, 0, 0 ); + } + +public: + + /** + Default Constructor. Defines default values. + + The constructor does *not* have a \c parent parameter since drawing + of the frame is not done transparently but by (one or more) explicit + calls of the frames paint() methode. See explanation given there + to learn about the why and how of this... + + \note The rectangle applies to the <b>inner</b> area of the frame. + The Frame is drawn around this area: touching it but not covering it. + The outer size of the frame and the position of its outer left corner + depends from the frame profile width. + + \Note To have a 'tiled' background image do not specify a backPixmap + but use a background <b>brush</b> holding this pixmap. + + \sa rect, setInnerRect, setProfile + */ + KDFrame( QRect innerRect = QRect(0,0,0,0), + SimpleFrame frame = FrameFlat, + int lineWidth = 1, + int midLineWidth = 0, + QPen pen = QPen(), // solid black line with 0 width + QBrush background = QBrush(), // no brush + const QPixmap* backPixmap = 0, // no pixmap + BackPixmapMode backPixmapMode = PixStretched, + int shadowWidth = 0, + CornerName sunPos = CornerTopLeft ) + { + _profileSections.setAutoDelete( true ); + _innerRect = innerRect; + setSimpleFrame( frame, + lineWidth, + midLineWidth, + pen, + background, + backPixmap, + backPixmapMode ); + _shadowWidth = shadowWidth; + _sunPos = sunPos; + } + + +/* + Constructor. Set up a frame by copying settings of another frame. + + The constructor does *not* have a \c parent parameter since drawing + of the frame is not done transparently but by (one or more) explicit + calls of the frames paint() methode. See explanation given there + to learn about the why and how of this... + + \note The rectangle applies to the <b>inner</b> area of the frame. + The Frame is drawn around this area: touching it but not covering it. + The outer size of the frame and the position of its outer left corner + depends from the frame profile width. + + \sa rect, setInnerRect, setProfile + + KDFrame( QRect innerRect, + const KDFrame& R, + CornerName sunPos = CornerUNKNOWN ) + { + deepCopy( *this, R ); + if( innerRect.isValid() ) + _innerRect = innerRect; + if( CornerUNKNOWN != sunPos ) + _sunPos = sunPos; + + _profileSections.setAutoDelete( true ); + } +*/ +private: + KDFrame( const KDFrame& ) : QObject(0) {} + KDFrame& operator=( const KDFrame& ){return *this;} + + + +public: + /** + Destructor. Only defined to have it virtual. + */ + virtual ~KDFrame(); + + /* + Kopierroutine, aufgerufen im Copy-C'tor und im Zuweisungs-Operator + */ + static void deepCopy( KDFrame& D, const KDFrame& R ) + { + D._shadowWidth = R._shadowWidth; + D._sunPos = R._sunPos; + D._background = R._background; + D._backPixmap = R._backPixmap; + D._backPixmapMode = R._backPixmapMode; + D._innerRect = R._innerRect; + D._topProfile = R._topProfile; + D._rightProfile = R._rightProfile; + D._bottomProfile = R._bottomProfile; + D._leftProfile = R._leftProfile; + D._cornerTL = R._cornerTL; + D._cornerTR = R._cornerTR; + D._cornerBL = R._cornerBL; + D._cornerBR = R._cornerBR; + D._profileSections= R._profileSections; + D._profileSections.setAutoDelete( true ); + R.setProfileSectionsAutoDelete( false ); + } + + friend QTextStream& operator<<( QTextStream& s, const KDFrame& p ); + friend QTextStream& operator>>( QTextStream& s, KDFrame& p ); + + /** + Creates a DOM element node that represents a frame for use + in a DOM document. + + \param document the DOM document to which the node will belong + \param parent the parent node to which the new node will be appended + \param elementName the name of the new node + \param frame the frame to be represented + */ + static void createFrameNode( QDomDocument& document, QDomNode& parent, + const QString& elementName, + const KDFrame& frame ); + + /** + Creates a DOM element node that represents a frame profile for use + in a DOM document. + + \param document the DOM document to which the node will belong + \param parent the parent node to which the new node will be appended + \param elementName the name of the new node + \param profile the profile to be represented + */ + static void createFrameProfileNode( QDomDocument& document, + QDomNode& parent, + const QString& elementName, + KDFrameProfile profile ); + + /** + Reads data from a DOM element node that represents a frame + object and fills a KDFrame object with the data. + + \param element the DOM element to read from + \param frame the frame object to read the data into + */ + static bool readFrameNode( const QDomElement& element, + KDFrame& frame ); + + /** + Reads data from a DOM element node that represents a frame + profile and fills a KDFrameProfile object with the data. + + \param element the DOM element to read from + \param profile the frame profile object to read the data into + */ + static bool readFrameProfileNode( const QDomElement& element, + KDFrameProfile& profile ); + +signals: + /** + This signal is emitted when any of the frame parameters has changed. + */ + void changed(); + +private: + void setProfileSectionsAutoDelete( bool on ) const + { + ((KDFrame*)this)->_profileSections.setAutoDelete( on ); + } + + /** + Stores the width of the shadow. + */ + int _shadowWidth; + + /** + Stores the position of the sun. + */ + CornerName _sunPos; + + /** + Stores the brush to be used to fill the inner area. + */ + QBrush _background; + + /** + Stores the pixmap to be painted into the inner area. + */ + QPixmap _backPixmap; + + /** + Stores the way how to display the pixmap that is + to be painted into the inner area. + */ + BackPixmapMode _backPixmapMode; + + /** + Stores the position and size of the frame. + */ + QRect _innerRect; + + /** + Stores all currently used profile settings for a controlled deletion. + + \note The other pointer lists (like _topProfile or _rightProfile) + do NOT delete the objects that belong to their pointers, + but all sections will be deleted via this extra _profileSections list: + this allows for using the same KDFrameProfileSection* to be used + by several lists - typically done for simple frames where all 4 sides + are composed the same way. + */ + KDFrameProfile _profileSections; + + /** + Stores the profile settings for the top side of the frame. + */ + KDFrameProfile _topProfile; + /** + Stores the profile settings for the right side of the frame. + */ + KDFrameProfile _rightProfile; + /** + Stores the profile settings for the bottom side of the frame. + */ + KDFrameProfile _bottomProfile; + /** + Stores the profile settings for the left side of the frame. + */ + KDFrameProfile _leftProfile; + + /** + Stores the settings for the top left corner of the frame. + */ + KDFrameCorner _cornerTL; + /** + Stores the settings for the top right corner of the frame. + */ + KDFrameCorner _cornerTR; + /** + Stores the settings for the bottom left corner of the frame. + */ + KDFrameCorner _cornerBL; + /** + Stores the settings for the bottom right corner of the frame. + */ + KDFrameCorner _cornerBR; +}; + + +/** + Writes the KDFrame object p as an XML document to the text stream s. + + \param s the text stream to write to + \param p the KDFrame object to write + \return the text stream after the write operation + */ +QTextStream& operator<<( QTextStream& s, const KDFrame& p ); + + +/** + Reads the an XML document from the text stream s into the + KDFrame object p + + \param s the text stream to read from + \param p the KDFrame object to read into + \return the text stream after the read operation + */ +QTextStream& operator>>( QTextStream& s, KDFrame& p ); + + +#endif diff --git a/kchart/kdchart/KDFrameProfileSection.cpp b/kchart/kdchart/KDFrameProfileSection.cpp new file mode 100644 index 00000000..bfd64ff3 --- /dev/null +++ b/kchart/kdchart/KDFrameProfileSection.cpp @@ -0,0 +1,95 @@ +/* -*- 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. + ** + **********************************************************************/ +#include <KDFrameProfileSection.h> +#include <KDXMLTools.h> + +KDFrameProfileSection::~KDFrameProfileSection() +{ + // Intentionally left blank for now. +} + + +void KDFrameProfileSection::createFrameProfileSectionNode( QDomDocument& document, + QDomNode& parent, + const QString& elementName, + const KDFrameProfileSection* section ) + +{ + QDomElement sectionElement = document.createElement( elementName ); + parent.appendChild( sectionElement ); + KDXML::createStringNode( document, sectionElement, "Direction", + KDFrameProfileSection::directionToString( section->_direction ) ); + KDXML::createStringNode( document, sectionElement, "Curvature", + KDFrameProfileSection::curvatureToString( section->_curvature ) ); + KDXML::createIntNode( document, sectionElement, "Width", section->_width ); + KDXML::createPenNode( document, sectionElement, "Style", section->_pen ); +} + + +bool KDFrameProfileSection::readFrameProfileSectionNode( const QDomElement& element, + KDFrameProfileSection* section ) +{ + bool ok = true; + Direction tempDirection = DirPlain; + Curvature tempCurvature = CvtPlain; + int tempWidth; + QPen tempPen; + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Direction" ) { + QString value; + ok = ok & KDXML::readStringNode( element, value ); + tempDirection = stringToDirection( value ); + } else if( tagName == "Curvature" ) { + QString value; + ok = ok & KDXML::readStringNode( element, value ); + tempCurvature = stringToCurvature( value ); + } else if( tagName == "Width" ) { + ok = ok & KDXML::readIntNode( element, tempWidth ); + } else if( tagName == "Style" || tagName == "Pen" ) { + ok = ok & KDXML::readPenNode( element, tempPen ); + } else { + qDebug( "Unknown tag in frame" ); + } + } + node = node.nextSibling(); + } + + if( ok ) { + section->_direction = tempDirection; + section->_curvature = tempCurvature; + section->_width = tempWidth; + section->_pen = tempPen; + } + + return ok; +} diff --git a/kchart/kdchart/KDFrameProfileSection.h b/kchart/kdchart/KDFrameProfileSection.h new file mode 100644 index 00000000..f1b4ac87 --- /dev/null +++ b/kchart/kdchart/KDFrameProfileSection.h @@ -0,0 +1,232 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDFRAMEPROFILESECTION__ +#define __KDFRAMEPROFILESECTION__ + +#include <qregion.h> +#include <qpen.h> +#include <qdom.h> +#include "KDChartGlobal.h" + + +/** + Settings for one profile section of one side of the frame. + */ +class KDCHART_EXPORT KDFrameProfileSection +{ +public: + /** + Profile Direction Mode: specifying whether a part of a profile looks embossed or + engraved in comparison to the next outer part (or to the surrounding area, resp.). + + \li \c DirPlain the part of the profile is neither embossed nor engraved. + \li \c DirRaising the part of the profile lookes embossed from the outer part. + \li \c DirSinking the part of the profile lookes engraved into the outer part. + + \Note Since a frame border may consist of several parts you must specify the + direction mode for each of these parts. This is quite different from the + QFrame::Shadow / QFrame::Shape schema where you would select a predefined + profile. KDFrame lets you specify both the number of sections used + to compose frame border's profile and the look of each individual + section by calling \c addProfileSection() multiple times. + + Some commonly used profiles are allready pre-defined for your convenience, + \c setSimpleProfile is used to select one of them, see \c setProfile + for an example how to do this. + + \sa addProfileSection, setProfile, setSimpleProfile, SimpleProfile + */ + enum Direction { DirPlain, DirRaising, DirSinking }; + + /** + Converts the specified direction enum to a string representation. + + \param dir the direction enum to convert + \return the string representation of the direction enum + */ + static QString directionToString( Direction dir ) { + switch( dir ) { + case DirPlain: + return "Plain"; + case DirRaising: + return "Raising"; + case DirSinking: + return "Sinking"; + default: // should not happen + return "Plain"; + } + } + + + /** + Converts the specified string to a direction enum value. + + \param string the string to convert + \return the direction enum value + */ + static Direction stringToDirection( const QString& string ) { + if( string == "Plain" ) + return DirPlain; + else if( string == "Raising" ) + return DirRaising; + else if( string == "Sinking" ) + return DirSinking; + else // default, should not happen + return DirPlain; + } + + + + /** + Profile Curvature Mode: specifying the shape of a frame profile section. + (curvature setting will be ignored for \c DirPlain profiles) + + \li \c CvtFlat looking like a evenly sloping surface. + \li \c CvtConvex looking like quarter of a torus. + \li \c CvtConcave looking like half of a groove. + */ + enum Curvature { CvtPlain, CvtConvex, CvtConcave }; + + /** + Converts the specified curvature enum to a string representation. + + \param curv the curvature enum to convert + \return the string representation of the curvature enum + */ + static QString curvatureToString( Curvature curv ) { + switch( curv ) { + case CvtPlain: + return "Plain"; + case CvtConvex: + return "Convex"; + case CvtConcave: + return "Concave"; + default: // should not happen + return "Plain"; + } + } + + + /** + Converts the specified string to a curvature enum value. + + \param string the string to convert + \return the curvature enum value + */ + static Curvature stringToCurvature( const QString& string ) { + if( string == "Plain" ) + return CvtPlain; + else if( string == "Convex" ) + return CvtConvex; + else if( string == "Concave" ) + return CvtConcave; + else // default, should not happen + return CvtPlain; + } + + + /** + Ctor of KDFrameProfileSection. + + \Note Instead of instantiating KDFrameProfileSection yourself + you would normally rather call \c KDFrame::addProfileSection() + */ + KDFrameProfileSection( Direction direction, + Curvature curvature, + int width, + QPen pen ) + : _direction( direction ), + _curvature( curvature ), + _width( width ), + _pen( pen ) {} + + /** + Default ctor of FrameProfileSection. + + \Note Instead of instantiating KDFrameProfileSection yourself, + you would normally rather call \c KDFrame::addProfileSection() + */ + KDFrameProfileSection() { + _direction = DirPlain; + _curvature = CvtPlain; + _width = 1; + _pen = QPen( Qt::SolidLine ); + } + + /** + Destructor. Only defined to have it virtual. + */ + virtual ~KDFrameProfileSection(); + + /** + Creates a DOM element node that represents a frame profile + section for use in a DOM document. + + \param document the DOM document to which the node will belong + \param parent the parent node to which the new node will be appended + \param elementName the name of the new node + \param section the section to be represented + */ + static void createFrameProfileSectionNode( QDomDocument& document, + QDomNode& parent, + const QString& elementName, + const KDFrameProfileSection* section ); + + /** + Reads data from a DOM element node that represents a frame + profile section and fills a KDFrameProfileSection object with + the data. + + \param element the DOM element to read from + \param section a pointer to the frame profile section object to + read the data into + */ + static bool readFrameProfileSectionNode( const QDomElement& element, + KDFrameProfileSection* section ); + + Direction direction() const { return _direction; } + Curvature curvature() const { return _curvature; } + int width() const { return _width; } + QPen pen() const { return _pen; } + +private: + Direction _direction; + Curvature _curvature; + int _width; + QPen _pen; +}; + +/** + Settings for all the profile sections of one side the frame. + + \sa setProfile, profile + */ +typedef QPtrList < KDFrameProfileSection > KDFrameProfile; + +#endif diff --git a/kchart/kdchart/KDXMLTools.cpp b/kchart/kdchart/KDXMLTools.cpp new file mode 100644 index 00000000..a78c1638 --- /dev/null +++ b/kchart/kdchart/KDXMLTools.cpp @@ -0,0 +1,809 @@ +/* -*- 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. + ** + **********************************************************************/ +#include "KDXMLTools.h" +#include <qbuffer.h> +#include <qimage.h> +#include <zlib.h> + +namespace KDXML { + + void createBoolNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, bool value ) + { + QDomElement newElement = + doc.createElement( elementName ); + parent.appendChild( newElement ); + QDomText elementContent = + doc.createTextNode( value ? "true" : "false" ); + newElement.appendChild( elementContent ); + } + + + void createOrientationNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, Qt::Orientation value ) + { + QDomElement newElement = + doc.createElement( elementName ); + parent.appendChild( newElement ); + QDomText elementContent = + doc.createTextNode( (Qt::Vertical == value) ? "vertical" : "horizontal" ); + newElement.appendChild( elementContent ); + } + + + + void createSizeNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QSize& value ) + { + QDomElement newElement = + doc.createElement( elementName ); + parent.appendChild( newElement ); + newElement.setAttribute( "Width", value.width() ); + newElement.setAttribute( "Height", value.height() ); + } + + + void createIntNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, int value ) + { + QDomElement newElement = + doc.createElement( elementName ); + parent.appendChild( newElement ); + QDomText elementContent = + doc.createTextNode( QString::number( value ) ); + newElement.appendChild( elementContent ); + } + + + void createDoubleNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, double value ) + { + QDomElement newElement = + doc.createElement( elementName ); + parent.appendChild( newElement ); + QDomText elementContent = + doc.createTextNode( QString::number( value ) ); + newElement.appendChild( elementContent ); + } + + + void createStringNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, + const QString& text ) + { + QDomElement newElement = + doc.createElement( elementName ); + parent.appendChild( newElement ); + QDomText elementContent = + doc.createTextNode( text ); + newElement.appendChild( elementContent ); + } + + + void createColorNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QColor& color ) + { + QDomElement colorElement = doc.createElement( elementName ); + parent.appendChild( colorElement ); + colorElement.setAttribute( "Red", + QString::number( color.red() ) ); + colorElement.setAttribute( "Green", + QString::number( color.green() ) ); + colorElement.setAttribute( "Blue", + QString::number( color.blue() ) ); + } + + + void createBrushNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QBrush& brush ) + + { + QDomElement brushElement = doc.createElement( elementName ); + parent.appendChild( brushElement ); + createColorNode( doc, brushElement, "Color", brush.color() ); + createStringNode( doc, brushElement, "Style", + KDXML::brushStyleToString( brush.style() ) ); + if( brush.style() == Qt::CustomPattern && brush.pixmap() ) + createPixmapNode( doc, brushElement, "Pixmap", *brush.pixmap() ); + } + + + void createPixmapNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QPixmap& pixmap ) + { + QDomElement pixmapElement = doc.createElement( elementName ); + parent.appendChild( pixmapElement ); + + createStringNode( doc, pixmapElement, "Format", "XPM.GZ" ); + if( pixmap.isNull() ){ + // we store an empty image without any data + createIntNode( doc, pixmapElement, "Length", 0 ); + }else{ + // Convert the pixmap to an image, save that image to an in-memory + // XPM representation and compress this representation. This + // conforms to the file format Qt Designer uses. + QByteArray ba; + QBuffer buffer( ba ); + buffer.open( IO_WriteOnly ); + QImageIO imgio( &buffer, "XPM" ); + QImage image = pixmap.convertToImage(); + imgio.setImage( image ); + imgio.write(); + buffer.close(); + ulong len = ba.size() * 2; + QByteArray bazip( len ); + ::compress( (uchar*) bazip.data(), &len, (uchar*) ba.data(), ba.size() ); + QString dataString; + static const char hexchars[] = "0123456789abcdef"; + for ( int i = 0; i < (int)len; ++i ) { + uchar c = (uchar) bazip[i]; + dataString += hexchars[c >> 4]; + dataString += hexchars[c & 0x0f]; + } + createIntNode( doc, pixmapElement, "Length", ba.size() ); + createStringNode( doc, pixmapElement, "Data", dataString ); + } + } + + + void createRectNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QRect& rect ) + { + QDomElement rectElement = doc.createElement( elementName ); + parent.appendChild( rectElement ); + QDomElement xElement = doc.createElement( "X" ); + rectElement.appendChild( xElement ); + QDomText xContent = doc.createTextNode( QString::number( rect.x() ) ); + xElement.appendChild( xContent ); + QDomElement yElement = doc.createElement( "Y" ); + rectElement.appendChild( yElement ); + QDomText yContent = doc.createTextNode( QString::number( rect.y() ) ); + yElement.appendChild( yContent ); + QDomElement widthElement = doc.createElement( "Width" ); + rectElement.appendChild( widthElement ); + QDomText widthContent = doc.createTextNode( QString::number( rect.width() ) ); + widthElement.appendChild( widthContent ); + QDomElement heightElement = doc.createElement( "Height" ); + rectElement.appendChild( heightElement ); + QDomText heightContent = doc.createTextNode( QString::number( rect.height() ) ); + heightElement.appendChild( heightContent ); + } + + + void createStringListNodes( QDomDocument& doc, QDomNode& parent, + const QString& elementName, + const QStringList* list ) + { + if( !list ) + return; + + for( QStringList::ConstIterator it = list->begin(); + it != list->end(); ++it ) { + QDomElement element = doc.createElement( elementName ); + parent.appendChild( element ); + QDomText elementContent = doc.createTextNode( *it ); + element.appendChild( elementContent ); + } + } + + + void createFontNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QFont& font ) + { + QDomElement fontElement = doc.createElement( elementName ); + parent.appendChild( fontElement ); + createStringNode( doc, fontElement, "Family", font.family() ); + createIntNode( doc, fontElement, "PointSize", font.pointSize() ); + createIntNode( doc, fontElement, "Weight", font.weight() ); + createBoolNode( doc, fontElement, "Italic", font.italic() ); +#if COMPAT_QT_VERSION < 0x030000 + // Qt 3 handles the charset internally. + createIntNode( doc, fontElement, "CharSet", font.charSet() ); +#endif + } + + + void createPenNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QPen& pen ) + { + QDomElement penElement = doc.createElement( elementName ); + parent.appendChild( penElement ); + createIntNode( doc, penElement, "Width", pen.width() ); + createColorNode( doc, penElement, "Color", pen.color() ); + createStringNode( doc, penElement, "Style", penStyleToString( pen.style() ) ); + } + + + void createDateTimeNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, + const QDateTime& datetime ) + { + QDomElement dateTimeElement = doc.createElement( elementName ); + parent.appendChild( dateTimeElement ); + createDateNode( doc, dateTimeElement, "Date", datetime.date() ); + createTimeNode( doc, dateTimeElement, "Time", datetime.time() ); + } + + + void createDateNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QDate& date ) + { + QDomElement dateElement = doc.createElement( elementName ); + parent.appendChild( dateElement ); + dateElement.setAttribute( "Year", QString::number( date.year() ) ); + dateElement.setAttribute( "Month", QString::number( date.month() ) ); + dateElement.setAttribute( "Day", QString::number( date.day() ) ); + } + + + void createTimeNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QTime& time ) + { + QDomElement timeElement = doc.createElement( elementName ); + parent.appendChild( timeElement ); + timeElement.setAttribute( "Hour", + QString::number( time.hour() ) ); + timeElement.setAttribute( "Minute", + QString::number( time.minute() ) ); + timeElement.setAttribute( "Second", + QString::number( time.second() ) ); + timeElement.setAttribute( "Millisecond", + QString::number( time.msec() ) ); + } + + + QString penStyleToString( Qt::PenStyle style ) + { + switch( style ) { + case Qt::NoPen: + return "NoPen"; + case Qt::SolidLine: + return "SolidLine"; + case Qt::DashLine: + return "DashLine"; + case Qt::DotLine: + return "DotLine"; + case Qt::DashDotLine: + return "DashDotLine"; + case Qt::DashDotDotLine: + return "DashDotDotLine"; + default: // should not happen + return "SolidLine"; + } + } + + + + QString brushStyleToString( Qt::BrushStyle style ) + { + // PENDING(kalle) Support custom patterns + switch( style ) { + case Qt::NoBrush: + return "NoBrush"; + case Qt::SolidPattern: + return "SolidPattern"; + case Qt::Dense1Pattern: + return "Dense1Pattern"; + case Qt::Dense2Pattern: + return "Dense2Pattern"; + case Qt::Dense3Pattern: + return "Dense3Pattern"; + case Qt::Dense4Pattern: + return "Dense4Pattern"; + case Qt::Dense5Pattern: + return "Dense5Pattern"; + case Qt::Dense6Pattern: + return "Dense6Pattern"; + case Qt::Dense7Pattern: + return "Dense7Pattern"; + case Qt::HorPattern: + return "HorPattern"; + case Qt::VerPattern: + return "VerPattern"; + case Qt::CrossPattern: + return "CrossPattern"; + case Qt::BDiagPattern: + return "BDiagPattern"; + case Qt::FDiagPattern: + return "FDiagPattern"; + case Qt::DiagCrossPattern: + return "DiagCrossPattern"; + default: // should not happen (but can for a custom pattern) + return "SolidPattern"; + } + } + + + bool readStringNode( const QDomElement& element, QString& value ) + { + value = element.text(); + return true; + } + + + bool readIntNode( const QDomElement& element, int& value ) + { + bool ok = false; + int temp = element.text().toInt( &ok ); + if( ok ) + value = temp; + return ok; + } + + + bool readDoubleNode( const QDomElement& element, double& value ) + { + bool ok = false; + double temp = element.text().toDouble( &ok ); + if( ok ) + value = temp; + return ok; + } + + + bool readBoolNode( const QDomElement& element, bool& value ) + { + if( element.text() == "true" ) { + value = true; + return true; + } else if( element.text() == "false" ) { + value = false; + return true; + } else + return false; + } + + + bool readOrientationNode( const QDomElement& element, Qt::Orientation& value ) + { + if( element.text() == "vertical" ) { + value = Qt::Vertical; + return true; + } else if( element.text() == "horizontal" ) { + value = Qt::Horizontal; + return true; + } else + return false; + } + + + bool readSizeNode( const QDomElement& element, QSize& value ) + { + bool ok = false; + int width, height; + if( element.hasAttribute( "Width" ) ) { + width = element.attribute( "Width" ).toInt( &ok ); + if( ok && element.hasAttribute( "Height" ) ) { + height = element.attribute( "Height" ).toInt( &ok ); + if( ok ){ + value.setWidth( width ); + value.setHeight( height ); + } + } + } + return ok; + } + + + bool readColorNode( const QDomElement& element, QColor& value ) + { + bool ok = true; + int red=0, green=0, blue=0; + if( element.hasAttribute( "Red" ) ) { + bool redOk = false; + red = element.attribute( "Red" ).toInt( &redOk ); + ok = ok & redOk; + } + if( element.hasAttribute( "Green" ) ) { + bool greenOk = false; + green = element.attribute( "Green" ).toInt( &greenOk ); + ok = ok & greenOk; + } + if( element.hasAttribute( "Blue" ) ) { + bool blueOk = false; + blue = element.attribute( "Blue" ).toInt( &blueOk ); + ok = ok & blueOk; + } + + if( ok ) + value.setRgb( red, green, blue ); + + return ok; + } + + + bool readBrushNode( const QDomElement& element, QBrush& brush ) + { + bool ok = true; + QColor tempColor; + Qt::BrushStyle tempStyle=Qt::SolidPattern; + QPixmap tempPixmap; + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Color" ) { + ok = ok & readColorNode( element, tempColor ); + } else if( tagName == "Style" ) { + QString value; + ok = ok & readStringNode( element, value ); + tempStyle = stringToBrushStyle( value ); + } else if( tagName == "Pixmap" ) { + ok = ok & readPixmapNode( element, tempPixmap ); + } else { + qDebug( "Unknown tag in brush" ); + } + } + node = node.nextSibling(); + } + + if( ok ) { + brush.setColor( tempColor ); + brush.setStyle( tempStyle ); + if( !tempPixmap.isNull() ) + brush.setPixmap( tempPixmap ); + } + + return ok; + } + + + bool readPixmapNode( const QDomElement& element, QPixmap& pixmap ) + { + bool ok = true; + unsigned long tempLength; + QString tempData; + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Format" ) { + QString formatName; + ok = ok & readStringNode( element, formatName ); +#ifndef NDEBUG + if( formatName != "XPM.GZ" ) + qDebug( "Unsupported pixmap format in XML file" ); +#endif + } else if( tagName == "Length" ) { + int itempLength; + ok = ok & readIntNode( element, itempLength ); + tempLength = itempLength; + } else if( tagName == "Data" ) { + ok = ok & readStringNode( element, tempData ); + } else { + qDebug( "Unknown tag in Pixmap" ); + } + } + node = node.nextSibling(); + } + + if( ok ) { + if( 0 < tempLength ) { + // Decode the image file format in the same way Qt Designer does. + char *ba = new char[ tempData.length() / 2 ]; + for ( int i = 0; i < (int)tempData.length() / 2; ++i ) { + char h = tempData[ 2 * i ].latin1(); + char l = tempData[ 2 * i + 1 ].latin1(); + uchar r = 0; + if ( h <= '9' ) + r += h - '0'; + else + r += h - 'a' + 10; + r = r << 4; + if ( l <= '9' ) + r += l - '0'; + else + r += l - 'a' + 10; + ba[ i ] = r; + } + + if( tempLength < tempData.length() * 5 ) + tempLength = tempData.length() * 5; + QByteArray baunzip( tempLength ); + ::uncompress( (uchar*) baunzip.data(), &tempLength, + (uchar*) ba, tempData.length()/2 ); + QImage image; + image.loadFromData( (const uchar*)baunzip.data(), tempLength, "XPM" ); + + if( image.isNull() ) + pixmap.resize( 0, 0 ); // This is _not_ an error, we just read a NULL pixmap! + else + ok = ok & pixmap.convertFromImage( image, 0 ); + } else + pixmap.resize( 0, 0 ); // This is _not_ an error, we just read a empty pixmap! + } + + return ok; + } + + + bool readPenNode( const QDomElement& element, QPen& pen ) + { + bool ok = true; + int tempWidth; + QColor tempColor; + Qt::PenStyle tempStyle=Qt::SolidLine; + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Width" ) { + ok = ok & readIntNode( element, tempWidth ); + } else if( tagName == "Color" ) { + ok = ok & readColorNode( element, tempColor ); + } else if( tagName == "Style" ) { + QString value; + ok = ok & readStringNode( element, value ); + tempStyle = stringToPenStyle( value ); + } else { + qDebug( "Unknown tag in brush" ); + } + } + node = node.nextSibling(); + } + + if( ok ) { + pen.setWidth( tempWidth ); + pen.setColor( tempColor ); + pen.setStyle( tempStyle ); + } + + return ok; + } + + bool readFontNode( const QDomElement& element, QFont& font ) + { + bool ok = true; + QString family; + int pointSize, weight; + bool italic; + int charSet; + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Family" ) { + ok = ok & readStringNode( element, family ); + } else if( tagName == "PointSize" ) { + ok = ok & readIntNode( element, pointSize ); + } else if( tagName == "Weight" ) { + ok = ok & readIntNode( element, weight ); + } else if( tagName == "Italic" ) { + ok = ok & readBoolNode( element, italic ); + } else if( tagName == "CharSet" ) { + ok = ok & readIntNode( element, charSet ); + } else { + qDebug( "Unknown tag in color map" ); + } + } + node = node.nextSibling(); + } + + if( ok ) { + font.setFamily( family ); + font.setPointSize( pointSize ); + font.setWeight( weight ); + font.setItalic( italic ); +#if COMPAT_QT_VERSION < 0x030000 + // Qt 3 handles charsets internally. + font.setCharSet( (QFont::CharSet)charSet ); +#endif + } + + return ok; + } + + bool readRectNode( const QDomElement& element, QRect& value ) + { + bool ok = true; + int width, height, x, y; + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Width" ) { + ok = ok & readIntNode( element, width ); + } else if( tagName == "Height" ) { + ok = ok & readIntNode( element, height ); + } else if( tagName == "X" ) { + ok = ok & readIntNode( element, x ); + } else if( tagName == "Y" ) { + ok = ok & readIntNode( element, y ); + } else { + qDebug( "Unknown tag in rect" ); + } + } + node = node.nextSibling(); + } + + if( ok ) { + value.setX( x ); + value.setY( y ); + value.setWidth( width ); + value.setHeight( height ); + } + + return ok; + } + + + + bool readDateTimeNode( const QDomElement& element, QDateTime& datetime ) + { + bool ok = true; + QDate tempDate; + QTime tempTime; + QDomNode node = element.firstChild(); + while( !node.isNull() ) { + QDomElement element = node.toElement(); + if( !element.isNull() ) { // was really an element + QString tagName = element.tagName(); + if( tagName == "Date" ) { + ok = ok & readDateNode( element, tempDate ); + } else if( tagName == "Time" ) { + ok = ok & readTimeNode( element, tempTime ); + } else { + qDebug( "Unknown tag in datetime" ); + } + } + node = node.nextSibling(); + } + + if( ok ) { + datetime.setDate( tempDate ); + datetime.setTime( tempTime ); + } + + return ok; + } + + + bool readDateNode( const QDomElement& element, QDate& value ) + { + bool ok = true; + int year=0, month=0, day=0; + if( element.hasAttribute( "Year" ) ) { + bool yearOk = false; + year = element.attribute( "Year" ).toInt( &yearOk ); + ok = ok & yearOk; + } + if( element.hasAttribute( "Month" ) ) { + bool monthOk = false; + month = element.attribute( "Month" ).toInt( &monthOk ); + ok = ok & monthOk; + } + if( element.hasAttribute( "Day" ) ) { + bool dayOk = false; + day = element.attribute( "Day" ).toInt( &dayOk ); + ok = ok & dayOk; + } + + if( ok ) + value.setYMD( year, month, day ); + + return ok; + } + + + + bool readTimeNode( const QDomElement& element, QTime& value ) + { + bool ok = true; + int hour=0, minute=0, second=0, msec=0; + if( element.hasAttribute( "Hour" ) ) { + bool hourOk = false; + hour = element.attribute( "Hour" ).toInt( &hourOk ); + ok = ok & hourOk; + } + if( element.hasAttribute( "Minute" ) ) { + bool minuteOk = false; + minute = element.attribute( "Minute" ).toInt( &minuteOk ); + ok = ok & minuteOk; + } + if( element.hasAttribute( "Second" ) ) { + bool secondOk = false; + second = element.attribute( "Second" ).toInt( &secondOk ); + ok = ok & secondOk; + } + if( element.hasAttribute( "Millisecond" ) ) { + bool msecOk = false; + msec = element.attribute( "Millisecond" ).toInt( &msecOk ); + ok = ok & msecOk; + } + + if( ok ) + value.setHMS( hour, minute, second, msec ); + + return ok; + } + + + + Qt::PenStyle stringToPenStyle( const QString& style ) + { + if( style == "NoPen" ) + return Qt::NoPen; + else if( style == "SolidLine" ) + return Qt::SolidLine; + else if( style == "DashLine" ) + return Qt::DashLine; + else if( style == "DotLine" ) + return Qt::DotLine; + else if( style == "DashDotLine" ) + return Qt::DashDotLine; + else if( style == "DashDotDotLine" ) + return Qt::DashDotDotLine; + else // should not happen + return Qt::SolidLine; + } + + + Qt::BrushStyle stringToBrushStyle( const QString& style ) + { + // PENDING(kalle) Support custom patterns + if( style == "NoBrush" ) + return Qt::NoBrush; + else if( style == "SolidPattern" ) + return Qt::SolidPattern; + else if( style == "Dense1Pattern" ) + return Qt::Dense1Pattern; + else if( style == "Dense2Pattern" ) + return Qt::Dense2Pattern; + else if( style == "Dense3Pattern" ) + return Qt::Dense3Pattern; + else if( style == "Dense4Pattern" ) + return Qt::Dense4Pattern; + else if( style == "Dense5Pattern" ) + return Qt::Dense5Pattern; + else if( style == "Dense6Pattern" ) + return Qt::Dense6Pattern; + else if( style == "Dense7Pattern" ) + return Qt::Dense7Pattern; + else if( style == "HorPattern" ) + return Qt::HorPattern; + else if( style == "VerPattern" ) + return Qt::VerPattern; + else if( style == "CrossPattern" ) + return Qt::CrossPattern; + else if( style == "BDiagPattern" ) + return Qt::BDiagPattern; + else if( style == "FDiagPattern" ) + return Qt::FDiagPattern; + else if( style == "DiagCrossPattern" ) + return Qt::DiagCrossPattern; + else // should not happen (but can with custom patterns) + return Qt::SolidPattern; + } + +} diff --git a/kchart/kdchart/KDXMLTools.h b/kchart/kdchart/KDXMLTools.h new file mode 100644 index 00000000..7798c20e --- /dev/null +++ b/kchart/kdchart/KDXMLTools.h @@ -0,0 +1,104 @@ +/* -*- 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. + ** + **********************************************************************/ +#ifndef __KDXMLTOOLS_H__ +#define __KDXMLTOOLS_H__ + +// *must* include this one as first one: +#include <KDChartGlobal.h> + +#include <qpen.h> +#include <qdom.h> +#include <qstring.h> +#include <qcolor.h> +#include <qbrush.h> +#include <qsize.h> +#include <qrect.h> +#include <qfont.h> +#include <qstringlist.h> +#include <qdatetime.h> + +namespace KDXML { + QString penStyleToString( Qt::PenStyle style ); + Qt::PenStyle stringToPenStyle( const QString& style ); + QString brushStyleToString( Qt::BrushStyle style ); + Qt::BrushStyle stringToBrushStyle( const QString& style ); + + void createBoolNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, bool value ); + void createOrientationNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, Qt::Orientation value ); + void createSizeNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QSize& value ); + void createIntNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, int value ); + void createDoubleNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, double value ); + void createStringNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, + const QString& text ); + void createColorNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QColor& color ); + void createBrushNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QBrush& brush ); + void createPixmapNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QPixmap& pixmap ); + void createRectNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QRect& rect ); + void createStringListNodes( QDomDocument& doc, QDomNode& parent, + const QString& elementName, + const QStringList* thelist ); // PCH: This is required to avoid MSVC compiler warnings and errors. + void createFontNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QFont& font ); + + void createPenNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QPen& pen ); + void createDateTimeNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, + const QDateTime& datetime ); + void createDateNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QDate& date ); + void createTimeNode( QDomDocument& doc, QDomNode& parent, + const QString& elementName, const QTime& time ); + bool readIntNode( const QDomElement& element, int& value ); + bool readStringNode( const QDomElement& element, QString& value ); + bool readDoubleNode( const QDomElement& element, double& value ); + bool readBoolNode( const QDomElement& element, bool& value ); + bool readOrientationNode( const QDomElement& element, Qt::Orientation& value ); + bool readSizeNode( const QDomElement& element, QSize& value ); + bool readColorNode( const QDomElement& element, QColor& value ); + bool readBrushNode( const QDomElement& element, QBrush& brush ); + bool readPixmapNode( const QDomElement& element, QPixmap& pixmap ); + bool readRectNode( const QDomElement& element, QRect& value ); + bool readFontNode( const QDomElement& element, QFont& font ); + bool readPenNode( const QDomElement& element, QPen& pen ); + bool readDateTimeNode( const QDomElement& element, QDateTime& datetime ); + bool readDateNode( const QDomElement& element, QDate& date ); + bool readTimeNode( const QDomElement& element, QTime& time ); +} +#endif diff --git a/kchart/kdchart/LICENSE b/kchart/kdchart/LICENSE new file mode 100644 index 00000000..8d0b8788 --- /dev/null +++ b/kchart/kdchart/LICENSE @@ -0,0 +1,349 @@ + + The KDChart Library is Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB. + + You may use, distribute and copy the KDChart Library under the terms of + GNU General Public License version 2, which is displayed below. + +------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + +------------------------------------------------------------------------- diff --git a/kchart/kdchart/Makefile.am b/kchart/kdchart/Makefile.am new file mode 100644 index 00000000..0538e50c --- /dev/null +++ b/kchart/kdchart/Makefile.am @@ -0,0 +1,94 @@ +lib_LTLIBRARIES = libkdchart.la + +libkdchart_la_SOURCES = KDChart.cpp \ + KDChartAreaPainter.cpp \ + KDChartAxesPainter.cpp \ + KDChartAxisParams.cpp \ + KDChartBarPainter.cpp \ + KDChartBaseSeries.cpp \ + KDChartBWPainter.cpp \ + KDChartCustomBox.cpp \ + KDChartDataIntern.cpp \ + KDChartHiLoPainter.cpp \ + KDChartLinesPainter.cpp \ + KDChartPainter.cpp \ + KDChartParams.cpp \ + KDChartParams_frame.cpp\ + KDChartParams_io.cpp \ + KDChartPiePainter.cpp \ + KDChartPlaneSeries.cpp \ + KDChartPolarPainter.cpp \ + KDChartPropertySet.cpp \ + KDChartRingPainter.cpp \ + KDChartSeriesCollection.cpp \ + KDChartTableBase.cpp \ + KDChartTextPiece.cpp \ + KDChartVectorSeries.cpp \ + KDChartVectorTable.cpp \ + KDChartWidget.cpp \ + KDDrawText.cpp \ + KDFrame.cpp \ + KDFrameProfileSection.cpp \ + KDXMLTools.cpp \ + KDChartEnums.cpp \ + KDChartAxisParamsWrapper.cpp \ + KDChartCustomBoxWrapper.cpp \ + KDChartParamsWrapper.cpp \ + KDChartTableDataWrapper.cpp + #KDChartObjectFactory.cpp + #KDChartWrapperFactory.cpp + +libkdchart_la_LDFLAGS = $(all_libraries) -no-undefined +libkdchart_la_LIBADD = $(LIB_QT) $(LIB_KDECORE) + +noinst_HEADERS = KDChart.h \ + KDChartAreaPainter.h \ + KDChartAxesPainter.h \ + KDChartBarPainter.h \ + KDChartBaseSeries.h \ + KDChartBWPainter.h \ + KDChartDataIntern.h \ + KDChartDataRegion.h \ + KDChartHiLoPainter.h \ + KDChartLinesPainter.h \ + KDChartNotEnoughSpaceException.h \ + KDChartUnknownTypeException.h \ + KDChartPainter.h \ + KDChartPiePainter.h \ + KDChartPlaneSeries.h \ + KDChartPolarPainter.h \ + KDChartRingPainter.h \ + KDChartSeriesCollection.h \ + KDChartTextPiece.h \ + KDChartUnknownTypeException.h \ + KDChartVectorSeries.h \ + KDDrawText.h \ + KDXMLTools.h +METASOURCES = AUTO + +INCLUDES = $(KOFFICECORE_INCLUDES) -I$(srcdir)/.. -I$(srcdir) $(all_includes) + +include_HEADERS = KDChartTableBase.h \ + KDChartListTable.h \ + KDChartVectorTable.h \ + KDChartTable.h \ + KDChartParams.h \ + KDFrameProfileSection.h \ + KDChartPropertySet.h \ + KDChartDataIntern.h \ + KDChartDataRegion.h \ + KDFrame.h \ + KDChartGlobal.h \ + KDChartWidget.h \ + KDChartCustomBox.h \ + KDChartEnums.h \ + KDChartTextPiece.h \ + KDChartAxisParams.h \ + kdchart_export.h \ + KDChartCustomBoxWrapper.h \ + KDChartWrapperFactory.h \ + KDChartAxisParamsWrapper.h \ + KDChartParamsWrapper.h \ + KDChartTableDataWrapper.h \ + KDChartObjectFactory.h + diff --git a/kchart/kdchart/README b/kchart/kdchart/README new file mode 100644 index 00000000..075e9d8c --- /dev/null +++ b/kchart/kdchart/README @@ -0,0 +1,77 @@ +README last edited: 20.11.2003 + + + +CONTENTS + + 1. What you find here + 2. Which projects are allowed to used this code + 3. Where to find additional information + 4. Obtaining commercial support + + + +1. What you find here +--------------------- + + This directory contains the GPL'ed version of KD Chart, + this is from the KD Chart webpage: + + ''KD Chart is a tool for creating business charts and is + the most powerful Qt component of its kind. + + Besides having all the standard features, KD Chart(...)'' + + Go on + reading: http://www.klaralvdalens-datakonsult.se/kdchart/ + + + +2. Which projects are allowed to used this code +----------------------------------------------- + + This version of the KD Chart code may be used EXCLUSIVELY + for software projects using the GNU General Public License, + for details see file LICENSE included with these sources. + + If you intend to develop software that is closed-source or + otherwise not compatible with the GNU General Public License + contact Klarälvdalens Datakonsult AB for further information + and licensing conditions: info@klaralvdalens-datakonsult.se + + + +3. Where to find additional information +--------------------------------------- + + * See the function calls embedded in KChart to learn how + to use KD Chart for your own projects. + + * See the (doxygen enabled) documentation inside the + source. + + * Carefully study the KD Chart Programmers Manual + provided here: + +http://www.klaralvdalens-datakonsult.se/kdchart/ProgrammersManual/KDChart.pdf + + Note that this manual frequently refers to a lot of sample + files which are shipped together with the commercial + version of KD Chart (being dual-licensed, see section 2). + It is possible to understand the manual without studying + these samples by just reading the documentation inside + the source. + + + +4. Obtaining commercial support +------------------------------- + Commercial support (and licensing for software projects that + are not compatible to the GPL, see section 2.) is available + from Klarälvdalens Datakonsult AB, please send your mail to: + + info@klaralvdalens-datakonsult.se + + + +filename: KOffice / kchart / kdchart / README diff --git a/kchart/kdchart/README_BEFORE_HACKING b/kchart/kdchart/README_BEFORE_HACKING new file mode 100644 index 00000000..034ed389 --- /dev/null +++ b/kchart/kdchart/README_BEFORE_HACKING @@ -0,0 +1,8 @@ +This is a mostly verbatim copy of KDChart, Klarälvdalens Datakonsult AB's +charting engine. We are developing KDChart at a rapid pace and will update +the version here quite often, so in order to avoid clashes, we would like +to ask you to get in contact with us first before changing, adding or removing +anything here. You can reach us at kalle@klaralvdalens-datakonsult.se or +khz@klaralvdalens-datakonsult.se + +Thanks! diff --git a/kchart/kdchart/kdchart_export.h b/kchart/kdchart/kdchart_export.h new file mode 100644 index 00000000..81f1d47f --- /dev/null +++ b/kchart/kdchart/kdchart_export.h @@ -0,0 +1,47 @@ +#ifndef KDCHART_EXPORT_H +#define KDCHART_EXPORT_H + +#include <qglobal.h> + +#if 1 +#include <koffice_export.h> +#else + +#if defined(Q_OS_WIN32) && defined(KDCHART_DLL) +#define DLL_EXPORT __declspec(dllexport) +#define DLL_IMPORT __declspec(dllimport) +#else +#define DLL_EXPORT +#define DLL_IMPORT +#endif + +#if defined(KDCHART_DLL) +#define KDCHART_EXPORT DLL_EXPORT +#else +#define KDCHART_EXPORT DLL_IMPORT +#endif + +#endif + +#endif // #if 1 + +/* + How to make a KD Chart DLL rather than linking statically: + + 1. We have an #include <kdchart_export.h> statement in all + of our public KD Chart header files + + 2. We have the KDCHART_EXPORT macro added to all of our + public classes. + + So e.g. the class header reads: + + class KDCHART_EXPORT KDChartWidget : public QWidget + { + + } + + + 3. In order to create a DLL + just add "CONFIG += dll" to the qmake command line or to the [KD Chart directory]/src/src.pro file +*/ |