http://www-user.tu-chemnitz.de/~heha/petzold/
One of the primary goals of Windows when it was initially released was to promote a standardized user interface. For many common menu items, this happened fairly quickly. Almost every software manufacturer adopted the Alt-File-Open selection to open a file. However, the actual file-open dialog boxes were often quite dissimilar.
Beginning with Windows 3.1, a solution to this problem became available. This is an enhancement called the "common dialog box library." This library consists of several functions that invoke standard dialog boxes for opening and saving files, searching and replacing, choosing colors, choosing fonts (all of which I'll demonstrate in this chapter), and printing (which I'll demonstrate inChapter 13).
To use these functions, you basically initialize the fields of a structure and pass a pointer to the structure to a function in the common dialog box library. The function creates and displays the dialog box. When the user makes the dialog box go away, the function you called returns control to your program and you obtain information from the structure you passed to it.
You'll need to include the COMMDLG.H header file in any C source code file that uses the common dialog box library. The common dialog boxes are documented in /Platform SDK/User Interface Services/User Input/Common Dialog Box Library.
When we added a menu to POPPAD in Chapter 10, several standard menu options were left unimplemented. We are now ready to add logic to POPPAD to open files, read them in, and save the edited files on disk. In the process, we'll also add font selection and search-and-replace logic to POPPAD.
The files that contribute to the POPPAD3 program are shown in Figure 11-11.
Figure 11-11. The POPPAD3 program.
POPPAD.C/*--------------------------------------- POPPAD.C -- Popup Editor (c) Charles Petzold, 1998 ---------------------------------------*/ #include |
POPFILE.C/*------------------------------------------ POPFILE.C -- Popup Editor File Functions ------------------------------------------*/ #include |
POPFIND.C/*-------------------------------------------------------- POPFIND.C -- Popup Editor Search and Replace Functions --------------------------------------------------------*/ #include |
POPFONT.C/*------------------------------------------ POPFONT.C -- Popup Editor Font Functions ------------------------------------------*/ #include |
POPPRNT0.C/*--------------------------------------------------------------- POPPRNT0.C -- Popup Editor Printing Functions (dummy version) ---------------------------------------------------------------*/ #include |
POPPAD.RC (excerpts)//Microsoft Developer Studio generated resource script. #include "resource.h" #include "afxres.h" / // Dialog ABOUTBOX DIALOG DISCARDABLE 32, 32, 180, 100 STYLE DS_MODALFRAME | WS_POPUP FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,66,80,50,14 ICON "POPPAD",IDC_STATIC,7,7,20,20 CTEXT "PopPad",IDC_STATIC,40,12,100,8 CTEXT "Popup Editor for Windows",IDC_STATIC,7,40,166,8 CTEXT "(c) Charles Petzold, 1998",IDC_STATIC,7,52,166,8 END PRINTDLGBOX DIALOG DISCARDABLE 32, 32, 186, 95 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "PopPad" FONT 8, "MS Sans Serif" BEGIN PUSHBUTTON "Cancel",IDCANCEL,67,74,50,14 CTEXT "Sending",IDC_STATIC,8,8,172,8 CTEXT "",IDC_FILENAME,8,28,172,8 CTEXT "to print spooler.",IDC_STATIC,8,48,172,8 END / // Menu POPPAD MENU DISCARDABLE BEGIN POPUP "&File" BEGIN MENUITEM "&New\tCtrl+N", IDM_FILE_NEW MENUITEM "&Open...\tCtrl+O", IDM_FILE_OPEN MENUITEM "&Save\tCtrl+S", IDM_FILE_SAVE MENUITEM "Save &As...", IDM_FILE_SAVE_AS MENUITEM SEPARATOR MENUITEM "&Print\tCtrl+P", IDM_FILE_PRINT MENUITEM SEPARATOR MENUITEM "E&xit", IDM_APP_EXIT END POPUP "&Edit" BEGIN MENUITEM "&Undo\tCtrl+Z", IDM_EDIT_UNDO MENUITEM SEPARATOR MENUITEM "Cu&t\tCtrl+X", IDM_EDIT_CUT MENUITEM "&Copy\tCtrl+C", IDM_EDIT_COPY MENUITEM "&Paste\tCtrl+V", IDM_EDIT_PASTE MENUITEM "De&lete\tDel", IDM_EDIT_CLEAR MENUITEM SEPARATOR MENUITEM "&Select All", IDM_EDIT_SELECT_ALL END POPUP "&Search" BEGIN MENUITEM "&Find...\tCtrl+F", IDM_SEARCH_FIND MENUITEM "Find &Next\tF3", IDM_SEARCH_NEXT MENUITEM "&Replace...\tCtrl+R", IDM_SEARCH_REPLACE END POPUP "F&ormat" BEGIN MENUITEM "&Font...", IDM_FORMAT_FONT END POPUP "&Help" BEGIN MENUITEM "&Help", IDM_HELP MENUITEM "&About PopPad...", IDM_APP_ABOUT END END / // Accelerator POPPAD ACCELERATORS DISCARDABLE BEGIN VK_BACK, IDM_EDIT_UNDO, VIRTKEY, ALT, NOINVERT VK_DELETE, IDM_EDIT_CLEAR, VIRTKEY, NOINVERT VK_DELETE, IDM_EDIT_CUT, VIRTKEY, SHIFT, NOINVERT VK_F1, IDM_HELP, VIRTKEY, NOINVERT VK_F3, IDM_SEARCH_NEXT, VIRTKEY, NOINVERT VK_INSERT, IDM_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT VK_INSERT, IDM_EDIT_PASTE, VIRTKEY, SHIFT, NOINVERT "^C", IDM_EDIT_COPY, ASCII, NOINVERT "^F", IDM_SEARCH_FIND, ASCII, NOINVERT "^N", IDM_FILE_NEW, ASCII, NOINVERT "^O", IDM_FILE_OPEN, ASCII, NOINVERT "^P", IDM_FILE_PRINT, ASCII, NOINVERT "^R", IDM_SEARCH_REPLACE, ASCII, NOINVERT "^S", IDM_FILE_SAVE, ASCII, NOINVERT "^V", IDM_EDIT_PASTE, ASCII, NOINVERT "^X", IDM_EDIT_CUT, ASCII, NOINVERT "^Z", IDM_EDIT_UNDO, ASCII, NOINVERT END / // Icon POPPAD ICON DISCARDABLE "poppad.ico" |
RESOURCE.H (excerpts)// Microsoft Developer Studio generated include file. // Used by poppad.rc #define IDC_FILENAME 1000 #define IDM_FILE_NEW 40001 #define IDM_FILE_OPEN 40002 #define IDM_FILE_SAVE 40003 #define IDM_FILE_SAVE_AS 40004 #define IDM_FILE_PRINT 40005 #define IDM_APP_EXIT 40006 #define IDM_EDIT_UNDO 40007 #define IDM_EDIT_CUT 40008 #define IDM_EDIT_COPY 40009 #define IDM_EDIT_PASTE 40010 #define IDM_EDIT_CLEAR 40011 #define IDM_EDIT_SELECT_ALL 40012 #define IDM_SEARCH_FIND 40013 #define IDM_SEARCH_NEXT 40014 #define IDM_SEARCH_REPLACE 40015 #define IDM_FORMAT_FONT 40016 #define IDM_HELP 40017 #define IDM_APP_ABOUT 40018 |
To avoid duplicating source code in Chapter 13, I've added printing to the menu in POPPAD.RC along with some other support.
POPPAD.C contains all the basic source code for the program. POPFILE.C has the code to invoke the File Open and File Save dialog boxes, and it also contains the file I/O routines. POPFIND.C contains the search-and-replace logic. POPFONT.C has the font selection logic. POPPRNT0.C doesn't do much: POPPRNT0.C will be replaced with POPPRNT.C inChapter 13 to create the final POPPAD program.
Let's look at POPPAD.C first. POPPAD.C maintains two filename strings: The first, stored inWndProc using the name szFileName, is the fully qualified drive, path, and filename. The second, stored as szTitleName, is the filename by itself. This is used in the DoCaption function in POPPAD3 to display the filename in the title bar of the window and is used in theOKMessage and AskAboutSave functions to display message boxes to the user.
POPFILE.C contains several functions to display the File Open and File Save dialog boxes and to perform the actual file I/O. The dialog boxes are displayed using the functionsGetOpenFileName and GetSaveFileName. Both of these functions use a structure of type OPENFILENAME, defined in COMMDLG.H. In POPFILE.C, a global variable namedofn is used for this structure. Most of the fields of ofn are initialized in the PopFileInitialize function, which POPPAD.C calls when processing the WM_CREATE message in WndProc.
It's convenient to make ofn a static global structure because GetOpenFileName andGetSaveFileName return some information to the structure that should be used in subsequent calls to these functions.
Although common dialog boxes have a lot of options—including setting your own dialog box template and hooking into the dialog box procedure—my use of the File Open and File Save dialog boxes in POPFILE.C is quite basic. The only fields of the OPENFILENAME structure that are set are lStructSize (the size of the structure), hwndOwner (the dialog box's owner), lpstrFilter (which I'll discuss shortly), lpstrFile and nMaxFile (a pointer to a buffer to receive the fully qualified filename and the size of that buffer), lpstrFileTitle and nMaxFileTitle (a buffer and its size for the filename by itself), Flags (which sets options for the dialog box), and lpstrDefExt (which is set to a text string containing the default filename extension if the user does not specify one when typing a filename in the dialog box).
When the user selects Open from the File menu, POPPAD3 calls POPFILE's PopFileOpenDlg function, passing to it the window handle, a pointer to the filename buffer, and a pointer to the file title buffer. PopFileOpenDlg sets the hwndOwner, lpstrFile, and lpstrFileTitle fields of the OPENFILENAME structure appropriately, sets Flags to OFN_ CREATEPROMPT, and then calls GetOpenFileName, which displays the familiar dialog box shown in Figure 11-12.
Figure 11-12. The File Open dialog box.
When the user ends this dialog box, the GetOpenFileName function returns. The OFN_CREATEPROMPT flag instructs GetOpenFileName to display a message box asking the user whether the file should be created if the selected file does not exist.
The combo box in the lower left corner lists the types of files that will be displayed in the file list. This is known as a "filter." The user can change the filter by selecting another file type from the combo box list. In the PopFileInitialize function in POPFILE.C, I define a filter in the variable szFilter (an array of character strings) for three types of files: text files with the extension .TXT, ASCII files with the extension .ASC, and all files. A pointer to the first string in this array is set to the lpstrFilter field of the OPENFILENAME structure.
If the user changes the filter when the dialog box is active, the nFilterIndex field of OPENFILENAME reflects the user's choice. Because the structure is stored as a static variable, the next time the dialog box is invoked the filter will be set to the selected file type.
The PopFileSaveDlg function in POPFILE.C is similar. It sets the Flags parameter to OFN_OVERWRITEPROMPT and calls GetSaveFileName to invoke the File Save dialog box. The OFN_OVERWRITEPROMPT flag causes a message box to be displayed asking the user whether a file should be overwritten if the selected file already exists.
In many of the programs in this book, you may never notice a difference between the Unicode and non-Unicode versions. In the Unicode version of POPPAD3, for example, the edit control maintains Unicode text and all the common dialog boxes use Unicode text strings. When the program needs to do a search-and-replace, for example, the entire operation is done with Unicode strings with no conversion necessary.
However, POPPAD3 does file I/O, and that means that the program is not entirely self-enclosed. If the Unicode version of POPPAD3 obtains the contents of the edit buffer and writes it out to the disk, that file will be in Unicode. If the non-Unicode version of POPPAD3 reads that file and puts it into its edit buffer, the result will be garbage. The same goes for files saved by the non-Unicode version and read by the Unicode version.
The solution involves identification and conversion. First, in the PopFileWrite function in POPFILE.C, you'll see that the Unicode version of the program writes out the word 0xFEFF at the beginning of the file. This is defined as a byte order mark, indicating that the text file actually contains Unicode text.
Secondly, in the PopFileRead function, the program uses the IsTextUnicode functions to determine whether the file contains the byte order mark. The function even checks to see if the byte order mark is reversed, which means that a Unicode text file was created on a Macintosh or other machine that used the opposite byte order from Intel processors. In this case, every pair of bytes is reversed. If the file is Unicode but it's being read by the non-Unicode version of POPPAD3, then the text is converted by WideCharToMultiChar, which is really a wide-char-to-ANSI function (unless you're running a Far East version of Windows). Only then can the text be put into the edit buffer.
Similarly, if the file is a non-Unicode text file but the Unicode version of the program is running, the text must be converted using MultiCharToWideChar.
We'll be looking at fonts in more detail in Chapter 17, but nothing quite beats the common dialog box functions for choosing fonts.
During the WM_CREATE message, POPPAD calls PopFontInitialize in POPFONT.C. This function obtains a LOGFONT structure based on the system font, creates a font from it, and sends a WM_SETFONT message to the edit control to set a new font. (Although the default edit control font is the system font, the PopFontInitialize function creates a new font for the edit control because eventually the font will be deleted and it wouldn't be wise to delete the stock system font.)
When POPPAD receives a WM_COMMAND message for the program's font option, it callsPopFontChooseFont. This function initializes a CHOOSEFONT structure and then callsChooseFont to display the font selection dialog box. If the user presses the OK button,ChooseFont will return TRUE. POPPAD then calls PopFontSetFont to set the new font in the edit control. The old font is deleted.
Finally, during the WM_DESTROY message, POPPAD calls PopFontDeinitialize to delete the last font that PopFontSetFont created.
The common dialog box library also includes two dialog boxes for the text search and replace functions. These two functions (FindText andReplaceText) use a structure of type FINDREPLACE. The POPFIND.C file, shown in Figure 10-11, has two routines (PopFindFindDlg and PopFindReplaceDlg) to call these functions, and it also has a couple of functions to search through the text in the edit control and to replace text.
There are a few considerations with using the search and replace functions. First, the dialog boxes they invoke are modeless dialog boxes, which means you should alter your message loop to call IsDialogMessage when the dialog boxes are active. Second, the FINDREPLACE structure you pass to FindText and ReplaceText must be a static variable; because the dialog box is modal, the functions return after the dialog box is displayed rather than after it's destroyed. Nevertheless, the dialog box procedure must be able to continue to access the structure.
Third, while the FindText and ReplaceText dialogs are displayed, they communicate with the owner window through a special message. The message number can be obtained by calling the RegisterWindowMessage function with the FINDMSGSTRING parameter. This is done while processing the WM_CREATE message in WndProc, and the message number is stored in a static variable.
While processing the default message case, WndProc compares the message variable with the value returned from RegisterWindowMessage. The lParam message parameter is a pointer to the FINDREPLACE structure, and the Flags field indicates whether the user has used the dialog box to find text or replace text or whether the dialog box is terminating. POPPAD3 calls the PopFindFindText and PopFindReplaceText functions in POPFIND.C to perform the search and replace functions.
So far I've shown two programs that let you view selected colors: COLORS1 in Chapter 9 and COLORS2 in this chapter. Now it's time for COLORS3, a program that makes only one Windows function call. The COLORS3 source code is shown in Figure 11-13.
The one Windows function that COLORS3 calls is ChooseColor, another function in the common dialog box library. It displays the dialog box shown in Figure 11-14. Color selection is similar to that in COLORS1 and COLORS2, but it's somewhat more interactive.
Figure 11-13. The COLORS3 program.
COLORS3.C/*---------------------------------------------- COLORS3.C -- Version using Common Dialog Box (c) Charles Petzold, 1998 ----------------------------------------------*/ #include |
Figure 11-14. The COLORS3 display.
The ChooseColor function uses a structure of type CHOOSECOLOR and an array of 16 DWORDs to store custom colors that the user selects from the dialog box. The rgbResult field can be initialized to a color value that will be displayed if the CC_RGBINIT flag is set in theFlags field. When using the function normally, the rgbResult field will be set to the color that the user selects.
Notice that the hwndOwner field of the Color dialog box is set to NULL. When theChooseColor function calls DialogBox to display the dialog box, the third parameter toDialogBox is also set to NULL. This is perfectly legitimate. It means that the dialog box is not owned by another window. The caption in the dialog box will appear in the Task List, and the dialog box will seem to function much like a normal window.
You can also use this technique with your own dialog boxes in your own programs. It's possible to make a Windows program that creates only a dialog box and does all processing within the dialog box procedure.