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