/*=========================================================================*\
 *									   *
 * SORT.C - Multi-threaded PM sample.					   *
 *									   *
 *									   *
 *	(C) Copyright IBM Corporation 1992.				   *
 *									   *
 *	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.		   *
 *-------------------------------------------------------------------------*
 *
 *   This program demonstrates the use of many threads by doing
 *   LISTCNT simultaneous sorts.  Each sorting algorithm runs
 *   from a separate thread, the routine which updates the display
 *   is run from another thread, and the main thread is used to
 *   handle the main window's messages.  The display thread is
 *   started when the program begins and is not terminated (allows
 *   default cleanup to terminate thread) as the display routine
 *   is used throughout the program's life.
 *   This code was written to allow easy modification.	To change
 *   the number of simultaneous sorts, change the LISTCNT #define
 *   and modify the global arrays which control the screen
 *   drawing color, the sort each thread is to use, and the sort name.
 *   Note:  This program was not intended to infer the relative
 *	    speeds of the various sorting algorithms.
 *
 *---------------------------------------------------------------
 *
 *   Procedures in this file:
 *     main()		  Sets up the PM environment and calls
 *			  the main dialog procedure ClientWndProc
 *     ClientWndProc()	  Handles the main window messages
 *     CalcThread()	  Generic stub that sets up, executes, and
 *			  terminates each sort in an aux. thread
 *     DispThread()	  Updates the display during the sorts
 *     BubbleSort()	  Implements a bubble sort
 *     InsertionSort()	  Implements an insertion sort
 *     BatcherSort()	  Stub that calls recursive Batcher sort
 *     BatcherSortR()	  Implements a Batcher sort
 *     QuickSort()	  Stub that calls recursive Quick sort
 *     QuickSortR()	  Implements a Quick sort
 *     EnableMenuItem()	  Activates/deactivates a menu choice
 *     EntryFldDlgProc()  Handles the set number of disks dialog box
 *     RandomizeData()	  Randomizes the data arrays for the sorts
 *     FixSysMenu()	  Remove Restore, Size, Min, & Max from system menu
 *
\*==============================================================*/
/*--------------------------------------------------------------*\
 *  Include files, macros, defined constants, and externs	*
\*--------------------------------------------------------------*/
#define INCL_WIN
#define INCL_WINHEAP
#define INCL_HELP
#define INCL_WINDIALOGS
#define INCL_GPIPRIMITIVES
#define INCL_GPIBITMAPS
#define INCL_DOSPROCESS
#define INCL_DOSSEMAPHORES
#include <os2.h>
#include <stdio.h>
#include <string.h>
#include <process.h>
#include <stdlib.h>
#include "sort.h"

/*--------------------------------------------------------------*\
 *  Entry point declarations					*
\*--------------------------------------------------------------*/

MRESULT EXPENTRY ClientWndProc	(HWND, ULONG, MPARAM, MPARAM);
MRESULT EXPENTRY EntryFldDlgProc(HWND, ULONG, MPARAM, MPARAM);
MRESULT EXPENTRY AboutDlgProc(HWND, ULONG, MPARAM, MPARAM);
LONG	MessageBox(HWND, LONG, LONG, BOOL);
VOID APIENTRY ExitProc(ULONG);
BOOL Init(VOID);

INT main(LONG ,CHAR **);
VOID APIENTRY CalcThread(ULONG);
VOID APIENTRY DispThread(ULONG);

VOID EnableMenuItem(HWND, LONG, BOOL);
VOID RandomizeData(ULONG);
VOID BubbleSort(PCALCPARAM);
VOID BatcherSort(PCALCPARAM);
VOID BatcherSortR(PCALCPARAM, ULONG, ULONG, ULONG, BOOL);
VOID QuickSort(PCALCPARAM);
VOID QuickSortR(PCALCPARAM, LONG, LONG);
VOID InsertionSort(PCALCPARAM);
VOID FixSysMenu(HWND);
VOID InitHelp(VOID);
VOID HelpHelpForHelp(MPARAM);
VOID HelpExtended(MPARAM);
VOID HelpKeys(MPARAM);
VOID HelpIndex(MPARAM);
VOID HelpAbout(MPARAM);
VOID DisplayHelpPanel(LONG);
VOID DestroyHelpInstance(VOID);
HEV  hev;
RECTL  rcl;		 /* Rectangle struct for painting  */

/*--------------------------------------------------------------*\
 *  Global variables						*
\*--------------------------------------------------------------*/
CHAR szClientClass[40];
USHORT Data[LISTCNT][MAX_ELEMS];     /* Array that contains the data to sort  */

/* Global data that defines each of the sort routines */

			      /* Color of each of the LISTCNT data lists.  */
ULONG  ulColors[LISTCNT] = { CLR_RED,CLR_BLUE,CLR_DARKGREEN,CLR_CYAN };

	  /* List of sorts to be executed for each of the LISTCNT threads. */
VOID (*pExecSub[LISTCNT])(PCALCPARAM)={BubbleSort,BatcherSort,QuickSort,
			   InsertionSort};
						   /* Ascii names of sorts */
CHAR *szSubNames[LISTCNT]={"Bubble Sort",
			   "Batcher Sort",
			   "Quick Sort",
			   "Insertion Sort"};

HAB    hab;				 /* Anchor block for the process */
HMQ    hmq;
BOOL   fHelpEnabled;		 /* flag to determine if help is enabled */
CHAR   szUntitled[MESSAGELEN];		 /* buffer for "Untitled" string */
HWND   hwndFrame, hwndClient;
static CHAR szLibName[HELPLIBRARYNAMELEN];
static CHAR szWindowTitle[HELPLIBRARYNAMELEN];
static HWND hwndHelpInstance;
RECTL  rectl;

/****************************************************************\
 * Function name: main(LONG sArgc,CHAR **ppArgv)
 *
 * Parameters:	NONE
 *
 * Returns: Always returns 0
 *
 * Purpose: Sets up the PM environment, calls the main window proc,
 *	    handles the window's messages then cleans up and exits.
 *
 * Usage:
\****************************************************************/
INT main(LONG sArgc,CHAR **ppArgv)
{
   QMSG		qmsg;
   CHAR *pszName = "SORT";

   ULONG flFrameFlags = FCF_STANDARD;

   strncpy(szClientClass,pszName,strlen(pszName) );

   /* These PM calls should be error checked */
   hab = WinInitialize(0);
   hmq = WinCreateMsgQueue(hab, 0);

   if(!Init())
   {
       MessageBox(HWND_DESKTOP,
		  IDMSG_INITFAILED,
		  MB_OK | MB_ERROR,
		  TRUE);
       DosExit(EXIT_PROCESS,RETURN_ERROR);
   }


   hwndFrame = WinCreateStdWindow(HWND_DESKTOP,
				  WS_VISIBLE,
				  &flFrameFlags,
				  szClientClass,
				  NULL,
				  0L,
				  (HMODULE)0,
				  ID_RESOURCE,
				  &hwndClient);
   if(hwndFrame == NULLHANDLE)
   {
       MessageBox(HWND_DESKTOP,
		  IDMSG_MAINWINCREATEFAILED,
		  MB_OK | MB_ERROR,
		  TRUE);
       DosExit(EXIT_PROCESS,RETURN_ERROR);
   }
   InitHelp();

   while(WinGetMsg(hab,&qmsg,0,0,0))	    /* Message loop */
      WinDispatchMsg(hab,&qmsg);

   /* destroy the help instance */
   DestroyHelpInstance();

   DosExit(EXIT_PROCESS,RETURN_SUCCESS);
   return 0L;
}

/****************************************************************\
 *  Initialization routine
 *---------------------------------------------------------------
 *
 *  Name:    Init()
 *
 *  Purpose: Performs initialization functions required before the main
 *	     window can be created.
 *
 *  Usage:   Called once before the main window is created.
 *
 *  Method:  - installs the routine ExitProc into the DosExitList chain
 *	     - registers all window classes
 *	     - performs any command line processing
 *
 *
 *  Returns: TRUE - initialization is successful
 *	     FALSE - initialization failed
\****************************************************************/
BOOL Init(VOID)
{

    /* Add ExitProc to the exit list to handle the exit processing. If
     * there is an error, then terminate the process since there have
     * not been any resources allocated yet			     */

    if(DosExitList(EXLST_ADD, ExitProc))
    {
	MessageBox(HWND_DESKTOP,
		   IDMSG_CANNOTLOADEXITLIST,
		   MB_OK | MB_ERROR,
		   TRUE);
	DosExit(EXIT_PROCESS, RETURN_ERROR);
    }

    if(DosCreateEventSem(NULL,&hev,DC_SEM_SHARED,FALSE ) )
    {
	return(FALSE);
    }

     /* load application name from resource file */
    if(!WinLoadString(hab, 0, IDS_APPNAME, MAXNAMEL, szClientClass))
	return FALSE;

    /* load "untitled" string */
    if(!WinLoadString(hab, 0, IDS_UNTITLED, MESSAGELEN, szUntitled))
	return FALSE;
    /* register the main client window class */
    if(!WinRegisterClass(hab,
			(PSZ)szClientClass,
			(PFNWP)ClientWndProc,
			CS_SIZEREDRAW | CS_CLIPCHILDREN,
			0))
    {
	 return FALSE;
    }
    return( TRUE);
}						    /* Init() */

/****************************************************************\
 *  Exit list processing procedure
 *--------------------------------------------------------------
 *
 *  Name:    ExitProc(usTermCode)
 *
 *  Purpose: Cleans up certain resources when the application terminates
 *
 *  Usage:   Routine is called by DosExitList when the application exits
 *
 *  Method:  Global resources, such as the main window and message queue,
 *	     are destroyed and any system resources used are freed.
 *
 *  Returns: Returns EXLST_EXIT to the DosExitList handler
 *
\****************************************************************/
VOID APIENTRY ExitProc(ULONG usTermCode)
{
   /* destroy the main window if it exists */
   if(WinIsWindow(hab, hwndFrame))  {
       WinDestroyWindow(hwndFrame);
   }

   /*--------------------------------------------------*\
    *	   Any other system resources used	       *
    *	   (e.g. memory or files) should be freed here *
   \*--------------------------------------------------*/

   WinDestroyMsgQueue(hmq);

   WinTerminate(hab);

   DosExitList(EXLST_EXIT, 0L);		/* termination complete */
   return;
}					    /* ExitProc() */

/****************************************************************\
 * Function name: ClientWndProc()
 *
 * Parameters:	hwnd, msg, mp1, mp2.  Standard PM Dialog Proc params.
 *		No user data is expected in the WM_CREATE.
 *
 * Returns: Returns with WM_QUIT message
 *
 * Purpose: Handles all the messages associated with the main window
 *	    and calls the appropriate handling procedures.
 *
 * Usage  : Called only by main()
\****************************************************************/
MRESULT EXPENTRY ClientWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
   HPS	  hps=0;			 /* Handle for painting		   */
   POINTL ptl;				 /* Point struct for painting	   */
   static CALCPARAM cp[LISTCNT];	 /* Struct passed to sort threads  */
   static TID	 tidCalc[LISTCNT];	 /* Sort threads IDs		   */
   static CALCPARAM cpDisp;		 /* Struct passed to disp thread   */
   static TID	 tidDisplay;		 /* Secondary display thread ID	   */
   static USHORT usWindowYCoord=1;	 /* Holds current client window ht */
   static BYTE	 cThreadCnt = 0;	 /* Count of sort threads running  */
   static USHORT cSetSize=MAX_ELEMS;	 /* User sort set size		   */
   USHORT cnt,cnt2,usID;		 /* Utility counters		   */
   ULONG  ulBlip;			 /* Holds the marker pos for Paint */
   MRESULT sRC;

   switch(msg)
   {
      case WM_CREATE:
	 RandomizeData(cSetSize);	 /* Initially randomize data set   */
	 /* Start display thread */
	 cpDisp.hwnd	    = hwnd;
	 cpDisp.Array	    = &usWindowYCoord;
	 cpDisp.pcThreadCnt = &cThreadCnt;
	 cpDisp.pcSetSize   = &cSetSize;
	 DosCreateThread( &tidDisplay,
	  		DispThread,
			(ULONG) ((PVOID) &cpDisp),
			0,
			STACKSIZE);
	if( tidDisplay == (USHORT)-1)
	 {
	    WinAlarm(HWND_DESKTOP, WA_ERROR);  /* can't start display thread */
	    DosExit(EXIT_PROCESS,1);
	 }
	 /* Up the display priority an arbitrary amount to cause it to
	    update more rapidly and to give it a higher priority than
	    the sort threads.*/
	 DosSetPriority(PRTYS_THREAD, PRTYC_REGULAR, 5, tidDisplay);

	 return (MRESULT)0L;

      case WM_SIZE:
	 WinInvalidateRect(hwnd,NULL,FALSE);	 /* Force a redraw */
	 sRC = WinDefWindowProc(hwnd, msg, mp1, mp2);
	 return sRC;

      case WM_PAINT:
	 hps = WinBeginPaint(hwnd,0,0);	  /* Get paint handle	   */
	 WinQueryWindowRect(hwnd,&rcl);
	 if(NELEMS < MAX_ELEMS )
	 {
	       cSetSize = NELEMS;
	 }
	 else
	 {
	       cSetSize = MAX_ELEMS;
	 }
	 RandomizeData(cSetSize);	 /* Initially randomize data set   */
	 usWindowYCoord = rcl.yTop;
	  /* Draw if screen */
	  /* is big enough  */
	 if( (XOFFSET + (2 * COLUMNOFFSET))< (rcl.xRight-rcl.xLeft) &&
	    (YOFFSET * ROWCNT) < (rcl.yTop-rcl.yBottom ) )
	 {
	    DrawRect(rcl.xLeft,rcl.yBottom,	    /* White out the screen  */
		     rcl.xRight,rcl.yTop,CLR_WHITE);

	    DrawLine(XOFFSET, YOFFSET,		    /* Draw baseline	     */
		     rcl.xRight,YOFFSET,CLR_BLACK);

	    DrawLine(XOFFSET, YOFFSET,		    /* Draw vertical line    */
		     XOFFSET,rcl.yTop,CLR_BLACK);

	    for(cnt=0;cnt<LISTCNT;cnt++)	    /* Draw data points	     */
	    {
	       for(cnt2=0;cnt2<cSetSize;cnt2++)
	       {
		  ulBlip = (rcl.yTop-YOFFSET-5) * Data[cnt][cnt2] / RAND_MAX
			   + YOFFSET+1;

		  Draw2Pel(XOFFSET+1 + cnt2, ulBlip, ulColors[cnt]);
	       }
	    }
	    for(cnt=0,cnt2= 0;cnt<COLUMNCNT;cnt++)	    /* Do bottom legend	     */
	    {
	       if(cnt2 >= ROWCNT )
	       {
		    cnt2 = 0;
	       }
	       ptl.x = XOFFSET + cnt2++ *COLUMNOFFSET;
	       /*
		*next row after the last column
		*/
	       if(cnt >=  (COLUMNCNT / ROWCNT ) )
	       {
		    ptl.y = YOFFSET - ( 2 * ROWOFFSET);
	       }
	       else
	       {
		    ptl.y = YOFFSET - ROWOFFSET;
	       }
	       GpiSetColor(hps,ulColors[cnt]);
	       GpiCharStringAt(hps,&ptl,
			       (LONG) strlen(szSubNames[cnt]),
			       szSubNames[cnt]);
	    }
	 }

	 WinEndPaint(hps);
	 return (MRESULT)0L;

      case WM_COMMAND:
	 switch(SHORT1FROMMP(mp1))
	 {
	    case IDM_START:

	       /*
		*clear the drawing semaphore
		*/
	       DosPostEventSem(hev);
	       /* Try to get stack space */
	       for(cnt=0;cnt<LISTCNT;cnt++)
	       {
		  cp[cnt].hwnd		= hwnd;	 /* Set the static struct  */
		  cp[cnt].fContinueCalc = TRUE;
		  cp[cnt].pFunc		= pExecSub[cnt];
		  cp[cnt].usID		= cnt;
		  cp[cnt].Array		= Data[cnt];
		  cp[cnt].cArray	= cSetSize;

		  /* Try to start the thread */
		  DosCreateThread( &tidCalc[cnt],
		  		CalcThread,
				(ULONG) (PVOID) &cp[cnt],
				0,
				STACKSIZE);
		  if( tidCalc[cnt] == (USHORT)-1)
		  {
		     WinAlarm(HWND_DESKTOP, WA_ERROR);
		     return (MRESULT)0L;
		  }
		  if(cThreadCnt++ == 0)	 /* When the first thread starts */
		  {
		     /* Disable Start, Set, and Randomize, enable Stop */
		     EnableMenuItem(hwnd,IDM_START,FALSE);
		     EnableMenuItem(hwnd,IDM_SET,FALSE);
		     EnableMenuItem(hwnd,IDM_RANDOM,FALSE);
		     EnableMenuItem(hwnd,IDM_STOP,TRUE);
		  }
	       }
	       return (MRESULT)0L;

	    case IDM_STOP:
	       for(cnt=0;cnt<LISTCNT;cnt++)
		  cp[cnt].fContinueCalc = FALSE;  /* Notify thread to quit  */
	       return (MRESULT)0L;

	    case IDM_RANDOM:
	       RandomizeData(cSetSize);		       /* Randomize data */
	       WinInvalidateRect(hwnd,NULL,FALSE);     /* Force a redraw */
	       return (MRESULT)0L;

	    case IDM_SET:
	       if(WinDlgBox(HWND_DESKTOP, hwnd, /* Pop up the query/set box */
			    EntryFldDlgProc,(HMODULE)0,IDD_SETCOUNT,
			    (VOID FAR *) &cSetSize))
	       {
		  WinInvalidateRect(hwnd,NULL,FALSE);	  /* Force a redraw */
	       }
	       return (MRESULT)0L;

	    case IDM_GENERALHELP:
	       HelpExtended(mp2);
	       return (MRESULT)0L;

	    case IDM_USINGHELP:
	       HelpHelpForHelp(mp2);
	       return (MRESULT)0L;

	    case IDM_TUTORIAL:
	       HelpKeys(mp2);
	       return (MRESULT)0L;

	    case IDM_HELPINDEX:
	       HelpIndex(mp2);
	       return (MRESULT)0L;

	    case IDM_HELPPRODUCTINFO:
	       HelpAbout(mp2);
	       return (MRESULT)0L;

	    default:
	       sRC = WinDefWindowProc(hwnd, msg, mp1, mp2);
	       return sRC;
	 }
	 break;

      case IDM_DONE:
	 usID = SHORT1FROMMP(mp1);  /* Get ID of quit thread	 */

	 if(--cThreadCnt == 0)		    /* If all quit, enable menus */
	 {
	    EnableMenuItem(hwnd,IDM_START,TRUE);
	    EnableMenuItem(hwnd,IDM_SET,TRUE);
	    EnableMenuItem(hwnd,IDM_RANDOM,TRUE);
	    EnableMenuItem(hwnd,IDM_STOP,FALSE);
	 }
	 return (MRESULT)0L;

      case HM_QUERY_KEYS_HELP:
	 return ((MRESULT)PANEL_KEYSHELP);

      case WM_CLOSE:

	 DosCloseEventSem(hev);
	 for(cnt=0;cnt<LISTCNT;cnt++)
	 {
	    DosKillThread(tidCalc[cnt]);
	 }
	 DosKillThread(tidDisplay);
	 WinPostMsg(hwnd, WM_QUIT, 0L, 0L);
	 return 0L;

      default:
	 sRC = WinDefWindowProc(hwnd, msg, mp1, mp2);
	 return sRC;
   }

   return (MRESULT)0L;
}

/****************************************************************\
 * Function name: CalcThread()
 *
 * Parameters:	pcp is a struct which contains the hwnd handle, the
 *		continue flag which is initially set to TRUE, the ID
 *		which is the
 *
 * Returns: VOID
 *
 * Purpose: This generic stub calls the passed "sort" function with a pointer
 *	    to the data and an item count, then when the sort terminates, it
 *	    cleans up by Posting a done message, then terminating the thread.
 *
 * Usage  : No DosExitCritSec() is called since _endthread()
 *		   clears the critical section when the thread is
 *		   terminated.
 *
\****************************************************************/
VOID APIENTRY CalcThread(ULONG param)
{
   PVOID pcp;
   pcp = (PVOID) param;
   (*((PCALCPARAM)pcp)->pFunc)(pcp);	     /* Execute recurs routine */
			   /* Set Crit so the IDM_DONE isn't processed */
			/* until this thread has completely terminated */
   DosEnterCritSec();

   WinPostMsg(((PCALCPARAM)pcp)->hwnd,IDM_DONE,
	      MPFROMSHORT(((PCALCPARAM)pcp)->usID),NULL);/* Post done */

   /*
    *set the post count for each thread
    *that goes through
    */
   DosPostEventSem(hev);

   return;
}

/****************************************************************\
 * Function name: DispThread()
 *
 * Parameters:	pcp is a struct which contains the hwnd handle, the
 *		count of active threads, the ->Array element points to
 *		the current height of the window which can be dynamically
 *		resized by the user, the pcSetSize points to the current
 *		set size which can be changed by the user only when the
 *		sort is stopped.
 *		None of the other pcp members are used.
 *
 * Returns: VOID
 *
 * Purpose: This routine is run as a secondary thread to update the screen
 *	    when the sort threads are running.	It works by making passes
 *	    through the data lists and redrawing them.	A DosSleep(0) is
 *	    called at the end of each pass to help sychronize things a bit.
 *
 * Usage  : Note that this thread is started when the program is
 *		   initialized, and is never terminated.  Note that
 *		   fDoUpdate is used in such a way that the screen is
 *		   ALWAYS updated once after all threads have terminated.
 *		   This avoids the possibility of the display not being
 *		   fully accurate when the sorts terminate.
 *
\****************************************************************/
VOID APIENTRY DispThread(ULONG param )
{
   PVOID pvData;
   USHORT cnt,cnt2;	 /* Utility counters				*/
   HPS hps;		 /* Presentation space handle			*/
   POINTL ptl;		 /* Used for various drawing macros		*/
   ULONG  ulBlip;	 /* Holds the Y offset for a given data point	*/
   PCALCPARAM pcp;	 /* after all sort threads terminate		*/
   USHORT *pusYCoord;
   ULONG  ulPostCount;

   pvData=(PVOID)param;
   pcp = (PCALCPARAM) pvData;
   pusYCoord = pcp->Array;
   /* Points to location that always	*/
   /* contains current client wind hgt	*/

   hps = WinGetPS( pcp->hwnd);

   while(TRUE)
   {
      /*
       *sit and wait on the clearing of the semaphore
       */
      DosWaitEventSem(hev,SEM_INDEFINITE_WAIT);
      DosQueryEventSem(hev,&ulPostCount);
      /*
       *wait on all 4 threads + the post used to start
       *to post
       */
      if(ulPostCount == 5)
      {
	  ulPostCount = 0;
	  DosResetEventSem(hev,&ulPostCount);
      }
      for(cnt=0;cnt<*((PCALCPARAM)pcp)->pcSetSize;cnt++)/* Update data set */
      {
	 DrawLine(XOFFSET+1 + cnt,YOFFSET+1,	  /* Erase vertical column */
		  XOFFSET+1 + cnt,*pusYCoord, CLR_WHITE);

	 for(cnt2=0;cnt2<LISTCNT;cnt2++)	  /* Draw each point	   */
	 {
	    ulBlip = (ULONG) (*pusYCoord-YOFFSET-5)*Data[cnt2][cnt] / RAND_MAX
		      + YOFFSET+1;
	    Draw2Pel(XOFFSET+1 + cnt, ulBlip, ulColors[cnt2]);
	 }
      }
   }

   /* Note that these 3 lines NEVER get executed, but should the program be
      modified to end the thread, this is the appropriate termination code. */
   WinReleasePS(hps);
//   WinTerminate(habb);
   return;
}

/****************************************************************\
 * Function name: BubbleSort()
 *
 * Parameters:	pcp is a struct which contains the hwnd handle, a
 *		pointer to the data array, and a count of the items in
 *		the data array.
 *		None of the other pcp members are used.
 *
 * Returns: VOID
 *
 * Purpose:  Implements a bubble sort which is an O(n^2) algorithm.  It
 *	     works by repeatedly going through the data and comparing
 *	     consecutive elements and swapping them if they aren't in
 *	     the correct order.	 This guarantees that each pass will
 *	     place at least one additional item in its appropriate
 *	     place.
 *
 * Usage:
\****************************************************************/
VOID BubbleSort(PCALCPARAM pcp)
{
   BOOL fModified = FALSE; /* Set whenever a swap is done, if an entire */
			   /* pass doesn't set the flag, the we're done */
   INT	  cnt;
   INT	  cnt2;		   /* Counters used for the 2-level loops	*/
   USHORT usTemp;	   /* Used to hold a data item during a swap	*/

   for(cnt=(ULONG)(pcp->cArray-1);cnt>=0;cnt--) /*Set the max no. of passes*/
   {
      for(cnt2=0;cnt2<cnt;cnt2++) /* Only sort thru current cnt pass	*/
      {
	 if(!pcp->fContinueCalc)  /* User wishes to terminate the sort	*/
	    return;

	 if(pcp->Array[cnt2]>pcp->Array[cnt2+1]) /* Items need to swap	*/
	 {
	    fModified = TRUE;

	    usTemp = pcp->Array[cnt2];
	    pcp->Array[cnt2] = pcp->Array[cnt2+1];
	    pcp->Array[cnt2+1]= usTemp;
	 }
      }
      if(!fModified)	      /* Nothing changed during the entire pass */
	 break;
      fModified = FALSE;		       /* Reset the modify flag */
   }
   return;
}

/****************************************************************\
 * Function name: InsertionSort()
 *
 * Parameters:	pcp is a struct which contains the hwnd handle, a
 *		pointer to the data array, and a count of the items in
 *		the data array.
 *		None of the other pcp members are used.
 *
 * Returns: VOID
 *
 * Purpose:  Implements an insertion sort which is an O(n^2) algorithm.
 *	     This sort works much faster and does not require that much
 *	     additional code to implement.  It works by setting a sorted
 *	     list to be just the first element, the working each
 *	     successive item into the already sorted list.
 *
 * Usage:
\****************************************************************/
VOID InsertionSort(PCALCPARAM pcp)
{
   SHORT cnt,cnt2;	      /* Counters used for the 2-level loops	*/
   USHORT usTemp;	      /* Used to hold a data item during a swap */

   for(cnt=1;cnt<(LONG)pcp->cArray;cnt++)  /* Insert each item in turn */
   {
      if(!pcp->fContinueCalc)	   /* User wishes to terminate the sort */
	 return;
      usTemp=pcp->Array[cnt];			/* Hold value to insert */
      cnt2=(LONG)(cnt-1);
      while(pcp->Array[cnt2]>usTemp)	/* Move items down to make room */
      {
	 pcp->Array[cnt2+1]=pcp->Array[cnt2];
	 cnt2--;
	 if(cnt2<0)
	    break;
      }
      pcp->Array[cnt2+1] = usTemp;		     /* Insert the item */
   }
   return;
}

/****************************************************************\
 * Function name: BatcherSort()
 *
 * Parameters:	pcp is a struct which contains the continue flag,
 *		a pointer to the data array, and a count of the items in
 *		the data array.
 *		None of the other pcp members are used.
 *
 * Returns: VOID
 *
 * Purpose:  This routine is a stub that calls the recursive Batcher sort
 *	     with the proper initial arguments.
 *
 * Usage:  It passes the pcp, the size of the array to be sorted,
 *		    the offset to start sorting at(0), the number number
 *		    of elements each item is from neighboring elements (1),
 *		    and Half flag to sort the halves (1=YES).
\****************************************************************/
VOID BatcherSort(PCALCPARAM pcp)
{
   BatcherSortR(pcp,pcp->cArray,0,1,1);
   return;
}

/****************************************************************\
 * Function name: BatcherSortR()
 *
 * Parameters:	pcp is a struct which contains the continue flag,
 *		a pointer to the data array.
 *		None of the other pcp members are used.
 *		usArrSize is the number of elements in the current sort set.
 *		usStart is the offset to the 1st element in the set.
 *		usSkip is the spacing between consecutive elements.
 *		fHalves sorts the 2 halves when set, otherwise skips the
 *			1st 2 of the 4 sub-sorts.
 *
 * Returns: VOID
 *
 * Purpose:  Implements Batcher sort which is O(n lg n).  The advantage
 *	     of the batcher sort is that the comparisons made do NOT
 *	     depend upon the outcome of previous comparisons.  This makes
 *	     it a good algorithm for parallelism.  The algorithm works as
 *	     follows:  Sort the first half, sort the second half, sort the
 *	     odd elements, sort the even elements, then compare swap
 *	     elements 2/3, 4/5, ...
 *
 * Usage  :  There are several adaptations to the algorithm to
 *		    allow it to work with arbitrary sized data sets
 *		    (the original required a power of 2 sized data
 *		    set):  if the set size is less than 2, the routine
 *		    returns, the first "half" is always the largest
 *		    possible power of two, and the top value for the
 *		    final compare/swap is adjusted to round up in
 *		    case of an odd data set.
 *		    Another optimization is that involving the fHalves
 *		    flag.  This stems from the observation that when
 *		    the odd/even sort recurses, the first and second
 *		    halves are already sorted, thus the first 2
 *		    recursive calls are unnecessary in this case.
 *
\****************************************************************/
VOID BatcherSortR(PCALCPARAM pcp, ULONG usArrSize, ULONG usStart,
		  ULONG usSkip, BOOL fHalves)
{
   USHORT cnt,usUpper,usTemp; /* Utility variables */

   if(!pcp->fContinueCalc) /* User wishes to terminate the sort */
      return;

   if(usArrSize<2) /* No sorting needed if <2 items in the set */
      return;

   if(usArrSize==2) /* Do simple compare/swap if there are 2 elements */
   {
      if(pcp->Array[usStart]>pcp->Array[usStart+usSkip])
      {
	 usTemp = pcp->Array[usStart];
	 pcp->Array[usStart] = pcp->Array[usStart+usSkip];
	 pcp->Array[usStart+usSkip]= usTemp;
      }
      return;
   }

   usTemp=1;		      /* usTemp ends up holding the smallest power */
   while(usTemp < usArrSize)  /* of 2 that is at least as big as usArrSize */
      usTemp *= 2;

   if(fHalves)	/* If the sort was NOT called by the odd/even recurses */
   {
      BatcherSortR(pcp,(ULONG)(usTemp/2),usStart,usSkip,(BOOL)1);    /* Sort 1st half */
      BatcherSortR(pcp,(ULONG)(usArrSize-usTemp/2),	  /* Sort 2nd half */
		   (ULONG)(usStart+usTemp/2*usSkip),usSkip,(BOOL)1);
   }
   BatcherSortR(pcp,(ULONG)(usArrSize-usArrSize/2),usStart,(ULONG)(usSkip*2),(BOOL)0); /* Sort evens */
   BatcherSortR(pcp,(ULONG)(usArrSize/2),(ULONG)(usStart+usSkip),(ULONG)(usSkip*2),(BOOL)0);	/* Sort odds  */

   if(!pcp->fContinueCalc) /* User wishes to terminate the sort */
      return;

   usUpper=(ULONG)(usStart+usSkip+(usArrSize-usArrSize/2-1)*2*usSkip);
   for(cnt=usStart+usSkip;cnt<usUpper;cnt+=usSkip*2) /* Do final compares */
   {
      if(pcp->Array[cnt]>pcp->Array[cnt+usSkip])
      {
	 usTemp = pcp->Array[cnt];
	 pcp->Array[cnt] = pcp->Array[cnt+usSkip];
	 pcp->Array[cnt+usSkip]= usTemp;
      }
      if(!pcp->fContinueCalc) /* User wishes to terminate the sort */
	 return;
   }
   return;
}


/****************************************************************\
 * Function name: QuickSort()
 *
 * Parameters:	pcp is a struct which contains the continue flag,
 *		a pointer to the data array, and a count of the items in
 *		the data array.
 *		None of the other pcp members are used.
 *
 * Returns: VOID
 *
 * Purpose:  This routine is a stub that calls the recursive Quick sort
 *	     with the proper initial arguments.
 *
 * Usage  :  It passes the pcp (Which contains the array ptr),
 *		    the offset to start sorting at(0), and the offset to
 *		    finish sorting at (Array size-1).
 *
\****************************************************************/
VOID QuickSort(PCALCPARAM pcp)
{
   QuickSortR(pcp,0,(pcp->cArray-1));
   return;
}


/****************************************************************\
 * Function name: QuickSortR()
 *
 * Parameters:	pcp is a struct which contains the continue flag,
 *		a pointer to the data array.
 *		None of the other pcp members are used.
 *		sLeft is the offset of the leftmost sort element (smallest).
 *		sRight is the offset of rightmost sort element (largest).
 *
 * Returns: VOID
 *
 * Purpose:  Implements Quick sort which is O(n lg n).	Quick sort is a
 *	     good all-purpose sorting algorithm and is widely used.  This
 *	     implementation works by placing the first element in the list
 *	     into its correct place, by moving all elements smaller to its
 *	     left, and all numbers larger to its right.	 The Quick sort
 *	     recurses on the smaller side of the properly placed element,
 *	     and loops back through the same routine for the larger side.
 *
 *	     NOTE: This routine does not recurse on both sides of the
 *		   partition element.  It only recurses on the smaller
 *		   of the two sides.  This was done in order to eliminate
 *		   the stack overrun when it tries to sort an already
 *		   sorted list.
 *
 * Usage:
 *
\****************************************************************/
VOID QuickSortR(PCALCPARAM pcp, LONG sLeft, LONG sRight)
{
   SHORT sTempLeft  = sLeft;	/* Holds lower bound on split position	    */
   SHORT sTempRight = sRight;	/* Holds upper bound on split position	    */
   SHORT sSplit;		/* Used to hold offset of positioned elem   */
   USHORT usTemp;		/* Holds temp element during positioning    */

   while(TRUE)
   {
      if(sLeft >= sRight)	   /* If there is <=1 element, return */
	 return;

      if(!pcp->fContinueCalc)	   /* User wishes to terminate the sort */
	 return;

      /* Split list, sSplit will contain the split element */
      usTemp = pcp->Array[sTempLeft];

      while(TRUE)    /* Loop thru 2 cases until the split position is found */
      {
	 while(pcp->Array[sTempRight] > usTemp && /* Skip elems on right side  */
			   sTempRight > sLeft)	  /* that are > the split elem */
	    sTempRight--;

	 if(sTempRight<=sTempLeft)  /* Left and Right have met, split is found */
	 {
	    pcp->Array[sTempLeft]=usTemp;  /* Place split elem at proper place */
	    sSplit = sTempLeft;
	    break;
	 }
	 pcp->Array[sTempLeft++] =
		       pcp->Array[sTempRight];	  /* Move small element to LHS */

	 while(pcp->Array[sTempLeft] < usTemp &&  /* Skip elems on left side   */
			   sTempLeft < sRight)	  /* that are < the split elem */
	    sTempLeft++;

	 if(sTempRight<=sTempLeft)  /* Left and Right have met, split is found */
	 {
	    pcp->Array[sTempRight]=usTemp; /* Place split elem at proper place */
	    sSplit = sTempRight;
	    break;
	 }
	 pcp->Array[sTempRight--] =
		       pcp->Array[sTempLeft];	  /* Move large element to RHS */
      }

      if ((sSplit - sLeft) > (sRight - sSplit)) {
	 /* left half is larger than right half */
	 QuickSortR(pcp, sSplit+1, sRight);   /* Sort the 2nd half of the list */
	 if(!pcp->fContinueCalc)	    /* User wishes to terminate sort */
	   return;
	 sTempLeft = sLeft;		    /* Set up to sort the 1st half */
	 sTempRight = sRight = sSplit - 1;
      }
      else {
	 /* right half is larger than left half */
	 QuickSortR(pcp, sLeft, sSplit-1);    /* Sort the 1st half of the list */
	 if(!pcp->fContinueCalc)	    /* User wishes to terminate sort */
	   return;
	 sTempRight = sRight;		    /* Set up to sort the 2nd half */
	 sTempLeft = sLeft = sSplit + 1;
      }
   }
   return;
}

/****************************************************************\
 * Function name: EnableMenuItem()
 *
 * Parameters:	hwnd is a handle of the current window.
 *		sMenuItem is the ID of the item to Enable/Disable.
 *		fEnable will enable item if TRUE, otherwise disables it.
 *
 * Returns: VOID
 *
 * Purpose: This routine handles enabling/disabling of menu items.  This
 *	    is done by getting Parent and Menu hwnd handles then sending
 *	    the appropriate message.
 *
 * Usage:
 *
\****************************************************************/
VOID EnableMenuItem(HWND hwnd, LONG sMenuItem, BOOL fEnable)
{
   HWND hwndParent = WinQueryWindow(hwnd, QW_PARENT);
   HWND hwndMenu   = WinWindowFromID(hwndParent, FID_MENU);

   WinSendMsg(hwndMenu, MM_SETITEMATTR,
	      MPFROM2SHORT(sMenuItem, TRUE),
	      MPFROM2SHORT(MIA_DISABLED, fEnable ? 0 : MIA_DISABLED));
   return;
}

/****************************************************************\
 * Function name: EntryFldDlgProc()
 *
 * Parameters:	hwnd, msg, mp1, mp2.  Standard PM Dialog Proc params.
 *		A pointer to the current set size is passed in WM_INITDLG.
 *
 * Returns: Terminates with a TRUE iff a new valid data set size is entered.
 *
 * Purpose: Handles all the messages associated with the set entry field
 *	    and calls the appropriate handling procedures.  The purpose
 *	    of this dialog box is to get a new data set size for the
 *	    sort routines.
 *
 *
 * Usage  : If the value entered is valid, the proc will set the
 *		   variable passed in through the WM_INITDLG to the value.
\****************************************************************/
MRESULT EXPENTRY EntryFldDlgProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
   SHORT   sNewSize=0;				      /* Holds new set set */
   static  SHORT *pcSetSize;  /* Set to point to user-passed set size */
   MRESULT sRC;

   switch(msg)
   {
      case WM_INITDLG:
	 FixSysMenu(hwnd);
	 pcSetSize=PVOIDFROMMP(mp2);
	 WinSendDlgItemMsg(hwnd, IDD_ENTRYFLD,EM_SETTEXTLIMIT,	/* Limit len */
				 MPFROM2SHORT(4,0),NULL);
	 WinSetDlgItemShort(hwnd, IDD_ENTRYFLD,
			      *pcSetSize,TRUE);
	 return (MRESULT)0L;			       /* Allow normal focus setting */

      case WM_COMMAND:
	 switch(SHORT1FROMMP(mp1))
	 {
	    case DID_OK:
	       WinQueryDlgItemShort(hwnd, IDD_ENTRYFLD,
				    &sNewSize, TRUE); /* Get the short	    */
	       if(sNewSize>0 && sNewSize<=MAX_ELEMS)  /* Set new data set size */
	       {
		  *pcSetSize =	sNewSize;
		  WinDismissDlg(hwnd,TRUE);
	       }
	       else				      /* Invalid value	    */
	       {
		  MessageBox(hwnd, IDMSG_ERRORSIZE,
				     MB_OK | MB_ERROR, TRUE);
		  return (MRESULT)FALSE;
	       }
	       return (MRESULT)0L;

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

	    default:
	       sRC = WinDefDlgProc(hwnd, msg, mp1, mp2);
	       return sRC;
	 }
	 break;

      default:
	 sRC = WinDefDlgProc(hwnd, msg, mp1, mp2);
	 return sRC;
   }
   return (MRESULT)0L;
}

/****************************************************************\
 * Function name: AboutDlgProc()
 *
 * Parameters:	hwnd, msg, mp1, mp2.  Standard PM Dialog Proc params.
 *
 * Returns:
 *
 * Purpose: Handles all the messages associated with the About dialog.
 *
 * Usage:
 *
\****************************************************************/
MRESULT EXPENTRY AboutDlgProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
   MRESULT sRC;

   switch(msg)
   {
      case WM_INITDLG:
	 FixSysMenu(hwnd);
	 return (MRESULT)0L;

      case WM_COMMAND:
	 WinDismissDlg(hwnd, TRUE);
	 return (MRESULT)0L;

      default:
	 sRC = WinDefDlgProc(hwnd, msg, mp1, mp2);
	 return sRC;
   }
   return (MRESULT)0L;
}

/****************************************************************\
 * Function name: RandomizeData()
 *
 * Parameters:	cSetSize is the current size of the data set
 *
 * Returns: VOID
 *
 * Purpose: This routine randomizes the data arrays.
 *
 * Usage  : This routine assumes that LISTCNT and NELEMS define
 *		   the dimensions of the global Data array which is
 *		   randomized.
\****************************************************************/
VOID RandomizeData(ULONG cSetSize)
{
   USHORT cnt,cnt2;
   for(cnt=0;cnt<LISTCNT;cnt++)
   {
      for(cnt2=0;cnt2<cSetSize;cnt2++)
	 Data[cnt][cnt2] = rand();
   }
   return;
}

/****************************************************************\
 * Function name: FixSysMenu()
 *
 * Parameters:	hwndDlg - handle to calling dialog
 *
 * Returns: VOID
 *
 * Purpose: This routine removes the Restore, Size, Minimize, and
 *	    Maximize options from the system menu of a dialog.
 *
 * Usage  : Called during the WM_INITDLG of a dialog procedure.
 *
\****************************************************************/
VOID FixSysMenu(HWND hwndDlg)
{
   HWND hwndMenu;				    /* handle to system menu */

   hwndMenu = WinWindowFromID(hwndDlg, FID_SYSMENU);

   WinSendMsg(hwndMenu,		      /* delete Restore from the system menu */
	      MM_DELETEITEM,
	      MPFROM2SHORT(SC_RESTORE, TRUE),
	      MPFROMP(NULL));

   WinSendMsg(hwndMenu,			 /* delete Size from the system menu */
	      MM_DELETEITEM,
	      MPFROM2SHORT(SC_SIZE, TRUE),
	      MPFROMP(NULL));

   WinSendMsg(hwndMenu,		     /* delete Minimize from the system menu */
	      MM_DELETEITEM,
	      MPFROM2SHORT(SC_MINIMIZE, TRUE),
	      MPFROMP(NULL));

   WinSendMsg(hwndMenu,		     /* delete Maximize from the system menu */
	      MM_DELETEITEM,
	      MPFROM2SHORT(SC_MAXIMIZE, TRUE),
	      MPFROMP(NULL));
   return;
}

/****************************************************************\
 *  Routine for initializing the help manager
 *--------------------------------------------------------------
 *
 *  Name:    InitHelp()
 *
 *  Purpose: Initializes the IPF help facility
 *
 *  Usage:   Called once during initialization of the program
 *
 *  Method:  Initializes the HELPINIT structure and creates the help
 *	     instance. If successful, the help instance is associated
 *	     with the main window
 *
 *  Returns: VOID
 *
\****************************************************************/
VOID InitHelp(VOID)
{
   HELPINIT hini;

    /* If we return because of an error, Help will be disabled */
   fHelpEnabled = FALSE;
			     /* Initialize help init structure */
   hini.cb = sizeof(HELPINIT);
   hini.ulReturnCode = 0;
			   /* If tutorial added, add name here */
   hini.pszTutorialName = (PSZ)NULL;

   hini.phtHelpTable = (PHELPTABLE)MAKELONG(SORT_HELP_TABLE, 0xFFFF);
   hini.hmodHelpTableModule = 0;
   hini.hmodAccelActionBarModule = 0;
   hini.idAccelTable = 0;
   hini.idActionBar = 0;

   if(!WinLoadString(hab,
		     0,
		     IDS_HELPWINDOWTITLE,
		     HELPLIBRARYNAMELEN,
		     (PSZ)szWindowTitle))
   {
       MessageBox(hwndClient, IDMSG_CANNOTLOADSTRING,
			     MB_OK | MB_ERROR, FALSE);
       return;
   }
   hini.pszHelpWindowTitle = (PSZ)szWindowTitle;

		    /* If debugging, show panel ids, else don't */
#ifdef DEBUG
   hini.fShowPanelId = CMIC_SHOW_PANEL_ID;
#else
   hini.fShowPanelId = CMIC_HIDE_PANEL_ID;
#endif

   if(!WinLoadString(hab,
		     0,
		     IDS_HELPLIBRARYNAME,
		     HELPLIBRARYNAMELEN,
		     (PSZ)szLibName))
   {
       MessageBox(hwndClient, IDMSG_CANNOTLOADSTRING,
			     MB_OK | MB_ERROR, FALSE);
       return;
   }
   hini.pszHelpLibraryName = (PSZ)szLibName;
				     /* Creating help instance */
   hwndHelpInstance = WinCreateHelpInstance(hab, &hini);

   if(hwndHelpInstance == 0 || hini.ulReturnCode)
   {
       MessageBox(hwndFrame, IDMSG_HELPLOADERROR, MB_OK | MB_ERROR, TRUE);
       return;
   }
		    /* Associate help instance with main frame */
   if(!WinAssociateHelpInstance(hwndHelpInstance, hwndFrame))
   {
       MessageBox(hwndFrame, IDMSG_HELPLOADERROR,
				 MB_OK | MB_ERROR, TRUE);
       return;
   }
/* Help manager is successfully initialized so set flag to TRUE */
   fHelpEnabled = TRUE;
   return;
}						  /* InitHelp() */

/****************************************************************\
 *  Processes the Help for Help command from the menu bar
 *--------------------------------------------------------------
 *
 *  Name:    HelpHelpForHelp(mp2)
 *
 *  Purpose: Processes the WM_COMMAND message posted by the Help for
 *	     Help item of the Help menu
 *
 *  Usage:   Called from MainCommand when the Help for Help menu item
 *	     is selected
 *
 *  Method:  Sends an HM_DISPLAY_HELP message to the help instance so
 *	     that the default Help For Help is displayed.
 *
 *  Returns: VOID
 *
\****************************************************************/
VOID HelpHelpForHelp(MPARAM mp2)
{
	   /* This just displays the system help for help panel */
   if(fHelpEnabled)
       if(WinSendMsg(hwndHelpInstance, HM_DISPLAY_HELP, NULL, NULL))
	   MessageBox(hwndClient,
		      IDMSG_HELPDISPLAYERROR,
		      MB_OK | MB_ERROR,
		      FALSE);

   /* This routine currently doesn't use the mp2 parameter but     *\
    *  it is referenced here to prevent an 'Unreferenced Parameter'
   \*  warning at compile time.					   */
   return;
}					   /* HelpHelpForHelp() */

/****************************************************************\
 *  Processes the Extended Help command from the menu bar
 *--------------------------------------------------------------
 *
 *  Name:    HelpExtended(mp2)
 *
 *  Purpose: Processes the WM_COMMAND message posted by the Extended
 *	     Help item of the Help menu.
 *
 *  Usage:   Called from MainCommand when the Extended Help menu item
 *	     is selected.
 *
 *  Method:  Sends an HM_EXT_HELP message to the help instance so that
 *	     the default Extended Help is displayed.
 *
 *  Returns: VOID
 *
\****************************************************************/
VOID HelpExtended(MPARAM mp2)
{
	   /* This just displays the system extended help panel */
   if(fHelpEnabled)
       if(WinSendMsg(hwndHelpInstance, HM_EXT_HELP, NULL, NULL))
	   MessageBox(hwndClient,
		      IDMSG_HELPDISPLAYERROR,
		      MB_OK | MB_ERROR,
		      FALSE);

   /* This routine currently doesn't use the mp2 parameter but     *\
    *  it is referenced here to prevent an 'Unreferenced Parameter'
   \*  warning at compile time.					   */
   return;
}					      /* HelpExtended() */

/****************************************************************\
 *  Processes the Keys Help command from the menu bar
 *--------------------------------------------------------------
 *
 *  Name:    HelpKeys(mp2)
 *
 *  Purpose: Processes the WM_COMMAND message posted by the Keys Help
 *	     item of the Help menu.
 *
 *  Usage:   Called from MainCommand when the Keys Help menu item is
 *	     selected
 *
 *  Method:  Sends an HM_KEYS_HELP message to the help instance so that
 *	     the default Keys Help is displayed.
 *
 *  Returns: VOID
 *
\****************************************************************/
VOID HelpKeys(MPARAM mp2)
{
	       /* This just displays the system keys help panel */
   if(fHelpEnabled)
       if(WinSendMsg(hwndHelpInstance, HM_KEYS_HELP, NULL, NULL))
	   MessageBox(hwndClient,
		      IDMSG_HELPDISPLAYERROR,
		      MB_OK | MB_ERROR,
		      FALSE);

   /* This routine currently doesn't use the mp2 parameter but     *\
    *  it is referenced here to prevent an 'Unreferenced Parameter'
   \*  warning at compile time.					   */
   return;
}						  /* HelpKeys() */

/****************************************************************\
 *  Processes the Index Help command from the menu bar
 *--------------------------------------------------------------
 *
 *  Name:    HelpIndex(mp2)
 *
 *  Purpose: Processes the WM_COMMAND message posted by the Index Help
 *	     item of the Help menu
 *
 *  Usage:   Called from MainCommand when the Index Help menu item
 *	     is selected
 *
 *  Method:  Sends an HM_INDEX_HELP message to the help instance so
 *	     that the default Index Help is displayed.
 *
 *
 *  Returns: VOID
 *
\****************************************************************/
VOID HelpIndex(MPARAM mp2)
{
	      /* This just displays the system help index panel */
   if(fHelpEnabled)
       if(WinSendMsg(hwndHelpInstance, HM_HELP_INDEX, NULL, NULL))
	   MessageBox(hwndClient,
		      IDMSG_HELPDISPLAYERROR,
		      MB_OK | MB_ERROR,
		      FALSE);

   /* This routine currently doesn't use the mp2 parameter but     *\
    *  it is referenced here to prevent an 'Unreferenced Parameter'
   \*  warning at compile time.					   */
   return;
}						 /* HelpIndex() */

/****************************************************************\
 *  Processes the About command from the Help menu
 *--------------------------------------------------------------
 *
 *  Name:    HelpAbout(mp2)
 *
 *  Purpose: Processes the WM_COMMAND message posted by the About item
 *	     of the Help menu
 *
 *  Usage:   Called from MainCommand when the About menu item is selected
 *
 *  Method:  Calls WinDlgBox to display the about box dialog.
 *
 *  Returns: VOID
 *
\****************************************************************/
VOID HelpAbout(MPARAM mp2)
{
				 /* Display the AboutBox dialog */
   WinDlgBox(HWND_DESKTOP,
		hwndClient,
		AboutDlgProc,
		(HMODULE)0,
		IDD_PRODUCTINFO,
		NULL);

   /* This routine currently doesn't use the mp2 parameter but     *\
    *  it is referenced here to prevent an 'Unreferenced Parameter'
   \*  warning at compile time.					   */
   return;
}						 /* HelpAbout() */

/****************************************************************\
 *  Displays the help panel indicated
 *--------------------------------------------------------------
 *
 *  Name:    DisplayHelpPanel(idPanel)
 *
 *  Purpose: Displays the help panel whose id is given
 *
 *  Usage:   Called whenever a help panel is desired to be displayed,
 *	     usually from the WM_HELP processing of the dialog boxes
 *
 *  Method:  Sends HM_DISPLAY_HELP message to the help instance
 *
 *  Returns: VOID
 *
\****************************************************************/
VOID DisplayHelpPanel(LONG idPanel)
{
   if(fHelpEnabled)
       if(WinSendMsg(hwndHelpInstance,
		     HM_DISPLAY_HELP,
		     MPFROMLONG(MAKELONG(idPanel, NULL)),
		     MPFROMSHORT(HM_RESOURCEID)))

	   MessageBox(hwndFrame,
		      IDMSG_HELPDISPLAYERROR,
		      MB_OK | MB_ERROR,
		      TRUE);
   return;
}					  /* DisplayHelpPanel() */

/****************************************************************\
 *  Destroys the help instance
 *--------------------------------------------------------------
 *
 *  Name:    DestroyHelpInstance(VOID)
 *
 *  Purpose: Destroys the help instance for the application
 *
 *  Usage:   Called after exit from message loop
 *
 *  Method:  Calls WinDestroyHelpInstance() to destroy the help instance
 *
 *  Returns: VOID
 *
\****************************************************************/
VOID DestroyHelpInstance(VOID)
{
   if(hwndHelpInstance != 0)
   {
       WinDestroyHelpInstance(hwndHelpInstance);
   }
   return;
}				       /* DestroyHelpInstance() */

/****************************************************************\
 *  Message Box procedure
 *--------------------------------------------------------------
 *
 *  Name:    MessageBox(hwndOwner, nIdMsg, fsStyle, fBeep)
 *
 *  Purpose: Displays the message box with the message given in idMsg
 *	     retrieved from the message table and using the style
 *	     flags in fsStyle
 *
 *  Usage:   Called whenever a MessageBox is to be displayed
 *
 *  Method:  - Message string is loaded from the process' message table
 *	     - Alarm beep is sounded if desired
 *	     - Message box with the message is displayed
 *	     - WinMessageBox return value is returned
 *
 *  Returns: Return value from WinMessageBox()
 *
\****************************************************************/
LONG MessageBox(HWND hwndOwner, LONG idMsg, LONG fsStyle, BOOL fBeep)
{
    CHAR szText[MESSAGELEN];
    LONG sRC;

    if(!WinLoadMessage(hab,
		      (HMODULE)NULL,
		      idMsg,
		      MESSAGELEN,
		      (PSZ)szText))
    {
	WinAlarm(HWND_DESKTOP, WA_ERROR);
	return RETURN_ERROR;
    }

    if(fBeep)
	WinAlarm(HWND_DESKTOP, WA_ERROR);

    sRC = WinMessageBox(HWND_DESKTOP,
			hwndOwner,
			szText,
			(PSZ)NULL,
			IDD_MSGBOX,
			fsStyle);
    return sRC;
}				       /* MessageBox() */
