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