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

source code of qtcharts/src/charts/piechart/qpiemodelmapper.cpp