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"