AppleWin/source/frontends/qapple/qhexedit.cpp
Andrea Odetti 246e66d9be Address area: do not show 1 more row than necessary.
Signed-off-by: Andrea Odetti <mariofutire@gmail.com>
2017-10-14 19:18:44 +01:00

1139 lines
33 KiB
C++

#include <QApplication>
#include <QClipboard>
#include <QKeyEvent>
#include <QPainter>
#include <QScrollBar>
#include "qhexedit.h"
#include <algorithm>
// ********************************************************************** Constructor, destructor
QHexEdit::QHexEdit(QWidget *parent) : QAbstractScrollArea(parent)
{
_addressArea = true;
_addressWidth = 4;
_asciiArea = true;
_overwriteMode = true;
_highlighting = true;
_readOnly = false;
_cursorPosition = 0;
_lastEventSize = 0;
_hexCharsInLine = 47;
_bytesPerLine = 16;
_editAreaIsAscii = false;
_hexCaps = false;
_dynamicBytesPerLine = false;
_chunks = new Chunks(this);
_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);
// the timer is started inside setReadOnly() if necessary
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)
{
if (!asciiArea)
_editAreaIsAscii = false;
_asciiArea = asciiArea;
adjust();
setCursorPosition(_cursorPosition);
viewport()->update();
}
bool QHexEdit::asciiArea()
{
return _asciiArea;
}
void QHexEdit::setBytesPerLine(int count)
{
_bytesPerLine = count;
_hexCharsInLine = count * 3 - 1;
adjust();
setCursorPosition(_cursorPosition);
viewport()->update();
}
int QHexEdit::bytesPerLine()
{
return _bytesPerLine;
}
void QHexEdit::setCursorPosition(qint64 position)
{
// 2. Check, if cursor in range?
if (position > (_chunks->size() * 2 - 1))
position = _chunks->size() * 2 - (_overwriteMode ? 1 : 0);
if (position < 0)
position = 0;
// 3. Calc new position of cursor
_bPosCurrent = position / 2;
_pxCursorY = ((position / 2 - _bPosFirst) / _bytesPerLine + 1) * _pxCharHeight;
int x = (position % (2 * _bytesPerLine));
if (_editAreaIsAscii)
{
_pxCursorX = x / 2 * _pxCharWidth + _pxPosAsciiX;
_cursorPosition = position & 0xFFFFFFFFFFFFFFFE;
} else {
_pxCursorX = (((x / 2) * 3) + (x % 2)) * _pxCharWidth + _pxPosHexX;
_cursorPosition = position;
}
if (_overwriteMode)
_cursorRect = QRect(_pxCursorX - horizontalScrollBar()->value(), _pxCursorY - _pxCursorWidth + _pxSelectionSub, _pxCharWidth, _pxCursorWidth);
else
_cursorRect = QRect(_pxCursorX - horizontalScrollBar()->value(), _pxCursorY - _pxCharHeight + _pxSelectionSub, _pxCursorWidth, _pxCharHeight);
// 4. Immediately redraw everything
_blink = true;
viewport()->update();
emit currentAddressChanged(_bPosCurrent);
}
qint64 QHexEdit::cursorPosition(QPoint pos)
{
// Calc cursor position 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 + _hexCharsInLine) * _pxCharWidth)))
{
_editAreaIsAscii = false;
int x = (posX - _pxPosHexX) / _pxCharWidth;
x = (x / 3) * 2 + x % 3;
int y = (posY / _pxCharHeight) * 2 * _bytesPerLine;
result = _bPosFirst * 2 + x + y;
}
else
if (_asciiArea && (posX >= _pxPosAsciiX) && (posX < (_pxPosAsciiX + (1 + _bytesPerLine) * _pxCharWidth)))
{
_editAreaIsAscii = true;
int x = 2 * (posX - _pxPosAsciiX) / _pxCharWidth;
int y = (posY / _pxCharHeight) * 2 * _bytesPerLine;
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;
if (_readOnly)
_cursorTimer.stop();
else
_cursorTimer.start();
}
void QHexEdit::setHexCaps(const bool isCaps)
{
if (_hexCaps != isCaps)
{
_hexCaps = isCaps;
viewport()->update();
}
}
bool QHexEdit::hexCaps()
{
return _hexCaps;
}
void QHexEdit::setDynamicBytesPerLine(const bool isDynamic)
{
_dynamicBytesPerLine = isDynamic;
resizeEvent(NULL);
}
bool QHexEdit::dynamicBytesPerLine()
{
return _dynamicBytesPerLine;
}
// ********************************************************************** 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 / _bytesPerLine));
if (_cursorPosition > ((_bPosFirst + (_rowsShown - 1)*_bytesPerLine) * 2))
verticalScrollBar()->setValue((int)(_cursorPosition / 2 / _bytesPerLine) - _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()*(_editAreaIsAscii ? 1 : 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()*(_editAreaIsAscii ? 1 : 2));
refresh();
}
// ********************************************************************** Handle events
void QHexEdit::keyPressEvent(QKeyEvent *event)
{
// Cursor movements
if (event->matches(QKeySequence::MoveToNextChar))
{
qint64 pos = _cursorPosition + 1;
if (_editAreaIsAscii)
pos += 1;
setCursorPosition(pos);
resetSelection(pos);
}
if (event->matches(QKeySequence::MoveToPreviousChar))
{
qint64 pos = _cursorPosition - 1;
if (_editAreaIsAscii)
pos -= 1;
setCursorPosition(pos);
resetSelection(pos);
}
if (event->matches(QKeySequence::MoveToEndOfLine))
{
qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine)) + (2 * _bytesPerLine) - 1;
setCursorPosition(pos);
resetSelection(_cursorPosition);
}
if (event->matches(QKeySequence::MoveToStartOfLine))
{
qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine));
setCursorPosition(pos);
resetSelection(_cursorPosition);
}
if (event->matches(QKeySequence::MoveToPreviousLine))
{
setCursorPosition(_cursorPosition - (2 * _bytesPerLine));
resetSelection(_cursorPosition);
}
if (event->matches(QKeySequence::MoveToNextLine))
{
setCursorPosition(_cursorPosition + (2 * _bytesPerLine));
resetSelection(_cursorPosition);
}
if (event->matches(QKeySequence::MoveToNextPage))
{
setCursorPosition(_cursorPosition + (((_rowsShown - 1) * 2 * _bytesPerLine)));
resetSelection(_cursorPosition);
}
if (event->matches(QKeySequence::MoveToPreviousPage))
{
setCursorPosition(_cursorPosition - (((_rowsShown - 1) * 2 * _bytesPerLine)));
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;
if (_editAreaIsAscii)
pos += 1;
setCursorPosition(pos);
setSelection(pos);
}
if (event->matches(QKeySequence::SelectPreviousChar))
{
qint64 pos = _cursorPosition - 1;
if (_editAreaIsAscii)
pos -= 1;
setSelection(pos);
setCursorPosition(pos);
}
if (event->matches(QKeySequence::SelectEndOfLine))
{
qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine)) + (2 * _bytesPerLine) - 1;
setCursorPosition(pos);
setSelection(pos);
}
if (event->matches(QKeySequence::SelectStartOfLine))
{
qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine));
setCursorPosition(pos);
setSelection(pos);
}
if (event->matches(QKeySequence::SelectPreviousLine))
{
qint64 pos = _cursorPosition - (2 * _bytesPerLine);
setCursorPosition(pos);
setSelection(pos);
}
if (event->matches(QKeySequence::SelectNextLine))
{
qint64 pos = _cursorPosition + (2 * _bytesPerLine);
setCursorPosition(pos);
setSelection(pos);
}
if (event->matches(QKeySequence::SelectNextPage))
{
qint64 pos = _cursorPosition + (((viewport()->height() / _pxCharHeight) - 1) * 2 * _bytesPerLine);
setCursorPosition(pos);
setSelection(pos);
}
if (event->matches(QKeySequence::SelectPreviousPage))
{
qint64 pos = _cursorPosition - (((viewport()->height() / _pxCharHeight) - 1) * 2 * _bytesPerLine);
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)
{
/* 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());
} else
/* Paste */
if (event->matches(QKeySequence::Paste))
{
QClipboard *clipboard = QApplication::clipboard();
QByteArray ba = QByteArray().fromHex(clipboard->text().toLatin1());
if (_overwriteMode)
{
ba = ba.left(std::min<qint64>(ba.size(), (_chunks->size() - _bPosCurrent)));
replace(_bPosCurrent, ba.size(), ba);
}
else
insert(_bPosCurrent, ba);
setCursorPosition(_cursorPosition + 2 * ba.size());
resetSelection(getSelectionBegin());
} else
/* 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);
} else
/* 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);
}
} else
/* undo */
if (event->matches(QKeySequence::Undo))
{
undo();
} else
/* redo */
if (event->matches(QKeySequence::Redo))
{
redo();
} else
if ((QApplication::keyboardModifiers() == Qt::NoModifier) ||
(QApplication::keyboardModifiers() == Qt::KeypadModifier) ||
(QApplication::keyboardModifiers() == Qt::ShiftModifier) ||
(QApplication::keyboardModifiers() == (Qt::AltModifier | Qt::ControlModifier)) ||
(QApplication::keyboardModifiers() == Qt::GroupSwitchModifier))
{
/* Hex and ascii input */
int key;
if (_editAreaIsAscii)
key = (uchar)event->text()[0].toLatin1();
else
key = int(event->text()[0].toLower().toLatin1());
if ((((key >= '0' && key <= '9') || (key >= 'a' && key <= 'f')) && _editAreaIsAscii == false)
|| (key >= ' ' && _editAreaIsAscii))
{
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)
{
char ch = key;
if (!_editAreaIsAscii){
QByteArray hexValue = _chunks->data(_bPosCurrent, 1).toHex();
if ((_cursorPosition % 2) == 0)
hexValue[0] = key;
else
hexValue[1] = key;
ch = QByteArray().fromHex(hexValue)[0];
}
replace(_bPosCurrent, ch);
if (_editAreaIsAscii)
setCursorPosition(_cursorPosition + 2);
else
setCursorPosition(_cursorPosition + 1);
resetSelection(_cursorPosition);
}
}
}
}
/* Copy */
if (event->matches(QKeySequence::Copy))
{
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);
}
// Switch between insert/overwrite mode
if ((event->key() == Qt::Key_Insert) && (event->modifiers() == Qt::NoModifier))
{
setOverwriteMode(!overwriteMode());
setCursorPosition(_cursorPosition);
}
// switch from hex to ascii edit
if (event->key() == Qt::Key_Tab && !_editAreaIsAscii){
_editAreaIsAscii = true;
setCursorPosition(_cursorPosition);
}
// switch from ascii to hex edit
if (event->key() == Qt::Key_Backtab && _editAreaIsAscii){
_editAreaIsAscii = false;
setCursorPosition(_cursorPosition);
}
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());
int pxOfsX = horizontalScrollBar()->value();
if (event->rect() != _cursorRect)
{
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, 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;
int numberOfRows = (_dataShown.size() - 1) / _bytesPerLine + 1;
for (int row=0, pxPosY = _pxCharHeight; row < numberOfRows; row++, pxPosY +=_pxCharHeight)
{
address = QString("%1").arg(_bPosFirst + row*_bytesPerLine + _addressOffset, _addrDigits, 16, QChar('0'));
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 * _bytesPerLine;
for (int colIdx = 0; ((bPosLine + colIdx) < _dataShown.size() && (colIdx < _bytesPerLine)); 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);
painter.drawText(pxPosX, pxPosY, hexCaps()?hex.toUpper():hex);
pxPosX += 3*_pxCharWidth;
// render ascii value
if (_asciiArea)
{
int ch = (uchar)_dataShown.at(bPosLine + colIdx);
if ( ch < 0x20 )
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));
}
// _cursorPosition counts in 2, _bPosFirst counts in 1
int hexPositionInShowData = _cursorPosition - 2 * _bPosFirst;
// due to scrolling the cursor can go out of the currently displayed data
if ((hexPositionInShowData >= 0) && (hexPositionInShowData < _hexDataShown.size()))
{
// paint cursor
if (_readOnly)
{
// make the background stick out
QColor color = viewport()->palette().dark().color();
painter.fillRect(QRect(_pxCursorX - pxOfsX, _pxCursorY - _pxCharHeight + _pxSelectionSub, _pxCharWidth, _pxCharHeight), color);
}
else
{
if (_blink && hasFocus())
painter.fillRect(_cursorRect, this->palette().color(QPalette::WindowText));
}
if (_editAreaIsAscii)
{
// every 2 hex there is 1 ascii
int asciiPositionInShowData = hexPositionInShowData / 2;
int ch = (uchar)_dataShown.at(asciiPositionInShowData);
if ( ch < 0x20 )
ch = '.';
painter.drawText(_pxCursorX - pxOfsX, _pxCursorY, QChar(ch));
}
else
{
painter.drawText(_pxCursorX - pxOfsX, _pxCursorY, _hexDataShown.mid(hexPositionInShowData, 1));
}
}
// emit event, if size has changed
if (_lastEventSize != _chunks->size())
{
_lastEventSize = _chunks->size();
emit currentSizeChanged(_lastEventSize);
}
}
void QHexEdit::resizeEvent(QResizeEvent *)
{
if (_dynamicBytesPerLine)
{
int pxFixGaps = 0;
if (_addressArea)
pxFixGaps = addressWidth() * _pxCharWidth + _pxGapAdr;
pxFixGaps += _pxGapAdrHex;
if (_asciiArea)
pxFixGaps += _pxGapHexAscii;
// +1 because the last hex value do not have space. so it is effective one char more
int charWidth = (viewport()->width() - pxFixGaps ) / _pxCharWidth + 1;
// 2 hex alfa-digits 1 space 1 ascii per byte = 4; if ascii is disabled then 3
// to prevent devision by zero use the min value 1
setBytesPerLine(std::max(charWidth / (_asciiArea ? 4 : 3),1));
}
adjust();
}
bool QHexEdit::focusNextPrevChild(bool next)
{
if (_addressArea)
{
if ( (next && _editAreaIsAscii) || (!next && !_editAreaIsAscii ))
return QWidget::focusNextPrevChild(next);
else
return false;
}
else
{
return QWidget::focusNextPrevChild(next);
}
}
// ********************************************************************** 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 + _hexCharsInLine * _pxCharWidth + _pxGapHexAscii;
// set horizontalScrollBar()
int pxWidth = _pxPosAsciiX;
if (_asciiArea)
pxWidth += _bytesPerLine*_pxCharWidth;
horizontalScrollBar()->setRange(0, pxWidth - viewport()->width());
horizontalScrollBar()->setPageStep(viewport()->width());
// set verticalScrollbar()
_rowsShown = ((viewport()->height()-4)/_pxCharHeight);
int lineCount = (int)((_chunks->size() - 1) / (qint64)_bytesPerLine) + 1;
verticalScrollBar()->setRange(0, lineCount - _rowsShown);
verticalScrollBar()->setPageStep(_rowsShown);
int value = verticalScrollBar()->value();
_bPosFirst = (qint64)value * _bytesPerLine;
_bPosLast = _bPosFirst + (qint64)(_rowsShown * _bytesPerLine) - 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 + _bytesPerLine + 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);
}