File indexing completed on 2025-01-05 03:56:11
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2009-04-26 0007 * Description : Qt Model for Images - drag and drop handling 0008 * 0009 * SPDX-FileCopyrightText: 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "dragdropimplementations.h" 0016 0017 // Qt includes 0018 0019 #include <QApplication> 0020 #include <QClipboard> 0021 #include <QCursor> 0022 #include <QDrag> 0023 #include <QDropEvent> 0024 #include <QMimeData> 0025 0026 // Local includes 0027 0028 #include "digikam_config.h" 0029 0030 namespace Digikam 0031 { 0032 0033 // ------------ Model sample implementation ------------- 0034 0035 DragDropModelImplementation::DragDropModelImplementation() 0036 : m_dragDropHandler(nullptr) 0037 { 0038 } 0039 0040 DragDropModelImplementation::~DragDropModelImplementation() 0041 { 0042 } 0043 0044 Qt::ItemFlags DragDropModelImplementation::dragDropFlags(const QModelIndex& index) const 0045 { 0046 Q_UNUSED(index); 0047 0048 if (!m_dragDropHandler) 0049 { 0050 return Qt::NoItemFlags; 0051 } 0052 0053 return (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled); 0054 } 0055 0056 Qt::ItemFlags DragDropModelImplementation::dragDropFlagsV2(const QModelIndex& index) const 0057 { 0058 Qt::ItemFlags flags; 0059 0060 if (isDragEnabled(index)) 0061 { 0062 flags |= Qt::ItemIsDragEnabled; 0063 } 0064 0065 if (isDropEnabled(index)) 0066 { 0067 flags |= Qt::ItemIsDropEnabled; 0068 } 0069 0070 return flags; 0071 } 0072 0073 bool DragDropModelImplementation::isDragEnabled(const QModelIndex& index) const 0074 { 0075 Q_UNUSED(index); 0076 0077 return true; 0078 } 0079 0080 bool DragDropModelImplementation::isDropEnabled(const QModelIndex& index) const 0081 { 0082 Q_UNUSED(index); 0083 0084 return true; 0085 } 0086 0087 Qt::DropActions DragDropModelImplementation::supportedDropActions() const 0088 { 0089 return Qt::CopyAction|Qt::MoveAction; 0090 } 0091 0092 QStringList DragDropModelImplementation::mimeTypes() const 0093 { 0094 if (m_dragDropHandler) 0095 { 0096 return m_dragDropHandler->mimeTypes(); 0097 } 0098 0099 return QStringList(); 0100 } 0101 0102 bool DragDropModelImplementation::dropMimeData(const QMimeData*, Qt::DropAction, int, int, const QModelIndex&) 0103 { 0104 // we require custom solutions 0105 0106 return false; 0107 } 0108 0109 QMimeData* DragDropModelImplementation::mimeData(const QModelIndexList& indexes) const 0110 { 0111 if (!m_dragDropHandler) 0112 { 0113 return nullptr; 0114 } 0115 0116 return m_dragDropHandler->createMimeData(indexes); 0117 } 0118 0119 void DragDropModelImplementation::setDragDropHandler(AbstractItemDragDropHandler* handler) 0120 { 0121 m_dragDropHandler = handler; 0122 } 0123 0124 AbstractItemDragDropHandler* DragDropModelImplementation::dragDropHandler() const 0125 { 0126 return m_dragDropHandler; 0127 } 0128 0129 // ------------ View sample implementation ------------- 0130 0131 void DragDropViewImplementation::cut() 0132 { 0133 QMimeData* const data = asView()->model()->mimeData(asView()->selectionModel()->selectedIndexes()); 0134 0135 if (data) 0136 { 0137 encodeIsCutSelection(data, true); 0138 qApp->clipboard()->setMimeData(data); 0139 } 0140 } 0141 0142 void DragDropViewImplementation::copy() 0143 { 0144 QMimeData* const data = asView()->model()->mimeData(asView()->selectionModel()->selectedIndexes()); 0145 0146 if (data) 0147 { 0148 encodeIsCutSelection(data, false); 0149 qApp->clipboard()->setMimeData(data); 0150 } 0151 } 0152 0153 void DragDropViewImplementation::paste() 0154 { 0155 const QMimeData* const data = qApp->clipboard()->mimeData(QClipboard::Clipboard); 0156 0157 if (!data) 0158 { 0159 return; 0160 } 0161 0162 // We need to have a real (context menu action) or fake (Ctrl+V shortcut) mouse position 0163 QPoint eventPos = asView()->mapFromGlobal(QCursor::pos()); 0164 0165 if (!asView()->rect().contains(eventPos)) 0166 { 0167 eventPos = QPoint(0, 0); 0168 } 0169 0170 bool cutAction = decodeIsCutSelection(data); 0171 QDropEvent event(eventPos, 0172 cutAction ? Qt::MoveAction : Qt::CopyAction, 0173 data, Qt::NoButton, 0174 cutAction ? Qt::ShiftModifier : Qt::ControlModifier); 0175 0176 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0177 0178 QModelIndex index = asView()->indexAt(event.position().toPoint()); 0179 0180 #else 0181 0182 QModelIndex index = asView()->indexAt(event.pos()); 0183 0184 #endif 0185 0186 if (!dragDropHandler()->accepts(&event, index)) 0187 { 0188 return; 0189 } 0190 0191 dragDropHandler()->dropEvent(asView(), &event, index); 0192 } 0193 0194 void DragDropViewImplementation::startDrag(Qt::DropActions supportedActions) 0195 { 0196 QModelIndexList indexes = asView()->selectionModel()->selectedIndexes(); 0197 0198 if (indexes.count() > 0) 0199 { 0200 QMimeData* const data = asView()->model()->mimeData(indexes); 0201 0202 if (!data) 0203 { 0204 return; 0205 } 0206 0207 QPixmap pixmap = pixmapForDrag(indexes); 0208 QDrag* const drag = new QDrag(asView()); 0209 drag->setPixmap(pixmap); 0210 drag->setMimeData(data); 0211 drag->exec(supportedActions, Qt::CopyAction); 0212 } 0213 } 0214 0215 void DragDropViewImplementation::dragEnterEvent(QDragEnterEvent* e) 0216 { 0217 AbstractItemDragDropHandler* const handler = dragDropHandler(); 0218 0219 if (handler && handler->acceptsMimeData(e->mimeData())) 0220 { 0221 e->accept(); 0222 } 0223 else 0224 { 0225 e->ignore(); 0226 } 0227 } 0228 0229 void DragDropViewImplementation::dragMoveEvent(QDragMoveEvent* e) 0230 { 0231 // Note: Must call parent view first. This is done by the DECLARE... macro. 0232 0233 AbstractItemDragDropHandler* const handler = dragDropHandler(); 0234 0235 if (handler) 0236 { 0237 0238 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0239 0240 QModelIndex index = asView()->indexAt(e->position().toPoint()); 0241 0242 #else 0243 0244 QModelIndex index = asView()->indexAt(e->pos()); 0245 0246 #endif 0247 0248 Qt::DropAction action = handler->accepts(e, mapIndexForDragDrop(index)); 0249 0250 if (action == Qt::IgnoreAction) 0251 { 0252 e->ignore(); 0253 } 0254 else 0255 { 0256 e->setDropAction(action); 0257 e->accept(); 0258 } 0259 } 0260 } 0261 0262 void DragDropViewImplementation::dropEvent(QDropEvent* e) 0263 { 0264 // Note: Must call parent view first. This is done by the DECLARE... macro. 0265 0266 AbstractItemDragDropHandler* const handler = dragDropHandler(); 0267 0268 if (handler) 0269 { 0270 0271 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0272 0273 QModelIndex index = asView()->indexAt(e->position().toPoint()); 0274 0275 #else 0276 0277 QModelIndex index = asView()->indexAt(e->pos()); 0278 0279 #endif 0280 0281 if (handler->dropEvent(asView(), e, mapIndexForDragDrop(index))) 0282 { 0283 e->accept(); 0284 } 0285 } 0286 } 0287 0288 #ifdef Q_OS_WIN 0289 0290 static const QString mimeTypeCutSelection(QLatin1String("Preferred DropEffect")); 0291 0292 #else 0293 0294 static const QString mimeTypeCutSelection(QLatin1String("application/x-kde-cutselection")); 0295 0296 #endif 0297 0298 void DragDropViewImplementation::encodeIsCutSelection(QMimeData* mime, bool cut) 0299 { 0300 0301 #ifdef Q_OS_WIN 0302 0303 const QByteArray cutSelection = cut ? QByteArrayLiteral("\x02\x00\x00\x00") 0304 : QByteArrayLiteral("\x01\x00\x00\x00"); 0305 0306 #else 0307 0308 const QByteArray cutSelection = cut ? "1" : "0"; 0309 0310 #endif 0311 0312 mime->setData(mimeTypeCutSelection, cutSelection); 0313 } 0314 0315 bool DragDropViewImplementation::decodeIsCutSelection(const QMimeData* mime) 0316 { 0317 QByteArray a = mime->data(mimeTypeCutSelection); 0318 0319 if (a.isEmpty()) 0320 { 0321 return false; 0322 } 0323 0324 #ifdef Q_OS_WIN 0325 0326 return (a == QByteArrayLiteral("\x02\x00\x00\x00")); // true if "0x02" 0327 0328 #else 0329 0330 return (a.at(0) == '1'); // true if 1 0331 0332 #endif 0333 0334 } 0335 0336 } // namespace Digikam