home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c081_7 / 2.ddi / WEXAMPLE.ZIP / TSTAPP.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-02-13  |  26.4 KB  |  849 lines

  1. // Borland C++ - (C) Copyright 1991 by Borland International
  2.  
  3. //*******************************************************************
  4. //
  5. // program - TstApp.c
  6. // purpose - A windows program to send messages to msgwnd server.
  7. //           This illustrates how DDE messages can be sent from
  8. //         one application and received by another.
  9. //
  10. //           To use this program, build MSGWND and TSTAPP. There
  11. //         are project files for this. When you run TSTAPP, MSGWND
  12. //           will be started automatically.
  13. //
  14. //*******************************************************************
  15.  
  16.  
  17. #include <windows.h>
  18. #include <dde.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <time.h>
  23. #include <bios.h>
  24. #include <ctype.h>
  25. #include <io.h>
  26. #include <dos.h>
  27.  
  28. #include "tstapp.h"
  29. #include "msgproc.h"
  30. #include "ddeclnt.h"
  31.  
  32. #define MAX_QRT_LEN 100
  33.  
  34. typedef struct scrollkeys
  35.   {
  36.     WORD wVirtkey;
  37.     int  iMessage;
  38.     WORD wRequest;
  39.   } SCROLLKEYS;
  40.  
  41. SCROLLKEYS key2scroll [] =
  42.   {
  43.     VK_HOME,  WM_COMMAND, IDM_HOME,
  44.     VK_END,   WM_VSCROLL, SB_BOTTOM,
  45.     VK_PRIOR, WM_VSCROLL, SB_PAGEUP,
  46.     VK_NEXT,  WM_VSCROLL, SB_PAGEDOWN,
  47.     VK_UP,    WM_VSCROLL, SB_LINEUP,
  48.     VK_DOWN,  WM_VSCROLL, SB_LINEDOWN,
  49.     VK_LEFT,  WM_HSCROLL, SB_PAGEUP,
  50.     VK_RIGHT, WM_HSCROLL, SB_PAGEDOWN
  51.   };
  52.  
  53. # define NUMKEYS (sizeof key2scroll / sizeof key2scroll[0])
  54.  
  55. // data initialized by first instance
  56. typedef struct tagSETUPDATA
  57.   {
  58.     char   szAppName[10]; // name of application
  59.   } SETUPDATA;
  60.  
  61. SETUPDATA SetUpData;
  62.  
  63. // Data that can be referenced throughout the
  64. // program but not passed to other instances
  65.  
  66. HANDLE    hInst;         // hInstance of application
  67. HWND      hWndMain;      // hWnd of main window
  68. //char      szAppName[10]; // name of application
  69.  
  70. int       xChar, yChar, yCharnl;
  71. int       xClient, yClient;
  72.  
  73. LOGFONT   cursfont;
  74. HANDLE    holdsfont;
  75. HANDLE    hnewsfont;
  76.  
  77. RECT      wrect;
  78.  
  79. // window scroll/paint stuff
  80. int       nVscrollMax, nHscrollMax;
  81. int       nVscrollPos, nHscrollPos;
  82. int       numlines;
  83. int       maxwidth;
  84. int       nVscrollInc, nHscrollInc;
  85. int       nPaintBeg, nPaintEnd;
  86. int       nPageMaxLines;
  87.  
  88. // for scroll print
  89. RECT      rect;
  90. int       blanklen;
  91. char      blanks[256];
  92.  
  93. // to keep lines
  94. # define  MAX_KEEP   50
  95. HANDLE    hkeep[MAX_KEEP + 1];
  96. int       hwm_keep;
  97.  
  98. // function prototypes
  99.  
  100. int      PASCAL        WinMain(HANDLE hInstance, HANDLE hPrevInstance,
  101.                                LPSTR lpszCmdLine, int cmdShow);
  102.  
  103. void                   InitTstApp(HANDLE hInstance, HANDLE hPrevInstance,
  104.                                  LPSTR lpszCmdLine, int cmdShow);
  105. void                   InitTstAppFirst(HANDLE hInstance);
  106. void                   InitTstAppAdded(HANDLE hPrevInstance);
  107. void                   InitTstAppEvery(HANDLE hInstance, int cmdShow);
  108. void                   CloseTstApp(void);
  109. BOOL FAR PASCAL        About(HWND hDlg, WORD message, WORD wParam, LONG lParam);
  110.  
  111. long FAR PASCAL        TstAppWndProc(HWND hWnd, WORD message,
  112.                                     WORD wParam, LONG lParam);
  113.  
  114. void                   SetupScroll(HWND hWnd);
  115. void                   TstAppPaint(HWND hWnd);
  116. void                   ScrollPrint(HWND hWnd, char *str);
  117. void                   ClearKeep(void);
  118.  
  119. //*******************************************************************
  120. // WinMain - TstApp main
  121. //
  122. // paramaters:
  123. //             hInstance     - The instance of this instance of this
  124. //                             application.
  125. //             hPrevInstance - The instance of the previous instance
  126. //                             of this application. This will be 0
  127. //                             if this is the first instance.
  128. //             lpszCmdLine   - A long pointer to the command line that
  129. //                             started this application.
  130. //             cmdShow       - Indicates how the window is to be shown
  131. //                             initially. ie. SW_SHOWNORMAL, SW_HIDE,
  132. //                             SW_MIMIMIZE.
  133. //
  134. // returns:
  135. //             wParam from last message.
  136. //
  137. //*******************************************************************
  138. int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance,
  139.                    LPSTR lpszCmdLine, int cmdShow)
  140. {
  141.     MSG   msg;
  142.  
  143.     InitTstApp(hInstance, hPrevInstance, lpszCmdLine, cmdShow);
  144.  
  145.     while (GetMessage(&msg, NULL, 0, 0))
  146.     {
  147.         TranslateMessage(&msg);
  148.         DispatchMessage(&msg);
  149.     }
  150.  
  151.     return(msg.wParam);
  152. }
  153.  
  154. //*******************************************************************
  155.  
  156. //*******************************************************************
  157. // InitTstApp - init the TstApp application
  158. //
  159. // paramaters:
  160. //             hInstance     - The instance of this instance of this
  161. //                             application.
  162. //             hPrevInstance - The instance of the previous instance
  163. //                             of this application. This will be 0
  164. //                             if this is the first instance.
  165. //             lpszCmdLine   - A long pointer to the command line that
  166. //                             started this application.
  167. //             cmdShow       - Indicates how the window is to be shown
  168. //                             initially. ie. SW_SHOWNORMAL, SW_HIDE,
  169. //                             SW_MIMIMIZE.
  170. //
  171. //*******************************************************************
  172. # pragma argsused
  173. void InitTstApp(HANDLE hInstance, HANDLE hPrevInstance,
  174.                LPSTR lpszCmdLine, int cmdShow)
  175. {
  176.     if (! hPrevInstance)
  177.         InitTstAppFirst(hInstance);
  178.     else
  179.         InitTstAppAdded(hPrevInstance);
  180.  
  181.     InitTstAppEvery(hInstance, cmdShow);
  182. }
  183.  
  184. //*******************************************************************
  185. // InitTstAppFirst - done only for first instance of TstApp
  186. //
  187. // paramaters:
  188. //             hInstance     - The instance of this instance of this
  189. //                             application.
  190. //
  191. //*******************************************************************
  192. void InitTstAppFirst(HANDLE hInstance)
  193. {
  194.     WNDCLASS wcTstAppClass;
  195.  
  196.     LoadString(hInstance, IDS_NAME, (LPSTR) SetUpData.szAppName, 10);
  197.  
  198.     // fill in window class information
  199.  
  200.     wcTstAppClass.lpszClassName = SetUpData.szAppName;
  201.     wcTstAppClass.hInstance     = hInstance;
  202.     wcTstAppClass.lpfnWndProc   = TstAppWndProc;
  203.     wcTstAppClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
  204.     wcTstAppClass.hIcon         = LoadIcon(hInstance, SetUpData.szAppName);
  205.     wcTstAppClass.lpszMenuName  = (LPSTR) SetUpData.szAppName;
  206.     wcTstAppClass.hbrBackground = GetStockObject(WHITE_BRUSH);
  207.     wcTstAppClass.style         = CS_HREDRAW | CS_VREDRAW;
  208.     wcTstAppClass.cbClsExtra    = 0;
  209.     wcTstAppClass.cbWndExtra    = 0;
  210.  
  211.     // register the class
  212.  
  213.     RegisterClass(&wcTstAppClass);
  214. }
  215.  
  216. //*******************************************************************
  217. // InitTstAppAdded - done only for added instances of TstApp
  218. //
  219. // paramaters:
  220. //             hPrevInstance - The instance of the previous instance
  221. //                             of this application.
  222. //
  223. //*******************************************************************
  224. void InitTstAppAdded(HANDLE hPrevInstance)
  225. {
  226.     // get the results of the initialization of first instance
  227.     GetInstanceData(hPrevInstance, (PSTR) &SetUpData, sizeof(SETUPDATA));
  228. }
  229.  
  230. //*******************************************************************
  231. // InitTstAppEvery - done for every instance of TstApp
  232. //                   Will create the window and sets a fixed font
  233. //                   for tabular display.
  234. //
  235. // paramaters:
  236. //             hInstance     - The instance of this instance of this
  237. //                             application.
  238. //             cmdShow       - Indicates how the window is to be shown
  239. //                             initially. ie. SW_SHOWNORMAL, SW_HIDE,
  240. //                             SW_MIMIMIZE.
  241. //
  242. //*******************************************************************
  243. void InitTstAppEvery(HANDLE hInstance, int cmdShow)
  244. {
  245.     TEXTMETRIC tm;
  246.     HDC        hDC;
  247.  
  248.     hInst = hInstance;       // save for use by window procs
  249.  
  250.     hWndMain = CreateWindow(
  251.                   SetUpData.szAppName,     // window class name
  252.                   SetUpData.szAppName,     // window title
  253.                   WS_OVERLAPPEDWINDOW |    // type of window
  254.                     WS_HSCROLL |
  255.                     WS_VSCROLL,
  256.           CW_USEDEFAULT,           // x  window location
  257.                   0,                       // y
  258.                   500,                     // cx and size
  259.                   150,                     // cy
  260.                   NULL,                    // no parent for this window
  261.                   NULL,                    // use the class menu
  262.                   hInstance,               // who created this window
  263.                   NULL                     // no parms to pass on
  264.                   );
  265.  
  266.     hDC = GetDC(hWndMain);
  267.  
  268.     // build fied screen font
  269.     cursfont.lfHeight         =  6;
  270.     cursfont.lfWidth          =  6;
  271.     cursfont.lfEscapement     =  0;
  272.     cursfont.lfOrientation    =  0;
  273.     cursfont.lfWeight         =  FW_NORMAL;
  274.     cursfont.lfItalic         =  FALSE;
  275.     cursfont.lfUnderline      =  FALSE;
  276.     cursfont.lfStrikeOut      =  FALSE;
  277.     cursfont.lfCharSet        =  ANSI_CHARSET;
  278.     cursfont.lfOutPrecision   =  OUT_DEFAULT_PRECIS;
  279.     cursfont.lfClipPrecision  =  CLIP_DEFAULT_PRECIS;
  280.     cursfont.lfQuality        =  DEFAULT_QUALITY;
  281.     cursfont.lfPitchAndFamily =  FIXED_PITCH | FF_DONTCARE;
  282.     strcpy(cursfont.lfFaceName, "System");
  283.  
  284.     hnewsfont = CreateFontIndirect((LPLOGFONT) &cursfont);
  285.     holdsfont = SelectObject(hDC, hnewsfont);
  286.  
  287.     // get text metrics for paint
  288.     GetTextMetrics(hDC, &tm);
  289.     xChar = tm.tmAveCharWidth;
  290.     yChar = tm.tmHeight + tm.tmExternalLeading;
  291.     yCharnl = tm.tmHeight;
  292.  
  293.     // init blank line
  294.     blanklen = 255;
  295.     memset(blanks, ' ', blanklen);
  296.  
  297.     ReleaseDC(hWndMain, hDC);
  298.  
  299.     ShowWindow(hWndMain, cmdShow);
  300.     UpdateWindow(hWndMain);
  301. }
  302.  
  303. //*******************************************************************
  304. // CloseTstApp - done at termination of every instance of TstApp
  305. //               This is where wrapup code that should be run
  306. //               at the termination of the application should go.
  307. //
  308. //*******************************************************************
  309. void CloseTstApp()
  310. {
  311.     ClearKeep();
  312.     CloseMsgWnd();
  313. }
  314.  
  315. //*******************************************************************
  316. // About - handle about dialog box messages
  317. //
  318. // paramaters:
  319. //             hDlg          - The window handle for this dialog box
  320. //             message       - The message number
  321. //             wParam        - The WORD parmater for this message
  322. //             lParam        - The LONG parmater for this message
  323. //
  324. //*******************************************************************
  325. # pragma argsused
  326. BOOL FAR PASCAL About(HWND hDlg, WORD message, WORD wParam, LONG lParam)
  327. {
  328.     if (message == WM_INITDIALOG)
  329.         return(TRUE);
  330.  
  331.     else if (message == WM_COMMAND)
  332.     {
  333.         switch (wParam)
  334.         {
  335.             case IDOK:
  336.         EndDialog(hDlg, TRUE);
  337.                 return(TRUE);
  338.  
  339.             default:
  340.                 return(TRUE);
  341.         }
  342.     }
  343.  
  344.     return(FALSE);
  345. }
  346.  
  347. //*******************************************************************
  348.  
  349. //*******************************************************************
  350. // TstAppWndProc - every message for this instance will come here
  351. //
  352. //   Handle the messages for this application.
  353. //
  354. // paramaters:
  355. //             hWnd          - The window handle for this message
  356. //             message       - The message number
  357. //             wParam        - The WORD parmater for this message
  358. //             lParam        - The LONG parmater for this message
  359. //
  360. // returns:
  361. //             depends on message.
  362. //
  363. //*******************************************************************
  364. long FAR PASCAL TstAppWndProc(HWND hWnd, WORD message,
  365.                              WORD wParam, LONG lParam)
  366. {
  367.     static   int msgno;
  368.  
  369.     FARPROC  lpproc;
  370.     char     buf[128];
  371.     int      i;
  372.     HANDLE   hTmp;
  373.     LPSTR    lpTmp;
  374.  
  375.     switch (message)
  376.     {
  377.         case WMU_DDE_CLOSED:
  378.             // This message is sent by the ddeclnt module when the dde
  379.             // conversation is terminated.
  380.             CloseMsgWnd();
  381.             break;
  382.  
  383.         case WM_COMMAND:
  384.             switch (wParam)
  385.             {
  386.                 case IDM_QUIT:
  387.                     // User has selected QUIT from menu.
  388.                     PostMessage(hWnd, WM_CLOSE, 0, 0L);
  389.                     break;
  390.  
  391.                 case IDM_HOME:
  392.                     // Home key was hit.
  393.                     SendMessage(hWnd, WM_HSCROLL, SB_TOP, 0L); /* not set up */
  394.                     SendMessage(hWnd, WM_VSCROLL, SB_TOP, 0L);
  395.                     break;
  396.  
  397.                 case IDM_ABOUT:
  398.                     // User has selected ABOUT from menu.
  399.                     lpproc = MakeProcInstance(About, hInst);
  400.                     DialogBox(hInst,
  401.                               MAKEINTRESOURCE(ABOUT),
  402.                               hWnd,
  403.                               lpproc);
  404.                     FreeProcInstance(lpproc);
  405.                     break;
  406.  
  407.                 case IDM_MESSAGE:
  408.                     // User has selected MSG from menu.
  409.  
  410.                     // Format the message to send.
  411.                     sprintf(buf, "TstApp hWnd %04X Msg # %04d: ", hWnd, ++msgno);
  412.  
  413.                     // Show it in our window.
  414.                     ScrollPrint(hWnd, buf);
  415.  
  416.             // Send it via dde to msgwnd application.
  417.                     SendMsgWndMsg(hWnd, "print", buf);
  418.                     break;
  419.  
  420.                 case IDM_CLEAR:
  421.                     // User has selected CLEAR from menu.
  422.  
  423.                     // Tell msgwnd applicaton to clear its window.
  424.                     SendMsgWndMsg(hWnd, "clear", NULL);
  425.                     break;
  426.  
  427.                 case IDM_LINES:
  428.                     // User has selected LINES from menu.
  429.  
  430.                     // Ask msgwnd applicaton how many lines it has recieved.
  431.                     hTmp = GetLines(hWnd);
  432.                     if (hTmp)
  433.                     {
  434.                         lpTmp = GlobalLock(hTmp);
  435.                         if (lpTmp)
  436.             {
  437.                             _fstrcpy(buf, lpTmp);
  438.                             ScrollPrint(hWnd, buf);
  439.                             GlobalUnlock(hTmp);
  440.                         }
  441.  
  442.                         GlobalFree(hTmp);
  443.                     }
  444.                     break;
  445.  
  446.                 default:
  447.                     break;
  448.             }
  449.             break;
  450.  
  451.         case WM_SIZE:
  452.             // Save size of window client area.
  453.  
  454.             yClient = HIWORD(lParam);
  455.             xClient = LOWORD(lParam);
  456.  
  457.             // Go setup scroll ranges and file display area based upon
  458.             // client area size.
  459.  
  460.             SetupScroll(hWnd);
  461.             break;
  462.  
  463.         case WM_VSCROLL:
  464.             // React to the various vertical scroll related actions.
  465.             switch (wParam)
  466.             {
  467.                 case SB_TOP:
  468.                     nVscrollInc = -nVscrollPos;
  469.                     break;
  470.  
  471.                 case SB_BOTTOM:
  472.                     nVscrollInc = nVscrollMax - nVscrollPos;
  473.                     break;
  474.  
  475.                 case SB_LINEUP:
  476.             nVscrollInc = -1;
  477.                     break;
  478.  
  479.                 case SB_LINEDOWN:
  480.                     nVscrollInc = 1;
  481.                     break;
  482.  
  483.                 case SB_PAGEUP:
  484.                     nVscrollInc = -max(1, yClient / yChar);
  485.                     break;
  486.  
  487.                 case SB_PAGEDOWN:
  488.                     nVscrollInc = max(1, yClient / yChar);
  489.                     break;
  490.  
  491.                 case SB_THUMBPOSITION:
  492.                     nVscrollInc = LOWORD(lParam) - nVscrollPos;
  493.                     break;
  494.  
  495.                 case SB_THUMBTRACK:
  496.             nVscrollInc = LOWORD(lParam) - nVscrollPos;
  497.                     break;
  498.  
  499.                 default:
  500.                     nVscrollInc = 0;
  501.             }
  502.  
  503.             nVscrollInc = max(-nVscrollPos,
  504.                               min(nVscrollInc, nVscrollMax - nVscrollPos));
  505.             if (nVscrollInc)
  506.             {
  507.                 nVscrollPos += nVscrollInc;
  508.                 ScrollWindow(hWnd, 0, -yChar * nVscrollInc, NULL, NULL);
  509.                 SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE);
  510.                 UpdateWindow(hWnd);
  511.             }
  512.             break;
  513.  
  514.         case WM_HSCROLL:
  515.             // React to the various horizontal scroll related actions.
  516.         switch (wParam)
  517.             {
  518.                 case SB_LINEUP:
  519.                     nHscrollInc = -1;
  520.                     break;
  521.  
  522.                 case SB_LINEDOWN:
  523.                     nHscrollInc = 1;
  524.                     break;
  525.  
  526.                 case SB_PAGEUP:
  527.                     nHscrollInc = -8;
  528.                     break;
  529.  
  530.                 case SB_PAGEDOWN:
  531.                     nHscrollInc = 8;
  532.                     break;
  533.  
  534.                 case SB_THUMBPOSITION:
  535.                     nHscrollInc = LOWORD(lParam) - nHscrollPos;
  536.             break;
  537.  
  538.                 case SB_THUMBTRACK:
  539.                     nHscrollInc = LOWORD(lParam) - nHscrollPos;
  540.                     break;
  541.  
  542.                 default:
  543.                     nHscrollInc = 0;
  544.             }
  545.  
  546.             nHscrollInc = max(-nHscrollPos,
  547.                               min(nHscrollInc, nHscrollMax - nHscrollPos));
  548.             if (nHscrollInc)
  549.             {
  550.                 nHscrollPos += nHscrollInc;
  551.                 ScrollWindow(hWnd, -xChar * nHscrollInc, 0, NULL, NULL);
  552.                 SetScrollPos(hWnd, SB_HORZ, nHscrollPos, TRUE);
  553.                 UpdateWindow(hWnd);
  554.             }
  555.             break;
  556.  
  557.         case WM_KEYDOWN:
  558.             // Translate various keydown messages to appropriate horizontal
  559.             // and vertical scroll actions.
  560.  
  561.             for (i = 0; i < NUMKEYS; i++)
  562.             {
  563.                 if (wParam == key2scroll[i].wVirtkey)
  564.                 {
  565.                     SendMessage(hWnd, key2scroll[i].iMessage,
  566.                                 key2scroll[i].wRequest, 0L);
  567.                     break;
  568.                 }
  569.             }
  570.             break;
  571.  
  572.         case WM_PAINT:
  573.             // Go paint the client area of the window with the appropriate
  574.             // part of the selected file.
  575.  
  576.         TstAppPaint(hWnd);
  577.             break;
  578.  
  579.         case WM_DESTROY:
  580.             // This is the end if we were closed by a DestroyWindow call.
  581.             CloseTstApp();         // take any necessary wrapup action.
  582.             PostQuitMessage(0);    // this is the end...
  583.             break;
  584.  
  585.         case WM_QUERYENDSESSION:
  586.             // If we return TRUE we are saying it's ok with us to end the
  587.             // windows session.
  588.             CloseTstApp();         // take any necessary wrapup action.
  589.             return((long) TRUE);   // we agree to end session.
  590.  
  591.         case WM_CLOSE:
  592.             // Tell windows to terminate us.
  593.             DestroyWindow(hWnd);
  594.             break;
  595.  
  596.     default:
  597.             // Let windows handle all messages we choose to ignore.
  598.             return(DefWindowProc(hWnd, message, wParam, lParam));
  599.     }
  600.  
  601.     return(0L);
  602. }
  603.  
  604. //*******************************************************************
  605. // SetupScroll - setup scroll ranges
  606. //
  607. //   Setup the vertical and horizontal scroll ranges and positions
  608. //   of the applicatons main window based on:
  609. //
  610. //       numlines - The maximum number of lines to display.
  611. //       maxwidth - The maximum width of any line to display.
  612. //
  613. //   The resulting variables, nVscrollPos and nPageMaxLines, are used
  614. //   by the function TstAppPaint to determine what part of the selected
  615. //   file to display in the window.
  616. //
  617. // paramaters:
  618. //             hWnd          - The callers window handle
  619. //
  620. //*******************************************************************
  621. void SetupScroll(HWND hWnd)
  622. {
  623.     // numlines established during open
  624.     nVscrollMax = max(0, numlines - yClient / yChar);
  625.     nVscrollPos = min(nVscrollPos, nVscrollMax);
  626.  
  627.     nHscrollMax = max(0, maxwidth - xClient / xChar);
  628.     nHscrollPos = min(nHscrollPos, nHscrollMax);
  629.  
  630.     SetScrollRange (hWnd, SB_VERT, 0, nVscrollMax, FALSE);
  631.     SetScrollPos   (hWnd, SB_VERT, nVscrollPos, TRUE);
  632.  
  633.     SetScrollRange (hWnd, SB_HORZ, 0, nHscrollMax, FALSE);
  634.     SetScrollPos   (hWnd, SB_HORZ, nHscrollPos, TRUE);
  635.  
  636.     nPageMaxLines = min(numlines, yClient / yChar);
  637.  
  638.     rect.left = 0;
  639.     rect.top = 0;
  640.     rect.right = xClient;
  641.     rect.bottom = yClient;
  642.  
  643.     blanklen = rect.right / xChar + 1;
  644. }
  645.  
  646. //*******************************************************************
  647. // TstAppPaint - paint the main window
  648. //
  649. // This function is responsible for redisplaying a portion of the saved
  650. // strings.  Which strings it displays depends on the current scroll
  651. // position.
  652. //
  653. // paramaters:
  654. //             hWnd          - The callers window handle
  655. //
  656. //*******************************************************************
  657. void TstAppPaint(HWND hWnd)
  658. {
  659.     PAINTSTRUCT  ps;
  660.     HDC          hDC;
  661.     char         currec[256];
  662.     int          ypos;
  663.     LPSTR        lcp;
  664.     int          i;
  665.     int          ndone;
  666.  
  667.     // Get display context.
  668.     BeginPaint(hWnd, (LPPAINTSTRUCT) &ps);
  669.     hDC = ps.hdc;
  670.  
  671.     // Select fixed font.
  672.     SelectObject(hDC, hnewsfont);
  673.  
  674.     // Setup scroll ranges.
  675.     SetupScroll(hWnd);
  676.  
  677.     // See if we have any lines to show.
  678.     if (hwm_keep)
  679.       {
  680.         // Y position of bottom line in client area.
  681.         ypos = rect.bottom - yChar;
  682.  
  683.     // Index into keep list of first line (from bottom) to show.
  684.         i = nVscrollMax - nVscrollPos;
  685.  
  686.         ndone = 1;
  687.         while (ndone)
  688.           {
  689.             lcp = GlobalLock(hkeep[i]);
  690.             if (lcp)
  691.               {
  692.                 // We must fill line with blanks to width of window
  693.                 // or else some previous longet text might show through.
  694.                 _fstrcpy(currec, blanks);
  695.  
  696.                 // Line to show.
  697.                 _fstrncpy(currec, lcp, _fstrlen(lcp));
  698.  
  699.                 // Send to window.
  700.                 TextOut(hDC,
  701.                         xChar * (-nHscrollPos + 0),
  702.                         ypos,
  703.             currec,
  704.                         blanklen);
  705.  
  706.                 // New Y is one character height higher.
  707.                 ypos -= yChar;
  708.  
  709.                 GlobalUnlock(hkeep[i]);
  710.               }
  711.  
  712.             // Index of next keep string to show.
  713.             i++;
  714.  
  715.             // No use drawing lines beyond top of client area.
  716.             // They would not show, so don't wast the energy.
  717.             if (ypos < -yChar)
  718.               ndone = 0;
  719.  
  720.             // Have we done all of the lines?
  721.             if (i > (hwm_keep - 1))
  722.               ndone = 0;
  723.       }
  724.       }
  725.  
  726.     // Release the display context.
  727.     ReleaseDC(hWnd, hDC);
  728.  
  729.     EndPaint(hWnd, (LPPAINTSTRUCT) &ps);
  730. }
  731.  
  732. //*******************************************************************
  733. // ScrollPrint - saves string sent to it and cause it to display
  734. //
  735. // This function gets a string and saves it in a list of strings.
  736. // The oldest string is deleted when the list reaches its maximum
  737. // size.
  738. //
  739. // Depending on the scroll position at the time the string is recieved
  740. // one of two methods of displaying the string will be used:
  741. //
  742. // If the scroll position was at the bottom, the window is scrolled up
  743. // one line, the bottom line is made invalid, and windows is told to
  744. // repaint. This will cause only the new string to be output, resulting
  745. // in minimum redraw on the screen.
  746. //
  747. // If the scroll position was not at the bottom of the window because
  748. // the user had been looking at previous messages, windows will be told
  749. // to redraw the entire window starting with the new string.
  750. //
  751. // paramaters:
  752. //             hWnd          - The window to put the message in.
  753. //             str           - the string to print in window.
  754. //
  755. //*******************************************************************
  756. void ScrollPrint(HWND hWnd, char *str)
  757. {
  758.     HDC   hDC;
  759.     char  currec[256];
  760.     int   i;
  761.     int   lstr;
  762.     LPSTR lcp;
  763.     RECT  trect;
  764.  
  765.     // If our keep stack is full free oldest member.
  766.     if (hwm_keep >= MAX_KEEP)
  767.       GlobalFree(hkeep[hwm_keep]);
  768.  
  769.     // Move all handles to make room for new one.
  770.     for (i = hwm_keep; i > 0; i--)
  771.       hkeep[i] = hkeep[i - 1];
  772.  
  773.     // If keep stack not yet full add one to high watter mark.
  774.     if (hwm_keep < MAX_KEEP)
  775.       hwm_keep++;
  776.  
  777.     // Make sure we know how many saved lines there are.
  778.     numlines = hwm_keep;
  779.  
  780.     // Length of new string.
  781.     lstr = strlen(str);
  782.  
  783.     // Is it longer than any previous string.
  784.     if (lstr > maxwidth)
  785.       maxwidth = lstr;
  786.  
  787.     // Get storage to save it.
  788.     hkeep[0] = GlobalAlloc(GMEM_MOVEABLE, (DWORD) (lstr + 1));
  789.     if (hkeep[0])
  790.       {
  791.         // Lock it down to get address.
  792.         lcp = GlobalLock(hkeep[0]);
  793.         if (lcp)
  794.           {
  795.         // Save string.
  796.             _fstrcpy(lcp, str);
  797.             GlobalUnlock(hkeep[0]);
  798.           }
  799.       }
  800.  
  801.     // See what we have to do to display it efficently.
  802.     if (!(nVscrollMax - nVscrollPos))
  803.       {
  804.         // We are scrolled to bottom of list.
  805.  
  806.         // Scroll contents of window up one character hehght.
  807.         ScrollWindow(hWnd, 0, -yChar, &rect, &rect);
  808.  
  809.         // Set scroll position to last line
  810.         nVscrollPos = numlines - yChar / yClient;
  811.  
  812.         // Tell windows to repaint only the bottom line of window.
  813.         GetClientRect(hWnd, &trect);
  814.         trect.top = trect.bottom - yChar;
  815.     InvalidateRect(hWnd, &trect, TRUE);
  816.       }
  817.     else
  818.       {
  819.         // We are not scrolled to bottom of list.
  820.  
  821.         // Set scroll position to last line.
  822.         nVscrollPos = numlines - yChar / yClient;
  823.  
  824.         // Tell windows to repaint the entire window.
  825.         InvalidateRect(hWnd, NULL, TRUE);
  826.       }
  827. }
  828.  
  829. //*******************************************************************
  830. // ClearKeep - free all saved strings
  831. //
  832. //
  833. //*******************************************************************
  834. void ClearKeep()
  835.   {
  836.     int i;
  837.  
  838.     for (i = 0; i < hwm_keep; i++)
  839.       {
  840.         GlobalFree(hkeep[i]);
  841.         hkeep[i] = 0;
  842.       }
  843.  
  844.     // Reset counters.
  845.     numlines = hwm_keep = 0;
  846.   }
  847.  
  848. //*******************************************************************
  849.