File indexing completed on 2024-05-12 17:07:12
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 #include <KPluginInfo> 0014 #include <KServiceTypeTrader> 0015 0016 #include <algorithm> 0017 0018 #include "debug.h" 0019 0020 ModulesModel::ModulesModel(QObject *parent) 0021 : QAbstractListModel(parent) 0022 { 0023 } 0024 0025 ModulesModel::~ModulesModel() = default; 0026 0027 int ModulesModel::rowCount(const QModelIndex &parent) const 0028 { 0029 if (parent.isValid()) { 0030 return 0; 0031 } 0032 0033 return m_data.count(); 0034 } 0035 0036 QVariant ModulesModel::data(const QModelIndex &index, int role) const 0037 { 0038 if (!checkIndex(index)) { 0039 return QVariant(); 0040 } 0041 0042 const auto &item = m_data.at(index.row()); 0043 0044 switch (role) { 0045 case Qt::DisplayRole: 0046 return item.display; 0047 case DescriptionRole: 0048 return item.description; 0049 case TypeRole: 0050 return item.type; 0051 case AutoloadEnabledRole: 0052 if (item.type == KDEDConfig::AutostartType) { 0053 return item.autoloadEnabled; 0054 } 0055 return QVariant(); 0056 case StatusRole: { 0057 if (!m_runningModulesKnown) { 0058 return KDEDConfig::UnknownStatus; 0059 } 0060 if (m_runningModules.contains(item.moduleName)) { 0061 return KDEDConfig::Running; 0062 } 0063 return KDEDConfig::NotRunning; 0064 } 0065 case ModuleNameRole: 0066 return item.moduleName; 0067 case ImmutableRole: 0068 return item.immutable; 0069 } 0070 0071 return QVariant(); 0072 } 0073 0074 bool ModulesModel::representsDefault() const 0075 { 0076 bool isDefault = true; 0077 for (int i = 0; i < m_data.count(); ++i) { 0078 auto &item = m_data[i]; 0079 if (item.type != KDEDConfig::AutostartType || item.immutable) { 0080 continue; 0081 } 0082 isDefault &= item.autoloadEnabled; 0083 } 0084 return isDefault; 0085 } 0086 0087 bool ModulesModel::needsSave() const 0088 { 0089 bool save = false; 0090 for (int i = 0; i < m_data.count(); ++i) { 0091 auto &item = m_data[i]; 0092 if (item.type != KDEDConfig::AutostartType || item.immutable) { 0093 continue; 0094 } 0095 save |= item.autoloadEnabled != item.savedAutoloadEnabled; 0096 } 0097 return save; 0098 } 0099 0100 bool ModulesModel::setData(const QModelIndex &index, const QVariant &value, int role) 0101 { 0102 bool dirty = false; 0103 0104 if (!checkIndex(index)) { 0105 return dirty; 0106 } 0107 0108 auto &item = m_data[index.row()]; 0109 0110 if (item.type != KDEDConfig::AutostartType || item.immutable) { 0111 return dirty; 0112 } 0113 0114 switch (role) { 0115 case AutoloadEnabledRole: { 0116 const bool autoloadEnabled = value.toBool(); 0117 if (item.autoloadEnabled != autoloadEnabled) { 0118 item.autoloadEnabled = autoloadEnabled; 0119 dirty = true; 0120 } 0121 Q_EMIT autoloadedModulesChanged(); 0122 break; 0123 } 0124 } 0125 0126 if (dirty) { 0127 Q_EMIT dataChanged(index, index, {role}); 0128 } 0129 0130 return dirty; 0131 } 0132 0133 QHash<int, QByteArray> ModulesModel::roleNames() const 0134 { 0135 return { 0136 {Qt::DisplayRole, QByteArrayLiteral("display")}, 0137 {DescriptionRole, QByteArrayLiteral("description")}, 0138 {TypeRole, QByteArrayLiteral("type")}, 0139 {AutoloadEnabledRole, QByteArrayLiteral("autoloadEnabled")}, 0140 {StatusRole, QByteArrayLiteral("status")}, 0141 {ModuleNameRole, QByteArrayLiteral("moduleName")}, 0142 {ImmutableRole, QByteArrayLiteral("immutable")}, 0143 }; 0144 } 0145 0146 // This code was copied from kded.cpp 0147 // TODO: move this KCM to the KDED framework and share the code? 0148 static QVector<KPluginMetaData> availableModules() 0149 { 0150 QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("kf5/kded")); 0151 QSet<QString> moduleIds; 0152 for (const KPluginMetaData &md : qAsConst(plugins)) { 0153 moduleIds.insert(md.pluginId()); 0154 } 0155 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0156 // also search for old .desktop based kded modules 0157 const KPluginInfo::List oldStylePlugins = KPluginInfo::fromServices(KServiceTypeTrader::self()->query(QStringLiteral("KDEDModule"))); 0158 for (const KPluginInfo &info : oldStylePlugins) { 0159 if (moduleIds.contains(info.pluginName())) { 0160 qCWarning(KCM_KDED).nospace() << "kded module " << info.pluginName() 0161 << " has already been found using " 0162 "JSON metadata, please don't install the now unneeded .desktop file (" 0163 << info.entryPath() << ")."; 0164 } else { 0165 qCDebug(KCM_KDED).nospace() << "kded module " << info.pluginName() << " still uses .desktop files (" << info.entryPath() 0166 << "). Please port it to JSON metadata."; 0167 plugins.append(info.toMetaData()); 0168 } 0169 } 0170 #endif 0171 return plugins; 0172 } 0173 0174 // this code was copied from kded.cpp 0175 static bool isModuleLoadedOnDemand(const KPluginMetaData &module) 0176 { 0177 bool loadOnDemand = true; 0178 // use toVariant() since it could be string or bool in the json and QJsonObject does not convert 0179 QVariant p = module.rawData().value(QStringLiteral("X-KDE-Kded-load-on-demand")).toVariant(); 0180 if (p.isValid() && p.canConvert<bool>() && (p.toBool() == false)) { 0181 loadOnDemand = false; 0182 } 0183 return loadOnDemand; 0184 } 0185 0186 void ModulesModel::load() 0187 { 0188 beginResetModel(); 0189 0190 m_data.clear(); 0191 0192 KConfig kdedrc(QStringLiteral("kded5rc"), KConfig::NoGlobals); 0193 0194 QStringList knownModules; 0195 0196 QVector<ModulesModelData> autostartModules; 0197 QVector<ModulesModelData> onDemandModules; 0198 0199 const auto modules = availableModules(); 0200 for (const KPluginMetaData &module : modules) { 0201 QString servicePath = module.metaDataFileName(); 0202 0203 // autoload defaults to false if it is not found 0204 const bool autoload = module.rawData().value(QStringLiteral("X-KDE-Kded-autoload")).toVariant().toBool(); 0205 0206 // keep estimating dbusModuleName in sync with KDEDModule (kdbusaddons) and kded (kded) 0207 // currently (KF5) the module name in the D-Bus object path is set by the pluginId 0208 const QString dbusModuleName = module.pluginId(); 0209 qCDebug(KCM_KDED) << "reading kded info from" << servicePath << "autoload =" << autoload << "dbus module name =" << dbusModuleName; 0210 0211 if (knownModules.contains(dbusModuleName)) { 0212 continue; 0213 } 0214 0215 knownModules.append(dbusModuleName); 0216 0217 KConfigGroup cg(&kdedrc, QStringLiteral("Module-%1").arg(dbusModuleName)); 0218 const bool autoloadEnabled = cg.readEntry("autoload", true); 0219 const bool immutable = cg.isEntryImmutable("autoload"); 0220 0221 ModulesModelData data{module.name(), module.description(), KDEDConfig::UnknownType, autoloadEnabled, dbusModuleName, immutable, autoloadEnabled}; 0222 0223 // The logic has to be identical to Kded::initModules. 0224 // They interpret X-KDE-Kded-autoload as false if not specified 0225 // X-KDE-Kded-load-on-demand as true if not specified 0226 if (autoload) { 0227 data.type = KDEDConfig::AutostartType; 0228 autostartModules << data; 0229 } else if (isModuleLoadedOnDemand(module)) { 0230 data.type = KDEDConfig::OnDemandType; 0231 onDemandModules << data; 0232 } else { 0233 qCWarning(KCM_KDED) << "kcmkded: Module " << module.name() << "from file" << module.metaDataFileName() 0234 << " not loaded on demand or startup! Skipping."; 0235 continue; 0236 } 0237 } 0238 0239 QCollator collator; 0240 // Otherwise "Write" daemon with quotes will be at the top 0241 collator.setIgnorePunctuation(true); 0242 auto sortAlphabetically = [&collator](const ModulesModelData &a, const ModulesModelData &b) { 0243 return collator.compare(a.display, b.display) < 0; 0244 }; 0245 0246 std::sort(autostartModules.begin(), autostartModules.end(), sortAlphabetically); 0247 std::sort(onDemandModules.begin(), onDemandModules.end(), sortAlphabetically); 0248 0249 m_data << autostartModules << onDemandModules; 0250 0251 endResetModel(); 0252 } 0253 0254 bool ModulesModel::runningModulesKnown() const 0255 { 0256 return m_runningModulesKnown; 0257 } 0258 0259 void ModulesModel::setRunningModulesKnown(bool known) 0260 { 0261 if (m_runningModulesKnown != known) { 0262 m_runningModulesKnown = known; 0263 Q_EMIT dataChanged(index(0, 0), index(m_data.count() - 1, 0), {StatusRole}); 0264 } 0265 } 0266 0267 QStringList ModulesModel::runningModules() const 0268 { 0269 return m_runningModules; 0270 } 0271 0272 void ModulesModel::setRunningModules(const QStringList &runningModules) 0273 { 0274 if (m_runningModules == runningModules) { 0275 return; 0276 } 0277 0278 m_runningModules = runningModules; 0279 if (m_runningModulesKnown) { 0280 Q_EMIT dataChanged(index(0, 0), index(m_data.count() - 1, 0), {StatusRole}); 0281 } 0282 } 0283 0284 void ModulesModel::refreshAutoloadEnabledSavedState() 0285 { 0286 for (int i = 0; i < m_data.count(); ++i) { 0287 auto &item = m_data[i]; 0288 item.savedAutoloadEnabled = item.autoloadEnabled; 0289 } 0290 }