3e006a0c42
The intent behind this code seemingly was that KGlobalAccel would update the shortcut of the QAction but that is not the case. This leads to our config going out of sync when global shortcuts are changed from the oustide (the kcm) and resetting on next start since we set shortcuts with NoAutoLoading. BUG:438662 FIXED-IN:5.84
922 lines
27 KiB
C++
922 lines
27 KiB
C++
/*
|
|
SPDX-FileCopyrightText: 2005 Aaron Seigo <aseigo@kde.org>
|
|
SPDX-FileCopyrightText: 2007 Riccardo Iaconelli <riccardo@kde.org>
|
|
SPDX-FileCopyrightText: 2008 Ménard Alexis <darktears31@gmail.com>
|
|
SPDX-FileCopyrightText: 2009 Chani Armitage <chani@kde.org>
|
|
|
|
SPDX-License-Identifier: LGPL-2.0-or-later
|
|
*/
|
|
|
|
#include "applet.h"
|
|
#include "private/applet_p.h"
|
|
|
|
#include "config-plasma.h"
|
|
|
|
#include <QAbstractButton>
|
|
#include <QDebug>
|
|
#include <QFile>
|
|
#include <QList>
|
|
#include <QMessageBox>
|
|
#include <QMetaEnum>
|
|
|
|
#include <KActionCollection>
|
|
#include <KAuthorized>
|
|
#include <KColorScheme>
|
|
#include <KConfigLoader>
|
|
#include <KDesktopFile>
|
|
#include <KGlobalAccel>
|
|
#include <KLocalizedString>
|
|
#include <KService>
|
|
#include <KWindowSystem>
|
|
|
|
#include "containment.h"
|
|
#include "corona.h"
|
|
#include "package.h"
|
|
#include "plasma.h"
|
|
#include "pluginloader.h"
|
|
#include "scripting/appletscript.h"
|
|
|
|
#include "debug_p.h"
|
|
#include "private/associatedapplicationmanager_p.h"
|
|
#include "private/containment_p.h"
|
|
#if PLASMA_BUILD_DEPRECATED_SINCE(5, 83)
|
|
#include "private/package_p.h"
|
|
#endif
|
|
|
|
#include <cmath>
|
|
#include <limits>
|
|
|
|
namespace Plasma
|
|
{
|
|
static KPluginMetaData appletMetadataForDirectory(const QString &path)
|
|
{
|
|
return QFile::exists(path + QLatin1String("/metadata.json"))
|
|
? KPluginMetaData(path + QLatin1String("/metadata.json"))
|
|
: KPluginMetaData::fromDesktopFile(path + QLatin1String("/metadata.desktop"), {QStringLiteral("plasma-applet.desktop")});
|
|
}
|
|
|
|
Applet::Applet(const KPluginMetaData &info, QObject *parent, uint appletId)
|
|
: QObject(parent)
|
|
, d(new AppletPrivate(info, appletId, this))
|
|
{
|
|
qCDebug(LOG_PLASMA) << " From KPluginMetaData, valid? " << info.isValid();
|
|
// WARNING: do not access config() OR globalConfig() in this method!
|
|
// that requires a scene, which is not available at this point
|
|
d->init();
|
|
d->setupPackage();
|
|
}
|
|
|
|
#if PLASMA_BUILD_DEPRECATED_SINCE(5, 28)
|
|
Applet::Applet(const KPluginInfo &info, QObject *parent, uint appletId)
|
|
: Applet(info.toMetaData(), parent, appletId)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
Applet::Applet(QObject *parent, const QString &serviceID, uint appletId)
|
|
: QObject(parent)
|
|
, d(new AppletPrivate(KPluginMetaData(serviceID), appletId, this))
|
|
{
|
|
// WARNING: do not access config() OR globalConfig() in this method!
|
|
// that requires a scene, which is not available at this point
|
|
d->init();
|
|
d->setupPackage();
|
|
}
|
|
|
|
Applet::Applet(QObject *parentObject, const QVariantList &args)
|
|
: QObject(nullptr)
|
|
, d(new AppletPrivate(KPluginMetaData(), args.count() > 2 ? args[2].toInt() : 0, this))
|
|
{
|
|
setParent(parentObject);
|
|
if (!args.isEmpty()) {
|
|
const QVariant first = args.first();
|
|
if (first.canConvert<KPackage::Package>()) {
|
|
d->package = first.value<KPackage::Package>();
|
|
}
|
|
}
|
|
if (args.count() > 1) {
|
|
const QVariant second = args[1];
|
|
if (second.canConvert<QString>()) {
|
|
d->appletDescription = KPluginMetaData(second.toString());
|
|
} else if (second.canConvert<QVariantMap>()) {
|
|
auto metadata = second.toMap().value(QStringLiteral("MetaData")).toMap();
|
|
d->appletDescription = KPluginMetaData(QJsonObject::fromVariantMap(metadata), {});
|
|
}
|
|
}
|
|
d->icon = d->appletDescription.iconName();
|
|
|
|
if (args.contains(QVariant::fromValue(QStringLiteral("org.kde.plasma:force-create")))) {
|
|
setProperty("org.kde.plasma:force-create", true);
|
|
}
|
|
|
|
// WARNING: do not access config() OR globalConfig() in this method!
|
|
// that requires a scene, which is not available at this point
|
|
d->init(QString(), args.mid(3));
|
|
d->setupPackage();
|
|
}
|
|
|
|
Applet::Applet(const QString &packagePath, uint appletId)
|
|
: QObject(nullptr)
|
|
, d(new AppletPrivate(appletMetadataForDirectory(packagePath), appletId, this))
|
|
{
|
|
d->init(packagePath);
|
|
d->setupPackage();
|
|
}
|
|
|
|
Applet::~Applet()
|
|
{
|
|
if (d->transient) {
|
|
d->resetConfigurationObject();
|
|
}
|
|
// let people know that i will die
|
|
Q_EMIT appletDeleted(this);
|
|
|
|
// ConfigLoader is deleted when AppletPrivate closes not Applet
|
|
// It saves on closure and emits a signal.
|
|
// disconnect early to avoid a crash. See 411221
|
|
disconnect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged()));
|
|
delete d;
|
|
}
|
|
|
|
void Applet::init()
|
|
{
|
|
// Don't implement anything here, it will be overridden by subclasses
|
|
}
|
|
|
|
uint Applet::id() const
|
|
{
|
|
return d->appletId;
|
|
}
|
|
|
|
void Applet::save(KConfigGroup &g) const
|
|
{
|
|
if (d->transient || !d->appletDescription.isValid()) {
|
|
return;
|
|
}
|
|
|
|
KConfigGroup group = g;
|
|
if (!group.isValid()) {
|
|
group = *d->mainConfigGroup();
|
|
}
|
|
|
|
// qCDebug(LOG_PLASMA) << "saving" << pluginName() << "to" << group.name();
|
|
// we call the dptr member directly for locked since isImmutable()
|
|
// also checks kiosk and parent containers
|
|
group.writeEntry("immutability", (int)d->immutability);
|
|
group.writeEntry("plugin", d->appletDescription.pluginId());
|
|
|
|
if (!d->started) {
|
|
return;
|
|
}
|
|
|
|
KConfigGroup appletConfigGroup(&group, "Configuration");
|
|
saveState(appletConfigGroup);
|
|
|
|
if (d->configLoader) {
|
|
// we're saving so we know its changed, we don't need or want the configChanged
|
|
// signal bubbling up at this point due to that
|
|
disconnect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged()));
|
|
d->configLoader->save();
|
|
connect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged()));
|
|
}
|
|
}
|
|
|
|
void Applet::restore(KConfigGroup &group)
|
|
{
|
|
setImmutability((Types::ImmutabilityType)group.readEntry("immutability", (int)Types::Mutable));
|
|
|
|
KConfigGroup shortcutConfig(&group, "Shortcuts");
|
|
QString shortcutText = shortcutConfig.readEntryUntranslated("global", QString());
|
|
if (!shortcutText.isEmpty()) {
|
|
setGlobalShortcut(QKeySequence(shortcutText));
|
|
/*
|
|
#ifndef NDEBUG
|
|
// qCDebug(LOG_PLASMA) << "got global shortcut for" << name() << "of" << QKeySequence(shortcutText);
|
|
#endif
|
|
#ifndef NDEBUG
|
|
// qCDebug(LOG_PLASMA) << "set to" << d->activationAction->objectName()
|
|
#endif
|
|
<< d->activationAction->globalShortcut().primary();
|
|
*/
|
|
}
|
|
|
|
// local shortcut, if any
|
|
// TODO: implement; the shortcut will need to be registered with the containment
|
|
/*
|
|
#include "accessmanager.h"
|
|
#include "authorizationmanager.h"
|
|
#include "private/plasmoidservice_p.h"
|
|
shortcutText = shortcutConfig.readEntryUntranslated("local", QString());
|
|
if (!shortcutText.isEmpty()) {
|
|
//TODO: implement; the shortcut
|
|
}
|
|
*/
|
|
|
|
// User background hints
|
|
// TODO support flags in the config
|
|
QByteArray hintsString = config().readEntry("UserBackgroundHints", QString()).toUtf8();
|
|
QMetaEnum hintEnum = QMetaEnum::fromType<Plasma::Types::BackgroundHints>();
|
|
bool ok;
|
|
int value = hintEnum.keyToValue(hintsString.constData(), &ok);
|
|
if (ok) {
|
|
d->userBackgroundHints = Plasma::Types::BackgroundHints(value);
|
|
d->userBackgroundHintsInitialized = true;
|
|
Q_EMIT userBackgroundHintsChanged();
|
|
if (d->backgroundHints & Plasma::Types::ConfigurableBackground) {
|
|
Q_EMIT effectiveBackgroundHintsChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Applet::setLaunchErrorMessage(const QString &message)
|
|
{
|
|
if (message == d->launchErrorMessage) {
|
|
return;
|
|
}
|
|
|
|
d->failed = true;
|
|
d->launchErrorMessage = message;
|
|
}
|
|
|
|
void Applet::saveState(KConfigGroup &group) const
|
|
{
|
|
if (d->script) {
|
|
Q_EMIT d->script->saveState(group);
|
|
}
|
|
|
|
if (group.config()->name() != config().config()->name()) {
|
|
// we're being saved to a different file!
|
|
// let's just copy the current values in our configuration over
|
|
KConfigGroup c = config();
|
|
c.copyTo(&group);
|
|
}
|
|
}
|
|
|
|
KConfigGroup Applet::config() const
|
|
{
|
|
if (d->transient) {
|
|
return KConfigGroup(KSharedConfig::openConfig(), "PlasmaTransientsConfig");
|
|
}
|
|
|
|
if (isContainment()) {
|
|
return *(d->mainConfigGroup());
|
|
}
|
|
|
|
return KConfigGroup(d->mainConfigGroup(), "Configuration");
|
|
}
|
|
|
|
KConfigGroup Applet::globalConfig() const
|
|
{
|
|
KConfigGroup globalAppletConfig;
|
|
QString group = isContainment() ? QStringLiteral("ContainmentGlobals") : QStringLiteral("AppletGlobals");
|
|
|
|
Containment *cont = containment();
|
|
Corona *corona = nullptr;
|
|
if (cont) {
|
|
corona = cont->corona();
|
|
}
|
|
if (corona) {
|
|
KSharedConfig::Ptr coronaConfig = corona->config();
|
|
globalAppletConfig = KConfigGroup(coronaConfig, group);
|
|
} else {
|
|
globalAppletConfig = KConfigGroup(KSharedConfig::openConfig(), group);
|
|
}
|
|
|
|
return KConfigGroup(&globalAppletConfig, d->globalName());
|
|
}
|
|
|
|
void Applet::destroy()
|
|
{
|
|
if (immutability() != Types::Mutable || d->transient || !d->started) {
|
|
return; // don't double delete
|
|
}
|
|
|
|
d->setDestroyed(true);
|
|
// FIXME: an animation on leave if !isContainment() would be good again .. which should be handled by the containment class
|
|
d->cleanUpAndDelete();
|
|
}
|
|
|
|
bool Applet::destroyed() const
|
|
{
|
|
return d->transient;
|
|
}
|
|
|
|
KConfigLoader *Applet::configScheme() const
|
|
{
|
|
if (!d->configLoader) {
|
|
const QString xmlPath = d->package.isValid() ? d->package.filePath("mainconfigxml") : QString();
|
|
KConfigGroup cfg = config();
|
|
if (xmlPath.isEmpty()) {
|
|
d->configLoader = new KConfigLoader(cfg, nullptr);
|
|
} else {
|
|
QFile file(xmlPath);
|
|
d->configLoader = new KConfigLoader(cfg, &file);
|
|
QObject::connect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged()));
|
|
}
|
|
}
|
|
|
|
return d->configLoader;
|
|
}
|
|
|
|
#if PLASMA_BUILD_DEPRECATED_SINCE(5, 6)
|
|
Package Applet::package() const
|
|
{
|
|
Package p;
|
|
p.d->internalPackage = new KPackage::Package(d->package);
|
|
return p;
|
|
}
|
|
#endif
|
|
|
|
KPackage::Package Applet::kPackage() const
|
|
{
|
|
return d->package;
|
|
}
|
|
|
|
void Applet::updateConstraints(Plasma::Types::Constraints constraints)
|
|
{
|
|
d->scheduleConstraintsUpdate(constraints);
|
|
}
|
|
|
|
void Applet::constraintsEvent(Plasma::Types::Constraints constraints)
|
|
{
|
|
// NOTE: do NOT put any code in here that reacts to constraints updates
|
|
// as it will not get called for any applet that reimplements constraintsEvent
|
|
// without calling the Applet:: version as well, which it shouldn't need to.
|
|
// INSTEAD put such code into flushPendingConstraintsEvents
|
|
Q_UNUSED(constraints)
|
|
// qCDebug(LOG_PLASMA) << constraints << "constraints are FormFactor: " << formFactor()
|
|
// << ", Location: " << location();
|
|
if (d->script) {
|
|
d->script->constraintsEvent(constraints);
|
|
}
|
|
}
|
|
|
|
QString Applet::title() const
|
|
{
|
|
if (!d->customTitle.isEmpty()) {
|
|
return d->customTitle;
|
|
}
|
|
|
|
if (d->appletDescription.isValid()) {
|
|
return d->appletDescription.name();
|
|
}
|
|
|
|
return i18n("Unknown");
|
|
}
|
|
|
|
void Applet::setTitle(const QString &title)
|
|
{
|
|
if (title == d->customTitle) {
|
|
return;
|
|
}
|
|
|
|
d->customTitle = title;
|
|
Q_EMIT titleChanged(title);
|
|
}
|
|
|
|
QString Applet::icon() const
|
|
{
|
|
return d->icon;
|
|
}
|
|
|
|
void Applet::setIcon(const QString &icon)
|
|
{
|
|
if (icon == d->icon) {
|
|
return;
|
|
}
|
|
|
|
d->icon = icon;
|
|
Q_EMIT iconChanged(icon);
|
|
}
|
|
|
|
bool Applet::isBusy() const
|
|
{
|
|
return d->busy;
|
|
}
|
|
|
|
void Applet::setBusy(bool busy)
|
|
{
|
|
if (busy == d->busy) {
|
|
return;
|
|
}
|
|
|
|
d->busy = busy;
|
|
Q_EMIT busyChanged(busy);
|
|
}
|
|
|
|
Plasma::Types::BackgroundHints Applet::backgroundHints() const
|
|
{
|
|
return d->backgroundHints;
|
|
}
|
|
|
|
void Applet::setBackgroundHints(Plasma::Types::BackgroundHints hint)
|
|
{
|
|
if (d->backgroundHints == hint) {
|
|
return;
|
|
}
|
|
|
|
Plasma::Types::BackgroundHints oldeffectiveHints = effectiveBackgroundHints();
|
|
|
|
d->backgroundHints = hint;
|
|
Q_EMIT backgroundHintsChanged();
|
|
|
|
if (oldeffectiveHints != effectiveBackgroundHints()) {
|
|
Q_EMIT effectiveBackgroundHintsChanged();
|
|
}
|
|
}
|
|
|
|
Plasma::Types::BackgroundHints Applet::effectiveBackgroundHints() const
|
|
{
|
|
if (d->userBackgroundHintsInitialized && (d->backgroundHints & Plasma::Types::ConfigurableBackground)) {
|
|
return d->userBackgroundHints;
|
|
} else {
|
|
return d->backgroundHints;
|
|
}
|
|
}
|
|
|
|
Plasma::Types::BackgroundHints Applet::userBackgroundHints() const
|
|
{
|
|
return d->userBackgroundHints;
|
|
}
|
|
|
|
void Applet::setUserBackgroundHints(Plasma::Types::BackgroundHints hint)
|
|
{
|
|
if (d->userBackgroundHints == hint && d->userBackgroundHintsInitialized) {
|
|
return;
|
|
}
|
|
|
|
d->userBackgroundHints = hint;
|
|
d->userBackgroundHintsInitialized = true;
|
|
QMetaEnum hintEnum = QMetaEnum::fromType<Plasma::Types::BackgroundHints>();
|
|
config().writeEntry("UserBackgroundHints", hintEnum.valueToKey(d->userBackgroundHints));
|
|
if (containment() && containment()->corona()) {
|
|
containment()->corona()->requestConfigSync();
|
|
}
|
|
|
|
Q_EMIT userBackgroundHintsChanged();
|
|
|
|
if (d->backgroundHints & Plasma::Types::ConfigurableBackground) {
|
|
Q_EMIT effectiveBackgroundHintsChanged();
|
|
}
|
|
}
|
|
|
|
#if PLASMA_BUILD_DEPRECATED_SINCE(5, 28)
|
|
KPluginInfo Applet::pluginInfo() const
|
|
{
|
|
return KPluginInfo(d->appletDescription);
|
|
}
|
|
#endif
|
|
|
|
KPluginMetaData Applet::pluginMetaData() const
|
|
{
|
|
return d->appletDescription;
|
|
}
|
|
|
|
Types::ImmutabilityType Applet::immutability() const
|
|
{
|
|
// if this object is itself system immutable, then just return that; it's the most
|
|
// restrictive setting possible and will override anything that might be happening above it
|
|
// in the Corona->Containment->Applet hierarchy
|
|
if (d->transient || (d->mainConfig && d->mainConfig->isImmutable())) {
|
|
return Types::SystemImmutable;
|
|
}
|
|
|
|
// Returning the more strict immutability between the applet immutability, Containment and Corona
|
|
Types::ImmutabilityType upperImmutability = Types::Mutable;
|
|
|
|
if (isContainment()) {
|
|
Corona *cor = static_cast<Containment *>(const_cast<Applet *>(this))->corona();
|
|
if (cor) {
|
|
upperImmutability = cor->immutability();
|
|
}
|
|
} else {
|
|
const Containment *cont = containment();
|
|
if (cont) {
|
|
if (cont->corona()) {
|
|
upperImmutability = cont->corona()->immutability();
|
|
} else {
|
|
upperImmutability = cont->immutability();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (upperImmutability != Types::Mutable) {
|
|
// it's either system or user immutable, and we already check for local system immutability,
|
|
// so upperImmutability is guaranteed to be as or more severe as this object's immutability
|
|
return upperImmutability;
|
|
} else {
|
|
return d->immutability;
|
|
}
|
|
}
|
|
|
|
void Applet::setImmutability(const Types::ImmutabilityType immutable)
|
|
{
|
|
if (d->immutability == immutable || immutable == Types::SystemImmutable) {
|
|
// we do not store system immutability in d->immutability since that gets saved
|
|
// out to the config file; instead, we check with
|
|
// the config group itself for this information at all times. this differs from
|
|
// corona, where SystemImmutability is stored in d->immutability.
|
|
return;
|
|
}
|
|
|
|
d->immutability = immutable;
|
|
updateConstraints(Types::ImmutableConstraint);
|
|
}
|
|
|
|
QString Applet::launchErrorMessage() const
|
|
{
|
|
return d->launchErrorMessage;
|
|
}
|
|
|
|
bool Applet::failedToLaunch() const
|
|
{
|
|
return d->failed;
|
|
}
|
|
|
|
bool Applet::configurationRequired() const
|
|
{
|
|
return d->needsConfig;
|
|
}
|
|
|
|
QString Applet::configurationRequiredReason() const
|
|
{
|
|
return d->configurationRequiredReason;
|
|
}
|
|
|
|
void Applet::setConfigurationRequired(bool needsConfig, const QString &reason)
|
|
{
|
|
if (d->needsConfig == needsConfig && reason == d->configurationRequiredReason) {
|
|
return;
|
|
}
|
|
|
|
d->needsConfig = needsConfig;
|
|
d->configurationRequiredReason = reason;
|
|
|
|
Q_EMIT configurationRequiredChanged(needsConfig, reason);
|
|
}
|
|
|
|
bool Applet::isUserConfiguring() const
|
|
{
|
|
return d->userConfiguring;
|
|
}
|
|
|
|
void Applet::setUserConfiguring(bool configuring)
|
|
{
|
|
if (configuring == d->userConfiguring) {
|
|
return;
|
|
}
|
|
|
|
d->userConfiguring = configuring;
|
|
Q_EMIT userConfiguringChanged(configuring);
|
|
}
|
|
|
|
Types::ItemStatus Applet::status() const
|
|
{
|
|
return d->itemStatus;
|
|
}
|
|
|
|
void Applet::setStatus(const Types::ItemStatus status)
|
|
{
|
|
if (status == d->itemStatus) {
|
|
return;
|
|
}
|
|
d->itemStatus = status;
|
|
Q_EMIT statusChanged(status);
|
|
}
|
|
|
|
void Applet::flushPendingConstraintsEvents()
|
|
{
|
|
if (d->pendingConstraints == Types::NoConstraint) {
|
|
return;
|
|
}
|
|
|
|
if (d->constraintsTimer.isActive()) {
|
|
d->constraintsTimer.stop();
|
|
}
|
|
|
|
// qCDebug(LOG_PLASMA) << "flushing constraints: " << d->pendingConstraints << "!!!!!!!!!!!!!!!!!!!!!!!!!!!";
|
|
Plasma::Types::Constraints c = d->pendingConstraints;
|
|
d->pendingConstraints = Types::NoConstraint;
|
|
|
|
if (c & Plasma::Types::UiReadyConstraint) {
|
|
d->setUiReady();
|
|
}
|
|
|
|
if (c & Plasma::Types::StartupCompletedConstraint) {
|
|
// common actions
|
|
bool unlocked = immutability() == Types::Mutable;
|
|
QAction *closeApplet = d->actions->action(QStringLiteral("remove"));
|
|
if (closeApplet) {
|
|
closeApplet->setEnabled(unlocked);
|
|
closeApplet->setVisible(unlocked);
|
|
connect(closeApplet, SIGNAL(triggered(bool)), this, SLOT(askDestroy()), Qt::UniqueConnection);
|
|
}
|
|
|
|
QAction *configAction = d->actions->action(QStringLiteral("configure"));
|
|
if (configAction) {
|
|
if (d->hasConfigurationInterface) {
|
|
bool canConfig = unlocked || KAuthorized::authorize(QStringLiteral("plasma/allow_configure_when_locked"));
|
|
configAction->setVisible(canConfig);
|
|
configAction->setEnabled(canConfig);
|
|
}
|
|
}
|
|
|
|
QAction *runAssociatedApplication = d->actions->action(QStringLiteral("run associated application"));
|
|
if (runAssociatedApplication) {
|
|
connect(runAssociatedApplication, &QAction::triggered, this, &Applet::runAssociatedApplication, Qt::UniqueConnection);
|
|
}
|
|
|
|
d->updateShortcuts();
|
|
}
|
|
|
|
if (c & Plasma::Types::ImmutableConstraint) {
|
|
bool unlocked = immutability() == Types::Mutable;
|
|
QAction *action = d->actions->action(QStringLiteral("remove"));
|
|
if (action) {
|
|
action->setVisible(unlocked);
|
|
action->setEnabled(unlocked);
|
|
}
|
|
|
|
action = d->actions->action(QStringLiteral("configure"));
|
|
if (action && d->hasConfigurationInterface) {
|
|
bool canConfig = unlocked || KAuthorized::authorize(QStringLiteral("plasma/allow_configure_when_locked"));
|
|
action->setVisible(canConfig);
|
|
action->setEnabled(canConfig);
|
|
}
|
|
|
|
// an immutable constraint will always happen at startup
|
|
// make sure don't emit a change signal for nothing
|
|
if (d->oldImmutability != immutability()) {
|
|
Q_EMIT immutabilityChanged(immutability());
|
|
}
|
|
d->oldImmutability = immutability();
|
|
}
|
|
|
|
// now take care of constraints in special subclass: Containment
|
|
Containment *containment = qobject_cast<Plasma::Containment *>(this);
|
|
if (containment) {
|
|
containment->d->containmentConstraintsEvent(c);
|
|
}
|
|
|
|
// pass the constraint on to the actual subclass
|
|
constraintsEvent(c);
|
|
|
|
if (c & Types::StartupCompletedConstraint) {
|
|
// start up is done, we can now go do a mod timer
|
|
if (d->modificationsTimer) {
|
|
if (d->modificationsTimer->isActive()) {
|
|
d->modificationsTimer->stop();
|
|
}
|
|
} else {
|
|
d->modificationsTimer = new QBasicTimer;
|
|
}
|
|
}
|
|
|
|
if (c & Plasma::Types::FormFactorConstraint) {
|
|
Q_EMIT formFactorChanged(formFactor());
|
|
}
|
|
|
|
if (c & Plasma::Types::LocationConstraint) {
|
|
Q_EMIT locationChanged(location());
|
|
}
|
|
}
|
|
|
|
QList<QAction *> Applet::contextualActions()
|
|
{
|
|
// qCDebug(LOG_PLASMA) << "empty context actions";
|
|
return d->script ? d->script->contextualActions() : QList<QAction *>();
|
|
}
|
|
|
|
KActionCollection *Applet::actions() const
|
|
{
|
|
return d->actions;
|
|
}
|
|
|
|
Types::FormFactor Applet::formFactor() const
|
|
{
|
|
Containment *c = containment();
|
|
QObject *pw = qobject_cast<QObject *>(parent());
|
|
Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(pw);
|
|
// assumption: this loop is usually is -really- short or doesn't run at all
|
|
while (!parentApplet && pw && pw->parent()) {
|
|
pw = pw->parent();
|
|
parentApplet = qobject_cast<Plasma::Applet *>(pw);
|
|
}
|
|
|
|
return c ? c->d->formFactor : Plasma::Types::Planar;
|
|
}
|
|
|
|
Types::ContainmentDisplayHints Applet::containmentDisplayHints() const
|
|
{
|
|
Containment *c = containment();
|
|
|
|
return c ? c->d->containmentDisplayHints : Plasma::Types::NoContainmentDisplayHint;
|
|
}
|
|
|
|
Containment *Applet::containment() const
|
|
{
|
|
Containment *c = qobject_cast<Containment *>(const_cast<Applet *>(this));
|
|
if (c && c->isContainment()) {
|
|
return c;
|
|
} else {
|
|
c = nullptr;
|
|
}
|
|
|
|
QObject *parent = this->parent();
|
|
|
|
while (parent) {
|
|
Containment *possibleC = qobject_cast<Containment *>(parent);
|
|
|
|
if (possibleC && possibleC->isContainment()) {
|
|
c = possibleC;
|
|
break;
|
|
}
|
|
parent = parent->parent();
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
void Applet::setGlobalShortcut(const QKeySequence &shortcut)
|
|
{
|
|
if (!d->activationAction) {
|
|
d->activationAction = new QAction(this);
|
|
d->activationAction->setText(i18n("Activate %1 Widget", title()));
|
|
d->activationAction->setObjectName(QStringLiteral("activate widget %1").arg(id())); // NO I18N
|
|
connect(d->activationAction, &QAction::triggered, this, &Applet::activated);
|
|
connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutChanged, this, [this](QAction *action, const QKeySequence &shortcut) {
|
|
if (action == d->activationAction) {
|
|
d->activationAction->setShortcut(shortcut);
|
|
d->globalShortcutChanged();
|
|
}
|
|
});
|
|
} else if (d->activationAction->shortcut() == shortcut) {
|
|
return;
|
|
}
|
|
|
|
d->activationAction->setShortcut(shortcut);
|
|
d->globalShortcutEnabled = true;
|
|
QList<QKeySequence> seqs;
|
|
seqs << shortcut;
|
|
KGlobalAccel::self()->setShortcut(d->activationAction, seqs, KGlobalAccel::NoAutoloading);
|
|
d->globalShortcutChanged();
|
|
}
|
|
|
|
QKeySequence Applet::globalShortcut() const
|
|
{
|
|
if (d->activationAction) {
|
|
QList<QKeySequence> shortcuts = KGlobalAccel::self()->shortcut(d->activationAction);
|
|
if (!shortcuts.isEmpty()) {
|
|
return shortcuts.first();
|
|
}
|
|
}
|
|
|
|
return QKeySequence();
|
|
}
|
|
|
|
Types::Location Applet::location() const
|
|
{
|
|
Containment *c = containment();
|
|
return c ? c->d->location : Plasma::Types::Desktop;
|
|
}
|
|
|
|
bool Applet::hasConfigurationInterface() const
|
|
{
|
|
return d->hasConfigurationInterface;
|
|
}
|
|
|
|
void Applet::setHasConfigurationInterface(bool hasInterface)
|
|
{
|
|
if (hasInterface == d->hasConfigurationInterface) {
|
|
return;
|
|
}
|
|
|
|
QAction *configAction = d->actions->action(QStringLiteral("configure"));
|
|
if (configAction) {
|
|
bool enable = hasInterface;
|
|
if (enable) {
|
|
const bool unlocked = immutability() == Types::Mutable;
|
|
enable = unlocked || KAuthorized::authorize(QStringLiteral("plasma/allow_configure_when_locked"));
|
|
}
|
|
configAction->setEnabled(enable);
|
|
}
|
|
|
|
d->hasConfigurationInterface = hasInterface;
|
|
}
|
|
|
|
void Applet::configChanged()
|
|
{
|
|
if (d->script) {
|
|
if (d->configLoader) {
|
|
d->configLoader->load();
|
|
}
|
|
d->script->configChanged();
|
|
}
|
|
}
|
|
|
|
void Applet::setAssociatedApplication(const QString &string)
|
|
{
|
|
AssociatedApplicationManager::self()->setApplication(this, string);
|
|
|
|
QAction *runAssociatedApplication = d->actions->action(QStringLiteral("run associated application"));
|
|
if (runAssociatedApplication) {
|
|
bool valid = AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this);
|
|
runAssociatedApplication->setVisible(valid);
|
|
runAssociatedApplication->setEnabled(valid);
|
|
}
|
|
}
|
|
|
|
void Applet::setAssociatedApplicationUrls(const QList<QUrl> &urls)
|
|
{
|
|
AssociatedApplicationManager::self()->setUrls(this, urls);
|
|
|
|
QAction *runAssociatedApplication = d->actions->action(QStringLiteral("run associated application"));
|
|
if (runAssociatedApplication) {
|
|
bool valid = AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this);
|
|
runAssociatedApplication->setVisible(valid);
|
|
runAssociatedApplication->setEnabled(valid);
|
|
}
|
|
}
|
|
|
|
QString Applet::associatedApplication() const
|
|
{
|
|
return AssociatedApplicationManager::self()->application(this);
|
|
}
|
|
|
|
QList<QUrl> Applet::associatedApplicationUrls() const
|
|
{
|
|
return AssociatedApplicationManager::self()->urls(this);
|
|
}
|
|
|
|
void Applet::runAssociatedApplication()
|
|
{
|
|
AssociatedApplicationManager::self()->run(this);
|
|
}
|
|
|
|
bool Applet::hasValidAssociatedApplication() const
|
|
{
|
|
return AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this);
|
|
}
|
|
|
|
#if PLASMA_BUILD_DEPRECATED_SINCE(5, 19)
|
|
Applet *Applet::loadPlasmoid(const QString &path, uint appletId)
|
|
{
|
|
const KPluginMetaData md = appletMetadataForDirectory(path);
|
|
if (md.isValid()) {
|
|
QStringList types = md.serviceTypes();
|
|
|
|
if (types.contains(QLatin1String("Plasma/Containment"))) {
|
|
return new Containment(md, appletId);
|
|
} else {
|
|
return new Applet(md, nullptr, appletId);
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
|
|
void Applet::timerEvent(QTimerEvent *event)
|
|
{
|
|
if (d->transient) {
|
|
d->constraintsTimer.stop();
|
|
if (d->modificationsTimer) {
|
|
d->modificationsTimer->stop();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (event->timerId() == d->constraintsTimer.timerId()) {
|
|
d->constraintsTimer.stop();
|
|
|
|
// Don't flushPendingConstraints if we're just starting up
|
|
// flushPendingConstraints will be called by Corona
|
|
if (!(d->pendingConstraints & Plasma::Types::StartupCompletedConstraint)) {
|
|
flushPendingConstraintsEvents();
|
|
}
|
|
} else if (d->modificationsTimer && event->timerId() == d->modificationsTimer->timerId()) {
|
|
d->modificationsTimer->stop();
|
|
// invalid group, will result in save using the default group
|
|
KConfigGroup cg;
|
|
|
|
save(cg);
|
|
Q_EMIT configNeedsSaving();
|
|
}
|
|
}
|
|
|
|
bool Applet::isContainment() const
|
|
{
|
|
// HACK: this is a special case for the systray
|
|
// containment in an applet that is not a containment
|
|
Applet *pa = qobject_cast<Applet *>(parent());
|
|
if (pa && !pa->isContainment()) {
|
|
return true;
|
|
}
|
|
// normal "acting as a containment" condition
|
|
return qobject_cast<const Containment *>(this) && qobject_cast<Corona *>(parent());
|
|
}
|
|
|
|
} // Plasma namespace
|
|
|
|
#include "moc_applet.cpp"
|