File indexing completed on 2024-04-28 09:48:36

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"