KDE/kdelibs/plasma

Sebastian Kügler sebas at kde.org
Sun Aug 9 00:01:17 CEST 2009


SVN commit 1009004 by sebas:

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 at kde.org



 M  +102 -11   containment.cpp  
 M  +11 -1     containment.h  
 M  +4 -0      private/containment_p.h  


--- trunk/KDE/kdelibs/plasma/containment.cpp #1009003:1009004
@@ -43,6 +43,9 @@
 #include <kstandarddirs.h>
 #include <ktemporaryfile.h>
 #include <kwindowsystem.h>
+#include "kio/jobclasses.h" // for KIO::JobFlags
+#include "kio/job.h"
+#include "kio/scheduler.h"
 
 #include "animator.h"
 #include "context.h"
@@ -611,7 +614,7 @@
         //if there is only one, don't create a submenu
         if(enabled < 2) {
             foreach(QAction *action, containmentMenu->actions()) {
-                desktopMenu.addAction(action); 
+                desktopMenu.addAction(action);
             }
         } else {
             desktopMenu.addMenu(containmentMenu);
@@ -1058,6 +1061,7 @@
 
     QGraphicsSceneDragDropEvent *dropEvent = dynamic_cast<QGraphicsSceneDragDropEvent*>(event);
     QGraphicsSceneMouseEvent *mouseEvent = dynamic_cast<QGraphicsSceneMouseEvent*>(event);
+    //kDebug() << "Something dropped mimetype, -data: " << appletMimetype << event->mimeData()->text();
 
     QPointF pos;
     QPointF scenePos;
@@ -1081,7 +1085,7 @@
     }
 
     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";
         return;
     }
@@ -1093,7 +1097,6 @@
     if (!appletMimetype.isEmpty() && mimeData->hasFormat(appletMimetype)) {
         QString data = mimeData->data(appletMimetype);
         const QStringList appletNames = data.split('\n', QString::SkipEmptyParts);
-
         foreach (const QString &appletName, appletNames) {
             //kDebug() << "doing" << appletName;
             QRectF geom(q->mapFromScene(scenePos), QSize(0, 0));
@@ -1123,12 +1126,14 @@
             QRectF geom(pos, QSize());
             QVariantList args;
             args << url.url();
-            //             kDebug() << mimeName;
+            //kDebug() << "can decode" << mimeName << args;
+            //kDebug() << "protocol:" << url.protocol();
             KPluginInfo::List appletList = Applet::listAppletInfoForMimetype(mimeName);
 
             if (!appletList.isEmpty()) {
-                //TODO: should we show a dialog here to choose which plasmoid load if
-                //!appletList.isEmpty()
+                // The mimetype is known, i.e. there are applet that can load this mimetype
+                // Offer the applets in a popupmenu
+                kDebug() << "Local file.";
                 QMenu choices;
                 QHash<QAction *, QString> actionsToPlugins;
                 foreach (const KPluginInfo &info, appletList) {
@@ -1141,16 +1146,29 @@
 
                     actionsToPlugins.insert(action, info.pluginName());
                 }
+                actionsToPlugins.insert(choices.addAction(i18n("Icon")), "icon");
 
-                actionsToPlugins.insert(choices.addAction(i18n("Icon")), "icon");
                 QAction *choice = choices.exec(screenPos);
                 if (choice) {
                     q->addApplet(actionsToPlugins[choice], args, geom);
                 }
-            } else if (url.protocol() != "data") {
-                // We don't try to do anything with data: URIs
-                // no special applet associated with this mimetype, let's
-                q->addApplet("icon", args, geom);
+
+            } else if (url.protocol() != "data") { // Why not data:?
+                //kDebug() << "Let's start a KIO::TransferJob to retrieve the mimetype" << KMimeType::findByUrl(url)->name();
+
+
+                // 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 @@
                 pluginFormats.insert(plugin.pluginName(), format);
             }
         }
+        kDebug() << "Mimetype ..." << formats << seenPlugins.keys() << pluginFormats.values();
 
         QString selectedPlugin;
 
@@ -1225,6 +1244,76 @@
     }
 }
 
+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
 {
     return d->toolBox;
@@ -1681,6 +1770,8 @@
     positionToolBox();
 }
 
+
+
 void ContainmentPrivate::zoomOut()
 {
     emit q->zoomRequested(q, Plasma::ZoomOut);
--- trunk/KDE/kdelibs/plasma/containment.h #1009003:1009004
@@ -32,6 +32,11 @@
 #include <plasma/applet.h>
 #include <plasma/animator.h>
 
+namespace KIO
+{
+    class Job;
+}
+
 namespace Plasma
 {
 
@@ -542,7 +547,7 @@
 
     private:
         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))
         Q_PRIVATE_SLOT(d, void triggerShowAddWidgets())
         Q_PRIVATE_SLOT(d, void handleDisappeared(AppletHandle *handle))
@@ -552,6 +557,11 @@
         Q_PRIVATE_SLOT(d, void requestConfiguration())
         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 AppletPrivate;
         friend class CoronaPrivate;
--- trunk/KDE/kdelibs/plasma/private/containment_p.h #1009003:1009004
@@ -80,6 +80,7 @@
     void containmentAppletAnimationComplete(QGraphicsItem *item, Plasma::Animator::Animation anim);
     void zoomIn();
     void zoomOut();
+    void mimeTypeRetrieved(KIO::Job *job, const QString &mimetype);
     void containmentActions(KMenu &desktopMenu);
     void appletActions(KMenu &desktopMenu, Applet *applet, bool includeApplet);
     bool showContextMenu(const QPointF &point, const QPoint &screenPos, bool includeApplet);
@@ -126,6 +127,9 @@
     Containment::Type type;
     static bool s_positioning;
     bool drawWallpaper;
+    QHash<KIO::Job*, QPointF> dropPoints;
+    QHash<KIO::Job*, QMenu*> dropMenus;
+
 };
 
 } // Plasma namespace


More information about the Plasma-devel mailing list