diff --git a/applets/icon/iconapplet.h b/applets/icon/iconapplet.h --- a/applets/icon/iconapplet.h +++ b/applets/icon/iconapplet.h @@ -60,6 +60,8 @@ Q_INVOKABLE void processDrop(QObject *dropEvent); Q_INVOKABLE void configure(); + Q_INVOKABLE bool isAcceptableDrag(QObject *dropEvent); + signals: void urlChanged(const QUrl &url); @@ -71,6 +73,9 @@ private: void setIconName(const QString &iconName); + static QList urlsFromDrop(QObject *dropEvent); + static bool isExecutable(const QMimeType &mimeType); + void populate(); void populateFromDesktopFile(const QString &path); diff --git a/applets/icon/iconapplet.cpp b/applets/icon/iconapplet.cpp --- a/applets/icon/iconapplet.cpp +++ b/applets/icon/iconapplet.cpp @@ -373,21 +373,9 @@ void IconApplet::processDrop(QObject *dropEvent) { Q_ASSERT(dropEvent); + Q_ASSERT(isAcceptableDrag(dropEvent)); - // DeclarativeDropEvent and co aren't public - const QObject *mimeData = qvariant_cast(dropEvent->property("mimeData")); - Q_ASSERT(mimeData); - - const QJsonArray &droppedUrls = mimeData->property("urls").toJsonArray(); - - QList urls; - urls.reserve(droppedUrls.count()); - foreach (const QJsonValue &droppedUrl, droppedUrls) { - const QUrl url(droppedUrl.toString()); - if (url.isValid()) { - urls.append(url); - } - } + const auto &urls = urlsFromDrop(dropEvent); if (urls.isEmpty()) { return; @@ -403,9 +391,7 @@ QMimeDatabase db; const QMimeType mimeType = db.mimeTypeForUrl(m_url); - if (KAuthorized::authorize(QStringLiteral("shell_access")) - && (mimeType.inherits(QStringLiteral("application/x-executable")) - || mimeType.inherits(QStringLiteral("application/x-shellscript")))) { + if (isExecutable(mimeType)) { // isAcceptableDrag has the KAuthorized check for this QProcess::startDetached(m_url.toLocalFile(), QUrl::toStringList(urls)); return; } @@ -427,6 +413,61 @@ } } +bool IconApplet::isAcceptableDrag(QObject *dropEvent) +{ + Q_ASSERT(dropEvent); + + const auto &urls = urlsFromDrop(dropEvent); + + if (urls.isEmpty()) { + return false; + } + + const QString &localPath = m_url.toLocalFile(); + if (KDesktopFile::isDesktopFile(localPath)) { + return true; + } + + QMimeDatabase db; + const QMimeType mimeType = db.mimeTypeForUrl(m_url); + + if (KAuthorized::authorize(QStringLiteral("shell_access")) && isExecutable(mimeType)) { + return true; + } + + if (mimeType.inherits(QStringLiteral("inode/directory"))) { + return true; + } + + return false; +} + +QList IconApplet::urlsFromDrop(QObject *dropEvent) +{ + // DeclarativeDropEvent and co aren't public + const QObject *mimeData = qvariant_cast(dropEvent->property("mimeData")); + Q_ASSERT(mimeData); + + const QJsonArray &droppedUrls = mimeData->property("urls").toJsonArray(); + + QList urls; + urls.reserve(droppedUrls.count()); + for (const QJsonValue &droppedUrl : droppedUrls) { + const QUrl url(droppedUrl.toString()); + if (url.isValid()) { + urls.append(url); + } + } + + return urls; +} + +bool IconApplet::isExecutable(const QMimeType &mimeType) +{ + return (mimeType.inherits(QStringLiteral("application/x-executable")) + || mimeType.inherits(QStringLiteral("application/x-shellscript"))); +} + void IconApplet::configure() { KPropertiesDialog *dialog = m_configDialog.data(); diff --git a/applets/icon/package/contents/ui/main.qml b/applets/icon/package/contents/ui/main.qml --- a/applets/icon/package/contents/ui/main.qml +++ b/applets/icon/package/contents/ui/main.qml @@ -79,10 +79,22 @@ id: dropArea anchors.fill: parent preventStealing: true - onDragEnter: root.containsAcceptableDrag = event.mimeData.hasUrls + onDragEnter: { + var acceptable = plasmoid.nativeInterface.isAcceptableDrag(event); + root.containsAcceptableDrag = acceptable; + + if (!acceptable) { + event.ignore(); + } + } onDragLeave: root.containsAcceptableDrag = false onDrop: { - plasmoid.nativeInterface.processDrop(event) + if (root.containsAcceptableDrag) { + plasmoid.nativeInterface.processDrop(event) + } else { + event.ignore(); + } + root.containsAcceptableDrag = false } }