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