mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-23 10:22:50 +00:00
333 lines
11 KiB
C++
333 lines
11 KiB
C++
/*
|
|
Copyright (c) 2009 Stephen Kelly <steveire@gmail.com>
|
|
|
|
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 "dragdropmanager_p.h"
|
|
#include "specialcollectionattribute_p.h"
|
|
#include "collectionutils_p.h"
|
|
|
|
#include <QApplication>
|
|
#include <QDropEvent>
|
|
#include <QMenu>
|
|
#include <QDrag>
|
|
|
|
#include <KDE/KIcon>
|
|
#include <KDE/KLocalizedString>
|
|
#include <KDE/KUrl>
|
|
|
|
#include "akonadi/collection.h"
|
|
#include "akonadi/entitytreemodel.h"
|
|
|
|
using namespace Akonadi;
|
|
|
|
DragDropManager::DragDropManager(QAbstractItemView *view)
|
|
: mShowDropActionMenu(true)
|
|
, mIsManualSortingActive(false)
|
|
, m_view(view)
|
|
{
|
|
}
|
|
|
|
Akonadi::Collection DragDropManager::currentDropTarget(QDropEvent *event) const
|
|
{
|
|
const QModelIndex index = m_view->indexAt(event->pos());
|
|
Collection collection = m_view->model()->data(index, EntityTreeModel::CollectionRole).value<Collection>();
|
|
if (!collection.isValid()) {
|
|
const Item item = m_view->model()->data(index, EntityTreeModel::ItemRole).value<Item>();
|
|
if (item.isValid()) {
|
|
collection = m_view->model()->data(index.parent(), EntityTreeModel::CollectionRole).value<Collection>();
|
|
}
|
|
}
|
|
|
|
return collection;
|
|
}
|
|
|
|
bool DragDropManager::dropAllowed(QDragMoveEvent *event) const
|
|
{
|
|
// Check if the collection under the cursor accepts this data type
|
|
const Collection targetCollection = currentDropTarget(event);
|
|
if (targetCollection.isValid()) {
|
|
const QStringList supportedContentTypes = targetCollection.contentMimeTypes();
|
|
|
|
const QMimeData *data = event->mimeData();
|
|
const KUrl::List urls = KUrl::List::fromMimeData(data);
|
|
foreach (const KUrl &url, urls) {
|
|
const Collection collection = Collection::fromUrl(url);
|
|
if (collection.isValid()) {
|
|
if (!supportedContentTypes.contains(Collection::mimeType()) &&
|
|
!supportedContentTypes.contains(Collection::virtualMimeType())) {
|
|
break;
|
|
}
|
|
|
|
// Check if we don't try to drop on one of the children
|
|
if (hasAncestor(m_view->indexAt(event->pos()), collection.id())) {
|
|
break;
|
|
}
|
|
} else { // This is an item.
|
|
const QString type = url.queryItems()[QString::fromLatin1("type")];
|
|
if (!supportedContentTypes.contains(type)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool DragDropManager::hasAncestor(const QModelIndex &_index, Collection::Id parentId) const
|
|
{
|
|
QModelIndex index(_index);
|
|
while (index.isValid()) {
|
|
if (m_view->model()->data(index, EntityTreeModel::CollectionIdRole).toLongLong() == parentId) {
|
|
return true;
|
|
}
|
|
|
|
index = index.parent();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool DragDropManager::processDropEvent(QDropEvent *event, bool &menuCanceled, bool dropOnItem)
|
|
{
|
|
const Collection targetCollection = currentDropTarget(event);
|
|
if (!targetCollection.isValid()) {
|
|
return false;
|
|
}
|
|
|
|
if (!mIsManualSortingActive && !dropOnItem) {
|
|
return false;
|
|
}
|
|
|
|
const QStringList supportedContentTypes = targetCollection.contentMimeTypes();
|
|
|
|
const QMimeData *data = event->mimeData();
|
|
const KUrl::List urls = KUrl::List::fromMimeData(data);
|
|
foreach (const KUrl &url, urls) {
|
|
const Collection collection = Collection::fromUrl(url);
|
|
if (!collection.isValid()) {
|
|
if (!dropOnItem) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
int actionCount = 0;
|
|
Qt::DropAction defaultAction;
|
|
// TODO check if the source supports moving
|
|
|
|
bool moveAllowed, copyAllowed, linkAllowed;
|
|
moveAllowed = copyAllowed = linkAllowed = false;
|
|
|
|
if ((targetCollection.rights() & (Collection::CanCreateCollection | Collection::CanCreateItem)) &&
|
|
(event->possibleActions() & Qt::MoveAction)) {
|
|
moveAllowed = true;
|
|
}
|
|
if ((targetCollection.rights() & (Collection::CanCreateCollection | Collection::CanCreateItem)) &&
|
|
(event->possibleActions() & Qt::CopyAction)) {
|
|
copyAllowed = true;
|
|
}
|
|
|
|
if ((targetCollection.rights() & Collection::CanLinkItem) &&
|
|
(event->possibleActions() & Qt::LinkAction)) {
|
|
linkAllowed = true;
|
|
}
|
|
|
|
if (mIsManualSortingActive && !dropOnItem) {
|
|
moveAllowed = true;
|
|
copyAllowed = false;
|
|
linkAllowed = false;
|
|
}
|
|
|
|
if (!moveAllowed && !copyAllowed && !linkAllowed) {
|
|
kDebug() << "Cannot drop here:" << event->possibleActions() << m_view->model()->supportedDragActions() << m_view->model()->supportedDropActions();
|
|
return false;
|
|
}
|
|
|
|
// first check whether the user pressed a modifier key to select a specific action
|
|
if ((QApplication::keyboardModifiers() & Qt::ControlModifier) &&
|
|
(QApplication::keyboardModifiers() & Qt::ShiftModifier)) {
|
|
if (linkAllowed) {
|
|
defaultAction = Qt::LinkAction;
|
|
actionCount = 1;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else if ((QApplication::keyboardModifiers() & Qt::ControlModifier)) {
|
|
if (copyAllowed) {
|
|
defaultAction = Qt::CopyAction;
|
|
actionCount = 1;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else if ((QApplication::keyboardModifiers() & Qt::ShiftModifier)) {
|
|
if (moveAllowed) {
|
|
defaultAction = Qt::MoveAction;
|
|
actionCount = 1;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (actionCount == 1) {
|
|
kDebug() << "Selecting drop action" << defaultAction << ", there are no other possibilities";
|
|
event->setDropAction(defaultAction);
|
|
return true;
|
|
}
|
|
|
|
if (!mShowDropActionMenu) {
|
|
if (moveAllowed) {
|
|
defaultAction = Qt::MoveAction;
|
|
} else if (copyAllowed) {
|
|
defaultAction = Qt::CopyAction;
|
|
} else if (linkAllowed) {
|
|
defaultAction = Qt::LinkAction;
|
|
} else {
|
|
return false;
|
|
}
|
|
event->setDropAction(defaultAction);
|
|
return true;
|
|
}
|
|
|
|
// otherwise show up a menu to allow the user to select an action
|
|
QMenu popup(m_view);
|
|
QAction *moveDropAction = 0;
|
|
QAction *copyDropAction = 0;
|
|
QAction *linkAction = 0;
|
|
QString sequence;
|
|
|
|
if (moveAllowed) {
|
|
sequence = QKeySequence(Qt::ShiftModifier).toString();
|
|
sequence.chop(1); // chop superfluous '+'
|
|
moveDropAction = popup.addAction(KIcon(QString::fromLatin1("go-jump")), i18n("&Move Here") + QLatin1Char('\t') + sequence);
|
|
}
|
|
|
|
if (copyAllowed) {
|
|
sequence = QKeySequence(Qt::ControlModifier).toString();
|
|
sequence.chop(1); // chop superfluous '+'
|
|
copyDropAction = popup.addAction(KIcon(QString::fromLatin1("edit-copy")), i18n("&Copy Here") + QLatin1Char('\t') + sequence);
|
|
}
|
|
|
|
if (linkAllowed) {
|
|
sequence = QKeySequence(Qt::ControlModifier + Qt::ShiftModifier).toString();
|
|
sequence.chop(1); // chop superfluous '+'
|
|
linkAction = popup.addAction(KIcon(QLatin1String("edit-link")), i18n("&Link Here") + QLatin1Char('\t') + sequence);
|
|
}
|
|
|
|
popup.addSeparator();
|
|
popup.addAction(KIcon(QString::fromLatin1("process-stop")), i18n("C&ancel") + QLatin1Char('\t') + QKeySequence(Qt::Key_Escape).toString());
|
|
|
|
QAction *activatedAction = popup.exec(QCursor::pos());
|
|
if (!activatedAction) {
|
|
menuCanceled = true;
|
|
return false;
|
|
} else if (activatedAction == moveDropAction) {
|
|
event->setDropAction(Qt::MoveAction);
|
|
} else if (activatedAction == copyDropAction) {
|
|
event->setDropAction(Qt::CopyAction);
|
|
} else if (activatedAction == linkAction) {
|
|
event->setDropAction(Qt::LinkAction);
|
|
} else {
|
|
menuCanceled = true;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void DragDropManager::startDrag(Qt::DropActions supportedActions)
|
|
{
|
|
QModelIndexList indexes;
|
|
bool sourceDeletable = true;
|
|
foreach (const QModelIndex &index, m_view->selectionModel()->selectedRows()) {
|
|
if (!m_view->model()->flags(index).testFlag(Qt::ItemIsDragEnabled)) {
|
|
continue;
|
|
}
|
|
|
|
if (sourceDeletable) {
|
|
Collection source = index.data(EntityTreeModel::CollectionRole).value<Collection>();
|
|
if (!source.isValid()) {
|
|
// index points to an item
|
|
source = index.data(EntityTreeModel::ParentCollectionRole).value<Collection>();
|
|
sourceDeletable = source.rights() & Collection::CanDeleteItem;
|
|
} else {
|
|
// index points to a collection
|
|
sourceDeletable = (source.rights() & Collection::CanDeleteCollection) && !source.hasAttribute<SpecialCollectionAttribute>() && !source.isVirtual();
|
|
}
|
|
}
|
|
indexes.append(index);
|
|
}
|
|
|
|
if (indexes.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
QMimeData *mimeData = m_view->model()->mimeData(indexes);
|
|
if (!mimeData) {
|
|
return;
|
|
}
|
|
|
|
QDrag *drag = new QDrag(m_view);
|
|
drag->setMimeData(mimeData);
|
|
if (indexes.size() > 1) {
|
|
drag->setPixmap(KIcon(QLatin1String("document-multiple")).pixmap(QSize(22, 22)));
|
|
} else {
|
|
QPixmap pixmap = indexes.first().data(Qt::DecorationRole).value<QIcon>().pixmap(QSize(22, 22));
|
|
if (pixmap.isNull()) {
|
|
pixmap = KIcon(QLatin1String("text-plain")).pixmap(QSize(22, 22));
|
|
}
|
|
drag->setPixmap(pixmap);
|
|
}
|
|
|
|
if (!sourceDeletable) {
|
|
supportedActions &= ~Qt::MoveAction;
|
|
}
|
|
|
|
Qt::DropAction defaultAction = Qt::IgnoreAction;
|
|
if ((QApplication::keyboardModifiers() & Qt::ControlModifier) &&
|
|
(QApplication::keyboardModifiers() & Qt::ShiftModifier)) {
|
|
defaultAction = Qt::LinkAction;
|
|
} else if ((QApplication::keyboardModifiers() & Qt::ControlModifier)) {
|
|
defaultAction = Qt::CopyAction;
|
|
} else if ((QApplication::keyboardModifiers() & Qt::ShiftModifier)) {
|
|
defaultAction = Qt::MoveAction;
|
|
}
|
|
|
|
drag->exec(supportedActions, defaultAction);
|
|
}
|
|
|
|
bool DragDropManager::showDropActionMenu() const
|
|
{
|
|
return mShowDropActionMenu;
|
|
}
|
|
|
|
void DragDropManager::setShowDropActionMenu(bool show)
|
|
{
|
|
mShowDropActionMenu = show;
|
|
}
|
|
|
|
bool DragDropManager::isManualSortingActive() const
|
|
{
|
|
return mIsManualSortingActive;
|
|
}
|
|
|
|
void DragDropManager::setManualSortingActive(bool active)
|
|
{
|
|
mIsManualSortingActive = active;
|
|
}
|