File indexing completed on 2024-04-28 05:52:37
0001 /* 0002 This file is part of the Okteta Gui library, made within the KDE community. 0003 0004 SPDX-FileCopyrightText: 2008-2009 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 "mousenavigator.hpp" 0010 0011 // lib 0012 #include <abstractbytearrayview.hpp> 0013 #include <bytearraytableranges.hpp> 0014 #include <bytearraytablecursor.hpp> 0015 #include <bytearraytablelayout.hpp> 0016 // Okteta core 0017 #include <Okteta/TextByteArrayAnalyzer> 0018 // Qt 0019 #include <QApplication> 0020 #include <QClipboard> 0021 #include <QMouseEvent> 0022 #include <QDrag> 0023 #include <QTimer> 0024 0025 namespace Okteta { 0026 static constexpr int DefaultScrollTimerPeriod = 100; 0027 0028 MouseNavigator::MouseNavigator(AbstractByteArrayView* view, AbstractMouseController* parent) 0029 : AbstractMouseController(view, parent) 0030 , mLMBPressed(false) 0031 , mInLMBDoubleClick(false) 0032 , mDragStartPossible(false) 0033 { 0034 mScrollTimer = new QTimer(this); 0035 mDragStartTimer = new QTimer(this); 0036 mTrippleClickTimer = new QTimer(this); 0037 0038 connect(mScrollTimer, &QTimer::timeout, this, &MouseNavigator::autoScrollTimerDone); 0039 connect(mDragStartTimer, &QTimer::timeout, this, &MouseNavigator::startDrag); 0040 mDragStartTimer->setSingleShot(true); 0041 mTrippleClickTimer->setSingleShot(true); 0042 } 0043 0044 MouseNavigator::~MouseNavigator() = default; 0045 0046 bool MouseNavigator::handleMousePressEvent(QMouseEvent* mouseEvent) 0047 { 0048 bool eventUsed = false; 0049 0050 if (mouseEvent->button() == Qt::LeftButton) { 0051 ByteArrayTableCursor* tableCursor = mView->tableCursor(); 0052 ByteArrayTableRanges* tableRanges = mView->tableRanges(); 0053 ByteArrayTableLayout* tableLayout = mView->layout(); 0054 0055 mView->pauseCursor(); 0056 mView->finishByteEdit(); 0057 0058 mLMBPressed = true; 0059 0060 // select whole line? 0061 if (mTrippleClickTimer->isActive() 0062 && (mouseEvent->globalPos() - mDoubleClickPoint).manhattanLength() < QApplication::startDragDistance()) { 0063 mTrippleClickTimer->stop(); 0064 const Address indexAtFirstDoubleClickLinePosition = tableLayout->indexAtFirstLinePosition(mDoubleClickLine); 0065 tableRanges->setSelectionStart(indexAtFirstDoubleClickLinePosition); 0066 tableCursor->gotoIndex(indexAtFirstDoubleClickLinePosition); 0067 tableCursor->gotoLineEnd(); 0068 tableRanges->setSelectionEnd(mView->cursorPosition()); 0069 0070 mView->updateChanged(); 0071 } else { 0072 // TODO: pos() is now, not at the moment of the event, use globalPos() for that,.says dox 0073 const QPoint mousePoint = mView->viewportToColumns(mouseEvent->pos()); 0074 0075 // start of a drag perhaps? 0076 if (tableRanges->hasSelection() && tableRanges->selectionIncludes(mView->indexByPoint(mousePoint))) { 0077 mDragStartPossible = true; 0078 mDragStartTimer->start(QApplication::startDragTime()); 0079 mDragStartPoint = mousePoint; 0080 } else { 0081 mView->placeCursor(mousePoint); 0082 mView->ensureCursorVisible(); 0083 0084 const Address realIndex = tableCursor->realIndex(); 0085 if (tableRanges->selectionStarted()) { 0086 if (mouseEvent->modifiers() & Qt::SHIFT) { 0087 tableRanges->setSelectionEnd(realIndex); 0088 } else { 0089 tableRanges->removeSelection(); 0090 tableRanges->setSelectionStart(realIndex); 0091 } 0092 } else { // start of a new selection possible 0093 tableRanges->setSelectionStart(realIndex); 0094 0095 if (!mView->isReadOnly() && (mouseEvent->modifiers() & Qt::SHIFT)) { // TODO: why only for readwrite? 0096 tableRanges->setSelectionEnd(realIndex); 0097 } 0098 } 0099 0100 tableRanges->removeFurtherSelections(); 0101 } 0102 0103 if (tableRanges->isModified()) { 0104 mView->updateChanged(); 0105 mView->viewport()->setCursor(mView->isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor); 0106 } 0107 } 0108 0109 mView->unpauseCursor(); 0110 mView->emitSelectionSignals(); 0111 0112 eventUsed = true; 0113 } 0114 0115 return eventUsed ? true : AbstractMouseController::handleMousePressEvent(mouseEvent); 0116 } 0117 0118 bool MouseNavigator::handleMouseMoveEvent(QMouseEvent* mouseEvent) 0119 { 0120 bool eventUsed = false; 0121 0122 if (mouseEvent->buttons() == Qt::LeftButton) { 0123 const QPoint movePoint = mView->viewportToColumns(mouseEvent->pos()); 0124 0125 if (mLMBPressed) { 0126 if (mDragStartPossible) { 0127 mDragStartTimer->stop(); 0128 // moved enough for a drag? 0129 if ((movePoint - mDragStartPoint).manhattanLength() > QApplication::startDragDistance()) { 0130 startDrag(); 0131 } 0132 if (!mView->isReadOnly()) { 0133 mView->viewport()->setCursor(Qt::IBeamCursor); 0134 } 0135 } else { 0136 // selecting 0137 handleMouseMove(movePoint); 0138 } 0139 } else if (!mView->isReadOnly()) { 0140 ByteArrayTableRanges* tableRanges = mView->tableRanges(); 0141 0142 // visual feedback for possible dragging 0143 const bool InSelection = 0144 tableRanges->hasSelection() && tableRanges->selectionIncludes(mView->indexByPoint(movePoint)); 0145 mView->viewport()->setCursor(InSelection ? Qt::ArrowCursor : Qt::IBeamCursor); 0146 } 0147 eventUsed = true; 0148 } 0149 0150 return eventUsed ? true : AbstractMouseController::handleMouseMoveEvent(mouseEvent); 0151 } 0152 0153 bool MouseNavigator::handleMouseReleaseEvent(QMouseEvent* mouseEvent) 0154 { 0155 bool eventUsed = false; 0156 0157 if (mouseEvent->button() == Qt::LeftButton) { 0158 ByteArrayTableRanges* tableRanges = mView->tableRanges(); 0159 0160 // const QPoint releasePoint = mView->viewportToColumns( mouseEvent->pos() ); 0161 0162 // this is not the release of a doubleclick so we need to process it? 0163 if (!mInLMBDoubleClick) { 0164 // const int line = mView->lineAt( releasePoint.y() ); 0165 // const int pos = mActiveColumn->linePositionOfX( releasePoint.x() ); // TODO: can we be sure here about the active column? 0166 // const Address index = tableLayout->indexAtCCoord( Coord(pos,line) ); // TODO: can this be another index than the one of the cursor??? 0167 // Q_EMIT mView->clicked( index ); // TODO: who needs this? 0168 } 0169 0170 if (mLMBPressed) { 0171 mLMBPressed = false; 0172 0173 if (mScrollTimer->isActive()) { 0174 mScrollTimer->stop(); 0175 } 0176 0177 // was only click inside selection, nothing dragged? 0178 if (mDragStartPossible) { 0179 mView->selectAll(false); 0180 mDragStartTimer->stop(); 0181 mDragStartPossible = false; 0182 0183 mView->placeCursor(mDragStartPoint); 0184 mView->ensureCursorVisible(); 0185 0186 mView->unpauseCursor(); 0187 } 0188 // was end of selection operation? 0189 else if (tableRanges->hasSelection()) { 0190 if (QApplication::clipboard()->supportsSelection()) { 0191 mView->copyToClipboard(QClipboard::Selection); 0192 } 0193 } 0194 } 0195 0196 Q_EMIT mView->cursorPositionChanged(mView->cursorPosition()); 0197 0198 mInLMBDoubleClick = false; 0199 0200 if (tableRanges->selectionJustStarted()) { 0201 tableRanges->removeSelection(); 0202 } 0203 0204 mView->emitSelectionSignals(); 0205 eventUsed = true; 0206 } 0207 0208 return eventUsed ? true : AbstractMouseController::handleMouseReleaseEvent(mouseEvent); 0209 } 0210 0211 bool MouseNavigator::handleMouseDoubleClickEvent(QMouseEvent* mouseEvent) 0212 { 0213 bool eventUsed = false; 0214 0215 if (mouseEvent->button() == Qt::LeftButton) { 0216 ByteArrayTableCursor* tableCursor = mView->tableCursor(); 0217 0218 mDoubleClickLine = tableCursor->line(); 0219 0220 const Address index = tableCursor->validIndex(); 0221 0222 if (mView->activeCoding() == AbstractByteArrayView::CharCodingId) { 0223 mView->selectWord(index); 0224 0225 // as we already have a doubleclick maybe it is a tripple click 0226 mTrippleClickTimer->start(qApp->doubleClickInterval()); 0227 mDoubleClickPoint = mouseEvent->globalPos(); 0228 } 0229 // else 0230 // mValueEditor->goInsideByte(); TODO: make this possible again 0231 0232 mInLMBDoubleClick = true; // 0233 mLMBPressed = true; 0234 0235 Q_EMIT mView->doubleClicked(index); 0236 eventUsed = true; 0237 } 0238 0239 return eventUsed ? true : AbstractMouseController::handleMouseDoubleClickEvent(mouseEvent); 0240 } 0241 0242 void MouseNavigator::autoScrollTimerDone() 0243 { 0244 if (mLMBPressed) { 0245 handleMouseMove(mView->viewportToColumns(mView->viewport()->mapFromGlobal(QCursor::pos()))); 0246 } 0247 } 0248 0249 void MouseNavigator::handleMouseMove(QPoint point) // handles the move of the mouse with pressed buttons 0250 { 0251 ByteArrayTableCursor* tableCursor = mView->tableCursor(); 0252 ByteArrayTableRanges* tableRanges = mView->tableRanges(); 0253 0254 const int yOffset = mView->yOffset(); 0255 const int behindLastYOffset = yOffset + mView->visibleHeight(); 0256 // scrolltimer but inside of viewport? 0257 if (mScrollTimer->isActive()) { 0258 if (yOffset <= point.y() && point.y() < behindLastYOffset) { 0259 mScrollTimer->stop(); 0260 } 0261 } 0262 // no scrolltimer and outside of viewport? 0263 else { 0264 if (point.y() < yOffset || behindLastYOffset <= point.y()) { 0265 mScrollTimer->start(DefaultScrollTimerPeriod); 0266 } 0267 } 0268 mView->pauseCursor(); 0269 0270 mView->placeCursor(point); 0271 mView->ensureCursorVisible(); 0272 0273 // do wordwise selection? 0274 if (mInLMBDoubleClick && tableRanges->hasFirstWordSelection()) { 0275 Address newIndex = tableCursor->realIndex(); 0276 const AddressRange firstWordSelection = tableRanges->firstWordSelection(); 0277 const TextByteArrayAnalyzer textAnalyzer(mView->byteArrayModel(), mView->charCodec()); 0278 // are we before the selection? 0279 if (firstWordSelection.startsBehind(newIndex)) { 0280 tableRanges->ensureWordSelectionForward(false); 0281 newIndex = textAnalyzer.indexOfLeftWordSelect(newIndex); 0282 } 0283 // or behind? 0284 else if (firstWordSelection.endsBefore(newIndex)) { 0285 tableRanges->ensureWordSelectionForward(true); 0286 newIndex = textAnalyzer.indexOfRightWordSelect(newIndex); 0287 } 0288 // or inside? 0289 else { 0290 tableRanges->ensureWordSelectionForward(true); 0291 newIndex = firstWordSelection.nextBehindEnd(); 0292 } 0293 0294 tableCursor->gotoIndex(newIndex); 0295 } 0296 0297 if (tableRanges->selectionStarted()) { 0298 tableRanges->setSelectionEnd(mView->cursorPosition()); 0299 } 0300 0301 mView->updateChanged(); 0302 mView->unpauseCursor(); 0303 mView->emitSelectionSignals(); 0304 } 0305 0306 void MouseNavigator::startDrag() 0307 { 0308 // reset states 0309 mLMBPressed = false; 0310 mInLMBDoubleClick = false; 0311 mDragStartPossible = false; 0312 0313 // create data 0314 QMimeData* dragData = mView->selectionAsMimeData(); 0315 if (!dragData) { 0316 return; 0317 } 0318 0319 auto* drag = new QDrag(mView); 0320 drag->setMimeData(dragData); 0321 0322 Qt::DropActions request = (mView->isReadOnly() || mView->isOverwriteMode()) ? Qt::CopyAction : Qt::CopyAction | Qt::MoveAction; 0323 Qt::DropAction dropAction = drag->exec(request); 0324 0325 if (dropAction == Qt::MoveAction) { 0326 auto* targetByteArrayView = qobject_cast<AbstractByteArrayView*>(drag->target()); 0327 // Not inside this widget itself? 0328 if (!targetByteArrayView 0329 || targetByteArrayView->byteArrayModel() != mView->byteArrayModel()) { 0330 mView->removeSelectedData(); 0331 } 0332 } 0333 } 0334 0335 } 0336 0337 #include "moc_mousenavigator.cpp"