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