File indexing completed on 2024-04-14 15:52:51
0001 /* 0002 This file is part of the Okteta Gui library, made within the KDE community. 0003 0004 SPDX-FileCopyrightText: 2008-2010 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 "abstractbytearrayview_p.hpp" 0010 0011 // lib 0012 #include "controller/undoredocontroller.hpp" 0013 #include "controller/clipboardcontroller.hpp" 0014 #include "controller/keynavigator.hpp" 0015 #include "controller/chareditor.hpp" 0016 #include "controller/dropper.hpp" 0017 #include "controller/mousenavigator.hpp" 0018 #include "controller/mousepaster.hpp" 0019 #include "controller/tapnavigator.hpp" 0020 #include "controller/zoomwheelcontroller.hpp" 0021 #include "controller/zoompinchcontroller.hpp" 0022 #include "controller/touchonlytapandholdgesture.hpp" 0023 #include "controller/touchonlytapandholdgesturerecognizer.hpp" 0024 0025 #include "widgetcolumnstylist.hpp" 0026 #include "cursor.hpp" 0027 #include "bordercolumnrenderer.hpp" 0028 #include "oktetagui.hpp" 0029 // Okteta core 0030 #include <Okteta/ValueCodec> 0031 #include <Okteta/Bookmarkable> 0032 #include <Okteta/Versionable> 0033 #include <Okteta/TextByteArrayAnalyzer> 0034 #include <Okteta/Bookmark> 0035 // KF 0036 #include <KLocalizedString> 0037 // Qt 0038 #include <QScroller> 0039 #include <QPainter> 0040 #include <QDragEnterEvent> 0041 #include <QDragMoveEvent> 0042 #include <QDragLeaveEvent> 0043 #include <QDropEvent> 0044 #include <QTimerEvent> 0045 #include <QGestureEvent> 0046 #include <QTapGesture> 0047 #include <QPinchGesture> 0048 #include <QStyleHints> 0049 #include <QApplication> 0050 #include <QToolTip> 0051 #include <QMimeData> 0052 #include <QMenu> 0053 #include <QIcon> 0054 #include <QScroller> 0055 #include <QScrollerProperties> 0056 0057 namespace Okteta { 0058 0059 static constexpr Address DefaultStartOffset = 0;// 5; 0060 static constexpr Address DefaultFirstLineOffset = 0; 0061 0062 // zooming is done in steps of font size points 0063 static constexpr int DefaultZoomStep = 1; 0064 static constexpr int MinFontPointSize = 4; 0065 static constexpr int MaxFontPointSize = 128; 0066 0067 static constexpr AbstractByteArrayView::ValueCoding DefaultValueCoding = AbstractByteArrayView::HexadecimalCoding; 0068 static constexpr AbstractByteArrayView::CharCoding DefaultCharCoding = AbstractByteArrayView::LocalEncoding; 0069 0070 static constexpr AbstractByteArrayView::LayoutStyle DefaultResizeStyle = AbstractByteArrayView::FixedLayoutStyle; 0071 0072 inline QString octetStreamFormatName() { return QStringLiteral("application/octet-stream"); } 0073 0074 class NullModel : public AbstractByteArrayModel 0075 { 0076 Q_OBJECT 0077 0078 public: 0079 NullModel(); 0080 ~NullModel() override; 0081 0082 public: // data access API 0083 Byte byte(Address offset) const override; 0084 Size size() const override; 0085 0086 public: // state read API 0087 bool isModified() const override; 0088 0089 public: // modification API 0090 Size replace(const AddressRange& removeSection, const Byte* insertData, int insertLength) override; 0091 bool swap(Address firstStart, const AddressRange& secondRange) override; 0092 Size fill(Byte fillByte, Address offset = 0, Size fillLength = -1) override; 0093 void setByte(Address offset, Byte byte) override; 0094 void setModified(bool modified) override; 0095 }; 0096 0097 NullModel::NullModel() = default; 0098 NullModel::~NullModel() = default; 0099 0100 Byte NullModel::byte(Address offset) const { Q_UNUSED(offset) return 0; } 0101 Size NullModel::size() const { return 0; } 0102 bool NullModel::isModified() const { return false; } 0103 Size NullModel::replace(const AddressRange& removeSection, const Byte* insertData, int insertLength) 0104 { 0105 Q_UNUSED(removeSection) Q_UNUSED(insertData) Q_UNUSED(insertLength) 0106 return 0; 0107 } 0108 bool NullModel::swap(Address firstStart, const AddressRange& secondRange) 0109 { 0110 Q_UNUSED(firstStart) Q_UNUSED(secondRange) 0111 return false; 0112 } 0113 Size NullModel::fill(Byte fillByte, Address offset, Size fillLength) 0114 { 0115 Q_UNUSED(fillByte) Q_UNUSED(offset) Q_UNUSED(fillLength) 0116 return 0; 0117 } 0118 void NullModel::setByte(Address offset, Byte byte) 0119 { 0120 Q_UNUSED(offset) Q_UNUSED(byte) 0121 } 0122 void NullModel::setModified(bool modified) 0123 { 0124 Q_UNUSED(modified) 0125 } 0126 0127 Q_GLOBAL_STATIC(NullModel, nullModel) 0128 0129 Qt::GestureType touchOnlyTapAndHoldGestureType() 0130 { 0131 static Qt::GestureType type = 0132 QGestureRecognizer::registerRecognizer(new TouchOnlyTapAndHoldGestureRecognizer); 0133 return type; 0134 } 0135 0136 AbstractByteArrayViewPrivate::AbstractByteArrayViewPrivate(AbstractByteArrayView* parent) 0137 : ColumnsViewPrivate(parent) 0138 , mByteArrayModel(nullModel()) 0139 , mTableLayout(new ByteArrayTableLayout(DefaultNoOfBytesPerLine, DefaultFirstLineOffset, DefaultStartOffset, 0, 0)) 0140 , mTableCursor(new ByteArrayTableCursor(mTableLayout)) 0141 , mTableRanges(new ByteArrayTableRanges(mTableLayout)) 0142 , mCursorPixmaps(new Cursor()) 0143 , mReadOnly(false) 0144 , mOverWriteOnly(false) 0145 , mOverWrite(true) 0146 , mInZooming(false) 0147 , mCursorPaused(false) 0148 , mBlinkCursorVisible(false) 0149 , mCursorVisible(false) 0150 // , mDefaultFontSize( p->font().pointSize() ) crashes in font() 0151 , mResizeStyle(DefaultResizeStyle) 0152 { 0153 } 0154 0155 AbstractByteArrayViewPrivate::~AbstractByteArrayViewPrivate() 0156 { 0157 delete mDropper; 0158 0159 delete mZoomWheelController; 0160 0161 delete mMousePaster; 0162 delete mMouseNavigator; 0163 0164 delete mCharEditor; 0165 delete mValueEditor; 0166 delete mKeyNavigator; 0167 delete mClipboardController; 0168 delete mUndoRedoController; 0169 delete mTabController; 0170 0171 delete mZoomPinchController; 0172 delete mTapNavigator; 0173 0174 delete mStylist; 0175 0176 delete mTableRanges; 0177 delete mTableCursor; 0178 delete mTableLayout; 0179 delete mValueCodec; 0180 delete mCharCodec; 0181 0182 delete mCursorPixmaps; 0183 } 0184 0185 void AbstractByteArrayViewPrivate::init() 0186 { 0187 Q_Q(AbstractByteArrayView); 0188 0189 // initialize layout 0190 mTableLayout->setLength(mByteArrayModel->size()); 0191 mTableLayout->setNoOfLinesPerPage(q->noOfLinesPerPage()); 0192 0193 mStylist = new WidgetColumnStylist(q); 0194 0195 mOffsetColumn = 0196 new OffsetColumnRenderer(mStylist, mTableLayout, OffsetFormat::Hexadecimal); 0197 mOffsetBorderColumn = 0198 new BorderColumnRenderer(mStylist, false); 0199 0200 mValueCodec = ValueCodec::createCodec((ValueCoding)DefaultValueCoding); 0201 mValueCoding = DefaultValueCoding; 0202 mCharCodec = CharCodec::createCodec((CharCoding)DefaultCharCoding); 0203 mCharCoding = DefaultCharCoding; 0204 0205 mTabController = new TabController(q, nullptr); 0206 mUndoRedoController = new UndoRedoController(q, mTabController); 0207 mClipboardController = new ClipboardController(q, mUndoRedoController); 0208 mKeyNavigator = new KeyNavigator(q, mClipboardController); 0209 mValueEditor = new ValueEditor(mTableCursor, q, mKeyNavigator); 0210 mCharEditor = new CharEditor(mTableCursor, q, mKeyNavigator); 0211 0212 mMousePaster = new MousePaster(q, nullptr); 0213 mMouseNavigator = new MouseNavigator(q, mMousePaster); 0214 mMouseController = mMouseNavigator; 0215 mTapNavigator = new TapNavigator(q); 0216 0217 mZoomWheelController = new ZoomWheelController(q, nullptr); 0218 mDropper = new Dropper(q); 0219 mZoomPinchController = new ZoomPinchController(q); 0220 0221 setWheelController(mZoomWheelController); 0222 0223 QObject::connect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged, 0224 q, [&](int flashTime) { onCursorFlashTimeChanged(flashTime); }); 0225 0226 q->setAcceptDrops(true); 0227 0228 q->grabGesture(Qt::TapGesture); 0229 q->grabGesture(touchOnlyTapAndHoldGestureType()); 0230 q->grabGesture(Qt::PinchGesture); 0231 0232 // there seems no way to generate a QScroller explicitly (using QScroller::scroller(widget)) 0233 // while also setting it to use the touch gesture 0234 // So we implicitly generate one with QScroller::grabGesture(widget) and the pick it as current active 0235 // by QScroller::scroller(widget) 0236 QScroller::grabGesture(q->viewport(), QScroller::TouchGesture); 0237 QScroller* scroller = QScroller::scroller(q->viewport()); 0238 QScrollerProperties scrollerProperties = scroller->scrollerProperties(); 0239 scrollerProperties.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff); 0240 scrollerProperties.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff); 0241 // values used in other KDE apps 0242 scrollerProperties.setScrollMetric(QScrollerProperties::DecelerationFactor, 0.3); 0243 scrollerProperties.setScrollMetric(QScrollerProperties::MaximumVelocity, 1); 0244 scrollerProperties.setScrollMetric(QScrollerProperties::AcceleratingFlickMaximumTime, 0.2); // Workaround for QTBUG-88249 (non-flick gestures recognized as accelerating flick) 0245 scrollerProperties.setScrollMetric(QScrollerProperties::DragStartDistance, 0.0); 0246 scroller->setScrollerProperties(scrollerProperties); 0247 } 0248 0249 void AbstractByteArrayViewPrivate::setByteArrayModel(AbstractByteArrayModel* byteArrayModel) 0250 { 0251 Q_Q(AbstractByteArrayView); 0252 0253 mByteArrayModel->disconnect(q); 0254 mCursorPaused = true; 0255 0256 mByteArrayModel = byteArrayModel ? byteArrayModel : nullModel(); 0257 0258 // affected: 0259 // length -> no of lines -> width 0260 mTableLayout->setLength(mByteArrayModel->size()); 0261 adjustLayoutToSize(); 0262 0263 // if the model is readonly make the view too, per default 0264 if (mByteArrayModel->isReadOnly()) { 0265 setReadOnly(true); 0266 } 0267 0268 QObject::connect(mByteArrayModel, &AbstractByteArrayModel::readOnlyChanged, 0269 q, [&](bool isReadOnly) { onByteArrayReadOnlyChange(isReadOnly); }); 0270 QObject::connect(mByteArrayModel, &AbstractByteArrayModel::contentsChanged, 0271 q, [&](const Okteta::ArrayChangeMetricsList& changeList) { onContentsChanged(changeList); }); 0272 0273 Bookmarkable* bookmarks = qobject_cast<Bookmarkable*>(mByteArrayModel); 0274 if (bookmarks) { 0275 QObject::connect(mByteArrayModel, SIGNAL(bookmarksAdded(QVector<Okteta::Bookmark>)), 0276 q, SLOT(onBookmarksChange(QVector<Okteta::Bookmark>))); 0277 QObject::connect(mByteArrayModel, SIGNAL(bookmarksRemoved(QVector<Okteta::Bookmark>)), 0278 q, SLOT(onBookmarksChange(QVector<Okteta::Bookmark>))); 0279 } 0280 Versionable* versionControl = qobject_cast<Versionable*>(mByteArrayModel); 0281 if (versionControl) { 0282 QObject::connect(mByteArrayModel, SIGNAL(revertedToVersionIndex(int)), 0283 q, SLOT(onRevertedToVersionIndex(int))); 0284 } 0285 0286 q->viewport()->update(); 0287 0288 mTableCursor->gotoStart(); 0289 ensureCursorVisible(); 0290 0291 unpauseCursor(); 0292 0293 emit q->cursorPositionChanged(cursorPosition()); 0294 } 0295 0296 void AbstractByteArrayViewPrivate::toggleOffsetColumn(bool showOffsetColumn) 0297 { 0298 Q_Q(AbstractByteArrayView); 0299 0300 const bool isVisible = mOffsetColumn->isVisible(); 0301 // no change? 0302 if (isVisible == showOffsetColumn) { 0303 return; 0304 } 0305 0306 mOffsetColumn->setVisible(showOffsetColumn); 0307 0308 updateViewByWidth(); 0309 0310 emit q->offsetColumnVisibleChanged(showOffsetColumn); 0311 } 0312 0313 void AbstractByteArrayViewPrivate::setOffsetCoding(AbstractByteArrayView::OffsetCoding offsetCoding) 0314 { 0315 Q_Q(AbstractByteArrayView); 0316 0317 const OffsetFormat::Format format = static_cast<OffsetFormat::Format>(offsetCoding); 0318 const OffsetFormat::Format currentFormat = mOffsetColumn->format(); 0319 // no change? 0320 if (currentFormat == format) { 0321 return; 0322 } 0323 0324 mOffsetColumn->setFormat(format, q->fontMetrics()); 0325 0326 updateViewByWidth(); 0327 0328 emit q->offsetCodingChanged(offsetCoding); 0329 } 0330 0331 void AbstractByteArrayViewPrivate::changeEvent(QEvent* event) 0332 { 0333 Q_Q(AbstractByteArrayView); 0334 0335 q->ColumnsView::changeEvent(event); 0336 0337 if (event->type() == QEvent::FontChange 0338 && !mInZooming) { 0339 mDefaultFontSize = q->font().pointSize(); 0340 // TODO: why reset zoomlevel here? should this not rather recalculate the new applied font size? 0341 mZoomLevel = 1.0; 0342 } 0343 } 0344 0345 void AbstractByteArrayViewPrivate::zoomIn() { zoomIn(DefaultZoomStep); } 0346 void AbstractByteArrayViewPrivate::zoomOut() { zoomOut(DefaultZoomStep); } 0347 0348 void AbstractByteArrayViewPrivate::zoomIn(int pointIncrement) 0349 { 0350 Q_Q(AbstractByteArrayView); 0351 0352 QFont newFont(q->font()); 0353 int newPointSize = QFontInfo(newFont).pointSize() + pointIncrement; 0354 if (newPointSize > MaxFontPointSize) { 0355 newPointSize = MaxFontPointSize; 0356 } 0357 0358 mZoomLevel = (double)newPointSize / mDefaultFontSize; 0359 newFont.setPointSize(newPointSize); 0360 0361 mInZooming = true; 0362 q->setFont(newFont); 0363 mInZooming = false; 0364 0365 emit q->zoomLevelChanged(mZoomLevel); 0366 } 0367 0368 void AbstractByteArrayViewPrivate::zoomOut(int pointDecrement) 0369 { 0370 Q_Q(AbstractByteArrayView); 0371 0372 QFont newFont(q->font()); 0373 int newPointSize = QFontInfo(newFont).pointSize() - pointDecrement; 0374 if (newPointSize < MinFontPointSize) { 0375 newPointSize = MinFontPointSize; 0376 } 0377 0378 mZoomLevel = (double)newPointSize / mDefaultFontSize; 0379 newFont.setPointSize(newPointSize); 0380 0381 mInZooming = true; 0382 q->setFont(newFont); 0383 mInZooming = false; 0384 0385 emit q->zoomLevelChanged(mZoomLevel); 0386 } 0387 0388 void AbstractByteArrayViewPrivate::zoomTo(int newPointSize) 0389 { 0390 Q_Q(AbstractByteArrayView); 0391 0392 if (newPointSize < MinFontPointSize) { 0393 newPointSize = MinFontPointSize; 0394 } else if (newPointSize > MaxFontPointSize) { 0395 newPointSize = MaxFontPointSize; 0396 } 0397 0398 QFont newFont(q->font()); 0399 if (QFontInfo(newFont).pointSize() == newPointSize) { 0400 return; 0401 } 0402 0403 newFont.setPointSize(newPointSize); 0404 mZoomLevel = (double)newPointSize / mDefaultFontSize; 0405 0406 mInZooming = true; 0407 q->setFont(newFont); 0408 mInZooming = false; 0409 0410 emit q->zoomLevelChanged(mZoomLevel); 0411 } 0412 0413 void AbstractByteArrayViewPrivate::unZoom() 0414 { 0415 zoomTo(mDefaultFontSize); 0416 } 0417 0418 void AbstractByteArrayViewPrivate::setZoomLevel(double zoomLevel) 0419 { 0420 Q_Q(AbstractByteArrayView); 0421 0422 const int currentPointSize = q->fontInfo().pointSize(); 0423 0424 // TODO: here we catch any new zoomlevels which are out of bounds and the zoom already at that bound 0425 if ((currentPointSize <= MinFontPointSize && zoomLevel < (double)MinFontPointSize / mDefaultFontSize) 0426 || (MaxFontPointSize <= currentPointSize && (double)MaxFontPointSize / mDefaultFontSize < zoomLevel)) { 0427 return; 0428 } 0429 0430 int newPointSize = (int)(zoomLevel * mDefaultFontSize); 0431 if (newPointSize < MinFontPointSize) { 0432 newPointSize = MinFontPointSize; 0433 } else if (newPointSize > MaxFontPointSize) { 0434 newPointSize = MaxFontPointSize; 0435 } 0436 0437 QFont newFont(q->font()); 0438 0439 // other than in zoomTo(), where the new zoomlevel is calculated from the integers, here 0440 // use the passed zoomlevel value, to avoid getting trapped inside a small integer value, 0441 // if the zoom tool operates relatively 0442 // think about, if this is the right approach 0443 mZoomLevel = zoomLevel; 0444 newFont.setPointSize(newPointSize); 0445 0446 mInZooming = true; 0447 q->setFont(newFont); 0448 mInZooming = false; 0449 0450 emit q->zoomLevelChanged(mZoomLevel); 0451 } 0452 0453 void AbstractByteArrayViewPrivate::setStartOffset(Address startOffset) 0454 { 0455 Q_Q(AbstractByteArrayView); 0456 0457 if (!mTableLayout->setStartOffset(startOffset)) { 0458 return; 0459 } 0460 0461 pauseCursor(); 0462 0463 // affects: 0464 // the no of lines -> width 0465 adjustLayoutToSize(); 0466 0467 q->viewport()->update(); 0468 0469 mTableCursor->updateCoord(); 0470 ensureCursorVisible(); 0471 0472 unpauseCursor(); 0473 emit q->cursorPositionChanged(cursorPosition()); 0474 } 0475 0476 void AbstractByteArrayViewPrivate::setFirstLineOffset(Address firstLineOffset) 0477 { 0478 Q_Q(AbstractByteArrayView); 0479 0480 if (!mTableLayout->setFirstLineOffset(firstLineOffset)) { 0481 return; 0482 } 0483 0484 pauseCursor(); 0485 0486 // affects: 0487 // the no of lines -> width 0488 adjustLayoutToSize(); 0489 0490 q->viewport()->update(); 0491 0492 mTableCursor->updateCoord(); 0493 ensureCursorVisible(); 0494 0495 unpauseCursor(); 0496 emit q->cursorPositionChanged(cursorPosition()); 0497 } 0498 0499 void AbstractByteArrayViewPrivate::setLayoutStyle(AbstractByteArrayView::LayoutStyle layoutStyle) 0500 { 0501 Q_Q(AbstractByteArrayView); 0502 0503 const bool isChanged = (mResizeStyle != layoutStyle); 0504 0505 if (!isChanged) { 0506 return; 0507 } 0508 0509 mResizeStyle = layoutStyle; 0510 updateViewByWidth(); 0511 0512 emit q->layoutStyleChanged(mResizeStyle); 0513 } 0514 0515 void AbstractByteArrayViewPrivate::setNoOfBytesPerLine(int noOfBytesPerLine) 0516 { 0517 Q_Q(AbstractByteArrayView); 0518 0519 // if the number is explicitly set we expect a wish for no automatic resize 0520 setLayoutStyle(AbstractByteArrayView::FixedLayoutStyle); 0521 0522 if (!mTableLayout->setNoOfBytesPerLine(noOfBytesPerLine)) { 0523 return; 0524 } 0525 0526 updateViewByWidth(); 0527 0528 emit q->noOfBytesPerLineChanged(mTableLayout->noOfBytesPerLine()); 0529 } 0530 0531 void AbstractByteArrayViewPrivate::setReadOnly(bool readOnly) 0532 { 0533 Q_Q(AbstractByteArrayView); 0534 0535 const bool isChanged = (mReadOnly != readOnly); 0536 0537 if (!isChanged) { 0538 return; 0539 } 0540 0541 mReadOnly = readOnly; 0542 0543 adaptController(); 0544 0545 if (mByteArrayModel->isReadOnly()) { 0546 emit q->readOnlyChanged(mReadOnly); 0547 } 0548 } 0549 0550 void AbstractByteArrayViewPrivate::setOverwriteMode(bool overwriteMode) 0551 { 0552 Q_Q(AbstractByteArrayView); 0553 0554 const bool isChanged = ((mOverWrite != overwriteMode) 0555 && (!mOverWriteOnly || overwriteMode)); 0556 0557 if (!isChanged) { 0558 return; 0559 } 0560 0561 mOverWrite = overwriteMode; 0562 0563 // affected: 0564 // cursor shape 0565 const bool changeCursor = !(mCursorPaused || isByteEditorActive()); 0566 if (changeCursor) { 0567 pauseCursor(); 0568 } 0569 0570 mTableCursor->setAppendPosEnabled(!mOverWrite); 0571 0572 if (changeCursor) { 0573 unpauseCursor(); 0574 } 0575 0576 emit q->overwriteModeChanged(mOverWrite); 0577 emit q->cutAvailable(!mOverWrite && mTableRanges->hasSelection()); 0578 } 0579 0580 void AbstractByteArrayViewPrivate::setOverwriteOnly(bool overwriteOnly) 0581 { 0582 const bool isChanged = (mOverWriteOnly != overwriteOnly); 0583 0584 if (!isChanged) { 0585 return; 0586 } 0587 0588 mOverWriteOnly = overwriteOnly; 0589 0590 if (mOverWriteOnly) { 0591 setOverwriteMode(true); 0592 } 0593 } 0594 0595 void AbstractByteArrayViewPrivate::setValueCoding(AbstractByteArrayView::ValueCoding valueCoding) 0596 { 0597 if (mValueCoding == valueCoding) { 0598 return; 0599 } 0600 0601 ValueCodec* newValueCodec 0602 = ValueCodec::createCodec((ValueCoding)valueCoding); 0603 if (!newValueCodec) { 0604 return; 0605 } 0606 0607 delete mValueCodec; 0608 mValueCodec = newValueCodec; 0609 mValueCoding = valueCoding; 0610 } 0611 void AbstractByteArrayViewPrivate::setCharCoding(AbstractByteArrayView::CharCoding charCoding) 0612 { 0613 if (mCharCoding == charCoding) { 0614 return; 0615 } 0616 0617 CharCodec* newCharCodec 0618 = CharCodec::createCodec((CharCoding)charCoding); 0619 if (!newCharCodec) { 0620 return; 0621 } 0622 0623 delete mCharCodec; 0624 mCharCodec = newCharCodec; 0625 mCharCoding = charCoding; 0626 } 0627 void AbstractByteArrayViewPrivate::setCharCoding(const QString& charCodingName) 0628 { 0629 if (mCharCodec->name() == charCodingName) { 0630 return; 0631 } 0632 0633 CharCodec* newCharCodec = 0634 CharCodec::createCodec(charCodingName); 0635 if (!newCharCodec) { 0636 return; 0637 } 0638 0639 delete mCharCodec; 0640 mCharCodec = newCharCodec; 0641 mCharCoding = AbstractByteArrayView::LocalEncoding; // TODO: add encoding no to every known codec 0642 } 0643 0644 void AbstractByteArrayViewPrivate::setMarking(const AddressRange& _marking) 0645 { 0646 AddressRange marking(_marking); 0647 marking.restrictEndTo(mTableLayout->length() - 1); 0648 0649 const AddressRange oldMarking = mTableRanges->marking(); 0650 0651 if (marking == oldMarking) { 0652 return; 0653 } 0654 0655 mTableRanges->setMarking(marking); 0656 0657 updateChanged(); 0658 } 0659 0660 // TODO: make this use select( start, end ) 0661 bool AbstractByteArrayViewPrivate::selectWord(Address index) 0662 { 0663 bool result = false; 0664 0665 if (0 <= index && index < mTableLayout->length()) { 0666 const TextByteArrayAnalyzer textAnalyzer(mByteArrayModel, mCharCodec); 0667 const AddressRange wordSection = textAnalyzer.wordSection(index); 0668 if (wordSection.isValid()) { 0669 pauseCursor(); 0670 finishByteEditor(); 0671 0672 mTableRanges->setFirstWordSelection(wordSection); 0673 mTableCursor->gotoIndex(wordSection.nextBehindEnd()); 0674 0675 endViewUpdate(); 0676 0677 result = true; 0678 } 0679 } 0680 return result; 0681 } 0682 0683 // TODO: make this use select( start, end ) 0684 void AbstractByteArrayViewPrivate::selectAll(bool select) 0685 { 0686 pauseCursor(); 0687 finishByteEditor(); 0688 0689 if (select) { 0690 mTableRanges->setSelection(AddressRange(0, mTableLayout->length() - 1)); 0691 mTableCursor->gotoEnd(); 0692 } else { 0693 mTableRanges->removeSelection(); 0694 } 0695 0696 endViewUpdate(); 0697 } 0698 0699 void AbstractByteArrayViewPrivate::setCursorPosition(Address index, bool behind) 0700 { 0701 pauseCursor(); 0702 finishByteEditor(); 0703 0704 if (behind) { 0705 --index; 0706 } 0707 mTableCursor->gotoCIndex(index); 0708 if (behind) { 0709 mTableCursor->stepBehind(); 0710 } 0711 0712 mTableRanges->removeSelection(); 0713 ensureCursorVisible(); 0714 0715 endViewUpdate(); 0716 } 0717 0718 void AbstractByteArrayViewPrivate::setSelectionCursorPosition(Address index) 0719 { 0720 pauseCursor(); 0721 finishByteEditor(); 0722 0723 if (!mTableRanges->selectionStarted()) { 0724 mTableRanges->setSelectionStart(mTableCursor->realIndex()); 0725 } 0726 0727 mTableCursor->gotoCIndex(index); 0728 0729 mTableRanges->setSelectionEnd(mTableCursor->realIndex()); 0730 ensureCursorVisible(); 0731 0732 endViewUpdate(); 0733 } 0734 0735 void AbstractByteArrayViewPrivate::setSelection(const AddressRange& _selection) 0736 { 0737 AddressRange selection(_selection); 0738 selection.restrictEndTo(mTableLayout->length() - 1); 0739 0740 const AddressRange oldSelection = mTableRanges->selection(); 0741 0742 if (!selection.isValid() 0743 || selection == oldSelection) { 0744 return; 0745 } 0746 0747 pauseCursor(); 0748 finishByteEditor(); 0749 0750 mTableRanges->setSelection(selection); 0751 mTableCursor->gotoCIndex(selection.nextBehindEnd()); 0752 0753 // TODO: ensureVisible( *mActiveColumn, mTableLayout->coordOfIndex(selection.start()) ); 0754 ensureCursorVisible(); 0755 0756 endViewUpdate(); 0757 } 0758 0759 QByteArray AbstractByteArrayViewPrivate::selectedData() const 0760 { 0761 if (!mTableRanges->hasSelection()) { 0762 return {}; 0763 } 0764 0765 const AddressRange selection = mTableRanges->selection(); 0766 QByteArray data; 0767 data.resize(selection.width()); 0768 byteArrayModel()->copyTo(reinterpret_cast<Byte*>(data.data()), selection.start(), selection.width()); 0769 return data; 0770 } 0771 0772 QMimeData* AbstractByteArrayViewPrivate::selectionAsMimeData() const 0773 { 0774 if (!mTableRanges->hasSelection()) { 0775 return nullptr; 0776 } 0777 0778 auto* mimeData = new QMimeData; 0779 mimeData->setData(octetStreamFormatName(), selectedData()); 0780 return mimeData; 0781 } 0782 0783 void AbstractByteArrayViewPrivate::cutToClipboard(QClipboard::Mode mode) 0784 { 0785 if (isEffectiveReadOnly() || mOverWrite) { 0786 return; 0787 } 0788 0789 QMimeData* cutData = selectionAsMimeData(); 0790 if (!cutData) { 0791 return; 0792 } 0793 0794 QApplication::clipboard()->setMimeData(cutData, mode); 0795 0796 removeSelectedData(); 0797 } 0798 0799 void AbstractByteArrayViewPrivate::copyToClipboard(QClipboard::Mode mode) const 0800 { 0801 QMimeData* cutData = selectionAsMimeData(); 0802 if (!cutData) { 0803 return; 0804 } 0805 0806 // if( mode == QClipboard::Selection ) 0807 // q->disconnect( QApplication::clipboard(), SIGNAL(selectionChanged()) ); 0808 0809 QApplication::clipboard()->setMimeData(cutData, mode); 0810 0811 // TODO: why did we do this? And why does the disconnect above not work? 0812 // got connected multiple times after a few selections by mouse 0813 // connect( QApplication::clipboard(), SIGNAL(selectionChanged()), SLOT(clipboardChanged()) ); 0814 } 0815 0816 void AbstractByteArrayViewPrivate::pasteFromClipboard(QClipboard::Mode mode) 0817 { 0818 if (isEffectiveReadOnly()) { 0819 return; 0820 } 0821 0822 const QMimeData* data = QApplication::clipboard()->mimeData(mode); 0823 pasteData(data); 0824 } 0825 0826 void AbstractByteArrayViewPrivate::pasteData(const QMimeData* data) 0827 { 0828 if (!data || data->formats().isEmpty()) { 0829 return; 0830 } 0831 0832 // SYNC: with bytearraydocumentfactory.cpp 0833 // if there is a octet stream, use it, otherwise take the dump of the format 0834 // with the highest priority 0835 // TODO: this may not be, what is expected, think about it, if we just 0836 // take byte array descriptions, like encodings in chars or values 0837 // would need the movement of the encoders into the core library 0838 QString dataFormatName = octetStreamFormatName(); 0839 if (!data->hasFormat(dataFormatName)) { 0840 dataFormatName = data->formats()[0]; 0841 } 0842 0843 const QByteArray byteArray = data->data(dataFormatName); 0844 0845 if (!byteArray.isEmpty()) { 0846 insert(byteArray); 0847 } 0848 } 0849 0850 bool AbstractByteArrayViewPrivate::canReadData(const QMimeData* data) const 0851 { 0852 Q_UNUSED(data) 0853 // taking all for now, see comment in pasteData above 0854 return true;// data->hasFormat( OctetStreamFormatName ); 0855 } 0856 0857 void AbstractByteArrayViewPrivate::insert(const QByteArray& data) 0858 { 0859 Q_Q(AbstractByteArrayView); 0860 0861 Size lengthOfInserted; 0862 Address insertionOffset = -1; 0863 if (mOverWrite) { 0864 if (mTableRanges->hasSelection()) { 0865 // replacing the selection: 0866 // we restrict the replacement to the minimum length of selection and input 0867 AddressRange selection = mTableRanges->removeSelection(); 0868 selection.restrictEndByWidth(data.size()); 0869 insertionOffset = selection.start(); 0870 lengthOfInserted = mByteArrayModel->replace(selection, reinterpret_cast<const Byte*>(data.constData()), selection.width()); 0871 } else { 0872 const Size length = mTableLayout->length(); 0873 if (!isCursorBehind() && length > 0) { 0874 // replacing the normal data, at least until the end 0875 AddressRange insertRange = AddressRange::fromWidth(cursorPosition(), data.size()); 0876 insertRange.restrictEndTo(length - 1); 0877 insertionOffset = insertRange.start(); 0878 lengthOfInserted = mByteArrayModel->replace(insertRange, reinterpret_cast<const Byte*>(data.constData()), insertRange.width()); 0879 } else { 0880 lengthOfInserted = 0; 0881 } 0882 } 0883 } else { 0884 if (mTableRanges->hasSelection()) { 0885 // replacing the selection 0886 const AddressRange selection = mTableRanges->removeSelection(); 0887 insertionOffset = selection.start(); 0888 lengthOfInserted = mByteArrayModel->replace(selection, data); 0889 } else { 0890 insertionOffset = cursorPosition(); 0891 lengthOfInserted = mByteArrayModel->insert(insertionOffset, data); 0892 } 0893 } 0894 // if inserting ourself we want to place the cursor at the end of the inserted data 0895 if (lengthOfInserted > 0) { 0896 const Address postInsertionOffset = insertionOffset + lengthOfInserted; 0897 if (postInsertionOffset != cursorPosition()) { 0898 pauseCursor(); 0899 mTableCursor->gotoCIndex(postInsertionOffset); 0900 unpauseCursor(); 0901 emit q->cursorPositionChanged(cursorPosition()); 0902 } 0903 } 0904 } 0905 0906 void AbstractByteArrayViewPrivate::removeSelectedData() 0907 { 0908 // Can't we do this? 0909 if (isEffectiveReadOnly() || mOverWrite) { // TODO: || mValueEditor->isInEditMode() ) 0910 return; 0911 } 0912 0913 const AddressRange selection = mTableRanges->removeSelection(); 0914 0915 mByteArrayModel->remove(selection); 0916 0917 // clearUndoRedo(); 0918 } 0919 0920 bool AbstractByteArrayViewPrivate::getNextChangedRange(CoordRange* changedRange, const CoordRange& visibleRange) const 0921 { 0922 const bool result = mTableRanges->overlapsChanges(visibleRange, changedRange); 0923 0924 if (result) { 0925 changedRange->restrictTo(visibleRange); 0926 } 0927 0928 return result; 0929 } 0930 0931 void AbstractByteArrayViewPrivate::adaptController() 0932 { 0933 AbstractController* controller = 0934 isEffectiveReadOnly() ? static_cast<AbstractController*>(mKeyNavigator) : 0935 activeCoding() == AbstractByteArrayView::CharCodingId ? static_cast<AbstractController*>(mCharEditor) : 0936 static_cast<AbstractController*>(mValueEditor); 0937 setController(controller); 0938 } 0939 0940 void AbstractByteArrayViewPrivate::updateViewByWidth() 0941 { 0942 Q_Q(AbstractByteArrayView); 0943 0944 pauseCursor(); 0945 0946 adjustToLayoutNoOfBytesPerLine(); 0947 adjustLayoutToSize(); 0948 0949 q->viewport()->update(); 0950 0951 mTableCursor->updateCoord(); 0952 // TODO: see for what actions if would be usable to have ensureCursorVisible() 0953 // TODO: think about adding action "showCursor"/"show FocusItem" to menu 0954 // disabled as this prevents splitting views with aligned areas from working 0955 // ensureCursorVisible(); 0956 0957 unpauseCursor(); 0958 emit q->cursorPositionChanged(cursorPosition()); 0959 } 0960 0961 void AbstractByteArrayViewPrivate::adjustLayoutToSize() 0962 { 0963 Q_Q(AbstractByteArrayView); 0964 0965 // check whether there is a change with the numbers of fitting bytes per line 0966 if (mResizeStyle != AbstractByteArrayView::FixedLayoutStyle) { 0967 // changes? 0968 if (mTableLayout->setNoOfBytesPerLine(fittingBytesPerLine())) { 0969 adjustToLayoutNoOfBytesPerLine(); 0970 } 0971 } 0972 0973 q->setNoOfLines(mTableLayout->noOfLines()); 0974 } 0975 0976 void AbstractByteArrayViewPrivate::onCursorFlashTimeChanged(int flashTime) 0977 { 0978 Q_Q(AbstractByteArrayView); 0979 0980 if (!mCursorVisible) { 0981 return; 0982 } 0983 0984 if (mCursorBlinkTimerId != 0) { 0985 q->killTimer(mCursorBlinkTimerId); 0986 } 0987 0988 if (flashTime >= 2) { 0989 mCursorBlinkTimerId = q->startTimer(flashTime / 2); 0990 } else { 0991 mCursorBlinkTimerId = 0; 0992 } 0993 0994 // ensure cursor is drawn if set to not-blinking and currently in off-blink state 0995 if (!mBlinkCursorVisible && (mCursorBlinkTimerId == 0)) { 0996 blinkCursor(); 0997 } 0998 } 0999 1000 void AbstractByteArrayViewPrivate::startCursor() 1001 { 1002 Q_Q(AbstractByteArrayView); 1003 1004 mCursorPaused = false; 1005 mCursorVisible = true; 1006 1007 updateCursors(); 1008 1009 const int flashTime = QGuiApplication::styleHints()->cursorFlashTime(); 1010 if (flashTime >= 2) { 1011 mCursorBlinkTimerId = q->startTimer(flashTime / 2); 1012 } 1013 } 1014 1015 void AbstractByteArrayViewPrivate::stopCursor() 1016 { 1017 Q_Q(AbstractByteArrayView); 1018 1019 mCursorVisible = false; 1020 1021 if (mCursorBlinkTimerId != 0) { 1022 q->killTimer(mCursorBlinkTimerId); 1023 mCursorBlinkTimerId = 0; 1024 } 1025 1026 pauseCursor(); 1027 } 1028 1029 void AbstractByteArrayViewPrivate::unpauseCursor() 1030 { 1031 mCursorPaused = false; 1032 1033 if (mCursorVisible) { 1034 updateCursors(); 1035 } 1036 } 1037 1038 void AbstractByteArrayViewPrivate::initPainterFromWidget(QPainter* painter) const 1039 { 1040 Q_Q(const AbstractByteArrayView); 1041 1042 const QPalette& palette = q->palette(); 1043 painter->setPen(QPen(palette.brush(q->foregroundRole()), 1)); 1044 painter->setBrush(palette.brush(q->backgroundRole())); 1045 painter->setFont(q->font()); 1046 } 1047 1048 QMenu* AbstractByteArrayViewPrivate::createStandardContextMenu(QPoint position) 1049 { 1050 Q_UNUSED(position); 1051 1052 Q_Q(AbstractByteArrayView); 1053 1054 auto menu = new QMenu(q); 1055 1056 if (mUndoRedoController->addContextMenuActions(menu) > 0) { 1057 menu->addSeparator(); 1058 } 1059 1060 if (mClipboardController->addContextMenuActions(menu) > 0) { 1061 menu->addSeparator(); 1062 } 1063 1064 mKeyNavigator->addContextMenuActions(menu); 1065 1066 return menu; 1067 } 1068 1069 void AbstractByteArrayViewPrivate::mousePressEvent(QMouseEvent* mouseEvent) 1070 { 1071 Q_Q(AbstractByteArrayView); 1072 1073 if (mMouseController->handleMousePressEvent(mouseEvent)) { 1074 mouseEvent->accept(); 1075 } else { 1076 q->ColumnsView::mousePressEvent(mouseEvent); 1077 } 1078 } 1079 1080 void AbstractByteArrayViewPrivate::mouseMoveEvent(QMouseEvent* mouseEvent) 1081 { 1082 Q_Q(AbstractByteArrayView); 1083 1084 if (mMouseController->handleMouseMoveEvent(mouseEvent)) { 1085 mouseEvent->accept(); 1086 } else { 1087 q->ColumnsView::mouseMoveEvent(mouseEvent); 1088 } 1089 } 1090 1091 void AbstractByteArrayViewPrivate::mouseReleaseEvent(QMouseEvent* mouseEvent) 1092 { 1093 Q_Q(AbstractByteArrayView); 1094 1095 if (mMouseController->handleMouseReleaseEvent(mouseEvent)) { 1096 mouseEvent->accept(); 1097 } else { 1098 q->ColumnsView::mouseReleaseEvent(mouseEvent); 1099 } 1100 } 1101 1102 // gets called after press and release instead of a plain press event (?) 1103 void AbstractByteArrayViewPrivate::mouseDoubleClickEvent(QMouseEvent* mouseEvent) 1104 { 1105 Q_Q(AbstractByteArrayView); 1106 1107 if (mMouseController->handleMouseDoubleClickEvent(mouseEvent)) { 1108 mouseEvent->accept(); 1109 } else { 1110 q->ColumnsView::mouseDoubleClickEvent(mouseEvent); 1111 } 1112 } 1113 1114 bool AbstractByteArrayViewPrivate::event(QEvent* event) 1115 { 1116 Q_Q(AbstractByteArrayView); 1117 1118 // 1119 if (event->type() == QEvent::KeyPress) { 1120 auto* keyEvent = static_cast<QKeyEvent*>(event); 1121 if (keyEvent->key() == Qt::Key_Tab || keyEvent->key() == Qt::Key_Backtab) { 1122 q->keyPressEvent(keyEvent); 1123 if (keyEvent->isAccepted()) { 1124 return true; 1125 } 1126 } 1127 } else if ((event->type() == QEvent::MouseMove) || 1128 (event->type() == QEvent::MouseButtonPress) || 1129 (event->type() == QEvent::MouseButtonRelease)) { 1130 // discard any events synthesized from touch input 1131 auto* mouseEvent = static_cast<QMouseEvent*>(event); 1132 if (mouseEvent->source() == Qt::MouseEventSynthesizedByQt) { 1133 event->accept(); 1134 return true; 1135 } 1136 } else if ((event->type() == QEvent::PaletteChange)) { 1137 if (mCursorVisible) { 1138 updateCursors(); 1139 } 1140 } else if ((event->type() == QEvent::ContextMenu) && 1141 (static_cast<QContextMenuEvent*>(event)->reason() == QContextMenuEvent::Keyboard)) { 1142 ensureCursorVisible(); 1143 1144 const QPoint cursorPos = cursorRect().center(); 1145 QContextMenuEvent adaptedContextMenuEvent(QContextMenuEvent::Keyboard, cursorPos, 1146 q->viewport()->mapToGlobal(cursorPos)); 1147 adaptedContextMenuEvent.setAccepted(event->isAccepted()); 1148 1149 const bool result = q->ColumnsView::event(&adaptedContextMenuEvent); 1150 event->setAccepted(adaptedContextMenuEvent.isAccepted()); 1151 1152 return result; 1153 } else if (event->type() == QEvent::Gesture) { 1154 auto* gestureEvent = static_cast<QGestureEvent*>(event); 1155 if (auto* tapGesture = static_cast<QTapGesture*>(gestureEvent->gesture(Qt::TapGesture))) { 1156 return mTapNavigator->handleTapGesture(tapGesture); 1157 } else if (auto* tapAndHoldGesture = static_cast<TouchOnlyTapAndHoldGesture*>(gestureEvent->gesture(touchOnlyTapAndHoldGestureType()))) { 1158 if (tapAndHoldGesture->state() == Qt::GestureFinished) { 1159 const QPoint viewPortPos = tapAndHoldGesture->position().toPoint(); 1160 const QPoint pos = viewPortPos + q->viewport()->pos(); 1161 // TODO: QScrollArea for some reason only deals with QContextMenuEvent::Keyboard, ignores others? 1162 // case QEvent::ContextMenu: 1163 // if (static_cast<QContextMenuEvent *>(e)->reason() == QContextMenuEvent::Keyboard) 1164 // return QFrame::event(e); 1165 // e->ignore(); 1166 // break; 1167 // why that? QFrame delegates to QWidget, which does the normal policy dance 1168 // context menu 1169 QContextMenuEvent simulatedContextMenuEvent(QContextMenuEvent::Keyboard, pos, 1170 q->viewport()->mapToGlobal(viewPortPos)); 1171 simulatedContextMenuEvent.setAccepted(event->isAccepted()); 1172 1173 const bool result = q->ColumnsView::event(&simulatedContextMenuEvent); 1174 event->setAccepted(simulatedContextMenuEvent.isAccepted()); 1175 1176 return result; 1177 } 1178 } else if (auto* pinchGesture = static_cast<QPinchGesture*>(gestureEvent->gesture(Qt::PinchGesture))) { 1179 return mZoomPinchController->handlePinchGesture(pinchGesture); 1180 }; 1181 } 1182 1183 return q->ColumnsView::event(event); 1184 } 1185 1186 bool AbstractByteArrayViewPrivate::viewportEvent(QEvent* event) 1187 { 1188 Q_Q(AbstractByteArrayView); 1189 1190 if (event->type() == QEvent::ToolTip) { 1191 auto* helpEvent = static_cast<QHelpEvent*>(event); 1192 1193 QString toolTip; 1194 1195 Bookmarkable* bookmarks = qobject_cast<Bookmarkable*>(mByteArrayModel); 1196 if (bookmarks) { 1197 const Address index = indexByPoint(q->viewportToColumns(helpEvent->pos())); 1198 if (index != -1) { 1199 if (bookmarks->containsBookmarkFor(index)) { 1200 toolTip = bookmarks->bookmarkFor(index).name(); 1201 } 1202 } 1203 } 1204 1205 if (!toolTip.isNull()) { 1206 QToolTip::showText(helpEvent->globalPos(), toolTip); 1207 } else { 1208 QToolTip::hideText(); 1209 event->ignore(); 1210 } 1211 1212 return true; 1213 } else if ((event->type() == QEvent::MouseMove) || 1214 (event->type() == QEvent::MouseButtonPress) || 1215 (event->type() == QEvent::MouseButtonRelease)) { 1216 // discard any events synthesized from touch input 1217 auto* mouseEvent = static_cast<QMouseEvent*>(event); 1218 if (mouseEvent->source() == Qt::MouseEventSynthesizedByQt) { 1219 event->accept(); 1220 return true; 1221 } 1222 } 1223 1224 return q->ColumnsView::viewportEvent(event); 1225 } 1226 1227 void AbstractByteArrayViewPrivate::resizeEvent(QResizeEvent* resizeEvent) 1228 { 1229 Q_Q(AbstractByteArrayView); 1230 1231 if (mResizeStyle != AbstractByteArrayView::FixedLayoutStyle) { 1232 // changes? 1233 if (mTableLayout->setNoOfBytesPerLine(fittingBytesPerLine())) { 1234 q->setNoOfLines(mTableLayout->noOfLines()); 1235 updateViewByWidth(); 1236 } 1237 } 1238 1239 q->ColumnsView::resizeEvent(resizeEvent); 1240 1241 mTableLayout->setNoOfLinesPerPage(q->noOfLinesPerPage()); // TODO: doesn't work with the new size!!! 1242 } 1243 1244 void AbstractByteArrayViewPrivate::focusInEvent(QFocusEvent* focusEvent) 1245 { 1246 Q_Q(AbstractByteArrayView); 1247 1248 q->ColumnsView::focusInEvent(focusEvent); 1249 startCursor(); 1250 1251 const Qt::FocusReason focusReason = focusEvent->reason(); 1252 if (focusReason != Qt::ActiveWindowFocusReason 1253 && focusReason != Qt::PopupFocusReason) { 1254 emit q->focusChanged(true); 1255 } 1256 } 1257 1258 void AbstractByteArrayViewPrivate::focusOutEvent(QFocusEvent* focusEvent) 1259 { 1260 Q_Q(AbstractByteArrayView); 1261 1262 stopCursor(); 1263 q->ColumnsView::focusOutEvent(focusEvent); 1264 1265 const Qt::FocusReason focusReason = focusEvent->reason(); 1266 if (focusReason != Qt::ActiveWindowFocusReason 1267 && focusReason != Qt::PopupFocusReason) { 1268 emit q->focusChanged(false); 1269 } 1270 } 1271 1272 void AbstractByteArrayViewPrivate::dragEnterEvent(QDragEnterEvent* dragEnterEvent) 1273 { 1274 if (mDropper->handleDragEnterEvent(dragEnterEvent)) { 1275 dragEnterEvent->accept(); 1276 } else { 1277 dragEnterEvent->ignore(); 1278 } 1279 } 1280 1281 void AbstractByteArrayViewPrivate::dragMoveEvent(QDragMoveEvent* dragMoveEvent) 1282 { 1283 if (mDropper->handleDragMoveEvent(dragMoveEvent)) { 1284 dragMoveEvent->accept(); 1285 } else { 1286 dragMoveEvent->ignore(); 1287 } 1288 } 1289 1290 void AbstractByteArrayViewPrivate::dragLeaveEvent(QDragLeaveEvent* dragLeaveEvent) 1291 { 1292 if (mDropper->handleDragLeaveEvent(dragLeaveEvent)) { 1293 dragLeaveEvent->accept(); 1294 } else { 1295 dragLeaveEvent->ignore(); 1296 } 1297 } 1298 1299 void AbstractByteArrayViewPrivate::dropEvent(QDropEvent* dropEvent) 1300 { 1301 if (mDropper->handleDropEvent(dropEvent)) { 1302 dropEvent->accept(); 1303 } else { 1304 dropEvent->ignore(); 1305 } 1306 } 1307 1308 void AbstractByteArrayViewPrivate::contextMenuEvent(QContextMenuEvent* contextMenuEvent) 1309 { 1310 auto menu = createStandardContextMenu(contextMenuEvent->pos()); 1311 menu->setAttribute(Qt::WA_DeleteOnClose); 1312 1313 menu->popup(contextMenuEvent->globalPos()); 1314 } 1315 1316 void AbstractByteArrayViewPrivate::timerEvent(QTimerEvent* timerEvent) 1317 { 1318 Q_Q(AbstractByteArrayView); 1319 1320 if (timerEvent->timerId() == mCursorBlinkTimerId) { 1321 blinkCursor(); 1322 } 1323 1324 q->ColumnsView::timerEvent(timerEvent); 1325 } 1326 1327 void AbstractByteArrayViewPrivate::onBookmarksChange(const QVector<Bookmark>& bookmarks) 1328 { 1329 for (const Bookmark& bookmark : bookmarks) { 1330 const Address position = bookmark.offset(); 1331 mTableRanges->addChangedRange(position, position); 1332 } 1333 1334 updateChanged(); 1335 unpauseCursor(); 1336 } 1337 1338 void AbstractByteArrayViewPrivate::onRevertedToVersionIndex(int versionIndex) 1339 { 1340 Q_UNUSED(versionIndex) 1341 // TODO: only call this if needed 1342 cancelByteEditor(); 1343 } 1344 1345 void AbstractByteArrayViewPrivate::onByteArrayReadOnlyChange(bool isByteArrayReadOnly) 1346 { 1347 Q_Q(AbstractByteArrayView); 1348 1349 adaptController(); 1350 1351 if (!mReadOnly) { 1352 emit q->readOnlyChanged(isByteArrayReadOnly); 1353 } 1354 } 1355 1356 void AbstractByteArrayViewPrivate::onContentsChanged(const ArrayChangeMetricsList& changeList) 1357 { 1358 Q_Q(AbstractByteArrayView); 1359 1360 pauseCursor(); 1361 1362 const bool atEnd = mTableCursor->atEnd(); 1363 const Size oldLength = mTableLayout->length(); // TODO: hack for mDataCursor->adaptSelectionToChange 1364 // update lengths 1365 int oldNoOfLines = q->noOfLines(); 1366 mTableLayout->setLength(mByteArrayModel->size()); 1367 const int newNoOfLines = mTableLayout->noOfLines(); 1368 if (oldNoOfLines != newNoOfLines) { 1369 q->setNoOfLines(newNoOfLines); 1370 const LineRange changedLines = (oldNoOfLines < newNoOfLines) ? 1371 LineRange(oldNoOfLines, newNoOfLines - 1) : 1372 LineRange(newNoOfLines, oldNoOfLines - 1); 1373 mTableRanges->addChangedOffsetLines(changedLines); 1374 } 1375 1376 // adapt cursor(s) 1377 if (atEnd) { 1378 mTableCursor->gotoEnd(); 1379 } else { 1380 mTableCursor->adaptToChanges(changeList, oldLength); 1381 } 1382 1383 mTableRanges->adaptToChanges(changeList, oldLength); 1384 // qCDebug(LOG_OKTETA_GUI) << "Cursor:"<<mDataCursor->index()<<", selection:"<<mTableRanges->selectionStart()<<"-"<<mTableRanges->selectionEnd() 1385 // <<", BytesPerLine: "<<mTableLayout->noOfBytesPerLine()<<endl; 1386 ensureCursorVisible(); 1387 1388 endViewUpdate(); 1389 } 1390 1391 void AbstractByteArrayViewPrivate::endViewUpdate() 1392 { 1393 updateChanged(); 1394 1395 unpauseCursor(); 1396 1397 emitSelectionUpdates(); 1398 } 1399 1400 void AbstractByteArrayViewPrivate::emitSelectionUpdates() 1401 { 1402 Q_Q(AbstractByteArrayView); 1403 1404 bool selectionChanged = false; 1405 bool hasSelectionChanged = false; 1406 mTableRanges->takeHasSelectionChanged(&hasSelectionChanged, &selectionChanged); 1407 1408 if (selectionChanged) { 1409 emit q->selectionChanged(mTableRanges->selection()); 1410 } 1411 if (hasSelectionChanged) { 1412 const bool hasSelection = mTableRanges->hasSelection(); 1413 if (!mOverWrite) { 1414 emit q->cutAvailable(hasSelection); 1415 } 1416 emit q->copyAvailable(hasSelection); 1417 emit q->hasSelectedDataChanged(hasSelection); 1418 } 1419 emit q->cursorPositionChanged(cursorPosition()); 1420 } 1421 1422 #if 0 1423 void AbstractByteArrayViewPrivate::onClipboardChanged() 1424 { 1425 Q_Q(AbstractByteArrayView); 1426 1427 // don't listen to selection changes 1428 q->disconnect(QApplication::clipboard(), SIGNAL(selectionChanged())); 1429 selectAll(false); 1430 } 1431 #endif 1432 1433 } 1434 1435 #include "abstractbytearrayview_p.moc"