forked from qt-creator/qt-creator
find support for lazily loaded binary editors. Added
case-insensitive search capatibilities (the default). Slower but fits better with the other editors.
This commit is contained in:
@@ -39,10 +39,22 @@
|
|||||||
#include <QtGui/QWheelEvent>
|
#include <QtGui/QWheelEvent>
|
||||||
#include <QtGui/QApplication>
|
#include <QtGui/QApplication>
|
||||||
#include <QtGui/QClipboard>
|
#include <QtGui/QClipboard>
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QByteArrayMatcher>
|
||||||
|
|
||||||
using namespace BINEditor;
|
using namespace BINEditor;
|
||||||
|
|
||||||
|
// QByteArray::toLower() is broken, it stops at the first \0
|
||||||
|
static void lower(QByteArray &ba)
|
||||||
|
{
|
||||||
|
char *data = ba.data();
|
||||||
|
char *end = data + ba.size();
|
||||||
|
while (data != end) {
|
||||||
|
if (*data >= 0x41 && *data <= 0x5A)
|
||||||
|
*data += 0x20;
|
||||||
|
++data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static QByteArray calculateHexPattern(const QByteArray &pattern)
|
static QByteArray calculateHexPattern(const QByteArray &pattern)
|
||||||
{
|
{
|
||||||
QByteArray result;
|
QByteArray result;
|
||||||
@@ -75,6 +87,7 @@ BinEditor::BinEditor(QWidget *parent)
|
|||||||
m_anchorPosition = 0;
|
m_anchorPosition = 0;
|
||||||
m_lowNibble = false;
|
m_lowNibble = false;
|
||||||
m_cursorVisible = false;
|
m_cursorVisible = false;
|
||||||
|
m_caseSensitiveSearch = false;
|
||||||
setFocusPolicy(Qt::WheelFocus);
|
setFocusPolicy(Qt::WheelFocus);
|
||||||
m_addressString = QString(9, QLatin1Char(':'));
|
m_addressString = QString(9, QLatin1Char(':'));
|
||||||
}
|
}
|
||||||
@@ -126,7 +139,7 @@ void BinEditor::addLazyData(int block, const QByteArray &data)
|
|||||||
viewport()->update();
|
viewport()->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BinEditor::requestDataAt(int pos) const
|
bool BinEditor::requestDataAt(int pos, bool synchronous) const
|
||||||
{
|
{
|
||||||
if (!m_inLazyMode)
|
if (!m_inLazyMode)
|
||||||
return true;
|
return true;
|
||||||
@@ -136,7 +149,7 @@ bool BinEditor::requestDataAt(int pos) const
|
|||||||
if (it == m_lazyData.end()) {
|
if (it == m_lazyData.end()) {
|
||||||
if (!m_lazyRequests.contains(block)) {
|
if (!m_lazyRequests.contains(block)) {
|
||||||
m_lazyRequests.insert(block);
|
m_lazyRequests.insert(block);
|
||||||
emit const_cast<BinEditor*>(this)->lazyDataRequested(block);
|
emit const_cast<BinEditor*>(this)->lazyDataRequested(block, synchronous);
|
||||||
if (!m_lazyRequests.contains(block))
|
if (!m_lazyRequests.contains(block))
|
||||||
return true; // synchronous data source
|
return true; // synchronous data source
|
||||||
}
|
}
|
||||||
@@ -171,9 +184,30 @@ QByteArray BinEditor::dataMid(int from, int length) const
|
|||||||
{
|
{
|
||||||
if (!m_inLazyMode)
|
if (!m_inLazyMode)
|
||||||
return m_data.mid(from, length);
|
return m_data.mid(from, length);
|
||||||
return QByteArray(length, '\0'); // ### TODO
|
|
||||||
|
int end = from + length;
|
||||||
|
int block = from / m_blockSize;
|
||||||
|
|
||||||
|
QByteArray data;
|
||||||
|
do {
|
||||||
|
data += m_lazyData.value(block++, m_emptyBlock);
|
||||||
|
} while (block * m_blockSize < end);
|
||||||
|
|
||||||
|
return data.mid(from - ((from / m_blockSize) * m_blockSize), length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray BinEditor::blockData(int block) const
|
||||||
|
{
|
||||||
|
if (!m_inLazyMode) {
|
||||||
|
QByteArray data = m_data.mid(block * m_blockSize, m_blockSize);
|
||||||
|
if (data.size() < m_blockSize)
|
||||||
|
data.resize(m_blockSize);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return m_lazyData.value(block, m_emptyBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void BinEditor::setFontSettings(const TextEditor::FontSettings &fs)
|
void BinEditor::setFontSettings(const TextEditor::FontSettings &fs)
|
||||||
{
|
{
|
||||||
setFont(fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_TEXT)).font());
|
setFont(fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_TEXT)).font());
|
||||||
@@ -301,6 +335,7 @@ void BinEditor::setLazyData(int cursorPosition, int size, int blockSize)
|
|||||||
{
|
{
|
||||||
m_inLazyMode = true;
|
m_inLazyMode = true;
|
||||||
m_blockSize = blockSize;
|
m_blockSize = blockSize;
|
||||||
|
Q_ASSERT((blockSize/16) * 16 == blockSize);
|
||||||
m_emptyBlock = QByteArray(blockSize, '\0');
|
m_emptyBlock = QByteArray(blockSize, '\0');
|
||||||
m_data.clear();
|
m_data.clear();
|
||||||
m_lazyData.clear();
|
m_lazyData.clear();
|
||||||
@@ -421,35 +456,116 @@ void BinEditor::updateLines(int fromPosition, int toPosition)
|
|||||||
viewport()->update(0, y, viewport()->width(), h);
|
viewport()->update(0, y, viewport()->width(), h);
|
||||||
}
|
}
|
||||||
|
|
||||||
int BinEditor::find(const QByteArray &pattern, int from, QTextDocument::FindFlags findFlags)
|
int BinEditor::dataIndexOf(const QByteArray &pattern, int from, bool caseSensitive) const
|
||||||
{
|
{
|
||||||
if (pattern.isEmpty())
|
if (!m_inLazyMode && caseSensitive) {
|
||||||
return false;
|
return m_data.indexOf(pattern, from);
|
||||||
|
}
|
||||||
|
|
||||||
|
int trailing = pattern.size();
|
||||||
|
if (trailing > m_blockSize)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
QByteArray buffer(m_blockSize + trailing, Qt::Uninitialized);
|
||||||
|
char *b = buffer.data();
|
||||||
|
QByteArrayMatcher matcher(pattern);
|
||||||
|
|
||||||
|
int block = from / m_blockSize;
|
||||||
|
|
||||||
|
while (from < m_size) {
|
||||||
|
if (!requestDataAt(block * m_blockSize, true))
|
||||||
|
return -1;
|
||||||
|
QByteArray data = blockData(block);
|
||||||
|
::memcpy(b, b + m_blockSize, trailing);
|
||||||
|
::memcpy(b + trailing, data.constData(), m_blockSize);
|
||||||
|
|
||||||
|
if (!caseSensitive)
|
||||||
|
::lower(buffer);
|
||||||
|
|
||||||
|
int pos = matcher.indexIn(buffer, from - (block * m_blockSize) + trailing);
|
||||||
|
if (pos >= 0)
|
||||||
|
return pos + block * m_blockSize - trailing;
|
||||||
|
++block;
|
||||||
|
from = block * m_blockSize - trailing;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BinEditor::dataLastIndexOf(const QByteArray &pattern, int from, bool caseSensitive) const
|
||||||
|
{
|
||||||
|
if (!m_inLazyMode && caseSensitive)
|
||||||
|
return m_data.lastIndexOf(pattern, from);
|
||||||
|
|
||||||
|
int trailing = pattern.size();
|
||||||
|
if (trailing > m_blockSize)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
QByteArray buffer(m_blockSize + trailing, Qt::Uninitialized);
|
||||||
|
char *b = buffer.data();
|
||||||
|
|
||||||
|
int block = from / m_blockSize;
|
||||||
|
|
||||||
|
while (from > 0) {
|
||||||
|
if (!requestDataAt(block * m_blockSize, true))
|
||||||
|
return -1;
|
||||||
|
QByteArray data = blockData(block);
|
||||||
|
::memcpy(b + m_blockSize, b, trailing);
|
||||||
|
::memcpy(b, data.constData(), m_blockSize);
|
||||||
|
|
||||||
|
if (!caseSensitive)
|
||||||
|
::lower(buffer);
|
||||||
|
|
||||||
|
int pos = buffer.lastIndexOf(pattern, from - (block * m_blockSize));
|
||||||
|
if (pos >= 0)
|
||||||
|
return pos + block * m_blockSize;
|
||||||
|
--block;
|
||||||
|
from = block * m_blockSize + (m_blockSize-1) + trailing;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int BinEditor::find(const QByteArray &pattern_arg, int from, QTextDocument::FindFlags findFlags)
|
||||||
|
{
|
||||||
|
if (pattern_arg.isEmpty())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
QByteArray pattern = pattern_arg;
|
||||||
|
|
||||||
|
bool caseSensitiveSearch = (findFlags & QTextDocument::FindCaseSensitively);
|
||||||
|
|
||||||
|
if (!caseSensitiveSearch)
|
||||||
|
::lower(pattern);
|
||||||
|
|
||||||
bool backwards = (findFlags & QTextDocument::FindBackward);
|
bool backwards = (findFlags & QTextDocument::FindBackward);
|
||||||
int found = backwards ? m_data.lastIndexOf(pattern, from)
|
int found = backwards ? dataLastIndexOf(pattern, from, caseSensitiveSearch)
|
||||||
: m_data.indexOf(pattern, from);
|
: dataIndexOf(pattern, from, caseSensitiveSearch);
|
||||||
|
|
||||||
int foundHex = -1;
|
int foundHex = -1;
|
||||||
QByteArray hexPattern = calculateHexPattern(pattern);
|
QByteArray hexPattern = calculateHexPattern(pattern_arg);
|
||||||
if (!hexPattern.isEmpty()) {
|
if (!hexPattern.isEmpty()) {
|
||||||
foundHex = backwards ? m_data.lastIndexOf(hexPattern, from)
|
foundHex = backwards ? dataLastIndexOf(hexPattern, from)
|
||||||
: m_data.indexOf(hexPattern, from);
|
: dataIndexOf(hexPattern, from);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pos = (found >= 0 && (foundHex < 0 || found < foundHex)) ? found : foundHex;
|
int pos = (found >= 0 && (foundHex < 0 || found < foundHex)) ? found : foundHex;
|
||||||
|
|
||||||
|
if (pos >= m_size)
|
||||||
|
pos = -1;
|
||||||
|
|
||||||
if (pos >= 0) {
|
if (pos >= 0) {
|
||||||
setCursorPosition(pos);
|
setCursorPosition(pos);
|
||||||
setCursorPosition(pos + (found == pos ? pattern.size() : hexPattern.size()), KeepAnchor);
|
setCursorPosition(pos + (found == pos ? pattern.size() : hexPattern.size()), KeepAnchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
int BinEditor::findPattern(const QByteArray &data, int from, int offset, int *match)
|
int BinEditor::findPattern(const QByteArray &data, const QByteArray &dataHex, int from, int offset, int *match)
|
||||||
{
|
{
|
||||||
if (m_searchPattern.isEmpty())
|
if (m_searchPattern.isEmpty())
|
||||||
return -1;
|
return -1;
|
||||||
int normal = m_searchPattern.isEmpty()? -1 : data.indexOf(m_searchPattern, from - offset);
|
int normal = m_searchPattern.isEmpty()? -1 : data.indexOf(m_searchPattern, from - offset);
|
||||||
int hex = m_searchPatternHex.isEmpty()? -1 : data.indexOf(m_searchPatternHex, from - offset);
|
int hex = m_searchPatternHex.isEmpty()? -1 : dataHex.indexOf(m_searchPatternHex, from - offset);
|
||||||
|
|
||||||
if (normal >= 0 && (hex < 0 || normal < hex)) {
|
if (normal >= 0 && (hex < 0 || normal < hex)) {
|
||||||
if (match)
|
if (match)
|
||||||
@@ -513,12 +629,17 @@ void BinEditor::paintEvent(QPaintEvent *e)
|
|||||||
|
|
||||||
int matchLength = 0;
|
int matchLength = 0;
|
||||||
|
|
||||||
QByteArray patternData;
|
QByteArray patternData, patternDataHex;
|
||||||
int patternOffset = qMax(0, topLine*16 - m_searchPattern.size());
|
int patternOffset = qMax(0, topLine*16 - m_searchPattern.size());
|
||||||
if (!m_searchPattern.isEmpty())
|
if (!m_searchPattern.isEmpty()) {
|
||||||
patternData = dataMid(patternOffset, m_numVisibleLines * 16 + (topLine*16 - patternOffset));
|
patternData = dataMid(patternOffset, m_numVisibleLines * 16 + (topLine*16 - patternOffset));
|
||||||
|
patternDataHex = patternData;
|
||||||
|
if (!m_caseSensitiveSearch)
|
||||||
|
::lower(patternData);
|
||||||
|
}
|
||||||
|
|
||||||
int foundPatternAt = findPattern(patternData, patternOffset, patternOffset, &matchLength);
|
|
||||||
|
int foundPatternAt = findPattern(patternData, patternDataHex, patternOffset, patternOffset, &matchLength);
|
||||||
|
|
||||||
int selStart = qMin(m_cursorPosition, m_anchorPosition);
|
int selStart = qMin(m_cursorPosition, m_anchorPosition);
|
||||||
int selEnd = qMax(m_cursorPosition, m_anchorPosition);
|
int selEnd = qMax(m_cursorPosition, m_anchorPosition);
|
||||||
@@ -581,7 +702,7 @@ void BinEditor::paintEvent(QPaintEvent *e)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (foundPatternAt >= 0 && pos >= foundPatternAt + matchLength)
|
if (foundPatternAt >= 0 && pos >= foundPatternAt + matchLength)
|
||||||
foundPatternAt = findPattern(patternData, foundPatternAt + matchLength, patternOffset, &matchLength);
|
foundPatternAt = findPattern(patternData, patternDataHex, foundPatternAt + matchLength, patternOffset, &matchLength);
|
||||||
|
|
||||||
|
|
||||||
uchar value = (uchar)dataAt(pos);
|
uchar value = (uchar)dataAt(pos);
|
||||||
@@ -917,11 +1038,14 @@ void BinEditor::copy()
|
|||||||
QApplication::clipboard()->setText(QString::fromLatin1(dataMid(selStart, selEnd - selStart)));
|
QApplication::clipboard()->setText(QString::fromLatin1(dataMid(selStart, selEnd - selStart)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BinEditor::highlightSearchResults(const QByteArray &pattern, QTextDocument::FindFlags /*findFlags*/)
|
void BinEditor::highlightSearchResults(const QByteArray &pattern, QTextDocument::FindFlags findFlags)
|
||||||
{
|
{
|
||||||
if (m_searchPattern == pattern)
|
if (m_searchPattern == pattern)
|
||||||
return;
|
return;
|
||||||
m_searchPattern = pattern;
|
m_searchPattern = pattern;
|
||||||
|
m_caseSensitiveSearch = (findFlags & QTextDocument::FindCaseSensitively);
|
||||||
|
if (!m_caseSensitiveSearch)
|
||||||
|
::lower(m_searchPattern);
|
||||||
m_searchPatternHex = calculateHexPattern(pattern);
|
m_searchPatternHex = calculateHexPattern(pattern);
|
||||||
viewport()->update();
|
viewport()->update();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ public:
|
|||||||
void setData(const QByteArray &data);
|
void setData(const QByteArray &data);
|
||||||
QByteArray data() const;
|
QByteArray data() const;
|
||||||
|
|
||||||
|
inline int dataSize() const { return m_size; }
|
||||||
|
|
||||||
inline bool inLazyMode() const { return m_inLazyMode; }
|
inline bool inLazyMode() const { return m_inLazyMode; }
|
||||||
void setLazyData(int cursorPosition, int size, int blockSize = 4096);
|
void setLazyData(int cursorPosition, int size, int blockSize = 4096);
|
||||||
inline int lazyDataBlockSize() const { return m_blockSize; }
|
inline int lazyDataBlockSize() const { return m_blockSize; }
|
||||||
@@ -118,7 +120,7 @@ Q_SIGNALS:
|
|||||||
void copyAvailable(bool);
|
void copyAvailable(bool);
|
||||||
void cursorPositionChanged(int position);
|
void cursorPositionChanged(int position);
|
||||||
|
|
||||||
void lazyDataRequested(int block);
|
void lazyDataRequested(int block, bool syncronous);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void scrollContentsBy(int dx, int dy);
|
void scrollContentsBy(int dx, int dy);
|
||||||
@@ -141,12 +143,17 @@ private:
|
|||||||
int m_blockSize;
|
int m_blockSize;
|
||||||
mutable QSet<int> m_lazyRequests;
|
mutable QSet<int> m_lazyRequests;
|
||||||
QByteArray m_emptyBlock;
|
QByteArray m_emptyBlock;
|
||||||
|
QByteArray m_lowerBlock;
|
||||||
int m_size;
|
int m_size;
|
||||||
|
|
||||||
bool requestDataAt(int pos) const;
|
int dataIndexOf(const QByteArray &pattern, int from, bool caseSensitive = true) const;
|
||||||
|
int dataLastIndexOf(const QByteArray &pattern, int from, bool caseSensitive = true) const;
|
||||||
|
|
||||||
|
bool requestDataAt(int pos, bool synchronous = false) const;
|
||||||
char dataAt(int pos) const;
|
char dataAt(int pos) const;
|
||||||
void changeDataAt(int pos, char c);
|
void changeDataAt(int pos, char c);
|
||||||
QByteArray dataMid(int from, int length) const;
|
QByteArray dataMid(int from, int length) const;
|
||||||
|
QByteArray blockData(int block) const;
|
||||||
|
|
||||||
int m_unmodifiedState;
|
int m_unmodifiedState;
|
||||||
int m_readOnly;
|
int m_readOnly;
|
||||||
@@ -171,6 +178,7 @@ private:
|
|||||||
|
|
||||||
QByteArray m_searchPattern;
|
QByteArray m_searchPattern;
|
||||||
QByteArray m_searchPatternHex;
|
QByteArray m_searchPatternHex;
|
||||||
|
bool m_caseSensitiveSearch;
|
||||||
|
|
||||||
QBasicTimer m_cursorBlinkTimer;
|
QBasicTimer m_cursorBlinkTimer;
|
||||||
|
|
||||||
@@ -184,7 +192,7 @@ private:
|
|||||||
|
|
||||||
void changeData(int position, uchar character, bool highNibble = false);
|
void changeData(int position, uchar character, bool highNibble = false);
|
||||||
|
|
||||||
int findPattern(const QByteArray &data, int from, int offset, int *match);
|
int findPattern(const QByteArray &data, const QByteArray &dataHex, int from, int offset, int *match);
|
||||||
void drawItems(QPainter *painter, int x, int y, const QString &itemString);
|
void drawItems(QPainter *painter, int x, int y, const QString &itemString);
|
||||||
|
|
||||||
struct BinEditorEditCommand {
|
struct BinEditorEditCommand {
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ public:
|
|||||||
int found = m_editor->find(pattern, pos, Find::IFindSupport::textDocumentFlagsForFindFlags(findFlags));
|
int found = m_editor->find(pattern, pos, Find::IFindSupport::textDocumentFlagsForFindFlags(findFlags));
|
||||||
if (found < 0)
|
if (found < 0)
|
||||||
found = m_editor->find(pattern,
|
found = m_editor->find(pattern,
|
||||||
(findFlags & Find::IFindSupport::FindBackward)?m_editor->data().size()-1:0,
|
(findFlags & Find::IFindSupport::FindBackward)?m_editor->dataSize()-1:0,
|
||||||
Find::IFindSupport::textDocumentFlagsForFindFlags(findFlags));
|
Find::IFindSupport::textDocumentFlagsForFindFlags(findFlags));
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,7 @@ public:
|
|||||||
m_mimeType(QLatin1String(BINEditor::Constants::C_BINEDITOR_MIMETYPE))
|
m_mimeType(QLatin1String(BINEditor::Constants::C_BINEDITOR_MIMETYPE))
|
||||||
{
|
{
|
||||||
m_editor = parent;
|
m_editor = parent;
|
||||||
connect(m_editor, SIGNAL(lazyDataRequested(int)), this, SLOT(provideData(int)));
|
connect(m_editor, SIGNAL(lazyDataRequested(int, bool)), this, SLOT(provideData(int)));
|
||||||
}
|
}
|
||||||
~BinEditorFile() {}
|
~BinEditorFile() {}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user