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