mirror of https://github.com/bsnes-emu/bsnes.git
308 lines
8.3 KiB
C++
308 lines
8.3 KiB
C++
#if defined(Hiro_HexEdit)
|
|
|
|
namespace hiro {
|
|
|
|
auto pHexEdit::construct() -> void {
|
|
qtWidget = qtHexEdit = new QtHexEdit(*this);
|
|
|
|
qtHexEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
qtHexEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
qtHexEdit->setTextInteractionFlags(Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse);
|
|
|
|
qtLayout = new QHBoxLayout;
|
|
qtLayout->setAlignment(Qt::AlignRight);
|
|
qtLayout->setMargin(0);
|
|
qtLayout->setSpacing(0);
|
|
qtHexEdit->setLayout(qtLayout);
|
|
|
|
qtScrollBar = new QtHexEditScrollBar(*this);
|
|
qtScrollBar->setSingleStep(1);
|
|
qtLayout->addWidget(qtScrollBar);
|
|
|
|
qtScrollBar->connect(qtScrollBar, SIGNAL(actionTriggered(int)), SLOT(onScroll()));
|
|
|
|
pWidget::construct();
|
|
_setState();
|
|
}
|
|
|
|
auto pHexEdit::destruct() -> void {
|
|
delete qtScrollBar;
|
|
delete qtLayout;
|
|
delete qtHexEdit;
|
|
qtWidget = qtHexEdit = nullptr;
|
|
qtLayout = nullptr;
|
|
qtScrollBar = nullptr;
|
|
}
|
|
|
|
auto pHexEdit::setAddress(unsigned address) -> void {
|
|
_setState();
|
|
}
|
|
|
|
auto pHexEdit::setBackgroundColor(Color color) -> void {
|
|
_setState();
|
|
}
|
|
|
|
auto pHexEdit::setColumns(unsigned columns) -> void {
|
|
_setState();
|
|
}
|
|
|
|
auto pHexEdit::setForegroundColor(Color color) -> void {
|
|
_setState();
|
|
}
|
|
|
|
auto pHexEdit::setLength(unsigned length) -> void {
|
|
_setState();
|
|
}
|
|
|
|
auto pHexEdit::setRows(unsigned rows) -> void {
|
|
_setState();
|
|
}
|
|
|
|
auto pHexEdit::update() -> void {
|
|
if(!state().onRead) {
|
|
qtHexEdit->setPlainText("");
|
|
return;
|
|
}
|
|
|
|
unsigned cursorPosition = qtHexEdit->textCursor().position();
|
|
|
|
string output;
|
|
unsigned address = state().address;
|
|
for(unsigned row = 0; row < state().rows; row++) {
|
|
output.append(hex(address, 8L));
|
|
output.append(" ");
|
|
|
|
string hexdata;
|
|
string ansidata = " ";
|
|
|
|
for(unsigned column = 0; column < state().columns; column++) {
|
|
if(address < state().length) {
|
|
uint8_t data = self().doRead(address++);
|
|
hexdata.append(hex(data, 2L));
|
|
hexdata.append(" ");
|
|
ansidata.append(data >= 0x20 && data <= 0x7e ? (char)data : '.');
|
|
} else {
|
|
hexdata.append(" ");
|
|
ansidata.append(" ");
|
|
}
|
|
}
|
|
|
|
output.append(hexdata);
|
|
output.append(ansidata);
|
|
if(address >= state().length) break;
|
|
if(row != state().rows - 1) output.append("\n");
|
|
}
|
|
|
|
qtHexEdit->setPlainText(QString::fromUtf8(output));
|
|
QTextCursor cursor = qtHexEdit->textCursor();
|
|
cursor.setPosition(cursorPosition);
|
|
qtHexEdit->setTextCursor(cursor);
|
|
}
|
|
|
|
auto pHexEdit::_keyPressEvent(QKeyEvent* event) -> void {
|
|
if(!state().onRead) return;
|
|
|
|
//allow Ctrl+C (copy)
|
|
if(event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier) {
|
|
qtHexEdit->keyPressEventAcknowledge(event);
|
|
return;
|
|
}
|
|
|
|
//disallow other text operations (cut, paste, etc)
|
|
if(event->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier)) return;
|
|
|
|
QTextCursor cursor = qtHexEdit->textCursor();
|
|
signed lineWidth = 10 + (state().columns * 3) + 1 + state().columns + 1;
|
|
signed cursorY = cursor.position() / lineWidth;
|
|
signed cursorX = cursor.position() % lineWidth;
|
|
|
|
unsigned nibble = 0;
|
|
switch(event->key()) {
|
|
default: return;
|
|
|
|
case Qt::Key_Left:
|
|
if(cursorX > 0) {
|
|
cursor.setPosition(cursor.position() - 1);
|
|
qtHexEdit->setTextCursor(cursor);
|
|
}
|
|
return;
|
|
|
|
case Qt::Key_Right:
|
|
if(cursorX < lineWidth - 1) {
|
|
cursor.setPosition(cursor.position() + 1);
|
|
qtHexEdit->setTextCursor(cursor);
|
|
}
|
|
return;
|
|
|
|
case Qt::Key_Home:
|
|
cursor.setPosition(cursorY * lineWidth + 10);
|
|
qtHexEdit->setTextCursor(cursor);
|
|
return;
|
|
|
|
case Qt::Key_End:
|
|
cursor.setPosition(cursorY * lineWidth + 57);
|
|
qtHexEdit->setTextCursor(cursor);
|
|
return;
|
|
|
|
case Qt::Key_Up:
|
|
if(cursorY > 0) {
|
|
cursor.setPosition(cursor.position() - lineWidth);
|
|
qtHexEdit->setTextCursor(cursor);
|
|
} else {
|
|
_scrollTo(qtScrollBar->sliderPosition() - 1);
|
|
}
|
|
return;
|
|
|
|
case Qt::Key_Down:
|
|
if(cursorY >= _rows() - 1) {
|
|
//cannot scroll down further
|
|
} else if(cursorY < state().rows - 1) {
|
|
cursor.setPosition(cursor.position() + lineWidth);
|
|
qtHexEdit->setTextCursor(cursor);
|
|
} else {
|
|
_scrollTo(qtScrollBar->sliderPosition() + 1);
|
|
}
|
|
return;
|
|
|
|
case Qt::Key_PageUp:
|
|
_scrollTo(qtScrollBar->sliderPosition() - state().rows);
|
|
return;
|
|
|
|
case Qt::Key_PageDown:
|
|
_scrollTo(qtScrollBar->sliderPosition() + state().rows);
|
|
return;
|
|
|
|
case Qt::Key_0: nibble = 0; break;
|
|
case Qt::Key_1: nibble = 1; break;
|
|
case Qt::Key_2: nibble = 2; break;
|
|
case Qt::Key_3: nibble = 3; break;
|
|
case Qt::Key_4: nibble = 4; break;
|
|
case Qt::Key_5: nibble = 5; break;
|
|
case Qt::Key_6: nibble = 6; break;
|
|
case Qt::Key_7: nibble = 7; break;
|
|
case Qt::Key_8: nibble = 8; break;
|
|
case Qt::Key_9: nibble = 9; break;
|
|
case Qt::Key_A: nibble = 10; break;
|
|
case Qt::Key_B: nibble = 11; break;
|
|
case Qt::Key_C: nibble = 12; break;
|
|
case Qt::Key_D: nibble = 13; break;
|
|
case Qt::Key_E: nibble = 14; break;
|
|
case Qt::Key_F: nibble = 15; break;
|
|
}
|
|
|
|
if(cursorX >= 10) {
|
|
//not on an offset
|
|
cursorX -= 10;
|
|
if((cursorX % 3) != 2) {
|
|
//not on a space
|
|
bool cursorNibble = (cursorX % 3) == 1; //0 = high, 1 = low
|
|
cursorX /= 3;
|
|
if(cursorX < state().columns) {
|
|
//not in ANSI region
|
|
unsigned address = state().address + (cursorY * state().columns + cursorX);
|
|
|
|
if(address >= state().length) return; //do not edit past end of file
|
|
uint8_t data = self().doRead(address);
|
|
|
|
//write modified value
|
|
if(cursorNibble == 1) {
|
|
data = (data & 0xf0) | (nibble << 0);
|
|
} else {
|
|
data = (data & 0x0f) | (nibble << 4);
|
|
}
|
|
self().doWrite(address, data);
|
|
|
|
//auto-advance cursor to next nibble/byte
|
|
unsigned step = 1;
|
|
if(cursorNibble && cursorX != state().columns - 1) step = 2;
|
|
cursor.setPosition(cursor.position() + step);
|
|
qtHexEdit->setTextCursor(cursor);
|
|
|
|
//refresh output to reflect modified data
|
|
update();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//number of actual rows
|
|
auto pHexEdit::_rows() -> signed {
|
|
return (max(1u, state().length) + state().columns - 1) / state().columns;
|
|
}
|
|
|
|
//number of scrollable row positions
|
|
auto pHexEdit::_rowsScrollable() -> signed {
|
|
return max(0u, _rows() - state().rows);
|
|
}
|
|
|
|
auto pHexEdit::_scrollTo(signed position) -> void {
|
|
if(position > _rowsScrollable()) position = _rowsScrollable();
|
|
if(position < 0) position = 0;
|
|
qtScrollBar->setSliderPosition(position);
|
|
}
|
|
|
|
auto pHexEdit::_setState() -> void {
|
|
lock();
|
|
if(auto color = state().backgroundColor) {
|
|
QPalette palette = qtHexEdit->palette();
|
|
palette.setColor(QPalette::Base, QColor(color.red(), color.green(), color.blue()));
|
|
qtHexEdit->setPalette(palette);
|
|
qtHexEdit->setAutoFillBackground(true);
|
|
} else {
|
|
//todo
|
|
}
|
|
if(auto color = state().foregroundColor) {
|
|
QPalette palette = qtHexEdit->palette();
|
|
palette.setColor(QPalette::Text, QColor(color.red(), color.green(), color.blue()));
|
|
qtHexEdit->setPalette(palette);
|
|
} else {
|
|
//todo
|
|
}
|
|
//add one if last row is not equal to column length (eg only part of the row is present)
|
|
bool indivisible = state().columns == 0 || (state().length % state().columns) != 0;
|
|
qtScrollBar->setRange(0, state().length / state().columns + indivisible - state().rows);
|
|
qtScrollBar->setSliderPosition(state().address / state().columns);
|
|
qtScrollBar->setPageStep(state().rows);
|
|
update();
|
|
unlock();
|
|
}
|
|
|
|
auto QtHexEdit::keyPressEvent(QKeyEvent* event) -> void {
|
|
p._keyPressEvent(event);
|
|
}
|
|
|
|
auto QtHexEdit::keyPressEventAcknowledge(QKeyEvent* event) -> void {
|
|
QTextEdit::keyPressEvent(event);
|
|
}
|
|
|
|
auto QtHexEdit::wheelEvent(QWheelEvent* event) -> void {
|
|
if(event->orientation() == Qt::Vertical) {
|
|
signed offset = event->delta() < 0 ? +1 : -1;
|
|
p._scrollTo(p.qtScrollBar->sliderPosition() + offset);
|
|
event->accept();
|
|
}
|
|
}
|
|
|
|
auto QtHexEditScrollBar::event(QEvent* event) -> bool {
|
|
if(event->type() == QEvent::Wheel) {
|
|
auto wheelEvent = (QWheelEvent*)event;
|
|
if(wheelEvent->orientation() == Qt::Vertical) {
|
|
signed offset = wheelEvent->delta() < 0 ? +1 : -1;
|
|
p._scrollTo(sliderPosition() + offset);
|
|
return true;
|
|
}
|
|
}
|
|
return QScrollBar::event(event);
|
|
}
|
|
|
|
auto QtHexEditScrollBar::onScroll() -> void {
|
|
if(p.locked()) return;
|
|
unsigned address = sliderPosition();
|
|
p.state().address = address * p.state().columns;
|
|
p.update();
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|