Programming Microsoft Windows Ce Net 3Rd [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Programming Microsoft Windows Ce Net 3Rd [Electronic resources] - نسخه متنی

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید






Chapter 1, the HelloCE example displayed a line of text using a call to DrawText. That line from the example is shown here:

DrawText (hdc, TEXT ("Hello Windows CE!"), -1, &rect, 
DT_CENTER | DT_VCENTER | DT_SINGLELINE);

DrawText is a fairly high-level function that allows a program to display text while having Windows deal with most of the details. The first few parameters of DrawText are almost self-explanatory. The handle of the device context being used is passed, along with the text to display couched in a TEXT macro, which declares the string as a Unicode string necessary for Windows CE. The third parameter is the number of characters to print, or as is the case here, a -1 indicating that the string being passed is null terminated and Windows should compute the length.

The fourth parameter is a pointer to a rect structure that specifies the formatting rectangle for the text. DrawText uses this rectangle as a basis for formatting the text to be printed. How the text is formatted depends on the function's last parameter, the formatting flags. These flags specify how the text is to be placed within the formatting rectangle, or in the case of the DT_CALCRECT flag, the flags have DrawText compute the dimensions of the text that is to be printed. DrawText even formats multiple lines with line breaks automatically computed. In the case of HelloCE, the flags specify that the text should be centered horizontally (DT_CENTER), and centered vertically (DT_VCENTER). The DT_VCENTER flag works only on single lines of text, so the final parameter, DT_SINGLELINE, specifies that the text shouldn't be flowed across multiple lines if the rectangle isn't wide enough to display the entire string.

Another way to draw text is by employing the following function:

BOOL ExtTextOut (HDC hdc, int X, int Y, UINT fuOptions,
const RECT *lprc, LPCTSTR lpString,
UINT cbCount, const int *lpDx);

The ExtTextOut function has a few advantages over DrawText. First, ExtTextOut tends to be faster for drawing single lines of text. Second, the text isn't formatted inside a rectangle; instead, x and y starting coordinates are passed, specifying where the text will be drawn. Generally, the point defined by the coordinates is the upper left corner of the rectangle, but this can be changed with the text alignment settings of the DC. The rect parameter that's passed is used as a clipping rectangle or, if the background mode is opaque, the area where the background color is drawn. This rectangle parameter can be NULL if you don't want any clipping or opaquing. The next two parameters are the text and the character count. The last parameter, ExtTextOut, allows an application to specify the horizontal distance between adjacent character cells.

Windows CE differs from other versions of Windows in having only these two text drawing functions for displaying text. You can emulate most of what you can do with the text functions typically used in other versions of Windows, such as TextOut and TabbedTextOut, by using either DrawText or ExtTextOut. This is one of the areas in which Windows CE has broken with earlier versions of Windows, sacrificing backward compatibility to achieve a smaller operating system.


Device Context Attributes


What I haven't mentioned yet about HelloCE's use of DrawText is the large number of assumptions the program makes about the DC configuration when displaying the text. Drawing in a Windows device context takes a large number of parameters, such as foreground and background color and how the text should be drawn over the background as well as the font of the text. Instead of specifying all these parameters for each drawing call, the device context keeps track of the current settings, referred to as attributes, and uses them as appropriate for each call to draw to the device context.

Foreground and Background Colors


The most obvious of the text attributes are the foreground and background color. Two functions, SetTextColor and GetTextColor, allow a program to set and retrieve the current color. These functions work well with both gray-scale screens and the color screens supported by Windows CE devices.

To determine how many colors a device supports, use GetDeviceCaps as mentioned previously. The prototype for this function is the following:

int GetDeviceCaps (HDC hdc, int nIndex);

You need the handle to the DC being queried because different DCs have different capabilities. For example, a printer DC differs from a display DC. The second parameter indicates the capability being queried. In the case of returning the colors available on the device, the NUMCOLORS value returns the number of colors as long as the device supports 256 colors or fewer. Beyond that, the returned value for NUMCOLORS is -1 and the colors can be returned using the BITSPIXEL value, which returns the number of bits used to represent each pixel. This value can be converted to the number of colors by raising 2 to the power of the BITSPIXEL returned value, as in the following code sample:

nNumColors = GetDeviceCaps (hdc, NUMCOLORS);
if (nNumColors == -1)
nNumColors = 1 << GetDeviceCaps (hdc, BITSPIXEL);

Text Alignment


When displaying text with ExtTextOut, the system uses the text alignment of the DC to determine where to draw the text. The text can be aligned both horizontally and vertically, using this function:

UINT WINAPI SetTextAlign (HDC hdc, INT fmode);

The alignment flags passed to fmode are as follows:



TA_LEFTThe left edge of the text is aligned with the reference point.



TA_RIGHTThe right edge of the text is aligned with the reference point.



TA_TOP The top edge of the text is aligned with the reference point.



TA_CENTER The text is centered horizontally with the reference point.



TA_BOTTOM The bottom edge of the text is aligned with the reference point.



TA_BASELINEThe base line of the text is aligned with the reference point.



TA_NOUPDATECP The current point of the DC is not updated after the ExtTextOut call.



TA_UPDATECP The current point of the DC is updated after the ExtTextOut call.



The reference point in the description refers to the x and y coordinates passed to the ExtTextOut function. For each call to SetTextAlign, a flag for vertical alignment and a flag for horizontal alignment can be combined.

Because it might be difficult to visualize what each of these flags does, Figure 2-1 shows the results of each flag. In the figure, the X is the reference point.


Figure 2-1: The relationship between the current drawing point and the text alignment flags

Drawing Mode


Another attribute that affects text output is the background mode. When letters are drawn on the device context, the system draws the letters themselves in the foreground color. The space between the letters is another matter. If the background mode is set to opaque, the space is drawn with the current background color. But if the background mode is set to transparent, the space between the letters is left in whatever state it was in before the text was drawn. While this might not seem like a big difference, imagine a window background filled with a drawing or graph. If text is written over the top of the graph and the background mode is set to opaque, the area around the text will be filled, and the background color will overwrite the graph. If the background mode is transparent, the text will appear as if it had been placed on the graph, and the graph will show through between the letters of the text.


The TextDemo Example Program


The TextDemo program, shown in Listing 2-1, demonstrates the relationships among the text color, the background color, and the background mode.

Listing 2-1: The TextDemo program







TextDemo.h
//================================================================
// Header file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
// Returns number of elements
#define dim(x) (sizeof(x) / sizeof(x[0]))
//----------------------------------------------------------------------
// Generic defines and data types
//
struct decodeUINT { // Structure associates
UINT Code; // messages
// with a function.
LRESULT (*Fxn)(HWND, UINT, WPARAM, LPARAM);
};
struct decodeCMD { // Structure associates
UINT Code; // menu IDs with a
LRESULT (*Fxn)(HWND, WORD, HWND, WORD); // function.
};
//----------------------------------------------------------------------
// Function prototypes
//
HWND InitInstance (HINSTANCE, LPWSTR, int);
int TermInstance (HINSTANCE, int);
// Window procedures
LRESULT CALLBACK MainWndProc (HWND, UINT, WPARAM, LPARAM);
// Message handlers
LRESULT DoPaintMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoDestroyMain (HWND, UINT, WPARAM, LPARAM);


TextDemo.cpp
//================================================================
// TextDemo - Text output demo
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include "TextDemo.h" // Program-specific stuff
//----------------------------------------------------------------------
// Global data
//
const TCHAR szAppName[] = TEXT ("TextDemo");
HINSTANCE hInst; // Program instance handle
// Message dispatch table for MainWindowProc
const struct decodeUINT MainMessages[] = {
WM_PAINT, DoPaintMain,
WM_DESTROY, DoDestroyMain,
};
//======================================================================
// Program Entry Point
//
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow) {
MSG msg;
int rc = 0;
HWND hwndMain;
// Initialize this instance.
hwndMain = InitInstance (hInstance, lpCmdLine, nCmdShow);
if (hwndMain == 0)
return 0x10;
// Application message loop
while (GetMessage (&msg, NULL, 0, 0)) {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
// Instance cleanup
return TermInstance (hInstance, msg.wParam);
}
//----------------------------------------------------------------------
// InitInstance - Instance initialization
//
HWND InitInstance (HINSTANCE hInstance, LPWSTR lpCmdLine, int nCmdShow){
WNDCLASS wc;
HWND hWnd;
hInst = hInstance; // Save handle in global variable.
#if defined(WIN32_PLATFORM_PSPC)
// If Pocket PC, allow only one instance of the application.
hWnd = FindWindow (szAppName, NULL);
if (hWnd) {
SetForegroundWindow ((HWND)(((DWORD)hWnd) | 0x01));
return 0;
}
#endif
// Register application main window class.
wc.style = 0; // Window style
wc.lpfnWndProc = MainWndProc; // Callback function
wc.cbClsExtra = 0; // Extra class data
wc.cbWndExtra = 0; // Extra window data
wc.hInstance = hInstance; // Owner handle
wc.hIcon = NULL, // Application icon
wc.hCursor = LoadCursor (NULL, IDC_ARROW);// Default cursor
wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = NULL; // Menu name
wc.lpszClassName = szAppName; // Window class name
if (RegisterClass (&wc) == 0) return 0;
// Create main window.
hWnd = CreateWindowEx (WS_EX_NODRAG, // Ex Style flags
szAppName, // Window class
TEXT("TextDemo"), // Window title
// Style flags
WS_VISIBLE | WS_CAPTION | WS_SYSMENU,
CW_USEDEFAULT, // x position
CW_USEDEFAULT, // y position
CW_USEDEFAULT, // Initial width
CW_USEDEFAULT, // Initial height
NULL, // Parent
NULL, // Menu, must be null
hInstance, // Application instance
NULL); // Pointer to create
// Parameters
// Return fail code if window not created.
if ((!hWnd) || (!IsWindow (hWnd))) return 0;
// Standard show and update calls
ShowWindow (hWnd, nCmdShow);
UpdateWindow (hWnd);
return hWnd;
}
//----------------------------------------------------------------------
// TermInstance - Program cleanup
//
int TermInstance (HINSTANCE hInstance, int nDefRC) {
return nDefRC;
}
//======================================================================
// Message handling procedures for MainWindow
//
//----------------------------------------------------------------------
// MainWndProc - Callback function for application window
//
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
INT i;
//
// Search message list to see if we need to handle this
// message. If in list, call procedure.
//
for (i = 0; i < dim(MainMessages); i++) {
if (wMsg == MainMessages[i].Code)
return (*MainMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
}
return DefWindowProc (hWnd, wMsg, wParam, lParam);
}
//----------------------------------------------------------------------
// DoPaintMain - Process WM_PAINT message for window.
//
LRESULT DoPaintMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
PAINTSTRUCT ps;
RECT rect, rectCli;
HBRUSH hbrOld;
HDC hdc;
INT i, cy;
DWORD dwColorTable[] = {0x00000000, 0x00808080,
0x00cccccc, 0x00ffffff};
GetClientRect (hWnd, &rectCli);
hdc = BeginPaint (hWnd, &ps);
// Get the height and length of the string.
DrawText (hdc, TEXT ("Hello Windows CE"), -1, &rect,
DT_CALCRECT | DT_CENTER | DT_SINGLELINE);
cy = rect.bottom - rect.top + 5;
// Draw black rectangle on right half of window.
hbrOld = (HBRUSH)SelectObject (hdc, GetStockObject (BLACK_BRUSH));
Rectangle (hdc, rectCli.left + (rectCli.right - rectCli.left) / 2,
rectCli.top, rectCli.right, rectCli.bottom);
SelectObject (hdc, hbrOld);
rectCli.bottom = rectCli.top + cy;
SetBkMode (hdc, TRANSPARENT);
for (i = 0; i < 4; i++) {
SetTextColor (hdc, dwColorTable[i]);
SetBkColor (hdc, dwColorTable[3-i]);
DrawText (hdc, TEXT ("Hello Windows CE"), -1, &rectCli,
DT_CENTER | DT_SINGLELINE);
rectCli.top += cy;
rectCli.bottom += cy;
}
SetBkMode (hdc, OPAQUE);
for (i = 0; i < 4; i++) {
SetTextColor (hdc, dwColorTable[i]);
SetBkColor (hdc, dwColorTable[3-i]);
DrawText (hdc, TEXT ("Hello Windows CE"), -1, &rectCli,
DT_CENTER | DT_SINGLELINE);
rectCli.top += cy;
rectCli.bottom += cy;
}
EndPaint (hWnd, &ps);
return 0;
}
//----------------------------------------------------------------------
// DoDestroyMain - Process WM_DESTROY message for window.
//
LRESULT DoDestroyMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
PostQuitMessage (0);
return 0;
}











The meat of TextDemo is in the OnPaintMain function. The first call to DrawText doesn't draw anything in the device context. Instead, the DT_CALCRECT flag instructs Windows to store the dimensions of the rectangle for the text string in rect. This information is used to compute the height of the string, which is stored in cy. Next, a black rectangle is drawn on the right side of the window. I'll talk about how a rectangle is drawn later in the chapter; it's used in this program to produce two different backgrounds before the text is written. The function then prints out the same string using different foreground and background colors and both the transparent and opaque drawing modes. The result of this combination is shown in Figure 2-2.

The first four lines are drawn using the transparent mode. The second four are drawn using the opaque mode. The text color is set from black to white so that each line drawn uses a different color, while at the same time the background color is set from white to black. In transparent mode, the background color is irrelevant because it isn't used; but in opaque mode, the background color is readily apparent on each line.


Figure 2-2: TextDemo shows how the text color, background color, and background mode relate.


Fonts


If the ability to set the foreground and background colors were all the flexibility that Windows provided, we might as well be back in the days of MS-DOS and character attributes. Arguably, the most dramatic change from MS-DOS is Windows' ability to change the font used to display text. All Windows operating systems are built around the concept of WYSIWYG—what you see is what you get—and changeable fonts are a major tool used to achieve that goal.

Two types of fonts appear in all Windows operating systems—raster and TrueType. Raster fonts are stored as bitmaps, small pixel-by-pixel images, one for each character in the font. Raster fonts are easy to store and use but have one major problem: they don't scale well. Just as a small picture looks grainy when blown up to a much larger size, raster fonts begin to look blocky as they are scaled to larger and larger font sizes.

TrueType fonts solve the scaling problem. Instead of being stored as images, each TrueType character is stored as a description of how to draw the character. The font engine, which is the part of Windows that draws characters on the screen, then takes the description and draws it on the screen in any size needed. A Windows CE system can support either TrueType or raster fonts, but not both. Fortunately, the programming interface is the same for both raster and TrueType fonts, relieving Windows developers from worrying about the font technology in all but the most exacting of applications.

The font functions under Windows CE closely track the same functions under other versions of Windows. Let's look at the functions used in the life of a font, from creation through selection in a DC and finally to deletion of the font. How to query the current font as well as enumerate the available fonts is also covered in the following sections.

Creating a Font


Before an application is able to use a font other than the default font, the font must be created and then selected into the device context. Any text drawn in a DC after the new font has been selected into the DC will then use the new font.

Creating a font in Windows CE can be accomplished this way:

HFONT CreateFontIndirect (const LOGFONT *lplf);

This function is passed a pointer to a LOGFONT structure that must be filled with the description of the font you want.

typedef struct tagLOGFONT {
LONG lfHeight;
LONG lfWidth;
LONG lfEscapement;
LONG lfOrientation;
LONG lfWeight;
BYTE lfItalic;
BYTE lfUnderline;
BYTE lfStrikeOut;
BYTE lfCharSet;
BYTE lfOutPrecision;
BYTE lfClipPrecision;
BYTE lfQuality;
BYTE lfPitchAndFamily;
TCHAR lfFaceName[LF_FACESIZE];
} LOGFONT;

The lfHeight field specifies the height of the font in device units. If this field is 0, the font manager returns the default font size for the font family requested. For most applications, however, you want to create a font of a particular point size. The following equation can be used to convert point size to the lfHeight field:

lfHeight = –1 * (PointSize * GetDeviceCaps (hdc, LOGPIXELSY) / 72);

Here GetDeviceCaps is passed a LOGPIXELSY field instructing it to return the number of logical pixels per inch in the vertical direction. The 72 is the number of points (a typesetting unit of measure) per inch.

The lfWidth field specifies the average character width. Since the height of a font is more important than its width, most programs set this value to 0. This tells the font manager to compute the proper width based on the height of the font. The lfEscapement and lfOrientation fields specify the angle in tenths of degrees of the base line of the text and the x-axis. The lfWeight field specifies the boldness of the font from 0 through 1000, with 400 being a normal font and 700 being bold. The next three fields specify whether the font is to be italic, underline, or strikeout.

The lpCharSet field specifies the character set you have chosen. This field is more important in international releases of software, where it can be used to request a specific language's character set. The lfOutPrecision field can be used to specify how closely Windows matches your requested font. Among a number of flags available, an OUT_TT_ONLY_PRECIS flag specifies that the font created must be a TrueType font. The lfClipPrecision field specifies how Windows should clip characters that are partially outside the region being displayed.

The lfQuality field is set to one of the following:



DEFAULT_QUALITY Default system quality.



DRAFT_QUALITY Sacrifice quality for speed.



CLEARTYPE_QUALITY Render text using ClearType technology.



CLEARTYPE_COMPAT_QUALITY Render text using ClearType. Use the same spacing as non-ClearType font.



ClearType is a text display technology that provides a sharper look for fonts using the ability to address the individual red, green, and blue LEDs that make up a pixel on a color LCD display. Depending on the system, ClearType might not be supported or it might be enabled for all fonts in the system. For systems that support ClearType but don't enabled it globally, using the CLEARTYPE_QUALITY or CLEARTYPE_COMPAT_QUALITY flags will create a font that will be rendered using ClearType. Because ClearType doesn't improve the look of all fonts, you should test to see whether applying ClearType improves the rendering of your chosen font.

The lfPitchAndFamily field specifies the family of the font you want. This field is handy when you're requesting a family such as Swiss, which features proportional fonts without serifs, or a family such as Roman, which features proportional fonts with serifs, but you don't have a specific font in mind. You can also use this field to specify simply a proportional or a monospaced font and allow Windows to determine which font matches the other specified characteristics passed into the LOGFONT structure. Finally, the lfFaceName field can be used to specify the typeface name of a specific font.

When CreateFontIndirect is called with a filled LOGFONT structure, Windows creates a logical font that best matches the characteristics provided. To use the font, however, the final step of selecting the font into a device context must be made.

Selecting a Font into a Device Context


You select a font into a DC by using the following function:

HGDIOBJ SelectObject (HDC hdc, HGDIOBJ hgdiobj);

This function is used for more than just setting the default font; you use this function to select other GDI objects, as we shall soon see. The function returns the previously selected object (in our case, the previously selected font), which should be saved so that it can be selected back into the DC when we're finished with the new font. The line of code looks like the following:

hOldFont = (HFONT)SelectObject (hdc, hFont);

When the logical font is selected, the system determines the closest match to the logical font from the fonts available in the system. For devices without TrueType fonts, this match could be a fair amount off from the specified parameters. Because of this, never assume that just because you've requested a particular font, the font returned exactly matches the one you requested. For example, the height of the font you asked for might not be the height of the font that's selected into the device context.

Querying a Font's Characteristics


To determine the characteristics of the font that is selected into a device context, a call to

BOOL GetTextMetrics (HDC hdc, LPTEXTMETRIC lptm);

returns the characteristics of that font. A TEXTMETRIC structure is returned with the information and is defined as

typedef struct tagTEXTMETRIC {
LONG tmHeight;
LONG tmAscent;
LONG tmDescent;
LONG tmInternalLeading;
LONG tmExternalLeading;
LONG tmAveCharWidth;
LONG tmMaxCharWidth;
LONG tmWeight;
LONG tmOverhang;
LONG tmDigitizedAspectX;
LONG tmDigitizedAspectY;
char tmFirstChar;
char tmLastChar;
char tmDefaultChar;
char tmBreakChar;
BYTE tmItalic;
BYTE tmUnderlined;
BYTE tmStruckOut;
BYTE tmPitchAndFamily;
BYTE tmCharSet;
} TEXTMETRIC;

The TEXTMETRIC structure contains a number of the fields we saw in the LOGFONT structure, but this time the values listed in TEXTMETRIC are the values of the font that's selected into the device context. Figure 2-3 shows the relationship of some of the fields to actual characters.


Figure 2-3: Fields from the TEXTMETRIC structure and how they relate to a font

Aside from determining whether you really got the font you wanted, the GetTextmetrics call has another valuable purpose—determining the height of the font. Recall that in TextDemo, the height of the line was computed using a call to DrawText. While that method is convenient, it tends to be slow. You can use the TEXTMETRIC data to compute this height in a much more straightforward manner. By adding the tmHeight field, which is the height of the characters, to the tmExternalLeading field, which is the distance between the bottom pixel of one row and the top pixel of the next row of characters, you can determine the vertical distance between the baselines of two lines of text.

Although GetTextMetrics is great for determining the height of a font, it provides only the average and maximum widths of a font. If more detail is needed for a TrueType font, the function

BOOL GetCharABCWidths (HDC hdc, UINT uFirstChar, UINT uLastChar,
LPABC lpabc);

can be used. GetCharABCWidths returns the "ABC" widths of a series of characters delineated by the uFirstChar and uLastChar parameters. The font examined is the font currently selected in the device context specified by the hdc parameter. The ABC structure is defined as follows:

typedef struct _ABC { 
int abcA;
UINT abcB;
int abcC;
} ABC;

The abcA field is the distance to add to the current position before drawing the character, or glyph. The abcB field is the width of the glyph, while the abcC field is the distance to add to the current position after drawing the glyph. Both abcA and abcC can be negative to indicate underhangs and overhangs.

To examine the widths of bitmap fonts, GetCharWidth32 can be used. It returns an array of character widths for each character in a range of characters.

Destroying a Font


Like other GDI resources, fonts must be destroyed after the program has finished using them. Failure to delete fonts before terminating a program causes what's known as a resource leak—an orphaned graphic resource that's taking up valuable memory but that's no longer owned by an application.

To destroy a font, first deselect it from any device contexts it has been selected into. You do this by calling SelectObject; the font passed is the font that was returned by the original SelectObject call made to select the font. After the font has been deselected, a call to

BOOL DeleteObject (HGDIOBJ hObject);

(with hObject containing the font handle) deletes the font from the system.

As you can see from this process, font management is no small matter in Windows. The many parameters of the LOGFONT structure might look daunting, but they give an application tremendous power to specify a font exactly.

One problem when dealing with fonts is determining just what types of fonts are available on a specific device. Windows CE devices come with a set of standard fonts, but a specific system might have been loaded with additional fonts by either the manufacturer or the user. Fortunately, Windows provides a method for enumerating all the available fonts in a system.

Enumerating Fonts


To determine what fonts are available on a system, Windows provides this function:

int EnumFontFamilies (HDC hdc, LPCTSTR lpszFamily,
FONTENUMPROC lpEnumFontFamProc, LPARAM lParam);

This function lets you list all the font families as well as each font within a family. The first parameter is the obligatory handle to the device context. The second parameter is a string to the name of the family to enumerate. If this parameter is null, the function enumerates each of the available families.

The third parameter is something different—a pointer to a function provided by the application. The function is a callback function that Windows calls once for each font being enumerated. The final parameter, lParam, is a generic parameter that can be used by the application. This value is passed unmodified to the application's callback procedure.

While the name of the callback function can be anything, the prototype of the callback must match the declaration:

int CALLBACK EnumFontFamProc (LOGFONT *lpelf, TEXTMETRIC *lpntm,
DWORD FontType, LPARAM lParam);

The first parameter passed back to the callback function is a pointer to a LOGFONT structure describing the font being enumerated. The second parameter, a pointer to a textmetric structure, further describes the font. The font type parameter indicates whether the font is a raster or TrueType font.


The FontList Example Program


The FontList program, shown in Listing 2-2, uses the EnumFontFamilies function in two ways to enumerate all fonts in the system.

Listing 2-2: The FontList program enumerates all fonts in the system.








FontList.h
//================================================================
// Header file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
// Returns number of elements
#define dim(x) (sizeof(x) / sizeof(x[0]))
//----------------------------------------------------------------------
// Generic defines and data types
//
struct decodeUINT { // Structure associates
UINT Code; // messages
// with a function.
LRESULT (*Fxn)(HWND, UINT, WPARAM, LPARAM);
};
struct decodeCMD { // Structure associates
UINT Code; // menu IDs with a
LRESULT (*Fxn)(HWND, WORD, HWND, WORD); // function.
};
//----------------------------------------------------------------------
// Program-specific structures
//
#define FAMILYMAX 24
typedef struct {
int nNumFonts;
TCHAR szFontFamily[LF_FACESIZE];
} FONTFAMSTRUCT;
typedef FONTFAMSTRUCT *PFONTFAMSTRUCT;
typedef struct {
INT yCurrent;
HDC hdc;
} PAINTFONTINFO;
typedef PAINTFONTINFO *PPAINTFONTINFO;
//----------------------------------------------------------------------
// Function prototypes
//
HWND InitInstance (HINSTANCE, LPWSTR, int);
int TermInstance (HINSTANCE, int);
// Window procedures
LRESULT CALLBACK MainWndProc (HWND, UINT, WPARAM, LPARAM);
// Message handlers
LRESULT DoCreateMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoPaintMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoDestroyMain (HWND, UINT, WPARAM, LPARAM);


FontList.cpp
//======================================================================
// FontList - Lists the available fonts in the system
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include "FontList.h" // Program-specific stuff
//----------------------------------------------------------------------
// Global data
//
const TCHAR szAppName[] = TEXT ("FontList");
HINSTANCE hInst; // Program instance handle
FONTFAMSTRUCT ffs[FAMILYMAX];
INT sFamilyCnt = 0;
// Message dispatch table for MainWindowProc
const struct decodeUINT MainMessages[] = {
WM_CREATE, DoCreateMain,
WM_PAINT, DoPaintMain,
WM_DESTROY, DoDestroyMain,
};
//======================================================================
// Program entry point
//
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow) {
MSG msg;
int rc = 0;
HWND hwndMain;
// Initialize this instance.
hwndMain = InitInstance (hInstance, lpCmdLine, nCmdShow);
if (hwndMain == 0)
return 0x10;
// Application message loop
while (GetMessage (&msg, NULL, 0, 0)) {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
// Instance cleanup
return TermInstance (hInstance, msg.wParam);
}
//----------------------------------------------------------------------
// InitInstance - Instance initialization
//
HWND InitInstance (HINSTANCE hInstance, LPWSTR lpCmdLine, int nCmdShow) {
WNDCLASS wc;
HWND hWnd;
// Save program instance handle in global variable.
hInst = hInstance;
#if defined(WIN32_PLATFORM_PSPC)
// If Pocket PC, allow only one instance of the application.
hWnd = FindWindow (szAppName, NULL);
if (hWnd) {
SetForegroundWindow ((HWND)(((DWORD)hWnd) | 0x01));
return 0;
}
#endif
// Register application main window class.
wc.style = 0; // Window style
wc.lpfnWndProc = MainWndProc; // Callback function
wc.cbClsExtra = 0; // Extra class data
wc.cbWndExtra = 0; // Extra window data
wc.hInstance = hInstance; // Owner handle
wc.hIcon = NULL, // Application icon
wc.hCursor = LoadCursor (NULL, IDC_ARROW);// Default cursor
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL; // Menu name
wc.lpszClassName = szAppName; // Window class name
if (RegisterClass (&wc) == 0) return 0;
// Create main window.
hWnd = CreateWindowEx (WS_EX_NODRAG, // Ex style flags
szAppName, // Window class
TEXT("Font Listing"),// Window title
// Style flags
WS_VISIBLE | WS_CAPTION | WS_SYSMENU,
CW_USEDEFAULT, // x position
CW_USEDEFAULT, // y position
CW_USEDEFAULT, // Initial width
CW_USEDEFAULT, // Initial height
NULL, // Parent
NULL, // Menu, must be null
hInstance, // Application instance
NULL); // Pointer to create
// parameters
// Return fail code if window not created.
if (!IsWindow (hWnd)) return 0;
// Standard show and update calls
ShowWindow (hWnd, nCmdShow);
UpdateWindow (hWnd);
return hWnd;
}
//----------------------------------------------------------------------
// TermInstance - Program cleanup
//
int TermInstance (HINSTANCE hInstance, int nDefRC) {
return nDefRC;
}
//======================================================================
// Font callback functions
//
//----------------------------------------------------------------------
// FontFamilyCallback - Callback function that enumerates the font
// families
//
int CALLBACK FontFamilyCallback (CONST LOGFONT *lplf,
CONST TEXTMETRIC *lpntm,
DWORD nFontType, LPARAM lParam) {
int rc = 1;
// Stop enumeration if array filled.
if (sFamilyCnt >= FAMILYMAX)
return 0;
// Copy face name of font.
lstrcpy (ffs[sFamilyCnt++].szFontFamily, lplf->lfFaceName);
return rc;
}
//----------------------------------------------------------------------
// EnumSingleFontFamily - Callback function that enumerates fonts
//
int CALLBACK EnumSingleFontFamily (CONST LOGFONT *lplf,
CONST TEXTMETRIC *lpntm,
DWORD nFontType, LPARAM lParam) {
PFONTFAMSTRUCT pffs;
pffs = (PFONTFAMSTRUCT) lParam;
pffs->nNumFonts++; // Increment count of fonts in family
return 1;
}
//----------------------------------------------------------------
// PaintSingleFontFamily - Callback function that draws a font
//
int CALLBACK PaintSingleFontFamily (CONST LOGFONT *lplf,
CONST TEXTMETRIC *lpntm,
DWORD nFontType, LPARAM lParam) {
PPAINTFONTINFO ppfi;
TCHAR szOut[256];
INT nFontHeight, nPointSize;
HFONT hFont, hOldFont;
ppfi = (PPAINTFONTINFO) lParam; // Translate lParam into struct
// pointer.
// Create the font from the LOGFONT structure passed.
hFont = CreateFontIndirect (lplf);
// Select the font into the device context.
hOldFont = (HFONT)SelectObject (ppfi->hdc, hFont);
// Compute font size.
nPointSize = (lplf->lfHeight * 72) /
GetDeviceCaps(ppfi->hdc,LOGPIXELSY);
// Format string and paint on display.
wsprintf (szOut, TEXT ("%s Point:%d"), lplf->lfFaceName,
nPointSize);
ExtTextOut (ppfi->hdc, 25, ppfi->yCurrent, 0, NULL,
szOut, lstrlen (szOut), NULL);
// Compute the height of the default font.
nFontHeight = lpntm->tmHeight + lpntm->tmExternalLeading;
// Update new draw point.
ppfi->yCurrent += nFontHeight;
// Deselect font and delete.
SelectObject (ppfi->hdc, hOldFont);
DeleteObject (hFont);
return 1;
}
//================================================================
// Message handling procedures for MainWindow
//
//----------------------------------------------------------------
// MainWndProc - Callback function for application window
//
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
INT i;
//
// Search message list to see if we need to handle this
// message. If in list, call procedure.
//
for (i = 0; i < dim(MainMessages); i++) {
if (wMsg == MainMessages[i].Code)
return (*MainMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
}
return DefWindowProc (hWnd, wMsg, wParam, lParam);
}
//----------------------------------------------------------------------
// DoCreateMain - Process WM_CREATE message for window.
//
LRESULT DoCreateMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
HDC hdc;
INT i, rc;
//Enumerate the available fonts.
hdc = GetDC (hWnd);
rc = EnumFontFamilies ((HDC)hdc, (LPTSTR)NULL,
FontFamilyCallback, 0);
for (i = 0; i < sFamilyCnt; i++) {
ffs[i].nNumFonts = 0;
rc = EnumFontFamilies ((HDC)hdc, ffs[i].szFontFamily,
EnumSingleFontFamily,
(LPARAM)(PFONTFAMSTRUCT)&ffs[i]);
}
ReleaseDC (hWnd, hdc);
return 0;
}
//---------------------------------------------------------------
// DoPaintMain - Process WM_PAINT message for window.
//
LRESULT DoPaintMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
PAINTSTRUCT ps;
RECT rect;
HDC hdc;
TEXTMETRIC tm;
INT nFontHeight, i;
TCHAR szOut[256];
PAINTFONTINFO pfi;
GetClientRect (hWnd, &rect);
hdc = BeginPaint (hWnd, &ps);
// Get the height of the default font.
GetTextMetrics (hdc, &tm);
nFontHeight = tm.tmHeight + tm.tmExternalLeading;
// Initialize struct that is passed to enumerate function.
pfi.yCurrent = rect.top;
pfi.hdc = hdc;
for (i = 0; i < sFamilyCnt; i++) {
// Format output string, and paint font family name.
wsprintf (szOut, TEXT("Family: %s "),
ffs[i].szFontFamily);
ExtTextOut (hdc, 5, pfi.yCurrent, 0, NULL,
szOut, lstrlen (szOut), NULL);
pfi.yCurrent += nFontHeight;
// Enumerate each family to draw a sample of that font.
EnumFontFamilies ((HDC)hdc, ffs[i].szFontFamily,
PaintSingleFontFamily,
(LPARAM)&pfi);
}
EndPaint (hWnd, &ps);
return 0;
}
//----------------------------------------------------------------
// DoDestroyMain - Process WM_DESTROY message for window.
//
LRESULT DoDestroyMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
PostQuitMessage (0);
return 0;
}











Enumerating the different fonts begins when the application is processing the WM_CREATE message in OnCreateMain. Here EnumFontFamilies is called with the FontFamily field set to NULL so that each family will be enumerated. The callback function is FontFamilyCallback, where the name of the font family is copied into an array of strings.

The remainder of the work is performed during the processing of the WM_PAINT message. The OnPaintMain function begins with the standard litany of getting the size of the client area and calling BeginPaint, which returns the handle to the device context of the window. GetTextMetrics is then called to compute the row height of the default font. A loop is then entered in which EnumerateFontFamilies is called for each family name that had been stored during the enumeration process in OnCreateMain. The callback process for this callback sequence is somewhat more complex than the code we've seen so far.

The PaintSingleFontFamily callback procedure, used in the enumeration of the individual fonts, employs the lParam parameter to retrieve a pointer to a PAINTFONTINFO structure defined in lParam pointer, FontList avoids having to declare global variables to communicate with the callback procedure.

The callback procedure next creates the font using the pointer to LOGFONT that was passed to the callback procedure. The new font is then selected into the device context, while the handle to the previously selected font is retained in hOldFont. The point size of the enumerated font is computed using the inverse of the equation mentioned earlier in the chapter. The callback procedure then produces a line of text showing the name of the font family along with the point size of this particular font. Instead of using DrawText, the callback uses ExtTextOut to draw the string.

After displaying the text, the function computes the height of the line of text just drawn using the combination of tmHeight and tmExternalLeading that was provided in the passed TEXTMETRIC structure. The new font is then deselected using a second call to SelectObject, this time passing the handle to the font that was the original selected font. The new font is then deleted using DeleteObject. Finally, the callback function returns a nonzero value to indicate to Windows that it is okay to make another call to the enumerate callback.

Figure 2-4 shows the Font Listing window. Notice that the font names are displayed in that font and that each font has a specific set of available sizes.


Figure 2-4: The Font Listing window shows some of the available fonts for a Handheld PC.

Unfinished Business


If you look closely at Chapter 4, I'll hold off describing how to properly implement the solution until then.

/ 169