8#include "SensorTreeModel.h"
12#include <KLocalizedString>
17#include <QRegularExpression>
19#include "formatter/Formatter.h"
20#include "systemstats/SensorInfo.h"
23#include "SensorDaemonInterface_p.h"
24#include "SensorGroup_p.h"
25#include "SensorQuery.h"
27using namespace KSysGuard;
43 collator->setNumericMode(
true);
47 return collator->compare(first, second) < 0;
52 thread_local static std::optional<QCollator> collator;
55thread_local std::optional<QCollator> Compare::collator = std::nullopt;
57struct Q_DECL_HIDDEN SensorTreeItem {
58 SensorTreeItem *parent =
nullptr;
60 std::map<QString, std::unique_ptr<SensorTreeItem>, Compare> children;
62 inline int indexOf(
const QString &segment)
const
64 auto itr = std::find_if(children.cbegin(), children.cend(), [segment](
const auto &item) {
65 return item.second->segment == segment;
68 if (itr != children.cend()) {
69 return std::distance(children.cbegin(), itr);
75 inline SensorTreeItem *itemAt(std::size_t index)
const
77 if (index >= children.size()) {
81 auto itr = children.cbegin();
82 std::advance(itr, index);
83 return itr->second.get();
91 : rootItem(new SensorTreeItem)
94 m_sensorGroup =
new SensorGroup;
102 SensorTreeItem *rootItem;
105 void addSensor(
const QString &sensorId,
const SensorInfo &info);
106 void removeSensor(
const QString &sensorId);
112 SensorGroup *m_sensorGroup;
120void SensorTreeModel::Private::addSensor(
const QString &sensorId,
const SensorInfo &info)
124 if (!segments.
count() || segments.
at(0).isEmpty()) {
125 qDebug() <<
"Rejecting sensor" << sensorId <<
"- sensor id is not well-formed.";
129 QString sensorIdExpr = m_sensorGroup->groupRegexForId(sensorId);
132 if (m_groupMatches.
contains(sensorIdExpr)) {
133 m_groupMatches[sensorIdExpr]++;
135 m_groupMatches[sensorIdExpr] = 1;
138 if (m_groupMatches[sensorIdExpr] == 2) {
140 newInfo.name = m_sensorGroup->sensorNameForRegEx(sensorIdExpr);
141 newInfo.description = info.description;
142 newInfo.variantType = info.variantType;
143 newInfo.unit = info.unit;
144 newInfo.min = info.min;
145 newInfo.max = info.max;
147 addSensor(sensorIdExpr, newInfo);
151 SensorTreeItem *item = rootItem;
152 for (
auto segment : segments) {
153 if (
auto itr = item->children.find(segment); itr != item->children.end() && itr->second) {
154 item = itr->second.get();
156 auto newItem = std::make_unique<SensorTreeItem>();
157 newItem->parent = item;
158 newItem->segment = segment;
162 auto index = std::distance(item->children.begin(), item->children.upper_bound(segment));
165 item->children[segment] = std::move(newItem);
168 item = item->children[segment].get();
172 sensorInfos[item] = info;
175void SensorTreeModel::Private::removeSensor(
const QString &sensorId)
177 QString sensorIdExpr = m_sensorGroup->groupRegexForId(sensorId);
179 if (m_groupMatches[sensorIdExpr] == 1) {
180 m_groupMatches.remove(sensorIdExpr);
181 removeSensor(sensorIdExpr);
182 }
else if (m_groupMatches.contains(sensorIdExpr)) {
183 m_groupMatches[sensorIdExpr]--;
187 SensorTreeItem *item =
find(sensorId);
192 SensorTreeItem *
parent = item->parent;
197 auto remove = [
this](SensorTreeItem *item, SensorTreeItem *
parent) {
198 const int index = item->parent->indexOf(item->segment);
201 q->beginRemoveRows(parentIndex, index, index);
203 auto itr = item->parent->children.find(item->segment);
204 item->parent->children.erase(itr);
208 sensorInfos.remove(item);
229 SensorTreeItem *item =
static_cast<SensorTreeItem *
>(index.
internalPointer());
231 segments << item->segment;
233 while (item->parent && item->parent != rootItem) {
235 segments.
prepend(item->segment);
241SensorTreeItem *KSysGuard::SensorTreeModel::Private::find(
const QString &sensorId)
243 auto item = rootItem;
245 for (
const QString &segment : segments) {
246 if (
auto itr = item->children.find(segment); itr != item->children.end() && itr->second) {
247 item = itr->second.get();
257 , d(new Private(this))
259 connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::sensorAdded,
this, &SensorTreeModel::onSensorAdded);
260 connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::sensorRemoved,
this, &SensorTreeModel::onSensorRemoved);
261 connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::metaDataChanged,
this, &SensorTreeModel::onMetaDataChanged);
265SensorTreeModel::~SensorTreeModel()
275 for (
int i = 0; i < e.
keyCount(); ++i) {
289 return i18n(
"Sensor Browser");
297 return QStringList() << QStringLiteral(
"application/x-ksysguard");
302 if (!
checkIndex(index, CheckIndexOption::IndexIsValid)) {
307 SensorTreeItem *item =
static_cast<SensorTreeItem *
>(index.
internalPointer());
309 if (d->sensorInfos.contains(item)) {
310 auto info = d->sensorInfos.value(item);
314 return i18nc(
"Name (unit)",
"%1 (%2)", info.name, unit);
320 return d->m_sensorGroup->segmentNameForRegEx(item->segment);
322 }
else if (role == SensorId) {
323 if (rowCount(index)) {
326 return d->sensorId(index);
333QMimeData *SensorTreeModel::mimeData(
const QModelIndexList &indexes)
const
337 if (indexes.count() != 1) {
343 if (!
checkIndex(index, CheckIndexOption::IndexIsValid)) {
347 if (rowCount(index)) {
351 mimeData->
setData(QStringLiteral(
"application/x-ksysguard"), d->sensorId(index).toUtf8());
358 if (!
checkIndex(index, CheckIndexOption::IndexIsValid)) {
362 if (!rowCount(index)) {
369int SensorTreeModel::rowCount(
const QModelIndex &parent)
const
376 const SensorTreeItem *item =
static_cast<SensorTreeItem *
>(
parent.internalPointer());
377 return item->children.size();
380 return d->rootItem->children.size();
383int SensorTreeModel::columnCount(
const QModelIndex &parent)
const
392 SensorTreeItem *parentItem = d->rootItem;
395 if (
parent.model() !=
this) {
399 parentItem =
static_cast<SensorTreeItem *
>(
parent.internalPointer());
402 if (row < 0 || row >=
int(parentItem->children.size())) {
410 return createIndex(row, column, parentItem->itemAt(row));
415 if (!
checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::DoNotUseParent)) {
423 const SensorTreeItem *item =
static_cast<SensorTreeItem *
>(index.
internalPointer());
424 SensorTreeItem *parentItem = item->parent;
426 if (parentItem == d->rootItem) {
430 return createIndex(parentItem->parent->indexOf(parentItem->segment), 0, parentItem);
433void SensorTreeModel::init()
436 connect(query, &SensorQuery::finished, [query,
this]() {
437 query->deleteLater();
438 const auto result =
query->result();
440 for (
auto pair : result) {
441 d->addSensor(pair.first, pair.second);
448void KSysGuard::SensorTreeModel::onSensorAdded(
const QString &sensor)
450 SensorDaemonInterface::instance()->requestMetaData(sensor);
453void KSysGuard::SensorTreeModel::onSensorRemoved(
const QString &sensor)
455 d->removeSensor(sensor);
458void KSysGuard::SensorTreeModel::onMetaDataChanged(
const QString &sensorId,
const SensorInfo &info)
460 auto item = d->find(sensorId);
462 d->addSensor(sensorId, info);
464 d->sensorInfos[item] = info;
466 auto parentItem = item->parent;
472 if (parentItem != d->rootItem) {
473 parentIndex = createIndex(parentItem->parent->indexOf(parentItem->segment), 0, parentItem);
476 auto itemIndex = index(parentItem->indexOf(item->segment), 0, parentIndex);
477 Q_EMIT dataChanged(itemIndex, itemIndex);
An object to query the daemon for a list of sensors and their metadata.
A model representing a tree of sensors that are available from the daemon.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
const QList< QKeySequence > & find()
void beginInsertRows(const QModelIndex &parent, int first, int last)
bool checkIndex(const QModelIndex &index, CheckIndexOptions options) const const
QModelIndex createIndex(int row, int column, const void *ptr) const const
virtual QHash< int, QByteArray > roleNames() const const
bool contains(const Key &key) const const
iterator insert(const Key &key, const T &value)
const_reference at(qsizetype i) const const
qsizetype count() const const
void prepend(parameter_type value)
void setData(const QString &mimeType, const QByteArray &data)
void * internalPointer() const const
const QObjectList & children() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
bool isEmpty() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString join(QChar separator) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)