10.9.5 Replacing the WM_COMMAND Case

Replace the WM_COMMAND case so that it processes all File-menu commands except Print. The New command should clear the current filename and empty the edit control if there is any text in it. The Open command should retrieve the selected filename, open the file, and fill the edit control. The Save command should write the contents of the edit control back to the current file. Finally, the Save As command should prompt the user for a filename and write the contents of the edit control.

10.9.5.1 Handling the New Command

If the user chooses the New command and there is text in the current file that has been modified, your application should prompt the user with a message box to determine whether the changes should be saved. Add the following statements to the WM_COMMAND case:

case IDM_NEW:

    if (!QuerySaveFile(hWnd))
        return NULL;

    fChanges = FALSE;
    FileName[0] = 0;
    SetNewBuffer(hWnd, NULL, Untitled);
    break;

The locally defined function QuerySaveFile checks the file for changes and prompts the user to save the changes. If the changes are saved, the filename is cleared and the editing buffer is emptied by the locally defined function SetNewBuffer.

10.9.5.2 Handling the Open Command

If the user chooses the Open command and there is text in the current file that has been modified, your application should prompt the user to determine whether the changes should be saved before opening the new file. Add the following statements to the WM_COMMAND case:

case IDM_OPEN:
    if (!QuerySaveFile(hWnd))
        return NULL;

    lpOpenDlg = MakeProcInstance((FARPROC) OpenDlg, hinst);
    hFile = DialogBox(hinst, "Open", hWnd, (DLGPROC) lpOpenDlg);
    FreeProcInstance(lpOpenDlg);

    if (hFile == -1)
        return NULL;

    hEditBuffer =
        LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT,
            FileStatus.st_size+1);

    if (!hEditBuffer) {
        _lclose(hFile);
        MessageBox(hWnd, "Not enough memory.",
            NULL, MB_OK | MB_ICONHAND);
        return NULL;
    }

    hSaveCursor = SetCursor(hHourGlass);
    pEditBuffer = LocalLock(hEditBuffer);
    IOStatus = read(hFile, pEditBuffer, FileStatus.st_size);
    _lclose(hFile);

    if (IOStatus != FileStatus.st_size) {
        sprintf(str, "Error reading %s.", FileName);
        SetCursor(hSaveCursor);          /* removes hourglass */
        MessageBox(hWnd, str, NULL,
            MB_OK | MB_ICONEXCLAMATION);
    }

    LocalUnlock(hEditBuffer);
    sprintf(str, "EditFile - %s", FileName);
    SetNewBuffer(hWnd, hEditBuffer, str);
    SetCursor(hSaveCursor);              /* restores cursor   */
    break;

When the IDM_OPEN case is processed, the QuerySaveFile function checks the existing file for changes before displaying the Open dialog box. The DialogBox function returns a file handle of the open file. This handle is created in the OpenDlg dialog box procedure. If the file cannot be opened, the function returns NULL and processing ends. Otherwise, the LocalAlloc function allocates the space necessary to load the file into memory. The amount of space allocated is determined by the FileStatus structure, which is filled with information about the open file by the OpenDlg dialog box procedure. If there is no available memory, a message box is displayed and processing ends. Otherwise, the SetCursor function displays the hourglass cursor, the LocalLock function locks the new buffer, and the C run-time read function copies the contents of the file into memory. If the file was not read completely, a message box is displayed. SetCursor restores the cursor before the MessageBox function is called. The LocalUnlock function unlocks the editing buffer, and after a new window title is created, SetNewBuffer changes the editing buffer and title.

10.9.5.3 Handling the Save Command

If the user chooses the Save command and there is no current filename, the application should carry out the same action as the Save As command. Add the following statements to the WM_COMMAND case:

case IDM_SAVE:
    if (!FileName[0])
        goto saveas;

    if (fChanges)
        SaveFile(hWnd);

    break;

The IDM_SAVE case checks for a filename and, if none exists, skips to the IDM_SAVEAS case. If a filename does exist, the locally defined SaveFile function saves the file only if changes have been made to it.

10.9.5.4 Handling the Save As Command

The Save As command should always prompt for a filename. You should save the file only if the user gives a valid filename. Add the following statements to the WM_COMMAND case:

case IDM_SAVEAS:
saveas:
    lpSaveAsDlg = MakeProcInstance((FARPROC) SaveAsDlg, hinst);
    Success = DialogBox(hinst, "SaveAs", hWnd, (DLGPROC) lpSaveAsDlg);
    FreeProcInstance(lpSaveAsDlg);

    if (Success == IDOK) {
        sprintf(str, "EditFile - %s", FileName);
        SetWindowText(hWnd, str);
        SaveFile(hWnd);
    }
    break;                        /* user canceled */

The DialogBox function displays the SaveAs dialog box. The MakeProcInstance and FreeProcInstance functions create and free the procedure-instance address for the SaveAsDlg dialog box procedure. The DialogBox function returns IDOK from SaveAsDlg if the user enters a valid filename. The SetWindowText function then changes the window title, and the SaveFile function saves the contents of the editing buffer to the file.

10.9.5.5 Handling the Exit Command

The Exit command should now prompt the user to determine whether the current file should be saved. Also, to keep track of the changes to the file, your application should process notification messages from the edit control window. Modify the IDM_EXIT case and add the IDC_EDIT case to the WM_COMMAND case, as follows:

case IDM_EXIT:
    QuerySaveFile(hWnd);
    DestroyWindow(hWnd);
    break;

case IDC_EDIT:
    if (HIWORD(lParam) == EN_CHANGE)
        fChanges = TRUE;
    return NULL;