File indexing completed on 2025-02-09 06:41:25

0001 /*
0002     SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
0003     SPDX-FileCopyrightText: 2015 Eike Hein <hein@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "configmodel.h"
0009 #include "Plasma/Applet"
0010 #include "Plasma/Containment"
0011 #include "configview.h"
0012 #include "debug_p.h"
0013 #include "private/configcategory_p.h"
0014 #include "sharedqmlengine.h"
0015 
0016 #include <QDebug>
0017 #include <QDir>
0018 #include <QQmlComponent>
0019 #include <QQmlContext>
0020 #include <QQmlEngine>
0021 #include <QQuickItem>
0022 
0023 #include <KLocalizedString>
0024 #include <KPackage/Package>
0025 #include <KQuickConfigModule>
0026 #include <KQuickConfigModuleLoader>
0027 
0028 #include <Plasma/Corona>
0029 #include <Plasma/PluginLoader>
0030 #include <kquickconfigmoduleloader.h>
0031 
0032 namespace PlasmaQuick
0033 {
0034 //////////////////////////////ConfigModel
0035 
0036 class ConfigModelPrivate
0037 {
0038 public:
0039     ConfigModelPrivate(ConfigModel *model);
0040     ~ConfigModelPrivate();
0041 
0042     ConfigModel *q;
0043     QList<ConfigCategory *> categories;
0044     QPointer<Plasma::Applet> appletInterface;
0045     QHash<QString, KQuickConfigModule *> kcms;
0046 
0047     void appendCategory(ConfigCategory *c);
0048     void removeCategory(ConfigCategory *c);
0049     void removeCategoryAt(int index);
0050     void clear();
0051     QVariant get(int row) const;
0052 
0053     static ConfigCategory *categories_at(QQmlListProperty<ConfigCategory> *prop, qsizetype index);
0054     static qsizetype categories_count(QQmlListProperty<ConfigCategory> *prop);
0055     static void categories_append(QQmlListProperty<ConfigCategory> *prop, ConfigCategory *o);
0056     static void categories_clear(QQmlListProperty<ConfigCategory> *prop);
0057 };
0058 
0059 ConfigModelPrivate::ConfigModelPrivate(ConfigModel *model)
0060     : q(model)
0061 {
0062 }
0063 
0064 ConfigModelPrivate::~ConfigModelPrivate()
0065 {
0066 }
0067 
0068 ConfigCategory *ConfigModelPrivate::categories_at(QQmlListProperty<ConfigCategory> *prop, qsizetype index)
0069 {
0070     ConfigModel *model = qobject_cast<ConfigModel *>(prop->object);
0071     if (!model || index >= model->d->categories.count() || index < 0) {
0072         return nullptr;
0073     } else {
0074         return model->d->categories.at(index);
0075     }
0076 }
0077 
0078 void ConfigModelPrivate::categories_append(QQmlListProperty<ConfigCategory> *prop, ConfigCategory *o)
0079 {
0080     ConfigModel *model = qobject_cast<ConfigModel *>(prop->object);
0081     if (!o || !model) {
0082         return;
0083     }
0084 
0085     if (o->parent() == prop->object) {
0086         o->setParent(nullptr);
0087     }
0088 
0089     o->setParent(prop->object);
0090     model->d->appendCategory(o);
0091 }
0092 
0093 qsizetype ConfigModelPrivate::categories_count(QQmlListProperty<ConfigCategory> *prop)
0094 {
0095     ConfigModel *model = qobject_cast<ConfigModel *>(prop->object);
0096     if (model) {
0097         return model->d->categories.count();
0098     } else {
0099         return 0;
0100     }
0101 }
0102 
0103 void ConfigModelPrivate::categories_clear(QQmlListProperty<ConfigCategory> *prop)
0104 {
0105     ConfigModel *model = qobject_cast<ConfigModel *>(prop->object);
0106     if (!model) {
0107         return;
0108     }
0109 
0110     model->clear();
0111 }
0112 
0113 void ConfigModelPrivate::clear()
0114 {
0115     q->beginResetModel();
0116     while (!categories.isEmpty()) {
0117         categories.first()->setParent(nullptr);
0118         categories.pop_front();
0119     }
0120     q->endResetModel();
0121     Q_EMIT q->countChanged();
0122 }
0123 
0124 void ConfigModelPrivate::appendCategory(ConfigCategory *c)
0125 {
0126     if (!c) {
0127         return;
0128     }
0129 
0130     q->beginInsertRows(QModelIndex(), categories.size(), categories.size());
0131     categories.append(c);
0132 
0133     auto emitChange = [this, c] {
0134         const int row = categories.indexOf(c);
0135         if (row > -1) {
0136             QModelIndex modelIndex = q->index(row);
0137             Q_EMIT q->dataChanged(modelIndex, modelIndex);
0138         }
0139     };
0140 
0141     QObject::connect(c, &ConfigCategory::nameChanged, q, emitChange);
0142     QObject::connect(c, &ConfigCategory::iconChanged, q, emitChange);
0143     QObject::connect(c, &ConfigCategory::sourceChanged, q, emitChange);
0144     QObject::connect(c, &ConfigCategory::pluginNameChanged, q, emitChange);
0145     QObject::connect(c, &ConfigCategory::visibleChanged, q, emitChange);
0146 
0147     q->endInsertRows();
0148     Q_EMIT q->countChanged();
0149 }
0150 
0151 void ConfigModelPrivate::removeCategory(ConfigCategory *c)
0152 {
0153     const int index = categories.indexOf(c);
0154     if (index > -1) {
0155         removeCategoryAt(index);
0156     }
0157 }
0158 
0159 void ConfigModelPrivate::removeCategoryAt(int index)
0160 {
0161     if (index < 0 || index >= categories.count()) {
0162         return;
0163     }
0164 
0165     q->beginRemoveRows(QModelIndex(), index, index);
0166 
0167     ConfigCategory *c = categories.takeAt(index);
0168     if (c->parent() == q) {
0169         c->deleteLater();
0170     }
0171 
0172     q->endRemoveRows();
0173     Q_EMIT q->countChanged();
0174 }
0175 
0176 QVariant ConfigModelPrivate::get(int row) const
0177 {
0178     QVariantMap value;
0179     if (row < 0 || row >= categories.count()) {
0180         return value;
0181     }
0182 
0183     value[QStringLiteral("name")] = categories.at(row)->name();
0184     value[QStringLiteral("icon")] = categories.at(row)->icon();
0185     value[QStringLiteral("pluginName")] = categories.at(row)->pluginName();
0186     value[QStringLiteral("source")] = q->data(q->index(row, 0), ConfigModel::SourceRole);
0187     value[QStringLiteral("includeMargins")] = categories.at(row)->includeMargins();
0188     value[QStringLiteral("visible")] = categories.at(row)->visible();
0189     value[QStringLiteral("kcm")] = q->data(q->index(row, 0), ConfigModel::KCMRole);
0190 
0191     return value;
0192 }
0193 
0194 ConfigModel::ConfigModel(QObject *parent)
0195     : QAbstractListModel(parent)
0196     , d(new ConfigModelPrivate(this))
0197 {
0198 }
0199 
0200 ConfigModel::~ConfigModel()
0201 {
0202     delete d;
0203 }
0204 
0205 int ConfigModel::rowCount(const QModelIndex &index) const
0206 {
0207     if (index.column() > 0) {
0208         return 0;
0209     }
0210     return d->categories.count();
0211 }
0212 
0213 QVariant ConfigModel::data(const QModelIndex &index, int role) const
0214 {
0215     if (index.row() < 0 || index.row() >= d->categories.count()) {
0216         return QVariant();
0217     }
0218     switch (role) {
0219     case NameRole:
0220         return d->categories.at(index.row())->name();
0221     case IconRole:
0222         return d->categories.at(index.row())->icon();
0223     case SourceRole: {
0224         const QString source = d->categories.at(index.row())->source();
0225         // Quick check if source is an absolute path or not
0226         if (d->appletInterface && !source.isEmpty() && !(source.startsWith(QLatin1Char('/')) && source.endsWith(QLatin1String("qml")))) {
0227             if (!d->appletInterface.data()->kPackage().isValid()) {
0228                 qWarning() << "wrong applet" << d->appletInterface.data()->pluginMetaData().name();
0229             }
0230             return d->appletInterface.data()->kPackage().fileUrl("ui", source);
0231         } else {
0232             return source;
0233         }
0234     }
0235     case PluginNameRole:
0236         return d->categories.at(index.row())->pluginName();
0237     case IncludeMarginsRole:
0238         return d->categories.at(index.row())->includeMargins();
0239     case VisibleRole:
0240         return d->categories.at(index.row())->visible();
0241     case KCMRole: {
0242         const QString pluginName = d->categories.at(index.row())->pluginName();
0243         // no kcm is registered for this row, it's a normal qml-only entry
0244         if (pluginName.isEmpty()) {
0245             return QVariant();
0246         }
0247 
0248         if (d->kcms.contains(pluginName)) {
0249             return QVariant::fromValue(d->kcms.value(pluginName));
0250         }
0251         auto parent = const_cast<ConfigModel *>(this);
0252         auto engine = new PlasmaQuick::SharedQmlEngine(parent);
0253         auto cmResult = KQuickConfigModuleLoader::loadModule(KPluginMetaData(pluginName), parent, QVariantList(), engine->engine());
0254         if (KQuickConfigModule *cm = cmResult.plugin) {
0255             if (QQmlContext *ctx = QQmlEngine::contextForObject(this)) {
0256                 // assign the ConfigModule the same QML context as we have so it can use the same QML engine as we do
0257                 QQmlEngine::setContextForObject(cmResult.plugin, ctx);
0258             }
0259 
0260             d->kcms[pluginName] = cm;
0261             return QVariant::fromValue(cm);
0262         } else {
0263             qCDebug(LOG_PLASMAQUICK) << "Error loading KCM:" << cmResult.errorText;
0264             return QVariant();
0265         }
0266     }
0267     default:
0268         return QVariant();
0269     }
0270 }
0271 
0272 QHash<int, QByteArray> ConfigModel::roleNames() const
0273 {
0274     return {
0275         {NameRole, "name"},
0276         {IconRole, "icon"},
0277         {SourceRole, "source"},
0278         {PluginNameRole, "pluginName"},
0279         {IncludeMarginsRole, "includeMargins"},
0280         {VisibleRole, "visible"},
0281         {KCMRole, "kcm"},
0282     };
0283 }
0284 
0285 QVariant ConfigModel::get(int row) const
0286 {
0287     return d->get(row);
0288 }
0289 
0290 void ConfigModel::appendCategory(const QString &iconName, const QString &name, const QString &path, const QString &pluginName)
0291 {
0292     ConfigCategory *cat = new ConfigCategory(this);
0293     cat->setIcon(iconName);
0294     cat->setName(name);
0295     cat->setSource(path);
0296     cat->setPluginName(pluginName);
0297     d->appendCategory(cat);
0298 }
0299 
0300 void ConfigModel::appendCategory(const QString &iconName, const QString &name, const QString &path, const QString &pluginName, bool visible)
0301 {
0302     ConfigCategory *cat = new ConfigCategory(this);
0303     cat->setIcon(iconName);
0304     cat->setName(name);
0305     cat->setSource(path);
0306     cat->setPluginName(pluginName);
0307     cat->setVisible(visible);
0308     d->appendCategory(cat);
0309 }
0310 
0311 void ConfigModel::appendCategory(ConfigCategory *category)
0312 {
0313     d->appendCategory(category);
0314 }
0315 
0316 void ConfigModel::removeCategory(ConfigCategory *category)
0317 {
0318     d->removeCategory(category);
0319 }
0320 
0321 void ConfigModel::removeCategoryAt(int index)
0322 {
0323     d->removeCategoryAt(index);
0324 }
0325 
0326 void ConfigModel::clear()
0327 {
0328     d->clear();
0329 }
0330 
0331 void ConfigModel::setApplet(Plasma::Applet *interface)
0332 {
0333     d->appletInterface = interface;
0334 }
0335 
0336 Plasma::Applet *ConfigModel::applet() const
0337 {
0338     return d->appletInterface.data();
0339 }
0340 
0341 QQmlListProperty<ConfigCategory> ConfigModel::categories()
0342 {
0343     return QQmlListProperty<ConfigCategory>(this,
0344                                             nullptr,
0345                                             ConfigModelPrivate::categories_append,
0346                                             ConfigModelPrivate::categories_count,
0347                                             ConfigModelPrivate::categories_at,
0348                                             ConfigModelPrivate::categories_clear);
0349 }
0350 
0351 }
0352 
0353 #include "moc_configmodel.cpp"