1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Charts module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 or (at your option) any later version |
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by |
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
22 | ** included in the packaging of this file. Please review the following |
23 | ** information to ensure the GNU General Public License requirements will |
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
25 | ** |
26 | ** $QT_END_LICENSE$ |
27 | ** |
28 | ****************************************************************************/ |
29 | |
30 | #include <QtCharts/QLegend> |
31 | #include <private/qlegend_p.h> |
32 | #include <QtCharts/QAbstractSeries> |
33 | #include <private/qabstractseries_p.h> |
34 | #include <private/qchart_p.h> |
35 | #include <private/legendlayout_p.h> |
36 | #include <private/chartpresenter_p.h> |
37 | #include <private/abstractchartlayout_p.h> |
38 | #include <QtCharts/QLegendMarker> |
39 | #include <private/qlegendmarker_p.h> |
40 | #include <private/legendmarkeritem_p.h> |
41 | #include <private/chartdataset_p.h> |
42 | #include <QtGui/QPainter> |
43 | #include <QtGui/QPen> |
44 | #include <QtWidgets/QGraphicsItemGroup> |
45 | |
46 | QT_CHARTS_BEGIN_NAMESPACE |
47 | |
48 | /*! |
49 | \class QLegend |
50 | \inmodule QtCharts |
51 | \inherits QGraphicsWidget |
52 | \brief The QLegend class displays the legend of a chart. |
53 | |
54 | A legend is a graphical object that displays the legend of a chart. The legend state is updated |
55 | by QChart when series change. By default, the legend is attached to the chart, but it can be |
56 | detached to make it independent of chart layout. Legend objects cannot be created or deleted, |
57 | but they can be referenced via the QChart class. |
58 | |
59 | \image examples_percentbarchart_legend.png |
60 | |
61 | \sa QChart |
62 | */ |
63 | /*! |
64 | \qmltype Legend |
65 | \instantiates QLegend |
66 | \inqmlmodule QtCharts |
67 | |
68 | \brief Displays the legend of a chart. |
69 | |
70 | A legend is a graphical object that displays the legend of a chart. The legend state is updated |
71 | by the ChartView type when series change. The \l Legend type properties can be attached to the |
72 | ChartView type. For example: |
73 | \code |
74 | ChartView { |
75 | legend.visible: true |
76 | legend.alignment: Qt.AlignBottom |
77 | // Add a few series... |
78 | } |
79 | \endcode |
80 | |
81 | \image examples_percentbarchart_legend.png |
82 | |
83 | \note There is no QML API available for modifying legend markers. Markers can be modified by |
84 | creating a custom legend, as illustrated by \l {qmlcustomlegend}{Qml Custom Example}. |
85 | */ |
86 | |
87 | /*! |
88 | \property QLegend::alignment |
89 | \brief How the legend is aligned with the chart. |
90 | |
91 | Can be Qt::AlignTop, Qt::AlignBottom, Qt::AlignLeft, Qt::AlignRight. If you set more than one |
92 | flag, the result is undefined. |
93 | */ |
94 | /*! |
95 | \qmlproperty alignment Legend::alignment |
96 | Defines how the legend is aligned with the chart. Can be \l{Qt::AlignLeft}{Qt.AlignLeft}, |
97 | \l{Qt::AlignRight}{Qt.AlignRight}, \l{Qt::AlignBottom}{Qt.AlignBottom}, or |
98 | \l{Qt::AlignTop}{Qt.AlignTop}. If you set more than one flag, the result is undefined. |
99 | */ |
100 | |
101 | /*! |
102 | \property QLegend::backgroundVisible |
103 | \brief Whether the legend background is visible. |
104 | */ |
105 | /*! |
106 | \qmlproperty bool Legend::backgroundVisible |
107 | Whether the legend background is visible. |
108 | */ |
109 | |
110 | /*! |
111 | \qmlproperty bool Legend::visible |
112 | Whether the legend is visible. |
113 | |
114 | By default, this property is \c true. |
115 | \sa QGraphicsObject::visible |
116 | */ |
117 | |
118 | /*! |
119 | \property QLegend::color |
120 | \brief The background (brush) color of the legend. |
121 | |
122 | If you change the color of the legend, the style of the legend brush is set to |
123 | Qt::SolidPattern. |
124 | */ |
125 | /*! |
126 | \qmlproperty color Legend::color |
127 | The background (brush) color of the legend. |
128 | */ |
129 | |
130 | /*! |
131 | \property QLegend::borderColor |
132 | \brief The line color of the legend. |
133 | */ |
134 | /*! |
135 | \qmlproperty color Legend::borderColor |
136 | The line color of the legend. |
137 | */ |
138 | |
139 | /*! |
140 | \property QLegend::font |
141 | \brief The font of the markers used by the legend. |
142 | */ |
143 | /*! |
144 | \qmlproperty Font Legend::font |
145 | The font of the markers used by the legend. |
146 | */ |
147 | |
148 | /*! |
149 | \property QLegend::labelColor |
150 | \brief The color of the brush used to draw labels. |
151 | */ |
152 | /*! |
153 | \qmlproperty color Legend::labelColor |
154 | The color of the brush used to draw labels. |
155 | */ |
156 | |
157 | /*! |
158 | \property QLegend::reverseMarkers |
159 | \brief Whether reverse order is used for the markers in the legend. |
160 | |
161 | This property is \c false by default. |
162 | */ |
163 | /*! |
164 | \qmlproperty bool Legend::reverseMarkers |
165 | Whether reverse order is used for the markers in the legend. This property |
166 | is \c false by default. |
167 | */ |
168 | |
169 | /*! |
170 | \property QLegend::showToolTips |
171 | \brief Whether tooltips are shown when the text is truncated. |
172 | |
173 | This property is \c false by default. |
174 | */ |
175 | |
176 | /*! |
177 | \enum QLegend::MarkerShape |
178 | |
179 | This enum describes the shape used when rendering legend marker items. |
180 | |
181 | \value MarkerShapeDefault Default shape determined by QLegend is used for the marker. |
182 | This value is supported only for individual QLegendMarker items. |
183 | \value MarkerShapeRectangle Rectangular markers are used. |
184 | Marker size is determined by font size. |
185 | \value MarkerShapeCircle Circular markers are used. |
186 | Marker size is determined by font size. |
187 | \value MarkerShapeFromSeries The marker shape is determined by the series. |
188 | In case of a scatter series, the legend marker looks like a scatter dot and is the same |
189 | size as the dot. In case of a line or spline series, the legend marker looks like a small |
190 | segment of the line. For other series types, rectangular markers are shown. |
191 | |
192 | \sa markerShape |
193 | */ |
194 | |
195 | /*! |
196 | \qmlproperty enumeration Legend::markerShape |
197 | \since 5.9 |
198 | |
199 | The default shape of the legend markers. |
200 | The default value is \c{MarkerShapeRectangle}. |
201 | |
202 | \value Legend.MarkerShapeRectangle Legend markers are rectangular |
203 | \value Legend.MarkerShapeCircle Legend markers are circular |
204 | \value Legend.MarkerShapeFromSeries Legend marker shape is determined by the series |
205 | |
206 | \sa QLegend::MarkerShape |
207 | */ |
208 | |
209 | /*! |
210 | \property QLegend::markerShape |
211 | \since 5.9 |
212 | |
213 | The default shape of the legend markers. |
214 | The default value is \c{MarkerShapeRectangle}. |
215 | */ |
216 | |
217 | /*! |
218 | \qmlproperty bool Legend::showToolTips |
219 | Whether tooltips are shown when the text is truncated. This property is \c false by default. |
220 | This property currently has no effect as there is no support for tooltips in QML. |
221 | */ |
222 | |
223 | /*! |
224 | \fn void QLegend::backgroundVisibleChanged(bool) |
225 | This signal is emitted when the visibility of the legend background changes to \a visible. |
226 | */ |
227 | |
228 | /*! |
229 | \fn void QLegend::colorChanged(QColor) |
230 | This signal is emitted when the color of the legend background changes to \a color. |
231 | */ |
232 | |
233 | /*! |
234 | \fn void QLegend::borderColorChanged(QColor) |
235 | This signal is emitted when the border color of the legend background changes to \a color. |
236 | */ |
237 | |
238 | /*! |
239 | \fn void QLegend::fontChanged(QFont) |
240 | This signal is emitted when the font of the markers of the legend changes to \a font. |
241 | */ |
242 | |
243 | /*! |
244 | \fn void QLegend::labelColorChanged(QColor color) |
245 | This signal is emitted when the color of the brush used to draw the legend |
246 | labels changes to \a color. |
247 | */ |
248 | |
249 | /*! |
250 | \fn void QLegend::reverseMarkersChanged(bool) |
251 | This signal is emitted when the use of reverse order for the markers in the |
252 | legend is changed to \a reverseMarkers. |
253 | */ |
254 | |
255 | /*! |
256 | \fn void QLegend::showToolTipsChanged(bool showToolTips) |
257 | This signal is emitted when the visibility of tooltips is changed to \a showToolTips. |
258 | */ |
259 | |
260 | QLegend::QLegend(QChart *chart): QGraphicsWidget(chart), |
261 | d_ptr(new QLegendPrivate(chart->d_ptr->m_presenter, chart, this)) |
262 | { |
263 | setZValue(ChartPresenter::LegendZValue); |
264 | setFlags(QGraphicsItem::ItemClipsChildrenToShape); |
265 | QObject::connect(sender: chart->d_ptr->m_dataset, SIGNAL(seriesAdded(QAbstractSeries*)), receiver: d_ptr.data(), SLOT(handleSeriesAdded(QAbstractSeries*))); |
266 | QObject::connect(sender: chart->d_ptr->m_dataset, SIGNAL(seriesRemoved(QAbstractSeries*)), receiver: d_ptr.data(), SLOT(handleSeriesRemoved(QAbstractSeries*))); |
267 | setLayout(d_ptr->m_layout); |
268 | } |
269 | |
270 | /*! |
271 | Destroys the legend object. The legend is always owned by a QChart, so an application |
272 | should never call this function. |
273 | */ |
274 | QLegend::~QLegend() |
275 | { |
276 | } |
277 | |
278 | /*! |
279 | \internal |
280 | */ |
281 | void QLegend::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) |
282 | { |
283 | Q_UNUSED(option) |
284 | Q_UNUSED(widget) |
285 | |
286 | if (!d_ptr->m_backgroundVisible) |
287 | return; |
288 | |
289 | painter->setOpacity(opacity()); |
290 | painter->setPen(d_ptr->m_pen); |
291 | painter->setBrush(d_ptr->m_brush); |
292 | painter->drawRoundedRect(rect: rect(), xRadius: d_ptr->roundness(size: rect().width()), yRadius: d_ptr->roundness(size: rect().height()), |
293 | mode: Qt::RelativeSize); |
294 | } |
295 | |
296 | |
297 | /*! |
298 | Sets the \a brush that is used to draw the background of the legend. |
299 | */ |
300 | void QLegend::setBrush(const QBrush &brush) |
301 | { |
302 | if (d_ptr->m_brush != brush) { |
303 | d_ptr->m_brush = brush; |
304 | update(); |
305 | emit colorChanged(color: brush.color()); |
306 | } |
307 | } |
308 | |
309 | /*! |
310 | Returns the brush used by the legend. |
311 | */ |
312 | QBrush QLegend::brush() const |
313 | { |
314 | if (d_ptr->m_brush == QChartPrivate::defaultBrush()) |
315 | return QBrush(); |
316 | else |
317 | return d_ptr->m_brush; |
318 | } |
319 | |
320 | void QLegend::setColor(QColor color) |
321 | { |
322 | QBrush b = brush(); |
323 | if (b.style() != Qt::SolidPattern || b.color() != color) { |
324 | b.setStyle(Qt::SolidPattern); |
325 | b.setColor(color); |
326 | setBrush(b); |
327 | } |
328 | } |
329 | |
330 | QColor QLegend::color() |
331 | { |
332 | return d_ptr->m_brush.color(); |
333 | } |
334 | |
335 | /*! |
336 | Sets the \a pen that is used to draw the legend borders. |
337 | */ |
338 | void QLegend::setPen(const QPen &pen) |
339 | { |
340 | if (d_ptr->m_pen != pen) { |
341 | d_ptr->m_pen = pen; |
342 | update(); |
343 | emit borderColorChanged(color: pen.color()); |
344 | } |
345 | } |
346 | |
347 | /*! |
348 | Returns the pen used by the legend. |
349 | */ |
350 | |
351 | QPen QLegend::pen() const |
352 | { |
353 | if (d_ptr->m_pen == QChartPrivate::defaultPen()) |
354 | return QPen(); |
355 | else |
356 | return d_ptr->m_pen; |
357 | } |
358 | |
359 | void QLegend::setFont(const QFont &font) |
360 | { |
361 | if (d_ptr->m_font != font) { |
362 | // Hide items to avoid flickering |
363 | d_ptr->items()->setVisible(false); |
364 | d_ptr->m_font = font; |
365 | foreach (QLegendMarker *marker, d_ptr->markers()) { |
366 | marker->setFont(d_ptr->m_font); |
367 | } |
368 | layout()->invalidate(); |
369 | emit fontChanged(font); |
370 | } |
371 | } |
372 | |
373 | QFont QLegend::font() const |
374 | { |
375 | return d_ptr->m_font; |
376 | } |
377 | |
378 | void QLegend::setBorderColor(QColor color) |
379 | { |
380 | QPen p = pen(); |
381 | if (p.color() != color) { |
382 | p.setColor(color); |
383 | setPen(p); |
384 | } |
385 | } |
386 | |
387 | QColor QLegend::borderColor() |
388 | { |
389 | return d_ptr->m_pen.color(); |
390 | } |
391 | |
392 | /*! |
393 | Sets the brush used to draw the legend labels to \a brush. |
394 | */ |
395 | void QLegend::setLabelBrush(const QBrush &brush) |
396 | { |
397 | if (d_ptr->m_labelBrush != brush) { |
398 | d_ptr->m_labelBrush = brush; |
399 | foreach (QLegendMarker *marker, d_ptr->markers()) { |
400 | marker->setLabelBrush(d_ptr->m_labelBrush); |
401 | // Note: The pen of the marker rectangle could be exposed in the public QLegend API |
402 | // instead of mapping it from label brush color |
403 | marker->setPen(brush.color()); |
404 | } |
405 | emit labelColorChanged(color: brush.color()); |
406 | } |
407 | } |
408 | |
409 | /*! |
410 | Returns the brush used to draw labels. |
411 | */ |
412 | QBrush QLegend::labelBrush() const |
413 | { |
414 | if (d_ptr->m_labelBrush == QChartPrivate::defaultBrush()) |
415 | return QBrush(); |
416 | else |
417 | return d_ptr->m_labelBrush; |
418 | } |
419 | |
420 | void QLegend::setLabelColor(QColor color) |
421 | { |
422 | QBrush b = labelBrush(); |
423 | if (b.style() != Qt::SolidPattern || b.color() != color) { |
424 | b.setStyle(Qt::SolidPattern); |
425 | b.setColor(color); |
426 | setLabelBrush(b); |
427 | } |
428 | } |
429 | |
430 | QColor QLegend::labelColor() const |
431 | { |
432 | return d_ptr->m_labelBrush.color(); |
433 | } |
434 | |
435 | |
436 | void QLegend::setAlignment(Qt::Alignment alignment) |
437 | { |
438 | if (d_ptr->m_alignment != alignment) { |
439 | d_ptr->m_alignment = alignment; |
440 | layout()->invalidate(); |
441 | } |
442 | } |
443 | |
444 | Qt::Alignment QLegend::alignment() const |
445 | { |
446 | return d_ptr->m_alignment; |
447 | } |
448 | |
449 | /*! |
450 | Detaches the legend from the chart. The chart will no longer adjust the layout of the legend. |
451 | */ |
452 | void QLegend::detachFromChart() |
453 | { |
454 | d_ptr->m_attachedToChart = false; |
455 | // layout()->invalidate(); |
456 | d_ptr->m_chart->layout()->invalidate(); |
457 | setParent(0); |
458 | |
459 | } |
460 | |
461 | /*! |
462 | Attaches the legend to a chart. The chart may adjust the layout of the legend. |
463 | */ |
464 | void QLegend::attachToChart() |
465 | { |
466 | d_ptr->m_attachedToChart = true; |
467 | // layout()->invalidate(); |
468 | d_ptr->m_chart->layout()->invalidate(); |
469 | setParent(d_ptr->m_chart); |
470 | } |
471 | |
472 | /*! |
473 | Returns \c true, if the legend is attached to a chart. |
474 | */ |
475 | bool QLegend::isAttachedToChart() |
476 | { |
477 | return d_ptr->m_attachedToChart; |
478 | } |
479 | |
480 | /*! |
481 | Sets the visibility of the legend background to \a visible. |
482 | */ |
483 | void QLegend::setBackgroundVisible(bool visible) |
484 | { |
485 | if (d_ptr->m_backgroundVisible != visible) { |
486 | d_ptr->m_backgroundVisible = visible; |
487 | update(); |
488 | emit backgroundVisibleChanged(visible); |
489 | } |
490 | } |
491 | |
492 | /*! |
493 | Returns the visibility of the legend background. |
494 | */ |
495 | bool QLegend::isBackgroundVisible() const |
496 | { |
497 | return d_ptr->m_backgroundVisible; |
498 | } |
499 | |
500 | /*! |
501 | Returns the list of markers in the legend. The list can be filtered by specifying |
502 | the \a series for which the markers are returned. |
503 | */ |
504 | QList<QLegendMarker*> QLegend::markers(QAbstractSeries *series) const |
505 | { |
506 | return d_ptr->markers(series); |
507 | } |
508 | |
509 | bool QLegend::reverseMarkers() |
510 | { |
511 | return d_ptr->m_reverseMarkers; |
512 | } |
513 | |
514 | void QLegend::setReverseMarkers(bool reverseMarkers) |
515 | { |
516 | if (d_ptr->m_reverseMarkers != reverseMarkers) { |
517 | d_ptr->m_reverseMarkers = reverseMarkers; |
518 | layout()->invalidate(); |
519 | emit reverseMarkersChanged(reverseMarkers); |
520 | } |
521 | } |
522 | |
523 | /*! |
524 | Returns whether the tooltips are shown for the legend labels |
525 | when they are elided. |
526 | */ |
527 | |
528 | bool QLegend::showToolTips() const |
529 | { |
530 | return d_ptr->m_showToolTips; |
531 | } |
532 | |
533 | /*! |
534 | When \a show is \c true, the legend labels will show a tooltip when |
535 | the mouse hovers over them if the label itself is shown elided. |
536 | This is \c false by default. |
537 | */ |
538 | |
539 | void QLegend::setShowToolTips(bool show) |
540 | { |
541 | if (d_ptr->m_showToolTips != show) { |
542 | d_ptr->m_showToolTips = show; |
543 | d_ptr->updateToolTips(); |
544 | emit showToolTipsChanged(showToolTips: show); |
545 | } |
546 | } |
547 | |
548 | QLegend::MarkerShape QLegend::markerShape() const |
549 | { |
550 | return d_ptr->m_markerShape; |
551 | } |
552 | |
553 | void QLegend::setMarkerShape(QLegend::MarkerShape shape) |
554 | { |
555 | QLegend::MarkerShape newShape = shape; |
556 | if (newShape == MarkerShapeDefault) |
557 | newShape = MarkerShapeRectangle; |
558 | if (d_ptr->m_markerShape != newShape) { |
559 | d_ptr->m_markerShape = newShape; |
560 | layout()->invalidate(); |
561 | emit markerShapeChanged(shape: newShape); |
562 | } |
563 | } |
564 | |
565 | /*! |
566 | \internal |
567 | \a event, see QGraphicsWidget for details. |
568 | */ |
569 | void QLegend::hideEvent(QHideEvent *event) |
570 | { |
571 | if (isAttachedToChart()) |
572 | d_ptr->m_presenter->layout()->invalidate(); |
573 | QGraphicsWidget::hideEvent(event); |
574 | } |
575 | /*! |
576 | \internal |
577 | \a event, see QGraphicsWidget for details. |
578 | */ |
579 | void QLegend::showEvent(QShowEvent *event) |
580 | { |
581 | if (isAttachedToChart()) |
582 | layout()->invalidate(); |
583 | QGraphicsWidget::showEvent(event); |
584 | //layout activation will show the items |
585 | } |
586 | |
587 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
588 | |
589 | QLegendPrivate::QLegendPrivate(ChartPresenter *presenter, QChart *chart, QLegend *q) |
590 | : q_ptr(q), |
591 | m_presenter(presenter), |
592 | m_layout(new LegendLayout(q)), |
593 | m_chart(chart), |
594 | m_items(new QGraphicsItemGroup(q)), |
595 | m_alignment(Qt::AlignTop), |
596 | m_brush(QChartPrivate::defaultBrush()), |
597 | m_pen(QChartPrivate::defaultPen()), |
598 | m_labelBrush(QChartPrivate::defaultBrush()), |
599 | m_diameter(5), |
600 | m_attachedToChart(true), |
601 | m_backgroundVisible(false), |
602 | m_reverseMarkers(false), |
603 | m_showToolTips(false), |
604 | m_markerShape(QLegend::MarkerShapeRectangle) |
605 | { |
606 | m_items->setHandlesChildEvents(false); |
607 | } |
608 | |
609 | QLegendPrivate::~QLegendPrivate() |
610 | { |
611 | |
612 | } |
613 | |
614 | void QLegendPrivate::setOffset(const QPointF &offset) |
615 | { |
616 | m_layout->setOffset(x: offset.x(), y: offset.y()); |
617 | } |
618 | |
619 | QPointF QLegendPrivate::offset() const |
620 | { |
621 | return m_layout->offset(); |
622 | } |
623 | |
624 | int QLegendPrivate::roundness(qreal size) |
625 | { |
626 | return 100 * m_diameter / int(size); |
627 | } |
628 | |
629 | QList<QLegendMarker*> QLegendPrivate::markers(QAbstractSeries *series) |
630 | { |
631 | // Return all markers |
632 | if (!series) { |
633 | return m_markers; |
634 | } |
635 | |
636 | // Create filtered list |
637 | QList<QLegendMarker *> markers; |
638 | foreach (QLegendMarker *marker, m_markers) { |
639 | if (marker->series() == series) { |
640 | markers.append(t: marker); |
641 | } |
642 | } |
643 | return markers; |
644 | } |
645 | |
646 | qreal QLegendPrivate::maxMarkerWidth() const |
647 | { |
648 | qreal maxWidth = 0.0; |
649 | for (int i = 0; i < m_markers.size(); i++) { |
650 | LegendMarkerItem *item = m_markers.at(i)->d_ptr->item(); |
651 | if (item) |
652 | maxWidth = qMax(a: item->markerRect().width(), b: maxWidth); |
653 | } |
654 | return maxWidth; |
655 | } |
656 | |
657 | void QLegendPrivate::handleSeriesAdded(QAbstractSeries *series) |
658 | { |
659 | if (m_series.contains(t: series)) { |
660 | return; |
661 | } |
662 | |
663 | QList<QLegendMarker*> newMarkers = series->d_ptr->createLegendMarkers(legend: q_ptr); |
664 | decorateMarkers(markers: newMarkers); |
665 | addMarkers(markers: newMarkers); |
666 | |
667 | QObject::connect(sender: series->d_ptr.data(), SIGNAL(countChanged()), receiver: this, SLOT(handleCountChanged())); |
668 | QObject::connect(sender: series, SIGNAL(visibleChanged()), receiver: this, SLOT(handleSeriesVisibleChanged())); |
669 | |
670 | m_series.append(t: series); |
671 | m_items->setVisible(false); |
672 | m_layout->invalidate(); |
673 | } |
674 | |
675 | void QLegendPrivate::handleSeriesRemoved(QAbstractSeries *series) |
676 | { |
677 | if (m_series.contains(t: series)) { |
678 | m_series.removeOne(t: series); |
679 | } |
680 | |
681 | // Find out, which markers to remove |
682 | QList<QLegendMarker *> removed; |
683 | foreach (QLegendMarker *m, m_markers) { |
684 | if (m->series() == series) { |
685 | removed << m; |
686 | } |
687 | } |
688 | removeMarkers(markers: removed); |
689 | |
690 | QObject::disconnect(sender: series->d_ptr.data(), SIGNAL(countChanged()), receiver: this, SLOT(handleCountChanged())); |
691 | QObject::disconnect(sender: series, SIGNAL(visibleChanged()), receiver: this, SLOT(handleSeriesVisibleChanged())); |
692 | |
693 | m_layout->invalidate(); |
694 | } |
695 | |
696 | void QLegendPrivate::handleSeriesVisibleChanged() |
697 | { |
698 | QAbstractSeries *series = qobject_cast<QAbstractSeries *> (object: sender()); |
699 | Q_ASSERT(series); |
700 | |
701 | foreach (QLegendMarker *marker, m_markers) { |
702 | if (marker->series() == series) { |
703 | marker->setVisible(series->isVisible()); |
704 | } |
705 | } |
706 | |
707 | if (m_chart->isVisible()) |
708 | m_layout->invalidate(); |
709 | } |
710 | |
711 | QObject *QLegendPrivate::relatedObject(const QLegendMarker *l) |
712 | { |
713 | return l->d_ptr->relatedObject(); |
714 | } |
715 | |
716 | // Find equivalent QLegendMarker by checking for relatedObject() |
717 | static int indexOfEquivalent(const QLegendMarker *needle, |
718 | const QList<QLegendMarker *> &hayStack) |
719 | { |
720 | const QObject *needleObject = QLegendPrivate::relatedObject(l: needle); |
721 | for (int i = 0, size = hayStack.size(); i < size; ++i) { |
722 | if (QLegendPrivate::relatedObject(l: hayStack.at(i)) == needleObject) |
723 | return i; |
724 | } |
725 | return -1; |
726 | } |
727 | |
728 | // Find QLegendMarker for series |
729 | static int indexOfSeries(const QAbstractSeries *series, |
730 | const QList<QLegendMarker *> &hayStack) |
731 | { |
732 | for (int i = 0, size = hayStack.size(); i < size; ++i) { |
733 | if (hayStack.at(i)->series() == series) |
734 | return i; |
735 | } |
736 | return -1; |
737 | } |
738 | |
739 | void QLegendPrivate::handleCountChanged() |
740 | { |
741 | // Here we handle the changes in marker count. |
742 | // Can happen for example when pieslice(s) have been added to or removed from pieseries. |
743 | |
744 | QAbstractSeriesPrivate *seriesP = qobject_cast<QAbstractSeriesPrivate *>(object: sender()); |
745 | QAbstractSeries *series = seriesP->q_ptr; |
746 | QList<QLegendMarker *> createdMarkers = seriesP->createLegendMarkers(legend: q_ptr); |
747 | QVector<bool> isNew(createdMarkers.size(), true); |
748 | |
749 | const int pos = indexOfSeries(series, hayStack: m_markers); |
750 | // Remove markers of the series from m_markers and check against the newly |
751 | // created ones. |
752 | if (pos != -1) { |
753 | while (pos < m_markers.size() && m_markers.at(i: pos)->series() == series) { |
754 | QLegendMarker *oldMarker = m_markers.takeAt(i: pos); |
755 | const int newIndex = indexOfEquivalent(needle: oldMarker, hayStack: createdMarkers); |
756 | if (newIndex == -1) { |
757 | removeMarkerHelper(marker: oldMarker); // no longer exists |
758 | } else { |
759 | // Replace newly created marker by its equivalent |
760 | delete createdMarkers[newIndex]; |
761 | createdMarkers[newIndex] = oldMarker; |
762 | isNew[newIndex] = false; |
763 | } |
764 | } |
765 | } |
766 | |
767 | for (int i = 0, size = createdMarkers.size(); i < size; ++i) { |
768 | if (isNew.at(i)) { |
769 | insertMarkerHelper(marker: createdMarkers.at(i)); |
770 | decorateMarker(marker: createdMarkers.at(i)); |
771 | } |
772 | } |
773 | |
774 | // Re-insert createdMarkers into m_markers in correct order. |
775 | if (pos == -1 || pos == m_markers.size()) { |
776 | m_markers.append(t: createdMarkers); |
777 | } else { |
778 | for (int c = createdMarkers.size() - 1; c >= 0; --c) |
779 | m_markers.insert(i: pos, t: createdMarkers.at(i: c)); |
780 | } |
781 | |
782 | q_ptr->layout()->invalidate(); |
783 | } |
784 | |
785 | // Helper function for marker insertion except m_markers handling |
786 | void QLegendPrivate::insertMarkerHelper(QLegendMarker *marker) |
787 | { |
788 | LegendMarkerItem *item = marker->d_ptr->item(); |
789 | m_items->addToGroup(item); |
790 | m_markerHash.insert(akey: item, avalue: marker); |
791 | } |
792 | |
793 | void QLegendPrivate::addMarkers(QList<QLegendMarker *> markers) |
794 | { |
795 | foreach (QLegendMarker *marker, markers) { |
796 | insertMarkerHelper(marker); |
797 | m_markers << marker; |
798 | } |
799 | } |
800 | |
801 | // Helper function for marker removal except m_markers handling |
802 | void QLegendPrivate::removeMarkerHelper(QLegendMarker *marker) |
803 | { |
804 | LegendMarkerItem *item = marker->d_ptr->item(); |
805 | item->setVisible(false); |
806 | m_items->removeFromGroup(item); |
807 | m_markerHash.remove(akey: item); |
808 | delete marker; |
809 | } |
810 | |
811 | void QLegendPrivate::removeMarkers(QList<QLegendMarker *> markers) |
812 | { |
813 | foreach (QLegendMarker *marker, markers) { |
814 | m_markers.removeOne(t: marker); |
815 | removeMarkerHelper(marker); |
816 | } |
817 | } |
818 | |
819 | void QLegendPrivate::decorateMarker(QLegendMarker *marker) |
820 | { |
821 | marker->setFont(m_font); |
822 | marker->setLabelBrush(m_labelBrush); |
823 | } |
824 | |
825 | void QLegendPrivate::decorateMarkers(QList<QLegendMarker *> markers) |
826 | { |
827 | for (QLegendMarker *marker : markers) |
828 | decorateMarker(marker); |
829 | } |
830 | |
831 | void QLegendPrivate::updateToolTips() |
832 | { |
833 | foreach (QLegendMarker *m, m_markers) { |
834 | if (m->d_ptr->m_item->displayedLabel() != m->label()) |
835 | m->d_ptr->m_item->setToolTip(m->label()); |
836 | else |
837 | m->d_ptr->m_item->setToolTip(QString()); |
838 | } |
839 | } |
840 | |
841 | QT_CHARTS_END_NAMESPACE |
842 | |
843 | #include "moc_qlegend.cpp" |
844 | #include "moc_qlegend_p.cpp" |
845 | |