mirror of
https://github.com/redstrate/Novus.git
synced 2025-04-23 04:27:45 +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
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
add_subdirectory(exd)
|
add_subdirectory(exd)
|
||||||
|
add_subdirectory(hex)
|
||||||
add_subdirectory(mdl)
|
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