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] == 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;
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 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
335 QMimeData *mimeData =
new QMimeData();
337 if (indexes.count() != 1) {
341 const QModelIndex &index = indexes.at(0);
343 if (!
checkIndex(index, CheckIndexOption::IndexIsValid)) {
347 if (rowCount(index)) {
351 mimeData->setData(QStringLiteral(
"application/x-ksysguard"), d->sensorId(index).toUtf8());
356Qt::ItemFlags SensorTreeModel::flags(
const QModelIndex &index)
const
358 if (!
checkIndex(index, CheckIndexOption::IndexIsValid)) {
362 if (!rowCount(index)) {
369int SensorTreeModel::rowCount(
const QModelIndex &parent)
const
371 if (parent.isValid()) {
372 if (!
checkIndex(parent, CheckIndexOption::IndexIsValid)) {
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
390QModelIndex SensorTreeModel::index(
int row,
int column,
const QModelIndex &parent)
const
392 SensorTreeItem *parentItem = d->rootItem;
394 if (parent.isValid()) {
395 if (parent.model() !=
this) {
396 return QModelIndex();
399 parentItem =
static_cast<SensorTreeItem *
>(parent.internalPointer());
402 if (row < 0 || row >=
int(parentItem->children.size())) {
403 return QModelIndex();
407 return QModelIndex();
410 return createIndex(row, column, parentItem->itemAt(row));
415 if (!
checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::DoNotUseParent)) {
416 return QModelIndex();
419 if (index.column() > 0) {
420 return QModelIndex();
423 const SensorTreeItem *item =
static_cast<SensorTreeItem *
>(index.internalPointer());
424 SensorTreeItem *parentItem = item->parent;
426 if (parentItem == d->rootItem) {
427 return QModelIndex();
430 return createIndex(parentItem->parent->indexOf(parentItem->segment), 0, parentItem);
433void SensorTreeModel::init()
435 auto query =
new SensorQuery{QString(),
this};
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;
471 auto parentIndex = QModelIndex{};
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);
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)