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

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