| /* | |
| * Hex Edit control | |
| * | |
| * Copyright 2005 Robert Shearman | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| * | |
| * TODO: | |
| * - Selection support | |
| * - Cut, copy and paste | |
| * - Mouse support | |
| */ | |
| #include <stdarg.h> | |
| #include <string.h> | |
| #include <assert.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "wingdi.h" | |
| #include "winuser.h" | |
| #include "winnls.h" | |
| #include "commctrl.h" | |
| #include "main.h" | |
| /* spaces dividing hex and ASCII */ | |
| #define DIV_SPACES 4 | |
| typedef struct tagHEXEDIT_INFO | |
| { | |
| HWND hwndSelf; | |
| HFONT hFont; | |
| BOOL bFocus : 1; | |
| BOOL bFocusHex : 1; /* TRUE if focus is on hex, FALSE if focus on ASCII */ | |
| BOOL bInsert : 1; /* insert mode if TRUE, overwrite mode if FALSE */ | |
| INT nHeight; /* height of text */ | |
| INT nCaretPos; /* caret pos in nibbles */ | |
| BYTE *pData; | |
| INT cbData; | |
| INT nBytesPerLine; /* bytes of hex to display per line of the control */ | |
| INT nScrollPos; /* first visible line */ | |
| } HEXEDIT_INFO; | |
| const WCHAR szHexEditClass[] = {'H','e','x','E','d','i','t',0}; | |
| static inline LRESULT HexEdit_SetFont (HEXEDIT_INFO *infoPtr, HFONT hFont, BOOL redraw); | |
| static inline BYTE hexchar_to_byte(WCHAR ch) | |
| { | |
| if (ch >= '0' && ch <= '9') | |
| return ch - '0'; | |
| else if (ch >= 'a' && ch <= 'f') | |
| return ch - 'a' + 10; | |
| else if (ch >= 'A' && ch <= 'F') | |
| return ch - 'A' + 10; | |
| else | |
| return -1; | |
| } | |
| static LPWSTR HexEdit_GetLineText(BYTE *pData, LONG cbData, LONG pad) | |
| { | |
| static const WCHAR percent_02xW[] = {'%','0','2','X',' ',0}; | |
| LPWSTR lpszLine = HeapAlloc(GetProcessHeap(), 0, | |
| (cbData * 3 + pad * 3 + DIV_SPACES + cbData + 1) * sizeof(WCHAR)); | |
| LONG i; | |
| if (!lpszLine) | |
| return NULL; | |
| for (i = 0; i < cbData; i++) | |
| wsprintfW(lpszLine + i*3, percent_02xW, pData[i]); | |
| for (i = 0; i < pad * 3; i++) | |
| lpszLine[cbData * 3 + i] = ' '; | |
| for (i = 0; i < DIV_SPACES; i++) | |
| lpszLine[cbData * 3 + pad * 3 + i] = ' '; | |
| /* attempt an ASCII representation if the characters are printable, | |
| * otherwise display a '.' */ | |
| for (i = 0; i < cbData; i++) | |
| { | |
| /* (C1_ALPHA|C1_BLANK|C1_PUNCT|C1_DIGIT|C1_LOWER|C1_UPPER) */ | |
| if (isprint(pData[i])) | |
| lpszLine[cbData * 3 + pad * 3 + DIV_SPACES + i] = pData[i]; | |
| else | |
| lpszLine[cbData * 3 + pad * 3 + DIV_SPACES + i] = '.'; | |
| } | |
| lpszLine[cbData * 3 + pad * 3 + DIV_SPACES + cbData] = 0; | |
| return lpszLine; | |
| } | |
| static void | |
| HexEdit_Paint(HEXEDIT_INFO *infoPtr) | |
| { | |
| PAINTSTRUCT ps; | |
| HDC hdc = BeginPaint(infoPtr->hwndSelf, &ps); | |
| INT nXStart, nYStart; | |
| COLORREF clrOldText; | |
| HFONT hOldFont; | |
| BYTE *pData; | |
| INT iMode; | |
| LONG lByteOffset = infoPtr->nScrollPos * infoPtr->nBytesPerLine; | |
| /* Make a gap from the frame */ | |
| nXStart = GetSystemMetrics(SM_CXBORDER); | |
| nYStart = GetSystemMetrics(SM_CYBORDER); | |
| if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED) | |
| clrOldText = SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT)); | |
| else | |
| clrOldText = SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT)); | |
| iMode = SetBkMode(hdc, TRANSPARENT); | |
| hOldFont = SelectObject(hdc, infoPtr->hFont); | |
| for (pData = infoPtr->pData + lByteOffset; pData < infoPtr->pData + infoPtr->cbData; pData += infoPtr->nBytesPerLine) | |
| { | |
| LPWSTR lpszLine; | |
| LONG nLineLen = min((LONG)((infoPtr->pData + infoPtr->cbData) - pData), | |
| infoPtr->nBytesPerLine); | |
| lpszLine = HexEdit_GetLineText(pData, nLineLen, infoPtr->nBytesPerLine - nLineLen); | |
| /* FIXME: draw hex <-> ASCII mapping highlighted? */ | |
| TextOutW(hdc, nXStart, nYStart, lpszLine, infoPtr->nBytesPerLine * 3 + DIV_SPACES + nLineLen); | |
| nYStart += infoPtr->nHeight; | |
| HeapFree(GetProcessHeap(), 0, lpszLine); | |
| } | |
| SelectObject(hdc, hOldFont); | |
| SetBkMode(hdc, iMode); | |
| SetTextColor(hdc, clrOldText); | |
| EndPaint(infoPtr->hwndSelf, &ps); | |
| } | |
| static void | |
| HexEdit_UpdateCaret(HEXEDIT_INFO *infoPtr) | |
| { | |
| HDC hdc; | |
| HFONT hOldFont; | |
| SIZE size; | |
| INT nCaretBytePos = infoPtr->nCaretPos/2; | |
| INT nByteLinePos = nCaretBytePos % infoPtr->nBytesPerLine; | |
| INT nLine = nCaretBytePos / infoPtr->nBytesPerLine; | |
| LONG nLineLen = min(infoPtr->cbData - nLine * infoPtr->nBytesPerLine, infoPtr->nBytesPerLine); | |
| LPWSTR lpszLine = HexEdit_GetLineText(infoPtr->pData + nLine * infoPtr->nBytesPerLine, nLineLen, infoPtr->nBytesPerLine - nLineLen); | |
| INT nCharOffset; | |
| /* calculate offset of character caret is on in the line */ | |
| if (infoPtr->bFocusHex) | |
| nCharOffset = nByteLinePos*3 + infoPtr->nCaretPos % 2; | |
| else | |
| nCharOffset = infoPtr->nBytesPerLine*3 + DIV_SPACES + nByteLinePos; | |
| hdc = GetDC(infoPtr->hwndSelf); | |
| hOldFont = SelectObject(hdc, infoPtr->hFont); | |
| GetTextExtentPoint32W(hdc, lpszLine, nCharOffset, &size); | |
| SelectObject(hdc, hOldFont); | |
| ReleaseDC(infoPtr->hwndSelf, hdc); | |
| if (!nLineLen) size.cx = 0; | |
| HeapFree(GetProcessHeap(), 0, lpszLine); | |
| SetCaretPos( | |
| GetSystemMetrics(SM_CXBORDER) + size.cx, | |
| GetSystemMetrics(SM_CYBORDER) + (nLine - infoPtr->nScrollPos) * infoPtr->nHeight); | |
| } | |
| static void | |
| HexEdit_UpdateScrollbars(HEXEDIT_INFO *infoPtr) | |
| { | |
| RECT rcClient; | |
| INT nLines = infoPtr->cbData / infoPtr->nBytesPerLine; | |
| INT nVisibleLines; | |
| SCROLLINFO si; | |
| GetClientRect(infoPtr->hwndSelf, &rcClient); | |
| InflateRect(&rcClient, -GetSystemMetrics(SM_CXBORDER), -GetSystemMetrics(SM_CYBORDER)); | |
| nVisibleLines = (rcClient.bottom - rcClient.top) / infoPtr->nHeight; | |
| si.cbSize = sizeof(si); | |
| si.fMask = SIF_RANGE | SIF_PAGE; | |
| si.nMin = 0; | |
| si.nMax = max(nLines - nVisibleLines, nLines); | |
| si.nPage = nVisibleLines; | |
| SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si, TRUE); | |
| } | |
| static void | |
| HexEdit_EnsureVisible(HEXEDIT_INFO *infoPtr, INT nCaretPos) | |
| { | |
| INT nLine = nCaretPos / (2 * infoPtr->nBytesPerLine); | |
| SCROLLINFO si; | |
| si.cbSize = sizeof(si); | |
| si.fMask = SIF_POS | SIF_PAGE; | |
| GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si); | |
| if (nLine < si.nPos) | |
| si.nPos = nLine; | |
| else if (nLine >= si.nPos + si.nPage) | |
| si.nPos = nLine - si.nPage + 1; | |
| else | |
| return; | |
| si.fMask = SIF_POS; | |
| SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si, FALSE); | |
| SendMessageW(infoPtr->hwndSelf, WM_VSCROLL, MAKELONG(SB_THUMBPOSITION, 0), 0); | |
| } | |
| static LRESULT | |
| HexEdit_SetData(HEXEDIT_INFO *infoPtr, INT cbData, const BYTE *pData) | |
| { | |
| HeapFree(GetProcessHeap(), 0, infoPtr->pData); | |
| infoPtr->cbData = 0; | |
| infoPtr->pData = HeapAlloc(GetProcessHeap(), 0, cbData); | |
| if (infoPtr->pData) | |
| { | |
| memcpy(infoPtr->pData, pData, cbData); | |
| infoPtr->cbData = cbData; | |
| infoPtr->nCaretPos = 0; | |
| HexEdit_UpdateScrollbars(infoPtr); | |
| HexEdit_UpdateCaret(infoPtr); | |
| InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); | |
| return TRUE; | |
| } | |
| return FALSE; | |
| } | |
| static LRESULT | |
| HexEdit_GetData(HEXEDIT_INFO *infoPtr, INT cbData, BYTE *pData) | |
| { | |
| if (pData) | |
| memcpy(pData, infoPtr->pData, min(cbData, infoPtr->cbData)); | |
| return infoPtr->cbData; | |
| } | |
| static inline LRESULT | |
| HexEdit_Char (HEXEDIT_INFO *infoPtr, WCHAR ch) | |
| { | |
| INT nCaretBytePos = infoPtr->nCaretPos/2; | |
| assert(nCaretBytePos >= 0); | |
| /* backspace is special */ | |
| if (ch == '\b') | |
| { | |
| if (infoPtr->nCaretPos == 0) | |
| return 0; | |
| /* if at end of byte then delete the whole byte */ | |
| if (infoPtr->bFocusHex && (infoPtr->nCaretPos % 2 == 0)) | |
| { | |
| memmove(infoPtr->pData + nCaretBytePos - 1, | |
| infoPtr->pData + nCaretBytePos, | |
| infoPtr->cbData - nCaretBytePos); | |
| infoPtr->cbData--; | |
| infoPtr->nCaretPos -= 2; /* backtrack two nibble */ | |
| } | |
| else /* blank upper nibble */ | |
| { | |
| infoPtr->pData[nCaretBytePos] &= 0x0f; | |
| infoPtr->nCaretPos--; /* backtrack one nibble */ | |
| } | |
| } | |
| else | |
| { | |
| if (infoPtr->bFocusHex && hexchar_to_byte(ch) == (BYTE)-1) | |
| { | |
| MessageBeep(MB_ICONWARNING); | |
| return 0; | |
| } | |
| if ((infoPtr->bInsert && (infoPtr->nCaretPos % 2 == 0)) || (nCaretBytePos >= infoPtr->cbData)) | |
| { | |
| /* make room for another byte */ | |
| infoPtr->cbData++; | |
| infoPtr->pData = HeapReAlloc(GetProcessHeap(), 0, infoPtr->pData, infoPtr->cbData + 1); | |
| if (!infoPtr->pData) return 0; | |
| /* move everything after caret up one byte */ | |
| memmove(infoPtr->pData + nCaretBytePos + 1, | |
| infoPtr->pData + nCaretBytePos, | |
| infoPtr->cbData - nCaretBytePos); | |
| /* zero new byte */ | |
| infoPtr->pData[nCaretBytePos] = 0x0; | |
| } | |
| /* overwrite a byte */ | |
| assert(nCaretBytePos < infoPtr->cbData); | |
| if (infoPtr->bFocusHex) | |
| { | |
| BYTE orig_byte = infoPtr->pData[nCaretBytePos]; | |
| BYTE digit = hexchar_to_byte(ch); | |
| if (infoPtr->nCaretPos % 2) /* set low nibble */ | |
| infoPtr->pData[nCaretBytePos] = (orig_byte & 0xf0) | digit; | |
| else /* set high nibble */ | |
| infoPtr->pData[nCaretBytePos] = (orig_byte & 0x0f) | digit << 4; | |
| infoPtr->nCaretPos++; /* advance one nibble */ | |
| } | |
| else | |
| { | |
| infoPtr->pData[nCaretBytePos] = (BYTE)ch; | |
| infoPtr->nCaretPos += 2; /* advance two nibbles */ | |
| } | |
| } | |
| HexEdit_UpdateScrollbars(infoPtr); | |
| InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); | |
| HexEdit_UpdateCaret(infoPtr); | |
| HexEdit_EnsureVisible(infoPtr, infoPtr->nCaretPos); | |
| return 0; | |
| } | |
| static inline LRESULT | |
| HexEdit_Create (HEXEDIT_INFO *infoPtr, LPCREATESTRUCTW lpcs) | |
| { | |
| HexEdit_SetFont(infoPtr, GetStockObject(SYSTEM_FONT), FALSE); | |
| HexEdit_UpdateScrollbars(infoPtr); | |
| return 0; | |
| } | |
| static inline LRESULT | |
| HexEdit_Destroy (HEXEDIT_INFO *infoPtr) | |
| { | |
| HWND hwnd = infoPtr->hwndSelf; | |
| HeapFree(GetProcessHeap(), 0, infoPtr->pData); | |
| /* free info data */ | |
| HeapFree(GetProcessHeap(), 0, infoPtr); | |
| SetWindowLongPtrW(hwnd, 0, 0); | |
| return 0; | |
| } | |
| static inline LRESULT | |
| HexEdit_EraseBackground (HEXEDIT_INFO *infoPtr, HDC hdc) | |
| { | |
| HBRUSH hBrush, hSolidBrush = NULL; | |
| RECT rc; | |
| if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED) | |
| hBrush = hSolidBrush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE)); | |
| else | |
| { | |
| hBrush = (HBRUSH)SendMessageW(GetParent(infoPtr->hwndSelf), WM_CTLCOLOREDIT, | |
| (WPARAM)hdc, (LPARAM)infoPtr->hwndSelf); | |
| if (!hBrush) | |
| hBrush = hSolidBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOW)); | |
| } | |
| GetClientRect (infoPtr->hwndSelf, &rc); | |
| FillRect (hdc, &rc, hBrush); | |
| if (hSolidBrush) | |
| DeleteObject(hSolidBrush); | |
| return -1; | |
| } | |
| static inline LRESULT | |
| HexEdit_GetFont (HEXEDIT_INFO *infoPtr) | |
| { | |
| return (LRESULT)infoPtr->hFont; | |
| } | |
| static inline LRESULT | |
| HexEdit_KeyDown (HEXEDIT_INFO *infoPtr, DWORD key, DWORD flags) | |
| { | |
| INT nInc = (infoPtr->bFocusHex) ? 1 : 2; | |
| SCROLLINFO si; | |
| switch (key) | |
| { | |
| case VK_LEFT: | |
| infoPtr->nCaretPos -= nInc; | |
| if (infoPtr->nCaretPos < 0) | |
| infoPtr->nCaretPos = 0; | |
| break; | |
| case VK_RIGHT: | |
| infoPtr->nCaretPos += nInc; | |
| if (infoPtr->nCaretPos > infoPtr->cbData*2) | |
| infoPtr->nCaretPos = infoPtr->cbData*2; | |
| break; | |
| case VK_UP: | |
| if ((infoPtr->nCaretPos - infoPtr->nBytesPerLine*2) >= 0) | |
| infoPtr->nCaretPos -= infoPtr->nBytesPerLine*2; | |
| break; | |
| case VK_DOWN: | |
| if ((infoPtr->nCaretPos + infoPtr->nBytesPerLine*2) <= infoPtr->cbData*2) | |
| infoPtr->nCaretPos += infoPtr->nBytesPerLine*2; | |
| break; | |
| case VK_HOME: | |
| infoPtr->nCaretPos = 0; | |
| break; | |
| case VK_END: | |
| infoPtr->nCaretPos = infoPtr->cbData*2; | |
| break; | |
| case VK_PRIOR: /* page up */ | |
| si.cbSize = sizeof(si); | |
| si.fMask = SIF_PAGE; | |
| GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si); | |
| if ((infoPtr->nCaretPos - (INT)si.nPage*infoPtr->nBytesPerLine*2) >= 0) | |
| infoPtr->nCaretPos -= si.nPage*infoPtr->nBytesPerLine*2; | |
| else | |
| infoPtr->nCaretPos = 0; | |
| break; | |
| case VK_NEXT: /* page down */ | |
| si.cbSize = sizeof(si); | |
| si.fMask = SIF_PAGE; | |
| GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si); | |
| if ((infoPtr->nCaretPos + (INT)si.nPage*infoPtr->nBytesPerLine*2) <= infoPtr->cbData*2) | |
| infoPtr->nCaretPos += si.nPage*infoPtr->nBytesPerLine*2; | |
| else | |
| infoPtr->nCaretPos = infoPtr->cbData*2; | |
| break; | |
| default: | |
| return 0; | |
| } | |
| HexEdit_UpdateCaret(infoPtr); | |
| HexEdit_EnsureVisible(infoPtr, infoPtr->nCaretPos); | |
| return 0; | |
| } | |
| static inline LRESULT | |
| HexEdit_KillFocus (HEXEDIT_INFO *infoPtr, HWND receiveFocus) | |
| { | |
| infoPtr->bFocus = FALSE; | |
| DestroyCaret(); | |
| return 0; | |
| } | |
| static inline LRESULT | |
| HexEdit_LButtonDown (HEXEDIT_INFO *infoPtr) | |
| { | |
| SetFocus(infoPtr->hwndSelf); | |
| /* FIXME: hittest and set caret */ | |
| return 0; | |
| } | |
| static inline LRESULT HexEdit_NCCreate (HWND hwnd, LPCREATESTRUCTW lpcs) | |
| { | |
| HEXEDIT_INFO *infoPtr; | |
| SetWindowLongW(hwnd, GWL_EXSTYLE, | |
| lpcs->dwExStyle | WS_EX_CLIENTEDGE); | |
| /* allocate memory for info structure */ | |
| infoPtr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEXEDIT_INFO)); | |
| SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr); | |
| /* initialize info structure */ | |
| infoPtr->nCaretPos = 0; | |
| infoPtr->hwndSelf = hwnd; | |
| infoPtr->nBytesPerLine = 2; | |
| infoPtr->bFocusHex = TRUE; | |
| infoPtr->bInsert = TRUE; | |
| return DefWindowProcW(infoPtr->hwndSelf, WM_NCCREATE, 0, (LPARAM)lpcs); | |
| } | |
| static inline LRESULT | |
| HexEdit_SetFocus (HEXEDIT_INFO *infoPtr, HWND lostFocus) | |
| { | |
| infoPtr->bFocus = TRUE; | |
| CreateCaret(infoPtr->hwndSelf, NULL, 1, infoPtr->nHeight); | |
| HexEdit_UpdateCaret(infoPtr); | |
| ShowCaret(infoPtr->hwndSelf); | |
| return 0; | |
| } | |
| static inline LRESULT | |
| HexEdit_SetFont (HEXEDIT_INFO *infoPtr, HFONT hFont, BOOL redraw) | |
| { | |
| TEXTMETRICW tm; | |
| HDC hdc; | |
| HFONT hOldFont = NULL; | |
| LONG i; | |
| RECT rcClient; | |
| infoPtr->hFont = hFont; | |
| hdc = GetDC(infoPtr->hwndSelf); | |
| if (infoPtr->hFont) | |
| hOldFont = SelectObject(hdc, infoPtr->hFont); | |
| GetTextMetricsW(hdc, &tm); | |
| infoPtr->nHeight = tm.tmHeight + tm.tmExternalLeading; | |
| GetClientRect(infoPtr->hwndSelf, &rcClient); | |
| for (i = 0; ; i++) | |
| { | |
| BYTE *pData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, i); | |
| LPWSTR lpszLine = HexEdit_GetLineText(pData, i, 0); | |
| SIZE size; | |
| GetTextExtentPoint32W(hdc, lpszLine, lstrlenW(lpszLine), &size); | |
| HeapFree(GetProcessHeap(), 0, lpszLine); | |
| HeapFree(GetProcessHeap(), 0, pData); | |
| if (size.cx > (rcClient.right - rcClient.left)) | |
| { | |
| infoPtr->nBytesPerLine = i - 1; | |
| break; | |
| } | |
| } | |
| if (infoPtr->hFont) | |
| SelectObject(hdc, hOldFont); | |
| ReleaseDC (infoPtr->hwndSelf, hdc); | |
| if (redraw) | |
| InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); | |
| return 0; | |
| } | |
| static inline LRESULT | |
| HexEdit_VScroll (HEXEDIT_INFO *infoPtr, INT action) | |
| { | |
| SCROLLINFO si; | |
| /* get all scroll bar info */ | |
| si.cbSize = sizeof(si); | |
| si.fMask = SIF_ALL; | |
| GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si); | |
| switch (LOWORD(action)) | |
| { | |
| case SB_TOP: /* user pressed the home key */ | |
| si.nPos = si.nMin; | |
| break; | |
| case SB_BOTTOM: /* user pressed the end key */ | |
| si.nPos = si.nMax; | |
| break; | |
| case SB_LINEUP: /* user clicked the up arrow */ | |
| si.nPos -= 1; | |
| break; | |
| case SB_LINEDOWN: /* user clicked the down arrow */ | |
| si.nPos += 1; | |
| break; | |
| case SB_PAGEUP: /* user clicked the scroll bar above the scroll thumb */ | |
| si.nPos -= si.nPage; | |
| break; | |
| case SB_PAGEDOWN: /* user clicked the scroll bar below the scroll thumb */ | |
| si.nPos += si.nPage; | |
| break; | |
| case SB_THUMBTRACK: /* user dragged the scroll thumb */ | |
| si.nPos = si.nTrackPos; | |
| break; | |
| default: | |
| break; | |
| } | |
| /* set the position and then retrieve it to let the system handle the | |
| * cases where the position is out of range */ | |
| si.fMask = SIF_POS; | |
| SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si, TRUE); | |
| GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si); | |
| if (si.nPos != infoPtr->nScrollPos) | |
| { | |
| ScrollWindow(infoPtr->hwndSelf, 0, infoPtr->nHeight * (infoPtr->nScrollPos - si.nPos), NULL, NULL); | |
| infoPtr->nScrollPos = si.nPos; | |
| UpdateWindow(infoPtr->hwndSelf); | |
| /* need to update caret position since it depends on the scroll position */ | |
| HexEdit_UpdateCaret(infoPtr); | |
| } | |
| return 0; | |
| } | |
| static LRESULT WINAPI | |
| HexEdit_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) | |
| { | |
| HEXEDIT_INFO *infoPtr = (HEXEDIT_INFO *)GetWindowLongPtrW(hwnd, 0); | |
| if (!infoPtr && (uMsg != WM_NCCREATE)) | |
| return DefWindowProcW(hwnd, uMsg, wParam, lParam); | |
| switch (uMsg) | |
| { | |
| case HEM_SETDATA: | |
| return HexEdit_SetData (infoPtr, (INT)wParam, (const BYTE *)lParam); | |
| case HEM_GETDATA: | |
| return HexEdit_GetData (infoPtr, (INT)wParam, (BYTE *)lParam); | |
| case WM_CHAR: | |
| return HexEdit_Char (infoPtr, (WCHAR)wParam); | |
| case WM_CREATE: | |
| return HexEdit_Create (infoPtr, (LPCREATESTRUCTW)lParam); | |
| case WM_DESTROY: | |
| return HexEdit_Destroy (infoPtr); | |
| case WM_ERASEBKGND: | |
| return HexEdit_EraseBackground (infoPtr, (HDC)wParam); | |
| case WM_GETDLGCODE: | |
| return DLGC_WANTCHARS | DLGC_WANTARROWS; | |
| case WM_GETFONT: | |
| return HexEdit_GetFont (infoPtr); | |
| case WM_KEYDOWN: | |
| return HexEdit_KeyDown (infoPtr, wParam, lParam); | |
| case WM_KILLFOCUS: | |
| return HexEdit_KillFocus (infoPtr, (HWND)wParam); | |
| case WM_LBUTTONDOWN: | |
| return HexEdit_LButtonDown (infoPtr); | |
| case WM_NCCREATE: | |
| return HexEdit_NCCreate (hwnd, (LPCREATESTRUCTW)lParam); | |
| case WM_PAINT: | |
| HexEdit_Paint(infoPtr); | |
| return 0; | |
| case WM_SETFOCUS: | |
| return HexEdit_SetFocus (infoPtr, (HWND)wParam); | |
| case WM_SETFONT: | |
| return HexEdit_SetFont (infoPtr, (HFONT)wParam, LOWORD(lParam)); | |
| case WM_VSCROLL: | |
| return HexEdit_VScroll (infoPtr, (INT)wParam); | |
| default: | |
| return DefWindowProcW(hwnd, uMsg, wParam, lParam); | |
| } | |
| return 0; | |
| } | |
| void HexEdit_Register(void) | |
| { | |
| WNDCLASSW wndClass; | |
| ZeroMemory(&wndClass, sizeof(WNDCLASSW)); | |
| wndClass.style = 0; | |
| wndClass.lpfnWndProc = HexEdit_WindowProc; | |
| wndClass.cbClsExtra = 0; | |
| wndClass.cbWndExtra = sizeof(HEXEDIT_INFO *); | |
| wndClass.hCursor = NULL; | |
| wndClass.hbrBackground = NULL; | |
| wndClass.lpszClassName = szHexEditClass; | |
| RegisterClassW(&wndClass); | |
| } |