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 "HistoryProxySource.h"
9
10#include <QDebug>
11
12HistoryProxySource::HistoryProxySource(QObject *parent)
13 : ChartDataSource(parent)
14{
15}
16
17int HistoryProxySource::itemCount() const
18{
19 if (m_fillMode == DoNotFill) {
20 return m_history.size();
21 } else {
22 return m_maximumHistory;
23 }
24}
25
26QVariant HistoryProxySource::item(int index) const
27{
28 if (index < 0 || !m_dataSource || m_dataSource->itemCount() == 0) {
29 return QVariant{};
30 }
31
32 if (m_fillMode == DoNotFill && index >= m_history.count()) {
33 return QVariant{};
34 }
35
36 if (m_fillMode == FillFromStart && index >= m_history.count()) {
37 return QVariant{QMetaType(m_dataSource->item(index: 0).userType())};
38 }
39
40 if (m_fillMode == FillFromEnd && m_history.count() != m_maximumHistory) {
41 auto actualIndex = index - (m_maximumHistory - m_history.count());
42 if (actualIndex < 0 || actualIndex >= m_history.size()) {
43 return QVariant{QMetaType(m_dataSource->item(index: 0).userType())};
44 } else {
45 return m_history.at(i: actualIndex);
46 }
47 }
48
49 if (index < m_history.count()) {
50 return m_history.at(i: index);
51 } else {
52 return QVariant{};
53 }
54}
55
56QVariant HistoryProxySource::minimum() const
57{
58 if (m_history.isEmpty() || !m_dataSource) {
59 return QVariant{};
60 }
61
62 // TODO: Find a nicer solution for data sources to indicate
63 // "I provide a min/max value not derived from my items"
64 auto model = m_dataSource->property(name: "model").value<QObject *>();
65 if (model) {
66 auto minProperty = model->property(name: "minimum");
67 auto maxProperty = model->property(name: "maximum");
68 if (minProperty.isValid() && minProperty != maxProperty) {
69 return minProperty;
70 }
71 }
72
73 return *std::min_element(first: m_history.begin(), last: m_history.end(), comp: variantCompare);
74}
75
76QVariant HistoryProxySource::maximum() const
77{
78 if (m_history.isEmpty() || !m_dataSource) {
79 return QVariant{};
80 }
81
82 auto model = m_dataSource->property(name: "model").value<QObject *>();
83 if (model) {
84 auto minProperty = model->property(name: "minimum");
85 auto maxProperty = model->property(name: "maximum");
86 if (maxProperty.isValid() && maxProperty != minProperty) {
87 return maxProperty;
88 }
89 }
90
91 return *std::max_element(first: m_history.begin(), last: m_history.end(), comp: variantCompare);
92}
93
94QVariant HistoryProxySource::first() const
95{
96 if (!m_history.isEmpty()) {
97 return m_history.first();
98 }
99 return QVariant{};
100}
101
102ChartDataSource *HistoryProxySource::source() const
103{
104 return m_dataSource;
105}
106
107void HistoryProxySource::setSource(ChartDataSource *newSource)
108{
109 if (newSource == m_dataSource) {
110 return;
111 }
112
113 if (m_dataSource) {
114 m_dataSource->disconnect(receiver: this);
115 }
116
117 m_dataSource = newSource;
118 clear();
119 if (m_dataSource) {
120 connect(sender: m_dataSource, signal: &ChartDataSource::dataChanged, context: this, slot: [this]() {
121 if (!m_updateTimer) {
122 update();
123 }
124 });
125 }
126 Q_EMIT sourceChanged();
127}
128
129int HistoryProxySource::item() const
130{
131 return m_item;
132}
133
134void HistoryProxySource::setItem(int newItem)
135{
136 if (newItem == m_item) {
137 return;
138 }
139
140 m_item = newItem;
141 clear();
142 Q_EMIT itemChanged();
143}
144
145int HistoryProxySource::maximumHistory() const
146{
147 return m_maximumHistory;
148}
149
150void HistoryProxySource::setMaximumHistory(int newMaximumHistory)
151{
152 if (newMaximumHistory == m_maximumHistory) {
153 return;
154 }
155
156 m_maximumHistory = newMaximumHistory;
157 while (m_history.size() > 0 && m_history.size() > m_maximumHistory) {
158 m_history.removeLast();
159 }
160
161 Q_EMIT maximumHistoryChanged();
162}
163
164int HistoryProxySource::interval() const
165{
166 return m_updateTimer ? m_updateTimer->interval() : -1;
167}
168
169void HistoryProxySource::setInterval(int newInterval)
170{
171 if (m_updateTimer && newInterval == m_updateTimer->interval()) {
172 return;
173 }
174
175 if (newInterval > 0) {
176 if (!m_updateTimer) {
177 m_updateTimer = std::make_unique<QTimer>();
178 // We need precise timers to avoid missing updates when dealing with semi-constantly
179 // updating source. That is, if the source updates at 500ms and we also update at that
180 // rate, a drift of 2ms can cause us to miss updates.
181 m_updateTimer->setTimerType(Qt::PreciseTimer);
182 connect(sender: m_updateTimer.get(), signal: &QTimer::timeout, context: this, slot: &HistoryProxySource::update);
183 }
184 m_updateTimer->setInterval(newInterval);
185 m_updateTimer->start();
186 } else {
187 m_updateTimer.reset();
188 }
189
190 Q_EMIT intervalChanged();
191}
192
193HistoryProxySource::FillMode HistoryProxySource::fillMode() const
194{
195 return m_fillMode;
196}
197
198void HistoryProxySource::setFillMode(FillMode newFillMode)
199{
200 if (newFillMode == m_fillMode) {
201 return;
202 }
203
204 m_fillMode = newFillMode;
205 clear();
206 Q_EMIT fillModeChanged();
207}
208
209void HistoryProxySource::clear()
210{
211 m_history.clear();
212 Q_EMIT dataChanged();
213}
214
215void HistoryProxySource::update()
216{
217 if (!m_dataSource) {
218 return;
219 }
220
221 m_history.prepend(t: m_dataSource->item(index: m_item));
222 while (m_history.size() > 0 && m_history.size() > m_maximumHistory) {
223 m_history.removeLast();
224 }
225
226 Q_EMIT dataChanged();
227}
228
229#include "moc_HistoryProxySource.cpp"
230

source code of kquickcharts/src/datasource/HistoryProxySource.cpp