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
37 #include "wine/heap.h"
40 /* spaces dividing hex and ASCII */
43 typedef struct tagHEXEDIT_INFO
48 BOOL bFocusHex
: 1; /* TRUE if focus is on hex, FALSE if focus on ASCII */
49 BOOL bInsert
: 1; /* insert mode if TRUE, overwrite mode if FALSE */
50 INT nHeight
; /* height of text */
51 INT nCaretPos
; /* caret pos in nibbles */
54 INT nBytesPerLine
; /* bytes of hex to display per line of the control */
55 INT nScrollPos
; /* first visible line */
58 const WCHAR szHexEditClass
[] = {'H','e','x','E','d','i','t',0};
60 static inline LRESULT
HexEdit_SetFont (HEXEDIT_INFO
*infoPtr
, HFONT hFont
, BOOL redraw
);
62 static inline BYTE
hexchar_to_byte(WCHAR ch
)
64 if (ch
>= '0' && ch
<= '9')
66 else if (ch
>= 'a' && ch
<= 'f')
68 else if (ch
>= 'A' && ch
<= 'F')
74 static LPWSTR
HexEdit_GetLineText(int offset
, BYTE
*pData
, LONG cbData
, LONG pad
)
76 static const WCHAR percent_04xW
[] = {'%','0','4','X',' ',' ',0};
77 static const WCHAR percent_02xW
[] = {'%','0','2','X',' ',0};
79 WCHAR
*lpszLine
= heap_xalloc((6 + cbData
* 3 + pad
* 3 + DIV_SPACES
+ cbData
+ 1) * sizeof(WCHAR
));
82 wsprintfW(lpszLine
, percent_04xW
, offset
);
84 for (i
= 0; i
< cbData
; i
++)
85 wsprintfW(lpszLine
+ 6 + i
*3, percent_02xW
, pData
[offset
+ i
]);
86 for (i
= 0; i
< pad
* 3; i
++)
87 lpszLine
[6 + cbData
* 3 + i
] = ' ';
89 for (i
= 0; i
< DIV_SPACES
; i
++)
90 lpszLine
[6 + cbData
* 3 + pad
* 3 + i
] = ' ';
92 /* attempt an ASCII representation if the characters are printable,
93 * otherwise display a '.' */
94 for (i
= 0; i
< cbData
; i
++)
96 /* (C1_ALPHA|C1_BLANK|C1_PUNCT|C1_DIGIT|C1_LOWER|C1_UPPER) */
97 if (iswprint(pData
[offset
+ i
]))
98 lpszLine
[6 + cbData
* 3 + pad
* 3 + DIV_SPACES
+ i
] = pData
[offset
+ i
];
100 lpszLine
[6 + cbData
* 3 + pad
* 3 + DIV_SPACES
+ i
] = '.';
102 lpszLine
[6 + cbData
* 3 + pad
* 3 + DIV_SPACES
+ cbData
] = 0;
107 HexEdit_Paint(HEXEDIT_INFO
*infoPtr
)
110 HDC hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
111 INT nXStart
, nYStart
;
115 LONG lByteOffset
= infoPtr
->nScrollPos
* infoPtr
->nBytesPerLine
;
118 /* Make a gap from the frame */
119 nXStart
= GetSystemMetrics(SM_CXBORDER
);
120 nYStart
= GetSystemMetrics(SM_CYBORDER
);
122 if (GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_DISABLED
)
123 clrOldText
= SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
125 clrOldText
= SetTextColor(hdc
, GetSysColor(COLOR_WINDOWTEXT
));
127 iMode
= SetBkMode(hdc
, TRANSPARENT
);
128 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
130 for (i
= lByteOffset
; i
< infoPtr
->cbData
; i
+= infoPtr
->nBytesPerLine
)
133 LONG nLineLen
= min(infoPtr
->cbData
- i
, infoPtr
->nBytesPerLine
);
135 lpszLine
= HexEdit_GetLineText(i
, infoPtr
->pData
, nLineLen
, infoPtr
->nBytesPerLine
- nLineLen
);
137 /* FIXME: draw hex <-> ASCII mapping highlighted? */
138 TextOutW(hdc
, nXStart
, nYStart
, lpszLine
, lstrlenW(lpszLine
));
140 nYStart
+= infoPtr
->nHeight
;
144 SelectObject(hdc
, hOldFont
);
145 SetBkMode(hdc
, iMode
);
146 SetTextColor(hdc
, clrOldText
);
148 EndPaint(infoPtr
->hwndSelf
, &ps
);
152 HexEdit_UpdateCaret(HEXEDIT_INFO
*infoPtr
)
157 INT nCaretBytePos
= infoPtr
->nCaretPos
/2;
158 INT nByteLinePos
= nCaretBytePos
% infoPtr
->nBytesPerLine
;
159 INT nLine
= nCaretBytePos
/ infoPtr
->nBytesPerLine
;
160 LONG nLineLen
= min(infoPtr
->cbData
- nLine
* infoPtr
->nBytesPerLine
, infoPtr
->nBytesPerLine
);
161 LPWSTR lpszLine
= HexEdit_GetLineText(nLine
* infoPtr
->nBytesPerLine
, infoPtr
->pData
, nLineLen
, infoPtr
->nBytesPerLine
- nLineLen
);
164 /* calculate offset of character caret is on in the line */
165 if (infoPtr
->bFocusHex
)
166 nCharOffset
= 6 + nByteLinePos
*3 + infoPtr
->nCaretPos
% 2;
168 nCharOffset
= 6 + infoPtr
->nBytesPerLine
*3 + DIV_SPACES
+ nByteLinePos
;
170 hdc
= GetDC(infoPtr
->hwndSelf
);
171 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
173 GetTextExtentPoint32W(hdc
, lpszLine
, nCharOffset
, &size
);
175 SelectObject(hdc
, hOldFont
);
176 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
178 if (!nLineLen
) size
.cx
= 0;
183 GetSystemMetrics(SM_CXBORDER
) + size
.cx
,
184 GetSystemMetrics(SM_CYBORDER
) + (nLine
- infoPtr
->nScrollPos
) * infoPtr
->nHeight
);
188 HexEdit_UpdateScrollbars(HEXEDIT_INFO
*infoPtr
)
191 INT nLines
= infoPtr
->cbData
/ infoPtr
->nBytesPerLine
;
195 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
196 InflateRect(&rcClient
, -GetSystemMetrics(SM_CXBORDER
), -GetSystemMetrics(SM_CYBORDER
));
198 nVisibleLines
= (rcClient
.bottom
- rcClient
.top
) / infoPtr
->nHeight
;
199 si
.cbSize
= sizeof(si
);
200 si
.fMask
= SIF_RANGE
| SIF_PAGE
;
202 si
.nMax
= max(nLines
- nVisibleLines
, nLines
);
203 si
.nPage
= nVisibleLines
;
204 SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &si
, TRUE
);
208 HexEdit_EnsureVisible(HEXEDIT_INFO
*infoPtr
, INT nCaretPos
)
210 INT nLine
= nCaretPos
/ (2 * infoPtr
->nBytesPerLine
);
213 si
.cbSize
= sizeof(si
);
214 si
.fMask
= SIF_POS
| SIF_PAGE
;
215 GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &si
);
218 else if (nLine
>= si
.nPos
+ si
.nPage
)
219 si
.nPos
= nLine
- si
.nPage
+ 1;
224 SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &si
, FALSE
);
225 SendMessageW(infoPtr
->hwndSelf
, WM_VSCROLL
, MAKELONG(SB_THUMBPOSITION
, 0), 0);
230 HexEdit_SetData(HEXEDIT_INFO
*infoPtr
, INT cbData
, const BYTE
*pData
)
232 heap_free(infoPtr
->pData
);
235 infoPtr
->pData
= heap_xalloc(cbData
);
236 memcpy(infoPtr
->pData
, pData
, cbData
);
237 infoPtr
->cbData
= cbData
;
239 infoPtr
->nCaretPos
= 0;
240 HexEdit_UpdateScrollbars(infoPtr
);
241 HexEdit_UpdateCaret(infoPtr
);
242 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
247 HexEdit_GetData(HEXEDIT_INFO
*infoPtr
, INT cbData
, BYTE
*pData
)
250 memcpy(pData
, infoPtr
->pData
, min(cbData
, infoPtr
->cbData
));
251 return infoPtr
->cbData
;
254 static inline LRESULT
255 HexEdit_Char (HEXEDIT_INFO
*infoPtr
, WCHAR ch
)
257 INT nCaretBytePos
= infoPtr
->nCaretPos
/2;
259 assert(nCaretBytePos
>= 0);
261 /* backspace is special */
264 if (infoPtr
->nCaretPos
== 0)
267 /* if at end of byte then delete the whole byte */
268 if (infoPtr
->bFocusHex
&& (infoPtr
->nCaretPos
% 2 == 0))
270 memmove(infoPtr
->pData
+ nCaretBytePos
- 1,
271 infoPtr
->pData
+ nCaretBytePos
,
272 infoPtr
->cbData
- nCaretBytePos
);
274 infoPtr
->nCaretPos
-= 2; /* backtrack two nibble */
276 else /* blank upper nibble */
278 infoPtr
->pData
[nCaretBytePos
] &= 0x0f;
279 infoPtr
->nCaretPos
--; /* backtrack one nibble */
284 if (infoPtr
->bFocusHex
&& hexchar_to_byte(ch
) == (BYTE
)-1)
286 MessageBeep(MB_ICONWARNING
);
290 if ((infoPtr
->bInsert
&& (infoPtr
->nCaretPos
% 2 == 0)) || (nCaretBytePos
>= infoPtr
->cbData
))
292 /* make room for another byte */
294 infoPtr
->pData
= heap_xrealloc(infoPtr
->pData
, infoPtr
->cbData
+ 1);
296 /* move everything after caret up one byte */
297 memmove(infoPtr
->pData
+ nCaretBytePos
+ 1,
298 infoPtr
->pData
+ nCaretBytePos
,
299 infoPtr
->cbData
- nCaretBytePos
);
301 infoPtr
->pData
[nCaretBytePos
] = 0x0;
304 /* overwrite a byte */
306 assert(nCaretBytePos
< infoPtr
->cbData
);
308 if (infoPtr
->bFocusHex
)
310 BYTE orig_byte
= infoPtr
->pData
[nCaretBytePos
];
311 BYTE digit
= hexchar_to_byte(ch
);
312 if (infoPtr
->nCaretPos
% 2) /* set low nibble */
313 infoPtr
->pData
[nCaretBytePos
] = (orig_byte
& 0xf0) | digit
;
314 else /* set high nibble */
315 infoPtr
->pData
[nCaretBytePos
] = (orig_byte
& 0x0f) | digit
<< 4;
316 infoPtr
->nCaretPos
++; /* advance one nibble */
320 infoPtr
->pData
[nCaretBytePos
] = (BYTE
)ch
;
321 infoPtr
->nCaretPos
+= 2; /* advance two nibbles */
325 HexEdit_UpdateScrollbars(infoPtr
);
326 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
327 HexEdit_UpdateCaret(infoPtr
);
328 HexEdit_EnsureVisible(infoPtr
, infoPtr
->nCaretPos
);
332 static inline LRESULT
333 HexEdit_Destroy (HEXEDIT_INFO
*infoPtr
)
335 HWND hwnd
= infoPtr
->hwndSelf
;
336 heap_free(infoPtr
->pData
);
339 SetWindowLongPtrW(hwnd
, 0, 0);
343 static inline LRESULT
344 HexEdit_GetFont (HEXEDIT_INFO
*infoPtr
)
346 return (LRESULT
)infoPtr
->hFont
;
349 static inline LRESULT
350 HexEdit_KeyDown (HEXEDIT_INFO
*infoPtr
, DWORD key
, DWORD flags
)
352 INT nInc
= (infoPtr
->bFocusHex
) ? 1 : 2;
358 infoPtr
->nCaretPos
-= nInc
;
359 if (infoPtr
->nCaretPos
< 0)
360 infoPtr
->nCaretPos
= 0;
363 infoPtr
->nCaretPos
+= nInc
;
364 if (infoPtr
->nCaretPos
> infoPtr
->cbData
*2)
365 infoPtr
->nCaretPos
= infoPtr
->cbData
*2;
368 if ((infoPtr
->nCaretPos
- infoPtr
->nBytesPerLine
*2) >= 0)
369 infoPtr
->nCaretPos
-= infoPtr
->nBytesPerLine
*2;
372 if ((infoPtr
->nCaretPos
+ infoPtr
->nBytesPerLine
*2) <= infoPtr
->cbData
*2)
373 infoPtr
->nCaretPos
+= infoPtr
->nBytesPerLine
*2;
376 infoPtr
->nCaretPos
= 0;
379 infoPtr
->nCaretPos
= infoPtr
->cbData
*2;
381 case VK_PRIOR
: /* page up */
382 si
.cbSize
= sizeof(si
);
384 GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &si
);
385 if ((infoPtr
->nCaretPos
- (INT
)si
.nPage
*infoPtr
->nBytesPerLine
*2) >= 0)
386 infoPtr
->nCaretPos
-= si
.nPage
*infoPtr
->nBytesPerLine
*2;
388 infoPtr
->nCaretPos
= 0;
390 case VK_NEXT
: /* page down */
391 si
.cbSize
= sizeof(si
);
393 GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &si
);
394 if ((infoPtr
->nCaretPos
+ (INT
)si
.nPage
*infoPtr
->nBytesPerLine
*2) <= infoPtr
->cbData
*2)
395 infoPtr
->nCaretPos
+= si
.nPage
*infoPtr
->nBytesPerLine
*2;
397 infoPtr
->nCaretPos
= infoPtr
->cbData
*2;
403 HexEdit_UpdateCaret(infoPtr
);
404 HexEdit_EnsureVisible(infoPtr
, infoPtr
->nCaretPos
);
410 static inline LRESULT
411 HexEdit_KillFocus (HEXEDIT_INFO
*infoPtr
, HWND receiveFocus
)
413 infoPtr
->bFocus
= FALSE
;
420 static inline LRESULT
421 HexEdit_LButtonDown (HEXEDIT_INFO
*infoPtr
)
423 SetFocus(infoPtr
->hwndSelf
);
425 /* FIXME: hittest and set caret */
431 static inline LRESULT
HexEdit_NCCreate (HWND hwnd
, LPCREATESTRUCTW lpcs
)
433 HEXEDIT_INFO
*infoPtr
;
434 SetWindowLongW(hwnd
, GWL_EXSTYLE
,
435 lpcs
->dwExStyle
| WS_EX_CLIENTEDGE
);
437 /* allocate memory for info structure */
438 infoPtr
= heap_xalloc(sizeof(HEXEDIT_INFO
));
439 memset(infoPtr
, 0, sizeof(HEXEDIT_INFO
));
440 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
442 /* initialize info structure */
443 infoPtr
->nCaretPos
= 0;
444 infoPtr
->hwndSelf
= hwnd
;
445 infoPtr
->nBytesPerLine
= 2;
446 infoPtr
->bFocusHex
= TRUE
;
447 infoPtr
->bInsert
= TRUE
;
449 return DefWindowProcW(infoPtr
->hwndSelf
, WM_NCCREATE
, 0, (LPARAM
)lpcs
);
452 static inline LRESULT
453 HexEdit_SetFocus (HEXEDIT_INFO
*infoPtr
, HWND lostFocus
)
455 infoPtr
->bFocus
= TRUE
;
457 CreateCaret(infoPtr
->hwndSelf
, NULL
, 1, infoPtr
->nHeight
);
458 HexEdit_UpdateCaret(infoPtr
);
459 ShowCaret(infoPtr
->hwndSelf
);
465 static inline LRESULT
466 HexEdit_SetFont (HEXEDIT_INFO
*infoPtr
, HFONT hFont
, BOOL redraw
)
470 HFONT hOldFont
= NULL
;
474 infoPtr
->hFont
= hFont
;
476 hdc
= GetDC(infoPtr
->hwndSelf
);
478 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
480 GetTextMetricsW(hdc
, &tm
);
481 infoPtr
->nHeight
= tm
.tmHeight
+ tm
.tmExternalLeading
;
483 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
487 BYTE
*pData
= heap_xalloc(i
);
492 lpszLine
= HexEdit_GetLineText(0, pData
, i
, 0);
493 GetTextExtentPoint32W(hdc
, lpszLine
, lstrlenW(lpszLine
), &size
);
496 if (size
.cx
> (rcClient
.right
- rcClient
.left
))
498 infoPtr
->nBytesPerLine
= i
- 1;
503 HexEdit_UpdateScrollbars(infoPtr
);
506 SelectObject(hdc
, hOldFont
);
507 ReleaseDC (infoPtr
->hwndSelf
, hdc
);
510 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
515 static inline LRESULT
516 HexEdit_VScroll (HEXEDIT_INFO
*infoPtr
, INT action
)
520 /* get all scroll bar info */
521 si
.cbSize
= sizeof(si
);
523 GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &si
);
525 switch (LOWORD(action
))
527 case SB_TOP
: /* user pressed the home key */
531 case SB_BOTTOM
: /* user pressed the end key */
535 case SB_LINEUP
: /* user clicked the up arrow */
539 case SB_LINEDOWN
: /* user clicked the down arrow */
543 case SB_PAGEUP
: /* user clicked the scroll bar above the scroll thumb */
547 case SB_PAGEDOWN
: /* user clicked the scroll bar below the scroll thumb */
551 case SB_THUMBTRACK
: /* user dragged the scroll thumb */
552 si
.nPos
= si
.nTrackPos
;
559 /* set the position and then retrieve it to let the system handle the
560 * cases where the position is out of range */
562 SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &si
, TRUE
);
563 GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &si
);
565 if (si
.nPos
!= infoPtr
->nScrollPos
)
567 ScrollWindow(infoPtr
->hwndSelf
, 0, infoPtr
->nHeight
* (infoPtr
->nScrollPos
- si
.nPos
), NULL
, NULL
);
568 infoPtr
->nScrollPos
= si
.nPos
;
569 UpdateWindow(infoPtr
->hwndSelf
);
571 /* need to update caret position since it depends on the scroll position */
572 HexEdit_UpdateCaret(infoPtr
);
578 static LRESULT WINAPI
579 HexEdit_WindowProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
581 HEXEDIT_INFO
*infoPtr
= (HEXEDIT_INFO
*)GetWindowLongPtrW(hwnd
, 0);
583 if (!infoPtr
&& (uMsg
!= WM_NCCREATE
))
584 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
589 return HexEdit_SetData (infoPtr
, (INT
)wParam
, (const BYTE
*)lParam
);
592 return HexEdit_GetData (infoPtr
, (INT
)wParam
, (BYTE
*)lParam
);
595 return HexEdit_Char (infoPtr
, (WCHAR
)wParam
);
598 return HexEdit_Destroy (infoPtr
);
601 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
604 return HexEdit_GetFont (infoPtr
);
607 return HexEdit_KeyDown (infoPtr
, wParam
, lParam
);
610 return HexEdit_KillFocus (infoPtr
, (HWND
)wParam
);
613 return HexEdit_LButtonDown (infoPtr
);
616 return HexEdit_NCCreate (hwnd
, (LPCREATESTRUCTW
)lParam
);
619 HexEdit_Paint(infoPtr
);
623 return HexEdit_SetFocus (infoPtr
, (HWND
)wParam
);
626 return HexEdit_SetFont (infoPtr
, (HFONT
)wParam
, LOWORD(lParam
));
629 return HexEdit_VScroll (infoPtr
, (INT
)wParam
);
632 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
637 void HexEdit_Register(void)
641 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
643 wndClass
.lpfnWndProc
= HexEdit_WindowProc
;
644 wndClass
.cbClsExtra
= 0;
645 wndClass
.cbWndExtra
= sizeof(HEXEDIT_INFO
*);
646 wndClass
.hCursor
= LoadCursorW(0, (const WCHAR
*)IDC_IBEAM
);
647 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
648 wndClass
.lpszClassName
= szHexEditClass
;
650 RegisterClassW(&wndClass
);