KChart

KChartLegend.cpp
1 /*
2  * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
3  *
4  * This file is part of the KD Chart library.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #include "KChartLegend.h"
21 #include "KChartLegend_p.h"
22 #include <KChartTextAttributes.h>
23 #include <KChartMarkerAttributes.h>
24 #include <KChartPalette.h>
25 #include <KChartAbstractDiagram.h>
26 #include "KTextDocument.h"
27 #include <KChartDiagramObserver.h>
28 #include "KChartLayoutItems.h"
29 #include "KChartPrintingParameters.h"
30 
31 #include <QFont>
32 #include <QGridLayout>
33 #include <QPainter>
34 #include <QTextTableCell>
35 #include <QTextCursor>
36 #include <QTextCharFormat>
37 #include <QTextDocumentFragment>
38 #include <QTimer>
39 #include <QAbstractTextDocumentLayout>
40 #include <QtDebug>
41 #include <QLabel>
42 
43 using namespace KChart;
44 
45 Legend::Private::Private() :
46  referenceArea( nullptr ),
47  position( Position::East ),
48  alignment( Qt::AlignCenter ),
49  textAlignment( Qt::AlignCenter ),
50  relativePosition( RelativePosition() ),
51  orientation( Qt::Vertical ),
52  order( Qt::AscendingOrder ),
53  showLines( false ),
54  titleText( QObject::tr( "Legend" ) ),
55  spacing( 1 ),
56  useAutomaticMarkerSize( true ),
57  legendStyle( MarkersOnly )
58 {
59  // By default we specify a simple, hard point as the 'relative' position's ref. point,
60  // since we can not be sure that there will be any parent specified for the legend.
61  relativePosition.setReferencePoints( PositionPoints( QPointF( 0.0, 0.0 ) ) );
62  relativePosition.setReferencePosition( Position::NorthWest );
63  relativePosition.setAlignment( Qt::AlignTop | Qt::AlignLeft );
64  relativePosition.setHorizontalPadding( Measure( 4.0, KChartEnums::MeasureCalculationModeAbsolute ) );
65  relativePosition.setVerticalPadding( Measure( 4.0, KChartEnums::MeasureCalculationModeAbsolute ) );
66 }
67 
68 Legend::Private::~Private()
69 {
70  // this bloc left empty intentionally
71 }
72 
73 
74 #define d d_func()
75 
76 
77 Legend::Legend( QWidget* parent ) :
78  AbstractAreaWidget( new Private(), parent )
79 {
80  d->referenceArea = parent;
81  init();
82 }
83 
84 Legend::Legend( AbstractDiagram* diagram, QWidget* parent ) :
85  AbstractAreaWidget( new Private(), parent )
86 {
87  d->referenceArea = parent;
88  init();
89  setDiagram( diagram );
90 }
91 
92 Legend::~Legend()
93 {
94  Q_EMIT destroyedLegend( this );
95 }
96 
97 void Legend::init()
98 {
99  setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
100 
101  d->layout = new QGridLayout( this );
102  d->layout->setContentsMargins( 2, 2, 2, 2 );
103  d->layout->setSpacing( d->spacing );
104 
105  const Measure normalFontSizeTitle( 12, KChartEnums::MeasureCalculationModeAbsolute );
106  const Measure normalFontSizeLabels( 10, KChartEnums::MeasureCalculationModeAbsolute );
107  const Measure minimalFontSize( 4, KChartEnums::MeasureCalculationModeAbsolute );
108 
109  TextAttributes textAttrs;
110  textAttrs.setPen( QPen( Qt::black ) );
111  textAttrs.setFont( QFont( QLatin1String( "helvetica" ), 10, QFont::Normal, false ) );
112  textAttrs.setFontSize( normalFontSizeLabels );
113  textAttrs.setMinimalFontSize( minimalFontSize );
114  setTextAttributes( textAttrs );
115 
116  TextAttributes titleTextAttrs;
117  titleTextAttrs.setPen( QPen( Qt::black ) );
118  titleTextAttrs.setFont( QFont( QLatin1String( "helvetica" ), 12, QFont::Bold, false ) );
119  titleTextAttrs.setFontSize( normalFontSizeTitle );
120  titleTextAttrs.setMinimalFontSize( minimalFontSize );
121  setTitleTextAttributes( titleTextAttrs );
122 
123  FrameAttributes frameAttrs;
124  frameAttrs.setVisible( true );
125  frameAttrs.setPen( QPen( Qt::black ) );
126  frameAttrs.setPadding( 1 );
127  setFrameAttributes( frameAttrs );
128 
129  d->position = Position::NorthEast;
130  d->alignment = Qt::AlignCenter;
131 }
132 
133 
134 QSize Legend::minimumSizeHint() const
135 {
136  return sizeHint();
137 }
138 
139 //#define DEBUG_LEGEND_PAINT
140 
141 QSize Legend::sizeHint() const
142 {
143 #ifdef DEBUG_LEGEND_PAINT
144  qDebug() << "Legend::sizeHint() started";
145 #endif
146  Q_FOREACH( AbstractLayoutItem* paintItem, d->paintItems ) {
147  paintItem->sizeHint();
148  }
150 }
151 
153 {
154  buildLegend();
155 }
156 
157 void Legend::resizeLayout( const QSize& size )
158 {
159 #ifdef DEBUG_LEGEND_PAINT
160  qDebug() << "Legend::resizeLayout started";
161 #endif
162  if ( d->layout ) {
163  d->reflowHDatasetItems( this );
164  d->layout->setGeometry( QRect(QPoint( 0,0 ), size) );
165  activateTheLayout();
166  }
167 #ifdef DEBUG_LEGEND_PAINT
168  qDebug() << "Legend::resizeLayout done";
169 #endif
170 }
171 
172 void Legend::activateTheLayout()
173 {
174  if ( d->layout && d->layout->parent() ) {
175  d->layout->activate();
176  }
177 }
178 
179 void Legend::setLegendStyle( LegendStyle style )
180 {
181  if ( d->legendStyle == style ) {
182  return;
183  }
184  d->legendStyle = style;
185  setNeedRebuild();
186 }
187 
188 Legend::LegendStyle Legend::legendStyle() const
189 {
190  return d->legendStyle;
191 }
192 
194 {
195  Legend* legend = new Legend( new Private( *d ), nullptr );
196  legend->setTextAttributes( textAttributes() );
197  legend->setTitleTextAttributes( titleTextAttributes() );
198  legend->setFrameAttributes( frameAttributes() );
199  legend->setUseAutomaticMarkerSize( useAutomaticMarkerSize() );
200  legend->setPosition( position() );
201  legend->setAlignment( alignment() );
202  legend->setTextAlignment( textAlignment() );
203  legend->setLegendStyle( legendStyle() );
204  return legend;
205 }
206 
207 
208 bool Legend::compare( const Legend* other ) const
209 {
210  if ( other == this ) {
211  return true;
212  }
213  if ( !other ) {
214  return false;
215  }
216 
217  return ( AbstractAreaBase::compare( other ) ) &&
218  (isVisible() == other->isVisible()) &&
219  (position() == other->position()) &&
220  (alignment() == other->alignment())&&
221  (textAlignment() == other->textAlignment())&&
222  (floatingPosition() == other->floatingPosition()) &&
223  (orientation() == other->orientation())&&
224  (showLines() == other->showLines())&&
225  (texts() == other->texts())&&
226  (brushes() == other->brushes())&&
227  (pens() == other->pens())&&
228  (markerAttributes() == other->markerAttributes())&&
229  (useAutomaticMarkerSize() == other->useAutomaticMarkerSize()) &&
230  (textAttributes() == other->textAttributes()) &&
231  (titleText() == other->titleText())&&
232  (titleTextAttributes() == other->titleTextAttributes()) &&
233  (spacing() == other->spacing()) &&
234  (legendStyle() == other->legendStyle());
235 }
236 
237 
238 void Legend::paint( QPainter* painter )
239 {
240 #ifdef DEBUG_LEGEND_PAINT
241  qDebug() << "entering Legend::paint( QPainter* painter )";
242 #endif
243  if ( !diagram() ) {
244  return;
245  }
246 
247  activateTheLayout();
248 
249  Q_FOREACH( AbstractLayoutItem* paintItem, d->paintItems ) {
250  paintItem->paint( painter );
251  }
252 
253 #ifdef DEBUG_LEGEND_PAINT
254  qDebug() << "leaving Legend::paint( QPainter* painter )";
255 #endif
256 }
257 
258 void Legend::paint( QPainter *painter, const QRect& rect )
259 {
260  if ( rect.isEmpty() ) {
261  return;
262  }
263  // set up the contents of the widget so we get a useful geometry
266 
267  const QRect oldGeometry(geometry() );
268  const QRect newGeo( QPoint(0,0), rect.size() );
269  if (oldGeometry != newGeo) {
270  setGeometry(newGeo);
271  needSizeHint();
272  }
273  painter->translate( rect.left(), rect.top() );
274  paintAll( *painter );
275  painter->translate( -rect.left(), -rect.top() );
276 
277  if (oldGeometry != newGeo) {
278  setGeometry(oldGeometry);
279  }
281 }
282 
283 uint Legend::datasetCount() const
284 {
285  int modelLabelsCount = 0;
286  Q_FOREACH ( DiagramObserver* observer, d->observers ) {
287  AbstractDiagram* diagram = observer->diagram();
288  Q_ASSERT( diagram->datasetLabels().count() == diagram->datasetBrushes().count() );
289  modelLabelsCount += diagram->datasetLabels().count();
290  }
291  return modelLabelsCount;
292 }
293 
294 
296 {
297  if ( area == d->referenceArea ) {
298  return;
299  }
300  d->referenceArea = area;
301  setNeedRebuild();
302 }
303 
305 {
306  return d->referenceArea ? d->referenceArea : qobject_cast< const QWidget* >( parent() );
307 }
308 
309 
311 {
312  if ( d->observers.isEmpty() ) {
313  return nullptr;
314  }
315  return d->observers.first()->diagram();
316 }
317 
319 {
320  DiagramList list;
321  for ( int i = 0; i < d->observers.size(); ++i ) {
322  list << d->observers.at(i)->diagram();
323  }
324  return list;
325 }
326 
328 {
329  ConstDiagramList list;
330  for ( int i = 0; i < d->observers.size(); ++i ) {
331  list << d->observers.at(i)->diagram();
332  }
333  return list;
334 }
335 
337 {
338  if ( newDiagram ) {
339  DiagramObserver* observer = new DiagramObserver( newDiagram, this );
340 
341  DiagramObserver* oldObs = d->findObserverForDiagram( newDiagram );
342  if ( oldObs ) {
343  delete oldObs;
344  d->observers[ d->observers.indexOf( oldObs ) ] = observer;
345  } else {
346  d->observers.append( observer );
347  }
348  connect( observer, SIGNAL(diagramAboutToBeDestroyed(AbstractDiagram*)),
349  SLOT(resetDiagram(AbstractDiagram*)));
350  connect( observer, SIGNAL(diagramDataChanged(AbstractDiagram*)),
351  SLOT(setNeedRebuild()));
352  connect( observer, SIGNAL(diagramDataHidden(AbstractDiagram*)),
353  SLOT(setNeedRebuild()));
354  connect( observer, SIGNAL(diagramAttributesChanged(AbstractDiagram*)),
355  SLOT(setNeedRebuild()));
356  setNeedRebuild();
357  }
358 }
359 
361 {
362  int datasetBrushOffset = 0;
363  QList< AbstractDiagram * > diagrams = this->diagrams();
364  for ( int i = 0; i <diagrams.count(); i++ ) {
365  if ( diagrams.at( i ) == oldDiagram ) {
366  for ( int i = 0; i < oldDiagram->datasetBrushes().count(); i++ ) {
367  d->brushes.remove(datasetBrushOffset + i);
368  d->texts.remove(datasetBrushOffset + i);
369  }
370  for ( int i = 0; i < oldDiagram->datasetPens().count(); i++ ) {
371  d->pens.remove(datasetBrushOffset + i);
372  }
373  break;
374  }
375  datasetBrushOffset += diagrams.at(i)->datasetBrushes().count();
376  }
377 
378  if ( oldDiagram ) {
379  DiagramObserver *oldObs = d->findObserverForDiagram( oldDiagram );
380  if ( oldObs ) {
381  delete oldObs;
382  d->observers.removeAt( d->observers.indexOf( oldObs ) );
383  }
384  setNeedRebuild();
385  }
386 }
387 
389 {
390  // removeDiagram() may change the d->observers list. So, build up the list of
391  // diagrams to remove first and then remove them one by one.
393  for ( int i = 0; i < d->observers.size(); ++i ) {
394  diagrams.append( d->observers.at( i )->diagram() );
395  }
396  for ( int i = 0; i < diagrams.count(); ++i ) {
397  removeDiagram( diagrams[ i ] );
398  }
399 }
400 
402  AbstractDiagram* oldDiagram )
403 {
404  AbstractDiagram* old = oldDiagram;
405  if ( !d->observers.isEmpty() && !old ) {
406  old = d->observers.first()->diagram();
407  if ( !old ) {
408  d->observers.removeFirst(); // first entry had a 0 diagram
409  }
410  }
411  if ( old ) {
412  removeDiagram( old );
413  }
414  if ( newDiagram ) {
415  addDiagram( newDiagram );
416  }
417 }
418 
420 {
421  uint offset = 0;
422 
423  for ( int i = 0; i < d->observers.count(); ++i ) {
424  if ( d->observers.at(i)->diagram() == diagram ) {
425  return offset;
426  }
427  AbstractDiagram* diagram = d->observers.at(i)->diagram();
428  if ( !diagram->model() ) {
429  continue;
430  }
431  offset = offset + diagram->model()->columnCount();
432  }
433 
434  return offset;
435 }
436 
438 {
439  replaceDiagram( newDiagram );
440 }
441 
442 void Legend::resetDiagram( AbstractDiagram* oldDiagram )
443 {
444  removeDiagram( oldDiagram );
445 }
446 
447 void Legend::setVisible( bool visible )
448 {
449  // do NOT bail out if visible == isVisible(), because the return value of isVisible() also depends
450  // on the visibility of the parent.
451  QWidget::setVisible( visible );
452  emitPositionChanged();
453 }
454 
455 void Legend::setNeedRebuild()
456 {
457  buildLegend();
458  sizeHint();
459 }
460 
462 {
463  if ( d->position == position ) {
464  return;
465  }
466  d->position = position;
467  emitPositionChanged();
468 }
469 
470 void Legend::emitPositionChanged()
471 {
472  Q_EMIT positionChanged( this );
473  Q_EMIT propertiesChanged();
474 }
475 
476 
478 {
479  return d->position;
480 }
481 
483 {
484  if ( d->alignment == alignment ) {
485  return;
486  }
487  d->alignment = alignment;
488  emitPositionChanged();
489 }
490 
492 {
493  return d->alignment;
494 }
495 
497 {
498  if ( d->textAlignment == alignment ) {
499  return;
500  }
501  d->textAlignment = alignment;
502  emitPositionChanged();
503 }
504 
506 {
507  return d->textAlignment;
508 }
509 
511 {
512  if ( d->legendLineSymbolAlignment == alignment ) {
513  return;
514  }
515  d->legendLineSymbolAlignment = alignment;
516  emitPositionChanged();
517 }
518 
520 {
521  return d->legendLineSymbolAlignment ;
522 }
523 
524 void Legend::setFloatingPosition( const RelativePosition& relativePosition )
525 {
526  d->position = Position::Floating;
527  if ( d->relativePosition != relativePosition ) {
528  d->relativePosition = relativePosition;
529  emitPositionChanged();
530  }
531 }
532 
534 {
535  return d->relativePosition;
536 }
537 
538 void Legend::setOrientation( Qt::Orientation orientation )
539 {
540  if ( d->orientation == orientation ) {
541  return;
542  }
543  d->orientation = orientation;
544  setNeedRebuild();
545  emitPositionChanged();
546 }
547 
548 Qt::Orientation Legend::orientation() const
549 {
550  return d->orientation;
551 }
552 
553 void Legend::setSortOrder( Qt::SortOrder order )
554 {
555  if ( d->order == order ) {
556  return;
557  }
558  d->order = order;
559  setNeedRebuild();
560  emitPositionChanged();
561 }
562 
563 Qt::SortOrder Legend::sortOrder() const
564 {
565  return d->order;
566 }
567 
568 void Legend::setShowLines( bool legendShowLines )
569 {
570  if ( d->showLines == legendShowLines ) {
571  return;
572  }
573  d->showLines = legendShowLines;
574  setNeedRebuild();
575  emitPositionChanged();
576 }
577 
578 bool Legend::showLines() const
579 {
580  return d->showLines;
581 }
582 
583 void Legend::setUseAutomaticMarkerSize( bool useAutomaticMarkerSize )
584 {
585  d->useAutomaticMarkerSize = useAutomaticMarkerSize;
586  setNeedRebuild();
587  emitPositionChanged();
588 }
589 
590 bool Legend::useAutomaticMarkerSize() const
591 {
592  return d->useAutomaticMarkerSize;
593 }
594 
596 {
597  if ( !d->texts.count() ) {
598  return;
599  }
600  d->texts.clear();
601  setNeedRebuild();
602 }
603 
604 void Legend::setText( uint dataset, const QString& text )
605 {
606  if ( d->texts[ dataset ] == text ) {
607  return;
608  }
609  d->texts[ dataset ] = text;
610  setNeedRebuild();
611 }
612 
613 QString Legend::text( uint dataset ) const
614 {
615  if ( d->texts.find( dataset ) != d->texts.end() ) {
616  return d->texts[ dataset ];
617  } else {
618  return d->modelLabels[ dataset ];
619  }
620 }
621 
622 const QMap<uint,QString> Legend::texts() const
623 {
624  return d->texts;
625 }
626 
627 void Legend::setColor( uint dataset, const QColor& color )
628 {
629  if ( d->brushes[ dataset ] != color ) {
630  d->brushes[ dataset ] = color;
631  setNeedRebuild();
632  update();
633  }
634 }
635 
636 void Legend::setBrush( uint dataset, const QBrush& brush )
637 {
638  if ( d->brushes[ dataset ] != brush ) {
639  d->brushes[ dataset ] = brush;
640  setNeedRebuild();
641  update();
642  }
643 }
644 
645 QBrush Legend::brush( uint dataset ) const
646 {
647  if ( d->brushes.contains( dataset ) ) {
648  return d->brushes[ dataset ];
649  } else {
650  return d->modelBrushes[ dataset ];
651  }
652 }
653 
654 const QMap<uint,QBrush> Legend::brushes() const
655 {
656  return d->brushes;
657 }
658 
659 
660 void Legend::setBrushesFromDiagram( AbstractDiagram* diagram )
661 {
662  bool changed = false;
663  QList<QBrush> datasetBrushes = diagram->datasetBrushes();
664  for ( int i = 0; i < datasetBrushes.count(); i++ ) {
665  if ( d->brushes[ i ] != datasetBrushes[ i ] ) {
666  d->brushes[ i ] = datasetBrushes[ i ];
667  changed = true;
668  }
669  }
670  if ( changed ) {
671  setNeedRebuild();
672  update();
673  }
674 }
675 
676 
677 void Legend::setPen( uint dataset, const QPen& pen )
678 {
679  if ( d->pens[dataset] == pen ) {
680  return;
681  }
682  d->pens[dataset] = pen;
683  setNeedRebuild();
684  update();
685 }
686 
687 QPen Legend::pen( uint dataset ) const
688 {
689  if ( d->pens.find( dataset ) != d->pens.end() ) {
690  return d->pens[ dataset ];
691  } else {
692  return d->modelPens[ dataset ];
693  }
694 }
695 
696 const QMap<uint,QPen> Legend::pens() const
697 {
698  return d->pens;
699 }
700 
701 
702 void Legend::setMarkerAttributes( uint dataset, const MarkerAttributes& markerAttributes )
703 {
704  if ( d->markerAttributes[dataset] == markerAttributes ) {
705  return;
706  }
707  d->markerAttributes[ dataset ] = markerAttributes;
708  setNeedRebuild();
709  update();
710 }
711 
712 MarkerAttributes Legend::markerAttributes( uint dataset ) const
713 {
714  if ( d->markerAttributes.find( dataset ) != d->markerAttributes.end() ) {
715  return d->markerAttributes[ dataset ];
716  } else if ( static_cast<uint>( d->modelMarkers.count() ) > dataset ) {
717  return d->modelMarkers[ dataset ];
718  } else {
719  return MarkerAttributes();
720  }
721 }
722 
723 const QMap<uint, MarkerAttributes> Legend::markerAttributes() const
724 {
725  return d->markerAttributes;
726 }
727 
728 
729 void Legend::setTextAttributes( const TextAttributes &a )
730 {
731  if ( d->textAttributes == a ) {
732  return;
733  }
734  d->textAttributes = a;
735  setNeedRebuild();
736 }
737 
738 TextAttributes Legend::textAttributes() const
739 {
740  return d->textAttributes;
741 }
742 
743 void Legend::setTitleText( const QString& text )
744 {
745  if ( d->titleText == text ) {
746  return;
747  }
748  d->titleText = text;
749  setNeedRebuild();
750 }
751 
752 QString Legend::titleText() const
753 {
754  return d->titleText;
755 }
756 
757 void Legend::setTitleTextAttributes( const TextAttributes &a )
758 {
759  if ( d->titleTextAttributes == a ) {
760  return;
761  }
762  d->titleTextAttributes = a;
763  setNeedRebuild();
764 }
765 
766 TextAttributes Legend::titleTextAttributes() const
767 {
768  return d->titleTextAttributes;
769 }
770 
772 {
773 #ifdef DEBUG_LEGEND_PAINT
774  qDebug() << "entering Legend::forceRebuild()";
775 #endif
776  buildLegend();
777 #ifdef DEBUG_LEGEND_PAINT
778  qDebug() << "leaving Legend::forceRebuild()";
779 #endif
780 }
781 
782 void Legend::setSpacing( uint space )
783 {
784  if ( d->spacing == space && d->layout->spacing() == int( space ) ) {
785  return;
786  }
787  d->spacing = space;
788  d->layout->setSpacing( space );
789  setNeedRebuild();
790 }
791 
792 uint Legend::spacing() const
793 {
794  return d->spacing;
795 }
796 
797 void Legend::setDefaultColors()
798 {
800  for ( int i = 0; i < pal.size(); i++ ) {
801  setBrush( i, pal.getBrush( i ) );
802  }
803 }
804 
805 void Legend::setRainbowColors()
806 {
807  Palette pal = Palette::rainbowPalette();
808  for ( int i = 0; i < pal.size(); i++ ) {
809  setBrush( i, pal.getBrush( i ) );
810  }
811 }
812 
813 void Legend::setSubduedColors( bool ordered )
814 {
815  Palette pal = Palette::subduedPalette();
816  if ( ordered ) {
817  for ( int i = 0; i < pal.size(); i++ ) {
818  setBrush( i, pal.getBrush( i ) );
819  }
820  } else {
821  static const int s_subduedColorsCount = 18;
822  Q_ASSERT( pal.size() >= s_subduedColorsCount );
823  static const int order[ s_subduedColorsCount ] = {
824  0, 5, 10, 15, 2, 7, 12, 17, 4,
825  9, 14, 1, 6, 11, 16, 3, 8, 13
826  };
827  for ( int i = 0; i < s_subduedColorsCount; i++ ) {
828  setBrush( i, pal.getBrush( order[i] ) );
829  }
830  }
831 }
832 
833 void Legend::resizeEvent( QResizeEvent * event )
834 {
835  Q_UNUSED( event );
836 #ifdef DEBUG_LEGEND_PAINT
837  qDebug() << "Legend::resizeEvent() called";
838 #endif
839  forceRebuild();
840  sizeHint();
841  QTimer::singleShot( 0, this, SLOT(emitPositionChanged()) );
842 }
843 
844 void Legend::Private::fetchPaintOptions( Legend *q )
845 {
846  modelLabels.clear();
847  modelBrushes.clear();
848  modelPens.clear();
849  modelMarkers.clear();
850  // retrieve the diagrams' settings for all non-hidden datasets
851  for ( int i = 0; i < observers.size(); ++i ) {
852  const AbstractDiagram* diagram = observers.at( i )->diagram();
853  if ( !diagram ) {
854  continue;
855  }
856  const QStringList diagramLabels = diagram->datasetLabels();
857  const QList<QBrush> diagramBrushes = diagram->datasetBrushes();
858  const QList<QPen> diagramPens = diagram->datasetPens();
859  const QList<MarkerAttributes> diagramMarkers = diagram->datasetMarkers();
860 
861  const bool ascend = q->sortOrder() == Qt::AscendingOrder;
862  int dataset = ascend ? 0 : diagramLabels.count() - 1;
863  const int end = ascend ? diagramLabels.count() : -1;
864  for ( ; dataset != end; dataset += ascend ? 1 : -1 ) {
865  if ( diagram->isHidden( dataset ) || q->datasetIsHidden( dataset ) ) {
866  continue;
867  }
868  modelLabels += diagramLabels[ dataset ];
869  modelBrushes += diagramBrushes[ dataset ];
870  modelPens += diagramPens[ dataset ];
871  modelMarkers += diagramMarkers[ dataset ];
872  }
873  }
874 
875  Q_ASSERT( modelLabels.count() == modelBrushes.count() );
876 }
877 
878 QSizeF Legend::Private::markerSize( Legend *q, int dataset, qreal fontHeight ) const
879 {
880  QSizeF suppliedSize = q->markerAttributes( dataset ).markerSize();
881  if ( q->useAutomaticMarkerSize() || !suppliedSize.isValid() ) {
882  return QSizeF( fontHeight, fontHeight );
883  } else {
884  return suppliedSize;
885  }
886 }
887 
888 QSizeF Legend::Private::maxMarkerSize( Legend *q, qreal fontHeight ) const
889 {
890  QSizeF ret( 1.0, 1.0 );
891  if ( q->legendStyle() != LinesOnly ) {
892  for ( int dataset = 0; dataset < modelLabels.count(); ++dataset ) {
893  ret = ret.expandedTo( markerSize( q, dataset, fontHeight ) );
894  }
895  }
896  return ret;
897 }
898 
899 HDatasetItem::HDatasetItem()
900  : markerLine(nullptr),
901  label(nullptr),
902  separatorLine(nullptr),
903  spacer(nullptr)
904 {}
905 
906 static void updateToplevelLayout(QWidget *w)
907 {
908  while ( w ) {
909  if ( w->isTopLevel() ) {
910  // The null check has proved necessary during destruction of the Legend / Chart
911  if ( w->layout() ) {
912  w->layout()->update();
913  }
914  break;
915  } else {
916  w = qobject_cast< QWidget * >( w->parent() );
917  Q_ASSERT( w );
918  }
919  }
920 }
921 
922 void Legend::buildLegend()
923 {
924  /* Grid layout partitioning (horizontal orientation): row zero is the title, row one the divider
925  line between title and dataset items, row two for each item: line, marker, text label and separator
926  line in that order.
927  In a vertically oriented legend, row pairs (2, 3), ... contain a possible separator line (first row)
928  and (second row) line, marker, text label each. */
929  d->destroyOldLayout();
930 
931  if ( orientation() == Qt::Vertical ) {
932  d->layout->setColumnStretch( 6, 1 );
933  } else {
934  d->layout->setColumnStretch( 6, 0 );
935  }
936 
937  d->fetchPaintOptions( this );
938 
939  const KChartEnums::MeasureOrientation measureOrientation =
940  orientation() == Qt::Vertical ? KChartEnums::MeasureOrientationMinimum
941  : KChartEnums::MeasureOrientationHorizontal;
942 
943  // legend caption
944  if ( !titleText().isEmpty() && titleTextAttributes().isVisible() ) {
945  TextLayoutItem* titleItem =
946  new TextLayoutItem( titleText(), titleTextAttributes(), referenceArea(),
947  measureOrientation, d->textAlignment );
948  titleItem->setParentWidget( this );
949 
950  d->paintItems << titleItem;
951  d->layout->addItem( titleItem, 0, 0, 1, 5, Qt::AlignCenter );
952 
953  // The line between the title and the legend items, if any.
954  if ( showLines() && d->modelLabels.count() ) {
956  d->paintItems << lineItem;
957  d->layout->addItem( lineItem, 1, 0, 1, 5, Qt::AlignCenter );
958  }
959  }
960 
961  qreal fontHeight = textAttributes().calculatedFontSize( referenceArea(), measureOrientation );
962  {
963  QFont tmpFont = textAttributes().font();
964  tmpFont.setPointSizeF( fontHeight );
966  fontHeight = QFontMetricsF( tmpFont, GlobalMeasureScaling::paintDevice() ).height();
967  } else {
968  fontHeight = QFontMetricsF( tmpFont ).height();
969  }
970  }
971 
972  const QSizeF maxMarkerSize = d->maxMarkerSize( this, fontHeight );
973 
974  // If we show a marker on a line, we paint it after 8 pixels
975  // of the line have been painted. This allows to see the line style
976  // at the right side of the marker without the line needing to
977  // be too long.
978  // (having the marker in the middle of the line would require longer lines)
979  const int lineLengthLeftOfMarker = 8;
980 
981  int maxLineLength = 18;
982  {
983  bool hasComplexPenStyle = false;
984  for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ) {
985  const QPen pn = pen( dataset );
986  const Qt::PenStyle ps = pn.style();
987  if ( ps != Qt::NoPen ) {
988  maxLineLength = qMin( pn.width() * 18, maxLineLength );
989  if ( ps != Qt::SolidLine ) {
990  hasComplexPenStyle = true;
991  }
992  }
993  }
994  if ( legendStyle() != LinesOnly ) {
995  if ( hasComplexPenStyle )
996  maxLineLength += lineLengthLeftOfMarker;
997  maxLineLength += int( maxMarkerSize.width() );
998  }
999  }
1000 
1001  // for all datasets: add (line)marker items and text items to the layout;
1002  // actual layout happens in flowHDatasetItems() for horizontal layout, here for vertical
1003  for ( int dataset = 0; dataset < d->modelLabels.count(); ++dataset ) {
1004  const int vLayoutRow = 2 + dataset * 2;
1005  HDatasetItem dsItem;
1006 
1007  // It is possible to set the marker brush through markerAttributes as well as
1008  // the dataset brush set in the diagram - the markerAttributes have higher precedence.
1009  MarkerAttributes markerAttrs = markerAttributes( dataset );
1010  markerAttrs.setMarkerSize( d->markerSize( this, dataset, fontHeight ) );
1011  const QBrush markerBrush = markerAttrs.markerColor().isValid() ?
1012  QBrush( markerAttrs.markerColor() ) : brush( dataset );
1013 
1014  switch ( legendStyle() ) {
1015  case MarkersOnly:
1016  dsItem.markerLine = new MarkerLayoutItem( diagram(), markerAttrs, markerBrush,
1017  markerAttrs.pen(), Qt::AlignLeft | Qt::AlignVCenter );
1018  break;
1019  case LinesOnly:
1020  dsItem.markerLine = new LineLayoutItem( diagram(), maxLineLength, pen( dataset ),
1021  d->legendLineSymbolAlignment, Qt::AlignCenter );
1022  break;
1023  case MarkersAndLines:
1024  dsItem.markerLine = new LineWithMarkerLayoutItem(
1025  diagram(), maxLineLength, pen( dataset ), lineLengthLeftOfMarker, markerAttrs,
1026  markerBrush, markerAttrs.pen(), Qt::AlignCenter );
1027  break;
1028  default:
1029  Q_ASSERT( false );
1030  }
1031 
1032  dsItem.label = new TextLayoutItem( text( dataset ), textAttributes(), referenceArea(),
1033  measureOrientation, d->textAlignment );
1034  dsItem.label->setParentWidget( this );
1035 
1036  // horizontal layout is deferred to flowDatasetItems()
1037 
1038  if ( orientation() == Qt::Horizontal ) {
1039  d->hLayoutDatasets << dsItem;
1040  continue;
1041  }
1042 
1043  // (actual) vertical layout here
1044  if ( dsItem.markerLine ) {
1045  d->layout->addItem( dsItem.markerLine, vLayoutRow, 1, 1, 1, Qt::AlignCenter );
1046  d->paintItems << dsItem.markerLine;
1047  }
1048  d->layout->addItem( dsItem.label, vLayoutRow, 3, 1, 1, Qt::AlignLeft | Qt::AlignVCenter );
1049  d->paintItems << dsItem.label;
1050 
1051  // horizontal separator line, only between items
1052  if ( showLines() && dataset != d->modelLabels.count() - 1 ) {
1054  d->layout->addItem( lineItem, vLayoutRow + 1, 0, 1, 5, Qt::AlignCenter );
1055  d->paintItems << lineItem;
1056  }
1057  }
1058 
1059  if ( orientation() == Qt::Horizontal ) {
1060  d->flowHDatasetItems( this );
1061  }
1062 
1063  // vertical line (only in vertical mode)
1064  if ( orientation() == Qt::Vertical && showLines() && d->modelLabels.count() ) {
1066  d->paintItems << lineItem;
1067  d->layout->addItem( lineItem, 2, 2, d->modelLabels.count() * 2, 1 );
1068  }
1069 
1070  updateToplevelLayout( this );
1071 
1072  Q_EMIT propertiesChanged();
1073 #ifdef DEBUG_LEGEND_PAINT
1074  qDebug() << "leaving Legend::buildLegend()";
1075 #endif
1076 }
1077 
1078 int HDatasetItem::height() const
1079 {
1080  return qMax( markerLine->sizeHint().height(), label->sizeHint().height() );
1081 }
1082 
1083 void Legend::Private::reflowHDatasetItems( Legend *q )
1084 {
1085  if (hLayoutDatasets.isEmpty()) {
1086  return;
1087  }
1088 
1089  paintItems.clear();
1090  // Dissolve exactly the QHBoxLayout(s) created as "currentLine" in flowHDatasetItems - don't remove the
1091  // caption and line under the caption! Those are easily identified because they aren't QLayouts.
1092  for ( int i = layout->count() - 1; i >= 0; i-- ) {
1093  QLayoutItem *const item = layout->itemAt( i );
1094  QLayout *const hbox = item->layout();
1095  if ( !hbox ) {
1096  AbstractLayoutItem *alItem = dynamic_cast< AbstractLayoutItem * >( item );
1097  Q_ASSERT( alItem );
1098  paintItems << alItem;
1099  continue;
1100  }
1101  Q_ASSERT( dynamic_cast< QHBoxLayout * >( hbox ) );
1102  layout->takeAt( i );
1103  // detach children so they aren't deleted with the parent
1104  for ( int j = hbox->count() - 1; j >= 0; j-- ) {
1105  hbox->takeAt( j );
1106  }
1107  delete hbox;
1108  }
1109 
1110  flowHDatasetItems( q );
1111 }
1112 
1113 // this works pretty much like flow layout for text, and it is only applicable to dataset items
1114 // laid out horizontally
1115 void Legend::Private::flowHDatasetItems( Legend *q )
1116 {
1117  const int separatorLineWidth = 3; // hardcoded in VerticalLineLayoutItem::sizeHint()
1118 
1119  const int allowedWidth = q->areaGeometry().width();
1120  QHBoxLayout *currentLine = new QHBoxLayout;
1121  int columnSpan = 5;
1122  int mainLayoutColumn = 0;
1123  int row = 0;
1124  if ( !titleText.isEmpty() && titleTextAttributes.isVisible() ) {
1125  ++row;
1126  if (q->showLines()){
1127  ++row;
1128  }
1129  }
1130  layout->addItem( currentLine, row, mainLayoutColumn,
1131  /*rowSpan*/1 , columnSpan, Qt::AlignLeft | Qt::AlignVCenter );
1132  mainLayoutColumn += columnSpan;
1133 
1134  for ( int dataset = 0; dataset < hLayoutDatasets.size(); dataset++ ) {
1135  HDatasetItem *hdsItem = &hLayoutDatasets[ dataset ];
1136 
1137  bool spacerUsed = false;
1138  bool separatorUsed = false;
1139  if ( !currentLine->isEmpty() ) {
1140  const int separatorWidth = ( q->showLines() ? separatorLineWidth : 0 ) + q->spacing();
1141  const int payloadWidth = hdsItem->markerLine->sizeHint().width() +
1142  hdsItem->label->sizeHint().width();
1143  if ( currentLine->sizeHint().width() + separatorWidth + payloadWidth > allowedWidth ) {
1144  // too wide, "line break"
1145 #ifdef DEBUG_LEGEND_PAINT
1146  qDebug() << Q_FUNC_INFO << "break" << mainLayoutColumn
1147  << currentLine->sizeHint().width()
1148  << currentLine->sizeHint().width() + separatorWidth + payloadWidth
1149  << allowedWidth;
1150 #endif
1151  currentLine = new QHBoxLayout;
1152  layout->addItem( currentLine, row, mainLayoutColumn,
1153  /*rowSpan*/1 , columnSpan, Qt::AlignLeft | Qt::AlignVCenter );
1154  mainLayoutColumn += columnSpan;
1155  } else {
1156  // > 1 dataset item in line, put spacing and maybe a separator between them
1157  if ( !hdsItem->spacer ) {
1158  hdsItem->spacer = new QSpacerItem( q->spacing(), 1 );
1159  }
1160  currentLine->addItem( hdsItem->spacer );
1161  spacerUsed = true;
1162 
1163  if ( q->showLines() ) {
1164  if ( !hdsItem->separatorLine ) {
1165  hdsItem->separatorLine = new VerticalLineLayoutItem;
1166  }
1167  paintItems << hdsItem->separatorLine;
1168  currentLine->addItem( hdsItem->separatorLine );
1169  separatorUsed = true;
1170  }
1171  }
1172  }
1173  // those have no parents in the current layout, so they wouldn't get cleaned up otherwise
1174  if ( !spacerUsed ) {
1175  delete hdsItem->spacer;
1176  hdsItem->spacer = nullptr;
1177  }
1178  if ( !separatorUsed ) {
1179  delete hdsItem->separatorLine;
1180  hdsItem->separatorLine = nullptr;
1181  }
1182 
1183  currentLine->addItem( hdsItem->markerLine );
1184  paintItems << hdsItem->markerLine;
1185  currentLine->addItem( hdsItem->label );
1186  paintItems << hdsItem->label;
1187  }
1188 }
1189 
1190 bool Legend::hasHeightForWidth() const
1191 {
1192  // this is better than using orientation() because, for layout purposes, we're not height-for-width
1193  // *yet* before buildLegend() has been called, and the layout logic might get upset if we say
1194  // something that will only be true in the future
1195  return !d->hLayoutDatasets.isEmpty();
1196 }
1197 
1198 int Legend::heightForWidth( int width ) const
1199 {
1200  if ( d->hLayoutDatasets.isEmpty() ) {
1201  return -1;
1202  }
1203 
1204  int ret = 0;
1205  // space for caption and line under caption (if any)
1206  for (int i = 0; i < 2; i++) {
1207  if ( QLayoutItem *item = d->layout->itemAtPosition( i, 0 ) ) {
1208  ret += item->sizeHint().height();
1209  }
1210  }
1211  const int separatorLineWidth = 3; // ### hardcoded in VerticalLineLayoutItem::sizeHint()
1212 
1213  int currentLineWidth = 0;
1214  int currentLineHeight = 0;
1215  Q_FOREACH( const HDatasetItem &hdsItem, d->hLayoutDatasets ) {
1216  const int payloadWidth = hdsItem.markerLine->sizeHint().width() +
1217  hdsItem.label->sizeHint().width();
1218  if ( !currentLineWidth ) {
1219  // first iteration
1220  currentLineWidth = payloadWidth;
1221  } else {
1222  const int separatorWidth = ( showLines() ? separatorLineWidth : 0 ) + spacing();
1223  currentLineWidth += separatorWidth + payloadWidth;
1224  if ( currentLineWidth > width ) {
1225  // too wide, "line break"
1226 #ifdef DEBUG_LEGEND_PAINT
1227  qDebug() << Q_FUNC_INFO << "heightForWidth break" << currentLineWidth
1228  << currentLineWidth + separatorWidth + payloadWidth
1229  << width;
1230 #endif
1231  ret += currentLineHeight + spacing();
1232  currentLineWidth = payloadWidth;
1233  currentLineHeight = 0;
1234  }
1235  }
1236  currentLineHeight = qMax( currentLineHeight, hdsItem.height() );
1237  }
1238  ret += currentLineHeight; // one less spacings than lines
1239  return ret;
1240 }
1241 
1242 void Legend::Private::destroyOldLayout()
1243 {
1244  // in the horizontal layout case, the QHBoxLayout destructor also deletes child layout items
1245  // (it isn't documented that QLayoutItems delete their children)
1246  for ( int i = layout->count() - 1; i >= 0; i-- ) {
1247  delete layout->takeAt( i );
1248  }
1249  Q_ASSERT( !layout->count() );
1250  hLayoutDatasets.clear();
1251  paintItems.clear();
1252 }
1253 
1254 void Legend::setHiddenDatasets( const QList<uint> hiddenDatasets )
1255 {
1256  d->hiddenDatasets = hiddenDatasets;
1257 }
1258 
1259 const QList<uint> Legend::hiddenDatasets() const
1260 {
1261  return d->hiddenDatasets;
1262 }
1263 
1264 void Legend::setDatasetHidden( uint dataset, bool hidden )
1265 {
1266  if ( hidden && !d->hiddenDatasets.contains( dataset ) ) {
1267  d->hiddenDatasets.append( dataset );
1268  } else if ( !hidden && d->hiddenDatasets.contains( dataset ) ) {
1269  d->hiddenDatasets.removeAll( dataset );
1270  }
1271 }
1272 
1273 bool Legend::datasetIsHidden( uint dataset ) const
1274 {
1275  return d->hiddenDatasets.contains( dataset );
1276 }
QLayout * layout() const const
QList< MarkerAttributes > datasetMarkers() const
The set of dataset markers currently used, for use in legends, etc.
Defines relative position information: reference area, position in this area (reference position)...
Class only listed here to document inheritance of some KChart classes.
Qt::PenStyle style() const const
Position position() const
Returns the position of a non-floating legend.
QSize size() const const
virtual QLayoutItem * itemAt(int index) const const =0
int width() const const
Legend defines the interface for the legend drawing class.
Definition: KChartLegend.h:52
AbstractDiagram defines the interface for diagram classes.
virtual QSize sizeHint() const const override
PenStyle
int size() const
virtual void addItem(QLayoutItem *item)=0
void setReferenceArea(const QWidget *area)
Specifies the reference area for font size of title text, and for font size of the item texts...
void setMarkerAttributes(uint dataset, const MarkerAttributes &)
Note that any sizes specified via setMarkerAttributes are ignored, unless you disable the automatic s...
const QWidget * referenceArea() const
Returns the reference area, that is used for font size of title text, and for font size of the item t...
void addDiagram(KChart::AbstractDiagram *newDiagram)
Add the given diagram to the legend.
A set of attributes controlling the appearance of data set markers.
QBrush getBrush(int position) const
Query the palette for a brush at the specified position.
DiagramList diagrams() const
The list of all diagrams associated with the legend.
const T & at(int i) const const
Defines a position, using compass terminology.
bool isValid() const const
void setPen(const QPen &pen)
Set the pen to use for rendering the text.
virtual QLayout * layout()
A Palette is a set of brushes (or colors) to be used for painting data sets.
Definition: KChartPalette.h:46
bool isVisible() const const
Layout item showing a coloured line and a data point marker.
bool compare(const AbstractAreaBase *other) const
Returns true if both areas have the same settings.
QSizeF expandedTo(const QSizeF &otherSize) const const
bool isTopLevel() const const
ConstDiagramList constDiagrams() const
QList< QBrush > datasetBrushes() const
The set of dataset brushes currently used, for use in legends, etc.
void setPosition(Position position)
Specify the position of a non-floating legend.
void setFont(const QFont &font)
Set the font to be used for rendering the text.
virtual bool isEmpty() const const override
void needSizeHint() override
Call this to trigger an conditional re-building of the widget&#39;s internals.
virtual void paintAll(QPainter &painter)
Default impl: just call paint.
void setDiagram(KChart::AbstractDiagram *newDiagram)
A convenience method doing the same as replaceDiagram( newDiagram, 0 );.
AlignCenter
bool isHidden() const
Retrieve the hidden status specified globally.
QList< QPen > datasetPens() const
The set of dataset pens currently used, for use in legends, etc.
Class only listed here to document inheritance of some KChart classes.
Stores the absolute target points of a Position.
virtual void paint(QPainter *painter) override
Overwrite this to paint the inner contents of your widget.
int count(const T &value) const const
void setLegendSymbolAlignment(Qt::Alignment)
Specify the alignment of the legend symbol( alignment of Legend::LinesOnly) within the legend...
virtual Legend * clone() const
Creates an exact copy of this legend.
void append(const T &value)
int top() const const
Class only listed here to document inheritance of some KChart classes.
int left() const const
virtual void addItem(QLayoutItem *item) override
Layout item showing a horizontal line.
MeasureOrientation
Measure orientation mode: the way how the absolute value of a KChart::Measure is determined during KC...
Definition: KChartEnums.h:293
QPaintDevice * device() const const
virtual void setParentWidget(QWidget *widget)
Inform the item about its widget: This enables the item, to trigger that widget&#39;s update...
bool compare(const Legend *other) const
Returns true if both legends have the same settings.
void setFloatingPosition(const RelativePosition &relativePosition)
Specify the position and alignment of a floating legend.
void resetTexts()
Removes all legend texts that might have been set by setText.
A DiagramObserver watches the associated diagram for changes and deletion and emits corresponsing sig...
Base class for all layout items of KChart.
void setUseAutomaticMarkerSize(bool useAutomaticMarkerSize)
This option is on by default, it means that Marker sizes in the Legend will be the same as the font h...
static void setPaintDevice(QPaintDevice *paintDevice)
Set the paint device to use for calculating font metrics.
void update()
Layout item showing a text.
static const Palette & defaultPalette()
Provide access to the three builtin palettes, one with standard bright colors, one with more subdued ...
Qt::Alignment alignment() const
Returns the alignment of a non-floating legend.
void removeDiagram(KChart::AbstractDiagram *oldDiagram)
Removes the diagram from the legend&#39;s list of diagrams.
bool isEmpty() const const
void replaceDiagram(KChart::AbstractDiagram *newDiagram, KChart::AbstractDiagram *oldDiagram=nullptr)
Replaces the old diagram, or appends the new diagram, it there is none yet.
virtual QSize sizeHint() const const
const RelativePosition floatingPosition() const
Returns the position of a floating legend.
void setPointSizeF(qreal pointSize)
QCA_EXPORT void init()
static QPaintDevice * paintDevice()
Return the paint device to use for calculating font metrics.
void setHiddenDatasets(const QList< uint > hiddenDatasets)
Sets a list of datasets that are to be hidden in the legend.
KChart::AbstractDiagram * diagram() const
The first diagram of the legend or 0 if there was none added to the legend.
void setTextAlignment(Qt::Alignment)
Specify the alignment of the text elements within the legend.
int width() const const
int width() const const
AscendingOrder
uint dataSetOffset(KChart::AbstractDiagram *diagram)
Returns the offset of the first dataset of diagram.
void removeDiagrams()
Removes all diagrams from the legend&#39;s list of diagrams.
void setFontSize(const Measure &measure)
Set the size of the font used for rendering text.
Qt::Alignment legendSymbolAlignment() const
Returns the alignment used while drawing legend symbol(alignment of Legend::LinesOnly) within the leg...
virtual int columnCount(const QModelIndex &parent) const const =0
void setAlignment(Qt::Alignment)
Specify the alignment of a non-floating legend.
virtual int count() const const =0
Layout item showing a vertial line.
void translate(const QPointF &offset)
A set of text attributes.
void setColor(uint dataset, const QColor &color)
Note: there is no color() getter method, since setColor just sets a QBrush with the respective color...
Layout item showing a coloured line.
Qt::Alignment alignment() const const
An area in the chart with a background, a frame, etc.
Layout item showing a data point marker.
Vertical
QAbstractItemModel * model() const const
Global namespace.
void setMinimalFontSize(const Measure &measure)
Set the minimal size of the font used for rendering text.
QObject * parent() const const
void setMarkerSize(const QSizeF &size)
Normally you need to specify a valid QSizeF here, but for Legends you can use the invalid size QSizeF...
void forceRebuild() override
Call this to trigger an unconditional re-building of the widget&#39;s internals.
QStringList datasetLabels() const
The set of dataset labels currently displayed, for use in legends, etc.
virtual QSize sizeHint() const const =0
Measure is used to specify relative and absolute sizes in KChart, e.g.
Definition: KChartMeasure.h:48
qreal width() const const
A set of attributes for frames around items.
Qt::Alignment textAlignment() const
Returns the alignment used while rendering text elements within the legend.
virtual QLayoutItem * takeAt(int index)=0
qreal height() const const
bool isValid() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Thu Jan 14 2021 22:38:05 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.