|
INPRISE Online And ZD Journals Present:
You’ve seen font-selection toolbars in many Windows programs. These toolbars let you select a font, the font size, and attributes such as bold, italic, or underline. A font toolbar is a great feature for any application that uses fonts. In this article, we’ll demonstrate how to set up a font toolbar. In particular, we’ll show you how to use the API function EnumFontFamilesEx() to fill a combo box with a list of available fonts. The feature set
A font toolbar also might have buttons for setting text alignment to left, right, or centered. Naturally, the toolbar could include other application-specific items as well. The font selection combo box contains a list of installed fonts. This combo box should have the csDropDownList style so users can pick a font name only from the list, and its Sorted property should be true so the list of fonts is alphabetical. (The font name is technically called the typeface, but most folks use the term font.) The question then becomes one of which fonts to display. You can show raster fonts, device fonts (ATM fonts, for example), or TrueType fonts. The font size selection combo box is pretty basic: You simply load it with the font sizes you’d like to support. Although you’ll see all sorts of point sizes listed in different Windows applications, showing the even numbers from 8 to 40 seems to be a sensible approach. This combo box should have the csDropDown style so users can enter any font size in addition to selecting a size from the list. It should also include code that changes the font size when the user presses [Enter]. The font attribute buttons (bold, italic, and underline) should be shown in either an up state or a down state. For example, if the bold style is in effect, then the bold button should appear in the down state. The buttons should operate independently, so that any font attribute can be on or off. Building the font bar Next, place two combo boxes on the form, to display the font name and the font size. Naturally, the combo box for the font name should be wide enough to display the font name in its entirety (a width of 150 should do). The font size combo box can be fairly narrow, since it has to show only three digits. Assign the combo boxes meaningful Name properties—for example, FontCombo and FontSizeCombo. The next step is to add the buttons for selecting font attributes. Place three SpeedButton components on the panel to the right of the font size combo box. Change the captions as required, or create bitmaps for the buttons. In order for the buttons to toggle, you must set their AllowAllUp property to true. Then, set each button’s GroupIndex property to a unique value: 1 for the bold button, 2 for the italic button, and 3 for the underline button, for example. Your form should now resemble the one shown in Figure A. Now you can move on to writing the code that will make the font toolbar useful. Figure A: The font toolbar looks like this up to this point.
Populating the combo boxes EnumFontFamiliesEx() LOGFONT lf; lf.lfCharSet = DEFAULT_CHARSET; strcpy(lf.lfFaceName, ""); lf.lfPitchAndFamily = 0; The lfCharSet member controls the character set whose fonts will be enumerated. In this case, you want all fonts in the default Windows character set. Next, you set the lfFaceName member to an empty string, telling Windows that you want a list of all fonts in the specified character set. If you set lfFaceName to a specific typeface name (Arial, for example) then only the fonts in that font family will be enumerated. Finally, you must set the lfPitchAndFamily member to 0 (except in a couple of special cases we won’t go into here). Having done all of that, you can call EnumFontFamiliesEx() to have Windows enumerate all the fonts: EnumFontFamiliesEx(Canvas->Handle, &lf, (WNDENUMPROC)FontEnumProc, 0, 0); The first parameter takes a handle to a device context; here you use the canvas’s Handle property. The second parameter takes a pointer to the LOGFONT structure you just created. The third parameter takes a pointer to the callback function, which will be called once for each font in the system. The cast to WNDENUMPROC is necessary to get the code to compile (Windows C programmers love that kind of thing). The fourth parameter can be used for any application-specific data you want to pass to the callback function (we won’t use it in our case), and the final parameter is reserved and should always be 0. When you call EnumFontFamiliesEx(), Windows begins enumerating the fonts. The callback function is called once for each installed font. Let’s take a look at the callback function, since that’s where all the action takes place. The callback function The callback function’s declaration looks like this: int CALLBACK EnumFontFamProc( ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *lpntme, long FontType, LPARAM lParam ); As you can see, the function gives you four parameters. The lpelfe parameter is a pointer to a function that contains information about the font (more on that in just a bit). The lpntme parameter is a pointer to a structure that gives you the text metrics for the font; the FontType parameter tells you whether the font is a raster font, a device font, or a TrueType font; and the lParam parameter holds any user-defined data you want to pass to the function. The only parameter we’re concerned with in this case is the lpelfe parameter—a pointer to an instance of the ENUMLOGFONTEX structure. (You could also make use of the FontType parameter if, for example, you wanted to limit the list to TrueType fonts.) This structure contains a data member called elfFullName, which contains the name of the font. You can add that text string directly to the combo box using the Add() method, as follows: int CALLBACK FontEnumProc( ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *lpntme, long FontType, LPARAM lParam) { Form1->FontCombo->Items->Add( (char*)lpelfe->elfFullName); return 1; } This code illustrates how to get the font name and add it to the combo box. In a real-world application, you’ll need to have some method of ensuring that the combo box doesn’t contain duplicate strings. You’ll get duplicate strings because Windows will execute the callback function for each font style in a particular font family. For example the Arial font contains the typeface names Arial Bold, Arial Italic, Arial Bold Italic, and so on. The elfFullName member of the ENUMLOGFONTEX structure provides the font name itself, and the elfStyle member contains the style. If desired, you could build the font list by combining the elfFullName member with the elfStyle member. You might think to set the combo box’s Sorted property to true and the Duplicates property to false, thereby preventing duplicate strings in the combo box. For some odd reason, TComboBox::Items is of type TStrings rather than of type TStringList (as it is with list boxes), so there’s no Duplicates property. Oh, well, I guess we’ll have to do it the hard way. We won’t show the code here, but see Listing B for an example of how to eliminate duplicate strings. After enumerating all of the fonts, EnumFontFamiliesEx() returns and the combo box contains a list of available fonts. Filling the font size combo box for (int i=8;i<40;i+=2) { FontSizeCombo->Items->Add(i); } This code puts font sizes from 8 to 40 in the font size combo box, showing only even font sizes. Some programs use a rather odd algorithm for displaying font sizes in their font size selection combo boxes. You can follow those conventions or you can just go the easy route and use our code. Handling clicks on the font bar The good news is that a single event handler will handle clicks on any font bar component. First, select all the components on the font bar (two combo boxes and three speed buttons). Now, switch to the Events tab in the Object Inspector, type FontBarClick to the right of the OnClick event, and press [Enter]. C++Builder will create an event handler for you, and you can begin typing code. Since you don’t care which component was clicked, you’ll set all the font attributes each time the OnClick handler is called. The main body of the OnClick handler contains this code: Memo->Font->Name = FontCombo->Text; Memo->Font->Size = FontSizeCombo->Text.ToIntDef(10); TFontStyles styles; if (BoldBtn->Down) styles << fsBold; if (ItalicBtn->Down) styles << fsItalic; if (UnderlineBtn->Down) styles << fsUnderline; Memo->Font->Style = styles; You extract the font name from the font combo box and assign it to the Memo font’s Name property. Since Windows gave you the font names, you know that any font the user selects is valid. Next, the code sets the font’s Size property to the value contained in the font size combo box. As you can see, the AnsiString class’s ToIntDef() method converts the text in the combo box to an integer. Remember, the user may type a font size directly rather than choosing a font size from the dropdown list. The ToIntDef() method ensures that if the user types invalid characters, VCL will supply a default value (10) rather than throwing an exception. Finally, you read the states of the three font style buttons and build a TFontStyles set based on which buttons are down. After that, you assign the results of the set to the Style property of the Memo’s font. All
finished? Figure B: Our sample application lets you control the Memo control’s font characteristics.
You’ll see that we’ve added a few niceties to make the toolbar behave properly. For example, the size value changes when the user presses [Enter] in the font size combo box. It’s natural to press [Enter] after typing a new value, but this action will result in an annoying beep and nothing more unless you write code to account for that possibility. As you can see in Listing B, the code simply responds to the OnKeyPress event and converts the [Enter] keypress into a 0, eliminating the beep. After that, you call the FontBarClick() method to update the font. Now that you have the basic idea, it will be relatively simple to add text-alignment buttons next to the font-style buttons. You could add buttons for left-justified, centered, or right-justified text. You’ll have to change the Memo component to a RichEdit component, but for the most part it’s a trivial exercise. One hint: Change the GroupIndex value for the alignment buttons to the same value. Doing so will ensure that only one of the buttons can be down at a time. Also, provide OnClick handlers for each of the buttons. For example, the handler for the Center button would contain just one line of code: RichEdit->Paragraph->Alignment = taCenter; Conclusion
Kent Reisdorph is a senior software engineer at TurboPower Software and a member of TeamB, Borland's volunteer online support group. He's the author of Teach Yourself C++Builder in 21 Days and Teach Yourself C++Builder in 14 Days. You can contact Kent at kentr@turbopower.com. Back to Top Back to 1998 Index of Articles Copyright © 1998, Ziff-Davis Inc. All rights reserved. ZD Journals and the ZD Journals logo are trademarks of Ziff-Davis Inc. Reproduction in whole or in part in any form or medium without express written permission of Ziff-Davis is prohibited. |