File indexing completed on 2024-04-21 03:56:42

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 "LegendModel.h"
0009 
0010 #include "Chart.h"
0011 #include "datasource/ChartDataSource.h"
0012 
0013 LegendModel::LegendModel(QObject *parent)
0014     : QAbstractListModel(parent)
0015 {
0016 }
0017 
0018 QHash<int, QByteArray> LegendModel::roleNames() const
0019 {
0020     static QHash<int, QByteArray> names = {
0021         {NameRole, "name"},
0022         {ShortNameRole, "shortName"},
0023         {ColorRole, "color"},
0024         {ValueRole, "value"},
0025     };
0026 
0027     return names;
0028 }
0029 
0030 int LegendModel::rowCount(const QModelIndex &parent) const
0031 {
0032     if (parent.isValid()) {
0033         return 0;
0034     }
0035 
0036     return m_items.size();
0037 }
0038 
0039 QVariant LegendModel::data(const QModelIndex &index, int role) const
0040 {
0041     if (!checkIndex(index, CheckIndexOption::ParentIsInvalid | CheckIndexOption::IndexIsValid)) {
0042         return {};
0043     }
0044 
0045     switch (role) {
0046     case NameRole:
0047         return m_items.at(index.row()).name;
0048     case ShortNameRole:
0049         return m_items.at(index.row()).shortName;
0050     case ColorRole:
0051         return m_items.at(index.row()).color;
0052     case ValueRole:
0053         return m_items.at(index.row()).value;
0054     }
0055 
0056     return QVariant{};
0057 }
0058 
0059 Chart *LegendModel::chart() const
0060 {
0061     return m_chart;
0062 }
0063 
0064 void LegendModel::setChart(Chart *newChart)
0065 {
0066     if (newChart == m_chart) {
0067         return;
0068     }
0069 
0070     if (m_chart) {
0071         for (const auto &connection : std::as_const(m_connections)) {
0072             disconnect(connection);
0073         }
0074         m_connections.clear();
0075     }
0076 
0077     m_chart = newChart;
0078     queueUpdate();
0079     Q_EMIT chartChanged();
0080 }
0081 
0082 int LegendModel::sourceIndex() const
0083 {
0084     return m_sourceIndex;
0085 }
0086 
0087 void LegendModel::setSourceIndex(int index)
0088 {
0089     if (index == m_sourceIndex) {
0090         return;
0091     }
0092 
0093     m_sourceIndex = index;
0094     queueUpdate();
0095     Q_EMIT sourceIndexChanged();
0096 }
0097 
0098 void LegendModel::queueUpdate()
0099 {
0100     if (!m_updateQueued) {
0101         m_updateQueued = true;
0102         QMetaObject::invokeMethod(this, &LegendModel::update, Qt::QueuedConnection);
0103     }
0104 }
0105 
0106 void LegendModel::queueDataChange()
0107 {
0108     if (!m_dataChangeQueued) {
0109         m_dataChangeQueued = true;
0110         QMetaObject::invokeMethod(this, &LegendModel::updateData, Qt::QueuedConnection);
0111     }
0112 }
0113 
0114 void LegendModel::update()
0115 {
0116     m_updateQueued = false;
0117 
0118     if (!m_chart) {
0119         return;
0120     }
0121 
0122     beginResetModel();
0123     m_items.clear();
0124 
0125     ChartDataSource *colorSource = m_chart->colorSource();
0126     ChartDataSource *nameSource = m_chart->nameSource();
0127     ChartDataSource *shortNameSource = m_chart->shortNameSource();
0128 
0129     m_connections.push_back(connect(m_chart, &Chart::colorSourceChanged, this, &LegendModel::queueUpdate, Qt::UniqueConnection));
0130     m_connections.push_back(connect(m_chart, &Chart::nameSourceChanged, this, &LegendModel::queueUpdate, Qt::UniqueConnection));
0131 
0132     auto sources = m_chart->valueSources();
0133     int itemCount = countItems();
0134 
0135     std::transform(sources.cbegin(), sources.cend(), std::back_inserter(m_connections), [this](ChartDataSource *source) {
0136         return connect(source, &ChartDataSource::dataChanged, this, &LegendModel::queueDataChange, Qt::UniqueConnection);
0137     });
0138 
0139     m_connections.push_back(connect(m_chart, &Chart::valueSourcesChanged, this, &LegendModel::queueUpdate, Qt::UniqueConnection));
0140 
0141     if ((!colorSource && !(nameSource || shortNameSource)) || itemCount <= 0) {
0142         endResetModel();
0143         return;
0144     }
0145 
0146     if (colorSource) {
0147         m_connections.push_back(connect(colorSource, &ChartDataSource::dataChanged, this, &LegendModel::queueDataChange, Qt::UniqueConnection));
0148     }
0149 
0150     if (nameSource) {
0151         m_connections.push_back(connect(nameSource, &ChartDataSource::dataChanged, this, &LegendModel::queueDataChange, Qt::UniqueConnection));
0152     }
0153 
0154     if (shortNameSource) {
0155         m_connections.push_back(connect(shortNameSource, &ChartDataSource::dataChanged, this, &LegendModel::queueDataChange, Qt::UniqueConnection));
0156     }
0157 
0158     for (int i = 0; i < itemCount; ++i) {
0159         LegendItem item;
0160         item.name = nameSource ? nameSource->item(i).toString() : QString();
0161         item.shortName = shortNameSource ? shortNameSource->item(i).toString() : QString();
0162         item.color = colorSource ? colorSource->item(i).value<QColor>() : QColor();
0163         item.value = getValueForItem(i);
0164         m_items.push_back(item);
0165     }
0166 
0167     endResetModel();
0168 }
0169 
0170 void LegendModel::updateData()
0171 {
0172     ChartDataSource *colorSource = m_chart->colorSource();
0173     ChartDataSource *nameSource = m_chart->nameSource();
0174     ChartDataSource *shortNameSource = m_chart->shortNameSource();
0175 
0176     auto itemCount = countItems();
0177 
0178     m_dataChangeQueued = false;
0179 
0180     if (itemCount != int(m_items.size())) {
0181         // Number of items changed, so trigger a full update
0182         queueUpdate();
0183         return;
0184     }
0185 
0186     QList<QList<int>> changedRows(itemCount);
0187 
0188     std::for_each(m_items.begin(), m_items.end(), [&, i = 0](LegendItem &item) mutable {
0189         auto name = nameSource ? nameSource->item(i).toString() : QString{};
0190         if (item.name != name) {
0191             item.name = name;
0192             changedRows[i] << NameRole;
0193         }
0194 
0195         auto shortName = shortNameSource ? shortNameSource->item(i).toString() : QString{};
0196         if (item.shortName != shortName) {
0197             item.shortName = shortName;
0198             changedRows[i] << ShortNameRole;
0199         }
0200 
0201         auto color = colorSource ? colorSource->item(i).toString() : QColor{};
0202         if (item.color != color) {
0203             item.color = color;
0204             changedRows[i] << ColorRole;
0205         }
0206 
0207         auto value = getValueForItem(i);
0208         if (item.value != value) {
0209             item.value = value;
0210             changedRows[i] << ValueRole;
0211         }
0212 
0213         i++;
0214     });
0215 
0216     for (auto i = 0; i < changedRows.size(); ++i) {
0217         auto changedRoles = changedRows.at(i);
0218         if (!changedRoles.isEmpty()) {
0219             Q_EMIT dataChanged(index(i, 0), index(i, 0), changedRoles);
0220         }
0221     }
0222 }
0223 
0224 int LegendModel::countItems()
0225 {
0226     auto sources = m_chart->valueSources();
0227     int itemCount = 0;
0228 
0229     switch (m_chart->indexingMode()) {
0230     case Chart::IndexSourceValues:
0231         if (sources.count() > 0) {
0232             itemCount = sources.at(0)->itemCount();
0233         }
0234         break;
0235     case Chart::IndexEachSource:
0236         itemCount = sources.count();
0237         break;
0238     case Chart::IndexAllValues:
0239         itemCount = std::accumulate(sources.cbegin(), sources.cend(), 0, [](int current, ChartDataSource *source) -> int {
0240             return current + source->itemCount();
0241         });
0242         break;
0243     }
0244 
0245     return itemCount;
0246 }
0247 
0248 QVariant LegendModel::getValueForItem(int item)
0249 {
0250     const auto sources = m_chart->valueSources();
0251     auto value = QVariant{};
0252 
0253     switch (m_chart->indexingMode()) {
0254     case Chart::IndexSourceValues:
0255         value = sources.at(0)->item(item);
0256         break;
0257     case Chart::IndexEachSource:
0258         value = sources.at(item)->first();
0259         break;
0260     case Chart::IndexAllValues:
0261         for (auto source : sources) {
0262             if (item >= source->itemCount()) {
0263                 item -= source->itemCount();
0264             } else {
0265                 value = source->item(item);
0266                 break;
0267             }
0268         }
0269         break;
0270     }
0271 
0272     return value;
0273 }
0274 
0275 #include "moc_LegendModel.cpp"