mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-23 10:22:50 +00:00
343 lines
11 KiB
C++
343 lines
11 KiB
C++
/*
|
|
Copyright (c) 2008 Volker Krause <vkrause@kde.org>
|
|
|
|
This library is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU Library General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or (at your
|
|
option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
|
|
License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to the
|
|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
02110-1301, USA.
|
|
*/
|
|
|
|
#include "pastehelper_p.h"
|
|
|
|
#include "collectioncopyjob.h"
|
|
#include "collectionmovejob.h"
|
|
#include "collectionfetchjob.h"
|
|
#include "item.h"
|
|
#include "itemcreatejob.h"
|
|
#include "itemcopyjob.h"
|
|
#include "itemmodifyjob.h"
|
|
#include "itemmovejob.h"
|
|
#include "linkjob.h"
|
|
#include "transactionsequence.h"
|
|
#include "session.h"
|
|
#include "unlinkjob.h"
|
|
|
|
#include <KDebug>
|
|
#include <KUrl>
|
|
|
|
#include <QtCore/QByteArray>
|
|
#include <QtCore/QMimeData>
|
|
#include <QtCore/QStringList>
|
|
#include <QtCore/QMutexLocker>
|
|
|
|
#include <boost/bind.hpp>
|
|
|
|
using namespace Akonadi;
|
|
|
|
class PasteHelperJob: public Akonadi::TransactionSequence
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
explicit PasteHelperJob(Qt::DropAction action, const Akonadi::Item::List &items,
|
|
const Akonadi::Collection::List &collections,
|
|
const Akonadi::Collection &destination,
|
|
QObject *parent = 0);
|
|
virtual ~PasteHelperJob();
|
|
|
|
private Q_SLOTS:
|
|
void onDragSourceCollectionFetched(KJob *job);
|
|
|
|
private:
|
|
void runActions();
|
|
void runItemsActions();
|
|
void runCollectionsActions();
|
|
|
|
private:
|
|
Qt::DropAction mAction;
|
|
Akonadi::Item::List mItems;
|
|
Akonadi::Collection::List mCollections;
|
|
Akonadi::Collection mDestCollection;
|
|
};
|
|
|
|
PasteHelperJob::PasteHelperJob(Qt::DropAction action, const Item::List &items,
|
|
const Collection::List &collections,
|
|
const Collection &destination,
|
|
QObject *parent)
|
|
: TransactionSequence(parent)
|
|
, mAction(action)
|
|
, mItems(items)
|
|
, mCollections(collections)
|
|
, mDestCollection(destination)
|
|
{
|
|
//FIXME: The below code disables transactions in otder to avoid data loss due to nested
|
|
//transactions (copy and colcopy in the server doesn't see the items retrieved into the cache and copies empty payloads).
|
|
//Remove once this is fixed properly, see the other FIXME comments.
|
|
setProperty("transactionsDisabled", true);
|
|
|
|
Collection dragSourceCollection;
|
|
if (!items.isEmpty() && items.first().parentCollection().isValid()) {
|
|
// Check if all items have the same parent collection ID
|
|
const Collection parent = items.first().parentCollection();
|
|
if (std::find_if(items.constBegin(), items.constEnd(),
|
|
boost::bind(&Entity::operator!=, boost::bind(static_cast<Collection (Item::*)() const>(&Item::parentCollection), _1), parent))
|
|
== items.constEnd())
|
|
{
|
|
dragSourceCollection = parent;
|
|
}
|
|
}
|
|
|
|
kDebug() << items.first().parentCollection().id() << dragSourceCollection.id();
|
|
|
|
if (dragSourceCollection.isValid()) {
|
|
// Disable autocommitting, because starting a Link/Unlink/Copy/Move job
|
|
// after the transaction has ended leaves the job hanging
|
|
setAutomaticCommittingEnabled(false);
|
|
|
|
CollectionFetchJob *fetch = new CollectionFetchJob(dragSourceCollection,
|
|
CollectionFetchJob::Base,
|
|
this);
|
|
QObject::connect(fetch, SIGNAL(finished(KJob*)),
|
|
this, SLOT(onDragSourceCollectionFetched(KJob*)));
|
|
} else {
|
|
runActions();
|
|
}
|
|
}
|
|
|
|
PasteHelperJob::~PasteHelperJob()
|
|
{
|
|
}
|
|
|
|
void PasteHelperJob::onDragSourceCollectionFetched(KJob *job)
|
|
{
|
|
CollectionFetchJob *fetch = qobject_cast<CollectionFetchJob*>(job);
|
|
kDebug() << fetch->error() << fetch->collections().count();
|
|
if (fetch->error() || fetch->collections().count() != 1) {
|
|
runActions();
|
|
commit();
|
|
return;
|
|
}
|
|
|
|
|
|
// If the source collection is virtual, treat copy and move actions differently
|
|
const Collection sourceCollection = fetch->collections().first();
|
|
kDebug() << "FROM: " << sourceCollection.id() << sourceCollection.name() << sourceCollection.isVirtual();
|
|
kDebug() << "DEST: " << mDestCollection.id() << mDestCollection.name() << mDestCollection.isVirtual();
|
|
kDebug() << "ACTN:" << mAction;
|
|
if (sourceCollection.isVirtual()) {
|
|
switch (mAction) {
|
|
case Qt::CopyAction:
|
|
if (mDestCollection.isVirtual()) {
|
|
new LinkJob(mDestCollection, mItems, this);
|
|
} else {
|
|
new ItemCopyJob(mItems, mDestCollection, this);
|
|
}
|
|
break;
|
|
case Qt::MoveAction:
|
|
new UnlinkJob(sourceCollection, mItems, this);
|
|
if (mDestCollection.isVirtual()) {
|
|
new LinkJob(mDestCollection, mItems, this);
|
|
} else {
|
|
new ItemCopyJob(mItems, mDestCollection, this);
|
|
}
|
|
break;
|
|
case Qt::LinkAction:
|
|
new LinkJob(mDestCollection, mItems, this);
|
|
break;
|
|
default:
|
|
Q_ASSERT(false);
|
|
}
|
|
runCollectionsActions();
|
|
commit();
|
|
} else {
|
|
runActions();
|
|
}
|
|
|
|
commit();
|
|
}
|
|
|
|
void PasteHelperJob::runActions()
|
|
{
|
|
runItemsActions();
|
|
runCollectionsActions();
|
|
}
|
|
|
|
void PasteHelperJob::runItemsActions()
|
|
{
|
|
if (mItems.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
switch (mAction) {
|
|
case Qt::CopyAction:
|
|
new ItemCopyJob(mItems, mDestCollection, this);
|
|
break;
|
|
case Qt::MoveAction:
|
|
new ItemMoveJob(mItems, mDestCollection, this);
|
|
break;
|
|
case Qt::LinkAction:
|
|
new LinkJob(mDestCollection, mItems, this);
|
|
break;
|
|
default:
|
|
Q_ASSERT(false); // WTF?!
|
|
}
|
|
}
|
|
|
|
void PasteHelperJob::runCollectionsActions()
|
|
{
|
|
if (mCollections.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
switch (mAction) {
|
|
case Qt::CopyAction:
|
|
foreach (const Collection &col, mCollections) { // FIXME: remove once we have a batch job for collections as well
|
|
new CollectionCopyJob(col, mDestCollection, this);
|
|
}
|
|
break;
|
|
case Qt::MoveAction:
|
|
foreach (const Collection &col, mCollections) { // FIXME: remove once we have a batch job for collections as well
|
|
new CollectionMoveJob(col, mDestCollection, this);
|
|
}
|
|
break;
|
|
case Qt::LinkAction:
|
|
// Not supported for collections
|
|
break;
|
|
default:
|
|
Q_ASSERT(false); // WTF?!
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool PasteHelper::canPaste(const QMimeData *mimeData, const Collection &collection)
|
|
{
|
|
if (!mimeData || !collection.isValid()) {
|
|
return false;
|
|
}
|
|
|
|
// check that the target collection has the rights to
|
|
// create the pasted items resp. collections
|
|
Collection::Rights neededRights = Collection::ReadOnly;
|
|
if (KUrl::List::canDecode(mimeData)) {
|
|
const KUrl::List urls = KUrl::List::fromMimeData(mimeData);
|
|
foreach (const KUrl &url, urls) {
|
|
if (url.hasQueryItem(QLatin1String("item"))) {
|
|
neededRights |= Collection::CanCreateItem;
|
|
} else if (url.hasQueryItem(QLatin1String("collection"))) {
|
|
neededRights |= Collection::CanCreateCollection;
|
|
}
|
|
}
|
|
|
|
if ((collection.rights() & neededRights) == 0) {
|
|
return false;
|
|
}
|
|
|
|
// check that the target collection supports the mime types of the
|
|
// items/collections that shall be pasted
|
|
bool supportsMimeTypes = true;
|
|
foreach (const KUrl &url, urls) {
|
|
// collections do not provide mimetype information, so ignore this check
|
|
if (url.hasQueryItem(QLatin1String("collection"))) {
|
|
continue;
|
|
}
|
|
|
|
const QString mimeType = url.queryItemValue(QLatin1String("type"));
|
|
if (!collection.contentMimeTypes().contains(mimeType)) {
|
|
supportsMimeTypes = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!supportsMimeTypes) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
KJob *PasteHelper::paste(const QMimeData *mimeData, const Collection &collection, bool copy, Session *session)
|
|
{
|
|
if (!canPaste(mimeData, collection)) {
|
|
return 0;
|
|
}
|
|
|
|
// we try to drop data not coming with the akonadi:// url
|
|
// find a type the target collection supports
|
|
foreach (const QString &type, mimeData->formats()) {
|
|
if (!collection.contentMimeTypes().contains(type)) {
|
|
continue;
|
|
}
|
|
|
|
QByteArray item = mimeData->data(type);
|
|
// HACK for some unknown reason the data is sometimes 0-terminated...
|
|
if (!item.isEmpty() && item.at(item.size() - 1) == 0) {
|
|
item.resize(item.size() - 1);
|
|
}
|
|
|
|
Item it;
|
|
it.setMimeType(type);
|
|
it.setPayloadFromData(item);
|
|
|
|
ItemCreateJob *job = new ItemCreateJob(it, collection);
|
|
return job;
|
|
}
|
|
|
|
if (!KUrl::List::canDecode(mimeData)) {
|
|
return 0;
|
|
}
|
|
|
|
// data contains an url list
|
|
return pasteUriList(mimeData, collection, copy ? Qt::CopyAction : Qt::MoveAction, session);
|
|
}
|
|
|
|
KJob *PasteHelper::pasteUriList(const QMimeData *mimeData, const Collection &destination, Qt::DropAction action, Session *session)
|
|
{
|
|
if (!KUrl::List::canDecode(mimeData)) {
|
|
return 0;
|
|
}
|
|
|
|
if (!canPaste(mimeData, destination)) {
|
|
return 0;
|
|
}
|
|
|
|
const KUrl::List urls = KUrl::List::fromMimeData(mimeData);
|
|
Collection::List collections;
|
|
Item::List items;
|
|
foreach (const KUrl &url, urls) {
|
|
const Collection collection = Collection::fromUrl(url);
|
|
if (collection.isValid()) {
|
|
collections.append(collection);
|
|
}
|
|
Item item = Item::fromUrl(url);
|
|
if (url.hasQueryItem(QLatin1String("parent"))) {
|
|
item.setParentCollection(Collection(url.queryItem(QLatin1String("parent")).toLongLong()));
|
|
}
|
|
if (item.isValid()) {
|
|
items.append(item);
|
|
}
|
|
// TODO: handle non Akonadi URLs?
|
|
}
|
|
|
|
|
|
PasteHelperJob *job = new PasteHelperJob(action, items,
|
|
collections, destination,
|
|
session);
|
|
|
|
return job;
|
|
}
|
|
|
|
#include "pastehelper.moc"
|