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:
parent
4fa1460b66
commit
95f10d9622
20 changed files with 2510 additions and 1 deletions
|
@ -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
25
parts/hex/CMakeLists.txt
Normal 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})
|
47
parts/hex/document/buffer/qhexbuffer.cpp
Normal file
47
parts/hex/document/buffer/qhexbuffer.cpp
Normal 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);
|
||||
}
|
36
parts/hex/document/buffer/qhexbuffer.h
Normal file
36
parts/hex/document/buffer/qhexbuffer.h
Normal 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
|
48
parts/hex/document/buffer/qmemorybuffer.cpp
Normal file
48
parts/hex/document/buffer/qmemorybuffer.cpp
Normal 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));
|
||||
}
|
30
parts/hex/document/buffer/qmemorybuffer.h
Normal file
30
parts/hex/document/buffer/qmemorybuffer.h
Normal 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
|
107
parts/hex/document/buffer/qmemoryrefbuffer.cpp
Normal file
107
parts/hex/document/buffer/qmemoryrefbuffer.cpp
Normal 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;
|
||||
}
|
30
parts/hex/document/buffer/qmemoryrefbuffer.h
Normal file
30
parts/hex/document/buffer/qmemoryrefbuffer.h
Normal 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
|
177
parts/hex/document/qhexcursor.cpp
Normal file
177
parts/hex/document/qhexcursor.cpp
Normal 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();
|
||||
}
|
86
parts/hex/document/qhexcursor.h
Normal file
86
parts/hex/document/qhexcursor.h
Normal 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
|
242
parts/hex/document/qhexdocument.cpp
Normal file
242
parts/hex/document/qhexdocument.cpp
Normal 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;
|
||||
}
|
135
parts/hex/document/qhexdocument.h
Normal file
135
parts/hex/document/qhexdocument.h
Normal 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
|
147
parts/hex/document/qhexmetadata.cpp
Normal file
147
parts/hex/document/qhexmetadata.cpp
Normal 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);
|
||||
}
|
70
parts/hex/document/qhexmetadata.h
Normal file
70
parts/hex/document/qhexmetadata.h
Normal 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
|
500
parts/hex/document/qhexrenderer.cpp
Normal file
500
parts/hex/document/qhexrenderer.cpp
Normal 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();
|
||||
}
|
82
parts/hex/document/qhexrenderer.h
Normal file
82
parts/hex/document/qhexrenderer.h
Normal 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
17
parts/hex/hexpart.cpp
Normal 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
15
parts/hex/hexpart.h
Normal 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
649
parts/hex/qhexview.cpp
Normal 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
65
parts/hex/qhexview.h
Normal 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
|
Loading…
Add table
Reference in a new issue