Integrated newer version (0.6.1) of the QHexEdit Widget from http://qhexedit2.googlecode.com. This newer version supports copy-paste and undo-redo.

This commit is contained in:
Srivats P. 2011-10-23 17:39:15 +05:30
parent baf709b24d
commit 763219e153
9 changed files with 1137 additions and 185 deletions

View File

@ -1,8 +1,12 @@
TEMPLATE = lib
CONFIG += qt staticlib warn_on
HEADERS = src/qhexedit.h \
src/qhexedit_p.h
HEADERS = src/commands.h\
src/qhexedit.h \
src/qhexedit_p.h \
src/xbytearray.h
SOURCES = src/qhexedit.cpp \
src/qhexedit_p.cpp
SOURCES = src/commands.cpp \
src/qhexedit.cpp \
src/qhexedit_p.cpp \
src/xbytearray.cpp

View File

@ -0,0 +1,115 @@
#include "commands.h"
CharCommand::CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar, QUndoCommand *parent)
: QUndoCommand(parent)
{
_xData = xData;
_charPos = charPos;
_newChar = newChar;
_cmd = cmd;
}
bool CharCommand::mergeWith(const QUndoCommand *command)
{
const CharCommand *nextCommand = static_cast<const CharCommand *>(command);
bool result = false;
if (_cmd != remove)
{
if (nextCommand->_cmd == replace)
if (nextCommand->_charPos == _charPos)
{
_newChar = nextCommand->_newChar;
result = true;
}
}
return result;
}
void CharCommand::undo()
{
switch (_cmd)
{
case insert:
_xData->remove(_charPos, 1);
break;
case replace:
_xData->replace(_charPos, _oldChar);
_xData->setDataChanged(_charPos, _wasChanged);
break;
case remove:
_xData->insert(_charPos, _oldChar);
_xData->setDataChanged(_charPos, _wasChanged);
break;
}
}
void CharCommand::redo()
{
switch (_cmd)
{
case insert:
_xData->insert(_charPos, _newChar);
break;
case replace:
_oldChar = _xData->data()[_charPos];
_wasChanged = _xData->dataChanged(_charPos);
_xData->replace(_charPos, _newChar);
break;
case remove:
_oldChar = _xData->data()[_charPos];
_wasChanged = _xData->dataChanged(_charPos);
_xData->remove(_charPos, 1);
break;
}
}
ArrayCommand::ArrayCommand(XByteArray * xData, Cmd cmd, int baPos, QByteArray newBa, int len, QUndoCommand *parent)
: QUndoCommand(parent)
{
_cmd = cmd;
_xData = xData;
_baPos = baPos;
_newBa = newBa;
_len = len;
}
void ArrayCommand::undo()
{
switch (_cmd)
{
case insert:
_xData->remove(_baPos, _newBa.length());
break;
case replace:
_xData->replace(_baPos, _oldBa);
_xData->setDataChanged(_baPos, _wasChanged);
break;
case remove:
_xData->insert(_baPos, _oldBa);
_xData->setDataChanged(_baPos, _wasChanged);
break;
}
}
void ArrayCommand::redo()
{
switch (_cmd)
{
case insert:
_xData->insert(_baPos, _newBa);
break;
case replace:
_oldBa = _xData->data().mid(_baPos, _len);
_wasChanged = _xData->dataChanged(_baPos, _len);
_xData->replace(_baPos, _newBa);
break;
case remove:
_oldBa = _xData->data().mid(_baPos, _len);
_wasChanged = _xData->dataChanged(_baPos, _len);
_xData->remove(_baPos, _len);
break;
}
}

View File

@ -0,0 +1,70 @@
#ifndef COMMANDS_H
#define COMMANDS_H
/** \cond docNever */
#include <QUndoCommand>
#include "xbytearray.h"
/*! CharCommand is a class to prived undo/redo functionality in QHexEdit.
A QUndoCommand represents a single editing action on a document. CharCommand
is responsable for manipulations on single chars. It can insert. replace and
remove characters. A manipulation stores allways to actions
1. redo (or do) action
2. undo action.
CharCommand also supports command compression via mergeWidht(). This allows
the user to execute a undo command contation e.g. 3 steps in a single command.
If you for example insert a new byt "34" this means for the editor doing 3
steps: insert a "00", replace it with "03" and the replace it with "34". These
3 steps are combined into a single step, insert a "34".
*/
class CharCommand : public QUndoCommand
{
public:
enum { Id = 1234 };
enum Cmd {insert, remove, replace};
CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar,
QUndoCommand *parent=0);
void undo();
void redo();
bool mergeWith(const QUndoCommand *command);
int id() const { return Id; }
private:
XByteArray * _xData;
int _charPos;
bool _wasChanged;
char _newChar;
char _oldChar;
Cmd _cmd;
};
/*! ArrayCommand provides undo/redo functionality for handling binary strings. It
can undo/redo insert, replace and remove binary strins (QByteArrays).
*/
class ArrayCommand : public QUndoCommand
{
public:
enum Cmd {insert, remove, replace};
ArrayCommand(XByteArray * xData, Cmd cmd, int baPos, QByteArray newBa=QByteArray(), int len=0,
QUndoCommand *parent=0);
void undo();
void redo();
private:
Cmd _cmd;
XByteArray * _xData;
int _baPos;
int _len;
QByteArray _wasChanged;
QByteArray _newBa;
QByteArray _oldBa;
};
/** \endcond docNever */
#endif // COMMANDS_H

View File

@ -9,9 +9,11 @@ QHexEdit::QHexEdit(QWidget *parent) : QScrollArea(parent)
setWidget(qHexEdit_p);
setWidgetResizable(true);
connect(qHexEdit_p, SIGNAL(currentAddressChanged(int)), this, SIGNAL(currentAddressChanged(int)));
connect(qHexEdit_p, SIGNAL(currentSizeChanged(int)), this, SIGNAL(currentSizeChanged(int)));
connect(qHexEdit_p, SIGNAL(dataChanged()), this, SIGNAL(dataChanged()));
connect(qHexEdit_p, SIGNAL(currentAddress(int)), this, SIGNAL(currentAddress(int)));
connect(qHexEdit_p, SIGNAL(overwriteModeChanged(bool)), this, SIGNAL(overwriteModeChanged(bool)));
setFocusPolicy(Qt::NoFocus);
}
void QHexEdit::insert(int i, const QByteArray & ba)
@ -29,11 +31,31 @@ void QHexEdit::remove(int pos, int len)
qHexEdit_p->remove(pos, len);
}
QString QHexEdit::toReadableString()
{
return qHexEdit_p->toRedableString();
}
QString QHexEdit::selectionToReadableString()
{
return qHexEdit_p->selectionToReadableString();
}
void QHexEdit::setAddressArea(bool addressArea)
{
qHexEdit_p->setAddressArea(addressArea);
}
void QHexEdit::redo()
{
qHexEdit_p->redo();
}
void QHexEdit::undo()
{
qHexEdit_p->undo();
}
void QHexEdit::setAddressWidth(int addressWidth)
{
qHexEdit_p->setAddressWidth(addressWidth);
@ -56,7 +78,7 @@ void QHexEdit::setAddressOffset(int offset)
int QHexEdit::addressOffset()
{
return addressOffset();
return qHexEdit_p->addressOffset();
}
void QHexEdit::setData(const QByteArray &data)
@ -89,6 +111,16 @@ QColor QHexEdit::highlightingColor()
return qHexEdit_p->highlightingColor();
}
void QHexEdit::setSelectionColor(const QColor &color)
{
qHexEdit_p->setSelectionColor(color);
}
QColor QHexEdit::selectionColor()
{
return qHexEdit_p->selectionColor();
}
void QHexEdit::setOverwriteMode(bool overwriteMode)
{
qHexEdit_p->setOverwriteMode(overwriteMode);
@ -99,8 +131,22 @@ bool QHexEdit::overwriteMode()
return qHexEdit_p->overwriteMode();
}
void QHexEdit::setReadOnly(bool readOnly)
{
qHexEdit_p->setReadOnly(readOnly);
}
bool QHexEdit::isReadOnly()
{
return qHexEdit_p->isReadOnly();
}
void QHexEdit::setFont(const QFont &font)
{
qHexEdit_p->setFont(font);
}
const QFont & QHexEdit::font() const
{
return qHexEdit_p->font();
}

View File

@ -7,27 +7,40 @@
/** \mainpage
QHexEdit is a binary editor widget for Qt.
\version Version 0.4.3
\version Version 0.6.1
\image html hexedit.png
*/
/*! QHexEdit is a hex editor widget written in C++ for the Qt (Qt4) framework.
It is a simple editor for binary data, just like QPlainTextEdit is for text data.
There are sip configuration files included, so it is easy to create bindings
for PyQt and you can use this widget also in python.
It is a simple editor for binary data, just like QPlainTextEdit is for text
data. There are sip configuration files included, so it is easy to create
bindings for PyQt and you can use this widget also in python.
QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use the
mouse or the keyboard to navigate inside the widget. If you hit the keys (0..9, a..f)
you will change the data. Changed data is highlighted and can be accessed via data().
QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use
the mouse or the keyboard to navigate inside the widget. If you hit the keys
(0..9, a..f) you will change the data. Changed data is highlighted and can be
accessed via data().
Normaly QHexEdit works in the overwrite Mode. You can set overwriteMode(false) and
insert data. In this case the size of data() increases. It is also possible to delete
bytes under the cursor, here the size of data decreases.
Normaly QHexEdit works in the overwrite Mode. You can set overwriteMode(false)
and insert data. In this case the size of data() increases. It is also possible
to delete bytes (del or backspace), here the size of data decreases.
There are some limitations: The size of data has in general to be below 10 megabytes,
otherwise the scroll sliders ard not shown and you can't scroll any more. Copy and
paste functionality is perhaps a subject of a later release.
You can select data with keyboard hits or mouse movements. The copy-key will
copy the selected data into the clipboard. The cut-key copies also but delets
it afterwards. In overwrite mode, the paste function overwrites the content of
the (does not change the length) data. In insert mode, clipboard data will be
inserted. The clipboard content is expected in ASCII Hex notation. Unknown
characters will be ignored.
QHexEdit comes with undo/redo functionality. All changes can be undone, by
pressing the undo-key (usually ctr-z). They can also be redone afterwards.
The undo/redo framework is cleared, when setData() sets up a new
content for the editor.
This widget can only handle small amounts of data. The size has to be below 10
megabytes, otherwise the scroll sliders ard not shown and you can't scroll any
more.
*/
class QHexEdit : public QScrollArea
{
@ -55,11 +68,30 @@ paste functionality is perhaps a subject of a later release.
*/
Q_PROPERTY(QColor highlightingColor READ highlightingColor WRITE setHighlightingColor)
/*! Property selection color sets (setSelectionColor()) the backgorund
color of selected text areas. You can also read the color
(selectionColor()).
*/
Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor)
/*! Porperty overwrite mode sets (setOverwriteMode()) or gets (overwriteMode()) the mode
in which the editor works. In overwritem mode the user will overwrite existing data.
in which the editor works. In overwrite mode the user will overwrite existing data. The
size of data will be constant. In insert mode the size will grow, when inserting
new data.
*/
Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode)
/*! Porperty readOnly sets (setReadOnly()) or gets (isReadOnly) the mode
in which the editor works. In readonly mode the the user can only navigate
through the data and select data; modifying is not possible. This
property's default is false.
*/
Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
/*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/
Q_PROPERTY(QFont font READ font WRITE setFont)
public:
/*! Creates an instance of QHexEdit.
\param parent Parent widget of QHexEdit.
@ -69,23 +101,33 @@ public:
/*! Inserts a byte array.
\param i Index position, where to insert
\param ba byte array, which is to insert
In overwrite mode, the existing data will be overwritten, in insertmode ba will be
insertet and size of data grows.
*/
void insert(int i, const QByteArray & ba);
/*! Inserts a char.
\param i Index position, where to insert
\param ch Char, which is to insert
In overwrite mode, the existing data will be overwritten, in insertmode ba will be
insertet and size of data grows.
*/
void insert(int i, char ch);
/*! Removes len bytes from the content.
\param pos Index position, where to remove
\param len Amount of bytes to remove
In overwrite mode, the existing bytes will be overwriten with 0x00.
*/
void remove(int pos, int len=1);
/*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/
void setFont(const QFont &);
/*! Gives back a formatted image of the content of QHexEdit
*/
QString toReadableString();
/*! Gives back a formatted image of the selected content of QHexEdit
*/
QString selectionToReadableString();
/*! \cond docNever */
void setAddressOffset(int offset);
@ -96,11 +138,21 @@ public:
QColor addressAreaColor();
void setHighlightingColor(QColor const &color);
QColor highlightingColor();
void setSelectionColor(QColor const &color);
QColor selectionColor();
void setOverwriteMode(bool);
bool overwriteMode();
void setReadOnly(bool);
bool isReadOnly();
const QFont &font() const;
void setFont(const QFont &);
/*! \endcond docNever */
public slots:
/*! Redoes the last operation. If there is no operation to redo, i.e.
there is no redo step in the undo/redo history, nothing happens.
*/
void redo();
/*! Set the minimum width of the address area.
\param addressWidth Width in characters.
@ -122,10 +174,18 @@ public slots:
*/
void setHighlighting(bool mode);
/*! Undoes the last operation. If there is no operation to undo, i.e.
there is no undo step in the undo/redo history, nothing happens.
*/
void undo();
signals:
/*! Contains the address, where the cursor is located. */
void currentAddress(int address);
void currentAddressChanged(int address);
/*! Contains the size of the data to edit. */
void currentSizeChanged(int size);
/*! The signal is emited every time, the data is changed. */
void dataChanged();

View File

@ -1,6 +1,7 @@
#include <QtGui>
#include "qhexedit_p.h"
#include "commands.h"
const int HEXCHARS_IN_LINE = 47;
const int GAP_ADR_HEX = 10;
@ -9,6 +10,8 @@ const int BYTES_PER_LINE = 16;
QHexEditPrivate::QHexEditPrivate(QScrollArea *parent) : QWidget(parent)
{
_undoStack = new QUndoStack(this);
_scrollArea = parent;
setAddressWidth(4);
setAddressOffset(0);
@ -16,41 +19,44 @@ QHexEditPrivate::QHexEditPrivate(QScrollArea *parent) : QWidget(parent)
setAsciiArea(true);
setHighlighting(true);
setOverwriteMode(true);
setAddressAreaColor(QColor(Qt::lightGray).lighter(110));
setHighlightingColor(QColor(Qt::yellow).lighter(160));
setReadOnly(false);
setAddressAreaColor(QColor(0xd4, 0xd4, 0xd4, 0xff));
setHighlightingColor(QColor(0xff, 0xff, 0x99, 0xff));
setSelectionColor(QColor(0x6d, 0x9e, 0xff, 0xff));
setFont(QFont("Courier", 10));
setFont(QFont("Mono", 10));
connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor()));
_cursorTimer.setInterval(500);
_cursorTimer.start();
_size = 0;
resetSelection(0);
setFocusPolicy(Qt::StrongFocus);
connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor()));
_cursorTimer.setInterval(500);
_cursorTimer.start();
}
void QHexEditPrivate::setAddressOffset(int offset)
{
_addressOffset = offset;
_xData.setAddressOffset(offset);
adjust();
}
int QHexEditPrivate::addressOffset()
{
return _addressOffset;
return _xData.addressOffset();
}
void QHexEditPrivate::setData(const QByteArray &data)
{
_data = data;
_originalData = data;
_xData.setData(data);
_undoStack->clear();
adjust();
setCursorPos(0);
setFocus();
}
QByteArray QHexEditPrivate::data()
{
return _data;
return _xData.data();
}
void QHexEditPrivate::setAddressAreaColor(const QColor &color)
@ -75,54 +81,123 @@ QColor QHexEditPrivate::highlightingColor()
return _highlightingColor;
}
void QHexEditPrivate::setOverwriteMode(bool overwriteMode)
void QHexEditPrivate::setSelectionColor(const QColor &color)
{
if (overwriteMode != _overwriteMode)
_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;
}
void QHexEditPrivate::insert(int index, const QByteArray & ba)
{
if (ba.length() > 0)
{
emit overwriteModeChanged(overwriteMode);
_overwriteMode = overwriteMode;
adjust();
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();
}
}
}
bool QHexEditPrivate::overwriteMode()
void QHexEditPrivate::insert(int index, char ch)
{
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);
QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::insert, index, ch);
_undoStack->push(charCommand);
emit dataChanged();
}
void QHexEditPrivate::remove(int index, int len)
{
_data.remove(index, len);
_originalData.remove(index, 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);
emit dataChanged();
}
void QHexEditPrivate::replace(int index, const QByteArray & ba)
{
QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length());
_undoStack->push(arrayCommand);
emit dataChanged();
}
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);
}
_xData.setAddressWidth(addressWidth);
setCursorPos(_cursorPosition);
}
void QHexEditPrivate::setAsciiArea(bool asciiArea)
@ -143,95 +218,340 @@ void QHexEditPrivate::setHighlighting(bool 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)
{
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);
/*****************************************************************************/
/* Cursor movements */
/*****************************************************************************/
if (event->matches(QKeySequence::MoveToNextChar))
{
setCursorPos(_cursorPosition + 1);
down = true;
resetSelection(_cursorPosition);
}
if (event->matches(QKeySequence::MoveToPreviousChar))
setCursorPos(_cursorPosition - 1);
if (event->matches(QKeySequence::MoveToStartOfLine))
setCursorPos(_cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)));
{
setCursorPos(_cursorPosition - 1);
resetSelection(_cursorPosition);
}
if (event->matches(QKeySequence::MoveToEndOfLine))
setCursorPos(_cursorPosition | (2 * BYTES_PER_LINE -1));
{
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));
if (event->matches(QKeySequence::MoveToPreviousPage))
setCursorPos(_cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE));
if (event->matches(QKeySequence::MoveToStartOfDocument))
setCursorPos(0);
{
setCursorPos(_cursorPosition - (2 * BYTES_PER_LINE));
resetSelection(_cursorPosition);
}
if (event->matches(QKeySequence::MoveToNextLine))
{
setCursorPos(_cursorPosition + (2 * BYTES_PER_LINE));
down = true;
}
if (event->matches(QKeySequence::MoveToEndOfDocument))
{
setCursorPos(_data.size() * 2);
down = true;
resetSelection(_cursorPosition);
}
if (event->matches(QKeySequence::MoveToNextPage))
{
setCursorPos(_cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE));
down = true;
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);
}
// 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);
/*****************************************************************************/
/* 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 */
int key = int(event->text()[0].toAscii());
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);
}
_scrollArea->ensureVisible(_cursorX, _cursorY + _charHeight/2, 3, _charHeight/2 + 2);
update();
}
void QHexEditPrivate::mouseMoveEvent(QMouseEvent * event)
{
_blink = false;
update();
int actPos = cursorPos(event->pos());
setCursorPos(actPos);
setSelection(actPos);
}
void QHexEditPrivate::mousePressEvent(QMouseEvent * event)
{
setCursorPos(event->pos());
_blink = false;
update();
int cPos = cursorPos(event->pos());
resetSelection(cPos);
setCursorPos(cPos);
}
void QHexEditPrivate::paintEvent(QPaintEvent *event)
@ -256,8 +576,8 @@ void QHexEditPrivate::paintEvent(QPaintEvent *event)
if (firstLineIdx < 0)
firstLineIdx = 0;
int lastLineIdx = ((event->rect().bottom() / _charHeight) + _charHeight) * BYTES_PER_LINE;
if (lastLineIdx > _data.size())
lastLineIdx = _data.size();
if (lastLineIdx > _xData.size())
lastLineIdx = _xData.size();
int yPosStart = ((firstLineIdx) / BYTES_PER_LINE) * _charHeight + _charHeight;
// paint address area
@ -266,33 +586,51 @@ void QHexEditPrivate::paintEvent(QPaintEvent *event)
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'));
.arg(lineIdx + _xData.addressOffset(), _xData.realAddressNumbers(), 16, QChar('0'));
painter.drawText(_xPosAdr, yPos, address);
}
}
// paint hex area
QByteArray hexBa(_data.mid(firstLineIdx, lastLineIdx - firstLineIdx + 1).toHex());
QByteArray hexBa(_xData.data().mid(firstLineIdx, lastLineIdx - firstLineIdx + 1).toHex());
QBrush highLighted = QBrush(_highlightingColor);
painter.setBackground(highLighted);
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) < _data.size() and (colIdx < BYTES_PER_LINE)); colIdx++)
for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() and (colIdx < BYTES_PER_LINE)); colIdx++)
{
// hilight diff bytes
if (_highlighting)
int posBa = lineIdx + colIdx;
if ((getSelectionBegin() <= posBa) && (getSelectionEnd() > posBa))
{
int posBa = lineIdx + colIdx;
if (posBa >= _originalData.size())
painter.setBackgroundMode(Qt::TransparentMode);
else
if (_data[posBa] == _originalData[posBa])
painter.setBackgroundMode(Qt::TransparentMode);
else
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
@ -306,26 +644,40 @@ void QHexEditPrivate::paintEvent(QPaintEvent *event)
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)
{
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);
int xPosAscii = _xPosAscii;
for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() and (colIdx < BYTES_PER_LINE)); colIdx++)
{
painter.drawText(xPosAscii, yPos, _xData.asciiChar(lineIdx + colIdx));
xPosAscii += _charWidth;
}
}
}
// paint cursor
if ((_data.size() > 0) and _blink)
painter.fillRect(_cursorX, _cursorY, _cursorWidth, _cursorHeight, this->palette().color(QPalette::WindowText));
if (_blink)
{
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)
@ -337,11 +689,11 @@ void QHexEditPrivate::setCursorPos(int position)
// cursor in range?
if (_overwriteMode)
{
if (position > (_data.size() * 2 - 1))
position = _data.size() * 2 - 1;
if (position > (_xData.size() * 2 - 1))
position = _xData.size() * 2 - 1;
} else {
if (position > (_data.size() * 2))
position = _data.size() * 2;
if (position > (_xData.size() * 2))
position = _xData.size() * 2;
}
if (position < 0)
@ -356,11 +708,12 @@ void QHexEditPrivate::setCursorPos(int position)
// immiadately draw cursor
_blink = true;
update();
emit currentAddress(_cursorPosition/2);
emit currentAddressChanged(_cursorPosition/2);
}
void QHexEditPrivate::setCursorPos(QPoint pos)
int QHexEditPrivate::cursorPos(QPoint pos)
{
int result = -1;
// find char under cursor
if ((pos.x() >= _xPosHex) and (pos.x() < (_xPosHex + HEXCHARS_IN_LINE * _charWidth)))
{
@ -369,11 +722,55 @@ void QHexEditPrivate::setCursorPos(QPoint pos)
x = (x / 3) * 2;
else
x = ((x / 3) * 2) + 1;
int y = (pos.y() / _charHeight) * 2 * BYTES_PER_LINE;
setCursorPos(x + y);
int y = ((pos.y() - 3) / _charHeight) * 2 * BYTES_PER_LINE;
result = x + y;
}
return result;
}
int QHexEditPrivate::cursorPos()
{
return _cursorPosition;
}
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)
@ -388,26 +785,15 @@ 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;
_xPosHex = _xData.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);
setMinimumHeight(((_xData.size()/16 + 1) * _charHeight) + 5);
setMinimumWidth(_xPosAscii + (BYTES_PER_LINE * _charWidth));
update();

View File

@ -5,6 +5,7 @@
#include <QtGui>
#include "xbytearray.h"
class QHexEditPrivate : public QWidget
{
@ -13,24 +14,37 @@ Q_OBJECT
public:
QHexEditPrivate(QScrollArea *parent);
void setAddressAreaColor(QColor const &color);
QColor addressAreaColor();
void setAddressOffset(int offset);
int addressOffset();
void setCursorPos(int position);
int cursorPos();
void setData(QByteArray const &data);
QByteArray data();
void setAddressAreaColor(QColor const &color);
QColor addressAreaColor();
void setHighlightingColor(QColor const &color);
QColor highlightingColor();
void setOverwriteMode(bool overwriteMode);
bool overwriteMode();
void insert(int i, const QByteArray & ba);
void insert(int i, char ch);
void setReadOnly(bool readOnly);
bool isReadOnly();
void setSelectionColor(QColor const &color);
QColor selectionColor();
XByteArray & xData();
void insert(int index, const QByteArray & ba);
void insert(int index, char ch);
void remove(int index, int len=1);
void replace(int index, char ch);
void replace(int index, const QByteArray & ba);
void setAddressArea(bool addressArea);
void setAddressWidth(int addressWidth);
@ -38,17 +52,32 @@ public:
void setHighlighting(bool mode);
virtual void setFont(const QFont &font);
void undo();
void redo();
QString toRedableString();
QString selectionToReadableString();
signals:
void currentAddress(int address);
void currentAddressChanged(int address);
void currentSizeChanged(int size);
void dataChanged();
void overwriteModeChanged(bool state);
protected:
void keyPressEvent(QKeyEvent * event);
void mouseMoveEvent(QMouseEvent * event);
void mousePressEvent(QMouseEvent * event);
void paintEvent(QPaintEvent *event);
void setCursorPos(QPoint pos);
void setCursorPos(int position);
int cursorPos(QPoint pos); // calc cursorpos from graphics position. DOES NOT STORE POSITION
void resetSelection(int pos);
void setSelection(int pos); // set min (if below init) or max (if greater init)
int getSelectionBegin();
int getSelectionEnd();
private slots:
void updateCursor();
@ -57,23 +86,32 @@ private:
void adjust();
QColor _addressAreaColor;
QByteArray _data;
QByteArray _originalData;
QColor _highlightingColor;
QColor _selectionColor;
QScrollArea *_scrollArea;
QTimer _cursorTimer;
QUndoStack *_undoStack;
bool _blink;
bool _addressArea;
bool _asciiArea;
bool _highlighting;
XByteArray _xData; // Hält den Inhalt des Hex Editors
bool _blink; // true: then cursor blinks
bool _renderingRequired; // Flag to store that rendering is necessary
bool _addressArea; // left area of QHexEdit
bool _asciiArea; // medium area
bool _highlighting; // highlighting of changed bytes
bool _overwriteMode;
bool _readOnly; // true: the user can only look and navigate
int _addressNumbers, _realAddressNumbers;
int _addressOffset;
int _charWidth, _charHeight;
int _cursorX, _cursorY, _cursorWidth, _cursorHeight, _cursorPosition;
int _xPosAdr, _xPosHex, _xPosAscii;
int _charWidth, _charHeight; // char dimensions (dpendend on font)
int _cursorX, _cursorY; // graphics position of the cursor
int _cursorPosition; // charakter positioin in stream (on byte ends in to steps)
int _xPosAdr, _xPosHex, _xPosAscii; // graphics x-position of the areas
int _selectionBegin; // First selected char
int _selectionEnd; // Last selected char
int _selectionInit; // That's, where we pressed the mouse button
int _size;
};
/** \endcond docNever */

View File

@ -0,0 +1,167 @@
#include "xbytearray.h"
XByteArray::XByteArray()
{
_oldSize = -99;
_addressNumbers = 4;
_addressOffset = 0;
}
int XByteArray::addressOffset()
{
return _addressOffset;
}
void XByteArray::setAddressOffset(int offset)
{
_addressOffset = offset;
}
int XByteArray::addressWidth()
{
return _addressNumbers;
}
void XByteArray::setAddressWidth(int width)
{
if ((width >= 0) and (width<=6))
{
_addressNumbers = width;
}
}
QByteArray & XByteArray::data()
{
return _data;
}
void XByteArray::setData(QByteArray data)
{
_data = data;
_changedData = QByteArray(data.length(), char(0));
}
bool XByteArray::dataChanged(int i)
{
return bool(_changedData[i]);
}
QByteArray XByteArray::dataChanged(int i, int len)
{
return _changedData.mid(i, len);
}
void XByteArray::setDataChanged(int i, bool state)
{
_changedData[i] = char(state);
}
void XByteArray::setDataChanged(int i, const QByteArray & state)
{
int length = state.length();
int len;
if ((i + length) > _changedData.length())
len = _changedData.length() - i;
else
len = length;
_changedData.replace(i, len, state);
}
int XByteArray::realAddressNumbers()
{
if (_oldSize != _data.size())
{
// is addressNumbers wide enought?
QString test = QString("%1")
.arg(_data.size() + _addressOffset, _addressNumbers, 16, QChar('0'));
_realAddressNumbers = test.size();
}
return _realAddressNumbers;
}
int XByteArray::size()
{
return _data.size();
}
QByteArray & XByteArray::insert(int i, char ch)
{
_data.insert(i, ch);
_changedData.insert(i, char(1));
return _data;
}
QByteArray & XByteArray::insert(int i, const QByteArray & ba)
{
_data.insert(i, ba);
_changedData.insert(i, QByteArray(ba.length(), char(1)));
return _data;
}
QByteArray & XByteArray::remove(int i, int len)
{
_data.remove(i, len);
_changedData.remove(i, len);
return _data;
}
QByteArray & XByteArray::replace(int index, char ch)
{
_data[index] = ch;
_changedData[index] = char(1);
return _data;
}
QByteArray & XByteArray::replace(int index, const QByteArray & ba)
{
int len = ba.length();
return replace(index, len, ba);
}
QByteArray & XByteArray::replace(int index, int length, const QByteArray & ba)
{
int len;
if ((index + length) > _data.length())
len = _data.length() - index;
else
len = length;
_data.replace(index, len, ba.mid(0, len));
_changedData.replace(index, len, QByteArray(len, char(1)));
return _data;
}
QChar XByteArray::asciiChar(int index)
{
char ch = _data[index];
if ((ch < 0x20) or (ch > 0x7e))
ch = '.';
return QChar(ch);
}
QString XByteArray::toRedableString(int start, int end)
{
int adrWidth = realAddressNumbers();
if (_addressNumbers > adrWidth)
adrWidth = _addressNumbers;
if (end < 0)
end = _data.size();
QString result;
for (int i=start; i < end; i += 16)
{
QString adrStr = QString("%1").arg(_addressOffset + i, adrWidth, 16, QChar('0'));
QString hexStr;
QString ascStr;
for (int j=0; j<16; j++)
{
if ((i + j) < _data.size())
{
hexStr.append(" ").append(_data.mid(i+j, 1).toHex());
ascStr.append(asciiChar(i+j));
}
}
result += adrStr + " " + QString("%1").arg(hexStr, -48) + " " + QString("%1").arg(ascStr, -17) + "\n";
}
return result;
}

View File

@ -0,0 +1,66 @@
#ifndef XBYTEARRAY_H
#define XBYTEARRAY_H
/** \cond docNever */
#include <QtCore>
/*! XByteArray represents the content of QHexEcit.
XByteArray comprehend the data itself and informations to store if it was
changed. The QHexEdit component uses these informations to perform nice
rendering of the data
XByteArray also provides some functionality to insert, replace and remove
single chars and QByteArras. Additionally some functions support rendering
and converting to readable strings.
*/
class XByteArray
{
public:
explicit XByteArray();
int addressOffset();
void setAddressOffset(int offset);
int addressWidth();
void setAddressWidth(int width);
QByteArray & data();
void setData(QByteArray data);
bool dataChanged(int i);
QByteArray dataChanged(int i, int len);
void setDataChanged(int i, bool state);
void setDataChanged(int i, const QByteArray & state);
int realAddressNumbers();
int size();
QByteArray & insert(int i, char ch);
QByteArray & insert(int i, const QByteArray & ba);
QByteArray & remove(int pos, int len);
QByteArray & replace(int index, char ch);
QByteArray & replace(int index, const QByteArray & ba);
QByteArray & replace(int index, int length, const QByteArray & ba);
QChar asciiChar(int index);
QString toRedableString(int start=0, int end=-1);
signals:
public slots:
private:
QByteArray _data;
QByteArray _changedData;
int _addressNumbers; // wanted width of address area
int _addressOffset; // will be added to the real addres inside bytearray
int _realAddressNumbers; // real width of address area (can be greater then wanted width)
int _oldSize; // size of data
};
/** \endcond docNever */
#endif // XBYTEARRAY_H