home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / S10025.ZIP / SAYWHAT.ALL
Text File  |  1988-03-08  |  59KB  |  2,191 lines

  1. Microsoft Systems Journal
  2. Volume 3; Issue 1; January, 1988
  3.  
  4. Code Listings For:
  5.  
  6.     Saywhat
  7.     pp. 09-30
  8.  
  9. Author(s): Michael Geary
  10. Title:     Converting Windows Applications For Microsoft OS/2 PM
  11.  
  12.  
  13.  
  14. Figure 1w
  15. =========
  16.  
  17.  
  18. #  MAKE file for SAYWHAT (Windows version)
  19.  
  20. sw.obj:  sw.c  sw.h
  21.     cl -c -AS -DLINT_ARGS -Gcsw -Oas -W3 -Zdp sw.c
  22.  
  23. sw.res:  sw.rc  sw.h
  24.     rc -r sw.rc
  25.  
  26. saywhat.exe:  sw.obj  sw.res  sw.def
  27.     link4 sw, saywhat/align:16, saywhat/map/line, slibw, sw.def
  28.     rc sw.res saywhat.exe
  29.     mapsym saywhat
  30.  
  31.  
  32. Figure 3w
  33. =========
  34.  
  35.  
  36. ; SW.DEF - Module definition file for SAYWHAT (Windows version)
  37.  
  38. NAME    SayWhat
  39.  
  40. DESCRIPTION 'Say What!'
  41.  
  42. STUB    'WINSTUB.EXE'
  43.  
  44. CODE    MOVEABLE
  45. DATA    MOVEABLE MULTIPLE
  46.  
  47. HEAPSIZE  128
  48. STACKSIZE 4096
  49.  
  50. EXPORTS
  51.     SayAboutDlgProc     @1
  52.     SayWhatDlgProc      @2
  53.     SayWhatWndProc      @3
  54.  
  55.  
  56. Figure 4w
  57. =========
  58.  
  59.  
  60. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  61.  
  62. #include <style.h>
  63. #include "sw.h"
  64.  
  65. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  66.  
  67. STRINGTABLE
  68. BEGIN
  69.     STR_NAME,    "SayWhat!"
  70.     STR_TITLE,   "Say What!"
  71.     STR_WHAT,    "Hello Windows!"
  72. END
  73.  
  74. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  75.  
  76. SayWhat!    MENU
  77. BEGIN
  78.  
  79.   POPUP "&Say"
  80.   BEGIN
  81.     MENUITEM "&What..."                 , CMD_WHAT
  82.     MENUITEM SEPARATOR
  83.     MENUITEM "E&xit"                    , CMD_EXIT
  84.     MENUITEM SEPARATOR
  85.     MENUITEM "A&bout SayWhat!..."       , CMD_ABOUT
  86.   END
  87.  
  88. END
  89.  
  90. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  91.  
  92. DLG_ABOUT DIALOG 19, 17, 130, 83
  93. STYLE WS_DLGFRAME | WS_POPUP
  94. BEGIN
  95.   CTEXT "Microsoft Windows",   -1,  0,  8, 127,  8
  96.   CTEXT "Say What!",           -1,  0, 18, 127,  8
  97.   CTEXT "Version 1.00",        -1,  0, 30, 127,  8
  98.   CTEXT "By Michael Geary",    -1,  0, 44, 129,  8
  99.   DEFPUSHBUTTON "Ok",        IDOK, 48, 62,  32, 14
  100. END
  101.  
  102. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  103.  
  104. DLG_WHAT DIALOG 49, 41, 177, 103
  105. CAPTION "Say What!"
  106. STYLE WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_POPUP
  107. BEGIN
  108.   CONTROL "Say &What:"   -1,            "Static",    SS_LEFT | WS_GROUP,                                  8, 10,  40,  8
  109.   CONTROL ""             ITEM_WHAT,     "Edit",      ES_LEFT | ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP,  56,  8, 112, 12
  110.   CONTROL "&Time Delay:" -1,            "Static",    SS_LEFT,                                             8, 28,  53,  8
  111.   CONTROL ""             ITEM_INTBAR,   "ScrollBar", SBS_HORZ | WS_TABSTOP,                              56, 28,  88,  8
  112.   CONTROL ""             ITEM_INTERVAL, "Edit",      ES_LEFT | WS_BORDER | WS_TABSTOP,                  152, 26,  16, 12
  113.   CONTROL "&Stability:"  -1,            "Static",    SS_LEFT,                                             8, 46,  56,  8
  114.   CONTROL ""             ITEM_DISTBAR,  "ScrollBar", SBS_HORZ | WS_TABSTOP,                              56, 46,  88,  8
  115.   CONTROL ""             ITEM_DISTANCE, "Edit",      ES_LEFT | WS_BORDER | WS_TABSTOP,                  152, 44,  16, 12
  116.   CONTROL "Painting:"    -1,            "Static",    SS_LEFT,                                             8, 64,  40,  8
  117.   CONTROL "&Clean"       ITEM_CLEAN,    "Button",    BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,         56, 62,  36, 12
  118.   CONTROL "&Flicker"     ITEM_FLICKER,  "Button",    BS_AUTORADIOBUTTON,                                 96, 62,  42, 12
  119.   CONTROL "Enter"        IDOK,          "Button",    BS_DEFPUSHBUTTON | WS_GROUP | WS_TABSTOP,           24, 82,  48, 14
  120.   CONTROL "Esc=Close"    IDCANCEL,      "Button",    BS_PUSHBUTTON | WS_TABSTOP,                        104, 82,  48, 14
  121. END
  122.  
  123. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  124.  
  125.  
  126. Figure 5w
  127. =========
  128.  
  129.  
  130. /* SW.H - C header file for SAYWHAT (Windows version) */
  131.  
  132. /* String table constants */
  133.  
  134. #define STR_NAME        101
  135. #define STR_TITLE       102
  136. #define STR_WHAT        103
  137.  
  138. /* Menu command IDs */
  139.  
  140. #define CMD_ABOUT       201
  141. #define CMD_EXIT        202
  142. #define CMD_WHAT        203
  143.  
  144. /* Dialog box resource IDs */
  145.  
  146. #define DLG_ABOUT       301
  147. #define DLG_WHAT        302
  148.  
  149. /* 'What...' dialog box item IDs */
  150.  
  151. #define ITEM_WHAT       401
  152. #define ITEM_INTBAR     402
  153. #define ITEM_INTERVAL   403
  154. #define ITEM_DISTBAR    404
  155. #define ITEM_DISTANCE   405
  156. #define ITEM_CLEAN      406
  157. #define ITEM_FLICKER    407
  158.  
  159. /* Timer IDs */
  160.  
  161. #define TIMER_MOVE      501
  162. #define TIMER_CHAR      502
  163.  
  164.  
  165. Figure 6w
  166. =========
  167.  
  168.  
  169. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *\
  170.  *  SW.C - C code for SayWhat - Windows version                  * 
  171. \* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  172.  
  173. #ifndef LINT_ARGS
  174. #define LINT_ARGS  /* turn on argument checking for C runtime */
  175. #endif
  176.  
  177. #include <windows.h>
  178. #include <limits.h>
  179. #include <stdlib.h>
  180. #include <string.h>
  181. #include <time.h>
  182. #include "sw.h"
  183.  
  184. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  185.  
  186. #define MIN_INTERVAL    1       /* limits for nInterval */
  187. #define MAX_INTERVAL    999
  188.  
  189. #define MIN_DISTANCE    1       /* limits for nDistance */
  190. #define MAX_DISTANCE    99
  191.  
  192. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  193.  
  194. /*  Static variables  */
  195.  
  196. HANDLE  hInstance;              /* application instance handle */
  197.  
  198. HWND    hWndWhat;               /* main window handle */
  199. HWND    hWndPanel;              /* contral panel dialog handle */
  200.  
  201. FARPROC lpfnDlgProc;            /* ProcInstance for dialog */
  202.  
  203. char    szAppName[10];          /* window class name */
  204. char    szTitle[15];            /* main window title */
  205.  
  206. char    szText[40];             /* current "what" text */
  207. NPSTR   pText = szText;         /* ptr into szText for icon mode */
  208. char    cBlank = ' ';           /* a blank we can point to */
  209.  
  210. RECT    rcText;                 /* current text rectangle */
  211. POINT   ptAdvance;              /* increments for SayAdvanceText */
  212. POINT   ptCharSize;             /* X and Y size of a character */
  213. POINT   ptScreenSize;           /* X and Y size of the screen */
  214.  
  215. int     nDisplayPlanes;         /* number of display planes */
  216. DWORD   rgbTextColor;           /* current text color */
  217. HBRUSH  hbrBkgd;                /* brush for erasing background */
  218.  
  219. int     nInterval = 40;         /* current "Interval" setting */
  220. int     nDistance = 30;         /* current "Distance" setting */
  221. int     nDistLeft = 0;          /* change direction when hits 0 */
  222. BOOL    bCleanPaint = TRUE;     /* clean or flickery painting? */
  223. BOOL    bMouseDown = FALSE;     /* is mouse down? */
  224. BOOL    bIconic = FALSE;        /* is main window iconic? */
  225.  
  226. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  227.  
  228. /*  Full prototypes for our functions to get type checking  */
  229.  
  230. BOOL FAR PASCAL SayAboutDlgProc( HWND, unsigned, WORD, LONG );
  231. void            SayAdvanceTextChar( HWND );
  232. void            SayAdvanceTextPos( HWND );
  233. void            SayChangeColor( HWND );
  234. void            SayDoBarMsg( HWND, HWND, WORD, int );
  235. void            SayFillRect( HDC, int, int, int, int );
  236. void            SayInitBar( HWND, int, int, int, int );
  237. BOOL            SayInitApp( HANDLE, int );
  238. void            SayInvalidateText( HWND );
  239. void            SayLimitTextPos( HWND );
  240. void            SayMoveText( HWND, POINT );
  241. void            SaySetBar( HWND, int *, int );
  242. void            SayExitApp( int );
  243. BOOL FAR PASCAL SayWhatDlgProc( HWND, unsigned, WORD, LONG );
  244. void            SayWhatPaint( HWND );
  245. LONG FAR PASCAL SayWhatWndProc( HWND, unsigned, WORD, LONG );
  246. void     PASCAL WinMain( HANDLE, HANDLE, LPSTR, int );
  247.  
  248. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  249.  
  250. /*  Dialog function for the "About" box  */
  251.  
  252. BOOL FAR PASCAL SayAboutDlgProc( hWndDlg, wMsg, wParam, lParam )
  253.     HWND        hWndDlg;
  254.     unsigned    wMsg;
  255.     WORD        wParam;
  256.     LONG        lParam;
  257. {
  258.     switch( wMsg )
  259.     {
  260.       case WM_COMMAND:
  261.         switch( wParam )
  262.         {
  263.           case IDOK:
  264.             EndDialog( hWndDlg, TRUE );
  265.             return TRUE;
  266.         }
  267.     }
  268.     return FALSE;
  269. }
  270.  
  271. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  272.  
  273. /*  Advances to next display character in iconic mode.
  274.  *  Forces in a blank when it reaches the end of string.
  275.  */
  276.  
  277. void SayAdvanceTextChar( hWnd )
  278.     HWND        hWnd;
  279. {
  280.     if( ! bIconic )
  281.         return;
  282.  
  283.     if( pText == &cBlank )
  284.         pText = szText;
  285.     else if( ! *(++pText) )
  286.         pText = &cBlank;
  287.  
  288.     SayChangeColor( hWnd );
  289.     SayInvalidateText( hWnd );
  290. }
  291.  
  292. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  293.  
  294. /*  Advances text position according to ptAdvance.  Decrements
  295.  *  nDistLeft first, and when it reaches zero, sets a new
  296.  *  randomized ptAdvance and nDistLeft, also changes color.
  297.  *  Does nothing if mouse is down, so text will track mouse.
  298.  */
  299.  
  300. void SayAdvanceTextPos( hWnd )
  301.     HWND        hWnd;
  302. {
  303.     int         i;
  304.  
  305.     if( bMouseDown )
  306.         return;
  307.  
  308.     SayInvalidateText( hWnd );
  309.  
  310.     if( nDistLeft-- < 0 ) {
  311.         nDistLeft = rand() % nDistance + 1;
  312.         do {
  313.             i = rand();
  314.             ptAdvance.x = (
  315.                 i < SHRT_MAX/3   ? -1
  316.               : i < SHRT_MAX/3*2 ?  0
  317.               :                     1
  318.             );
  319.             i = rand();
  320.             ptAdvance.y = (
  321.                 i < SHRT_MAX/3   ? -1
  322.               : i < SHRT_MAX/3*2 ?  0
  323.               :                     1
  324.             );
  325.         } while( ptAdvance.x == 0  &&  ptAdvance.y == 0 );
  326.         if( ! bIconic )
  327.             SayChangeColor( hWnd );
  328.     } else {
  329.         rcText.left   += ptAdvance.x;
  330.         rcText.right  += ptAdvance.x;
  331.         rcText.top    += ptAdvance.y;
  332.         rcText.bottom += ptAdvance.y;
  333.     }
  334.  
  335.     SayInvalidateText( hWnd );
  336. }
  337.  
  338. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  339.  
  340. /*  Changes color to a random selection, if color is available.
  341.  *  Forces a color change - if the random selection is the same
  342.  *  as the old one, it tries again.
  343.  */
  344.  
  345. void SayChangeColor( hWnd )
  346.     HWND        hWnd;
  347. {
  348.     HDC         hDC;
  349.     DWORD       rgbNew;
  350.     DWORD       rgbWindow;
  351.  
  352.     if( nDisplayPlanes <= 1 ) {
  353.         rgbTextColor = GetSysColor(COLOR_WINDOWTEXT);
  354.     } else {
  355.         rgbWindow = GetSysColor(COLOR_WINDOW);
  356.         hDC = GetDC( hWnd );
  357.         do {
  358.             rgbNew = GetNearestColor(
  359.                 hDC,
  360.                 MAKELONG( rand(), rand() ) & 0x00FFFFFFL
  361.             );
  362.         } while( rgbNew == rgbWindow || rgbNew == rgbTextColor );
  363.         rgbTextColor = rgbNew;    
  364.         ReleaseDC( hWnd, hDC );
  365.     }
  366. }
  367.  
  368. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  369.  
  370. /*  Handles scroll bar messages from the control dialog box.
  371.  *  Adjusts scroll bar position, taking its limits into account,
  372.  *  copies the scroll bar value into the adjacent edit control,
  373.  *  then sets the nDistance or nInterval variable appropriately.
  374.  */
  375.  
  376. void SayDoBarMsg( hWndDlg, hWndBar, wCode, nThumb )
  377.     HWND        hWndDlg;
  378.     HWND        hWndBar;
  379.     WORD        wCode;
  380.     int         nThumb;
  381. {
  382.     int         nPos;
  383.     int         nOldPos;
  384.     int         nMin;
  385.     int         nMax;
  386.     int         idBar;
  387.  
  388.     idBar = GetWindowWord( hWndBar, GWW_ID );
  389.  
  390.     nOldPos = nPos = GetScrollPos( hWndBar, SB_CTL );
  391.     GetScrollRange( hWndBar, SB_CTL, &nMin, &nMax );
  392.  
  393.     switch( wCode )
  394.     {
  395.         case SB_LINEUP:         --nPos;             break;        
  396.  
  397.         case SB_LINEDOWN:       ++nPos;             break;    
  398.  
  399.         case SB_PAGEUP:         nPos -= 10;         break;    
  400.  
  401.         case SB_PAGEDOWN:       nPos += 10;         break;    
  402.  
  403.         case SB_THUMBPOSITION:
  404.         case SB_THUMBTRACK:     nPos = nThumb;      break;    
  405.  
  406.         case SB_TOP:            nPos = nMin;        break;    
  407.  
  408.         case SB_BOTTOM:         nPos = nMax;        break;    
  409.     }
  410.  
  411.     if( nPos < nMin )
  412.         nPos = nMin;
  413.  
  414.     if( nPos > nMax )
  415.         nPos = nMax;
  416.  
  417.     if( nPos == nOldPos )
  418.         return;
  419.  
  420.     SetScrollPos( hWndBar, SB_CTL, nPos, TRUE );
  421.  
  422.     SetDlgItemInt( hWndDlg, idBar+1, nPos, FALSE );
  423.  
  424.     switch( idBar )
  425.     {
  426.       case ITEM_DISTBAR:
  427.         nDistance = nPos;
  428.         break;
  429.  
  430.       case ITEM_INTBAR:
  431.         KillTimer( hWndWhat, TIMER_MOVE );
  432.         nInterval = nPos;
  433.         SetTimer( hWndWhat, TIMER_MOVE, nInterval, NULL );
  434.         InvalidateRect( hWndWhat, NULL, FALSE );
  435.         break;
  436.     }
  437. }
  438.  
  439. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  440.  
  441. /*  Terminates the application, freeing up allocated resources.
  442.  *  Note that this function does NOT return to the caller, but
  443.  *  exits the program.
  444.  */
  445.  
  446. void SayExitApp( nRet )
  447.     int         nRet;
  448. {
  449.     if( GetModuleUsage(hInstance) == 1 ) {
  450.         DeleteObject( hbrBkgd );
  451.     }
  452.  
  453.     exit( nRet );
  454. }
  455.  
  456. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  457.  
  458. /*  Fills a specified rectangle with the background color.
  459.  *  Checks that the rectangle is non-empty first.
  460.  */
  461.  
  462. void SayFillRect( hDC, nLeft, nTop, nRight, nBottom )
  463.     HDC         hDC;
  464.     int         nLeft;
  465.     int         nTop;
  466.     int         nRight;
  467.     int         nBottom;
  468.  
  469. {
  470.     RECT        rcFill;
  471.  
  472.     if( nLeft >= nRight  ||  nTop >= nBottom )
  473.         return;
  474.  
  475.     SetRect( &rcFill, nLeft, nTop, nRight, nBottom );    
  476.  
  477.     FillRect( hDC, &rcFill, hbrBkgd );
  478. }
  479.  
  480. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  481.  
  482. /*  Initializes the application.  */
  483.  
  484. BOOL SayInitApp( hPrevInstance, nCmdShow )
  485.     HANDLE      hPrevInstance;
  486.     int         nCmdShow;
  487. {
  488.     WNDCLASS    Class;
  489.     HDC         hDC;
  490.     TEXTMETRIC  Metrics;
  491.  
  492.     LoadString(
  493.         hInstance, STR_NAME,  szAppName, sizeof(szAppName)
  494.     );
  495.     LoadString(
  496.         hInstance, STR_TITLE, szTitle,   sizeof(szTitle)
  497.     );
  498.     LoadString(
  499.         hInstance, STR_WHAT,  szText,    sizeof(szText)
  500.     );
  501.  
  502.     hDC = CreateIC( "DISPLAY", NULL, NULL, NULL );
  503.     GetTextMetrics( hDC, &Metrics );
  504.     nDisplayPlanes = GetDeviceCaps( hDC, PLANES );
  505.     DeleteDC( hDC );
  506.  
  507.     ptCharSize.x = Metrics.tmMaxCharWidth;
  508.     ptCharSize.y = Metrics.tmHeight;
  509.  
  510.     ptScreenSize.x = GetSystemMetrics(SM_CXSCREEN);
  511.     ptScreenSize.y = GetSystemMetrics(SM_CYSCREEN);
  512.  
  513.     if( ! hPrevInstance ) {
  514.  
  515.         hbrBkgd = CreateSolidBrush( GetSysColor(COLOR_WINDOW) );
  516.  
  517.         Class.style         = 0; /* CS_HREDRAW | CS_VREDRAW; */
  518.         Class.lpfnWndProc   = SayWhatWndProc;
  519.         Class.cbClsExtra    = 0;
  520.         Class.cbWndExtra    = 0;
  521.         Class.hInstance     = hInstance;
  522.         Class.hIcon         = NULL;
  523.         Class.hCursor       = LoadCursor( NULL, IDC_ARROW );
  524.         Class.hbrBackground = COLOR_WINDOW + 1;
  525.         Class.lpszMenuName  = szAppName;
  526.         Class.lpszClassName = szAppName;
  527.  
  528.         if( ! RegisterClass( &Class ) )
  529.             return FALSE;
  530.  
  531.     } else {
  532.  
  533.         GetInstanceData(
  534.             hPrevInstance, (NPSTR)&hbrBkgd, sizeof(hbrBkgd)
  535.         );
  536.     }
  537.  
  538.     hWndWhat = CreateWindow(
  539.         szAppName,
  540.         szTitle,
  541.         WS_OVERLAPPEDWINDOW,
  542.         CW_USEDEFAULT, 0,
  543.         CW_USEDEFAULT, 0,
  544.         (HWND)NULL,
  545.         (HMENU)NULL,
  546.         hInstance,
  547.         (LPSTR)NULL
  548.     );
  549.  
  550.     if( ! hWndWhat )
  551.         return FALSE;
  552.  
  553.     ShowWindow( hWndWhat, nCmdShow );
  554.     UpdateWindow( hWndWhat );
  555.  
  556.     return TRUE;
  557. }
  558.  
  559. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  560.  
  561. /*  Initializes one scroll bar in the control dialog.  */
  562.  
  563. void SayInitBar( hWndDlg, idBar, nValue, nMin, nMax )
  564.     HWND        hWndDlg;
  565.     int         idBar;
  566.     int         nValue;
  567.     int         nMin;
  568.     int         nMax;
  569. {
  570.     HWND        hWndBar;
  571.  
  572.     hWndBar = GetDlgItem( hWndDlg, idBar );
  573.  
  574.     SetScrollRange( hWndBar, SB_CTL, nMin, nMax, FALSE );
  575.     SetScrollPos( hWndBar, SB_CTL, nValue, FALSE );
  576.  
  577.     SetDlgItemInt( hWndDlg, idBar+1, nValue, FALSE );
  578. }
  579.  
  580. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  581.  
  582. /*  Invalidates the text within the main window, adjusting the
  583.  *  text rectangle if it's gone out of bounds.
  584.  */
  585.  
  586. void SayInvalidateText( hWnd )
  587.     HWND        hWnd;
  588. {
  589.     SayLimitTextPos( hWnd );
  590.     InvalidateRect( hWnd, &rcText, FALSE );
  591. }
  592.  
  593. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  594.  
  595. /*  Checks the text position against the window client area
  596.  *  rectangle.  If it's moved off the window in any direction,
  597.  *  forces it back inside, and also reverses the ptAdvance value
  598.  *  for that direction so it will "bounce" off the edge.  Handles
  599.  *  both the iconic and open window cases.
  600.  */
  601.  
  602. void SayLimitTextPos( hWnd )
  603.     HWND        hWnd;
  604. {
  605.     RECT        rcClient;
  606.     POINT       ptTextSize;
  607.  
  608.     ptTextSize = ptCharSize;
  609.  
  610.     if( ! bIconic ) {
  611.         pText = szText;
  612.         ptTextSize.x *= strlen(szText);
  613.     }
  614.  
  615.     GetClientRect( hWndWhat, &rcClient );
  616.  
  617.     if( rcText.left > rcClient.right - ptTextSize.x ) {
  618.         rcText.left = rcClient.right - ptTextSize.x;
  619.         ptAdvance.x = -ptAdvance.x;
  620.     }
  621.  
  622.     if( rcText.left < rcClient.left ) {
  623.         rcText.left = rcClient.left;
  624.         ptAdvance.x = -ptAdvance.x;
  625.     }
  626.  
  627.     if( rcText.top > rcClient.bottom - ptTextSize.y ) {
  628.         rcText.top = rcClient.bottom - ptTextSize.y;
  629.         ptAdvance.y = -ptAdvance.y;
  630.     }
  631.  
  632.     if( rcText.top < rcClient.top ) {
  633.         rcText.top = rcClient.top;
  634.         ptAdvance.y = -ptAdvance.y;
  635.     }
  636.  
  637.     rcText.right  = rcText.left + ptTextSize.x;
  638.     rcText.bottom = rcText.top  + ptTextSize.y;
  639. }
  640.  
  641. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  642.  
  643. /*  Moves the text within the window, by invalidating the old
  644.  *  position, adjusting rcText according to ptMove, and then
  645.  *  invalidating the new position.
  646.  */
  647.  
  648. void SayMoveText( hWnd, ptMove )
  649.     HWND        hWnd;
  650.     POINT       ptMove;
  651. {
  652.     SayInvalidateText( hWnd );
  653.     rcText.left = ptMove.x - (rcText.right  - rcText.left  >> 1);
  654.     rcText.top  = ptMove.y - (rcText.bottom - rcText.top   >> 1);
  655.     SayInvalidateText( hWnd );
  656. }
  657.  
  658. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  659.  
  660. /*  Sets one of the dialog scroll bars to *pnValue.  If that value
  661.  *  is out of range, limits it to the proper range and forces
  662.  *  *pnValue to be within the range as well.
  663.  */
  664.  
  665. void SaySetBar( hWndDlg, pnValue, idBar )
  666.     HWND        hWndDlg;
  667.     int *       pnValue;
  668.     int         idBar;
  669. {
  670.     HWND        hWndBar;
  671.     int         nMin;
  672.     int         nMax;
  673.     int         nValue;
  674.     BOOL        bOK;
  675.  
  676.     hWndBar = GetDlgItem( hWndDlg, idBar );
  677.  
  678.     GetScrollRange( hWndBar, SB_CTL, &nMin, &nMax );
  679.  
  680.     nValue = GetDlgItemInt( hWndDlg, idBar+1, &bOK, FALSE );
  681.  
  682.     if( bOK  &&  nValue >= nMin  &&  nValue <= nMax ) {
  683.         *pnValue = nValue;
  684.         SetScrollPos( hWndBar, SB_CTL, nValue, TRUE );
  685.     } else {
  686.         SetDlgItemInt(
  687.             hWndDlg,
  688.             idBar+1,
  689.             GetScrollPos( hWndBar, SB_CTL ),
  690.             FALSE
  691.         );
  692.     }
  693. }
  694.  
  695. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  696.  
  697. /*  Dialog function for the control panel dialog box.  */
  698.  
  699. BOOL FAR PASCAL SayWhatDlgProc( hWndDlg, wMsg, wParam, lParam )
  700.     HWND        hWndDlg;
  701.     unsigned    wMsg;
  702.     WORD        wParam;
  703.     LONG        lParam;
  704. {
  705.     HWND        hWndBar;
  706.     RECT        rcWin;
  707.     int         n;
  708.  
  709.     switch( wMsg )
  710.     {
  711.       case WM_COMMAND:
  712.         switch( wParam )
  713.         {
  714.           case IDOK:
  715.             KillTimer( hWndWhat, TIMER_MOVE );
  716.             GetDlgItemText(
  717.                 hWndDlg, ITEM_WHAT, szText, sizeof(szText)
  718.             );
  719.             if( strlen(szText) == 0 )
  720.                 LoadString(
  721.                     hInstance, STR_WHAT, szText, sizeof(szText)
  722.                 );
  723.             pText = szText;
  724.             SaySetBar( hWndDlg, &nInterval, ITEM_INTBAR );
  725.             SaySetBar( hWndDlg, &nDistance, ITEM_DISTBAR );
  726.             SetTimer( hWndWhat, TIMER_MOVE, nInterval, NULL );
  727.             InvalidateRect( hWndWhat, NULL, FALSE );
  728.             return TRUE;
  729.  
  730.           case IDCANCEL:
  731.             DestroyWindow( hWndDlg );
  732.             return TRUE;
  733.  
  734.           case ITEM_CLEAN:
  735.           case ITEM_FLICKER:
  736.             bCleanPaint =
  737.                 IsDlgButtonChecked( hWndDlg, ITEM_CLEAN );
  738.             return TRUE;
  739.         }
  740.         return FALSE;
  741.  
  742.       case WM_HSCROLL:
  743.         if( HIWORD(lParam) )
  744.             SayDoBarMsg(
  745.                 hWndDlg, HIWORD(lParam), wParam, LOWORD(lParam)
  746.             );
  747.         return TRUE;
  748.  
  749.       case WM_INITDIALOG:
  750.         GetWindowRect( hWndDlg, &rcWin );
  751.         ClientToScreen( hWndWhat, (LPPOINT)&rcWin.left );
  752.         ClientToScreen( hWndWhat, (LPPOINT)&rcWin.right );
  753.         n = rcWin.right - ptScreenSize.x + ptCharSize.x;
  754.         if( n > 0 )
  755.             rcWin.left -= n;
  756.         rcWin.left &= ~7;  /* byte align */
  757.         n = rcWin.bottom - ptScreenSize.y + ptCharSize.y;
  758.         if( n > 0 ) 
  759.             rcWin.top -= n;
  760.         SetWindowPos(
  761.             hWndDlg,
  762.             (HWND)NULL,
  763.             rcWin.left,
  764.             rcWin.top,
  765.             0, 0,
  766.             SWP_NOSIZE | SWP_NOZORDER |
  767.                 SWP_NOREDRAW | SWP_NOACTIVATE
  768.         );
  769.         SetDlgItemText( hWndDlg, ITEM_WHAT, szText );
  770.         SendDlgItemMessage(
  771.             hWndDlg, ITEM_WHAT,
  772.             EM_LIMITTEXT, sizeof(szText)-1, 0L
  773.         );
  774.         SayInitBar(
  775.             hWndDlg, ITEM_INTBAR,
  776.             nInterval, MIN_INTERVAL, MAX_INTERVAL
  777.         );
  778.         SayInitBar(
  779.             hWndDlg, ITEM_DISTBAR,
  780.             nDistance, MIN_DISTANCE, MAX_DISTANCE
  781.         );
  782.         CheckDlgButton( hWndDlg, ITEM_CLEAN, TRUE );
  783.         return TRUE;
  784.  
  785.       case WM_NCDESTROY:
  786.         FreeProcInstance( lpfnDlgProc );
  787.         hWndPanel = NULL;
  788.         return FALSE;
  789.     }
  790.     return FALSE;
  791. }
  792.  
  793. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  794.  
  795. /*  Painting procedure for the main window.  Handles both the
  796.  *  clean and flickery painting methods for demonstration
  797.  *  purposes.
  798.  */
  799.  
  800. void SayWhatPaint( hWnd )
  801.     HWND        hWnd;
  802. {
  803.     PAINTSTRUCT ps;
  804.  
  805.     BeginPaint( hWnd, &ps );
  806.  
  807.     SetTextColor( ps.hdc, rgbTextColor );
  808.  
  809.     SayLimitTextPos( hWnd );
  810.  
  811.     if( bCleanPaint ) {
  812.  
  813.         /* Clean painting, avoid redundant erasing */
  814.  
  815.         TextOut(
  816.             ps.hdc,
  817.             rcText.left,
  818.             rcText.top,
  819.             pText,
  820.             bIconic ? 1 : strlen(szText)
  821.         );
  822.  
  823.         SayFillRect(
  824.             ps.hdc,
  825.             ps.rcPaint.left,
  826.             ps.rcPaint.top,
  827.             rcText.left,
  828.             ps.rcPaint.bottom
  829.         );
  830.  
  831.         SayFillRect(
  832.             ps.hdc,
  833.             rcText.left,
  834.             ps.rcPaint.top,
  835.             rcText.right,
  836.             rcText.top
  837.         );
  838.  
  839.         SayFillRect(
  840.             ps.hdc,
  841.             rcText.left,
  842.             rcText.bottom,
  843.             rcText.right,
  844.             ps.rcPaint.bottom
  845.         );
  846.  
  847.         SayFillRect(
  848.             ps.hdc,
  849.             rcText.right,
  850.             ps.rcPaint.top,
  851.             ps.rcPaint.right,
  852.             ps.rcPaint.bottom
  853.         );
  854.  
  855.     } else {
  856.         
  857.         /* Flickery painting, erase background and
  858.            paint traditionally */
  859.  
  860.         FillRect( ps.hdc, &ps.rcPaint, hbrBkgd );
  861.  
  862.         TextOut(
  863.             ps.hdc,
  864.             rcText.left,
  865.             rcText.top,
  866.             pText,
  867.             bIconic ? 1 : strlen(szText)
  868.         );
  869.  
  870.     }
  871.  
  872.     EndPaint( hWnd, &ps );
  873.  
  874.     if( ! nInterval )
  875.         SayAdvanceTextPos( hWnd );
  876. }
  877.  
  878. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  879.  
  880. /*  Window function for the main window.  */
  881.  
  882. LONG FAR PASCAL SayWhatWndProc( hWnd, wMsg, wParam, lParam )
  883.     HWND        hWnd;
  884.     unsigned    wMsg;
  885.     WORD        wParam;
  886.     LONG        lParam;
  887. {
  888.     FARPROC     lpfnAbout;
  889.  
  890.     switch( wMsg )
  891.     {
  892.       case WM_COMMAND:
  893.         switch( wParam )
  894.         {
  895.           case CMD_ABOUT:
  896.             lpfnAbout =
  897.                 MakeProcInstance( SayAboutDlgProc, hInstance );
  898.             DialogBox(
  899.                 hInstance, MAKEINTRESOURCE(DLG_ABOUT),
  900.                 hWnd, lpfnAbout
  901.             );
  902.             FreeProcInstance( lpfnAbout );
  903.             return 0L;
  904.  
  905.           case CMD_EXIT:
  906.             DestroyWindow( hWndWhat );
  907.             return 0L;
  908.  
  909.           case CMD_WHAT:
  910.             if( hWndPanel ) {
  911.                 BringWindowToTop( hWndPanel );
  912.             } else {
  913.                 lpfnDlgProc =
  914.                     MakeProcInstance( SayWhatDlgProc, hInstance );
  915.                 if( ! lpfnDlgProc )
  916.                     return 0L;
  917.                 hWndPanel = CreateDialog(
  918.                     hInstance,
  919.                     MAKEINTRESOURCE(DLG_WHAT),
  920.                     (HWND)NULL,
  921.                     lpfnDlgProc
  922.                 );
  923.                 if( ! hWndPanel )
  924.                     FreeProcInstance( lpfnDlgProc );
  925.             }
  926.         }
  927.         break;
  928.  
  929.       case WM_CREATE:
  930.         srand( (int)time(NULL) );
  931.         SetTimer( hWnd, TIMER_MOVE, nInterval, NULL );
  932.         return 0L;
  933.  
  934.       case WM_DESTROY:
  935.         if( hWndPanel )
  936.             DestroyWindow( hWndPanel );
  937.         PostQuitMessage( 0 );
  938.         return 0L;
  939.  
  940.       case WM_KEYDOWN:
  941.         SayInvalidateText( hWnd );
  942.         switch( wParam )
  943.         {
  944.           case VK_LEFT:
  945.             rcText.left -= ptCharSize.x;
  946.             ptAdvance.x  = -1;
  947.             ptAdvance.y  = 0;
  948.             break;
  949.  
  950.           case VK_RIGHT:
  951.             rcText.left += ptCharSize.x;
  952.             ptAdvance.x  = 1;
  953.             ptAdvance.y  = 0;
  954.             break;
  955.  
  956.           case VK_UP:
  957.             rcText.top  -= ptCharSize.y >> 1;
  958.             ptAdvance.x  = 0;
  959.             ptAdvance.y  = -1;
  960.             break;
  961.  
  962.           case VK_DOWN:
  963.             rcText.top  += ptCharSize.y >> 1;
  964.             ptAdvance.x  = 0;
  965.             ptAdvance.y  = 1;
  966.             break;
  967.  
  968.           default:
  969.             return 0L;
  970.         }
  971.         SayInvalidateText( hWnd );
  972.         nDistLeft = nDistance;
  973.         return 0L;
  974.  
  975.       case WM_LBUTTONDOWN:
  976.         if( bMouseDown )
  977.             break;
  978.         KillTimer( hWnd, TIMER_MOVE );
  979.         bMouseDown = TRUE;
  980.         SetCapture( hWnd );
  981.         SayMoveText( hWnd, MAKEPOINT(lParam) );
  982.         break;
  983.  
  984.       case WM_LBUTTONUP:
  985.         if( ! bMouseDown )
  986.             break;
  987.         bMouseDown = FALSE;
  988.         ReleaseCapture();
  989.         SayMoveText( hWnd, MAKEPOINT(lParam) );
  990.         SetTimer( hWnd, TIMER_MOVE, nInterval, NULL );
  991.         break;
  992.  
  993.       case WM_MOUSEMOVE:
  994.         if( bMouseDown )
  995.             SayMoveText( hWnd, MAKEPOINT(lParam) );
  996.         break;
  997.  
  998.       case WM_PAINT:
  999.         SayWhatPaint( hWnd );
  1000.         return 0L;
  1001.  
  1002.       case WM_SIZE:
  1003.         if( wParam == SIZEICONIC ) {
  1004.             if( ! bIconic )
  1005.                 SetTimer( hWnd, TIMER_CHAR, 1000, NULL );
  1006.             bIconic = TRUE;
  1007.         } else {
  1008.             if( bIconic )
  1009.                 KillTimer( hWnd, TIMER_CHAR );
  1010.             bIconic = FALSE;
  1011.         }
  1012.         SayInvalidateText( hWnd );
  1013.         nDistLeft = 0;
  1014.         SayAdvanceTextPos( hWnd );
  1015.         return 0L;
  1016.  
  1017.       case WM_TIMER:
  1018.         switch( wParam )
  1019.         {
  1020.           case TIMER_MOVE:
  1021.             SayAdvanceTextPos( hWnd );
  1022.             break;
  1023.  
  1024.           case TIMER_CHAR:
  1025.             SayAdvanceTextChar( hWnd );
  1026.             break;
  1027.         }
  1028.         return 0L;
  1029.     }
  1030.     return DefWindowProc( hWnd, wMsg, wParam, lParam );
  1031. }
  1032.  
  1033. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1034.  
  1035. /*  Main function for the application.  */
  1036.  
  1037. void PASCAL WinMain( hInst, hPrevInst, lpszCmdLine, nCmdShow )
  1038.     HANDLE      hInst;
  1039.     HANDLE      hPrevInst;
  1040.     LPSTR       lpszCmdLine;
  1041.     int         nCmdShow;
  1042. {
  1043.     MSG         msg;
  1044.  
  1045.     hInstance = hInst;
  1046.  
  1047.     if( ! SayInitApp( hPrevInst, nCmdShow ) )
  1048.         SayExitApp( 1 );
  1049.  
  1050.     while( GetMessage( &msg, NULL, 0, 0 ) ) {
  1051.  
  1052.         if( hWndPanel  &&  IsDialogMessage( hWndPanel, &msg ) )
  1053.             continue;
  1054.  
  1055.         TranslateMessage( &msg );
  1056.         DispatchMessage( &msg );
  1057.     }
  1058.  
  1059.  
  1060.     SayExitApp( msg.wParam );
  1061. }
  1062.  
  1063. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1064.  
  1065.  
  1066. Figure 1pm
  1067. =========
  1068.  
  1069.  
  1070. #  MAKE file for SAYWHAT (Presentation Manager version)
  1071.  
  1072. swp.obj:    swp.c  swp.h
  1073.     cl -c -AS -DLINT_ARGS -G2csw -Oat -W3 -Zp swp.c
  1074.  
  1075. swp.res:    swp.rc  swp.h
  1076.     rc -r swp.rc
  1077.  
  1078. saywhatp.exe:  swp.obj  swp.res  swp.def
  1079.     link @swp.lnk
  1080.     mapsym saywhat
  1081.     rc swp.res saywhat.exe
  1082.  
  1083.  
  1084.  
  1085. Figure 2pm
  1086. =========
  1087.  
  1088.  
  1089. swp
  1090. saywhatp/align:16
  1091. saywhatp/map/co
  1092. wincalls gpicalls doscalls slibc slibc5
  1093. swp.def
  1094.  
  1095.  
  1096.  
  1097. Figure 3pm
  1098. =========
  1099.  
  1100.  
  1101. ; SWP.DEF - Module definition file for SAYWHAT (PM version)
  1102.  
  1103. NAME    SayWhat
  1104.  
  1105. DESCRIPTION 'Say What!'
  1106.  
  1107. STUB    'OS2STUB.EXE'
  1108.  
  1109. CODE    MOVEABLE
  1110. DATA    MOVEABLE MULTIPLE
  1111.  
  1112. HEAPSIZE  128
  1113. STACKSIZE 8192
  1114.  
  1115. EXPORTS
  1116.     SayAboutDlgProc     @1
  1117.     SayWhatDlgProc      @2
  1118.     SayWhatWndProc      @3
  1119.  
  1120.  
  1121.  
  1122. Figure 4pm
  1123. =========
  1124.  
  1125.  
  1126. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *\
  1127.  *  SWP.RC - Resource file for SayWhat - PM version              *
  1128. \* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1129.  
  1130. #include <os2pm.h>
  1131. #include "swp.h"
  1132.  
  1133. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1134.  
  1135. STRINGTABLE
  1136. {
  1137.   STR_NAME,    "SayWhat!"
  1138.   STR_TITLE,   "Say What!"
  1139.   STR_WHAT,    "Hello Windows!"
  1140. }
  1141.  
  1142. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1143.  
  1144. MENU SayWhat!
  1145. {
  1146.   SUBMENU "~Say",                  -1
  1147.   {
  1148.     MENUITEM "~What...",           CMD_WHAT
  1149.     MENUITEM SEPARATOR
  1150.     MENUITEM "E~xit",              CMD_EXIT
  1151.     MENUITEM SEPARATOR
  1152.     MENUITEM "A~bout SayWhat!...", CMD_ABOUT
  1153.   }
  1154. }
  1155.  
  1156. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1157.  
  1158. DLGTEMPLATE DLG_ABOUT
  1159. {
  1160.   DIALOG "", DLG_ABOUT, 19, 17, 130, 83, FS_DLGBORDER | WS_SAVEBITS | WS_VISIBLE
  1161.   {
  1162.     CTEXT "Microsoft Windows",   -1,  0, 67, 127,  8
  1163.     CTEXT "Say What!",           -1,  0, 57, 127,  8
  1164.     CTEXT "Version 1.00",        -1,  0, 45, 127,  8
  1165.     CTEXT "By Michael Geary",    -1,  0, 31, 129,  8
  1166.     DEFPUSHBUTTON "Ok",        IDOK, 48,  7,  32, 14
  1167.   }
  1168. }
  1169.  
  1170. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1171.  
  1172. DLGTEMPLATE DLG_WHAT
  1173. {
  1174.   DIALOG "Say What!", DLG_WHAT, 49, 41, 177, 103, FS_TITLEBAR | FS_SYSMENU | FS_BORDER | WS_VISIBLE
  1175.   {
  1176.     CONTROL "Say ~What:"   -1,              8, 85,  40,  8, WC_STATIC,    SS_LEFT | WS_GROUP
  1177.     CONTROL ""             ITEM_WHAT,      56, 83, 112, 12, WC_EDIT,      ES_LEFT | ES_AUTOSCROLL | ES_MARGIN | WS_TABSTOP
  1178.     CONTROL "~Time Delay:" -1,              8, 66,  53,  9, WC_STATIC,    SS_LEFT
  1179.     CONTROL ""             ITEM_INTBAR,    56, 67,  88,  8, WC_SCROLLBAR, SBS_HORZ | WS_TABSTOP
  1180.     CONTROL ""             ITEM_INTERVAL, 152, 65,  16, 12, WC_EDIT,      ES_LEFT | ES_MARGIN | WS_TABSTOP
  1181.     CONTROL "~Stability:"  -1,              8, 48,  56,  9, WC_STATIC,    SS_LEFT
  1182.     CONTROL ""             ITEM_DISTBAR,   56, 49,  88,  8, WC_SCROLLBAR, SBS_HORZ | WS_TABSTOP
  1183.     CONTROL ""             ITEM_DISTANCE, 152, 47,  16, 12, WC_EDIT,      ES_LEFT | ES_MARGIN | WS_TABSTOP
  1184.     CONTROL "Painting:"    -1,              8, 31,  40,  8, WC_STATIC,    SS_LEFT
  1185.     CONTROL "~Clean"       ITEM_CLEAN,     56, 29,  36, 12, WC_BUTTON,    BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP
  1186.     CONTROL "~Flicker"     ITEM_FLICKER,   96, 29,  42, 12, WC_BUTTON,    BS_AUTORADIOBUTTON
  1187.     CONTROL "Enter"        IDOK,           24,  7,  48, 14, WC_BUTTON,    BS_DEFPUSHBUTTON | WS_GROUP | WS_TABSTOP
  1188.     CONTROL "Esc=Close"    IDCANCEL,      104,  7,  48, 14, WC_BUTTON,    BS_PUSHBUTTON | WS_TABSTOP
  1189.   }
  1190. }
  1191.  
  1192. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1193.  
  1194.  
  1195. Figure 5pm
  1196. =========
  1197.  
  1198.  
  1199. /* SWP.H - C header file for SAYWHAT (PM version) */
  1200.  
  1201. /* String table constants */
  1202.  
  1203. #define STR_NAME        101
  1204. #define STR_TITLE       102
  1205. #define STR_WHAT        103
  1206.  
  1207. /* Menu command IDs */
  1208.  
  1209. #define CMD_ABOUT       201
  1210. #define CMD_EXIT        202
  1211. #define CMD_WHAT        203
  1212.  
  1213. /* Dialog box resource IDs */
  1214.  
  1215. #define DLG_ABOUT       301
  1216. #define DLG_WHAT        302
  1217.  
  1218. /* 'What...' dialog box item IDs */
  1219.  
  1220. #define ITEM_WHAT       401
  1221. #define ITEM_INTBAR     402
  1222. #define ITEM_INTERVAL   403
  1223. #define ITEM_DISTBAR    404
  1224. #define ITEM_DISTANCE   405
  1225. #define ITEM_CLEAN      406
  1226. #define ITEM_FLICKER    407
  1227.  
  1228. /* Timer IDs */
  1229.  
  1230. #define TIMER_MOVE      501
  1231. #define TIMER_CHAR      502
  1232.  
  1233. /* Menu resource IDs */
  1234.  
  1235. #define MENU_WHAT       601
  1236.  
  1237.  
  1238. Figure 6pm
  1239. =========
  1240.  
  1241.  
  1242. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *\
  1243.  *  SWP.C - C code for SayWhat - Presentation Manager version    *
  1244. \* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1245.  
  1246. #ifndef LINT_ARGS
  1247. #define LINT_ARGS  /* turn on argument checking for C runtime */
  1248. #endif
  1249.  
  1250. #include <os2pm.h>
  1251. #include <limits.h>
  1252. #include <stdlib.h>
  1253. #include <string.h>
  1254. #include <time.h>
  1255. #include "swp.h"
  1256.  
  1257. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1258.  
  1259. #define MIN_INTERVAL    1       /* limits for nInterval */
  1260. #define MAX_INTERVAL    999
  1261.  
  1262. #define MIN_DISTANCE    1       /* limits for nDistance */
  1263. #define MAX_DISTANCE    99
  1264.  
  1265. #define COLORDATAMAX    5
  1266.  
  1267. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1268.  
  1269. /*  Static variables  */
  1270.  
  1271. HAB     hAB = NULL;             /* anchor block handle */
  1272. HMQ     hMsgQ = NULL;           /* message queue handle */
  1273.  
  1274. HWND    hWndWhatFrame;          /* frame window handle */
  1275. HWND    hWndWhat;               /* client window handle */
  1276. HWND    hWndPanel;              /* control panel dialog handle */
  1277.  
  1278. CHAR    szAppName[10];          /* window class name */
  1279. CHAR    szTitle[15];            /* main window title */
  1280.  
  1281. CHAR    szText[40];             /* current "what" text */
  1282. PSZ     npszText = szText;      /* ptr into szText for icon mode */
  1283. CHAR    cBlank = ' ';           /* a blank we can point to */
  1284.  
  1285. RECT    rcText;                 /* current text rectangle */
  1286. POINT   ptAdvance;              /* increments for SayAdvanceText */
  1287. POINT   ptCharSize;             /* X and Y size of a character */
  1288. POINT   ptScreenSize;           /* X and Y size of the screen */
  1289.  
  1290. LONG    lColorMax;              /* number of available colors */
  1291. LONG    lColor;                 /* current text color index */
  1292.  
  1293. SHORT   nInterval = 40;         /* current "Interval" setting */
  1294. SHORT   nDistance = 30;         /* current "Distance" setting */
  1295. SHORT   nDistLeft = 0;          /* change direction when hits 0 */
  1296. BOOL    bCleanPaint = TRUE;     /* clean or flickery painting? */
  1297. BOOL    bMouseDown = FALSE;     /* is mouse down? */
  1298. BOOL    bIconic = FALSE;        /* is main window iconic? */
  1299.  
  1300. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1301.  
  1302. /*  Full prototypes for our functions to get type checking  */
  1303.  
  1304. ULONG FAR PASCAL SayAboutDlgProc( HWND, USHORT, ULONG, ULONG );
  1305. VOID             SayAdvanceTextChar( HWND );
  1306. VOID             SayAdvanceTextPos( HWND );
  1307. VOID             SayChangeColor( HWND );
  1308. VOID             SayDoBarMsg( HWND, USHORT, USHORT, SHORT );
  1309. VOID             SayExitApp( INT );
  1310. VOID             SayFillRect( HPS, SHORT, SHORT, SHORT, SHORT );
  1311. VOID             SayInitBar( HWND, SHORT, SHORT, SHORT, SHORT );
  1312. BOOL             SayInitApp( VOID );
  1313. VOID             SayInvalidateText( HWND );
  1314. VOID             SayLimitTextPos( HWND );
  1315. VOID             SayMoveText( HWND, POINT );
  1316. VOID             SaySetBar( HWND, SHORT *, SHORT );
  1317. ULONG FAR PASCAL SayWhatDlgProc( HWND, USHORT, ULONG, ULONG );
  1318. VOID             SayWhatPaint( HWND );
  1319. ULONG FAR PASCAL SayWhatWndProc( HWND, USHORT, ULONG, ULONG );
  1320. void  cdecl      main( INT, PSZ );
  1321.  
  1322. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1323.  
  1324. /*  Dialog function for the "About" box  */
  1325.  
  1326. ULONG FAR PASCAL SayAboutDlgProc( hWndDlg, wMsg, lParam1, lParam2 )
  1327.     HWND        hWndDlg;
  1328.     USHORT      wMsg;
  1329.     ULONG       lParam1;
  1330.     ULONG       lParam2;
  1331. {
  1332.     switch( wMsg )
  1333.     {
  1334.       case WM_COMMAND:
  1335.         switch( LOUSHORT(lParam1) )
  1336.         {
  1337.           case IDOK:
  1338.             WinDismissDlg( hWndDlg, TRUE );
  1339.             break;
  1340.         }
  1341.     }
  1342.     return WinDefDlgProc( hWndDlg, wMsg, lParam1, lParam2 );
  1343. }
  1344.  
  1345. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1346.  
  1347. /*  Advances to next display character in iconic mode.
  1348.  *  Forces in a blank when it reaches the end of string.
  1349.  */
  1350.  
  1351. VOID SayAdvanceTextChar( hWnd )
  1352.     HWND        hWnd;
  1353. {
  1354.     if( ! bIconic )
  1355.         return;
  1356.  
  1357.     if( npszText == &cBlank )
  1358.         npszText = szText;
  1359.     else if( ! *(++npszText) )
  1360.         npszText = &cBlank;
  1361.  
  1362.     SayChangeColor( hWnd );
  1363.     SayInvalidateText( hWnd );
  1364. }
  1365.  
  1366. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1367.  
  1368. /*  Advances text position according to ptAdvance.  Decrements
  1369.  *  nDistLeft first, and when it reaches zero, sets a new
  1370.  *  randomized ptAdvance and nDistLeft, also changes color.
  1371.  *  Does nothing if mouse is down, so text will track mouse.
  1372.  */
  1373.  
  1374. VOID SayAdvanceTextPos( hWnd )
  1375.     HWND        hWnd;
  1376. {
  1377.     SHORT       i;
  1378.  
  1379.     if( bMouseDown )
  1380.         return;
  1381.  
  1382.     SayInvalidateText( hWnd );
  1383.  
  1384.     if( nDistLeft-- < 0 ) {
  1385.         nDistLeft = rand() % nDistance + 1;
  1386.         do {
  1387.             i = rand();
  1388.             ptAdvance.x = (
  1389.                 i < SHRT_MAX/3   ? -1
  1390.               : i < SHRT_MAX/3*2 ?  0
  1391.               :                     1
  1392.             );
  1393.             i = rand();
  1394.             ptAdvance.y = (
  1395.                 i < SHRT_MAX/3   ? -1
  1396.               : i < SHRT_MAX/3*2 ?  0
  1397.               :                     1
  1398.             );
  1399.         } while( ptAdvance.x == 0  &&  ptAdvance.y == 0 );
  1400.         if( ! bIconic )
  1401.             SayChangeColor( hWnd );
  1402.     } else {
  1403.         rcText.xLeft   += ptAdvance.x;
  1404.         rcText.xRight  += ptAdvance.x;
  1405.         rcText.yTop    += ptAdvance.y;
  1406.         rcText.yBottom += ptAdvance.y;
  1407.     }
  1408.  
  1409.     SayInvalidateText( hWnd );
  1410. }
  1411.  
  1412. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1413.  
  1414. /*  Changes color to a random selection, if color is available.
  1415.  *  Forces a color change - if the random selection is the same
  1416.  *  as the old one, it tries again.
  1417.  */
  1418.  
  1419. VOID SayChangeColor( hWnd )
  1420.     HWND        hWnd;
  1421. {
  1422.     HPS         hPS;
  1423.     LONG        lWindow;
  1424.     LONG        lNew;
  1425.  
  1426.     hPS = WinGetPS( hWnd );
  1427.  
  1428.     if( lColorMax <= 2 ) {
  1429.         lColor = GpiQueryColorIndex(
  1430.             hPS,
  1431.             WinQuerySysColor( hAB, SCLR_WINDOWTEXT ),
  1432.             0L
  1433.         );
  1434.     } else {
  1435.         lWindow = GpiQueryColorIndex(
  1436.             hPS,
  1437.             WinQuerySysColor( hAB, SCLR_WINDOW ),
  1438.             0L
  1439.         );
  1440.         do {
  1441.             lNew = rand() % lColorMax;
  1442.         } while( lNew == lWindow || lNew == lColor );
  1443.         lColor = lNew;
  1444.     }
  1445.  
  1446.     WinReleasePS( hPS );
  1447.  
  1448. }
  1449.  
  1450. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1451.  
  1452. /*  Handles scroll bar messages from the control dialog box.
  1453.  *  Adjusts scroll bar position, taking its limits into account,
  1454.  *  copies the scroll bar value into the adjacent edit control,
  1455.  *  then sets the nDistance or nInterval variable appropriately.
  1456.  */
  1457.  
  1458. VOID SayDoBarMsg( hWndDlg, idBar, wCode, nThumb )
  1459.     HWND        hWndDlg;
  1460.     USHORT      idBar;    
  1461.     USHORT      wCode;
  1462.     SHORT       nThumb;
  1463. {
  1464.     SHORT       nPos;
  1465.     SHORT       nOldPos;
  1466.     ULONG       lRange;
  1467.  
  1468.     nOldPos = nPos =
  1469.         (SHORT)WinSendDlgItemMsg(
  1470.             hWndDlg, idBar, SBM_QUERYPOS, 0L, 0L
  1471.         );
  1472.  
  1473.     lRange =
  1474.         WinSendDlgItemMsg(
  1475.             hWndDlg, idBar, SBM_QUERYRANGE, 0L, 0L
  1476.         );
  1477.  
  1478.     switch( wCode ) {
  1479.  
  1480.         case SB_LINEUP:         --nPos;                   break;        
  1481.  
  1482.         case SB_LINEDOWN:       ++nPos;                   break;    
  1483.  
  1484.         case SB_PAGEUP:         nPos -= 10;               break;    
  1485.  
  1486.         case SB_PAGEDOWN:       nPos += 10;               break;    
  1487.  
  1488.         case SB_THUMBPOSITION:
  1489.         case SB_THUMBTRACK:     nPos = nThumb;            break;    
  1490.  
  1491.         case SB_TOP:            nPos = LOUSHORT(lRange);  break;    
  1492.  
  1493.         case SB_BOTTOM:         nPos = HIUSHORT(lRange);  break;    
  1494.  
  1495.     }
  1496.  
  1497.     if( nPos < LOUSHORT(lRange) )
  1498.         nPos = LOUSHORT(lRange);
  1499.  
  1500.     if( nPos > HIUSHORT(lRange) )
  1501.         nPos = HIUSHORT(lRange);
  1502.  
  1503.     if( nPos == nOldPos )
  1504.         return;
  1505.  
  1506.     WinSendDlgItemMsg(
  1507.         hWndDlg, idBar, SBM_SETPOS, (LONG)nPos, 0L
  1508.     );
  1509.  
  1510.     WinSetDlgItemShort( hWndDlg, idBar+1, nPos, FALSE );
  1511.  
  1512.     switch( idBar )
  1513.     {
  1514.       case ITEM_DISTBAR:
  1515.         nDistance = nPos;
  1516.         break;
  1517.  
  1518.       case ITEM_INTBAR:
  1519.         WinStopTimer( hAB, hWndWhat, TIMER_MOVE );
  1520.         nInterval = nPos;
  1521.         WinStartTimer( hAB, hWndWhat, TIMER_MOVE, nInterval );
  1522.         WinInvalidateRect( hWndWhat, NULL );
  1523.         break;
  1524.     }
  1525. }
  1526.  
  1527. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1528.  
  1529. /*  Terminates the application, freeing up allocated resources.
  1530.  *  Note that this function does NOT return to the caller, but
  1531.  *  exits the program.
  1532.  */
  1533.  
  1534. VOID SayExitApp( nRet )
  1535.     INT         nRet;
  1536. {
  1537.     if( hWndWhatFrame )
  1538.         WinDestroyWindow( hWndWhatFrame );
  1539.  
  1540.     if( hMsgQ )
  1541.         WinDestroyMsgQueue( hMsgQ );
  1542.  
  1543.     if( hAB )
  1544.         WinTerminate( hAB );
  1545.  
  1546.     exit( nRet );
  1547. }
  1548.  
  1549. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1550.  
  1551. /*  Fills a specified rectangle with the background color.
  1552.  *  Checks that the rectangle is non-empty first.
  1553.  */
  1554.  
  1555. VOID SayFillRect( hPS, xLeft, xBottom, xRight, xTop )
  1556.     HPS         hPS;
  1557.     SHORT       xLeft;
  1558.     SHORT       xBottom;
  1559.     SHORT       xRight;
  1560.     SHORT       xTop;
  1561.  
  1562. {
  1563.     RECT        rcFill;
  1564.  
  1565.     if( xLeft >= xRight  ||  xBottom >= xTop )
  1566.         return;
  1567.  
  1568.     WinSetRect( hAB, &rcFill, xLeft, xBottom, xRight, xTop );    
  1569.  
  1570.     WinFillRect(
  1571.         hPS,
  1572.         &rcFill,
  1573.         WinQuerySysColor( hAB, SCLR_WINDOW )
  1574.     );
  1575. }
  1576.  
  1577. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1578.  
  1579. /*  Initializes the application.  */
  1580.  
  1581. BOOL SayInitApp()
  1582. {
  1583.     HPS         hPS;
  1584.     BOOL        bOK;
  1585.  
  1586.     hAB = WinInitialize();
  1587.     if( ! hAB )
  1588.         return FALSE;
  1589.  
  1590.     hMsgQ = WinCreateMsgQueue( hAB, 0 );
  1591.     if( ! hMsgQ )
  1592.         return FALSE;
  1593.  
  1594.     WinLoadString(
  1595.         hAB, NULL, STR_NAME,  szAppName, sizeof(szAppName)
  1596.     );
  1597.     WinLoadString(
  1598.         hAB, NULL, STR_TITLE, szTitle,   sizeof(szTitle)
  1599.     );
  1600.     WinLoadString(
  1601.         hAB, NULL, STR_WHAT,  szText,    sizeof(szText)
  1602.     );
  1603.  
  1604.     bOK = WinRegisterClass(
  1605.         hAB,
  1606.         szAppName,
  1607.         (LPFNWP)SayWhatWndProc,
  1608.         CS_SYNCPAINT,
  1609.         0,
  1610.         NULL
  1611.     );
  1612.     if( ! bOK )
  1613.         return FALSE;
  1614.  
  1615.     hWndWhatFrame = WinCreateStdWindow(
  1616.         (HWND)NULL,
  1617.         FS_TITLEBAR | FS_SYSMENU |
  1618.             FS_MENU | FS_MINMAX | FS_SIZEBORDER,
  1619.         szAppName,
  1620.         szTitle,
  1621.         0L,
  1622.         (HMODULE)NULL,
  1623.         MENU_WHAT,
  1624.         &hWndWhat
  1625.     );
  1626.  
  1627.     if( ! hWndWhatFrame )
  1628.         return FALSE;
  1629.  
  1630.     WinShowWindow( hWndWhat, TRUE );
  1631.  
  1632.     return TRUE;
  1633. }
  1634.  
  1635. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1636.  
  1637. /*  Initializes one scroll bar in the control dialog.  */
  1638.  
  1639. VOID SayInitBar( hWndDlg, idBar, nValue, nMin, nMax )
  1640.     HWND        hWndDlg;
  1641.     SHORT       idBar;
  1642.     SHORT       nValue;
  1643.     SHORT       nMin;
  1644.     SHORT       nMax;
  1645. {
  1646.     HWND        hWndBar;
  1647.  
  1648.     hWndBar = WinWindowFromID( hWndDlg, idBar );
  1649.  
  1650.     WinSendDlgItemMsg(
  1651.         hWndDlg,
  1652.         idBar,
  1653.         SBM_SETSCROLLBAR,
  1654.         (LONG)nValue,
  1655.         MAKELONG( nMin, nMax )
  1656.     );
  1657.  
  1658.     WinSetDlgItemShort( hWndDlg, idBar+1, nValue, FALSE );
  1659. }
  1660.  
  1661. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1662.  
  1663. /*  Invalidates the text within the main window, adjusting the
  1664.  *  text rectangle if it's gone out of bounds.
  1665.  */
  1666.  
  1667. VOID SayInvalidateText( hWnd )
  1668.     HWND        hWnd;
  1669. {
  1670.     SayLimitTextPos( hWnd );
  1671.     WinInvalidateRect( hWnd, &rcText );
  1672. }
  1673.  
  1674. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1675.  
  1676. /*  Checks the text position against the window client area
  1677.  *  rectangle.  If it's moved off the window in any direction,
  1678.  *  forces it back inside, and also reverses the ptAdvance value
  1679.  *  for that direction so it will "bounce" off the edge.  Handles
  1680.  *  both the iconic and open window cases.
  1681.  */
  1682.  
  1683. VOID SayLimitTextPos( hWnd )
  1684.     HWND        hWnd;
  1685. {
  1686.     RECT        rcClient;
  1687.     POINT       ptTextSize;
  1688.  
  1689.     ptTextSize = ptCharSize;
  1690.  
  1691.     if( ! bIconic ) {
  1692.         npszText = szText;
  1693.         ptTextSize.x *= strlen(szText);
  1694.     }
  1695.  
  1696.     WinQueryWindowRect( hWndWhat, &rcClient );
  1697.  
  1698.     if( rcText.xLeft > rcClient.xRight - ptTextSize.x ) {
  1699.         rcText.xLeft = rcClient.xRight - ptTextSize.x;
  1700.         ptAdvance.x = -ptAdvance.x;
  1701.     }
  1702.  
  1703.     if( rcText.xLeft < rcClient.xLeft ) {
  1704.         rcText.xLeft = rcClient.xLeft;
  1705.         ptAdvance.x = -ptAdvance.x;
  1706.     }
  1707.  
  1708.     if( rcText.yBottom < rcClient.yBottom ) {
  1709.         rcText.yBottom = rcClient.yBottom;
  1710.         ptAdvance.y = -ptAdvance.y;
  1711.     }
  1712.  
  1713.     if( rcText.yBottom > rcClient.yTop - ptTextSize.y ) {
  1714.         rcText.yBottom = rcClient.yTop - ptTextSize.y;
  1715.         ptAdvance.y = -ptAdvance.y;
  1716.     }
  1717.  
  1718.     rcText.xRight = rcText.xLeft   + ptTextSize.x;
  1719.     rcText.yTop   = rcText.yBottom + ptTextSize.y;
  1720. }
  1721.  
  1722. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1723.  
  1724. /*  Moves the text within the window, by invalidating the old
  1725.  *  position, adjusting rcText according to ptMove, and then
  1726.  *  invalidating the new position.
  1727.  */
  1728.  
  1729. VOID SayMoveText( hWnd, ptMove )
  1730.     HWND        hWnd;
  1731.     POINT       ptMove;
  1732. {
  1733.     SayInvalidateText( hWnd );
  1734.     rcText.xLeft   =
  1735.         ptMove.x - (rcText.xRight - rcText.xLeft    >> 1);
  1736.     rcText.yBottom =
  1737.         ptMove.y - (rcText.yTop   - rcText.yBottom  >> 1);
  1738.     SayInvalidateText( hWnd );
  1739. }
  1740.  
  1741. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1742.  
  1743. /*  Sets one of the dialog scroll bars to *pnValue.  If that value
  1744.  *  is out of range, limits it to the proper range and forces
  1745.  *  *pnValue to be within the range as well.
  1746.  */
  1747.  
  1748. VOID SaySetBar( hWndDlg, pnValue, idBar )
  1749.     HWND        hWndDlg;
  1750.     SHORT *     pnValue;
  1751.     SHORT       idBar;
  1752. {
  1753.     ULONG       lRange;        
  1754.     SHORT       nValue;
  1755.     BOOL        bOK;
  1756.  
  1757.     lRange =
  1758.         WinSendDlgItemMsg(
  1759.             hWndDlg, idBar, SBM_QUERYRANGE, 0L, 0L
  1760.         );
  1761.  
  1762.     nValue =
  1763.         WinQueryDlgItemShort( hWndDlg, idBar+1, &bOK, FALSE );
  1764.  
  1765.     if(
  1766.         bOK  &&
  1767.         nValue >= LOUSHORT(lRange)  &&
  1768.         nValue <= HIUSHORT(lRange)
  1769.     ) {
  1770.         *pnValue = nValue;
  1771.         WinSendDlgItemMsg(
  1772.             hWndDlg, idBar, SBM_SETPOS, (LONG)nValue, (LONG)TRUE
  1773.         );
  1774.     } else {
  1775.         WinSetDlgItemShort(
  1776.             hWndDlg,
  1777.             idBar + 1,
  1778.             (INT)WinSendDlgItemMsg(
  1779.                 hWndDlg, idBar, SBM_QUERYPOS, 0L, 0L
  1780.             ),
  1781.             FALSE
  1782.         );
  1783.     }
  1784. }
  1785.  
  1786. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1787.  
  1788. /*  Dialog function for the control panel dialog box.  */
  1789.  
  1790. ULONG FAR PASCAL SayWhatDlgProc( hWndDlg, wMsg, lParam1, lParam2 )
  1791.     HWND        hWndDlg;
  1792.     USHORT      wMsg;
  1793.     ULONG       lParam1;
  1794.     ULONG       lParam2;
  1795. {
  1796.     HWND        hWndBar;
  1797.     RECT        rcWin;
  1798.     SHORT       n;
  1799.  
  1800.     switch( wMsg )
  1801.     {
  1802.     case WM_COMMAND:
  1803.         switch( LOUSHORT(lParam1) )
  1804.         {
  1805.           case IDOK:
  1806.             WinStopTimer( hAB, hWndWhat, TIMER_MOVE );
  1807.             WinQueryWindowText(
  1808.                 WinWindowFromID( hWndDlg, ITEM_WHAT ),
  1809.                 sizeof(szText),
  1810.                 szText
  1811.             );
  1812.             if( strlen(szText) == 0 )
  1813.                 WinLoadString(
  1814.                     hAB, NULL, STR_WHAT, szText, sizeof(szText)
  1815.                 );
  1816.             npszText = szText;
  1817.             SaySetBar( hWndDlg, &nInterval, ITEM_INTBAR );
  1818.             SaySetBar( hWndDlg, &nDistance, ITEM_DISTBAR );
  1819.             WinStartTimer( hAB, hWndWhat, TIMER_MOVE, nInterval );
  1820.             WinInvalidateRect( hWndWhat, NULL );
  1821.             break;
  1822.  
  1823.           case IDCANCEL:
  1824.             WinDestroyWindow( hWndDlg );
  1825.             break;
  1826.  
  1827.           case ITEM_CLEAN:
  1828.           case ITEM_FLICKER:
  1829.             bCleanPaint = (BOOL)WinSendDlgItemMsg(
  1830.                 hWndDlg, ITEM_CLEAN,
  1831.                 BM_QUERYCHECK, 0L, 0L
  1832.             );
  1833.             break;
  1834.         }
  1835.         break;
  1836.  
  1837.     case WM_DESTROY:
  1838.         hWndPanel = NULL;
  1839.         break;
  1840.  
  1841.     case WM_HSCROLL:
  1842.         SayDoBarMsg(
  1843.             hWndDlg, LOUSHORT(lParam1),
  1844.             HIUSHORT(lParam2), LOUSHORT(lParam2)
  1845.         );
  1846.         break;
  1847.  
  1848.     case WM_INITDLG:
  1849.         WinQueryWindowRect( hWndDlg, &rcWin );
  1850.         WinMapWindowPoints(
  1851.             hWndWhat, (HWND)NULL, (LPPOINT)&rcWin.xLeft, 2
  1852.         );
  1853.         n = rcWin.xRight - ptScreenSize.x + ptCharSize.x;
  1854.         if( n > 0 )
  1855.             rcWin.xLeft -= n;
  1856.         rcWin.xLeft &= ~7;  /* byte align */
  1857.         n = rcWin.yTop - ptScreenSize.y + ptCharSize.y;
  1858.         if( n > 0 )
  1859.             rcWin.yBottom -= n;
  1860.         WinSetWindowPos(
  1861.             hWndDlg,
  1862.             (HWND)NULL,
  1863.             rcWin.xLeft,
  1864.             rcWin.yBottom,
  1865.             0, 0,
  1866.             SWP_MOVE
  1867.         );
  1868.         WinSetWindowText(
  1869.             WinWindowFromID( hWndDlg, ITEM_WHAT ), szText
  1870.         );
  1871.         WinSendDlgItemMsg(
  1872.             hWndDlg, ITEM_WHAT, EM_SETTEXTLIMIT,
  1873.             (LONG)sizeof(szText)-1, 0L
  1874.         );
  1875.         SayInitBar(
  1876.             hWndDlg, ITEM_INTBAR,  nInterval,
  1877.             MIN_INTERVAL, MAX_INTERVAL
  1878.         );
  1879.         SayInitBar(
  1880.             hWndDlg, ITEM_DISTBAR, nDistance,
  1881.             MIN_DISTANCE, MAX_DISTANCE
  1882.         );
  1883.         WinSendDlgItemMsg(
  1884.             hWndDlg, ITEM_CLEAN, BM_SETCHECK, (LONG)TRUE, 0L
  1885.         );
  1886.         break;
  1887.     }
  1888.     return WinDefDlgProc( hWndDlg, wMsg, lParam1, lParam2 );
  1889. }
  1890.  
  1891. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1892.  
  1893. /*  Painting procedure for the main window.  Handles both the
  1894.  *  clean and flickery painting methods for demonstration
  1895.  *  purposes.
  1896.  */
  1897.  
  1898. VOID SayWhatPaint( hWnd )
  1899.     HWND        hWnd;
  1900. {
  1901.     HPS         hPS;
  1902.     RECT        rcPaint;
  1903.     GPOINT      gptText;
  1904.  
  1905.     hPS = WinBeginPaint( hWnd, (HPS)NULL, &rcPaint );
  1906.  
  1907.     GpiSetColor( hPS, lColor );
  1908.  
  1909.     SayLimitTextPos( hWnd );
  1910.  
  1911.     gptText.x = (LONG)rcText.xLeft;
  1912.     gptText.y = (LONG)rcText.yBottom;
  1913.  
  1914.     if( bCleanPaint ) {
  1915.  
  1916.         /* Clean painting, avoid redundant erasing */
  1917.  
  1918.         GpiSetBackMix( hPS, MIX_OVERPAINT );
  1919.  
  1920.         GpiCharStringAt(
  1921.             hPS,
  1922.             &gptText,
  1923.             (LONG)( bIconic ? 1 : strlen(szText) ),
  1924.             npszText
  1925.         );
  1926.  
  1927.         SayFillRect(
  1928.             hPS,
  1929.             rcPaint.xLeft,
  1930.             rcPaint.yBottom,
  1931.             rcText.xLeft,
  1932.             rcPaint.yTop
  1933.         );
  1934.  
  1935.         SayFillRect(
  1936.             hPS,
  1937.             rcText.xLeft,
  1938.             rcText.yTop,
  1939.             rcText.xRight,
  1940.             rcPaint.yTop
  1941.         );
  1942.  
  1943.         SayFillRect(
  1944.             hPS,
  1945.             rcText.xLeft,
  1946.             rcPaint.yBottom,
  1947.             rcText.xRight,
  1948.             rcText.yBottom
  1949.         );
  1950.  
  1951.         SayFillRect(
  1952.             hPS,
  1953.             rcText.xRight,
  1954.             rcPaint.yBottom,
  1955.             rcPaint.xRight,
  1956.             rcPaint.yTop
  1957.         );
  1958.  
  1959.     } else {
  1960.         
  1961.         /* Flickery painting, erase background and
  1962.            paint traditionally */
  1963.  
  1964.         WinFillRect(
  1965.             hPS,
  1966.             &rcPaint,
  1967.             WinQuerySysColor( hAB, SCLR_WINDOW )
  1968.         );
  1969.  
  1970.         GpiCharStringAt(
  1971.             hPS,
  1972.             &gptText,
  1973.             (LONG)( bIconic ? 1 : strlen(szText) ),
  1974.             npszText
  1975.         );
  1976.     }
  1977.  
  1978.     WinEndPaint( hPS );
  1979.  
  1980.     if( ! nInterval )
  1981.         SayInvalidateText( hWnd );
  1982. }
  1983.  
  1984. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1985.  
  1986. /*  Window function for the main window.  */
  1987.  
  1988. ULONG FAR PASCAL SayWhatWndProc( hWnd, wMsg, lParam1, lParam2 )
  1989.     HWND        hWnd;
  1990.     USHORT      wMsg;
  1991.     ULONG       lParam1;
  1992.     ULONG       lParam2;
  1993. {
  1994.     POINT       ptMouse;
  1995.     GSIZE       gsChar;
  1996.     HPS         hPS;
  1997.     LONG        ColorData[COLORDATAMAX];
  1998.     BOOL        bNowIconic;
  1999.  
  2000.     switch( wMsg )
  2001.     {
  2002.       case WM_BUTTON1DOWN:
  2003.         if( bMouseDown )
  2004.             break;
  2005.         WinStopTimer( hAB, hWnd, TIMER_MOVE );
  2006.         bMouseDown = TRUE;
  2007.         WinSetCapture( hAB, hWnd );
  2008.         ptMouse.x = LOUSHORT(lParam1);
  2009.         ptMouse.y = HIUSHORT(lParam1);
  2010.         SayMoveText( hWnd, ptMouse );
  2011.         return 0L;
  2012.  
  2013.       case WM_BUTTON1UP:
  2014.         if( ! bMouseDown )
  2015.             break;
  2016.         bMouseDown = FALSE;
  2017.         WinSetCapture( hAB, (HWND)NULL );
  2018.         ptMouse.x = LOUSHORT(lParam1);
  2019.         ptMouse.y = HIUSHORT(lParam1);
  2020.         SayMoveText( hWnd, ptMouse );
  2021.         WinStartTimer( hAB, hWnd, TIMER_MOVE, nInterval );
  2022.         return 0L;
  2023.  
  2024.       case WM_CHAR:
  2025.         if(
  2026.             ( LOUSHORT(lParam1) & KC_KEYUP )  ||
  2027.             ! ( LOUSHORT(lParam1) & KC_VIRTUALKEY )
  2028.         ) {
  2029.             break;
  2030.         }
  2031.         SayInvalidateText( hWnd );
  2032.         switch( HIUSHORT(lParam2) )
  2033.         {
  2034.           case VK_LEFT:
  2035.             rcText.xLeft -= ptCharSize.x;
  2036.             ptAdvance.x   = -1;
  2037.             ptAdvance.y   = 0;
  2038.             break;
  2039.           case VK_RIGHT:
  2040.             rcText.xLeft += ptCharSize.x;
  2041.             ptAdvance.x   = 1;
  2042.             ptAdvance.y   = 0;
  2043.             break;
  2044.           case VK_UP:
  2045.             rcText.yBottom -= ptCharSize.y >> 1;
  2046.             ptAdvance.x     = 0;
  2047.             ptAdvance.y     = -1;
  2048.             break;
  2049.           case VK_DOWN:
  2050.             rcText.yBottom += ptCharSize.y >> 1;
  2051.             ptAdvance.x     = 0;
  2052.             ptAdvance.y     = 1;
  2053.             break;
  2054.           default:
  2055.             return 0L;
  2056.         }
  2057.         SayInvalidateText( hWnd );
  2058.         nDistLeft = nDistance;
  2059.         return 0L;
  2060.  
  2061.       case WM_COMMAND:
  2062.         switch( LOUSHORT(lParam1) )
  2063.         {
  2064.           case CMD_ABOUT:
  2065.             WinDlgBox(
  2066.                 (HWND)NULL, hWnd, (LPFNWP)SayAboutDlgProc,
  2067.                 NULL, DLG_ABOUT, NULL
  2068.             );
  2069.             return 0L;
  2070.  
  2071.           case CMD_EXIT:
  2072.             WinDestroyWindow( hWndWhatFrame );
  2073.             return 0L;
  2074.  
  2075.           case CMD_WHAT:
  2076.             if( hWndPanel ) {
  2077.                 WinSetWindowPos(
  2078.                     hWndPanel,
  2079.                     HWND_TOP,
  2080.                     0, 0, 0, 0,
  2081.                     SWP_ZORDER | SWP_ACTIVATE 
  2082.                 );
  2083.             } else {
  2084.                 hWndPanel = WinLoadDlg(
  2085.                     (HWND)NULL,
  2086.                     (HWND)NULL,
  2087.                     (LPFNWP)SayWhatDlgProc,
  2088.                     NULL,
  2089.                     DLG_WHAT,
  2090.                     NULL
  2091.                 );
  2092.             }
  2093.         }
  2094.         return 0L;
  2095.  
  2096.       case WM_CREATE:
  2097.         /* find out character/screen sizes, number of colors */
  2098.         hPS = WinGetPS( hWnd );
  2099.         GpiQueryCharBox( hPS, &gsChar );
  2100.         GpiQueryColorData( hPS, (LONG)COLORDATAMAX, ColorData );
  2101.         WinReleasePS( hPS );
  2102.         lColorMax = ColorData[3];
  2103.         ptCharSize.x = gsChar.width;
  2104.         ptCharSize.y = gsChar.height;
  2105.         ptScreenSize.x =
  2106.           WinQuerySysValue( (HWND)NULL, SV_CXSCREEN );
  2107.         ptScreenSize.y =
  2108.           WinQuerySysValue( (HWND)NULL, SV_CYSCREEN );
  2109.         /* initialize timer */
  2110.         srand( (INT)time(NULL) );
  2111.         WinStartTimer( hAB, hWnd, TIMER_MOVE, nInterval );
  2112.         return 0L;
  2113.  
  2114.       case WM_DESTROY:
  2115.         if( hWndPanel )
  2116.             WinDestroyWindow( hWndPanel );
  2117.         WinPostQueueMsg( hMsgQ, WM_QUIT, 0L, 0L );
  2118.         return 0L;
  2119.  
  2120.       case WM_ERASEBACKGROUND:
  2121.         return 1L;  /* don't erase */
  2122.  
  2123.       case WM_MINMAX:
  2124.         bNowIconic = ( LOUSHORT(lParam1) == SWP_MINIMIZE );
  2125.         if( bIconic != bNowIconic ) {
  2126.             bIconic = bNowIconic;
  2127.             if( bIconic )
  2128.                 WinStopTimer( hAB, hWnd, TIMER_CHAR );
  2129.             else
  2130.                 WinStartTimer( hAB, hWnd, TIMER_CHAR, 1000 );
  2131.         }
  2132.         return 1L;
  2133.  
  2134.       case WM_MOUSEMOVE:
  2135.         if( bMouseDown ) {
  2136.             ptMouse.x = LOUSHORT(lParam1);
  2137.             ptMouse.y = HIUSHORT(lParam1);
  2138.             SayMoveText( hWnd, ptMouse );
  2139.         }
  2140.         return 0L;
  2141.  
  2142.       case WM_PAINT:
  2143.         SayWhatPaint( hWnd );
  2144.         return 0L;
  2145.  
  2146.       case WM_SIZE:
  2147.         SayInvalidateText( hWnd );
  2148.         nDistLeft = 0;
  2149.         SayAdvanceTextPos( hWnd );
  2150.         return 0L;
  2151.  
  2152.       case WM_TIMER:
  2153.         switch( LOUSHORT(lParam1) ) {
  2154.             case TIMER_MOVE:
  2155.                 SayAdvanceTextPos( hWnd );
  2156.                 break;
  2157.             case TIMER_CHAR:
  2158.                 SayAdvanceTextChar( hWnd );
  2159.                 break;
  2160.         }
  2161.         return 0L;
  2162.     }
  2163.     return WinDefWindowProc( hWnd, wMsg, lParam1, lParam2 );
  2164. }
  2165.  
  2166. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2167.  
  2168. /*  Main function for the application.  */
  2169.  
  2170. void cdecl main( nArgs, pArgs )
  2171.     INT         nArgs;
  2172.     PSZ         pArgs;
  2173. {
  2174.     QMSG        qMsg;
  2175.  
  2176.     if( ! SayInitApp() )
  2177.         SayExitApp( 1 );
  2178.  
  2179.     while( WinGetMsg( hAB, &qMsg, (HWND)NULL, 0, 0 ) ) {
  2180.  
  2181.         if( hWndPanel  &&  WinProcessDlgMsg( hWndPanel, &qMsg ) )
  2182.             continue;
  2183.  
  2184.         WinDispatchMsg( hAB, &qMsg );
  2185.     }
  2186.  
  2187.     SayExitApp( 0 );
  2188. }
  2189.  
  2190. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2191.