home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / msj / msjv3_1 / saywhat / sw.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-03-02  |  24.0 KB  |  914 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 6w
  15. =========
  16.  
  17. */
  18.  
  19. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *\
  20.  *  SW.C - C code for SayWhat - Windows version                  * 
  21. \* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  22.  
  23. #ifndef LINT_ARGS
  24. #define LINT_ARGS  /* turn on argument checking for C runtime */
  25. #endif
  26.  
  27. #include <windows.h>
  28. #include <limits.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31. #include <time.h>
  32. #include "sw.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. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  43.  
  44. /*  Static variables  */
  45.  
  46. HANDLE  hInstance;              /* application instance handle */
  47.  
  48. HWND    hWndWhat;               /* main window handle */
  49. HWND    hWndPanel;              /* contral panel dialog handle */
  50.  
  51. FARPROC lpfnDlgProc;            /* ProcInstance for dialog */
  52.  
  53. char    szAppName[10];          /* window class name */
  54. char    szTitle[15];            /* main window title */
  55.  
  56. char    szText[40];             /* current "what" text */
  57. NPSTR   pText = szText;         /* ptr into szText for icon mode */
  58. char    cBlank = ' ';           /* a blank we can point to */
  59.  
  60. RECT    rcText;                 /* current text rectangle */
  61. POINT   ptAdvance;              /* increments for SayAdvanceText */
  62. POINT   ptCharSize;             /* X and Y size of a character */
  63. POINT   ptScreenSize;           /* X and Y size of the screen */
  64.  
  65. int     nDisplayPlanes;         /* number of display planes */
  66. DWORD   rgbTextColor;           /* current text color */
  67. HBRUSH  hbrBkgd;                /* brush for erasing background */
  68.  
  69. int     nInterval = 40;         /* current "Interval" setting */
  70. int     nDistance = 30;         /* current "Distance" setting */
  71. int     nDistLeft = 0;          /* change direction when hits 0 */
  72. BOOL    bCleanPaint = TRUE;     /* clean or flickery painting? */
  73. BOOL    bMouseDown = FALSE;     /* is mouse down? */
  74. BOOL    bIconic = FALSE;        /* is main window iconic? */
  75.  
  76. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  77.  
  78. /*  Full prototypes for our functions to get type checking  */
  79.  
  80. BOOL FAR PASCAL SayAboutDlgProc( HWND, unsigned, WORD, LONG );
  81. void            SayAdvanceTextChar( HWND );
  82. void            SayAdvanceTextPos( HWND );
  83. void            SayChangeColor( HWND );
  84. void            SayDoBarMsg( HWND, HWND, WORD, int );
  85. void            SayFillRect( HDC, int, int, int, int );
  86. void            SayInitBar( HWND, int, int, int, int );
  87. BOOL            SayInitApp( HANDLE, int );
  88. void            SayInvalidateText( HWND );
  89. void            SayLimitTextPos( HWND );
  90. void            SayMoveText( HWND, POINT );
  91. void            SaySetBar( HWND, int *, int );
  92. void            SayExitApp( int );
  93. BOOL FAR PASCAL SayWhatDlgProc( HWND, unsigned, WORD, LONG );
  94. void            SayWhatPaint( HWND );
  95. LONG FAR PASCAL SayWhatWndProc( HWND, unsigned, WORD, LONG );
  96. void     PASCAL WinMain( HANDLE, HANDLE, LPSTR, int );
  97.  
  98. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  99.  
  100. /*  Dialog function for the "About" box  */
  101.  
  102. BOOL FAR PASCAL SayAboutDlgProc( hWndDlg, wMsg, wParam, lParam )
  103.     HWND        hWndDlg;
  104.     unsigned    wMsg;
  105.     WORD        wParam;
  106.     LONG        lParam;
  107. {
  108.     switch( wMsg )
  109.     {
  110.       case WM_COMMAND:
  111.         switch( wParam )
  112.         {
  113.           case IDOK:
  114.             EndDialog( hWndDlg, TRUE );
  115.             return TRUE;
  116.         }
  117.     }
  118.     return FALSE;
  119. }
  120.  
  121. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  122.  
  123. /*  Advances to next display character in iconic mode.
  124.  *  Forces in a blank when it reaches the end of string.
  125.  */
  126.  
  127. void SayAdvanceTextChar( hWnd )
  128.     HWND        hWnd;
  129. {
  130.     if( ! bIconic )
  131.         return;
  132.  
  133.     if( pText == &cBlank )
  134.         pText = szText;
  135.     else if( ! *(++pText) )
  136.         pText = &cBlank;
  137.  
  138.     SayChangeColor( hWnd );
  139.     SayInvalidateText( hWnd );
  140. }
  141.  
  142. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  143.  
  144. /*  Advances text position according to ptAdvance.  Decrements
  145.  *  nDistLeft first, and when it reaches zero, sets a new
  146.  *  randomized ptAdvance and nDistLeft, also changes color.
  147.  *  Does nothing if mouse is down, so text will track mouse.
  148.  */
  149.  
  150. void SayAdvanceTextPos( hWnd )
  151.     HWND        hWnd;
  152. {
  153.     int         i;
  154.  
  155.     if( bMouseDown )
  156.         return;
  157.  
  158.     SayInvalidateText( hWnd );
  159.  
  160.     if( nDistLeft-- < 0 ) {
  161.         nDistLeft = rand() % nDistance + 1;
  162.         do {
  163.             i = rand();
  164.             ptAdvance.x = (
  165.                 i < SHRT_MAX/3   ? -1
  166.               : i < SHRT_MAX/3*2 ?  0
  167.               :                     1
  168.             );
  169.             i = rand();
  170.             ptAdvance.y = (
  171.                 i < SHRT_MAX/3   ? -1
  172.               : i < SHRT_MAX/3*2 ?  0
  173.               :                     1
  174.             );
  175.         } while( ptAdvance.x == 0  &&  ptAdvance.y == 0 );
  176.         if( ! bIconic )
  177.             SayChangeColor( hWnd );
  178.     } else {
  179.         rcText.left   += ptAdvance.x;
  180.         rcText.right  += ptAdvance.x;
  181.         rcText.top    += ptAdvance.y;
  182.         rcText.bottom += ptAdvance.y;
  183.     }
  184.  
  185.     SayInvalidateText( hWnd );
  186. }
  187.  
  188. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  189.  
  190. /*  Changes color to a random selection, if color is available.
  191.  *  Forces a color change - if the random selection is the same
  192.  *  as the old one, it tries again.
  193.  */
  194.  
  195. void SayChangeColor( hWnd )
  196.     HWND        hWnd;
  197. {
  198.     HDC         hDC;
  199.     DWORD       rgbNew;
  200.     DWORD       rgbWindow;
  201.  
  202.     if( nDisplayPlanes <= 1 ) {
  203.         rgbTextColor = GetSysColor(COLOR_WINDOWTEXT);
  204.     } else {
  205.         rgbWindow = GetSysColor(COLOR_WINDOW);
  206.         hDC = GetDC( hWnd );
  207.         do {
  208.             rgbNew = GetNearestColor(
  209.                 hDC,
  210.                 MAKELONG( rand(), rand() ) & 0x00FFFFFFL
  211.             );
  212.         } while( rgbNew == rgbWindow || rgbNew == rgbTextColor );
  213.         rgbTextColor = rgbNew;    
  214.         ReleaseDC( hWnd, hDC );
  215.     }
  216. }
  217.  
  218. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  219.  
  220. /*  Handles scroll bar messages from the control dialog box.
  221.  *  Adjusts scroll bar position, taking its limits into account,
  222.  *  copies the scroll bar value into the adjacent edit control,
  223.  *  then sets the nDistance or nInterval variable appropriately.
  224.  */
  225.  
  226. void SayDoBarMsg( hWndDlg, hWndBar, wCode, nThumb )
  227.     HWND        hWndDlg;
  228.     HWND        hWndBar;
  229.     WORD        wCode;
  230.     int         nThumb;
  231. {
  232.     int         nPos;
  233.     int         nOldPos;
  234.     int         nMin;
  235.     int         nMax;
  236.     int         idBar;
  237.  
  238.     idBar = GetWindowWord( hWndBar, GWW_ID );
  239.  
  240.     nOldPos = nPos = GetScrollPos( hWndBar, SB_CTL );
  241.     GetScrollRange( hWndBar, SB_CTL, &nMin, &nMax );
  242.  
  243.     switch( wCode )
  244.     {
  245.         case SB_LINEUP:         --nPos;             break;        
  246.  
  247.         case SB_LINEDOWN:       ++nPos;             break;    
  248.  
  249.         case SB_PAGEUP:         nPos -= 10;         break;    
  250.  
  251.         case SB_PAGEDOWN:       nPos += 10;         break;    
  252.  
  253.         case SB_THUMBPOSITION:
  254.         case SB_THUMBTRACK:     nPos = nThumb;      break;    
  255.  
  256.         case SB_TOP:            nPos = nMin;        break;    
  257.  
  258.         case SB_BOTTOM:         nPos = nMax;        break;    
  259.     }
  260.  
  261.     if( nPos < nMin )
  262.         nPos = nMin;
  263.  
  264.     if( nPos > nMax )
  265.         nPos = nMax;
  266.  
  267.     if( nPos == nOldPos )
  268.         return;
  269.  
  270.     SetScrollPos( hWndBar, SB_CTL, nPos, TRUE );
  271.  
  272.     SetDlgItemInt( hWndDlg, idBar+1, nPos, FALSE );
  273.  
  274.     switch( idBar )
  275.     {
  276.       case ITEM_DISTBAR:
  277.         nDistance = nPos;
  278.         break;
  279.  
  280.       case ITEM_INTBAR:
  281.         KillTimer( hWndWhat, TIMER_MOVE );
  282.         nInterval = nPos;
  283.         SetTimer( hWndWhat, TIMER_MOVE, nInterval, NULL );
  284.         InvalidateRect( hWndWhat, NULL, FALSE );
  285.         break;
  286.     }
  287. }
  288.  
  289. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  290.  
  291. /*  Terminates the application, freeing up allocated resources.
  292.  *  Note that this function does NOT return to the caller, but
  293.  *  exits the program.
  294.  */
  295.  
  296. void SayExitApp( nRet )
  297.     int         nRet;
  298. {
  299.     if( GetModuleUsage(hInstance) == 1 ) {
  300.         DeleteObject( hbrBkgd );
  301.     }
  302.  
  303.     exit( nRet );
  304. }
  305.  
  306. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  307.  
  308. /*  Fills a specified rectangle with the background color.
  309.  *  Checks that the rectangle is non-empty first.
  310.  */
  311.  
  312. void SayFillRect( hDC, nLeft, nTop, nRight, nBottom )
  313.     HDC         hDC;
  314.     int         nLeft;
  315.     int         nTop;
  316.     int         nRight;
  317.     int         nBottom;
  318.  
  319. {
  320.     RECT        rcFill;
  321.  
  322.     if( nLeft >= nRight  ||  nTop >= nBottom )
  323.         return;
  324.  
  325.     SetRect( &rcFill, nLeft, nTop, nRight, nBottom );    
  326.  
  327.     FillRect( hDC, &rcFill, hbrBkgd );
  328. }
  329.  
  330. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  331.  
  332. /*  Initializes the application.  */
  333.  
  334. BOOL SayInitApp( hPrevInstance, nCmdShow )
  335.     HANDLE      hPrevInstance;
  336.     int         nCmdShow;
  337. {
  338.     WNDCLASS    Class;
  339.     HDC         hDC;
  340.     TEXTMETRIC  Metrics;
  341.  
  342.     LoadString(
  343.         hInstance, STR_NAME,  szAppName, sizeof(szAppName)
  344.     );
  345.     LoadString(
  346.         hInstance, STR_TITLE, szTitle,   sizeof(szTitle)
  347.     );
  348.     LoadString(
  349.         hInstance, STR_WHAT,  szText,    sizeof(szText)
  350.     );
  351.  
  352.     hDC = CreateIC( "DISPLAY", NULL, NULL, NULL );
  353.     GetTextMetrics( hDC, &Metrics );
  354.     nDisplayPlanes = GetDeviceCaps( hDC, PLANES );
  355.     DeleteDC( hDC );
  356.  
  357.     ptCharSize.x = Metrics.tmMaxCharWidth;
  358.     ptCharSize.y = Metrics.tmHeight;
  359.  
  360.     ptScreenSize.x = GetSystemMetrics(SM_CXSCREEN);
  361.     ptScreenSize.y = GetSystemMetrics(SM_CYSCREEN);
  362.  
  363.     if( ! hPrevInstance ) {
  364.  
  365.         hbrBkgd = CreateSolidBrush( GetSysColor(COLOR_WINDOW) );
  366.  
  367.         Class.style         = 0; /* CS_HREDRAW | CS_VREDRAW; */
  368.         Class.lpfnWndProc   = SayWhatWndProc;
  369.         Class.cbClsExtra    = 0;
  370.         Class.cbWndExtra    = 0;
  371.         Class.hInstance     = hInstance;
  372.         Class.hIcon         = NULL;
  373.         Class.hCursor       = LoadCursor( NULL, IDC_ARROW );
  374.         Class.hbrBackground = COLOR_WINDOW + 1;
  375.         Class.lpszMenuName  = szAppName;
  376.         Class.lpszClassName = szAppName;
  377.  
  378.         if( ! RegisterClass( &Class ) )
  379.             return FALSE;
  380.  
  381.     } else {
  382.  
  383.         GetInstanceData(
  384.             hPrevInstance, (NPSTR)&hbrBkgd, sizeof(hbrBkgd)
  385.         );
  386.     }
  387.  
  388.     hWndWhat = CreateWindow(
  389.         szAppName,
  390.         szTitle,
  391.         WS_OVERLAPPEDWINDOW,
  392.         CW_USEDEFAULT, 0,
  393.         CW_USEDEFAULT, 0,
  394.         (HWND)NULL,
  395.         (HMENU)NULL,
  396.         hInstance,
  397.         (LPSTR)NULL
  398.     );
  399.  
  400.     if( ! hWndWhat )
  401.         return FALSE;
  402.  
  403.     ShowWindow( hWndWhat, nCmdShow );
  404.     UpdateWindow( hWndWhat );
  405.  
  406.     return TRUE;
  407. }
  408.  
  409. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  410.  
  411. /*  Initializes one scroll bar in the control dialog.  */
  412.  
  413. void SayInitBar( hWndDlg, idBar, nValue, nMin, nMax )
  414.     HWND        hWndDlg;
  415.     int         idBar;
  416.     int         nValue;
  417.     int         nMin;
  418.     int         nMax;
  419. {
  420.     HWND        hWndBar;
  421.  
  422.     hWndBar = GetDlgItem( hWndDlg, idBar );
  423.  
  424.     SetScrollRange( hWndBar, SB_CTL, nMin, nMax, FALSE );
  425.     SetScrollPos( hWndBar, SB_CTL, nValue, FALSE );
  426.  
  427.     SetDlgItemInt( hWndDlg, idBar+1, nValue, FALSE );
  428. }
  429.  
  430. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  431.  
  432. /*  Invalidates the text within the main window, adjusting the
  433.  *  text rectangle if it's gone out of bounds.
  434.  */
  435.  
  436. void SayInvalidateText( hWnd )
  437.     HWND        hWnd;
  438. {
  439.     SayLimitTextPos( hWnd );
  440.     InvalidateRect( hWnd, &rcText, FALSE );
  441. }
  442.  
  443. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  444.  
  445. /*  Checks the text position against the window client area
  446.  *  rectangle.  If it's moved off the window in any direction,
  447.  *  forces it back inside, and also reverses the ptAdvance value
  448.  *  for that direction so it will "bounce" off the edge.  Handles
  449.  *  both the iconic and open window cases.
  450.  */
  451.  
  452. void SayLimitTextPos( hWnd )
  453.     HWND        hWnd;
  454. {
  455.     RECT        rcClient;
  456.     POINT       ptTextSize;
  457.  
  458.     ptTextSize = ptCharSize;
  459.  
  460.     if( ! bIconic ) {
  461.         pText = szText;
  462.         ptTextSize.x *= strlen(szText);
  463.     }
  464.  
  465.     GetClientRect( hWndWhat, &rcClient );
  466.  
  467.     if( rcText.left > rcClient.right - ptTextSize.x ) {
  468.         rcText.left = rcClient.right - ptTextSize.x;
  469.         ptAdvance.x = -ptAdvance.x;
  470.     }
  471.  
  472.     if( rcText.left < rcClient.left ) {
  473.         rcText.left = rcClient.left;
  474.         ptAdvance.x = -ptAdvance.x;
  475.     }
  476.  
  477.     if( rcText.top > rcClient.bottom - ptTextSize.y ) {
  478.         rcText.top = rcClient.bottom - ptTextSize.y;
  479.         ptAdvance.y = -ptAdvance.y;
  480.     }
  481.  
  482.     if( rcText.top < rcClient.top ) {
  483.         rcText.top = rcClient.top;
  484.         ptAdvance.y = -ptAdvance.y;
  485.     }
  486.  
  487.     rcText.right  = rcText.left + ptTextSize.x;
  488.     rcText.bottom = rcText.top  + ptTextSize.y;
  489. }
  490.  
  491. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  492.  
  493. /*  Moves the text within the window, by invalidating the old
  494.  *  position, adjusting rcText according to ptMove, and then
  495.  *  invalidating the new position.
  496.  */
  497.  
  498. void SayMoveText( hWnd, ptMove )
  499.     HWND        hWnd;
  500.     POINT       ptMove;
  501. {
  502.     SayInvalidateText( hWnd );
  503.     rcText.left = ptMove.x - (rcText.right  - rcText.left  >> 1);
  504.     rcText.top  = ptMove.y - (rcText.bottom - rcText.top   >> 1);
  505.     SayInvalidateText( hWnd );
  506. }
  507.  
  508. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  509.  
  510. /*  Sets one of the dialog scroll bars to *pnValue.  If that value
  511.  *  is out of range, limits it to the proper range and forces
  512.  *  *pnValue to be within the range as well.
  513.  */
  514.  
  515. void SaySetBar( hWndDlg, pnValue, idBar )
  516.     HWND        hWndDlg;
  517.     int *       pnValue;
  518.     int         idBar;
  519. {
  520.     HWND        hWndBar;
  521.     int         nMin;
  522.     int         nMax;
  523.     int         nValue;
  524.     BOOL        bOK;
  525.  
  526.     hWndBar = GetDlgItem( hWndDlg, idBar );
  527.  
  528.     GetScrollRange( hWndBar, SB_CTL, &nMin, &nMax );
  529.  
  530.     nValue = GetDlgItemInt( hWndDlg, idBar+1, &bOK, FALSE );
  531.  
  532.     if( bOK  &&  nValue >= nMin  &&  nValue <= nMax ) {
  533.         *pnValue = nValue;
  534.         SetScrollPos( hWndBar, SB_CTL, nValue, TRUE );
  535.     } else {
  536.         SetDlgItemInt(
  537.             hWndDlg,
  538.             idBar+1,
  539.             GetScrollPos( hWndBar, SB_CTL ),
  540.             FALSE
  541.         );
  542.     }
  543. }
  544.  
  545. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  546.  
  547. /*  Dialog function for the control panel dialog box.  */
  548.  
  549. BOOL FAR PASCAL SayWhatDlgProc( hWndDlg, wMsg, wParam, lParam )
  550.     HWND        hWndDlg;
  551.     unsigned    wMsg;
  552.     WORD        wParam;
  553.     LONG        lParam;
  554. {
  555.     HWND        hWndBar;
  556.     RECT        rcWin;
  557.     int         n;
  558.  
  559.     switch( wMsg )
  560.     {
  561.       case WM_COMMAND:
  562.         switch( wParam )
  563.         {
  564.           case IDOK:
  565.             KillTimer( hWndWhat, TIMER_MOVE );
  566.             GetDlgItemText(
  567.                 hWndDlg, ITEM_WHAT, szText, sizeof(szText)
  568.             );
  569.             if( strlen(szText) == 0 )
  570.                 LoadString(
  571.                     hInstance, STR_WHAT, szText, sizeof(szText)
  572.                 );
  573.             pText = szText;
  574.             SaySetBar( hWndDlg, &nInterval, ITEM_INTBAR );
  575.             SaySetBar( hWndDlg, &nDistance, ITEM_DISTBAR );
  576.             SetTimer( hWndWhat, TIMER_MOVE, nInterval, NULL );
  577.             InvalidateRect( hWndWhat, NULL, FALSE );
  578.             return TRUE;
  579.  
  580.           case IDCANCEL:
  581.             DestroyWindow( hWndDlg );
  582.             return TRUE;
  583.  
  584.           case ITEM_CLEAN:
  585.           case ITEM_FLICKER:
  586.             bCleanPaint =
  587.                 IsDlgButtonChecked( hWndDlg, ITEM_CLEAN );
  588.             return TRUE;
  589.         }
  590.         return FALSE;
  591.  
  592.       case WM_HSCROLL:
  593.         if( HIWORD(lParam) )
  594.             SayDoBarMsg(
  595.                 hWndDlg, HIWORD(lParam), wParam, LOWORD(lParam)
  596.             );
  597.         return TRUE;
  598.  
  599.       case WM_INITDIALOG:
  600.         GetWindowRect( hWndDlg, &rcWin );
  601.         ClientToScreen( hWndWhat, (LPPOINT)&rcWin.left );
  602.         ClientToScreen( hWndWhat, (LPPOINT)&rcWin.right );
  603.         n = rcWin.right - ptScreenSize.x + ptCharSize.x;
  604.         if( n > 0 )
  605.             rcWin.left -= n;
  606.         rcWin.left &= ~7;  /* byte align */
  607.         n = rcWin.bottom - ptScreenSize.y + ptCharSize.y;
  608.         if( n > 0 ) 
  609.             rcWin.top -= n;
  610.         SetWindowPos(
  611.             hWndDlg,
  612.             (HWND)NULL,
  613.             rcWin.left,
  614.             rcWin.top,
  615.             0, 0,
  616.             SWP_NOSIZE | SWP_NOZORDER |
  617.                 SWP_NOREDRAW | SWP_NOACTIVATE
  618.         );
  619.         SetDlgItemText( hWndDlg, ITEM_WHAT, szText );
  620.         SendDlgItemMessage(
  621.             hWndDlg, ITEM_WHAT,
  622.             EM_LIMITTEXT, sizeof(szText)-1, 0L
  623.         );
  624.         SayInitBar(
  625.             hWndDlg, ITEM_INTBAR,
  626.             nInterval, MIN_INTERVAL, MAX_INTERVAL
  627.         );
  628.         SayInitBar(
  629.             hWndDlg, ITEM_DISTBAR,
  630.             nDistance, MIN_DISTANCE, MAX_DISTANCE
  631.         );
  632.         CheckDlgButton( hWndDlg, ITEM_CLEAN, TRUE );
  633.         return TRUE;
  634.  
  635.       case WM_NCDESTROY:
  636.         FreeProcInstance( lpfnDlgProc );
  637.         hWndPanel = NULL;
  638.         return FALSE;
  639.     }
  640.     return FALSE;
  641. }
  642.  
  643. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  644.  
  645. /*  Painting procedure for the main window.  Handles both the
  646.  *  clean and flickery painting methods for demonstration
  647.  *  purposes.
  648.  */
  649.  
  650. void SayWhatPaint( hWnd )
  651.     HWND        hWnd;
  652. {
  653.     PAINTSTRUCT ps;
  654.  
  655.     BeginPaint( hWnd, &ps );
  656.  
  657.     SetTextColor( ps.hdc, rgbTextColor );
  658.  
  659.     SayLimitTextPos( hWnd );
  660.  
  661.     if( bCleanPaint ) {
  662.  
  663.         /* Clean painting, avoid redundant erasing */
  664.  
  665.         TextOut(
  666.             ps.hdc,
  667.             rcText.left,
  668.             rcText.top,
  669.             pText,
  670.             bIconic ? 1 : strlen(szText)
  671.         );
  672.  
  673.         SayFillRect(
  674.             ps.hdc,
  675.             ps.rcPaint.left,
  676.             ps.rcPaint.top,
  677.             rcText.left,
  678.             ps.rcPaint.bottom
  679.         );
  680.  
  681.         SayFillRect(
  682.             ps.hdc,
  683.             rcText.left,
  684.             ps.rcPaint.top,
  685.             rcText.right,
  686.             rcText.top
  687.         );
  688.  
  689.         SayFillRect(
  690.             ps.hdc,
  691.             rcText.left,
  692.             rcText.bottom,
  693.             rcText.right,
  694.             ps.rcPaint.bottom
  695.         );
  696.  
  697.         SayFillRect(
  698.             ps.hdc,
  699.             rcText.right,
  700.             ps.rcPaint.top,
  701.             ps.rcPaint.right,
  702.             ps.rcPaint.bottom
  703.         );
  704.  
  705.     } else {
  706.         
  707.         /* Flickery painting, erase background and
  708.            paint traditionally */
  709.  
  710.         FillRect( ps.hdc, &ps.rcPaint, hbrBkgd );
  711.  
  712.         TextOut(
  713.             ps.hdc,
  714.             rcText.left,
  715.             rcText.top,
  716.             pText,
  717.             bIconic ? 1 : strlen(szText)
  718.         );
  719.  
  720.     }
  721.  
  722.     EndPaint( hWnd, &ps );
  723.  
  724.     if( ! nInterval )
  725.         SayAdvanceTextPos( hWnd );
  726. }
  727.  
  728. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  729.  
  730. /*  Window function for the main window.  */
  731.  
  732. LONG FAR PASCAL SayWhatWndProc( hWnd, wMsg, wParam, lParam )
  733.     HWND        hWnd;
  734.     unsigned    wMsg;
  735.     WORD        wParam;
  736.     LONG        lParam;
  737. {
  738.     FARPROC     lpfnAbout;
  739.  
  740.     switch( wMsg )
  741.     {
  742.       case WM_COMMAND:
  743.         switch( wParam )
  744.         {
  745.           case CMD_ABOUT:
  746.             lpfnAbout =
  747.                 MakeProcInstance( SayAboutDlgProc, hInstance );
  748.             DialogBox(
  749.                 hInstance, MAKEINTRESOURCE(DLG_ABOUT),
  750.                 hWnd, lpfnAbout
  751.             );
  752.             FreeProcInstance( lpfnAbout );
  753.             return 0L;
  754.  
  755.           case CMD_EXIT:
  756.             DestroyWindow( hWndWhat );
  757.             return 0L;
  758.  
  759.           case CMD_WHAT:
  760.             if( hWndPanel ) {
  761.                 BringWindowToTop( hWndPanel );
  762.             } else {
  763.                 lpfnDlgProc =
  764.                     MakeProcInstance( SayWhatDlgProc, hInstance );
  765.                 if( ! lpfnDlgProc )
  766.                     return 0L;
  767.                 hWndPanel = CreateDialog(
  768.                     hInstance,
  769.                     MAKEINTRESOURCE(DLG_WHAT),
  770.                     (HWND)NULL,
  771.                     lpfnDlgProc
  772.                 );
  773.                 if( ! hWndPanel )
  774.                     FreeProcInstance( lpfnDlgProc );
  775.             }
  776.         }
  777.         break;
  778.  
  779.       case WM_CREATE:
  780.         srand( (int)time(NULL) );
  781.         SetTimer( hWnd, TIMER_MOVE, nInterval, NULL );
  782.         return 0L;
  783.  
  784.       case WM_DESTROY:
  785.         if( hWndPanel )
  786.             DestroyWindow( hWndPanel );
  787.         PostQuitMessage( 0 );
  788.         return 0L;
  789.  
  790.       case WM_KEYDOWN:
  791.         SayInvalidateText( hWnd );
  792.         switch( wParam )
  793.         {
  794.           case VK_LEFT:
  795.             rcText.left -= ptCharSize.x;
  796.             ptAdvance.x  = -1;
  797.             ptAdvance.y  = 0;
  798.             break;
  799.  
  800.           case VK_RIGHT:
  801.             rcText.left += ptCharSize.x;
  802.             ptAdvance.x  = 1;
  803.             ptAdvance.y  = 0;
  804.             break;
  805.  
  806.           case VK_UP:
  807.             rcText.top  -= ptCharSize.y >> 1;
  808.             ptAdvance.x  = 0;
  809.             ptAdvance.y  = -1;
  810.             break;
  811.  
  812.           case VK_DOWN:
  813.             rcText.top  += ptCharSize.y >> 1;
  814.             ptAdvance.x  = 0;
  815.             ptAdvance.y  = 1;
  816.             break;
  817.  
  818.           default:
  819.             return 0L;
  820.         }
  821.         SayInvalidateText( hWnd );
  822.         nDistLeft = nDistance;
  823.         return 0L;
  824.  
  825.       case WM_LBUTTONDOWN:
  826.         if( bMouseDown )
  827.             break;
  828.         KillTimer( hWnd, TIMER_MOVE );
  829.         bMouseDown = TRUE;
  830.         SetCapture( hWnd );
  831.         SayMoveText( hWnd, MAKEPOINT(lParam) );
  832.         break;
  833.  
  834.       case WM_LBUTTONUP:
  835.         if( ! bMouseDown )
  836.             break;
  837.         bMouseDown = FALSE;
  838.         ReleaseCapture();
  839.         SayMoveText( hWnd, MAKEPOINT(lParam) );
  840.         SetTimer( hWnd, TIMER_MOVE, nInterval, NULL );
  841.         break;
  842.  
  843.       case WM_MOUSEMOVE:
  844.         if( bMouseDown )
  845.             SayMoveText( hWnd, MAKEPOINT(lParam) );
  846.         break;
  847.  
  848.       case WM_PAINT:
  849.         SayWhatPaint( hWnd );
  850.         return 0L;
  851.  
  852.       case WM_SIZE:
  853.         if( wParam == SIZEICONIC ) {
  854.             if( ! bIconic )
  855.                 SetTimer( hWnd, TIMER_CHAR, 1000, NULL );
  856.             bIconic = TRUE;
  857.         } else {
  858.             if( bIconic )
  859.                 KillTimer( hWnd, TIMER_CHAR );
  860.             bIconic = FALSE;
  861.         }
  862.         SayInvalidateText( hWnd );
  863.         nDistLeft = 0;
  864.         SayAdvanceTextPos( hWnd );
  865.         return 0L;
  866.  
  867.       case WM_TIMER:
  868.         switch( wParam )
  869.         {
  870.           case TIMER_MOVE:
  871.             SayAdvanceTextPos( hWnd );
  872.             break;
  873.  
  874.           case TIMER_CHAR:
  875.             SayAdvanceTextChar( hWnd );
  876.             break;
  877.         }
  878.         return 0L;
  879.     }
  880.     return DefWindowProc( hWnd, wMsg, wParam, lParam );
  881. }
  882.  
  883. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  884.  
  885. /*  Main function for the application.  */
  886.  
  887. void PASCAL WinMain( hInst, hPrevInst, lpszCmdLine, nCmdShow )
  888.     HANDLE      hInst;
  889.     HANDLE      hPrevInst;
  890.     LPSTR       lpszCmdLine;
  891.     int         nCmdShow;
  892. {
  893.     MSG         msg;
  894.  
  895.     hInstance = hInst;
  896.  
  897.     if( ! SayInitApp( hPrevInst, nCmdShow ) )
  898.         SayExitApp( 1 );
  899.  
  900.     while( GetMessage( &msg, NULL, 0, 0 ) ) {
  901.  
  902.         if( hWndPanel  &&  IsDialogMessage( hWndPanel, &msg ) )
  903.             continue;
  904.  
  905.         TranslateMessage( &msg );
  906.         DispatchMessage( &msg );
  907.     }
  908.  
  909.  
  910.     SayExitApp( msg.wParam );
  911. }
  912.  
  913. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  914.