1 /*
2  *  fontview display class
3  *
4  *  display.c
5  *
6  *  Copyright (C) 2007  Timo Kreuzer <timo <dot> kreuzer <at> reactos <dot> org>
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License along
19  *  with this program; if not, write to the Free Software Foundation, Inc.,
20  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 
23 #include "precomp.h"
24 
25 #include <stdio.h>
26 #include <malloc.h>
27 
28 #define SPACING1 8
29 #define SPACING2 5
30 
31 extern INT g_NumFonts;
32 extern WCHAR g_FontTitle[];
33 
34 const WCHAR g_szFontDisplayClassName[] = L"FontDisplayClass";
35 LRESULT CALLBACK DisplayProc(HWND, UINT, WPARAM, LPARAM);
36 
37 /* Internal data storage type */
38 typedef struct
39 {
40 	int nPageHeight;
41 	WCHAR szTypeFaceName[LF_FULLFACESIZE];
42 	WCHAR szFormat[MAX_FORMAT];
43 	WCHAR szString[MAX_STRING];
44 
45 	HFONT hCaptionFont;
46 	HFONT hCharSetFont;
47 	HFONT hSizeFont;
48 	HFONT hFonts[MAX_SIZES];
49 	int nSizes[MAX_SIZES];
50 	int nHeights[MAX_SIZES];
51 } DISPLAYDATA;
52 
53 /* This is the only public function, it registers the class */
54 BOOL
55 Display_InitClass(HINSTANCE hInstance)
56 {
57 	WNDCLASSEXW wincl;
58 
59 	/* Set the fontdisplay window class structure */
60 	wincl.cbSize = sizeof(WNDCLASSEX);
61 	wincl.style = CS_DBLCLKS;
62 	wincl.lpfnWndProc = DisplayProc;
63 	wincl.cbClsExtra = 0;
64 	wincl.cbWndExtra = 0;
65 	wincl.hInstance = hInstance;
66 	wincl.hIcon = NULL;
67 	wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
68 	wincl.hbrBackground = GetStockObject(WHITE_BRUSH);
69 	wincl.lpszMenuName = NULL;
70 	wincl.lpszClassName = g_szFontDisplayClassName;
71 	wincl.hIconSm = NULL;
72 
73 	/* Register the window class, and if it fails return FALSE */
74 	if (!RegisterClassExW (&wincl))
75 	{
76 		return FALSE;
77 	}
78 	return TRUE;
79 }
80 
81 static int
82 Display_DrawText(HDC hDC, DISPLAYDATA* pData, int nYPos)
83 {
84 	HFONT hOldFont;
85 	TEXTMETRIC tm;
86 	int i, y;
87 	WCHAR szSize[5];
88 	WCHAR szCaption[LF_FULLFACESIZE + 20];
89 
90 	/* This is the location on the DC where we draw */
91 	y = -nYPos;
92 
93 	hOldFont = SelectObject(hDC, pData->hCaptionFont);
94 	GetTextMetrics(hDC, &tm);
95 
96 	swprintf(szCaption, L"%s%s", pData->szTypeFaceName, pData->szFormat);
97 	TextOutW(hDC, 0, y, szCaption, (INT)wcslen(szCaption));
98 	y += tm.tmHeight + SPACING1;
99 
100 	/* Draw a separation Line */
101 	SelectObject(hDC, GetStockObject(BLACK_PEN));
102 	MoveToEx(hDC, 0, y, NULL);
103 	LineTo(hDC, 10000, y);
104 	y += SPACING2;
105 
106 	/* TODO: Output font info */
107 
108 	/* Output Character set */
109 	SelectObject(hDC, pData->hCharSetFont);
110 	GetTextMetrics(hDC, &tm);
111 	swprintf(szCaption, L"abcdefghijklmnopqrstuvwxyz");
112 	TextOutW(hDC, 0, y, szCaption, (INT)wcslen(szCaption));
113 	y += tm.tmHeight + 1;
114 
115 	swprintf(szCaption, L"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
116 	TextOutW(hDC, 0, y, szCaption, (INT)wcslen(szCaption));
117 	y += tm.tmHeight + 1;
118 
119 	swprintf(szCaption, L"0123456789.:,;(\"~!@#$%%^&*')");
120 	TextOutW(hDC, 0, y, szCaption, (INT)wcslen(szCaption));
121 	y += tm.tmHeight + 1;
122 
123 	/* Draw a separation Line */
124 	SelectObject(hDC, GetStockObject(BLACK_PEN));
125 	MoveToEx(hDC, 0, y, NULL);
126 	LineTo(hDC, 10000, y);
127 	y += SPACING2;
128 
129 	/* Output the strings for different sizes */
130 	for (i = 0; i < MAX_SIZES; i++)
131 	{
132 		SelectObject(hDC, pData->hFonts[i]);
133 		TextOutW(hDC, 20, y, pData->szString, (INT)wcslen(pData->szString));
134 		GetTextMetrics(hDC, &tm);
135 		y += tm.tmHeight + 1;
136 		SelectObject(hDC, pData->hSizeFont);
137 		swprintf(szSize, L"%d", pData->nSizes[i]);
138 		TextOutW(hDC, 0, y - 13 - tm.tmDescent, szSize, (INT)wcslen(szSize));
139 	}
140 	SelectObject(hDC, hOldFont);
141 
142 	return y;
143 }
144 
145 static int
146 CALLBACK
147 EnumFontFamProcW(
148 	const LOGFONTW *lpelfe,
149 	const TEXTMETRICW *lptm,
150 	DWORD FontType,
151 	LPARAM lParam)
152 {
153 	PNEWTEXTMETRICW pntmw = (PNEWTEXTMETRICW)lptm;
154 	PBOOL pfOpenType = (PBOOL)lParam;
155 
156 	if (FontType & TRUETYPE_FONTTYPE)
157 	{
158 		if (pntmw->ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE))
159 		{
160 			*pfOpenType = TRUE;
161 			return FALSE;
162 		}
163 	}
164 	return TRUE;
165 }
166 
167 static LRESULT
168 Display_SetTypeFace(HWND hwnd, PLOGFONTW pLogFont)
169 {
170 	DISPLAYDATA* pData;
171 	TEXTMETRIC tm;
172 	HDC hDC;
173 	RECT rect;
174 	SCROLLINFO si;
175 	int i;
176 	LOGFONTW logfont;
177 	BOOL fOpenType;
178 	BYTE Buffer[512];
179 	LPOUTLINETEXTMETRICW pOTM = (LPOUTLINETEXTMETRICW)Buffer;
180 	LPWSTR pch;
181 
182 	/* Set the new type face name */
183 	pData = (DISPLAYDATA*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
184 	lstrcpynW(pData->szTypeFaceName, pLogFont->lfFaceName,
185 	          ARRAYSIZE(pData->szTypeFaceName));
186 
187 	/* Create the new fonts */
188 	hDC = GetDC(hwnd);
189 	DeleteObject(pData->hCharSetFont);
190 
191 	logfont = *pLogFont;
192 	logfont.lfHeight = -MulDiv(16, GetDeviceCaps(GetDC(NULL), LOGPIXELSY), 72);
193 	pData->hCharSetFont = CreateFontIndirectW(&logfont);
194 
195 	/* Get font format */
196 	SelectObject(hDC, pData->hCharSetFont);
197 	GetTextMetrics(hDC, &tm);
198 	if (tm.tmPitchAndFamily & TMPF_TRUETYPE)
199 	{
200 		if (GetOutlineTextMetricsW(hDC, sizeof(Buffer), pOTM))
201 		{
202 			LPBYTE pb = Buffer;
203 			pb += (WORD)(DWORD_PTR)pOTM->otmpStyleName;
204 			pch = (LPWSTR)pb;
205 			if (*pch)
206 			{
207 				lstrcatW(pData->szTypeFaceName, L" ");
208 				lstrcatW(pData->szTypeFaceName, pch);
209 			}
210 		}
211 
212 		fOpenType = FALSE;
213 		EnumFontFamiliesExW(hDC, &logfont,
214 			EnumFontFamProcW, (LPARAM)&fOpenType, 0);
215 
216 		if (fOpenType)
217 			swprintf(pData->szFormat, L" (OpenType)");
218 		else
219 			swprintf(pData->szFormat, L" (TrueType)");
220 	}
221 	else if (tm.tmPitchAndFamily & TMPF_VECTOR)
222 	{
223 		swprintf(pData->szFormat, L" (Vector)");
224 	}
225 	else
226 	{
227 		swprintf(pData->szFormat, L" (Raster)");
228 	}
229 
230 	for (i = 0; i < MAX_SIZES; i++)
231 	{
232 		DeleteObject(pData->hFonts[i]);
233 		logfont.lfHeight = -MulDiv(pData->nSizes[i], GetDeviceCaps(hDC, LOGPIXELSY), 72);
234 		pData->hFonts[i] = CreateFontIndirectW(&logfont);
235 	}
236 
237 	/* Calculate new page dimensions */
238 	pData->nPageHeight = Display_DrawText(hDC, pData, 0);
239 	ReleaseDC(hwnd, hDC);
240 
241 	/* Set the vertical scrolling range and page size */
242 	GetClientRect(hwnd, &rect);
243 	si.cbSize = sizeof(si);
244 	si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
245 	si.nMin   = 0;
246 	si.nMax   = pData->nPageHeight;
247 	si.nPage  = rect.bottom;
248 	si.nPos   = 0;
249 	si.nTrackPos = 0;
250 	SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
251 
252 	return 0;
253 }
254 
255 static LRESULT
256 Display_SetString(HWND hwnd, LPCWSTR pszString)
257 {
258 	DISPLAYDATA* pData;
259 
260 	pData = (DISPLAYDATA*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
261 	lstrcpynW(pData->szString, pszString, ARRAYSIZE(pData->szString));
262 
263 	InvalidateRect(hwnd, NULL, TRUE);
264 
265 	return 0;
266 }
267 
268 static LRESULT
269 Display_OnCreate(HWND hwnd)
270 {
271 	DISPLAYDATA* pData;
272 	const int nSizes[MAX_SIZES] = {8, 12, 18, 24, 36, 48, 60, 72};
273 	int i;
274 	LOGFONTW LogFont = {50, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
275 	                    ANSI_CHARSET, OUT_DEFAULT_PRECIS,
276 	                    CLIP_DEFAULT_PRECIS, PROOF_QUALITY,
277 	                    DEFAULT_PITCH , L"MS Shell Dlg"};
278 
279 	/* Create data structure */
280 	pData = malloc(sizeof(DISPLAYDATA));
281 	ZeroMemory(pData, sizeof(DISPLAYDATA));
282 
283 	/* Set the window's GWLP_USERDATA to our data structure */
284 	SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pData);
285 
286 	for (i = 0; i < MAX_SIZES; i++)
287 	{
288 		pData->nSizes[i] = nSizes[i];
289 	}
290 
291 	pData->hCaptionFont = CreateFontIndirectW(&LogFont);
292 	LogFont.lfHeight = 12;
293 	pData->hSizeFont = CreateFontIndirectW(&LogFont);
294 
295 	Display_SetString(hwnd,
296 		L"Jackdaws love my big sphinx of quartz. 1234567890");
297 
298 	Display_SetTypeFace(hwnd, &LogFont);
299 
300 	return 0;
301 }
302 
303 static LRESULT
304 Display_OnPaint(HWND hwnd)
305 {
306 	DISPLAYDATA* pData;
307 	PAINTSTRUCT ps;
308 	SCROLLINFO si;
309 
310 	pData = (DISPLAYDATA*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
311 
312 	/* Get the Scroll position */
313 	si.cbSize = sizeof(si);
314 	si.fMask = SIF_POS;
315 	GetScrollInfo(hwnd, SB_VERT, &si);
316 
317 	BeginPaint(hwnd, &ps);
318 
319 	/* Erase background */
320 	FillRect(ps.hdc, &ps.rcPaint, GetStockObject(WHITE_BRUSH));
321 
322 	/* Draw the text */
323 	Display_DrawText(ps.hdc, pData, si.nPos);
324 
325 	EndPaint(hwnd, &ps);
326 
327 	return 0;
328 }
329 
330 static LRESULT
331 Display_OnSize(HWND hwnd)
332 {
333 	RECT rect;
334 	SCROLLINFO si;
335 	int nOldPos;
336 
337 	GetClientRect(hwnd, &rect);
338 
339 	/* Get the old scroll pos */
340 	si.cbSize = sizeof(si);
341 	si.fMask  = SIF_POS;
342 	GetScrollInfo(hwnd, SB_VERT, &si);
343 	nOldPos = si.nPos;
344 
345 	/* Set the new page size */
346 	si.fMask  = SIF_PAGE;
347 	si.nPage  = rect.bottom;
348 	SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
349 
350 	/* Get the new scroll pos */
351 	si.fMask  = SIF_POS;
352 	GetScrollInfo(hwnd, SB_VERT, &si);
353 
354 	/* If they don't match ... */
355 	if (nOldPos != si.nPos)
356 	{
357 		/* ... scroll the window */
358 		ScrollWindowEx(hwnd, 0, nOldPos - si.nPos, NULL, NULL, NULL, NULL, SW_INVALIDATE);
359 		UpdateWindow(hwnd);
360 	}
361 
362 	return 0;
363 }
364 
365 static LRESULT
366 Display_OnVScroll(HWND hwnd, WPARAM wParam)
367 {
368 	SCROLLINFO si;
369 	int nPos;
370 
371 	si.cbSize = sizeof(si);
372 	si.fMask  = SIF_POS | SIF_RANGE | SIF_TRACKPOS;
373 	GetScrollInfo(hwnd, SB_VERT, &si);
374 
375 	switch(LOWORD(wParam))
376 	{
377 		case SB_PAGEUP:
378 			nPos = si.nPos - 50;
379 			break;
380 		case SB_PAGEDOWN:
381 			nPos = si.nPos + 50;
382 			break;
383 		case SB_LINEUP:
384 			nPos = si.nPos - 10;
385 			break;
386 		case SB_LINEDOWN:
387 			nPos = si.nPos + 10;
388 			break;
389 		case SB_THUMBTRACK:
390 		case SB_THUMBPOSITION:
391 			nPos = si.nTrackPos;
392 			break;
393 		default:
394 			nPos = si.nPos;
395 	}
396 
397 	nPos = max(nPos, si.nMin);
398 	nPos = min(nPos, si.nMax);
399 	if (nPos != si.nPos)
400 	{
401 		ScrollWindowEx(hwnd, 0, si.nPos - nPos, NULL, NULL, NULL, NULL, SW_INVALIDATE);
402 		si.cbSize = sizeof(si);
403 		si.nPos = nPos;
404 		si.fMask  = SIF_POS;
405 		SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
406 		UpdateWindow(hwnd);
407 	}
408 
409 	return 0;
410 }
411 
412 static LRESULT
413 Display_OnDestroy(HWND hwnd)
414 {
415 	DISPLAYDATA* pData;
416 	int i;
417 
418 	pData = (DISPLAYDATA*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
419 
420 	/* Delete the fonts */
421 	DeleteObject(pData->hCaptionFont);
422 	DeleteObject(pData->hCharSetFont);
423 	DeleteObject(pData->hSizeFont);
424 
425 	for (i = 0; i < MAX_SIZES; i++)
426 	{
427 		DeleteObject(pData->hFonts[i]);
428 	}
429 
430 	/* Free the data structure */
431 	free(pData);
432 
433 	return 0;
434 }
435 
436 LRESULT
437 Display_OnPrint(HWND hwnd)
438 {
439 	PRINTDLG pfont;
440 	TEXTMETRIC tm;
441 	int copies, yPos;
442 
443 	/* Clears the memory before using it */
444 	ZeroMemory(&pfont, sizeof(pfont));
445 
446 	pfont.lStructSize = sizeof(pfont);
447 	pfont.hwndOwner = hwnd;
448 	pfont.hDevMode = NULL;
449 	pfont.hDevNames = NULL;
450 	pfont.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC;
451 	pfont.nCopies = 1;
452 	pfont.nFromPage = 0xFFFF;
453 	pfont.nToPage = 0xFFFF;
454 	pfont.nMinPage = 1;
455 	pfont.nMaxPage = 0xFFFF;
456 
457 	/* Opens up the print dialog box */
458 	if (PrintDlg(&pfont))
459 	{
460 		DOCINFO docinfo;
461 #if 0
462 		DISPLAYDATA* pData;
463 
464 		pData = malloc(sizeof(DISPLAYDATA));
465 		ZeroMemory(pData, sizeof(DISPLAYDATA));
466 
467 		/* Sets up the font layout */
468 		pData = (DISPLAYDATA*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
469 #endif
470 		docinfo.cbSize = sizeof(DOCINFO);
471 		docinfo.lpszDocName = L"Printing Font";
472 		docinfo.lpszOutput = NULL;
473 		docinfo.lpszDatatype = NULL;
474 		docinfo.fwType = 0;
475 
476 		/* We start printing */
477 		StartDoc(pfont.hDC, &docinfo);
478 
479 		/* Grabs the text metrics for the printer */
480 		GetTextMetrics(pfont.hDC, &tm);
481 
482 		/* Start out with 0 for the y position for the page */
483 		yPos = 0;
484 
485 		/* Starts out with the current page */
486 		StartPage(pfont.hDC);
487 
488 		/* Used when printing for more than one copy */
489 		for (copies = 0; copies < pfont.nCopies; copies++)
490 		{
491 			/* Test output */
492 			TextOutW(pfont.hDC, 10, yPos, L"Testing...1...2...3", 19);
493 
494 			/* TODO: Determine if using Display_DrawText() will work for both rendering out to the
495 			window and to the printer output */
496 #if 0
497 			Display_DrawText(pfont.hDC, pData, yPos);
498 #endif
499 
500 			/* Ends the current page */
501 			EndPage(pfont.hDC);
502 
503 			/* If we are making more than one copy, start a new page */
504 			if (copies != pfont.nCopies)
505 			{
506 				yPos = 0;
507 				StartPage(pfont.hDC);
508 			}
509 		}
510 
511 		/* The printing is now over */
512 		EndDoc(pfont.hDC);
513 
514 		DeleteDC(pfont.hDC);
515 #if 0
516 		/* Frees the memory since we no longer need it for now */
517 		free(pData);
518 #endif
519 	}
520 
521 	return 0;
522 }
523 
524 LRESULT
525 Display_GetFullName(HWND hwnd, INT length, PWSTR ptr)
526 {
527     DISPLAYDATA *pData;
528     INT len;
529 
530     pData = (DISPLAYDATA*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
531 
532     len = wcslen(pData->szTypeFaceName) + wcslen(pData->szFormat) + 2;
533 
534     if (ptr != NULL && length >= len)
535     {
536         swprintf(ptr, L"%s%s", pData->szTypeFaceName, pData->szFormat);
537     }
538 
539     return (LRESULT)len;
540 }
541 
542 LRESULT CALLBACK
543 DisplayProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
544 {
545 	switch (message)
546 	{
547 		case WM_CREATE:
548 			return Display_OnCreate(hwnd);
549 
550 		case WM_PAINT:
551 			return Display_OnPaint(hwnd);
552 
553 		case WM_SIZE:
554 			return Display_OnSize(hwnd);
555 
556 		case WM_VSCROLL:
557 			return Display_OnVScroll(hwnd, wParam);
558 
559 		case FVM_SETTYPEFACE:
560 			return Display_SetTypeFace(hwnd, (PLOGFONTW)lParam);
561 
562 		case FVM_SETSTRING:
563 			return Display_SetString(hwnd, (WCHAR *)lParam);
564 
565 		case FVM_GETFULLNAME:
566 			return Display_GetFullName(hwnd, (INT)wParam, (PWSTR)lParam);
567 
568 		case WM_DESTROY:
569 			return Display_OnDestroy(hwnd);
570 
571 		default:
572 			return DefWindowProcW(hwnd, message, wParam, lParam);
573 	}
574 
575 	return 0;
576 }
577 
578