Warning, file /plasma/plasma-workspace/kcms/wallpaper/wallpapermodule.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 #include "wallpapermodule.h"
0002 
0003 #include "config-workspace.h"
0004 #include "defaultwallpaper.h"
0005 #include "kcm_wallpaper_debug.h"
0006 #include "qdbusinterface.h"
0007 #include "qdbusreply.h"
0008 #include <defaultwallpaper.h>
0009 #include <outputorderwatcher.h>
0010 
0011 #include <KPackage/Package>
0012 #include <KPackage/PackageLoader>
0013 #include <Plasma/Corona>
0014 #include <Plasma/PluginLoader>
0015 
0016 #include <KConfig>
0017 #include <KConfigGroup>
0018 #include <KConfigLoader>
0019 #include <KConfigPropertyMap>
0020 #include <KLocalizedString>
0021 #include <KPluginFactory>
0022 #include <plasmaactivities/consumer.h>
0023 
0024 #include <QAbstractItemModel>
0025 #include <QApplication>
0026 #include <QDBusArgument>
0027 #include <QDBusConnection>
0028 #include <QDBusMetaType>
0029 #include <QFile>
0030 #include <QQmlEngine>
0031 #include <QQuickItem>
0032 #include <QQuickWindow>
0033 #include <QScreen>
0034 #include <QWindow>
0035 
0036 K_PLUGIN_CLASS_WITH_JSON(WallpaperModule, "kcm_wallpaper.json")
0037 
0038 Q_DECLARE_METATYPE(QColor)
0039 
0040 QDBusArgument &operator<<(QDBusArgument &argument, const QColor &color)
0041 {
0042     argument.beginStructure();
0043     argument << color.rgba();
0044     argument.endStructure();
0045     return argument;
0046 }
0047 
0048 const QDBusArgument &operator>>(const QDBusArgument &argument, QColor &color)
0049 {
0050     argument.beginStructure();
0051     QRgb rgba;
0052     argument >> rgba;
0053     argument.endStructure();
0054     color = QColor::fromRgba(rgba);
0055     return argument;
0056 }
0057 
0058 WallpaperModule::WallpaperModule(QObject *parent, const KPluginMetaData &data)
0059     : KQuickConfigModule(parent, data)
0060     , m_config(KSharedConfig::openConfig(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/plasma-org.kde.plasma.desktop-appletsrc",
0061                                          KConfig::OpenFlag::SimpleConfig))
0062     , m_activityConsumer(new KActivities::Consumer(this))
0063 {
0064     qDBusRegisterMetaType<QColor>();
0065 
0066     KActivities::Consumer c(this);
0067 
0068     connect(m_activityConsumer, &KActivities::Consumer::currentActivityChanged, this, &WallpaperModule::setCurrentActivity);
0069 
0070     constexpr const char *uri = "org.kde.plasma.kcm.wallpaper";
0071 
0072     qmlRegisterAnonymousType<QScreen>(uri, 1);
0073     qmlRegisterAnonymousType<KConfigPropertyMap>(uri, 1);
0074     // Only register types once
0075     [[maybe_unused]] static int configModelRegisterResult = qmlRegisterType<PlasmaQuick::ConfigModel>("org.kde.plasma.configuration", 2, 0, "ConfigModel");
0076 
0077     m_outputOrderWatcher = OutputOrderWatcher::instance(this);
0078     connect(m_outputOrderWatcher, &OutputOrderWatcher::outputOrderChanged, this, [this](const QStringList &outputOrder) {
0079         if (!outputOrder.contains(m_selectedScreen->name())) {
0080             // current screen was removed
0081             m_selectedScreen = mainUi()->window()->screen();
0082             Q_EMIT selectedScreenChanged();
0083         }
0084         onScreenChanged();
0085     });
0086 
0087     connect(this, &KQuickConfigModule::mainUiReady, this, [this]() {
0088         connect(
0089             mainUi(),
0090             &QQuickItem::windowChanged,
0091             this,
0092             [this]() {
0093                 m_selectedScreen = mainUi()->window()->screen();
0094                 Q_EMIT selectedScreenChanged();
0095                 onScreenChanged();
0096             },
0097             Qt::ConnectionType::SingleShotConnection);
0098     });
0099 
0100     const bool connected = QDBusConnection::sessionBus().connect(QStringLiteral("org.kde.plasmashell"),
0101                                                                  QStringLiteral("/PlasmaShell"),
0102                                                                  QStringLiteral("org.kde.PlasmaShell"),
0103                                                                  QStringLiteral("wallpaperChanged"),
0104                                                                  this,
0105                                                                  SLOT(onWallpaperChanged(uint)));
0106     if (!connected) {
0107         qCFatal(KCM_WALLPAPER_DEBUG) << "Could not connect to dbus service org.kde.plasmashell";
0108     }
0109 
0110     setButtons(Apply | Default);
0111 
0112     m_screens = qApp->screens();
0113     connect(qApp, &QGuiApplication::screenRemoved, this, [this](QScreen *screen) {
0114         m_screens.removeAll(screen);
0115         Q_EMIT screensChanged();
0116     });
0117     connect(qApp, &QGuiApplication::screenAdded, this, [this](QScreen *screen) {
0118         m_screens << screen;
0119         Q_EMIT screensChanged();
0120     });
0121 }
0122 
0123 void WallpaperModule::onScreenChanged()
0124 {
0125     if (m_activityId.isEmpty() || m_activityId == QUuid().toString()) {
0126         return;
0127     }
0128 
0129     if (m_selectedScreen == nullptr) {
0130         return;
0131     }
0132 
0133     const auto outputOrder = m_outputOrderWatcher->outputOrder();
0134     if (outputOrder.isEmpty()) {
0135         return;
0136     }
0137     int screenId = screenIdFromName(m_selectedScreen->name());
0138     Q_ASSERT(screenId != -1);
0139 
0140     auto containmentsGroup = m_config->group(QStringLiteral("Containments"));
0141 
0142     for (const auto &contIndex : containmentsGroup.groupList()) {
0143         const auto contConfig = containmentsGroup.group(contIndex);
0144         if (m_activityId != contConfig.readEntry("activityId")) {
0145             continue;
0146         }
0147 
0148         auto lastScreenIdx = contConfig.readEntry("lastScreen", -1);
0149         if (lastScreenIdx < 0) {
0150             continue;
0151         }
0152 
0153         if (lastScreenIdx == screenId) {
0154             m_containmentIdx = contIndex;
0155             break;
0156         }
0157     }
0158     Q_ASSERT(m_containmentIdx != "");
0159 
0160     auto containmentConfigGroup = containmentsGroup.group(m_containmentIdx);
0161     m_loadedWallpaperplugin = containmentConfigGroup.readEntry("wallpaperplugin");
0162 
0163     auto previousConfig = m_wallpaperConfiguration;
0164     setWallpaperPluginConfiguration(m_loadedWallpaperplugin);
0165 
0166     setRepresentsDefaults(isDefault());
0167     setNeedsSave(false);
0168 
0169     if (m_loadedWallpaperplugin != m_currentWallpaperPlugin) {
0170         m_currentWallpaperPlugin = m_loadedWallpaperplugin;
0171         Q_EMIT currentWallpaperPluginChanged();
0172     } else {
0173         Q_EMIT wallpaperConfigurationChanged();
0174     }
0175 
0176     delete previousConfig;
0177 }
0178 
0179 int WallpaperModule::screenIdFromName(const QString &screenName) const
0180 {
0181     int screenId = -1;
0182     int idx = 0;
0183     const auto outputOrder = m_outputOrderWatcher->outputOrder();
0184     for (const auto &output : outputOrder) {
0185         if (output == screenName) {
0186             screenId = idx;
0187             break;
0188         }
0189         ++idx;
0190     }
0191 
0192     return screenId;
0193 }
0194 
0195 void WallpaperModule::setWallpaperPluginConfiguration(const QString &wallpaperplugin, bool loadDefaults)
0196 {
0197     auto wallpaperConfig = m_config->group(QStringLiteral("Containments")).group(m_containmentIdx).group(QStringLiteral("Wallpaper")).group(wallpaperplugin);
0198     m_wallpaperConfigGeneral = wallpaperConfig.group(QStringLiteral("General"));
0199 
0200     KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Wallpaper"));
0201     pkg.setDefaultPackageRoot(QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/wallpapers"));
0202     pkg.setPath(wallpaperplugin);
0203     QFile file(pkg.filePath("config", QStringLiteral("main.xml")));
0204 
0205     m_configLoader = new KConfigLoader(wallpaperConfig, &file, this);
0206     if (loadDefaults) {
0207         m_configLoader->setDefaults();
0208     }
0209     m_wallpaperConfiguration = new KConfigPropertyMap(m_configLoader, this);
0210 
0211     // set the default wallpaper value
0212     m_defaultWallpaper = DefaultWallpaper::defaultWallpaperPackage().path();
0213     m_wallpaperConfiguration->insert(QStringLiteral("ImageDefault"), m_defaultWallpaper);
0214 
0215     // set the default Image value if necessary
0216     if (m_wallpaperConfiguration->value(QStringLiteral("Image")).isNull()) {
0217         m_wallpaperConfiguration->insert(QStringLiteral("Image"), m_defaultWallpaper);
0218     }
0219 
0220     connect(m_wallpaperConfiguration, &QQmlPropertyMap::valueChanged, this, [this](const QString & /* key */, const QVariant & /* value */) {
0221         setRepresentsDefaults(isDefault());
0222         setNeedsSave(m_configLoader->isSaveNeeded() || m_loadedWallpaperplugin != m_currentWallpaperPlugin);
0223     });
0224 }
0225 
0226 class WallpaperConfigModel : public PlasmaQuick::ConfigModel
0227 {
0228     Q_OBJECT
0229 
0230 public:
0231     WallpaperConfigModel(QObject *parent);
0232 public Q_SLOTS:
0233     void repopulate();
0234 };
0235 
0236 void WallpaperModule::onWallpaperChanged(uint screenIdx)
0237 {
0238     m_config->markAsClean();
0239     m_config->reparseConfiguration();
0240 
0241     int screen = screenIdFromName(m_selectedScreen->name());
0242     if (screen >= 0 && screenIdx == uint(screen)) {
0243         onScreenChanged();
0244     }
0245 }
0246 
0247 void WallpaperModule::setCurrentActivity(const QString &activityId)
0248 {
0249     if (m_activityId != activityId) {
0250         m_activityId = activityId;
0251         onScreenChanged();
0252     }
0253 }
0254 
0255 bool WallpaperModule::isDefault() const
0256 {
0257     if (m_currentWallpaperPlugin != QStringLiteral("org.kde.image")) {
0258         return false;
0259     }
0260     for (const auto &item : m_configLoader->items()) {
0261         if (!item->isDefault()) {
0262             if (item->name() == QStringLiteral("Image") && item->property() == m_defaultWallpaper) {
0263                 continue;
0264             }
0265             if (item->name() == QStringLiteral("SlidePaths")) {
0266                 continue;
0267             }
0268             return false;
0269         }
0270     }
0271     return true;
0272 }
0273 
0274 void WallpaperModule::defaults()
0275 {
0276     KQuickConfigModule::defaults();
0277 
0278     if (m_currentWallpaperPlugin != QStringLiteral("org.kde.image")) {
0279         setCurrentWallpaperPlugin(QStringLiteral("org.kde.image"));
0280         Q_EMIT currentWallpaperPluginChanged();
0281     }
0282 
0283     auto previousConfig = m_wallpaperConfiguration;
0284     disconnect(previousConfig, nullptr);
0285 
0286     setWallpaperPluginConfiguration(m_currentWallpaperPlugin, true);
0287     m_wallpaperConfiguration->insert(QStringLiteral("Image"), m_defaultWallpaper);
0288 
0289     setRepresentsDefaults(isDefault());
0290     setNeedsSave(m_configLoader->isSaveNeeded() || m_loadedWallpaperplugin != m_currentWallpaperPlugin);
0291 
0292     Q_EMIT wallpaperConfigurationChanged();
0293 
0294     delete previousConfig;
0295 }
0296 
0297 void WallpaperModule::load()
0298 {
0299     KQuickConfigModule::load();
0300     onScreenChanged();
0301 }
0302 
0303 void WallpaperModule::save()
0304 {
0305     KQuickConfigModule::save();
0306     auto iface = new QDBusInterface("org.kde.plasmashell", "/PlasmaShell", "org.kde.PlasmaShell", QDBusConnection::sessionBus(), this);
0307     if (!iface->isValid()) {
0308         qCWarning(KCM_WALLPAPER_DEBUG) << qPrintable(QDBusConnection::sessionBus().lastError().message());
0309         return;
0310     }
0311 
0312     QList<uint> screenIds;
0313 
0314     if (m_allScreens) {
0315         uint idx = 0;
0316         const auto outputOrder = m_outputOrderWatcher->outputOrder();
0317         for (const auto &output : outputOrder) {
0318             screenIds.append(idx);
0319             ++idx;
0320         }
0321     } else {
0322         int screenId = screenIdFromName(m_selectedScreen->name());
0323         if (screenId == -1) {
0324             qCWarning(KCM_WALLPAPER_DEBUG) << "Screen not found";
0325             return;
0326         }
0327         screenIds.append(uint(screenId));
0328     }
0329 
0330     QVariantMap params;
0331     for (const auto &key : m_wallpaperConfiguration->keys()) {
0332         params.insert(key, m_wallpaperConfiguration->value(key));
0333     }
0334 
0335     if (m_currentWallpaperPlugin == QLatin1String("org.kde.image")) {
0336         params.remove(QStringLiteral("PreviewImage"));
0337     }
0338 
0339     for (const uint screenId : screenIds) {
0340         const QDBusReply<void> response = iface->call(QStringLiteral("setWallpaper"), m_currentWallpaperPlugin, params, screenId);
0341         if (!response.isValid()) {
0342             qCWarning(KCM_WALLPAPER_DEBUG) << "failed to set wallpaper:" << response.error();
0343         }
0344     }
0345 
0346     setRepresentsDefaults(isDefault());
0347     Q_EMIT settingsSaved();
0348 }
0349 
0350 QQmlPropertyMap *WallpaperModule::wallpaperConfiguration() const
0351 {
0352     return m_wallpaperConfiguration;
0353 }
0354 
0355 QString WallpaperModule::currentWallpaperPlugin() const
0356 {
0357     return m_currentWallpaperPlugin;
0358 }
0359 
0360 bool WallpaperModule::allScreens() const
0361 {
0362     return m_allScreens;
0363 }
0364 
0365 void WallpaperModule::setAllScreens(const bool allScreens)
0366 {
0367     if (allScreens != m_allScreens) {
0368         m_allScreens = allScreens;
0369         setNeedsSave(m_allScreens || m_configLoader->isSaveNeeded() || m_loadedWallpaperplugin != m_currentWallpaperPlugin);
0370         Q_EMIT allScreensChanged();
0371     }
0372 }
0373 
0374 QList<QScreen *> WallpaperModule::screens() const
0375 {
0376     return m_screens;
0377 }
0378 
0379 QScreen *WallpaperModule::selectedScreen() const
0380 {
0381     return m_selectedScreen;
0382 }
0383 
0384 void WallpaperModule::setSelectedScreen(const QString &screenName)
0385 {
0386     const auto screens = qApp->screens();
0387     auto it = std::find_if(screens.constBegin(), screens.cend(), [screenName](const QScreen *screen) {
0388         return screen->name() == screenName;
0389     });
0390     if (it != screens.cend() && m_selectedScreen != *it) {
0391         m_selectedScreen = *it;
0392         Q_EMIT selectedScreenChanged();
0393 
0394         onScreenChanged();
0395     }
0396 }
0397 
0398 QString WallpaperModule::wallpaperPluginSource()
0399 {
0400     if (m_currentWallpaperPlugin.isEmpty()) {
0401         return QString();
0402     }
0403 
0404     const auto model = wallpaperConfigModel();
0405     const auto wallpaperPluginCount = model->count();
0406     for (int i = 0; i < wallpaperPluginCount; ++i) {
0407         if (model->data(model->index(i), PlasmaQuick::ConfigModel::PluginNameRole) == m_currentWallpaperPlugin) {
0408             return model->data(model->index(i), PlasmaQuick::ConfigModel::SourceRole).toString();
0409         }
0410     }
0411 
0412     return QString();
0413 }
0414 
0415 void WallpaperModule::setCurrentWallpaperPlugin(const QString &wallpaperPlugin)
0416 {
0417     if (wallpaperPlugin == m_currentWallpaperPlugin) {
0418         return;
0419     }
0420 
0421     m_currentWallpaperPlugin = wallpaperPlugin;
0422     auto previousConfig = m_wallpaperConfiguration;
0423     disconnect(previousConfig, nullptr);
0424 
0425     setWallpaperPluginConfiguration(m_currentWallpaperPlugin);
0426 
0427     setNeedsSave(needsSave() || m_loadedWallpaperplugin != m_currentWallpaperPlugin);
0428 
0429     Q_EMIT currentWallpaperPluginChanged();
0430 
0431     delete previousConfig;
0432 }
0433 
0434 PlasmaQuick::ConfigModel *WallpaperModule::wallpaperConfigModel()
0435 {
0436     if (!m_wallpaperConfigModel) {
0437         m_wallpaperConfigModel = new WallpaperConfigModel(this);
0438         QDBusConnection::sessionBus().connect(QString(),
0439                                               QStringLiteral("/KPackage/Plasma/Wallpaper"),
0440                                               QStringLiteral("org.kde.plasma.kpackage"),
0441                                               QStringLiteral("packageInstalled"),
0442                                               m_wallpaperConfigModel,
0443                                               SLOT(repopulate()));
0444         QDBusConnection::sessionBus().connect(QString(),
0445                                               QStringLiteral("/KPackage/Plasma/Wallpaper"),
0446                                               QStringLiteral("org.kde.plasma.kpackage"),
0447                                               QStringLiteral("packageUpdated"),
0448                                               m_wallpaperConfigModel,
0449                                               SLOT(repopulate()));
0450         QDBusConnection::sessionBus().connect(QString(),
0451                                               QStringLiteral("/KPackage/Plasma/Wallpaper"),
0452                                               QStringLiteral("org.kde.plasma.kpackage"),
0453                                               QStringLiteral("packageUninstalled"),
0454                                               m_wallpaperConfigModel,
0455                                               SLOT(repopulate()));
0456     }
0457     return m_wallpaperConfigModel;
0458 }
0459 
0460 WallpaperConfigModel::WallpaperConfigModel(QObject *parent)
0461     : PlasmaQuick::ConfigModel(parent)
0462 {
0463     repopulate();
0464 }
0465 
0466 void WallpaperConfigModel::repopulate()
0467 {
0468     clear();
0469     for (const KPluginMetaData &m : KPackage::PackageLoader::self()->listPackages(QStringLiteral("Plasma/Wallpaper"))) {
0470         KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Wallpaper"), m.pluginId());
0471         if (!pkg.isValid()) {
0472             continue;
0473         }
0474         appendCategory(pkg.metadata().iconName(), pkg.metadata().name(), pkg.fileUrl("ui", QStringLiteral("config.qml")).toString(), m.pluginId());
0475     }
0476 }
0477 
0478 #include "wallpapermodule.moc"