File indexing completed on 2024-05-19 16:39:56

0001 /*
0002  * SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005  */
0006 
0007 #include "ProcessSortFilterModel.h"
0008 
0009 #include <QDebug>
0010 
0011 #include <processcore/process_data_model.h>
0012 
0013 using namespace KSysGuard;
0014 
0015 ProcessSortFilterModel::ProcessSortFilterModel(QObject *parent)
0016     : QSortFilterProxyModel(parent)
0017 {
0018     setSortRole(ProcessDataModel::Value);
0019     setSortCaseSensitivity(Qt::CaseInsensitive);
0020     setSortLocaleAware(true);
0021 
0022     setFilterRole(ProcessDataModel::Value);
0023     setFilterCaseSensitivity(Qt::CaseInsensitive);
0024     setRecursiveFilteringEnabled(true);
0025 }
0026 
0027 void ProcessSortFilterModel::setSourceModel(QAbstractItemModel *newSourceModel)
0028 {
0029     auto oldSourceModel = sourceModel();
0030 
0031     if (newSourceModel == oldSourceModel) {
0032         return;
0033     }
0034 
0035     if (oldSourceModel) {
0036         oldSourceModel->disconnect(this);
0037     }
0038 
0039     QSortFilterProxyModel::setSourceModel(newSourceModel);
0040     if (newSourceModel) {
0041         connect(newSourceModel, &QAbstractItemModel::modelReset, this, &ProcessSortFilterModel::findColumns);
0042         connect(newSourceModel, &QAbstractItemModel::columnsInserted, this, &ProcessSortFilterModel::findColumns);
0043         connect(newSourceModel, &QAbstractItemModel::columnsRemoved, this, &ProcessSortFilterModel::findColumns);
0044         connect(newSourceModel, &QAbstractItemModel::columnsMoved, this, &ProcessSortFilterModel::findColumns);
0045         findColumns();
0046     }
0047 }
0048 
0049 bool ProcessSortFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
0050 {
0051     // not handled a reset yet, we'll invalidate at the end of modelReset anyway
0052     if (m_uidColumn == -1 && m_pidColumn == -1 && filterKeyColumn() == -1) {
0053         return false;
0054     }
0055 
0056 
0057     auto source = sourceModel();
0058 
0059     // Show regardless if an ancestor matches, this is kinda the inverse of recursiveFilteringEnabled
0060     if (sourceParent.parent().isValid()) {
0061         if (filterAcceptsRow(sourceParent.row(), sourceParent.parent())) {
0062             return true;
0063         }
0064     }
0065 
0066     bool result = true;
0067 
0068     if (m_viewMode != ViewAll && m_uidColumn != -1) {
0069         auto uid = source->data(source->index(sourceRow, m_uidColumn, sourceParent), ProcessDataModel::Value).toUInt();
0070 
0071         switch (m_viewMode) {
0072         case ViewOwn:
0073             result = m_currentUser.userId().nativeId() == uid;
0074             break;
0075         case ViewUser:
0076             result = uid >= 1000 && uid < 65534;
0077             break;
0078         case ViewSystem:
0079             result = uid < 1000 || uid >= 65534;
0080             break;
0081         default:
0082             break;
0083         }
0084     }
0085 
0086     if (!m_filterPids.isEmpty()) {
0087         auto pid = source->data(source->index(sourceRow, m_pidColumn, sourceParent), ProcessDataModel::Value);
0088         result = m_filterPids.contains(pid);
0089     }
0090 
0091     if (!result) {
0092         return false;
0093     }
0094 
0095     if (filterString().isEmpty()) {
0096         return true;
0097     }
0098 
0099     const QString name = source->data(source->index(sourceRow, 0, sourceParent), filterRole()).toString();
0100 
0101     const QString filter = filterString();
0102 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0103     const QVector<QStringRef> splitFilterStrings = filter.splitRef(QLatin1Char(','), Qt::SkipEmptyParts);
0104 
0105     for (const QStringRef &string : splitFilterStrings) {
0106 #else
0107     const QVector<QStringView> splitFilterStrings = QStringView(filter).split(QLatin1Char(','), Qt::SkipEmptyParts);
0108 
0109     for (const QStringView &string : splitFilterStrings) {
0110 #endif
0111         if (name.contains(string.trimmed(), Qt::CaseInsensitive)) {
0112             return true;
0113         }
0114     }
0115 
0116     return false;
0117 }
0118 
0119 bool ProcessSortFilterModel::filterAcceptsColumn(int sourceColumn, const QModelIndex &sourceParent) const
0120 {
0121     Q_UNUSED(sourceParent)
0122 
0123     auto attribute = sourceModel()->headerData(sourceColumn, Qt::Horizontal, ProcessDataModel::Attribute).toString();
0124     if (m_hiddenAttributes.contains(attribute)) {
0125         return false;
0126     }
0127 
0128     return true;
0129 }
0130 
0131 QString ProcessSortFilterModel::filterString() const
0132 {
0133     return m_filterString;
0134 }
0135 
0136 void ProcessSortFilterModel::setFilterString(const QString &newFilterString)
0137 {
0138     if (newFilterString == m_filterString) {
0139         return;
0140     }
0141 
0142     m_filterString = newFilterString;
0143     setFilterWildcard(m_filterString);
0144     Q_EMIT filterStringChanged();
0145 }
0146 
0147 ProcessSortFilterModel::ViewMode ProcessSortFilterModel::viewMode() const
0148 {
0149     return m_viewMode;
0150 }
0151 
0152 void ProcessSortFilterModel::setViewMode(ViewMode newViewMode)
0153 {
0154     if (newViewMode == m_viewMode) {
0155         return;
0156     }
0157 
0158     m_viewMode = newViewMode;
0159     invalidateFilter();
0160     Q_EMIT viewModeChanged();
0161 }
0162 
0163 QStringList ProcessSortFilterModel::hiddenAttributes() const
0164 {
0165     return m_hiddenAttributes;
0166 }
0167 
0168 void ProcessSortFilterModel::setHiddenAttributes(const QStringList &newHiddenAttributes)
0169 {
0170     if (newHiddenAttributes == m_hiddenAttributes) {
0171         return;
0172     }
0173 
0174     m_hiddenAttributes = newHiddenAttributes;
0175     invalidateFilter();
0176     Q_EMIT hiddenAttributesChanged();
0177 }
0178 
0179 QVariantList ProcessSortFilterModel::filterPids() const
0180 {
0181     return m_filterPids;
0182 }
0183 
0184 void ProcessSortFilterModel::setFilterPids(const QVariantList &newFilterPids)
0185 {
0186     if (newFilterPids == m_filterPids) {
0187         return;
0188     }
0189 
0190     m_filterPids = newFilterPids;
0191     invalidateFilter();
0192     Q_EMIT filterPidsChanged();
0193 }
0194 
0195 void ProcessSortFilterModel::sort(int column, Qt::SortOrder order)
0196 {
0197     QSortFilterProxyModel::sort(column, order);
0198 }
0199 
0200 void ProcessSortFilterModel::findColumns()
0201 {
0202     m_uidColumn = -1;
0203     m_pidColumn = -1;
0204     int nameColumn = -1;
0205 
0206     auto source = sourceModel();
0207 
0208     for (auto column = 0; column < source->columnCount(); ++column) {
0209         auto attribute = source->headerData(column, Qt::Horizontal, ProcessDataModel::Attribute).toString();
0210         if (attribute == QStringLiteral("uid")) {
0211             m_uidColumn = column;
0212         } else if (attribute == QStringLiteral("pid")) {
0213             m_pidColumn = column;
0214         } else if (attribute == QStringLiteral("name")) {
0215             nameColumn = column;
0216         }
0217     }
0218     setFilterKeyColumn(nameColumn);
0219 }