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