home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tolkit45.zip / os2tk45 / samples / os2 / hanoi / hanoi.c next >
C/C++ Source or Header  |  1999-05-11  |  59KB  |  1,669 lines

  1. /*==============================================================*\
  2.  *  HANOI.C - Sample PM application
  3.  *
  4.  *
  5.  *      Copyright  1995  IBM Corp.                                         *
  6.  *                                                                         *
  7.  *      DISCLAIMER OF WARRANTIES.  The following [enclosed] code is        *
  8.  *      sample code created by IBM Corporation. This sample code is not    *
  9.  *      part of any standard or IBM product and is provided to you solely  *
  10.  *      for  the purpose of assisting you in the development of your       *
  11.  *      applications.  The code is provided "AS IS", without               *
  12.  *      warranty of any kind.  IBM shall not be liable for any damages     *
  13.  *      arising out of your use of the sample code, even if they have been *
  14.  *      advised of the possibility of   such damages.                      *                               *
  15.  *--------------------------------------------------------------
  16.  *
  17.  *  This program implements a tower of Hanoi program.  This
  18.  *  sample app was written to demonstrate the use of a multi-
  19.  *  threaded program.  The main thread handles the PM interface,
  20.  *  the second thread is started to do the recursive execution
  21.  *  of the Hanoi algorithm.
  22.  *
  23.  *--------------------------------------------------------------
  24.  *
  25.  *  This source file contains the following functions:
  26.  *      main(argc, *argv[])     - main routine
  27.  *      MainWndProc(hwnd, msg, mp1, mp2) - main window procedure
  28.  *      MainPaint(hwnd)         - WM_PAINT processing of main
  29.  *      MainCommand(mp1, mp2)   - WM_COMMAND processing of main
  30.  *      CalcThread()            - Sets up and terminates the secondary thread
  31.  *      DrawDisk(hps, cPole, bLevel, fDraw) - Draws or erases a disk from poles
  32.  *      MoveDisk(hps, bFrom, bTo) - Moves a disk from one pole to another
  33.  *      Hanoi(bHeight, bFrom, bTo, bTemp) - Recursive routine for tower of Hanoi
  34.  *      EnableMenuItem(hwnd, sMenuItem, fEnable) - Activates/deactivates
  35.  *                                                 a menu choice
  36.  *      EntryFldDlgProc(hwnd, msg, mp1, mp2) - Handles the set number of
  37.  *                                             disks dialog box
  38.  *      SetupTowers()        - Sets up the global tower data
  39.  *      FixSysMenu(hwndDlg)  - Remove Restore, Size, Min, & Max from system menu
  40.  *      AboutDlgProc(hwnd, msg, mp1, mp2) - processing about dialog box
  41.  *      ObjectWndProc(hwnd, msg, mp1, mp2) -
  42.  *      MessageBox(hwndOwner, IdMsg, fsStyle, fBeep) - process all messages
  43.  *      Init(VOID)                      - iniitialize the process
  44.  *      ExitProc(VOID)                  - terminate the process
  45.  *      CreateBackgroundThread(VOID)    - process the background thread creating
  46.  *      BackgroundThread(ulThreadParam) - handle the background thread process
  47.  *      DestroyBackgroundThread(VOID)   - destroy background thread process
  48.  *      InitHelp(VOID)                  - initialize the help process
  49.  *      HelpHelpForHelp(mp2)            - process the help for help message
  50.  *      HelpExtended(mp2)               - process the extended help message
  51.  *      HelpKeys(mp2)                   - process the help menu of keys
  52.  *      HelpIndex(mp2)                  - process the help index message
  53.  *      HelpAbout(mp2)                  - process the about dialog box message
  54.  *      DisplayHelpPanel(idPanel)       - process the help panel display
  55.  *      DestroyHelpInstance(VOID)       - destroy the help instance
  56.  *      PostBkThreadMsg(msg, mp1, mp2)  - handle the background thread message
  57.  *
  58. \*==============================================================*/
  59. /*--------------------------------------------------------------*\
  60.  *  Include files, macros, defined constants, and externs
  61. \*--------------------------------------------------------------*/
  62. #define INCL_WIN
  63. #define INCL_HELP
  64. #define INCL_WINHEAP
  65. #define INCL_WINDIALOGS
  66. #define INCL_WINWINDOWMGR
  67. #define INCL_GPIPRIMITIVES
  68. #define INCL_GPIBITMAPS
  69. #define INCL_GPILCIDS
  70. #define INCL_DOSPROCESS
  71. #define INCL_DOSSEMAPHORES
  72.  
  73. #include <os2.h>
  74. #include <stdio.h>
  75. #include <string.h>
  76. #include <stdlib.h>
  77. #include "hanoi.h"
  78.  
  79. /*--------------------------------------------------------------*\
  80.  *  Global variables
  81. \*--------------------------------------------------------------*/
  82. CHAR   szClientClass[] = "Hanoi";
  83. BYTE   abTowers[3][MAXDISKCNT];    /* Used to hold disk numbers on each post */
  84. BYTE   abTowersNdx[3];             /* Current number of disks on each post   */
  85. BYTE   cTowerSize = DEFAULTSIZE;   /* Holds the total number of disks        */
  86. int    aPolePos[3]= {POSTOFFSET,  /* Holds offset drawing information       */
  87.                        POSTOFFSET + POSTSPACE,
  88.                        POSTOFFSET + 2 * POSTSPACE};
  89. ULONG  ulIterations;
  90. TID    tidCalcThread = 0;                         /* Secondary thread ID */
  91. BOOL   fContinueCalc;
  92. HWND   hwndMainFrame = NULLHANDLE;    /* Handle to the main frame window */
  93. HWND   hwndMain;                     /* Handle to the main client window */
  94. CHAR   szAppName[MAXNAMEL];        /* Buffer for application name string */
  95. HAB    hab;                              /* Anchor block for the process */
  96. HMQ    hmq;                      /* Handle to the process' message queue */
  97. CHAR   szUntitled[MESSAGELEN];           /* buffer for "Untitled" string */
  98. static BOOL fThreadCreated = FALSE;
  99. static HEV hevThreadInit;         /* semaphore for thread initialization */
  100. static HAB habBkThread;
  101. static HMQ hmqBkThread;
  102. static TID tidBkThread;
  103. static HWND hwndObject;
  104. static CHAR szObjectClass[MAXNAMEL];
  105. BOOL   fHelpEnabled = FALSE;         /* flag to determine if help is enabled */
  106. static CHAR szLibName[HELPLIBRARYNAMELEN];
  107. static CHAR szWindowTitle[HELPLIBRARYNAMELEN];
  108. static HWND hwndHelpInstance = NULLHANDLE;
  109. BOOL   fViewOn = TRUE;                           /* flag to view on or off */
  110. CHAR   *pszDisk       ="Disk Information";
  111. CHAR   *pszDisksMoved ="Disks Moved:";
  112.  
  113. /*--------------------------------------------------------------*\
  114.  *  Entry point declarations                                    *
  115. \*--------------------------------------------------------------*/
  116. MRESULT EXPENTRY MainWndProc(HWND, ULONG, MPARAM, MPARAM);
  117. MRESULT EXPENTRY EntryFldDlgProc(HWND, ULONG, MPARAM, MPARAM);
  118. MRESULT EXPENTRY AboutDlgProc(HWND, ULONG, MPARAM, MPARAM);
  119. MRESULT EXPENTRY ObjectWndProc(HWND, ULONG, MPARAM, MPARAM);
  120. ULONG   MessageBox(HWND, LONG, LONG, BOOL);
  121. VOID APIENTRY ExitProc(VOID);
  122. BOOL Init(VOID);
  123. VOID MainPaint(HWND);
  124. LONG MainCommand(HWND, MPARAM, MPARAM);
  125. VOID CalcThread(VOID);
  126. VOID DrawDisk(HPS, BYTE, BYTE, BYTE);
  127. VOID MoveDisk(HPS, BYTE, BYTE);
  128. VOID Hanoi(BYTE, BYTE, BYTE, BYTE);
  129. VOID EnableMenuItem(HWND, LONG, BOOL);
  130. VOID SetupTowers(VOID);
  131. VOID FixSysMenu(HWND);
  132.  
  133. BOOL CreateBackgroundThread(VOID);
  134. VOID BackgroundThread(ULONG);
  135. BOOL PostBkThreadMsg(ULONG, MPARAM, MPARAM);
  136. VOID DestroyBackgroundThread(VOID);
  137.  
  138. VOID InitHelp(VOID);
  139. VOID HelpHelpForHelp(MPARAM);
  140. VOID HelpExtended(MPARAM);
  141. VOID HelpKeys(MPARAM);
  142. VOID HelpIndex(MPARAM);
  143. VOID HelpAbout(MPARAM);
  144. VOID DisplayHelpPanel(LONG);
  145. VOID DestroyHelpInstance(VOID);
  146.  
  147. /****************************************************************\
  148.  *  Main routine
  149.  *--------------------------------------------------------------
  150.  *
  151.  *  Name:    main(argc, *argv[])
  152.  *
  153.  *  Purpose: Initializes the PM environment, calls the initialization
  154.  *           routine, creates main window, and polls the message queue
  155.  *
  156.  *  Usage:
  157.  *
  158.  *  Method:  - obtains anchor block handle and creates message
  159.  *             queue
  160.  *           - calls the initialization routine
  161.  *           - creates the main frame window which creates the
  162.  *             main client window
  163.  *           - polls the message queue via Get/Dispatch Msg loop
  164.  *           - upon exiting the loop, performs exit housekeeping
  165.  *             and then exits
  166.  *
  167.  *  Returns: 0 - if successful execution completed
  168.  *           1 - if error
  169.  *
  170. \****************************************************************/
  171. int main(LONG argc, CHAR *argv[])
  172. {
  173.    QMSG     qmsg;                      /* Message structure */
  174.    ULONG    flCtlData = FCF_STANDARD;  /* Frame control data */
  175.  
  176.    /* If command line arg, use as the initial number of disks */
  177.    if(argc > 1)
  178.    {
  179.       LONG sHold = (LONG)atoi(argv[1]);
  180.       if(sHold > 0L && sHold <= MAXDISKCNT)
  181.          cTowerSize = (BYTE)sHold;
  182.    }
  183.  
  184.    SetupTowers();
  185.  
  186.    hab = WinInitialize(0UL);
  187.  
  188.    if(!hab)
  189.    {
  190.       DosBeep(BEEP_WARN_FREQ, BEEP_WARN_DUR);
  191.       return RETURN_ERROR;
  192.    }
  193.  
  194.    hmq = WinCreateMsgQueue(hab, 0L);
  195.  
  196.    if(!hmq)
  197.    {
  198.       DosBeep(BEEP_WARN_FREQ, BEEP_WARN_DUR);
  199.       WinTerminate(hab);
  200.       return RETURN_ERROR;
  201.    }
  202.  
  203.    if(!Init())
  204.    {
  205.       MessageBox(HWND_DESKTOP,
  206.                  IDMSG_INITFAILED,
  207.                  MB_OK | MB_ERROR,
  208.                  TRUE);
  209.  
  210.       return RETURN_ERROR;
  211.    }
  212.    /* Create the main window */
  213.  
  214.    hwndMainFrame = WinCreateStdWindow(HWND_DESKTOP,
  215.                                       WS_VISIBLE,
  216.                                       &flCtlData,
  217.                                       szAppName,
  218.                                       NULL,
  219.                                       WS_VISIBLE,
  220.                                       (HMODULE)NULL,
  221.                                       ID_RESOURCE,
  222.                                       (PHWND)&hwndMain);
  223.    if(hwndMainFrame == NULLHANDLE)
  224.    {
  225.       MessageBox(HWND_DESKTOP,
  226.                  IDMSG_MAINWINCREATEFAILED,
  227.                  MB_OK | MB_ERROR,
  228.                  TRUE);
  229.  
  230.       return RETURN_ERROR;
  231.    }
  232.    InitHelp();
  233.    /* Get/Dispatch Message loop */
  234.    while(WinGetMsg(hab, &qmsg, NULLHANDLE, 0UL, 0UL))
  235.        WinDispatchMsg(hab, &qmsg);
  236.  
  237.    /* Destroy the help instance */
  238.    DestroyHelpInstance();
  239.  
  240. #ifdef BACKGROUND_THREAD
  241.    DestroyBackgroundThread();
  242. #endif
  243.  
  244.    /* Perform exit housekeeping */
  245.    ExitProc();
  246.  
  247.    return RETURN_SUCCESS;
  248. }   /* main() */
  249.  
  250. /****************************************************************\
  251.  *  Initialization routine
  252.  *--------------------------------------------------------------
  253.  *
  254.  *  Name:    Init(VOID)
  255.  *
  256.  *  Purpose: Performs initialization functions required before the main
  257.  *           window can be created.
  258.  *
  259.  *  Usage:   Called once before the main window is created.
  260.  *
  261.  *  Method   - registers all window classes
  262.  *           - performs any command line processing
  263.  *
  264.  *
  265.  *  Returns: TRUE - initialization is successful
  266.  *           FALSE - initialization failed
  267. \****************************************************************/
  268. BOOL Init(VOID)
  269. {
  270.    /* load application name from resource file */
  271.    if(!WinLoadString(hab, NULLHANDLE, IDS_APPNAME, MAXNAMEL, szAppName))
  272.       return FALSE;
  273.  
  274.    /* load "untitled" string */
  275.    if(!WinLoadString(hab, NULLHANDLE, IDS_UNTITLED, MESSAGELEN, szUntitled))
  276.       return FALSE;
  277.  
  278.    /* register the main client window class */
  279.    if(!WinRegisterClass(hab,
  280.                         (PSZ)szAppName,
  281.                         (PFNWP)MainWndProc,
  282.                         CS_SIZEREDRAW | CS_CLIPCHILDREN,
  283.                         0UL))
  284.    {
  285.       return FALSE;
  286.    }
  287.  
  288.    /* If you wish to create a thread for background processing, define
  289.     *  the BACKGROUND_THREAD constant used by the following routine.
  290.     * The routines for the background thread are included in this source
  291.     *  file.
  292.     */
  293.  
  294. #ifdef BACKGROUND_THREAD
  295.    if(!CreateBackgroundThread())
  296.       return FALSE;
  297. #endif
  298.  
  299.    return TRUE;
  300. }   /* Init() */
  301.  
  302. /****************************************************************\
  303.  *  Exit housekeeping procedure
  304.  *--------------------------------------------------------------
  305.  *
  306.  *  Name:    ExitProc()
  307.  *
  308.  *  Purpose: Cleans up certain resources when the application terminates
  309.  *
  310.  *  Usage:   Routine is called by main just before the application exits
  311.  *
  312.  *  Method:  Global resources, such as the main window and message queue,
  313.  *           are destroyed and any system resources used are freed.
  314.  *
  315.  *  Returns: VOID
  316.  *
  317. \****************************************************************/
  318. VOID APIENTRY ExitProc()
  319. {
  320.     /* Destroy the secondary thread if it exists */
  321.     if (tidCalcThread)
  322.     {
  323.        fContinueCalc = FALSE;                    /* Notify thread to quit */
  324.        DosWaitThread(&tidCalcThread, DCWW_WAIT); /* Wait until complete */
  325.     }
  326.  
  327.     /* destroy the main window if it exists */
  328.     if(WinIsWindow(hab, hwndMainFrame))
  329.        WinDestroyWindow(hwndMainFrame);
  330.  
  331.     /*--------------------------------------------------*\
  332.      *      Any other system resources used
  333.      *      (e.g. memory or files) should be freed here
  334.     \*--------------------------------------------------*/
  335.     WinDestroyMsgQueue(hmq);
  336.  
  337.     WinTerminate(hab);
  338.  
  339.     return;
  340.  
  341. }   /* ExitProc() */
  342.  
  343. /****************************************************************\
  344.  *  Main client window procedure
  345.  *--------------------------------------------------------------
  346.  *
  347.  *  Name:    MainWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  348.  *
  349.  *  Purpose: Handles all the messages associated with the main window
  350.  *           and calls the appropriate handling procedures.
  351.  *
  352.  *  Usage:   Called for each message placed in the window's message queue
  353.  *
  354.  *  Method:  Called only by main().  Note that when WM_PAINT executes,
  355.  *           the secondary thread may change data during the update
  356.  *           which may cause text to appear at the "old" position in the
  357.  *           window.  However, this is NOT a write
  358.  *           conflict, as only 1 thread does the writing.
  359.  *
  360.  *  Returns: Return values are determined by each message
  361.  *
  362. \****************************************************************/
  363. MRESULT EXPENTRY MainWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  364. {
  365.    CHAR   szMsg[MSGBUFSIZE];           /* Sprintf buffer */
  366.    MRESULT sRC;
  367.  
  368.    switch(msg)
  369.    {
  370.       case WM_PAINT:
  371.          MainPaint(hwnd);
  372.          break;
  373.  
  374.       case WM_COMMAND:
  375.          MainCommand(hwnd, mp1, mp2);
  376.          break;
  377.  
  378.       case WM_SIZE:
  379. //         WinSendMsg(hwnd, WM_ERASEBACKGROUND, ,,,,
  380.          break;
  381.  
  382.       case IDM_DONE:
  383.          EnableMenuItem(hwnd, IDM_START, TRUE);  /* Reenable Start & set  */
  384.          EnableMenuItem(hwnd, IDM_SET, TRUE);
  385.          EnableMenuItem(hwnd, IDM_STOP, FALSE);  /* Disable stop          */
  386.                                                  /* Print msg             */
  387.          if(ulIterations > 1)
  388.             sprintf(szMsg,"%lu disk transfers were made.", ulIterations);
  389.          else
  390.             sprintf(szMsg,"%lu disk transfer was made.", ulIterations);
  391.          WinMessageBox(HWND_DESKTOP,
  392.                        hwnd,
  393.                        szMsg,
  394.                        "Done!",
  395.                        0UL,
  396.                        MB_OK | MB_MOVEABLE | MB_INFORMATION);
  397.          SetupTowers();                          /* Reset towers          */
  398.          WinInvalidateRect(hwnd, NULL, FALSE);   /* Force a screen redraw */
  399.          break;
  400.  
  401.       case HM_QUERY_KEYS_HELP:
  402.          return (MRESULT)PANEL_KEYSHELP;
  403.  
  404.       default:
  405.          sRC = WinDefWindowProc(hwnd, msg, mp1, mp2);
  406.          return sRC;
  407.    }
  408.    /* all window procedures should return 0 as a default */
  409.    return (MRESULT)NULL;
  410. }   /* MainWndProc() */
  411.  
  412. /****************************************************************\
  413.  *  Main client painting routine
  414.  *--------------------------------------------------------------
  415.  *
  416.  *  Name:    MainPaint(HWND hwnd)
  417.  *
  418.  *  Purpose: Paints the main client window.
  419.  *
  420.  *  Usage:   Routine is called whenver the client window
  421.  *           procedure receives a WM_PAINT message
  422.  *
  423.  *
  424.  *  Method:  -begins painting by calling WinBeginPaint and retrieving
  425.  *            the HPS for the window
  426.  *           -performs any painting desired
  427.  *           -ends painting by calling WinEndPaint
  428.  *
  429.  *  Returns: 0 - if successful execution completed
  430.  *           1 - if error
  431.  *
  432. \****************************************************************/
  433. VOID MainPaint(HWND hwnd)
  434. {
  435.    RECTL  rclUpdate, rect;               /* Rectangle struct for painting */
  436.    HPS    hps = WinBeginPaint(hwnd, NULLHANDLE, (PRECTL)&rclUpdate);
  437.    POINTL ptl;                           /* Point struct for painting     */
  438.    BYTE   cPole, cnt;                    /* Utility variables             */
  439.    CHAR   szBuffer[BUFF_SIZE];
  440.    LONG   lVert, bHeight, bLeft;
  441.    FONTMETRICS FontMetrics;
  442.  
  443.    /* Fill update rectangle with window color. */
  444.    WinFillRect(hps, &rclUpdate, SYSCLR_WINDOW);
  445.    WinQueryWindowRect(hwndMain, &rect);
  446.    /*
  447.     *     Get the character set size.
  448.     */
  449.    GpiQueryFontMetrics(hps, sizeof(FontMetrics), &FontMetrics);
  450.    ptl.x = (rect.xRight -
  451.             ((signed long)strlen(pszDisk) * FontMetrics.lAveCharWidth)) / 2;
  452.    ptl.y = (rect.yTop - BASETHICK - 3 * FontMetrics.lEmHeight)  / 2;
  453.    if(fViewOn)                    /*  view disk information  */
  454.    {
  455.       memset((void *)szBuffer, 0, sizeof(szBuffer));
  456.       strcpy(szBuffer, pszDisk);
  457.       GpiCharStringAt(hps, &ptl, (long)strlen(szBuffer), szBuffer);
  458.    }
  459.    /*
  460.     *  Center the base in the middle of our screen.
  461.     */
  462.    bLeft = rect.xRight - BASELEN;
  463.    if(bLeft < 0L)
  464.       bLeft = 0L;
  465.    DrawRect( bLeft / 2,
  466.              (rect.yTop - BASETHICK) / 2,
  467.              (rect.xRight + BASELEN) / 2,
  468.              (rect.yTop + BASETHICK) / 2,
  469.               CLR_DARKGREEN);
  470.  
  471.    /* Draw the 3 posts.  */
  472.    lVert   = (rect.yTop - BASETHICK) / 2 + BASETHICK;
  473.    bHeight = lVert + (cTowerSize * DISKSPACE + POSTEXTRAHT);
  474.    for(cnt = 0; cnt < 3; cnt++)
  475.    {
  476.       int LeftPole;
  477.  
  478.       aPolePos[cnt] = ((rect.xRight - BASELEN) / 2) +
  479.                        ( (BASELEN - (POSTHALF + POSTWIDTH) ) / 2 * cnt);
  480.       LeftPole = aPolePos[cnt];
  481.       if(LeftPole < 0)
  482.          LeftPole = 0;
  483.       if((aPolePos[cnt] + POSTHALF + POSTWIDTH) > 1)
  484.       {
  485.          DrawRect(LeftPole,
  486.                   bHeight,
  487.                   aPolePos[cnt] + POSTHALF + POSTWIDTH,
  488.                   lVert,
  489.                   CLR_DARKGREEN);
  490.       }
  491.       else
  492.       {
  493.          DrawRect(LeftPole,
  494.                   bHeight,
  495.                   aPolePos[cnt] + POSTHALF + POSTWIDTH,
  496.                   lVert,
  497.                   CLR_WHITE);
  498.       }
  499.    }
  500.  
  501.    /* Place the appropriate disks on each pole. */
  502.    for(cPole = 0; cPole < 3; cPole++)
  503.    {
  504.       BYTE bCnt;                         /* Utility variable             */
  505.  
  506.       for(bCnt = 0; bCnt < abTowersNdx[cPole]; bCnt++)
  507.       {
  508.          DrawDisk(hps, cPole, bCnt, fDRAW);
  509.       }
  510.    }
  511.    WinEndPaint(hps);
  512.    return;
  513. }   /* MainPaint() */
  514.  
  515. /****************************************************************\
  516.  *  Main window WM_COMMAND processing procedure
  517.  *--------------------------------------------------------------
  518.  *
  519.  *  Name:    MainCommand(HWND hwnd, MPARAM mp1, MPARAM mp2)
  520.  *
  521.  *  Purpose: Calls the appropriate procedures that deal with the
  522.  *           selected menu item.
  523.  *
  524.  *  Usage:   Routine is called whenever a WM_COMMAND message is posted
  525.  *           to the main window.
  526.  *
  527.  *  Method:  a switch statement branches on the id of the menu item
  528.  *           that posted the message and the appropriate action for
  529.  *           that item is taken.  Any menu ids that are not part of
  530.  *           the standard menu set are passed onto the user defined
  531.  *           WM_COMMAND processing procedure.
  532.  *
  533.  *  Returns: Return values are determined by each message
  534.  *
  535. \****************************************************************/
  536. LONG MainCommand(HWND hwnd, MPARAM mp1, MPARAM mp2)
  537. {
  538.    switch(SHORT1FROMMP(mp1))
  539.    {
  540.       case IDM_START:
  541.                               /* Set continuation fContinueCalc */
  542.          fContinueCalc = TRUE;
  543.          ulIterations = 0UL;            /* Zero iteration counter */
  544.          if(DosCreateThread(&tidCalcThread, (PFNTHREAD)CalcThread,
  545.                                    0UL, 0UL, STACK))
  546.             DosBeep(440, 175);
  547.          else               /* Disallow menu items that could change data */
  548.          {                          /* while the second thread is running */
  549.             EnableMenuItem(hwnd, IDM_START, FALSE);  /* Disable Start item */
  550.             EnableMenuItem(hwnd, IDM_SET,   FALSE);  /* Disable Set item   */
  551.             EnableMenuItem(hwnd, IDM_STOP,  TRUE);   /* Enable Stop item   */
  552.          }
  553.          break;
  554.  
  555.       case IDM_STOP:
  556.          fContinueCalc = FALSE;       /* Notify thread to quit */
  557.          break;
  558.  
  559.       case IDM_SET:                /* Pop up the query/set box */
  560.          if(WinDlgBox(HWND_DESKTOP, hwnd,
  561.                    (PFNWP)EntryFldDlgProc, NULLHANDLE, IDD_SETCOUNT, NULL))
  562.          {
  563.             SetupTowers();                     /* Reset towers */
  564.             WinInvalidateRect(hwnd, NULL, FALSE);
  565.          }
  566.          break;
  567.  
  568.       case IDM_ON:
  569.          fViewOn = TRUE;
  570.          WinInvalidateRect(hwnd, NULL, FALSE);
  571.          break;
  572.  
  573.       case IDM_OFF:
  574.          fViewOn = FALSE;
  575.          WinInvalidateRect(hwnd, NULL, FALSE);
  576.          break;
  577.  
  578.       case IDM_HELPHELPFORHELP:
  579.          HelpHelpForHelp(mp2);
  580.          break;
  581.  
  582.       case IDM_HELPEXTENDED:
  583.          HelpExtended(mp2);
  584.          break;
  585.  
  586.       case IDM_HELPKEYS:
  587.          HelpKeys(mp2);
  588.          break;
  589.  
  590.       case HM_QUERY_KEYS_HELP:
  591.          return PANEL_KEYSHELP;
  592.  
  593.       case IDM_HELPINDEX:
  594.          HelpIndex(mp2);
  595.          break;
  596.  
  597.       case IDM_HELPPRODUCTINFO:
  598.          HelpAbout(mp2);
  599.          break;
  600.       default:
  601.          break;
  602.    }
  603.    return 0L;
  604. }         /*  MainCommand()  */
  605.  
  606. /****************************************************************\
  607.  *  Creates the Background thread
  608.  *--------------------------------------------------------------
  609.  *
  610.  *  Name:    CreateBackgroundThread(VOID)
  611.  *
  612.  *  Purpose: Routine creates the background thread
  613.  *
  614.  *  Usage:   Routine is called at initialization time to create a
  615.  *           background processing thread.
  616.  *
  617.  *  Method:  The routine calls DosCreateThread with the Background
  618.  *           thread routine.
  619.  *
  620.  *  Returns: TRUE if thread is created successfully.
  621.  *           FALSE if not or if the thread was already created
  622.  *
  623. \****************************************************************/
  624. BOOL CreateBackgroundThread(VOID)
  625. {
  626.    ULONG sRet;
  627.  
  628.    /*------------------------------------------------------------------*
  629.     *  Note: C-runtime considerations:
  630.     *  If you use non-reentrant C runtime functions in the second thread,
  631.     *  then you will need to use _beginthread and _endthread instead of
  632.     *  DosCreateThread and DosExit.
  633.     *------------------------------------------------------------------*/
  634.    if(!fThreadCreated)
  635.    {
  636.       DosCreateEventSem((PSZ)NULL, &hevThreadInit, 0UL, FALSE);
  637.       sRet = DosCreateThread(&tidBkThread,
  638.                              (PFNTHREAD)BackgroundThread,
  639.                              0UL,
  640.                              0UL,
  641.                              THREADSTACKSIZE);
  642.    }
  643.    else
  644.    {
  645.       return FALSE;
  646.    }
  647.  
  648.                   /* wait until the thread has finished initialization */
  649.    if(DosWaitEventSem(hevThreadInit, SEM_TIMEOUT))
  650.       return FALSE;
  651.  
  652.     return (BOOL)(sRet == 0UL);
  653. }   /* CreateBackgroundThread() */
  654.  
  655. /****************************************************************\
  656.  *  Destroys the Background thread
  657.  *--------------------------------------------------------------
  658.  *
  659.  *  Name:    DestroyBackgroundThread(VOID)
  660.  *
  661.  *  Purpose: Routine destroys the background thread
  662.  *
  663.  *  Usage:   Routine is called at exit time to destroy the background
  664.  *           processing thread.
  665.  *
  666.  *  Method:  The routine posts a WM_CLOSE message to the object window
  667.  *           to end its message loop.  It then waits to make sure that
  668.  *           the thread has been terminated before it returns.
  669.  *
  670.  *  Returns: VOID
  671.  *
  672. \****************************************************************/
  673. VOID DestroyBackgroundThread(VOID)
  674. {
  675.    PostBkThreadMsg(WM_CLOSE, MPVOID, MPVOID);
  676.  
  677.    DosWaitThread(&tidBkThread, 0UL);
  678.    return;
  679. }                                  /* DestroyBackgroundThread() */
  680.  
  681. /****************************************************************\
  682.  *  DrawDisk procedure
  683.  *--------------------------------------------------------------
  684.  *
  685.  *  Name:    DrawDisk(HPS hps, BYTE cPole, BYTE bLevel, BYTE fDraw)
  686.  *
  687.  *  Purpose: This routine takes a PS handle, the Hanoi spindle and
  688.  *           disk level and draws an appropriately sized disk.
  689.  *
  690.  *  Usage:   Routine is called whenever the disk need to display.
  691.  *
  692.  *  Method:  Does not grab exclusive access to the screen before
  693.  *           drawing.
  694.  *
  695.  *  Returns: VOID
  696.  *
  697. \****************************************************************/
  698. VOID DrawDisk(HPS hps, BYTE cPole, BYTE bLevel, BYTE fDraw)
  699. {
  700.    int Ystart;
  701.    /* Calculate the disk's width */
  702.    int Width = (MAXDISKWIDTH - MINDISKWIDTH) * abTowers[cPole][bLevel]
  703.               / cTowerSize + MINDISKWIDTH;
  704.    /* Center disk on pole */
  705.    int Xstart = (aPolePos[cPole] - Width / 2) + DISKTHICK;
  706.    int Xend   = Xstart + Width;
  707.    POINTL ptl;
  708.    RECTL  rect;             /* Rectangle struct for painting */
  709.  
  710.    /* Calculate Bottom of disk */
  711.    WinQueryWindowRect(hwndMain, &rect);
  712.    Ystart = (rect.yTop + BASETHICK) / 2 + (DISKSPACE * bLevel);
  713.  
  714.    if(aPolePos[cPole] < (Width / 2))
  715.       Xstart = 0;             /* disk's left edge is outside space */
  716.  
  717.    if(fDraw == fDRAW)             /* If we are to draw the disk */
  718.    {
  719.       DrawRect(Xstart, Ystart, Xend,
  720.                Ystart + DISKTHICK - 1, CLR_RED);
  721.    }
  722.    else       /* We are to erase the disk, then redraw the pole */
  723.    {
  724.       int LeftPole;
  725.  
  726.       DrawRect(Xstart, Ystart, Xend,
  727.                Ystart + DISKTHICK - 1, CLR_WHITE);
  728.  
  729.       LeftPole = aPolePos[cPole];
  730.       if(LeftPole < 0)
  731.          LeftPole = 0;
  732.       if((aPolePos[cPole] + POSTHALF + POSTWIDTH) > 0)
  733.          DrawRect(LeftPole,
  734.                   Ystart,
  735.                   aPolePos[cPole] + POSTHALF + POSTWIDTH,
  736.                   Ystart + DISKTHICK - 1,
  737.                   CLR_DARKGREEN);
  738.    }
  739.    return;
  740. }
  741.  
  742. /****************************************************************\
  743.  *  CalcThread procedure
  744.  *--------------------------------------------------------------
  745.  *
  746.  * Name:    CalcThread(VOID)
  747.  *
  748.  * Purpose: Calls the recursive Hanoi with initial setting of 0, 2, 1
  749.  *          meaning from pole 0, to pole 2, using pole 1 as a temporary.
  750.  *          Hanoi returns when finished, or the user says stop.
  751.  *          This proc then sets a critical section so the posted
  752.  *          message won't be handled until the thread is terminated.
  753.  *
  754.  * Usage:   Routine is called whenever a IDM_START message is posted to
  755.  *          the main window.
  756.  *
  757.  * Method:
  758.  *
  759.  * Returns: VOID
  760.  *
  761. \****************************************************************/
  762. VOID CalcThread(VOID)
  763. {                                    /* Anchor block for the process */
  764.    HAB habb = WinInitialize(0UL);     /* Called to increase Ring 2 stack size */
  765.  
  766.           /* Execute the recursive routine */
  767.    Hanoi(cTowerSize, (BYTE)0, (BYTE)2, (BYTE)1);
  768.    WinTerminate(habb);
  769.  
  770.    DosEnterCritSec(); /* Set critical so that IDM_DONE isn't processed */
  771.                       /* until this thread has completely terminated   */
  772.    WinPostMsg(hwndMain, IDM_DONE, MPVOID, MPVOID);
  773.  
  774.    DosExitCritSec();             /* Restore normal thread dispatching */
  775.  
  776.    DosExit(EXIT_THREAD, 0UL);    /* Terminate thread */
  777.    return;
  778. }       /*  CalcThread()  */
  779.  
  780. /****************************************************************\
  781.  *  EnableMenuItem procedure
  782.  *--------------------------------------------------------------
  783.  *
  784.  * Name:    EnableMenuItem(HWND hwnd, LONG sMenuItem, BOOL fEnable)
  785.  *
  786.  * Purpose: This routine handles enabling/disabling of menu items.  This
  787.  *          is done by getting Parent and Menu hwnd handles then sending
  788.  *          the appropriate message.
  789.  *
  790.  * Usage:   Routine is called to enable or disable one item from menu.
  791.  *
  792.  * Method:
  793.  *
  794.  * Returns: VOID
  795.  *
  796. \****************************************************************/
  797. VOID EnableMenuItem(HWND hwnd, LONG sMenuItem, BOOL fEnable)
  798. {
  799.    HWND hwndParent = WinQueryWindow(hwnd, QW_PARENT);
  800.    HWND hwndMenu   = WinWindowFromID(hwndParent, FID_MENU);
  801.  
  802.    WinSendMsg(hwndMenu, MM_SETITEMATTR,
  803.               MPFROM2SHORT(sMenuItem, TRUE),
  804.               MPFROM2SHORT(MIA_DISABLED, fEnable ? 0 : MIA_DISABLED));
  805.    return;
  806. }     /*  EnableMenuItem()  */
  807.  
  808. /****************************************************************\
  809.  *  Hanoi recursion procedure
  810.  *--------------------------------------------------------------
  811.  *
  812.  *  Name:    Hanoi(BYTE bHeight, BYTE bFrom, BYTE bTo, BYTE bTemp)
  813.  *
  814.  *  Purpose: This routine implements a recursive Hanoi program that
  815.  *           works as follows:  By recursion, move all the disks,
  816.  *           except for the bottom disk to the temporary stack.
  817.  *           Then move the bottom disk to the target spindle.
  818.  *           Now recursively move the stack on the temporary spindle
  819.  *           to the target spindle.  The limiting case is when Hanoi()
  820.  *           is called with a bHeight of 0 in which case the depth
  821.  *           recursion is terminated.
  822.  *
  823.  *  Usage:
  824.  *
  825.  *  Method:  This routine checks the ->fContinueCalc flag, which is
  826.  *           set by the main thread when the user selects stop, to
  827.  *           see if the user wishes to abort the algorithm.  If so,
  828.  *           it backs out and exits.
  829.  *
  830.  *  Returns: VOID
  831.  *
  832. \****************************************************************/
  833. VOID Hanoi(BYTE bHeight, BYTE bFrom, BYTE bTo, BYTE bTemp)
  834.              /* bHeight - The number of disks in the from stack to move */
  835.              /* bFrom   - The from spindle number, 0-2 */
  836.              /* bTo     - The to spindle number, 0-2 */
  837.              /* bTemp   - The temporary spindle number */
  838. {
  839.    HPS    hps;                           /* Handle for painting */
  840.    RECTL  rect;                /* Rectangle struct for painting */
  841.    CHAR   szBuffer[BUFF_SIZE];
  842.    LONG   lIncrement = 1L;
  843.    POINTL ptl;
  844.    int    Center;
  845.    FONTMETRICS FontMetrics;
  846.  
  847.    if(bHeight <= 0 || !fContinueCalc)       /* Exit up if no more */
  848.       return;                    /* disks or the user said Stop */
  849.  
  850.    Hanoi((BYTE)(bHeight - 1),         /* Move all but bottom disk */
  851.          (BYTE)bFrom,
  852.          (BYTE)bTemp,
  853.          (BYTE)bTo);
  854.  
  855.    if(!fContinueCalc)                   /* If user said to stop */
  856.       return;
  857.    /* Display bFrom -> bTo */
  858.    hps = WinGetPS(hwndMain);
  859.    MoveDisk(hps, bFrom, bTo);             /* Move the bottom disk */
  860.    if (fViewOn)
  861.    {
  862.       WinQueryWindowRect(hwndMain, &rect);
  863.       GpiQueryFontMetrics(hps, sizeof(FontMetrics), &FontMetrics);
  864.       /*
  865.        *  Set center.
  866.        */
  867.       Center = rect.xRight / 2;
  868.       /*
  869.        *left hand side of text to blank out
  870.        */
  871.       rect.xLeft = ptl.x = (Center +
  872.                             ((signed long)(strlen(pszDisksMoved)) / 2)
  873.                              * FontMetrics.lAveCharWidth);
  874.  
  875.       /*
  876.        *   Set bottom.
  877.        */
  878.       ptl.y = (rect.yTop - BASETHICK - 8 * FontMetrics.lEmHeight)  /2;
  879.       /*
  880.        *top
  881.        */
  882.       rect.yTop = (rect.yTop - BASETHICK - 6 * FontMetrics.lEmHeight)  /2;
  883.  
  884.  
  885.       rect.yBottom = ptl.y;
  886.       /*
  887.        *  Clear off the previous numbers by drawing an opaque rectangle.
  888.        */
  889.       memset((void *)szBuffer, (int)' ', sizeof(szBuffer));
  890.  
  891.       GpiCharStringPosAt(hps,
  892.                          &ptl,
  893.                          &rect,
  894.                          (ULONG)CHS_OPAQUE,
  895.                          DELETE_WIDTH,
  896.                          szBuffer,
  897.                          &lIncrement);
  898.  
  899.       szBuffer[DELETE_WIDTH] = '\0';
  900.       GpiCharStringAt(hps, &ptl, (signed long)strlen(szBuffer), szBuffer);
  901.       strcpy(szBuffer, pszDisksMoved);
  902.       /*
  903.        *  Display the number of disks moved.
  904.        */
  905.       /*
  906.        *  Initial text to draw is "Disks moved:"
  907.        */
  908.       ptl.x = Center - ((((int)strlen(pszDisksMoved) + MAX_DIGITS) / 2)* FontMetrics.lAveCharWidth);
  909.       GpiCharStringAt(hps, &ptl, (signed long)strlen(szBuffer), szBuffer);
  910.       sprintf(szBuffer, "%5lu", ulIterations + 1 );
  911.  
  912.       ptl.x = Center +
  913.               ((((int)strlen(pszDisksMoved) / 2) + 1) * FontMetrics.lAveCharWidth);
  914.       GpiCharStringAt(hps, &ptl, (int)strlen(szBuffer), szBuffer);
  915.    }
  916.    WinReleasePS(hps);
  917.    ulIterations++;
  918.  
  919.    /* Move disks over */
  920.    Hanoi((BYTE)(bHeight - 1), (BYTE)bTemp, (BYTE)bTo, (BYTE)bFrom);
  921.    return;
  922. }      /*  Hanoi()  */
  923.  
  924. /****************************************************************\
  925.  *  SetupTowers procedure
  926.  *--------------------------------------------------------------
  927.  *
  928.  *  Name:    SetupTowers(VOID)
  929.  *
  930.  *  Purpose: This routine initializes the global arrays that represent
  931.  *           the Hanoi stacks.  This involves placing all the disks on
  932.  *           the first peg, emptying the other 2 pegs and setting the
  933.  *           associated counts.
  934.  *
  935.  *  Usage:   Routine is called when program started or restarted.
  936.  *
  937.  *  Method:  Calling uses the global variable cTowerSize to determine
  938.  *           how many disks there are.
  939.  *
  940.  *  Returns: VOID
  941.  *
  942. \****************************************************************/
  943. VOID SetupTowers(VOID)
  944. {
  945.    int cnt;
  946.  
  947.    for(cnt = 0; cnt < cTowerSize; cnt++)   /* Set up initial post with disks */
  948.       abTowers[0][cnt] = (unsigned char)(cTowerSize - (BYTE)cnt - (BYTE)1);
  949.  
  950.    abTowersNdx[0] = (BYTE)cTowerSize;    /* Set disk count for initial post */
  951.    abTowersNdx[1] = abTowersNdx[2] = (BYTE)0;     /* Zero other post counts */
  952.    return;
  953. }   /*  SetupTowers()  */
  954.  
  955. /****************************************************************\
  956.  *  MoveDisk procedure
  957.  *--------------------------------------------------------------
  958.  *
  959.  *  Name:    MoveDisk(HPS hps, BYTE bFrom, BYTE bTo)
  960.  *
  961.  *  Purpose: This routine moves the top disk from the bFrom spindle
  962.  *           to the top of the bTo spindle.
  963.  *
  964.  *  Usage:
  965.  *
  966.  *  Method:  Does error checking for trying to move a disk from a
  967.  *           spindle that does not have any disks on it.
  968.  *
  969.  *  Returns: VOID
  970.  *
  971. \****************************************************************/
  972. VOID MoveDisk(HPS hps, BYTE bFrom, BYTE bTo)
  973.                         /* hps - Handle for painting */
  974.                         /* bFrom - The from spindle number, 0-2 */
  975.                         /* bTo - The to spindle number, 0-2 */
  976. {
  977.    BYTE bDiskNum;                       /* Disk number to move */
  978.    CHAR bTOSndx = (unsigned char)(abTowersNdx[bFrom] - 1);    /* Top of stack index */
  979.  
  980.    DrawDisk(hps, bFrom, bTOSndx, fERASE);  /* Remove disk off from stack */
  981.  
  982.    bDiskNum = abTowers[bFrom][bTOSndx]; /* Get move disk number */
  983.    abTowersNdx[bFrom]--;                /* Decrease count on from spindle */
  984.  
  985.    /* Offset to place the disk at Place on stack in memory */
  986.    bTOSndx = abTowersNdx[bTo]++;
  987.    abTowers[bTo][bTOSndx] = bDiskNum;
  988.                                         /* Draw disk on the to stack */
  989.    DrawDisk(hps,bTo,bTOSndx,fDRAW);
  990.    return;
  991. }    /*  MoveDisk()  */
  992.  
  993. /****************************************************************\
  994.  *  EntryFldDlgProc procedure
  995.  *--------------------------------------------------------------
  996.  *
  997.  *  Name:   EntryFldDlgProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  998.  *
  999.  *
  1000.  * Purpose: Handles all the messages associated with the set entry field
  1001.  *          and calls the appropriate handling procedures.  The purpose
  1002.  *          of this dialog box is to get a new number of disks for the
  1003.  *          Hanoi routine.
  1004.  *
  1005.  * Usage:
  1006.  *
  1007.  * Method:  If the value entered is valid, global cTowerSize is changed
  1008.  *          to the new value, and TRUE is returned.
  1009.  *
  1010.  * Returns: Terminates with a TRUE if a new valid Tower Size has been entered.
  1011.  *
  1012. \****************************************************************/
  1013. MRESULT EXPENTRY EntryFldDlgProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  1014. {
  1015.    short NewSize = (short)0;                  /* Holds new number of disks */
  1016.  
  1017.    switch(msg)
  1018.    {
  1019.       case WM_INITDLG:
  1020.          FixSysMenu(hwnd);
  1021.  
  1022.          WinSendDlgItemMsg(hwnd, IDD_ENTRYFLD, EM_SETTEXTLIMIT,
  1023.                            MPFROM2SHORT(2,0), MPVOID);  /* Limit length */
  1024.          WinSetDlgItemShort(hwnd, IDD_ENTRYFLD, cTowerSize, TRUE);
  1025.          return (MRESULT)NULL;           /* Allow normal focus setting */
  1026.  
  1027.       case WM_COMMAND:
  1028.          switch(SHORT1FROMMP(mp1))
  1029.          {
  1030.             case DID_OK:
  1031.                WinQueryDlgItemShort(hwnd, IDD_ENTRYFLD, &NewSize, TRUE);
  1032.                if(NewSize > (short)0 && NewSize <= MAXDISKCNT) /* Set new Tower size */
  1033.                {
  1034.                   cTowerSize = (BYTE)NewSize;
  1035.                   WinDismissDlg(hwnd, TRUE);
  1036.                }
  1037.                else                    /* Invalid value */
  1038.                   MessageBox(hwndMainFrame,
  1039.                              IDMSG_DISKNUMBERERROR,
  1040.                              MB_OK | MB_ICONEXCLAMATION,
  1041.                              TRUE);
  1042.                return (MRESULT)NULL;
  1043.  
  1044.             case DID_CANCEL:
  1045.                WinDismissDlg(hwnd, FALSE);
  1046.                return (MRESULT)NULL;
  1047.  
  1048.             default:
  1049.                return WinDefDlgProc(hwnd, msg, mp1, mp2);
  1050.          }
  1051.  
  1052.       default:
  1053.          return WinDefDlgProc(hwnd, msg, mp1, mp2);
  1054.    }
  1055. }    /*  EntryFldDlgProc()  */
  1056.  
  1057. /****************************************************************\
  1058.  *  Dialog procedure for the About dialog box
  1059.  *--------------------------------------------------------------
  1060.  *
  1061.  *  Name:    AboutDlgProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  1062.  *
  1063.  *  Purpose: Handles all the messages associated with the About Box
  1064.  *
  1065.  *  Usage:   Called for each message sent to the About Box dialog box.
  1066.  *
  1067.  *  Method:  The about box only has a button control so this routine
  1068.  *           only processes WM_COMMAND messages. Any WM_COMMAND
  1069.  *           posted must have come from the Ok button so we dismiss
  1070.  *           the dialog upon receiving it.
  1071.  *
  1072.  *  Returns: Dependent upon message sent
  1073.  *
  1074. \****************************************************************/
  1075. MRESULT EXPENTRY AboutDlgProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  1076. {
  1077.    switch(msg)
  1078.    {
  1079.       case WM_INITDLG:
  1080.          FixSysMenu(hwnd);
  1081.          return (MRESULT)NULL;
  1082.  
  1083.       case WM_COMMAND:
  1084.          WinDismissDlg(hwnd, TRUE);
  1085.          return (MRESULT)NULL;
  1086.  
  1087.       default:
  1088.          return WinDefDlgProc(hwnd, msg, mp1, mp2);
  1089.    }
  1090. }        /*  AboutDlgProc()  */
  1091.  
  1092. /****************************************************************\
  1093.  *  FixSysMenu procedure
  1094.  *--------------------------------------------------------------
  1095.  *
  1096.  *  Name:    FixSysMenu(HWND hwndDlg)
  1097.  *
  1098.  *  Purpose: This routine removes the Restore, Size, Minimize, and
  1099.  *           Maximize options from the system menu of a dialog.
  1100.  *
  1101.  *  Usage:
  1102.  *
  1103.  *  Method:  Called during the WM_INITDLG of a dialog procedure.
  1104.  *
  1105.  *  Returns: VOID
  1106.  *
  1107. \****************************************************************/
  1108. VOID FixSysMenu(HWND hwndDlg)
  1109. {
  1110.    HWND hwndMenu = WinWindowFromID(hwndDlg, FID_SYSMENU);
  1111.  
  1112.    WinSendMsg(hwndMenu,  /* Delete Restore from the system menu */
  1113.               MM_DELETEITEM,
  1114.               MPFROM2SHORT(SC_RESTORE, TRUE),
  1115.               MPVOID);
  1116.  
  1117.    WinSendMsg(hwndMenu,     /* Delete Size from the system menu */
  1118.               MM_DELETEITEM,
  1119.               MPFROM2SHORT(SC_SIZE, TRUE),
  1120.               MPVOID);
  1121.  
  1122.    WinSendMsg(hwndMenu, /* Delete Minimize from the system menu */
  1123.               MM_DELETEITEM,
  1124.               MPFROM2SHORT(SC_MINIMIZE, TRUE),
  1125.               MPVOID);
  1126.  
  1127.    WinSendMsg(hwndMenu, /* Delete Maximize from the system menu */
  1128.               MM_DELETEITEM,
  1129.               MPFROM2SHORT(SC_MAXIMIZE, TRUE),
  1130.               MPVOID);
  1131.    return;
  1132. }             /*  FixSysMenu()  */
  1133.  
  1134. /****************************************************************\
  1135.  *  Message Box procedure
  1136.  *--------------------------------------------------------------
  1137.  *
  1138.  *  Name:    MessageBox(HWND hwndOwner, LONG IdMsg, LONG fsStyle, BOOL fBeep)
  1139.  *
  1140.  *  Purpose: Displays the message box with the message given in idMsg
  1141.  *           retrieved from the message table and using the style
  1142.  *           flags in fsStyle
  1143.  *
  1144.  *  Usage:   Called whenever a MessageBox is to be displayed
  1145.  *
  1146.  *  Method:  - Message string is loaded from the process' message table
  1147.  *           - Alarm beep is sounded if desired
  1148.  *           - Message box with the message is displayed
  1149.  *           - WinMessageBox return value is returned
  1150.  *
  1151.  *  Returns: Return value from WinMessageBox()
  1152.  *
  1153. \****************************************************************/
  1154. ULONG MessageBox(HWND hwndOwner, LONG IdMsg, LONG fsStyle, BOOL fBeep)
  1155.            /* hwndOwner - Handle of the message box's owner */
  1156.            /* idMsg - ID if the message in the message table */
  1157.            /* fsStyle - Style of the message box */
  1158.            /* fBeep - If TRUE, beep before message box is displayed */
  1159. {
  1160.    CHAR szText[MESSAGELEN];
  1161.  
  1162.    if(!WinLoadMessage(hab,
  1163.                       NULLHANDLE,
  1164.                       (unsigned long)IdMsg,
  1165.                       MESSAGELEN,
  1166.                       (PSZ)szText))
  1167.    {
  1168.       WinAlarm(HWND_DESKTOP, WA_ERROR);
  1169.       return RETURN_ERROR;
  1170.    }
  1171.  
  1172.    if(fBeep)
  1173.       WinAlarm(HWND_DESKTOP, WA_ERROR);
  1174.  
  1175.    return WinMessageBox(HWND_DESKTOP,
  1176.                         hwndOwner,
  1177.                         szText,
  1178.                         (PSZ)NULL,
  1179.                         IDD_MSGBOX,
  1180.                         (unsigned long)fsStyle);
  1181. }   /* MessageBox() */
  1182.  
  1183. /****************************************************************\
  1184.  *  Routine for initializing the help manager
  1185.  *--------------------------------------------------------------
  1186.  *
  1187.  *  Name:    InitHelp(VOID)
  1188.  *
  1189.  *  Purpose: Initializes the IPF help facility
  1190.  *
  1191.  *  Usage:   Called once during initialization of the program
  1192.  *
  1193.  *  Method:  Initializes the HELPINIT structure and creates the help
  1194.  *           instance. If successful, the help instance is associated
  1195.  *           with the main window
  1196.  *
  1197.  *  Returns: VOID
  1198.  *
  1199. \****************************************************************/
  1200. VOID InitHelp(VOID)
  1201. {
  1202.    HELPINIT hini;
  1203.  
  1204.    /* If we return because of an error, Help will be disabled */
  1205.    fHelpEnabled = FALSE;
  1206.                              /* Initialize help init structure */
  1207.    hini.cb = sizeof(HELPINIT);
  1208.    hini.ulReturnCode = 0UL;
  1209.                              /* If tutorial added, add name here */
  1210.    hini.pszTutorialName = (PSZ)NULL;
  1211.  
  1212.    hini.phtHelpTable = (PHELPTABLE)MAKELONG(HANOI_HELP_TABLE, 0xFFFF);
  1213.    hini.hmodHelpTableModule = NULLHANDLE;
  1214.    hini.hmodAccelActionBarModule = NULLHANDLE;
  1215.    hini.idAccelTable = 0;
  1216.    hini.idActionBar  = 0;
  1217.  
  1218.    if(!WinLoadString(hab,
  1219.                      NULLHANDLE,
  1220.                      IDS_HELPWINDOWTITLE,
  1221.                      HELPLIBRARYNAMELEN,
  1222.                      (PSZ)szWindowTitle))
  1223.    {
  1224.       MessageBox(hwndMain, IDMSG_CANNOTLOADSTRING, MB_OK | MB_ERROR, FALSE);
  1225.       return;
  1226.    }
  1227.    hini.pszHelpWindowTitle = (PSZ)szWindowTitle;
  1228.  
  1229.                              /* If debugging, show panel ids, else don't */
  1230. #ifdef DEBUG
  1231.    hini.fShowPanelId = CMIC_SHOW_PANEL_ID;
  1232. #else
  1233.    hini.fShowPanelId = CMIC_HIDE_PANEL_ID;
  1234. #endif
  1235.  
  1236.    if(!WinLoadString(hab,
  1237.                      NULLHANDLE,
  1238.                      IDS_HELPLIBRARYNAME,
  1239.                      HELPLIBRARYNAMELEN,
  1240.                      (PSZ)szLibName))
  1241.    {
  1242.       MessageBox(hwndMain, IDMSG_CANNOTLOADSTRING, MB_OK | MB_ERROR, FALSE);
  1243.       return;
  1244.    }
  1245.    hini.pszHelpLibraryName = (PSZ)szLibName;
  1246.                    /* Creating help instance */
  1247.    hwndHelpInstance = WinCreateHelpInstance(hab, &hini);
  1248.  
  1249.    if(hwndHelpInstance == NULLHANDLE || hini.ulReturnCode)
  1250.    {
  1251.       MessageBox(hwndMainFrame, IDMSG_HELPLOADERROR, MB_OK | MB_ERROR, TRUE);
  1252.       return;
  1253.    }
  1254.                              /* Associate help instance with main frame */
  1255.    if(!WinAssociateHelpInstance(hwndHelpInstance, hwndMainFrame))
  1256.    {
  1257.       MessageBox(hwndMainFrame, IDMSG_HELPLOADERROR, MB_OK | MB_ERROR, TRUE);
  1258.       return;
  1259.    }
  1260.  
  1261.    /* Help manager is successfully initialized so set flag to TRUE */
  1262.    fHelpEnabled = TRUE;
  1263.    return;
  1264. }   /* InitHelp() */
  1265.  
  1266. /****************************************************************\
  1267.  *  Processes the Help for Help command from the menu bar
  1268.  *--------------------------------------------------------------
  1269.  *
  1270.  *  Name:    HelpHelpForHelp(MPARAM mp2)
  1271.  *
  1272.  *  Purpose: Processes the WM_COMMAND message posted by the Help for
  1273.  *           Help item of the Help menu
  1274.  *
  1275.  *  Usage:   Called from MainCommand when the Help for Help menu item
  1276.  *           is selected
  1277.  *
  1278.  *  Method:  Sends an HM_DISPLAY_HELP message to the help instance so
  1279.  *           that the default Help For Help is displayed.
  1280.  *
  1281.  *  Returns: VOID
  1282.  *
  1283. \****************************************************************/
  1284. VOID HelpHelpForHelp(MPARAM mp2)
  1285. {
  1286.            /* This just displays the system help for help panel */
  1287.    if(fHelpEnabled)
  1288.       if((LONG)WinSendMsg(hwndHelpInstance, HM_DISPLAY_HELP, MPVOID, MPVOID))
  1289.          MessageBox(hwndMain, IDMSG_HELPDISPLAYERROR, MB_OK | MB_ERROR, FALSE);
  1290.  
  1291.    /* This routine currently doesn't use the mp2 parameter but
  1292.     *  it is referenced here to prevent an 'Unreferenced Parameter'
  1293.     *  warning at compile time.
  1294.     */
  1295.    mp2;
  1296.    return;
  1297. }   /* HelpHelpForHelp() */
  1298.  
  1299. /****************************************************************\
  1300.  *  Processes the Extended Help command from the menu bar
  1301.  *--------------------------------------------------------------
  1302.  *
  1303.  *  Name:    HelpExtended(MPARAM mp2)
  1304.  *
  1305.  *  Purpose: Processes the WM_COMMAND message posted by the Extended
  1306.  *           Help item of the Help menu.
  1307.  *
  1308.  *  Usage:   Called from MainCommand when the Extended Help menu item
  1309.  *           is selected.
  1310.  *
  1311.  *  Method:  Sends an HM_EXT_HELP message to the help instance so that
  1312.  *           the default Extended Help is displayed.
  1313.  *
  1314.  *  Returns: VOID
  1315.  *
  1316. \****************************************************************/
  1317. VOID HelpExtended(MPARAM mp2)
  1318. {
  1319.            /* This just displays the system extended help panel */
  1320.    if(fHelpEnabled)
  1321.       if((LONG)WinSendMsg(hwndHelpInstance, HM_EXT_HELP, MPVOID, MPVOID))
  1322.          MessageBox(hwndMain, IDMSG_HELPDISPLAYERROR, MB_OK | MB_ERROR, FALSE);
  1323.  
  1324.     /* This routine currently doesn't use the mp2 parameter but
  1325.      *  it is referenced here to prevent an 'Unreferenced Parameter'
  1326.      *  warning at compile time.
  1327.      */
  1328.    mp2;
  1329.    return;
  1330. }   /* HelpExtended() */
  1331.  
  1332. /****************************************************************\
  1333.  *  Processes the Keys Help command from the menu bar
  1334.  *--------------------------------------------------------------
  1335.  *
  1336.  *  Name:    HelpKeys(MPARAM mp2)
  1337.  *
  1338.  *  Purpose: Processes the WM_COMMAND message posted by the Keys Help
  1339.  *           item of the Help menu.
  1340.  *
  1341.  *  Usage:   Called from MainCommand when the Keys Help menu item is
  1342.  *           selected
  1343.  *
  1344.  *  Method:  Sends an HM_KEYS_HELP message to the help instance so that
  1345.  *           the default Keys Help is displayed.
  1346.  *
  1347.  *  Returns: VOID
  1348.  *
  1349. \****************************************************************/
  1350. VOID HelpKeys(MPARAM mp2)
  1351. {
  1352.                /* This just displays the system keys help panel */
  1353.    if(fHelpEnabled)
  1354.       if((LONG)WinSendMsg(hwndHelpInstance, HM_KEYS_HELP, MPVOID, MPVOID))
  1355.          MessageBox(hwndMain, IDMSG_HELPDISPLAYERROR, MB_OK | MB_ERROR, FALSE);
  1356.  
  1357.     /* This routine currently doesn't use the mp2 parameter but
  1358.      *  it is referenced here to prevent an 'Unreferenced Parameter'
  1359.      *  warning at compile time.
  1360.      */
  1361.    mp2;
  1362.    return;
  1363. }   /* HelpKeys() */
  1364.  
  1365. /****************************************************************\
  1366.  *  Processes the Index Help command from the menu bar
  1367.  *--------------------------------------------------------------
  1368.  *
  1369.  *  Name:    HelpIndex(MPARAM mp2)
  1370.  *
  1371.  *  Purpose: Processes the WM_COMMAND message posted by the Index Help
  1372.  *           item of the Help menu
  1373.  *
  1374.  *  Usage:   Called from MainCommand when the Index Help menu item
  1375.  *           is selected
  1376.  *
  1377.  *  Method:  Sends an HM_INDEX_HELP message to the help instance so
  1378.  *           that the default Index Help is displayed.
  1379.  *
  1380.  *
  1381.  *  Returns: VOID
  1382.  *
  1383. \****************************************************************/
  1384. VOID HelpIndex(MPARAM mp2)
  1385. {
  1386.               /* This just displays the system help index panel */
  1387.    if(fHelpEnabled)
  1388.       if((LONG)WinSendMsg(hwndHelpInstance, HM_HELP_INDEX, MPVOID, MPVOID))
  1389.          MessageBox(hwndMain, IDMSG_HELPDISPLAYERROR, MB_OK | MB_ERROR, FALSE);
  1390.  
  1391.     /* This routine currently doesn't use the mp2 parameter but
  1392.      *  it is referenced here to prevent an 'Unreferenced Parameter'
  1393.      *  warning at compile time.
  1394.      */
  1395.    mp2;
  1396.    return;
  1397. }   /* HelpIndex() */
  1398.  
  1399. /****************************************************************\
  1400.  *  Processes the About command from the Help menu
  1401.  *--------------------------------------------------------------
  1402.  *
  1403.  *  Name:    HelpAbout(MPARAM mp2)
  1404.  *
  1405.  *  Purpose: Processes the WM_COMMAND message posted by the About item
  1406.  *           of the Help menu
  1407.  *
  1408.  *  Usage:   Called from MainCommand when the About menu item is selected
  1409.  *
  1410.  *  Method:  Calls WinDlgBox to display the about box dialog.
  1411.  *
  1412.  *  Returns: VOID
  1413.  *
  1414. \****************************************************************/
  1415. VOID HelpAbout(MPARAM mp2)
  1416. {
  1417.                                  /* Display the AboutBox dialog */
  1418.    WinDlgBox(HWND_DESKTOP,
  1419.              hwndMain,
  1420.              (PFNWP)AboutDlgProc,
  1421.              NULLHANDLE,
  1422.              IDD_PRODUCTINFO,
  1423.              NULL);
  1424.  
  1425.     /* This routine currently doesn't use the mp2 parameter but
  1426.      *  it is referenced here to prevent an 'Unreferenced Parameter
  1427.      *  warning at compile time.
  1428.      */
  1429.    return;
  1430. }   /* HelpAbout() */
  1431.  
  1432.  
  1433. /****************************************************************\
  1434.  *  Displays the help panel indicated
  1435.  *--------------------------------------------------------------
  1436.  *
  1437.  *  Name:    DisplayHelpPanel(LONG idPanel)
  1438.  *
  1439.  *  Purpose: Displays the help panel whose id is given
  1440.  *
  1441.  *  Usage:   Called whenever a help panel is desired to be displayed,
  1442.  *           usually from the WM_HELP processing of the dialog boxes
  1443.  *
  1444.  *  Method:  Sends HM_DISPLAY_HELP message to the help instance
  1445.  *
  1446.  *  Returns: VOID
  1447.  *
  1448. \****************************************************************/
  1449. VOID DisplayHelpPanel(LONG idPanel)
  1450.                         /* ID of the help panel to be displayed */
  1451. {
  1452.    if(fHelpEnabled)
  1453.       if(WinSendMsg(hwndHelpInstance,
  1454.                     HM_DISPLAY_HELP,
  1455.                     MPFROMLONG(MAKELONG(idPanel, NULL)),
  1456.                     MPFROMSHORT(HM_RESOURCEID)))
  1457.          MessageBox(hwndMainFrame,
  1458.                     IDMSG_HELPDISPLAYERROR,
  1459.                     MB_OK | MB_ERROR, TRUE);
  1460.     return;
  1461. }   /* DisplayHelpPanel() */
  1462.  
  1463.  
  1464. /****************************************************************\
  1465.  *  Destroys the help instance
  1466.  *--------------------------------------------------------------
  1467.  *
  1468.  *  Name:    DestroyHelpInstance(VOID)
  1469.  *
  1470.  *  Purpose: Destroys the help instance for the application
  1471.  *
  1472.  *  Usage:   Called after exit from message loop
  1473.  *
  1474.  *  Method:  Calls WinDestroyHelpInstance() to destroy the help instance
  1475.  *
  1476.  *  Returns: VOID
  1477.  *
  1478. \****************************************************************/
  1479. VOID DestroyHelpInstance(VOID)
  1480. {
  1481.     if(hwndHelpInstance != NULLHANDLE)
  1482.     {
  1483.        WinDestroyHelpInstance(hwndHelpInstance);
  1484.     }
  1485.     return;
  1486.  
  1487. }   /* DestroyHelpInstance() */
  1488.  
  1489.  
  1490. /****************************************************************\
  1491.  *  Posts a message to the Background thread
  1492.  *--------------------------------------------------------------
  1493.  *
  1494.  *  Name:    PostBkThreadMsg(ULONG msg, MPARAM mp1, MPARAM mp2)
  1495.  *
  1496.  *  Purpose: Routine posts a message to the object window of the
  1497.  *           background thread.
  1498.  *
  1499.  *  Usage:   Routine is called whenever a message is to be posted to
  1500.  *           the background processing thread.
  1501.  *
  1502.  *  Method:  The routine posts the message to the object window of the
  1503.  *           thread.
  1504.  *
  1505.  *  Returns:  the return value from WinPostMsg().
  1506.  *
  1507. \****************************************************************/
  1508. BOOL PostBkThreadMsg(ULONG msg, MPARAM mp1, MPARAM mp2)
  1509. {
  1510.    BOOL sRC = WinPostMsg(hwndObject, msg, mp1, mp2);
  1511.  
  1512.    return sRC;
  1513. }   /* PostBkThreadMsg() */
  1514.  
  1515.  
  1516. /****************************************************************\
  1517.  *  Background thread routine
  1518.  *--------------------------------------------------------------
  1519.  *
  1520.  *  Name:    BackgroundThread(ULONG ulThreadParam)
  1521.  *
  1522.  *  Purpose: Routine is a background thread used for tasks to be
  1523.  *           completed in the background.
  1524.  *
  1525.  *  Usage:   Routine is called at initialization time to create a
  1526.  *           background processing thread.
  1527.  *
  1528.  *  Method:  The routine initializes itself as a PM thread and creates
  1529.  *           a message queue.  It then creates an object window through
  1530.  *           which it will receive and send messages.  It then polls
  1531.  *           through a message loop, processing any messages it receives
  1532.  *           in the object window's window procedure. When the loop
  1533.  *           ends, it terminates.
  1534.  *
  1535.  *  Returns: VOID
  1536.  *
  1537. \****************************************************************/
  1538. VOID BackgroundThread(ULONG ulThreadParam)
  1539.                      /* Parameter passed into DosCreateThread() */
  1540. {
  1541.    QMSG qmsg;
  1542.                             /* Create message queue for thread */
  1543.    habBkThread = WinInitialize(0UL);
  1544.  
  1545.    do
  1546.    {
  1547.       if(habBkThread == NULLHANDLE)
  1548.       {
  1549.          WinPostMsg(hwndMain, TM_THREADINITFAILED, MPVOID, MPVOID);
  1550.          DosExit(EXIT_THREAD, 0UL);
  1551.       }
  1552.       hmqBkThread = WinCreateMsgQueue(habBkThread, 0L);
  1553.       if(hmqBkThread == NULLHANDLE)
  1554.       {
  1555.          WinPostMsg(hwndMain, TM_THREADINITFAILED, MPVOID, MPVOID);
  1556.          WinTerminate(habBkThread);
  1557.          DosExit(EXIT_THREAD, 0UL);
  1558.       }
  1559.  
  1560.         /*
  1561.          *  Load the string for the object window class and register the class.
  1562.          */
  1563.       if(!WinLoadString(habBkThread,
  1564.                         NULLHANDLE,
  1565.                         IDS_OBJECTCLASS,
  1566.                         MAXNAMEL,
  1567.                         (PSZ)szObjectClass))
  1568.       {
  1569.          WinPostMsg(hwndMain, TM_THREADINITFAILED, MPVOID, MPVOID);
  1570.          break;
  1571.       }
  1572.         /* Register the main client window class */
  1573.       if(!WinRegisterClass(habBkThread,
  1574.                           (PSZ)szObjectClass,
  1575.                           (PFNWP)ObjectWndProc,
  1576.                           CS_SIZEREDRAW | CS_CLIPCHILDREN,
  1577.                           0UL))
  1578.       {
  1579.          WinPostMsg(hwndMain, TM_THREADINITFAILED, MPVOID, MPVOID);
  1580.          break;
  1581.       }
  1582.  
  1583.         /* Create the object window */
  1584.       hwndObject = WinCreateWindow(HWND_OBJECT,
  1585.                                      szObjectClass,
  1586.                                      NULL,
  1587.                                      0UL,
  1588.                                      0L,
  1589.                                      0L,
  1590.                                      0L,
  1591.                                      0L,
  1592.                                      NULLHANDLE,
  1593.                                      HWND_TOP,
  1594.                                      OBJECTID,
  1595.                                      NULL,
  1596.                                      NULL);
  1597.       if(hwndObject == NULLHANDLE)
  1598.       {
  1599.          WinPostMsg(hwndMain, TM_THREADINITFAILED, MPVOID, MPVOID);
  1600.          break;
  1601.       }
  1602.  
  1603.         /*
  1604.          *  Set thread created flag so another thread of this type
  1605.          *  cannot be created.
  1606.          */
  1607.       fThreadCreated = TRUE;
  1608.  
  1609.                    /* Clear initialization semaphore.   */
  1610.       DosPostEventSem(hevThreadInit);
  1611.  
  1612.                                                     /* Message loop */
  1613.       while(WinGetMsg(hmqBkThread, &qmsg, NULLHANDLE, 0UL, 0UL))
  1614.          WinDispatchMsg(hmqBkThread, &qmsg);
  1615.  
  1616.    }ONCE;
  1617.  
  1618.    /* Destroy object window, clean up message queue and terminate */
  1619.    if(WinIsWindow(habBkThread, hwndObject))
  1620.        WinDestroyWindow(hwndObject);
  1621.  
  1622.    WinDestroyMsgQueue(hmqBkThread);
  1623.    WinTerminate(habBkThread);
  1624.  
  1625.    /* If termination is due to an error initializing the thread, then clear
  1626.       the initialization semaphore so that the main thread can continue. */
  1627.  
  1628.    if(!fThreadCreated)
  1629.       DosPostEventSem(hevThreadInit);
  1630.    DosExit(EXIT_THREAD, 0UL);
  1631.                 /* The thread parameter is not currently used */
  1632.    return;
  1633. }   /* BackgroundThread() */
  1634.  
  1635.  
  1636. /****************************************************************\
  1637.  *  Dialog procedure for the Object window
  1638.  *--------------------------------------------------------------
  1639.  *
  1640.  *  Name:    ObjectWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  1641.  *
  1642.  *  Purpose: Processes all messages sent to the Object window
  1643.  *
  1644.  *  Usage:   Called for each message sent to the Object window.
  1645.  *
  1646.  *  Method:  The Object window processes the messages that tell the
  1647.  *           background thread what action to take. Since the object
  1648.  *           window is not visible, it will not process any of the
  1649.  *           standard window messages.
  1650.  *
  1651.  *  Returns: Dependent upon message sent
  1652.  *
  1653. \****************************************************************/
  1654. MRESULT EXPENTRY ObjectWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  1655. {
  1656.    MRESULT  sRC;
  1657.  
  1658.    switch(msg)
  1659.    {
  1660.       /*-------------------------------------------------------*\
  1661.        *  Include here any user defined messages to determine
  1662.        *  which action the background thread should take.
  1663.       \*-------------------------------------------------------*/
  1664.    default:
  1665.       sRC = WinDefWindowProc(hwnd, msg, mp1, mp2);
  1666.    }
  1667.    return sRC;
  1668. }   /* ObjectWndProc() */
  1669.