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