1 #include <QApplication>
2 #include <QClipboard>
3 #include <QKeyEvent>
4 #include <QPainter>
5 #include <QScrollBar>
6 
7 #include "qhexedit.h"
8 #include <algorithm>
9 
10 
11 // ********************************************************************** Constructor, destructor
12 
QHexEdit(QWidget * parent)13 QHexEdit::QHexEdit(QWidget *parent) : QAbstractScrollArea(parent)
14 {
15     _addressArea = true;
16     _addressWidth = 4;
17     _asciiArea = true;
18     _overwriteMode = true;
19     _highlighting = true;
20     _readOnly = false;
21     _cursorPosition = 0;
22     _lastEventSize = 0;
23     _hexCharsInLine = 47;
24     _bytesPerLine = 16;
25     _editAreaIsAscii = false;
26 
27     _chunks = new Chunks(this);
28     _undoStack = new UndoStack(_chunks, this);
29 #ifdef Q_OS_WIN32
30     setFont(QFont("Courier", 10));
31 #else
32     setFont(QFont("Monospace", 10));
33 #endif
34     setAddressAreaColor(this->palette().alternateBase().color());
35     setHighlightingColor(QColor(0xff, 0xff, 0x99, 0xff));
36     setSelectionColor(this->palette().highlight().color());
37 
38     connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor()));
39     connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(adjust()));
40     connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(adjust()));
41     connect(_undoStack, SIGNAL(indexChanged(int)), this, SLOT(dataChangedPrivate(int)));
42 
43     _cursorTimer.setInterval(500);
44     _cursorTimer.start();
45 
46     setAddressWidth(4);
47     setAddressArea(true);
48     setAsciiArea(true);
49     setOverwriteMode(true);
50     setHighlighting(true);
51     setReadOnly(false);
52 
53     init();
54 
55 }
56 
~QHexEdit()57 QHexEdit::~QHexEdit()
58 {
59 }
60 
61 // ********************************************************************** Properties
62 
setAddressArea(bool addressArea)63 void QHexEdit::setAddressArea(bool addressArea)
64 {
65     _addressArea = addressArea;
66     adjust();
67     setCursorPosition(_cursorPosition);
68     viewport()->update();
69 }
70 
addressArea()71 bool QHexEdit::addressArea()
72 {
73     return _addressArea;
74 }
75 
setAddressAreaColor(const QColor & color)76 void QHexEdit::setAddressAreaColor(const QColor &color)
77 {
78     _addressAreaColor = color;
79     viewport()->update();
80 }
81 
addressAreaColor()82 QColor QHexEdit::addressAreaColor()
83 {
84     return _addressAreaColor;
85 }
86 
setAddressOffset(qint64 addressOffset)87 void QHexEdit::setAddressOffset(qint64 addressOffset)
88 {
89     _addressOffset = addressOffset;
90     adjust();
91     setCursorPosition(_cursorPosition);
92     viewport()->update();
93 }
94 
addressOffset()95 qint64 QHexEdit::addressOffset()
96 {
97     return _addressOffset;
98 }
99 
setAddressWidth(int addressWidth)100 void QHexEdit::setAddressWidth(int addressWidth)
101 {
102     _addressWidth = addressWidth;
103     adjust();
104     setCursorPosition(_cursorPosition);
105     viewport()->update();
106 }
107 
addressWidth()108 int QHexEdit::addressWidth()
109 {
110     qint64 size = _chunks->size();
111     int n = 1;
112     if (size > Q_INT64_C(0x100000000)){ n += 8; size /= Q_INT64_C(0x100000000);}
113     if (size > 0x10000){ n += 4; size /= 0x10000;}
114     if (size > 0x100){ n += 2; size /= 0x100;}
115     if (size > 0x10){ n += 1; }
116 
117     if (n > _addressWidth)
118         return n;
119     else
120         return _addressWidth;
121 }
122 
setAsciiArea(bool asciiArea)123 void QHexEdit::setAsciiArea(bool asciiArea)
124 {
125     if (!asciiArea)
126         _editAreaIsAscii = false;
127     _asciiArea = asciiArea;
128     adjust();
129     setCursorPosition(_cursorPosition);
130     viewport()->update();
131 }
132 
asciiArea()133 bool QHexEdit::asciiArea()
134 {
135     return _asciiArea;
136 }
137 
setBytesPerLine(int count)138 void QHexEdit::setBytesPerLine(int count)
139 {
140     _bytesPerLine = count;
141     _hexCharsInLine = count * 3 - 1;
142 
143     adjust();
144     setCursorPosition(_cursorPosition);
145     viewport()->update();
146 }
147 
bytesPerLine()148 int QHexEdit::bytesPerLine()
149 {
150     return _bytesPerLine;
151 }
152 
setCursorPosition(qint64 position)153 void QHexEdit::setCursorPosition(qint64 position)
154 {
155     // 1. delete old cursor
156     _blink = false;
157     viewport()->update(_cursorRect);
158 
159     // 2. Check, if cursor in range?
160     if (position > (_chunks->size() * 2 - 1))
161         position = _chunks->size() * 2  - (_overwriteMode ? 1 : 0);
162 
163     if (position < 0)
164         position = 0;
165 
166     // 3. Calc new position of cursor
167     _bPosCurrent = position / 2;
168     _pxCursorY = (int)((position / 2 - _bPosFirst) / _bytesPerLine + 1) * _pxCharHeight;
169     int x = (position % (2 * _bytesPerLine));
170     if (_editAreaIsAscii)
171     {
172         _pxCursorX = x / 2 * _pxCharWidth + _pxPosAsciiX;
173         _cursorPosition = position & 0xFFFFFFFFFFFFFFFE;
174     } else {
175         _pxCursorX = (((x / 2) * 3) + (x % 2)) * _pxCharWidth + _pxPosHexX;
176         _cursorPosition = position;
177     }
178 
179     if (_overwriteMode)
180         _cursorRect = QRect(_pxCursorX - horizontalScrollBar()->value(), _pxCursorY + _pxCursorWidth, _pxCharWidth, _pxCursorWidth);
181     else
182         _cursorRect = QRect(_pxCursorX - horizontalScrollBar()->value(), _pxCursorY - _pxCharHeight + 4, _pxCursorWidth, _pxCharHeight);
183 
184     // 4. Immediately draw new cursor
185     _blink = true;
186     viewport()->update(_cursorRect);
187     emit currentAddressChanged(_bPosCurrent);
188 }
189 
cursorPosition(QPoint pos)190 qint64 QHexEdit::cursorPosition(QPoint pos)
191 {
192     // Calc cursor position depending on a graphical position
193     qint64 result = -1;
194     int posX = pos.x() + horizontalScrollBar()->value();
195     int posY = pos.y() - 3;
196     if ((posX >= _pxPosHexX) && (posX < (_pxPosHexX + (1 + _hexCharsInLine) * _pxCharWidth)))
197     {
198         _editAreaIsAscii = false;
199         int x = (posX - _pxPosHexX) / _pxCharWidth;
200         x = (x / 3) * 2 + x % 3;
201         int y = (posY / _pxCharHeight) * 2 * _bytesPerLine;
202         result = _bPosFirst * 2 + x + y;
203     } else
204         if (_asciiArea && (posX >= _pxPosAsciiX) && (posX < (_pxPosAsciiX + (1 + _bytesPerLine) * _pxCharWidth)))
205         {
206             _editAreaIsAscii = true;
207             int x = 2 * (posX - _pxPosAsciiX) / _pxCharWidth;
208             int y = (posY / _pxCharHeight) * 2 * _bytesPerLine;
209             result = _bPosFirst * 2 + x + y;
210         }
211     return result;
212 }
213 
cursorPosition()214 qint64 QHexEdit::cursorPosition()
215 {
216     return _cursorPosition;
217 }
218 
setData(const QByteArray & ba)219 void QHexEdit::setData(const QByteArray &ba)
220 {
221     _data = ba;
222     _bData.setData(_data);
223     setData(_bData);
224 }
225 
data()226 QByteArray QHexEdit::data()
227 {
228     return _chunks->data(0, -1);
229 }
230 
setHighlighting(bool highlighting)231 void QHexEdit::setHighlighting(bool highlighting)
232 {
233     _highlighting = highlighting;
234     viewport()->update();
235 }
236 
highlighting()237 bool QHexEdit::highlighting()
238 {
239     return _highlighting;
240 }
241 
setHighlightingColor(const QColor & color)242 void QHexEdit::setHighlightingColor(const QColor &color)
243 {
244     _brushHighlighted = QBrush(color);
245     _penHighlighted = QPen(viewport()->palette().color(QPalette::WindowText));
246     viewport()->update();
247 }
248 
highlightingColor()249 QColor QHexEdit::highlightingColor()
250 {
251     return _brushHighlighted.color();
252 }
253 
setOverwriteMode(bool overwriteMode)254 void QHexEdit::setOverwriteMode(bool overwriteMode)
255 {
256     _overwriteMode = overwriteMode;
257     emit overwriteModeChanged(overwriteMode);
258 }
259 
overwriteMode()260 bool QHexEdit::overwriteMode()
261 {
262     return _overwriteMode;
263 }
264 
setSelectionColor(const QColor & color)265 void QHexEdit::setSelectionColor(const QColor &color)
266 {
267     _brushSelection = QBrush(color);
268     _penSelection = QPen(Qt::white);
269     viewport()->update();
270 }
271 
selectionColor()272 QColor QHexEdit::selectionColor()
273 {
274     return _brushSelection.color();
275 }
276 
isReadOnly()277 bool QHexEdit::isReadOnly()
278 {
279     return _readOnly;
280 }
281 
setReadOnly(bool readOnly)282 void QHexEdit::setReadOnly(bool readOnly)
283 {
284     _readOnly = readOnly;
285 }
286 
isUpperCase()287 bool QHexEdit::isUpperCase()
288 {
289     return _upperCase;
290 }
291 
setUpperCase(bool upperCase)292 void QHexEdit::setUpperCase(bool upperCase)
293 {
294     _upperCase = upperCase;
295 }
296 
297 // ********************************************************************** Access to data of qhexedit
setData(QIODevice & iODevice)298 bool QHexEdit::setData(QIODevice &iODevice)
299 {
300     bool ok = _chunks->setIODevice(iODevice);
301     init();
302     dataChangedPrivate();
303     return ok;
304 }
305 
dataAt(qint64 pos,qint64 count)306 QByteArray QHexEdit::dataAt(qint64 pos, qint64 count)
307 {
308     return _chunks->data(pos, count);
309 }
310 
write(QIODevice & iODevice,qint64 pos,qint64 count)311 bool QHexEdit::write(QIODevice &iODevice, qint64 pos, qint64 count)
312 {
313     return _chunks->write(iODevice, pos, count);
314 }
315 
316 // ********************************************************************** Char handling
insert(qint64 index,char ch)317 void QHexEdit::insert(qint64 index, char ch)
318 {
319     _undoStack->insert(index, ch);
320     refresh();
321 }
322 
remove(qint64 index,qint64 len)323 void QHexEdit::remove(qint64 index, qint64 len)
324 {
325     _undoStack->removeAt(index, len);
326     refresh();
327 }
328 
replace(qint64 index,char ch)329 void QHexEdit::replace(qint64 index, char ch)
330 {
331     _undoStack->overwrite(index, ch);
332     refresh();
333 }
334 
335 // ********************************************************************** ByteArray handling
insert(qint64 pos,const QByteArray & ba)336 void QHexEdit::insert(qint64 pos, const QByteArray &ba)
337 {
338     _undoStack->insert(pos, ba);
339     refresh();
340 }
341 
replace(qint64 pos,qint64 len,const QByteArray & ba)342 void QHexEdit::replace(qint64 pos, qint64 len, const QByteArray &ba)
343 {
344     _undoStack->overwrite(pos, (int)len, ba);
345     refresh();
346 }
347 
348 // ********************************************************************** Utility functions
ensureVisible()349 void QHexEdit::ensureVisible()
350 {
351     if (_cursorPosition < (_bPosFirst * 2))
352         verticalScrollBar()->setValue((int)(_cursorPosition / 2 / _bytesPerLine));
353     if (_cursorPosition > ((_bPosFirst + (_rowsShown - 1)*_bytesPerLine) * 2))
354         verticalScrollBar()->setValue((int)(_cursorPosition / 2 / _bytesPerLine) - _rowsShown + 1);
355     if (_pxCursorX < horizontalScrollBar()->value())
356         horizontalScrollBar()->setValue(_pxCursorX);
357     if ((_pxCursorX + _pxCharWidth) > (horizontalScrollBar()->value() + viewport()->width()))
358         horizontalScrollBar()->setValue(_pxCursorX + _pxCharWidth - viewport()->width());
359     viewport()->update();
360 }
361 
indexOf(const QByteArray & ba,qint64 from)362 qint64 QHexEdit::indexOf(const QByteArray &ba, qint64 from)
363 {
364     qint64 pos = _chunks->indexOf(ba, from);
365     if (pos > -1)
366     {
367         qint64 curPos = pos*2;
368         setCursorPosition(curPos + ba.length()*2);
369         resetSelection(curPos);
370         setSelection(curPos + ba.length()*2);
371         ensureVisible();
372     }
373     return pos;
374 }
375 
isModified()376 bool QHexEdit::isModified()
377 {
378     return _modified;
379 }
380 
lastIndexOf(const QByteArray & ba,qint64 from)381 qint64 QHexEdit::lastIndexOf(const QByteArray &ba, qint64 from)
382 {
383     qint64 pos = _chunks->lastIndexOf(ba, from);
384     if (pos > -1)
385     {
386         qint64 curPos = pos*2;
387         setCursorPosition(curPos - 1);
388         resetSelection(curPos);
389         setSelection(curPos + ba.length()*2);
390         ensureVisible();
391     }
392     return pos;
393 }
394 
redo()395 void QHexEdit::redo()
396 {
397     _undoStack->redo();
398     setCursorPosition(_chunks->pos()*(_editAreaIsAscii ? 1 : 2));
399     refresh();
400 }
401 
selectionToReadableString()402 QString QHexEdit::selectionToReadableString()
403 {
404     QByteArray ba = _chunks->data(getSelectionBegin(), getSelectionEnd() - getSelectionBegin());
405     return toReadable(ba);
406 }
407 
setFont(const QFont & font)408 void QHexEdit::setFont(const QFont &font)
409 {
410     QWidget::setFont(font);
411 
412 #if ((QT_VERSION_MINOR >= 11) && (QT_VERSION_MAJOR == 5)) || (QT_VERSION_MAJOR >= 6)
413     _pxCharWidth = fontMetrics().horizontalAdvance('2');
414 #else
415     _pxCharWidth = fontMetrics().width(QLatin1Char('2'));
416 #endif
417 
418     _pxCharHeight = fontMetrics().height();
419     _pxGapAdr = _pxCharWidth / 2;
420     _pxGapAdrHex = _pxCharWidth;
421     _pxGapHexAscii = 2 * _pxCharWidth;
422     _pxCursorWidth = _pxCharHeight / 7;
423     _pxSelectionSub = _pxCharHeight / 5;
424     viewport()->update();
425 }
426 
toReadableString()427 QString QHexEdit::toReadableString()
428 {
429     QByteArray ba = _chunks->data();
430     return toReadable(ba);
431 }
432 
undo()433 void QHexEdit::undo()
434 {
435     _undoStack->undo();
436     setCursorPosition(_chunks->pos()*(_editAreaIsAscii ? 1 : 2));
437     refresh();
438 }
439 
440 // ********************************************************************** Handle events
keyPressEvent(QKeyEvent * event)441 void QHexEdit::keyPressEvent(QKeyEvent *event)
442 {
443     // Cursor movements
444     if (event->matches(QKeySequence::MoveToNextChar))
445     {
446         qint64 pos = _cursorPosition + 1;
447         if (_editAreaIsAscii)
448             pos += 1;
449         setCursorPosition(pos);
450         resetSelection(pos);
451     }
452     if (event->matches(QKeySequence::MoveToPreviousChar))
453     {
454         qint64 pos = _cursorPosition - 1;
455         if (_editAreaIsAscii)
456             pos -= 1;
457         setCursorPosition(pos);
458         resetSelection(pos);
459     }
460     if (event->matches(QKeySequence::MoveToEndOfLine))
461     {
462         qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine)) + (2 * _bytesPerLine) - 1;
463         setCursorPosition(pos);
464         resetSelection(_cursorPosition);
465     }
466     if (event->matches(QKeySequence::MoveToStartOfLine))
467     {
468         qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine));
469         setCursorPosition(pos);
470         resetSelection(_cursorPosition);
471     }
472     if (event->matches(QKeySequence::MoveToPreviousLine))
473     {
474         setCursorPosition(_cursorPosition - (2 * _bytesPerLine));
475         resetSelection(_cursorPosition);
476     }
477     if (event->matches(QKeySequence::MoveToNextLine))
478     {
479         setCursorPosition(_cursorPosition + (2 * _bytesPerLine));
480         resetSelection(_cursorPosition);
481     }
482     if (event->matches(QKeySequence::MoveToNextPage))
483     {
484         setCursorPosition(_cursorPosition + (((_rowsShown - 1) * 2 * _bytesPerLine)));
485         resetSelection(_cursorPosition);
486     }
487     if (event->matches(QKeySequence::MoveToPreviousPage))
488     {
489         setCursorPosition(_cursorPosition - (((_rowsShown - 1) * 2 * _bytesPerLine)));
490         resetSelection(_cursorPosition);
491     }
492     if (event->matches(QKeySequence::MoveToEndOfDocument))
493     {
494         setCursorPosition(_chunks->size() * 2 );
495         resetSelection(_cursorPosition);
496     }
497     if (event->matches(QKeySequence::MoveToStartOfDocument))
498     {
499         setCursorPosition(0);
500         resetSelection(_cursorPosition);
501     }
502 
503     // Select commands
504     if (event->matches(QKeySequence::SelectAll))
505     {
506         resetSelection(0);
507         setSelection(2 * _chunks->size() + 1);
508     }
509     if (event->matches(QKeySequence::SelectNextChar))
510     {
511         qint64 pos = _cursorPosition + 1;
512         if (_editAreaIsAscii)
513             pos += 1;
514         setCursorPosition(pos);
515         setSelection(pos);
516     }
517     if (event->matches(QKeySequence::SelectPreviousChar))
518     {
519         qint64 pos = _cursorPosition - 1;
520         if (_editAreaIsAscii)
521             pos -= 1;
522         setSelection(pos);
523         setCursorPosition(pos);
524     }
525     if (event->matches(QKeySequence::SelectEndOfLine))
526     {
527         qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine)) + (2 * _bytesPerLine) - 1;
528         setCursorPosition(pos);
529         setSelection(pos);
530     }
531     if (event->matches(QKeySequence::SelectStartOfLine))
532     {
533         qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine));
534         setCursorPosition(pos);
535         setSelection(pos);
536     }
537     if (event->matches(QKeySequence::SelectPreviousLine))
538     {
539         qint64 pos = _cursorPosition - (2 * _bytesPerLine);
540         setCursorPosition(pos);
541         setSelection(pos);
542     }
543     if (event->matches(QKeySequence::SelectNextLine))
544     {
545         qint64 pos = _cursorPosition + (2 * _bytesPerLine);
546         setCursorPosition(pos);
547         setSelection(pos);
548     }
549     if (event->matches(QKeySequence::SelectNextPage))
550     {
551         qint64 pos = _cursorPosition + (((viewport()->height() / _pxCharHeight) - 1) * 2 * _bytesPerLine);
552         setCursorPosition(pos);
553         setSelection(pos);
554     }
555     if (event->matches(QKeySequence::SelectPreviousPage))
556     {
557         qint64 pos = _cursorPosition - (((viewport()->height() / _pxCharHeight) - 1) * 2 * _bytesPerLine);
558         setCursorPosition(pos);
559         setSelection(pos);
560     }
561     if (event->matches(QKeySequence::SelectEndOfDocument))
562     {
563         qint64 pos = _chunks->size() * 2;
564         setCursorPosition(pos);
565         setSelection(pos);
566     }
567     if (event->matches(QKeySequence::SelectStartOfDocument))
568     {
569         qint64 pos = 0;
570         setCursorPosition(pos);
571         setSelection(pos);
572     }
573 
574     // Edit Commands
575     if (!_readOnly)
576     {
577         /* Cut */
578         if (event->matches(QKeySequence::Cut))
579         {
580             QByteArray ba = _chunks->data(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()).toHex();
581             for (qint64 idx = 32; idx < ba.size(); idx +=33)
582                 ba.insert((int)idx, "\n");
583             QClipboard *clipboard = QApplication::clipboard();
584             clipboard->setText(ba);
585             if (_overwriteMode)
586             {
587                 qint64 len = getSelectionEnd() - getSelectionBegin();
588                 replace(getSelectionBegin(), (int)len, QByteArray((int)len, char(0)));
589             }
590             else
591             {
592                 remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin());
593             }
594             setCursorPosition(2 * getSelectionBegin());
595             resetSelection(2 * getSelectionBegin());
596         } else
597 
598         /* Paste */
599         if (event->matches(QKeySequence::Paste))
600         {
601             QClipboard *clipboard = QApplication::clipboard();
602             QByteArray ba = QByteArray().fromHex(clipboard->text().toLatin1());
603             if (_overwriteMode)
604             {
605                 ba = ba.left((int)std::min<qint64>(ba.size(), (_chunks->size() - _bPosCurrent)));
606                 replace(_bPosCurrent, ba.size(), ba);
607             }
608             else
609                 insert(_bPosCurrent, ba);
610             setCursorPosition(_cursorPosition + 2 * ba.size());
611             resetSelection(getSelectionBegin());
612         } else
613 
614         /* Delete char */
615         if (event->matches(QKeySequence::Delete))
616         {
617             if (getSelectionBegin() != getSelectionEnd())
618             {
619                 _bPosCurrent = getSelectionBegin();
620                 if (_overwriteMode)
621                 {
622                     QByteArray ba = QByteArray(getSelectionEnd() - getSelectionBegin(), char(0));
623                     replace(_bPosCurrent, ba.size(), ba);
624                 }
625                 else
626                 {
627                     remove(_bPosCurrent, getSelectionEnd() - getSelectionBegin());
628                 }
629             }
630             else
631             {
632                 if (_overwriteMode)
633                     replace(_bPosCurrent, char(0));
634                 else
635                     remove(_bPosCurrent, 1);
636             }
637             setCursorPosition(2 * _bPosCurrent);
638             resetSelection(2 * _bPosCurrent);
639         } else
640 
641         /* Backspace */
642         if ((event->key() == Qt::Key_Backspace) && (event->modifiers() == Qt::NoModifier))
643         {
644             if (getSelectionBegin() != getSelectionEnd())
645             {
646                 _bPosCurrent = getSelectionBegin();
647                 setCursorPosition(2 * _bPosCurrent);
648                 if (_overwriteMode)
649                 {
650                     QByteArray ba = QByteArray(getSelectionEnd() - getSelectionBegin(), char(0));
651                     replace(_bPosCurrent, ba.size(), ba);
652                 }
653                 else
654                 {
655                     remove(_bPosCurrent, getSelectionEnd() - getSelectionBegin());
656                 }
657                 resetSelection(2 * _bPosCurrent);
658             }
659             else
660             {
661                 bool behindLastByte = false;
662                 if ((_cursorPosition / 2) == _chunks->size())
663                     behindLastByte = true;
664 
665                 _bPosCurrent -= 1;
666                 if (_overwriteMode)
667                     replace(_bPosCurrent, char(0));
668                 else
669                     remove(_bPosCurrent, 1);
670 
671                 if (!behindLastByte)
672                     _bPosCurrent -= 1;
673 
674                 setCursorPosition(2 * _bPosCurrent);
675                 resetSelection(2 * _bPosCurrent);
676             }
677         } else
678 
679         /* undo */
680         if (event->matches(QKeySequence::Undo))
681         {
682             undo();
683         } else
684 
685         /* redo */
686         if (event->matches(QKeySequence::Redo))
687         {
688             redo();
689         } else
690 
691         if ((QApplication::keyboardModifiers() == Qt::NoModifier) ||
692             (QApplication::keyboardModifiers() == Qt::KeypadModifier) ||
693             (QApplication::keyboardModifiers() == Qt::ShiftModifier) ||
694             (QApplication::keyboardModifiers() == (Qt::AltModifier | Qt::ControlModifier)) ||
695             (QApplication::keyboardModifiers() == Qt::GroupSwitchModifier))
696         {
697             /* Hex and ascii input */
698             int key;
699             if (_editAreaIsAscii)
700                 key = (uchar)event->text()[0].toLatin1();
701             else
702                 key = int(event->text()[0].toLower().toLatin1());
703 
704             if ((((key >= '0' && key <= '9') || (key >= 'a' && key <= 'f')) && _editAreaIsAscii == false)
705                 || (key >= ' ' && _editAreaIsAscii))
706             {
707                 if (getSelectionBegin() != getSelectionEnd())
708                 {
709                     if (_overwriteMode)
710                     {
711                         qint64 len = getSelectionEnd() - getSelectionBegin();
712                         replace(getSelectionBegin(), (int)len, QByteArray((int)len, char(0)));
713                     } else
714                     {
715                         remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin());
716                         _bPosCurrent = getSelectionBegin();
717                     }
718                     setCursorPosition(2 * _bPosCurrent);
719                     resetSelection(2 * _bPosCurrent);
720                 }
721 
722                 // If insert mode, then insert a byte
723                 if (_overwriteMode == false)
724                     if ((_cursorPosition % 2) == 0)
725                         insert(_bPosCurrent, char(0));
726 
727                 // Change content
728                 if (_chunks->size() > 0)
729                 {
730                     char ch = key;
731                     if (!_editAreaIsAscii){
732                         QByteArray hexValue = _chunks->data(_bPosCurrent, 1).toHex();
733                         if ((_cursorPosition % 2) == 0)
734                             hexValue[0] = key;
735                         else
736                             hexValue[1] = key;
737                         ch = QByteArray().fromHex(hexValue)[0];
738                     }
739                     replace(_bPosCurrent, ch);
740                     if (_editAreaIsAscii)
741                         setCursorPosition(_cursorPosition + 2);
742                     else
743                         setCursorPosition(_cursorPosition + 1);
744                     resetSelection(_cursorPosition);
745                 }
746             }
747         }
748     }
749 
750     /* Copy */
751     if (event->matches(QKeySequence::Copy))
752     {
753         QByteArray ba = _chunks->data(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()).toHex();
754         for (qint64 idx = 32; idx < ba.size(); idx += 33)
755             ba.insert((int)idx, "\n");
756         if(_upperCase)
757             ba = ba.toUpper();
758         QClipboard *clipboard = QApplication::clipboard();
759         clipboard->setText(ba);
760     }
761 
762     // Switch between insert/overwrite mode
763     if ((event->key() == Qt::Key_Insert) && (event->modifiers() == Qt::NoModifier))
764     {
765         setOverwriteMode(!overwriteMode());
766         setCursorPosition(_cursorPosition);
767     }
768 
769     // switch from hex to ascii edit
770     if (event->key() == Qt::Key_Tab && !_editAreaIsAscii){
771         _editAreaIsAscii = true;
772         setCursorPosition(_cursorPosition);
773     }
774 
775     // switch from ascii to hex edit
776     if (event->key() == Qt::Key_Backtab  && _editAreaIsAscii){
777         _editAreaIsAscii = false;
778         setCursorPosition(_cursorPosition);
779     }
780 
781     refresh();
782 }
783 
mouseMoveEvent(QMouseEvent * event)784 void QHexEdit::mouseMoveEvent(QMouseEvent * event)
785 {
786     _blink = false;
787     viewport()->update();
788     qint64 actPos = cursorPosition(event->pos());
789     if (actPos >= 0)
790     {
791         setCursorPosition(actPos);
792         setSelection(actPos);
793     }
794 }
795 
mousePressEvent(QMouseEvent * event)796 void QHexEdit::mousePressEvent(QMouseEvent * event)
797 {
798     _blink = false;
799     viewport()->update();
800     qint64 cPos = cursorPosition(event->pos());
801     if (cPos >= 0)
802     {
803         resetSelection(cPos);
804         setCursorPosition(cPos);
805     }
806 }
807 
paintEvent(QPaintEvent * event)808 void QHexEdit::paintEvent(QPaintEvent *event)
809 {
810     QPainter painter(viewport());
811     int pxOfsX = horizontalScrollBar()->value();
812 
813     if (event->rect() != _cursorRect)
814     {
815         int pxPosStartY = _pxCharHeight;
816 
817         // draw some patterns if needed
818         painter.fillRect(event->rect(), viewport()->palette().color(QPalette::Base));
819         if (_addressArea)
820             painter.fillRect(QRect(-pxOfsX, event->rect().top(), _pxPosHexX - _pxGapAdrHex/2, height()), _addressAreaColor);
821         if (_asciiArea)
822         {
823             int linePos = _pxPosAsciiX - (_pxGapHexAscii / 2);
824             painter.setPen(Qt::gray);
825             painter.drawLine(linePos - pxOfsX, event->rect().top(), linePos - pxOfsX, height());
826         }
827 
828         painter.setPen(viewport()->palette().color(QPalette::WindowText));
829 
830         // paint address area
831         if (_addressArea)
832         {
833             QString address;
834             for (int row=0, pxPosY = _pxCharHeight; row <= (_dataShown.size()/_bytesPerLine); row++, pxPosY +=_pxCharHeight)
835             {
836                 address = QString("%1").arg(_bPosFirst + row*_bytesPerLine + _addressOffset, _addrDigits, 16, QChar('0'));
837                 // upper or lower case
838                 if (_upperCase)
839                     address = address.toUpper();
840 
841                 painter.drawText(_pxPosAdrX - pxOfsX, pxPosY, address);
842             }
843         }
844 
845         // paint hex and ascii area
846         QPen colStandard = QPen(viewport()->palette().color(QPalette::WindowText));
847 
848         painter.setBackgroundMode(Qt::TransparentMode);
849 
850         for (int row = 0, pxPosY = pxPosStartY; row <= _rowsShown; row++, pxPosY +=_pxCharHeight)
851         {
852             QByteArray hex;
853             int pxPosX = _pxPosHexX  - pxOfsX;
854             int pxPosAsciiX2 = _pxPosAsciiX  - pxOfsX;
855             qint64 bPosLine = (qint64)row * _bytesPerLine;
856             for (int colIdx = 0; ((bPosLine + colIdx) < _dataShown.size() && (colIdx < _bytesPerLine)); colIdx++)
857             {
858                 QColor c = viewport()->palette().color(QPalette::Base);
859                 painter.setPen(colStandard);
860 
861                 qint64 posBa = _bPosFirst + bPosLine + colIdx;
862                 if ((getSelectionBegin() <= posBa) && (getSelectionEnd() > posBa))
863                 {
864                     c = _brushSelection.color();
865                     painter.setPen(_penSelection);
866                 }
867                 else
868                 {
869                     if (_highlighting)
870                         if (_markedShown.at((int)(posBa - _bPosFirst)))
871                         {
872                             c = _brushHighlighted.color();
873                             painter.setPen(_penHighlighted);
874                         }
875                 }
876 
877                 // render hex value
878                 QRect r;
879                 if (colIdx == 0)
880                     r.setRect(pxPosX, pxPosY - _pxCharHeight + _pxSelectionSub, 2*_pxCharWidth, _pxCharHeight);
881                 else
882                     r.setRect(pxPosX - _pxCharWidth, pxPosY - _pxCharHeight + _pxSelectionSub, 3*_pxCharWidth, _pxCharHeight);
883                 painter.fillRect(r, c);
884                 hex = _hexDataShown.mid((int)((bPosLine + colIdx) * 2), 2);
885 
886                 // upper or lower case
887                 if (_upperCase)
888                     hex = hex.toUpper();
889 
890                 painter.drawText(pxPosX, pxPosY, hex);
891                 pxPosX += 3*_pxCharWidth;
892 
893                 // render ascii value
894                 if (_asciiArea)
895                 {
896                     int ch = (uchar)_dataShown.at((int)(bPosLine + colIdx));
897                     if ( ch < 0x20 )
898                         ch = '.';
899                     r.setRect(pxPosAsciiX2, pxPosY - _pxCharHeight + _pxSelectionSub, _pxCharWidth, _pxCharHeight);
900                     painter.fillRect(r, c);
901                     painter.drawText(pxPosAsciiX2, pxPosY, QChar(ch));
902                     pxPosAsciiX2 += _pxCharWidth;
903                 }
904             }
905         }
906         painter.setBackgroundMode(Qt::TransparentMode);
907         painter.setPen(viewport()->palette().color(QPalette::WindowText));
908     }
909 
910     // paint cursor
911     if (_blink && !_readOnly && hasFocus())
912         painter.fillRect(_cursorRect, this->palette().color(QPalette::WindowText));
913     else
914         painter.drawRect(QRect(_pxCursorX - pxOfsX, _pxCursorY - _pxCharHeight + 2, _pxCharWidth, _pxCharHeight - 1));
915 
916     // emit event, if size has changed
917     if (_lastEventSize != _chunks->size())
918     {
919         _lastEventSize = _chunks->size();
920         emit currentSizeChanged(_lastEventSize);
921     }
922 }
923 
resizeEvent(QResizeEvent *)924 void QHexEdit::resizeEvent(QResizeEvent *)
925 {
926     adjust();
927 }
928 
focusNextPrevChild(bool next)929 bool QHexEdit::focusNextPrevChild(bool next)
930 {
931     if (_addressArea)
932     {
933         if ((next && _editAreaIsAscii) || (!next && !_editAreaIsAscii))
934             return QWidget::focusNextPrevChild(next);
935         else
936             return false;
937     }
938     else
939     {
940         return QWidget::focusNextPrevChild(next);
941     }
942 }
943 
944 // ********************************************************************** Handle selections
resetSelection()945 void QHexEdit::resetSelection()
946 {
947     _bSelectionBegin = _bSelectionInit;
948     _bSelectionEnd = _bSelectionInit;
949 }
950 
resetSelection(qint64 pos)951 void QHexEdit::resetSelection(qint64 pos)
952 {
953     pos = pos / 2 ;
954     if (pos < 0)
955         pos = 0;
956     if (pos > _chunks->size())
957         pos = _chunks->size();
958 
959     _bSelectionInit = pos;
960     _bSelectionBegin = pos;
961     _bSelectionEnd = pos;
962 }
963 
setSelection(qint64 pos)964 void QHexEdit::setSelection(qint64 pos)
965 {
966     pos = pos / 2;
967     if (pos < 0)
968         pos = 0;
969     if (pos > _chunks->size())
970         pos = _chunks->size();
971 
972     if (pos >= _bSelectionInit)
973     {
974         _bSelectionEnd = pos;
975         _bSelectionBegin = _bSelectionInit;
976     }
977     else
978     {
979         _bSelectionBegin = pos;
980         _bSelectionEnd = _bSelectionInit;
981     }
982 }
983 
getSelectionBegin()984 int QHexEdit::getSelectionBegin()
985 {
986     return (int)_bSelectionBegin;
987 }
988 
getSelectionEnd()989 int QHexEdit::getSelectionEnd()
990 {
991     return (int)_bSelectionEnd;
992 }
993 
994 // ********************************************************************** Private utility functions
init()995 void QHexEdit::init()
996 {
997     _undoStack->clear();
998     setAddressOffset(0);
999     resetSelection(0);
1000     setCursorPosition(0);
1001     verticalScrollBar()->setValue(0);
1002     _modified = false;
1003 }
1004 
adjust()1005 void QHexEdit::adjust()
1006 {
1007     // recalc Graphics
1008     if (_addressArea)
1009     {
1010         _addrDigits = addressWidth();
1011         _pxPosHexX = _pxGapAdr + _addrDigits*_pxCharWidth + _pxGapAdrHex;
1012     }
1013     else
1014         _pxPosHexX = _pxGapAdrHex;
1015     _pxPosAdrX = _pxGapAdr;
1016     _pxPosAsciiX = _pxPosHexX + _hexCharsInLine * _pxCharWidth + _pxGapHexAscii;
1017 
1018     // set horizontalScrollBar()
1019     int pxWidth = _pxPosAsciiX;
1020     if (_asciiArea)
1021         pxWidth += _bytesPerLine*_pxCharWidth;
1022     horizontalScrollBar()->setRange(0, pxWidth - viewport()->width());
1023     horizontalScrollBar()->setPageStep(viewport()->width());
1024 
1025     // set verticalScrollbar()
1026     _rowsShown = ((viewport()->height()-4)/_pxCharHeight);
1027     int lineCount = (int)(_chunks->size() / (qint64)_bytesPerLine) + 1;
1028     verticalScrollBar()->setRange(0, lineCount - _rowsShown);
1029     verticalScrollBar()->setPageStep(_rowsShown);
1030 
1031     int value = verticalScrollBar()->value();
1032     _bPosFirst = (qint64)value * _bytesPerLine;
1033     _bPosLast = _bPosFirst + ((qint64)_rowsShown * _bytesPerLine) - 1;
1034     if (_bPosLast >= _chunks->size())
1035         _bPosLast = _chunks->size() - 1;
1036     readBuffers();
1037     setCursorPosition(_cursorPosition);
1038 }
1039 
dataChangedPrivate(int)1040 void QHexEdit::dataChangedPrivate(int)
1041 {
1042     _modified = _undoStack->index() != 0;
1043     adjust();
1044     emit dataChanged();
1045 }
1046 
refresh()1047 void QHexEdit::refresh()
1048 {
1049     ensureVisible();
1050     readBuffers();
1051 }
1052 
readBuffers()1053 void QHexEdit::readBuffers()
1054 {
1055     _dataShown = _chunks->data(_bPosFirst, _bPosLast - _bPosFirst + _bytesPerLine + 1, &_markedShown);
1056     _hexDataShown = QByteArray(_dataShown.toHex());
1057 }
1058 
toReadable(const QByteArray & ba)1059 QString QHexEdit::toReadable(const QByteArray &ba)
1060 {
1061     QString result;
1062 
1063     for (int i=0; i < ba.size(); i += 16)
1064     {
1065         QString addrStr = QString("%1").arg(_addressOffset + i, addressWidth(), 16, QChar('0'));
1066         QString hexStr;
1067         QString ascStr;
1068         for (int j=0; j<16; j++)
1069         {
1070             if ((i + j) < ba.size())
1071             {
1072                 hexStr.append(" ").append(ba.mid(i+j, 1).toHex());
1073                 char ch = ba[i + j];
1074                 if ((ch < 0x20) || (ch > 0x7e))
1075                         ch = '.';
1076                 ascStr.append(QChar(ch));
1077             }
1078         }
1079         result += addrStr + " " + QString("%1").arg(hexStr, -48) + "  " + QString("%1").arg(ascStr, -17) + "\n";
1080     }
1081     return result;
1082 }
1083 
updateCursor()1084 void QHexEdit::updateCursor()
1085 {
1086     if (_blink)
1087         _blink = false;
1088     else
1089         _blink = true;
1090     viewport()->update(_cursorRect);
1091 }
1092