File indexing completed on 2024-04-28 05:52:36

0001 /*
0002     This file is part of the Okteta Gui library, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2008 Friedrich W. H. Kossebau <kossebau@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #include "dropper.hpp"
0010 
0011 // lib
0012 #include <bytearraytableranges.hpp>
0013 #include <bytearraytablecursor.hpp>
0014 #include <bytearraytablelayout.hpp>
0015 #include <abstractbytearrayview.hpp>
0016 // Okteta core
0017 #include <Okteta/AbstractByteArrayModel>
0018 // Qt
0019 #include <QApplication>
0020 #include <QDragEnterEvent>
0021 #include <QDragMoveEvent>
0022 #include <QDragLeaveEvent>
0023 #include <QDropEvent>
0024 #include <QMimeData>
0025 
0026 namespace Okteta {
0027 
0028 static constexpr char DropperOctetStreamFormatName[] = "application/octet-stream";
0029 
0030 Dropper::Dropper(AbstractByteArrayView* view)
0031     : mByteArrayView(view)
0032     , mIsActive(false)
0033 {
0034 }
0035 
0036 Dropper::~Dropper() = default;
0037 
0038 bool Dropper::isActive() const { return mIsActive; }
0039 
0040 bool Dropper::handleDragEnterEvent(QDragEnterEvent* dragEnterEvent)
0041 {
0042     bool eventUsed = false;
0043 
0044     if (!mByteArrayView->isReadOnly()
0045         && mByteArrayView->canReadData(dragEnterEvent->mimeData())) {
0046         mIsActive = true;
0047         // TODO: store value edit data
0048         ByteArrayTableCursor* tableCursor = mByteArrayView->tableCursor();
0049         // TODO: behind state should not be controllable, add cursorData for (re)storingdragEnterEvent
0050         mBeforeDragCursorPos = tableCursor->index();
0051         mBeforeDragCursorIsBehind = tableCursor->isBehind();
0052         mCursorIsMovedByDrag = false;
0053 
0054         eventUsed = true;
0055     }
0056 
0057     return eventUsed;
0058 }
0059 
0060 bool Dropper::handleDragMoveEvent(QDragMoveEvent* dragMoveEvent)
0061 {
0062     bool eventUsed = false;
0063 
0064     if (!mByteArrayView->isReadOnly()
0065         && mByteArrayView->canReadData(dragMoveEvent->mimeData())) {
0066         mCursorIsMovedByDrag = true;
0067 
0068         // let text cursor follow mouse
0069         mByteArrayView->pauseCursor();
0070         // TODO: just for following skip the value edit, remember we are and get back
0071         mByteArrayView->finishByteEdit();
0072         mByteArrayView->placeCursor(dragMoveEvent->pos());
0073         mByteArrayView->unpauseCursor();
0074 
0075         eventUsed = true;
0076     }
0077 
0078     return eventUsed;
0079 }
0080 
0081 bool Dropper::handleDragLeaveEvent(QDragLeaveEvent* dragLeaveEvent)
0082 {
0083     Q_UNUSED(dragLeaveEvent)
0084 
0085     const bool eventUsed = true;
0086     // bye... and thanks for all the cursor movement...
0087     mIsActive = false;
0088     if (mCursorIsMovedByDrag) {
0089         mByteArrayView->pauseCursor();
0090         // TODO: get back to value edit mode if we were in
0091         ByteArrayTableCursor* tableCursor = mByteArrayView->tableCursor();
0092         tableCursor->gotoIndex(mBeforeDragCursorPos);
0093         if (mBeforeDragCursorIsBehind) {
0094             tableCursor->stepBehind();
0095         }
0096         mByteArrayView->unpauseCursor();
0097     }
0098 
0099     return eventUsed;
0100 }
0101 
0102 bool Dropper::handleDropEvent(QDropEvent* dropEvent)
0103 {
0104     bool eventUsed = false;
0105 
0106     if (!mByteArrayView->isReadOnly()
0107         && mByteArrayView->canReadData(dropEvent->mimeData())) {
0108         // leave state
0109         mIsActive = false;
0110 
0111         // is this an internal dnd?
0112         auto* sourceByteArrayView = qobject_cast<AbstractByteArrayView*>(dropEvent->source());
0113         if (sourceByteArrayView
0114             && sourceByteArrayView->byteArrayModel() == mByteArrayView->byteArrayModel()) {
0115             handleInternalDrag(dropEvent, sourceByteArrayView);
0116         } else {
0117             // mByteArrayView->tableRanges()->removeSelection();
0118             mByteArrayView->pasteData(dropEvent->mimeData());
0119         }
0120     }
0121 
0122     return eventUsed;
0123 }
0124 
0125 void Dropper::handleInternalDrag(QDropEvent* dropEvent, AbstractByteArrayView* sourceByteArrayView)
0126 {
0127     // get drag origin
0128     AddressRange selection = sourceByteArrayView->tableRanges()->removeSelection();
0129 
0130     ByteArrayTableCursor* tableCursor = mByteArrayView->tableCursor();
0131     AbstractByteArrayModel* byteArrayModel = mByteArrayView->byteArrayModel();
0132 
0133     Address insertIndex = tableCursor->realIndex();
0134 
0135     // is this a move?
0136     if (dropEvent->proposedAction() == Qt::MoveAction) {
0137         // ignore the copy hold in the event but only move
0138         Address newCursorIndex;
0139         // need to swap?
0140         if (selection.end() < insertIndex) {
0141             newCursorIndex = insertIndex;
0142             const Address firstIndex = selection.start();
0143             selection.set(selection.nextBehindEnd(), insertIndex - 1);
0144             insertIndex = firstIndex;
0145         } else {
0146             newCursorIndex = insertIndex + selection.width();
0147         }
0148 
0149         const bool success = byteArrayModel->swap(insertIndex, selection);
0150         if (success) {
0151             tableCursor->gotoCIndex(newCursorIndex);
0152             Q_EMIT mByteArrayView->cursorPositionChanged(tableCursor->realIndex());
0153         }
0154     }
0155     // is a copy
0156     else {
0157         // TODO: should this be a method of AbstractByteArrayModel, to reuse piece data?
0158 
0159         // get data
0160         const QByteArray data =
0161             dropEvent->mimeData()->data(QLatin1String(DropperOctetStreamFormatName));
0162 
0163         if (!data.isEmpty()) {
0164             if (mByteArrayView->isOverwriteMode()) {
0165                 const Size length = mByteArrayView->layout()->length();
0166                 if (!tableCursor->isBehind() && length > 0) {
0167                     AddressRange overwriteRange = AddressRange::fromWidth(insertIndex, data.size());
0168                     overwriteRange.restrictEndTo(length - 1);
0169                     if (overwriteRange.isValid()) {
0170                         byteArrayModel->replace(overwriteRange, reinterpret_cast<const Byte*>(data.constData()), overwriteRange.width());
0171                     }
0172                 }
0173             } else {
0174                 byteArrayModel->insert(insertIndex, reinterpret_cast<const Byte*>(data.constData()), data.size());
0175             }
0176         }
0177     }
0178 }
0179 
0180 }