10.9.9 Adding Helper Functions

To support the EditFile application, you must add several functions to your C-language source file:

Function Description

CheckFileName Checks a filename for wildcards, adds the default filename extension if one is needed, and checks for the existence of the file.
SaveFile Saves the contents of the editing buffer in a file.
QuerySaveFile Prompts the user to save changes if the file has changed without having been saved.
SetNewBuffer Frees the existing editing buffer and replaces it with a new one.

The CheckFileName function verifies that a filename is not empty and that it contains no wildcards. It also checks to see whether the file already exists by using the OpenFile function and the OF_EXIST option. If the file exists, CheckFileName prompts the user to see whether the file should be overwritten. To create this function, add the following statements:

BOOL CheckFileName(hWnd, pDest, pSrc)
HWND hWnd;
PSTR pDest, pSrc;
{
    PSTR pTmp;

    if (!pSrc[0])
        return FALSE;  /* indicates no filename specified */

    pTmp = pSrc;
    while (*pTmp) {      /* searches string for wildcards   */
        switch (*pTmp++) {
            case '*':
            case '?':
                MessageBox(hWnd, "Wildcards not allowed.",
                    NULL, MB_OK | MB_ICONEXCLAMATION);
                return FALSE;
        }
    }

    AddExt(pSrc, DefExt);  /* adds default extension if needed */

    if (OpenFile(pSrc, &OfStruct, OF_EXIST) >= 0) {
        sprintf(str, "Replace existing %s?", pSrc);
        if (MessageBox(hWnd, str, "EditFile",
            MB_OKCANCEL | MB_ICONHAND) == IDCANCEL);
            return FALSE;
    }
    lstrcpy(pDest, pSrc);
    return TRUE;
}

To open a file for writing, the SaveFile function uses the OF_CREATE option of the OpenFile function. The OF_CREATE option directs OpenFile to delete the existing contents of the file. The SaveFile function then retrieves a file-buffer handle from the edit control, locks the buffer, and copies the contents to the file. To create this function, add the following statements:

BOOL SaveFile(hWnd)
HWND hWnd;
{
    int IOStatus;           /* result of file write */

    if ((hFile = OpenFile(FileName, &OfStruct,
            OF_PROMPT | OF_CANCEL | OF_CREATE)) < 0) {
        sprintf(str, "Cannot write to %s.", FileName);
        MessageBox(hWnd, str, NULL, MB_OK | MB_ICONEXCLAMATION);
        return FALSE;
    }

    hEditBuffer = SendMessage(hEditWnd, EM_GETHANDLE, 0, 0L);
    pEditBuffer = LocalLock(hEditBuffer);
    hSaveCursor = SetCursor(hHourGlass);
    IOStatus = _lwrite(hFile, pEditBuffer, strlen(pEditBuffer));
    _lclose(hFile);
    SetCursor(hSaveCursor);

    if (IOStatus != strlen(pEditBuffer)) {
        sprintf(str, "Error writing to %s.", FileName);
        MessageBox(hWnd, str, NULL, MB_OK | MB_ICONHAND);
        fSuccess = FALSE;
    }

    else {
        fSuccess = TRUE;    /* indicates file was saved          */
        fChanges = FALSE;   /* indicates changes have been saved */
    }

    LocalUnlock(hEditBuffer);
    return fSuccess;
}

The EM_GETHANDLE message, sent by using the SendMessage function, directs the edit control to return the handle of its editing buffer. This buffer is located in local memory, so it is locked by using the LocalLock function. Once this buffer is locked, its contents are written to the file by using the _lwrite function. The SetCursor function displays the hourglass cursor to indicate a lengthy operation. If _lwrite fails to write all bytes, the SaveFile function displays a message box. The LocalUnlock function unlocks the editing buffer before the SaveFile function returns.

The QuerySaveFile function checks for changes to the file and prompts the user to save or delete the changes, or cancel the operation. If the user wants to save the changes, the function prompts the user for a filename by using the SaveAs dialog box. To create this function, add the following statements:

BOOL QuerySaveFile(hWnd)
HWND hWnd;
{
    int Response;
    FARPROC lpSaveAsDlg;

    if (fChanges) {
        sprintf(str, "Save current changes: %s", FileName);
        Response = MessageBox(hWnd, str,
            "EditFile",  MB_YESNOCANCEL | MB_ICONEXCLAMATION);
        if (Response == IDYES) {

check_name:
            if (!FileName[0]) {
                lpSaveAsDlg = MakeProcInstance((FARPROC) SaveAsDlg,
                    hinst);
                Response = DialogBox(hinst, "SaveAs",
                    hWnd, (DLGPROC) lpSaveAsDlg);
                FreeProcInstance(lpSaveAsDlg);

                if (Response == IDOK)
                    goto check_name;

                else
                    return FALSE;
            }
            SaveFile(hWnd);
        }
        else if (Response == IDCANCEL)
            return FALSE;
    }
    else
        return TRUE;
}

The SetNewBuffer function retrieves and frees the editing buffer before allocating and setting a new editing buffer. It then updates the edit control window. To create this function, add the following statements:

void SetNewBuffer(hWnd, hNewBuffer, Title)
HWND hWnd;
HANDLE hNewBuffer;
PSTR Title;
{
    HANDLE hOldBuffer;


    hOldBuffer = SendMessage(hEditWnd, EM_GETHANDLE, 0, 0L);
    LocalFree((HLOCAL) hOldBuffer);
    if (!hNewBuffer)      /* allocates buffer if none exists */
        hNewBuffer = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, 1);

    SendMessage(hEditWnd, EM_SETHANDLE, hNewBuffer, 0L);
    InvalidateRect(hEditWnd, NULL, TRUE);    /* updates buffer */
    UpdateWindow(hEditWnd);
    SetWindowText(hWnd, Title);
    SetFocus(hEditWnd);
    fChanges = FALSE;
}

The new text will not be displayed until the edit control repaints its client area. The InvalidateRect function invalidates part of the edit control's client area. The NULL argument means that the entire control needs repainting, and TRUE specifies that the background should be erased before repainting. All of this prepares the control for painting. The UpdateWindow function causes Windows to send the edit control a WM_PAINT message immediately.