KDChartLegend.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB.  All rights reserved.
00003 **
00004 ** This file is part of the KD Chart library.
00005 **
00006 ** Licensees holding valid commercial KD Chart licenses may use this file in
00007 ** accordance with the KD Chart Commercial License Agreement provided with
00008 ** the Software.
00009 **
00010 **
00011 ** This file may be distributed and/or modified under the terms of the
00012 ** GNU General Public License version 2 and version 3 as published by the
00013 ** Free Software Foundation and appearing in the file LICENSE.GPL included.
00014 **
00015 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00016 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00017 **
00018 ** Contact info@kdab.com if any conditions of this licensing are not
00019 ** clear to you.
00020 **
00021 **********************************************************************/
00022 
00023 #include "KDChartLegend.h"
00024 #include "KDChartLegend_p.h"
00025 #include <KDChartTextAttributes.h>
00026 #include <KDChartMarkerAttributes.h>
00027 #include <QFont>
00028 #include <QPainter>
00029 #include <QTextTableCell>
00030 #include <QTextCursor>
00031 #include <QTextCharFormat>
00032 #include <QTextDocumentFragment>
00033 #include <QTimer>
00034 #include <QAbstractTextDocumentLayout>
00035 #include <QtDebug>
00036 #include <QLabel>
00037 #include <KDChartAbstractDiagram.h>
00038 #include "KDTextDocument.h"
00039 #include <KDChartDiagramObserver.h>
00040 #include <QGridLayout>
00041 #include "KDChartLayoutItems.h"
00042 
00043 #include <KDABLibFakes>
00044 
00045 using namespace KDChart;
00046 
00047 Legend::Private::Private() :
00048     referenceArea(0),
00049     position( Position::East ),
00050     alignment( Qt::AlignCenter ),
00051     textAlignment( Qt::AlignCenter ),
00052     relativePosition( RelativePosition() ),
00053     orientation( Qt::Vertical ),
00054     order( Qt::AscendingOrder ),
00055     showLines( false ),
00056     texts(),
00057     textAttributes(),
00058     titleText( QObject::tr( "Legend" ) ),
00059     titleTextAttributes(),
00060     spacing( 1 ),
00061     useAutomaticMarkerSize( true ),
00062     legendStyle( MarkersOnly )
00063     //needRebuild( true )
00064 {
00065     // By default we specify a simple, hard point as the 'relative' position's ref. point,
00066     // since we can not be sure that there will be any parent specified for the legend.
00067     relativePosition.setReferencePoints(   PositionPoints( QPointF( 0.0, 0.0 ) ) );
00068     relativePosition.setReferencePosition( Position::NorthWest );
00069     relativePosition.setAlignment( Qt::AlignTop | Qt::AlignLeft );
00070     relativePosition.setHorizontalPadding( KDChart::Measure( 4.0, KDChartEnums::MeasureCalculationModeAbsolute ) );
00071     relativePosition.setVerticalPadding(   KDChart::Measure( 4.0, KDChartEnums::MeasureCalculationModeAbsolute ) );
00072 }
00073 
00074 Legend::Private::~Private()
00075 {
00076     // this bloc left empty intentionally
00077 }
00078 
00079 
00080 
00081 #define d d_func()
00082 
00083 
00084 Legend::Legend( QWidget* parent ) :
00085     AbstractAreaWidget( new Private(), parent )
00086 {
00087     d->referenceArea = parent;
00088     init();
00089 }
00090 
00091 Legend::Legend( KDChart::AbstractDiagram* diagram, QWidget* parent ) :
00092     AbstractAreaWidget( new Private(), parent )
00093 {
00094     d->referenceArea = parent;
00095     init();
00096     setDiagram( diagram );
00097 }
00098 
00099 Legend::~Legend()
00100 {
00101     emit destroyedLegend( this );
00102 }
00103 
00104 void Legend::init()
00105 {
00106     setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
00107 
00108     d->layout = new QGridLayout( this );
00109     d->layout->setMargin( 2 );
00110     d->layout->setSpacing( d->spacing );
00111     //setLayout( d->layout );
00112 
00113     const Measure normalFontSizeTitle(  12, KDChartEnums::MeasureCalculationModeAbsolute );
00114     const Measure normalFontSizeLabels( 10, KDChartEnums::MeasureCalculationModeAbsolute );
00115     const Measure minimalFontSize(       4, KDChartEnums::MeasureCalculationModeAbsolute );
00116 
00117     TextAttributes textAttrs;
00118     textAttrs.setPen( QPen( Qt::black ) );
00119     textAttrs.setFont( QFont( QLatin1String( "helvetica" ), 10, QFont::Normal, false ) );
00120     textAttrs.setFontSize(        normalFontSizeLabels );
00121     textAttrs.setMinimalFontSize( minimalFontSize );
00122     setTextAttributes( textAttrs );
00123 
00124     TextAttributes titleTextAttrs;
00125     titleTextAttrs.setPen( QPen( Qt::black ) );
00126     titleTextAttrs.setFont( QFont( QLatin1String( "helvetica" ), 12, QFont::Bold, false ) );
00127     titleTextAttrs.setFontSize(        normalFontSizeTitle );
00128     titleTextAttrs.setMinimalFontSize( minimalFontSize );
00129     setTitleTextAttributes( titleTextAttrs );
00130 
00131     FrameAttributes frameAttrs;
00132     frameAttrs.setVisible( true );
00133     frameAttrs.setPen( QPen( Qt::black ) );
00134     frameAttrs.setPadding( 1 );
00135     setFrameAttributes( frameAttrs );
00136 
00137     d->position = Position::NorthEast;
00138     d->alignment = Qt::AlignCenter;
00139 }
00140 
00141 
00142 QSize Legend::minimumSizeHint() const
00143 {
00144     return sizeHint();
00145 }
00146 
00147 //#define DEBUG_LEGEND_PAINT
00148 
00149 QSize Legend::sizeHint() const
00150 {
00151 #ifdef DEBUG_LEGEND_PAINT
00152     qDebug()  << "Legend::sizeHint() started";
00153 #endif
00154     Q_FOREACH( KDChart::AbstractLayoutItem* layoutItem, d->layoutItems ) {
00155         layoutItem->sizeHint();
00156     }
00157     return AbstractAreaWidget::sizeHint();
00158 }
00159 
00160 void Legend::needSizeHint()
00161 {
00162     // Re-build the Legend's content, if it has not been build yet,
00163     // or if the Legend's geometry has changed, resp.
00164     buildLegend();
00165 }
00166 
00167 void Legend::resizeLayout( const QSize& size )
00168 {
00169 #ifdef DEBUG_LEGEND_PAINT
00170     qDebug() << "Legend::resizeLayout started";
00171 #endif
00172     if( d->layout ){
00173         d->layout->setGeometry( QRect(QPoint(0,0), size) );
00174         activateTheLayout();
00175     }
00176 #ifdef DEBUG_LEGEND_PAINT
00177     qDebug() << "Legend::resizeLayout done";
00178 #endif
00179 }
00180 
00181 void Legend::activateTheLayout()
00182 {
00183     if( d->layout && d->layout->parent() )
00184         d->layout->activate();
00185 }
00186 
00187 
00188 void Legend::setLegendStyle( LegendStyle style )
00189 {
00190     if( d->legendStyle == style ) return;
00191     d->legendStyle = style;
00192     setNeedRebuild();
00193 }
00194 
00195 Legend::LegendStyle Legend::legendStyle() const
00196 {
00197     return d->legendStyle;
00198 }
00199 
00203 Legend* Legend::clone() const
00204 {
00205     Legend* legend = new Legend( new Private( *d ), 0 );
00206     legend->setTextAttributes( textAttributes() );
00207     legend->setTitleTextAttributes( titleTextAttributes() );
00208     legend->setFrameAttributes( frameAttributes() );
00209     legend->setUseAutomaticMarkerSize( useAutomaticMarkerSize() );
00210     legend->setPosition( position() );
00211     legend->setAlignment( alignment() );
00212     legend->setTextAlignment( textAlignment() );
00213     legend->setLegendStyle( legendStyle() );
00214     return legend;
00215 }
00216 
00217 
00218 bool Legend::compare( const Legend* other )const
00219 {
00220     if( other == this ) return true;
00221     if( ! other ){
00222         //qDebug() << "Legend::compare() cannot compare to Null pointer";
00223         return false;
00224     }
00225 
00226     qDebug() << ( static_cast<const AbstractAreaBase*>(this)->compare( other ) );
00227     qDebug() << (isVisible()              == other->isVisible());
00228     qDebug() << (position()               == other->position());
00229     qDebug() << (alignment()              == other->alignment());
00230     qDebug() << (textAlignment()          == other->textAlignment());
00231     qDebug() << (floatingPosition()       == other->floatingPosition());
00232     qDebug() << (orientation()            == other->orientation());
00233     qDebug() << (showLines()              == other->showLines());
00234     qDebug() << (texts()                  == other->texts());
00235     qDebug() << (brushes()                == other->brushes());
00236     qDebug() << (pens()                   == other->pens());
00237     qDebug() << (markerAttributes()       == other->markerAttributes());
00238     qDebug() << (useAutomaticMarkerSize() == other->useAutomaticMarkerSize());
00239     qDebug() << (textAttributes()         == other->textAttributes());
00240     qDebug() << (titleText()              == other->titleText());
00241     qDebug() << (titleTextAttributes()    == other->titleTextAttributes());
00242     qDebug() << (spacing()                == other->spacing());
00243     qDebug() << (legendStyle()            == other->legendStyle());
00244 
00245     return  ( static_cast<const AbstractAreaBase*>(this)->compare( other ) ) &&
00246             (isVisible()              == other->isVisible()) &&
00247             (position()               == other->position()) &&
00248             (alignment()              == other->alignment())&&
00249             (textAlignment()          == other->textAlignment())&&
00250             (floatingPosition()       == other->floatingPosition()) &&
00251             (orientation()            == other->orientation())&&
00252             (showLines()              == other->showLines())&&
00253             (texts()                  == other->texts())&&
00254             (brushes()                == other->brushes())&&
00255             (pens()                   == other->pens())&&
00256             (markerAttributes()       == other->markerAttributes())&&
00257             (useAutomaticMarkerSize() == other->useAutomaticMarkerSize()) &&
00258             (textAttributes()         == other->textAttributes()) &&
00259             (titleText()              == other->titleText())&&
00260             (titleTextAttributes()    == other->titleTextAttributes()) &&
00261             (spacing()                == other->spacing()) &&
00262             (legendStyle()            == other->legendStyle());
00263 }
00264 
00265 
00266 void Legend::paint( QPainter* painter )
00267 {
00268 #ifdef DEBUG_LEGEND_PAINT
00269     qDebug() << "entering Legend::paint( QPainter* painter )";
00270 #endif
00271     // rule: We do not show a legend, if there is no diagram.
00272     if( ! diagram() ) return;
00273 
00274     // re-calculate/adjust the Legend's internal layout and contents, if needed:
00275     //buildLegend();
00276 
00277     // PENDING(kalle) Support palette
00278 
00279     Q_FOREACH( KDChart::AbstractLayoutItem* layoutItem, d->layoutItems ) {
00280         layoutItem->paint( painter );
00281     }
00282 #ifdef DEBUG_LEGEND_PAINT
00283     qDebug() << "leaving Legend::paint( QPainter* painter )";
00284 #endif
00285 }
00286 
00287 
00288 uint Legend::datasetCount() const
00289 {
00290     int modelLabelsCount = 0;
00291     int modelBrushesCount = 0;
00292     for (int i = 0; i < d->observers.size(); ++i) {
00293         DiagramObserver * obs = d->observers.at(i);
00294         modelLabelsCount  += obs->diagram()->datasetLabels().count();
00295         modelBrushesCount += obs->diagram()->datasetBrushes().count();
00296     }
00297     Q_ASSERT( modelLabelsCount == modelBrushesCount );
00298     return modelLabelsCount;
00299 }
00300 
00301 
00302 void Legend::setReferenceArea( const QWidget* area )
00303 {
00304     if( area == d->referenceArea ) return;
00305     d->referenceArea = area;
00306     setNeedRebuild();
00307 }
00308 
00309 const QWidget* Legend::referenceArea() const
00310 {
00311     //qDebug() << d->referenceArea;
00312     return (d->referenceArea ? d->referenceArea : static_cast<const QWidget*>(parent()));
00313 }
00314 
00315 
00316 AbstractDiagram* Legend::diagram() const
00317 {
00318     if( d->observers.isEmpty() )
00319         return 0;
00320     return d->observers.first()->diagram();
00321 }
00322 
00323 DiagramList Legend::diagrams() const
00324 {
00325     DiagramList list;
00326     for (int i = 0; i < d->observers.size(); ++i)
00327         list << d->observers.at(i)->diagram();
00328     return list;
00329 }
00330 
00331 ConstDiagramList Legend::constDiagrams() const
00332 {
00333     ConstDiagramList list;
00334     for (int i = 0; i < d->observers.size(); ++i)
00335         list << d->observers.at(i)->diagram();
00336     return list;
00337 }
00338 
00339 void Legend::addDiagram( AbstractDiagram* newDiagram )
00340 {
00341     if ( newDiagram )
00342     {
00343         DiagramObserver* observer = new DiagramObserver( newDiagram, this );
00344 
00345         DiagramObserver* oldObs = d->findObserverForDiagram( newDiagram );
00346         if( oldObs ){
00347             delete oldObs;
00348             d->observers[ d->observers.indexOf( oldObs ) ] = observer;
00349         }else{
00350             d->observers.append( observer );
00351         }
00352         connect( observer, SIGNAL( diagramAboutToBeDestroyed(AbstractDiagram*) ),
00353                            SLOT( resetDiagram(AbstractDiagram*) ));
00354         connect( observer, SIGNAL( diagramDataChanged(AbstractDiagram*) ),
00355                  SLOT( setNeedRebuild() ));
00356         connect( observer, SIGNAL( diagramDataHidden(AbstractDiagram*) ),
00357                  SLOT( setNeedRebuild() ));
00358         connect( observer, SIGNAL( diagramAttributesChanged(AbstractDiagram*) ),
00359                         SLOT( setNeedRebuild() ));
00360         setNeedRebuild();
00361     }
00362 }
00363 
00364 void Legend::removeDiagram( AbstractDiagram* oldDiagram )
00365 {
00366     int datasetBrushOffset = 0;
00367     QList<AbstractDiagram*> diagrams = this->diagrams();
00368     for(int i =0; i <diagrams.count(); i++)
00369     {
00370         if(diagrams.at(i) == oldDiagram)
00371         {
00372             for( int i = 0; i < oldDiagram->datasetBrushes().count(); i++ ){
00373                 d->brushes.remove(datasetBrushOffset + i);
00374                 d->texts.remove(datasetBrushOffset + i);
00375             }
00376             for( int i = 0; i < oldDiagram->datasetPens().count(); i++ ){
00377                 d->pens.remove(datasetBrushOffset + i);
00378             }
00379             break;
00380         }
00381         datasetBrushOffset += diagrams.at(i)->datasetBrushes().count();
00382     }
00383 
00384     if( oldDiagram ){
00385         DiagramObserver* oldObs = d->findObserverForDiagram( oldDiagram );
00386         if( oldObs ){
00387             //qDebug() << "before delete oldObs;";
00388             delete oldObs;
00389             //qDebug() << "after delete oldObs;";
00390             d->observers.removeAt( d->observers.indexOf( oldObs ) );
00391             //qDebug() << "after d->observers.removeAt()";
00392         }
00393         setNeedRebuild();
00394     }
00395 }
00396 
00397 void Legend::removeDiagrams()
00398 {
00399     for (int i = 0; i < d->observers.size(); ++i)
00400         removeDiagram( d->observers.at(i)->diagram() );
00401 }
00402 
00403 void Legend::replaceDiagram( AbstractDiagram* newDiagram,
00404                              AbstractDiagram* oldDiagram )
00405 {
00406     KDChart::AbstractDiagram* old = oldDiagram;
00407     if( ! d->observers.isEmpty() && ! old ){
00408         old = d->observers.first()->diagram();
00409         if( ! old )
00410             d->observers.removeFirst(); // first entry had a 0 diagram
00411     }
00412     if( old )
00413         removeDiagram( old );
00414     if( newDiagram )
00415         addDiagram( newDiagram );
00416 }
00417 
00418 uint Legend::dataSetOffset(KDChart::AbstractDiagram* diagram)
00419 {
00420     uint offset = 0;
00421 
00422     for (int i = 0; i < d->observers.count(); ++i)
00423     {
00424         if(d->observers.at(i)->diagram() == diagram)
00425             return offset;
00426 
00427         KDChart::AbstractDiagram* diagram = d->observers.at(i)->diagram();
00428         if(!diagram->model())
00429             continue;
00430 
00431         offset = offset + diagram->model()->columnCount();
00432     }
00433 
00434     return offset;
00435 }
00436 
00437 void Legend::setDiagram( KDChart::AbstractDiagram* newDiagram )
00438 {
00439     replaceDiagram( newDiagram );
00440 }
00441 
00442 void Legend::resetDiagram( AbstractDiagram* oldDiagram )
00443 {
00444     //qDebug() << oldDiagram;
00445     removeDiagram( oldDiagram );
00446 }
00447 
00448 void Legend::setVisible( bool visible )
00449 {
00450     if( isVisible() == visible )
00451         return;
00452     QWidget::setVisible( visible );
00453     emitPositionChanged();
00454 }
00455 
00456 void Legend::setNeedRebuild()
00457 {
00458     //qDebug() << "setNeedRebuild()";
00459     buildLegend();
00460     sizeHint();
00461 }
00462 
00463 void Legend::setPosition( Position position )
00464 {
00465     if( d->position == position )
00466         return;
00467     d->position = position;
00468     emitPositionChanged();
00469 }
00470 
00471 void Legend::emitPositionChanged()
00472 {
00473     emit positionChanged( this );
00474     emit propertiesChanged();
00475 }
00476 
00477 
00478 Position Legend::position() const
00479 {
00480     return d->position;
00481 }
00482 
00483 void Legend::setAlignment( Qt::Alignment alignment )
00484 {
00485     if( d->alignment == alignment )
00486         return;
00487     d->alignment = alignment;
00488     emitPositionChanged();
00489 }
00490 
00491 Qt::Alignment Legend::alignment() const
00492 {
00493     return d->alignment;
00494 }
00495 
00496 void Legend::setTextAlignment( Qt::Alignment alignment )
00497 {
00498     if( d->textAlignment == alignment )
00499         return;
00500     d->textAlignment = alignment;
00501     emitPositionChanged();
00502 }
00503 
00504 Qt::Alignment Legend::textAlignment() const
00505 {
00506     return d->textAlignment;
00507 }
00508 
00509 void Legend::setFloatingPosition( const RelativePosition& relativePosition )
00510 {
00511     d->position = Position::Floating;
00512     if( d->relativePosition != relativePosition ){
00513         d->relativePosition  = relativePosition;
00514         emitPositionChanged();
00515     }
00516 }
00517 
00518 const RelativePosition Legend::floatingPosition() const
00519 {
00520     return d->relativePosition;
00521 }
00522 
00523 void Legend::setOrientation( Qt::Orientation orientation )
00524 {
00525     if( d->orientation == orientation ) return;
00526     d->orientation = orientation;
00527     setNeedRebuild();
00528     emitPositionChanged();
00529 }
00530 
00531 Qt::Orientation Legend::orientation() const
00532 {
00533     return d->orientation;
00534 }
00535 
00536 void Legend::setSortOrder( Qt::SortOrder order )
00537 {
00538     if( d->order == order )
00539         return;
00540     d->order = order;
00541     setNeedRebuild();
00542     emitPositionChanged();
00543 }
00544 
00545 Qt::SortOrder Legend::sortOrder() const
00546 {
00547     return d->order;
00548 }
00549 
00550 void Legend::setShowLines( bool legendShowLines )
00551 {
00552     if( d->showLines == legendShowLines ) return;
00553     d->showLines = legendShowLines;
00554     setNeedRebuild();
00555     emitPositionChanged();
00556 }
00557 
00558 bool Legend::showLines() const
00559 {
00560     return d->showLines;
00561 }
00562 
00563 void Legend::setUseAutomaticMarkerSize( bool useAutomaticMarkerSize )
00564 {
00565     d->useAutomaticMarkerSize = useAutomaticMarkerSize;
00566     setNeedRebuild();
00567     emitPositionChanged();
00568 }
00569 
00570 bool Legend::useAutomaticMarkerSize() const
00571 {
00572     return d->useAutomaticMarkerSize;
00573 }
00574 
00580 void Legend::resetTexts()
00581 {
00582     if( ! d->texts.count() ) return;
00583     d->texts.clear();
00584     setNeedRebuild();
00585 }
00586 
00587 void Legend::setText( uint dataset, const QString& text )
00588 {
00589     if( d->texts[ dataset ] == text ) return;
00590     d->texts[ dataset ] = text;
00591     setNeedRebuild();
00592 }
00593 
00594 QString Legend::text( uint dataset ) const
00595 {
00596     if( d->texts.find( dataset ) != d->texts.end() ){
00597         return d->texts[ dataset ];
00598     }else{
00599         return d->modelLabels[ dataset ];
00600     }
00601 }
00602 
00603 const QMap<uint,QString> Legend::texts() const
00604 {
00605     return d->texts;
00606 }
00607 
00608 void Legend::setColor( uint dataset, const QColor& color )
00609 {
00610     if( d->brushes[ dataset ] == color ) return;
00611     d->brushes[ dataset ] = color;
00612     setNeedRebuild();
00613     update();
00614 }
00615 
00616 void Legend::setBrush( uint dataset, const QBrush& brush )
00617 {
00618     if( d->brushes[ dataset ] == brush ) return;
00619     d->brushes[ dataset ] = brush;
00620     setNeedRebuild();
00621     update();
00622 }
00623 
00624 QBrush Legend::brush( uint dataset ) const
00625 {
00626     if( d->brushes.find( dataset ) != d->brushes.end() )
00627         return d->brushes[ dataset ];
00628     else
00629         return d->modelBrushes[ dataset ];
00630 }
00631 
00632 const QMap<uint,QBrush> Legend::brushes() const
00633 {
00634     return d->brushes;
00635 }
00636 
00637 
00638 void Legend::setBrushesFromDiagram( KDChart::AbstractDiagram* diagram )
00639 {
00640     bool bChangesDone = false;
00641     QList<QBrush> datasetBrushes = diagram->datasetBrushes();
00642     for( int i = 0; i < datasetBrushes.count(); i++ ){
00643         if( d->brushes[ i ] != datasetBrushes[ i ] ){
00644             d->brushes[ i ]  = datasetBrushes[ i ];
00645             bChangesDone = true;
00646         }
00647     }
00648     if( bChangesDone ) {
00649         setNeedRebuild();
00650         update();
00651     }
00652 }
00653 
00654 
00655 void Legend::setPen( uint dataset, const QPen& pen )
00656 {
00657     if( d->pens[dataset] == pen ) return;
00658     d->pens[dataset] = pen;
00659     setNeedRebuild();
00660     update();
00661 }
00662 
00663 QPen Legend::pen( uint dataset ) const
00664 {
00665     if( d->pens.find( dataset ) != d->pens.end() )
00666         return d->pens[dataset];
00667     else
00668         return d->modelPens[ dataset ];
00669 }
00670 
00671 const QMap<uint,QPen> Legend::pens() const
00672 {
00673     return d->pens;
00674 }
00675 
00676 
00677 void Legend::setMarkerAttributes( uint dataset, const MarkerAttributes& markerAttributes )
00678 {
00679     if( d->markerAttributes[dataset] == markerAttributes ) return;
00680     d->markerAttributes[ dataset ] = markerAttributes;
00681     setNeedRebuild();
00682     update();
00683 }
00684 
00685 MarkerAttributes Legend::markerAttributes( uint dataset ) const
00686 {
00687     if( d->markerAttributes.find( dataset ) != d->markerAttributes.end() )
00688         return d->markerAttributes[ dataset ];
00689     else if ( static_cast<uint>( d->modelMarkers.count() ) > dataset )
00690         return d->modelMarkers[ dataset ];
00691     return MarkerAttributes();
00692 }
00693 
00694 const QMap<uint, MarkerAttributes> Legend::markerAttributes() const
00695 {
00696     return d->markerAttributes;
00697 }
00698 
00699 
00700 void Legend::setTextAttributes( const TextAttributes &a )
00701 {
00702     if( d->textAttributes == a ) return;
00703     d->textAttributes = a;
00704     setNeedRebuild();
00705 }
00706 
00707 TextAttributes Legend::textAttributes() const
00708 {
00709     return d->textAttributes;
00710 }
00711 
00712 void Legend::setTitleText( const QString& text )
00713 {
00714     if( d->titleText == text ) return;
00715     d->titleText = text;
00716     setNeedRebuild();
00717 }
00718 
00719 QString Legend::titleText() const
00720 {
00721     return d->titleText;
00722 }
00723 
00724 void Legend::setTitleTextAttributes( const TextAttributes &a )
00725 {
00726     if( d->titleTextAttributes == a ) return;
00727     d->titleTextAttributes = a;
00728     setNeedRebuild();
00729 }
00730 
00731 TextAttributes Legend::titleTextAttributes() const
00732 {
00733     return d->titleTextAttributes;
00734 }
00735 
00736 void Legend::forceRebuild()
00737 {
00738 #ifdef DEBUG_LEGEND_PAINT
00739     qDebug() << "entering Legend::forceRebuild()";
00740 #endif
00741     //setSpacing(d->layout->spacing());
00742     buildLegend();
00743 #ifdef DEBUG_LEGEND_PAINT
00744     qDebug() << "leaving Legend::forceRebuild()";
00745 #endif
00746 }
00747 
00748 void Legend::setSpacing( uint space )
00749 {
00750     if( d->spacing == space && d->layout->spacing() == static_cast<int>(space) ) return;
00751     d->spacing = space;
00752     d->layout->setSpacing( space );
00753     setNeedRebuild();
00754 }
00755 
00756 uint Legend::spacing() const
00757 {
00758     return d->spacing;
00759 }
00760 
00761 void Legend::setDefaultColors()
00762 {
00763     setColor(  0, Qt::red );
00764     setColor(  1, Qt::green );
00765     setColor(  2, Qt::blue );
00766     setColor(  3, Qt::cyan );
00767     setColor(  4, Qt::magenta );
00768     setColor(  5, Qt::yellow );
00769     setColor(  6, Qt::darkRed );
00770     setColor(  7, Qt::darkGreen );
00771     setColor(  8, Qt::darkBlue );
00772     setColor(  9, Qt::darkCyan );
00773     setColor( 10, Qt::darkMagenta );
00774     setColor( 11, Qt::darkYellow );
00775 }
00776 
00777 void Legend::setRainbowColors()
00778 {
00779     setColor(  0, QColor(255,  0,196) );
00780     setColor(  1, QColor(255,  0, 96) );
00781     setColor(  2, QColor(255, 128,64) );
00782     setColor(  3, Qt::yellow );
00783     setColor(  4, Qt::green );
00784     setColor(  5, Qt::cyan );
00785     setColor(  6, QColor( 96, 96,255) );
00786     setColor(  7, QColor(160,  0,255) );
00787     for( int i = 8; i < 16; ++i )
00788         setColor( i, brush( i - 8 ).color().light() );
00789 }
00790 
00791 void Legend::setSubduedColors( bool ordered )
00792 {
00793 static const int NUM_SUBDUEDCOLORS = 18;
00794 static const QColor SUBDUEDCOLORS[ NUM_SUBDUEDCOLORS ] = {
00795     QColor( 0xe0,0x7f,0x70 ),
00796     QColor( 0xe2,0xa5,0x6f ),
00797     QColor( 0xe0,0xc9,0x70 ),
00798     QColor( 0xd1,0xe0,0x70 ),
00799     QColor( 0xac,0xe0,0x70 ),
00800     QColor( 0x86,0xe0,0x70 ),
00801     QColor( 0x70,0xe0,0x7f ),
00802     QColor( 0x70,0xe0,0xa4 ),
00803     QColor( 0x70,0xe0,0xc9 ),
00804     QColor( 0x70,0xd1,0xe0 ),
00805     QColor( 0x70,0xac,0xe0 ),
00806     QColor( 0x70,0x86,0xe0 ),
00807     QColor( 0x7f,0x70,0xe0 ),
00808     QColor( 0xa4,0x70,0xe0 ),
00809     QColor( 0xc9,0x70,0xe0 ),
00810     QColor( 0xe0,0x70,0xd1 ),
00811     QColor( 0xe0,0x70,0xac ),
00812     QColor( 0xe0,0x70,0x86 ),
00813 };
00814     if( ordered )
00815         for(int i=0; i<NUM_SUBDUEDCOLORS; ++i)
00816             setColor( i, SUBDUEDCOLORS[i] );
00817     else{
00818         setColor( 0, SUBDUEDCOLORS[ 0] );
00819         setColor( 1, SUBDUEDCOLORS[ 5] );
00820         setColor( 2, SUBDUEDCOLORS[10] );
00821         setColor( 3, SUBDUEDCOLORS[15] );
00822         setColor( 4, SUBDUEDCOLORS[ 2] );
00823         setColor( 5, SUBDUEDCOLORS[ 7] );
00824         setColor( 6, SUBDUEDCOLORS[12] );
00825         setColor( 7, SUBDUEDCOLORS[17] );
00826         setColor( 8, SUBDUEDCOLORS[ 4] );
00827         setColor( 9, SUBDUEDCOLORS[ 9] );
00828         setColor(10, SUBDUEDCOLORS[14] );
00829         setColor(11, SUBDUEDCOLORS[ 1] );
00830         setColor(12, SUBDUEDCOLORS[ 6] );
00831         setColor(13, SUBDUEDCOLORS[11] );
00832         setColor(14, SUBDUEDCOLORS[16] );
00833         setColor(15, SUBDUEDCOLORS[ 3] );
00834         setColor(16, SUBDUEDCOLORS[ 8] );
00835         setColor(17, SUBDUEDCOLORS[13] );
00836     }
00837 }
00838 
00839 void Legend::resizeEvent ( QResizeEvent * event )
00840 {
00841     Q_UNUSED( event );
00842 #ifdef DEBUG_LEGEND_PAINT
00843     qDebug() << "Legend::resizeEvent() called";
00844 #endif
00845     forceRebuild();
00846     sizeHint();
00847     QTimer::singleShot(0, this, SLOT(emitPositionChanged()));
00848 }
00849 
00850 void Legend::buildLegend()
00851 {
00852     /*
00853     if( !d->needRebuild ) {
00854 #ifdef DEBUG_LEGEND_PAINT
00855         qDebug() << "leaving Legend::buildLegend() with NO action (was already build)";
00856 #endif
00857         // Note: We do *not* need to send positionChanged here,
00858         //       because we send it in the resizeEvent, so layouting
00859         //       is done at the right time.
00860         return;
00861     }
00862 #ifdef DEBUG_LEGEND_PAINT
00863     qDebug() << "entering Legend::buildLegend() **********************************";
00864 #endif
00865     d->needRebuild = false;
00866     */
00867 
00868     Q_FOREACH( QLayoutItem* layoutItem, d->layoutItems ) {
00869         d->layout->removeItem( layoutItem );
00870     }
00871     qDeleteAll( d->layoutItems );
00872     d->layoutItems.clear();
00873 
00874     if( orientation() == Qt::Vertical ) {
00875         d->layout->setColumnStretch( 6, 1 );
00876     } else {
00877         d->layout->setColumnStretch( 6, 0 );
00878     }
00879 
00880     d->modelLabels.clear();
00881     d->modelBrushes.clear();
00882     d->modelPens.clear();
00883     d->modelMarkers.clear();
00884     // retrieve the diagrams' settings for all non-hidden datasets
00885     for (int i = 0; i < d->observers.size(); ++i){
00886         const AbstractDiagram* diagram = d->observers.at(i)->diagram();
00887         if( diagram ){
00888             //qDebug() << "accessing" << diagram;
00889             const QStringList             diagramLabels(  diagram->datasetLabels()  );
00890             const QList<QBrush>           diagramBrushes( diagram->datasetBrushes() );
00891             const QList<QPen>             diagramPens(    diagram->datasetPens()    );
00892             const QList<MarkerAttributes> diagramMarkers( diagram->datasetMarkers() );
00893             const int begin = sortOrder() == Qt::AscendingOrder ? 0 : diagramLabels.count() - 1;
00894             const int end = sortOrder() == Qt::AscendingOrder ? diagramLabels.count() : -1;
00895             for ( int dataset = begin; dataset != end; dataset += begin < end ? 1 : -1 )
00896             {
00897                 // only show the label if the diagrams is NOT having the dataset set to hidden
00898                 // and the dataset is not hidden in this legend either
00899                 if( !diagram->isHidden( dataset ) && !datasetIsHidden( dataset ) ){
00900                     d->modelLabels  += diagramLabels[   dataset ];
00901                     d->modelBrushes += diagramBrushes[  dataset ];
00902                     d->modelPens    += diagramPens[     dataset ];
00903                     d->modelMarkers += diagramMarkers[  dataset ];
00904                 }
00905             }
00906         }
00907     }
00908 
00909     Q_ASSERT( d->modelLabels.count() == d->modelBrushes.count() );
00910 
00911     // legend caption
00912     if( !titleText().isEmpty() && titleTextAttributes().isVisible() ) {
00913         // PENDING(kalle) Other properties!
00914         KDChart::TextLayoutItem* titleItem =
00915             new KDChart::TextLayoutItem( titleText(),
00916                 titleTextAttributes(),
00917                 referenceArea(),
00918                 (orientation() == Qt::Vertical)
00919                 ? KDChartEnums::MeasureOrientationMinimum
00920                 : KDChartEnums::MeasureOrientationHorizontal,
00921                 d->textAlignment );
00922         titleItem->setParentWidget( this );
00923 
00924         d->layoutItems << titleItem;
00925         if( orientation() == Qt::Vertical )
00926             d->layout->addItem( titleItem, 0, 0, 1, 5, Qt::AlignCenter );
00927         else
00928             d->layout->addItem( titleItem, 0, 0, 1, d->modelLabels.count() ? (d->modelLabels.count()*4) : 1, Qt::AlignCenter );
00929 
00930         // The line between the title and the legend items, if any.
00931         if( showLines() && d->modelLabels.count() ) {
00932             KDChart::HorizontalLineLayoutItem* lineItem = new KDChart::HorizontalLineLayoutItem();
00933             d->layoutItems << lineItem;
00934             if( orientation() == Qt::Vertical ){
00935                 d->layout->addItem( lineItem, 1, 0, 1, 5, Qt::AlignCenter );
00936             }else{
00937                 // we have 1+count*4 columns, because we have both, a leading and a trailing spacer
00938                 d->layout->addItem( lineItem, 1, 0, 1, 1+d->modelLabels.count()*4, Qt::AlignCenter );
00939             }
00940         }
00941     }
00942 
00943     const KDChartEnums::MeasureOrientation orient =
00944             (orientation() == Qt::Vertical)
00945             ? KDChartEnums::MeasureOrientationMinimum
00946             : KDChartEnums::MeasureOrientationHorizontal;
00947     const TextAttributes labelAttrs( textAttributes() );
00948     const qreal fontHeight = labelAttrs.calculatedFontSize( referenceArea(), orient );
00949     const LegendStyle style = legendStyle();
00950     //qDebug() << "fontHeight:" << fontHeight;
00951 
00952     const bool bShowMarkers = (style != LinesOnly);
00953 
00954     QSizeF maxMarkersSize(1.0, 1.0);
00955     QVector <MarkerAttributes> markerAttrs( d->modelLabels.count() );
00956     if( bShowMarkers ){
00957         for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ) {
00958             markerAttrs[dataset] = markerAttributes( dataset );
00959             QSizeF siz;
00960             if( useAutomaticMarkerSize() ||
00961                 ! markerAttrs[dataset].markerSize().isValid() )
00962             {
00963                 siz = QSizeF(fontHeight, fontHeight);
00964                 markerAttrs[dataset].setMarkerSize( siz );
00965             }else{
00966                 siz = markerAttrs[dataset].markerSize();
00967             }
00968             maxMarkersSize =
00969                     QSizeF(qMax(maxMarkersSize.width(),  siz.width()),
00970                            qMax(maxMarkersSize.height(), siz.height()));
00971         }
00972     }
00973 
00974     // If we show a marker on a line, we paint it after 8 pixels
00975     // of the line have been painted. This allows to see the line style
00976     // at the right side of the marker without the line needing to
00977     // be too long.
00978     // (having the marker in the middle of the line would require longer lines)
00979     const int markerOffsOnLine = 8;
00980 
00981     int maxLineLength = 18;
00982     {
00983         bool hasComplexPenStyle = false;
00984         for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ){
00985             const QPen pn = pen(dataset);
00986             const Qt::PenStyle ps = pn.style();
00987             if( ps != Qt::NoPen ){
00988                 maxLineLength = qMax( pn.width() * 18, maxLineLength );
00989                 if( ps != Qt::SolidLine )
00990                     hasComplexPenStyle = true;
00991             }
00992         }
00993         if( hasComplexPenStyle && bShowMarkers )
00994             maxLineLength =
00995                     maxLineLength + markerOffsOnLine +
00996                     static_cast<int>(maxMarkersSize.width());
00997     }
00998 
00999 #define ADD_MARKER_SPACER_FOR_HORIZONTAL_MODE( column ) \
01000 { \
01001     if( orientation() != Qt::Vertical ) \
01002         d->layout->addItem( new QSpacerItem( spacing(), 1 ), \
01003                             2, \
01004                             column ); \
01005 }
01006 
01007     // Horizontal needs a leading spacer
01008     ADD_MARKER_SPACER_FOR_HORIZONTAL_MODE( 0 )
01009 
01010     // for all datasets: add (line)marker items and text items to the layout
01011     for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ) {
01012         KDChart::AbstractLayoutItem* markerLineItem = 0;
01013         // It is possible to set the marker brush both through the MarkerAttributes,
01014         // as well as through the dataset brush set in the diagram, whereas the
01015         // MarkerAttributes are preferred.
01016         const QBrush markerBrush = markerAttrs[dataset].markerColor().isValid() ?
01017                                    QBrush(markerAttrs[dataset].markerColor()) : brush( dataset );
01018         switch( style ){
01019             case( MarkersOnly ):
01020                 markerLineItem = new KDChart::MarkerLayoutItem(
01021                         diagram(),
01022                         markerAttrs[dataset],
01023                         markerBrush,
01024                         markerAttrs[dataset].pen(),
01025                         Qt::AlignLeft );
01026                 break;
01027             case( LinesOnly ):
01028                 markerLineItem = new KDChart::LineLayoutItem(
01029                         diagram(),
01030                         maxLineLength,
01031                         pen( dataset ),
01032                         Qt::AlignCenter );
01033                 break;
01034             case( MarkersAndLines ):
01035                 markerLineItem = new KDChart::LineWithMarkerLayoutItem(
01036                         diagram(),
01037                         maxLineLength,
01038                         pen( dataset ),
01039                         markerOffsOnLine,
01040                         markerAttrs[dataset],
01041                         markerBrush,
01042                         markerAttrs[dataset].pen(),
01043                         Qt::AlignCenter );
01044                 break;
01045             default:
01046                 Q_ASSERT( false ); // all styles need to be handled
01047         }
01048         if( markerLineItem ){
01049             d->layoutItems << markerLineItem;
01050             if( orientation() == Qt::Vertical )
01051                 d->layout->addItem( markerLineItem,
01052                                     dataset*2+2, // first row is title, second is line
01053                                     1,
01054                                     1, 1, Qt::AlignCenter );
01055             else
01056                 d->layout->addItem( markerLineItem,
01057                                     2, // all in row two
01058                                     dataset*4+1 );
01059         }
01060 
01061         // PENDING(kalle) Other properties!
01062         KDChart::TextLayoutItem* labelItem =
01063             new KDChart::TextLayoutItem( text( dataset ),
01064                 labelAttrs,
01065                 referenceArea(), orient,
01066                 d->textAlignment );
01067         labelItem->setParentWidget( this );
01068 
01069         d->layoutItems << labelItem;
01070         if( orientation() == Qt::Vertical )
01071             d->layout->addItem( labelItem,
01072                                 dataset*2+2, // first row is title, second is line
01073                                 3 );
01074         else
01075             d->layout->addItem( labelItem,
01076                                 2, // all in row two
01077                                 dataset*4+2 );
01078 
01079         // horizontal lines (only in vertical mode, and not after the last item)
01080         if( orientation() == Qt::Vertical && showLines() && dataset != d->modelLabels.count()-1 ) {
01081             KDChart::HorizontalLineLayoutItem* lineItem = new KDChart::HorizontalLineLayoutItem();
01082             d->layoutItems << lineItem;
01083             d->layout->addItem( lineItem,
01084                                 dataset*2+1+2,
01085                                 0,
01086                                 1, 5, Qt::AlignCenter );
01087         }
01088 
01089         // vertical lines (only in horizontal mode, and not after the last item)
01090         if( orientation() == Qt::Horizontal && showLines() && dataset != d->modelLabels.count()-1 ) {
01091             KDChart::VerticalLineLayoutItem* lineItem = new KDChart::VerticalLineLayoutItem();
01092             d->layoutItems << lineItem;
01093             d->layout->addItem( lineItem,
01094                                 2, // all in row two
01095                                 style == MarkersAndLines ? dataset*4+4 : dataset*4+3 ,
01096                                 1, 1, Qt::AlignCenter );
01097         }
01098 
01099         // Horizontal needs a spacer
01100         ADD_MARKER_SPACER_FOR_HORIZONTAL_MODE( dataset*4+4 )
01101     }
01102 
01103     // vertical line (only in vertical mode)
01104     if( orientation() == Qt::Vertical && showLines() && d->modelLabels.count() ) {
01105         KDChart::VerticalLineLayoutItem* lineItem = new KDChart::VerticalLineLayoutItem();
01106         d->layoutItems << lineItem;
01107         d->layout->addItem( lineItem, 2, 2, d->modelLabels.count()*2, 1 );
01108     }
01109 
01110     // This line is absolutely necessary, otherwise: #2516.
01111     activateTheLayout();
01112 
01113     emit propertiesChanged();
01114     //emit positionChanged( this );
01115     //emitPositionChanged();
01116 #ifdef DEBUG_LEGEND_PAINT
01117     qDebug() << "leaving Legend::buildLegend()";
01118 #endif
01119 }
01120 
01121 void Legend::setHiddenDatasets( const QList<uint> hiddenDatasets )
01122 {
01123     d->hiddenDatasets = hiddenDatasets;
01124 }
01125 
01126 const QList<uint> Legend::hiddenDatasets() const
01127 {
01128     return d->hiddenDatasets;
01129 }
01130 
01131 void Legend::setDatasetHidden( uint dataset, bool hidden )
01132 {
01133     if( hidden && !d->hiddenDatasets.contains( dataset ) )
01134         d->hiddenDatasets.append( dataset );
01135     else if( !hidden && d->hiddenDatasets.contains( dataset ) )
01136         d->hiddenDatasets.removeAll( dataset );
01137 }
01138 
01139 bool Legend::datasetIsHidden( uint dataset ) const
01140 {
01141     return d->hiddenDatasets.contains( dataset );
01142 }
01143