xref: /reactos/base/applications/charmap/map.c (revision 595b846d)
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 = 0; 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     POINT pt;
316     INT x, y;
317 
318     pt.x = ptx;
319     pt.y = pty;
320 
321     for (x = 0; x < XCELLS; x++)
322     for (y = 0; y < YCELLS; y++)
323     {
324         if (PtInRect(&infoPtr->Cells[y][x].CellInt,
325                      pt))
326         {
327             /* if the cell is not already active */
328             if (!infoPtr->Cells[y][x].bActive)
329             {
330                 /* set previous active cell to inactive */
331                 if (infoPtr->pActiveCell)
332                 {
333                     /* invalidate normal cells, required when
334                      * moving a small active cell via keyboard */
335                     if (!infoPtr->pActiveCell->bLarge)
336                     {
337                         InvalidateRect(infoPtr->hMapWnd,
338                                        &infoPtr->pActiveCell->CellInt,
339                                        TRUE);
340                     }
341 
342                     infoPtr->pActiveCell->bActive = FALSE;
343                     infoPtr->pActiveCell->bLarge = FALSE;
344                 }
345 
346                 /* set new cell to active */
347                 infoPtr->pActiveCell = &infoPtr->Cells[y][x];
348                 infoPtr->pActiveCell->bActive = TRUE;
349                 infoPtr->pActiveCell->bLarge = TRUE;
350                 if (infoPtr->hLrgWnd)
351                     MoveLargeCell(infoPtr);
352                 else
353                     CreateLargeCell(infoPtr);
354             }
355             else
356             {
357                 /* flick between large and small */
358                 if (infoPtr->pActiveCell->bLarge)
359                 {
360                     DestroyWindow(infoPtr->hLrgWnd);
361                     infoPtr->hLrgWnd = NULL;
362                 }
363                 else
364                 {
365                     CreateLargeCell(infoPtr);
366                 }
367 
368                 infoPtr->pActiveCell->bLarge = (infoPtr->pActiveCell->bLarge) ? FALSE : TRUE;
369             }
370 
371             break;
372         }
373     }
374 }
375 
376 
377 static
378 BOOL
379 MapOnCreate(PMAP infoPtr,
380             HWND hwnd,
381             HWND hParent)
382 {
383     RECT rc;
384     BOOL Ret = FALSE;
385 
386     infoPtr = HeapAlloc(GetProcessHeap(),
387                         0,
388                         sizeof(MAP));
389     if (infoPtr)
390     {
391         SetLastError(0);
392         SetWindowLongPtrW(hwnd,
393                           0,
394                           (DWORD_PTR)infoPtr);
395         if (GetLastError() == 0)
396         {
397             ZeroMemory(infoPtr,
398                        sizeof(MAP));
399 
400             infoPtr->hMapWnd = hwnd;
401             infoPtr->hParent = hParent;
402 
403             GetClientRect(hwnd, &rc);
404             infoPtr->ClientSize.cx = rc.right;
405             infoPtr->ClientSize.cy = rc.bottom;
406             infoPtr->CellSize.cx = infoPtr->ClientSize.cx / XCELLS;
407             infoPtr->CellSize.cy = infoPtr->ClientSize.cy / YCELLS;
408 
409             infoPtr->pActiveCell = NULL;
410 
411             SetGrid(infoPtr);
412 
413             SetScrollPos(infoPtr->hParent, SB_VERT, 0, TRUE);
414 
415             Ret = TRUE;
416         }
417     }
418 
419     return Ret;
420 }
421 
422 
423 static
424 VOID
425 OnVScroll(PMAP infoPtr,
426           INT Value,
427           INT Pos)
428 {
429     INT iYDiff, iOldYStart = infoPtr->iYStart;
430 
431     switch (Value)
432     {
433         case SB_LINEUP:
434             infoPtr->iYStart -=  1;
435             break;
436 
437         case SB_LINEDOWN:
438             infoPtr->iYStart +=  1;
439             break;
440 
441         case SB_PAGEUP:
442             infoPtr->iYStart -= YCELLS;
443             break;
444 
445         case SB_PAGEDOWN:
446             infoPtr->iYStart += YCELLS;
447             break;
448 
449         case SB_THUMBTRACK:
450             infoPtr->iYStart = Pos;
451             break;
452 
453        default:
454             break;
455        }
456 
457     infoPtr->iYStart = max(0, infoPtr->iYStart);
458     infoPtr->iYStart = min(infoPtr->iYStart, infoPtr->NumRows);
459 
460     iYDiff = iOldYStart - infoPtr->iYStart;
461     if (iYDiff)
462     {
463         if (infoPtr->hLrgWnd != NULL)
464         {
465             ShowWindow(infoPtr->hLrgWnd, SW_HIDE);
466         }
467 
468         SetScrollPos(infoPtr->hMapWnd,
469                      SB_VERT,
470                      infoPtr->iYStart,
471                      TRUE);
472 
473         if (abs(iYDiff) < YCELLS)
474         {
475             RECT rect;
476             GetClientRect(infoPtr->hMapWnd, &rect);
477             rect.top += 2;
478             rect.bottom -= 2;
479             ScrollWindowEx(infoPtr->hMapWnd,
480                            0,
481                            iYDiff * infoPtr->CellSize.cy,
482                            &rect,
483                            &rect,
484                            NULL,
485                            NULL,
486                            SW_INVALIDATE);
487         }
488         else
489         {
490             InvalidateRect(infoPtr->hMapWnd,
491                            NULL,
492                            TRUE);
493         }
494 
495         if (infoPtr->hLrgWnd != NULL)
496         {
497             ShowWindow(infoPtr->hLrgWnd, SW_SHOW);
498         }
499     }
500 }
501 
502 
503 static
504 VOID
505 OnPaint(PMAP infoPtr,
506         WPARAM wParam)
507 {
508     PAINTSTRUCT ps;
509     HDC hdc;
510 
511 
512     if (wParam != 0)
513     {
514         if (!GetUpdateRect(infoPtr->hMapWnd,
515                            &ps.rcPaint,
516                            TRUE))
517         {
518             return;
519         }
520         ps.hdc = (HDC)wParam;
521     }
522     else
523     {
524         hdc = BeginPaint(infoPtr->hMapWnd,
525                          &ps);
526         if (hdc == NULL)
527         {
528             return;
529         }
530     }
531 
532     DrawGrid(infoPtr, &ps);
533 
534     FillGrid(infoPtr, &ps);
535 
536     if (wParam == 0)
537     {
538         EndPaint(infoPtr->hMapWnd,
539                  &ps);
540     }
541 }
542 
543 
544 LRESULT
545 CALLBACK
546 MapWndProc(HWND hwnd,
547            UINT uMsg,
548            WPARAM wParam,
549            LPARAM lParam)
550 {
551     PMAP infoPtr;
552     LRESULT Ret = 0;
553 
554     infoPtr = (PMAP)GetWindowLongPtrW(hwnd,
555                                       0);
556 
557     switch (uMsg)
558     {
559         case WM_CREATE:
560         {
561             if (!MapOnCreate(infoPtr,
562                              hwnd,
563                              ((LPCREATESTRUCTW)lParam)->hwndParent))
564             {
565                 return (LRESULT)-1;
566             }
567 
568             break;
569         }
570 
571         case WM_LBUTTONDOWN:
572         {
573             OnClick(infoPtr,
574                     LOWORD(lParam),
575                     HIWORD(lParam));
576 
577             break;
578         }
579 
580         case WM_LBUTTONDBLCLK:
581         {
582             NotifyParentOfSelection(infoPtr,
583                                     FM_SETCHAR,
584                                     infoPtr->pActiveCell->ch);
585 
586 
587             break;
588         }
589 
590         case WM_VSCROLL:
591         {
592             OnVScroll(infoPtr,
593                       LOWORD(wParam),
594                       HIWORD(wParam));
595 
596             break;
597         }
598 
599         case FM_SETFONT:
600             SetFont(infoPtr, (LPWSTR)lParam);
601             break;
602 
603         case FM_GETCHAR:
604         {
605             if (!infoPtr->pActiveCell) return 0;
606             return infoPtr->pActiveCell->ch;
607         }
608 
609         case FM_GETHFONT:
610             return (LRESULT)infoPtr->hFont;
611 
612         case WM_PAINT:
613         {
614             OnPaint(infoPtr,
615                     wParam);
616             break;
617         }
618 
619         case WM_DESTROY:
620         {
621             DeleteObject(infoPtr->hFont);
622             HeapFree(GetProcessHeap(),
623                      0,
624                      infoPtr);
625             SetWindowLongPtrW(hwnd,
626                               0,
627                               (DWORD_PTR)NULL);
628             break;
629         }
630 
631         default:
632         {
633             Ret = DefWindowProcW(hwnd,
634                                  uMsg,
635                                  wParam,
636                                  lParam);
637             break;
638         }
639     }
640 
641     return Ret;
642 }
643 
644 
645 BOOL
646 RegisterMapClasses(HINSTANCE hInstance)
647 {
648     WNDCLASSW wc = {0};
649 
650     wc.style = CS_DBLCLKS;
651     wc.lpfnWndProc = MapWndProc;
652     wc.cbWndExtra = sizeof(PMAP);
653     wc.hInstance = hInstance;
654     wc.hCursor = LoadCursorW(NULL,
655                             (LPWSTR)IDC_ARROW);
656     wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
657     wc.lpszClassName = szMapWndClass;
658 
659     if (RegisterClassW(&wc))
660     {
661         wc.lpfnWndProc = LrgCellWndProc;
662         wc.cbWndExtra = 0;
663         wc.lpszClassName = szLrgCellWndClass;
664 
665         return RegisterClassW(&wc) != 0;
666     }
667 
668     return FALSE;
669 }
670 
671 VOID
672 UnregisterMapClasses(HINSTANCE hInstance)
673 {
674     UnregisterClassW(szMapWndClass,
675                     hInstance);
676 
677     UnregisterClassW(szLrgCellWndClass,
678                     hInstance);
679 }
680