Changeset View
Changeset View
Standalone View
Standalone View
sensors/SensorTreeModel.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | Copyright (c) 2019 Eike Hein <hein@kde.org> | ||||
3 | Copyright (C) 2020 Arjen Hiemstra <ahiemstra@heimr.nl> | ||||
4 | | ||||
5 | This library is free software; you can redistribute it and/or | ||||
6 | modify it under the terms of the GNU Library General Public | ||||
7 | License as published by the Free Software Foundation; either | ||||
8 | version 2 of the License, or (at your option) any later version. | ||||
9 | | ||||
10 | This library is distributed in the hope that it will be useful, | ||||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
13 | Library General Public License for more details. | ||||
14 | | ||||
15 | You should have received a copy of the GNU Library General Public License | ||||
16 | along with this library; see the file COPYING.LIB. If not, write to | ||||
17 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||||
18 | Boston, MA 02110-1301, USA. | ||||
19 | */ | ||||
20 | | ||||
21 | #include "SensorTreeModel.h" | ||||
22 | | ||||
23 | #include <KLocalizedString> | ||||
24 | #include <QDebug> | ||||
25 | #include <QMetaEnum> | ||||
26 | #include <QMimeData> | ||||
27 | | ||||
28 | #include "formatter/Formatter.h" | ||||
29 | | ||||
30 | #include "Sensor.h" | ||||
31 | #include "SensorDaemonInterface_p.h" | ||||
32 | #include "SensorInfo_p.h" | ||||
33 | #include "SensorQuery.h" | ||||
34 | | ||||
35 | using namespace KSysGuard; | ||||
36 | | ||||
37 | struct Q_DECL_HIDDEN SensorTreeItem | ||||
38 | { | ||||
39 | SensorTreeItem *parent = nullptr; | ||||
40 | QString name; | ||||
41 | QVector<SensorTreeItem *> children; | ||||
42 | | ||||
43 | inline int indexOf(const QString &name) | ||||
44 | { | ||||
45 | int index = -1; | ||||
46 | | ||||
47 | for (int i = 0; i < children.count(); ++i) { | ||||
48 | if (children.at(i)->name == name) { | ||||
49 | return i; | ||||
50 | } | ||||
51 | } | ||||
52 | | ||||
53 | return index; | ||||
54 | } | ||||
55 | | ||||
56 | ~SensorTreeItem() | ||||
57 | { | ||||
58 | qDeleteAll(children); | ||||
59 | } | ||||
60 | }; | ||||
61 | | ||||
62 | class Q_DECL_HIDDEN SensorTreeModel::Private | ||||
63 | { | ||||
64 | public: | ||||
65 | Private(SensorTreeModel *qq) | ||||
66 | : rootItem(new SensorTreeItem) | ||||
67 | , q(qq) | ||||
68 | { | ||||
69 | } | ||||
70 | ~Private() | ||||
71 | { | ||||
72 | delete rootItem; | ||||
73 | } | ||||
74 | | ||||
75 | SensorTreeItem *rootItem; | ||||
76 | QHash<SensorTreeItem *, SensorInfo> sensorInfos; | ||||
77 | | ||||
78 | void addSensor(const QString &sensorId, const SensorInfo &info); | ||||
79 | void removeSensor(const QString &sensorId); | ||||
80 | | ||||
81 | QString sensorId(const QModelIndex &index); | ||||
82 | | ||||
83 | SensorTreeItem *find(const QString &sensorId); | ||||
84 | | ||||
85 | private: | ||||
86 | SensorTreeModel *q; | ||||
87 | }; | ||||
88 | | ||||
89 | void SensorTreeModel::Private::addSensor(const QString &sensorId, const SensorInfo &info) | ||||
90 | { | ||||
91 | const QStringList &segments = sensorId.split(QLatin1Char('/')); | ||||
92 | | ||||
93 | if (!segments.count() || segments.at(0).isEmpty()) { | ||||
94 | qDebug() << "Rejecting sensor" << sensorId << "- sensor id is not well-formed."; | ||||
95 | return; | ||||
96 | } | ||||
97 | | ||||
98 | SensorTreeItem *item = rootItem; | ||||
99 | | ||||
100 | for (auto segment : segments) { | ||||
101 | int index = item->indexOf(segment); | ||||
102 | | ||||
103 | if (index != -1) { | ||||
104 | item = item->children.at(index); | ||||
105 | } else { | ||||
106 | SensorTreeItem *newItem = new SensorTreeItem(); | ||||
107 | newItem->parent = item; | ||||
108 | newItem->name = segment; | ||||
109 | | ||||
110 | const QModelIndex &parentIndex = (item == rootItem) ? QModelIndex() : q->createIndex(item->parent->children.indexOf(item), 0, item); | ||||
111 | q->beginInsertRows(parentIndex, item->children.count(), item->children.count()); | ||||
112 | item->children.append(newItem); | ||||
113 | q->endInsertRows(); | ||||
114 | | ||||
115 | item = newItem; | ||||
116 | } | ||||
117 | } | ||||
118 | | ||||
119 | sensorInfos[item] = info; | ||||
120 | } | ||||
121 | | ||||
122 | void SensorTreeModel::Private::removeSensor(const QString &sensorId) | ||||
123 | { | ||||
124 | SensorTreeItem *item = rootItem; | ||||
125 | | ||||
126 | const auto segments = sensorId.split(QLatin1Char('/')); | ||||
127 | for (const QString &segment : segments) { | ||||
128 | int index = item->indexOf(segment); | ||||
129 | Q_ASSERT(index != -1); | ||||
130 | item = item->children.at(index); | ||||
131 | } | ||||
132 | | ||||
133 | SensorTreeItem *parent = item->parent; | ||||
134 | | ||||
135 | if (!parent) { | ||||
136 | return; | ||||
137 | } | ||||
138 | | ||||
139 | auto remove = [this](SensorTreeItem *item, SensorTreeItem *parent) { | ||||
140 | const int index = item->parent->children.indexOf(item); | ||||
141 | | ||||
142 | const QModelIndex &parentIndex = (parent == rootItem) ? QModelIndex() : q->createIndex(parent->parent->children.indexOf(parent), 0, parent); | ||||
143 | q->beginRemoveRows(parentIndex, index, index); | ||||
144 | delete item->parent->children.takeAt(index); | ||||
145 | q->endRemoveRows(); | ||||
146 | | ||||
147 | sensorInfos.remove(item); | ||||
148 | }; | ||||
149 | | ||||
150 | remove(item, parent); | ||||
151 | | ||||
152 | while (!parent->children.count()) { | ||||
153 | item = parent; | ||||
154 | parent = parent->parent; | ||||
155 | | ||||
156 | if (!parent) { | ||||
157 | break; | ||||
158 | } | ||||
159 | | ||||
160 | remove(item, parent); | ||||
161 | } | ||||
162 | } | ||||
163 | | ||||
164 | QString SensorTreeModel::Private::sensorId(const QModelIndex &index) | ||||
165 | { | ||||
166 | QStringList segments; | ||||
167 | | ||||
168 | SensorTreeItem *item = static_cast<SensorTreeItem *>(index.internalPointer()); | ||||
169 | | ||||
170 | segments << item->name; | ||||
171 | | ||||
172 | while (item->parent && item->parent != rootItem) { | ||||
173 | item = item->parent; | ||||
174 | segments.prepend(item->name); | ||||
175 | } | ||||
176 | | ||||
177 | return segments.join(QLatin1Char('/')); | ||||
178 | } | ||||
179 | | ||||
180 | SensorTreeItem *KSysGuard::SensorTreeModel::Private::find(const QString &sensorId) | ||||
181 | { | ||||
182 | auto item = rootItem; | ||||
183 | const auto segments = sensorId.split(QLatin1Char('/')); | ||||
184 | for (const QString &segment : segments) { | ||||
185 | int index = item->indexOf(segment); | ||||
186 | if (index != -1) { | ||||
187 | item = item->children.at(index); | ||||
188 | } else { | ||||
189 | return nullptr; | ||||
190 | } | ||||
191 | } | ||||
192 | return item; | ||||
193 | } | ||||
194 | | ||||
195 | SensorTreeModel::SensorTreeModel(QObject *parent) | ||||
196 | : QAbstractItemModel(parent) | ||||
197 | , d(new Private(this)) | ||||
198 | { | ||||
199 | connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::sensorAdded, this, &SensorTreeModel::onSensorAdded); | ||||
200 | connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::sensorRemoved, this, &SensorTreeModel::onSensorRemoved); | ||||
201 | connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::metaDataChanged, this, &SensorTreeModel::onMetaDataChanged); | ||||
202 | init(); | ||||
203 | } | ||||
204 | | ||||
205 | SensorTreeModel::~SensorTreeModel() | ||||
206 | { | ||||
207 | } | ||||
208 | | ||||
209 | QHash<int, QByteArray> SensorTreeModel::roleNames() const | ||||
210 | { | ||||
211 | QHash<int, QByteArray> roles = QAbstractItemModel::roleNames(); | ||||
212 | | ||||
213 | QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("AdditionalRoles")); | ||||
214 | | ||||
215 | for (int i = 0; i < e.keyCount(); ++i) { | ||||
216 | roles.insert(e.value(i), e.key(i)); | ||||
217 | } | ||||
218 | | ||||
219 | return roles; | ||||
220 | } | ||||
221 | | ||||
222 | QVariant SensorTreeModel::headerData(int section, Qt::Orientation, int role) const | ||||
223 | { | ||||
224 | if (role != Qt::DisplayRole) { | ||||
225 | return QVariant(); | ||||
226 | } | ||||
227 | | ||||
228 | if (section == 0) { | ||||
229 | return i18n("Sensor Browser"); | ||||
230 | } | ||||
231 | | ||||
232 | return QVariant(); | ||||
233 | } | ||||
234 | | ||||
235 | QStringList SensorTreeModel::mimeTypes() const | ||||
236 | { | ||||
237 | return QStringList() << QStringLiteral("application/x-ksysguard"); | ||||
238 | } | ||||
239 | | ||||
240 | QVariant SensorTreeModel::data(const QModelIndex &index, int role) const | ||||
241 | { | ||||
242 | const bool check = checkIndex(index, CheckIndexOption::IndexIsValid); | ||||
243 | | ||||
244 | Q_ASSERT(check); | ||||
245 | | ||||
246 | if (!check) { | ||||
247 | return QVariant(); | ||||
248 | } | ||||
249 | | ||||
250 | if (role == Qt::DisplayRole) { | ||||
251 | SensorTreeItem *item = static_cast<SensorTreeItem *>(index.internalPointer()); | ||||
252 | | ||||
253 | if (d->sensorInfos.contains(item)) { | ||||
254 | auto info = d->sensorInfos.value(item); | ||||
255 | const QString &unit = Formatter::symbol(info.unit); | ||||
256 | | ||||
257 | if (!unit.isEmpty()) { | ||||
258 | return i18nc("Name (unit)", "%1 (%2)", info.name, unit); | ||||
259 | } | ||||
260 | | ||||
261 | return info.name; | ||||
262 | } | ||||
263 | | ||||
264 | const QString &name = item->name; | ||||
265 | if (name.isEmpty()) { | ||||
266 | return i18n("EMPTY"); | ||||
267 | } else { | ||||
268 | return name; | ||||
269 | } | ||||
270 | // Only leaf nodes are valid sensors | ||||
271 | } else if (role == SensorId) { | ||||
272 | if (rowCount(index)) { | ||||
273 | return QString(); | ||||
274 | } else { | ||||
275 | return d->sensorId(index); | ||||
276 | } | ||||
277 | } | ||||
278 | | ||||
279 | return QVariant(); | ||||
280 | } | ||||
281 | | ||||
282 | QMimeData *SensorTreeModel::mimeData(const QModelIndexList &indexes) const | ||||
283 | { | ||||
284 | QMimeData *mimeData = new QMimeData(); | ||||
285 | | ||||
286 | if (indexes.count() != 1) { | ||||
287 | return mimeData; | ||||
288 | } | ||||
289 | | ||||
290 | const QModelIndex &index = indexes.at(0); | ||||
291 | | ||||
292 | const bool check = checkIndex(index, CheckIndexOption::IndexIsValid); | ||||
293 | | ||||
294 | Q_ASSERT(check); | ||||
295 | | ||||
296 | if (!check) { | ||||
297 | return mimeData; | ||||
298 | } | ||||
299 | | ||||
300 | if (rowCount(index)) { | ||||
301 | return mimeData; | ||||
302 | } | ||||
303 | | ||||
304 | mimeData->setData(QStringLiteral("application/x-ksysguard"), d->sensorId(index).toUtf8()); | ||||
305 | | ||||
306 | return mimeData; | ||||
307 | } | ||||
308 | | ||||
309 | Qt::ItemFlags SensorTreeModel::flags(const QModelIndex &index) const | ||||
310 | { | ||||
311 | const bool check = checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::DoNotUseParent); | ||||
312 | | ||||
313 | Q_ASSERT(check); | ||||
314 | | ||||
315 | if (!check) { | ||||
316 | return Qt::NoItemFlags; | ||||
317 | } | ||||
318 | | ||||
319 | if (!rowCount(index)) { | ||||
320 | return Qt::ItemIsDragEnabled | Qt::ItemIsSelectable | Qt::ItemIsEnabled; | ||||
321 | } | ||||
322 | | ||||
323 | return Qt::ItemIsEnabled; | ||||
324 | } | ||||
325 | | ||||
326 | int SensorTreeModel::rowCount(const QModelIndex &parent) const | ||||
327 | { | ||||
328 | if (parent.isValid()) { | ||||
329 | const bool check = checkIndex(parent, CheckIndexOption::IndexIsValid | CheckIndexOption::DoNotUseParent); | ||||
330 | | ||||
331 | Q_ASSERT(check); | ||||
332 | | ||||
333 | if (!check) { | ||||
334 | return 0; | ||||
335 | } | ||||
336 | | ||||
337 | const SensorTreeItem *item = static_cast<SensorTreeItem *>(parent.internalPointer()); | ||||
338 | return item->children.count(); | ||||
339 | } | ||||
340 | | ||||
341 | return d->rootItem->children.count(); | ||||
342 | } | ||||
343 | | ||||
344 | int SensorTreeModel::columnCount(const QModelIndex &parent) const | ||||
345 | { | ||||
346 | Q_UNUSED(parent) | ||||
347 | | ||||
348 | return 1; | ||||
349 | } | ||||
350 | | ||||
351 | QModelIndex SensorTreeModel::index(int row, int column, const QModelIndex &parent) const | ||||
352 | { | ||||
353 | SensorTreeItem *parentItem = d->rootItem; | ||||
354 | | ||||
355 | if (parent.isValid()) { | ||||
356 | Q_ASSERT(parent.model() == this); | ||||
357 | | ||||
358 | if (parent.model() != this) { | ||||
359 | return QModelIndex(); | ||||
360 | } | ||||
361 | | ||||
362 | parentItem = static_cast<SensorTreeItem *>(parent.internalPointer()); | ||||
363 | } | ||||
364 | | ||||
365 | if (row < 0 || row >= parentItem->children.count()) { | ||||
366 | return QModelIndex(); | ||||
367 | } | ||||
368 | | ||||
369 | if (column < 0) { | ||||
370 | return QModelIndex(); | ||||
371 | } | ||||
372 | | ||||
373 | return createIndex(row, column, parentItem->children.at(row)); | ||||
374 | } | ||||
375 | | ||||
376 | QModelIndex SensorTreeModel::parent(const QModelIndex &index) const | ||||
377 | { | ||||
378 | const bool check = checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::DoNotUseParent); | ||||
379 | | ||||
380 | Q_ASSERT(check); | ||||
381 | | ||||
382 | if (!check) { | ||||
383 | return QModelIndex(); | ||||
384 | } | ||||
385 | | ||||
386 | if (index.column() > 0) { | ||||
387 | return QModelIndex(); | ||||
388 | } | ||||
389 | | ||||
390 | const SensorTreeItem *item = static_cast<SensorTreeItem *>(index.internalPointer()); | ||||
391 | SensorTreeItem *parentItem = item->parent; | ||||
392 | | ||||
393 | if (parentItem == d->rootItem) { | ||||
394 | return QModelIndex(); | ||||
395 | } | ||||
396 | | ||||
397 | return createIndex(parentItem->parent->children.indexOf(parentItem), 0, parentItem); | ||||
398 | } | ||||
399 | | ||||
400 | void SensorTreeModel::init() | ||||
401 | { | ||||
402 | auto query = new SensorQuery{QString(), this}; | ||||
403 | connect(query, &SensorQuery::finished, [query, this]() { | ||||
404 | query->deleteLater(); | ||||
405 | const auto result = query->result(); | ||||
406 | for (auto pair : result) { | ||||
407 | d->addSensor(pair.first, pair.second); | ||||
408 | } | ||||
409 | }); | ||||
410 | query->execute(); | ||||
411 | } | ||||
412 | | ||||
413 | void KSysGuard::SensorTreeModel::onSensorAdded(const QString &sensor) | ||||
414 | { | ||||
415 | SensorDaemonInterface::instance()->requestMetaData(sensor); | ||||
416 | } | ||||
417 | | ||||
418 | void KSysGuard::SensorTreeModel::onSensorRemoved(const QString &sensor) | ||||
419 | { | ||||
420 | d->removeSensor(sensor); | ||||
421 | } | ||||
422 | | ||||
423 | void KSysGuard::SensorTreeModel::onMetaDataChanged(const QString &sensorId, const SensorInfo &info) | ||||
424 | { | ||||
425 | auto item = d->find(sensorId); | ||||
426 | if (!item) { | ||||
427 | d->addSensor(sensorId, info); | ||||
428 | } else { | ||||
429 | item->name = info.name; | ||||
430 | d->sensorInfos[item] = info; | ||||
431 | | ||||
432 | auto parentItem = item->parent; | ||||
433 | if (!parentItem) { | ||||
434 | return; | ||||
435 | } | ||||
436 | | ||||
437 | auto parentIndex = QModelIndex{}; | ||||
438 | if (parentItem != d->rootItem) { | ||||
439 | parentIndex = createIndex(parentItem->parent->children.indexOf(parentItem), 0, parentItem); | ||||
440 | } | ||||
441 | | ||||
442 | auto itemIndex = index(parentItem->children.indexOf(item), 0, parentIndex); | ||||
443 | Q_EMIT dataChanged(itemIndex, itemIndex); | ||||
444 | } | ||||
445 | } |