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 foreach (QGraphicsItem *item, childItems())
221 item->setOpacity(m_series->opacity());
222}
223
224void AbstractBarChartItem::handleUpdatedBars()
225{
226 if (!m_series->d_func()->blockBarUpdate()) {
227 // Handle changes in pen, brush, labels etc.
228 int setCount = m_series->count();
229 const bool seriesVisualsDirty = m_series->d_func()->visualsDirty();
230 const bool seriesLabelsDirty = m_series->d_func()->labelsDirty();
231 m_series->d_func()->setVisualsDirty(false);
232
233 const bool updateLabels =
234 m_series->isLabelsVisible() && m_series->isVisible() && presenter();
235 if (updateLabels) {
236 createLabelItems();
237 m_series->d_func()->setLabelsDirty(false);
238 }
239
240 for (int set = 0; set < setCount; set++) {
241 QBarSet *barSet = m_series->d_func()->barsetAt(index: set);
242
243 QBarSetPrivate *barSetP = barSet->d_ptr.data();
244 const bool setVisualsDirty = barSetP->visualsDirty();
245 const bool setLabelsDirty = barSetP->labelsDirty();
246 barSetP->setVisualsDirty(false);
247 if (updateLabels)
248 barSetP->setLabelsDirty(false);
249 const int actualBarCount = barSet->count();
250 const QList<Bar *> bars = m_barMap.value(key: barSet);
251 for (int i = 0; i < bars.size(); i++) {
252 Bar *bar = bars.at(i);
253 if (seriesVisualsDirty || setVisualsDirty || bar->visualsDirty()) {
254 bar->setPen(barSetP->m_pen);
255 if (barSet->isBarSelected(index: i) && barSetP->m_selectedColor.isValid())
256 bar->setBrush(barSetP->m_selectedColor);
257 else
258 bar->setBrush(barSetP->m_brush);
259
260 bar->setVisualsDirty(false);
261 bar->update();
262 }
263 if (updateLabels && actualBarCount > bar->index()) {
264 if (seriesLabelsDirty || setLabelsDirty || bar->labelDirty()) {
265 bar->setLabelDirty(false);
266 QGraphicsTextItem *label = bar->labelItem();
267 QString valueLabel;
268 qreal value = barSetP->value(index: bar->index());
269 if (value == 0.0) {
270 label->setVisible(false);
271 } else {
272 label->setVisible(m_series->isLabelsVisible());
273 valueLabel = generateLabelText(set, category: bar->index(), value);
274 }
275 label->setHtml(valueLabel);
276 label->setFont(barSetP->m_labelFont);
277 label->setDefaultTextColor(barSetP->m_labelBrush.color());
278 label->update();
279 }
280 }
281 }
282 }
283 }
284}
285
286void AbstractBarChartItem::handleLabelsPositionChanged()
287{
288 positionLabels();
289}
290
291void AbstractBarChartItem::positionLabels()
292{
293 // By default position labels on horizontal bar series
294 // Vertical bar series overload positionLabels() to call positionLabelsVertical()
295
296 if (!m_series->isLabelsVisible())
297 return;
298 createLabelItems();
299
300 QTransform transform;
301 const qreal angle = m_series->d_func()->labelsAngle();
302 if (angle != 0.0)
303 transform.rotate(a: angle);
304
305 int setCount = m_series->count();
306 for (int set = 0; set < setCount; set++) {
307 QBarSet *barSet = m_series->d_func()->barsetAt(index: set);
308 const QList<Bar *> bars = m_barMap.value(key: barSet);
309 for (int i = 0; i < bars.size(); i++) {
310 Bar *bar = bars.at(i);
311 QGraphicsTextItem *label = bar->labelItem();
312
313 QRectF labelRect = label->boundingRect();
314 QPointF center = labelRect.center();
315
316 qreal xPos = 0;
317 qreal yPos = m_layout.at(i: bar->layoutIndex()).center().y() - center.y();
318
319 int xDiff = 0;
320 if (angle != 0.0) {
321 label->setTransformOriginPoint(ax: center.x(), ay: center.y());
322 label->setRotation(m_series->d_func()->labelsAngle());
323 qreal oldWidth = labelRect.width();
324 labelRect = transform.mapRect(labelRect);
325 xDiff = (labelRect.width() - oldWidth) / 2;
326 }
327
328 int offset = bar->pen().width() / 2 + 2;
329
330 switch (m_series->labelsPosition()) {
331 case QAbstractBarSeries::LabelsCenter:
332 xPos = m_layout.at(i: bar->layoutIndex()).center().x() - center.x();
333 break;
334 case QAbstractBarSeries::LabelsOutsideEnd:
335 xPos = m_layout.at(i: bar->layoutIndex()).right() + offset + xDiff;
336 if (xPos + labelRect.width() - offset <= m_rect.right())
337 break;
338 Q_FALLTHROUGH();
339 case QAbstractBarSeries::LabelsInsideEnd:
340 xPos = m_layout.at(i: bar->layoutIndex()).right() - labelRect.width() - offset + xDiff;
341 break;
342 case QAbstractBarSeries::LabelsInsideBase:
343 xPos = m_layout.at(i: bar->layoutIndex()).left() + offset + xDiff;
344 break;
345 default:
346 // Invalid position, never comes here
347 break;
348 }
349
350 label->setPos(ax: xPos, ay: yPos);
351 label->setZValue(zValue() + 1);
352 }
353 }
354}
355
356void AbstractBarChartItem::handleBarValueChange(int index, QBarSet *barset)
357{
358 markLabelsDirty(barset, index, count: 1);
359 handleLayoutChanged();
360}
361
362void AbstractBarChartItem::handleBarValueAdd(int index, int count, QBarSet *barset)
363{
364 Q_UNUSED(count);
365
366 // Value insertions into middle of barset need to dirty the rest of the labels of the set
367 markLabelsDirty(barset, index, count: -1);
368 handleLayoutChanged();
369}
370
371void AbstractBarChartItem::handleBarValueRemove(int index, int count, QBarSet *barset)
372{
373 Q_UNUSED(count);
374
375 // Value removals from the middle of barset need to dirty the rest of the labels of the set.
376 markLabelsDirty(barset, index, count: -1);
377
378 // make sure labels are not visible for removed bars
379 const auto bars = m_barMap.value(key: barset);
380 for (int c = barset->count(); c < bars.size(); ++c) {
381 auto label = bars.at(i: c)->labelItem();
382 if (label)
383 label->setVisible(false);
384 }
385
386 handleLayoutChanged();
387}
388
389void AbstractBarChartItem::handleSeriesAdded(QAbstractSeries *series)
390{
391 Q_UNUSED(series);
392
393 // If the parent series was added, do nothing, as series pos and width calculations will
394 // happen anyway.
395 QAbstractBarSeries *addedSeries = static_cast<QAbstractBarSeries *>(series);
396 if (addedSeries != m_series) {
397 calculateSeriesPositionAdjustmentAndWidth();
398 handleLayoutChanged();
399 }
400}
401
402void AbstractBarChartItem::handleSeriesRemoved(QAbstractSeries *series)
403{
404 // If the parent series was removed, disconnect everything connected by this item,
405 // as the item will be scheduled for deletion but it is done asynchronously with deleteLater.
406 QAbstractBarSeries *removedSeries = static_cast<QAbstractBarSeries *>(series);
407 if (removedSeries == m_series) {
408 disconnect(sender: m_series->d_func(), signal: 0, receiver: this, member: 0);
409 disconnect(sender: m_series, signal: 0, receiver: this, member: 0);
410 disconnect(sender: m_series->chart()->d_ptr->m_dataset, signal: 0, receiver: this, member: 0);
411 } else {
412 calculateSeriesPositionAdjustmentAndWidth();
413 handleLayoutChanged();
414 }
415}
416
417void AbstractBarChartItem::positionLabelsVertical()
418{
419 if (!m_series->isLabelsVisible())
420 return;
421 createLabelItems();
422
423 QTransform transform;
424 const qreal angle = m_series->d_func()->labelsAngle();
425 if (angle != 0.0)
426 transform.rotate(a: angle);
427
428 int setCount = m_series->count();
429 for (int set = 0; set < setCount; set++) {
430 QBarSet *barSet = m_series->d_func()->barsetAt(index: set);
431 const QList<Bar *> bars = m_barMap.value(key: barSet);
432 for (int i = 0; i < bars.size(); i++) {
433 Bar *bar = bars.at(i);
434 QGraphicsTextItem *label = bar->labelItem();
435
436 QRectF labelRect = label->boundingRect();
437 QPointF center = labelRect.center();
438
439 qreal xPos = m_layout.at(i: bar->layoutIndex()).center().x() - center.x();
440 qreal yPos = 0;
441
442 int yDiff = 0;
443 if (angle != 0.0) {
444 label->setTransformOriginPoint(ax: center.x(), ay: center.y());
445 label->setRotation(m_series->d_func()->labelsAngle());
446 qreal oldHeight = labelRect.height();
447 labelRect = transform.mapRect(labelRect);
448 yDiff = (labelRect.height() - oldHeight) / 2;
449 }
450
451 int offset = bar->pen().width() / 2 + 2;
452
453 switch (m_series->labelsPosition()) {
454 case QAbstractBarSeries::LabelsCenter:
455 yPos = m_layout.at(i: bar->layoutIndex()).center().y() - center.y();
456 break;
457 case QAbstractBarSeries::LabelsOutsideEnd:
458 yPos = m_layout.at(i: bar->layoutIndex()).top() - labelRect.height() - offset + yDiff;
459 if (yPos + offset >= m_rect.y())
460 break;
461 Q_FALLTHROUGH();
462 case QAbstractBarSeries::LabelsInsideEnd:
463 yPos = m_layout.at(i: bar->layoutIndex()).top() + offset + yDiff;
464 break;
465 case QAbstractBarSeries::LabelsInsideBase:
466 yPos = m_layout.at(i: bar->layoutIndex()).bottom() - labelRect.height() - offset + yDiff;
467 break;
468 default:
469 // Invalid position, never comes here
470 break;
471 }
472
473 label->setPos(ax: xPos, ay: yPos);
474 label->setZValue(zValue() + 1);
475 }
476 }
477}
478
479void AbstractBarChartItem::createLabelItems()
480{
481 if (!m_labelItemsMissing)
482 return;
483
484 m_labelItemsMissing = false;
485
486 for (const QList<Bar *> &bars : std::as_const(t&: m_barMap)) {
487 for (Bar *bar : bars) {
488 QGraphicsTextItem *label = bar->labelItem();
489 if (!label) {
490 QGraphicsTextItem *newLabel = new QGraphicsTextItem(this);
491 newLabel->setAcceptHoverEvents(false);
492 newLabel->document()->setDocumentMargin(ChartPresenter::textMargin());
493 bar->setLabelItem(newLabel);
494 }
495 }
496 }
497}
498
499// This function is called whenever barsets change
500void AbstractBarChartItem::handleSetStructureChange()
501{
502 QList<QBarSet *> newSets = m_series->barSets();
503 QList<QBarSet *> oldSets = m_barMap.keys();
504
505 // Remove obsolete sets
506 for (int i = 0; i < oldSets.size(); i++) {
507 if (!newSets.contains(t: oldSets.at(i))) {
508 qDeleteAll(c: m_barMap.value(key: oldSets.at(i)));
509 m_barMap.remove(key: oldSets.at(i));
510 }
511 }
512
513 // Create new sets
514 for (int s = 0; s < newSets.size(); s++) {
515 QBarSet *set = newSets.at(i: s);
516 if (!m_barMap.contains(key: set)) {
517 QList<Bar *> bars;
518 m_barMap.insert(key: set, value: bars);
519 } else {
520 // Dirty the old set labels to ensure labels are updated correctly on all series types
521 markLabelsDirty(barset: set, index: 0, count: -1);
522 }
523 }
524
525 if (themeManager())
526 themeManager()->updateSeries(series: m_series);
527}
528
529QString AbstractBarChartItem::generateLabelText(int set, int category, qreal value)
530{
531 Q_UNUSED(set);
532 Q_UNUSED(category);
533 static const QString valueTag(QLatin1String("@value"));
534 QString valueString = presenter()->numberToString(value, f: 'g', prec: m_series->labelsPrecision());
535 QString valueLabel;
536 if (m_series->labelsFormat().isEmpty()) {
537 valueLabel = valueString;
538 } else {
539 valueLabel = m_series->labelsFormat();
540 valueLabel.replace(before: valueTag, after: valueString);
541 }
542
543 return valueLabel;
544}
545
546void AbstractBarChartItem::updateBarItems()
547{
548 int min(0);
549 int max(0);
550 if (m_orientation == Qt::Vertical) {
551 min = qFloor(v: domain()->minX()) - 1;
552 max = qCeil(v: domain()->maxX()) + 1;
553 } else {
554 min = qFloor(v: domain()->minY()) - 1;
555 max = qCeil(v: domain()->maxY()) + 1;
556 }
557
558 int lastBarIndex = m_series->d_func()->categoryCount() - 1;
559
560 if (lastBarIndex < 0) {
561 // Indicate invalid categories by negatives
562 // Last should be one less than the first to make loops work right in case of no categories
563 m_firstCategory = -1;
564 m_lastCategory = -2;
565 m_categoryCount = 0;
566 } else {
567 m_firstCategory = qMax(a: qMin(a: min, b: lastBarIndex), b: 0);
568 m_lastCategory = qMax(a: qMin(a: max, b: lastBarIndex), b: m_firstCategory);
569 m_categoryCount = m_lastCategory - m_firstCategory + 1;
570 }
571
572 QList<QBarSet *> newSets = m_series->barSets();
573 QList<QBarSet *> oldSets = m_barMap.keys();
574
575 Q_ASSERT(newSets.size() == oldSets.size());
576
577 int layoutSize = m_categoryCount * newSets.size();
578
579 QList<QRectF> oldLayout = m_layout;
580 if (layoutSize != m_layout.size())
581 m_layout.resize(size: layoutSize);
582
583 // Create new graphic items for bars or remove excess ones
584 int layoutIndex = 0;
585 for (int s = 0; s < newSets.size(); s++) {
586 QBarSet *set = newSets.at(i: s);
587 QList<Bar *> bars = m_barMap.value(key: set);
588 int addCount = m_categoryCount - bars.size();
589 if (addCount > 0) {
590 for (int c = 0; c < addCount; c++) {
591 Bar *bar = new Bar(set, this);
592 bars.append(t: bar);
593 connect(sender: bar, signal: &Bar::clicked, context: m_series, slot: &QAbstractBarSeries::clicked);
594 connect(sender: bar, signal: &Bar::hovered, context: m_series, slot: &QAbstractBarSeries::hovered);
595 connect(sender: bar, signal: &Bar::pressed, context: m_series, slot: &QAbstractBarSeries::pressed);
596 connect(sender: bar, signal: &Bar::released, context: m_series, slot: &QAbstractBarSeries::released);
597 connect(sender: bar, signal: &Bar::doubleClicked, context: m_series, slot: &QAbstractBarSeries::doubleClicked);
598
599 connect(sender: bar, signal: &Bar::clicked, context: set, slot: &QBarSet::clicked);
600 connect(sender: bar, signal: &Bar::hovered, context: set, slot: &QBarSet::hovered);
601 connect(sender: bar, signal: &Bar::pressed, context: set, slot: &QBarSet::pressed);
602 connect(sender: bar, signal: &Bar::released, context: set, slot: &QBarSet::released);
603 connect(sender: bar, signal: &Bar::doubleClicked, context: set, slot: &QBarSet::doubleClicked);
604
605 m_labelItemsMissing = true;
606 }
607 }
608 // Update bar indexes
609 QHash<int, Bar*> indexMap;
610 QList<Bar *> unassignedBars(m_categoryCount, nullptr);
611 int unassignedIndex(0);
612 QList<Bar *> newBars;
613 newBars.reserve(size: m_categoryCount);
614 for (int c = 0; c < bars.size(); c++) {
615 Bar *bar = bars.at(i: c);
616 if (bar->index() < m_firstCategory || bar->index() > m_lastCategory) {
617 // Delete excess unassigned bars first
618 if (addCount < 0) {
619 addCount++;
620 delete bar;
621 bar = nullptr;
622 } else {
623 unassignedBars[unassignedIndex++] = bar;
624 bar->setLayoutIndex(layoutIndex);
625 newBars.append(t: bar);
626 layoutIndex++;
627 }
628 } else {
629 indexMap.insert(key: bar->index(), value: bar);
630 newBars.append(t: bar);
631 m_layout[layoutIndex] = oldLayout.at(i: bar->layoutIndex());
632 bar->setLayoutIndex(layoutIndex);
633 layoutIndex++;
634 }
635 }
636 unassignedIndex = 0;
637 for (int c = m_firstCategory; c <= m_lastCategory; c++) {
638 Bar *bar = indexMap.value(key: c);
639 if (!bar) {
640 bar = unassignedBars.at(i: unassignedIndex++);
641 bar->setIndex(c);
642 indexMap.insert(key: bar->index(), value: bar);
643 }
644 }
645
646 m_indexForBarMap.insert(key: set, value: indexMap);
647
648 if (m_animation) {
649 for (int i = 0; i < unassignedIndex; i++) {
650 Bar *bar = unassignedBars.at(i);
651 initializeLayout(set: s, category: bar->index(), layoutIndex: bar->layoutIndex(), resetAnimation: m_resetAnimation);
652 bar->setRect(m_layout.at(i: bar->layoutIndex()));
653 // Make bar initially hidden to avoid artifacts, layout setting will show it
654 bar->setVisible(false);
655 }
656 }
657
658 m_barMap.insert(key: set, value: newBars);
659 }
660}
661
662void AbstractBarChartItem::markLabelsDirty(QBarSet *barset, int index, int count)
663{
664 Q_ASSERT(barset);
665
666 if (index <= 0 && count < 0) {
667 barset->d_ptr.data()->setLabelsDirty(true);
668 } else {
669 const QList<Bar *> bars = m_barMap.value(key: barset);
670 const int maxIndex = count > 0 ? index + count : barset->count();
671 for (int i = 0; i < bars.size(); i++) {
672 Bar *bar = bars.at(i);
673 if (bar->index() >= index && bar->index() < maxIndex)
674 bar->setLabelDirty(true);
675 }
676 }
677}
678
679void AbstractBarChartItem::calculateSeriesPositionAdjustmentAndWidth()
680{
681 m_seriesPosAdjustment = 0.0;
682 m_seriesWidth = 1.0;
683
684 if (!m_series->chart())
685 return;
686
687 // Find out total number of bar series in chart and the index of this series among them
688 const QList<QAbstractSeries *> seriesList = m_series->chart()->series();
689 int index = -1;
690 int count = 0;
691 for (QAbstractSeries *series : seriesList) {
692 if (qobject_cast<QAbstractBarSeries *>(object: series)){
693 if (series == m_series)
694 index = count;
695 count++;
696 }
697 }
698 if (index > -1 && count > 1) {
699 m_seriesWidth = 1.0 / count;
700 m_seriesPosAdjustment = (m_seriesWidth * index) + (m_seriesWidth / 2.0) - 0.5;
701 }
702}
703
704ChartAnimation *AbstractBarChartItem::animation() const
705{
706 return m_animation;
707}
708
709QT_END_NAMESPACE
710
711#include "moc_abstractbarchartitem_p.cpp"
712

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