Trying out different QHexEdit function as QTextEdit was too inefficient.
This commit is contained in:
parent
4e7e82dbcd
commit
d4d59b0c01
|
@ -432,6 +432,8 @@ set(SRC_DRIVERS_SDL
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/LuaControl.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/LuaControl.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/CheatsConf.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/CheatsConf.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/HexEditor.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/HexEditor.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/QHexEdit.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/chunks.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleUtilities.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleUtilities.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleVideoConf.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleVideoConf.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleSoundConf.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleSoundConf.cpp
|
||||||
|
|
|
@ -70,20 +70,20 @@ static int getROM( unsigned int offset)
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
static int conv2xchar( int i )
|
//static int conv2xchar( int i )
|
||||||
{
|
//{
|
||||||
int c = 0;
|
// int c = 0;
|
||||||
|
//
|
||||||
if ( (i >= 0) && (i < 10) )
|
// if ( (i >= 0) && (i < 10) )
|
||||||
{
|
// {
|
||||||
c = i + '0';
|
// c = i + '0';
|
||||||
}
|
// }
|
||||||
else if ( i < 16 )
|
// else if ( i < 16 )
|
||||||
{
|
// {
|
||||||
c = (i - 10) + 'A';
|
// c = (i - 10) + 'A';
|
||||||
}
|
// }
|
||||||
return c;
|
// return c;
|
||||||
}
|
//}
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
HexEditorDialog_t::HexEditorDialog_t(QWidget *parent)
|
HexEditorDialog_t::HexEditorDialog_t(QWidget *parent)
|
||||||
: QDialog( parent )
|
: QDialog( parent )
|
||||||
|
@ -100,20 +100,9 @@ HexEditorDialog_t::HexEditorDialog_t(QWidget *parent)
|
||||||
|
|
||||||
mainLayout = new QVBoxLayout();
|
mainLayout = new QVBoxLayout();
|
||||||
|
|
||||||
txtBuf = new QPlainTextEdit();
|
editor = new QHexEdit(this);
|
||||||
|
|
||||||
txtBuf->setFont( font );
|
mainLayout->addWidget( editor );
|
||||||
txtBuf->moveCursor(QTextCursor::Start);
|
|
||||||
//txtBuf->setReadOnly(true);
|
|
||||||
txtBuf->setOverwriteMode(true);
|
|
||||||
txtBuf->setCenterOnScroll(false);
|
|
||||||
txtBuf->setLineWrapMode( QPlainTextEdit::NoWrap );
|
|
||||||
|
|
||||||
connect( txtBuf->verticalScrollBar(), SIGNAL(sliderMoved(int)), this, SLOT(vbarMoved(int)) );
|
|
||||||
connect( txtBuf->verticalScrollBar(), SIGNAL(sliderPressed(void)), this, SLOT(vbarPressed(void)) );
|
|
||||||
//connect( txtBuf->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(vbarMoved(int)) );
|
|
||||||
|
|
||||||
mainLayout->addWidget( txtBuf );
|
|
||||||
|
|
||||||
setLayout( mainLayout );
|
setLayout( mainLayout );
|
||||||
|
|
||||||
|
@ -127,6 +116,28 @@ HexEditorDialog_t::HexEditorDialog_t(QWidget *parent)
|
||||||
redraw = false;
|
redraw = false;
|
||||||
total_instructions_lp = 0;
|
total_instructions_lp = 0;
|
||||||
|
|
||||||
|
dataArray = new QByteArray( 0x20000, '0' );
|
||||||
|
dataBuffer = new QBuffer( dataArray, this );
|
||||||
|
dataBuffer->setBuffer( dataArray );
|
||||||
|
|
||||||
|
printf("DataArray: Size: %i \n", dataArray->size() );
|
||||||
|
editor->setData( *dataBuffer );
|
||||||
|
|
||||||
|
editor->setAddressArea(true);
|
||||||
|
editor->setAsciiArea(true);
|
||||||
|
editor->setHighlighting(true);
|
||||||
|
editor->setOverwriteMode(true);
|
||||||
|
editor->setReadOnly(true);
|
||||||
|
editor->setHexCaps(true);
|
||||||
|
|
||||||
|
editor->setHighlightingColor( QColor("yellow") );
|
||||||
|
editor->setAddressAreaColor( QColor("white") );
|
||||||
|
editor->setSelectionColor( QColor("blue") );
|
||||||
|
//editor->setFont( font );
|
||||||
|
|
||||||
|
editor->setAddressWidth(6);
|
||||||
|
editor->setBytesPerLine(16);
|
||||||
|
|
||||||
showMemViewResults(true);
|
showMemViewResults(true);
|
||||||
|
|
||||||
periodicTimer = new QTimer( this );
|
periodicTimer = new QTimer( this );
|
||||||
|
@ -164,39 +175,6 @@ void HexEditorDialog_t::keyReleaseEvent(QKeyEvent *event)
|
||||||
//assignHotkey( event );
|
//assignHotkey( event );
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
int HexEditorDialog_t::calcVisibleRange( int *start_out, int *end_out, int *center_out )
|
|
||||||
{
|
|
||||||
int start_pos, end_pos;
|
|
||||||
QPoint bottom_left( 0, txtBuf->viewport()->height() - 1);
|
|
||||||
|
|
||||||
start_pos = txtBuf->cursorForPosition(QPoint(0, 0)).position();
|
|
||||||
end_pos = txtBuf->cursorForPosition(bottom_left).position();
|
|
||||||
|
|
||||||
start_pos = (start_pos / numCharsPerLine) - 1;
|
|
||||||
end_pos = (end_pos / numCharsPerLine) + 1;
|
|
||||||
|
|
||||||
if ( start_pos < 0 )
|
|
||||||
{
|
|
||||||
start_pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//printf("Start: %i End: %i\n", start_pos, end_pos );
|
|
||||||
|
|
||||||
if ( start_out )
|
|
||||||
{
|
|
||||||
*start_out = start_pos;
|
|
||||||
}
|
|
||||||
if ( end_out )
|
|
||||||
{
|
|
||||||
*end_out = end_pos;
|
|
||||||
}
|
|
||||||
if ( center_out )
|
|
||||||
{
|
|
||||||
*center_out = (start_pos + end_pos) / 2;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
void HexEditorDialog_t::setMode(int new_mode)
|
void HexEditorDialog_t::setMode(int new_mode)
|
||||||
{
|
{
|
||||||
if ( mode != new_mode )
|
if ( mode != new_mode )
|
||||||
|
@ -218,38 +196,8 @@ void HexEditorDialog_t::initMem(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void HexEditorDialog_t::vbarMoved( int pos )
|
|
||||||
{
|
|
||||||
printf("VBAR: %i \n", pos );
|
|
||||||
}
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
void HexEditorDialog_t::vbarPressed( void )
|
|
||||||
{
|
|
||||||
printf("VBAR: %i \n", txtBuf->verticalScrollBar()->value() );
|
|
||||||
}
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
void HexEditorDialog_t::setCursor( int pos )
|
|
||||||
{
|
|
||||||
QTextCursor cursor = txtBuf->textCursor();
|
|
||||||
cursor.setPosition( pos );
|
|
||||||
txtBuf->setTextCursor( cursor );
|
|
||||||
}
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
void HexEditorDialog_t::advCursor( int num )
|
|
||||||
{
|
|
||||||
QTextCursor cursor = txtBuf->textCursor();
|
|
||||||
cursor.setPosition( cursor.position() + num );
|
|
||||||
txtBuf->setTextCursor( cursor );
|
|
||||||
}
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
void HexEditorDialog_t::showMemViewResults (bool reset)
|
void HexEditorDialog_t::showMemViewResults (bool reset)
|
||||||
{
|
{
|
||||||
int i, row, row_start, row_end;
|
|
||||||
int addr = 0, lineAddr = 0, c, un, ln;
|
|
||||||
char addrStr[128], valStr[16][8], ascii[18];
|
|
||||||
char valChg[16], asciiChg, activityColoringOn, colorEnable;
|
|
||||||
QTextCursor cursorSave;
|
|
||||||
|
|
||||||
switch ( mode )
|
switch ( mode )
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
|
@ -282,7 +230,7 @@ void HexEditorDialog_t::showMemViewResults (bool reset)
|
||||||
}
|
}
|
||||||
mbuf_size = 0;
|
mbuf_size = 0;
|
||||||
redraw = true;
|
redraw = true;
|
||||||
txtBuf->setPlainText("No ROM Loaded");
|
//txtBuf->setPlainText("No ROM Loaded");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -294,6 +242,8 @@ void HexEditorDialog_t::showMemViewResults (bool reset)
|
||||||
printf("Mode: %i MemSize:%i 0x%08x\n", mode, memSize, (unsigned int)memSize );
|
printf("Mode: %i MemSize:%i 0x%08x\n", mode, memSize, (unsigned int)memSize );
|
||||||
reset = 1;
|
reset = 1;
|
||||||
|
|
||||||
|
dataArray->resize( mbuf_size );
|
||||||
|
|
||||||
if ( mbuf )
|
if ( mbuf )
|
||||||
{
|
{
|
||||||
free(mbuf); mbuf = NULL;
|
free(mbuf); mbuf = NULL;
|
||||||
|
@ -312,143 +262,6 @@ void HexEditorDialog_t::showMemViewResults (bool reset)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( reset )
|
|
||||||
{
|
|
||||||
txtBuf->clear();
|
|
||||||
row_start = 0;
|
|
||||||
row_end = numLines;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
calcVisibleRange( &row_start, &row_end, NULL );
|
|
||||||
}
|
|
||||||
if ( row_start < 0 )
|
|
||||||
{
|
|
||||||
row_start = 0;
|
|
||||||
}
|
|
||||||
if ( row_end > numLines )
|
|
||||||
{
|
|
||||||
row_end = numLines;
|
|
||||||
}
|
|
||||||
cursorSave = txtBuf->textCursor();
|
|
||||||
//printf("Cursor Start Position: %i\n", txtBuf->textCursor().position() );
|
|
||||||
|
|
||||||
for (row=row_start; row<row_end; row++)
|
|
||||||
{
|
|
||||||
lineAddr = (row*16);
|
|
||||||
|
|
||||||
//printf("Pre Cursor Line %i Position: %i\n", row, txtBuf->textCursor().position() );
|
|
||||||
|
|
||||||
if ( !reset )
|
|
||||||
{
|
|
||||||
setCursor( row * numCharsPerLine );
|
|
||||||
}
|
|
||||||
|
|
||||||
//printf("Post Cursor Line %i Position: %i\n", row, txtBuf->textCursor().position() );
|
|
||||||
|
|
||||||
if ( reset )
|
|
||||||
{
|
|
||||||
sprintf( addrStr, "%08X ", lineAddr );
|
|
||||||
|
|
||||||
txtBuf->insertPlainText( addrStr );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
advCursor(9);
|
|
||||||
}
|
|
||||||
|
|
||||||
asciiChg = reset;
|
|
||||||
|
|
||||||
for (i=0; i<16; i++)
|
|
||||||
{
|
|
||||||
valChg[i] = reset;
|
|
||||||
|
|
||||||
addr = lineAddr+i;
|
|
||||||
|
|
||||||
c = memAccessFunc(addr);
|
|
||||||
|
|
||||||
if ( mbuf[addr].draw )
|
|
||||||
{
|
|
||||||
mbuf[addr].draw = 0;
|
|
||||||
valChg[i] = 1;
|
|
||||||
asciiChg = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
un = ( c & 0x00f0 ) >> 4;
|
|
||||||
ln = ( c & 0x000f );
|
|
||||||
|
|
||||||
valStr[i][0] = ' ';
|
|
||||||
valStr[i][1] = conv2xchar(un);
|
|
||||||
valStr[i][2] = conv2xchar(ln);
|
|
||||||
valStr[i][3] = ' ';
|
|
||||||
valStr[i][4] = 0;
|
|
||||||
|
|
||||||
if ( isprint(c) )
|
|
||||||
{
|
|
||||||
ascii[i] = c;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ascii[i] = '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( reset )
|
|
||||||
{
|
|
||||||
txtBuf->insertPlainText( valStr[i] );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( valChg[i] )
|
|
||||||
{
|
|
||||||
for (int j=0; j<4; j++)
|
|
||||||
{
|
|
||||||
txtBuf->textCursor().deleteChar();
|
|
||||||
}
|
|
||||||
txtBuf->insertPlainText( valStr[i] );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
advCursor(4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ascii[16] = 0;
|
|
||||||
|
|
||||||
if ( reset )
|
|
||||||
{
|
|
||||||
txtBuf->insertPlainText( ascii );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( asciiChg )
|
|
||||||
{
|
|
||||||
for (int j=0; j<16; j++)
|
|
||||||
{
|
|
||||||
txtBuf->textCursor().deleteChar();
|
|
||||||
}
|
|
||||||
txtBuf->insertPlainText( ascii );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
advCursor(16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( reset )
|
|
||||||
{
|
|
||||||
txtBuf->insertPlainText("\n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
advCursor(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore Cursor to Saved Location
|
|
||||||
txtBuf->setTextCursor( cursorSave );
|
|
||||||
txtBuf->ensureCursorVisible();
|
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void HexEditorDialog_t::updatePeriodic(void)
|
void HexEditorDialog_t::updatePeriodic(void)
|
||||||
|
|
|
@ -14,7 +14,10 @@
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QFrame>
|
#include <QFrame>
|
||||||
#include <QGroupBox>
|
#include <QGroupBox>
|
||||||
#include <QPlainTextEdit>
|
#include <QBuffer>
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
#include "Qt/QHexEdit.h"
|
||||||
|
|
||||||
struct memByte_t
|
struct memByte_t
|
||||||
{
|
{
|
||||||
|
@ -47,12 +50,12 @@ class HexEditorDialog_t : public QDialog
|
||||||
void showMemViewResults (bool reset);
|
void showMemViewResults (bool reset);
|
||||||
int checkMemActivity(void);
|
int checkMemActivity(void);
|
||||||
int calcVisibleRange( int *start_out, int *end_out, int *center_out );
|
int calcVisibleRange( int *start_out, int *end_out, int *center_out );
|
||||||
void setCursor( int pos );
|
|
||||||
void advCursor( int num );
|
|
||||||
|
|
||||||
QFont font;
|
QFont font;
|
||||||
QPlainTextEdit *txtBuf;
|
QHexEdit *editor;
|
||||||
QTimer *periodicTimer;
|
QTimer *periodicTimer;
|
||||||
|
QByteArray *dataArray;
|
||||||
|
QBuffer *dataBuffer;
|
||||||
|
|
||||||
int mode;
|
int mode;
|
||||||
int numLines;
|
int numLines;
|
||||||
|
@ -72,7 +75,5 @@ class HexEditorDialog_t : public QDialog
|
||||||
void closeWindow(void);
|
void closeWindow(void);
|
||||||
private slots:
|
private slots:
|
||||||
void updatePeriodic(void);
|
void updatePeriodic(void);
|
||||||
void vbarMoved( int pos );
|
|
||||||
void vbarPressed( void );
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,424 @@
|
||||||
|
#ifndef QHEXEDIT_H
|
||||||
|
#define QHEXEDIT_H
|
||||||
|
|
||||||
|
#include <QAbstractScrollArea>
|
||||||
|
#include <QPen>
|
||||||
|
#include <QBrush>
|
||||||
|
#include <QBuffer>
|
||||||
|
|
||||||
|
#include "Qt/chunks.h"
|
||||||
|
//#include "commands.h"
|
||||||
|
|
||||||
|
#ifdef QHEXEDIT_EXPORTS
|
||||||
|
#define QHEXEDIT_API Q_DECL_EXPORT
|
||||||
|
#elif QHEXEDIT_IMPORTS
|
||||||
|
#define QHEXEDIT_API Q_DECL_IMPORT
|
||||||
|
#else
|
||||||
|
#define QHEXEDIT_API
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** \mainpage
|
||||||
|
QHexEdit is a binary editor widget for Qt.
|
||||||
|
|
||||||
|
\version Version 0.8.9
|
||||||
|
\image html qhexedit.png
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** QHexEdit is a hex editor widget written in C++ for the Qt (Qt4, Qt5) framework.
|
||||||
|
It is a simple editor for binary data, just like QPlainTextEdit is for text
|
||||||
|
data. There are sip configuration files included, so it is easy to create
|
||||||
|
bindings for PyQt and you can use this widget also in python 2 and 3.
|
||||||
|
|
||||||
|
QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use
|
||||||
|
the mouse or the keyboard to navigate inside the widget. If you hit the keys
|
||||||
|
(0..9, a..f) you will change the data. Changed data is highlighted and can be
|
||||||
|
accessed via data().
|
||||||
|
|
||||||
|
Normally QHexEdit works in the overwrite mode. You can set overwrite mode(false)
|
||||||
|
and insert data. In this case the size of data() increases. It is also possible
|
||||||
|
to delete bytes (del or backspace), here the size of data decreases.
|
||||||
|
|
||||||
|
You can select data with keyboard hits or mouse movements. The copy-key will
|
||||||
|
copy the selected data into the clipboard. The cut-key copies also but deletes
|
||||||
|
it afterwards. In overwrite mode, the paste function overwrites the content of
|
||||||
|
the (does not change the length) data. In insert mode, clipboard data will be
|
||||||
|
inserted. The clipboard content is expected in ASCII Hex notation. Unknown
|
||||||
|
characters will be ignored.
|
||||||
|
|
||||||
|
QHexEdit comes with undo/redo functionality. All changes can be undone, by
|
||||||
|
pressing the undo-key (usually ctr-z). They can also be redone afterwards.
|
||||||
|
The undo/redo framework is cleared, when setData() sets up a new
|
||||||
|
content for the editor. You can search data inside the content with indexOf()
|
||||||
|
and lastIndexOf(). The replace() function is to change located subdata. This
|
||||||
|
'replaced' data can also be undone by the undo/redo framework.
|
||||||
|
|
||||||
|
QHexEdit is based on QIODevice, that's why QHexEdit can handle big amounts of
|
||||||
|
data. The size of edited data can be more then two gigabytes without any
|
||||||
|
restrictions.
|
||||||
|
*/
|
||||||
|
class QHEXEDIT_API QHexEdit : public QAbstractScrollArea
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
/*! Property address area switch the address area on or off. Set addressArea true
|
||||||
|
(show it), false (hide it).
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool addressArea READ addressArea WRITE setAddressArea)
|
||||||
|
|
||||||
|
/*! Property address area color sets (setAddressAreaColor()) the background
|
||||||
|
color of address areas. You can also read the color (addressAreaColor()).
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(QColor addressAreaColor READ addressAreaColor WRITE setAddressAreaColor)
|
||||||
|
|
||||||
|
/*! Property addressOffset is added to the Numbers of the Address Area.
|
||||||
|
A offset in the address area (left side) is sometimes useful, whe you show
|
||||||
|
only a segment of a complete memory picture. With setAddressOffset() you set
|
||||||
|
this property - with addressOffset() you get the current value.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(qint64 addressOffset READ addressOffset WRITE setAddressOffset)
|
||||||
|
|
||||||
|
/*! Set and get the minimum width of the address area, width in characters.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(int addressWidth READ addressWidth WRITE setAddressWidth)
|
||||||
|
|
||||||
|
/*! Switch the ascii area on (true, show it) or off (false, hide it).
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool asciiArea READ asciiArea WRITE setAsciiArea)
|
||||||
|
|
||||||
|
/*! Set and get bytes number per line.*/
|
||||||
|
Q_PROPERTY(int bytesPerLine READ bytesPerLine WRITE setBytesPerLine)
|
||||||
|
|
||||||
|
/*! Property cursorPosition sets or gets the position of the editor cursor
|
||||||
|
in QHexEdit. Every byte in data has two cursor positions: the lower and upper
|
||||||
|
Nibble. Maximum cursor position is factor two of data.size().
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(qint64 cursorPosition READ cursorPosition WRITE setCursorPosition)
|
||||||
|
|
||||||
|
/*! Property data holds the content of QHexEdit. Call setData() to set the
|
||||||
|
content of QHexEdit, data() returns the actual content. When calling setData()
|
||||||
|
with a QByteArray as argument, QHexEdit creates a internal copy of the data
|
||||||
|
If you want to edit big files please use setData(), based on QIODevice.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(QByteArray data READ data WRITE setData NOTIFY dataChanged)
|
||||||
|
|
||||||
|
/*! That property defines if the hex values looks as a-f if the value is false(default)
|
||||||
|
or A-F if value is true.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool hexCaps READ hexCaps WRITE setHexCaps)
|
||||||
|
|
||||||
|
/*! Property defines the dynamic calculation of bytesPerLine parameter depends of width of widget.
|
||||||
|
set this property true to avoid horizontal scrollbars and show the maximal possible data. defalut value is false*/
|
||||||
|
Q_PROPERTY(bool dynamicBytesPerLine READ dynamicBytesPerLine WRITE setDynamicBytesPerLine)
|
||||||
|
|
||||||
|
/*! Switch the highlighting feature on or of: true (show it), false (hide it).
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool highlighting READ highlighting WRITE setHighlighting)
|
||||||
|
|
||||||
|
/*! Property highlighting color sets (setHighlightingColor()) the background
|
||||||
|
color of highlighted text areas. You can also read the color
|
||||||
|
(highlightingColor()).
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(QColor highlightingColor READ highlightingColor WRITE setHighlightingColor)
|
||||||
|
|
||||||
|
/*! Property overwrite mode sets (setOverwriteMode()) or gets (overwriteMode()) the mode
|
||||||
|
in which the editor works. In overwrite mode the user will overwrite existing data. The
|
||||||
|
size of data will be constant. In insert mode the size will grow, when inserting
|
||||||
|
new data.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode)
|
||||||
|
|
||||||
|
/*! Property selection color sets (setSelectionColor()) the background
|
||||||
|
color of selected text areas. You can also read the color
|
||||||
|
(selectionColor()).
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor)
|
||||||
|
|
||||||
|
/*! Property readOnly sets (setReadOnly()) or gets (isReadOnly) the mode
|
||||||
|
in which the editor works. In readonly mode the the user can only navigate
|
||||||
|
through the data and select data; modifying is not possible. This
|
||||||
|
property's default is false.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
|
||||||
|
|
||||||
|
/*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/
|
||||||
|
Q_PROPERTY(QFont font READ font WRITE setFont)
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! Creates an instance of QHexEdit.
|
||||||
|
\param parent Parent widget of QHexEdit.
|
||||||
|
*/
|
||||||
|
QHexEdit(QWidget *parent=0);
|
||||||
|
|
||||||
|
// Access to data of qhexedit
|
||||||
|
|
||||||
|
/*! Sets the data of QHexEdit. The QIODevice will be opened just before reading
|
||||||
|
and closed immediately afterwards. This is to allow other programs to rewrite
|
||||||
|
the file while editing it.
|
||||||
|
*/
|
||||||
|
bool setData(QIODevice &iODevice);
|
||||||
|
|
||||||
|
/*! Gives back the data as a QByteArray starting at position \param pos and
|
||||||
|
delivering \param count bytes.
|
||||||
|
*/
|
||||||
|
QByteArray dataAt(qint64 pos, qint64 count=-1);
|
||||||
|
|
||||||
|
/*! Gives back the data into a \param iODevice starting at position \param pos
|
||||||
|
and delivering \param count bytes.
|
||||||
|
*/
|
||||||
|
bool write(QIODevice &iODevice, qint64 pos=0, qint64 count=-1);
|
||||||
|
|
||||||
|
|
||||||
|
// Char handling
|
||||||
|
|
||||||
|
/*! Inserts a char.
|
||||||
|
\param pos Index position, where to insert
|
||||||
|
\param ch Char, which is to insert
|
||||||
|
The char will be inserted and size of data grows.
|
||||||
|
*/
|
||||||
|
void insert(qint64 pos, char ch);
|
||||||
|
|
||||||
|
/*! Removes len bytes from the content.
|
||||||
|
\param pos Index position, where to remove
|
||||||
|
\param len Amount of bytes to remove
|
||||||
|
*/
|
||||||
|
void remove(qint64 pos, qint64 len=1);
|
||||||
|
|
||||||
|
/*! Replaces a char.
|
||||||
|
\param pos Index position, where to overwrite
|
||||||
|
\param ch Char, which is to insert
|
||||||
|
The char will be overwritten and size remains constant.
|
||||||
|
*/
|
||||||
|
void replace(qint64 pos, char ch);
|
||||||
|
|
||||||
|
|
||||||
|
// ByteArray handling
|
||||||
|
|
||||||
|
/*! Inserts a byte array.
|
||||||
|
\param pos Index position, where to insert
|
||||||
|
\param ba QByteArray, which is to insert
|
||||||
|
The QByteArray will be inserted and size of data grows.
|
||||||
|
*/
|
||||||
|
void insert(qint64 pos, const QByteArray &ba);
|
||||||
|
|
||||||
|
/*! Replaces \param len bytes with a byte array \param ba
|
||||||
|
\param pos Index position, where to overwrite
|
||||||
|
\param ba QByteArray, which is inserted
|
||||||
|
\param len count of bytes to overwrite
|
||||||
|
The data is overwritten and size of data may change.
|
||||||
|
*/
|
||||||
|
void replace(qint64 pos, qint64 len, const QByteArray &ba);
|
||||||
|
|
||||||
|
|
||||||
|
// Utility functions
|
||||||
|
/*! Calc cursor position from graphics position
|
||||||
|
* \param point from where the cursor position should be calculated
|
||||||
|
* \return Cursor position
|
||||||
|
*/
|
||||||
|
qint64 cursorPosition(QPoint point);
|
||||||
|
|
||||||
|
/*! Ensure the cursor to be visbile
|
||||||
|
*/
|
||||||
|
void ensureVisible();
|
||||||
|
|
||||||
|
/*! Find first occurrence of ba in QHexEdit data
|
||||||
|
* \param ba Data to find
|
||||||
|
* \param from Point where the search starts
|
||||||
|
* \return pos if fond, else -1
|
||||||
|
*/
|
||||||
|
qint64 indexOf(const QByteArray &ba, qint64 from);
|
||||||
|
|
||||||
|
/*! Returns if any changes where done on document
|
||||||
|
* \return true when document is modified else false
|
||||||
|
*/
|
||||||
|
bool isModified();
|
||||||
|
|
||||||
|
/*! Find last occurrence of ba in QHexEdit data
|
||||||
|
* \param ba Data to find
|
||||||
|
* \param from Point where the search starts
|
||||||
|
* \return pos if fond, else -1
|
||||||
|
*/
|
||||||
|
qint64 lastIndexOf(const QByteArray &ba, qint64 from);
|
||||||
|
|
||||||
|
/*! Gives back a formatted image of the selected content of QHexEdit
|
||||||
|
*/
|
||||||
|
QString selectionToReadableString();
|
||||||
|
|
||||||
|
/*! Return the selected content of QHexEdit as QByteArray
|
||||||
|
*/
|
||||||
|
QString selectedData();
|
||||||
|
|
||||||
|
/*! Set Font of QHexEdit
|
||||||
|
* \param font
|
||||||
|
*/
|
||||||
|
void setFont(const QFont &font);
|
||||||
|
|
||||||
|
/*! Gives back a formatted image of the content of QHexEdit
|
||||||
|
*/
|
||||||
|
QString toReadableString();
|
||||||
|
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
/*! Redoes the last operation. If there is no operation to redo, i.e.
|
||||||
|
there is no redo step in the undo/redo history, nothing happens.
|
||||||
|
*/
|
||||||
|
void redo();
|
||||||
|
|
||||||
|
/*! Undoes the last operation. If there is no operation to undo, i.e.
|
||||||
|
there is no undo step in the undo/redo history, nothing happens.
|
||||||
|
*/
|
||||||
|
void undo();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
/*! Contains the address, where the cursor is located. */
|
||||||
|
void currentAddressChanged(qint64 address);
|
||||||
|
|
||||||
|
/*! Contains the size of the data to edit. */
|
||||||
|
void currentSizeChanged(qint64 size);
|
||||||
|
|
||||||
|
/*! The signal is emitted every time, the data is changed. */
|
||||||
|
void dataChanged();
|
||||||
|
|
||||||
|
/*! The signal is emitted every time, the overwrite mode is changed. */
|
||||||
|
void overwriteModeChanged(bool state);
|
||||||
|
|
||||||
|
|
||||||
|
/*! \cond docNever */
|
||||||
|
public:
|
||||||
|
~QHexEdit();
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
bool addressArea();
|
||||||
|
void setAddressArea(bool addressArea);
|
||||||
|
|
||||||
|
QColor addressAreaColor();
|
||||||
|
void setAddressAreaColor(const QColor &color);
|
||||||
|
|
||||||
|
qint64 addressOffset();
|
||||||
|
void setAddressOffset(qint64 addressArea);
|
||||||
|
|
||||||
|
int addressWidth();
|
||||||
|
void setAddressWidth(int addressWidth);
|
||||||
|
|
||||||
|
bool asciiArea();
|
||||||
|
void setAsciiArea(bool asciiArea);
|
||||||
|
|
||||||
|
int bytesPerLine();
|
||||||
|
void setBytesPerLine(int count);
|
||||||
|
|
||||||
|
qint64 cursorPosition();
|
||||||
|
void setCursorPosition(qint64 position);
|
||||||
|
|
||||||
|
QByteArray data();
|
||||||
|
void setData(const QByteArray &ba);
|
||||||
|
|
||||||
|
void setHexCaps(const bool isCaps);
|
||||||
|
bool hexCaps();
|
||||||
|
|
||||||
|
void setDynamicBytesPerLine(const bool isDynamic);
|
||||||
|
bool dynamicBytesPerLine();
|
||||||
|
|
||||||
|
bool highlighting();
|
||||||
|
void setHighlighting(bool mode);
|
||||||
|
|
||||||
|
QColor highlightingColor();
|
||||||
|
void setHighlightingColor(const QColor &color);
|
||||||
|
|
||||||
|
bool overwriteMode();
|
||||||
|
void setOverwriteMode(bool overwriteMode);
|
||||||
|
|
||||||
|
bool isReadOnly();
|
||||||
|
void setReadOnly(bool readOnly);
|
||||||
|
|
||||||
|
QColor selectionColor();
|
||||||
|
void setSelectionColor(const QColor &color);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Handle events
|
||||||
|
void keyPressEvent(QKeyEvent *event);
|
||||||
|
void mouseMoveEvent(QMouseEvent * event);
|
||||||
|
void mousePressEvent(QMouseEvent * event);
|
||||||
|
void paintEvent(QPaintEvent *event);
|
||||||
|
void resizeEvent(QResizeEvent *);
|
||||||
|
virtual bool focusNextPrevChild(bool next);
|
||||||
|
private:
|
||||||
|
// Handle selections
|
||||||
|
void resetSelection(qint64 pos); // set selectionStart and selectionEnd to pos
|
||||||
|
void resetSelection(); // set selectionEnd to selectionStart
|
||||||
|
void setSelection(qint64 pos); // set min (if below init) or max (if greater init)
|
||||||
|
qint64 getSelectionBegin();
|
||||||
|
qint64 getSelectionEnd();
|
||||||
|
|
||||||
|
// Private utility functions
|
||||||
|
void init();
|
||||||
|
void readBuffers();
|
||||||
|
QString toReadable(const QByteArray &ba);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void adjust(); // recalc pixel positions
|
||||||
|
void dataChangedPrivate(int idx=0); // emit dataChanged() signal
|
||||||
|
void refresh(); // ensureVisible() and readBuffers()
|
||||||
|
void updateCursor(); // update blinking cursor
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Name convention: pixel positions start with _px
|
||||||
|
int _pxCharWidth, _pxCharHeight; // char dimensions (dependend on font)
|
||||||
|
int _pxPosHexX; // X-Pos of HeaxArea
|
||||||
|
int _pxPosAdrX; // X-Pos of Address Area
|
||||||
|
int _pxPosAsciiX; // X-Pos of Ascii Area
|
||||||
|
int _pxGapAdr; // gap left from AddressArea
|
||||||
|
int _pxGapAdrHex; // gap between AddressArea and HexAerea
|
||||||
|
int _pxGapHexAscii; // gap between HexArea and AsciiArea
|
||||||
|
int _pxCursorWidth; // cursor width
|
||||||
|
int _pxSelectionSub; // offset selection rect
|
||||||
|
int _pxCursorX; // current cursor pos
|
||||||
|
int _pxCursorY; // current cursor pos
|
||||||
|
|
||||||
|
// Name convention: absolute byte positions in chunks start with _b
|
||||||
|
qint64 _bSelectionBegin; // first position of Selection
|
||||||
|
qint64 _bSelectionEnd; // end of Selection
|
||||||
|
qint64 _bSelectionInit; // memory position of Selection
|
||||||
|
qint64 _bPosFirst; // position of first byte shown
|
||||||
|
qint64 _bPosLast; // position of last byte shown
|
||||||
|
qint64 _bPosCurrent; // current position
|
||||||
|
|
||||||
|
// variables to store the property values
|
||||||
|
bool _addressArea; // left area of QHexEdit
|
||||||
|
QColor _addressAreaColor;
|
||||||
|
int _addressWidth;
|
||||||
|
bool _asciiArea;
|
||||||
|
qint64 _addressOffset;
|
||||||
|
int _bytesPerLine;
|
||||||
|
int _hexCharsInLine;
|
||||||
|
bool _highlighting;
|
||||||
|
bool _overwriteMode;
|
||||||
|
QBrush _brushSelection;
|
||||||
|
QPen _penSelection;
|
||||||
|
QBrush _brushHighlighted;
|
||||||
|
QPen _penHighlighted;
|
||||||
|
bool _readOnly;
|
||||||
|
bool _hexCaps;
|
||||||
|
bool _dynamicBytesPerLine;
|
||||||
|
|
||||||
|
// other variables
|
||||||
|
bool _editAreaIsAscii; // flag about the ascii mode edited
|
||||||
|
int _addrDigits; // real no of addressdigits, may be > addressWidth
|
||||||
|
bool _blink; // help get cursor blinking
|
||||||
|
QBuffer _bData; // buffer, when setup with QByteArray
|
||||||
|
Chunks *_chunks; // IODevice based access to data
|
||||||
|
QTimer _cursorTimer; // for blinking cursor
|
||||||
|
qint64 _cursorPosition; // absolute position of cursor, 1 Byte == 2 tics
|
||||||
|
QRect _cursorRect; // physical dimensions of cursor
|
||||||
|
QByteArray _data; // QHexEdit's data, when setup with QByteArray
|
||||||
|
QByteArray _dataShown; // data in the current View
|
||||||
|
QByteArray _hexDataShown; // data in view, transformed to hex
|
||||||
|
qint64 _lastEventSize; // size, which was emitted last time
|
||||||
|
QByteArray _markedShown; // marked data in view
|
||||||
|
bool _modified; // Is any data in editor modified?
|
||||||
|
int _rowsShown; // lines of text shown
|
||||||
|
//UndoStack * _undoStack; // Stack to store edit actions for undo/redo
|
||||||
|
/*! \endcond docNever */
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QHEXEDIT_H
|
|
@ -0,0 +1,323 @@
|
||||||
|
#include "chunks.h"
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#define NORMAL 0
|
||||||
|
#define HIGHLIGHTED 1
|
||||||
|
|
||||||
|
#define BUFFER_SIZE 0x10000
|
||||||
|
#define CHUNK_SIZE 0x1000
|
||||||
|
#define READ_CHUNK_MASK Q_INT64_C(0xfffffffffffff000)
|
||||||
|
|
||||||
|
// ***************************************** Constructors and file settings
|
||||||
|
|
||||||
|
Chunks::Chunks(QObject *parent): QObject(parent)
|
||||||
|
{
|
||||||
|
QBuffer *buf = new QBuffer(this);
|
||||||
|
setIODevice(*buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
Chunks::Chunks(QIODevice &ioDevice, QObject *parent): QObject(parent)
|
||||||
|
{
|
||||||
|
setIODevice(ioDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Chunks::setIODevice(QIODevice &ioDevice)
|
||||||
|
{
|
||||||
|
_ioDevice = &ioDevice;
|
||||||
|
bool ok = _ioDevice->open(QIODevice::ReadOnly);
|
||||||
|
if (ok) // Try to open IODevice
|
||||||
|
{
|
||||||
|
_size = _ioDevice->size();
|
||||||
|
_ioDevice->close();
|
||||||
|
}
|
||||||
|
else // Fallback is an empty buffer
|
||||||
|
{
|
||||||
|
QBuffer *buf = new QBuffer(this);
|
||||||
|
_ioDevice = buf;
|
||||||
|
_size = 0;
|
||||||
|
}
|
||||||
|
_chunks.clear();
|
||||||
|
_pos = 0;
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***************************************** Getting data out of Chunks
|
||||||
|
|
||||||
|
QByteArray Chunks::data(qint64 pos, qint64 maxSize, QByteArray *highlighted)
|
||||||
|
{
|
||||||
|
qint64 ioDelta = 0;
|
||||||
|
int chunkIdx = 0;
|
||||||
|
|
||||||
|
Chunk chunk;
|
||||||
|
QByteArray buffer;
|
||||||
|
|
||||||
|
// Do some checks and some arrangements
|
||||||
|
if (highlighted)
|
||||||
|
highlighted->clear();
|
||||||
|
|
||||||
|
if (pos >= _size)
|
||||||
|
return buffer;
|
||||||
|
|
||||||
|
if (maxSize < 0)
|
||||||
|
maxSize = _size;
|
||||||
|
else
|
||||||
|
if ((pos + maxSize) > _size)
|
||||||
|
maxSize = _size - pos;
|
||||||
|
|
||||||
|
_ioDevice->open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
while (maxSize > 0)
|
||||||
|
{
|
||||||
|
chunk.absPos = LLONG_MAX;
|
||||||
|
bool chunksLoopOngoing = true;
|
||||||
|
while ((chunkIdx < _chunks.count()) && chunksLoopOngoing)
|
||||||
|
{
|
||||||
|
// In this section, we track changes before our required data and
|
||||||
|
// we take the editdet data, if availible. ioDelta is a difference
|
||||||
|
// counter to justify the read pointer to the original data, if
|
||||||
|
// data in between was deleted or inserted.
|
||||||
|
|
||||||
|
chunk = _chunks[chunkIdx];
|
||||||
|
if (chunk.absPos > pos)
|
||||||
|
chunksLoopOngoing = false;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chunkIdx += 1;
|
||||||
|
qint64 count;
|
||||||
|
qint64 chunkOfs = pos - chunk.absPos;
|
||||||
|
if (maxSize > ((qint64)chunk.data.size() - chunkOfs))
|
||||||
|
{
|
||||||
|
count = (qint64)chunk.data.size() - chunkOfs;
|
||||||
|
ioDelta += CHUNK_SIZE - chunk.data.size();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
count = maxSize;
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
buffer += chunk.data.mid(chunkOfs, (int)count);
|
||||||
|
maxSize -= count;
|
||||||
|
pos += count;
|
||||||
|
if (highlighted)
|
||||||
|
*highlighted += chunk.dataChanged.mid(chunkOfs, (int)count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((maxSize > 0) && (pos < chunk.absPos))
|
||||||
|
{
|
||||||
|
// In this section, we read data from the original source. This only will
|
||||||
|
// happen, whe no copied data is available
|
||||||
|
|
||||||
|
qint64 byteCount;
|
||||||
|
QByteArray readBuffer;
|
||||||
|
if ((chunk.absPos - pos) > maxSize)
|
||||||
|
byteCount = maxSize;
|
||||||
|
else
|
||||||
|
byteCount = chunk.absPos - pos;
|
||||||
|
|
||||||
|
maxSize -= byteCount;
|
||||||
|
_ioDevice->seek(pos + ioDelta);
|
||||||
|
readBuffer = _ioDevice->read(byteCount);
|
||||||
|
buffer += readBuffer;
|
||||||
|
if (highlighted)
|
||||||
|
*highlighted += QByteArray(readBuffer.size(), NORMAL);
|
||||||
|
pos += readBuffer.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ioDevice->close();
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Chunks::write(QIODevice &iODevice, qint64 pos, qint64 count)
|
||||||
|
{
|
||||||
|
if (count == -1)
|
||||||
|
count = _size;
|
||||||
|
bool ok = iODevice.open(QIODevice::WriteOnly);
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
for (qint64 idx=pos; idx < count; idx += BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
QByteArray ba = data(idx, BUFFER_SIZE);
|
||||||
|
iODevice.write(ba);
|
||||||
|
}
|
||||||
|
iODevice.close();
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***************************************** Set and get highlighting infos
|
||||||
|
|
||||||
|
void Chunks::setDataChanged(qint64 pos, bool dataChanged)
|
||||||
|
{
|
||||||
|
if ((pos < 0) || (pos >= _size))
|
||||||
|
return;
|
||||||
|
int chunkIdx = getChunkIndex(pos);
|
||||||
|
qint64 posInBa = pos - _chunks[chunkIdx].absPos;
|
||||||
|
_chunks[chunkIdx].dataChanged[(int)posInBa] = char(dataChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Chunks::dataChanged(qint64 pos)
|
||||||
|
{
|
||||||
|
QByteArray highlighted;
|
||||||
|
data(pos, 1, &highlighted);
|
||||||
|
return bool(highlighted.at(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***************************************** Search API
|
||||||
|
|
||||||
|
qint64 Chunks::indexOf(const QByteArray &ba, qint64 from)
|
||||||
|
{
|
||||||
|
qint64 result = -1;
|
||||||
|
QByteArray buffer;
|
||||||
|
|
||||||
|
for (qint64 pos=from; (pos < _size) && (result < 0); pos += BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
buffer = data(pos, BUFFER_SIZE + ba.size() - 1);
|
||||||
|
int findPos = buffer.indexOf(ba);
|
||||||
|
if (findPos >= 0)
|
||||||
|
result = pos + (qint64)findPos;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 Chunks::lastIndexOf(const QByteArray &ba, qint64 from)
|
||||||
|
{
|
||||||
|
qint64 result = -1;
|
||||||
|
QByteArray buffer;
|
||||||
|
|
||||||
|
for (qint64 pos=from; (pos > 0) && (result < 0); pos -= BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
qint64 sPos = pos - BUFFER_SIZE - (qint64)ba.size() + 1;
|
||||||
|
if (sPos < 0)
|
||||||
|
sPos = 0;
|
||||||
|
buffer = data(sPos, pos - sPos);
|
||||||
|
int findPos = buffer.lastIndexOf(ba);
|
||||||
|
if (findPos >= 0)
|
||||||
|
result = sPos + (qint64)findPos;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***************************************** Char manipulations
|
||||||
|
|
||||||
|
bool Chunks::insert(qint64 pos, char b)
|
||||||
|
{
|
||||||
|
if ((pos < 0) || (pos > _size))
|
||||||
|
return false;
|
||||||
|
int chunkIdx;
|
||||||
|
if (pos == _size)
|
||||||
|
chunkIdx = getChunkIndex(pos-1);
|
||||||
|
else
|
||||||
|
chunkIdx = getChunkIndex(pos);
|
||||||
|
qint64 posInBa = pos - _chunks[chunkIdx].absPos;
|
||||||
|
_chunks[chunkIdx].data.insert(posInBa, b);
|
||||||
|
_chunks[chunkIdx].dataChanged.insert(posInBa, char(1));
|
||||||
|
for (int idx=chunkIdx+1; idx < _chunks.size(); idx++)
|
||||||
|
_chunks[idx].absPos += 1;
|
||||||
|
_size += 1;
|
||||||
|
_pos = pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Chunks::overwrite(qint64 pos, char b)
|
||||||
|
{
|
||||||
|
if ((pos < 0) || (pos >= _size))
|
||||||
|
return false;
|
||||||
|
int chunkIdx = getChunkIndex(pos);
|
||||||
|
qint64 posInBa = pos - _chunks[chunkIdx].absPos;
|
||||||
|
_chunks[chunkIdx].data[(int)posInBa] = b;
|
||||||
|
_chunks[chunkIdx].dataChanged[(int)posInBa] = char(1);
|
||||||
|
_pos = pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Chunks::removeAt(qint64 pos)
|
||||||
|
{
|
||||||
|
if ((pos < 0) || (pos >= _size))
|
||||||
|
return false;
|
||||||
|
int chunkIdx = getChunkIndex(pos);
|
||||||
|
qint64 posInBa = pos - _chunks[chunkIdx].absPos;
|
||||||
|
_chunks[chunkIdx].data.remove(posInBa, 1);
|
||||||
|
_chunks[chunkIdx].dataChanged.remove(posInBa, 1);
|
||||||
|
for (int idx=chunkIdx+1; idx < _chunks.size(); idx++)
|
||||||
|
_chunks[idx].absPos -= 1;
|
||||||
|
_size -= 1;
|
||||||
|
_pos = pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ***************************************** Utility functions
|
||||||
|
|
||||||
|
char Chunks::operator[](qint64 pos)
|
||||||
|
{
|
||||||
|
return data(pos, 1)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 Chunks::pos()
|
||||||
|
{
|
||||||
|
return _pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 Chunks::size()
|
||||||
|
{
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Chunks::getChunkIndex(qint64 absPos)
|
||||||
|
{
|
||||||
|
// This routine checks, if there is already a copied chunk available. If os, it
|
||||||
|
// returns a reference to it. If there is no copied chunk available, original
|
||||||
|
// data will be copied into a new chunk.
|
||||||
|
|
||||||
|
int foundIdx = -1;
|
||||||
|
int insertIdx = 0;
|
||||||
|
qint64 ioDelta = 0;
|
||||||
|
|
||||||
|
|
||||||
|
for (int idx=0; idx < _chunks.size(); idx++)
|
||||||
|
{
|
||||||
|
Chunk chunk = _chunks[idx];
|
||||||
|
if ((absPos >= chunk.absPos) && (absPos < (chunk.absPos + chunk.data.size())))
|
||||||
|
{
|
||||||
|
foundIdx = idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (absPos < chunk.absPos)
|
||||||
|
{
|
||||||
|
insertIdx = idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ioDelta += chunk.data.size() - CHUNK_SIZE;
|
||||||
|
insertIdx = idx + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundIdx == -1)
|
||||||
|
{
|
||||||
|
Chunk newChunk;
|
||||||
|
qint64 readAbsPos = absPos - ioDelta;
|
||||||
|
qint64 readPos = (readAbsPos & READ_CHUNK_MASK);
|
||||||
|
_ioDevice->open(QIODevice::ReadOnly);
|
||||||
|
_ioDevice->seek(readPos);
|
||||||
|
newChunk.data = _ioDevice->read(CHUNK_SIZE);
|
||||||
|
_ioDevice->close();
|
||||||
|
newChunk.absPos = absPos - (readAbsPos - readPos);
|
||||||
|
newChunk.dataChanged = QByteArray(newChunk.data.size(), char(0));
|
||||||
|
_chunks.insert(insertIdx, newChunk);
|
||||||
|
foundIdx = insertIdx;
|
||||||
|
}
|
||||||
|
return foundIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef MODUL_TEST
|
||||||
|
int Chunks::chunkSize()
|
||||||
|
{
|
||||||
|
return _chunks.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,77 @@
|
||||||
|
#ifndef CHUNKS_H
|
||||||
|
#define CHUNKS_H
|
||||||
|
|
||||||
|
/** \cond docNever */
|
||||||
|
|
||||||
|
/*! The Chunks class is the storage backend for QHexEdit.
|
||||||
|
*
|
||||||
|
* When QHexEdit loads data, Chunks access them using a QIODevice interface. When the app uses
|
||||||
|
* a QByteArray interface, QBuffer is used to provide again a QIODevice like interface. No data
|
||||||
|
* will be changed, therefore Chunks opens the QIODevice in QIODevice::ReadOnly mode. After every
|
||||||
|
* access Chunks closes the QIODevice, that's why external applications can overwrite files while
|
||||||
|
* QHexEdit shows them.
|
||||||
|
*
|
||||||
|
* When the the user starts to edit the data, Chunks creates a local copy of a chunk of data (4
|
||||||
|
* kilobytes) and notes all changes there. Parallel to that chunk, there is a second chunk,
|
||||||
|
* which keep track of which bytes are changed and which not.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QtCore>
|
||||||
|
|
||||||
|
struct Chunk
|
||||||
|
{
|
||||||
|
QByteArray data;
|
||||||
|
QByteArray dataChanged;
|
||||||
|
qint64 absPos;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Chunks: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
// Constructors and file settings
|
||||||
|
Chunks(QObject *parent);
|
||||||
|
Chunks(QIODevice &ioDevice, QObject *parent);
|
||||||
|
bool setIODevice(QIODevice &ioDevice);
|
||||||
|
|
||||||
|
// Getting data out of Chunks
|
||||||
|
QByteArray data(qint64 pos=0, qint64 count=-1, QByteArray *highlighted=0);
|
||||||
|
bool write(QIODevice &iODevice, qint64 pos=0, qint64 count=-1);
|
||||||
|
|
||||||
|
// Set and get highlighting infos
|
||||||
|
void setDataChanged(qint64 pos, bool dataChanged);
|
||||||
|
bool dataChanged(qint64 pos);
|
||||||
|
|
||||||
|
// Search API
|
||||||
|
qint64 indexOf(const QByteArray &ba, qint64 from);
|
||||||
|
qint64 lastIndexOf(const QByteArray &ba, qint64 from);
|
||||||
|
|
||||||
|
// Char manipulations
|
||||||
|
bool insert(qint64 pos, char b);
|
||||||
|
bool overwrite(qint64 pos, char b);
|
||||||
|
bool removeAt(qint64 pos);
|
||||||
|
|
||||||
|
// Utility functions
|
||||||
|
char operator[](qint64 pos);
|
||||||
|
qint64 pos();
|
||||||
|
qint64 size();
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
int getChunkIndex(qint64 absPos);
|
||||||
|
|
||||||
|
QIODevice * _ioDevice;
|
||||||
|
qint64 _pos;
|
||||||
|
qint64 _size;
|
||||||
|
QList<Chunk> _chunks;
|
||||||
|
|
||||||
|
#ifdef MODUL_TEST
|
||||||
|
public:
|
||||||
|
int chunkSize();
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/** \endcond docNever */
|
||||||
|
|
||||||
|
#endif // CHUNKS_H
|
Loading…
Reference in New Issue