1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QtCharts/QBarModelMapper>
5#include <private/qbarmodelmapper_p.h>
6#include <QtCharts/QAbstractBarSeries>
7#include <QtCharts/QBarSet>
8#include <QtCharts/QChart>
9#include <QtCore/QAbstractItemModel>
10
11QT_BEGIN_NAMESPACE
12
13/*!
14 \class QBarModelMapper
15 \inmodule QtCharts
16 \brief The QBarModelMapper class is the base class for model mapper classes.
17 \internal
18
19 Model mappers enable using a data model derived from the QAbstractItemModel class
20 as a data source for a chart.
21*/
22
23QBarModelMapper::QBarModelMapper(QObject *parent) :
24 QObject(parent),
25 d_ptr(new QBarModelMapperPrivate(this))
26{
27}
28
29QAbstractItemModel *QBarModelMapper::model() const
30{
31 Q_D(const QBarModelMapper);
32 return d->m_model;
33}
34
35void QBarModelMapper::setModel(QAbstractItemModel *model)
36{
37 if (model == 0)
38 return;
39
40 Q_D(QBarModelMapper);
41 if (d->m_model)
42 disconnect(sender: d->m_model, signal: 0, receiver: d, member: 0);
43
44 d->m_model = model;
45 d->initializeBarFromModel();
46 // connect signals from the model
47 connect(sender: d->m_model, SIGNAL(modelReset()), receiver: d, SLOT(initializeBarFromModel()));
48 connect(sender: d->m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), receiver: d, SLOT(modelUpdated(QModelIndex,QModelIndex)));
49 connect(sender: d->m_model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), receiver: d, SLOT(modelHeaderDataUpdated(Qt::Orientation,int,int)));
50 connect(sender: d->m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), receiver: d, SLOT(modelRowsAdded(QModelIndex,int,int)));
51 connect(sender: d->m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), receiver: d, SLOT(modelRowsRemoved(QModelIndex,int,int)));
52 connect(sender: d->m_model, SIGNAL(columnsInserted(QModelIndex,int,int)), receiver: d, SLOT(modelColumnsAdded(QModelIndex,int,int)));
53 connect(sender: d->m_model, SIGNAL(columnsRemoved(QModelIndex,int,int)), receiver: d, SLOT(modelColumnsRemoved(QModelIndex,int,int)));
54 connect(sender: d->m_model, SIGNAL(destroyed()), receiver: d, SLOT(handleModelDestroyed()));
55}
56
57QAbstractBarSeries *QBarModelMapper::series() const
58{
59 Q_D(const QBarModelMapper);
60 return d->m_series;
61}
62
63void QBarModelMapper::setSeries(QAbstractBarSeries *series)
64{
65 Q_D(QBarModelMapper);
66 if (d->m_series)
67 disconnect(sender: d->m_series, signal: 0, receiver: d, member: 0);
68
69 if (series == 0)
70 return;
71
72 d->m_series = series;
73 d->initializeBarFromModel();
74 // connect the signals from the series
75 connect(sender: d->m_series, SIGNAL(barsetsAdded(QList<QBarSet*>)), receiver: d, SLOT(barSetsAdded(QList<QBarSet*>)));
76 connect(sender: d->m_series, SIGNAL(barsetsRemoved(QList<QBarSet*>)), receiver: d, SLOT(barSetsRemoved(QList<QBarSet*>)));
77 connect(sender: d->m_series, SIGNAL(destroyed()), receiver: d, SLOT(handleSeriesDestroyed()));
78}
79
80/*!
81 Returns which row/column of the model contains the first values of the QBarSets in the series.
82 The default value is 0.
83*/
84int QBarModelMapper::first() const
85{
86 Q_D(const QBarModelMapper);
87 return d->m_first;
88}
89
90/*!
91 Sets which row of the model contains the \a first values of the QBarSets in the series.
92 The default value is 0.
93*/
94void QBarModelMapper::setFirst(int first)
95{
96 Q_D(QBarModelMapper);
97 d->m_first = qMax(a: first, b: 0);
98 d->initializeBarFromModel();
99}
100
101/*!
102 Returns the number of rows/columns of the model that are mapped as the data for QAbstractBarSeries
103 Minimal and default value is: -1 (count limited by the number of rows/columns in the model)
104*/
105int QBarModelMapper::count() const
106{
107 Q_D(const QBarModelMapper);
108 return d->m_count;
109}
110
111/*!
112 Sets the \a count of rows/columns of the model that are mapped as the data for QAbstractBarSeries
113 Minimal and default value is: -1 (count limited by the number of rows/columns in the model)
114*/
115void QBarModelMapper::setCount(int count)
116{
117 Q_D(QBarModelMapper);
118 d->m_count = qMax(a: count, b: -1);
119 d->initializeBarFromModel();
120}
121
122/*!
123 Returns the orientation that is used when QBarModelMapper accesses the model.
124 This mean whether the consecutive values of the bar set are read from row (Qt::Horizontal)
125 or from columns (Qt::Vertical)
126*/
127Qt::Orientation QBarModelMapper::orientation() const
128{
129 Q_D(const QBarModelMapper);
130 return d->m_orientation;
131}
132
133/*!
134 Returns the \a orientation that is used when QBarModelMapper accesses the model.
135 This mean whether the consecutive values of the pie are read from row (Qt::Horizontal)
136 or from columns (Qt::Vertical)
137*/
138void QBarModelMapper::setOrientation(Qt::Orientation orientation)
139{
140 Q_D(QBarModelMapper);
141 d->m_orientation = orientation;
142 d->initializeBarFromModel();
143}
144
145/*!
146 Returns which section of the model is used as the data source for the first bar set
147*/
148int QBarModelMapper::firstBarSetSection() const
149{
150 Q_D(const QBarModelMapper);
151 return d->m_firstBarSetSection;
152}
153
154/*!
155 Sets the model section that is used as the data source for the first bar set
156 Parameter \a firstBarSetSection specifies the section of the model.
157*/
158void QBarModelMapper::setFirstBarSetSection(int firstBarSetSection)
159{
160 Q_D(QBarModelMapper);
161 d->m_firstBarSetSection = qMax(a: -1, b: firstBarSetSection);
162 d->initializeBarFromModel();
163}
164
165/*!
166 Returns which section of the model is used as the data source for the last bar set
167*/
168int QBarModelMapper::lastBarSetSection() const
169{
170 Q_D(const QBarModelMapper);
171 return d->m_lastBarSetSection;
172}
173
174/*!
175 Sets the model section that is used as the data source for the last bar set
176 Parameter \a lastBarSetSection specifies the section of the model.
177*/
178void QBarModelMapper::setLastBarSetSection(int lastBarSetSection)
179{
180 Q_D(QBarModelMapper);
181 d->m_lastBarSetSection = qMax(a: -1, b: lastBarSetSection);
182 d->initializeBarFromModel();
183}
184
185///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
186
187QBarModelMapperPrivate::QBarModelMapperPrivate(QBarModelMapper *q) :
188 QObject(q),
189 m_series(0),
190 m_model(0),
191 m_first(0),
192 m_count(-1),
193 m_orientation(Qt::Vertical),
194 m_firstBarSetSection(-1),
195 m_lastBarSetSection(-1),
196 m_seriesSignalsBlock(false),
197 m_modelSignalsBlock(false),
198 q_ptr(q)
199{
200}
201
202void QBarModelMapperPrivate::blockModelSignals(bool block)
203{
204 m_modelSignalsBlock = block;
205}
206
207void QBarModelMapperPrivate::blockSeriesSignals(bool block)
208{
209 m_seriesSignalsBlock = block;
210}
211
212QBarSet *QBarModelMapperPrivate::barSet(QModelIndex index)
213{
214 if (!index.isValid())
215 return 0;
216
217 if (m_orientation == Qt::Vertical && index.column() >= m_firstBarSetSection && index.column() <= m_lastBarSetSection) {
218 if (index.row() >= m_first && (m_count == - 1 || index.row() < m_first + m_count)) {
219 return m_series->barSets().at(i: index.column() - m_firstBarSetSection);
220 }
221 } else if (m_orientation == Qt::Horizontal && index.row() >= m_firstBarSetSection && index.row() <= m_lastBarSetSection) {
222 if (index.column() >= m_first && (m_count == - 1 || index.column() < m_first + m_count))
223 return m_series->barSets().at(i: index.row() - m_firstBarSetSection);
224 }
225 return 0; // This part of model has not been mapped to any slice
226}
227
228QModelIndex QBarModelMapperPrivate::barModelIndex(int barSection, int posInBar)
229{
230 if (m_count != -1 && posInBar >= m_count)
231 return QModelIndex(); // invalid
232
233 if (barSection < m_firstBarSetSection || barSection > m_lastBarSetSection)
234 return QModelIndex(); // invalid
235
236 if (m_orientation == Qt::Vertical)
237 return m_model->index(row: posInBar + m_first, column: barSection);
238 else
239 return m_model->index(row: barSection, column: posInBar + m_first);
240}
241
242void QBarModelMapperPrivate::handleSeriesDestroyed()
243{
244 m_series = 0;
245}
246
247void QBarModelMapperPrivate::modelUpdated(QModelIndex topLeft, QModelIndex bottomRight)
248{
249 Q_UNUSED(topLeft);
250 Q_UNUSED(bottomRight);
251
252 if (m_model == 0 || m_series == 0)
253 return;
254
255 if (m_modelSignalsBlock)
256 return;
257
258 blockSeriesSignals();
259 QModelIndex index;
260 for (int row = topLeft.row(); row <= bottomRight.row(); row++) {
261 for (int column = topLeft.column(); column <= bottomRight.column(); column++) {
262 index = topLeft.sibling(arow: row, acolumn: column);
263 QBarSet *bar = barSet(index);
264 if (bar) {
265 if (m_orientation == Qt::Vertical)
266 bar->replace(index: row - m_first, value: m_model->data(index).toReal());
267 else
268 bar->replace(index: column - m_first, value: m_model->data(index).toReal());
269 }
270 }
271 }
272 blockSeriesSignals(block: false);
273}
274
275void QBarModelMapperPrivate::modelHeaderDataUpdated(Qt::Orientation orientation, int first, int last)
276{
277 if (m_model == 0 || m_series == 0)
278 return;
279
280 if (m_modelSignalsBlock)
281 return;
282
283 blockSeriesSignals();
284 if (orientation != m_orientation) {
285 for (int section = first; section <= last; section++) {
286 if (section >= m_firstBarSetSection && section <= m_lastBarSetSection) {
287 QBarSet *bar = m_series->barSets().at(i: section - m_firstBarSetSection);
288 if (bar)
289 bar->setLabel(m_model->headerData(section, orientation).toString());
290 }
291 }
292 }
293 blockSeriesSignals(block: false);
294}
295
296void QBarModelMapperPrivate::modelRowsAdded(QModelIndex parent, int start, int end)
297{
298 Q_UNUSED(parent);
299 if (m_modelSignalsBlock)
300 return;
301
302 blockSeriesSignals();
303 if (m_orientation == Qt::Vertical)
304 insertData(start, end);
305 else if (start <= m_firstBarSetSection || start <= m_lastBarSetSection) // if the changes affect the map - reinitialize
306 initializeBarFromModel();
307 blockSeriesSignals(block: false);
308}
309
310void QBarModelMapperPrivate::modelRowsRemoved(QModelIndex parent, int start, int end)
311{
312 Q_UNUSED(parent);
313 if (m_modelSignalsBlock)
314 return;
315
316 blockSeriesSignals();
317 if (m_orientation == Qt::Vertical)
318 removeData(start, end);
319 else if (start <= m_firstBarSetSection || start <= m_lastBarSetSection) // if the changes affect the map - reinitialize
320 initializeBarFromModel();
321 blockSeriesSignals(block: false);
322}
323
324void QBarModelMapperPrivate::modelColumnsAdded(QModelIndex parent, int start, int end)
325{
326 Q_UNUSED(parent);
327 if (m_modelSignalsBlock)
328 return;
329
330 blockSeriesSignals();
331 if (m_orientation == Qt::Horizontal)
332 insertData(start, end);
333 else if (start <= m_firstBarSetSection || start <= m_lastBarSetSection) // if the changes affect the map - reinitialize
334 initializeBarFromModel();
335 blockSeriesSignals(block: false);
336}
337
338void QBarModelMapperPrivate::modelColumnsRemoved(QModelIndex parent, int start, int end)
339{
340 Q_UNUSED(parent);
341 if (m_modelSignalsBlock)
342 return;
343
344 blockSeriesSignals();
345 if (m_orientation == Qt::Horizontal)
346 removeData(start, end);
347 else if (start <= m_firstBarSetSection || start <= m_lastBarSetSection) // if the changes affect the map - reinitialize
348 initializeBarFromModel();
349 blockSeriesSignals(block: false);
350}
351
352void QBarModelMapperPrivate::handleModelDestroyed()
353{
354 m_model = 0;
355}
356
357void QBarModelMapperPrivate::insertData(int start, int end)
358{
359 Q_UNUSED(end);
360 Q_UNUSED(start);
361 Q_UNUSED(end);
362 // Currently barchart needs to be fully recalculated when change is made.
363 // Re-initialize
364 initializeBarFromModel();
365}
366
367void QBarModelMapperPrivate::removeData(int start, int end)
368{
369 Q_UNUSED(end);
370 Q_UNUSED(start);
371 Q_UNUSED(end);
372 // Currently barchart needs to be fully recalculated when change is made.
373 // Re-initialize
374 initializeBarFromModel();
375}
376
377void QBarModelMapperPrivate::barSetsAdded(const QList<QBarSet *> &sets)
378{
379 if (m_seriesSignalsBlock)
380 return;
381
382 if (sets.size() == 0)
383 return;
384
385 int firstIndex = m_series->barSets().indexOf(t: sets.at(i: 0));
386 if (firstIndex == -1)
387 return;
388
389 int maxCount = 0;
390 for (int i = 0; i < sets.size(); i++) {
391 if (sets.at(i)->count() > m_count)
392 maxCount = sets.at(i)->count();
393 }
394
395 if (m_count != -1 && m_count < maxCount)
396 m_count = maxCount;
397
398 m_lastBarSetSection += sets.size();
399
400 blockModelSignals();
401 int modelCapacity = m_orientation == Qt::Vertical ? m_model->rowCount() - m_first : m_model->columnCount() - m_first;
402 if (maxCount > modelCapacity) {
403 if (m_orientation == Qt::Vertical)
404 m_model->insertRows(row: m_model->rowCount(), count: maxCount - modelCapacity);
405 else
406 m_model->insertColumns(column: m_model->columnCount(), count: maxCount - modelCapacity);
407 }
408
409 if (m_orientation == Qt::Vertical)
410 m_model->insertColumns(column: firstIndex + m_firstBarSetSection, count: sets.size());
411 else
412 m_model->insertRows(row: firstIndex + m_firstBarSetSection, count: sets.size());
413
414
415 for (int i = firstIndex + m_firstBarSetSection; i < firstIndex + m_firstBarSetSection + sets.size(); i++) {
416 m_model->setHeaderData(section: i, orientation: m_orientation == Qt::Vertical ? Qt::Horizontal : Qt::Vertical, value: sets.at(i: i - firstIndex - m_firstBarSetSection)->label());
417 for (int j = 0; j < sets.at(i: i - firstIndex - m_firstBarSetSection)->count(); j++)
418 m_model->setData(index: barModelIndex(barSection: i, posInBar: j), value: sets.at(i: i - firstIndex - m_firstBarSetSection)->at(index: j));
419 }
420 blockModelSignals(block: false);
421 initializeBarFromModel();
422}
423
424void QBarModelMapperPrivate::barSetsRemoved(const QList<QBarSet *> &sets)
425{
426 if (m_seriesSignalsBlock)
427 return;
428
429 if (sets.size() == 0)
430 return;
431
432 int firstIndex = m_barSets.indexOf(t: sets.at(i: 0));
433 if (firstIndex == -1)
434 return;
435
436 m_lastBarSetSection -= sets.size();
437
438 for (int i = firstIndex + sets.size() - 1; i >= firstIndex; i--)
439 m_barSets.removeAt(i);
440
441 blockModelSignals();
442 if (m_orientation == Qt::Vertical)
443 m_model->removeColumns(column: firstIndex + m_firstBarSetSection, count: sets.size());
444 else
445 m_model->removeRows(row: firstIndex + m_firstBarSetSection, count: sets.size());
446 blockModelSignals(block: false);
447 initializeBarFromModel();
448}
449
450void QBarModelMapperPrivate::valuesAdded(int index, int count)
451{
452 if (m_seriesSignalsBlock)
453 return;
454
455 if (m_count != -1)
456 m_count += count;
457
458 int barSetIndex = m_barSets.indexOf(t: qobject_cast<QBarSet *>(object: QObject::sender()));
459
460 blockModelSignals();
461 if (m_orientation == Qt::Vertical)
462 m_model->insertRows(row: index + m_first, count);
463 else
464 m_model->insertColumns(column: index + m_first, count);
465
466 for (int j = index; j < index + count; j++)
467 m_model->setData(index: barModelIndex(barSection: barSetIndex + m_firstBarSetSection, posInBar: j), value: m_barSets.at(i: barSetIndex)->at(index: j));
468
469 blockModelSignals(block: false);
470 initializeBarFromModel();
471}
472
473void QBarModelMapperPrivate::valuesRemoved(int index, int count)
474{
475 if (m_seriesSignalsBlock)
476 return;
477
478 if (m_count != -1)
479 m_count -= count;
480
481 blockModelSignals();
482 if (m_orientation == Qt::Vertical)
483 m_model->removeRows(row: index + m_first, count);
484 else
485 m_model->removeColumns(column: index + m_first, count);
486
487 blockModelSignals(block: false);
488 initializeBarFromModel();
489}
490
491void QBarModelMapperPrivate::barLabelChanged()
492{
493 if (m_seriesSignalsBlock)
494 return;
495
496 int barSetIndex = m_barSets.indexOf(t: qobject_cast<QBarSet *>(object: QObject::sender()));
497
498 blockModelSignals();
499 m_model->setHeaderData(section: barSetIndex + m_firstBarSetSection, orientation: m_orientation == Qt::Vertical ? Qt::Horizontal : Qt::Vertical, value: m_barSets.at(i: barSetIndex)->label());
500 blockModelSignals(block: false);
501 initializeBarFromModel();
502}
503
504void QBarModelMapperPrivate::barValueChanged(int index)
505{
506 if (m_seriesSignalsBlock)
507 return;
508
509 int barSetIndex = m_barSets.indexOf(t: qobject_cast<QBarSet *>(object: QObject::sender()));
510
511 blockModelSignals();
512 m_model->setData(index: barModelIndex(barSection: barSetIndex + m_firstBarSetSection, posInBar: index), value: m_barSets.at(i: barSetIndex)->at(index));
513 blockModelSignals(block: false);
514 initializeBarFromModel();
515}
516
517QBarSet *qt_allocate_bar_set_cpp(const QString &label)
518{
519 return new QBarSet(label);
520}
521
522Q_CHARTS_EXPORT QBarSet *(*qt_allocate_bar_set)(const QString &label) = &qt_allocate_bar_set_cpp;
523
524void QBarModelMapperPrivate::initializeBarFromModel()
525{
526 if (m_model == 0 || m_series == 0)
527 return;
528
529 blockSeriesSignals();
530 // clear current content
531 m_series->clear();
532 m_barSets.clear();
533
534 // create the initial bar sets
535 for (int i = m_firstBarSetSection; i <= m_lastBarSetSection; i++) {
536 int posInBar = 0;
537 QModelIndex barIndex = barModelIndex(barSection: i, posInBar);
538 // check if there is such model index
539 if (barIndex.isValid()) {
540 QBarSet *barSet = qt_allocate_bar_set(m_model->headerData(section: i, orientation: m_orientation == Qt::Vertical ? Qt::Horizontal : Qt::Vertical).toString());
541 while (barIndex.isValid()) {
542 barSet->append(value: m_model->data(index: barIndex, role: Qt::DisplayRole).toDouble());
543 posInBar++;
544 barIndex = barModelIndex(barSection: i, posInBar);
545 }
546 connect(sender: barSet, SIGNAL(valuesAdded(int,int)), receiver: this, SLOT(valuesAdded(int,int)));
547 connect(sender: barSet, SIGNAL(valuesRemoved(int,int)), receiver: this, SLOT(valuesRemoved(int,int)));
548 connect(sender: barSet, SIGNAL(valueChanged(int)), receiver: this, SLOT(barValueChanged(int)));
549 connect(sender: barSet, SIGNAL(labelChanged()), receiver: this, SLOT(barLabelChanged()));
550 m_series->append(set: barSet);
551 m_barSets.append(t: barSet);
552 } else {
553 break;
554 }
555 }
556 blockSeriesSignals(block: false);
557}
558
559QT_END_NAMESPACE
560
561#include "moc_qbarmodelmapper.cpp"
562#include "moc_qbarmodelmapper_p.cpp"
563

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