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