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     QFont tmpFont = labelAttrs.font();
00956     tmpFont.setPointSizeF( fontHeight );
00957 
00958     if ( GlobalMeasureScaling::paintDevice() )
00959     {
00960         QFontMetricsF metr( tmpFont, GlobalMeasureScaling::paintDevice() );
00961         fontHeight = metr.height();
00962     }
00963     else
00964     {
00965         QFontMetricsF metr( tmpFont );
00966         fontHeight = metr.height();
00967     }
00968 
00969 
00970     const bool bShowMarkers = (style != LinesOnly);
00971 
00972     QSizeF maxMarkersSize(1.0, 1.0);
00973     QVector <MarkerAttributes> markerAttrs( d->modelLabels.count() );
00974     if( bShowMarkers ){
00975         for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ) {
00976             markerAttrs[dataset] = markerAttributes( dataset );
00977             QSizeF siz;
00978             if( useAutomaticMarkerSize() ||
00979                 ! markerAttrs[dataset].markerSize().isValid() )
00980             {
00981                 siz = QSizeF(fontHeight, fontHeight);
00982                 markerAttrs[dataset].setMarkerSize( siz );
00983             }else{
00984                 siz = markerAttrs[dataset].markerSize();
00985             }
00986             maxMarkersSize =
00987                     QSizeF(qMax(maxMarkersSize.width(),  siz.width()),
00988                            qMax(maxMarkersSize.height(), siz.height()));
00989         }
00990     }
00991 
00992     // If we show a marker on a line, we paint it after 8 pixels
00993     // of the line have been painted. This allows to see the line style
00994     // at the right side of the marker without the line needing to
00995     // be too long.
00996     // (having the marker in the middle of the line would require longer lines)
00997     const int markerOffsOnLine = 8;
00998 
00999     int maxLineLength = 18;
01000     {
01001         bool hasComplexPenStyle = false;
01002         for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ){
01003             const QPen pn = pen(dataset);
01004             const Qt::PenStyle ps = pn.style();
01005             if( ps != Qt::NoPen ){
01006                 maxLineLength = qMax( pn.width() * 18, maxLineLength );
01007                 if( ps != Qt::SolidLine )
01008                     hasComplexPenStyle = true;
01009             }
01010         }
01011         if( hasComplexPenStyle && bShowMarkers )
01012             maxLineLength =
01013                     maxLineLength + markerOffsOnLine +
01014                     static_cast<int>(maxMarkersSize.width());
01015     }
01016 
01017 #define ADD_MARKER_SPACER_FOR_HORIZONTAL_MODE( column ) \
01018 { \
01019     if( orientation() != Qt::Vertical ) \
01020         d->layout->addItem( new QSpacerItem( spacing(), 1 ), \
01021                             2, \
01022                             column ); \
01023 }
01024 
01025     // Horizontal needs a leading spacer
01026     ADD_MARKER_SPACER_FOR_HORIZONTAL_MODE( 0 )
01027 
01028     // for all datasets: add (line)marker items and text items to the layout
01029     for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ) {
01030         KDChart::AbstractLayoutItem* markerLineItem = 0;
01031         // It is possible to set the marker brush both through the MarkerAttributes,
01032         // as well as through the dataset brush set in the diagram, whereas the
01033         // MarkerAttributes are preferred.
01034         const QBrush markerBrush = markerAttrs[dataset].markerColor().isValid() ?
01035                                    QBrush(markerAttrs[dataset].markerColor()) : brush( dataset );
01036         switch( style ){
01037             case( MarkersOnly ):
01038                 markerLineItem = new KDChart::MarkerLayoutItem(
01039                         diagram(),
01040                         markerAttrs[dataset],
01041                         markerBrush,
01042                         markerAttrs[dataset].pen(),
01043                         Qt::AlignLeft );
01044                 break;
01045             case( LinesOnly ):
01046                 markerLineItem = new KDChart::LineLayoutItem(
01047                         diagram(),
01048                         maxLineLength,
01049                         pen( dataset ),
01050                         Qt::AlignCenter );
01051                 break;
01052             case( MarkersAndLines ):
01053                 markerLineItem = new KDChart::LineWithMarkerLayoutItem(
01054                         diagram(),
01055                         maxLineLength,
01056                         pen( dataset ),
01057                         markerOffsOnLine,
01058                         markerAttrs[dataset],
01059                         markerBrush,
01060                         markerAttrs[dataset].pen(),
01061                         Qt::AlignCenter );
01062                 break;
01063             default:
01064                 Q_ASSERT( false ); // all styles need to be handled
01065         }
01066         if( markerLineItem ){
01067             d->layoutItems << markerLineItem;
01068             if( orientation() == Qt::Vertical )
01069                 d->layout->addItem( markerLineItem,
01070                                     dataset*2+2, // first row is title, second is line
01071                                     1,
01072                                     1, 1, Qt::AlignCenter );
01073             else
01074                 d->layout->addItem( markerLineItem,
01075                                     2, // all in row two
01076                                     dataset*4+1 );
01077         }
01078 
01079         // PENDING(kalle) Other properties!
01080         KDChart::TextLayoutItem* labelItem =
01081             new KDChart::TextLayoutItem( text( dataset ),
01082                 labelAttrs,
01083                 referenceArea(), orient,
01084                 d->textAlignment );
01085         labelItem->setParentWidget( this );
01086 
01087         d->layoutItems << labelItem;
01088         if( orientation() == Qt::Vertical )
01089             d->layout->addItem( labelItem,
01090                                 dataset*2+2, // first row is title, second is line
01091                                 3 );
01092         else
01093             d->layout->addItem( labelItem,
01094                                 2, // all in row two
01095                                 dataset*4+2 );
01096 
01097         // horizontal lines (only in vertical mode, and not after the last item)
01098         if( orientation() == Qt::Vertical && showLines() && dataset != d->modelLabels.count()-1 ) {
01099             KDChart::HorizontalLineLayoutItem* lineItem = new KDChart::HorizontalLineLayoutItem();
01100             d->layoutItems << lineItem;
01101             d->layout->addItem( lineItem,
01102                                 dataset*2+1+2,
01103                                 0,
01104                                 1, 5, Qt::AlignCenter );
01105         }
01106 
01107         // vertical lines (only in horizontal mode, and not after the last item)
01108         if( orientation() == Qt::Horizontal && showLines() && dataset != d->modelLabels.count()-1 ) {
01109             KDChart::VerticalLineLayoutItem* lineItem = new KDChart::VerticalLineLayoutItem();
01110             d->layoutItems << lineItem;
01111             d->layout->addItem( lineItem,
01112                                 2, // all in row two
01113                                 style == MarkersAndLines ? dataset*4+4 : dataset*4+3 ,
01114                                 1, 1, Qt::AlignCenter );
01115         }
01116 
01117         // Horizontal needs a spacer
01118         ADD_MARKER_SPACER_FOR_HORIZONTAL_MODE( dataset*4+4 )
01119     }
01120 
01121     // vertical line (only in vertical mode)
01122     if( orientation() == Qt::Vertical && showLines() && d->modelLabels.count() ) {
01123         KDChart::VerticalLineLayoutItem* lineItem = new KDChart::VerticalLineLayoutItem();
01124         d->layoutItems << lineItem;
01125         d->layout->addItem( lineItem, 2, 2, d->modelLabels.count()*2, 1 );
01126     }
01127 
01128     // This line is absolutely necessary, otherwise: #2516.
01129     activateTheLayout();
01130 
01131     emit propertiesChanged();
01132     //emit positionChanged( this );
01133     //emitPositionChanged();
01134 #ifdef DEBUG_LEGEND_PAINT
01135     qDebug() << "leaving Legend::buildLegend()";
01136 #endif
01137 }
01138 
01139 void Legend::setHiddenDatasets( const QList<uint> hiddenDatasets )
01140 {
01141     d->hiddenDatasets = hiddenDatasets;
01142 }
01143 
01144 const QList<uint> Legend::hiddenDatasets() const
01145 {
01146     return d->hiddenDatasets;
01147 }
01148 
01149 void Legend::setDatasetHidden( uint dataset, bool hidden )
01150 {
01151     if( hidden && !d->hiddenDatasets.contains( dataset ) )
01152         d->hiddenDatasets.append( dataset );
01153     else if( !hidden && d->hiddenDatasets.contains( dataset ) )
01154         d->hiddenDatasets.removeAll( dataset );
01155 }
01156 
01157 bool Legend::datasetIsHidden( uint dataset ) const
01158 {
01159     return d->hiddenDatasets.contains( dataset );
01160 }
01161 
 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/