home *** CD-ROM | disk | FTP | other *** search
/ The Developer Connection…ice Driver Kit for OS/2 3 / DEV3-D1.ISO / devtools / os2tk21j / c / samples / hanoi / hanoi.c__ / hanoi.c
Encoding:
C/C++ Source or Header  |  1993-04-03  |  57.6 KB  |  1,696 lines

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