diff --git a/src/plasma/containment.cpp b/src/plasma/containment.cpp index 15b8abadc..46d092763 100644 --- a/src/plasma/containment.cpp +++ b/src/plasma/containment.cpp @@ -1,605 +1,605 @@ /* * Copyright 2007 by Aaron Seigo * Copyright 2008 by Ménard Alexis * Copyright 2009 Chani Armitage * Copyright 2012 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 "containment.h" #include "private/containment_p.h" #include "config-plasma.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #if !PLASMA_NO_KIO #include "kio/jobclasses.h" // for KIO::JobFlags #include "kio/job.h" #include "kio/scheduler.h" #endif #include "containmentactions.h" #include "corona.h" #include "pluginloader.h" #include "debug_p.h" #include "private/applet_p.h" #include "plasma/plasma.h" namespace Plasma { Containment::Containment(QObject *parent, const QString &serviceId, uint containmentId) : Applet(parent, serviceId, containmentId), d(new ContainmentPrivate(this)) { // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point setContainmentType(Types::CustomContainment); setHasConfigurationInterface(true); } Containment::Containment(QObject *parent, const QVariantList &args) : Applet(parent, args), d(new ContainmentPrivate(this)) { // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point setHasConfigurationInterface(true); } Containment::Containment(const KPluginMetaData &md, uint appletId) : Applet(md, nullptr, appletId), d(new ContainmentPrivate(this)) { // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point setHasConfigurationInterface(true); } Containment::~Containment() { qDeleteAll(d->localActionPlugins); delete d; } void Containment::init() { Applet::init(); static_cast(this)->d->setupScripting(); if (d->type == Types::NoContainmentType) { //setContainmentType(Plasma::Types::DesktopContainment); //Try to determine the containment type. It must be done as soon as possible QString type = pluginMetaData().value(QStringLiteral("X-Plasma-ContainmentType")); if (type == QLatin1String("Panel")) { setContainmentType(Plasma::Types::PanelContainment); } else if (type == QLatin1String("Custom")) { setContainmentType(Plasma::Types::CustomContainment); } else if (type == QLatin1String("CustomPanel")) { setContainmentType(Plasma::Types::CustomPanelContainment); //default to desktop } else { setContainmentType(Plasma::Types::DesktopContainment); } } //connect actions ContainmentPrivate::addDefaultActions(actions(), this); bool unlocked = immutability() == Types::Mutable; //fix the text of the actions that need title() //btw, do we really want to use title() when it's a desktopcontainment? QAction *closeApplet = actions()->action(QStringLiteral("remove")); if (closeApplet) { closeApplet->setText(i18nc("%1 is the name of the applet", "Remove this %1", title())); } QAction *configAction = actions()->action(QStringLiteral("configure")); if (configAction) { configAction->setText(i18nc("%1 is the name of the applet", "%1 Settings", title())); } QAction *appletBrowserAction = actions()->action(QStringLiteral("add widgets")); if (appletBrowserAction) { appletBrowserAction->setVisible(unlocked); appletBrowserAction->setEnabled(unlocked); connect(appletBrowserAction, SIGNAL(triggered()), this, SLOT(triggerShowAddWidgets())); } if (immutability() != Types::SystemImmutable && corona()) { QAction *lockDesktopAction = corona()->actions()->action(QStringLiteral("lock widgets")); //keep a pointer so nobody notices it moved to corona if (lockDesktopAction) { actions()->addAction(QStringLiteral("lock widgets"), lockDesktopAction); } } //HACK: this is valid only in the systray case connect(this, &Containment::configureRequested, this, [=] (Plasma::Applet *a) { if (Plasma::Applet *p = qobject_cast(parent())) { emit p->containment()->configureRequested(a); } }); } // helper function for sorting the list of applets bool appletConfigLessThan(const KConfigGroup &c1, const KConfigGroup &c2) { int i1 = c1.readEntry("id", 0); int i2 = c2.readEntry("id", 0); return (i1 < i2); } void Containment::restore(KConfigGroup &group) { /* #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "!!!!!!!!!!!!initConstraints" << group.name() << d->type; // qCDebug(LOG_PLASMA) << " location:" << group.readEntry("location", (int)d->location); // qCDebug(LOG_PLASMA) << " geom:" << group.readEntry("geometry", geometry()); // qCDebug(LOG_PLASMA) << " formfactor:" << group.readEntry("formfactor", (int)d->formFactor); // qCDebug(LOG_PLASMA) << " screen:" << group.readEntry("screen", d->screen); #endif */ setLocation((Plasma::Types::Location)group.readEntry("location", (int)d->location)); setFormFactor((Plasma::Types::FormFactor)group.readEntry("formfactor", (int)d->formFactor)); d->lastScreen = group.readEntry("lastScreen", d->lastScreen); setWallpaper(group.readEntry("wallpaperplugin", ContainmentPrivate::defaultWallpaper)); d->activityId = group.readEntry("activityId", QString()); flushPendingConstraintsEvents(); restoreContents(group); setImmutability((Types::ImmutabilityType)group.readEntry("immutability", (int)Types::Mutable)); if (isContainment()) { KConfigGroup cfg = KConfigGroup(corona()->config(), "ActionPlugins"); cfg = KConfigGroup(&cfg, QString::number(containmentType())); //qCDebug(LOG_PLASMA) << cfg.keyList(); if (cfg.exists()) { foreach (const QString &key, cfg.keyList()) { //qCDebug(LOG_PLASMA) << "loading" << key; setContainmentActions(key, cfg.readEntry(key, QString())); } } else { //shell defaults KConfigGroup defaultActionsCfg; switch (d->type) { case Plasma::Types::PanelContainment: /* fall through*/ case Plasma::Types::CustomPanelContainment: defaultActionsCfg = KConfigGroup(KSharedConfig::openConfig(corona()->kPackage().filePath("defaults")), "Panel"); break; case Plasma::Types::DesktopContainment: defaultActionsCfg = KConfigGroup(KSharedConfig::openConfig(corona()->kPackage().filePath("defaults")), "Desktop"); break; default: //for any other type of containment, there are no defaults break; } if (defaultActionsCfg.isValid()) { defaultActionsCfg = KConfigGroup(&defaultActionsCfg, "ContainmentActions"); foreach (const QString &key, defaultActionsCfg.keyList()) { setContainmentActions(key, defaultActionsCfg.readEntry(key, QString())); } } } } /* #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Containment" << id() << #endif "screen" << screen() << "geometry is" << geometry() << "config entries" << group.entryMap(); */ } void Containment::save(KConfigGroup &g) const { if (Applet::d->transient) { return; } KConfigGroup group = g; if (!group.isValid()) { group = config(); } // locking is saved in Applet::save Applet::save(group); // group.writeEntry("screen", d->screen); group.writeEntry("lastScreen", d->lastScreen); group.writeEntry("formfactor", (int)d->formFactor); group.writeEntry("location", (int)d->location); group.writeEntry("activityId", d->activityId); group.writeEntry("wallpaperplugin", d->wallpaper); saveContents(group); } void Containment::saveContents(KConfigGroup &group) const { KConfigGroup applets(&group, "Applets"); foreach (const Applet *applet, d->applets) { KConfigGroup appletConfig(&applets, QString::number(applet->id())); applet->save(appletConfig); } } void Containment::restoreContents(KConfigGroup &group) { KConfigGroup applets(&group, "Applets"); //restore the applets ordered by id QStringList groups = applets.groupList(); qSort(groups.begin(), groups.end()); // Sort the applet configs in order of geometry to ensure that applets // are added from left to right or top to bottom for a panel containment QList appletConfigs; foreach (const QString &appletGroup, groups) { //qCDebug(LOG_PLASMA) << "reading from applet group" << appletGroup; KConfigGroup appletConfig(&applets, appletGroup); appletConfigs.append(appletConfig); } qStableSort(appletConfigs.begin(), appletConfigs.end(), appletConfigLessThan); QMutableListIterator it(appletConfigs); while (it.hasNext()) { KConfigGroup &appletConfig = it.next(); int appId = appletConfig.name().toUInt(); QString plugin = appletConfig.readEntry("plugin", QString()); if (plugin.isEmpty()) { continue; } d->createApplet(plugin, QVariantList(), appId); } //if there are no applets, none of them is "loading" if (Containment::applets().isEmpty()) { d->appletsUiReady = true; } foreach (Applet *applet, Containment::applets()) { if (!applet->pluginMetaData().isValid()) { applet->updateConstraints(Plasma::Types::UiReadyConstraint); } } } Plasma::Types::ContainmentType Containment::containmentType() const { return d->type; } void Containment::setContainmentType(Plasma::Types::ContainmentType type) { if (d->type == type) { return; } d->type = type; emit containmentTypeChanged(); } Corona *Containment::corona() const { if(Plasma::Corona* corona = qobject_cast(parent())) { return corona; //case in which this containment is child of an applet, hello systray :) } else { Plasma::Applet *parentApplet = qobject_cast(parent()); if (parentApplet && parentApplet->containment()) { return parentApplet->containment()->corona(); } } return nullptr; } void Containment::setFormFactor(Types::FormFactor formFactor) { if (d->formFactor == formFactor) { return; } //qCDebug(LOG_PLASMA) << "switching FF to " << formFactor; d->formFactor = formFactor; updateConstraints(Plasma::Types::FormFactorConstraint); KConfigGroup c = config(); c.writeEntry("formfactor", (int)formFactor); emit configNeedsSaving(); emit formFactorChanged(formFactor); } void Containment::setLocation(Types::Location location) { if (d->location == location) { return; } d->location = location; foreach (Applet *applet, d->applets) { applet->updateConstraints(Plasma::Types::LocationConstraint); } updateConstraints(Plasma::Types::LocationConstraint); KConfigGroup c = config(); c.writeEntry("location", (int)location); emit configNeedsSaving(); emit locationChanged(location); } Applet *Containment::createApplet(const QString &name, const QVariantList &args) { Plasma::Applet *applet = d->createApplet(name, args); if (applet) { emit appletCreated(applet); } return applet; } void Containment::addApplet(Applet *applet) { if (!applet) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "adding null applet!?!"; #endif return; } if (immutability() != Types::Mutable && !applet->property("org.kde.plasma:force-create").toBool()) { return; } - if (d->applets.contains(applet)) { #ifndef NDEBUG + if (d->applets.contains(applet)) { // qCDebug(LOG_PLASMA) << "already have this applet!"; -#endif } +#endif Containment *currentContainment = applet->containment(); if (currentContainment && currentContainment != this) { emit currentContainment->appletRemoved(applet); disconnect(applet, 0, currentContainment, 0); KConfigGroup oldConfig = applet->config(); currentContainment->d->applets.removeAll(applet); applet->setParent(this); // now move the old config to the new location //FIXME: this doesn't seem to get the actual main config group containing plugin=, etc KConfigGroup c = config().group("Applets").group(QString::number(applet->id())); oldConfig.reparent(&c); applet->d->resetConfigurationObject(); disconnect(applet, SIGNAL(activated()), currentContainment, SIGNAL(activated())); //change the group to its configloader, if any //FIXME: this is very, very brutal if (applet->configScheme()) { const QString oldGroupPrefix = QStringLiteral("Containments") + QString::number(currentContainment->id()) + QStringLiteral("Applets"); const QString newGroupPrefix = QStringLiteral("Containments") + QString::number(id()) + QStringLiteral("Applets"); applet->configScheme()->setCurrentGroup(applet->configScheme()->currentGroup().replace(0, oldGroupPrefix.length(), newGroupPrefix)); foreach (KConfigSkeletonItem *item, applet->configScheme()->items()) { item->setGroup(item->group().replace(0, oldGroupPrefix.length(), newGroupPrefix)); } } } else { applet->setParent(this); } //make sure the applets are sorted by id auto position = std::lower_bound(d->applets.begin(), d->applets.end(), applet, [](Plasma::Applet *a1, Plasma::Applet *a2) { return a1->id() < a2->id(); }); d->applets.insert(position, applet); if (!d->uiReady) { d->loadingApplets << applet; } connect(applet, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving())); connect(applet, SIGNAL(appletDeleted(Plasma::Applet*)), this, SLOT(appletDeleted(Plasma::Applet*))); connect(applet, SIGNAL(statusChanged(Plasma::Types::ItemStatus)), this, SLOT(checkStatus(Plasma::Types::ItemStatus))); connect(applet, SIGNAL(activated()), this, SIGNAL(activated())); if (!currentContainment) { const bool isNew = applet->d->mainConfigGroup()->entryMap().isEmpty(); if (!isNew) { applet->restore(*applet->d->mainConfigGroup()); } applet->init(); applet->d->setupScripting(); if (isNew) { applet->save(*applet->d->mainConfigGroup()); emit configNeedsSaving(); } //FIXME: an on-appear animation would be nice to have again } applet->updateConstraints(Plasma::Types::AllConstraints); applet->flushPendingConstraintsEvents(); emit appletAdded(applet); if (!currentContainment) { applet->updateConstraints(Plasma::Types::StartupCompletedConstraint); applet->flushPendingConstraintsEvents(); } applet->d->scheduleModificationNotification(); } QList Containment::applets() const { return d->applets; } int Containment::screen() const { Q_ASSERT(corona()); if (Corona* c = corona()) { return c->screenForContainment(this); } else { return -1; } } int Containment::lastScreen() const { return d->lastScreen; } void Containment::setWallpaper(const QString &pluginName) { if (pluginName != d->wallpaper) { d->wallpaper = pluginName; KConfigGroup cfg = config(); cfg.writeEntry("wallpaperplugin", d->wallpaper); emit configNeedsSaving(); emit wallpaperChanged(); } } QString Containment::wallpaper() const { return d->wallpaper; } void Containment::setContainmentActions(const QString &trigger, const QString &pluginName) { KConfigGroup cfg = d->containmentActionsConfig(); ContainmentActions *plugin = 0; plugin = containmentActions().value(trigger); if (plugin && plugin->pluginInfo().pluginName() != pluginName) { containmentActions().remove(trigger); delete plugin; plugin = 0; } if (pluginName.isEmpty()) { cfg.deleteEntry(trigger); } else if (plugin) { //it already existed, just reload config plugin->setContainment(this); //to be safe //FIXME make a truly unique config group KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); plugin->restore(pluginConfig); } else { plugin = PluginLoader::self()->loadContainmentActions(this, pluginName); if (plugin) { cfg.writeEntry(trigger, pluginName); containmentActions().insert(trigger, plugin); plugin->setContainment(this); KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); plugin->restore(pluginConfig); } else { //bad plugin... gets removed. is this a feature or a bug? cfg.deleteEntry(trigger); } } emit configNeedsSaving(); } QHash &Containment::containmentActions() { return d->localActionPlugins; } bool Containment::isUiReady() const { return d->uiReady && d->appletsUiReady && Applet::d->started; } void Containment::setActivity(const QString &activityId) { if (activityId.isEmpty() || d->activityId == activityId) { return; } d->activityId = activityId; KConfigGroup c = config(); c.writeEntry("activityId", activityId); emit configNeedsSaving(); emit activityChanged(activityId); } QString Containment::activity() const { return d->activityId; } void Containment::reactToScreenChange() { int newScreen = screen(); if (newScreen >= 0) { d->lastScreen = newScreen; KConfigGroup c = config(); c.writeEntry("lastScreen", d->lastScreen); emit configNeedsSaving(); } emit screenChanged(newScreen); } } // Plasma namespace #include "moc_containment.cpp"