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 <private/abstractbarchartitem_p.h>
31#include <private/bar_p.h>
32#include <QtCharts/QBarSet>
33#include <private/qbarset_p.h>
34#include <QtCharts/QAbstractBarSeries>
35#include <private/qabstractbarseries_p.h>
36#include <private/qchart_p.h>
37#include <private/chartpresenter_p.h>
38#include <private/charttheme_p.h>
39#include <private/baranimation_p.h>
40#include <private/chartdataset_p.h>
41#include <QtCore/QtMath>
42#include <QtGui/QPainter>
43#include <QtGui/QTextDocument>
44
45QT_CHARTS_BEGIN_NAMESPACE
46
47AbstractBarChartItem::AbstractBarChartItem(QAbstractBarSeries *series, QGraphicsItem* item) :
48 ChartItem(series->d_func(),item),
49 m_animation(0),
50 m_series(series),
51 m_firstCategory(-1),
52 m_lastCategory(-2),
53 m_categoryCount(0),
54 m_labelItemsMissing(false),
55 m_orientation(Qt::Horizontal),
56 m_resetAnimation(true)
57{
58 setAcceptedMouseButtons({});
59 setFlag(flag: ItemClipsChildrenToShape);
60 setFlag(flag: QGraphicsItem::ItemIsSelectable);
61 connect(sender: series->d_func(), SIGNAL(updatedLayout()), receiver: this, SLOT(handleLayoutChanged()));
62 connect(sender: series->d_func(), SIGNAL(updatedBars()), receiver: this, SLOT(handleUpdatedBars()));
63 connect(sender: series->d_func(), SIGNAL(labelsVisibleChanged(bool)), receiver: this, SLOT(handleLabelsVisibleChanged(bool)));
64 connect(sender: series->d_func(), SIGNAL(restructuredBars()), receiver: this, SLOT(handleDataStructureChanged()));
65 connect(sender: series->d_func(), signal: &QAbstractBarSeriesPrivate::setValueChanged,
66 receiver: this, slot: &AbstractBarChartItem::handleBarValueChange);
67 connect(sender: series->d_func(), signal: &QAbstractBarSeriesPrivate::setValueAdded,
68 receiver: this, slot: &AbstractBarChartItem::handleBarValueAdd);
69 connect(sender: series->d_func(), signal: &QAbstractBarSeriesPrivate::setValueRemoved,
70 receiver: this, slot: &AbstractBarChartItem::handleBarValueRemove);
71 connect(sender: series, SIGNAL(visibleChanged()), receiver: this, SLOT(handleVisibleChanged()));
72 connect(sender: series, SIGNAL(opacityChanged()), receiver: this, SLOT(handleOpacityChanged()));
73 connect(sender: series, SIGNAL(labelsFormatChanged(QString)), receiver: this, SLOT(handleUpdatedBars()));
74 connect(sender: series, SIGNAL(labelsFormatChanged(QString)), receiver: this, SLOT(positionLabels()));
75 connect(sender: series, SIGNAL(labelsPositionChanged(QAbstractBarSeries::LabelsPosition)),
76 receiver: this, SLOT(handleLabelsPositionChanged()));
77 connect(sender: series, SIGNAL(labelsAngleChanged(qreal)), receiver: this, SLOT(positionLabels()));
78 connect(sender: series, signal: &QAbstractBarSeries::labelsPrecisionChanged,
79 receiver: this, slot: &AbstractBarChartItem::handleUpdatedBars);
80 connect(sender: series, signal: &QAbstractBarSeries::labelsPrecisionChanged,
81 receiver: this, slot: &AbstractBarChartItem::positionLabels);
82 connect(sender: series->chart()->d_ptr->m_dataset, signal: &ChartDataSet::seriesAdded,
83 receiver: this, slot: &AbstractBarChartItem::handleSeriesAdded);
84 connect(sender: series->chart()->d_ptr->m_dataset, signal: &ChartDataSet::seriesRemoved,
85 receiver: this, slot: &AbstractBarChartItem::handleSeriesRemoved);
86 setZValue(ChartPresenter::BarSeriesZValue);
87 calculateSeriesPositionAdjustmentAndWidth();
88 handleDataStructureChanged();
89}
90
91AbstractBarChartItem::~AbstractBarChartItem()
92{
93}
94
95void AbstractBarChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
96{
97 Q_UNUSED(painter);
98 Q_UNUSED(option);
99 Q_UNUSED(widget);
100}
101
102QRectF AbstractBarChartItem::boundingRect() const
103{
104 return m_rect;
105}
106
107void AbstractBarChartItem::initializeFullLayout()
108{
109 qreal setCount = m_series->count();
110
111 for (int set = 0; set < setCount; set++) {
112 QBarSet *barSet = m_series->barSets().at(i: set);
113 const QList<Bar *> bars = m_barMap.value(akey: barSet);
114 for (int i = 0; i < bars.size(); i++) {
115 Bar *bar = bars.at(i);
116 initializeLayout(set, category: bar->index(), layoutIndex: bar->layoutIndex(), resetAnimation: true);
117 // Make bar initially hidden to avoid artifacts, layout setting will show it
118 bar->setVisible(false);
119 }
120 }
121}
122
123void AbstractBarChartItem::applyLayout(const QVector<QRectF> &layout)
124{
125 QSizeF size = geometry().size();
126 if (geometry().size().isValid()) {
127 if (m_animation) {
128 // If geometry changes along the value axis, do full animation reset, as
129 // it can cause "ungrounded" bars otherwise.
130 // Changes to axis labels on bar axis can occur naturally with scrolling,
131 // which can cause geometry changes that shouldn't trigger animation reset.
132 const bool sizeChanged = m_orientation == Qt::Horizontal
133 ? m_oldSize.width() != size.width()
134 : m_oldSize.height() != size.height();
135 m_oldSize = size;
136 if (m_resetAnimation || sizeChanged) {
137 initializeFullLayout();
138 m_resetAnimation = false;
139 }
140 m_animation->setup(oldLayout: m_layout, newLayout: layout);
141 presenter()->startAnimation(animation: m_animation);
142 } else {
143 setLayout(layout);
144 update();
145 }
146 }
147}
148
149void AbstractBarChartItem::setAnimation(BarAnimation *animation)
150{
151 m_animation = animation;
152 m_resetAnimation = true;
153}
154
155void AbstractBarChartItem::setLayout(const QVector<QRectF> &layout)
156{
157 int setCount = m_series->count();
158 if (layout.size() != m_layout.size() || m_barMap.size() != setCount)
159 return;
160
161 m_layout = layout;
162
163 const bool visible = m_series->isVisible();
164 for (int set = 0; set < setCount; set++) {
165 QBarSet *barSet = m_series->d_func()->barsetAt(index: set);
166 const QList<Bar *> bars = m_barMap.value(akey: barSet);
167 for (int i = 0; i < bars.size(); i++) {
168 Bar *bar = bars.at(i);
169 const QRectF &rect = layout.at(i: bar->layoutIndex());
170 bar->setRect(rect);
171 // Hide empty bars to avoid artifacts at animation start when adding a new series
172 // as it doesn't have correct axes yet
173 bar->setVisible(visible && !rect.isEmpty());
174 }
175 }
176
177 positionLabels();
178}
179
180void AbstractBarChartItem::resetAnimation()
181{
182 m_resetAnimation = true;
183}
184
185void AbstractBarChartItem::handleDomainUpdated()
186{
187 QRectF rect(QPointF(0,0),domain()->size());
188
189 if(m_rect != rect){
190 prepareGeometryChange();
191 m_rect = rect;
192 }
193
194 handleLayoutChanged();
195}
196
197void AbstractBarChartItem::handleLayoutChanged()
198{
199 if ((m_rect.width() <= 0) || (m_rect.height() <= 0))
200 return; // rect size zero.
201 updateBarItems();
202 QVector<QRectF> layout = calculateLayout();
203 handleUpdatedBars();
204 applyLayout(layout);
205}
206
207void AbstractBarChartItem::handleLabelsVisibleChanged(bool visible)
208{
209 bool newVisible = visible && m_series->isVisible();
210 for (const QList<Bar *> &bars : qAsConst(t&: m_barMap)) {
211 for (Bar *bar : bars) {
212 QGraphicsTextItem *label = bar->labelItem();
213 if (label)
214 label->setVisible(newVisible);
215 }
216 }
217 if (newVisible) {
218 handleUpdatedBars();
219 positionLabels();
220 }
221 update();
222}
223
224void AbstractBarChartItem::handleDataStructureChanged()
225{
226 handleSetStructureChange();
227 handleLayoutChanged();
228}
229
230void AbstractBarChartItem::handleVisibleChanged()
231{
232 bool visible = m_series->isVisible();
233 handleLabelsVisibleChanged(visible: m_series->isLabelsVisible());
234
235 for (auto i = m_barMap.cbegin(), end = m_barMap.cend(); i != end; ++i) {
236 const QList<Bar *> &bars = i.value();
237 for (int j = 0; j < bars.size(); j++) {
238 Bar *bar = bars.at(i: j);
239 bar->setVisible(visible && i.key()->at(index: bar->index()) != 0.0);
240 }
241 }
242}
243
244void AbstractBarChartItem::handleOpacityChanged()
245{
246 foreach (QGraphicsItem *item, childItems())
247 item->setOpacity(m_series->opacity());
248}
249
250void AbstractBarChartItem::handleUpdatedBars()
251{
252 if (!m_series->d_func()->blockBarUpdate()) {
253 // Handle changes in pen, brush, labels etc.
254 int setCount = m_series->count();
255 const bool seriesVisualsDirty = m_series->d_func()->visualsDirty();
256 const bool seriesLabelsDirty = m_series->d_func()->labelsDirty();
257 m_series->d_func()->setVisualsDirty(false);
258
259 const bool updateLabels =
260 m_series->isLabelsVisible() && m_series->isVisible() && presenter();
261 if (updateLabels) {
262 createLabelItems();
263 m_series->d_func()->setLabelsDirty(false);
264 }
265
266 for (int set = 0; set < setCount; set++) {
267 QBarSet *barSet = m_series->d_func()->barsetAt(index: set);
268 QBarSetPrivate *barSetP = barSet->d_ptr.data();
269 const bool setVisualsDirty = barSetP->visualsDirty();
270 const bool setLabelsDirty = barSetP->labelsDirty();
271 barSetP->setVisualsDirty(false);
272 if (updateLabels)
273 barSetP->setLabelsDirty(false);
274 const int actualBarCount = barSet->count();
275 const QList<Bar *> bars = m_barMap.value(akey: barSet);
276 for (int i = 0; i < bars.size(); i++) {
277 Bar *bar = bars.at(i);
278 if (seriesVisualsDirty || setVisualsDirty || bar->visualsDirty()) {
279 bar->setPen(barSetP->m_pen);
280 bar->setBrush(barSetP->m_brush);
281 bar->setVisualsDirty(false);
282 bar->update();
283 }
284 if (updateLabels && actualBarCount > bar->index()) {
285 if (seriesLabelsDirty || setLabelsDirty || bar->labelDirty()) {
286 bar->setLabelDirty(false);
287 QGraphicsTextItem *label = bar->labelItem();
288 QString valueLabel;
289 qreal value = barSetP->value(index: bar->index());
290 if (value == 0.0) {
291 label->setVisible(false);
292 } else {
293 label->setVisible(m_series->isLabelsVisible());
294 valueLabel = generateLabelText(set, category: bar->index(), value);
295 }
296 label->setHtml(valueLabel);
297 label->setFont(barSetP->m_labelFont);
298 label->setDefaultTextColor(barSetP->m_labelBrush.color());
299 label->update();
300 }
301 }
302 }
303 }
304 }
305}
306
307void AbstractBarChartItem::handleLabelsPositionChanged()
308{
309 positionLabels();
310}
311
312void AbstractBarChartItem::positionLabels()
313{
314 // By default position labels on horizontal bar series
315 // Vertical bar series overload positionLabels() to call positionLabelsVertical()
316
317 if (!m_series->isLabelsVisible())
318 return;
319 createLabelItems();
320
321 QTransform transform;
322 const qreal angle = m_series->d_func()->labelsAngle();
323 if (angle != 0.0)
324 transform.rotate(a: angle);
325
326 int setCount = m_series->count();
327 for (int set = 0; set < setCount; set++) {
328 QBarSet *barSet = m_series->d_func()->barsetAt(index: set);
329 const QList<Bar *> bars = m_barMap.value(akey: barSet);
330 for (int i = 0; i < bars.size(); i++) {
331 Bar *bar = bars.at(i);
332 QGraphicsTextItem *label = bar->labelItem();
333
334 QRectF labelRect = label->boundingRect();
335 QPointF center = labelRect.center();
336
337 qreal xPos = 0;
338 qreal yPos = m_layout.at(i: bar->layoutIndex()).center().y() - center.y();
339
340 int xDiff = 0;
341 if (angle != 0.0) {
342 label->setTransformOriginPoint(ax: center.x(), ay: center.y());
343 label->setRotation(m_series->d_func()->labelsAngle());
344 qreal oldWidth = labelRect.width();
345 labelRect = transform.mapRect(labelRect);
346 xDiff = (labelRect.width() - oldWidth) / 2;
347 }
348
349 int offset = bar->pen().width() / 2 + 2;
350
351 switch (m_series->labelsPosition()) {
352 case QAbstractBarSeries::LabelsCenter:
353 xPos = m_layout.at(i: bar->layoutIndex()).center().x() - center.x();
354 break;
355 case QAbstractBarSeries::LabelsOutsideEnd:
356 xPos = m_layout.at(i: bar->layoutIndex()).right() + offset + xDiff;
357 if (xPos + labelRect.width() - offset <= m_rect.right())
358 break;
359 Q_FALLTHROUGH();
360 case QAbstractBarSeries::LabelsInsideEnd:
361 xPos = m_layout.at(i: bar->layoutIndex()).right() - labelRect.width() - offset + xDiff;
362 break;
363 case QAbstractBarSeries::LabelsInsideBase:
364 xPos = m_layout.at(i: bar->layoutIndex()).left() + offset + xDiff;
365 break;
366 default:
367 // Invalid position, never comes here
368 break;
369 }
370
371 label->setPos(ax: xPos, ay: yPos);
372 label->setZValue(zValue() + 1);
373 }
374 }
375}
376
377void AbstractBarChartItem::handleBarValueChange(int index, QtCharts::QBarSet *barset)
378{
379 markLabelsDirty(barset, index, count: 1);
380 handleLayoutChanged();
381}
382
383void AbstractBarChartItem::handleBarValueAdd(int index, int count, QBarSet *barset)
384{
385 Q_UNUSED(count)
386
387 // Value insertions into middle of barset need to dirty the rest of the labels of the set
388 markLabelsDirty(barset, index, count: -1);
389 handleLayoutChanged();
390}
391
392void AbstractBarChartItem::handleBarValueRemove(int index, int count, QBarSet *barset)
393{
394 Q_UNUSED(count)
395
396 // Value removals from the middle of barset need to dirty the rest of the labels of the set.
397 markLabelsDirty(barset, index, count: -1);
398
399 // make sure labels are not visible for removed bars
400 const auto bars = m_barMap.value(akey: barset);
401 for (int c = barset->count(); c < bars.count(); ++c) {
402 auto label = bars.at(i: c)->labelItem();
403 if (label)
404 label->setVisible(false);
405 }
406
407 handleLayoutChanged();
408}
409
410void AbstractBarChartItem::handleSeriesAdded(QAbstractSeries *series)
411{
412 Q_UNUSED(series)
413
414 // If the parent series was added, do nothing, as series pos and width calculations will
415 // happen anyway.
416 QAbstractBarSeries *addedSeries = static_cast<QAbstractBarSeries *>(series);
417 if (addedSeries != m_series) {
418 calculateSeriesPositionAdjustmentAndWidth();
419 handleLayoutChanged();
420 }
421}
422
423void AbstractBarChartItem::handleSeriesRemoved(QAbstractSeries *series)
424{
425 // If the parent series was removed, disconnect everything connected by this item,
426 // as the item will be scheduled for deletion but it is done asynchronously with deleteLater.
427 QAbstractBarSeries *removedSeries = static_cast<QAbstractBarSeries *>(series);
428 if (removedSeries == m_series) {
429 disconnect(sender: m_series->d_func(), signal: 0, receiver: this, member: 0);
430 disconnect(sender: m_series, signal: 0, receiver: this, member: 0);
431 disconnect(sender: m_series->chart()->d_ptr->m_dataset, signal: 0, receiver: this, member: 0);
432 } else {
433 calculateSeriesPositionAdjustmentAndWidth();
434 handleLayoutChanged();
435 }
436}
437
438void AbstractBarChartItem::positionLabelsVertical()
439{
440 if (!m_series->isLabelsVisible())
441 return;
442 createLabelItems();
443
444 QTransform transform;
445 const qreal angle = m_series->d_func()->labelsAngle();
446 if (angle != 0.0)
447 transform.rotate(a: angle);
448
449 int setCount = m_series->count();
450 for (int set = 0; set < setCount; set++) {
451 QBarSet *barSet = m_series->d_func()->barsetAt(index: set);
452 const QList<Bar *> bars = m_barMap.value(akey: barSet);
453 for (int i = 0; i < bars.size(); i++) {
454 Bar *bar = bars.at(i);
455 QGraphicsTextItem *label = bar->labelItem();
456
457 QRectF labelRect = label->boundingRect();
458 QPointF center = labelRect.center();
459
460 qreal xPos = m_layout.at(i: bar->layoutIndex()).center().x() - center.x();
461 qreal yPos = 0;
462
463 int yDiff = 0;
464 if (angle != 0.0) {
465 label->setTransformOriginPoint(ax: center.x(), ay: center.y());
466 label->setRotation(m_series->d_func()->labelsAngle());
467 qreal oldHeight = labelRect.height();
468 labelRect = transform.mapRect(labelRect);
469 yDiff = (labelRect.height() - oldHeight) / 2;
470 }
471
472 int offset = bar->pen().width() / 2 + 2;
473
474 switch (m_series->labelsPosition()) {
475 case QAbstractBarSeries::LabelsCenter:
476 yPos = m_layout.at(i: bar->layoutIndex()).center().y() - center.y();
477 break;
478 case QAbstractBarSeries::LabelsOutsideEnd:
479 yPos = m_layout.at(i: bar->layoutIndex()).top() - labelRect.height() - offset + yDiff;
480 if (yPos + offset >= m_rect.y())
481 break;
482 Q_FALLTHROUGH();
483 case QAbstractBarSeries::LabelsInsideEnd:
484 yPos = m_layout.at(i: bar->layoutIndex()).top() + offset + yDiff;
485 break;
486 case QAbstractBarSeries::LabelsInsideBase:
487 yPos = m_layout.at(i: bar->layoutIndex()).bottom() - labelRect.height() - offset + yDiff;
488 break;
489 default:
490 // Invalid position, never comes here
491 break;
492 }
493
494 label->setPos(ax: xPos, ay: yPos);
495 label->setZValue(zValue() + 1);
496 }
497 }
498}
499
500void AbstractBarChartItem::createLabelItems()
501{
502 if (!m_labelItemsMissing)
503 return;
504
505 m_labelItemsMissing = false;
506
507 for (const QList<Bar *> &bars : qAsConst(t&: m_barMap)) {
508 for (Bar *bar : bars) {
509 QGraphicsTextItem *label = bar->labelItem();
510 if (!label) {
511 QGraphicsTextItem *newLabel = new QGraphicsTextItem(this);
512 newLabel->setAcceptHoverEvents(false);
513 newLabel->document()->setDocumentMargin(ChartPresenter::textMargin());
514 bar->setLabelItem(newLabel);
515 }
516 }
517 }
518}
519
520// This function is called whenever barsets change
521void AbstractBarChartItem::handleSetStructureChange()
522{
523 QList<QBarSet *> newSets = m_series->barSets();
524 QList<QBarSet *> oldSets = m_barMap.keys();
525
526 // Remove obsolete sets
527 for (int i = 0; i < oldSets.size(); i++) {
528 if (!newSets.contains(t: oldSets.at(i))) {
529 qDeleteAll(c: m_barMap.value(akey: oldSets.at(i)));
530 m_barMap.remove(key: oldSets.at(i));
531 }
532 }
533
534 // Create new sets
535 for (int s = 0; s < newSets.size(); s++) {
536 QBarSet *set = newSets.at(i: s);
537 if (!m_barMap.contains(key: set)) {
538 QList<Bar *> bars;
539 m_barMap.insert(key: set, value: bars);
540 } else {
541 // Dirty the old set labels to ensure labels are updated correctly on all series types
542 markLabelsDirty(barset: set, index: 0, count: -1);
543 }
544 }
545
546 if (themeManager())
547 themeManager()->updateSeries(series: m_series);
548}
549
550QString AbstractBarChartItem::generateLabelText(int set, int category, qreal value)
551{
552 Q_UNUSED(set);
553 Q_UNUSED(category);
554 static const QString valueTag(QLatin1String("@value"));
555 QString valueString = presenter()->numberToString(value, f: 'g', prec: m_series->labelsPrecision());
556 QString valueLabel;
557 if (m_series->labelsFormat().isEmpty()) {
558 valueLabel = valueString;
559 } else {
560 valueLabel = m_series->labelsFormat();
561 valueLabel.replace(before: valueTag, after: valueString);
562 }
563
564 return valueLabel;
565}
566
567void AbstractBarChartItem::updateBarItems()
568{
569 int min(0);
570 int max(0);
571 if (m_orientation == Qt::Vertical) {
572 min = qFloor(v: domain()->minX()) - 1;
573 max = qCeil(v: domain()->maxX()) + 1;
574 } else {
575 min = qFloor(v: domain()->minY()) - 1;
576 max = qCeil(v: domain()->maxY()) + 1;
577 }
578
579 int lastBarIndex = m_series->d_func()->categoryCount() - 1;
580
581 if (lastBarIndex < 0) {
582 // Indicate invalid categories by negatives
583 // Last should be one less than the first to make loops work right in case of no categories
584 m_firstCategory = -1;
585 m_lastCategory = -2;
586 m_categoryCount = 0;
587 } else {
588 m_firstCategory = qMax(a: qMin(a: min, b: lastBarIndex), b: 0);
589 m_lastCategory = qMax(a: qMin(a: max, b: lastBarIndex), b: m_firstCategory);
590 m_categoryCount = m_lastCategory - m_firstCategory + 1;
591 }
592
593 QList<QBarSet *> newSets = m_series->barSets();
594 QList<QBarSet *> oldSets = m_barMap.keys();
595
596 Q_ASSERT(newSets.size() == oldSets.size());
597
598 int layoutSize = m_categoryCount * newSets.size();
599
600 QVector<QRectF> oldLayout = m_layout;
601 if (layoutSize != m_layout.size())
602 m_layout.resize(size: layoutSize);
603
604 // Create new graphic items for bars or remove excess ones
605 int layoutIndex = 0;
606 for (int s = 0; s < newSets.size(); s++) {
607 QBarSet *set = newSets.at(i: s);
608 QList<Bar *> bars = m_barMap.value(akey: set);
609 int addCount = m_categoryCount - bars.size();
610 if (addCount > 0) {
611 for (int c = 0; c < addCount; c++) {
612 Bar *bar = new Bar(set, this);
613 bars.append(t: bar);
614 connect(sender: bar, signal: &Bar::clicked, receiver: m_series, slot: &QAbstractBarSeries::clicked);
615 connect(sender: bar, signal: &Bar::hovered, receiver: m_series, slot: &QAbstractBarSeries::hovered);
616 connect(sender: bar, signal: &Bar::pressed, receiver: m_series, slot: &QAbstractBarSeries::pressed);
617 connect(sender: bar, signal: &Bar::released, receiver: m_series, slot: &QAbstractBarSeries::released);
618 connect(sender: bar, signal: &Bar::doubleClicked, receiver: m_series, slot: &QAbstractBarSeries::doubleClicked);
619
620 connect(sender: bar, signal: &Bar::clicked, receiver: set, slot: &QBarSet::clicked);
621 connect(sender: bar, signal: &Bar::hovered, receiver: set, slot: &QBarSet::hovered);
622 connect(sender: bar, signal: &Bar::pressed, receiver: set, slot: &QBarSet::pressed);
623 connect(sender: bar, signal: &Bar::released, receiver: set, slot: &QBarSet::released);
624 connect(sender: bar, signal: &Bar::doubleClicked, receiver: set, slot: &QBarSet::doubleClicked);
625
626 m_labelItemsMissing = true;
627 }
628 }
629 // Update bar indexes
630 QHash<int, Bar*> indexMap;
631 QVector<Bar *> unassignedBars(m_categoryCount, nullptr);
632 int unassignedIndex(0);
633 QList<Bar *> newBars;
634 newBars.reserve(size: m_categoryCount);
635 for (int c = 0; c < bars.size(); c++) {
636 Bar *bar = bars.at(i: c);
637 if (bar->index() < m_firstCategory || bar->index() > m_lastCategory) {
638 // Delete excess unassigned bars first
639 if (addCount < 0) {
640 addCount++;
641 delete bar;
642 bar = nullptr;
643 } else {
644 unassignedBars[unassignedIndex++] = bar;
645 bar->setLayoutIndex(layoutIndex);
646 newBars.append(t: bar);
647 layoutIndex++;
648 }
649 } else {
650 indexMap.insert(key: bar->index(), value: bar);
651 newBars.append(t: bar);
652 m_layout[layoutIndex] = oldLayout.at(i: bar->layoutIndex());
653 bar->setLayoutIndex(layoutIndex);
654 layoutIndex++;
655 }
656 }
657 unassignedIndex = 0;
658 for (int c = m_firstCategory; c <= m_lastCategory; c++) {
659 Bar *bar = indexMap.value(key: c);
660 if (!bar) {
661 bar = unassignedBars.at(i: unassignedIndex++);
662 bar->setIndex(c);
663 indexMap.insert(key: bar->index(), value: bar);
664 }
665 }
666
667 m_indexForBarMap.insert(key: set, value: indexMap);
668
669 if (m_animation) {
670 for (int i = 0; i < unassignedIndex; i++) {
671 Bar *bar = unassignedBars.at(i);
672 initializeLayout(set: s, category: bar->index(), layoutIndex: bar->layoutIndex(), resetAnimation: m_resetAnimation);
673 bar->setRect(m_layout.at(i: bar->layoutIndex()));
674 // Make bar initially hidden to avoid artifacts, layout setting will show it
675 bar->setVisible(false);
676 }
677 }
678
679 m_barMap.insert(key: set, value: newBars);
680 }
681}
682
683void AbstractBarChartItem::markLabelsDirty(QBarSet *barset, int index, int count)
684{
685 Q_ASSERT(barset);
686
687 if (index <= 0 && count < 0) {
688 barset->d_ptr.data()->setLabelsDirty(true);
689 } else {
690 const QList<Bar *> bars = m_barMap.value(akey: barset);
691 const int maxIndex = count > 0 ? index + count : barset->count();
692 for (int i = 0; i < bars.size(); i++) {
693 Bar *bar = bars.at(i);
694 if (bar->index() >= index && bar->index() < maxIndex)
695 bar->setLabelDirty(true);
696 }
697 }
698}
699
700void AbstractBarChartItem::calculateSeriesPositionAdjustmentAndWidth()
701{
702 m_seriesPosAdjustment = 0.0;
703 m_seriesWidth = 1.0;
704
705 if (!m_series->chart())
706 return;
707
708 // Find out total number of bar series in chart and the index of this series among them
709 const QList<QAbstractSeries *> seriesList = m_series->chart()->series();
710 int index = -1;
711 int count = 0;
712 for (QAbstractSeries *series : seriesList) {
713 if (qobject_cast<QAbstractBarSeries *>(object: series)){
714 if (series == m_series)
715 index = count;
716 count++;
717 }
718 }
719 if (index > -1 && count > 1) {
720 m_seriesWidth = 1.0 / count;
721 m_seriesPosAdjustment = (m_seriesWidth * index) + (m_seriesWidth / 2.0) - 0.5;
722 }
723}
724
725ChartAnimation *AbstractBarChartItem::animation() const
726{
727 return m_animation;
728}
729
730QT_CHARTS_END_NAMESPACE
731
732#include "moc_abstractbarchartitem_p.cpp"
733

source code of qtcharts/src/charts/barchart/abstractbarchartitem.cpp