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