1 /*
2     This file is part of the Okteta Gui library, made within the KDE community.
3 
4     SPDX-FileCopyrightText: 2008-2010 Friedrich W. H. Kossebau <kossebau@kde.org>
5 
6     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7 */
8 
9 #include "abstractbytearrayview_p.hpp"
10 
11 // lib
12 #include "controller/undoredocontroller.hpp"
13 #include "controller/clipboardcontroller.hpp"
14 #include "controller/keynavigator.hpp"
15 #include "controller/chareditor.hpp"
16 #include "controller/dropper.hpp"
17 #include "controller/mousenavigator.hpp"
18 #include "controller/mousepaster.hpp"
19 #include "controller/tapnavigator.hpp"
20 #include "controller/zoomwheelcontroller.hpp"
21 #include "controller/zoompinchcontroller.hpp"
22 #include "controller/touchonlytapandholdgesture.hpp"
23 #include "controller/touchonlytapandholdgesturerecognizer.hpp"
24 
25 #include "widgetcolumnstylist.hpp"
26 #include "cursor.hpp"
27 #include "bordercolumnrenderer.hpp"
28 #include "oktetagui.hpp"
29 // Okteta core
30 #include <Okteta/ValueCodec>
31 #include <Okteta/Bookmarkable>
32 #include <Okteta/Versionable>
33 #include <Okteta/TextByteArrayAnalyzer>
34 #include <Okteta/Bookmark>
35 // KF
36 #include <KLocalizedString>
37 // Qt
38 #include <QScroller>
39 #include <QPainter>
40 #include <QDragEnterEvent>
41 #include <QDragMoveEvent>
42 #include <QDragLeaveEvent>
43 #include <QDropEvent>
44 #include <QTimerEvent>
45 #include <QGestureEvent>
46 #include <QTapGesture>
47 #include <QPinchGesture>
48 #include <QStyleHints>
49 #include <QApplication>
50 #include <QToolTip>
51 #include <QMimeData>
52 #include <QMenu>
53 #include <QIcon>
54 #include <QScroller>
55 #include <QScrollerProperties>
56 
57 namespace Okteta {
58 
59 static constexpr Address DefaultStartOffset = 0;// 5;
60 static constexpr Address DefaultFirstLineOffset = 0;
61 
62 // zooming is done in steps of font size points
63 static constexpr int DefaultZoomStep = 1;
64 static constexpr int MinFontPointSize = 4;
65 static constexpr int MaxFontPointSize = 128;
66 
67 static constexpr AbstractByteArrayView::ValueCoding DefaultValueCoding =  AbstractByteArrayView::HexadecimalCoding;
68 static constexpr AbstractByteArrayView::CharCoding DefaultCharCoding = AbstractByteArrayView::LocalEncoding;
69 
70 static constexpr AbstractByteArrayView::LayoutStyle DefaultResizeStyle = AbstractByteArrayView::FixedLayoutStyle;
71 
octetStreamFormatName()72 inline QString octetStreamFormatName() { return QStringLiteral("application/octet-stream"); }
73 
74 class NullModel : public AbstractByteArrayModel
75 {
76     Q_OBJECT
77 
78 public:
79     NullModel();
80     ~NullModel() override;
81 
82 public: // data access API
83     Byte byte(Address offset) const override;
84     Size size() const override;
85 
86 public: // state read API
87     bool isModified() const override;
88 
89 public: // modification API
90     Size replace(const AddressRange& removeSection, const Byte* insertData, int insertLength) override;
91     bool swap(Address firstStart, const AddressRange& secondRange) override;
92     Size fill(Byte fillByte, Address offset = 0, Size fillLength = -1) override;
93     void setByte(Address offset, Byte byte) override;
94     void setModified(bool modified) override;
95 };
96 
97 NullModel::NullModel() = default;
98 NullModel::~NullModel() = default;
99 
byte(Address offset) const100 Byte NullModel::byte(Address offset) const { Q_UNUSED(offset) return 0; }
size() const101 Size NullModel::size() const { return 0; }
isModified() const102 bool NullModel::isModified() const { return false; }
replace(const AddressRange & removeSection,const Byte * insertData,int insertLength)103 Size NullModel::replace(const AddressRange& removeSection, const Byte* insertData, int insertLength)
104 {
105     Q_UNUSED(removeSection) Q_UNUSED(insertData) Q_UNUSED(insertLength)
106     return 0;
107 }
swap(Address firstStart,const AddressRange & secondRange)108 bool NullModel::swap(Address firstStart, const AddressRange& secondRange)
109 {
110     Q_UNUSED(firstStart) Q_UNUSED(secondRange)
111     return false;
112 }
fill(Byte fillByte,Address offset,Size fillLength)113 Size NullModel::fill(Byte fillByte, Address offset, Size fillLength)
114 {
115     Q_UNUSED(fillByte) Q_UNUSED(offset) Q_UNUSED(fillLength)
116     return 0;
117 }
setByte(Address offset,Byte byte)118 void NullModel::setByte(Address offset, Byte byte)
119 {
120     Q_UNUSED(offset) Q_UNUSED(byte)
121 }
setModified(bool modified)122 void NullModel::setModified(bool modified)
123 {
124     Q_UNUSED(modified)
125 }
126 
Q_GLOBAL_STATIC(NullModel,nullModel)127 Q_GLOBAL_STATIC(NullModel, nullModel)
128 
129 Qt::GestureType touchOnlyTapAndHoldGestureType()
130 {
131     static Qt::GestureType type =
132         QGestureRecognizer::registerRecognizer(new TouchOnlyTapAndHoldGestureRecognizer);
133     return type;
134 }
135 
AbstractByteArrayViewPrivate(AbstractByteArrayView * parent)136 AbstractByteArrayViewPrivate::AbstractByteArrayViewPrivate(AbstractByteArrayView* parent)
137     : ColumnsViewPrivate(parent)
138     , mByteArrayModel(nullModel())
139     , mTableLayout(new ByteArrayTableLayout(DefaultNoOfBytesPerLine, DefaultFirstLineOffset, DefaultStartOffset, 0, 0))
140     , mTableCursor(new ByteArrayTableCursor(mTableLayout))
141     , mTableRanges(new ByteArrayTableRanges(mTableLayout))
142     , mCursorPixmaps(new Cursor())
143     , mReadOnly(false)
144     , mOverWriteOnly(false)
145     , mOverWrite(true)
146     , mInZooming(false)
147     , mCursorPaused(false)
148     , mBlinkCursorVisible(false)
149     , mCursorVisible(false)
150     // , mDefaultFontSize( p->font().pointSize() ) crashes in font()
151     , mResizeStyle(DefaultResizeStyle)
152 {
153 }
154 
~AbstractByteArrayViewPrivate()155 AbstractByteArrayViewPrivate::~AbstractByteArrayViewPrivate()
156 {
157     delete mDropper;
158 
159     delete mZoomWheelController;
160 
161     delete mMousePaster;
162     delete mMouseNavigator;
163 
164     delete mCharEditor;
165     delete mValueEditor;
166     delete mKeyNavigator;
167     delete mClipboardController;
168     delete mUndoRedoController;
169     delete mTabController;
170 
171     delete mZoomPinchController;
172     delete mTapNavigator;
173 
174     delete mStylist;
175 
176     delete mTableRanges;
177     delete mTableCursor;
178     delete mTableLayout;
179     delete mValueCodec;
180     delete mCharCodec;
181 
182     delete mCursorPixmaps;
183 }
184 
init()185 void AbstractByteArrayViewPrivate::init()
186 {
187     Q_Q(AbstractByteArrayView);
188 
189     // initialize layout
190     mTableLayout->setLength(mByteArrayModel->size());
191     mTableLayout->setNoOfLinesPerPage(q->noOfLinesPerPage());
192 
193     mStylist = new WidgetColumnStylist(q);
194 
195     mOffsetColumn =
196         new OffsetColumnRenderer(mStylist, mTableLayout, OffsetFormat::Hexadecimal);
197     mOffsetBorderColumn =
198         new BorderColumnRenderer(mStylist, false);
199 
200     mValueCodec = ValueCodec::createCodec((ValueCoding)DefaultValueCoding);
201     mValueCoding = DefaultValueCoding;
202     mCharCodec = CharCodec::createCodec((CharCoding)DefaultCharCoding);
203     mCharCoding = DefaultCharCoding;
204 
205     mTabController = new TabController(q, nullptr);
206     mUndoRedoController = new UndoRedoController(q, mTabController);
207     mClipboardController = new ClipboardController(q, mUndoRedoController);
208     mKeyNavigator = new KeyNavigator(q, mClipboardController);
209     mValueEditor = new ValueEditor(mTableCursor, q, mKeyNavigator);
210     mCharEditor = new CharEditor(mTableCursor, q, mKeyNavigator);
211 
212     mMousePaster = new MousePaster(q, nullptr);
213     mMouseNavigator = new MouseNavigator(q, mMousePaster);
214     mMouseController = mMouseNavigator;
215     mTapNavigator = new TapNavigator(q);
216 
217     mZoomWheelController = new ZoomWheelController(q, nullptr);
218     mDropper = new Dropper(q);
219     mZoomPinchController = new ZoomPinchController(q);
220 
221     setWheelController(mZoomWheelController);
222 
223     QObject::connect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged,
224                      q, [&](int flashTime) { onCursorFlashTimeChanged(flashTime); });
225 
226     q->setAcceptDrops(true);
227 
228     q->grabGesture(Qt::TapGesture);
229     q->grabGesture(touchOnlyTapAndHoldGestureType());
230     q->grabGesture(Qt::PinchGesture);
231 
232     // there seems no way to generate a QScroller explicitly (using QScroller::scroller(widget))
233     // while also setting it to use the touch gesture
234     // So we implicitly generate one with QScroller::grabGesture(widget) and the pick it as current active
235     // by QScroller::scroller(widget)
236     QScroller::grabGesture(q->viewport(), QScroller::TouchGesture);
237     QScroller* scroller = QScroller::scroller(q->viewport());
238     QScrollerProperties scrollerProperties = scroller->scrollerProperties();
239     scrollerProperties.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
240     scrollerProperties.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
241     // values used in other KDE apps
242     scrollerProperties.setScrollMetric(QScrollerProperties::DecelerationFactor, 0.3);
243     scrollerProperties.setScrollMetric(QScrollerProperties::MaximumVelocity, 1);
244     scrollerProperties.setScrollMetric(QScrollerProperties::AcceleratingFlickMaximumTime, 0.2); // Workaround for QTBUG-88249 (non-flick gestures recognized as accelerating flick)
245     scrollerProperties.setScrollMetric(QScrollerProperties::DragStartDistance, 0.0);
246     scroller->setScrollerProperties(scrollerProperties);
247 }
248 
setByteArrayModel(AbstractByteArrayModel * byteArrayModel)249 void AbstractByteArrayViewPrivate::setByteArrayModel(AbstractByteArrayModel* byteArrayModel)
250 {
251     Q_Q(AbstractByteArrayView);
252 
253     mByteArrayModel->disconnect(q);
254     mCursorPaused = true;
255 
256     mByteArrayModel = byteArrayModel ? byteArrayModel : nullModel();
257 
258     // affected:
259     // length -> no of lines -> width
260     mTableLayout->setLength(mByteArrayModel->size());
261     adjustLayoutToSize();
262 
263     // if the model is readonly make the view too, per default
264     if (mByteArrayModel->isReadOnly()) {
265         setReadOnly(true);
266     }
267 
268     QObject::connect(mByteArrayModel, &AbstractByteArrayModel::readOnlyChanged,
269                      q, [&](bool isReadOnly) { onByteArrayReadOnlyChange(isReadOnly); });
270     QObject::connect(mByteArrayModel, &AbstractByteArrayModel::contentsChanged,
271                      q, [&](const Okteta::ArrayChangeMetricsList& changeList) { onContentsChanged(changeList); });
272 
273     Bookmarkable* bookmarks = qobject_cast<Bookmarkable*>(mByteArrayModel);
274     if (bookmarks) {
275         QObject::connect(mByteArrayModel, SIGNAL(bookmarksAdded(QVector<Okteta::Bookmark>)),
276                          q, SLOT(onBookmarksChange(QVector<Okteta::Bookmark>)));
277         QObject::connect(mByteArrayModel, SIGNAL(bookmarksRemoved(QVector<Okteta::Bookmark>)),
278                          q, SLOT(onBookmarksChange(QVector<Okteta::Bookmark>)));
279     }
280     Versionable* versionControl = qobject_cast<Versionable*>(mByteArrayModel);
281     if (versionControl) {
282         QObject::connect(mByteArrayModel, SIGNAL(revertedToVersionIndex(int)),
283                          q, SLOT(onRevertedToVersionIndex(int)));
284     }
285 
286     q->viewport()->update();
287 
288     mTableCursor->gotoStart();
289     ensureCursorVisible();
290 
291     unpauseCursor();
292 
293     emit q->cursorPositionChanged(cursorPosition());
294 }
295 
toggleOffsetColumn(bool showOffsetColumn)296 void AbstractByteArrayViewPrivate::toggleOffsetColumn(bool showOffsetColumn)
297 {
298     Q_Q(AbstractByteArrayView);
299 
300     const bool isVisible = mOffsetColumn->isVisible();
301     // no change?
302     if (isVisible == showOffsetColumn) {
303         return;
304     }
305 
306     mOffsetColumn->setVisible(showOffsetColumn);
307 
308     updateViewByWidth();
309 
310     emit q->offsetColumnVisibleChanged(showOffsetColumn);
311 }
312 
setOffsetCoding(AbstractByteArrayView::OffsetCoding offsetCoding)313 void AbstractByteArrayViewPrivate::setOffsetCoding(AbstractByteArrayView::OffsetCoding offsetCoding)
314 {
315     Q_Q(AbstractByteArrayView);
316 
317     const OffsetFormat::Format format = static_cast<OffsetFormat::Format>(offsetCoding);
318     const OffsetFormat::Format currentFormat = mOffsetColumn->format();
319     // no change?
320     if (currentFormat == format) {
321         return;
322     }
323 
324     mOffsetColumn->setFormat(format, q->fontMetrics());
325 
326     updateViewByWidth();
327 
328     emit q->offsetCodingChanged(offsetCoding);
329 }
330 
changeEvent(QEvent * event)331 void AbstractByteArrayViewPrivate::changeEvent(QEvent* event)
332 {
333     Q_Q(AbstractByteArrayView);
334 
335     q->ColumnsView::changeEvent(event);
336 
337     if (event->type() == QEvent::FontChange
338         && !mInZooming) {
339         mDefaultFontSize = q->font().pointSize();
340         // TODO: why reset zoomlevel here? should this not rather recalculate the new applied font size?
341         mZoomLevel = 1.0;
342     }
343 }
344 
zoomIn()345 void AbstractByteArrayViewPrivate::zoomIn()  { zoomIn(DefaultZoomStep); }
zoomOut()346 void AbstractByteArrayViewPrivate::zoomOut() { zoomOut(DefaultZoomStep); }
347 
zoomIn(int pointIncrement)348 void AbstractByteArrayViewPrivate::zoomIn(int pointIncrement)
349 {
350     Q_Q(AbstractByteArrayView);
351 
352     QFont newFont(q->font());
353     int newPointSize = QFontInfo(newFont).pointSize() + pointIncrement;
354     if (newPointSize > MaxFontPointSize) {
355         newPointSize = MaxFontPointSize;
356     }
357 
358     mZoomLevel = (double)newPointSize / mDefaultFontSize;
359     newFont.setPointSize(newPointSize);
360 
361     mInZooming = true;
362     q->setFont(newFont);
363     mInZooming = false;
364 
365     emit q->zoomLevelChanged(mZoomLevel);
366 }
367 
zoomOut(int pointDecrement)368 void AbstractByteArrayViewPrivate::zoomOut(int pointDecrement)
369 {
370     Q_Q(AbstractByteArrayView);
371 
372     QFont newFont(q->font());
373     int newPointSize = QFontInfo(newFont).pointSize() - pointDecrement;
374     if (newPointSize < MinFontPointSize) {
375         newPointSize = MinFontPointSize;
376     }
377 
378     mZoomLevel = (double)newPointSize / mDefaultFontSize;
379     newFont.setPointSize(newPointSize);
380 
381     mInZooming = true;
382     q->setFont(newFont);
383     mInZooming = false;
384 
385     emit q->zoomLevelChanged(mZoomLevel);
386 }
387 
zoomTo(int newPointSize)388 void AbstractByteArrayViewPrivate::zoomTo(int newPointSize)
389 {
390     Q_Q(AbstractByteArrayView);
391 
392     if (newPointSize < MinFontPointSize) {
393         newPointSize = MinFontPointSize;
394     } else if (newPointSize > MaxFontPointSize) {
395         newPointSize = MaxFontPointSize;
396     }
397 
398     QFont newFont(q->font());
399     if (QFontInfo(newFont).pointSize() == newPointSize) {
400         return;
401     }
402 
403     newFont.setPointSize(newPointSize);
404     mZoomLevel = (double)newPointSize / mDefaultFontSize;
405 
406     mInZooming = true;
407     q->setFont(newFont);
408     mInZooming = false;
409 
410     emit q->zoomLevelChanged(mZoomLevel);
411 }
412 
unZoom()413 void AbstractByteArrayViewPrivate::unZoom()
414 {
415     zoomTo(mDefaultFontSize);
416 }
417 
setZoomLevel(double zoomLevel)418 void AbstractByteArrayViewPrivate::setZoomLevel(double zoomLevel)
419 {
420     Q_Q(AbstractByteArrayView);
421 
422     const int currentPointSize = q->fontInfo().pointSize();
423 
424     // TODO: here we catch any new zoomlevels which are out of bounds and the zoom already at that bound
425     if ((currentPointSize <= MinFontPointSize && zoomLevel < (double)MinFontPointSize / mDefaultFontSize)
426         || (MaxFontPointSize <= currentPointSize && (double)MaxFontPointSize / mDefaultFontSize < zoomLevel)) {
427         return;
428     }
429 
430     int newPointSize = (int)(zoomLevel * mDefaultFontSize);
431     if (newPointSize < MinFontPointSize) {
432         newPointSize = MinFontPointSize;
433     } else if (newPointSize > MaxFontPointSize) {
434         newPointSize = MaxFontPointSize;
435     }
436 
437     QFont newFont(q->font());
438 
439     // other than in zoomTo(), where the new zoomlevel is calculated from the integers, here
440     // use the passed zoomlevel value, to avoid getting trapped inside a small integer value,
441     // if the zoom tool operates relatively
442     // think about, if this is the right approach
443     mZoomLevel = zoomLevel;
444     newFont.setPointSize(newPointSize);
445 
446     mInZooming = true;
447     q->setFont(newFont);
448     mInZooming = false;
449 
450     emit q->zoomLevelChanged(mZoomLevel);
451 }
452 
setStartOffset(Address startOffset)453 void AbstractByteArrayViewPrivate::setStartOffset(Address startOffset)
454 {
455     Q_Q(AbstractByteArrayView);
456 
457     if (!mTableLayout->setStartOffset(startOffset)) {
458         return;
459     }
460 
461     pauseCursor();
462 
463     // affects:
464     // the no of lines -> width
465     adjustLayoutToSize();
466 
467     q->viewport()->update();
468 
469     mTableCursor->updateCoord();
470     ensureCursorVisible();
471 
472     unpauseCursor();
473     emit q->cursorPositionChanged(cursorPosition());
474 }
475 
setFirstLineOffset(Address firstLineOffset)476 void AbstractByteArrayViewPrivate::setFirstLineOffset(Address firstLineOffset)
477 {
478     Q_Q(AbstractByteArrayView);
479 
480     if (!mTableLayout->setFirstLineOffset(firstLineOffset)) {
481         return;
482     }
483 
484     pauseCursor();
485 
486     // affects:
487     // the no of lines -> width
488     adjustLayoutToSize();
489 
490     q->viewport()->update();
491 
492     mTableCursor->updateCoord();
493     ensureCursorVisible();
494 
495     unpauseCursor();
496     emit q->cursorPositionChanged(cursorPosition());
497 }
498 
setLayoutStyle(AbstractByteArrayView::LayoutStyle layoutStyle)499 void AbstractByteArrayViewPrivate::setLayoutStyle(AbstractByteArrayView::LayoutStyle layoutStyle)
500 {
501     Q_Q(AbstractByteArrayView);
502 
503     const bool isChanged = (mResizeStyle != layoutStyle);
504 
505     if (!isChanged) {
506         return;
507     }
508 
509     mResizeStyle = layoutStyle;
510     updateViewByWidth();
511 
512     emit q->layoutStyleChanged(mResizeStyle);
513 }
514 
setNoOfBytesPerLine(int noOfBytesPerLine)515 void AbstractByteArrayViewPrivate::setNoOfBytesPerLine(int noOfBytesPerLine)
516 {
517     Q_Q(AbstractByteArrayView);
518 
519     // if the number is explicitly set we expect a wish for no automatic resize
520     setLayoutStyle(AbstractByteArrayView::FixedLayoutStyle);
521 
522     if (!mTableLayout->setNoOfBytesPerLine(noOfBytesPerLine)) {
523         return;
524     }
525 
526     updateViewByWidth();
527 
528     emit q->noOfBytesPerLineChanged(mTableLayout->noOfBytesPerLine());
529 }
530 
setReadOnly(bool readOnly)531 void AbstractByteArrayViewPrivate::setReadOnly(bool readOnly)
532 {
533     Q_Q(AbstractByteArrayView);
534 
535     const bool isChanged = (mReadOnly != readOnly);
536 
537     if (!isChanged) {
538         return;
539     }
540 
541     mReadOnly = readOnly;
542 
543     adaptController();
544 
545     if (mByteArrayModel->isReadOnly()) {
546         emit q->readOnlyChanged(mReadOnly);
547     }
548 }
549 
setOverwriteMode(bool overwriteMode)550 void AbstractByteArrayViewPrivate::setOverwriteMode(bool overwriteMode)
551 {
552     Q_Q(AbstractByteArrayView);
553 
554     const bool isChanged = ((mOverWrite != overwriteMode)
555                             && (!mOverWriteOnly || overwriteMode));
556 
557     if (!isChanged) {
558         return;
559     }
560 
561     mOverWrite = overwriteMode;
562 
563     // affected:
564     // cursor shape
565     const bool changeCursor = !(mCursorPaused || isByteEditorActive());
566     if (changeCursor) {
567         pauseCursor();
568     }
569 
570     mTableCursor->setAppendPosEnabled(!mOverWrite);
571 
572     if (changeCursor) {
573         unpauseCursor();
574     }
575 
576     emit q->overwriteModeChanged(mOverWrite);
577     emit q->cutAvailable(!mOverWrite && mTableRanges->hasSelection());
578 }
579 
setOverwriteOnly(bool overwriteOnly)580 void AbstractByteArrayViewPrivate::setOverwriteOnly(bool overwriteOnly)
581 {
582     const bool isChanged = (mOverWriteOnly != overwriteOnly);
583 
584     if (!isChanged) {
585         return;
586     }
587 
588     mOverWriteOnly = overwriteOnly;
589 
590     if (mOverWriteOnly) {
591         setOverwriteMode(true);
592     }
593 }
594 
setValueCoding(AbstractByteArrayView::ValueCoding valueCoding)595 void AbstractByteArrayViewPrivate::setValueCoding(AbstractByteArrayView::ValueCoding valueCoding)
596 {
597     if (mValueCoding == valueCoding) {
598         return;
599     }
600 
601     ValueCodec* newValueCodec
602         = ValueCodec::createCodec((ValueCoding)valueCoding);
603     if (!newValueCodec) {
604         return;
605     }
606 
607     delete mValueCodec;
608     mValueCodec = newValueCodec;
609     mValueCoding = valueCoding;
610 }
setCharCoding(AbstractByteArrayView::CharCoding charCoding)611 void AbstractByteArrayViewPrivate::setCharCoding(AbstractByteArrayView::CharCoding charCoding)
612 {
613     if (mCharCoding == charCoding) {
614         return;
615     }
616 
617     CharCodec* newCharCodec
618         = CharCodec::createCodec((CharCoding)charCoding);
619     if (!newCharCodec) {
620         return;
621     }
622 
623     delete mCharCodec;
624     mCharCodec = newCharCodec;
625     mCharCoding = charCoding;
626 }
setCharCoding(const QString & charCodingName)627 void AbstractByteArrayViewPrivate::setCharCoding(const QString& charCodingName)
628 {
629     if (mCharCodec->name() == charCodingName) {
630         return;
631     }
632 
633     CharCodec* newCharCodec =
634         CharCodec::createCodec(charCodingName);
635     if (!newCharCodec) {
636         return;
637     }
638 
639     delete mCharCodec;
640     mCharCodec = newCharCodec;
641     mCharCoding = AbstractByteArrayView::LocalEncoding; // TODO: add encoding no to every known codec
642 }
643 
setMarking(const AddressRange & _marking)644 void AbstractByteArrayViewPrivate::setMarking(const AddressRange& _marking)
645 {
646     AddressRange marking(_marking);
647     marking.restrictEndTo(mTableLayout->length() - 1);
648 
649     const AddressRange oldMarking = mTableRanges->marking();
650 
651     if (marking == oldMarking) {
652         return;
653     }
654 
655     mTableRanges->setMarking(marking);
656 
657     updateChanged();
658 }
659 
660 // TODO: make this use select( start, end )
selectWord(Address index)661 bool AbstractByteArrayViewPrivate::selectWord(Address index)
662 {
663     Q_Q(AbstractByteArrayView);
664 
665     bool result = false;
666 
667     if (0 <= index && index < mTableLayout->length()) {
668         const TextByteArrayAnalyzer textAnalyzer(mByteArrayModel, mCharCodec);
669         const AddressRange wordSection = textAnalyzer.wordSection(index);
670         if (wordSection.isValid()) {
671             pauseCursor();
672             finishByteEditor();
673 
674             mTableRanges->setFirstWordSelection(wordSection);
675             mTableCursor->gotoIndex(wordSection.nextBehindEnd());
676 
677             endViewUpdate();
678 
679             result = true;
680         }
681     }
682     return result;
683 }
684 
685 // TODO: make this use select( start, end )
selectAll(bool select)686 void AbstractByteArrayViewPrivate::selectAll(bool select)
687 {
688     Q_Q(AbstractByteArrayView);
689 
690     pauseCursor();
691     finishByteEditor();
692 
693     if (select) {
694         mTableRanges->setSelection(AddressRange(0, mTableLayout->length() - 1));
695         mTableCursor->gotoEnd();
696     } else {
697         mTableRanges->removeSelection();
698     }
699 
700     endViewUpdate();
701 }
702 
setCursorPosition(Address index,bool behind)703 void AbstractByteArrayViewPrivate::setCursorPosition(Address index, bool behind)
704 {
705     Q_Q(AbstractByteArrayView);
706 
707     pauseCursor();
708     finishByteEditor();
709 
710     if (behind) {
711         --index;
712     }
713     mTableCursor->gotoCIndex(index);
714     if (behind) {
715         mTableCursor->stepBehind();
716     }
717 
718     mTableRanges->removeSelection();
719     ensureCursorVisible();
720 
721     endViewUpdate();
722 }
723 
setSelectionCursorPosition(Address index)724 void AbstractByteArrayViewPrivate::setSelectionCursorPosition(Address index)
725 {
726     Q_Q(AbstractByteArrayView);
727 
728     pauseCursor();
729     finishByteEditor();
730 
731     if (!mTableRanges->selectionStarted()) {
732         mTableRanges->setSelectionStart(mTableCursor->realIndex());
733     }
734 
735     mTableCursor->gotoCIndex(index);
736 
737     mTableRanges->setSelectionEnd(mTableCursor->realIndex());
738     ensureCursorVisible();
739 
740     endViewUpdate();
741 }
742 
setSelection(const AddressRange & _selection)743 void AbstractByteArrayViewPrivate::setSelection(const AddressRange& _selection)
744 {
745     Q_Q(AbstractByteArrayView);
746 
747     AddressRange selection(_selection);
748     selection.restrictEndTo(mTableLayout->length() - 1);
749 
750     const AddressRange oldSelection = mTableRanges->selection();
751 
752     if (!selection.isValid()
753         || selection == oldSelection) {
754         return;
755     }
756 
757     pauseCursor();
758     finishByteEditor();
759 
760     mTableRanges->setSelection(selection);
761     mTableCursor->gotoCIndex(selection.nextBehindEnd());
762 
763 // TODO:            ensureVisible( *mActiveColumn, mTableLayout->coordOfIndex(selection.start()) );
764     ensureCursorVisible();
765 
766     endViewUpdate();
767 }
768 
selectedData() const769 QByteArray AbstractByteArrayViewPrivate::selectedData() const
770 {
771     if (!mTableRanges->hasSelection()) {
772         return {};
773     }
774 
775     const AddressRange selection = mTableRanges->selection();
776     QByteArray data;
777     data.resize(selection.width());
778     byteArrayModel()->copyTo(reinterpret_cast<Byte*>(data.data()), selection.start(), selection.width());
779     return data;
780 }
781 
selectionAsMimeData() const782 QMimeData* AbstractByteArrayViewPrivate::selectionAsMimeData() const
783 {
784     if (!mTableRanges->hasSelection()) {
785         return nullptr;
786     }
787 
788     auto* mimeData = new QMimeData;
789     mimeData->setData(octetStreamFormatName(), selectedData());
790     return mimeData;
791 }
792 
cutToClipboard(QClipboard::Mode mode)793 void AbstractByteArrayViewPrivate::cutToClipboard(QClipboard::Mode mode)
794 {
795     if (isEffectiveReadOnly() || mOverWrite) {
796         return;
797     }
798 
799     QMimeData* cutData = selectionAsMimeData();
800     if (!cutData) {
801         return;
802     }
803 
804     QApplication::clipboard()->setMimeData(cutData, mode);
805 
806     removeSelectedData();
807 }
808 
copyToClipboard(QClipboard::Mode mode) const809 void AbstractByteArrayViewPrivate::copyToClipboard(QClipboard::Mode mode) const
810 {
811     QMimeData* cutData = selectionAsMimeData();
812     if (!cutData) {
813         return;
814     }
815 
816 //     if( mode == QClipboard::Selection )
817 //         q->disconnect( QApplication::clipboard(), SIGNAL(selectionChanged()) );
818 
819     QApplication::clipboard()->setMimeData(cutData, mode);
820 
821     // TODO: why did we do this? And why does the disconnect above not work?
822     // got connected multiple times after a few selections by mouse
823 //         connect( QApplication::clipboard(), SIGNAL(selectionChanged()), SLOT(clipboardChanged()) );
824 }
825 
pasteFromClipboard(QClipboard::Mode mode)826 void AbstractByteArrayViewPrivate::pasteFromClipboard(QClipboard::Mode mode)
827 {
828     if (isEffectiveReadOnly()) {
829         return;
830     }
831 
832     const QMimeData* data = QApplication::clipboard()->mimeData(mode);
833     pasteData(data);
834 }
835 
pasteData(const QMimeData * data)836 void AbstractByteArrayViewPrivate::pasteData(const QMimeData* data)
837 {
838     if (!data || data->formats().isEmpty()) {
839         return;
840     }
841 
842     // SYNC: with bytearraydocumentfactory.cpp
843     // if there is a octet stream, use it, otherwise take the dump of the format
844     // with the highest priority
845     // TODO: this may not be, what is expected, think about it, if we just
846     // take byte array descriptions, like encodings in chars or values
847     // would need the movement of the encoders into the core library
848     QString dataFormatName = octetStreamFormatName();
849     if (!data->hasFormat(dataFormatName)) {
850         dataFormatName = data->formats()[0];
851     }
852 
853     const QByteArray byteArray = data->data(dataFormatName);
854 
855     if (!byteArray.isEmpty()) {
856         insert(byteArray);
857     }
858 }
859 
canReadData(const QMimeData * data) const860 bool AbstractByteArrayViewPrivate::canReadData(const QMimeData* data) const
861 {
862     Q_UNUSED(data)
863     // taking all for now, see comment in pasteData above
864     return true;// data->hasFormat( OctetStreamFormatName );
865 }
866 
insert(const QByteArray & data)867 void AbstractByteArrayViewPrivate::insert(const QByteArray& data)
868 {
869     Q_Q(AbstractByteArrayView);
870 
871     Size lengthOfInserted;
872     Address insertionOffset = -1;
873     if (mOverWrite) {
874         if (mTableRanges->hasSelection()) {
875             // replacing the selection:
876             // we restrict the replacement to the minimum length of selection and input
877             AddressRange selection = mTableRanges->removeSelection();
878             selection.restrictEndByWidth(data.size());
879             insertionOffset = selection.start();
880             lengthOfInserted = mByteArrayModel->replace(selection, reinterpret_cast<const Byte*>(data.constData()), selection.width());
881         } else {
882             const Size length = mTableLayout->length();
883             if (!isCursorBehind() && length > 0) {
884                 // replacing the normal data, at least until the end
885                 AddressRange insertRange = AddressRange::fromWidth(cursorPosition(), data.size());
886                 insertRange.restrictEndTo(length - 1);
887                 insertionOffset = insertRange.start();
888                 lengthOfInserted = mByteArrayModel->replace(insertRange, reinterpret_cast<const Byte*>(data.constData()), insertRange.width());
889             } else {
890                 lengthOfInserted = 0;
891             }
892         }
893     } else {
894         if (mTableRanges->hasSelection()) {
895             // replacing the selection
896             const AddressRange selection = mTableRanges->removeSelection();
897             insertionOffset = selection.start();
898             lengthOfInserted = mByteArrayModel->replace(selection, data);
899         } else {
900             insertionOffset = cursorPosition();
901             lengthOfInserted = mByteArrayModel->insert(insertionOffset, data);
902         }
903     }
904     // if inserting ourself we want to place the cursor at the end of the inserted data
905     if (lengthOfInserted > 0) {
906         const Address postInsertionOffset = insertionOffset + lengthOfInserted;
907         if (postInsertionOffset != cursorPosition()) {
908             pauseCursor();
909             mTableCursor->gotoCIndex(postInsertionOffset);
910             unpauseCursor();
911             emit q->cursorPositionChanged(cursorPosition());
912         }
913     }
914 }
915 
removeSelectedData()916 void AbstractByteArrayViewPrivate::removeSelectedData()
917 {
918     // Can't we do this?
919     if (isEffectiveReadOnly() || mOverWrite) { // TODO: || mValueEditor->isInEditMode() )
920         return;
921     }
922 
923     const AddressRange selection = mTableRanges->removeSelection();
924 
925     mByteArrayModel->remove(selection);
926 
927 //     clearUndoRedo();
928 }
929 
getNextChangedRange(CoordRange * changedRange,const CoordRange & visibleRange) const930 bool AbstractByteArrayViewPrivate::getNextChangedRange(CoordRange* changedRange, const CoordRange& visibleRange) const
931 {
932     const bool result = mTableRanges->overlapsChanges(visibleRange, changedRange);
933 
934     if (result) {
935         changedRange->restrictTo(visibleRange);
936     }
937 
938     return result;
939 }
940 
adaptController()941 void AbstractByteArrayViewPrivate::adaptController()
942 {
943     AbstractController* controller =
944         isEffectiveReadOnly() ?                                 static_cast<AbstractController*>(mKeyNavigator) :
945         activeCoding() == AbstractByteArrayView::CharCodingId ? static_cast<AbstractController*>(mCharEditor) :
946                                                                 static_cast<AbstractController*>(mValueEditor);
947     setController(controller);
948 }
949 
updateViewByWidth()950 void AbstractByteArrayViewPrivate::updateViewByWidth()
951 {
952     Q_Q(AbstractByteArrayView);
953 
954     pauseCursor();
955 
956     adjustToLayoutNoOfBytesPerLine();
957     adjustLayoutToSize();
958 
959     q->viewport()->update();
960 
961     mTableCursor->updateCoord();
962     // TODO: see for what actions if would be usable to have ensureCursorVisible()
963     // TODO: think about adding action "showCursor"/"show FocusItem" to menu
964     // disabled as this prevents splitting views with aligned areas from working
965     // ensureCursorVisible();
966 
967     unpauseCursor();
968     emit q->cursorPositionChanged(cursorPosition());
969 }
970 
adjustLayoutToSize()971 void AbstractByteArrayViewPrivate::adjustLayoutToSize()
972 {
973     Q_Q(AbstractByteArrayView);
974 
975     // check whether there is a change with the numbers of fitting bytes per line
976     if (mResizeStyle != AbstractByteArrayView::FixedLayoutStyle) {
977         // changes?
978         if (mTableLayout->setNoOfBytesPerLine(fittingBytesPerLine())) {
979             adjustToLayoutNoOfBytesPerLine();
980         }
981     }
982 
983     q->setNoOfLines(mTableLayout->noOfLines());
984 }
985 
onCursorFlashTimeChanged(int flashTime)986 void AbstractByteArrayViewPrivate::onCursorFlashTimeChanged(int flashTime)
987 {
988     Q_Q(AbstractByteArrayView);
989 
990     if (!mCursorVisible) {
991         return;
992     }
993 
994     if (mCursorBlinkTimerId != 0) {
995         q->killTimer(mCursorBlinkTimerId);
996     }
997 
998     if (flashTime >= 2) {
999         mCursorBlinkTimerId = q->startTimer(flashTime / 2);
1000     } else {
1001         mCursorBlinkTimerId = 0;
1002     }
1003 
1004     // ensure cursor is drawn if set to not-blinking and currently in off-blink state
1005     if (!mBlinkCursorVisible && (mCursorBlinkTimerId == 0)) {
1006         blinkCursor();
1007     }
1008 }
1009 
startCursor()1010 void AbstractByteArrayViewPrivate::startCursor()
1011 {
1012     Q_Q(AbstractByteArrayView);
1013 
1014     mCursorPaused = false;
1015     mCursorVisible = true;
1016 
1017     updateCursors();
1018 
1019     const int flashTime = QGuiApplication::styleHints()->cursorFlashTime();
1020     if (flashTime >= 2) {
1021         mCursorBlinkTimerId = q->startTimer(flashTime / 2);
1022     }
1023 }
1024 
stopCursor()1025 void AbstractByteArrayViewPrivate::stopCursor()
1026 {
1027     Q_Q(AbstractByteArrayView);
1028 
1029     mCursorVisible = false;
1030 
1031     if (mCursorBlinkTimerId != 0) {
1032         q->killTimer(mCursorBlinkTimerId);
1033         mCursorBlinkTimerId = 0;
1034     }
1035 
1036     pauseCursor();
1037 }
1038 
unpauseCursor()1039 void AbstractByteArrayViewPrivate::unpauseCursor()
1040 {
1041     mCursorPaused = false;
1042 
1043     if (mCursorVisible) {
1044         updateCursors();
1045     }
1046 }
1047 
initPainterFromWidget(QPainter * painter) const1048 void AbstractByteArrayViewPrivate::initPainterFromWidget(QPainter* painter) const
1049 {
1050     Q_Q(const AbstractByteArrayView);
1051 
1052     const QPalette& palette = q->palette();
1053     painter->setPen(QPen(palette.brush(q->foregroundRole()), 1));
1054     painter->setBrush(palette.brush(q->backgroundRole()));
1055     painter->setFont(q->font());
1056 }
1057 
createStandardContextMenu(QPoint position)1058 QMenu* AbstractByteArrayViewPrivate::createStandardContextMenu(QPoint position)
1059 {
1060     Q_UNUSED(position);
1061 
1062     Q_Q(AbstractByteArrayView);
1063 
1064     auto menu = new QMenu(q);
1065 
1066     if (mUndoRedoController->addContextMenuActions(menu) > 0) {
1067         menu->addSeparator();
1068     }
1069 
1070     if (mClipboardController->addContextMenuActions(menu) > 0) {
1071         menu->addSeparator();
1072     }
1073 
1074     mKeyNavigator->addContextMenuActions(menu);
1075 
1076     return menu;
1077 }
1078 
mousePressEvent(QMouseEvent * mouseEvent)1079 void AbstractByteArrayViewPrivate::mousePressEvent(QMouseEvent* mouseEvent)
1080 {
1081     Q_Q(AbstractByteArrayView);
1082 
1083     if (mMouseController->handleMousePressEvent(mouseEvent)) {
1084         mouseEvent->accept();
1085     } else {
1086         q->ColumnsView::mousePressEvent(mouseEvent);
1087     }
1088 }
1089 
mouseMoveEvent(QMouseEvent * mouseEvent)1090 void AbstractByteArrayViewPrivate::mouseMoveEvent(QMouseEvent* mouseEvent)
1091 {
1092     Q_Q(AbstractByteArrayView);
1093 
1094     if (mMouseController->handleMouseMoveEvent(mouseEvent)) {
1095         mouseEvent->accept();
1096     } else {
1097         q->ColumnsView::mouseMoveEvent(mouseEvent);
1098     }
1099 }
1100 
mouseReleaseEvent(QMouseEvent * mouseEvent)1101 void AbstractByteArrayViewPrivate::mouseReleaseEvent(QMouseEvent* mouseEvent)
1102 {
1103     Q_Q(AbstractByteArrayView);
1104 
1105     if (mMouseController->handleMouseReleaseEvent(mouseEvent)) {
1106         mouseEvent->accept();
1107     } else {
1108         q->ColumnsView::mouseReleaseEvent(mouseEvent);
1109     }
1110 }
1111 
1112 // gets called after press and release instead of a plain press event (?)
mouseDoubleClickEvent(QMouseEvent * mouseEvent)1113 void AbstractByteArrayViewPrivate::mouseDoubleClickEvent(QMouseEvent* mouseEvent)
1114 {
1115     Q_Q(AbstractByteArrayView);
1116 
1117     if (mMouseController->handleMouseDoubleClickEvent(mouseEvent)) {
1118         mouseEvent->accept();
1119     } else {
1120         q->ColumnsView::mouseDoubleClickEvent(mouseEvent);
1121     }
1122 }
1123 
event(QEvent * event)1124 bool AbstractByteArrayViewPrivate::event(QEvent* event)
1125 {
1126     Q_Q(AbstractByteArrayView);
1127 
1128     //
1129     if (event->type() == QEvent::KeyPress) {
1130         auto* keyEvent = static_cast<QKeyEvent*>(event);
1131         if (keyEvent->key() == Qt::Key_Tab || keyEvent->key() == Qt::Key_Backtab) {
1132             q->keyPressEvent(keyEvent);
1133             if (keyEvent->isAccepted()) {
1134                 return true;
1135             }
1136         }
1137     } else if ((event->type() == QEvent::MouseMove) ||
1138                (event->type() == QEvent::MouseButtonPress) ||
1139                (event->type() == QEvent::MouseButtonRelease)) {
1140         // discard any events synthesized from touch input
1141         auto* mouseEvent = static_cast<QMouseEvent*>(event);
1142         if (mouseEvent->source() == Qt::MouseEventSynthesizedByQt) {
1143             event->accept();
1144             return true;
1145         }
1146     } else if ((event->type() == QEvent::PaletteChange)) {
1147         if (mCursorVisible) {
1148             updateCursors();
1149         }
1150     } else if ((event->type() == QEvent::ContextMenu) &&
1151                (static_cast<QContextMenuEvent*>(event)->reason() == QContextMenuEvent::Keyboard)) {
1152         ensureCursorVisible();
1153 
1154         const QPoint cursorPos = cursorRect().center();
1155         QContextMenuEvent adaptedContextMenuEvent(QContextMenuEvent::Keyboard, cursorPos,
1156                                                   q->viewport()->mapToGlobal(cursorPos));
1157         adaptedContextMenuEvent.setAccepted(event->isAccepted());
1158 
1159         const bool result = q->ColumnsView::event(&adaptedContextMenuEvent);
1160         event->setAccepted(adaptedContextMenuEvent.isAccepted());
1161 
1162         return result;
1163     } else if (event->type() == QEvent::Gesture) {
1164         auto* gestureEvent = static_cast<QGestureEvent*>(event);
1165         if (auto* tapGesture = static_cast<QTapGesture*>(gestureEvent->gesture(Qt::TapGesture))) {
1166             return mTapNavigator->handleTapGesture(tapGesture);
1167         } else if (auto* tapAndHoldGesture = static_cast<TouchOnlyTapAndHoldGesture*>(gestureEvent->gesture(touchOnlyTapAndHoldGestureType()))) {
1168             if (tapAndHoldGesture->state() == Qt::GestureFinished) {
1169                 const QPoint viewPortPos = tapAndHoldGesture->position().toPoint();
1170                 const QPoint pos = viewPortPos + q->viewport()->pos();
1171                 // TODO: QScrollArea for some reason only deals with QContextMenuEvent::Keyboard, ignores others?
1172                 // case QEvent::ContextMenu:
1173                 //     if (static_cast<QContextMenuEvent *>(e)->reason() == QContextMenuEvent::Keyboard)
1174                 //         return QFrame::event(e);
1175                 //     e->ignore();
1176                 //     break;
1177                 // why that? QFrame delegates to QWidget, which does the normal policy dance
1178                 // context menu
1179                 QContextMenuEvent simulatedContextMenuEvent(QContextMenuEvent::Keyboard, pos,
1180                                                           q->viewport()->mapToGlobal(viewPortPos));
1181                 simulatedContextMenuEvent.setAccepted(event->isAccepted());
1182 
1183                 const bool result = q->ColumnsView::event(&simulatedContextMenuEvent);
1184                 event->setAccepted(simulatedContextMenuEvent.isAccepted());
1185 
1186                 return result;
1187             }
1188         } else if (auto* pinchGesture = static_cast<QPinchGesture*>(gestureEvent->gesture(Qt::PinchGesture))) {
1189             return mZoomPinchController->handlePinchGesture(pinchGesture);
1190         };
1191     }
1192 
1193     return q->ColumnsView::event(event);
1194 }
1195 
viewportEvent(QEvent * event)1196 bool AbstractByteArrayViewPrivate::viewportEvent(QEvent* event)
1197 {
1198     Q_Q(AbstractByteArrayView);
1199 
1200     if (event->type() == QEvent::ToolTip) {
1201         auto* helpEvent = static_cast<QHelpEvent*>(event);
1202 
1203         QString toolTip;
1204 
1205         Bookmarkable* bookmarks = qobject_cast<Bookmarkable*>(mByteArrayModel);
1206         if (bookmarks) {
1207             const Address index = indexByPoint(q->viewportToColumns(helpEvent->pos()));
1208             if (index != -1) {
1209                 if (bookmarks->containsBookmarkFor(index)) {
1210                     toolTip = bookmarks->bookmarkFor(index).name();
1211                 }
1212             }
1213         }
1214 
1215         if (!toolTip.isNull()) {
1216             QToolTip::showText(helpEvent->globalPos(), toolTip);
1217         } else {
1218             QToolTip::hideText();
1219             event->ignore();
1220         }
1221 
1222         return true;
1223     } else if ((event->type() == QEvent::MouseMove) ||
1224                (event->type() == QEvent::MouseButtonPress) ||
1225                (event->type() == QEvent::MouseButtonRelease)) {
1226         // discard any events synthesized from touch input
1227         auto* mouseEvent = static_cast<QMouseEvent*>(event);
1228         if (mouseEvent->source() == Qt::MouseEventSynthesizedByQt) {
1229             event->accept();
1230             return true;
1231         }
1232     }
1233 
1234     return q->ColumnsView::viewportEvent(event);
1235 }
1236 
resizeEvent(QResizeEvent * resizeEvent)1237 void AbstractByteArrayViewPrivate::resizeEvent(QResizeEvent* resizeEvent)
1238 {
1239     Q_Q(AbstractByteArrayView);
1240 
1241     if (mResizeStyle != AbstractByteArrayView::FixedLayoutStyle) {
1242         // changes?
1243         if (mTableLayout->setNoOfBytesPerLine(fittingBytesPerLine())) {
1244             q->setNoOfLines(mTableLayout->noOfLines());
1245             updateViewByWidth();
1246         }
1247     }
1248 
1249     q->ColumnsView::resizeEvent(resizeEvent);
1250 
1251     mTableLayout->setNoOfLinesPerPage(q->noOfLinesPerPage());   // TODO: doesn't work with the new size!!!
1252 }
1253 
focusInEvent(QFocusEvent * focusEvent)1254 void AbstractByteArrayViewPrivate::focusInEvent(QFocusEvent* focusEvent)
1255 {
1256     Q_Q(AbstractByteArrayView);
1257 
1258     q->ColumnsView::focusInEvent(focusEvent);
1259     startCursor();
1260 
1261     const Qt::FocusReason focusReason = focusEvent->reason();
1262     if (focusReason != Qt::ActiveWindowFocusReason
1263         && focusReason != Qt::PopupFocusReason) {
1264         emit q->focusChanged(true);
1265     }
1266 }
1267 
focusOutEvent(QFocusEvent * focusEvent)1268 void AbstractByteArrayViewPrivate::focusOutEvent(QFocusEvent* focusEvent)
1269 {
1270     Q_Q(AbstractByteArrayView);
1271 
1272     stopCursor();
1273     q->ColumnsView::focusOutEvent(focusEvent);
1274 
1275     const Qt::FocusReason focusReason = focusEvent->reason();
1276     if (focusReason != Qt::ActiveWindowFocusReason
1277         && focusReason != Qt::PopupFocusReason) {
1278         emit q->focusChanged(false);
1279     }
1280 }
1281 
dragEnterEvent(QDragEnterEvent * dragEnterEvent)1282 void AbstractByteArrayViewPrivate::dragEnterEvent(QDragEnterEvent* dragEnterEvent)
1283 {
1284     if (mDropper->handleDragEnterEvent(dragEnterEvent)) {
1285         dragEnterEvent->accept();
1286     } else {
1287         dragEnterEvent->ignore();
1288     }
1289 }
1290 
dragMoveEvent(QDragMoveEvent * dragMoveEvent)1291 void AbstractByteArrayViewPrivate::dragMoveEvent(QDragMoveEvent* dragMoveEvent)
1292 {
1293     if (mDropper->handleDragMoveEvent(dragMoveEvent)) {
1294         dragMoveEvent->accept();
1295     } else {
1296         dragMoveEvent->ignore();
1297     }
1298 }
1299 
dragLeaveEvent(QDragLeaveEvent * dragLeaveEvent)1300 void AbstractByteArrayViewPrivate::dragLeaveEvent(QDragLeaveEvent* dragLeaveEvent)
1301 {
1302     if (mDropper->handleDragLeaveEvent(dragLeaveEvent)) {
1303         dragLeaveEvent->accept();
1304     } else {
1305         dragLeaveEvent->ignore();
1306     }
1307 }
1308 
dropEvent(QDropEvent * dropEvent)1309 void AbstractByteArrayViewPrivate::dropEvent(QDropEvent* dropEvent)
1310 {
1311     if (mDropper->handleDropEvent(dropEvent)) {
1312         dropEvent->accept();
1313     } else {
1314         dropEvent->ignore();
1315     }
1316 }
1317 
contextMenuEvent(QContextMenuEvent * contextMenuEvent)1318 void AbstractByteArrayViewPrivate::contextMenuEvent(QContextMenuEvent* contextMenuEvent)
1319 {
1320     auto menu = createStandardContextMenu(contextMenuEvent->pos());
1321     menu->setAttribute(Qt::WA_DeleteOnClose);
1322 
1323     menu->popup(contextMenuEvent->globalPos());
1324 }
1325 
timerEvent(QTimerEvent * timerEvent)1326 void AbstractByteArrayViewPrivate::timerEvent(QTimerEvent* timerEvent)
1327 {
1328     Q_Q(AbstractByteArrayView);
1329 
1330     if (timerEvent->timerId() == mCursorBlinkTimerId) {
1331         blinkCursor();
1332     }
1333 
1334     q->ColumnsView::timerEvent(timerEvent);
1335 }
1336 
onBookmarksChange(const QVector<Bookmark> & bookmarks)1337 void AbstractByteArrayViewPrivate::onBookmarksChange(const QVector<Bookmark>& bookmarks)
1338 {
1339     for (const Bookmark& bookmark : bookmarks) {
1340         const Address position = bookmark.offset();
1341         mTableRanges->addChangedRange(position, position);
1342     }
1343 
1344     updateChanged();
1345     unpauseCursor();
1346 }
1347 
onRevertedToVersionIndex(int versionIndex)1348 void AbstractByteArrayViewPrivate::onRevertedToVersionIndex(int versionIndex)
1349 {
1350     Q_UNUSED(versionIndex)
1351     // TODO: only call this if needed
1352     cancelByteEditor();
1353 }
1354 
onByteArrayReadOnlyChange(bool isByteArrayReadOnly)1355 void AbstractByteArrayViewPrivate::onByteArrayReadOnlyChange(bool isByteArrayReadOnly)
1356 {
1357     Q_Q(AbstractByteArrayView);
1358 
1359     adaptController();
1360 
1361     if (!mReadOnly) {
1362         emit q->readOnlyChanged(isByteArrayReadOnly);
1363     }
1364 }
1365 
onContentsChanged(const ArrayChangeMetricsList & changeList)1366 void AbstractByteArrayViewPrivate::onContentsChanged(const ArrayChangeMetricsList& changeList)
1367 {
1368     Q_Q(AbstractByteArrayView);
1369 
1370     pauseCursor();
1371 
1372     const bool atEnd = mTableCursor->atEnd();
1373     const Size oldLength = mTableLayout->length(); // TODO: hack for mDataCursor->adaptSelectionToChange
1374     // update lengths
1375     int oldNoOfLines = q->noOfLines();
1376     mTableLayout->setLength(mByteArrayModel->size());
1377     const int newNoOfLines = mTableLayout->noOfLines();
1378     if (oldNoOfLines != newNoOfLines) {
1379         q->setNoOfLines(newNoOfLines);
1380         const LineRange changedLines = (oldNoOfLines < newNoOfLines) ?
1381                                        LineRange(oldNoOfLines, newNoOfLines - 1) :
1382                                        LineRange(newNoOfLines, oldNoOfLines - 1);
1383         mTableRanges->addChangedOffsetLines(changedLines);
1384     }
1385 
1386     // adapt cursor(s)
1387     if (atEnd) {
1388         mTableCursor->gotoEnd();
1389     } else {
1390         mTableCursor->adaptToChanges(changeList, oldLength);
1391     }
1392 
1393     mTableRanges->adaptToChanges(changeList, oldLength);
1394     // qCDebug(LOG_OKTETA_GUI) << "Cursor:"<<mDataCursor->index()<<", selection:"<<mTableRanges->selectionStart()<<"-"<<mTableRanges->selectionEnd()
1395     //          <<", BytesPerLine: "<<mTableLayout->noOfBytesPerLine()<<endl;
1396     ensureCursorVisible();
1397 
1398     endViewUpdate();
1399 }
1400 
endViewUpdate()1401 void AbstractByteArrayViewPrivate::endViewUpdate()
1402 {
1403     updateChanged();
1404 
1405     unpauseCursor();
1406 
1407     emitSelectionUpdates();
1408 }
1409 
emitSelectionUpdates()1410 void AbstractByteArrayViewPrivate::emitSelectionUpdates()
1411 {
1412     Q_Q(AbstractByteArrayView);
1413 
1414     bool selectionChanged = false;
1415     bool hasSelectionChanged = false;
1416     mTableRanges->takeHasSelectionChanged(&hasSelectionChanged, &selectionChanged);
1417 
1418     if (selectionChanged) {
1419         emit q->selectionChanged(mTableRanges->selection());
1420     }
1421     if (hasSelectionChanged) {
1422         const bool hasSelection = mTableRanges->hasSelection();
1423         if (!mOverWrite) {
1424             emit q->cutAvailable(hasSelection);
1425         }
1426         emit q->copyAvailable(hasSelection);
1427         emit q->hasSelectedDataChanged(hasSelection);
1428     }
1429     emit q->cursorPositionChanged(cursorPosition());
1430 }
1431 
1432 #if 0
1433 void AbstractByteArrayViewPrivate::onClipboardChanged()
1434 {
1435     Q_Q(AbstractByteArrayView);
1436 
1437     // don't listen to selection changes
1438     q->disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()));
1439     selectAll(false);
1440 }
1441 #endif
1442 
1443 }
1444 
1445 #include "abstractbytearrayview_p.moc"
1446