CPARROW.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:24k
源码类别:

Windows编程

开发平台:

Visual C++

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