1 /*
2  *  SPDX-FileCopyrightText: 2013 Giorgos Tsiapaliokas <terietor@gmail.com>
3  *
4  *  SPDX-License-Identifier: GPL-2.0-or-later
5  */
6 
7 #include "view.h"
8 
9 #include <QDBusInterface>
10 #include <QDBusReply>
11 #include <QDebug>
12 #include <QFileDialog>
13 #include <QQmlContext>
14 #include <QQmlEngine>
15 #include <QQuickItem>
16 
17 #include <KDesktopFile>
18 #include <KLocalizedString>
19 
20 #include <KPackage/Package>
21 #include <KPackage/PackageLoader>
22 
23 class ViewerCorona : public Plasma::Corona
24 {
25 public:
ViewerCorona()26     ViewerCorona()
27         : Plasma::Corona()
28         , m_view(nullptr)
29     {
30     }
31 
setView(View * view)32     void setView(View *view)
33     {
34         m_view = view;
35     }
36 
screenGeometry(int id) const37     QRect screenGeometry(int id) const override
38     {
39         Q_UNUSED(id);
40         if (m_view) {
41             return m_view->geometry();
42         } else {
43             return QRect();
44         }
45     }
46 
screenForContainment(const Plasma::Containment * containment) const47     int screenForContainment(const Plasma::Containment *containment) const override
48     {
49         return 0;
50     }
51 
52 private:
53     View *m_view;
54 };
55 
View(ViewerCorona * cor,bool konsoleVisible,QWindow * parent)56 View::View(ViewerCorona *cor, bool konsoleVisible, QWindow *parent)
57     : PlasmaQuick::ContainmentView(cor, parent)
58 {
59     cor->setView(this);
60     m_konsoleVisible = konsoleVisible;
61     engine()->rootContext()->setContextProperty("desktop", this);
62     setSource(QUrl::fromLocalFile(cor->kPackage().filePath("views", "Desktop.qml")));
63 }
64 
~View()65 View::~View()
66 {
67 }
68 
resizeEvent(QResizeEvent * event)69 void View::resizeEvent(QResizeEvent *event)
70 {
71     emit corona()->screenGeometryChanged(0);
72     emit corona()->availableScreenRectChanged();
73     emit corona()->availableScreenRegionChanged();
74     PlasmaQuick::ContainmentView::resizeEvent(event);
75 }
76 
pluginFromPath(const QString & path) const77 QString View::pluginFromPath(const QString &path) const
78 {
79     QDir dir(path);
80     if (!dir.exists()) {
81         return QString();
82     }
83 
84     QString metadataPath = dir.absolutePath();
85     if (!QFile(metadataPath).exists()) {
86         return QString();
87     } else {
88         return metadataPath;
89     }
90 }
91 
addApplet(const QString & applet)92 void View::addApplet(const QString &applet)
93 {
94     QString metadataPath = pluginFromPath(applet);
95 
96     Plasma::Containment *c = containment();
97 
98     if (!c) {
99         qCritical("Containment doesn't exist");
100         return;
101     }
102 
103     Plasma::Applet *a = nullptr;
104     if (metadataPath.isEmpty()) {
105         a = containment()->createApplet(applet);
106     } else {
107         a = Plasma::Applet::loadPlasmoid(metadataPath);
108 
109         // Load translations from KPackage files if bundled
110         const QString localePath = a->kPackage().filePath("translations");
111         if (!localePath.isEmpty()) {
112             const QString localeDomain = QByteArray("plasma_applet_") + a->pluginMetaData().pluginId();
113             KLocalizedString::addDomainLocaleDir(localeDomain.toLatin1(), localePath);
114         }
115 
116         containment()->addApplet(a);
117     }
118 
119     if (!a->pluginMetaData().isValid()) {
120         // xgettext:no-c-format
121         qCritical() << i18n("Applet %1 does not exist.", applet);
122         return;
123     }
124     m_lastAppletName = applet;
125 }
126 
addContainment(const QString & cont)127 void View::addContainment(const QString &cont)
128 {
129     QString actualCont = pluginFromPath(cont);
130     if (actualCont.isEmpty()) {
131         actualCont = cont;
132     }
133 
134     Plasma::Containment *c = corona()->createContainment(actualCont);
135 
136     if (!c->pluginMetaData().isValid()) {
137         // xgettext:no-c-format
138         qCritical() << i18n("Containment %1 does not exist.", actualCont);
139         return;
140     }
141 
142     setContainment(c);
143 
144     connect(containment(), &Plasma::Containment::appletRemoved, [=](Plasma::Applet *applet) {
145         if (applet && applet->pluginMetaData().isValid()) {
146             addApplet(applet->pluginMetaData().pluginId());
147         }
148     });
149 }
150 
addFormFactor(const QString & formFactor)151 void View::addFormFactor(const QString &formFactor)
152 {
153     Plasma::Types::FormFactor formFactorType = Plasma::Types::Planar;
154     const QString ff = formFactor.toLower();
155 
156     if (ff.isEmpty() || ff == QStringLiteral("planar")) {
157         formFactorType = Plasma::Types::Planar;
158     } else if (ff == QStringLiteral("vertical")) {
159         formFactorType = Plasma::Types::Vertical;
160     } else if (ff == QStringLiteral("horizontal")) {
161         formFactorType = Plasma::Types::Horizontal;
162     } else if (ff == QStringLiteral("mediacenter")) {
163         formFactorType = Plasma::Types::MediaCenter;
164     } else if (ff == QStringLiteral("application")) {
165         formFactorType = Plasma::Types::Application;
166     } else {
167         qWarning() << "FormFactor " << ff << "doesn't exist. Planar formFactor has been used!!";
168     }
169 
170     Plasma::Containment *c = containment();
171     if (!c) {
172         qCritical("Containment doesn't exist!");
173         return;
174     }
175 
176     c->setFormFactor(formFactorType);
177 }
178 
changeFormFactor(int formFactor)179 void View::changeFormFactor(int formFactor)
180 {
181     QString formFactorType = "planar";
182     switch (formFactor) {
183     case Plasma::Types::Planar:
184         formFactorType = "planar";
185         break;
186     case Plasma::Types::Vertical:
187         formFactorType = "vertical";
188         break;
189     case Plasma::Types::Horizontal:
190         formFactorType = "horizontal";
191         break;
192     case Plasma::Types::MediaCenter:
193         formFactorType = "mediacenter";
194         break;
195     case Plasma::Types::Application:
196         formFactorType = "application";
197         break;
198     }
199 
200     addFormFactor(formFactorType);
201 }
202 
addLocation(const QString & location)203 void View::addLocation(const QString &location)
204 {
205     Plasma::Types::Location locationType = Plasma::Types::Floating;
206 
207     const QString l = location.toLower();
208 
209     if (l.isEmpty() || l == QStringLiteral("floating")) {
210         locationType = Plasma::Types::Floating;
211     } else if (l == QStringLiteral("desktop")) {
212         locationType = Plasma::Types::Desktop;
213     } else if (l == QStringLiteral("fullscreen")) {
214         locationType = Plasma::Types::FullScreen;
215     } else if (l == QStringLiteral("topedge")) {
216         locationType = Plasma::Types::TopEdge;
217     } else if (l == QStringLiteral("bottomedge")) {
218         locationType = Plasma::Types::BottomEdge;
219     } else if (l == QStringLiteral("rightedge")) {
220         locationType = Plasma::Types::RightEdge;
221     } else if (l == QStringLiteral("leftedge")) {
222         locationType = Plasma::Types::LeftEdge;
223     } else {
224         qWarning() << "Location " << l << "doesn't exist. Floating location has been used!!";
225     }
226 
227     Plasma::Containment *c = containment();
228     if (!c) {
229         qCritical("Containment doesn't exist!");
230         return;
231     }
232 
233     setLocation(locationType);
234 }
235 
emitExternalData(const QString & data)236 void View::emitExternalData(const QString &data)
237 {
238     if (data.isEmpty()) {
239         return;
240     }
241 
242     Plasma::Applet *applet = containment()->applets().constFirst();
243 
244     QObject *graphicsObject = qobject_cast<QQuickItem *>(applet->property("_plasma_graphicObject").value<QObject *>());
245     if (!graphicsObject) {
246         return;
247     }
248 
249     QMetaObject::invokeMethod(graphicsObject, "externalData", Q_ARG(QString, QString()), Q_ARG(QVariant, data));
250 }
251 
konsoleVisible()252 bool View::konsoleVisible()
253 {
254     return m_konsoleVisible;
255 }
256 
changeLocation(int location)257 void View::changeLocation(int location)
258 {
259     QString locationType = "floating";
260     switch (location) {
261     case Plasma::Types::Floating:
262         locationType = "floating";
263         break;
264     case Plasma::Types::Desktop:
265         locationType = "desktop";
266         break;
267     case Plasma::Types::FullScreen:
268         locationType = "fullscreen";
269         break;
270     case Plasma::Types::TopEdge:
271         locationType = "topedge";
272         break;
273     case Plasma::Types::BottomEdge:
274         locationType = "bottomedge";
275         break;
276     case Plasma::Types::RightEdge:
277         locationType = "rightedge";
278         break;
279     case Plasma::Types::LeftEdge:
280         locationType = "leftedge";
281         break;
282     }
283 
284     addLocation(locationType);
285 }
286 
createCorona()287 ViewerCorona *View::createCorona()
288 {
289     KPackage::Package package = KPackage::PackageLoader::self()->loadPackage("Plasma/Shell");
290     package.setPath("org.kde.plasma.plasmoidviewershell");
291 
292     ViewerCorona *cor = new ViewerCorona();
293     cor->setKPackage(package);
294 
295     return cor;
296 }
297 
takeScreenShot()298 void View::takeScreenShot()
299 {
300     QDBusInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Screenshot"), QStringLiteral("org.kde.kwin.Screenshot"));
301 
302     QDBusPendingCall async = interface.asyncCall(QStringLiteral("screenshotArea"), x(), y(), width(), height());
303 
304     QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this);
305 
306     connect(watcher, &QDBusPendingCallWatcher::finished, [](QDBusPendingCallWatcher *call) {
307         QDBusPendingReply<QString> reply = *call;
308         call->deleteLater();
309 
310         if (!reply.isValid()) {
311             qDebug() << "The screenshot has failed, the reply is invalid with error" << reply.error().message();
312             return;
313         }
314 
315         QString dest = QFileDialog::getSaveFileName(nullptr, i18nc("@title:window", "Save Screenshot"), QDir::homePath(), QStringLiteral("Images (*.png)"));
316 
317         if (dest.isEmpty()) {
318             return;
319         }
320 
321         if (!dest.endsWith(QStringLiteral(".png"))) {
322             dest.append(QStringLiteral(".png"));
323         }
324 
325         QFile f(reply.value());
326         f.rename(dest);
327     });
328 }
329 
330 #include "moc_view.cpp"
331