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