[SHELL/EXPERIMENTS]
[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 wcscpy(infoPtr->CurrentFont.lfFaceName,
232 lpFontName);
233
234 infoPtr->hFont = CreateFontIndirectW(&infoPtr->CurrentFont);
235
236 InvalidateRect(infoPtr->hMapWnd,
237 NULL,
238 TRUE);
239
240 /* Test if zoom window must be reopened */
241 if (infoPtr->pActiveCell != NULL &&
242 infoPtr->pActiveCell->bLarge)
243 {
244 CreateLargeCell(infoPtr);
245 }
246 }
247
248
249 static
250 LRESULT
251 NotifyParentOfSelection(PMAP infoPtr,
252 UINT code,
253 WCHAR ch)
254 {
255 LRESULT Ret = 0;
256
257 if (infoPtr->hParent != NULL)
258 {
259 DWORD dwIdc = GetWindowLongPtr(infoPtr->hMapWnd, GWLP_ID);
260 /*
261 * Push directly into the event queue instead of waiting
262 * the parent to be unlocked.
263 * High word of LPARAM is still available for future needs...
264 */
265 Ret = PostMessage(infoPtr->hParent,
266 WM_COMMAND,
267 MAKELPARAM((WORD)dwIdc, (WORD)code),
268 (LPARAM)LOWORD(ch));
269 }
270
271 return Ret;
272 }
273
274
275 static
276 VOID
277 OnClick(PMAP infoPtr,
278 WORD ptx,
279 WORD pty)
280 {
281 POINT pt;
282 INT x, y;
283
284 pt.x = ptx;
285 pt.y = pty;
286
287 for (x = 0; x < XCELLS; x++)
288 for (y = 0; y < YCELLS; y++)
289 {
290 if (PtInRect(&infoPtr->Cells[y][x].CellInt,
291 pt))
292 {
293 /* if the cell is not already active */
294 if (!infoPtr->Cells[y][x].bActive)
295 {
296 /* set previous active cell to inactive */
297 if (infoPtr->pActiveCell)
298 {
299 /* invalidate normal cells, required when
300 * moving a small active cell via keyboard */
301 if (!infoPtr->pActiveCell->bLarge)
302 {
303 InvalidateRect(infoPtr->hMapWnd,
304 &infoPtr->pActiveCell->CellInt,
305 TRUE);
306 }
307
308 infoPtr->pActiveCell->bActive = FALSE;
309 infoPtr->pActiveCell->bLarge = FALSE;
310 }
311
312 /* set new cell to active */
313 infoPtr->pActiveCell = &infoPtr->Cells[y][x];
314 infoPtr->pActiveCell->bActive = TRUE;
315 infoPtr->pActiveCell->bLarge = TRUE;
316 if (infoPtr->hLrgWnd)
317 MoveLargeCell(infoPtr);
318 else
319 CreateLargeCell(infoPtr);
320 }
321 else
322 {
323 /* flick between large and small */
324 if (infoPtr->pActiveCell->bLarge)
325 {
326 DestroyWindow(infoPtr->hLrgWnd);
327 infoPtr->hLrgWnd = NULL;
328 }
329 else
330 {
331 CreateLargeCell(infoPtr);
332 }
333
334 infoPtr->pActiveCell->bLarge = (infoPtr->pActiveCell->bLarge) ? FALSE : TRUE;
335 }
336
337 break;
338 }
339 }
340 }
341
342
343 static
344 BOOL
345 OnCreate(PMAP infoPtr,
346 HWND hwnd,
347 HWND hParent)
348 {
349 RECT rc;
350 BOOL Ret = FALSE;
351
352 infoPtr = HeapAlloc(GetProcessHeap(),
353 0,
354 sizeof(MAP));
355 if (infoPtr)
356 {
357 SetLastError(0);
358 SetWindowLongPtrW(hwnd,
359 0,
360 (DWORD_PTR)infoPtr);
361 if (GetLastError() == 0)
362 {
363 ZeroMemory(infoPtr,
364 sizeof(MAP));
365
366 infoPtr->hMapWnd = hwnd;
367 infoPtr->hParent = hParent;
368
369 GetClientRect(hwnd, &rc);
370 infoPtr->ClientSize.cx = rc.right;
371 infoPtr->ClientSize.cy = rc.bottom;
372 infoPtr->CellSize.cx = infoPtr->ClientSize.cx / XCELLS;
373 infoPtr->CellSize.cy = infoPtr->ClientSize.cy / YCELLS;
374
375 infoPtr->pActiveCell = NULL;
376
377 SetGrid(infoPtr);
378
379 SetScrollRange(hwnd, SB_VERT, 0, 255, FALSE);
380 SetScrollPos(hwnd, SB_VERT, 0, TRUE);
381
382 Ret = TRUE;
383 }
384 }
385
386 return Ret;
387 }
388
389
390 static
391 VOID
392 OnVScroll(PMAP infoPtr,
393 INT Value,
394 INT Pos)
395 {
396 INT iYDiff, iOldYStart = infoPtr->iYStart;
397
398 switch (Value)
399 {
400 case SB_LINEUP:
401 infoPtr->iYStart -= 1;
402 break;
403
404 case SB_LINEDOWN:
405 infoPtr->iYStart += 1;
406 break;
407
408 case SB_PAGEUP:
409 infoPtr->iYStart -= YCELLS;
410 break;
411
412 case SB_PAGEDOWN:
413 infoPtr->iYStart += YCELLS;
414 break;
415
416 case SB_THUMBTRACK:
417 infoPtr->iYStart = Pos;
418 break;
419
420 default:
421 break;
422 }
423
424 infoPtr->iYStart = max(0,
425 min(infoPtr->iYStart, 255*16));
426
427 iYDiff = iOldYStart - infoPtr->iYStart;
428 if (iYDiff)
429 {
430 if (infoPtr->hLrgWnd != NULL)
431 {
432 ShowWindow(infoPtr->hLrgWnd, SW_HIDE);
433 }
434
435 SetScrollPos(infoPtr->hMapWnd,
436 SB_VERT,
437 infoPtr->iYStart,
438 TRUE);
439
440 if (abs(iYDiff) < YCELLS)
441 {
442 RECT rect;
443 GetClientRect(infoPtr->hMapWnd, &rect);
444 rect.top += 2;
445 rect.bottom -= 2;
446 ScrollWindowEx(infoPtr->hMapWnd,
447 0,
448 iYDiff * infoPtr->CellSize.cy,
449 &rect,
450 &rect,
451 NULL,
452 NULL,
453 SW_INVALIDATE);
454 }
455 else
456 {
457 InvalidateRect(infoPtr->hMapWnd,
458 NULL,
459 TRUE);
460 }
461
462 if (infoPtr->hLrgWnd != NULL)
463 {
464 ShowWindow(infoPtr->hLrgWnd, SW_SHOW);
465 }
466 }
467 }
468
469
470 static
471 VOID
472 OnPaint(PMAP infoPtr,
473 WPARAM wParam)
474 {
475 PAINTSTRUCT ps;
476 HDC hdc;
477
478
479 if (wParam != 0)
480 {
481 if (!GetUpdateRect(infoPtr->hMapWnd,
482 &ps.rcPaint,
483 TRUE))
484 {
485 return;
486 }
487 hdc = (HDC)wParam;
488 }
489 else
490 {
491 hdc = BeginPaint(infoPtr->hMapWnd,
492 &ps);
493 if (hdc == NULL)
494 {
495 return;
496 }
497 }
498
499 DrawGrid(infoPtr, &ps);
500
501 FillGrid(infoPtr, &ps);
502
503 if (wParam == 0)
504 {
505 EndPaint(infoPtr->hMapWnd,
506 &ps);
507 }
508 }
509
510
511 LRESULT
512 CALLBACK
513 MapWndProc(HWND hwnd,
514 UINT uMsg,
515 WPARAM wParam,
516 LPARAM lParam)
517 {
518 PMAP infoPtr;
519 LRESULT Ret = 0;
520
521 infoPtr = (PMAP)GetWindowLongPtrW(hwnd,
522 0);
523
524 switch (uMsg)
525 {
526 case WM_CREATE:
527 {
528 if (!OnCreate(infoPtr,
529 hwnd,
530 ((LPCREATESTRUCTW)lParam)->hwndParent))
531 {
532 return (LRESULT)-1;
533 }
534
535 break;
536 }
537
538 case WM_LBUTTONDOWN:
539 {
540 OnClick(infoPtr,
541 LOWORD(lParam),
542 HIWORD(lParam));
543
544 break;
545 }
546
547 case WM_LBUTTONDBLCLK:
548 {
549 NotifyParentOfSelection(infoPtr,
550 FM_SETCHAR,
551 infoPtr->pActiveCell->ch);
552
553
554 break;
555 }
556
557 case WM_VSCROLL:
558 {
559 OnVScroll(infoPtr,
560 LOWORD(wParam),
561 HIWORD(wParam));
562
563 break;
564 }
565
566 case FM_SETFONT:
567 SetFont(infoPtr, (LPWSTR)lParam);
568 break;
569
570 case FM_GETCHAR:
571 {
572 if (!infoPtr->pActiveCell) return 0;
573 return infoPtr->pActiveCell->ch;
574 }
575
576 case FM_GETHFONT:
577 return (LRESULT)infoPtr->hFont;
578
579 case WM_PAINT:
580 {
581 OnPaint(infoPtr,
582 wParam);
583 break;
584 }
585
586 case WM_DESTROY:
587 {
588 DeleteObject(infoPtr->hFont);
589 HeapFree(GetProcessHeap(),
590 0,
591 infoPtr);
592 SetWindowLongPtrW(hwnd,
593 0,
594 (DWORD_PTR)NULL);
595 break;
596 }
597
598 default:
599 {
600 Ret = DefWindowProcW(hwnd,
601 uMsg,
602 wParam,
603 lParam);
604 break;
605 }
606 }
607
608 return Ret;
609 }
610
611
612 BOOL
613 RegisterMapClasses(HINSTANCE hInstance)
614 {
615 WNDCLASSW wc = {0};
616
617 wc.style = CS_DBLCLKS;
618 wc.lpfnWndProc = MapWndProc;
619 wc.cbWndExtra = sizeof(PMAP);
620 wc.hInstance = hInstance;
621 wc.hCursor = LoadCursorW(NULL,
622 (LPWSTR)IDC_ARROW);
623 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
624 wc.lpszClassName = szMapWndClass;
625
626 if (RegisterClassW(&wc))
627 {
628 wc.lpfnWndProc = LrgCellWndProc;
629 wc.cbWndExtra = 0;
630 wc.lpszClassName = szLrgCellWndClass;
631
632 return RegisterClassW(&wc) != 0;
633 }
634
635 return FALSE;
636 }
637
638 VOID
639 UnregisterMapClasses(HINSTANCE hInstance)
640 {
641 UnregisterClassW(szMapWndClass,
642 hInstance);
643
644 UnregisterClassW(szLrgCellWndClass,
645 hInstance);
646 }