Warning, file /system/kjournald/lib/filtercriteriamodel.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-License-Identifier: LGPL-2.1-or-later OR MIT 0003 SPDX-FileCopyrightText: 2021 Andreas Cord-Landwehr <cordlandwehr@kde.org> 0004 */ 0005 0006 #include "filtercriteriamodel.h" 0007 #include "filtercriteriamodel_p.h" 0008 #include "kjournald_export.h" 0009 #include "kjournaldlib_log_general.h" 0010 #include <KLocalizedString> 0011 #include <QDebug> 0012 #include <QDir> 0013 #include <QString> 0014 #include <memory> 0015 0016 constexpr quint8 sDefaultPriorityLevel{5}; 0017 0018 QString FilterCriteriaModelPrivate::mapPriorityToString(int priority) 0019 { 0020 switch (priority) { 0021 case 0: 0022 return i18nc("Radio box option, log priority value", "Emergency"); 0023 case 1: 0024 return i18nc("Radio box option, log priority value", "Alert"); 0025 case 2: 0026 return i18nc("Radio box option, log priority value", "Critical"); 0027 case 3: 0028 return i18nc("Radio box option, log priority value", "Error"); 0029 case 4: 0030 return i18nc("Radio box option, log priority value", "Warning"); 0031 case 5: 0032 return i18nc("Radio box option, log priority value", "Notice"); 0033 case 6: 0034 return i18nc("Radio box option, log priority value", "Info"); 0035 case 7: 0036 return i18nc("Radio box option, log priority value", "Debug"); 0037 case -1: 0038 return i18nc("Radio box option, log priority value", "No Filter"); 0039 } 0040 return QLatin1String(""); 0041 } 0042 0043 SelectionEntry::SelectionEntry(const QString &text, 0044 const QVariant &data, 0045 FilterCriteriaModel::Category category, 0046 bool selected, 0047 std::shared_ptr<SelectionEntry> parent) 0048 : mText(text) 0049 , mData(data) 0050 , mCategory(category) 0051 , mSelected(selected) 0052 , mParentItem(parent) 0053 { 0054 } 0055 0056 void SelectionEntry::appendChild(std::shared_ptr<SelectionEntry> item) 0057 { 0058 mChildItems.push_back(item); 0059 } 0060 0061 std::shared_ptr<SelectionEntry> SelectionEntry::child(int row) 0062 { 0063 if (row < 0 || row >= mChildItems.size()) { 0064 return nullptr; 0065 } 0066 return mChildItems.at(row); 0067 } 0068 0069 int SelectionEntry::childCount() const 0070 { 0071 return mChildItems.size(); 0072 } 0073 0074 int SelectionEntry::row() const 0075 { 0076 auto parent = mParentItem.lock(); 0077 if (parent) { 0078 for (int i = 0; i < parent->mChildItems.size(); ++i) { 0079 if (parent->mChildItems.at(i).get() == this) { 0080 return i; 0081 } 0082 } 0083 } 0084 return 0; 0085 } 0086 0087 int SelectionEntry::columnCount() const 0088 { 0089 return 1; 0090 } 0091 0092 QVariant SelectionEntry::data(FilterCriteriaModel::Roles role) const 0093 { 0094 switch (role) { 0095 case FilterCriteriaModel::Roles::CATEGORY: 0096 return QVariant::fromValue(mCategory); 0097 case FilterCriteriaModel::Roles::TEXT: 0098 return QVariant::fromValue(mText); // TODO abbreviate 0099 case FilterCriteriaModel::Roles::DATA: 0100 return QVariant::fromValue(mData); 0101 case FilterCriteriaModel::Roles::LONGTEXT: 0102 return QVariant::fromValue(mText); 0103 case FilterCriteriaModel::Roles::SELECTED: 0104 return QVariant::fromValue(mSelected); 0105 } 0106 return QVariant(); 0107 } 0108 0109 bool SelectionEntry::setData(const QVariant &value, FilterCriteriaModel::Roles role) 0110 { 0111 if (role == FilterCriteriaModel::Roles::SELECTED) { 0112 mSelected = value.toBool(); 0113 return true; 0114 } 0115 qCWarning(KJOURNALDLIB_GENERAL) << "no settable role"; 0116 return false; 0117 } 0118 0119 std::shared_ptr<SelectionEntry> SelectionEntry::parentItem() 0120 { 0121 return mParentItem.lock(); 0122 } 0123 0124 FilterCriteriaModelPrivate::FilterCriteriaModelPrivate() 0125 { 0126 rebuildModel(); 0127 } 0128 0129 FilterCriteriaModelPrivate::~FilterCriteriaModelPrivate() = default; 0130 0131 void FilterCriteriaModelPrivate::rebuildModel() 0132 { 0133 mRootItem = std::make_unique<SelectionEntry>(); 0134 { 0135 auto parent = std::make_shared<SelectionEntry>(i18nc("Section title for log message source", "Transport"), 0136 QVariant(), 0137 FilterCriteriaModel::Category::TRANSPORT, 0138 false, 0139 mRootItem); 0140 mRootItem->appendChild(parent); 0141 mRootItem->child(FilterCriteriaModel::Category::TRANSPORT) 0142 ->appendChild(std::move(std::make_unique<SelectionEntry>(i18nc("Checkbox option for kernel log messages", "Kernel"), 0143 QLatin1String("kernel"), 0144 FilterCriteriaModel::Category::TRANSPORT, 0145 false, 0146 parent))); 0147 } 0148 { 0149 auto parent = std::make_shared<SelectionEntry>(i18nc("Section title for log message priority", "Priority"), 0150 QVariant(), 0151 FilterCriteriaModel::Category::PRIORITY, 0152 false, 0153 mRootItem); 0154 mRootItem->appendChild(parent); 0155 for (int i = 0; i <= 7; ++i) { 0156 mRootItem->child(FilterCriteriaModel::Category::PRIORITY) 0157 ->appendChild(std::move(std::make_unique<SelectionEntry>(mapPriorityToString(i), 0158 QString::number(i), 0159 FilterCriteriaModel::Category::PRIORITY, 0160 i == sDefaultPriorityLevel ? true : false, 0161 parent))); 0162 } 0163 // add "no filter" option at end 0164 mRootItem->child(FilterCriteriaModel::Category::PRIORITY) 0165 ->appendChild(std::move( 0166 std::make_unique<SelectionEntry>(mapPriorityToString(-1), QString::number(-1), FilterCriteriaModel::Category::PRIORITY, false, parent))); 0167 mPriorityLevel = sDefaultPriorityLevel; 0168 } 0169 { 0170 auto parent = std::make_shared<SelectionEntry>(i18nc("Section title for systemd unit", "Unit"), 0171 QVariant(), 0172 FilterCriteriaModel::Category::SYSTEMD_UNIT, 0173 false, 0174 mRootItem); 0175 mRootItem->appendChild(parent); 0176 QVector<QString> units = JournaldHelper::queryUnique(mJournal, JournaldHelper::Field::_SYSTEMD_UNIT); 0177 std::sort(std::begin(units), std::end(units), [](const QString &a, const QString &b) { 0178 return QString::compare(a, b, Qt::CaseInsensitive) <= 0; 0179 }); 0180 for (const auto &unit : std::as_const(units)) { 0181 // skip any non-service units, because we expect users only be interested in filtering those 0182 if (!unit.endsWith(QLatin1String(".service"))) { 0183 continue; 0184 } 0185 mRootItem->child(FilterCriteriaModel::Category::SYSTEMD_UNIT) 0186 ->appendChild(std::move( 0187 std::make_unique<SelectionEntry>(JournaldHelper::cleanupString(unit), unit, FilterCriteriaModel::Category::SYSTEMD_UNIT, false, parent))); 0188 } 0189 } 0190 { 0191 auto parent = std::make_shared<SelectionEntry>(i18nc("Section title for process list", "Process"), 0192 QVariant(), 0193 FilterCriteriaModel::Category::EXE, 0194 false, 0195 mRootItem); 0196 mRootItem->appendChild(parent); 0197 QVector<QString> exes = JournaldHelper::queryUnique(mJournal, JournaldHelper::Field::_EXE); 0198 std::sort(std::begin(exes), std::end(exes), [](const QString &a, const QString &b) { 0199 return QString::compare(a, b, Qt::CaseInsensitive) <= 0; 0200 }); 0201 for (const auto &exe : std::as_const(exes)) { 0202 mRootItem->child(FilterCriteriaModel::Category::EXE) 0203 ->appendChild( 0204 std::move(std::make_unique<SelectionEntry>(JournaldHelper::cleanupString(exe), exe, FilterCriteriaModel::Category::EXE, false, parent))); 0205 } 0206 } 0207 } 0208 0209 FilterCriteriaModel::FilterCriteriaModel(QObject *parent) 0210 : QAbstractItemModel(parent) 0211 , d(new FilterCriteriaModelPrivate) 0212 { 0213 beginResetModel(); 0214 d->mJournal = std::make_shared<LocalJournal>(); 0215 d->rebuildModel(); 0216 endResetModel(); 0217 } 0218 0219 FilterCriteriaModel::FilterCriteriaModel(const QString &journalPath, QObject *parent) 0220 : QAbstractItemModel(parent) 0221 , d(new FilterCriteriaModelPrivate) 0222 { 0223 beginResetModel(); 0224 d->mJournal = std::make_shared<LocalJournal>(journalPath); 0225 d->rebuildModel(); 0226 endResetModel(); 0227 } 0228 0229 FilterCriteriaModel::~FilterCriteriaModel() = default; 0230 0231 bool FilterCriteriaModel::setJournaldPath(const QString &path) 0232 { 0233 beginResetModel(); 0234 d->mJournal = std::make_shared<LocalJournal>(path); 0235 bool success = d->mJournal->isValid(); 0236 d->rebuildModel(); 0237 endResetModel(); 0238 return success; 0239 } 0240 0241 QHash<int, QByteArray> FilterCriteriaModel::roleNames() const 0242 { 0243 QHash<int, QByteArray> roles; 0244 roles[FilterCriteriaModel::TEXT] = "text"; 0245 roles[FilterCriteriaModel::DATA] = "data"; 0246 roles[FilterCriteriaModel::LONGTEXT] = "longtext"; 0247 roles[FilterCriteriaModel::CATEGORY] = "category"; 0248 roles[FilterCriteriaModel::SELECTED] = "selected"; 0249 return roles; 0250 } 0251 0252 void FilterCriteriaModel::setSystemJournal() 0253 { 0254 beginResetModel(); 0255 d->mJournal = std::make_shared<LocalJournal>(); 0256 d->rebuildModel(); 0257 endResetModel(); 0258 } 0259 0260 int FilterCriteriaModel::priorityFilter() const 0261 { 0262 return static_cast<qint8>(d->mPriorityLevel.value_or(-1)); 0263 } 0264 0265 QStringList FilterCriteriaModel::systemdUnitFilter() const 0266 { 0267 std::shared_ptr<SelectionEntry> parent = d->mRootItem->child(FilterCriteriaModel::Category::SYSTEMD_UNIT); 0268 QStringList entries; 0269 for (int i = 0; i < parent->childCount(); ++i) { 0270 if (parent->child(i)->data(FilterCriteriaModel::SELECTED).toBool()) { 0271 entries.append(parent->child(i)->data(FilterCriteriaModel::DATA).toString()); 0272 } 0273 } 0274 return entries; 0275 } 0276 0277 QStringList FilterCriteriaModel::exeFilter() const 0278 { 0279 std::shared_ptr<SelectionEntry> parent = d->mRootItem->child(FilterCriteriaModel::Category::EXE); 0280 QStringList entries; 0281 for (int i = 0; i < parent->childCount(); ++i) { 0282 if (parent->child(i)->data(FilterCriteriaModel::SELECTED).toBool()) { 0283 entries.append(parent->child(i)->data(FilterCriteriaModel::DATA).toString()); 0284 } 0285 } 0286 return entries; 0287 } 0288 0289 bool FilterCriteriaModel::isKernelFilterEnabled() const 0290 { 0291 std::shared_ptr<SelectionEntry> parent = d->mRootItem->child(FilterCriteriaModel::Category::TRANSPORT); 0292 for (int i = 0; i < parent->childCount(); ++i) { 0293 if (parent->child(i)->data(FilterCriteriaModel::DATA) == QLatin1String("kernel") && parent->child(i)->data(FilterCriteriaModel::SELECTED).toBool()) { 0294 return true; 0295 } 0296 } 0297 return false; 0298 } 0299 0300 QModelIndex FilterCriteriaModel::index(int row, int column, const QModelIndex &parent) const 0301 { 0302 if (!hasIndex(row, column, parent)) 0303 return QModelIndex(); 0304 0305 SelectionEntry *parentItem; 0306 0307 if (!parent.isValid()) { 0308 parentItem = d->mRootItem.get(); 0309 } else { 0310 parentItem = static_cast<SelectionEntry *>(parent.internalPointer()); 0311 } 0312 0313 std::shared_ptr<SelectionEntry> childItem = parentItem->child(row); 0314 if (childItem) { 0315 return createIndex(row, column, childItem.get()); 0316 } 0317 return QModelIndex(); 0318 } 0319 0320 QModelIndex FilterCriteriaModel::parent(const QModelIndex &index) const 0321 { 0322 if (!index.isValid()) { 0323 return QModelIndex(); 0324 } 0325 0326 SelectionEntry *childItem = static_cast<SelectionEntry *>(index.internalPointer()); 0327 std::shared_ptr<SelectionEntry> parentItem = childItem->parentItem(); 0328 0329 if (parentItem.get() == d->mRootItem.get()) { 0330 return QModelIndex(); 0331 } 0332 0333 return createIndex(parentItem->row(), 0, parentItem.get()); 0334 } 0335 0336 int FilterCriteriaModel::rowCount(const QModelIndex &parent) const 0337 { 0338 if (!parent.isValid()) { 0339 return d->mRootItem->childCount(); 0340 } else { 0341 auto parentItem = static_cast<SelectionEntry *>(parent.internalPointer()); 0342 return parentItem->childCount(); 0343 } 0344 return 0; 0345 } 0346 0347 int FilterCriteriaModel::columnCount(const QModelIndex &parent) const 0348 { 0349 return 1; 0350 } 0351 0352 QVariant FilterCriteriaModel::data(const QModelIndex &index, int role) const 0353 { 0354 if (!index.parent().isValid()) { 0355 if (index.row() < 0 || index.row() >= d->mRootItem->childCount()) { 0356 qCCritical(KJOURNALDLIB_GENERAL) << "Index out of range" << index; 0357 return QVariant(); 0358 } 0359 return d->mRootItem->child(index.row())->data(static_cast<FilterCriteriaModel::Roles>(role)); 0360 } else { 0361 auto entry = static_cast<SelectionEntry *>(index.internalPointer()); 0362 if (nullptr != entry) { 0363 return entry->data(static_cast<FilterCriteriaModel::Roles>(role)); 0364 } 0365 } 0366 return QVariant(); 0367 } 0368 0369 bool FilterCriteriaModel::setData(const QModelIndex &index, const QVariant &value, int role) 0370 { 0371 auto entry = static_cast<SelectionEntry *>(index.internalPointer()); 0372 if (nullptr == entry) { 0373 return QAbstractItemModel::setData(index, value, role); 0374 } 0375 if (value == entry->data(static_cast<FilterCriteriaModel::Roles>(role))) { 0376 return false; // nothing to do 0377 } 0378 0379 // clear operations for top-level categories 0380 const bool result = entry->setData(value, static_cast<FilterCriteriaModel::Roles>(role)); 0381 const auto category = entry->data(FilterCriteriaModel::Roles::CATEGORY).value<FilterCriteriaModel::Category>(); 0382 Q_EMIT dataChanged(index, index, {role}); 0383 0384 if (result && category == FilterCriteriaModel::Category::PRIORITY && static_cast<FilterCriteriaModel::Roles>(role) == SELECTED) { 0385 // only listen on changes that set entry data to true, because this is considered a selector in the list 0386 std::shared_ptr<SelectionEntry> parent = d->mRootItem->child(FilterCriteriaModel::Category::PRIORITY); 0387 for (int i = 0; i < parent->childCount(); ++i) { 0388 const bool selectedValue = (i == index.row()); 0389 parent->child(i)->setData(selectedValue, FilterCriteriaModel::SELECTED); 0390 static_cast<SelectionEntry *>(FilterCriteriaModel::index(i, 0, index.parent()).internalPointer()) 0391 ->setData(selectedValue, static_cast<FilterCriteriaModel::Roles>(role)); 0392 } 0393 Q_EMIT dataChanged(FilterCriteriaModel::index(0, 0, index.parent()), FilterCriteriaModel::index(parent->childCount() - 1, 0, index.parent()), {role}); 0394 Q_ASSERT(index.row() >= 0); 0395 if (parent->child(index.row())->data(FilterCriteriaModel::DATA).toInt() >= 0) { 0396 d->mPriorityLevel = parent->child(index.row())->data(FilterCriteriaModel::DATA).toInt(); 0397 } else { 0398 d->mPriorityLevel = std::nullopt; 0399 } 0400 qCDebug(KJOURNALDLIB_GENERAL) << "set priority level to:" << static_cast<qint8>(d->mPriorityLevel.value_or(-1)); 0401 Q_EMIT priorityFilterChanged(index.row()); 0402 } else if (result && category == FilterCriteriaModel::Category::SYSTEMD_UNIT) { 0403 // for checkable entries update parent's selected state 0404 if (value.toBool() == true) { 0405 setData(index.parent(), true, FilterCriteriaModel::Roles::SELECTED); 0406 } else { 0407 const auto parent = static_cast<SelectionEntry *>(index.parent().internalPointer()); 0408 if (parent) { 0409 bool hasSelectedSibling{false}; 0410 for (int i = 0; i < parent->childCount(); ++i) { 0411 hasSelectedSibling = hasSelectedSibling || parent->child(i)->data(SELECTED).toBool(); 0412 } 0413 setData(index.parent(), hasSelectedSibling, FilterCriteriaModel::Roles::SELECTED); 0414 } 0415 } 0416 Q_EMIT systemdUnitFilterChanged(); 0417 } else if (result && category == FilterCriteriaModel::Category::EXE) { 0418 // for checkable entries update parent's selected state 0419 if (value.toBool() == true) { 0420 setData(index.parent(), true, FilterCriteriaModel::Roles::SELECTED); 0421 } else { 0422 const auto parent = static_cast<SelectionEntry *>(index.parent().internalPointer()); 0423 if (parent) { 0424 bool hasSelectedSibling{false}; 0425 for (int i = 0; i < parent->childCount(); ++i) { 0426 hasSelectedSibling = hasSelectedSibling || parent->child(i)->data(SELECTED).toBool(); 0427 } 0428 setData(index.parent(), hasSelectedSibling, FilterCriteriaModel::Roles::SELECTED); 0429 } 0430 } 0431 Q_EMIT exeFilterChanged(); 0432 } else if (result && category == FilterCriteriaModel::Category::TRANSPORT) { 0433 Q_EMIT kernelFilterChanged(); 0434 } 0435 return result; 0436 } 0437 0438 QVector<std::pair<QString, bool>> FilterCriteriaModel::entries(FilterCriteriaModel::Category category) const 0439 { 0440 QVector<std::pair<QString, bool>> values; 0441 0442 for (int i = 0; i < d->mRootItem->child(static_cast<int>(category))->childCount(); ++i) { 0443 values.append( 0444 std::make_pair<QString, bool>(d->mRootItem->child(static_cast<int>(category))->child(i)->data(FilterCriteriaModel::DATA).toString(), false)); 0445 } 0446 return values; 0447 }