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"