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 }