Make dropping remote content onto Plasma work
The idea is that you drop a file from a webpage, or basically a URL onto Plasma and Plasma creates a suitable applet to display this URL. For example an image frame for picture, or a previewer for pdf files. Downloading the data itself (and possibly saving it) is left to the applets. The mimetype needs to be retrieved as it cannot be determined from the URL. The code pathes I've changed or added are, roughly: - "something" is dropped onto Plasma - if it's a remote URL, we don't know the mimetype of the object behind the URL yet - a KIO::TransferJob is used to retrieve the mimetype asynchronously, and will call back - we open a QMenu that says "Fetching file type..." - If the user closes the menu while the mimetype is being retrieved, we will open a new one - When the TransferJob calls back, and we have our mimetype, we offer a list of applets suitable for this kind of content - If the user chooses to create an applet, we put the transfer job on hold to make it available for recycling by the applet - If the user dismisses the offering, we kill the job Thanks to marco for reviewing and everybody else for the input :) Next steps are making some more applets work with this. CCMAIL:plasma-devel@kde.org svn path=/trunk/KDE/kdelibs/; revision=1009004
This commit is contained in:
parent
1fe4e538f0
commit
5ea600487a
113
containment.cpp
113
containment.cpp
@ -43,6 +43,9 @@
|
|||||||
#include <kstandarddirs.h>
|
#include <kstandarddirs.h>
|
||||||
#include <ktemporaryfile.h>
|
#include <ktemporaryfile.h>
|
||||||
#include <kwindowsystem.h>
|
#include <kwindowsystem.h>
|
||||||
|
#include "kio/jobclasses.h" // for KIO::JobFlags
|
||||||
|
#include "kio/job.h"
|
||||||
|
#include "kio/scheduler.h"
|
||||||
|
|
||||||
#include "animator.h"
|
#include "animator.h"
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
@ -611,7 +614,7 @@ void ContainmentPrivate::appletActions(KMenu &desktopMenu, Applet *applet, bool
|
|||||||
//if there is only one, don't create a submenu
|
//if there is only one, don't create a submenu
|
||||||
if(enabled < 2) {
|
if(enabled < 2) {
|
||||||
foreach(QAction *action, containmentMenu->actions()) {
|
foreach(QAction *action, containmentMenu->actions()) {
|
||||||
desktopMenu.addAction(action);
|
desktopMenu.addAction(action);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
desktopMenu.addMenu(containmentMenu);
|
desktopMenu.addMenu(containmentMenu);
|
||||||
@ -1058,6 +1061,7 @@ void ContainmentPrivate::dropData(QGraphicsSceneEvent *event)
|
|||||||
|
|
||||||
QGraphicsSceneDragDropEvent *dropEvent = dynamic_cast<QGraphicsSceneDragDropEvent*>(event);
|
QGraphicsSceneDragDropEvent *dropEvent = dynamic_cast<QGraphicsSceneDragDropEvent*>(event);
|
||||||
QGraphicsSceneMouseEvent *mouseEvent = dynamic_cast<QGraphicsSceneMouseEvent*>(event);
|
QGraphicsSceneMouseEvent *mouseEvent = dynamic_cast<QGraphicsSceneMouseEvent*>(event);
|
||||||
|
//kDebug() << "Something dropped mimetype, -data: " << appletMimetype << event->mimeData()->text();
|
||||||
|
|
||||||
QPointF pos;
|
QPointF pos;
|
||||||
QPointF scenePos;
|
QPointF scenePos;
|
||||||
@ -1081,7 +1085,7 @@ void ContainmentPrivate::dropData(QGraphicsSceneEvent *event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!mimeData) {
|
if (!mimeData) {
|
||||||
//Selection is either empty or not sopported on this OS
|
//Selection is either empty or not supported on this OS
|
||||||
kDebug() << "no mime data";
|
kDebug() << "no mime data";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1093,7 +1097,6 @@ void ContainmentPrivate::dropData(QGraphicsSceneEvent *event)
|
|||||||
if (!appletMimetype.isEmpty() && mimeData->hasFormat(appletMimetype)) {
|
if (!appletMimetype.isEmpty() && mimeData->hasFormat(appletMimetype)) {
|
||||||
QString data = mimeData->data(appletMimetype);
|
QString data = mimeData->data(appletMimetype);
|
||||||
const QStringList appletNames = data.split('\n', QString::SkipEmptyParts);
|
const QStringList appletNames = data.split('\n', QString::SkipEmptyParts);
|
||||||
|
|
||||||
foreach (const QString &appletName, appletNames) {
|
foreach (const QString &appletName, appletNames) {
|
||||||
//kDebug() << "doing" << appletName;
|
//kDebug() << "doing" << appletName;
|
||||||
QRectF geom(q->mapFromScene(scenePos), QSize(0, 0));
|
QRectF geom(q->mapFromScene(scenePos), QSize(0, 0));
|
||||||
@ -1123,12 +1126,14 @@ void ContainmentPrivate::dropData(QGraphicsSceneEvent *event)
|
|||||||
QRectF geom(pos, QSize());
|
QRectF geom(pos, QSize());
|
||||||
QVariantList args;
|
QVariantList args;
|
||||||
args << url.url();
|
args << url.url();
|
||||||
// kDebug() << mimeName;
|
//kDebug() << "can decode" << mimeName << args;
|
||||||
|
//kDebug() << "protocol:" << url.protocol();
|
||||||
KPluginInfo::List appletList = Applet::listAppletInfoForMimetype(mimeName);
|
KPluginInfo::List appletList = Applet::listAppletInfoForMimetype(mimeName);
|
||||||
|
|
||||||
if (!appletList.isEmpty()) {
|
if (!appletList.isEmpty()) {
|
||||||
//TODO: should we show a dialog here to choose which plasmoid load if
|
// The mimetype is known, i.e. there are applet that can load this mimetype
|
||||||
//!appletList.isEmpty()
|
// Offer the applets in a popupmenu
|
||||||
|
kDebug() << "Local file.";
|
||||||
QMenu choices;
|
QMenu choices;
|
||||||
QHash<QAction *, QString> actionsToPlugins;
|
QHash<QAction *, QString> actionsToPlugins;
|
||||||
foreach (const KPluginInfo &info, appletList) {
|
foreach (const KPluginInfo &info, appletList) {
|
||||||
@ -1141,16 +1146,29 @@ void ContainmentPrivate::dropData(QGraphicsSceneEvent *event)
|
|||||||
|
|
||||||
actionsToPlugins.insert(action, info.pluginName());
|
actionsToPlugins.insert(action, info.pluginName());
|
||||||
}
|
}
|
||||||
|
|
||||||
actionsToPlugins.insert(choices.addAction(i18n("Icon")), "icon");
|
actionsToPlugins.insert(choices.addAction(i18n("Icon")), "icon");
|
||||||
|
|
||||||
QAction *choice = choices.exec(screenPos);
|
QAction *choice = choices.exec(screenPos);
|
||||||
if (choice) {
|
if (choice) {
|
||||||
q->addApplet(actionsToPlugins[choice], args, geom);
|
q->addApplet(actionsToPlugins[choice], args, geom);
|
||||||
}
|
}
|
||||||
} else if (url.protocol() != "data") {
|
|
||||||
// We don't try to do anything with data: URIs
|
} else if (url.protocol() != "data") { // Why not data:?
|
||||||
// no special applet associated with this mimetype, let's
|
//kDebug() << "Let's start a KIO::TransferJob to retrieve the mimetype" << KMimeType::findByUrl(url)->name();
|
||||||
q->addApplet("icon", args, geom);
|
|
||||||
|
|
||||||
|
// It may be a directory or a file, let's stat
|
||||||
|
KIO::JobFlags flags = KIO::HideProgressInfo;
|
||||||
|
KIO::TransferJob *job = KIO::get(url, KIO::NoReload, flags);
|
||||||
|
|
||||||
|
dropPoints[job] = dropEvent->scenePos();
|
||||||
|
QObject::connect(job, SIGNAL(mimetype(KIO::Job *, const QString&)),
|
||||||
|
q, SLOT(mimeTypeRetrieved(KIO::Job *, const QString&)));
|
||||||
|
|
||||||
|
QMenu *choices = new QMenu("Content dropped");
|
||||||
|
choices->addAction(KIcon("process-working"), i18n("Fetching file type..."));
|
||||||
|
choices->popup(dropEvent->screenPos());
|
||||||
|
dropMenus[job] = choices;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1174,6 +1192,7 @@ void ContainmentPrivate::dropData(QGraphicsSceneEvent *event)
|
|||||||
pluginFormats.insert(plugin.pluginName(), format);
|
pluginFormats.insert(plugin.pluginName(), format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
kDebug() << "Mimetype ..." << formats << seenPlugins.keys() << pluginFormats.values();
|
||||||
|
|
||||||
QString selectedPlugin;
|
QString selectedPlugin;
|
||||||
|
|
||||||
@ -1225,6 +1244,76 @@ void ContainmentPrivate::dropData(QGraphicsSceneEvent *event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ContainmentPrivate::mimeTypeRetrieved(KIO::Job * job, const QString &mimetype)
|
||||||
|
{
|
||||||
|
kDebug() << "Mimetype Job returns." << mimetype;
|
||||||
|
if (job->error()) {
|
||||||
|
// TODO: error feedback
|
||||||
|
kDebug() << "ERROR" << job->error() << ' ' << job->errorString();
|
||||||
|
} else {
|
||||||
|
KIO::TransferJob* tjob = dynamic_cast<KIO::TransferJob*>(job);
|
||||||
|
if (!tjob) {
|
||||||
|
kDebug() << "job should be a TransferJob, but isn't";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QPointF posi; // will be overwritten with the event's position
|
||||||
|
if (!dropPoints.keys().contains(tjob)) {
|
||||||
|
kDebug() << "Bailing out. Cannot find associated dropEvent related to the TransferJob";
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
posi = dropPoints[tjob];
|
||||||
|
kDebug() << "Received a suitable dropEvent at" << posi;
|
||||||
|
}
|
||||||
|
QMenu *choices = dropMenus[tjob];
|
||||||
|
if (!choices) {
|
||||||
|
kDebug() << "Bailing out. No QMenu found for this job.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QVariantList args;
|
||||||
|
args << tjob->url().url() << mimetype;
|
||||||
|
|
||||||
|
kDebug() << "Creating menu for:" << mimetype << posi << args;
|
||||||
|
KPluginInfo::List appletList = Applet::listAppletInfoForMimetype(mimetype);
|
||||||
|
if (!appletList.isEmpty()) {
|
||||||
|
choices->clear();
|
||||||
|
QHash<QAction *, QString> actionsToPlugins;
|
||||||
|
foreach (const KPluginInfo &info, appletList) {
|
||||||
|
kDebug() << info.name();
|
||||||
|
QAction *action;
|
||||||
|
if (!info.icon().isEmpty()) {
|
||||||
|
action = choices->addAction(KIcon(info.icon()), info.name());
|
||||||
|
} else {
|
||||||
|
action = choices->addAction(info.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
actionsToPlugins.insert(action, info.pluginName());
|
||||||
|
kDebug() << info.pluginName();
|
||||||
|
}
|
||||||
|
actionsToPlugins.insert(choices->addAction(i18n("Icon")), "icon");
|
||||||
|
|
||||||
|
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
|
||||||
|
tjob->putOnHold();
|
||||||
|
KIO::Scheduler::publishSlaveOnHold();
|
||||||
|
|
||||||
|
addApplet(actionsToPlugins[choice], args, QRectF(posi, QSize()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we can at least create an icon as a link to the URL
|
||||||
|
addApplet("icon", args, QRectF(posi, QSize()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// job is not needed anymore, clean it up, (we have already returned when an applet
|
||||||
|
// is created that might do something with the file now.
|
||||||
|
job->kill();
|
||||||
|
}
|
||||||
|
|
||||||
const QGraphicsItem *Containment::toolBoxItem() const
|
const QGraphicsItem *Containment::toolBoxItem() const
|
||||||
{
|
{
|
||||||
return d->toolBox;
|
return d->toolBox;
|
||||||
@ -1681,6 +1770,8 @@ void ContainmentPrivate::zoomIn()
|
|||||||
positionToolBox();
|
positionToolBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ContainmentPrivate::zoomOut()
|
void ContainmentPrivate::zoomOut()
|
||||||
{
|
{
|
||||||
emit q->zoomRequested(q, Plasma::ZoomOut);
|
emit q->zoomRequested(q, Plasma::ZoomOut);
|
||||||
|
@ -32,6 +32,11 @@
|
|||||||
#include <plasma/applet.h>
|
#include <plasma/applet.h>
|
||||||
#include <plasma/animator.h>
|
#include <plasma/animator.h>
|
||||||
|
|
||||||
|
namespace KIO
|
||||||
|
{
|
||||||
|
class Job;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Plasma
|
namespace Plasma
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -542,7 +547,7 @@ class PLASMA_EXPORT Containment : public Applet
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Q_PRIVATE_SLOT(d, void appletDestroyed(Plasma::Applet*))
|
Q_PRIVATE_SLOT(d, void appletDestroyed(Plasma::Applet*))
|
||||||
Q_PRIVATE_SLOT(d, void containmentAppletAnimationComplete(QGraphicsItem *item,
|
Q_PRIVATE_SLOT(d, void containmentAppletAnimationComplete(QGraphicsItem *,
|
||||||
Plasma::Animator::Animation anim))
|
Plasma::Animator::Animation anim))
|
||||||
Q_PRIVATE_SLOT(d, void triggerShowAddWidgets())
|
Q_PRIVATE_SLOT(d, void triggerShowAddWidgets())
|
||||||
Q_PRIVATE_SLOT(d, void handleDisappeared(AppletHandle *handle))
|
Q_PRIVATE_SLOT(d, void handleDisappeared(AppletHandle *handle))
|
||||||
@ -552,6 +557,11 @@ class PLASMA_EXPORT Containment : public Applet
|
|||||||
Q_PRIVATE_SLOT(d, void requestConfiguration())
|
Q_PRIVATE_SLOT(d, void requestConfiguration())
|
||||||
Q_PRIVATE_SLOT(d, void updateToolBoxVisibility())
|
Q_PRIVATE_SLOT(d, void updateToolBoxVisibility())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This slot is called when the 'stat' after a job event has finished.
|
||||||
|
*/
|
||||||
|
Q_PRIVATE_SLOT(d, void mimeTypeRetrieved(KIO::Job *, const QString &))
|
||||||
|
|
||||||
friend class Applet;
|
friend class Applet;
|
||||||
friend class AppletPrivate;
|
friend class AppletPrivate;
|
||||||
friend class CoronaPrivate;
|
friend class CoronaPrivate;
|
||||||
|
@ -80,6 +80,7 @@ public:
|
|||||||
void containmentAppletAnimationComplete(QGraphicsItem *item, Plasma::Animator::Animation anim);
|
void containmentAppletAnimationComplete(QGraphicsItem *item, Plasma::Animator::Animation anim);
|
||||||
void zoomIn();
|
void zoomIn();
|
||||||
void zoomOut();
|
void zoomOut();
|
||||||
|
void mimeTypeRetrieved(KIO::Job *job, const QString &mimetype);
|
||||||
void containmentActions(KMenu &desktopMenu);
|
void containmentActions(KMenu &desktopMenu);
|
||||||
void appletActions(KMenu &desktopMenu, Applet *applet, bool includeApplet);
|
void appletActions(KMenu &desktopMenu, Applet *applet, bool includeApplet);
|
||||||
bool showContextMenu(const QPointF &point, const QPoint &screenPos, bool includeApplet);
|
bool showContextMenu(const QPointF &point, const QPoint &screenPos, bool includeApplet);
|
||||||
@ -126,6 +127,9 @@ public:
|
|||||||
Containment::Type type;
|
Containment::Type type;
|
||||||
static bool s_positioning;
|
static bool s_positioning;
|
||||||
bool drawWallpaper;
|
bool drawWallpaper;
|
||||||
|
QHash<KIO::Job*, QPointF> dropPoints;
|
||||||
|
QHash<KIO::Job*, QMenu*> dropMenus;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // Plasma namespace
|
} // Plasma namespace
|
||||||
|
Loading…
Reference in New Issue
Block a user