1 /*
2 * PROJECT: ReactOS Character Map
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/applications/charmap/map.c
5 * PURPOSE: class implementation for painting glyph region
6 * COPYRIGHT: Copyright 2007 Ged Murphy <gedmurphy@reactos.org>
7 * Copyright 2022 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
8 *
9 */
10
11 #include "precomp.h"
12
13 #include <stdlib.h>
14 #include <winnls.h>
15
16 static const WCHAR szMapWndClass[] = L"FontMapWnd";
17 static const WCHAR szLrgCellWndClass[] = L"LrgCellWnd";
18
19 #define MAX_ROWS (0xFFFF / XCELLS) + 1 - YCELLS
20
21 static
22 VOID
SetGrid(PMAP infoPtr)23 SetGrid(PMAP infoPtr)
24 {
25 INT x, y;
26 PCELL Cell;
27
28 for (y = 0; y < YCELLS; y++)
29 for (x = 0; x < XCELLS; x++)
30 {
31 Cell = &infoPtr->Cells[y][x];
32 Cell->CellExt.left = x * infoPtr->CellSize.cx + 1;
33 Cell->CellExt.top = y * infoPtr->CellSize.cy + 1;
34 Cell->CellExt.right = (x + 1) * infoPtr->CellSize.cx + 2;
35 Cell->CellExt.bottom = (y + 1) * infoPtr->CellSize.cy + 2;
36
37 Cell->CellInt = Cell->CellExt;
38
39 InflateRect(&Cell->CellInt, -1, -1);
40 }
41 }
42
43 static
44 VOID
UpdateCells(PMAP infoPtr)45 UpdateCells(PMAP infoPtr)
46 {
47 INT x, y;
48 INT i = XCELLS * infoPtr->iYStart;
49 WCHAR ch;
50 PCELL Cell;
51
52 for (y = 0; y < YCELLS; ++y)
53 {
54 for (x = 0; x < XCELLS; ++x, ++i)
55 {
56 if (i < infoPtr->NumValidGlyphs)
57 ch = (WCHAR)infoPtr->ValidGlyphs[i];
58 else
59 ch = 0xFFFF;
60
61 Cell = &infoPtr->Cells[y][x];
62 Cell->ch = ch;
63 }
64 }
65 }
66
67 static
68 VOID
FillGrid(PMAP infoPtr,PAINTSTRUCT * ps)69 FillGrid(PMAP infoPtr,
70 PAINTSTRUCT *ps)
71 {
72 HFONT hOldFont;
73 INT x, y;
74 RECT rc;
75 PCELL Cell;
76 INT i;
77 HBRUSH hOldBrush, hbrGray = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
78 HPEN hOldPen, hPenGray = CreatePen(PS_SOLID, 1, RGB(140, 140, 140));
79
80 UpdateCells(infoPtr);
81
82 hOldFont = SelectObject(ps->hdc, infoPtr->hFont);
83 hOldPen = SelectObject(ps->hdc, GetStockObject(BLACK_PEN));
84 hOldBrush = SelectObject(ps->hdc, GetStockObject(WHITE_BRUSH));
85
86 i = XCELLS * infoPtr->iYStart;
87
88 for (y = 0; y < YCELLS; y++)
89 {
90 for (x = 0; x < XCELLS; x++, i++)
91 {
92 Cell = &infoPtr->Cells[y][x];
93 if (!IntersectRect(&rc, &ps->rcPaint, &Cell->CellExt))
94 continue;
95
96 rc = Cell->CellExt;
97 Rectangle(ps->hdc, rc.left, rc.top, rc.right, rc.bottom);
98
99 if (i < infoPtr->NumValidGlyphs)
100 {
101 DrawTextW(ps->hdc, &Cell->ch, 1, &Cell->CellInt,
102 DT_CENTER | DT_VCENTER | DT_SINGLELINE);
103 if (Cell == infoPtr->pActiveCell)
104 {
105 rc = Cell->CellInt;
106
107 /* Draw gray box */
108 SelectObject(ps->hdc, GetStockObject(NULL_BRUSH));
109 SelectObject(ps->hdc, hPenGray);
110 Rectangle(ps->hdc, rc.left, rc.top, rc.right, rc.bottom);
111 SelectObject(ps->hdc, hOldPen);
112 SelectObject(ps->hdc, hOldBrush);
113
114 if (GetFocus() == infoPtr->hMapWnd)
115 {
116 /* Draw focus rectangle */
117 InflateRect(&rc, -1, -1);
118 DrawFocusRect(ps->hdc, &rc);
119 }
120 }
121 }
122 else
123 {
124 FillRect(ps->hdc, &Cell->CellInt, hbrGray);
125 }
126 }
127 }
128
129 SelectObject(ps->hdc, hOldFont);
130 SelectObject(ps->hdc, hOldPen);
131 SelectObject(ps->hdc, hOldBrush);
132 DeleteObject(hPenGray);
133 }
134
135
136 static
137 BOOL
CreateLargeCell(PMAP infoPtr)138 CreateLargeCell(PMAP infoPtr)
139 {
140 RECT rLarge = infoPtr->pActiveCell->CellExt;
141
142 MapWindowPoints(infoPtr->hMapWnd, infoPtr->hParent, (LPPOINT)&rLarge, 2);
143
144 InflateRect(&rLarge, XLARGE - XCELLS, YLARGE - YCELLS);
145
146 infoPtr->hLrgWnd = CreateWindowExW(0,
147 szLrgCellWndClass,
148 NULL,
149 WS_CHILDWINDOW | WS_VISIBLE,
150 rLarge.left,
151 rLarge.top,
152 rLarge.right - rLarge.left,
153 rLarge.bottom - rLarge.top,
154 infoPtr->hParent,
155 NULL,
156 hInstance,
157 infoPtr);
158 if (!infoPtr->hLrgWnd)
159 return FALSE;
160
161 return TRUE;
162 }
163
164
165 static
166 VOID
MoveLargeCell(PMAP infoPtr)167 MoveLargeCell(PMAP infoPtr)
168 {
169 RECT rLarge = infoPtr->pActiveCell->CellExt;
170
171 MapWindowPoints(infoPtr->hMapWnd, infoPtr->hParent, (LPPOINT)&rLarge, 2);
172
173 InflateRect(&rLarge, XLARGE - XCELLS, YLARGE - YCELLS);
174
175 MoveWindow(infoPtr->hLrgWnd,
176 rLarge.left,
177 rLarge.top,
178 rLarge.right - rLarge.left,
179 rLarge.bottom - rLarge.top,
180 TRUE);
181
182 InvalidateRect(infoPtr->hLrgWnd, NULL, TRUE);
183 }
184
185
186 static
187 VOID
GetPossibleCharacters(WCHAR * ch,INT chLen,INT codePageIdx)188 GetPossibleCharacters(WCHAR* ch, INT chLen, INT codePageIdx)
189 {
190 INT i, j;
191
192 ZeroMemory(ch, sizeof(ch[0]) * chLen);
193
194 if (codePageIdx <= 0 || codePageIdx > SIZEOF(codePages))
195 {
196 /* this is unicode, so just load up the first MAX_GLYPHS characters
197 start at 0x21 to bypass whitespace characters */
198 INT len = min(MAX_GLYPHS, chLen);
199 for (i = 0x21, j = 0; i < len; i++)
200 ch[j++] = (WCHAR)i;
201 }
202 else
203 {
204 /* This is a codepage, so use NLS to translate the first 256 characters */
205 CHAR multiByteString[256] = { 0 };
206 for (i = 0x21; i < SIZEOF(multiByteString); i++)
207 multiByteString[i] = (CHAR)i;
208
209 if (!MultiByteToWideChar(codePages[codePageIdx - 1], 0, multiByteString, sizeof(multiByteString), ch, chLen))
210 {
211 /* Failed for some reason, so clear the array */
212 memset(ch, 0, sizeof(ch[0]) * chLen);
213 }
214 }
215 }
216
217
218 static
219 VOID
SetFont(PMAP infoPtr,LPWSTR lpFontName)220 SetFont(PMAP infoPtr,
221 LPWSTR lpFontName)
222 {
223 HDC hdc;
224 WCHAR ch[MAX_GLYPHS];
225 WORD out[MAX_GLYPHS];
226 DWORD i, j;
227
228 /* Destroy Zoom window, since it was created with older font */
229 DestroyWindow(infoPtr->hLrgWnd);
230 infoPtr->hLrgWnd = NULL;
231
232 if (infoPtr->hFont)
233 DeleteObject(infoPtr->hFont);
234
235 ZeroMemory(&infoPtr->CurrentFont,
236 sizeof(LOGFONTW));
237
238 hdc = GetDC(infoPtr->hMapWnd);
239 infoPtr->CurrentFont.lfHeight = GetDeviceCaps(hdc, LOGPIXELSY) / 5;
240
241 infoPtr->CurrentFont.lfCharSet = DEFAULT_CHARSET;
242 lstrcpynW(infoPtr->CurrentFont.lfFaceName,
243 lpFontName,
244 SIZEOF(infoPtr->CurrentFont.lfFaceName));
245
246 infoPtr->hFont = CreateFontIndirectW(&infoPtr->CurrentFont);
247
248 InvalidateRect(infoPtr->hMapWnd,
249 NULL,
250 TRUE);
251
252 // Get all the valid glyphs in this font
253
254 SelectObject(hdc, infoPtr->hFont);
255
256 // Get the code page associated with the selected 'character set'
257 GetPossibleCharacters(ch, MAX_GLYPHS, infoPtr->CharMap);
258
259 if (GetGlyphIndicesW(hdc,
260 ch,
261 MAX_GLYPHS,
262 out,
263 GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
264 {
265 j = 0;
266 for (i = 0; i < MAX_GLYPHS; i++)
267 {
268 if (out[i] != 0xffff && out[i] != 0x0000 && ch[i] != 0x0000)
269 {
270 infoPtr->ValidGlyphs[j] = ch[i];
271 j++;
272 }
273 }
274 infoPtr->NumValidGlyphs = j;
275 }
276
277 ReleaseDC(infoPtr->hMapWnd, hdc);
278
279 infoPtr->NumRows = infoPtr->NumValidGlyphs / XCELLS;
280 if (infoPtr->NumValidGlyphs % XCELLS)
281 infoPtr->NumRows += 1;
282 infoPtr->NumRows = (infoPtr->NumRows > YCELLS) ? infoPtr->NumRows - YCELLS : 0;
283
284 SetScrollRange(infoPtr->hMapWnd, SB_VERT, 0, infoPtr->NumRows, FALSE);
285 SetScrollPos(infoPtr->hMapWnd, SB_VERT, 0, TRUE);
286 infoPtr->iYStart = 0;
287 }
288
289
290 static
291 LRESULT
NotifyParentOfSelection(PMAP infoPtr,UINT code,WCHAR ch)292 NotifyParentOfSelection(PMAP infoPtr,
293 UINT code,
294 WCHAR ch)
295 {
296 LRESULT Ret = 0;
297
298 if (infoPtr->hParent != NULL)
299 {
300 DWORD dwIdc = GetWindowLongPtr(infoPtr->hMapWnd, GWLP_ID);
301 /*
302 * Push directly into the event queue instead of waiting
303 * the parent to be unlocked.
304 * High word of LPARAM is still available for future needs...
305 */
306 Ret = PostMessage(infoPtr->hParent,
307 WM_COMMAND,
308 MAKELPARAM((WORD)dwIdc, (WORD)code),
309 (LPARAM)LOWORD(ch));
310 }
311
312 return Ret;
313 }
314
315
316 static
317 VOID
LimitCaretXY(PMAP infoPtr,INT * pX,INT * pY)318 LimitCaretXY(PMAP infoPtr, INT *pX, INT *pY)
319 {
320 INT i, X = *pX, Y = *pY, iYStart = infoPtr->iYStart;
321
322 i = XCELLS * (iYStart + Y) + X;
323 while (i >= infoPtr->NumValidGlyphs)
324 {
325 if (X > 0)
326 {
327 --X;
328 }
329 else
330 {
331 X = XCELLS - 1;
332 --Y;
333 }
334 i = XCELLS * (iYStart + Y) + X;
335 }
336
337 *pX = X;
338 *pY = Y;
339 }
340
341 static
342 VOID
SetCaretXY(PMAP infoPtr,INT X,INT Y,BOOL bLarge,BOOL bInvalidateAll)343 SetCaretXY(PMAP infoPtr, INT X, INT Y, BOOL bLarge, BOOL bInvalidateAll)
344 {
345
346 /* set previous active cell to inactive */
347 if (!bInvalidateAll)
348 {
349 InvalidateRect(infoPtr->hMapWnd,
350 &infoPtr->pActiveCell->CellInt,
351 FALSE);
352 }
353
354 LimitCaretXY(infoPtr, &X, &Y);
355 infoPtr->CaretX = X;
356 infoPtr->CaretY = Y;
357 UpdateCells(infoPtr);
358
359 /* set new cell to active */
360 infoPtr->pActiveCell = &infoPtr->Cells[Y][X];
361 if (!bInvalidateAll)
362 {
363 InvalidateRect(infoPtr->hMapWnd,
364 &infoPtr->pActiveCell->CellInt,
365 FALSE);
366 }
367
368 /* Create if needed */
369 if (bLarge)
370 {
371 if (infoPtr->hLrgWnd)
372 MoveLargeCell(infoPtr);
373 else
374 CreateLargeCell(infoPtr);
375 }
376 else
377 {
378 /* Destroy large window */
379 if (infoPtr->hLrgWnd)
380 {
381 DestroyWindow(infoPtr->hLrgWnd);
382 infoPtr->hLrgWnd = NULL;
383 }
384 }
385
386 if (bInvalidateAll)
387 InvalidateRect(infoPtr->hMapWnd, NULL, FALSE);
388
389 UpdateStatusBar(infoPtr->pActiveCell->ch);
390 }
391
392 static
393 VOID
OnClick(PMAP infoPtr,WORD ptx,WORD pty)394 OnClick(PMAP infoPtr,
395 WORD ptx,
396 WORD pty)
397 {
398 /*
399 * Find the cell the mouse pointer is over.
400 * Since each cell is the same size, this can be done quickly using CellSize.
401 * Clamp to XCELLS - 1 and YCELLS - 1 because the map can sometimes be slightly
402 * larger than infoPtr.CellSize * XCELLS , due to the map size being a non integer
403 * multiple of infoPtr.CellSize .
404 */
405 INT x = min(XCELLS - 1, ptx / max(1, infoPtr->CellSize.cx));
406 INT y = min(YCELLS - 1, pty / max(1, infoPtr->CellSize.cy));
407
408 SetCaretXY(infoPtr, x, y, TRUE, FALSE);
409 }
410
411
412 static
413 BOOL
MapOnCreate(PMAP infoPtr,HWND hwnd,HWND hParent)414 MapOnCreate(PMAP infoPtr,
415 HWND hwnd,
416 HWND hParent)
417 {
418 RECT rc;
419
420 infoPtr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MAP));
421 if (!infoPtr)
422 return FALSE;
423
424 SetWindowLongPtrW(hwnd, 0, (LONG_PTR)infoPtr);
425
426 infoPtr->hMapWnd = hwnd;
427 infoPtr->hParent = hParent;
428
429 GetClientRect(hwnd, &rc);
430 infoPtr->ClientSize.cx = rc.right;
431 infoPtr->ClientSize.cy = rc.bottom;
432 infoPtr->CellSize.cx = infoPtr->ClientSize.cx / XCELLS;
433 infoPtr->CellSize.cy = infoPtr->ClientSize.cy / YCELLS;
434
435 infoPtr->pActiveCell = &infoPtr->Cells[0][0];
436
437 SetGrid(infoPtr);
438
439 SetScrollPos(infoPtr->hParent, SB_VERT, 0, TRUE);
440 return TRUE;
441 }
442
443 static
444 VOID
OnVScroll(PMAP infoPtr,INT Value,INT Pos)445 OnVScroll(PMAP infoPtr,
446 INT Value,
447 INT Pos)
448 {
449 INT iYDiff, iOldYStart = infoPtr->iYStart;
450 INT X, Y;
451
452 switch (Value)
453 {
454 case SB_LINEUP:
455 infoPtr->iYStart -= 1;
456 break;
457
458 case SB_LINEDOWN:
459 infoPtr->iYStart += 1;
460 break;
461
462 case SB_PAGEUP:
463 infoPtr->iYStart -= YCELLS;
464 break;
465
466 case SB_PAGEDOWN:
467 infoPtr->iYStart += YCELLS;
468 break;
469
470 case SB_THUMBTRACK:
471 infoPtr->iYStart = Pos;
472 break;
473
474 case SB_TOP:
475 infoPtr->iYStart = 0;
476 SetCaretXY(infoPtr, 0, 0, FALSE, TRUE);
477 return;
478
479 case SB_BOTTOM:
480 infoPtr->iYStart = infoPtr->NumRows;
481 SetCaretXY(infoPtr, XCELLS - 1, YCELLS - 1, FALSE, TRUE);
482 break;
483
484 default:
485 break;
486 }
487
488 infoPtr->iYStart = max(0, infoPtr->iYStart);
489 infoPtr->iYStart = min(infoPtr->iYStart, infoPtr->NumRows);
490
491 UpdateCells(infoPtr);
492
493 X = infoPtr->CaretX;
494 Y = infoPtr->CaretY;
495 LimitCaretXY(infoPtr, &X, &Y);
496 SetCaretXY(infoPtr, X, Y, IsWindow(infoPtr->hLrgWnd), FALSE);
497
498 iYDiff = iOldYStart - infoPtr->iYStart;
499 if (iYDiff)
500 {
501 if (infoPtr->hLrgWnd != NULL)
502 {
503 ShowWindow(infoPtr->hLrgWnd, SW_HIDE);
504 }
505
506 SetScrollPos(infoPtr->hMapWnd,
507 SB_VERT,
508 infoPtr->iYStart,
509 TRUE);
510
511 if (abs(iYDiff) < YCELLS)
512 {
513 RECT rect;
514
515 /* Invalidate the rect around the active cell since a new cell will become active */
516 if (infoPtr->pActiveCell)
517 {
518 InvalidateRect(infoPtr->hMapWnd,
519 &infoPtr->pActiveCell->CellExt,
520 TRUE);
521 }
522
523 GetClientRect(infoPtr->hMapWnd, &rect);
524 rect.top += 2;
525 rect.bottom -= 2;
526 ScrollWindowEx(infoPtr->hMapWnd,
527 0,
528 iYDiff * infoPtr->CellSize.cy,
529 &rect,
530 &rect,
531 NULL,
532 NULL,
533 SW_INVALIDATE);
534 }
535 else
536 {
537 InvalidateRect(infoPtr->hMapWnd,
538 NULL,
539 TRUE);
540 }
541
542 if (infoPtr->hLrgWnd != NULL)
543 {
544 ShowWindow(infoPtr->hLrgWnd, SW_SHOW);
545 }
546 }
547
548 UpdateStatusBar(infoPtr->pActiveCell->ch);
549 }
550
551
552 static
553 VOID
OnPaint(PMAP infoPtr,WPARAM wParam)554 OnPaint(PMAP infoPtr,
555 WPARAM wParam)
556 {
557 PAINTSTRUCT ps;
558 HDC hdc;
559
560 if (wParam != 0)
561 {
562 if (!GetUpdateRect(infoPtr->hMapWnd, &ps.rcPaint, TRUE))
563 return;
564
565 ps.hdc = (HDC)wParam;
566 }
567 else
568 {
569 hdc = BeginPaint(infoPtr->hMapWnd, &ps);
570 if (hdc == NULL)
571 return;
572 }
573
574 FillGrid(infoPtr, &ps);
575
576 if (wParam == 0)
577 {
578 EndPaint(infoPtr->hMapWnd, &ps);
579 }
580 }
581
582 static
583 VOID
MoveUpDown(PMAP infoPtr,INT DY)584 MoveUpDown(PMAP infoPtr, INT DY)
585 {
586 INT Y = infoPtr->CaretY;
587
588 if (DY < 0) /* Move Up */
589 {
590 if (Y <= 0)
591 {
592 SendMessageW(infoPtr->hMapWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0);
593 return;
594 }
595
596 Y -= 1;
597 }
598 else if (DY > 0) /* Move Down */
599 {
600 if (Y + 1 >= YCELLS)
601 {
602 SendMessageW(infoPtr->hMapWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0);
603 return;
604 }
605
606 Y += 1;
607 }
608
609 SetCaretXY(infoPtr, infoPtr->CaretX, Y, IsWindow(infoPtr->hLrgWnd), FALSE);
610 }
611
612 static
613 VOID
MoveLeftRight(PMAP infoPtr,INT DX)614 MoveLeftRight(PMAP infoPtr, INT DX)
615 {
616 INT X = infoPtr->CaretX;
617 INT Y = infoPtr->CaretY;
618
619 if (DX < 0) /* Move Left */
620 {
621 if (X <= 0) /* at left edge */
622 {
623 if (Y <= 0) /* at top */
624 {
625 Y = 0;
626 if (infoPtr->iYStart > 0)
627 X = XCELLS - 1;
628 SendMessageW(infoPtr->hMapWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0);
629 }
630 else
631 {
632 X = XCELLS - 1;
633 Y -= 1;
634 }
635 }
636 else /* Not at left edge */
637 {
638 X -= 1;
639 }
640 }
641 else if (DX > 0) /* Move Right */
642 {
643 if (X + 1 >= XCELLS) /* at right edge */
644 {
645 if (Y + 1 >= YCELLS) /* at bottom */
646 {
647 Y = YCELLS - 1;
648 if (infoPtr->iYStart < infoPtr->NumRows)
649 X = 0;
650 SendMessageW(infoPtr->hMapWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0);
651 }
652 else
653 {
654 X = 0;
655 Y += 1;
656 }
657 }
658 else
659 {
660 X += 1;
661 }
662 }
663
664 SetCaretXY(infoPtr, X, Y, IsWindow(infoPtr->hLrgWnd), FALSE);
665 }
666
667 static
668 VOID
OnKeyDown(PMAP infoPtr,WPARAM wParam,LPARAM lParam)669 OnKeyDown(PMAP infoPtr, WPARAM wParam, LPARAM lParam)
670 {
671 BOOL bCtrlDown = (GetKeyState(VK_CONTROL) < 0);
672
673 switch (wParam)
674 {
675 case VK_UP:
676 if (bCtrlDown)
677 SetCaretXY(infoPtr, infoPtr->CaretX, 0, FALSE, FALSE);
678 else
679 MoveUpDown(infoPtr, -1);
680 break;
681
682 case VK_DOWN:
683 if (bCtrlDown)
684 SetCaretXY(infoPtr, infoPtr->CaretX, YCELLS - 1, FALSE, FALSE);
685 else
686 MoveUpDown(infoPtr, +1);
687 break;
688
689 case VK_LEFT:
690 if (bCtrlDown)
691 SetCaretXY(infoPtr, 0, infoPtr->CaretY, FALSE, FALSE);
692 else
693 MoveLeftRight(infoPtr, -1);
694 break;
695
696 case VK_RIGHT:
697 if (bCtrlDown)
698 SetCaretXY(infoPtr, XCELLS - 1, infoPtr->CaretY, FALSE, FALSE);
699 else
700 MoveLeftRight(infoPtr, +1);
701 break;
702
703 case VK_PRIOR: /* Page Up */
704 SendMessageW(infoPtr->hMapWnd, WM_VSCROLL, MAKEWPARAM(SB_PAGEUP, 0), 0);
705 break;
706
707 case VK_NEXT: /* Page Down */
708 SendMessageW(infoPtr->hMapWnd, WM_VSCROLL, MAKEWPARAM(SB_PAGEDOWN, 0), 0);
709 break;
710
711 case VK_HOME:
712 if (bCtrlDown)
713 SendMessageW(infoPtr->hMapWnd, WM_VSCROLL, MAKEWPARAM(SB_TOP, 0), 0);
714 else
715 SetCaretXY(infoPtr, 0, infoPtr->CaretY, FALSE, FALSE);
716 break;
717
718 case VK_END:
719 if (bCtrlDown)
720 SendMessageW(infoPtr->hMapWnd, WM_VSCROLL, MAKEWPARAM(SB_BOTTOM, 0), 0);
721 else
722 SetCaretXY(infoPtr, XCELLS - 1, infoPtr->CaretY, FALSE, FALSE);
723 break;
724 }
725 }
726
727 LRESULT
728 CALLBACK
MapWndProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)729 MapWndProc(HWND hwnd,
730 UINT uMsg,
731 WPARAM wParam,
732 LPARAM lParam)
733 {
734 PMAP infoPtr = (PMAP)GetWindowLongPtrW(hwnd, 0);
735 LRESULT Ret = 0;
736 WCHAR lfFaceName[LF_FACESIZE];
737
738 switch (uMsg)
739 {
740 case WM_CREATE:
741 {
742 if (!MapOnCreate(infoPtr,
743 hwnd,
744 ((LPCREATESTRUCTW)lParam)->hwndParent))
745 {
746 return (LRESULT)-1;
747 }
748
749 break;
750 }
751
752 case WM_KEYDOWN:
753 {
754 OnKeyDown(infoPtr, wParam, lParam);
755 break;
756 }
757
758 case WM_LBUTTONDOWN:
759 {
760 SetFocus(hwnd);
761 OnClick(infoPtr, LOWORD(lParam), HIWORD(lParam));
762 break;
763 }
764
765 case WM_MOUSEMOVE:
766 {
767 if (wParam & MK_LBUTTON)
768 {
769 OnClick(infoPtr, LOWORD(lParam), HIWORD(lParam));
770 }
771 break;
772 }
773
774 case WM_LBUTTONDBLCLK:
775 {
776 if (!infoPtr->pActiveCell || GetFocus() != hwnd)
777 break;
778
779 NotifyParentOfSelection(infoPtr,
780 FM_SETCHAR,
781 infoPtr->pActiveCell->ch);
782
783 if (infoPtr->hLrgWnd)
784 {
785 DestroyWindow(infoPtr->hLrgWnd);
786 infoPtr->hLrgWnd = NULL;
787 }
788 break;
789 }
790
791 case WM_VSCROLL:
792 {
793 OnVScroll(infoPtr, LOWORD(wParam), HIWORD(wParam));
794 break;
795 }
796
797 case FM_SETCHARMAP:
798 infoPtr->CaretX = infoPtr->CaretY = infoPtr->iYStart = 0;
799 infoPtr->CharMap = LOWORD(wParam);
800 wcsncpy(lfFaceName,
801 infoPtr->CurrentFont.lfFaceName,
802 SIZEOF(lfFaceName));
803 SetFont(infoPtr, lfFaceName);
804 break;
805
806 case FM_SETFONT:
807 infoPtr->CaretX = infoPtr->CaretY = infoPtr->iYStart = 0;
808 SetFont(infoPtr, (LPWSTR)lParam);
809 break;
810
811 case FM_GETCHAR:
812 {
813 if (!infoPtr->pActiveCell) return 0;
814 return infoPtr->pActiveCell->ch;
815 }
816
817 case FM_GETHFONT:
818 return (LRESULT)infoPtr->hFont;
819
820 case WM_PAINT:
821 OnPaint(infoPtr, wParam);
822 break;
823
824 case WM_DESTROY:
825 DeleteObject(infoPtr->hFont);
826 HeapFree(GetProcessHeap(), 0, infoPtr);
827 SetWindowLongPtrW(hwnd, 0, (LONG_PTR)NULL);
828 break;
829
830 case WM_GETDLGCODE:
831 return DLGC_WANTARROWS;
832
833 case WM_SETFOCUS:
834 case WM_KILLFOCUS:
835 if (!infoPtr->hLrgWnd)
836 InvalidateRect(hwnd, &(infoPtr->pActiveCell->CellInt), FALSE);
837 break;
838
839 default:
840 Ret = DefWindowProcW(hwnd, uMsg, wParam, lParam);
841 break;
842 }
843
844 return Ret;
845 }
846
847
848 BOOL
RegisterMapClasses(HINSTANCE hInstance)849 RegisterMapClasses(HINSTANCE hInstance)
850 {
851 WNDCLASSW wc = {0};
852
853 wc.style = CS_DBLCLKS;
854 wc.lpfnWndProc = MapWndProc;
855 wc.cbWndExtra = sizeof(PMAP);
856 wc.hInstance = hInstance;
857 wc.hCursor = LoadCursorW(NULL,
858 (LPWSTR)IDC_ARROW);
859 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
860 wc.lpszClassName = szMapWndClass;
861
862 if (RegisterClassW(&wc))
863 {
864 wc.lpfnWndProc = LrgCellWndProc;
865 wc.cbWndExtra = 0;
866 wc.lpszClassName = szLrgCellWndClass;
867
868 return RegisterClassW(&wc) != 0;
869 }
870
871 return FALSE;
872 }
873
874 VOID
UnregisterMapClasses(HINSTANCE hInstance)875 UnregisterMapClasses(HINSTANCE hInstance)
876 {
877 UnregisterClassW(szMapWndClass, hInstance);
878 UnregisterClassW(szLrgCellWndClass, hInstance);
879 }
880