File indexing completed on 2023-10-01 04:11:42

0001 /*
0002     SPDX-FileCopyrightText: 2005 Aaron Seigo <aseigo@kde.org>
0003     SPDX-FileCopyrightText: 2007 Riccardo Iaconelli <riccardo@kde.org>
0004     SPDX-FileCopyrightText: 2008 Ménard Alexis <darktears31@gmail.com>
0005     SPDX-FileCopyrightText: 2009 Chani Armitage <chani@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "applet.h"
0011 #include "private/applet_p.h"
0012 
0013 #include "config-plasma.h"
0014 
0015 #include <QAbstractButton>
0016 #include <QDebug>
0017 #include <QFile>
0018 #include <QList>
0019 #include <QMessageBox>
0020 #include <QMetaEnum>
0021 
0022 #include <KActionCollection>
0023 #include <KAuthorized>
0024 #include <KColorScheme>
0025 #include <KConfigLoader>
0026 #include <KDesktopFile>
0027 #include <KGlobalAccel>
0028 #include <KLocalizedString>
0029 #include <KPackage/Package>
0030 #include <KService>
0031 
0032 #include "containment.h"
0033 #include "corona.h"
0034 #include "package.h"
0035 #include "plasma.h"
0036 #include "pluginloader.h"
0037 #include "scripting/appletscript.h"
0038 
0039 #include "debug_p.h"
0040 #include "private/associatedapplicationmanager_p.h"
0041 #include "private/containment_p.h"
0042 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 83)
0043 #include "private/package_p.h"
0044 #endif
0045 
0046 #include <cmath>
0047 #include <limits>
0048 
0049 namespace Plasma
0050 {
0051 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 86)
0052 static KPluginMetaData appletMetadataForDirectory(const QString &path)
0053 {
0054     return QFile::exists(path + QLatin1String("/metadata.json"))
0055         ? KPluginMetaData(path + QLatin1String("/metadata.json"))
0056         : KPluginMetaData::fromDesktopFile(path + QLatin1String("/metadata.desktop"), {QStringLiteral("plasma-applet.desktop")});
0057 }
0058 
0059 Applet::Applet(const KPluginMetaData &info, QObject *parent, uint appletId)
0060     : QObject(parent)
0061     , d(new AppletPrivate(info, appletId, this))
0062 {
0063     qCDebug(LOG_PLASMA) << " From KPluginMetaData, valid? " << info.isValid();
0064     // WARNING: do not access config() OR globalConfig() in this method!
0065     //          that requires a scene, which is not available at this point
0066     d->init();
0067     d->setupPackage();
0068 }
0069 
0070 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 28)
0071 Applet::Applet(const KPluginInfo &info, QObject *parent, uint appletId)
0072     : Applet(info.toMetaData(), parent, appletId)
0073 {
0074 }
0075 #endif
0076 
0077 Applet::Applet(QObject *parent, const QString &serviceID, uint appletId)
0078     : QObject(parent)
0079     , d(new AppletPrivate(KPluginMetaData(serviceID), appletId, this))
0080 {
0081     // WARNING: do not access config() OR globalConfig() in this method!
0082     //          that requires a scene, which is not available at this point
0083     d->init();
0084     d->setupPackage();
0085 }
0086 
0087 Applet::Applet(QObject *parentObject, const QVariantList &args)
0088     : QObject(nullptr)
0089     , d(new AppletPrivate(KPluginMetaData(), args.count() > 2 ? args[2].toInt() : 0, this))
0090 {
0091     setParent(parentObject);
0092     if (!args.isEmpty()) {
0093         const QVariant first = args.first();
0094         if (first.canConvert<KPackage::Package>()) {
0095             d->package = first.value<KPackage::Package>();
0096         }
0097     }
0098     if (args.count() > 1) {
0099         const QVariant second = args[1];
0100         if (second.canConvert<QString>()) {
0101             d->appletDescription = KPluginMetaData(second.toString());
0102         } else if (second.canConvert<QVariantMap>()) {
0103             auto metadata = second.toMap().value(QStringLiteral("MetaData")).toMap();
0104             d->appletDescription = KPluginMetaData(QJsonObject::fromVariantMap(metadata), {});
0105         }
0106     }
0107     d->icon = d->appletDescription.iconName();
0108 
0109     if (args.contains(QVariant::fromValue(QStringLiteral("org.kde.plasma:force-create")))) {
0110         setProperty("org.kde.plasma:force-create", true);
0111     }
0112 
0113     // WARNING: do not access config() OR globalConfig() in this method!
0114     //          that requires a scene, which is not available at this point
0115     d->init(QString(), args.mid(3));
0116     d->setupPackage();
0117 }
0118 
0119 Applet::Applet(const QString &packagePath, uint appletId)
0120     : QObject(nullptr)
0121     , d(new AppletPrivate(appletMetadataForDirectory(packagePath), appletId, this))
0122 {
0123     d->init(packagePath);
0124     d->setupPackage();
0125 }
0126 #endif
0127 
0128 Applet::Applet(QObject *parentObject, const KPluginMetaData &data, const QVariantList &args)
0129     : QObject(nullptr)
0130     , d(new AppletPrivate(data, args.count() > 2 ? args[2].toInt() : 0, this))
0131 {
0132     setParent(parentObject);
0133     if (!args.isEmpty()) {
0134         const QVariant first = args.first();
0135         if (first.canConvert<KPackage::Package>()) {
0136             d->package = first.value<KPackage::Package>();
0137         }
0138     }
0139     d->icon = d->appletDescription.iconName();
0140 
0141     if (args.contains(QVariant::fromValue(QStringLiteral("org.kde.plasma:force-create")))) {
0142         setProperty("org.kde.plasma:force-create", true);
0143     }
0144 
0145     // WARNING: do not access config() OR globalConfig() in this method!
0146     //          that requires a scene, which is not available at this point
0147     d->init(QString(), args.mid(3));
0148     d->setupPackage();
0149 }
0150 
0151 Applet::~Applet()
0152 {
0153     if (d->transient) {
0154         d->resetConfigurationObject();
0155     }
0156     // let people know that i will die
0157     Q_EMIT appletDeleted(this);
0158 
0159     // ConfigLoader is deleted when AppletPrivate closes not Applet
0160     // It saves on closure and emits a signal.
0161     // disconnect early to avoid a crash. See  411221
0162     if (d->configLoader) {
0163         disconnect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged()));
0164     }
0165     delete d;
0166 }
0167 
0168 void Applet::init()
0169 {
0170     // Don't implement anything here, it will be overridden by subclasses
0171 }
0172 
0173 uint Applet::id() const
0174 {
0175     return d->appletId;
0176 }
0177 
0178 void Applet::save(KConfigGroup &g) const
0179 {
0180     if (d->transient || !d->appletDescription.isValid()) {
0181         return;
0182     }
0183 
0184     KConfigGroup group = g;
0185     if (!group.isValid()) {
0186         group = *d->mainConfigGroup();
0187     }
0188 
0189     // qCDebug(LOG_PLASMA) << "saving" << pluginName() << "to" << group.name();
0190     // we call the dptr member directly for locked since isImmutable()
0191     // also checks kiosk and parent containers
0192     group.writeEntry("immutability", (int)d->immutability);
0193     group.writeEntry("plugin", d->appletDescription.pluginId());
0194 
0195     if (!d->started) {
0196         return;
0197     }
0198 
0199     KConfigGroup appletConfigGroup(&group, "Configuration");
0200     saveState(appletConfigGroup);
0201 
0202     if (d->configLoader) {
0203         // we're saving so we know its changed, we don't need or want the configChanged
0204         // signal bubbling up at this point due to that
0205         disconnect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged()));
0206         d->configLoader->save();
0207         connect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged()));
0208     }
0209 }
0210 
0211 void Applet::restore(KConfigGroup &group)
0212 {
0213     setImmutability((Types::ImmutabilityType)group.readEntry("immutability", (int)Types::Mutable));
0214 
0215     KConfigGroup shortcutConfig(&group, "Shortcuts");
0216     QString shortcutText = shortcutConfig.readEntryUntranslated("global", QString());
0217     if (!shortcutText.isEmpty()) {
0218         setGlobalShortcut(QKeySequence(shortcutText));
0219         /*
0220         #ifndef NDEBUG
0221         // qCDebug(LOG_PLASMA) << "got global shortcut for" << name() << "of" << QKeySequence(shortcutText);
0222         #endif
0223         #ifndef NDEBUG
0224         // qCDebug(LOG_PLASMA) << "set to" << d->activationAction->objectName()
0225         #endif
0226                  << d->activationAction->globalShortcut().primary();
0227                  */
0228     }
0229 
0230     // User background hints
0231     // TODO support flags in the config
0232     QByteArray hintsString = config().readEntry("UserBackgroundHints", QString()).toUtf8();
0233     QMetaEnum hintEnum = QMetaEnum::fromType<Plasma::Types::BackgroundHints>();
0234     bool ok;
0235     int value = hintEnum.keyToValue(hintsString.constData(), &ok);
0236     if (ok) {
0237         d->userBackgroundHints = Plasma::Types::BackgroundHints(value);
0238         d->userBackgroundHintsInitialized = true;
0239         Q_EMIT userBackgroundHintsChanged();
0240         if (d->backgroundHints & Plasma::Types::ConfigurableBackground) {
0241             Q_EMIT effectiveBackgroundHintsChanged();
0242         }
0243     }
0244 }
0245 
0246 void Applet::setLaunchErrorMessage(const QString &message)
0247 {
0248     if (message == d->launchErrorMessage) {
0249         return;
0250     }
0251 
0252     d->failed = true;
0253     d->launchErrorMessage = message;
0254 }
0255 
0256 void Applet::saveState(KConfigGroup &group) const
0257 {
0258     if (d->script) {
0259         Q_EMIT d->script->saveState(group);
0260     }
0261 
0262     if (group.config()->name() != config().config()->name()) {
0263         // we're being saved to a different file!
0264         // let's just copy the current values in our configuration over
0265         KConfigGroup c = config();
0266         c.copyTo(&group);
0267     }
0268 }
0269 
0270 KConfigGroup Applet::config() const
0271 {
0272     if (d->transient) {
0273         return KConfigGroup(KSharedConfig::openConfig(), "PlasmaTransientsConfig");
0274     }
0275 
0276     if (isContainment()) {
0277         return *(d->mainConfigGroup());
0278     }
0279 
0280     return KConfigGroup(d->mainConfigGroup(), "Configuration");
0281 }
0282 
0283 KConfigGroup Applet::globalConfig() const
0284 {
0285     KConfigGroup globalAppletConfig;
0286     QString group = isContainment() ? QStringLiteral("ContainmentGlobals") : QStringLiteral("AppletGlobals");
0287 
0288     Containment *cont = containment();
0289     Corona *corona = nullptr;
0290     if (cont) {
0291         corona = cont->corona();
0292     }
0293     if (corona) {
0294         KSharedConfig::Ptr coronaConfig = corona->config();
0295         globalAppletConfig = KConfigGroup(coronaConfig, group);
0296     } else {
0297         globalAppletConfig = KConfigGroup(KSharedConfig::openConfig(), group);
0298     }
0299 
0300     return KConfigGroup(&globalAppletConfig, d->globalName());
0301 }
0302 
0303 void Applet::destroy()
0304 {
0305     if (immutability() != Types::Mutable || d->transient || !d->started) {
0306         return; // don't double delete
0307     }
0308 
0309     d->setDestroyed(true);
0310     // FIXME: an animation on leave if !isContainment() would be good again .. which should be handled by the containment class
0311     d->cleanUpAndDelete();
0312 }
0313 
0314 bool Applet::destroyed() const
0315 {
0316     return d->transient;
0317 }
0318 
0319 KConfigLoader *Applet::configScheme() const
0320 {
0321     if (!d->configLoader) {
0322         const QString xmlPath = d->package.isValid() ? d->package.filePath("mainconfigxml") : QString();
0323         KConfigGroup cfg = config();
0324         if (xmlPath.isEmpty()) {
0325             d->configLoader = new KConfigLoader(cfg, nullptr);
0326         } else {
0327             QFile file(xmlPath);
0328             d->configLoader = new KConfigLoader(cfg, &file);
0329             QObject::connect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged()));
0330         }
0331     }
0332 
0333     return d->configLoader;
0334 }
0335 
0336 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 6)
0337 Package Applet::package() const
0338 {
0339     Package p;
0340     p.d->internalPackage = new KPackage::Package(d->package);
0341     return p;
0342 }
0343 #endif
0344 
0345 KPackage::Package Applet::kPackage() const
0346 {
0347     return d->package;
0348 }
0349 
0350 void Applet::updateConstraints(Plasma::Types::Constraints constraints)
0351 {
0352     d->scheduleConstraintsUpdate(constraints);
0353 }
0354 
0355 void Applet::constraintsEvent(Plasma::Types::Constraints constraints)
0356 {
0357     // NOTE: do NOT put any code in here that reacts to constraints updates
0358     //      as it will not get called for any applet that reimplements constraintsEvent
0359     //      without calling the Applet:: version as well, which it shouldn't need to.
0360     //      INSTEAD put such code into flushPendingConstraintsEvents
0361     Q_UNUSED(constraints)
0362     // qCDebug(LOG_PLASMA) << constraints << "constraints are FormFactor: " << formFactor()
0363     //         << ", Location: " << location();
0364     if (d->script) {
0365         d->script->constraintsEvent(constraints);
0366     }
0367 }
0368 
0369 QString Applet::title() const
0370 {
0371     if (!d->customTitle.isEmpty()) {
0372         return d->customTitle;
0373     }
0374 
0375     if (d->appletDescription.isValid()) {
0376         return d->appletDescription.name();
0377     }
0378 
0379     return i18n("Unknown");
0380 }
0381 
0382 void Applet::setTitle(const QString &title)
0383 {
0384     if (title == d->customTitle) {
0385         return;
0386     }
0387 
0388     d->customTitle = title;
0389     Q_EMIT titleChanged(title);
0390 }
0391 
0392 QString Applet::icon() const
0393 {
0394     return d->icon;
0395 }
0396 
0397 void Applet::setIcon(const QString &icon)
0398 {
0399     if (icon == d->icon) {
0400         return;
0401     }
0402 
0403     d->icon = icon;
0404     Q_EMIT iconChanged(icon);
0405 }
0406 
0407 bool Applet::isBusy() const
0408 {
0409     return d->busy;
0410 }
0411 
0412 void Applet::setBusy(bool busy)
0413 {
0414     if (busy == d->busy) {
0415         return;
0416     }
0417 
0418     d->busy = busy;
0419     Q_EMIT busyChanged(busy);
0420 }
0421 
0422 Plasma::Types::BackgroundHints Applet::backgroundHints() const
0423 {
0424     return d->backgroundHints;
0425 }
0426 
0427 void Applet::setBackgroundHints(Plasma::Types::BackgroundHints hint)
0428 {
0429     if (d->backgroundHints == hint) {
0430         return;
0431     }
0432 
0433     Plasma::Types::BackgroundHints oldeffectiveHints = effectiveBackgroundHints();
0434 
0435     d->backgroundHints = hint;
0436     Q_EMIT backgroundHintsChanged();
0437 
0438     if (oldeffectiveHints != effectiveBackgroundHints()) {
0439         Q_EMIT effectiveBackgroundHintsChanged();
0440     }
0441 }
0442 
0443 Plasma::Types::BackgroundHints Applet::effectiveBackgroundHints() const
0444 {
0445     if (d->userBackgroundHintsInitialized && (d->backgroundHints & Plasma::Types::ConfigurableBackground)) {
0446         return d->userBackgroundHints;
0447     } else {
0448         return d->backgroundHints;
0449     }
0450 }
0451 
0452 Plasma::Types::BackgroundHints Applet::userBackgroundHints() const
0453 {
0454     return d->userBackgroundHints;
0455 }
0456 
0457 void Applet::setUserBackgroundHints(Plasma::Types::BackgroundHints hint)
0458 {
0459     if (d->userBackgroundHints == hint && d->userBackgroundHintsInitialized) {
0460         return;
0461     }
0462 
0463     d->userBackgroundHints = hint;
0464     d->userBackgroundHintsInitialized = true;
0465     QMetaEnum hintEnum = QMetaEnum::fromType<Plasma::Types::BackgroundHints>();
0466     config().writeEntry("UserBackgroundHints", hintEnum.valueToKey(d->userBackgroundHints));
0467     if (containment() && containment()->corona()) {
0468         containment()->corona()->requestConfigSync();
0469     }
0470 
0471     Q_EMIT userBackgroundHintsChanged();
0472 
0473     if (d->backgroundHints & Plasma::Types::ConfigurableBackground) {
0474         Q_EMIT effectiveBackgroundHintsChanged();
0475     }
0476 }
0477 
0478 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 28)
0479 KPluginInfo Applet::pluginInfo() const
0480 {
0481     return KPluginInfo(d->appletDescription);
0482 }
0483 #endif
0484 
0485 KPluginMetaData Applet::pluginMetaData() const
0486 {
0487     return d->appletDescription;
0488 }
0489 
0490 Types::ImmutabilityType Applet::immutability() const
0491 {
0492     // if this object is itself system immutable, then just return that; it's the most
0493     // restrictive setting possible and will override anything that might be happening above it
0494     // in the Corona->Containment->Applet hierarchy
0495     if (d->transient || (d->mainConfig && d->mainConfig->isImmutable())) {
0496         return Types::SystemImmutable;
0497     }
0498 
0499     // Returning the more strict immutability between the applet immutability, Containment and Corona
0500     Types::ImmutabilityType upperImmutability = Types::Mutable;
0501 
0502     if (isContainment()) {
0503         Corona *cor = static_cast<Containment *>(const_cast<Applet *>(this))->corona();
0504         if (cor) {
0505             upperImmutability = cor->immutability();
0506         }
0507     } else {
0508         const Containment *cont = containment();
0509         if (cont) {
0510             if (cont->corona()) {
0511                 upperImmutability = cont->corona()->immutability();
0512             } else {
0513                 upperImmutability = cont->immutability();
0514             }
0515         }
0516     }
0517 
0518     if (upperImmutability != Types::Mutable) {
0519         // it's either system or user immutable, and we already check for local system immutability,
0520         // so upperImmutability is guaranteed to be as or more severe as this object's immutability
0521         return upperImmutability;
0522     } else {
0523         return d->immutability;
0524     }
0525 }
0526 
0527 void Applet::setImmutability(const Types::ImmutabilityType immutable)
0528 {
0529     if (d->immutability == immutable || immutable == Types::SystemImmutable) {
0530         // we do not store system immutability in d->immutability since that gets saved
0531         // out to the config file; instead, we check with
0532         // the config group itself for this information at all times. this differs from
0533         // corona, where SystemImmutability is stored in d->immutability.
0534         return;
0535     }
0536 
0537     d->immutability = immutable;
0538     updateConstraints(Types::ImmutableConstraint);
0539 }
0540 
0541 QString Applet::launchErrorMessage() const
0542 {
0543     return d->launchErrorMessage;
0544 }
0545 
0546 bool Applet::failedToLaunch() const
0547 {
0548     return d->failed;
0549 }
0550 
0551 bool Applet::configurationRequired() const
0552 {
0553     return d->needsConfig;
0554 }
0555 
0556 QString Applet::configurationRequiredReason() const
0557 {
0558     return d->configurationRequiredReason;
0559 }
0560 
0561 void Applet::setConfigurationRequired(bool needsConfig, const QString &reason)
0562 {
0563     if (d->needsConfig == needsConfig && reason == d->configurationRequiredReason) {
0564         return;
0565     }
0566 
0567     d->needsConfig = needsConfig;
0568     d->configurationRequiredReason = reason;
0569 
0570     Q_EMIT configurationRequiredChanged(needsConfig, reason);
0571 }
0572 
0573 bool Applet::isUserConfiguring() const
0574 {
0575     return d->userConfiguring;
0576 }
0577 
0578 void Applet::setUserConfiguring(bool configuring)
0579 {
0580     if (configuring == d->userConfiguring) {
0581         return;
0582     }
0583 
0584     d->userConfiguring = configuring;
0585     Q_EMIT userConfiguringChanged(configuring);
0586 }
0587 
0588 Types::ItemStatus Applet::status() const
0589 {
0590     return d->itemStatus;
0591 }
0592 
0593 void Applet::setStatus(const Types::ItemStatus status)
0594 {
0595     if (status == d->itemStatus) {
0596         return;
0597     }
0598     d->itemStatus = status;
0599     Q_EMIT statusChanged(status);
0600 }
0601 
0602 void Applet::flushPendingConstraintsEvents()
0603 {
0604     if (d->pendingConstraints == Types::NoConstraint) {
0605         return;
0606     }
0607 
0608     if (d->constraintsTimer.isActive()) {
0609         d->constraintsTimer.stop();
0610     }
0611 
0612     // qCDebug(LOG_PLASMA) << "flushing constraints: " << d->pendingConstraints << "!!!!!!!!!!!!!!!!!!!!!!!!!!!";
0613     Plasma::Types::Constraints c = d->pendingConstraints;
0614     d->pendingConstraints = Types::NoConstraint;
0615 
0616     if (c & Plasma::Types::UiReadyConstraint) {
0617         d->setUiReady();
0618     }
0619 
0620     if (c & Plasma::Types::StartupCompletedConstraint) {
0621         // common actions
0622         bool unlocked = immutability() == Types::Mutable;
0623         QAction *closeApplet = d->actions->action(QStringLiteral("remove"));
0624         if (closeApplet) {
0625             closeApplet->setEnabled(unlocked);
0626             closeApplet->setVisible(unlocked);
0627             connect(closeApplet, SIGNAL(triggered(bool)), this, SLOT(askDestroy()), Qt::UniqueConnection);
0628         }
0629 
0630         QAction *configAction = d->actions->action(QStringLiteral("configure"));
0631         if (configAction) {
0632             if (d->hasConfigurationInterface) {
0633                 bool canConfig = unlocked || KAuthorized::authorize(QStringLiteral("plasma/allow_configure_when_locked"));
0634                 configAction->setVisible(canConfig);
0635                 configAction->setEnabled(canConfig);
0636             }
0637         }
0638 
0639         QAction *runAssociatedApplication = d->actions->action(QStringLiteral("run associated application"));
0640         if (runAssociatedApplication) {
0641             connect(runAssociatedApplication, &QAction::triggered, this, &Applet::runAssociatedApplication, Qt::UniqueConnection);
0642         }
0643 
0644         d->updateShortcuts();
0645     }
0646 
0647     if (c & Plasma::Types::ImmutableConstraint) {
0648         bool unlocked = immutability() == Types::Mutable;
0649         QAction *action = d->actions->action(QStringLiteral("remove"));
0650         if (action) {
0651             action->setVisible(unlocked);
0652             action->setEnabled(unlocked);
0653         }
0654 
0655         action = d->actions->action(QStringLiteral("configure"));
0656         if (action && d->hasConfigurationInterface) {
0657             bool canConfig = unlocked || KAuthorized::authorize(QStringLiteral("plasma/allow_configure_when_locked"));
0658             action->setVisible(canConfig);
0659             action->setEnabled(canConfig);
0660         }
0661 
0662         // an immutable constraint will always happen at startup
0663         // make sure don't emit a change signal for nothing
0664         if (d->oldImmutability != immutability()) {
0665             Q_EMIT immutabilityChanged(immutability());
0666         }
0667         d->oldImmutability = immutability();
0668     }
0669 
0670     // now take care of constraints in special subclass: Containment
0671     Containment *containment = qobject_cast<Plasma::Containment *>(this);
0672     if (containment) {
0673         containment->d->containmentConstraintsEvent(c);
0674     }
0675 
0676     // pass the constraint on to the actual subclass
0677     constraintsEvent(c);
0678 
0679     if (c & Types::StartupCompletedConstraint) {
0680         // start up is done, we can now go do a mod timer
0681         if (d->modificationsTimer) {
0682             if (d->modificationsTimer->isActive()) {
0683                 d->modificationsTimer->stop();
0684             }
0685         } else {
0686             d->modificationsTimer = new QBasicTimer;
0687         }
0688     }
0689 
0690     if (c & Plasma::Types::FormFactorConstraint) {
0691         Q_EMIT formFactorChanged(formFactor());
0692     }
0693 
0694     if (c & Plasma::Types::LocationConstraint) {
0695         Q_EMIT locationChanged(location());
0696     }
0697 }
0698 
0699 QList<QAction *> Applet::contextualActions()
0700 {
0701     // qCDebug(LOG_PLASMA) << "empty context actions";
0702     return d->script ? d->script->contextualActions() : QList<QAction *>();
0703 }
0704 
0705 KActionCollection *Applet::actions() const
0706 {
0707     return d->actions;
0708 }
0709 
0710 Types::FormFactor Applet::formFactor() const
0711 {
0712     Containment *c = containment();
0713     QObject *pw = qobject_cast<QObject *>(parent());
0714     Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(pw);
0715     // assumption: this loop is usually is -really- short or doesn't run at all
0716     while (!parentApplet && pw && pw->parent()) {
0717         pw = pw->parent();
0718         parentApplet = qobject_cast<Plasma::Applet *>(pw);
0719     }
0720 
0721     return c ? c->d->formFactor : Plasma::Types::Planar;
0722 }
0723 
0724 Types::ContainmentDisplayHints Applet::containmentDisplayHints() const
0725 {
0726     Containment *c = containment();
0727 
0728     return c ? c->d->containmentDisplayHints : Plasma::Types::NoContainmentDisplayHint;
0729 }
0730 
0731 Containment *Applet::containment() const
0732 {
0733     Containment *c = qobject_cast<Containment *>(const_cast<Applet *>(this));
0734     if (c && c->isContainment()) {
0735         return c;
0736     } else {
0737         c = nullptr;
0738     }
0739 
0740     QObject *parent = this->parent();
0741 
0742     while (parent) {
0743         Containment *possibleC = qobject_cast<Containment *>(parent);
0744 
0745         if (possibleC && possibleC->isContainment()) {
0746             c = possibleC;
0747             break;
0748         }
0749         parent = parent->parent();
0750     }
0751 
0752     return c;
0753 }
0754 
0755 void Applet::setGlobalShortcut(const QKeySequence &shortcut)
0756 {
0757     if (!d->activationAction) {
0758         d->activationAction = new QAction(this);
0759         d->activationAction->setText(i18n("Activate %1 Widget", title()));
0760         d->activationAction->setObjectName(QStringLiteral("activate widget %1").arg(id())); // NO I18N
0761         connect(d->activationAction, &QAction::triggered, this, &Applet::activated);
0762         connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutChanged, this, [this](QAction *action, const QKeySequence &shortcut) {
0763             if (action == d->activationAction) {
0764                 d->activationAction->setShortcut(shortcut);
0765                 d->globalShortcutChanged();
0766             }
0767         });
0768     } else if (d->activationAction->shortcut() == shortcut) {
0769         return;
0770     }
0771 
0772     d->activationAction->setShortcut(shortcut);
0773     d->globalShortcutEnabled = true;
0774     QList<QKeySequence> seqs{shortcut};
0775     KGlobalAccel::self()->setShortcut(d->activationAction, seqs, KGlobalAccel::NoAutoloading);
0776     d->globalShortcutChanged();
0777 }
0778 
0779 QKeySequence Applet::globalShortcut() const
0780 {
0781     if (d->activationAction) {
0782         QList<QKeySequence> shortcuts = KGlobalAccel::self()->shortcut(d->activationAction);
0783         if (!shortcuts.isEmpty()) {
0784             return shortcuts.first();
0785         }
0786     }
0787 
0788     return QKeySequence();
0789 }
0790 
0791 Types::Location Applet::location() const
0792 {
0793     Containment *c = containment();
0794     return c ? c->d->location : Plasma::Types::Desktop;
0795 }
0796 
0797 bool Applet::hasConfigurationInterface() const
0798 {
0799     return d->hasConfigurationInterface;
0800 }
0801 
0802 void Applet::setHasConfigurationInterface(bool hasInterface)
0803 {
0804     if (hasInterface == d->hasConfigurationInterface) {
0805         return;
0806     }
0807 
0808     QAction *configAction = d->actions->action(QStringLiteral("configure"));
0809     if (configAction) {
0810         bool enable = hasInterface;
0811         if (enable) {
0812             const bool unlocked = immutability() == Types::Mutable;
0813             enable = unlocked || KAuthorized::authorize(QStringLiteral("plasma/allow_configure_when_locked"));
0814         }
0815         configAction->setEnabled(enable);
0816     }
0817 
0818     d->hasConfigurationInterface = hasInterface;
0819 }
0820 
0821 void Applet::configChanged()
0822 {
0823     if (d->script) {
0824         if (d->configLoader) {
0825             d->configLoader->load();
0826         }
0827         d->script->configChanged();
0828     }
0829 }
0830 
0831 void Applet::setAssociatedApplication(const QString &string)
0832 {
0833     AssociatedApplicationManager::self()->setApplication(this, string);
0834 
0835     QAction *runAssociatedApplication = d->actions->action(QStringLiteral("run associated application"));
0836     if (runAssociatedApplication) {
0837         bool valid = AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this);
0838         runAssociatedApplication->setVisible(valid);
0839         runAssociatedApplication->setEnabled(valid);
0840     }
0841 }
0842 
0843 void Applet::setAssociatedApplicationUrls(const QList<QUrl> &urls)
0844 {
0845     AssociatedApplicationManager::self()->setUrls(this, urls);
0846 
0847     QAction *runAssociatedApplication = d->actions->action(QStringLiteral("run associated application"));
0848     if (runAssociatedApplication) {
0849         bool valid = AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this);
0850         runAssociatedApplication->setVisible(valid);
0851         runAssociatedApplication->setEnabled(valid);
0852     }
0853 }
0854 
0855 QString Applet::associatedApplication() const
0856 {
0857     return AssociatedApplicationManager::self()->application(this);
0858 }
0859 
0860 QList<QUrl> Applet::associatedApplicationUrls() const
0861 {
0862     return AssociatedApplicationManager::self()->urls(this);
0863 }
0864 
0865 void Applet::runAssociatedApplication()
0866 {
0867     AssociatedApplicationManager::self()->run(this);
0868 }
0869 
0870 bool Applet::hasValidAssociatedApplication() const
0871 {
0872     return AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this);
0873 }
0874 
0875 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 19)
0876 Applet *Applet::loadPlasmoid(const QString &path, uint appletId)
0877 {
0878     const KPluginMetaData md = appletMetadataForDirectory(path);
0879     if (md.isValid()) {
0880         QStringList types = md.serviceTypes();
0881 
0882         if (types.contains(QLatin1String("Plasma/Containment")) || md.rawData().contains(QStringLiteral("X-Plasma-ContainmentType"))) {
0883             return new Containment(md, appletId);
0884         } else {
0885             return new Applet(md, nullptr, appletId);
0886         }
0887     }
0888 
0889     return nullptr;
0890 }
0891 #endif
0892 
0893 QString Applet::filePath(const QByteArray &key, const QString &filename) const
0894 {
0895     if (d->package.isValid()) {
0896         return d->package.filePath(key, filename);
0897     }
0898     return QString();
0899 }
0900 
0901 void Applet::timerEvent(QTimerEvent *event)
0902 {
0903     if (d->transient) {
0904         d->constraintsTimer.stop();
0905         if (d->modificationsTimer) {
0906             d->modificationsTimer->stop();
0907         }
0908         return;
0909     }
0910 
0911     if (event->timerId() == d->constraintsTimer.timerId()) {
0912         d->constraintsTimer.stop();
0913 
0914         // Don't flushPendingConstraints if we're just starting up
0915         // flushPendingConstraints will be called by Corona
0916         if (!(d->pendingConstraints & Plasma::Types::StartupCompletedConstraint)) {
0917             flushPendingConstraintsEvents();
0918         }
0919     } else if (d->modificationsTimer && event->timerId() == d->modificationsTimer->timerId()) {
0920         d->modificationsTimer->stop();
0921         // invalid group, will result in save using the default group
0922         KConfigGroup cg;
0923 
0924         save(cg);
0925         Q_EMIT configNeedsSaving();
0926     }
0927 }
0928 
0929 bool Applet::isContainment() const
0930 {
0931     // HACK: this is a special case for the systray
0932     // containment in an applet that is not a containment
0933     Applet *pa = qobject_cast<Applet *>(parent());
0934     if (pa && !pa->isContainment()) {
0935         return true;
0936     }
0937     // normal "acting as a containment" condition
0938     return qobject_cast<const Containment *>(this) && qobject_cast<Corona *>(parent());
0939 }
0940 
0941 } // Plasma namespace
0942 
0943 #include "moc_applet.cpp"