Win32 Series - The Common Dialog Boxes

 

http://www-user.tu-chemnitz.de/~heha/petzold/

 

The Common Dialog Boxes

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.

POPPAD Revisited

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

POPPAD.ICO

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 hwndOwnerlpstrFile, 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.

Unicode File I/O

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.

Changing the Font

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.

Search and Replace

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.

The One-Function-Call Windows Program

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 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.

 

你可能感兴趣的:(Win32 Series - The Common Dialog Boxes)