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