/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc ** All rights reserved. ** For any questions to Digia, please use contact form at http://qt.digia.com ** ** This file is part of the Qt Commercial Charts Add-on. ** ** $QT_BEGIN_LICENSE$ ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. ** ** If you have questions regarding the use of this file, please use ** contact form at http://qt.digia.com ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qbarmodelmapper.h" #include "qbarmodelmapper_p.h" #include "qbarseries.h" #include "qbarset.h" #include "qchart.h" #include QTCOMMERCIALCHART_BEGIN_NAMESPACE /*! \class QBarModelMapper \brief part of QtCommercial chart API. \mainclass Model mappers allow you to use QAbstractItemModel derived models as a data source for a chart series. The instance of this class cannot be created directly. QHBarModelMapper of QVBarModelMapper should be used instead. This class is used to create a connection between QBarSeries and QAbstractItemModel derived model object. Model mapper maintains equal size of all the BarSets. Adding/removing value from the BarSet causes the the same change in the rest of the BarSets added to the same series. NOTE: used model has to support adding/removing rows/columns and modifying the data of the cells. */ /*! \property QBarModelMapper::series \brief Defines the QPieSeries object that is used by the mapper. All the data in the series is discarded when it is set to the mapper. When new series is specified the old series is disconnected (it preserves its data) */ /*! \property QBarModelMapper::model \brief Defines the model that is used by the mapper. */ /*! \property QBarModelMapper::first \brief Defines which item of the model's row/column should be mapped as the value of the first QBarSet in the series. Minimal and default value is: 0 */ /*! \property QBarModelMapper::count \brief Defines the number of rows/columns of the model that are mapped as the data for QBarSeries Minimal and default value is: -1 (count limited by the number of rows/columns in the model) */ /*! \fn void QBarModelMapper::seriesReplaced() Emitted when the series to which mapper is connected to has changed. */ /*! \fn void QBarModelMapper::modelReplaced() Emitted when the model to which mapper is connected to has changed. */ /*! \fn void QBarModelMapper::firstChanged() Emitted when the value for the first has changed. */ /*! \fn void QBarModelMapper::countChanged() Emitted when the value for the count has changed. */ /*! Constructs a mapper object which is a child of \a parent. */ QBarModelMapper::QBarModelMapper(QObject *parent) : QObject(parent), d_ptr(new QBarModelMapperPrivate(this)) { } QAbstractItemModel* QBarModelMapper::model() const { Q_D(const QBarModelMapper); return d->m_model; } void QBarModelMapper::setModel(QAbstractItemModel *model) { if (model == 0) return; Q_D(QBarModelMapper); if (d->m_model) { disconnect(d->m_model, 0, d, 0); } d->m_model = model; d->initializeBarFromModel(); // connect signals from the model connect(d->m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), d, SLOT(modelUpdated(QModelIndex,QModelIndex))); connect(d->m_model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), d, SLOT(modelHeaderDataUpdated(Qt::Orientation,int,int))); connect(d->m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), d, SLOT(modelRowsAdded(QModelIndex,int,int))); connect(d->m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), d, SLOT(modelRowsRemoved(QModelIndex,int,int))); connect(d->m_model, SIGNAL(columnsInserted(QModelIndex,int,int)), d, SLOT(modelColumnsAdded(QModelIndex,int,int))); connect(d->m_model, SIGNAL(columnsRemoved(QModelIndex,int,int)), d, SLOT(modelColumnsRemoved(QModelIndex,int,int))); emit modelReplaced(); } QBarSeries* QBarModelMapper::series() const { Q_D(const QBarModelMapper); return d->m_series; } void QBarModelMapper::setSeries(QBarSeries *series) { Q_D(QBarModelMapper); if (d->m_series) { disconnect(d->m_series, 0, d, 0); } if (series == 0) return; d->m_series = series; d->initializeBarFromModel(); // connect the signals from the series connect(d->m_series, SIGNAL(barsetsAdded(QList)), d, SLOT(barSetsAdded(QList))); connect(d->m_series, SIGNAL(barsetsRemoved(QList)), d, SLOT(barSetsRemoved(QList))); emit seriesReplaced(); } int QBarModelMapper::first() const { Q_D(const QBarModelMapper); return d->m_first; } void QBarModelMapper::setFirst(int first) { Q_D(QBarModelMapper); if (first != d->m_first) { d->m_first = qMax(first, 0); d->initializeBarFromModel(); emit firstChanged(); } } int QBarModelMapper::count() const { Q_D(const QBarModelMapper); return d->m_count; } void QBarModelMapper::setCount(int count) { Q_D(QBarModelMapper); if (count != d->m_count) { d->m_count = qMax(count, -1); d->initializeBarFromModel(); emit countChanged(); } } /*! Returns the orientation that is used when QBarModelMapper accesses the model. This mean whether the consecutive values of the bar set are read from row (Qt::Horizontal) or from columns (Qt::Vertical) */ Qt::Orientation QBarModelMapper::orientation() const { Q_D(const QBarModelMapper); return d->m_orientation; } /*! Returns the \a orientation that is used when QBarModelMapper accesses the model. This mean whether the consecutive values of the pie are read from row (Qt::Horizontal) or from columns (Qt::Vertical) */ void QBarModelMapper::setOrientation(Qt::Orientation orientation) { Q_D(QBarModelMapper); d->m_orientation = orientation; d->initializeBarFromModel(); } /*! Returns which section of the model is used as the data source for the first bar set */ int QBarModelMapper::firstBarSetSection() const { Q_D(const QBarModelMapper); return d->m_firstBarSetSection; } /*! Sets the model section that is used as the data source for the first bar set Parameter \a firstBarSetSection specifies the section of the model. */ void QBarModelMapper::setFirstBarSetSection(int firstBarSetSection) { Q_D(QBarModelMapper); d->m_firstBarSetSection = qMax(-1, firstBarSetSection); d->initializeBarFromModel(); } /*! Returns which section of the model is used as the data source for the last bar set */ int QBarModelMapper::lastBarSetSection() const { Q_D(const QBarModelMapper); return d->m_lastBarSetSection; } /*! Sets the model section that is used as the data source for the last bar set Parameter \a lastBarSetSection specifies the section of the model. */ void QBarModelMapper::setLastBarSetSection(int lastBarSetSection) { Q_D(QBarModelMapper); d->m_lastBarSetSection = qMax(-1, lastBarSetSection); d->initializeBarFromModel(); } /*! Resets the QBarModelMapper to the default state. first: 0; count: -1; firstBarSetSection: -1; lastBarSetSection: -1; categoriesSection: -1 */ void QBarModelMapper::reset() { Q_D(QBarModelMapper); d->m_first = 0; d->m_count = -1; d->m_firstBarSetSection = -1; d->m_lastBarSetSection = -1; d->initializeBarFromModel(); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// QBarModelMapperPrivate::QBarModelMapperPrivate(QBarModelMapper *q) : m_series(0), m_model(0), m_first(0), m_count(-1), m_orientation(Qt::Vertical), m_firstBarSetSection(-1), m_lastBarSetSection(-1), m_seriesSignalsBlock(false), m_modelSignalsBlock(false), q_ptr(q) { } void QBarModelMapperPrivate::blockModelSignals(bool block) { m_modelSignalsBlock = block; } void QBarModelMapperPrivate::blockSeriesSignals(bool block) { m_seriesSignalsBlock = block; } QBarSet* QBarModelMapperPrivate::barSet(QModelIndex index) { if (!index.isValid()) return 0; if (m_orientation == Qt::Vertical && index.column() >= m_firstBarSetSection && index.column() <= m_lastBarSetSection) { if (index.row() >= m_first && (m_count == - 1 || index.row() < m_first + m_count)) { // if (m_model->index(index.row(), m_valuesSection).isValid() && m_model->index(index.row(), m_labelsSection).isValid()) return m_series->barSets().at(index.column() - m_firstBarSetSection); // else // return 0; } } else if (m_orientation == Qt::Horizontal && index.row() >= m_firstBarSetSection && index.row() <= m_lastBarSetSection) { if (index.column() >= m_first && (m_count == - 1 || index.column() < m_first + m_count)) return m_series->barSets().at(index.row() - m_firstBarSetSection); } return 0; // This part of model has not been mapped to any slice } QModelIndex QBarModelMapperPrivate::barModelIndex(int barSection, int posInBar) { if (m_count != -1 && posInBar >= m_count) return QModelIndex(); // invalid if (barSection < m_firstBarSetSection || barSection > m_lastBarSetSection) return QModelIndex(); // invalid if (m_orientation == Qt::Vertical) return m_model->index(posInBar + m_first, barSection); else return m_model->index(barSection, posInBar + m_first); } void QBarModelMapperPrivate::modelUpdated(QModelIndex topLeft, QModelIndex bottomRight) { Q_UNUSED(topLeft) Q_UNUSED(bottomRight) if (m_model == 0 || m_series == 0) return; if (m_modelSignalsBlock) return; blockSeriesSignals(); QModelIndex index; for (int row = topLeft.row(); row <= bottomRight.row(); row++) { for (int column = topLeft.column(); column <= bottomRight.column(); column++) { index = topLeft.sibling(row, column); QBarSet* bar = barSet(index); if (bar) { if (m_orientation == Qt::Vertical) bar->replace(row - m_first, m_model->data(index).toReal()); else bar->replace(column - m_first, m_model->data(index).toReal()); } } } blockSeriesSignals(false); } void QBarModelMapperPrivate::modelHeaderDataUpdated(Qt::Orientation orientation, int first, int last) { if (m_model == 0 || m_series == 0) return; if (m_modelSignalsBlock) return; blockSeriesSignals(); if (orientation != m_orientation) { for (int section = first; section <= last; section++) { if (section >= m_firstBarSetSection && section <= m_lastBarSetSection) { QBarSet* bar = m_series->barSets().at(section - m_firstBarSetSection); if (bar) bar->setLabel(m_model->headerData(section, orientation).toString()); } } } blockSeriesSignals(false); } void QBarModelMapperPrivate::modelRowsAdded(QModelIndex parent, int start, int end) { Q_UNUSED(parent); Q_UNUSED(end) if (m_modelSignalsBlock) return; blockSeriesSignals(); if (m_orientation == Qt::Vertical) // insertData(start, end); initializeBarFromModel(); else if (start <= m_firstBarSetSection || start <= m_lastBarSetSection) // if the changes affect the map - reinitialize initializeBarFromModel(); blockSeriesSignals(false); } void QBarModelMapperPrivate::modelRowsRemoved(QModelIndex parent, int start, int end) { Q_UNUSED(parent); Q_UNUSED(end) if (m_modelSignalsBlock) return; blockSeriesSignals(); if (m_orientation == Qt::Vertical) // removeData(start, end); initializeBarFromModel(); else if (start <= m_firstBarSetSection || start <= m_lastBarSetSection) // if the changes affect the map - reinitialize initializeBarFromModel(); blockSeriesSignals(false); } void QBarModelMapperPrivate::modelColumnsAdded(QModelIndex parent, int start, int end) { Q_UNUSED(parent); Q_UNUSED(end) if (m_modelSignalsBlock) return; blockSeriesSignals(); if (m_orientation == Qt::Horizontal) // insertData(start, end); initializeBarFromModel(); else if (start <= m_firstBarSetSection || start <= m_lastBarSetSection) // if the changes affect the map - reinitialize initializeBarFromModel(); blockSeriesSignals(false); } void QBarModelMapperPrivate::modelColumnsRemoved(QModelIndex parent, int start, int end) { Q_UNUSED(parent); Q_UNUSED(end) if (m_modelSignalsBlock) return; blockSeriesSignals(); if (m_orientation == Qt::Horizontal) // removeData(start, end); initializeBarFromModel(); else if (start <= m_firstBarSetSection || start <= m_lastBarSetSection) // if the changes affect the map - reinitialize initializeBarFromModel(); blockSeriesSignals(false); } void QBarModelMapperPrivate::insertData(int start, int end) { Q_UNUSED(end) Q_UNUSED(start) Q_UNUSED(end) // To be implemented } void QBarModelMapperPrivate::removeData(int start, int end) { Q_UNUSED(end) Q_UNUSED(start) Q_UNUSED(end) // To be implemented } void QBarModelMapperPrivate::barSetsAdded(QList sets) { if (m_seriesSignalsBlock) return; if (sets.count() == 0) return; int firstIndex = m_series->barSets().indexOf(sets.at(0)); if (firstIndex == -1) return; int maxCount = 0; for(int i = 0; i < sets.count(); i++) if (sets.at(i)->count() > m_count) maxCount = sets.at(i)->count(); if (m_count != -1 && m_count < maxCount) m_count = maxCount; m_lastBarSetSection += sets.count(); blockModelSignals(); int modelCapacity = m_orientation == Qt::Vertical ? m_model->rowCount() - m_first : m_model->columnCount() - m_first; if (maxCount > modelCapacity) { if (m_orientation == Qt::Vertical) m_model->insertRows(m_model->rowCount(), maxCount - modelCapacity); else m_model->insertColumns(m_model->columnCount(), maxCount - modelCapacity); } if (m_orientation == Qt::Vertical) m_model->insertColumns(firstIndex + m_firstBarSetSection, sets.count()); else m_model->insertRows(firstIndex + m_firstBarSetSection, sets.count()); for(int i = firstIndex + m_firstBarSetSection; i < firstIndex + m_firstBarSetSection + sets.count(); i++) { m_model->setHeaderData(i, m_orientation == Qt::Vertical ? Qt::Horizontal : Qt::Vertical, sets.at(i - firstIndex - m_firstBarSetSection)->label()); for (int j = 0; j < sets.at(i - firstIndex - m_firstBarSetSection)->count(); j++) m_model->setData(barModelIndex(i, j), sets.at(i - firstIndex - m_firstBarSetSection)->at(j).y()); } blockModelSignals(false); initializeBarFromModel(); } void QBarModelMapperPrivate::barSetsRemoved(QList sets) { if (m_seriesSignalsBlock) return; if (sets.count() == 0) return; int firstIndex = m_barSets.indexOf(sets.at(0)); if (firstIndex == -1) return; m_lastBarSetSection -= sets.count(); for (int i = firstIndex + sets.count() - 1; i >= firstIndex; i--) m_barSets.removeAt(i); blockModelSignals(); if (m_orientation == Qt::Vertical) m_model->removeColumns(firstIndex + m_firstBarSetSection, sets.count()); else m_model->removeRows(firstIndex + m_firstBarSetSection, sets.count()); blockModelSignals(false); initializeBarFromModel(); } void QBarModelMapperPrivate::valuesAdded(int index, int count) { if (m_seriesSignalsBlock) return; if (m_count != -1) m_count += count; int barSetIndex = m_barSets.indexOf(qobject_cast(QObject::sender())); blockModelSignals(); if (m_orientation == Qt::Vertical) m_model->insertRows(index + m_first, count); else m_model->insertColumns(index + m_first, count); for (int j = index; j < index + count; j++) m_model->setData(barModelIndex(barSetIndex + m_firstBarSetSection, j), m_barSets.at(barSetIndex)->at(j).y()); blockModelSignals(false); initializeBarFromModel(); } void QBarModelMapperPrivate::valuesRemoved(int index, int count) { if (m_seriesSignalsBlock) return; if (m_count != -1) m_count -= count; blockModelSignals(); if (m_orientation == Qt::Vertical) m_model->removeRows(index + m_first, count); else m_model->removeColumns(index + m_first, count); blockModelSignals(false); initializeBarFromModel(); } void QBarModelMapperPrivate::barLabelChanged() { if (m_seriesSignalsBlock) return; int barSetIndex = m_barSets.indexOf(qobject_cast(QObject::sender())); blockModelSignals(); m_model->setHeaderData(barSetIndex + m_firstBarSetSection, m_orientation == Qt::Vertical ? Qt::Horizontal : Qt::Vertical, m_barSets.at(barSetIndex)->label()); blockModelSignals(false); initializeBarFromModel(); } void QBarModelMapperPrivate::barValueChanged(int index) { if (m_seriesSignalsBlock) return; int barSetIndex = m_barSets.indexOf(qobject_cast(QObject::sender())); blockModelSignals(); m_model->setData(barModelIndex(barSetIndex + m_firstBarSetSection, index), m_barSets.at(barSetIndex)->at(index).y()); blockModelSignals(false); initializeBarFromModel(); } void QBarModelMapperPrivate::initializeBarFromModel() { if (m_model == 0 || m_series == 0) return; blockSeriesSignals(); // clear current content m_series->clear(); m_barSets.clear(); // create the initial bar sets for (int i = m_firstBarSetSection; i <= m_lastBarSetSection; i++) { int posInBar = 0; QModelIndex barIndex = barModelIndex(i, posInBar); // check if there is such model index if (barIndex.isValid()) { QBarSet *barSet = new QBarSet(m_model->headerData(i, m_orientation == Qt::Vertical ? Qt::Horizontal : Qt::Vertical).toString()); while (barIndex.isValid()) { barSet->append(m_model->data(barIndex, Qt::DisplayRole).toDouble()); posInBar++; barIndex = barModelIndex(i, posInBar); } connect(barSet, SIGNAL(valuesAdded(int, int)), this, SLOT(valuesAdded(int, int))); connect(barSet, SIGNAL(valuesRemoved(int, int)), this, SLOT(valuesRemoved(int, int))); connect(barSet, SIGNAL(valueChanged(int)), this, SLOT(barValueChanged(int))); connect(barSet, SIGNAL(labelChanged()), this, SLOT(barLabelChanged())); m_series->append(barSet); m_barSets.append(barSet); } else { break; } } blockSeriesSignals(false); } #include "moc_qbarmodelmapper.cpp" #include "moc_qbarmodelmapper_p.cpp" QTCOMMERCIALCHART_END_NAMESPACE