Libksysguard

SensorTreeModel.cpp
1 /*
2  Copyright (c) 2019 Eike Hein <[email protected]>
3  Copyright (C) 2020 Arjen Hiemstra <[email protected]>
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 #include <QRegularExpression>
28 
29 #include "formatter/Formatter.h"
30 
31 #include "Sensor.h"
32 #include "SensorDaemonInterface_p.h"
33 #include "SensorInfo_p.h"
34 #include "SensorQuery.h"
35 #include "SensorGroup_p.h"
36 
37 using namespace KSysGuard;
38 
39 struct Q_DECL_HIDDEN SensorTreeItem
40 {
41  SensorTreeItem *parent = nullptr;
42  QString segment;
44 
45  inline int indexOf(const QString &segment)
46  {
47  int index = 0;
48  for (auto child : qAsConst(children)) {
49  if (child->segment == segment) {
50  return index;
51  }
52  index++;
53  }
54 
55  return -1;
56  }
57 
58  inline SensorTreeItem *itemAt(int index) {
59  int currentIndex = 0;
60  for (auto child : qAsConst(children)) {
61  if (currentIndex++ == index) {
62  return child;
63  }
64  }
65  return nullptr;
66  }
67 
68  ~SensorTreeItem()
69  {
70  qDeleteAll(children);
71  }
72 };
73 
74 class Q_DECL_HIDDEN SensorTreeModel::Private
75 {
76 public:
77  Private(SensorTreeModel *qq)
78  : rootItem(new SensorTreeItem)
79  , q(qq)
80  {
81  m_sensorGroup = new SensorGroup;
82  }
83  ~Private()
84  {
85  delete rootItem;
86  delete m_sensorGroup;
87  }
88 
89  SensorTreeItem *rootItem;
91 
92  void addSensor(const QString &sensorId, const SensorInfo &info);
93  void removeSensor(const QString &sensorId);
94 
95  QString sensorId(const QModelIndex &index);
96 
97  SensorTreeItem *find(const QString &sensorId);
98 
99  SensorGroup *m_sensorGroup;
100 
101  QHash<QString, int> m_groupMatches;
102 
103 private:
104  SensorTreeModel *q;
105 };
106 
107 void SensorTreeModel::Private::addSensor(const QString &sensorId, const SensorInfo &info)
108 {
109  const QStringList &segments = sensorId.split(QLatin1Char('/'));
110 
111  if (!segments.count() || segments.at(0).isEmpty()) {
112  qDebug() << "Rejecting sensor" << sensorId << "- sensor id is not well-formed.";
113  return;
114  }
115 
116  QString sensorIdExpr = m_sensorGroup->groupRegexForId(sensorId);
117 
118  if (!sensorIdExpr.isEmpty()) {
119  if (m_groupMatches.contains(sensorIdExpr)) {
120  m_groupMatches[sensorIdExpr]++;
121  } else {
122  m_groupMatches[sensorIdExpr] = 1;
123  }
124 
125  if (m_groupMatches[sensorIdExpr] == 2) {
126  SensorInfo newInfo;
127  newInfo.name = m_sensorGroup->sensorNameForRegEx(sensorIdExpr);
128  newInfo.description = info.description;
129  newInfo.variantType = info.variantType;
130  newInfo.unit = info.unit;
131  newInfo.min = info.min;
132  newInfo.max = info.max;
133 
134  addSensor(sensorIdExpr, newInfo);
135  }
136  }
137  SensorTreeItem *item = rootItem;
138 
139  for (auto segment : segments) {
140  auto child = item->children.value(segment, nullptr);
141 
142  if (child) {
143  item = child;
144 
145  } else {
146  SensorTreeItem *newItem = new SensorTreeItem();
147  newItem->parent = item;
148  newItem->segment = segment;
149 
150  const QModelIndex &parentIndex = (item == rootItem) ? QModelIndex() : q->createIndex(item->parent->indexOf(item->segment), 0, item);
151 
152  auto index = std::distance(item->children.begin(), item->children.upperBound(segment));
153 
154  q->beginInsertRows(parentIndex, index, index);
155  item->children.insert(segment, newItem);
156  q->endInsertRows();
157 
158  item = newItem;
159  }
160  }
161 
162  sensorInfos[item] = info;
163 }
164 
165 void SensorTreeModel::Private::removeSensor(const QString &sensorId)
166 {
167  QString sensorIdExpr = m_sensorGroup->groupRegexForId(sensorId);
168  if (!sensorIdExpr.isEmpty()) {
169  if (m_groupMatches[sensorIdExpr] == 1) {
170  m_groupMatches.remove(sensorIdExpr);
171  removeSensor(sensorIdExpr);
172  } else if (m_groupMatches.contains(sensorIdExpr)) {
173  m_groupMatches[sensorIdExpr]--;
174  }
175  }
176 
177  SensorTreeItem *item = find(sensorId);
178  if (!item) {
179  return;
180  }
181 
182  SensorTreeItem *parent = item->parent;
183  if (!parent) {
184  return;
185  }
186 
187  auto remove = [this](SensorTreeItem *item, SensorTreeItem *parent) {
188  const int index = item->parent->indexOf(item->segment);
189 
190  const QModelIndex &parentIndex = (parent == rootItem) ? QModelIndex() : q->createIndex(parent->parent->indexOf(parent->segment), 0, parent);
191  q->beginRemoveRows(parentIndex, index, index);
192  delete item->parent->children.take(item->segment);
193  q->endRemoveRows();
194 
195  sensorInfos.remove(item);
196  };
197 
198  remove(item, parent);
199 
200  while (!parent->children.count()) {
201  item = parent;
202  parent = parent->parent;
203 
204  if (!parent) {
205  break;
206  }
207 
208  remove(item, parent);
209  }
210 }
211 
212 QString SensorTreeModel::Private::sensorId(const QModelIndex &index)
213 {
214  QStringList segments;
215 
216  SensorTreeItem *item = static_cast<SensorTreeItem *>(index.internalPointer());
217 
218  segments << item->segment;
219 
220  while (item->parent && item->parent != rootItem) {
221  item = item->parent;
222  segments.prepend(item->segment);
223  }
224 
225  return segments.join(QLatin1Char('/'));
226 }
227 
228 SensorTreeItem *KSysGuard::SensorTreeModel::Private::find(const QString &sensorId)
229 {
230  auto item = rootItem;
231  const auto segments = sensorId.split(QLatin1Char('/'));
232  for (const QString &segment : segments) {
233  item = item->children.value(segment, nullptr);
234  if (!item) {
235  return nullptr;
236  }
237  }
238  return item;
239 }
240 
241 SensorTreeModel::SensorTreeModel(QObject *parent)
242  : QAbstractItemModel(parent)
243  , d(new Private(this))
244 {
245  connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::sensorAdded, this, &SensorTreeModel::onSensorAdded);
246  connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::sensorRemoved, this, &SensorTreeModel::onSensorRemoved);
247  connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::metaDataChanged, this, &SensorTreeModel::onMetaDataChanged);
248  init();
249 }
250 
251 SensorTreeModel::~SensorTreeModel()
252 {
253 }
254 
255 QHash<int, QByteArray> SensorTreeModel::roleNames() const
256 {
258 
259  QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("AdditionalRoles"));
260 
261  for (int i = 0; i < e.keyCount(); ++i) {
262  roles.insert(e.value(i), e.key(i));
263  }
264 
265  return roles;
266 }
267 
268 QVariant SensorTreeModel::headerData(int section, Qt::Orientation, int role) const
269 {
270  if (role != Qt::DisplayRole) {
271  return QVariant();
272  }
273 
274  if (section == 0) {
275  return i18n("Sensor Browser");
276  }
277 
278  return QVariant();
279 }
280 
281 QStringList SensorTreeModel::mimeTypes() const
282 {
283  return QStringList() << QStringLiteral("application/x-ksysguard");
284 }
285 
286 QVariant SensorTreeModel::data(const QModelIndex &index, int role) const
287 {
288  if (!checkIndex(index, CheckIndexOption::IndexIsValid)) {
289  return QVariant();
290  }
291 
292  if (role == Qt::DisplayRole) {
293  SensorTreeItem *item = static_cast<SensorTreeItem *>(index.internalPointer());
294 
295  if (d->sensorInfos.contains(item)) {
296  auto info = d->sensorInfos.value(item);
297  const QString &unit = Formatter::symbol(info.unit);
298 
299  if (!unit.isEmpty()) {
300  return i18nc("Name (unit)", "%1 (%2)", info.name, unit);
301  }
302 
303  return info.name;
304  }
305 
306  return d->m_sensorGroup->segmentNameForRegEx(item->segment);
307  // Only leaf nodes are valid sensors
308  } else if (role == SensorId) {
309  if (rowCount(index)) {
310  return QString();
311  } else {
312  return d->sensorId(index);
313  }
314  }
315 
316  return QVariant();
317 }
318 
319 QMimeData *SensorTreeModel::mimeData(const QModelIndexList &indexes) const
320 {
321  QMimeData *mimeData = new QMimeData();
322 
323  if (indexes.count() != 1) {
324  return mimeData;
325  }
326 
327  const QModelIndex &index = indexes.at(0);
328 
329  if (!checkIndex(index, CheckIndexOption::IndexIsValid)) {
330  return mimeData;
331  }
332 
333  if (rowCount(index)) {
334  return mimeData;
335  }
336 
337  mimeData->setData(QStringLiteral("application/x-ksysguard"), d->sensorId(index).toUtf8());
338 
339  return mimeData;
340 }
341 
342 Qt::ItemFlags SensorTreeModel::flags(const QModelIndex &index) const
343 {
344  if (!checkIndex(index, CheckIndexOption::IndexIsValid)) {
345  return Qt::NoItemFlags;
346  }
347 
348  if (!rowCount(index)) {
350  }
351 
352  return Qt::ItemIsEnabled;
353 }
354 
355 int SensorTreeModel::rowCount(const QModelIndex &parent) const
356 {
357  if (parent.isValid()) {
358  if (!checkIndex(parent, CheckIndexOption::IndexIsValid)) {
359  return 0;
360  }
361 
362  const SensorTreeItem *item = static_cast<SensorTreeItem *>(parent.internalPointer());
363  return item->children.count();
364  }
365 
366  return d->rootItem->children.count();
367 }
368 
369 int SensorTreeModel::columnCount(const QModelIndex &parent) const
370 {
371  Q_UNUSED(parent)
372 
373  return 1;
374 }
375 
376 QModelIndex SensorTreeModel::index(int row, int column, const QModelIndex &parent) const
377 {
378  SensorTreeItem *parentItem = d->rootItem;
379 
380  if (parent.isValid()) {
381  if (parent.model() != this) {
382  return QModelIndex();
383  }
384 
385  parentItem = static_cast<SensorTreeItem *>(parent.internalPointer());
386  }
387 
388  if (row < 0 || row >= parentItem->children.count()) {
389  return QModelIndex();
390  }
391 
392  if (column < 0) {
393  return QModelIndex();
394  }
395 
396  return createIndex(row, column, parentItem->itemAt(row));
397 }
398 
400 {
401  if (!checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::DoNotUseParent)) {
402  return QModelIndex();
403  }
404 
405  if (index.column() > 0) {
406  return QModelIndex();
407  }
408 
409  const SensorTreeItem *item = static_cast<SensorTreeItem *>(index.internalPointer());
410  SensorTreeItem *parentItem = item->parent;
411 
412  if (parentItem == d->rootItem) {
413  return QModelIndex();
414  }
415 
416  return createIndex(parentItem->parent->indexOf(parentItem->segment), 0, parentItem);
417 }
418 
419 void SensorTreeModel::init()
420 {
421  auto query = new SensorQuery{QString(), this};
422  connect(query, &SensorQuery::finished, [query, this]() {
423  query->deleteLater();
424  const auto result = query->result();
425  beginResetModel();
426  for (auto pair : result) {
427  d->addSensor(pair.first, pair.second);
428  }
429  endResetModel();
430  });
431  query->execute();
432 }
433 
434 void KSysGuard::SensorTreeModel::onSensorAdded(const QString &sensor)
435 {
436  SensorDaemonInterface::instance()->requestMetaData(sensor);
437 }
438 
439 void KSysGuard::SensorTreeModel::onSensorRemoved(const QString &sensor)
440 {
441  d->removeSensor(sensor);
442 }
443 
444 void KSysGuard::SensorTreeModel::onMetaDataChanged(const QString &sensorId, const SensorInfo &info)
445 {
446  auto item = d->find(sensorId);
447  if (!item) {
448  d->addSensor(sensorId, info);
449  } else {
450  d->sensorInfos[item] = info;
451 
452  auto parentItem = item->parent;
453  if (!parentItem) {
454  return;
455  }
456 
457  auto parentIndex = QModelIndex{};
458  if (parentItem != d->rootItem) {
459  parentIndex = createIndex(parentItem->parent->indexOf(parentItem->segment), 0, parentItem);
460  }
461 
462  auto itemIndex = index(parentItem->indexOf(item->segment), 0, parentIndex);
463  Q_EMIT dataChanged(itemIndex, itemIndex);
464  }
465 }
QHash::iterator insert(const Key &key, const T &value)
std::optional< QSqlQuery > query(const QString &queryStatement)
int keyCount() const const
virtual QHash< int, QByteArray > roleNames() const const
const T & at(int i) const const
static QString symbol(Unit unit)
Returns a symbol that corresponds to the given unit.
Definition: Formatter.cpp:328
virtual const QMetaObject * metaObject() const const
StandardShortcut find(const QKeySequence &keySeq)
QString join(const QString &separator) const const
QString & remove(int position, int n)
T value(int i) const const
A model representing a tree of sensors that are available from the daemon.
bool isValid() const const
int count(const T &value) const const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
DisplayRole
int value(int index) const const
bool isEmpty() const const
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
void * internalPointer() const const
QModelIndex parent() const const
An object to query the daemon for a list of sensors and their metadata.
Definition: SensorQuery.h:38
QMetaEnum enumerator(int index) const const
QCA_EXPORT void init()
QString i18n(const char *text, const TYPE &arg...)
int column() const const
void prepend(const T &value)
Orientation
void setData(const QString &mimeType, const QByteArray &data)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
const char * key(int index) const const
Q_EMITQ_EMIT
typedef ItemFlags
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Thu Mar 4 2021 23:09:25 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.