1 /*
2 SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7 #include "wallpaperinterface.h"
8
9 #include "containmentinterface.h"
10 #include <kdeclarative/configpropertymap.h>
11 #include <kdeclarative/qmlobjectsharedengine.h>
12
13 #include <KActionCollection>
14 #include <KConfigLoader>
15 #include <KDesktopFile>
16
17 #include <QDebug>
18 #include <QFile>
19 #include <QQmlContext>
20 #include <QQmlExpression>
21 #include <QQmlProperty>
22
23 #include <Plasma/PluginLoader>
24 #include <kpackage/packageloader.h>
25
26 QHash<QObject *, WallpaperInterface *> WallpaperInterface::s_rootObjects = QHash<QObject *, WallpaperInterface *>();
27
WallpaperInterface(ContainmentInterface * parent)28 WallpaperInterface::WallpaperInterface(ContainmentInterface *parent)
29 : QQuickItem(parent)
30 , m_containmentInterface(parent)
31 , m_qmlObject(nullptr)
32 , m_configuration(nullptr)
33 , m_configLoader(nullptr)
34 {
35 m_actions = new KActionCollection(this);
36
37 // resize at the beginning to avoid as much resize events as possible
38 if (parent) {
39 setSize(QSizeF(parent->width(), parent->height()));
40 }
41
42 if (!m_containmentInterface->containment()->wallpaper().isEmpty()) {
43 syncWallpaperPackage();
44 }
45 connect(m_containmentInterface->containment(), &Plasma::Containment::wallpaperChanged, this, &WallpaperInterface::syncWallpaperPackage);
46 }
47
~WallpaperInterface()48 WallpaperInterface::~WallpaperInterface()
49 {
50 if (m_qmlObject) {
51 s_rootObjects.remove(m_qmlObject->engine());
52 }
53 }
54
listWallpaperMetadataForMimetype(const QString & mimetype,const QString & formFactor)55 QList<KPluginMetaData> WallpaperInterface::listWallpaperMetadataForMimetype(const QString &mimetype, const QString &formFactor)
56 {
57 auto filter = [&mimetype, &formFactor](const KPluginMetaData &md) -> bool {
58 if (!formFactor.isEmpty() && !md.value(QStringLiteral("X-Plasma-FormFactors")).contains(formFactor)) {
59 return false;
60 }
61 return md.value(QStringLiteral("X-Plasma-DropMimeTypes"), QStringList()).contains(mimetype);
62 };
63 return KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Wallpaper"), QString(), filter);
64 }
65
kPackage() const66 KPackage::Package WallpaperInterface::kPackage() const
67 {
68 return m_pkg;
69 }
70
pluginName() const71 QString WallpaperInterface::pluginName() const
72 {
73 return m_wallpaperPlugin;
74 }
75
configuration() const76 KDeclarative::ConfigPropertyMap *WallpaperInterface::configuration() const
77 {
78 return m_configuration;
79 }
80
configScheme()81 KConfigLoader *WallpaperInterface::configScheme()
82 {
83 if (!m_configLoader) {
84 // FIXME: do we need "mainconfigxml" in wallpaper packagestructures?
85 const QString xmlPath = m_pkg.filePath("config", QStringLiteral("main.xml"));
86
87 KConfigGroup cfg = m_containmentInterface->containment()->config();
88 cfg = KConfigGroup(&cfg, "Wallpaper");
89 cfg = KConfigGroup(&cfg, m_wallpaperPlugin);
90
91 if (xmlPath.isEmpty()) {
92 m_configLoader = new KConfigLoader(cfg, nullptr, this);
93 } else {
94 QFile file(xmlPath);
95 m_configLoader = new KConfigLoader(cfg, &file, this);
96 }
97 }
98
99 return m_configLoader;
100 }
101
syncWallpaperPackage()102 void WallpaperInterface::syncWallpaperPackage()
103 {
104 if (m_wallpaperPlugin == m_containmentInterface->containment()->wallpaper() && m_qmlObject->rootObject()) {
105 return;
106 }
107
108 m_wallpaperPlugin = m_containmentInterface->containment()->wallpaper();
109
110 if (!m_qmlObject) {
111 m_qmlObject = new KDeclarative::QmlObjectSharedEngine(this);
112 s_rootObjects[m_qmlObject->engine()] = this;
113 m_qmlObject->setInitializationDelayed(true);
114 connect(m_qmlObject, &KDeclarative::QmlObject::finished, this, &WallpaperInterface::loadFinished);
115 }
116
117 m_actions->clear();
118 m_pkg = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Wallpaper"));
119 m_pkg.setPath(m_wallpaperPlugin);
120 if (!m_pkg.isValid()) {
121 qWarning() << "Error loading the wallpaper, no valid package loaded";
122 return;
123 }
124
125 if (m_configLoader) {
126 m_configLoader->deleteLater();
127 }
128 if (m_configuration) {
129 m_configuration->deleteLater();
130 }
131 m_configLoader = nullptr;
132 m_configuration = nullptr;
133 if (configScheme()) {
134 m_configuration = new KDeclarative::ConfigPropertyMap(configScheme(), this);
135 }
136
137 m_qmlObject->rootContext()->setContextProperty(QStringLiteral("wallpaper"), this);
138 m_qmlObject->setSource(m_pkg.fileUrl("mainscript"));
139
140 const QString rootPath = m_pkg.metadata().value(QStringLiteral("X-Plasma-RootPath"));
141 if (!rootPath.isEmpty()) {
142 m_qmlObject->setTranslationDomain(QLatin1String("plasma_wallpaper_") + rootPath);
143 } else {
144 m_qmlObject->setTranslationDomain(QLatin1String("plasma_wallpaper_") + m_pkg.metadata().pluginId());
145 }
146
147 // initialize with our size to avoid as much resize events as possible
148 QVariantHash props;
149 props[QStringLiteral("width")] = width();
150 props[QStringLiteral("height")] = height();
151 m_qmlObject->completeInitialization(props);
152 }
153
loadFinished()154 void WallpaperInterface::loadFinished()
155 {
156 if (m_qmlObject->mainComponent() //
157 && m_qmlObject->rootObject() //
158 && !m_qmlObject->mainComponent()->isError()) {
159 m_qmlObject->rootObject()->setProperty("z", -1000);
160 m_qmlObject->rootObject()->setProperty("parent", QVariant::fromValue(this));
161
162 // set anchors
163 QQmlExpression expr(m_qmlObject->engine()->rootContext(), m_qmlObject->rootObject(), QStringLiteral("parent"));
164 QQmlProperty prop(m_qmlObject->rootObject(), QStringLiteral("anchors.fill"));
165 prop.write(expr.evaluate());
166
167 } else if (m_qmlObject->mainComponent()) {
168 qWarning() << "Error loading the wallpaper" << m_qmlObject->mainComponent()->errors();
169 s_rootObjects.remove(m_qmlObject->engine());
170 m_qmlObject->deleteLater();
171 m_qmlObject = nullptr;
172
173 } else {
174 qWarning() << "Error loading the wallpaper, package not found";
175 }
176
177 Q_EMIT packageChanged();
178 Q_EMIT configurationChanged();
179 }
180
contextualActions() const181 QList<QAction *> WallpaperInterface::contextualActions() const
182 {
183 return m_actions->actions();
184 }
185
supportsMimetype(const QString & mimetype) const186 bool WallpaperInterface::supportsMimetype(const QString &mimetype) const
187 {
188 return m_pkg.metadata().value(QStringLiteral("X-Plasma-DropMimeTypes"), QStringList()).contains(mimetype);
189 }
190
setUrl(const QUrl & url)191 void WallpaperInterface::setUrl(const QUrl &url)
192 {
193 if (m_qmlObject->rootObject()) {
194 QMetaObject::invokeMethod(m_qmlObject->rootObject(), "setUrl", Qt::DirectConnection, Q_ARG(QVariant, QVariant::fromValue(url)));
195 }
196 }
197
setAction(const QString & name,const QString & text,const QString & icon,const QString & shortcut)198 void WallpaperInterface::setAction(const QString &name, const QString &text, const QString &icon, const QString &shortcut)
199 {
200 QAction *action = m_actions->action(name);
201
202 if (action) {
203 action->setText(text);
204 } else {
205 Q_ASSERT(!m_actions->action(name));
206 action = new QAction(text, this);
207 m_actions->addAction(name, action);
208
209 connect(action, &QAction::triggered, this, [this, name] {
210 executeAction(name);
211 });
212 }
213
214 if (!icon.isEmpty()) {
215 action->setIcon(QIcon::fromTheme(icon));
216 }
217
218 if (!shortcut.isEmpty()) {
219 action->setShortcut(shortcut);
220 }
221
222 action->setObjectName(name);
223 setProperty("contextualActions", QVariant::fromValue(contextualActions()));
224 }
225
removeAction(const QString & name)226 void WallpaperInterface::removeAction(const QString &name)
227 {
228 QAction *action = m_actions->action(name);
229
230 if (action) {
231 m_actions->removeAction(action);
232 }
233 setProperty("contextualActions", QVariant::fromValue(contextualActions()));
234 }
235
action(QString name) const236 QAction *WallpaperInterface::action(QString name) const
237 {
238 return m_actions->action(name);
239 }
240
executeAction(const QString & name)241 void WallpaperInterface::executeAction(const QString &name)
242 {
243 if (m_qmlObject->rootObject()) {
244 const QByteArray actionName("action_" + name.toUtf8());
245 QMetaObject::invokeMethod(m_qmlObject->rootObject(), actionName.constData(), Qt::DirectConnection);
246 }
247 }
248
qmlAttachedProperties(QObject * object)249 WallpaperInterface *WallpaperInterface::qmlAttachedProperties(QObject *object)
250 {
251 // at the moment of the attached object creation, the root item is the only one that hasn't a parent
252 // only way to avoid creation of this attached for everybody but the root item
253 return object->parent() ? nullptr : s_rootObjects.value(QtQml::qmlEngine(object));
254 }
255
isLoading() const256 bool WallpaperInterface::isLoading() const
257 {
258 return m_loading;
259 }
260
261 #include "moc_wallpaperinterface.cpp"
262