diff --git a/src/scriptengines/qml/plasmoid/appletinterface.cpp b/src/scriptengines/qml/plasmoid/appletinterface.cpp index 57c50a139..d54e5f7d8 100644 --- a/src/scriptengines/qml/plasmoid/appletinterface.cpp +++ b/src/scriptengines/qml/plasmoid/appletinterface.cpp @@ -1,846 +1,846 @@ /* * Copyright 2008-2013 Aaron Seigo * Copyright 2010-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 "appletinterface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "containmentinterface.h" #include "wallpaperinterface.h" #include #include Q_DECLARE_METATYPE(AppletInterface *) AppletInterface::AppletInterface(DeclarativeAppletScript *script, const QVariantList &args, QQuickItem *parent) : AppletQuickItem(script->applet(), parent), m_configuration(nullptr), m_appletScriptEngine(script), m_toolTipTextFormat(0), m_toolTipItem(nullptr), m_args(args), m_backgroundHints(Plasma::Types::StandardBackground), m_hideOnDeactivate(true), m_oldKeyboardShortcut(0), m_dummyNativeInterface(nullptr), m_positionBeforeRemoval(QPointF(-1, -1)) { qmlRegisterType(); connect(this, &AppletInterface::configNeedsSaving, applet(), &Plasma::Applet::configNeedsSaving); connect(applet(), &Plasma::Applet::immutabilityChanged, this, &AppletInterface::immutabilityChanged); connect(applet(), &Plasma::Applet::userConfiguringChanged, this, &AppletInterface::userConfiguringChanged); connect(applet(), &Plasma::Applet::contextualActionsAboutToShow, this, &AppletInterface::contextualActionsAboutToShow); connect(applet(), &Plasma::Applet::statusChanged, this, &AppletInterface::statusChanged); connect(applet(), &Plasma::Applet::destroyedChanged, this, &AppletInterface::destroyedChanged); connect(applet(), &Plasma::Applet::titleChanged, this, &AppletInterface::titleChanged); connect(applet(), &Plasma::Applet::titleChanged, this, [this]() { if (m_toolTipMainText.isNull()) { emit toolTipMainTextChanged(); } }); connect(applet(), &Plasma::Applet::iconChanged, this, &AppletInterface::iconChanged); connect(applet(), &Plasma::Applet::busyChanged, this, &AppletInterface::busyChanged); connect(applet(), &Plasma::Applet::configurationRequiredChanged, this, [this](bool configurationRequired, const QString &reason) { Q_UNUSED(configurationRequired); Q_UNUSED(reason); emit configurationRequiredChanged(); emit configurationRequiredReasonChanged(); }); connect(applet(), &Plasma::Applet::activated, this, &AppletInterface::activated); connect(appletScript(), &DeclarativeAppletScript::formFactorChanged, this, &AppletInterface::formFactorChanged); connect(appletScript(), &DeclarativeAppletScript::locationChanged, this, &AppletInterface::locationChanged); connect(appletScript(), &DeclarativeAppletScript::contextChanged, this, &AppletInterface::contextChanged); if (applet()->containment()) { connect(applet()->containment(), &Plasma::Containment::screenChanged, this, &AppletInterface::screenChanged); // Screen change implies geo change for good measure. connect(applet()->containment(), &Plasma::Containment::screenChanged, this, &AppletInterface::screenGeometryChanged); connect(applet()->containment()->corona(), &Plasma::Corona::screenGeometryChanged, this, [this](int id) { if (id == applet()->containment()->screen()) { emit screenGeometryChanged(); } }); } connect(this, &AppletInterface::expandedChanged, [=](bool expanded) { //if both compactRepresentationItem and fullRepresentationItem exist, //the applet is in a popup if (expanded) { if (compactRepresentationItem() && fullRepresentationItem() && fullRepresentationItem()->window() && compactRepresentationItem()->window() && fullRepresentationItem()->window() != compactRepresentationItem()->window() && fullRepresentationItem()->parentItem()) { fullRepresentationItem()->parentItem()->installEventFilter(this); } else if (fullRepresentationItem() && fullRepresentationItem()->parentItem()) { fullRepresentationItem()->parentItem()->removeEventFilter(this); } } }); } AppletInterface::~AppletInterface() { } DeclarativeAppletScript *AppletInterface::appletScript() const { return m_appletScriptEngine; } void AppletInterface::init() { if (qmlObject()->rootObject() && m_configuration) { return; } m_configuration = new KDeclarative::ConfigPropertyMap(applet()->configScheme(), this); AppletQuickItem::init(); geometryChanged(QRectF(), QRectF(x(), y(), width(), height())); emit busyChanged(); applet()->updateConstraints(Plasma::Types::UiReadyConstraint); connect(applet(), &Plasma::Applet::activated, [ = ]() { // in case the applet doesn't want to get shrinked on reactivation, // we always expand it again (only in order to conform with legacy behaviour) bool activate = !( isExpanded() && isActivationTogglesExpanded() ); setExpanded(activate); if (activate) { if (QQuickItem *i = qobject_cast(fullRepresentationItem())) { // Bug 372476: never pull focus away from it, only setFocus(true) i->setFocus(true, Qt::ShortcutFocusReason); } } }); if (m_args.count() == 1) { emit externalData(QString(), m_args.first()); } else if (!m_args.isEmpty()) { emit externalData(QString(), m_args); } } void AppletInterface::destroyedChanged(bool destroyed) { //if an item loses its scene before losing the focus, will never //be able to gain focus again if (destroyed && window() && window()->activeFocusItem()) { QQuickItem *focus = window()->activeFocusItem(); QQuickItem *candidate = focus; bool isAncestor = false; //search if the current focus item is a child or grandchild of the applet while (candidate) { if (candidate == this) { isAncestor = true; break; } candidate = candidate->parentItem(); } if (isAncestor) { //Found? remove focus for the whole hierarchy candidate = focus; while (candidate && candidate != this) { candidate->setFocus(false); candidate = candidate->parentItem(); } } } setVisible(!destroyed); } Plasma::Types::FormFactor AppletInterface::formFactor() const { return applet()->formFactor(); } Plasma::Types::Location AppletInterface::location() const { return applet()->location(); } QString AppletInterface::currentActivity() const { if (applet()->containment()) { return applet()->containment()->activity(); } else { return QString(); } } QObject *AppletInterface::configuration() const { return m_configuration; } uint AppletInterface::id() const { return applet()->id(); } QString AppletInterface::pluginName() const { return applet()->pluginMetaData().isValid() ? applet()->pluginMetaData().pluginId() : QString(); } QString AppletInterface::icon() const { return applet()->icon(); } void AppletInterface::setIcon(const QString &icon) { if (applet()->icon() == icon) { return; } applet()->setIcon(icon); } QString AppletInterface::title() const { return applet()->title(); } void AppletInterface::setTitle(const QString &title) { if (applet()->title() == title) { return; } applet()->setTitle(title); } QString AppletInterface::toolTipMainText() const { if (m_toolTipMainText.isNull()) { return title(); } else { return m_toolTipMainText; } } void AppletInterface::setToolTipMainText(const QString &text) { //Here we are abusing the difference between a null and an empty string. //by default is null so falls back to the name //the fist time it gets set, an empty non null one is set, and won't fallback anymore if (!m_toolTipMainText.isNull() && m_toolTipMainText == text) { return; } if (text.isEmpty()) { m_toolTipMainText = QStringLiteral("");//this "" makes it non-null } else { m_toolTipMainText = text; } emit toolTipMainTextChanged(); } QString AppletInterface::toolTipSubText() const { if (m_toolTipSubText.isNull() && applet()->pluginMetaData().isValid()) { return applet()->pluginMetaData().description(); } else { return m_toolTipSubText; } } void AppletInterface::setToolTipSubText(const QString &text) { //Also there the difference between null and empty gets exploited if (!m_toolTipSubText.isNull() && m_toolTipSubText == text) { return; } if (text.isEmpty()) { m_toolTipSubText = QStringLiteral("");//this "" makes it non-null } else { m_toolTipSubText = text; } emit toolTipSubTextChanged(); } int AppletInterface::toolTipTextFormat() const { return m_toolTipTextFormat; } void AppletInterface::setToolTipTextFormat(int format) { if (m_toolTipTextFormat == format) { return; } m_toolTipTextFormat = format; emit toolTipTextFormatChanged(); } QQuickItem *AppletInterface::toolTipItem() const { return m_toolTipItem.data(); } void AppletInterface::setToolTipItem(QQuickItem *toolTipItem) { if (m_toolTipItem.data() == toolTipItem) { return; } m_toolTipItem = toolTipItem; connect(m_toolTipItem.data(), &QObject::destroyed, this, &AppletInterface::toolTipItemChanged); emit toolTipItemChanged(); } bool AppletInterface::isBusy() const { return applet()->isBusy(); } void AppletInterface::setBusy(bool busy) { applet()->setBusy(busy); } Plasma::Types::BackgroundHints AppletInterface::backgroundHints() const { return m_backgroundHints; } void AppletInterface::setBackgroundHints(Plasma::Types::BackgroundHints hint) { if (m_backgroundHints == hint) { return; } m_backgroundHints = hint; emit backgroundHintsChanged(); } void AppletInterface::setConfigurationRequired(bool needsConfiguring, const QString &reason) { appletScript()->setConfigurationRequired(needsConfiguring, reason); } QString AppletInterface::file(const QString &fileType) { return appletScript()->filePath(fileType, QString()); } QString AppletInterface::file(const QString &fileType, const QString &filePath) { return appletScript()->filePath(fileType, filePath); } QList AppletInterface::contextualActions() const { QList actions; Plasma::Applet *a = applet(); if (a->failedToLaunch()) { return actions; } foreach (const QString &name, m_actions) { QAction *action = a->actions()->action(name); if (action) { actions << action; } } return actions; } void AppletInterface::setActionSeparator(const QString &name) { Plasma::Applet *a = applet(); QAction *action = a->actions()->action(name); if (action) { action->setSeparator(true); } else { action = new QAction(this); action->setSeparator(true); a->actions()->addAction(name, action); m_actions.append(name); } } void AppletInterface::setAction(const QString &name, const QString &text, const QString &icon, const QString &shortcut) { Plasma::Applet *a = applet(); QAction *action = a->actions()->action(name); if (action) { action->setText(text); } else { action = new QAction(text, this); a->actions()->addAction(name, action); Q_ASSERT(!m_actions.contains(name)); m_actions.append(name); connect(action, &QAction::triggered, this, [this, name] { executeAction(name); }); } if (!icon.isEmpty()) { action->setIcon(QIcon::fromTheme(icon)); } if (!shortcut.isEmpty()) { action->setShortcut(shortcut); } action->setObjectName(name); } void AppletInterface::removeAction(const QString &name) { Plasma::Applet *a = applet(); QAction *action = a->actions()->action(name); delete action; m_actions.removeAll(name); } void AppletInterface::clearActions() { Q_FOREACH (const QString &action, m_actions) { removeAction(action); } } QAction *AppletInterface::action(QString name) const { return applet()->actions()->action(name); } bool AppletInterface::immutable() const { return applet()->immutability() != Plasma::Types::Mutable; } Plasma::Types::ImmutabilityType AppletInterface::immutability() const { return applet()->immutability(); } bool AppletInterface::userConfiguring() const { return applet()->isUserConfiguring(); } int AppletInterface::apiVersion() const { // Look for C++ plugins first auto filter = [](const KPluginMetaData &md) -> bool { return md.value(QStringLiteral("X-Plasma-API")) == QLatin1String("declarativeappletscript") && md.value(QStringLiteral("X-Plasma-ComponentTypes")).contains(QLatin1String("Applet")); }; QVector plugins = KPluginLoader::findPlugins(QStringLiteral("plasma/scriptengines"), filter); if (plugins.isEmpty()) { return -1; } return plugins.first().value(QStringLiteral("X-KDE-PluginInfo-Version")).toInt(); } void AppletInterface::setAssociatedApplication(const QString &string) { if (applet()->associatedApplication() == string) { return; } applet()->setAssociatedApplication(string); emit associatedApplicationChanged(); } QString AppletInterface::associatedApplication() const { return applet()->associatedApplication(); } void AppletInterface::setAssociatedApplicationUrls(const QList &urls) { if (applet()->associatedApplicationUrls() == urls) { return; } applet()->setAssociatedApplicationUrls(urls); emit associatedApplicationUrlsChanged(); } QList AppletInterface::associatedApplicationUrls() const { return applet()->associatedApplicationUrls(); } void AppletInterface::setStatus(const Plasma::Types::ItemStatus &status) { applet()->setStatus(status); } Plasma::Types::ItemStatus AppletInterface::status() const { return applet()->status(); } int AppletInterface::screen() const { if (Plasma::Containment* c = applet()->containment()) { return c->screen(); } return -1; } QRect AppletInterface::screenGeometry() const { - if (!applet() || !applet()->containment()) { + if (!applet() || !applet()->containment() || !applet()->containment()->corona()) { return QRect(); } return applet()->containment()->corona()->screenGeometry(applet()->containment()->screen()); } void AppletInterface::setHideOnWindowDeactivate(bool hide) { if (m_hideOnDeactivate != hide) { m_hideOnDeactivate = hide; emit hideOnWindowDeactivateChanged(); } } bool AppletInterface::hideOnWindowDeactivate() const { return m_hideOnDeactivate; } QKeySequence AppletInterface::globalShortcut() const { return applet()->globalShortcut(); } void AppletInterface::setGlobalShortcut(const QKeySequence &sequence) { applet()->setGlobalShortcut(sequence); } QObject *AppletInterface::nativeInterface() { if (qstrcmp(applet()->metaObject()->className(),"Plasma::Applet") != 0) { return applet(); } else { if (!m_dummyNativeInterface) { m_dummyNativeInterface = new QObject(this); } return m_dummyNativeInterface; } } bool AppletInterface::configurationRequired() const { return applet()->configurationRequired(); } void AppletInterface::setConfigurationRequiredProperty(bool needsConfiguring) { appletScript()->setConfigurationRequired(needsConfiguring, applet()->configurationRequiredReason()); } QString AppletInterface::configurationRequiredReason() const { return applet()->configurationRequiredReason(); } void AppletInterface::setConfigurationRequiredReason(const QString &reason) { appletScript()->setConfigurationRequired(applet()->configurationRequired(), reason); } QString AppletInterface::downloadPath(const QString &file) { Q_UNUSED(file); return downloadPath(); } QString AppletInterface::downloadPath() const { const QString downloadDir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + QStringLiteral("/Plasma/") + applet()->pluginMetaData().pluginId() + QLatin1Char('/'); if (!QFile::exists(downloadDir)) { QDir dir({ QLatin1Char('/') }); dir.mkpath(downloadDir); } return downloadDir; } QStringList AppletInterface::downloadedFiles() const { const QString downloadDir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + QStringLiteral("/Plasma/") + applet()->pluginMetaData().pluginId() + QLatin1Char('/'); QDir dir(downloadDir); return dir.entryList(QDir::Files | QDir::NoSymLinks | QDir::Readable); } void AppletInterface::executeAction(const QString &name) { if (qmlObject()->rootObject()) { const QMetaObject *metaObj = qmlObject()->rootObject()->metaObject(); const QByteArray actionMethodName = "action_" + name.toUtf8(); const QByteArray actionFunctionName = actionMethodName + QByteArray("()"); if (metaObj->indexOfMethod(QMetaObject::normalizedSignature(actionFunctionName.constData()).constData()) != -1) { QMetaObject::invokeMethod(qmlObject()->rootObject(), actionMethodName.constData(), Qt::DirectConnection); } else { QMetaObject::invokeMethod(qmlObject()->rootObject(), "actionTriggered", Qt::DirectConnection, Q_ARG(QVariant, name)); } } } QVariantList AppletInterface::availableScreenRegion() const { QVariantList regVal; if (!applet()->containment() || !applet()->containment()->corona()) { return regVal; } QRegion reg = QRect(0, 0, width(), height()); int screenId = screen(); if (screenId > -1) { reg = applet()->containment()->corona()->availableScreenRegion(screenId); } auto it = reg.begin(); const auto itEnd = reg.end(); for (; it != itEnd; ++it) { QRect rect = *it; //make it relative QRect geometry = applet()->containment()->corona()->screenGeometry(screenId); rect.moveTo(rect.topLeft() - geometry.topLeft()); regVal << QVariant::fromValue(QRectF(rect)); } return regVal; } QRect AppletInterface::availableScreenRect() const { if (!applet()->containment() || !applet()->containment()->corona()) { return QRect(); } QRect rect(0, 0, width(), height()); int screenId = screen(); if (screenId > -1) { rect = applet()->containment()->corona()->availableScreenRect(screenId); //make it relative QRect geometry = applet()->containment()->corona()->screenGeometry(screenId); rect.moveTo(rect.topLeft() - geometry.topLeft()); } return rect; } bool AppletInterface::event(QEvent *event) { // QAction keyboard shortcuts cannot work with QML2 (and probably newver will // since in Qt qtquick and qwidgets cannot depend from each other in any way) // so do a simple keyboard shortcut matching here if (event->type() == QEvent::KeyPress) { QKeyEvent *ke = static_cast(event); QKeySequence seq(ke->key()|ke->modifiers()); QList actions = applet()->actions()->actions(); //find the wallpaper action if we are a containment ContainmentInterface *ci = qobject_cast(this); if (ci) { WallpaperInterface *wi = ci->wallpaperInterface(); if (wi) { actions << wi->contextualActions(); } } //add any actions of the corona if (applet()->containment() && applet()->containment()->corona()) { actions << applet()->containment()->corona()->actions()->actions(); } bool keySequenceUsed = false; foreach (auto a, actions) { if (a->shortcut().isEmpty()) { continue; } //this will happen on a normal, non emacs shortcut if (seq.matches(a->shortcut()) == QKeySequence::ExactMatch) { event->accept(); a->trigger(); m_oldKeyboardShortcut = 0; return true; //first part of an emacs style shortcut? } else if (seq.matches(a->shortcut()) == QKeySequence::PartialMatch) { keySequenceUsed = true; m_oldKeyboardShortcut = ke->key()|ke->modifiers(); //no match at all, but it can be the second part of an emacs style shortcut } else { QKeySequence seq(m_oldKeyboardShortcut, ke->key()|ke->modifiers()); if (seq.matches(a->shortcut()) == QKeySequence::ExactMatch) { event->accept(); a->trigger(); return true; } } } if (!keySequenceUsed) { m_oldKeyboardShortcut = 0; } } return AppletQuickItem::event(event); } bool AppletInterface::eventFilter(QObject *watched, QEvent *event) { if (event->type() == QEvent::MouseButtonPress) { QMouseEvent *e = static_cast(event); //pass it up to the applet //well, actually we have to pass it to the *containment* //because all the code for showing an applet's contextmenu is actually in Containment. Plasma::Containment *c = applet()->containment(); if (c) { const QString trigger = Plasma::ContainmentActions::eventToString(event); Plasma::ContainmentActions *plugin = c->containmentActions().value(trigger); if (!plugin) { return false; } ContainmentInterface *ci = c->property("_plasma_graphicObject").value(); if (!ci) { return false; } //the plugin can be a single action or a context menu //Don't have an action list? execute as single action //and set the event position as action data if (plugin->contextualActions().length() == 1) { // but first check whether we are not a popup // we don't want to randomly creates applets without confirmation if (static_cast(watched)->window() != ci->window()) { return true; } QAction *action = plugin->contextualActions().at(0); action->setData(e->globalPos()); action->trigger(); return true; } QMenu *desktopMenu = new QMenu; if (desktopMenu->winId()) { desktopMenu->windowHandle()->setTransientParent(window()); } emit applet()->contextualActionsAboutToShow(); ci->addAppletActions(desktopMenu, applet(), event); if (!desktopMenu->isEmpty()) { desktopMenu->setAttribute(Qt::WA_DeleteOnClose); desktopMenu->popup(e->globalPos()); return true; } delete desktopMenu; return false; } } return AppletQuickItem::eventFilter(watched, event); } #include "moc_appletinterface.cpp"