File indexing completed on 2025-07-13 05:02:37

0001 /*
0002     SPDX-FileCopyrightText: 2020 Kai Uwe Broulik <kde@broulik.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "modulesmodel.h"
0008 
0009 #include <QCollator>
0010 
0011 #include <KConfig>
0012 #include <KConfigGroup>
0013 
0014 #include <algorithm>
0015 
0016 #include "debug.h"
0017 
0018 ModulesModel::ModulesModel(QObject *parent)
0019     : QAbstractListModel(parent)
0020 {
0021 }
0022 
0023 ModulesModel::~ModulesModel() = default;
0024 
0025 int ModulesModel::rowCount(const QModelIndex &parent) const
0026 {
0027     if (parent.isValid()) {
0028         return 0;
0029     }
0030 
0031     return m_data.count();
0032 }
0033 
0034 QVariant ModulesModel::data(const QModelIndex &index, int role) const
0035 {
0036     if (!checkIndex(index)) {
0037         return QVariant();
0038     }
0039 
0040     const auto &item = m_data.at(index.row());
0041 
0042     switch (role) {
0043     case Qt::DisplayRole:
0044         return item.display;
0045     case DescriptionRole:
0046         return item.description;
0047     case TypeRole:
0048         return item.type;
0049     case AutoloadEnabledRole:
0050         if (item.type == KDEDConfig::AutostartType) {
0051             return item.autoloadEnabled;
0052         }
0053         return QVariant();
0054     case StatusRole: {
0055         if (!m_runningModulesKnown) {
0056             return KDEDConfig::UnknownStatus;
0057         }
0058         if (m_runningModules.contains(item.moduleName)) {
0059             return KDEDConfig::Running;
0060         }
0061         return KDEDConfig::NotRunning;
0062     }
0063     case ModuleNameRole:
0064         return item.moduleName;
0065     case ImmutableRole:
0066         return item.immutable;
0067     }
0068 
0069     return QVariant();
0070 }
0071 
0072 bool ModulesModel::representsDefault() const
0073 {
0074     bool isDefault = true;
0075     for (int i = 0; i < m_data.count(); ++i) {
0076         auto &item = m_data[i];
0077         if (item.type != KDEDConfig::AutostartType || item.immutable) {
0078             continue;
0079         }
0080         isDefault &= item.autoloadEnabled;
0081     }
0082     return isDefault;
0083 }
0084 
0085 bool ModulesModel::needsSave() const
0086 {
0087     bool save = false;
0088     for (int i = 0; i < m_data.count(); ++i) {
0089         auto &item = m_data[i];
0090         if (item.type != KDEDConfig::AutostartType || item.immutable) {
0091             continue;
0092         }
0093         save |= item.autoloadEnabled != item.savedAutoloadEnabled;
0094     }
0095     return save;
0096 }
0097 
0098 bool ModulesModel::setData(const QModelIndex &index, const QVariant &value, int role)
0099 {
0100     bool dirty = false;
0101 
0102     if (!checkIndex(index)) {
0103         return dirty;
0104     }
0105 
0106     auto &item = m_data[index.row()];
0107 
0108     if (item.type != KDEDConfig::AutostartType || item.immutable) {
0109         return dirty;
0110     }
0111 
0112     switch (role) {
0113     case AutoloadEnabledRole: {
0114         const bool autoloadEnabled = value.toBool();
0115         if (item.autoloadEnabled != autoloadEnabled) {
0116             item.autoloadEnabled = autoloadEnabled;
0117             dirty = true;
0118         }
0119         Q_EMIT autoloadedModulesChanged();
0120         break;
0121     }
0122     }
0123 
0124     if (dirty) {
0125         Q_EMIT dataChanged(index, index, {role});
0126     }
0127 
0128     return dirty;
0129 }
0130 
0131 QHash<int, QByteArray> ModulesModel::roleNames() const
0132 {
0133     return {
0134         {Qt::DisplayRole, QByteArrayLiteral("display")},
0135         {DescriptionRole, QByteArrayLiteral("description")},
0136         {TypeRole, QByteArrayLiteral("type")},
0137         {AutoloadEnabledRole, QByteArrayLiteral("autoloadEnabled")},
0138         {StatusRole, QByteArrayLiteral("status")},
0139         {ModuleNameRole, QByteArrayLiteral("moduleName")},
0140         {ImmutableRole, QByteArrayLiteral("immutable")},
0141     };
0142 }
0143 
0144 void ModulesModel::load()
0145 {
0146     beginResetModel();
0147 
0148     m_data.clear();
0149 
0150     KConfig kdedrc(QStringLiteral("kded5rc"), KConfig::NoGlobals);
0151 
0152     QStringList knownModules;
0153 
0154     QList<ModulesModelData> autostartModules;
0155     QList<ModulesModelData> onDemandModules;
0156 
0157     const auto modules = KPluginMetaData::findPlugins(QStringLiteral("kf6/kded"));
0158     for (const KPluginMetaData &module : modules) {
0159         QString servicePath = module.fileName();
0160 
0161         // autoload defaults to false if it is not found
0162         const bool autoload = module.value(QStringLiteral("X-KDE-Kded-autoload"), false);
0163 
0164         // keep estimating dbusModuleName in sync with KDEDModule (kdbusaddons) and kded (kded)
0165         // currently (KF5) the module name in the D-Bus object path is set by the pluginId
0166         const QString dbusModuleName = module.pluginId();
0167         qCDebug(KCM_KDED) << "reading kded info from" << servicePath << "autoload =" << autoload << "dbus module name =" << dbusModuleName;
0168 
0169         if (knownModules.contains(dbusModuleName)) {
0170             continue;
0171         }
0172 
0173         knownModules.append(dbusModuleName);
0174 
0175         KConfigGroup cg(&kdedrc, QStringLiteral("Module-%1").arg(dbusModuleName));
0176         const bool autoloadEnabled = cg.readEntry("autoload", true);
0177         const bool immutable = cg.isEntryImmutable("autoload");
0178 
0179         ModulesModelData data{module.name(), module.description(), KDEDConfig::UnknownType, autoloadEnabled, dbusModuleName, immutable, autoloadEnabled};
0180 
0181         // The logic has to be identical to Kded::initModules.
0182         // They interpret X-KDE-Kded-autoload as false if not specified
0183         //                X-KDE-Kded-load-on-demand as true if not specified
0184         if (autoload) {
0185             data.type = KDEDConfig::AutostartType;
0186             autostartModules << data;
0187         } else if (module.value(QStringLiteral("X-KDE-Kded-load-on-demand"), false)) {
0188             data.type = KDEDConfig::OnDemandType;
0189             onDemandModules << data;
0190         } else {
0191             qCWarning(KCM_KDED) << "kcmkded: Module" << module.name() << "from file" << module.fileName() << "not loaded on demand or startup! Skipping.";
0192             continue;
0193         }
0194     }
0195 
0196     QCollator collator;
0197     // Otherwise "Write" daemon with quotes will be at the top
0198     collator.setIgnorePunctuation(true);
0199     auto sortAlphabetically = [&collator](const ModulesModelData &a, const ModulesModelData &b) {
0200         return collator.compare(a.display, b.display) < 0;
0201     };
0202 
0203     std::sort(autostartModules.begin(), autostartModules.end(), sortAlphabetically);
0204     std::sort(onDemandModules.begin(), onDemandModules.end(), sortAlphabetically);
0205 
0206     m_data << autostartModules << onDemandModules;
0207 
0208     endResetModel();
0209 }
0210 
0211 bool ModulesModel::runningModulesKnown() const
0212 {
0213     return m_runningModulesKnown;
0214 }
0215 
0216 void ModulesModel::setRunningModulesKnown(bool known)
0217 {
0218     if (m_runningModulesKnown != known) {
0219         m_runningModulesKnown = known;
0220         Q_EMIT dataChanged(index(0, 0), index(m_data.count() - 1, 0), {StatusRole});
0221     }
0222 }
0223 
0224 QStringList ModulesModel::runningModules() const
0225 {
0226     return m_runningModules;
0227 }
0228 
0229 void ModulesModel::setRunningModules(const QStringList &runningModules)
0230 {
0231     if (m_runningModules == runningModules) {
0232         return;
0233     }
0234 
0235     m_runningModules = runningModules;
0236     if (m_runningModulesKnown) {
0237         Q_EMIT dataChanged(index(0, 0), index(m_data.count() - 1, 0), {StatusRole});
0238     }
0239 }
0240 
0241 void ModulesModel::refreshAutoloadEnabledSavedState()
0242 {
0243     for (int i = 0; i < m_data.count(); ++i) {
0244         auto &item = m_data[i];
0245         item.savedAutoloadEnabled = item.autoloadEnabled;
0246     }
0247 }