2009-02-25 09:15:00 +01:00
|
|
|
/**************************************************************************
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2009-06-17 00:01:27 +10:00
|
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** Commercial Usage
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
|
|
|
|
** accordance with the Qt Commercial License Agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and Nokia.
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** GNU Lesser General Public License Usage
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
|
** General Public License version 2.1 as published by the Free Software
|
|
|
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
|
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** If you are unsure which license is appropriate for your use, please
|
2009-06-17 00:01:27 +10:00
|
|
|
** contact the sales department at http://www.qtsoftware.com/contact.
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
**************************************************************************/
|
2008-12-02 14:09:21 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "bineditor.h"
|
|
|
|
|
|
|
|
|
|
#include <texteditor/fontsettings.h>
|
|
|
|
|
#include <texteditor/texteditorconstants.h>
|
|
|
|
|
|
|
|
|
|
#include <QtGui/QScrollBar>
|
|
|
|
|
#include <QtGui/QFontMetrics>
|
|
|
|
|
#include <QtGui/QPainter>
|
|
|
|
|
#include <QtGui/QScrollBar>
|
|
|
|
|
#include <QtGui/QWheelEvent>
|
|
|
|
|
#include <QtGui/QApplication>
|
|
|
|
|
#include <QtGui/QClipboard>
|
|
|
|
|
#include <QtCore/QDebug>
|
|
|
|
|
|
|
|
|
|
using namespace BINEditor;
|
|
|
|
|
|
|
|
|
|
static QByteArray calculateHexPattern(const QByteArray &pattern)
|
|
|
|
|
{
|
|
|
|
|
QByteArray result;
|
|
|
|
|
if (pattern.size() % 2 == 0) {
|
|
|
|
|
bool ok = true;
|
|
|
|
|
int i = 0;
|
|
|
|
|
while (i < pattern.size()) {
|
|
|
|
|
ushort s = pattern.mid(i, 2).toUShort(&ok, 16);
|
|
|
|
|
if (!ok) {
|
|
|
|
|
return QByteArray();
|
|
|
|
|
}
|
|
|
|
|
result.append(s);
|
|
|
|
|
i += 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BinEditor::BinEditor(QWidget *parent)
|
|
|
|
|
: QAbstractScrollArea(parent)
|
|
|
|
|
{
|
|
|
|
|
m_ieditor = 0;
|
2009-07-21 16:46:24 +02:00
|
|
|
m_inLazyMode = false;
|
|
|
|
|
m_blockSize = 4096;
|
2008-12-02 12:01:29 +01:00
|
|
|
init();
|
|
|
|
|
m_unmodifiedState = 0;
|
2009-07-21 16:46:24 +02:00
|
|
|
m_readOnly = false;
|
2008-12-02 12:01:29 +01:00
|
|
|
m_hexCursor = true;
|
|
|
|
|
m_cursorPosition = 0;
|
|
|
|
|
m_anchorPosition = 0;
|
|
|
|
|
m_lowNibble = false;
|
|
|
|
|
m_cursorVisible = false;
|
|
|
|
|
setFocusPolicy(Qt::WheelFocus);
|
|
|
|
|
m_addressString = QString(9, QLatin1Char(':'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BinEditor::~BinEditor()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::init()
|
|
|
|
|
{
|
|
|
|
|
QFontMetrics fm(fontMetrics());
|
|
|
|
|
m_margin = 4;
|
|
|
|
|
m_descent = fm.descent();
|
|
|
|
|
m_ascent = fm.ascent();
|
|
|
|
|
m_lineHeight = fm.lineSpacing();
|
|
|
|
|
m_charWidth = fm.width(QChar(QLatin1Char('M')));
|
|
|
|
|
m_columnWidth = 2 * m_charWidth + fm.width(QChar(QLatin1Char(' ')));
|
2009-07-21 16:46:24 +02:00
|
|
|
m_numLines = m_size / 16 + 1;
|
2008-12-02 12:01:29 +01:00
|
|
|
m_numVisibleLines = viewport()->height() / m_lineHeight;
|
|
|
|
|
m_textWidth = 16 * m_charWidth + m_charWidth;
|
|
|
|
|
int m_numberWidth = fm.width(QChar(QLatin1Char('9')));
|
|
|
|
|
m_labelWidth = 8 * m_numberWidth + 2 * m_charWidth;
|
|
|
|
|
|
|
|
|
|
int expectedCharWidth = m_columnWidth / 3;
|
|
|
|
|
const char *hex = "0123456789abcdef";
|
|
|
|
|
m_isMonospacedFont = true;
|
|
|
|
|
while (*hex) {
|
|
|
|
|
if (fm.width(QLatin1Char(*hex)) != expectedCharWidth) {
|
|
|
|
|
m_isMonospacedFont = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
++hex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
horizontalScrollBar()->setRange(0, 2 * m_margin + 16 * m_columnWidth
|
|
|
|
|
+ m_labelWidth + m_textWidth - viewport()->width());
|
|
|
|
|
horizontalScrollBar()->setPageStep(viewport()->width());
|
|
|
|
|
verticalScrollBar()->setRange(0, m_numLines - m_numVisibleLines);
|
|
|
|
|
verticalScrollBar()->setPageStep(m_numVisibleLines);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-07-21 16:46:24 +02:00
|
|
|
void BinEditor::addLazyData(int block, const QByteArray &data)
|
|
|
|
|
{
|
|
|
|
|
Q_ASSERT(m_inLazyMode);
|
|
|
|
|
Q_ASSERT(data.size() == m_blockSize);
|
|
|
|
|
m_lazyData.insert(block, data);
|
|
|
|
|
m_lazyRequests.remove(block);
|
|
|
|
|
viewport()->update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BinEditor::requestDataAt(int pos) const
|
|
|
|
|
{
|
|
|
|
|
if (!m_inLazyMode)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
int block = pos / m_blockSize;
|
|
|
|
|
QMap<int, QByteArray>::const_iterator it = m_lazyData.find(block);
|
|
|
|
|
if (it == m_lazyData.end()) {
|
|
|
|
|
if (!m_lazyRequests.contains(block)) {
|
|
|
|
|
m_lazyRequests.insert(block);
|
|
|
|
|
emit const_cast<BinEditor*>(this)->lazyDataRequested(block);
|
|
|
|
|
if (!m_lazyRequests.contains(block))
|
|
|
|
|
return true; // synchronous data source
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char BinEditor::dataAt(int pos) const
|
|
|
|
|
{
|
|
|
|
|
if (!m_inLazyMode)
|
|
|
|
|
return m_data.at(pos);
|
|
|
|
|
|
|
|
|
|
int block = pos / m_blockSize;
|
|
|
|
|
return m_lazyData.value(block, m_emptyBlock).at(pos - (block*m_blockSize));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::changeDataAt(int pos, char c)
|
|
|
|
|
{
|
|
|
|
|
if (!m_inLazyMode) {
|
|
|
|
|
m_data[pos] = c;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
int block = pos / m_blockSize;
|
|
|
|
|
if (m_lazyData.contains(block))
|
|
|
|
|
m_lazyData[block][pos - (block*m_blockSize)] = c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QByteArray BinEditor::dataMid(int from, int length) const
|
|
|
|
|
{
|
|
|
|
|
if (!m_inLazyMode)
|
|
|
|
|
return m_data.mid(from, length);
|
|
|
|
|
return QByteArray(length, '\0'); // ### TODO
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
void BinEditor::setFontSettings(const TextEditor::FontSettings &fs)
|
|
|
|
|
{
|
|
|
|
|
setFont(fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_TEXT)).font());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::setBlinkingCursorEnabled(bool enable)
|
|
|
|
|
{
|
|
|
|
|
if (enable && QApplication::cursorFlashTime() > 0)
|
|
|
|
|
m_cursorBlinkTimer.start(QApplication::cursorFlashTime() / 2, this);
|
|
|
|
|
else
|
|
|
|
|
m_cursorBlinkTimer.stop();
|
|
|
|
|
m_cursorVisible = enable;
|
|
|
|
|
updateLines();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::focusInEvent(QFocusEvent *)
|
|
|
|
|
{
|
|
|
|
|
setBlinkingCursorEnabled(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::focusOutEvent(QFocusEvent *)
|
|
|
|
|
{
|
|
|
|
|
setBlinkingCursorEnabled(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::timerEvent(QTimerEvent *e)
|
|
|
|
|
{
|
|
|
|
|
if (e->timerId() == m_autoScrollTimer.timerId()) {
|
|
|
|
|
QRect visible = viewport()->rect();
|
|
|
|
|
QPoint pos;
|
|
|
|
|
const QPoint globalPos = QCursor::pos();
|
|
|
|
|
pos = viewport()->mapFromGlobal(globalPos);
|
|
|
|
|
QMouseEvent ev(QEvent::MouseMove, pos, globalPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
|
|
|
|
|
mouseMoveEvent(&ev);
|
|
|
|
|
int deltaY = qMax(pos.y() - visible.top(), visible.bottom() - pos.y()) - visible.height();
|
|
|
|
|
int deltaX = qMax(pos.x() - visible.left(), visible.right() - pos.x()) - visible.width();
|
|
|
|
|
int delta = qMax(deltaX, deltaY);
|
|
|
|
|
if (delta >= 0) {
|
|
|
|
|
if (delta < 7)
|
|
|
|
|
delta = 7;
|
|
|
|
|
int timeout = 4900 / (delta * delta);
|
|
|
|
|
m_autoScrollTimer.start(timeout, this);
|
|
|
|
|
|
|
|
|
|
if (deltaY > 0)
|
|
|
|
|
verticalScrollBar()->triggerAction(pos.y() < visible.center().y() ?
|
|
|
|
|
QAbstractSlider::SliderSingleStepSub
|
|
|
|
|
: QAbstractSlider::SliderSingleStepAdd);
|
|
|
|
|
if (deltaX > 0)
|
|
|
|
|
horizontalScrollBar()->triggerAction(pos.x() < visible.center().x() ?
|
|
|
|
|
QAbstractSlider::SliderSingleStepSub
|
|
|
|
|
: QAbstractSlider::SliderSingleStepAdd);
|
|
|
|
|
}
|
|
|
|
|
} else if (e->timerId() == m_cursorBlinkTimer.timerId()) {
|
|
|
|
|
m_cursorVisible = !m_cursorVisible;
|
|
|
|
|
updateLines();
|
|
|
|
|
}
|
|
|
|
|
QAbstractScrollArea::timerEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void BinEditor::setModified(bool modified)
|
|
|
|
|
{
|
|
|
|
|
int unmodifiedState = modified ? -1 : m_undoStack.size();
|
|
|
|
|
if (unmodifiedState == m_unmodifiedState)
|
|
|
|
|
return;
|
|
|
|
|
m_unmodifiedState = unmodifiedState;
|
|
|
|
|
emit modificationChanged(m_undoStack.size() != m_unmodifiedState);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BinEditor::isModified() const
|
|
|
|
|
{
|
|
|
|
|
return (m_undoStack.size() != m_unmodifiedState);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-21 16:46:24 +02:00
|
|
|
void BinEditor::setReadOnly(bool readOnly)
|
|
|
|
|
{
|
|
|
|
|
m_readOnly = readOnly;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BinEditor::isReadOnly() const
|
|
|
|
|
{
|
|
|
|
|
return m_readOnly;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
void BinEditor::setData(const QByteArray &data)
|
|
|
|
|
{
|
2009-07-21 16:46:24 +02:00
|
|
|
m_inLazyMode = false;
|
|
|
|
|
m_lazyData.clear();
|
|
|
|
|
m_lazyRequests.clear();
|
2008-12-02 12:01:29 +01:00
|
|
|
m_data = data;
|
2009-07-21 16:46:24 +02:00
|
|
|
m_size = data.size();
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
m_unmodifiedState = 0;
|
|
|
|
|
m_undoStack.clear();
|
|
|
|
|
m_redoStack.clear();
|
2009-07-21 16:46:24 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
init();
|
2009-07-21 16:46:24 +02:00
|
|
|
m_cursorPosition = 0;
|
|
|
|
|
verticalScrollBar()->setValue(0);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-07-21 16:46:24 +02:00
|
|
|
emit cursorPositionChanged(m_cursorPosition);
|
2008-12-02 12:01:29 +01:00
|
|
|
viewport()->update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QByteArray BinEditor::data() const
|
|
|
|
|
{
|
|
|
|
|
return m_data;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-21 16:46:24 +02:00
|
|
|
bool BinEditor::applyModifications(QByteArray &data) const
|
|
|
|
|
{
|
|
|
|
|
if (!m_inLazyMode) {
|
|
|
|
|
data = m_data;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (data.size() != m_size)
|
|
|
|
|
return false;
|
|
|
|
|
for (QMap<int,QByteArray>::const_iterator it = m_lazyData.begin(); it != m_lazyData.end(); ++it) {
|
|
|
|
|
::memcpy(data.data() + it.key() * m_blockSize, it->constData(), m_blockSize);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::setLazyData(int cursorPosition, int size, int blockSize)
|
|
|
|
|
{
|
|
|
|
|
m_inLazyMode = true;
|
|
|
|
|
m_blockSize = blockSize;
|
|
|
|
|
m_emptyBlock = QByteArray(blockSize, '\0');
|
|
|
|
|
m_data.clear();
|
|
|
|
|
m_lazyData.clear();
|
|
|
|
|
m_lazyRequests.clear();
|
|
|
|
|
m_size = size;
|
|
|
|
|
|
|
|
|
|
m_unmodifiedState = 0;
|
|
|
|
|
m_undoStack.clear();
|
|
|
|
|
m_redoStack.clear();
|
|
|
|
|
|
|
|
|
|
init();
|
|
|
|
|
|
|
|
|
|
m_cursorPosition = cursorPosition;
|
|
|
|
|
verticalScrollBar()->setValue(m_cursorPosition / 16);
|
|
|
|
|
|
|
|
|
|
emit cursorPositionChanged(m_cursorPosition);
|
|
|
|
|
viewport()->update();
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
void BinEditor::resizeEvent(QResizeEvent *)
|
|
|
|
|
{
|
|
|
|
|
init();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::scrollContentsBy(int dx, int dy)
|
|
|
|
|
{
|
|
|
|
|
viewport()->scroll(isRightToLeft() ? -dx : dx, dy * m_lineHeight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::changeEvent(QEvent *e)
|
|
|
|
|
{
|
|
|
|
|
QAbstractScrollArea::changeEvent(e);
|
2008-12-09 11:07:24 +01:00
|
|
|
if (e->type() == QEvent::ActivationChange) {
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!isActiveWindow())
|
|
|
|
|
m_autoScrollTimer.stop();
|
|
|
|
|
}
|
|
|
|
|
init();
|
|
|
|
|
viewport()->update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void BinEditor::wheelEvent(QWheelEvent *e)
|
|
|
|
|
{
|
|
|
|
|
if (e->modifiers() & Qt::ControlModifier) {
|
|
|
|
|
const int delta = e->delta();
|
|
|
|
|
if (delta < 0)
|
|
|
|
|
zoomOut();
|
|
|
|
|
else if (delta > 0)
|
|
|
|
|
zoomIn();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QAbstractScrollArea::wheelEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QRect BinEditor::cursorRect() const
|
|
|
|
|
{
|
|
|
|
|
int topLine = verticalScrollBar()->value();
|
|
|
|
|
int line = m_cursorPosition / 16;
|
|
|
|
|
int y = (line - topLine) * m_lineHeight;
|
|
|
|
|
int xoffset = horizontalScrollBar()->value();
|
|
|
|
|
int column = m_cursorPosition % 16;
|
|
|
|
|
int x = m_hexCursor ?
|
|
|
|
|
(-xoffset + m_margin + m_labelWidth + column * m_columnWidth)
|
|
|
|
|
: (-xoffset + m_margin + m_labelWidth + 16 * m_columnWidth + m_charWidth + column * m_charWidth);
|
|
|
|
|
int w = m_hexCursor ? m_columnWidth : m_charWidth;
|
|
|
|
|
return QRect(x, y, w, m_lineHeight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int BinEditor::posAt(const QPoint &pos) const
|
|
|
|
|
{
|
|
|
|
|
int xoffset = horizontalScrollBar()->value();
|
|
|
|
|
int x = xoffset + pos.x() - m_margin - m_labelWidth;
|
|
|
|
|
int column = qMin(15, qMax(0,x) / m_columnWidth);
|
|
|
|
|
int topLine = verticalScrollBar()->value();
|
|
|
|
|
int line = pos.y() / m_lineHeight;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (x > 16 * m_columnWidth + m_charWidth/2) {
|
|
|
|
|
x -= 16 * m_columnWidth + m_charWidth;
|
|
|
|
|
for (column = 0; column < 15; ++column) {
|
|
|
|
|
int pos = (topLine + line) * 16 + column;
|
2009-07-21 16:46:24 +02:00
|
|
|
if (pos < 0 || pos >= m_size)
|
2008-12-02 12:01:29 +01:00
|
|
|
break;
|
2009-07-21 16:46:24 +02:00
|
|
|
QChar qc(QLatin1Char(dataAt(pos)));
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!qc.isPrint())
|
|
|
|
|
qc = 0xB7;
|
|
|
|
|
x -= fontMetrics().width(qc);
|
|
|
|
|
if (x <= 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-21 16:46:24 +02:00
|
|
|
return (qMin(m_size, qMin(m_numLines, topLine + line) * 16) + column);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BinEditor::inTextArea(const QPoint &pos) const
|
|
|
|
|
{
|
|
|
|
|
int xoffset = horizontalScrollBar()->value();
|
|
|
|
|
int x = xoffset + pos.x() - m_margin - m_labelWidth;
|
|
|
|
|
return (x > 16 * m_columnWidth + m_charWidth/2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void BinEditor::updateLines(int fromPosition, int toPosition)
|
|
|
|
|
{
|
|
|
|
|
if (fromPosition < 0)
|
|
|
|
|
fromPosition = m_cursorPosition;
|
|
|
|
|
if (toPosition < 0)
|
|
|
|
|
toPosition = fromPosition;
|
|
|
|
|
int topLine = verticalScrollBar()->value();
|
|
|
|
|
int firstLine = qMin(fromPosition, toPosition) / 16;
|
|
|
|
|
int lastLine = qMax(fromPosition, toPosition) / 16;
|
|
|
|
|
int y = (firstLine - topLine) * m_lineHeight;
|
|
|
|
|
int h = (lastLine - firstLine + 1 ) * m_lineHeight;
|
|
|
|
|
|
|
|
|
|
viewport()->update(0, y, viewport()->width(), h);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int BinEditor::find(const QByteArray &pattern, int from, QTextDocument::FindFlags findFlags)
|
|
|
|
|
{
|
|
|
|
|
if (pattern.isEmpty())
|
|
|
|
|
return false;
|
|
|
|
|
bool backwards = (findFlags & QTextDocument::FindBackward);
|
|
|
|
|
int found = backwards ? m_data.lastIndexOf(pattern, from)
|
|
|
|
|
: m_data.indexOf(pattern, from);
|
|
|
|
|
int foundHex = -1;
|
|
|
|
|
QByteArray hexPattern = calculateHexPattern(pattern);
|
|
|
|
|
if (!hexPattern.isEmpty()) {
|
|
|
|
|
foundHex = backwards ? m_data.lastIndexOf(hexPattern, from)
|
|
|
|
|
: m_data.indexOf(hexPattern, from);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int pos = (found >= 0 && (foundHex < 0 || found < foundHex)) ? found : foundHex;
|
|
|
|
|
if (pos >= 0) {
|
|
|
|
|
setCursorPosition(pos);
|
|
|
|
|
setCursorPosition(pos + (found == pos ? pattern.size() : hexPattern.size()), KeepAnchor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int BinEditor::findPattern(const QByteArray &data, int from, int offset, int *match)
|
|
|
|
|
{
|
|
|
|
|
if (m_searchPattern.isEmpty())
|
|
|
|
|
return -1;
|
|
|
|
|
int normal = m_searchPattern.isEmpty()? -1 : data.indexOf(m_searchPattern, from - offset);
|
|
|
|
|
int hex = m_searchPatternHex.isEmpty()? -1 : data.indexOf(m_searchPatternHex, from - offset);
|
|
|
|
|
|
|
|
|
|
if (normal >= 0 && (hex < 0 || normal < hex)) {
|
|
|
|
|
if (match)
|
|
|
|
|
*match = m_searchPattern.length();
|
|
|
|
|
return normal + offset;
|
|
|
|
|
}
|
|
|
|
|
if (hex >= 0) {
|
|
|
|
|
if (match)
|
|
|
|
|
*match = m_searchPatternHex.length();
|
|
|
|
|
return hex + offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void BinEditor::drawItems(QPainter *painter, int x, int y, const QString &itemString)
|
|
|
|
|
{
|
|
|
|
|
if (m_isMonospacedFont) {
|
|
|
|
|
painter->drawText(x, y, itemString);
|
|
|
|
|
} else {
|
|
|
|
|
for (int i = 0; i < 16; ++i)
|
|
|
|
|
painter->drawText(x + i*m_columnWidth, y, itemString.mid(i*3, 2));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString BinEditor::addressString(uint address)
|
|
|
|
|
{
|
|
|
|
|
QChar *addressStringData = m_addressString.data();
|
|
|
|
|
const char *hex = "0123456789abcdef";
|
|
|
|
|
for (int h = 0; h < 4; ++h) {
|
|
|
|
|
int shift = 4*(7-h);
|
|
|
|
|
addressStringData[h] = hex[(address & (0xf<<shift))>>shift];
|
|
|
|
|
}
|
|
|
|
|
for (int h = 4; h < 8; ++h) {
|
|
|
|
|
int shift = 4*(7-h);
|
|
|
|
|
addressStringData[h+1] = hex[(address & (0xf<<shift))>>shift];
|
|
|
|
|
}
|
|
|
|
|
return m_addressString;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::paintEvent(QPaintEvent *e)
|
|
|
|
|
{
|
|
|
|
|
QPainter painter(viewport());
|
|
|
|
|
int topLine = verticalScrollBar()->value();
|
|
|
|
|
int xoffset = horizontalScrollBar()->value();
|
|
|
|
|
painter.drawLine(-xoffset + m_margin + m_labelWidth - m_charWidth/2, 0,
|
|
|
|
|
-xoffset + m_margin + m_labelWidth - m_charWidth/2, viewport()->height());
|
|
|
|
|
painter.drawLine(-xoffset + m_margin + m_labelWidth + 16 * m_columnWidth + m_charWidth/2, 0,
|
|
|
|
|
-xoffset + m_margin + m_labelWidth + 16 * m_columnWidth + m_charWidth/2, viewport()->height());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int viewport_height = viewport()->height();
|
|
|
|
|
QBrush alternate_base = palette().alternateBase();
|
|
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
|
|
|
int bg_x = -xoffset + m_margin + (2 * i + 1) * m_columnWidth + m_labelWidth;
|
|
|
|
|
QRect r(bg_x - m_charWidth/2, 0, m_columnWidth, viewport_height);
|
|
|
|
|
painter.fillRect(e->rect() & r, palette().alternateBase());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int matchLength = 0;
|
|
|
|
|
|
|
|
|
|
QByteArray patternData;
|
|
|
|
|
int patternOffset = qMax(0, topLine*16 - m_searchPattern.size());
|
|
|
|
|
if (!m_searchPattern.isEmpty())
|
2009-07-21 16:46:24 +02:00
|
|
|
patternData = dataMid(patternOffset, m_numVisibleLines * 16 + (topLine*16 - patternOffset));
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
int foundPatternAt = findPattern(patternData, patternOffset, patternOffset, &matchLength);
|
|
|
|
|
|
|
|
|
|
int selStart = qMin(m_cursorPosition, m_anchorPosition);
|
|
|
|
|
int selEnd = qMax(m_cursorPosition, m_anchorPosition);
|
|
|
|
|
|
2009-07-21 16:46:24 +02:00
|
|
|
QString itemString(16*3, QLatin1Char(' '));
|
2008-12-02 12:01:29 +01:00
|
|
|
QChar *itemStringData = itemString.data();
|
|
|
|
|
const char *hex = "0123456789abcdef";
|
|
|
|
|
|
|
|
|
|
painter.setPen(palette().text().color());
|
|
|
|
|
for (int i = 0; i <= m_numVisibleLines; ++i) {
|
|
|
|
|
int line = topLine + i;
|
|
|
|
|
if (line >= m_numLines)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
int y = i * m_lineHeight + m_ascent;
|
|
|
|
|
if (y - m_ascent > e->rect().bottom())
|
|
|
|
|
break;
|
|
|
|
|
if (y + m_descent < e->rect().top())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
painter.drawText(-xoffset, i * m_lineHeight + m_ascent, addressString(((uint) line) * 16));
|
2009-07-21 16:46:24 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
int cursor = -1;
|
2009-07-21 16:46:24 +02:00
|
|
|
if (line * 16 <= m_cursorPosition && m_cursorPosition < line * 16 + 16)
|
|
|
|
|
cursor = m_cursorPosition - line * 16;
|
|
|
|
|
|
|
|
|
|
bool hasData = requestDataAt(line * 16);
|
|
|
|
|
|
|
|
|
|
QString printable;
|
|
|
|
|
|
|
|
|
|
if (hasData) {
|
|
|
|
|
for (int c = 0; c < 16; ++c) {
|
|
|
|
|
int pos = line * 16 + c;
|
|
|
|
|
if (pos >= m_size)
|
|
|
|
|
break;
|
|
|
|
|
QChar qc(QLatin1Char(dataAt(pos)));
|
|
|
|
|
if (qc.unicode() >= 127 || !qc.isPrint())
|
|
|
|
|
qc = 0xB7;
|
|
|
|
|
printable += qc;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
printable = QString(16, QLatin1Char(' '));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QRect selectionRect;
|
|
|
|
|
QRect printableSelectionRect;
|
|
|
|
|
|
|
|
|
|
bool isFullySelected = (selStart < selEnd && selStart <= line*16 && (line+1)*16 <= selEnd);
|
|
|
|
|
|
2009-07-21 16:46:24 +02:00
|
|
|
if (hasData) {
|
|
|
|
|
for (int c = 0; c < 16; ++c) {
|
|
|
|
|
int pos = line * 16 + c;
|
|
|
|
|
if (pos >= m_size) {
|
|
|
|
|
while (c < 16) {
|
|
|
|
|
itemStringData[c*3] = itemStringData[c*3+1] = ' ';
|
|
|
|
|
++c;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-07-21 16:46:24 +02:00
|
|
|
if (foundPatternAt >= 0 && pos >= foundPatternAt + matchLength)
|
|
|
|
|
foundPatternAt = findPattern(patternData, foundPatternAt + matchLength, patternOffset, &matchLength);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
|
2009-07-21 16:46:24 +02:00
|
|
|
uchar value = (uchar)dataAt(pos);
|
|
|
|
|
itemStringData[c*3] = hex[value >> 4];
|
|
|
|
|
itemStringData[c*3+1] = hex[value & 0xf];
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-07-21 16:46:24 +02:00
|
|
|
int item_x = -xoffset + m_margin + c * m_columnWidth + m_labelWidth;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-07-21 16:46:24 +02:00
|
|
|
if (foundPatternAt >= 0 && pos >= foundPatternAt && pos < foundPatternAt + matchLength) {
|
|
|
|
|
painter.fillRect(item_x, y-m_ascent, m_columnWidth, m_lineHeight, QColor(0xffef0b));
|
|
|
|
|
int printable_item_x = -xoffset + m_margin + m_labelWidth + 16 * m_columnWidth + m_charWidth
|
|
|
|
|
+ painter.fontMetrics().width( printable.left(c));
|
|
|
|
|
painter.fillRect(printable_item_x, y-m_ascent,
|
|
|
|
|
painter.fontMetrics().width(printable.at(c)),
|
|
|
|
|
m_lineHeight, QColor(0xffef0b));
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-07-21 16:46:24 +02:00
|
|
|
if (selStart < selEnd && !isFullySelected && pos >= selStart && pos < selEnd) {
|
|
|
|
|
selectionRect |= QRect(item_x, y-m_ascent, m_columnWidth, m_lineHeight);
|
|
|
|
|
int printable_item_x = -xoffset + m_margin + m_labelWidth + 16 * m_columnWidth + m_charWidth
|
|
|
|
|
+ painter.fontMetrics().width( printable.left(c));
|
|
|
|
|
printableSelectionRect |= QRect(printable_item_x, y-m_ascent,
|
|
|
|
|
painter.fontMetrics().width(printable.at(c)),
|
|
|
|
|
m_lineHeight);
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int x = -xoffset + m_margin + m_labelWidth;
|
|
|
|
|
|
|
|
|
|
if (isFullySelected) {
|
|
|
|
|
painter.save();
|
|
|
|
|
painter.fillRect(x, y-m_ascent, 16*m_columnWidth, m_lineHeight, palette().highlight());
|
|
|
|
|
painter.setPen(palette().highlightedText().color());
|
|
|
|
|
drawItems(&painter, x, y, itemString);
|
|
|
|
|
painter.restore();
|
|
|
|
|
} else {
|
|
|
|
|
drawItems(&painter, x, y, itemString);
|
|
|
|
|
if (!selectionRect.isEmpty()) {
|
|
|
|
|
painter.save();
|
|
|
|
|
painter.fillRect(selectionRect, palette().highlight());
|
|
|
|
|
painter.setPen(palette().highlightedText().color());
|
|
|
|
|
painter.setClipRect(selectionRect);
|
|
|
|
|
drawItems(&painter, x, y, itemString);
|
|
|
|
|
painter.restore();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (cursor >= 0) {
|
|
|
|
|
int w = painter.fontMetrics().boundingRect(itemString.mid(cursor*3, 2)).width();
|
|
|
|
|
QRect cursorRect(x + cursor * m_columnWidth, y - m_ascent, w + 1, m_lineHeight);
|
|
|
|
|
painter.save();
|
|
|
|
|
painter.setPen(Qt::red);
|
|
|
|
|
painter.drawRect(cursorRect.adjusted(0, 0, 0, -1));
|
|
|
|
|
painter.restore();
|
|
|
|
|
if (m_hexCursor && m_cursorVisible) {
|
|
|
|
|
if (m_lowNibble)
|
|
|
|
|
cursorRect.adjust(painter.fontMetrics().width(itemString.left(1)), 0, 0, 0);
|
|
|
|
|
painter.fillRect(cursorRect, Qt::red);
|
|
|
|
|
painter.save();
|
|
|
|
|
painter.setClipRect(cursorRect);
|
|
|
|
|
painter.setPen(Qt::white);
|
|
|
|
|
drawItems(&painter, x, y, itemString);
|
|
|
|
|
painter.restore();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int text_x = -xoffset + m_margin + m_labelWidth + 16 * m_columnWidth + m_charWidth;
|
|
|
|
|
|
|
|
|
|
if (isFullySelected) {
|
|
|
|
|
painter.save();
|
|
|
|
|
painter.fillRect(text_x, y-m_ascent, painter.fontMetrics().width(printable), m_lineHeight,
|
|
|
|
|
palette().highlight());
|
|
|
|
|
painter.setPen(palette().highlightedText().color());
|
|
|
|
|
painter.drawText(text_x, y, printable);
|
|
|
|
|
painter.restore();
|
|
|
|
|
}else {
|
|
|
|
|
painter.drawText(text_x, y, printable);
|
|
|
|
|
if (!printableSelectionRect.isEmpty()) {
|
|
|
|
|
painter.save();
|
|
|
|
|
painter.fillRect(printableSelectionRect, palette().highlight());
|
|
|
|
|
painter.setPen(palette().highlightedText().color());
|
|
|
|
|
painter.setClipRect(printableSelectionRect);
|
|
|
|
|
painter.drawText(text_x, y, printable);
|
|
|
|
|
painter.restore();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cursor >= 0) {
|
|
|
|
|
QRect cursorRect(text_x + painter.fontMetrics().width(printable.left(cursor)),
|
|
|
|
|
y-m_ascent,
|
|
|
|
|
painter.fontMetrics().width(printable.at(cursor)),
|
|
|
|
|
m_lineHeight);
|
|
|
|
|
painter.save();
|
|
|
|
|
if (m_hexCursor || !m_cursorVisible) {
|
|
|
|
|
painter.setPen(Qt::red);
|
|
|
|
|
painter.drawRect(cursorRect.adjusted(0, 0, 0, -1));
|
|
|
|
|
} else {
|
|
|
|
|
painter.setClipRect(cursorRect);
|
|
|
|
|
painter.fillRect(cursorRect, Qt::red);
|
|
|
|
|
painter.setPen(Qt::white);
|
|
|
|
|
painter.drawText(text_x, y, printable);
|
|
|
|
|
}
|
|
|
|
|
painter.restore();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int BinEditor::cursorPosition() const
|
|
|
|
|
{
|
|
|
|
|
return m_cursorPosition;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::setCursorPosition(int pos, MoveMode moveMode)
|
|
|
|
|
{
|
2009-07-21 16:46:24 +02:00
|
|
|
pos = qMin(m_size-1, qMax(0, pos));
|
2008-12-02 12:01:29 +01:00
|
|
|
if (pos == m_cursorPosition
|
|
|
|
|
&& (m_anchorPosition == m_cursorPosition || moveMode == KeepAnchor)
|
|
|
|
|
&& !m_lowNibble)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
int oldCursorPosition = m_cursorPosition;
|
|
|
|
|
|
|
|
|
|
bool hasSelection = m_anchorPosition != m_cursorPosition;
|
|
|
|
|
m_lowNibble = false;
|
|
|
|
|
if (!hasSelection)
|
|
|
|
|
updateLines();
|
|
|
|
|
m_cursorPosition = pos;
|
|
|
|
|
if (moveMode == MoveAnchor) {
|
|
|
|
|
if (hasSelection)
|
|
|
|
|
updateLines(m_anchorPosition, oldCursorPosition);
|
|
|
|
|
m_anchorPosition = m_cursorPosition;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hasSelection = m_anchorPosition != m_cursorPosition;
|
|
|
|
|
updateLines(hasSelection ? oldCursorPosition : m_cursorPosition, m_cursorPosition);
|
|
|
|
|
ensureCursorVisible();
|
|
|
|
|
if (hasSelection != (m_anchorPosition != m_anchorPosition))
|
|
|
|
|
emit copyAvailable(m_anchorPosition != m_cursorPosition);
|
|
|
|
|
emit cursorPositionChanged(m_cursorPosition);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void BinEditor::ensureCursorVisible()
|
|
|
|
|
{
|
|
|
|
|
QRect cr = cursorRect();
|
|
|
|
|
QRect vr = viewport()->rect();
|
|
|
|
|
if (!vr.contains(cr)) {
|
|
|
|
|
if (cr.top() < vr.top())
|
|
|
|
|
verticalScrollBar()->setValue(m_cursorPosition / 16);
|
|
|
|
|
else if (cr.bottom() > vr.bottom())
|
|
|
|
|
verticalScrollBar()->setValue(m_cursorPosition / 16 - m_numVisibleLines + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::mousePressEvent(QMouseEvent *e)
|
|
|
|
|
{
|
|
|
|
|
if (e->button() != Qt::LeftButton)
|
|
|
|
|
return;
|
|
|
|
|
setCursorPosition(posAt(e->pos()));
|
|
|
|
|
setBlinkingCursorEnabled(true);
|
|
|
|
|
if (m_hexCursor == inTextArea(e->pos())) {
|
|
|
|
|
m_hexCursor = !m_hexCursor;
|
|
|
|
|
updateLines();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::mouseMoveEvent(QMouseEvent *e)
|
|
|
|
|
{
|
|
|
|
|
if (!(e->buttons() & Qt::LeftButton))
|
|
|
|
|
return;
|
|
|
|
|
setCursorPosition(posAt(e->pos()), KeepAnchor);
|
|
|
|
|
if (m_hexCursor == inTextArea(e->pos())) {
|
|
|
|
|
m_hexCursor = !m_hexCursor;
|
|
|
|
|
updateLines();
|
|
|
|
|
}
|
|
|
|
|
QRect visible = viewport()->rect();
|
|
|
|
|
if (visible.contains(e->pos()))
|
|
|
|
|
m_autoScrollTimer.stop();
|
|
|
|
|
else if (!m_autoScrollTimer.isActive())
|
|
|
|
|
m_autoScrollTimer.start(100, this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::mouseReleaseEvent(QMouseEvent *)
|
|
|
|
|
{
|
|
|
|
|
if (m_autoScrollTimer.isActive()) {
|
|
|
|
|
m_autoScrollTimer.stop();
|
|
|
|
|
ensureCursorVisible();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::selectAll()
|
|
|
|
|
{
|
|
|
|
|
setCursorPosition(0);
|
2009-07-21 16:46:24 +02:00
|
|
|
setCursorPosition(m_size-1, KeepAnchor);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::clear()
|
|
|
|
|
{
|
|
|
|
|
setData(QByteArray());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BinEditor::event(QEvent *e) {
|
|
|
|
|
if (e->type() == QEvent::KeyPress) {
|
|
|
|
|
switch (static_cast<QKeyEvent*>(e)->key()) {
|
|
|
|
|
case Qt::Key_Tab:
|
|
|
|
|
case Qt::Key_Backtab:
|
|
|
|
|
m_hexCursor = !m_hexCursor;
|
|
|
|
|
setBlinkingCursorEnabled(true);
|
|
|
|
|
ensureCursorVisible();
|
|
|
|
|
e->accept();
|
|
|
|
|
return true;
|
|
|
|
|
default:;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return QAbstractScrollArea::event(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::keyPressEvent(QKeyEvent *e)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (e == QKeySequence::SelectAll) {
|
|
|
|
|
e->accept();
|
|
|
|
|
selectAll();
|
|
|
|
|
return;
|
|
|
|
|
} else if (e == QKeySequence::Copy) {
|
|
|
|
|
e->accept();
|
|
|
|
|
copy();
|
|
|
|
|
return;
|
|
|
|
|
} else if (e == QKeySequence::Undo) {
|
|
|
|
|
e->accept();
|
|
|
|
|
undo();
|
|
|
|
|
return;
|
|
|
|
|
} else if (e == QKeySequence::Redo) {
|
|
|
|
|
e->accept();
|
|
|
|
|
redo();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MoveMode moveMode = e->modifiers() & Qt::ShiftModifier ? KeepAnchor : MoveAnchor;
|
|
|
|
|
switch (e->key()) {
|
|
|
|
|
case Qt::Key_Up:
|
|
|
|
|
setCursorPosition(m_cursorPosition - 16, moveMode);
|
|
|
|
|
break;
|
|
|
|
|
case Qt::Key_Down:
|
|
|
|
|
setCursorPosition(m_cursorPosition + 16, moveMode);
|
|
|
|
|
break;
|
|
|
|
|
case Qt::Key_Right:
|
|
|
|
|
setCursorPosition(m_cursorPosition + 1, moveMode);
|
|
|
|
|
break;
|
|
|
|
|
case Qt::Key_Left:
|
|
|
|
|
setCursorPosition(m_cursorPosition - 1, moveMode);
|
|
|
|
|
break;
|
|
|
|
|
case Qt::Key_PageUp:
|
|
|
|
|
case Qt::Key_PageDown: {
|
|
|
|
|
int line = qMax(0, m_cursorPosition / 16 - verticalScrollBar()->value());
|
|
|
|
|
verticalScrollBar()->triggerAction(e->key() == Qt::Key_PageUp ?
|
|
|
|
|
QScrollBar::SliderPageStepSub : QScrollBar::SliderPageStepAdd);
|
|
|
|
|
setCursorPosition((verticalScrollBar()->value() + line) * 16 + m_cursorPosition % 16, moveMode);
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case Qt::Key_Home:
|
|
|
|
|
setCursorPosition((e->modifiers() & Qt::ControlModifier) ?
|
|
|
|
|
0 : (m_cursorPosition/16 * 16), moveMode);
|
|
|
|
|
break;
|
|
|
|
|
case Qt::Key_End:
|
|
|
|
|
setCursorPosition((e->modifiers() & Qt::ControlModifier) ?
|
2009-07-21 16:46:24 +02:00
|
|
|
(m_size-1) : (m_cursorPosition/16 * 16 + 15), moveMode);
|
2008-12-02 12:01:29 +01:00
|
|
|
break;
|
|
|
|
|
|
2009-07-21 16:46:24 +02:00
|
|
|
default:
|
|
|
|
|
if (m_readOnly)
|
|
|
|
|
break;
|
|
|
|
|
{
|
2008-12-02 12:01:29 +01:00
|
|
|
QString text = e->text();
|
|
|
|
|
for (int i = 0; i < text.length(); ++i) {
|
|
|
|
|
QChar c = text.at(i);
|
|
|
|
|
if (m_hexCursor) {
|
|
|
|
|
c = c.toLower();
|
|
|
|
|
int nibble = -1;
|
|
|
|
|
if (c.unicode() >= 'a' && c.unicode() <= 'f')
|
|
|
|
|
nibble = c.unicode() - 'a' + 10;
|
|
|
|
|
else if (c.unicode() >= '0' && c.unicode() <= '9')
|
|
|
|
|
nibble = c.unicode() - '0';
|
|
|
|
|
if (nibble < 0)
|
|
|
|
|
continue;
|
|
|
|
|
if (m_lowNibble) {
|
2009-07-21 16:46:24 +02:00
|
|
|
changeData(m_cursorPosition, nibble + (dataAt(m_cursorPosition) & 0xf0));
|
2008-12-02 12:01:29 +01:00
|
|
|
m_lowNibble = false;
|
|
|
|
|
setCursorPosition(m_cursorPosition + 1);
|
|
|
|
|
} else {
|
2009-07-21 16:46:24 +02:00
|
|
|
changeData(m_cursorPosition, (nibble << 4) + (dataAt(m_cursorPosition) & 0x0f), true);
|
2008-12-02 12:01:29 +01:00
|
|
|
m_lowNibble = true;
|
|
|
|
|
updateLines();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (c.unicode() >= 128 || !c.isPrint())
|
|
|
|
|
continue;
|
|
|
|
|
changeData(m_cursorPosition, c.unicode(), m_cursorPosition + 1);
|
|
|
|
|
setCursorPosition(m_cursorPosition + 1);
|
|
|
|
|
}
|
|
|
|
|
setBlinkingCursorEnabled(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
e->accept();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::zoomIn(int range)
|
|
|
|
|
{
|
|
|
|
|
QFont f = font();
|
|
|
|
|
const int newSize = f.pointSize() + range;
|
|
|
|
|
if (newSize <= 0)
|
|
|
|
|
return;
|
|
|
|
|
f.setPointSize(newSize);
|
|
|
|
|
setFont(f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::zoomOut(int range)
|
|
|
|
|
{
|
|
|
|
|
zoomIn(-range);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::copy()
|
|
|
|
|
{
|
|
|
|
|
int selStart = qMin(m_cursorPosition, m_anchorPosition);
|
|
|
|
|
int selEnd = qMax(m_cursorPosition, m_anchorPosition);
|
|
|
|
|
if (selStart < selEnd)
|
2009-07-21 16:46:24 +02:00
|
|
|
QApplication::clipboard()->setText(QString::fromLatin1(dataMid(selStart, selEnd - selStart)));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::highlightSearchResults(const QByteArray &pattern, QTextDocument::FindFlags /*findFlags*/)
|
|
|
|
|
{
|
|
|
|
|
if (m_searchPattern == pattern)
|
|
|
|
|
return;
|
|
|
|
|
m_searchPattern = pattern;
|
|
|
|
|
m_searchPatternHex = calculateHexPattern(pattern);
|
|
|
|
|
viewport()->update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void BinEditor::changeData(int position, uchar character, bool highNibble)
|
|
|
|
|
{
|
2009-07-21 16:46:24 +02:00
|
|
|
if (!requestDataAt(position))
|
|
|
|
|
return;
|
2008-12-02 12:01:29 +01:00
|
|
|
m_redoStack.clear();
|
|
|
|
|
if (m_unmodifiedState > m_undoStack.size())
|
|
|
|
|
m_unmodifiedState = -1;
|
|
|
|
|
BinEditorEditCommand cmd;
|
|
|
|
|
cmd.position = position;
|
2009-07-21 16:46:24 +02:00
|
|
|
cmd.character = (uchar) dataAt(position);
|
2008-12-02 12:01:29 +01:00
|
|
|
cmd.highNibble = highNibble;
|
|
|
|
|
|
|
|
|
|
if (!highNibble && !m_undoStack.isEmpty() && m_undoStack.top().position == position && m_undoStack.top().highNibble) {
|
|
|
|
|
// compress
|
|
|
|
|
cmd.character = m_undoStack.top().character;
|
|
|
|
|
m_undoStack.pop();
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-21 16:46:24 +02:00
|
|
|
changeDataAt(position, (char) character);
|
2008-12-02 12:01:29 +01:00
|
|
|
bool emitModificationChanged = (m_undoStack.size() == m_unmodifiedState);
|
|
|
|
|
m_undoStack.push(cmd);
|
|
|
|
|
if (emitModificationChanged) {
|
|
|
|
|
emit modificationChanged(m_undoStack.size() != m_unmodifiedState);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_undoStack.size() == 1)
|
|
|
|
|
emit undoAvailable(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void BinEditor::undo()
|
|
|
|
|
{
|
|
|
|
|
if (m_undoStack.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
bool emitModificationChanged = (m_undoStack.size() == m_unmodifiedState);
|
|
|
|
|
BinEditorEditCommand cmd = m_undoStack.pop();
|
|
|
|
|
emitModificationChanged |= (m_undoStack.size() == m_unmodifiedState);
|
2009-07-21 16:46:24 +02:00
|
|
|
uchar c = dataAt(cmd.position);
|
|
|
|
|
changeDataAt(cmd.position, (char)cmd.character);
|
2008-12-02 12:01:29 +01:00
|
|
|
cmd.character = c;
|
|
|
|
|
m_redoStack.push(cmd);
|
|
|
|
|
setCursorPosition(cmd.position);
|
|
|
|
|
if (emitModificationChanged)
|
|
|
|
|
emit modificationChanged(m_undoStack.size() != m_unmodifiedState);
|
|
|
|
|
if (!m_undoStack.size())
|
|
|
|
|
emit undoAvailable(false);
|
|
|
|
|
if (m_redoStack.size() == 1)
|
|
|
|
|
emit redoAvailable(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BinEditor::redo()
|
|
|
|
|
{
|
|
|
|
|
if (m_redoStack.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
BinEditorEditCommand cmd = m_redoStack.pop();
|
2009-07-21 16:46:24 +02:00
|
|
|
uchar c = dataAt(cmd.position);
|
|
|
|
|
changeDataAt(cmd.position, (char)cmd.character);
|
2008-12-02 12:01:29 +01:00
|
|
|
cmd.character = c;
|
|
|
|
|
bool emitModificationChanged = (m_undoStack.size() == m_unmodifiedState);
|
|
|
|
|
m_undoStack.push(cmd);
|
|
|
|
|
setCursorPosition(cmd.position + 1);
|
|
|
|
|
if (emitModificationChanged)
|
|
|
|
|
emit modificationChanged(m_undoStack.size() != m_unmodifiedState);
|
|
|
|
|
if (m_undoStack.size() == 1)
|
|
|
|
|
emit undoAvailable(true);
|
|
|
|
|
if (!m_redoStack.size())
|
|
|
|
|
emit redoAvailable(false);
|
|
|
|
|
}
|