summaryrefslogtreecommitdiffstats
path: root/libkdchart/KDChart.cpp
blob: 5a4b4719fe63281798a53cba86a433acee220028 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
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