home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / sdktools / scrnsave / cparrow.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  25KB  |  799 lines

  1. /** FILE: arrow.c ********** Module Header ********************************
  2.  *
  3.  * Control panel utility library routines for managing "cpArrow" window
  4.  * class/spinner controls used in applet dialogs.
  5.  *
  6.  * History:
  7.  *  15:30 on Thur  25 Apr 1991  -by-  Steve Cathcart   [stevecat]
  8.  *        Took base code from Win 3.1 source
  9.  *  10:30 on Tues  04 Feb 1992  -by-  Steve Cathcart   [stevecat]
  10.  *        Updated code to latest Win 3.1 sources
  11.  *  12:00 on Fri   07 Aug 1992  -by-  Steve Cathcart   [stevecat]
  12.  *        Implemented new drawing scheme for spinner/arrow control
  13.  *
  14.  *  Copyright (C) 1990-1997 Microsoft Corporation
  15.  *
  16.  *************************************************************************/
  17. //==========================================================================
  18. //                        Include files
  19. //==========================================================================
  20. // C Runtime
  21. #include <stddef.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24.  
  25. #include <windows.h>
  26.  
  27. // Application specific
  28. #include "cparrow.h"
  29.  
  30. //==========================================================================
  31. //                        Local Definitions
  32. //==========================================================================
  33.  
  34. //  Offsets to use with GetWindowLong
  35. #define GWL_SPINNERSTATE    0
  36.  
  37. //  Control state flags.
  38. #define SPINNERSTATE_GRAYED      0x0001
  39. #define SPINNERSTATE_HIDDEN      0x0002
  40. #define SPINNERSTATE_MOUSEOUT    0x0004
  41. #define SPINNERSTATE_UPCLICK     0x0008
  42. #define SPINNERSTATE_DOWNCLICK   0x0010
  43.  
  44. //  Combination of click states.
  45. #define SPINNERSTATE_CLICKED   (SPINNERSTATE_UPCLICK | SPINNERSTATE_DOWNCLICK)
  46.  
  47. //  Combination of state flags.
  48. #define SPINNERSTATE_ALL         0x001F
  49.  
  50. //  Sinner Control color indices
  51. #define SPINNERCOLOR_FACE        0
  52. #define SPINNERCOLOR_ARROW       1
  53. #define SPINNERCOLOR_SHADOW      2
  54. #define SPINNERCOLOR_HIGHLIGHT   3
  55. #define SPINNERCOLOR_FRAME       4
  56.  
  57. #define CCOLORS                  5
  58.  
  59. //==========================================================================
  60. //                        External Declarations
  61. //==========================================================================
  62.  
  63.  
  64. //==========================================================================
  65. //                        Local Data Declarations
  66. //==========================================================================
  67.  
  68. /*
  69.  * Macros to change the control state given the state flag(s)
  70.  */
  71. #define StateSet(dwState, wFlags)    (dwState |=  (wFlags))
  72. #define StateClear(dwState, wFlags)  (dwState &= ~(wFlags))
  73. #define StateTest(dwState, wFlags)   (dwState &   (wFlags))
  74.  
  75.  
  76. //Array of default colors, matching the order of SPINNERCOLOR_* values.
  77. DWORD rgColorDef[CCOLORS]={
  78.                          COLOR_BTNFACE,             //  SPINNERCOLOR_FACE
  79.                          COLOR_BTNTEXT,             //  SPINNERCOLOR_ARROW
  80.                          COLOR_BTNSHADOW,           //  SPINNERCOLOR_SHADOW
  81.                          COLOR_BTNHIGHLIGHT,        //  SPINNERCOLOR_HIGHLIGHT
  82.                          COLOR_WINDOWFRAME          //  SPINNERCOLOR_FRAME
  83.                          };
  84.  
  85. BOOL   bArrowTimed = FALSE;
  86. BOOL   bRight;
  87. HANDLE hParent;
  88.  
  89.  
  90. //==========================================================================
  91. //                        Local Function Prototypes
  92. //==========================================================================
  93. void Draw3DButtonRect (HDC hDC, HPEN hPenHigh, HPEN hPenShadow, int x1,
  94.                        int y1, int x2, int y2, BOOL fClicked);
  95. LONG SpinnerPaint (HWND hWnd, DWORD dwSpinnerState);
  96.  
  97.  
  98. //==========================================================================
  99. //                            Functions
  100. //==========================================================================
  101.  
  102. BOOL OddArrowWindow(HWND hArrowWnd)
  103. {
  104.  
  105. #ifdef  OLD_CODE
  106.     HWND hParent;
  107.     RECT rResize;
  108.     BOOL bResize;
  109.  
  110.  
  111.     GetWindowRect(hArrowWnd, (LPRECT) &rResize);
  112.     if (!(bResize = (rResize.right - rResize.left) % 2))
  113.     {
  114.         rResize.right++;
  115.         ScreenToClient(hParent = GetParent(hArrowWnd), (LPPOINT) & rResize.left);
  116.         ScreenToClient(hParent, (LPPOINT) & rResize.right);
  117.         MoveWindow(hArrowWnd, rResize.left, rResize.top,
  118.                              (rResize.right - rResize.left),
  119.                              (rResize.bottom - rResize.top), FALSE);
  120.     }
  121.     return(bResize);
  122. #endif  //  OLD_CODE
  123.  
  124.     return(TRUE);
  125. }
  126.  
  127.  
  128. VOID ArrowTimerProc(HWND hWnd, UINT wMsg, UINT nID, DWORD dwTime)
  129. {
  130.     WORD  wScroll;
  131.     DWORD dwSpinnerState;
  132.  
  133.     dwSpinnerState = (DWORD) GetWindowLong (hWnd, GWL_SPINNERSTATE);
  134.  
  135.     if (StateTest(dwSpinnerState, SPINNERSTATE_CLICKED))
  136.     {
  137.         wScroll = (StateTest(dwSpinnerState, SPINNERSTATE_DOWNCLICK)) ?
  138.                                                     SB_LINEDOWN : SB_LINEUP;
  139.         if (bRight == WM_RBUTTONDOWN)
  140.             wScroll += SB_PAGEUP - SB_LINEUP;
  141.  
  142.             SendMessage(hParent, WM_VSCROLL,
  143.                         MAKELONG(wScroll, GetWindowLong(hWnd, GWL_ID)),
  144.                         (LONG) hWnd);
  145.     }
  146.  
  147.     //  Don't need to call KillTimer(), because SetTimer will
  148.     //  reset the right one
  149.  
  150.     SetTimer(hWnd, nID, 50, (TIMERPROC) ArrowTimerProc);
  151.  
  152.     return ;
  153.  
  154.     wMsg = wMsg;
  155.     dwTime = dwTime;
  156. }
  157.  
  158.  
  159. /*
  160.  * ClickedRectCalc
  161.  *
  162.  * Description:
  163.  *  Calculates the rectangle of the clicked region based on the
  164.  *  state flags SPINNERSTATE_UPCLICK and SPINNERSTATE_DOWNCLICK.
  165.  *
  166.  * Parameter:
  167.  *  hWnd            HWND handle to the control window.
  168.  *  lpRect          LPRECT rectangle structure to fill.
  169.  *
  170.  * Return Value:
  171.  *  void
  172.  *
  173.  */
  174.  
  175. void ClickedRectCalc(HWND hWnd, DWORD dwState, LPRECT lpRect)
  176. {
  177.     int  cx, cy;
  178.  
  179.     GetClientRect (hWnd, lpRect);
  180.  
  181.     cx = lpRect->right  >> 1;
  182.     cy = lpRect->bottom >> 1;
  183.  
  184.     if (StateTest(dwState, SPINNERSTATE_DOWNCLICK))
  185.         lpRect->top = cy;
  186.     else
  187.         lpRect->bottom = 1+cy;
  188.  
  189.     return;
  190. }
  191.  
  192. /*
  193.  * ArrowControlProc
  194.  *
  195.  * Description:
  196.  *
  197.  *  Window Procedure for the Spinner/Arrow custom control.  Handles all
  198.  *  messages like WM_PAINT just as a normal application window would.
  199.  *  State information about the control is maintained ALL drawing is
  200.  *  handled during WM_PAINT message processing.
  201.  *
  202.  */
  203. LRESULT APIENTRY ArrowControlProc(HWND hArrow, UINT message, WPARAM wParam, LONG lParam)
  204. {
  205.     WORD    wScroll;
  206.     POINT   pt;
  207.     RECT    rect;
  208.     int     x, y;
  209.     int     cy;
  210.     DWORD   dwSpinnerState, dwState;
  211.  
  212.  
  213.     dwSpinnerState = (DWORD) GetWindowLong (hArrow, GWL_SPINNERSTATE);
  214.  
  215.     switch (message)
  216.     {
  217.     case WM_CREATE:
  218.         dwSpinnerState = 0;
  219.         SetWindowLong (hArrow, GWL_SPINNERSTATE, (LONG) dwSpinnerState);
  220.         break;
  221.  
  222.  
  223.     case WM_ENABLE:
  224.         //  Handles disabling/enabling case.  Example of a
  225.         //  change-state-and-repaint strategy since we let the
  226.         //  painting code take care of the visuals.
  227.  
  228.         if (wParam)
  229.             StateClear(dwSpinnerState, SPINNERSTATE_GRAYED);
  230.         else
  231.             StateSet(dwSpinnerState, SPINNERSTATE_GRAYED);
  232.  
  233.         SetWindowLong (hArrow, GWL_SPINNERSTATE, (LONG) dwSpinnerState);
  234.  
  235.         //  Force a repaint since the control will look different.
  236.  
  237.         InvalidateRect (hArrow, NULL, TRUE);
  238.         UpdateWindow (hArrow);
  239.         break;
  240.  
  241.  
  242.     case WM_SHOWWINDOW:
  243.         //  Set or clear the hidden flag. Windows will
  244.         //  automatically force a repaint if we become visible.
  245.  
  246.         if (wParam)
  247.             StateClear(dwSpinnerState, SPINNERSTATE_HIDDEN);
  248.         else
  249.             StateSet(dwSpinnerState, SPINNERSTATE_HIDDEN);
  250.  
  251.         SetWindowLong (hArrow, GWL_SPINNERSTATE, (LONG) dwSpinnerState);
  252.         break;
  253.  
  254.  
  255.     case WM_CANCELMODE:
  256.         //  IMPORTANT MESSAGE!  WM_CANCELMODE means that a
  257.         //  dialog or some other modal process has started.
  258.         //  we must make sure that we cancel any clicked state
  259.         //  we are in, kill the timers, and release the capture.
  260.  
  261.         StateClear(dwSpinnerState, SPINNERSTATE_CLICKED);
  262.         if (bArrowTimed)
  263.         {
  264.             SendMessage (hParent, WM_VSCROLL, MAKELONG(SB_ENDSCROLL,
  265.                            GetWindowLong (hArrow, GWL_ID)), (LONG) hArrow);
  266.             KillTimer (hArrow, GetWindowLong (hArrow, GWL_ID));
  267.             bArrowTimed = FALSE;
  268.         }
  269.         ReleaseCapture();
  270.         break;
  271.  
  272.     case WM_RBUTTONDOWN:
  273.     case WM_LBUTTONDOWN:
  274.         //  When we get a mouse down message, we know that the mouse
  275.         //  is over the control.  We then do the following steps
  276.         //  to set up the new state:
  277.         //   1.  Hit-test the coordinates of the click to
  278.         //       determine in which half the click occurred.
  279.         //   2.  Set the appropriate SPINNERSTATE_*CLICK state
  280.         //       and repaint that clicked half.  This is another
  281.         //       example of a change-state-and-repaint strategy.
  282.         //   3.  Send an initial scroll message.
  283.         //   4.  Set the mouse capture.
  284.         //   5.  Set the initial delay timer before repeating
  285.         //       the scroll message.
  286.  
  287.         if (bRight)
  288.             break;
  289.  
  290.         bRight = message;
  291.  
  292.         hParent = GetParent (hArrow);
  293.  
  294.         //  Get the mouse coordinates.
  295.         x = (int) LOWORD(lParam);
  296.         y = (int) HIWORD(lParam);
  297.  
  298.         //  Only need to hit-test the upper half
  299.         //  Then change-state-and-repaint
  300.  
  301.         GetClientRect (hArrow, &rect);
  302.         cy = rect.bottom >> 1;
  303.  
  304.         if (y > cy)
  305.         {
  306.             StateSet(dwSpinnerState, SPINNERSTATE_DOWNCLICK);
  307.             rect.top = cy;
  308.             wScroll = SB_LINEDOWN;
  309.         }
  310.         else
  311.         {
  312.             StateSet(dwSpinnerState, SPINNERSTATE_UPCLICK);
  313.             rect.bottom = cy + 1;
  314.             wScroll = SB_LINEUP;
  315.         }
  316.  
  317.         SetWindowLong (hArrow, GWL_SPINNERSTATE, (LONG) dwSpinnerState);
  318.  
  319.         InvalidateRect (hArrow, &rect, TRUE);
  320.         UpdateWindow (hArrow);
  321.  
  322.         SetCapture (hArrow);
  323.  
  324.         //  Process SHIFT key state along with button message
  325.  
  326.         if (wParam & MK_SHIFT)
  327.         {
  328.             if (message != WM_RBUTTONDOWN)
  329.                 wScroll += (WORD) (SB_TOP - SB_LINEUP);
  330.             else
  331.                 wScroll += (WORD) (SB_THUMBPOSITION - SB_LINEUP);
  332.         }
  333.         else
  334.         {
  335.             if (message == WM_RBUTTONDOWN)
  336.                 wScroll += SB_PAGEUP - SB_LINEUP;
  337.  
  338.             bArrowTimed = SetTimer (hArrow, GetWindowLong (hArrow, GWL_ID),
  339.                                              200, (TIMERPROC) ArrowTimerProc);
  340.         }
  341.         SendMessage (hParent, WM_VSCROLL, MAKELONG(wScroll,
  342.                               GetWindowLong (hArrow, GWL_ID)), (LONG) hArrow);
  343.         break;
  344.  
  345.     case WM_MOUSEMOVE:
  346.         //  On WM_MOUSEMOVE messages we want to know if the mouse
  347.         //  has moved out of the control when the control is in
  348.         //  a clicked state.  If the control has not been clicked,
  349.         //  then we have nothing to do.  Otherwise we want to set
  350.         //  the SPINNERSTATE_MOUSEOUT flag and repaint so the button
  351.         //  visually comes up.
  352.  
  353.         if (!StateTest(dwSpinnerState, SPINNERSTATE_CLICKED))
  354.             break;
  355.  
  356.         //  Save copy of original state
  357.         dwState = dwSpinnerState;
  358.  
  359.         //  Get the mouse coordinates.
  360.         pt.x = (int) LOWORD(lParam);
  361.         pt.y = (int) HIWORD(lParam);
  362.  
  363.         //  Get the area we originally clicked and the new POINT
  364.         ClickedRectCalc (hArrow, dwSpinnerState, &rect);
  365.  
  366.         //  Hit-Test the rectange and change the state if necessary.
  367.         if (PtInRect(&rect, pt))
  368.             StateClear(dwSpinnerState, SPINNERSTATE_MOUSEOUT);
  369.         else
  370.             StateSet(dwSpinnerState, SPINNERSTATE_MOUSEOUT);
  371.  
  372.         SetWindowLong (hArrow, GWL_SPINNERSTATE, (LONG) dwSpinnerState);
  373.  
  374.         //  If the state changed, repaint the appropriate part of
  375.         //  the control.
  376.         if (dwState != dwSpinnerState)
  377.         {
  378.             InvalidateRect (hArrow, &rect, TRUE);
  379.             UpdateWindow (hArrow);
  380.         }
  381.  
  382.         break;
  383.  
  384.  
  385.     case WM_LBUTTONUP:
  386.     case WM_RBUTTONUP:
  387.         //  A mouse button up event is much like WM_CANCELMODE since
  388.         //  we have to clean out whatever state the control is in:
  389.         //   1.  Kill any repeat timers we might have created.
  390.         //   2.  Release the mouse capture.
  391.         //   3.  Clear the clicked states and repaint, another example
  392.         //       of a change-state-and-repaint strategy.
  393.  
  394.         if ((UINT) (bRight - WM_LBUTTONDOWN + WM_LBUTTONUP) == message)
  395.         {
  396.             bRight = 0;
  397.             ReleaseCapture();
  398.  
  399.             if (bArrowTimed)
  400.             {
  401.                 SendMessage (hParent, WM_VSCROLL, MAKELONG(SB_ENDSCROLL,
  402.                                GetWindowLong (hArrow, GWL_ID)), (LONG) hArrow);
  403.                 KillTimer (hArrow, GetWindowLong (hArrow, GWL_ID));
  404.                 bArrowTimed = FALSE;
  405.             }
  406.  
  407.             //  Repaint if necessary, only if we are clicked AND the mouse
  408.             //  is still in the boundaries of the control.
  409.  
  410.             if (StateTest(dwSpinnerState, SPINNERSTATE_CLICKED) &&
  411.                 StateTest(dwSpinnerState, ~SPINNERSTATE_MOUSEOUT))
  412.             {
  413.                 //  Calculate the rectangle before clearing states.
  414.                 ClickedRectCalc (hArrow, dwSpinnerState, &rect);
  415.  
  416.                 //  Clear the states so we repaint properly.
  417.                 StateClear(dwSpinnerState, SPINNERSTATE_CLICKED | SPINNERSTATE_MOUSEOUT);
  418.  
  419.                 SetWindowLong (hArrow, GWL_SPINNERSTATE, (LONG) dwSpinnerState);
  420.                 InvalidateRect (hArrow, &rect, TRUE);
  421.                 UpdateWindow (hArrow);
  422.             }
  423.         }
  424.         break;
  425.  
  426.  
  427.     case WM_PAINT:
  428.         return SpinnerPaint (hArrow, dwSpinnerState);
  429.  
  430.  
  431.     default:
  432.         return (DefWindowProc (hArrow, message, wParam, lParam));
  433.         break;
  434.     }
  435.     return(0L);
  436. }
  437.  
  438.  
  439. /*
  440.  * SpinnerPaint
  441.  *
  442.  * Description:
  443.  *
  444.  *  Handles all WM_PAINT messages for the control and paints
  445.  *  the control for the current state, whether it be clicked
  446.  *  or disabled.
  447.  *
  448.  * Parameters:
  449.  *  hWnd            HWND Handle to the control.
  450.  *  dwSpinnerState  DWORD Spinner control status flags
  451.  *
  452.  * Return Value:
  453.  *  LONG            0L.
  454.  */
  455.  
  456. LONG SpinnerPaint (HWND hWnd, DWORD dwSpinnerState)
  457. {
  458.     PAINTSTRUCT ps;
  459.     LPRECT      lpRect;
  460.     RECT        rect;
  461.     HDC         hDC;
  462.     COLORREF    rgCr[CCOLORS];
  463.     HPEN        rgHPen[CCOLORS];
  464.     int         iColor;
  465.  
  466.     HBRUSH      hBrushArrow;
  467.     HBRUSH      hBrushFace;
  468.     HBRUSH      hBrushBlack;
  469.  
  470.     POINT       rgpt1[3];
  471.     POINT       rgpt2[3];
  472.  
  473.     int         xAdd1=0, yAdd1=0;
  474.     int         xAdd2=0, yAdd2=0;
  475.  
  476.     int         cx,  cy;        //  Whole dimensions
  477.     int         cx2, cy2;       //  Half dimensions
  478.     int         cx4, cy4;       //  Quarter dimensions
  479.  
  480.     lpRect = ▭
  481.  
  482.     hDC = BeginPaint (hWnd, &ps);
  483.     GetClientRect (hWnd, lpRect);
  484.  
  485.     //  Get colors that we'll need.  We do not want to cache these
  486.     //  items since we may our top-level parent window may have
  487.     //  received a WM_WININICHANGE message at which time the control
  488.     //  is repainted.  Since this control never sees that message,
  489.     //  we cannot assume that colors will remain the same throughout
  490.     //  the life of the control.
  491.  
  492.     for (iColor = 0; iColor < CCOLORS; iColor++)
  493.     {
  494.         rgCr[iColor] = GetSysColor (rgColorDef[iColor]);
  495.  
  496.         rgHPen[iColor] = CreatePen (PS_SOLID, 1, rgCr[iColor]);
  497.     }
  498.  
  499.     hBrushFace  = CreateSolidBrush (rgCr[SPINNERCOLOR_FACE]);
  500.     hBrushArrow = CreateSolidBrush (rgCr[SPINNERCOLOR_ARROW]);
  501.     hBrushBlack = GetStockObject (BLACK_BRUSH);
  502.  
  503.     //  These values are extremely cheap to calculate for the amount
  504.     //  we are going to use them.
  505.  
  506.     cx  = lpRect->right  - lpRect->left;
  507.     cy  = lpRect->bottom - lpRect->top;
  508.     cx2 = cx  >> 1;
  509.     cy2 = cy  >> 1;
  510.     cx4 = cx2 >> 1;
  511.     cy4 = cy2 >> 1;
  512.  
  513.     //  If one half is depressed, set the x/yAdd varaibles that we use
  514.     //  to shift the small arrow image down and right.
  515.  
  516.     if (!StateTest(dwSpinnerState, SPINNERSTATE_MOUSEOUT))
  517.     {
  518.         if (StateTest(dwSpinnerState, SPINNERSTATE_UPCLICK))
  519.         {
  520.             xAdd1 = 1;
  521.             yAdd1 = 1;
  522.         }
  523.         else if (StateTest(dwSpinnerState, SPINNERSTATE_DOWNCLICK))
  524.         {
  525.             xAdd2 = 1;
  526.             yAdd2 = 1;
  527.         }
  528.     }
  529.  
  530.     //  Draw the face color and the outer frame
  531.     SelectObject (hDC, hBrushFace);
  532.     SelectObject (hDC, rgHPen[SPINNERCOLOR_FRAME]);
  533.  
  534.     Rectangle (hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
  535.  
  536.     //  Draw the horizontal center line.
  537.     MoveToEx (hDC, 0, cy2, NULL);
  538.     LineTo (hDC, cx, cy2);
  539.  
  540.     //  We do one of three modifications for drawing the borders:
  541.     //   1) Both halves un-clicked.
  542.     //   2) Top clicked,   bottom unclicked.
  543.     //   3) Top unclicked, bottom clicked.
  544.     //
  545.     //  Case 1 is xAdd1==xAdd2==0
  546.     //  Case 2 is xAdd1==1, xAdd2=0
  547.     //  Case 3 is xAdd1==0, xAdd2==1
  548.  
  549.     //  Draw top and bottom buttons borders.
  550.     Draw3DButtonRect (hDC, rgHPen[SPINNERCOLOR_HIGHLIGHT],
  551.                       rgHPen[SPINNERCOLOR_SHADOW],
  552.                       0,  0,  cx-1, cy2,  (BOOL) xAdd1);
  553.  
  554.     Draw3DButtonRect (hDC, rgHPen[SPINNERCOLOR_HIGHLIGHT],
  555.                       rgHPen[SPINNERCOLOR_SHADOW],
  556.                       0, cy2, cx-1, cy-1, (BOOL) xAdd2);
  557.  
  558.  
  559.     //  Select default line color.
  560.     SelectObject (hDC, rgHPen[SPINNERCOLOR_ARROW]);
  561.  
  562.     //  Draw the arrows depending on the enable state.
  563.     if (StateTest (dwSpinnerState, SPINNERSTATE_GRAYED))
  564.     {
  565.         //  Draw arrow color lines in the upper left of the
  566.         //  top arrow and on the top of the bottom arrow.
  567.         //  Pen was already selected as a default.
  568.  
  569.         MoveToEx (hDC, cx2,   cy4-2, NULL);      //Top arrow
  570.         LineTo   (hDC, cx2-3, cy4+1);
  571.         MoveToEx (hDC, cx2-3, cy2+cy4-2, NULL);  //Bottom arrow
  572.         LineTo   (hDC, cx2+3, cy2+cy4-2);
  573.  
  574.         //  Draw highlight color lines in the bottom of the
  575.         //  top arrow and on the lower right of the bottom arrow.
  576.  
  577.         SelectObject (hDC, rgHPen[SPINNERCOLOR_HIGHLIGHT]);
  578.         MoveToEx (hDC, cx2-3, cy4+1, NULL);      //Top arrow
  579.         LineTo   (hDC, cx2+3, cy4+1);
  580.         MoveToEx (hDC, cx2+3, cy2+cy4-2, NULL);  //Bottom arrow
  581.         LineTo   (hDC, cx2,   cy2+cy4+1);
  582.         SetPixel (hDC, cx2,   cy2+cy4+1, rgCr[SPINNERCOLOR_HIGHLIGHT]);
  583.     }
  584.     else
  585.     {
  586.         //  Top arrow polygon
  587.         rgpt1[0].x = xAdd1 + cx2;
  588.         rgpt1[0].y = yAdd1 + cy4 - 2;
  589.         rgpt1[1].x = xAdd1 + cx2 - 3;
  590.         rgpt1[1].y = yAdd1 + cy4 + 1;
  591.         rgpt1[2].x = xAdd1 + cx2 + 3;
  592.         rgpt1[2].y = yAdd1 + cy4 + 1;
  593.  
  594.         //  Bottom arrow polygon
  595.         rgpt2[0].x = xAdd2 + cx2;
  596.         rgpt2[0].y = yAdd2 + cy2 + cy4 + 1;
  597.         rgpt2[1].x = xAdd2 + cx2 - 3;
  598.         rgpt2[1].y = yAdd2 + cy2 + cy4 - 2;
  599.         rgpt2[2].x = xAdd2 + cx2 + 3;
  600.         rgpt2[2].y = yAdd2 + cy2 + cy4 - 2;
  601.  
  602.         //  Draw the arrows
  603.         SelectObject (hDC, hBrushArrow);
  604.         Polygon (hDC, (LPPOINT)rgpt1, 3);
  605.         Polygon (hDC, (LPPOINT)rgpt2, 3);
  606.     }
  607.  
  608.     //  Clean up
  609.     EndPaint(hWnd, &ps);
  610.  
  611.     DeleteObject (hBrushFace);
  612.     DeleteObject (hBrushArrow);
  613.  
  614.     for (iColor = 0; iColor < CCOLORS; iColor++)
  615.     {
  616.         if (rgHPen[iColor])
  617.             DeleteObject (rgHPen[iColor]);
  618.     }
  619.  
  620.     return 0L;
  621. }
  622.  
  623.  
  624. /*
  625.  * Draw3DButtonRect
  626.  *
  627.  * Description:
  628.  *  Draws the 3D button look within a given rectangle.  This rectangle
  629.  *  is assumed to be bounded by a one pixel black border, so everything
  630.  *  is bumped in by one.
  631.  *
  632.  * Parameters:
  633.  *  hDC         DC to draw to.
  634.  *  hPenHigh    HPEN highlight color pen.
  635.  *  hPenShadow  HPEN shadow color pen.
  636.  *  x1          int Upper left corner x.
  637.  *  y1          int Upper left corner y.
  638.  *  x2          int Lower right corner x.
  639.  *  y2          int Lower right corner y.
  640.  *  fClicked    BOOL specifies if the button is down or not (TRUE==DOWN)
  641.  *
  642.  * Return Value:
  643.  *  void
  644.  *
  645.  */
  646.  
  647. void Draw3DButtonRect (HDC hDC, HPEN hPenHigh, HPEN hPenShadow, int x1,
  648.                        int y1, int x2, int y2, BOOL fClicked)
  649. {
  650.     HPEN  hPenOrg;
  651.  
  652.     //  Shrink the rectangle to account for borders.
  653.     x1+=1;
  654.     x2-=1;
  655.     y1+=1;
  656.     y2-=1;
  657.  
  658.     hPenOrg = SelectObject (hDC, hPenShadow);
  659.  
  660.     if (fClicked)
  661.     {
  662.         //  Shadow on left and top edge when clicked.
  663.         MoveToEx (hDC, x1, y2, NULL);
  664.         LineTo (hDC, x1, y1);
  665.         LineTo (hDC, x2+1, y1);
  666.     }
  667.     else
  668.     {
  669.         //  Lowest shadow line.
  670.         MoveToEx (hDC, x1, y2, NULL);
  671.         LineTo (hDC, x2, y2);
  672.         LineTo (hDC, x2, y1-1);
  673.  
  674.         //  Upper shadow line.
  675.         MoveToEx (hDC, x1+1, y2-1, NULL);
  676.         LineTo (hDC, x2-1, y2-1);
  677.         LineTo (hDC, x2-1, y1);
  678.  
  679.         SelectObject (hDC, hPenHigh);
  680.  
  681.         //  Upper highlight line.
  682.         MoveToEx (hDC, x1, y2-1, NULL);
  683.         LineTo (hDC, x1, y1);
  684.         LineTo (hDC, x2, y1);
  685.     }
  686.  
  687.     if (hPenOrg)
  688.         SelectObject (hDC, hPenOrg);
  689.  
  690.     return;
  691. }
  692.  
  693.  
  694. BOOL RegisterArrowClass (HANDLE hModule)
  695. {
  696.     WNDCLASS wcArrow;
  697.  
  698.     wcArrow.lpszClassName = TEXT("cpArrow");
  699.     wcArrow.hInstance     = hModule;
  700.     wcArrow.lpfnWndProc   = ArrowControlProc;
  701.     wcArrow.hCursor       = LoadCursor(NULL, IDC_ARROW);
  702.     wcArrow.hIcon         = NULL;
  703.     wcArrow.lpszMenuName  = NULL;
  704.     wcArrow.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  705.     wcArrow.style         = CS_HREDRAW | CS_VREDRAW;
  706.     wcArrow.cbClsExtra    = 0;
  707.     wcArrow.cbWndExtra    = sizeof(DWORD);
  708.  
  709.     return(RegisterClass((LPWNDCLASS) &wcArrow));
  710. }
  711.  
  712.  
  713. /*
  714. short ArrowVScrollProc(wScroll, nCurrent, lpAVS)
  715.  
  716. wScroll is an SB_* message
  717. nCurrent is the base value to change
  718. lpAVS is a far pointer to the structure containing change amounts
  719.       and limits to be used, along with a flags location for errors
  720.  
  721. returns a short value of the final amount
  722.         the flags element in the lpAVS struct is
  723.                 0 if no problems found
  724.          OVERFLOW set if the change exceeded upper limit (limit is returned)
  725.         UNDERFLOW set if the change exceeded lower limit (limit is returned)
  726.    UNKNOWNCOMMAND set if wScroll is not a known SB_* message
  727.  
  728. NOTE: Only one of OVERFLOW or UNDERFLOW may be set.  If you send in values
  729.       that would allow both to be set, that's your problem.  Either can
  730.       be set in combination with UNKNOWNCOMMAND (when the command is not
  731.       known and the input value is out of bounds).
  732. */
  733.  
  734. short ArrowVScrollProc(short wScroll, short nCurrent, LPARROWVSCROLL lpAVS)
  735. {
  736.     short    nDelta;
  737.  
  738. /* Find the message and put the relative change in nDelta.  If the
  739.    message is an absolute change, put 0 in nDelta and set nCurrent
  740.    to the value specified.  If the command is unknown, set error
  741.    flag, set nDelta to 0, and proceed through checks.
  742. */
  743.  
  744.     switch (wScroll)
  745.     {
  746.     case SB_LINEUP:
  747.         nDelta = lpAVS->lineup;
  748.         break;
  749.     case SB_LINEDOWN:
  750.         nDelta = lpAVS->linedown;
  751.         break;
  752.     case SB_PAGEUP:
  753.         nDelta = lpAVS->pageup;
  754.         break;
  755.     case SB_PAGEDOWN:
  756.         nDelta = lpAVS->pagedown;
  757.         break;
  758.     case SB_TOP:
  759.         nCurrent = lpAVS->top;
  760.         nDelta = 0;
  761.         break;
  762.     case SB_BOTTOM:
  763.         nCurrent = lpAVS->bottom;
  764.         nDelta = 0;
  765.         break;
  766.     case SB_THUMBTRACK:
  767.         nCurrent = lpAVS->thumbtrack;
  768.         nDelta = 0;
  769.         break;
  770.     case SB_THUMBPOSITION:
  771.         nCurrent = lpAVS->thumbpos;
  772.         nDelta = 0;
  773.         break;
  774.     case SB_ENDSCROLL:
  775.         nDelta = 0;
  776.         break;
  777.     default:
  778.         lpAVS->flags = UNKNOWNCOMMAND;
  779.         nDelta = 0;
  780.         break;
  781.     }
  782.     if (nCurrent + nDelta > lpAVS->top)
  783.     {
  784.         nCurrent = lpAVS->top;
  785.         nDelta = 0;
  786.         lpAVS->flags = OVERFLOW;
  787.     }
  788.     else if (nCurrent + nDelta < lpAVS->bottom)
  789.     {
  790.         nCurrent = lpAVS->bottom;
  791.         nDelta = 0;
  792.         lpAVS->flags = UNDERFLOW;
  793.     }
  794.     else
  795.         lpAVS->flags = 0;
  796.     return(nCurrent + nDelta);
  797. }
  798.  
  799.