KD Chart 2  [rev.2.5.1]
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Pages
KDChartLegend.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Copyright (C) 2001-2013 Klaralvdalens Datakonsult AB. All rights reserved.
3 **
4 ** This file is part of the KD Chart library.
5 **
6 ** Licensees holding valid commercial KD Chart licenses may use this file in
7 ** accordance with the KD Chart Commercial License Agreement provided with
8 ** the Software.
9 **
10 **
11 ** This file may be distributed and/or modified under the terms of the
12 ** GNU General Public License version 2 and version 3 as published by the
13 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
14 **
15 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17 **
18 ** Contact info@kdab.com if any conditions of this licensing are not
19 ** clear to you.
20 **
21 **********************************************************************/
22 
23 #include "KDChartLegend.h"
24 #include "KDChartLegend_p.h"
25 #include <KDChartTextAttributes.h>
27 #include <KDChartPalette.h>
28 #include <KDChartAbstractDiagram.h>
29 #include "KDTextDocument.h"
30 #include <KDChartDiagramObserver.h>
31 #include "KDChartLayoutItems.h"
32 
33 #include <QFont>
34 #include <QGridLayout>
35 #include <QPainter>
36 #include <QTextTableCell>
37 #include <QTextCursor>
38 #include <QTextCharFormat>
39 #include <QTextDocumentFragment>
40 #include <QTimer>
41 #include <QAbstractTextDocumentLayout>
42 #include <QtDebug>
43 #include <QLabel>
44 
45 #include <KDABLibFakes>
46 
47 using namespace KDChart;
48 
49 Legend::Private::Private() :
50  referenceArea(0),
51  position( Position::East ),
52  alignment( Qt::AlignCenter ),
53  textAlignment( Qt::AlignCenter ),
54  relativePosition( RelativePosition() ),
55  orientation( Qt::Vertical ),
56  order( Qt::AscendingOrder ),
57  showLines( false ),
58  texts(),
59  textAttributes(),
60  titleText( QObject::tr( "Legend" ) ),
61  titleTextAttributes(),
62  spacing( 1 ),
63  useAutomaticMarkerSize( true ),
64  legendStyle( MarkersOnly )
65  //needRebuild( true )
66 {
67  // By default we specify a simple, hard point as the 'relative' position's ref. point,
68  // since we can not be sure that there will be any parent specified for the legend.
69  relativePosition.setReferencePoints( PositionPoints( QPointF( 0.0, 0.0 ) ) );
70  relativePosition.setReferencePosition( Position::NorthWest );
71  relativePosition.setAlignment( Qt::AlignTop | Qt::AlignLeft );
72  relativePosition.setHorizontalPadding( KDChart::Measure( 4.0, KDChartEnums::MeasureCalculationModeAbsolute ) );
73  relativePosition.setVerticalPadding( KDChart::Measure( 4.0, KDChartEnums::MeasureCalculationModeAbsolute ) );
74 }
75 
76 Legend::Private::~Private()
77 {
78  // this bloc left empty intentionally
79 }
80 
81 
82 #define d d_func()
83 
84 
85 Legend::Legend( QWidget* parent ) :
86  AbstractAreaWidget( new Private(), parent )
87 {
88  d->referenceArea = parent;
89  init();
90 }
91 
93  AbstractAreaWidget( new Private(), parent )
94 {
95  d->referenceArea = parent;
96  init();
97  setDiagram( diagram );
98 }
99 
101 {
102  emit destroyedLegend( this );
103 }
104 
105 void Legend::init()
106 {
107  setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
108 
109  d->layout = new QGridLayout( this );
110  d->layout->setMargin( 2 );
111  d->layout->setSpacing( d->spacing );
112  //setLayout( d->layout );
113 
114  const Measure normalFontSizeTitle( 12, KDChartEnums::MeasureCalculationModeAbsolute );
115  const Measure normalFontSizeLabels( 10, KDChartEnums::MeasureCalculationModeAbsolute );
116  const Measure minimalFontSize( 4, KDChartEnums::MeasureCalculationModeAbsolute );
117 
118  TextAttributes textAttrs;
119  textAttrs.setPen( QPen( Qt::black ) );
120  textAttrs.setFont( QFont( QLatin1String( "helvetica" ), 10, QFont::Normal, false ) );
121  textAttrs.setFontSize( normalFontSizeLabels );
122  textAttrs.setMinimalFontSize( minimalFontSize );
123  setTextAttributes( textAttrs );
124 
125  TextAttributes titleTextAttrs;
126  titleTextAttrs.setPen( QPen( Qt::black ) );
127  titleTextAttrs.setFont( QFont( QLatin1String( "helvetica" ), 12, QFont::Bold, false ) );
128  titleTextAttrs.setFontSize( normalFontSizeTitle );
129  titleTextAttrs.setMinimalFontSize( minimalFontSize );
130  setTitleTextAttributes( titleTextAttrs );
131 
132  FrameAttributes frameAttrs;
133  frameAttrs.setVisible( true );
134  frameAttrs.setPen( QPen( Qt::black ) );
135  frameAttrs.setPadding( 1 );
136  setFrameAttributes( frameAttrs );
137 
138  d->position = Position::NorthEast;
139  d->alignment = Qt::AlignCenter;
140 }
141 
142 
144 {
145  return sizeHint();
146 }
147 
148 //#define DEBUG_LEGEND_PAINT
149 
150 QSize Legend::sizeHint() const
151 {
152 #ifdef DEBUG_LEGEND_PAINT
153  qDebug() << "Legend::sizeHint() started";
154 #endif
155  Q_FOREACH( KDChart::AbstractLayoutItem* layoutItem, d->layoutItems ) {
156  layoutItem->sizeHint();
157  }
158  return AbstractAreaWidget::sizeHint();
159 }
160 
162 {
163  // Re-build the Legend's content, if it has not been build yet,
164  // or if the Legend's geometry has changed, resp.
165  buildLegend();
166 }
167 
168 void Legend::resizeLayout( const QSize& size )
169 {
170 #ifdef DEBUG_LEGEND_PAINT
171  qDebug() << "Legend::resizeLayout started";
172 #endif
173  if ( d->layout ) {
174  d->layout->setGeometry( QRect(QPoint(0,0), size) );
175  activateTheLayout();
176  }
177 #ifdef DEBUG_LEGEND_PAINT
178  qDebug() << "Legend::resizeLayout done";
179 #endif
180 }
181 
182 void Legend::activateTheLayout()
183 {
184  if ( d->layout && d->layout->parent() )
185  d->layout->activate();
186 }
187 
188 
190 {
191  if ( d->legendStyle == style ) return;
192  d->legendStyle = style;
193  setNeedRebuild();
194 }
195 
197 {
198  return d->legendStyle;
199 }
200 
205 {
206  Legend* legend = new Legend( new Private( *d ), 0 );
207  legend->setTextAttributes( textAttributes() );
209  legend->setFrameAttributes( frameAttributes() );
211  legend->setPosition( position() );
212  legend->setAlignment( alignment() );
213  legend->setTextAlignment( textAlignment() );
214  legend->setLegendStyle( legendStyle() );
215  return legend;
216 }
217 
218 
219 bool Legend::compare( const Legend* other ) const
220 {
221  if ( other == this ) return true;
222  if ( !other ) {
223  return false;
224  }
225 
226  return ( static_cast<const AbstractAreaBase*>(this)->compare( other ) ) &&
227  (isVisible() == other->isVisible()) &&
228  (position() == other->position()) &&
229  (alignment() == other->alignment())&&
230  (textAlignment() == other->textAlignment())&&
231  (floatingPosition() == other->floatingPosition()) &&
232  (orientation() == other->orientation())&&
233  (showLines() == other->showLines())&&
234  (texts() == other->texts())&&
235  (brushes() == other->brushes())&&
236  (pens() == other->pens())&&
237  (markerAttributes() == other->markerAttributes())&&
239  (textAttributes() == other->textAttributes()) &&
240  (titleText() == other->titleText())&&
241  (titleTextAttributes() == other->titleTextAttributes()) &&
242  (spacing() == other->spacing()) &&
243  (legendStyle() == other->legendStyle());
244 }
245 
246 
247 void Legend::paint( QPainter* painter )
248 {
249 #ifdef DEBUG_LEGEND_PAINT
250  qDebug() << "entering Legend::paint( QPainter* painter )";
251 #endif
252  if ( ! diagram() ) {
253  return;
254  }
255 
256  // re-calculate/adjust the Legend's internal layout and contents, if needed:
257  //buildLegend();
258 
259  // PENDING(kalle) Support palette
260 
261  Q_FOREACH( KDChart::AbstractLayoutItem* layoutItem, d->layoutItems ) {
262  layoutItem->paint( painter );
263  }
264 #ifdef DEBUG_LEGEND_PAINT
265  qDebug() << "leaving Legend::paint( QPainter* painter )";
266 #endif
267 }
268 
269 
271 {
272  int modelLabelsCount = 0;
273  KDAB_FOREACH ( DiagramObserver* observer, d->observers ) {
274  AbstractDiagram* diagram = observer->diagram();
275  Q_ASSERT( diagram->datasetLabels().count() == diagram->datasetBrushes().count() );
276  modelLabelsCount += diagram->datasetLabels().count();
277  }
278  return modelLabelsCount;
279 }
280 
281 
283 {
284  if ( area == d->referenceArea ) return;
285  d->referenceArea = area;
286  setNeedRebuild();
287 }
288 
290 {
291  return d->referenceArea ? d->referenceArea : qobject_cast< const QWidget* >( parent() );
292 }
293 
294 
296 {
297  if ( d->observers.isEmpty() )
298  return 0;
299  return d->observers.first()->diagram();
300 }
301 
303 {
304  DiagramList list;
305  for (int i = 0; i < d->observers.size(); ++i)
306  list << d->observers.at(i)->diagram();
307  return list;
308 }
309 
311 {
312  ConstDiagramList list;
313  for (int i = 0; i < d->observers.size(); ++i)
314  list << d->observers.at(i)->diagram();
315  return list;
316 }
317 
319 {
320  if ( newDiagram )
321  {
322  DiagramObserver* observer = new DiagramObserver( newDiagram, this );
323 
324  DiagramObserver* oldObs = d->findObserverForDiagram( newDiagram );
325  if ( oldObs ) {
326  delete oldObs;
327  d->observers[ d->observers.indexOf( oldObs ) ] = observer;
328  } else {
329  d->observers.append( observer );
330  }
331  connect( observer, SIGNAL( diagramAboutToBeDestroyed(AbstractDiagram*) ),
332  SLOT( resetDiagram(AbstractDiagram*) ));
333  connect( observer, SIGNAL( diagramDataChanged(AbstractDiagram*) ),
334  SLOT( setNeedRebuild() ));
335  connect( observer, SIGNAL( diagramDataHidden(AbstractDiagram*) ),
336  SLOT( setNeedRebuild() ));
337  connect( observer, SIGNAL( diagramAttributesChanged(AbstractDiagram*) ),
338  SLOT( setNeedRebuild() ));
339  setNeedRebuild();
340  }
341 }
342 
344 {
345  int datasetBrushOffset = 0;
347  for (int i =0; i <diagrams.count(); i++)
348  {
349  if (diagrams.at(i) == oldDiagram)
350  {
351  for ( int i = 0; i < oldDiagram->datasetBrushes().count(); i++ ) {
352  d->brushes.remove(datasetBrushOffset + i);
353  d->texts.remove(datasetBrushOffset + i);
354  }
355  for ( int i = 0; i < oldDiagram->datasetPens().count(); i++ ) {
356  d->pens.remove(datasetBrushOffset + i);
357  }
358  break;
359  }
360  datasetBrushOffset += diagrams.at(i)->datasetBrushes().count();
361  }
362 
363  if ( oldDiagram ) {
364  DiagramObserver* oldObs = d->findObserverForDiagram( oldDiagram );
365  if ( oldObs ) {
366  delete oldObs;
367  d->observers.removeAt( d->observers.indexOf( oldObs ) );
368  }
369  setNeedRebuild();
370  }
371 }
372 
374 {
375  // removeDiagram() may change the d->observers list. So, build up the list of
376  // diagrams to remove first and then remove them one by one.
378  for (int i = 0; i < d->observers.size(); ++i)
379  diagrams.append( d->observers.at(i)->diagram() );
380  for (int i = 0; i < diagrams.count(); ++i)
381  removeDiagram( diagrams[i] );
382 }
383 
385  AbstractDiagram* oldDiagram )
386 {
387  KDChart::AbstractDiagram* old = oldDiagram;
388  if ( ! d->observers.isEmpty() && ! old ) {
389  old = d->observers.first()->diagram();
390  if ( ! old )
391  d->observers.removeFirst(); // first entry had a 0 diagram
392  }
393  if ( old )
394  removeDiagram( old );
395  if ( newDiagram )
396  addDiagram( newDiagram );
397 }
398 
400 {
401  uint offset = 0;
402 
403  for (int i = 0; i < d->observers.count(); ++i)
404  {
405  if (d->observers.at(i)->diagram() == diagram)
406  return offset;
407 
408  KDChart::AbstractDiagram* diagram = d->observers.at(i)->diagram();
409  if (!diagram->model())
410  continue;
411 
412  offset = offset + diagram->model()->columnCount();
413  }
414 
415  return offset;
416 }
417 
419 {
420  replaceDiagram( newDiagram );
421 }
422 
423 void Legend::resetDiagram( AbstractDiagram* oldDiagram )
424 {
425  removeDiagram( oldDiagram );
426 }
427 
428 void Legend::setVisible( bool visible )
429 {
430  // do NOT bail out if visible != isVisible(), because the return value of isVisible() also depends
431  // on the visibility of the parent.
432  QWidget::setVisible( visible );
433  emitPositionChanged();
434 }
435 
436 void Legend::setNeedRebuild()
437 {
438  buildLegend();
439  sizeHint();
440 }
441 
443 {
444  if ( d->position == position )
445  return;
446  d->position = position;
447  emitPositionChanged();
448 }
449 
450 void Legend::emitPositionChanged()
451 {
452  emit positionChanged( this );
453  emit propertiesChanged();
454 }
455 
456 
458 {
459  return d->position;
460 }
461 
462 void Legend::setAlignment( Qt::Alignment alignment )
463 {
464  if ( d->alignment == alignment )
465  return;
466  d->alignment = alignment;
467  emitPositionChanged();
468 }
469 
470 Qt::Alignment Legend::alignment() const
471 {
472  return d->alignment;
473 }
474 
475 void Legend::setTextAlignment( Qt::Alignment alignment )
476 {
477  if ( d->textAlignment == alignment )
478  return;
479  d->textAlignment = alignment;
480  emitPositionChanged();
481 }
482 
483 Qt::Alignment Legend::textAlignment() const
484 {
485  return d->textAlignment;
486 }
487 
488 void Legend::setLegendSymbolAlignment(Qt::Alignment alignment)
489 {
490  if (d->legendLineSymbolAlignment == alignment)
491  return;
492 
493  d->legendLineSymbolAlignment = alignment;
494  emitPositionChanged();
495 }
496 
497 Qt::Alignment Legend::legendSymbolAlignment() const
498 {
499  return d->legendLineSymbolAlignment ;
500 }
501 
502 void Legend::setFloatingPosition( const RelativePosition& relativePosition )
503 {
504  d->position = Position::Floating;
505  if ( d->relativePosition != relativePosition ) {
506  d->relativePosition = relativePosition;
507  emitPositionChanged();
508  }
509 }
510 
512 {
513  return d->relativePosition;
514 }
515 
516 void Legend::setOrientation( Qt::Orientation orientation )
517 {
518  if ( d->orientation == orientation ) return;
519  d->orientation = orientation;
520  setNeedRebuild();
521  emitPositionChanged();
522 }
523 
524 Qt::Orientation Legend::orientation() const
525 {
526  return d->orientation;
527 }
528 
529 void Legend::setSortOrder( Qt::SortOrder order )
530 {
531  if ( d->order == order )
532  return;
533  d->order = order;
534  setNeedRebuild();
535  emitPositionChanged();
536 }
537 
538 Qt::SortOrder Legend::sortOrder() const
539 {
540  return d->order;
541 }
542 
543 void Legend::setShowLines( bool legendShowLines )
544 {
545  if ( d->showLines == legendShowLines ) return;
546  d->showLines = legendShowLines;
547  setNeedRebuild();
548  emitPositionChanged();
549 }
550 
551 bool Legend::showLines() const
552 {
553  return d->showLines;
554 }
555 
556 void Legend::setUseAutomaticMarkerSize( bool useAutomaticMarkerSize )
557 {
558  d->useAutomaticMarkerSize = useAutomaticMarkerSize;
559  setNeedRebuild();
560  emitPositionChanged();
561 }
562 
564 {
565  return d->useAutomaticMarkerSize;
566 }
567 
574 {
575  if ( ! d->texts.count() ) return;
576  d->texts.clear();
577  setNeedRebuild();
578 }
579 
580 void Legend::setText( uint dataset, const QString& text )
581 {
582  if ( d->texts[ dataset ] == text ) return;
583  d->texts[ dataset ] = text;
584  setNeedRebuild();
585 }
586 
587 QString Legend::text( uint dataset ) const
588 {
589  if ( d->texts.find( dataset ) != d->texts.end() ) {
590  return d->texts[ dataset ];
591  } else {
592  return d->modelLabels[ dataset ];
593  }
594 }
595 
597 {
598  return d->texts;
599 }
600 
601 void Legend::setColor( uint dataset, const QColor& color )
602 {
603  if ( d->brushes[ dataset ] != color ) {
604  d->brushes[ dataset ] = color;
605  setNeedRebuild();
606  update();
607  }
608 }
609 
610 void Legend::setBrush( uint dataset, const QBrush& brush )
611 {
612  if ( d->brushes[ dataset ] != brush ) {
613  d->brushes[ dataset ] = brush;
614  setNeedRebuild();
615  update();
616  }
617 }
618 
619 QBrush Legend::brush( uint dataset ) const
620 {
621  if ( d->brushes.contains( dataset ) ) {
622  return d->brushes[ dataset ];
623  } else {
624  return d->modelBrushes[ dataset ];
625  }
626 }
627 
629 {
630  return d->brushes;
631 }
632 
633 
635 {
636  bool bChangesDone = false;
637  QList<QBrush> datasetBrushes = diagram->datasetBrushes();
638  for ( int i = 0; i < datasetBrushes.count(); i++ ) {
639  if ( d->brushes[ i ] != datasetBrushes[ i ] ) {
640  d->brushes[ i ] = datasetBrushes[ i ];
641  bChangesDone = true;
642  }
643  }
644  if ( bChangesDone ) {
645  setNeedRebuild();
646  update();
647  }
648 }
649 
650 
651 void Legend::setPen( uint dataset, const QPen& pen )
652 {
653  if ( d->pens[dataset] == pen ) return;
654  d->pens[dataset] = pen;
655  setNeedRebuild();
656  update();
657 }
658 
659 QPen Legend::pen( uint dataset ) const
660 {
661  if ( d->pens.find( dataset ) != d->pens.end() )
662  return d->pens[dataset];
663  else
664  return d->modelPens[ dataset ];
665 }
666 
668 {
669  return d->pens;
670 }
671 
672 
673 void Legend::setMarkerAttributes( uint dataset, const MarkerAttributes& markerAttributes )
674 {
675  if ( d->markerAttributes[dataset] == markerAttributes ) return;
676  d->markerAttributes[ dataset ] = markerAttributes;
677  setNeedRebuild();
678  update();
679 }
680 
682 {
683  if ( d->markerAttributes.find( dataset ) != d->markerAttributes.end() )
684  return d->markerAttributes[ dataset ];
685  else if ( static_cast<uint>( d->modelMarkers.count() ) > dataset )
686  return d->modelMarkers[ dataset ];
687  return MarkerAttributes();
688 }
689 
691 {
692  return d->markerAttributes;
693 }
694 
695 
697 {
698  if ( d->textAttributes == a ) return;
699  d->textAttributes = a;
700  setNeedRebuild();
701 }
702 
704 {
705  return d->textAttributes;
706 }
707 
708 void Legend::setTitleText( const QString& text )
709 {
710  if ( d->titleText == text ) return;
711  d->titleText = text;
712  setNeedRebuild();
713 }
714 
715 QString Legend::titleText() const
716 {
717  return d->titleText;
718 }
719 
721 {
722  if ( d->titleTextAttributes == a ) return;
723  d->titleTextAttributes = a;
724  setNeedRebuild();
725 }
726 
728 {
729  return d->titleTextAttributes;
730 }
731 
733 {
734 #ifdef DEBUG_LEGEND_PAINT
735  qDebug() << "entering Legend::forceRebuild()";
736 #endif
737  //setSpacing(d->layout->spacing());
738  buildLegend();
739 #ifdef DEBUG_LEGEND_PAINT
740  qDebug() << "leaving Legend::forceRebuild()";
741 #endif
742 }
743 
744 void Legend::setSpacing( uint space )
745 {
746  if ( d->spacing == space && d->layout->spacing() == static_cast<int>(space) ) return;
747  d->spacing = space;
748  d->layout->setSpacing( space );
749  setNeedRebuild();
750 }
751 
752 uint Legend::spacing() const
753 {
754  return d->spacing;
755 }
756 
758 {
760  for ( int i = 0; i < pal.size(); i++ ) {
761  setBrush( i, pal.getBrush( i ) );
762  }
763 }
764 
766 {
768  for ( int i = 0; i < pal.size(); i++ ) {
769  setBrush( i, pal.getBrush( i ) );
770  }
771 }
772 
773 void Legend::setSubduedColors( bool ordered )
774 {
776  if ( ordered ) {
777  for ( int i = 0; i < pal.size(); i++ ) {
778  setBrush( i, pal.getBrush( i ) );
779  }
780  } else {
781  static const int s_subduedColorsCount = 18;
782  Q_ASSERT( pal.size() >= s_subduedColorsCount );
783  static const int order[ s_subduedColorsCount ] = {
784  0, 5, 10, 15, 2, 7, 12, 17, 4,
785  9, 14, 1, 6, 11, 16, 3, 8, 13
786  };
787  for ( int i = 0; i < s_subduedColorsCount; i++ ) {
788  setBrush( i, pal.getBrush( order[i] ) );
789  }
790  }
791 }
792 
793 void Legend::resizeEvent ( QResizeEvent * event )
794 {
795  Q_UNUSED( event );
796 #ifdef DEBUG_LEGEND_PAINT
797  qDebug() << "Legend::resizeEvent() called";
798 #endif
799  forceRebuild();
800  sizeHint();
801  QTimer::singleShot(0, this, SLOT(emitPositionChanged()));
802 }
803 
804 void Legend::buildLegend()
805 {
806  Q_FOREACH( QLayoutItem* layoutItem, d->layoutItems ) {
807  d->layout->removeItem( layoutItem );
808  }
809  qDeleteAll( d->layoutItems );
810  d->layoutItems.clear();
811 
812  if ( orientation() == Qt::Vertical ) {
813  d->layout->setColumnStretch( 6, 1 );
814  } else {
815  d->layout->setColumnStretch( 6, 0 );
816  }
817 
818  d->modelLabels.clear();
819  d->modelBrushes.clear();
820  d->modelPens.clear();
821  d->modelMarkers.clear();
822  // retrieve the diagrams' settings for all non-hidden datasets
823  for ( int i = 0; i < d->observers.size(); ++i ) {
824  const AbstractDiagram* diagram = d->observers.at( i )->diagram();
825  if ( diagram ) {
826  const QStringList diagramLabels( diagram->datasetLabels() );
827  const QList<QBrush> diagramBrushes( diagram->datasetBrushes() );
828  const QList<QPen> diagramPens( diagram->datasetPens() );
829  const QList<MarkerAttributes> diagramMarkers( diagram->datasetMarkers() );
830  const int begin = sortOrder() == Qt::AscendingOrder ? 0 : diagramLabels.count() - 1;
831  const int end = sortOrder() == Qt::AscendingOrder ? diagramLabels.count() : -1;
832  for ( int dataset = begin; dataset != end; dataset += begin < end ? 1 : -1 ) {
833  // only show the label if the diagrams is NOT having the dataset set to hidden
834  // and the dataset is not hidden in this legend either
835  if ( !diagram->isHidden( dataset ) && !datasetIsHidden( dataset ) ) {
836  d->modelLabels += diagramLabels[ dataset ];
837  d->modelBrushes += diagramBrushes[ dataset ];
838  d->modelPens += diagramPens[ dataset ];
839  d->modelMarkers += diagramMarkers[ dataset ];
840  }
841  }
842  }
843  }
844 
845  Q_ASSERT( d->modelLabels.count() == d->modelBrushes.count() );
846 
847  // legend caption
848  if ( !titleText().isEmpty() && titleTextAttributes().isVisible() ) {
849  // PENDING(kalle) Other properties!
850  KDChart::TextLayoutItem* titleItem =
854  d->textAlignment );
855  titleItem->setParentWidget( this );
856 
857  d->layoutItems << titleItem;
858  if ( orientation() == Qt::Vertical )
859  d->layout->addItem( titleItem, 0, 0, 1, 5, Qt::AlignCenter );
860  else
861  d->layout->addItem( titleItem, 0, 0, 1, d->modelLabels.count() ? (d->modelLabels.count()*4) : 1, Qt::AlignCenter );
862 
863  // The line between the title and the legend items, if any.
864  if ( showLines() && d->modelLabels.count() ) {
866  d->layoutItems << lineItem;
867  if ( orientation() == Qt::Vertical ) {
868  d->layout->addItem( lineItem, 1, 0, 1, 5, Qt::AlignCenter );
869  } else {
870  // we have 1+count*4 columns, because we have both, a leading and a trailing spacer
871  d->layout->addItem( lineItem, 1, 0, 1, 1+d->modelLabels.count()*4, Qt::AlignCenter );
872  }
873  }
874  }
875 
876  const KDChartEnums::MeasureOrientation orient = orientation() == Qt::Vertical ?
879  const TextAttributes labelAttrs( textAttributes() );
880  qreal fontHeight = labelAttrs.calculatedFontSize( referenceArea(), orient );
881  const LegendStyle style = legendStyle();
882  QFont tmpFont = labelAttrs.font();
883  tmpFont.setPointSizeF( fontHeight );
884 
886  {
887  QFontMetricsF metr( tmpFont, GlobalMeasureScaling::paintDevice() );
888  fontHeight = metr.height();
889  }
890  else
891  {
892  QFontMetricsF metr( tmpFont );
893  fontHeight = metr.height();
894  }
895 
896 
897  const bool bShowMarkers = (style != LinesOnly);
898 
899  QSizeF maxMarkersSize(1.0, 1.0);
900  QVector <MarkerAttributes> markerAttrs( d->modelLabels.count() );
901  if ( bShowMarkers ) {
902  for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ) {
903  markerAttrs[dataset] = markerAttributes( dataset );
904  QSizeF siz;
905  if ( useAutomaticMarkerSize() ||
906  ! markerAttrs[dataset].markerSize().isValid() )
907  {
908  siz = QSizeF(fontHeight, fontHeight);
909  markerAttrs[dataset].setMarkerSize( siz );
910  } else {
911  siz = markerAttrs[dataset].markerSize();
912  }
913  maxMarkersSize =
914  QSizeF(qMax(maxMarkersSize.width(), siz.width()),
915  qMax(maxMarkersSize.height(), siz.height()));
916  }
917  }
918 
919  // If we show a marker on a line, we paint it after 8 pixels
920  // of the line have been painted. This allows to see the line style
921  // at the right side of the marker without the line needing to
922  // be too long.
923  // (having the marker in the middle of the line would require longer lines)
924  const int markerOffsOnLine = 8;
925 
926  int maxLineLength = 18;
927  {
928  bool hasComplexPenStyle = false;
929  for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ) {
930  const QPen pn = pen(dataset);
931  const Qt::PenStyle ps = pn.style();
932  if ( ps != Qt::NoPen ) {
933  maxLineLength = qMax( pn.width() * 18, maxLineLength );
934  if ( ps != Qt::SolidLine )
935  hasComplexPenStyle = true;
936  }
937  }
938  if ( hasComplexPenStyle && bShowMarkers )
939  maxLineLength =
940  maxLineLength + markerOffsOnLine +
941  static_cast<int>(maxMarkersSize.width());
942  }
943 
944  // Horizontal needs a leading spacer
945  if ( orientation() != Qt::Vertical ) {
946  d->layout->addItem( new QSpacerItem( spacing(), 1 ), 2, 0 );
947  }
948 
949  // for all datasets: add (line)marker items and text items to the layout
950  for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ) {
951  KDChart::AbstractLayoutItem* markerLineItem = 0;
952  // It is possible to set the marker brush both through the MarkerAttributes,
953  // as well as through the dataset brush set in the diagram, whereas the
954  // MarkerAttributes are preferred.
955  const QBrush markerBrush = markerAttrs[dataset].markerColor().isValid() ?
956  QBrush(markerAttrs[dataset].markerColor()) : brush( dataset );
957  switch ( style ) {
958  case( MarkersOnly ):
959  markerLineItem = new KDChart::MarkerLayoutItem(
960  diagram(),
961  markerAttrs[dataset],
962  markerBrush,
963  markerAttrs[dataset].pen(),
964  Qt::AlignLeft );
965  break;
966  case( LinesOnly ):
967  markerLineItem = new KDChart::LineLayoutItem(
968  diagram(),
969  maxLineLength,
970  pen( dataset ),
971  d->legendLineSymbolAlignment,
972  Qt::AlignCenter );
973  break;
974  case( MarkersAndLines ):
975  markerLineItem = new KDChart::LineWithMarkerLayoutItem(
976  diagram(),
977  maxLineLength,
978  pen( dataset ),
979  markerOffsOnLine,
980  markerAttrs[dataset],
981  markerBrush,
982  markerAttrs[dataset].pen(),
983  Qt::AlignCenter );
984  break;
985  default:
986  Q_ASSERT( false ); // all styles need to be handled
987  }
988  if ( markerLineItem ) {
989  d->layoutItems << markerLineItem;
990  if ( orientation() == Qt::Vertical )
991  d->layout->addItem( markerLineItem,
992  dataset*2+2, // first row is title, second is line
993  1,
994  1, 1, Qt::AlignCenter );
995  else
996  d->layout->addItem( markerLineItem,
997  2, // all in row two
998  dataset*4+1 );
999  }
1000 
1001  // PENDING(kalle) Other properties!
1002  KDChart::TextLayoutItem* labelItem =
1003  new KDChart::TextLayoutItem( text( dataset ),
1004  labelAttrs,
1005  referenceArea(), orient,
1006  d->textAlignment );
1007  labelItem->setParentWidget( this );
1008 
1009  d->layoutItems << labelItem;
1010  if ( orientation() == Qt::Vertical )
1011  d->layout->addItem( labelItem,
1012  dataset*2+2, // first row is title, second is line
1013  3 );
1014  else
1015  d->layout->addItem( labelItem,
1016  2, // all in row two
1017  dataset*4+2 );
1018 
1019  // horizontal lines (only in vertical mode, and not after the last item)
1020  if ( orientation() == Qt::Vertical && showLines() && dataset != d->modelLabels.count()-1 ) {
1022  d->layoutItems << lineItem;
1023  d->layout->addItem( lineItem,
1024  dataset*2+1+2,
1025  0,
1026  1, 5, Qt::AlignCenter );
1027  }
1028 
1029  // vertical lines (only in horizontal mode, and not after the last item)
1030  if ( orientation() == Qt::Horizontal && showLines() && dataset != d->modelLabels.count()-1 ) {
1032  d->layoutItems << lineItem;
1033  d->layout->addItem( lineItem,
1034  2, // all in row two
1035  style == MarkersAndLines ? dataset*4+4 : dataset*4+3 ,
1036  1, 1, Qt::AlignCenter );
1037  }
1038 
1039  // Horizontal needs a spacer
1040  if ( orientation() != Qt::Vertical ) {
1041  d->layout->addItem( new QSpacerItem( spacing(), 1 ), 2, dataset * 4 + 4 );
1042  }
1043  }
1044 
1045  // vertical line (only in vertical mode)
1046  if ( orientation() == Qt::Vertical && showLines() && d->modelLabels.count() ) {
1048  d->layoutItems << lineItem;
1049  d->layout->addItem( lineItem, 2, 2, d->modelLabels.count()*2, 1 );
1050  }
1051 
1052  // This line is absolutely necessary, otherwise: #2516.
1053  activateTheLayout();
1054 
1055  emit propertiesChanged();
1056 #ifdef DEBUG_LEGEND_PAINT
1057  qDebug() << "leaving Legend::buildLegend()";
1058 #endif
1059 }
1060 
1061 void Legend::setHiddenDatasets( const QList<uint> hiddenDatasets )
1062 {
1063  d->hiddenDatasets = hiddenDatasets;
1064 }
1065 
1067 {
1068  return d->hiddenDatasets;
1069 }
1070 
1071 void Legend::setDatasetHidden( uint dataset, bool hidden )
1072 {
1073  if ( hidden && !d->hiddenDatasets.contains( dataset ) )
1074  d->hiddenDatasets.append( dataset );
1075  else if ( !hidden && d->hiddenDatasets.contains( dataset ) )
1076  d->hiddenDatasets.removeAll( dataset );
1077 }
1078 
1079 bool Legend::datasetIsHidden( uint dataset ) const
1080 {
1081  return d->hiddenDatasets.contains( dataset );
1082 }

Klarälvdalens Datakonsult AB (KDAB)
Qt-related services and products
http://www.kdab.com/
http://www.kdab.com/products/kd-chart/