
/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples. 
*       Copyright (C) 1993-1995 Microsoft Corporation.
*       All rights reserved. 
*       This source code is only intended as a supplement to 
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the 
*       Microsoft samples programs.
\******************************************************************************/

/****************************** Module Header *******************************
* Module Name: TPRINT.C
*
* Print functions.
*
* Functions:
*
* gtab_print()
* gtab_printsetup()
* gtab_prtwidths()
* gtab_printjob()
* AbortProc()
* AbortDlg()
* gtab_printpage()
* gtab_setrects()
* gtab_printhead()
*
* Comments:
*
* See table.h for interface description.
*
****************************************************************************/

#include <string.h>
#include <windows.h>
#include <commdlg.h>

#include "gutils.h"
#include "gutilsrc.h"
#include "table.h"
#include "tpriv.h"

/* in tpaint.c, calls GetTextExtentPoint */
extern int GetTextExtent(HDC, LPSTR, int);

extern HANDLE hLibInst;

/* function prototypes */
lpTable gtab_printsetup(HWND hwnd, lpTable ptab, HANDLE heap,
        lpPrintContext pcontext);
BOOL gtab_prtwidths(HWND hwnd, lpTable ptab, HANDLE heap, lpPrintContext
        pcontext);
void gtab_printjob(HWND hwnd, lpTable ptab, lpPrintContext pcontext);
int APIENTRY AbortProc(HDC hpr, int code);
int APIENTRY AbortDlg(HWND hdlg, UINT msg, UINT wParam, LONG lParam);
BOOL gtab_printpage(HWND hwnd, lpTable ptab, lpPrintContext pcontext, int page);
void gtab_setrects(lpPrintContext pcontext, LPRECT rcinner, LPRECT rcouter);
void gtab_printhead(HWND hwnd, HDC hdc, lpTable ptab, lpTitle head, int page);


/***************************************************************************
 * Function: gtab_print
 *
 * Purpose:
 *
 * Prints a table.
 */
void
gtab_print(HWND hwnd, lpTable ptab, HANDLE heap, lpPrintContext pcontext)
{
        BOOL fNoContext, fNoMargin, fNoPD;
        lpTable ptab_prt;

        fNoContext = FALSE;
        fNoPD = FALSE;
        fNoMargin = FALSE;

        if (pcontext == NULL) {
                fNoContext = TRUE;
                pcontext = (lpPrintContext) gmem_get(heap,
                        sizeof(PrintContext));
                pcontext->head = pcontext->foot = NULL;
                pcontext->margin = NULL;
                pcontext->pd = NULL;
                pcontext->id = 0;
        }
        if (pcontext->pd == NULL) {
                fNoPD = TRUE;
        }
        if (pcontext->margin == NULL) {
                fNoMargin = TRUE;
        }
        ptab_prt = gtab_printsetup(hwnd, ptab, heap, pcontext);

        if (ptab_prt != NULL) {
                gtab_printjob(hwnd, ptab_prt, pcontext);

                gtab_deltable(hwnd, ptab_prt);
        }
        if (fNoMargin) {
                gmem_free(heap, (LPSTR)pcontext->margin,
                        sizeof(Margin));
                pcontext->margin = NULL;
        }
        if (fNoPD) {
                if (pcontext->pd->hDevMode != NULL) {
                        GlobalFree(pcontext->pd->hDevMode);
                }
                if (pcontext->pd->hDevNames != NULL) {
                        GlobalFree(pcontext->pd->hDevNames);
                }
                gmem_free(heap, (LPSTR) pcontext->pd, sizeof(PRINTDLG));
                pcontext->pd = NULL;
        }
        if (fNoContext) {
                gmem_free(heap, (LPSTR) pcontext, sizeof(PrintContext));
        }
}



/***************************************************************************
 * Function: gtab_printsetup
 *
 * Purpose:
 *
 * Sets up printercontext - builds lpTable for printer, incl. sizing
 * and initialises pcontext fields that may be null.
 */
lpTable
gtab_printsetup(HWND hwnd, lpTable ptab, HANDLE heap, lpPrintContext pcontext)
{
        lpTable pprttab;
        PRINTDLG FAR * pd;
        int ncols, i;
        ColPropsList cplist;

        /* set fields for context that user left null */
        if (pcontext->margin == NULL) {
                pcontext->margin = (lpMargin) gmem_get(heap, sizeof(Margin));
                if (pcontext->margin == NULL) {
                        return(NULL);
                }
                pcontext->margin->left = 10;
                pcontext->margin->right = 10;
                pcontext->margin->top = 15;
                pcontext->margin->bottom = 15;
                pcontext->margin->topinner = 15;
                pcontext->margin->bottominner = 15;
        }

        if (pcontext->pd == NULL) {
                pd = (PRINTDLG FAR *) gmem_get(heap, sizeof(PRINTDLG));
                if (pd == NULL) {
                        return(NULL);
                }
                pcontext->pd = pd;

                pd->lStructSize = sizeof(PRINTDLG);
                pd->hwndOwner = hwnd;
                pd->hDevMode = (HANDLE) NULL;
                pd->hDevNames = (HANDLE) NULL;
                pd->Flags = PD_RETURNDC|PD_RETURNDEFAULT;

                if (PrintDlg(pd) == FALSE) {
                        return(NULL);
                }
        }

        /* now create a Table struct by querying the owner */
        pprttab = (lpTable) gmem_get(heap, sizeof(Table));

        if (pprttab == NULL) {
                return(NULL);
        }
        pprttab->hdr = ptab->hdr;

        /* get the row/column count from owner window */
        if (pcontext->id == 0) {
                pprttab->hdr.id = ptab->hdr.id;
        } else {
                pprttab->hdr.id = pcontext->id;
        }
        pprttab->hdr.props.valid = 0;
        pprttab->hdr.sendscroll = FALSE;
        if (gtab_sendtq(hwnd, TQ_GETSIZE, (long) (LPSTR)&pprttab->hdr) == FALSE) {
                return(NULL);
        }

        /* alloc and init the col data structs */
        ncols = pprttab->hdr.ncols;
        pprttab->pcolhdr = (lpColProps) gmem_get(heap, sizeof(ColProps) * ncols);
        if (pprttab->pcolhdr == NULL) {
                gmem_free(heap, (LPSTR)pprttab, sizeof(Table));
                return(NULL);
        }

        /* init col properties to default */
        for (i=0; i < ncols; i++) {
                pprttab->pcolhdr[i].props.valid = 0;
                pprttab->pcolhdr[i].nchars = 0;
        }
        /* get the column props from owner */
        cplist.plist = pprttab->pcolhdr;
        cplist.id = pprttab->hdr.id;
        cplist.startcol = 0;
        cplist.ncols = ncols;
        gtab_sendtq(hwnd, TQ_GETCOLPROPS, (long) (LPSTR)&cplist);


        pprttab->scrollscale = 1;
        pprttab->pcellpos = (lpCellPos) gmem_get(heap,
                sizeof(CellPos) * ptab->hdr.ncols);
        if (pprttab->pcellpos == NULL) {
                gmem_free(heap, (LPSTR) pprttab->pcolhdr, sizeof(ColProps) * ncols);
                gmem_free(heap, (LPSTR)pprttab, sizeof(Table));
                return(NULL);
        }
                

        pprttab->pdata = NULL;
        pprttab->nlines = 0;

        if (!gtab_prtwidths(hwnd, pprttab, heap, pcontext)) {
                gmem_free(heap, (LPSTR)pprttab->pcellpos,
                        sizeof(CellPos) * ptab->hdr.ncols);
                gmem_free(heap, (LPSTR)pprttab, sizeof(Table));
                return(NULL);
        }
        return(pprttab);
}


/***************************************************************************
 * Function: gtab_prtwidths
 *
 * Purpose:
 *
 * Calc the height/width settings and alloc line data 
 */
BOOL
gtab_prtwidths(HWND hwnd, lpTable ptab, HANDLE heap, lpPrintContext pcontext)
{
        TEXTMETRIC tm;
        int cx, cxtotal, i, curx, cury;
        lpProps hdrprops, cellprops;
        lpCellPos xpos, ypos;
        RECT rcinner, rcouter;

        hdrprops = &ptab->hdr.props;
        GetTextMetrics(pcontext->pd->hDC, &tm);
        ptab->avewidth = tm.tmAveCharWidth;
        ptab->rowheight = tm.tmHeight + tm.tmExternalLeading;
        if (hdrprops->valid & P_HEIGHT) {
                ptab->rowheight = hdrprops->height;
        }

        /* set sizes for headers */
        gtab_setrects(pcontext, &rcinner, &rcouter);

        /* set width/pos for each col. */
        cxtotal = 0;
        curx = rcinner.left;
        for (i = 0; i < ptab->hdr.ncols; i++) {
                cellprops = &ptab->pcolhdr[i].props;
                xpos = &ptab->pcellpos[i];

                if (cellprops->valid & P_WIDTH) {
                        cx = cellprops->width;
                } else if (hdrprops->valid & P_WIDTH) {
                        cx = hdrprops->width;
                } else {
                        cx = ptab->pcolhdr[i].nchars + 1;
                        cx *= ptab->avewidth;
                }
                /* add 2 for intercol spacing */
                cx += 2;

                xpos->size = cx;
                xpos->start = curx + 1;
                xpos->clipstart = xpos->start;
                xpos->clipend = xpos->start + xpos->size - 2;
                xpos->clipend = min(xpos->clipend, rcinner.right);

                cxtotal += xpos->size;
                curx += xpos->size;
        }
        ptab->rowwidth = cxtotal;

        if(pcontext->head != NULL) {
                xpos = &pcontext->head->xpos;
                ypos = &pcontext->head->ypos;

                xpos->start = rcouter.left + 1;
                xpos->clipstart = rcouter.left + 1;
                xpos->clipend = rcouter.right - 1;
                xpos->size = rcouter.right - rcouter.left;

                ypos->start = rcouter.top;
                ypos->clipstart = rcouter.top;
                ypos->clipend = rcinner.top;
                ypos->size = ptab->rowheight;
        }

        if (pcontext->foot != NULL) {
                xpos = &pcontext->foot->xpos;
                ypos = &pcontext->foot->ypos;

                xpos->start = rcouter.left + 1;
                xpos->clipstart = rcouter.left + 1;
                xpos->clipend = rcouter.right - 1;
                xpos->size = rcouter.right - rcouter.left;

                ypos->start = rcouter.bottom - ptab->rowheight;
                ypos->clipstart = rcinner.bottom;
                ypos->clipend = rcouter.bottom;
                ypos->size = ptab->rowheight;
        }

        /* set nr of lines per page */
        ptab->nlines = (rcinner.bottom - rcinner.top) / ptab->rowheight;
        if (!gtab_alloclinedata(hwnd, heap, ptab)) {
                return(FALSE);
        }
        /* set line positions */
        cury = rcinner.top;
        for (i = 0; i < ptab->nlines; i++) {
                ypos = &ptab->pdata[i].linepos;
                ypos->start = cury;
                ypos->clipstart = ypos->start;
                ypos->clipend = ypos->start + ypos->size;
                ypos->clipend = min(ypos->clipend, rcinner.bottom);
                cury += ypos->size;
        }
        return(TRUE);
}


/* static information for this module */
BOOL bAbort;
FARPROC lpAbortProc;
DLGPROC lpAbortDlg;
HWND hAbortWnd;
int npage;
int pages;

/***************************************************************************
 * Function: gtab_printjob
 *
 * Purpose:
 *
 * Sets up print job and dialogs
 */ 
void
gtab_printjob(HWND hwnd, lpTable ptab, lpPrintContext pcontext)
{
        int moveables;
        int endpage;
        int startpage = 1;
        HDC hpr;
        int status;
        HANDLE hcurs;
        static char str[256];
        DOCINFO di;

        hcurs = SetCursor(LoadCursor(NULL, IDC_WAIT));

        moveables = ptab->nlines - ptab->hdr.fixedrows;
        pages = (int) (ptab->hdr.nrows - ptab->hdr.fixedrows + moveables - 1)
                        / moveables;
        endpage = pages;

        if (pcontext->pd->Flags & PD_PAGENUMS) {
                startpage = pcontext->pd->nFromPage;
                endpage = pcontext->pd->nToPage;
        }
        hpr = pcontext->pd->hDC;

        lpAbortDlg = (DLGPROC) MakeProcInstance((WNDPROC) AbortDlg, hLibInst);
        lpAbortProc = (FARPROC) MakeProcInstance((WNDPROC)AbortProc, hLibInst);

        SetAbortProc(hpr, (ABORTPROC) lpAbortProc);

        di.lpszDocName = "Table";
        di.cbSize = lstrlen(di.lpszDocName);
        di.lpszOutput = NULL;

        StartDoc(hpr, &di);

        bAbort = FALSE;

        /* add abort modeless dialog later!! */
        hAbortWnd = CreateDialog(hLibInst, "GABRTDLG", hwnd, lpAbortDlg);
        if (hAbortWnd != NULL) {
                ShowWindow(hAbortWnd, SW_NORMAL);
                EnableWindow(hwnd, FALSE);
        }
        SetCursor(hcurs);


        for (npage = startpage; npage<=endpage; npage++) {
                wsprintf(str, "Page %d of %d pages",  npage, pages);
                SetDlgItemText(hAbortWnd, IDC_LPAGENR, str);
                status = gtab_printpage(hwnd, ptab, pcontext, npage);
                if (status < 0) {
                        AbortDoc(hpr);
                        break;
                }
        }
        if (status >= 0) {
                EndDoc(hpr);
        }
        
        if (hAbortWnd != NULL) {
                EnableWindow(hwnd, TRUE);
                DestroyWindow(hAbortWnd);
        }
        FreeProcInstance((WNDPROC) lpAbortDlg);
        FreeProcInstance(lpAbortProc);

        DeleteDC(hpr);
}

/***************************************************************************
 * Function: AbortProc
 *
 * Purpose:
 *
 * Abort procedure for print job
 */
int APIENTRY
AbortProc(HDC hpr, int code)
{

        MSG msg;

        if (!hAbortWnd) {
                return(TRUE);
        }
        while (!bAbort && PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
                if (!IsDialogMessage(hAbortWnd, &msg)) {
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                }
        }
        return(!bAbort);
}

/***************************************************************************
 * Function: AbortDlg
 *
 * Purpose:
 *
 * Dialog for abort procedure
 */
int APIENTRY
AbortDlg(HWND hdlg, UINT msg, UINT wParam, LONG lParam)
{
        switch(msg) {

        case WM_COMMAND:
                bAbort = TRUE;
                DestroyWindow (hdlg);
                return TRUE;

        case WM_INITDIALOG:
                return TRUE;
        }
        return(FALSE);
}

/***************************************************************************
 * Function: gtab_printpage
 *
 * Purpose:
 *
 * Print a single page. page number is 1-based
 */
BOOL
gtab_printpage(HWND hwnd, lpTable ptab, lpPrintContext pcontext, int page)
{
        HDC hpr;
        int moveables, i;
        int x1, y1, x2, y2;

        hpr = pcontext->pd->hDC;
        StartPage(hpr);

        moveables = ptab->nlines - ptab->hdr.fixedrows;
        ptab->toprow = moveables * (page-1);
        gtab_invallines(hwnd, ptab, ptab->hdr.fixedrows, moveables);

        for (i =0; i < ptab->nlines; i++) {
                gtab_paint(hwnd, hpr, ptab, i);
        }
        if ((ptab->hdr.vseparator) && (ptab->hdr.fixedcols > 0)) {
                x1 = ptab->pcellpos[ptab->hdr.fixedcols -1].clipend+1;
                y1 = ptab->pdata[0].linepos.clipstart;
                y2 = ptab->pdata[ptab->nlines-1].linepos.clipend;
                MoveToEx(hpr, x1, y1, NULL);
                LineTo(hpr, x1, y2);
        }
        if ((ptab->hdr.hseparator) && (ptab->hdr.fixedrows > 0)) {
                y1 = ptab->pdata[ptab->hdr.fixedrows-1].linepos.clipend;
                x1 = ptab->pcellpos[0].clipstart;
                x2 = ptab->pcellpos[ptab->hdr.ncols-1].clipend;
                MoveToEx(hpr, x1, y1, NULL);
                LineTo(hpr, x2, y1);
        }

        if (pcontext->head != NULL) {
                gtab_printhead(hwnd, hpr, ptab, pcontext->head, page);
        }
        if (pcontext->foot != NULL) {
                gtab_printhead(hwnd, hpr, ptab, pcontext->foot, page);
        }

        return(EndPage(hpr));
}


/***************************************************************************
 * Function: gtab_setrects
 *
 * Purpose:
 *
 * Calculate the outline positions in pixels for the headers
 * (outer rect) and for the page itself (inner rect). Based on
 * page size and PrintContext margin info (which is in millimetres).
 */
void
gtab_setrects(lpPrintContext pcontext, LPRECT rcinner, LPRECT rcouter)
{
        HDC hpr;
        int hpixels, hmms;
        int vpixels, vmms;
        int h_pixpermm, v_pixpermm;

        hpr = pcontext->pd->hDC;
        hpixels = GetDeviceCaps(hpr, HORZRES);
        vpixels = GetDeviceCaps(hpr, VERTRES);
        vmms = GetDeviceCaps(hpr, VERTSIZE);
        hmms = GetDeviceCaps(hpr, HORZSIZE);

        h_pixpermm = hpixels / hmms;
        v_pixpermm = vpixels / vmms;

        rcouter->top = (pcontext->margin->top * v_pixpermm);
        rcouter->bottom = vpixels - (pcontext->margin->bottom * v_pixpermm);
        rcouter->left = (pcontext->margin->left * h_pixpermm);
        rcouter->right = hpixels - (pcontext->margin->right * h_pixpermm);

        rcinner->left = rcouter->left;
        rcinner->right = rcouter->right;
        rcinner->top = rcouter->top +
                (pcontext->margin->topinner * v_pixpermm);
        rcinner->bottom = rcouter->bottom -
                (pcontext->margin->bottominner * v_pixpermm);
}


/***************************************************************************
 * Function: gtab_printhead
 *
 * Purpose:
 *
 * Print header information
 */
void
gtab_printhead(HWND hwnd, HDC hdc, lpTable ptab, lpTitle head, int page)
{
        RECT rc, rcbox;
        int i, cx, x, y, tab;
        UINT align;
        LPSTR chp, tabp;
        DWORD fcol, bkcol;
        char str[256];

        rc.top = head->ypos.clipstart;
        rc.bottom = head->ypos.clipend;
        rc.left = head->xpos.clipstart;
        rc.right = head->xpos.clipend;

        /* update page number */
        chp = str;
        for (i = 0; i < lstrlen(head->ptext); i++) {
                switch(head->ptext[i]) {

                case '#':
                        chp += wsprintf(chp, "%d", page);
                        break;
                
                case '$':
                        chp += wsprintf(chp, "%d", pages);
                        break;
                
                default:
                        *chp++ = head->ptext[i];
                        break;
                }
        }
        *chp = '\0';
        chp = str;

        if (head->props.valid & P_ALIGN) {
                align = head->props.alignment;
        } else {
                align = P_LEFT;
        }

        /* set colours if not default */
        if (head->props.valid & P_FCOLOUR) {
                fcol = SetTextColor(hdc, head->props.forecolour);
        }
        if (head->props.valid & P_BCOLOUR) {
                bkcol = SetBkColor(hdc, head->props.backcolour);
        }

        /* calc offset of text within cell for right-align or centering */
        if (align == P_LEFT) {
                cx = ptab->avewidth/2;
        } else {
                cx = LOWORD(GetTextExtent(hdc, chp, lstrlen(chp)));
                if (align == P_CENTRE) {
                        cx = (head->xpos.size - cx) / 2;
                } else {
                        cx = head->xpos.size - cx - (ptab->avewidth/2);
                }
        }
        cx += head->xpos.start;

        /* expand tabs on output */
        tab = ptab->avewidth * 8;
        x = 0;
        y = head->ypos.start;

        for ( ; (tabp = strchr(chp, '\t')) != NULL; ) {
                /* perform output upto tab char */
                ExtTextOut(hdc, x+cx, y, ETO_CLIPPED, &rc, chp, tabp-chp, NULL);
                
                /* advance past the tab */
                x += LOWORD(GetTextExtent(hdc, chp, tabp - chp));
                x = ( (x + tab) / tab) * tab;
                chp = ++tabp;
        }

        /*no more tabs - output rest of string */
        ExtTextOut(hdc, x+cx, y, ETO_CLIPPED, &rc, chp, lstrlen(chp), NULL);

        /* reset colours to original if not default */
        if (head->props.valid & P_FCOLOUR) {
                SetTextColor(hdc, fcol);
        }
        if (head->props.valid & P_BCOLOUR) {
                SetBkColor(hdc, bkcol);
        }

        /* now box cell if marked */
        if (head->props.valid & P_BOX) {
                if (head->props.box != 0) {
                        rcbox.top = head->ypos.start;
                        rcbox.bottom = rcbox.top + head->ypos.size;
                        rcbox.left = head->xpos.start;
                        rcbox.right = rcbox.left + head->xpos.size;
                        gtab_boxcell(hwnd, hdc, &rcbox, &rc, head->props.box);
                }
        }
}
