home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / msj / msjv3_1 / saywhat / swp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-03-02  |  25.2 KB  |  968 lines

  1. /*
  2. Microsoft Systems Journal
  3. Volume 3; Issue 1; January, 1988
  4.  
  5. Code Listing For:
  6.  
  7.     Saywhat
  8.     pp. 09-30
  9.  
  10. Author(s): Michael Geary
  11. Title:     Converting Windows Applications For Microsoft OS/2 PM
  12.  
  13.  
  14. Figure 6pm
  15. =========
  16.  
  17. */
  18.  
  19. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *\
  20.  *  SWP.C - C code for SayWhat - Presentation Manager version    *
  21. \* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  22.  
  23. #ifndef LINT_ARGS
  24. #define LINT_ARGS  /* turn on argument checking for C runtime */
  25. #endif
  26.  
  27. #include <os2pm.h>
  28. #include <limits.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31. #include <time.h>
  32. #include "swp.h"
  33.  
  34. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35.  
  36. #define MIN_INTERVAL    1       /* limits for nInterval */
  37. #define MAX_INTERVAL    999
  38.  
  39. #define MIN_DISTANCE    1       /* limits for nDistance */
  40. #define MAX_DISTANCE    99
  41.  
  42. #define COLORDATAMAX    5
  43.  
  44. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  45.  
  46. /*  Static variables  */
  47.  
  48. HAB     hAB = NULL;             /* anchor block handle */
  49. HMQ     hMsgQ = NULL;           /* message queue handle */
  50.  
  51. HWND    hWndWhatFrame;          /* frame window handle */
  52. HWND    hWndWhat;               /* client window handle */
  53. HWND    hWndPanel;              /* control panel dialog handle */
  54.  
  55. CHAR    szAppName[10];          /* window class name */
  56. CHAR    szTitle[15];            /* main window title */
  57.  
  58. CHAR    szText[40];             /* current "what" text */
  59. PSZ     npszText = szText;      /* ptr into szText for icon mode */
  60. CHAR    cBlank = ' ';           /* a blank we can point to */
  61.  
  62. RECT    rcText;                 /* current text rectangle */
  63. POINT   ptAdvance;              /* increments for SayAdvanceText */
  64. POINT   ptCharSize;             /* X and Y size of a character */
  65. POINT   ptScreenSize;           /* X and Y size of the screen */
  66.  
  67. LONG    lColorMax;              /* number of available colors */
  68. LONG    lColor;                 /* current text color index */
  69.  
  70. SHORT   nInterval = 40;         /* current "Interval" setting */
  71. SHORT   nDistance = 30;         /* current "Distance" setting */
  72. SHORT   nDistLeft = 0;          /* change direction when hits 0 */
  73. BOOL    bCleanPaint = TRUE;     /* clean or flickery painting? */
  74. BOOL    bMouseDown = FALSE;     /* is mouse down? */
  75. BOOL    bIconic = FALSE;        /* is main window iconic? */
  76.  
  77. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  78.  
  79. /*  Full prototypes for our functions to get type checking  */
  80.  
  81. ULONG FAR PASCAL SayAboutDlgProc( HWND, USHORT, ULONG, ULONG );
  82. VOID             SayAdvanceTextChar( HWND );
  83. VOID             SayAdvanceTextPos( HWND );
  84. VOID             SayChangeColor( HWND );
  85. VOID             SayDoBarMsg( HWND, USHORT, USHORT, SHORT );
  86. VOID             SayExitApp( INT );
  87. VOID             SayFillRect( HPS, SHORT, SHORT, SHORT, SHORT );
  88. VOID             SayInitBar( HWND, SHORT, SHORT, SHORT, SHORT );
  89. BOOL             SayInitApp( VOID );
  90. VOID             SayInvalidateText( HWND );
  91. VOID             SayLimitTextPos( HWND );
  92. VOID             SayMoveText( HWND, POINT );
  93. VOID             SaySetBar( HWND, SHORT *, SHORT );
  94. ULONG FAR PASCAL SayWhatDlgProc( HWND, USHORT, ULONG, ULONG );
  95. VOID             SayWhatPaint( HWND );
  96. ULONG FAR PASCAL SayWhatWndProc( HWND, USHORT, ULONG, ULONG );
  97. void  cdecl      main( INT, PSZ );
  98.  
  99. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  100.  
  101. /*  Dialog function for the "About" box  */
  102.  
  103. ULONG FAR PASCAL SayAboutDlgProc( hWndDlg, wMsg, lParam1, lParam2 )
  104.     HWND        hWndDlg;
  105.     USHORT      wMsg;
  106.     ULONG       lParam1;
  107.     ULONG       lParam2;
  108. {
  109.     switch( wMsg )
  110.     {
  111.       case WM_COMMAND:
  112.         switch( LOUSHORT(lParam1) )
  113.         {
  114.           case IDOK:
  115.             WinDismissDlg( hWndDlg, TRUE );
  116.             break;
  117.         }
  118.     }
  119.     return WinDefDlgProc( hWndDlg, wMsg, lParam1, lParam2 );
  120. }
  121.  
  122. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  123.  
  124. /*  Advances to next display character in iconic mode.
  125.  *  Forces in a blank when it reaches the end of string.
  126.  */
  127.  
  128. VOID SayAdvanceTextChar( hWnd )
  129.     HWND        hWnd;
  130. {
  131.     if( ! bIconic )
  132.         return;
  133.  
  134.     if( npszText == &cBlank )
  135.         npszText = szText;
  136.     else if( ! *(++npszText) )
  137.         npszText = &cBlank;
  138.  
  139.     SayChangeColor( hWnd );
  140.     SayInvalidateText( hWnd );
  141. }
  142.  
  143. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  144.  
  145. /*  Advances text position according to ptAdvance.  Decrements
  146.  *  nDistLeft first, and when it reaches zero, sets a new
  147.  *  randomized ptAdvance and nDistLeft, also changes color.
  148.  *  Does nothing if mouse is down, so text will track mouse.
  149.  */
  150.  
  151. VOID SayAdvanceTextPos( hWnd )
  152.     HWND        hWnd;
  153. {
  154.     SHORT       i;
  155.  
  156.     if( bMouseDown )
  157.         return;
  158.  
  159.     SayInvalidateText( hWnd );
  160.  
  161.     if( nDistLeft-- < 0 ) {
  162.         nDistLeft = rand() % nDistance + 1;
  163.         do {
  164.             i = rand();
  165.             ptAdvance.x = (
  166.                 i < SHRT_MAX/3   ? -1
  167.               : i < SHRT_MAX/3*2 ?  0
  168.               :                     1
  169.             );
  170.             i = rand();
  171.             ptAdvance.y = (
  172.                 i < SHRT_MAX/3   ? -1
  173.               : i < SHRT_MAX/3*2 ?  0
  174.               :                     1
  175.             );
  176.         } while( ptAdvance.x == 0  &&  ptAdvance.y == 0 );
  177.         if( ! bIconic )
  178.             SayChangeColor( hWnd );
  179.     } else {
  180.         rcText.xLeft   += ptAdvance.x;
  181.         rcText.xRight  += ptAdvance.x;
  182.         rcText.yTop    += ptAdvance.y;
  183.         rcText.yBottom += ptAdvance.y;
  184.     }
  185.  
  186.     SayInvalidateText( hWnd );
  187. }
  188.  
  189. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  190.  
  191. /*  Changes color to a random selection, if color is available.
  192.  *  Forces a color change - if the random selection is the same
  193.  *  as the old one, it tries again.
  194.  */
  195.  
  196. VOID SayChangeColor( hWnd )
  197.     HWND        hWnd;
  198. {
  199.     HPS         hPS;
  200.     LONG        lWindow;
  201.     LONG        lNew;
  202.  
  203.     hPS = WinGetPS( hWnd );
  204.  
  205.     if( lColorMax <= 2 ) {
  206.         lColor = GpiQueryColorIndex(
  207.             hPS,
  208.             WinQuerySysColor( hAB, SCLR_WINDOWTEXT ),
  209.             0L
  210.         );
  211.     } else {
  212.         lWindow = GpiQueryColorIndex(
  213.             hPS,
  214.             WinQuerySysColor( hAB, SCLR_WINDOW ),
  215.             0L
  216.         );
  217.         do {
  218.             lNew = rand() % lColorMax;
  219.         } while( lNew == lWindow || lNew == lColor );
  220.         lColor = lNew;
  221.     }
  222.  
  223.     WinReleasePS( hPS );
  224.  
  225. }
  226.  
  227. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  228.  
  229. /*  Handles scroll bar messages from the control dialog box.
  230.  *  Adjusts scroll bar position, taking its limits into account,
  231.  *  copies the scroll bar value into the adjacent edit control,
  232.  *  then sets the nDistance or nInterval variable appropriately.
  233.  */
  234.  
  235. VOID SayDoBarMsg( hWndDlg, idBar, wCode, nThumb )
  236.     HWND        hWndDlg;
  237.     USHORT      idBar;    
  238.     USHORT      wCode;
  239.     SHORT       nThumb;
  240. {
  241.     SHORT       nPos;
  242.     SHORT       nOldPos;
  243.     ULONG       lRange;
  244.  
  245.     nOldPos = nPos =
  246.         (SHORT)WinSendDlgItemMsg(
  247.             hWndDlg, idBar, SBM_QUERYPOS, 0L, 0L
  248.         );
  249.  
  250.     lRange =
  251.         WinSendDlgItemMsg(
  252.             hWndDlg, idBar, SBM_QUERYRANGE, 0L, 0L
  253.         );
  254.  
  255.     switch( wCode ) {
  256.  
  257.         case SB_LINEUP:         --nPos;                   break;        
  258.  
  259.         case SB_LINEDOWN:       ++nPos;                   break;    
  260.  
  261.         case SB_PAGEUP:         nPos -= 10;               break;    
  262.  
  263.         case SB_PAGEDOWN:       nPos += 10;               break;    
  264.  
  265.         case SB_THUMBPOSITION:
  266.         case SB_THUMBTRACK:     nPos = nThumb;            break;    
  267.  
  268.         case SB_TOP:            nPos = LOUSHORT(lRange);  break;    
  269.  
  270.         case SB_BOTTOM:         nPos = HIUSHORT(lRange);  break;    
  271.  
  272.     }
  273.  
  274.     if( nPos < LOUSHORT(lRange) )
  275.         nPos = LOUSHORT(lRange);
  276.  
  277.     if( nPos > HIUSHORT(lRange) )
  278.         nPos = HIUSHORT(lRange);
  279.  
  280.     if( nPos == nOldPos )
  281.         return;
  282.  
  283.     WinSendDlgItemMsg(
  284.         hWndDlg, idBar, SBM_SETPOS, (LONG)nPos, 0L
  285.     );
  286.  
  287.     WinSetDlgItemShort( hWndDlg, idBar+1, nPos, FALSE );
  288.  
  289.     switch( idBar )
  290.     {
  291.       case ITEM_DISTBAR:
  292.         nDistance = nPos;
  293.         break;
  294.  
  295.       case ITEM_INTBAR:
  296.         WinStopTimer( hAB, hWndWhat, TIMER_MOVE );
  297.         nInterval = nPos;
  298.         WinStartTimer( hAB, hWndWhat, TIMER_MOVE, nInterval );
  299.         WinInvalidateRect( hWndWhat, NULL );
  300.         break;
  301.     }
  302. }
  303.  
  304. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  305.  
  306. /*  Terminates the application, freeing up allocated resources.
  307.  *  Note that this function does NOT return to the caller, but
  308.  *  exits the program.
  309.  */
  310.  
  311. VOID SayExitApp( nRet )
  312.     INT         nRet;
  313. {
  314.     if( hWndWhatFrame )
  315.         WinDestroyWindow( hWndWhatFrame );
  316.  
  317.     if( hMsgQ )
  318.         WinDestroyMsgQueue( hMsgQ );
  319.  
  320.     if( hAB )
  321.         WinTerminate( hAB );
  322.  
  323.     exit( nRet );
  324. }
  325.  
  326. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  327.  
  328. /*  Fills a specified rectangle with the background color.
  329.  *  Checks that the rectangle is non-empty first.
  330.  */
  331.  
  332. VOID SayFillRect( hPS, xLeft, xBottom, xRight, xTop )
  333.     HPS         hPS;
  334.     SHORT       xLeft;
  335.     SHORT       xBottom;
  336.     SHORT       xRight;
  337.     SHORT       xTop;
  338.  
  339. {
  340.     RECT        rcFill;
  341.  
  342.     if( xLeft >= xRight  ||  xBottom >= xTop )
  343.         return;
  344.  
  345.     WinSetRect( hAB, &rcFill, xLeft, xBottom, xRight, xTop );    
  346.  
  347.     WinFillRect(
  348.         hPS,
  349.         &rcFill,
  350.         WinQuerySysColor( hAB, SCLR_WINDOW )
  351.     );
  352. }
  353.  
  354. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  355.  
  356. /*  Initializes the application.  */
  357.  
  358. BOOL SayInitApp()
  359. {
  360.     HPS         hPS;
  361.     BOOL        bOK;
  362.  
  363.     hAB = WinInitialize();
  364.     if( ! hAB )
  365.         return FALSE;
  366.  
  367.     hMsgQ = WinCreateMsgQueue( hAB, 0 );
  368.     if( ! hMsgQ )
  369.         return FALSE;
  370.  
  371.     WinLoadString(
  372.         hAB, NULL, STR_NAME,  szAppName, sizeof(szAppName)
  373.     );
  374.     WinLoadString(
  375.         hAB, NULL, STR_TITLE, szTitle,   sizeof(szTitle)
  376.     );
  377.     WinLoadString(
  378.         hAB, NULL, STR_WHAT,  szText,    sizeof(szText)
  379.     );
  380.  
  381.     bOK = WinRegisterClass(
  382.         hAB,
  383.         szAppName,
  384.         (LPFNWP)SayWhatWndProc,
  385.         CS_SYNCPAINT,
  386.         0,
  387.         NULL
  388.     );
  389.     if( ! bOK )
  390.         return FALSE;
  391.  
  392.     hWndWhatFrame = WinCreateStdWindow(
  393.         (HWND)NULL,
  394.         FS_TITLEBAR | FS_SYSMENU |
  395.             FS_MENU | FS_MINMAX | FS_SIZEBORDER,
  396.         szAppName,
  397.         szTitle,
  398.         0L,
  399.         (HMODULE)NULL,
  400.         MENU_WHAT,
  401.         &hWndWhat
  402.     );
  403.  
  404.     if( ! hWndWhatFrame )
  405.         return FALSE;
  406.  
  407.     WinShowWindow( hWndWhat, TRUE );
  408.  
  409.     return TRUE;
  410. }
  411.  
  412. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  413.  
  414. /*  Initializes one scroll bar in the control dialog.  */
  415.  
  416. VOID SayInitBar( hWndDlg, idBar, nValue, nMin, nMax )
  417.     HWND        hWndDlg;
  418.     SHORT       idBar;
  419.     SHORT       nValue;
  420.     SHORT       nMin;
  421.     SHORT       nMax;
  422. {
  423.     HWND        hWndBar;
  424.  
  425.     hWndBar = WinWindowFromID( hWndDlg, idBar );
  426.  
  427.     WinSendDlgItemMsg(
  428.         hWndDlg,
  429.         idBar,
  430.         SBM_SETSCROLLBAR,
  431.         (LONG)nValue,
  432.         MAKELONG( nMin, nMax )
  433.     );
  434.  
  435.     WinSetDlgItemShort( hWndDlg, idBar+1, nValue, FALSE );
  436. }
  437.  
  438. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  439.  
  440. /*  Invalidates the text within the main window, adjusting the
  441.  *  text rectangle if it's gone out of bounds.
  442.  */
  443.  
  444. VOID SayInvalidateText( hWnd )
  445.     HWND        hWnd;
  446. {
  447.     SayLimitTextPos( hWnd );
  448.     WinInvalidateRect( hWnd, &rcText );
  449. }
  450.  
  451. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  452.  
  453. /*  Checks the text position against the window client area
  454.  *  rectangle.  If it's moved off the window in any direction,
  455.  *  forces it back inside, and also reverses the ptAdvance value
  456.  *  for that direction so it will "bounce" off the edge.  Handles
  457.  *  both the iconic and open window cases.
  458.  */
  459.  
  460. VOID SayLimitTextPos( hWnd )
  461.     HWND        hWnd;
  462. {
  463.     RECT        rcClient;
  464.     POINT       ptTextSize;
  465.  
  466.     ptTextSize = ptCharSize;
  467.  
  468.     if( ! bIconic ) {
  469.         npszText = szText;
  470.         ptTextSize.x *= strlen(szText);
  471.     }
  472.  
  473.     WinQueryWindowRect( hWndWhat, &rcClient );
  474.  
  475.     if( rcText.xLeft > rcClient.xRight - ptTextSize.x ) {
  476.         rcText.xLeft = rcClient.xRight - ptTextSize.x;
  477.         ptAdvance.x = -ptAdvance.x;
  478.     }
  479.  
  480.     if( rcText.xLeft < rcClient.xLeft ) {
  481.         rcText.xLeft = rcClient.xLeft;
  482.         ptAdvance.x = -ptAdvance.x;
  483.     }
  484.  
  485.     if( rcText.yBottom < rcClient.yBottom ) {
  486.         rcText.yBottom = rcClient.yBottom;
  487.         ptAdvance.y = -ptAdvance.y;
  488.     }
  489.  
  490.     if( rcText.yBottom > rcClient.yTop - ptTextSize.y ) {
  491.         rcText.yBottom = rcClient.yTop - ptTextSize.y;
  492.         ptAdvance.y = -ptAdvance.y;
  493.     }
  494.  
  495.     rcText.xRight = rcText.xLeft   + ptTextSize.x;
  496.     rcText.yTop   = rcText.yBottom + ptTextSize.y;
  497. }
  498.  
  499. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  500.  
  501. /*  Moves the text within the window, by invalidating the old
  502.  *  position, adjusting rcText according to ptMove, and then
  503.  *  invalidating the new position.
  504.  */
  505.  
  506. VOID SayMoveText( hWnd, ptMove )
  507.     HWND        hWnd;
  508.     POINT       ptMove;
  509. {
  510.     SayInvalidateText( hWnd );
  511.     rcText.xLeft   =
  512.         ptMove.x - (rcText.xRight - rcText.xLeft    >> 1);
  513.     rcText.yBottom =
  514.         ptMove.y - (rcText.yTop   - rcText.yBottom  >> 1);
  515.     SayInvalidateText( hWnd );
  516. }
  517.  
  518. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  519.  
  520. /*  Sets one of the dialog scroll bars to *pnValue.  If that value
  521.  *  is out of range, limits it to the proper range and forces
  522.  *  *pnValue to be within the range as well.
  523.  */
  524.  
  525. VOID SaySetBar( hWndDlg, pnValue, idBar )
  526.     HWND        hWndDlg;
  527.     SHORT *     pnValue;
  528.     SHORT       idBar;
  529. {
  530.     ULONG       lRange;        
  531.     SHORT       nValue;
  532.     BOOL        bOK;
  533.  
  534.     lRange =
  535.         WinSendDlgItemMsg(
  536.             hWndDlg, idBar, SBM_QUERYRANGE, 0L, 0L
  537.         );
  538.  
  539.     nValue =
  540.         WinQueryDlgItemShort( hWndDlg, idBar+1, &bOK, FALSE );
  541.  
  542.     if(
  543.         bOK  &&
  544.         nValue >= LOUSHORT(lRange)  &&
  545.         nValue <= HIUSHORT(lRange)
  546.     ) {
  547.         *pnValue = nValue;
  548.         WinSendDlgItemMsg(
  549.             hWndDlg, idBar, SBM_SETPOS, (LONG)nValue, (LONG)TRUE
  550.         );
  551.     } else {
  552.         WinSetDlgItemShort(
  553.             hWndDlg,
  554.             idBar + 1,
  555.             (INT)WinSendDlgItemMsg(
  556.                 hWndDlg, idBar, SBM_QUERYPOS, 0L, 0L
  557.             ),
  558.             FALSE
  559.         );
  560.     }
  561. }
  562.  
  563. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  564.  
  565. /*  Dialog function for the control panel dialog box.  */
  566.  
  567. ULONG FAR PASCAL SayWhatDlgProc( hWndDlg, wMsg, lParam1, lParam2 )
  568.     HWND        hWndDlg;
  569.     USHORT      wMsg;
  570.     ULONG       lParam1;
  571.     ULONG       lParam2;
  572. {
  573.     HWND        hWndBar;
  574.     RECT        rcWin;
  575.     SHORT       n;
  576.  
  577.     switch( wMsg )
  578.     {
  579.     case WM_COMMAND:
  580.         switch( LOUSHORT(lParam1) )
  581.         {
  582.           case IDOK:
  583.             WinStopTimer( hAB, hWndWhat, TIMER_MOVE );
  584.             WinQueryWindowText(
  585.                 WinWindowFromID( hWndDlg, ITEM_WHAT ),
  586.                 sizeof(szText),
  587.                 szText
  588.             );
  589.             if( strlen(szText) == 0 )
  590.                 WinLoadString(
  591.                     hAB, NULL, STR_WHAT, szText, sizeof(szText)
  592.                 );
  593.             npszText = szText;
  594.             SaySetBar( hWndDlg, &nInterval, ITEM_INTBAR );
  595.             SaySetBar( hWndDlg, &nDistance, ITEM_DISTBAR );
  596.             WinStartTimer( hAB, hWndWhat, TIMER_MOVE, nInterval );
  597.             WinInvalidateRect( hWndWhat, NULL );
  598.             break;
  599.  
  600.           case IDCANCEL:
  601.             WinDestroyWindow( hWndDlg );
  602.             break;
  603.  
  604.           case ITEM_CLEAN:
  605.           case ITEM_FLICKER:
  606.             bCleanPaint = (BOOL)WinSendDlgItemMsg(
  607.                 hWndDlg, ITEM_CLEAN,
  608.                 BM_QUERYCHECK, 0L, 0L
  609.             );
  610.             break;
  611.         }
  612.         break;
  613.  
  614.     case WM_DESTROY:
  615.         hWndPanel = NULL;
  616.         break;
  617.  
  618.     case WM_HSCROLL:
  619.         SayDoBarMsg(
  620.             hWndDlg, LOUSHORT(lParam1),
  621.             HIUSHORT(lParam2), LOUSHORT(lParam2)
  622.         );
  623.         break;
  624.  
  625.     case WM_INITDLG:
  626.         WinQueryWindowRect( hWndDlg, &rcWin );
  627.         WinMapWindowPoints(
  628.             hWndWhat, (HWND)NULL, (LPPOINT)&rcWin.xLeft, 2
  629.         );
  630.         n = rcWin.xRight - ptScreenSize.x + ptCharSize.x;
  631.         if( n > 0 )
  632.             rcWin.xLeft -= n;
  633.         rcWin.xLeft &= ~7;  /* byte align */
  634.         n = rcWin.yTop - ptScreenSize.y + ptCharSize.y;
  635.         if( n > 0 )
  636.             rcWin.yBottom -= n;
  637.         WinSetWindowPos(
  638.             hWndDlg,
  639.             (HWND)NULL,
  640.             rcWin.xLeft,
  641.             rcWin.yBottom,
  642.             0, 0,
  643.             SWP_MOVE
  644.         );
  645.         WinSetWindowText(
  646.             WinWindowFromID( hWndDlg, ITEM_WHAT ), szText
  647.         );
  648.         WinSendDlgItemMsg(
  649.             hWndDlg, ITEM_WHAT, EM_SETTEXTLIMIT,
  650.             (LONG)sizeof(szText)-1, 0L
  651.         );
  652.         SayInitBar(
  653.             hWndDlg, ITEM_INTBAR,  nInterval,
  654.             MIN_INTERVAL, MAX_INTERVAL
  655.         );
  656.         SayInitBar(
  657.             hWndDlg, ITEM_DISTBAR, nDistance,
  658.             MIN_DISTANCE, MAX_DISTANCE
  659.         );
  660.         WinSendDlgItemMsg(
  661.             hWndDlg, ITEM_CLEAN, BM_SETCHECK, (LONG)TRUE, 0L
  662.         );
  663.         break;
  664.     }
  665.     return WinDefDlgProc( hWndDlg, wMsg, lParam1, lParam2 );
  666. }
  667.  
  668. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  669.  
  670. /*  Painting procedure for the main window.  Handles both the
  671.  *  clean and flickery painting methods for demonstration
  672.  *  purposes.
  673.  */
  674.  
  675. VOID SayWhatPaint( hWnd )
  676.     HWND        hWnd;
  677. {
  678.     HPS         hPS;
  679.     RECT        rcPaint;
  680.     GPOINT      gptText;
  681.  
  682.     hPS = WinBeginPaint( hWnd, (HPS)NULL, &rcPaint );
  683.  
  684.     GpiSetColor( hPS, lColor );
  685.  
  686.     SayLimitTextPos( hWnd );
  687.  
  688.     gptText.x = (LONG)rcText.xLeft;
  689.     gptText.y = (LONG)rcText.yBottom;
  690.  
  691.     if( bCleanPaint ) {
  692.  
  693.         /* Clean painting, avoid redundant erasing */
  694.  
  695.         GpiSetBackMix( hPS, MIX_OVERPAINT );
  696.  
  697.         GpiCharStringAt(
  698.             hPS,
  699.             &gptText,
  700.             (LONG)( bIconic ? 1 : strlen(szText) ),
  701.             npszText
  702.         );
  703.  
  704.         SayFillRect(
  705.             hPS,
  706.             rcPaint.xLeft,
  707.             rcPaint.yBottom,
  708.             rcText.xLeft,
  709.             rcPaint.yTop
  710.         );
  711.  
  712.         SayFillRect(
  713.             hPS,
  714.             rcText.xLeft,
  715.             rcText.yTop,
  716.             rcText.xRight,
  717.             rcPaint.yTop
  718.         );
  719.  
  720.         SayFillRect(
  721.             hPS,
  722.             rcText.xLeft,
  723.             rcPaint.yBottom,
  724.             rcText.xRight,
  725.             rcText.yBottom
  726.         );
  727.  
  728.         SayFillRect(
  729.             hPS,
  730.             rcText.xRight,
  731.             rcPaint.yBottom,
  732.             rcPaint.xRight,
  733.             rcPaint.yTop
  734.         );
  735.  
  736.     } else {
  737.         
  738.         /* Flickery painting, erase background and
  739.            paint traditionally */
  740.  
  741.         WinFillRect(
  742.             hPS,
  743.             &rcPaint,
  744.             WinQuerySysColor( hAB, SCLR_WINDOW )
  745.         );
  746.  
  747.         GpiCharStringAt(
  748.             hPS,
  749.             &gptText,
  750.             (LONG)( bIconic ? 1 : strlen(szText) ),
  751.             npszText
  752.         );
  753.     }
  754.  
  755.     WinEndPaint( hPS );
  756.  
  757.     if( ! nInterval )
  758.         SayInvalidateText( hWnd );
  759. }
  760.  
  761. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  762.  
  763. /*  Window function for the main window.  */
  764.  
  765. ULONG FAR PASCAL SayWhatWndProc( hWnd, wMsg, lParam1, lParam2 )
  766.     HWND        hWnd;
  767.     USHORT      wMsg;
  768.     ULONG       lParam1;
  769.     ULONG       lParam2;
  770. {
  771.     POINT       ptMouse;
  772.     GSIZE       gsChar;
  773.     HPS         hPS;
  774.     LONG        ColorData[COLORDATAMAX];
  775.     BOOL        bNowIconic;
  776.  
  777.     switch( wMsg )
  778.     {
  779.       case WM_BUTTON1DOWN:
  780.         if( bMouseDown )
  781.             break;
  782.         WinStopTimer( hAB, hWnd, TIMER_MOVE );
  783.         bMouseDown = TRUE;
  784.         WinSetCapture( hAB, hWnd );
  785.         ptMouse.x = LOUSHORT(lParam1);
  786.         ptMouse.y = HIUSHORT(lParam1);
  787.         SayMoveText( hWnd, ptMouse );
  788.         return 0L;
  789.  
  790.       case WM_BUTTON1UP:
  791.         if( ! bMouseDown )
  792.             break;
  793.         bMouseDown = FALSE;
  794.         WinSetCapture( hAB, (HWND)NULL );
  795.         ptMouse.x = LOUSHORT(lParam1);
  796.         ptMouse.y = HIUSHORT(lParam1);
  797.         SayMoveText( hWnd, ptMouse );
  798.         WinStartTimer( hAB, hWnd, TIMER_MOVE, nInterval );
  799.         return 0L;
  800.  
  801.       case WM_CHAR:
  802.         if(
  803.             ( LOUSHORT(lParam1) & KC_KEYUP )  ||
  804.             ! ( LOUSHORT(lParam1) & KC_VIRTUALKEY )
  805.         ) {
  806.             break;
  807.         }
  808.         SayInvalidateText( hWnd );
  809.         switch( HIUSHORT(lParam2) )
  810.         {
  811.           case VK_LEFT:
  812.             rcText.xLeft -= ptCharSize.x;
  813.             ptAdvance.x   = -1;
  814.             ptAdvance.y   = 0;
  815.             break;
  816.           case VK_RIGHT:
  817.             rcText.xLeft += ptCharSize.x;
  818.             ptAdvance.x   = 1;
  819.             ptAdvance.y   = 0;
  820.             break;
  821.           case VK_UP:
  822.             rcText.yBottom -= ptCharSize.y >> 1;
  823.             ptAdvance.x     = 0;
  824.             ptAdvance.y     = -1;
  825.             break;
  826.           case VK_DOWN:
  827.             rcText.yBottom += ptCharSize.y >> 1;
  828.             ptAdvance.x     = 0;
  829.             ptAdvance.y     = 1;
  830.             break;
  831.           default:
  832.             return 0L;
  833.         }
  834.         SayInvalidateText( hWnd );
  835.         nDistLeft = nDistance;
  836.         return 0L;
  837.  
  838.       case WM_COMMAND:
  839.         switch( LOUSHORT(lParam1) )
  840.         {
  841.           case CMD_ABOUT:
  842.             WinDlgBox(
  843.                 (HWND)NULL, hWnd, (LPFNWP)SayAboutDlgProc,
  844.                 NULL, DLG_ABOUT, NULL
  845.             );
  846.             return 0L;
  847.  
  848.           case CMD_EXIT:
  849.             WinDestroyWindow( hWndWhatFrame );
  850.             return 0L;
  851.  
  852.           case CMD_WHAT:
  853.             if( hWndPanel ) {
  854.                 WinSetWindowPos(
  855.                     hWndPanel,
  856.                     HWND_TOP,
  857.                     0, 0, 0, 0,
  858.                     SWP_ZORDER | SWP_ACTIVATE 
  859.                 );
  860.             } else {
  861.                 hWndPanel = WinLoadDlg(
  862.                     (HWND)NULL,
  863.                     (HWND)NULL,
  864.                     (LPFNWP)SayWhatDlgProc,
  865.                     NULL,
  866.                     DLG_WHAT,
  867.                     NULL
  868.                 );
  869.             }
  870.         }
  871.         return 0L;
  872.  
  873.       case WM_CREATE:
  874.         /* find out character/screen sizes, number of colors */
  875.         hPS = WinGetPS( hWnd );
  876.         GpiQueryCharBox( hPS, &gsChar );
  877.         GpiQueryColorData( hPS, (LONG)COLORDATAMAX, ColorData );
  878.         WinReleasePS( hPS );
  879.         lColorMax = ColorData[3];
  880.         ptCharSize.x = gsChar.width;
  881.         ptCharSize.y = gsChar.height;
  882.         ptScreenSize.x =
  883.           WinQuerySysValue( (HWND)NULL, SV_CXSCREEN );
  884.         ptScreenSize.y =
  885.           WinQuerySysValue( (HWND)NULL, SV_CYSCREEN );
  886.         /* initialize timer */
  887.         srand( (INT)time(NULL) );
  888.         WinStartTimer( hAB, hWnd, TIMER_MOVE, nInterval );
  889.         return 0L;
  890.  
  891.       case WM_DESTROY:
  892.         if( hWndPanel )
  893.             WinDestroyWindow( hWndPanel );
  894.         WinPostQueueMsg( hMsgQ, WM_QUIT, 0L, 0L );
  895.         return 0L;
  896.  
  897.       case WM_ERASEBACKGROUND:
  898.         return 1L;  /* don't erase */
  899.  
  900.       case WM_MINMAX:
  901.         bNowIconic = ( LOUSHORT(lParam1) == SWP_MINIMIZE );
  902.         if( bIconic != bNowIconic ) {
  903.             bIconic = bNowIconic;
  904.             if( bIconic )
  905.                 WinStopTimer( hAB, hWnd, TIMER_CHAR );
  906.             else
  907.                 WinStartTimer( hAB, hWnd, TIMER_CHAR, 1000 );
  908.         }
  909.         return 1L;
  910.  
  911.       case WM_MOUSEMOVE:
  912.         if( bMouseDown ) {
  913.             ptMouse.x = LOUSHORT(lParam1);
  914.             ptMouse.y = HIUSHORT(lParam1);
  915.             SayMoveText( hWnd, ptMouse );
  916.         }
  917.         return 0L;
  918.  
  919.       case WM_PAINT:
  920.         SayWhatPaint( hWnd );
  921.         return 0L;
  922.  
  923.       case WM_SIZE:
  924.         SayInvalidateText( hWnd );
  925.         nDistLeft = 0;
  926.         SayAdvanceTextPos( hWnd );
  927.         return 0L;
  928.  
  929.       case WM_TIMER:
  930.         switch( LOUSHORT(lParam1) ) {
  931.             case TIMER_MOVE:
  932.                 SayAdvanceTextPos( hWnd );
  933.                 break;
  934.             case TIMER_CHAR:
  935.                 SayAdvanceTextChar( hWnd );
  936.                 break;
  937.         }
  938.         return 0L;
  939.     }
  940.     return WinDefWindowProc( hWnd, wMsg, lParam1, lParam2 );
  941. }
  942.  
  943. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  944.  
  945. /*  Main function for the application.  */
  946.  
  947. void cdecl main( nArgs, pArgs )
  948.     INT         nArgs;
  949.     PSZ         pArgs;
  950. {
  951.     QMSG        qMsg;
  952.  
  953.     if( ! SayInitApp() )
  954.         SayExitApp( 1 );
  955.  
  956.     while( WinGetMsg( hAB, &qMsg, (HWND)NULL, 0, 0 ) ) {
  957.  
  958.         if( hWndPanel  &&  WinProcessDlgMsg( hWndPanel, &qMsg ) )
  959.             continue;
  960.  
  961.         WinDispatchMsg( hAB, &qMsg );
  962.     }
  963.  
  964.     SayExitApp( 0 );
  965. }
  966.  
  967. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  968.