diff --git a/src/scriptengines/qml/plasmoid/containmentinterface.cpp b/src/scriptengines/qml/plasmoid/containmentinterface.cpp index 599f5afbd..b90bada74 100644 --- a/src/scriptengines/qml/plasmoid/containmentinterface.cpp +++ b/src/scriptengines/qml/plasmoid/containmentinterface.cpp @@ -32,6 +32,15 @@ #include #include #include +#include +#include +#include + +#ifndef PLASMA_NO_KIO +#include "kio/jobclasses.h" // for KIO::JobFlags +#include "kio/job.h" +#include "kio/scheduler.h" +#endif #include #include @@ -153,6 +162,24 @@ QVariantList ContainmentInterface::availableScreenRegion(int id) const return regVal; } +void ContainmentInterface::addApplet(const QString &plugin, const QVariantList &args, const QPoint &pos) +{ + //HACK + //This is necessary to delay the appletAdded signal (of containmentInterface) AFTER the applet graphics object has been created + blockSignals(true); + Plasma::Applet *applet = containment()->createApplet(plugin, args); + + QObject *appletGraphicObject; + if (applet) { + appletGraphicObject = applet->property("graphicObject").value(); + } + + blockSignals(false); + + emit appletAdded(appletGraphicObject, pos.x(), pos.y()); + emit appletsChanged(); +} + void ContainmentInterface::processMimeData(QMimeData *mimeData, int x, int y) { if (!mimeData) { @@ -168,25 +195,270 @@ void ContainmentInterface::processMimeData(QMimeData *mimeData, int x, int y) const QStringList appletNames = data.split('\n', QString::SkipEmptyParts); foreach (const QString &appletName, appletNames) { qDebug() << "adding" << appletName; - QRectF geom(QPoint(x, y), QSize(0, 0)); - //HACK - //This is necessary to delay the appletAdded signal (of containmentInterface) AFTER the applet graphics object has been created - blockSignals(true); - Plasma::Applet *applet = containment()->createApplet(appletName); + addApplet(appletName, QVariantList(), QPoint(x, y)); + } + } else if (mimeData->hasUrls()) { + //TODO: collect the mimetypes of available script engines and offer + // to create widgets out of the matching URLs, if any + const QList urls = KUrlMimeData::urlsFromMimeData(mimeData); + foreach (const QUrl &url, urls) { - QObject *appletGraphicObject; - if (applet) { - appletGraphicObject = applet->property("graphicObject").value(); +#ifndef PLASMA_NO_KIO + KMimeType::Ptr mime = KMimeType::findByUrl(url); + QString mimeName = mime->name(); + QVariantList args; + args << url.url(); + qDebug() << "can decode" << mimeName << args; + + // It may be a directory or a file, let's stat + KIO::JobFlags flags = KIO::HideProgressInfo; + KIO::MimetypeJob *job = KIO::mimetype(url, flags); + m_dropPoints[job] = QPoint(x, y); + + + QObject::connect(job, SIGNAL(result(KJob*)), this, SLOT(dropJobResult(KJob*))); + QObject::connect(job, SIGNAL(mimetype(KIO::Job*,QString)), + this, SLOT(mimeTypeRetrieved(KIO::Job*,QString))); + + QMenu *choices = new QMenu("Content dropped"); + choices->addAction(QIcon::fromTheme("process-working"), i18n("Fetching file type...")); + choices->popup(window() ? window()->mapToGlobal(QPoint(x, y)) : QPoint(x, y)); + + + m_dropMenus[job] = choices; +#endif + } + + } else { + QStringList formats = mimeData->formats(); + QHash seenPlugins; + QHash pluginFormats; + + foreach (const QString &format, formats) { + KPluginInfo::List plugins = Plasma::PluginLoader::self()->listAppletInfoForMimeType(format); + + foreach (const KPluginInfo &plugin, plugins) { + if (seenPlugins.contains(plugin.pluginName())) { + continue; + } + + seenPlugins.insert(plugin.pluginName(), plugin); + pluginFormats.insert(plugin.pluginName(), format); + } + } + //qDebug() << "Mimetype ..." << formats << seenPlugins.keys() << pluginFormats.values(); + + QString selectedPlugin; + + if (seenPlugins.isEmpty()) { + // do nothing + } else if (seenPlugins.count() == 1) { + selectedPlugin = seenPlugins.constBegin().key(); + } else { + QMenu choices; + QHash actionsToPlugins; + foreach (const KPluginInfo &info, seenPlugins) { + QAction *action; + if (!info.icon().isEmpty()) { + action = choices.addAction(QIcon::fromTheme(info.icon()), info.name()); + } else { + action = choices.addAction(info.name()); + } + + actionsToPlugins.insert(action, info.pluginName()); } - blockSignals(false); + QAction *choice = choices.exec(window() ? window()->mapToGlobal(QPoint(x, y)) : QPoint(x, y)); + if (choice) { + selectedPlugin = actionsToPlugins[choice]; + } + } - emit appletAdded(appletGraphicObject, x, y); - emit appletsChanged(); + if (!selectedPlugin.isEmpty()) { + + KTemporaryFile tempFile; + if (mimeData && tempFile.open()) { + //TODO: what should we do with files after the applet is done with them?? + tempFile.setAutoRemove(false); + + { + QDataStream stream(&tempFile); + QByteArray data = mimeData->data(pluginFormats[selectedPlugin]); + stream.writeRawData(data, data.size()); + } + + QVariantList args; + args << tempFile.fileName(); + qDebug() << args; + tempFile.close(); + + addApplet(selectedPlugin, args, QPoint(x, y)); + } } } } +void ContainmentInterface::clearDataForMimeJob(KIO::Job *job) +{ +#ifndef PLASMA_NO_KIO + QObject::disconnect(job, 0, this, 0); + m_dropPoints.remove(job); + QMenu *choices = m_dropMenus.take(job); + delete choices; + job->kill(); +#endif // PLASMA_NO_KIO +} + +void ContainmentInterface::dropJobResult(KJob *job) +{ +#ifndef PLASMA_NO_KIO + KIO::TransferJob* tjob = dynamic_cast(job); + if (!tjob) { + qDebug() << "job is not a KIO::TransferJob, won't handle the drop..."; + clearDataForMimeJob(tjob); + return; + } + if (job->error()) { + qDebug() << "ERROR" << tjob->error() << ' ' << tjob->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 +} + +void ContainmentInterface::mimeTypeRetrieved(KIO::Job *job, const QString &mimetype) +{ +#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"; + clearDataForMimeJob(job); + return; + } + KPluginInfo::List appletList = Plasma::PluginLoader::self()->listAppletInfoForUrl(tjob->url()); + if (mimetype.isEmpty() && !appletList.count()) { + clearDataForMimeJob(job); + qDebug() << "No applets found matching the url (" << tjob->url() << ") or the mimetype (" << mimetype << ")"; + return; + } else { + + QPoint posi; // will be overwritten with the event's position + if (m_dropPoints.keys().contains(tjob)) { + posi = m_dropPoints[tjob]; + qDebug() << "Received a suitable dropEvent at" << posi; + } else { + qDebug() << "Bailing out. Cannot find associated dropEvent related to the TransferJob"; + clearDataForMimeJob(job); + return; + } + + QMenu *choices = m_dropMenus.value(tjob); + if (!choices) { + qDebug() << "Bailing out. No QMenu found for this job."; + clearDataForMimeJob(job); + return; + } + + QVariantList args; + args << tjob->url().url() << mimetype; + + qDebug() << "Creating menu for:" << mimetype << posi << args; + + appletList << Plasma::PluginLoader::self()->listAppletInfoForMimeType(mimetype); + KPluginInfo::List wallpaperList; + //TODO: how restore wallpaper dnd? + /*if (drawWallpaper) { + if (wallpaper && wallpaper->supportsMimetype(mimetype)) { + wallpaperList << wallpaper->d->wallpaperDescription; + } else { + wallpaperList = Wallpaper::listWallpaperInfoForMimetype(mimetype); + } + }*/ + + if (!appletList.isEmpty() || !wallpaperList.isEmpty()) { + choices->clear(); + 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()); + } else { + action = choices->addAction(info.name()); + } + + actionsToApplets.insert(action, info.pluginName()); + qDebug() << info.pluginName(); + } + actionsToApplets.insert(choices->addAction(i18n("Icon")), "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()); + } + } + + 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 (plugin.isEmpty()) { + //set wallpapery stuff + plugin = actionsToWallpapers.value(choice); + //TODO: wallpapers + /* + if (!wallpaper || plugin != wallpaper->pluginName()) { + qDebug() << "Wallpaper dropped:" << tjob->url(); + q->setWallpaper(plugin); + } + + if (wallpaper) { + qDebug() << "Wallpaper dropped:" << tjob->url(); + wallpaper->setUrls(KUrl::List() << tjob->url()); + }*/ + } else { + addApplet(actionsToApplets[choice], args, posi); + } + + clearDataForMimeJob(job); + return; + } + } else { + // we can at least create an icon as a link to the URL + addApplet("icon", args, posi); + } + } + + clearDataForMimeJob(job); +#endif // PLASMA_NO_KIO +} + + + void ContainmentInterface::appletAddedForward(Plasma::Applet *applet) { if (!applet) { diff --git a/src/scriptengines/qml/plasmoid/containmentinterface.h b/src/scriptengines/qml/plasmoid/containmentinterface.h index c04cc111b..81cc7ae83 100644 --- a/src/scriptengines/qml/plasmoid/containmentinterface.h +++ b/src/scriptengines/qml/plasmoid/containmentinterface.h @@ -30,6 +30,10 @@ class QmlObject; class WallpaperInterface; +namespace KIO { + class Job; +} + class ContainmentInterface : public AppletInterface { Q_OBJECT @@ -84,10 +88,17 @@ protected Q_SLOTS: void appletAddedForward(Plasma::Applet *applet); void appletRemovedForward(Plasma::Applet *applet); void loadWallpaper(); + void dropJobResult(KJob *job); + void mimeTypeRetrieved(KIO::Job *job, const QString &mimetype); private: + void clearDataForMimeJob(KIO::Job *job); + void addApplet(const QString &plugin, const QVariantList &args, const QPoint &pos); + WallpaperInterface *m_wallpaperInterface; QList m_appletInterfaces; + QHash m_dropPoints; + QHash m_dropMenus; }; #endif