From f684e6b2e04d14ac9e2b8ddc2de8b30b60f6262f Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 20 Feb 2017 11:09:43 +0100 Subject: [PATCH] [WIP]: integrate drop menu and filecopy drop menu Summary: add an optional menu parameter to processMimeData to mnually pass an existing qmenu. folderview can use that to pass its own filecopyjob drop qmenu to be populated with extra entries Test Plan: dropping a file on folderview will show a menu with entries from both. works both with folderview and desktop containments Reviewers: #plasma, hein Reviewed By: #plasma, hein Subscribers: plasma-devel, #frameworks Tags: #plasma, #frameworks Differential Revision: https://phabricator.kde.org/D4576 Change-Id: Iae911d4e692f2e591358959758ca6ddd131379c0 --- .../qml/plasmoid/containmentinterface.cpp | 282 +++++++++++------- .../qml/plasmoid/containmentinterface.h | 7 +- 2 files changed, 184 insertions(+), 105 deletions(-) diff --git a/src/scriptengines/qml/plasmoid/containmentinterface.cpp b/src/scriptengines/qml/plasmoid/containmentinterface.cpp index 4caaf7a37..445f42e3e 100644 --- a/src/scriptengines/qml/plasmoid/containmentinterface.cpp +++ b/src/scriptengines/qml/plasmoid/containmentinterface.cpp @@ -40,6 +40,7 @@ #include "kio/jobclasses.h" // for KIO::JobFlags #include "kio/job.h" #include "kio/scheduler.h" +#include #endif #include @@ -407,17 +408,17 @@ QPointF ContainmentInterface::adjustToAvailableScreenRegion(int x, int y, int w, return rect.topLeft(); } -void ContainmentInterface::processMimeData(QObject *mimeDataProxy, int x, int y) +void ContainmentInterface::processMimeData(QObject *mimeDataProxy, int x, int y, KIO::DropJob *dropJob) { QMimeData* mime = qobject_cast(mimeDataProxy); if (mime) { - processMimeData(mime, x, y); + processMimeData(mime, x, y, dropJob); } else { - processMimeData(mimeDataProxy->property("mimeData").value(), x, y); + processMimeData(mimeDataProxy->property("mimeData").value(), x, y, dropJob); } } -void ContainmentInterface::processMimeData(QMimeData *mimeData, int x, int y) +void ContainmentInterface::processMimeData(QMimeData *mimeData, int x, int y, KIO::DropJob *dropJob) { if (!mimeData) { return; @@ -458,11 +459,15 @@ void ContainmentInterface::processMimeData(QMimeData *mimeData, int x, int y) QObject::connect(job, SIGNAL(mimetype(KIO::Job*,QString)), this, SLOT(mimeTypeRetrieved(KIO::Job*,QString))); - QMenu *choices = new QMenu(i18n("Content dropped")); - choices->addAction(QIcon::fromTheme(QStringLiteral("process-working")), i18n("Fetching file type...")); - choices->popup(window() ? window()->mapToGlobal(QPoint(x, y)) : QPoint(x, y)); + if (dropJob) { + m_dropJobs[job] = dropJob; + } else { + QMenu *choices = new QMenu(i18n("Content dropped")); + choices->addAction(QIcon::fromTheme(QStringLiteral("process-working")), i18n("Fetching file type...")); + choices->popup(window() ? window()->mapToGlobal(QPoint(x, y)) : QPoint(x, y)); - m_dropMenus[job] = choices; + m_dropMenus[job] = choices; + } #endif } @@ -489,34 +494,49 @@ void ContainmentInterface::processMimeData(QMimeData *mimeData, int x, int y) if (seenPlugins.isEmpty()) { // do nothing + //directly create if only one offer only if the conteinment didn't pass an existing plugin } else if (seenPlugins.count() == 1) { selectedPlugin = seenPlugins.constBegin().key(); + Plasma::Applet *applet = createApplet(selectedPlugin, QVariantList(), QRect(x, y, -1, -1)); + setAppletArgs(applet, pluginFormats[selectedPlugin], mimeData->data(pluginFormats[selectedPlugin])); } else { - QMenu choices; + QMenu *choices = nullptr; + if (!dropJob) { + choices = new QMenu(); + } + QList extraActions; QHash actionsToPlugins; foreach (const KPluginInfo &info, seenPlugins) { QAction *action; if (!info.icon().isEmpty()) { - action = choices.addAction(QIcon::fromTheme(info.icon()), info.name()); + action = new QAction(QIcon::fromTheme(info.icon()), info.name()); } else { - action = choices.addAction(info.name()); + action = new QAction(info.name()); } + extraActions << action; + if (choices) { + choices->addAction(action); + } + action->setData(info.pluginName()); + connect(action, &QAction::triggered, this, [this, x, y, mimeData, action]() { + const QString selectedPlugin = action->data().toString(); + Plasma::Applet *applet = createApplet(selectedPlugin, QVariantList(), QRect(x, y, -1, -1)); + setAppletArgs(applet, selectedPlugin, mimeData->data(selectedPlugin)); + }); actionsToPlugins.insert(action, info.pluginName()); } - QAction *choice = choices.exec(window() ? window()->mapToGlobal(QPoint(x, y)) : QPoint(x, y)); - if (choice) { - selectedPlugin = actionsToPlugins[choice]; + //if the menu was created by ourselves, delete it + if (choices) { + QAction *choice = choices->exec(window() ? window()->mapToGlobal(QPoint(x, y)) : QPoint(x, y)); + + delete choices; + } else { + Q_ASSERT(dropJob); + dropJob->setApplicationActions(extraActions); } } - - if (!selectedPlugin.isEmpty()) { - - Plasma::Applet *applet = createApplet(selectedPlugin, QVariantList(), QRect(x, y, -1, -1)); - setAppletArgs(applet, pluginFormats[selectedPlugin], mimeData->data(pluginFormats[selectedPlugin])); - - } } } @@ -526,7 +546,7 @@ void ContainmentInterface::clearDataForMimeJob(KIO::Job *job) QObject::disconnect(job, 0, this, 0); m_dropPoints.remove(job); QMenu *choices = m_dropMenus.take(job); - delete choices; + m_dropJobs.remove(job); job->kill(); #endif // PLASMA_NO_KIO } @@ -537,9 +557,6 @@ void ContainmentInterface::dropJobResult(KJob *job) if (job->error()) { qDebug() << "ERROR" << job->error() << ' ' << job->errorString(); } - // We call mimetypeRetrieved since there might be other mechanisms - // for finding suitable applets. Cleanup happens there as well. - mimeTypeRetrieved(qobject_cast(job), QString()); #endif // PLASMA_NO_KIO } @@ -547,6 +564,7 @@ void ContainmentInterface::mimeTypeRetrieved(KIO::Job *job, const QString &mimet { #ifndef PLASMA_NO_KIO qDebug() << "Mimetype Job returns." << mimetype; + KIO::TransferJob *tjob = dynamic_cast(job); if (!tjob) { qDebug() << "job should be a TransferJob, but isn't"; @@ -571,8 +589,11 @@ void ContainmentInterface::mimeTypeRetrieved(KIO::Job *job, const QString &mimet } QMenu *choices = m_dropMenus.value(tjob); - if (!choices) { - qDebug() << "Bailing out. No QMenu found for this job."; + QList dropActions; + KIO::DropJob *dropJob = m_dropJobs.value(tjob); + + if (!choices && !dropJob) { + qDebug() << "Bailing out. No QMenu or drop job found for this job."; clearDataForMimeJob(job); return; } @@ -598,71 +619,25 @@ void ContainmentInterface::mimeTypeRetrieved(KIO::Job *job, const QString &mimet if (!appletList.isEmpty() || !wallpaperList.isEmpty() || isPlasmaPackage) { QAction *installPlasmaPackageAction = nullptr; if (isPlasmaPackage) { - choices->addSection(i18n("Plasma Package")); - installPlasmaPackageAction = choices->addAction(QIcon::fromTheme(QStringLiteral("application-x-plasma")), i18n("Install")); - } - - QHash actionsToApplets; - choices->addSection(i18n("Widgets")); - foreach (const KPluginInfo &info, appletList) { - qDebug() << info.name(); - QAction *action; - if (!info.icon().isEmpty()) { - action = choices->addAction(QIcon::fromTheme(info.icon()), info.name()); + if (choices) { + choices->addSection(i18n("Plasma Package")); + installPlasmaPackageAction = choices->addAction(QIcon::fromTheme(QStringLiteral("application-x-plasma")), i18n("Install")); } else { - action = choices->addAction(info.name()); + QAction *action = new QAction(i18n("Plasma Package")); + action->setSeparator(true); + dropActions << action; + + installPlasmaPackageAction = new QAction(QIcon::fromTheme(QStringLiteral("application-x-plasma")), i18n("Install")); + Q_ASSERT(dropJob); + dropActions << installPlasmaPackageAction; + dropJob->setApplicationActions(dropActions); } - - actionsToApplets.insert(action, info.pluginName()); - qDebug() << info.pluginName(); - } - actionsToApplets.insert(choices->addAction(i18n("Icon")), QStringLiteral("org.kde.plasma.icon")); - - QHash actionsToWallpapers; - if (!wallpaperList.isEmpty()) { - choices->addSection(i18n("Wallpaper")); - - QMap sorted; - foreach (const KPluginInfo &info, appletList) { - sorted.insert(info.name(), info); - } - - foreach (const KPluginInfo &info, wallpaperList) { - QAction *action; - if (!info.icon().isEmpty()) { - action = choices->addAction(QIcon::fromTheme(info.icon()), info.name()); - } else { - action = choices->addAction(info.name()); - } - - actionsToWallpapers.insert(action, info.pluginName()); - } - } - - // HACK If the QMenu becomes empty at any point after the "determining mimetype" - // popup was shown, it self-destructs, does not matter if we call clear() or remove - // the action manually, hence we remove the aforementioned item after we populated the menu - choices->removeAction(choices->actions().at(0)); - - QAction *choice = choices->exec(); - if (choice) { - // Put the job on hold so it can be recycled to fetch the actual content, - // which is to be expected when something's dropped onto the desktop and - // an applet is to be created with this URL - if (!mimetype.isEmpty() && !tjob->error()) { - tjob->putOnHold(); - KIO::Scheduler::publishSlaveOnHold(); - } - - QString plugin = actionsToApplets.value(choice); - - if (choice == installPlasmaPackageAction) { + const QString &packagePath = tjob->url().toLocalFile(); + connect(installPlasmaPackageAction, &QAction::triggered, this, [this, tjob, posi, packagePath]() { using namespace KPackage; PackageStructure *structure = PackageLoader::self()->loadPackageStructure(QStringLiteral("Plasma/Applet")); Package package(structure); - const QString &packagePath = tjob->url().toLocalFile(); - KJob *installJob = package.update(packagePath); connect(installJob, &KJob::result, this, [this, packagePath, structure, posi](KJob *job) { auto fail = [job](const QString &text) { @@ -681,6 +656,7 @@ void ContainmentInterface::mimeTypeRetrieved(KIO::Job *job, const QString &mimet using namespace KPackage; Package package(structure); // TODO how can I get the path of the actual package? + package.setPath(packagePath); // TODO how can I get the plugin id? Package::metadata() is deprecated @@ -691,28 +667,128 @@ void ContainmentInterface::mimeTypeRetrieved(KIO::Job *job, const QString &mimet createApplet(package.metadata().pluginId(), QVariantList(), QRect(posi, QSize(-1,-1))); }); - } else if (plugin.isEmpty()) { - //set wallpapery stuff - plugin = actionsToWallpapers.value(choice); - if (m_wallpaperInterface && tjob->url().isValid()) { - m_wallpaperInterface->setUrl(tjob->url()); - } + }); + } + + if (choices) { + choices->addSection(i18n("Widgets")); + } else { + QAction *action = new QAction(i18n("Widgets")); + action->setSeparator(true); + dropActions << action; + } + foreach (const KPluginInfo &info, appletList) { + qDebug() << info.name(); + QAction *action; + if (!info.icon().isEmpty()) { + action = new QAction(QIcon::fromTheme(info.icon()), info.name()); } else { - Plasma::Applet *applet = createApplet(actionsToApplets[choice], QVariantList(), QRect(posi, QSize(-1,-1))); - setAppletArgs(applet, mimetype, tjob->url().toString()); + action = new QAction(info.name()); } - clearDataForMimeJob(job); - return; + if (choices) { + choices->addAction(action); + } + dropActions << action; + action->setData(info.pluginName()); + qDebug() << info.pluginName(); + const QUrl url = tjob->url(); + connect(action, &QAction::triggered, this, [this, action, posi, mimetype, url]() { + Plasma::Applet *applet = createApplet(action->data().toString(), QVariantList(), QRect(posi, QSize(-1,-1))); + setAppletArgs(applet, mimetype, url.toString()); + }); + } + { + QAction *action = new QAction(i18n("Icon")); + if (choices) { + choices->addAction(action); + } + dropActions << action; + action->setData(QStringLiteral("org.kde.plasma.icon")); + const QUrl url = tjob->url(); + connect(action, &QAction::triggered, this, [this, action, posi, mimetype, url](){ + Plasma::Applet *applet = createApplet(action->data().toString(), QVariantList(), QRect(posi, QSize(-1,-1))); + setAppletArgs(applet, mimetype, url.toString()); + }); } - } else { - // we can at least create an icon as a link to the URL - Plasma::Applet *applet = createApplet(QStringLiteral("org.kde.plasma.icon"), QVariantList(), QRect(posi, QSize(-1,-1))); - setAppletArgs(applet, mimetype, tjob->url().toString()); - } - } - clearDataForMimeJob(job); + QHash actionsToWallpapers; + if (!wallpaperList.isEmpty()) { + if (choices) { + choices->addSection(i18n("Wallpaper")); + } else { + QAction *action = new QAction(i18n("Wallpaper")); + action->setSeparator(true); + dropActions << action; + } + + QMap sorted; + foreach (const KPluginInfo &info, appletList) { + sorted.insert(info.name(), info); + } + + foreach (const KPluginInfo &info, wallpaperList) { + QAction *action; + if (!info.icon().isEmpty()) { + action = new QAction(QIcon::fromTheme(info.icon()), info.name()); + } else { + action = new QAction(info.name()); + } + + if (choices) { + choices->addAction(action); + } + dropActions << action; + actionsToWallpapers.insert(action, info.pluginName()); + const QUrl url = tjob->url(); + connect(action, &QAction::triggered, this, [this, action, url]() { + //set wallpapery stuff + if (m_wallpaperInterface && url.isValid()) { + m_wallpaperInterface->setUrl(url); + } + }); + } + } + + } else { + //case in which we created the menu ourselves, just the "fetching type entry, directly create the icon applet + if (choices) { + Plasma::Applet *applet = createApplet(QStringLiteral("org.kde.plasma.icon"), QVariantList(), QRect(posi, QSize(-1,-1))); + setAppletArgs(applet, mimetype, tjob->url().toString()); + } else { + QAction *action; + if (choices) { + choices->addSection(i18n("Widgets")); + action = choices->addAction(i18n("Icon")); + } else { + QAction *sep = new QAction(i18n("Widgets")); + sep->setSeparator(true); + dropActions << sep; + // we can at least create an icon as a link to the URL + action = new QAction(i18n("Icon")); + dropActions << action; + } + + const QUrl url = tjob->url(); + connect(action, &QAction::triggered, this, [this, posi, mimetype, url](){ + Plasma::Applet *applet = createApplet(QStringLiteral("org.kde.plasma.icon"), QVariantList(), QRect(posi, QSize(-1,-1))); + setAppletArgs(applet, mimetype, url.toString()); + }); + } + } + + if (choices) { + // HACK If the QMenu becomes empty at any point after the "determining mimetype" + // popup was shown, it self-destructs, does not matter if we call clear() or remove + // the action manually, hence we remove the aforementioned item after we populated the menu + choices->removeAction(choices->actions().at(0)); + choices->exec(); + } else { + dropJob->setApplicationActions(dropActions); + } + + clearDataForMimeJob(tjob); + } #endif // PLASMA_NO_KIO } diff --git a/src/scriptengines/qml/plasmoid/containmentinterface.h b/src/scriptengines/qml/plasmoid/containmentinterface.h index f1d96c1ae..8a0cbed1d 100644 --- a/src/scriptengines/qml/plasmoid/containmentinterface.h +++ b/src/scriptengines/qml/plasmoid/containmentinterface.h @@ -32,6 +32,7 @@ class WallpaperInterface; namespace KIO { class Job; +class DropJob; } namespace KActivities @@ -104,12 +105,12 @@ public: /** * Process the mime data arrived to a particular coordinate, either with a drag and drop or paste with middle mouse button */ - Q_INVOKABLE void processMimeData(QMimeData *data, int x, int y); + Q_INVOKABLE void processMimeData(QMimeData *data, int x, int y, KIO::DropJob *dropJob = 0); /** * Process the mime data arrived to a particular coordinate, either with a drag and drop or paste with middle mouse button */ - Q_INVOKABLE void processMimeData(QObject *data, int x, int y); + Q_INVOKABLE void processMimeData(QObject *data, int x, int y, KIO::DropJob *dropJob = 0); /** * Search for a containment at those coordinates. @@ -197,6 +198,8 @@ private: QList m_appletInterfaces; QHash m_dropPoints; QHash m_dropMenus; + QHash m_dropCallbacks; + QHash m_dropJobs; KActivities::Info *m_activityInfo; QPointer m_containment; QWeakPointer m_contextMenu;