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>
15 static const WCHAR szMapWndClass
[] = L
"FontMapWnd";
16 static const WCHAR szLrgCellWndClass
[] = L
"LrgCellWnd";
18 #define MAX_ROWS (0xFFFF / XCELLS) + 1 - YCELLS
27 for (y
= 0; y
< YCELLS
; y
++)
28 for (x
= 0; x
< XCELLS
; x
++)
30 infoPtr
->Cells
[y
][x
].CellExt
.left
= x
* infoPtr
->CellSize
.cx
+ 1;
31 infoPtr
->Cells
[y
][x
].CellExt
.top
= y
* infoPtr
->CellSize
.cy
+ 1;
32 infoPtr
->Cells
[y
][x
].CellExt
.right
= (x
+ 1) * infoPtr
->CellSize
.cx
+ 2;
33 infoPtr
->Cells
[y
][x
].CellExt
.bottom
= (y
+ 1) * infoPtr
->CellSize
.cy
+ 2;
35 CopyRect(&infoPtr
->Cells
[y
][x
].CellInt
,
36 &infoPtr
->Cells
[y
][x
].CellExt
);
38 InflateRect(&infoPtr
->Cells
[y
][x
].CellInt
,
47 DrawActiveCell(PMAP infoPtr
,
51 infoPtr
->pActiveCell
->CellInt
.left
,
52 infoPtr
->pActiveCell
->CellInt
.top
,
53 infoPtr
->pActiveCell
->CellInt
.right
,
54 infoPtr
->pActiveCell
->CellInt
.bottom
);
61 DrawGrid(PMAP infoPtr
,
68 for (y
= 0; y
< YCELLS
; y
++)
69 for (x
= 0; x
< XCELLS
; x
++)
71 Cell
= &infoPtr
->Cells
[y
][x
];
73 if (!IntersectRect(&rc
,
84 Cell
->CellExt
.bottom
);
86 if (infoPtr
->pActiveCell
== Cell
)
88 DrawActiveCell(infoPtr
, ps
->hdc
);
96 FillGrid(PMAP infoPtr
,
106 hOldFont
= SelectObject(ps
->hdc
,
109 i
= XCELLS
* infoPtr
->iYStart
;
113 for (y
= 0; y
< YCELLS
; y
++)
114 for (x
= 0; x
< XCELLS
; x
++)
116 if (i
>= infoPtr
->NumValidGlyphs
) break;
118 ch
= (WCHAR
)infoPtr
->ValidGlyphs
[i
];
120 Cell
= &infoPtr
->Cells
[y
][x
];
122 if (IntersectRect(&rc
,
132 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
);
140 SelectObject(ps
->hdc
,
147 CreateLargeCell(PMAP infoPtr
)
152 &infoPtr
->pActiveCell
->CellExt
);
154 MapWindowPoints(infoPtr
->hMapWnd
,
163 infoPtr
->hLrgWnd
= CreateWindowExW(0,
166 WS_CHILDWINDOW
| WS_VISIBLE
,
169 rLarge
.right
- rLarge
.left
,
170 rLarge
.bottom
- rLarge
.top
,
175 if (!infoPtr
->hLrgWnd
)
184 MoveLargeCell(PMAP infoPtr
)
189 &infoPtr
->pActiveCell
->CellExt
);
191 MapWindowPoints(infoPtr
->hMapWnd
,
200 MoveWindow(infoPtr
->hLrgWnd
,
203 rLarge
.right
- rLarge
.left
,
204 rLarge
.bottom
- rLarge
.top
,
207 InvalidateRect(infoPtr
->hLrgWnd
,
215 GetPossibleCharacters(WCHAR
* ch
, INT chLen
, INT codePageIdx
)
219 memset(ch
, 0, sizeof(ch
[0]) * chLen
);
221 if (codePageIdx
<= 0 || codePageIdx
> SIZEOF(codePages
))
223 /* this is unicode, so just load up the first MAX_GLYPHS characters
224 start at 0x21 to bypass whitespace characters */
225 INT len
= min(MAX_GLYPHS
, chLen
);
226 for (i
= 0x21, j
= 0; i
< len
; i
++)
231 /* This is a codepage, so use NLS to translate the first 256 characters */
232 CHAR multiByteString
[256] = { 0 };
233 for (i
= 0x21; i
< SIZEOF(multiByteString
); i
++)
234 multiByteString
[i
] = (CHAR
)i
;
236 if (!MultiByteToWideChar(codePages
[codePageIdx
- 1], 0, multiByteString
, sizeof(multiByteString
), ch
, chLen
))
238 /* Failed for some reason, so clear the array */
239 memset(ch
, 0, sizeof(ch
[0]) * chLen
);
247 SetFont(PMAP infoPtr
,
251 WCHAR ch
[MAX_GLYPHS
];
252 WORD out
[MAX_GLYPHS
];
255 /* Destroy Zoom window, since it was created with older font */
256 DestroyWindow(infoPtr
->hLrgWnd
);
257 infoPtr
->hLrgWnd
= NULL
;
260 DeleteObject(infoPtr
->hFont
);
262 ZeroMemory(&infoPtr
->CurrentFont
,
265 hdc
= GetDC(infoPtr
->hMapWnd
);
266 infoPtr
->CurrentFont
.lfHeight
= GetDeviceCaps(hdc
, LOGPIXELSY
) / 5;
268 infoPtr
->CurrentFont
.lfCharSet
= DEFAULT_CHARSET
;
269 lstrcpynW(infoPtr
->CurrentFont
.lfFaceName
,
271 SIZEOF(infoPtr
->CurrentFont
.lfFaceName
));
273 infoPtr
->hFont
= CreateFontIndirectW(&infoPtr
->CurrentFont
);
275 InvalidateRect(infoPtr
->hMapWnd
,
279 if (infoPtr
->pActiveCell
)
280 infoPtr
->pActiveCell
->bActive
= FALSE
;
281 infoPtr
->pActiveCell
= &infoPtr
->Cells
[0][0];
282 infoPtr
->pActiveCell
->bActive
= TRUE
;
284 // Get all the valid glyphs in this font
286 SelectObject(hdc
, infoPtr
->hFont
);
288 // Get the code page associated with the selected 'character set'
289 GetPossibleCharacters(ch
, MAX_GLYPHS
, infoPtr
->CharMap
);
291 if (GetGlyphIndicesW(hdc
,
295 GGI_MARK_NONEXISTING_GLYPHS
) != GDI_ERROR
)
298 for (i
= 0; i
< MAX_GLYPHS
; i
++)
300 if (out
[i
] != 0xffff && out
[i
] != 0x0000 && ch
[i
] != 0x0000)
302 infoPtr
->ValidGlyphs
[j
] = ch
[i
];
306 infoPtr
->NumValidGlyphs
= j
;
309 ReleaseDC(infoPtr
->hMapWnd
, hdc
);
311 infoPtr
->NumRows
= infoPtr
->NumValidGlyphs
/ XCELLS
;
312 if (infoPtr
->NumValidGlyphs
% XCELLS
)
313 infoPtr
->NumRows
+= 1;
314 infoPtr
->NumRows
= (infoPtr
->NumRows
> YCELLS
) ? infoPtr
->NumRows
- YCELLS
: 0;
316 SetScrollRange(infoPtr
->hMapWnd
, SB_VERT
, 0, infoPtr
->NumRows
, FALSE
);
317 SetScrollPos(infoPtr
->hMapWnd
, SB_VERT
, 0, TRUE
);
318 infoPtr
->iYStart
= 0;
324 NotifyParentOfSelection(PMAP infoPtr
,
330 if (infoPtr
->hParent
!= NULL
)
332 DWORD dwIdc
= GetWindowLongPtr(infoPtr
->hMapWnd
, GWLP_ID
);
334 * Push directly into the event queue instead of waiting
335 * the parent to be unlocked.
336 * High word of LPARAM is still available for future needs...
338 Ret
= PostMessage(infoPtr
->hParent
,
340 MAKELPARAM((WORD
)dwIdc
, (WORD
)code
),
350 OnClick(PMAP infoPtr
,
357 * Find the cell the mouse pointer is over.
358 * Since each cell is the same size, this can be done quickly using CellSize.
359 * Clamp to XCELLS - 1 and YCELLS - 1 because the map can sometimes be slightly
360 * larger than infoPtr.CellSize * XCELLS , due to the map size being a non integer
361 * multiple of infoPtr.CellSize .
363 x
= min(XCELLS
- 1, ptx
/ max(1, infoPtr
->CellSize
.cx
));
364 y
= min(YCELLS
- 1, pty
/ max(1, infoPtr
->CellSize
.cy
));
366 /* Make sure the mouse is within a valid glyph */
367 i
= XCELLS
* infoPtr
->iYStart
+ y
* XCELLS
+ x
;
368 if (i
>= infoPtr
->NumValidGlyphs
)
370 if (infoPtr
->pActiveCell
)
371 infoPtr
->pActiveCell
->bActive
= FALSE
;
372 infoPtr
->pActiveCell
= NULL
;
376 /* if the cell is not already active */
377 if (!infoPtr
->Cells
[y
][x
].bActive
)
379 /* set previous active cell to inactive */
380 if (infoPtr
->pActiveCell
)
382 /* invalidate normal cells, required when
383 * moving a small active cell via keyboard */
384 if (!infoPtr
->pActiveCell
->bLarge
)
386 InvalidateRect(infoPtr
->hMapWnd
,
387 &infoPtr
->pActiveCell
->CellInt
,
391 infoPtr
->pActiveCell
->bActive
= FALSE
;
392 infoPtr
->pActiveCell
->bLarge
= FALSE
;
395 /* set new cell to active */
396 infoPtr
->pActiveCell
= &infoPtr
->Cells
[y
][x
];
397 infoPtr
->pActiveCell
->bActive
= TRUE
;
398 infoPtr
->pActiveCell
->bLarge
= TRUE
;
399 if (infoPtr
->hLrgWnd
)
400 MoveLargeCell(infoPtr
);
402 CreateLargeCell(infoPtr
);
409 MapOnCreate(PMAP infoPtr
,
416 infoPtr
= HeapAlloc(GetProcessHeap(),
422 SetWindowLongPtrW(hwnd
,
425 if (GetLastError() == 0)
430 infoPtr
->hMapWnd
= hwnd
;
431 infoPtr
->hParent
= hParent
;
433 GetClientRect(hwnd
, &rc
);
434 infoPtr
->ClientSize
.cx
= rc
.right
;
435 infoPtr
->ClientSize
.cy
= rc
.bottom
;
436 infoPtr
->CellSize
.cx
= infoPtr
->ClientSize
.cx
/ XCELLS
;
437 infoPtr
->CellSize
.cy
= infoPtr
->ClientSize
.cy
/ YCELLS
;
439 infoPtr
->pActiveCell
= NULL
;
443 SetScrollPos(infoPtr
->hParent
, SB_VERT
, 0, TRUE
);
455 OnVScroll(PMAP infoPtr
,
459 INT iYDiff
, iOldYStart
= infoPtr
->iYStart
;
464 infoPtr
->iYStart
-= 1;
468 infoPtr
->iYStart
+= 1;
472 infoPtr
->iYStart
-= YCELLS
;
476 infoPtr
->iYStart
+= YCELLS
;
480 infoPtr
->iYStart
= Pos
;
487 infoPtr
->iYStart
= max(0, infoPtr
->iYStart
);
488 infoPtr
->iYStart
= min(infoPtr
->iYStart
, infoPtr
->NumRows
);
490 iYDiff
= iOldYStart
- infoPtr
->iYStart
;
493 if (infoPtr
->hLrgWnd
!= NULL
)
495 ShowWindow(infoPtr
->hLrgWnd
, SW_HIDE
);
498 SetScrollPos(infoPtr
->hMapWnd
,
503 if (abs(iYDiff
) < YCELLS
)
507 /* Invalidate the rect around the active cell since a new cell will become active */
508 if (infoPtr
->pActiveCell
&& infoPtr
->pActiveCell
->bActive
)
510 InvalidateRect(infoPtr
->hMapWnd
,
511 &infoPtr
->pActiveCell
->CellExt
,
515 GetClientRect(infoPtr
->hMapWnd
, &rect
);
518 ScrollWindowEx(infoPtr
->hMapWnd
,
520 iYDiff
* infoPtr
->CellSize
.cy
,
529 InvalidateRect(infoPtr
->hMapWnd
,
534 if (infoPtr
->hLrgWnd
!= NULL
)
536 ShowWindow(infoPtr
->hLrgWnd
, SW_SHOW
);
544 OnPaint(PMAP infoPtr
,
553 if (!GetUpdateRect(infoPtr
->hMapWnd
,
559 ps
.hdc
= (HDC
)wParam
;
563 hdc
= BeginPaint(infoPtr
->hMapWnd
,
571 DrawGrid(infoPtr
, &ps
);
573 FillGrid(infoPtr
, &ps
);
577 EndPaint(infoPtr
->hMapWnd
,
585 MapWndProc(HWND hwnd
,
592 WCHAR lfFaceName
[LF_FACESIZE
];
594 infoPtr
= (PMAP
)GetWindowLongPtrW(hwnd
,
601 if (!MapOnCreate(infoPtr
,
603 ((LPCREATESTRUCTW
)lParam
)->hwndParent
))
622 if (wParam
& MK_LBUTTON
)
631 case WM_LBUTTONDBLCLK
:
633 if (!infoPtr
->pActiveCell
)
636 NotifyParentOfSelection(infoPtr
,
638 infoPtr
->pActiveCell
->ch
);
640 if (infoPtr
->pActiveCell
->bLarge
)
642 DestroyWindow(infoPtr
->hLrgWnd
);
643 infoPtr
->hLrgWnd
= NULL
;
646 infoPtr
->pActiveCell
->bLarge
= FALSE
;
661 infoPtr
->CharMap
= LOWORD(wParam
);
663 infoPtr
->CurrentFont
.lfFaceName
,
665 SetFont(infoPtr
, lfFaceName
);
669 SetFont(infoPtr
, (LPWSTR
)lParam
);
674 if (!infoPtr
->pActiveCell
) return 0;
675 return infoPtr
->pActiveCell
->ch
;
679 return (LRESULT
)infoPtr
->hFont
;
690 DeleteObject(infoPtr
->hFont
);
691 HeapFree(GetProcessHeap(),
694 SetWindowLongPtrW(hwnd
,
702 Ret
= DefWindowProcW(hwnd
,
715 RegisterMapClasses(HINSTANCE hInstance
)
719 wc
.style
= CS_DBLCLKS
;
720 wc
.lpfnWndProc
= MapWndProc
;
721 wc
.cbWndExtra
= sizeof(PMAP
);
722 wc
.hInstance
= hInstance
;
723 wc
.hCursor
= LoadCursorW(NULL
,
725 wc
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
726 wc
.lpszClassName
= szMapWndClass
;
728 if (RegisterClassW(&wc
))
730 wc
.lpfnWndProc
= LrgCellWndProc
;
732 wc
.lpszClassName
= szLrgCellWndClass
;
734 return RegisterClassW(&wc
) != 0;
741 UnregisterMapClasses(HINSTANCE hInstance
)
743 UnregisterClassW(szMapWndClass
,
746 UnregisterClassW(szLrgCellWndClass
,