4 * Copyright 2005 Robert Shearman
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.
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.
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
22 * - Cut, copy and paste
39 /* spaces dividing hex and ASCII */
42 typedef struct tagHEXEDIT_INFO
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 */
53 INT nBytesPerLine
; /* bytes of hex to display per line of the control */
54 INT nScrollPos
; /* first visible line */
57 static inline LRESULT
HexEdit_SetFont (HEXEDIT_INFO
*infoPtr
, HFONT hFont
, BOOL redraw
);
59 static inline BYTE
hexchar_to_byte(TCHAR ch
)
61 if (ch
>= '0' && ch
<= '9')
63 else if (ch
>= 'a' && ch
<= 'f')
65 else if (ch
>= 'A' && ch
<= 'F')
71 static LPTSTR
HexEdit_GetLineText(BYTE
*pData
, LONG cbData
, LONG pad
)
73 LPTSTR lpszLine
= HeapAlloc(GetProcessHeap(), 0,
74 (cbData
* 3 + pad
* 3 + DIV_SPACES
+ cbData
+ 1) * sizeof(TCHAR
));
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
] = ' ';
85 for (i
= 0; i
< DIV_SPACES
; i
++)
86 lpszLine
[cbData
* 3 + pad
* 3 + i
] = ' ';
88 /* attempt an ASCII representation if the characters are printable,
89 * otherwise display a '.' */
90 for (i
= 0; i
< cbData
; i
++)
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
];
96 lpszLine
[cbData
* 3 + pad
* 3 + DIV_SPACES
+ i
] = '.';
98 lpszLine
[cbData
* 3 + pad
* 3 + DIV_SPACES
+ cbData
] = 0;
103 HexEdit_Paint(HEXEDIT_INFO
*infoPtr
)
106 HDC hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
107 INT nXStart
, nYStart
;
112 LONG lByteOffset
= infoPtr
->nScrollPos
* infoPtr
->nBytesPerLine
;
114 /* Make a gap from the frame */
115 nXStart
= GetSystemMetrics(SM_CXBORDER
);
116 nYStart
= GetSystemMetrics(SM_CYBORDER
);
118 if (GetWindowLong(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_DISABLED
)
119 clrOldText
= SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
121 clrOldText
= SetTextColor(hdc
, GetSysColor(COLOR_WINDOWTEXT
));
123 iMode
= SetBkMode(hdc
, TRANSPARENT
);
124 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
126 for (pData
= infoPtr
->pData
+ lByteOffset
; pData
< infoPtr
->pData
+ infoPtr
->cbData
; pData
+= infoPtr
->nBytesPerLine
)
129 LONG nLineLen
= min((LONG
)((infoPtr
->pData
+ infoPtr
->cbData
) - pData
),
130 infoPtr
->nBytesPerLine
);
132 lpszLine
= HexEdit_GetLineText(pData
, nLineLen
, infoPtr
->nBytesPerLine
- nLineLen
);
134 /* FIXME: draw hex <-> ASCII mapping highlighted? */
135 TextOut(hdc
, nXStart
, nYStart
, lpszLine
, infoPtr
->nBytesPerLine
* 3 + DIV_SPACES
+ nLineLen
);
137 nYStart
+= infoPtr
->nHeight
;
138 HeapFree(GetProcessHeap(), 0, lpszLine
);
141 SelectObject(hdc
, hOldFont
);
142 SetBkMode(hdc
, iMode
);
143 SetTextColor(hdc
, clrOldText
);
145 EndPaint(infoPtr
->hwndSelf
, &ps
);
149 HexEdit_UpdateCaret(HEXEDIT_INFO
*infoPtr
)
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
);
161 /* calculate offset of character caret is on in the line */
162 if (infoPtr
->bFocusHex
)
163 nCharOffset
= nByteLinePos
*3 + infoPtr
->nCaretPos
% 2;
165 nCharOffset
= infoPtr
->nBytesPerLine
*3 + DIV_SPACES
+ nByteLinePos
;
167 hdc
= GetDC(infoPtr
->hwndSelf
);
168 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
170 GetTextExtentPoint32(hdc
, lpszLine
, nCharOffset
, &size
);
172 SelectObject(hdc
, hOldFont
);
173 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
175 if (!nLineLen
) size
.cx
= 0;
177 HeapFree(GetProcessHeap(), 0, lpszLine
);
180 GetSystemMetrics(SM_CXBORDER
) + size
.cx
,
181 GetSystemMetrics(SM_CYBORDER
) + (nLine
- infoPtr
->nScrollPos
) * infoPtr
->nHeight
);
185 HexEdit_UpdateScrollbars(HEXEDIT_INFO
*infoPtr
)
188 INT nLines
= infoPtr
->cbData
/ infoPtr
->nBytesPerLine
;
192 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
193 InflateRect(&rcClient
, -GetSystemMetrics(SM_CXBORDER
), -GetSystemMetrics(SM_CYBORDER
));
195 nVisibleLines
= (rcClient
.bottom
- rcClient
.top
) / infoPtr
->nHeight
;
196 si
.cbSize
= sizeof(si
);
197 si
.fMask
= SIF_RANGE
| SIF_PAGE
;
199 si
.nMax
= max(nLines
- nVisibleLines
, nLines
);
200 si
.nPage
= nVisibleLines
;
201 SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &si
, TRUE
);
205 HexEdit_EnsureVisible(HEXEDIT_INFO
*infoPtr
, INT nCaretPos
)
207 INT nLine
= nCaretPos
/ (2 * infoPtr
->nBytesPerLine
);
210 si
.cbSize
= sizeof(si
);
211 si
.fMask
= SIF_POS
| SIF_PAGE
;
212 GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &si
);
215 else if (nLine
>= si
.nPos
+ si
.nPage
)
216 si
.nPos
= nLine
- si
.nPage
+ 1;
221 SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &si
, FALSE
);
222 SendMessage(infoPtr
->hwndSelf
, WM_VSCROLL
, MAKELONG(SB_THUMBPOSITION
, 0), 0);
227 HexEdit_SetData(HEXEDIT_INFO
*infoPtr
, INT cbData
, const BYTE
*pData
)
229 HeapFree(GetProcessHeap(), 0, infoPtr
->pData
);
232 infoPtr
->pData
= HeapAlloc(GetProcessHeap(), 0, cbData
);
235 memcpy(infoPtr
->pData
, pData
, cbData
);
236 infoPtr
->cbData
= cbData
;
238 infoPtr
->nCaretPos
= 0;
239 HexEdit_UpdateScrollbars(infoPtr
);
240 HexEdit_UpdateCaret(infoPtr
);
241 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
248 HexEdit_GetData(HEXEDIT_INFO
*infoPtr
, INT cbData
, BYTE
*pData
)
251 memcpy(pData
, infoPtr
->pData
, min(cbData
, infoPtr
->cbData
));
252 return infoPtr
->cbData
;
255 static inline LRESULT
256 HexEdit_Char (HEXEDIT_INFO
*infoPtr
, TCHAR ch
)
258 INT nCaretBytePos
= infoPtr
->nCaretPos
/2;
260 assert(nCaretBytePos
>= 0);
262 /* backspace is special */
265 if (infoPtr
->nCaretPos
== 0)
268 /* if at end of byte then delete the whole byte */
269 if (infoPtr
->bFocusHex
&& (infoPtr
->nCaretPos
% 2 == 0))
271 memmove(infoPtr
->pData
+ nCaretBytePos
- 1,
272 infoPtr
->pData
+ nCaretBytePos
,
273 infoPtr
->cbData
- nCaretBytePos
);
275 infoPtr
->nCaretPos
-= 2; /* backtrack two nibble */
277 else /* blank upper nibble */
279 infoPtr
->pData
[nCaretBytePos
] &= 0x0f;
280 infoPtr
->nCaretPos
--; /* backtrack one nibble */
285 if (infoPtr
->bFocusHex
&& hexchar_to_byte(ch
) == (BYTE
)-1)
287 MessageBeep(MB_ICONWARNING
);
291 if ((infoPtr
->bInsert
&& (infoPtr
->nCaretPos
% 2 == 0)) || (nCaretBytePos
>= infoPtr
->cbData
))
293 /* make room for another byte */
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
);
302 infoPtr
->pData
[nCaretBytePos
] = 0x0;
305 /* overwrite a byte */
307 assert(nCaretBytePos
< infoPtr
->cbData
);
309 if (infoPtr
->bFocusHex
)
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 */
321 infoPtr
->pData
[nCaretBytePos
] = (BYTE
)ch
;
322 infoPtr
->nCaretPos
+= 2; /* advance two nibbles */
326 HexEdit_UpdateScrollbars(infoPtr
);
327 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
328 HexEdit_UpdateCaret(infoPtr
);
329 HexEdit_EnsureVisible(infoPtr
, infoPtr
->nCaretPos
);
333 static inline LRESULT
334 HexEdit_Create (HEXEDIT_INFO
*infoPtr
, LPCREATESTRUCT lpcs
)
336 HexEdit_SetFont(infoPtr
, GetStockObject(SYSTEM_FONT
), FALSE
);
337 HexEdit_UpdateScrollbars(infoPtr
);
343 static inline LRESULT
344 HexEdit_Destroy (HEXEDIT_INFO
*infoPtr
)
346 HWND hwnd
= infoPtr
->hwndSelf
;
347 HeapFree(GetProcessHeap(), 0, infoPtr
->pData
);
349 HeapFree(GetProcessHeap(), 0, infoPtr
);
350 SetWindowLongPtr(hwnd
, 0, 0);
355 static inline LRESULT
356 HexEdit_EraseBackground (HEXEDIT_INFO
*infoPtr
, HDC hdc
)
358 HBRUSH hBrush
, hSolidBrush
= NULL
;
361 if (GetWindowLong(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_DISABLED
)
362 hBrush
= hSolidBrush
= CreateSolidBrush(GetSysColor(COLOR_BTNFACE
));
365 hBrush
= (HBRUSH
)SendMessage(GetParent(infoPtr
->hwndSelf
), WM_CTLCOLOREDIT
,
366 (WPARAM
)hdc
, (LPARAM
)infoPtr
->hwndSelf
);
368 hBrush
= hSolidBrush
= CreateSolidBrush(GetSysColor(COLOR_WINDOW
));
371 GetClientRect (infoPtr
->hwndSelf
, &rc
);
373 FillRect (hdc
, &rc
, hBrush
);
376 DeleteObject(hSolidBrush
);
382 static inline LRESULT
383 HexEdit_GetFont (HEXEDIT_INFO
*infoPtr
)
385 return (LRESULT
)infoPtr
->hFont
;
388 static inline LRESULT
389 HexEdit_KeyDown (HEXEDIT_INFO
*infoPtr
, DWORD key
, DWORD flags
)
391 INT nInc
= (infoPtr
->bFocusHex
) ? 1 : 2;
397 infoPtr
->nCaretPos
-= nInc
;
398 if (infoPtr
->nCaretPos
< 0)
399 infoPtr
->nCaretPos
= 0;
402 infoPtr
->nCaretPos
+= nInc
;
403 if (infoPtr
->nCaretPos
> infoPtr
->cbData
*2)
404 infoPtr
->nCaretPos
= infoPtr
->cbData
*2;
407 if ((infoPtr
->nCaretPos
- infoPtr
->nBytesPerLine
*2) >= 0)
408 infoPtr
->nCaretPos
-= infoPtr
->nBytesPerLine
*2;
411 if ((infoPtr
->nCaretPos
+ infoPtr
->nBytesPerLine
*2) <= infoPtr
->cbData
*2)
412 infoPtr
->nCaretPos
+= infoPtr
->nBytesPerLine
*2;
415 infoPtr
->nCaretPos
= 0;
418 infoPtr
->nCaretPos
= infoPtr
->cbData
*2;
420 case VK_PRIOR
: /* page up */
421 si
.cbSize
= sizeof(si
);
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;
427 infoPtr
->nCaretPos
= 0;
429 case VK_NEXT
: /* page down */
430 si
.cbSize
= sizeof(si
);
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;
436 infoPtr
->nCaretPos
= infoPtr
->cbData
*2;
442 HexEdit_UpdateCaret(infoPtr
);
443 HexEdit_EnsureVisible(infoPtr
, infoPtr
->nCaretPos
);
449 static inline LRESULT
450 HexEdit_KillFocus (HEXEDIT_INFO
*infoPtr
, HWND receiveFocus
)
452 infoPtr
->bFocus
= FALSE
;
459 static inline LRESULT
460 HexEdit_LButtonDown (HEXEDIT_INFO
*infoPtr
)
462 SetFocus(infoPtr
->hwndSelf
);
464 /* FIXME: hittest and set caret */
470 static inline LRESULT
HexEdit_NCCreate (HWND hwnd
, LPCREATESTRUCT lpcs
)
472 HEXEDIT_INFO
*infoPtr
;
473 SetWindowLong(hwnd
, GWL_EXSTYLE
,
474 lpcs
->dwExStyle
| WS_EX_CLIENTEDGE
);
476 /* allocate memory for info structure */
477 infoPtr
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HEXEDIT_INFO
));
478 SetWindowLongPtr(hwnd
, 0, (DWORD_PTR
)infoPtr
);
480 /* initialize info structure */
481 infoPtr
->nCaretPos
= 0;
482 infoPtr
->hwndSelf
= hwnd
;
483 infoPtr
->nBytesPerLine
= 2;
484 infoPtr
->bFocusHex
= TRUE
;
485 infoPtr
->bInsert
= TRUE
;
487 return DefWindowProc(infoPtr
->hwndSelf
, WM_NCCREATE
, 0, (LPARAM
)lpcs
);
490 static inline LRESULT
491 HexEdit_SetFocus (HEXEDIT_INFO
*infoPtr
, HWND lostFocus
)
493 infoPtr
->bFocus
= TRUE
;
495 CreateCaret(infoPtr
->hwndSelf
, NULL
, 1, infoPtr
->nHeight
);
496 HexEdit_UpdateCaret(infoPtr
);
497 ShowCaret(infoPtr
->hwndSelf
);
503 static inline LRESULT
504 HexEdit_SetFont (HEXEDIT_INFO
*infoPtr
, HFONT hFont
, BOOL redraw
)
508 HFONT hOldFont
= NULL
;
512 infoPtr
->hFont
= hFont
;
514 hdc
= GetDC(infoPtr
->hwndSelf
);
516 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
518 GetTextMetrics(hdc
, &tm
);
519 infoPtr
->nHeight
= tm
.tmHeight
+ tm
.tmExternalLeading
;
521 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
525 BYTE
*pData
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, i
);
526 LPTSTR lpszLine
= HexEdit_GetLineText(pData
, i
, 0);
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
))
533 infoPtr
->nBytesPerLine
= i
- 1;
539 SelectObject(hdc
, hOldFont
);
540 ReleaseDC (infoPtr
->hwndSelf
, hdc
);
543 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
548 static inline LRESULT
549 HexEdit_VScroll (HEXEDIT_INFO
*infoPtr
, INT action
)
553 /* get all scroll bar info */
554 si
.cbSize
= sizeof(si
);
556 GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &si
);
558 switch (LOWORD(action
))
560 case SB_TOP
: /* user pressed the home key */
564 case SB_BOTTOM
: /* user pressed the end key */
568 case SB_LINEUP
: /* user clicked the up arrow */
572 case SB_LINEDOWN
: /* user clicked the down arrow */
576 case SB_PAGEUP
: /* user clicked the scroll bar above the scroll thumb */
580 case SB_PAGEDOWN
: /* user clicked the scroll bar below the scroll thumb */
584 case SB_THUMBTRACK
: /* user dragged the scroll thumb */
585 si
.nPos
= si
.nTrackPos
;
592 /* set the position and then retrieve it to let the system handle the
593 * cases where the position is out of range */
595 SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &si
, TRUE
);
596 GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &si
);
598 if (si
.nPos
!= infoPtr
->nScrollPos
)
600 ScrollWindow(infoPtr
->hwndSelf
, 0, infoPtr
->nHeight
* (infoPtr
->nScrollPos
- si
.nPos
), NULL
, NULL
);
601 infoPtr
->nScrollPos
= si
.nPos
;
602 UpdateWindow(infoPtr
->hwndSelf
);
604 /* need to update caret position since it depends on the scroll position */
605 HexEdit_UpdateCaret(infoPtr
);
611 static LRESULT WINAPI
612 HexEdit_WindowProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
614 HEXEDIT_INFO
*infoPtr
= (HEXEDIT_INFO
*)GetWindowLongPtr (hwnd
, 0);
616 if (!infoPtr
&& (uMsg
!= WM_NCCREATE
))
617 return DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
622 return HexEdit_SetData (infoPtr
, (INT
)wParam
, (const BYTE
*)lParam
);
625 return HexEdit_GetData (infoPtr
, (INT
)wParam
, (BYTE
*)lParam
);
628 return HexEdit_Char (infoPtr
, (TCHAR
)wParam
);
631 return HexEdit_Create (infoPtr
, (LPCREATESTRUCT
)lParam
);
634 return HexEdit_Destroy (infoPtr
);
637 return HexEdit_EraseBackground (infoPtr
, (HDC
)wParam
);
640 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
643 return HexEdit_GetFont (infoPtr
);
646 return HexEdit_KeyDown (infoPtr
, wParam
, lParam
);
649 return HexEdit_KillFocus (infoPtr
, (HWND
)wParam
);
652 return HexEdit_LButtonDown (infoPtr
);
655 return HexEdit_NCCreate (hwnd
, (LPCREATESTRUCT
)lParam
);
658 HexEdit_Paint(infoPtr
);
662 return HexEdit_SetFocus (infoPtr
, (HWND
)wParam
);
665 return HexEdit_SetFont (infoPtr
, (HFONT
)wParam
, LOWORD(lParam
));
668 return HexEdit_VScroll (infoPtr
, (INT
)wParam
);
671 return DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
676 void HexEdit_Register(void)
680 ZeroMemory(&wndClass
, sizeof(WNDCLASS
));
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
;
689 RegisterClass(&wndClass
);