bsnes/phoenix/windows/widget/hex-edit.cpp

252 lines
6.8 KiB
C++
Raw Normal View History

namespace phoenix {
static LRESULT CALLBACK HexEdit_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
HexEdit& hexEdit = *(HexEdit*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
switch(msg) {
case WM_KEYDOWN:
if(hexEdit.p.keyPress(wparam)) return 0;
break;
case WM_MOUSEWHEEL: {
signed offset = -((int16_t)HIWORD(wparam) / WHEEL_DELTA);
hexEdit.p.scrollTo(hexEdit.p.scrollPosition() + offset);
return true;
}
case WM_SIZE: {
RECT rc;
GetClientRect(hexEdit.p.hwnd, &rc);
SetWindowPos(hexEdit.p.scrollBar, HWND_TOP, rc.right - 18, 0, 18, rc.bottom, SWP_SHOWWINDOW);
break;
}
case WM_VSCROLL: {
SCROLLINFO info;
memset(&info, 0, sizeof(SCROLLINFO));
info.cbSize = sizeof(SCROLLINFO);
info.fMask = SIF_ALL;
GetScrollInfo((HWND)lparam, SB_CTL, &info);
switch(LOWORD(wparam)) {
case SB_LEFT: info.nPos = info.nMin; break;
case SB_RIGHT: info.nPos = info.nMax; break;
case SB_LINELEFT: info.nPos--; break;
case SB_LINERIGHT: info.nPos++; break;
case SB_PAGELEFT: info.nPos -= info.nMax >> 3; break;
case SB_PAGERIGHT: info.nPos += info.nMax >> 3; break;
case SB_THUMBTRACK: info.nPos = info.nTrackPos; break;
}
info.fMask = SIF_POS;
SetScrollInfo((HWND)lparam, SB_CTL, &info, TRUE);
GetScrollInfo((HWND)lparam, SB_CTL, &info); //get clamped position
hexEdit.p.scrollTo(info.nPos);
return TRUE;
}
}
return hexEdit.p.windowProc(hwnd, msg, wparam, lparam);
}
void pHexEdit::setColumns(unsigned columns) {
update();
}
void pHexEdit::setLength(unsigned length) {
SetScrollRange(scrollBar, SB_CTL, 0, rowsScrollable(), TRUE);
EnableWindow(scrollBar, rowsScrollable() > 0);
update();
}
void pHexEdit::setOffset(unsigned offset) {
SetScrollPos(scrollBar, SB_CTL, offset / hexEdit.state.columns, TRUE);
update();
}
void pHexEdit::setRows(unsigned rows) {
update();
}
void pHexEdit::update() {
if(!hexEdit.onRead) {
SetWindowText(hwnd, L"");
return;
}
unsigned cursorPosition = Edit_GetSel(hwnd);
string output;
unsigned offset = hexEdit.state.offset;
for(unsigned row = 0; row < hexEdit.state.rows; row++) {
output.append(hex<8>(offset));
output.append(" ");
string hexdata;
string ansidata = " ";
for(unsigned column = 0; column < hexEdit.state.columns; column++) {
if(offset < hexEdit.state.length) {
uint8_t data = hexEdit.onRead(offset++);
hexdata.append(hex<2>(data));
hexdata.append(" ");
ansidata.append(data >= 0x20 && data <= 0x7e ? (char)data : '.');
} else {
hexdata.append(" ");
ansidata.append(" ");
}
}
output.append(hexdata);
output.append(ansidata);
if(offset >= hexEdit.state.length) break;
if(row != hexEdit.state.rows - 1) output.append("\r\n");
}
SetWindowText(hwnd, utf16_t(output));
Edit_SetSel(hwnd, LOWORD(cursorPosition), HIWORD(cursorPosition));
}
void pHexEdit::constructor() {
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE, L"EDIT", L"",
WS_CHILD | WS_TABSTOP | ES_AUTOHSCROLL | ES_READONLY | ES_MULTILINE | ES_WANTRETURN,
0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0
);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&hexEdit);
scrollBar = CreateWindowEx(
0, L"SCROLLBAR", L"",
WS_VISIBLE | WS_CHILD | SBS_VERT,
0, 0, 0, 0, hwnd, (HMENU)id, GetModuleHandle(0), 0
);
SetWindowLongPtr(scrollBar, GWLP_USERDATA, (LONG_PTR)&hexEdit);
windowProc = (LRESULT CALLBACK (*)(HWND, UINT, LPARAM, WPARAM))GetWindowLongPtr(hwnd, GWLP_WNDPROC);
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)HexEdit_windowProc);
setDefaultFont();
setLength(hexEdit.state.length);
setOffset(hexEdit.state.offset);
update();
PostMessage(hwnd, EM_SETSEL, 10, 10);
synchronize();
}
void pHexEdit::destructor() {
DestroyWindow(hwnd);
}
void pHexEdit::orphan() {
destructor();
constructor();
}
bool pHexEdit::keyPress(unsigned scancode) {
if(!hexEdit.onRead) return false;
signed position = LOWORD(Edit_GetSel(hwnd));
signed lineWidth = 10 + (hexEdit.state.columns * 3) + 1 + hexEdit.state.columns + 2;
signed cursorY = position / lineWidth;
signed cursorX = position % lineWidth;
if(scancode == VK_HOME) {
signed offset = cursorY * lineWidth + 10;
Edit_SetSel(hwnd, offset, offset);
return true;
}
if(scancode == VK_END) {
signed offset = cursorY * lineWidth + 57;
Edit_SetSel(hwnd, offset, offset);
return true;
}
if(scancode == VK_UP) {
if(cursorY > 0) return false;
scrollTo(scrollPosition() - 1);
return true;
}
if(scancode == VK_DOWN) {
if(cursorY >= rows() - 1) return true;
if(cursorY < hexEdit.state.rows - 1) return false;
scrollTo(scrollPosition() + 1);
return true;
}
if(scancode == VK_PRIOR) {
scrollTo(scrollPosition() - hexEdit.state.rows);
return true;
}
if(scancode == VK_NEXT) {
scrollTo(scrollPosition() + hexEdit.state.rows);
return true;
}
//convert scancode to hex nibble
if(scancode >= '0' && scancode <= '9') scancode = scancode - '0';
else if(scancode >= 'A' && scancode <= 'F') scancode = scancode - 'A' + 10;
else if(scancode >= 'a' && scancode <= 'f') scancode = scancode - 'a' + 10;
else return false;
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 < hexEdit.state.columns) {
//not in ANSI region
unsigned offset = hexEdit.state.offset + (cursorY * hexEdit.state.columns + cursorX);
if(offset >= hexEdit.state.length) return false; //do not edit past end of data
uint8_t data = hexEdit.onRead(offset);
//write modified value
if(cursorNibble == 1) {
data = (data & 0xf0) | (scancode << 0);
} else {
data = (data & 0x0f) | (scancode << 4);
}
if(hexEdit.onWrite) hexEdit.onWrite(offset, data);
//auto-advance cursor to next nibble or byte
position++;
if(cursorNibble && cursorX != hexEdit.state.columns - 1) position++;
Edit_SetSel(hwnd, position, position);
//refresh output to reflect modified data
update();
}
}
}
return true;
}
signed pHexEdit::rows() {
return (max(1u, hexEdit.state.length) + hexEdit.state.columns - 1) / hexEdit.state.columns;
}
signed pHexEdit::rowsScrollable() {
return max(0u, rows() - hexEdit.state.rows);
}
signed pHexEdit::scrollPosition() {
return hexEdit.state.offset / hexEdit.state.columns;
}
void pHexEdit::scrollTo(signed position) {
if(position > rowsScrollable()) position = rowsScrollable();
if(position < 0) position = 0;
if(position == scrollPosition()) return;
hexEdit.setOffset(position * hexEdit.state.columns);
}
}