Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #include <QtCharts/QCandlestickModelMapper>
++ #include <QtCharts/QCandlestickSeries>
++ #include <QtCharts/QCandlestickSet>
++ #include <QtCore/QAbstractItemModel>
++ #include <private/qcandlestickmodelmapper_p.h>
++
++ #include <algorithm>
++
++ QT_BEGIN_NAMESPACE
++
++ /*!
++ \class QCandlestickModelMapper
++ \since 5.8
++ \inmodule QtCharts
++ \brief Abstract model mapper class for candlestick series.
++
++ Model mappers allow the use of a QAbstractItemModel-derived model as a data source for a chart
++ series, creating a connection between a QCandlestickSeries and the model object. A model mapper
++ maintains an equal size across all \l {QCandlestickSet} {QCandlestickSets}.
++
++ \note The model used must support adding and removing rows/columns and modifying the data of the
++ cells.
++ */
++
++ /*!
++ \property QCandlestickModelMapper::model
++ \brief Defines the model that is used by the mapper.
++ */
++
++ /*!
++ \property QCandlestickModelMapper::series
++ \brief Defines the QCandlestickSeries object that is used by the mapper.
++
++ \note All data in the series is discarded when it is set to the mapper. When a new series is
++ specified, the old series is disconnected (preserving its data).
++ */
++
++ /*!
++ \fn Qt::Orientation QCandlestickModelMapper::orientation() const
++ Returns the orientation that is used when QCandlestickModelMapper accesses the model. This
++ determines whether the consecutive values of the set are read from rows (Qt::Horizontal) or from
++ columns (Qt::Vertical).
++ */
++
++ /*!
++ \fn void QCandlestickModelMapper::modelReplaced()
++ \brief Emitted when the model, to which the mapper is connected, has changed.
++ \sa model
++ */
++
++ /*!
++ \fn void QCandlestickModelMapper::seriesReplaced()
++ \brief Emitted when the series to which mapper is connected to has changed.
++ \sa series
++ */
++
++ /*!
++ Constructs a model mapper object as a child of \a parent.
++ */
++ QCandlestickModelMapper::QCandlestickModelMapper(QObject *parent)
++ : QObject(parent),
++ d_ptr(new QCandlestickModelMapperPrivate(this))
++ {
++ }
++
++ void QCandlestickModelMapper::setModel(QAbstractItemModel *model)
++ {
++ Q_D(QCandlestickModelMapper);
++
++ if (d->m_model == model)
++ return;
++
++ if (d->m_model)
++ disconnect(d->m_model, 0, d, 0);
++
++ d->m_model = model;
++ emit modelReplaced();
++
++ if (!d->m_model)
++ return;
++
++ d->initializeCandlestickFromModel();
++ // connect signals from the model
++ connect(d->m_model, SIGNAL(modelReset()), d, SLOT(initializeCandlestickFromModel()));
++ connect(d->m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
++ d, SLOT(modelDataUpdated(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(modelRowsInserted(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(modelColumnsInserted(QModelIndex,int,int)));
++ connect(d->m_model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
++ d, SLOT(modelColumnsRemoved(QModelIndex,int,int)));
++ connect(d->m_model, SIGNAL(destroyed()), d, SLOT(modelDestroyed()));
++ }
++
++ QAbstractItemModel *QCandlestickModelMapper::model() const
++ {
++ Q_D(const QCandlestickModelMapper);
++
++ return d->m_model;
++ }
++
++ void QCandlestickModelMapper::setSeries(QCandlestickSeries *series)
++ {
++ Q_D(QCandlestickModelMapper);
++
++ if (d->m_series == series)
++ return;
++
++ if (d->m_series)
++ disconnect(d->m_series, 0, d, 0);
++
++ d->m_series = series;
++ emit seriesReplaced();
++
++ if (!d->m_series)
++ return;
++
++ d->initializeCandlestickFromModel();
++ // connect the signals from the series
++ connect(d->m_series, SIGNAL(candlestickSetsAdded(QList<QCandlestickSet*>)),
++ d, SLOT(candlestickSetsAdded(QList<QCandlestickSet*>)));
++ connect(d->m_series, SIGNAL(candlestickSetsRemoved(QList<QCandlestickSet*>)),
++ d, SLOT(candlestickSetsRemoved(QList<QCandlestickSet*>)));
++ connect(d->m_series, SIGNAL(destroyed()), d, SLOT(seriesDestroyed()));
++ }
++
++ QCandlestickSeries *QCandlestickModelMapper::series() const
++ {
++ Q_D(const QCandlestickModelMapper);
++
++ return d->m_series;
++ }
++
++ /*!
++ Sets the row/column of the model that contains the \a timestamp values of the sets in the
++ series. Default value is -1 (invalid mapping).
++ */
++ void QCandlestickModelMapper::setTimestamp(int timestamp)
++ {
++ Q_D(QCandlestickModelMapper);
++
++ timestamp = qMax(timestamp, -1);
++
++ if (d->m_timestamp == timestamp)
++ return;
++
++ d->m_timestamp = timestamp;
++ emit d->timestampChanged();
++ d->initializeCandlestickFromModel();
++ }
++
++ /*!
++ Returns the row/column of the model that contains the timestamp values of the sets in the
++ series. Default value is -1 (invalid mapping).
++ */
++ int QCandlestickModelMapper::timestamp() const
++ {
++ Q_D(const QCandlestickModelMapper);
++
++ return d->m_timestamp;
++ }
++
++ /*!
++ Sets the row/column of the model that contains the \a open values of the sets in the series.
++ Default value is -1 (invalid mapping).
++ */
++ void QCandlestickModelMapper::setOpen(int open)
++ {
++ Q_D(QCandlestickModelMapper);
++
++ open = qMax(open, -1);
++
++ if (d->m_open == open)
++ return;
++
++ d->m_open = open;
++ emit d->openChanged();
++ d->initializeCandlestickFromModel();
++ }
++
++ /*!
++ Returns the row/column of the model that contains the open values of the sets in the series.
++ Default value is -1 (invalid mapping).
++ */
++ int QCandlestickModelMapper::open() const
++ {
++ Q_D(const QCandlestickModelMapper);
++
++ return d->m_open;
++ }
++
++ /*!
++ Sets the row/column of the model that contains the \a high values of the sets in the series.
++ Default value is -1 (invalid mapping).
++ */
++ void QCandlestickModelMapper::setHigh(int high)
++ {
++ Q_D(QCandlestickModelMapper);
++
++ high = qMax(high, -1);
++
++ if (d->m_high == high)
++ return;
++
++ d->m_high = high;
++ emit d->highChanged();
++ d->initializeCandlestickFromModel();
++ }
++
++ /*!
++ Returns the row/column of the model that contains the high values of the sets in the series.
++ Default value is -1 (invalid mapping).
++ */
++ int QCandlestickModelMapper::high() const
++ {
++ Q_D(const QCandlestickModelMapper);
++
++ return d->m_high;
++ }
++
++ /*!
++ Sets the row/column of the model that contains the \a low values of the sets in the series.
++ Default value is -1 (invalid mapping).
++ */
++ void QCandlestickModelMapper::setLow(int low)
++ {
++ Q_D(QCandlestickModelMapper);
++
++ low = qMax(low, -1);
++
++ if (d->m_low == low)
++ return;
++
++ d->m_low = low;
++ emit d->lowChanged();
++ d->initializeCandlestickFromModel();
++ }
++
++ /*!
++ Returns the row/column of the model that contains the low values of the sets in the series.
++ Default value is -1 (invalid mapping).
++ */
++ int QCandlestickModelMapper::low() const
++ {
++ Q_D(const QCandlestickModelMapper);
++
++ return d->m_low;
++ }
++
++ /*!
++ Sets the row/column of the model that contains the \a close values of the sets in the series.
++ Default value is -1 (invalid mapping).
++ */
++ void QCandlestickModelMapper::setClose(int close)
++ {
++ Q_D(QCandlestickModelMapper);
++
++ close = qMax(close, -1);
++
++ if (d->m_close == close)
++ return;
++
++ d->m_close = close;
++ emit d->closeChanged();
++ d->initializeCandlestickFromModel();
++ }
++
++ /*!
++ Returns the row/column of the model that contains the close values of the sets in the series.
++ Default value is -1 (invalid mapping).
++ */
++ int QCandlestickModelMapper::close() const
++ {
++ Q_D(const QCandlestickModelMapper);
++
++ return d->m_close;
++ }
++
++ /*!
++ Sets the section of the model that is used as the data source for the first candlestick set.
++ Parameter \a firstSetSection specifies the section of the model. Default value is -1.
++ */
++ void QCandlestickModelMapper::setFirstSetSection(int firstSetSection)
++ {
++ Q_D(QCandlestickModelMapper);
++
++ firstSetSection = qMax(firstSetSection, -1);
++
++ if (d->m_firstSetSection == firstSetSection)
++ return;
++
++ d->m_firstSetSection = firstSetSection;
++ emit d->firstSetSectionChanged();
++ d->initializeCandlestickFromModel();
++ }
++
++ /*!
++ Returns the section of the model that is used as the data source for the first candlestick set.
++ Default value is -1 (invalid mapping).
++ */
++ int QCandlestickModelMapper::firstSetSection() const
++ {
++ Q_D(const QCandlestickModelMapper);
++
++ return d->m_firstSetSection;
++ }
++
++ /*!
++ Sets the section of the model that is used as the data source for the last candlestick set.
++ Parameter \a lastSetSection specifies the section of the model. Default value is -1.
++ */
++ void QCandlestickModelMapper::setLastSetSection(int lastSetSection)
++ {
++ Q_D(QCandlestickModelMapper);
++
++ lastSetSection = qMax(lastSetSection, -1);
++
++ if (d->m_lastSetSection == lastSetSection)
++ return;
++
++ d->m_lastSetSection = lastSetSection;
++ emit d->lastSetSectionChanged();
++ d->initializeCandlestickFromModel();
++ }
++
++ /*!
++ Returns the section of the model that is used as the data source for the last candlestick set.
++ Default value is -1 (invalid mapping).
++ */
++ int QCandlestickModelMapper::lastSetSection() const
++ {
++ Q_D(const QCandlestickModelMapper);
++
++ return d->m_lastSetSection;
++ }
++
++ ////////////////////////////////////////////////////////////////////////////////////////////////////
++
++ QCandlestickModelMapperPrivate::QCandlestickModelMapperPrivate(QCandlestickModelMapper *q)
++ : QObject(q),
++ m_model(nullptr),
++ m_series(nullptr),
++ m_timestamp(-1),
++ m_open(-1),
++ m_high(-1),
++ m_low(-1),
++ m_close(-1),
++ m_firstSetSection(-1),
++ m_lastSetSection(-1),
++ m_modelSignalsBlock(false),
++ m_seriesSignalsBlock(false),
++ q_ptr(q)
++ {
++ }
++
++ void QCandlestickModelMapperPrivate::initializeCandlestickFromModel()
++ {
++ if (!m_model || !m_series)
++ return;
++
++ blockSeriesSignals();
++ // clear current content
++ m_series->clear();
++ m_sets.clear();
++
++ // create the initial candlestick sets
++ QList<QCandlestickSet *> sets;
++ for (int i = m_firstSetSection; i <= m_lastSetSection; ++i) {
++ QModelIndex timestampIndex = candlestickModelIndex(i, m_timestamp);
++ QModelIndex openIndex = candlestickModelIndex(i, m_open);
++ QModelIndex highIndex = candlestickModelIndex(i, m_high);
++ QModelIndex lowIndex = candlestickModelIndex(i, m_low);
++ QModelIndex closeIndex = candlestickModelIndex(i, m_close);
++ if (timestampIndex.isValid()
++ && openIndex.isValid()
++ && highIndex.isValid()
++ && lowIndex.isValid()
++ && closeIndex.isValid()) {
++ QCandlestickSet *set = new QCandlestickSet();
++ set->setTimestamp(m_model->data(timestampIndex, Qt::DisplayRole).toReal());
++ set->setOpen(m_model->data(openIndex, Qt::DisplayRole).toReal());
++ set->setHigh(m_model->data(highIndex, Qt::DisplayRole).toReal());
++ set->setLow(m_model->data(lowIndex, Qt::DisplayRole).toReal());
++ set->setClose(m_model->data(closeIndex, Qt::DisplayRole).toReal());
++
++ connect(set, SIGNAL(timestampChanged()), this, SLOT(candlestickSetChanged()));
++ connect(set, SIGNAL(openChanged()), this, SLOT(candlestickSetChanged()));
++ connect(set, SIGNAL(highChanged()), this, SLOT(candlestickSetChanged()));
++ connect(set, SIGNAL(lowChanged()), this, SLOT(candlestickSetChanged()));
++ connect(set, SIGNAL(closeChanged()), this, SLOT(candlestickSetChanged()));
++
++ sets.append(set);
++ } else {
++ break;
++ }
++ }
++ m_series->append(sets);
++ m_sets.append(sets);
++ blockSeriesSignals(false);
++ }
++
++ void QCandlestickModelMapperPrivate::modelDataUpdated(QModelIndex topLeft, QModelIndex bottomRight)
++ {
++ Q_Q(QCandlestickModelMapper);
++
++ if (!m_model || !m_series)
++ 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);
++ QCandlestickSet *set = candlestickSet(index);
++ if (set) {
++ int pos = (q->orientation() == Qt::Vertical) ? row : column;
++ if (pos == m_timestamp)
++ set->setTimestamp(m_model->data(index).toReal());
++ else if (pos == m_open)
++ set->setOpen(m_model->data(index).toReal());
++ else if (pos == m_high)
++ set->setHigh(m_model->data(index).toReal());
++ else if (pos == m_low)
++ set->setLow(m_model->data(index).toReal());
++ else if (pos == m_close)
++ set->setClose(m_model->data(index).toReal());
++ }
++ }
++ }
++ blockSeriesSignals(false);
++ }
++
++ void QCandlestickModelMapperPrivate::modelHeaderDataUpdated(Qt::Orientation orientation, int first,
++ int last)
++ {
++ Q_UNUSED(orientation);
++ Q_UNUSED(first);
++ Q_UNUSED(last);
++ }
++
++ void QCandlestickModelMapperPrivate::modelRowsInserted(QModelIndex parent, int start, int end)
++ {
++ Q_UNUSED(parent);
++
++ Q_Q(QCandlestickModelMapper);
++
++ if (m_modelSignalsBlock)
++ return;
++
++ blockSeriesSignals();
++ if (q->orientation() == Qt::Vertical)
++ insertData(start, end);
++ else if (start <= m_firstSetSection || start <= m_lastSetSection)
++ initializeCandlestickFromModel(); // if the changes affect the map - reinitialize
++ blockSeriesSignals(false);
++ }
++
++ void QCandlestickModelMapperPrivate::modelRowsRemoved(QModelIndex parent, int start, int end)
++ {
++ Q_UNUSED(parent);
++
++ Q_Q(QCandlestickModelMapper);
++
++ if (m_modelSignalsBlock)
++ return;
++
++ blockSeriesSignals();
++ if (q->orientation() == Qt::Vertical)
++ removeData(start, end);
++ else if (start <= m_firstSetSection || start <= m_lastSetSection)
++ initializeCandlestickFromModel(); // if the changes affect the map - reinitialize
++ blockSeriesSignals(false);
++ }
++
++ void QCandlestickModelMapperPrivate::modelColumnsInserted(QModelIndex parent, int start, int end)
++ {
++ Q_UNUSED(parent);
++
++ Q_Q(QCandlestickModelMapper);
++
++ if (m_modelSignalsBlock)
++ return;
++
++ blockSeriesSignals();
++ if (q->orientation() == Qt::Horizontal)
++ insertData(start, end);
++ else if (start <= m_firstSetSection || start <= m_lastSetSection)
++ initializeCandlestickFromModel(); // if the changes affect the map - reinitialize
++ blockSeriesSignals(false);
++ }
++
++ void QCandlestickModelMapperPrivate::modelColumnsRemoved(QModelIndex parent, int start, int end)
++ {
++ Q_UNUSED(parent);
++
++ Q_Q(QCandlestickModelMapper);
++
++ if (m_modelSignalsBlock)
++ return;
++
++ blockSeriesSignals();
++ if (q->orientation() == Qt::Horizontal)
++ removeData(start, end);
++ else if (start <= m_firstSetSection || start <= m_lastSetSection)
++ initializeCandlestickFromModel(); // if the changes affect the map - reinitialize
++ blockSeriesSignals(false);
++ }
++
++ void QCandlestickModelMapperPrivate::modelDestroyed()
++ {
++ m_model = 0;
++ }
++
++ void QCandlestickModelMapperPrivate::candlestickSetsAdded(const QList<QCandlestickSet *> &sets)
++ {
++ Q_Q(QCandlestickModelMapper);
++
++ if (m_seriesSignalsBlock)
++ return;
++
++ if (sets.isEmpty())
++ return;
++
++ int firstIndex = m_series->sets().indexOf(sets.at(0));
++ if (firstIndex == -1)
++ return;
++
++ m_lastSetSection += sets.size();
++
++ blockModelSignals();
++ if (q->orientation() == Qt::Vertical)
++ m_model->insertColumns(firstIndex + m_firstSetSection, sets.size());
++ else
++ m_model->insertRows(firstIndex + m_firstSetSection, sets.size());
++
++ for (int i = 0; i < sets.size(); ++i) {
++ int section = i + firstIndex + m_firstSetSection;
++ m_model->setData(candlestickModelIndex(section, m_timestamp), sets.at(i)->timestamp());
++ m_model->setData(candlestickModelIndex(section, m_open), sets.at(i)->open());
++ m_model->setData(candlestickModelIndex(section, m_high), sets.at(i)->high());
++ m_model->setData(candlestickModelIndex(section, m_low), sets.at(i)->low());
++ m_model->setData(candlestickModelIndex(section, m_close), sets.at(i)->close());
++ }
++ blockModelSignals(false);
++ initializeCandlestickFromModel();
++ }
++
++ void QCandlestickModelMapperPrivate::candlestickSetsRemoved(const QList<QCandlestickSet *> &sets)
++ {
++ Q_Q(QCandlestickModelMapper);
++
++ if (m_seriesSignalsBlock)
++ return;
++
++ if (sets.isEmpty())
++ return;
++
++ QList<int> removedIndices;
++ for (auto &set : sets) {
++ int index = m_sets.indexOf(set);
++ if (index != -1)
++ removedIndices << index;
++ }
++
++ if (removedIndices.isEmpty())
++ return;
++
++ std::sort(removedIndices.begin(), removedIndices.end());
++
++ for (int i = removedIndices.size() - 1; i >= 0; --i) {
++ m_sets.removeAt(removedIndices[i]);
++ --m_lastSetSection;
++ }
++
++ blockModelSignals();
++
++ // There is no guarantee removed sets are continuous, so remove them one by one
++ for (int i = removedIndices.size() - 1; i >= 0; --i) {
++ if (q->orientation() == Qt::Vertical)
++ m_model->removeColumns(removedIndices[i] + m_firstSetSection, 1);
++ else
++ m_model->removeRows(removedIndices[i] + m_firstSetSection, 1);
++ }
++
++ blockModelSignals(false);
++ initializeCandlestickFromModel();
++ }
++
++ void QCandlestickModelMapperPrivate::candlestickSetChanged()
++ {
++ if (m_seriesSignalsBlock)
++ return;
++
++ QCandlestickSet *set = qobject_cast<QCandlestickSet *>(QObject::sender());
++ if (!set)
++ return;
++
++ int section = m_series->sets().indexOf(set);
++ if (section < 0)
++ return;
++
++ section += m_firstSetSection;
++
++ blockModelSignals();
++ m_model->setData(candlestickModelIndex(section, m_timestamp), set->timestamp());
++ m_model->setData(candlestickModelIndex(section, m_open), set->open());
++ m_model->setData(candlestickModelIndex(section, m_high), set->high());
++ m_model->setData(candlestickModelIndex(section, m_low), set->low());
++ m_model->setData(candlestickModelIndex(section, m_close), set->close());
++ blockModelSignals(false);
++ }
++
++ void QCandlestickModelMapperPrivate::seriesDestroyed()
++ {
++ m_series = 0;
++ }
++
++ QCandlestickSet *QCandlestickModelMapperPrivate::candlestickSet(QModelIndex index)
++ {
++ Q_Q(QCandlestickModelMapper);
++
++ if (!index.isValid())
++ return 0;
++
++ int section = (q->orientation() == Qt::Vertical) ? index.column() : index.row();
++ int pos = (q->orientation() == Qt::Vertical) ? index.row() : index.column();
++
++ if (section < m_firstSetSection || section > m_lastSetSection)
++ return 0; // This part of model has not been mapped to any candlestick set.
++
++ if (pos != m_timestamp && pos != m_open && pos != m_high && pos != m_low && pos != m_close)
++ return 0; // This part of model has not been mapped to any candlestick set.
++
++ return m_series->sets().at(section - m_firstSetSection);
++ }
++
++ QModelIndex QCandlestickModelMapperPrivate::candlestickModelIndex(int section, int pos)
++ {
++ Q_Q(QCandlestickModelMapper);
++
++ if (section < m_firstSetSection || section > m_lastSetSection)
++ return QModelIndex(); // invalid
++
++ if (pos != m_timestamp && pos != m_open && pos != m_high && pos != m_low && pos != m_close)
++ return QModelIndex(); // invalid
++
++ if (q->orientation() == Qt::Vertical)
++ return m_model->index(pos, section);
++ else
++ return m_model->index(section, pos);
++ }
++
++ void QCandlestickModelMapperPrivate::insertData(int start, int end)
++ {
++ Q_UNUSED(start);
++ Q_UNUSED(end);
++
++ // Currently candlestickchart needs to be fully recalculated when change is made.
++ initializeCandlestickFromModel();
++ }
++
++ void QCandlestickModelMapperPrivate::removeData(int start, int end)
++ {
++ Q_UNUSED(start);
++ Q_UNUSED(end);
++
++ // Currently candlestickchart needs to be fully recalculated when change is made.
++ initializeCandlestickFromModel();
++ }
++
++ void QCandlestickModelMapperPrivate::blockModelSignals(bool block)
++ {
++ m_modelSignalsBlock = block;
++ }
++
++ void QCandlestickModelMapperPrivate::blockSeriesSignals(bool block)
++ {
++ m_seriesSignalsBlock = block;
++ }
++
++ QT_END_NAMESPACE
++
++ #include "moc_qcandlestickmodelmapper.cpp"
++ #include "moc_qcandlestickmodelmapper_p.cpp"