|
|
Constant | Value |
FW_DONTCARE | 0 |
FW_THIN | 100 |
FW_EXTRALIGHT | 200 |
FW_ULTRALIGHT | 200 |
FW_LIGHT | 300 |
FW_NORMAL | 400 |
FW_REGULAR | 400 |
FW_MEDIUM | 500 |
Constant | Value |
FW_SEMIBOLD | 600 |
FW_DEMIBOLD | 600 |
FW_BOLD | 700 |
FW_EXTRABOLD | 800 |
FW_ULTRABOLD | 800 |
FW_BLACK | 900 |
FW_HEAVY | 900 |
The actual interpretation and availability of these weights depend on the font. Some fonts only have FW_NORMAL, FW_REGULAR, and FW_BOLD weights. If you specify FW_DONTCARE, a default weight is used, just as with most of the rest of the arguments.
The sixth argument, bItalic, specifies whether the font is to be italicized. This is a boolean value; 0 indicates that the font is not italicized, and any other value indicates that the font is italicized.
The seventh argument, bUnderline, specifies whether the font is to be underlined. This is also a boolean value; 0 indicates that the font is not underlined, and any other value indicates that the font is underlined.
The eighth argument, cStrikeOut, specifies whether the characters in the font are displayed with a line through the character. This is another boolean value using a non-zero value as TRUE and 0 as FALSE.
The ninth argument, nCharSet, specifies the font's character set. The available constants for this value are listed in Table 7.2.
Constant | Value |
ANSI_CHARSET | 0 |
DEFAULT_CHARSET | 1 |
SYMBOL_CHARSET | 2 |
SHIFTJIS_CHARSET | 128 |
OEM_CHARSET | 255 |
The system on which your application is running might have other character sets, and the OEM character set is system dependent, making it different for systems from different manufacturers. If you are using one of these character sets, it is risky to try to manipulate the strings to be output, so it's best to just pass along the string to be displayed.
The tenth argument, nOutPrecision, specifies how closely the output must match the requested font's height, width, character orientation, escapement, and pitch. The available values for this argument are
The OUT_DEVICE_PRECIS, OUT_RASTER_PRECIS, and OUT_TT_PRECIS values control which font is chosen if there are multiple fonts with the same name. For instance, if you use the OUT_TT_PRECIS value and specify a font with both a TrueType and raster version, then the TrueType version is used. In fact, the OUT_TT_PRECIS value forces the system to use a TrueType font, even when the specified font does not have a TrueType version.
The eleventh argument, nClipPrecision, specifies how to clip characters that are partially outside of the display area. The values for this argument are
These values can be ORed together to specify a combination of clipping techniques.
The twelfth argument, nQuality, specifies the output quality and how carefully the GDI (Graphics Device Interface) must attempt to match the logical font attributes to the physical font output. The available values for this argument are
The thirteenth argument, nPitchAndFamily, specifies the pitch and family of the font. This value consists of two values that are ORed together to create a combination value. The first set of available values is
This value specifies the pitch to be used with the font. The second set of available values specifies the family of fonts to be used. The available values for this portion of the argument are
The font family describes in a general way the appearance of a font. You can use the font family value to choose an alternative font when a specific font does not exist on a system. The final argument, lpszFacename, is a standard C-style string that contains the name of the font to be used. This font name comes from the font information received by the EnumFontFamProc callback function.
Today you will build an application that allows the user to select from a list of available fonts to be displayed. The user will be able to enter some text to be displayed in the selected font, allowing the user to see what the font looks like.
To begin today's application, follow these steps:
FIGURE 7.1. The main dialog layout.
Object | Property | Setting |
Static Text | ID | IDC_STATIC |
|
Caption | &Enter Some Text: |
Edit Box | ID | IDC_ESAMPTEXT |
Static Text | ID | IDC_STATIC |
|
Caption | &Select a Font |
List Box | ID | IDC_LFONTS |
Group Box | ID | IDC_STATIC |
|
Caption | Font Sample |
Static Text | ID | IDC_DISPLAYTEXT |
(inside group box; size to | Caption | Empty string |
fill the group box) |
|
|
Command Button | ID | IDC_EXIT |
|
Caption | E&xit |
Object | Name | Category | Type |
IDC_DISPLAYTEXT | m_ctlDisplayText | Control | CStatic |
|
m_strDisplayText | Value | CString |
IDC_LFONTS | m_ctlFontList | Control | CListBox |
|
m_strFontName | Value | CString |
IDC_ESAMPTEXT | m_strSampText | Value | CString |
To be able to create your list of fonts, you need to add your callback function to get each font list and add it to the list box that you placed on the dialog window. To do this, edit the Day7Dlg.h header file and add the function declaration in Listing 7.1 near the top of the file. This function cannot be added through any of the tools available in Visual C++. You need to open the file and add it yourself.
1: #if _MSC_VER > 1000 2: #pragma once 3: #endif // _MSC_VER > 1000 4: 5: int CALLBACK EnumFontFamProc(LPENUMLOGFONT lpelf, 6: LPNEWTEXTMETRIC lpntm, DWORD nFontType, long lParam); 7: 8: //////////////////////////////////////////////////////////////////// 9: // CDay7Dlg dialog 10: 11: class CDay7Dlg : public CDialog 12: . 13: .
14: .
Once you add the function declaration to the header file, open the Day7Dlg.cpp source-code file, scroll to the bottom of the file, and add the function definition in Listing 7.2.
1: int CALLBACK EnumFontFamProc(LPENUMLOGFONT lpelf, 2: LPNEWTEXTMETRIC lpntm, DWORD nFontType, long lParam) 3: { 4: // Create a pointer to the dialog window 5: CDay7Dlg* pWnd = (CDay7Dlg*) lParam; 6: 7: // Add the font name to the list box 8: pWnd->m_ctlFontList.AddString(lpelf->elfLogFont.lfFaceName); 9: // Return 1 to continue font enumeration 10: return 1; 11: }
Now that you have the callback function defined, you need to add a function to request the list of fonts from the operating system. To add this function, follow these steps:
1: void CDay7Dlg::FillFontList() 2: { 3: LOGFONT lf; 4: 5: // Initialize the LOGFONT structure 6: lf.lfCharSet = DEFAULT_CHARSET; 7: strcpy(lf.lfFaceName, ""); 8: // Clear the list box 9: m_ctlFontList.ResetContent(); 10: // Create a device context variable 11: CClientDC dc (this); 12: // Enumerate the font families 13: ::EnumFontFamiliesEx((HDC) dc, &lf, 14: (FONTENUMPROC) EnumFontFamProc, (LPARAM) this, 0); 15: }
1: BOOL CDay7Dlg::OnInitDialog() 2: { 3: CDialog::OnInitDialog(); 4: . 5: . 6: . 7: // TODO: Add extra initialization here 8: 9: /////////////////////// 10: // MY CODE STARTS HERE 11: /////////////////////// 12: 13: // Fill the font list box 14: FillFontList(); 15: 16: /////////////////////// 17: // MY CODE ENDS HERE 18: /////////////////////// 19: 20: return TRUE; // return TRUE unless you set the focus to a control 21: }
If you compile and run your application now, you should find that your list box is filled with the names of all the fonts available on the system. However, there's one aspect of this list that you probably don't want in your application. Figure 7.2 shows many duplicate entries in the list of fonts in the list box. It would be nice if you could eliminate these duplicates and have only one line per font.
FIGURE 7.2. Listing all the fonts in the system.
It turns out that the EnumFontFamiliesEx function call is synchronous in nature. This means that it doesn't return until all the fonts in the system are listed in calls to your callback function. You can place code in the FillFontList function to remove all the duplicate entries once the list box is filled. To do this, modify the FillFontList function as in Listing 7.5.
Listing 7.5. The modified FILLFONTLIST function.
1: void CDay7Dlg::FillFontList() 2: { 3: int iCount; // The number of fonts 4: int iCurCount; // The current font 5: CString strCurFont; // The current font name 6: CString strPrevFont = ""; // The previous font name 7: LOGFONT lf; 8: 9: // Initialize the LOGFONT structure 10: lf.lfCharSet = DEFAULT_CHARSET; 11: strcpy(lf.lfFaceName, ""); 12: // Clear the list box 13: m_ctlFontList.ResetContent(); 14: // Create a device context variable 15: CClientDC dc (this); 16: // Enumerate the font families 17: ::EnumFontFamiliesEx((HDC) dc, &lf, 18: (FONTENUMPROC) EnumFontFamProc, (LPARAM) this, 0); 19: // Get the number of fonts in the list box 20: iCount = m_ctlFontList.GetCount(); 21: // Loop from the last entry in the list box to the first, 22: // searching for and deleting the duplicate entries 23: for (iCurCount = iCount; iCurCount > 0; iCurCount--) 24: { 25: // Get the current font name 26: m_ctlFontList.GetText((iCurCount - 1), strCurFont); 27: // Is it the same as the previous font name? 28: if (strCurFont == strPrevFont) 29: { 30: // If yes, then delete it 31: m_ctlFontList.DeleteString((iCurCount - 1)); 32: } 33: // Set the previous font name to the current font name 34: strPrevFont = strCurFont; 35: } 36: }
Notice that the for loop started at the end of the list and worked backward. This allowed you to delete the current entry without worrying about manipulating the loop counter to prevent skipping lines in the list box. If you compile and run your application, there shouldn't be any duplicate entries in the list of available fonts.
Before you can display the font for the user, you need to place some text into the display area. The edit box near the top of the dialog is where the user enters text to be displayed in the font selected. To add the functionality, do the following:
1: BOOL CDay7Dlg::OnInitDialog() 2: { 3: CDialog::OnInitDialog(); 4: . 5: . 6: . 7: // TODO: Add extra initialization here 8: 9: /////////////////////// 10: // MY CODE STARTS HERE 11: /////////////////////// 12: 13: // Fill the font list box 14: FillFontList(); 15: 16: // Initialize the text to be entered 17: m_strSampText = "Testing"; 18: // Copy the text to the font sample area 19: m_strDisplayText = m_strSampText; 20: // Update the dialog 21: UpdateData(FALSE); 22: 23: /////////////////////// 24: // MY CODE ENDS HERE 25: /////////////////////// 26: 27: return TRUE; // return TRUE unless you set the focus to a control 28: }
1: void CDay7Dlg::OnChangeEsamptext()
2: { 3: // TODO: If this is a RICHEDIT control, the control will not 4: // send this notification unless you override the ÂCDialog::OnInitialUpdate() 5: // function and call CRichEditCrtl().SetEventMask() 6: // with the EN_CHANGE flag ORed into the mask. 7: 8: // TODO: Add your control notification handler code here 9: 10: /////////////////////// 11: // MY CODE STARTS HERE 12: /////////////////////// 13: 14: // Update the variables with the dialog controls 15: UpdateData(TRUE); 16: 17: // Copy the current text to the font sample 18: m_strDisplayText = m_strSampText; 19: 20: // Update the dialog with the variables 21: UpdateData(FALSE); 22: 23: /////////////////////// 24: // MY CODE ENDS HERE 25: /////////////////////// 26: }
If you compile and run your application, you should be able to type text into the edit box and see it change in the font display area in the group box below.
Before you can start changing the font for the display area, you'll need to have a CFont member variable of the dialog class that you can use to set and change the display font. To add this variable, follow these steps:
When adding the code to use the selected font, you'll add it as a separate function that is not attached to a control. Why you do this will become clear as you proceed further through building and running today's application. To add the function to display and use the selected font, follow these steps:
1: void CDay7Dlg::SetMyFont() 2: { 3: CRect rRect; // The rectangle of the display area 4: int iHeight; // The height of the display area 5: 6: // Has a font been selected? 7: if (m_strFontName != "") 8: { 9: // Get the dimensions of the font sample display area 10: m_ctlDisplayText.GetWindowRect(&rRect); 11: // Calculate the area height 12: iHeight = rRect.top - rRect.bottom; 13: // Make sure the height is positive 14: if (iHeight < 0) 15: iHeight = 0 - iHeight; 16: // Release the current font 17: m_fSampFont.Detach(); 18: // Create the font to be used 19: m_fSampFont.CreateFont((iHeight - 5), 0, 0, 0, FW_NORMAL, 20: 0, 0, 0, DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, 21: CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 22: FF_DONTCARE, m_strFontName); 23: 24: // Set the font for the sample display area 25: m_ctlDisplayText.SetFont(&m_fSampFont); 26: } 27: }
1: void CDay7Dlg::OnSelchangeLfonts() 2: { 3: // TODO: Add your control notification handler code here 4: 5: /////////////////////// 6: // MY CODE STARTS HERE
7: ///////////////////////
8: 9: // Update the variables with the dialog controls 10: UpdateData(TRUE); 11: 12: // Set the font for the sample 13: SetMyFont(); 14: 15: /////////////////////// 16: // MY CODE ENDS HERE 17: /////////////////////// 18: }
In the SetMyFont function, you first checked to make sure that a font had been selected. Next, you retrieved the area of the static text control that will be used to display the font. This enables you to specify a font height just slightly smaller than the height of the area you have available to display the font in. After you calculated the height of the static text control and made sure that it is a positive value, you created the selected font and told the static text control to use the newly created font.
In the OnSelchangeLfonts function, you copy the control values to the attached variables and then call the SetMyFont function to use the selected font. If you compile and run your application, you should be able to select a font and see it displayed in the sample static text control, as in Figure 7.3.
FIGURE 7.3. Displaying the selected font.
Today you learned how to use fonts in Visual C++ applications. You learned how to get a list of the available fonts that are loaded on the system and then how to create a font for use on a display object. You learned how you can create and use callback functions to get a list of resources from the Windows operating system. You also learned how you can access controls from the callback function using a window pointer that you passed to the function requesting the resource list.
1: void CDay7Dlg::SetMyFont() 2: { 3: 4: // Has a font been selected? 5: if (m_strFontName != "") 6: { 7: // Assume that the font size has already been initialized in the 8: // m_lLogFont structure. This allows you to only have to specify 9: // the font name. 10: tcscpy(m_lLogFont.lfFaceName, m_strFontName); 11: // Create the font to be used 12: m_fSampFont.CreateFontIndirect(&m_lLogFont); 13: 14: // Set the font for the sample display area 15: m_ctlDisplayText.SetFont(&m_fSampFont); 16: } 17: }
int CALLBACK EnumFontFamProc(LPENUMLOGFONT lpelf, LPNEWTEXTMETRIC lpntm, DWORD nFontType, long lParam) { // Create a pointer to the dialog window CDay7Dlg* pWnd = (CDay7Dlg*) lParam; // Limit the list to TrueType fonts if ((nFontType & TRUETYPE_FONTTYPE) == TRUETYPE_FONTTYPE) { // Add the font name to the list box pWnd->m_ctlFontList.AddString( lpelf->elfLogFont.lfFaceName); } // Return 1 to continue font enumeration return 1; }
The Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. The answers to the quiz questions and exercises are provided in Appendix B, "Answers."
FIGURE 7.4. Displaying the selected font with the font name.
FIGURE 7.5. Displaying the selected font in italics.
Well, you've made it through the first week. By this point, you've gotten a good taste for what's possible when building applications with Visual C++. Now it's time to look back over what's been covered and what you should have learned up to this point.
What you might want to do at this point, to cement your understanding of how you can use these elements in your own applications, is to try designing and building a couple of simple applications of your own. You can use a variety of controls and add some additional dialogs, just so you can make sure that you do understand and are comfortable with these topics. In fact, you might want to try out all the topics that I've covered up to this point in small applications of your own design. That's the true test of your understanding of how the concepts work. You might also want to dive into the MFC documentation to learn a little about some of the more advanced functionality that I haven't covered to see if you can figure out how you can use and incorporate it into your applications.
One of the most important things that you should understand at this point is how you can use controls and dialog windows in your applications to get and display information to the user. This is an important part of any Windows application because just about every application interacts with the user in some way. You should be able to place any of the standard controls on a dialog in your application and be able to incorporate them into your application without any problem. Likewise, you should be comfortable with using the standard message box and dialog windows provided to your application by the Windows operating system. You should also be able to create and incorporate your own custom dialog windows into any application you might want to build. If you don't feel comfortable with any of these topics, you might want to go back and review Day 2 to get a better understanding of how you can use controls and Day 5 to understand how you can incorporate standard and custom dialog windows into your applications.
Another key skill that you will be using in the majority of your applications is the ability to build and incorporate menus into your applications. You need to have a firm understanding of how to design a good menu, how to make sure that there are no conflicting mnemonics, and how you can attach application functionality to the menu selections. At this point, you should be able to create your own customized menus, with entries for each of the various functions that your application performs, and integrate it with your application with no problems. If you aren't 100% comfortable with this topic, you might want to go back and study Day 6 a little more.
You will find that there are various situations in which you need to have some means of triggering actions on a regular basis or in which you need to keep track of how long some process has been running. For both of these situations, as well as numerous others, you'll often find yourself turning to the use of timers in your application. If you are even slightly foggy on how you can integrate timers into your applications, you will definitely want to go back and review Day 4.
Understanding how you can use text and fonts in your applications will allow you to build more flexibility into the appearance of your applications--to give your users the ability to customize the appearance as they want. You will be able to examine the available fonts on the computer on which your application is running and, if a font that you want to use in your application isn't available, choose another font that is close to use instead. If you still have any questions on how the font infrastructure in Windows works and how you can use it in your applications, you'll want to go back and review Day 7 once more.
Depending on the nature of your application, being able to capture and track mouse and keyboard actions by the user can be very important. If you are building a drawing application, this is crucial information. If you are building an application that needs to include drag-and-drop capabilities, this is important once again. There are any number of situations in which you'll want to include this functionality into your applications. By this point, you should understand how you can capture the various mouse events and determine which mouse buttons are involved in the event. You should also be able to capture keyboard events in situations where the keyboard input isn't captured by any controls that are on the window. If you don't feel like you have a complete grasp of this, you should take another look at Day 3.
Finally, you should be familiar with the Visual C++ development environment, the Developer Studio. You should have a good understanding of what each area of the environment is for and how you can use the various tools and utilities in building your applications. You should be comfortable with using the workspace pane to navigate around your application project, locating and bringing into the various editors and designers any part of your application. You should be comfortable with locating and redesigning the icon that will be displayed to represent your application and with finding any member functions or variables in any of your application's classes.
By now you should be getting fairly comfortable working with Visual C++. If you feel like you understand all the topics that I've covered so far, you are ready to continue forward, learning more about the various things that you can do, and functionality that you can build, using Visual C++ as your programming tool. With that said, it's on to the second week...