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