home *** CD-ROM | disk | FTP | other *** search
/ Inside Multimedia 1995 July / IMM0795.ISO / share / os2 / pmfract / src / smplhelp.c < prev    next >
C/C++ Source or Header  |  1994-01-24  |  29KB  |  836 lines

  1. /*--------------------------------------------------------
  2.    SMPLHELP.C -- a Simple Help Displayer system
  3.  
  4.    This DLL system is used to display a simple help window
  5.    to go along with a main application window of choice.
  6.  
  7.    Interface is through three functions, only one of which
  8.    is required to be called:
  9.  
  10.       SimpleHelpInit (HAB hab);    // optional one-time initialization
  11.       SimpleHelpExit (void);       // optional clean-up call
  12.       SimpleHelp (HAB hab, etc.);  // Required call to display
  13.                                    // a given text resource.
  14.  
  15.    Help text is stored as user resources in the callers EXE file.
  16.    The type and ID of these user resources is passed to SimpleHelp,
  17.    which causes the help window to be created if it does not
  18.    exist and the described text to be displayed.
  19.  
  20.    Completed 12/26/89     Code by Donald P. Egen
  21.                            (but much inspired from a variety of sources)
  22.  
  23.    Version 1.01  01/02/90  DPE
  24.                            Add a little paranoia code at initialization,
  25.                            and do a better job of handling the
  26.                WinRegisterWindowDestroy processing.
  27.  
  28.    Version 1.2     09/03/90  DPE
  29.                Do the reformatting as a subthread.
  30.                This improves responsiveness on very
  31.                large texts being displayed.
  32.  
  33.    Version 1.3     09/30/90  DPE
  34.                Support the OS/2 1.2 Scroll bar
  35.                thumb size (SBM_SETTHUMBSIZE).
  36.  
  37.          10/16/90  DPE
  38.                SHORTS are negative half the time!!!
  39.                (ARRRGGHH!!)
  40.  
  41.    Version 1.4     03/17/91  DPE
  42.                Fix running off the end of the input
  43.                segment if the last line ends with a CR/LF
  44.                but there is no EOF character.
  45.                (ARRRGHH!! again)
  46.  
  47.   --------------------------------------------------------*/
  48.  
  49. #define INCL_WIN
  50. #define INCL_GPI
  51. #define INCL_DOS
  52. #include <os2.h>
  53. #include <stdio.h>
  54. #include <stdlib.h>
  55. #include <string.h>
  56. #include "smplhelp.hh"
  57. #include "smplhelp.h"
  58.  
  59. #define SYSVAL(x)  ( (USHORT) WinQuerySysValue(HWND_DESKTOP, x) )
  60.  
  61. HMODULE hmodHelp = (HMODULE) 0;
  62. HMODULE hmodHelpLast = (HMODULE) 0, hmodHelpNew = (HMODULE) 0;
  63. USHORT    idTypeLast = 0, idNameLast = 0, idTypeNew = 0, idNameNew = 0;
  64. HWND   hwndScroll ;
  65. HWND   hwndHelpFrame = (HWND) 0, hwndHelp = (HWND) 0;
  66. HWND   hwndCaller = (HWND) 0;
  67. TID    tidReformat = 0;
  68. BOOL   flReformatAgain = FALSE;
  69. ULONG  semReformat = 0L;
  70. SHORT  sThreadStack[STACKSIZE];
  71. ULONG  semWindowPS = 0L;
  72. volatile BOOL flReformatStop = FALSE;
  73. PCHAR  pResource, pFormat ;
  74. SEL    selResource, selFormat;
  75. SHORT  cxClient, cyClient, cxChar, cyChar, cyDesc, cyLines,
  76.        sScrollPos, sNumLines, sScrollMax ;
  77. ULONG  ulResourceSize ;
  78. BOOL   fInitOnce = FALSE, fInitWnd = FALSE;
  79. CHAR  szClientClass [10] ;
  80. CHAR  szTitleBarHelp [20] ;
  81. CHAR   szWorking[15];
  82. CHAR   szBadAttach[35];
  83. ULONG flFrameFlags = FCF_TITLEBAR      | FCF_SYSMENU  |
  84.                      FCF_SIZEBORDER    | FCF_MINMAX   |
  85.              FCF_SHELLPOSITION | FCF_NOMOVEWITHOWNER |
  86.                      FCF_VERTSCROLL    | FCF_ICON |
  87.                      FCF_MENU          | FCF_ACCELTABLE ;
  88.  
  89. MRESULT EXPENTRY HelpWndProc (HWND, USHORT, MPARAM, MPARAM) ;
  90. MRESULT CALLBACK HelpAboutProc(HWND hDlg, USHORT msg, MPARAM mp1, MPARAM mp2);
  91. static    void     HelpReformat(void);
  92.  
  93. static void OneTimeInit (HAB hab)
  94.      {
  95.  
  96.      if (!fInitOnce)
  97.         {  /* one per process init - get strings, register class */
  98.            WinLoadString (hab, hmodHelp, IDS_CLASS,
  99.                           sizeof szClientClass, szClientClass);
  100.            WinLoadString (hab, hmodHelp, IDS_TITLE,
  101.               sizeof szTitleBarHelp, szTitleBarHelp) ;
  102.        WinLoadString (hab, hmodHelp, IDS_WORKING,
  103.               sizeof szWorking, szWorking);
  104.        WinLoadString (hab, hmodHelp, IDS_BADATTACH,
  105.               sizeof szBadAttach, szBadAttach);
  106.  
  107.            WinRegisterClass (hab, szClientClass, HelpWndProc, CS_SIZEREDRAW, 0) ;
  108.            fInitOnce = TRUE;
  109.         }
  110.  
  111.      }
  112.  
  113.  
  114. VOID CALLBACK SimpleHelpInit (HAB hab)
  115.      {  /* Externalize the One Time Init function */
  116.  
  117.      OneTimeInit (hab);
  118.  
  119.      }
  120.  
  121. VOID CALLBACK SimpleHelp (HAB hab, HWND hwndUser, PSZ achUser,
  122.               HMODULE hmodUser, USHORT idType, USHORT idName)
  123.      {
  124.      /*
  125.         This function is the primary interface to the Simple Help System.
  126.         It is assumed to be called from a user Client Window Proc.
  127.         We do what is necessary to get us up and the focus of attention,
  128.         and ready to display the requested help text.
  129.         On return from this function, the Simple Help window is an independent
  130.         top-level window in the same thread as the original caller.
  131.  
  132.         We can be recalled to change the help text even if we are already
  133.         open.  This allows a user routine to have possibly multiple submenu
  134.         entries in his "Help" menu, and the text displayed here can be
  135.         switched by the Help menu in the original window.
  136.      */
  137.  
  138.      CHAR szTitleText [40];
  139.      SWP  swpFrame;
  140.      USHORT cxScreen, cyScreen, cyIcon, cxMe, cyMe, xMe, yMe;
  141.  
  142.      /* do the One Time Initialization if it has not been done already */
  143.      OneTimeInit(hab);
  144.  
  145.      if (!fInitWnd)
  146.         {  /* if we don't have a window at the moment, create an empty one */
  147.        hwndHelpFrame = WinCreateStdWindow (HWND_DESKTOP, 0,
  148.                                            &flFrameFlags, szClientClass, NULL,
  149.                        0L, hmodHelp, ID_RESOURCE, &hwndHelp) ;
  150.  
  151.        cxScreen = SYSVAL(SV_CXSCREEN);
  152.        cyScreen = SYSVAL(SV_CYSCREEN);
  153.        cyIcon = SYSVAL(SV_CYICON);
  154.  
  155.        cxMe = cxScreen / 3;
  156.        cyMe = cyScreen - 2 * cyIcon;
  157.        xMe = cxScreen - cxMe;
  158.        yMe = 2 * cyIcon;
  159.        WinSetWindowPos (hwndHelpFrame, HWND_TOP,
  160.         xMe, yMe, cxMe, cyMe,
  161.         SWP_ACTIVATE | SWP_ZORDER | SWP_MOVE | SWP_SIZE | SWP_SHOW);
  162.        /* now put the window up */
  163.        WinShowWindow(hwndHelpFrame, TRUE);
  164.            fInitWnd = TRUE;
  165.         }
  166.  
  167.      /* Register so that that we get called when our caller goes away */
  168.      if (hwndCaller != hwndUser)
  169.         {  /* our caller changed.  Unregister the previous caller */
  170.        if (hwndCaller != (HWND) 0)
  171.                 WinRegisterWindowDestroy (hwndCaller, FALSE);
  172.            /* now register the new guy */
  173.            hwndCaller = hwndUser;
  174.            WinRegisterWindowDestroy (hwndCaller, TRUE);
  175.         }
  176.  
  177.      /* If we are minimized, restore and activate us */
  178.      WinQueryWindowPos (hwndHelpFrame, &swpFrame);
  179.      if ( (swpFrame.fs & SWP_MINIMIZE) && (!(swpFrame.fs & SWP_MAXIMIZE)) )
  180.         {
  181.         WinSetWindowPos (hwndHelpFrame, HWND_TOP,
  182.                 0, 0, 0, 0,
  183.                 SWP_RESTORE | SWP_ACTIVATE | SWP_ZORDER);
  184.         }
  185.      else    /* just activate and bring us to the top */
  186.         {
  187.         WinSetWindowPos (hwndHelpFrame, HWND_TOP,
  188.                    0, 0, 0, 0,
  189.                    SWP_ACTIVATE | SWP_ZORDER);
  190.         }
  191.  
  192.      /* set up the title bar by concatenating the resource string with the
  193.         user's argument string.  The idea is to create "Help for <ZXCVBNM>" */
  194.      strcpy(szTitleText, szTitleBarHelp);
  195.      strcat(szTitleText, achUser);
  196.      WinSetWindowText (hwndHelpFrame, szTitleText);
  197.  
  198.      /* now display the text resource requested */
  199.      WinSendMsg (hwndHelp, WM_NEW_TEXT, MPFROM2SHORT(hmodUser, 0),
  200.                  MPFROM2SHORT(idType, idName) );
  201.  
  202.      }
  203.  
  204. VOID CALLBACK SimpleHelpExit (void)
  205.      {  /* if we are still active, close us down */
  206.      if (fInitWnd)
  207.         WinSendMsg (hwndHelp, WM_CLOSE, 0L, 0L);
  208.      }
  209.  
  210. static void ReFormat (HWND hwnd)
  211.      {
  212.  
  213.      USHORT rc;
  214.      CHAR szErrorMessage[35];
  215.  
  216.      if (tidReformat)
  217.     {   /* already active, queue for another try */
  218.     flReformatStop = TRUE;
  219.     flReformatAgain = TRUE;
  220.     return;
  221.     }
  222.  
  223.      /* fire up the sub thread */
  224.      DosSemSet((HSEM) &semReformat);
  225.      flReformatAgain = FALSE;
  226.      flReformatStop = FALSE;
  227.      WinEnableWindow (hwndScroll, FALSE);
  228.      rc = DosCreateThread (HelpReformat, &tidReformat,
  229.                  (PBYTE) &sThreadStack[STACKSIZE]);
  230.      if (rc == 0)
  231.     /* chap down the  priority */
  232.     rc = DosSetPrty(PRTYS_THREAD, PRTYC_NOCHANGE, -5, tidReformat);
  233.      else
  234.     { /* clean up */
  235.     DosSemClear((HSEM) &semReformat);
  236.     tidReformat = 0;
  237.     }
  238.      if (rc != 0)
  239.     {   /* report the bad news */
  240.     sprintf(szErrorMessage, szBadAttach, rc);
  241.     WinMessageBox (HWND_DESKTOP, hwnd,
  242.          (PSZ) szErrorMessage, (PSZ) szClientClass,
  243.          ID_EMESSAGEBOX,
  244.          MB_OK | MB_ICONASTERISK | MB_APPLMODAL);
  245.     }
  246.  
  247.      }
  248.  
  249. MRESULT EXPENTRY HelpWndProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
  250.      {
  251.      HPS           hps ;
  252.      POINTL        ptl;
  253.      SHORT       sLineLength, sLine, sScrollInc, sPaintBeg, sPaintEnd;
  254.      RECTL         rclInvalid ;
  255.      PCHAR         outp;
  256.      SHORT       cxNew, cyNew;
  257.  
  258.      switch (msg)
  259.           {
  260.           case WM_CREATE:
  261.  
  262.                     /*-----------------------------------------
  263.                        Prime the pump by allocating an initial dummy
  264.                        resource segment
  265.                       -----------------------------------------*/
  266.  
  267.                DosAllocSeg (25, &selResource, SEG_NONSHARED);
  268.                ulResourceSize = 20L;
  269.                pResource = MAKEP (selResource, 0) ;
  270.            *pResource = AEOF;    /* set EOF at front of segment */
  271.  
  272.                     /*-----------------------------------------------
  273.                       Allocate a segment for formatting the text into.
  274.                       Note: Zero length means Max length (65536 bytyes)
  275.                       -----------------------------------------------*/
  276.  
  277.                DosAllocSeg(0, &selFormat, SEG_NONSHARED) ;
  278.                pFormat = MAKEP (selFormat, 0) ;
  279.  
  280.                     /*----------------------
  281.                        Query character size
  282.                       ----------------------*/
  283.  
  284.                {
  285.                    FONTMETRICS   fm ;
  286.  
  287.                    hps = WinGetPS (hwnd) ;
  288.  
  289.                    GpiQueryFontMetrics (hps, (LONG) sizeof fm, &fm) ;
  290.                    cxChar = (SHORT) fm.lAveCharWidth ;
  291.                    cyChar = (SHORT) fm.lMaxBaselineExt ;
  292.                    cyDesc = (SHORT) fm.lMaxDescender ;
  293.  
  294.                    WinReleasePS (hps) ;
  295.                }
  296.                     /*------------------------------------------
  297.                        Get scroll bar handle while the getting is good
  298.                       ------------------------------------------*/
  299.  
  300.                hwndScroll = WinWindowFromID (
  301.                                    WinQueryWindow (hwnd, QW_PARENT, FALSE),
  302.                                    FID_VERTSCROLL) ;
  303.  
  304.                     /*------------------------------------------
  305.                        Get the client window size from the CREATESTRUCT
  306.                       ------------------------------------------*/
  307.  
  308.                {
  309.                    PCREATESTRUCT pcrst = (PCREATESTRUCT) PVOIDFROMMP (mp2);
  310.  
  311.                    cxClient = pcrst->cx;
  312.            cyClient = pcrst->cy;
  313.            cyLines  = cyClient / cyChar;
  314.                }
  315.  
  316.                return 0 ;
  317.  
  318.           case WM_NEW_TEXT:
  319.                /* switch to a new incoming text resource */
  320.            /* free the old resource segment */
  321.  
  322.            /* we must wait for the subthread if it is running */
  323.            if (tidReformat)
  324.           {
  325.           flReformatStop = TRUE;
  326.           DosSemWait ((HSEM) &semReformat, SEM_INDEFINITE_WAIT);
  327.           }
  328.  
  329.                DosFreeSeg (selResource);
  330.                hmodHelpLast = hmodHelpNew;
  331.                idTypeLast = idTypeNew;
  332.                idNameLast = idNameNew;
  333.  
  334.                     /*-----------------------------------------
  335.                        Load the resource, get size and address
  336.                       -----------------------------------------*/
  337.  
  338.                DosGetResource ( hmodHelpNew = (HMODULE) SHORT1FROMMP(mp1),
  339.                                idTypeNew = SHORT1FROMMP(mp2),
  340.                                idNameNew = SHORT2FROMMP(mp2),
  341.                                &selResource) ;
  342.                DosSizeSeg (selResource, &ulResourceSize) ;
  343.                pResource = MAKEP (selResource, 0) ;
  344.  
  345.            /* Reformat to initial state. */
  346.            /* If we waited at the top of this message,
  347.           this will enter queued state, and we will restart
  348.           when we process the WM_SUB_DONE message
  349.           (which should be in the message queue by now). */
  350.  
  351.                ReFormat(hwnd);
  352.  
  353.                /* force an entire screen update */
  354.                WinInvalidateRect (hwnd, NULL, FALSE);
  355.  
  356.            return 0 ;
  357.  
  358.       case WM_SUB_DONE:
  359.            /* Reformatting is complete.
  360.           If there is another reformat queued, start it.
  361.           Else, clean up and schedule repaint */
  362.  
  363.            tidReformat = 0;
  364.            flReformatStop = FALSE;
  365.  
  366.            if (flReformatAgain)
  367.           {  /* fire up again */
  368.           flReformatAgain = FALSE;
  369.           ReFormat(hwnd);
  370.           return 0;
  371.           }
  372.  
  373.            /* fix up the scroll bar */
  374.            sScrollMax = max (0, sNumLines - cyLines) ;
  375.            sScrollPos = min (0, sScrollMax) ;
  376.  
  377.            WinSendMsg (hwndScroll, SBM_SETSCROLLBAR,
  378.                   MPFROM2SHORT (sScrollPos, 0),
  379.                   MPFROM2SHORT (0, sScrollMax)) ;
  380.  
  381.            WinSendMsg (hwndScroll, SBM_SETTHUMBSIZE,
  382.                   MPFROM2SHORT(cyLines, sNumLines), 0L);
  383.  
  384.            WinEnableWindow (hwndScroll, sScrollMax ? TRUE : FALSE) ;
  385.  
  386.            /* trigger screen update */
  387.  
  388.            WinInvalidateRect (hwnd, NULL, FALSE);
  389.  
  390.            return 0;
  391.  
  392.       case WM_SIZE:
  393.            /*
  394.           if we are getting minimized, then the new window
  395.           sizes will both be zeros.
  396.           If so, ignore the message.
  397.           If the "New" values are the same as the old values,
  398.           also ignore the message.
  399.            */
  400.            cxNew = SHORT1FROMMP (mp2) ;
  401.            cyNew = SHORT2FROMMP (mp2) ;
  402.  
  403.            if ( (cxNew == 0) && (cyNew == 0) )
  404.            /* being minimized */
  405.            return 0;
  406.  
  407.            if ( (cxNew == cxClient) && (cyNew == cyClient) )
  408.            return 0;
  409.  
  410.            /* nope, must handle the real change in window size */
  411.            cxClient = cxNew;
  412.            cyClient = cyNew;
  413.            cyLines    = cyClient / cyChar;
  414.  
  415.                /* reformat everything to the new size */
  416.            ReFormat(hwnd);
  417.  
  418.            WinInvalidateRect (hwnd, NULL, FALSE);
  419.  
  420.                return 0 ;
  421.  
  422.           case WM_CHAR:
  423.                 if (CHARMSG(&msg)->fs & KC_KEYUP)
  424.                    break;
  425.  
  426.                 if (CHARMSG(&msg)->fs & KC_VIRTUALKEY)
  427.                 {
  428.                     if (CHARMSG(&msg)->fs & KC_CTRL)
  429.                     {
  430.                         switch(CHARMSG(&msg)->vkey)
  431.                         {    /* Note: Cntl- optional for Top/Bottom movement */
  432.                              case VK_HOME:
  433.                                  return WinSendMsg (hwnd, WM_VSCROLL,
  434.                                         MPFROMSHORT(1), MPFROM2SHORT(0, SB_SLIDERPOSITION));
  435.                              case VK_END:
  436.                                  return WinSendMsg (hwnd, WM_VSCROLL,
  437.                                         MPFROMSHORT(1), MPFROM2SHORT(sNumLines - 1, SB_SLIDERPOSITION));
  438.                          }
  439.                     break;
  440.                     }
  441.  
  442.                     switch (CHARMSG(&msg)->vkey)
  443.                     {   /* Note: Home/End don't need Cntl- for vert movement */
  444.                         case VK_HOME:
  445.                             return WinSendMsg (hwnd, WM_VSCROLL,
  446.                                    MPFROMSHORT(1), MPFROM2SHORT(0, SB_SLIDERPOSITION));
  447.                         case VK_END:
  448.                             return WinSendMsg (hwnd, WM_VSCROLL,
  449.                                    MPFROMSHORT(1), MPFROM2SHORT(sNumLines - 1, SB_SLIDERPOSITION));
  450.                         case VK_UP:
  451.                         case VK_DOWN:
  452.                         case VK_PAGEUP:
  453.                         case VK_PAGEDOWN:
  454.                              return WinSendMsg (hwndScroll, msg, mp1, mp2) ;
  455.                     }
  456.                 }
  457.                 break ;
  458.  
  459.           case WM_VSCROLL:
  460.                switch (SHORT2FROMMP (mp2))
  461.                {
  462.                     case SB_LINEUP:
  463.                          sScrollInc = -1 ;
  464.                          break ;
  465.  
  466.                     case SB_LINEDOWN:
  467.                          sScrollInc = 1 ;
  468.                          break ;
  469.  
  470.                     case SB_PAGEUP:
  471.              sScrollInc = min (-1, -cyLines) ;
  472.                          break ;
  473.  
  474.                     case SB_PAGEDOWN:
  475.              sScrollInc = max (1, cyLines) ;
  476.                          break ;
  477.  
  478.                     case SB_SLIDERTRACK:
  479.                     case SB_SLIDERPOSITION:
  480.                          sScrollInc = SHORT1FROMMP (mp2) - sScrollPos;
  481.                          break ;
  482.  
  483.                     default:
  484.                          sScrollInc = 0 ;
  485.                          break ;
  486.                }
  487.  
  488.                sScrollInc = max (-sScrollPos,
  489.                              min (sScrollInc, sScrollMax - sScrollPos)) ;
  490.  
  491.                if (sScrollInc != 0) ;
  492.                {
  493.                     sScrollPos += sScrollInc ;
  494.                     WinScrollWindow (hwnd, 0, cyChar * sScrollInc,
  495.                    NULL, NULL, (HRGN) 0, NULL, SW_INVALIDATERGN) ;
  496.  
  497.                     if (SHORT2FROMMP(mp2) != SB_SLIDERTRACK)
  498.                         WinSendMsg (hwndScroll, SBM_SETPOS,
  499.                                     MPFROMSHORT (sScrollPos), NULL) ;
  500.                     WinUpdateWindow (hwnd) ;
  501.                }
  502.                return 0 ;
  503.  
  504.           case WM_SYSCOLORCHANGE:
  505.                /* Global color change.  Trigger Paint to handle it */
  506.                WinInvalidateRect (hwnd, NULL, FALSE);
  507.                return 0;
  508.  
  509.            case WM_COMMAND:
  510.                 switch (COMMANDMSG(&msg)->cmd) {
  511.                      case IDM_ABOUT:
  512.                           {
  513.                              HWND          hwndAbout;
  514.                              USHORT        rc;
  515.  
  516.                              /*
  517.                                 The following kludge is required because of APAR PJ00398.
  518.                                 If ICON static controls in a dialog resource are compiled
  519.                                 into a DLL, it will not load and will cause the entire
  520.                                 dialog box to not display.  ICON resources will not load
  521.                                 into dialog boxes which are in DLLs.  The ICON, dialog box,
  522.                                 and the calling code are all in the same DLL.
  523.                              */
  524.  
  525.                  hwndAbout = WinLoadDlg (HWND_DESKTOP, hwnd,   /* load em up */
  526.                                          HelpAboutProc, hmodHelp, IDD_ABOUT, NULL);
  527.                              /* fix the ICON */
  528.                              WinSendMsg (WinWindowFromID (hwndAbout, IDC_MYICON),
  529.                                         SM_SETHANDLE,
  530.                                         MPFROMLONG( (LONG) WinSendMsg (hwndHelpFrame,
  531.                                                            WM_QUERYICON, 0L, 0L) ),
  532.                                         0L);
  533.                  rc = WinProcessDlg(hwndAbout);   /* do the dialog box    */
  534.                  WinDestroyWindow (hwndAbout);    /*   then zilch it      */
  535.                           }
  536.  
  537.                           return 0;
  538.  
  539.                      case IDM_RESTORE:
  540.                           /* flip-flop between current and last displayed
  541.                              help text.  This allows flip-flop between
  542.                              "Help for <ZXCVBNM>" and "Help for Help" */
  543.  
  544.                           WinPostMsg (hwnd, WM_NEW_TEXT,
  545.                                MPFROM2SHORT(hmodHelpLast, 0),
  546.                                MPFROM2SHORT(idTypeLast, idNameLast) );
  547.  
  548.                           return 0;
  549.  
  550.                 default: return 0;
  551.             }
  552.             break;
  553.  
  554.           case WM_HELP:
  555.                WinPostMsg (hwnd, WM_NEW_TEXT,
  556.                            MPFROM2SHORT(hmodHelp, 0),
  557.                            MPFROM2SHORT(IDT_TEXT, IDT_HELPONHELP) );
  558.                return 0 ;
  559.  
  560.           case WM_PAINT:
  561.                /* It's showtime, folks */
  562.  
  563.            /* synchronize with the subthread if it is active */
  564.  
  565.            DosSemRequest ((HSEM) &semWindowPS, SEM_INDEFINITE_WAIT);
  566.  
  567.                hps = WinBeginPaint (hwnd, NULL, &rclInvalid) ;
  568.  
  569.                /* set the foreground and background colors to the
  570.                   system color values SYSCLR_HELPTEXT and
  571.                   SYSCLR_HELPBACKGROUND, respectively.             */
  572.  
  573.                GpiCreateLogColorTable (hps, LCOL_RESET, LCOLF_RGB,
  574.                               0L, 0L, NULL);
  575.                GpiSetColor (hps, WinQuerySysColor (HWND_DESKTOP,
  576.                                    SYSCLR_HELPTEXT, 0L) );
  577.                WinFillRect (hps, &rclInvalid,
  578.                                  WinQuerySysColor (HWND_DESKTOP,
  579.                                  SYSCLR_HELPBACKGROUND, 0L) );
  580.  
  581.            if (tidReformat)
  582.            {   /* reformat is active.
  583.               put out a "Working..." message */
  584.  
  585.            WinQueryWindowRect(hwnd, &rclInvalid);
  586.            WinDrawText(hps, -1, szWorking, &rclInvalid,
  587.              0L, 0L, DT_CENTER | DT_VCENTER | DT_TEXTATTRS);
  588.  
  589.            }
  590.            else
  591.            {   /* put out the real text */
  592.           sPaintBeg = max (0, sScrollPos +
  593.                               (cyClient - (SHORT) rclInvalid.yTop) / cyChar) ;
  594.           sPaintEnd = min (sNumLines, sScrollPos +
  595.                               (cyClient - (SHORT) rclInvalid.yBottom)
  596.                                    / cyChar + 1) ;
  597.  
  598.           /* Skip down to the first line to be emitted */
  599.  
  600.           outp = pFormat;
  601.           for (sLine = 0; sLine < sPaintBeg; sLine++)
  602.           {
  603.               outp += strlen(outp) + 1;
  604.           }
  605.  
  606.           /* Now draw what changed */
  607.  
  608.           for (sLine = sPaintBeg ; sLine < sPaintEnd ; sLine++)
  609.           {
  610.                ptl.x = cxChar ;
  611.                ptl.y = cyClient - cyChar * (sLine + 1 - sScrollPos)
  612.                     + cyDesc ;
  613.  
  614.                sLineLength = strlen (outp);
  615.                GpiCharStringAt (hps, &ptl,
  616.                  (LONG) sLineLength,
  617.                  outp) ;
  618.  
  619.                outp += sLineLength + 1;
  620.           }
  621.           /* That's all, folks */
  622.            }
  623.  
  624.            WinEndPaint (hps) ;
  625.  
  626.            DosSemClear ((HSEM) &semWindowPS);
  627.  
  628.                return 0 ;
  629.  
  630.           case WM_OTHERWINDOWDESTROYED:
  631.                /* if the Other Window Destroyed is our caller, and
  632.                   we are still around, close us down. */
  633.                if ( fInitWnd && (hwndCaller == HWNDFROMMP(mp1)) )
  634.                   WinSendMsg (hwnd, WM_CLOSE, 0L, 0L);
  635.                return 0;
  636.  
  637.           case WM_CLOSE:
  638.                /* close us down */
  639.            WinRegisterWindowDestroy (hwndCaller, FALSE);  /* deregister caller */
  640.                WinDestroyWindow (hwndHelpFrame);
  641.            return 0;
  642.  
  643.       case WM_DESTROY:
  644.            /* if the reformat thread is active, wait for it to finish */
  645.  
  646.            if (tidReformat)
  647.           {
  648.           flReformatStop = TRUE;
  649.           DosSemWait ( (HSEM) &semReformat, SEM_INDEFINITE_WAIT);
  650.           tidReformat = 0;
  651.           }
  652.  
  653.                DosFreeSeg (selResource) ;
  654.                DosFreeSeg (selFormat) ;
  655.            hwndHelpFrame = (HWND) 0;
  656.            hwndHelp = (HWND) 0;
  657.            hwndScroll = (HWND) 0;
  658.            hwndCaller = (HWND) 0;
  659.                fInitWnd = FALSE;
  660.            return 0 ;
  661.           }
  662.      return WinDefWindowProc (hwnd, msg, mp1, mp2) ;
  663.      }
  664.  
  665. MRESULT CALLBACK HelpAboutProc(HWND hDlg, USHORT msg, MPARAM mp1, MPARAM mp2)
  666. /*
  667.     About... dialog procedure
  668. */
  669. {
  670.     switch(msg) {
  671.         case WM_COMMAND:
  672.             switch(COMMANDMSG(&msg)->cmd) {
  673.                 case DID_OK: WinDismissDlg(hDlg, TRUE); break;
  674.                 default: break;
  675.             }
  676.         break;
  677.  
  678.         default: return WinDefDlgProc(hDlg, msg, mp1, mp2);
  679.     }
  680.     return FALSE;
  681. }
  682.  
  683. static void HelpReformat(void)
  684.     {
  685.  
  686.      HAB       habThread;
  687.      HPS       hps ;
  688.      PCHAR         inp, outp, olast, tptr;
  689.      POINTL        aptlBox[TXTBOX_COUNT];
  690.      SHORT         sLineLength,
  691.                    sFormatWidth ;
  692.  
  693.      /* Reformat the Original Resource (Text) lines into
  694.         lines that will display at full width in the
  695.         current window size.                             */
  696.  
  697.  
  698.      habThread = WinInitialize(0);
  699.  
  700.      olast = outp = pFormat;
  701.      inp = pResource;
  702.      sNumLines = sLineLength = 0;
  703.      sFormatWidth = cxClient - 2 * cxChar;
  704.  
  705.      while ( ( ! flReformatStop )
  706.          && ( ( (USHORT) (inp - pResource) ) < (USHORT) ulResourceSize )
  707.          && (*inp != '\0') && (*inp != AEOF) )
  708.      {
  709.          switch (*inp)
  710.          {
  711.              case '\r':
  712.                   inp++;
  713.                   break;
  714.  
  715.              case '\n':
  716.          case '\t':          /* white space  */
  717.              case ' ':
  718.           /* Look for line overflow first */
  719.  
  720.           DosSemRequest((HSEM) &semWindowPS, SEM_INDEFINITE_WAIT);
  721.           hps = WinGetPS (hwndHelp);
  722.                   GpiQueryTextBox (hps, (LONG) sLineLength,
  723.                                    (PCH) olast,
  724.                    TXTBOX_COUNT, aptlBox);
  725.  
  726.           WinReleasePS(hps);
  727.           DosSemClear((HSEM) &semWindowPS);
  728.  
  729.                   if ((aptlBox[TXTBOX_BOTTOMRIGHT].x -
  730.                        aptlBox[TXTBOX_BOTTOMLEFT].x) > sFormatWidth)
  731.                   {
  732.              tptr = outp;    /* try to backscan for last blank    */
  733.                      while (sLineLength > 0)
  734.                            {
  735.                            sLineLength--;
  736.                            if (*--tptr == ' ') break;
  737.                            }
  738.                      if (sLineLength == 0)
  739.              {          /* only one word on line, take it */
  740.             *outp++ = '\0';
  741.                         sNumLines++;
  742.                         olast = outp;
  743.                         sLineLength = 0;
  744.                      }
  745.                      else
  746.              {          /* overlay space, term line */
  747.                         *tptr = '\0';
  748.                         sNumLines++;
  749.                         olast = tptr + 1;
  750.             *outp++ = ' ';     /* deposit blank after remainder */
  751.                         sLineLength = outp - olast;
  752.                       }
  753.                   }
  754.                   else
  755.           {               /* no overflow */
  756.                      if ( ! ( (outp == olast) && (*inp == '\n') ) )
  757.                      {
  758.             *outp++ = ' ';      /* deposit the space */
  759.                         sLineLength++;
  760.                      }
  761.                   }
  762.  
  763.           if (*inp == '\n')     /* if it was new line */
  764.                   {
  765.              inp++;         /*  consume the new line  */
  766.                      /* check special front of */
  767.                      /*   line cases       */
  768.  
  769.              if ((USHORT)(inp - pResource) >= (USHORT) ulResourceSize )
  770.                      /* exit if at end -- no line */
  771.                      /* exists to be at the front of */
  772.              {
  773.             break;
  774.              }
  775.              else
  776.                      if ((*inp == '#') || (*inp == '\r'))
  777.                      {
  778.             *outp++ = '\0';  /* force new line, double space */
  779.                         sNumLines++;
  780.                         *outp++ = ' ';
  781.                         *outp++ = '\0';
  782.                         sNumLines++;
  783.                         olast = outp;
  784.                         sLineLength = 0;
  785.                         inp++;
  786.                      }
  787.                      else
  788.                      if (*inp == '\\')
  789.                      {
  790.             *outp++ = '\0';   /* force new line, single space */
  791.                         sNumLines++;
  792.                         olast = outp;
  793.                         sLineLength = 0;
  794.                         inp++;
  795.                      }
  796.                      else
  797.                      if ( (*inp == ' ') || (*inp == '\t') )
  798.              {              /* leading space */
  799.                         if (sLineLength != 0)
  800.                         {
  801.                *outp++ = '\0';   /* force new line, single space */
  802.                            sNumLines++;
  803.                            olast = outp;
  804.                         }
  805.                         sLineLength = 1;
  806.             *outp++ = ' ';      /* and keep the space */
  807.                         inp++;
  808.                      }
  809.                   }
  810.                   else
  811.              inp++;      /* just consume the white space */
  812.                   break;
  813.  
  814.              default:
  815.           *outp++ = *inp++;    /* deposit the nonblank character */
  816.                   sLineLength++;
  817.      }            /* end switch */
  818.      }             /* end while  */
  819.  
  820.      if (*(outp-1) != '\0')
  821.      {
  822.     *outp++ = '\0';        /* terminate the last line if necessary */
  823.     sNumLines++;
  824.      }
  825.  
  826.      WinTerminate (habThread);
  827.  
  828.      /* indicate we done */
  829.      DosEnterCritSec();
  830.      DosSemClear(&semReformat);
  831.      WinPostMsg (hwndHelp, WM_SUB_DONE, 0L, 0L);
  832.  
  833.      /* and exit thread */
  834.  
  835.     }
  836.