KDChartLegend.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2011 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.txt 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     // removeDiagram() may change the d->observers list. So, build up the list of
00400     // diagrams to remove first and then remove them one by one.
00401     QList<AbstractDiagram*> diagrams;
00402     for (int i = 0; i < d->observers.size(); ++i)
00403         diagrams.append( d->observers.at(i)->diagram() );
00404     for (int i = 0; i < diagrams.count(); ++i)
00405         removeDiagram( diagrams[i] );
00406 }
00407 
00408 void Legend::replaceDiagram( AbstractDiagram* newDiagram,
00409                              AbstractDiagram* oldDiagram )
00410 {
00411     KDChart::AbstractDiagram* old = oldDiagram;
00412     if( ! d->observers.isEmpty() && ! old ){
00413         old = d->observers.first()->diagram();
00414         if( ! old )
00415             d->observers.removeFirst(); // first entry had a 0 diagram
00416     }
00417     if( old )
00418         removeDiagram( old );
00419     if( newDiagram )
00420         addDiagram( newDiagram );
00421 }
00422 
00423 uint Legend::dataSetOffset(KDChart::AbstractDiagram* diagram)
00424 {
00425     uint offset = 0;
00426 
00427     for (int i = 0; i < d->observers.count(); ++i)
00428     {
00429         if(d->observers.at(i)->diagram() == diagram)
00430             return offset;
00431 
00432         KDChart::AbstractDiagram* diagram = d->observers.at(i)->diagram();
00433         if(!diagram->model())
00434             continue;
00435 
00436         offset = offset + diagram->model()->columnCount();
00437     }
00438 
00439     return offset;
00440 }
00441 
00442 void Legend::setDiagram( KDChart::AbstractDiagram* newDiagram )
00443 {
00444     replaceDiagram( newDiagram );
00445 }
00446 
00447 void Legend::resetDiagram( AbstractDiagram* oldDiagram )
00448 {
00449     //qDebug() << oldDiagram;
00450     removeDiagram( oldDiagram );
00451 }
00452 
00453 void Legend::setVisible( bool visible )
00454 {
00455     if( isVisible() == visible )
00456         return;
00457     QWidget::setVisible( visible );
00458     emitPositionChanged();
00459 }
00460 
00461 void Legend::setNeedRebuild()
00462 {
00463     //qDebug() << "setNeedRebuild()";
00464     buildLegend();
00465     sizeHint();
00466 }
00467 
00468 void Legend::setPosition( Position position )
00469 {
00470     if( d->position == position )
00471         return;
00472     d->position = position;
00473     emitPositionChanged();
00474 }
00475 
00476 void Legend::emitPositionChanged()
00477 {
00478     emit positionChanged( this );
00479     emit propertiesChanged();
00480 }
00481 
00482 
00483 Position Legend::position() const
00484 {
00485     return d->position;
00486 }
00487 
00488 void Legend::setAlignment( Qt::Alignment alignment )
00489 {
00490     if( d->alignment == alignment )
00491         return;
00492     d->alignment = alignment;
00493     emitPositionChanged();
00494 }
00495 
00496 Qt::Alignment Legend::alignment() const
00497 {
00498     return d->alignment;
00499 }
00500 
00501 void Legend::setTextAlignment( Qt::Alignment alignment )
00502 {
00503     if( d->textAlignment == alignment )
00504         return;
00505     d->textAlignment = alignment;
00506     emitPositionChanged();
00507 }
00508 
00509 Qt::Alignment Legend::textAlignment() const
00510 {
00511     return d->textAlignment;
00512 }
00513 
00514 void Legend::setFloatingPosition( const RelativePosition& relativePosition )
00515 {
00516     d->position = Position::Floating;
00517     if( d->relativePosition != relativePosition ){
00518         d->relativePosition  = relativePosition;
00519         emitPositionChanged();
00520     }
00521 }
00522 
00523 const RelativePosition Legend::floatingPosition() const
00524 {
00525     return d->relativePosition;
00526 }
00527 
00528 void Legend::setOrientation( Qt::Orientation orientation )
00529 {
00530     if( d->orientation == orientation ) return;
00531     d->orientation = orientation;
00532     setNeedRebuild();
00533     emitPositionChanged();
00534 }
00535 
00536 Qt::Orientation Legend::orientation() const
00537 {
00538     return d->orientation;
00539 }
00540 
00541 void Legend::setSortOrder( Qt::SortOrder order )
00542 {
00543     if( d->order == order )
00544         return;
00545     d->order = order;
00546     setNeedRebuild();
00547     emitPositionChanged();
00548 }
00549 
00550 Qt::SortOrder Legend::sortOrder() const
00551 {
00552     return d->order;
00553 }
00554 
00555 void Legend::setShowLines( bool legendShowLines )
00556 {
00557     if( d->showLines == legendShowLines ) return;
00558     d->showLines = legendShowLines;
00559     setNeedRebuild();
00560     emitPositionChanged();
00561 }
00562 
00563 bool Legend::showLines() const
00564 {
00565     return d->showLines;
00566 }
00567 
00568 void Legend::setUseAutomaticMarkerSize( bool useAutomaticMarkerSize )
00569 {
00570     d->useAutomaticMarkerSize = useAutomaticMarkerSize;
00571     setNeedRebuild();
00572     emitPositionChanged();
00573 }
00574 
00575 bool Legend::useAutomaticMarkerSize() const
00576 {
00577     return d->useAutomaticMarkerSize;
00578 }
00579 
00585 void Legend::resetTexts()
00586 {
00587     if( ! d->texts.count() ) return;
00588     d->texts.clear();
00589     setNeedRebuild();
00590 }
00591 
00592 void Legend::setText( uint dataset, const QString& text )
00593 {
00594     if( d->texts[ dataset ] == text ) return;
00595     d->texts[ dataset ] = text;
00596     setNeedRebuild();
00597 }
00598 
00599 QString Legend::text( uint dataset ) const
00600 {
00601     if( d->texts.find( dataset ) != d->texts.end() ){
00602         return d->texts[ dataset ];
00603     }else{
00604         return d->modelLabels[ dataset ];
00605     }
00606 }
00607 
00608 const QMap<uint,QString> Legend::texts() const
00609 {
00610     return d->texts;
00611 }
00612 
00613 void Legend::setColor( uint dataset, const QColor& color )
00614 {
00615     if( d->brushes[ dataset ] == color ) return;
00616     d->brushes[ dataset ] = color;
00617     setNeedRebuild();
00618     update();
00619 }
00620 
00621 void Legend::setBrush( uint dataset, const QBrush& brush )
00622 {
00623     if( d->brushes[ dataset ] == brush ) return;
00624     d->brushes[ dataset ] = brush;
00625     setNeedRebuild();
00626     update();
00627 }
00628 
00629 QBrush Legend::brush( uint dataset ) const
00630 {
00631     if( d->brushes.find( dataset ) != d->brushes.end() )
00632         return d->brushes[ dataset ];
00633     else
00634         return d->modelBrushes[ dataset ];
00635 }
00636 
00637 const QMap<uint,QBrush> Legend::brushes() const
00638 {
00639     return d->brushes;
00640 }
00641 
00642 
00643 void Legend::setBrushesFromDiagram( KDChart::AbstractDiagram* diagram )
00644 {
00645     bool bChangesDone = false;
00646     QList<QBrush> datasetBrushes = diagram->datasetBrushes();
00647     for( int i = 0; i < datasetBrushes.count(); i++ ){
00648         if( d->brushes[ i ] != datasetBrushes[ i ] ){
00649             d->brushes[ i ]  = datasetBrushes[ i ];
00650             bChangesDone = true;
00651         }
00652     }
00653     if( bChangesDone ) {
00654         setNeedRebuild();
00655         update();
00656     }
00657 }
00658 
00659 
00660 void Legend::setPen( uint dataset, const QPen& pen )
00661 {
00662     if( d->pens[dataset] == pen ) return;
00663     d->pens[dataset] = pen;
00664     setNeedRebuild();
00665     update();
00666 }
00667 
00668 QPen Legend::pen( uint dataset ) const
00669 {
00670     if( d->pens.find( dataset ) != d->pens.end() )
00671         return d->pens[dataset];
00672     else
00673         return d->modelPens[ dataset ];
00674 }
00675 
00676 const QMap<uint,QPen> Legend::pens() const
00677 {
00678     return d->pens;
00679 }
00680 
00681 
00682 void Legend::setMarkerAttributes( uint dataset, const MarkerAttributes& markerAttributes )
00683 {
00684     if( d->markerAttributes[dataset] == markerAttributes ) return;
00685     d->markerAttributes[ dataset ] = markerAttributes;
00686     setNeedRebuild();
00687     update();
00688 }
00689 
00690 MarkerAttributes Legend::markerAttributes( uint dataset ) const
00691 {
00692     if( d->markerAttributes.find( dataset ) != d->markerAttributes.end() )
00693         return d->markerAttributes[ dataset ];
00694     else if ( static_cast<uint>( d->modelMarkers.count() ) > dataset )
00695         return d->modelMarkers[ dataset ];
00696     return MarkerAttributes();
00697 }
00698 
00699 const QMap<uint, MarkerAttributes> Legend::markerAttributes() const
00700 {
00701     return d->markerAttributes;
00702 }
00703 
00704 
00705 void Legend::setTextAttributes( const TextAttributes &a )
00706 {
00707     if( d->textAttributes == a ) return;
00708     d->textAttributes = a;
00709     setNeedRebuild();
00710 }
00711 
00712 TextAttributes Legend::textAttributes() const
00713 {
00714     return d->textAttributes;
00715 }
00716 
00717 void Legend::setTitleText( const QString& text )
00718 {
00719     if( d->titleText == text ) return;
00720     d->titleText = text;
00721     setNeedRebuild();
00722 }
00723 
00724 QString Legend::titleText() const
00725 {
00726     return d->titleText;
00727 }
00728 
00729 void Legend::setTitleTextAttributes( const TextAttributes &a )
00730 {
00731     if( d->titleTextAttributes == a ) return;
00732     d->titleTextAttributes = a;
00733     setNeedRebuild();
00734 }
00735 
00736 TextAttributes Legend::titleTextAttributes() const
00737 {
00738     return d->titleTextAttributes;
00739 }
00740 
00741 void Legend::forceRebuild()
00742 {
00743 #ifdef DEBUG_LEGEND_PAINT
00744     qDebug() << "entering Legend::forceRebuild()";
00745 #endif
00746     //setSpacing(d->layout->spacing());
00747     buildLegend();
00748 #ifdef DEBUG_LEGEND_PAINT
00749     qDebug() << "leaving Legend::forceRebuild()";
00750 #endif
00751 }
00752 
00753 void Legend::setSpacing( uint space )
00754 {
00755     if( d->spacing == space && d->layout->spacing() == static_cast<int>(space) ) return;
00756     d->spacing = space;
00757     d->layout->setSpacing( space );
00758     setNeedRebuild();
00759 }
00760 
00761 uint Legend::spacing() const
00762 {
00763     return d->spacing;
00764 }
00765 
00766 void Legend::setDefaultColors()
00767 {
00768     setColor(  0, Qt::red );
00769     setColor(  1, Qt::green );
00770     setColor(  2, Qt::blue );
00771     setColor(  3, Qt::cyan );
00772     setColor(  4, Qt::magenta );
00773     setColor(  5, Qt::yellow );
00774     setColor(  6, Qt::darkRed );
00775     setColor(  7, Qt::darkGreen );
00776     setColor(  8, Qt::darkBlue );
00777     setColor(  9, Qt::darkCyan );
00778     setColor( 10, Qt::darkMagenta );
00779     setColor( 11, Qt::darkYellow );
00780 }
00781 
00782 void Legend::setRainbowColors()
00783 {
00784     setColor(  0, QColor(255,  0,196) );
00785     setColor(  1, QColor(255,  0, 96) );
00786     setColor(  2, QColor(255, 128,64) );
00787     setColor(  3, Qt::yellow );
00788     setColor(  4, Qt::green );
00789     setColor(  5, Qt::cyan );
00790     setColor(  6, QColor( 96, 96,255) );
00791     setColor(  7, QColor(160,  0,255) );
00792     for( int i = 8; i < 16; ++i )
00793         setColor( i, brush( i - 8 ).color().light() );
00794 }
00795 
00796 void Legend::setSubduedColors( bool ordered )
00797 {
00798 static const int NUM_SUBDUEDCOLORS = 18;
00799 static const QColor SUBDUEDCOLORS[ NUM_SUBDUEDCOLORS ] = {
00800     QColor( 0xe0,0x7f,0x70 ),
00801     QColor( 0xe2,0xa5,0x6f ),
00802     QColor( 0xe0,0xc9,0x70 ),
00803     QColor( 0xd1,0xe0,0x70 ),
00804     QColor( 0xac,0xe0,0x70 ),
00805     QColor( 0x86,0xe0,0x70 ),
00806     QColor( 0x70,0xe0,0x7f ),
00807     QColor( 0x70,0xe0,0xa4 ),
00808     QColor( 0x70,0xe0,0xc9 ),
00809     QColor( 0x70,0xd1,0xe0 ),
00810     QColor( 0x70,0xac,0xe0 ),
00811     QColor( 0x70,0x86,0xe0 ),
00812     QColor( 0x7f,0x70,0xe0 ),
00813     QColor( 0xa4,0x70,0xe0 ),
00814     QColor( 0xc9,0x70,0xe0 ),
00815     QColor( 0xe0,0x70,0xd1 ),
00816     QColor( 0xe0,0x70,0xac ),
00817     QColor( 0xe0,0x70,0x86 ),
00818 };
00819     if( ordered )
00820         for(int i=0; i<NUM_SUBDUEDCOLORS; ++i)
00821             setColor( i, SUBDUEDCOLORS[i] );
00822     else{
00823         setColor( 0, SUBDUEDCOLORS[ 0] );
00824         setColor( 1, SUBDUEDCOLORS[ 5] );
00825         setColor( 2, SUBDUEDCOLORS[10] );
00826         setColor( 3, SUBDUEDCOLORS[15] );
00827         setColor( 4, SUBDUEDCOLORS[ 2] );
00828         setColor( 5, SUBDUEDCOLORS[ 7] );
00829         setColor( 6, SUBDUEDCOLORS[12] );
00830         setColor( 7, SUBDUEDCOLORS[17] );
00831         setColor( 8, SUBDUEDCOLORS[ 4] );
00832         setColor( 9, SUBDUEDCOLORS[ 9] );
00833         setColor(10, SUBDUEDCOLORS[14] );
00834         setColor(11, SUBDUEDCOLORS[ 1] );
00835         setColor(12, SUBDUEDCOLORS[ 6] );
00836         setColor(13, SUBDUEDCOLORS[11] );
00837         setColor(14, SUBDUEDCOLORS[16] );
00838         setColor(15, SUBDUEDCOLORS[ 3] );
00839         setColor(16, SUBDUEDCOLORS[ 8] );
00840         setColor(17, SUBDUEDCOLORS[13] );
00841     }
00842 }
00843 
00844 void Legend::resizeEvent ( QResizeEvent * event )
00845 {
00846     Q_UNUSED( event );
00847 #ifdef DEBUG_LEGEND_PAINT
00848     qDebug() << "Legend::resizeEvent() called";
00849 #endif
00850     forceRebuild();
00851     sizeHint();
00852     QTimer::singleShot(0, this, SLOT(emitPositionChanged()));
00853 }
00854 
00855 void Legend::buildLegend()
00856 {
00857     /*
00858     if( !d->needRebuild ) {
00859 #ifdef DEBUG_LEGEND_PAINT
00860         qDebug() << "leaving Legend::buildLegend() with NO action (was already build)";
00861 #endif
00862         // Note: We do *not* need to send positionChanged here,
00863         //       because we send it in the resizeEvent, so layouting
00864         //       is done at the right time.
00865         return;
00866     }
00867 #ifdef DEBUG_LEGEND_PAINT
00868     qDebug() << "entering Legend::buildLegend() **********************************";
00869 #endif
00870     d->needRebuild = false;
00871     */
00872 
00873     Q_FOREACH( QLayoutItem* layoutItem, d->layoutItems ) {
00874         d->layout->removeItem( layoutItem );
00875     }
00876     qDeleteAll( d->layoutItems );
00877     d->layoutItems.clear();
00878 
00879     if( orientation() == Qt::Vertical ) {
00880         d->layout->setColumnStretch( 6, 1 );
00881     } else {
00882         d->layout->setColumnStretch( 6, 0 );
00883     }
00884 
00885     d->modelLabels.clear();
00886     d->modelBrushes.clear();
00887     d->modelPens.clear();
00888     d->modelMarkers.clear();
00889     // retrieve the diagrams' settings for all non-hidden datasets
00890     for (int i = 0; i < d->observers.size(); ++i){
00891         const AbstractDiagram* diagram = d->observers.at(i)->diagram();
00892         if( diagram ){
00893             //qDebug() << "accessing" << diagram;
00894             const QStringList             diagramLabels(  diagram->datasetLabels()  );
00895             const QList<QBrush>           diagramBrushes( diagram->datasetBrushes() );
00896             const QList<QPen>             diagramPens(    diagram->datasetPens()    );
00897             const QList<MarkerAttributes> diagramMarkers( diagram->datasetMarkers() );
00898             const int begin = sortOrder() == Qt::AscendingOrder ? 0 : diagramLabels.count() - 1;
00899             const int end = sortOrder() == Qt::AscendingOrder ? diagramLabels.count() : -1;
00900             for ( int dataset = begin; dataset != end; dataset += begin < end ? 1 : -1 )
00901             {
00902                 // only show the label if the diagrams is NOT having the dataset set to hidden
00903                 // and the dataset is not hidden in this legend either
00904                 if( !diagram->isHidden( dataset ) && !datasetIsHidden( dataset ) ){
00905                     d->modelLabels  += diagramLabels[   dataset ];
00906                     d->modelBrushes += diagramBrushes[  dataset ];
00907                     d->modelPens    += diagramPens[     dataset ];
00908                     d->modelMarkers += diagramMarkers[  dataset ];
00909                 }
00910             }
00911         }
00912     }
00913 
00914     Q_ASSERT( d->modelLabels.count() == d->modelBrushes.count() );
00915 
00916     // legend caption
00917     if( !titleText().isEmpty() && titleTextAttributes().isVisible() ) {
00918         // PENDING(kalle) Other properties!
00919         KDChart::TextLayoutItem* titleItem =
00920             new KDChart::TextLayoutItem( titleText(),
00921                 titleTextAttributes(),
00922                 referenceArea(),
00923                 (orientation() == Qt::Vertical)
00924                 ? KDChartEnums::MeasureOrientationMinimum
00925                 : KDChartEnums::MeasureOrientationHorizontal,
00926                 d->textAlignment );
00927         titleItem->setParentWidget( this );
00928 
00929         d->layoutItems << titleItem;
00930         if( orientation() == Qt::Vertical )
00931             d->layout->addItem( titleItem, 0, 0, 1, 5, Qt::AlignCenter );
00932         else
00933             d->layout->addItem( titleItem, 0, 0, 1, d->modelLabels.count() ? (d->modelLabels.count()*4) : 1, Qt::AlignCenter );
00934 
00935         // The line between the title and the legend items, if any.
00936         if( showLines() && d->modelLabels.count() ) {
00937             KDChart::HorizontalLineLayoutItem* lineItem = new KDChart::HorizontalLineLayoutItem();
00938             d->layoutItems << lineItem;
00939             if( orientation() == Qt::Vertical ){
00940                 d->layout->addItem( lineItem, 1, 0, 1, 5, Qt::AlignCenter );
00941             }else{
00942                 // we have 1+count*4 columns, because we have both, a leading and a trailing spacer
00943                 d->layout->addItem( lineItem, 1, 0, 1, 1+d->modelLabels.count()*4, Qt::AlignCenter );
00944             }
00945         }
00946     }
00947 
00948     const KDChartEnums::MeasureOrientation orient =
00949             (orientation() == Qt::Vertical)
00950             ? KDChartEnums::MeasureOrientationMinimum
00951             : KDChartEnums::MeasureOrientationHorizontal;
00952     const TextAttributes labelAttrs( textAttributes() );
00953     const qreal fontHeight = labelAttrs.calculatedFontSize( referenceArea(), orient );
00954     const LegendStyle style = legendStyle();
00955     //qDebug() << "fontHeight:" << fontHeight;
00956 
00957     const bool bShowMarkers = (style != LinesOnly);
00958 
00959     QSizeF maxMarkersSize(1.0, 1.0);
00960     QVector <MarkerAttributes> markerAttrs( d->modelLabels.count() );
00961     if( bShowMarkers ){
00962         for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ) {
00963             markerAttrs[dataset] = markerAttributes( dataset );
00964             QSizeF siz;
00965             if( useAutomaticMarkerSize() ||
00966                 ! markerAttrs[dataset].markerSize().isValid() )
00967             {
00968                 siz = QSizeF(fontHeight, fontHeight);
00969                 markerAttrs[dataset].setMarkerSize( siz );
00970             }else{
00971                 siz = markerAttrs[dataset].markerSize();
00972             }
00973             maxMarkersSize =
00974                     QSizeF(qMax(maxMarkersSize.width(),  siz.width()),
00975                            qMax(maxMarkersSize.height(), siz.height()));
00976         }
00977     }
00978 
00979     // If we show a marker on a line, we paint it after 8 pixels
00980     // of the line have been painted. This allows to see the line style
00981     // at the right side of the marker without the line needing to
00982     // be too long.
00983     // (having the marker in the middle of the line would require longer lines)
00984     const int markerOffsOnLine = 8;
00985 
00986     int maxLineLength = 18;
00987     {
00988         bool hasComplexPenStyle = false;
00989         for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ){
00990             const QPen pn = pen(dataset);
00991             const Qt::PenStyle ps = pn.style();
00992             if( ps != Qt::NoPen ){
00993                 maxLineLength = qMax( pn.width() * 18, maxLineLength );
00994                 if( ps != Qt::SolidLine )
00995                     hasComplexPenStyle = true;
00996             }
00997         }
00998         if( hasComplexPenStyle && bShowMarkers )
00999             maxLineLength =
01000                     maxLineLength + markerOffsOnLine +
01001                     static_cast<int>(maxMarkersSize.width());
01002     }
01003 
01004 #define ADD_MARKER_SPACER_FOR_HORIZONTAL_MODE( column ) \
01005 { \
01006     if( orientation() != Qt::Vertical ) \
01007         d->layout->addItem( new QSpacerItem( spacing(), 1 ), \
01008                             2, \
01009                             column ); \
01010 }
01011 
01012     // Horizontal needs a leading spacer
01013     ADD_MARKER_SPACER_FOR_HORIZONTAL_MODE( 0 )
01014 
01015     // for all datasets: add (line)marker items and text items to the layout
01016     for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ) {
01017         KDChart::AbstractLayoutItem* markerLineItem = 0;
01018         // It is possible to set the marker brush both through the MarkerAttributes,
01019         // as well as through the dataset brush set in the diagram, whereas the
01020         // MarkerAttributes are preferred.
01021         const QBrush markerBrush = markerAttrs[dataset].markerColor().isValid() ?
01022                                    QBrush(markerAttrs[dataset].markerColor()) : brush( dataset );
01023         switch( style ){
01024             case( MarkersOnly ):
01025                 markerLineItem = new KDChart::MarkerLayoutItem(
01026                         diagram(),
01027                         markerAttrs[dataset],
01028                         markerBrush,
01029                         markerAttrs[dataset].pen(),
01030                         Qt::AlignLeft );
01031                 break;
01032             case( LinesOnly ):
01033                 markerLineItem = new KDChart::LineLayoutItem(
01034                         diagram(),
01035                         maxLineLength,
01036                         pen( dataset ),
01037                         Qt::AlignCenter );
01038                 break;
01039             case( MarkersAndLines ):
01040                 markerLineItem = new KDChart::LineWithMarkerLayoutItem(
01041                         diagram(),
01042                         maxLineLength,
01043                         pen( dataset ),
01044                         markerOffsOnLine,
01045                         markerAttrs[dataset],
01046                         markerBrush,
01047                         markerAttrs[dataset].pen(),
01048                         Qt::AlignCenter );
01049                 break;
01050             default:
01051                 Q_ASSERT( false ); // all styles need to be handled
01052         }
01053         if( markerLineItem ){
01054             d->layoutItems << markerLineItem;
01055             if( orientation() == Qt::Vertical )
01056                 d->layout->addItem( markerLineItem,
01057                                     dataset*2+2, // first row is title, second is line
01058                                     1,
01059                                     1, 1, Qt::AlignCenter );
01060             else
01061                 d->layout->addItem( markerLineItem,
01062                                     2, // all in row two
01063                                     dataset*4+1 );
01064         }
01065 
01066         // PENDING(kalle) Other properties!
01067         KDChart::TextLayoutItem* labelItem =
01068             new KDChart::TextLayoutItem( text( dataset ),
01069                 labelAttrs,
01070                 referenceArea(), orient,
01071                 d->textAlignment );
01072         labelItem->setParentWidget( this );
01073 
01074         d->layoutItems << labelItem;
01075         if( orientation() == Qt::Vertical )
01076             d->layout->addItem( labelItem,
01077                                 dataset*2+2, // first row is title, second is line
01078                                 3 );
01079         else
01080             d->layout->addItem( labelItem,
01081                                 2, // all in row two
01082                                 dataset*4+2 );
01083 
01084         // horizontal lines (only in vertical mode, and not after the last item)
01085         if( orientation() == Qt::Vertical && showLines() && dataset != d->modelLabels.count()-1 ) {
01086             KDChart::HorizontalLineLayoutItem* lineItem = new KDChart::HorizontalLineLayoutItem();
01087             d->layoutItems << lineItem;
01088             d->layout->addItem( lineItem,
01089                                 dataset*2+1+2,
01090                                 0,
01091                                 1, 5, Qt::AlignCenter );
01092         }
01093 
01094         // vertical lines (only in horizontal mode, and not after the last item)
01095         if( orientation() == Qt::Horizontal && showLines() && dataset != d->modelLabels.count()-1 ) {
01096             KDChart::VerticalLineLayoutItem* lineItem = new KDChart::VerticalLineLayoutItem();
01097             d->layoutItems << lineItem;
01098             d->layout->addItem( lineItem,
01099                                 2, // all in row two
01100                                 style == MarkersAndLines ? dataset*4+4 : dataset*4+3 ,
01101                                 1, 1, Qt::AlignCenter );
01102         }
01103 
01104         // Horizontal needs a spacer
01105         ADD_MARKER_SPACER_FOR_HORIZONTAL_MODE( dataset*4+4 )
01106     }
01107 
01108     // vertical line (only in vertical mode)
01109     if( orientation() == Qt::Vertical && showLines() && d->modelLabels.count() ) {
01110         KDChart::VerticalLineLayoutItem* lineItem = new KDChart::VerticalLineLayoutItem();
01111         d->layoutItems << lineItem;
01112         d->layout->addItem( lineItem, 2, 2, d->modelLabels.count()*2, 1 );
01113     }
01114 
01115     // This line is absolutely necessary, otherwise: #2516.
01116     activateTheLayout();
01117 
01118     emit propertiesChanged();
01119     //emit positionChanged( this );
01120     //emitPositionChanged();
01121 #ifdef DEBUG_LEGEND_PAINT
01122     qDebug() << "leaving Legend::buildLegend()";
01123 #endif
01124 }
01125 
01126 void Legend::setHiddenDatasets( const QList<uint> hiddenDatasets )
01127 {
01128     d->hiddenDatasets = hiddenDatasets;
01129 }
01130 
01131 const QList<uint> Legend::hiddenDatasets() const
01132 {
01133     return d->hiddenDatasets;
01134 }
01135 
01136 void Legend::setDatasetHidden( uint dataset, bool hidden )
01137 {
01138     if( hidden && !d->hiddenDatasets.contains( dataset ) )
01139         d->hiddenDatasets.append( dataset );
01140     else if( !hidden && d->hiddenDatasets.contains( dataset ) )
01141         d->hiddenDatasets.removeAll( dataset );
01142 }
01143 
01144 bool Legend::datasetIsHidden( uint dataset ) const
01145 {
01146     return d->hiddenDatasets.contains( dataset );
01147 }
01148 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Defines

Klarälvdalens Datakonsult AB (KDAB)
Qt-related services and products
http://www.kdab.com/
http://www.kdab.com/products/kd-chart/