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