From: Peter Penz Date: Sun, 4 Sep 2011 15:40:44 +0000 (+0200) Subject: Improved drag and drop support X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/commitdiff_plain/7a91492cff931c0c4e0d38dd0aee77d9dcb29373?ds=sidebyside Improved drag and drop support --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7826ee00f..62d59d990 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -49,6 +49,7 @@ set(dolphinprivate_LIB_SRCS views/dolphinremoteencoding.cpp views/dolphinviewactionhandler.cpp views/dolphinviewautoscroller.cpp + views/draganddrophelper.cpp views/folderexpander.cpp views/renamedialog.cpp views/tooltips/filemetadatatooltip.cpp diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index 33dc9979b..401ba218d 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -123,24 +123,6 @@ bool KFileItemModel::setData(int index, const QHash& value return false; } -int KFileItemModel::indexForKeyboardSearch(const QString& text, int startFromIndex) const -{ - startFromIndex = qMax(0, startFromIndex); - for (int i = startFromIndex; i < count(); i++) { - if (data(i)["name"].toString().startsWith(text, Qt::CaseInsensitive)) { - kDebug() << data(i)["name"].toString(); - return i; - } - } - for (int i = 0; i < startFromIndex; i++) { - if (data(i)["name"].toString().startsWith(text, Qt::CaseInsensitive)) { - kDebug() << data(i)["name"].toString(); - return i; - } - } - return -1; -} - bool KFileItemModel::supportsGrouping() const { return true; @@ -189,6 +171,30 @@ QMimeData* KFileItemModel::createMimeData(const QSet& indexes) const return data; } +int KFileItemModel::indexForKeyboardSearch(const QString& text, int startFromIndex) const +{ + startFromIndex = qMax(0, startFromIndex); + for (int i = startFromIndex; i < count(); i++) { + if (data(i)["name"].toString().startsWith(text, Qt::CaseInsensitive)) { + kDebug() << data(i)["name"].toString(); + return i; + } + } + for (int i = 0; i < startFromIndex; i++) { + if (data(i)["name"].toString().startsWith(text, Qt::CaseInsensitive)) { + kDebug() << data(i)["name"].toString(); + return i; + } + } + return -1; +} + +bool KFileItemModel::supportsDropping(int index) const +{ + const KFileItem item = fileItem(index); + return item.isNull() ? false : item.isDir(); +} + KFileItem KFileItemModel::fileItem(int index) const { if (index >= 0 && index < count()) { diff --git a/src/kitemviews/kfileitemmodel.h b/src/kitemviews/kfileitemmodel.h index 839d4e724..c70892dd3 100644 --- a/src/kitemviews/kfileitemmodel.h +++ b/src/kitemviews/kfileitemmodel.h @@ -54,10 +54,6 @@ public: virtual QHash data(int index) const; virtual bool setData(int index, const QHash &values); - /** - * @reimp - */ - virtual int indexForKeyboardSearch(const QString& text, int startFromIndex = 0) const; /** * @return True * @reimp @@ -73,6 +69,12 @@ public: /** @reimp */ virtual QMimeData* createMimeData(const QSet& indexes) const; + /** @reimp */ + virtual int indexForKeyboardSearch(const QString& text, int startFromIndex = 0) const; + + /** @reimp */ + virtual bool supportsDropping(int index) const; + /** * @return The file-item for the index \a index. If the index is in a valid * range it is assured that the file-item is not null. The runtime diff --git a/src/kitemviews/kitemlistcontroller.cpp b/src/kitemviews/kitemlistcontroller.cpp index 025249a34..ed23dd1f7 100644 --- a/src/kitemviews/kitemlistcontroller.cpp +++ b/src/kitemviews/kitemlistcontroller.cpp @@ -482,16 +482,43 @@ bool KItemListController::dragLeaveEvent(QGraphicsSceneDragDropEvent* event, con bool KItemListController::dragMoveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform) { - Q_UNUSED(event); Q_UNUSED(transform); + if (!m_model || !m_view) { + return false; + } + + KItemListWidget* oldHoveredWidget = hoveredWidget(); + KItemListWidget* newHoveredWidget = widgetForPos(event->pos()); + if (oldHoveredWidget != newHoveredWidget) { + if (oldHoveredWidget) { + oldHoveredWidget->setHovered(false); + emit itemUnhovered(oldHoveredWidget->index()); + } + + if (newHoveredWidget) { + const int index = newHoveredWidget->index(); + if (m_model->supportsDropping(index)) { + newHoveredWidget->setHovered(true); + } + emit itemHovered(index); + } + } + return false; } bool KItemListController::dropEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform) { - Q_UNUSED(event); - Q_UNUSED(transform); - return false; + Q_UNUSED(transform) + if (!m_view) { + return false; + } + + const QPointF pos = transform.map(event->pos()); + const int index = m_view->itemAt(pos); + emit itemDropEvent(index, event); + + return true; } bool KItemListController::hoverEnterEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform) @@ -503,46 +530,22 @@ bool KItemListController::hoverEnterEvent(QGraphicsSceneHoverEvent* event, const bool KItemListController::hoverMoveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform) { - // The implementation assumes that only one item can get hovered no matter - // whether they overlap or not. - Q_UNUSED(transform); if (!m_model || !m_view) { return false; } - // Search the previously hovered item that might get unhovered - KItemListWidget* unhoveredWidget = 0; - foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) { - if (widget->isHovered()) { - unhoveredWidget = widget; - break; + KItemListWidget* oldHoveredWidget = hoveredWidget(); + KItemListWidget* newHoveredWidget = widgetForPos(event->pos()); + if (oldHoveredWidget != newHoveredWidget) { + if (oldHoveredWidget) { + oldHoveredWidget->setHovered(false); + emit itemUnhovered(oldHoveredWidget->index()); } - } - // Search the currently hovered item - KItemListWidget* hoveredWidget = 0; - foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) { - const QPointF mappedPos = widget->mapFromItem(m_view, event->pos()); - - const bool hovered = widget->contains(mappedPos) && - !widget->expansionToggleRect().contains(mappedPos) && - !widget->selectionToggleRect().contains(mappedPos); - if (hovered) { - hoveredWidget = widget; - break; - } - } - - if (unhoveredWidget != hoveredWidget) { - if (unhoveredWidget) { - unhoveredWidget->setHovered(false); - emit itemUnhovered(unhoveredWidget->index()); - } - - if (hoveredWidget) { - hoveredWidget->setHovered(true); - emit itemHovered(hoveredWidget->index()); + if (newHoveredWidget) { + newHoveredWidget->setHovered(true); + emit itemHovered(newHoveredWidget->index()); } } @@ -755,4 +758,35 @@ void KItemListController::startDragging() drag->exec(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction, Qt::IgnoreAction); } +KItemListWidget* KItemListController::hoveredWidget() const +{ + Q_ASSERT(m_view); + + foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) { + if (widget->isHovered()) { + return widget; + } + } + + return 0; +} + +KItemListWidget* KItemListController::widgetForPos(const QPointF& pos) const +{ + Q_ASSERT(m_view); + + foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) { + const QPointF mappedPos = widget->mapFromItem(m_view, pos); + + const bool hovered = widget->contains(mappedPos) && + !widget->expansionToggleRect().contains(mappedPos) && + !widget->selectionToggleRect().contains(mappedPos); + if (hovered) { + return widget; + } + } + + return 0; +} + #include "kitemlistcontroller.moc" diff --git a/src/kitemviews/kitemlistcontroller.h b/src/kitemviews/kitemlistcontroller.h index 8ac2b554b..80c750581 100644 --- a/src/kitemviews/kitemlistcontroller.h +++ b/src/kitemviews/kitemlistcontroller.h @@ -34,6 +34,7 @@ class KItemModelBase; class KItemListKeyboardSearchManager; class KItemListSelectionManager; class KItemListView; +class KItemListWidget; class QGraphicsSceneHoverEvent; class QGraphicsSceneDragDropEvent; class QGraphicsSceneMouseEvent; @@ -113,6 +114,7 @@ signals: * Is emitted if the item with the index \p index gets hovered. */ void itemHovered(int index); + /** * Is emitted if the item with the index \p index gets unhovered. * It is assured that the signal itemHovered() for this index @@ -122,6 +124,13 @@ signals: void itemExpansionToggleClicked(int index); + /** + * Is emitted if a drop event is done above the item with the index + * \a index. If \a index is < 0 the drop event is done above an + * empty area of the view. + */ + void itemDropEvent(int index, QGraphicsSceneDragDropEvent* event); + void modelChanged(KItemModelBase* current, KItemModelBase* previous); void viewChanged(KItemListView* current, KItemListView* previous); @@ -142,6 +151,18 @@ private: */ void startDragging(); + /** + * @return Widget that is currently in the hovered state. 0 is returned + * if no widget is marked as hovered. + */ + KItemListWidget* hoveredWidget() const; + + /** + * @return Widget that is below the position \a pos. 0 is returned + * if no widget is below the position. + */ + KItemListWidget* widgetForPos(const QPointF& pos) const; + private: SelectionBehavior m_selectionBehavior; KItemModelBase* m_model; diff --git a/src/kitemviews/kitemmodelbase.cpp b/src/kitemviews/kitemmodelbase.cpp index 79766fae1..541f802e3 100644 --- a/src/kitemviews/kitemmodelbase.cpp +++ b/src/kitemviews/kitemmodelbase.cpp @@ -116,6 +116,12 @@ int KItemModelBase::indexForKeyboardSearch(const QString& text, int startFromInd return -1; } +bool KItemModelBase::supportsDropping(int index) const +{ + Q_UNUSED(index); + return false; +} + void KItemModelBase::onGroupRoleChanged(const QByteArray& current, const QByteArray& previous) { Q_UNUSED(current); diff --git a/src/kitemviews/kitemmodelbase.h b/src/kitemviews/kitemmodelbase.h index c4e046402..de6e1bb1d 100644 --- a/src/kitemviews/kitemmodelbase.h +++ b/src/kitemviews/kitemmodelbase.h @@ -123,6 +123,20 @@ public: * @param startFromIndex the index from which to start searching from */ virtual int indexForKeyboardSearch(const QString& text, int startFromIndex = 0) const; + + /** + * @return True, if the item with the index \a index basically supports dropping. + * Per default false is returned. + * + * The information is used only to give a visual feedback during a drag operation + * and not to decide whether a drop event gets emitted. It is it is still up to + * the receiver of KItemListController::itemDropEvent() to decide how to handle + * the drop event. + */ + // TODO: Should the MIME-data be passed too so that the model can do a more specific + // decision whether it accepts the drop? + virtual bool supportsDropping(int index) const; + signals: /** * Is emitted if one or more items have been inserted. Each item-range consists diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index 53171966c..200de7904 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -22,10 +22,12 @@ #include #include +#include #include +#include +#include #include #include -#include #include #include @@ -60,6 +62,7 @@ #include "dolphin_detailsmodesettings.h" #include "dolphin_generalsettings.h" #include "dolphinitemlistcontainer.h" +#include "draganddrophelper.h" #include "renamedialog.h" #include "settings/dolphinsettings.h" #include "viewmodecontroller.h" @@ -172,6 +175,7 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) : connect(controller, SIGNAL(itemExpansionToggleClicked(int)), this, SLOT(slotItemExpansionToggleClicked(int))); connect(controller, SIGNAL(itemHovered(int)), this, SLOT(slotItemHovered(int))); connect(controller, SIGNAL(itemUnhovered(int)), this, SLOT(slotItemUnhovered(int))); + connect(controller, SIGNAL(itemDropEvent(int,QGraphicsSceneDragDropEvent*)), this, SLOT(slotItemDropEvent(int,QGraphicsSceneDragDropEvent*))); KItemListSelectionManager* selectionManager = controller->selectionManager(); connect(selectionManager, SIGNAL(selectionChanged(QSet,QSet)), @@ -776,6 +780,19 @@ void DolphinView::slotItemUnhovered(int index) emit requestItemInfo(KFileItem()); } +void DolphinView::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event) +{ + const KFileItem destItem = fileItemModel()->fileItem(index); + + QDropEvent dropEvent(event->pos().toPoint(), + event->possibleActions(), + event->mimeData(), + event->buttons(), + event->modifiers()); + + DragAndDropHelper::dropUrls(destItem, url(), &dropEvent, this); +} + void DolphinView::slotSelectionChanged(const QSet& current, const QSet& previous) { const int currentCount = current.count(); diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h index 085de332d..49da948d6 100644 --- a/src/views/dolphinview.h +++ b/src/views/dolphinview.h @@ -18,7 +18,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ - #ifndef DOLPHINVIEW_H #define DOLPHINVIEW_H @@ -48,6 +47,7 @@ class KFileItemModel; class KUrl; class ToolTipManager; class ViewProperties; +class QGraphicsSceneDragDropEvent; class QRegExp; /** @@ -559,6 +559,7 @@ private slots: void slotItemExpansionToggleClicked(int index); void slotItemHovered(int index); void slotItemUnhovered(int index); + void slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event); /** * Emits the signal \a selectionChanged() with a small delay. This is diff --git a/src/views/draganddrophelper.cpp b/src/views/draganddrophelper.cpp index bc1361b9a..6cd17b6ba 100644 --- a/src/views/draganddrophelper.cpp +++ b/src/views/draganddrophelper.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2007 by Peter Penz * + * Copyright (C) 2007-2011 by Peter Penz * * Copyright (C) 2007 by David Faure * * * * This program is free software; you can redistribute it and/or modify * @@ -20,83 +20,25 @@ #include "draganddrophelper.h" -#include #include -#include #include #include - -#include "views/dolphiniconsview.h" -#include "views/dolphinviewcontroller.h" - -#include -#include +#include #include -#include -#include - -class DragAndDropHelperSingleton -{ -public: - DragAndDropHelper instance; -}; -K_GLOBAL_STATIC(DragAndDropHelperSingleton, s_dragAndDropHelper) - -DragAndDropHelper& DragAndDropHelper::instance() -{ - return s_dragAndDropHelper->instance; -} - -void DragAndDropHelper::startDrag(QAbstractItemView* itemView, - Qt::DropActions supportedActions, - DolphinViewController* dolphinViewController) -{ - // Do not start a new drag until the previous one has been finished. - // This is a (possibly temporary) fix for bug #187884. - static bool isDragging = false; - if (isDragging) { - return; - } - isDragging = true; - - const QModelIndexList indexes = itemView->selectionModel()->selectedIndexes(); - if (!indexes.isEmpty()) { - QMimeData *data = itemView->model()->mimeData(indexes); - if (!data) { - return; - } - - if (dolphinViewController) { - dolphinViewController->requestToolTipHiding(); - } - - QDrag* drag = new QDrag(itemView); - drag->setPixmap(createDragPixmap(itemView)); - drag->setMimeData(data); - - m_dragSource = itemView; - drag->exec(supportedActions, Qt::IgnoreAction); - m_dragSource = 0; - } - isDragging = false; -} - -bool DragAndDropHelper::isDragSource(QAbstractItemView* itemView) const -{ - return m_dragSource && (m_dragSource == itemView); -} +#include +#include -void DragAndDropHelper::dropUrls(const KFileItem& destItem, - const KUrl& destPath, - QDropEvent* event, - QWidget* widget) +QString DragAndDropHelper::dropUrls(const KFileItem& destItem, + const KUrl& destPath, + QDropEvent* event, + QWidget* widget) { const bool dropToItem = !destItem.isNull() && (destItem.isDir() || destItem.isDesktopFile()); const KUrl destination = dropToItem ? destItem.url() : destPath; const QMimeData* mimeData = event->mimeData(); if (mimeData->hasFormat("application/x-kde-dndextract")) { - QString remoteDBusClient = mimeData->data("application/x-kde-dndextract"); + const QString remoteDBusClient = mimeData->data("application/x-kde-dndextract"); QDBusMessage message = QDBusMessage::createMethodCall(remoteDBusClient, "/DndExtract", "org.kde.DndExtract", "extractSelectedFilesTo"); message.setArguments(QVariantList() << destination.pathOrUrl()); @@ -104,79 +46,15 @@ void DragAndDropHelper::dropUrls(const KFileItem& destItem, } else { const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData()); const int urlsCount = urls.count(); - if ((urlsCount == 1) && (urls.first() == destination)) { - emit errorMessage(i18nc("@info:status", "A folder cannot be dropped into itself")); + if (urlsCount == 1 && urls.first() == destination) { + return i18nc("@info:status", "A folder cannot be dropped into itself"); } else if (dropToItem) { KonqOperations::doDrop(destItem, destination, event, widget); } else { KonqOperations::doDrop(KFileItem(), destination, event, widget); } } -} - -DragAndDropHelper::DragAndDropHelper() - : m_dragSource(0) -{ -} - -QPixmap DragAndDropHelper::createDragPixmap(QAbstractItemView* itemView) const -{ - const QModelIndexList selectedIndexes = itemView->selectionModel()->selectedIndexes(); - Q_ASSERT(!selectedIndexes.isEmpty()); - - QAbstractProxyModel* proxyModel = static_cast(itemView->model()); - KDirModel* dirModel = static_cast(proxyModel->sourceModel()); - - const int itemCount = selectedIndexes.count(); - - // If more than one item is dragged, align the items inside a - // rectangular grid. The maximum grid size is limited to 5 x 5 items. - int xCount = 3; - int size = KIconLoader::SizeMedium; - if (itemCount > 16) { - xCount = 5; - size = KIconLoader::SizeSmall; - } else if (itemCount > 9) { - xCount = 4; - size = KIconLoader::SizeSmallMedium; - } - - if (itemCount < xCount) { - xCount = itemCount; - } - - int yCount = itemCount / xCount; - if (itemCount % xCount != 0) { - ++yCount; - } - if (yCount > xCount) { - yCount = xCount; - } - - // Draw the selected items into the grid cells - QPixmap dragPixmap(xCount * size + xCount - 1, yCount * size + yCount - 1); - dragPixmap.fill(Qt::transparent); - - QPainter painter(&dragPixmap); - int x = 0; - int y = 0; - foreach (const QModelIndex& selectedIndex, selectedIndexes) { - const QModelIndex index = proxyModel->mapToSource(selectedIndex); - const KFileItem item = dirModel->itemForIndex(index); - const QPixmap pixmap = item.pixmap(size, size); - painter.drawPixmap(x, y, pixmap); - - x += size + 1; - if (x >= dragPixmap.width()) { - x = 0; - y += size + 1; - } - if (y >= dragPixmap.height()) { - break; - } - } - return dragPixmap; + return QString(); } -#include "draganddrophelper.moc" diff --git a/src/views/draganddrophelper.h b/src/views/draganddrophelper.h index b107efc18..85e47077d 100644 --- a/src/views/draganddrophelper.h +++ b/src/views/draganddrophelper.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2007 by Peter Penz * + * Copyright (C) 2007-2011 by Peter Penz * * Copyright (C) 2007 by David Faure * * * * This program is free software; you can redistribute it and/or modify * @@ -22,44 +22,17 @@ #define DRAGANDDROPHELPER_H #include "libdolphin_export.h" -#include -#include -class DolphinViewController; +#include + class KFileItem; class KUrl; class QDropEvent; -class QAbstractItemView; -class QMimeData; class QWidget; -/** - * @brief Helper class for having a common drag and drop behavior. - * - * The class is used by DolphinIconsView, DolphinDetailsView, - * DolphinColumnView and PanelTreeView to have a consistent - * drag and drop behavior between all views. - */ -class LIBDOLPHINPRIVATE_EXPORT DragAndDropHelper : public QObject +class LIBDOLPHINPRIVATE_EXPORT DragAndDropHelper { - Q_OBJECT - public: - static DragAndDropHelper& instance(); - - /** - * Creates a drag object for the view \a itemView for all selected items. - */ - void startDrag(QAbstractItemView* itemView, - Qt::DropActions supportedActions, - DolphinViewController* dolphinViewController = 0); - - /** - * Returns true if and only if the view \a itemView was the last view to - * be passed to startDrag(...), and that drag is still in progress. - */ - bool isDragSource(QAbstractItemView* itemView) const; - /** * Handles the dropping of URLs to the given * destination. A context menu with the options @@ -69,28 +42,13 @@ public: * @param destPath Path of the destination. * @param event Drop event. * @param widget Source widget where the dragging has been started. + * @return Error message if dropping is not possible. If an empty string + * is returned, the dropping has been successful. */ - void dropUrls(const KFileItem& destItem, - const KUrl& destPath, - QDropEvent* event, - QWidget* widget); -signals: - void errorMessage(const QString& msg); - -private: - DragAndDropHelper(); - - /** - * Creates a pixmap the contains the all icons of the items - * that are dragged. - */ - QPixmap createDragPixmap(QAbstractItemView* itemView) const; - - // The last view passed in startDrag(...), or 0 if - // no startDrag(...) initiated drag is in progress. - QAbstractItemView *m_dragSource; - - friend class DragAndDropHelperSingleton; + static QString dropUrls(const KFileItem& destItem, + const KUrl& destPath, + QDropEvent* event, + QWidget* widget); }; #endif