KDChartLegend.cpp

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

Generated on Thu Mar 4 23:19:11 2010 for KD Chart 2 by  doxygen 1.5.4