kchart

KDChartPainter.cpp

00001 /* -*- Mode: C++ -*-
00002    KDChart - a multi-platform charting engine
00003    */
00004 
00005 /****************************************************************************
00006  ** Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB.  All rights reserved.
00007  **
00008  ** This file is part of the KDChart library.
00009  **
00010  ** This file may be distributed and/or modified under the terms of the
00011  ** GNU General Public License version 2 as published by the Free Software
00012  ** Foundation and appearing in the file LICENSE.GPL included in the
00013  ** packaging of this file.
00014  **
00015  ** Licensees holding valid commercial KDChart licenses may use this file in
00016  ** accordance with the KDChart Commercial License Agreement provided with
00017  ** the Software.
00018  **
00019  ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020  ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021  **
00022  ** See http://www.klaralvdalens-datakonsult.se/?page=products for
00023  **   information about KDChart Commercial License Agreements.
00024  **
00025  ** Contact info@klaralvdalens-datakonsult.se if any conditions of this
00026  ** licensing are not clear to you.
00027  **
00028  **********************************************************************/
00029 #include <KDChartParams.h>
00030 #if defined ( SUN7 ) || defined (_SGIAPI) || defined ( Q_WS_WIN)
00031   #include <math.h>
00032 #else
00033   #include <cmath>
00034   #include <stdlib.h>
00035 #endif
00036 
00037 #include <KDDrawText.h>
00038 #include <KDChartPainter.h>
00039 #include <KDChartEnums.h>
00040 #include <KDChartParams.h>
00041 #include <KDChartCustomBox.h>
00042 #include <KDChartTableBase.h>
00043 #include <KDChartDataRegion.h>
00044 #include <KDChartUnknownTypeException.h>
00045 #include <KDChartNotEnoughSpaceException.h>
00046 #include <KDChartBarPainter.h>
00047 #include <KDChartAreaPainter.h>
00048 #include <KDChartLinesPainter.h>
00049 #include <KDChartPiePainter.h>
00050 #include <KDChartPolarPainter.h>
00051 #include <KDChartRingPainter.h>
00052 #include <KDChartHiLoPainter.h>
00053 #include <KDChartBWPainter.h>
00054 #include <KDChartTextPiece.h>
00055 
00056 #include <KDChart.h>  // for static method KDChart::painterToDrawRect()
00057 
00058 #include <qpainter.h>
00059 #include <qpaintdevice.h>
00060 #include <qpaintdevicemetrics.h>
00061 
00062 #define DEGTORAD(d) (d)*M_PI/180
00063 
00064 
00096 KDChartPainter::KDChartPainter( KDChartParams* params ) :
00097 _outermostRect( QRect(QPoint(0,0), QSize(0,0))),
00098 _legendTitle( 0 ),
00099 _params( params ),
00100 _legendNewLinesStartAtLeft( true ),
00101 _legendTitleHeight( 0 ),
00102 _legendTitleWidth( 0 ),
00103 _legendTitleMetricsHeight( 0 )
00104 {
00105     // This constructor intentionally left blank so far; we cannot setup the
00106     // geometry yet since we do not know the size of the painter.
00107 }
00108 
00113 KDChartPainter::~KDChartPainter()
00114 {
00115     delete _legendTitle;
00116 }
00117 
00118 bool KDChartPainter::calculateAllAxesLabelTextsAndCalcValues(
00119         QPainter*,
00120         KDChartTableDataBase*,
00121         double,
00122         double,
00123         double& )
00124 {
00125     // This function intentionally returning false; it is implemented
00126     // by the KDChartAxesPainter class only.
00127     return false;
00128 }
00129 
00145 KDChartPainter* KDChartPainter::create( KDChartParams* params, bool make2nd )
00146 {
00147     KDChartParams::ChartType cType = make2nd
00148         ? params->additionalChartType()
00149         : params->chartType();
00150     switch ( cType )
00151     {
00152         case KDChartParams::Bar:
00153             return new KDChartBarPainter( params );
00154         case KDChartParams::Line:
00155             return new KDChartLinesPainter( params );
00156         case KDChartParams::Area:
00157             return new KDChartAreaPainter( params );
00158         case KDChartParams::Pie:
00159             return new KDChartPiePainter( params );
00160         case KDChartParams::Ring:
00161             return new KDChartRingPainter( params );
00162         case KDChartParams::HiLo:
00163             return new KDChartHiLoPainter( params );
00164         case KDChartParams::BoxWhisker:
00165             return new KDChartBWPainter( params );
00166         case KDChartParams::Polar:
00167             return new KDChartPolarPainter( params );
00168         case KDChartParams::NoType:
00169         default:
00170             return 0;
00171     }
00172 }
00173 
00174 
00191 void KDChartPainter::registerPainter( const QString& /*painterName*/,
00192         KDChartPainter* /*painter*/ )
00193 {
00194     // PENDING(kalle) Implement this
00195     qDebug( "Sorry, not implemented:  KDChartPainter::registerPainter()" );
00196 }
00197 
00198 
00208 void KDChartPainter::unregisterPainter( const QString& /*painterName*/ )
00209 {
00210     // PENDING(kalle) Implement this
00211     qDebug( "Sorry, not implemented:  KDChartPainter::unregisterPainter()" );
00212 }
00213 
00214 
00227 void KDChartPainter::paint( QPainter* painter,
00228                             KDChartTableDataBase* data,
00229                             bool paintFirst,
00230                             bool paintLast,
00231                             KDChartDataRegionList* regions,
00232                             const QRect* rect,
00233                             bool mustCalculateGeometry )
00234 {
00235 
00236   
00237   if( paintFirst && regions ) 
00238         regions->clear();
00239   
00240     // Protect against non-existing data
00241     if( data->usedCols() == 0 && data->usedRows() == 0 )
00242         return ;
00243 
00244     QRect drawRect;
00245     //Pending Michel: at this point we have to setupGeometry
00246     if( mustCalculateGeometry || _outermostRect.isNull() ){
00247       if( rect ) 
00248             drawRect = *rect;
00249         else if( !KDChart::painterToDrawRect( painter, drawRect ) ){
00250             qDebug("ERROR: KDChartPainter::paint() could not calculate the drawing area.");
00251             return;
00252         }
00253       setupGeometry( painter, data, drawRect );
00254     }
00255     else
00256         drawRect = _outermostRect;
00257 
00258     //qDebug("A2: _legendRect:\n%i,%i\n%i,%i", _legendRect.left(),_legendRect.top(),_legendRect.right(),_legendRect.bottom() );
00259 
00260 
00261     // Note: In addition to the below paintArea calls there might be several
00262     //       other paintArea calls regarding to the BASE areas (AreaAxisBASE,
00263     //       AreaHdFtBASE, AreaCustomBoxesBASE).
00264     //       These additional calls result in smaller areas being drawn inside
00265     //       on the larger ones specifies here.
00266     if ( paintFirst ) {
00267         paintArea( painter, KDChartEnums::AreaOutermost );
00268         paintArea( painter, KDChartEnums::AreaInnermost );
00269 
00270         paintArea( painter, KDChartEnums::AreaDataAxesLegendHeadersFooters );
00271 
00272         paintArea( painter, KDChartEnums::AreaHeaders );
00273         paintArea( painter, KDChartEnums::AreaFooters );
00274         // header areas are drawn in the following order:
00275         //   1st center: main header, left main header, right main header
00276         //   2nd above:  header #0,   left header #0,   right header #0
00277         //   3rd below:  header #2,   left header #2,   right header #2
00278         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeader  );
00279         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeaderL );
00280         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeaderR );
00281         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeader0  );
00282         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeader0L );
00283         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeader0R );
00284         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeader2  );
00285         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeader2L );
00286         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosHeader2R );
00287         // footer areas are drawn in the same order as the header areas:
00288         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooter  );
00289         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooterL );
00290         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooterR );
00291         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooter0  );
00292         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooter0L );
00293         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooter0R );
00294         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooter2  );
00295         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooter2L );
00296         paintArea( painter, KDChartEnums::AreaHdFtBASE + KDChartParams::HdFtPosFooter2R );
00297 
00298         paintHeaderFooter( painter, data );
00299 
00300         paintArea( painter, KDChartEnums::AreaDataAxesLegend );
00301         paintArea( painter, KDChartEnums::AreaDataAxes );
00302         paintArea( painter, KDChartEnums::AreaAxes );
00303         for( int axis = KDChartAxisParams::AxisPosSTART;
00304              KDChartAxisParams::AxisPosEND >= axis; ++axis )
00305             paintArea( painter, KDChartEnums::AreaAxisBASE + axis );
00306         paintArea( painter, KDChartEnums::AreaData );
00307         paintAxes( painter, data );
00308     }
00309 
00310     painter->save();
00311     paintData( painter, data, !paintFirst, regions );
00312     painter->restore();
00313 
00314     if ( paintLast ) {
00315         // paint the frame lines of all little data region areas
00316         // on top of all data representations
00317         paintDataRegionAreas( painter, regions );
00318         if( KDChartParams::Bar          != params()->chartType() ||
00319             KDChartParams::BarMultiRows != params()->barChartSubType() )
00320             paintDataValues( painter, data, regions );
00321         if (params()->legendPosition()!=KDChartParams::NoLegend)
00322             paintArea(   painter, KDChartEnums::AreaLegend );
00323         paintLegend( painter, data );
00324         paintCustomBoxes( painter, regions );
00325     }
00326 }
00327 
00328 
00332 void KDChartPainter::paintArea( QPainter* painter,
00333         uint area,
00334         KDChartDataRegionList* regions,
00335         uint dataRow,
00336         uint dataCol,
00337         uint data3rd )
00338 {
00339     if( KDChartEnums::AreaCustomBoxesBASE != (KDChartEnums::AreaBASEMask & area) ){
00340         bool bFound;
00341         const KDChartParams::KDChartFrameSettings* settings =
00342             params()->frameSettings( area, bFound );
00343         if( bFound ) {
00344             bool allCustomBoxes;
00345             QRect rect( calculateAreaRect( allCustomBoxes,
00346                                            area,
00347                                            dataRow, dataCol, data3rd, regions ) );
00348 
00349             if( !allCustomBoxes )
00350                 paintAreaWithGap( painter, rect, *settings );
00351         }
00352     }
00353 }
00354 
00355 
00356 void KDChartPainter::paintDataRegionAreas( QPainter* painter,
00357                                            KDChartDataRegionList* regions )
00358 {
00359     if( regions ){
00360         int iterIdx;
00361         bool bFound;
00362         const KDChartParams::KDChartFrameSettings* settings =
00363             params()->frameSettings( KDChartEnums::AreaChartDataRegion, bFound, &iterIdx );
00364         while( bFound ) {
00365             bool bDummy;
00366             QRect rect( calculateAreaRect( bDummy,
00367                                            KDChartEnums::AreaChartDataRegion,
00368                                            settings->dataRow(),
00369                                            settings->dataCol(),
00370                                            settings->data3rd(),
00371                                            regions ) );
00372             // NOTE: we can *not* draw any background behind the
00373             //       data representations.
00374             // reason: for being able to do that we would have to
00375             //         know the respective regions _before_ the
00376             //         data representations are drawn; since that
00377             //         is impossible, we just draw the borders only
00378             //         ( == the corners and the edges ) and ignore the background
00379             //
00380             // (actually: Since the respective interface function does not allow
00381             //            specifying a background there is nothing to be ignored anyway.)
00382             settings->frame().paint( painter,
00383                                      KDFrame::PaintBorder,
00384                                      trueFrameRect( rect, settings ) );
00385             settings = params()->nextFrameSettings( bFound, &iterIdx );
00386         }
00387     }
00388 }
00389 
00390 
00391 QRect KDChartPainter::trueFrameRect( const QRect& orgRect,
00392                                      const KDChartParams::KDChartFrameSettings* settings ) const
00393 {
00394     QRect rect( orgRect );
00395     if( settings ){
00396         rect.moveBy( -settings->innerGapX(), -settings->innerGapY() );
00397         rect.setWidth(  rect.width()  + 2*settings->innerGapX() );
00398         rect.setHeight( rect.height() + 2*settings->innerGapY() );
00399     }
00400     return rect;
00401 }
00402 
00403 
00410 void KDChartPainter::paintAreaWithGap( QPainter* painter,
00411                                        QRect rect,
00412                                        const KDChartParams::KDChartFrameSettings& settings )
00413 {
00414     if( painter && rect.isValid() )
00415         settings.frame().paint( painter,
00416                                 KDFrame::PaintAll,
00417                                 trueFrameRect( rect, &settings ) );
00418 }
00419 
00420 
00424 void KDChartPainter::paintDataValues( QPainter* painter,
00425         KDChartTableDataBase* data,
00426         KDChartDataRegionList* regions )
00427 {
00428     KDChartDataRegion* region;
00429     if (    painter
00430             && data
00431             && regions
00432             && regions->count()
00433             && params()
00434             && (    params()->printDataValues( 0 )
00435                 || params()->printDataValues( 1 ) ) ) {
00436 
00437         // out of loop status saving
00438         painter->save();
00439      
00440         QFont font0( params()->dataValuesFont( 0 ) );
00441          
00442         if( params()->dataValuesUseFontRelSize(  0 ) ) {
00443             float size = QMIN(_areaWidthP1000, _areaHeightP1000) * abs(params()->dataValuesFontRelSize( 0 ));        
00444             if ( 9.0 > size )
00445                 size = 9.0;
00446             font0.setPixelSize( static_cast < int > ( size ) );
00447         }
00448         painter->setFont( font0 );
00449         QFontMetrics fm0( painter->fontMetrics() );
00450         double fm0HeightP100( fm0.height() / 100.0 );
00451         QFont font1( params()->dataValuesFont( 1 ) );
00452 
00453         if( params()->dataValuesUseFontRelSize(  1 ) ) {
00454             float size = QMIN(_areaWidthP1000, _areaHeightP1000) * abs(params()->dataValuesFontRelSize( 1 ));
00455             if ( 9.0 > size )
00456                 size = 9.0;
00457             font1.setPixelSize( static_cast < int > ( size ) );
00458         } else 
00459       font1.setPixelSize( font0.pixelSize());
00460         painter->setFont( font1 );
00461         QFontMetrics fm1( painter->fontMetrics() );
00462         double fm1HeightP100( fm1.height() / 100.0 );
00463 
00464         bool lastDigitIrrelevant0 = true;
00465         bool lastDigitIrrelevant1 = true;
00466         // get and format the texts
00467         for ( region=regions->first();
00468                 region != 0;
00469                 region = regions->next() ) {
00470             QVariant vValY;
00471             if( data->cellCoord( region->row, region->col, vValY, 1 ) ){
00472                 if( QVariant::String == vValY.type() ){
00473                     const QString sVal( vValY.toString() );
00474                     if( !sVal.isEmpty() )
00475                         region->text = sVal;
00476                 }else if( QVariant::Double == vValY.type() ){
00477                     double value( vValY.toDouble() );
00478                     region->negative = 0.0 > value;
00479                     double divi( pow( 10.0, params()->dataValuesDivPow10( region->chart ) ) );
00480                     if ( 1.0 != divi )
00481                         value /= divi;
00482                     int digits( params()->dataValuesDigitsBehindComma( region->chart ) );
00483                     bool autoDigits( KDCHART_DATA_VALUE_AUTO_DIGITS == digits );
00484                     if( autoDigits ) {
00485                         if( 10 < digits )
00486                             digits = 10;
00487                     } else
00488                         (   region->chart
00489                             ? lastDigitIrrelevant1
00490                             : lastDigitIrrelevant0 ) = false;
00491                     if( value == KDCHART_NEG_INFINITE )
00492                         region->text = "-LEMNISKATE";
00493                     else if( value == KDCHART_POS_INFINITE )
00494                         region->text = "+LEMNISKATE";
00495                     else {
00496                         region->text.setNum( value, 'f', digits );
00497                         if ( autoDigits && region->text.contains( '.' ) ) {
00498                             int len = region->text.length();
00499                             while (    3 < len
00500                                     && '0' == region->text[ len-1 ]
00501                                     && '.' != region->text[ len-2 ] ) {
00502                                 --len;
00503                                 region->text.truncate( len );
00504                             }
00505                             if( '0' != region->text[ len-1 ] )
00506                                 (   region->chart
00507                                     ? lastDigitIrrelevant1
00508                                     : lastDigitIrrelevant0 ) = false;
00509                         }
00510                     }
00511                 }
00512             }
00513         }
00514 
00515         if ( lastDigitIrrelevant0 || lastDigitIrrelevant1 )
00516             for ( region=regions->first();
00517                     region != 0;
00518                     region = regions->next() )
00519                 if (   (     ( lastDigitIrrelevant0 && !region->chart )
00520                             || ( lastDigitIrrelevant1 &&  region->chart ) )
00521                         && region->text.contains( '.' )
00522                         && ( 2 < region->text.length() ) )
00523                     region->text.truncate ( region->text.length() - 2 );
00524 
00525 
00526         // draw the Data Value Texts and calculate the text regions
00527         painter->setPen( Qt::black );
00528 
00529         bool allowOverlapping = params()->allowOverlappingDataValueTexts();
00530         bool drawThisOne;
00531         QRegion lastRegionDone;
00532 
00533         QFontMetrics actFM( painter->fontMetrics() );
00534 
00535         QFont* oldFont = 0;
00536         int oldRotation = 0;
00537         uint oldChart = UINT_MAX;
00538         uint oldDatacolorNo = UINT_MAX;
00539         for ( region=regions->first();
00540                 region != 0;
00541                 region = regions->next() ) {
00542 
00543             // in loop status saving
00544             painter->save();
00545 
00546             if ( region->text.length() ) {
00547 
00548                 QVariant vValY;
00549                 bool zero =
00550                     data->cellCoord( region->row, region->col, vValY, 1 ) &&
00551                     QVariant::Double == vValY.type() &&
00552                     ( 0.0 == vValY.toDouble() || 0 == vValY.toDouble() );
00553                 uint align( params()->dataValuesAnchorAlign( region->chart,
00554                             region->negative ) );
00555                 KDChartParams::ChartType cType = region->chart
00556                     ? params()->additionalChartType()
00557                     : params()->chartType();
00558 
00559 
00560                 // these use the bounding rect of region-region:
00561                 bool bIsAreaChart = KDChartParams::Area == cType;
00562                 bool rectangular = (    KDChartParams::Bar        == cType
00563                                      || KDChartParams::Line       == cType
00564                                      || bIsAreaChart
00565                                      || KDChartParams::HiLo       == cType
00566                                      || KDChartParams::BoxWhisker == cType );
00567 
00568                 // these use the nine anchor points stored in region->points
00569                 bool circular    = (    KDChartParams::Pie        == cType
00570                                      || KDChartParams::Ring       == cType
00571                                      || KDChartParams::Polar      == cType );
00572 
00573 
00574                 KDChartEnums::PositionFlag anchorPos(
00575                     params()->dataValuesAnchorPosition( region->chart, region->negative ) );
00576 
00577                 QPoint anchor(
00578                         rectangular
00579                         ? KDChartEnums::positionFlagToPoint( region->rect(), anchorPos )
00580                         : KDChartEnums::positionFlagToPoint( region->points, anchorPos ) );
00581 
00582                 double & fmHeightP100 = region->chart ? fm1HeightP100 : fm0HeightP100;
00583 
00584                 int angle = region->startAngle;
00585                 switch ( anchorPos ) {
00586                     case KDChartEnums::PosTopLeft:
00587                     case KDChartEnums::PosCenterLeft:
00588                     case KDChartEnums::PosBottomLeft:
00589                         angle += region->angleLen;
00590                         break;
00591                     case KDChartEnums::PosTopCenter:
00592                     case KDChartEnums::PosCenter:
00593                     case KDChartEnums::PosBottomCenter:
00594                         angle += region->angleLen / 2;
00595                         break;
00596                         /*
00597                            case KDChartEnums::PosTopRight:
00598                            case KDChartEnums::PosCenterRight:
00599                            case KDChartEnums::PosBottomRight:
00600                            angle += 0;
00601                            break;
00602                            */
00603                     default:
00604                         break;
00605                 }
00606                  double anchorDX( params()->dataValuesAnchorDeltaX( region->chart, region->negative )
00607                         * fmHeightP100 );
00608                  double anchorDY( params()->dataValuesAnchorDeltaY( region->chart, region->negative )
00609                         * fmHeightP100 );
00610                  if ( circular ) {
00611                      if ( 0.0 != anchorDY ) {
00612                         double normAngle = angle / 16;
00613                         double normAngleRad = DEGTORAD( normAngle );
00614                         double sinAngle = sin( normAngleRad );
00615                         QPoint& pM = region->points[ KDChartEnums::PosCenter ];
00616                         double dX( pM.x() - anchor.x() );
00617                         double dY( pM.y() - anchor.y() );
00618                         double radialLen( sinAngle ? dY / sinAngle : dY );
00619                         double radialFactor( ( radialLen == 0.0 ) ? 0.0 : ( ( radialLen - anchorDY ) / radialLen ) );
00620                         anchor.setX( static_cast < int > ( pM.x() - dX * radialFactor ) );
00621                         anchor.setY( static_cast < int > ( pM.y() - dY * radialFactor ) );
00622                      }
00623                 } else {
00624                     anchor.setX( anchor.x() + static_cast < int > ( anchorDX ) );
00625                     anchor.setY( anchor.y() + static_cast < int > ( anchorDY ) );
00626                 }
00627 
00628 
00629                 if(anchor.x() < -250){
00630                     anchor.setX(-250);
00631                     //qDebug("!! bad negative x position in KDChartPainter::paintDataValues() !!");
00632                 }
00633                 if(anchor.y() < -2500){
00634                     anchor.setY(-2500);
00635                     //qDebug("!! bad negative y position in KDChartPainter::paintDataValues() !!");
00636                 }
00637 
00638                 int rotation( params()->dataValuesRotation( region->chart,
00639                                                             region->negative ) );
00640                 bool incRotationBy90 = false;
00641                 if( region->text == "-LEMNISKATE" ||
00642                         region->text == "+LEMNISKATE" ){
00643                     if( params()->dataValuesShowInfinite( region->chart ) ){
00644                         //bool bIsLineChart = KDChartParams::Line == cType;
00645                         if( region->text == "-LEMNISKATE" )
00646                             align = Qt::AlignRight + Qt::AlignVCenter;
00647                         else
00648                             align = Qt::AlignLeft  + Qt::AlignVCenter;
00649                         if( !rotation )
00650                             rotation = 90;
00651                         else
00652                             incRotationBy90 = true;
00653                         region->text = " 8 ";
00654                     }else{
00655                         region->text = "";
00656                     }
00657                 }
00658 
00659                 if ( rotation ) {
00660           anchor = painter->worldMatrix().map( anchor );
00661 
00662                     //   Temporary solution for fixing the data labels size
00663                     // bug when in QPrinter::HighResolution mode:
00664                     //   There seem to be no backdraws by acting like this,
00665                     // but further investigation is required to detect the
00666                     // real error in the previous code/
00667                     if (    KDCHART_SAGGITAL_ROTATION   == rotation
00668                          || KDCHART_TANGENTIAL_ROTATION == rotation ) {
00669                         rotation = (   KDCHART_TANGENTIAL_ROTATION == rotation
00670                                      ? -1440
00671                                      : 0 )
00672                                  + angle;
00673                         rotation /= 16;
00674                         if( incRotationBy90 )
00675                             rotation += 90;
00676                         if ( 360 <= rotation )
00677                             rotation -= 360;
00678                         else if ( 0 > rotation )
00679                             rotation += 360;
00680                         rotation = 360 - rotation;
00681                     }else if( incRotationBy90 )
00682                         rotation = (rotation + 90) % 360;
00683 
00684 
00685              if( rotation != oldRotation ) {
00686               painter->rotate( rotation - oldRotation );
00687                       // Comment this out - zooming and scrolling
00688               // oldRotation = rotation;
00689              }
00690 
00691                     QFont* actFont = region->chart ? &font1 : &font0;
00692                     if( oldFont != actFont ) {
00693                         painter->setFont( *actFont );
00694                         actFM = QFontMetrics( painter->fontMetrics() );
00695             // Comment this out - zooming and scrolling
00696                         //oldFont = actFont;
00697                     }
00698 
00699                     KDDrawTextRegionAndTrueRect infosKDD =
00700                         KDDrawText::measureRotatedText( painter,
00701                                                         rotation,
00702                                                         anchor,
00703                                                         region->text,
00704                                                         0,
00705                                                         align,
00706                                                         &actFM,
00707                                                         true,
00708                                                         true,
00709                                                         5 );
00710                     //anchor = painter->worldMatrix().map( anchor );
00711 
00712                     if( allowOverlapping ) {
00713                         drawThisOne = true;
00714                     }else {
00715                         QRegion sectReg( infosKDD.region.intersect( lastRegionDone ) );
00716                         drawThisOne = sectReg.isEmpty();
00717                     }
00718                     if( drawThisOne ) {
00719                         lastRegionDone     = lastRegionDone.unite( infosKDD.region );
00720                         region->pTextRegion = new QRegion( infosKDD.region );
00721 
00722                         if( params()->dataValuesAutoColor( region->chart ) ) {
00723                             if( bIsAreaChart ){
00724                                 QColor color( params()->dataColor( region->row ) );
00725                                 /*
00726                                 if(    ( (0.0 > anchorDY) &&  region->negative )
00727                                     || ( (0.0 < anchorDY) && !region->negative ) )
00728                                     painter->setPen(
00729                                         QColor( static_cast < int > ( 255- color.red() ),
00730                                                 static_cast < int > ( 255- color.green() ),
00731                                                 static_cast < int > ( 255- color.blue() ) ) );
00732                                 else
00733                                 */
00734                                     painter->setPen( color.dark() );
00735                             }else{
00736                                 if( zero ) {
00737                                     if( oldDatacolorNo != UINT_MAX ) {
00738                                         painter->setPen( Qt::black );
00739                                         oldDatacolorNo = UINT_MAX;
00740                                     }
00741                                 }
00742                                 else {
00743                                     uint datacolorNo = (    KDChartParams::Pie   == cType
00744                                                         || KDChartParams::Ring  == cType )
00745                                         ? region->col
00746                                         : region->row;
00747                                     if(  oldDatacolorNo != datacolorNo ) {
00748                                         oldDatacolorNo = datacolorNo;
00749                                         QColor color( params()->dataColor( datacolorNo ) );
00750                                         painter->setPen( QColor(
00751                                                         static_cast < int > (255-color.red()  ),
00752                                                         static_cast < int > (255-color.green()),
00753                                                         static_cast < int > (255-color.blue() )));
00754                                     }
00755                                 }
00756                             }
00757                         }
00758                         else if( oldChart != region->chart ) {
00759                             oldChart = region->chart;
00760                             painter->setPen( params()->dataValuesColor( region->chart ) );
00761                         }
00762 
00763                         if( params()->optimizeOutputForScreen() ){
00764                             painter->rotate( -oldRotation );
00765                             oldRotation = 0;
00766                             if ( anchor.y() < 0 )
00767                   anchor.setY( -anchor.y() );
00768 
00769                             KDDrawText::drawRotatedText( painter,
00770                                                          rotation,
00771                                                          anchor,
00772                                                          region->text,
00773                                                          region->chart ? &font1 : &font0,
00774                                                          align,
00775                                                          false,   // bool showAnchor
00776                                                          0,       // const QFontMetrics* fontMet
00777                                                          false,   // bool noFirstrotate
00778                                                          false,   // bool noBackrotate
00779                                                          0,       // KDDrawTextRegionAndTrueRect* infos
00780                                                          true );  // bool optimizeOutputForScreen
00781                         }else{
00782                            painter->setPen( params()->dataValuesColor( region->chart ) );
00783                            painter->drawText( infosKDD.x , infosKDD.y ,
00784                                                infosKDD.width, infosKDD.height,
00785                                                Qt::AlignHCenter | Qt::AlignVCenter | Qt::SingleLine,
00786                                                region->text );
00787 
00788                         }
00789 
00790 
00791                     } // if not intersect
00792 
00793                 } else {
00794 
00795                     // no rotation:
00796                     painter->rotate( -oldRotation );
00797                     oldRotation = 0;
00798                     QFontMetrics & fm = region->chart ? fm1 : fm0;
00799                     int boundingRectWidth = fm.boundingRect( region->text ).width();
00800                     int leftBearing = fm.leftBearing( region->text[ 0 ] );
00801                     const QChar c =  region->text.at( region->text.length() - 1 );
00802                     int rightBearing = fm.rightBearing( c );
00803                     int w =  boundingRectWidth + leftBearing + rightBearing + 1;
00804                     int h = fm.height(); // ascent + descent + 1
00805                     int dx = 0;
00806                     int dy = 0;
00807                     switch( align & ( Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter ) ) {
00808                         case Qt::AlignRight:
00809                             dx = -w+1;
00810                             break;
00811                         case Qt::AlignHCenter:
00812                             // Center on the middle of the bounding rect, not
00813                             // the painted area, because numbers appear centered then
00814                             dx = -( ( boundingRectWidth / 2 ) + leftBearing );
00815                             break;
00816                     }
00817                     switch( align & ( Qt::AlignTop | Qt::AlignBottom | Qt::AlignVCenter ) ) {
00818                         case Qt::AlignBottom:
00819                             dy = -h+1;
00820                             break;
00821                         case Qt::AlignVCenter:
00822                             dy = -h / 2;
00823                             break;
00824                     }
00825 
00826                     QRegion thisRegion(
00827                             QRect( anchor.x() + dx, anchor.y() + dy, w, h ) );
00828                     if( allowOverlapping )
00829                         drawThisOne = true;
00830                     else {
00831                         QRegion sectReg( thisRegion.intersect( lastRegionDone ) );
00832                         drawThisOne = sectReg.isEmpty();
00833                     }
00834                     if( drawThisOne ) {
00835                         lastRegionDone      = lastRegionDone.unite( thisRegion );
00836                         region->pTextRegion = new QRegion( thisRegion );
00837 #ifdef DEBUG_TEXT_PAINTING
00838                         // for testing:
00839                         QRect rect( region->pTextRegion->boundingRect() );
00840                         painter->drawRect( rect );
00841                         painter->setPen( Qt::red );
00842                         rect.setLeft( rect.left() + leftBearing );
00843                         rect.setTop( rect.top() + ( fm.height()-fm.boundingRect( region->text ).height() ) /2 );
00844                         rect.setWidth( fm.boundingRect( region->text ).width() );
00845                         rect.setHeight( fm.boundingRect( region->text ).height() );
00846                         painter->drawRect( rect );
00847                         painter->setPen( Qt::black );
00848 #endif
00849                         /*
00850 
00851 NOTE: The following will be REMOVED again once
00852 the layout policy feature is implemented !!!
00853 
00854 */
00855                         QRect textRect( region->pTextRegion->boundingRect() );
00856                         if( bIsAreaChart ){
00857                             QBrush brush( params()->dataValuesBackground( region->chart ) );
00858                             painter->setBrush( brush );
00859                             painter->setPen(   Qt::NoPen );
00860                             QRect rect( textRect );
00861                             rect.moveBy( -2, 0 );
00862                             rect.setWidth( rect.width() + 4 );
00863                             painter->drawRect( rect );
00864                         }
00865                         painter->setFont( region->chart ? font1 : font0 );
00866                         if( params()->dataValuesAutoColor( region->chart ) ) {
00867                             if( bIsAreaChart ){
00868                                 QColor color( params()->dataColor( region->row ) );
00869                                 /*
00870                                 if(    ( (0.0 > anchorDY) &&  region->negative )
00871                                     || ( (0.0 < anchorDY) && !region->negative ) )
00872                                     painter->setPen(
00873                                         QColor( static_cast < int > ( 255- color.red() ),
00874                                                 static_cast < int > ( 255- color.green() ),
00875                                                 static_cast < int > ( 255- color.blue() ) ) );
00876                                 else
00877                                 */
00878                                     painter->setPen( color.dark() );
00879                             }else{
00880                                 if( zero )
00881                                     painter->setPen( Qt::black );
00882                                 else {
00883                                     QColor color( params()->dataColor(
00884                                                 (    KDChartParams::Pie   == params()->chartType()
00885                                                     || KDChartParams::Ring  == params()->chartType() )
00886                                                 ? region->col
00887                                                 : region->row ) );
00888                                     painter->setPen( QColor( static_cast < int > ( 255- color.red() ),
00889                                                 static_cast < int > ( 255- color.green() ),
00890                                                 static_cast < int > ( 255- color.blue() ) ) );
00891                                 }
00892                             }
00893                         }else{
00894                             painter->setPen( params()->dataValuesColor( region->chart ) );
00895                         }
00896                         painter->drawText( textRect.left(),    textRect.top(),
00897                                            textRect.width()+1, textRect.height()+1,
00898                                            Qt::AlignLeft | Qt::AlignTop, region->text );
00899                     }
00900 
00901 
00902                 }
00903             }
00904             //
00905           painter->restore();
00906 
00907         }
00908     painter->restore();
00909     }
00910 
00911 }
00912 
00913 
00917 void KDChartPainter::paintCustomBoxes( QPainter* painter,
00918                                        KDChartDataRegionList* regions )
00919 {
00920     // paint all of the custom boxes AND their surrounding frames+background (if any)
00921     bool bGlobalFound;
00922     const KDChartParams::KDChartFrameSettings* globalFrameSettings
00923         = params()->frameSettings( KDChartEnums::AreasCustomBoxes, bGlobalFound );
00924 
00925     uint idx;
00926     for( idx = 0; idx <= params()->maxCustomBoxIdx(); ++idx ) {
00927         const KDChartCustomBox * box = params()->customBox( idx );
00928         if( box ) {
00929             // paint border and background
00930             paintArea( painter,
00931                        KDChartEnums::AreaCustomBoxesBASE + idx,
00932                        regions,
00933                        box->dataRow(),
00934                        box->dataCol(),
00935                        box->data3rd() );
00936             // retrieve frame information
00937             bool bIndividualFound;
00938             const KDChartParams::KDChartFrameSettings * individualFrameSettings
00939                 = params()->frameSettings( KDChartEnums::AreaCustomBoxesBASE + idx,
00940                                            bIndividualFound );
00941             const KDChartParams::KDChartFrameSettings * settings
00942                 = bIndividualFound ? individualFrameSettings
00943                                    : bGlobalFound ? globalFrameSettings : 0;
00944             // paint content
00945             const QPoint anchor( calculateAnchor( *box, regions ) );
00946             box->paint( painter,
00947                         anchor,
00948                         _areaWidthP1000,
00949                         _areaHeightP1000,
00950                         settings ? settings->framePtr() : 0,
00951                         trueFrameRect( box->trueRect( anchor, _areaWidthP1000, _areaHeightP1000 ),
00952                                        settings ) );
00953         }
00954     }
00955 }
00956 
00957 
00961 QPoint KDChartPainter::calculateAnchor( const KDChartCustomBox & box,
00962         KDChartDataRegionList* regions ) const
00963 {
00964     QPoint pt(0,0);
00965 
00966     // Recursion handling:
00967     //
00968     //    *  calculateAnchor() normally calls calculateAreaRect()
00969     //
00970     //    *  calculateAreaRect() will in turn calls calculateAnchor() in case of
00971     //       box.anchorArea() being based on KDChartEnums::AreaCustomBoxesBASE
00972     //
00973     //    This is Ok as long as the recursive call of calculateAnchor() is NOT
00974     //    intend examination the same box as a previous call.
00975     //
00976     // Rule:
00977     //
00978     //    A box may be aligned to another box (and the 2nd box may again be
00979     //    aligned to a 3rd box and so on) but NO CIRCULAR alignment is allowed.
00980     //
00981     if( !box.anchorBeingCalculated() ) {
00982 
00983         box.setInternalFlagAnchorBeingCalculated( true );
00984 
00985         bool allCustomBoxes;
00986         QRect rect( calculateAreaRect( allCustomBoxes,
00987                                        box.anchorArea(),
00988                                        box.dataRow(),
00989                                        box.dataCol(),
00990                                        box.data3rd(),
00991                                        regions ) );
00992         if( allCustomBoxes ) {
00993             //
00994             //  Dear user of this library.
00995             //
00996             //  You faced the above error during program runtime?
00997             //
00998             //  The reason for this is that you may NOT use  AreasCustomBoxes
00999             //  as a value for the KDChartCustomBox anchor area.
01000             //
01001             //  This is due to the fact that an anchor area allways must specify one AREA
01002             //  or some contiguous areas that form an area when combined.
01003             //  The flag  AreasCustomBoxes  however specifies a list of custom boxes
01004             //  that normally do not form a contiguos ares, so they cannot be used as anchor area.
01005             //
01006             //  In order to specify a SINGLE custom box please use AreaCustomBoxBASE+boxId.
01007             //
01008         }
01009         pt = KDChartEnums::positionFlagToPoint( rect, box.anchorPosition() );
01010 
01011         box.setInternalFlagAnchorBeingCalculated( false );
01012     }
01013 
01014     return pt;
01015 }
01016 
01017 
01022 QRect KDChartPainter::calculateAreaRect( bool & allCustomBoxes,
01023                                          uint area,
01024                                          uint dataRow,
01025                                          uint dataCol,
01026                                          uint /*data3rd*/,
01027                                          KDChartDataRegionList* regions ) const
01028 {
01029     QRect rect(0,0, 0,0);
01030     allCustomBoxes = false;
01031     uint pos;
01032     switch( area ) {
01033         case KDChartEnums::AreaData:
01034             rect = _dataRect;
01035             break;
01036         case KDChartEnums::AreaAxes:
01037             break;
01038         case KDChartEnums::AreaLegend:
01039             rect = _legendRect;
01040             break;
01041         case KDChartEnums::AreaDataAxes:
01042             rect = _axesRect;
01043             break;
01044         case KDChartEnums::AreaDataAxesLegend:
01045             rect = _axesRect;
01046             if( _legendRect.isValid() ) {
01047                 if( rect.isValid() )
01048                     rect = rect.unite( _legendRect );
01049                 else
01050                     rect = _legendRect;
01051             }
01052             break;
01053         case KDChartEnums::AreaHeaders: {
01054                                             bool bStart = true;
01055                                             for( pos = KDChartParams::HdFtPosHeadersSTART;
01056                                                     KDChartParams::HdFtPosHeadersEND >= pos;
01057                                                     ++pos ) {
01058                                                 const QRect& r = params()->headerFooterRect( pos );
01059                                                 if( r.isValid() ) {
01060                                                     if( bStart )
01061                                                         rect = r;
01062                                                     else
01063                                                         rect = rect.unite( r );
01064                                                     bStart = false;
01065                                                 }
01066                                             }
01067                                         }
01068                                         break;
01069         case KDChartEnums::AreaFooters: {
01070                                             bool bStart = true;
01071                                             for( pos = KDChartParams::HdFtPosFootersSTART;
01072                                                     KDChartParams::HdFtPosFootersEND >= pos;
01073                                                     ++pos ) {
01074                                                 const QRect& r = params()->headerFooterRect( pos );
01075                                                 if( r.isValid() ) {
01076                                                     if( bStart )
01077                                                         rect = r;
01078                                                     else
01079                                                         rect = rect.unite( r );
01080                                                     bStart = false;
01081                                                 }
01082                                             }
01083                                         }
01084                                         break;
01085         case KDChartEnums::AreaDataAxesLegendHeadersFooters: {
01086                                                                  rect = _axesRect;
01087                                                                  bool bStart = !rect.isValid();
01088                                                                  if( _legendRect.isValid() ) {
01089                                                                      if( bStart )
01090                                                                          rect = _legendRect;
01091                                                                      else
01092                                                                          rect = rect.unite( _legendRect );
01093                                                                      bStart = false;
01094                                                                  }
01095                                                                  for( pos = KDChartParams::HdFtPosSTART;
01096                                                                          KDChartParams::HdFtPosEND >= pos;
01097                                                                          ++pos ) {
01098                                                                      const QRect& r = params()->headerFooterRect( pos );
01099                                                                      if( r.isValid() ) {
01100                                                                          if( bStart )
01101                                                                              rect = r;
01102                                                                          else
01103                                                                              rect = rect.unite( r );
01104                                                                          bStart = false;
01105                                                                      }
01106                                                                  }
01107                                                              }
01108                                                              break;
01109         case KDChartEnums::AreaOutermost:
01110                                                              rect = _outermostRect;
01111                                                              break;
01112         case KDChartEnums::AreaInnermost:
01113                                                              rect = _innermostRect;
01114                                                              break;
01115         case KDChartEnums::AreasCustomBoxes:
01116                                                              allCustomBoxes = true;
01117                                                              break;
01118         case KDChartEnums::AreaChartDataRegion:
01119                                                              if( regions ) {
01120                                                                  KDChartDataRegion* current;
01121                                                                  for ( current = regions->first();
01122                                                                          current != 0;
01123                                                                          current =  regions->next() ) {
01124                                                                      if (    current->row == dataRow
01125                                                                              && current->col == dataCol
01126                                                                              //
01127                                                                              // the line below prepared for true 3-dimensional data charts
01128                                                                              //
01129                                                                              /* && current->region.thirdDimension == data3rd */ ) {
01130                                                                          rect = current->rect();
01131                                                                          break;
01132                                                                      }
01133                                                                  }
01134                                                              }
01135                                                              break;
01136         case KDChartEnums::AreaUNKNOWN:
01137                                                              break;
01138 
01139         default: {
01140                      uint maskBASE = KDChartEnums::AreaBASEMask & area;
01141                      pos = area - maskBASE;
01142                      if ( KDChartEnums::AreaAxisBASE == maskBASE ) {
01143                          rect = params()->axisParams( pos ).axisTrueAreaRect();
01144                      } else if ( KDChartEnums::AreaHdFtBASE == maskBASE ) {
01145                          rect = params()->headerFooterRect( pos );
01146                      } else if ( KDChartEnums::AreaCustomBoxesBASE == maskBASE ) {
01147                          const KDChartCustomBox * box = params()->customBox( pos );
01148                          if( box ) {
01149                              rect = box->trueRect( calculateAnchor( *box, regions ),
01150                                      _areaWidthP1000,
01151                                      _areaHeightP1000 );
01152                          }
01153                      }
01154                  }
01155     }
01156     return rect;
01157 }
01158 
01159 
01160 QPoint KDChartPainter::pointOnCircle( const QRect& rect, double angle )
01161 {
01162     // There are two ways of computing this: The simple, but slow one
01163     // is to use QPointArray.makeArc() and take the first point. The
01164     // more advanced, but faster one is to do the trigonometric
01165     // computionations ourselves. Since the comments in
01166     // QPointArray::makeArc() very often say that the code there is
01167     // "poor", we'd better do it outselves...
01168 
01169     double normAngle = angle / 16.0;
01170     double normAngleRad = DEGTORAD( normAngle );
01171     double cosAngle = cos( normAngleRad );
01172     double sinAngle = -sin( normAngleRad );
01173     double posX = floor( cosAngle * ( double ) rect.width() / 2.0 + 0.5 );
01174     double posY = floor( sinAngle * ( double ) rect.height() / 2.0 + 0.5 );
01175     return QPoint( static_cast<int>(posX) + rect.center().x(),
01176                    static_cast<int>(posY) + rect.center().y() );
01177 
01178 }
01179 
01180 void KDChartPainter::makeArc( QPointArray& points,
01181                               const QRect& rect,
01182                               double startAngle, double angles )
01183 {
01184     double endAngle = startAngle + angles;
01185     int rCX = rect.center().x();
01186     int rCY = rect.center().y();
01187     double rWid2 = ( double ) rect.width() / 2.0;
01188     double rHig2 = ( double ) rect.height() / 2.0;
01189     int numSteps = static_cast<int>(angles);
01190     if( floor( angles ) < angles )
01191         ++numSteps;
01192     points.resize( numSteps );
01193     double angle = startAngle;
01194     if( angle < 0.0 )
01195         angle += 5760.0;
01196     else if( angle >= 5760.0 )
01197         angle -= 5760.0;
01198     for(int i = 0; i < numSteps; ++i){
01199         double normAngle = angle / 16.0;
01200         double normAngleRad = DEGTORAD( normAngle );
01201         double cosAngle = cos( normAngleRad );
01202         double sinAngle = -sin( normAngleRad );
01203         double posX = floor( cosAngle * rWid2 + 0.5 );
01204         double posY = floor( sinAngle * rHig2 + 0.5 );
01205         points[i] = QPoint( ( int ) posX + rCX,
01206                             ( int ) posY + rCY );
01207         if( i+1 >= numSteps-1 )
01208             angle = endAngle; // the very last step width may be smaller than 1.0
01209         else
01210             angle += 1.0;
01211         if( angle >= 5760.0 )
01212             angle -= 5760.0;
01213     }
01214 }
01215 
01225 void KDChartPainter::paintAxes( QPainter* /*painter*/, KDChartTableDataBase* /*data*/ )
01226 {
01227     // This method intentionally left blank.
01228 }
01229 
01230 
01231 int KDChartPainter::legendTitleVertGap() const
01232 {
01233     return _legendTitleHeight
01234            + static_cast < int > ( _legendTitleMetricsHeight * 0.20 );
01235 }
01236 
01237 
01238 QFont KDChartPainter::trueLegendFont() const
01239 {
01240     QFont trueFont = params()->legendFont();
01241     if ( params()->legendFontUseRelSize() ) {
01242         const double averageValueP1000 = QMIN(_areaWidthP1000, _areaHeightP1000);//( _areaWidthP1000 + _areaHeightP1000 ) / 2.0;
01243         trueFont.setPixelSize(
01244             static_cast < int > ( params()->legendFontRelSize() * averageValueP1000 ) );
01245     }
01246     return trueFont;
01247 }
01248 
01249 
01255 void KDChartPainter::calculateHorizontalLegendSize( QPainter* painter,
01256                                                     QSize& size,
01257                                                     bool& legendNewLinesStartAtLeft ) const
01258 {
01259  
01260   legendNewLinesStartAtLeft = false;
01261   QRect legendRect( _legendRect );
01262   /*
01263    * Pending Michel reset the left side before calculating 
01264    *the new legend position calculation
01265    *otherwise we occasionally reach the edge and get a wrong 
01266    *result
01267    */
01268 
01269   legendRect.setLeft( _innermostRect.left() );
01270   
01271     const int em2 = 2 * _legendEMSpace;
01272     const int em4 = 4 * _legendEMSpace;
01273     const int emDiv2 = static_cast < int > ( _legendEMSpace / 2.0 );
01274 
01275     const int xposHori0 = legendRect.left() + _legendEMSpace;
01276     
01277     int xpos = xposHori0;
01278 
01279     int ypos = legendRect.top() + emDiv2;
01280 
01281     // first paint the title, if any
01282     if( _legendTitle )
01283         xpos += _legendTitleWidth + em4;
01284 
01285     int maxX = _legendTitleWidth + _legendEMSpace;
01286 
01287     // save the x position: here start the item texts if in horizontal mode
01288     int xposHori1 = xpos;
01289 
01290     // add the space of the box plus the space between the box and the text
01291     int x2 = xpos + em2;
01292 
01293     // loop over all the datasets, each one has one row in the legend
01294     // if its data are to be used in at least one of the charts drawn
01295     // *but* only if there is a legend text for it!
01296     const int rightEdge = _innermostRect.right()-_legendEMSpace;
01297     bool bFirstLFWithTitle = _legendTitle;
01298     painter->setFont( trueLegendFont() );
01299     QFontMetrics txtMetrics( painter->fontMetrics() );
01300     int dataset;
01301     for ( dataset = 0; dataset < _numLegendTexts; ++dataset ) {
01302         /*
01303            if( KDChartParams::DataEntry == params()->chartSourceMode( dataset ) ) {
01304            */
01305         if( !_legendTexts[ dataset ].isEmpty() ){
01306             int txtWidth = txtMetrics.width( _legendTexts[ dataset ] ) + 1;
01307             if( x2 + txtWidth > rightEdge ){
01308                 if( xposHori1 + em2 + txtWidth > rightEdge){
01309                     xposHori1 = xposHori0;
01310                     legendNewLinesStartAtLeft = true;
01311                 }
01312                 xpos = xposHori1;
01313                 x2   = xpos + em2;
01314                 ypos += bFirstLFWithTitle ? legendTitleVertGap() : _legendSpacing;
01315                 bFirstLFWithTitle = false;
01316             }
01317             maxX = QMAX(maxX, x2+txtWidth+_legendEMSpace);
01318             
01319             xpos += txtWidth + em4;
01320             x2   += txtWidth + em4;
01321         }
01322     }
01323     if( bFirstLFWithTitle ) 
01324         ypos += _legendTitleHeight;
01325     else 
01326         ypos += txtMetrics.height();
01327     
01328     size.setWidth(  maxX - legendRect.left() );
01329     size.setHeight( ypos + emDiv2 - _legendRect.top()  );
01330 }
01331 
01332 
01333 bool KDChartPainter::mustDrawVerticalLegend() const
01334 {
01335     return
01336         params()->legendOrientation() == Qt::Vertical ||
01337         params()->legendPosition() == KDChartParams::LegendLeft ||
01338         params()->legendPosition() == KDChartParams::LegendRight ||
01339         params()->legendPosition() == KDChartParams::LegendTopLeft ||
01340         params()->legendPosition() == KDChartParams::LegendTopLeftLeft ||
01341         params()->legendPosition() == KDChartParams::LegendTopRight ||
01342         params()->legendPosition() == KDChartParams::LegendTopRightRight ||
01343         params()->legendPosition() == KDChartParams::LegendBottomLeft ||
01344         params()->legendPosition() == KDChartParams::LegendBottomLeftLeft ||
01345         params()->legendPosition() == KDChartParams::LegendBottomRight ||
01346         params()->legendPosition() == KDChartParams::LegendBottomRightRight;
01347 }
01348 
01349 
01358 void KDChartPainter::paintLegend( QPainter* painter,
01359         KDChartTableDataBase* /*data*/ )
01360 {
01361     if ( params()->legendPosition() == KDChartParams::NoLegend ) 
01362         return ; // do not draw legend
01363 
01364     const bool bVertical = mustDrawVerticalLegend();
01365     painter->save();
01366 
01367     
01368     bool bFrameFound;
01369     params()->frameSettings( KDChartEnums::AreaLegend, bFrameFound );
01370 
01371     // start out with a rectangle around the legend
01372     //painter->setPen( QPen( Qt::black, 1 ) );
01373     //painter->setBrush( QBrush::NoBrush );
01374     //Pending Michel: let us paint the frame at the end of the drawmarker 
01375     //and draw text process, in case we need to resize it then    
01376     /*
01377     if( !bFrameFound ) {
01378       painter->drawRect( _legendRect );
01379     }
01380     */
01381     //qDebug("B:  _legendRect:\n          %i,%i\n          %i,%i", _legendRect.left(),_legendRect.top(),_legendRect.right(),_legendRect.bottom() );
01382     //qDebug("B: legendArea():\n          %i,%i\n          %i,%i\n", _params->legendArea().left(),_params->legendArea().top(),_params->legendArea().right(),_params->legendArea().bottom() );
01383 
01384     const int em2 = 2 * _legendEMSpace;
01385     const int em4 = 4 * _legendEMSpace;
01386     const int emDiv2 = static_cast < int > ( _legendEMSpace / 2.0 );
01387 
01388     const int xposHori0 = _legendRect.left() + _legendEMSpace;
01389 
01390     int xpos = xposHori0;
01391 
01392     int ypos = _legendRect.top() + emDiv2;
01393     
01394  
01395        
01396 
01397     // first paint the title, if any
01398     if( _legendTitle ) {
01399         painter->setFont( params()->legendTitleFont() );
01400         _legendTitle->draw( painter,
01401                             xpos,
01402                             ypos,
01403                             QRegion( xpos,
01404                                      ypos ,
01405                                      _legendTitleWidth,
01406                                      _legendTitleHeight ),
01407                             params()->legendTitleTextColor() );
01408         if( bVertical ) 
01409             ypos += legendTitleVertGap();
01410     
01411         else 
01412             xpos += _legendTitleWidth + em4;
01413     
01414     }
01415 
01416     // save the x position: here start the item texts if in horizontal mode
01417     const int xposHori1 = _legendNewLinesStartAtLeft ? xposHori0 : xpos;
01418 
01419     // add the space of the box plus the space between the box and the text
01420     int x2 = xpos + em2;
01421 
01422     // loop over all the datasets, each one has one row in the legend
01423     // if its data are to be used in at least one of the charts drawn
01424     // *but* only if there is a legend text for it!
01425     const int rightEdge = _legendRect.right();
01426     bool bFirstLF = true;
01427     painter->setFont( trueLegendFont() );
01428     QFontMetrics txtMetrics( painter->fontMetrics() );
01429     int dataset;
01430     for ( dataset = 0; dataset < _numLegendTexts; ++dataset ) {
01431         /*
01432            if( KDChartParams::DataEntry == params()->chartSourceMode( dataset ) ) {
01433            */
01434         if( !_legendTexts[ dataset ].isEmpty() ){
01435             int txtWidth = txtMetrics.width( _legendTexts[ dataset ] ) + 1;
01436   
01437             // calculate the width and height for the marker, relative to the font height
01438             // we need the legend text to be aligned to the marker
01439             // substract a gap. 
01440         int legHeight = static_cast <int>((txtMetrics.height() - (int)(txtMetrics.height() * 0.1))*0.85);
01441         
01442         //int legHeight = static_cast <int> (_legendRect.height()*0.8);
01443         
01444             if( !bVertical && x2 + txtWidth >= rightEdge ){
01445           _legendRect.setHeight( _legendRect.height() + _legendSpacing );
01446                 xpos = xposHori1;
01447                 x2   = xpos + em2;
01448                 ypos += bFirstLF ? legendTitleVertGap() : _legendSpacing;
01449                 bFirstLF = false;
01450             }
01451             painter->setBrush( QBrush( params()->dataColor( dataset ),
01452                                QBrush::SolidPattern ) );
01453 
01454             if( params()->legendShowLines() ){
01455                 painter->setPen( QPen( params()->dataColor( dataset ), 2,
01456                                  params()->lineStyle( dataset ) ) );
01457                 painter->drawLine(
01458                     xpos - emDiv2,
01459                     ypos + emDiv2 + 1,
01460                     xpos + static_cast < int > ( _legendEMSpace * 1.5 ),
01461                     ypos + emDiv2 + 1);
01462          }
01463 
01464             /*
01465             // draw marker if we have a marker, OR we have no marker and no line
01466             if ( params()->lineMarker() ||
01467                  params()->lineStyle( dataset ) == Qt::NoPen )*/           
01468         drawMarker( painter,
01469             params(),
01470             _areaWidthP1000, _areaHeightP1000,
01471             _dataRect.x(), _dataRect.y(),
01472             params()->lineMarker()
01473             ? params()->lineMarkerStyle( dataset )
01474             : KDChartParams::LineMarkerSquare,
01475             params()->dataColor(dataset),
01476             QPoint(xpos + emDiv2,
01477                    bVertical? ypos + emDiv2: !bFirstLF ?ypos + _legendSpacing:_legendRect.center().y() - (legHeight / 2 ))/*ypos + emDiv2*/ ,
01478             0, 0, 0, NULL,  // these params are deadweight here. TODO
01479                 &legHeight /*&_legendEMSpace*/, &legHeight /*&_legendEMSpace*/,
01480             bVertical ? Qt::AlignCenter : (Qt::AlignTop | Qt::AlignHCenter) );  
01481         /*       
01482         painter->drawText(_legendRect.topLeft(), "topLeft" );
01483             painter->drawText(_legendRect.topLeft().x(), _legendRect.center().y(), "center" );
01484            painter->drawText(_legendRect.bottomLeft(), "bottomLeft" );
01485         */
01486             /* old:
01487             painter->setPen( Qt::black );
01488             painter->drawRect( xpos,
01489                                ypos + ( _legendHeight - _legendEMSpace ) / 2,
01490                                _legendEMSpace,
01491                                _legendEMSpace );
01492             */         
01493             painter->setPen( params()->legendTextColor() );
01494             painter->drawText( x2,
01495                                bVertical ?  ypos : !bFirstLF ? ypos + _legendSpacing : _legendRect.center().y() - (legHeight / 2 ),
01496                                txtWidth,
01497                                legHeight,
01498                                Qt::AlignLeft | Qt::AlignVCenter,
01499                                _legendTexts[ dataset ] );
01500 
01501             if( bVertical )
01502                 ypos += _legendSpacing;
01503             else {
01504                 xpos += txtWidth + em4;
01505                 x2   += txtWidth + em4;
01506             }
01507         }
01508     }
01509 
01510     painter->setPen( QPen( Qt::black, 1 ) );
01511     painter->setBrush( QBrush::NoBrush );
01512     if( !bFrameFound )
01513       painter->drawRect( _legendRect );
01514     
01515 
01516     painter->restore();
01517 }
01518 
01519 
01520 void adjustFromTo(int& from, int& to)
01521 {
01522     if( abs(from) > abs(to) ){
01523         int n = from;
01524         from = to;
01525         to = n;
01526     }
01527 }
01528 
01529 
01530 bool KDChartPainter::axesOverlapping( int axis1, int axis2 )
01531 {
01532     KDChartAxisParams::AxisPos basicPos = KDChartAxisParams::basicAxisPos( axis1 );
01533     if( basicPos != KDChartAxisParams::basicAxisPos( axis2 ) )
01534         // Only axes of the same position can be compared. (e.g. 2 left axes)
01535         return false;
01536 
01537     if( KDChartAxisParams::AxisPosLeft  != basicPos &&
01538             KDChartAxisParams::AxisPosRight != basicPos )
01539         // Available space usage only possible for (vertical) ordinate axes.
01540         return false;
01541 
01542     int f1 = params()->axisParams( axis1 ).axisUseAvailableSpaceFrom();
01543     int t1 = params()->axisParams( axis1 ).axisUseAvailableSpaceTo();
01544     int f2 = params()->axisParams( axis2 ).axisUseAvailableSpaceFrom();
01545     int t2 = params()->axisParams( axis2 ).axisUseAvailableSpaceTo();
01546     adjustFromTo(f1,t1);
01547     adjustFromTo(f2,t2);
01548     // give these values some meaning
01549     // to be able to compare mixed fixed and/or relative figures:
01550     const double guessedAxisHeightP1000 = _areaHeightP1000 * 80.0 / 100.0;
01551     if(f1 < 0) f1 = static_cast < int > ( f1 * -guessedAxisHeightP1000 );
01552     if(t1 < 0) t1 = static_cast < int > ( t1 * -guessedAxisHeightP1000 );
01553     if(f2 < 0) f2 = static_cast < int > ( f2 * -guessedAxisHeightP1000 );
01554     if(t2 < 0) t2 = static_cast < int > ( t2 * -guessedAxisHeightP1000 );
01555     const bool res = (f1 >= f2 && f1 < t2) || (f2 >= f1 && f2 < t1);
01556     return res;
01557 }
01558 
01559 
01560 void internSetAxisArea( KDChartParams* params, int axis,
01561         int x0, int y0, int w0, int h0 )
01562 {
01563     // axis may never occupy more than 1000 per mille of the available space
01564     int nFrom = QMAX(-1000, params->axisParams( axis ).axisUseAvailableSpaceFrom());
01565     int nTo   = QMAX(-1000, params->axisParams( axis ).axisUseAvailableSpaceTo());
01566     adjustFromTo(nFrom,nTo);
01567 
01568     KDChartAxisParams::AxisPos basicPos = KDChartAxisParams::basicAxisPos( axis );
01569     int x, y, w, h;
01570     if( KDChartAxisParams::AxisPosBottom == basicPos ||
01571             KDChartAxisParams::AxisPosTop    == basicPos ){
01572 
01573         // Note: available space usage is ignored for abscissa axes!
01574         //
01575         //if( nFrom < 0 )
01576         //  x = x0 + w0*nFrom/-1000;
01577         //else
01578         //  x = x0 +    nFrom;
01579         //y = y0;
01580         //if( nTo < 0 )
01581         //  w = x0 + w0*nTo/-1000 - x;
01582         //else
01583         //  w = x0 +    nTo       - x;
01584         //h = h0;
01585 
01586         x = x0;
01587         y = y0;
01588         w = w0;
01589         h = h0;
01590 
01591     }else{
01592         x = x0;
01593         if( nTo < 0 )
01594             y = y0 + h0 - h0*nTo/-1000;
01595         else
01596             y = y0 + h0 -    nTo;
01597         w = w0;
01598         if( nFrom < 0 )
01599             h = y0 + h0 - h0*nFrom/-1000 - y;
01600         else
01601             h = y0 + h0 -    nFrom       - y;
01602     }
01603 
01604     params->setAxisArea( axis,
01605             QRect( x,
01606                 y,
01607                 w,
01608                 h ) );
01609 }
01610 
01611 
01620 void KDChartPainter::paintHeaderFooter( QPainter* painter,
01621         KDChartTableDataBase* /*data*/ )
01622 {
01623     const double averageValueP1000 = QMIN(_areaWidthP1000, _areaHeightP1000);//( _areaWidthP1000 + _areaHeightP1000 ) / 2.0;
01624 
01625     painter->save();
01626 
01627     for( int iHdFt  = KDChartParams::HdFtPosSTART;
01628             iHdFt <= KDChartParams::HdFtPosEND;  ++iHdFt ){
01629         QString txt( params()->headerFooterText( iHdFt ) );
01630         if ( !txt.isEmpty() ) {
01631             QFont actFont( params()->headerFooterFont( iHdFt ) );
01632             if ( params()->headerFooterFontUseRelSize( iHdFt ) )
01633                 actFont.setPixelSize( static_cast < int > (
01634                     params()->headerFooterFontRelSize( iHdFt ) * averageValueP1000 ) );
01635             painter->setPen( params()->headerFooterColor( iHdFt ) );
01636             painter->setFont( actFont );
01637             // Note: The alignment flags used here match the rect calculation
01638             //       done in KDChartPainter::setupGeometry().
01639             //       AlignTop is done to ensure that the hd/ft texts of the same
01640             //       group (e.g. Hd2L and Hd2 and Hd2R) have the same baselines.
01641 
01642             QRect rect( params()->headerFooterRect( iHdFt ) );
01643             int dXY = iHdFt < KDChartParams::HdFtPosFootersSTART
01644                 ? _hdLeading/3
01645                 : _ftLeading/3;
01646             rect.moveBy(dXY, dXY);
01647             rect.setWidth(  rect.width() -2*dXY +1 );
01648             rect.setHeight( rect.height()-2*dXY +1 );
01649             painter->drawText( rect,
01650                     Qt::AlignLeft | Qt::AlignTop | Qt::SingleLine,
01651                     txt );
01652         }
01653     }
01654     painter->restore();
01655 }
01656 
01657 
01658 int KDChartPainter::calculateHdFtRects(
01659         QPainter* painter,
01660         double averageValueP1000,
01661         int  xposLeft,
01662         int  xposRight,
01663         bool bHeader,
01664         int& yposTop,
01665         int& yposBottom )
01666 {
01667     int& leading = (bHeader ? _hdLeading : _ftLeading);
01668     leading = 0; // pixels between the header (or footer, resp.) text
01669     // and the border of the respective Hd/Ft area
01670 
01671     const int rangesCnt = 3;
01672     const int ranges[ rangesCnt ]
01673         = { bHeader ? KDChartParams::HdFtPosHeaders0START : KDChartParams::HdFtPosFooters0START,
01674             bHeader ? KDChartParams::HdFtPosHeaders1START : KDChartParams::HdFtPosFooters1START,
01675             bHeader ? KDChartParams::HdFtPosHeaders2START : KDChartParams::HdFtPosFooters2START };
01676     const int rangeSize = 3;
01677     QFontMetrics* metrics[rangesCnt * rangeSize];
01678 
01679     int i;
01680     for( i = 0; i < rangesCnt*rangeSize; ++i )
01681         metrics[ i ] = 0;
01682 
01683     int iRange;
01684     int iHdFt;
01685     for( iRange = 0; iRange < rangesCnt; ++iRange ){
01686         for( i = 0; i < rangeSize; ++i ){
01687             iHdFt = ranges[iRange] + i;
01688             QString txt( params()->headerFooterText( iHdFt ) );
01689             if ( !txt.isEmpty() ) {
01690                 QFont actFont( params()->headerFooterFont( iHdFt ) );
01691                 if ( params()->headerFooterFontUseRelSize( iHdFt ) ) {
01692                     actFont.setPixelSize( static_cast < int > (
01693                             params()->headerFooterFontRelSize( iHdFt ) * averageValueP1000 ) );
01694                 }
01695                 painter->setFont( actFont );
01696                 metrics[ iRange*rangeSize + i ] = new QFontMetrics( painter->fontMetrics() );
01697                 leading = QMAX( leading, metrics[ iRange*rangeSize + i ]->lineSpacing() / 2 );
01698             }
01699         }
01700     }
01701 
01702     if( bHeader )
01703         ++yposTop;//yposTop += leading/3;
01704     //else
01705     //--yposBottom;//yposBottom -= leading/3;
01706 
01707     int leading23 = leading*2/3 +1;
01708 
01709     for( iRange =
01710             bHeader ? 0                  : rangesCnt-1;
01711             bHeader ? iRange < rangesCnt : iRange >= 0;
01712             bHeader ? ++iRange           : --iRange ){
01713         // Ascents and heights must be looked at to ensure that the hd/ft texts
01714         // of the same group (e.g. Hd2L and Hd2 and Hd2R) have equal baselines.
01715         int ascents[rangeSize];
01716         int heights[rangeSize];
01717         int widths[ rangeSize];
01718         int maxAscent = 0;
01719         int maxHeight = 0;
01720         for( i = 0; i < rangeSize; ++i ){
01721             iHdFt = ranges[iRange] + i;
01722             if ( metrics[ iRange*rangeSize + i ] ) {
01723                 QFontMetrics& m = *metrics[ iRange*rangeSize + i ];
01724                 ascents[i] = m.ascent();
01725                 heights[i] = m.height() + leading23;
01726 
01727                 // the following adds two spaces to work around a bug in Qt:
01728                 // bounding rect sometimes is too small, if using italicized fonts
01729                 widths[ i] = m.boundingRect( params()->headerFooterText( iHdFt )+"  " ).width() + leading23;
01730 
01731                 maxAscent = QMAX( maxAscent, ascents[i] );
01732                 maxHeight = QMAX( maxHeight, heights[i] );
01733             }else{
01734                 heights[i] = 0;
01735             }
01736         }
01737 
01738         if( !bHeader )
01739             yposBottom -= maxHeight;
01740 
01741         for( i = 0; i < rangeSize; ++i ){
01742             if( heights[i] ){
01743                 iHdFt = ranges[iRange] + i;
01744                 int x1;
01745                 switch( i ){
01746                     case 1:  x1 = xposLeft+1;
01747                             break;
01748                     case 2:  x1 = xposRight-widths[i]-1;
01749                             break;
01750                     default: x1 = xposLeft + (xposRight-xposLeft-widths[i]) / 2;
01751                 }
01752                 ((KDChartParams*)params())->__internalStoreHdFtRect( iHdFt,
01753                                                                     QRect( x1,
01754                                                                         bHeader
01755                                                                         ? yposTop    + maxAscent - ascents[i]
01756                                                                         : yposBottom + maxAscent - ascents[i],
01757                                                                         widths[ i],
01758                                                                         heights[i] - 1 ) );
01759             }
01760         }
01761         if( bHeader )
01762             yposTop    += leading + maxHeight;
01763         else
01764             yposBottom -= leading;
01765     }
01766     for( i = 0; i < rangesCnt*rangeSize; ++i )
01767         if( metrics[ i ] )
01768             delete metrics[ i ];
01769     return leading;
01770 }
01771 
01772 
01773 
01774 void KDChartPainter::findChartDatasets( KDChartTableDataBase* data,
01775                                         bool paint2nd,
01776                                         uint chart,
01777                                         uint& chartDatasetStart,
01778                                         uint& chartDatasetEnd )
01779 {
01780     if(    params()->neverUsedSetChartSourceMode()
01781         || !params()->findDatasets( KDChartParams::DataEntry,
01782                                     KDChartParams::ExtraLinesAnchor,
01783                                     chartDatasetStart,
01784                                     chartDatasetEnd,
01785                                     chart ) ) {
01786         uint maxRow, maxRowMinus1;
01787         switch ( data->usedRows() ) {
01788             case 0:
01789                 return ;
01790             case 1:
01791                 maxRow = 0;
01792                 maxRowMinus1 = 0;
01793                 break;
01794             default:
01795                 maxRow = data->usedRows() - 1;
01796                 maxRowMinus1 = maxRow;
01797         }
01798         chartDatasetStart = paint2nd ? maxRow : 0;
01799         chartDatasetEnd = paint2nd
01800                         ? maxRow
01801                         : (   ( KDChartParams::NoType == params()->additionalChartType() )
01802                             ? maxRow
01803                             : maxRowMinus1 );
01804 
01805     }
01806 }
01807 
01808 
01809 void KDChartPainter::calculateAllAxesRects(
01810         QPainter* painter,
01811         bool finalPrecision,
01812         KDChartTableDataBase* data
01813         )
01814 {
01815     const bool bIsAreaChart = KDChartParams::Area == params()->chartType();
01816     const bool bMultiRows = KDChartParams::Bar == params()->chartType() &&
01817         KDChartParams::BarMultiRows == params()->barChartSubType();
01818 
01819     const int trueWidth  = _outermostRect.width();
01820     const int trueHeight = _outermostRect.height();
01821     const double averageValueP1000 = QMIN(_areaWidthP1000, _areaHeightP1000);//( _areaWidthP1000 + _areaHeightP1000 ) / 2.0;
01822 
01823     // store the axes' 0 offsets
01824     int nAxesLeft0   = _axesRect.left() - _outermostRect.left();
01825     int nAxesRight0  = _outermostRect.right() - _axesRect.right();
01826     int nAxesTop0    = _axesRect.top() - _outermostRect.top();
01827     int nAxesBottom0 = _outermostRect.bottom() - _axesRect.bottom();
01828     if( bMultiRows ){
01829         uint chartDatasetStart, chartDatasetEnd;
01830         findChartDatasets( data, false, 0, chartDatasetStart, chartDatasetEnd );
01831         const int datasets = chartDatasetEnd - chartDatasetStart + 1;
01832         int numValues = 0;
01833         if ( params()->numValues() != -1 )
01834             numValues = params()->numValues();
01835         else
01836             numValues = data->usedCols();
01837         if( datasets ){
01838             const int additionalGapWidth = static_cast < int > ( 1.0 * _axesRect.width() / (9.75*numValues + 4.0*datasets) * 4.0*datasets );
01839             nAxesRight0 += additionalGapWidth;
01840             nAxesTop0   += static_cast < int > ( additionalGapWidth * 0.52 );
01841             //const double widthFactor = additionalGapWidth*1.0 / _axesRect.width();
01842             //nAxesTop0   += static_cast < int > ( _axesRect.height() * widthFactor );
01843         }
01844     }
01845     // store the distances to be added to the axes' 0 offsets
01846     int nAxesLeftADD  =0;
01847     int nAxesRightADD =0;
01848     int nAxesTopADD   =0;
01849     int nAxesBottomADD=0;
01850 
01851     // determine whether the axes widths of one side should be added
01852     // or their maximum should be used
01853     bool bAddLeft = axesOverlapping( KDChartAxisParams::AxisPosLeft,
01854             KDChartAxisParams::AxisPosLeft2 );
01855     bool bAddRight = axesOverlapping( KDChartAxisParams::AxisPosRight,
01856             KDChartAxisParams::AxisPosRight2 );
01857     bool bAddTop = axesOverlapping( KDChartAxisParams::AxisPosTop,
01858             KDChartAxisParams::AxisPosTop2 );
01859     bool bAddBottom = axesOverlapping( KDChartAxisParams::AxisPosBottom,
01860             KDChartAxisParams::AxisPosBottom2 );
01861     // iterate over all axes
01862     uint iAxis;
01863     for ( iAxis = 0; iAxis < KDCHART_MAX_AXES; ++iAxis ) {
01864 
01865         const KDChartAxisParams& para = params()->axisParams( iAxis );
01866         int areaSize = 0;
01867 
01868         if ( para.axisVisible()
01869                 && KDChartAxisParams::AxisTypeUnknown != para.axisType() ) {
01870 
01871             const KDChartAxisParams::AxisPos
01872                 basicPos( KDChartAxisParams::basicAxisPos( iAxis ) );
01873 
01874             int areaMin = para.axisAreaMin();
01875             int areaMax = para.axisAreaMax();
01876             if ( 0 > areaMin )
01877                 areaMin = static_cast < int > ( -1.0 * averageValueP1000 * areaMin );
01878             if ( 0 > areaMax )
01879                 areaMax = static_cast < int > ( -1.0 * averageValueP1000 * areaMax );
01880 
01881             // make sure areaMin will not be too small
01882             // for the label texts
01883             switch ( basicPos ) {
01884                 case KDChartAxisParams::AxisPosBottom:
01885                 case KDChartAxisParams::AxisPosTop:
01886                     if ( para.axisLabelsVisible() ) {
01887                         int fntHeight;
01888                         if ( para.axisLabelsFontUseRelSize() )
01889                             fntHeight = static_cast < int > (
01890                                     para.axisLabelsFontRelSize()
01891                                     * averageValueP1000 );
01892                         else {
01893                             painter->setFont( para.axisLabelsFont() );
01894                             QFontMetrics metrics( painter->fontMetrics() );
01895                             fntHeight = metrics.height();
01896                         }
01897                         // adjust text height in case of formatted Date/Time values
01898                         uint dataDataset, dataDataset2;
01899                         if( !params()->findDataset( KDChartParams::DataEntry,
01900                                                     dataDataset,
01901                                                     dataDataset2,
01902                                                     KDCHART_ALL_CHARTS ) ) {
01903                             qDebug( "IMPLEMENTATION ERROR: findDataset( DataEntry, ... ) should *always* return true. (a)" );
01904                             dataDataset = KDCHART_ALL_DATASETS;
01905                         }
01906                         QVariant::Type valType = QVariant::Invalid;
01907                         const bool dataCellsHaveSeveralCoordinates =
01908                             (KDCHART_ALL_DATASETS == dataDataset)
01909                             ? data->cellsHaveSeveralCoordinates( &valType )
01910                             : data->cellsHaveSeveralCoordinates( dataDataset, dataDataset2, &valType );
01911                         QString format( para.axisLabelsDateTimeFormat() );
01912                         if(    dataCellsHaveSeveralCoordinates
01913                             && QVariant::DateTime == valType ){
01914                             if( KDCHART_AXIS_LABELS_AUTO_DATETIME_FORMAT == format )
01915                                 areaMin = QMAX( areaMin, static_cast < int > ( fntHeight * 6.75 ) );
01916                             else
01917                                 areaMin = QMAX( areaMin, fntHeight * ( 3 + format.contains("\n") ) );
01918                         }
01919                         else
01920                             areaMin = QMAX( areaMin, fntHeight * 3 );
01921                     }
01922                     break;
01923                 case KDChartAxisParams::AxisPosLeft:
01924                 case KDChartAxisParams::AxisPosRight:
01925                 default:
01926                     break;
01927             }
01928 
01929 
01930             switch ( para.axisAreaMode() ) {
01931                 case KDChartAxisParams::AxisAreaModeAutoSize:
01932                 {
01933                     areaSize = areaMin;
01934                     switch ( basicPos ) {
01935                         case KDChartAxisParams::AxisPosBottom:
01936                         case KDChartAxisParams::AxisPosTop:
01937                             break;
01938                         case KDChartAxisParams::AxisPosLeft:
01939                         case KDChartAxisParams::AxisPosRight:
01940                             if( finalPrecision ){
01941                                 internal__KDChart__CalcValues& cv = calcVal[iAxis];
01942                                 const int nUsableAxisWidth = static_cast < int > (cv.pTextsW);
01943                                 const KDChartAxisParams & para = params()->axisParams( iAxis );
01944                                 QFont axisLabelsFont( para.axisLabelsFont() );
01945                                 if ( para.axisLabelsFontUseRelSize() ) {
01946                                     axisLabelsFont.setPixelSize( static_cast < int > ( cv.nTxtHeight ) );
01947                                 }
01948                                 painter->setFont( para.axisLabelsFont() );
01949                                 QFontMetrics axisLabelsFontMetrics( painter->fontMetrics() );
01950                                 const int lenEM( axisLabelsFontMetrics.boundingRect("M").width() );
01951                                 const QStringList* labelTexts = para.axisLabelTexts();
01952                                 uint nLabels = ( 0 != labelTexts )
01953                                             ? labelTexts->count()
01954                                             : 0;
01955                                 int maxLabelsWidth = 0;
01956                                 for ( uint i = 0; i < nLabels; ++i )
01957                                     maxLabelsWidth =
01958                                         QMAX( maxLabelsWidth,
01959                                               axisLabelsFontMetrics.boundingRect(*labelTexts->at(i)).width() );
01960                                 if( nUsableAxisWidth < maxLabelsWidth )
01961                                     areaSize = maxLabelsWidth
01962                                              + (para.axisTrueAreaRect().width() - nUsableAxisWidth)
01963                                              + lenEM;
01964                             }
01965                             break;
01966                         default:
01967                             break;
01968                     }
01969                 }
01970                 break;
01971                 case KDChartAxisParams::AxisAreaModeMinMaxSize:
01972                 {
01973                     qDebug( "Sorry, not implemented: AxisAreaModeMinMaxSize" );
01974                 }
01975 
01976                 //
01977                 //
01978                 //   F E A T U R E   P L A N N E D   F O R   F U T U R E . . .
01979                 //
01980                 //
01981 
01982                 // break;
01983 
01984                 case KDChartAxisParams::AxisAreaModeFixedSize:
01985                 {
01986                     areaSize = areaMax ? QMIN( areaMin, areaMax ) : areaMin;
01987                 }
01988                 break;
01989             }
01990 
01991             switch ( basicPos ) {
01992                 case KDChartAxisParams::AxisPosBottom:
01993                     if( bAddBottom )
01994                         nAxesBottomADD += areaSize;
01995                     else
01996                         nAxesBottomADD = QMAX( nAxesBottomADD, areaSize );
01997                     break;
01998                 case KDChartAxisParams::AxisPosLeft:
01999                     if( bAddLeft )
02000                         nAxesLeftADD += areaSize;
02001                     else
02002                         nAxesLeftADD = QMAX( nAxesLeftADD, areaSize );
02003                     break;
02004                 case KDChartAxisParams::AxisPosTop:
02005                     if( bAddTop )
02006                         nAxesTopADD += areaSize;
02007                     else
02008                         nAxesTopADD = QMAX( nAxesTopADD, areaSize );
02009                     break;
02010                 case KDChartAxisParams::AxisPosRight:
02011                     if( bAddRight )
02012                         nAxesRightADD += areaSize;
02013                     else
02014                         nAxesRightADD = QMAX( nAxesRightADD, areaSize );
02015                     break;
02016                 default:
02017                     break;
02018             }
02019         }
02020         // Note: to prevent users from erroneously calling this
02021         //       function we do *not* provide a wrapper for it
02022         //       in the KDChartParams class but rather call it
02023         //       *directly* using a dirty typecast.
02024         ( ( KDChartAxisParams& ) para ).setAxisTrueAreaSize( areaSize );
02025     }
02026     int nMinDistance = static_cast < int > ( 30.0 * averageValueP1000 );
02027 
02028     int nAxesBottom = QMAX( nAxesBottom0 + nAxesBottomADD, nMinDistance );
02029 
02030     // for micro alignment with the X axis, we adjust the Y axis - but not for Area Charts:
02031     // otherwise the areas drawn would overwrite the Y axis line.
02032     int nAxesLeft   = QMAX( nAxesLeft0   + nAxesLeftADD,   nMinDistance )
02033                       - (bIsAreaChart ? 0 : 1);
02034 
02035     int nAxesTop    = QMAX( nAxesTop0    + nAxesTopADD,    nMinDistance );
02036 
02037     int nAxesRight  = QMAX( nAxesRight0  + nAxesRightADD,  nMinDistance );
02038 
02039     int nBottom  = params()->axisParams( KDChartAxisParams::AxisPosBottom ).axisTrueAreaSize();
02040     int nLeft    = params()->axisParams( KDChartAxisParams::AxisPosLeft ).axisTrueAreaSize();
02041     int nTop     = params()->axisParams( KDChartAxisParams::AxisPosTop ).axisTrueAreaSize();
02042     int nRight   = params()->axisParams( KDChartAxisParams::AxisPosRight ).axisTrueAreaSize();
02043     int nBottom2 = params()->axisParams( KDChartAxisParams::AxisPosBottom2 ).axisTrueAreaSize();
02044     int nLeft2   = params()->axisParams( KDChartAxisParams::AxisPosLeft2 ).axisTrueAreaSize();
02045     int nTop2    = params()->axisParams( KDChartAxisParams::AxisPosTop2 ).axisTrueAreaSize();
02046     int nRight2  = params()->axisParams( KDChartAxisParams::AxisPosRight2 ).axisTrueAreaSize();
02047 
02048     internSetAxisArea( _params,
02049             KDChartAxisParams::AxisPosBottom,
02050             _outermostRect.left() + nAxesLeft,
02051             _outermostRect.top()  + trueHeight - nAxesBottom,
02052             trueWidth - nAxesLeft - nAxesRight + 1,
02053             nBottom );
02054     internSetAxisArea( _params,
02055             KDChartAxisParams::AxisPosLeft,
02056             _outermostRect.left() + (bAddLeft ? nAxesLeft0 + nLeft2 : nAxesLeft0),
02057             _outermostRect.top()  + nAxesTop,
02058             nLeft,
02059             trueHeight - nAxesTop - nAxesBottom + 1 );
02060 
02061     internSetAxisArea( _params,
02062             KDChartAxisParams::AxisPosTop,
02063             _outermostRect.left() + nAxesLeft,
02064             _outermostRect.top()  + (bAddTop ? nAxesTop0 + nTop2 : nAxesTop0),
02065             trueWidth - nAxesLeft - nAxesRight + 1,
02066             nTop );
02067     internSetAxisArea( _params,
02068             KDChartAxisParams::AxisPosRight,
02069             _outermostRect.left() + trueWidth - nAxesRight,
02070             _outermostRect.top()  + nAxesTop,
02071             nRight,
02072             trueHeight - nAxesTop - nAxesBottom + 1 );
02073 
02074     internSetAxisArea( _params,
02075             KDChartAxisParams::AxisPosBottom2,
02076             _outermostRect.left() + nAxesLeft,
02077             _outermostRect.top()  + trueHeight - nAxesBottom + (bAddBottom ? nBottom : 0),
02078             trueWidth - nAxesLeft - nAxesRight + 1,
02079             nBottom2 );
02080     internSetAxisArea( _params,
02081             KDChartAxisParams::AxisPosLeft2,
02082             _outermostRect.left() + nAxesLeft0,
02083             _outermostRect.top()  + nAxesTop,
02084             nLeft2,
02085             trueHeight - nAxesTop - nAxesBottom + 1 );
02086 
02087     internSetAxisArea( _params,
02088             KDChartAxisParams::AxisPosTop2,
02089             _outermostRect.left() + nAxesLeft,
02090             _outermostRect.top()  + nAxesTop0,
02091             trueWidth - nAxesLeft - nAxesRight + 1,
02092             nTop2 );
02093     internSetAxisArea( _params,
02094             KDChartAxisParams::AxisPosRight2,
02095             _outermostRect.left() + trueWidth - nAxesRight + (bAddRight ? nRight : 0),
02096             _outermostRect.top()  + nAxesTop,
02097             nRight2,
02098             trueHeight - nAxesTop - nAxesBottom + 1 );
02099 
02100     _dataRect = QRect( _outermostRect.left() + nAxesLeft,
02101                        _outermostRect.top()  + nAxesTop,
02102                        trueWidth - nAxesLeft - nAxesRight + 1,
02103                        trueHeight - nAxesTop - nAxesBottom + 1 );
02104 }
02105 
02106 
02107 
02120 void KDChartPainter::setupGeometry( QPainter* painter,
02121                                     KDChartTableDataBase* data,
02122                                     const QRect& drawRect )
02123 {
02124   //qDebug("INVOKING: KDChartPainter::setupGeometry()");
02125     // avoid recursion from repaint() being called due to params() changed signals...
02126     const bool oldBlockSignalsState = params()->signalsBlocked();
02127     const_cast < KDChartParams* > ( params() )->blockSignals( true );
02128 
02129     _outermostRect = drawRect;
02130 
02131     int yposTop    = _outermostRect.topLeft().y();
02132     int xposLeft   = _outermostRect.topLeft().x();
02133     int yposBottom = _outermostRect.bottomRight().y();
02134     int xposRight  = _outermostRect.bottomRight().x();
02135 
02136     const int trueWidth  = _outermostRect.width();
02137     const int trueHeight = _outermostRect.height();
02138 
02139     // Temporary values used to calculate start values xposLeft, yposTop, xposRight, yposBottom.
02140     // They will be replaced immediately after these calculations.
02141     _areaWidthP1000    = trueWidth / 1000.0;
02142     _areaHeightP1000   = trueHeight / 1000.0;
02143 
02144 
02145     xposLeft   +=   0 < params()->globalLeadingLeft()
02146         ? params()->globalLeadingLeft()
02147         : static_cast < int > ( params()->globalLeadingLeft()  * -_areaWidthP1000 );
02148     yposTop    +=   0 < params()->globalLeadingTop()
02149         ? params()->globalLeadingTop()
02150         : static_cast < int > ( params()->globalLeadingTop()   * -_areaHeightP1000 );
02151     xposRight  -=   0 < params()->globalLeadingRight()
02152         ? params()->globalLeadingRight()
02153         : static_cast < int > ( params()->globalLeadingRight() * -_areaWidthP1000 );
02154     yposBottom -=   0 < params()->globalLeadingBottom()
02155         ? params()->globalLeadingBottom()
02156         : static_cast < int > ( params()->globalLeadingBottom()* -_areaHeightP1000 );
02157 
02158     _innermostRect = QRect( QPoint(xposLeft,  yposTop),
02159                             QPoint(xposRight, yposBottom) );
02160 
02161     _logicalWidth  = xposRight  - xposLeft;
02162     _logicalHeight = yposBottom - yposTop;
02163 
02164     // true values (having taken the global leadings into account)
02165     // to be used by all following functions
02166     _areaWidthP1000 =  _logicalWidth  / 1000.0;
02167     _areaHeightP1000 = _logicalHeight / 1000.0;
02168 
02169     double averageValueP1000 = QMIN(_areaWidthP1000, _areaHeightP1000);//( _areaWidthP1000 + _areaHeightP1000 ) / 2.0;
02170 
02171     // new code design:
02172     //        1. now min-header-leading is text height/2
02173     //        2. leading or legendSpacing (whichever is larger)
02174     //           will be added if legend is below the header(s)
02175     //        3. leading will be added between header and data area
02176     //           in case there is no top legend but grid is to be shown.
02177     int headerLineLeading = calculateHdFtRects(
02178             painter,
02179             averageValueP1000,
02180             xposLeft, xposRight,
02181             false,
02182             yposTop, yposBottom );
02183     calculateHdFtRects(
02184             painter,
02185             averageValueP1000,
02186             xposLeft, xposRight,
02187             true,
02188             yposTop, yposBottom );
02189 
02190     // Calculate legend position. First check whether there is going
02191     // to be a legend at all:
02192     if ( params()->legendPosition() != KDChartParams::NoLegend ) {
02193         // Now calculate the size needed for the legend
02194         findLegendTexts( data );
02195 
02196         bool hasLegendTitle = false;
02197         if ( !params()->legendTitleText().isEmpty() )
02198             hasLegendTitle = true;
02199 
02200         _legendTitleWidth = 0;
02201         if( _legendTitle )
02202             delete _legendTitle;
02203         _legendTitle = 0;
02204         if ( hasLegendTitle ) {
02205             QFont actLegendTitleFont = params()->legendTitleFont();
02206             if ( params()->legendTitleFontUseRelSize() ) {
02207                 int nTxtHeight =
02208                     static_cast < int > ( params()->legendTitleFontRelSize()
02209                                             * averageValueP1000 );
02210                 actLegendTitleFont.setPixelSize( nTxtHeight );
02211                 // qDebug("l-t-height %i",nTxtHeight);
02212                 const_cast < KDChartParams* > ( params() )->setLegendTitleFont( actLegendTitleFont, false );
02213             }
02214             painter->setFont( actLegendTitleFont );
02215             QFontMetrics legendTitleMetrics( painter->fontMetrics() );
02216             _legendTitleMetricsHeight = legendTitleMetrics.height();
02217 
02218             _legendTitle = new KDChartTextPiece( painter,
02219                                                  params()->legendTitleText(),
02220                                                  actLegendTitleFont );
02221             _legendTitleWidth = _legendTitle->width();
02222             _legendTitleHeight = _legendTitle->height();
02223             // qDebug("1. _legendTitleHeight %i",_legendTitleHeight);
02224         }
02225 
02226         painter->setFont( trueLegendFont() );
02227         QFontMetrics legendMetrics( painter->fontMetrics() );
02228         _legendSpacing = legendMetrics.lineSpacing();
02229         _legendHeight = legendMetrics.height();
02230         _legendLeading = legendMetrics.leading();
02231 
02232         _legendEMSpace = legendMetrics.width( 'M' );
02233 
02234         int sizeX = 0;
02235         int sizeY = 0;
02236 
02237         for ( int dataset = 0; dataset < _numLegendTexts; dataset++ ) {
02238             sizeX = QMAX( sizeX, legendMetrics.width( _legendTexts[ dataset ] ) );
02239             if( !_legendTexts[ dataset ].isEmpty() )
02240                 sizeY += _legendSpacing;
02241         }
02242         // add space below the legend's bottom line
02243         sizeY += _legendEMSpace - _legendLeading;
02244         // add space for the legend title if any was set
02245         if ( hasLegendTitle )
02246             sizeY += legendTitleVertGap();
02247 
02248         // assume 4 em spaces: before the color box, the color box, after the
02249         // color box and after the legend text
02250         sizeX += ( _legendEMSpace * 4 );
02251 
02252         // We cannot setup the title width earlier as the title does
02253         // not have a color box. The two em spaces are before the
02254         // color box (where the title does not start yet, it is
02255         // left-aligned with the color boxes) and after the title (to
02256         // have some space before the boundary line comes).
02257         sizeX = QMAX( sizeX, _legendTitleWidth + _legendEMSpace*2 );
02258 
02259     //qDebug("setupGeometry  mustDrawVerticalLegend: %s", mustDrawVerticalLegend() ? "YES":"NO ");
02260 
02261     //     PENDING Michel: do that after having calculated the position
02262         if( !mustDrawVerticalLegend() ){      
02263             QSize size;
02264             calculateHorizontalLegendSize( painter,
02265                                            size,
02266                                            _legendNewLinesStartAtLeft );
02267             sizeX = size.width();
02268             sizeY = size.height();
02269         }
02270 
02271         switch ( params()->legendPosition() ) {
02272             case KDChartParams::LegendTop:
02273                 if ( headerLineLeading )
02274                     yposTop += QMAX( (int)params()->legendSpacing(), headerLineLeading );
02275                 _legendRect = QRect( xposLeft + ( (xposRight-xposLeft) - sizeX ) / 2,
02276                                      yposTop, sizeX, sizeY );
02277                 yposTop = _legendRect.bottom() + params()->legendSpacing();
02278                 //qDebug("A:  _legendRect:\n%i,%i\n%i,%i", _legendRect.left(),_legendRect.top(),_legendRect.right(),_legendRect.bottom() ); 
02279                 break;
02280             case KDChartParams::LegendBottom:
02281                 if ( params()->showGrid() )
02282                     yposTop += headerLineLeading;
02283                 _legendRect = QRect( xposLeft + ( (xposRight-xposLeft) - sizeX ) / 2,
02284                                      yposBottom - sizeY,
02285                                      sizeX, sizeY );
02286                 yposBottom = _legendRect.top() - params()->legendSpacing();
02287                 break;
02288             case KDChartParams::LegendLeft:
02289                 if ( params()->showGrid() )
02290                     yposTop += headerLineLeading;
02291                 _legendRect = QRect( xposLeft + 1, ( yposBottom - yposTop - sizeY ) / 2 +
02292                                      yposTop,
02293                                      sizeX, sizeY );
02294                 xposLeft = _legendRect.right() + params()->legendSpacing();
02295                 break;
02296             case KDChartParams::LegendRight:
02297                 if ( params()->showGrid() )
02298                     yposTop += headerLineLeading;
02299                 _legendRect = QRect( xposRight - sizeX - 1,
02300                         ( yposBottom - yposTop - sizeY ) / 2 + yposTop,
02301                         sizeX, sizeY );
02302                 xposRight = _legendRect.left() - params()->legendSpacing();
02303                 break;
02304             case KDChartParams::LegendTopLeft:
02305                 if ( headerLineLeading )
02306                     yposTop += QMAX( (int)params()->legendSpacing(), headerLineLeading );
02307                 _legendRect = QRect( xposLeft + 1, yposTop, sizeX, sizeY );
02308                 yposTop = _legendRect.bottom() + params()->legendSpacing();
02309                 xposLeft = _legendRect.right() + params()->legendSpacing();
02310                 break;
02311             case KDChartParams::LegendTopLeftTop:
02312                 if ( headerLineLeading )
02313                     yposTop += QMAX( (int)params()->legendSpacing(), headerLineLeading );
02314                 _legendRect = QRect( xposLeft + 1, yposTop, sizeX, sizeY );
02315                 yposTop = _legendRect.bottom() + params()->legendSpacing();
02316                 break;
02317             case KDChartParams::LegendTopLeftLeft:
02318                 if ( headerLineLeading )
02319                     yposTop += QMAX( (int)params()->legendSpacing(), headerLineLeading );
02320                 _legendRect = QRect( xposLeft + 1, yposTop, sizeX, sizeY );
02321                 xposLeft = _legendRect.right() + params()->legendSpacing();
02322                 break;
02323             case KDChartParams::LegendTopRight:
02324                 if ( headerLineLeading )
02325                     yposTop += QMAX( (int)params()->legendSpacing(), headerLineLeading );
02326                 _legendRect = QRect( xposRight - sizeX - 1,
02327                         yposTop, sizeX, sizeY );
02328                 yposTop = _legendRect.bottom() + params()->legendSpacing();
02329                 xposRight = _legendRect.left() - params()->legendSpacing();
02330                 break;
02331             case KDChartParams::LegendTopRightTop:
02332                 if ( headerLineLeading )
02333                     yposTop += QMAX( (int)params()->legendSpacing(), headerLineLeading );
02334                 _legendRect = QRect( xposRight - sizeX - 1,
02335                         yposTop, sizeX, sizeY );
02336                 yposTop = _legendRect.bottom() + params()->legendSpacing();
02337                 break;
02338             case KDChartParams::LegendTopRightRight:
02339                 if ( headerLineLeading )
02340                     yposTop += QMAX( (int)params()->legendSpacing(), headerLineLeading );
02341                 _legendRect = QRect( xposRight - sizeX - 1,
02342                         yposTop, sizeX, sizeY );
02343                 xposRight = _legendRect.left() - params()->legendSpacing();
02344                 break;
02345             case KDChartParams::LegendBottomLeft:
02346                 if ( params()->showGrid() )
02347                     yposTop += headerLineLeading;
02348                 _legendRect = QRect( xposLeft + 1, yposBottom - sizeY, sizeX, sizeY );
02349                 yposBottom = _legendRect.top() - params()->legendSpacing();
02350                 xposLeft = _legendRect.right() + params()->legendSpacing();
02351                 break;
02352             case KDChartParams::LegendBottomLeftBottom:
02353                 if ( params()->showGrid() )
02354                     yposTop += headerLineLeading;
02355                 _legendRect = QRect( xposLeft + 1, yposBottom - sizeY, sizeX, sizeY );
02356                 yposBottom = _legendRect.top() - params()->legendSpacing();
02357                 break;
02358             case KDChartParams::LegendBottomLeftLeft:
02359                 if ( params()->showGrid() )
02360                     yposTop += headerLineLeading;
02361                 _legendRect = QRect( xposLeft + 1, yposBottom - sizeY, sizeX, sizeY );
02362                 xposLeft = _legendRect.right() + params()->legendSpacing();
02363                 break;
02364             case KDChartParams::LegendBottomRight:
02365                 if ( params()->showGrid() )
02366                     yposTop += headerLineLeading;
02367                 _legendRect = QRect( xposRight - sizeX - 1,
02368                         yposBottom - sizeY, sizeX, sizeY );
02369                 yposBottom = _legendRect.top() - params()->legendSpacing();
02370                 xposRight = _legendRect.left() - params()->legendSpacing();
02371                 break;
02372             case KDChartParams::LegendBottomRightBottom:
02373                 if ( params()->showGrid() )
02374                     yposTop += headerLineLeading;
02375                 _legendRect = QRect( xposRight - sizeX - 1,
02376                         yposBottom - sizeY, sizeX, sizeY );
02377                 yposBottom = _legendRect.top() - params()->legendSpacing();
02378                 break;
02379             case KDChartParams::LegendBottomRightRight:
02380                 if ( params()->showGrid() )
02381                     yposTop += headerLineLeading;
02382                 _legendRect = QRect( xposRight - sizeX - 1,
02383                         yposBottom - sizeY, sizeX, sizeY );
02384                 xposRight = _legendRect.left() - params()->legendSpacing();
02385                 break;
02386             default:
02387                 // Should not be able to happen
02388                 qDebug( "KDChart: Unknown legend position" );
02389         }
02390         _params->setLegendArea( _legendRect );
02391 
02392     }else{
02393       _params->setLegendArea( QRect(QPoint(0,0), QSize(0,0)) );
02394     }
02395      
02396 
02397     _axesRect = QRect( QPoint(xposLeft, yposTop), QPoint(xposRight, yposBottom) );
02398 
02399     // important rule: do *not* calculate axes areas for Polar charts!
02400     //                 (even if left and bottom axes might be set active)
02401     if( KDChartParams::Polar == params()->chartType() ) {
02402         _dataRect = _axesRect;
02403     } else {
02404         // 1st step: make a preliminary approximation of the axes sizes,
02405         //           as a basis of following label texts calculation
02406         calculateAllAxesRects( painter, false, data );
02407         // 2nd step: calculate all labels (preliminary data, will be
02408         //           overwritten by KDChartAxesPainter)
02409         //           to find out the longest possible axis labels
02410         double dblDummy;
02411         if( calculateAllAxesLabelTextsAndCalcValues(
02412                 painter,
02413                 data,
02414                 _areaWidthP1000,
02415                 _areaHeightP1000,
02416                 dblDummy ) )
02417             // 3rd step: calculate the _true_ axes rects based upon
02418             //           the preliminary axes labels
02419             calculateAllAxesRects( painter, true, data );
02420     }
02421     _params->setDataArea( _dataRect );
02422 
02423     const_cast < KDChartParams* > ( params() )->blockSignals( oldBlockSignalsState );
02424 }
02425 
02426 
02430 void KDChartPainter::findLegendTexts( KDChartTableDataBase* data )
02431 {
02432     uint dataset;
02433     QVariant vValY;
02434     switch ( params()->legendSource() ) {
02435         case KDChartParams::LegendManual: {
02436             // The easiest case: Take manually set strings, no matter whether any
02437             // have been set.
02438             _numLegendTexts = numLegendFallbackTexts( data );
02439             for ( dataset = 0; dataset < static_cast<uint>(_numLegendTexts); dataset++ )
02440                 _legendTexts[ dataset ] = params()->legendText( dataset );
02441             break;
02442         }
02443         case KDChartParams::LegendFirstColumn: {
02444             // Take whatever is in the first column
02445             for ( dataset = 0; dataset < data->usedRows(); dataset++ ){
02446                 if( data->cellCoord( dataset, 0, vValY, 1 ) ){
02447                     if( QVariant::String == vValY.type() )
02448                         _legendTexts[ dataset ] = vValY.toString();
02449                     else
02450                         _legendTexts[ dataset ] = "";
02451                 }
02452             }
02453             _numLegendTexts = data->usedRows();
02454             break;
02455         }
02456         case KDChartParams::LegendAutomatic: {
02457             // First, try the first row
02458             bool notfound = false;
02459             _numLegendTexts = numLegendFallbackTexts( data ); // assume this for cleaner
02460             // code below
02461             for ( dataset = 0; dataset < data->usedRows(); dataset++ ) {
02462                 if( data->cellCoord( dataset, 0, vValY, 1 ) ){
02463                     if( QVariant::String == vValY.type() )
02464                         _legendTexts[ dataset ] = vValY.toString();
02465                     else
02466                         _legendTexts[ dataset ] = "";
02467                     if( _legendTexts[ dataset ].isEmpty() ){
02468                         notfound = true;
02469                         break;
02470                     }
02471                 }
02472             }
02473 
02474             // If there were no entries for all the datasets, use the manually set
02475             // texts, and resort to Series 1, Series 2, ... where nothing has been
02476             // set.
02477             if ( notfound ) {
02478                 for ( dataset = 0; dataset < numLegendFallbackTexts( data );
02479                         dataset++ ) {
02480                     _legendTexts[ dataset ] = params()->legendText( dataset );
02481                     if ( _legendTexts[ dataset ].isEmpty() || _legendTexts[ dataset ].isNull() ) {
02482                         _legendTexts[ dataset ] = fallbackLegendText( dataset );
02483                         // there
02484                         _numLegendTexts = numLegendFallbackTexts( data );
02485                     }
02486                 }
02487             }
02488             break;
02489         }
02490         default:
02491             // Should not happen
02492             qDebug( "KDChart: Unknown legend source" );
02493     }
02494 }
02495 
02496 
02512 QString KDChartPainter::fallbackLegendText( uint dataset ) const
02513 {
02514     return QObject::tr( "Series " ) + QString::number( dataset + 1 );
02515 }
02516 
02517 
02530 uint KDChartPainter::numLegendFallbackTexts( KDChartTableDataBase* data ) const
02531 {
02532     return data->usedRows();
02533 }
02534 
02535 
02545 void KDChartPainter::drawMarker( QPainter* painter,
02546                                  int style,
02547                                  const QColor& color,
02548                                  const QPoint& p,
02549                                  const QSize& size,
02550                                  uint align )
02551 {
02552     int width = size.width();
02553     int height = size.height();
02554     drawMarker( painter,
02555                 0,
02556                 0.0, 0.0,
02557                 0,0,
02558                 style,
02559                 color,
02560                 p,
02561                 0,0,0,
02562                 0,
02563                 &width,
02564                 &height,
02565                 align );
02566 }
02567 
02568 
02584 KDChartDataRegion* KDChartPainter::drawMarker( QPainter* painter,
02585                                                const KDChartParams* params,
02586                                                double areaWidthP1000,
02587                                                double areaHeightP1000,
02588                                                int deltaX,
02589                                                int deltaY,
02590                                                int style,
02591                                                const QColor& color,
02592                                                const QPoint& _p,
02593                                                uint dataset, uint value, uint chart,
02594                                                KDChartDataRegionList* regions,
02595                                                int* width,
02596                                                int* height,
02597                                                uint align )
02598 {
02599     KDChartDataRegion* datReg = 0;
02600     const double areaSizeP1000 = QMIN(areaWidthP1000, areaHeightP1000);
02601     int xsize  = width ? *width : (params ? params->lineMarkerSize().width() : 12);
02602     if( 0 > xsize )
02603         xsize = static_cast < int > (xsize * -areaSizeP1000);
02604     int ysize  = height ? *height : (params ? params->lineMarkerSize().height() : 12);
02605     if( 0 > ysize )
02606         ysize = static_cast < int > (ysize * -areaSizeP1000);
02607     if( KDChartParams::LineMarkerCross != style ){
02608         xsize = QMAX( xsize, 4 );
02609         ysize = QMAX( ysize, 4 );
02610     }
02611     uint xsize2 = xsize / 2;
02612     uint ysize2 = ysize / 2;
02613     uint xsize4 = xsize / 4;
02614     uint ysize4 = ysize / 4;
02615     uint xsize6 = xsize / 6;
02616     uint ysize6 = ysize / 6;
02617     painter->setPen( color );
02618     const uint xysize2 = QMIN( xsize2, ysize2 );
02619 
02620     int x = _p.x();
02621     int y = _p.y();
02622     if( align & Qt::AlignLeft )
02623         x += xsize2;
02624     else if( align & Qt::AlignRight )
02625         x -= xsize2;
02626     if( align & Qt::AlignTop )
02627         y += ysize2;
02628     else if( align & Qt::AlignBottom )
02629         y -= ysize2;
02630     const QPoint p(x, y);
02631 
02632     switch ( style ) {
02633         case KDChartParams::LineMarkerSquare: {
02634                                                 const QPen oldPen( painter->pen() );
02635                                                 const QBrush oldBrush( painter->brush() );
02636                                                 painter->setBrush( color );
02637                                                 painter->setPen(   color );
02638                                                 QRect rect( QPoint( p.x() - xsize2, p.y() - ysize2 ), QPoint( p.x() + xsize2, p.y() + ysize2 ) );
02639                                                 painter->drawRect( rect );
02640                                                 // Don't use rect for drawing after this!
02641                                                 rect.moveBy( deltaX, deltaY );
02642                                                 if ( regions ){
02643                                                     datReg =
02644                                                         new KDChartDataRegion(
02645                                                                 dataset, value,
02646                                                                 chart,   rect );
02647                                                     regions->append( datReg );
02648                                                 }
02649                                                 painter->setPen(   oldPen );
02650                                                 painter->setBrush( oldBrush );
02651                                                 break;
02652                                               }
02653         case KDChartParams::LineMarkerDiamond:{
02654                                                 const QBrush oldBrush( painter->brush() );
02655                                                 painter->setBrush( color );
02656                                                 QPointArray points( 4 );
02657                                                 points.setPoint( 0, p.x() - xsize2, p.y() );
02658                                                 points.setPoint( 1, p.x(),          p.y() - ysize2 );
02659                                                 points.setPoint( 2, p.x() + xsize2, p.y() );
02660                                                 points.setPoint( 3, p.x(),          p.y() + ysize2 );
02661                                                 painter->drawPolygon( points );
02662                                                 // Don't use points for drawing after this!
02663                                                 points.translate( deltaX, deltaY );
02664                                                 if ( regions ){
02665                                                     datReg = new KDChartDataRegion(
02666                                                                     dataset, value,
02667                                                                     chart,   points );
02668                                                     regions->append( datReg  );
02669                                                 }
02670                                                 painter->setBrush( oldBrush );
02671                                                 break;
02672                                               }
02673         case KDChartParams::LineMarker1Pixel: {
02674                                                 QRect rect( p, p );
02675                                                 painter->drawRect( rect );
02676                                                 // Don't use rect for drawing after this!
02677                                                 rect.moveBy( deltaX, deltaY );
02678                                                 if ( regions ){
02679                                                     datReg = new KDChartDataRegion(
02680                                                                     dataset, value,
02681                                                                     chart, rect );
02682                                                     regions->append( datReg );
02683                                                 }
02684                                                 break;
02685                                               }
02686         case KDChartParams::LineMarker4Pixels:{
02687                                                 QRect rect( p, QPoint( p.x()+1, p.y()+1 ) );
02688                                                 painter->drawRect( rect );
02689                                                 // Don't use rect for drawing after this!
02690                                                 rect.moveBy( deltaX, deltaY );
02691                                                 if ( regions ){
02692                                                     datReg = new KDChartDataRegion(
02693                                                                     dataset, value,
02694                                                                     chart, rect );
02695                                                     regions->append( datReg );
02696                                                 }
02697                                                 break;
02698                                               }
02699         case KDChartParams::LineMarkerRing:   {
02700                                                 const QPen oldPen( painter->pen() );
02701                                                 painter->setPen( QPen( color, QMIN(xsize4, ysize4) ) );
02702                                                 const QBrush oldBrush( painter->brush() );
02703                                                 painter->setBrush( Qt::NoBrush );
02704                                                 painter->drawEllipse( p.x() - xsize2, p.y() - ysize2, xsize, ysize );
02705                                                 if ( regions ) {
02706                                                     QPointArray points;
02707                                                     points.makeEllipse( p.x() - xsize2, p.y() - ysize2, xsize, ysize );
02708                                                     // Don't use points for drawing after this!
02709                                                     points.translate( deltaX, deltaY );
02710                                                     if( points.size() > 0 ){
02711                                                         datReg = new KDChartDataRegion(
02712                                                                         dataset, value,
02713                                                                         chart,   points );
02714                                                         regions->append( datReg );
02715                                                     }
02716                                                 }
02717                                                 painter->setBrush( oldBrush );
02718                                                 painter->setPen(   oldPen );
02719                                                 break;
02720                                               }
02721         case KDChartParams::LineMarkerCross:  {
02722                                                 const QPen oldPen( painter->pen() );
02723                                                 painter->setPen( color );
02724                                                 const QBrush oldBrush( painter->brush() );
02725                                                 painter->setBrush( color );
02726                                                 int numPoints = (ysize && xsize) ? 12 : 4;
02727                                                 QPointArray points( numPoints );
02728                                                 if( ysize && xsize ){
02729                                                     points.setPoint( 0, p.x() - xsize6, p.y() - ysize6 );
02730                                                     points.setPoint( 1, p.x() - xsize6, p.y() - ysize2 );
02731                                                     points.setPoint( 2, p.x() + xsize6, p.y() - ysize2 );
02732                                                     points.setPoint( 3, p.x() + xsize6, p.y() - ysize6 );
02733                                                     points.setPoint( 4, p.x() + xsize2, p.y() - ysize6 );
02734                                                     points.setPoint( 5, p.x() + xsize2, p.y() + ysize6 );
02735                                                     points.setPoint( 6, p.x() + xsize6, p.y() + ysize6 );
02736                                                     points.setPoint( 7, p.x() + xsize6, p.y() + ysize2 );
02737                                                     points.setPoint( 8, p.x() - xsize6, p.y() + ysize2 );
02738                                                     points.setPoint( 9, p.x() - xsize6, p.y() + ysize6 );
02739                                                     points.setPoint(10, p.x() - xsize2, p.y() + ysize6 );
02740                                                     points.setPoint(11, p.x() - xsize2, p.y() - ysize6 );
02741                                                 }else if( ysize ){
02742                                                     points.setPoint( 0, p.x() - ysize6, p.y() - ysize2 );
02743                                                     points.setPoint( 1, p.x() + ysize6, p.y() - ysize2 );
02744                                                     points.setPoint( 2, p.x() + ysize6, p.y() + ysize2 );
02745                                                     points.setPoint( 3, p.x() - ysize6, p.y() + ysize2 );
02746                                                 }else{
02747                                                     points.setPoint( 0, p.x() - xsize2, p.y() - xsize6 );
02748                                                     points.setPoint( 1, p.x() + xsize2, p.y() - xsize6 );
02749                                                     points.setPoint( 2, p.x() + xsize2, p.y() + xsize6 );
02750                                                     points.setPoint( 3, p.x() - xsize2, p.y() + xsize6 );
02751                                                 }
02752                                                 painter->drawPolygon( points );
02753                                                 // Don't use points for drawing after this!
02754                                                 points.translate( deltaX, deltaY );
02755                                                 if( regions ){
02756                                                     datReg = new KDChartDataRegion(
02757                                                                     dataset, value,
02758                                                                     chart,   points );
02759                                                     regions->append( datReg );
02760                                                 }
02761                                                 painter->setBrush( oldBrush );
02762                                                 painter->setPen(   oldPen );
02763                                                 break;
02764                                               }
02765         case KDChartParams::LineMarkerFastCross: {
02766                                                 const QPen oldPen( painter->pen() );
02767                                                 painter->setPen( color );
02768                                                 painter->drawLine( QPoint(p.x() - xysize2, p.y()),
02769                                                                    QPoint(p.x() + xysize2, p.y()) );
02770                                                 painter->drawLine( QPoint(p.x(), p.y() - xysize2),
02771                                                                    QPoint(p.x(), p.y() + xysize2) );
02772                                                 QRect rect( QPoint( p.x() - 2, p.y() - 2 ),
02773                                                             QPoint( p.x() + 2, p.y() + 2 ) );
02774                                                 // Don't use rect for drawing after this!
02775                                                 rect.moveBy( deltaX, deltaY );
02776                                                 if ( regions ){
02777                                                     datReg =
02778                                                         new KDChartDataRegion(
02779                                                                 dataset, value,
02780                                                                 chart,   rect );
02781                                                     regions->append( datReg );
02782                                                 }
02783                                                 painter->setPen(   oldPen );
02784                                                 break;
02785                                               }
02786         case KDChartParams::LineMarkerCircle:
02787         default:                              {
02788                                                 const QBrush oldBrush( painter->brush() );
02789                                                 painter->setBrush( color );
02790                                                 painter->drawEllipse( p.x() - xsize2, p.y() - ysize2, xsize, ysize );
02791                                                 if ( regions ) {
02792                                                     QPointArray points;
02793                                                     points.makeEllipse( p.x() - xsize2, p.y() - ysize2, xsize, ysize );
02794                                                     // Don't use points for drawing after this!
02795                                                     points.translate( deltaX, deltaY );
02796                                                     if( points.size() > 0 ){
02797                                                         datReg = new KDChartDataRegion(
02798                                                                         dataset, value,
02799                                                                         chart,   points );
02800                                                         regions->append( datReg );
02801                                                     }
02802                                                 }
02803                                                 painter->setBrush( oldBrush );
02804                                               }
02805     }
02806     return datReg;
02807 }
02808 
02809 
02810 void KDChartPainter::drawExtraLinesAndMarkers(
02811         KDChartPropertySet& propSet,
02812         const QPen& defaultPen,
02813         const KDChartParams::LineMarkerStyle& defaultMarkerStyle,
02814         int myPointX,
02815         int myPointY,
02816         QPainter* painter,
02817         const KDChartAxisParams* abscissaPara,
02818         const KDChartAxisParams* ordinatePara,
02819         const double areaWidthP1000,
02820         const double areaHeightP1000,
02821         bool bDrawInFront )
02822 {
02823 
02824     // we can safely call the following functions and ignore their
02825     // return values since they will touch the parameters' values
02826     // if the propSet *contains* corresponding own values only.
02827     int  iDummy;
02828     uint extraLinesAlign = 0;
02829     if( propSet.hasOwnExtraLinesAlign( iDummy, extraLinesAlign )
02830         && ( extraLinesAlign
02831             & ( Qt::AlignLeft | Qt::AlignRight  | Qt::AlignHCenter |
02832                 Qt::AlignTop  | Qt::AlignBottom | Qt::AlignVCenter ) ) ){
02833         bool extraLinesInFront = false;
02834         propSet.hasOwnExtraLinesInFront( iDummy, extraLinesInFront );
02835         if( bDrawInFront == extraLinesInFront ){
02836             const double areaSizeP1000 = QMIN(areaWidthP1000, areaHeightP1000);
02837             int          extraLinesLength = -20;
02838             int          extraLinesWidth = defaultPen.width();
02839             QColor       extraLinesColor = defaultPen.color();
02840             Qt::PenStyle extraLinesStyle = defaultPen.style();
02841             uint         extraMarkersAlign = 0;
02842             propSet.hasOwnExtraLinesLength( iDummy, extraLinesLength );
02843             propSet.hasOwnExtraLinesWidth(  iDummy, extraLinesWidth  );
02844             propSet.hasOwnExtraLinesColor(  iDummy, extraLinesColor  );
02845             propSet.hasOwnExtraLinesStyle(  iDummy, extraLinesStyle  );
02846             const int horiLenP2 = (0 > extraLinesLength)
02847                                 ? static_cast<int>(areaWidthP1000  * extraLinesLength) / 2
02848                                 : extraLinesLength / 2;
02849             const int vertLenP2 = (0 > extraLinesLength)
02850                                 ? static_cast<int>(areaHeightP1000 * extraLinesLength) / 2
02851                                 : extraLinesLength / 2;
02852             // draw the extra line(s)
02853             QPoint pL( (Qt::AlignLeft == (extraLinesAlign & Qt::AlignLeft))
02854                     ? 0
02855                     : (Qt::AlignHCenter == (extraLinesAlign & Qt::AlignHCenter))
02856                         ? myPointX - horiLenP2
02857                         : myPointX,
02858                     myPointY );
02859             QPoint pR( (Qt::AlignRight == (extraLinesAlign & Qt::AlignRight))
02860                     ? abscissaPara->axisTrueAreaRect().width()
02861                     : (Qt::AlignHCenter == (extraLinesAlign & Qt::AlignHCenter))
02862                         ? myPointX + horiLenP2
02863                         : myPointX,
02864                     myPointY );
02865             QPoint pT( myPointX,
02866                     (Qt::AlignTop == (extraLinesAlign & Qt::AlignTop))
02867                     ? 0
02868                     : (Qt::AlignVCenter == (extraLinesAlign & Qt::AlignVCenter))
02869                         ? myPointY - vertLenP2
02870                         : myPointY );
02871             QPoint pB( myPointX,
02872                     (Qt::AlignBottom == (extraLinesAlign & Qt::AlignBottom))
02873                     ? ordinatePara->axisTrueAreaRect().height()
02874                     : (Qt::AlignVCenter == (extraLinesAlign & Qt::AlignVCenter))
02875                         ? myPointY + vertLenP2
02876                         : myPointY );
02877             const QPen extraPen( extraLinesColor,
02878                                 0 > extraLinesWidth
02879                                 ? static_cast < int > ( areaSizeP1000 * -extraLinesWidth )
02880                                 : extraLinesWidth,
02881                                 extraLinesStyle );
02882             const QPen oldPen( painter->pen() );
02883             painter->setPen( extraPen );
02884             if( extraLinesAlign & ( Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter ) )
02885                 painter->drawLine( pL, pR );
02886             if( extraLinesAlign & ( Qt::AlignTop | Qt::AlignBottom | Qt::AlignVCenter ) )
02887                 painter->drawLine( pT, pB );
02888             painter->setPen( oldPen );
02889             // draw the marker(s) of the extra line(s)
02890             propSet.hasOwnExtraMarkersAlign( iDummy, extraMarkersAlign );
02891             if( extraMarkersAlign
02892                     & ( Qt::AlignLeft | Qt::AlignRight |
02893                         Qt::AlignTop  | Qt::AlignBottom ) ){
02894                 QSize  extraMarkersSize  = params()->lineMarkerSize();
02895                 QColor extraMarkersColor = extraLinesColor;
02896                 int    extraMarkersStyle = defaultMarkerStyle;
02897                 propSet.hasOwnExtraMarkersSize(  iDummy, extraMarkersSize );
02898                 propSet.hasOwnExtraMarkersColor( iDummy, extraMarkersColor );
02899                 propSet.hasOwnExtraMarkersStyle( iDummy, extraMarkersStyle );
02900                 // draw the extra marker(s)
02901                 int w = extraMarkersSize.width();
02902                 int h = extraMarkersSize.height();
02903                 if( w < 0 )
02904                     w = static_cast < int > (w * -areaSizeP1000);
02905                 if( h < 0 )
02906                     h = static_cast < int > (h * -areaSizeP1000);
02907                 if( extraMarkersAlign & Qt::AlignLeft )
02908                     drawMarker( painter,
02909                                 params(),
02910                                 _areaWidthP1000, _areaHeightP1000,
02911                                 _dataRect.x(), _dataRect.y(),
02912                                 (KDChartParams::LineMarkerStyle)extraMarkersStyle,
02913                                 extraMarkersColor,
02914                                 pL,
02915                                 0, 0, 0, 0,
02916                                 &w, &h,
02917                                 Qt::AlignCenter );
02918                 if( extraMarkersAlign & Qt::AlignRight )
02919                     drawMarker( painter,
02920                                 params(),
02921                                 _areaWidthP1000, _areaHeightP1000,
02922                                 _dataRect.x(), _dataRect.y(),
02923                                 (KDChartParams::LineMarkerStyle)extraMarkersStyle,
02924                                 extraMarkersColor,
02925                                 pR,
02926                                 0, 0, 0, 0,
02927                                 &w, &h,
02928                                 Qt::AlignCenter );
02929                 if( extraMarkersAlign & Qt::AlignTop )
02930                     drawMarker( painter,
02931                                 params(),
02932                                 _areaWidthP1000, _areaHeightP1000,
02933                                 _dataRect.x(), _dataRect.y(),
02934                                 (KDChartParams::LineMarkerStyle)extraMarkersStyle,
02935                                 extraMarkersColor,
02936                                 pT,
02937                                 0, 0, 0, 0,
02938                                 &w, &h,
02939                                 Qt::AlignCenter );
02940                 if( extraMarkersAlign & Qt::AlignBottom )
02941                     drawMarker( painter,
02942                                 params(),
02943                                 _areaWidthP1000, _areaHeightP1000,
02944                                 _dataRect.x(), _dataRect.y(),
02945                                 (KDChartParams::LineMarkerStyle)extraMarkersStyle,
02946                                 extraMarkersColor,
02947                                 pB,
02948                                 0, 0, 0, 0,
02949                                 &w, &h,
02950                                 Qt::AlignCenter );
02951             }
02952         }
02953     }
02954 }
02955 
02956 
KDE Home | KDE Accessibility Home | Description of Access Keys