File indexing completed on 2024-04-28 05:31:45
0001 /* 0002 SPDX-FileCopyrightText: 2019 Eike Hein <hein@kde.org> 0003 SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "SensorTreeModel.h" 0009 0010 #include <optional> 0011 0012 #include <KLocalizedString> 0013 #include <QCollator> 0014 #include <QDebug> 0015 #include <QMetaEnum> 0016 #include <QMimeData> 0017 #include <QRegularExpression> 0018 0019 #include "formatter/Formatter.h" 0020 #include "systemstats/SensorInfo.h" 0021 0022 #include "Sensor.h" 0023 #include "SensorDaemonInterface_p.h" 0024 #include "SensorGroup_p.h" 0025 #include "SensorQuery.h" 0026 0027 using namespace KSysGuard; 0028 0029 struct Compare { 0030 bool operator()(const QString &first, const QString &second) const 0031 { 0032 // Place "All" object at the top. 0033 if (first == QLatin1String("all") && first != second) { 0034 return true; 0035 } 0036 0037 if (second == QLatin1String("all")) { 0038 return false; 0039 } 0040 0041 if (!collator) { 0042 collator = QCollator(); 0043 collator->setNumericMode(true); 0044 collator->setCaseSensitivity(Qt::CaseInsensitive); 0045 } 0046 0047 return collator->compare(first, second) < 0; 0048 } 0049 0050 // This uses thread-local storage because QCollator may not be thread safe. 0051 // We store it in an optional to make sure we can initialize it above. 0052 thread_local static std::optional<QCollator> collator; 0053 }; 0054 0055 thread_local std::optional<QCollator> Compare::collator = std::nullopt; 0056 0057 struct Q_DECL_HIDDEN SensorTreeItem { 0058 SensorTreeItem *parent = nullptr; 0059 QString segment; 0060 std::map<QString, std::unique_ptr<SensorTreeItem>, Compare> children; 0061 0062 inline int indexOf(const QString &segment) const 0063 { 0064 auto itr = std::find_if(children.cbegin(), children.cend(), [segment](const auto &item) { 0065 return item.second->segment == segment; 0066 }); 0067 0068 if (itr != children.cend()) { 0069 return std::distance(children.cbegin(), itr); 0070 } 0071 0072 return -1; 0073 } 0074 0075 inline SensorTreeItem *itemAt(std::size_t index) const 0076 { 0077 if (index >= children.size()) { 0078 return nullptr; 0079 } 0080 0081 auto itr = children.cbegin(); 0082 std::advance(itr, index); 0083 return itr->second.get(); 0084 } 0085 }; 0086 0087 class Q_DECL_HIDDEN SensorTreeModel::Private 0088 { 0089 public: 0090 Private(SensorTreeModel *qq) 0091 : rootItem(new SensorTreeItem) 0092 , q(qq) 0093 { 0094 m_sensorGroup = new SensorGroup; 0095 } 0096 ~Private() 0097 { 0098 delete rootItem; 0099 delete m_sensorGroup; 0100 } 0101 0102 SensorTreeItem *rootItem; 0103 QHash<SensorTreeItem *, SensorInfo> sensorInfos; 0104 0105 void addSensor(const QString &sensorId, const SensorInfo &info); 0106 void removeSensor(const QString &sensorId); 0107 0108 QString sensorId(const QModelIndex &index); 0109 0110 SensorTreeItem *find(const QString &sensorId); 0111 0112 SensorGroup *m_sensorGroup; 0113 0114 QHash<QString, int> m_groupMatches; 0115 0116 private: 0117 SensorTreeModel *q; 0118 }; 0119 0120 void SensorTreeModel::Private::addSensor(const QString &sensorId, const SensorInfo &info) 0121 { 0122 const QStringList &segments = sensorId.split(QLatin1Char('/')); 0123 0124 if (!segments.count() || segments.at(0).isEmpty()) { 0125 qDebug() << "Rejecting sensor" << sensorId << "- sensor id is not well-formed."; 0126 return; 0127 } 0128 0129 QString sensorIdExpr = m_sensorGroup->groupRegexForId(sensorId); 0130 0131 if (!sensorIdExpr.isEmpty()) { 0132 if (m_groupMatches.contains(sensorIdExpr)) { 0133 m_groupMatches[sensorIdExpr]++; 0134 } else { 0135 m_groupMatches[sensorIdExpr] = 1; 0136 } 0137 0138 if (m_groupMatches[sensorIdExpr] == 2) { 0139 SensorInfo newInfo; 0140 newInfo.name = m_sensorGroup->sensorNameForRegEx(sensorIdExpr); 0141 newInfo.description = info.description; 0142 newInfo.variantType = info.variantType; 0143 newInfo.unit = info.unit; 0144 newInfo.min = info.min; 0145 newInfo.max = info.max; 0146 0147 addSensor(sensorIdExpr, newInfo); 0148 } 0149 } 0150 0151 SensorTreeItem *item = rootItem; 0152 for (auto segment : segments) { 0153 if (auto itr = item->children.find(segment); itr != item->children.end() && itr->second) { 0154 item = itr->second.get(); 0155 } else { 0156 auto newItem = std::make_unique<SensorTreeItem>(); 0157 newItem->parent = item; 0158 newItem->segment = segment; 0159 0160 const QModelIndex &parentIndex = (item == rootItem) ? QModelIndex() : q->createIndex(item->parent->indexOf(item->segment), 0, item); 0161 0162 auto index = std::distance(item->children.begin(), item->children.upper_bound(segment)); 0163 0164 q->beginInsertRows(parentIndex, index, index); 0165 item->children[segment] = std::move(newItem); 0166 q->endInsertRows(); 0167 0168 item = item->children[segment].get(); 0169 } 0170 } 0171 0172 sensorInfos[item] = info; 0173 } 0174 0175 void SensorTreeModel::Private::removeSensor(const QString &sensorId) 0176 { 0177 QString sensorIdExpr = m_sensorGroup->groupRegexForId(sensorId); 0178 if (!sensorIdExpr.isEmpty()) { 0179 if (m_groupMatches[sensorIdExpr] == 1) { 0180 m_groupMatches.remove(sensorIdExpr); 0181 removeSensor(sensorIdExpr); 0182 } else if (m_groupMatches.contains(sensorIdExpr)) { 0183 m_groupMatches[sensorIdExpr]--; 0184 } 0185 } 0186 0187 SensorTreeItem *item = find(sensorId); 0188 if (!item) { 0189 return; 0190 } 0191 0192 SensorTreeItem *parent = item->parent; 0193 if (!parent) { 0194 return; 0195 } 0196 0197 auto remove = [this](SensorTreeItem *item, SensorTreeItem *parent) { 0198 const int index = item->parent->indexOf(item->segment); 0199 0200 const QModelIndex &parentIndex = (parent == rootItem) ? QModelIndex() : q->createIndex(parent->parent->indexOf(parent->segment), 0, parent); 0201 q->beginRemoveRows(parentIndex, index, index); 0202 0203 auto itr = item->parent->children.find(item->segment); 0204 item->parent->children.erase(itr); 0205 0206 q->endRemoveRows(); 0207 0208 sensorInfos.remove(item); 0209 }; 0210 0211 remove(item, parent); 0212 0213 while (!parent->children.size()) { 0214 item = parent; 0215 parent = parent->parent; 0216 0217 if (!parent) { 0218 break; 0219 } 0220 0221 remove(item, parent); 0222 } 0223 } 0224 0225 QString SensorTreeModel::Private::sensorId(const QModelIndex &index) 0226 { 0227 QStringList segments; 0228 0229 SensorTreeItem *item = static_cast<SensorTreeItem *>(index.internalPointer()); 0230 0231 segments << item->segment; 0232 0233 while (item->parent && item->parent != rootItem) { 0234 item = item->parent; 0235 segments.prepend(item->segment); 0236 } 0237 0238 return segments.join(QLatin1Char('/')); 0239 } 0240 0241 SensorTreeItem *KSysGuard::SensorTreeModel::Private::find(const QString &sensorId) 0242 { 0243 auto item = rootItem; 0244 const auto segments = sensorId.split(QLatin1Char('/')); 0245 for (const QString &segment : segments) { 0246 if (auto itr = item->children.find(segment); itr != item->children.end() && itr->second) { 0247 item = itr->second.get(); 0248 } else { 0249 return nullptr; 0250 } 0251 } 0252 return item; 0253 } 0254 0255 SensorTreeModel::SensorTreeModel(QObject *parent) 0256 : QAbstractItemModel(parent) 0257 , d(new Private(this)) 0258 { 0259 connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::sensorAdded, this, &SensorTreeModel::onSensorAdded); 0260 connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::sensorRemoved, this, &SensorTreeModel::onSensorRemoved); 0261 connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::metaDataChanged, this, &SensorTreeModel::onMetaDataChanged); 0262 init(); 0263 } 0264 0265 SensorTreeModel::~SensorTreeModel() 0266 { 0267 } 0268 0269 QHash<int, QByteArray> SensorTreeModel::roleNames() const 0270 { 0271 QHash<int, QByteArray> roles = QAbstractItemModel::roleNames(); 0272 0273 QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("AdditionalRoles")); 0274 0275 for (int i = 0; i < e.keyCount(); ++i) { 0276 roles.insert(e.value(i), e.key(i)); 0277 } 0278 0279 return roles; 0280 } 0281 0282 QVariant SensorTreeModel::headerData(int section, Qt::Orientation, int role) const 0283 { 0284 if (role != Qt::DisplayRole) { 0285 return QVariant(); 0286 } 0287 0288 if (section == 0) { 0289 return i18n("Sensor Browser"); 0290 } 0291 0292 return QVariant(); 0293 } 0294 0295 QStringList SensorTreeModel::mimeTypes() const 0296 { 0297 return QStringList() << QStringLiteral("application/x-ksysguard"); 0298 } 0299 0300 QVariant SensorTreeModel::data(const QModelIndex &index, int role) const 0301 { 0302 if (!checkIndex(index, CheckIndexOption::IndexIsValid)) { 0303 return QVariant(); 0304 } 0305 0306 if (role == Qt::DisplayRole) { 0307 SensorTreeItem *item = static_cast<SensorTreeItem *>(index.internalPointer()); 0308 0309 if (d->sensorInfos.contains(item)) { 0310 auto info = d->sensorInfos.value(item); 0311 const QString &unit = Formatter::symbol(info.unit); 0312 0313 if (!unit.isEmpty()) { 0314 return i18nc("Name (unit)", "%1 (%2)", info.name, unit); 0315 } 0316 0317 return info.name; 0318 } 0319 0320 return d->m_sensorGroup->segmentNameForRegEx(item->segment); 0321 // Only leaf nodes are valid sensors 0322 } else if (role == SensorId) { 0323 if (rowCount(index)) { 0324 return QString(); 0325 } else { 0326 return d->sensorId(index); 0327 } 0328 } 0329 0330 return QVariant(); 0331 } 0332 0333 QMimeData *SensorTreeModel::mimeData(const QModelIndexList &indexes) const 0334 { 0335 QMimeData *mimeData = new QMimeData(); 0336 0337 if (indexes.count() != 1) { 0338 return mimeData; 0339 } 0340 0341 const QModelIndex &index = indexes.at(0); 0342 0343 if (!checkIndex(index, CheckIndexOption::IndexIsValid)) { 0344 return mimeData; 0345 } 0346 0347 if (rowCount(index)) { 0348 return mimeData; 0349 } 0350 0351 mimeData->setData(QStringLiteral("application/x-ksysguard"), d->sensorId(index).toUtf8()); 0352 0353 return mimeData; 0354 } 0355 0356 Qt::ItemFlags SensorTreeModel::flags(const QModelIndex &index) const 0357 { 0358 if (!checkIndex(index, CheckIndexOption::IndexIsValid)) { 0359 return Qt::NoItemFlags; 0360 } 0361 0362 if (!rowCount(index)) { 0363 return Qt::ItemIsDragEnabled | Qt::ItemIsSelectable | Qt::ItemIsEnabled; 0364 } 0365 0366 return Qt::ItemIsEnabled; 0367 } 0368 0369 int SensorTreeModel::rowCount(const QModelIndex &parent) const 0370 { 0371 if (parent.isValid()) { 0372 if (!checkIndex(parent, CheckIndexOption::IndexIsValid)) { 0373 return 0; 0374 } 0375 0376 const SensorTreeItem *item = static_cast<SensorTreeItem *>(parent.internalPointer()); 0377 return item->children.size(); 0378 } 0379 0380 return d->rootItem->children.size(); 0381 } 0382 0383 int SensorTreeModel::columnCount(const QModelIndex &parent) const 0384 { 0385 Q_UNUSED(parent) 0386 0387 return 1; 0388 } 0389 0390 QModelIndex SensorTreeModel::index(int row, int column, const QModelIndex &parent) const 0391 { 0392 SensorTreeItem *parentItem = d->rootItem; 0393 0394 if (parent.isValid()) { 0395 if (parent.model() != this) { 0396 return QModelIndex(); 0397 } 0398 0399 parentItem = static_cast<SensorTreeItem *>(parent.internalPointer()); 0400 } 0401 0402 if (row < 0 || row >= int(parentItem->children.size())) { 0403 return QModelIndex(); 0404 } 0405 0406 if (column < 0) { 0407 return QModelIndex(); 0408 } 0409 0410 return createIndex(row, column, parentItem->itemAt(row)); 0411 } 0412 0413 QModelIndex SensorTreeModel::parent(const QModelIndex &index) const 0414 { 0415 if (!checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::DoNotUseParent)) { 0416 return QModelIndex(); 0417 } 0418 0419 if (index.column() > 0) { 0420 return QModelIndex(); 0421 } 0422 0423 const SensorTreeItem *item = static_cast<SensorTreeItem *>(index.internalPointer()); 0424 SensorTreeItem *parentItem = item->parent; 0425 0426 if (parentItem == d->rootItem) { 0427 return QModelIndex(); 0428 } 0429 0430 return createIndex(parentItem->parent->indexOf(parentItem->segment), 0, parentItem); 0431 } 0432 0433 void SensorTreeModel::init() 0434 { 0435 auto query = new SensorQuery{QString(), this}; 0436 connect(query, &SensorQuery::finished, [query, this]() { 0437 query->deleteLater(); 0438 const auto result = query->result(); 0439 beginResetModel(); 0440 for (auto pair : result) { 0441 d->addSensor(pair.first, pair.second); 0442 } 0443 endResetModel(); 0444 }); 0445 query->execute(); 0446 } 0447 0448 void KSysGuard::SensorTreeModel::onSensorAdded(const QString &sensor) 0449 { 0450 SensorDaemonInterface::instance()->requestMetaData(sensor); 0451 } 0452 0453 void KSysGuard::SensorTreeModel::onSensorRemoved(const QString &sensor) 0454 { 0455 d->removeSensor(sensor); 0456 } 0457 0458 void KSysGuard::SensorTreeModel::onMetaDataChanged(const QString &sensorId, const SensorInfo &info) 0459 { 0460 auto item = d->find(sensorId); 0461 if (!item) { 0462 d->addSensor(sensorId, info); 0463 } else { 0464 d->sensorInfos[item] = info; 0465 0466 auto parentItem = item->parent; 0467 if (!parentItem) { 0468 return; 0469 } 0470 0471 auto parentIndex = QModelIndex{}; 0472 if (parentItem != d->rootItem) { 0473 parentIndex = createIndex(parentItem->parent->indexOf(parentItem->segment), 0, parentItem); 0474 } 0475 0476 auto itemIndex = index(parentItem->indexOf(item->segment), 0, parentIndex); 0477 Q_EMIT dataChanged(itemIndex, itemIndex); 0478 } 0479 }