xref: /reactos/dll/cpl/console/font.c (revision f59c58d8)
1 /*
2  * PROJECT:         ReactOS Console Configuration DLL
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            dll/cpl/console/font.c
5  * PURPOSE:         Font dialog
6  * PROGRAMMERS:     Johannes Anderwald (johannes.anderwald@reactos.org)
7  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8  *                  Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
9  */
10 
11 #include "console.h"
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 
17 /*
18  * Current active font, corresponding to the active console font,
19  * and used for painting the text samples.
20  */
21 FONT_PREVIEW FontPreview = {NULL, 0, 0};
22 
23 
24 /*
25  * Standard font pixel/point heights for TrueType fonts
26  */
27 static const SHORT TrueTypePoints[] =
28 {
29     5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72
30 };
31 
32 typedef struct _FONTSIZE_LIST_CTL
33 {
34     LIST_CTL RasterSizeList;    // ListBox for Raster font sizes; needs to handle bisection.
35     HWND hWndTTSizeList;        // ComboBox for TrueType font sizes.
36     BOOL bIsTTSizeDirty;        // TRUE or FALSE depending on whether we have edited the edit zone.
37     BOOL UseRasterOrTTList;     // TRUE: Use the Raster size list; FALSE: Use the TrueType size list.
38     BOOL TTSizePixelUnit;       // TRUE: Size in pixels (default); FALSE: Size in points.
39     LONG CurrentRasterSize;
40     LONG CurrentTTSize;         // In whatever unit (pixels or points) currently selected.
41 } FONTSIZE_LIST_CTL, *PFONTSIZE_LIST_CTL;
42 
43 /* Used by FontTypeChange() only */
44 static INT   CurrentSelFont  = LB_ERR;
45 static DWORD CurrentFontType = (DWORD)-1;   // Invalid font type
46 
47 /* Detect whether the current code page has changed */
48 static UINT CurrentCodePage = INVALID_CP;
49 
50 
51 VOID
52 RefreshFontPreview(
53     IN FONT_PREVIEW* Preview,
54     IN PCONSOLE_STATE_INFO pConInfo)
55 {
56     HFONT hFont = CreateConsoleFont(pConInfo);
57     if (!hFont)
58     {
59         DPRINT1("RefreshFontPreview: CreateConsoleFont() failed\n");
60         return;
61     }
62 
63     if (Preview->hFont) DeleteObject(Preview->hFont);
64     Preview->hFont = hFont;
65     GetFontCellSize(NULL, hFont, &Preview->CharHeight, &Preview->CharWidth);
66 }
67 
68 VOID
69 UpdateFontPreview(
70     IN FONT_PREVIEW* Preview,
71     IN HFONT hFont,
72     IN UINT  CharWidth,
73     IN UINT  CharHeight)
74 {
75     if (Preview->hFont) DeleteObject(Preview->hFont);
76     Preview->hFont = hFont;
77     Preview->CharWidth  = CharWidth;
78     Preview->CharHeight = CharHeight;
79 }
80 
81 // PLIST_GETCOUNT
82 static INT
83 RasterSizeList_GetCount(
84     IN PLIST_CTL ListCtl)
85 {
86     return (INT)SendMessageW(ListCtl->hWndList, LB_GETCOUNT, 0, 0);
87 }
88 
89 // PLIST_GETDATA
90 static ULONG_PTR
91 RasterSizeList_GetData(
92     IN PLIST_CTL ListCtl,
93     IN INT Index)
94 {
95     return (ULONG_PTR)SendMessageW(ListCtl->hWndList, LB_GETITEMDATA, (WPARAM)Index, 0);
96 }
97 
98 
99 INT
100 LogicalSizeToPointSize(
101     IN HDC hDC OPTIONAL,
102     IN UINT LogicalSize)
103 {
104     INT PointSize;
105     HDC hOrgDC = hDC;
106 
107     if (!hDC)
108         hDC = GetDC(NULL);
109 
110     // LogicalSize = tm.tmHeight - tm.tmInternalLeading;
111     PointSize = MulDiv(LogicalSize, 72, GetDeviceCaps(hDC, LOGPIXELSY));
112 
113     if (!hOrgDC)
114         ReleaseDC(NULL, hDC);
115 
116     return PointSize;
117 }
118 
119 INT
120 PointSizeToLogicalSize(
121     IN HDC hDC OPTIONAL,
122     IN INT PointSize)
123 {
124     INT LogicalSize;
125     HDC hOrgDC = hDC;
126 
127     if (!hDC)
128         hDC = GetDC(NULL);
129 
130     LogicalSize = MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);
131 
132     if (!hOrgDC)
133         ReleaseDC(NULL, hDC);
134 
135     return LogicalSize;
136 }
137 
138 
139 static VOID
140 FontSizeList_SelectFontSize(
141     IN PFONTSIZE_LIST_CTL SizeList,
142     IN ULONG FontSize)
143 {
144     INT nSel;
145     WCHAR szFontSize[100];
146 
147     //
148     // FIXME: Check whether FontSize == 0
149     // (or in the case of raster font maybe, whether HIWORD(FontSize) == Height == 0) ??
150     //
151 
152     /* Find and select the best font size in the list corresponding to the current size */
153     if (SizeList->UseRasterOrTTList)
154     {
155         INT idx;
156 
157         /* Raster font size (in pixels) */
158         SizeList->CurrentRasterSize = FontSize;
159 
160         nSel = BisectListSortedByValue(&SizeList->RasterSizeList, FontSize, NULL, FALSE);
161         idx  = (INT)SendMessageW(SizeList->RasterSizeList.hWndList, LB_GETCOUNT, 0, 0);
162         if (nSel == LB_ERR)
163         {
164             /* Not found, select the first element of the list */
165             nSel = 0;
166         }
167         else if (nSel >= idx)
168         {
169             /*
170              * We got an index beyond the end of the list (as per Bisect* functionality),
171              * so instead, select the last element of the list.
172              */
173             nSel = idx - 1;
174         }
175         SendMessageW(SizeList->RasterSizeList.hWndList, LB_SETCURSEL, (WPARAM)nSel, 0);
176     }
177     else
178     {
179         /* TrueType font size (in pixels or points) */
180         SizeList->CurrentTTSize = FontSize;
181 
182         // _ultow(szFontSize, FontSize, 10);
183         StringCchPrintfW(szFontSize, ARRAYSIZE(szFontSize), L"%d", FontSize);
184 
185         /* Find the font size in the list, or add it both in the ComboBox list, sorted by size value (string), and its edit box */
186         nSel = (INT)SendMessageW(SizeList->hWndTTSizeList, CB_FINDSTRINGEXACT, 0, (LPARAM)szFontSize);
187         if (nSel == CB_ERR)
188         {
189             nSel = (INT)SendMessageW(SizeList->hWndTTSizeList, CB_ADDSTRING, -1, (LPARAM)szFontSize);
190             // ComboBox_SetText(...)
191             SetWindowTextW(SizeList->hWndTTSizeList, szFontSize);
192             SizeList->bIsTTSizeDirty = TRUE;
193         }
194         SendMessageW(SizeList->hWndTTSizeList, CB_SETCURSEL, (WPARAM)nSel, 0);
195     }
196 }
197 
198 static LONG
199 FontSizeList_GetSelectedFontSize(
200     IN PFONTSIZE_LIST_CTL SizeList)
201 {
202     INT nSel;
203     LONG FontSize;
204     PWCHAR pszNext = NULL;
205     WCHAR szFontSize[100];
206 
207     if (SizeList->UseRasterOrTTList)
208     {
209         /* Raster font size (in pixels) */
210 
211         nSel = (INT)SendMessageW(SizeList->RasterSizeList.hWndList, LB_GETCURSEL, 0, 0);
212         if (nSel == LB_ERR) return 0;
213 
214         FontSize = (LONG)SizeList->RasterSizeList.GetData(&SizeList->RasterSizeList, nSel);
215         if (FontSize == LB_ERR) return 0;
216 
217         SizeList->CurrentRasterSize = FontSize;
218     }
219     else
220     {
221         /* TrueType font size (in pixels or points) */
222 
223         if (!SizeList->bIsTTSizeDirty)
224         {
225             /*
226              * The user just selected an existing size, read the ComboBox selection.
227              *
228              * See: https://support.microsoft.com/en-us/help/66365/how-to-process-a-cbn-selchange-notification-message
229              * for more details.
230              */
231             INT Length;
232 
233             nSel = (INT)SendMessageW(SizeList->hWndTTSizeList, CB_GETCURSEL, 0, 0);
234             if (nSel == CB_ERR) return 0;
235 
236             Length = (INT)SendMessageW(SizeList->hWndTTSizeList, CB_GETLBTEXTLEN, nSel, 0);
237             ASSERT((Length != LB_ERR) && (Length < ARRAYSIZE(szFontSize)));
238 
239             Length = (INT)SendMessageW(SizeList->hWndTTSizeList, CB_GETLBTEXT, nSel, (LPARAM)szFontSize);
240             ASSERT((Length != LB_ERR) && (Length < ARRAYSIZE(szFontSize)));
241             szFontSize[Length] = L'\0';
242 
243             /* Validate the font size */
244             FontSize = wcstoul(szFontSize, &pszNext, 10);
245             if ((FontSize == 0) || (*pszNext))
246                 return 0;
247         }
248         else
249         {
250             /* Read the ComboBox edit string, as the user has entered a custom size */
251             // ComboBox_GetText(...)
252             GetWindowTextW(SizeList->hWndTTSizeList, szFontSize, ARRAYSIZE(szFontSize));
253 
254             /* Validate the font size */
255             FontSize = wcstoul(szFontSize, &pszNext, 10);
256             if ((FontSize == 0) || (*pszNext))
257                 return 0;
258 
259             /* Find if the font size already exists in the list; if not, add it */
260             nSel = (INT)SendMessageW(SizeList->hWndTTSizeList, CB_FINDSTRINGEXACT, 0, (LPARAM)szFontSize);
261             if (nSel == CB_ERR)
262             {
263                 nSel = (INT)SendMessageW(SizeList->hWndTTSizeList, CB_ADDSTRING, -1, (LPARAM)szFontSize);
264                 //// ComboBox_SetText(...)
265                 //SetWindowTextW(SizeList->hWndTTSizeList, szFontSize);
266                 //SizeList->bIsTTSizeDirty = TRUE;
267             }
268             SendMessageW(SizeList->hWndTTSizeList, CB_SETCURSEL, (WPARAM)nSel, 0);
269         }
270 
271         SizeList->bIsTTSizeDirty = FALSE;
272 
273         SizeList->CurrentTTSize = FontSize;
274 
275         /*
276          * If the font size is given in points, instead of pixels,
277          * convert it into logical size.
278          */
279         if (!SizeList->TTSizePixelUnit)
280             FontSize = -PointSizeToLogicalSize(NULL, FontSize);
281     }
282 
283     return FontSize;
284 }
285 
286 
287 static VOID
288 AddFontToList(
289     IN HWND hWndList,
290     IN LPCWSTR pszFaceName,
291     IN DWORD FontType)
292 {
293     INT iItem;
294 
295     /* Make sure the font doesn't already exist in the list */
296     if (SendMessageW(hWndList, LB_FINDSTRINGEXACT, 0, (LPARAM)pszFaceName) != LB_ERR)
297         return;
298 
299     /* Add the font */
300     iItem = (INT)SendMessageW(hWndList, LB_ADDSTRING, 0, (LPARAM)pszFaceName);
301     if (iItem == LB_ERR)
302     {
303         DPRINT1("Failed to add font '%S'\n", pszFaceName);
304         return;
305     }
306 
307     DPRINT1("Add font '%S'\n", pszFaceName);
308 
309     /* Store this information in the list-item's userdata area */
310     // SendMessageW(hWndList, LB_SETITEMDATA, idx, MAKELPARAM(fFixed, fTrueType));
311     SendMessageW(hWndList, LB_SETITEMDATA, iItem, (LPARAM)FontType);
312 }
313 
314 typedef struct _FACE_NAMES_PROC_PARAM
315 {
316     HWND hWndList;
317     UINT CodePage;
318 } FACE_NAMES_PROC_PARAM, *PFACE_NAMES_PROC_PARAM;
319 
320 static BOOL CALLBACK
321 EnumFaceNamesProc(
322     IN PLOGFONTW lplf,
323     IN PNEWTEXTMETRICW lpntm,
324     IN DWORD  FontType,
325     IN LPARAM lParam)
326 {
327     PFACE_NAMES_PROC_PARAM Param = (PFACE_NAMES_PROC_PARAM)lParam;
328 
329     if (IsValidConsoleFont2(lplf, lpntm, FontType, Param->CodePage))
330     {
331         /* Add the face name to the list */
332         AddFontToList(Param->hWndList, lplf->lfFaceName, FontType);
333     }
334 
335     /* Continue enumerating the faces */
336     return TRUE;
337 }
338 
339 static BOOL CALLBACK
340 EnumFontSizesProc(
341     IN PLOGFONTW lplf,
342     IN PNEWTEXTMETRICW lpntm,
343     IN DWORD  FontType,
344     IN LPARAM lParam)
345 {
346     PFONTSIZE_LIST_CTL SizeList = (PFONTSIZE_LIST_CTL)lParam;
347     UINT iItem, iDupItem;
348     WCHAR szFontSize[100];
349 
350     if (FontType != TRUETYPE_FONTTYPE)
351     {
352         WPARAM FontSize;
353 
354         /*
355          * Format:
356          * Width  = FontSize.X = LOWORD(FontSize);
357          * Height = FontSize.Y = HIWORD(FontSize);
358          */
359 
360         StringCchPrintfW(szFontSize, ARRAYSIZE(szFontSize), L"%d x %d", lplf->lfWidth, lplf->lfHeight);
361         FontSize = MAKEWPARAM(lplf->lfWidth, lplf->lfHeight);
362 
363         /* Add the font size into the list, sorted by size value. Avoid any duplicates. */
364         /* Store this information in the list-item's userdata area */
365         iDupItem = LB_ERR;
366         iItem = BisectListSortedByValue(&SizeList->RasterSizeList, FontSize, &iDupItem, TRUE);
367         if (iItem == LB_ERR)
368             iItem = 0;
369         if (iDupItem == LB_ERR)
370         {
371             iItem = (UINT)SendMessageW(SizeList->RasterSizeList.hWndList, LB_INSERTSTRING, iItem, (LPARAM)szFontSize);
372             if (iItem != LB_ERR && iItem != LB_ERRSPACE)
373                 iItem = SendMessageW(SizeList->RasterSizeList.hWndList, LB_SETITEMDATA, iItem, FontSize);
374         }
375 
376         return TRUE;
377     }
378     else
379     {
380         /* TrueType or vectored font: list all the hardcoded font points */
381         ULONG i;
382         for (i = 0; i < ARRAYSIZE(TrueTypePoints); ++i)
383         {
384             // _ultow(szFontSize, TrueTypePoints[i], 10);
385             StringCchPrintfW(szFontSize, ARRAYSIZE(szFontSize), L"%d", TrueTypePoints[i]);
386 
387             /* Add the font size into the list, sorted by size value (string). Avoid any duplicates. */
388             if (SendMessageW(SizeList->hWndTTSizeList, CB_FINDSTRINGEXACT, 0, (LPARAM)szFontSize) == CB_ERR)
389                 iItem = (UINT)SendMessageW(SizeList->hWndTTSizeList, CB_INSERTSTRING, -1, (LPARAM)szFontSize);
390         }
391 
392         /* Stop the enumeration now */
393         return FALSE;
394     }
395 }
396 
397 static VOID
398 FaceNameList_Initialize(
399     IN HWND hWndList,
400     IN UINT CodePage)
401 {
402     FACE_NAMES_PROC_PARAM Param;
403     HDC hDC;
404     LOGFONTW lf;
405     INT idx;
406 
407     /* Reset the face names list */
408     SendMessageW(hWndList, LB_RESETCONTENT, 0, 0);
409 
410     Param.hWndList = hWndList;
411     Param.CodePage = CodePage;
412 
413     ZeroMemory(&lf, sizeof(lf));
414     lf.lfCharSet = DEFAULT_CHARSET; // CodePageToCharSet(CodePage);
415     // lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
416 
417     hDC = GetDC(NULL);
418     EnumFontFamiliesExW(hDC, &lf, (FONTENUMPROCW)EnumFaceNamesProc, (LPARAM)&Param, 0);
419     ReleaseDC(NULL, hDC);
420 
421     idx = (INT)SendMessageW(hWndList, LB_GETCOUNT, 0, 0);
422     if (idx != LB_ERR && idx != 0)
423     {
424         /* We have found some faces and filled the list, we are fine! */
425         return;
426     }
427 
428     /* No fonts were found. Manually add default ones into the list. */
429     DPRINT1("The ideal console fonts were not found; manually add default ones.\n");
430 
431     AddFontToList(hWndList, L"Terminal", RASTER_FONTTYPE);
432 #if 0
433     // TODO: insert only the *single* default TT font, that should
434     // be found in the TT font cache with the codepage number 0.
435     AddFontToList(hWndList, L"Lucida Console", TRUETYPE_FONTTYPE);
436 #endif
437 }
438 
439 static VOID
440 FaceNameList_SelectFont(
441     IN HWND hDlg,
442     IN HWND hWndList,
443     IN PFONTSIZE_LIST_CTL SizeList,
444     IN LPCWSTR FaceName,
445     IN ULONG FontFamily,
446     IN ULONG FontWeight,
447     IN COORD FontSize)
448 {
449     INT iItem;
450 
451     iItem = (INT)SendMessageW(hWndList, LB_FINDSTRINGEXACT, 0, (LPARAM)FaceName);
452     if (iItem == LB_ERR)
453         iItem = (INT)SendMessageW(hWndList, LB_FINDSTRINGEXACT, 0, (LPARAM)L"Terminal");
454     if (iItem == LB_ERR)
455         iItem = 0;
456     SendMessageW(hWndList, LB_SETCURSEL, (WPARAM)iItem, 0);
457 
458     if (FontWeight >= FW_BOLD)
459         CheckDlgButton(hDlg, IDC_CHECK_BOLD_FONTS, BST_CHECKED);
460     else
461         CheckDlgButton(hDlg, IDC_CHECK_BOLD_FONTS, BST_UNCHECKED);
462 
463     /* Select the current font size */
464     /*
465      * Format:
466      * Width  = FontSize.X = LOWORD(FontSize);
467      * Height = FontSize.Y = HIWORD(FontSize);
468      */
469     SizeList->CurrentRasterSize = MAKELONG(FontSize.X, FontSize.Y);
470     SizeList->CurrentTTSize = FontSize.Y;
471     // FontSizeList_SelectFontSize(SizeList, SizeList->CurrentRasterSize);
472 
473     // return iItem;
474 }
475 
476 static VOID
477 UpdateFontSizeList(
478     IN HWND hDlg,
479     IN PFONTSIZE_LIST_CTL SizeList)
480 {
481     HWND hDlgItem;
482 
483     if (SizeList->UseRasterOrTTList)
484     {
485         /*
486          * Raster font: show the Raster size list, and
487          * hide the TrueType size list and the units.
488          */
489 
490         // EnableDlgItem(hDlg, IDC_CHECK_BOLD_FONTS, FALSE);
491 
492         hDlgItem = GetDlgItem(hDlg, IDC_RADIO_PIXEL_UNIT);
493         ShowWindow(hDlgItem, SW_HIDE);
494         EnableWindow(hDlgItem, FALSE);
495 
496         hDlgItem = GetDlgItem(hDlg, IDC_RADIO_POINT_UNIT);
497         ShowWindow(hDlgItem, SW_HIDE);
498         EnableWindow(hDlgItem, FALSE);
499 
500         hDlgItem = SizeList->hWndTTSizeList;
501         ShowWindow(hDlgItem, SW_HIDE);
502         EnableWindow(hDlgItem, FALSE);
503 
504         hDlgItem = SizeList->RasterSizeList.hWndList;
505         EnableWindow(hDlgItem, TRUE);
506         ShowWindow(hDlgItem, SW_SHOW);
507     }
508     else
509     {
510         /*
511          * TrueType font: show the TrueType size list
512          * and the units, and hide the Raster size list.
513          */
514 
515         // EnableDlgItem(hDlg, IDC_CHECK_BOLD_FONTS, TRUE);
516 
517         hDlgItem = SizeList->RasterSizeList.hWndList;
518         ShowWindow(hDlgItem, SW_HIDE);
519         EnableWindow(hDlgItem, FALSE);
520 
521         hDlgItem = SizeList->hWndTTSizeList;
522         EnableWindow(hDlgItem, TRUE);
523         ShowWindow(hDlgItem, SW_SHOW);
524 
525         hDlgItem = GetDlgItem(hDlg, IDC_RADIO_PIXEL_UNIT);
526         EnableWindow(hDlgItem, TRUE);
527         ShowWindow(hDlgItem, SW_SHOW);
528 
529         hDlgItem = GetDlgItem(hDlg, IDC_RADIO_POINT_UNIT);
530         EnableWindow(hDlgItem, TRUE);
531         ShowWindow(hDlgItem, SW_SHOW);
532     }
533 }
534 
535 static BOOL
536 FontSizeChange(
537     IN HWND hDlg,
538     IN PFONTSIZE_LIST_CTL SizeList,
539     IN OUT PCONSOLE_STATE_INFO pConInfo);
540 
541 static BOOL
542 FontTypeChange(
543     IN HWND hDlg,
544     IN PFONTSIZE_LIST_CTL SizeList,
545     IN OUT PCONSOLE_STATE_INFO pConInfo)
546 {
547     HWND hFontList = GetDlgItem(hDlg, IDC_LBOX_FONTTYPE);
548     INT Length, nSel;
549     LPWSTR FaceName;
550     DWORD FontType;
551     LPCWSTR FontGrpBoxLabelTpl = NULL;
552     WCHAR FontGrpBoxLabel[260];
553 
554     nSel = (INT)SendMessageW(hFontList, LB_GETCURSEL, 0, 0);
555     if (nSel == LB_ERR) return FALSE;
556 
557     /*
558      * This is disabled, because there can be external parameters
559      * that may have changed (e.g. ConInfo->FontWeight, code page, ...)
560      * and that we don't control here, and that need a font refresh.
561      */
562 #if 0
563     /* Check whether the selection has changed */
564     if (nSel == CurrentSelFont)
565         return FALSE;
566 #endif
567 
568     Length = (INT)SendMessageW(hFontList, LB_GETTEXTLEN, nSel, 0);
569     if (Length == LB_ERR) return FALSE;
570 
571     FaceName = HeapAlloc(GetProcessHeap(),
572                          HEAP_ZERO_MEMORY,
573                          (Length + 1) * sizeof(WCHAR));
574     if (FaceName == NULL) return FALSE;
575 
576     Length = (INT)SendMessageW(hFontList, LB_GETTEXT, nSel, (LPARAM)FaceName);
577     ASSERT(Length != LB_ERR);
578     FaceName[Length] = L'\0';
579 
580     StringCchCopyW(pConInfo->FaceName, ARRAYSIZE(pConInfo->FaceName), FaceName);
581     DPRINT("pConInfo->FaceName = '%S'\n", pConInfo->FaceName);
582 
583     /*
584      * Retrieve the read-only font group box label string template,
585      * and set the group box label to the name of the selected font.
586      */
587     Length = LoadStringW(hApplet, IDS_GROUPBOX_FONT_NAME, (LPWSTR)&FontGrpBoxLabelTpl, 0);
588     if (FontGrpBoxLabelTpl && Length > 0)
589     {
590         StringCchCopyNW(FontGrpBoxLabel, ARRAYSIZE(FontGrpBoxLabel), FontGrpBoxLabelTpl, Length);
591         StringCchCatW(FontGrpBoxLabel, ARRAYSIZE(FontGrpBoxLabel), FaceName);
592         SetDlgItemTextW(hDlg, IDC_GROUPBOX_FONT_NAME, FontGrpBoxLabel);
593     }
594 
595     HeapFree(GetProcessHeap(), 0, FaceName);
596 
597     /*
598      * Reset the font size list, only:
599      * - if we have changed the type of font, or
600      * - if the font type is the same and is RASTER but the font has changed.
601      * Otherwise, if the font type is not RASTER and has not changed,
602      * we always display the TrueType default sizes and we don't need to
603      * recreate the list when we change between different TrueType fonts.
604      */
605     FontType = (DWORD)SendMessageW(hFontList, LB_GETITEMDATA, nSel, 0);
606     if (FontType != LB_ERR)
607     {
608         SizeList->UseRasterOrTTList = (FontType == RASTER_FONTTYPE);
609 
610         /* Display the correct font size list (if needed) */
611         if (CurrentFontType != FontType)
612             UpdateFontSizeList(hDlg, SizeList);
613 
614         /* Enumerate the available sizes for the selected font */
615         if ((CurrentFontType != FontType) ||
616             (FontType == RASTER_FONTTYPE && CurrentSelFont != nSel))
617         {
618             LOGFONTW lf;
619             HDC hDC;
620 
621             if (SizeList->UseRasterOrTTList)
622                 SendMessageW(SizeList->RasterSizeList.hWndList, LB_RESETCONTENT, 0, 0);
623             else
624                 SendMessageW(SizeList->hWndTTSizeList, CB_RESETCONTENT, 0, 0);
625 
626             ZeroMemory(&lf, sizeof(lf));
627             lf.lfCharSet = DEFAULT_CHARSET; // CodePageToCharSet(pConInfo->CodePage);
628             // lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
629             StringCchCopyW(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), pConInfo->FaceName);
630 
631             hDC = GetDC(NULL);
632             EnumFontFamiliesExW(hDC, &lf, (FONTENUMPROCW)EnumFontSizesProc, (LPARAM)SizeList, 0);
633             ReleaseDC(NULL, hDC);
634 
635             /* Re-select the current font size */
636             if (SizeList->UseRasterOrTTList)
637                 FontSizeList_SelectFontSize(SizeList, SizeList->CurrentRasterSize);
638             else
639                 FontSizeList_SelectFontSize(SizeList, SizeList->CurrentTTSize);
640         }
641     }
642     else
643     {
644         /* We failed, display the raster fonts size list */
645         SizeList->UseRasterOrTTList = TRUE;
646         UpdateFontSizeList(hDlg, SizeList);
647     }
648     CurrentFontType = FontType;
649     CurrentSelFont  = nSel;
650 
651     FontSizeChange(hDlg, SizeList, pConInfo);
652     return TRUE;
653 }
654 
655 static BOOL
656 FontSizeChange(
657     IN HWND hDlg,
658     IN PFONTSIZE_LIST_CTL SizeList,
659     IN OUT PCONSOLE_STATE_INFO pConInfo)
660 {
661     LONG FontSize;
662     UINT CharWidth, CharHeight;
663     HFONT hFont;
664     WCHAR szFontSize[100];
665 
666     /*
667      * Retrieve the current selected font size.
668      * - If SizeList->UseRasterOrTTList is TRUE, or if it is FALSE but
669      *   if SizeList->TTSizePixelUnit is TRUE, then the font size is in pixels;
670      * - If SizeList->TTSizePixelUnit is FALSE, then the font size is in points.
671      */
672     FontSize = FontSizeList_GetSelectedFontSize(SizeList);
673     if (FontSize == 0)
674         return FALSE; // We have got an invalid font size...
675 
676     /*
677      * For TrueType fonts we set the requested width to zero
678      * so as to obtain a default aspect-ratio width.
679      */
680     CharHeight = (UINT)(SizeList->UseRasterOrTTList ? HIWORD(FontSize) : FontSize);
681     CharWidth  = (UINT)(SizeList->UseRasterOrTTList ? LOWORD(FontSize) : 0);
682 
683     hFont = CreateConsoleFont2((LONG)CharHeight, (LONG)CharWidth, pConInfo);
684     if (!hFont)
685     {
686         DPRINT1("FontSizeChange: CreateConsoleFont2() failed\n");
687         return FALSE;
688     }
689 
690     /* Retrieve the real character size in pixels */
691     GetFontCellSize(NULL, hFont, &CharHeight, &CharWidth);
692 
693     /*
694      * Update the font preview as well, and store the font handle. It will be
695      * freed at later update or when the font preview is refreshed or reset.
696      * For TrueType fonts, the preview will show the actual character width.
697      */
698     UpdateFontPreview(&FontPreview, hFont, CharWidth, CharHeight);
699 
700     /*
701      * Format:
702      * Width  = FontSize.X = LOWORD(FontSize);
703      * Height = FontSize.Y = HIWORD(FontSize);
704      */
705     pConInfo->FontSize.X = (SHORT)(SizeList->UseRasterOrTTList ? CharWidth : 0);
706     pConInfo->FontSize.Y = (SHORT)CharHeight;
707 
708     DPRINT("pConInfo->FontSize = (%d x %d) ; (CharWidth x CharHeight) = (%d x %d)\n",
709            pConInfo->FontSize.X, pConInfo->FontSize.Y, CharWidth, CharHeight);
710 
711     InvalidateRect(GetDlgItem(hDlg, IDC_STATIC_FONT_WINDOW_PREVIEW), NULL, TRUE);
712     InvalidateRect(GetDlgItem(hDlg, IDC_STATIC_SELECT_FONT_PREVIEW), NULL, TRUE);
713 
714     StringCchPrintfW(szFontSize, ARRAYSIZE(szFontSize), L"%d", CharWidth);
715     SetDlgItemText(hDlg, IDC_FONT_SIZE_X, szFontSize);
716     StringCchPrintfW(szFontSize, ARRAYSIZE(szFontSize), L"%d", CharHeight);
717     SetDlgItemText(hDlg, IDC_FONT_SIZE_Y, szFontSize);
718 
719     return TRUE;
720 }
721 
722 
723 INT_PTR
724 CALLBACK
725 FontProc(HWND hDlg,
726          UINT uMsg,
727          WPARAM wParam,
728          LPARAM lParam)
729 {
730     PFONTSIZE_LIST_CTL SizeList;
731 
732     SizeList = (PFONTSIZE_LIST_CTL)GetWindowLongPtrW(hDlg, DWLP_USER);
733 
734     switch (uMsg)
735     {
736         case WM_INITDIALOG:
737         {
738             SizeList = (PFONTSIZE_LIST_CTL)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*SizeList));
739             if (!SizeList)
740             {
741                 EndDialog(hDlg, 0);
742                 return (INT_PTR)TRUE;
743             }
744             SizeList->RasterSizeList.hWndList = GetDlgItem(hDlg, IDC_LBOX_FONTSIZE);
745             SizeList->RasterSizeList.GetCount = RasterSizeList_GetCount;
746             SizeList->RasterSizeList.GetData  = RasterSizeList_GetData;
747             SizeList->hWndTTSizeList = GetDlgItem(hDlg, IDC_CBOX_FONTSIZE);
748             SizeList->bIsTTSizeDirty = FALSE;
749             SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)SizeList);
750 
751             /* By default show the raster font size list */
752             SizeList->UseRasterOrTTList = TRUE;
753 
754             /* By default show the font sizes in pixel units */
755             CheckRadioButton(hDlg, IDC_RADIO_PIXEL_UNIT, IDC_RADIO_POINT_UNIT, IDC_RADIO_PIXEL_UNIT);
756             SizeList->TTSizePixelUnit = TRUE;
757 
758             UpdateFontSizeList(hDlg, SizeList);
759 
760             DPRINT1("ConInfo->FaceName = '%S'\n", ConInfo->FaceName);
761 
762             /* Face names list and current font selection will be done during PSN_SETACTIVE notification */
763             // CurrentCodePage = INVALID_CP;
764 
765             return TRUE;
766         }
767 
768         case WM_DESTROY:
769         {
770             if (SizeList)
771                 HeapFree(GetProcessHeap(), 0, SizeList);
772             return (INT_PTR)TRUE;
773         }
774 
775         case WM_DRAWITEM:
776         {
777             LPDRAWITEMSTRUCT drawItem = (LPDRAWITEMSTRUCT)lParam;
778 
779             if (drawItem->CtlID == IDC_STATIC_SELECT_FONT_PREVIEW)
780                 PaintText(drawItem, ConInfo, Screen);
781 
782             return TRUE;
783         }
784 
785         case WM_DISPLAYCHANGE:
786         {
787             /* Retransmit to the preview window */
788             SendDlgItemMessageW(hDlg, IDC_STATIC_FONT_WINDOW_PREVIEW,
789                                 WM_DISPLAYCHANGE, wParam, lParam);
790             break;
791         }
792 
793         case WM_FONTCHANGE:
794         {
795             /* The pool of font resources has changed, re-enumerate the fonts */
796             HWND hFontList = GetDlgItem(hDlg, IDC_LBOX_FONTTYPE);
797 
798             /* Initialize the font list */
799             FaceNameList_Initialize(hFontList, ConInfo->CodePage);
800 
801             /* Select the current font */
802             FaceNameList_SelectFont(hDlg, hFontList,
803                                     SizeList,
804                                     ConInfo->FaceName,
805                                     ConInfo->FontFamily,
806                                     ConInfo->FontWeight,
807                                     ConInfo->FontSize);
808 
809             /* Refresh everything */
810             FontTypeChange(hDlg, SizeList, ConInfo);
811             break;
812         }
813 
814         case WM_NOTIFY:
815         {
816             switch (((LPNMHDR)lParam)->code)
817             {
818                 case PSN_APPLY:
819                 {
820                     ApplyConsoleInfo(hDlg);
821                     return TRUE;
822                 }
823 
824                 case PSN_SETACTIVE:
825                 {
826                     /* Check whether the current code page has changed.
827                      * If so, re-enumerate the fonts. */
828                     if (CurrentCodePage != ConInfo->CodePage)
829                     {
830                         HWND hFontList;
831 
832                         /* Save the new code page */
833                         CurrentCodePage = ConInfo->CodePage;
834 
835                         hFontList = GetDlgItem(hDlg, IDC_LBOX_FONTTYPE);
836 
837                         /* Initialize the font list */
838                         FaceNameList_Initialize(hFontList, ConInfo->CodePage);
839 
840                         /* Select the current font */
841                         FaceNameList_SelectFont(hDlg, hFontList,
842                                                 SizeList,
843                                                 ConInfo->FaceName,
844                                                 ConInfo->FontFamily,
845                                                 ConInfo->FontWeight,
846                                                 ConInfo->FontSize);
847 
848                         /* Refresh everything */
849                         FontTypeChange(hDlg, SizeList, ConInfo);
850                     }
851 
852                     /* Fall back to default behaviour */
853                     break;
854                 }
855             }
856 
857             break;
858         }
859 
860         case WM_COMMAND:
861         {
862             if (HIWORD(wParam) == LBN_SELCHANGE /* || CBN_SELCHANGE */)
863             {
864                 switch (LOWORD(wParam))
865                 {
866                     case IDC_LBOX_FONTTYPE:
867                     {
868                         /* Change the property sheet state only if the font has really changed */
869                         if (FontTypeChange(hDlg, SizeList, ConInfo))
870                             PropSheet_Changed(GetParent(hDlg), hDlg);
871                         break;
872                     }
873 
874                     case IDC_LBOX_FONTSIZE:
875                     case IDC_CBOX_FONTSIZE:
876                     {
877                         /* Change the property sheet state only if the font has really changed */
878                         if (FontSizeChange(hDlg, SizeList, ConInfo))
879                             PropSheet_Changed(GetParent(hDlg), hDlg);
880                         break;
881                     }
882                 }
883             }
884             /* NOTE: CBN_EDITUPDATE is sent first, and is followed by CBN_EDITCHANGE */
885             else if (HIWORD(wParam) == CBN_EDITUPDATE && LOWORD(wParam) == IDC_CBOX_FONTSIZE)
886             {
887                 ULONG FontSize;
888                 PWCHAR pszNext = NULL;
889                 WCHAR szFontSize[100];
890                 WCHAR szMessage[260];
891 
892                 /* Read the ComboBox edit string, as the user has entered a custom size */
893                 GetWindowTextW(SizeList->hWndTTSizeList, szFontSize, ARRAYSIZE(szFontSize));
894 
895                 /* Validate the font size */
896                 FontSize = wcstoul(szFontSize, &pszNext, 10);
897                 if ((FontSize == 0) || (*pszNext))
898                 {
899                     // FIXME: Localize!
900                     StringCchPrintfW(szMessage, ARRAYSIZE(szMessage), L"\"%s\" is not a valid font size.", szFontSize);
901                     MessageBoxW(hDlg, szMessage, L"Error", MB_ICONINFORMATION | MB_OK);
902                 }
903                 /**/SizeList->bIsTTSizeDirty = TRUE;/**/
904             }
905             else if (HIWORD(wParam) == CBN_KILLFOCUS && LOWORD(wParam) == IDC_CBOX_FONTSIZE)
906             {
907                 /* Change the property sheet state only if the font has really changed */
908                 if (FontSizeChange(hDlg, SizeList, ConInfo))
909                     PropSheet_Changed(GetParent(hDlg), hDlg);
910             }
911             else
912             if (HIWORD(wParam) == BN_CLICKED)
913             {
914                 switch (LOWORD(wParam))
915                 {
916                 case IDC_CHECK_BOLD_FONTS:
917                 {
918                     if (IsDlgButtonChecked(hDlg, IDC_CHECK_BOLD_FONTS) == BST_CHECKED)
919                         ConInfo->FontWeight = FW_BOLD;
920                     else
921                         ConInfo->FontWeight = FW_NORMAL;
922 
923                     FontTypeChange(hDlg, SizeList, ConInfo);
924                     PropSheet_Changed(GetParent(hDlg), hDlg);
925                     break;
926                 }
927 
928                 case IDC_RADIO_PIXEL_UNIT:
929                 case IDC_RADIO_POINT_UNIT:
930                 {
931                     SizeList->TTSizePixelUnit = (LOWORD(wParam) == IDC_RADIO_PIXEL_UNIT);
932 
933                     /* The call is valid only for TrueType fonts */
934                     if (CurrentFontType != TRUETYPE_FONTTYPE)
935                         break;
936 
937                     /* Change the property sheet state only if the font has really changed */
938                     if (FontSizeChange(hDlg, SizeList, ConInfo))
939                         PropSheet_Changed(GetParent(hDlg), hDlg);
940                     break;
941                 }
942                 }
943             }
944 
945             break;
946         }
947 
948         default:
949             break;
950     }
951 
952     return FALSE;
953 }
954