home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: InfoMgt / InfoMgt.zip / TIMELI2.ZIP / TIMELINE.C < prev    next >
C/C++ Source or Header  |  1989-04-29  |  38KB  |  787 lines

  1. /*--------------------------------------------------------------------------
  2.   TIMELINE.C -- Time and Date Display for OS/2 Presentation Manager
  3.                 (c) 1989, Ziff Communications Co.
  4.                 PC Magazine * Charles Petzold, November 1988
  5.  
  6.                 Modified March-April 1989 by Eric L. Baatz to add overly
  7.                 verbose comments, use of mouse for display movement, color
  8.                 changes and termination, a help file, use of OS2.INI and
  9.                 parameter recognition.  Those modifications in no way
  10.                 change the ownership of TIMELINE, the holding of any
  11.                 copyrights, or extend or offer any warranties as to the
  12.                 fitness or usefulness of this code, which is presented on
  13.                 an "as is basis" with no warranties.
  14.   --------------------------------------------------------------------------*/
  15.  
  16. #define INCL_WIN
  17. #define INCL_GPI
  18. #include <os2.h>
  19. #include <string.h>
  20. #include <time.h>
  21. #include <stdlib.h>
  22. #include "timeline.h"
  23.  
  24. #define ID_TIMER   1   // Each timer a program starts must have a unique ID
  25. #define MAX_COLORS 16  // The number of entries in the default color
  26.                        // table through which TIMELINE will cycle the
  27.                        // text background color when Button 2 is clicked
  28. // Ignore status bits other than the following when a keystroke is received.
  29. #define VKEY_MASK  (KC_KEYUP | KC_ALT | KC_CTRL | KC_SHIFT | KC_VIRTUALKEY)
  30.  
  31. // Function prototypes for handling communications with the date/time display
  32. // window and the help dialog box.
  33. MRESULT EXPENTRY ClientWndProc(HWND,     // msg result, exported entry,
  34.                                          // handle for window
  35.                                USHORT,   // unsigned short
  36.                                MPARAM,   // message parameter
  37.                                MPARAM);
  38. MRESULT EXPENTRY HelpDlgProc( HWND, USHORT, MPARAM, MPARAM );
  39.  
  40.  
  41.        HAB     hab;                       // handle to an anchor block (an
  42.                                           // area of internal PM resources
  43.                                           // that is allocated per thread
  44.                                           // calling WinInitialize)
  45.        HPS     hps;                       // handle to presentation space
  46.        int     cArg;                      // count of TIMELINE parameters
  47.        char  **ppchArg;                   // pointer to strings that are the parameters
  48. static CHAR   szAppName[]  = "TimeLine2"; // string terminated by a zero
  49.                                           // name of this application and
  50.                                           // its window class
  51. static CHAR   szKeyName1[] = "POSITION";  // keyword for writing into OS2.INI
  52. static CHAR   szKeyName2[] = "COLOR";     // keyword for writing into OS2.INI
  53.  
  54.  
  55.  
  56. /*--------------------------------------------------------------------------
  57.  
  58.  When run from an OS/2 1.1 protected mode prompt or from a file such as
  59.  STARTUP.CMD, TIMELINE displays a small rectangle on the Presentation
  60.  Manager Desktop that shows the current date and time. The display is
  61.  updated every second.
  62.  
  63.  TIMELINE can be terminated by double clicking Mouse Button 1 while the
  64.  cursor is over the display or by pressing F3 or Alt+F4 when the display
  65.  has input focus.
  66.  
  67.  The display can be moved around the PM Desktop by dragging it with Mouse
  68.  Button 1 or by getting input focus to the display, pressing Alt+F7, pressing
  69.  the arrow keys, and pressing Enter (to cause the display to move) or
  70.  Esc (to cancel the move).
  71.  
  72.  The background color of the display can be cycled through the 16 default
  73.  colors by clicking Mouse Button 2 while the cursor is over the display or
  74.  by pressing the up- and down-arrow keys when the display has input focus.
  75.  
  76.  A simple help message is displayed if F1 is pressed while the display has
  77.  input focus.
  78.  
  79.  Whenever the display's background color or position is changed, the new
  80.  value(s) is written to OS2.INI.  If TIMELINE is started with no parameters,
  81.  that is:
  82.  
  83.     TIMELINE
  84.  
  85.  then the display's initial color and position are read from OS2.INI.
  86.  
  87.  TIMELINE can be started with command line parameters in the form:
  88.  
  89.     TIMELINE x y color
  90.  
  91.  where
  92.  
  93.     x      is the initial x coordinate for the bottom, left corner of the
  94.            PM Desktop.  If the value of x is negative or larger than the
  95.            PM's display device, x will be adjusted so TIMELINE's display
  96.            is visible on the Desktop.  (The point 0,0 is the bottom, left
  97.            corner of the Desktop.)
  98.  
  99.     y      is the initial y coordinate for the bottom, left corner of the
  100.            PM Desktop.
  101.  
  102.     color  is one of the following (case can be mixed):
  103.  
  104.            background, blue, red, pink, green, cyan, yellow, neutral,
  105.            darkgray, darkblue, darkred, darkpink, darkgreen, darkcyan,
  106.            brown, palegray
  107.  
  108.  For example:
  109.  
  110.     TIMELINE 451 0 DARKCYAN
  111.  
  112.  which puts the display in the bottom, right corner of a VGA display.
  113.  
  114.  
  115.  The purpose of this program was to provide a platform for my first PM
  116.  learning experience.  It turned out to be both fun and educational.
  117.  This program is offered in the hope that it will help someone else get
  118.  started.
  119.  
  120.  Thanks to Charles Petzold for TIMELINE's initial conception and for his
  121.  many subsequent comments and pointers.
  122.  
  123.  However, any bugs, oversights, and stylistic offenses are entirely mine.
  124.  I welcome feedback on them over CompuServe.
  125.  
  126.  Eric L. Baatz 74010,3664
  127.  
  128.  --------------------------------------------------------------------------*/
  129.  
  130.  
  131.  
  132. int main( int argc, char *argv[] )
  133. {
  134.   static ULONG fulFrameFlags =                   // flags ULONG
  135.                                FCF_BORDER   |    // thin border
  136.                                FCF_TASKLIST;     // add to PM task lst
  137.   HMQ          hmq;                              // handle to message queue
  138.   HWND         hwndFrame, hwndClient;            // handle to window
  139.   QMSG         qmsg;                             // queued message
  140.  
  141.   cArg = argc;       // make visible to other functions
  142.   ppchArg=argv;      // make visible to other functions
  143.  
  144.   
  145.   hab = WinInitialize( (USHORT) 0 );             // parameter must be 0
  146.   hmq = WinCreateMsgQueue( hab, (SHORT) 0 );     // use default queue size
  147.   
  148.   WinRegisterClass(hab,
  149.                    szAppName,                    // class name
  150.                    ClientWndProc,                // window procedure
  151.                    (ULONG) 0,                    // class style
  152.                    (USHORT) 0);                  // extra storage per window
  153.  
  154.   // Create the frame and client windows
  155.   hwndFrame = WinCreateStdWindow(HWND_DESKTOP,   // parent window
  156.                                  WS_VISIBLE,     // frame style
  157.                                  &fulFrameFlags, // frame style flags
  158.                                  szAppName,      // client class name
  159.                                  NULL,           // title bar text
  160.                                  (ULONG) 0,      // client window style
  161.                                  NULL,           // resource identifier
  162.                                  (USHORT) 0,     // frame window identifier
  163.                                  &hwndClient);   // returned client window
  164.                                                  // handle
  165.   
  166.   // Wait for a message.  If message is not WM_QUIT, dispatch it to
  167.   // appropriate window procedure.
  168.   while ( WinGetMsg(hab,
  169.                     &qmsg,                       // returned pointer to msg
  170.                     NULL,                        // window filter
  171.                     (USHORT) 0,                  // first message identity
  172.                     (USHORT) 0)                  // second message identity
  173.         )
  174.     WinDispatchMsg( hab, &qmsg );
  175.  
  176.   WinDestroyWindow( hwndFrame );
  177.   WinDestroyMsgQueue( hmq );
  178.   WinTerminate( hab );
  179.   return( 0 );
  180. }
  181.  
  182.  
  183.  
  184. VOID SizeWindow( HWND hwndFrame, PPOINTL pptlPosition )
  185. {
  186.   static CHAR szDummyTime[] = " Wed May 00 00:00:00 0000 ";  // "W" and "M"
  187.                                                              // used so sizing
  188.                                                              // will be for
  189.                                                              // maximum length
  190.                                                              // of a portion-
  191.                                                              // ally spaced
  192.                                                              // font
  193.   POINTL      aptl[TXTBOX_COUNT];   // array of points (x,y coordinates)
  194.   RECTL       rcl;                  // rectangle structure
  195.   LONG        lDesktopX, lDesktopY;
  196.   
  197.   // Find width and height of text string
  198.   
  199.   GpiQueryTextBox(hps,
  200.                   (LONG) strlen(szDummyTime), // number of chars in string
  201.                   szDummyTime,                // the string
  202.                   TXTBOX_COUNT,               // number of points to return
  203.                                               // starting from TOPLEFT
  204.                   aptl);                      // pointer to POINTLs that
  205.                                               // will contain the relative
  206.                                               // coordinates of the text box
  207.                                               // in world coordinates
  208.   
  209.   // Set up rectangle structure that will hold the text and be completely
  210.   // on the desktop
  211.  
  212.   //  xRight  = width of text box
  213.   rcl.xRight  = aptl[TXTBOX_BOTTOMRIGHT].x - aptl[TXTBOX_BOTTOMLEFT].x;
  214.  
  215.   //  yTop    = height of text box
  216.   rcl.yTop    = aptl[TXTBOX_TOPLEFT].y     - aptl[TXTBOX_BOTTOMLEFT].y;
  217.   
  218.   rcl.xLeft = rcl.yBottom = 0;
  219.   WinCalcFrameRect(hwndFrame,                // calculate size and position
  220.                    &rcl,                     // of frame necessary to display
  221.                    FALSE);                   // text starting at 0,0
  222.   rcl.xRight = rcl.xRight - rcl.xLeft;       // form relative width of frame
  223.   rcl.yTop   = rcl.yTop   - rcl.yBottom;     // form relative height of frame
  224.  
  225.   // Get x dimension of the screen (desktop).
  226.   if ( (lDesktopX = WinQuerySysValue( HWND_DESKTOP, SV_CXSCREEN )) == 0)
  227.     lDesktopX = rcl.xRight;
  228.   //  xLeft   = suggested x coordinate, unless that would cause clipping
  229.   rcl.xLeft   = ( (pptlPosition->x + rcl.xRight) > lDesktopX )
  230.                  ? lDesktopX - rcl.xRight : pptlPosition->x;
  231.                  
  232.   // Get y dimension of the screen (desktop).
  233.   if ( (lDesktopY = WinQuerySysValue( HWND_DESKTOP, SV_CYSCREEN )) == 0 )
  234.     lDesktopY = rcl.yTop;
  235.   //  yBottom = suggested y coordinate, unless that would cause clipping
  236.   rcl.yBottom = ( (pptlPosition->y + rcl.yTop) > lDesktopY )
  237.                  ? lDesktopY - rcl.yTop : pptlPosition->y;
  238.   
  239.   // Set frame window position and size
  240.   
  241.   WinSetWindowPos(hwndFrame,
  242.                   NULL,                      // relative wnd placement order
  243.                   (SHORT) rcl.xLeft,         // window x coordinate
  244.                   (SHORT) rcl.yBottom,       // window y coordindate
  245.                   (SHORT) rcl.xRight,        // window x size
  246.                   (SHORT) rcl.yTop,          // window y size
  247.                   SWP_MOVE | SWP_SIZE);      // change window x,y position
  248.                                              // change window size
  249.   // Write the new position into OS2.INI so it can be used as the
  250.   // date/time display's initial position when TIMELINE is next started
  251.   // without parameters.  Double use a convenient POINTL structure.
  252.   // The information is copied into a structure that is guaranteed to
  253.   // be continuous.
  254.   aptl[TXTBOX_TOPLEFT].x = rcl.xLeft;
  255.   aptl[TXTBOX_TOPLEFT].y = rcl.yBottom;        
  256.   WinWriteProfileData(hab,                          // handle to anchor block
  257.                       szAppName,                    // name of this appl
  258.                       szKeyName1,                   // keyword for info
  259.                       &aptl[TXTBOX_TOPLEFT],        // info to write (save)
  260.                       sizeof aptl[TXTBOX_TOPLEFT]); // bytes to write
  261. }
  262.  
  263.  
  264.  
  265. VOID UpdateTime( HWND hwnd )
  266. {
  267.   CHAR   *szTime;
  268.   CHAR    szFormattedTime[40];
  269.   RECTL   rcl;                          // rectangle structure
  270.   time_t  lTime;                        // long
  271.   
  272.   // Get ASCII time and date string and format it.  Padding with blanks
  273.   // assures that shorter date/time strings (due to proportional fonts) will
  274.   // always overwrite longer strings.
  275.   
  276.   time( &lTime );                                    // seconds since 1970
  277.   szTime = ctime(&lTime);                            // convert to date/time
  278.   strcpy( szFormattedTime, "   " );                  // start with blanks
  279.   strcat( szFormattedTime, szTime );                 // add date/time
  280.   szFormattedTime[strlen(szFormattedTime)-1] = '\0'; // get rid of line feed
  281.   strcat( szFormattedTime, "   " );                  // add trailing blanks 
  282.   
  283.   // Display string in client window using the current font and foreground
  284.   // and background colors.
  285.   
  286.   WinQueryWindowRect(hwnd,
  287.                      &rcl);             // set to window coordinates
  288.   GpiSetBackMix(hps,
  289.                 BM_OVERPAINT);          // new background overwrites
  290.   WinDrawText(hps,                      // handle to presentation space
  291.               -1,                       // string is zero terminated
  292.               szFormattedTime,          // string to be drawn
  293.               &rcl,                     // rectangle in which string is drawn
  294.               CLR_NEUTRAL,              // default foreground (text) color
  295.               CLR_BACKGROUND,           // default background color
  296.               DT_CENTER | DT_VCENTER);  // center text horizontally
  297.                                         // center text vertically
  298. }
  299.  
  300.  
  301. BOOL MoveWindow( HWND hwndFrame,PPOINTL pptlPosition, BOOL fKeyboard )
  302. {
  303.   static TRACKINFO tiFrame = {          // tracking information structure
  304.                               1,        // cxBorder (border width)
  305.                               1,        // cyBorder (border height)
  306.                               1,        // cxGrid (horizontal tracking unit)
  307.                               1,        // cyGrid (vertical tracking unit)
  308.                               1,        // cxKeyboard (pixel increments for
  309.                               1,        // cyKeyboard  keyboard interface)
  310.                                         // Change the preceding two params
  311.                                         // to something like 8 and 8 if you
  312.                                         // want to speed up the movement of
  313.                                         // the tracking rectangle when using
  314.                                         // the arrow keys
  315.                               {         // rclTrack (starting and ending)
  316.                                0,0,0,0  // xLeft, yBottom, xRight, yTop
  317.                               },
  318.                               {         // rclBoundary (bounds of rectangle)
  319.                                0,0,0,0
  320.                               },
  321.                               {         // ptlMinTrackSize
  322.                                0,0       // x, y
  323.                               },
  324.                               {         // ptlMaxTrackSize
  325.                                0,0       // x, y
  326.                               },
  327.                               0,        // fs (tracking options)
  328.                               0,        // cxLeft   (ignored unless
  329.                               0,        // cyBottom  TF_PARTINBOUNDARY is
  330.                               0,        // cxRight   set)
  331.                               0         // cyTop
  332.          };
  333.  
  334.   WinQueryWindowRect(hwndFrame,                    // get size of frame
  335.                      &tiFrame.rclTrack);
  336.   // Map the relative coordinates of the position of the frame into
  337.   // desktop coordinates
  338.   WinMapWindowPoints(hwndFrame,
  339.                      HWND_DESKTOP,
  340.                      (PPOINTL) &tiFrame.rclTrack,  // coordinates
  341.                      2L);                          // for a rectangle
  342.   // Allow tracking frame to move anywhere on the desktop.
  343.   tiFrame.fs = TF_MOVE | TF_ALLINBOUNDARY |
  344.                (fKeyboard ? TF_SETPOINTERPOS : 0);
  345.  
  346.   // Want the minimum and maximum size of the tracking box to always
  347.   // be the same size as the frame.
  348.   tiFrame.ptlMaxTrackSize.x =
  349.   tiFrame.ptlMinTrackSize.x = tiFrame.rclTrack.xRight -
  350.                               tiFrame.rclTrack.xLeft;
  351.   tiFrame.ptlMaxTrackSize.y =
  352.   tiFrame.ptlMinTrackSize.y = tiFrame.rclTrack.yTop -
  353.                               tiFrame.rclTrack.yBottom;
  354.  
  355.   // Confine the movement of the tracking rectangle to the desktop.
  356.   tiFrame.rclBoundary.xRight = WinQuerySysValue( HWND_DESKTOP,SV_CXSCREEN );
  357.   tiFrame.rclBoundary.yTop   = WinQuerySysValue( HWND_DESKTOP,SV_CYSCREEN );
  358.  
  359.   // Have PM draw the tracking rectangle and follow mouse movements.
  360.   // Return FALSE if the move was canceled by pressing the ESC key.
  361.   if ( WinTrackRect(HWND_DESKTOP,               // track over entire desktop
  362.                     NULL,
  363.                     &tiFrame) ) {               // location of tracking info
  364.     pptlPosition->x = tiFrame.rclTrack.xLeft;
  365.     pptlPosition->y = tiFrame.rclTrack.yBottom;
  366.     return TRUE;
  367.   }
  368.   return FALSE;
  369. }
  370.  
  371.  
  372.  
  373. MRESULT EXPENTRY ClientWndProc( HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2 )
  374. {
  375.          HDC     hdc;                   // handle to device context of hwnd
  376.   static HWND    hwndFrame;             // handle to window
  377.   static struct {
  378.                  CHAR *szColorKeyword;  // keyword
  379.                  LONG  ColorIndex;      // corresponding color index
  380.          } Color[MAX_COLORS] = {
  381.                                 {"BACKGROUND", CLR_BACKGROUND},
  382.                                 {"BLUE",       CLR_BLUE},
  383.                                 {"RED",        CLR_RED},
  384.                                 {"PINK",       CLR_PINK},
  385.                                 {"GREEN",      CLR_GREEN},
  386.                                 {"CYAN",       CLR_CYAN},
  387.                                 {"YELLOW",     CLR_YELLOW},
  388.                                 {"NEUTRAL",    CLR_NEUTRAL},
  389.                                 {"DARKGRAY",   CLR_DARKGRAY},
  390.                                 {"DARKBLUE",   CLR_DARKBLUE},
  391.                                 {"DARKRED",    CLR_DARKRED},
  392.                                 {"DARKPINK",   CLR_DARKPINK},
  393.                                 {"DARKGREEN",  CLR_DARKGREEN},
  394.                                 {"DARKCYAN",   CLR_DARKCYAN},
  395.                                 {"BROWN",      CLR_BROWN},
  396.                                 {"PALEGRAY",   CLR_PALEGRAY}
  397.            };
  398.   static LONG   alColorTable[MAX_COLORS*2];  // this array is actually pairs
  399.                                              // of LONG's (index, RGB)
  400.   static INT    iColor = 0;      // index into alColorTable for background
  401.                                  // color of date/time string
  402.   static INT    iIndex = 0;      // temporary
  403.   static SIZEL  sizel = {0L,0L}; // want the default presentation page size
  404.   static POINTL ptlPosition = { 0L, 0L};  // initial x and y coordinates of
  405.                                           // the lower left corner of the
  406.                                           // date/time display
  407.   static BOOL   FakeButton1Down = FALSE;  // TRUE if drag of date/time display
  408.                                           // is initiated from the keyboard
  409.   static BOOL   ProcessArrows   = TRUE;   // FALSE if WM_CHAR up or down arrows
  410.                                           // should be ignored (because they
  411.                                           // are left over from moving the
  412.                                           // date/time display by using ALT+F7
  413.                                           // and then using the arrow keys)
  414.   static BOOL   CycleColorsBack = FALSE;  // TRUE if the up arrow key is
  415.                                           // being used to cycle display
  416.                                           // colors backward
  417.          USHORT usDataSize;                // short temporary
  418.  
  419.   switch ( msg ) {
  420.  
  421.     // Received when window is first created, before it is displayed.  In
  422.     // particular, entry is here before the WinCreateStdWindow() in main()
  423.     // has a chance to return a value.  Therefore cannot have one global
  424.     // hwndFrame that is initialized by main().
  425.     case WM_CREATE:
  426.          hwndFrame = WinQueryWindow(hwnd,         // want handle to the parent
  427.                                     QW_PARENT,    // (ie, to the frame) of
  428.                                                   // this window
  429.                                     FALSE);       // do not lock parent
  430.          
  431.          // Create a presentation space for the client that everyone will use
  432.          // for drawing.  Having one space allows us to mess around with the
  433.          // colors and not have them go away.
  434.          hdc = WinOpenWindowDC ( hwnd );   // get this window's device context
  435.          hps = GpiCreatePS(hab,            // handle to anchor block
  436.                            hdc,            // handle to device context
  437.                            &sizel,         // presentation page size
  438.                            PU_PELS      |  // pel (pixel) coordinates
  439.                            GPIF_DEFAULT |  // default coordinate format
  440.                            GPIT_NORMAL  |  // normal (not micro) pres space
  441.                            GPIA_ASSOC);    // association with hdc required
  442.  
  443.          // Get the colors to cycle through when mouse Button 2 is clicked.
  444.          GpiQueryLogColorTable(hps,                  // presentation space
  445.                                (ULONG) LCOLOPT_INDEX,// return index, RGB pairs
  446.                                0L,                   // starting index
  447.                                (LONG) MAX_COLORS*2,  // number of pairs*2
  448.                                alColorTable);        // array to hold pairs
  449.  
  450.          // See if this was started with any parameters.  If so, use them.
  451.          // If not, check OS2.INI for parameters. In any case, check for
  452.          // sanity.
  453.          if ( cArg <= 1 ) {
  454.            // TIMELINE was not started with parameters.
  455.            usDataSize = sizeof ptlPosition;
  456.            if ( !WinQueryProfileData(hab,           //handle to anchor block
  457.                                      szAppName,     //application name
  458.                                      szKeyName1,    //keyword for data
  459.                                      &ptlPosition,  //array to receive data
  460.                                      &usDataSize) )  //bytes of data
  461.               ptlPosition.x = ptlPosition.y = 0L;
  462.            usDataSize = sizeof iColor;
  463.            if ( !WinQueryProfileData( hab, szAppName, szKeyName2,
  464.                                       &iColor, &usDataSize ) )
  465.              iColor = CLR_BACKGROUND;
  466.          }
  467.  
  468.          if ( cArg >= 3 ) {
  469.  
  470.            // The first parameter is the x coordinate.  Defaulted to 0
  471.            // by its declaration.
  472.            ptlPosition.x = atol( *(ppchArg+1) );
  473.  
  474.            // The second parameter is the y coordinate.  Defaulted to 0
  475.            // by its declaration.
  476.            ptlPosition.y = atol( *(ppchArg+2) );
  477.          }
  478.  
  479.          if ( cArg >= 4 ) {
  480.          // The third parameter is the text background color.  If it does
  481.          // not match with one of the known colors, use CLR_BACKGROUND.
  482.  
  483.            // Match keyword against known strings of known colors
  484.            while ( strcmpi( Color[iIndex].szColorKeyword, *(ppchArg+3) ) ) {
  485.              if ( iIndex++ == MAX_COLORS-1 ) {
  486.                iIndex = 0;
  487.                break;
  488.              }
  489.            }
  490.            // Convert index of string into index of desired color
  491.            for ( iColor = 0; iColor < MAX_COLORS*2; iColor += 2)
  492.              if ( Color[iIndex].ColorIndex == alColorTable[iColor] ) break;
  493.            if ( iColor >= MAX_COLORS*2 ) iColor = CLR_BACKGROUND;
  494.          }
  495.  
  496.          // Sanity check parameters.  If x or y position is less than 0,
  497.          // make it 0.  If the color index doesn't fit in the logical
  498.          // color table, make it CLR_BACKGROUND.  SizeWindow() will make
  499.          // sure that the date/time display is initially fully visible
  500.          // on the desktop (that is, that the x and y coordinate parameters
  501.          // end up making sense).
  502.          if ( ptlPosition.x < 0 )  ptlPosition.x = 0;
  503.          if ( ptlPosition.y < 0 )  ptlPosition.y = 0;
  504.          if ( iColor >= MAX_COLORS*2 || iColor < 0 ) iColor = CLR_BACKGROUND;
  505.  
  506.          // Set background color to choice.
  507.          GpiCreateLogColorTable(hps,             // presentation space effected
  508.                                 (ULONG) 0,       // no options
  509.                                 LCOLF_CONSECRGB, // last param is array of
  510.                                                  // consecutive RGB values
  511.                                 CLR_BACKGROUND,  // color table index of
  512.                                                  // first RGB value
  513.                                 1L,              // number of values in
  514.                                                  // last parameter
  515.                                 &alColorTable[iColor+1]);
  516.  
  517.          // Remember background color in case TIMELINE is started without
  518.          // parameters.
  519.          WinWriteProfileData(hab,                 // handle to anchor block
  520.                              szAppName,           // name of this application
  521.                              szKeyName2,          // keyword for info
  522.                              &iColor,             // info to write (save)
  523.                              sizeof iColor);      // number of bytes to write
  524.  
  525.          // Determine size and location of date/time display.
  526.          SizeWindow( hwndFrame, &ptlPosition );      // suggested x,y coords
  527.  
  528.          WinStartTimer( hab, hwnd, ID_TIMER, 1000 ); // one second per message
  529.          return 0;
  530.  
  531.     // Received when the timer goes off
  532.     case WM_TIMER:
  533.          // update the date/time display with current information
  534.          UpdateTime( hwnd );
  535.          return 0;
  536.  
  537.     // Received when the PM thinks the window needs repainting
  538.     case WM_PAINT:
  539.          WinBeginPaint(hwnd,               // window in which to draw
  540.                        hps,                // use the existing present space
  541.                        NULL);              // repainting not required
  542.          GpiErase( hps );                  // erase invalid area of window
  543.                                            // using zeroth color index value
  544.  
  545.          // update the date/time display with current information
  546.          UpdateTime( hwnd );
  547.          
  548.          WinEndPaint( hps );
  549.          return 0;
  550.  
  551.     // Received when mouse Button 1 is held down.  Make a tracking rectangle
  552.     // follow the mouse movements until the mouse is released, then redraw
  553.     // the date/time display at the new position.
  554.     case WM_BUTTON1DOWN:
  555.          WinSetFocus( HWND_DESKTOP, hwnd );       // make this window active
  556.                                                   // and have input focus
  557.          if ( MoveWindow( hwndFrame, &ptlPosition, FakeButton1Down ) ) {
  558.            // Draw the data/time display where the tracking rectangle stopped
  559.            WinSetWindowPos(hwndFrame,
  560.                            HWND_TOP,              // place on top of other wnds
  561.                            (SHORT) ptlPosition.x, // use new track frame x,y
  562.                            (SHORT) ptlPosition.y, // for new position
  563.                            0,                     // ignored
  564.                            0,                     // ignored
  565.                            SWP_MOVE);             // window move request
  566.            // Write the new position into OS2.INI so it can be used as the
  567.            // date/time display's initial position when TIMELINE is next
  568.            // started without parameters.
  569.            WinWriteProfileData(hab,                 // handle to anchor block
  570.                                szAppName,           // this application's name
  571.                                szKeyName1,          // keyword for info
  572.                                &ptlPosition,        // info to save (write)
  573.                                sizeof ptlPosition); // number of bytes to save
  574.          }
  575.          FakeButton1Down = FALSE;
  576.          // Put marker into queue that WM_CHAR processes to indicate end
  577.          // of arrow keystrokes that should be ignored
  578.          WinPostMsg( hwnd,
  579.                      WM_CHAR,
  580.                      MPFROMSH2CH((KC_KEYUP|KC_VIRTUALKEY), 0, 0),
  581.                      MPFROM2SHORT(0,VK_BUTTON3) );
  582.          return 0;
  583.  
  584.     // Received when mouse Button 2 is double clicked.  Treat as a
  585.     // WM_BUTTON2DOWN because the user is probably trying to cycle the
  586.     // mouse colors too fast.  Note that the WM_BUTTON2DOWN code must
  587.     // directly follow this case.
  588.     case WM_BUTTON2DBLCLK:
  589.  
  590.     // Received when mouse Button 2 is held down.  Change to the next text
  591.     // background color.
  592.     //
  593.     // Its interesting to note that Button 2 cannot be made to act just
  594.     // like Button 1 because WinTrackRect only seems to work for Button 1.
  595.     case WM_BUTTON2DOWN:
  596.          WinSetFocus( HWND_DESKTOP, hwnd );      // make sure of input focus
  597.          // if necessary, wrap index to start of color table
  598.          if ( CycleColorsBack ) {
  599.            CycleColorsBack = FALSE;
  600.            iColor = ( --iColor <= 0 ) ? MAX_COLORS*2-2 : --iColor;
  601.          }
  602.          else iColor = ( ++iColor == MAX_COLORS*2-1 ) ? 0 : ++iColor;
  603.          // set background color to new choice
  604.          GpiCreateLogColorTable(hps,             // presentation space effected
  605.                                 (ULONG) 0,       // no options
  606.                                 LCOLF_CONSECRGB, // last parameter is array
  607.                                                  // of consecutive RGB values
  608.                                 CLR_BACKGROUND,  // color table index of
  609.                                                  // first RGB value
  610.                                 1L,              // number of values in last
  611.                                                  // parameter
  612.                                 &alColorTable[iColor+1]);
  613.          WinInvalidateRegion(hwnd,    // window to force repaint on
  614.                              NULL,    // whole window is invalid
  615.                              FALSE);  // don't invalidate children windows
  616.          // Write the color index into OS2.INI so it can be used as the
  617.          // date/time display's initial background color when TIMELINE is
  618.          // next started without parameters.
  619.          WinWriteProfileData(hab,                 // handle to anchor block
  620.                              szAppName,           // name of this application
  621.                              szKeyName2,          // keyword for info
  622.                              &iColor,             // info to write (save)
  623.                              sizeof iColor);      // number of bytes to write
  624.          return 0;
  625.  
  626.     //Received when mouse Button 1 is double clicked.  Terminate.
  627.     case WM_BUTTON1DBLCLK:
  628.          WinPostMsg( hwnd, WM_QUIT, NULL, NULL );  // see main()
  629.          return 0;
  630.  
  631.     // Received when F1 is typed and handled through the ACCELTABLE.
  632.     // Produce a simple help message by a not so simple method
  633.     // (that is, use a real dialog box rather than something like
  634.     // WinMessageBox).
  635.     case WM_HELP:
  636.          switch ( COMMANDMSG(&msg)->source ) {
  637.  
  638.            // The following three cases should never occur for TIMELINE
  639.            case CMDSRC_PUSHBUTTON:
  640.            case CMDSRC_MENU:
  641.            case CMDSRC_OTHER:
  642.  
  643.            case CMDSRC_ACCELERATOR:
  644.                 WinDlgBox(HWND_DESKTOP,      // parent window (ie, allow
  645.                                              // dialog box anywhere on dsktop)
  646.                           hwndFrame,         // owner window
  647.                           HelpDlgProc,       // address of dialog procedure
  648.                           NULL,              // dialog box in .exe file
  649.                           timeline_help,     // dialog identifier in .exe file
  650.                           NULL);             // dialog procedure data
  651.                 WinInvalidateRegion(hwnd,    // window to force repaint on
  652.                                     NULL,    // whole window is invalid
  653.                                     FALSE);  // don't invalidate children wnds
  654.                 return 0;
  655.  
  656.            default:
  657.                 return 0;
  658.          }
  659.  
  660.     // Received when a character is typed and is not handled by something
  661.     // like the ACCELTABLE.  Ignore the character unless it is
  662.     //
  663.     // Alt+F7      then treat as if Mouse Button 1 is being held down (that
  664.     //             is, get ready to drag the display
  665.     // Alt+F4      then terminate
  666.     // F3          then terminate
  667.     // UP ARROW    then cycle display background color backward
  668.     // DOWN ARROW  then cycle display background color forward
  669.     case WM_CHAR:
  670.          switch ( CHARMSG(&msg)->vkey  ) {
  671.  
  672.            // Track cursor movement by keyboard arrows. Alt+F7 is treated
  673.            // the same way as BUTTON1DOWN
  674.            case VK_F7:
  675.                 if ( (CHARMSG(&msg)->fs & VKEY_MASK) ==
  676.                      (KC_KEYUP | KC_ALT | KC_VIRTUALKEY) ) {
  677.                   FakeButton1Down = TRUE;
  678.                   ProcessArrows = FALSE;
  679.                   WinPostMsg( hwnd, WM_BUTTON1DOWN, NULL, NULL );
  680.                 }
  681.                 return 0;
  682.  
  683.            // Terminate/Close
  684.            case VK_F4:
  685.                 if ( (CHARMSG(&msg)->fs & VKEY_MASK) == 
  686.                      (KC_KEYUP | KC_ALT | KC_VIRTUALKEY) ) {
  687.                   WinPostMsg( hwnd, WM_QUIT, NULL, NULL );  // see main()
  688.                 }
  689.                 return 0;
  690.  
  691.            // Terminate/Close
  692.            case VK_F3:
  693.                 if ( (CHARMSG(&msg)->fs & VKEY_MASK) ==
  694.                      (KC_KEYUP | KC_VIRTUALKEY) ) {
  695.                   WinPostMsg( hwnd, WM_QUIT, NULL, NULL );  // see main()
  696.                 }
  697.                 return 0;
  698.  
  699.            // Up arrow decrements color index
  700.            case VK_UP:
  701.                 if ( ProcessArrows ) {
  702.                   if ( (CHARMSG(&msg)->fs & VKEY_MASK) ==
  703.                        (KC_KEYUP | KC_VIRTUALKEY) ) {
  704.                     CycleColorsBack = TRUE;
  705.                     WinPostMsg( hwnd, WM_BUTTON2DOWN, NULL, NULL );
  706.                   }
  707.                 }
  708.                 return 0;
  709.  
  710.            // Down arrow increments color index (identical to clicking
  711.            // mouse button 2)
  712.            case VK_DOWN:
  713.                 if ( ProcessArrows ) {
  714.                   if ( (CHARMSG(&msg)->fs & VKEY_MASK) ==
  715.                        (KC_KEYUP | KC_VIRTUALKEY) ) {
  716.                     WinPostMsg( hwnd, WM_BUTTON2DOWN, NULL, NULL );
  717.                   }
  718.                 }
  719.                 return 0;
  720.  
  721.            // Received fake marker character that indicates WinTrackRect
  722.            // is done and therefore arrow keys should be processed again.
  723.            case VK_BUTTON3:
  724.                 if ( (CHARMSG(&msg)->fs & VKEY_MASK) ==
  725.                      (KC_KEYUP | KC_VIRTUALKEY) ) {
  726.                   ProcessArrows = TRUE;
  727.                 }
  728.                 return 0;
  729.  
  730.            default:
  731.                 return 0;
  732.            }
  733.  
  734.     // Received when main() has asked this window to go away
  735.     case WM_DESTROY:
  736.          WinStopTimer( hab, hwnd, ID_TIMER );
  737.          GpiDestroyPS( hps );
  738.          return 0;
  739.     }
  740.  
  741.   // If the message received isn't handled in the immediately preceding
  742.   // switch statement, fall into the next statement, which passes the
  743.   // message to the default window procedure.
  744.   return WinDefWindowProc( hwnd, msg, mp1, mp2 );
  745.  
  746. }
  747.  
  748.  
  749.  
  750. MRESULT EXPENTRY HelpDlgProc( HWND hwndDlg, USHORT msg, MPARAM mp1, MPARAM mp2 )
  751. {
  752.  
  753.   // Received an event message for the dialog box.  Process it below or
  754.   // pass it to the default dialog procedure. 
  755.   switch ( msg ) {
  756.  
  757.     // Received when a control in the dialog box has something to say.
  758.     // A command is ignored if it is not handled below.
  759.     case WM_COMMAND:
  760.       switch ( COMMANDMSG(&msg)->cmd ) {
  761.  
  762.         // Normally a dialog box has an "Enter" button, which when pushed
  763.         // results in transfer of control to here. As the help dialog
  764.         // box has no Enter button, control should never get to here.
  765.         case DID_OK:
  766.              return 0;
  767.  
  768.         // Typed ESC or pushed the ESC=Cancel button or typed Enter
  769.         // (because the ESC=Cancel button is the default)
  770.         case DID_CANCEL:
  771.              WinDismissDlg( hwndDlg, TRUE );  // remove the dialog box
  772.              return 0;
  773.  
  774.         // Ignore other commands
  775.         default:
  776.              return 0;
  777.       }
  778.  
  779.     // Received an event message that this procedure does not process.
  780.     // Pass the message to the default dialog procedure.
  781.     default:
  782.        return WinDefDlgProc( hwndDlg, msg, mp1, mp2 );
  783.   }
  784.  
  785.   return 0;
  786. }
  787.