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;
30 bool operator()(
const QString &first,
const QString &second)
const
33 if (first == QLatin1String(
"all") && first != second) {
37 if (second == QLatin1String(
"all")) {
42 collator = QCollator();
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();
90 Private(SensorTreeModel *qq)
91 : rootItem(new SensorTreeItem)
94 m_sensorGroup =
new SensorGroup;
102 SensorTreeItem *rootItem;
103 QHash<SensorTreeItem *, SensorInfo> sensorInfos;
105 void addSensor(
const QString &sensorId,
const SensorInfo &info);
106 void removeSensor(
const QString &sensorId);
108 QString sensorId(
const QModelIndex &index);
110 SensorTreeItem *
find(
const QString &sensorId);
112 SensorGroup *m_sensorGroup;
114 QHash<QString, int> m_groupMatches;
120void SensorTreeModel::Private::addSensor(
const QString &sensorId,
const SensorInfo &info)
122 const QStringList &segments = sensorId.split(QLatin1Char(
'/'));
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] == 1) {
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;
160 const QModelIndex &parentIndex = (item == rootItem) ? QModelIndex() : q->createIndex(item->parent->indexOf(item->segment), 0, item);
162 auto index = std::distance(item->children.begin(), item->children.upper_bound(segment));
164 q->beginInsertRows(parentIndex, index, index);
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);
200 const QModelIndex &parentIndex = (parent == rootItem) ? QModelIndex() : q->createIndex(parent->parent->indexOf(parent->segment), 0, parent);
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);
213 while (!parent->children.size()) {
215 parent = parent->parent;
225QString SensorTreeModel::Private::sensorId(
const QModelIndex &index)
227 QStringList segments;
229 SensorTreeItem *item =
static_cast<SensorTreeItem *
>(index.internalPointer());
231 segments << item->segment;
233 while (item->parent && item->parent != rootItem) {
235 segments.
prepend(item->segment);
238 return segments.
join(QLatin1Char(
'/'));
241SensorTreeItem *KSysGuard::SensorTreeModel::Private::find(
const QString &sensorId)
243 auto item = rootItem;
244 const auto segments = sensorId.
split(QLatin1Char(
'/'));
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();
255SensorTreeModel::SensorTreeModel(
QObject *parent)
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()
269QHash<int, QByteArray> SensorTreeModel::roleNames()
const
275 for (
int i = 0; i < e.
keyCount(); ++i) {
282QVariant SensorTreeModel::headerData(
int section,
Qt::Orientation,
int role)
const
289 return i18n(
"Sensor Browser");
295QStringList SensorTreeModel::mimeTypes()
const
297 return QStringList() << QStringLiteral(
"application/x-ksysguard");
300QVariant SensorTreeModel::data(
const QModelIndex &index,
int role)
const
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 QStringList segments;
321 while (item != d->rootItem) {
322 segments.
prepend(item->segment);
326 return d->m_sensorGroup->segmentNameForRegEx(segments.
join(u
'/'));
328 }
else if (role == SensorId) {
329 if (rowCount(index)) {
332 return d->sensorId(index);
339QMimeData *SensorTreeModel::mimeData(
const QModelIndexList &indexes)
const
341 QMimeData *mimeData =
new QMimeData();
343 if (indexes.count() != 1) {
347 const QModelIndex &index = indexes.at(0);
349 if (!
checkIndex(index, CheckIndexOption::IndexIsValid)) {
353 if (rowCount(index)) {
357 mimeData->setData(QStringLiteral(
"application/x-ksysguard"), d->sensorId(index).toUtf8());
362Qt::ItemFlags SensorTreeModel::flags(
const QModelIndex &index)
const
364 if (!
checkIndex(index, CheckIndexOption::IndexIsValid)) {
368 if (!rowCount(index)) {
375int SensorTreeModel::rowCount(
const QModelIndex &parent)
const
377 if (parent.isValid()) {
378 if (!
checkIndex(parent, CheckIndexOption::IndexIsValid)) {
382 const SensorTreeItem *item =
static_cast<SensorTreeItem *
>(parent.internalPointer());
383 return item->children.size();
386 return d->rootItem->children.size();
389int SensorTreeModel::columnCount(
const QModelIndex &parent)
const
396QModelIndex SensorTreeModel::index(
int row,
int column,
const QModelIndex &parent)
const
398 SensorTreeItem *parentItem = d->rootItem;
400 if (parent.isValid()) {
401 if (parent.model() !=
this) {
402 return QModelIndex();
405 parentItem =
static_cast<SensorTreeItem *
>(parent.internalPointer());
408 if (row < 0 || row >=
int(parentItem->children.size())) {
409 return QModelIndex();
413 return QModelIndex();
416 return createIndex(row, column, parentItem->itemAt(row));
421 if (!
checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::DoNotUseParent)) {
422 return QModelIndex();
425 if (index.column() > 0) {
426 return QModelIndex();
429 const SensorTreeItem *item =
static_cast<SensorTreeItem *
>(index.internalPointer());
430 SensorTreeItem *parentItem = item->parent;
432 if (parentItem == d->rootItem) {
433 return QModelIndex();
436 return createIndex(parentItem->parent->indexOf(parentItem->segment), 0, parentItem);
439void SensorTreeModel::init()
441 auto query =
new SensorQuery{QString(),
this};
442 connect(query, &SensorQuery::finished, [query,
this]() {
443 query->deleteLater();
444 const auto result =
query->result();
446 for (
auto pair : result) {
447 d->addSensor(pair.first, pair.second);
454void KSysGuard::SensorTreeModel::onSensorAdded(
const QString &sensor)
456 SensorDaemonInterface::instance()->requestMetaData(sensor);
459void KSysGuard::SensorTreeModel::onSensorRemoved(
const QString &sensor)
461 d->removeSensor(sensor);
464void KSysGuard::SensorTreeModel::onMetaDataChanged(
const QString &sensorId,
const SensorInfo &info)
466 auto item = d->find(sensorId);
468 d->addSensor(sensorId, info);
470 d->sensorInfos[item] = info;
472 auto parentItem = item->parent;
477 auto parentIndex = QModelIndex{};
478 if (parentItem != d->rootItem) {
479 parentIndex = createIndex(parentItem->parent->indexOf(parentItem->segment), 0, parentItem);
482 auto itemIndex = index(parentItem->indexOf(item->segment), 0, parentIndex);
483 Q_EMIT dataChanged(itemIndex, itemIndex);
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)
void init(KXmlGuiWindow *window, KGameDifficulty *difficulty=nullptr)
QAbstractItemModel(QObject *parent)
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
iterator insert(const Key &key, const T &value)
const_reference at(qsizetype i) const const
qsizetype count() const const
void prepend(parameter_type value)
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)