12.8.3 Adding an IDM_PRINT Case

For your application to carry out the printing operation, you must add an IDM_PRINT case to the WM_COMMAND case of the main window procedure. To do this, add the following statements:

case IDM_PRINT:

    hdcPrinter = GetPrinterDC();

    if (!hdcPrinter) {
        sprintf(str, "Cannot print %s", Filename);
        MessageBox(hWnd, str, NULL, MB_OK | MB_ICONHAND);
        break;
    }




    lpAbortDlg =  MakeProcInstance((FARPROC) AbortDlg, hinst);
    lpAbortProc = MakeProcInstance((FARPROC) AbortProc, hinst);

    SetAbortProc(hdcPrinter, (ABORTPROC) lpAbortProc);

    DocInfo.cbSize = sizeof(DOCINFO);
    DocInfo.lpszDocName = "PrntFile text";
    DocInfo.lpszOutput = (LPSTR) NULL;

    if (StartDoc(hdcPrinter, &DocInfo) < 0) {
        MessageBox(hWnd, "Unable to start print job",
            NULL, MB_OK | MB_ICONHAND);
        FreeProcInstance(AbortDlg);
        FreeProcInstance(AbortProc);
        DeleteDC(hdcPrinter);
        break;
    }

    StartPage(hdcPrinter);

    fAbort = FALSE;           /* clears abort flag */
    hAbortDlgWnd = CreateDialog(hinst, "AbortDlg", hWnd,
        (DLGPROC) lpAbortDlg);

    ShowWindow(hAbortDlgWnd, SW_NORMAL);
    UpdateWindow(hAbortDlgWnd);
    EnableWindow(hWnd, FALSE);
    GetTextMetrics(hdcPrinter, &TextMetric);

    LineSpace = TextMetric.tmHeight + TextMetric.tmExternalLeading;
    LinesPerPage = GetDeviceCaps (hdcPrinter, VERTRES) / LineSpace;
    dwLines = SendMessage(hEditWnd, EM_GETLINECOUNT, 0, 0L);
    CurrentLine = 1;

    for (dwIndex = IOStatus = 0; dwIndex < dwLines; dwIndex++) {
        pLine[0] = 128;                  /* maximum buffer size */
        pLine[1] = 0;
        LineLength = SendMessage(hEditWnd, EM_GETLINE,
            (WORD) dwIndex, (LONG) ((LPSTR) pLine));
        TextOut(hdcPrinter, 0, CurrentLine*LineSpace, (LPSTR) pLine,
            LineLength);

        if (++CurrentLine > LinesPerPage ) {
            EndPage(hdcPrinter);
            CurrentLine = 1;
            IOStatus = EndPage(hdcPrinter);
            if (IOStatus < 0 || fAbort)
                break;
            StartPage(hdcPrinter);
        }
    }


    if (IOStatus >= 0 && !fAbort) {
        EndPage(hdcPrinter);
        EndDoc(hdcPrinter);
    }

    EnableWindow(hWnd, TRUE);
    DestroyWindow(hAbortDlgWnd);
    FreeProcInstance(AbortDlg);
    FreeProcInstance(AbortProc);
    DeleteDC(hdcPrinter);
    break;

The locally defined GetPrinterDC function checks the WIN.INI file for the current printer and creates a device context for that printer. If there is not a current printer or the device context cannot be created, the function returns NULL and pro-cessing ends with a warning. Otherwise, the MakeProcInstance function creates procedure-instance addresses for the AbortDlg dialog box procedure and the AbortProc function. The SetAbortProc function sets the abort function. The StartDoc function starts the printing job and sets the printing title (shown in the Print Manager application). If StartDoc fails, the FreeProcInstance function frees the AbortDlg and AbortProc procedure instances and the DeleteDC function deletes the device context before processing ends.

The CreateDialog function creates the AbortDlg dialog box, and the EnableWindow function disables the main window. This prevents users from attempting to work in the main window while printing. Users can, however, continue to work in some other application.

Because the edit control may contain more than one line, you should provide adequate spacing between lines. This keeps one line from overwriting or touching another. The GetTextMetrics function retrieves current font information, such as height and external leading, which can be used to compute adequate line spacing. The height is the maximum height of characters in the font. The external leading is the recommended amount of space, in addition to the height, that should be used to separate lines of text in this font. The line spacing, which is assigned to the LineSpace variable, is the sum of the height and external leading members, TextMetric.tmHeight and TextMetric.tmExternalLeading.

Because the edit control might contain more lines than can fit on a single page, you should determine how many lines can fit on a page and advance to the next page whenever this line limit is reached. An application can use the GetTextMetrics and GetDeviceCaps functions (as shown in the preceding example) to determine how many lines fit on a page.

The TextOut function can print only one line at a time, so a for statement provides the loop required to print more than one line of text. The EM_GETLINECOUNT message, sent to the edit control by using the SendMessage function, retrieves the number of lines to be printed and determines the number of times to loop. On each execution of the loop, the EM_GETLINE message copies the contents of a line from the edit control to the line buffer pLine. The loop counter dwIndex is used with the EM_GETLINE message to specify which line to retrieve from the edit control. The EM_GETLINE message also causes SendMessage to return the length of the line. The length is assigned to the LineLength variable.

After a line has been copied from the edit control, it is printed by using the TextOut function. The product of the variables CurrentLine and LineSpacing determines the y-coordinate of the line on the page. The x-coordinate is set to zero. After a line is printed, the value of the CurrentLine variable is increased by one. If CurrentLine is greater than LinesPerPage, it is time to advance to the next page. Because any text printed beyond the physical bottom of a page is clipped and because there is no automatic page advance, you should keep track of the number of lines printed on a page and call the EndPage function to advance to the next page when necessary. If there are any errors during printing, the EndPage function returns an error value and processing ends.

After all lines in the edit control have been printed, the EndPage function advances the final page and the EndDoc function terminates the print request. The DeleteDC function deletes the printer device context, since it is no longer needed, and the DestroyWindow function destroys the AbortDlg dialog box.