diff --git a/applets/systemtray/systemtray.cpp b/applets/systemtray/systemtray.cpp index b4211d786..af82a0030 100644 --- a/applets/systemtray/systemtray.cpp +++ b/applets/systemtray/systemtray.cpp @@ -1,557 +1,557 @@ /*************************************************************************** * Copyright (C) 2015 Marco Martin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "systemtray.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class PlasmoidModel: public QStandardItemModel { public: PlasmoidModel(QObject *parent = 0) : QStandardItemModel(parent) { } QHash roleNames() const override { QHash roles = QStandardItemModel::roleNames(); roles[Qt::UserRole+1] = "plugin"; return roles; } }; SystemTray::SystemTray(QObject *parent, const QVariantList &args) : Plasma::Containment(parent, args), m_availablePlasmoidsModel(nullptr) { setHasConfigurationInterface(true); setContainmentType(Plasma::Types::CustomEmbeddedContainment); } SystemTray::~SystemTray() { } void SystemTray::init() { Containment::init(); - for (const auto &info: Plasma::PluginLoader::self()->listAppletInfo(QString())) { - if (!info.isValid() || info.property(QStringLiteral("X-Plasma-NotificationArea")) != "true") { + for (const auto &info: Plasma::PluginLoader::self()->listAppletMetaData(QString())) { + if (!info.isValid() || info.value(QStringLiteral("X-Plasma-NotificationArea")) != "true") { continue; } - m_systrayApplets[info.pluginName()] = info; + m_systrayApplets[info.pluginId()] = KPluginInfo(info); - if (info.isPluginEnabledByDefault()) { - m_defaultPlasmoids += info.pluginName(); + if (info.isEnabledByDefault()) { + m_defaultPlasmoids += info.pluginId(); } - const QString dbusactivation = info.property(QStringLiteral("X-Plasma-DBusActivationService")).toString(); + const QString dbusactivation = info.value(QStringLiteral("X-Plasma-DBusActivationService")); if (!dbusactivation.isEmpty()) { - qCDebug(SYSTEM_TRAY) << "ST Found DBus-able Applet: " << info.pluginName() << dbusactivation; - m_dbusActivatableTasks[info.pluginName()] = dbusactivation; + qCDebug(SYSTEM_TRAY) << "ST Found DBus-able Applet: " << info.pluginId() << dbusactivation; + m_dbusActivatableTasks[info.pluginId()] = dbusactivation; } } } void SystemTray::newTask(const QString &task) { foreach (Plasma::Applet *applet, applets()) { - if (!applet->pluginInfo().isValid()) { + if (!applet->pluginMetaData().isValid()) { continue; } //only allow one instance per applet - if (task == applet->pluginInfo().pluginName()) { + if (task == applet->pluginMetaData().pluginId()) { //Applet::destroy doesn't delete the applet from Containment::applets in the same event //potentially a dbus activated service being restarted can be added in this time. if (!applet->destroyed()) { return; } } } //known one, recycle the id to reuse old config if (m_knownPlugins.contains(task)) { Applet *applet = Plasma::PluginLoader::self()->loadApplet(task, m_knownPlugins.value(task), QVariantList()); //this should never happen unless explicitly wrong config is hand-written or //(more likely) a previously added applet is uninstalled if (!applet) { qWarning() << "Unable to find applet" << task; return; } applet->setProperty("org.kde.plasma:force-create", true); addApplet(applet); //create a new one automatic id, new config group } else { Applet * applet = createApplet(task, QVariantList() << "org.kde.plasma:force-create"); if (applet) { m_knownPlugins[task] = applet->id(); } } } void SystemTray::cleanupTask(const QString &task) { foreach (Plasma::Applet *applet, applets()) { - if (!applet->pluginInfo().isValid() || task == applet->pluginInfo().pluginName()) { + if (!applet->pluginMetaData().isValid() || task == applet->pluginMetaData().pluginId()) { //we are *not* cleaning the config here, because since is one //of those automatically loaded/unloaded by dbus, we want to recycle //the config the next time it's loaded, in case the user configured something here applet->deleteLater(); //HACK: we need to remove the applet from Containment::applets() as soon as possible //otherwise we may have disappearing applets for restarting dbus services //this may be removed when we depend from a frameworks version in which appletDeleted is emitted as soon as deleteLater() is called emit appletDeleted(applet); } } } void SystemTray::showPlasmoidMenu(QQuickItem *appletInterface, int x, int y) { if (!appletInterface) { return; } Plasma::Applet *applet = appletInterface->property("_plasma_applet").value(); QPointF pos = appletInterface->mapToScene(QPointF(x, y)); if (appletInterface->window() && appletInterface->window()->screen()) { pos = appletInterface->window()->mapToGlobal(pos.toPoint()); } else { pos = QPoint(); } QMenu *desktopMenu = new QMenu; connect(this, &QObject::destroyed, desktopMenu, &QMenu::close); desktopMenu->setAttribute(Qt::WA_DeleteOnClose); emit applet->contextualActionsAboutToShow(); foreach (QAction *action, applet->contextualActions()) { if (action) { desktopMenu->addAction(action); } } QAction *runAssociatedApplication = applet->actions()->action(QStringLiteral("run associated application")); if (runAssociatedApplication && runAssociatedApplication->isEnabled()) { desktopMenu->addAction(runAssociatedApplication); } if (applet->actions()->action(QStringLiteral("configure"))) { desktopMenu->addAction(applet->actions()->action(QStringLiteral("configure"))); } if (desktopMenu->isEmpty()) { delete desktopMenu; return; } desktopMenu->adjustSize(); if (QScreen *screen = appletInterface->window()->screen()) { const QRect geo = screen->availableGeometry(); pos = QPoint(qBound(geo.left(), (int)pos.x(), geo.right() - desktopMenu->width()), qBound(geo.top(), (int)pos.y(), geo.bottom() - desktopMenu->height())); } desktopMenu->popup(pos.toPoint()); } Q_INVOKABLE QString SystemTray::plasmoidCategory(QQuickItem *appletInterface) const { if (!appletInterface) { return "UnknownCategory"; } Plasma::Applet *applet = appletInterface->property("_plasma_applet").value(); - if (!applet || !applet->pluginInfo().isValid()) { + if (!applet || !applet->pluginMetaData().isValid()) { return "UnknownCategory"; } - const QString cat = applet->pluginInfo().property(QStringLiteral("X-Plasma-NotificationAreaCategory")).toString(); + const QString cat = applet->pluginMetaData().value(QStringLiteral("X-Plasma-NotificationAreaCategory")); if (cat.isEmpty()) { return "UnknownCategory"; } return cat; } void SystemTray::showStatusNotifierContextMenu(KJob *job, QQuickItem *statusNotifierIcon) { if (QCoreApplication::closingDown() || !statusNotifierIcon) { // apparently an edge case can be triggered due to the async nature of all this // see: https://bugs.kde.org/show_bug.cgi?id=251977 return; } Plasma::ServiceJob *sjob = qobject_cast(job); if (!sjob) { return; } QMenu *menu = qobject_cast(sjob->result().value()); if (menu) { menu->adjustSize(); const auto parameters = sjob->parameters(); int x = parameters[QStringLiteral("x")].toInt(); int y = parameters[QStringLiteral("y")].toInt(); //try tofind the icon screen coordinates, and adjust the position as a poor //man's popupPosition QRect screenItemRect(statusNotifierIcon->mapToScene(QPointF(0, 0)).toPoint(), QSize(statusNotifierIcon->width(), statusNotifierIcon->height())); if (statusNotifierIcon->window()) { screenItemRect.moveTopLeft(statusNotifierIcon->window()->mapToGlobal(screenItemRect.topLeft())); } switch (location()) { case Plasma::Types::LeftEdge: x = screenItemRect.right(); y = screenItemRect.top(); break; case Plasma::Types::RightEdge: x = screenItemRect.left() - menu->width(); y = screenItemRect.top(); break; case Plasma::Types::TopEdge: x = screenItemRect.left(); y = screenItemRect.bottom(); break; case Plasma::Types::BottomEdge: x = screenItemRect.left(); y = screenItemRect.top() - menu->height(); break; default: x = screenItemRect.left(); if (screenItemRect.top() - menu->height() >= statusNotifierIcon->window()->screen()->geometry().top()) { y = screenItemRect.top() - menu->height(); } else { y = screenItemRect.bottom(); } } menu->popup(QPoint(x, y)); } } QPointF SystemTray::popupPosition(QQuickItem* visualParent, int x, int y) { if (!visualParent) { return QPointF(0, 0); } QPointF pos = visualParent->mapToScene(QPointF(x, y)); if (visualParent->window() && visualParent->window()->screen()) { pos = visualParent->window()->mapToGlobal(pos.toPoint()); } else { return QPoint(); } return pos; } void SystemTray::reorderItemBefore(QQuickItem* before, QQuickItem* after) { if (!before || !after) { return; } before->setVisible(false); before->setParentItem(after->parentItem()); before->stackBefore(after); before->setVisible(true); } void SystemTray::reorderItemAfter(QQuickItem* after, QQuickItem* before) { if (!before || !after) { return; } after->setVisible(false); after->setParentItem(before->parentItem()); after->stackAfter(before); after->setVisible(true); } bool SystemTray::isSystemTrayApplet(const QString &appletId) { return m_systrayApplets.contains(appletId); } void SystemTray::restoreContents(KConfigGroup &group) { Q_UNUSED(group); //NOTE: RestoreContents shouldnn't do anything here because is too soon, so have an empty reimplementation } void SystemTray::restorePlasmoids() { if (!isContainment()) { qCWarning(SYSTEM_TRAY) << "Loaded as an applet, this shouldn't have happened"; return; } //First: remove all that are not allowed anymore foreach (Plasma::Applet *applet, applets()) { //Here it should always be valid. //for some reason it not always is. - if (!applet->pluginInfo().isValid()) { + if (!applet->pluginMetaData().isValid()) { applet->config().parent().deleteGroup(); applet->deleteLater(); } else { - const QString task = applet->pluginInfo().pluginName(); + const QString task = applet->pluginMetaData().pluginId(); if (!m_allowedPlasmoids.contains(task)) { //in those cases we do delete the applet config completely //as they were explicitly disabled by the user applet->config().parent().deleteGroup(); applet->deleteLater(); } } } KConfigGroup cg = config(); cg = KConfigGroup(&cg, "Applets"); foreach (const QString &group, cg.groupList()) { KConfigGroup appletConfig(&cg, group); QString plugin = appletConfig.readEntry("plugin"); if (!plugin.isEmpty()) { m_knownPlugins[plugin] = group.toInt(); } } QStringList ownApplets; QMap sortedApplets; for (auto it = m_systrayApplets.constBegin(); it != m_systrayApplets.constEnd(); ++it) { const KPluginInfo &info = it.value(); if (m_allowedPlasmoids.contains(info.pluginName()) && ! m_dbusActivatableTasks.contains(info.pluginName())) { //FIXME // if we already have a plugin with this exact name in it, then check if it is the // same plugin and skip it if it is indeed already listed if (sortedApplets.contains(info.name())) { bool dupe = false; // it is possible (though poor form) to have multiple applets // with the same visible name but different plugins, so we hve to check all values foreach (const KPluginInfo &existingInfo, sortedApplets.values(info.name())) { if (existingInfo.pluginName() == info.pluginName()) { dupe = true; break; } } if (dupe) { continue; } } // insertMulti becase it is possible (though poor form) to have multiple applets // with the same visible name but different plugins sortedApplets.insertMulti(info.name(), info); } } foreach (const KPluginInfo &info, sortedApplets) { qCDebug(SYSTEM_TRAY) << " Adding applet: " << info.name(); if (m_allowedPlasmoids.contains(info.pluginName())) { newTask(info.pluginName()); } } initDBusActivatables(); } QStringList SystemTray::defaultPlasmoids() const { return m_defaultPlasmoids; } QAbstractItemModel* SystemTray::availablePlasmoids() { if (!m_availablePlasmoidsModel) { m_availablePlasmoidsModel = new PlasmoidModel(this); foreach (const KPluginInfo &info, m_systrayApplets) { QString name = info.name(); const QString dbusactivation = info.property(QStringLiteral("X-Plasma-DBusActivationService")).toString(); if (!dbusactivation.isEmpty()) { name += i18n(" (Automatic load)"); } QStandardItem *item = new QStandardItem(QIcon::fromTheme(info.icon()), name); item->setData(info.pluginName()); m_availablePlasmoidsModel->appendRow(item); } } return m_availablePlasmoidsModel; } QStringList SystemTray::allowedPlasmoids() const { return m_allowedPlasmoids; } void SystemTray::setAllowedPlasmoids(const QStringList &allowed) { if (allowed == m_allowedPlasmoids) { return; } m_allowedPlasmoids = allowed; restorePlasmoids(); emit allowedPlasmoidsChanged(); } void SystemTray::initDBusActivatables() { /* Loading and unloading Plasmoids when dbus services come and go * * This works as follows: * - we collect a list of plugins and related services in m_dbusActivatableTasks * - we query DBus for the list of services, async (initDBusActivatables()) * - we go over that list, adding tasks when a service and plugin match (serviceNameFetchFinished()) * - we start watching for new services, and do the same (serviceNameFetchFinished()) * - whenever a service is gone, we check whether to unload a Plasmoid (serviceUnregistered()) */ QDBusPendingCall async = QDBusConnection::sessionBus().interface()->asyncCall(QStringLiteral("ListNames")); QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(async, this); connect(callWatcher, &QDBusPendingCallWatcher::finished, [=](QDBusPendingCallWatcher *callWatcher){ SystemTray::serviceNameFetchFinished(callWatcher, QDBusConnection::sessionBus()); }); QDBusPendingCall systemAsync = QDBusConnection::systemBus().interface()->asyncCall(QStringLiteral("ListNames")); QDBusPendingCallWatcher *systemCallWatcher = new QDBusPendingCallWatcher(systemAsync, this); connect(systemCallWatcher, &QDBusPendingCallWatcher::finished, [=](QDBusPendingCallWatcher *callWatcher){ SystemTray::serviceNameFetchFinished(callWatcher, QDBusConnection::systemBus()); }); } void SystemTray::serviceNameFetchFinished(QDBusPendingCallWatcher* watcher, const QDBusConnection &connection) { QDBusPendingReply propsReply = *watcher; watcher->deleteLater(); if (propsReply.isError()) { qCWarning(SYSTEM_TRAY) << "Could not get list of available D-Bus services"; } else { foreach (const QString& serviceName, propsReply.value()) { serviceRegistered(serviceName); } } // Watch for new services // We need to watch for all of new services here, since we want to "match" the names, // not just compare them // This makes mpris work, since it wants to match org.mpris.MediaPlayer2.dragonplayer // against org.mpris.MediaPlayer2 // QDBusServiceWatcher is not capable for watching wildcard service right now // See: // https://bugreports.qt.io/browse/QTBUG-51683 // https://bugreports.qt.io/browse/QTBUG-33829 connect(connection.interface(), &QDBusConnectionInterface::serviceOwnerChanged, this, &SystemTray::serviceOwnerChanged); } void SystemTray::serviceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner) { if (oldOwner.isEmpty()) { serviceRegistered(serviceName); } else if (newOwner.isEmpty()) { serviceUnregistered(serviceName); } } void SystemTray::serviceRegistered(const QString &service) { //qCDebug(SYSTEM_TRAY) << "DBus service appeared:" << service; for (auto it = m_dbusActivatableTasks.constBegin(), end = m_dbusActivatableTasks.constEnd(); it != end; ++it) { const QString &plugin = it.key(); if (!m_allowedPlasmoids.contains(plugin)) { continue; } const QString &pattern = it.value(); QRegExp rx(pattern); rx.setPatternSyntax(QRegExp::Wildcard); if (rx.exactMatch(service)) { //qCDebug(SYSTEM_TRAY) << "ST : DBus service " << m_dbusActivatableTasks[plugin] << "appeared. Loading " << plugin; newTask(plugin); m_dbusServiceCounts[plugin]++; } } } void SystemTray::serviceUnregistered(const QString &service) { //qCDebug(SYSTEM_TRAY) << "DBus service disappeared:" << service; for (auto it = m_dbusActivatableTasks.constBegin(), end = m_dbusActivatableTasks.constEnd(); it != end; ++it) { const QString &plugin = it.key(); if (!m_allowedPlasmoids.contains(plugin)) { continue; } const QString &pattern = it.value(); QRegExp rx(pattern); rx.setPatternSyntax(QRegExp::Wildcard); if (rx.exactMatch(service)) { m_dbusServiceCounts[plugin]--; Q_ASSERT(m_dbusServiceCounts[plugin] >= 0); if (m_dbusServiceCounts[plugin] == 0) { //qCDebug(SYSTEM_TRAY) << "ST : DBus service " << m_dbusActivatableTasks[plugin] << " disappeared. Unloading " << plugin; cleanupTask(plugin); } } } } K_EXPORT_PLASMA_APPLET_WITH_JSON(systemtray, SystemTray, "metadata.json") #include "systemtray.moc" diff --git a/components/shellprivate/widgetexplorer/widgetexplorer.cpp b/components/shellprivate/widgetexplorer/widgetexplorer.cpp index 848aa4745..74fa2b0dc 100644 --- a/components/shellprivate/widgetexplorer/widgetexplorer.cpp +++ b/components/shellprivate/widgetexplorer/widgetexplorer.cpp @@ -1,509 +1,509 @@ /* * Copyright (C) 2007 by Ivan Cukic * Copyright (C) 2009 by Ana Cecília Martins * Copyright 2013 by Sebastian Kügler * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library/Lesser General Public License * version 2, or (at your option) any later version, as published by the * Free Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library/Lesser General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "widgetexplorer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kcategorizeditemsviewmodels_p.h" #include "plasmaappletitemmodel_p.h" #include "openwidgetassistant_p.h" #include "config-workspace.h" using namespace KActivities; using namespace KCategorizedItemsViewModels; using namespace Plasma; WidgetAction::WidgetAction(QObject *parent) : QAction(parent) { } WidgetAction::WidgetAction(const QIcon &icon, const QString &text, QObject *parent) : QAction(icon, text, parent) { } class WidgetExplorerPrivate { public: WidgetExplorerPrivate(WidgetExplorer *w) : q(w), containment(0), itemModel(w), filterModel(w), activitiesConsumer(new KActivities::Consumer()) { QObject::connect(activitiesConsumer.data(), &Consumer::currentActivityChanged, q, [this] { initRunningApplets(); }); } void initFilters(); void initRunningApplets(); void containmentDestroyed(); void addContainment(Containment *containment); /** * Tracks a new running applet */ void appletAdded(Plasma::Applet *applet); /** * A running applet is no more */ void appletRemoved(Plasma::Applet *applet); WidgetExplorer *q; QString application; Plasma::Containment *containment; QHash runningApplets; // applet name => count //extra hash so we can look up the names of deleted applets QHash appletNames; QPointer openAssistant; KPackage::Package *package; PlasmaAppletItemModel itemModel; KCategorizedItemsViewModels::DefaultFilterModel filterModel; bool showSpecialFilters = true; DefaultItemFilterProxyModel filterItemModel; QPointer newStuffDialog; QScopedPointer activitiesConsumer; }; void WidgetExplorerPrivate::initFilters() { filterModel.clear(); if (showSpecialFilters) { filterModel.addFilter(i18n("All Widgets"), KCategorizedItemsViewModels::Filter(), QIcon::fromTheme(QStringLiteral("plasma"))); // Filters: Special filterModel.addFilter(i18n("Running"), KCategorizedItemsViewModels::Filter(QStringLiteral("running"), true), QIcon::fromTheme(QStringLiteral("dialog-ok"))); filterModel.addFilter(i18n("Uninstallable"), KCategorizedItemsViewModels::Filter(QStringLiteral("local"), true), QIcon::fromTheme(QStringLiteral("list-remove"))); filterModel.addSeparator(i18n("Categories:")); } typedef QPair catPair; QMap categories; QSet existingCategories = itemModel.categories(); //foreach (const QString &category, Plasma::Applet::listCategories(application)) { QStringList cats; const QList list = PluginLoader::self()->listAppletInfo(QString()); for (auto& info : list) { if (!info.isValid()) { continue; } if (info.property(QStringLiteral("NoDisplay")).toBool() || info.category() == QLatin1String("Containments") || info.category().isEmpty()) { // we don't want to show the hidden category continue; } const QString c = info.category(); if (-1 == cats.indexOf(c)) { cats << c; } } qWarning() << "TODO: port listCategories()"; foreach (const QString &category, cats) { const QString lowerCaseCat = category.toLower(); if (existingCategories.contains(lowerCaseCat)) { const QString trans = i18nd("libplasma5", category.toLocal8Bit()); categories.insert(trans.toLower(), qMakePair(trans, lowerCaseCat)); } } foreach (const catPair &category, categories) { filterModel.addFilter(category.first, KCategorizedItemsViewModels::Filter(QStringLiteral("category"), category.second)); } } void WidgetExplorer::classBegin() { } void WidgetExplorer::componentComplete() { setApplication(); d->initRunningApplets(); } QObject *WidgetExplorer::widgetsModel() const { return &d->filterItemModel; } QObject *WidgetExplorer::filterModel() const { return &d->filterModel; } bool WidgetExplorer::showSpecialFilters() const { return d->showSpecialFilters; } void WidgetExplorer::setShowSpecialFilters(bool show) { if (d->showSpecialFilters != show) { d->showSpecialFilters = show; d->initFilters(); emit showSpecialFiltersChanged(); } } QList WidgetExplorer::widgetsMenuActions() { QList actionList; QSignalMapper *mapper = new QSignalMapper(this); QObject::connect(mapper, SIGNAL(mapped(QString)), this, SLOT(downloadWidgets(QString))); WidgetAction *action = new WidgetAction(QIcon::fromTheme(QStringLiteral("applications-internet")), i18n("Download New Plasma Widgets"), this); QObject::connect(action, SIGNAL(triggered(bool)), mapper, SLOT(map())); mapper->setMapping(action, QString()); actionList << action; action = new WidgetAction(this); action->setSeparator(true); actionList << action; action = new WidgetAction(QIcon::fromTheme(QStringLiteral("package-x-generic")), i18n("Install Widget From Local File..."), this); QObject::connect(action, &QAction::triggered, this, &WidgetExplorer::openWidgetFile); actionList << action; return actionList; } QList WidgetExplorer::extraActions() const { QList actionList; // foreach (QAction *action, actions()) { // FIXME: where did actions() come from? // actionList << action; // } qWarning() << "extraactions needs reimplementation"; return actionList; } void WidgetExplorerPrivate::initRunningApplets() { //get applets from corona, count them, send results to model if (!containment) { return; } Plasma::Corona *c = containment->corona(); //we've tried our best to get a corona //we don't want just one containment, we want them all if (!c) { qWarning() << "WidgetExplorer failed to find corona"; return; } appletNames.clear(); runningApplets.clear(); const QList containments = c->containments(); for (Containment *containment : containments) { if (containment->containmentType() == Plasma::Types::DesktopContainment && containment->activity() != activitiesConsumer->currentActivity()) { continue; } addContainment(containment); } //qDebug() << runningApplets; itemModel.setRunningApplets(runningApplets); } void WidgetExplorerPrivate::addContainment(Containment *containment) { QObject::connect(containment, SIGNAL(appletAdded(Plasma::Applet*)), q, SLOT(appletAdded(Plasma::Applet*))); QObject::connect(containment, SIGNAL(appletRemoved(Plasma::Applet*)), q, SLOT(appletRemoved(Plasma::Applet*))); foreach (Applet *applet, containment->applets()) { - if (applet->pluginInfo().isValid()) { + if (applet->pluginMetaData().isValid()) { Containment *childContainment = applet->property("containment").value(); if (childContainment) { addContainment(childContainment); } - runningApplets[applet->pluginInfo().pluginName()]++; + runningApplets[applet->pluginMetaData().pluginId()]++; } else { - qDebug() << "Invalid plugininfo. :("; + qDebug() << "Invalid plugin metadata. :("; } } } void WidgetExplorerPrivate::containmentDestroyed() { containment = 0; } void WidgetExplorerPrivate::appletAdded(Plasma::Applet *applet) { - if (!applet->pluginInfo().isValid()) { + if (!applet->pluginMetaData().isValid()) { return; } - QString name = applet->pluginInfo().pluginName(); + QString name = applet->pluginMetaData().pluginId(); runningApplets[name]++; appletNames.insert(applet, name); itemModel.setRunningApplets(name, runningApplets[name]); } void WidgetExplorerPrivate::appletRemoved(Plasma::Applet *applet) { QString name = appletNames.take(applet); int count = 0; if (runningApplets.contains(name)) { count = runningApplets[name] - 1; if (count < 1) { runningApplets.remove(name); } else { runningApplets[name] = count; } } itemModel.setRunningApplets(name, count); } //WidgetExplorer WidgetExplorer::WidgetExplorer(QObject *parent) : QObject(parent), d(new WidgetExplorerPrivate(this)) { d->filterItemModel.setSortCaseSensitivity(Qt::CaseInsensitive); d->filterItemModel.setDynamicSortFilter(true); d->filterItemModel.setSourceModel(&d->itemModel); d->filterItemModel.sort(0); } WidgetExplorer::~WidgetExplorer() { delete d; } void WidgetExplorer::setApplication(const QString &app) { if (d->application == app && !app.isEmpty()) { return; } d->application = app; d->itemModel.setApplication(app); d->initFilters(); d->itemModel.setRunningApplets(d->runningApplets); emit applicationChanged(); } QString WidgetExplorer::application() { return d->application; } QStringList WidgetExplorer::provides() const { return d->itemModel.provides(); } void WidgetExplorer::setProvides(const QStringList &provides) { if (d->itemModel.provides() == provides) { return; } d->itemModel.setProvides(provides); emit providesChanged(); } void WidgetExplorer::setContainment(Plasma::Containment *containment) { if (d->containment != containment) { if (d->containment) { d->containment->disconnect(this); } d->containment = containment; if (d->containment) { connect(d->containment, SIGNAL(destroyed(QObject*)), this, SLOT(containmentDestroyed())); connect(d->containment, &Applet::immutabilityChanged, this, &WidgetExplorer::immutabilityChanged); } d->initRunningApplets(); emit containmentChanged(); } } Containment *WidgetExplorer::containment() const { return d->containment; } Plasma::Corona *WidgetExplorer::corona() const { if (d->containment) { return d->containment->corona(); } return 0; } void WidgetExplorer::addApplet(const QString &pluginName) { const QString p = PLASMA_RELATIVE_DATA_INSTALL_DIR "/plasmoids/"+pluginName; qWarning() << "--------> load applet: " << pluginName << " relpath: " << p; QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, p, QStandardPaths::LocateDirectory); qDebug() << " .. pathes: " << dirs; if (!dirs.count()) { qWarning() << "Failed to find plasmoid path for " << pluginName; return; } if (d->containment) { d->containment->createApplet(dirs.first()); } } void WidgetExplorer::immutabilityChanged(Plasma::Types::ImmutabilityType type) { if (type != Plasma::Types::Mutable) { emit shouldClose(); } } void WidgetExplorer::downloadWidgets(const QString &type) { Q_UNUSED(type); if (!d->newStuffDialog) { d->newStuffDialog = new KNS3::DownloadDialog( QLatin1String("plasmoids.knsrc") ); d->newStuffDialog.data()->setWindowTitle(i18n("Download New Plasma Widgets")); connect(d->newStuffDialog.data(), SIGNAL(accepted()), SLOT(newStuffFinished())); } d->newStuffDialog.data()->show(); emit shouldClose(); } void WidgetExplorer::openWidgetFile() { Plasma::OpenWidgetAssistant *assistant = d->openAssistant.data(); if (!assistant) { assistant = new Plasma::OpenWidgetAssistant(0); d->openAssistant = assistant; } KWindowSystem::setOnDesktop(assistant->winId(), KWindowSystem::currentDesktop()); assistant->setAttribute(Qt::WA_DeleteOnClose, true); assistant->show(); assistant->raise(); assistant->setFocus(); emit shouldClose(); } void WidgetExplorer::uninstall(const QString &pluginName) { static const QString packageRoot = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + PLASMA_RELATIVE_DATA_INSTALL_DIR "/plasmoids/"; KPackage::PackageStructure *structure = KPackage::PackageLoader::self()->loadPackageStructure(QStringLiteral("Plasma/Applet")); KPackage::Package pkg(structure); pkg.uninstall(pluginName, packageRoot); //FIXME: moreefficient way rather a linear scan? for (int i = 0; i < d->itemModel.rowCount(); ++i) { QStandardItem *item = d->itemModel.item(i); if (item->data(PlasmaAppletItemModel::PluginNameRole).toString() == pluginName) { d->itemModel.takeRow(i); break; } } // now remove all instances of that applet if (corona()) { const auto &containments = corona()->containments(); foreach (Containment *c, containments) { const auto &applets = c->applets(); foreach (Applet *applet, applets) { - const auto &appletInfo = applet->pluginInfo(); + const auto &appletInfo = applet->pluginMetaData(); - if (appletInfo.isValid() && appletInfo.pluginName() == pluginName) { + if (appletInfo.isValid() && appletInfo.pluginId() == pluginName) { applet->destroy(); } } } } } #include "moc_widgetexplorer.cpp" diff --git a/plasma-windowed/plasmawindowedcorona.cpp b/plasma-windowed/plasmawindowedcorona.cpp index fbacbf89c..b68d270d9 100644 --- a/plasma-windowed/plasmawindowedcorona.cpp +++ b/plasma-windowed/plasmawindowedcorona.cpp @@ -1,167 +1,167 @@ /* * Copyright 2014 Bhushan Shah * Copyright 2014 Marco Martin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see */ #include "plasmawindowedcorona.h" #include "plasmawindowedview.h" #include #include #include #include #include #include PlasmaWindowedCorona::PlasmaWindowedCorona(QObject *parent) : Plasma::Corona(parent), m_containment(0), m_hasStatusNotifier(false) { KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Shell")); package.setPath(QStringLiteral("org.kde.plasma.desktop")); setKPackage(package); //QMetaObject::invokeMethod(this, "load", Qt::QueuedConnection); load(); } void PlasmaWindowedCorona::loadApplet(const QString &applet, const QVariantList &arguments) { if (containments().isEmpty()) { return; } Plasma::Containment *cont = containments().first(); //forbid more instances per applet (todo: activate the correpsponding already loaded applet) for (Plasma::Applet *a : cont->applets()) { - if (a->pluginInfo().pluginName() == applet) { + if (a->pluginMetaData().pluginId() == applet) { return; } } PlasmaWindowedView *v = new PlasmaWindowedView(); v->setHasStatusNotifier(m_hasStatusNotifier); v->show(); KConfigGroup appletsGroup(KSharedConfig::openConfig(), "Applets"); QString plugin; for (const QString &group : appletsGroup.groupList()) { KConfigGroup cg(&appletsGroup, group); plugin = cg.readEntry("plugin", QString()); if (plugin == applet) { Plasma::Applet *a = Plasma::PluginLoader::self()->loadApplet(applet, group.toInt(), arguments); if (!a) { qWarning() << "Unable to load applet" << applet << "with arguments" <deleteLater(); return; } a->restore(cg); //Access a->config() before adding to containment //will cause applets to be saved in palsmawindowedrc //so applets will only be created on demand KConfigGroup cg2 = a->config(); cont->addApplet(a); v->setApplet(a); return; } } Plasma::Applet *a = Plasma::PluginLoader::self()->loadApplet(applet, 0, arguments); if (!a) { qWarning() << "Unable to load applet" << applet << "with arguments" <deleteLater(); return; } //Access a->config() before adding to containment //will cause applets to be saved in palsmawindowedrc //so applets will only be created on demand KConfigGroup cg2 = a->config(); cont->addApplet(a); v->setApplet(a); } void PlasmaWindowedCorona::activateRequested(const QStringList &arguments, const QString &workingDirectory) { Q_UNUSED(workingDirectory) if (arguments.count() <= 1) { return; } QVariantList args; QStringList::const_iterator constIterator; constIterator = arguments.constBegin(); ++constIterator; for (; constIterator != arguments.constEnd(); ++constIterator) { args << (*constIterator); } loadApplet(arguments[1], args); } QRect PlasmaWindowedCorona::screenGeometry(int id) const { Q_UNUSED(id); //TODO? return QRect(); } void PlasmaWindowedCorona::load() { /*this won't load applets, since applets are in plasmawindowedrc*/ loadLayout(QStringLiteral("plasmawindowed-appletsrc")); bool found = false; for (auto c : containments()) { if (c->containmentType() == Plasma::Types::DesktopContainment) { found = true; break; } } if (!found) { qDebug() << "Loading default layout"; createContainment(QStringLiteral("empty")); saveLayout(QStringLiteral("plasmawindowed-appletsrc")); } for (auto c : containments()) { if (c->containmentType() == Plasma::Types::DesktopContainment) { m_containment = c; m_containment->setFormFactor(Plasma::Types::Application); QAction *removeAction = c->actions()->action(QStringLiteral("remove")); if(removeAction) { removeAction->deleteLater(); } break; } } } void PlasmaWindowedCorona::setHasStatusNotifier(bool stay) { m_hasStatusNotifier = stay; } diff --git a/runners/windowedwidgets/windowedwidgetsrunner.cpp b/runners/windowedwidgets/windowedwidgetsrunner.cpp index f86159a8a..706b1bb5d 100644 --- a/runners/windowedwidgets/windowedwidgetsrunner.cpp +++ b/runners/windowedwidgets/windowedwidgetsrunner.cpp @@ -1,148 +1,144 @@ /* * Copyright (C) 2006 Aaron Seigo * Copyright (C) 2010 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License version 2 as * published by the Free Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "windowedwidgetsrunner.h" #include #include #include #include #include #include #include #include K_EXPORT_PLASMA_RUNNER(windowedwidgets, WindowedWidgetsRunner) WindowedWidgetsRunner::WindowedWidgetsRunner(QObject *parent, const QVariantList &args) : Plasma::AbstractRunner(parent, args) { Q_UNUSED(args) setObjectName( QLatin1String("WindowedWidgets" )); setPriority(AbstractRunner::HighestPriority); addSyntax(Plasma::RunnerSyntax(QStringLiteral(":q:"), i18n("Finds Plasma widgets whose name or description match :q:"))); setDefaultSyntax(Plasma::RunnerSyntax(i18nc("Note this is a KRunner keyword", "mobile applications"), i18n("list all Plasma widgets that can run as standalone applications"))); } WindowedWidgetsRunner::~WindowedWidgetsRunner() { } void WindowedWidgetsRunner::match(Plasma::RunnerContext &context) { const QString term = context.query(); if (!context.singleRunnerQueryMode() && term.length() < 3) { return; } QList matches; - - foreach (const KPluginInfo &info, Plasma::PluginLoader::self()->listAppletInfo(QString())) { - KService::Ptr service = info.service(); - if (!service->isValid()) { + foreach (const KPluginMetaData &md, Plasma::PluginLoader::self()->listAppletMetaData(QString())) { + if (!md.isValid()) { continue; } - if (((service->name().contains(term, Qt::CaseInsensitive) || - service->genericName().contains(term, Qt::CaseInsensitive) || - service->comment().contains(term, Qt::CaseInsensitive)) || - service->categories().contains(term, Qt::CaseInsensitive) || + if (((md.name().contains(term, Qt::CaseInsensitive) || + md.value(QLatin1String("GenericName")).contains(term, Qt::CaseInsensitive) || + md.description().contains(term, Qt::CaseInsensitive)) || + md.category().contains(term, Qt::CaseInsensitive) || term.startsWith(i18nc("Note this is a KRunner keyword", "mobile applications"))) && - !info.property(QStringLiteral("NoDisplay")).toBool()) { + !md.rawData().value(QStringLiteral("NoDisplay")).toBool()) { - QVariant val = info.property(QStringLiteral("X-Plasma-StandAloneApp")); + QVariant val = md.value(QStringLiteral("X-Plasma-StandAloneApp")); if (!val.isValid() || !val.toBool()) { continue; } Plasma::QueryMatch match(this); - setupMatch(service, match); - if (service->name().compare(term, Qt::CaseInsensitive) == 0) { + setupMatch(md, match); + if (md.name().compare(term, Qt::CaseInsensitive) == 0) { match.setType(Plasma::QueryMatch::ExactMatch); match.setRelevance(1); } else { match.setType(Plasma::QueryMatch::PossibleMatch); match.setRelevance(0.7); } matches << match; - - qDebug() << service; } } if (!context.isValid()) { return; } context.addMatches(matches); } void WindowedWidgetsRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match) { Q_UNUSED(context); - KService::Ptr service = KService::serviceByStorageId(match.data().toString()); - if (service) { - QProcess::startDetached(QStringLiteral("plasmawindowed"), QStringList() << service->property(QStringLiteral("X-KDE-PluginInfo-Name"), QVariant::String).toString()); + KPluginMetaData md(match.data().toString()); + if (md.isValid()) { + QProcess::startDetached(QStringLiteral("plasmawindowed"), QStringList() << md.pluginId()); } } -void WindowedWidgetsRunner::setupMatch(const KService::Ptr &service, Plasma::QueryMatch &match) +void WindowedWidgetsRunner::setupMatch(const KPluginMetaData &md, Plasma::QueryMatch &match) { - const QString name = service->name(); + const QString name = md.pluginId(); match.setText(name); - match.setData(service->storageId()); + match.setData(md.metaDataFileName()); - if (!service->genericName().isEmpty() && service->genericName() != name) { - match.setSubtext(service->genericName()); - } else if (!service->comment().isEmpty()) { - match.setSubtext(service->comment()); + if (!md.name().isEmpty() && md.name() != name) { + match.setSubtext(md.name()); + } else if (!md.description().isEmpty()) { + match.setSubtext(md.description()); } - if (!service->icon().isEmpty()) { - match.setIconName(service->icon()); + if (!md.iconName().isEmpty()) { + match.setIconName(md.iconName()); } } QMimeData * WindowedWidgetsRunner::mimeDataForMatch(const Plasma::QueryMatch &match) { KService::Ptr service = KService::serviceByStorageId(match.data().toString()); if (service) { QMimeData *data = new QMimeData(); data->setData(QStringLiteral("text/x-plasmoidservicename"), service->property(QStringLiteral("X-KDE-PluginInfo-Name"), QVariant::String).toString().toUtf8()); return data; } return 0; } #include "windowedwidgetsrunner.moc" diff --git a/runners/windowedwidgets/windowedwidgetsrunner.h b/runners/windowedwidgets/windowedwidgetsrunner.h index 2294965be..fbc8006ea 100644 --- a/runners/windowedwidgets/windowedwidgetsrunner.h +++ b/runners/windowedwidgets/windowedwidgetsrunner.h @@ -1,55 +1,55 @@ /* * Copyright (C) 2006 Aaron Seigo * Copyright (C) 2010 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License version 2 as * published by the Free Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef WINDOWEDWIDGETSRUNNER_H #define WINDOWEDWIDGETSRUNNER_H #include #include /** * This class looks for matches in the set of .desktop files installed by * applications. This way the user can type exactly what they see in the * appications menu and have it start the appropriate app. Essentially anything * that KService knows about, this runner can launch */ class WindowedWidgetsRunner : public Plasma::AbstractRunner { Q_OBJECT public: WindowedWidgetsRunner(QObject *parent, const QVariantList &args); ~WindowedWidgetsRunner() override; void match(Plasma::RunnerContext &context) override; void run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &action) override; protected Q_SLOTS: QMimeData * mimeDataForMatch(const Plasma::QueryMatch &match) override; protected: - void setupMatch(const KService::Ptr &service, Plasma::QueryMatch &action); + void setupMatch(const KPluginMetaData &md, Plasma::QueryMatch &action); }; #endif diff --git a/shell/alternativeshelper.cpp b/shell/alternativeshelper.cpp index d0f5dfd83..6d7630744 100644 --- a/shell/alternativeshelper.cpp +++ b/shell/alternativeshelper.cpp @@ -1,90 +1,90 @@ /* * Copyright 2014 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "alternativeshelper.h" #include #include #include #include AlternativesHelper::AlternativesHelper(Plasma::Applet *applet, QObject *parent) : QObject(parent), m_applet(applet) { } AlternativesHelper::~AlternativesHelper() { } QStringList AlternativesHelper::appletProvides() const { - return m_applet->pluginInfo().property(QStringLiteral("X-Plasma-Provides")).toStringList(); + return KPluginMetaData::readStringList(m_applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); } QString AlternativesHelper::currentPlugin() const { - return m_applet->pluginInfo().pluginName(); + return m_applet->pluginMetaData().pluginId(); } QQuickItem *AlternativesHelper::applet() const { return m_applet->property("_plasma_graphicObject").value(); } void AlternativesHelper::loadAlternative(const QString &plugin) { - if (plugin == m_applet->pluginInfo().pluginName() || m_applet->isContainment()) { + if (plugin == m_applet->pluginMetaData().pluginId() || m_applet->isContainment()) { return; } Plasma::Containment *cont = m_applet->containment(); if (!cont) { return; } QQuickItem *appletItem = m_applet->property("_plasma_graphicObject").value(); QQuickItem *contItem = cont->property("_plasma_graphicObject").value(); if (!appletItem || !contItem) { return; } // ensure the global shortcut is moved to the new applet const QKeySequence &shortcut = m_applet->globalShortcut(); m_applet->setGlobalShortcut(QKeySequence()); // need to unmap the old one first const QPoint newPos = appletItem->mapToItem(contItem, QPointF(0,0)).toPoint(); m_applet->destroy(); connect(m_applet, &QObject::destroyed, [=]() { Plasma::Applet *newApplet = Q_NULLPTR; QMetaObject::invokeMethod(contItem, "createApplet", Q_RETURN_ARG(Plasma::Applet *, newApplet), Q_ARG(QString, plugin), Q_ARG(QVariantList, QVariantList()), Q_ARG(QPoint, newPos)); if (newApplet) { newApplet->setGlobalShortcut(shortcut); } }); } #include "moc_alternativeshelper.cpp" diff --git a/shell/containmentconfigview.cpp b/shell/containmentconfigview.cpp index 833cfb06f..269ae2b3a 100644 --- a/shell/containmentconfigview.cpp +++ b/shell/containmentconfigview.cpp @@ -1,246 +1,246 @@ /* * Copyright 2013 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "currentcontainmentactionsmodel.h" #include "containmentconfigview.h" #include "plasmaquick/configmodel.h" #include "shellcorona.h" #include "config-workspace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //////////////////////////////ContainmentConfigView ContainmentConfigView::ContainmentConfigView(Plasma::Containment *cont, QWindow *parent) : ConfigView(cont, parent), m_containment(cont), m_wallpaperConfigModel(0), m_containmentActionConfigModel(0), m_containmentPluginsConfigModel(0), m_currentContainmentActionsModel(0), m_currentWallpaperConfig(0), m_ownWallpaperConfig(0) { qmlRegisterType(); rootContext()->setContextProperty(QStringLiteral("configDialog"), this); setCurrentWallpaper(cont->containment()->wallpaper()); KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Wallpaper")); pkg.setPath(m_containment->wallpaper()); KConfigGroup cfg = m_containment->config(); cfg = KConfigGroup(&cfg, "Wallpaper"); syncWallpaperObjects(); } ContainmentConfigView::~ContainmentConfigView() { } void ContainmentConfigView::init() { setSource(QUrl::fromLocalFile(m_containment->corona()->kPackage().filePath("containmentconfigurationui"))); } PlasmaQuick::ConfigModel *ContainmentConfigView::containmentActionConfigModel() { if (!m_containmentActionConfigModel) { m_containmentActionConfigModel = new PlasmaQuick::ConfigModel(this); KPluginInfo::List actions = Plasma::PluginLoader::self()->listContainmentActionsInfo(QString()); KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Generic")); foreach (const KPluginInfo &info, actions) { pkg.setDefaultPackageRoot(QStandardPaths::locate(QStandardPaths::GenericDataLocation, PLASMA_RELATIVE_DATA_INSTALL_DIR "/containmentactions", QStandardPaths::LocateDirectory)); m_containmentActionConfigModel->appendCategory(info.icon(), info.name(), pkg.filePath("ui", QStringLiteral("config.qml")), info.pluginName()); } } return m_containmentActionConfigModel; } QAbstractItemModel *ContainmentConfigView::currentContainmentActionsModel() { if (!m_currentContainmentActionsModel) { m_currentContainmentActionsModel = new CurrentContainmentActionsModel(m_containment, this); } return m_currentContainmentActionsModel; } QString ContainmentConfigView::containmentPlugin() const { - return m_containment->pluginInfo().pluginName(); + return m_containment->pluginMetaData().pluginId(); } void ContainmentConfigView::setContainmentPlugin(const QString &plugin) { if (plugin.isEmpty() || containmentPlugin() == plugin) { return; } m_containment = static_cast(m_containment->corona())->setContainmentTypeForScreen(m_containment->screen(), plugin); emit containmentPluginChanged(); } PlasmaQuick::ConfigModel *ContainmentConfigView::wallpaperConfigModel() { if (!m_wallpaperConfigModel) { m_wallpaperConfigModel = new PlasmaQuick::ConfigModel(this); QStringList dirs(QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, PLASMA_RELATIVE_DATA_INSTALL_DIR "/wallpapers", QStandardPaths::LocateDirectory)); KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("KPackage/Generic")); QStringList platform = KDeclarative::KDeclarative::runtimePlatform(); if (!platform.isEmpty()) { QMutableStringListIterator it(platform); while (it.hasNext()) { it.next(); it.setValue("platformcontents/" + it.value()); } platform.append(QStringLiteral("contents")); pkg.setContentsPrefixPaths(platform); } pkg.addFileDefinition("mainscript", QStringLiteral("ui/main.qml"), i18n("Main Script File")); foreach (const QString &dirPath, dirs) { QDir dir(dirPath); pkg.setDefaultPackageRoot(dirPath); QStringList packages; foreach (const QString &sdir, dir.entryList(QDir::AllDirs | QDir::Readable)) { const QString metadata = dirPath + '/' + sdir; if (QFile::exists(metadata + "/metadata.json") || QFile::exists(metadata + "/metadata.desktop")) { packages << sdir; } } foreach (const QString &package, packages) { pkg.setPath(package); if (!pkg.isValid()) { continue; } m_wallpaperConfigModel->appendCategory(pkg.metadata().iconName(), pkg.metadata().name(), pkg.filePath("ui", QStringLiteral("config.qml")), package); } } } return m_wallpaperConfigModel; } PlasmaQuick::ConfigModel *ContainmentConfigView::containmentPluginsConfigModel() { if (!m_containmentPluginsConfigModel) { m_containmentPluginsConfigModel = new PlasmaQuick::ConfigModel(this); KPluginInfo::List actions = Plasma::PluginLoader::self()->listContainmentsOfType(QStringLiteral("Desktop")); foreach (const KPluginInfo &info, actions) { m_containmentPluginsConfigModel->appendCategory(info.icon(), info.name(), QString(), info.pluginName()); } } return m_containmentPluginsConfigModel; } KDeclarative::ConfigPropertyMap *ContainmentConfigView::wallpaperConfiguration() const { return m_currentWallpaperConfig; } QString ContainmentConfigView::currentWallpaper() const { return m_currentWallpaper; } void ContainmentConfigView::setCurrentWallpaper(const QString &wallpaper) { if (m_currentWallpaper == wallpaper) { return; } delete m_ownWallpaperConfig; m_ownWallpaperConfig = 0; if (m_containment->wallpaper() == wallpaper) { syncWallpaperObjects(); } else { //we have to construct an independent ConfigPropertyMap when we want to configure wallpapers that are not the current one KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Generic")); pkg.setDefaultPackageRoot(PLASMA_RELATIVE_DATA_INSTALL_DIR "/wallpapers"); pkg.setPath(wallpaper); QFile file(pkg.filePath("config", QStringLiteral("main.xml"))); KConfigGroup cfg = m_containment->config(); cfg = KConfigGroup(&cfg, "Wallpaper"); m_currentWallpaperConfig = m_ownWallpaperConfig = new KDeclarative::ConfigPropertyMap(new KConfigLoader(cfg, &file), this); } m_currentWallpaper = wallpaper; emit currentWallpaperChanged(); emit wallpaperConfigurationChanged(); } void ContainmentConfigView::applyWallpaper() { m_containment->setWallpaper(m_currentWallpaper); syncWallpaperObjects(); if (m_currentWallpaperConfig && m_ownWallpaperConfig) { for (const auto &key : m_ownWallpaperConfig->keys()) { m_currentWallpaperConfig->insert(key, m_ownWallpaperConfig->value(key)); } } delete m_ownWallpaperConfig; m_ownWallpaperConfig = 0; emit wallpaperConfigurationChanged(); } void ContainmentConfigView::syncWallpaperObjects() { QObject *wallpaperGraphicsObject = m_containment->property("wallpaperGraphicsObject").value(); if (!wallpaperGraphicsObject) { return; } rootContext()->setContextProperty(QStringLiteral("wallpaper"), wallpaperGraphicsObject); //FIXME: why m_wallpaperGraphicsObject->property("configuration").value() doesn't work? m_currentWallpaperConfig = static_cast(wallpaperGraphicsObject->property("configuration").value()); } #include "private/moc_containmentconfigview.cpp" diff --git a/shell/scripting/containment.cpp b/shell/scripting/containment.cpp index 6040e625a..96e200992 100644 --- a/shell/scripting/containment.cpp +++ b/shell/scripting/containment.cpp @@ -1,309 +1,309 @@ /* * Copyright 2009 Aaron Seigo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "containment.h" #include #include #include #include #include #include #include #include #include "scriptengine.h" #include "widget.h" namespace WorkspaceScripting { class Containment::Private { public: QPointer containment; QString oldWallpaperPlugin; QString wallpaperPlugin; QString oldWallpaperMode; QString wallpaperMode; }; Containment::Containment(Plasma::Containment *containment, QObject *parent) : Applet(parent), d(new Containment::Private) { d->containment = containment; setCurrentConfigGroup(QStringList()); setCurrentGlobalConfigGroup(QStringList()); if (containment) { d->oldWallpaperPlugin = d->wallpaperPlugin = containment->wallpaper(); } } Containment::~Containment() { if (d->containment) { Plasma::Containment *containment = d->containment.data(); if (d->oldWallpaperPlugin != d->wallpaperPlugin || d->oldWallpaperMode != d->wallpaperMode) { containment->setWallpaper(d->wallpaperPlugin); } } reloadConfigIfNeeded(); delete d; } int Containment::screen() const { if (!d->containment) { return -1; } return d->containment.data()->screen(); } QString Containment::wallpaperPlugin() const { return d->wallpaperPlugin; } void Containment::setWallpaperPlugin(const QString &wallpaperPlugin) { d->wallpaperPlugin = wallpaperPlugin; } QString Containment::wallpaperMode() const { return d->wallpaperMode; } void Containment::setWallpaperMode(const QString &wallpaperMode) { d->wallpaperMode = wallpaperMode; } QString Containment::formFactor() const { if (!d->containment) { return QStringLiteral("Planar"); } switch (d->containment.data()->formFactor()) { case Plasma::Types::Planar: return QStringLiteral("planar"); break; case Plasma::Types::MediaCenter: return QStringLiteral("mediacenter"); break; case Plasma::Types::Horizontal: return QStringLiteral("horizontal"); break; case Plasma::Types::Vertical: return QStringLiteral("vertical"); break; case Plasma::Types::Application: return QStringLiteral("application"); break; } return QStringLiteral("Planar"); } QList Containment::widgetIds() const { //FIXME: the ints could overflow since Applet::id() returns a uint, // however QScript deals with QList very, very poory QList w; if (d->containment) { foreach (const Plasma::Applet *applet, d->containment.data()->applets()) { w.append(applet->id()); } } return w; } QScriptValue Containment::widgetById(QScriptContext *context, QScriptEngine *engine) { if (context->argumentCount() == 0) { return context->throwError(i18n("widgetById requires an id")); } const uint id = context->argument(0).toInt32(); Containment *c = qobject_cast(context->thisObject().toQObject()); if (!c) { return engine->undefinedValue(); } if (c->d->containment) { foreach (Plasma::Applet *w, c->d->containment.data()->applets()) { if (w->id() == id) { ScriptEngine *env = ScriptEngine::envFor(engine); return env->wrap(w); } } } return engine->undefinedValue(); } QScriptValue Containment::addWidget(QScriptContext *context, QScriptEngine *engine) { if (context->argumentCount() == 0) { return context->throwError(i18n("addWidget requires a name of a widget or a widget object")); } Containment *c = qobject_cast(context->thisObject().toQObject()); if (!c || !c->d->containment) { return engine->undefinedValue(); } QScriptValue v = context->argument(0); Plasma::Applet *applet = 0; QRectF geometry(-1, -1, -1, -1); if (context->argumentCount() > 4) { //The user provided a geometry as parameter if (context->argument(1).isNumber() && context->argument(2).isNumber() && context->argument(3).isNumber() && context->argument(4).isNumber()) { //Try to reconstruct a rectangle from the object hat has been passed //It's expected a js object such as //addWidget("org.kde.plasma.analogclock", 0, 100, 300, 400); geometry = QRectF(context->argument(1).toNumber(), context->argument(2).toNumber(), context->argument(3).toNumber(), context->argument(4).toNumber()); } } if (v.isString()) { //A position has been supplied: search for the containment's graphics object QQuickItem *containmentItem = nullptr; if (geometry.x() >= 0 && geometry.y() >= 0) { containmentItem = c->d->containment.data()->property("_plasma_graphicObject").value(); Plasma::Applet *applet = nullptr; if (containmentItem) { QMetaObject::invokeMethod(containmentItem , "createApplet", Qt::DirectConnection, Q_RETURN_ARG(Plasma::Applet *, applet), Q_ARG(QString, v.toString()), Q_ARG(QVariantList, QVariantList()), Q_ARG(QRectF, geometry)); } if (applet) { ScriptEngine *env = ScriptEngine::envFor(engine); return env->wrap(applet); } } //Case in which either: // * a geometry wasn't provided // * containmentItem wasn't found applet = c->d->containment.data()->createApplet(v.toString()); if (applet) { ScriptEngine *env = ScriptEngine::envFor(engine); return env->wrap(applet); } } else if (Widget *widget = qobject_cast(v.toQObject())) { applet = widget->applet(); c->d->containment.data()->addApplet(applet); return v; } return engine->undefinedValue(); } QScriptValue Containment::widgets(QScriptContext *context, QScriptEngine *engine) { Containment *c = qobject_cast(context->thisObject().toQObject()); if (!c || !c->d->containment) { return engine->undefinedValue(); } const QString widgetType = context->argumentCount() > 0 ? context->argument(0).toString() : QString(); QScriptValue widgets = engine->newArray(); ScriptEngine *env = ScriptEngine::envFor(engine); int count = 0; foreach (Plasma::Applet *widget, c->d->containment.data()->applets()) { - if (widgetType.isEmpty() || widget->pluginInfo().pluginName() == widgetType) { + if (widgetType.isEmpty() || widget->pluginMetaData().pluginId() == widgetType) { widgets.setProperty(count, env->wrap(widget)); ++count; } } widgets.setProperty(QStringLiteral("length"), count); return widgets; } uint Containment::id() const { if (!d->containment) { return 0; } return d->containment.data()->id(); } QString Containment::type() const { if (!d->containment) { return QString(); } - return d->containment.data()->pluginInfo().pluginName(); + return d->containment.data()->pluginMetaData().pluginId(); } void Containment::remove() { if (d->containment) { d->containment.data()->destroy(); } } void Containment::showConfigurationInterface() { if (d->containment) { QAction *configAction = d->containment.data()->actions()->action(QStringLiteral("configure")); if (configAction && configAction->isEnabled()) { configAction->trigger(); } } } Plasma::Applet *Containment::applet() const { return d->containment.data(); } Plasma::Containment *Containment::containment() const { return d->containment.data(); } } diff --git a/shell/scripting/widget.cpp b/shell/scripting/widget.cpp index a651c2aef..b58822b21 100644 --- a/shell/scripting/widget.cpp +++ b/shell/scripting/widget.cpp @@ -1,182 +1,182 @@ /* * Copyright 2009 Aaron Seigo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "widget.h" #include #include #include #include namespace WorkspaceScripting { class Widget::Private { public: Private() { } QPointer applet; }; Widget::Widget(Plasma::Applet *applet, QObject *parent) : Applet(parent), d(new Widget::Private) { d->applet = applet; setCurrentConfigGroup(QStringList()); setCurrentGlobalConfigGroup(QStringList()); } Widget::~Widget() { reloadConfigIfNeeded(); delete d; } uint Widget::id() const { if (d->applet) { return d->applet.data()->id(); } return 0; } QString Widget::type() const { if (d->applet) { - return d->applet.data()->pluginInfo().pluginName(); + return d->applet.data()->pluginMetaData().pluginId(); } return QString(); } void Widget::remove() { if (d->applet) { d->applet.data()->destroy(); d->applet.clear(); } } void Widget::setGlobalShortcut(const QString &shortcut) { if (d->applet) { d->applet.data()->setGlobalShortcut(QKeySequence(shortcut)); } } QString Widget::globalShorcut() const { if (d->applet) { return d->applet.data()->globalShortcut().toString(); } return QString(); } Plasma::Applet *Widget::applet() const { return d->applet.data(); } int Widget::index() const { if (!d->applet) { return -1; } Plasma::Applet *applet = d->applet.data(); Plasma::Containment *c = applet->containment(); if (!c) { return -1; } /*QGraphicsLayout *layout = c->layout(); if (!layout) { return - 1; } for (int i = 0; i < layout->count(); ++i) { if (layout->itemAt(i) == applet) { return i; } }*/ return -1; } void Widget::setIndex(int index) { Q_UNUSED(index) /* if (!d->applet) { return; } Plasma::Applet *applet = d->applet.data(); Plasma::Containment *c = applet->containment(); if (!c) { return; } //FIXME: this is hackish. would be nice to define this for gridlayouts too QGraphicsLinearLayout *layout = dynamic_cast(c->layout()); if (!layout) { return; } layout->insertItem(index, applet);*/ } QRectF Widget::geometry() const { /*if (d->applet) { return d->applet.data()->geometry(); } */ return QRectF(); } void Widget::setGeometry(const QRectF &geometry) { Q_UNUSED(geometry) /*if (d->applet) { d->applet.data()->setGeometry(geometry); KConfigGroup cg = d->applet.data()->config().parent(); if (cg.isValid()) { cg.writeEntry("geometry", geometry); } }*/ } void Widget::showConfigurationInterface() { /* if (d->applet) { d->applet.data()->showConfigurationInterface(); }*/ } } diff --git a/shell/shellcorona.cpp b/shell/shellcorona.cpp index 17f1ab551..0422bc3f9 100644 --- a/shell/shellcorona.cpp +++ b/shell/shellcorona.cpp @@ -1,2009 +1,2010 @@ /* * Copyright 2008 Aaron Seigo * Copyright 2013 Sebastian Kügler * Copyright 2013 Ivan Cukic * Copyright 2013 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "shellcorona.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config-ktexteditor.h" // HAVE_KTEXTEDITOR #include "alternativeshelper.h" #include "desktopview.h" #include "panelview.h" #include "scripting/scriptengine.h" #include "plasmaquick/configview.h" #include "shellmanager.h" #include "osd.h" #include "screenpool.h" #include "plasmashelladaptor.h" #include "debug.h" #include "futureutil.h" #ifndef NDEBUG #define CHECK_SCREEN_INVARIANTS screenInvariants(); #else #define CHECK_SCREEN_INVARIANTS #endif #if HAVE_X11 #include #include #endif static const int s_configSyncDelay = 10000; // 10 seconds ShellCorona::ShellCorona(QObject *parent) : Plasma::Corona(parent), m_screenPool(new ScreenPool(KSharedConfig::openConfig(), this)), m_activityController(new KActivities::Controller(this)), m_addPanelAction(nullptr), m_addPanelsMenu(nullptr), m_interactiveConsole(nullptr), m_waylandPlasmaShell(nullptr) { setupWaylandIntegration(); qmlRegisterUncreatableType("org.kde.plasma.shell", 2, 0, "Desktop", QStringLiteral("It is not possible to create objects of type Desktop")); qmlRegisterUncreatableType("org.kde.plasma.shell", 2, 0, "Panel", QStringLiteral("It is not possible to create objects of type Panel")); m_lookAndFeelPackage = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), "KDE"); const QString packageName = cg.readEntry("LookAndFeelPackage", QString()); if (!packageName.isEmpty()) { m_lookAndFeelPackage.setPath(packageName); } connect(this, &Plasma::Corona::containmentCreated, this, [this] (Plasma::Containment *c) { executeSetupPlasmoidScript(c, c); }); connect(this, &Plasma::Corona::availableScreenRectChanged, this, &Plasma::Corona::availableScreenRegionChanged); m_appConfigSyncTimer.setSingleShot(true); m_appConfigSyncTimer.setInterval(s_configSyncDelay); connect(&m_appConfigSyncTimer, &QTimer::timeout, this, &ShellCorona::syncAppConfig); //we want our application config with screen mapping to always be in sync with the applets one, so a crash at any time will still //leave containments pointing to the correct screens connect(this, &Corona::configSynced, this, &ShellCorona::syncAppConfig); m_waitingPanelsTimer.setSingleShot(true); m_waitingPanelsTimer.setInterval(250); connect(&m_waitingPanelsTimer, &QTimer::timeout, this, &ShellCorona::createWaitingPanels); m_reconsiderOutputsTimer.setSingleShot(true); m_reconsiderOutputsTimer.setInterval(1000); connect(&m_reconsiderOutputsTimer, &QTimer::timeout, this, &ShellCorona::reconsiderOutputs); m_desktopDefaultsConfig = KConfigGroup(KSharedConfig::openConfig(package().filePath("defaults")), "Desktop"); m_lnfDefaultsConfig = KConfigGroup(KSharedConfig::openConfig(m_lookAndFeelPackage.filePath("defaults")), "Desktop"); m_lnfDefaultsConfig = KConfigGroup(&m_lnfDefaultsConfig, QStringLiteral("org.kde.plasma.desktop")); new PlasmaShellAdaptor(this); QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject(QStringLiteral("/PlasmaShell"), this); connect(this, &Plasma::Corona::startupCompleted, this, []() { qDebug() << "Plasma Shell startup completed"; QDBusMessage ksplashProgressMessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KSplash"), QStringLiteral("/KSplash"), QStringLiteral("org.kde.KSplash"), QStringLiteral("setStage")); ksplashProgressMessage.setArguments(QList() << QStringLiteral("desktop")); QDBusConnection::sessionBus().asyncCall(ksplashProgressMessage); //TODO: remove }); // Look for theme config in plasmarc, if it isn't configured, take the theme from the // LookAndFeel package, if either is set, change the default theme connect(qApp, &QCoreApplication::aboutToQuit, this, [this]() { //saveLayout is a slot but arguments not compatible saveLayout(); }); connect(this, &ShellCorona::containmentAdded, this, &ShellCorona::handleContainmentAdded); QAction *dashboardAction = actions()->addAction(QStringLiteral("show dashboard")); QObject::connect(dashboardAction, &QAction::triggered, this, &ShellCorona::setDashboardShown); dashboardAction->setText(i18n("Show Desktop")); connect(KWindowSystem::self(), &KWindowSystem::showingDesktopChanged, [dashboardAction](bool showing) { dashboardAction->setText(showing ? i18n("Hide Desktop") : i18n("Show Desktop")); dashboardAction->setChecked(showing); }); dashboardAction->setAutoRepeat(true); dashboardAction->setCheckable(true); dashboardAction->setIcon(QIcon::fromTheme(QStringLiteral("dashboard-show"))); dashboardAction->setData(Plasma::Types::ControlAction); KGlobalAccel::self()->setGlobalShortcut(dashboardAction, Qt::CTRL + Qt::Key_F12); checkAddPanelAction(); connect(KSycoca::self(), SIGNAL(databaseChanged(QStringList)), this, SLOT(checkAddPanelAction(QStringList))); //Activity stuff QAction *activityAction = actions()->addAction(QStringLiteral("manage activities")); connect(activityAction, &QAction::triggered, this, &ShellCorona::toggleActivityManager); activityAction->setText(i18n("Activities...")); activityAction->setIcon(QIcon::fromTheme(QStringLiteral("preferences-activities"))); activityAction->setData(Plasma::Types::ConfigureAction); activityAction->setShortcut(QKeySequence(QStringLiteral("alt+d, alt+a"))); activityAction->setShortcutContext(Qt::ApplicationShortcut); KGlobalAccel::self()->setGlobalShortcut(activityAction, Qt::META + Qt::Key_Q); QAction *stopActivityAction = actions()->addAction(QStringLiteral("stop current activity")); QObject::connect(stopActivityAction, &QAction::triggered, this, &ShellCorona::stopCurrentActivity); stopActivityAction->setText(i18n("Stop Current Activity")); stopActivityAction->setData(Plasma::Types::ControlAction); stopActivityAction->setVisible(false); KGlobalAccel::self()->setGlobalShortcut(stopActivityAction, Qt::META + Qt::Key_S); connect(m_activityController, &KActivities::Controller::currentActivityChanged, this, &ShellCorona::currentActivityChanged); connect(m_activityController, &KActivities::Controller::activityAdded, this, &ShellCorona::activityAdded); connect(m_activityController, &KActivities::Controller::activityRemoved, this, &ShellCorona::activityRemoved); new Osd(this); } ShellCorona::~ShellCorona() { while (!containments().isEmpty()) { //deleting a containment will remove it from the list due to QObject::destroyed connect in Corona delete containments().first(); } qDeleteAll(m_panelViews); m_panelViews.clear(); } KPackage::Package ShellCorona::lookAndFeelPackage() { return m_lookAndFeelPackage; } void ShellCorona::setShell(const QString &shell) { if (m_shell == shell) { return; } m_shell = shell; KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Shell")); package.setPath(shell); package.setAllowExternalPaths(true); setKPackage(package); m_desktopDefaultsConfig = KConfigGroup(KSharedConfig::openConfig(package.filePath("defaults")), "Desktop"); m_lnfDefaultsConfig = KConfigGroup(KSharedConfig::openConfig(m_lookAndFeelPackage.filePath("defaults")), "Desktop"); m_lnfDefaultsConfig = KConfigGroup(&m_lnfDefaultsConfig, shell); const QString themeGroupKey = QStringLiteral("Theme"); const QString themeNameKey = QStringLiteral("name"); QString themeName; KConfigGroup plasmarc(KSharedConfig::openConfig(QStringLiteral("plasmarc")), themeGroupKey); themeName = plasmarc.readEntry(themeNameKey, themeName); if (themeName.isEmpty()) { KConfigGroup shellCfg = KConfigGroup(KSharedConfig::openConfig(package.filePath("defaults")), "Theme"); themeName = shellCfg.readEntry("name", "default"); KConfigGroup lnfCfg = KConfigGroup(KSharedConfig::openConfig( m_lookAndFeelPackage.filePath("defaults")), "plasmarc" ); lnfCfg = KConfigGroup(&lnfCfg, themeGroupKey); themeName = lnfCfg.readEntry(themeNameKey, themeName); } if (!themeName.isEmpty()) { Plasma::Theme *t = new Plasma::Theme(this); t->setThemeName(themeName); } //FIXME: this would change the runtime platform to a fixed one if available // but a different way to load platform specific components is needed beforehand // because if we import and use two different components plugin, the second time // the import is called it will fail /* KConfigGroup cg(KSharedConfig::openConfig(package.filePath("defaults")), "General"); KDeclarative::KDeclarative::setRuntimePlatform(cg.readEntry("DefaultRuntimePlatform", QStringList()));*/ unload(); /* * we want to make an initial load once we have the initial screen config and we have loaded the activities _IF_ KAMD is running * it is valid for KAMD to not be running. * * Potentially 2 async jobs * * here we connect for status changes from KAMD, and fetch the first config from kscreen. * load() will check that we have a kscreen config, and m_activityController->serviceStatus() is not loading (i.e not unknown) * * It might seem that we only need this connection if the activityConsumer is currently in state Unknown, however * there is an issue where m_activityController will start the kactivitymanagerd, as KAMD is starting the serviceStatus will be "not running" * Whilst we are loading the kscreen config, the event loop runs and we might find KAMD has started. * m_activityController will change from "not running" to unknown, and might still be unknown when the kscreen fetching is complete. * * if that happens we want to continue monitoring for state changes, and only finally load when it is up. * * See https://bugs.kde.org/show_bug.cgi?id=342431 be careful about changing * * The unique connection makes sure we don't reload plasma if KAMD ever crashes and reloads, the signal is disconnected in the body of load */ connect(m_activityController, &KActivities::Controller::serviceStatusChanged, this, &ShellCorona::load, Qt::UniqueConnection); if (m_activityController->serviceStatus() == KActivities::Controller::Running) { load(); } } QJsonObject dumpconfigGroupJS(const KConfigGroup &rootGroup) { QJsonObject result; QStringList hierarchy; QStringList escapedHierarchy; QList groups{rootGroup}; QSet visitedNodes; const QSet forbiddenKeys { QStringLiteral("activityId"), QStringLiteral("ItemsGeometries"), QStringLiteral("AppletOrder"), QStringLiteral("SystrayContainmentId"), QStringLiteral("location"), QStringLiteral("plugin") }; auto groupID = [&escapedHierarchy]() { return '/' + escapedHierarchy.join('/'); }; // Perform a depth-first tree traversal for config groups while (!groups.isEmpty()) { KConfigGroup cg = groups.last(); KConfigGroup parentCg = cg; //FIXME: name is not enough hierarchy.clear(); escapedHierarchy.clear(); while (parentCg.isValid() && parentCg.name() != rootGroup.name()) { const auto name = parentCg.name(); hierarchy.prepend(name); escapedHierarchy.prepend(QString::fromUtf8(QUrl::toPercentEncoding(name.toUtf8()))); parentCg = parentCg.parent(); } visitedNodes.insert(groupID()); groups.pop_back(); QJsonObject configGroupJson; if (!cg.keyList().isEmpty()) { //TODO: this is conditional if applet or containment const auto map = cg.entryMap(); auto i = map.cbegin(); for (; i != map.cend(); ++i) { //some blacklisted keys we don't want to save if (!forbiddenKeys.contains(i.key())) { configGroupJson.insert(i.key(), i.value()); } } } foreach (const QString &groupName, cg.groupList()) { if (groupName == QStringLiteral("Applets") || visitedNodes.contains(groupID() + '/' + groupName)) { continue; } groups << KConfigGroup(&cg, groupName); } if (!configGroupJson.isEmpty()) { result.insert(groupID(), configGroupJson); } } return result; } QByteArray ShellCorona::dumpCurrentLayoutJS() const { QJsonObject root; root.insert("serializationFormatVersion", "1"); //same gridUnit calculation as ScriptEngine int gridUnit = QFontMetrics(QGuiApplication::font()).boundingRect(QStringLiteral("M")).height(); if (gridUnit % 2 != 0) { gridUnit++; } auto isPanel = [] (Plasma::Containment *cont) { return (cont->formFactor() == Plasma::Types::Horizontal || cont->formFactor() == Plasma::Types::Vertical) && (cont->location() == Plasma::Types::TopEdge || cont->location() == Plasma::Types::BottomEdge || cont->location() == Plasma::Types::LeftEdge || cont->location() == Plasma::Types::RightEdge) && - cont->pluginInfo().pluginName() != QStringLiteral("org.kde.plasma.private.systemtray"); + cont->pluginMetaData().pluginId() != QStringLiteral("org.kde.plasma.private.systemtray"); }; auto isDesktop = [] (Plasma::Containment *cont) { return !cont->activity().isEmpty(); }; const auto containments = ShellCorona::containments(); // Collecting panels QJsonArray panelsJsonArray; foreach (Plasma::Containment *cont, containments) { if (!isPanel(cont)) { continue; } QJsonObject panelJson; const PanelView *view = m_panelViews.value(cont); const auto location = cont->location(); panelJson.insert("location", location == Plasma::Types::TopEdge ? "top" : location == Plasma::Types::LeftEdge ? "left" : location == Plasma::Types::RightEdge ? "right" : /* Plasma::Types::BottomEdge */ "bottom"); const qreal height = // If we do not have a panel, fallback to 4 units !view ? 4 : (qreal)view->thickness() / gridUnit; panelJson.insert("height", height); if (view) { const auto alignment = view->alignment(); panelJson.insert("maximumLength", (qreal)view->maximumLength() / gridUnit); panelJson.insert("minimumLength", (qreal)view->minimumLength() / gridUnit); panelJson.insert("offset", (qreal)view->offset() / gridUnit); panelJson.insert("alignment", alignment == Qt::AlignRight ? "right" : alignment == Qt::AlignCenter ? "center" : "left"); } // Saving the config keys const KConfigGroup contConfig = cont->config(); panelJson.insert("config", dumpconfigGroupJS(contConfig)); // Generate the applets array QJsonArray appletsJsonArray; // Try to parse the encoded applets order const KConfigGroup genericConf(&contConfig, QStringLiteral("General")); const QStringList appletsOrderStrings = genericConf.readEntry(QStringLiteral("AppletOrder"), QString()) .split(QChar(';')); // Consider the applet order to be valid only if there are as many entries as applets() if (appletsOrderStrings.length() == cont->applets().length()) { foreach (const QString &appletId, appletsOrderStrings) { KConfigGroup appletConfig(&contConfig, QStringLiteral("Applets")); appletConfig = KConfigGroup(&appletConfig, appletId); const QString pluginName = appletConfig.readEntry(QStringLiteral("plugin"), QString()); if (pluginName.isEmpty()) { continue; } QJsonObject appletJson; appletJson.insert("plugin", pluginName); appletJson.insert("config", dumpconfigGroupJS(appletConfig)); appletsJsonArray << appletJson; } } else { foreach (Plasma::Applet *applet, cont->applets()) { QJsonObject appletJson; KConfigGroup appletConfig = applet->config(); - appletJson.insert("plugin", applet->pluginInfo().pluginName()); + appletJson.insert("plugin", applet->pluginMetaData().pluginId()); appletJson.insert("config", dumpconfigGroupJS(appletConfig)); appletsJsonArray << appletJson; } } panelJson.insert("applets", appletsJsonArray); panelsJsonArray << panelJson; } root.insert("panels", panelsJsonArray); // Now we are collecting desktops QJsonArray desktopsJson; const auto currentActivity = m_activityController->currentActivity(); foreach (Plasma::Containment *cont, containments) { if (!isDesktop(cont) || cont->activity() != currentActivity) { continue; } QJsonObject desktopJson; desktopJson.insert("wallpaperPlugin", cont->wallpaper()); // Get the config for the containment KConfigGroup contConfig = cont->config(); desktopJson.insert("config", dumpconfigGroupJS(contConfig)); // Try to parse the item geometries const KConfigGroup genericConf(&contConfig, QStringLiteral("General")); const QStringList appletsGeomStrings = genericConf.readEntry(QStringLiteral("ItemsGeometries"), QString()) .split(QChar(';')); QHash appletGeometries; foreach (const QString &encoded, appletsGeomStrings) { const QStringList keyValue = encoded.split(QChar(':')); if (keyValue.length() != 2) { continue; } const QStringList rectPieces = keyValue.last().split(QChar(',')); if (rectPieces.length() != 5) { continue; } QRect rect(rectPieces[0].toInt(), rectPieces[1].toInt(), rectPieces[2].toInt(), rectPieces[3].toInt()); appletGeometries[keyValue.first()] = rect; } QJsonArray appletsJsonArray; foreach (Plasma::Applet *applet, cont->applets()) { const QRect geometry = appletGeometries.value( QStringLiteral("Applet-") % QString::number(applet->id())); QJsonObject appletJson; appletJson.insert("title", applet->title()); - appletJson.insert("plugin", applet->pluginInfo().pluginName()); + appletJson.insert("plugin", applet->pluginMetaData().pluginId()); appletJson.insert("geometry.x", geometry.x() / gridUnit); appletJson.insert("geometry.y", geometry.y() / gridUnit); appletJson.insert("geometry.width", geometry.width() / gridUnit); appletJson.insert("geometry.height", geometry.height() / gridUnit); KConfigGroup appletConfig = applet->config(); appletJson.insert("config", dumpconfigGroupJS(appletConfig)); appletsJsonArray << appletJson; } desktopJson.insert("applets", appletsJsonArray); desktopsJson << desktopJson; } root.insert("desktops", desktopsJson); QJsonDocument json; json.setObject(root); return "var plasma = getApiVersion(1);\n\n" "var layout = " + json.toJson() + ";\n\n" "plasma.loadSerializedLayout(layout);\n"; } void ShellCorona::loadLookAndFeelDefaultLayout(const QString &packageName) { KPackage::Package newPack = m_lookAndFeelPackage; newPack.setPath(packageName); if (!newPack.isValid()) { return; } KSharedConfig::Ptr conf = KSharedConfig::openConfig(QStringLiteral("plasma-") + m_shell + QStringLiteral("-appletsrc"), KConfig::SimpleConfig); m_lookAndFeelPackage.setPath(packageName); //get rid of old config for (const QString &group : conf->groupList()) { conf->deleteGroup(group); } conf->sync(); unload(); load(); } QString ShellCorona::shell() const { return m_shell; } void ShellCorona::load() { if (m_shell.isEmpty() || m_activityController->serviceStatus() != KActivities::Controller::Running) { return; } disconnect(m_activityController, &KActivities::Controller::serviceStatusChanged, this, &ShellCorona::load); m_screenPool->load(); //TODO: a kconf_update script is needed QString configFileName(QStringLiteral("plasma-") + m_shell + QStringLiteral("-appletsrc")); loadLayout(configFileName); checkActivities(); if (containments().isEmpty()) { // Seems like we never really get to this point since loadLayout already // (virtually) calls loadDefaultLayout if it does not load anything // from the config file. Maybe if the config file is not empty, // but still does not have any containments loadDefaultLayout(); processUpdateScripts(); } else { processUpdateScripts(); foreach(Plasma::Containment *containment, containments()) { if (containment->containmentType() == Plasma::Types::PanelContainment || containment->containmentType() == Plasma::Types::CustomPanelContainment) { //Don't give a view to containments that don't want one (negative lastscreen) //(this is pretty mucha special case for the systray) //also, make sure we don't have a view already. //this will be true for first startup as the view has already been created at the new Panel JS call if (!m_waitingPanels.contains(containment) && containment->lastScreen() >= 0 && !m_panelViews.contains(containment)) { m_waitingPanels << containment; } //historically CustomContainments are treated as desktops } else if (containment->containmentType() == Plasma::Types::DesktopContainment || containment->containmentType() == Plasma::Types::CustomContainment) { //FIXME ideally fix this, or at least document the crap out of it int screen = containment->lastScreen(); if (screen < 0) { screen = 0; qWarning() << "last screen is < 0 so putting containment on screen " << screen; } insertContainment(containment->activity(), screen, containment); } } } //NOTE: this is needed in case loadLayout() did *not* call loadDefaultLayout() //it needs to be after of loadLayout() as it would always create new //containments on each startup otherwise for (QScreen* screen : qGuiApp->screens()) { //the containments may have been created already by the startup script //check their existence in oder to not have duplicated desktopviews if (!m_desktopViewforId.contains(m_screenPool->id(screen->name()))) { addOutput(screen); } } connect(qGuiApp, &QGuiApplication::screenAdded, this, &ShellCorona::addOutput, Qt::UniqueConnection); connect(qGuiApp, &QGuiApplication::primaryScreenChanged, this, &ShellCorona::primaryOutputChanged, Qt::UniqueConnection); connect(qGuiApp, &QGuiApplication::screenRemoved, this, &ShellCorona::screenRemoved, Qt::UniqueConnection); if (!m_waitingPanels.isEmpty()) { m_waitingPanelsTimer.start(); } if (config()->isImmutable() || !KAuthorized::authorize(QStringLiteral("plasma/plasmashell/unlockedDesktop"))) { setImmutability(Plasma::Types::SystemImmutable); } else { KConfigGroup coronaConfig(config(), "General"); setImmutability((Plasma::Types::ImmutabilityType)coronaConfig.readEntry("immutability", (int)Plasma::Types::Mutable)); } } void ShellCorona::primaryOutputChanged() { if (!m_desktopViewforId.contains(0)) { return; } QScreen *oldPrimary = m_desktopViewforId.value(0)->screen(); QScreen *newPrimary = qGuiApp->primaryScreen(); if (!newPrimary || newPrimary == oldPrimary) { return; } m_screenPool->setPrimaryConnector(newPrimary->name()); qWarning()<<"Old primary output:"<id(newPrimary->name()); //swap order in m_desktopViewforId if (m_desktopViewforId.contains(0) && m_desktopViewforId.contains(oldIdOfPrimary)) { DesktopView *primaryDesktop = m_desktopViewforId.value(0); DesktopView *oldDesktopOfPrimary = m_desktopViewforId.value(oldIdOfPrimary); primaryDesktop->setScreenToFollow(newPrimary); oldDesktopOfPrimary->setScreenToFollow(oldPrimary); primaryDesktop->show(); oldDesktopOfPrimary->show(); } foreach (PanelView *panel, m_panelViews) { if (panel->screen() == oldPrimary) { panel->setScreenToFollow(newPrimary); } else if (panel->screen() == newPrimary) { panel->setScreenToFollow(oldPrimary); } } CHECK_SCREEN_INVARIANTS } #ifndef NDEBUG void ShellCorona::screenInvariants() const { Q_ASSERT(m_desktopViewforId.keys().count() <= QGuiApplication::screens().count()); QSet screens; foreach (const int id, m_desktopViewforId.keys()) { const DesktopView *view = m_desktopViewforId.value(id); QScreen *screen = view->screenToFollow(); Q_ASSERT(!screens.contains(screen)); Q_ASSERT(!m_redundantOutputs.contains(screen)); // commented out because a different part of the code-base is responsible for this // and sometimes is not yet called here. // Q_ASSERT(!view->fillScreen() || view->geometry() == screen->geometry()); Q_ASSERT(view->containment()); Q_ASSERT(view->containment()->screen() == id || view->containment()->screen() == -1); Q_ASSERT(view->containment()->lastScreen() == id || view->containment()->lastScreen() == -1); Q_ASSERT(view->isVisible()); foreach (const PanelView *panel, panelsForScreen(screen)) { Q_ASSERT(panel->containment()); Q_ASSERT(panel->containment()->screen() == id || panel->containment()->screen() == -1); Q_ASSERT(panel->isVisible()); } screens.insert(screen); } foreach (QScreen* out, m_redundantOutputs) { Q_ASSERT(isOutputRedundant(out)); } if (m_desktopViewforId.isEmpty()) { qWarning() << "no screens!!"; } } #endif void ShellCorona::showAlternativesForApplet(Plasma::Applet *applet) { const QString alternativesQML = package().filePath("appletalternativesui"); if (alternativesQML.isEmpty()) { return; } KDeclarative::QmlObject *qmlObj = new KDeclarative::QmlObject(this); qmlObj->setInitializationDelayed(true); qmlObj->setSource(QUrl::fromLocalFile(alternativesQML)); AlternativesHelper *helper = new AlternativesHelper(applet, qmlObj); qmlObj->rootContext()->setContextProperty(QStringLiteral("alternativesHelper"), helper); m_alternativesObjects << qmlObj; qmlObj->completeInitialization(); connect(qmlObj->rootObject(), SIGNAL(visibleChanged(bool)), this, SLOT(alternativesVisibilityChanged(bool))); connect(applet, &Plasma::Applet::destroyedChanged, this, [this, qmlObj] (bool destroyed) { if (!destroyed) { return; } QMutableListIterator it(m_alternativesObjects); while (it.hasNext()) { KDeclarative::QmlObject *obj = it.next(); if (obj == qmlObj) { it.remove(); obj->deleteLater(); } } }); } void ShellCorona::alternativesVisibilityChanged(bool visible) { if (visible) { return; } QObject *root = sender(); QMutableListIterator it(m_alternativesObjects); while (it.hasNext()) { KDeclarative::QmlObject *obj = it.next(); if (obj->rootObject() == root) { it.remove(); obj->deleteLater(); } } } void ShellCorona::unload() { if (m_shell.isEmpty()) { return; } qDeleteAll(m_desktopViewforId); m_desktopViewforId.clear(); qDeleteAll(m_panelViews); m_panelViews.clear(); m_desktopContainments.clear(); m_waitingPanels.clear(); m_activityContainmentPlugins.clear(); while (!containments().isEmpty()) { //deleting a containment will remove it from the list due to QObject::destroyed connect in Corona //this form doesn't crash, while qDeleteAll(containments()) does delete containments().first(); } } KSharedConfig::Ptr ShellCorona::applicationConfig() { return KSharedConfig::openConfig(); } void ShellCorona::requestApplicationConfigSync() { m_appConfigSyncTimer.start(); } void ShellCorona::loadDefaultLayout() { //pre-startup scripts QString script = m_lookAndFeelPackage.filePath("layouts", QString(shell() + "-prelayout.js").toLatin1()); if (!script.isEmpty()) { QFile file(script); if (file.open(QIODevice::ReadOnly | QIODevice::Text) ) { QString code = file.readAll(); qDebug() << "evaluating pre-startup script:" << script; WorkspaceScripting::ScriptEngine scriptEngine(this); connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::printError, this, [](const QString &msg) { qWarning() << msg; }); connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::print, this, [](const QString &msg) { qDebug() << msg; }); if (!scriptEngine.evaluateScript(code, script)) { qWarning() << "failed to initialize layout properly:" << script; } } } //NOTE: Is important the containments already exist for each screen // at the moment of the script execution,the same loop in :load() // is executed too late for (QScreen* screen : qGuiApp->screens()) { addOutput(screen); } script = ShellManager::s_testModeLayout; if (script.isEmpty()) { script = m_lookAndFeelPackage.filePath("layouts", QString(shell() + "-layout.js").toLatin1()); } if (script.isEmpty()) { script = package().filePath("defaultlayout"); } QFile file(script); if (file.open(QIODevice::ReadOnly | QIODevice::Text) ) { QString code = file.readAll(); qDebug() << "evaluating startup script:" << script; // We need to know which activities are here in order for // the scripting engine to work. activityAdded does not mind // if we pass it the same activity multiple times QStringList existingActivities = m_activityController->activities(); foreach (const QString &id, existingActivities) { activityAdded(id); } WorkspaceScripting::ScriptEngine scriptEngine(this); connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::printError, this, [](const QString &msg) { qWarning() << msg; }); connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::print, this, [](const QString &msg) { qDebug() << msg; }); if (!scriptEngine.evaluateScript(code, script)) { qWarning() << "failed to initialize layout properly:" << script; } } Q_EMIT startupCompleted(); } void ShellCorona::processUpdateScripts() { WorkspaceScripting::ScriptEngine scriptEngine(this); connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::printError, this, [](const QString &msg) { qWarning() << msg; }); connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::print, this, [](const QString &msg) { qDebug() << msg; }); foreach (const QString &script, WorkspaceScripting::ScriptEngine::pendingUpdateScripts(this)) { QFile file(script); if (file.open(QIODevice::ReadOnly | QIODevice::Text) ) { QString code = file.readAll(); scriptEngine.evaluateScript(code); } else { qWarning() << "Unable to open the script file" << script << "for reading"; } } } int ShellCorona::numScreens() const { return qGuiApp->screens().count(); } QRect ShellCorona::screenGeometry(int id) const { if (!m_desktopViewforId.contains(id)) { qWarning() << "requesting unexisting screen" << id; QScreen *s = qGuiApp->primaryScreen(); return s ? s->geometry() : QRect(); } return m_desktopViewforId.value(id)->geometry(); } QRegion ShellCorona::availableScreenRegion(int id) const { if (!m_desktopViewforId.contains(id)) { //each screen should have a view qWarning() << "requesting unexisting screen" << id; QScreen *s = qGuiApp->primaryScreen(); return s ? s->availableGeometry() : QRegion(); } DesktopView *view = m_desktopViewforId.value(id); QRegion r = view->geometry(); foreach (const PanelView *v, m_panelViews) { if (v->isVisible() && view->screen() == v->screen() && v->visibilityMode() != PanelView::AutoHide) { //if the panel is being moved around, we still want to calculate it from the edge r -= v->geometryByDistance(0); } } return r; } QRect ShellCorona::availableScreenRect(int id) const { if (!m_desktopViewforId.contains(id)) { //each screen should have a view qWarning() << "requesting unexisting screen" << id; QScreen *s = qGuiApp->primaryScreen(); return s ? s->availableGeometry() : QRect(); } DesktopView *view = m_desktopViewforId.value(id); QRect r = view->geometry(); foreach (PanelView *v, m_panelViews) { if (v->isVisible() && v->screen() == view->screen() && v->visibilityMode() != PanelView::AutoHide) { switch (v->location()) { case Plasma::Types::LeftEdge: r.setLeft(r.left() + v->thickness()); break; case Plasma::Types::RightEdge: r.setRight(r.right() - v->thickness()); break; case Plasma::Types::TopEdge: r.setTop(r.top() + v->thickness()); break; case Plasma::Types::BottomEdge: r.setBottom(r.bottom() - v->thickness()); default: break; } } } return r; } QStringList ShellCorona::availableActivities() const { return m_activityContainmentPlugins.keys(); } void ShellCorona::removeDesktop(DesktopView *desktopView) { const int idx = m_screenPool->id(desktopView->screenToFollow()->name()); if (!m_desktopViewforId.contains(idx)) { return; } QMutableHashIterator it(m_panelViews); while (it.hasNext()) { it.next(); PanelView *panelView = it.value(); if (panelView->containment()->screen() == idx) { m_waitingPanels << panelView->containment(); it.remove(); delete panelView; } } Q_ASSERT(m_desktopViewforId.value(idx) == desktopView); delete desktopView; m_desktopViewforId.remove(idx); } PanelView *ShellCorona::panelView(Plasma::Containment *containment) const { return m_panelViews.value(containment); } ///// SLOTS QList ShellCorona::panelsForScreen(QScreen *screen) const { QList ret; foreach (PanelView *v, m_panelViews) { if (v->screenToFollow() == screen) { ret += v; } } return ret; } DesktopView* ShellCorona::desktopForScreen(QScreen* screen) const { return m_desktopViewforId.value(m_screenPool->id(screen->name())); } void ShellCorona::screenRemoved(QScreen* screen) { if (DesktopView* v = desktopForScreen(screen)) { removeDesktop(v); } m_reconsiderOutputsTimer.start(); m_redundantOutputs.remove(screen); } bool ShellCorona::isOutputRedundant(QScreen* screen) const { Q_ASSERT(screen); const QRect geometry = screen->geometry(); //FIXME: QScreen doesn't have any idea of "this qscreen is clone of this other one //so this ultra inefficient heuristic has to stay until we have a slightly better api foreach (QScreen* s, qGuiApp->screens()) { if (screen == s) { continue; } const QRect sGeometry = s->geometry(); if (sGeometry.contains(geometry, false) && sGeometry.width() > geometry.width() && sGeometry.height() > geometry.height()) { return true; } } return false; } void ShellCorona::reconsiderOutputs() { foreach (QScreen* screen, qGuiApp->screens()) { if (m_redundantOutputs.contains(screen)) { if (!isOutputRedundant(screen)) { // qDebug() << "not redundant anymore" << out; addOutput(screen); } } else if (isOutputRedundant(screen)) { qDebug() << "new redundant screen" << screen; if (DesktopView* v = desktopForScreen(screen)) removeDesktop(v); m_redundantOutputs.insert(screen); } // else // qDebug() << "fine screen" << out; } updateStruts(); CHECK_SCREEN_INVARIANTS } void ShellCorona::addOutput(QScreen* screen) { Q_ASSERT(screen); connect(screen, &QScreen::geometryChanged, &m_reconsiderOutputsTimer, static_cast(&QTimer::start), Qt::UniqueConnection); if (isOutputRedundant(screen)) { m_redundantOutputs.insert(screen); return; } else { m_redundantOutputs.remove(screen); } int insertPosition = m_screenPool->id(screen->name()); if (insertPosition < 0) { insertPosition = m_screenPool->firstAvailableId(); } DesktopView *view = new DesktopView(this, screen); connect(view, &QQuickWindow::sceneGraphError, this, &ShellCorona::showOpenGLNotCompatibleWarning); Plasma::Containment *containment = createContainmentForActivity(m_activityController->currentActivity(), insertPosition); Q_ASSERT(containment); QAction *removeAction = containment->actions()->action(QStringLiteral("remove")); if (removeAction) { removeAction->deleteLater(); } m_screenPool->insertScreenMapping(insertPosition, screen->name()); m_desktopViewforId[insertPosition] = view; view->setContainment(containment); view->show(); Q_ASSERT(screen == view->screen()); //need to specifically call the reactToScreenChange, since when the screen is shown it's not yet //in the list. We still don't want to have an invisible view added. containment->reactToScreenChange(); //were there any panels for this screen before it popped up? if (!m_waitingPanels.isEmpty()) { m_waitingPanelsTimer.start(); } emit availableScreenRectChanged(); CHECK_SCREEN_INVARIANTS } Plasma::Containment *ShellCorona::createContainmentForActivity(const QString& activity, int screenNum) { if (m_desktopContainments.contains(activity)) { for (Plasma::Containment *cont : m_desktopContainments.value(activity)) { //in the case of a corrupt config file //with multiple containments with same lastScreen //it can happen two insertContainment happen for //the same screen, leading to the old containment //to be destroyed if (!cont->destroyed() && cont->screen() == screenNum && cont->activity() == activity) { return cont; } } } QString plugin = m_activityContainmentPlugins.value(activity); if (plugin.isEmpty()) { plugin = defaultContainmentPlugin(); } Plasma::Containment *containment = containmentForScreen(screenNum, plugin, QVariantList()); Q_ASSERT(containment); if (containment) { containment->setActivity(activity); insertContainment(activity, screenNum, containment); } return containment; } void ShellCorona::createWaitingPanels() { QList stillWaitingPanels; foreach (Plasma::Containment *cont, m_waitingPanels) { //ignore non existing (yet?) screens int requestedScreen = cont->lastScreen(); if (requestedScreen < 0) { requestedScreen = 0; } if (!m_desktopViewforId.contains(requestedScreen)) { stillWaitingPanels << cont; continue; } //TODO: does a similar check make sense? //Q_ASSERT(qBound(0, requestedScreen, m_screenPool->count() - 1) == requestedScreen); QScreen *screen = m_desktopViewforId.value(requestedScreen)->screenToFollow(); PanelView* panel = new PanelView(this, screen); connect(panel, &QQuickWindow::sceneGraphError, this, &ShellCorona::showOpenGLNotCompatibleWarning); connect(panel, &QWindow::visibleChanged, this, &Plasma::Corona::availableScreenRectChanged); connect(panel, &PanelView::locationChanged, this, &Plasma::Corona::availableScreenRectChanged); connect(panel, &PanelView::visibilityModeChanged, this, &Plasma::Corona::availableScreenRectChanged); connect(panel, &PanelView::thicknessChanged, this, &Plasma::Corona::availableScreenRectChanged); m_panelViews[cont] = panel; panel->setContainment(cont); cont->reactToScreenChange(); connect(cont, &QObject::destroyed, this, &ShellCorona::panelContainmentDestroyed); } m_waitingPanels = stillWaitingPanels; emit availableScreenRectChanged(); } void ShellCorona::panelContainmentDestroyed(QObject *cont) { auto view = m_panelViews.take(static_cast(cont)); view->deleteLater(); emit availableScreenRectChanged(); } void ShellCorona::handleContainmentAdded(Plasma::Containment *c) { connect(c, &Plasma::Containment::showAddWidgetsInterface, this, &ShellCorona::toggleWidgetExplorer); // Why queued? this is usually triggered after a context menu closes // due to its sync,modal nature it may eat some mouse event from the scene // waiting a bit to create a new window, the dialog seems to reliably // avoid the eating of one click in the panel after the context menu is gone connect(c, &Plasma::Containment::appletAlternativesRequested, this, &ShellCorona::showAlternativesForApplet, Qt::QueuedConnection); connect(c, &Plasma::Containment::appletCreated, this, [this, c] (Plasma::Applet *applet) { executeSetupPlasmoidScript(c, applet); }); } void ShellCorona::executeSetupPlasmoidScript(Plasma::Containment *containment, Plasma::Applet *applet) { - if (!applet->pluginInfo().isValid() || !containment->pluginInfo().isValid()) { + if (!applet->pluginMetaData().isValid() || !containment->pluginMetaData().isValid()) { return; } - const QString scriptFile = m_lookAndFeelPackage.filePath("plasmoidsetupscripts", applet->pluginInfo().pluginName() + ".js"); + const QString scriptFile = m_lookAndFeelPackage.filePath("plasmoidsetupscripts", applet->pluginMetaData().pluginId() + ".js"); if (scriptFile.isEmpty()) { return; } WorkspaceScripting::ScriptEngine scriptEngine(this); connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::printError, this, [](const QString &msg) { qWarning() << msg; }); connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::print, this, [](const QString &msg) { qDebug() << msg; }); QFile file(scriptFile); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qWarning() << i18n("Unable to load script file: %1", scriptFile); return; } QString script = file.readAll(); if (script.isEmpty()) { // qDebug() << "script is empty"; return; } scriptEngine.globalObject().setProperty(QStringLiteral("applet"), scriptEngine.wrap(applet)); scriptEngine.globalObject().setProperty(QStringLiteral("containment"), scriptEngine.wrap(containment)); scriptEngine.evaluateScript(script, scriptFile); } void ShellCorona::toggleWidgetExplorer() { const QPoint cursorPos = QCursor::pos(); foreach (DesktopView *view, m_desktopViewforId) { if (view->screen()->geometry().contains(cursorPos)) { //The view QML has to provide something to display the widget explorer view->rootObject()->metaObject()->invokeMethod(view->rootObject(), "toggleWidgetExplorer", Q_ARG(QVariant, QVariant::fromValue(sender()))); return; } } } void ShellCorona::toggleActivityManager() { const QPoint cursorPos = QCursor::pos(); foreach (DesktopView *view, m_desktopViewforId) { if (view->screen()->geometry().contains(cursorPos)) { //The view QML has to provide something to display the activity explorer view->rootObject()->metaObject()->invokeMethod(view->rootObject(), "toggleActivityManager", Qt::QueuedConnection); return; } } } void ShellCorona::syncAppConfig() { applicationConfig()->sync(); } void ShellCorona::setDashboardShown(bool show) { KWindowSystem::setShowingDesktop(show); } void ShellCorona::toggleDashboard() { setDashboardShown(!KWindowSystem::showingDesktop()); } void ShellCorona::loadInteractiveConsole() { if (KSharedConfig::openConfig()->isImmutable() || !KAuthorized::authorize(QStringLiteral("plasma-desktop/scripting_console"))) { delete m_interactiveConsole; m_interactiveConsole = 0; return; } if (!m_interactiveConsole) { const QString consoleQML = package().filePath("interactiveconsole"); if (consoleQML.isEmpty()) { return; } m_interactiveConsole = new KDeclarative::QmlObject(this); m_interactiveConsole->setInitializationDelayed(true); m_interactiveConsole->setSource(QUrl::fromLocalFile(consoleQML)); QObject *engine = new WorkspaceScripting::ScriptEngine(this, m_interactiveConsole); m_interactiveConsole->rootContext()->setContextProperty(QStringLiteral("scriptEngine"), engine); m_interactiveConsole->completeInitialization(); if (m_interactiveConsole->rootObject()) { connect(m_interactiveConsole->rootObject(), SIGNAL(visibleChanged(bool)), this, SLOT(interactiveConsoleVisibilityChanged(bool))); } } } void ShellCorona::showInteractiveConsole() { loadInteractiveConsole(); if (m_interactiveConsole && m_interactiveConsole->rootObject()) { m_interactiveConsole->rootObject()->setProperty("mode", "desktop"); m_interactiveConsole->rootObject()->setProperty("visible", true); } } void ShellCorona::loadScriptInInteractiveConsole(const QString &script) { showInteractiveConsole(); if (m_interactiveConsole) { m_interactiveConsole->rootObject()->setProperty("script", script); } } void ShellCorona::showInteractiveKWinConsole() { loadInteractiveConsole(); if (m_interactiveConsole && m_interactiveConsole->rootObject()) { m_interactiveConsole->rootObject()->setProperty("mode", "windowmanager"); m_interactiveConsole->rootObject()->setProperty("visible", true); } } void ShellCorona::loadKWinScriptInInteractiveConsole(const QString &script) { showInteractiveKWinConsole(); if (m_interactiveConsole) { m_interactiveConsole->rootObject()->setProperty("script", script); } } void ShellCorona::evaluateScript(const QString &script) { if (immutability() != Plasma::Types::Mutable) { if (calledFromDBus()) { sendErrorReply(QDBusError::Failed, QStringLiteral("Widgets are locked")); } return; } WorkspaceScripting::ScriptEngine scriptEngine(this); connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::printError, this, [](const QString &msg) { qWarning() << msg; }); connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::print, this, [](const QString &msg) { qDebug() << msg; }); scriptEngine.evaluateScript(script); if (scriptEngine.hasUncaughtException() && calledFromDBus()) { sendErrorReply(QDBusError::Failed, scriptEngine.uncaughtException().toString()); } } void ShellCorona::interactiveConsoleVisibilityChanged(bool visible) { if (!visible) { m_interactiveConsole->deleteLater(); m_interactiveConsole = nullptr; } } void ShellCorona::checkActivities() { KActivities::Controller::ServiceStatus status = m_activityController->serviceStatus(); //qDebug() << "$%$%$#%$%$%Status:" << status; if (status != KActivities::Controller::Running) { //panic and give up - better than causing a mess qDebug() << "ShellCorona::checkActivities is called whilst activity daemon is still connecting"; return; } QStringList existingActivities = m_activityController->activities(); foreach (const QString &id, existingActivities) { activityAdded(id); } // Checking whether the result we got is valid. Just in case. Q_ASSERT_X(!existingActivities.isEmpty(), "isEmpty", "There are no activities, and the service is running"); Q_ASSERT_X(existingActivities[0] != QStringLiteral("00000000-0000-0000-0000-000000000000"), "null uuid", "There is a nulluuid activity present"); // Killing the unassigned containments foreach (Plasma::Containment *cont, containments()) { if ((cont->containmentType() == Plasma::Types::DesktopContainment || cont->containmentType() == Plasma::Types::CustomContainment) && !existingActivities.contains(cont->activity())) { cont->destroy(); } } } void ShellCorona::currentActivityChanged(const QString &newActivity) { // qDebug() << "Activity changed:" << newActivity; foreach (int id, m_desktopViewforId.keys()) { Plasma::Containment *c = createContainmentForActivity(newActivity, id); QAction *removeAction = c->actions()->action(QStringLiteral("remove")); if (removeAction) { removeAction->deleteLater(); } m_desktopViewforId.value(id)->setContainment(c); } } void ShellCorona::activityAdded(const QString &id) { //TODO more sanity checks if (m_activityContainmentPlugins.contains(id)) { qWarning() << "Activity added twice" << id; return; } m_activityContainmentPlugins.insert(id, defaultContainmentPlugin()); } void ShellCorona::activityRemoved(const QString &id) { m_activityContainmentPlugins.remove(id); if (m_desktopContainments.contains(id)) { for (auto cont : m_desktopContainments.value(id)) { cont->destroy(); } } } void ShellCorona::insertActivity(const QString &id, const QString &plugin) { activityAdded(id); const QString currentActivityReally = m_activityController->currentActivity(); // TODO: This needs to go away! // The containment creation API does not know when we have a // new activity to create a containment for, we need to pretend // that the current activity has been changed QFuture currentActivity = m_activityController->setCurrentActivity(id); awaitFuture(currentActivity); if (!currentActivity.result()) { qDebug() << "Failed to create and switch to the activity"; return; } while (m_activityController->currentActivity() != id) { QCoreApplication::processEvents(); } m_activityContainmentPlugins.insert(id, plugin); foreach (int screenId, m_desktopViewforId.keys()) { Plasma::Containment *c = createContainmentForActivity(id, screenId); if (c) { c->config().writeEntry("lastScreen", screenId); } } } Plasma::Containment *ShellCorona::setContainmentTypeForScreen(int screen, const QString &plugin) { Plasma::Containment *oldContainment = containmentForScreen(screen); //no valid containment in given screen, giving up if (!oldContainment) { return 0; } if (plugin.isEmpty()) { return oldContainment; } DesktopView *view = 0; foreach (DesktopView *v, m_desktopViewforId) { if (v->containment() == oldContainment) { view = v; break; } } //no view? give up if (!view) { return oldContainment; } //create a new containment Plasma::Containment *newContainment = createContainmentDelayed(plugin); //if creation failed or invalid plugin, give up if (!newContainment) { return oldContainment; - } else if (!newContainment->pluginInfo().isValid()) { + } else if (!newContainment->pluginMetaData().isValid()) { newContainment->deleteLater(); return oldContainment; } newContainment->setWallpaper(oldContainment->wallpaper()); //At this point we have a valid new containment from plugin and a view //copy all configuration groups (excluded applets) KConfigGroup oldCg = oldContainment->config(); //newCg *HAS* to be from a KSharedConfig, because some KConfigSkeleton will need to be synced //this makes the configscheme work KConfigGroup newCg(KSharedConfig::openConfig(oldCg.config()->name()), "Containments"); newCg = KConfigGroup(&newCg, QString::number(newContainment->id())); //this makes containment->config() work, is a separate thing from its configscheme KConfigGroup newCg2 = newContainment->config(); foreach (const QString &group, oldCg.groupList()) { if (group != QLatin1String("Applets")) { KConfigGroup subGroup(&oldCg, group); KConfigGroup newSubGroup(&newCg, group); subGroup.copyTo(&newSubGroup); KConfigGroup newSubGroup2(&newCg2, group); subGroup.copyTo(&newSubGroup2); } } newContainment->init(); newCg.writeEntry("activityId", oldContainment->activity()); newContainment->restore(newCg); newContainment->updateConstraints(Plasma::Types::StartupCompletedConstraint); newContainment->flushPendingConstraintsEvents(); emit containmentAdded(newContainment); //Move the applets foreach (Plasma::Applet *applet, oldContainment->applets()) { newContainment->addApplet(applet); } //remove the "remove" action QAction *removeAction = newContainment->actions()->action(QStringLiteral("remove")); if (removeAction) { removeAction->deleteLater(); } view->setContainment(newContainment); newContainment->setActivity(oldContainment->activity()); m_desktopContainments.remove(oldContainment->activity()); insertContainment(oldContainment->activity(), screen, newContainment); //removing the focus from the item that is going to be destroyed //fixes a crash //delayout the destruction of the old containment fixes another crash view->rootObject()->setFocus(true, Qt::MouseFocusReason); QTimer::singleShot(2500, oldContainment, &Plasma::Applet::destroy); //Save now as we now have a screen, so lastScreen will not be -1 newContainment->save(newCg); requestConfigSync(); emit availableScreenRectChanged(); return newContainment; } void ShellCorona::checkAddPanelAction(const QStringList &sycocaChanges) { if (!sycocaChanges.isEmpty() && !sycocaChanges.contains(QStringLiteral("services"))) { return; } delete m_addPanelAction; m_addPanelAction = 0; delete m_addPanelsMenu; m_addPanelsMenu = 0; KPluginInfo::List panelContainmentPlugins = Plasma::PluginLoader::listContainmentsOfType(QStringLiteral("Panel")); auto filter = [](const KPluginMetaData &md) -> bool { return md.value(QStringLiteral("NoDisplay")) != QStringLiteral("true") && md.value(QStringLiteral("X-Plasma-ContainmentCategories")).contains(QStringLiteral("panel")); }; QList templates = KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/LayoutTemplate"), QString(), filter); if (panelContainmentPlugins.count() + templates.count() == 1) { m_addPanelAction = new QAction(i18n("Add Panel"), this); m_addPanelAction->setData(Plasma::Types::AddAction); connect(m_addPanelAction, SIGNAL(triggered(bool)), this, SLOT(addPanel())); } else if (!panelContainmentPlugins.isEmpty()) { m_addPanelsMenu = new QMenu; m_addPanelAction = m_addPanelsMenu->menuAction(); m_addPanelAction->setText(i18n("Add Panel")); m_addPanelAction->setData(Plasma::Types::AddAction); connect(m_addPanelsMenu, &QMenu::aboutToShow, this, &ShellCorona::populateAddPanelsMenu); connect(m_addPanelsMenu, SIGNAL(triggered(QAction*)), this, SLOT(addPanel(QAction*))); } if (m_addPanelAction) { m_addPanelAction->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); actions()->addAction(QStringLiteral("add panel"), m_addPanelAction); } } void ShellCorona::populateAddPanelsMenu() { m_addPanelsMenu->clear(); const KPluginInfo emptyInfo; KPluginInfo::List panelContainmentPlugins = Plasma::PluginLoader::listContainmentsOfType(QStringLiteral("Panel")); QMap > sorted; foreach (const KPluginInfo &plugin, panelContainmentPlugins) { if (plugin.property(QStringLiteral("NoDisplay")).toString() == QStringLiteral("true")) { continue; } sorted.insert(plugin.name(), qMakePair(plugin, KPluginMetaData())); } auto filter = [](const KPluginMetaData &md) -> bool { return md.value(QStringLiteral("NoDisplay")) != QStringLiteral("true") && md.value(QStringLiteral("X-Plasma-ContainmentCategories")).contains(QStringLiteral("panel")); }; const QList templates = KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/LayoutTemplate"), QString(), filter); for (auto tpl : templates) { sorted.insert(tpl.name(), qMakePair(emptyInfo, tpl)); } QMapIterator > it(sorted); KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LayoutTemplate")); while (it.hasNext()) { it.next(); QPair pair = it.value(); if (pair.first.isValid()) { KPluginInfo plugin = pair.first; QAction *action = m_addPanelsMenu->addAction(i18n("Empty %1", plugin.name())); if (!plugin.icon().isEmpty()) { action->setIcon(QIcon::fromTheme(plugin.icon())); } action->setData(plugin.pluginName()); } else { KPluginInfo info(pair.second); package.setPath(info.pluginName()); const QString scriptFile = package.filePath("mainscript"); if (!scriptFile.isEmpty()) { QAction *action = m_addPanelsMenu->addAction(info.name()); action->setData(QStringLiteral("plasma-desktop-template:%1").arg(info.pluginName())); } } } } void ShellCorona::addPanel() { KPluginInfo::List panelPlugins = Plasma::PluginLoader::listContainmentsOfType(QStringLiteral("Panel")); if (!panelPlugins.isEmpty()) { addPanel(panelPlugins.first().pluginName()); } } void ShellCorona::addPanel(QAction *action) { const QString plugin = action->data().toString(); if (plugin.startsWith(QLatin1String("plasma-desktop-template:"))) { WorkspaceScripting::ScriptEngine scriptEngine(this); connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::printError, this, [](const QString &msg) { qWarning() << msg; }); connect(&scriptEngine, &WorkspaceScripting::ScriptEngine::print, this, [](const QString &msg) { qDebug() << msg; }); const QString templateName = plugin.right(plugin.length() - qstrlen("plasma-desktop-template:")); scriptEngine.evaluateScript(QStringLiteral("loadTemplate(\"%1\")").arg(templateName)); } else if (!plugin.isEmpty()) { addPanel(plugin); } } Plasma::Containment *ShellCorona::addPanel(const QString &plugin) { Plasma::Containment *panel = createContainment(plugin); if (!panel) { return 0; } QList availableLocations; availableLocations << Plasma::Types::LeftEdge << Plasma::Types::TopEdge << Plasma::Types::RightEdge << Plasma::Types::BottomEdge; foreach (const Plasma::Containment *cont, m_panelViews.keys()) { availableLocations.removeAll(cont->location()); } Plasma::Types::Location loc; if (availableLocations.isEmpty()) { loc = Plasma::Types::TopEdge; } else { loc = availableLocations.first(); } panel->setLocation(loc); switch (loc) { case Plasma::Types::LeftEdge: case Plasma::Types::RightEdge: panel->setFormFactor(Plasma::Types::Vertical); break; default: panel->setFormFactor(Plasma::Types::Horizontal); break; } Q_ASSERT(panel); m_waitingPanels << panel; //not creating the panel view yet in order to have the same code path //between the first and subsequent plasma starts. we want to have the panel appearing only when its layout is completed, to not have //many visible relayouts. otherwise we had even panel resizes at startup that //made al lthe full representations be loaded. m_waitingPanelsTimer.start(); const QPoint cursorPos(QCursor::pos()); foreach (QScreen *screen, QGuiApplication::screens()) { //m_panelViews.contains(panel) == false iff addPanel is executed in a startup script if (screen->geometry().contains(cursorPos) && m_panelViews.contains(panel)) { m_panelViews[panel]->setScreenToFollow(screen); break; } } return panel; } int ShellCorona::screenForContainment(const Plasma::Containment *containment) const { //case in which this containment is child of an applet, hello systray :) if (Plasma::Applet *parentApplet = qobject_cast(containment->parent())) { if (Plasma::Containment* cont = parentApplet->containment()) { return screenForContainment(cont); } else { return -1; } } //if the desktop views already exist, base the decision upon them foreach (int id, m_desktopViewforId.keys()) { if (m_desktopViewforId.value(id)->containment() == containment && containment->activity() == m_activityController->currentActivity()) { return id; } } //if the panel views already exist, base upon them PanelView *view = m_panelViews.value(containment); if (view && view->screenToFollow()) { return m_screenPool->id(view->screenToFollow()->name()); } //Failed? fallback on lastScreen() //lastScreen() is the correct screen for panels //It is also correct for desktops *that have the correct activity()* //a containment with lastScreen() == 0 but another activity, //won't be associated to a screen // qDebug() << "ShellCorona screenForContainment: " << containment << " Last screen is " << containment->lastScreen(); for (auto screen : qGuiApp->screens()) { // containment->lastScreen() == m_screenPool->id(screen->name()) to check if the lastScreen refers to a screen that exists/it's known if (containment->lastScreen() == m_screenPool->id(screen->name()) && (containment->activity() == m_activityController->currentActivity() || containment->containmentType() == Plasma::Types::PanelContainment || containment->containmentType() == Plasma::Types::CustomPanelContainment)) { return containment->lastScreen(); } } return -1; } void ShellCorona::nextActivity() { const QStringList list = m_activityController->activities(KActivities::Info::Running); if (list.isEmpty()) { return; } const int start = list.indexOf(m_activityController->currentActivity()); const int i = (start + 1) % list.size(); m_activityController->setCurrentActivity(list.at(i)); } void ShellCorona::previousActivity() { const QStringList list = m_activityController->activities(KActivities::Info::Running); if (list.isEmpty()) { return; } const int start = list.indexOf(m_activityController->currentActivity()); int i = start - 1; if(i < 0) { i = list.size() - 1; } m_activityController->setCurrentActivity(list.at(i)); } void ShellCorona::stopCurrentActivity() { const QStringList list = m_activityController->activities(KActivities::Info::Running); if (list.isEmpty()) { return; } m_activityController->stopActivity(m_activityController->currentActivity()); } void ShellCorona::insertContainment(const QString &activity, int screenNum, Plasma::Containment *containment) { Plasma::Containment *cont = nullptr; for (Plasma::Containment *c : m_desktopContainments.value(activity)) { if (c->screen() == screenNum) { cont = c; if (containment == cont) { return; } break; } } Q_ASSERT(!m_desktopContainments.value(activity).values().contains(containment)); if (cont) { disconnect(cont, SIGNAL(destroyed(QObject*)), this, SLOT(desktopContainmentDestroyed(QObject*))); cont->destroy(); } m_desktopContainments[activity].insert(containment); //when a containment gets deleted update our map of containments connect(containment, SIGNAL(destroyed(QObject*)), this, SLOT(desktopContainmentDestroyed(QObject*))); } void ShellCorona::desktopContainmentDestroyed(QObject *obj) { // when QObject::destroyed arrives, ~Plasma::Containment has run, // members of Containment are not accessible anymore, // so keep ugly bookeeping by hand auto containment = static_cast(obj); for (auto a : m_desktopContainments) { QMutableSetIterator it(a); while (it.hasNext()) { it.next(); if (it.value() == containment) { it.remove(); return; } } } } void ShellCorona::showOpenGLNotCompatibleWarning() { static bool s_multipleInvokations = false; if (s_multipleInvokations) { return; } s_multipleInvokations = true; QCoreApplication::setAttribute(Qt::AA_ForceRasterWidgets); QMessageBox::critical(nullptr, i18n("Plasma Failed To Start"), i18n("Plasma is unable to start as it could not correctly use OpenGL 2.\n Please check that your graphic drivers are set up correctly.")); qCritical("Open GL context could not be created"); // this doesn't work and I have no idea why. QCoreApplication::exit(1); } void ShellCorona::setupWaylandIntegration() { if (!KWindowSystem::isPlatformWayland()) { return; } using namespace KWayland::Client; ConnectionThread *connection = ConnectionThread::fromApplication(this); if (!connection) { return; } Registry *registry = new Registry(this); registry->create(connection); connect(registry, &Registry::plasmaShellAnnounced, this, [this, registry] (quint32 name, quint32 version) { m_waylandPlasmaShell = registry->createPlasmaShell(name, version, this); } ); registry->setup(); } KWayland::Client::PlasmaShell *ShellCorona::waylandPlasmaShellInterface() const { return m_waylandPlasmaShell; } ScreenPool *ShellCorona::screenPool() const { return m_screenPool; } QList ShellCorona::screenIds() const { return m_desktopViewforId.keys(); } QString ShellCorona::defaultContainmentPlugin() const { QString plugin = m_lnfDefaultsConfig.readEntry("Containment", QString()); if (plugin.isEmpty()) { plugin = m_desktopDefaultsConfig.readEntry("Containment", "org.kde.desktopcontainment"); } return plugin; } void ShellCorona::updateStruts() { foreach(PanelView* view, m_panelViews) { view->updateStruts(); } } void ShellCorona::activateLauncherMenu() { for (auto it = m_panelViews.constBegin(), end = m_panelViews.constEnd(); it != end; ++it) { const auto applets = it.key()->applets(); for (auto applet : applets) { - if (applet->pluginInfo().property("X-Plasma-Provides").toStringList().contains(QStringLiteral("org.kde.plasma.launchermenu"))) { + const auto provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); + if (provides.contains(QLatin1String("org.kde.plasma.launchermenu"))) { if (!applet->globalShortcut().isEmpty()) { emit applet->activated(); return; } } } } } // Desktop corona handler #include "moc_shellcorona.cpp"