Update Korean translations.
[wine] / programs / regedit / hexedit.c
1 /*
2  * Hex Edit control
3  *
4  * Copyright 2005 Robert Shearman
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * TODO:
21  * - Selection support
22  * - Cut, copy and paste
23  * - Mouse support
24  */
25  
26 #include <stdarg.h>
27 #include <string.h>
28 #include <assert.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "wingdi.h"
33 #include "winuser.h"
34 #include "winnls.h"
35 #include "commctrl.h"
36
37 #include "main.h"
38
39 #include "wine/debug.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(regedit);
42
43 /* spaces dividing hex and ASCII */
44 #define DIV_SPACES 4
45
46 typedef struct tagHEXEDIT_INFO
47 {
48     HWND  hwndSelf;
49     HFONT hFont;
50     BOOL  bFocus : 1;
51     BOOL  bFocusHex : 1; /* TRUE if focus is on hex, FALSE if focus on ASCII */
52     BOOL  bInsert : 1; /* insert mode if TRUE, overwrite mode if FALSE */
53     INT   nHeight; /* height of text */
54     INT   nCaretPos; /* caret pos in nibbles */
55     BYTE *pData;
56     INT   cbData;
57     INT   nBytesPerLine; /* bytes of hex to display per line of the control */
58     INT   nScrollPos; /* first visible line */
59 } HEXEDIT_INFO;
60
61 static inline LRESULT HexEdit_SetFont (HEXEDIT_INFO *infoPtr, HFONT hFont, BOOL redraw);
62
63 static inline BYTE hexchar_to_byte(TCHAR ch)
64 {
65     if (ch >= '0' && ch <= '9')
66         return ch - '0';
67     else if (ch >= 'a' && ch <= 'f')
68         return ch - 'a' + 10;
69     else if (ch >= 'A' && ch <= 'F')
70         return ch - 'A' + 10;
71     else
72         return -1;
73 }
74
75 static LPTSTR HexEdit_GetLineText(BYTE *pData, LONG cbData, LONG pad)
76 {
77     LPTSTR lpszLine = HeapAlloc(GetProcessHeap(), 0,
78         (cbData * 3 + pad * 3 + DIV_SPACES + cbData + 1) * sizeof(TCHAR));
79     LONG i;
80
81     if (!lpszLine)
82         return NULL;
83
84     for (i = 0; i < cbData; i++)
85         wsprintf(lpszLine + i*3, TEXT("%02X "), pData[i]);
86     for (i = 0; i < pad * 3; i++)
87         lpszLine[cbData * 3 + i] = ' ';
88
89     for (i = 0; i < DIV_SPACES; i++)
90         lpszLine[cbData * 3 + pad * 3 + i] = ' ';
91
92     /* attempt an ASCII representation if the characters are printable,
93      * otherwise display a '.' */
94     for (i = 0; i < cbData; i++)
95     {
96         /* (C1_ALPHA|C1_BLANK|C1_PUNCT|C1_DIGIT|C1_LOWER|C1_UPPER) */
97         if (isprint(pData[i]))
98             lpszLine[cbData * 3 + pad * 3 + DIV_SPACES + i] = pData[i];
99         else
100             lpszLine[cbData * 3 + pad * 3 + DIV_SPACES + i] = '.';
101     }
102     lpszLine[cbData * 3 + pad * 3 + DIV_SPACES + cbData] = 0;
103     return lpszLine;
104 }
105
106 static void
107 HexEdit_Paint(HEXEDIT_INFO *infoPtr)
108 {
109     PAINTSTRUCT ps;
110     HDC hdc = BeginPaint(infoPtr->hwndSelf, &ps);
111     INT nXStart, nYStart;
112     COLORREF clrOldText;
113     HFONT hOldFont;
114     BYTE *pData;
115     INT iMode;
116     LONG lByteOffset = infoPtr->nScrollPos * infoPtr->nBytesPerLine;
117
118     /* Make a gap from the frame */
119     nXStart = GetSystemMetrics(SM_CXBORDER);
120     nYStart = GetSystemMetrics(SM_CYBORDER);
121
122     if (GetWindowLong(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
123         clrOldText = SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
124     else
125         clrOldText = SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
126
127     iMode = SetBkMode(hdc, TRANSPARENT);
128     hOldFont = SelectObject(hdc, infoPtr->hFont);
129         
130     for (pData = infoPtr->pData + lByteOffset; pData < infoPtr->pData + infoPtr->cbData; pData += infoPtr->nBytesPerLine)
131     {
132         LPTSTR lpszLine;
133         LONG nLineLen = min((LONG)((infoPtr->pData + infoPtr->cbData) - pData),
134             infoPtr->nBytesPerLine);
135
136         lpszLine = HexEdit_GetLineText(pData, nLineLen, infoPtr->nBytesPerLine - nLineLen);
137
138         /* FIXME: draw hex <-> ASCII mapping highlighted? */
139         TextOut(hdc, nXStart, nYStart, lpszLine, infoPtr->nBytesPerLine * 3 + DIV_SPACES + nLineLen);
140
141         nYStart += infoPtr->nHeight;
142         HeapFree(GetProcessHeap(), 0, lpszLine);
143     }
144
145     SelectObject(hdc, hOldFont);
146     SetBkMode(hdc, iMode);
147     SetTextColor(hdc, clrOldText);
148
149     EndPaint(infoPtr->hwndSelf, &ps);
150 }
151
152 static void
153 HexEdit_UpdateCaret(HEXEDIT_INFO *infoPtr)
154 {
155     HDC hdc;
156     HFONT hOldFont;
157     SIZE size;
158     INT nCaretBytePos = infoPtr->nCaretPos/2;
159     INT nByteLinePos = nCaretBytePos % infoPtr->nBytesPerLine;
160     INT nLine = nCaretBytePos / infoPtr->nBytesPerLine;
161     LONG nLineLen = min(infoPtr->cbData - nLine * infoPtr->nBytesPerLine, infoPtr->nBytesPerLine);
162     LPTSTR lpszLine = HexEdit_GetLineText(infoPtr->pData + nLine * infoPtr->nBytesPerLine, nLineLen, infoPtr->nBytesPerLine - nLineLen);
163     INT nCharOffset;
164
165     /* calculate offset of character caret is on in the line */
166     if (infoPtr->bFocusHex)
167         nCharOffset = nByteLinePos*3 + infoPtr->nCaretPos % 2;
168     else
169         nCharOffset = infoPtr->nBytesPerLine*3 + DIV_SPACES + nByteLinePos;
170
171     hdc = GetDC(infoPtr->hwndSelf);
172     hOldFont = SelectObject(hdc, infoPtr->hFont);
173
174     GetTextExtentPoint32(hdc, lpszLine, nCharOffset, &size);
175
176     SelectObject(hdc, hOldFont);
177     ReleaseDC(infoPtr->hwndSelf, hdc);
178
179     if (!nLineLen) size.cx = 0;
180
181     HeapFree(GetProcessHeap(), 0, lpszLine);
182
183     SetCaretPos(
184         GetSystemMetrics(SM_CXBORDER) + size.cx,
185         GetSystemMetrics(SM_CYBORDER) + (nLine - infoPtr->nScrollPos) * infoPtr->nHeight);
186 }
187
188 static void
189 HexEdit_UpdateScrollbars(HEXEDIT_INFO *infoPtr)
190 {
191     RECT rcClient;
192     INT nLines = infoPtr->cbData / infoPtr->nBytesPerLine;
193     INT nVisibleLines;
194     SCROLLINFO si;
195
196     GetClientRect(infoPtr->hwndSelf, &rcClient);
197     InflateRect(&rcClient, -GetSystemMetrics(SM_CXBORDER), -GetSystemMetrics(SM_CYBORDER));
198
199     nVisibleLines = (rcClient.bottom - rcClient.top) / infoPtr->nHeight;
200     si.cbSize = sizeof(si);
201     si.fMask = SIF_RANGE | SIF_PAGE;
202     si.nMin = 0;
203     si.nMax = max(nLines - nVisibleLines, nLines);
204     si.nPage = nVisibleLines;
205     SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si, TRUE);
206 }
207
208 static void
209 HexEdit_EnsureVisible(HEXEDIT_INFO *infoPtr, INT nCaretPos)
210 {
211     INT nLine = nCaretPos / (2 * infoPtr->nBytesPerLine);
212     SCROLLINFO si;
213
214     si.cbSize = sizeof(si);
215     si.fMask  = SIF_POS | SIF_PAGE;
216     GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si);
217     if (nLine < si.nPos)
218         si.nPos = nLine;
219     else if (nLine >= si.nPos + si.nPage)
220         si.nPos = nLine - si.nPage + 1;
221     else
222         return;
223     si.fMask = SIF_POS;
224
225     SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si, FALSE);
226     SendMessage(infoPtr->hwndSelf, WM_VSCROLL, MAKELONG(SB_THUMBPOSITION, 0), 0);
227 }
228
229
230 static LRESULT
231 HexEdit_SetData(HEXEDIT_INFO *infoPtr, INT cbData, const BYTE *pData)
232 {
233     HeapFree(GetProcessHeap(), 0, infoPtr->pData);
234     infoPtr->cbData = 0;
235
236     infoPtr->pData = HeapAlloc(GetProcessHeap(), 0, cbData);
237     if (infoPtr->pData)
238     {
239         memcpy(infoPtr->pData, pData, cbData);
240         infoPtr->cbData = cbData;
241
242         infoPtr->nCaretPos = 0;
243         HexEdit_UpdateScrollbars(infoPtr);
244         HexEdit_UpdateCaret(infoPtr);
245         InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
246         return TRUE;
247     }
248     return FALSE;
249 }
250
251 static LRESULT
252 HexEdit_GetData(HEXEDIT_INFO *infoPtr, INT cbData, BYTE *pData)
253 {
254     if (pData)
255         memcpy(pData, infoPtr->pData, min(cbData, infoPtr->cbData));
256     return infoPtr->cbData;
257 }
258
259 static inline LRESULT
260 HexEdit_Char (HEXEDIT_INFO *infoPtr, TCHAR ch)
261 {
262     INT nCaretBytePos = infoPtr->nCaretPos/2;
263
264     assert(nCaretBytePos >= 0);
265
266     /* backspace is special */
267     if (ch == '\b')
268     {
269         if (infoPtr->nCaretPos == 0)
270             return 0;
271
272         /* if at end of byte then delete the whole byte */
273         if (infoPtr->bFocusHex && (infoPtr->nCaretPos % 2 == 0))
274         {
275             memmove(infoPtr->pData + nCaretBytePos - 1,
276                     infoPtr->pData + nCaretBytePos,
277                     infoPtr->cbData - nCaretBytePos);
278             infoPtr->cbData--;
279             infoPtr->nCaretPos -= 2; /* backtrack two nibble */
280         }
281         else /* blank upper nibble */
282         {
283             infoPtr->pData[nCaretBytePos] &= 0x0f;
284             infoPtr->nCaretPos--; /* backtrack one nibble */
285         }
286     }
287     else
288     {
289         if (infoPtr->bFocusHex && hexchar_to_byte(ch) == (BYTE)-1)
290         {
291             MessageBeep(MB_ICONWARNING);
292             return 0;
293         }
294     
295         if ((infoPtr->bInsert && (infoPtr->nCaretPos % 2 == 0)) || (nCaretBytePos >= infoPtr->cbData))
296         {
297             /* make room for another byte */
298             infoPtr->cbData++;
299             infoPtr->pData = HeapReAlloc(GetProcessHeap(), 0, infoPtr->pData, infoPtr->cbData + 1);
300             if (!infoPtr->pData) return 0;
301             /* move everything after caret up one byte */
302             memmove(infoPtr->pData + nCaretBytePos + 1,
303                     infoPtr->pData + nCaretBytePos,
304                     infoPtr->cbData - nCaretBytePos);
305             /* zero new byte */
306             infoPtr->pData[nCaretBytePos] = 0x0;
307         }
308     
309         /* overwrite a byte */
310     
311         assert(nCaretBytePos < infoPtr->cbData);
312     
313         if (infoPtr->bFocusHex)
314         {
315             BYTE orig_byte = infoPtr->pData[nCaretBytePos];
316             BYTE digit = hexchar_to_byte(ch);
317             if (infoPtr->nCaretPos % 2) /* set low nibble */
318                 infoPtr->pData[nCaretBytePos] = (orig_byte & 0xf0) | digit;
319             else /* set high nibble */
320                 infoPtr->pData[nCaretBytePos] = (orig_byte & 0x0f) | digit << 4;
321             infoPtr->nCaretPos++; /* advance one nibble */
322         }
323         else
324         {
325             infoPtr->pData[nCaretBytePos] = (BYTE)ch;
326             infoPtr->nCaretPos += 2; /* advance two nibbles */
327         }
328     }
329
330     HexEdit_UpdateScrollbars(infoPtr);
331     InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
332     HexEdit_UpdateCaret(infoPtr);
333     HexEdit_EnsureVisible(infoPtr, infoPtr->nCaretPos);
334     return 0;
335 }
336
337 static inline LRESULT
338 HexEdit_Create (HEXEDIT_INFO *infoPtr, LPCREATESTRUCT lpcs)
339 {
340     HexEdit_SetFont(infoPtr, GetStockObject(SYSTEM_FONT), FALSE);
341     HexEdit_UpdateScrollbars(infoPtr);
342
343     return 0;
344 }
345
346
347 static inline LRESULT
348 HexEdit_Destroy (HEXEDIT_INFO *infoPtr)
349 {
350     HWND hwnd = infoPtr->hwndSelf;
351     HeapFree(GetProcessHeap(), 0, infoPtr->pData);
352     /* free info data */
353     HeapFree(GetProcessHeap(), 0, infoPtr);
354     SetWindowLongPtr(hwnd, 0, 0);
355     return 0;
356 }
357
358
359 static inline LRESULT
360 HexEdit_EraseBackground (HEXEDIT_INFO *infoPtr, HDC hdc)
361 {
362     HBRUSH hBrush, hSolidBrush = NULL;
363     RECT   rc;
364
365     if (GetWindowLong(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
366         hBrush = hSolidBrush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
367     else
368     {
369         hBrush = (HBRUSH)SendMessage(GetParent(infoPtr->hwndSelf), WM_CTLCOLOREDIT,
370                                      (WPARAM)hdc, (LPARAM)infoPtr->hwndSelf);
371         if (!hBrush)
372             hBrush = hSolidBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
373     }
374
375     GetClientRect (infoPtr->hwndSelf, &rc);
376
377     FillRect (hdc, &rc, hBrush);
378
379     if (hSolidBrush)
380         DeleteObject(hSolidBrush);
381
382     return -1;
383 }
384
385
386 static inline LRESULT
387 HexEdit_GetFont (HEXEDIT_INFO *infoPtr)
388 {
389     return (LRESULT)infoPtr->hFont;
390 }
391
392 static inline LRESULT
393 HexEdit_KeyDown (HEXEDIT_INFO *infoPtr, DWORD key, DWORD flags)
394 {
395     INT nInc = (infoPtr->bFocusHex) ? 1 : 2;
396     SCROLLINFO si;
397
398     switch (key)
399     {
400     case VK_LEFT:
401         infoPtr->nCaretPos -= nInc;
402         if (infoPtr->nCaretPos < 0)
403             infoPtr->nCaretPos = 0;
404         break;
405     case VK_RIGHT:
406         infoPtr->nCaretPos += nInc;
407         if (infoPtr->nCaretPos > infoPtr->cbData*2)
408             infoPtr->nCaretPos = infoPtr->cbData*2;
409         break;
410     case VK_UP:
411         if ((infoPtr->nCaretPos - infoPtr->nBytesPerLine*2) >= 0)
412             infoPtr->nCaretPos -= infoPtr->nBytesPerLine*2;
413         break;
414     case VK_DOWN:
415         if ((infoPtr->nCaretPos + infoPtr->nBytesPerLine*2) <= infoPtr->cbData*2)
416             infoPtr->nCaretPos += infoPtr->nBytesPerLine*2;
417         break;
418     case VK_HOME:
419         infoPtr->nCaretPos = 0;
420         break;
421     case VK_END:
422         infoPtr->nCaretPos = infoPtr->cbData*2;
423         break;
424     case VK_PRIOR: /* page up */
425         si.cbSize = sizeof(si);
426         si.fMask = SIF_PAGE;
427         GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si);
428         if ((infoPtr->nCaretPos - (INT)si.nPage*infoPtr->nBytesPerLine*2) >= 0)
429             infoPtr->nCaretPos -= si.nPage*infoPtr->nBytesPerLine*2;
430         else
431             infoPtr->nCaretPos = 0;
432         break;
433     case VK_NEXT: /* page down */
434         si.cbSize = sizeof(si);
435         si.fMask = SIF_PAGE;
436         GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si);
437         if ((infoPtr->nCaretPos + (INT)si.nPage*infoPtr->nBytesPerLine*2) <= infoPtr->cbData*2)
438             infoPtr->nCaretPos += si.nPage*infoPtr->nBytesPerLine*2;
439         else
440             infoPtr->nCaretPos = infoPtr->cbData*2;
441         break;
442     default:
443         return 0;
444     }
445
446     HexEdit_UpdateCaret(infoPtr);
447     HexEdit_EnsureVisible(infoPtr, infoPtr->nCaretPos);
448
449     return 0;
450 }
451
452
453 static inline LRESULT
454 HexEdit_KillFocus (HEXEDIT_INFO *infoPtr, HWND receiveFocus)
455 {
456     infoPtr->bFocus = FALSE;
457     DestroyCaret();
458
459     return 0;
460 }
461
462
463 static inline LRESULT
464 HexEdit_LButtonDown (HEXEDIT_INFO *infoPtr)
465 {
466     SetFocus(infoPtr->hwndSelf);
467
468     /* FIXME: hittest and set caret */
469
470     return 0;
471 }
472
473
474 static inline LRESULT HexEdit_NCCreate (HWND hwnd, LPCREATESTRUCT lpcs)
475 {
476     HEXEDIT_INFO *infoPtr;
477     SetWindowLong(hwnd, GWL_EXSTYLE, 
478                   lpcs->dwExStyle | WS_EX_CLIENTEDGE);
479
480     /* allocate memory for info structure */
481     infoPtr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEXEDIT_INFO));
482     SetWindowLongPtr(hwnd, 0, (DWORD_PTR)infoPtr);
483
484     /* initialize info structure */
485     infoPtr->nCaretPos = 0;
486     infoPtr->hwndSelf = hwnd;
487     infoPtr->nBytesPerLine = 2;
488     infoPtr->bFocusHex = TRUE;
489     infoPtr->bInsert = TRUE;
490
491     return DefWindowProc(infoPtr->hwndSelf, WM_NCCREATE, 0, (LPARAM)lpcs);
492 }
493
494 static inline LRESULT
495 HexEdit_SetFocus (HEXEDIT_INFO *infoPtr, HWND lostFocus)
496 {
497     infoPtr->bFocus = TRUE;
498
499     CreateCaret(infoPtr->hwndSelf, NULL, 1, infoPtr->nHeight);
500     HexEdit_UpdateCaret(infoPtr);
501     ShowCaret(infoPtr->hwndSelf);
502
503     return 0;
504 }
505
506
507 static inline LRESULT
508 HexEdit_SetFont (HEXEDIT_INFO *infoPtr, HFONT hFont, BOOL redraw)
509 {
510     TEXTMETRIC tm;
511     HDC hdc;
512     HFONT hOldFont = NULL;
513     LONG i;
514     RECT rcClient;
515
516     infoPtr->hFont = hFont;
517
518     hdc = GetDC(infoPtr->hwndSelf);
519     if (infoPtr->hFont)
520         hOldFont = SelectObject(hdc, infoPtr->hFont);
521
522     GetTextMetrics(hdc, &tm);
523     infoPtr->nHeight = tm.tmHeight + tm.tmExternalLeading;
524
525     GetClientRect(infoPtr->hwndSelf, &rcClient);
526
527     for (i = 0; ; i++)
528     {
529         BYTE *pData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, i);
530         LPTSTR lpszLine = HexEdit_GetLineText(pData, i, 0);
531         SIZE size;
532         GetTextExtentPoint32(hdc, lpszLine, lstrlen(lpszLine), &size);
533         HeapFree(GetProcessHeap(), 0, lpszLine);
534         HeapFree(GetProcessHeap(), 0, pData);
535         if (size.cx > (rcClient.right - rcClient.left))
536         {
537             infoPtr->nBytesPerLine = i - 1;
538             break;
539         }
540     }
541
542     if (infoPtr->hFont)
543         SelectObject(hdc, hOldFont);
544     ReleaseDC (infoPtr->hwndSelf, hdc);
545
546     if (redraw)
547         InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
548
549     return 0;
550 }
551
552 static inline LRESULT
553 HexEdit_VScroll (HEXEDIT_INFO *infoPtr, INT action)
554 {
555     SCROLLINFO si;
556
557     /* get all scroll bar info */
558     si.cbSize = sizeof(si);
559     si.fMask  = SIF_ALL;
560     GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si);
561
562     switch (LOWORD(action))
563     {
564     case SB_TOP: /* user pressed the home key */
565         si.nPos = si.nMin;
566         break;
567
568     case SB_BOTTOM: /* user pressed the end key */
569         si.nPos = si.nMax;
570         break;
571
572     case SB_LINEUP: /* user clicked the up arrow */
573         si.nPos -= 1;
574         break;
575
576     case SB_LINEDOWN: /* user clicked the down arrow */
577         si.nPos += 1;
578         break;
579
580     case SB_PAGEUP: /* user clicked the scroll bar above the scroll thumb */
581         si.nPos -= si.nPage;
582         break;
583
584     case SB_PAGEDOWN: /* user clicked the scroll bar below the scroll thumb */
585         si.nPos += si.nPage;
586         break;
587
588     case SB_THUMBTRACK: /* user dragged the scroll thumb */
589         si.nPos = si.nTrackPos;
590         break;
591
592     default:
593         break; 
594     }
595
596     /* set the position and then retrieve it to let the system handle the
597      * cases where the position is out of range */
598     si.fMask = SIF_POS;
599     SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si, TRUE);
600     GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si);
601
602     if (si.nPos != infoPtr->nScrollPos)
603     {                    
604         ScrollWindow(infoPtr->hwndSelf, 0, infoPtr->nHeight * (infoPtr->nScrollPos - si.nPos), NULL, NULL);
605         infoPtr->nScrollPos = si.nPos;
606         UpdateWindow(infoPtr->hwndSelf);
607
608         /* need to update caret position since it depends on the scroll position */
609         HexEdit_UpdateCaret(infoPtr);
610     }
611     return 0;
612 }
613
614
615 static LRESULT WINAPI
616 HexEdit_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
617 {
618     HEXEDIT_INFO *infoPtr = (HEXEDIT_INFO *)GetWindowLongPtr (hwnd, 0);
619
620     if (!infoPtr && (uMsg != WM_NCCREATE))
621         return DefWindowProc(hwnd, uMsg, wParam, lParam);
622
623     switch (uMsg)
624     {
625         case HEM_SETDATA:
626             return HexEdit_SetData (infoPtr, (INT)wParam, (const BYTE *)lParam);
627
628         case HEM_GETDATA:
629             return HexEdit_GetData (infoPtr, (INT)wParam, (BYTE *)lParam);
630
631         case WM_CHAR:
632             return HexEdit_Char (infoPtr, (TCHAR)wParam);
633
634         case WM_CREATE:
635             return HexEdit_Create (infoPtr, (LPCREATESTRUCT)lParam);
636
637         case WM_DESTROY:
638             return HexEdit_Destroy (infoPtr);
639
640         case WM_ERASEBKGND:
641             return HexEdit_EraseBackground (infoPtr, (HDC)wParam);
642
643         case WM_GETDLGCODE:
644             return DLGC_WANTCHARS | DLGC_WANTARROWS;
645
646         case WM_GETFONT:
647             return HexEdit_GetFont (infoPtr);
648
649         case WM_KEYDOWN:
650             return HexEdit_KeyDown (infoPtr, wParam, lParam);
651
652         case WM_KILLFOCUS:
653             return HexEdit_KillFocus (infoPtr, (HWND)wParam);
654
655         case WM_LBUTTONDOWN:
656             return HexEdit_LButtonDown (infoPtr);
657
658         case WM_NCCREATE:
659             return HexEdit_NCCreate (hwnd, (LPCREATESTRUCT)lParam);
660
661         case WM_PAINT:
662             HexEdit_Paint(infoPtr);
663             return 0;
664
665         case WM_SETFOCUS:
666             return HexEdit_SetFocus (infoPtr, (HWND)wParam);
667
668         case WM_SETFONT:
669             return HexEdit_SetFont (infoPtr, (HFONT)wParam, LOWORD(lParam));
670
671         case WM_VSCROLL:
672             return HexEdit_VScroll (infoPtr, (INT)wParam);
673
674         default:
675             return DefWindowProc(hwnd, uMsg, wParam, lParam);
676     }
677     return 0;
678 }
679
680 void HexEdit_Register()
681 {
682     WNDCLASS wndClass;
683
684     ZeroMemory(&wndClass, sizeof(WNDCLASS));
685     wndClass.style         = 0;
686     wndClass.lpfnWndProc   = HexEdit_WindowProc;
687     wndClass.cbClsExtra    = 0;
688     wndClass.cbWndExtra    = sizeof(HEXEDIT_INFO *);
689     wndClass.hCursor       = NULL;
690     wndClass.hbrBackground = NULL;
691     wndClass.lpszClassName = HEXEDIT_CLASS;
692
693     RegisterClass(&wndClass);
694 }
695
696
697 void HexEdit_Unregister()
698 {
699     UnregisterClass(HEXEDIT_CLASS, NULL);
700 }