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