File indexing completed on 2025-01-19 03:50:34
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2009-04-16 0007 * Description : Qt Model for Albums - drag and drop handling 0008 * 0009 * SPDX-FileCopyrightText: 2005-2006 by Joern Ahrens <joern dot ahrens at kdemail dot net> 0010 * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * SPDX-FileCopyrightText: 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0012 * SPDX-FileCopyrightText: 2009 by Andi Clemens <andi dot clemens at gmail dot com> 0013 * SPDX-FileCopyrightText: 2015 by Mohamed_Anwer <m_dot_anwer at gmx dot com> 0014 * 0015 * SPDX-License-Identifier: GPL-2.0-or-later 0016 * 0017 * ============================================================ */ 0018 0019 #include "albumdragdrop.h" 0020 0021 // Qt includes 0022 0023 #include <QDropEvent> 0024 #include <QMenu> 0025 #include <QIcon> 0026 0027 // KDE includes 0028 0029 #include <klocalizedstring.h> 0030 0031 // Local includes 0032 0033 #include "digikam_debug.h" 0034 #include "albummanager.h" 0035 #include "albumpointer.h" 0036 #include "importui.h" 0037 #include "ddragobjects.h" 0038 #include "dio.h" 0039 #include "iteminfo.h" 0040 #include "iteminfolist.h" 0041 0042 namespace Digikam 0043 { 0044 0045 AlbumDragDropHandler::AlbumDragDropHandler(AlbumModel* const model) 0046 : AlbumModelDragDropHandler(model) 0047 { 0048 } 0049 0050 AlbumModel* AlbumDragDropHandler::model() const 0051 { 0052 return (static_cast<AlbumModel*>(m_model)); 0053 } 0054 0055 bool AlbumDragDropHandler::dropEvent(QAbstractItemView* view, 0056 const QDropEvent* e, 0057 const QModelIndex& droppedOn) 0058 { 0059 if (accepts(e, droppedOn) == Qt::IgnoreAction) 0060 { 0061 return false; 0062 } 0063 0064 AlbumPointer<PAlbum> destAlbum = model()->albumForIndex(droppedOn); 0065 0066 if (!destAlbum) 0067 { 0068 return false; 0069 } 0070 0071 if (DAlbumDrag::canDecode(e->mimeData())) 0072 { 0073 QList<QUrl> urls; 0074 int albumId = 0; 0075 0076 if (!DAlbumDrag::decode(e->mimeData(), urls, albumId)) 0077 { 0078 return false; 0079 } 0080 0081 AlbumPointer<PAlbum> droppedAlbum = AlbumManager::instance()->findPAlbum(albumId); 0082 0083 if (!droppedAlbum) 0084 { 0085 return false; 0086 } 0087 0088 QMenu popMenu(view); 0089 QAction* const moveAction = popMenu.addAction(QIcon::fromTheme(QLatin1String("go-jump")), i18n("&Move Here")); 0090 QAction* const copyAction = popMenu.addAction(QIcon::fromTheme(QLatin1String("edit-copy")), i18n("&Copy Here")); 0091 popMenu.addSeparator(); 0092 popMenu.addAction(QIcon::fromTheme(QLatin1String("dialog-cancel")), i18n("C&ancel")); 0093 popMenu.setMouseTracking(true); 0094 QAction* const choice = popMenu.exec(QCursor::pos()); 0095 0096 if (choice == moveAction) 0097 { 0098 DIO::move(droppedAlbum, destAlbum); 0099 } 0100 else if (choice == copyAction) 0101 { 0102 DIO::copy(droppedAlbum, destAlbum); 0103 } 0104 0105 return true; 0106 } 0107 else if (DItemDrag::canDecode(e->mimeData())) 0108 { 0109 0110 QList<QUrl> urls; 0111 QList<int> albumIDs; 0112 QList<qlonglong> imageIDs; 0113 0114 if (!DItemDrag::decode(e->mimeData(), urls, albumIDs, imageIDs)) 0115 { 0116 return false; 0117 } 0118 0119 if (urls.isEmpty() || albumIDs.isEmpty() || imageIDs.isEmpty()) 0120 { 0121 return false; 0122 } 0123 0124 // Check if items dropped come from outside current album. 0125 // This can be the case with recursive content album mode. 0126 0127 ItemInfoList extImgInfList; 0128 0129 for (QList<qlonglong>::const_iterator it = imageIDs.constBegin(); it != imageIDs.constEnd(); ++it) 0130 { 0131 ItemInfo info(*it); 0132 0133 if (info.albumId() != destAlbum->id()) 0134 { 0135 extImgInfList << info; 0136 } 0137 } 0138 0139 if (extImgInfList.isEmpty()) 0140 { 0141 // Setting the dropped image as the album thumbnail 0142 // If the ctrl key is pressed, when dropping the image, the 0143 // thumbnail is set without a popup menu 0144 0145 bool set = false; 0146 0147 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0148 0149 if (e->modifiers() == Qt::ControlModifier) 0150 0151 #else 0152 0153 if (e->keyboardModifiers() == Qt::ControlModifier) 0154 0155 #endif 0156 0157 { 0158 set = true; 0159 } 0160 else 0161 { 0162 QMenu popMenu(view); 0163 QAction* setAction = nullptr; 0164 0165 if (imageIDs.count() == 1) 0166 { 0167 setAction = popMenu.addAction(i18n("Set as Album Thumbnail")); 0168 } 0169 0170 popMenu.addSeparator(); 0171 popMenu.addAction(QIcon::fromTheme(QLatin1String("dialog-cancel")), i18n("C&ancel")); 0172 popMenu.setMouseTracking(true); 0173 QAction* const choice = popMenu.exec(QCursor::pos()); 0174 set = (setAction == choice); 0175 } 0176 0177 if (set && destAlbum) 0178 { 0179 QString errMsg; 0180 AlbumManager::instance()->updatePAlbumIcon(destAlbum, imageIDs.first(), errMsg); 0181 } 0182 0183 return true; 0184 } 0185 0186 bool ddMove = false; 0187 bool ddCopy = false; 0188 bool setThumbnail = false; 0189 0190 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0191 0192 if (e->modifiers() == Qt::ShiftModifier) 0193 0194 #else 0195 0196 if (e->keyboardModifiers() == Qt::ShiftModifier) 0197 0198 #endif 0199 0200 { 0201 // If shift key is pressed while dragging, move the drag object without 0202 // displaying popup menu -> move 0203 0204 ddMove = true; 0205 } 0206 0207 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0208 0209 else if (e->modifiers() == Qt::ControlModifier) 0210 0211 #else 0212 0213 else if (e->keyboardModifiers() == Qt::ControlModifier) 0214 0215 #endif 0216 0217 { 0218 // If ctrl key is pressed while dragging, copy the drag object without 0219 // displaying popup menu -> copy 0220 0221 ddCopy = true; 0222 } 0223 else 0224 { 0225 QMenu popMenu(view); 0226 QAction* const moveAction = popMenu.addAction(QIcon::fromTheme(QLatin1String("go-jump")), i18n("&Move Here")); 0227 QAction* const copyAction = popMenu.addAction(QIcon::fromTheme(QLatin1String("edit-copy")), i18n("&Copy Here")); 0228 QAction* thumbnailAction = nullptr; 0229 0230 if (imageIDs.count() == 1) 0231 { 0232 thumbnailAction = popMenu.addAction(i18n("Set as Album Thumbnail")); 0233 } 0234 0235 popMenu.addSeparator(); 0236 popMenu.addAction(QIcon::fromTheme(QLatin1String("dialog-cancel")), i18n("C&ancel")); 0237 popMenu.setMouseTracking(true); 0238 QAction* const choice = popMenu.exec(QCursor::pos()); 0239 0240 if (choice) 0241 { 0242 if (choice == moveAction) 0243 { 0244 ddMove = true; 0245 } 0246 else if (choice == copyAction) 0247 { 0248 ddCopy = true; 0249 } 0250 else if (choice == thumbnailAction) 0251 { 0252 setThumbnail = true; 0253 } 0254 } 0255 } 0256 0257 if (!destAlbum) 0258 { 0259 return false; 0260 } 0261 0262 if (ddMove) 0263 { 0264 DIO::move(extImgInfList, destAlbum); 0265 } 0266 else if (ddCopy) 0267 { 0268 DIO::copy(extImgInfList, destAlbum); 0269 } 0270 else if (setThumbnail) 0271 { 0272 QString errMsg; 0273 AlbumManager::instance()->updatePAlbumIcon(destAlbum, extImgInfList.first().id(), errMsg); 0274 } 0275 0276 return true; 0277 } 0278 0279 // -- DnD from Camera GUI ---------------------------- 0280 0281 else if (DCameraItemListDrag::canDecode(e->mimeData())) 0282 { 0283 ImportUI* const ui = dynamic_cast<ImportUI*>(e->source()); 0284 0285 if (ui) 0286 { 0287 QMenu popMenu(view); 0288 QAction* const downAction = popMenu.addAction(QIcon::fromTheme(QLatin1String("file-export")), i18n("Download From Camera")); 0289 QAction* const downDelAction = popMenu.addAction(QIcon::fromTheme(QLatin1String("file-export")), i18n("Download && Delete From Camera")); 0290 popMenu.addSeparator(); 0291 popMenu.addAction(QIcon::fromTheme(QLatin1String("dialog-cancel")), i18n("C&ancel")); 0292 popMenu.setMouseTracking(true); 0293 QAction* const choice = popMenu.exec(QCursor::pos()); 0294 0295 if (choice) 0296 { 0297 if (choice == downAction) 0298 { 0299 ui->slotDownload(true, false, destAlbum); 0300 } 0301 else if (choice == downDelAction) 0302 { 0303 ui->slotDownload(true, true, destAlbum); 0304 } 0305 } 0306 } 0307 } 0308 0309 // -- DnD from an external source --------------------- 0310 0311 else if (e->mimeData()->hasUrls()) 0312 { 0313 QList<QUrl> srcURLs = e->mimeData()->urls(); 0314 bool ddMove = false; 0315 bool ddCopy = false; 0316 0317 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0318 0319 if (e->modifiers() == Qt::ShiftModifier) 0320 0321 #else 0322 0323 if (e->keyboardModifiers() == Qt::ShiftModifier) 0324 0325 #endif 0326 0327 { 0328 // If shift key is pressed while dropping, move the drag object without 0329 // displaying popup menu -> move 0330 0331 ddMove = true; 0332 } 0333 0334 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0335 0336 else if (e->modifiers() == Qt::ControlModifier) 0337 0338 #else 0339 0340 else if (e->keyboardModifiers() == Qt::ControlModifier) 0341 0342 #endif 0343 0344 { 0345 // If ctrl key is pressed while dropping, copy the drag object without 0346 // displaying popup menu -> copy 0347 0348 ddCopy = true; 0349 } 0350 else 0351 { 0352 QMenu popMenu(view); 0353 QAction* const moveAction = popMenu.addAction(QIcon::fromTheme(QLatin1String("go-jump")), i18n("&Move Here")); 0354 QAction* const copyAction = popMenu.addAction(QIcon::fromTheme(QLatin1String("edit-copy")), i18n("&Copy Here")); 0355 popMenu.addSeparator(); 0356 popMenu.addAction(QIcon::fromTheme(QLatin1String("dialog-cancel")), i18n("C&ancel")); 0357 popMenu.setMouseTracking(true); 0358 QAction* const choice = popMenu.exec(QCursor::pos()); 0359 0360 if (choice == copyAction) 0361 { 0362 ddCopy = true; 0363 } 0364 else if (choice == moveAction) 0365 { 0366 ddMove = true; 0367 } 0368 } 0369 0370 if (ddMove) 0371 { 0372 DIO::move(srcURLs, destAlbum); 0373 } 0374 else if (ddCopy) 0375 { 0376 DIO::copy(srcURLs, destAlbum); 0377 } 0378 0379 return true; 0380 } 0381 0382 return false; 0383 } 0384 0385 Qt::DropAction AlbumDragDropHandler::accepts(const QDropEvent* e, const QModelIndex& dropIndex) 0386 { 0387 PAlbum* const destAlbum = model()->albumForIndex(dropIndex); 0388 0389 if (!destAlbum) 0390 { 0391 return Qt::IgnoreAction; 0392 } 0393 0394 // Dropping on root is not allowed and 0395 // Dropping on trash is not implemented yet 0396 0397 if (destAlbum->isRoot() || destAlbum->isTrashAlbum()) 0398 { 0399 return Qt::IgnoreAction; 0400 } 0401 0402 if (DAlbumDrag::canDecode(e->mimeData())) 0403 { 0404 QList<QUrl> urls; 0405 int albumId = 0; 0406 0407 if (!DAlbumDrag::decode(e->mimeData(), urls, albumId)) 0408 { 0409 return Qt::IgnoreAction; 0410 } 0411 0412 PAlbum* const droppedAlbum = AlbumManager::instance()->findPAlbum(albumId); 0413 0414 if (!droppedAlbum) 0415 { 0416 return Qt::IgnoreAction; 0417 } 0418 0419 // Dragging an item on itself makes no sense 0420 0421 if (droppedAlbum == destAlbum) 0422 { 0423 return Qt::IgnoreAction; 0424 } 0425 0426 // Dragging a parent on its child makes no sense 0427 0428 if (droppedAlbum->isAncestorOf(destAlbum)) 0429 { 0430 return Qt::IgnoreAction; 0431 } 0432 0433 return Qt::MoveAction; 0434 } 0435 else if (DItemDrag::canDecode(e->mimeData()) || 0436 DCameraItemListDrag::canDecode(e->mimeData()) || 0437 e->mimeData()->hasUrls()) 0438 { 0439 return Qt::MoveAction; 0440 } 0441 0442 return Qt::IgnoreAction; 0443 } 0444 0445 QStringList AlbumDragDropHandler::mimeTypes() const 0446 { 0447 QStringList mimeTypes; 0448 0449 mimeTypes << DAlbumDrag::mimeTypes() 0450 << DItemDrag::mimeTypes() 0451 << DCameraItemListDrag::mimeTypes() 0452 << QLatin1String("text/uri-list"); 0453 0454 return mimeTypes; 0455 } 0456 0457 QMimeData* AlbumDragDropHandler::createMimeData(const QList<Album*>& albums) 0458 { 0459 if (albums.isEmpty()) 0460 { 0461 return nullptr; 0462 } 0463 0464 if (albums.size() > 1) 0465 { 0466 qCWarning(DIGIKAM_GENERAL_LOG) << "Dragging multiple albums is not implemented"; 0467 } 0468 0469 PAlbum* const palbum = dynamic_cast<PAlbum*>(albums.first()); 0470 0471 // Root or album root and Trash Albums are not draggable 0472 0473 if (!palbum || palbum->isRoot() || palbum->isAlbumRoot() || palbum->isTrashAlbum()) 0474 { 0475 return nullptr; 0476 } 0477 0478 return (new DAlbumDrag(albums.first()->databaseUrl(), albums.first()->id(), palbum->fileUrl())); 0479 } 0480 0481 } // namespace Digikam 0482 0483 #include "moc_albumdragdrop.cpp"