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