/**************************************************************************
 *  File name  :  prtprint.c
 *
 *  Description:  This module contains all the functions needed to display
 *		  a listbox with the valid printer queues and to select a
 *		  printer and the print properties.
 *
 *		  This source file contains the following functions:
 *
 *		  Print( hwnd, pmp )
 *		  PrintingDlgProc( hwnd, msg, mp1, mp2 )
 *		  PrintDlgProc( hwnd, msg, mp1, mp2 )
 *		  QueryPrintQueue( hwnd, pmp, fShowDialog)
 *		  QueryPrintDlgProc( hwnd, msg, mp1, mp2 )
 *		  QueryJobProperties( hab, pQueueInfo )
 *		  ProcessUserPrint( hwnd, pmp )
 *		  PrinterBitblt( hwnd, pmp )
 *		  PrinterPaginate( hwnd, pmp )
 *		  PrinterPlayMetaFile( hwnd, pmp )
 *		  PrinterResetMetaFile( hwnd, pmp )
 *		  FindQueue( pszQueueName, pQueueInfo, sCount )
 *		  SortQueues( pQueue1, pQueue2 )
 *
 *  Concepts   :  Listbox, printer queue display and printer property dislpay
 *		  and modification.
 *
 *  API's      :  DevCloseDC
 *		  DevEscape
 *		  DevOpenDC
 *		  DevPostDeviceModes
 *		  DevQueryCaps
 *
 *		  GpiAssociate
 *		  GpiBitblt
 *		  GpiConvert
 *		  GpiCreateLogFont
 *		  GpiCreatePS
 *		  GpiDeleteBitmap
 *		  GpiDeleteSetID
 *		  GpiDestroyPS
 *		  GpiPlayMetaFile
 *		  GpiQueryPS
 *		  GpiSetBitmap
 *		  GpiSetCharSet
 *
 *		  PrfQueryProfileString
 *
 *		  SplEnumQueue
 *
 *		  WinDefDlgProc
 *		  WinDismissDlg
 *		  WinDlgBox
 *		  WinEnableControl
 *		  WinLoadDlg
 *		  WinLoadString
 *		  WinMessageBox
 *		  WinPostMsg
 *		  WinQueryDlgItemShort
 *		  WinQueryDlgItemText
 *		  WinQueryWindowULong
 *		  WinSendMsg
 *		  WinSetDlgItemShort
 *		  WinSetDlgItemText
 *		  WinSetWindowULong
 *		  WinSendDlgItemMsg
 *		  WinShowWindow
 *
 *  Files      :  OS2.H, MAIN.H, HELP.H, XTRN.H
 *
 *  Copyright (C) 1991 IBM Corporation
 *
 *	DISCLAIMER OF WARRANTIES.  The following [enclosed] code is
 *	sample code created by IBM Corporation. This sample code is not
 *	part of any standard or IBM product and is provided to you solely
 *	for  the purpose of assisting you in the development of your
 *	applications.  The code is provided "AS IS", without
 *	warranty of any kind.  IBM shall not be liable for any damages
 *	arising out of your use of the sample code, even if they have been
 *	advised of the possibility of such damages.						       *
 *************************************************************************/

/* Include the required sections from the PM header file.  */
#define INCL_WINSTDFILE
#define INCL_WINSTDFONT
#define INCL_WINHELP
#define INCL_WINENTRYFIELDS
#define INCL_WINLISTBOXES
#define INCL_WINDIALOGS
#define INCL_WINERRORS
#define INCL_WINWINDOWMGR
#define INCL_WINSHELLDATA
#define INCL_GPIPRIMITIVES
#define INCL_GPITRANSFORMS
#define INCL_GPIBITMAPS
#define INCL_GPICONTROL
#define INCL_GPIMETAFILES
#define INCL_GPILCIDS
#define INCL_DEV
#define INCL_SPL
#define INCL_SPLDOSPRINT
#include <os2.h>

/* c language includes */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stddef.h>
#include <process.h>
#include <sys\types.h>
#include <sys\stat.h>

/* application includes */
#include "prtsamp.h"
#include "prtsdlg.h"
#include "pmassert.h"
#include "prtshlp.h"


/* static function prototypes */

static int _Optlink SortQueues(const void *pQueue1, const void *pQueue2);
MRESULT EXPENTRY PrintDlgProc(HWND, USHORT, MPARAM, MPARAM);
MRESULT EXPENTRY PrintingDlgProc(HWND, USHORT, MPARAM, MPARAM);
MRESULT EXPENTRY QueryPrintDlgProc(HWND, USHORT, MPARAM, MPARAM);
BOOL QueryJobProperties(HAB hab, PPRQINFO3 pPrQ);
LONG FindQueue(PSZ pszQueueName, PPRQINFO3 pQueueInfo, LONG lCount);
VOID PrinterPaginate(HWND hwnd, PMAIN_PARM pmp);
VOID PrinterBitblt(HWND hwnd, PMAIN_PARM pmp);
VOID PrinterPlayMetaFile(HWND hwnd, PMAIN_PARM pmp);
VOID PrinterResetMetaFile(HWND hwnd, PMAIN_PARM pmp);

/*************************************************************************
 *  Function   :  Print
 *
 *  Description:  This function displays the print dialog.
 *
 *  API's      :  WinDlgBox
 *		  WinSendMsg
 *		  WinLoadDlg
 *
 *  Parameters :  HWND	       window handle of frame window
 *		  PMAIN_PARM   global application data
 *
 *  Return     :  TRUE if print successful, otherwise FALSE
 *
 *************************************************************************/
BOOL Print(HWND hwnd, PMAIN_PARM pmp)
{
   /*
    * Query user print options.	 Of the standard print dialog, we've only
    * made use of the number of copies the user wants. See the PrintDlgProc
    * for details.
    */
   if (WinDlgBox( HWND_DESKTOP,
		  hwnd,
		  (PFNWP)PrintDlgProc,
		  (HMODULE)NULLHANDLE,
		  IDD_PRINT,
		  (PVOID)pmp) )
   {
      /*
       * User wants to print, so start print job and display text
       * showing job printing status.
       */
      WinSendMsg( hwnd, WM_USER_DISABLE_CLIENT, (MPARAM)0L, (MPARAM)0L );

      WinLoadDlg( HWND_DESKTOP,
		  hwnd,
		  (PFNWP)PrintingDlgProc,
		  (HMODULE)NULLHANDLE,
		  IDD_PRINTING,
		  (PVOID)pmp);
   }
   return FALSE;
}

/*************************************************************************
 *  Function   : PrintingDlgProc
 *
 *  Description:  This function displays the print dialog.
 *
 *  API's      :  WinPostMsg
 *		  WinDismissDlg
 *		  WinEnableControl
 *		  WinSetDlgItemText
 *		  WinDefDlgProc
 *
 *  Parameters :  HWND	       window handle of printer selection dialog
 *		  USHORT       message
 *		  MPARAM       message parameter 1
 *		  MPARAM       message parameter 2
 *
 *  Return     :  message result
 *
 *************************************************************************/
MRESULT EXPENTRY PrintingDlgProc(HWND hwnd,
				 USHORT msg,
				 MPARAM mp1,
				 MPARAM mp2)
{
   static PMAIN_PARM   pmp;

   switch (msg)
   {
   case WM_INITDLG:
      /* Get pointer to application specific data */
      pmp = (PMAIN_PARM) PVOIDFROMMP(mp2);

      /*
       * send WM_USER_PRINT message
       * disable client -- object window enables client
       *   when complete
       */
      pmp->fCancel = FALSE;
      WinPostMsg(pmp->hwndObject, WM_USER_PRINT, (MPARAM)hwnd, (MPARAM)0L);
      return 0L;

   case WM_USER_ACK:
      /*
       *  The job has either completed or aborted, so tell the client
       *  window and get out.
       */

      WinPostMsg(pmp->hwndClient, WM_USER_ACK, 0L, 0L);

      WinDismissDlg(hwnd, FALSE);
      return 0L;

   case WM_COMMAND:
      switch(SHORT1FROMMP(mp1))
      {
      case DID_CANCEL:
	 pmp->fCancel = TRUE;
	 WinEnableControl( hwnd, DID_CANCEL, FALSE );
	 WinSetDlgItemText( hwnd, IDD_PRINTINGTEXT, (PSZ)"Aborting..." );
	 return 0;
      }
      break;
   }
   return( WinDefDlgProc( hwnd, msg, mp1, mp2 ) );
}  /* End of PrintingDlgProc */

/*************************************************************************
 *  Function   :  PrintDlgProc
 *
 *  Description:
 *
 *  API's      :  WinSetDlgItemText
 *		  WinSetDlgItemShort
 *		  WinShowWindow
 *		  WinSendMsg
 *		  WinQueryDlgItemShort
 *		  WinQueryDlgItemText
 *		  WinDismissDlg
 *		  WinDefDlgProc
 *
 *  Parameters :  HWND	       window handle of printer selection dialog
 *		  USHORT       message
 *		  MPARAM       message parameter 1
 *		  MPARAM       message parameter 2
 *
 *  Return     :  MRESULT	  message result
 *
 *************************************************************************/
MRESULT EXPENTRY PrintDlgProc(HWND hwnd,
			      USHORT msg,
			      MPARAM mp1,
			      MPARAM mp2)
{
   static PMAIN_PARM pmp;
   PSZ	       psz;
   USHORT      us;
   ULONG       ulPanel;

   switch (msg)
   {
   case WM_INITDLG:
      /* Get pointer to application specific data */
      pmp = (PMAIN_PARM) PVOIDFROMMP(mp2);

      /* Show the printer name */
      WinSetDlgItemText(hwnd, IDD_PRTPRINTER, (PSZ)pmp->achQueueDesc);

      /* Show the document name */
      psz = strrchr( pmp->szFilename, '\\' );
      if (psz && *psz)
      {
	  ++psz;
	  WinSetDlgItemText(hwnd, IDD_PRTDOCUMENT, psz);
      }

      /* Initialize the number of copies */
      WinSetDlgItemShort(hwnd, IDD_PRTCOPIES,
			 pmp->usCopies, FALSE);

	/*
	 * Hide the page and preview controls.	The reason they are
	 * included in the print sample DLG file is for the benefit
	 * of programmers who want their dialogs to look exactly like
	 * those presented in the Programming Guide for printing.
	 */
	for (us = IDD_PRTALL; us <= IDD_PRTPREVIEW; ++us)
	    WinShowWindow(WinWindowFromID(hwnd, us), FALSE);

      /* Make entry fields read only */
      WinSendMsg(WinWindowFromID(hwnd, IDD_PRTPRINTER),
		 EM_SETREADONLY, (MPARAM)TRUE, (MPARAM)0L);
      WinSendMsg(WinWindowFromID(hwnd, IDD_PRTDOCUMENT),
		 EM_SETREADONLY, (MPARAM)TRUE, (MPARAM)0L);

      return 0;

   case WM_HELP:
      if( pmp->hwndHelp )
      {
	 ulPanel = PANEL_PRINTDLG;
	 WinSendMsg( pmp->hwndHelp, HM_DISPLAY_HELP,
		   (MPARAM)&ulPanel, (MPARAM)HM_RESOURCEID );
	 return (MRESULT)NULL;
      }
      break;

   case WM_CONTROL:
      switch(SHORT1FROMMP(mp1))
      {
      case IDD_PRTCOPIES:
	 if (SHORT2FROMMP(mp1) == EN_CHANGE)
	 {
	    /*
	     * Verify that a non-zero number was typed in the entry field.
	     * If not, beep and set it back to 1.
	     */
	    if (!WinQueryDlgItemShort( hwnd, IDD_PRTCOPIES,
				       (PSHORT)&pmp->usCopies, FALSE) ||
				       !pmp->usCopies )
	    {
	       CHAR    szCopies[4];	   /* text contents of entry field */

	       /*
		* If the field has length (no length means user backspaced
		* over the current value), the clear the invalid value and
		* beep to warn the user.
		*/
	       if (WinQueryDlgItemText( hwnd, IDD_PRTCOPIES,
					sizeof(szCopies), (PSZ)szCopies ))
	       {
		  WinSetDlgItemText(hwnd, IDD_PRTCOPIES, "");
		  DosBeep(600, 5);
	       }
	       pmp->usCopies = 1;
	    }
	 }
	 return 0;

	/* Not using these controls; see comment under WM_INITDLG */
      case IDD_PRTALL:
      case IDD_PRTSELECT:
      case IDD_PRTFIRSTPAGE:
      case IDD_PRTLASTPAGE:
      case IDD_PRTPREVIEW:
      case IDD_PRTDOCUMENT:
      case IDD_PRTPRINTER:
	 return 0;
      }
      break;

   case WM_COMMAND:
      switch(SHORT1FROMMP(mp1))
      {
      case DID_OK:
	 WinDismissDlg(hwnd, TRUE);
	 return 0;

      case DID_CANCEL:
	 WinDismissDlg(hwnd, FALSE);
	 return 0;

      case DID_HELP_BUTTON:
	 /* don't do anything yet */
	 return 0;
      }
      break;

   }
   return(WinDefDlgProc(hwnd, msg, mp1, mp2));

} /* End of PrintDlgProc */

/*************************************************************************
 *  Function   :  QueryPrintQueue
 *
 *  Description:  Query all printer queues, display them in a listbox
 *		  and do a selection.
 *
 *  Parameters:	  PMAIN_PARM  pmp has global application data
 *		  fShowDialog	  a flag:  should the dialog be displayed?
 *
 *  API's      :  WinLoadString
 *		  WinMessageBox
 *		  WinDlgBox
 *		  SplEnumQueue
 *
 *  Return     :  TRUE if printer changed, otherwise FALSE
 *
 *************************************************************************/
BOOL QueryPrintQueue(PMAIN_PARM pmp, BOOL fShowDialog)
{
    ULONG	    ul, cReturned, cTotal, cbNeeded;
    PPRQINFO3	    pQueueInfo, pqi;
    LONG	    i;
    BOOL	    fResult, fSameQueue, bOK;
    CHAR	    szWork[LEN_WORKSTRING];
    PSZ		    pszWork;
    SIZEL	    sizel;
    DEVOPENSTRUC    dos;
    CHAR	    achDriverName[DRIVERNAME_LENGTH];
    PCHAR	    pch;

   /*
    * Get count of total number of print queues and the size of the
    * buffer needed (cbNeeded) to hold them.
    */
   ul = SplEnumQueue( NULL,	     /* pszComputerName - local machine */
		      3,	     /* information level 3 requested	*/
		      NULL,	     /* pBuf - NULL for first call	*/
		      0L,	     /* cbBuf - 0 to get size needed	*/
		      &cReturned,    /* number of queues returned	*/
		      &cTotal,	     /* total number of queues		*/
		      &cbNeeded,     /* number bytes needed to store	*/
				     /* all requested information	*/
		      NULL );	     /* reserved			*/

   /* If there are no print queues display Message Box */
   if (cTotal == 0L)
   {	       /* this system has no printers defined */
      bOK = WinLoadString( pmp->hab,
			   (HMODULE)NULLHANDLE,
			   ERRMSG_NO_QUEUES_DEFINED,
			   LEN_WORKSTRING,
			   szWork );
      pmassert( pmp->hab, bOK );

      WinMessageBox( HWND_DESKTOP,
		     pmp->hwndFrame,
		     szWork,
		     pmp->pszTitle,
		     (USHORT)0,
		     MB_OK | MB_MOVEABLE | MB_CUAWARNING | MB_APPLMODAL);
	/* nothing changed */
      return FALSE;
   }

   /*
    * allocate memory to store the enumerated queue information
    */
   pQueueInfo = malloc( cbNeeded ) ;
    pmassert( pmp->hab, pQueueInfo );

    /* Call again to get print queue data. */
   ul = SplEnumQueue( NULL,		/* pszComputerName - local machine */
		      3,		/* information level 3 requested   */
		      pQueueInfo,	/* pBuf - gets enumerated queues   */
		      cbNeeded,		/* cbBuf - size of pBuf		   */
		      &cReturned,	/* number of queues returned	   */
		      &cTotal,		/* total number of queues	   */
		      &cbNeeded,	/* number bytes needed to store	   */
					/*   all requested information	   */
		      NULL );		/* reserved			   */

    pmassert( pmp->hab, ul == 0 );

    /*
     * Validate that the user-preferred queue has been initialized OK.
     * If not, initialize it now. User's preference of queues is
     * named by the variable pmp->achQueueName.
     */

    if( *pmp->achQueueName == 0 )
    {
       /*
	* There is no preferred queue name because there was none
	* in the INI file (perhaps this is the first time the
	* program has run on this system?) or the profile reading
	* failed somehow. Give the user the application default
	* queue.
	*
	* get default queue's name
	*/
       ul = PrfQueryProfileString( HINI_PROFILE,
				   "PM_SPOOLER",
				   "QUEUE",
				   NULL,
				   szWork,
				   LEN_WORKSTRING );

       if( ul )
       {
	  /* truncate queuename at terminating semicolon */
	  pch = strchr( szWork, ';' );
	  pmassert( pmp->hab, pch );
	  *pch = 0;
	  strcpy( pmp->achQueueName, szWork );
       }
       else
       {  /*
	   * no application default queue? odd. Give him the first queue
	   * from the SplEnumQueue calls
	   */
	  pmassert( pmp->hab, *pQueueInfo->pszName );
	  strcpy( pmp->achQueueName, pQueueInfo->pszName );
       }
    }

    pmassert( pmp->hab, 0 < strlen( pmp->achQueueName ));

    /*
     * store current driver data info into SplEnumQueue info
     * so that when user wants to change a print property
     * but not necessarily a printer, his previous settings
     * will be shown in the print properties dialog
     */
    i = FindQueue( pmp->achQueueName, pQueueInfo, cReturned );
    pmassert( pmp->hab, i != -1 );
    pqi = &pQueueInfo[i];
    if( pqi->pDriverData->cb == pmp->cbDriverDataLen )
    {
      memcpy( pqi->pDriverData, pmp->pDriverData, pmp->cbDriverDataLen );
    }


    /*
     * Display dialog box to select a printer
     */


    if (fShowDialog)
    {
	/* sort the data returned from SplEnumQueue by queue name  */
	qsort((void *)pQueueInfo,
	  (size_t)cReturned,
	  (size_t)sizeof(PRQINFO3),
	  SortQueues);

	/*
	 * Dialog procedure requires the data from SplEnumQueue.
	 * Use the pmp to pass this data to the dialog.
	 */
	pmp->pQueueInfo	 = pQueueInfo;
	pmp->lQueueCount = cReturned;

	/* present the dialog  */
	ul = WinDlgBox( HWND_DESKTOP,
			pmp->hwndFrame,
			(PFNWP)QueryPrintDlgProc,
			(HMODULE)0,
			IDD_QUERYPRINT,
			(PVOID)pmp);

	pmassert( pmp->hab, ul != DID_ERROR );

	/* done with these vars for now	 */
	pmp->pQueueInfo	 = NULL;
	pmp->lQueueCount = 0;


	if( ul == DID_CANCEL )
	{
	     /* free queueinfo buffer for SplEnumQueue	 */
	     free( pQueueInfo );
	     /* indicate to caller that no change occurred  */
	     return FALSE;
	}
    }




    /*
     * At this point, something changed about the printer destination:
     * form, printer, queue, resolution, something.
     *
     * Prepare a printer info DC and associate a printer info PS with it
     * for the print queue named by pmp->achQueueName.
     */



    /* set pqi to point to the PRQINFO3 structure for pmp->achQueueName	 */

    i = FindQueue( pmp->achQueueName, pQueueInfo, cReturned );
    pmassert( pmp->hab, i  != -1 );
    pqi = &pQueueInfo[i];

    /*
     * destroy previous printer info PS's and DC's and
     * allocations for their driver data
     */
    if( pmp->pDriverData )
    {
	free( pmp->pDriverData );
	pmp->pDriverData      = NULL;
	pmp->cbDriverDataLen  = 0;
    }

    if( pmp->hpsPrinterInfo )
    {
	GpiAssociate(pmp->hpsPrinterInfo, (HDC)NULL);
	GpiDestroyPS(pmp->hpsPrinterInfo);
	pmp->hpsPrinterInfo = (HPS)0;
    }

    if( pmp->hdcPrinterInfo )
    {
	DevCloseDC(pmp->hdcPrinterInfo);
	pmp->hdcPrinterInfo = (HDC)0;
    }

    /* store new driver name in pmp  */
    strcpy( pmp->achDriverName, pqi->pszDriverName );

    /* store new driver data length in pmp  */
    pmp->cbDriverDataLen  = pqi->pDriverData->cb;

    /* store new driver data in pmp  */
    pmp->pDriverData = (PDRIVDATA) malloc( pmp->cbDriverDataLen );
    pmassert( pmp->hab, pmp->pDriverData );
    memcpy( pmp->pDriverData, pqi->pDriverData, pmp->cbDriverDataLen );

    /* store new queue description in pmp  */
    strncpy(pmp->achQueueDesc, pqi->pszComment, QUEUEDESC_LENGTH);
    pmp->achQueueDesc[QUEUEDESC_LENGTH-1] = 0;

    /* initialize local variables to zeros */
    memset( &dos,   0, sizeof(dos));
    memset( &sizel, 0, sizeof(sizel));

    /* build a devopenstruct for the call to DevOpenDC */
    strcpy( achDriverName, pqi->pszDriverName );
    achDriverName[strcspn(achDriverName, ".")] = '\0';
    dos.pszLogAddress = pmp->achQueueName;
    dos.pszDriverName = (PSZ)achDriverName;
    dos.pdriv = pmp->pDriverData;

    /* Create an OD_INFO device context */
    pmp->hdcPrinterInfo = DevOpenDC(
			    pmp->hab,
			    OD_INFO,
			    "*",
			    3L,
			    (PVOID)&dos,
			    (HDC)NULL);
    pmassert(pmp->hab, pmp->hdcPrinterInfo != DEV_ERROR);


    /* create PS for printer info -- not a full-blown PS */
    pmp->hpsPrinterInfo = GpiCreatePS(
			pmp->hab,
			pmp->hdcPrinterInfo,
			(PSIZEL)&sizel,
			PU_TWIPS | GPIA_ASSOC);
    pmassert(pmp->hab, pmp->hpsPrinterInfo != GPI_ERROR);

    /* Store the page size of the device (PU_TWIPS) */
    ul = GpiQueryPS(pmp->hpsPrinterInfo, (PSIZEL)&pmp->sizelPage);
    pmassert(pmp->hab, ul != GPI_ERROR);


    /* free the buffer allocated for SplEnumQueue call	*/
    free( pQueueInfo );

    /* return indication that something changed	 */
    return TRUE;
}   /*	end of QueryPrintQueue */

/*************************************************************************
 *  Function   :  QueryPrintDlgProc
 *
 *  Description:  This function is the dialog window procedure for printer
 *		  selection.
 *		  This dialog presents to the user a list of printer
 *		  destinations, or queues.  Typically, the user would
 *		  hilight a queue, then press the Job Properties... button
 *		  to set items like orientation, form, resolution, etc.
 *		  The printer driver presents the job properties dialog
 *		  when the application calls DevPostDeviceModes.
 *
 *		  Today, there is no standard, uniform way to tell whether
 *		  the user pressed OK or Cancel at the Job Properties dialog;
 *		  therefore, the dialog processing assumes that
 *		  job properties were changed no matter if the
 *		  user pressed OK or cancel. See the use of the
 *		  fVisitedJobProperties Boolean variable.
 *
 *		  This function is associated with the dialog box that is
 *		  included in the function name of the procedure. It
 *		  provides the service routines for the events (messages)
 *		  that occur because the end user operates one of the dialog
 *		  box's buttons, entry fields, or controls.
 *
 *		  The SWITCH statement in the function distributes the dialog
 *		  box messages to the respective service routines, which are
 *		  set apart by the CASE clauses. Like any other PM window,
 *		  the Dialog Window procedures must provide an appropriate
 *		  service routine for their end user initiated messages as
 *		  well as for the general PM messages (like the WM_CLOSE
 *		  message). If a message is sent to this procedure for which
 *		  there is no programmed CASE condition (no service routine),
 *		  the message is defaulted to the WinDefDlgProc function,
 *		  where it is disposed of by PM.
 *
 *  Parameters :  HWND	       window handle of printer selection dialog
 *		  USHORT       message
 *		  MPARAM       message parameter 1
 *		  MPARAM       message parameter 2
 *
 *  API's      :  WinSetWindowULong
 *		  WinSendDlgItemMsg
 *		  WinEnableControl
 *		  WinSendDlgItemMsg
 *		  WinQueryWindowULong
 *		  WinDismissDlg
 *		  WinDefDlgProc
 *
 * Return      :  message result
 *
 *************************************************************************/
MRESULT EXPENTRY QueryPrintDlgProc( HWND hwnd,
				    USHORT msg,
				    MPARAM mp1,
				    MPARAM mp2 )
{
   static BOOL	       fVisitedJobProperties;
   PMAIN_PARM	       pmp;
   INT		       i, index;
   PSZ		       psz;
   HWND		       hwndListbox;
   ULONG	       ulPanel;

   switch (msg)
   {
   case WM_INITDLG:
      /* Get pointer to application specific data */
      pmp = (PMAIN_PARM) mp2;
      WinSetWindowULong( hwnd, QWL_USER, (ULONG) mp2 );


      /*
       *  Assume user will not select job properties.
       *  This affects how to dismiss this dialog.
       */
      fVisitedJobProperties = FALSE;

      /* Fill listbox with print objects */

	hwndListbox = WinWindowFromID( hwnd, IDD_QPLISTBOX );
	pmassert( pmp->hab, hwndListbox );

      for (i = 0; i < pmp->lQueueCount; ++i)
      {
       /* use printer comment if possible, else use queue name for display */
	 psz = *pmp->pQueueInfo[i].pszComment ?
		pmp->pQueueInfo[i].pszComment :
		pmp->pQueueInfo[i].pszName;
	    /* pmwin.h macro for inserting list box items   */
	    index = WinInsertLboxItem( hwndListbox, LIT_END, psz );

	    /* pre-select this one?  */
	    if( 0 == strcmp( pmp->pQueueInfo[i].pszName, pmp->achQueueName ))
	    {
	       WinSendMsg( hwndListbox, LM_SELECTITEM,
					    (MPARAM)index, (MPARAM)TRUE );
	    }
      }

	/* one must be selected */
	index = WinQueryLboxSelectedItem( hwndListbox );
	pmassert( pmp->hab, index != LIT_NONE );

      return FALSE;

   case WM_HELP:
      /* get pointer to structure from window words of this dialog */
      pmp = (PMAIN_PARM)WinQueryWindowULong( hwnd, QWL_USER );

      if( pmp->hwndHelp )
      {
	 ulPanel = PANEL_QUERYPRINT;
	 WinSendMsg( pmp->hwndHelp, HM_DISPLAY_HELP,
		   (MPARAM)&ulPanel, (MPARAM)HM_RESOURCEID );
	 return (MRESULT)NULL;
      }
      break;


   case WM_COMMAND:
      pmp = (PMAIN_PARM) WinQueryWindowULong( hwnd, QWL_USER );
      switch(SHORT1FROMMP(mp1))
      {
      case IDD_QPJOBPROP:
	  /*
	   * Flag that we visited the job properties dialog. Since
	   * DevPostDeviceModes (OS2_PM_DRV_DEVMODES) option
	   * DPDM_POSTJOBPROP has no return code or standard way to
	   * indicate user cancel, we have to assume something might
	   * have changed.
	   */
	  fVisitedJobProperties = TRUE;
	  hwndListbox = WinWindowFromID( hwnd, IDD_QPLISTBOX );
	  pmassert( pmp->hab, hwndListbox );
	  index = WinQueryLboxSelectedItem( hwndListbox );
	  pmassert( pmp->hab, index != LIT_NONE );
	  QueryJobProperties( pmp->hab, &pmp->pQueueInfo[ index ] );
	  return 0;

      case DID_OK:
	    hwndListbox = WinWindowFromID( hwnd, IDD_QPLISTBOX );
	    pmassert( pmp->hab, hwndListbox );
	    index = WinQueryLboxSelectedItem( hwndListbox );
	    pmassert( pmp->hab, index != LIT_NONE );

	 /* modify pmp->achQueueName; it is now the user's preferred queue */
	    strcpy( pmp->achQueueName,	pmp->pQueueInfo[ index ].pszName );
	    WinDismissDlg( hwnd, DID_OK );
	    return 0;


      case DID_CANCEL:
	 WinDismissDlg(hwnd, fVisitedJobProperties ? DID_OK : DID_CANCEL );
	 return (MRESULT)NULL;
      }
      break;

   }
   return( WinDefDlgProc(hwnd, msg, mp1, mp2) );
} /* End of QueryPrintDlgProc */


/*************************************************************************
 *  Function   :  QueryJobProperties
 *
 *  Description:  Query job properties (print properties) of the printer
 *		  driver.
 *
 *  Parameters :  HAB		  anchor block handle of the process
 *		  PPRQINFO3	  pointer to queue information structure
 *
 *  API's      :  DevPostDeviceModes
 *
 *  Return     :  TRUE if ok, otherwise FALSE
 *
 *************************************************************************/
BOOL QueryJobProperties(HAB hab, PPRQINFO3 pQueueInfo)
{
   CHAR	       achDriverName[DRIVERNAME_LENGTH];
   CHAR	       achDeviceName[DEVICENAME_LENGTH];
   INT	       i;
   LONG	       l;
   PSZ	       pszTemp;

   /*
    * The pszDriverName is of the form DRIVER.DEVICE (e.g.,
    * LASERJET.HP LaserJet IID) so we need to separate it
    * at the dot
    */
   i = strcspn(pQueueInfo->pszDriverName, ".");
   if (i)
   {
      strncpy(achDriverName, pQueueInfo->pszDriverName, i);
      achDriverName[i] = '\0';
      strcpy(achDeviceName, &pQueueInfo->pszDriverName[i + 1]);
   }
   else
   {
      strcpy(achDriverName, pQueueInfo->pszDriverName);
      *achDeviceName = '\0';
   }

   /*
    * There may be more than one printer assigned to this print queue
    * We will use the first in the comma separated list.  We would
    * need an expanded dialog for the user to be more specific.
    */
   pszTemp = strchr(pQueueInfo->pszPrinters, ',');
   if ( pszTemp )
   {
      /* Strip off comma and trailing printer names */
      *pszTemp = '\0' ;
   }

   /*
    * Post the job properties dialog for the printer to allow the
    * user to modify the options
    */
   l = DevPostDeviceModes( hab,
			   pQueueInfo->pDriverData,
			   achDriverName,
			   achDeviceName,
			   pQueueInfo->pszPrinters,
			   DPDM_POSTJOBPROP );

   return l == DEV_OK;
}  /* End of QueryJobProperties */

/*************************************************************************
 *
 *  Name:	  ProcessUserPrint
 *
 *  Description:  This routine handles the user print (WM_USER_PRINT)
 *		  message. We're running thread 2 here because it can be
 *		  a long operation. The device context and presentation
 *		  space are created here, then one of three functions
 *		  is called to print depending on whether we have a
 *		  bitmap, metafile or text document.
 *
 *  Parameters :  HWND		 window handle
 *		  PMAIN_PARM	 global application data
 *
 *  API's      :  DevOpenDC
 *		  GpiCreatePS
 *		  DevEscape
 *		  GpiAssociate
 *		  GpiDestroyPS
 *		  DevCloseDC
 *
 *  Return     :  VOID
 *
 *
 *************************************************************************/

VOID ProcessUserPrint( HWND hwnd, PMAIN_PARM pmp)
{
   DEVOPENSTRUC	   dos;
   USHORT	   us;
   VOID		   (*pfnPrintDocument)(HWND, PMAIN_PARM);
   CHAR		   achDriverName[DRIVERNAME_LENGTH];
   CHAR		   achQueueProcParams[8];
   BOOL		   fResult;

   /* Build the device context data for DevOpenDC */
   memset((PVOID)&dos, 0, sizeof(dos));

   strcpy(achDriverName, pmp->achDriverName);
   achDriverName[strcspn(achDriverName, ".")] = '\0';
   sprintf(achQueueProcParams, "COP=%d", pmp->usCopies);

   dos.pszLogAddress = pmp->achQueueName;
   dos.pszDriverName = (PSZ)achDriverName;
   dos.pdriv = pmp->pDriverData;
   dos.pszDataType = (PSZ)"PM_Q_STD";
   dos.pszComment = pmp->pszTitle;
   dos.pszQueueProcParams = (PSZ)achQueueProcParams;

   /*
    * Switch on whether we are writing text, a bitmap
    * or a metafile into the PS, and process
    */
   switch (pmp->ulMode)
   {
   case MODE_TEXT:
	/* Seek to beginning of user-specified file */
      fseek(pmp->f, 0, SEEK_SET);
      pfnPrintDocument = PrinterPaginate;
      break;

   case MODE_BITMAP:
      pfnPrintDocument = PrinterBitblt;

      /*
       * Don't create PM_Q_STD bitmaps because of color conversion
       * problems if printing color bitmaps on a monochrome device.
       */
      dos.pszDataType = (PSZ)"PM_Q_RAW";
      break;

   case MODE_METAFILE:
      pfnPrintDocument = PrinterPlayMetaFile;
      break;

   default:
      pmassert(pmp->hab, FALSE);
      return;
   }

   /* Create DC for the printer */
   pmp->hdcPrinter = DevOpenDC( pmp->hab,
				OD_QUEUED,
				"*",
				9L,
				(PVOID)&dos,
				(HDC)NULLHANDLE );

   /* Create PS for the printer */
   pmp->hpsPrinter = GPI_ERROR;
   if (pmp->hdcPrinter != DEV_ERROR)
   {
      pmp->hpsPrinter = GpiCreatePS( pmp->hab,
				     pmp->hdcPrinter,
				     (PSIZEL)&pmp->sizelPage,
				     PU_TWIPS | GPIA_NOASSOC );
      pmassert( pmp->hab, pmp->hpsPrinter != GPI_ERROR );

      if (pmp->hpsPrinter != GPI_ERROR)
      {
	 /*
	  * Didn't use GPI_ASSOC to create the PS so that in the case
	  * of a metafile, we can first play the metafile to reset the
	  * PS before it is associated to the DC.
	  */
	 if (pmp->ulMode == MODE_METAFILE)
	 {
	    PrinterResetMetaFile(hwnd, pmp);
	 }

	 /* Now associate the presentation space */
	 fResult = GpiAssociate(pmp->hpsPrinter, pmp->hdcPrinter);
	 pmassert(pmp->hab, fResult);

	 /* Issue STARTDOC to begin printing */
	 DevEscape( pmp->hdcPrinter,
		    DEVESC_STARTDOC,
		    (LONG)strlen(pmp->szFilename),
		    (PBYTE)pmp->szFilename,
		    (PLONG)NULL, (PBYTE)NULL );

	 /* Print the document */
	 (*pfnPrintDocument)(hwnd, pmp);

	 /*
	  * Issue ABORTDOC if user cancelled print job, or ENDDOC
	  * to for normal job termination.
	  */
	 DevEscape( pmp->hdcPrinter,
		    pmp->fCancel ? DEVESC_ABORTDOC : DEVESC_ENDDOC,
		    0L, (PBYTE)NULL, (PLONG)NULL, (PBYTE)NULL );

	 /* Release and destroy the PS */
	 GpiAssociate( pmp->hpsPrinter, (HDC)NULLHANDLE );
	 GpiDestroyPS( pmp->hpsPrinter );
      }
   }

   /* Clean up the DC */
   if (pmp->hdcPrinter != DEV_ERROR)
   {
      DevCloseDC(pmp->hdcPrinter);
      pmp->hdcPrinter = DEV_ERROR;
   }

   return;
}

/*************************************************************************
 *
 *  Name       :  PrinterBitblt
 *
 *  Description:  Load a bitmap into a memory DC using the WM_USER_LOAD_BITMAP
 *		  processing. Calculate what size to print the bitmap in order
 *		  to preserve the bitmap aspect ratio and still have it fit on
 *		  the page. Then bit blit into the printer PS, allowing the
 *		  device presentation driver to do the stretch blit.
 *
 *  Parameters :  HWND		 window handle
 *		  PMAIN_PARM	 global application data
 *
 *  API's      :  WinSendMsg
 *		  GpiQueryPS
 *		  GpiConvert
 *		  GpiBitblt
 *		  GpiSetBitmap
 *		  GpiDeleteBitmap
 *		  GpiAssociate
 *		  GpiDestroyPS
 *		  DevCloseDC
 *
 *  Return     :  VOID
 *
 *************************************************************************/

VOID PrinterBitblt(HWND hwnd, PMAIN_PARM pmp)
{
    BITMAPINFOHEADER bmi;
    POINTL  aptl[4];
    LONG    lResult;
    double  xRatio, yRatio;

    /* Clear our variables to ensure USER_LOAD_BITMAP starts fresh */
    pmp->hdcMem4Printer = (HDC)NULLHANDLE;
    pmp->hpsMem4Printer = (HPS)NULLHANDLE;
    pmp->hbm4Printer = (HBITMAP)NULLHANDLE;

    /* Send message to create a printer PS with our bitmap set into it */
    WinSendMsg(hwnd, WM_USER_LOAD_BITMAP, (MPARAM)hwnd,
	       (MPARAM)HDCMEM_COMPATIBILITY_PRINTER);

    /* Set up source and target blt rectangles */
    aptl[0].x = aptl[0].y = 0L;
    aptl[2].x = aptl[2].y = 0L;

    aptl[3].x = pmp->cxBitmap;
    aptl[3].y = pmp->cyBitmap;

    if (pmp->cxBitmapRes && pmp->cyBitmapRes)
    {
	/*
	 * Since the intended resolution for the bitmap is known,
	 * the actual size of the bitmap can be calculated, in
	 * twips (1440 twips/inch).
	 */
	aptl[1].x = ((double)pmp->cxBitmap)/((double)pmp->cxBitmapRes)
		    * 1440.0;
	aptl[1].y = ((double)pmp->cyBitmap)/((double)pmp->cyBitmapRes)
		    * 1440.0;
    }
    else
    {
       /*
	* Assume the bitmap aspect ratio is 1.0 and fit this to
	* the size of the presentation space
	*/
       GpiQueryPS(pmp->hpsPrinter, (PSIZEL)&aptl[1]);
       xRatio = ((double)aptl[1].x) / ((double)aptl[3].x);
       yRatio = ((double)aptl[1].y) / ((double)aptl[3].y);
       if (yRatio > xRatio)
	  aptl[1].y = xRatio * (double)aptl[3].y;
       else
	  aptl[1].x = yRatio * (double)aptl[3].x;
    }

    /* Convert twips to device pels */
    GpiConvert( pmp->hpsPrinter, CVTC_WORLD, CVTC_DEVICE, 1L,
		(PPOINTL)&aptl[1] );

    /* Translate the position of the bitmap on the page here */

    /* Bitblt the source bitmap onto the printer presentation space */
    lResult = GpiBitBlt( pmp->hpsPrinter,
			 pmp->hpsMem4Printer,
			 4L,
			 (PPOINTL)aptl,
			 ROP_SRCCOPY,
			 BBO_IGNORE );
    pmassert( pmp->hab, lResult == GPI_OK );

    /* Clean up bitmap, memory PS and memory DC */
    GpiSetBitmap( pmp->hpsMem4Printer, (HBITMAP)NULLHANDLE );
    GpiDeleteBitmap( pmp->hbm4Printer );
    GpiAssociate( pmp->hpsMem4Printer, (HDC)NULLHANDLE );
    GpiDestroyPS( pmp->hpsMem4Printer );
    DevCloseDC( pmp->hdcMem4Printer );

    return;
}  /* End of PrinterBitblt */

/*************************************************************************
 *
 *  Name:      :  PrinterPaginate
 *
 *  Description:  This function uses WM_USER_PAGINATE processing to
 *		  print text to printer There is a check of the fCancel
 *		  Boolean in the main parameters after paginating each
 *		  page. If this Boolean becomes true, then printing the
 *		  text is aborted.
 *
 *  API's      :  GpiCreateLogFont
 *		  GpiSetCharSet
 *		  WinSendMsg
 *		  DevEscape
 *		  GpiSetCharSet
 *		  GpiDeleteSetID
 *
 *  Return     :  VOID
 *
 *************************************************************************/
VOID PrinterPaginate(HWND hwnd, PMAIN_PARM pmp)
{
   ULONG   ul;
   BOOL	   fResult;

   /*
    * Create a logical font for the printer and set it
    * into the PS
    */
   ul = (ULONG)GpiCreateLogFont( pmp->hpsPrinter,
				 (PSTR8)NULL,
				 1L,
				 (PFATTRS)&pmp->fontdlg.fAttrs );
   pmassert(pmp->hab, ul == FONT_MATCH);

   fResult = GpiSetCharSet( pmp->hpsPrinter, 1L );
   pmassert( pmp->hab, fResult );

   do
   {
      ul = (ULONG)WinSendMsg( hwnd,
			      WM_USER_PAGINATE,
			      (MPARAM)hwnd,
			      (MPARAM)FLAGS_PAGINATE_PRINTER );
      if ( ul == PAGINATE_NOT_EOF )
      {
	 DevEscape( pmp->hdcPrinter,
		    DEVESC_NEWFRAME,
		    0L,
		    (PBYTE)NULL,
		    (PLONG)NULL,
		    (PBYTE)NULL );
      }
      else
      {
	   /* End of file; Break out of this loop and return to do enddoc */
	  break;
      }
    } while (  !pmp->fCancel );

   /* Clean up logical font id */
   GpiSetCharSet(pmp->hpsPrinter, 0L);
   GpiDeleteSetId(pmp->hpsPrinter, 1L);

   return;
}  /* End of PrinterPaginate */

/*************************************************************************
 *
 *  Name       :  PrinterPlayMetafile
 *
 *  Description:  Play metafile into printer PS without the reset and
 *		  suppress options.
 *
 *  Parameters :  HWND		 window handle
 *		  PMAIN_PARM	 global application data
 *
 *  API's      :  GpiPlayMetaFile
 *
 *  Return     :  VOID
 *
 *  reference  :  Graham C.E. Winn, "OS/2 PM GPI", VNR Computer Library
 *
 *************************************************************************/
VOID PrinterPlayMetaFile(HWND hwnd, PMAIN_PARM pmp)
{
   LONG	   alOptions[ LEN_PLAYMETAFILEOPTS ];
   BYTE	   abDesc[ LEN_PLAYMETAFILEDESCS ];
   LONG	   lSegments;
   BOOL	   fResult;

   lSegments = 0;

   memset(alOptions, 0, sizeof(alOptions));
   alOptions[ PMF_SEGBASE	  ] = 0L;
   alOptions[ PMF_LOADTYPE	  ] = LT_NOMODIFY;
   alOptions[ PMF_RESOLVE	  ] = RS_DEFAULT;
   alOptions[ PMF_LCIDS		  ] = LC_LOADDISC;
   alOptions[ PMF_RESET		  ] = RES_NORESET;
   alOptions[ PMF_SUPPRESS	  ] = SUP_NOSUPPRESS;
   alOptions[ PMF_COLORTABLES	  ] = CTAB_REPLACE;
   alOptions[ PMF_COLORREALIZABLE ] = CREA_NOREALIZE;
   alOptions[ PMF_DEFAULTS	  ] = DDEF_LOADDISC;

   fResult = (BOOL)GpiPlayMetaFile( pmp->hpsPrinter,
				    pmp->hmf,
				    LEN_PLAYMETAFILEOPTS,
				    alOptions,
				    (PLONG)&lSegments,
				    LEN_PLAYMETAFILEDESCS,
				    (PSZ)abDesc );
   pmassert( pmp->hab, fResult );

   return;
}  /* End of PrinterPlayMetafile */

/*************************************************************************
 *
 *  Function   :  PrinterResetMetafile
 *
 *  Description:  Reset the printer PS with page units, size and default viewing
 *		  transform specified in the metafile.	Note that the PS must
 *		  not be associated with the DC if this is an PM_Q_STD print job.
 *
 *  Parameters :  HWND		 window handle
 *		  PMAIN_PARM	 global application data
 *
 *  API's      :  GpiPlayMetaFile
 *
 *  Return     :  VOID
 *
 *  reference  :  Graham C.E. Winn, "OS/2 PM GPI", VNR Computer Library
 *
 *************************************************************************/
VOID PrinterResetMetaFile(HWND hwnd, PMAIN_PARM pmp)
{
   LONG	       alOptions[ LEN_PLAYMETAFILEOPTS ];
   BYTE	       abDesc[ LEN_PLAYMETAFILEDESCS ];
   LONG	       lSegments;
   BOOL	       fResult;

   lSegments = 0L;

   memset(alOptions, 0, sizeof(alOptions));
   alOptions[ PMF_SEGBASE	  ] = 0L;
   alOptions[ PMF_LOADTYPE	  ] = LT_NOMODIFY;
   alOptions[ PMF_RESOLVE	  ] = RS_DEFAULT;
   alOptions[ PMF_LCIDS		  ] = LC_LOADDISC;
   alOptions[ PMF_RESET		  ] = RES_RESET;
   alOptions[ PMF_SUPPRESS	  ] = SUP_SUPPRESS;
   alOptions[ PMF_COLORTABLES	  ] = CTAB_DEFAULT;
   alOptions[ PMF_COLORREALIZABLE ] = CREA_DEFAULT;
   alOptions[ PMF_DEFAULTS	  ] = DDEF_DEFAULT;

   fResult = (BOOL)GpiPlayMetaFile( pmp->hpsPrinter,
				    pmp->hmf,
				    LEN_PLAYMETAFILEOPTS,
				    alOptions,
				    (PLONG)&lSegments,
				    LEN_PLAYMETAFILEDESCS,
				    (PSZ)abDesc );
   pmassert( pmp->hab, fResult );

   return;
}  /* End of PrinterResetMetaFile */

/*************************************************************************
 *
 *   Function	:  FindQueue
 *
 *   Description:  Finds the queue name in the PRQINFO3 structure.
 *
 *   Parameters :  PSZ		pointer to the name of the queue
 *		   PPRQINFO3	pointer to queue selection structure 1
 *		   SHORT	number of queue names to compare
 *
 *   API's      :  [none]
 *
 *************************************************************************/
LONG FindQueue(PSZ pszQueueName, PPRQINFO3 pQueueInfo, LONG lCount)
{
   LONG i;

   for (i = 0L; i < lCount; ++i)
   {
      if ( !strcmp( pszQueueName, pQueueInfo[i].pszName ) )
	 return i;
   }

   return -1L;		       /*  if not found	 */
}  /* End of FindQueue */

/*************************************************************************
 *  Function   :  SortQueues
 *
 *  Description:  Compare routine for sorting the queue names table.
 *
 *  Parameters :  PPRQINFO3    pointer to queue selection structure 1
 *		  PPRQINFO3    pointer to queue selection structure 2
 *
 *  API's      :  [none]
 *
 *  Return     :  result of comparison
 *
 *************************************************************************/
static int _Optlink SortQueues(const void *pQueue1, const void *pQueue2)
{
return(strcmp(((PPRQINFO3)pQueue1)->pszComment,
	      ((PPRQINFO3)pQueue2)->pszComment));
} /* end of SortQueues() */
/***************************  End of prtprint.c ****************************/
