xref: /reactos/base/applications/charmap/map.c (revision ccef43f3)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
875 UnregisterMapClasses(HINSTANCE hInstance)
876 {
877     UnregisterClassW(szMapWndClass, hInstance);
878     UnregisterClassW(szLrgCellWndClass, hInstance);
879 }
880