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 in Chapter 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 <windows.h> #include <commdlg.h> #include "resource.h" #define EDITID 1 #define UNTITLED TEXT ("(untitled)") LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ; // Functions in POPFILE.C void PopFileInitialize (HWND) ; BOOL PopFileOpenDlg (HWND, PTSTR, PTSTR) ; BOOL PopFileSaveDlg (HWND, PTSTR, PTSTR) ; BOOL PopFileRead (HWND, PTSTR) ; BOOL PopFileWrite (HWND, PTSTR) ; // Functions in POPFIND.C HWND PopFindFindDlg (HWND) ; HWND PopFindReplaceDlg (HWND) ; BOOL PopFindFindText (HWND, int *, LPFINDREPLACE) ; BOOL PopFindReplaceText (HWND, int *, LPFINDREPLACE) ; BOOL PopFindNextText (HWND, int *) ; BOOL PopFindValidFind (void) ; // Functions in POPFONT.C void PopFontInitialize (HWND) ; BOOL PopFontChooseFont (HWND) ; void PopFontSetFont (HWND) ; void PopFontDeinitialize (void) ; // Functions in POPPRNT.C BOOL PopPrntPrintFile (HINSTANCE, HWND, HWND, PTSTR) ; // Global variables static HWND hDlgModeless ; static TCHAR szAppName[] = TEXT ("PopPad") ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { MSG msg ; HWND hwnd ; HACCEL hAccel ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (hInstance, szAppName) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = szAppName ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, NULL, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, szCmdLine) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; hAccel = LoadAccelerators (hInstance, szAppName) ; while (GetMessage (&msg, NULL, 0, 0)) { if (hDlgModeless == NULL || !IsDialogMessage (hDlgModeless, &msg)) { if (!TranslateAccelerator (hwnd, hAccel, &msg)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } } } return msg.wParam ; } void DoCaption (HWND hwnd, TCHAR * szTitleName) { TCHAR szCaption[64 + MAX_PATH] ; wsprintf (szCaption, TEXT ("%s - %s"), szAppName, szTitleName[0] ? szTitleName : UNTITLED) ; SetWindowText (hwnd, szCaption) ; } void OkMessage (HWND hwnd, TCHAR * szMessage, TCHAR * szTitleName) { TCHAR szBuffer[64 + MAX_PATH] ; wsprintf (szBuffer, szMessage, szTitleName[0] ? szTitleName : UNTITLED) ; MessageBox (hwnd, szBuffer, szAppName, MB_OK | MB_ICONEXCLAMATION) ; } short AskAboutSave (HWND hwnd, TCHAR * szTitleName) { TCHAR szBuffer[64 + MAX_PATH] ; int iReturn ; wsprintf (szBuffer, TEXT ("Save current changes in %s?"), szTitleName[0] ? szTitleName : UNTITLED) ; iReturn = MessageBox (hwnd, szBuffer, szAppName, MB_YESNOCANCEL | MB_ICONQUESTION) ; if (iReturn == IDYES) if (!SendMessage (hwnd, WM_COMMAND, IDM_FILE_SAVE, 0)) iReturn = IDCANCEL ; return iReturn ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static BOOL bNeedSave = FALSE ; static HINSTANCE hInst ; static HWND hwndEdit ; static int iOffset ; static TCHAR szFileName[MAX_PATH], szTitleName[MAX_PATH] ; static UINT messageFindReplace ; int iSelBeg, iSelEnd, iEnable ; LPFINDREPLACE pfr ; switch (message) { case WM_CREATE: hInst = ((LPCREATESTRUCT) lParam) -> hInstance ; // Create the edit control child window hwndEdit = CreateWindow (TEXT ("edit"), NULL, WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_NOHIDESEL | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0, 0, 0, 0, hwnd, (HMENU) EDITID, hInst, NULL) ; SendMessage (hwndEdit, EM_LIMITTEXT, 32000, 0L) ; // Initialize common dialog box stuff PopFileInitialize (hwnd) ; PopFontInitialize (hwndEdit) ; messageFindReplace = RegisterWindowMessage (FINDMSGSTRING) ; DoCaption (hwnd, szTitleName) ; return 0 ; case WM_SETFOCUS: SetFocus (hwndEdit) ; return 0 ; case WM_SIZE: MoveWindow (hwndEdit, 0, 0, LOWORD (lParam), HIWORD (lParam), TRUE) ; return 0 ; case WM_INITMENUPOPUP: switch (lParam) { case 1: // Edit menu // Enable Undo if edit control can do it EnableMenuItem ((HMENU) wParam, IDM_EDIT_UNDO, SendMessage (hwndEdit, EM_CANUNDO, 0, 0L) ? MF_ENABLED : MF_GRAYED) ; // Enable Paste if text is in the clipboard EnableMenuItem ((HMENU) wParam, IDM_EDIT_PASTE, IsClipboardFormatAvailable (CF_TEXT) ? MF_ENABLED : MF_GRAYED) ; // Enable Cut, Copy, and Del if text is selected SendMessage (hwndEdit, EM_GETSEL, (WPARAM) &iSelBeg, (LPARAM) &iSelEnd) ; iEnable = iSelBeg != iSelEnd ? MF_ENABLED : MF_GRAYED ; EnableMenuItem ((HMENU) wParam, IDM_EDIT_CUT, iEnable) ; EnableMenuItem ((HMENU) wParam, IDM_EDIT_COPY, iEnable) ; EnableMenuItem ((HMENU) wParam, IDM_EDIT_CLEAR, iEnable) ; break ; case 2: // Search menu // Enable Find, Next, and Replace if modeless // dialogs are not already active iEnable = hDlgModeless == NULL ? MF_ENABLED : MF_GRAYED ; EnableMenuItem ((HMENU) wParam, IDM_SEARCH_FIND, iEnable) ; EnableMenuItem ((HMENU) wParam, IDM_SEARCH_NEXT, iEnable) ; EnableMenuItem ((HMENU) wParam, IDM_SEARCH_REPLACE, iEnable) ; break ; } return 0 ; case WM_COMMAND: // Messages from edit control if (lParam && LOWORD (wParam) == EDITID) { switch (HIWORD (wParam)) { case EN_UPDATE : bNeedSave = TRUE ; return 0 ; case EN_ERRSPACE : case EN_MAXTEXT : MessageBox (hwnd, TEXT ("Edit control out of space."), szAppName, MB_OK | MB_ICONSTOP) ; return 0 ; } break ; } switch (LOWORD (wParam)) { // Messages from File menu case IDM_FILE_NEW: if (bNeedSave && IDCANCEL == AskAboutSave (hwnd, szTitleName)) return 0 ; SetWindowText (hwndEdit, TEXT ("\0")) ; szFileName[0] = `\0' ; szTitleName[0] = `\0' ; DoCaption (hwnd, szTitleName) ; bNeedSave = FALSE ; return 0 ; case IDM_FILE_OPEN: if (bNeedSave && IDCANCEL == AskAboutSave (hwnd, szTitleName)) return 0 ; if (PopFileOpenDlg (hwnd, szFileName, szTitleName)) { if (!PopFileRead (hwndEdit, szFileName)) { OkMessage (hwnd, TEXT ("Could not read file %s!"), szTitleName) ; szFileName[0] = `\0' ; szTitleName[0] = `\0' ; } } DoCaption (hwnd, szTitleName) ; bNeedSave = FALSE ; return 0 ; case IDM_FILE_SAVE: if (szFileName[0]) { if (PopFileWrite (hwndEdit, szFileName)) { bNeedSave = FALSE ; return 1 ; } else { OkMessage (hwnd, TEXT ("Could not write file %s"), szTitleName) ; return 0 ; } } // fall through case IDM_FILE_SAVE_AS: if (PopFileSaveDlg (hwnd, szFileName, szTitleName)) { DoCaption (hwnd, szTitleName) ; if (PopFileWrite (hwndEdit, szFileName)) { bNeedSave = FALSE ; return 1 ; } else { OkMessage (hwnd, TEXT ("Could not write file %s"), szTitleName) ; return 0 ; } } return 0 ; case IDM_FILE_PRINT: if (!PopPrntPrintFile (hInst, hwnd, hwndEdit, szTitleName)) OkMessage (hwnd, TEXT ("Could not print file %s"), szTitleName) ; return 0 ; case IDM_APP_EXIT: SendMessage (hwnd, WM_CLOSE, 0, 0) ; return 0 ; // Messages from Edit menu case IDM_EDIT_UNDO: SendMessage (hwndEdit, WM_UNDO, 0, 0) ; return 0 ; case IDM_EDIT_CUT: SendMessage (hwndEdit, WM_CUT, 0, 0) ; return 0 ; case IDM_EDIT_COPY: SendMessage (hwndEdit, WM_COPY, 0, 0) ; return 0 ; case IDM_EDIT_PASTE: SendMessage (hwndEdit, WM_PASTE, 0, 0) ; return 0 ; case IDM_EDIT_CLEAR: SendMessage (hwndEdit, WM_CLEAR, 0, 0) ; return 0 ; case IDM_EDIT_SELECT_ALL: SendMessage (hwndEdit, EM_SETSEL, 0, -1) ; return 0 ; // Messages from Search menu case IDM_SEARCH_FIND: SendMessage (hwndEdit, EM_GETSEL, 0, (LPARAM) &iOffset) ; hDlgModeless = PopFindFindDlg (hwnd) ; return 0 ; case IDM_SEARCH_NEXT: SendMessage (hwndEdit, EM_GETSEL, 0, (LPARAM) &iOffset) ; if (PopFindValidFind ()) PopFindNextText (hwndEdit, &iOffset) ; else hDlgModeless = PopFindFindDlg (hwnd) ; return 0 ; case IDM_SEARCH_REPLACE: SendMessage (hwndEdit, EM_GETSEL, 0, (LPARAM) &iOffset) ; hDlgModeless = PopFindReplaceDlg (hwnd) ; return 0 ; case IDM_FORMAT_FONT: if (PopFontChooseFont (hwnd)) PopFontSetFont (hwndEdit) ; return 0 ; // Messages from Help menu case IDM_HELP: OkMessage (hwnd, TEXT ("Help not yet implemented!"), TEXT ("\0")) ; return 0 ; case IDM_APP_ABOUT: DialogBox (hInst, TEXT ("AboutBox"), hwnd, AboutDlgProc) ; return 0 ; } break ; case WM_CLOSE: if (!bNeedSave || IDCANCEL != AskAboutSave (hwnd, szTitleName)) DestroyWindow (hwnd) ; return 0 ; case WM_QUERYENDSESSION : if (!bNeedSave || IDCANCEL != AskAboutSave (hwnd, szTitleName)) return 1 ; return 0 ; case WM_DESTROY: PopFontDeinitialize () ; PostQuitMessage (0) ; return 0 ; default: // Process "Find-Replace" messages if (message == messageFindReplace) { pfr = (LPFINDREPLACE) lParam ; if (pfr->Flags & FR_DIALOGTERM) hDlgModeless = NULL ; if (pfr->Flags & FR_FINDNEXT) if (!PopFindFindText (hwndEdit, &iOffset, pfr)) OkMessage (hwnd, TEXT ("Text not found!"), TEXT ("\0")) ; if (pfr->Flags & FR_REPLACE || pfr->Flags & FR_REPLACEALL) if (!PopFindReplaceText (hwndEdit, &iOffset, pfr)) OkMessage (hwnd, TEXT ("Text not found!"), TEXT ("\0")) ; if (pfr->Flags & FR_REPLACEALL) while (PopFindReplaceText (hwndEdit, &iOffset, pfr)) ; return 0 ; } break ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: return TRUE ; case WM_COMMAND: switch (LOWORD (wParam)) { case IDOK: EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } |
POPFILE.C/*------------------------------------------ POPFILE.C -- Popup Editor File Functions ------------------------------------------*/ #include <windows.h> #include <commdlg.h> static OPENFILENAME ofn ; void PopFileInitialize (HWND hwnd) { static TCHAR szFilter[] = TEXT ("Text Files (*.TXT)\0*.txt\0") \ TEXT ("ASCII Files (*.ASC)\0*.asc\0") \ TEXT ("All Files (*.*)\0*.*\0\0") ; ofn.lStructSize = sizeof (OPENFILENAME) ; ofn.hwndOwner = hwnd ; ofn.hInstance = NULL ; ofn.lpstrFilter = szFilter ; ofn.lpstrCustomFilter = NULL ; ofn.nMaxCustFilter = 0 ; ofn.nFilterIndex = 0 ; ofn.lpstrFile = NULL ; // Set in Open and Close functions ofn.nMaxFile = MAX_PATH ; ofn.lpstrFileTitle = NULL ; // Set in Open and Close functions ofn.nMaxFileTitle = MAX_PATH ; ofn.lpstrInitialDir = NULL ; ofn.lpstrTitle = NULL ; ofn.Flags = 0 ; // Set in Open and Close functions ofn.nFileOffset = 0 ; ofn.nFileExtension = 0 ; ofn.lpstrDefExt = TEXT ("txt") ; ofn.lCustData = 0L ; ofn.lpfnHook = NULL ; ofn.lpTemplateName = NULL ; } BOOL PopFileOpenDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName) { ofn.hwndOwner = hwnd ; ofn.lpstrFile = pstrFileName ; ofn.lpstrFileTitle = pstrTitleName ; ofn.Flags = OFN_HIDEREADONLY | OFN_CREATEPROMPT ; return GetOpenFileName (&ofn) ; } BOOL PopFileSaveDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName) { ofn.hwndOwner = hwnd ; ofn.lpstrFile = pstrFileName ; ofn.lpstrFileTitle = pstrTitleName ; ofn.Flags = OFN_OVERWRITEPROMPT ; return GetSaveFileName (&ofn) ; } BOOL PopFileRead (HWND hwndEdit, PTSTR pstrFileName) { BYTE bySwap ; DWORD dwBytesRead ; HANDLE hFile ; int i, iFileLength, iUniTest ; PBYTE pBuffer, pText, pConv ; // Open the file. if (INVALID_HANDLE_VALUE == (hFile = CreateFile (pstrFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL))) return FALSE ; // Get file size in bytes and allocate memory for read. // Add an extra two bytes for zero termination. iFileLength = GetFileSize (hFile, NULL) ; pBuffer = malloc (iFileLength + 2) ; // Read file and put terminating zeros at end. ReadFile (hFile, pBuffer, iFileLength, &dwBytesRead, NULL) ; CloseHandle (hFile) ; pBuffer[iFileLength] = `\0' ; pBuffer[iFileLength + 1] = `\0' ; // Test to see if the text is Unicode iUniTest = IS_TEXT_UNICODE_SIGNATURE | IS_TEXT_UNICODE_REVERSE_SIGNATURE ; if (IsTextUnicode (pBuffer, iFileLength, &iUniTest)) { pText = pBuffer + 2 ; iFileLength -= 2 ; if (iUniTest & IS_TEXT_UNICODE_REVERSE_SIGNATURE) { for (i = 0 ; i < iFileLength / 2 ; i++) { bySwap = ((BYTE *) pText) [2 * i] ; ((BYTE *) pText) [2 * i] = ((BYTE *) pText) [2 * i + 1] ; ((BYTE *) pText) [2 * i + 1] = bySwap ; } } // Allocate memory for possibly converted string pConv = malloc (iFileLength + 2) ; // If the edit control is not Unicode, convert Unicode text to // non-Unicode (i.e., in general, wide character). #ifndef UNICODE WideCharToMultiByte (CP_ACP, 0, (PWSTR) pText, -1, pConv, iFileLength + 2, NULL, NULL) ; // If the edit control is Unicode, just copy the string #else lstrcpy ((PTSTR) pConv, (PTSTR) pText) ; #endif } else // the file is not Unicode { pText = pBuffer ; // Allocate memory for possibly converted string. pConv = malloc (2 * iFileLength + 2) ; // If the edit control is Unicode, convert ASCII text. #ifdef UNICODE MultiByteToWideChar (CP_ACP, 0, pText, -1, (PTSTR) pConv, iFileLength + 1) ; // If not, just copy buffer #else lstrcpy ((PTSTR) pConv, (PTSTR) pText) ; #endif } SetWindowText (hwndEdit, (PTSTR) pConv) ; free (pBuffer) ; free (pConv) ; return TRUE ; } BOOL PopFileWrite (HWND hwndEdit, PTSTR pstrFileName) { DWORD dwBytesWritten ; HANDLE hFile ; int iLength ; PTSTR pstrBuffer ; WORD wByteOrderMark = 0xFEFF ; // Open the file, creating it if necessary if (INVALID_HANDLE_VALUE == (hFile = CreateFile (pstrFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL))) return FALSE ; // Get the number of characters in the edit control and allocate // memory for them. iLength = GetWindowTextLength (hwndEdit) ; pstrBuffer = (PTSTR) malloc ((iLength + 1) * sizeof (TCHAR)) ; if (!pstrBuffer) { CloseHandle (hFile) ; return FALSE ; } // If the edit control will return Unicode text, write the // byte order mark to the file. #ifdef UNICODE WriteFile (hFile, &wByteOrderMark, 2, &dwBytesWritten, NULL) ; #endif // Get the edit buffer and write that out to the file. GetWindowText (hwndEdit, pstrBuffer, iLength + 1) ; WriteFile (hFile, pstrBuffer, iLength * sizeof (TCHAR), &dwBytesWritten, NULL) ; if ((iLength * sizeof (TCHAR)) != (int) dwBytesWritten) { CloseHandle (hFile) ; free (pstrBuffer) ; return FALSE ; } CloseHandle (hFile) ; free (pstrBuffer) ; return TRUE ; } |
POPFIND.C/*-------------------------------------------------------- POPFIND.C -- Popup Editor Search and Replace Functions --------------------------------------------------------*/ #include <windows.h> #include <commdlg.h> #include <tchar.h> // for _tcsstr (strstr for Unicode & non-Unicode) #define MAX_STRING_LEN 256 static TCHAR szFindText [MAX_STRING_LEN] ; static TCHAR szReplText [MAX_STRING_LEN] ; HWND PopFindFindDlg (HWND hwnd) { static FINDREPLACE fr ; // must be static for modeless dialog!!! fr.lStructSize = sizeof (FINDREPLACE) ; fr.hwndOwner = hwnd ; fr.hInstance = NULL ; fr.Flags = FR_HIDEUPDOWN | FR_HIDEMATCHCASE | FR_HIDEWHOLEWORD ; fr.lpstrFindWhat = szFindText ; fr.lpstrReplaceWith = NULL ; fr.wFindWhatLen = MAX_STRING_LEN ; fr.wReplaceWithLen = 0 ; fr.lCustData = 0 ; fr.lpfnHook = NULL ; fr.lpTemplateName = NULL ; return FindText (&fr) ; } HWND PopFindReplaceDlg (HWND hwnd) { static FINDREPLACE fr ; // must be static for modeless dialog!!! fr.lStructSize = sizeof (FINDREPLACE) ; fr.hwndOwner = hwnd ; fr.hInstance = NULL ; fr.Flags = FR_HIDEUPDOWN | FR_HIDEMATCHCASE | FR_HIDEWHOLEWORD ; fr.lpstrFindWhat = szFindText ; fr.lpstrReplaceWith = szReplText ; fr.wFindWhatLen = MAX_STRING_LEN ; fr.wReplaceWithLen = MAX_STRING_LEN ; fr.lCustData = 0 ; fr.lpfnHook = NULL ; fr.lpTemplateName = NULL ; return ReplaceText (&fr) ; } BOOL PopFindFindText (HWND hwndEdit, int * piSearchOffset, LPFINDREPLACE pfr) { int iLength, iPos ; PTSTR pstrDoc, pstrPos ; // Read in the edit document iLength = GetWindowTextLength (hwndEdit) ; if (NULL == (pstrDoc = (PTSTR) malloc ((iLength + 1) * sizeof (TCHAR)))) return FALSE ; GetWindowText (hwndEdit, pstrDoc, iLength + 1) ; // Search the document for the find string pstrPos = _tcsstr (pstrDoc + * piSearchOffset, pfr->lpstrFindWhat) ; free (pstrDoc) ; // Return an error code if the string cannot be found if (pstrPos == NULL) return FALSE ; // Find the position in the document and the new start offset iPos = pstrPos - pstrDoc ; * piSearchOffset = iPos + lstrlen (pfr->lpstrFindWhat) ; // Select the found text SendMessage (hwndEdit, EM_SETSEL, iPos, * piSearchOffset) ; SendMessage (hwndEdit, EM_SCROLLCARET, 0, 0) ; return TRUE ; } BOOL PopFindNextText (HWND hwndEdit, int * piSearchOffset) { FINDREPLACE fr ; fr.lpstrFindWhat = szFindText ; return PopFindFindText (hwndEdit, piSearchOffset, &fr) ; } BOOL PopFindReplaceText (HWND hwndEdit, int * piSearchOffset, LPFINDREPLACE pfr) { // Find the text if (!PopFindFindText (hwndEdit, piSearchOffset, pfr)) return FALSE ; // Replace it SendMessage (hwndEdit, EM_REPLACESEL, 0, (LPARAM) pfr->lpstrReplaceWith) ; return TRUE ; } BOOL PopFindValidFind (void) { return * szFindText != `\0' ; } |
POPFONT.C/*------------------------------------------ POPFONT.C -- Popup Editor Font Functions ------------------------------------------*/ #include <windows.h> #include <commdlg.h> static LOGFONT logfont ; static HFONT hFont ; BOOL PopFontChooseFont (HWND hwnd) { CHOOSEFONT cf ; cf.lStructSize = sizeof (CHOOSEFONT) ; cf.hwndOwner = hwnd ; cf.hDC = NULL ; cf.lpLogFont = &logfont ; cf.iPointSize = 0 ; cf.Flags = CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS | CF_EFFECTS ; cf.rgbColors = 0 ; cf.lCustData = 0 ; cf.lpfnHook = NULL ; cf.lpTemplateName = NULL ; cf.hInstance = NULL ; cf.lpszStyle = NULL ; cf.nFontType = 0 ; // Returned from ChooseFont cf.nSizeMin = 0 ; cf.nSizeMax = 0 ; return ChooseFont (&cf) ; } void PopFontInitialize (HWND hwndEdit) { GetObject (GetStockObject (SYSTEM_FONT), sizeof (LOGFONT), (PTSTR) &logfont) ; hFont = CreateFontIndirect (&logfont) ; SendMessage (hwndEdit, WM_SETFONT, (WPARAM) hFont, 0) ; } void PopFontSetFont (HWND hwndEdit) { HFONT hFontNew ; RECT rect ; hFontNew = CreateFontIndirect (&logfont) ; SendMessage (hwndEdit, WM_SETFONT, (WPARAM) hFontNew, 0) ; DeleteObject (hFont) ; hFont = hFontNew ; GetClientRect (hwndEdit, &rect) ; InvalidateRect (hwndEdit, &rect, TRUE) ; } void PopFontDeinitialize (void) { DeleteObject (hFont) ; } |
POPPRNT0.C/*--------------------------------------------------------------- POPPRNT0.C -- Popup Editor Printing Functions (dummy version) ---------------------------------------------------------------*/ #include <windows.h> BOOL PopPrntPrintFile (HINSTANCE hInst, HWND hwnd, HWND hwndEdit, PTSTR pstrTitleName) { return FALSE ; } |
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 in Chapter 13 to create the final POPPAD program.
Let's look at POPPAD.C first. POPPAD.C maintains two filename strings: The first, stored in WndProc 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 the OKMessage 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 functions GetOpenFileName and GetSaveFileName. Both of these functions use a structure of type OPENFILENAME, defined in COMMDLG.H. In POPFILE.C, a global variable named ofn 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 and GetSaveFileName 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 calls PopFontChooseFont. This function initializes a CHOOSEFONT structure and then calls ChooseFont 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 and ReplaceText) 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 <windows.h> #include <commdlg.h> int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static CHOOSECOLOR cc ; static COLORREF crCustColors[16] ; cc.lStructSize = sizeof (CHOOSECOLOR) ; cc.hwndOwner = NULL ; cc.hInstance = NULL ; cc.rgbResult = RGB (0x80, 0x80, 0x80) ; cc.lpCustColors = crCustColors ; cc.Flags = CC_RGBINIT | CC_FULLOPEN ; cc.lCustData = 0 ; cc.lpfnHook = NULL ; cc.lpTemplateName = NULL ; return ChooseColor (&cc) ; } |
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 the Flags 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 the ChooseColor function calls DialogBox to display the dialog box, the third parameter to DialogBox 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.