File indexing completed on 2024-05-05 16:16:41

0001 /*
0002  * This file is part of KQuickCharts
0003  * SPDX-FileCopyrightText: 2019 Arjen Hiemstra <ahiemstra@heimr.nl>
0004  *
0005  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006  */
0007 
0008 #include "ModelSource.h"
0009 
0010 #include <QMetaProperty>
0011 
0012 #include "charts_datasource_logging.h"
0013 
0014 ModelSource::ModelSource(QObject *parent)
0015     : ChartDataSource(parent)
0016 {
0017     connect(this, &ModelSource::modelChanged, this, &ModelSource::dataChanged);
0018     connect(this, &ModelSource::columnChanged, this, &ModelSource::dataChanged);
0019     connect(this, &ModelSource::roleChanged, this, &ModelSource::dataChanged);
0020     connect(this, &ModelSource::indexColumnsChanged, this, &ModelSource::dataChanged);
0021 }
0022 
0023 int ModelSource::role() const
0024 {
0025     if (!m_model) {
0026         return -1;
0027     }
0028 
0029     if (m_role < 0 && !m_roleName.isEmpty()) {
0030         m_role = m_model->roleNames().key(m_roleName.toLatin1(), -1);
0031     }
0032 
0033     return m_role;
0034 }
0035 
0036 QString ModelSource::roleName() const
0037 {
0038     return m_roleName;
0039 }
0040 
0041 int ModelSource::column() const
0042 {
0043     return m_column;
0044 }
0045 
0046 QAbstractItemModel *ModelSource::model() const
0047 {
0048     return m_model;
0049 }
0050 
0051 bool ModelSource::indexColumns() const
0052 {
0053     return m_indexColumns;
0054 }
0055 
0056 int ModelSource::itemCount() const
0057 {
0058     if (!m_model) {
0059         return 0;
0060     }
0061 
0062     return m_indexColumns ? m_model->columnCount() : m_model->rowCount();
0063 }
0064 
0065 QVariant ModelSource::item(int index) const
0066 {
0067     if (!m_model) {
0068         return {};
0069     }
0070 
0071     // For certain model (QML ListModel for example), the roleNames() are more
0072     // dynamic and may only be valid when this method gets called. So try and
0073     // lookup the role first before anything else.
0074     if (m_role < 0) {
0075         if (m_roleName.isEmpty()) {
0076             return QVariant{};
0077         }
0078 
0079         m_role = m_model->roleNames().key(m_roleName.toLatin1(), -1);
0080         if (m_role < 0) {
0081             qCWarning(DATASOURCE) << "ModelSource: Invalid role " << m_role << m_roleName;
0082             return QVariant{};
0083         }
0084     }
0085 
0086     if (!m_indexColumns && (m_column < 0 || m_column > m_model->columnCount())) {
0087         qCDebug(DATASOURCE) << "ModelSource: Invalid column" << m_column;
0088         return QVariant{};
0089     }
0090 
0091     auto modelIndex = m_indexColumns ? m_model->index(0, index) : m_model->index(index, m_column);
0092     if (modelIndex.isValid()) {
0093         return m_model->data(modelIndex, m_role);
0094     }
0095 
0096     return QVariant{};
0097 }
0098 
0099 QVariant ModelSource::minimum() const
0100 {
0101     if (!m_model || itemCount() <= 0) {
0102         return {};
0103     }
0104 
0105     if (m_minimum.isValid()) {
0106         return m_minimum;
0107     }
0108 
0109     auto minProperty = m_model->property("minimum");
0110     auto maxProperty = m_model->property("maximum");
0111     if (minProperty.isValid() && minProperty != maxProperty) {
0112         return minProperty;
0113     }
0114 
0115     QVariant result = std::numeric_limits<float>::max();
0116     for (int i = 0; i < itemCount(); ++i) {
0117         result = std::min(result, item(i), variantCompare);
0118     }
0119     return result;
0120 }
0121 
0122 QVariant ModelSource::maximum() const
0123 {
0124     if (!m_model || itemCount() <= 0) {
0125         return {};
0126     }
0127 
0128     if (m_maximum.isValid()) {
0129         return m_maximum;
0130     }
0131 
0132     auto minProperty = m_model->property("minimum");
0133     auto maxProperty = m_model->property("maximum");
0134     if (maxProperty.isValid() && maxProperty != minProperty) {
0135         return maxProperty;
0136     }
0137 
0138     QVariant result = std::numeric_limits<float>::min();
0139     for (int i = 0; i < itemCount(); ++i) {
0140         result = std::max(result, item(i), variantCompare);
0141     }
0142     return result;
0143 }
0144 
0145 void ModelSource::setRole(int role)
0146 {
0147     if (role == m_role) {
0148         return;
0149     }
0150 
0151     m_role = role;
0152     if (m_model) {
0153         m_roleName = QString::fromLatin1(m_model->roleNames().value(role));
0154         Q_EMIT roleNameChanged();
0155     }
0156     Q_EMIT roleChanged();
0157 }
0158 
0159 void ModelSource::setRoleName(const QString &name)
0160 {
0161     if (name == m_roleName) {
0162         return;
0163     }
0164 
0165     m_roleName = name;
0166     if (m_model) {
0167         m_role = m_model->roleNames().key(m_roleName.toLatin1(), -1);
0168         Q_EMIT roleChanged();
0169     }
0170     Q_EMIT roleNameChanged();
0171 }
0172 
0173 void ModelSource::setColumn(int column)
0174 {
0175     if (column == m_column) {
0176         return;
0177     }
0178 
0179     m_column = column;
0180     Q_EMIT columnChanged();
0181 }
0182 
0183 void ModelSource::setIndexColumns(bool index)
0184 {
0185     if (index == m_indexColumns) {
0186         return;
0187     }
0188 
0189     m_indexColumns = index;
0190     Q_EMIT indexColumnsChanged();
0191 }
0192 
0193 void ModelSource::setModel(QAbstractItemModel *model)
0194 {
0195     if (m_model == model) {
0196         return;
0197     }
0198 
0199     if (m_model) {
0200         m_model->disconnect(this);
0201         m_minimum = QVariant{};
0202         m_maximum = QVariant{};
0203     }
0204 
0205     m_model = model;
0206     if (m_model) {
0207         connect(m_model, &QAbstractItemModel::rowsInserted, this, &ModelSource::dataChanged);
0208         connect(m_model, &QAbstractItemModel::rowsRemoved, this, &ModelSource::dataChanged);
0209         connect(m_model, &QAbstractItemModel::rowsMoved, this, &ModelSource::dataChanged);
0210         connect(m_model, &QAbstractItemModel::modelReset, this, &ModelSource::dataChanged);
0211         connect(m_model, &QAbstractItemModel::dataChanged, this, &ModelSource::dataChanged);
0212         connect(m_model, &QAbstractItemModel::layoutChanged, this, &ModelSource::dataChanged);
0213 
0214         connect(m_model, &QAbstractItemModel::destroyed, this, [this]() {
0215             m_minimum = QVariant{};
0216             m_maximum = QVariant{};
0217             m_model = nullptr;
0218         });
0219 
0220         auto minimumIndex = m_model->metaObject()->indexOfProperty("minimum");
0221         if (minimumIndex != -1) {
0222             auto minimum = m_model->metaObject()->property(minimumIndex);
0223             if (minimum.hasNotifySignal()) {
0224                 auto slot = metaObject()->method(metaObject()->indexOfSlot("onMinimumChanged()"));
0225                 connect(m_model, minimum.notifySignal(), this, slot);
0226                 m_minimum = minimum.read(m_model);
0227             }
0228         }
0229 
0230         auto maximumIndex = m_model->metaObject()->indexOfProperty("maximum");
0231         if (maximumIndex != -1) {
0232             auto maximum = m_model->metaObject()->property(maximumIndex);
0233             if (maximum.hasNotifySignal()) {
0234                 auto slot = metaObject()->method(metaObject()->indexOfSlot("onMaximumChanged()"));
0235                 connect(m_model, maximum.notifySignal(), this, slot);
0236                 m_maximum = maximum.read(m_model);
0237             }
0238         }
0239     }
0240 
0241     Q_EMIT modelChanged();
0242 }
0243 
0244 void ModelSource::onMinimumChanged()
0245 {
0246     auto newMinimum = m_model->property("minimum");
0247     if (newMinimum.isValid() && newMinimum != m_minimum) {
0248         m_minimum = newMinimum;
0249     }
0250 }
0251 
0252 void ModelSource::onMaximumChanged()
0253 {
0254     auto newMaximum = m_model->property("maximum");
0255     if (newMaximum.isValid() && newMaximum != m_maximum) {
0256         m_maximum = newMaximum;
0257     }
0258 }
0259 
0260 #include "moc_ModelSource.cpp"