Modula-2 home

  Home  
  Tutorial  
  Win32 API  
  Reference  
  Projects  
  Source code  
  Links  
Shows printer-friendly 
version in new window  

 

A customized Windows "Save as" common dialog box

For Stony Brook Modula-2

By Frank Schoonjans (frank.schoonjans@ugent.be)

 

In SPSS (a statistical software package) the following dialog box is displayed when you want to save a graph:

Notice the "Options" button. It allows, amongst others, to select the width and height of the JPG image.

But how did the programmers put this button in this dialog box?

Step 1. The basic Save as dialog box

The standard Windows Save as dialog box is one of the common dialog boxes available from COMMDLG library. It's basic use is illustrated in the following example.

MODULE Basic;

(* Version 1.0 - December 2003
   Frank Schoonjans
   http://www.modula2.org *)

FROM SYSTEM  IMPORT ADR,CAST,ADDRESS,FUNC;
FROM WIN32   IMPORT HWND,UINT,WPARAM,LPARAM,MAX_PATH;
FROM WINUSER IMPORT GetParent,LPNMHDR,WM_INITDIALOG,WM_NOTIFY,
                    MessageBox,MB_OK;
FROM WINX    IMPORT Instance,NULL_HWND;

IMPORT COMMCTRL,COMMDLG,Strings;

TYPE
     String  = ARRAY [0..MAX_PATH] OF CHAR;

CONST
     NIL_ADR = CAST(ADDRESS,0);

PROCEDURE SelectSaveFile(    hWndOwner : HWND;
                         VAR Path      : ARRAY OF CHAR;
                         VAR FileName  : ARRAY OF CHAR;
                         VAR Type      : INTEGER): BOOLEAN;
VAR OpenFile : COMMDLG.OPENFILENAME;
    fname    : String;
    Title    : String;
    filter   : String;
    ext      : ARRAY [0..3] OF CHAR;
BEGIN

    Strings.Assign(FileName,fname);

    filter:= "Windows metafile (*.wmf)"+CHR(0)+"*.wmf"+CHR(0)
            +"Device Independent Bitmap (*.bmp)"+CHR(0)+"*.bmp"+CHR(0)
            +"GIF Graphics Interchange Format (*.gif)"+CHR(0)+"*.gif"+CHR(0)
            +CHR(0);
    ext:="";

    Title:="Export graph";

    WITH OpenFile
    DO lStructSize       := SIZE(OpenFile);
       hwndOwner         := hWndOwner;
       hInstance         := Instance;
       lpstrFilter       := ADR(filter);
       lpstrCustomFilter := NIL_ADR;
       nMaxCustFilter    := 0;
       nFilterIndex      := Type;
       lpstrFile         := ADR(fname);
       nMaxFile          := SIZE(fname);
       lpstrFileTitle    := NIL_ADR;
       nMaxFileTitle     := 0;
       lpstrInitialDir   := ADR(Path);
       lpstrTitle        := ADR(Title);
       nFileOffset       := 0;
       nFileExtension    := 0;
       lpstrDefExt       := ADR(ext);
       lCustData         := 0;

       Flags             := COMMDLG.OFN_HIDEREADONLY;
       lpfnHook          := CAST(COMMDLG.LPOFNHOOKPROC,NIL);

       lpTemplateName    := NIL_ADR;
    END;

    IF ~COMMDLG.GetSaveFileName(OpenFile) THEN RETURN FALSE END;

    Type:=OpenFile.nFilterIndex;
    Strings.Assign(fname,FileName);
    RETURN TRUE
END SelectSaveFile;


VAR Path,FileName : ARRAY [0..255] OF CHAR;
    Type          : INTEGER;
BEGIN
    COMMCTRL.InitCommonControls;
    Path     := "C:\";
    FileName := "";
    Type     := 2;
    FUNC SelectSaveFile(NULL_HWND,Path,FileName,Type);
END Basic.

Basically, the Save as dialog box is obtained by initialising the members of an OPENFILENAME record and calling the GetSaveFileName procedure (both exported from COMMDLG). This results in the following dialog box.

Adding a Help button

The next step is to add a Help button in the dialog. This only requires a minor change in the code above, namely adding the flag OFN_SHOWHELP:

    Flags    := COMMDLG.OFN_HIDEREADONLY BOR COMMDLG.OFN_SHOWHELP;

This gives us a Help button:

Customizing the dialog and the Help button

So now we need to change the text of the help button into "Options...", and add some functionality to the button.

First we add the flags: OFN_EXPLORER and OFN_ENABLEHOOK.

    Flags    := COMMDLG.OFN_HIDEREADONLY BOR COMMDLG.OFN_EXPLORER
                BOR COMMDLG.OFN_ENABLEHOOK BOR COMMDLG.OFN_SHOWHELP;

The OFN_ENABLEHOOK flag enables the hook function specified in the lpfnHook member of the OPENFILENAME record.

    lpfnHook := HookProc;

Our HookProc procedure is listed below.

<*/PUSH*>
<*/CALLS:WIN32SYSTEM*>
PROCEDURE HookProc(hwnd : HWND; uint : UINT; wparam : WPARAM; lparam : LPARAM): UINT [EXPORT];
VAR lpnmhdr    : LPNMHDR;
    str        : String;
    hwndParent : HWND;
BEGIN
    CASE uint OF
    | WM_INITDIALOG :
        hwndParent := GetParent(hwnd);
        str:="&Options...";
        COMMDLG.CommDlg_OpenSave_SetControlText(hwndParent,Dlgs.pshHelp,str);
    | WM_NOTIFY : lpnmhdr:=CAST(LPNMHDR,lparam);
        CASE lpnmhdr^.code OF
        | COMMDLG.CDN_HELP :
            FUNC MessageBox(GetParent(hwnd),"Options button clicked.","Alert",MB_OK);
        ELSE
        END;
    ELSE
    END;
    RETURN 0;
END HookProc;
<*/POP*>

The main problem we had to solve here is to find out the identifier of the Help button, to use as the second parameter in the CommDlg_OpenSave_SetControlText procedure.

We found the following information on msdn.microsoft.com (search for "Open and Save As Dialog Boxes"):

The following table shows the identifiers of the standard controls in the Explorer-style Open and Save As dialog boxes. The identifiers are constants defined in Dlgs.h and Winuser.h.

 

Control identifier  Control description
chx1 The read-only check box
cmb1 Drop-down combo box that displays the list of file type filters
stc2 Label for the cmb1 combo box
cmb2 Drop-down combo box that displays the current drive or folder, and that allows the user to select a drive or folder to open
stc4 Label for the cmb2 combo box
cmb13 Drop-down combo box that displays the name of the current file, allows the user to type the name of a file to open, and select a file that has been opened or saved recently. This is for earlier Explorer-compatible applications without hook or dialog template. Compare with edt1.
edt1 Edit control that displays the name of the current file, or allows the user to type the name of the file to open. Compare with cmb13.
stc3 Label for the cmb13 combo box and the edt1 edit control
lst1 List box that displays the contents of the current drive or folder
stc1 Label for the lst1 list box
IDOK The OK command button (push button)
IDCANCEL The Cancel command button (push button)
pshHelp The Help command button (push button)

 

So we learn that the identifier of the Help button is pshHelp which is defined in Dlgs.h. We did not find a translated header Dlgs.def in the Stony Brook M2 distribution, so we provide a translation in this project download below (the value of pshHelp actually is 040EH).

So here is our customized Save as dialog box:

For illustration, we only display a simple message box when the user clicks Help. The idea is of course to display a new dialog box requesting additional input from the user.

Download

You can download a complete Stony Brook M2 project, including the example source and the Dlgs.def definition module.

Download