1
Fork 0
mirror of https://github.com/redstrate/Novus.git synced 2025-04-22 20:17:46 +00:00

Add a new hex part based on QHexView

This commit is contained in:
Joshua Goins 2023-10-12 20:18:11 -04:00
parent 4fa1460b66
commit 95f10d9622
20 changed files with 2510 additions and 1 deletions

View file

@ -2,4 +2,5 @@
# SPDX-License-Identifier: CC0-1.0
add_subdirectory(exd)
add_subdirectory(hex)
add_subdirectory(mdl)

25
parts/hex/CMakeLists.txt Normal file
View file

@ -0,0 +1,25 @@
# SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
# SPDX-License-Identifier: CC0-1.0
add_library(hexpart STATIC)
target_sources(hexpart PRIVATE
document/buffer/qhexbuffer.cpp
document/buffer/qhexbuffer.h
document/buffer/qmemorybuffer.cpp
document/buffer/qmemorybuffer.h
document/buffer/qmemoryrefbuffer.cpp
document/buffer/qmemoryrefbuffer.h
document/qhexcursor.cpp
document/qhexcursor.h
document/qhexdocument.cpp
document/qhexdocument.h
document/qhexmetadata.cpp
document/qhexmetadata.h
document/qhexrenderer.cpp
document/qhexrenderer.h
hexpart.cpp
hexpart.h
qhexview.cpp
qhexview.h)
target_link_libraries(hexpart PUBLIC physis Qt6::Core Qt6::Widgets)
target_include_directories(hexpart PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

View file

@ -0,0 +1,47 @@
// SPDX-FileCopyrightText: 2014 Dax89
// SPDX-License-Identifier: MIT
#include "qhexbuffer.h"
#include <QBuffer>
QHexBuffer::QHexBuffer(QObject *parent)
: QObject(parent)
{
}
uchar QHexBuffer::at(qint64 idx)
{
return this->read(idx, 1)[0];
}
bool QHexBuffer::isEmpty() const
{
return this->length() <= 0;
}
void QHexBuffer::replace(qint64 offset, const QByteArray &data)
{
this->remove(offset, data.length());
this->insert(offset, data);
}
void QHexBuffer::read(char *data, int size)
{
QBuffer *buffer = new QBuffer(this);
buffer->setData(data, size);
if (!buffer->isOpen())
buffer->open(QBuffer::ReadWrite);
this->read(buffer);
}
void QHexBuffer::read(const QByteArray &ba)
{
QBuffer *buffer = new QBuffer(this);
buffer->setData(ba);
if (!buffer->isOpen())
buffer->open(QBuffer::ReadWrite);
this->read(buffer);
}

View file

@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: 2014 Dax89
// SPDX-License-Identifier: MIT
#ifndef QHEXBUFFER_H
#define QHEXBUFFER_H
#include <QIODevice>
#include <QObject>
class QHexBuffer : public QObject
{
Q_OBJECT
public:
explicit QHexBuffer(QObject *parent = nullptr);
bool isEmpty() const;
public:
virtual uchar at(qint64 idx);
virtual void replace(qint64 offset, const QByteArray &data);
virtual void read(char *data, int size);
virtual void read(const QByteArray &ba);
public:
virtual qint64 length() const = 0;
virtual void insert(qint64 offset, const QByteArray &data) = 0;
virtual void remove(qint64 offset, int length) = 0;
virtual QByteArray read(qint64 offset, int length) = 0;
virtual bool read(QIODevice *iodevice) = 0;
virtual void write(QIODevice *iodevice) = 0;
virtual qint64 indexOf(const QByteArray &ba, qint64 from) = 0;
virtual qint64 lastIndexOf(const QByteArray &ba, qint64 from) = 0;
};
#endif // QHEXBUFFER_H

View file

@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2014 Dax89
// SPDX-License-Identifier: MIT
#include "qmemorybuffer.h"
QMemoryBuffer::QMemoryBuffer(QObject *parent)
: QHexBuffer(parent)
{
}
uchar QMemoryBuffer::at(qint64 idx)
{
return static_cast<uchar>(m_buffer.at(idx));
}
qint64 QMemoryBuffer::length() const
{
return static_cast<qint64>(m_buffer.length());
}
void QMemoryBuffer::insert(qint64 offset, const QByteArray &data)
{
m_buffer.insert(static_cast<int>(offset), data);
}
void QMemoryBuffer::remove(qint64 offset, int length)
{
m_buffer.remove(static_cast<int>(offset), length);
}
QByteArray QMemoryBuffer::read(qint64 offset, int length)
{
return m_buffer.mid(static_cast<int>(offset), length);
}
bool QMemoryBuffer::read(QIODevice *device)
{
m_buffer = device->readAll();
return true;
}
void QMemoryBuffer::write(QIODevice *device)
{
device->write(m_buffer);
}
qint64 QMemoryBuffer::indexOf(const QByteArray &ba, qint64 from)
{
return m_buffer.indexOf(ba, static_cast<int>(from));
}
qint64 QMemoryBuffer::lastIndexOf(const QByteArray &ba, qint64 from)
{
return m_buffer.lastIndexOf(ba, static_cast<int>(from));
}

View file

@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: 2014 Dax89
// SPDX-License-Identifier: MIT
#ifndef QMEMORYBUFFER_H
#define QMEMORYBUFFER_H
#include "qhexbuffer.h"
class QMemoryBuffer : public QHexBuffer
{
Q_OBJECT
public:
explicit QMemoryBuffer(QObject *parent = nullptr);
uchar at(qint64 idx) override;
qint64 length() const override;
void insert(qint64 offset, const QByteArray &data) override;
void remove(qint64 offset, int length) override;
QByteArray read(qint64 offset, int length) override;
bool read(QIODevice *device) override;
void write(QIODevice *device) override;
qint64 indexOf(const QByteArray &ba, qint64 from) override;
qint64 lastIndexOf(const QByteArray &ba, qint64 from) override;
private:
QByteArray m_buffer;
};
#endif // QMEMORYBUFFER_H

View file

@ -0,0 +1,107 @@
// SPDX-FileCopyrightText: 2014 Dax89
// SPDX-License-Identifier: MIT
#include "qmemoryrefbuffer.h"
#include <QObject>
#include <climits>
QMemoryRefBuffer::QMemoryRefBuffer(QObject *parent)
: QHexBuffer(parent)
{
}
qint64 QMemoryRefBuffer::length() const
{
return m_buffer->size();
}
void QMemoryRefBuffer::insert(qint64 offset, const QByteArray &data)
{
Q_UNUSED(offset)
Q_UNUSED(data)
/* Insertion unsupported */
}
void QMemoryRefBuffer::remove(qint64 offset, int length)
{
Q_UNUSED(offset)
Q_UNUSED(length)
/* Deletion unsupported */
}
QByteArray QMemoryRefBuffer::read(qint64 offset, int length)
{
m_buffer->seek(offset);
return m_buffer->read(length);
}
bool QMemoryRefBuffer::read(QIODevice *device)
{
m_buffer = qobject_cast<QBuffer *>(device);
if (m_buffer) {
m_buffer->setParent(this);
return true;
}
return false;
}
void QMemoryRefBuffer::write(QIODevice *device)
{
m_buffer->seek(0);
if (m_buffer->size() < INT_MAX) {
device->write(m_buffer->readAll());
} else {
while (m_buffer->pos() < m_buffer->size()) {
char tmpBuf[4096];
qint64 chunkLen = m_buffer->read(tmpBuf, 4096);
if (chunkLen == -1)
break;
if (chunkLen > 0) {
device->write(tmpBuf, chunkLen);
m_buffer->seek(m_buffer->pos() + chunkLen);
}
}
}
}
qint64 QMemoryRefBuffer::indexOf(const QByteArray &ba, qint64 from)
{
qint64 findPos = -1;
if (from < m_buffer->size()) {
findPos = from;
m_buffer->seek(from);
while (findPos < m_buffer->size()) {
QByteArray data = m_buffer->read(INT_MAX);
int idx = data.indexOf(ba);
if (idx >= 0) {
findPos += idx;
break;
}
if (findPos + data.size() >= m_buffer->size())
return -1;
m_buffer->seek(m_buffer->pos() + data.size() - ba.size());
}
}
return findPos;
}
qint64 QMemoryRefBuffer::lastIndexOf(const QByteArray &ba, qint64 from)
{
qint64 findPos = -1;
if (from >= 0 && ba.size() < INT_MAX) {
qint64 currPos = from;
while (currPos >= 0) {
qint64 readPos = (currPos < INT_MAX) ? 0 : currPos - INT_MAX;
m_buffer->seek(readPos);
QByteArray data = m_buffer->read(currPos - readPos);
int idx = data.lastIndexOf(ba, from);
if (idx >= 0) {
findPos = readPos + idx;
break;
}
if (readPos <= 0)
break;
currPos = readPos + ba.size();
}
}
return findPos;
}

View file

@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: 2014 Dax89
// SPDX-License-Identifier: MIT
#ifndef QMEMORYREFBUFFER_H
#define QMEMORYREFBUFFER_H
#include "qbuffer.h"
#include "qhexbuffer.h"
class QMemoryRefBuffer : public QHexBuffer
{
Q_OBJECT
public:
explicit QMemoryRefBuffer(QObject *parent = nullptr);
qint64 length() const override;
void insert(qint64 offset, const QByteArray &data) override;
void remove(qint64 offset, int length) override;
QByteArray read(qint64 offset, int length) override;
bool read(QIODevice *device) override;
void write(QIODevice *device) override;
qint64 indexOf(const QByteArray &ba, qint64 from) override;
qint64 lastIndexOf(const QByteArray &ba, qint64 from) override;
private:
QBuffer *m_buffer;
};
#endif // QMEMORYREFBUFFER_H

View file

@ -0,0 +1,177 @@
// SPDX-FileCopyrightText: 2014 Dax89
// SPDX-License-Identifier: MIT
#include "qhexcursor.h"
#include <QWidget>
QHexCursor::QHexCursor(QObject *parent)
: QObject(parent)
, m_insertionmode(QHexCursor::OverwriteMode)
{
m_position.line = m_position.column = 0;
m_position.line = m_position.column = 0;
m_selection.line = m_selection.column = 0;
m_selection.line = m_selection.column = 0;
m_position.nibbleindex = m_selection.nibbleindex = 1;
setLineWidth(DEFAULT_HEX_LINE_LENGTH);
}
const QHexPosition &QHexCursor::selectionStart() const
{
if (m_position.line < m_selection.line)
return m_position;
if (m_position.line == m_selection.line) {
if (m_position.column < m_selection.column)
return m_position;
}
return m_selection;
}
const QHexPosition &QHexCursor::selectionEnd() const
{
if (m_position.line > m_selection.line)
return m_position;
if (m_position.line == m_selection.line) {
if (m_position.column > m_selection.column)
return m_position;
}
return m_selection;
}
const QHexPosition &QHexCursor::position() const
{
return m_position;
}
QHexCursor::InsertionMode QHexCursor::insertionMode() const
{
return m_insertionmode;
}
int QHexCursor::selectionLength() const
{
return this->selectionEnd() - this->selectionStart() + 1;
}
quint64 QHexCursor::currentLine() const
{
return m_position.line;
}
int QHexCursor::currentColumn() const
{
return m_position.column;
}
int QHexCursor::currentNibble() const
{
return m_position.nibbleindex;
}
quint64 QHexCursor::selectionLine() const
{
return m_selection.line;
}
int QHexCursor::selectionColumn() const
{
return m_selection.column;
}
int QHexCursor::selectionNibble() const
{
return m_selection.nibbleindex;
}
bool QHexCursor::isLineSelected(quint64 line) const
{
if (!this->hasSelection())
return false;
quint64 first = std::min(m_position.line, m_selection.line);
quint64 last = std::max(m_position.line, m_selection.line);
if ((line < first) || (line > last))
return false;
return true;
}
bool QHexCursor::hasSelection() const
{
return m_position != m_selection;
}
void QHexCursor::clearSelection()
{
m_selection = m_position;
Q_EMIT positionChanged();
}
void QHexCursor::moveTo(const QHexPosition &pos)
{
this->moveTo(pos.line, pos.column, pos.nibbleindex);
}
void QHexCursor::select(const QHexPosition &pos)
{
this->select(pos.line, pos.column, pos.nibbleindex);
}
void QHexCursor::moveTo(quint64 line, int column, int nibbleindex)
{
m_selection.line = line;
m_selection.column = column;
m_selection.nibbleindex = nibbleindex;
this->select(line, column, nibbleindex);
}
void QHexCursor::select(quint64 line, int column, int nibbleindex)
{
m_position.line = line;
m_position.column = column;
m_position.nibbleindex = nibbleindex;
Q_EMIT positionChanged();
}
void QHexCursor::moveTo(qint64 offset)
{
quint64 line = offset / m_lineWidth;
this->moveTo(line, offset - (line * m_lineWidth));
}
void QHexCursor::select(int length)
{
this->select(m_position.line, std::min(m_lineWidth - 1, m_position.column + length - 1));
}
void QHexCursor::selectOffset(qint64 offset, int length)
{
this->moveTo(offset);
this->select(length);
}
void QHexCursor::setInsertionMode(QHexCursor::InsertionMode mode)
{
bool differentmode = (m_insertionmode != mode);
m_insertionmode = mode;
if (differentmode)
Q_EMIT insertionModeChanged();
}
void QHexCursor::setLineWidth(quint8 width)
{
m_lineWidth = width;
m_position.lineWidth = width;
m_selection.lineWidth = width;
}
void QHexCursor::switchInsertionMode()
{
if (m_insertionmode == QHexCursor::OverwriteMode)
m_insertionmode = QHexCursor::InsertMode;
else
m_insertionmode = QHexCursor::OverwriteMode;
Q_EMIT insertionModeChanged();
}

View file

@ -0,0 +1,86 @@
// SPDX-FileCopyrightText: 2014 Dax89
// SPDX-License-Identifier: MIT
#ifndef QHEXCURSOR_H
#define QHEXCURSOR_H
#include <QObject>
#define DEFAULT_HEX_LINE_LENGTH 0x10
#define DEFAULT_AREA_IDENTATION 0x01
struct QHexPosition {
quint64 line;
int column;
quint8 lineWidth;
int nibbleindex;
QHexPosition() = default;
inline qint64 offset() const
{
return static_cast<qint64>(line * lineWidth) + column;
}
inline int operator-(const QHexPosition &rhs) const
{
return this->offset() - rhs.offset();
}
inline bool operator==(const QHexPosition &rhs) const
{
return (line == rhs.line) && (column == rhs.column) && (nibbleindex == rhs.nibbleindex);
}
inline bool operator!=(const QHexPosition &rhs) const
{
return (line != rhs.line) || (column != rhs.column) || (nibbleindex != rhs.nibbleindex);
}
};
class QHexCursor : public QObject
{
Q_OBJECT
public:
enum InsertionMode { OverwriteMode, InsertMode };
public:
explicit QHexCursor(QObject *parent = nullptr);
public:
const QHexPosition &selectionStart() const;
const QHexPosition &selectionEnd() const;
const QHexPosition &position() const;
InsertionMode insertionMode() const;
int selectionLength() const;
quint64 currentLine() const;
int currentColumn() const;
int currentNibble() const;
quint64 selectionLine() const;
int selectionColumn() const;
int selectionNibble() const;
bool atEnd() const;
bool isLineSelected(quint64 line) const;
bool hasSelection() const;
void clearSelection();
public:
void moveTo(const QHexPosition &pos);
void moveTo(quint64 line, int column, int nibbleindex = 1);
void moveTo(qint64 offset);
void select(const QHexPosition &pos);
void select(quint64 line, int column, int nibbleindex = 1);
void select(int length);
void selectOffset(qint64 offset, int length);
void setInsertionMode(InsertionMode mode);
void setLineWidth(quint8 width);
void switchInsertionMode();
Q_SIGNALS:
void positionChanged();
void insertionModeChanged();
private:
InsertionMode m_insertionmode;
quint8 m_lineWidth;
QHexPosition m_position, m_selection;
};
#endif // QHEXCURSOR_H

View file

@ -0,0 +1,242 @@
// SPDX-FileCopyrightText: 2014 Dax89
// SPDX-License-Identifier: MIT
#include "qhexdocument.h"
#include <QApplication>
#include <QBuffer>
#include <QClipboard>
#include <QFile>
QHexDocument::QHexDocument(QHexBuffer *buffer, QObject *parent)
: QObject(parent)
, m_baseaddress(0)
{
m_buffer = buffer;
m_buffer->setParent(this); // Take Ownership
m_areaindent = DEFAULT_AREA_IDENTATION;
m_hexlinewidth = DEFAULT_HEX_LINE_LENGTH;
m_cursor = new QHexCursor(this);
m_cursor->setLineWidth(m_hexlinewidth);
m_metadata = new QHexMetadata(this);
m_metadata->setLineWidth(m_hexlinewidth);
connect(m_metadata, &QHexMetadata::metadataChanged, this, &QHexDocument::lineChanged);
connect(m_metadata, &QHexMetadata::metadataCleared, this, &QHexDocument::documentChanged);
connect(&m_undostack, &QUndoStack::canUndoChanged, this, &QHexDocument::canUndoChanged);
connect(&m_undostack, &QUndoStack::canRedoChanged, this, &QHexDocument::canRedoChanged);
}
bool QHexDocument::isEmpty() const
{
return m_buffer->isEmpty();
}
bool QHexDocument::atEnd() const
{
return m_cursor->position().offset() >= m_buffer->length();
}
bool QHexDocument::canUndo() const
{
return m_undostack.canUndo();
}
bool QHexDocument::canRedo() const
{
return m_undostack.canRedo();
}
qint64 QHexDocument::length() const
{
return m_buffer->length();
}
quint64 QHexDocument::baseAddress() const
{
return m_baseaddress;
}
QHexCursor *QHexDocument::cursor() const
{
return m_cursor;
}
int QHexDocument::areaIndent() const
{
return m_areaindent;
}
void QHexDocument::setAreaIndent(quint8 value)
{
m_areaindent = value;
}
int QHexDocument::hexLineWidth() const
{
return m_hexlinewidth;
}
void QHexDocument::setHexLineWidth(quint8 value)
{
m_hexlinewidth = value;
m_cursor->setLineWidth(value);
m_metadata->setLineWidth(value);
}
QHexMetadata *QHexDocument::metadata() const
{
return m_metadata;
}
QByteArray QHexDocument::read(qint64 offset, int len)
{
return m_buffer->read(offset, len);
}
void QHexDocument::removeSelection()
{
if (!m_cursor->hasSelection())
return;
this->remove(m_cursor->selectionStart().offset(), m_cursor->selectionLength());
m_cursor->clearSelection();
}
QByteArray QHexDocument::selectedBytes() const
{
if (!m_cursor->hasSelection())
return QByteArray();
return m_buffer->read(m_cursor->selectionStart().offset(), m_cursor->selectionLength());
}
char QHexDocument::at(int offset) const
{
return m_buffer->at(offset);
}
void QHexDocument::setBaseAddress(quint64 baseaddress)
{
if (m_baseaddress == baseaddress)
return;
m_baseaddress = baseaddress;
Q_EMIT documentChanged();
}
void QHexDocument::sync()
{
Q_EMIT documentChanged();
}
void QHexDocument::undo()
{
m_undostack.undo();
Q_EMIT documentChanged();
}
void QHexDocument::redo()
{
m_undostack.redo();
Q_EMIT documentChanged();
}
void QHexDocument::cut(bool hex)
{
if (!m_cursor->hasSelection())
return;
this->copy(hex);
this->removeSelection();
}
void QHexDocument::copy(bool hex)
{
if (!m_cursor->hasSelection())
return;
QClipboard *c = qApp->clipboard();
QByteArray bytes = this->selectedBytes();
if (hex)
bytes = bytes.toHex(' ').toUpper();
c->setText(QString::fromLatin1(bytes));
}
void QHexDocument::paste(bool hex)
{
QClipboard *c = qApp->clipboard();
QByteArray data = c->text().toUtf8();
if (data.isEmpty())
return;
this->removeSelection();
if (hex)
data = QByteArray::fromHex(data);
if (m_cursor->insertionMode() == QHexCursor::InsertMode)
this->insert(m_cursor->position().offset(), data);
else
this->replace(m_cursor->position().offset(), data);
}
void QHexDocument::insert(qint64 offset, uchar b)
{
this->insert(offset, QByteArray(1, b));
}
void QHexDocument::replace(qint64 offset, uchar b)
{
this->replace(offset, QByteArray(1, b));
}
void QHexDocument::insert(qint64 offset, const QByteArray &data)
{
Q_EMIT documentChanged();
}
void QHexDocument::replace(qint64 offset, const QByteArray &data)
{
Q_EMIT documentChanged();
}
void QHexDocument::remove(qint64 offset, int len)
{
Q_EMIT documentChanged();
}
QByteArray QHexDocument::read(qint64 offset, int len) const
{
return m_buffer->read(offset, len);
}
bool QHexDocument::saveTo(QIODevice *device)
{
if (!device->isWritable())
return false;
m_buffer->write(device);
return true;
}
qint64 QHexDocument::searchForward(const QByteArray &ba)
{
qint64 startPos = m_cursor->position().offset();
qint64 findPos = m_buffer->indexOf(ba, startPos);
if (findPos > -1) {
m_cursor->clearSelection();
m_cursor->moveTo(findPos);
m_cursor->select(ba.length());
}
return findPos;
}
qint64 QHexDocument::searchBackward(const QByteArray &ba)
{
qint64 startPos = m_cursor->position().offset() - 1;
if (m_cursor->hasSelection()) {
startPos = m_cursor->selectionStart().offset() - 1;
}
qint64 findPos = m_buffer->lastIndexOf(ba, startPos);
if (findPos > -1) {
m_cursor->clearSelection();
m_cursor->moveTo(findPos);
m_cursor->select(ba.length());
}
return findPos;
}

View file

@ -0,0 +1,135 @@
// SPDX-FileCopyrightText: 2014 Dax89
// SPDX-License-Identifier: MIT
#ifndef QHEXDOCUMENT_H
#define QHEXDOCUMENT_H
#include "buffer/qhexbuffer.h"
#include "qhexcursor.h"
#include "qhexmetadata.h"
#include <QFile>
#include <QUndoStack>
class QHexDocument : public QObject
{
Q_OBJECT
private:
explicit QHexDocument(QHexBuffer *buffer, QObject *parent = nullptr);
public:
bool isEmpty() const;
bool atEnd() const;
bool canUndo() const;
bool canRedo() const;
qint64 length() const;
quint64 baseAddress() const;
QHexCursor *cursor() const;
QHexMetadata *metadata() const;
int areaIndent() const;
void setAreaIndent(quint8 value);
int hexLineWidth() const;
void setHexLineWidth(quint8 value);
public:
void removeSelection();
QByteArray read(qint64 offset, int len = 0);
QByteArray selectedBytes() const;
char at(int offset) const;
void setBaseAddress(quint64 baseaddress);
void sync();
public Q_SLOTS:
void undo();
void redo();
void cut(bool hex = false);
void copy(bool hex = false);
void paste(bool hex = false);
void insert(qint64 offset, uchar b);
void replace(qint64 offset, uchar b);
void insert(qint64 offset, const QByteArray &data);
void replace(qint64 offset, const QByteArray &data);
void remove(qint64 offset, int len);
QByteArray read(qint64 offset, int len) const;
bool saveTo(QIODevice *device);
qint64 searchForward(const QByteArray &ba);
qint64 searchBackward(const QByteArray &ba);
public:
template<typename T>
static QHexDocument *fromDevice(QIODevice *iodevice, QObject *parent = nullptr);
template<typename T>
static QHexDocument *fromFile(QString filename, QObject *parent = nullptr);
template<typename T>
static QHexDocument *fromMemory(char *data, int size, QObject *parent = nullptr);
template<typename T>
static QHexDocument *fromMemory(const QByteArray &ba, QObject *parent = nullptr);
Q_SIGNALS:
void canUndoChanged(bool canUndo);
void canRedoChanged(bool canRedo);
void documentChanged();
void lineChanged(quint64 line);
private:
QHexBuffer *m_buffer;
QHexMetadata *m_metadata;
QUndoStack m_undostack;
QHexCursor *m_cursor;
quint64 m_baseaddress;
quint8 m_areaindent;
quint8 m_hexlinewidth;
};
template<typename T>
QHexDocument *QHexDocument::fromDevice(QIODevice *iodevice, QObject *parent)
{
bool needsclose = false;
if (!iodevice->isOpen()) {
needsclose = true;
iodevice->open(QIODevice::ReadWrite);
}
QHexBuffer *hexbuffer = new T();
if (hexbuffer->read(iodevice)) {
if (needsclose)
iodevice->close();
return new QHexDocument(hexbuffer, parent);
} else {
delete hexbuffer;
}
return nullptr;
}
template<typename T>
QHexDocument *QHexDocument::fromFile(QString filename, QObject *parent)
{
QFile f(filename);
f.open(QFile::ReadOnly);
QHexDocument *doc = QHexDocument::fromDevice<T>(&f, parent);
f.close();
return doc;
}
template<typename T>
QHexDocument *QHexDocument::fromMemory(char *data, int size, QObject *parent)
{
QHexBuffer *hexbuffer = new T();
hexbuffer->read(data, size);
return new QHexDocument(hexbuffer, parent);
}
template<typename T>
QHexDocument *QHexDocument::fromMemory(const QByteArray &ba, QObject *parent)
{
QHexBuffer *hexbuffer = new T();
hexbuffer->read(ba);
return new QHexDocument(hexbuffer, parent);
}
#endif // QHEXEDITDATA_H

View file

@ -0,0 +1,147 @@
// SPDX-FileCopyrightText: 2014 Dax89
// SPDX-License-Identifier: MIT
#include "qhexmetadata.h"
QHexMetadata::QHexMetadata(QObject *parent)
: QObject(parent)
{
}
const QHexLineMetadata &QHexMetadata::get(quint64 line) const
{
auto it = m_metadata.find(line);
return it.value();
}
QString QHexMetadata::comments(quint64 line, int column) const
{
if (!this->hasMetadata(line))
return QString();
QString s;
const auto &linemetadata = this->get(line);
for (auto &mi : linemetadata) {
if (!(mi.start <= column && column < mi.start + mi.length))
continue;
if (mi.comment.isEmpty())
continue;
if (!s.isEmpty())
s += QStringLiteral("\n");
s += mi.comment;
}
return s;
}
bool QHexMetadata::hasMetadata(quint64 line) const
{
return m_metadata.contains(line);
}
void QHexMetadata::clear(quint64 line)
{
auto it = m_metadata.find(line);
if (it == m_metadata.end())
return;
m_metadata.erase(it);
Q_EMIT metadataChanged(line);
}
void QHexMetadata::clear()
{
m_absoluteMetadata.clear();
m_metadata.clear();
Q_EMIT metadataCleared();
}
void QHexMetadata::metadata(qint64 begin, qint64 end, const QColor &fgcolor, const QColor &bgcolor, const QString &comment)
{
m_absoluteMetadata.append({begin, end, fgcolor, bgcolor, comment});
setAbsoluteMetadata(m_absoluteMetadata.back());
}
void QHexMetadata::setAbsoluteMetadata(const QHexMetadataAbsoluteItem &mai)
{
const quint64 firstRow = mai.begin / m_lineWidth;
const quint64 lastRow = mai.end / m_lineWidth;
for (quint64 row = firstRow; row <= lastRow; ++row) {
int start, length;
if (row == firstRow) {
start = mai.begin % m_lineWidth;
} else {
start = 0;
}
if (row == lastRow) {
const int lastChar = mai.end % m_lineWidth;
length = lastChar - start;
} else {
length = m_lineWidth;
}
if (length > 0) {
setMetadata({row, start, length, mai.foreground, mai.background, mai.comment});
}
}
}
void QHexMetadata::setLineWidth(quint8 width)
{
if (width != m_lineWidth) {
m_lineWidth = width;
// clean m_metadata
m_metadata.clear();
// and regenerate with new line width size
for (int i = 0; i < m_absoluteMetadata.size(); ++i) {
setAbsoluteMetadata(m_absoluteMetadata[i]);
}
}
}
void QHexMetadata::metadata(quint64 line, int start, int length, const QColor &fgcolor, const QColor &bgcolor, const QString &comment)
{
const qint64 begin = line * m_lineWidth + start;
const qint64 end = begin + length;
// delegate to the new interface
this->metadata(begin, end, fgcolor, bgcolor, comment);
}
void QHexMetadata::color(quint64 line, int start, int length, const QColor &fgcolor, const QColor &bgcolor)
{
this->metadata(line, start, length, fgcolor, bgcolor, QString());
}
void QHexMetadata::foreground(quint64 line, int start, int length, const QColor &fgcolor)
{
this->color(line, start, length, fgcolor, QColor());
}
void QHexMetadata::background(quint64 line, int start, int length, const QColor &bgcolor)
{
this->color(line, start, length, QColor(), bgcolor);
}
void QHexMetadata::comment(quint64 line, int start, int length, const QString &comment)
{
this->metadata(line, start, length, QColor(), QColor(), comment);
}
void QHexMetadata::setMetadata(const QHexMetadataItem &mi)
{
if (!m_metadata.contains(mi.line)) {
QHexLineMetadata linemetadata;
linemetadata.push_back(mi);
m_metadata[mi.line] = linemetadata;
} else {
QHexLineMetadata &linemetadata = m_metadata[mi.line];
linemetadata.push_back(mi);
}
Q_EMIT metadataChanged(mi.line);
}

View file

@ -0,0 +1,70 @@
// SPDX-FileCopyrightText: 2014 Dax89
// SPDX-License-Identifier: MIT
#ifndef QHEXMETADATA_H
#define QHEXMETADATA_H
#include <QColor>
#include <QHash>
#include <QObject>
#include <QVector>
#include <QtGlobal>
#include <list>
struct QHexMetadataAbsoluteItem {
qint64 begin;
qint64 end;
QColor foreground, background;
QString comment;
};
struct QHexMetadataItem {
quint64 line;
int start, length;
QColor foreground, background;
QString comment;
};
typedef std::list<QHexMetadataItem> QHexLineMetadata;
class QHexMetadata : public QObject
{
Q_OBJECT
public:
explicit QHexMetadata(QObject *parent = nullptr);
const QHexLineMetadata &get(quint64 line) const;
QString comments(quint64 line, int column) const;
bool hasMetadata(quint64 line) const;
void clear(quint64 line); // this is transient till next call to setLineWidth()
void clear();
void setLineWidth(quint8 width);
public:
// new interface with begin, end
void metadata(qint64 begin, qint64 end, const QColor &fgcolor, const QColor &bgcolor, const QString &comment);
// old interface with line, start, length
void metadata(quint64 line, int start, int length, const QColor &fgcolor, const QColor &bgcolor, const QString &comment);
void color(quint64 line, int start, int length, const QColor &fgcolor, const QColor &bgcolor);
void foreground(quint64 line, int start, int length, const QColor &fgcolor);
void background(quint64 line, int start, int length, const QColor &bgcolor);
void comment(quint64 line, int start, int length, const QString &comment);
private:
void setMetadata(const QHexMetadataItem &mi);
void setAbsoluteMetadata(const QHexMetadataAbsoluteItem &mi);
Q_SIGNALS:
void metadataChanged(quint64 line);
void metadataCleared();
private:
quint8 m_lineWidth;
QHash<quint64, QHexLineMetadata> m_metadata;
QVector<QHexMetadataAbsoluteItem> m_absoluteMetadata;
};
#endif // QHEXMETADATA_H

View file

@ -0,0 +1,500 @@
// SPDX-FileCopyrightText: 2014 Dax89
// SPDX-License-Identifier: MIT
#include "qhexrenderer.h"
#include <QApplication>
#include <QTextCursor>
#include <QWidget>
#include <cctype>
#include <cmath>
#define HEX_UNPRINTABLE_CHAR '.'
QHexRenderer::QHexRenderer(QHexDocument *document, const QFontMetricsF &fontmetrics, QObject *parent)
: QObject(parent)
, m_document(document)
, m_fontmetrics(fontmetrics)
{
m_selectedarea = QHexRenderer::HexArea;
m_cursorenabled = false;
}
void QHexRenderer::renderFrame(QPainter *painter)
{
QRect rect = painter->window();
int hexx = this->getHexColumnX();
int asciix = this->getAsciiColumnX();
int endx = this->getEndColumnX();
// x coordinates are in absolute space
// y coordinates are in viewport space
// see QHexView::paintEvent where the painter has been shifted horizontally
painter->drawLine(0, this->headerLineCount() * this->lineHeight() - 1, endx, this->headerLineCount() * this->lineHeight() - 1);
painter->drawLine(hexx, rect.top(), hexx, rect.bottom());
painter->drawLine(asciix, rect.top(), asciix, rect.bottom());
painter->drawLine(endx, rect.top(), endx, rect.bottom());
}
void QHexRenderer::render(QPainter *painter, quint64 begin, quint64 end, quint64 firstline)
{
QPalette palette = qApp->palette();
this->drawHeader(painter, palette);
quint64 documentLines = this->documentLines();
for (quint64 line = begin; line < std::min(end, documentLines); line++) {
QRect linerect = this->getLineRect(line, firstline);
if (line % 2)
painter->fillRect(linerect, palette.brush(QPalette::Window));
else
painter->fillRect(linerect, palette.brush(QPalette::Base));
this->drawAddress(painter, palette, linerect, line);
this->drawHex(painter, palette, linerect, line);
this->drawAscii(painter, palette, linerect, line);
}
}
void QHexRenderer::updateMetrics(const QFontMetricsF &fm)
{
m_fontmetrics = fm;
}
void QHexRenderer::enableCursor(bool b)
{
m_cursorenabled = b;
}
void QHexRenderer::selectArea(const QPoint &pt)
{
int area = this->hitTestArea(pt);
if (!this->editableArea(area))
return;
m_selectedarea = area;
}
bool QHexRenderer::hitTest(const QPoint &pt, QHexPosition *position, quint64 firstline) const
{
int area = this->hitTestArea(pt);
if (!this->editableArea(area))
return false;
position->line = std::min(firstline + (pt.y() / this->lineHeight()) - headerLineCount(), this->documentLastLine());
position->lineWidth = this->hexLineWidth();
if (area == QHexRenderer::HexArea) {
int relx = pt.x() - this->getHexColumnX() - this->borderSize();
int column = relx / this->getCellWidth();
position->column = column / 3;
// first char is nibble 1, 2nd and space are 0
position->nibbleindex = (column % 3 == 0) ? 1 : 0;
} else {
int relx = pt.x() - this->getAsciiColumnX() - this->borderSize();
position->column = relx / this->getCellWidth();
position->nibbleindex = 1;
}
if (position->line == this->documentLastLine()) // Check last line's columns
{
QByteArray ba = this->getLine(position->line);
position->column = std::min(position->column, static_cast<int>(ba.length()));
} else
position->column = std::min(position->column, hexLineWidth() - 1);
return true;
}
int QHexRenderer::hitTestArea(const QPoint &pt) const
{
if (pt.y() < headerLineCount() * lineHeight())
return QHexRenderer::HeaderArea;
if ((pt.x() >= this->borderSize()) && (pt.x() <= (this->getHexColumnX() - this->borderSize())))
return QHexRenderer::AddressArea;
if ((pt.x() > (this->getHexColumnX() + this->borderSize())) && (pt.x() < (this->getAsciiColumnX() - this->borderSize())))
return QHexRenderer::HexArea;
if ((pt.x() > (this->getAsciiColumnX() + this->borderSize())) && (pt.x() < (this->getEndColumnX() - this->borderSize())))
return QHexRenderer::AsciiArea;
return QHexRenderer::ExtraArea;
}
int QHexRenderer::selectedArea() const
{
return m_selectedarea;
}
bool QHexRenderer::editableArea(int area) const
{
return (area == QHexRenderer::HexArea || area == QHexRenderer::AsciiArea);
}
quint64 QHexRenderer::documentLastLine() const
{
return this->documentLines() - 1;
}
int QHexRenderer::documentLastColumn() const
{
return this->getLine(this->documentLastLine()).length();
}
quint64 QHexRenderer::documentLines() const
{
return std::ceil(this->rendererLength() / static_cast<float>(hexLineWidth()));
}
int QHexRenderer::documentWidth() const
{
return this->getEndColumnX();
}
int QHexRenderer::lineHeight() const
{
return qRound(m_fontmetrics.height());
}
QRect QHexRenderer::getLineRect(quint64 line, quint64 firstline) const
{
return QRect(0, static_cast<int>((line - firstline + headerLineCount()) * lineHeight()), this->getEndColumnX(), lineHeight());
}
int QHexRenderer::headerLineCount() const
{
return 1;
}
int QHexRenderer::borderSize() const
{
if (m_document)
return this->getNCellsWidth(m_document->areaIndent());
return this->getNCellsWidth(DEFAULT_AREA_IDENTATION);
}
int QHexRenderer::hexLineWidth() const
{
if (m_document)
return m_document->hexLineWidth();
return DEFAULT_HEX_LINE_LENGTH;
}
QString QHexRenderer::hexString(quint64 line, QByteArray *rawline) const
{
QByteArray lrawline = this->getLine(line);
if (rawline)
*rawline = lrawline;
return QString::fromLatin1(lrawline.toHex(' ').toUpper()) + QStringLiteral(" ");
}
QString QHexRenderer::asciiString(quint64 line, QByteArray *rawline) const
{
QByteArray lrawline = this->getLine(line);
if (rawline)
*rawline = lrawline;
QByteArray ascii = lrawline;
this->unprintableChars(ascii);
return QString::fromLatin1(ascii);
}
QByteArray QHexRenderer::getLine(quint64 line) const
{
return m_document->read(line * hexLineWidth(), hexLineWidth());
}
void QHexRenderer::blinkCursor()
{
m_cursorenabled = !m_cursorenabled;
}
qint64 QHexRenderer::rendererLength() const
{
return m_document->length() + 1;
}
int QHexRenderer::getAddressWidth() const
{
quint64 maxAddr = m_document->baseAddress() + this->rendererLength();
if (maxAddr <= 0xFFFF)
return 4;
if (maxAddr <= 0xFFFFFFFF)
return 8;
return QString::number(maxAddr, 16).length();
}
int QHexRenderer::getHexColumnX() const
{
return this->getNCellsWidth(this->getAddressWidth()) + 2 * this->borderSize();
}
int QHexRenderer::getAsciiColumnX() const
{
return this->getHexColumnX() + this->getNCellsWidth(hexLineWidth() * 3) + 2 * this->borderSize();
}
int QHexRenderer::getEndColumnX() const
{
return this->getAsciiColumnX() + this->getNCellsWidth(hexLineWidth()) + 2 * this->borderSize();
}
qreal QHexRenderer::getCellWidth() const
{
return m_fontmetrics.horizontalAdvance(QStringLiteral(" "));
}
int QHexRenderer::getNCellsWidth(int n) const
{
return qRound(n * getCellWidth());
}
void QHexRenderer::unprintableChars(QByteArray &ascii) const
{
for (char &ch : ascii) {
if (std::isprint(static_cast<unsigned char>(ch)))
continue;
ch = HEX_UNPRINTABLE_CHAR;
}
}
void QHexRenderer::applyDocumentStyles(QPainter *painter, QTextDocument *textdocument) const
{
textdocument->setDocumentMargin(0);
textdocument->setUndoRedoEnabled(false);
textdocument->setDefaultFont(painter->font());
}
void QHexRenderer::applyBasicStyle(QTextCursor &textcursor, const QByteArray &rawline, Factor factor) const
{
QPalette palette = qApp->palette();
QColor color = palette.color(QPalette::WindowText);
if (color.lightness() < 50) {
if (color == Qt::black)
color = Qt::gray;
else
color = color.darker();
} else
color = color.lighter();
QTextCharFormat charformat;
charformat.setForeground(color);
for (int i = 0; i < rawline.length(); i++) {
if ((rawline[i] != 0x00) && (static_cast<uchar>(rawline[i]) != 0xFF))
continue;
textcursor.setPosition(i * factor);
textcursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, factor);
textcursor.setCharFormat(charformat);
}
}
void QHexRenderer::applyMetadata(QTextCursor &textcursor, quint64 line, Factor factor) const
{
QHexMetadata *metadata = m_document->metadata();
if (!metadata->hasMetadata(line))
return;
const QHexLineMetadata &linemetadata = metadata->get(line);
for (const QHexMetadataItem &mi : linemetadata) {
QTextCharFormat charformat;
if (mi.background.isValid())
charformat.setBackground(mi.background);
if (mi.foreground.isValid())
charformat.setForeground(mi.foreground);
if (!mi.comment.isEmpty())
charformat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
textcursor.setPosition(mi.start * factor);
textcursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, (mi.length * factor) - (factor > 1 ? 1 : 0));
textcursor.setCharFormat(charformat);
}
}
void QHexRenderer::applySelection(QTextCursor &textcursor, quint64 line, Factor factor) const
{
QHexCursor *cursor = m_document->cursor();
if (!cursor->isLineSelected(line))
return;
const QHexPosition &startsel = cursor->selectionStart();
const QHexPosition &endsel = cursor->selectionEnd();
if (startsel.line == endsel.line) {
textcursor.setPosition(startsel.column * factor);
textcursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, ((endsel.column - startsel.column + 1) * factor));
} else {
if (line == startsel.line)
textcursor.setPosition(startsel.column * factor);
else
textcursor.setPosition(0);
if (line == endsel.line)
textcursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, ((endsel.column + 1) * factor));
else
textcursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
}
if (factor == Hex)
textcursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1);
QPalette palette = qApp->palette();
QTextCharFormat charformat;
charformat.setBackground(palette.color(QPalette::Highlight));
charformat.setForeground(palette.color(QPalette::HighlightedText));
textcursor.setCharFormat(charformat);
}
void QHexRenderer::applyCursorAscii(QTextCursor &textcursor, quint64 line) const
{
QHexCursor *cursor = m_document->cursor();
if ((line != cursor->currentLine()) || !m_cursorenabled)
return;
textcursor.clearSelection();
textcursor.setPosition(m_document->cursor()->currentColumn());
textcursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
QPalette palette = qApp->palette();
QTextCharFormat charformat;
if ((cursor->insertionMode() == QHexCursor::OverwriteMode) || (m_selectedarea != QHexRenderer::AsciiArea)) {
charformat.setForeground(palette.color(QPalette::Window));
if (m_selectedarea == QHexRenderer::AsciiArea)
charformat.setBackground(palette.color(QPalette::WindowText));
else
charformat.setBackground(palette.color(QPalette::WindowText).lighter(250));
} else
charformat.setUnderlineStyle(QTextCharFormat::UnderlineStyle::SingleUnderline);
textcursor.setCharFormat(charformat);
}
void QHexRenderer::applyCursorHex(QTextCursor &textcursor, quint64 line) const
{
QHexCursor *cursor = m_document->cursor();
if ((line != cursor->currentLine()) || !m_cursorenabled)
return;
textcursor.clearSelection();
textcursor.setPosition(m_document->cursor()->currentColumn() * 3);
if ((m_selectedarea == QHexRenderer::HexArea) && !m_document->cursor()->currentNibble())
textcursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor);
textcursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
if (m_selectedarea == QHexRenderer::AsciiArea)
textcursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
QPalette palette = qApp->palette();
QTextCharFormat charformat;
if ((cursor->insertionMode() == QHexCursor::OverwriteMode) || (m_selectedarea != QHexRenderer::HexArea)) {
charformat.setForeground(palette.color(QPalette::Window));
if (m_selectedarea == QHexRenderer::HexArea)
charformat.setBackground(palette.color(QPalette::WindowText));
else
charformat.setBackground(palette.color(QPalette::WindowText).lighter(250));
} else
charformat.setUnderlineStyle(QTextCharFormat::UnderlineStyle::SingleUnderline);
textcursor.setCharFormat(charformat);
}
void QHexRenderer::drawAddress(QPainter *painter, const QPalette &palette, const QRect &linerect, quint64 line)
{
quint64 addr = line * hexLineWidth() + m_document->baseAddress();
QString addrStr = QString::number(addr, 16).rightJustified(this->getAddressWidth(), QLatin1Char('0')).toUpper();
QRect addressrect = linerect;
addressrect.setWidth(this->getHexColumnX());
painter->save();
painter->setPen(palette.color(QPalette::Highlight));
painter->drawText(addressrect, Qt::AlignHCenter | Qt::AlignVCenter, addrStr);
painter->restore();
}
void QHexRenderer::drawHex(QPainter *painter, const QPalette &palette, const QRect &linerect, quint64 line)
{
Q_UNUSED(palette)
QTextDocument textdocument;
QTextCursor textcursor(&textdocument);
QByteArray rawline;
textcursor.insertText(this->hexString(line, &rawline));
if (line == this->documentLastLine())
textcursor.insertText(QStringLiteral(" "));
QRect hexrect = linerect;
hexrect.setX(this->getHexColumnX() + this->borderSize());
this->applyDocumentStyles(painter, &textdocument);
this->applyBasicStyle(textcursor, rawline, Hex);
this->applyMetadata(textcursor, line, Hex);
this->applySelection(textcursor, line, Hex);
this->applyCursorHex(textcursor, line);
painter->save();
painter->translate(hexrect.topLeft());
textdocument.drawContents(painter);
painter->restore();
}
void QHexRenderer::drawAscii(QPainter *painter, const QPalette &palette, const QRect &linerect, quint64 line)
{
Q_UNUSED(palette)
QTextDocument textdocument;
QTextCursor textcursor(&textdocument);
QByteArray rawline;
textcursor.insertText(this->asciiString(line, &rawline));
if (line == this->documentLastLine())
textcursor.insertText(QStringLiteral(" "));
QRect asciirect = linerect;
asciirect.setX(this->getAsciiColumnX() + this->borderSize());
this->applyDocumentStyles(painter, &textdocument);
this->applyBasicStyle(textcursor, rawline, Ascii);
this->applyMetadata(textcursor, line, Ascii);
this->applySelection(textcursor, line, Ascii);
this->applyCursorAscii(textcursor, line);
painter->save();
painter->translate(asciirect.topLeft());
textdocument.drawContents(painter);
painter->restore();
}
void QHexRenderer::drawHeader(QPainter *painter, const QPalette &palette)
{
QRect rect = QRect(0, 0, this->getEndColumnX(), this->headerLineCount() * this->lineHeight());
QString hexheader;
for (quint8 i = 0; i < this->hexLineWidth(); i++)
hexheader.append(QStringLiteral("%1 ").arg(QString::number(i, 16).rightJustified(2, QLatin1Char('0'))).toUpper());
QRect addressrect = rect;
addressrect.setWidth(this->getHexColumnX());
QRect hexrect = rect;
hexrect.setX(this->getHexColumnX() + this->borderSize());
hexrect.setWidth(this->getNCellsWidth(hexLineWidth() * 3));
QRect asciirect = rect;
asciirect.setX(this->getAsciiColumnX());
asciirect.setWidth(this->getEndColumnX() - this->getAsciiColumnX());
painter->save();
painter->setPen(palette.color(QPalette::Highlight));
painter->drawText(addressrect, Qt::AlignHCenter | Qt::AlignVCenter, QStringLiteral("Offset"));
// align left for maximum consistency with drawHex() which prints from the left.
// so hex and positions are aligned vertically
painter->drawText(hexrect, Qt::AlignLeft | Qt::AlignVCenter, hexheader);
painter->drawText(asciirect, Qt::AlignHCenter | Qt::AlignVCenter, QStringLiteral("Ascii"));
painter->restore();
}

View file

@ -0,0 +1,82 @@
// SPDX-FileCopyrightText: 2014 Dax89
// SPDX-License-Identifier: MIT
#ifndef QHEXRENDERER_H
#define QHEXRENDERER_H
/*
* Nibble encoding:
* AB -> [A][B]
* Nibble Index: 1 0
*/
#include "qhexdocument.h"
#include <QPainter>
#include <QTextDocument>
class QHexRenderer : public QObject
{
Q_OBJECT
public:
enum { HeaderArea, AddressArea, HexArea, AsciiArea, ExtraArea };
public:
explicit QHexRenderer(QHexDocument *document, const QFontMetricsF &fontmetrics, QObject *parent = nullptr);
void renderFrame(QPainter *painter);
void render(QPainter *painter, quint64 start, quint64 end, quint64 firstline); // begin included, end excluded
void updateMetrics(const QFontMetricsF &fm);
void enableCursor(bool b = true);
void selectArea(const QPoint &pt);
public:
void blinkCursor();
bool hitTest(const QPoint &pt, QHexPosition *position, quint64 firstline) const;
int hitTestArea(const QPoint &pt) const;
int selectedArea() const;
bool editableArea(int area) const;
quint64 documentLastLine() const;
int documentLastColumn() const;
quint64 documentLines() const;
int documentWidth() const;
int lineHeight() const;
QRect getLineRect(quint64 line, quint64 firstline) const;
int headerLineCount() const;
int borderSize() const;
int hexLineWidth() const;
private:
QString hexString(quint64 line, QByteArray *rawline = nullptr) const;
QString asciiString(quint64 line, QByteArray *rawline = nullptr) const;
QByteArray getLine(quint64 line) const;
qint64 rendererLength() const;
int getAddressWidth() const;
int getHexColumnX() const;
int getAsciiColumnX() const;
int getEndColumnX() const;
qreal getCellWidth() const;
int getNCellsWidth(int n) const;
void unprintableChars(QByteArray &ascii) const;
private:
enum Factor { Ascii = 1, Hex = 3 };
void applyDocumentStyles(QPainter *painter, QTextDocument *textdocument) const;
void applyBasicStyle(QTextCursor &textcursor, const QByteArray &rawline, Factor factor) const;
void applyMetadata(QTextCursor &textcursor, quint64 line, Factor factor) const;
void applySelection(QTextCursor &textcursor, quint64 line, Factor factor) const;
void applyCursorAscii(QTextCursor &textcursor, quint64 line) const;
void applyCursorHex(QTextCursor &textcursor, quint64 line) const;
void drawAddress(QPainter *painter, const QPalette &palette, const QRect &linerect, quint64 line);
void drawHex(QPainter *painter, const QPalette &palette, const QRect &linerect, quint64 line);
void drawAscii(QPainter *painter, const QPalette &palette, const QRect &linerect, quint64 line);
void drawHeader(QPainter *painter, const QPalette &palette);
private:
QHexDocument *m_document;
QFontMetricsF m_fontmetrics;
int m_selectedarea;
bool m_cursorenabled;
};
#endif // QHEXRENDERER_H

17
parts/hex/hexpart.cpp Normal file
View file

@ -0,0 +1,17 @@
// SPDX-FileCopyrightText: 2014 Dax89
// SPDX-License-Identifier: MIT
#include "hexpart.h"
#include "document/buffer/qmemoryrefbuffer.h"
HexPart::HexPart(QWidget *parent)
: QHexView(parent)
{
}
void HexPart::loadFile(physis_Buffer buffer)
{
setDocument(QHexDocument::fromMemory<QMemoryRefBuffer>(reinterpret_cast<char *>(buffer.data), buffer.size));
setReadOnly(true);
}

15
parts/hex/hexpart.h Normal file
View file

@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "physis.hpp"
#include "qhexview.h"
class HexPart : public QHexView
{
public:
explicit HexPart(QWidget *parent = nullptr);
void loadFile(physis_Buffer buffer);
};

649
parts/hex/qhexview.cpp Normal file
View file

@ -0,0 +1,649 @@
// SPDX-FileCopyrightText: 2014 Dax89
// SPDX-License-Identifier: MIT
#include "qhexview.h"
#include "document/buffer/qmemorybuffer.h"
#include <QApplication>
#include <QFontDatabase>
#include <QHelpEvent>
#include <QPaintEvent>
#include <QPainter>
#include <QScrollBar>
#include <QToolTip>
#include <cmath>
#define CURSOR_BLINK_INTERVAL 500 // ms
#define DOCUMENT_WHEEL_LINES 3
QHexView::QHexView(QWidget *parent)
: QAbstractScrollArea(parent)
, m_document(nullptr)
, m_renderer(nullptr)
, m_readonly(false)
{
QFont f = QFontDatabase::systemFont(QFontDatabase::FixedFont);
if (f.styleHint() != QFont::TypeWriter) {
f.setFamily(QStringLiteral("Monospace")); // Force Monospaced font
f.setStyleHint(QFont::TypeWriter);
}
this->setFont(f);
this->setFocusPolicy(Qt::StrongFocus);
this->setMouseTracking(true);
this->verticalScrollBar()->setSingleStep(1);
this->verticalScrollBar()->setPageStep(1);
m_blinktimer = new QTimer(this);
m_blinktimer->setInterval(CURSOR_BLINK_INTERVAL);
connect(m_blinktimer, &QTimer::timeout, this, &QHexView::blinkCursor);
this->setDocument(QHexDocument::fromMemory<QMemoryBuffer>(QByteArray(), this));
}
QHexDocument *QHexView::document()
{
return m_document;
}
void QHexView::setDocument(QHexDocument *document)
{
if (m_renderer)
m_renderer->deleteLater();
if (m_document)
m_document->deleteLater();
m_document = document;
m_renderer = new QHexRenderer(m_document, this->fontMetrics(), this);
connect(m_document, &QHexDocument::documentChanged, this, [&]() {
this->adjustScrollBars();
this->viewport()->update();
});
connect(m_document->cursor(), &QHexCursor::positionChanged, this, &QHexView::moveToSelection);
connect(m_document->cursor(), &QHexCursor::insertionModeChanged, this, &QHexView::renderCurrentLine);
this->adjustScrollBars();
this->viewport()->update();
}
void QHexView::setReadOnly(bool b)
{
m_readonly = b;
if (m_document)
m_document->cursor()->setInsertionMode(QHexCursor::OverwriteMode);
}
bool QHexView::event(QEvent *e)
{
if (m_renderer && (e->type() == QEvent::FontChange)) {
m_renderer->updateMetrics(QFontMetricsF(this->font()));
return true;
}
if (m_document && m_renderer && (e->type() == QEvent::ToolTip)) {
QHelpEvent *helpevent = static_cast<QHelpEvent *>(e);
QHexPosition position;
QPoint abspos = absolutePosition(helpevent->pos());
if (m_renderer->hitTest(abspos, &position, this->firstVisibleLine())) {
QString comments = m_document->metadata()->comments(position.line, position.column);
if (!comments.isEmpty())
QToolTip::showText(helpevent->globalPos(), comments, this);
}
return true;
}
return QAbstractScrollArea::event(e);
}
void QHexView::keyPressEvent(QKeyEvent *e)
{
if (!m_renderer || !m_document) {
QAbstractScrollArea::keyPressEvent(e);
return;
}
QHexCursor *cur = m_document->cursor();
m_blinktimer->stop();
m_renderer->enableCursor();
bool handled = this->processMove(cur, e);
if (!handled)
handled = this->processAction(cur, e);
if (!handled)
handled = this->processTextInput(cur, e);
if (!handled)
QAbstractScrollArea::keyPressEvent(e);
m_blinktimer->start();
}
QPoint QHexView::absolutePosition(const QPoint &pos) const
{
QPoint shift(horizontalScrollBar()->value(), 0);
return pos + shift;
}
void QHexView::mousePressEvent(QMouseEvent *e)
{
QAbstractScrollArea::mousePressEvent(e);
if (!m_renderer || (e->buttons() != Qt::LeftButton))
return;
QHexPosition position;
QPoint abspos = absolutePosition(e->pos());
if (!m_renderer->hitTest(abspos, &position, this->firstVisibleLine()))
return;
m_renderer->selectArea(abspos);
if (m_renderer->editableArea(m_renderer->selectedArea()))
m_document->cursor()->moveTo(position);
e->accept();
}
void QHexView::mouseMoveEvent(QMouseEvent *e)
{
QAbstractScrollArea::mouseMoveEvent(e);
if (!m_renderer || !m_document)
return;
QPoint abspos = absolutePosition(e->pos());
if (e->buttons() == Qt::LeftButton) {
if (m_blinktimer->isActive()) {
m_blinktimer->stop();
m_renderer->enableCursor(false);
}
QHexCursor *cursor = m_document->cursor();
QHexPosition position;
if (!m_renderer->hitTest(abspos, &position, this->firstVisibleLine()))
return;
cursor->select(position.line, position.column, 0);
e->accept();
}
if (e->buttons() != Qt::NoButton)
return;
int hittest = m_renderer->hitTestArea(abspos);
if (m_renderer->editableArea(hittest))
this->setCursor(Qt::IBeamCursor);
else
this->setCursor(Qt::ArrowCursor);
}
void QHexView::mouseReleaseEvent(QMouseEvent *e)
{
QAbstractScrollArea::mouseReleaseEvent(e);
if (e->button() != Qt::LeftButton)
return;
if (!m_blinktimer->isActive())
m_blinktimer->start();
e->accept();
}
void QHexView::focusInEvent(QFocusEvent *e)
{
QAbstractScrollArea::focusInEvent(e);
if (!m_renderer)
return;
m_renderer->enableCursor();
m_blinktimer->start();
}
void QHexView::focusOutEvent(QFocusEvent *e)
{
QAbstractScrollArea::focusOutEvent(e);
if (!m_renderer)
return;
m_blinktimer->stop();
m_renderer->enableCursor(false);
}
void QHexView::wheelEvent(QWheelEvent *e)
{
if (e->angleDelta().y() == 0) {
int value = this->verticalScrollBar()->value();
if (e->angleDelta().x() < 0) // Scroll Down
this->verticalScrollBar()->setValue(value + DOCUMENT_WHEEL_LINES);
else if (e->angleDelta().x() > 0) // Scroll Up
this->verticalScrollBar()->setValue(value - DOCUMENT_WHEEL_LINES);
return;
}
QAbstractScrollArea::wheelEvent(e);
}
void QHexView::resizeEvent(QResizeEvent *e)
{
QAbstractScrollArea::resizeEvent(e);
this->adjustScrollBars();
}
void QHexView::paintEvent(QPaintEvent *e)
{
if (!m_document)
return;
QPainter painter(this->viewport());
painter.setFont(this->font());
const QRect &r = e->rect();
const quint64 firstVisible = this->firstVisibleLine();
const int lineHeight = m_renderer->lineHeight();
const int headerCount = m_renderer->headerLineCount();
// these are lines from the point of view of the visible rect
// where the first "headerCount" are taken by the header
const int first = (r.top() / lineHeight); // included
const int lastPlusOne = (r.bottom() / lineHeight) + 1; // excluded
// compute document lines, adding firstVisible and removing the header
// the max is necessary if the rect covers the header
const quint64 begin = firstVisible + std::max(first - headerCount, 0);
const quint64 end = firstVisible + std::max(lastPlusOne - headerCount, 0);
painter.save();
painter.translate(-this->horizontalScrollBar()->value(), 0);
m_renderer->render(&painter, begin, end, firstVisible);
m_renderer->renderFrame(&painter);
painter.restore();
}
void QHexView::moveToSelection()
{
QHexCursor *cur = m_document->cursor();
if (!this->isLineVisible(cur->currentLine())) {
QScrollBar *vscrollbar = this->verticalScrollBar();
int scrollPos = static_cast<int>(std::max(quint64(0), (cur->currentLine() - this->visibleLines() / 2)) / documentSizeFactor());
vscrollbar->setValue(scrollPos);
} else
this->viewport()->update();
}
void QHexView::blinkCursor()
{
if (!m_renderer)
return;
m_renderer->blinkCursor();
this->renderCurrentLine();
}
void QHexView::moveNext(bool select)
{
QHexCursor *cur = m_document->cursor();
quint64 line = cur->currentLine();
int column = cur->currentColumn();
bool lastcell = (line >= m_renderer->documentLastLine()) && (column >= m_renderer->documentLastColumn());
if ((m_renderer->selectedArea() == QHexRenderer::AsciiArea) && lastcell)
return;
int nibbleindex = cur->currentNibble();
if (m_renderer->selectedArea() == QHexRenderer::HexArea) {
if (lastcell && !nibbleindex)
return;
if (cur->position().offset() >= m_document->length() && nibbleindex)
return;
}
if ((m_renderer->selectedArea() == QHexRenderer::HexArea)) {
nibbleindex++;
nibbleindex %= 2;
if (nibbleindex)
column++;
} else {
nibbleindex = 1;
column++;
}
if (column > m_renderer->hexLineWidth() - 1) {
line = std::min(m_renderer->documentLastLine(), line + 1);
column = 0;
nibbleindex = 1;
}
if (select)
cur->select(line, std::min(m_renderer->hexLineWidth() - 1, column), nibbleindex);
else
cur->moveTo(line, std::min(m_renderer->hexLineWidth() - 1, column), nibbleindex);
}
void QHexView::movePrevious(bool select)
{
QHexCursor *cur = m_document->cursor();
quint64 line = cur->currentLine();
int column = cur->currentColumn();
bool firstcell = !line && !column;
if ((m_renderer->selectedArea() == QHexRenderer::AsciiArea) && firstcell)
return;
int nibbleindex = cur->currentNibble();
if ((m_renderer->selectedArea() == QHexRenderer::HexArea) && firstcell && nibbleindex)
return;
if ((m_renderer->selectedArea() == QHexRenderer::HexArea)) {
nibbleindex--;
nibbleindex %= 2;
if (!nibbleindex)
column--;
} else {
nibbleindex = 1;
column--;
}
if (column < 0) {
line = std::max(quint64(0), line - 1);
column = m_renderer->hexLineWidth() - 1;
nibbleindex = 0;
}
if (select)
cur->select(line, std::max(0, column), nibbleindex);
else
cur->moveTo(line, std::max(0, column), nibbleindex);
}
void QHexView::renderCurrentLine()
{
if (m_document)
this->renderLine(m_document->cursor()->currentLine());
}
bool QHexView::processAction(QHexCursor *cur, QKeyEvent *e)
{
if (m_readonly)
return false;
if (e->modifiers() != Qt::NoModifier) {
if (e->matches(QKeySequence::SelectAll)) {
m_document->cursor()->moveTo(0, 0);
m_document->cursor()->select(m_renderer->documentLastLine(), m_renderer->documentLastColumn() - 1);
} else if (e->matches(QKeySequence::Undo))
m_document->undo();
else if (e->matches(QKeySequence::Redo))
m_document->redo();
else if (e->matches(QKeySequence::Cut))
m_document->cut((m_renderer->selectedArea() == QHexRenderer::HexArea));
else if (e->matches(QKeySequence::Copy))
m_document->copy((m_renderer->selectedArea() == QHexRenderer::HexArea));
else if (e->matches(QKeySequence::Paste))
m_document->paste((m_renderer->selectedArea() == QHexRenderer::HexArea));
else
return false;
return true;
}
if ((e->key() == Qt::Key_Backspace) || (e->key() == Qt::Key_Delete)) {
if (!cur->hasSelection()) {
const QHexPosition &pos = cur->position();
if (pos.offset() <= 0)
return true;
if (e->key() == Qt::Key_Backspace)
m_document->remove(cur->position().offset() - 1, 1);
else
m_document->remove(cur->position().offset(), 1);
} else {
QHexPosition oldpos = cur->selectionStart();
m_document->removeSelection();
cur->moveTo(oldpos.line, oldpos.column + 1);
}
if (e->key() == Qt::Key_Backspace) {
if (m_renderer->selectedArea() == QHexRenderer::HexArea)
this->movePrevious();
this->movePrevious();
}
} else if (e->key() == Qt::Key_Insert)
cur->switchInsertionMode();
else
return false;
return true;
}
bool QHexView::processMove(QHexCursor *cur, QKeyEvent *e)
{
if (e->matches(QKeySequence::MoveToNextChar) || e->matches(QKeySequence::SelectNextChar))
this->moveNext(e->matches(QKeySequence::SelectNextChar));
else if (e->matches(QKeySequence::MoveToPreviousChar) || e->matches(QKeySequence::SelectPreviousChar))
this->movePrevious(e->matches(QKeySequence::SelectPreviousChar));
else if (e->matches(QKeySequence::MoveToNextLine) || e->matches(QKeySequence::SelectNextLine)) {
if (m_renderer->documentLastLine() == cur->currentLine())
return true;
int nextline = cur->currentLine() + 1;
if (e->matches(QKeySequence::MoveToNextLine))
cur->moveTo(nextline, cur->currentColumn());
else
cur->select(nextline, cur->currentColumn());
} else if (e->matches(QKeySequence::MoveToPreviousLine) || e->matches(QKeySequence::SelectPreviousLine)) {
if (!cur->currentLine())
return true;
quint64 prevline = cur->currentLine() - 1;
if (e->matches(QKeySequence::MoveToPreviousLine))
cur->moveTo(prevline, cur->currentColumn());
else
cur->select(prevline, cur->currentColumn());
} else if (e->matches(QKeySequence::MoveToNextPage) || e->matches(QKeySequence::SelectNextPage)) {
if (m_renderer->documentLastLine() == cur->currentLine())
return true;
int pageline = std::min(m_renderer->documentLastLine(), cur->currentLine() + this->visibleLines());
if (e->matches(QKeySequence::MoveToNextPage))
cur->moveTo(pageline, cur->currentColumn());
else
cur->select(pageline, cur->currentColumn());
} else if (e->matches(QKeySequence::MoveToPreviousPage) || e->matches(QKeySequence::SelectPreviousPage)) {
if (!cur->currentLine())
return true;
quint64 pageline = std::max(quint64(0), cur->currentLine() - this->visibleLines());
if (e->matches(QKeySequence::MoveToPreviousPage))
cur->moveTo(pageline, cur->currentColumn());
else
cur->select(pageline, cur->currentColumn());
} else if (e->matches(QKeySequence::MoveToStartOfDocument) || e->matches(QKeySequence::SelectStartOfDocument)) {
if (!cur->currentLine())
return true;
if (e->matches(QKeySequence::MoveToStartOfDocument))
cur->moveTo(0, 0);
else
cur->select(0, 0);
} else if (e->matches(QKeySequence::MoveToEndOfDocument) || e->matches(QKeySequence::SelectEndOfDocument)) {
if (m_renderer->documentLastLine() == cur->currentLine())
return true;
if (e->matches(QKeySequence::MoveToEndOfDocument))
cur->moveTo(m_renderer->documentLastLine(), m_renderer->documentLastColumn());
else
cur->select(m_renderer->documentLastLine(), m_renderer->documentLastColumn());
} else if (e->matches(QKeySequence::MoveToStartOfLine) || e->matches(QKeySequence::SelectStartOfLine)) {
if (e->matches(QKeySequence::MoveToStartOfLine))
cur->moveTo(cur->currentLine(), 0);
else
cur->select(cur->currentLine(), 0);
} else if (e->matches(QKeySequence::MoveToEndOfLine) || e->matches(QKeySequence::SelectEndOfLine)) {
if (e->matches(QKeySequence::MoveToEndOfLine)) {
if (cur->currentLine() == m_renderer->documentLastLine())
cur->moveTo(cur->currentLine(), m_renderer->documentLastColumn());
else
cur->moveTo(cur->currentLine(), m_renderer->hexLineWidth() - 1, 0);
} else {
if (cur->currentLine() == m_renderer->documentLastLine())
cur->select(cur->currentLine(), m_renderer->documentLastColumn());
else
cur->select(cur->currentLine(), m_renderer->hexLineWidth() - 1, 0);
}
} else
return false;
return true;
}
bool QHexView::processTextInput(QHexCursor *cur, QKeyEvent *e)
{
if (m_readonly || (e->modifiers() & Qt::ControlModifier))
return false;
uchar key = static_cast<uchar>(e->text()[0].toLatin1());
if ((m_renderer->selectedArea() == QHexRenderer::HexArea)) {
if (!((key >= '0' && key <= '9') || (key >= 'a' && key <= 'f'))) // Check if is a Hex Char
return false;
// uchar val = static_cast<uchar>(QLatin1String(static_cast<char>(key)).toUInt(nullptr, 16));
uchar val = 1;
m_document->removeSelection();
if (m_document->atEnd() || (cur->currentNibble() && (cur->insertionMode() == QHexCursor::InsertMode))) {
m_document->insert(cur->position().offset(), val << 4); // X0 byte
this->moveNext();
return true;
}
uchar ch = static_cast<uchar>(m_document->at(cur->position().offset()));
if (cur->currentNibble()) // X0
val = (ch & 0x0F) | (val << 4);
else // 0X
val = (ch & 0xF0) | val;
m_document->replace(cur->position().offset(), val);
this->moveNext();
return true;
}
if ((m_renderer->selectedArea() == QHexRenderer::AsciiArea)) {
if (!(key >= 0x20 && key <= 0x7E)) // Check if is a Printable Char
return false;
m_document->removeSelection();
if (!m_document->atEnd() && (cur->insertionMode() == QHexCursor::OverwriteMode))
m_document->replace(cur->position().offset(), key);
else
m_document->insert(cur->position().offset(), key);
QKeyEvent keyevent(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier);
qApp->sendEvent(this, &keyevent);
return true;
}
return false;
}
void QHexView::adjustScrollBars()
{
QScrollBar *vscrollbar = this->verticalScrollBar();
int sizeFactor = this->documentSizeFactor();
vscrollbar->setSingleStep(sizeFactor);
quint64 docLines = m_renderer->documentLines();
quint64 visLines = this->visibleLines();
if (docLines > visLines) {
this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
vscrollbar->setMaximum(static_cast<int>((docLines - visLines) / sizeFactor + 1));
} else {
this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
vscrollbar->setValue(0);
vscrollbar->setMaximum(static_cast<int>(docLines));
}
QScrollBar *hscrollbar = this->horizontalScrollBar();
int documentWidth = m_renderer->documentWidth();
int viewportWidth = viewport()->width();
if (documentWidth > viewportWidth) {
this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
// +1 to see the rightmost vertical line, +2 seems more pleasant to the eyes
hscrollbar->setMaximum(documentWidth - viewportWidth + 2);
} else {
this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
hscrollbar->setValue(0);
hscrollbar->setMaximum(documentWidth);
}
}
void QHexView::renderLine(quint64 line)
{
if (!this->isLineVisible(line))
return;
this->viewport()->update(/*m_renderer->getLineRect(line, this->firstVisibleLine())*/);
}
quint64 QHexView::firstVisibleLine() const
{
return static_cast<quint64>(this->verticalScrollBar()->value()) * documentSizeFactor();
}
quint64 QHexView::lastVisibleLine() const
{
return this->firstVisibleLine() + this->visibleLines() - 1;
}
quint64 QHexView::visibleLines() const
{
int visLines = std::ceil(this->height() / m_renderer->lineHeight()) - m_renderer->headerLineCount();
return std::min(quint64(visLines), m_renderer->documentLines());
}
bool QHexView::isLineVisible(quint64 line) const
{
if (!m_document)
return false;
if (line < this->firstVisibleLine())
return false;
if (line > this->lastVisibleLine())
return false;
return true;
}
int QHexView::documentSizeFactor() const
{
int factor = 1;
if (m_document) {
quint64 docLines = m_renderer->documentLines();
if (docLines >= INT_MAX)
factor = static_cast<int>(docLines / INT_MAX) + 1;
}
return factor;
}

65
parts/hex/qhexview.h Normal file
View file

@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: 2014 Dax89
// SPDX-License-Identifier: MIT
#ifndef QHEXVIEW_H
#define QHEXVIEW_H
#include "document/qhexdocument.h"
#include "document/qhexrenderer.h"
#include <QAbstractScrollArea>
#include <QTimer>
class QHexView : public QAbstractScrollArea
{
Q_OBJECT
public:
explicit QHexView(QWidget *parent = nullptr);
QHexDocument *document();
void setDocument(QHexDocument *document);
void setReadOnly(bool b);
protected:
virtual bool event(QEvent *e);
virtual void keyPressEvent(QKeyEvent *e);
virtual void mousePressEvent(QMouseEvent *e);
virtual void mouseMoveEvent(QMouseEvent *e);
virtual void mouseReleaseEvent(QMouseEvent *e);
virtual void focusInEvent(QFocusEvent *e);
virtual void focusOutEvent(QFocusEvent *e);
virtual void wheelEvent(QWheelEvent *e);
virtual void resizeEvent(QResizeEvent *e);
virtual void paintEvent(QPaintEvent *e);
private Q_SLOTS:
void renderCurrentLine();
void moveToSelection();
void blinkCursor();
private:
void moveNext(bool select = false);
void movePrevious(bool select = false);
private:
bool processMove(QHexCursor *cur, QKeyEvent *e);
bool processTextInput(QHexCursor *cur, QKeyEvent *e);
bool processAction(QHexCursor *cur, QKeyEvent *e);
void adjustScrollBars();
void renderLine(quint64 line);
quint64 firstVisibleLine() const;
quint64 lastVisibleLine() const;
quint64 visibleLines() const;
bool isLineVisible(quint64 line) const;
int documentSizeFactor() const;
QPoint absolutePosition(const QPoint &pos) const;
private:
QHexDocument *m_document;
QHexRenderer *m_renderer;
QTimer *m_blinktimer;
bool m_readonly;
};
#endif // QHEXVIEW_H