parent
756197a69c
commit
5c1aa6f1c3
@ -1,12 +1,17 @@
|
|||||||
TEMPLATE = lib
|
TEMPLATE = lib
|
||||||
CONFIG += qt staticlib warn_on
|
CONFIG += qt staticlib warn_on
|
||||||
|
QT += core gui
|
||||||
|
|
||||||
HEADERS = src/commands.h\
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
|
VERSION = 4.0.0
|
||||||
|
|
||||||
|
DEFINES += QHEXEDIT_EXPORTS
|
||||||
|
|
||||||
|
HEADERS = src/chunks.h\
|
||||||
|
src/commands.h \
|
||||||
src/qhexedit.h \
|
src/qhexedit.h \
|
||||||
src/qhexedit_p.h \
|
|
||||||
src/xbytearray.h
|
|
||||||
|
|
||||||
SOURCES = src/commands.cpp \
|
SOURCES = src/chunks.cpp \
|
||||||
src/qhexedit.cpp \
|
src/commands.cpp \
|
||||||
src/qhexedit_p.cpp \
|
src/qhexedit.cpp
|
||||||
src/xbytearray.cpp
|
|
||||||
|
1
extra/qhexedit2/src/VERSION
Normal file
1
extra/qhexedit2/src/VERSION
Normal file
@ -0,0 +1 @@
|
|||||||
|
Release 0.8.4, 2017-01-16
|
323
extra/qhexedit2/src/chunks.cpp
Normal file
323
extra/qhexedit2/src/chunks.cpp
Normal file
@ -0,0 +1,323 @@
|
|||||||
|
#include "chunks.h"
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#define NORMAL 0
|
||||||
|
#define HIGHLIGHTED 1
|
||||||
|
|
||||||
|
#define BUFFER_SIZE 0x10000
|
||||||
|
#define CHUNK_SIZE 0x1000
|
||||||
|
#define READ_CHUNK_MASK Q_INT64_C(0xfffffffffffff000)
|
||||||
|
|
||||||
|
// ***************************************** Constructors and file settings
|
||||||
|
|
||||||
|
Chunks::Chunks(QObject *parent): QObject(parent)
|
||||||
|
{
|
||||||
|
QBuffer *buf = new QBuffer(this);
|
||||||
|
setIODevice(*buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
Chunks::Chunks(QIODevice &ioDevice, QObject *parent): QObject(parent)
|
||||||
|
{
|
||||||
|
setIODevice(ioDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Chunks::setIODevice(QIODevice &ioDevice)
|
||||||
|
{
|
||||||
|
_ioDevice = &ioDevice;
|
||||||
|
bool ok = _ioDevice->open(QIODevice::ReadOnly);
|
||||||
|
if (ok) // Try to open IODevice
|
||||||
|
{
|
||||||
|
_size = _ioDevice->size();
|
||||||
|
_ioDevice->close();
|
||||||
|
}
|
||||||
|
else // Fallback is an empty buffer
|
||||||
|
{
|
||||||
|
QBuffer *buf = new QBuffer(this);
|
||||||
|
_ioDevice = buf;
|
||||||
|
_size = 0;
|
||||||
|
}
|
||||||
|
_chunks.clear();
|
||||||
|
_pos = 0;
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***************************************** Getting data out of Chunks
|
||||||
|
|
||||||
|
QByteArray Chunks::data(qint64 pos, qint64 maxSize, QByteArray *highlighted)
|
||||||
|
{
|
||||||
|
qint64 ioDelta = 0;
|
||||||
|
int chunkIdx = 0;
|
||||||
|
|
||||||
|
Chunk chunk;
|
||||||
|
QByteArray buffer;
|
||||||
|
|
||||||
|
// Do some checks and some arrangements
|
||||||
|
if (highlighted)
|
||||||
|
highlighted->clear();
|
||||||
|
|
||||||
|
if (pos >= _size)
|
||||||
|
return buffer;
|
||||||
|
|
||||||
|
if (maxSize < 0)
|
||||||
|
maxSize = _size;
|
||||||
|
else
|
||||||
|
if ((pos + maxSize) > _size)
|
||||||
|
maxSize = _size - pos;
|
||||||
|
|
||||||
|
_ioDevice->open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
while (maxSize > 0)
|
||||||
|
{
|
||||||
|
chunk.absPos = LLONG_MAX;
|
||||||
|
bool chunksLoopOngoing = true;
|
||||||
|
while ((chunkIdx < _chunks.count()) && chunksLoopOngoing)
|
||||||
|
{
|
||||||
|
// In this section, we track changes before our required data and
|
||||||
|
// we take the editdet data, if availible. ioDelta is a difference
|
||||||
|
// counter to justify the read pointer to the original data, if
|
||||||
|
// data in between was deleted or inserted.
|
||||||
|
|
||||||
|
chunk = _chunks[chunkIdx];
|
||||||
|
if (chunk.absPos > pos)
|
||||||
|
chunksLoopOngoing = false;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chunkIdx += 1;
|
||||||
|
qint64 count;
|
||||||
|
qint64 chunkOfs = pos - chunk.absPos;
|
||||||
|
if (maxSize > ((qint64)chunk.data.size() - chunkOfs))
|
||||||
|
{
|
||||||
|
count = (qint64)chunk.data.size() - chunkOfs;
|
||||||
|
ioDelta += CHUNK_SIZE - chunk.data.size();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
count = maxSize;
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
buffer += chunk.data.mid(chunkOfs, (int)count);
|
||||||
|
maxSize -= count;
|
||||||
|
pos += count;
|
||||||
|
if (highlighted)
|
||||||
|
*highlighted += chunk.dataChanged.mid(chunkOfs, (int)count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((maxSize > 0) && (pos < chunk.absPos))
|
||||||
|
{
|
||||||
|
// In this section, we read data from the original source. This only will
|
||||||
|
// happen, whe no copied data is available
|
||||||
|
|
||||||
|
qint64 byteCount;
|
||||||
|
QByteArray readBuffer;
|
||||||
|
if ((chunk.absPos - pos) > maxSize)
|
||||||
|
byteCount = maxSize;
|
||||||
|
else
|
||||||
|
byteCount = chunk.absPos - pos;
|
||||||
|
|
||||||
|
maxSize -= byteCount;
|
||||||
|
_ioDevice->seek(pos + ioDelta);
|
||||||
|
readBuffer = _ioDevice->read(byteCount);
|
||||||
|
buffer += readBuffer;
|
||||||
|
if (highlighted)
|
||||||
|
*highlighted += QByteArray(readBuffer.size(), NORMAL);
|
||||||
|
pos += readBuffer.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ioDevice->close();
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Chunks::write(QIODevice &iODevice, qint64 pos, qint64 count)
|
||||||
|
{
|
||||||
|
if (count == -1)
|
||||||
|
count = _size;
|
||||||
|
bool ok = iODevice.open(QIODevice::WriteOnly);
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
for (qint64 idx=pos; idx < count; idx += BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
QByteArray ba = data(idx, BUFFER_SIZE);
|
||||||
|
iODevice.write(ba);
|
||||||
|
}
|
||||||
|
iODevice.close();
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***************************************** Set and get highlighting infos
|
||||||
|
|
||||||
|
void Chunks::setDataChanged(qint64 pos, bool dataChanged)
|
||||||
|
{
|
||||||
|
if ((pos < 0) || (pos >= _size))
|
||||||
|
return;
|
||||||
|
int chunkIdx = getChunkIndex(pos);
|
||||||
|
qint64 posInBa = pos - _chunks[chunkIdx].absPos;
|
||||||
|
_chunks[chunkIdx].dataChanged[(int)posInBa] = char(dataChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Chunks::dataChanged(qint64 pos)
|
||||||
|
{
|
||||||
|
QByteArray highlighted;
|
||||||
|
data(pos, 1, &highlighted);
|
||||||
|
return bool(highlighted.at(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***************************************** Search API
|
||||||
|
|
||||||
|
qint64 Chunks::indexOf(const QByteArray &ba, qint64 from)
|
||||||
|
{
|
||||||
|
qint64 result = -1;
|
||||||
|
QByteArray buffer;
|
||||||
|
|
||||||
|
for (qint64 pos=from; (pos < _size) && (result < 0); pos += BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
buffer = data(pos, BUFFER_SIZE + ba.size() - 1);
|
||||||
|
int findPos = buffer.indexOf(ba);
|
||||||
|
if (findPos >= 0)
|
||||||
|
result = pos + (qint64)findPos;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 Chunks::lastIndexOf(const QByteArray &ba, qint64 from)
|
||||||
|
{
|
||||||
|
qint64 result = -1;
|
||||||
|
QByteArray buffer;
|
||||||
|
|
||||||
|
for (qint64 pos=from; (pos > 0) && (result < 0); pos -= BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
qint64 sPos = pos - BUFFER_SIZE - (qint64)ba.size() + 1;
|
||||||
|
if (sPos < 0)
|
||||||
|
sPos = 0;
|
||||||
|
buffer = data(sPos, pos - sPos);
|
||||||
|
int findPos = buffer.lastIndexOf(ba);
|
||||||
|
if (findPos >= 0)
|
||||||
|
result = sPos + (qint64)findPos;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***************************************** Char manipulations
|
||||||
|
|
||||||
|
bool Chunks::insert(qint64 pos, char b)
|
||||||
|
{
|
||||||
|
if ((pos < 0) || (pos > _size))
|
||||||
|
return false;
|
||||||
|
int chunkIdx;
|
||||||
|
if (pos == _size)
|
||||||
|
chunkIdx = getChunkIndex(pos-1);
|
||||||
|
else
|
||||||
|
chunkIdx = getChunkIndex(pos);
|
||||||
|
qint64 posInBa = pos - _chunks[chunkIdx].absPos;
|
||||||
|
_chunks[chunkIdx].data.insert(posInBa, b);
|
||||||
|
_chunks[chunkIdx].dataChanged.insert(posInBa, char(1));
|
||||||
|
for (int idx=chunkIdx+1; idx < _chunks.size(); idx++)
|
||||||
|
_chunks[idx].absPos += 1;
|
||||||
|
_size += 1;
|
||||||
|
_pos = pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Chunks::overwrite(qint64 pos, char b)
|
||||||
|
{
|
||||||
|
if ((pos < 0) || (pos >= _size))
|
||||||
|
return false;
|
||||||
|
int chunkIdx = getChunkIndex(pos);
|
||||||
|
qint64 posInBa = pos - _chunks[chunkIdx].absPos;
|
||||||
|
_chunks[chunkIdx].data[(int)posInBa] = b;
|
||||||
|
_chunks[chunkIdx].dataChanged[(int)posInBa] = char(1);
|
||||||
|
_pos = pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Chunks::removeAt(qint64 pos)
|
||||||
|
{
|
||||||
|
if ((pos < 0) || (pos >= _size))
|
||||||
|
return false;
|
||||||
|
int chunkIdx = getChunkIndex(pos);
|
||||||
|
qint64 posInBa = pos - _chunks[chunkIdx].absPos;
|
||||||
|
_chunks[chunkIdx].data.remove(posInBa, 1);
|
||||||
|
_chunks[chunkIdx].dataChanged.remove(posInBa, 1);
|
||||||
|
for (int idx=chunkIdx+1; idx < _chunks.size(); idx++)
|
||||||
|
_chunks[idx].absPos -= 1;
|
||||||
|
_size -= 1;
|
||||||
|
_pos = pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***************************************** Utility functions
|
||||||
|
|
||||||
|
char Chunks::operator[](qint64 pos)
|
||||||
|
{
|
||||||
|
return data(pos, 1)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 Chunks::pos()
|
||||||
|
{
|
||||||
|
return _pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 Chunks::size()
|
||||||
|
{
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Chunks::getChunkIndex(qint64 absPos)
|
||||||
|
{
|
||||||
|
// This routine checks, if there is already a copied chunk available. If os, it
|
||||||
|
// returns a reference to it. If there is no copied chunk available, original
|
||||||
|
// data will be copied into a new chunk.
|
||||||
|
|
||||||
|
int foundIdx = -1;
|
||||||
|
int insertIdx = 0;
|
||||||
|
qint64 ioDelta = 0;
|
||||||
|
|
||||||
|
|
||||||
|
for (int idx=0; idx < _chunks.size(); idx++)
|
||||||
|
{
|
||||||
|
Chunk chunk = _chunks[idx];
|
||||||
|
if ((absPos >= chunk.absPos) && (absPos < (chunk.absPos + chunk.data.size())))
|
||||||
|
{
|
||||||
|
foundIdx = idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (absPos < chunk.absPos)
|
||||||
|
{
|
||||||
|
insertIdx = idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ioDelta += chunk.data.size() - CHUNK_SIZE;
|
||||||
|
insertIdx = idx + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundIdx == -1)
|
||||||
|
{
|
||||||
|
Chunk newChunk;
|
||||||
|
qint64 readAbsPos = absPos - ioDelta;
|
||||||
|
qint64 readPos = (readAbsPos & READ_CHUNK_MASK);
|
||||||
|
_ioDevice->open(QIODevice::ReadOnly);
|
||||||
|
_ioDevice->seek(readPos);
|
||||||
|
newChunk.data = _ioDevice->read(CHUNK_SIZE);
|
||||||
|
_ioDevice->close();
|
||||||
|
newChunk.absPos = absPos - (readAbsPos - readPos);
|
||||||
|
newChunk.dataChanged = QByteArray(newChunk.data.size(), char(0));
|
||||||
|
_chunks.insert(insertIdx, newChunk);
|
||||||
|
foundIdx = insertIdx;
|
||||||
|
}
|
||||||
|
return foundIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef MODUL_TEST
|
||||||
|
int Chunks::chunkSize()
|
||||||
|
{
|
||||||
|
return _chunks.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
77
extra/qhexedit2/src/chunks.h
Normal file
77
extra/qhexedit2/src/chunks.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#ifndef CHUNKS_H
|
||||||
|
#define CHUNKS_H
|
||||||
|
|
||||||
|
/** \cond docNever */
|
||||||
|
|
||||||
|
/*! The Chunks class is the storage backend for QHexEdit.
|
||||||
|
*
|
||||||
|
* When QHexEdit loads data, Chunks access them using a QIODevice interface. When the app uses
|
||||||
|
* a QByteArray interface, QBuffer is used to provide again a QIODevice like interface. No data
|
||||||
|
* will be changed, therefore Chunks opens the QIODevice in QIODevice::ReadOnly mode. After every
|
||||||
|
* access Chunks closes the QIODevice, that's why external applications can overwrite files while
|
||||||
|
* QHexEdit shows them.
|
||||||
|
*
|
||||||
|
* When the the user starts to edit the data, Chunks creates a local copy of a chunk of data (4
|
||||||
|
* kilobytes) and notes all changes there. Parallel to that chunk, there is a second chunk,
|
||||||
|
* which keep track of which bytes are changed and which not.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QtCore>
|
||||||
|
|
||||||
|
struct Chunk
|
||||||
|
{
|
||||||
|
QByteArray data;
|
||||||
|
QByteArray dataChanged;
|
||||||
|
qint64 absPos;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Chunks: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
// Constructors and file settings
|
||||||
|
Chunks(QObject *parent);
|
||||||
|
Chunks(QIODevice &ioDevice, QObject *parent);
|
||||||
|
bool setIODevice(QIODevice &ioDevice);
|
||||||
|
|
||||||
|
// Getting data out of Chunks
|
||||||
|
QByteArray data(qint64 pos=0, qint64 count=-1, QByteArray *highlighted=0);
|
||||||
|
bool write(QIODevice &iODevice, qint64 pos=0, qint64 count=-1);
|
||||||
|
|
||||||
|
// Set and get highlighting infos
|
||||||
|
void setDataChanged(qint64 pos, bool dataChanged);
|
||||||
|
bool dataChanged(qint64 pos);
|
||||||
|
|
||||||
|
// Search API
|
||||||
|
qint64 indexOf(const QByteArray &ba, qint64 from);
|
||||||
|
qint64 lastIndexOf(const QByteArray &ba, qint64 from);
|
||||||
|
|
||||||
|
// Char manipulations
|
||||||
|
bool insert(qint64 pos, char b);
|
||||||
|
bool overwrite(qint64 pos, char b);
|
||||||
|
bool removeAt(qint64 pos);
|
||||||
|
|
||||||
|
// Utility functions
|
||||||
|
char operator[](qint64 pos);
|
||||||
|
qint64 pos();
|
||||||
|
qint64 size();
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
int getChunkIndex(qint64 absPos);
|
||||||
|
|
||||||
|
QIODevice * _ioDevice;
|
||||||
|
qint64 _pos;
|
||||||
|
qint64 _size;
|
||||||
|
QList<Chunk> _chunks;
|
||||||
|
|
||||||
|
#ifdef MODUL_TEST
|
||||||
|
public:
|
||||||
|
int chunkSize();
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/** \endcond docNever */
|
||||||
|
|
||||||
|
#endif // CHUNKS_H
|
@ -1,9 +1,34 @@
|
|||||||
#include "commands.h"
|
#include "commands.h"
|
||||||
|
#include <QUndoCommand>
|
||||||
|
|
||||||
CharCommand::CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar, QUndoCommand *parent)
|
|
||||||
|
// Helper class to store single byte commands
|
||||||
|
class CharCommand : public QUndoCommand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum CCmd {insert, removeAt, overwrite};
|
||||||
|
|
||||||
|
CharCommand(Chunks * chunks, CCmd cmd, qint64 charPos, char newChar,
|
||||||
|
QUndoCommand *parent=0);
|
||||||
|
|
||||||
|
void undo();
|
||||||
|
void redo();
|
||||||
|
bool mergeWith(const QUndoCommand *command);
|
||||||
|
int id() const { return 1234; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Chunks * _chunks;
|
||||||
|
qint64 _charPos;
|
||||||
|
bool _wasChanged;
|
||||||
|
char _newChar;
|
||||||
|
char _oldChar;
|
||||||
|
CCmd _cmd;
|
||||||
|
};
|
||||||
|
|
||||||
|
CharCommand::CharCommand(Chunks * chunks, CCmd cmd, qint64 charPos, char newChar, QUndoCommand *parent)
|
||||||
: QUndoCommand(parent)
|
: QUndoCommand(parent)
|
||||||
{
|
{
|
||||||
_xData = xData;
|
_chunks = chunks;
|
||||||
_charPos = charPos;
|
_charPos = charPos;
|
||||||
_newChar = newChar;
|
_newChar = newChar;
|
||||||
_cmd = cmd;
|
_cmd = cmd;
|
||||||
@ -14,9 +39,9 @@ bool CharCommand::mergeWith(const QUndoCommand *command)
|
|||||||
const CharCommand *nextCommand = static_cast<const CharCommand *>(command);
|
const CharCommand *nextCommand = static_cast<const CharCommand *>(command);
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
if (_cmd != remove)
|
if (_cmd != CharCommand::removeAt)
|
||||||
{
|
{
|
||||||
if (nextCommand->_cmd == replace)
|
if (nextCommand->_cmd == overwrite)
|
||||||
if (nextCommand->_charPos == _charPos)
|
if (nextCommand->_charPos == _charPos)
|
||||||
{
|
{
|
||||||
_newChar = nextCommand->_newChar;
|
_newChar = nextCommand->_newChar;
|
||||||
@ -31,15 +56,15 @@ void CharCommand::undo()
|
|||||||
switch (_cmd)
|
switch (_cmd)
|
||||||
{
|
{
|
||||||
case insert:
|
case insert:
|
||||||
_xData->remove(_charPos, 1);
|
_chunks->removeAt(_charPos);
|
||||||
break;
|
break;
|
||||||
case replace:
|
case overwrite:
|
||||||
_xData->replace(_charPos, _oldChar);
|
_chunks->overwrite(_charPos, _oldChar);
|
||||||
_xData->setDataChanged(_charPos, _wasChanged);
|
_chunks->setDataChanged(_charPos, _wasChanged);
|
||||||
break;
|
break;
|
||||||
case remove:
|
case removeAt:
|
||||||
_xData->insert(_charPos, _oldChar);
|
_chunks->insert(_charPos, _oldChar);
|
||||||
_xData->setDataChanged(_charPos, _wasChanged);
|
_chunks->setDataChanged(_charPos, _wasChanged);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,67 +74,92 @@ void CharCommand::redo()
|
|||||||
switch (_cmd)
|
switch (_cmd)
|
||||||
{
|
{
|
||||||
case insert:
|
case insert:
|
||||||
_xData->insert(_charPos, _newChar);
|
_chunks->insert(_charPos, _newChar);
|
||||||
break;
|
break;
|
||||||
case replace:
|
case overwrite:
|
||||||
_oldChar = _xData->data()[_charPos];
|
_oldChar = (*_chunks)[_charPos];
|
||||||
_wasChanged = _xData->dataChanged(_charPos);
|
_wasChanged = _chunks->dataChanged(_charPos);
|
||||||
_xData->replace(_charPos, _newChar);
|
_chunks->overwrite(_charPos, _newChar);
|
||||||
break;
|
break;
|
||||||
case remove:
|
case removeAt:
|
||||||
_oldChar = _xData->data()[_charPos];
|
_oldChar = (*_chunks)[_charPos];
|
||||||
_wasChanged = _xData->dataChanged(_charPos);
|
_wasChanged = _chunks->dataChanged(_charPos);
|
||||||
_xData->remove(_charPos, 1);
|
_chunks->removeAt(_charPos);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UndoStack::UndoStack(Chunks * chunks, QObject * parent)
|
||||||
|
: QUndoStack(parent)
|
||||||
ArrayCommand::ArrayCommand(XByteArray * xData, Cmd cmd, int baPos, QByteArray newBa, int len, QUndoCommand *parent)
|
|
||||||
: QUndoCommand(parent)
|
|
||||||
{
|
{
|
||||||
_cmd = cmd;
|
_chunks = chunks;
|
||||||
_xData = xData;
|
_parent = parent;
|
||||||
_baPos = baPos;
|
|
||||||
_newBa = newBa;
|
|
||||||
_len = len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArrayCommand::undo()
|
void UndoStack::insert(qint64 pos, char c)
|
||||||
{
|
{
|
||||||
switch (_cmd)
|
if ((pos >= 0) && (pos <= _chunks->size()))
|
||||||
{
|
{
|
||||||
case insert:
|
QUndoCommand *cc = new CharCommand(_chunks, CharCommand::insert, pos, c);
|
||||||
_xData->remove(_baPos, _newBa.length());
|
this->push(cc);
|
||||||
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()
|
void UndoStack::insert(qint64 pos, const QByteArray &ba)
|
||||||
{
|
{
|
||||||
switch (_cmd)
|
if ((pos >= 0) && (pos <= _chunks->size()))
|
||||||
{
|
{
|
||||||
case insert:
|
QString txt = QString(tr("Inserting %1 bytes")).arg(ba.size());
|
||||||
_xData->insert(_baPos, _newBa);
|
beginMacro(txt);
|
||||||
break;
|
for (int idx=0; idx < ba.size(); idx++)
|
||||||
case replace:
|
{
|
||||||
_oldBa = _xData->data().mid(_baPos, _len);
|
QUndoCommand *cc = new CharCommand(_chunks, CharCommand::insert, pos + idx, ba.at(idx));
|
||||||
_wasChanged = _xData->dataChanged(_baPos, _len);
|
this->push(cc);
|
||||||
_xData->replace(_baPos, _newBa);
|
}
|
||||||
break;
|
endMacro();
|
||||||
case remove:
|
}
|
||||||
_oldBa = _xData->data().mid(_baPos, _len);
|
}
|
||||||
_wasChanged = _xData->dataChanged(_baPos, _len);
|
|
||||||
_xData->remove(_baPos, _len);
|
void UndoStack::removeAt(qint64 pos, qint64 len)
|
||||||
break;
|
{
|
||||||
|
if ((pos >= 0) && (pos < _chunks->size()))
|
||||||
|
{
|
||||||
|
if (len==1)
|
||||||
|
{
|
||||||
|
QUndoCommand *cc = new CharCommand(_chunks, CharCommand::removeAt, pos, char(0));
|
||||||
|
this->push(cc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QString txt = QString(tr("Delete %1 chars")).arg(len);
|
||||||
|
beginMacro(txt);
|
||||||
|
for (qint64 cnt=0; cnt<len; cnt++)
|
||||||
|
{
|
||||||
|
QUndoCommand *cc = new CharCommand(_chunks, CharCommand::removeAt, pos, char(0));
|
||||||
|
push(cc);
|
||||||
|
}
|
||||||
|
endMacro();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UndoStack::overwrite(qint64 pos, char c)
|
||||||
|
{
|
||||||
|
if ((pos >= 0) && (pos < _chunks->size()))
|
||||||
|
{
|
||||||
|
QUndoCommand *cc = new CharCommand(_chunks, CharCommand::overwrite, pos, c);
|
||||||
|
this->push(cc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UndoStack::overwrite(qint64 pos, int len, const QByteArray &ba)
|
||||||
|
{
|
||||||
|
if ((pos >= 0) && (pos < _chunks->size()))
|
||||||
|
{
|
||||||
|
QString txt = QString(tr("Overwrite %1 chars")).arg(len);
|
||||||
|
beginMacro(txt);
|
||||||
|
removeAt(pos, len);
|
||||||
|
insert(pos, ba);
|
||||||
|
endMacro();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,66 +3,43 @@
|
|||||||
|
|
||||||
/** \cond docNever */
|
/** \cond docNever */
|
||||||
|
|
||||||
#include <QUndoCommand>
|
#include <QUndoStack>
|
||||||
|
|
||||||
#include "xbytearray.h"
|
#include "chunks.h"
|
||||||
|
|
||||||
/*! CharCommand is a class to prived undo/redo functionality in QHexEdit.
|
/*! CharCommand is a class to provid undo/redo functionality in QHexEdit.
|
||||||
A QUndoCommand represents a single editing action on a document. CharCommand
|
A QUndoCommand represents a single editing action on a document. CharCommand
|
||||||
is responsable for manipulations on single chars. It can insert. replace and
|
is responsable for manipulations on single chars. It can insert. overwrite and
|
||||||
remove characters. A manipulation stores allways to actions
|
remove characters. A manipulation stores allways two actions
|
||||||
1. redo (or do) action
|
1. redo (or do) action
|
||||||
2. undo action.
|
2. undo action.
|
||||||
|
|
||||||
CharCommand also supports command compression via mergeWidht(). This allows
|
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.
|
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
|
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
|
steps: insert a "00", overwrite it with "03" and the overwrite it with "34". These
|
||||||
3 steps are combined into a single step, insert a "34".
|
3 steps are combined into a single step, insert a "34".
|
||||||
|
|
||||||
|
The byte array oriented commands are just put into a set of single byte commands,
|
||||||
|
which are pooled together with the macroBegin() and macroEnd() functionality of
|
||||||
|
Qt's QUndoStack.
|
||||||
*/
|
*/
|
||||||
class CharCommand : public QUndoCommand
|
|
||||||
|
class UndoStack : public QUndoStack
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum { Id = 1234 };
|
UndoStack(Chunks *chunks, QObject * parent=0);
|
||||||
enum Cmd {insert, remove, replace};
|
void insert(qint64 pos, char c);
|
||||||
|
void insert(qint64 pos, const QByteArray &ba);
|
||||||
CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar,
|
void removeAt(qint64 pos, qint64 len=1);
|
||||||
QUndoCommand *parent=0);
|
void overwrite(qint64 pos, char c);
|
||||||
|
void overwrite(qint64 pos, int len, const QByteArray &ba);
|
||||||
void undo();
|
|
||||||
void redo();
|
|
||||||
bool mergeWith(const QUndoCommand *command);
|
|
||||||
int id() const { return Id; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
XByteArray * _xData;
|
Chunks * _chunks;
|
||||||
int _charPos;
|
QObject * _parent;
|
||||||
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 */
|
/** \endcond docNever */
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,21 +1,33 @@
|
|||||||
#ifndef QHEXEDIT_H
|
#ifndef QHEXEDIT_H
|
||||||
#define QHEXEDIT_H
|
#define QHEXEDIT_H
|
||||||
|
|
||||||
#include <QtGui>
|
#include <QAbstractScrollArea>
|
||||||
#include "qhexedit_p.h"
|
#include <QPen>
|
||||||
|
#include <QBrush>
|
||||||
|
|
||||||
|
#include "chunks.h"
|
||||||
|
#include "commands.h"
|
||||||
|
|
||||||
|
#ifdef QHEXEDIT_EXPORTS
|
||||||
|
#define QHEXEDIT_API Q_DECL_EXPORT
|
||||||
|
#elif QHEXEDIT_IMPORTS
|
||||||
|
#define QHEXEDIT_API Q_DECL_IMPORT
|
||||||
|
#else
|
||||||
|
#define QHEXEDIT_API
|
||||||
|
#endif
|
||||||
|
|
||||||
/** \mainpage
|
/** \mainpage
|
||||||
QHexEdit is a binary editor widget for Qt.
|
QHexEdit is a binary editor widget for Qt.
|
||||||
|
|
||||||
\version Version 0.6.1
|
\version Version 0.8.3
|
||||||
\image html hexedit.png
|
\image html qhexedit.png
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*! QHexEdit is a hex editor widget written in C++ for the Qt (Qt4) framework.
|
/** QHexEdit is a hex editor widget written in C++ for the Qt (Qt4, Qt5) framework.
|
||||||
It is a simple editor for binary data, just like QPlainTextEdit is for text
|
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
|
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.
|
bindings for PyQt and you can use this widget also in python 2 and 3.
|
||||||
|
|
||||||
QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use
|
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
|
the mouse or the keyboard to navigate inside the widget. If you hit the keys
|
||||||
@ -36,44 +48,78 @@ characters will be ignored.
|
|||||||
QHexEdit comes with undo/redo functionality. All changes can be undone, by
|
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.
|
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
|
The undo/redo framework is cleared, when setData() sets up a new
|
||||||
content for the editor.
|
content for the editor. You can search data inside the content with indexOf()
|
||||||
|
and lastIndexOf(). The replace() function is to change located subdata. This
|
||||||
|
'replaced' data can also be undone by the undo/redo framework.
|
||||||
|
|
||||||
This widget can only handle small amounts of data. The size has to be below 10
|
QHexEdit is based on QIODevice, that's why QHexEdit can handle big amounts of
|
||||||
megabytes, otherwise the scroll sliders ard not shown and you can't scroll any
|
data. The size of edited data can be more then two gigabytes without any
|
||||||
more.
|
restrictions.
|
||||||
*/
|
*/
|
||||||
class QHexEdit : public QScrollArea
|
class QHEXEDIT_API QHexEdit : public QAbstractScrollArea
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
/*! Property data holds the content of QHexEdit. Call setData() to set the
|
|
||||||
content of QHexEdit, data() returns the actual content.
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(QByteArray data READ data WRITE setData)
|
|
||||||
|
|
||||||
/*! Property addressOffset is added to the Numbers of the Address Area.
|
/*! Property address area switch the address area on or off. Set addressArea true
|
||||||
A offset in the address area (left side) is sometimes usefull, whe you show
|
(show it), false (hide it).
|
||||||
only a segment of a complete memory picture. With setAddressOffset() you set
|
|
||||||
this property - with addressOffset() you get the actual value.
|
|
||||||
*/
|
*/
|
||||||
Q_PROPERTY(int addressOffset READ addressOffset WRITE setAddressOffset)
|
Q_PROPERTY(bool addressArea READ addressArea WRITE setAddressArea)
|
||||||
|
|
||||||
/*! Property address area color sets (setAddressAreaColor()) the backgorund
|
/*! Property address area color sets (setAddressAreaColor()) the backgorund
|
||||||
color of address areas. You can also read the color (addressaAreaColor()).
|
color of address areas. You can also read the color (addressaAreaColor()).
|
||||||
*/
|
*/
|
||||||
Q_PROPERTY(QColor addressAreaColor READ addressAreaColor WRITE setAddressAreaColor)
|
Q_PROPERTY(QColor addressAreaColor READ addressAreaColor WRITE setAddressAreaColor)
|
||||||
|
|
||||||
|
/*! Property addressOffset is added to the Numbers of the Address Area.
|
||||||
|
A offset in the address area (left side) is sometimes usefull, whe you show
|
||||||
|
only a segment of a complete memory picture. With setAddressOffset() you set
|
||||||
|
this property - with addressOffset() you get the current value.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(qint64 addressOffset READ addressOffset WRITE setAddressOffset)
|
||||||
|
|
||||||
|
/*! Set and get the minimum width of the address area, width in characters.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(int addressWidth READ addressWidth WRITE setAddressWidth)
|
||||||
|
|
||||||
|
/*! Switch the ascii area on (true, show it) or off (false, hide it).
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool asciiArea READ asciiArea WRITE setAsciiArea)
|
||||||
|
|
||||||
|
/*! Set and get bytes number per line.*/
|
||||||
|
Q_PROPERTY(int bytesPerLine READ bytesPerLine WRITE setBytesPerLine)
|
||||||
|
|
||||||
|
/*! Porperty cursorPosition sets or gets the position of the editor cursor
|
||||||
|
in QHexEdit. Every byte in data has to cursor positions: the lower and upper
|
||||||
|
Nibble. Maximum cursor position is factor two of data.size().
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(qint64 cursorPosition READ cursorPosition WRITE setCursorPosition)
|
||||||
|
|
||||||
|
/*! Property data holds the content of QHexEdit. Call setData() to set the
|
||||||
|
content of QHexEdit, data() returns the actual content. When calling setData()
|
||||||
|
with a QByteArray as argument, QHexEdit creates a internal copy of the data
|
||||||
|
If you want to edit big files please use setData(), based on QIODevice.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(QByteArray data READ data WRITE setData NOTIFY dataChanged)
|
||||||
|
|
||||||
|
/*! That property defines if the hex values looks as a-f if the value is false(default)
|
||||||
|
or A-F if value is true.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool hexCaps READ hexCaps WRITE setHexCaps)
|
||||||
|
|
||||||
|
/*! Property defines the dynamic calculation of bytesPerLine parameter depends of width of widget.
|
||||||
|
set this property true to avoid horizontal scrollbars and show the maximal possible data. defalut value is false*/
|
||||||
|
Q_PROPERTY(bool dynamicBytesPerLine READ dynamicBytesPerLine WRITE setDynamicBytesPerLine)
|
||||||
|
|
||||||
|
/*! Switch the highlighting feature on or of: true (show it), false (hide it).
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool highlighting READ highlighting WRITE setHighlighting)
|
||||||
|
|
||||||
/*! Property highlighting color sets (setHighlightingColor()) the backgorund
|
/*! Property highlighting color sets (setHighlightingColor()) the backgorund
|
||||||
color of highlighted text areas. You can also read the color
|
color of highlighted text areas. You can also read the color
|
||||||
(highlightingColor()).
|
(highlightingColor()).
|
||||||
*/
|
*/
|
||||||
Q_PROPERTY(QColor highlightingColor READ highlightingColor WRITE setHighlightingColor)
|
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
|
/*! Porperty overwrite mode sets (setOverwriteMode()) or gets (overwriteMode()) the mode
|
||||||
in which the editor works. In overwrite mode the user will overwrite existing data. The
|
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
|
size of data will be constant. In insert mode the size will grow, when inserting
|
||||||
@ -81,6 +127,12 @@ more.
|
|||||||
*/
|
*/
|
||||||
Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode)
|
Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode)
|
||||||
|
|
||||||
|
/*! 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 readOnly sets (setReadOnly()) or gets (isReadOnly) the mode
|
/*! Porperty readOnly sets (setReadOnly()) or gets (isReadOnly) the mode
|
||||||
in which the editor works. In readonly mode the the user can only navigate
|
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
|
through the data and select data; modifying is not possible. This
|
||||||
@ -91,62 +143,115 @@ more.
|
|||||||
/*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/
|
/*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/
|
||||||
Q_PROPERTY(QFont font READ font WRITE setFont)
|
Q_PROPERTY(QFont font READ font WRITE setFont)
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/*! Creates an instance of QHexEdit.
|
/*! Creates an instance of QHexEdit.
|
||||||
\param parent Parent widget of QHexEdit.
|
\param parent Parent widget of QHexEdit.
|
||||||
*/
|
*/
|
||||||
QHexEdit(QWidget *parent = 0);
|
QHexEdit(QWidget *parent=0);
|
||||||
|
|
||||||
/*! Inserts a byte array.
|
// Access to data of qhexedit
|
||||||
\param i Index position, where to insert
|
|
||||||
\param ba byte array, which is to insert
|
/*! Sets the data of QHexEdit. The QIODevice will be opend just before reading
|
||||||
In overwrite mode, the existing data will be overwritten, in insertmode ba will be
|
and closed immediately afterwards. This is to allow other programs to rewrite
|
||||||
insertet and size of data grows.
|
the file while editing it.
|
||||||
*/
|
*/
|
||||||
void insert(int i, const QByteArray & ba);
|
bool setData(QIODevice &iODevice);
|
||||||
|
|
||||||
|
/*! Givs back the data as a QByteArray starting at position \param pos and
|
||||||
|
delivering \param count bytes.
|
||||||
|
*/
|
||||||
|
QByteArray dataAt(qint64 pos, qint64 count=-1);
|
||||||
|
|
||||||
|
/*! Givs back the data into a \param iODevice starting at position \param pos
|
||||||
|
and delivering \param count bytes.
|
||||||
|
*/
|
||||||
|
bool write(QIODevice &iODevice, qint64 pos=0, qint64 count=-1);
|
||||||
|
|
||||||
|
|
||||||
|
// Char handling
|
||||||
|
|
||||||
/*! Inserts a char.
|
/*! Inserts a char.
|
||||||
\param i Index position, where to insert
|
\param pos Index position, where to insert
|
||||||
\param ch Char, which is to insert
|
\param ch Char, which is to insert
|
||||||
In overwrite mode, the existing data will be overwritten, in insertmode ba will be
|
The char will be inserted and size of data grows.
|
||||||
insertet and size of data grows.
|
|
||||||
*/
|
*/
|
||||||
void insert(int i, char ch);
|
void insert(qint64 pos, char ch);
|
||||||
|
|
||||||
/*! Removes len bytes from the content.
|
/*! Removes len bytes from the content.
|
||||||
\param pos Index position, where to remove
|
\param pos Index position, where to remove
|
||||||
\param len Amount of bytes 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);
|
void remove(qint64 pos, qint64 len=1);
|
||||||
|
|
||||||
/*! Gives back a formatted image of the content of QHexEdit
|
/*! Replaces a char.
|
||||||
|
\param pos Index position, where to overwrite
|
||||||
|
\param ch Char, which is to insert
|
||||||
|
The char will be overwritten and size remains constant.
|
||||||
*/
|
*/
|
||||||
QString toReadableString();
|
void replace(qint64 pos, char ch);
|
||||||
|
|
||||||
|
|
||||||
|
// ByteArray handling
|
||||||
|
|
||||||
|
/*! Inserts a byte array.
|
||||||
|
\param pos Index position, where to insert
|
||||||
|
\param ba QByteArray, which is to insert
|
||||||
|
The QByteArray will be inserted and size of data grows.
|
||||||
|
*/
|
||||||
|
void insert(qint64 pos, const QByteArray &ba);
|
||||||
|
|
||||||
|
/*! Replaces \param len bytes with a byte array \param ba
|
||||||
|
\param pos Index position, where to overwrite
|
||||||
|
\param ba QByteArray, which is inserted
|
||||||
|
\param len count of bytes to overwrite
|
||||||
|
The data is overwritten and size of data may change.
|
||||||
|
*/
|
||||||
|
void replace(qint64 pos, qint64 len, const QByteArray &ba);
|
||||||
|
|
||||||
|
|
||||||
|
// Utility functioins
|
||||||
|
/*! Calc cursor position from graphics position
|
||||||
|
* \param point from where the cursor position should be calculated
|
||||||
|
* \return Cursor postioin
|
||||||
|
*/
|
||||||
|
qint64 cursorPosition(QPoint point);
|
||||||
|
|
||||||
|
/*! Ensure the cursor to be visble
|
||||||
|
*/
|
||||||
|
void ensureVisible();
|
||||||
|
|
||||||
|
/*! Find first occurence of ba in QHexEdit data
|
||||||
|
* \param ba Data to find
|
||||||
|
* \param from Point where the search starts
|
||||||
|
* \return pos if fond, else -1
|
||||||
|
*/
|
||||||
|
qint64 indexOf(const QByteArray &ba, qint64 from);
|
||||||
|
|
||||||
|
/*! Returns if any changes where done on document
|
||||||
|
* \return true when document is modified else false
|
||||||
|
*/
|
||||||
|
bool isModified();
|
||||||
|
|
||||||
|
/*! Find last occurence of ba in QHexEdit data
|
||||||
|
* \param ba Data to find
|
||||||
|
* \param from Point where the search starts
|
||||||
|
* \return pos if fond, else -1
|
||||||
|
*/
|
||||||
|
qint64 lastIndexOf(const QByteArray &ba, qint64 from);
|
||||||
|
|
||||||
/*! Gives back a formatted image of the selected content of QHexEdit
|
/*! Gives back a formatted image of the selected content of QHexEdit
|
||||||
*/
|
*/
|
||||||
QString selectionToReadableString();
|
QString selectionToReadableString();
|
||||||
|
|
||||||
/*! \cond docNever */
|
/*! Set Font of QHexEdit
|
||||||
void setAddressOffset(int offset);
|
* \param font
|
||||||
int addressOffset();
|
*/
|
||||||
void setData(QByteArray const &data);
|
void setFont(const QFont &font);
|
||||||
QByteArray data();
|
|
||||||
void setAddressAreaColor(QColor const &color);
|
/*! Gives back a formatted image of the content of QHexEdit
|
||||||
QColor addressAreaColor();
|
*/
|
||||||
void setHighlightingColor(QColor const &color);
|
QString toReadableString();
|
||||||
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:
|
public slots:
|
||||||
/*! Redoes the last operation. If there is no operation to redo, i.e.
|
/*! Redoes the last operation. If there is no operation to redo, i.e.
|
||||||
@ -154,26 +259,6 @@ public slots:
|
|||||||
*/
|
*/
|
||||||
void redo();
|
void redo();
|
||||||
|
|
||||||
/*! Set the minimum width of the address area.
|
|
||||||
\param addressWidth Width in characters.
|
|
||||||
*/
|
|
||||||
void setAddressWidth(int addressWidth);
|
|
||||||
|
|
||||||
/*! Switch the address area on or off.
|
|
||||||
\param addressArea true (show it), false (hide it).
|
|
||||||
*/
|
|
||||||
void setAddressArea(bool addressArea);
|
|
||||||
|
|
||||||
/*! Switch the ascii area on or off.
|
|
||||||
\param asciiArea true (show it), false (hide it).
|
|
||||||
*/
|
|
||||||
void setAsciiArea(bool asciiArea);
|
|
||||||
|
|
||||||
/*! Switch the highlighting feature on or of.
|
|
||||||
\param mode true (show it), false (hide it).
|
|
||||||
*/
|
|
||||||
void setHighlighting(bool mode);
|
|
||||||
|
|
||||||
/*! Undoes the last operation. If there is no operation to undo, i.e.
|
/*! 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.
|
there is no undo step in the undo/redo history, nothing happens.
|
||||||
*/
|
*/
|
||||||
@ -182,24 +267,153 @@ public slots:
|
|||||||
signals:
|
signals:
|
||||||
|
|
||||||
/*! Contains the address, where the cursor is located. */
|
/*! Contains the address, where the cursor is located. */
|
||||||
void currentAddressChanged(int address);
|
void currentAddressChanged(qint64 address);
|
||||||
|
|
||||||
/*! Contains the size of the data to edit. */
|
/*! Contains the size of the data to edit. */
|
||||||
void currentSizeChanged(int size);
|
void currentSizeChanged(qint64 size);
|
||||||
|
|
||||||
/*! The signal is emited every time, the data is changed. */
|
/*! The signal is emitted every time, the data is changed. */
|
||||||
void dataChanged();
|
void dataChanged();
|
||||||
|
|
||||||
/*! The signal is emited every time, the overwrite mode is changed. */
|
/*! The signal is emitted every time, the overwrite mode is changed. */
|
||||||
void overwriteModeChanged(bool state);
|
void overwriteModeChanged(bool state);
|
||||||
|
|
||||||
|
|
||||||
|
/*! \cond docNever */
|
||||||
|
public:
|
||||||
|
~QHexEdit();
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
bool addressArea();
|
||||||
|
void setAddressArea(bool addressArea);
|
||||||
|
|
||||||
|
QColor addressAreaColor();
|
||||||
|
void setAddressAreaColor(const QColor &color);
|
||||||
|
|
||||||
|
qint64 addressOffset();
|
||||||
|
void setAddressOffset(qint64 addressArea);
|
||||||
|
|
||||||
|
int addressWidth();
|
||||||
|
void setAddressWidth(int addressWidth);
|
||||||
|
|
||||||
|
bool asciiArea();
|
||||||
|
void setAsciiArea(bool asciiArea);
|
||||||
|
|
||||||
|
int bytesPerLine();
|
||||||
|
void setBytesPerLine(int count);
|
||||||
|
|
||||||
|
qint64 cursorPosition();
|
||||||
|
void setCursorPosition(qint64 position);
|
||||||
|
|
||||||
|
QByteArray data();
|
||||||
|
void setData(const QByteArray &ba);
|
||||||
|
|
||||||
|
void setHexCaps(const bool isCaps);
|
||||||
|
bool hexCaps();
|
||||||
|
|
||||||
|
void setDynamicBytesPerLine(const bool isDynamic);
|
||||||
|
bool dynamicBytesPerLine();
|
||||||
|
|
||||||
|
bool highlighting();
|
||||||
|
void setHighlighting(bool mode);
|
||||||
|
|
||||||
|
QColor highlightingColor();
|
||||||
|
void setHighlightingColor(const QColor &color);
|
||||||
|
|
||||||
|
bool overwriteMode();
|
||||||
|
void setOverwriteMode(bool overwriteMode);
|
||||||
|
|
||||||
|
bool isReadOnly();
|
||||||
|
void setReadOnly(bool readOnly);
|
||||||
|
|
||||||
|
QColor selectionColor();
|
||||||
|
void setSelectionColor(const QColor &color);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Handle events
|
||||||
|
void keyPressEvent(QKeyEvent *event);
|
||||||
|
void mouseMoveEvent(QMouseEvent * event);
|
||||||
|
void mousePressEvent(QMouseEvent * event);
|
||||||
|
void paintEvent(QPaintEvent *event);
|
||||||
|
void resizeEvent(QResizeEvent *);
|
||||||
|
virtual bool focusNextPrevChild(bool next);
|
||||||
private:
|
private:
|
||||||
/*! \cond docNever */
|
// Handle selections
|
||||||
QHexEditPrivate *qHexEdit_p;
|
void resetSelection(qint64 pos); // set selectionStart and selectionEnd to pos
|
||||||
QHBoxLayout *layout;
|
void resetSelection(); // set selectionEnd to selectionStart
|
||||||
QScrollArea *scrollArea;
|
void setSelection(qint64 pos); // set min (if below init) or max (if greater init)
|
||||||
|
int getSelectionBegin();
|
||||||
|
int getSelectionEnd();
|
||||||
|
|
||||||
|
// Private utility functions
|
||||||
|
void init();
|
||||||
|
void readBuffers();
|
||||||
|
QString toReadable(const QByteArray &ba);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void adjust(); // recalc pixel positions
|
||||||
|
void dataChangedPrivate(int idx=0); // emit dataChanged() signal
|
||||||
|
void refresh(); // ensureVisible() and readBuffers()
|
||||||
|
void updateCursor(); // update blinking cursor
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Name convention: pixel positions start with _px
|
||||||
|
int _pxCharWidth, _pxCharHeight; // char dimensions (dpendend on font)
|
||||||
|
int _pxPosHexX; // X-Pos of HeaxArea
|
||||||
|
int _pxPosAdrX; // X-Pos of Address Area
|
||||||
|
int _pxPosAsciiX; // X-Pos of Ascii Area
|
||||||
|
int _pxGapAdr; // gap left from AddressArea
|
||||||
|
int _pxGapAdrHex; // gap between AddressArea and HexAerea
|
||||||
|
int _pxGapHexAscii; // gap between HexArea and AsciiArea
|
||||||
|
int _pxCursorWidth; // cursor width
|
||||||
|
int _pxSelectionSub; // offset selection rect
|
||||||
|
int _pxCursorX; // current cursor pos
|
||||||
|
int _pxCursorY; // current cursor pos
|
||||||
|
|
||||||
|
// Name convention: absolute byte positions in chunks start with _b
|
||||||
|
qint64 _bSelectionBegin; // first position of Selection
|
||||||
|
qint64 _bSelectionEnd; // end of Selection
|
||||||
|
qint64 _bSelectionInit; // memory position of Selection
|
||||||
|
qint64 _bPosFirst; // position of first byte shown
|
||||||
|
qint64 _bPosLast; // position of last byte shown
|
||||||
|
qint64 _bPosCurrent; // current position
|
||||||
|
|
||||||
|
// variables to store the property values
|
||||||
|
bool _addressArea; // left area of QHexEdit
|
||||||
|
QColor _addressAreaColor;
|
||||||
|
int _addressWidth;
|
||||||
|
bool _asciiArea;
|
||||||
|
qint64 _addressOffset;
|
||||||
|
int _bytesPerLine;
|
||||||
|
int _hexCharsInLine;
|
||||||
|
bool _highlighting;
|
||||||
|
bool _overwriteMode;
|
||||||
|
QBrush _brushSelection;
|
||||||
|
QPen _penSelection;
|
||||||
|
QBrush _brushHighlighted;
|
||||||
|
QPen _penHighlighted;
|
||||||
|
bool _readOnly;
|
||||||
|
bool _hexCaps;
|
||||||
|
bool _dynamicBytesPerLine;
|
||||||
|
|
||||||
|
// other variables
|
||||||
|
bool _editAreaIsAscii; // flag about the ascii mode edited
|
||||||
|
int _addrDigits; // real no of addressdigits, may be > addressWidth
|
||||||
|
bool _blink; // help get cursor blinking
|
||||||
|
QBuffer _bData; // buffer, when setup with QByteArray
|
||||||
|
Chunks *_chunks; // IODevice based access to data
|
||||||
|
QTimer _cursorTimer; // for blinking cursor
|
||||||
|
qint64 _cursorPosition; // absolute positioin of cursor, 1 Byte == 2 tics
|
||||||
|
QRect _cursorRect; // physical dimensions of cursor
|
||||||
|
QByteArray _data; // QHexEdit's data, when setup with QByteArray
|
||||||
|
QByteArray _dataShown; // data in the current View
|
||||||
|
QByteArray _hexDataShown; // data in view, transformed to hex
|
||||||
|
qint64 _lastEventSize; // size, which was emitted last time
|
||||||
|
QByteArray _markedShown; // marked data in view
|
||||||
|
bool _modified; // Is any data in editor modified?
|
||||||
|
int _rowsShown; // lines of text shown
|
||||||
|
UndoStack * _undoStack; // Stack to store edit actions for undo/redo
|
||||||
/*! \endcond docNever */
|
/*! \endcond docNever */
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif // QHEXEDIT_H
|
||||||
|
|
||||||
|
@ -1,800 +0,0 @@
|
|||||||
#include <QtGui>
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
_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 */
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
_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() and (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() and (colIdx < BYTES_PER_LINE)); colIdx++)
|
|
||||||
{
|
|
||||||
painter.drawText(xPosAscii, yPos, _xData.asciiChar(lineIdx + colIdx));
|
|
||||||
xPosAscii += _charWidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// paint cursor
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
// 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) 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() - 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)
|
|
||||||
_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);
|
|
||||||
setMinimumWidth(_xPosAscii + (BYTES_PER_LINE * _charWidth));
|
|
||||||
|
|
||||||
update();
|
|
||||||
}
|
|
@ -1,120 +0,0 @@
|
|||||||
#ifndef QHEXEDIT_P_H
|
|
||||||
#define QHEXEDIT_P_H
|
|
||||||
|
|
||||||
/** \cond docNever */
|
|
||||||
|
|
||||||
|
|
||||||
#include <QtGui>
|
|
||||||
#include "xbytearray.h"
|
|
||||||
|
|
||||||
class QHexEditPrivate : public QWidget
|
|
||||||
{
|
|
||||||
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 setHighlightingColor(QColor const &color);
|
|
||||||
QColor highlightingColor();
|
|
||||||
|
|
||||||
void setOverwriteMode(bool overwriteMode);
|
|
||||||
bool overwriteMode();
|
|
||||||
|
|
||||||
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);
|
|
||||||
void setAsciiArea(bool asciiArea);
|
|
||||||
void setHighlighting(bool mode);
|
|
||||||
virtual void setFont(const QFont &font);
|
|
||||||
|
|
||||||
void undo();
|
|
||||||
void redo();
|
|
||||||
|
|
||||||
QString toRedableString();
|
|
||||||
QString selectionToReadableString();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
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);
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void adjust();
|
|
||||||
|
|
||||||
QColor _addressAreaColor;
|
|
||||||
QColor _highlightingColor;
|
|
||||||
QColor _selectionColor;
|
|
||||||
QScrollArea *_scrollArea;
|
|
||||||
QTimer _cursorTimer;
|
|
||||||
QUndoStack *_undoStack;
|
|
||||||
|
|
||||||
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 _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 */
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,167 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
#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
|
|
Loading…
Reference in New Issue
Block a user