ostinato/extra/qhexedit2/src/qhexedit_p.cpp
2010-11-03 19:39:36 +05:30

415 lines
11 KiB
C++

#include <QtGui>
#include "qhexedit_p.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)
{
_scrollArea = parent;
setAddressWidth(4);
setAddressOffset(0);
setAddressArea(true);
setAsciiArea(true);
setHighlighting(true);
setOverwriteMode(true);
setAddressAreaColor(QColor(Qt::lightGray).lighter(110));
setHighlightingColor(QColor(Qt::yellow).lighter(160));
setFont(QFont("Mono", 10));
connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor()));
_cursorTimer.setInterval(500);
_cursorTimer.start();
setFocusPolicy(Qt::StrongFocus);
}
void QHexEditPrivate::setAddressOffset(int offset)
{
_addressOffset = offset;
adjust();
}
int QHexEditPrivate::addressOffset()
{
return _addressOffset;
}
void QHexEditPrivate::setData(const QByteArray &data)
{
_data = data;
_originalData = data;
adjust();
setCursorPos(0);
setFocus();
}
QByteArray QHexEditPrivate::data()
{
return _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::setOverwriteMode(bool overwriteMode)
{
if (overwriteMode != _overwriteMode)
{
emit overwriteModeChanged(overwriteMode);
_overwriteMode = overwriteMode;
adjust();
}
}
bool QHexEditPrivate::overwriteMode()
{
return _overwriteMode;
}
void QHexEditPrivate::insert(int i, const QByteArray & ba)
{
_data.insert(i, ba);
_originalData.insert(i, ba);
}
void QHexEditPrivate::insert(int i, char ch)
{
_data.insert(i, ch);
_originalData.insert(i, ch);
}
void QHexEditPrivate::remove(int index, int len)
{
_data.remove(index, len);
_originalData.remove(index, len);
}
void QHexEditPrivate::setAddressArea(bool addressArea)
{
_addressArea = addressArea;
adjust();
setCursorPos(_cursorPosition);
}
void QHexEditPrivate::setAddressWidth(int addressWidth)
{
if ((addressWidth >= 0) and (addressWidth<=6))
{
_addressNumbers = addressWidth;
adjust();
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::keyPressEvent(QKeyEvent *event)
{
bool down = false;
int charX = (_cursorX - _xPosHex) / _charWidth;
int posX = (charX / 3) * 2 + (charX % 3);
int posBa = (_cursorY / _charHeight) * BYTES_PER_LINE + posX / 2;
int key = int(event->text()[0].toAscii());
if ((key>='0' && key<='9') || (key>='a' && key <= 'f'))
{
// calc address
// insert char
if (_overwriteMode == false)
if ((charX % 3) == 0)
{
insert(posBa, char(0));
adjust();
}
QByteArray hexValue = _data.mid(posBa, 1).toHex();
if ((charX % 3) == 0)
hexValue[0] = key;
else
hexValue[1] = key;
_data.replace(posBa, 1, QByteArray().fromHex(hexValue));
emit dataChanged();
setCursorPos(_cursorPosition + 1);
down = true;
}
// delete char
if (event->matches(QKeySequence::Delete))
remove(posBa);
if (event->key() == Qt::Key_Backspace)
{
remove(posBa - 1);
setCursorPos(_cursorPosition - 2);
}
// handle other function keys
if (event->key() == Qt::Key_Insert)
setOverwriteMode(!_overwriteMode);
if (event->matches(QKeySequence::MoveToNextChar))
{
setCursorPos(_cursorPosition + 1);
down = true;
}
if (event->matches(QKeySequence::MoveToPreviousChar))
setCursorPos(_cursorPosition - 1);
if (event->matches(QKeySequence::MoveToStartOfLine))
setCursorPos(_cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)));
if (event->matches(QKeySequence::MoveToEndOfLine))
setCursorPos(_cursorPosition | (2 * BYTES_PER_LINE -1));
if (event->matches(QKeySequence::MoveToPreviousLine))
setCursorPos(_cursorPosition - (2 * BYTES_PER_LINE));
if (event->matches(QKeySequence::MoveToPreviousPage))
setCursorPos(_cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE));
if (event->matches(QKeySequence::MoveToStartOfDocument))
setCursorPos(0);
if (event->matches(QKeySequence::MoveToNextLine))
{
setCursorPos(_cursorPosition + (2 * BYTES_PER_LINE));
down = true;
}
if (event->matches(QKeySequence::MoveToEndOfDocument))
{
setCursorPos(_data.size() * 2);
down = true;
}
if (event->matches(QKeySequence::MoveToNextPage))
{
setCursorPos(_cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE));
down = true;
}
// when we move downwards, we have to go a little further
if (down)
_scrollArea->ensureVisible(_cursorX, _cursorY, 3, 3 + _charHeight);
else
_scrollArea->ensureVisible(_cursorX, _cursorY, 3, 3);
update();
}
void QHexEditPrivate::mousePressEvent(QMouseEvent * event)
{
setCursorPos(event->pos());
}
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 > _data.size())
lastLineIdx = _data.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 + _addressOffset, _realAddressNumbers, 16, QChar('0'));
painter.drawText(_xPosAdr, yPos, address);
}
}
// paint hex area
QByteArray hexBa(_data.mid(firstLineIdx, lastLineIdx - firstLineIdx + 1).toHex());
QBrush highLighted = QBrush(_highlightingColor);
painter.setBackground(highLighted);
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) < _data.size() and (colIdx < BYTES_PER_LINE)); colIdx++)
{
// hilight diff bytes
if (_highlighting)
{
int posBa = lineIdx + colIdx;
if (posBa >= _originalData.size())
painter.setBackgroundMode(Qt::TransparentMode);
else
if (_data[posBa] == _originalData[posBa])
painter.setBackgroundMode(Qt::TransparentMode);
else
painter.setBackgroundMode(Qt::OpaqueMode);
}
// 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);
// paint ascii area
if (_asciiArea)
{
for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight)
{
QByteArray ascii = _data.mid(lineIdx, BYTES_PER_LINE);
for (int idx=0; idx < ascii.size(); idx++)
if (((char)ascii[idx] < 0x20) or ((char)ascii[idx] > 0x7e))
ascii[idx] = '.';
painter.drawText(_xPosAscii, yPos, ascii);
}
}
// paint cursor
if ((_data.size() > 0) and _blink)
painter.fillRect(_cursorX, _cursorY, _cursorWidth, _cursorHeight, this->palette().color(QPalette::WindowText));
}
void QHexEditPrivate::setCursorPos(int position)
{
// delete cursor
_blink = false;
update();
// cursor in range?
if (_overwriteMode)
{
if (position > (_data.size() * 2 - 1))
position = _data.size() * 2 - 1;
} else {
if (position > (_data.size() * 2))
position = _data.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 currentAddress(_cursorPosition/2);
}
void QHexEditPrivate::setCursorPos(QPoint pos)
{
// find char under cursor
if ((pos.x() >= _xPosHex) and (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() / _charHeight) * 2 * BYTES_PER_LINE;
setCursorPos(x + y);
}
}
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();
// is addressNumbers wide enought?
QString test = QString("%1")
.arg(_data.size() + _addressOffset, _addressNumbers, 16, QChar('0'));
_realAddressNumbers = test.size();
_xPosAdr = 0;
if (_addressArea)
_xPosHex = _realAddressNumbers *_charWidth + GAP_ADR_HEX;
else
_xPosHex = 0;
_xPosAscii = _xPosHex + HEXCHARS_IN_LINE * _charWidth + GAP_HEX_ASCII;
if (_overwriteMode)
_cursorWidth = _charWidth;
else
_cursorWidth = 2;
_cursorHeight = _charHeight - 3;
// tell QAbstractScollbar, how big we are
setMinimumHeight(((_data.size()/16 + 1) * _charHeight) + 3);
setMinimumWidth(_xPosAscii + (BYTES_PER_LINE * _charWidth));
update();
}