File indexing completed on 2024-04-28 16:55:02

0001 /*
0002     SPDX-FileCopyrightText: 2014 Bhushan Shah <bhush94@gmail.com>
0003     SPDX-FileCopyrightText: 2014 Marco Martin <notmart@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 #include "standaloneappcorona.h"
0009 #include "debug.h"
0010 #include "panelview.h"
0011 
0012 #include <QAction>
0013 #include <QDebug>
0014 #include <QFile>
0015 #include <QQuickItem>
0016 
0017 #include <KActionCollection>
0018 #include <Plasma/PluginLoader>
0019 #include <kactivities/consumer.h>
0020 
0021 #include <KPackage/Package>
0022 #include <KPackage/PackageLoader>
0023 
0024 #include "scripting/scriptengine.h"
0025 
0026 StandaloneAppCorona::StandaloneAppCorona(const QString &coronaPlugin, QObject *parent)
0027     : Plasma::Corona(parent)
0028     , m_coronaPlugin(coronaPlugin)
0029     , m_activityConsumer(new KActivities::Consumer(this))
0030     , m_view(nullptr)
0031 {
0032     qmlRegisterUncreatableType<DesktopView>("org.kde.plasma.shell", 2, 0, "Desktop", QStringLiteral("It is not possible to create objects of type Desktop"));
0033     qmlRegisterUncreatableType<PanelView>("org.kde.plasma.shell", 2, 0, "Panel", QStringLiteral("It is not possible to create objects of type Panel"));
0034 
0035     KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Shell"));
0036     package.setPath(m_coronaPlugin);
0037     package.setAllowExternalPaths(true);
0038     if (!package.isValid()) {
0039         qCritical() << "starting invalid corona" << m_coronaPlugin;
0040     }
0041     setKPackage(package);
0042 
0043     Plasma::Theme theme;
0044     theme.setUseGlobalSettings(false);
0045 
0046     KConfigGroup lnfCfg = KConfigGroup(KSharedConfig::openConfig(package.filePath("defaults")), "Theme");
0047     theme.setThemeName(lnfCfg.readEntry("name", "default"));
0048 
0049     m_desktopDefaultsConfig = KConfigGroup(KSharedConfig::openConfig(package.filePath("defaults")), "Desktop");
0050 
0051     m_view = new DesktopView(this);
0052 
0053     connect(m_activityConsumer, &KActivities::Consumer::currentActivityChanged, this, &StandaloneAppCorona::currentActivityChanged);
0054     connect(m_activityConsumer, &KActivities::Consumer::activityAdded, this, &StandaloneAppCorona::activityAdded);
0055     connect(m_activityConsumer, &KActivities::Consumer::activityRemoved, this, &StandaloneAppCorona::activityRemoved);
0056 
0057     connect(m_activityConsumer, &KActivities::Consumer::serviceStatusChanged, this, &StandaloneAppCorona::load);
0058 }
0059 
0060 StandaloneAppCorona::~StandaloneAppCorona()
0061 {
0062     delete m_view;
0063 }
0064 
0065 QRect StandaloneAppCorona::screenGeometry(int id) const
0066 {
0067     Q_UNUSED(id);
0068     if (m_view) {
0069         return m_view->geometry();
0070     } else {
0071         return QRect();
0072     }
0073 }
0074 
0075 void StandaloneAppCorona::load()
0076 {
0077     loadLayout("plasma-" + m_coronaPlugin + "-appletsrc");
0078 
0079     bool found = false;
0080     for (auto c : containments()) {
0081         if (c->containmentType() == Plasma::Types::DesktopContainment || c->containmentType() == Plasma::Types::CustomContainment) {
0082             found = true;
0083             break;
0084         }
0085     }
0086 
0087     if (!found) {
0088         qDebug() << "Loading default layout";
0089         loadDefaultLayout();
0090         saveLayout("plasma-" + m_coronaPlugin + "-appletsrc");
0091     }
0092 
0093     for (auto c : containments()) {
0094         qDebug() << "containment found";
0095         if (c->containmentType() == Plasma::Types::DesktopContainment || c->containmentType() == Plasma::Types::CustomContainment) {
0096             QAction *removeAction = c->actions()->action(QStringLiteral("remove"));
0097             if (removeAction) {
0098                 removeAction->deleteLater();
0099             }
0100             m_view->setContainment(c);
0101             m_view->show();
0102             connect(m_view, &QWindow::visibleChanged, [=](bool visible) {
0103                 if (!visible) {
0104                     deleteLater();
0105                 }
0106             });
0107             break;
0108         }
0109     }
0110 }
0111 
0112 void StandaloneAppCorona::loadDefaultLayout()
0113 {
0114     const QString script = kPackage().filePath("defaultlayout");
0115     QFile file(script);
0116     if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
0117         QString code = file.readAll();
0118         qDebug() << "evaluating startup script:" << script;
0119 
0120         WorkspaceScripting::ScriptEngine scriptEngine(this);
0121 
0122         connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::printError, this, [](const QString &msg) {
0123             qCWarning(PLASMASHELL) << msg;
0124         });
0125         connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::print, this, [](const QString &msg) {
0126             qDebug() << msg;
0127         });
0128         scriptEngine.evaluateScript(code);
0129     }
0130 }
0131 
0132 Plasma::Containment *StandaloneAppCorona::createContainmentForActivity(const QString &activity, int screenNum)
0133 {
0134     for (Plasma::Containment *cont : containments()) {
0135         if (cont->activity() == activity
0136             && (cont->containmentType() == Plasma::Types::DesktopContainment || cont->containmentType() == Plasma::Types::CustomContainment)) {
0137             return cont;
0138         }
0139     }
0140 
0141     Plasma::Containment *containment =
0142         containmentForScreen(screenNum, activity, m_desktopDefaultsConfig.readEntry("Containment", "org.kde.desktopcontainment"), QVariantList());
0143     Q_ASSERT(containment);
0144 
0145     if (containment) {
0146         containment->setActivity(activity);
0147     }
0148 
0149     return containment;
0150 }
0151 
0152 void StandaloneAppCorona::activityAdded(const QString &id)
0153 {
0154     // TODO more sanity checks
0155     if (m_activityContainmentPlugins.contains(id)) {
0156         qCWarning(PLASMASHELL) << "Activity added twice" << id;
0157         return;
0158     }
0159 
0160     m_activityContainmentPlugins.insert(id, QString());
0161 }
0162 
0163 void StandaloneAppCorona::activityRemoved(const QString &id)
0164 {
0165     m_activityContainmentPlugins.remove(id);
0166 }
0167 
0168 void StandaloneAppCorona::currentActivityChanged(const QString &newActivity)
0169 {
0170     // qDebug() << "Activity changed:" << newActivity;
0171 
0172     if (containments().isEmpty()) {
0173         return;
0174     }
0175 
0176     Plasma::Containment *c = createContainmentForActivity(newActivity, 0);
0177 
0178     connect(c, &Plasma::Containment::showAddWidgetsInterface, this, &StandaloneAppCorona::toggleWidgetExplorer);
0179 
0180     QAction *removeAction = c->actions()->action(QStringLiteral("remove"));
0181     if (removeAction) {
0182         removeAction->deleteLater();
0183     }
0184     m_view->setContainment(c);
0185 }
0186 
0187 void StandaloneAppCorona::toggleWidgetExplorer()
0188 {
0189     // The view QML has to provide something to display the widget explorer
0190     m_view->rootObject()->metaObject()->invokeMethod(m_view->rootObject(), "toggleWidgetExplorer", Q_ARG(QVariant, QVariant::fromValue(sender())));
0191     return;
0192 }
0193 
0194 QStringList StandaloneAppCorona::availableActivities() const
0195 {
0196     return m_activityContainmentPlugins.keys();
0197 }
0198 
0199 void StandaloneAppCorona::insertActivity(const QString &id, const QString &plugin)
0200 {
0201     m_activityContainmentPlugins.insert(id, plugin);
0202     Plasma::Containment *c = createContainmentForActivity(id, 0);
0203     if (c) {
0204         c->config().writeEntry("lastScreen", 0);
0205     }
0206 }
0207 
0208 Plasma::Containment *StandaloneAppCorona::addPanel(const QString &plugin)
0209 {
0210     // this creates a panel that wwill be used for nothing
0211     // it's needed by the scriptengine to create
0212     // a corona useful also when launched in fullshell
0213     Plasma::Containment *panel = createContainment(plugin);
0214     if (!panel) {
0215         return nullptr;
0216     }
0217 
0218     return panel;
0219 }
0220 
0221 int StandaloneAppCorona::screenForContainment(const Plasma::Containment *containment) const
0222 {
0223     // this simple corona doesn't have multiscreen support
0224     if (containment->containmentType() != Plasma::Types::PanelContainment && containment->containmentType() != Plasma::Types::CustomPanelContainment) {
0225         if (containment->activity() != m_activityConsumer->currentActivity()) {
0226             return -1;
0227         }
0228     }
0229     return 0;
0230 }