/***************************************************************************
 *
 * File name   :  prtsamp.c
 *
 * Description :  This program demonstrates how to display and print ascii
 *		  text, meta files and bitmaps.	 It also shows how to use
 *		  the standard file dialog and the standard font dialog,
 *		  how to display and change printer properties and how to
 *		  display and select printer queues.
 *
 *		  This source file contains the following functions:
 *
 *		  main()
 *		  WinProc(hwnd, msg, mp1, mp2)
 *		  CalibrateScrollBar(pmp)
 *
 * Required
 *    files    :
 *		  PRTSAMP.C	   - Source code
 *		  PRTCREAT.C	   - Source code
 *		  PRTMENU.C	   - Source code
 *		  PRTOBJ.C	   - Source code
 *		  PRTPRINT.C	   - Source code
 *		  PRTPROF.C	   - Source code
 *		  PRTSAMP.H	   - Application header file
 *		  PMASSERT.H	   - Application header file
 *		  PRTSDLG.H	   - Application header file
 *		  PRTSHLP.H	   - Application header file
 *		  PRTSAMP.RC	   - Resource file
 *		  PRTSAMP.DLG	   - Dialog file
 *		  PRTSAMP.IPF	   - Help file
 *		  PRTSAMP.MAK	   - Make file for this program
 *		  PRTSAMP.BMP	   - Bitmap file
 *		  PRTSAMP.TXT	   - Text file
 *		  PRTSAMP.MET	   - Meta file
 *		  PRTSAMP.ICO	   - Print sample icon
 *
 *		  OS2.H		   - Presentation Manager include file
 *		  STDIO.H	   - Standard input/output c include file
 *		  STDLIB.H	   - Standard c include file
 *		  STRING.H	   - String handling function declarations
 *		  CTYPE.H
 *		  STDDEF.H
 *		  PROCESS.H
 *		  SYS\TYPES.H
 *		  SYS\STAT.H
 *
 *  Concepts   :  listboxes, directory search
 *
 *  API's      :  GpiAssociate
 *		  GpiConvert
 *		  GpiDeleteBitmap
 *		  GpiDeleteMetaFile
 *		  GpiDestroyPS
 *		  GpiDrawChain
 *		  GpiSetDefaultViewMatrix
 *
 *		  WinAddSwitchEntry
 *		  WinAssociateHelpInstance
 *		  WinBeginPaint
 *		  WinCreateHelpInstance
 *		  WinCreateMsgQueue
 *		  WinCreateStdWindow
 *		  WinDestroyHelpInstance
 *		  WinDestroyMsgQueue
 *		  WinDestroyWindow
 *		  WinDispatchMsg
 *		  WinEndPaint
 *		  WinFillRect
 *		  WinGetMsg
 *		  WinInitialize
 *		  WinInvalidateRect
 *		  WinIsWindowEnabled
 *		  WinLoadString
 *		  WinMessageBox
 *		  WinPostMsg
 *		  WinQuerySysPointer
 *		  WinQueryWindowRect
 *		  WinQueryWindowULong
 *		  WinRegisterClass
 *		  WinRemoveSwitchEntry
 *		  WinSendMsg
 *		  WinSetPointer
 *		  WinSetWindowText
 *		  WinTerminate
 *
 * Required
 *   libraries :  OS2386.LIB	 - Presentation Manager/OS2 library
 *		  C library
 *
 * Required
 *   programs  :  C Compiler
 *		  Linker
 *		  Resource Compiler
 *
 *  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 files.  */
#define INCL_DOSFILEMGR
#define INCL_WINSTDFILE
#define INCL_WINSTDFONT
#define INCL_WINWINDOWMGR
#define INCL_WINSCROLLBARS
#define INCL_WINDIALOGS
#define INCL_WINERRORS
#define INCL_WINPOINTERS
#define INCL_WININPUT
#define INCL_WINMENUS
#define INCL_WINHELP
#define INCL_WINTIMER
#define INCL_WINSYS
#define INCL_WINFRAMEMGR
#define INCL_GPITRANSFORMS
#define INCL_GPISEGMENTS
#define INCL_GPIMETAFILES
#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 "prtshlp.h"
#include "pmassert.h"

/**************************************************************************
 *
 *  Name       : main()
 *
 *  Description: Initializes the application, gets and dispatches messages
 *		 for the client window on thread 1 and terminates the
 *		 application when a WM_QUIT message is received.  It then
 *		 saves the configuration in os2.ini, destroys all OS/2
 *		 PM resources, and terminates.
 *
 *		 The following routine is the Presentation Manager program
 *		 main body. The main body of a PM program is concerned with
 *		 associating the application with the Presentation Manager
 *		 system, creating its message queue, registering and displaying
 *		 its main window, servicing its message queue during the time
 *		 that the application is active, and disassociating the
 *		 application from PM when the user is finished with the
 *		 it. The remaining parts of this source module that are
 *		 concerned with the Presentation Manager are the application's
 *		 window procedures (main window procedure, child window
 *		 procedures, and dialog window procedures) that process the
 *		 messages associated with the application's various windows.
 *
 *  Concepts   : - obtains anchor block handle and creates message
 *		     queue
 *		 - creates the main frame window which creates the
 *		     main client window
 *		 - polls the message queue via Get/Dispatch Msg loop
 *		 - upon exiting the loop, exits
 *
 *  API's      :  WinInitialize
 *		  WinCreateMsgQueue
 *		  WinRegisterClass
 *		  WinLoadString
 *		  WinCreateHelpInstance
 *		  WinMessageBox
 *		  WinCreateStdWindow
 *		  WinQueryWindowULong
 *		  WinAssociateHelpInstance
 *		  WinAddSwitchEntry
 *		  WinGetMsg
 *		  WinDispatchMsg
 *		  WinRemoveSwitchEntry
 *		  WinDestroyHelpInstance
 *		  WinDestroyWindow
 *		  WinDestroyMsgQueue
 *		  WinTerminate
 *
 *  Parameters :  [none]
 *
 *  Return     :  Always returns zero.	The ANSI standard requires that
 *		  main be declared as an INT.
 *
 *************************************************************************/
INT main(VOID)
{
					/* Define variables by type	*/
   BOOL	      bOK;			/* Boolean used for return code */
   HAB	      hab;			/* PM anchor block handle	*/
   HMQ	      hmq;			/* Message queue handle		*/
   QMSG	      qmsg;			/* Message from message queue	*/
   ULONG      ulCtlData;		/* Standard window create flags */
   HWND	      hwndFrame;		/* Frame window handle		*/
   HWND	      hwndClient;		/* Client area window handle	*/
   HWND	      hwndHelp;			/* Help window handle		*/
   PMAIN_PARM pmp;			/* Main parameters structure	*/
   SWCNTRL    swctl;			/* Struct to add to window list */
   HSWITCH    hsw;			/* Window list handle ret'd     */
   HELPINIT   hmi;			/* Struct for help creation	*/
   CHAR	      szWork[ LEN_WORKSTRING ]; /* General use string work area */
   PSZ	      pszTitle;			/* Pointer to program title	*/


   /* normal PM application startup */
   hab = WinInitialize( 0 );
   hmq = WinCreateMsgQueue( hab, 0 );

   /*
    * Register a class for my client window's behavior.
    * This class has enough extra window words to hold a pointer.
    */
   bOK = WinRegisterClass(
		     hab,
		     CLASSNAME,
		     WinProc,
		     CS_SIZEREDRAW,
		     sizeof( PMAIN_PARM ));
   /*
    * Ensure WinRegisterClass worked ok; if not, present a message box.
    * ( See pmassert.h. )
    */
   pmassert( hab, bOK );


   /* Load program title and allocate a local copy. Use it in help creation. */
   WinLoadString( hab, (HMODULE)NULLHANDLE,
		  PROGRAM_TITLE, LEN_WORKSTRING, szWork );
   pszTitle = strdup( szWork );

   /* Create help. */
   memset( &hmi, 0, sizeof( hmi ));
   hmi.cb		  = sizeof( HELPINIT );
   hmi.phtHelpTable	  = (PVOID)(0xffff0000 | ID_PRTSAMP);
   hmi.pszHelpWindowTitle = pszTitle;
   hmi.pszHelpLibraryName = HELP_FILE;
   hwndHelp = WinCreateHelpInstance( hab, &hmi );
   if( ! hwndHelp )
   {
      WinLoadString(hab, (HMODULE)NULLHANDLE,
		    ERRMSG_NO_HELP, LEN_WORKSTRING, szWork);
      WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, szWork, pszTitle, 0, MB_CANCEL);
   }

   /* done with local copy of title text */
   free( pszTitle );


   /* flags to control creation of window; use on call to WinCreateStdWindow */
   ulCtlData = FCF_SYSMENU | FCF_TITLEBAR | FCF_SIZEBORDER
			   | FCF_MINMAX | FCF_MENU | FCF_ACCELTABLE
			   | FCF_VERTSCROLL;

   hwndFrame = WinCreateStdWindow( HWND_DESKTOP,
				   WS_VISIBLE | FS_ICON | FS_SHELLPOSITION,
				   &ulCtlData,
				   CLASSNAME,
				   NULL,    /* title text set in prtcreat.c */
				   0,	    /* client style		    */
				   (HMODULE)NULLHANDLE,	 /* resources in exe */
				   ID_PRTSAMP,
				   &hwndClient	);

   pmassert( hab, hwndFrame );
   pmassert( hab, hwndClient );

   /* create.c placed pointer to main params in client's window words; get it */
   pmp = (PMAIN_PARM) WinQueryWindowULong( hwndClient, QWL_USER );
   /* store help instance handle in main parameter block */
   pmp->hwndHelp  = hwndHelp;

   bOK = WinAssociateHelpInstance( hwndHelp, hwndFrame );
   pmassert( hab, bOK );

   /* Add program to task list. */
   memset( &swctl, 0, sizeof( SWCNTRL ));
   strcpy( swctl.szSwtitle, pmp->pszTitle );
   swctl.hwnd	       = hwndFrame;
   swctl.uchVisibility = SWL_VISIBLE;
   swctl.fbJump	       = SWL_JUMPABLE;
   hsw = WinAddSwitchEntry( &swctl );

   /* message loop */
   while( WinGetMsg( hab, &qmsg, (HWND)NULLHANDLE, 0, 0 ))
   {
     WinDispatchMsg( hab, &qmsg );
   }

   /* clean up */
   WinRemoveSwitchEntry( hsw );
   WinDestroyHelpInstance( hwndHelp );
   WinDestroyWindow( hwndFrame );
   WinDestroyMsgQueue( hmq );
   WinTerminate( hab );

   DosExit( EXIT_PROCESS, 0 );
   return 0;
}  /* End of main() */

/*************************************************************************
 *
 * Name	      : WinProc(hwnd, msg, mp1, mp2)
 *
 * Description: Processes the messages sent to the main client
 *		window.	 This routine processes the basic
 *		messages all client windows should process.
 *
 * Concepts   : This procedure provides service routines for the general
 *		PM events (messages) that PM sends to the window, as well
 *		as the user initiated events (messages) that are generated
 *		when the user selects the action bar and pulldown menu
 *		controls or the corresponding keyboard accelerators.
 *
 *		The switch statement shown below distributes the window
 *		messages to the respective message service routines, which
 *		are set apart by the case statements.  The window
 *		procedures must provide an appropriate service routine for
 *		its end user initiated messages, as well as the general PM
 *		messages (like the WM_CLOSE message). If a message is sent
 *		to this procedure for which there is no programmed case
 *		clause (i.e., no service routine), the message is defaulted
 *		function WinDefWindowProc, where it is disposed of by PM.
 *
 *		Time-consuming tasks are posted to the object window where
 *		they can be performed without the 1/10 second turnaround
 *		time imposed on the client window. When the object window
 *		is busy, the client window is usually disabled. The object
 *		window then posts an acknowledgement
 *		back to the client when the lengthy task is completed.
 *
 *		Cases under the big switch appear in alphabetical order.
 *
 *  API's     :   WinLoadString
 *		  WinMessageBox
 *		  WinQueryWindowULong
 *		  WinSendMsg
 *		  WinPostMsg
 *		  WinIsWindowEnabled
 *		  WinSetPointer
 *		  WinQuerySysPointer
 *		  WinBeginPaint
 *		  WinQueryWindowRect
 *		  WinFillRect
 *		  WinEndPaint
 *		  WinInvalidateRect
 *		  WinSetWindowText
 *		  GpiDrawChain
 *		  GpiConvert
 *		  GpiSetDefaultViewMatrix
 *		  GpiAssociate
 *		  GpiDestroyPS
 *		  GpiDeleteBitmap
 *		  GpiDeleteMetaFile
 *
 * Parameters : HWND	     window handle
 *		ULONG	     message
 *		MPARAM	     message parameter 1
 *		MPARAM	     message parameter 2
 *
 * Result     : MRESULT	     message result
 *
 *************************************************************************/
MRESULT EXPENTRY WinProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
   BOOL		       bOK;
   PMAIN_PARM	       pmp;
   HPS		       hps;
   RECTL	       rectl;
   PCHAR	       pch;
   CHAR		       szWork[ LEN_WORKSTRING ];
   SHORT	       cx, cy;
   POINTL	       pointl;
   SHORT	       sStep;
   float	       floatWork;
   ULONG	       ulWork;

   switch(msg)
   {
   case WM_CLOSE:
      /* obtain the main parameter pointer from window words */
      pmp = (PMAIN_PARM) WinQueryWindowULong( hwnd, QWL_USER );

      if( pmp->fBusy )
      {
	 /* OK to close when object window is busy?  */
	 WinLoadString( pmp->hab, (HMODULE)NULLHANDLE,
		   ERRMSG_CLOSE_QUESTION, LEN_WORKSTRING, szWork );
	 ulWork = WinMessageBox( HWND_DESKTOP, pmp->hwndFrame,
		      szWork, pmp->pszTitle, 0, MB_YESNOCANCEL );

	 if( ulWork == MBID_YES )
	 {
	    /* close down the application in spite of being busy */
	    pmp->fCancel = TRUE;
	    /* disable client during exit */
	    WinSendMsg( hwnd, WM_USER_DISABLE_CLIENT, (MPARAM)0 , (MPARAM)0  );
	    /* start a watchdog timer to ensure a timely death */
	    WinStartTimer( pmp->hab, hwnd, ID_DEATH_TIMER, LEN_DEATH_TIMER );
	    /* Tell object window to close, quit, and post me a WM_QUIT */
	    WinPostMsg( pmp->hwndObject, WM_USER_CLOSE, (MPARAM)hwnd, (MPARAM)0 );
	 }
      }
      else
      {
	 /* not busy, so initiate closure by telling object window to close */
	 WinPostMsg( pmp->hwndObject, WM_USER_CLOSE, (MPARAM)hwnd, (MPARAM)0 );
      }
      return (MRESULT)NULL;


   case WM_COMMAND:
      /* do menu activities; see menu.c */
      return Menu( hwnd, msg, mp1, mp2 );


   case WM_CREATE:
      /*
       * Do one-time, startup processing in PRTCREAT.C.
       * This function allocates the pmp, a pointer to the program's
       * main parameters. See the declaration of this block of
       * parameters in PRTSAMP.H, the MAIN_PARM structure.
       *
       * The Create() function allocates this structure and
       * begins to initialize it. Throughout all the code, the pmp->
       * pointer is usually obtainable with a call to WinQueryWindowULong.
       * Window word space for this pointer was reserved on the call
       * to WinRegisterClass.
       *
       * Create() allocates, initializes, and stores the pmp pointer
       * in the client window words. It then starts thread 2 of the
       * application on which the object window operates. The pmp
       * pointer is passed to thread 2 with _beginthread in PRTCREAT.C.
       * The pmp is passed to the object window on the call to
       * WinCreateWindow in PRTOBJ.C. Finally, the object window stores
       * the pmp in its window words under the WM_CREATE case of the
       * ObjectWinProc in PRTOBJ.C.
       *
       */
      Create( hwnd );
      return (MRESULT)NULL;


   case WM_MOUSEMOVE:
      /* display which pointer? -- could query pmp->fBusy or... */
      if( WinIsWindowEnabled( hwnd ))
      {
	 /* not disabled; display regular pointer */
	 WinSetPointer( HWND_DESKTOP,
			WinQuerySysPointer( HWND_DESKTOP, SPTR_ARROW, FALSE ));
      }
      else
      {
	 /* disabled; display hourglass because I'm busy */
	 WinSetPointer( HWND_DESKTOP,
			WinQuerySysPointer( HWND_DESKTOP, SPTR_WAIT, FALSE ));
      }
     return (MRESULT) 1;



   case WM_NACK_BITMAP_NOT_SUPPORTED:
      /*
       * Object window does not support this format of bitmap -
       * show a message box.
       */
      pmp = (PMAIN_PARM) WinQueryWindowULong( hwnd, QWL_USER );
      WinSendMsg( hwnd, WM_USER_ENABLE_CLIENT, (MPARAM)0, (MPARAM)0 );
      bOK = WinLoadString( pmp->hab, (HMODULE)0,
		   ERRMSG_BITMAP_NOT_SUPPORTED, LEN_WORKSTRING, szWork );
      pmassert( pmp->hab, bOK );
      WinMessageBox( HWND_DESKTOP, pmp->hwndFrame,
		     szWork, pmp->pszTitle, 0, MB_CANCEL );

      pmp->ulNextMode	     = MODE_UNKNOWN;
      pmp->szNextFilename[0] = 0;
      WinPostMsg( hwnd, WM_USER_NEW_MODE, (MPARAM)0, (MPARAM)0 );
      return (MRESULT) 0;


   case WM_NACK_DEFAULT_FONT:
      /*
       * On the call to GpiCreateLogFont in PRTOBJ.C, the GPI returned
       * some font other than a perfect match. Warn user that a default
       * face is in use. This can happen for Postscript and PCL 5 printer
       * users.
       *
       * Only warn the user once after each visit to the font dialog.
       *
       * Don't re-enable the client yet. That message is still to come.
       */
      pmp = (PMAIN_PARM) WinQueryWindowULong( hwnd, QWL_USER );
      if( pmp->fVisitedFontDialog )
      {
	 /* only one warning per font dialog visit */
	 pmp->fVisitedFontDialog  = FALSE;

	 bOK = WinLoadString( pmp->hab, (HMODULE)0,
		      ERRMSG_DEFAULT_FONT, LEN_WORKSTRING, szWork );
	 pmassert( pmp->hab, bOK );
	 WinMessageBox( HWND_DESKTOP, pmp->hwndFrame,
			szWork, pmp->pszTitle, 0, MB_CANCEL );
      }
      return (MRESULT) 0;



   case WM_NACK_FILE_READING_ERROR:
      /*
       * Object window had a problem with reading the disk -
       * show a message box.
       */
      pmp = (PMAIN_PARM) WinQueryWindowULong( hwnd, QWL_USER );
      WinSendMsg( hwnd, WM_USER_ENABLE_CLIENT, (MPARAM)0, (MPARAM)0 );
      bOK = WinLoadString( pmp->hab, (HMODULE)NULLHANDLE,
			   ERRMSG_READ_ERROR, LEN_WORKSTRING, szWork );
      pmassert( pmp->hab, bOK );
      WinMessageBox( HWND_DESKTOP, pmp->hwndFrame,
		     szWork, pmp->pszTitle, 0, MB_CANCEL );
      pmp->ulNextMode	     = MODE_UNKNOWN;
      pmp->szNextFilename[0] = 0;
      WinPostMsg( hwnd, WM_USER_NEW_MODE, (MPARAM)0, (MPARAM)0 );
      return (MRESULT) 0;


   case WM_PAINT:
      pmp = (PMAIN_PARM) WinQueryWindowULong( hwnd, QWL_USER );

      /* current mode of the program affects window painting */
      switch( pmp->ulMode )
      {
      case MODE_UNKNOWN:
	 /* blank the client window with background color; use a cache ps */
	 hps = WinBeginPaint( hwnd, (HPS) 0, &rectl );
	 pmassert( pmp->hab, hps );
	 bOK = WinFillRect( pmp->hpsClient, &rectl, SYSCLR_WINDOW );
	 pmassert( pmp->hab, bOK );
	 WinEndPaint( hps );
	 return (MRESULT) 0;

      case MODE_TEXT:
	 if( pmp->fBusy )
	 {
	    /*
	     * Object window is busy with the client PS drawing into
	     * retained segments. Use a cached micro PS to merely
	     * fill the client window with a background wash.
	     *
	     * Proper painting of the text will occur in due time
	     * because the WM_USER_ACK case below will
	     * invalidate the client window and force a paint.
	     * The object window won't be busy then.
	     */
	    hps = WinBeginPaint( hwnd, (HPS) 0, &rectl );
	    pmassert( pmp->hab, hps );
	    bOK = WinFillRect( hps, &rectl, SYSCLR_WINDOW );
	    pmassert( pmp->hab, bOK );
	    WinEndPaint( hps );
	 }
	 else
	 {
	    /* PS not busy. Use GpiDrawChain to repaint the text */
	    hps = WinBeginPaint( hwnd, (HPS)pmp->hpsClient, &rectl );
	    pmassert( pmp->hab, hps );
	    bOK = WinFillRect( pmp->hpsClient, &rectl, SYSCLR_WINDOW );
	    pmassert( pmp->hab, bOK );
	    /*
	     * GpiDrawChain re-plays the GpiCharString orders that were
	     * retain in the WM_USER_PAGINATE case in prtobj.c
	     */
	    bOK = GpiDrawChain( pmp->hpsClient );
	    pmassert( pmp->hab, bOK );
	    bOK = WinEndPaint( pmp->hpsClient );
	    pmassert( pmp->hab, bOK );
	 }
	 return (MRESULT) 0;

      case MODE_BITMAP:
	 if( pmp->hbm )
	 {
	    WinSendMsg( hwnd, WM_USER_DISABLE_CLIENT, (MPARAM)0, (MPARAM)0 );
	    WinPostMsg( pmp->hwndObject, WM_USER_PAINT_BITMAP,
			(MPARAM)hwnd, (MPARAM)0 );
	 }
	 /* must call default window proc for window validation */
	 break;

      case MODE_METAFILE:
	 hps = WinBeginPaint( hwnd, (HPS) 0, &rectl );
	 pmassert( pmp->hab, hps );
	 WinFillRect( hps, &rectl, SYSCLR_WINDOW );
	 if( pmp->hmf )
	 {
	    WinSendMsg( hwnd, WM_USER_DISABLE_CLIENT, (MPARAM)0, (MPARAM)0 );
	    WinPostMsg( pmp->hwndObject, WM_USER_PAINT_METAFILE,
			(MPARAM)hwnd, (MPARAM)0 );
	 }
	 WinEndPaint( hps );
	 return (MRESULT) 0;

      default:
	 pmassert( pmp->hab, NULL == "bad case in WM_PAINT" );
      }
      break;


   case WM_SIZE:
      /* Do size process if frame is not minimized and mode is text. */
      pmp = (PMAIN_PARM) WinQueryWindowULong( hwnd, QWL_USER );

      /*
       * If the object window is busy drawing, the GPI calls here can fail
       * because the PS is busy. Check for this situation and delay
       * the sizing operation by a few milliseconds.
       */
      if( pmp->fBusy )
      {
	  WinStartTimer( pmp->hab, hwnd, ID_SIZE_TIMER, LEN_SIZE_TIMER );
	  return (MRESULT) 0;
      }

      switch( pmp->ulMode )
      {
      case MODE_TEXT:
	 ulWork = WinQueryWindowULong( pmp->hwndFrame, QWL_STYLE );

	 if( !( ulWork & WS_MINIMIZED ))
	 {
	    /* Frame is not minimized. Get window size in pels. */
	    WinQueryWindowRect( hwnd, &rectl );

	    /* how many twips will fit into the client window now? */
	    bOK = GpiConvert( pmp->hpsClient, CVTC_DEVICE,
			      CVTC_WORLD, 2L, (PPOINTL)&rectl );
	    pmassert( pmp->hab, bOK );

	    /* compute client size in twips, store in pmp */
	    pmp->sizelClient.cx = rectl.xRight - rectl.xLeft;
	    pmp->sizelClient.cy = rectl.yTop   - rectl.yBottom;

	    /* reset scroll bar values; set thumbbutton position at zero */
	    pmp->sVScrollPos = 0;
	    CalibrateScrollBar( pmp );

	    /*
	     * Modify def-view matrix Y translation to home the displayed page.
	     * This depends on the current scaling value.
	     */
	    pmp->matlfDefView.lM32 = (LONG)(pmp->floatScale *
		(float) ( OFFSET_XY_TWIPS - pmp->sVScrollRange ));
	    bOK = GpiSetDefaultViewMatrix( pmp->hpsClient, 9,
			     &pmp->matlfDefView, TRANSFORM_REPLACE );
	    pmassert( pmp->hab, bOK );

	    /* force a paint */
	    WinInvalidateRect( hwnd, NULL, FALSE );
	 }
	 break;
      }
      return (MRESULT) 0;




   case WM_TIMER:
      pmp = (PMAIN_PARM) WinQueryWindowULong( hwnd, QWL_USER );
      switch( (ULONG) mp1 )
      {
      case ID_DEATH_TIMER:
	 /* object window never posted a quit in allotted time.
	 WinPostMsg( hwnd, WM_QUIT, 0, 0 );
	 break;
      case ID_SIZE_TIMER:
	 /* object window was busy with the PS before; try sizing now */
	 bOK = WinStopTimer( pmp->hab, hwnd, ID_SIZE_TIMER );
	 pmassert( pmp->hab, bOK );
	 WinSendMsg( hwnd, WM_SIZE, 0, 0 );
	 break;
      }
      return (MRESULT) 0;





   case WM_USER_ACK:
      /*
       * Object window is done processing lengthy task.
       * mp1 contains the WM_USER msg originally posted to the object window
       * mp2 may contain a result code, depending on mp1
       */
      pmp = (PMAIN_PARM) WinQueryWindowULong( hwnd, QWL_USER );

      /* reenable the client window */
      WinSendMsg( hwnd, WM_USER_ENABLE_CLIENT, (MPARAM)0, (MPARAM)0 );

      switch( (ULONG)mp1 )
      {
      case WM_USER_LOAD_TEXT:
	 /*
	  * Do size processing so that document will
	  * "home" in the client window.
	  */
	 WinSendMsg( hwnd, WM_SIZE, (MPARAM)0, (MPARAM)0 );
	 break;
      case WM_USER_PAGINATE:
	 switch( (ULONG)mp2  )
	 {
	 case PAGINATE_EOF:
	 case PAGINATE_EOF_PART_PAGE:
	    /* seek top of file */
	    fseek( pmp->f, 0, SEEK_SET );
	 }
	 WinInvalidateRect( hwnd, NULL, FALSE );
	 break;
      }

      return (MRESULT) 0;


   case WM_USER_DISABLE_CLIENT:
      /*
       * usually disable before posting a task to the object window
       * this message may be sent; disable menu action bar as well
       */
      pmp = (PMAIN_PARM) WinQueryWindowULong( hwnd, QWL_USER );
      pmp->fBusy = TRUE;
      WinEnableWindow( pmp->hwndClient,	 FALSE );
      WinEnableWindow( pmp->hwndMenubar, FALSE );
      WinEnableControl( pmp->hwndFrame, FID_VERTSCROLL,	 FALSE );
      WinEnableMenuItem( pmp->hwndMenubar, IDM_PAGEDOWN, FALSE );
      WinEnableMenuItem( pmp->hwndMenubar, IDM_VIEWFULL, FALSE );
      WinEnableMenuItem( pmp->hwndMenubar, IDM_VIEWHALF, FALSE );
      return (MRESULT)0;


   case WM_USER_ENABLE_CLIENT:
      /*
       * usually enable upon receipt of object window ack/nack
       * this message may be sent; enable menu actions if text mode
       */
      pmp = (PMAIN_PARM) WinQueryWindowULong( hwnd, QWL_USER );
      pmp->fBusy = FALSE;
      WinEnableWindow( pmp->hwndClient,	 TRUE );
      WinEnableWindow( pmp->hwndMenubar, TRUE );
      if( pmp->ulMode == MODE_TEXT )
      {
	 WinEnableControl( pmp->hwndFrame, FID_VERTSCROLL,  TRUE );
	 WinEnableMenuItem( pmp->hwndMenubar, IDM_PAGEDOWN, TRUE );
	 WinEnableMenuItem( pmp->hwndMenubar, IDM_VIEWFULL, TRUE );
	 WinEnableMenuItem( pmp->hwndMenubar, IDM_VIEWHALF, TRUE );
      }
      return (MRESULT) 0;



   case WM_USER_NEW_MODE:
      /*
       * The program now has a new file, file type, or printer, or
       * printer form, orientation, resolution, etc. The receipt
       * and processing of this message works to reset the program:
       * Old file, bitmap, or metafile handles are closed, and
       * new ones get opened. The titlebar shows the new filename.
       * This case works very much like a program reset.
       */
      pmp = (PMAIN_PARM) WinQueryWindowULong( hwnd, QWL_USER );

      /* close processing on current file */

      switch( pmp->ulMode )
      {
      case MODE_BITMAP:
	/* destroy previous memory dc, ps, and hbm */
	if( pmp->hpsMemory )
	{
	  GpiAssociate( pmp->hpsMemory, (HDC)0 );
	  GpiDestroyPS( pmp->hpsMemory );
	  pmp->hpsMemory = (HPS) 0;
	}
	if( pmp->hdcMemory )
	{
	  DevCloseDC( pmp->hdcMemory );
	  pmp->hdcMemory = (HDC) 0;
	}
	if( pmp->hbm )
	{
	  GpiDeleteBitmap( pmp->hbm );
	  pmp->hbm = (HBITMAP) 0;
	}
	break;

      case MODE_METAFILE:
	/* destroy old metafile handle */
	if( pmp->hmf )
	{
	  GpiDeleteMetaFile( pmp->hmf );
	  pmp->hmf = (HMF) 0;
	}
	break;

      case MODE_TEXT:
	fclose( pmp->f );
	pmp->f = (FILE *) 0;
	/* turn off options for text mode */
	WinEnableMenuItem( pmp->hwndMenubar, IDM_VIEWHALF, FALSE );
	WinEnableMenuItem( pmp->hwndMenubar, IDM_VIEWFULL, FALSE );
	WinEnableMenuItem( pmp->hwndMenubar, IDM_PAGEDOWN, FALSE );
	WinEnableMenuItem( pmp->hwndMenubar, IDM_SETFONT,  FALSE );
	WinEnableControl( pmp->hwndFrame, FID_VERTSCROLL,  FALSE );
	break;
      }


      /* copy over current values with the next values */
      pmp->ulMode = pmp->ulNextMode;
      strcpy( pmp->szFilename, pmp->szNextFilename );

      /* enable the print menu option if mode is known and there
	 is a printer set up. */
      WinEnableMenuItem( pmp->hwndMenubar, IDM_PRINT,
       (pmp->ulMode != MODE_UNKNOWN && pmp->hpsPrinterInfo ) );


      /* update title bar text and show filename in use */
      if( *pmp->szFilename )
      {
	pch = strrchr( pmp->szFilename, '\\' );
	pmassert( pmp->hab, pch );
	pch++;
	pmassert( pmp->hab, *pch );
	sprintf( szWork, "%s - %s", pmp->pszTitle, pch );
      }
      else
      {
	strcpy( szWork, pmp->pszTitle );
      }
      WinSetWindowText( pmp->hwndTitlebar, szWork );

      /* process this new mode */
      switch( pmp->ulMode )
      {
      case MODE_BITMAP:
	/* load the bitmap into memory so it is compatible with the screen */
	WinSendMsg( hwnd, WM_USER_DISABLE_CLIENT, 0, 0 );
	WinPostMsg( pmp->hwndObject, WM_USER_LOAD_BITMAP,
		    (MPARAM)hwnd, (MPARAM)HDCMEM_COMPATIBILITY_SCREEN );
	break;

      case MODE_METAFILE:
	/* make object window read metafile */
	WinSendMsg( hwnd, WM_USER_DISABLE_CLIENT, 0, 0 );
	WinPostMsg( pmp->hwndObject, WM_USER_LOAD_METAFILE, (MPARAM)hwnd, 0 );
	break;

      case MODE_TEXT:
	/* turn on options for text mode */
	WinEnableMenuItem( pmp->hwndMenubar, IDM_VIEWHALF, TRUE	 );
	WinEnableMenuItem( pmp->hwndMenubar, IDM_VIEWFULL, TRUE	 );
	WinEnableMenuItem( pmp->hwndMenubar, IDM_PAGEDOWN, TRUE	 );
	WinEnableMenuItem( pmp->hwndMenubar, IDM_SETFONT,  TRUE	 );
	WinEnableControl( pmp->hwndFrame, FID_VERTSCROLL,  TRUE	 );
	/* reset view matrix that was last in effect for viewing text pages; */
	/* this gets lost after viewing a metafile */
	bOK = GpiSetDefaultViewMatrix( pmp->hpsClient, 9,
				       &pmp->matlfDefView, TRANSFORM_REPLACE );
	pmassert( pmp->hab, bOK );
	/* disable until text loaded */
	WinSendMsg( hwnd, WM_USER_DISABLE_CLIENT, 0, 0 );
	/* tell object window to load the text file */
	bOK = WinPostMsg( pmp->hwndObject, WM_USER_LOAD_TEXT, (MPARAM)hwnd, 0 );
	pmassert( pmp->hab, bOK );
	break;


      case MODE_UNKNOWN:
	/* clear the client window */
	WinInvalidateRect( hwnd, NULL, FALSE );
	break;

      }
      return (MRESULT) 0;

   case WM_VSCROLL:
      pmp = (PMAIN_PARM) WinQueryWindowULong( hwnd, QWL_USER );
      pmassert( pmp->hab, pmp->ulMode == MODE_TEXT );

      /* Compute some fraction of scroll bar range for a unit of scrolling. */
      sStep  = pmp->sVScrollRange / 50;

      switch( SHORT2FROMMP( mp2 ))
      {
      case SB_LINEUP:
	 pmp->sVScrollPos	-= sStep;
	 break;
      case SB_PAGEUP:
	 pmp->sVScrollPos	-= pmp->sizelClient.cy;
	 break;
      case SB_LINEDOWN:
	 pmp->sVScrollPos	+= sStep;
	 break;
      case SB_PAGEDOWN:
	 pmp->sVScrollPos += pmp->sizelClient.cy;
	 break;
      case SB_SLIDERPOSITION:
      case SB_SLIDERTRACK:
	 pmp->sVScrollPos = SHORT1FROMMP( mp2 );
	 break;
      }

      /* Don't allow step assignments to exceed limits of zero to range. */
      pmp->sVScrollPos = max( (SHORT)0,
			      min( pmp->sVScrollPos, pmp->sVScrollRange ));

      if( pmp->sVScrollPos != SHORT1FROMMR(WinSendMsg( pmp->hwndVScroll,
	  SBM_QUERYPOS, (MPARAM)0, (MPARAM)0 )))
      {
	 /*
	  * New scroll bar thumbbutton position is different than current.
	  * Set a new Y translation value to effect the scroll.
	  * Current scale setting affects the Y element of the matrix.
	  */
	 pmp->matlfDefView.lM32 =  (LONG)(pmp->floatScale *
	     (float)( OFFSET_XY_TWIPS +
		      pmp->sVScrollPos -
		      pmp->sVScrollRange ));
	 bOK = GpiSetDefaultViewMatrix( pmp->hpsClient,
					9,
					&pmp->matlfDefView,
					TRANSFORM_REPLACE );
	 pmassert( pmp->hab, bOK );
	 CalibrateScrollBar( pmp );
	 WinInvalidateRect( hwnd, NULL, FALSE );
      }
      return (MRESULT) 0;


   }

   return WinDefWindowProc( hwnd, msg, mp1, mp2 );
}  /* End of WinProc() */


 /************************************************************************
 *
 * Name	       : CalibrateScrollBar( pmp )
 *
 * Description : This function computes the scroll bar range to be that
 *		 portion of the text document that is clipped from the
 *		 client window, then sends this range to the scroll bar.
 *		 The thumb position used is that which is already in the pmp.
 *
 *		 Thumbsize is then computed by the scroll bar to be a ratio
 *		 of the visible part of the document / the entire document.
 *
 * Concepts    : Manipulation of thum position and scroll bar.
 *
 * API's       : WinSendMsg
 *
 * Parameters  : PMAIN_PARM	 structure containing important data such
 *				  as window handles, device context handles,
 *				  and other bitmap, printing and program
 *				  information
 *
 * Returns     : [nothing]
 *
 **************************************************************************/
VOID CalibrateScrollBar( PMAIN_PARM pmp )
{
   BOOL bOK;

   /*
    * The range of the scroll bar is the length of the
    * printed page that is clipped by the client window.
    */
   pmp->sVScrollRange = ( pmp->sizelPage.cy + 2*OFFSET_XY_TWIPS ) -
				   pmp->sizelClient.cy;

   /* Position the thumbbutton according to pmp->sVScrollPos. */
   bOK = (BOOL)WinSendMsg( pmp->hwndVScroll,
		     SBM_SETSCROLLBAR,
		     (MPARAM) pmp->sVScrollPos,
		     MPFROM2SHORT( (USHORT)0,  pmp->sVScrollRange ));
   pmassert( pmp->hab, bOK );

   /* Set thumbsize of the scroll bar. */

   bOK = (BOOL)WinSendMsg( pmp->hwndVScroll,
		     SBM_SETTHUMBSIZE,
		     MPFROM2SHORT( pmp->sizelClient.cy, pmp->sizelPage.cy ),
		     (MPARAM)0 );

   pmassert( pmp->hab, bOK );

}  /* End of CalibrateScrollBar(). */
/***************************  End of prtsamp.c ****************************/
