|  | /* | 
|  | * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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" | 
|  |  | 
|  | #include "wine/debug.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(regedit); | 
|  |  | 
|  | /* 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; | 
|  |  | 
|  | static inline LRESULT HexEdit_SetFont (HEXEDIT_INFO *infoPtr, HFONT hFont, BOOL redraw); | 
|  |  | 
|  | static inline BYTE hexchar_to_byte(TCHAR 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 LPTSTR HexEdit_GetLineText(BYTE *pData, LONG cbData, LONG pad) | 
|  | { | 
|  | LPTSTR lpszLine = HeapAlloc(GetProcessHeap(), 0, | 
|  | (cbData * 3 + pad * 3 + DIV_SPACES + cbData + 1) * sizeof(TCHAR)); | 
|  | LONG i; | 
|  |  | 
|  | if (!lpszLine) | 
|  | return NULL; | 
|  |  | 
|  | for (i = 0; i < cbData; i++) | 
|  | wsprintf(lpszLine + i*3, TEXT("%02X "), 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 (GetWindowLong(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) | 
|  | { | 
|  | LPTSTR 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? */ | 
|  | TextOut(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); | 
|  | LPTSTR 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); | 
|  |  | 
|  | GetTextExtentPoint32(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); | 
|  | SendMessage(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, TCHAR 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, LPCREATESTRUCT 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); | 
|  | SetWindowLongPtr(hwnd, 0, 0); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static inline LRESULT | 
|  | HexEdit_EraseBackground (HEXEDIT_INFO *infoPtr, HDC hdc) | 
|  | { | 
|  | HBRUSH hBrush, hSolidBrush = NULL; | 
|  | RECT   rc; | 
|  |  | 
|  | if (GetWindowLong(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED) | 
|  | hBrush = hSolidBrush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE)); | 
|  | else | 
|  | { | 
|  | hBrush = (HBRUSH)SendMessage(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, LPCREATESTRUCT lpcs) | 
|  | { | 
|  | HEXEDIT_INFO *infoPtr; | 
|  | SetWindowLong(hwnd, GWL_EXSTYLE, | 
|  | lpcs->dwExStyle | WS_EX_CLIENTEDGE); | 
|  |  | 
|  | /* allocate memory for info structure */ | 
|  | infoPtr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEXEDIT_INFO)); | 
|  | SetWindowLongPtr(hwnd, 0, (DWORD_PTR)infoPtr); | 
|  |  | 
|  | /* initialize info structure */ | 
|  | infoPtr->nCaretPos = 0; | 
|  | infoPtr->hwndSelf = hwnd; | 
|  | infoPtr->nBytesPerLine = 2; | 
|  | infoPtr->bFocusHex = TRUE; | 
|  | infoPtr->bInsert = TRUE; | 
|  |  | 
|  | return DefWindowProc(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) | 
|  | { | 
|  | TEXTMETRIC 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); | 
|  |  | 
|  | GetTextMetrics(hdc, &tm); | 
|  | infoPtr->nHeight = tm.tmHeight + tm.tmExternalLeading; | 
|  |  | 
|  | GetClientRect(infoPtr->hwndSelf, &rcClient); | 
|  |  | 
|  | for (i = 0; ; i++) | 
|  | { | 
|  | BYTE *pData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, i); | 
|  | LPTSTR lpszLine = HexEdit_GetLineText(pData, i, 0); | 
|  | SIZE size; | 
|  | GetTextExtentPoint32(hdc, lpszLine, lstrlen(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 *)GetWindowLongPtr (hwnd, 0); | 
|  |  | 
|  | if (!infoPtr && (uMsg != WM_NCCREATE)) | 
|  | return DefWindowProc(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, (TCHAR)wParam); | 
|  |  | 
|  | case WM_CREATE: | 
|  | return HexEdit_Create (infoPtr, (LPCREATESTRUCT)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, (LPCREATESTRUCT)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 DefWindowProc(hwnd, uMsg, wParam, lParam); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void HexEdit_Register() | 
|  | { | 
|  | WNDCLASS wndClass; | 
|  |  | 
|  | ZeroMemory(&wndClass, sizeof(WNDCLASS)); | 
|  | wndClass.style         = 0; | 
|  | wndClass.lpfnWndProc   = HexEdit_WindowProc; | 
|  | wndClass.cbClsExtra    = 0; | 
|  | wndClass.cbWndExtra    = sizeof(HEXEDIT_INFO *); | 
|  | wndClass.hCursor       = NULL; | 
|  | wndClass.hbrBackground = NULL; | 
|  | wndClass.lpszClassName = HEXEDIT_CLASS; | 
|  |  | 
|  | RegisterClass(&wndClass); | 
|  | } | 
|  |  | 
|  |  | 
|  | void HexEdit_Unregister() | 
|  | { | 
|  | UnregisterClass(HEXEDIT_CLASS, NULL); | 
|  | } |