KD Chart 2
[rev.2.5]
|
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