2016-07-10 17:07:36 +08:00
|
|
|
#include <QApplication>
|
|
|
|
#include <QClipboard>
|
|
|
|
#include <QKeyEvent>
|
|
|
|
#include <QPainter>
|
|
|
|
#include <QScrollBar>
|
|
|
|
|
|
|
|
#include "qhexedit.h"
|
|
|
|
|
|
|
|
const int HEXCHARS_IN_LINE = 47;
|
|
|
|
const int BYTES_PER_LINE = 16;
|
|
|
|
|
|
|
|
|
|
|
|
// ********************************************************************** Constructor, destructor
|
|
|
|
|
|
|
|
QHexEdit::QHexEdit(QWidget *parent) : QAbstractScrollArea(parent)
|
|
|
|
{
|
|
|
|
_chunks = new Chunks();
|
|
|
|
_undoStack = new UndoStack(_chunks, this);
|
|
|
|
#ifdef Q_OS_WIN32
|
|
|
|
setFont(QFont("Courier", 10));
|
|
|
|
#else
|
|
|
|
setFont(QFont("Monospace", 10));
|
|
|
|
#endif
|
|
|
|
setAddressAreaColor(this->palette().alternateBase().color());
|
|
|
|
setHighlightingColor(QColor(0xff, 0xff, 0x99, 0xff));
|
|
|
|
setSelectionColor(this->palette().highlight().color());
|
|
|
|
|
|
|
|
connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor()));
|
|
|
|
connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(adjust()));
|
|
|
|
connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(adjust()));
|
|
|
|
connect(_undoStack, SIGNAL(indexChanged(int)), this, SLOT(dataChangedPrivate(int)));
|
|
|
|
|
|
|
|
_cursorTimer.setInterval(500);
|
|
|
|
_cursorTimer.start();
|
|
|
|
|
|
|
|
setAddressWidth(4);
|
|
|
|
setAddressArea(true);
|
|
|
|
setAsciiArea(true);
|
|
|
|
setOverwriteMode(true);
|
|
|
|
setHighlighting(true);
|
|
|
|
setReadOnly(false);
|
|
|
|
|
|
|
|
init();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
QHexEdit::~QHexEdit()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// ********************************************************************** Properties
|
|
|
|
|
|
|
|
void QHexEdit::setAddressArea(bool addressArea)
|
|
|
|
{
|
|
|
|
_addressArea = addressArea;
|
|
|
|
adjust();
|
|
|
|
setCursorPosition(_cursorPosition);
|
|
|
|
viewport()->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QHexEdit::addressArea()
|
|
|
|
{
|
|
|
|
return _addressArea;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::setAddressAreaColor(const QColor &color)
|
|
|
|
{
|
|
|
|
_addressAreaColor = color;
|
|
|
|
viewport()->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
QColor QHexEdit::addressAreaColor()
|
|
|
|
{
|
|
|
|
return _addressAreaColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::setAddressOffset(qint64 addressOffset)
|
|
|
|
{
|
|
|
|
_addressOffset = addressOffset;
|
|
|
|
adjust();
|
|
|
|
setCursorPosition(_cursorPosition);
|
|
|
|
viewport()->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
qint64 QHexEdit::addressOffset()
|
|
|
|
{
|
|
|
|
return _addressOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::setAddressWidth(int addressWidth)
|
|
|
|
{
|
|
|
|
_addressWidth = addressWidth;
|
|
|
|
adjust();
|
|
|
|
setCursorPosition(_cursorPosition);
|
|
|
|
viewport()->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
int QHexEdit::addressWidth()
|
|
|
|
{
|
|
|
|
qint64 size = _chunks->size();
|
|
|
|
int n = 1;
|
|
|
|
if (size > Q_INT64_C(0x100000000)){ n += 8; size /= Q_INT64_C(0x100000000);}
|
|
|
|
if (size > 0x10000){ n += 4; size /= 0x10000;}
|
|
|
|
if (size > 0x100){ n += 2; size /= 0x100;}
|
|
|
|
if (size > 0x10){ n += 1; size /= 0x10;}
|
|
|
|
|
|
|
|
if (n > _addressWidth)
|
|
|
|
return n;
|
|
|
|
else
|
|
|
|
return _addressWidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::setAsciiArea(bool asciiArea)
|
|
|
|
{
|
|
|
|
_asciiArea = asciiArea;
|
|
|
|
viewport()->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QHexEdit::asciiArea()
|
|
|
|
{
|
|
|
|
return _asciiArea;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::setCursorPosition(qint64 position)
|
|
|
|
{
|
|
|
|
// 1. delete old cursor
|
|
|
|
_blink = false;
|
|
|
|
viewport()->update(_cursorRect);
|
|
|
|
|
|
|
|
// 2. Check, if cursor in range?
|
|
|
|
if (_overwriteMode && (position > (_chunks->size() * 2 - 1)))
|
|
|
|
position = _chunks->size() * 2 - 1;
|
|
|
|
if (!_overwriteMode && (position > (_chunks->size() * 2)))
|
|
|
|
position = _chunks->size() * 2;
|
|
|
|
if (position < 0)
|
|
|
|
position = 0;
|
|
|
|
|
|
|
|
// 3. Calc new position of curser
|
|
|
|
_cursorPosition = position;
|
|
|
|
_bPosCurrent = position / 2;
|
|
|
|
_pxCursorY = ((position/2 - _bPosFirst) / BYTES_PER_LINE + 1) * _pxCharHeight;
|
|
|
|
int x = (position % (2 * BYTES_PER_LINE));
|
|
|
|
_pxCursorX = (((x / 2) * 3) + (x % 2)) * _pxCharWidth + _pxPosHexX;
|
|
|
|
|
|
|
|
if (_overwriteMode)
|
|
|
|
_cursorRect = QRect(_pxCursorX - horizontalScrollBar()->value(), _pxCursorY + _pxCursorWidth, _pxCharWidth, _pxCursorWidth);
|
|
|
|
else
|
|
|
|
_cursorRect = QRect(_pxCursorX - horizontalScrollBar()->value(), _pxCursorY - _pxCharHeight + 4, _pxCursorWidth, _pxCharHeight);
|
|
|
|
|
|
|
|
// 4. Immiadately draw new cursor
|
|
|
|
_blink = true;
|
|
|
|
viewport()->update(_cursorRect);
|
|
|
|
emit currentAddressChanged(_bPosCurrent);
|
|
|
|
}
|
|
|
|
|
|
|
|
qint64 QHexEdit::cursorPosition(QPoint pos)
|
|
|
|
{
|
|
|
|
// Calc cursorposition depending on a graphical position
|
|
|
|
qint64 result = -1;
|
|
|
|
int posX = pos.x() + horizontalScrollBar()->value();
|
|
|
|
int posY = pos.y() - 3;
|
|
|
|
if ((posX >= _pxPosHexX) && (posX < (_pxPosHexX + (1 + HEXCHARS_IN_LINE) * _pxCharWidth)))
|
|
|
|
{
|
|
|
|
int x = (posX - _pxPosHexX - _pxCharWidth / 2) / _pxCharWidth;
|
|
|
|
x = (x / 3) * 2 + x % 3;
|
|
|
|
int y = (posY / _pxCharHeight) * 2 * BYTES_PER_LINE;
|
|
|
|
result = _bPosFirst * 2 + x + y;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
qint64 QHexEdit::cursorPosition()
|
|
|
|
{
|
|
|
|
return _cursorPosition;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::setData(const QByteArray &ba)
|
|
|
|
{
|
|
|
|
_data = ba;
|
|
|
|
_bData.setData(_data);
|
|
|
|
setData(_bData);
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray QHexEdit::data()
|
|
|
|
{
|
|
|
|
return _chunks->data(0, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::setHighlighting(bool highlighting)
|
|
|
|
{
|
|
|
|
_highlighting = highlighting;
|
|
|
|
viewport()->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QHexEdit::highlighting()
|
|
|
|
{
|
|
|
|
return _highlighting;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::setHighlightingColor(const QColor &color)
|
|
|
|
{
|
|
|
|
_brushHighlighted = QBrush(color);
|
|
|
|
_penHighlighted = QPen(viewport()->palette().color(QPalette::WindowText));
|
|
|
|
viewport()->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
QColor QHexEdit::highlightingColor()
|
|
|
|
{
|
|
|
|
return _brushHighlighted.color();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::setOverwriteMode(bool overwriteMode)
|
|
|
|
{
|
|
|
|
_overwriteMode = overwriteMode;
|
|
|
|
emit overwriteModeChanged(overwriteMode);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QHexEdit::overwriteMode()
|
|
|
|
{
|
|
|
|
return _overwriteMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::setSelectionColor(const QColor &color)
|
|
|
|
{
|
|
|
|
_brushSelection = QBrush(color);
|
|
|
|
_penSelection = QPen(Qt::white);
|
|
|
|
viewport()->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
QColor QHexEdit::selectionColor()
|
|
|
|
{
|
|
|
|
return _brushSelection.color();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QHexEdit::isReadOnly()
|
|
|
|
{
|
|
|
|
return _readOnly;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::setReadOnly(bool readOnly)
|
|
|
|
{
|
|
|
|
_readOnly = readOnly;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QHexEdit::isUpperCase()
|
|
|
|
{
|
|
|
|
return _upperCase;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::setUpperCase(bool upperCase)
|
|
|
|
{
|
|
|
|
_upperCase = upperCase;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ********************************************************************** Access to data of qhexedit
|
|
|
|
bool QHexEdit::setData(QIODevice &iODevice)
|
|
|
|
{
|
|
|
|
bool ok = _chunks->setIODevice(iODevice);
|
|
|
|
init();
|
|
|
|
dataChangedPrivate();
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray QHexEdit::dataAt(qint64 pos, qint64 count)
|
|
|
|
{
|
|
|
|
return _chunks->data(pos, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QHexEdit::write(QIODevice &iODevice, qint64 pos, qint64 count)
|
|
|
|
{
|
|
|
|
return _chunks->write(iODevice, pos, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ********************************************************************** Char handling
|
|
|
|
void QHexEdit::insert(qint64 index, char ch)
|
|
|
|
{
|
|
|
|
_undoStack->insert(index, ch);
|
|
|
|
refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::remove(qint64 index, qint64 len)
|
|
|
|
{
|
|
|
|
_undoStack->removeAt(index, len);
|
|
|
|
refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::replace(qint64 index, char ch)
|
|
|
|
{
|
|
|
|
_undoStack->overwrite(index, ch);
|
|
|
|
refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ********************************************************************** ByteArray handling
|
|
|
|
void QHexEdit::insert(qint64 pos, const QByteArray &ba)
|
|
|
|
{
|
|
|
|
_undoStack->insert(pos, ba);
|
|
|
|
refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::replace(qint64 pos, qint64 len, const QByteArray &ba)
|
|
|
|
{
|
|
|
|
_undoStack->overwrite(pos, len, ba);
|
|
|
|
refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ********************************************************************** Utility functions
|
|
|
|
void QHexEdit::ensureVisible()
|
|
|
|
{
|
|
|
|
if (_cursorPosition < (_bPosFirst * 2))
|
|
|
|
verticalScrollBar()->setValue((int)(_cursorPosition / 2 / BYTES_PER_LINE));
|
|
|
|
if (_cursorPosition > ((_bPosFirst + (_rowsShown - 1)*BYTES_PER_LINE) * 2))
|
|
|
|
verticalScrollBar()->setValue((int)(_cursorPosition / 2 / BYTES_PER_LINE) - _rowsShown + 1);
|
|
|
|
if (_pxCursorX < horizontalScrollBar()->value())
|
|
|
|
horizontalScrollBar()->setValue(_pxCursorX);
|
|
|
|
if ((_pxCursorX + _pxCharWidth) > (horizontalScrollBar()->value() + viewport()->width()))
|
|
|
|
horizontalScrollBar()->setValue(_pxCursorX + _pxCharWidth - viewport()->width());
|
|
|
|
viewport()->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
qint64 QHexEdit::indexOf(const QByteArray &ba, qint64 from)
|
|
|
|
{
|
|
|
|
qint64 pos = _chunks->indexOf(ba, from);
|
|
|
|
if (pos > -1)
|
|
|
|
{
|
|
|
|
qint64 curPos = pos*2;
|
|
|
|
setCursorPosition(curPos + ba.length()*2);
|
|
|
|
resetSelection(curPos);
|
|
|
|
setSelection(curPos + ba.length()*2);
|
|
|
|
ensureVisible();
|
|
|
|
}
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QHexEdit::isModified()
|
|
|
|
{
|
|
|
|
return _modified;
|
|
|
|
}
|
|
|
|
|
|
|
|
qint64 QHexEdit::lastIndexOf(const QByteArray &ba, qint64 from)
|
|
|
|
{
|
|
|
|
qint64 pos = _chunks->lastIndexOf(ba, from);
|
|
|
|
if (pos > -1)
|
|
|
|
{
|
|
|
|
qint64 curPos = pos*2;
|
|
|
|
setCursorPosition(curPos - 1);
|
|
|
|
resetSelection(curPos);
|
|
|
|
setSelection(curPos + ba.length()*2);
|
|
|
|
ensureVisible();
|
|
|
|
}
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::redo()
|
|
|
|
{
|
|
|
|
_undoStack->redo();
|
|
|
|
setCursorPosition(_chunks->pos()*2);
|
|
|
|
refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QHexEdit::selectionToReadableString()
|
|
|
|
{
|
|
|
|
QByteArray ba = _chunks->data(getSelectionBegin(), getSelectionEnd() - getSelectionBegin());
|
|
|
|
return toReadable(ba);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::setFont(const QFont &font)
|
|
|
|
{
|
|
|
|
QWidget::setFont(font);
|
|
|
|
_pxCharWidth = fontMetrics().width(QLatin1Char('2'));
|
|
|
|
_pxCharHeight = fontMetrics().height();
|
|
|
|
_pxGapAdr = _pxCharWidth / 2;
|
|
|
|
_pxGapAdrHex = _pxCharWidth;
|
|
|
|
_pxGapHexAscii = 2 * _pxCharWidth;
|
|
|
|
_pxCursorWidth = _pxCharHeight / 7;
|
|
|
|
_pxSelectionSub = _pxCharHeight / 5;
|
|
|
|
viewport()->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QHexEdit::toReadableString()
|
|
|
|
{
|
|
|
|
QByteArray ba = _chunks->data();
|
|
|
|
return toReadable(ba);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::undo()
|
|
|
|
{
|
|
|
|
_undoStack->undo();
|
|
|
|
setCursorPosition(_chunks->pos()*2);
|
|
|
|
refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ********************************************************************** Handle events
|
|
|
|
void QHexEdit::keyPressEvent(QKeyEvent *event)
|
|
|
|
{
|
|
|
|
// Cursor movements
|
|
|
|
if (event->matches(QKeySequence::MoveToNextChar))
|
|
|
|
{
|
|
|
|
setCursorPosition(_cursorPosition + 1);
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::MoveToPreviousChar))
|
|
|
|
{
|
|
|
|
setCursorPosition(_cursorPosition - 1);
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::MoveToEndOfLine))
|
|
|
|
{
|
|
|
|
setCursorPosition(_cursorPosition | (2 * BYTES_PER_LINE -1));
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::MoveToStartOfLine))
|
|
|
|
{
|
|
|
|
setCursorPosition(_cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)));
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::MoveToPreviousLine))
|
|
|
|
{
|
|
|
|
setCursorPosition(_cursorPosition - (2 * BYTES_PER_LINE));
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::MoveToNextLine))
|
|
|
|
{
|
|
|
|
setCursorPosition(_cursorPosition + (2 * BYTES_PER_LINE));
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::MoveToNextPage))
|
|
|
|
{
|
|
|
|
setCursorPosition(_cursorPosition + (((_rowsShown - 1) * 2 * BYTES_PER_LINE)));
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::MoveToPreviousPage))
|
|
|
|
{
|
|
|
|
setCursorPosition(_cursorPosition - (((_rowsShown - 1) * 2 * BYTES_PER_LINE)));
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::MoveToEndOfDocument))
|
|
|
|
{
|
|
|
|
setCursorPosition(_chunks->size() * 2);
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::MoveToStartOfDocument))
|
|
|
|
{
|
|
|
|
setCursorPosition(0);
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Select commands
|
|
|
|
if (event->matches(QKeySequence::SelectAll))
|
|
|
|
{
|
|
|
|
resetSelection(0);
|
|
|
|
setSelection(2*_chunks->size() + 1);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectNextChar))
|
|
|
|
{
|
|
|
|
qint64 pos = _cursorPosition + 1;
|
|
|
|
setCursorPosition(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectPreviousChar))
|
|
|
|
{
|
|
|
|
qint64 pos = _cursorPosition - 1;
|
|
|
|
setSelection(pos);
|
|
|
|
setCursorPosition(pos);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectEndOfLine))
|
|
|
|
{
|
|
|
|
qint64 pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)) + (2 * BYTES_PER_LINE);
|
|
|
|
setCursorPosition(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectStartOfLine))
|
|
|
|
{
|
|
|
|
qint64 pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE));
|
|
|
|
setCursorPosition(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectPreviousLine))
|
|
|
|
{
|
|
|
|
qint64 pos = _cursorPosition - (2 * BYTES_PER_LINE);
|
|
|
|
setCursorPosition(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectNextLine))
|
|
|
|
{
|
|
|
|
qint64 pos = _cursorPosition + (2 * BYTES_PER_LINE);
|
|
|
|
setCursorPosition(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectNextPage))
|
|
|
|
{
|
|
|
|
qint64 pos = _cursorPosition + (((viewport()->height() / _pxCharHeight) - 1) * 2 * BYTES_PER_LINE);
|
|
|
|
setCursorPosition(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectPreviousPage))
|
|
|
|
{
|
|
|
|
qint64 pos = _cursorPosition - (((viewport()->height() / _pxCharHeight) - 1) * 2 * BYTES_PER_LINE);
|
|
|
|
setCursorPosition(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectEndOfDocument))
|
|
|
|
{
|
|
|
|
qint64 pos = _chunks->size() * 2;
|
|
|
|
setCursorPosition(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectStartOfDocument))
|
|
|
|
{
|
|
|
|
qint64 pos = 0;
|
|
|
|
setCursorPosition(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Edit Commands
|
|
|
|
if (!_readOnly)
|
|
|
|
{
|
|
|
|
if ((QApplication::keyboardModifiers() == Qt::NoModifier) ||
|
|
|
|
(QApplication::keyboardModifiers() == Qt::KeypadModifier))
|
|
|
|
{
|
|
|
|
/* Hex input */
|
|
|
|
int key = int(event->text()[0].toLatin1());
|
|
|
|
if ((key>='0' && key<='9') || (key>='a' && key <= 'f'))
|
|
|
|
{
|
|
|
|
if (getSelectionBegin() != getSelectionEnd())
|
|
|
|
{
|
|
|
|
if (_overwriteMode)
|
|
|
|
{
|
|
|
|
qint64 len = getSelectionEnd() - getSelectionBegin();
|
|
|
|
replace(getSelectionBegin(), (int)len, QByteArray((int)len, char(0)));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin());
|
|
|
|
_bPosCurrent = getSelectionBegin();
|
|
|
|
}
|
|
|
|
setCursorPosition(2*_bPosCurrent);
|
|
|
|
resetSelection(2*_bPosCurrent);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If insert mode, then insert a byte
|
|
|
|
if (_overwriteMode == false)
|
|
|
|
if ((_cursorPosition % 2) == 0)
|
|
|
|
insert(_bPosCurrent, char(0));
|
|
|
|
|
|
|
|
// Change content
|
|
|
|
if (_chunks->size() > 0)
|
|
|
|
{
|
|
|
|
QByteArray hexValue = _chunks->data(_bPosCurrent, 1).toHex();
|
|
|
|
if ((_cursorPosition % 2) == 0)
|
|
|
|
hexValue[0] = key;
|
|
|
|
else
|
|
|
|
hexValue[1] = key;
|
|
|
|
|
|
|
|
replace(_bPosCurrent, QByteArray().fromHex(hexValue)[0]);
|
|
|
|
|
|
|
|
setCursorPosition(_cursorPosition + 1);
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cut */
|
|
|
|
if (event->matches(QKeySequence::Cut))
|
|
|
|
{
|
|
|
|
QByteArray ba = _chunks->data(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()).toHex();
|
|
|
|
for (qint64 idx = 32; idx < ba.size(); idx +=33)
|
|
|
|
ba.insert(idx, "\n");
|
|
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
|
|
clipboard->setText(ba);
|
|
|
|
if (_overwriteMode)
|
|
|
|
{
|
|
|
|
qint64 len = getSelectionEnd() - getSelectionBegin();
|
|
|
|
replace(getSelectionBegin(), (int)len, QByteArray((int)len, char(0)));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin());
|
|
|
|
}
|
|
|
|
setCursorPosition(2*getSelectionBegin());
|
|
|
|
resetSelection(2*getSelectionBegin());
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Paste */
|
|
|
|
if (event->matches(QKeySequence::Paste))
|
|
|
|
{
|
|
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
|
|
QByteArray ba = QByteArray().fromHex(clipboard->text().toLatin1());
|
|
|
|
if (_overwriteMode)
|
|
|
|
replace(_bPosCurrent, ba.size(), ba);
|
|
|
|
else
|
|
|
|
insert(_bPosCurrent, ba);
|
|
|
|
setCursorPosition(_cursorPosition + 2 * ba.size());
|
|
|
|
resetSelection(getSelectionBegin());
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Delete char */
|
|
|
|
if (event->matches(QKeySequence::Delete))
|
|
|
|
{
|
|
|
|
if (getSelectionBegin() != getSelectionEnd())
|
|
|
|
{
|
|
|
|
_bPosCurrent = getSelectionBegin();
|
|
|
|
if (_overwriteMode)
|
|
|
|
{
|
|
|
|
QByteArray ba = QByteArray(getSelectionEnd() - getSelectionBegin(), char(0));
|
|
|
|
replace(_bPosCurrent, ba.size(), ba);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
remove(_bPosCurrent, getSelectionEnd() - getSelectionBegin());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (_overwriteMode)
|
|
|
|
replace(_bPosCurrent, char(0));
|
|
|
|
else
|
|
|
|
remove(_bPosCurrent, 1);
|
|
|
|
}
|
|
|
|
setCursorPosition(2 * _bPosCurrent);
|
|
|
|
resetSelection(2 * _bPosCurrent);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Backspace */
|
|
|
|
if ((event->key() == Qt::Key_Backspace) && (event->modifiers() == Qt::NoModifier))
|
|
|
|
{
|
|
|
|
if (getSelectionBegin() != getSelectionEnd())
|
|
|
|
{
|
|
|
|
_bPosCurrent = getSelectionBegin();
|
|
|
|
setCursorPosition(2 * _bPosCurrent);
|
|
|
|
if (_overwriteMode)
|
|
|
|
{
|
|
|
|
QByteArray ba = QByteArray(getSelectionEnd() - getSelectionBegin(), char(0));
|
|
|
|
replace(_bPosCurrent, ba.size(), ba);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
remove(_bPosCurrent, getSelectionEnd() - getSelectionBegin());
|
|
|
|
}
|
|
|
|
resetSelection(2 * _bPosCurrent);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bool behindLastByte = false;
|
|
|
|
if ((_cursorPosition / 2) == _chunks->size())
|
|
|
|
behindLastByte = true;
|
|
|
|
|
|
|
|
_bPosCurrent -= 1;
|
|
|
|
if (_overwriteMode)
|
|
|
|
replace(_bPosCurrent, char(0));
|
|
|
|
else
|
|
|
|
remove(_bPosCurrent, 1);
|
|
|
|
|
|
|
|
if (!behindLastByte)
|
|
|
|
_bPosCurrent -= 1;
|
|
|
|
|
|
|
|
setCursorPosition(2 * _bPosCurrent);
|
|
|
|
resetSelection(2 * _bPosCurrent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* undo */
|
|
|
|
if (event->matches(QKeySequence::Undo))
|
|
|
|
{
|
|
|
|
undo();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* redo */
|
|
|
|
if (event->matches(QKeySequence::Redo))
|
|
|
|
{
|
|
|
|
redo();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy */
|
|
|
|
if (event->matches(QKeySequence::Copy))
|
|
|
|
{
|
|
|
|
QByteArray ba = _chunks->data(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()).toHex();
|
2016-10-10 14:05:04 +08:00
|
|
|
for (qint64 idx = 32; idx < ba.size(); idx += 33)
|
2016-07-10 17:07:36 +08:00
|
|
|
ba.insert(idx, "\n");
|
2016-10-10 14:05:04 +08:00
|
|
|
if(_upperCase)
|
|
|
|
ba = ba.toUpper();
|
2016-07-10 17:07:36 +08:00
|
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
|
|
clipboard->setText(ba);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Switch between insert/overwrite mode
|
|
|
|
if ((event->key() == Qt::Key_Insert) && (event->modifiers() == Qt::NoModifier))
|
|
|
|
{
|
|
|
|
setOverwriteMode(!overwriteMode());
|
|
|
|
setCursorPosition(_cursorPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ESC handler
|
|
|
|
if (event->key() == Qt::Key_Escape && parentWidget() != NULL)
|
|
|
|
{
|
|
|
|
parentWidget()->close();
|
|
|
|
}
|
|
|
|
|
|
|
|
refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::mouseMoveEvent(QMouseEvent * event)
|
|
|
|
{
|
|
|
|
_blink = false;
|
|
|
|
viewport()->update();
|
|
|
|
qint64 actPos = cursorPosition(event->pos());
|
|
|
|
if (actPos >= 0)
|
|
|
|
{
|
|
|
|
setCursorPosition(actPos);
|
|
|
|
setSelection(actPos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::mousePressEvent(QMouseEvent * event)
|
|
|
|
{
|
|
|
|
_blink = false;
|
|
|
|
viewport()->update();
|
|
|
|
qint64 cPos = cursorPosition(event->pos());
|
|
|
|
if (cPos >= 0)
|
|
|
|
{
|
|
|
|
resetSelection(cPos);
|
|
|
|
setCursorPosition(cPos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::paintEvent(QPaintEvent *event)
|
|
|
|
{
|
|
|
|
QPainter painter(viewport());
|
|
|
|
|
|
|
|
if (event->rect() != _cursorRect)
|
|
|
|
{
|
|
|
|
// process some useful calculations
|
|
|
|
int pxOfsX = horizontalScrollBar()->value();
|
|
|
|
int pxPosStartY = _pxCharHeight;
|
|
|
|
|
|
|
|
// draw some patterns if needed
|
|
|
|
painter.fillRect(event->rect(), viewport()->palette().color(QPalette::Base));
|
|
|
|
if (_addressArea)
|
|
|
|
painter.fillRect(QRect(-pxOfsX, event->rect().top(), _pxPosHexX - _pxGapAdrHex/2 - pxOfsX, height()), _addressAreaColor);
|
|
|
|
if (_asciiArea)
|
|
|
|
{
|
|
|
|
int linePos = _pxPosAsciiX - (_pxGapHexAscii / 2);
|
|
|
|
painter.setPen(Qt::gray);
|
|
|
|
painter.drawLine(linePos - pxOfsX, event->rect().top(), linePos - pxOfsX, height());
|
|
|
|
}
|
|
|
|
|
|
|
|
painter.setPen(viewport()->palette().color(QPalette::WindowText));
|
|
|
|
|
|
|
|
// paint address area
|
|
|
|
if (_addressArea)
|
|
|
|
{
|
|
|
|
QString address;
|
|
|
|
for (int row=0, pxPosY = _pxCharHeight; row <= (_dataShown.size()/BYTES_PER_LINE); row++, pxPosY +=_pxCharHeight)
|
|
|
|
{
|
|
|
|
address = QString("%1").arg(_bPosFirst + row*BYTES_PER_LINE + _addressOffset, _addrDigits, 16, QChar('0'));
|
|
|
|
// upper or lower case
|
|
|
|
if (_upperCase)
|
|
|
|
address = address.toUpper();
|
|
|
|
|
|
|
|
painter.drawText(_pxPosAdrX - pxOfsX, pxPosY, address);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// paint hex and ascii area
|
|
|
|
QPen colStandard = QPen(viewport()->palette().color(QPalette::WindowText));
|
|
|
|
|
|
|
|
painter.setBackgroundMode(Qt::TransparentMode);
|
|
|
|
|
|
|
|
for (int row = 0, pxPosY = pxPosStartY; row <= _rowsShown; row++, pxPosY +=_pxCharHeight)
|
|
|
|
{
|
|
|
|
QByteArray hex;
|
|
|
|
int pxPosX = _pxPosHexX - pxOfsX;
|
|
|
|
int pxPosAsciiX2 = _pxPosAsciiX - pxOfsX;
|
|
|
|
qint64 bPosLine = row * BYTES_PER_LINE;
|
|
|
|
for (int colIdx = 0; ((bPosLine + colIdx) < _dataShown.size() && (colIdx < BYTES_PER_LINE)); colIdx++)
|
|
|
|
{
|
|
|
|
QColor c = viewport()->palette().color(QPalette::Base);
|
|
|
|
painter.setPen(colStandard);
|
|
|
|
|
|
|
|
qint64 posBa = _bPosFirst + bPosLine + colIdx;
|
|
|
|
if ((getSelectionBegin() <= posBa) && (getSelectionEnd() > posBa))
|
|
|
|
{
|
|
|
|
c = _brushSelection.color();
|
|
|
|
painter.setPen(_penSelection);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (_highlighting)
|
|
|
|
if (_markedShown.at((int)(posBa - _bPosFirst)))
|
|
|
|
{
|
|
|
|
c = _brushHighlighted.color();
|
|
|
|
painter.setPen(_penHighlighted);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// render hex value
|
|
|
|
QRect r;
|
|
|
|
if (colIdx == 0)
|
|
|
|
r.setRect(pxPosX, pxPosY - _pxCharHeight + _pxSelectionSub, 2*_pxCharWidth, _pxCharHeight);
|
|
|
|
else
|
|
|
|
r.setRect(pxPosX - _pxCharWidth, pxPosY - _pxCharHeight + _pxSelectionSub, 3*_pxCharWidth, _pxCharHeight);
|
|
|
|
painter.fillRect(r, c);
|
|
|
|
hex = _hexDataShown.mid((bPosLine + colIdx) * 2, 2);
|
|
|
|
|
|
|
|
// upper or lower case
|
|
|
|
if (_upperCase)
|
|
|
|
hex = hex.toUpper();
|
|
|
|
|
|
|
|
painter.drawText(pxPosX, pxPosY, hex);
|
|
|
|
pxPosX += 3*_pxCharWidth;
|
|
|
|
|
|
|
|
// render ascii value
|
|
|
|
if (_asciiArea)
|
|
|
|
{
|
|
|
|
char ch = _dataShown.at(bPosLine + colIdx);
|
|
|
|
if ((ch < 0x20) || (ch > 0x7e))
|
|
|
|
ch = '.';
|
|
|
|
r.setRect(pxPosAsciiX2, pxPosY - _pxCharHeight + _pxSelectionSub, _pxCharWidth, _pxCharHeight);
|
|
|
|
painter.fillRect(r, c);
|
|
|
|
painter.drawText(pxPosAsciiX2, pxPosY, QChar(ch));
|
|
|
|
pxPosAsciiX2 += _pxCharWidth;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
painter.setBackgroundMode(Qt::TransparentMode);
|
|
|
|
painter.setPen(viewport()->palette().color(QPalette::WindowText));
|
|
|
|
}
|
|
|
|
|
|
|
|
// paint cursor
|
|
|
|
if (_blink && !_readOnly && hasFocus())
|
|
|
|
painter.fillRect(_cursorRect, this->palette().color(QPalette::WindowText));
|
|
|
|
else
|
|
|
|
painter.drawText(_pxCursorX, _pxCursorY, _hexDataShown.mid(_cursorPosition - _bPosFirst * 2, 1));
|
|
|
|
|
|
|
|
// emit event, if size has changed
|
|
|
|
if (_lastEventSize != _chunks->size())
|
|
|
|
{
|
|
|
|
_lastEventSize = _chunks->size();
|
|
|
|
emit currentSizeChanged(_lastEventSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::resizeEvent(QResizeEvent *)
|
|
|
|
{
|
|
|
|
adjust();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ********************************************************************** Handle selections
|
|
|
|
void QHexEdit::resetSelection()
|
|
|
|
{
|
|
|
|
_bSelectionBegin = _bSelectionInit;
|
|
|
|
_bSelectionEnd = _bSelectionInit;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::resetSelection(qint64 pos)
|
|
|
|
{
|
|
|
|
pos = pos / 2;
|
|
|
|
if (pos < 0)
|
|
|
|
pos = 0;
|
|
|
|
if (pos > _chunks->size())
|
|
|
|
pos = _chunks->size();
|
|
|
|
|
|
|
|
_bSelectionInit = pos;
|
|
|
|
_bSelectionBegin = pos;
|
|
|
|
_bSelectionEnd = pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::setSelection(qint64 pos)
|
|
|
|
{
|
|
|
|
pos = pos / 2;
|
|
|
|
if (pos < 0)
|
|
|
|
pos = 0;
|
|
|
|
if (pos > _chunks->size())
|
|
|
|
pos = _chunks->size();
|
|
|
|
|
|
|
|
if (pos >= _bSelectionInit)
|
|
|
|
{
|
|
|
|
_bSelectionEnd = pos;
|
|
|
|
_bSelectionBegin = _bSelectionInit;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_bSelectionBegin = pos;
|
|
|
|
_bSelectionEnd = _bSelectionInit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int QHexEdit::getSelectionBegin()
|
|
|
|
{
|
|
|
|
return _bSelectionBegin;
|
|
|
|
}
|
|
|
|
|
|
|
|
int QHexEdit::getSelectionEnd()
|
|
|
|
{
|
|
|
|
return _bSelectionEnd;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ********************************************************************** Private utility functions
|
|
|
|
void QHexEdit::init()
|
|
|
|
{
|
|
|
|
_undoStack->clear();
|
|
|
|
setAddressOffset(0);
|
|
|
|
resetSelection(0);
|
|
|
|
setCursorPosition(0);
|
|
|
|
verticalScrollBar()->setValue(0);
|
|
|
|
_modified = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::adjust()
|
|
|
|
{
|
|
|
|
// recalc Graphics
|
|
|
|
if (_addressArea)
|
|
|
|
{
|
|
|
|
_addrDigits = addressWidth();
|
|
|
|
_pxPosHexX = _pxGapAdr + _addrDigits*_pxCharWidth + _pxGapAdrHex;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
_pxPosHexX = _pxGapAdrHex;
|
|
|
|
_pxPosAdrX = _pxGapAdr;
|
|
|
|
_pxPosAsciiX = _pxPosHexX + HEXCHARS_IN_LINE * _pxCharWidth + _pxGapHexAscii;
|
|
|
|
|
|
|
|
// set horizontalScrollBar()
|
|
|
|
int pxWidth = _pxPosAsciiX;
|
|
|
|
if (_asciiArea)
|
|
|
|
pxWidth += BYTES_PER_LINE*_pxCharWidth;
|
|
|
|
horizontalScrollBar()->setRange(0, pxWidth - viewport()->width());
|
|
|
|
horizontalScrollBar()->setPageStep(viewport()->width());
|
|
|
|
|
|
|
|
// set verticalScrollbar()
|
|
|
|
_rowsShown = ((viewport()->height()-4)/_pxCharHeight);
|
|
|
|
int lineCount = (int)(_chunks->size() / (qint64)BYTES_PER_LINE) + 1;
|
|
|
|
verticalScrollBar()->setRange(0, lineCount - _rowsShown);
|
|
|
|
verticalScrollBar()->setPageStep(_rowsShown);
|
|
|
|
|
|
|
|
int value = verticalScrollBar()->value();
|
|
|
|
_bPosFirst = (qint64)value * BYTES_PER_LINE;
|
|
|
|
_bPosLast = _bPosFirst + (qint64)(_rowsShown * BYTES_PER_LINE) - 1;
|
|
|
|
if (_bPosLast >= _chunks->size())
|
|
|
|
_bPosLast = _chunks->size() - 1;
|
|
|
|
readBuffers();
|
|
|
|
setCursorPosition(_cursorPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::dataChangedPrivate(int)
|
|
|
|
{
|
|
|
|
_modified = _undoStack->index() != 0;
|
|
|
|
adjust();
|
|
|
|
emit dataChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::refresh()
|
|
|
|
{
|
|
|
|
ensureVisible();
|
|
|
|
readBuffers();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::readBuffers()
|
|
|
|
{
|
|
|
|
_dataShown = _chunks->data(_bPosFirst, _bPosLast - _bPosFirst + BYTES_PER_LINE + 1, &_markedShown);
|
|
|
|
_hexDataShown = QByteArray(_dataShown.toHex());
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QHexEdit::toReadable(const QByteArray &ba)
|
|
|
|
{
|
|
|
|
QString result;
|
|
|
|
|
|
|
|
for (int i=0; i < ba.size(); i += 16)
|
|
|
|
{
|
|
|
|
QString addrStr = QString("%1").arg(_addressOffset + i, addressWidth(), 16, QChar('0'));
|
|
|
|
QString hexStr;
|
|
|
|
QString ascStr;
|
|
|
|
for (int j=0; j<16; j++)
|
|
|
|
{
|
|
|
|
if ((i + j) < ba.size())
|
|
|
|
{
|
|
|
|
hexStr.append(" ").append(ba.mid(i+j, 1).toHex());
|
|
|
|
char ch = ba[i + j];
|
|
|
|
if ((ch < 0x20) || (ch > 0x7e))
|
|
|
|
ch = '.';
|
|
|
|
ascStr.append(QChar(ch));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result += addrStr + " " + QString("%1").arg(hexStr, -48) + " " + QString("%1").arg(ascStr, -17) + "\n";
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEdit::updateCursor()
|
|
|
|
{
|
|
|
|
if (_blink)
|
|
|
|
_blink = false;
|
|
|
|
else
|
|
|
|
_blink = true;
|
|
|
|
viewport()->update(_cursorRect);
|
|
|
|
}
|
|
|
|
|