e84896ff4f4f8290c202689e1c60c9b632499fe1
[reactos.git] / reactos / dll / cpl / console / font.c
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 // Some temporary code for future reference...
19 //
20 #if 0
21 /*
22 * This code comes from PuTTY
23 */
24 {
25 CHOOSEFONT cf;
26 LOGFONT lf;
27 HDC hdc;
28 FontSpec *fs = (FontSpec *)c->data;
29
30 hdc = GetDC(0);
31 lf.lfHeight = -MulDiv(fs->height,
32 GetDeviceCaps(hdc, LOGPIXELSY), 72);
33 ReleaseDC(0, hdc);
34 lf.lfWidth = lf.lfEscapement = lf.lfOrientation = 0;
35 lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = 0;
36 lf.lfWeight = (fs->isbold ? FW_BOLD : 0);
37 lf.lfCharSet = fs->charset;
38 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
39 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
40 lf.lfQuality = DEFAULT_QUALITY;
41 lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
42 strncpy(lf.lfFaceName, fs->name,
43 sizeof(lf.lfFaceName) - 1);
44 lf.lfFaceName[sizeof(lf.lfFaceName) - 1] = '\0';
45
46 cf.lStructSize = sizeof(cf);
47 cf.hwndOwner = dp->hwnd;
48 cf.lpLogFont = &lf;
49 cf.Flags = (dp->fixed_pitch_fonts ? CF_FIXEDPITCHONLY : 0) |
50 CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;
51
52 if (ChooseFont(&cf)) {
53 fs = fontspec_new(lf.lfFaceName, (lf.lfWeight == FW_BOLD),
54 cf.iPointSize / 10, lf.lfCharSet);
55 dlg_fontsel_set(ctrl, dp, fs);
56 fontspec_free(fs);
57
58 ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE);
59 }
60 }
61
62 /*
63 * This code is from consrv.
64 */
65 {
66 if (!GetTextMetricsW(drawItem->hDC, &Metrics))
67 {
68 DPRINT1("PaintText: GetTextMetrics failed\n");
69 SelectObject(drawItem->hDC, OldFont);
70 DeleteObject(Font);
71 return;
72 }
73 GuiData->CharWidth = Metrics.tmMaxCharWidth;
74 GuiData->CharHeight = Metrics.tmHeight + Metrics.tmExternalLeading;
75
76 /* Measure real char width more precisely if possible */
77 if (GetTextExtentPoint32W(drawItem->hDC, L"R", 1, &CharSize))
78 GuiData->CharWidth = CharSize.cx;
79 }
80
81 /*
82 * See also: Display_SetTypeFace in applications/fontview/display.c
83 */
84 #endif
85
86
87 /*
88 * Font pixel heights for TrueType fonts
89 */
90 static SHORT TrueTypePoints[] =
91 {
92 // 8, 9, 10, 11, 12, 14, 16, 18, 20,
93 // 22, 24, 26, 28, 36, 48, 72
94 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, 24, 28, 36, 72
95 };
96
97 #define CP_SHIFTJIS 932 // Japanese Shift-JIS
98 #define CP_HANGUL 949 // Korean Hangul
99 #define CP_GB2312 936 // Chinese Simplified (GB2312)
100 #define CP_BIG5 950 // Chinese Traditional (Big5)
101
102 /* IsFarEastCP(CodePage) */
103 #define IsCJKCodePage(CodePage) \
104 ((CodePage) == CP_SHIFTJIS || (CodePage) == CP_HANGUL || \
105 (CodePage) == CP_BIG5 || (CodePage) == CP_GB2312)
106
107 /* Retrieves the character set associated with a given code page */
108 BYTE CodePageToCharSet(UINT CodePage)
109 {
110 CHARSETINFO CharInfo;
111 if (TranslateCharsetInfo((LPDWORD)CodePage, &CharInfo, TCI_SRCCODEPAGE))
112 return CharInfo.ciCharset;
113 else
114 return DEFAULT_CHARSET;
115 }
116
117
118 static VOID
119 AddFontToList(
120 IN HWND hWndList,
121 IN LPCWSTR pszFontName,
122 IN DWORD FontType)
123 {
124 INT idx;
125
126 /* Make sure the font doesn't already exist in the list */
127 if (SendMessageW(hWndList, LB_FINDSTRINGEXACT, 0, (LPARAM)pszFontName) != LB_ERR)
128 return;
129
130 /* Add the font */
131 idx = (INT)SendMessageW(hWndList, LB_ADDSTRING, 0, (LPARAM)pszFontName);
132 if (idx == LB_ERR)
133 {
134 DPRINT1("Failed to add font '%S'\n", pszFontName);
135 return;
136 }
137
138 DPRINT1("Add font '%S'\n", pszFontName);
139
140 /* Store this information in the list-item's userdata area */
141 // SendMessageW(hWndList, LB_SETITEMDATA, idx, MAKEWPARAM(fFixed, fTrueType));
142 SendMessageW(hWndList, LB_SETITEMDATA, idx, (WPARAM)FontType);
143 }
144
145 static BOOL CALLBACK
146 EnumFontNamesProc(PLOGFONTW lplf,
147 PNEWTEXTMETRICW lpntm,
148 DWORD FontType,
149 LPARAM lParam)
150 {
151 HWND hwndCombo = (HWND)lParam;
152 LPWSTR pszName = lplf->lfFaceName;
153
154 /* Record the font's attributes (Fixedwidth and Truetype) */
155 // BOOL fFixed = ((lplf->lfPitchAndFamily & 0x03) == FIXED_PITCH);
156 // BOOL fTrueType = (lplf->lfOutPrecision == OUT_STROKE_PRECIS);
157
158 /*
159 * According to: http://support.microsoft.com/kb/247815
160 * the criteria for console-eligible fonts are:
161 * - The font must be a fixed-pitch font.
162 * - The font cannot be an italic font.
163 * - The font cannot have a negative A or C space.
164 * - If it is a TrueType font, it must be FF_MODERN.
165 * - If it is not a TrueType font, it must be OEM_CHARSET.
166 *
167 * Non documented: vertical fonts are forbidden (their name start with a '@').
168 *
169 * Additional criteria for Asian installations:
170 * - If it is not a TrueType font, the face name must be "Terminal".
171 * - If it is an Asian TrueType font, it must also be an Asian character set.
172 *
173 * To install additional TrueType fonts to be available for the console,
174 * add entries of type REG_SZ named "0", "00" etc... in:
175 * HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Console\TrueTypeFont
176 * The names of the fonts listed there should match those in:
177 * HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Fonts
178 */
179
180 /*
181 * In ReactOS we relax some of the criteria:
182 * - We allow fixed-pitch FF_MODERN (Monospace) TrueType fonts
183 * that can be italic or have negative A or C space.
184 * - If it is not a TrueType font, it can be from another character set
185 * than OEM_CHARSET.
186 * - We do not look into the magic registry key mentioned above.
187 */
188
189 /* Reject variable width fonts */
190 if (((lplf->lfPitchAndFamily & 0x03) != FIXED_PITCH)
191 #if 0 /* Reject italic and TrueType fonts with negative A or C space */
192 || (lplf->lfItalic)
193 || !(lpntm->ntmFlags & NTM_NONNEGATIVE_AC)
194 #endif
195 )
196 {
197 DPRINT1("Font '%S' rejected because it%s (lfPitchAndFamily = %d).\n",
198 pszName, !(lplf->lfPitchAndFamily & FIXED_PITCH) ? "'s not FIXED_PITCH"
199 : (!(lpntm->ntmFlags & NTM_NONNEGATIVE_AC) ? " has negative A or C space"
200 : " is broken"),
201 lplf->lfPitchAndFamily);
202 return TRUE;
203 }
204
205 /* Reject TrueType fonts that are not FF_MODERN */
206 if ((FontType == TRUETYPE_FONTTYPE) && ((lplf->lfPitchAndFamily & 0xF0) != FF_MODERN))
207 {
208 DPRINT1("TrueType font '%S' rejected because it's not FF_MODERN (lfPitchAndFamily = %d)\n",
209 pszName, lplf->lfPitchAndFamily);
210 return TRUE;
211 }
212
213 /* Is the current code page Chinese, Japanese or Korean? */
214 if (IsCJKCodePage(ConInfo->CodePage))
215 {
216 /* It's Asian */
217 if (FontType == TRUETYPE_FONTTYPE)
218 {
219 if (lplf->lfCharSet != CodePageToCharSet(ConInfo->CodePage))
220 {
221 DPRINT1("TrueType font '%S' rejected because it's not user Asian charset (lfCharSet = %d)\n",
222 pszName, lplf->lfCharSet);
223 return TRUE;
224 }
225 }
226 else
227 {
228 /* Reject non-TrueType fonts that are not Terminal */
229 if (wcscmp(pszName, L"Terminal") != 0)
230 {
231 DPRINT1("Non-TrueType font '%S' rejected because it's not Terminal\n", pszName);
232 return TRUE;
233 }
234 }
235 }
236 else
237 {
238 /* Not CJK */
239 if ((FontType != TRUETYPE_FONTTYPE) &&
240 (lplf->lfCharSet != ANSI_CHARSET) &&
241 (lplf->lfCharSet != DEFAULT_CHARSET) &&
242 (lplf->lfCharSet != OEM_CHARSET))
243 {
244 DPRINT1("Non-TrueType font '%S' rejected because it's not ANSI_CHARSET or DEFAULT_CHARSET or OEM_CHARSET (lfCharSet = %d)\n",
245 pszName, lplf->lfCharSet);
246 return TRUE;
247 }
248 }
249
250 /* Reject fonts that are vertical (tategaki) */
251 if (pszName[0] == L'@')
252 {
253 DPRINT1("Font '%S' rejected because it's vertical\n", pszName);
254 return TRUE;
255 }
256
257 /* Add the font to the list */
258 AddFontToList(hwndCombo, pszName, /* MAKEWPARAM(fFixed, fTrueType) */ FontType);
259
260 return TRUE;
261 }
262
263 static BOOL CALLBACK
264 EnumFontSizesProc(PLOGFONTW lplf,
265 PNEWTEXTMETRICW lpntm,
266 DWORD FontType,
267 LPARAM lParam)
268 {
269 HWND hwndCombo = (HWND)lParam;
270 WCHAR FontSize[100];
271
272 if (FontType != TRUETYPE_FONTTYPE)
273 {
274 // int logsize = lpntm->tmHeight - lpntm->tmInternalLeading;
275 // LONG pointsize = MulDiv(logsize, 72, GetDeviceCaps(hdc, LOGPIXELSY));
276
277 // swprintf(FontSize, L"%2d (%d x %d)", pointsize, lplf->lfWidth, lplf->lfHeight);
278 swprintf(FontSize, L"%d x %d", lplf->lfWidth, lplf->lfHeight);
279
280 /* Make sure the size doesn't already exist in the list */
281 if (SendMessageW(hwndCombo, LB_FINDSTRINGEXACT, 0, (LPARAM)FontSize) == LB_ERR)
282 {
283 /* Add the size */
284 INT idx = (INT)SendMessageW(hwndCombo, LB_ADDSTRING, 0, (LPARAM)FontSize);
285
286 /*
287 * Store this information in the list-item's userdata area.
288 * Format:
289 * Width = FontSize.X = LOWORD(FontSize);
290 * Height = FontSize.Y = HIWORD(FontSize);
291 */
292 SendMessageW(hwndCombo, LB_SETITEMDATA, idx, MAKEWPARAM(lplf->lfWidth, lplf->lfHeight));
293 }
294
295 return TRUE;
296 }
297 else
298 {
299 ULONG i;
300 for (i = 0; i < ARRAYSIZE(TrueTypePoints); ++i)
301 {
302 swprintf(FontSize, L"%2d", TrueTypePoints[i]);
303
304 /* Make sure the size doesn't already exist in the list */
305 if (SendMessageW(hwndCombo, LB_FINDSTRINGEXACT, 0, (LPARAM)FontSize) == LB_ERR)
306 {
307 /* Add the size */
308 INT idx = (INT)SendMessageW(hwndCombo, LB_ADDSTRING, 0, (LPARAM)FontSize);
309
310 /*
311 * Store this information in the list-item's userdata area.
312 * Format:
313 * Width = FontSize.X = LOWORD(FontSize);
314 * Height = FontSize.Y = HIWORD(FontSize);
315 */
316 SendMessageW(hwndCombo, LB_SETITEMDATA, idx, MAKEWPARAM(0, TrueTypePoints[i]));
317 }
318 }
319
320 return FALSE;
321 }
322 }
323
324
325 static VOID
326 FontSizeChange(HWND hwndDlg,
327 PCONSOLE_STATE_INFO pConInfo);
328
329 static VOID
330 FontTypeChange(HWND hwndDlg,
331 PCONSOLE_STATE_INFO pConInfo)
332 {
333 HWND hListBox = GetDlgItem(hwndDlg, IDC_LBOX_FONTTYPE);
334 INT Length, nSel;
335 LPWSTR FaceName;
336 HDC hDC;
337 LOGFONTW lf;
338
339 nSel = (INT)SendMessageW(hListBox, LB_GETCURSEL, 0, 0);
340 if (nSel == LB_ERR) return;
341
342 Length = (INT)SendMessageW(hListBox, LB_GETTEXTLEN, nSel, 0);
343 if (Length == LB_ERR) return;
344
345 FaceName = HeapAlloc(GetProcessHeap(),
346 HEAP_ZERO_MEMORY,
347 (Length + 1) * sizeof(WCHAR));
348 if (FaceName == NULL) return;
349
350 Length = (INT)SendMessageW(hListBox, LB_GETTEXT, nSel, (LPARAM)FaceName);
351 FaceName[Length] = L'\0';
352
353 StringCchCopyW(pConInfo->FaceName, ARRAYSIZE(pConInfo->FaceName), FaceName);
354 DPRINT1("pConInfo->FaceName = '%S'\n", pConInfo->FaceName);
355
356 /* Enumerate the available sizes for the selected font */
357 ZeroMemory(&lf, sizeof(lf));
358 lf.lfCharSet = DEFAULT_CHARSET; // CodePageToCharSet(pConInfo->CodePage);
359 // lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
360 StringCchCopyW(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), FaceName);
361
362 hDC = GetDC(NULL);
363 EnumFontFamiliesExW(hDC, &lf, (FONTENUMPROCW)EnumFontSizesProc,
364 (LPARAM)GetDlgItem(hwndDlg, IDC_LBOX_FONTSIZE), 0);
365 ReleaseDC(NULL, hDC);
366
367 HeapFree(GetProcessHeap(), 0, FaceName);
368
369 // TODO: Select a default font size????
370 FontSizeChange(hwndDlg, pConInfo);
371
372 // InvalidateRect(GetDlgItem(hwndDlg, IDC_STATIC_FONT_WINDOW_PREVIEW), NULL, TRUE);
373 // InvalidateRect(GetDlgItem(hwndDlg, IDC_STATIC_SELECT_FONT_PREVIEW), NULL, TRUE);
374 }
375
376 static VOID
377 FontSizeChange(HWND hwndDlg,
378 PCONSOLE_STATE_INFO pConInfo)
379 {
380 HWND hListBox = GetDlgItem(hwndDlg, IDC_LBOX_FONTSIZE);
381 INT nSel;
382 ULONG FontSize;
383 WCHAR FontSizeStr[20];
384
385 nSel = (INT)SendMessageW(hListBox, LB_GETCURSEL, 0, 0);
386 if (nSel == LB_ERR) return;
387
388 /*
389 * Format:
390 * Width = FontSize.X = LOWORD(FontSize);
391 * Height = FontSize.Y = HIWORD(FontSize);
392 */
393 FontSize = (ULONG)SendMessageW(hListBox, LB_GETITEMDATA, nSel, 0);
394 if (FontSize == LB_ERR) return;
395
396 pConInfo->FontSize.X = LOWORD(FontSize);
397 pConInfo->FontSize.Y = HIWORD(FontSize);
398 DPRINT1("pConInfo->FontSize = (%d x %d)\n", pConInfo->FontSize.X, pConInfo->FontSize.Y);
399
400 InvalidateRect(GetDlgItem(hwndDlg, IDC_STATIC_FONT_WINDOW_PREVIEW), NULL, TRUE);
401 InvalidateRect(GetDlgItem(hwndDlg, IDC_STATIC_SELECT_FONT_PREVIEW), NULL, TRUE);
402
403 swprintf(FontSizeStr, L"%2d", pConInfo->FontSize.X);
404 SetDlgItemText(hwndDlg, IDC_FONT_SIZE_X, FontSizeStr);
405 swprintf(FontSizeStr, L"%2d", pConInfo->FontSize.Y);
406 SetDlgItemText(hwndDlg, IDC_FONT_SIZE_Y, FontSizeStr);
407 }
408
409
410 INT_PTR
411 CALLBACK
412 FontProc(HWND hwndDlg,
413 UINT uMsg,
414 WPARAM wParam,
415 LPARAM lParam)
416 {
417 UNREFERENCED_PARAMETER(wParam);
418
419 switch (uMsg)
420 {
421 case WM_INITDIALOG:
422 {
423 HWND hListBox = GetDlgItem(hwndDlg, IDC_LBOX_FONTTYPE);
424 HDC hDC;
425 LOGFONTW lf;
426 INT idx;
427
428 ZeroMemory(&lf, sizeof(lf));
429 lf.lfCharSet = DEFAULT_CHARSET; // CodePageToCharSet(ConInfo->CodePage);
430 // lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
431
432 hDC = GetDC(NULL);
433 EnumFontFamiliesExW(hDC, &lf, (FONTENUMPROCW)EnumFontNamesProc, (LPARAM)hListBox, 0);
434 ReleaseDC(NULL, hDC);
435
436 idx = (INT)SendMessageW(hListBox, LB_GETCOUNT, 0, 0);
437 if ((idx == 0) || (idx == LB_ERR))
438 {
439 DPRINT1("The ideal console fonts are not found; manually add default ones.\n");
440
441 /* This world is not ideal. We have to do it realistically. */
442 AddFontToList(hListBox, L"Lucida Console", TRUETYPE_FONTTYPE);
443 if (CodePageToCharSet(ConInfo->CodePage) != DEFAULT_CHARSET)
444 AddFontToList(hListBox, L"Droid Sans Fallback", TRUETYPE_FONTTYPE);
445 }
446
447 DPRINT1("ConInfo->FaceName = '%S'\n", ConInfo->FaceName);
448 idx = (INT)SendMessageW(hListBox, LB_FINDSTRINGEXACT, 0, (LPARAM)ConInfo->FaceName);
449 if (idx != LB_ERR)
450 SendMessageW(hListBox, LB_SETCURSEL, (WPARAM)idx, 0);
451
452 FontTypeChange(hwndDlg, ConInfo);
453
454 return TRUE;
455 }
456
457 case WM_DRAWITEM:
458 {
459 LPDRAWITEMSTRUCT drawItem = (LPDRAWITEMSTRUCT)lParam;
460
461 if (drawItem->CtlID == IDC_STATIC_FONT_WINDOW_PREVIEW)
462 PaintConsole(drawItem, ConInfo);
463 else if (drawItem->CtlID == IDC_STATIC_SELECT_FONT_PREVIEW)
464 PaintText(drawItem, ConInfo, Screen);
465
466 return TRUE;
467 }
468
469 case WM_NOTIFY:
470 {
471 switch (((LPNMHDR)lParam)->code)
472 {
473 case PSN_APPLY:
474 {
475 ApplyConsoleInfo(hwndDlg);
476 return TRUE;
477 }
478 }
479
480 break;
481 }
482
483 case WM_COMMAND:
484 {
485 switch (HIWORD(wParam))
486 {
487 case LBN_SELCHANGE:
488 {
489 switch (LOWORD(wParam))
490 {
491 case IDC_LBOX_FONTTYPE:
492 {
493 FontTypeChange(hwndDlg, ConInfo);
494 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
495 break;
496 }
497
498 case IDC_LBOX_FONTSIZE:
499 {
500 FontSizeChange(hwndDlg, ConInfo);
501 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
502 break;
503 }
504 }
505
506 break;
507 }
508 }
509
510 break;
511 }
512
513 default:
514 break;
515 }
516
517 return FALSE;
518 }