2013-08-29 23:35:09 -04:00
|
|
|
#include "qhexedit_p.h"
|
|
|
|
#include "commands.h"
|
|
|
|
|
|
|
|
const int HEXCHARS_IN_LINE = 47;
|
|
|
|
const int GAP_ADR_HEX = 10;
|
|
|
|
const int GAP_HEX_ASCII = 16;
|
|
|
|
const int BYTES_PER_LINE = 16;
|
|
|
|
|
|
|
|
QHexEditPrivate::QHexEditPrivate(QScrollArea *parent) : QWidget(parent)
|
|
|
|
{
|
|
|
|
_undoStack = new QUndoStack(this);
|
|
|
|
|
|
|
|
_scrollArea = parent;
|
|
|
|
setAddressWidth(4);
|
|
|
|
setAddressOffset(0);
|
|
|
|
setAddressArea(true);
|
|
|
|
setAsciiArea(true);
|
|
|
|
setHighlighting(true);
|
|
|
|
setOverwriteMode(true);
|
|
|
|
setReadOnly(false);
|
|
|
|
setAddressAreaColor(QColor(0xd4, 0xd4, 0xd4, 0xff));
|
|
|
|
setHighlightingColor(QColor(0xff, 0xff, 0x99, 0xff));
|
|
|
|
setSelectionColor(QColor(0x6d, 0x9e, 0xff, 0xff));
|
|
|
|
setFont(QFont("Courier", 10));
|
|
|
|
|
|
|
|
_size = 0;
|
|
|
|
resetSelection(0);
|
|
|
|
|
|
|
|
setFocusPolicy(Qt::StrongFocus);
|
|
|
|
|
|
|
|
connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor()));
|
|
|
|
_cursorTimer.setInterval(500);
|
|
|
|
_cursorTimer.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::setAddressOffset(int offset)
|
|
|
|
{
|
|
|
|
_xData.setAddressOffset(offset);
|
|
|
|
adjust();
|
|
|
|
}
|
|
|
|
|
|
|
|
int QHexEditPrivate::addressOffset()
|
|
|
|
{
|
|
|
|
return _xData.addressOffset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::setData(const QByteArray &data)
|
|
|
|
{
|
|
|
|
_xData.setData(data);
|
|
|
|
_undoStack->clear();
|
|
|
|
adjust();
|
|
|
|
setCursorPos(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray QHexEditPrivate::data()
|
|
|
|
{
|
|
|
|
return _xData.data();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::setAddressAreaColor(const QColor &color)
|
|
|
|
{
|
|
|
|
_addressAreaColor = color;
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
QColor QHexEditPrivate::addressAreaColor()
|
|
|
|
{
|
|
|
|
return _addressAreaColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::setHighlightingColor(const QColor &color)
|
|
|
|
{
|
|
|
|
_highlightingColor = color;
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
QColor QHexEditPrivate::highlightingColor()
|
|
|
|
{
|
|
|
|
return _highlightingColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::setSelectionColor(const QColor &color)
|
|
|
|
{
|
|
|
|
_selectionColor = color;
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
QColor QHexEditPrivate::selectionColor()
|
|
|
|
{
|
|
|
|
return _selectionColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::setReadOnly(bool readOnly)
|
|
|
|
{
|
|
|
|
_readOnly = readOnly;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QHexEditPrivate::isReadOnly()
|
|
|
|
{
|
|
|
|
return _readOnly;
|
|
|
|
}
|
|
|
|
|
|
|
|
XByteArray & QHexEditPrivate::xData()
|
|
|
|
{
|
|
|
|
return _xData;
|
|
|
|
}
|
|
|
|
|
|
|
|
int QHexEditPrivate::indexOf(const QByteArray & ba, int from)
|
|
|
|
{
|
|
|
|
if (from > (_xData.data().length() - 1))
|
|
|
|
from = _xData.data().length() - 1;
|
|
|
|
int idx = _xData.data().indexOf(ba, from);
|
|
|
|
if (idx > -1)
|
|
|
|
{
|
|
|
|
int curPos = idx*2;
|
|
|
|
setCursorPos(curPos + ba.length()*2);
|
|
|
|
resetSelection(curPos);
|
|
|
|
setSelection(curPos + ba.length()*2);
|
|
|
|
ensureVisible();
|
|
|
|
}
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::insert(int index, const QByteArray & ba)
|
|
|
|
{
|
|
|
|
if (ba.length() > 0)
|
|
|
|
{
|
|
|
|
if (_overwriteMode)
|
|
|
|
{
|
|
|
|
QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length());
|
|
|
|
_undoStack->push(arrayCommand);
|
|
|
|
emit dataChanged();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::insert, index, ba, ba.length());
|
|
|
|
_undoStack->push(arrayCommand);
|
|
|
|
emit dataChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::insert(int index, char ch)
|
|
|
|
{
|
|
|
|
QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::insert, index, ch);
|
|
|
|
_undoStack->push(charCommand);
|
|
|
|
emit dataChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
int QHexEditPrivate::lastIndexOf(const QByteArray & ba, int from)
|
|
|
|
{
|
|
|
|
from -= ba.length();
|
|
|
|
if (from < 0)
|
|
|
|
from = 0;
|
|
|
|
int idx = _xData.data().lastIndexOf(ba, from);
|
|
|
|
if (idx > -1)
|
|
|
|
{
|
|
|
|
int curPos = idx*2;
|
|
|
|
setCursorPos(curPos);
|
|
|
|
resetSelection(curPos);
|
|
|
|
setSelection(curPos + ba.length()*2);
|
|
|
|
ensureVisible();
|
|
|
|
}
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::remove(int index, int len)
|
|
|
|
{
|
|
|
|
if (len > 0)
|
|
|
|
{
|
|
|
|
if (len == 1)
|
|
|
|
{
|
|
|
|
if (_overwriteMode)
|
|
|
|
{
|
|
|
|
QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::replace, index, char(0));
|
|
|
|
_undoStack->push(charCommand);
|
|
|
|
emit dataChanged();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::remove, index, char(0));
|
|
|
|
_undoStack->push(charCommand);
|
|
|
|
emit dataChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QByteArray ba = QByteArray(len, char(0));
|
|
|
|
if (_overwriteMode)
|
|
|
|
{
|
|
|
|
QUndoCommand *arrayCommand = new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length());
|
|
|
|
_undoStack->push(arrayCommand);
|
|
|
|
emit dataChanged();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::remove, index, ba, len);
|
|
|
|
_undoStack->push(arrayCommand);
|
|
|
|
emit dataChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::replace(int index, char ch)
|
|
|
|
{
|
|
|
|
QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::replace, index, ch);
|
|
|
|
_undoStack->push(charCommand);
|
|
|
|
resetSelection();
|
|
|
|
emit dataChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::replace(int index, const QByteArray & ba)
|
|
|
|
{
|
|
|
|
QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length());
|
|
|
|
_undoStack->push(arrayCommand);
|
|
|
|
resetSelection();
|
|
|
|
emit dataChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::replace(int pos, int len, const QByteArray &after)
|
|
|
|
{
|
|
|
|
QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, pos, after, len);
|
|
|
|
_undoStack->push(arrayCommand);
|
|
|
|
resetSelection();
|
|
|
|
emit dataChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::setAddressArea(bool addressArea)
|
|
|
|
{
|
|
|
|
_addressArea = addressArea;
|
|
|
|
adjust();
|
|
|
|
|
|
|
|
setCursorPos(_cursorPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::setAddressWidth(int addressWidth)
|
|
|
|
{
|
|
|
|
_xData.setAddressWidth(addressWidth);
|
|
|
|
|
|
|
|
setCursorPos(_cursorPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::setAsciiArea(bool asciiArea)
|
|
|
|
{
|
|
|
|
_asciiArea = asciiArea;
|
|
|
|
adjust();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::setFont(const QFont &font)
|
|
|
|
{
|
|
|
|
QWidget::setFont(font);
|
|
|
|
adjust();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::setHighlighting(bool mode)
|
|
|
|
{
|
|
|
|
_highlighting = mode;
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::setOverwriteMode(bool overwriteMode)
|
|
|
|
{
|
|
|
|
_overwriteMode = overwriteMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QHexEditPrivate::overwriteMode()
|
|
|
|
{
|
|
|
|
return _overwriteMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::redo()
|
|
|
|
{
|
|
|
|
_undoStack->redo();
|
|
|
|
emit dataChanged();
|
|
|
|
setCursorPos(_cursorPosition);
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::undo()
|
|
|
|
{
|
|
|
|
_undoStack->undo();
|
|
|
|
emit dataChanged();
|
|
|
|
setCursorPos(_cursorPosition);
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QHexEditPrivate::toRedableString()
|
|
|
|
{
|
|
|
|
return _xData.toRedableString();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QString QHexEditPrivate::selectionToReadableString()
|
|
|
|
{
|
|
|
|
return _xData.toRedableString(getSelectionBegin(), getSelectionEnd());
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::keyPressEvent(QKeyEvent *event)
|
|
|
|
{
|
|
|
|
int charX = (_cursorX - _xPosHex) / _charWidth;
|
|
|
|
int posX = (charX / 3) * 2 + (charX % 3);
|
|
|
|
int posBa = (_cursorY / _charHeight) * BYTES_PER_LINE + posX / 2;
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Cursor movements */
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
if (event->matches(QKeySequence::MoveToNextChar))
|
|
|
|
{
|
|
|
|
setCursorPos(_cursorPosition + 1);
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::MoveToPreviousChar))
|
|
|
|
{
|
|
|
|
setCursorPos(_cursorPosition - 1);
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::MoveToEndOfLine))
|
|
|
|
{
|
|
|
|
setCursorPos(_cursorPosition | (2 * BYTES_PER_LINE -1));
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::MoveToStartOfLine))
|
|
|
|
{
|
|
|
|
setCursorPos(_cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)));
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::MoveToPreviousLine))
|
|
|
|
{
|
|
|
|
setCursorPos(_cursorPosition - (2 * BYTES_PER_LINE));
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::MoveToNextLine))
|
|
|
|
{
|
|
|
|
setCursorPos(_cursorPosition + (2 * BYTES_PER_LINE));
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event->matches(QKeySequence::MoveToNextPage))
|
|
|
|
{
|
|
|
|
setCursorPos(_cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE));
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::MoveToPreviousPage))
|
|
|
|
{
|
|
|
|
setCursorPos(_cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE));
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::MoveToEndOfDocument))
|
|
|
|
{
|
|
|
|
setCursorPos(_xData.size() * 2);
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::MoveToStartOfDocument))
|
|
|
|
{
|
|
|
|
setCursorPos(0);
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Select commands */
|
|
|
|
/*****************************************************************************/
|
|
|
|
if (event->matches(QKeySequence::SelectAll))
|
|
|
|
{
|
|
|
|
resetSelection(0);
|
|
|
|
setSelection(2*_xData.size() + 1);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectNextChar))
|
|
|
|
{
|
|
|
|
int pos = _cursorPosition + 1;
|
|
|
|
setCursorPos(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectPreviousChar))
|
|
|
|
{
|
|
|
|
int pos = _cursorPosition - 1;
|
|
|
|
setSelection(pos);
|
|
|
|
setCursorPos(pos);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectEndOfLine))
|
|
|
|
{
|
|
|
|
int pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)) + (2 * BYTES_PER_LINE);
|
|
|
|
setCursorPos(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectStartOfLine))
|
|
|
|
{
|
|
|
|
int pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE));
|
|
|
|
setCursorPos(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectPreviousLine))
|
|
|
|
{
|
|
|
|
int pos = _cursorPosition - (2 * BYTES_PER_LINE);
|
|
|
|
setCursorPos(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectNextLine))
|
|
|
|
{
|
|
|
|
int pos = _cursorPosition + (2 * BYTES_PER_LINE);
|
|
|
|
setCursorPos(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event->matches(QKeySequence::SelectNextPage))
|
|
|
|
{
|
|
|
|
int pos = _cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE);
|
|
|
|
setCursorPos(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectPreviousPage))
|
|
|
|
{
|
|
|
|
int pos = _cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE);
|
|
|
|
setCursorPos(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectEndOfDocument))
|
|
|
|
{
|
|
|
|
int pos = _xData.size() * 2;
|
|
|
|
setCursorPos(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
if (event->matches(QKeySequence::SelectStartOfDocument))
|
|
|
|
{
|
|
|
|
int pos = 0;
|
|
|
|
setCursorPos(pos);
|
|
|
|
setSelection(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Edit Commands */
|
|
|
|
/*****************************************************************************/
|
|
|
|
if (!_readOnly)
|
|
|
|
{
|
|
|
|
/* Hex input */
|
2014-08-19 07:34:00 -04:00
|
|
|
int key = int(event->text()[0].toLatin1());
|
2013-08-29 23:35:09 -04:00
|
|
|
if ((key>='0' && key<='9') || (key>='a' && key <= 'f'))
|
|
|
|
{
|
|
|
|
if (getSelectionBegin() != getSelectionEnd())
|
|
|
|
{
|
|
|
|
posBa = getSelectionBegin();
|
|
|
|
remove(posBa, getSelectionEnd() - posBa);
|
|
|
|
setCursorPos(2*posBa);
|
|
|
|
resetSelection(2*posBa);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If insert mode, then insert a byte
|
|
|
|
if (_overwriteMode == false)
|
|
|
|
if ((charX % 3) == 0)
|
|
|
|
{
|
|
|
|
insert(posBa, char(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Change content
|
|
|
|
if (_xData.size() > 0)
|
|
|
|
{
|
|
|
|
QByteArray hexValue = _xData.data().mid(posBa, 1).toHex();
|
|
|
|
if ((charX % 3) == 0)
|
|
|
|
hexValue[0] = key;
|
|
|
|
else
|
|
|
|
hexValue[1] = key;
|
|
|
|
|
|
|
|
replace(posBa, QByteArray().fromHex(hexValue)[0]);
|
|
|
|
|
|
|
|
setCursorPos(_cursorPosition + 1);
|
|
|
|
resetSelection(_cursorPosition);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cut & Paste */
|
|
|
|
if (event->matches(QKeySequence::Cut))
|
|
|
|
{
|
|
|
|
QString result = QString();
|
|
|
|
for (int idx = getSelectionBegin(); idx < getSelectionEnd(); idx++)
|
|
|
|
{
|
|
|
|
result += _xData.data().mid(idx, 1).toHex() + " ";
|
|
|
|
if ((idx % 16) == 15)
|
|
|
|
result.append("\n");
|
|
|
|
}
|
|
|
|
remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin());
|
|
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
|
|
clipboard->setText(result);
|
|
|
|
setCursorPos(getSelectionBegin());
|
|
|
|
resetSelection(getSelectionBegin());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event->matches(QKeySequence::Paste))
|
|
|
|
{
|
|
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
|
|
QByteArray ba = QByteArray().fromHex(clipboard->text().toLatin1());
|
|
|
|
insert(_cursorPosition / 2, ba);
|
|
|
|
setCursorPos(_cursorPosition + 2 * ba.length());
|
|
|
|
resetSelection(getSelectionBegin());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Delete char */
|
|
|
|
if (event->matches(QKeySequence::Delete))
|
|
|
|
{
|
|
|
|
if (getSelectionBegin() != getSelectionEnd())
|
|
|
|
{
|
|
|
|
posBa = getSelectionBegin();
|
|
|
|
remove(posBa, getSelectionEnd() - posBa);
|
|
|
|
setCursorPos(2*posBa);
|
|
|
|
resetSelection(2*posBa);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (_overwriteMode)
|
|
|
|
replace(posBa, char(0));
|
|
|
|
else
|
|
|
|
remove(posBa, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Backspace */
|
|
|
|
if ((event->key() == Qt::Key_Backspace) && (event->modifiers() == Qt::NoModifier))
|
|
|
|
{
|
|
|
|
if (getSelectionBegin() != getSelectionEnd())
|
|
|
|
{
|
|
|
|
posBa = getSelectionBegin();
|
|
|
|
remove(posBa, getSelectionEnd() - posBa);
|
|
|
|
setCursorPos(2*posBa);
|
|
|
|
resetSelection(2*posBa);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (posBa > 0)
|
|
|
|
{
|
|
|
|
if (_overwriteMode)
|
|
|
|
replace(posBa - 1, char(0));
|
|
|
|
else
|
|
|
|
remove(posBa - 1, 1);
|
|
|
|
setCursorPos(_cursorPosition - 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* undo */
|
|
|
|
if (event->matches(QKeySequence::Undo))
|
|
|
|
{
|
|
|
|
undo();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* redo */
|
|
|
|
if (event->matches(QKeySequence::Redo))
|
|
|
|
{
|
|
|
|
redo();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event->matches(QKeySequence::Copy))
|
|
|
|
{
|
|
|
|
QString result = QString();
|
|
|
|
for (int idx = getSelectionBegin(); idx < getSelectionEnd(); idx++)
|
|
|
|
{
|
|
|
|
result += _xData.data().mid(idx, 1).toHex() + " ";
|
|
|
|
if ((idx % 16) == 15)
|
|
|
|
result.append('\n');
|
|
|
|
}
|
|
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
|
|
clipboard->setText(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Switch between insert/overwrite mode
|
|
|
|
if ((event->key() == Qt::Key_Insert) && (event->modifiers() == Qt::NoModifier))
|
|
|
|
{
|
|
|
|
_overwriteMode = !_overwriteMode;
|
|
|
|
setCursorPos(_cursorPosition);
|
|
|
|
overwriteModeChanged(_overwriteMode);
|
|
|
|
}
|
|
|
|
|
|
|
|
ensureVisible();
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::mouseMoveEvent(QMouseEvent * event)
|
|
|
|
{
|
|
|
|
_blink = false;
|
|
|
|
update();
|
|
|
|
int actPos = cursorPos(event->pos());
|
|
|
|
setCursorPos(actPos);
|
|
|
|
setSelection(actPos);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::mousePressEvent(QMouseEvent * event)
|
|
|
|
{
|
|
|
|
_blink = false;
|
|
|
|
update();
|
|
|
|
int cPos = cursorPos(event->pos());
|
|
|
|
resetSelection(cPos);
|
|
|
|
setCursorPos(cPos);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::paintEvent(QPaintEvent *event)
|
|
|
|
{
|
|
|
|
QPainter painter(this);
|
|
|
|
|
|
|
|
// draw some patterns if needed
|
|
|
|
painter.fillRect(event->rect(), this->palette().color(QPalette::Base));
|
|
|
|
if (_addressArea)
|
|
|
|
painter.fillRect(QRect(_xPosAdr, event->rect().top(), _xPosHex - GAP_ADR_HEX + 2, height()), _addressAreaColor);
|
|
|
|
if (_asciiArea)
|
|
|
|
{
|
|
|
|
int linePos = _xPosAscii - (GAP_HEX_ASCII / 2);
|
|
|
|
painter.setPen(Qt::gray);
|
|
|
|
painter.drawLine(linePos, event->rect().top(), linePos, height());
|
|
|
|
}
|
|
|
|
|
|
|
|
painter.setPen(this->palette().color(QPalette::WindowText));
|
|
|
|
|
|
|
|
// calc position
|
|
|
|
int firstLineIdx = ((event->rect().top()/ _charHeight) - _charHeight) * BYTES_PER_LINE;
|
|
|
|
if (firstLineIdx < 0)
|
|
|
|
firstLineIdx = 0;
|
|
|
|
int lastLineIdx = ((event->rect().bottom() / _charHeight) + _charHeight) * BYTES_PER_LINE;
|
|
|
|
if (lastLineIdx > _xData.size())
|
|
|
|
lastLineIdx = _xData.size();
|
|
|
|
int yPosStart = ((firstLineIdx) / BYTES_PER_LINE) * _charHeight + _charHeight;
|
|
|
|
|
|
|
|
// paint address area
|
|
|
|
if (_addressArea)
|
|
|
|
{
|
|
|
|
for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight)
|
|
|
|
{
|
|
|
|
QString address = QString("%1")
|
|
|
|
.arg(lineIdx + _xData.addressOffset(), _xData.realAddressNumbers(), 16, QChar('0'));
|
|
|
|
painter.drawText(_xPosAdr, yPos, address);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// paint hex area
|
|
|
|
QByteArray hexBa(_xData.data().mid(firstLineIdx, lastLineIdx - firstLineIdx + 1).toHex());
|
|
|
|
QBrush highLighted = QBrush(_highlightingColor);
|
|
|
|
QPen colHighlighted = QPen(this->palette().color(QPalette::WindowText));
|
|
|
|
QBrush selected = QBrush(_selectionColor);
|
|
|
|
QPen colSelected = QPen(Qt::white);
|
|
|
|
QPen colStandard = QPen(this->palette().color(QPalette::WindowText));
|
|
|
|
|
|
|
|
painter.setBackgroundMode(Qt::TransparentMode);
|
|
|
|
|
|
|
|
for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight)
|
|
|
|
{
|
|
|
|
QByteArray hex;
|
|
|
|
int xPos = _xPosHex;
|
|
|
|
for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() && (colIdx < BYTES_PER_LINE)); colIdx++)
|
|
|
|
{
|
|
|
|
int posBa = lineIdx + colIdx;
|
|
|
|
if ((getSelectionBegin() <= posBa) && (getSelectionEnd() > posBa))
|
|
|
|
{
|
|
|
|
painter.setBackground(selected);
|
|
|
|
painter.setBackgroundMode(Qt::OpaqueMode);
|
|
|
|
painter.setPen(colSelected);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (_highlighting)
|
|
|
|
{
|
|
|
|
// hilight diff bytes
|
|
|
|
painter.setBackground(highLighted);
|
|
|
|
if (_xData.dataChanged(posBa))
|
|
|
|
{
|
|
|
|
painter.setPen(colHighlighted);
|
|
|
|
painter.setBackgroundMode(Qt::OpaqueMode);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
painter.setPen(colStandard);
|
|
|
|
painter.setBackgroundMode(Qt::TransparentMode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// render hex value
|
|
|
|
if (colIdx == 0)
|
|
|
|
{
|
|
|
|
hex = hexBa.mid((lineIdx - firstLineIdx) * 2, 2);
|
|
|
|
painter.drawText(xPos, yPos, hex);
|
|
|
|
xPos += 2 * _charWidth;
|
|
|
|
} else {
|
|
|
|
hex = hexBa.mid((lineIdx + colIdx - firstLineIdx) * 2, 2).prepend(" ");
|
|
|
|
painter.drawText(xPos, yPos, hex);
|
|
|
|
xPos += 3 * _charWidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
painter.setBackgroundMode(Qt::TransparentMode);
|
|
|
|
painter.setPen(this->palette().color(QPalette::WindowText));
|
|
|
|
|
|
|
|
// paint ascii area
|
|
|
|
if (_asciiArea)
|
|
|
|
{
|
|
|
|
for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight)
|
|
|
|
{
|
|
|
|
int xPosAscii = _xPosAscii;
|
|
|
|
for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() && (colIdx < BYTES_PER_LINE)); colIdx++)
|
|
|
|
{
|
|
|
|
painter.drawText(xPosAscii, yPos, _xData.asciiChar(lineIdx + colIdx));
|
|
|
|
xPosAscii += _charWidth;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// paint cursor
|
|
|
|
if (_blink && !_readOnly && hasFocus())
|
|
|
|
{
|
|
|
|
if (_overwriteMode)
|
|
|
|
painter.fillRect(_cursorX, _cursorY + _charHeight - 2, _charWidth, 2, this->palette().color(QPalette::WindowText));
|
|
|
|
else
|
|
|
|
painter.fillRect(_cursorX, _cursorY, 2, _charHeight, this->palette().color(QPalette::WindowText));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_size != _xData.size())
|
|
|
|
{
|
|
|
|
_size = _xData.size();
|
|
|
|
emit currentSizeChanged(_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::setCursorPos(int position)
|
|
|
|
{
|
|
|
|
// delete cursor
|
|
|
|
_blink = false;
|
|
|
|
update();
|
|
|
|
|
|
|
|
// cursor in range?
|
|
|
|
if (_overwriteMode)
|
|
|
|
{
|
|
|
|
if (position > (_xData.size() * 2 - 1))
|
|
|
|
position = _xData.size() * 2 - 1;
|
|
|
|
} else {
|
|
|
|
if (position > (_xData.size() * 2))
|
|
|
|
position = _xData.size() * 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (position < 0)
|
|
|
|
position = 0;
|
|
|
|
|
|
|
|
// calc position
|
|
|
|
_cursorPosition = position;
|
|
|
|
_cursorY = (position / (2 * BYTES_PER_LINE)) * _charHeight + 4;
|
|
|
|
int x = (position % (2 * BYTES_PER_LINE));
|
|
|
|
_cursorX = (((x / 2) * 3) + (x % 2)) * _charWidth + _xPosHex;
|
|
|
|
|
|
|
|
// immiadately draw cursor
|
|
|
|
_blink = true;
|
|
|
|
update();
|
|
|
|
emit currentAddressChanged(_cursorPosition/2);
|
|
|
|
}
|
|
|
|
|
|
|
|
int QHexEditPrivate::cursorPos(QPoint pos)
|
|
|
|
{
|
|
|
|
int result = -1;
|
|
|
|
// find char under cursor
|
|
|
|
if ((pos.x() >= _xPosHex) && (pos.x() < (_xPosHex + HEXCHARS_IN_LINE * _charWidth)))
|
|
|
|
{
|
|
|
|
int x = (pos.x() - _xPosHex) / _charWidth;
|
|
|
|
if ((x % 3) == 0)
|
|
|
|
x = (x / 3) * 2;
|
|
|
|
else
|
|
|
|
x = ((x / 3) * 2) + 1;
|
|
|
|
int y = ((pos.y() - 3) / _charHeight) * 2 * BYTES_PER_LINE;
|
|
|
|
result = x + y;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int QHexEditPrivate::cursorPos()
|
|
|
|
{
|
|
|
|
return _cursorPosition;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::resetSelection()
|
|
|
|
{
|
|
|
|
_selectionBegin = _selectionInit;
|
|
|
|
_selectionEnd = _selectionInit;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::resetSelection(int pos)
|
|
|
|
{
|
|
|
|
if (pos < 0)
|
|
|
|
pos = 0;
|
|
|
|
pos = pos / 2;
|
|
|
|
_selectionInit = pos;
|
|
|
|
_selectionBegin = pos;
|
|
|
|
_selectionEnd = pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::setSelection(int pos)
|
|
|
|
{
|
|
|
|
if (pos < 0)
|
|
|
|
pos = 0;
|
|
|
|
pos = pos / 2;
|
|
|
|
if (pos >= _selectionInit)
|
|
|
|
{
|
|
|
|
_selectionEnd = pos;
|
|
|
|
_selectionBegin = _selectionInit;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_selectionBegin = pos;
|
|
|
|
_selectionEnd = _selectionInit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int QHexEditPrivate::getSelectionBegin()
|
|
|
|
{
|
|
|
|
return _selectionBegin;
|
|
|
|
}
|
|
|
|
|
|
|
|
int QHexEditPrivate::getSelectionEnd()
|
|
|
|
{
|
|
|
|
return _selectionEnd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void QHexEditPrivate::updateCursor()
|
|
|
|
{
|
|
|
|
if (_blink)
|
|
|
|
_blink = false;
|
|
|
|
else
|
|
|
|
_blink = true;
|
|
|
|
update(_cursorX, _cursorY, _charWidth, _charHeight);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::adjust()
|
|
|
|
{
|
|
|
|
_charWidth = fontMetrics().width(QLatin1Char('9'));
|
|
|
|
_charHeight = fontMetrics().height();
|
|
|
|
|
|
|
|
_xPosAdr = 0;
|
|
|
|
if (_addressArea)
|
|
|
|
_xPosHex = _xData.realAddressNumbers()*_charWidth + GAP_ADR_HEX;
|
|
|
|
else
|
|
|
|
_xPosHex = 0;
|
|
|
|
_xPosAscii = _xPosHex + HEXCHARS_IN_LINE * _charWidth + GAP_HEX_ASCII;
|
|
|
|
|
|
|
|
// tell QAbstractScollbar, how big we are
|
|
|
|
setMinimumHeight(((_xData.size()/16 + 1) * _charHeight) + 5);
|
|
|
|
if(_asciiArea)
|
|
|
|
setMinimumWidth(_xPosAscii + (BYTES_PER_LINE * _charWidth));
|
|
|
|
else
|
|
|
|
setMinimumWidth(_xPosHex + HEXCHARS_IN_LINE * _charWidth);
|
|
|
|
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QHexEditPrivate::ensureVisible()
|
|
|
|
{
|
|
|
|
// scrolls to cursorx, cusory (which are set by setCursorPos)
|
|
|
|
// x-margin is 3 pixels, y-margin is half of charHeight
|
|
|
|
_scrollArea->ensureVisible(_cursorX, _cursorY + _charHeight/2, 3, _charHeight/2 + 2);
|
|
|
|
}
|