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 "ModelHistorySource.h"
9
10 #include <QAbstractItemModel>
11 #include <QDebug>
12 #include <QTimer>
13 #include <QVariantList>
14
15 #include "QmlDeprecated.h"
16
17 #if QUICKCHARTS_BUILD_DEPRECATED_SINCE(5, 78)
18
ModelHistorySource(QObject * parent)19 ModelHistorySource::ModelHistorySource(QObject *parent)
20 : ModelSource(parent)
21 {
22 QML_DEPRECATED("ModelHistorySource", "5.78", "Use HistoryProxySource instead.")
23
24 connect(this, &ModelHistorySource::modelChanged, this, &ModelHistorySource::onModelChanged);
25 }
26
itemCount() const27 int ModelHistorySource::itemCount() const
28 {
29 return m_history.size();
30 }
31
item(int index) const32 QVariant ModelHistorySource::item(int index) const
33 {
34 if (index < 0 || index >= m_history.size()) {
35 return {};
36 }
37
38 return m_history.at(index);
39 }
40
minimum() const41 QVariant ModelHistorySource::minimum() const
42 {
43 if (m_history.isEmpty()) {
44 return {};
45 }
46
47 auto minProperty = model()->property("minimum");
48 auto maxProperty = model()->property("maximum");
49 if (maxProperty.isValid() && maxProperty != minProperty) {
50 return maxProperty;
51 }
52
53 return *std::min_element(m_history.begin(), m_history.end());
54 }
55
maximum() const56 QVariant ModelHistorySource::maximum() const
57 {
58 if (m_history.isEmpty()) {
59 return {};
60 }
61
62 auto minProperty = model()->property("minimum");
63 auto maxProperty = model()->property("maximum");
64 if (maxProperty.isValid() && maxProperty != minProperty) {
65 return maxProperty;
66 }
67
68 return *std::max_element(m_history.begin(), m_history.end());
69 }
70
row() const71 int ModelHistorySource::row() const
72 {
73 return m_row;
74 }
75
setRow(int row)76 void ModelHistorySource::setRow(int row)
77 {
78 if (m_row == row) {
79 return;
80 }
81
82 m_row = row;
83 Q_EMIT rowChanged();
84 }
85
maximumHistory() const86 int ModelHistorySource::maximumHistory() const
87 {
88 return m_maximumHistory;
89 }
90
setMaximumHistory(int maximumHistory)91 void ModelHistorySource::setMaximumHistory(int maximumHistory)
92 {
93 if (m_maximumHistory == maximumHistory) {
94 return;
95 }
96
97 m_maximumHistory = maximumHistory;
98 Q_EMIT maximumHistoryChanged();
99 }
100
interval() const101 int ModelHistorySource::interval() const
102 {
103 return m_updateTimer ? m_updateTimer->interval() : -1;
104 }
105
setInterval(int newInterval)106 void ModelHistorySource::setInterval(int newInterval)
107 {
108 if (m_updateTimer && newInterval == m_updateTimer->interval()) {
109 return;
110 }
111
112 if (newInterval > 0) {
113 if (!m_updateTimer) {
114 m_updateTimer = std::make_unique<QTimer>();
115 // We need precise timers to avoid missing updates when dealing with semi-constantly
116 // updating model. That is, if the model updates at 500ms and we also update at that
117 // rate, a drift of 2ms can cause us to miss updates.
118 m_updateTimer->setTimerType(Qt::PreciseTimer);
119 connect(m_updateTimer.get(), &QTimer::timeout, this, [this]() {
120 if (!model()) {
121 return;
122 }
123
124 auto index = model()->index(m_row, column());
125 onDataChanged(index, index, {role()});
126 });
127 if (model()) {
128 disconnect(model(), &QAbstractItemModel::dataChanged, this, &ModelHistorySource::onDataChanged);
129 }
130 }
131 m_updateTimer->setInterval(newInterval);
132 m_updateTimer->start();
133 } else {
134 m_updateTimer.reset();
135 onModelChanged();
136 }
137
138 Q_EMIT intervalChanged();
139 }
140
clear()141 void ModelHistorySource::clear()
142 {
143 m_history.clear();
144 Q_EMIT dataChanged();
145 }
146
onModelChanged()147 void ModelHistorySource::onModelChanged()
148 {
149 if (model() && !m_updateTimer) {
150 connect(model(), &QAbstractItemModel::dataChanged, this, &ModelHistorySource::onDataChanged);
151 }
152 }
153
onDataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight,const QVector<int> & roles)154 void ModelHistorySource::onDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
155 {
156 if (!model()) {
157 return;
158 }
159
160 if (!roles.isEmpty() && !roles.contains(role())) {
161 return;
162 }
163
164 if (topLeft.row() > m_row || bottomRight.row() < m_row) {
165 return;
166 }
167
168 if (topLeft.column() > column() || bottomRight.column() < column()) {
169 return;
170 }
171
172 auto entry = model()->data(model()->index(m_row, column()), role());
173
174 m_history.prepend(entry);
175 while (m_history.size() > m_maximumHistory) {
176 m_history.pop_back();
177 }
178
179 Q_EMIT dataChanged();
180 }
181
182 #endif // QUICKCHARTS_BUILD_DEPRECATED_SINCE
183