xref: /reactos/base/applications/charmap/map.c (revision 76dd2fcf)
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  *
8  */
9 
10 #include "precomp.h"
11 
12 #include <stdlib.h>
13 
14 static const WCHAR szMapWndClass[] = L"FontMapWnd";
15 static const WCHAR szLrgCellWndClass[] = L"LrgCellWnd";
16 
17 #define MAX_ROWS (0xFFFF / XCELLS) + 1 - YCELLS
18 
19 
20 static
21 VOID
22 SetGrid(PMAP infoPtr)
23 {
24     INT x, y;
25 
26     for (y = 0; y < YCELLS; y++)
27     for (x = 0; x < XCELLS; x++)
28     {
29         infoPtr->Cells[y][x].CellExt.left = x * infoPtr->CellSize.cx + 1;
30         infoPtr->Cells[y][x].CellExt.top = y * infoPtr->CellSize.cy + 1;
31         infoPtr->Cells[y][x].CellExt.right = (x + 1) * infoPtr->CellSize.cx + 2;
32         infoPtr->Cells[y][x].CellExt.bottom = (y + 1) * infoPtr->CellSize.cy + 2;
33 
34         CopyRect(&infoPtr->Cells[y][x].CellInt,
35                  &infoPtr->Cells[y][x].CellExt);
36 
37         InflateRect(&infoPtr->Cells[y][x].CellInt,
38                     -1,
39                     -1);
40     }
41 }
42 
43 static
44 VOID
45 DrawActiveCell(PMAP infoPtr,
46                HDC hdc)
47 {
48     Rectangle(hdc,
49               infoPtr->pActiveCell->CellInt.left,
50               infoPtr->pActiveCell->CellInt.top,
51               infoPtr->pActiveCell->CellInt.right,
52               infoPtr->pActiveCell->CellInt.bottom);
53 
54 }
55 
56 
57 static
58 VOID
59 DrawGrid(PMAP infoPtr,
60          PAINTSTRUCT *ps)
61 {
62     INT x, y;
63     RECT rc;
64     PCELL Cell;
65 
66     for (y = 0; y < YCELLS; y++)
67     for (x = 0; x < XCELLS; x++)
68     {
69         Cell = &infoPtr->Cells[y][x];
70 
71         if (!IntersectRect(&rc,
72                            &ps->rcPaint,
73                            &Cell->CellExt))
74         {
75             continue;
76         }
77 
78         Rectangle(ps->hdc,
79                   Cell->CellExt.left,
80                   Cell->CellExt.top,
81                   Cell->CellExt.right,
82                   Cell->CellExt.bottom);
83 
84         if (infoPtr->pActiveCell == Cell)
85         {
86             DrawActiveCell(infoPtr, ps->hdc);
87         }
88     }
89 }
90 
91 
92 static
93 VOID
94 FillGrid(PMAP infoPtr,
95          PAINTSTRUCT *ps)
96 {
97     HFONT hOldFont;
98     WCHAR ch;
99     INT x, y;
100     RECT rc;
101     PCELL Cell;
102     INT i, added;
103 
104     hOldFont = SelectObject(ps->hdc,
105                             infoPtr->hFont);
106 
107     i = XCELLS * infoPtr->iYStart;
108 
109     added = 0;
110 
111     for (y = 0; y < YCELLS; y++)
112     for (x = 0; x < XCELLS; x++)
113     {
114         ch = (WCHAR)infoPtr->ValidGlyphs[i];
115 
116         Cell = &infoPtr->Cells[y][x];
117 
118         if (IntersectRect(&rc,
119                             &ps->rcPaint,
120                             &Cell->CellExt))
121         {
122             Cell->ch = ch;
123 
124             DrawTextW(ps->hdc,
125                         &ch,
126                         1,
127                         &Cell->CellInt,
128                         DT_CENTER | DT_VCENTER | DT_SINGLELINE);
129 
130             added++;
131         }
132 
133         i++;
134         ch = (WCHAR)i;
135     }
136     SelectObject(ps->hdc,
137                  hOldFont);
138 }
139 
140 
141 static
142 BOOL
143 CreateLargeCell(PMAP infoPtr)
144 {
145     RECT rLarge;
146 
147     CopyRect(&rLarge,
148              &infoPtr->pActiveCell->CellExt);
149 
150     MapWindowPoints(infoPtr->hMapWnd,
151                     infoPtr->hParent,
152                     (VOID*)&rLarge,
153                     2);
154 
155     InflateRect(&rLarge,
156                 XLARGE - XCELLS,
157                 YLARGE - YCELLS);
158 
159     infoPtr->hLrgWnd = CreateWindowExW(0,
160                                        szLrgCellWndClass,
161                                        NULL,
162                                        WS_CHILDWINDOW | WS_VISIBLE,
163                                        rLarge.left,
164                                        rLarge.top,
165                                        rLarge.right - rLarge.left,
166                                        rLarge.bottom - rLarge.top,
167                                        infoPtr->hParent,
168                                        NULL,
169                                        hInstance,
170                                        infoPtr);
171     if (!infoPtr->hLrgWnd)
172         return FALSE;
173 
174     return TRUE;
175 }
176 
177 
178 static
179 VOID
180 MoveLargeCell(PMAP infoPtr)
181 {
182     RECT rLarge;
183 
184     CopyRect(&rLarge,
185              &infoPtr->pActiveCell->CellExt);
186 
187     MapWindowPoints(infoPtr->hMapWnd,
188                     infoPtr->hParent,
189                     (VOID*)&rLarge,
190                     2);
191 
192     InflateRect(&rLarge,
193                 XLARGE - XCELLS,
194                 YLARGE - YCELLS);
195 
196     MoveWindow(infoPtr->hLrgWnd,
197                rLarge.left,
198                rLarge.top,
199                rLarge.right - rLarge.left,
200                rLarge.bottom - rLarge.top,
201                TRUE);
202 
203     InvalidateRect(infoPtr->hLrgWnd,
204                    NULL,
205                    TRUE);
206 }
207 
208 
209 static
210 VOID
211 SetFont(PMAP infoPtr,
212         LPWSTR lpFontName)
213 {
214     HDC hdc;
215     WCHAR ch[MAX_GLYPHS];
216     WORD out[MAX_GLYPHS];
217     DWORD i, j;
218 
219     /* Destroy Zoom window, since it was created with older font */
220     DestroyWindow(infoPtr->hLrgWnd);
221     infoPtr->hLrgWnd = NULL;
222 
223     if (infoPtr->hFont)
224         DeleteObject(infoPtr->hFont);
225 
226     ZeroMemory(&infoPtr->CurrentFont,
227                sizeof(LOGFONTW));
228 
229     hdc = GetDC(infoPtr->hMapWnd);
230     infoPtr->CurrentFont.lfHeight = GetDeviceCaps(hdc, LOGPIXELSY) / 5;
231 
232     infoPtr->CurrentFont.lfCharSet =  DEFAULT_CHARSET;
233     wcsncpy(infoPtr->CurrentFont.lfFaceName,
234             lpFontName,
235             sizeof(infoPtr->CurrentFont.lfFaceName) / sizeof(infoPtr->CurrentFont.lfFaceName[0]));
236 
237     infoPtr->hFont = CreateFontIndirectW(&infoPtr->CurrentFont);
238 
239     InvalidateRect(infoPtr->hMapWnd,
240                    NULL,
241                    TRUE);
242 
243     infoPtr->pActiveCell = &infoPtr->Cells[0][0];
244 
245     // Get all the valid glyphs in this font
246 
247     SelectObject(hdc, infoPtr->hFont);
248 
249     for (i = 0; i < MAX_GLYPHS; i++)
250         ch[i] = (WCHAR)i;
251 
252     if (GetGlyphIndicesW(hdc,
253                          ch,
254                          MAX_GLYPHS,
255                          out,
256                          GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
257     {
258         j = 0;
259         for (i = ' ' + 1; i < MAX_GLYPHS; i++)
260         {
261             if (out[i] != 0xffff)
262             {
263                 infoPtr->ValidGlyphs[j] = ch[i];
264                 j++;
265             }
266         }
267         infoPtr->NumValidGlyphs = j;
268     }
269 
270     ReleaseDC(infoPtr->hMapWnd, hdc);
271 
272     infoPtr->NumRows = infoPtr->NumValidGlyphs / XCELLS;
273     if (infoPtr->NumValidGlyphs % XCELLS)
274         infoPtr->NumRows += 1;
275     infoPtr->NumRows = (infoPtr->NumRows > YCELLS) ? infoPtr->NumRows - YCELLS : 0;
276 
277     SetScrollRange(infoPtr->hMapWnd, SB_VERT, 0, infoPtr->NumRows, FALSE);
278     SetScrollPos(infoPtr->hMapWnd, SB_VERT, 0, TRUE);
279     infoPtr->iYStart = 0;
280 }
281 
282 
283 static
284 LRESULT
285 NotifyParentOfSelection(PMAP infoPtr,
286                         UINT code,
287                         WCHAR ch)
288 {
289     LRESULT Ret = 0;
290 
291     if (infoPtr->hParent != NULL)
292     {
293         DWORD dwIdc = GetWindowLongPtr(infoPtr->hMapWnd, GWLP_ID);
294         /*
295          * Push directly into the event queue instead of waiting
296          * the parent to be unlocked.
297          * High word of LPARAM is still available for future needs...
298          */
299         Ret = PostMessage(infoPtr->hParent,
300                           WM_COMMAND,
301                           MAKELPARAM((WORD)dwIdc, (WORD)code),
302                           (LPARAM)LOWORD(ch));
303     }
304 
305     return Ret;
306 }
307 
308 
309 static
310 VOID
311 OnClick(PMAP infoPtr,
312         WORD ptx,
313         WORD pty)
314 {
315     INT x, y;
316 
317     x = ptx / max(1, infoPtr->CellSize.cx);
318     y = pty / max(1, infoPtr->CellSize.cy);
319 
320     /* if the cell is not already active */
321     if (!infoPtr->Cells[y][x].bActive)
322     {
323         /* set previous active cell to inactive */
324         if (infoPtr->pActiveCell)
325         {
326             /* invalidate normal cells, required when
327              * moving a small active cell via keyboard */
328             if (!infoPtr->pActiveCell->bLarge)
329             {
330                 InvalidateRect(infoPtr->hMapWnd,
331                                &infoPtr->pActiveCell->CellInt,
332                                TRUE);
333             }
334 
335             infoPtr->pActiveCell->bActive = FALSE;
336             infoPtr->pActiveCell->bLarge = FALSE;
337         }
338 
339         /* set new cell to active */
340         infoPtr->pActiveCell = &infoPtr->Cells[y][x];
341         infoPtr->pActiveCell->bActive = TRUE;
342         infoPtr->pActiveCell->bLarge = TRUE;
343         if (infoPtr->hLrgWnd)
344             MoveLargeCell(infoPtr);
345         else
346             CreateLargeCell(infoPtr);
347     }
348 }
349 
350 
351 static
352 BOOL
353 MapOnCreate(PMAP infoPtr,
354             HWND hwnd,
355             HWND hParent)
356 {
357     RECT rc;
358     BOOL Ret = FALSE;
359 
360     infoPtr = HeapAlloc(GetProcessHeap(),
361                         0,
362                         sizeof(MAP));
363     if (infoPtr)
364     {
365         SetLastError(0);
366         SetWindowLongPtrW(hwnd,
367                           0,
368                           (DWORD_PTR)infoPtr);
369         if (GetLastError() == 0)
370         {
371             ZeroMemory(infoPtr,
372                        sizeof(MAP));
373 
374             infoPtr->hMapWnd = hwnd;
375             infoPtr->hParent = hParent;
376 
377             GetClientRect(hwnd, &rc);
378             infoPtr->ClientSize.cx = rc.right;
379             infoPtr->ClientSize.cy = rc.bottom;
380             infoPtr->CellSize.cx = infoPtr->ClientSize.cx / XCELLS;
381             infoPtr->CellSize.cy = infoPtr->ClientSize.cy / YCELLS;
382 
383             infoPtr->pActiveCell = NULL;
384 
385             SetGrid(infoPtr);
386 
387             SetScrollPos(infoPtr->hParent, SB_VERT, 0, TRUE);
388 
389             Ret = TRUE;
390         }
391     }
392 
393     return Ret;
394 }
395 
396 
397 static
398 VOID
399 OnVScroll(PMAP infoPtr,
400           INT Value,
401           INT Pos)
402 {
403     INT iYDiff, iOldYStart = infoPtr->iYStart;
404 
405     switch (Value)
406     {
407         case SB_LINEUP:
408             infoPtr->iYStart -=  1;
409             break;
410 
411         case SB_LINEDOWN:
412             infoPtr->iYStart +=  1;
413             break;
414 
415         case SB_PAGEUP:
416             infoPtr->iYStart -= YCELLS;
417             break;
418 
419         case SB_PAGEDOWN:
420             infoPtr->iYStart += YCELLS;
421             break;
422 
423         case SB_THUMBTRACK:
424             infoPtr->iYStart = Pos;
425             break;
426 
427        default:
428             break;
429        }
430 
431     infoPtr->iYStart = max(0, infoPtr->iYStart);
432     infoPtr->iYStart = min(infoPtr->iYStart, infoPtr->NumRows);
433 
434     iYDiff = iOldYStart - infoPtr->iYStart;
435     if (iYDiff)
436     {
437         if (infoPtr->hLrgWnd != NULL)
438         {
439             ShowWindow(infoPtr->hLrgWnd, SW_HIDE);
440         }
441 
442         SetScrollPos(infoPtr->hMapWnd,
443                      SB_VERT,
444                      infoPtr->iYStart,
445                      TRUE);
446 
447         if (abs(iYDiff) < YCELLS)
448         {
449             RECT rect;
450             GetClientRect(infoPtr->hMapWnd, &rect);
451             rect.top += 2;
452             rect.bottom -= 2;
453             ScrollWindowEx(infoPtr->hMapWnd,
454                            0,
455                            iYDiff * infoPtr->CellSize.cy,
456                            &rect,
457                            &rect,
458                            NULL,
459                            NULL,
460                            SW_INVALIDATE);
461         }
462         else
463         {
464             InvalidateRect(infoPtr->hMapWnd,
465                            NULL,
466                            TRUE);
467         }
468 
469         if (infoPtr->hLrgWnd != NULL)
470         {
471             ShowWindow(infoPtr->hLrgWnd, SW_SHOW);
472         }
473     }
474 }
475 
476 
477 static
478 VOID
479 OnPaint(PMAP infoPtr,
480         WPARAM wParam)
481 {
482     PAINTSTRUCT ps;
483     HDC hdc;
484 
485 
486     if (wParam != 0)
487     {
488         if (!GetUpdateRect(infoPtr->hMapWnd,
489                            &ps.rcPaint,
490                            TRUE))
491         {
492             return;
493         }
494         ps.hdc = (HDC)wParam;
495     }
496     else
497     {
498         hdc = BeginPaint(infoPtr->hMapWnd,
499                          &ps);
500         if (hdc == NULL)
501         {
502             return;
503         }
504     }
505 
506     DrawGrid(infoPtr, &ps);
507 
508     FillGrid(infoPtr, &ps);
509 
510     if (wParam == 0)
511     {
512         EndPaint(infoPtr->hMapWnd,
513                  &ps);
514     }
515 }
516 
517 
518 LRESULT
519 CALLBACK
520 MapWndProc(HWND hwnd,
521            UINT uMsg,
522            WPARAM wParam,
523            LPARAM lParam)
524 {
525     PMAP infoPtr;
526     LRESULT Ret = 0;
527 
528     infoPtr = (PMAP)GetWindowLongPtrW(hwnd,
529                                       0);
530 
531     switch (uMsg)
532     {
533         case WM_CREATE:
534         {
535             if (!MapOnCreate(infoPtr,
536                              hwnd,
537                              ((LPCREATESTRUCTW)lParam)->hwndParent))
538             {
539                 return (LRESULT)-1;
540             }
541 
542             break;
543         }
544 
545         case WM_LBUTTONDOWN:
546         {
547             OnClick(infoPtr,
548                     LOWORD(lParam),
549                     HIWORD(lParam));
550 
551             break;
552         }
553 
554         case WM_MOUSEMOVE:
555         {
556             if (wParam & MK_LBUTTON)
557             {
558                 OnClick(infoPtr,
559                         LOWORD(lParam),
560                         HIWORD(lParam));
561             }
562             break;
563         }
564 
565         case WM_LBUTTONDBLCLK:
566         {
567             NotifyParentOfSelection(infoPtr,
568                                     FM_SETCHAR,
569                                     infoPtr->pActiveCell->ch);
570 
571             if (infoPtr->pActiveCell->bLarge)
572             {
573                 DestroyWindow(infoPtr->hLrgWnd);
574                 infoPtr->hLrgWnd = NULL;
575             }
576 
577             infoPtr->pActiveCell->bLarge = FALSE;
578 
579             break;
580         }
581 
582         case WM_VSCROLL:
583         {
584             OnVScroll(infoPtr,
585                       LOWORD(wParam),
586                       HIWORD(wParam));
587 
588             break;
589         }
590 
591         case FM_SETFONT:
592             SetFont(infoPtr, (LPWSTR)lParam);
593             break;
594 
595         case FM_GETCHAR:
596         {
597             if (!infoPtr->pActiveCell) return 0;
598             return infoPtr->pActiveCell->ch;
599         }
600 
601         case FM_GETHFONT:
602             return (LRESULT)infoPtr->hFont;
603 
604         case WM_PAINT:
605         {
606             OnPaint(infoPtr,
607                     wParam);
608             break;
609         }
610 
611         case WM_DESTROY:
612         {
613             DeleteObject(infoPtr->hFont);
614             HeapFree(GetProcessHeap(),
615                      0,
616                      infoPtr);
617             SetWindowLongPtrW(hwnd,
618                               0,
619                               (DWORD_PTR)NULL);
620             break;
621         }
622 
623         default:
624         {
625             Ret = DefWindowProcW(hwnd,
626                                  uMsg,
627                                  wParam,
628                                  lParam);
629             break;
630         }
631     }
632 
633     return Ret;
634 }
635 
636 
637 BOOL
638 RegisterMapClasses(HINSTANCE hInstance)
639 {
640     WNDCLASSW wc = {0};
641 
642     wc.style = CS_DBLCLKS;
643     wc.lpfnWndProc = MapWndProc;
644     wc.cbWndExtra = sizeof(PMAP);
645     wc.hInstance = hInstance;
646     wc.hCursor = LoadCursorW(NULL,
647                             (LPWSTR)IDC_ARROW);
648     wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
649     wc.lpszClassName = szMapWndClass;
650 
651     if (RegisterClassW(&wc))
652     {
653         wc.lpfnWndProc = LrgCellWndProc;
654         wc.cbWndExtra = 0;
655         wc.lpszClassName = szLrgCellWndClass;
656 
657         return RegisterClassW(&wc) != 0;
658     }
659 
660     return FALSE;
661 }
662 
663 VOID
664 UnregisterMapClasses(HINSTANCE hInstance)
665 {
666     UnregisterClassW(szMapWndClass,
667                     hInstance);
668 
669     UnregisterClassW(szLrgCellWndClass,
670                     hInstance);
671 }
672