KQuickCharts

LegendModel.cpp
1/*
2 * This file is part of KQuickCharts
3 * SPDX-FileCopyrightText: 2019 Arjen Hiemstra <ahiemstra@heimr.nl>
4 *
5 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6 */
7
8#include "LegendModel.h"
9
10#include "Chart.h"
11#include "datasource/ChartDataSource.h"
12
13LegendModel::LegendModel(QObject *parent)
14 : QAbstractListModel(parent)
15{
16}
17
18QHash<int, QByteArray> LegendModel::roleNames() const
19{
20 static QHash<int, QByteArray> names = {
21 {NameRole, "name"},
22 {ShortNameRole, "shortName"},
23 {ColorRole, "color"},
24 {ValueRole, "value"},
25 };
26
27 return names;
28}
29
30int LegendModel::rowCount(const QModelIndex &parent) const
31{
32 if (parent.isValid()) {
33 return 0;
34 }
35
36 return m_items.size();
37}
38
39QVariant LegendModel::data(const QModelIndex &index, int role) const
40{
41 if (!checkIndex(index, CheckIndexOption::ParentIsInvalid | CheckIndexOption::IndexIsValid)) {
42 return {};
43 }
44
45 switch (role) {
46 case NameRole:
47 return m_items.at(index.row()).name;
48 case ShortNameRole:
49 return m_items.at(index.row()).shortName;
50 case ColorRole:
51 return m_items.at(index.row()).color;
52 case ValueRole:
53 return m_items.at(index.row()).value;
54 }
55
56 return QVariant{};
57}
58
59Chart *LegendModel::chart() const
60{
61 return m_chart;
62}
63
64void LegendModel::setChart(Chart *newChart)
65{
66 if (newChart == m_chart) {
67 return;
68 }
69
70 if (m_chart) {
71 for (const auto &connection : std::as_const(m_connections)) {
72 disconnect(connection);
73 }
74 m_connections.clear();
75 }
76
77 m_chart = newChart;
78 queueUpdate();
79 Q_EMIT chartChanged();
80}
81
82int LegendModel::sourceIndex() const
83{
84 return m_sourceIndex;
85}
86
87void LegendModel::setSourceIndex(int index)
88{
89 if (index == m_sourceIndex) {
90 return;
91 }
92
93 m_sourceIndex = index;
94 queueUpdate();
95 Q_EMIT sourceIndexChanged();
96}
97
98void LegendModel::queueUpdate()
99{
100 if (!m_updateQueued) {
101 m_updateQueued = true;
102 QMetaObject::invokeMethod(this, &LegendModel::update, Qt::QueuedConnection);
103 }
104}
105
106void LegendModel::queueDataChange()
107{
108 if (!m_dataChangeQueued) {
109 m_dataChangeQueued = true;
110 QMetaObject::invokeMethod(this, &LegendModel::updateData, Qt::QueuedConnection);
111 }
112}
113
114void LegendModel::update()
115{
116 m_updateQueued = false;
117
118 if (!m_chart) {
119 return;
120 }
121
123 m_items.clear();
124
125 ChartDataSource *colorSource = m_chart->colorSource();
126 ChartDataSource *nameSource = m_chart->nameSource();
127 ChartDataSource *shortNameSource = m_chart->shortNameSource();
128
129 m_connections.push_back(connect(m_chart, &Chart::colorSourceChanged, this, &LegendModel::queueUpdate, Qt::UniqueConnection));
130 m_connections.push_back(connect(m_chart, &Chart::nameSourceChanged, this, &LegendModel::queueUpdate, Qt::UniqueConnection));
131
132 auto sources = m_chart->valueSources();
133 int itemCount = countItems();
134
135 std::transform(sources.cbegin(), sources.cend(), std::back_inserter(m_connections), [this](ChartDataSource *source) {
136 return connect(source, &ChartDataSource::dataChanged, this, &LegendModel::queueDataChange, Qt::UniqueConnection);
137 });
138
139 m_connections.push_back(connect(m_chart, &Chart::valueSourcesChanged, this, &LegendModel::queueUpdate, Qt::UniqueConnection));
140
141 if ((!colorSource && !(nameSource || shortNameSource)) || itemCount <= 0) {
143 return;
144 }
145
146 if (colorSource) {
147 m_connections.push_back(connect(colorSource, &ChartDataSource::dataChanged, this, &LegendModel::queueDataChange, Qt::UniqueConnection));
148 }
149
150 if (nameSource) {
151 m_connections.push_back(connect(nameSource, &ChartDataSource::dataChanged, this, &LegendModel::queueDataChange, Qt::UniqueConnection));
152 }
153
154 if (shortNameSource) {
155 m_connections.push_back(connect(shortNameSource, &ChartDataSource::dataChanged, this, &LegendModel::queueDataChange, Qt::UniqueConnection));
156 }
157
158 for (int i = 0; i < itemCount; ++i) {
159 LegendItem item;
160 item.name = nameSource ? nameSource->item(i).toString() : QString();
161 item.shortName = shortNameSource ? shortNameSource->item(i).toString() : QString();
162 item.color = colorSource ? colorSource->item(i).value<QColor>() : QColor();
163 item.value = getValueForItem(i);
164 m_items.push_back(item);
165 }
166
168}
169
170void LegendModel::updateData()
171{
172 ChartDataSource *colorSource = m_chart->colorSource();
173 ChartDataSource *nameSource = m_chart->nameSource();
174 ChartDataSource *shortNameSource = m_chart->shortNameSource();
175
176 auto itemCount = countItems();
177
178 m_dataChangeQueued = false;
179
180 if (itemCount != int(m_items.size())) {
181 // Number of items changed, so trigger a full update
182 queueUpdate();
183 return;
184 }
185
186 QList<QList<int>> changedRows(itemCount);
187
188 std::for_each(m_items.begin(), m_items.end(), [&, i = 0](LegendItem &item) mutable {
189 auto name = nameSource ? nameSource->item(i).toString() : QString{};
190 if (item.name != name) {
191 item.name = name;
192 changedRows[i] << NameRole;
193 }
194
195 auto shortName = shortNameSource ? shortNameSource->item(i).toString() : QString{};
196 if (item.shortName != shortName) {
197 item.shortName = shortName;
198 changedRows[i] << ShortNameRole;
199 }
200
201 auto color = colorSource ? colorSource->item(i).toString() : QColor{};
202 if (item.color != color) {
203 item.color = color;
204 changedRows[i] << ColorRole;
205 }
206
207 auto value = getValueForItem(i);
208 if (item.value != value) {
209 item.value = value;
210 changedRows[i] << ValueRole;
211 }
212
213 i++;
214 });
215
216 for (auto i = 0; i < changedRows.size(); ++i) {
217 auto changedRoles = changedRows.at(i);
218 if (!changedRoles.isEmpty()) {
219 Q_EMIT dataChanged(index(i, 0), index(i, 0), changedRoles);
220 }
221 }
222}
223
224int LegendModel::countItems()
225{
226 auto sources = m_chart->valueSources();
227 int itemCount = 0;
228
229 switch (m_chart->indexingMode()) {
231 if (sources.count() > 0) {
232 itemCount = sources.at(0)->itemCount();
233 }
234 break;
236 itemCount = sources.count();
237 break;
239 itemCount = std::accumulate(sources.cbegin(), sources.cend(), 0, [](int current, ChartDataSource *source) -> int {
240 return current + source->itemCount();
241 });
242 break;
243 }
244
245 return itemCount;
246}
247
248QVariant LegendModel::getValueForItem(int item)
249{
250 const auto sources = m_chart->valueSources();
251 auto value = QVariant{};
252
253 switch (m_chart->indexingMode()) {
255 value = sources.at(0)->item(item);
256 break;
258 value = sources.at(item)->first();
259 break;
261 for (auto source : sources) {
262 if (item >= source->itemCount()) {
263 item -= source->itemCount();
264 } else {
265 value = source->item(item);
266 break;
267 }
268 }
269 break;
270 }
271
272 return value;
273}
274
275#include "moc_LegendModel.cpp"
Abstract base class for data sources.
Abstract base class for all charts.
Definition Chart.h:22
ChartDataSource * shortNameSource
The data source to use for short names of chart items.
Definition Chart.h:54
QQmlListProperty< ChartDataSource > valueSources
The data sources providing the data this chart needs to render.
Definition Chart.h:70
ChartDataSource * nameSource
The data source to use for names of chart items.
Definition Chart.h:46
ChartDataSource * colorSource
The data source to use for colors of chart items.
Definition Chart.h:62
@ IndexAllValues
Index each value, continuing with the index for each value source.
Definition Chart.h:36
@ IndexSourceValues
Index each value, restart indexing for each value source.
Definition Chart.h:34
@ IndexEachSource
Index each value source, never index individual values.
Definition Chart.h:35
IndexingMode indexingMode
The indexing mode used for indexing colors and names.
Definition Chart.h:81
QString name(StandardShortcut id)
bool checkIndex(const QModelIndex &index, CheckIndexOptions options) const const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
int row() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QObject * parent() const const
QueuedConnection
QString toString() const const
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:57 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.