/*************************************************************************

      File:  PRINT.C

   Purpose:  Routines called to print a DIB.  Uses banding or lets GDI
             do the banding.  Works with 3.0 (i.e. via escapes) or 3.1
             (i.e. via printing APIs).

 Functions:  DIBPrint
             BandDIBToPrinter
             PrintABand
             DeviceSupportsEscape
             TranslatePrintRect
             GetPrinterDC
             PrintAbortProc
             PrintAbortDlg
             DoStartDoc
             DoSetAbortProc
             DoStartPage
             DoEndPage
             DoEndDoc
             FindGDIFunction
             ShowPrintError

  Comments:

   History:   Date      Reason
             6/ 1/91    Created

*************************************************************************/

#include "master.h"


// The following typedef's are for printing functions.  They are defined
//  in PRINT.H (!!!!!!!!!!!!!!!!!!!!!!!?????) included with the 3.1
//  SDK -- as this app is supposed to compile in 3.0, I define them
//  here instead.

typedef struct
   {
   BOOL bGraphics;            // Band includes graphics
   BOOL bText;                // Band includes text.
   RECT GraphicsRect;         // Rectangle to output graphics into.
   }
BANDINFOSTRUCT;


// LPDOCINFO is now defined in 3.1's WINDOWS.H.  We're compiling under
//  both 3.0 and 3.1.  For now, we'll define our own LPDOCINFO here.
//  This is a LESS than adequate solution!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

typedef struct
   {
   short cbSize;
   LPSTR lpszDocName;
   LPSTR lpszOutput;
   }
OURDOCINFO, far * LPOURDOCINFO;



// The following typedef's and string variables are used to link to
//  printing functions on-the-fly in Windows 3.1.  These API are not
//  present in Windows 3.0!  As such, we must use GetModuleHandle() and
//  GetProcAddress() to find and call them.

typedef int (FAR PASCAL *LPSTARTDOC)     (HDC, LPOURDOCINFO);
typedef int (FAR PASCAL *LPSETABORTPROC) (HDC, FARPROC);
typedef int (FAR PASCAL *LPSTARTPAGE)    (HDC);
typedef int (FAR PASCAL *LPENDPAGE)      (HDC);
typedef int (FAR PASCAL *LPENDDOC)       (HDC);


   // The following strings are used to link to function within
   //  GDI on-the-fly.  These functions were added in 3.1.  We
   //  can't call them directly, because that would not allow this
   //  application to run under Windows 3.0.  We, therefore, use
   //  GetModuleHandle()/GetProcAddress() to link to these functions.
   //  See FindGDIFunction() below.

char szGDIModule[]    = "GDI";         // Module name for GDI in Win31.
char szStartDoc[]     = "StartDoc";    // StartDoc() function in GDI.
char szSetAbortProc[] = "SetAbortProc";// SetAbortProc() function in GDI.
char szStartPage[]    = "StartPage";   // StartPage() function in GDI.
char szEndPage[]      = "EndPage";     // EndPage function in GDI.
char szEndDoc[]       = "EndDoc";      // EndDoc function in GDI.




// Globals for this module.

static HWND hDlgAbort    = NULL;        // Handle to abort dialog box.
static char szPrintDlg[] = "PrintDLG";  // Name of Print dialog from .RC
static BOOL bAbort       = FALSE;       // Abort a print operation?
static BOOL gbUseEscapes = TRUE;        // Use Escape() or 3.1 printer APIs?


// Macros

#define ChangePrintPercent(nPct)    SendMessage(hDlgAbort, MYWM_CHANGEPCT, nPct, NULL)



// Function prototypes.

BOOL FAR PASCAL __export PrintAbortProc (HDC hDC, short code);
int  FAR PASCAL __export PrintAbortDlg  (HWND hWnd,
                            unsigned msg,
                                WORD wParam,
                                LONG lParam);

DWORD    BandDIBToPrinter    (HDC hPrnDC,
                            LPSTR lpDIBHdr,
                            LPSTR lpBits,
                           LPRECT lpPrintRect);
DWORD    PrintABand          (HDC hDC,
                           LPRECT lpRectOut,
                           LPRECT lpRectClip,
                             BOOL fDoText,
                             BOOL fDoGraphics,
                            LPSTR lpDIBHdr,
                            LPSTR lpDIBBits);
HDC     GetPrinterDC        (void);
void    TranslatePrintRect  (HDC hDC,
                          LPRECT lpPrintRect,
                            WORD wUnits,
                            WORD cxDIB,
                            WORD cyDIB);
DWORD    DoStartDoc          (HDC hPrnDC, LPSTR lpszDocName);
DWORD    DoEndPage           (HDC hPrnDC);
DWORD    DoSetAbortProc      (HDC hPrnDC,
                          FARPROC lpfnAbortProc);
DWORD    DoStartPage         (HDC hPrnDC);
DWORD    DoEndPage           (HDC hPrnDC);
DWORD    DoEndDoc            (HDC hPrnDC);
FARPROC  FindGDIFunction     (LPSTR lpszFnName);
BOOL     DeviceSupportsEscape(HDC hDC,
                             int nEscapeCode);



//---------------------------------------------------------------------
//
// Function:   DIBPrint
//
// Purpose:    This routine drives the printing operation.  It has the code
//             to handle both banding and non-banding printers.  A banding
//             printer can be distinguished by the GetDeviceCaps() API (see
//             the code below.  On banding devices, must repeatedly call the
//             NEXTBAND escape to get the next banding rectangle to print
//             into.  If the device supports the BANDINFO escape, it should
//             be used to determine whether the band "wants" text or
//             graphics (or both).  On non-banding devices, we can ignore
//             all this and call PrintPage() on the entire page!
//
// Parms:      hDIB        == Handle to global memory with a DIB spec in it.
//                              can be either a Win30 DIB or an OS/2 PM DIB.
//             lpPrintRect == Rect to print (decoded based on next parm)
//             wUnits      == Units lpPrintRect is in (see
//                              TranslatePrintRect()).
//             dwROP       == Raster operation to use.
//!!!!!!!!!!!!!!!!!!!!dwROP isn't used !!!!!!!!!!!!!!!!!!!!!!
//             fBanding    == TRUE when want to do banding (use NEXTBAND).
//
// Returns:   Encoded error value -- bitwise combination of ERR_PRN_*
//             in PRINT.H.  More than one error can be returned --
//             the application can parse the bits in the DWORD returned,
//             or call ShowPrintError() to display all the errors
//             that ocurred.
//
// History:   Date      Reason
//             6/01/91  Created
//            10/26/91  Added error return codes.
//                      Use DeviceSupportsEscape() instead
//                        of QUERYESCSUPPORT.
//            10/29/91  Added the fUse31APIs flag.
//            11/14/91  Added more error checking.
//                      Added lpDocName as a parameter.
//
//---------------------------------------------------------------------

DWORD DIBPrint (HANDLE hDIB,
                LPRECT lpPrintRect,
                  WORD wUnits,
                 DWORD dwROP,
                  BOOL fBanding,
                  BOOL fUse31APIs,
                 LPSTR lpszDocName)
{
   HDC            hPrnDC;
   RECT           rect;
   static FARPROC lpAbortProc;
   static FARPROC lpAbortDlg;
   LPSTR          lpDIBHdr, lpBits;
   DWORD          dwErr = ERR_PRN_NONE;


      // Do some setup (like getting pointers to the DIB and its header,
      //  and a printer DC).  Also, set the global gbUseEscapes to force
      //  using printer escapes or the 3.1 printing API.

   if (!hDIB)
      return ERR_PRN_NODIB;

   gbUseEscapes = !fUse31APIs;
   lpDIBHdr     = GlobalLock (hDIB);
   lpBits       = FindDIBBits (lpDIBHdr);

   if (hPrnDC = GetPrinterDC ())
      {
      SetStretchBltMode (hPrnDC, COLORONCOLOR);
      TranslatePrintRect (hPrnDC,
                          lpPrintRect,
                          wUnits,
                          (WORD) DIBWidth (lpDIBHdr),
                          (WORD) DIBHeight (lpDIBHdr));


         // Initialize the abort procedure.  Then STARTDOC.

      lpAbortProc = MakeProcInstance(PrintAbortProc, hInst);
      lpAbortDlg  = MakeProcInstance(PrintAbortDlg,  hInst);
      hDlgAbort   = CreateDialog(hInst, szPrintDlg, GetFocus (), lpAbortDlg);
      bAbort      = FALSE;

      if (dwErr |= DoSetAbortProc (hPrnDC, lpAbortProc))
         goto PRINTERRORCLEANUP;

      if (dwErr |= DoStartDoc (hPrnDC, lpszDocName))
         goto PRINTERRORCLEANUP;

      if (fBanding)
         dwErr |= BandDIBToPrinter (hPrnDC, lpDIBHdr, lpBits, lpPrintRect);
      else
         {
            // When not doing banding, call PrintABand() to dump the
            //  entire page to the printer in one shot (i.e. give it
            //  a band that covers the entire printing rectangle,
            //  and tell it to print graphics and text).

         rect = *lpPrintRect;

         dwErr |= PrintABand (hPrnDC,
                              lpPrintRect,
                              &rect,
                              TRUE,
                              TRUE,
                              lpDIBHdr,
                              lpBits);


            // Non-banding devices need the NEWFRAME or EndPage() call.

         dwErr |= DoEndPage (hPrnDC);
         }



         // End the print operation.  Only send the ENDDOC if
         //   we didn't abort or error.

      if (!bAbort)
         dwErr |= DoEndDoc (hPrnDC);


         // All done, clean up.

PRINTERRORCLEANUP:
      DestroyWindow (hDlgAbort);

      FreeProcInstance(lpAbortProc);
      FreeProcInstance(lpAbortDlg);

      DeleteDC (hPrnDC);
      }
   else
      dwErr |= ERR_PRN_NODC;

   GlobalUnlock (hDIB);

   return dwErr;
}



//---------------------------------------------------------------------
//
// Function:   BandDIBToPrinter
//
// Purpose:    Repeatedly call the NEXTBAND escape to get the next
//             banding rectangle to print into.  If the device supports
//             the BANDINFO escape, use it to determine whether the band
//             wants text or graphics (or both).  For each band, call
//             PrintABand() to do the actual output.
//
// Parms:      hPrnDC   == DC to printer.
//             lpDIBHdr == Ptr to DIB header (BITMAPINFOHEADER or
//                         BITMAPCOREHEADER)
//             lpBits   == Ptr to DIB's bitmap bits.
//
// Returns:    WORD -- One (or more) of the printer errors defined as
//             ERR_PRN_* in PRINT.H.
//
//             ERR_PRN_NONE (0) if no error.
//
// History:   Date      Reason
//            10/26/91  Chopped out of DIBPrint().
//                      Use DeviceSupportsEscape() instead of
//                        QUERYESCSUPPORT.
//            11/14/91  Added Error return codes ERR_PRN_BANDINFO
//                        and errors from PrintABand.
//            01/22/91  Fixed NEXTBAND error return check (was checking
//                        if != 0, now check if > 0).
//
//---------------------------------------------------------------------

DWORD BandDIBToPrinter (HDC hPrnDC,
                      LPSTR lpDIBHdr,
                      LPSTR lpBits,
                     LPRECT lpPrintRect)
{
   BANDINFOSTRUCT bi;
   BOOL           bBandInfoDevice;
   RECT           rect;
   DWORD          dwError = ERR_PRN_NONE;
   int            nEscRet;


      // All printers should support the NEXTBAND escape -- we'll
      //  check here, just in case, though!

   if (!DeviceSupportsEscape (hPrnDC, NEXTBAND))
      return ERR_PRN_CANTBAND;


      // Check if device supports the BANDINFO escape.  Then setup
      //  the BANDINFOSTRUCT (we'll use the values we put into it
      //  here later even if the device doesn't support BANDINFO).

   bBandInfoDevice = DeviceSupportsEscape (hPrnDC, BANDINFO);
   bi.bGraphics    = TRUE;
   bi.bText        = TRUE;
   bi.GraphicsRect = *lpPrintRect;


      // Enter the banding loop.  For each band, call BANDINFO if
      //  appropriate.  Then call PrintABand() to do the actual
      //  output.  Terminate loop when NEXTBAND returns an empty rect.

   while (((nEscRet = Escape (hPrnDC, NEXTBAND, NULL, NULL, (LPSTR) &rect)) > 0) &&
         !IsRectEmpty (&rect))
      {
      if (bBandInfoDevice)
         if (!Escape (hPrnDC,
                      BANDINFO,
                      sizeof (BANDINFOSTRUCT),
                      (LPSTR) &bi,
                      (LPSTR) &bi))
            dwError |= ERR_PRN_BANDINFO;

      dwError |= PrintABand (hPrnDC,
                             lpPrintRect,
                             &rect,
                             bi.bText,
                             bi.bGraphics,
                             lpDIBHdr,
                             lpBits);
      }

   if (nEscRet <= 0)
      dwError |= ERR_PRN_NEXTBAND;

   return dwError;
}




//---------------------------------------------------------------------
//
// Function:   PrintABand
//
// Purpose:    This routine does ALL output to the printer.  It is driven by
//             BandDIBToPrinter().  It is called for both banding and non-
//             banding printing devices.  lpClipRect contains the rectangular
//             area we should do our output into (i.e. we should clip our
//             output to this area).  The flags fDoText and fDoGraphics
//             should be set appropriately (if we want any text output to
//             the rectangle, set fDoText to true).  Normally these flags
//             are returned on banding devices which support the BANDINFO
//             escape.  On non-banding devices, all output goes to the
//             entire page, so this routine is passes a rectangle for
//             the entire page, and fDoText = fDoGraphics = TRUE.
//
//             This routine is also responsible for doing stretching of
//             the DIB.  As such, the lpRectOut parameter points to a
//             rectangle on the printed page where the entire DIB will
//             fit -- the DIB is stretched appropriately to fit in this
//             rectangle.
//
//             After printing a band, updates the print % shown in the
//             abort dialog box.
//
// Parms:      hDC         == DC to do output into.
//             lpRectOut   == Rectangle on DC DIB should fit in.
//             lpRectClip  == Rectangle to output during THIS call.
//             fDoText     == Output text into this rectangle (unused by DIBView)?
//             fDoGraphics == Output graphics into this rectangle?
//             lpDIBHdr    == Pointer to DIB's header (either a
//                              BITMAPINFOHEADER or a BITMAPCOREHEADER)
//             lpDIBBits   == Pointer to the DIB's bitmap bits.
//
// Returns:    One or more of the ERR_PRN_* errors in PRINT.H (or'd
//             together. ERR_PRN_NONE (0) if no error.
//
// History:   Date      Reason
//             6/01/91  Created
//
//---------------------------------------------------------------------

DWORD PrintABand (HDC hDC,
               LPRECT lpRectOut,
               LPRECT lpRectClip,
                 BOOL fDoText,
                 BOOL fDoGraphics,
                LPSTR lpDIBHdr,
                LPSTR lpDIBBits)
{
   int    nxLogPix, nyLogPix;
   RECT   rect;
   double dblXScaling, dblYScaling;
   DWORD  dwError = ERR_PRN_NONE;


   if (fDoGraphics)
      {
      nxLogPix = GetDeviceCaps (hDC, LOGPIXELSX);
      nyLogPix = GetDeviceCaps (hDC, LOGPIXELSY);

      dblXScaling = ((double) lpRectOut->right - lpRectOut->left) /
                     DIBWidth (lpDIBHdr);
      dblYScaling = ((double) lpRectOut->bottom - lpRectOut->top) /
                     DIBHeight (lpDIBHdr);


         // Now we set up a temporary rectangle -- this rectangle
         //  holds the coordinates on the paper where our bitmap
         //  WILL be output.  We can intersect this rectangle with
         //  the lpClipRect to see what we NEED to output to this
         //  band.  Then, we determine the coordinates in the DIB
         //  to which this rectangle corresponds (using dbl?Scaling).

      IntersectRect (&rect, lpRectOut, lpRectClip);

      if (!IsRectEmpty (&rect))
         {
         RECT rectIn;
         int  nPct;

         rectIn.left   = (int) ((rect.left - lpRectOut->left) /
                                 dblXScaling + 0.5);
         rectIn.top    = (int) ((rect.top  - lpRectOut->top) /
                                 dblYScaling + 0.5);
         rectIn.right  = (int) (rectIn.left + (rect.right  - rect.left) /
                                 dblXScaling + 0.5);
         rectIn.bottom = (int) (rectIn.top  +  (rect.bottom - rect.top) /
                                 dblYScaling + 0.5);

            // Could just always call StretchDIBits() below, but
            //  we want to give SetDIBitsToDevice() a work out, too!

         if ((rect.right - rect.left == rectIn.right - rectIn.left) &&
             (rect.bottom - rect.top == rectIn.bottom - rectIn.top))
            {
            if (!SetDIBitsToDevice (hDC,                            // DestDC
                                    rect.left,                      // DestX
                                    rect.top,                  // DestY
                                    rect.right - rect.left,    // DestWidth
                                    rect.bottom - rect.top,    // DestHeight
                                    rectIn.left,               // SrcX
                                    (int) DIBHeight (lpDIBHdr)-// SrcY
                                       rectIn.top -
                                       (rectIn.bottom - rectIn.top),
                                    0,                         // nStartScan
                                    (int) DIBHeight (lpDIBHdr),// nNumScans
                                    lpDIBBits,                 // lpBits
                                    (LPBITMAPINFO) lpDIBHdr,   // lpBitInfo
                                    DIB_RGB_COLORS))           // wUsage
               dwError |= ERR_PRN_SETDIBITSTODEV;
            }
         else
            {
            if (!StretchDIBits (hDC,                           // DestDC
                                rect.left,                     // DestX
                                rect.top,                      // DestY
                                rect.right - rect.left,        // DestWidth
                                rect.bottom - rect.top,        // DestHeight
                                rectIn.left,                   // SrcX
                                (int) DIBHeight (lpDIBHdr) -   // SrcY
                                   rectIn.top -
                                   (rectIn.bottom - rectIn.top),
                                rectIn.right - rectIn.left,    // SrcWidth
                                rectIn.bottom - rectIn.top,    // SrcHeight
                                lpDIBBits,                     // lpBits
                                (LPBITMAPINFO) lpDIBHdr,       // lpBitInfo
                                DIB_RGB_COLORS,                // wUsage
                                SRCCOPY))                      // dwROP
               dwError |= ERR_PRN_STRETCHDIBITS;
            }


            // Change percentage of print shown in abort dialog.

         nPct = MulDiv (rect.bottom,
                        100,
                        lpRectOut->bottom);
         ChangePrintPercent (nPct);
         }
      }

   return dwError;
}



//---------------------------------------------------------------------
//
// Function:   DeviceSupportsEscape
//
// Purpose:    Uses QUERYESCSUPPORT to see if the given device
//             supports the given escape code.
//
// Parms:      hDC         == Device to check if escape is supported on.
//             nEscapeCode == Escape code to check for.
//
// History:   Date      Reason
//            10/26/91  Created
//
//---------------------------------------------------------------------

BOOL DeviceSupportsEscape (HDC hDC, int nEscapeCode)
{
   return Escape(hDC, QUERYESCSUPPORT, sizeof(int), (LPSTR) &nEscapeCode, NULL);
}



//---------------------------------------------------------------------
//
// Function:   TranslatePrintRect
//
// Purpose:    Given a rectangle and what units that rectangle is in,
//             translate the rectangle to the appropriate value in
//             device units.
//
// Parms:      hDC         == DC translation is relative to.
//             lpPrintRect == Pointer to rectangle to translate.
//             wUnits      == Units lpPrintRect is in:
//                              UNITS_INCHES == Units are in inches, stretch
//                                                DIB to this size on page.
//                              UNITS_STRETCHTOPAGE == lpPrintRect doesn't
//                                                matter, stretch DIB to
//                                                fill the entire page.
//                              UNITS_BESTFIT == lpPrintRect doesn't matter,
//                                                stretch DIB as much as
//                                                possible horizontally,
//                                                and preserve its aspect
//                                                ratio vertically.
//                              UNITS_SCALE == lpPrintRect->top is factor to
//                                                stretch vertically.
//                                                lpPrintRect->left is
//                                                factor to stretch horiz.
//                              UNITS_PIXELS == lpPrintRect is in pixels.
//             cxDIB       == DIB's width.
//             cyDIB       == DIB's height.
//
// History:   Date      Reason
//             6/01/91  Created
//
//---------------------------------------------------------------------

void TranslatePrintRect (HDC hDC,
                      LPRECT lpPrintRect,
                        WORD wUnits,
                        WORD cxDIB,
                        WORD cyDIB)
{
   int cxPage, cyPage, cxInch, cyInch;

   if (!hDC)
      return;

   cxPage = GetDeviceCaps (hDC, HORZRES);
   cyPage = GetDeviceCaps (hDC, VERTRES);
   cxInch = GetDeviceCaps (hDC, LOGPIXELSX);
   cyInch = GetDeviceCaps (hDC, LOGPIXELSY);

   switch (wUnits)
      {
         // lpPrintRect contains units in inches.  Convert to pixels.

      case UNITS_INCHES:
         lpPrintRect->top    *= cyInch;
         lpPrintRect->left   *= cxInch;
         lpPrintRect->bottom *= cyInch;
         lpPrintRect->right  *= cxInch;
         break;


         // lpPrintRect contains no pertinent info -- create a rectangle
         //  which covers the entire printing page.

      case UNITS_STRETCHTOPAGE:
         lpPrintRect->top    = 0;
         lpPrintRect->left   = 0;
         lpPrintRect->bottom = cyPage;
         lpPrintRect->right  = cxPage;
         break;


         // lpPrintRect contains no pertinent info -- create a rectangle
         //  which preserves the DIB's aspect ratio, and fills the page
         //  horizontally.  NOTE:  Assumes DIB is 1 to 1 aspect ratio,
         //  could use biXPelsPerMeter in a DIB to munge these values
         //  for non 1 to 1 aspect ratio DIBs (I've never seen such
         //  a beast, though)!

      case UNITS_BESTFIT:
         lpPrintRect->top    = 0;
         lpPrintRect->left   = 0;
         lpPrintRect->bottom = (int)(((double) cyDIB * cyPage * cyInch) /
                                     ((double) cxDIB * cxInch));
         lpPrintRect->right  = cxPage;
         break;



         // lpPrintRect's top/left contain multipliers to multiply the
         //  DIB's height/width by.

      case UNITS_SCALE:
         {
         int cxMult, cyMult;

         cxMult              = lpPrintRect->left;
         cyMult              = lpPrintRect->top;
         lpPrintRect->top    = 0;
         lpPrintRect->left   = 0;
         lpPrintRect->bottom = cyDIB * cyMult;
         lpPrintRect->right  = cxDIB * cxMult;
         }


         // lpPrintRect already contains device units, don't touch it.

      case UNITS_PIXELS:
      default:
         // Don't touch the units...
         break;
      }
}





//---------------------------------------------------------------------
//
// Function:   GetPrinterDC
//
// Purpose:    Returns a DC to the currently selected printer.  Returns
//             NULL on error.
//
// Parms:      None
//
// History:   Date      Reason
//             6/01/91  Created
//
//---------------------------------------------------------------------

HDC GetPrinterDC (void)
{
   PRINTDLG pd;

   pd.lStructSize          = sizeof (pd);
   pd.hwndOwner            = NULL;
   pd.hDevMode             = NULL;
   pd.hDevNames            = NULL;
   pd.hDC                  = NULL;
   pd.Flags                = PD_RETURNDC | PD_RETURNDEFAULT;
   pd.nFromPage            = 0;
   pd.nToPage              = 0;
   pd.nMinPage             = 0;
   pd.nMaxPage             = 0;
   pd.nCopies              = 0;
   pd.hInstance            = NULL;
   pd.lCustData            = NULL;
   pd.lpfnPrintHook        = NULL;
   pd.lpfnSetupHook        = NULL;
   pd.lpPrintTemplateName  = NULL;
   pd.lpSetupTemplateName  = NULL;
   pd.hPrintTemplate       = NULL;
   pd.hSetupTemplate       = NULL;

   if (PrintDlg (&pd))
      return pd.hDC;
   else
      return NULL;
}






//-------------------- Abort Routines ----------------------------


//---------------------------------------------------------------------
//
// Function:   PrintAbortProc
//
// Purpose:    Abort procedure while printing is occurring.  Registered
//             with Windows via the SETABORTPROC escape.  This routine
//             is called regularly by the sytem during a print operation.
//
//             By putting a PeekMessage loop here, multitasking can occur.
//             PeekMessage will yield to other apps if they have messages
//             waiting for them.
//
//             Doesn't bother if the global, bAbort, is set.  This var
//             is set by PrintAbortDlg() when a user cancels a print
//             operation.
//
// Parms:      hDC  == DC printing is being done to
//             code == Error code (see docs for SETABORTPROC printer
//                      escape).
//
// History:   Date      Reason
//             6/01/91  Created
//
//---------------------------------------------------------------------

BOOL FAR PASCAL __export PrintAbortProc(HDC hDC, short code)
{
   MSG msg;

   bAbort |= (code != 0);

   while (!bAbort && PeekMessage (&msg, 0, 0, 0, PM_REMOVE))
      if (!IsDialogMessage (hDlgAbort, &msg))
         {
         TranslateMessage (&msg);
         DispatchMessage (&msg);
         }

   return (!bAbort);
}



//---------------------------------------------------------------------
//
// Function:   PrintAbortDlg
//
// Purpose:    Dialog box window procedure for the "cancel" dialog
//             box put up while DIBView is printing.
//
//             Functions sets bAbort (a global variable) to true
//             if the user aborts the print operation.  Other functions
//             in this module then "do the right thing."
//
//             Also handles MYWM_CHANGEPCT to change % done displayed
//             in dialog box.
//
// Parms:      hWnd    == Handle to this dialog box.
//             message == Message for window.
//             wParam  == Depends on message.
//             lParam  == Depends on message.
//
// History:   Date      Reason
//             6/01/91  Created
//
//---------------------------------------------------------------------

int FAR PASCAL __export PrintAbortDlg(HWND hWnd, unsigned msg, WORD wParam, LONG lParam)
{
   switch (msg)
      {
      case WM_INITDIALOG:
         SetFocus(hWnd);
         return TRUE;


      case WM_COMMAND:
         bAbort = TRUE;
         return TRUE;


      case MYWM_CHANGEPCT:
         {
         char szBuf[20];

         wsprintf (szBuf, "%3d%% done", wParam);
         SetDlgItemText (hWnd, IDD_PRNPCT, szBuf);
         return TRUE;
         }
      }

   return FALSE;
}



//---------------------------------------------------------------------
//
// Function:   DoStartDoc
//
// Purpose:    Called at the beginning of printing a document.  Does
//             the "right thing," depending on whether we're using
//             3.0 style printer escapes, or the 3.1 printing API
//             (i.e. either does an Escape(STARTDOC) or StartDoc()).
//
//             Note that it uses FindGDIFunction() to find the address
//             of StartDoc() we can't just put a call to StartDoc()
//             here because we want this .EXE file to be compatible with
//             Windows 3.0 as well as 3.1.  3.0 didn't have a function
//             "StartDoc()!"
//
// Parms:      hPrnDC == DC to printer
//
// Returns:    An error code defined as ERR_PRN_* in PRINT.H:
//                ERR_PRN_NONE (0) if no error.
//
// History:   Date      Reason
//             6/01/91  Created
//            11/14/91  Added error return code.
//
//---------------------------------------------------------------------

DWORD DoStartDoc (HDC hPrnDC, LPSTR lpszDocName)
{
   if (gbUseEscapes)
      {
      if (Escape (hPrnDC, STARTDOC, lstrlen (lpszDocName),
                  lpszDocName, NULL) < 0)
         return ERR_PRN_STARTDOC;
      }
   else
      {
      LPSTARTDOC Win31StartDoc;
      OURDOCINFO DocInfo;

      Win31StartDoc       = (LPSTARTDOC) FindGDIFunction (szStartDoc);
      DocInfo.cbSize      = sizeof (DocInfo);
      DocInfo.lpszDocName = lpszDocName;
      DocInfo.lpszOutput  = NULL;

      if (Win31StartDoc)
         {
         if (Win31StartDoc (hPrnDC, &DocInfo) < 0)
            return ERR_PRN_STARTDOC;
         }
      else
         return ERR_PRN_NOFNSTARTDOC;
      }

   return ERR_PRN_NONE;
}




//---------------------------------------------------------------------
//
// Function:   DoSetAbortProc
//
// Purpose:    Called at the beginning of printing a document.  Does
//             the "right thing," depending on whether we're using
//             3.0 style printer escapes, or the 3.1 printing API
//             (i.e. either does an Escape(SETABORTPROC) or SetAbortProc()).
//
//             Note that it uses FindGDIFunction() to find the address
//             of SetAbortProc() we can't just put a call to SetAbortProc()
//             here because we want this .EXE file to be compatible with
//             Windows 3.0 as well as 3.1.  3.0 didn't have a function
//             "SetAbortProc()!"
//
// Parms:      hPrnDC == DC to printer
//
// Returns:    An error code defined as ERR_PRN_* in PRINT.H:
//                ERR_PRN_NONE (0) if no error.
//
// History:   Date      Reason
//             6/01/91  Created
//            11/14/91  Added error return code.
//
//---------------------------------------------------------------------

DWORD DoSetAbortProc (HDC hPrnDC, FARPROC lpfnAbortProc)
{
   LPSETABORTPROC Win31SetAbortProc;

   if (gbUseEscapes)
      {
      if (Escape(hPrnDC, SETABORTPROC, NULL, (LPSTR) lpfnAbortProc, NULL) < 0)
         return ERR_PRN_SETABORTPROC;
      }
   else
      {
      Win31SetAbortProc = (LPSETABORTPROC) FindGDIFunction (szSetAbortProc);
      if (Win31SetAbortProc)
         {
         if (Win31SetAbortProc (hPrnDC, lpfnAbortProc) < 0)
           return ERR_PRN_SETABORTPROC;
         }
      else
         return ERR_PRN_NOFNSETABORTPROC;
      }

   return ERR_PRN_NONE;
}



//---------------------------------------------------------------------
//
// Function:   DoStartPage
//
// Purpose:    Called at the beginning of printing a page.  Does
//             the "right thing," depending on whether we're using
//             3.0 style printer escapes, or the 3.1 printing API.
//             Routine does nothing under 3.0 or when using the 3.0
//             Escapes, as there was no equivalent to StartPage()
//             in 3.0.
//
//             Note that it uses FindGDIFunction() to find the address
//             of StartPage() we can't just put a call to StartPage()
//             here because we want this .EXE file to be compatible with
//             Windows 3.0 as well as 3.1.  3.0 didn't have a function
//             "StartPage()!"
//
// Parms:      hPrnDC == DC to printer
//
// Returns:    An error code defined as ERR_PRN_* in PRINT.H:
//                ERR_PRN_NONE (0) if no error.
//
// History:   Date      Reason
//             6/01/91  Created
//            11/14/91  Added error return code.
//
//---------------------------------------------------------------------

DWORD DoStartPage (HDC hPrnDC)
{
   LPSTARTPAGE Win31StartPage;

   if (!gbUseEscapes)
      {
      Win31StartPage = (LPSTARTPAGE) FindGDIFunction (szStartPage);
      if (Win31StartPage)
         {
         if (!Win31StartPage (hPrnDC))
            return ERR_PRN_STARTPAGE;
         }
      else
         return ERR_PRN_NOFNSTARTPAGE;
      }

   return ERR_PRN_NONE;
}



//---------------------------------------------------------------------
//
// Function:   DoEndPage
//
// Purpose:    Called at the end of printing a page.  Does the
//             "right thing," depending on whether we're using
//             3.0 style printer escapes, or the 3.1 printing API
//             (i.e. either does an Escape(NEWFRAME) or EndPage()).
//
//             Note that it uses FindGDIFunction() to find the address
//             of EndPage() we can't just put a call to EndPage()
//             here because we want this .EXE file to be compatible with
//             Windows 3.0 as well as 3.1.  3.0 didn't have a function
//             "EndPage()!"
//
// Parms:      hPrnDC == DC to printer
//
// Returns:    An error code defined as ERR_PRN_* in PRINT.H:
//                ERR_PRN_NONE (0) if no error.
//
// History:   Date      Reason
//             6/01/91  Created
//            11/14/91  Added error return code.
//
//---------------------------------------------------------------------

DWORD DoEndPage (HDC hPrnDC)
{
   LPENDPAGE Win31EndPage;

   if (gbUseEscapes)
      {
      if (Escape (hPrnDC, NEWFRAME, NULL, NULL, NULL) < 0)
         return ERR_PRN_NEWFRAME;
      }
   else
      {
      Win31EndPage = (LPENDPAGE) FindGDIFunction (szEndPage);
      if (Win31EndPage)
         {
         if (Win31EndPage (hPrnDC) < 0)
            return ERR_PRN_NEWFRAME;
         }
      else
         return ERR_PRN_NOFNENDPAGE;
      }

   return ERR_PRN_NONE;
}



//---------------------------------------------------------------------
//
// Function:   DoEndDoc
//
// Purpose:    Called at the end of printing a document.  Does
//             the "right thing," depending on whether we're using
//             3.0 style printer escapes, or the 3.1 printing API
//             (i.e. either does an Escape(ENDDOC) or EndDoc()).
//
//             Note that it uses FindGDIFunction() to find the address
//             of EndDoc() we can't just put a call to EndDoc()
//             here because we want this .EXE file to be compatible with
//             Windows 3.0 as well as 3.1.  3.0 didn't have a function
//             "EndDoc()!"
//
// Parms:      hPrnDC == DC to printer
//
// Returns:    An error code defined as ERR_PRN_* in PRINT.H:
//                ERR_PRN_NONE (0) if no error.
//
// History:   Date      Reason
//             6/01/91  Created
//            11/14/91  Added error return code.
//
//---------------------------------------------------------------------

DWORD DoEndDoc (HDC hPrnDC)
{
   LPENDDOC Win31EndDoc;

   if (gbUseEscapes)
      {
      if (Escape(hPrnDC, ENDDOC, NULL, NULL, NULL) < 0)
         return ERR_PRN_ENDDOC;
      }
   else
      {
      Win31EndDoc = (LPENDDOC) FindGDIFunction (szEndDoc);
      if (Win31EndDoc)
         {
         if (Win31EndDoc (hPrnDC) < 0)
            return ERR_PRN_ENDDOC;
         }
      else
         return ERR_PRN_NOFNENDDOC;
      }

   return ERR_PRN_NONE;
}




//---------------------------------------------------------------------
//
// Function:   FindGDIFunction
//
// Purpose:    Uses GetModuleHandle() and GetProcAddress() to find
//             a given function inside GDI itself.  This is useful
//             to "link" to functions on-the-fly that are only
//             present in Windows 3.1.  If we were to call these
//             functions directly from this EXE file, Windows 3.0 would
//             complain that we are not calling valid functions.
//
// Parms:      lpszFnName == Name of function within GDI.EXE to find
//                            an address for.
//
// History:   Date      Reason
//             6/01/91  Created
//
//---------------------------------------------------------------------

FARPROC FindGDIFunction (LPSTR lpszFnName)
{
   HANDLE hGDI;

   hGDI = GetModuleHandle (szGDIModule);

   if (!hGDI)
      return NULL;

   return GetProcAddress (hGDI, lpszFnName);
}





//---------------------------------------------------------------------
//
// Function:   ShowPrintError
//
// Purpose:    Decode a printing error and display a message box
//             which describes what the error is.
//
//             Errors are stored in a bitwise fashion, so we
//             check all the valid error bits in the error, and
//             display a messagebox for each error.
//
// Parms:      hWnd   == Parent for message box which shows error.
//             wError == Error bitfield (see ERR_PRN_* in PRINT.H).
//
// History:   Date      Reason
//            11/14/91  Created
//
//---------------------------------------------------------------------

void ShowPrintError (HWND hWnd, DWORD dwError)
{
   char szError[100];
   int  i = 0;

   if (dwError == ERR_PRN_NONE)
      {
      if (LoadString (hInst, IDS_PRN_NONE, szError, 100))
         MessageBox (hWnd, szError, NULL, MB_OK);
      return;
      }

   while (dwError)
      {
      i++;

      if (dwError & 1)
         {
         if (LoadString (hInst, i + IDS_PRN_NONE, szError, 100))
            MessageBox (hWnd, szError, NULL, MB_OK);
         else
            MessageBeep (0);
         }

      dwError >>= 1;
      }
}



