Sync with trunk 48067
[reactos.git] / base / applications / regedit / hexedit.c
1 /*
2 * Hex editor control
3 *
4 * Copyright (C) 2004 Thomas Weidenmueller <w3seek@reactos.com>
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 Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <regedit.h>
22 typedef struct
23 {
24 HWND hWndSelf;
25 HWND hWndParent;
26 HLOCAL hBuffer;
27 DWORD style;
28 DWORD MaxBuffer;
29 INT ColumnsPerLine;
30 INT nLines;
31 INT nVisibleLinesComplete;
32 INT nVisibleLines;
33 INT Index;
34 INT LineHeight;
35 INT CharWidth;
36 HFONT hFont;
37 BOOL SbVisible;
38
39 INT LeftMargin;
40 INT AddressSpacing;
41 INT SplitSpacing;
42
43 BOOL EditingField;
44 INT CaretCol;
45 INT CaretLine;
46 BOOL InMid;
47
48 INT SelStart;
49 INT SelEnd;
50 BOOL SelOnField;
51 } HEXEDIT_DATA, *PHEXEDIT_DATA;
52
53 /* hit test codes */
54 #define HEHT_LEFTMARGIN (0x1)
55 #define HEHT_ADDRESS (0x2)
56 #define HEHT_ADDRESSSPACING (0x3)
57 #define HEHT_HEXDUMP (0x4)
58 #define HEHT_HEXDUMPSPACING (0x5)
59 #define HEHT_ASCIIDUMP (0x6)
60 #define HEHT_RIGHTMARGIN (0x7)
61
62 INT_PTR CALLBACK HexEditWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
63
64 ATOM
65 WINAPI
66 RegisterHexEditorClass(HINSTANCE hInstance)
67 {
68 WNDCLASSEX WndClass;
69
70 ZeroMemory(&WndClass, sizeof(WNDCLASSEX));
71 WndClass.cbSize = sizeof(WNDCLASSEX);
72 WndClass.style = CS_DBLCLKS;
73 WndClass.lpfnWndProc = (WNDPROC)HexEditWndProc;
74 WndClass.cbWndExtra = sizeof(PHEXEDIT_DATA);
75 WndClass.hInstance = hInstance;
76 WndClass.hCursor = LoadCursor(0, IDC_IBEAM);
77 WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
78 WndClass.lpszClassName = HEX_EDIT_CLASS_NAME;
79
80 return RegisterClassEx(&WndClass);
81 }
82
83 BOOL
84 WINAPI
85 UnregisterHexEditorClass(HINSTANCE hInstance)
86 {
87 return UnregisterClass(HEX_EDIT_CLASS_NAME, hInstance);
88 }
89
90 /*** Helper functions *********************************************************/
91
92 static VOID
93 HEXEDIT_MoveCaret(PHEXEDIT_DATA hed, BOOL Scroll)
94 {
95 SCROLLINFO si;
96
97 si.cbSize = sizeof(SCROLLINFO);
98 si.fMask = SIF_POS;
99 GetScrollInfo(hed->hWndSelf, SB_VERT, &si);
100
101 if(Scroll)
102 {
103 if(si.nPos > hed->CaretLine)
104 {
105 si.nPos = hed->CaretLine;
106 SetScrollInfo(hed->hWndSelf, SB_VERT, &si, TRUE);
107 GetScrollInfo(hed->hWndSelf, SB_VERT, &si);
108 InvalidateRect(hed->hWndSelf, NULL, TRUE);
109 }
110 else if(hed->CaretLine >= (hed->nVisibleLinesComplete + si.nPos))
111 {
112 si.nPos = hed->CaretLine - hed->nVisibleLinesComplete + 1;
113 SetScrollInfo(hed->hWndSelf, SB_VERT, &si, TRUE);
114 GetScrollInfo(hed->hWndSelf, SB_VERT, &si);
115 InvalidateRect(hed->hWndSelf, NULL, TRUE);
116 }
117 }
118
119 if(hed->EditingField)
120 SetCaretPos(hed->LeftMargin + ((4 + hed->AddressSpacing + (3 * hed->CaretCol) + hed->InMid * 2) * hed->CharWidth) - 1, (hed->CaretLine - si.nPos) * hed->LineHeight);
121 else
122 SetCaretPos(hed->LeftMargin + ((4 + hed->AddressSpacing + hed->SplitSpacing + (3 * hed->ColumnsPerLine) + hed->CaretCol) * hed->CharWidth) - 2, (hed->CaretLine - si.nPos) * hed->LineHeight);
123 }
124
125 static VOID
126 HEXEDIT_Update(PHEXEDIT_DATA hed)
127 {
128 SCROLLINFO si;
129 RECT rcClient;
130 BOOL SbVisible;
131 INT bufsize, cvislines;
132
133 GetClientRect(hed->hWndSelf, &rcClient);
134 hed->style = GetWindowLongPtr(hed->hWndSelf, GWL_STYLE);
135
136 bufsize = (hed->hBuffer ? (INT) LocalSize(hed->hBuffer) : 0);
137 hed->nLines = max(bufsize / hed->ColumnsPerLine, 1);
138 if(bufsize > hed->ColumnsPerLine && (bufsize % hed->ColumnsPerLine) > 0)
139 {
140 hed->nLines++;
141 }
142
143 if(hed->LineHeight > 0)
144 {
145 hed->nVisibleLinesComplete = cvislines = rcClient.bottom / hed->LineHeight;
146 hed->nVisibleLines = hed->nVisibleLinesComplete;
147 if(rcClient.bottom % hed->LineHeight)
148 {
149 hed->nVisibleLines++;
150 }
151 }
152 else
153 {
154 hed->nVisibleLines = cvislines = 0;
155 }
156
157 SbVisible = bufsize > 0 && cvislines < hed->nLines;
158 ShowScrollBar(hed->hWndSelf, SB_VERT, SbVisible);
159
160 /* update scrollbar */
161 si.cbSize = sizeof(SCROLLINFO);
162 si.fMask = SIF_RANGE | SIF_PAGE;
163 si.nMin = 0;
164 si.nMax = ((bufsize > 0) ? hed->nLines - 1 : 0);
165 si.nPage = ((hed->LineHeight > 0) ? rcClient.bottom / hed->LineHeight : 0);
166 SetScrollInfo(hed->hWndSelf, SB_VERT, &si, TRUE);
167
168 if(IsWindowVisible(hed->hWndSelf) && SbVisible != hed->SbVisible)
169 {
170 InvalidateRect(hed->hWndSelf, NULL, TRUE);
171 }
172
173 hed->SbVisible = SbVisible;
174 }
175
176 static HFONT
177 HEXEDIT_GetFixedFont(VOID)
178 {
179 LOGFONT lf;
180 GetObject(GetStockObject(ANSI_FIXED_FONT), sizeof(LOGFONT), &lf);
181 return CreateFontIndirect(&lf);
182 }
183
184 static VOID
185 HEXEDIT_PaintLines(PHEXEDIT_DATA hed, HDC hDC, DWORD ScrollPos, DWORD First, DWORD Last, RECT *rc)
186 {
187 DWORD dx, dy, linestart;
188 INT i, isave, i0, i1, x;
189 PBYTE buf, current, end, line;
190 size_t bufsize;
191 TCHAR hex[3], addr[17];
192 RECT rct, rct2;
193
194 FillRect(hDC, rc, (HBRUSH)(COLOR_WINDOW + 1));
195 SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
196
197 if (hed->SelStart < hed->SelEnd)
198 {
199 i0 = hed->SelStart;
200 i1 = hed->SelEnd;
201 }
202 else
203 {
204 i0 = hed->SelEnd;
205 i1 = hed->SelStart;
206 }
207
208 if(hed->hBuffer)
209 {
210 bufsize = LocalSize(hed->hBuffer);
211 buf = LocalLock(hed->hBuffer);
212 }
213 else
214 {
215 buf = NULL;
216 bufsize = 0;
217
218 if(ScrollPos + First == 0)
219 {
220 /* draw address */
221 _stprintf(addr, _T("%04X"), 0);
222 TextOut(hDC, hed->LeftMargin, First * hed->LineHeight, addr, 4);
223 }
224 }
225
226 if(buf)
227 {
228 end = buf + bufsize;
229 dy = First * hed->LineHeight;
230 linestart = (ScrollPos + First) * hed->ColumnsPerLine;
231 i = linestart;
232 current = buf + linestart;
233 Last = min(hed->nLines - ScrollPos, Last);
234
235 SetBkMode(hDC, TRANSPARENT);
236 while(First <= Last && current < end)
237 {
238 DWORD dh;
239
240 dx = hed->LeftMargin;
241
242 /* draw address */
243 _stprintf(addr, _T("%04lX"), linestart);
244 TextOut(hDC, dx, dy, addr, 4);
245
246 dx += ((4 + hed->AddressSpacing) * hed->CharWidth);
247 dh = (3 * hed->CharWidth);
248
249 rct.left = dx;
250 rct.top = dy;
251 rct.right = rct.left + dh;
252 rct.bottom = dy + hed->LineHeight;
253
254 /* draw hex map */
255 dx += (hed->CharWidth / 2);
256 line = current;
257 isave = i;
258 for(x = 0; x < hed->ColumnsPerLine && current < end; x++)
259 {
260 rct.left += dh;
261 rct.right += dh;
262
263 _stprintf(hex, _T("%02X"), *(current++));
264 if (i0 <= i && i < i1)
265 {
266 rct2.left = dx;
267 rct2.top = dy;
268 rct2.right = dx + hed->CharWidth * 2 + 1;
269 rct2.bottom = dy + hed->LineHeight;
270 InflateRect(&rct2, hed->CharWidth / 2, 0);
271 FillRect(hDC, &rct2, (HBRUSH)(COLOR_HIGHLIGHT + 1));
272 SetTextColor(hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
273 ExtTextOut(hDC, dx, dy, 0, &rct, hex, 2, NULL);
274 SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
275 }
276 else
277 ExtTextOut(hDC, dx, dy, ETO_OPAQUE, &rct, hex, 2, NULL);
278 dx += dh;
279 i++;
280 }
281
282 /* draw ascii map */
283 dx = ((4 + hed->AddressSpacing + hed->SplitSpacing + (hed->ColumnsPerLine * 3)) * hed->CharWidth);
284 current = line;
285 i = isave;
286 for(x = 0; x < hed->ColumnsPerLine && current < end; x++)
287 {
288 _stprintf(hex, _T("%C"), *(current++));
289 hex[0] = ((hex[0] & _T('\x007f')) >= _T(' ') ? hex[0] : _T('.'));
290 if (i0 <= i && i < i1)
291 {
292 rct2.left = dx;
293 rct2.top = dy;
294 rct2.right = dx + hed->CharWidth;
295 rct2.bottom = dy + hed->LineHeight;
296 FillRect(hDC, &rct2, (HBRUSH)(COLOR_HIGHLIGHT + 1));
297 SetTextColor(hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
298 TextOut(hDC, dx, dy, hex, 1);
299 SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
300 }
301 else
302 TextOut(hDC, dx, dy, hex, 1);
303 dx += hed->CharWidth;
304 i++;
305 }
306
307 dy += hed->LineHeight;
308 linestart += hed->ColumnsPerLine;
309 First++;
310 }
311 }
312
313 LocalUnlock(hed->hBuffer);
314 }
315
316 static DWORD
317 HEXEDIT_HitRegionTest(PHEXEDIT_DATA hed, POINTS pt)
318 {
319 int d;
320
321 if(pt.x <= hed->LeftMargin)
322 {
323 return HEHT_LEFTMARGIN;
324 }
325
326 pt.x -= hed->LeftMargin;
327 d = (4 * hed->CharWidth);
328 if(pt.x <= d)
329 {
330 return HEHT_ADDRESS;
331 }
332
333 pt.x -= d;
334 d = (hed->AddressSpacing * hed->CharWidth);
335 if(pt.x <= d)
336 {
337 return HEHT_ADDRESSSPACING;
338 }
339
340 pt.x -= d;
341 d = ((3 * hed->ColumnsPerLine + 1) * hed->CharWidth);
342 if(pt.x <= d)
343 {
344 return HEHT_HEXDUMP;
345 }
346
347 pt.x -= d;
348 d = ((hed->SplitSpacing - 1) * hed->CharWidth);
349 if(pt.x <= d)
350 {
351 return HEHT_HEXDUMPSPACING;
352 }
353
354 pt.x -= d;
355 d = (hed->ColumnsPerLine * hed->CharWidth);
356 if(pt.x <= d)
357 {
358 return HEHT_ASCIIDUMP;
359 }
360
361 return HEHT_RIGHTMARGIN;
362 }
363
364 static DWORD
365 HEXEDIT_IndexFromPoint(PHEXEDIT_DATA hed, POINTS pt, DWORD Hit, POINT *EditPos, BOOL *EditField)
366 {
367 SCROLLINFO si;
368 DWORD Index, bufsize;
369
370 si.cbSize = sizeof(SCROLLINFO);
371 si.fMask = SIF_POS;
372 GetScrollInfo(hed->hWndSelf, SB_VERT, &si);
373
374 EditPos->x = 0;
375
376 if(hed->LineHeight > 0)
377 {
378 EditPos->y = min(si.nPos + (pt.y / hed->LineHeight), hed->nLines - 1);
379 }
380 else
381 {
382 EditPos->y = si.nPos;
383 }
384
385 switch(Hit)
386 {
387 case HEHT_LEFTMARGIN:
388 case HEHT_ADDRESS:
389 case HEHT_ADDRESSSPACING:
390 case HEHT_HEXDUMP:
391 pt.x -= (SHORT) hed->LeftMargin + ((4 + hed->AddressSpacing) * hed->CharWidth);
392 *EditField = TRUE;
393 break;
394
395 default:
396 pt.x -= hed->LeftMargin + ((4 + hed->AddressSpacing + hed->SplitSpacing + (3 * hed->ColumnsPerLine)) * hed->CharWidth);
397 *EditField = FALSE;
398 break;
399 }
400
401 if(pt.x > 0)
402 {
403 INT BlockWidth = (*EditField ? hed->CharWidth * 3 : hed->CharWidth);
404 EditPos->x = min(hed->ColumnsPerLine, (pt.x + BlockWidth / 2) / BlockWidth);
405 }
406
407 bufsize = (hed->hBuffer ? (DWORD) LocalSize(hed->hBuffer) : 0);
408 Index = (EditPos->y * hed->ColumnsPerLine) + EditPos->x;
409 if(Index > bufsize)
410 {
411 INT tmp = bufsize % hed->ColumnsPerLine;
412 Index = bufsize;
413 EditPos->x = (tmp == 0 ? hed->ColumnsPerLine : tmp);
414 }
415 return Index;
416 }
417
418 /*** Control specific messages ************************************************/
419
420 static LRESULT
421 HEXEDIT_HEM_LOADBUFFER(PHEXEDIT_DATA hed, PVOID Buffer, DWORD Size)
422 {
423 if(Buffer != NULL && Size > 0)
424 {
425 LPVOID buf;
426
427 if(hed->MaxBuffer > 0 && Size > hed->MaxBuffer)
428 {
429 Size = hed->MaxBuffer;
430 }
431
432 if(hed->hBuffer)
433 {
434 if(Size > 0)
435 {
436 if(LocalSize(hed->hBuffer) != Size)
437 {
438 hed->hBuffer = LocalReAlloc(hed->hBuffer, Size, LMEM_MOVEABLE | LMEM_ZEROINIT);
439 }
440 }
441 else
442 {
443 hed->hBuffer = LocalFree(hed->hBuffer);
444 hed->Index = 0;
445 HEXEDIT_Update(hed);
446
447 return 0;
448 }
449 }
450 else if(Size > 0)
451 {
452 hed->hBuffer = LocalAlloc(LHND, Size);
453 }
454
455 if(Size > 0)
456 {
457 buf = LocalLock(hed->hBuffer);
458 if(buf)
459 {
460 memcpy(buf, Buffer, Size);
461 }
462 else
463 Size = 0;
464 LocalUnlock(hed->hBuffer);
465 }
466
467 hed->Index = 0;
468 HEXEDIT_Update(hed);
469 return Size;
470 }
471 else if(hed->hBuffer)
472 {
473 hed->Index = 0;
474 hed->hBuffer = LocalFree(hed->hBuffer);
475 HEXEDIT_Update(hed);
476 }
477
478 return 0;
479 }
480
481 static LRESULT
482 HEXEDIT_HEM_COPYBUFFER(PHEXEDIT_DATA hed, PVOID Buffer, DWORD Size)
483 {
484 size_t nCpy;
485
486 if(!hed->hBuffer)
487 {
488 return 0;
489 }
490
491 if(Buffer != NULL && Size > 0)
492 {
493 nCpy = min(Size, LocalSize(hed->hBuffer));
494 if(nCpy > 0)
495 {
496 PVOID buf;
497
498 buf = LocalLock(hed->hBuffer);
499 if(buf)
500 {
501 memcpy(Buffer, buf, nCpy);
502 }
503 else
504 nCpy = 0;
505 LocalUnlock(hed->hBuffer);
506 }
507 return nCpy;
508 }
509
510 return (LRESULT)LocalSize(hed->hBuffer);
511 }
512
513 static LRESULT
514 HEXEDIT_HEM_SETMAXBUFFERSIZE(PHEXEDIT_DATA hed, DWORD nMaxSize)
515 {
516 hed->MaxBuffer = nMaxSize;
517 if (hed->MaxBuffer == 0)
518 {
519 hed->hBuffer = LocalFree(hed->hBuffer);
520 return 0;
521 }
522 if (hed->hBuffer)
523 hed->hBuffer = LocalReAlloc(hed->hBuffer, hed->MaxBuffer, LMEM_MOVEABLE);
524 else
525 hed->hBuffer = LocalAlloc(LMEM_MOVEABLE, hed->MaxBuffer);
526 HEXEDIT_Update(hed);
527 return 0;
528 }
529
530 /*** Message Proc *************************************************************/
531
532 static LRESULT
533 HEXEDIT_WM_NCCREATE(HWND hWnd, CREATESTRUCT *cs)
534 {
535 PHEXEDIT_DATA hed;
536
537 if(!(hed = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEXEDIT_DATA))))
538 {
539 return FALSE;
540 }
541
542 hed->hWndSelf = hWnd;
543 hed->hWndParent = cs->hwndParent;
544 hed->style = cs->style;
545
546 hed->ColumnsPerLine = 8;
547 hed->LeftMargin = 2;
548 hed->AddressSpacing = 2;
549 hed->SplitSpacing = 2;
550 hed->EditingField = TRUE; /* in hexdump field */
551
552 SetWindowLongPtr(hWnd, 0, (DWORD_PTR)hed);
553 HEXEDIT_Update(hed);
554
555 return TRUE;
556 }
557
558 static LRESULT
559 HEXEDIT_WM_NCDESTROY(PHEXEDIT_DATA hed)
560 {
561 if(hed->hBuffer)
562 {
563 //while(LocalUnlock(hed->hBuffer));
564 LocalFree(hed->hBuffer);
565 }
566
567 if(hed->hFont)
568 {
569 DeleteObject(hed->hFont);
570 }
571
572 SetWindowLongPtr(hed->hWndSelf, 0, (DWORD_PTR)0);
573 HeapFree(GetProcessHeap(), 0, hed);
574
575 return 0;
576 }
577
578 static LRESULT
579 HEXEDIT_WM_CREATE(PHEXEDIT_DATA hed)
580 {
581 UNREFERENCED_PARAMETER(hed);
582 return 1;
583 }
584
585 static LRESULT
586 HEXEDIT_WM_SETFOCUS(PHEXEDIT_DATA hed)
587 {
588 CreateCaret(hed->hWndSelf, 0, 1, hed->LineHeight);
589 HEXEDIT_MoveCaret(hed, FALSE);
590 ShowCaret(hed->hWndSelf);
591 return 0;
592 }
593
594 static LRESULT
595 HEXEDIT_WM_KILLFOCUS(PHEXEDIT_DATA hed)
596 {
597 UNREFERENCED_PARAMETER(hed);
598 DestroyCaret();
599 return 0;
600 }
601
602 static LRESULT
603 HEXEDIT_WM_VSCROLL(PHEXEDIT_DATA hed, WORD ThumbPosition, WORD SbCmd)
604 {
605 int ScrollY;
606 SCROLLINFO si;
607
608 UNREFERENCED_PARAMETER(ThumbPosition);
609
610 ZeroMemory(&si, sizeof(SCROLLINFO));
611 si.cbSize = sizeof(SCROLLINFO);
612 si.fMask = SIF_ALL;
613 GetScrollInfo(hed->hWndSelf, SB_VERT, &si);
614
615 ScrollY = si.nPos;
616 switch(SbCmd)
617 {
618 case SB_TOP:
619 si.nPos = si.nMin;
620 break;
621
622 case SB_BOTTOM:
623 si.nPos = si.nMax;
624 break;
625
626 case SB_LINEUP:
627 si.nPos--;
628 break;
629
630 case SB_LINEDOWN:
631 si.nPos++;
632 break;
633
634 case SB_PAGEUP:
635 si.nPos -= si.nPage;
636 break;
637
638 case SB_PAGEDOWN:
639 si.nPos += si.nPage;
640 break;
641
642 case SB_THUMBTRACK:
643 si.nPos = si.nTrackPos;
644 break;
645 }
646
647 si.fMask = SIF_POS;
648 SetScrollInfo(hed->hWndSelf, SB_VERT, &si, TRUE);
649 GetScrollInfo(hed->hWndSelf, SB_VERT, &si);
650
651 if(si.nPos != ScrollY)
652 {
653 ScrollWindow(hed->hWndSelf, 0, (ScrollY - si.nPos) * hed->LineHeight, NULL, NULL);
654 UpdateWindow(hed->hWndSelf);
655 }
656
657 return 0;
658 }
659
660 static LRESULT
661 HEXEDIT_WM_SETFONT(PHEXEDIT_DATA hed, HFONT hFont, BOOL bRedraw)
662 {
663 HDC hDC;
664 TEXTMETRIC tm;
665 HFONT hOldFont = 0;
666
667 if(hFont == 0)
668 {
669 hFont = HEXEDIT_GetFixedFont();
670 }
671
672 hed->hFont = hFont;
673 hDC = GetDC(hed->hWndSelf);
674 if(hFont)
675 {
676 hOldFont = SelectObject(hDC, hFont);
677 }
678 GetTextMetrics(hDC, &tm);
679 hed->LineHeight = tm.tmHeight;
680 hed->CharWidth = tm.tmAveCharWidth;
681 if(hOldFont)
682 {
683 SelectObject(hDC, hOldFont);
684 }
685 ReleaseDC(hed->hWndSelf, hDC);
686
687 if(bRedraw)
688 {
689 InvalidateRect(hed->hWndSelf, NULL, TRUE);
690 }
691
692 return 0;
693 }
694
695 static LRESULT
696 HEXEDIT_WM_GETFONT(PHEXEDIT_DATA hed)
697 {
698 return (LRESULT)hed->hFont;
699 }
700
701 static LRESULT
702 HEXEDIT_WM_PAINT(PHEXEDIT_DATA hed)
703 {
704 PAINTSTRUCT ps;
705 SCROLLINFO si;
706 RECT rc;
707 HBITMAP hbmp, hbmpold;
708 INT nLines, nFirst;
709 HFONT hOldFont;
710 HDC hTempDC;
711 DWORD height;
712
713 if(GetUpdateRect(hed->hWndSelf, &rc, FALSE) && (hed->LineHeight > 0))
714 {
715 ZeroMemory(&si, sizeof(SCROLLINFO));
716 si.cbSize = sizeof(SCROLLINFO);
717 si.fMask = SIF_POS;
718 GetScrollInfo(hed->hWndSelf, SB_VERT, &si);
719
720 height = (rc.bottom - rc.top);
721 nLines = height / hed->LineHeight;
722 if((height % hed->LineHeight) > 0)
723 {
724 nLines++;
725 }
726 if(nLines > hed->nLines - si.nPos)
727 {
728 nLines = hed->nLines - si.nPos;
729 }
730 nFirst = rc.top / hed->LineHeight;
731
732 BeginPaint(hed->hWndSelf, &ps);
733 if(!(hTempDC = CreateCompatibleDC(ps.hdc)))
734 {
735 FillRect(ps.hdc, &rc, (HBRUSH)(COLOR_WINDOW + 1));
736 goto epaint;
737 }
738 if(!(hbmp = CreateCompatibleBitmap(ps.hdc, ps.rcPaint.right, ps.rcPaint.bottom)))
739 {
740 FillRect(ps.hdc, &rc, (HBRUSH)(COLOR_WINDOW + 1));
741 DeleteDC(hTempDC);
742 goto epaint;
743 }
744 hbmpold = SelectObject(hTempDC, hbmp);
745 hOldFont = SelectObject(hTempDC, hed->hFont);
746 HEXEDIT_PaintLines(hed, hTempDC, si.nPos, nFirst, nFirst + nLines, &ps.rcPaint);
747 BitBlt(ps.hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, hTempDC, rc.left, rc.top, SRCCOPY);
748 SelectObject(hTempDC, hOldFont);
749 SelectObject(hTempDC, hbmpold);
750
751 DeleteObject(hbmp);
752 DeleteDC(hTempDC);
753
754 epaint:
755 EndPaint(hed->hWndSelf, &ps);
756 }
757
758 return 0;
759 }
760
761 static LRESULT
762 HEXEDIT_WM_MOUSEWHEEL(PHEXEDIT_DATA hed, int cyMoveLines, WORD ButtonsDown, LPPOINTS MousePos)
763 {
764 SCROLLINFO si;
765 int ScrollY;
766
767 UNREFERENCED_PARAMETER(ButtonsDown);
768 UNREFERENCED_PARAMETER(MousePos);
769
770 SetFocus(hed->hWndSelf);
771
772 si.cbSize = sizeof(SCROLLINFO);
773 si.fMask = SIF_ALL;
774 GetScrollInfo(hed->hWndSelf, SB_VERT, &si);
775
776 ScrollY = si.nPos;
777
778 si.fMask = SIF_POS;
779 si.nPos += cyMoveLines;
780 SetScrollInfo(hed->hWndSelf, SB_VERT, &si, TRUE);
781
782 GetScrollInfo(hed->hWndSelf, SB_VERT, &si);
783 if(si.nPos != ScrollY)
784 {
785 ScrollWindow(hed->hWndSelf, 0, (ScrollY - si.nPos) * hed->LineHeight, NULL, NULL);
786 UpdateWindow(hed->hWndSelf);
787 }
788
789 return 0;
790 }
791
792 static LRESULT
793 HEXEDIT_WM_GETDLGCODE(LPMSG Msg)
794 {
795 UNREFERENCED_PARAMETER(Msg);
796 return DLGC_WANTARROWS | DLGC_WANTCHARS;
797 }
798
799 static LRESULT
800 HEXEDIT_WM_LBUTTONDOWN(PHEXEDIT_DATA hed, INT Buttons, POINTS Pt)
801 {
802 BOOL NewField;
803 POINT EditPos;
804 DWORD Hit;
805
806 UNREFERENCED_PARAMETER(Buttons);
807 SetFocus(hed->hWndSelf);
808
809 if (GetAsyncKeyState(VK_SHIFT) < 0)
810 {
811 if (hed->SelOnField)
812 hed->Index = HEXEDIT_IndexFromPoint(hed, Pt, HEHT_HEXDUMP, &EditPos, &NewField);
813 else
814 hed->Index = HEXEDIT_IndexFromPoint(hed, Pt, HEHT_ASCIIDUMP, &EditPos, &NewField);
815 hed->SelEnd = hed->Index;
816 hed->EditingField = hed->SelOnField;
817 }
818 else
819 {
820 Hit = HEXEDIT_HitRegionTest(hed, Pt);
821 hed->Index = HEXEDIT_IndexFromPoint(hed, Pt, Hit, &EditPos, &NewField);
822 hed->SelStart = hed->SelEnd = hed->Index;
823 hed->SelOnField = hed->EditingField = NewField;
824 SetCapture(hed->hWndSelf);
825 }
826 hed->CaretCol = EditPos.x;
827 hed->CaretLine = EditPos.y;
828 hed->InMid = FALSE;
829 InvalidateRect(hed->hWndSelf, NULL, FALSE);
830 HEXEDIT_MoveCaret(hed, TRUE);
831
832 return 0;
833 }
834
835 static LRESULT
836 HEXEDIT_WM_LBUTTONUP(PHEXEDIT_DATA hed, INT Buttons, POINTS Pt)
837 {
838 BOOL NewField;
839 POINT EditPos;
840 if (GetCapture() == hed->hWndSelf)
841 {
842 if (hed->SelOnField)
843 hed->Index = HEXEDIT_IndexFromPoint(hed, Pt, HEHT_HEXDUMP, &EditPos, &NewField);
844 else
845 hed->Index = HEXEDIT_IndexFromPoint(hed, Pt, HEHT_ASCIIDUMP, &EditPos, &NewField);
846 hed->CaretCol = EditPos.x;
847 hed->CaretLine = EditPos.y;
848 hed->SelEnd = hed->Index;
849 ReleaseCapture();
850 InvalidateRect(hed->hWndSelf, NULL, FALSE);
851 HEXEDIT_MoveCaret(hed, TRUE);
852 }
853 return 0;
854 }
855
856 static LRESULT
857 HEXEDIT_WM_MOUSEMOVE(PHEXEDIT_DATA hed, INT Buttons, POINTS Pt)
858 {
859 BOOL NewField;
860 POINT EditPos;
861 if (GetCapture() == hed->hWndSelf)
862 {
863 if (hed->SelOnField)
864 hed->Index = HEXEDIT_IndexFromPoint(hed, Pt, HEHT_HEXDUMP, &EditPos, &NewField);
865 else
866 hed->Index = HEXEDIT_IndexFromPoint(hed, Pt, HEHT_ASCIIDUMP, &EditPos, &NewField);
867 hed->CaretCol = EditPos.x;
868 hed->CaretLine = EditPos.y;
869 hed->SelEnd = hed->Index;
870 InvalidateRect(hed->hWndSelf, NULL, FALSE);
871 HEXEDIT_MoveCaret(hed, TRUE);
872 }
873 return 0;
874 }
875
876 static BOOL
877 HEXEDIT_WM_KEYDOWN(PHEXEDIT_DATA hed, INT VkCode)
878 {
879 size_t bufsize;
880 PBYTE buf;
881 INT i0, i1;
882
883 if(GetKeyState(VK_MENU) & 0x8000)
884 {
885 return FALSE;
886 }
887
888 bufsize = (hed->hBuffer ? LocalSize(hed->hBuffer) : 0);
889
890 if (hed->SelStart < hed->SelEnd)
891 {
892 i0 = hed->SelStart;
893 i1 = hed->SelEnd;
894 }
895 else
896 {
897 i0 = hed->SelEnd;
898 i1 = hed->SelStart;
899 }
900
901 switch(VkCode)
902 {
903 case VK_DELETE:
904 if (hed->SelStart != hed->SelEnd)
905 {
906 buf = (PBYTE) LocalLock(hed->hBuffer);
907 if (buf)
908 {
909 MoveMemory(buf + i0, buf + i1, bufsize - i1);
910 LocalUnlock(hed->hBuffer);
911 }
912 HexEdit_SetMaxBufferSize(hed->hWndSelf, bufsize - (i1 - i0));
913 hed->InMid = FALSE;
914 hed->Index = hed->SelStart = hed->SelEnd = i0;
915 hed->CaretCol = hed->Index % hed->ColumnsPerLine;
916 hed->CaretLine = hed->Index / hed->ColumnsPerLine;
917 }
918 else
919 {
920 if (hed->InMid && hed->EditingField)
921 {
922 buf = (PBYTE) LocalLock(hed->hBuffer);
923 if (buf)
924 {
925 MoveMemory(buf + hed->Index, buf + hed->Index + 1,
926 bufsize - hed->Index - 1);
927 LocalUnlock(hed->hBuffer);
928 }
929 HexEdit_SetMaxBufferSize(hed->hWndSelf, bufsize - 1);
930 hed->InMid = FALSE;
931 }
932 else if (hed->Index < bufsize)
933 {
934 buf = (PBYTE) LocalLock(hed->hBuffer);
935 if (buf)
936 {
937 MoveMemory(buf + hed->Index, buf + hed->Index + 1,
938 bufsize - hed->Index - 1);
939 LocalUnlock(hed->hBuffer);
940 }
941 HexEdit_SetMaxBufferSize(hed->hWndSelf, bufsize - 1);
942 }
943 }
944 InvalidateRect(hed->hWndSelf, NULL, TRUE);
945 HEXEDIT_MoveCaret(hed, TRUE);
946 break;
947
948 case VK_BACK:
949 if (hed->SelStart != hed->SelEnd)
950 {
951 buf = (PBYTE) LocalLock(hed->hBuffer);
952 if (buf)
953 {
954 MoveMemory(buf + i0, buf + i1, bufsize - i1);
955 LocalUnlock(hed->hBuffer);
956 }
957 HexEdit_SetMaxBufferSize(hed->hWndSelf, bufsize - (i1 - i0));
958 hed->InMid = FALSE;
959 hed->Index = hed->SelStart = hed->SelEnd = i0;
960 hed->CaretCol = hed->Index % hed->ColumnsPerLine;
961 hed->CaretLine = hed->Index / hed->ColumnsPerLine;
962 }
963 else
964 {
965 if (hed->InMid && hed->EditingField)
966 {
967 buf = (PBYTE) LocalLock(hed->hBuffer);
968 if (buf)
969 {
970 MoveMemory(buf + hed->Index, buf + hed->Index + 1,
971 bufsize - hed->Index - 1);
972 LocalUnlock(hed->hBuffer);
973 }
974 }
975 else if (hed->Index > 0)
976 {
977 buf = (PBYTE) LocalLock(hed->hBuffer);
978 if (buf)
979 {
980 MoveMemory(buf + hed->Index - 1, buf + hed->Index,
981 bufsize - hed->Index);
982 LocalUnlock(hed->hBuffer);
983 }
984 hed->Index--;
985 hed->SelStart = hed->SelEnd = hed->Index;
986 hed->CaretCol = hed->Index % hed->ColumnsPerLine;
987 hed->CaretLine = hed->Index / hed->ColumnsPerLine;
988 }
989 HexEdit_SetMaxBufferSize(hed->hWndSelf, bufsize - 1);
990 hed->InMid = FALSE;
991 }
992 InvalidateRect(hed->hWndSelf, NULL, TRUE);
993 HEXEDIT_MoveCaret(hed, TRUE);
994 break;
995
996 case VK_LEFT:
997 if (hed->Index > 0)
998 {
999 hed->Index--;
1000 if (GetAsyncKeyState(VK_SHIFT) < 0)
1001 hed->SelEnd = hed->Index;
1002 else
1003 hed->SelStart = hed->SelEnd = hed->Index;
1004 hed->CaretCol = hed->Index % hed->ColumnsPerLine;
1005 hed->CaretLine = hed->Index / hed->ColumnsPerLine;
1006 hed->InMid = FALSE;
1007 InvalidateRect(hed->hWndSelf, NULL, TRUE);
1008 HEXEDIT_MoveCaret(hed, TRUE);
1009 }
1010 break;
1011
1012 case VK_RIGHT:
1013 if (hed->Index < (INT)bufsize)
1014 {
1015 hed->Index++;
1016 if (GetAsyncKeyState(VK_SHIFT) < 0)
1017 hed->SelEnd = hed->Index;
1018 else
1019 hed->SelStart = hed->SelEnd = hed->Index;
1020 hed->CaretCol = hed->Index % hed->ColumnsPerLine;
1021 hed->CaretLine = hed->Index / hed->ColumnsPerLine;
1022 hed->InMid = FALSE;
1023 InvalidateRect(hed->hWndSelf, NULL, TRUE);
1024 HEXEDIT_MoveCaret(hed, TRUE);
1025 }
1026 break;
1027
1028 case VK_UP:
1029 if (hed->Index >= hed->ColumnsPerLine)
1030 {
1031 hed->Index -= hed->ColumnsPerLine;
1032 if (GetAsyncKeyState(VK_SHIFT) < 0)
1033 hed->SelEnd = hed->Index;
1034 else
1035 hed->SelStart = hed->SelEnd = hed->Index;
1036 hed->CaretCol = hed->Index % hed->ColumnsPerLine;
1037 hed->CaretLine = hed->Index / hed->ColumnsPerLine;
1038 hed->InMid = FALSE;
1039 InvalidateRect(hed->hWndSelf, NULL, TRUE);
1040 HEXEDIT_MoveCaret(hed, TRUE);
1041 }
1042 break;
1043
1044 case VK_DOWN:
1045 if (hed->Index + hed->ColumnsPerLine <= (INT) bufsize)
1046 hed->Index += hed->ColumnsPerLine;
1047 else
1048 hed->Index = bufsize;
1049 hed->CaretCol = hed->Index % hed->ColumnsPerLine;
1050 hed->CaretLine = hed->Index / hed->ColumnsPerLine;
1051 if (GetAsyncKeyState(VK_SHIFT) < 0)
1052 hed->SelEnd = hed->Index;
1053 else
1054 hed->SelStart = hed->SelEnd = hed->Index;
1055 hed->InMid = FALSE;
1056 InvalidateRect(hed->hWndSelf, NULL, TRUE);
1057 HEXEDIT_MoveCaret(hed, TRUE);
1058 break;
1059 }
1060
1061 return FALSE;
1062 }
1063
1064 static BOOL
1065 HEXEDIT_WM_CHAR(PHEXEDIT_DATA hed, WCHAR ch)
1066 {
1067 size_t bufsize;
1068 PBYTE buf;
1069 INT i0, i1;
1070
1071 bufsize = (hed->hBuffer ? LocalSize(hed->hBuffer) : 0);
1072 if (hed->SelStart < hed->SelEnd)
1073 {
1074 i0 = hed->SelStart;
1075 i1 = hed->SelEnd;
1076 }
1077 else
1078 {
1079 i0 = hed->SelEnd;
1080 i1 = hed->SelStart;
1081 }
1082 if (!hed->EditingField)
1083 {
1084 if (0x20 <= ch && ch <= 0xFF)
1085 {
1086 if (hed->SelStart != hed->SelEnd)
1087 {
1088 buf = (PBYTE) LocalLock(hed->hBuffer);
1089 if (buf)
1090 {
1091 MoveMemory(buf + i0, buf + i1, bufsize - i1);
1092 LocalUnlock(hed->hBuffer);
1093 }
1094 HexEdit_SetMaxBufferSize(hed->hWndSelf, bufsize - (i1 - i0));
1095 hed->InMid = FALSE;
1096 bufsize = (hed->hBuffer ? LocalSize(hed->hBuffer) : 0);
1097 hed->Index = hed->SelStart = hed->SelEnd = i0;
1098 }
1099 HexEdit_SetMaxBufferSize(hed->hWndSelf, bufsize + 1);
1100 buf = (PBYTE) LocalLock(hed->hBuffer);
1101 if (buf)
1102 {
1103 MoveMemory(buf + hed->Index + 1, buf + hed->Index,
1104 bufsize - hed->Index);
1105 buf[hed->Index] = ch;
1106 LocalUnlock(hed->hBuffer);
1107 }
1108 hed->Index++;
1109 hed->CaretCol = hed->Index % hed->ColumnsPerLine;
1110 hed->CaretLine = hed->Index / hed->ColumnsPerLine;
1111 InvalidateRect(hed->hWndSelf, NULL, TRUE);
1112 HEXEDIT_MoveCaret(hed, TRUE);
1113 return FALSE;
1114 }
1115 }
1116 else
1117 {
1118 if (('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'F') ||
1119 ('a' <= ch && ch <= 'f'))
1120 {
1121 if (hed->SelStart != hed->SelEnd)
1122 {
1123 buf = (PBYTE) LocalLock(hed->hBuffer);
1124 if (buf)
1125 {
1126 MoveMemory(buf + i0, buf + i1, bufsize - i1);
1127 LocalUnlock(hed->hBuffer);
1128 }
1129 HexEdit_SetMaxBufferSize(hed->hWndSelf, bufsize - (i1 - i0));
1130 hed->InMid = FALSE;
1131 bufsize = (hed->hBuffer ? LocalSize(hed->hBuffer) : 0);
1132 hed->Index = hed->SelStart = hed->SelEnd = i0;
1133 }
1134 if (hed->InMid)
1135 {
1136 buf = (PBYTE) LocalLock(hed->hBuffer);
1137 if (buf)
1138 {
1139 if ('0' <= ch && ch <= '9')
1140 buf[hed->Index] |= ch - '0';
1141 else if ('A' <= ch && ch <= 'F')
1142 buf[hed->Index] |= ch + 10 - 'A';
1143 else if ('a' <= ch && ch <= 'f')
1144 buf[hed->Index] |= ch + 10 - 'a';
1145 LocalUnlock(hed->hBuffer);
1146 }
1147 hed->InMid = FALSE;
1148 hed->Index++;
1149 }
1150 else
1151 {
1152 HexEdit_SetMaxBufferSize(hed->hWndSelf, bufsize + 1);
1153 buf = (PBYTE) LocalLock(hed->hBuffer);
1154 if (buf)
1155 {
1156 MoveMemory(buf + hed->Index + 1, buf + hed->Index,
1157 bufsize - hed->Index);
1158 if ('0' <= ch && ch <= '9')
1159 buf[hed->Index] = (ch - '0') << 4;
1160 else if ('A' <= ch && ch <= 'F')
1161 buf[hed->Index] = (ch + 10 - 'A') << 4;
1162 else if ('a' <= ch && ch <= 'f')
1163 buf[hed->Index] = (ch + 10 - 'a') << 4;
1164 LocalUnlock(hed->hBuffer);
1165 }
1166 hed->InMid = TRUE;
1167 }
1168 hed->CaretCol = hed->Index % hed->ColumnsPerLine;
1169 hed->CaretLine = hed->Index / hed->ColumnsPerLine;
1170 InvalidateRect(hed->hWndSelf, NULL, TRUE);
1171 HEXEDIT_MoveCaret(hed, TRUE);
1172 return FALSE;
1173 }
1174 }
1175 return TRUE;
1176 }
1177
1178 static LRESULT
1179 HEXEDIT_WM_SIZE(PHEXEDIT_DATA hed, DWORD sType, WORD NewWidth, WORD NewHeight)
1180 {
1181 UNREFERENCED_PARAMETER(sType);
1182 UNREFERENCED_PARAMETER(NewHeight);
1183 UNREFERENCED_PARAMETER(NewWidth);
1184 HEXEDIT_Update(hed);
1185 return 0;
1186 }
1187
1188 INT_PTR CALLBACK
1189 HexEditWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1190 {
1191 PHEXEDIT_DATA hed;
1192 POINTS p;
1193
1194 hed = (PHEXEDIT_DATA)(LONG_PTR)GetWindowLongPtr(hWnd, (DWORD_PTR)0);
1195 switch(uMsg)
1196 {
1197 case WM_ERASEBKGND:
1198 return TRUE;
1199
1200 case WM_PAINT:
1201 return HEXEDIT_WM_PAINT(hed);
1202
1203 case WM_KEYDOWN:
1204 return HEXEDIT_WM_KEYDOWN(hed, (INT)wParam);
1205
1206 case WM_CHAR:
1207 return HEXEDIT_WM_CHAR(hed, (WCHAR)wParam);
1208
1209 case WM_VSCROLL:
1210 return HEXEDIT_WM_VSCROLL(hed, HIWORD(wParam), LOWORD(wParam));
1211
1212 case WM_SIZE:
1213 return HEXEDIT_WM_SIZE(hed, (DWORD)wParam, LOWORD(lParam), HIWORD(lParam));
1214
1215 case WM_LBUTTONDOWN:
1216 {
1217 p.x = LOWORD(lParam);
1218 p.y = HIWORD(lParam);
1219 return HEXEDIT_WM_LBUTTONDOWN(hed, (INT)wParam, p);
1220 }
1221
1222 case WM_LBUTTONUP:
1223 {
1224 p.x = LOWORD(lParam);
1225 p.y = HIWORD(lParam);
1226 return HEXEDIT_WM_LBUTTONUP(hed, (INT)wParam, p);
1227 }
1228
1229 case WM_MOUSEMOVE:
1230 {
1231 p.x = LOWORD(lParam);
1232 p.y = HIWORD(lParam);
1233 return HEXEDIT_WM_MOUSEMOVE(hed, (INT)wParam, p);
1234 }
1235
1236 case WM_MOUSEWHEEL:
1237 {
1238 UINT nScrollLines = 3;
1239 int delta = 0;
1240
1241 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES, 0, &nScrollLines, 0);
1242 delta -= (SHORT)HIWORD(wParam);
1243 if(abs(delta) >= WHEEL_DELTA && nScrollLines != 0)
1244 {
1245 p.x = LOWORD(lParam);
1246 p.y = HIWORD(lParam);
1247 return HEXEDIT_WM_MOUSEWHEEL(hed, nScrollLines * (delta / WHEEL_DELTA), LOWORD(wParam), &p);
1248 }
1249 break;
1250 }
1251
1252 case HEM_LOADBUFFER:
1253 return HEXEDIT_HEM_LOADBUFFER(hed, (PVOID)wParam, (DWORD)lParam);
1254
1255 case HEM_COPYBUFFER:
1256 return HEXEDIT_HEM_COPYBUFFER(hed, (PVOID)wParam, (DWORD)lParam);
1257
1258 case HEM_SETMAXBUFFERSIZE:
1259 return HEXEDIT_HEM_SETMAXBUFFERSIZE(hed, (DWORD)lParam);
1260
1261 case WM_SETFOCUS:
1262 return HEXEDIT_WM_SETFOCUS(hed);
1263
1264 case WM_KILLFOCUS:
1265 return HEXEDIT_WM_KILLFOCUS(hed);
1266
1267 case WM_GETDLGCODE:
1268 return HEXEDIT_WM_GETDLGCODE((LPMSG)lParam);
1269
1270 case WM_SETFONT:
1271 return HEXEDIT_WM_SETFONT(hed, (HFONT)wParam, (BOOL)LOWORD(lParam));
1272
1273 case WM_GETFONT:
1274 return HEXEDIT_WM_GETFONT(hed);
1275
1276 case WM_CREATE:
1277 return HEXEDIT_WM_CREATE(hed);
1278
1279 case WM_NCCREATE:
1280 if(!hed)
1281 {
1282 return HEXEDIT_WM_NCCREATE(hWnd, (CREATESTRUCT*)lParam);
1283 }
1284 break;
1285
1286 case WM_NCDESTROY:
1287 if(hed)
1288 {
1289 return HEXEDIT_WM_NCDESTROY(hed);
1290 }
1291 break;
1292
1293 case WM_CONTEXTMENU:
1294 /* FIXME: Implement Cut, Copy, Paste, Delete and Select All */
1295 break;
1296
1297 case WM_COMMAND:
1298 /* FIXME: Implement Cut, Copy, Paste, Delete and Select All */
1299 break;
1300 }
1301
1302 return DefWindowProc(hWnd, uMsg, wParam, lParam);
1303 }
1304