kchart

KDChartAxesPainter.cpp

00001 /* -*- Mode: C++ -*-
00002    KDChart - a multi-platform charting engine
00003    */
00004 
00005 /****************************************************************************
00006  ** Copyright (C) 2001-2003 Klar�vdalens Datakonsult AB.  All rights reserved.
00007  **
00008  ** This file is part of the KDChart library.
00009  **
00010  ** This file may be distributed and/or modified under the terms of the
00011  ** GNU General Public License version 2 as published by the Free Software
00012  ** Foundation and appearing in the file LICENSE.GPL included in the
00013  ** packaging of this file.
00014  **
00015  ** Licensees holding valid commercial KDChart licenses may use this file in
00016  ** accordance with the KDChart Commercial License Agreement provided with
00017  ** the Software.
00018  **
00019  ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020  ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021  **
00022  ** See http://www.klaralvdalens-datakonsult.se/?page=products for
00023  **   information about KDChart Commercial License Agreements.
00024  **
00025  ** Contact info@klaralvdalens-datakonsult.se if any conditions of this
00026  ** licensing are not clear to you.
00027  **
00028  **********************************************************************/
00029 #include <qpainter.h>
00030 #include <qlabel.h>
00031 
00032 #include <KDDrawText.h>
00033 #include "KDChartAxesPainter.h"
00034 #include "KDChartAxisParams.h"
00035 #include "KDChartParams.h"
00036 
00037 #include <stdlib.h>
00038 
00039 
00045 int secondsSinceUTCStart( const QDateTime& dt )
00046 {
00047     QDateTime dtStart( QDate( 1970, 1, 1 ) );
00048     return dtStart.secsTo( dt );
00049 }
00050 
00051 
00064     KDChartAxesPainter::KDChartAxesPainter( KDChartParams* params ) :
00065 KDChartPainter( params )
00066 {
00067     // Intentionally left blank.
00068     // We cannot setup the geometry yet
00069     // since we do not know the size of the painter.
00070 }
00071 
00075 KDChartAxesPainter::~KDChartAxesPainter()
00076 {
00077     // intentionally left blank
00078 }
00079 
00080 
00081 #if COMPAT_QT_VERSION < 0x030000
00082 QDateTime dateTimeFromString( const QString& s ) // only ISODate is allowed
00083 {
00084     int year(  s.mid(  0, 4 ).toInt() );
00085     int month( s.mid(  5, 2 ).toInt() );
00086     int day(   s.mid(  8, 2 ).toInt() );
00087     QString t( s.mid( 11 ) );
00088     int hour(   t.mid( 0, 2 ).toInt() );
00089     int minute( t.mid( 3, 2 ).toInt() );
00090     int second( t.mid( 6, 2 ).toInt() );
00091     int msec(   t.mid( 9, 3 ).toInt() );
00092     if ( year && month && day )
00093         return QDateTime( QDate( year, month, day ),
00094                 QTime( hour, minute, second, msec ) );
00095     else
00096         return QDateTime();
00097 }
00098 QString dateTimeToString( const QDateTime& dt )  // ISODate is returned
00099 {
00100     QString date;
00101     QString month(
00102             QString::number( dt.date().month() ).rightJustify( 2, '0' ) );
00103     QString day(
00104             QString::number( dt.date().day() ).rightJustify( 2, '0' ) );
00105     date = QString::number( dt.date().year() ) + "-" + month + "-" + day;
00106     QString time;
00107     time.sprintf( "%.2d:%.2d:%.2d",
00108             dt.time().hour(), dt.time().minute(), dt.time().second() );
00109     return date + "T" + time;
00110 }
00111 #endif
00112 
00113 
00119 void reCalculateLabelTexts(
00120         QPainter* painter,
00121         const KDChartTableDataBase& data,
00122         const KDChartParams& params,
00123         uint axisNumber,
00124         double averageValueP1000,
00125         double delimLen,
00126         internal__KDChart__CalcValues& cv )
00127 {
00128     KDChartAxesPainter::calculateLabelTexts(
00129             painter,
00130             data,
00131             params,
00132             axisNumber,
00133             averageValueP1000,
00134             delimLen,
00135             // start of reference parameters
00136             cv.basicPos,
00137             cv.orig,
00138             cv.dest,
00139             cv.pXDeltaFactor,
00140             cv.pYDeltaFactor,
00141             cv.pXDelimDeltaFaktor,
00142             cv.pYDelimDeltaFaktor,
00143             cv.nSubDelimFactor,
00144             cv.pDelimDelta,
00145             cv.nTxtHeight,
00146             cv.pTextsX,
00147             cv.pTextsY,
00148             cv.pTextsW,
00149             cv.pTextsH,
00150             cv.textAlign,
00151             cv.bLogarithmic,
00152             cv.isDateTime,
00153             cv.autoDtLabels,
00154             cv.dtLow,
00155             cv.dtHigh,
00156             cv.dtDeltaScale,
00157             true,
00158             cv.nDelta,
00159             cv.nDeltaPix );
00160             const KDChartAxisParams & para = params.axisParams( axisNumber );
00161             cv.bSteadyCalc = para.axisSteadyValueCalc();
00162             cv.bDecreasing = para.axisValuesDecreasing();
00163             cv.nLow        = para.trueAxisLow();
00164             cv.nHigh       = para.trueAxisHigh();
00165 }
00166 
00167 
00168 bool KDChartAxesPainter::calculateAllAxesLabelTextsAndCalcValues(
00169         QPainter* painter,
00170         KDChartTableDataBase* data,
00171         double areaWidthP1000,
00172         double areaHeightP1000,
00173         double& delimLen)
00174 {
00175     uint iAxis;
00176     double averageValueP1000 = QMIN(areaWidthP1000, areaHeightP1000);//( areaWidthP1000 + areaHeightP1000 ) / 2.0;
00177     //qDebug("KChart::KDChartAxesPainter::calculateAllAxesLabelTextsAndCalcValues()  averageValueP1000: %f", averageValueP1000);
00178     // length of little delimiter-marks indicating axis scaling
00179     delimLen = 20.0 * averageValueP1000; // per mille of area
00180 
00181     // Determine axes calculation values and labels before drawing the axes.
00182 
00183     // step #1: calculate all values independendly from the other axes' values
00184     for( iAxis = 0;  iAxis < KDCHART_MAX_AXES;  ++iAxis )
00185     {
00186         internal__KDChart__CalcValues& cv = calcVal[iAxis];
00187         cv.processThisAxis = (    params()->axisParams( iAxis ).axisVisible()
00188                                && KDChartAxisParams::AxisTypeUnknown
00189                                   != params()->axisParams( iAxis ).axisType() );
00190         if( cv.processThisAxis ){
00191             cv.nSubDelimFactor = 0.0;
00192             cv.pDelimDelta     = 0.0;
00193             cv.nTxtHeight      = 0.0;
00194             cv.pTextsX         = 0.0;
00195             cv.pTextsY         = 0.0;
00196             cv.pTextsW         = 0.0;
00197             cv.pTextsH         = 0.0;
00198             cv.textAlign       = Qt::AlignHCenter | Qt::AlignVCenter;
00199             cv.isDateTime      = false;
00200             cv.autoDtLabels    = false;
00201             calculateLabelTexts( painter,
00202                                  *data,
00203                                  *params(),
00204                                  iAxis,
00205                                  averageValueP1000,
00206                                  delimLen,
00207                                  // start of reference parameters
00208                                  cv.basicPos,
00209                                  cv.orig,
00210                                  cv.dest,
00211                                  cv.pXDeltaFactor,
00212                                  cv.pYDeltaFactor,
00213                                  cv.pXDelimDeltaFaktor,
00214                                  cv.pYDelimDeltaFaktor,
00215                                  cv.nSubDelimFactor,
00216                                  cv.pDelimDelta,
00217                                  cv.nTxtHeight,
00218                                  cv.pTextsX,
00219                                  cv.pTextsY,
00220                                  cv.pTextsW,
00221                                  cv.pTextsH,
00222                                  cv.textAlign,
00223                                  cv.bLogarithmic,
00224                                  cv.isDateTime,
00225                                  cv.autoDtLabels,
00226                                  cv.dtLow,
00227                                  cv.dtHigh,
00228                                  cv.dtDeltaScale );
00229             const KDChartAxisParams & para = params()->axisParams( iAxis );
00230             cv.bSteadyCalc = para.axisSteadyValueCalc();
00231             cv.bDecreasing = para.axisValuesDecreasing();
00232             cv.nLow        = para.trueAxisLow();
00233             cv.nHigh       = para.trueAxisHigh();
00234             cv.nDelta      = para.trueAxisDelta();
00235             cv.nDeltaPix   = para.trueAxisDeltaPixels();
00236             cv.pLastX      = cv.dest.x();
00237             cv.pLastY      = cv.dest.y();
00238         }
00239     }
00240 
00241     // step #2: if isometric axes are desired adjust/re-calculate some values
00242     for ( iAxis = 0; iAxis < KDCHART_MAX_AXES; ++iAxis ){
00243         internal__KDChart__CalcValues& cv = calcVal[iAxis];
00244         if(    cv.processThisAxis
00245                 && cv.bSteadyCalc ){
00246             const KDChartAxisParams & para = params()->axisParams( iAxis );
00247             const uint isoRef = para.isometricReferenceAxis();
00248             if(    KDCHART_NO_AXIS != isoRef
00249                     && iAxis != isoRef
00250                     && (    KDCHART_MAX_AXES  > isoRef
00251                          || KDCHART_ALL_AXES == isoRef ) ){
00252                 if( KDCHART_ALL_AXES == isoRef ){
00253                     uint iAxis2;
00254                     // first find the axis values to be taken as reference
00255                     double nDelta          = cv.nDelta;
00256                     double nDeltaPix       = cv.nDeltaPix;
00257                     double nSubDelimFactor = cv.nSubDelimFactor;
00258                     for ( iAxis2 = 0;
00259                           iAxis2 < KDCHART_MAX_AXES;
00260                           ++iAxis2 ){
00261                         internal__KDChart__CalcValues& cv2 = calcVal[iAxis2];
00262                         if(    cv2.processThisAxis
00263                             && cv2.bSteadyCalc
00264                             && (0.0 != cv2.nDelta)
00265                             && (fabs(cv2.nDeltaPix / cv2.nDelta) < fabs(nDeltaPix / nDelta)) ){
00266                             if( (nDelta >= 0.0) == (cv2.nDelta >= 0.0) )
00267                                 nDelta = cv2.nDelta;
00268                             else
00269                                 nDelta = cv2.nDelta * -1.0;
00270                             if( (nDeltaPix >= 0.0) == (cv2.nDeltaPix >= 0.0) )
00271                                 nDeltaPix = cv2.nDeltaPix;
00272                             else
00273                                 nDeltaPix = cv2.nDeltaPix * -1.0;
00274                             if( (nSubDelimFactor >= 0.0) == (cv2.nSubDelimFactor >= 0.0) )
00275                                 nSubDelimFactor = cv2.nSubDelimFactor;
00276                             else
00277                                 nSubDelimFactor = cv2.nSubDelimFactor * -1.0;
00278                         }
00279                     }
00280                     // now adjust all axes (if necessary)
00281                     for ( iAxis2 = 0;
00282                           iAxis2 < KDCHART_MAX_AXES;
00283                           ++iAxis2 ){
00284                         internal__KDChart__CalcValues& cv2 = calcVal[iAxis2];
00285                         if(    cv2.processThisAxis
00286                             && cv2.bSteadyCalc
00287                             && (    fabs(cv2.nDelta)    != fabs(nDelta)
00288                                  || fabs(cv2.nDeltaPix) != fabs(nDeltaPix) ) ){
00289                             //qDebug("\nrecalculating scale for axis %x", iAxis2);
00290                             //qDebug("cv2.nDelta %f   cv2.nDeltaPix %f       nDelta %f   nDeltaPix %f\n",
00291                             //        cv2.nDelta,cv2.nDeltaPix,nDelta,nDeltaPix);
00292                             if( (cv2.nDelta >= 0.0) == (nDelta >= 0.0) )
00293                                 cv2.nDelta = nDelta;
00294                             else
00295                                 cv2.nDelta = nDelta * -1.0;
00296                             if( (cv2.nDeltaPix >= 0.0) == (nDeltaPix >= 0.0) )
00297                                 cv2.nDeltaPix = nDeltaPix;
00298                             else
00299                                 cv2.nDeltaPix = nDeltaPix * -1.0;
00300                             reCalculateLabelTexts( painter,
00301                                                    *data,
00302                                                    *params(),
00303                                                    iAxis2,
00304                                                    averageValueP1000,
00305                                                    delimLen,
00306                                                    cv2 );
00307                             if( (cv2.nSubDelimFactor >= 0.0) == (nSubDelimFactor >= 0.0) )
00308                                 cv2.nSubDelimFactor = nSubDelimFactor;
00309                             else
00310                                 cv2.nSubDelimFactor = nSubDelimFactor * -1.0;
00311                         }
00312                     }
00313                 }else{
00314                     internal__KDChart__CalcValues& cv2 = calcVal[isoRef];
00315                     // adjust this axis or the other axis (if necessary)
00316                     if(    cv2.processThisAxis
00317                             && cv2.bSteadyCalc
00318                             && (    cv2.nDelta    != cv.nDelta
00319                                 || cv2.nDeltaPix != cv.nDeltaPix ) ){
00320                         if(    cv2.nDelta > cv.nDelta
00321                                 || (     cv2.nDelta   == cv.nDelta
00322                                     && cv2.nDeltaPix < cv.nDeltaPix ) ){
00323                             // adjust this axis
00324                             //qDebug("recalculating scale for this axis %x", iAxis);
00325                             cv.nDelta    = cv2.nDelta;
00326                             cv.nDeltaPix = cv2.nDeltaPix;
00327                             reCalculateLabelTexts(
00328                                     painter,
00329                                     *data,
00330                                     *params(),
00331                                     iAxis,
00332                                     averageValueP1000,
00333                                     delimLen,
00334                                     cv );
00335                             cv.nSubDelimFactor = cv2.nSubDelimFactor;
00336                         }else{
00337                             // adjust the other axis
00338                             //qDebug("\nrecalculating scale for other axis %x", isoRef);
00339                             //qDebug("cv2.nDelta %f   cv2.nDeltaPix %f       cv.nDelta %f   cv.nDeltaPix %f",
00340                             //        cv2.nDelta,cv2.nDeltaPix,cv.nDelta,cv.nDeltaPix);
00341                             cv2.nDelta    = cv.nDelta;
00342                             cv2.nDeltaPix = cv.nDeltaPix;
00343                             reCalculateLabelTexts(
00344                                     painter,
00345                                     *data,
00346                                     *params(),
00347                                     isoRef,
00348                                     averageValueP1000,
00349                                     delimLen,
00350                                     cv2 );
00351                             cv2.nSubDelimFactor = cv.nSubDelimFactor;
00352                         }
00353                     }
00354                 }
00355             }
00356         }
00357     }
00358     return true;
00359 }
00360 
00361 
00368 void KDChartAxesPainter::paintAxes( QPainter* painter,
00369         KDChartTableDataBase* data )
00370 {
00371     if ( !painter || !data || 0 == params() )
00372         return ;
00373 
00374     const bool bMultiRowBarChart = KDChartParams::Bar == params()->chartType() &&
00375                                    KDChartParams::BarMultiRows == params()->barChartSubType();
00376 
00377     double areaWidthP1000 = _logicalWidth / 1000.0;
00378     double areaHeightP1000 = _logicalHeight / 1000.0;
00379     double averageValueP1000 = QMIN(areaWidthP1000, areaHeightP1000);//( areaWidthP1000 + areaHeightP1000 ) / 2.0;
00380     // length of little delimiter-marks indicating axis scaling
00381     double delimLen;
00382 
00383 //qDebug("-------------------------------------------------------------------------------------");
00384 
00385     calculateAllAxesLabelTextsAndCalcValues( painter, data, areaWidthP1000, areaHeightP1000, delimLen );
00386 
00387 
00388     // Now the labels are known, so let us paint the axes...
00389     painter->save();
00390     painter->setPen( Qt::NoPen );
00391 
00392     bool screenOutput = params()->optimizeOutputForScreen();
00393     uint iAxis;
00394 
00395     for ( iAxis = 0; iAxis < KDCHART_MAX_AXES; ++iAxis ){
00396         internal__KDChart__CalcValues& cv = calcVal[iAxis];
00397         if( cv.processThisAxis ){
00398 
00399             const KDChartAxisParams & para = params()->axisParams( iAxis );
00400 
00401             internal__KDChart__CalcValues& cv = calcVal[iAxis];
00402 
00403             const QColor labelsColor( para.axisLabelsColor() );
00404 
00405             // Debugging axis areas:
00406             //painter->fillRect(para.axisTrueAreaRect(), Qt::yellow);
00407 
00408             uint lineWidth = 0 <= para.axisLineWidth()
00409                 ? para.axisLineWidth()
00410                 : -1 * static_cast < int > ( para.axisLineWidth()
00411                         * averageValueP1000 );
00412             ( ( KDChartAxisParams& ) para ).setAxisTrueLineWidth( lineWidth );
00413 
00414             uint gridLineWidth
00415                 = ( KDCHART_AXIS_GRID_AUTO_LINEWIDTH
00416                         == para.axisGridLineWidth() )
00417                 ? lineWidth
00418                 : (   ( 0 <= para.axisGridLineWidth() )
00419                         ? para.axisGridLineWidth()
00420                         : -1 * static_cast < int > ( para.axisGridLineWidth()
00421                             * averageValueP1000 ) );
00422 
00423             uint gridSubLineWidth
00424                 = ( KDCHART_AXIS_GRID_AUTO_LINEWIDTH
00425                         == para.axisGridSubLineWidth() )
00426                 ? lineWidth
00427                 : (   ( 0 <= para.axisGridSubLineWidth() )
00428                         ? para.axisGridSubLineWidth()
00429                         : -1 * static_cast < int > ( para.axisGridSubLineWidth()
00430                             * averageValueP1000 ) );
00431 
00432             // Magic to find out axis scaling factors and labels text height
00433             // =============================================================
00434             //                                             - khz, 02/24/2001
00435             //
00436             // 1st Calculate the axis label texts height regarding to
00437             //     user-defined per-axis settings.
00438             //
00439             // 2nd This height is given to calculateLabelTexts() to
00440             //     calculate the delimiter and sub-delimiter distances as
00441             //     well as the axis scaling factors.
00442             //     If neccessary and possible the short replacement strings
00443             //     are taken that might have been specified by the user.
00444             //     - see KDChartAxisParams::setAxisLabelStringLists() -
00445             //
00446             // 3rd Before displaying the texts we make sure they fit into
00447             //     their space, if needed we will do the following
00448             //     in order to avoid clipping of text parts:
00449             //
00450             //     (a) ABSCISSA axes only: rotate the texts in 5 steps
00451             //                             until they are drawn vertically
00452             //
00453             //     (b) further reduce the texts' font height down to 6pt
00454             //         .
00455             //
00456             // If the texts *still* don't fit into their space, we are lost
00457             // and they will be clipped. Such is live.
00458             //
00459             // Why all this?
00460             //
00461             // Because I do not believe in axis areas growing and shrinking
00462             // regarding to long or short label texts: start such behaviour
00463             // and become mad.
00464             //
00465             // Better plan: ask the user to specify a way how to abbreviate
00466             //              label texts (e.g. by writing "200" instead
00467             //              of that wide and unreadable  "200,000.00")
00468             //
00469             //
00470             //   F E A T U R E   P L A N N E D   F O R   F U T U R E . . .
00471             //
00472             //
00473 
00474             // Note: The labels-touch-edges flag may have been set to true
00475             //       inside the calculateLabelTexts() function.
00476             bool bTouchEdges = para.axisLabelsTouchEdges();
00477 
00478             // NOTE: The steady-value-calc flag may have been set to true
00479             //       inside the calculateLabelTexts() function
00480             //       by a special setAxisLabelTextParams() call,
00481             //       therefor we do not store its value before calling that function.
00482             if( cv.bLogarithmic )
00483                 cv.nSubDelimFactor = 0.1;
00484 
00485             const double nUsableAxisHeight = cv.pTextsH;
00486             const double nUsableAxisWidth  = cv.pTextsW;
00487 
00488             const bool isHorizontalAxis
00489                 = (KDChartAxisParams::AxisPosBottom == cv.basicPos) ||
00490                 (KDChartAxisParams::AxisPosTop    == cv.basicPos);
00491 
00492             QStringList* labelTexts = ( QStringList* ) para.axisLabelTexts();
00493             uint nLabels = ( 0 != labelTexts )
00494                 ? labelTexts->count()
00495                 : 0;
00496             // start point of 1st delimiter on the axis-line == grid-start
00497             QPoint p1( cv.orig );
00498             // end point of 1st delimiter near the label text
00499             QPoint p2( cv.orig );
00500             // end point of small sub-delimiter
00501             QPoint p2a( cv.orig );
00502             // start point of 1st grid-line (beginnig at the axis)
00503             QPoint pGA( cv.orig );
00504             // end point of 1st grid-line at the other side of the chart
00505             QPoint pGZ( cv.orig );
00506             // start point of zero-line, this is often identical with p1
00507             // but will be different in case of shifted zero-line
00508             double axisZeroLineStartX = p1.x();
00509             double axisZeroLineStartY = p1.y();
00510 
00511             p2.setX(  p2.x()  + static_cast < int > ( cv.pXDelimDeltaFaktor * delimLen ) );
00512             p2.setY(  p2.y()  + static_cast < int > ( cv.pYDelimDeltaFaktor * delimLen ) );
00513             p2a.setX( p2a.x() + static_cast < int > ( cv.pXDelimDeltaFaktor * delimLen * 2.0 / 3.0 ) );
00514             p2a.setY( p2a.y() + static_cast < int > ( cv.pYDelimDeltaFaktor * delimLen * 2.0 / 3.0 ) );
00515             pGZ.setX( pGZ.x() - static_cast < int > ( cv.pXDelimDeltaFaktor * (_dataRect.width()  - 1) ) );
00516             pGZ.setY( pGZ.y() - static_cast < int > ( cv.pYDelimDeltaFaktor * (_dataRect.height() - 1) ) );
00517 
00518             if ( nLabels ) {
00519                 // Sometimes the first or last labels partially reach out of
00520                 // their axis area: we allow this
00521                 const bool oldClippingFlag = painter->hasClipping();
00522                 painter->setClipping( false );
00523 
00524                 if( para.hasAxisFirstLabelText() )
00525                     labelTexts->first() = para.axisFirstLabelText();
00526                 if( para.hasAxisLastLabelText() )
00527                     labelTexts->last() = para.axisLastLabelText();
00528 
00529                 const double pXDelta = cv.pXDeltaFactor * cv.pDelimDelta;
00530                 const double pYDelta = cv.pYDeltaFactor * cv.pDelimDelta;
00531 
00532                 // draw label texts and delimiters and grid
00533                 painter->setPen( QPen( para.axisLineColor(),
00534                                  lineWidth ) );
00535 
00536                 const QString formatDT = cv.isDateTime
00537                     ? para.axisLabelsDateTimeFormat()
00538                     : QString();
00539 
00540                 // calculate font size
00541                 const double minTextHeight = para.axisLabelsFontMinSize();
00542                 //qDebug("KChart::KDChartAxesPainter::paintAxes()  cv.nTxtHeight: %f   minTextHeight: %f", cv.nTxtHeight, minTextHeight);
00543                 if ( minTextHeight > cv.nTxtHeight )
00544                     cv.nTxtHeight = minTextHeight;
00545                 QFont actFont( para.axisLabelsFont() );
00546                 if ( para.axisLabelsFontUseRelSize() ) {
00547                     actFont.setPixelSize( static_cast < int > ( cv.nTxtHeight ) );
00548                 }
00549                 painter->setFont( actFont );
00550                 QFontMetrics fm( painter->fontMetrics() );
00551 
00552                 int nLeaveOut = 0;
00553                 int nRotation = 0;
00554 
00555                 // Draw simple string labels
00556                 // or calculate and draw nice Date/Time ruler?
00557                 QString commonDtHeader;
00558                 if( cv.autoDtLabels ){
00559                     cv.textAlign = Qt::AlignCenter;
00560                     //qDebug(dtLow.toString("\nd.MM.yyyy  -  h:mm:ss" ));
00561                     //qDebug(dtHigh.toString( "d.MM.yyyy  -  h:mm:ss" ));
00562                     const QDate& dLow  = cv.dtLow.date();
00563                     const QTime& tLow  = cv.dtLow.time();
00564                     const QDate& dHigh = cv.dtHigh.date();
00565                     const QTime& tHigh = cv.dtHigh.time();
00566                     bool sameYear   = dLow.year() == dHigh.year();
00567                     bool sameMonth  = sameYear   && (dLow.month()  == dHigh.month() );
00568                     bool sameDay    = sameMonth  && (dLow.day()    == dHigh.day()   );
00569                     bool sameHour   = sameDay    && (tLow.hour()   == tHigh.hour()  );
00570                     bool sameMinute = sameHour   && (tLow.minute() == tHigh.minute());
00571                     bool sameSecond = sameMinute && (tLow.second() == tHigh.second());
00572                     if( sameDay ){
00573                         commonDtHeader = QString::number( dLow.day() )
00574                             + ". "
00575 #if COMPAT_QT_VERSION >= 0x030000
00576                             + QDate::longMonthName( dLow.month() )
00577 #else
00578                             + dLow.monthName( dLow.month() )
00579 #endif
00580                             + ' '
00581                             + QString::number( dLow.year() );
00582                         if( sameHour ){
00583                             commonDtHeader += "  /  "
00584                                 + QString::number( tLow.hour() )
00585                                 + ':';
00586                             if( sameMinute ){
00587                                 if( 10 > tLow.minute() )
00588                                     commonDtHeader += '0';
00589                                 commonDtHeader += QString::number( tLow.minute() )
00590                                     + ':';
00591                                 if( sameSecond ){
00592                                     if( 10 > tLow.second() )
00593                                         commonDtHeader += '0';
00594                                     commonDtHeader += QString::number( tLow.second() );
00595                                     //
00596                                     // " Huston, we have a problem! "
00597                                     //
00598                                     // Currently we don't support milli secs
00599                                     // since they will not fit into a double
00600                                     // when looking at years...
00601                                     //
00602                                     // This will be improved in release 2.0.
00603                                     //                     (khz, 2002/07/12)
00604                                 }
00605                                 else
00606                                     commonDtHeader += "00";
00607                             }
00608                             else
00609                                 commonDtHeader += "00";
00610                         }
00611                     }else if( sameMonth )
00612 #if COMPAT_QT_VERSION >= 0x030000
00613                         commonDtHeader = QDate::longMonthName( dLow.month() )
00614 #else
00615                             commonDtHeader = dLow.monthName( dLow.month() )
00616 #endif
00617                             + ' '
00618                             + QString::number( dLow.year() );
00619                     else if( sameYear )
00620                         commonDtHeader = QString::number( dLow.year() );
00621                     //if( !commonDtHeader.isEmpty() )
00622                     //    qDebug(commonDtHeader);
00623                 }else{
00624                     // make sure all label texts fit into their space
00625                     // by rotating and/or shrinking the texts
00626                     // or by leaving out some of the labels
00627                     QRegion unitedRegions;
00628 
00629                     const bool tryLeavingOut =
00630                         ( para.axisValueLeaveOut() == KDCHART_AXIS_LABELS_AUTO_LEAVEOUT )
00631                         || ( 0 < para.axisValueLeaveOut() );
00632                     if( tryLeavingOut ) {
00633                         if( para.axisValueLeaveOut()
00634                                 == KDCHART_AXIS_LABELS_AUTO_LEAVEOUT )
00635                             nLeaveOut = 0;
00636                         else
00637                             nLeaveOut = para.axisValueLeaveOut();
00638 
00639                     }
00640                     else
00641                         nLeaveOut = 0;
00642                     int stepWidthLeaveOut = nLeaveOut+1;
00643                     int iStepsLeaveOut = 0;
00644 
00645                     const bool tryShrinking = !para.axisLabelsDontShrinkFont();
00646                     const double nInitialTxtHeight = cv.nTxtHeight;
00647 
00648                     const bool tryRotating = isHorizontalAxis
00649                         && !para.axisLabelsDontAutoRotate();
00650                     const int nInitialRotation = (    (360 >  para.axisLabelsRotation())
00651                             && (270 <= para.axisLabelsRotation()) )
00652                         ? para.axisLabelsRotation()
00653                         : 0;
00654                     nRotation = nInitialRotation;
00655 
00656                     bool textsDontFitIntoArea;
00657                     bool textsOverlapping;
00658                     bool textsMatching;
00659                     do {
00660                         textsDontFitIntoArea = false;
00661                         textsOverlapping = false;
00662                         textsMatching = true;
00663                         // test if all texts match without mutually overlapping
00664                         unitedRegions = QRegion();
00665                         int align = nRotation
00666                             ? (Qt::AlignRight | Qt::AlignVCenter) // adjusting for rotation
00667                             : cv.textAlign;
00668                         QPoint anchor(200,200);
00669                         int iLeaveOut = 0;
00670                         double iLabel=0.0;
00671                         for ( QStringList::Iterator it = labelTexts->begin();
00672                                 it != labelTexts->end();
00673                                 ++it ) {
00674                             iLabel += 1.0;
00675                             if( iLeaveOut < nLeaveOut ) {
00676                                 ++iLeaveOut;
00677                             } else {
00678                                 iLeaveOut = 0;
00679                                 anchor.setX( p2.x() + static_cast < int > ( pXDelta * (iLabel - 0.5) ) );
00680                                 anchor.setY( p2.y() + static_cast < int > ( pYDelta * (iLabel - 0.5) ) );
00681 
00682                                 // allow for shearing and/or scaling of the painter
00683                                 anchor = painter->worldMatrix().map( anchor );
00684 
00685                                 QString text;
00686                                 if( cv.isDateTime ){
00687 #if COMPAT_QT_VERSION >= 0x030000
00688                                     QDateTime dt( QDateTime::fromString( *it,
00689                                                 Qt::ISODate ) );
00690                                     text = dt.toString( formatDT );
00691 #else
00692                                     QDateTime dt( dateTimeFromString( *it ) );
00693                                     text = dt.toString();
00694 #endif
00695                                 }else{
00696                                     text = *it;
00697                                 }
00698                                 KDDrawTextRegionAndTrueRect infosKDD =
00699                                     KDDrawText::measureRotatedText( painter,
00700                                             nRotation,
00701                                             anchor,
00702                                             text,
00703                                             0,
00704                                             align,
00705                                             &fm,
00706                                             false,
00707                                             false,
00708                                             15 );
00709                                 if( infosKDD.region.boundingRect().left()
00710                                         < params()->globalLeadingLeft()+1 ){
00711                                     textsMatching = false;
00712                                     textsDontFitIntoArea = true;
00713                                     //qDebug("too wide");
00714                                 }
00715                                 //qDebug("nRotation: %i",nRotation);
00716                                 QRegion sectReg;
00717                                 if( nRotation ){
00718                                     //qDebug("test 1");
00719                                     sectReg = infosKDD.region.intersect( unitedRegions );
00720                                 }else{
00721                                     //qDebug("test 2");
00722                                     QRect rect( infosKDD.region.boundingRect() );
00723                                     rect.addCoords(-2,-2,2,2);
00724                                     QRegion biggerRegion( rect );
00725                                     sectReg = biggerRegion.intersect( unitedRegions );
00726                                 }
00727                                 if ( sectReg.isEmpty() )
00728                                     unitedRegions = unitedRegions.unite( infosKDD.region );
00729                                 else {
00730                                     textsMatching = false;
00731                                     textsOverlapping = true;
00732                                     //qDebug("label regions are intersecting");
00733                                     break;
00734                                 }
00735                             }
00736                         }
00737 /*
00738                         if(!iAxis){
00739 
00740                           qDebug("nTxtHeight: "+QString::number(cv.nTxtHeight)+"   nRotation: "+QString::number(nRotation)+
00741                           "   matching: "+QString(textsMatching ? "TRUE":"FALSE"));
00742                           qDebug("nUsableAxisHeight: %f,  unitedRegions.boundingRect().height(): %i ",
00743                               nUsableAxisHeight, unitedRegions.boundingRect().height());
00744                           }
00745 */
00746                         if( isHorizontalAxis ) {
00747                             if( nUsableAxisHeight < unitedRegions.boundingRect().height() ){
00748                                 //textsMatching = false;
00749                                 textsDontFitIntoArea = true;
00750                             }
00751                         } else {
00752                             if( nUsableAxisWidth < unitedRegions.boundingRect().width() ){
00753                                 //qDebug("textsMatching: %s",textsMatching ? "TRUE" : "FALSE");
00754                                 textsMatching = false;
00755                                 textsDontFitIntoArea = true;
00756                                 //qDebug("too wide");
00757                             }
00758                             //else qDebug("not too wide");
00759                         }
00760                         /*
00761                         if(textsMatching && !iAxis){
00762                           qDebug("--------------------------");
00763                           qDebug("nTxtHeight: "+QString::number(cv.nTxtHeight)+"   nRotation: "+QString::number(nRotation));
00764                           qDebug("matching");
00765                           }
00766                         */
00767                         if( !textsMatching ) {
00768                             bool rotatingDoesNotHelp = false;
00769                             // step 1: In case of labels being too wide
00770                             //         to fit into the available space
00771                             //         we try to rotate the texts in 5 steps.
00772                             //         This is done for Abscissa axes only.
00773                             if ( tryRotating ) {
00774                                 //qDebug("try rotating");
00775                                 // The following is designed for horizontal axes
00776                                 // since we currently don't support label rotating
00777                                 // on vertical axes.             (khz, 2002/08/15)
00778                                 if( textsDontFitIntoArea  ){
00779                                     if( nRotation != nInitialRotation ){
00780                                       //textsDontFitIntoArea = false;
00781                                       nRotation = nInitialRotation;
00782                                     }
00783                                     rotatingDoesNotHelp = true;
00784                                     //qDebug("rotating does not help (a)");
00785                                 }
00786                                 else{
00787                                     if( nRotation ) {
00788                                         if( 270 < nRotation ) {
00789                                             nRotation -= 5;
00790                                             if( 270 > nRotation )
00791                                                 nRotation = 270; // drawing vertically now
00792                                         } else {
00793                                             if( nInitialRotation )
00794                                                 nRotation = nInitialRotation;
00795                                             else
00796                                                 nRotation = 0; // reset rotation to ZERO
00797                                             rotatingDoesNotHelp = true;
00798                                             //qDebug("rotating does not help (b)");
00799                                         }
00800                                     } else {
00801                                         if( nInitialRotation )
00802                                             nRotation = nInitialRotation;
00803                                         else
00804                                             nRotation = 350; // (re-)start rotating with -10
00805                                     }
00806                                 }
00807                             }
00808                             if ( !tryRotating || rotatingDoesNotHelp ) {
00809 
00810                                 // step 2: In case of labels being too wide and
00811                                 //         rotating them did not help or is forbidden
00812                                 //         we try to reduce the font size.
00813                                 if ( tryShrinking && (minTextHeight < cv.nTxtHeight) ) {
00814                                     //qDebug("try shrinking");
00815                                     cv.nTxtHeight -= 1.0;
00816                                     if ( minTextHeight > cv.nTxtHeight )
00817                                         cv.nTxtHeight = minTextHeight;
00818                                 } else {
00819 
00820                                     // step 3: In case reducing the font size is not possible
00821                                     //         any further (or is not allowed at all) we try
00822                                     //         to leave out some of the labels.
00823                                     if(    tryLeavingOut
00824                                         && textsOverlapping
00825                                         && (nLeaveOut+1 < static_cast < int > ( nLabels ) ) ) {
00826                                         //qDebug("try leaving out");
00827                                         ++iStepsLeaveOut;
00828                                         //if(!iAxis)qDebug("iStepsLeaveOut: %i", iStepsLeaveOut);
00829                                         nLeaveOut =
00830                                             iStepsLeaveOut*stepWidthLeaveOut - 1;
00831                                         if( tryShrinking )
00832                                             cv.nTxtHeight = nInitialTxtHeight;
00833                                     }
00834                                     else
00835                                         break;
00836                                 }
00837                                 if( tryShrinking ) {
00838                                     actFont.setPixelSize( static_cast < int > ( cv.nTxtHeight ) );
00839                                     //qDebug("axis:     cv.nTxtHeight: %f", iAxis, cv.nTxtHeight);
00840                                     painter->setFont( actFont );
00841                                     fm = painter->fontMetrics();
00842                                 }
00843                             }
00844                         }
00845 //qDebug("nLeaveOut: %i",nLeaveOut);
00846                     } while( !textsMatching );
00847 
00848                     if( nRotation ){
00849                         // The following is designed for horizontal axes
00850                         // since we currently don't support label rotating
00851                         // on vertical axes.             (khz, 2002/08/15)
00852                         //int oldVert = textAlign & (Qt::AlignTop | Qt::AlignBottom);
00853                         //int steepness = abs(270-nRotation);
00854                         //bool steep = (30 > steepness);
00855                         cv.textAlign = Qt::AlignRight | Qt::AlignVCenter;  // adjusting for rotation
00856                         //cv.textAlign = Qt::AlignRight | Qt::AlignVCenter;
00857                         /*  ( steep ? Qt::AlignVCenter : oldVert);*/
00858                         //int dx = pXDelta / 2 - steep ? (nTxtHeight / 4) : 0;
00859                         double dx = (pXDelta / 2) - (cv.nTxtHeight / 4);
00860                         double dy = /*steep ? 0 : */(cv.nTxtHeight / 2.0);
00861                         cv.pTextsX += dx;
00862                         cv.pTextsY += dy;
00863                     }
00864                     /*
00865                        QBrush oldBrush = painter->brush();
00866                        QRegion oldReg = painter->clipRegion();//QPainter::CoordPainter);
00867                        painter->setBrush(Qt::Dense4Pattern);
00868                        painter->setClipRegion(unitedRegions);//,QPainter::CoordPainter);
00869                        painter->drawRect(0,0,2000,1500);
00870                        painter->setClipRegion(oldReg);//,QPainter::CoordPainter);
00871                        painter->setBrush(oldBrush);
00872                        */
00873                     /*if(!iAxis){
00874                       qDebug("==========================");
00875                       qDebug("nTxtHeight: "+QString::number(nTxtHeight)+"   nRotation: "+QString::number(nRotation));
00876                       qDebug(textsMatching ? "matching":"not matching");
00877                       }*/
00878                 }
00879 
00880                 painter->setFont( actFont );
00881                 fm = QFontMetrics( painter->fontMetrics() );
00882 
00883                 // set colour of grid pen
00884                 QPen gridPen, leaveOutGridPen;
00885                 if( para.axisShowGrid() && !bMultiRowBarChart )
00886                     gridPen.setColor( para.axisGridColor() );
00887 
00888                 const int pXDeltaDiv2 = static_cast < int > ( pXDelta / 2.0 );
00889                 const int pYDeltaDiv2 = static_cast < int > ( pYDelta / 2.0 );
00890 
00891                 bool bDrawAdditionalSubGridLine = false;
00892                 double pGXMicroAdjust = 0.0;
00893                 double pGYMicroAdjust = 0.0;
00894                 if ( !bTouchEdges ) {
00895                     // adjust the data values pos
00896                     p1.setX( p1.x() + pXDeltaDiv2 );
00897                     p1.setY( p1.y() + pYDeltaDiv2 );
00898                     p2.setX( p2.x() + pXDeltaDiv2 );
00899                     p2.setY( p2.y() + pYDeltaDiv2 );
00900                     // adjust the short delimiter lines pos
00901                     p2a.setX( p2a.x() + pXDeltaDiv2 );
00902                     p2a.setY( p2a.y() + pYDeltaDiv2 );
00903                     // adjust grid lines pos
00904                     bDrawAdditionalSubGridLine =
00905                         isHorizontalAxis && !
00906                         params()->axisParams(
00907                                 KDChartAxisParams::AxisPosRight ).axisVisible() &&
00908                         !bMultiRowBarChart;
00909                     pGA.setX( pGA.x() + pXDeltaDiv2 );
00910                     pGA.setY( pGA.y() + pYDeltaDiv2 );
00911                     pGZ.setX( pGZ.x() + pXDeltaDiv2 );
00912                     pGZ.setY( pGZ.y() + pYDeltaDiv2 );
00913                     // fine-tune grid line pos for grid of vertical axis
00914                     if( KDChartAxisParams::AxisTypeNORTH == para.axisType() ) {
00915                         pGXMicroAdjust = cv.pXDeltaFactor * lineWidth / 2.0;
00916                         pGYMicroAdjust = cv.pYDeltaFactor * lineWidth / 2.0;
00917                     }
00918                 }
00919                 double x1, y1, x2, y2, xGA, yGA, xGZ, yGZ,
00920                 p1X, p1Y, p2X, p2Y, pGAX, pGAY, pGZX, pGZY, xT, yT;
00921 
00922                 double pXSubDelimDelta = pXDelta * cv.nSubDelimFactor;
00923                 double pYSubDelimDelta = pYDelta * cv.nSubDelimFactor;
00924 
00925                 if (    !cv.autoDtLabels
00926                         && 0.0 != cv.nSubDelimFactor
00927                         && para.axisShowSubDelimiters()
00928                         && para.axisLabelsVisible()
00929                         && !nLeaveOut ) {
00930                     QPen pen( para.axisLineColor(), static_cast < int > ( 0.5 * lineWidth ) );
00931                     uint penWidth = pen.width();
00932                     bool bOk = true;
00933 
00934                     if( cv.bLogarithmic )
00935                         cv.nSubDelimFactor = 0.1;
00936 
00937                     while ( fabs( ( pXDelta + pYDelta ) * cv.nSubDelimFactor / 6.0 )
00938                             <= 1.0 + penWidth
00939                             && bOk ) {
00940                         if ( 0 < penWidth ) {
00941                             --penWidth;
00942                             pen.setWidth( penWidth );
00943                         }else{
00944                             if( cv.bLogarithmic ){
00945                                 break; // there is nothing we can do: we allways
00946                                 // want 10 sub-delims per logarithmic step
00947                             }else{
00948                                 if ( 0.5 != cv.nSubDelimFactor ) {
00949                                     // emercency: reduce number of sub-scaling
00950                                     cv.nSubDelimFactor = 0.5;
00951 
00952                                     pXSubDelimDelta = pXDelta * cv.nSubDelimFactor;
00953                                     pYSubDelimDelta = pYDelta * cv.nSubDelimFactor;
00954                                 } else
00955                                     bOk = false;
00956                             }
00957                         }
00958                     }
00959                     if ( bOk ) {
00960                         x1 = p1.x();
00961                         y1 = p1.y();
00962                         x2 = p2a.x();
00963                         y2 = p2a.y();
00964                         xGA = pGA.x();
00965                         yGA = pGA.y();
00966                         xGZ = pGZ.x();
00967                         yGZ = pGZ.y();
00968                         p1X = x1;
00969                         p1Y = y1;
00970                         p2X = x2;
00971                         p2Y = y2;
00972                         pGAX = xGA;
00973                         pGAY = yGA;
00974                         pGZX = xGZ;
00975                         pGZY = yGZ;
00976 
00977                         // set up grid pen for drawing the sub-grid lines
00978                         const QPen oldGridPen( gridPen );
00979                         if ( para.axisShowGrid() ) {
00980                             gridPen.setColor( para.axisGridSubColor() );
00981                             gridPen.setWidth( gridSubLineWidth );
00982                             gridPen.setStyle( para.axisGridSubStyle() );
00983                         }
00984                         const QPen oldPen( painter->pen() );
00985                         painter->setPen( pen );
00986                         double nSubDelim = ( labelTexts->count() - 1 )
00987                             / cv.nSubDelimFactor;
00988 
00989                         //qDebug("subDelim: %f",
00990                         modf( nSubDelim, &nSubDelim );
00991 
00992                         int logarithCnt = 1;
00993                         double xLogarithOffs = 0;
00994                         double yLogarithOffs = 0;
00995                         double dDummy;
00996                         double mainDelim = 0.0;
00997                         bool paint = true;
00998 
00999                         for ( double iDelim = 1.0;
01000                                 iDelim <= nSubDelim + 1.0;
01001                                 iDelim += 1.0, logarithCnt++ ) {
01002                           // test if it is a sub or a main delimiter
01003                             if ( mainDelim > 0.0 )
01004                               paint = true;
01005                             else
01006                               paint = false;
01007 
01008                             if ( cv.bLogarithmic )
01009                             {
01010                                 if ( logarithCnt == 11 )
01011                                 {
01012                                     xLogarithOffs +=
01013                                         pXDelta * log10( 10*cv.nSubDelimFactor*10 );
01014                                     yLogarithOffs +=
01015                                         pYDelta * log10( 10*cv.nSubDelimFactor*10 );
01016                                     logarithCnt=1;
01017                                 }
01018 
01019                                 pXSubDelimDelta =
01020                                     pXDelta * log10( 10*cv.nSubDelimFactor*logarithCnt );
01021                                 pYSubDelimDelta =
01022                                     pYDelta * log10( 10*cv.nSubDelimFactor*logarithCnt );
01023                             }
01024 
01025                             if ( para.axisShowGrid() && !bMultiRowBarChart) {
01026                                 // draw the sub grid line
01027                                 if( 0.0 != modf((iDelim-1.0) * cv.nSubDelimFactor, &dDummy) )
01028 
01029                                     saveDrawLine( *painter,
01030                                                   QPoint( static_cast<int>( pGAX - pGXMicroAdjust ),
01031                                                           static_cast<int>( pGAY - pGYMicroAdjust ) ),
01032                                                   QPoint( static_cast<int>( pGZX - pGXMicroAdjust ),
01033                                                           static_cast<int>( pGZY - pGYMicroAdjust ) ),
01034                                                   gridPen );
01035 
01036                                 if( cv.bLogarithmic ){
01037                                     pGAX = xGA + pXSubDelimDelta + xLogarithOffs;
01038                                     pGAY = yGA + pYSubDelimDelta + yLogarithOffs;
01039                                     pGZX = xGZ + pXSubDelimDelta + xLogarithOffs;
01040                                     pGZY = yGZ + pYSubDelimDelta + yLogarithOffs;
01041                                 }else{
01042                                     pGAX = xGA + iDelim * pXSubDelimDelta;
01043                                     pGAY = yGA + iDelim * pYSubDelimDelta;
01044                                     pGZX = xGZ + iDelim * pXSubDelimDelta;
01045                                     pGZY = yGZ + iDelim * pYSubDelimDelta;
01046                                     /*
01047                                     if( !modf(iDelim * cv.nSubDelimFactor, &dDummy) ){
01048                                        pGAX = xGA + (iDelim * cv.nSubDelimFactor) * pXDelta;
01049                                        pGAY = yGA + (iDelim * cv.nSubDelimFactor) * pYDelta;
01050                                        pGZX = xGZ + (iDelim * cv.nSubDelimFactor) * pXDelta;
01051                                        pGZY = yGZ + (iDelim * cv.nSubDelimFactor) * pYDelta;
01052                                     }
01053                                     */
01054                                 }
01055                             }
01056 
01057 
01058                             // draw the short delimiter line
01059                             // PENDING: Michel - make sure not to draw the sub-delimiters over the main ones.
01060                             // by testing if it is a sub delimiter or a main one
01061                             if ( paint )
01062                             painter->drawLine( QPoint( static_cast<int>( p1X ), static_cast<int>( p1Y ) ),
01063                                                QPoint( static_cast<int>( p2X ), static_cast<int>( p2Y ) ) );
01064 
01065                             mainDelim += 1.0;
01066 
01067 
01068                             if( cv.bLogarithmic ){
01069                                 p1X = x1 + pXSubDelimDelta + xLogarithOffs;
01070                                 p1Y = y1 + pYSubDelimDelta + yLogarithOffs;
01071                                 p2X = x2 + pXSubDelimDelta + xLogarithOffs;
01072                                 p2Y = y2 + pYSubDelimDelta + yLogarithOffs;
01073                             }else{
01074                                 p1X = x1 + iDelim * pXSubDelimDelta;
01075                                 p1Y = y1 + iDelim * pYSubDelimDelta;
01076                                 p2X = x2 + iDelim * pXSubDelimDelta;
01077                                 p2Y = y2 + iDelim * pYSubDelimDelta;
01078                             }
01079 
01080                             if ( mainDelim >= nSubDelim/(labelTexts->count() -1) )
01081                               mainDelim = 0.0;
01082 
01083 
01084                         } // for
01085                         // draw additional sub grid line
01086                         if( bDrawAdditionalSubGridLine
01087                                 && para.axisShowGrid() ) {
01088 
01089                             saveDrawLine( *painter,
01090                                           QPoint( static_cast<int>( pGAX - pGXMicroAdjust ),
01091                                                   static_cast<int>( pGAY - pGYMicroAdjust ) ),
01092                                           QPoint( static_cast<int>( pGZX - pGXMicroAdjust ),
01093                                                   static_cast<int>( pGZY - pGYMicroAdjust ) ),
01094                                                   gridPen );
01095 
01096                         }
01097                         painter->setPen( oldPen );
01098                         gridPen = oldGridPen;
01099                     }
01100                 }
01101                 x1 = p1.x();
01102                 y1 = p1.y();
01103                 x2 = p2.x();
01104                 y2 = p2.y();
01105                 xGA = pGA.x();
01106                 yGA = pGA.y();
01107                 xGZ = pGZ.x();
01108                 yGZ = pGZ.y();
01109                 p1X = x1;
01110                 p1Y = y1;
01111                 p2X = x2;
01112                 p2Y = y2;
01113                 pGAX = xGA;
01114                 pGAY = yGA;
01115                 pGZX = xGZ;
01116                 pGZY = yGZ;
01117                 xT = cv.pTextsX;
01118                 yT = cv.pTextsY;
01119                 // set up grid pen for drawing the normal grid lines
01120                 if ( para.axisShowGrid() ) {
01121                     gridPen.setWidth( gridLineWidth );
01122                     gridPen.setStyle( para.axisGridStyle() );
01123                     // if axis not visible draw the 1st grid line too
01124                     if( !para.axisLineVisible() )
01125               saveDrawLine( *painter, cv.orig, cv.dest, gridPen );
01126                 }
01127                 if( nLeaveOut ) {
01128                     leaveOutGridPen = gridPen;
01129                     leaveOutGridPen.setWidth( gridLineWidth / 2 );
01130                     leaveOutGridPen.setStyle( Qt::DotLine );
01131                 }
01132                 //  =========================================================
01133                 //  ||  The labels and delimiters and grid printing loops  ||
01134                 //  =========================================================
01135                 //
01136                 double iLabel = 0.0;
01137                 if( cv.autoDtLabels )
01138                 {
01139                     /*
01140                        qDebug("\ndtLow: %i %i %i    %i:%i:%i",
01141                        dtLow.date().year(),
01142                        dtLow.date().month(),
01143                        dtLow.date().day(),
01144                        dtLow.time().hour(),
01145                        dtLow.time().minute(),
01146                        dtLow.time().second());
01147                        qDebug("dtHigh: %i %i %i    %i:%i:%i",
01148                        dtHigh.date().year(),
01149                        dtHigh.date().month(),
01150                        dtHigh.date().day(),
01151                        dtHigh.time().hour(),
01152                        dtHigh.time().minute(),
01153                        dtHigh.time().second());
01154                        */
01155                     int pXD = static_cast <int> (cv.pXDelimDeltaFaktor * 1.25 * (cv.nTxtHeight+4));
01156                     int pYD = static_cast <int> (cv.pYDelimDeltaFaktor * 1.25 * (cv.nTxtHeight+4));
01157                     int orgXD = pXD;
01158                     int orgYD = pYD;
01159                     cv.pTextsW = fabs( (0.0 == pXDelta) ? pXD : pXDelta );
01160                     cv.pTextsH = fabs( (0.0 == pYDelta) ? pYD : pYDelta );
01161 
01162                     double pSecX     = x1;
01163                     double pSecY     = y1;
01164                     bool   secPaint= false;
01165                     double pMinX     = x1;
01166                     double pMinY     = y1;
01167                     bool   minPaint= false;
01168                     double pHourX    = x1;
01169                     double pHourY    = y1;
01170                     bool   hourPaint= false;
01171                     double pDayX     = x1;
01172                     double pDayY     = y1;
01173                     bool   dayPaint= false;
01174                     /* khz: currently not used
01175                        double pWeekX    = x1;
01176                        double pWeekY    = y1;
01177                        bool   weekPaint= false;
01178                        */
01179                     double pMonthX   = x1;
01180                     double pMonthY   = y1;
01181                     bool   monthPaint= false;
01182                     /*double pQuarterX = x1;
01183                       double pQuarterY = y1;
01184                       bool   minPaint= false;
01185                       */
01186                     double pYearX    = x1;
01187                     double pYearY    = y1;
01188                     bool   yearPaint= false;
01189 
01190                     double pXYDelta = fabs( pXDelta ) + fabs( pYDelta );
01191 
01192                     if( 0.0 == para.trueAxisDeltaPixels() )
01193                         ( ( KDChartAxisParams& ) para ).setTrueAxisDeltaPixels( QMIN(_logicalWidth, _logicalHeight) / 150 );
01194 
01195                     bool dtGoDown = cv.dtLow > cv.dtHigh;
01196                     int  mult = dtGoDown ? -1 : 1;
01197                     const QDateTime& startDt = dtGoDown ? cv.dtHigh : cv.dtLow;
01198 
01199                     ( ( KDChartAxisParams& ) para ).setAxisDtLowPos( x1, y1 );
01200                     // adjust stored dt-low and scale settings
01201                     ( ( KDChartAxisParams& ) para ).setTrueAxisDtLow( startDt );
01202                     ( ( KDChartAxisParams& ) para ).setTrueAxisDtScale( cv.dtDeltaScale );
01203 
01204                     int gridDX = pGZ.x() - pGA.x();
01205                     int gridDY = pGZ.y() - pGA.y();
01206                     if ( para.axisShowGrid() ) {
01207                         gridPen.setColor( para.axisGridColor() );
01208                         gridPen.setWidth( gridLineWidth );
01209                         gridPen.setStyle( para.axisGridStyle() );
01210                     }
01211                     QPen subGridPen(    gridPen.color(), 1, para.axisGridStyle() );
01212                     QPen subSubGridPen( gridPen.color(), 1, para.axisGridSubStyle() );
01213                     QPen pen = subGridPen;
01214 
01215                     QDateTime dt(    startDt );
01216                     QDateTime newDt( startDt );
01217                     for( uint i=1; i <= nLabels; ++i ){
01218                         switch( cv.dtDeltaScale ) {
01219                             case KDChartAxisParams::ValueScaleSecond:
01220                                 dtAddSecs( dt, 1 * mult, newDt );
01221                                 break;
01222                             case KDChartAxisParams::ValueScaleMinute:
01223                                 dtAddSecs( dt, 60 * mult, newDt );
01224                                 break;
01225                             case KDChartAxisParams::ValueScaleHour:
01226                                 dtAddSecs( dt, 3600 * mult, newDt );
01227                                 break;
01228                             case KDChartAxisParams::ValueScaleDay:
01229                                 dtAddDays( dt, 1 * mult, newDt );
01230                                 break;
01231                             case KDChartAxisParams::ValueScaleWeek:
01232                                 dtAddDays( dt, 7 * mult, newDt );
01233                                 break;
01234                             case KDChartAxisParams::ValueScaleMonth:
01235                                 dtAddMonths( dt,1 * mult, newDt );
01236                                 break;
01237                             case KDChartAxisParams::ValueScaleQuarter:
01238                                 dtAddMonths( dt,3 * mult, newDt );
01239                                 break;
01240                             case KDChartAxisParams::ValueScaleYear:
01241                                 dtAddYears( dt, 1 * mult, newDt );
01242                                 break;
01243                             default:
01244                                 dtAddDays( dt, 1 * mult, newDt );
01245                                 break;
01246                         }
01247                         const QDateTime& testDt
01248                             = dtGoDown
01249                             ? (   ( newDt < cv.dtLow )
01250                                     ? cv.dtLow
01251                                     : newDt )
01252                             : (   ( newDt > cv.dtHigh )
01253                                     ? cv.dtHigh
01254                                     : newDt );
01255                         /*
01256                            qDebug("    dt: %i %i %i    %i:%i:%i",
01257                            newDt.date().year(),newDt.date().month(),newDt.date().day(),
01258                            newDt.time().hour(),newDt.time().minute(),newDt.time().second());
01259                            qDebug("testDt: %i %i %i    %i:%i:%i",
01260                            testDt.date().year(),testDt.date().month(),testDt.date().day(),
01261                            testDt.time().hour(),testDt.time().minute(),testDt.time().second());
01262                            */
01263                         bool endLoop = (i == nLabels) || (&testDt != &newDt);
01264 
01265                         secPaint = ( KDChartAxisParams::ValueScaleSecond >= cv.dtDeltaScale ) &&
01266                             ( testDt.time().second() != dt.time().second() ||
01267                               ( endLoop && ((pSecX != x1) || (pSecY != y1))));
01268                         minPaint = ( KDChartAxisParams::ValueScaleMinute >= cv.dtDeltaScale ) &&
01269                             ( testDt.time().minute() != dt.time().minute() ||
01270                               ( endLoop && ((pMinX != x1) || (pMinY != y1))));
01271                         hourPaint = ( KDChartAxisParams::ValueScaleHour >= cv.dtDeltaScale ) &&
01272                             ( testDt.time().hour() != dt.time().hour() ||
01273                               ( endLoop && ((pHourX != x1) || (pHourY != y1))));
01274                         dayPaint = ( KDChartAxisParams::ValueScaleDay >= cv.dtDeltaScale ) &&
01275                             ( testDt.date().day() != dt.date().day() ||
01276                               ( endLoop && ((pDayX != x1) || (pDayY != y1))));
01277                         /* khz: currently not used
01278                            weekPaint = ( KDChartAxisParams::ValueScaleWeek >= cv.dtDeltaScale ) &&
01279                            ( testDt.date().week() != dt.date().week() ||
01280                            ( endLoop && ((pWeekX != x1) || (pWeekY != y1))));
01281                            */
01282                         monthPaint = ( KDChartAxisParams::ValueScaleMonth >= cv.dtDeltaScale ) &&
01283                             ( testDt.date().month() != dt.date().month() ||
01284                               ( endLoop && ((pMonthX != x1) || (pMonthY != y1))));
01285                         yearPaint = ( KDChartAxisParams::ValueScaleYear >= cv.dtDeltaScale ) &&
01286                             ( testDt.date().year() != dt.date().year() ||
01287                               ( endLoop && ((pYearX != x1) || (pYearY != y1))));
01288 
01289                         p1X = x1 + iLabel * pXDelta;
01290                         p1Y = y1 + iLabel * pYDelta;
01291                         p2X = p1X + pXDelta;
01292                         p2Y = p1Y + pYDelta;
01293                         pXD = orgXD;
01294                         pYD = orgYD;
01295 
01296                         if( endLoop ){
01297                             ( ( KDChartAxisParams& ) para ).setAxisDtHighPos( p1X, p1Y );
01298                             // adjust stored dt-high settings
01299                             ( ( KDChartAxisParams& ) para ).setTrueAxisDtHigh( dt );
01300                         }
01301                         pen = subGridPen;
01302                         /*
01303                         // old code: just draw the seconds without any tests
01304                         // (not wise to do that when supporting sec1000
01305                         //  and the like some day...)
01306                         if( newDt.time().second() != dt.time().second() ){
01307                         painter->drawLine( QPoint( p1X, p1Y ), QPoint( p1X+pXD, p1Y+pYD ) );
01308                         painter->drawLine( QPoint( p1X+pXD, p1Y+pYD ),
01309                         QPoint( p1X+pXD + pXDelta, p1Y+pYD + pYDelta ) );
01310                         painter->drawText( p1X+pXD-orgXD, p1Y+pYD-orgYD,
01311                         pTextsW, pTextsH,
01312                         textAlign | Qt::DontClip,
01313                         QString::number( dt.time().second() ) );
01314                         pXD += orgXD;
01315                         pYD += orgYD;
01316                         }
01317                         */
01318                         if( secPaint ){
01319                             painter->drawLine( QPoint( static_cast<int>( pSecX+pXD ),
01320                                                        static_cast<int>( pSecY+pYD ) ),
01321                                                QPoint( static_cast<int>( p2X + pXD ),
01322                                                        static_cast<int>( p2Y + pYD ) ) );
01323                             if( (pXDelta/2.0 < p2X - pSecX) || (pYDelta/2.0 < p2Y - pSecY) ){
01324                                 QPen oldPen( painter->pen() );
01325                                 painter->setPen( QPen( labelsColor ) );
01326                                 painter->drawText( static_cast<int>( pSecX+pXD-orgXD ),
01327                                                    static_cast<int>( pSecY+pYD-orgYD ),
01328                                                    static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pSecX))),
01329                                                    static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pSecY))),
01330                                         cv.textAlign | Qt::DontClip,
01331                                         QString::number( dt.time().second() ) );
01332                                 painter->setPen( oldPen );
01333                                 if ( para.axisShowGrid() ){
01334 
01335                                     saveDrawLine( *painter,
01336                                                   QPoint( static_cast<int>( pSecX ),
01337                                                           static_cast<int>( pSecY ) ),
01338                                                   QPoint( static_cast<int>( pSecX + gridDX ),
01339                                                           static_cast<int>( pSecY + gridDY ) ),
01340                                             pen );
01341                                     pen = gridPen;
01342                                 }
01343                                 if( !minPaint || pMinX != pSecX || pMinY != pSecY ){
01344                                     painter->drawLine( QPoint( static_cast<int>( pSecX ),
01345                                                                static_cast<int>( pSecY ) ),
01346                                                        QPoint( static_cast<int>( pSecX+pXD ),
01347                                                                static_cast<int>( pSecY+pYD ) ) );
01348                                 }
01349                             }
01350                             if( endLoop && !minPaint )
01351                                 painter->drawLine( QPoint( static_cast<int>( p2X ),
01352                                                            static_cast<int>( p2Y ) ),
01353                                                    QPoint( static_cast<int>( p2X+pXD ),
01354                                                            static_cast<int>( p2Y+pYD ) ) );
01355                             pSecX = p1X + pXDelta;
01356                             pSecY = p1Y + pYDelta;
01357                             pXD += orgXD;
01358                             pYD += orgYD;
01359                         }
01360                         if( minPaint ){
01361                             painter->drawLine( QPoint( static_cast<int>( pMinX+pXD ),
01362                                                        static_cast<int>( pMinY+pYD ) ),
01363                                                QPoint( static_cast<int>( p2X + pXD ),
01364                                                        static_cast<int>( p2Y + pYD ) ) );
01365                             if( (pXDelta/2.0 < p2X - pMinX) || (pYDelta/2.0 < p2Y - pMinY) ){
01366                                 QPen oldPen( painter->pen() );
01367                                 painter->setPen( QPen( labelsColor ) );
01368                                 painter->drawText( static_cast<int>( pMinX+pXD-orgXD ),
01369                                                    static_cast<int>( pMinY+pYD-orgYD ),
01370                                                    static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pMinX)) ),
01371                                                    static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pMinY)) ),
01372                                                    cv.textAlign | Qt::DontClip,
01373                                                    QString::number( dt.time().minute() ) );
01374                                 painter->setPen( oldPen );
01375                                 if ( para.axisShowGrid() ){
01376                                     if( !secPaint && 10 < pXYDelta  ){
01377                                         saveDrawLine( *painter,
01378                                                       QPoint( static_cast<int>( pMinX+pXDelta/2 ),
01379                                                               static_cast<int>( pMinY+pYDelta/2 ) ),
01380                                                       QPoint( static_cast<int>( pMinX+pXDelta/2 + gridDX ),
01381                                                               static_cast<int>( pMinY+pYDelta/2 + gridDY ) ),
01382                                                       subSubGridPen );
01383                                     }
01384                                     saveDrawLine( *painter,
01385                                                   QPoint( static_cast<int>( pMinX ),
01386                                                           static_cast<int>( pMinY ) ),
01387                                                   QPoint( static_cast<int>( pMinX + gridDX ),
01388                                                           static_cast<int>( pMinY + gridDY ) ),
01389                                                   pen );
01390                                     pen = gridPen;
01391                                 }
01392                                 if( !hourPaint || pHourX != pMinX || pHourY != pMinY ){
01393                                     painter->drawLine( QPoint( static_cast<int>( pMinX ),
01394                                                                static_cast<int>( pMinY ) ),
01395                                                        QPoint( static_cast<int>( pMinX+pXD ),
01396                                                                static_cast<int>( pMinY+pYD ) ) );
01397                                 }
01398                             }
01399                             if( endLoop && !hourPaint )
01400                                 painter->drawLine( QPoint( static_cast<int>( p2X ),
01401                                                            static_cast<int>( p2Y ) ),
01402                                                    QPoint( static_cast<int>( p2X+pXD ),
01403                                                            static_cast<int>( p2Y+pYD ) ) );
01404                             pMinX = p1X + pXDelta;
01405                             pMinY = p1Y + pYDelta;
01406                             pXD += orgXD;
01407                             pYD += orgYD;
01408                         }
01409                         if( hourPaint ){
01410                             painter->drawLine( QPoint( static_cast<int>( pHourX+pXD ),
01411                                                        static_cast<int>( pHourY+pYD ) ),
01412                                                QPoint( static_cast<int>( p2X + pXD ),
01413                                                        static_cast<int>( p2Y + pYD ) ) );
01414                             /*
01415                                qDebug("line");
01416                                qDebug("pXDelta / 2.0 : %f", pXDelta/2.0);
01417                                qDebug("p2X - pHourX  : %f", p2X - pHourX);
01418                                */
01419                             if( (pXDelta/2.0 < p2X - pHourX) || (pYDelta/2.0 < p2Y - pHourY) ){
01420                                 /*
01421                                    qDebug("pHourX              %f", pHourX          );
01422                                    qDebug("      +pXD          %i",        pXD      );
01423                                    qDebug("          -orgXD    %i",            orgXD);
01424                                    qDebug("pHourY              %f", pHourY          );
01425                                    qDebug("      +pYD          %i",        pYD      );
01426                                    qDebug("          -orgYD    %i",            orgYD);
01427                                    */
01428                                 QPen oldPen( painter->pen() );
01429                                 painter->setPen( QPen( labelsColor ) );
01430                                 painter->drawText( static_cast<int>( pHourX+pXD-orgXD ),
01431                                                    static_cast<int>( pHourY+pYD-orgYD ),
01432                                                    static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pHourX))),
01433                                                    static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pHourY))),
01434                                                    cv.textAlign | Qt::DontClip,
01435                                                    QString::number( dt.time().hour() ) );
01436                                 painter->setPen( oldPen );
01437                                 if ( para.axisShowGrid() ){
01438                                     if( !minPaint && 10 < pXYDelta  ){
01439                                         saveDrawLine( *painter,
01440                                                       QPoint( static_cast<int>( pHourX+pXDelta/2 ),
01441                                                               static_cast<int>( pHourY+pYDelta/2 ) ),
01442                                                       QPoint( static_cast<int>( pHourX+pXDelta/2 + gridDX ),
01443                                                               static_cast<int>( pHourY+pYDelta/2 + gridDY ) ),
01444                                                       subSubGridPen );
01445                                     }
01446                                     saveDrawLine( *painter,
01447                                                   QPoint( static_cast<int>( pHourX ),
01448                                                           static_cast<int>( pHourY ) ),
01449                                                   QPoint( static_cast<int>( pHourX + gridDX ),
01450                                                           static_cast<int>( pHourY + gridDY ) ),
01451                                                   pen );
01452                                     pen = gridPen;
01453                                 }
01454                                 if( !dayPaint || pDayX != pHourX || pDayY != pHourY ){
01455                                     painter->drawLine( QPoint( static_cast<int>( pHourX ),
01456                                                                static_cast<int>( pHourY ) ),
01457                                                        QPoint( static_cast<int>( pHourX+pXD ),
01458                                                                static_cast<int>( pHourY+pYD ) ) );
01459                                 }
01460                             }
01461                             if( endLoop && !dayPaint )
01462                                 painter->drawLine( QPoint( static_cast<int>( p2X ),
01463                                                            static_cast<int>( p2Y ) ),
01464                                                    QPoint( static_cast<int>( p2X+pXD ),
01465                                                            static_cast<int>( p2Y+pYD ) ) );
01466                             pHourX = p1X + pXDelta;
01467                             pHourY = p1Y + pYDelta;
01468                             pXD += orgXD;
01469                             pYD += orgYD;
01470                         }
01471                         if( dayPaint ){
01472                             painter->drawLine( QPoint( static_cast<int>( pDayX+pXD ),
01473                                                        static_cast<int>( pDayY+pYD ) ),
01474                                                QPoint( static_cast<int>( p2X + pXD ),
01475                                                        static_cast<int>( p2Y + pYD ) ) );
01476                             if( (pXDelta/2.0 < p2X - pDayX) || (pYDelta/2.0 < p2Y - pDayY) ){
01477                                 QPen oldPen( painter->pen() );
01478                                 painter->setPen( QPen( labelsColor ) );
01479                                 painter->drawText( static_cast<int>( pDayX+pXD-orgXD ),
01480                                                    static_cast<int>( pDayY+pYD-orgYD ),
01481                                                    static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pDayX)) ),
01482                                                    static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pDayY)) ),
01483                                         cv.textAlign | Qt::DontClip,
01484                                         QString::number( dt.date().day() ) );
01485                                 painter->setPen( oldPen );
01486                                 /* khz: currently not used
01487                                    if( !weekPaint || pWeekX != pDayX || pWeekY != pDayY )
01488                                    */
01489                                 if ( para.axisShowGrid() ){
01490                                     if( !hourPaint && 10 < pXYDelta  ){
01491                                         saveDrawLine( *painter,
01492                                                       QPoint( static_cast<int>( pDayX+pXDelta/2 ),
01493                                                               static_cast<int>( pDayY+pYDelta/2 ) ),
01494                                                       QPoint( static_cast<int>( pDayX+pXDelta/2 + gridDX ),
01495                                                               static_cast<int>( pDayY+pYDelta/2 + gridDY ) ),
01496                                                 subSubGridPen );
01497                                     }
01498                                     saveDrawLine( *painter,
01499                                                   QPoint( static_cast<int>( pDayX ),
01500                                                           static_cast<int>( pDayY ) ),
01501                                                   QPoint( static_cast<int>( pDayX + gridDX ),
01502                                                           static_cast<int>( pDayY + gridDY ) ),
01503                                             pen );
01504                                     pen = gridPen;
01505                                 }
01506                                 if( !monthPaint || pMonthX != pDayX || pMonthY != pDayY ){
01507                                     painter->drawLine( QPoint( static_cast<int>( pDayX ),
01508                                                                static_cast<int>( pDayY ) ),
01509                                                        QPoint( static_cast<int>( pDayX+pXD ),
01510                                                                static_cast<int>( pDayY+pYD ) ) );
01511                                 }
01512                             }
01513                             /* khz: currently not used
01514                                if( endLoop && !weekPaint )
01515                                */
01516                             if( endLoop && !monthPaint )
01517                                 painter->drawLine( QPoint( static_cast<int>( p2X ),
01518                                                            static_cast<int>( p2Y ) ),
01519                                                    QPoint( static_cast<int>( p2X+pXD ),
01520                                                            static_cast<int>( p2Y+pYD ) ) );
01521                             pDayX = p1X + pXDelta;
01522                             pDayY = p1Y + pYDelta;
01523                             pXD += orgXD;
01524                             pYD += orgYD;
01525                         }
01526                         /* khz: currently unused
01527                            if( weekPaint ){
01528                            painter->drawLine( QPoint( pWeekX+pXD, pWeekY+pYD ),
01529                             QPoint( p2X + pXD, p2Y + pYD ) );
01530                            if( (pXDelta/2.0 < p2X - pWeekX) || (pYDelta/2.0 < p2Y - pWeekY) ){
01531                            QPen oldPen( painter->pen() );
01532                            painter->setPen( QPen( labelsColor ) );
01533                            painter->drawText( pWeekX+pXD-orgXD, pWeekY+pYD-orgYD,
01534                            painter->setPen( oldPen );
01535                            fabs((0.0 == pXDelta) ? pTextsW : (p2X - pWeekX)),
01536                            fabs((0.0 == pYDelta) ? pTextsH : (p2Y - pWeekY)),
01537                            textAlign | Qt::DontClip,
01538                            QString::number( dt.date().week() ) );
01539                            if ( para.axisShowGrid() ){
01540                            if( !dayPaint && 40 < pXYDelta  ){
01541                         // draw 7 lines:
01542                         //saveDrawLine( *painter,
01543                         //            QPoint( pWeekX+pXDelta/2,
01544                         //                    pWeekY+pYDelta/2 ),
01545                         //            QPoint( pWeekX+pXDelta/2 + gridDX,
01546                         //                    pWeekY+pYDelta/2 + gridDY ),
01547                         //            subSubGridPen );
01548                         }
01549                         saveDrawLine( *painter,
01550                         QPoint( pWeekX,
01551                         pWeekY ),
01552                         QPoint( pWeekX + gridDX,
01553                         pWeekY + gridDY ),
01554                         pen );
01555                         pen = gridPen;
01556                         }
01557                         if( !monthPaint || pMonthX != pDayX || pMonthY != pDayY ){
01558                         painter->drawLine( QPoint( pWeekX, pWeekY ), QPoint( pWeekX+pXD, pWeekY+pYD ) );
01559                         }
01560                         }
01561                         if( endLoop && !monthPaint )
01562                         painter->drawLine( QPoint( p2X, p2Y ), QPoint( p2X+pXD, p2Y+pYD ) );
01563                         pWeekX = p1X + pXDelta;
01564                         pWeekY = p1Y + pYDelta;
01565                         pXD += orgXD;
01566                         pYD += orgYD;
01567                         }
01568                         */
01569                         if( monthPaint ){
01570                             painter->drawLine( QPoint( static_cast<int>( pMonthX+pXD ),
01571                                                        static_cast<int>( pMonthY+pYD ) ),
01572                                                QPoint( static_cast<int>( p2X + pXD ),
01573                                                        static_cast<int>( p2Y + pYD ) ) );
01574                             if( (pXDelta/2.0 < p2X - pMonthX) || (pYDelta/2.0 < p2Y - pMonthY) ){
01575                                 QPen oldPen( painter->pen() );
01576                                 painter->setPen( QPen( labelsColor ) );
01577                                 painter->drawText( static_cast<int>( pMonthX+pXD-orgXD ),
01578                                                    static_cast<int>( pMonthY+pYD-orgYD ),
01579                                                    static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pMonthX)) ),
01580                                                    static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pMonthY)) ),
01581                                         cv.textAlign | Qt::DontClip,
01582                                         QString::number( dt.date().month() ) );
01583                                 painter->setPen( oldPen );
01584                                 if ( para.axisShowGrid() ){
01585                                     /* khz: currently unused
01586                                        if( !weekPaint &&
01587                                        && 10 < pXYDelta  ){
01588                                        saveDrawLine( *painter,
01589                                        QPoint( pMonthX+pXDelta/2,
01590                                        pMonthY+pYDelta/2 ),
01591                                        QPoint( pMonthX+pXDelta/2 + gridDX,
01592                                        pMonthY+pYDelta/2 + gridDY ),
01593                                        subSubGridPen );
01594                                        }
01595                                        */
01596                                     saveDrawLine( *painter,
01597                                                   QPoint( static_cast<int>( pMonthX ),
01598                                                           static_cast<int>( pMonthY ) ),
01599                                                   QPoint( static_cast<int>( pMonthX + gridDX ),
01600                                                           static_cast<int>( pMonthY + gridDY ) ),
01601                                             pen );
01602                                     pen = gridPen;
01603                                 }
01604                                 if( !yearPaint || pYearX != pMonthX || pYearY != pMonthY ){
01605                                     painter->drawLine( QPoint( static_cast<int>( pMonthX ),
01606                                                                static_cast<int>( pMonthY ) ),
01607                                                        QPoint( static_cast<int>( pMonthX+pXD ),
01608                                                                static_cast<int>( pMonthY+pYD ) ) );
01609                                 }
01610                             }
01611                             if( endLoop && !yearPaint )
01612                                 painter->drawLine( QPoint( static_cast<int>( p2X ),
01613                                                            static_cast<int>( p2Y ) ),
01614                                                    QPoint( static_cast<int>( p2X+pXD ),
01615                                                            static_cast<int>( p2Y+pYD ) ) );
01616                             pMonthX = p1X + pXDelta;
01617                             pMonthY = p1Y + pYDelta;
01618                             pXD += orgXD;
01619                             pYD += orgYD;
01620                         }
01621                         if( yearPaint ){
01622                             painter->drawLine( QPoint( static_cast<int>( pYearX+pXD ),
01623                                                        static_cast<int>( pYearY+pYD  ) ),
01624                                                QPoint( static_cast<int>( p2X + pXD ),
01625                                                        static_cast<int>( p2Y + pYD ) ) );
01626                             if( (pXDelta/2.0 < p2X - pYearX) || (pYDelta/2.0 < p2Y - pYearY) ){
01627                                 QPen oldPen( painter->pen() );
01628                                 painter->setPen( QPen( labelsColor ) );
01629                                 painter->drawText( static_cast<int>( pYearX+pXD-orgXD ),
01630                                                    static_cast<int>( pYearY+pYD-orgYD ),
01631                                                    static_cast<int>( fabs((0.0 == pXDelta) ? cv.pTextsW : (p2X - pYearX)) ),
01632                                                    static_cast<int>( fabs((0.0 == pYDelta) ? cv.pTextsH : (p2Y - pYearY)) ),
01633                                         cv.textAlign | Qt::DontClip,
01634                                         QString::number( dt.date().year() ) );
01635                                 painter->setPen( oldPen );
01636                                 if ( para.axisShowGrid() ){
01637                                     if( !monthPaint && 10 < pXYDelta  ){
01638                                         saveDrawLine( *painter,
01639                                                       QPoint( static_cast<int>( pYearX+pXDelta/2 ),
01640                                                               static_cast<int>( pYearY+pYDelta/2 ) ),
01641                                                       QPoint( static_cast<int>( pYearX+pXDelta/2 + gridDX ),
01642                                                               static_cast<int>( pYearY+pYDelta/2 + gridDY ) ),
01643                                                 subSubGridPen );
01644                                     }
01645                                     saveDrawLine( *painter,
01646                                                   QPoint( static_cast<int>( pYearX ),
01647                                                           static_cast<int>( pYearY ) ),
01648                                                   QPoint( static_cast<int>( pYearX + gridDX ),
01649                                                           static_cast<int>( pYearY + gridDY ) ),
01650                                             pen );
01651                                     pen = gridPen;
01652                                 }
01653                                 painter->drawLine( QPoint( static_cast<int>( pYearX ),
01654                                                            static_cast<int>( pYearY ) ),
01655                                                    QPoint( static_cast<int>( pYearX+pXD ),
01656                                                            static_cast<int>( pYearY+pYD ) ) );
01657                             }
01658                             if( endLoop )
01659                                 painter->drawLine( QPoint( static_cast<int>( p2X ),
01660                                                            static_cast<int>( p2Y ) ),
01661                                                    QPoint( static_cast<int>( p2X+pXD ),
01662                                                            static_cast<int>( p2Y+pYD ) ) );
01663                             pYearX = p1X + pXDelta;
01664                             pYearY = p1Y + pYDelta;
01665                             pXD += orgXD;
01666                             pYD += orgYD;
01667                         }
01668                         if( &testDt != &newDt )
01669                             break;
01670                         dt = newDt;
01671                         iLabel += 1.0;
01672                     }
01673                     if( !commonDtHeader.isEmpty() ){
01674                         QPen oldPen( painter->pen() );
01675                         painter->setPen( QPen( labelsColor ) );
01676                         painter->drawText( static_cast<int>( x1 + pXD ), static_cast<int>( y1 + pYD ),
01677                                            commonDtHeader );
01678                         painter->setPen( oldPen );
01679                     }
01680                 }else{
01681                     int iLeaveOut = nLeaveOut;
01682                     QString label;
01683                     for ( QStringList::Iterator labelIter = labelTexts->begin();
01684                           labelIter != labelTexts->end();
01685                           ++labelIter ) {
01686                         QDateTime dt;
01687                         if( cv.isDateTime ){
01688 #if COMPAT_QT_VERSION >= 0x030000
01689                             dt = QDateTime::fromString( *labelIter,
01690                                     Qt::ISODate );
01691                             label = dt.toString( formatDT );
01692 #else
01693                             dt = dateTimeFromString( *labelIter );
01694                             label = dt.toString();
01695 #endif
01696                         }else{
01697                             label = *labelIter;
01698                         }
01699 
01700                         if( iLeaveOut < nLeaveOut )
01701                             ++iLeaveOut;
01702                         else
01703                             iLeaveOut = 0;
01704                         //Pending Michel: test if the user implicitely wants to get rid
01705                         //of the non fractional values delimiters and grid lines.
01706                         // axisDigitsBehindComma == 0 and the user implicitely
01707                         // setAxisShowFractionalValuesDelimiters() to false
01708                         bool showDelim =  para.axisShowFractionalValuesDelimiters();
01709                         if ( para.axisShowGrid() && !bMultiRowBarChart ) {
01710               if ( !label.isNull() || showDelim ){
01711                             if( !iLeaveOut )
01712                                 // draw the main grid line
01713 
01714                                 saveDrawLine( *painter,
01715                                               QPoint( static_cast<int>( pGAX - pGXMicroAdjust ),
01716                                                       static_cast<int>( pGAY - pGYMicroAdjust ) ),
01717                                               QPoint( static_cast<int>( pGZX - pGXMicroAdjust ),
01718                                                       static_cast<int>( pGZY - pGYMicroAdjust ) ),
01719                                               gridPen );
01720 
01721                             else if( para.axisShowSubDelimiters()  )
01722                                 // draw a thin sub grid line instead of main line
01723                                 saveDrawLine( *painter,
01724                                               QPoint( static_cast<int>( pGAX - pGXMicroAdjust ),
01725                                                       static_cast<int>( pGAY - pGYMicroAdjust ) ),
01726                                               QPoint( static_cast<int>( pGZX - pGXMicroAdjust ),
01727                                                       static_cast<int>( pGZY - pGYMicroAdjust ) ),
01728                                               leaveOutGridPen );
01729               }
01730                         }
01731                         if ( para.axisLabelsVisible() ) {
01732                             if( !iLeaveOut ) {
01733                               /*PENDING Michel: those points should not be redrawn if sub-delimiters are drawn
01734                    *drawing the submarkers
01735                    * make it visible or not
01736                    *In the case we have a null label - axisDigitsBehindComma is implicitely set to 0 -
01737                    *also paint or dont paint the delimiter corresponding to this label - default is paint.
01738                    */
01739                   if ( !label.isNull() || showDelim )
01740                                 painter->drawLine( QPoint( static_cast<int>( p1X ),
01741                                                            static_cast<int>( p1Y ) ),
01742                                                    QPoint( static_cast<int>( p2X ),
01743                                                            static_cast<int>( p2Y ) ) );
01744 
01745                   cv.pLastX = p1X;
01746                   cv.pLastY = p1Y;
01747                   QPen oldPen( painter->pen() );
01748                   painter->setPen( QPen( labelsColor ) );
01749                   if(    para.axisLabelsDontShrinkFont()
01750                      && isHorizontalAxis
01751                      && (Qt::AlignHCenter == (cv.textAlign & Qt::AlignHCenter)) ) {
01752                 double w = fm.width( label ) + 4.0;
01753                 double x0 = cv.pTextsX + cv.pTextsW / 2.0;
01754 
01755                 painter->drawText( static_cast<int>( x0 - w / 2.0 ),
01756                            static_cast<int>( cv.pTextsY ),
01757                            static_cast<int>( w ),
01758                            static_cast<int>( cv.pTextsH ),
01759                            cv.textAlign, label );
01760                   } else {
01761                 if( nRotation ){
01762                   KDDrawText::drawRotatedText(
01763                         painter,
01764                         nRotation,
01765                         painter->worldMatrix().map(
01766                                                 QPoint( static_cast<int>( cv.pTextsX ),
01767                                                         static_cast<int>( cv.pTextsY ) ) ),
01768                                                 label,
01769                                                 0,
01770                                                 cv.textAlign,
01771                                                 false,
01772                                                 &fm,
01773                                                 screenOutput,screenOutput,0,
01774                                                 screenOutput );
01775                                     } else {
01776                       // Pending Michel draw the axis labels
01777                                         painter->drawText( static_cast<int>( cv.pTextsX ),
01778                                                            static_cast<int>( cv.pTextsY ),
01779                                                            static_cast<int>( cv.pTextsW ),
01780                                                            static_cast<int>( cv.pTextsH ),
01781                                                            cv.textAlign | Qt::DontClip,
01782                                                            label );
01783 
01784                                         // debugging text rect
01785                                         /*
01786                                         painter->drawRect(static_cast <int>(cv.pTextsX),
01787                               static_cast <int>(cv.pTextsY),
01788                               static_cast <int> (nUsableAxisWidth),
01789                               static_cast <int> (nUsableAxisHeight));
01790                     */
01791                                     }
01792                                 }
01793                                 painter->setPen( oldPen );
01794                             }
01795                         }
01796 
01797 
01798                         if( cv.isDateTime ){
01799                             if( labelTexts->begin() == labelIter ){
01800                                 ((KDChartAxisParams&)para).setAxisDtLowPos(
01801                                                                            pGAX - pGXMicroAdjust,
01802                                                                            pGAY - pGYMicroAdjust );
01803                                 // adjust stored dt-low settings
01804                                 ( ( KDChartAxisParams& ) para ).setTrueAxisDtLow( dt );
01805                             }else{
01806                                 ((KDChartAxisParams&)para).setAxisDtHighPos(
01807                                                                             pGAX - pGXMicroAdjust,
01808                                                                             pGAY - pGYMicroAdjust );
01809                                 // adjust stored dt-high settings
01810                                 ( ( KDChartAxisParams& ) para ).setTrueAxisDtHigh( dt );
01811                             }
01812                         }
01813 
01814                         iLabel += 1.0;
01815                         p1X = x1 + iLabel * pXDelta;
01816                         p1Y = y1 + iLabel * pYDelta;
01817                         p2X = x2 + iLabel * pXDelta;
01818                         p2Y = y2 + iLabel * pYDelta;
01819                         cv.pTextsX = xT + iLabel * pXDelta;
01820                         cv.pTextsY = yT + iLabel * pYDelta;
01821 
01822                         pGAX = xGA + iLabel * pXDelta;
01823                         pGAY = yGA + iLabel * pYDelta;
01824                         pGZX = xGZ + iLabel * pXDelta;
01825                         pGZY = yGZ + iLabel * pYDelta;
01826                         /*
01827                         pGAX = xGA + iLabel * pXSubDelimDelta / cv.nSubDelimFactor;
01828                         pGAY = yGA + iLabel * pYSubDelimDelta / cv.nSubDelimFactor;
01829                         pGZX = xGZ + iLabel * pXSubDelimDelta / cv.nSubDelimFactor;
01830                         pGZY = yGZ + iLabel * pYSubDelimDelta / cv.nSubDelimFactor;
01831                         */
01832                     }
01833                 }
01834 
01835 
01836                 // adjust zero-line start, if not starting at origin
01837                 if ( cv.bSteadyCalc &&
01838                      ( para.axisValuesDecreasing() ||
01839                        (0.0 != para.trueAxisLow())      ) ) {
01840                     //double x = p1.x();
01841                     double x = 0.0;
01842                      /* we have to find the *real* X axis position,
01843                      this is NOT always the p1.x() as it is the
01844                      case for left2 or right2 axes. [cmw, 12/01/2005] */
01845                      if (cv.basicPos==KDChartAxisParams::AxisPosRight)
01846                          x = static_cast<double>(_dataRect.right());
01847                      else
01848                          x = static_cast<double>(_dataRect.left());
01849                     double y = p1.y();
01850                     double mult = para.trueAxisLow() / para.trueAxisDelta();
01851                     x -= mult * pXDelta;
01852                     y -= mult * pYDelta;
01853                     axisZeroLineStartX = x;
01854                     axisZeroLineStartY = y;
01855                     //qDebug( "axisZeroLineStartX %f,  axisZeroLineStartY %f",
01856                     //        axisZeroLineStartX, axisZeroLineStartY );
01857                 }
01858 
01859                 painter->setClipping( oldClippingFlag );
01860             } // if( nLabels )
01861 
01862             // draw zero-line (Ok, this might be overwritten by axes
01863             //  cause those are drawn after all labels and grid and
01864             //  zero-line(s) has been painted, see code below, starting
01865             // with "// draw all the axes".
01866             if ( cv.bSteadyCalc && !cv.isDateTime ) {
01867                 ( ( KDChartAxisParams& ) para ).setAxisZeroLineStart( axisZeroLineStartX, axisZeroLineStartY );
01868                 double axisZeroLineStart;
01869                 int minCoord, maxCoord;
01870                 double xFactor, yFactor;
01871                 switch( cv.basicPos ){
01872                     case KDChartAxisParams::AxisPosLeft:
01873                         xFactor =  1.0;
01874                         yFactor =  0.0;
01875                         axisZeroLineStart = axisZeroLineStartY;
01876                         minCoord = QMIN( cv.orig.y(), cv.dest.y() );
01877                         maxCoord = QMAX( cv.orig.y(), cv.dest.y() );
01878 
01879                         break;
01880                     case KDChartAxisParams::AxisPosRight:
01881                         xFactor = -1.0;
01882                         yFactor =  0.0;
01883                         axisZeroLineStart = axisZeroLineStartY;
01884                         minCoord = QMIN( cv.orig.y(), cv.dest.y() );
01885                         maxCoord = QMAX( cv.orig.y(), cv.dest.y() );
01886                         break;
01887                     case KDChartAxisParams::AxisPosTop:
01888                         xFactor =  0.0;
01889                         yFactor =  1.0;
01890                         axisZeroLineStart = axisZeroLineStartX;
01891                         minCoord = QMIN( cv.orig.x(), cv.dest.x() );
01892                         maxCoord = QMAX( cv.orig.x(), cv.dest.x() );
01893                         break;
01894                     case KDChartAxisParams::AxisPosBottom:
01895                         xFactor =  0.0;
01896                         yFactor = -1.0;
01897                         axisZeroLineStart = axisZeroLineStartX;
01898                         minCoord = QMIN( cv.orig.x(), cv.dest.x() );
01899                         maxCoord = QMAX( cv.orig.x(), cv.dest.x() );
01900                         break;
01901                     default:
01902                         xFactor =  0.0;
01903                         yFactor =  0.0;
01904                         axisZeroLineStart = 0.0;
01905                         minCoord = 0;
01906                         maxCoord = 0;
01907                 }
01908                 if( axisZeroLineStart >= minCoord &&
01909                         axisZeroLineStart <= maxCoord ){
01910                     QPoint pZ0( static_cast<int>( para.axisZeroLineStartX() ),
01911                                 static_cast<int>( para.axisZeroLineStartY() ) );
01912                     QPoint pZ(  static_cast<int>( para.axisZeroLineStartX()
01913                                 + xFactor * _dataRect.width() ),
01914                                 static_cast<int>( para.axisZeroLineStartY()
01915                                 + yFactor * _dataRect.height() ) );
01916                     //qDebug("------");
01917                     saveDrawLine( *painter,
01918                             pZ0,
01919                             pZ,
01920                             QPen( para.axisZeroLineColor(),
01921                                 lineWidth ) );
01922                 }
01923             }
01924 
01925         }
01926 
01927     }
01928 
01929     // Drawing all the axes lines:
01930 /*
01931     // 1. test if the standard axes are share one or several corner points
01932     //    if yes, we first draw a polyline using a "Qt::MiterJoin" PenJoinStyle
01933     //    to make sure the corners are filled
01934     internal__KDChart__CalcValues& cv1 = calcVal[ KDChartAxisParams::AxisPosLeft   ];
01935     internal__KDChart__CalcValues& cv2 = calcVal[ KDChartAxisParams::AxisPosBottom ];
01936     const KDChartAxisParams& pa1 = params()->axisParams( KDChartAxisParams::AxisPosLeft );
01937     const KDChartAxisParams& pa2 = params()->axisParams( KDChartAxisParams::AxisPosBottom );
01938 qDebug("\n\nx1: %i   y1: %i   w1: %i\nx2: %i   y2: %i   w2: %i",
01939 cv1.orig.x(), cv1.orig.y(), pa1.axisTrueLineWidth(),
01940 cv2.orig.x(), cv2.orig.y(), pa2.axisTrueLineWidth() );
01941     if( cv1.orig == cv2.orig ){
01942         const QColor c1( pa1.axisLineColor() );
01943         const QColor c2( pa2.axisLineColor() );
01944         const QPoint pA( cv1.dest );
01945         const QPoint pB( cv1.orig );
01946         const QPoint pC( cv2.dest );
01947         QPen pen( QColor( (c1.red()   + c2.red())  /2,
01948                           (c1.green() + c2.green())/2,
01949                           (c1.blue()  + c2.blue()) /2 ),
01950                   QMIN(pa1.axisTrueLineWidth(), pa2.axisTrueLineWidth()) );
01951         pen.setJoinStyle( Qt::MiterJoin );
01952         painter->setPen( pen );
01953         QPointArray a;
01954         a.putPoints( 0, 3, pA.x(),pA.y(), pB.x(),pB.y(), pC.x(),pC.y() );
01955         painter->drawPolyline( a );
01956 qDebug("done\n" );
01957     }
01958 */
01959     // 2. draw the axes using their normal color
01960     for( iAxis = 0; iAxis < KDCHART_MAX_AXES; ++iAxis ){
01961         internal__KDChart__CalcValues& cv = calcVal[iAxis];
01962         const KDChartAxisParams & para = params()->axisParams( iAxis );
01963         if( cv.processThisAxis && para.axisLineVisible() ){
01964             painter->setPen( QPen( para.axisLineColor(),
01965                              para.axisTrueLineWidth() ) );
01966             int x =         cv.dest.x();
01967             if( 2.0 >= QABS(cv.pLastX - x) )
01968                 x = static_cast < int > ( cv.pLastX );
01969             int y =         cv.dest.y();
01970             if( 2.0 >= QABS(cv.pLastY - y) )
01971                 y = static_cast < int > ( cv.pLastY );
01972             painter->drawLine( cv.orig, QPoint(x,y) );
01973         }
01974     }
01975 
01976     painter->restore();
01977 }
01978 
01979 
01980 double fastPow10( int x )
01981 {
01982     double res = 1.0;
01983     if( 0 <= x ){
01984         for( int i = 1; i <= x; ++i )
01985             res *= 10.0;
01986     }else{
01987         for( int i = -1; i >= x; --i )
01988             res /= 10.0;
01989     }
01990     return res;
01991 }
01992 double fastPow10( double x )
01993 {
01994     return pow(10.0, x);
01995 }
01996 
01997 
02017 /**** static ****/
02018 void KDChartAxesPainter::calculateLabelTexts(
02019         QPainter* painter,
02020         const KDChartTableDataBase& data,
02021         const KDChartParams& params,
02022         uint axisNumber,
02023         double averageValueP1000,
02024         double delimLen,
02025         // start of return parameters
02026         KDChartAxisParams::AxisPos& basicPos,
02027         QPoint& orig,
02028         QPoint& dest,
02029         double& pXDeltaFactor,
02030         double& pYDeltaFactor,
02031         double& pXDelimDeltaFaktor,
02032         double& pYDelimDeltaFaktor,
02033         double& nSubDelimFactor,
02034         double& pDelimDelta,
02035         double& nTxtHeight,
02036         double& pTextsX,
02037         double& pTextsY,
02038         double& pTextsW,
02039         double& pTextsH,
02040         int& textAlign,
02041         bool& isLogarithmic,
02042         bool& isDateTime,
02043         bool& autoDtLabels,
02044         QDateTime& dtLow,
02045         QDateTime& dtHigh,
02046         KDChartAxisParams::ValueScale& dtDeltaScale,
02047         bool adjustTheValues,
02048         double trueDelta,
02049         double trueDeltaPix )
02050 {
02051 //qDebug("\nentering KDChartAxesPainter::calculateLabelTexts() :   nTxtHeight: "+QString::number(nTxtHeight));
02052     const KDChartAxisParams & para = params.axisParams( axisNumber );
02053 
02054     // store whether the labels are to be drawn in reverted order
02055     const bool bDecreasing = para.axisValuesDecreasing();
02056 
02057     basicPos = KDChartAxisParams::basicAxisPos( axisNumber );
02058 
02059     pXDeltaFactor = 0.0;
02060     pYDeltaFactor = 0.0;
02061     pXDelimDeltaFaktor = 0.0;
02062     pYDelimDeltaFaktor = 0.0;
02063     int axisLength;
02064     switch ( basicPos ) {
02065         case KDChartAxisParams::AxisPosBottom: {
02066             axisLength = para.axisTrueAreaRect().width();
02067             orig = bDecreasing
02068                  ? para.axisTrueAreaRect().topRight()
02069                  : para.axisTrueAreaRect().topLeft();
02070             dest = bDecreasing
02071                  ? para.axisTrueAreaRect().topLeft()
02072                  : para.axisTrueAreaRect().topRight();
02073             pYDelimDeltaFaktor = 1.0;
02074             pXDeltaFactor      = bDecreasing ? -1.0 : 1.0;
02075             //qDebug("\nsetting pXDeltaFactor for axis %x", axisNumber);
02076             //qDebug(bDecreasing ? "bDecreasing =  TRUE" : "bDecreasing = FALSE");
02077             //qDebug("pXDeltaFactor = %f\n",pXDeltaFactor);
02078         }
02079         break;
02080         case KDChartAxisParams::AxisPosLeft: {
02081             axisLength = para.axisTrueAreaRect().height();
02082             orig = bDecreasing
02083                  ? para.axisTrueAreaRect().topRight()
02084                  : para.axisTrueAreaRect().bottomRight();
02085             dest = bDecreasing
02086                  ? para.axisTrueAreaRect().bottomRight()
02087                  : para.axisTrueAreaRect().topRight();
02088             pXDelimDeltaFaktor = -1.0;
02089             pYDeltaFactor      = bDecreasing ? 1.0 : -1.0;
02090         }
02091         break;
02092         case KDChartAxisParams::AxisPosTop: {
02093             axisLength = para.axisTrueAreaRect().width();
02094             orig = bDecreasing
02095                  ? para.axisTrueAreaRect().bottomRight()
02096                  : para.axisTrueAreaRect().bottomLeft();
02097             dest = bDecreasing
02098                  ? para.axisTrueAreaRect().bottomLeft()
02099                  : para.axisTrueAreaRect().bottomRight();
02100             pYDelimDeltaFaktor = -1.0;
02101             pXDeltaFactor      =  bDecreasing ? -1.0 : 1.0;
02102         }
02103         break;
02104         case KDChartAxisParams::AxisPosRight: {
02105             axisLength = para.axisTrueAreaRect().height();
02106             orig = bDecreasing
02107                  ? para.axisTrueAreaRect().topLeft()
02108                  : para.axisTrueAreaRect().bottomLeft();
02109             dest = bDecreasing
02110                  ? para.axisTrueAreaRect().bottomLeft()
02111                  : para.axisTrueAreaRect().topLeft();
02112             pXDelimDeltaFaktor = 1.0;
02113             pYDeltaFactor      = bDecreasing ? 1.0 : -1.0;
02114         }
02115         break;
02116         default: {
02117             axisLength = 0;
02118             qDebug( "IMPLEMENTATION ERROR: KDChartAxesPainter::paintAxes() unhandled enum value." );
02119         }
02120         break;
02121     }
02122 
02123     // which dataset(s) is/are represented by this axis?
02124     uint dataset, dataset2, chart;
02125     if ( !params.axisDatasets( axisNumber, dataset, dataset2, chart ) ) {
02126         dataset = KDCHART_ALL_DATASETS;
02127         dataset2 = KDCHART_ALL_DATASETS;
02128         chart = 0;
02129         //qDebug("\nautomatic set values:   chart: %u,\ndataset: %u,  dataset2: %u",
02130         //chart, dataset, dataset2);
02131     }
02132     // which dataset(s) with mode DataEntry (or mode ExtraLinesAnchor, resp.)
02133     // is/are represented by this axis?
02134     uint dataDataset, dataDataset2;
02135     if( params.findDatasets( KDChartParams::DataEntry,
02136                              KDChartParams::ExtraLinesAnchor,
02137                              dataDataset,
02138                              dataDataset2,
02139                              chart ) ) {
02140         // adjust dataDataset in case MORE THAN ONE AXIS
02141         //                    is representing THIS CHART
02142         if(    (    KDCHART_ALL_DATASETS != dataset
02143                  && KDCHART_NO_DATASET   != dataset )
02144             || (    KDCHART_ALL_DATASETS != dataDataset
02145                  && KDCHART_NO_DATASET   != dataDataset ) ){
02146             int ds = (KDCHART_ALL_DATASETS != dataset)
02147                    ? dataset
02148                    : 0;
02149             int dds = (KDCHART_ALL_DATASETS != dataDataset)
02150                     ? dataDataset
02151                     : 0;
02152             dataDataset  = QMAX( ds, dds );
02153         }
02154         if(    (    KDCHART_ALL_DATASETS != dataset2
02155                  && KDCHART_NO_DATASET   != dataset2 )
02156             || (    KDCHART_ALL_DATASETS != dataDataset2
02157                  && KDCHART_NO_DATASET   != dataDataset2 ) ){
02158             int ds2 = (KDCHART_ALL_DATASETS != dataset2)
02159                     ? dataset2
02160                     : KDCHART_MAX_AXES-1;
02161             int dds2 = (KDCHART_ALL_DATASETS != dataDataset2)
02162                      ? dataDataset2
02163                      : KDCHART_MAX_AXES-1;
02164             dataDataset2  = QMIN( ds2, dds2 );
02165         }
02166     }
02167     else {
02168         // Should not happen
02169         qDebug( "IMPLEMENTATION ERROR: findDatasets( DataEntry, ExtraLinesAnchor ) should *always* return true. (b)" );
02170         dataDataset = KDCHART_ALL_DATASETS;
02171     }
02172     //qDebug("\naxisNumber: %x\nchart: %x\ndataset: %x,  dataset2: %x,\ndataDataset: %x,  dataDataset2: %x",
02173     //axisNumber, chart, dataset, dataset2, dataDataset, dataDataset2);
02174 
02175     if ( para.axisLabelsFontUseRelSize() ){
02176         nTxtHeight = para.axisLabelsFontRelSize()
02177             * averageValueP1000;
02178 //qDebug("using rel. size in KDChartAxesPainter::calculateLabelTexts() :   nTxtHeight: "+QString::number(nTxtHeight));
02179     }else {
02180         QFontInfo info( para.axisLabelsFont() );
02181         nTxtHeight = info.pointSize();
02182 //qDebug("using FIXED size in KDChartAxesPainter::calculateLabelTexts() :   nTxtHeight: "+QString::number(nTxtHeight));
02183     }
02184 
02185     const KDChartEnums::NumberNotation notation = para.axisLabelsNotation();
02186     const int     behindComma    = para.axisDigitsBehindComma();
02187     const int     divPow10       = para.axisLabelsDivPow10();
02188     const QString decimalPoint   = para.axisLabelsDecimalPoint();
02189     const QString thousandsPoint = para.axisLabelsThousandsPoint();
02190     const QString prefix         = para.axisLabelsPrefix();
02191     const QString postfix        = para.axisLabelsPostfix();
02192     const int     totalLen       = para.axisLabelsTotalLen();
02193     const QChar   padFill        = para.axisLabelsPadFill();
02194     const bool    blockAlign     = para.axisLabelsBlockAlign();
02195 
02196     QStringList labelTexts;
02197     int colNum = para.labelTextsDataRow();
02198     bool bDone = true;
02199     switch ( para.axisLabelTextsFormDataRow() ) {
02200         case KDChartAxisParams::LabelsFromDataRowYes: {
02201             // Take whatever is in the specified column (even if not a string)
02202             int trueBehindComma = -1;
02203             QVariant value;
02204             for ( uint iDataset = 0; iDataset < data.usedRows(); iDataset++ ) {
02205                 if( data.cellCoord( iDataset, colNum, value, 1 ) ){
02206                     if( QVariant::String == value.type() )
02207                         labelTexts.append( value.toString() );
02208                     else {
02209                         labelTexts.append( applyLabelsFormat( value.toDouble(),
02210                                                               divPow10,
02211                                                               behindComma,
02212                                                               para.axisValueDelta(),
02213                                                               trueBehindComma,
02214                                                               notation,
02215                                                               decimalPoint,
02216                                                               thousandsPoint,
02217                                                               prefix,
02218                                                               postfix,
02219                                                               totalLen,
02220                                                               padFill,
02221                                                               blockAlign ) );
02222 
02223             }
02224                 }
02225             }
02226             break;
02227         }
02228         case KDChartAxisParams::LabelsFromDataRowGuess: {
02229             QVariant value;
02230             for ( uint iDataset = 0; iDataset < data.usedRows(); iDataset++ ) {
02231                 if( data.cellCoord( iDataset, colNum, value, 1 ) ){
02232                     if( QVariant::String == value.type() ){
02233                         const QString sVal( value.toString() );
02234                         if( !sVal.isEmpty() && !sVal.isNull() )
02235                             labelTexts.append( sVal );
02236                     }
02237                 }else{
02238                     labelTexts.clear();
02239                     bDone = false;
02240                     break;
02241                 }
02242             }
02243             break;
02244         }
02245         case KDChartAxisParams::LabelsFromDataRowNo: {
02246             bDone = false;
02247             break;
02248         }
02249         default:
02250             // Should not happen
02251             qDebug( "KDChart: Unknown label texts source" );
02252     }
02253 
02254     // if necessary adjust text params *including* the steady value calc setting
02255     const bool dataCellsHaveSeveralCoordinates =
02256         (KDCHART_ALL_DATASETS == dataDataset)
02257         ? data.cellsHaveSeveralCoordinates()
02258         : data.cellsHaveSeveralCoordinates( dataDataset, dataDataset2 );
02259     if( dataCellsHaveSeveralCoordinates && !para.axisSteadyValueCalc() )
02260         ((KDChartParams&)params).setAxisLabelTextParams(
02261             axisNumber,
02262             true,
02263             KDCHART_AXIS_LABELS_AUTO_LIMIT,
02264             KDCHART_AXIS_LABELS_AUTO_LIMIT,
02265             KDCHART_AXIS_LABELS_AUTO_DELTA,
02266             para.axisLabelsDigitsBehindComma() );// NOTE: This sets MANY other params to default values too!
02267 
02268 
02269     const KDChartParams::ChartType params_chartType
02270         = ( 0 == chart )
02271         ? params.chartType()
02272         : params.additionalChartType();
02273 
02274 
02275     // store whether we are calculating Ordinate-like axis values
02276     const bool bSteadyCalc = para.axisSteadyValueCalc();
02277 
02278     // store whether logarithmic calculation is wanted
02279     isLogarithmic = bSteadyCalc &&
02280             (KDChartParams::Line == params_chartType) &&
02281             (KDChartAxisParams::AxisCalcLogarithmic == para.axisCalcMode());
02282 
02283     //qDebug(bSteadyCalc ? "bSteadyCalc":"NOT bSteadyCalc");
02284     //qDebug(isLogarithmic? "isLogarithmic":"NOT isLogarithmic");
02285 
02286     // store whether this is a vertical axis or a horizontal axis
02287     const bool bVertAxis = (KDChartAxisParams::AxisPosLeft  == basicPos) ||
02288                            (KDChartAxisParams::AxisPosRight == basicPos);
02289 
02290     // store the coordinate number to be used for this axis
02291     const int coordinate = bVertAxis ? 1 : 2;
02292 
02293     // store whether our coordinates are double or QDateTime values
02294     const QVariant::Type valueType =
02295         (KDCHART_ALL_DATASETS == dataDataset)
02296         ? data.cellsValueType(                            coordinate )
02297         : data.cellsValueType( dataDataset, dataDataset2, coordinate );
02298     isDateTime = valueType == QVariant::DateTime;
02299     bool bIsDouble = valueType == QVariant::Double;
02300 
02301     autoDtLabels = isDateTime && ( KDCHART_AXIS_LABELS_AUTO_DATETIME_FORMAT
02302             == para.axisLabelsDateTimeFormat() );
02303 
02304     if( autoDtLabels || bSteadyCalc )
02305         ( ( KDChartAxisParams& ) para ).setAxisLabelsTouchEdges( true );
02306 
02307     bool bStatistical = KDChartParams::HiLo       == params_chartType
02308                      || KDChartParams::BoxWhisker == params_chartType;
02309 
02310     if ( !bVertAxis && KDChartParams::BoxWhisker == params_chartType
02311                     && ! para.axisLabelStringCount() ) {
02312         uint ds1 = (KDCHART_ALL_DATASETS == dataDataset)
02313             ? 0
02314             : dataDataset;
02315         uint ds2 = (KDCHART_ALL_DATASETS == dataDataset)
02316             ? data.usedRows() - 1
02317             : dataDataset2;
02318         for (uint i = ds1; i <= ds2; ++i)
02319             labelTexts.append(
02320                     QObject::tr( "Series " ) + QString::number( i + 1 ) );
02321         bDone = true;
02322     }
02323 
02324     double nLow   =  1.0 + bSteadyCalc;// ? 0.0 : data.colsScrolledBy();
02325     double nHigh  = 10.0;
02326     double nDelta =  1.0;
02327     if ( !bDone ) {
02328         bDone = true;
02329 
02330         // look if exact label specification was made via limits and delta
02331         if (       ! isLogarithmic
02332                 && ! para.axisLabelStringCount()
02333                 && ! ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueStart() )
02334                 && ! ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() )
02335                 && ! ( para.axisValueStart() == para.axisValueEnd() )
02336                 && ! ( KDCHART_AXIS_LABELS_AUTO_DELTA == para.axisValueDelta() )
02337                 && ! ( 0.0 == para.axisValueDelta() ) ) {
02338             nLow   = para.axisValueStart().toDouble();
02339             nHigh  = para.axisValueEnd().toDouble();
02340             nDelta = para.axisValueDelta();
02341             int behindComma = para.axisDigitsBehindComma();
02342             int trueBehindComma = -1;
02343             bool upwards = (nLow < nHigh);
02344             if( upwards != (0.0 < nDelta) )
02345                 nDelta *= -1.0;
02346             double nVal = nLow;
02347             bDone = false;
02348             bool bShowVeryLastLabel = false;
02349             //qDebug("\n nLow: %f,  nHigh: %f,  nDelta: %f", nLow, nHigh, nDelta );
02350             while( bShowVeryLastLabel || (upwards ? (nVal <= nHigh) : (nVal >= nHigh)) ){
02351                 //qDebug("nVal    : %f", nVal );
02352                 labelTexts.append( applyLabelsFormat( nVal,
02353                                                       divPow10,
02354                                                       behindComma,
02355                                                       nDelta,
02356                                                       trueBehindComma,
02357                                                       notation,
02358                                                       decimalPoint,
02359                                                       thousandsPoint,
02360                                                       prefix,
02361                                                       postfix,
02362                                                       totalLen,
02363                                                       padFill,
02364                                                       blockAlign ) );
02365                 nVal += nDelta;
02366                 //qDebug("nVal-neu: %f", nVal );
02367                 if( ! (upwards ? (nVal <= nHigh) : (nVal >= nHigh)) ){
02368                     // work around a rounding error: show the last label, even if not nVal == nHigh is not matching exactly
02369                     if( bShowVeryLastLabel )
02370                         bShowVeryLastLabel = false;
02371                     else{
02372                         QString sHigh(  applyLabelsFormat( nHigh,
02373                                                            divPow10,
02374                                                            behindComma,
02375                                                            nDelta,
02376                                                            trueBehindComma,
02377                                                            notation,
02378                                                            decimalPoint,
02379                                                            thousandsPoint,
02380                                                            prefix,
02381                                                            postfix,
02382                                                            totalLen,
02383                                                            padFill,
02384                                                            blockAlign ) );
02385                         QString sValue( applyLabelsFormat( nVal,
02386                                                            divPow10,
02387                                                            behindComma,
02388                                                            nDelta,
02389                                                            trueBehindComma,
02390                                                            notation,
02391                                                            decimalPoint,
02392                                                            thousandsPoint,
02393                                                            prefix,
02394                                                            postfix,
02395                                                            totalLen,
02396                                                            padFill,
02397                                                            blockAlign ) );
02398                         bShowVeryLastLabel = (sValue == sHigh);
02399                         //qDebug("test:                 sHigh: "+sHigh+"   sValue: "+sValue );
02400                     }
02401                 }
02402                 bDone = true;
02403             }
02404             ( ( KDChartAxisParams& ) para ).setTrueAxisLowHighDelta( nLow, nHigh, nDelta );
02405             //qDebug("\n[Z-0] nLow: %f,  nHigh: %f,  nDelta: %f", nLow, nHigh, nDelta );
02406         }         // look if a string list was specified
02407         else if ( para.axisLabelStringCount() ) {
02408             int nLabels = bSteadyCalc
02409                 ? para.axisLabelStringCount()
02410                 : bStatistical ? data.usedRows() : data.usedCols();
02411             calculateBasicTextFactors( nTxtHeight, para, averageValueP1000,
02412                     basicPos, orig, delimLen, nLabels,
02413                     // start of return parameters
02414                     pDelimDelta,
02415                     pTextsX, pTextsY, pTextsW, pTextsH,
02416                     textAlign );
02417             bool useShortLabels = false;
02418             QStringList tmpList( para.axisLabelStringList() );
02419 
02420             // find start- and/or end-entry
02421             int iStart = 0;
02422             int iEnd = para.axisLabelStringCount() - 1;
02423             if(    ! ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueStart() )
02424                 || ! ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() ) ) {
02425                 const bool testStart = !( QVariant::String == para.axisValueStart().type() );
02426                 const bool testEnd   = !( QVariant::String == para.axisValueEnd().type() );
02427                 QString sStart = testStart
02428                     ? para.axisValueStart().toString().upper()
02429                     : QString::null;
02430                 QString sEnd = testEnd
02431                     ? para.axisValueEnd().toString().upper()
02432                     : QString::null;
02433 
02434                 uint i = 0;
02435                 for ( QStringList::Iterator it = tmpList.begin();
02436                         it != tmpList.end(); ++it, ++i ) {
02437                     if ( 0 == iStart &&
02438                          0 == QString::compare( sStart, ( *it ).upper() ) ) {
02439                         iStart = i;
02440                     }
02441                     if ( 0 == QString::compare( sEnd, ( *it ).upper() ) ) {
02442                         iEnd = i;
02443                     }
02444                 }
02445             }
02446 
02447             // check text widths to ensure all the entries will fit
02448             // into the available space
02449             if (    para.axisLabelStringCount()
02450                  && para.axisShortLabelsStringCount()
02451                  && para.axisLabelStringList() != para.axisShortLabelsStringList() ) {
02452                 QFont font( para.axisLabelsFont() );
02453                 if ( para.axisLabelsFontUseRelSize() )
02454                     font.setPixelSize( static_cast < int > ( nTxtHeight ) );
02455                 painter->setFont( font );
02456                 QFontMetrics fm( painter->fontMetrics() );
02457 
02458                 QStringList::Iterator it = tmpList.begin();
02459                 for ( int i = 0; i < nLabels; ++i ) {
02460                     if ( it != tmpList.end() ) {
02461                         if ( fm.width( *it ) > pTextsW ) {
02462                             useShortLabels = true;
02463                             break;
02464                         }
02465                         ++it;
02466                     }
02467                 }
02468             }
02469             if ( useShortLabels )
02470                 tmpList = para.axisShortLabelsStringList();
02471             else
02472                 tmpList = para.axisLabelStringList();
02473 
02474             // prepare transfering the strings into the labelTexts list
02475             double ddelta
02476                 = ( KDCHART_AXIS_LABELS_AUTO_DELTA == para.axisValueDelta() )
02477                 ? 1.0
02478                 : para.axisValueDelta();
02479             modf( ddelta, &ddelta );
02480             bool positive = ( 0.0 <= ddelta );
02481             int delta = static_cast < int > ( fabs( ddelta ) );
02482             // find 1st significant entry
02483             QStringList::Iterator it = positive
02484                 ? tmpList.begin()
02485                 : tmpList.fromLast();
02486             if ( positive )
02487                 for ( int i = 0; i < (int)tmpList.count(); ++i ) {
02488                     if ( i >= iStart )
02489                         break;
02490                     ++it;
02491                 }
02492             else
02493                 for ( int i = tmpList.count() - 1; i >= 0; --i ) {
02494                     if ( i <= iEnd )
02495                         break;
02496                     --it;
02497                 }
02498             // transfer the strings
02499             int meter = delta;
02500             int i2 = positive ? iStart : iEnd;
02501             for ( int iLabel = 0; iLabel < nLabels; ) {
02502                 if ( positive ) {
02503                     if ( it == tmpList.end() ) {
02504                         it = tmpList.begin();
02505                         i2 = 0;
02506                     }
02507                 } else {
02508                     if ( it == tmpList.begin() ) {
02509                         it = tmpList.end();
02510                         i2 = tmpList.count();
02511                     }
02512                 }
02513                 if ( ( positive && i2 >= iStart )
02514                         || ( !positive && i2 <= iEnd ) ) {
02515                     if ( meter >= delta ) {
02516                         labelTexts << *it;
02517                         ++iLabel;
02518                         meter = 1;
02519                     } else {
02520                         meter += 1;
02521                     }
02522                 }
02523                 if ( positive ) {
02524                     if ( i2 == iEnd ) {
02525                         it = tmpList.begin();
02526                         i2 = 0;
02527                     } else {
02528                         ++it;
02529                         ++i2;
02530                     }
02531                 } else {
02532                     if ( i2 == iStart ) {
02533                         it = tmpList.end();
02534                         i2 = tmpList.count();
02535                     } else {
02536                         --it;
02537                         --i2;
02538                     }
02539                 }
02540             }
02541         } else {
02542             // find out if the associated dataset contains only strings
02543             // if yes, we will take these as label texts
02544             uint dset = ( dataset == KDCHART_ALL_DATASETS ) ? 0 : dataset;
02545             //qDebug("\ndset: %u", dset);
02546             bDone = false;
02547             QVariant value;
02548             for ( uint col = 0; col < data.usedCols(); ++col ) {
02549                 if( data.cellCoord( dset, col, value, coordinate ) ){
02550                     if( QVariant::String == value.type() ){
02551                         const QString sVal = value.toString();
02552                         if( !sVal.isEmpty() && !sVal.isNull() ){
02553                             labelTexts.append( sVal );
02554                             bDone = true;
02555                         }
02556                     }else{
02557                         labelTexts.clear();
02558                         bDone = false;
02559                         break;
02560                     }
02561                 }
02562             }
02563         }
02564     }
02565 
02566 
02567     if ( bDone ) {
02568         // Some strings were found, now let us see which of them are
02569         // actually to be taken right now.
02570         //
02571         //
02572         //   F E A T U R E   P L A N N E D   F O R   F U T U R E . . .
02573         //
02574         //
02575 
02576     }
02577     else {
02578         // No strings were found, so let us either calculate the texts
02579         // based upon the numerical values of the associated dataset(s)
02580         // or just compose some default texts...
02581         if ( data.usedCols() && bSteadyCalc ) {
02582             // double values for numerical coordinates
02583             double nLow = 0.01;
02584             double nHigh = 0.0;
02585             double orgLow = 0.0;
02586             double orgHigh = 0.0;
02587             double nDelta = 0.0;
02588             double nDist = 0.0;
02589 
02590             //  VERTICAL axes support three modes:
02591             enum { Normal, Stacked, Percent } mode;
02592 
02593             if( bVertAxis ){
02594                 switch ( params_chartType ) {
02595                     case KDChartParams::Bar:
02596                         if ( KDChartParams::BarStacked
02597                                 == params.barChartSubType() )
02598                             mode = Stacked;
02599                         else if ( KDChartParams::BarPercent
02600                                 == params.barChartSubType() )
02601                             mode = Percent;
02602                         else
02603                             mode = Normal;
02604                         break;
02605                     case KDChartParams::Line:
02606                         if ( KDChartParams::LineStacked
02607                                 == params.lineChartSubType() )
02608                             mode = Stacked;
02609                         else if ( KDChartParams::LinePercent
02610                                 == params.lineChartSubType() )
02611                             mode = Percent;
02612                         else
02613                             mode = Normal;
02614                         break;
02615                     case KDChartParams::Area:
02616                         if ( KDChartParams::AreaStacked
02617                                 == params.areaChartSubType() )
02618                             mode = Stacked;
02619                         else if ( KDChartParams::AreaPercent
02620                                 == params.areaChartSubType() )
02621                             mode = Percent;
02622                         else
02623                             mode = Normal;
02624                         break;
02625                     case KDChartParams::HiLo:
02626                     case KDChartParams::BoxWhisker:
02627                         mode = Normal;
02628                         break;
02629                     case KDChartParams::Polar:
02630                         if ( KDChartParams::PolarStacked
02631                                 == params.polarChartSubType() )
02632                             mode = Stacked;
02633                         else if ( KDChartParams::PolarPercent
02634                                 == params.polarChartSubType() )
02635                             mode = Percent;
02636                         else
02637                             mode = Normal;
02638                         break;
02639                     default: {
02640                                  // Should not happen
02641                                  qDebug( "IMPLEMENTATION ERROR: Unknown params_chartType in calculateLabelTexts()" );
02642                                  mode = Normal;
02643                              }
02644                 }
02645             }else
02646                 mode = Normal; // this axis is not a vertical axis
02647 
02648             uint nLabels = 200;
02649 
02650             // find highest and lowest value of associated dataset(s)
02651             bool bOrdFactorsOk = false;
02652 
02653             if( adjustTheValues ){
02654                 nDelta = fabs( trueDelta );
02655                 pDelimDelta = trueDeltaPix;
02656                 nLow = QMIN( para.trueAxisLow(), para.trueAxisHigh() );
02657                 //qDebug("\nsearching: para.trueAxisLow() %f    para.trueAxisHigh() %f",para.trueAxisLow(),para.trueAxisHigh());
02658                 double orgLow( nLow );
02659                 modf( nLow / nDelta, &nLow );
02660                 nLow *= nDelta;
02661                 if ( nLow > orgLow )
02662                     nLow -= nDelta;
02663                 if ( 0.0 < nLow && 0.0 >= orgLow )
02664                     nLow = 0.0;
02665                 nHigh = nLow;
02666                 double dx = fabs( pXDeltaFactor * pDelimDelta );
02667                 double dy = fabs( pYDeltaFactor * pDelimDelta );
02668                 double x = 0.0;
02669                 double y = 0.0;
02670                 nLabels = 1;
02671                 if( axisLength ){
02672                     do{
02673                         ++nLabels;
02674                         nHigh += nDelta;
02675                         x += dx;
02676                         y += dy;
02677                     }while( x < axisLength && y < axisLength );
02678                     nHigh -= nDelta;
02679                     --nLabels;
02680                 }
02681                 nDist = nHigh - nLow;
02682                 bOrdFactorsOk = true;
02683 
02684             }
02685 
02686             if( !bOrdFactorsOk ){
02687                 const bool bAutoCalcStart =
02688                        ( Percent != mode )
02689                     && ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueStart() );
02690                 const bool bAutoCalcEnd =
02691                        ( Percent != mode )
02692                     && ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() );
02693 
02694                 if( !bIsDouble && !isDateTime ){
02695                     // no data at all: let us use our default 0..10 range
02696                     nLow   = 0.0;
02697                     nHigh  = 10.0;
02698                     nDist  = 10.0;
02699                     nDelta = 1.0;
02700                     nSubDelimFactor = 0.5;
02701                     bIsDouble     = true;
02702                     bOrdFactorsOk = true;
02703                 }else if( mode == Percent ){
02704                     // precentage mode: we use a 0..100 range
02705                     nLow   =   0.0;
02706                     nHigh  = 100.0;
02707                     nDist  = 100.0;
02708                     nDelta =  10.0;
02709                     nSubDelimFactor = 0.25;
02710                     bOrdFactorsOk = true;
02711                 }else{
02712                     //qDebug("\ngo:      nLow:  %f  nHigh: %f", nLow, nHigh );
02713                     // get the raw start value
02714                     const bool bStackedMode = (mode == Stacked);
02715                     if( bAutoCalcStart ){
02716                         if ( dataDataset == KDCHART_ALL_DATASETS ) {
02717                             if( bIsDouble ){
02718                                 nLow = bStackedMode
02719                                      ? QMIN( data.minColSum(), 0.0 )
02720                                      : data.minValue( coordinate,
02721                                                       isLogarithmic );
02722                                 //qDebug("\n1:      nLow:  %f", nLow );
02723 
02724                             }else{
02725                                 dtLow = data.minDtValue( coordinate );
02726                             }
02727                         } else {
02728                             if( bIsDouble ){
02729                                 nLow = bStackedMode
02730                                      ? QMIN( data.minColSum( dataDataset, dataDataset2 ),
02731                                              0.0 )
02732                                      : data.minInRows( dataDataset,dataDataset2,
02733                                                        coordinate,
02734                                                        isLogarithmic );
02735                             }else{
02736                                 dtLow = data.minDtInRows( dataDataset,dataDataset2,
02737                                                           coordinate );
02738                             }
02739                         }
02740                     }else{
02741                         if( bIsDouble ){
02742                             if( QVariant::Double == para.axisValueStart().type() )
02743                                 nLow  = para.axisValueStart().toDouble();
02744                         }else{
02745                             if( QVariant::DateTime == para.axisValueStart().type() )
02746                                 dtLow = para.axisValueStart().toDateTime();
02747                         }
02748                     }
02749 
02750                     // get the raw end value
02751                     if( bAutoCalcEnd ){
02752                         if ( dataDataset == KDCHART_ALL_DATASETS ) {
02753                             if( bIsDouble ){
02754                                 nHigh = bStackedMode
02755                                       ? QMAX( data.maxColSum(), 0.0 )
02756                                       : data.maxValue( coordinate );
02757                             }else{
02758                                 dtHigh = data.maxDtValue( coordinate );
02759                             }
02760                         } else {
02761                             if( bIsDouble )
02762                                 nHigh = bStackedMode
02763                                       ? QMAX( data.maxColSum( dataDataset, dataDataset2 ),
02764                                               0.0 )
02765                                       : data.maxInRows( dataDataset,dataDataset2,
02766                                                         coordinate );
02767                             else
02768                                 dtHigh = data.maxDtInRows( dataDataset,dataDataset2,
02769                                                           coordinate );
02770                         }
02771                         //qDebug("\nbAutoCalcEnd:\n  nLow:  %f\n  nHigh: %f", nLow, nHigh );
02772                     }else{
02773                         if( bIsDouble ){
02774                             if( QVariant::Double == para.axisValueEnd().type() )
02775                                 nHigh = para.axisValueEnd().toDouble();
02776                         }else{
02777                             if( QVariant::DateTime == para.axisValueEnd().type() )
02778                                 dtHigh = para.axisValueEnd().toDateTime();
02779                         }
02780                     }
02781                 }
02782 
02783 
02784                 //qDebug("\nnew:      nLow:  %f  nHigh: %f", nLow, nHigh );
02785 
02786                 if( bIsDouble ) {
02787                     if(    DBL_MAX == nLow
02788                         || (    ( 0.0 == nHigh || 0 == nHigh )
02789                              && ( 0.0 == nLow  || 0 == nLow  ) ) ) {
02790                         // qDebug("NO values or all values have ZERO value, showing 0.0 - 10.0 span");
02791                         nLow   = 0.0;
02792                         nHigh  = 10.0;
02793                         nDist  = 10.0;
02794                         nDelta = 1.0;
02795                         nSubDelimFactor = 0.5;
02796                         bOrdFactorsOk = true;
02797                         //qDebug("nLow: %f,  nHigh: %f", nLow, nHigh);
02798                     }else if( nLow == nHigh ){
02799                         // if both values are equal, but NOT Zero
02800                         // -> move the appropriate one to Zero
02801                         if( nLow < 0.0 )
02802                             nHigh = 0.0;
02803                         else
02804                             nLow = 0.0;
02805                         //qDebug("nLow: %f,  nHigh: %f", nLow, nHigh);
02806                     }else if( nHigh < nLow ){
02807                         // make sure nLow is <= nHigh
02808                         double nTmp = nLow;
02809                         nLow = nHigh;
02810                         nHigh = nTmp;
02811                     }
02812                 } else if( isDateTime ){
02813                     bool toggleDts = dtLow > dtHigh;
02814                     if( toggleDts ) {
02815                         QDateTime dt( dtLow );
02816                         dtLow = dtHigh;
02817                         dtHigh = dt;
02818                     }
02819                     double secDist = dtLow.secsTo( dtHigh );
02820                     secDist += (dtHigh.time().msec() - dtLow.time().msec()) / 1000.0;
02821                     const double aDist = fabs( secDist );
02822                     const double secMin   = 60.0;
02823                     const double secHour  = 60.0 * secMin;
02824                     const double secDay   = 24.0 * secHour;
02825                     //
02826                     // we temporarily disable week alignment until bug
02827                     // is fixed (1st week of year must not start in the
02828                     // preceeding year but rather be shown incompletely)
02829                     //
02830                     //                                 (khz, 2003/10/10)
02831                     //
02832                     //const int secWeek  =  7 * secDay;
02833                     const double secMonth = 30 * secDay;   // approx.
02834                     const double secYear  = 12 * secMonth; // approx.
02835                     if(      2.0*secMin > aDist )
02836                         dtDeltaScale = KDChartAxisParams::ValueScaleSecond;
02837                     else if( 2.0*secHour > aDist )
02838                         dtDeltaScale = KDChartAxisParams::ValueScaleMinute;
02839                     else if( 2.0*secDay > aDist )
02840                         dtDeltaScale = KDChartAxisParams::ValueScaleHour;
02841                     // khz: else if( 2*secWeek > aDist )
02842                     // khz:    dtDeltaScale = KDChartAxisParams::ValueScaleDay;
02843                     else if( 2.0*secMonth > aDist )
02844                         dtDeltaScale = KDChartAxisParams::ValueScaleDay;
02845                     // khz: dtDeltaScale = KDChartAxisParams::ValueScaleWeek;
02846 
02847                     else if( 2.0*secYear > aDist )
02848                         dtDeltaScale = KDChartAxisParams::ValueScaleMonth;
02849                     else if( 10.0*secYear > aDist )
02850                         dtDeltaScale = KDChartAxisParams::ValueScaleQuarter;
02851                     else
02852                         dtDeltaScale = KDChartAxisParams::ValueScaleYear;
02853 
02854 
02855                     //const int yearLow   = dtLow.date().year();
02856                     const int monthLow  = dtLow.date().month();
02857                     // khz: currently unused: const int dowLow    = dtLow.date().dayOfWeek();
02858                     const int dayLow    = dtLow.date().day();
02859                     const int hourLow   = dtLow.time().hour();
02860                     const int minuteLow = dtLow.time().minute();
02861                     const int secondLow = dtLow.time().second();
02862 
02863                     //const int yearHigh   = dtHigh.date().year();
02864                     const int monthHigh  = dtHigh.date().month();
02865                     // khz: currently unused: const int dowHigh    = dtHigh.date().dayOfWeek();
02866                     const int hourHigh   = dtHigh.time().hour();
02867                     const int minuteHigh = dtHigh.time().minute();
02868                     const int secondHigh = dtHigh.time().second();
02869                     int yearLowD   = 0;
02870                     int monthLowD  = 0;
02871                     int dayLowD    = 0;
02872                     int hourLowD   = 0;
02873                     int minuteLowD = 0;
02874                     int secondLowD = 0;
02875                     int yearHighD   = 0;
02876                     int monthHighD  = 0;
02877                     int dayHighD    = 0;
02878                     int hourHighD   = 0;
02879                     int minuteHighD = 0;
02880                     int secondHighD = 0;
02881                     bool gotoEndOfMonth = false;
02882                     switch( dtDeltaScale ) {
02883                         case KDChartAxisParams::ValueScaleSecond:
02884                             //qDebug("\nKDChartAxisParams::ValueScaleSecond");
02885                             if( 5.0 < aDist ){
02886                                 secondLowD = secondLow % 5;
02887                                 if( secondHigh % 5 )
02888                                     secondHighD = 5 - secondHigh % 5;
02889                             }
02890                             break;
02891                         case KDChartAxisParams::ValueScaleMinute:
02892                             //qDebug("\nKDChartAxisParams::ValueScaleMinute");
02893                             secondLowD = secondLow;
02894                             secondHighD = 59-secondHigh;
02895                             break;
02896                         case KDChartAxisParams::ValueScaleHour:
02897                             //qDebug("\nKDChartAxisParams::ValueScaleHour");
02898                             minuteLowD = minuteLow;
02899                             secondLowD = secondLow;
02900                             minuteHighD = 59-minuteHigh;
02901                             secondHighD = 59-secondHigh;
02902                             break;
02903                         case KDChartAxisParams::ValueScaleDay:
02904                             //qDebug("\nKDChartAxisParams::ValueScaleDay");
02905                             hourLowD   = hourLow;
02906                             minuteLowD = minuteLow;
02907                             secondLowD = secondLow;
02908                             hourHighD   = 23-hourHigh;
02909                             minuteHighD = 59-minuteHigh;
02910                             secondHighD = 59-secondHigh;
02911                             break;
02912                         case KDChartAxisParams::ValueScaleWeek:
02913                             //qDebug("\nKDChartAxisParams::ValueScaleWeek");
02914                             // khz: week scaling is disabled at the moment
02915                             /*
02916                             dayLowD = dowLow - 1;
02917                             hourLowD   = hourLow;
02918                             minuteLowD = minuteLow;
02919                             secondLowD = secondLow;
02920                             if( 7 > dowHigh )
02921                             dayHighD = 7 - dowHigh + 1;
02922                             */
02923                             break;
02924                         case KDChartAxisParams::ValueScaleMonth:
02925                             //qDebug("\nKDChartAxisParams::ValueScaleMonth");
02926                             if( 1 < dayLow )
02927                                 dayLowD = dayLow - 1;
02928                             hourLowD   = hourLow;
02929                             minuteLowD = minuteLow;
02930                             secondLowD = secondLow;
02931                             gotoEndOfMonth = true;
02932                             break;
02933                         case KDChartAxisParams::ValueScaleQuarter:
02934                             //qDebug("\nKDChartAxisParams::ValueScaleQuarter");
02935                             monthLowD = ( monthLow - 1 ) % 3;
02936                             dayLowD    = dayLow;
02937                             hourLowD   = hourLow;
02938                             minuteLowD = minuteLow;
02939                             secondLowD = secondLow;
02940                             if( ( monthHigh - 1 ) % 3 )
02941                                 monthHighD = 3 - ( monthHigh - 1 ) % 3;
02942                             gotoEndOfMonth = true;
02943                             break;
02944                         case KDChartAxisParams::ValueScaleYear:
02945                             //qDebug("\nKDChartAxisParams::ValueScaleYear");
02946                             monthLowD  = monthLow;
02947                             dayLowD    = dayLow;
02948                             hourLowD   = hourLow;
02949                             minuteLowD = minuteLow;
02950                             secondLowD = secondLow;
02951                             if( 12 > monthHigh )
02952                                 monthHighD = 12 - monthHigh;
02953                             gotoEndOfMonth = true;
02954                             break;
02955                         default:
02956                             /* NOOP */
02957                             break;
02958                     }
02959                     dtLow  = dtLow.addSecs(   -1 * (secondLowD + 60*minuteLowD + 3600*hourLowD) );
02960                     dtLow  = dtLow.addDays(   -1 * dayLowD   );
02961                     dtAddMonths( dtLow, -1 * monthLowD, dtLow );
02962                     dtAddYears(  dtLow, -1 * yearLowD,  dtLow );
02963                     dtHigh = dtHigh.addSecs(   secondHighD + 60*minuteHighD + 3600* hourHighD );
02964                     dtHigh = dtHigh.addDays(   dayHighD   );
02965                     dtAddMonths( dtHigh, monthHighD, dtHigh );
02966                     dtAddYears(  dtHigh, yearHighD,  dtHigh );
02967                     if( gotoEndOfMonth ){
02968                         dtHigh.setDate( QDate( dtHigh.date().year(),
02969                                     dtHigh.date().month(),
02970                                     dtHigh.date().daysInMonth() ) );
02971                         dtHigh.setTime( QTime( 23, 59, 59 ) );
02972                     }
02973                     if( toggleDts ) {
02974                         QDateTime dt( dtLow );
02975                         dtLow = dtHigh;
02976                         dtHigh = dt;
02977                     }
02978                     // secDist = dtLow.secsTo( dtHigh );
02979 
02980                     // NOTE: nSubDelimFactor is not set here since it
02981                     //        cannot be used for QDateTime values.
02982                     nSubDelimFactor = 0.0;
02983                     bOrdFactorsOk = true;
02984                 }
02985 
02986 
02987                 if( !bOrdFactorsOk ) {
02988                     // adjust one or both of our limit values
02989                     // according to max-empty-inner-span settings
02990                     nDist = nHigh - nLow;
02991                     if( !isLogarithmic ){
02992                         // replace nLow (or nHigh, resp.) by zero if NOT ALL OF
02993                         // our values are located outside of the 'max. empty
02994                         //  inner space' (i.e. percentage of the y-axis range
02995                         // that may to contain NO data entries)
02996                         int maxEmpty = para.axisMaxEmptyInnerSpan();
02997                         if( bAutoCalcStart ) {
02998                             //qDebug("\nbAutoCalcStart:\n  nLow:  %f\n  nHigh: %f", nLow, nHigh );
02999                             if( 0.0 < nLow ) {
03000                                 if(    maxEmpty == KDCHART_AXIS_IGNORE_EMPTY_INNER_SPAN
03001                                     || maxEmpty > ( nLow / nHigh * 100.0 ) )
03002                                     nLow = 0.0;
03003                                 else if( nDist / 100.0 < nLow )
03004                                     nLow -= nDist / 100.0; // shift lowest value
03005                             }
03006                             else if( nDist / 100.0 < fabs( nLow ) )
03007                                 nLow -= nDist / 100.0; // shift lowest value
03008                             nDist = nHigh - nLow;
03009                             //qDebug("* nLow:  %f\n  nHigh: %f", nLow, nHigh );
03010                         }
03011                         if( bAutoCalcEnd ) {
03012                             //qDebug("\nbAutoCalcEnd:\n  nLow:  %f\n  nHigh: %f", nLow, nHigh );
03013                             if( 0.0 > nHigh ) {
03014                                 if(    maxEmpty == KDCHART_AXIS_IGNORE_EMPTY_INNER_SPAN
03015                                         || maxEmpty > ( nHigh / nLow * 100.0 ) )
03016                                     nHigh = 0.0;
03017                                 else if( nDist / 100.0 > nHigh )
03018                                     nHigh += nDist / 100.0; // shift highest value
03019                             }
03020                             else if( nDist / 100.0 < fabs( nHigh ) )
03021                                  nHigh += nDist / 100.0; // shift highest value
03022                             nDist = nHigh - nLow;
03023                             //qDebug("* nLow:  %f\n  nHigh: %f\n\n", nLow, nHigh );
03024                         }
03025                     }
03026                 }
03027 
03028 
03029                 if( isLogarithmic ){
03030                     if( bIsDouble ) {
03031                         //qDebug("\n[L--] nLow: %f,  nHigh: %f,  nDelta: %f", nLow, nHigh, nDelta );
03032                         if( 0.0 == QABS( nLow ) )
03033                             nLow = -5;
03034                         else{
03035                             // find the Low / High values for the log. axis
03036                             nLow = log10( QABS( nLow ) );
03037                             //if( 0.0 >= nLow ){
03038                                 //nLow = fastPow10( -nLow );
03039                             //}
03040                         }
03041                         nHigh = log10( QABS( nHigh ) );
03042 
03043                         //qDebug("[L-0] nLow: %f,  nHigh: %f", nLow, nHigh );
03044                         double intPart=0.0; // initialization necessary for Borland C++
03045                         double fractPart = modf( nLow, &intPart );
03046                         //qDebug("  intPart: %f\nfractPart: %f", intPart, fractPart );
03047                         if( 0.0 > nLow && 0.0 != fractPart )
03048                             nLow = intPart - 1.0;
03049                         else
03050                             nLow = intPart;
03051                         fractPart = modf( nHigh, &intPart );
03052                         if( 0.0 != fractPart )
03053                           nHigh = intPart + 1.0;
03054 
03055                         nDist = nHigh - nLow;
03056                         nDelta = 1.0;
03057                         nSubDelimFactor = 0.1;
03058                         //qDebug("\n[L-1] nLow: %f,  nHigh: %f,  nDelta: %f", nLow, nHigh, nDelta );
03059                         bOrdFactorsOk = true;
03060                     }
03061                 }
03062 
03063 
03064                 if ( !bOrdFactorsOk ) {
03065                     // adjust one or both of our limit values
03066                     // according to first two digits of (nHigh - nLow) delta
03067                     double nDivisor;
03068                     double nRound;
03069                     nDist = nHigh - nLow;
03070                     //qDebug("* nLow:  %f\n  nHigh: %f  nDist: %f\n\n", nLow, nHigh, nDist );
03071                     // find out factors and adjust nLow and nHigh
03072                     orgLow  = nLow;
03073                     orgHigh = nHigh;
03074                     calculateOrdinateFactors( para, isLogarithmic,
03075                                               nDist, nDivisor, nRound,
03076                                               nDelta, nSubDelimFactor,
03077                                               nLow, nHigh );
03078                     nLabels = params.roundVal( nDist / nDelta );
03079 
03080                     //qDebug("\n0.  nOrgHigh: %f\n    nOrgLow:  %f",
03081                     //       orgHigh, orgLow);
03082                     //qDebug("\n    nDist:    %f\n    nHigh:    %f\n    nLow:     %f",
03083                     //       nDist, nHigh, nLow);
03084                     //qDebug("    nDelta: %f", nDelta);
03085                     //qDebug("    nRound: %f", nRound);
03086                     //qDebug("    nLabels: %u", nLabels);
03087 
03088                     if( para.axisSteadyValueCalc() ) {
03089                         ++nLabels;
03090                         //qDebug("*   nLabels: %u", nLabels );
03091                     }
03092                 }
03093 
03094 
03095                 // calculate the amount of nLabels to be written we could take
03096                 // based on the space we have for writing the label texts
03097                 if( ! (    KDCHART_AXIS_LABELS_AUTO_DELTA
03098                         == para.axisValueDelta() ) ){
03099                     nDist = nHigh - nLow;
03100                     nDelta = para.axisValueDelta();
03101                     nLabels = params.roundVal( nDist / nDelta );
03102 
03103                     //qDebug("\nI nLow: %f\n  nHigh: %f\n  nDelta: %f\n  nLabels: %u",
03104                     //       nLow, nHigh, nDelta, nLabels );
03105 
03106                     if( para.axisSteadyValueCalc() ) {
03107                         ++nLabels;
03108 
03109                         //qDebug("* nLabels: %u", nLabels );
03110 
03111                     }
03112                 }
03113 
03114                 // make sure labels fit into available height, if vertical axis
03115                 if( bVertAxis ) {
03116                     //Pending Michel
03117                     //find out the width
03118                     const KDChartAxisParams & xpara = params.axisParams( KDChartAxisParams::AxisPosBottom );
03119                     double areaWidth = xpara.axisTrueAreaRect().width();
03120                     //make sure to avoid inf
03121                     double areaHeight = para.axisTrueAreaRect().height()>0?para.axisTrueAreaRect().height():1.0;
03122                     double widthHeight = areaWidth / areaHeight;
03123                     //qDebug( "widthHeight %f, nDelta %f", widthHeight, nDelta);
03124                     //qDebug( "maxValue %f", data.maxValue());
03125                     //qDebug( "maxColSum %f", data.maxColSum());
03126                     //qDebug( "axisValueEnd() %f", para.axisValueEnd().toDouble());
03127                     double nDivisor;
03128                     double nRound;
03129                     orgLow = nLow;
03130                     orgHigh = nHigh;
03131 
03132                     //check if there are axis limitation - if not (auto calculation):
03133                     //adjust the axis for 3dbars in order to display the whole top of the bar
03134                     //in relation to the with and the height of the area.
03135                     // add conditions for multirows here
03136                     if ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd().toDouble()) {
03137                         if (params.threeDBars() ) {
03138                             if (  KDChartParams::BarPercent != params.barChartSubType()) {
03139                                 if ( widthHeight > 1.5 )
03140                                     orgHigh += nDelta * widthHeight;
03141                                 else
03142                                     orgHigh += widthHeight * 0.5;
03143                             }
03144                         }
03145                     }  else {
03146                         orgHigh = nHigh = para.axisValueEnd().toDouble();
03147                     }
03148                     //qDebug("\ncalc ordinate 0.  nDist: %f\n  nLow: %f\n  nHigh: %f\n  nDelta: %f\n  nLabels: %u", nDist, nLow, nHigh, nDelta, nLabels );
03149                     bool bTryNext = false;
03150                     uint minLabels = para.axisSteadyValueCalc() ? 3 : 2;
03151                     // the following must be processed at least twice - to avoid rounding errors
03152                     int pass = 0;
03153                     do{
03154                         nDist = nHigh - nLow;
03155                         nLow = orgLow;
03156                         nHigh = orgHigh;
03157                         /*
03158                         qDebug("\n=============================================================================\ncalc ordinate 1.  nDist: %f\n  nLow: %f\n  nHigh: %f\n  nDelta: %f\n  nLabels: %u",
03159                         nDist, nLow, nHigh, nDelta, nLabels );
03160                         */
03161                         calculateOrdinateFactors( para, isLogarithmic,
03162                                 nDist, nDivisor, nRound,
03163                                 nDelta,
03164                                 nSubDelimFactor, nLow, nHigh,
03165                                 bTryNext );
03166                         nLabels = params.roundVal( nDist / nDelta );
03167 
03168                         //qDebug("\ncalc ordinate 2.  nDist: %f\n+ nLow: %f\n  nHigh: %f\n  nDelta: %f\n  nLabels: %u",
03169                         //nDist, nLow, nHigh, nDelta, nLabels );
03170                         //QString sDelta;sDelta.setNum( nDelta, 'f', 24 );
03171                         //QString sLow;    sLow.setNum( nLow,   'f', 24 );
03172                         //qDebug("nLow: %f,  sLow: %s,  sDelta: %s", nLow, sLow.latin1(), sDelta.latin1());
03173 
03174                         // special case: End values was set by the user, but no Detla values was set.
03175                         if( !bAutoCalcEnd && orgHigh > nLow + nLabels * nDelta ) {
03176                             ++nLabels;
03177                             //qDebug("\nnLabels: %u\n", nLabels );
03178                         }
03179                         if( para.axisSteadyValueCalc() ) {
03180                             ++nLabels;
03181                             //qDebug("\nnLabels: %u\n", nLabels );
03182                         }
03183                         //qDebug("calc ordinate n.  nDist = nHigh - nLow: %f = %f - %f",nDist, nHigh, nLow);
03184                         //qDebug("    nRound: %f\n", nRound);
03185                         bTryNext = true;
03186                         ++pass;
03187                     }while (    ( pass < 2 )
03188                              || (    ( minLabels < nLabels )
03189                                   && ( areaHeight < ( nTxtHeight * 1.5 ) * nLabels ) ) );
03190                 }
03191             }
03192 
03193             // finally we can build the texts
03194             if( bIsDouble ) {
03195                 int trueBehindComma = -1;
03196                 double nVal = nLow;
03197                 for ( uint i = 0; i < nLabels; ++i ) {
03198           if( isLogarithmic ) {
03199                         labelTexts.append( applyLabelsFormat(
03200                             fastPow10( static_cast < int > ( nVal ) ),
03201                             divPow10,
03202                             behindComma,
03203                             1.0 == nDelta ? KDCHART_AXIS_LABELS_AUTO_DELTA : nDelta,
03204                             trueBehindComma,
03205                             notation,
03206                             decimalPoint,
03207                             thousandsPoint,
03208                             prefix,
03209                             postfix,
03210                             totalLen,
03211                             padFill,
03212                             blockAlign ) );
03213                   }  else {
03214                         labelTexts.append( applyLabelsFormat( nVal,
03215                                                               divPow10,
03216                                                               behindComma,
03217                                                               nDelta,
03218                                                               trueBehindComma,
03219                                                               notation,
03220                                                               decimalPoint,
03221                                                               thousandsPoint,
03222                                                               prefix,
03223                                                               postfix,
03224                                                               totalLen,
03225                                                               padFill,
03226                                                               blockAlign ) );
03227           }
03228                     nVal += nDelta;
03229                 }
03230 
03231                 // save our true Low and High value
03232                 //qDebug(para.axisSteadyValueCalc()?"\ntrue " : "\nfalse");
03233                 //qDebug("nVal: %f,  nDelta: %f", nVal, nDelta );
03234                 if ( para.axisSteadyValueCalc() ) {
03235                     nHigh = nVal - nDelta;
03236                 }
03237                 ( ( KDChartAxisParams& ) para ).setTrueAxisLowHighDelta( nLow, nHigh, nDelta );
03238                 //qDebug("[Z] nLow: %f,  nHigh: %f,  nDelta: %f", nLow, nHigh, nDelta );
03239 
03240             } else {
03241                 bool goDown = dtLow > dtHigh;
03242                 int mult = goDown ? -1 : 1;
03243                 QDateTime dt( dtLow );
03244                 nLabels = 0;
03245                 /*
03246                    qDebug("dtLow:  ");
03247                    qDebug(dtLow.toString(  Qt::ISODate ));
03248                    qDebug("dtHigh: ");
03249                    qDebug(dtHigh.toString( Qt::ISODate ));
03250                    */
03251                 bool bDone=false;
03252                 while( !bDone ) {
03253                     /*
03254                        qDebug("dtLow: %i %i %i    %i:%i:%i",
03255                        dtLow.date().year(),
03256                        dtLow.date().month(),
03257                        dtLow.date().day(),
03258                        dtLow.time().hour(),
03259                        dtLow.time().minute(),
03260                        dtLow.time().second());
03261                        qDebug("dtHigh: %i %i %i    %i:%i:%i",
03262                        dtHigh.date().year(),
03263                        dtHigh.date().month(),
03264                        dtHigh.date().day(),
03265                        dtHigh.time().hour(),
03266                        dtHigh.time().minute(),
03267                        dtHigh.time().second());
03268                        qDebug("dt: %i %i %i    %i:%i:%i",
03269                        dt.date().year(),
03270                        dt.date().month(),
03271                        dt.date().day(),
03272                        dt.time().hour(),
03273                        dt.time().minute(),
03274                        dt.time().second());
03275                        */
03276                     ++nLabels;
03277                     if( autoDtLabels )
03278                         labelTexts.append( "x" );
03279                     else
03280 #if COMPAT_QT_VERSION >= 0x030000
03281                         labelTexts.append( dt.toString( Qt::ISODate ) );
03282 #else
03283                     labelTexts.append( dateTimeToString( dt ) );
03284 #endif
03285                     bDone = (goDown ? (dt < dtLow ) : (dt > dtHigh));
03286                     /*if( bDone ){
03287                       dtHigh = dt;
03288                       }else*/{
03289                           switch( dtDeltaScale ) {
03290                               case KDChartAxisParams::ValueScaleSecond:
03291                                   dtAddSecs( dt, 1 * mult, dt );
03292                                   break;
03293                               case KDChartAxisParams::ValueScaleMinute:
03294                                   dtAddSecs( dt, 60 * mult, dt );
03295                                   break;
03296                               case KDChartAxisParams::ValueScaleHour:
03297                                   dtAddSecs( dt, 3600 * mult, dt );
03298                                   break;
03299                               case KDChartAxisParams::ValueScaleDay:
03300                                   dtAddDays( dt, 1 * mult, dt );
03301                                   break;
03302                               case KDChartAxisParams::ValueScaleWeek:
03303                                   dtAddDays( dt, 7 * mult, dt );
03304                                   break;
03305                               case KDChartAxisParams::ValueScaleMonth:
03306                                   dtAddMonths( dt,1 * mult, dt );
03307                                   break;
03308                               case KDChartAxisParams::ValueScaleQuarter:
03309                                   dtAddMonths( dt,3 * mult, dt );
03310                                   break;
03311                               case KDChartAxisParams::ValueScaleYear:
03312                                   dtAddYears( dt, 1 * mult, dt );
03313                                   break;
03314                               default:
03315                                   dtAddDays( dt, 1 * mult, dt );
03316                                   break;
03317                           }
03318                       }
03319                 }
03320                 //if( autoDtLabels )
03321                 //    labelTexts.append( "x" );
03322                 ( ( KDChartAxisParams& ) para ).setTrueAxisDtLowHighDeltaScale(
03323                                                                                dtLow, dtHigh,
03324                                                                                dtDeltaScale );
03325                 // note: pDelimDelta will be calculated below,
03326                 //       look for "COMMOM CALC OF NLABELS, DELIM DELTA..."
03327             }
03328             bDone = true;
03329         }
03330 
03331         // let's generate some strings
03332         if ( !bDone ) {
03333             // default scenario for abscissa axes
03334             uint count = bStatistical
03335                 ? (data.usedRows() ? data.usedRows() : 1)
03336                 : (data.usedCols() ? data.usedCols() : 1);
03337             //double start( 1.0 );
03338             double start( 1.0 + (bSteadyCalc ? 0.0 : static_cast < double >(data.colsScrolledBy())) );
03339 //qDebug("colsScrolledBy: %i", data.colsScrolledBy());
03340 //if(bVertAxis)
03341 //qDebug("vert nVal starting: %f",start);
03342 //else
03343 //qDebug("horz nVal starting: %f",start);
03344 //if(bSteadyCalc)
03345 //qDebug("bSteadyCalc");
03346 //else
03347 //qDebug("not bSteadyCalc");
03348             double delta( 1.0 );
03349             double finis( start + delta * ( count - 1 ) );
03350             const bool startIsDouble = QVariant::Double == para.axisValueStart().type();
03351             const bool endIsDouble   = QVariant::Double == para.axisValueEnd().type();
03352 
03353             bool deltaIsAuto = true;
03354             if ( !( KDCHART_AXIS_LABELS_AUTO_DELTA == para.axisValueDelta() ) ) {
03355                 delta = para.axisValueDelta();
03356                 deltaIsAuto = false;
03357             }
03358             if ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueStart() ) {
03359                 if ( ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() ) ) {
03360                     finis = start + delta * ( count - 1 );
03361                 } else {
03362                     if( endIsDouble ){
03363                         finis = para.axisValueEnd().toDouble();
03364                         start = finis - delta * ( count - 1 );
03365 //qDebug("1 changing:   start: %f",start);
03366                     } else {
03367                         //
03368                         //
03369                         //   F E A T U R E   P L A N N E D   F O R   F U T U R E . . .
03370                         //
03371                         //
03372                     }
03373                 }
03374             }else{
03375                 if ( startIsDouble ) {
03376                     start = para.axisValueStart().toDouble() + (bSteadyCalc ? 0.0 : static_cast < double >(data.colsScrolledBy()));
03377 //qDebug("2 changing:   start: %f",start);
03378                 } else {
03379                     //
03380                     //
03381                     //   F E A T U R E   P L A N N E D   F O R   F U T U R E . . .
03382                     //
03383                     //
03384                 }
03385                 if ( !( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() ) ) {
03386                     if (endIsDouble ) {
03387                         finis = para.axisValueEnd().toDouble();
03388                         if ( deltaIsAuto ) {
03389                             delta = ( finis - start ) / count;
03390                         } else {
03391                             count = static_cast < uint > (
03392                                     ( finis - start ) / delta );
03393                         }
03394                     } else {
03395                         // auto-rows like
03396                         // sunday, monday, tuesday, ...
03397                         //
03398                         //
03399                         //   F E A T U R E   P L A N N E D   F O R   F U T U R E . . .
03400                         //
03401                         //
03402                     }
03403                 }
03404                 else {
03405                     finis = start + delta * ( count - 1 );
03406                 }
03407             }
03408             QString prefix( QObject::tr( "Item " ) );
03409             QString postfix;
03410 
03411 
03412             if ( startIsDouble && endIsDouble ) {
03413                 int precis =
03414                     KDCHART_AXIS_LABELS_AUTO_DIGITS == para.axisDigitsBehindComma()
03415                     ? 0
03416                     : para.axisDigitsBehindComma();
03417                 double s = start;
03418                 double f = finis;
03419 //qDebug("label loop:   s: %f   f: %f",s,f);
03420                 bool up = ( 0.0 < delta );
03421                 // check the text widths of one large(?) entry
03422                 // and hope all the entries will
03423                 // fit into the available space
03424                 double value = up ? s : f;
03425                 uint nLabels = 0;
03426                 while ( up ? ( value <= f ) : ( value >= s ) ) {
03427                     ++nLabels;
03428                     value += delta * up ? 1.0 : -1.0;
03429                 }
03430                 calculateBasicTextFactors( nTxtHeight, para,
03431                         averageValueP1000,
03432                         basicPos, orig, delimLen, nLabels,
03433                         // start of return parameters
03434                         pDelimDelta,
03435                         pTextsX, pTextsY, pTextsW, pTextsH,
03436                         textAlign );
03437                 QFont font( para.axisLabelsFont() );
03438                 if ( para.axisLabelsFontUseRelSize() )
03439                     font.setPixelSize( static_cast < int > ( nTxtHeight ) );
03440                 painter->setFont( font );
03441                 QFontMetrics fm( painter->fontMetrics() );
03442 
03443                 if ( fm.width( prefix +
03444                             QString::number( -fabs( ( s + f ) / 2.0 + delta ),
03445                                 'f', precis ) )
03446                         > pTextsW ) {
03447                     prefix = "";
03448                     postfix = "";
03449                 }
03450                 // now transfer the strings into labelTexts
03451                 value = up ? s : f;
03452                 while ( up ? ( value <= f ) : ( value >= s ) ) {
03453                     labelTexts.append(
03454                             prefix + QString::number( value, 'f', precis )
03455                             + postfix );
03456                     value += delta * up ? 1.0 : -1.0;
03457                 }
03458             } else {
03459                 // pending(KHZ): make sure this branch will ever be reached
03460                 // check the text widths largest entry
03461                 // to make sure it will fit into the available space
03462                 calculateBasicTextFactors( nTxtHeight, para,
03463                         averageValueP1000,
03464                         basicPos, orig, delimLen,
03465                         count,
03466                         // start of return parameters
03467                         pDelimDelta,
03468                         pTextsX, pTextsY, pTextsW, pTextsH,
03469                         textAlign );
03470                 QFont font( para.axisLabelsFont() );
03471                 if ( para.axisLabelsFontUseRelSize() )
03472                     font.setPixelSize( static_cast < int > ( nTxtHeight ) );
03473                 painter->setFont( font );
03474                 QFontMetrics fm( painter->fontMetrics() );
03475 
03476                 if ( fm.width( prefix + QString::number( count - 1 ) )
03477                         > pTextsW ) {
03478                     prefix = "";
03479                     postfix = "";
03480                 }
03481                 // now transfer the strings into labelTexts
03482                 for ( uint i = 1; i <= count; ++i )
03483                     labelTexts.append(
03484                             prefix + QString::number( i ) + postfix );
03485             }
03486         }
03487     }
03488 
03489     /*
03490         finishing: COMMOM CALC OF NLABELS, DELIM DELTA...
03491     */
03492     uint nLabels = labelTexts.count()
03493         ? labelTexts.count()
03494         : 0;
03495     ( ( KDChartAxisParams& ) para ).setAxisLabelTexts( &labelTexts );
03496 
03497     if( !adjustTheValues ){
03498 
03499         calculateBasicTextFactors( nTxtHeight, para, averageValueP1000,
03500                 basicPos, orig, delimLen, nLabels,
03501                 // start of return parameters
03502                 pDelimDelta,
03503                 pTextsX, pTextsY, pTextsW, pTextsH,
03504                 textAlign );
03505     }
03506 
03507     ( ( KDChartAxisParams& ) para ).setTrueAxisDeltaPixels( pDelimDelta );
03508 
03509     //qDebug("\nsetting:   para.trueAxisLow() %f    para.trueAxisHigh() %f",para.trueAxisLow(),para.trueAxisHigh());
03510     //qDebug("\npDelimDelta: %f", pDelimDelta );
03511 
03512     /*
03513        qDebug( "Found label texts:" );
03514        for ( QStringList::Iterator it = labelTexts.begin();
03515        it != labelTexts.end(); ++it )
03516        qDebug( ">>>  %s", (*it).latin1() );
03517        qDebug( "\n" );
03518        */
03519 //qDebug("\nleaving KDChartAxesPainter::calculateLabelTexts() :   nTxtHeight: "+QString::number(nTxtHeight));
03520 }
03521 
03522 
03543 /**** static ****/
03544 void KDChartAxesPainter::calculateBasicTextFactors( double nTxtHeight,
03545         const KDChartAxisParams& para,
03546         double /*averageValueP1000*/,
03547         KDChartAxisParams::AxisPos basicPos,
03548         const QPoint& orig,
03549         double delimLen,
03550         uint nLabels,
03551         // start of return params
03552         double& pDelimDelta,
03553         double& pTextsX,
03554         double& pTextsY,
03555         double& pTextsW,
03556         double& pTextsH,
03557         int& textAlign )
03558 {
03559     switch ( basicPos ) {
03560         case KDChartAxisParams::AxisPosBottom: {
03561             bool bTouchEdges = para.axisLabelsTouchEdges();
03562             double wid = para.axisTrueAreaRect().width();
03563             double divi = bTouchEdges
03564                 ? ( 1 < nLabels ? nLabels - 1 : 1 )
03565                 : ( nLabels ? nLabels : 10 );
03566             pDelimDelta = wid / divi;
03567 
03568             pTextsW = pDelimDelta - 4.0;
03569             pTextsX = orig.x() + 2.0
03570                 - ( bTouchEdges
03571                         ? pDelimDelta / 2.0
03572                         : 0.0 );
03573             pTextsH = para.axisTrueAreaRect().height() - delimLen * 1.33;
03574             pTextsY = orig.y()
03575                 + delimLen * 1.33;
03576             textAlign = Qt::AlignHCenter | Qt::AlignTop;
03577             /*
03578               qDebug("pTextsW %f    wid %f    nLabels %u", pTextsW, wid, nLabels );
03579             */
03580         }
03581         break;
03582         case KDChartAxisParams::AxisPosLeft: {
03583             double hig = para.axisTrueAreaRect().height();
03584             pDelimDelta = hig / ( 1 < nLabels ? nLabels - 1 : 1 );
03585 
03586             pTextsX = para.axisTrueAreaRect().bottomLeft().x()
03587                 + 2.0;
03588             pTextsY = orig.y() - nTxtHeight / 2;
03589             pTextsW = para.axisTrueAreaRect().width()
03590                 - delimLen * 1.33 - 2.0;
03591             pTextsH = nTxtHeight;
03592             textAlign = Qt::AlignRight | Qt::AlignVCenter;
03593         }
03594         break;
03595         case KDChartAxisParams::AxisPosTop: {
03596             bool bTouchEdges = para.axisLabelsTouchEdges();
03597             double wid = para.axisTrueAreaRect().width();
03598             double divi = bTouchEdges
03599                 ? ( 1 < nLabels ? nLabels - 1 : 1 )
03600                 : ( nLabels ? nLabels : 10 );
03601             pDelimDelta = wid / divi;
03602 
03603             pTextsW = pDelimDelta - 4.0;
03604             pDelimDelta = wid / divi;
03605 
03606             pTextsX = orig.x() + 2.0
03607                 - ( bTouchEdges
03608                         ? pDelimDelta / 2.0
03609                         : 0.0 );
03610             pTextsH = para.axisTrueAreaRect().height() - delimLen * 1.33;
03611             pTextsY = para.axisTrueAreaRect().topLeft().y();
03612             textAlign = Qt::AlignHCenter | Qt::AlignBottom;
03613         }
03614         break;
03615         case KDChartAxisParams::AxisPosRight: {
03616             double hig = para.axisTrueAreaRect().height();
03617             pDelimDelta = hig / ( 1 < nLabels ? nLabels - 1 : 1 );
03618 
03619             pTextsX = para.axisTrueAreaRect().bottomLeft().x()
03620                 + delimLen * 1.33;
03621             pTextsY = orig.y() - nTxtHeight / 2;
03622             pTextsW = para.axisTrueAreaRect().width()
03623                 - delimLen * 1.33 - 2.0;
03624             pTextsH = nTxtHeight;
03625             textAlign = Qt::AlignLeft | Qt::AlignVCenter;
03626         }
03627         break;
03628         default: {
03629             qDebug( "IMPLEMENTATION ERROR: KDChartAxesPainter::calculateBasicTextFactors() unhandled enum value." );
03630             // NOOP since the 'basicPos' does not support more that these four values.
03631         }
03632         break;
03633     }
03634 }
03635 
03636 
03651 QString KDChartAxesPainter::truncateBehindComma( const double nVal,
03652         const int    behindComma,
03653         const double nDelta,
03654         int& trueBehindComma )
03655 {
03656     const int  nTrustedPrecision = 6; // when using 15 we got 1.850000 rounded to 1.849999999999999
03657 
03658     const bool bUseAutoDigits = KDCHART_AXIS_LABELS_AUTO_DIGITS == behindComma;
03659     const bool bAutoDelta     = KDCHART_AXIS_LABELS_AUTO_DELTA == nDelta;
03660     QString sVal;
03661     sVal.setNum( nVal, 'f', bUseAutoDigits ? nTrustedPrecision
03662                                            : QMIN(behindComma, nTrustedPrecision) );
03663     //qDebug("nVal: %f    sVal: "+sVal, nVal );
03664     //qDebug( QString("                     %1").arg(sVal));
03665     if ( bUseAutoDigits ) {
03666         int comma = sVal.find( '.' );
03667         if ( -1 < comma ) {
03668             if ( bAutoDelta ) {
03669                 int i = sVal.length();
03670                 while ( 1 < i
03671                         && '0' == sVal[ i - 1 ] )
03672                     --i;
03673                 sVal.truncate( i );
03674                 if ( '.' == sVal[ i - 1 ] )
03675                     sVal.truncate( i - 1 );
03676             } else {
03677                 if ( 0 > trueBehindComma ) {
03678                     QString sDelta = QString::number( nDelta, 'f', nTrustedPrecision );
03679                     int i = sDelta.length();
03680                     while ( 1 < i
03681                             && '0' == sDelta[ i - 1 ] )
03682                         --i;
03683                     sDelta.truncate( i );
03684                     if ( '.' == sDelta[ i - 1 ] )
03685                         trueBehindComma = 0;
03686                     else {
03687                         int deltaComma = sDelta.find( '.' );
03688                         if ( -1 < deltaComma )
03689                             trueBehindComma = sDelta.length() - deltaComma - 1;
03690                         else
03691                             trueBehindComma = 0;
03692                     }
03693                 }
03694                 // now we cut off the too-many digits behind the comma
03695                 int nPos = comma + ( trueBehindComma ? trueBehindComma + 1 : 0 );
03696                 sVal.truncate( nPos );
03697             }
03698         }
03699     }
03700     //qDebug( QString("                       -  %1").arg(trueBehindComma));
03701     return sVal;
03702 }
03703 
03704 #if defined ( Q_WS_WIN)
03705 #define trunc(x) ((int)(x))
03706 #endif
03707 
03708 QString KDChartAxesPainter::applyLabelsFormat( const double nVal_,
03709                         int   divPow10,
03710                         int   behindComma,
03711                         double nDelta_,
03712                         int&  trueBehindComma,
03713                         KDChartEnums::NumberNotation notation,
03714                         const QString& decimalPoint,
03715                         const QString& thousandsPoint,
03716                         const QString& prefix,
03717                         const QString& postfix,
03718                         int            totalLen,
03719                         const QChar&   padFill,
03720                         bool           blockAlign )
03721 {
03722     double nVal = nVal_ / fastPow10( divPow10 );
03723     double nDelta = nDelta_;
03724 
03725     double valLog10 = 0.0;
03726     if( notation == KDChartEnums::NumberNotationScientific ||
03727         notation == KDChartEnums::NumberNotationScientificBig ){
03728         valLog10 = (nVal != 0.0) ? trunc( log10(QABS(nVal)) ) : 0.0;
03729         //qDebug("nVal old: %f   valLog10: %f",nVal,valLog10);
03730         nVal   /= fastPow10( valLog10 );
03731         nDelta /= fastPow10( valLog10 );
03732         //qDebug("nVal new: %f",nVal);
03733     }
03734     QString sVal = truncateBehindComma( nVal,
03735                                         behindComma,
03736                                         nDelta,
03737                                         trueBehindComma );
03738     //qDebug("sVal    : "+sVal+"   behindComma: %i",behindComma);
03739 
03740     int posComma = sVal.find( '.' );
03741     if( 0 <= posComma ){
03742         sVal.replace( posComma, 1, decimalPoint);
03743     }else{
03744         posComma = sVal.length();
03745     }
03746     if( notation == KDChartEnums::NumberNotationScientific ||
03747         notation == KDChartEnums::NumberNotationScientificBig ){
03748         if( notation == KDChartEnums::NumberNotationScientific )
03749             sVal.append( "e" );
03750         else
03751             sVal.append( "E" );
03752         sVal.append( QString::number( valLog10, 'f', 0 ) );
03753     } else {
03754         if( thousandsPoint.length() ){
03755             const int minLen = (0 < sVal.length() && '-' == sVal[0])
03756                             ? 4
03757                             : 3;
03758             int n = posComma; // number of digits at the left side of the comma
03759             while( minLen < n ){
03760                 n -= 3;
03761                 sVal.insert(n, thousandsPoint);
03762             }
03763         }
03764     }
03765     sVal.append( postfix );
03766     int nFill = totalLen - (sVal.length() + prefix.length());
03767     if( 0 > nFill )
03768         nFill = 0;
03769     if( !blockAlign )
03770         sVal.prepend( prefix );
03771     for(int i=0; i < nFill; ++i)
03772         sVal.prepend( padFill );
03773     if( blockAlign )
03774         sVal.prepend( prefix );
03775     if ( totalLen > 0 )
03776         sVal.truncate( totalLen );
03777     /*Pending Michel: Force non fractional values
03778      *In case it is a fractional value
03779      *and the user has set axisLabelsDigitsBehindComma() == 0
03780      *return an empty string
03781      */
03782     if ( behindComma == 0 && QString::number(nVal).find('.') > 0 )
03783       sVal = QString::null;//sVal = "";
03784     return sVal;
03785 }
03786 
03792 void KDChartAxesPainter::calculateOrdinateFactors(
03793         const KDChartAxisParams& para,
03794         bool isLogarithmic,
03795         double& nDist,
03796         double& nDivisor,
03797         double& nRound,
03798         double& nDelta,
03799         double& nSubDelimFactor,
03800         double& nLow,
03801         double& nHigh,
03802         bool findNextRound )
03803 {
03804     if ( findNextRound ) {
03805         if ( 1.0 > nRound )
03806             nRound = 1.0;
03807         else
03808             if ( 2.0 > nRound )
03809                 nRound = 2.0;
03810             else
03811                 if ( 2.5 > nRound )
03812                     nRound = 2.5;
03813                 else
03814                     if ( 5.0 > nRound )
03815                         nRound = 5.0;
03816                     else {
03817                         nDivisor *= 10.0;
03818                         nRound = 1.0;
03819                     }
03820     } else {
03821         nDivisor = 1.0;
03822         QString sDistDigis2;
03823         sDistDigis2.setNum( nDist, 'f', 24);
03824         if ( 1.0 > nDist ) {
03825             sDistDigis2.remove( 0, 2 );
03826             nDivisor = 0.01;
03827             while ( 0 < sDistDigis2.length()
03828                     && '0' == sDistDigis2[ 0 ] ) {
03829                 nDivisor *= 0.1;
03830                 sDistDigis2.remove( 0, 1 );
03831             }
03832         } else {
03833             if ( 10.0 > nDist ) {
03834                 nDivisor = 0.1;
03835                 // remove comma, if present
03836                 sDistDigis2.remove( 1, 1 );
03837             } else
03838                 if ( 100.0 > nDist )
03839                     nDivisor = 1.0;
03840                 else {
03841                     int comma = sDistDigis2.find( '.' );
03842                     if ( -1 < comma )
03843                         sDistDigis2.truncate( comma );
03844                     nDivisor = fastPow10( (int)sDistDigis2.length() - 2 );
03845                 }
03846         }
03847         sDistDigis2.truncate( 2 );
03848         bool bOk;
03849         double nDistDigis2( sDistDigis2.toDouble( &bOk ) );
03850         if ( !bOk )
03851             nDistDigis2 = 10.0;
03852         if ( 75.0 <= nDistDigis2 )
03853             nRound = 5.0;
03854         else
03855             if ( 40.0 <= nDistDigis2 )
03856                 nRound = 2.5;
03857             else
03858                 if ( 20.0 <= nDistDigis2 )
03859                     nRound = 2.0;
03860                 else
03861                     nRound = 1.0;
03862     }
03863 
03864     nDelta = nRound * nDivisor;
03865 
03866     // make sure its a whole number > 0 if its a log axis. Just round up.
03867     if( isLogarithmic )
03868         nDelta = static_cast < int > ( nDelta ) < nDelta
03869                ? static_cast < int > ( nDelta ) + 1
03870                : static_cast < int > ( nDelta );
03871 
03872     bool bInvertedAxis = ( 0.0 > nDist );
03873     if( bInvertedAxis )
03874         nDelta *= -1.0;
03875 
03876     /*
03877        qDebug("  n D i s t       :  %f", nDist   );
03878        qDebug("  n D i v i s o r :  %f", nDivisor);
03879        qDebug("  n R o u n d     :  %f", nRound  );
03880        qDebug("  n D e l t a     :  %f", nDelta  );
03881        qDebug("  nHigh           :  %f", nHigh   );
03882        qDebug("  nLow            :  %f", nLow    );
03883        */
03884     if(    KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueStart()
03885         || !para.axisValueStartIsExact() ) {
03886         double orgLow( nLow );
03887         modf( nLow / nDelta, &nLow );
03888         nLow *= nDelta;
03889         if( bInvertedAxis ){
03890             if ( nLow < orgLow )
03891                 nLow += nDelta;
03892             if ( 0.0 > nLow && 0.0 <= orgLow )
03893                 nLow = 0.0;
03894         }else{
03895             if ( nLow > orgLow )
03896                 nLow -= nDelta;
03897             if ( 0.0 < nLow && 0.0 >= orgLow )
03898                 nLow = 0.0;
03899         }
03900     }
03901     if ( KDCHART_AXIS_LABELS_AUTO_LIMIT == para.axisValueEnd() ) {
03902         double orgHigh( nHigh );
03903         modf( nHigh / nDelta, &nHigh );
03904         nHigh *= nDelta;
03905         if( bInvertedAxis ){
03906             if ( nHigh > orgHigh )
03907                 nHigh -= nDelta;
03908             if ( 0.0 < nHigh && 0.0 >= orgHigh )
03909                 nHigh = 0.0;
03910         }else{
03911             if ( nHigh < orgHigh )
03912                 nHigh += nDelta;
03913             if ( 0.0 > nHigh && 0.0 <= orgHigh )
03914                 nHigh = 0.0;
03915         }
03916     }
03917 
03918     //qDebug("  n H i g h       :  %f", nHigh   );
03919     //qDebug("  n L o w         :  %f\n\n", nLow    );
03920 
03921     if ( 1.0 == nRound )
03922         nSubDelimFactor = 0.5;
03923     else
03924         if ( 2.0 == nRound )
03925             nSubDelimFactor = 0.25;
03926         else
03927             if ( 2.5 == nRound )
03928                 nSubDelimFactor = 0.2;
03929             else
03930                 if ( 5.0 == nRound )
03931                     nSubDelimFactor = 0.2;
03932                 else {
03933                     // Should not happen
03934                     qDebug( "IMPLEMENTATION ERROR: Unknown nRound in calculateOrdinateFactors()" );
03935                     nSubDelimFactor = 1.0;
03936                 }
03937 
03938     nDist = nHigh - nLow;
03939 }
03940 
03941 /**** static ****/
03942 void KDChartAxesPainter::saveDrawLine( QPainter& painter,
03943         QPoint pA,
03944         QPoint pZ,
03945         QPen pen )
03946 {
03947     const QPen oldPen( painter.pen() );
03948     bool bNice =    ( pen.color() == oldPen.color() )
03949         && ( pen.width() == oldPen.width() )
03950         && ( pen.style() == oldPen.style() );
03951     if ( !bNice )
03952         painter.setPen( pen );
03953     painter.drawLine( pA, pZ );
03954     if ( !bNice )
03955         painter.setPen( oldPen );
03956 }
03957 
03958 /**** static ****/
03959 void KDChartAxesPainter::dtAddSecs( const QDateTime& org, const int secs, QDateTime& dest )
03960 {
03961     //qDebug("entering KDChartAxesPainter::dtAddSecs() ..");
03962     int s = org.time().second();
03963     int m = org.time().minute();
03964     int h = org.time().hour();
03965     int days = 0;
03966     if( -1 < secs ){
03967         int mins = (s + secs) / 60;
03968         if( 0 == mins )
03969             s += secs;
03970         else{
03971             s = (s + secs) % 60;
03972             int hours = (m + mins) / 60;
03973             if( 0 == hours )
03974                 m += mins;
03975             else{
03976                 m = (m + mins) % 60;
03977                 days = (h + hours) / 24;
03978                 if( 0 == days )
03979                     h += hours;
03980                 else{
03981                     h = (h + hours) % 24;
03982                 }
03983             }
03984         }
03985     }
03986     dest.setTime( QTime(h,m,s) );
03987     dest.setDate( org.date() );
03988     if( days )
03989         dtAddDays( dest, days, dest );
03990     //qDebug(".. KDChartAxesPainter::dtAddSecs() done.");
03991 }
03992 
03993 /**** static ****/
03994 void KDChartAxesPainter::dtAddDays( const QDateTime& org, const int days, QDateTime& dest )
03995 {
03996     //qDebug("entering KDChartAxesPainter::dtAddDays() ..");
03997     int d = org.date().day();
03998     int m = org.date().month();
03999     int y = org.date().year();
04000     int dd = (-1 < days) ? 1 : -1;
04001     int di = 0;
04002     while( di != days ){
04003         d += dd;
04004         // underrunning day?
04005         if( 1 > d ){
04006             if( 1 < m ){
04007                 --m;
04008                 d = QDate( y,m,1 ).daysInMonth();
04009             }
04010             else{
04011                 --y;
04012                 m = 12;
04013                 d = 31;
04014             }
04015             // overrunning day?
04016         }else if( QDate( y,m,1 ).daysInMonth() < d ){
04017             if( 12 > m )
04018                 ++m;
04019             else{
04020                 ++y;
04021                 m = 1;
04022             }
04023             d = 1;
04024         }
04025         di += dd;
04026     }
04027     dest = QDateTime( QDate( y,m,d ), org.time() );
04028     //qDebug(".. KDChartAxesPainter::dtAddDays() done.");
04029 }
04030 
04031 /**** static ****/
04032 void KDChartAxesPainter::dtAddMonths( const QDateTime& org, const int months, QDateTime& dest )
04033 {
04034     //qDebug("entering KDChartAxesPainter::dtAddMonths() ..");
04035     int d = org.date().day();
04036     int m = org.date().month();
04037     int y = org.date().year();
04038     int md = (-1 < months) ? 1 : -1;
04039     int mi = 0;
04040     while( mi != months ){
04041         m += md;
04042         if( 1 > m ){
04043             --y;
04044             m = 12;
04045         }else if( 12 < m ){
04046             ++y;
04047             m = 1;
04048         }
04049         mi += md;
04050     }
04051     // QMIN takes care for intercalary day
04052     dest = QDateTime( QDate( y,m,QMIN( d, QDate( y,m,1 ).daysInMonth() ) ),
04053             org.time() );
04054     //qDebug(".. KDChartAxesPainter::dtAddMonths() done.");
04055 }
04056 
04057 /**** static ****/
04058 void KDChartAxesPainter::dtAddYears( const QDateTime& org, const int years, QDateTime& dest )
04059 {
04060     //qDebug("entering KDChartAxesPainter::dtAddYears() ..");
04061     int d = org.date().day();
04062     int m = org.date().month();
04063     int y = org.date().year() + years;
04064     dest.setTime( org.time() );
04065     // QMIN takes care for intercalary day
04066     dest = QDateTime( QDate( y,m,QMIN( d, QDate( y,m,d ).daysInMonth() ) ),
04067             org.time() );
04068     //qDebug(".. KDChartAxesPainter::dtAddYears() done.");
04069 }
04070 
04071 
04072 
04073 void KDChartAxesPainter::calculateAbscissaInfos( const KDChartParams& params,
04074                                                  const KDChartTableDataBase& data,
04075                                                  uint datasetStart,
04076                                                  uint datasetEnd,
04077                                                  double logWidth,
04078                                                  const QRect& dataRect,
04079                                                  abscissaInfos& infos )
04080 {
04081     if( params.axisParams( KDChartAxisParams::AxisPosBottom ).axisVisible()
04082         && ( KDChartAxisParams::AxisTypeUnknown
04083              != params.axisParams( KDChartAxisParams::AxisPosBottom ).axisType() ) )
04084         infos.abscissaPara = &params.axisParams( KDChartAxisParams::AxisPosBottom );
04085     else
04086         if( params.axisParams( KDChartAxisParams::AxisPosBottom2 ).axisVisible()
04087             && ( KDChartAxisParams::AxisTypeUnknown
04088                  != params.axisParams( KDChartAxisParams::AxisPosBottom2 ).axisType() ) )
04089             infos.abscissaPara = &params.axisParams( KDChartAxisParams::AxisPosBottom2 );
04090         else
04091             if( params.axisParams( KDChartAxisParams::AxisPosTop ).axisVisible()
04092                 && ( KDChartAxisParams::AxisTypeUnknown
04093                      != params.axisParams( KDChartAxisParams::AxisPosTop ).axisType() ) )
04094                 infos.abscissaPara = &params.axisParams( KDChartAxisParams::AxisPosTop );
04095             else
04096                 if( params.axisParams( KDChartAxisParams::AxisPosTop2 ).axisVisible()
04097                     && ( KDChartAxisParams::AxisTypeUnknown
04098                          != params.axisParams( KDChartAxisParams::AxisPosTop2 ).axisType() ) )
04099                     infos.abscissaPara = &params.axisParams( KDChartAxisParams::AxisPosTop2 );
04100                 else
04101                     // default is bottom axis:
04102                     infos.abscissaPara = &params.axisParams( KDChartAxisParams::AxisPosBottom );
04103 
04104     if( infos.abscissaPara->axisLabelsTouchEdges() )
04105         infos.bCenterThePoints = false;
04106 
04107     infos.bAbscissaDecreasing = infos.abscissaPara->axisValuesDecreasing();
04108     infos.bAbscissaIsLogarithmic
04109         = KDChartAxisParams::AxisCalcLogarithmic == infos.abscissaPara->axisCalcMode();
04110 
04111 
04112     // Number of values: If -1, use all values, otherwise use the
04113     // specified number of values.
04114     infos.numValues = 0;
04115     if ( params.numValues() > -1 )
04116         infos.numValues = params.numValues();
04117     else
04118         infos.numValues = data.usedCols();
04119 
04120     QVariant::Type type2Ref = QVariant::Invalid;
04121     infos.bCellsHaveSeveralCoordinates =
04122         data.cellsHaveSeveralCoordinates( datasetStart, datasetEnd,
04123                                           &type2Ref );
04124 
04125     infos.numLabels = (infos.abscissaPara &&
04126                        infos.abscissaPara->axisLabelTexts())
04127                       ? infos.abscissaPara->axisLabelTexts()->count()
04128                       : infos.numValues;
04129     if( 0 >= infos.numLabels )
04130         infos.numLabels = 1;
04131 
04132     infos.bAbscissaHasTrueAxisValues =
04133         infos.abscissaPara && (0.0 != infos.abscissaPara->trueAxisDelta());
04134     infos.abscissaStart = infos.bAbscissaHasTrueAxisValues
04135                           ? infos.abscissaPara->trueAxisLow()
04136                           : 0.0;
04137     infos.abscissaEnd   = infos.bAbscissaHasTrueAxisValues
04138                           ? infos.abscissaPara->trueAxisHigh()
04139                           : 1.0 * (infos.numLabels - 1);
04140     infos.abscissaSpan  = fabs( infos.abscissaEnd - infos.abscissaStart );
04141     infos.abscissaDelta = infos.bAbscissaHasTrueAxisValues
04142                           ? infos.abscissaPara->trueAxisDelta()
04143                           : (   ( 0.0 != infos.abscissaSpan  )
04144                                 ? ( infos.abscissaSpan / infos.numLabels )
04145                                 : infos.abscissaSpan );
04146 
04147     //qDebug( bAbscissaDecreasing ? "bAbscissaDecreasing =  TRUE" : "bAbscissaDecreasing = FALSE");
04148     //qDebug( abscissaHasTrueAxisValues ? "abscissaHasTrueAxisValues =  TRUE" : "abscissaHasTrueAxisValues = FALSE");
04149     //qDebug( "abscissaDelta = %f", abscissaDelta);
04150 
04151     infos.bAbscissaHasTrueAxisDtValues =
04152         (QVariant::DateTime == type2Ref) &&
04153         infos.abscissaPara &&
04154         infos.abscissaPara->trueAxisDtLow().isValid();
04155     if( infos.bAbscissaHasTrueAxisDtValues ){
04156         infos.numLabels = 200;
04157         infos.bCenterThePoints = false;
04158     }
04159 
04160     infos.dtLowPos = infos.bAbscissaHasTrueAxisDtValues
04161                      ? infos.abscissaPara->axisDtLowPosX() - dataRect.x()
04162                      : 0.0;
04163     infos.dtHighPos = infos.bAbscissaHasTrueAxisDtValues
04164                       ? infos.abscissaPara->axisDtHighPosX() - dataRect.x()
04165                       : logWidth;
04166     infos.abscissaDtStart = infos.bAbscissaHasTrueAxisDtValues
04167                             ? infos.abscissaPara->trueAxisDtLow()
04168                             : QDateTime();
04169     infos.abscissaDtEnd = infos.bAbscissaHasTrueAxisDtValues
04170                           ? infos.abscissaPara->trueAxisDtHigh()
04171                           : QDateTime();
04172 
04173     //Pending Michel case when same hh:mm:ss but different msecs
04174     infos.bScaleLessThanDay = ( infos.bAbscissaHasTrueAxisDtValues
04175                                 ? infos.abscissaPara->trueAxisDtDeltaScale()
04176                                 : KDChartAxisParams::ValueScaleDay )
04177                               < KDChartAxisParams::ValueScaleDay;
04178     if ( infos.abscissaDtStart.time() == infos.abscissaDtEnd.time() &&  infos.bScaleLessThanDay )
04179         infos.dtHighPos = logWidth;
04180 
04181     // adjust the milli seconds:
04182     infos.abscissaDtStart.setTime(
04183         QTime( infos.abscissaDtStart.time().hour(),
04184                infos.abscissaDtStart.time().minute(),
04185                infos.abscissaDtStart.time().second(),
04186                0 ) );
04187     infos.abscissaDtEnd.setTime(
04188         QTime( infos.abscissaDtEnd.time().hour(),
04189                infos.abscissaDtEnd.time().minute(),
04190                infos.abscissaDtEnd.time().second(),
04191                999 ) );
04192     //qDebug( infos.abscissaPara->trueAxisDtLow().toString("yyyy-MM-dd-hh:mm:ss.zzz"));
04193     //qDebug( infos.abscissaPara->trueAxisDtHigh().toString("yyyy-MM-dd-hh:mm:ss.zzz"));
04194     //qDebug(infos.abscissaDtStart.toString("yyyy-MM-dd-hh:mm:ss.zzz"));
04195     //qDebug(infos.abscissaDtEnd.toString("yyyy-MM-dd-hh:mm:ss.zzz"));
04196 /*
04197   infos.bScaleLessThanDay = ( infos.bAbscissaHasTrueAxisDtValues
04198   ? infos.abscissaPara->trueAxisDtDeltaScale()
04199   : KDChartAxisParams::ValueScaleDay )
04200   < KDChartAxisParams::ValueScaleDay;
04201 */
04202     if( infos.bAbscissaHasTrueAxisDtValues ){
04203         if( infos.bScaleLessThanDay  ){
04204             infos.abscissaDtSpan = infos.abscissaDtStart.secsTo( infos.abscissaDtEnd );
04205 
04206             /*  NOTE: We do *not* add the milli seconds because they aren't covered
04207                 by the span indicated by infos.dtHighPos - infos.dtLowPos.
04208                 if( infos.abscissaDtStart.time().msec() || infos.abscissaDtEnd.time().msec() )
04209                 infos.abscissaDtSpan =
04210                 ( infos.abscissaDtEnd.time().msec() -
04211                 infos.abscissaDtStart.time().msec() ) / 1000.0;
04212             */
04213         }
04214         else{
04215             infos.abscissaDtSpan = infos.abscissaDtStart.daysTo( infos.abscissaDtEnd );
04216             if( infos.abscissaDtStart.time().msec() || infos.abscissaDtEnd.time().msec() )
04217                 infos.abscissaDtSpan +=
04218                     ( infos.abscissaDtEnd.time().msec() -
04219                       infos.abscissaDtStart.time().msec() ) / (86400.0 * 1000.0);
04220             if( infos.abscissaDtEnd.time().second() )
04221                 infos.abscissaDtSpan += infos.abscissaDtEnd.time().second() / 86400.0;
04222             if( infos.abscissaDtEnd.time().minute() )
04223                 infos.abscissaDtSpan += infos.abscissaDtEnd.time().minute() / 1440.0;
04224             if( infos.abscissaDtEnd.time().hour() )
04225                 infos.abscissaDtSpan += infos.abscissaDtEnd.time().hour()   / 24.0;
04226         }
04227     }else
04228         infos.abscissaDtSpan = 10.0;
04229     if( 0 == infos.abscissaDtSpan || 0.0 == infos.abscissaDtSpan )
04230         infos.abscissaDtSpan = 1.0;
04231 
04232     infos.abscissaDtPixelsPerScaleUnit = (infos.dtHighPos - infos.dtLowPos)/ infos.abscissaDtSpan;
04233 
04234     if( infos.bAbscissaHasTrueAxisDtValues )
04235         infos.abscissaDelta = 20.0;
04236 
04237     infos.pointDist
04238         = ( infos.abscissaPara && (0.0 != infos.abscissaPara->trueAxisDeltaPixels()) )
04239         ? infos.abscissaPara->trueAxisDeltaPixels()
04240         : ( logWidth /
04241             (
04242                 (1 > ((double)(infos.numLabels) - (infos.bCenterThePoints ? 0.0 : 1.0)))
04243                 ? ((double)(infos.numLabels) - (infos.bCenterThePoints ? 0.0 : 1.0))
04244                 : 1 ) );
04245 
04246     infos.abscissaPixelsPerUnit = ( 0.0 != infos.abscissaDelta  )
04247                                   ? ( infos.pointDist / infos.abscissaDelta )
04248                                   : infos.pointDist;
04249 
04250     //const double infos.abscissaZeroPos2 = -1.0 * infos.abscissaPixelsPerUnit * infos.abscissaStart;
04251     infos.abscissaZeroPos = infos.abscissaPara->axisZeroLineStartX() - dataRect.x();
04252     //qDebug("abscissaZeroPos %f    abscissaZeroPos2 %f",abscissaZeroPos,abscissaZeroPos2);
04253 
04254     /*
04255       qDebug(abscissaPara ?
04256       "\nabscissaPara: OK" :
04257       "\nabscissaPara: leer");
04258       qDebug(abscissaHasTrueAxisValues ?
04259       "abscissaHasTrueAxisValues: TRUE" :
04260       "abscissaHasTrueAxisValues: FALSE");
04261       qDebug("abscissaStart: %f", abscissaStart);
04262       qDebug("abscissaEnd  : %f",   abscissaEnd);
04263       qDebug("abscissaPara->trueAxisDelta(): %f", abscissaPara->trueAxisDelta());
04264       qDebug("numValues  : %u,      numLabels  : %u",   numValues, numLabels);
04265     */
04266 }
04267 
04268 
04269 
04270 bool KDChartAxesPainter::calculateAbscissaAxisValue( const QVariant& value,
04271                                                      abscissaInfos& ai,
04272                                                      int colNumber,
04273                                                      double& xValue )
04274 {
04275     if( ai.bCellsHaveSeveralCoordinates ) {
04276         if( QVariant::Double == value.type() ) {
04277             double dVal = value.toDouble();
04278             if( ai.bAbscissaIsLogarithmic ){
04279                 if( 0.0 < dVal )
04280                     xValue = ai.abscissaPixelsPerUnit * log10( dVal );
04281                 else
04282                     xValue = -10250.0;
04283             }else{
04284                 xValue = ai.abscissaPixelsPerUnit * dVal;
04285             }
04286             xValue *= ai.bAbscissaDecreasing ? -1.0 : 1.0;
04287             xValue += ai.abscissaZeroPos;
04288         }
04289         else if( ai.bAbscissaHasTrueAxisDtValues &&
04290                  QVariant::DateTime == value.type() ) {
04291             const QDateTime dtVal = value.toDateTime();
04292             double dT = ( ai.bScaleLessThanDay )
04293                       ? ai.abscissaDtStart.secsTo( dtVal )
04294                       : ai.abscissaDtStart.daysTo( dtVal );
04295             /*
04296             qDebug("abscissaDtStart:  %i %i %i   %i:%i:%i.%i",
04297             ai.abscissaDtStart.date().year(),
04298             ai.abscissaDtStart.date().month(),
04299             ai.abscissaDtStart.date().day(),
04300             ai.abscissaDtStart.time().hour(),
04301             ai.abscissaDtStart.time().minute(),
04302             ai.abscissaDtStart.time().second(),
04303             ai.abscissaDtStart.time().msec());
04304             */
04305             //qDebug("days to = %f",dT);
04306 
04307             /*
04308             qDebug("                        dtVal: %i %i %i   %i:%i:%i.%i",
04309             dtVal.date().year(),
04310             dtVal.date().month(),
04311             dtVal.date().day(),
04312             dtVal.time().hour(),
04313             dtVal.time().minute(),
04314             dtVal.time().second(),
04315             dtVal.time().msec());
04316             */
04317             xValue = ai.abscissaDtPixelsPerScaleUnit * dT;
04318             if( dtVal.time().msec() )
04319                 xValue += (ai.abscissaDtPixelsPerScaleUnit * dtVal.time().msec())
04320                     / (   ai.bScaleLessThanDay
04321                         ? 1000.0
04322                         : (1000.0 * 86400.0) );
04323             //qDebug("xValue: %f",xValue);
04324             if( !ai.bScaleLessThanDay ){
04325                 if( dtVal.time().second() )
04326                     xValue += (ai.abscissaDtPixelsPerScaleUnit * dtVal.time().second())
04327                         / 86400.0;
04328                 if( dtVal.time().minute() )
04329                     xValue += (ai.abscissaDtPixelsPerScaleUnit * dtVal.time().minute())
04330                         / 1440.0;
04331                 if( dtVal.time().hour() )
04332                     xValue += (ai.abscissaDtPixelsPerScaleUnit * dtVal.time().hour())
04333                         / 24.0;
04334             }
04335             xValue *= ai.bAbscissaDecreasing ? -1.0 : 1.0;
04336             xValue += ai.dtLowPos;
04337             // qDebug("xValue = dtLowPos + abscissaDtPixelsPerScaleUnit * dT\n%f = %f + %f * %f",
04338             // xValue, dtLowPos, abscissaDtPixelsPerScaleUnit, dT);
04339         }
04340         else
04341             return false;
04342     } else
04343         xValue = ai.pointDist * ( double ) colNumber;
04344     return true;
04345 }
04346 
04347 
04348 
04349 /*************************************************************************/
04350 /*************************************************************************/
04351 /*************************************************************************/
04352 /***                                                                   ***/
04353 /***  Framework for data drawing using cartesian axes (Bar, Line, ...) ***/
04354 /***                                                                   ***/
04355 /*************************************************************************/
04356 /*************************************************************************/
04357 /*************************************************************************/
04358 
04369 void KDChartAxesPainter::paintData( QPainter* painter,
04370         KDChartTableDataBase* data,
04371         bool paint2nd,
04372         KDChartDataRegionList* regions )
04373 {
04374     bool bNormalMode = isNormalMode();
04375 
04376     uint chart = paint2nd ? 1 : 0;
04377 
04378     // find out the ordinate axis (or axes, resp.) belonging to this chart
04379     // (up to 4 ordinates might be in use: 2 left ones and 2 right ones)
04380     uint axesCount;
04381     KDChartParams::AxesArray ordinateAxes;
04382     ordinateAxes.resize( KDCHART_CNT_ORDINATES );
04383     if( !params()->chartAxes( chart, axesCount, ordinateAxes ) ) {
04384         // no axis - no fun!
04385         return;
04386         // We cannot draw data without an axis having calculated high/low
04387         // values and position of the zero line before.
04388 
04389         // PENDING(khz) Allow drawing without having a visible axis!
04390     }
04391 
04392     //const KDChartParams::ChartType params_chartType
04393     //  = paint2nd ? params()->additionalChartType() : params()->chartType();
04394 
04395     double logWidth = _dataRect.width();
04396     double areaWidthP1000 = logWidth / 1000.0;
04397 
04398     int nClipShiftUp = clipShiftUp(bNormalMode, areaWidthP1000);
04399     QRect ourClipRect( _dataRect );
04400     if ( 0 < ourClipRect.top() ) {
04401         ourClipRect.setTop( ourClipRect.top() - nClipShiftUp );
04402         ourClipRect.setHeight( ourClipRect.height() + nClipShiftUp - 1 );
04403     } else
04404         ourClipRect.setHeight( ourClipRect.height() + nClipShiftUp / 2 - 1 );
04405 
04406     // protect axes ?
04407     //ourClipRect.setBottom( ourClipRect.bottom() - 1 );
04408     //ourClipRect.setLeft( ourClipRect.left() + 1 );
04409     //ourClipRect.setRight( ourClipRect.right() - 1 );
04410 
04411     const QWMatrix & world = painter->worldMatrix();
04412     ourClipRect =
04413 #if COMPAT_QT_VERSION >= 0x030000
04414     world.mapRect( ourClipRect );
04415 #else
04416     world.map( ourClipRect );
04417 #endif
04418     painter->setClipRect( ourClipRect );
04419     painter->translate( _dataRect.x(), _dataRect.y() );
04420 
04421     painter->setPen( params()->outlineDataColor() );
04422 
04423     // find out which datasets are to be represented by this chart
04424     uint chartDatasetStart, chartDatasetEnd;
04425     findChartDatasets( data, paint2nd, chart, chartDatasetStart, chartDatasetEnd );
04426 
04427     // Note: 'aI' is *not* the axis number!
04428     for( uint aI = 0; aI < axesCount; ++aI ) {
04429         // 'axis' is the REAL axis number!
04430         uint axis = ordinateAxes.at( aI );
04431 
04432         const KDChartAxisParams* axisPara = &params()->axisParams( axis );
04433 
04434         uint datasetStart, datasetEnd;
04435         uint axisDatasetStart, axisDatasetEnd;
04436         uint dummy;
04437         if( params()->axisDatasets( axis,
04438                                     axisDatasetStart,
04439                                     axisDatasetEnd, dummy )
04440             && ( KDCHART_ALL_DATASETS != axisDatasetStart ) ) {
04441 
04442             if( KDCHART_NO_DATASET == axisDatasetStart ){
04443                 //==========
04444                 continue; //  NO DATASETS  -->  STOP PROCESSING THIS AXIS
04445                 //==========
04446             }
04447 
04448             if(    axisDatasetStart >= chartDatasetStart
04449                 && axisDatasetStart <= chartDatasetEnd )
04450                 datasetStart = QMAX( axisDatasetStart, chartDatasetStart );
04451             else if(    axisDatasetStart <= chartDatasetStart
04452                      && axisDatasetEnd   >= chartDatasetStart )
04453                 datasetStart = chartDatasetStart;
04454             else
04455                 datasetStart = 20;
04456             if(    axisDatasetEnd >= chartDatasetStart
04457                 && axisDatasetEnd <= chartDatasetEnd )
04458                 datasetEnd = QMIN( axisDatasetEnd, chartDatasetEnd );
04459             else if(    axisDatasetEnd   >= chartDatasetEnd
04460                      && axisDatasetStart <= chartDatasetEnd )
04461                 datasetEnd = chartDatasetEnd;
04462             else
04463                 datasetEnd = 0;
04464         } else {
04465             datasetStart = chartDatasetStart;
04466             datasetEnd   = chartDatasetEnd;
04467         }
04468 
04469         //qDebug("\n=========================================================="
04470         //       "\naxis   %u   axisDatasetStart %u   axisDatasetEnd %u   /   chartDatasetStart %u   chartDatasetEnd %u",
04471         //axis, axisDatasetStart, axisDatasetEnd, chartDatasetStart, chartDatasetEnd );
04472 
04473         double logHeight   = axisPara->axisTrueAreaRect().height();
04474         double axisYOffset = axisPara->axisTrueAreaRect().y() - _dataRect.y();
04475 
04476         //qDebug("\n==========================================================\naxis   %u   logHeight %f   axisDatasetStart %u   chartDatasetStart %u   axisDatasetEnd %u   chartDatasetEnd %u",
04477         //axis, logHeight, axisDatasetStart, chartDatasetStart, axisDatasetEnd, chartDatasetEnd );
04478         //if( KDCHART_ALL_DATASETS == axisDatasetStart )
04479         //    qDebug("  ALL DATASETS");
04480         //if( KDCHART_NO_DATASET == axisDatasetStart )
04481         //    qDebug("  N O   DATESETS");
04482 
04483         double maxColumnValue = axisPara->trueAxisHigh();
04484         double minColumnValue = axisPara->trueAxisLow();
04485         double columnValueDistance = maxColumnValue - minColumnValue;
04486 
04487 
04488         // call the chart type specific data painter:
04489         specificPaintData( painter,
04490                            ourClipRect,
04491                            data,
04492                            regions,
04493                            axisPara,
04494                            bNormalMode,
04495                            chart,
04496                            logWidth,
04497                            areaWidthP1000,
04498                            logHeight,
04499                            axisYOffset,
04500                            minColumnValue,
04501                            maxColumnValue,
04502                            columnValueDistance,
04503                            chartDatasetStart,
04504                            chartDatasetEnd,
04505                            datasetStart,
04506                            datasetEnd );
04507     }
04508     painter->translate( - _dataRect.x(), - _dataRect.y() );
04509 }
KDE Home | KDE Accessibility Home | Description of Access Keys