Index: containments/desktop/plugins/folder/foldermodel.cpp =================================================================== --- containments/desktop/plugins/folder/foldermodel.cpp +++ containments/desktop/plugins/folder/foldermodel.cpp @@ -149,25 +149,23 @@ /* * position dropped items at the desired target position - * delay this via queued connection, such that the row is available and can be mapped - * when we emit the move request * * TODO: push this somehow into the Positioner */ - connect(m_dirModel, &QAbstractItemModel::rowsInserted, + connect(this, &QAbstractItemModel::rowsInserted, this, [this](const QModelIndex &parent, int first, int last) { for (int i = first; i <= last; ++i) { - const auto index = m_dirModel->index(i, 0, parent); - const auto url = m_dirModel->itemForIndex(index).url(); + const auto idx = index(i, 0, parent); + const auto url = itemForIndex(idx).url(); auto it = m_dropTargetPositions.find(url.fileName()); if (it != m_dropTargetPositions.end()) { const auto pos = it.value(); m_dropTargetPositions.erase(it); setSortMode(-1); emit move(pos.x(), pos.y(), {url}); } } - }, Qt::QueuedConnection); + }); /* * Dropped files may not actually show up as new files, e.g. when we overwrite * an existing file. Or files that fail to be listed by the dirLister, or... @@ -959,6 +957,15 @@ } } +static bool isDropBetweenSharedViews(const QList &urls, const QUrl &folderUrl) +{ + for (const auto &url : urls) { + if (!folderUrl.isParentOf(url)) + return false; + } + return true; +} + void FolderModel::drop(QQuickItem *target, QObject* dropEvent, int row) { QMimeData *mimeData = qobject_cast(dropEvent->property("mimeData").value()); @@ -1033,6 +1040,38 @@ } + auto dropTargetFolderUrl = dropTargetUrl; + if (dropTargetFolderUrl.fileName() == QLatin1String(".")) { + // the target URL for desktop:/ is e.g. 'file://home/user/Desktop/.' + dropTargetFolderUrl = dropTargetFolderUrl.adjusted(QUrl::RemoveFilename); + } + + // use dropTargetUrl to resolve desktop:/ to the actual file location which is also used by the mime data + if (isDropBetweenSharedViews(mimeData->urls(), dropTargetFolderUrl)) { + /* QMimeData operates on local URLs, but the dir lister and thus screen mapper and positioner may + * use a fancy scheme like desktop:/ instead. Ensure we always use the latter to properly map URLs, + * i.e. go from file:///home/user/Desktop/file to desktop:/file + */ + auto mappableUrl = [this, dropTargetFolderUrl](const QUrl &url) -> QString { + QString mappedUrl = url.toString(); + if (dropTargetFolderUrl != m_dirModel->dirLister()->url()) { + const auto local = dropTargetFolderUrl.toString(); + const auto internal = m_dirModel->dirLister()->url().toString(); + if (mappedUrl.startsWith(local)) { + mappedUrl.replace(0, local.size(), internal); + } + } + return mappedUrl; + }; + setSortMode(-1); + for (const auto &url : mimeData->urls()) { + m_dropTargetPositions.insert(url.fileName(), dropPos); + m_screenMapper->addMapping(mappableUrl(url), m_screen, ScreenMapper::DelayedSignal); + } + m_dropTargetPositionsCleanup->start(); + return; + } + Qt::DropAction proposedAction((Qt::DropAction)dropEvent->property("proposedAction").toInt()); Qt::DropActions possibleActions(dropEvent->property("possibleActions").toInt()); Qt::MouseButtons buttons(dropEvent->property("buttons").toInt());