text: /**************************** Module Header ********************************\
  * Module Name: sbctl.c
  *
  * Copyright (c) 1985 - 1999, Microsoft Corporation
  *
  * Scroll bar internal routines
  *
  * History:
  *   11/21/90 JimA      Created.
  *   02-04-91 IanJa     Revalidaion added
  \***************************************************************************/
  
  #include "precomp.h"
  #pragma hdrstop
  
  void CalcSBStuff(
      PWND pwnd,
      PSBCALC pSBCalc,
      BOOL fVert);
  
  #define IsScrollBarControl(h) (GETFNID(h) == FNID_SCROLLBAR)
  
  /*
   * Now it is possible to selectively Enable/Disable just one arrow of a Window
   * scroll bar; Various bits in the 7th word in the rgwScroll array indicates which
   * one of these arrows are disabled; The following masks indicate which bit of the
   * word indicates which arrow;
   */
  #define WSB_HORZ_LF  0x0001  // Represents the Left arrow of the horizontal scroll bar.
  #define WSB_HORZ_RT  0x0002  // Represents the Right arrow of the horizontal scroll bar.
  #define WSB_VERT_UP  0x0004  // Represents the Up arrow of the vert scroll bar.
  #define WSB_VERT_DN  0x0008  // Represents the Down arrow of the vert scroll bar.
  
  #define WSB_VERT (WSB_VERT_UP | WSB_VERT_DN)
  #define WSB_HORZ   (WSB_HORZ_LF | WSB_HORZ_RT)
  
  void DrawCtlThumb(PSBWND);
  
  /*
   * RETURN_IF_PSBTRACK_INVALID:
   * This macro tests whether the pSBTrack we have is invalid, which can happen
   * if it gets freed during a callback.
   * This protects agains the original pSBTrack being freed and no new one
   * being allocated or a new one being allocated at a different address.
   * This does not protect against the original pSBTrack being freed and a new
   * one being allocated at the same address.
   * If pSBTrack has changed, we assert that there is not already a new one
   * because we are really not expecting this.
   */
  #define RETURN_IF_PSBTRACK_INVALID(pSBTrack, pwnd) \
      if ((pSBTrack) != PWNDTOPSBTRACK(pwnd)) {      \
          UserAssert(PWNDTOPSBTRACK(pwnd) == NULL);  \
          return;                                    \
      }
  
  /*
   * REEVALUATE_PSBTRACK
   * This macro just refreshes the local variable pSBTrack, in case it has
   * been changed during a callback.  After performing this operation, pSBTrack
   * should be tested to make sure it is not now NULL.
   */
  #if DBG
  #define REEVALUATE_PSBTRACK(pSBTrack, pwnd, str)          \
      if ((pSBTrack) != PWNDTOPSBTRACK(pwnd)) {             \
          RIPMSG3(RIP_VERBOSE,                              \
                  "%s: pSBTrack changed from %#p to %#p",   \
                  (str), (pSBTrack), PWNDTOPSBTRACK(pwnd)); \
      }                                                     \
      (pSBTrack) = PWNDTOPSBTRACK(pwnd)
  #else
  #define REEVALUATE_PSBTRACK(pSBTrack, pwnd, str)          \
      (pSBTrack) = PWNDTOPSBTRACK(pwnd)
  #endif
  
  /***************************************************************************\
  * HitTestScrollBar
  *
  * 11/15/96      vadimg          ported from Memphis sources
  \***************************************************************************/
  
  int HitTestScrollBar(PWND pwnd, BOOL fVert, POINT pt)
  {
      UINT wDisable;
      int px;
      BOOL fCtl = IsScrollBarControl(pwnd);
      SBCALC SBCalc, *pSBCalc;
  
      if (fCtl) {
          wDisable = ((PSBWND)pwnd)->wDisableFlags;
      } else {
          //
          // Reflect the click coordinates on the horizontal
          // scroll bar if the window is mirrored
          //
          if (TestWF(pwnd,WEFLAYOUTRTL) && !fVert) {
              pt.x = pwnd->rcWindow.right - pt.x;
          } else {
              pt.x -= pwnd->rcWindow.left;
          }
  
          pt.y -= pwnd->rcWindow.top;
          wDisable = GetWndSBDisableFlags(pwnd, fVert);
      }
  
      if ((wDisable & SB_DISABLE_MASK) == SB_DISABLE_MASK) {
          return HTERROR;
      }
  
      if (fCtl) {
          pSBCalc = &(((PSBWND)pwnd)->SBCalc);
      } else {
          pSBCalc = &SBCalc;
          CalcSBStuff(pwnd, pSBCalc, fVert);
      }
  
      px = fVert ? pt.y : pt.x;
  
      if (px < pSBCalc->pxUpArrow) {
          if (wDisable & LTUPFLAG) {
              return HTERROR;
          }
          return HTSCROLLUP;
      } else if (px >= pSBCalc->pxDownArrow) {
          if (wDisable & RTDNFLAG) {
              return HTERROR;
          }
          return HTSCROLLDOWN;
      } else if (px < pSBCalc->pxThumbTop) {
          return HTSCROLLUPPAGE;
      } else if (px < pSBCalc->pxThumbBottom) {
          return HTSCROLLTHUMB;
      } else if (px < pSBCalc->pxDownArrow) {
          return HTSCROLLDOWNPAGE;
      }
      return HTERROR;
  }
  
  BOOL _SBGetParms(
      PWND pwnd,
      int code,
      PSBDATA pw,
      LPSCROLLINFO lpsi)
  {
      PSBTRACK pSBTrack;
  
      pSBTrack = PWNDTOPSBTRACK(pwnd);
  
  
      if (lpsi->fMask & SIF_RANGE) {
          lpsi->nMin = pw->posMin;
          lpsi->nMax = pw->posMax;
      }
  
      if (lpsi->fMask & SIF_PAGE)
          lpsi->nPage = pw->page;
  
      if (lpsi->fMask & SIF_POS) {
          lpsi->nPos = pw->pos;
      }
  
      if (lpsi->fMask & SIF_TRACKPOS)
      {
          if (pSBTrack && (pSBTrack->nBar == code) && (pSBTrack->spwndTrack == pwnd)) {
              // posNew is in the context of psbiSB's window and bar code
              lpsi->nTrackPos = pSBTrack->posNew;
          } else {
              lpsi->nTrackPos = pw->pos;
          }
      }
      return ((lpsi->fMask & SIF_ALL) ? TRUE : FALSE);
  }
  
  /***************************************************************************\
  * GetWndSBDisableFlags
  *
  * This returns the scroll bar Disable flags of the scroll bars of a
  *  given Window.
  *
  *
  * History:
  *  4-18-91 MikeHar Ported for the 31 merge
  \***************************************************************************/
  
  UINT GetWndSBDisableFlags(
      PWND pwnd,  // The window whose scroll bar Disable Flags are to be returned;
      BOOL fVert)  // If this is TRUE, it means Vertical scroll bar.
  {
      PSBINFO pw;
  
      if ((pw = pwnd->pSBInfo) == NULL) {
          RIPERR0(ERROR_NO_SCROLLBARS, RIP_VERBOSE, "");
          return 0;
      }
  
      return (fVert ? (pw->WSBflags & WSB_VERT) >> 2 : pw->WSBflags & WSB_HORZ);
  }
  
  
  /***************************************************************************\
  *  xxxEnableSBCtlArrows()
  *
  *  This function can be used to selectively Enable/Disable
  *     the arrows of a scroll bar Control
  *
  * History:
  * 04-18-91 MikeHar      Ported for the 31 merge
  \***************************************************************************/
  
  BOOL xxxEnableSBCtlArrows(
      PWND pwnd,
      UINT wArrows)
  {
      UINT wOldFlags;
      UINT wNewFlags;
  
      CheckLock(pwnd);
      UserAssert(IsWinEventNotifyDeferredOK());
  
      wOldFlags = ((PSBWND)pwnd)->wDisableFlags; // Get the original status
  
      if (wArrows == ESB_ENABLE_BOTH) {      // Enable both the arrows
          ((PSBWND)pwnd)->wDisableFlags &= ~SB_DISABLE_MASK;
      } else {
          ((PSBWND)pwnd)->wDisableFlags |= wArrows;
      }
  
      /*
       * Check if the status has changed because of this call
       */
      if (wOldFlags == ((PSBWND)pwnd)->wDisableFlags)
          return FALSE;
  
      /*
       * Else, redraw the scroll bar control to reflect the new state
       */
      if (IsVisible(pwnd))
          xxxInvalidateRect(pwnd, NULL, TRUE);
  
      wNewFlags = ((PSBWND)pwnd)->wDisableFlags;
  
      /*
       * state change notifications
       */
      if ((wOldFlags & ESB_DISABLE_UP) != (wNewFlags & ESB_DISABLE_UP)) {
          xxxWindowEvent(EVENT_OBJECT_STATECHANGE, pwnd, OBJID_CLIENT,
                  INDEX_SCROLLBAR_UP, WEF_USEPWNDTHREAD);
      }
  
      if ((wOldFlags & ESB_DISABLE_DOWN) != (wNewFlags & ESB_DISABLE_DOWN)) {
          xxxWindowEvent(EVENT_OBJECT_STATECHANGE, pwnd, OBJID_CLIENT,
                  INDEX_SCROLLBAR_DOWN, WEF_USEPWNDTHREAD);
      }
  
      return TRUE;
  }
  
  
  /***************************************************************************\
  * xxxEnableWndSBArrows()
  *
  *  This function can be used to selectively Enable/Disable
  *     the arrows of a Window Scroll bar(s)
  *
  * History:
  *  4-18-91 MikeHar      Ported for the 31 merge
  \***************************************************************************/
  
  BOOL xxxEnableWndSBArrows(
      PWND pwnd,
      UINT wSBflags,
      UINT wArrows)
  {
      INT wOldFlags;
      PSBINFO pw;
      BOOL bRetValue = FALSE;
      HDC hdc;
  
      CheckLock(pwnd);
      UserAssert(IsWinEventNotifyDeferredOK());
  
      if ((pw = pwnd->pSBInfo) != NULL) {
          wOldFlags = pw->WSBflags;
      } else {
  
          /*
           * Originally everything is enabled; Check to see if this function is
           * asked to disable anything; Otherwise, no change in status; So, must
           * return immediately;
           */
          if(!wArrows)
              return FALSE;          // No change in status!
  
          wOldFlags = 0;    // Both are originally enabled;
          if((pw = _InitPwSB(pwnd)) == NULL)  // Allocate the pSBInfo for hWnd
              return FALSE;
      }
  
  
      if((hdc = _GetWindowDC(pwnd)) == NULL)
          return FALSE;
  
      /*
       *  First Take care of the Horizontal Scroll bar, if one exists.
       */
      if((wSBflags == SB_HORZ) || (wSBflags == SB_BOTH)) {
          if(wArrows == ESB_ENABLE_BOTH)      // Enable both the arrows
              pw->WSBflags &= ~SB_DISABLE_MASK;
          else
              pw->WSBflags |= wArrows;
  
          /*
           * Update the display of the Horizontal Scroll Bar;
           */
          if(pw->WSBflags != wOldFlags) {
              bRetValue = TRUE;
              wOldFlags = pw->WSBflags;
              if (TestWF(pwnd, WFHPRESENT) && !TestWF(pwnd, WFMINIMIZED) &&
                      IsVisible(pwnd)) {
                  xxxDrawScrollBar(pwnd, hdc, FALSE);  // Horizontal Scroll Bar.
              }
          }
          // Left button
          if ((wOldFlags & ESB_DISABLE_LEFT) != (pw->WSBflags & ESB_DISABLE_LEFT)) {
              xxxWindowEvent(EVENT_OBJECT_STATECHANGE, pwnd, OBJID_HSCROLL,
                      INDEX_SCROLLBAR_UP, WEF_USEPWNDTHREAD);
          }
  
          // Right button
          if ((wOldFlags & ESB_DISABLE_RIGHT) != (pw->WSBflags & ESB_DISABLE_RIGHT)) {
              xxxWindowEvent(EVENT_OBJECT_STATECHANGE, pwnd, OBJID_HSCROLL,
                      INDEX_SCROLLBAR_DOWN, WEF_USEPWNDTHREAD);
          }
      }
  
      /*
       *  Then take care of the Vertical Scroll bar, if one exists.
       */
      if((wSBflags == SB_VERT) || (wSBflags == SB_BOTH)) {
          if(wArrows == ESB_ENABLE_BOTH)      // Enable both the arrows
              pw->WSBflags &= ~(SB_DISABLE_MASK << 2);
          else
              pw->WSBflags |= (wArrows << 2);
  
          /*
           * Update the display of the Vertical Scroll Bar;
           */
          if(pw->WSBflags != wOldFlags) {
              bRetValue = TRUE;
              if (TestWF(pwnd, WFVPRESENT) && !TestWF(pwnd, WFMINIMIZED) &&
                      IsVisible(pwnd)) {
                  xxxDrawScrollBar(pwnd, hdc, TRUE);  // Vertical Scroll Bar
              }
  
              // Up button
              if ((wOldFlags & (ESB_DISABLE_UP << 2)) != (pw->WSBflags & (ESB_DISABLE_UP << 2))) {
                  xxxWindowEvent(EVENT_OBJECT_STATECHANGE, pwnd, OBJID_VSCROLL,
                          INDEX_SCROLLBAR_UP, WEF_USEPWNDTHREAD);
              }
  
              // Down button
              if ((wOldFlags & (ESB_DISABLE_DOWN << 2)) != (pw->WSBflags & (ESB_DISABLE_DOWN << 2))) {
                  xxxWindowEvent(EVENT_OBJECT_STATECHANGE, pwnd, OBJID_VSCROLL,
                          INDEX_SCROLLBAR_DOWN, WEF_USEPWNDTHREAD);
              }
          }
      }
  
      _ReleaseDC(hdc);
  
      return bRetValue;
  }
  
  
  /***************************************************************************\
  * EnableScrollBar()
  *
  * This function can be used to selectively Enable/Disable
  *     the arrows of a scroll bar; It could be used with Windows Scroll
  *     bars as well as scroll bar controls
  *
  * History:
  *  4-18-91 MikeHar Ported for the 31 merge
  \***************************************************************************/
  
  BOOL xxxEnableScrollBar(
      PWND pwnd,
      UINT wSBflags,  // Whether it is a Window Scroll Bar; if so, HORZ or VERT?
                      // Possible values are SB_HORZ, SB_VERT, SB_CTL or SB_BOTH
      UINT wArrows)   // Which arrows must be enabled/disabled:
                      // ESB_ENABLE_BOTH = > Enable both arrows.
                      // ESB_DISABLE_LTUP = > Disable Left/Up arrow;
                      // ESB_DISABLE_RTDN = > DIsable Right/Down arrow;
                      // ESB_DISABLE_BOTH = > Disable both the arrows;
  {
  #define ES_NOTHING 0
  #define ES_DISABLE 1
  #define ES_ENABLE  2
      UINT wOldFlags;
      UINT wEnableWindow;
  
      CheckLock(pwnd);
  
      if(wSBflags != SB_CTL) {
          return xxxEnableWndSBArrows(pwnd, wSBflags, wArrows);
      }
  
      /*
       *  Let us assume that we don't have to call EnableWindow
       */
      wEnableWindow = ES_NOTHING;
  
      wOldFlags = ((PSBWND)pwnd)->wDisableFlags & (UINT)SB_DISABLE_MASK;
  
      /*
       * Check if the present state of the arrows is exactly the same
       *  as what the caller wants:
       */
      if (wOldFlags == wArrows)
          return FALSE ;          // If so, nothing needs to be done;
  
      /*
       * Check if the caller wants to disable both the arrows
       */
      if (wArrows == ESB_DISABLE_BOTH) {
          wEnableWindow = ES_DISABLE;      // Yes! So, disable the whole SB Ctl.
      } else {
  
          /*
           * Check if the caller wants to enable both the arrows
           */
          if(wArrows == ESB_ENABLE_BOTH) {
  
              /*
               * We need to enable the SB Ctl only if it was already disabled.
               */
              if(wOldFlags == ESB_DISABLE_BOTH)
                  wEnableWindow = ES_ENABLE;// EnableWindow(.., TRUE);
          } else {
  
              /*
               * Now, Caller wants to disable only one arrow;
               * Check if one of the arrows was already disabled and we want
               * to disable the other;If so, the whole SB Ctl will have to be
               * disabled; Check if this is the case:
               */
              if((wOldFlags | wArrows) == ESB_DISABLE_BOTH)
                  wEnableWindow = ES_DISABLE;      // EnableWindow(, FALSE);
           }
      }
      if(wEnableWindow != ES_NOTHING) {
  
          /*
           * EnableWindow returns old state of the window; We must return
           * TRUE only if the Old state is different from new state.
           */
          if(xxxEnableWindow(pwnd, (BOOL)(wEnableWindow == ES_ENABLE))) {
              return !(TestWF(pwnd, WFDISABLED));
          } else {
              return TestWF(pwnd, WFDISABLED);
          }
      }
  
      return (BOOL)xxxSendMessage(pwnd, SBM_ENABLE_ARROWS, (DWORD)wArrows, 0);
  #undef ES_NOTHING
  #undef ES_DISABLE
  #undef ES_ENABLE
  }
  
  /***************************************************************************\
  *
  *  DrawSize() -
  *
  \***************************************************************************/
  void FAR DrawSize(PWND pwnd, HDC hdc, int cxFrame,int cyFrame)
  {
      int     x, y;
      //HBRUSH  hbrSave;
  
      if (TestWF(pwnd, WEFLEFTSCROLL)) {
          x = cxFrame;
      } else {
          x = pwnd->rcWindow.right - pwnd->rcWindow.left - cxFrame - SYSMET(CXVSCROLL);
      }
      y = pwnd->rcWindow.bottom - pwnd->rcWindow.top  - cyFrame - SYSMET(CYHSCROLL);
  
      // If we have a scrollbar control, or the sizebox is not associated with
      // a sizeable window, draw the flat gray sizebox.  Otherwise, use the
      // sizing grip.
      if (IsScrollBarControl(pwnd))
      {
          if (TestWF(pwnd, SBFSIZEGRIP))
              goto DrawSizeGrip;
          else
              goto DrawBox;
  
      }
      else if (!SizeBoxHwnd(pwnd))
      {
  DrawBox:
          {
              //hbrSave = GreSelectBrush(hdc, SYSHBR(3DFACE));
              //GrePatBlt(hdc, x, y, SYSMET(CXVSCROLL), SYSMET(CYHSCROLL), PATCOPY);
              //GreSelectBrush(hdc, hbrSave);
  
              POLYPATBLT PolyData;
  
              PolyData.x         = x;
              PolyData.y         = y;
              PolyData.cx        = SYSMET(CXVSCROLL);
              PolyData.cy        = SYSMET(CYHSCROLL);
              PolyData.BrClr.hbr = SYSHBR(3DFACE);
  
              GrePolyPatBlt(hdc,PATCOPY,&PolyData,1,PPB_BRUSH);
  
          }
      }
      else
      {
  DrawSizeGrip:
          // Blt out the grip bitmap.
          BitBltSysBmp(hdc, x, y, TestWF(pwnd, WEFLEFTSCROLL) ? OBI_NCGRIP_L : OBI_NCGRIP);
      }
  }
  
  /***************************************************************************\
  * xxxSelectColorObjects
  *
  *
  *
  * History:
  \***************************************************************************/
  
  HBRUSH xxxGetColorObjects(
      PWND pwnd,
      HDC hdc)
  {
      HBRUSH hbrRet;
  
      CheckLock(pwnd);
  
      // Use the scrollbar color even if the scrollbar is disabeld.
      if (!IsScrollBarControl(pwnd))
          hbrRet = (HBRUSH)xxxDefWindowProc(pwnd, WM_CTLCOLORSCROLLBAR, (WPARAM)hdc, (LPARAM)HWq(pwnd));
      else {
          // B#12770 - GetControlBrush sends a WM_CTLCOLOR message to the
          // owner.  If the app doesn't process the message, DefWindowProc32
          // will always return the appropriate system brush. If the app.
          // returns an invalid object, GetControlBrush will call DWP for
          // the default brush. Thus hbrRet doesn't need any validation
          // here.
          hbrRet = xxxGetControlBrush(pwnd, hdc, WM_CTLCOLORSCROLLBAR);
      }
  
      return hbrRet;
  }
  
  /***************************************************************************\
  *
  *  DrawGroove()
  *
  *  Draws lines & middle of thumb groove
  *  Note that pw points into prc.  Moreover, note that both pw & prc are
  *  NEAR pointers, so *prc better not be on the stack.
  *
  \***************************************************************************/
  void NEAR DrawGroove(HDC hdc, HBRUSH  hbr, LPRECT prc, BOOL fVert)
  {
      if ((hbr == SYSHBR(3DHILIGHT)) || (hbr == gpsi->hbrGray))
          FillRect(hdc, prc, hbr);
      else
      {
          RECT    rc;
  
      // Draw sides
          CopyRect(&rc, prc);
          DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_ADJUST | BF_FLAT |
              (fVert ? BF_LEFT | BF_RIGHT : BF_TOP | BF_BOTTOM));
  
      // Fill middle
          FillRect(hdc, &rc, hbr);
      }
  }
  
  /***************************************************************************\
  * CalcTrackDragRect
  *
  * Give the rectangle for a scrollbar in pSBTrack->pSBCalc,
  * calculate pSBTrack->rcTrack, the rectangle where tracking
  * may occur without cancelling the thumbdrag operation.
  *
  \***************************************************************************/
  
  void CalcTrackDragRect(PSBTRACK pSBTrack) {
  
      int     cx;
      int     cy;
      LPINT   pwX, pwY;
  
      //
      // Point pwX and pwY at the parts of the rectangle
      // corresponding to pSBCalc->pxLeft, pxTop, etc.
      //
      // pSBTrack->pSBCalc->pxLeft is the left edge of a vertical
      // scrollbar and the top edge of horizontal one.
      // pSBTrack->pSBCalc->pxTop is the top of a vertical
      // scrollbar and the left of horizontal one.
      // etc...
      //
      // Point pwX and pwY to the corresponding parts
      // of pSBTrack->rcTrack.
      //
  
      pwX = pwY = (LPINT)&pSBTrack->rcTrack;
  
      if (pSBTrack->fTrackVert) {
          cy = SYSMET(CYVTHUMB);
          pwY++;
      } else {
          cy = SYSMET(CXHTHUMB);
          pwX++;
      }
      /*
       * Later5.0 GerardoB: People keep complaining about this tracking region
       *  being too narrow so let's make it wider while PM decides what to do
       *  about it.
       * We also used to have some hard coded min and max values but that should
       *  depend on some metric, if at all needed.
       */
      cx = (pSBTrack->pSBCalc->pxRight - pSBTrack->pSBCalc->pxLeft) * 8;
      cy *= 2;
  
      *(pwX + 0) = pSBTrack->pSBCalc->pxLeft - cx;
      *(pwY + 0) = pSBTrack->pSBCalc->pxTop - cy;
      *(pwX + 2) = pSBTrack->pSBCalc->pxRight + cx;
      *(pwY + 2) = pSBTrack->pSBCalc->pxBottom + cy;
  }
  
  void RecalcTrackRect(PSBTRACK pSBTrack) {
      LPINT pwX, pwY;
      RECT rcSB;
  
  
      if (!pSBTrack->fCtlSB)
          CalcSBStuff(pSBTrack->spwndTrack, pSBTrack->pSBCalc, pSBTrack->fTrackVert);
  
      pwX = (LPINT)&rcSB;
      pwY = pwX + 1;
      if (!pSBTrack->fTrackVert)
          pwX = pwY--;
  
      *(pwX + 0) = pSBTrack->pSBCalc->pxLeft;
      *(pwY + 0) = pSBTrack->pSBCalc->pxTop;
      *(pwX + 2) = pSBTrack->pSBCalc->pxRight;
      *(pwY + 2) = pSBTrack->pSBCalc->pxBottom;
  
      switch(pSBTrack->cmdSB) {
      case SB_LINEUP:
          *(pwY + 2) = pSBTrack->pSBCalc->pxUpArrow;
          break;
      case SB_LINEDOWN:
          *(pwY + 0) = pSBTrack->pSBCalc->pxDownArrow;
          break;
      case SB_PAGEUP:
          *(pwY + 0) = pSBTrack->pSBCalc->pxUpArrow;
          *(pwY + 2) = pSBTrack->pSBCalc->pxThumbTop;
          break;
      case SB_THUMBPOSITION:
          CalcTrackDragRect(pSBTrack);
          break;
      case SB_PAGEDOWN:
          *(pwY + 0) = pSBTrack->pSBCalc->pxThumbBottom;
          *(pwY + 2) = pSBTrack->pSBCalc->pxDownArrow;
          break;
      }
  
      if (pSBTrack->cmdSB != SB_THUMBPOSITION) {
          CopyRect(&pSBTrack->rcTrack, &rcSB);
      }
  }
  
  /***************************************************************************\
  * DrawThumb2
  *
  *
  *
  * History:
  * 01-03-94  FritzS   Chicago changes
  \***************************************************************************/
  
  void DrawThumb2(
      PWND pwnd,
      PSBCALC pSBCalc,
      HDC hdc,
      HBRUSH hbr,
      BOOL fVert,
      UINT wDisable)  /* Disabled flags for the scroll bar */
  {
      int    *pLength;
      int    *pWidth;
      RECT   rcSB;
      PSBTRACK pSBTrack;
  
      //
      // Bail out if the scrollbar has an empty rect
      //
      if ((pSBCalc->pxTop >= pSBCalc->pxBottom) || (pSBCalc->pxLeft >= pSBCalc->pxRight))
          return;
      pLength = (LPINT)&rcSB;
      if (fVert)
          pWidth = pLength++;
      else
          pWidth  = pLength + 1;
  
      pWidth[0] = pSBCalc->pxLeft;
      pWidth[2] = pSBCalc->pxRight;
  
      /*
       * If both scroll bar arrows are disabled, then we should not draw
       * the thumb.  So, quit now!
       */
      if (((wDisable & LTUPFLAG) && (wDisable & RTDNFLAG)) ||
          ((pSBCalc->pxDownArrow - pSBCalc->pxUpArrow) < pSBCalc->cpxThumb)) {
          pLength[0] = pSBCalc->pxUpArrow;
          pLength[2] = pSBCalc->pxDownArrow;
  
          DrawGroove(hdc, hbr, &rcSB, fVert);
          return;
      }
  
      if (pSBCalc->pxUpArrow < pSBCalc->pxThumbTop) {
          // Fill in space above Thumb
          pLength[0] = pSBCalc->pxUpArrow;
          pLength[2] = pSBCalc->pxThumbTop;
  
          DrawGroove(hdc, hbr, &rcSB, fVert);
      }
  
      if (pSBCalc->pxThumbBottom < pSBCalc->pxDownArrow) {
          // Fill in space below Thumb
          pLength[0] = pSBCalc->pxThumbBottom;
          pLength[2] = pSBCalc->pxDownArrow;
  
          DrawGroove(hdc, hbr, &rcSB, fVert);
      }
  
      //
      // Draw elevator
      //
      pLength[0] = pSBCalc->pxThumbTop;
      pLength[2] = pSBCalc->pxThumbBottom;
  
      // Not soft!
      DrawPushButton(hdc, &rcSB, 0, 0);
  
      /*
       * If we're tracking a page scroll, then we've obliterated the hilite.
       * We need to correct the hiliting rectangle, and rehilite it.
       */
      pSBTrack = PWNDTOPSBTRACK(pwnd);
  
      if (pSBTrack && (pSBTrack->cmdSB == SB_PAGEUP || pSBTrack->cmdSB == SB_PAGEDOWN) &&
              (pwnd == pSBTrack->spwndTrack) &&
              (BOOL)pSBTrack->fTrackVert == fVert) {
  
          if (pSBTrack->fTrackRecalc) {
              RecalcTrackRect(pSBTrack);
              pSBTrack->fTrackRecalc = FALSE;
          }
  
          pLength = (int *)&pSBTrack->rcTrack;
  
          if (fVert)
              pLength++;
  
          if (pSBTrack->cmdSB == SB_PAGEUP)
              pLength[2] = pSBCalc->pxThumbTop;
          else
              pLength[0] = pSBCalc->pxThumbBottom;
  
          if (pLength[0] < pLength[2])
              InvertRect(hdc, &pSBTrack->rcTrack);
      }
  }
  
  /***************************************************************************\
  * xxxDrawSB2
  *
  *
  *
  * History:
  \***************************************************************************/
  
  void xxxDrawSB2(
      PWND pwnd,
      PSBCALC pSBCalc,
      HDC hdc,
      BOOL fVert,
      UINT wDisable)
  {
  
      int     cLength;
      int     cWidth;
      int     *pwX;
      int     *pwY;
      HBRUSH hbr;
      HBRUSH hbrSave;
      int cpxArrow;
      RECT    rc, rcSB;
      COLORREF crText, crBk;
  
      CheckLock(pwnd);
  
      cLength = (pSBCalc->pxBottom - pSBCalc->pxTop) / 2;
      cWidth = (pSBCalc->pxRight - pSBCalc->pxLeft);
  
      if ((cLength <= 0) || (cWidth <= 0)) {
          return;
      }
      if (fVert)
          cpxArrow = SYSMET(CYVSCROLL);
      else
          cpxArrow = SYSMET(CXHSCROLL);
  
      /*
       * Save background and DC color, since they get changed in
       * xxxGetColorObjects. Restore before we return.
       */
      crBk = GreGetBkColor(hdc);
      crText = GreGetTextColor(hdc);
  
      hbr = xxxGetColorObjects(pwnd, hdc);
  
      if (cLength > cpxArrow)
          cLength = cpxArrow;
      pwX = (int *)&rcSB;
      pwY = pwX + 1;
      if (!fVert)
          pwX = pwY--;
  
      pwX[0] = pSBCalc->pxLeft;
      pwY[0] = pSBCalc->pxTop;
      pwX[2] = pSBCalc->pxRight;
      pwY[2] = pSBCalc->pxBottom;
  
      hbrSave = GreSelectBrush(hdc, SYSHBR(BTNTEXT));
  
      //
      // BOGUS
      // Draw scrollbar arrows as disabled if the scrollbar itself is
      // disabled OR if the window it is a part of is disabled?
      //
      if (fVert) {
          if ((cLength == SYSMET(CYVSCROLL)) && (cWidth == SYSMET(CXVSCROLL))) {
              BitBltSysBmp(hdc, rcSB.left, rcSB.top, (wDisable & LTUPFLAG) ? OBI_UPARROW_I : OBI_UPARROW);
              BitBltSysBmp(hdc, rcSB.left, rcSB.bottom - cLength, (wDisable & RTDNFLAG) ? OBI_DNARROW_I : OBI_DNARROW);
          } else {
              CopyRect(&rc, &rcSB);
              rc.bottom = rc.top + cLength;
              DrawFrameControl(hdc, &rc, DFC_SCROLL,
                  DFCS_SCROLLUP | ((wDisable & LTUPFLAG) ? DFCS_INACTIVE : 0));
  
              rc.bottom = rcSB.bottom;
              rc.top = rcSB.bottom - cLength;
              DrawFrameControl(hdc, &rc, DFC_SCROLL,
                  DFCS_SCROLLDOWN | ((wDisable & RTDNFLAG) ? DFCS_INACTIVE : 0));
          }
      } else {
          if ((cLength == SYSMET(CXHSCROLL)) && (cWidth == SYSMET(CYHSCROLL))) {
              BitBltSysBmp(hdc, rcSB.left, rcSB.top, (wDisable & LTUPFLAG) ? OBI_LFARROW_I : OBI_LFARROW);
              BitBltSysBmp(hdc, rcSB.right - cLength, rcSB.top, (wDisable & RTDNFLAG) ? OBI_RGARROW_I : OBI_RGARROW);
          } else {
              CopyRect(&rc, &rcSB);
              rc.right = rc.left + cLength;
              DrawFrameControl(hdc, &rc, DFC_SCROLL,
                  DFCS_SCROLLLEFT | ((wDisable & LTUPFLAG) ? DFCS_INACTIVE : 0));
  
              rc.right = rcSB.right;
              rc.left = rcSB.right - cLength;
              DrawFrameControl(hdc, &rc, DFC_SCROLL,
                  DFCS_SCROLLRIGHT | ((wDisable & RTDNFLAG) ? DFCS_INACTIVE : 0));
          }
      }
  
      hbrSave = GreSelectBrush(hdc, hbrSave);
      DrawThumb2(pwnd, pSBCalc, hdc, hbr, fVert, wDisable);
      GreSelectBrush(hdc, hbrSave);
  
      GreSetBkColor(hdc, crBk);
      GreSetTextColor(hdc, crText);
  }
  
  /***************************************************************************\
  * zzzSetSBCaretPos
  *
  *
  *
  * History:
  \***************************************************************************/
  
  void zzzSetSBCaretPos(
      PSBWND psbwnd)
  {
  
      if ((PWND)psbwnd == PtiCurrent()->pq->spwndFocus) {
          zzzSetCaretPos((psbwnd->fVert ? psbwnd->SBCalc.pxLeft : psbwnd->SBCalc.pxThumbTop) + SYSMET(CXEDGE),
                  (psbwnd->fVert ? psbwnd->SBCalc.pxThumbTop : psbwnd->SBCalc.pxLeft) + SYSMET(CYEDGE));
      }
  }
  
  /***************************************************************************\
  * CalcSBStuff2
  *
  *
  *
  * History:
  \***************************************************************************/
  
  void CalcSBStuff2(
      PSBCALC  pSBCalc,
      LPRECT lprc,
      CONST PSBDATA pw,
      BOOL fVert)
  {
      int cpx;
      DWORD dwRange;
      int denom;
  
      if (fVert) {
          pSBCalc->pxTop = lprc->top;
          pSBCalc->pxBottom = lprc->bottom;
          pSBCalc->pxLeft = lprc->left;
          pSBCalc->pxRight = lprc->right;
          pSBCalc->cpxThumb = SYSMET(CYVSCROLL);
      } else {
  
          /*
           * For horiz scroll bars, "left" & "right" are "top" and "bottom",
           * and vice versa.
           */
          pSBCalc->pxTop = lprc->left;
          pSBCalc->pxBottom = lprc->right;
          pSBCalc->pxLeft = lprc->top;
          pSBCalc->pxRight = lprc->bottom;
          pSBCalc->cpxThumb = SYSMET(CXHSCROLL);
      }
  
      pSBCalc->pos = pw->pos;
      pSBCalc->page = pw->page;
      pSBCalc->posMin = pw->posMin;
      pSBCalc->posMax = pw->posMax;
  
      dwRange = ((DWORD)(pSBCalc->posMax - pSBCalc->posMin)) + 1;
  
      //
      // For the case of short scroll bars that don't have enough
      // room to fit the full-sized up and down arrows, shorten
      // their sizes to make 'em fit
      //
      cpx = min((pSBCalc->pxBottom - pSBCalc->pxTop) / 2, pSBCalc->cpxThumb);
  
      pSBCalc->pxUpArrow   = pSBCalc->pxTop    + cpx;
      pSBCalc->pxDownArrow = pSBCalc->pxBottom - cpx;
  
      if ((pw->page != 0) && (dwRange != 0)) {
          // JEFFBOG -- This is the one and only place where we should
          // see 'range'.  Elsewhere it should be 'range - page'.
  
          /*
           * The minimun thumb size used to depend on the frame/edge metrics.
           * People that increase the scrollbar width/height expect the minimun
           *  to grow with proportianally. So NT5 bases the minimun on
           *  CXH/YVSCROLL, which is set by default in cpxThumb.
           */
          /*
           * i is used to keep the macro "max" from executing EngMulDiv twice.
           */
          int i = EngMulDiv(pSBCalc->pxDownArrow - pSBCalc->pxUpArrow,
                                               pw->page, dwRange);
          pSBCalc->cpxThumb = max(pSBCalc->cpxThumb / 2, i);
      }
  
      pSBCalc->pxMin = pSBCalc->pxTop + cpx;
      pSBCalc->cpx = pSBCalc->pxBottom - cpx - pSBCalc->cpxThumb - pSBCalc->pxMin;
  
      denom = dwRange - (pw->page ? pw->page : 1);
      if (denom)
          pSBCalc->pxThumbTop = EngMulDiv(pw->pos - pw->posMin,
              pSBCalc->cpx, denom) +
              pSBCalc->pxMin;
      else
          pSBCalc->pxThumbTop = pSBCalc->pxMin - 1;
  
      pSBCalc->pxThumbBottom = pSBCalc->pxThumbTop + pSBCalc->cpxThumb;
  
  }
  
  /***************************************************************************\
  * SBCtlSetup
  *
  *
  *
  * History:
  \***************************************************************************/
  
  void SBCtlSetup(
      PSBWND psbwnd)
  {
      RECT rc;
  
      GetRect((PWND)psbwnd, &rc, GRECT_CLIENT | GRECT_CLIENTCOORDS);
      CalcSBStuff2(&psbwnd->SBCalc, &rc, (PSBDATA)&psbwnd->SBCalc, psbwnd->fVert);
  }
  
  /***************************************************************************\
  * HotTrackSB
  *
  \***************************************************************************/
  
  #ifdef COLOR_HOTTRACKING
  
  DWORD GetTrackFlags(int ht, BOOL fDraw)
  {
      if (fDraw) {
          switch(ht) {
          case HTSCROLLUP:
          case HTSCROLLUPPAGE:
              return LTUPFLAG;
  
          case HTSCROLLDOWN:
          case HTSCROLLDOWNPAGE:
              return RTDNFLAG;
  
          case HTSCROLLTHUMB:
              return LTUPFLAG | RTDNFLAG;
  
          default:
              return 0;
          }
      } else {
          return 0;
      }
  }
  
  BOOL xxxHotTrackSB(PWND pwnd, int htEx, BOOL fDraw)
  {
      SBCALC SBCalc;
      HDC  hdc;
      BOOL fVert = HIWORD(htEx);
      int ht = LOWORD(htEx);
      DWORD dwTrack = GetTrackFlags(ht, fDraw);
  
      CheckLock(pwnd);
  
      /*
       * xxxDrawSB2 does not callback or leave the critical section when it's
       * not a SB control and the window belongs to a different thread. It
       * calls xxxDefWindowProc which simply returns the brush color.
       */
      CalcSBStuff(pwnd, &SBCalc, fVert);
      hdc = _GetDCEx(pwnd, NULL, DCX_WINDOW | DCX_USESTYLE | DCX_CACHE);
      xxxDrawSB2(pwnd, &SBCalc, hdc, fVert, GetWndSBDisableFlags(pwnd, fVert), dwTrack);
      _ReleaseDC(hdc);
      return TRUE;
  }
  
  void xxxHotTrackSBCtl(PSBWND psbwnd, int ht, BOOL fDraw)
  {
      DWORD dwTrack = GetTrackFlags(ht, fDraw);
      HDC hdc;
  
      CheckLock(psbwnd);
  
      SBCtlSetup(psbwnd);
      hdc = _GetDCEx((PWND)psbwnd, NULL, DCX_WINDOW | DCX_USESTYLE | DCX_CACHE);
      xxxDrawSB2((PWND)psbwnd, &psbwnd->SBCalc, hdc, psbwnd->fVert, psbwnd->wDisableFlags, dwTrack);
      _ReleaseDC(hdc);
  }
  #endif // COLOR_HOTTRACKING
  
  BOOL SBSetParms(PSBDATA pw, LPSCROLLINFO lpsi, LPBOOL lpfScroll, LPLONG lplres)
  {
      // pass the struct because we modify the struct but don't want that
      // modified version to get back to the calling app
  
      BOOL fChanged = FALSE;
  
      if (lpsi->fMask & SIF_RETURNOLDPOS)
          // save previous position
          *lplres = pw->pos;
  
      if (lpsi->fMask & SIF_RANGE) {
          // if the range MAX is below the range MIN -- then treat is as a
          // zero range starting at the range MIN.
          if (lpsi->nMax < lpsi->nMin)
              lpsi->nMax = lpsi->nMin;
  
          if ((pw->posMin != lpsi->nMin) || (pw->posMax != lpsi->nMax)) {
              pw->posMin = lpsi->nMin;
              pw->posMax = lpsi->nMax;
  
              if (!(lpsi->fMask & SIF_PAGE)) {
                  lpsi->fMask |= SIF_PAGE;
                  lpsi->nPage = pw->page;
              }
  
              if (!(lpsi->fMask & SIF_POS)) {
                  lpsi->fMask |= SIF_POS;
                  lpsi->nPos = pw->pos;
              }
  
              fChanged = TRUE;
          }
      }
  
      if (lpsi->fMask & SIF_PAGE) {
          DWORD dwMaxPage = (DWORD) abs(pw->posMax - pw->posMin) + 1;
  
          // Clip page to 0, posMax - posMin + 1
  
          if (lpsi->nPage > dwMaxPage)
              lpsi->nPage = dwMaxPage;
  
  
          if (pw->page != (int)(lpsi->nPage)) {
              pw->page = lpsi->nPage;
  
              if (!(lpsi->fMask & SIF_POS)) {
                  lpsi->fMask |= SIF_POS;
                  lpsi->nPos = pw->pos;
              }
  
              fChanged = TRUE;
          }
      }
  
      if (lpsi->fMask & SIF_POS) {
          int iMaxPos = pw->posMax - ((pw->page) ? pw->page - 1 : 0);
          // Clip pos to posMin, posMax - (page - 1).
  
          if (lpsi->nPos < pw->posMin)
              lpsi->nPos = pw->posMin;
          else if (lpsi->nPos > iMaxPos)
              lpsi->nPos = iMaxPos;
  
  
          if (pw->pos != lpsi->nPos) {
              pw->pos = lpsi->nPos;
              fChanged = TRUE;
          }
      }
  
      if (!(lpsi->fMask & SIF_RETURNOLDPOS)) {
          // Return the new position
          *lplres = pw->pos;
      }
  
      /*
       * This was added by JimA as Cairo merge but will conflict
       * with the documentation for SetScrollPos
       */
  /*
      else if (*lplres == pw->pos)
          *lplres = 0;
  */
      if (lpsi->fMask & SIF_RANGE) {
          if (*lpfScroll = (pw->posMin != pw->posMax))
              goto checkPage;
      } else if (lpsi->fMask & SIF_PAGE)
  checkPage:
          *lpfScroll = (pw->page <= (pw->posMax - pw->posMin));
  
      return fChanged;
  }
  
  
  /***************************************************************************\
  * CalcSBStuff
  *
  *
  *
  * History:
  \***************************************************************************/
  
  void CalcSBStuff(
      PWND pwnd,
      PSBCALC pSBCalc,
      BOOL fVert)
  {
      RECT    rcT;
      RECT    rcClient;
      int     cx, iTemp;
  
      //
      // Get client rectangle.  We know that scrollbars always align to the right
      // and to the bottom of the client area.
      //
      GetRect(pwnd, &rcClient, GRECT_CLIENT | GRECT_WINDOWCOORDS);
      
      if (TestWF(pwnd, WEFLAYOUTRTL)) {
          cx             = pwnd->rcWindow.right - pwnd->rcWindow.left;
          iTemp          = rcClient.left;
          rcClient.left  = cx - rcClient.right;
          rcClient.right = cx - iTemp;
      }
  
      if (fVert) {
           // Only add on space if vertical scrollbar is really there.
          if (TestWF(pwnd, WEFLEFTSCROLL)) {
              rcT.right = rcT.left = rcClient.left;
              if (TestWF(pwnd, WFVPRESENT))
                  rcT.left -= SYSMET(CXVSCROLL);
          } else {
              rcT.right = rcT.left = rcClient.right;
              if (TestWF(pwnd, WFVPRESENT))
                  rcT.right += SYSMET(CXVSCROLL);
          }
  
          rcT.top = rcClient.top;
          rcT.bottom = rcClient.bottom;
      } else {
          // Only add on space if horizontal scrollbar is really there.
          rcT.bottom = rcT.top = rcClient.bottom;
          if (TestWF(pwnd, WFHPRESENT))
              rcT.bottom += SYSMET(CYHSCROLL);
  
          rcT.left = rcClient.left;
          rcT.right = rcClient.right;
      }
  
      // If InitPwSB stuff fails (due to our heap being full) there isn't anything reasonable
      // we can do here, so just let it go through.  We won't fault but the scrollbar won't work
      // properly either...
      if (_InitPwSB(pwnd))
          CalcSBStuff2(pSBCalc, &rcT, (fVert) ? &pwnd->pSBInfo->Vert :  &pwnd->pSBInfo->Horz, fVert);
  
  }
  
  /***************************************************************************\
  *
  *  DrawCtlThumb()
  *
  \***************************************************************************/
  void DrawCtlThumb(PSBWND psb)
  {
      HBRUSH  hbr, hbrSave;
      HDC     hdc = (HDC) _GetWindowDC((PWND) psb);
  
      SBCtlSetup(psb);
  
      hbrSave = GreSelectBrush(hdc, hbr = xxxGetColorObjects((PWND) psb, hdc));
  
      DrawThumb2((PWND) psb, &psb->SBCalc, hdc, hbr, psb->fVert, psb->wDisableFlags);
  
      GreSelectBrush(hdc, hbrSave);
      _ReleaseDC(hdc);
  }
  
  
  /***************************************************************************\
  * xxxDrawThumb
  *
  *
  *
  * History:
  \***************************************************************************/
  
  void xxxDrawThumb(
      PWND pwnd,
      PSBCALC pSBCalc,
      BOOL fVert)
  {
      HBRUSH hbr, hbrSave;
      HDC hdc;
      UINT wDisableFlags;
      SBCALC SBCalc;
  
      CheckLock(pwnd);
  
      if (!pSBCalc) pSBCalc = &SBCalc;
      hdc = (HDC)_GetWindowDC(pwnd);
  
      CalcSBStuff(pwnd, &SBCalc, fVert);
      wDisableFlags = GetWndSBDisableFlags(pwnd, fVert);
  
      hbrSave = GreSelectBrush(hdc, hbr = xxxGetColorObjects(pwnd, hdc));
  
      DrawThumb2(pwnd, &SBCalc, hdc, hbr, fVert, wDisableFlags);
  
      GreSelectBrush(hdc, hbrSave);
  
      /*
       * Won't hurt even if DC is already released (which happens automatically
       * if window is destroyed during xxxSelectColorObjects)
       */
      _ReleaseDC(hdc);
  }
  
  /***************************************************************************\
  * xxxSetScrollBar
  *
  *
  *
  * History:
  \***************************************************************************/
  
  LONG xxxSetScrollBar(
      PWND pwnd,
      int code,
      LPSCROLLINFO lpsi,
      BOOL fRedraw)
  {
      BOOL        fVert;
      PSBDATA pw;
      PSBINFO pSBInfo;
      BOOL fOldScroll;
      BOOL fScroll;
      WORD        wfScroll;
      LONG     lres;
      BOOL        fNewScroll;
  
      CheckLock(pwnd);
      UserAssert(IsWinEventNotifyDeferredOK());
  
      if (fRedraw)
          // window must be visible to redraw
          fRedraw = IsVisible(pwnd);
  
      if (code == SB_CTL)
  #ifdef FE_SB // xxxSetScrollBar()
          // scroll bar control; send the control a message
          if(GETPTI(pwnd)->TIF_flags & TIF_16BIT) {
              //
              // If the target application is 16bit apps, we don't pass win40's message.
              // This fix for Ichitaro v6.3. It eats the message. It never forwards
              // the un-processed messages to original windows procedure via
              // CallWindowProc().
              //
              // Is this from xxxSetScrollPos() ?
              if(lpsi->fMask == (SIF_POS|SIF_RETURNOLDPOS)) {
                  return (int)xxxSendMessage(pwnd, SBM_SETPOS, lpsi->nPos, fRedraw);
              // Is this from xxxSetScrollRange() ?
              } else if(lpsi->fMask == SIF_RANGE) {
                  xxxSendMessage(pwnd, SBM_SETRANGE, lpsi->nMin, lpsi->nMax);
                  return TRUE;
              // Others...
              } else {
                  return (LONG)xxxSendMessage(pwnd, SBM_SETSCROLLINFO, (WPARAM) fRedraw, (LPARAM) lpsi);
              }
          } else {
              return (LONG)xxxSendMessage(pwnd, SBM_SETSCROLLINFO, (WPARAM) fRedraw, (LPARAM) lpsi);
          }
  #else
          // scroll bar control; send the control a message
          return (LONG)xxxSendMessage(pwnd, SBM_SETSCROLLINFO, (WPARAM) fRedraw, (LPARAM) lpsi);
  #endif // FE_SB
  
      fVert = (code != SB_HORZ);
  
      wfScroll = (fVert) ? WFVSCROLL : WFHSCROLL;
  
      fScroll = fOldScroll = (TestWF(pwnd, wfScroll)) ? TRUE : FALSE;
  
      /*
       * Don't do anything if we're setting position of a nonexistent scroll bar.
       */
      if (!(lpsi->fMask & SIF_RANGE) && !fOldScroll && (pwnd->pSBInfo == NULL)) {
          RIPERR0(ERROR_NO_SCROLLBARS, RIP_VERBOSE, "");
          return 0;
      }
  
      if (fNewScroll = !(pSBInfo = pwnd->pSBInfo)) {
          if ((pSBInfo = _InitPwSB(pwnd)) == NULL)
              return 0;
      }
  
      pw = (fVert) ? &(pSBInfo->Vert) : &(pSBInfo->Horz);
  
      if (!SBSetParms(pw, lpsi, &fScroll, &lres) && !fNewScroll) {
          // no change -- but if REDRAW is specified and there's a scrollbar,
          // redraw the thumb
          if (fOldScroll && fRedraw)
              goto redrawAfterSet;
  
          return lres;
      }
  
      ClrWF(pwnd, wfScroll);
  
      if (fScroll)
          SetWF(pwnd, wfScroll);
      else if (!TestWF(pwnd, (WFHSCROLL | WFVSCROLL))) {
          // if neither scroll bar is set and both ranges are 0, then free up the
          // scroll info
  
          pSBInfo = pwnd->pSBInfo;
  
          if ((pSBInfo->Horz.posMin == pSBInfo->Horz.posMax) &&
              (pSBInfo->Vert.posMin == pSBInfo->Vert.posMax)) {
              DesktopFree(pwnd->head.rpdesk, (HANDLE)(pwnd->pSBInfo));
              pwnd->pSBInfo = NULL;
          }
      }
  
      if (lpsi->fMask & SIF_DISABLENOSCROLL) {
          if (fOldScroll) {
              SetWF(pwnd, wfScroll);
              xxxEnableWndSBArrows(pwnd, code, (fScroll) ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH);
          }
      } else if (fOldScroll ^ fScroll) {
          PSBTRACK pSBTrack = PWNDTOPSBTRACK(pwnd);
          if (pSBTrack && (pwnd == pSBTrack->spwndTrack)) {
              pSBTrack->fTrackRecalc = TRUE;
          }
          xxxRedrawFrame(pwnd);
          // Note: after xxx, pSBTrack may no longer be valid (but we return now)
          return lres;
      }
  
      if (fScroll && fRedraw && (fVert ? TestWF(pwnd, WFVPRESENT) : TestWF(pwnd, WFHPRESENT))) {
          PSBTRACK pSBTrack;
  redrawAfterSet:
          xxxWindowEvent(EVENT_OBJECT_VALUECHANGE, pwnd, (fVert ? OBJID_VSCROLL : OBJID_HSCROLL),
                  INDEX_SCROLLBAR_SELF, WEF_USEPWNDTHREAD);
          
          pSBTrack = PWNDTOPSBTRACK(pwnd);
          // Bail out if the caller is trying to change the position of
          // a scrollbar that is in the middle of tracking.  We'll hose
          // TrackThumb() otherwise.
  
          if (pSBTrack && (pwnd == pSBTrack->spwndTrack) &&
                  ((BOOL)(pSBTrack->fTrackVert) == fVert) &&
                  (pSBTrack->xxxpfnSB == xxxTrackThumb)) {
              return lres;
          }
  
          xxxDrawThumb(pwnd, NULL, fVert);
          // Note: after xxx, pSBTrack may no longer be valid (but we return now)
      }
  
      return lres;
  }
  
  
  
  /***************************************************************************\
  * xxxDrawScrollBar
  *
  *
  *
  * History:
  \***************************************************************************/
  
  void xxxDrawScrollBar(
      PWND pwnd,
      HDC hdc,
      BOOL fVert)
  {
      SBCALC SBCalc;
      PSBCALC pSBCalc;
      PSBTRACK pSBTrack = PWNDTOPSBTRACK(pwnd);
  
      CheckLock(pwnd);
      if (pSBTrack && (pwnd == pSBTrack->spwndTrack) && (pSBTrack->fCtlSB == FALSE)
           && (fVert == (BOOL)pSBTrack->fTrackVert)) {
          pSBCalc = pSBTrack->pSBCalc;
      } else {
          pSBCalc = &SBCalc;
      }
      CalcSBStuff(pwnd, pSBCalc, fVert);
  
      xxxDrawSB2(pwnd, pSBCalc, hdc, fVert, GetWndSBDisableFlags(pwnd, fVert));
  }
  
  /***************************************************************************\
  * SBPosFromPx
  *
  * Compute scroll bar position from pixel location
  *
  * History:
  \***************************************************************************/
  
  int SBPosFromPx(
      PSBCALC  pSBCalc,
      int px)
  {
      if (px < pSBCalc->pxMin) {
          return pSBCalc->posMin;
      }
      if (px >= pSBCalc->pxMin + pSBCalc->cpx) {
          return (pSBCalc->posMax - (pSBCalc->page ? pSBCalc->page - 1 : 0));
      }
      if (pSBCalc->cpx)
          return (pSBCalc->posMin + EngMulDiv(pSBCalc->posMax - pSBCalc->posMin -
              (pSBCalc->page ? pSBCalc->page - 1 : 0),
              px - pSBCalc->pxMin, pSBCalc->cpx));
      else
          return (pSBCalc->posMin - 1);
  }
  
  /***************************************************************************\
  * InvertScrollHilite
  *
  *
  *
  * History:
  \***************************************************************************/
  
  void InvertScrollHilite(
      PWND pwnd,
      PSBTRACK pSBTrack)
  {
      HDC hdc;
  
      /*
       * Don't invert if the thumb is all the way at the top or bottom
       * or you will end up inverting the line between the arrow and the thumb.
       */
      if (!IsRectEmpty(&pSBTrack->rcTrack)) {
          if (pSBTrack->fTrackRecalc) {
              RecalcTrackRect(pSBTrack);
              pSBTrack->fTrackRecalc = FALSE;
          }
  
          hdc = (HDC)_GetWindowDC(pwnd);
          InvertRect(hdc, &pSBTrack->rcTrack);
          _ReleaseDC(hdc);
      }
  }
  
  /***************************************************************************\
  * xxxDoScroll
  *
  * Sends scroll notification to the scroll bar owner
  *
  * History:
  \***************************************************************************/
  
  void xxxDoScroll(
      PWND pwnd,
      PWND pwndNotify,
      int cmd,
      int pos,
      BOOL fVert
  )
  {
      TL tlpwndNotify;
  
      /*
       * Special case!!!! this routine is always passed pwnds that are
       * not thread locked, so they need to be thread locked here.  The
       * callers always know that by the time DoScroll() returns,
       * pwnd and pwndNotify could be invalid.
       */
      ThreadLock(pwndNotify, &tlpwndNotify);
      xxxSendMessage(pwndNotify, (UINT)(fVert ? WM_VSCROLL : WM_HSCROLL),
              MAKELONG(cmd, pos), (LPARAM)HW(pwnd));
  
      ThreadUnlock(&tlpwndNotify);
  }
  
  // -------------------------------------------------------------------------
  //
  //  CheckScrollRecalc()
  //
  // -------------------------------------------------------------------------
  //void CheckScrollRecalc(PWND pwnd, PSBSTATE pSBState, PSBCALC pSBCalc)
  //{
  //    if ((pSBState->pwndCalc != pwnd) || ((pSBState->nBar != SB_CTL) && (pSBState->nBar != ((pSBState->fVertSB) ? SB_VERT : SB_HORZ))))
  //    {
  //        // Calculate SB stuff based on whether it's a control or in a window
  //        if (pSBState->fCtlSB)
  //            SBCtlSetup((PSBWND) pwnd);
  //        else
  //            CalcSBStuff(pwnd, pSBCalc, pSBState->fVertSB);
  //    }
  //}
  
  
  /***************************************************************************\
  * xxxMoveThumb
  *
  * History:
  \***************************************************************************/
  
  void xxxMoveThumb(
      PWND pwnd,
      PSBCALC  pSBCalc,
      int px)
  {
      HBRUSH  hbr, hbrSave;
      HDC     hdc;
      PSBTRACK pSBTrack;
  
      CheckLock(pwnd);
  
      pSBTrack = PWNDTOPSBTRACK(pwnd);
  
      if ((pSBTrack == NULL) || (px == pSBTrack->pxOld))
          return;
  
  pxReCalc:
  
      pSBTrack->posNew = SBPosFromPx(pSBCalc, px);
  
      /* Tentative position changed -- notify the guy. */
      if (pSBTrack->posNew != pSBTrack->posOld) {
          if (pSBTrack->spwndSBNotify != NULL) {
              xxxDoScroll(pSBTrack->spwndSB, pSBTrack->spwndSBNotify, SB_THUMBTRACK, pSBTrack->posNew, pSBTrack->fTrackVert
              );
  
          }
          // After xxxDoScroll, re-evaluate pSBTrack
          REEVALUATE_PSBTRACK(pSBTrack, pwnd, "xxxMoveThumb(1)");
          if ((pSBTrack == NULL) || (pSBTrack->xxxpfnSB == NULL))
              return;
  
          pSBTrack->posOld = pSBTrack->posNew;
  
          //
          // Anything can happen after the SendMessage above!
          // Make sure that the SBINFO structure contains data for the
          // window being tracked -- if not, recalculate data in SBINFO
          //
  //        CheckScrollRecalc(pwnd, pSBState, pSBCalc);
          // when we yield, our range can get messed with
          // so make sure we handle this
  
          if (px >= pSBCalc->pxMin + pSBCalc->cpx)
          {
              px = pSBCalc->pxMin + pSBCalc->cpx;
              goto pxReCalc;
          }
  
      }
  
      hdc = _GetWindowDC(pwnd);
  
      pSBCalc->pxThumbTop = px;
      pSBCalc->pxThumbBottom = pSBCalc->pxThumbTop + pSBCalc->cpxThumb;
  
      // at this point, the disable flags are always going to be 0 --
      // we're in the middle of tracking.
      hbrSave = GreSelectBrush(hdc, hbr = xxxGetColorObjects(pwnd, hdc));
  
      // After xxxGetColorObjects, re-evaluate pSBTrack
      REEVALUATE_PSBTRACK(pSBTrack, pwnd, "xxxMoveThumb(2)");
      if (pSBTrack == NULL) {
          RIPMSG1(RIP_ERROR, "Did we use to leak hdc %#p?", hdc) ;
          _ReleaseDC(hdc);
          return;
      }
      DrawThumb2(pwnd, pSBCalc, hdc, hbr, pSBTrack->fTrackVert, 0);
      GreSelectBrush(hdc, hbrSave);
      _ReleaseDC(hdc);
  
      pSBTrack->pxOld = px;
  }
  
  /***************************************************************************\
  * zzzDrawInvertScrollArea
  *
  *
  *
  * History:
  \***************************************************************************/
  
  void zzzDrawInvertScrollArea(
      PWND pwnd,
      PSBTRACK pSBTrack,
      BOOL fHit,
      UINT cmd)
  {
      HDC hdc;
      RECT rcTemp;
      int cx, cy;
      UINT bm;
  
      if ((cmd != SB_LINEUP) && (cmd != SB_LINEDOWN)) {
          // not hitting on arrow -- just invert the area and return
          InvertScrollHilite(pwnd, pSBTrack);
  
          if (cmd == SB_PAGEUP) {
              if (fHit)
                  SetWF(pwnd, WFPAGEUPBUTTONDOWN);
              else
                  ClrWF(pwnd, WFPAGEUPBUTTONDOWN);
          } else {
              if (fHit)
                  SetWF(pwnd, WFPAGEDNBUTTONDOWN);
              else
                  ClrWF(pwnd, WFPAGEDNBUTTONDOWN);
          }
  
          zzzWindowEvent(EVENT_OBJECT_STATECHANGE, pwnd,
                  (pSBTrack->fCtlSB ? OBJID_CLIENT : (pSBTrack->fTrackVert ? OBJID_VSCROLL : OBJID_HSCROLL)),
                  ((cmd == SB_PAGEUP) ? INDEX_SCROLLBAR_UPPAGE : INDEX_SCROLLBAR_DOWNPAGE),
                  WEF_USEPWNDTHREAD);
          // Note: after zzz, pSBTrack may no longer be valid (but we return now)
          return;
      }
  
      if (pSBTrack->fTrackRecalc) {
          RecalcTrackRect(pSBTrack);
          pSBTrack->fTrackRecalc = FALSE;
      }
  
      CopyRect(&rcTemp, &pSBTrack->rcTrack);
  
      hdc = _GetWindowDC(pwnd);
  
      if (pSBTrack->fTrackVert) {
          cx = SYSMET(CXVSCROLL);
          cy = SYSMET(CYVSCROLL);
      } else {
          cx = SYSMET(CXHSCROLL);
          cy = SYSMET(CYHSCROLL);
      }
  
      if ((cx == (rcTemp.right - rcTemp.left)) &&
          (cy == (rcTemp.bottom - rcTemp.top))) {
          if (cmd == SB_LINEUP)
              bm = (pSBTrack->fTrackVert) ? OBI_UPARROW : OBI_LFARROW;
          else // SB_LINEDOWN
              bm = (pSBTrack->fTrackVert) ? OBI_DNARROW : OBI_RGARROW;
  
          if (fHit)
              bm += DOBI_PUSHED;
  
          BitBltSysBmp(hdc, rcTemp.left, rcTemp.top, bm);
      } else {
          DrawFrameControl(hdc, &rcTemp, DFC_SCROLL,
              ((pSBTrack->fTrackVert) ? DFCS_SCROLLVERT : DFCS_SCROLLHORZ) |
              ((fHit) ? DFCS_PUSHED | DFCS_FLAT : 0) |
              ((cmd == SB_LINEUP) ? DFCS_SCROLLMIN : DFCS_SCROLLMAX));
      }
  
      _ReleaseDC(hdc);
  
  
      if (cmd == SB_LINEUP) {
          if (fHit)
              SetWF(pwnd, WFLINEUPBUTTONDOWN);
          else
              ClrWF(pwnd, WFLINEUPBUTTONDOWN);
      } else {
          if (fHit)
              SetWF(pwnd, WFLINEDNBUTTONDOWN);
          else
              ClrWF(pwnd, WFLINEDNBUTTONDOWN);
      }
  
      zzzWindowEvent(EVENT_OBJECT_STATECHANGE, pwnd,
              (pSBTrack->fCtlSB ? OBJID_CLIENT : (pSBTrack->fTrackVert ? OBJID_VSCROLL : OBJID_HSCROLL)),
              (cmd == SB_LINEUP ? INDEX_SCROLLBAR_UP : INDEX_SCROLLBAR_DOWN),
              WEF_USEPWNDTHREAD);
          // Note: after zzz, pSBTrack may no longer be valid (but we return now)
  }
  
  /***************************************************************************\
  * xxxEndScroll
  *
  *
  *
  * History:
  \***************************************************************************/
  
  void xxxEndScroll(
      PWND pwnd,
      BOOL fCancel)
  {
      UINT oldcmd;
      PSBTRACK pSBTrack;
      CheckLock(pwnd);
      UserAssert(!IsWinEventNotifyDeferred());
  
      pSBTrack = PWNDTOPSBTRACK(pwnd);
      if (pSBTrack && PtiCurrent()->pq->spwndCapture == pwnd && pSBTrack->xxxpfnSB != NULL) {
  
          oldcmd = pSBTrack->cmdSB;
          pSBTrack->cmdSB = 0;
          xxxReleaseCapture();
  
          // After xxxReleaseCapture, revalidate pSBTrack
          RETURN_IF_PSBTRACK_INVALID(pSBTrack, pwnd);
  
          if (pSBTrack->xxxpfnSB == xxxTrackThumb) {
  
              if (fCancel) {
                  pSBTrack->posOld = pSBTrack->pSBCalc->pos;
              }
  
              /*
               * DoScroll does thread locking on these two pwnds -
               * this is ok since they are not used after this
               * call.
               */
              if (pSBTrack->spwndSBNotify != NULL) {
                  xxxDoScroll(pSBTrack->spwndSB, pSBTrack->spwndSBNotify,
                          SB_THUMBPOSITION, pSBTrack->posOld, pSBTrack->fTrackVert
                  );
                  // After xxxDoScroll, revalidate pSBTrack
                  RETURN_IF_PSBTRACK_INVALID(pSBTrack, pwnd);
              }
  
              if (pSBTrack->fCtlSB) {
                  DrawCtlThumb((PSBWND) pwnd);
              } else {
                  xxxDrawThumb(pwnd, pSBTrack->pSBCalc, pSBTrack->fTrackVert);
                  // Note: after xxx, pSBTrack may no longer be valid
              }
  
          } else if (pSBTrack->xxxpfnSB == xxxTrackBox) {
              DWORD lParam;
              POINT ptMsg;
  
              if (pSBTrack->hTimerSB != 0) {
                  _KillSystemTimer(pwnd, IDSYS_SCROLL);
                  pSBTrack->hTimerSB = 0;
              }
              lParam = _GetMessagePos();
              if (TestWF(pwnd, WEFLAYOUTRTL)) {
                  ptMsg.x = pwnd->rcWindow.right - GET_X_LPARAM(lParam);
              } else {
                  ptMsg.x = GET_X_LPARAM(lParam) - pwnd->rcWindow.left;
              }
              ptMsg.y = GET_Y_LPARAM(lParam) - pwnd->rcWindow.top;
              if (PtInRect(&pSBTrack->rcTrack, ptMsg)) {
                  zzzDrawInvertScrollArea(pwnd, pSBTrack, FALSE, oldcmd);
                  // Note: after zzz, pSBTrack may no longer be valid
              }
          }
  
          /*
           * Always send SB_ENDSCROLL message.
           *
           * DoScroll does thread locking on these two pwnds -
           * this is ok since they are not used after this
           * call.
           */
  
          // After xxxDrawThumb or zzzDrawInvertScrollArea, revalidate pSBTrack
          RETURN_IF_PSBTRACK_INVALID(pSBTrack, pwnd);
  
          if (pSBTrack->spwndSBNotify != NULL) {
              xxxDoScroll(pSBTrack->spwndSB, pSBTrack->spwndSBNotify,
                      SB_ENDSCROLL, 0, pSBTrack->fTrackVert);
              // After xxxDoScroll, revalidate pSBTrack
              RETURN_IF_PSBTRACK_INVALID(pSBTrack, pwnd);
          }
  
          ClrWF(pwnd, WFSCROLLBUTTONDOWN);
          ClrWF(pwnd, WFVERTSCROLLTRACK);
  
          xxxWindowEvent(EVENT_SYSTEM_SCROLLINGEND, pwnd,
                  (pSBTrack->fCtlSB ? OBJID_CLIENT :
                          (pSBTrack->fTrackVert ? OBJID_VSCROLL : OBJID_HSCROLL)),
                  INDEXID_CONTAINER, 0);
          
          // After xxxWindowEvent, revalidate pSBTrack
          RETURN_IF_PSBTRACK_INVALID(pSBTrack, pwnd);
  
          /*
           * If this is a Scroll Bar Control, turn the caret back on.
           */
          if (pSBTrack->spwndSB != NULL) {
              zzzShowCaret(pSBTrack->spwndSB);
              // After zzz, revalidate pSBTrack
              RETURN_IF_PSBTRACK_INVALID(pSBTrack, pwnd);
          }
  
  
          pSBTrack->xxxpfnSB = NULL;
  
          /*
           * Unlock structure members so they are no longer holding down windows.
           */
          Unlock(&pSBTrack->spwndSB);
          Unlock(&pSBTrack->spwndSBNotify);
          Unlock(&pSBTrack->spwndTrack);
          UserFreePool(pSBTrack);
          PWNDTOPSBTRACK(pwnd) = NULL;
      }
  }
  
  
  /***************************************************************************\
  * xxxContScroll
  *
  *
  *
  * History:
  \***************************************************************************/
  
  VOID xxxContScroll(
      PWND pwnd,
      UINT message,
      UINT_PTR ID,
      LPARAM lParam)
  {
      LONG pt;
      PSBTRACK pSBTrack = PWNDTOPSBTRACK(pwnd);
  
      UNREFERENCED_PARAMETER(message);
      UNREFERENCED_PARAMETER(ID);
      UNREFERENCED_PARAMETER(lParam);
  
      if (pSBTrack == NULL)
          return;
  
      CheckLock(pwnd);
  
      pt = _GetMessagePos();
  
      if (TestWF(pwnd, WEFLAYOUTRTL)) {
          pt = MAKELONG(pwnd->rcWindow.right - GET_X_LPARAM(pt), GET_Y_LPARAM(pt) - pwnd->rcWindow.top);
      } else {
          pt = MAKELONG( GET_X_LPARAM(pt) - pwnd->rcWindow.left, GET_Y_LPARAM(pt) - pwnd->rcWindow.top);
      }
      xxxTrackBox(pwnd, WM_NULL, 0, pt, NULL);
      // After xxxTrackBox, revalidate pSBTrack
      RETURN_IF_PSBTRACK_INVALID(pSBTrack, pwnd);
  
      if (pSBTrack->fHitOld) {
          pSBTrack->hTimerSB = _SetSystemTimer(pwnd, IDSYS_SCROLL,
                  gpsi->dtScroll / 8, xxxContScroll);
  
          /*
           * DoScroll does thread locking on these two pwnds -
           * this is ok since they are not used after this
           * call.
           */
          if (pSBTrack->spwndSBNotify != NULL) {
              xxxDoScroll(pSBTrack->spwndSB, pSBTrack->spwndSBNotify,
                      pSBTrack->cmdSB, 0, pSBTrack->fTrackVert);
              // Note: after xxx, pSBTrack may no longer be valid (but we return now)
          }
      }
  
      return;
  }
  
  /***************************************************************************\
  * xxxTrackBox
  *
  *
  *
  * History:
  \***************************************************************************/
  
  void xxxTrackBox(
      PWND pwnd,
      UINT message,
      WPARAM wParam,
      LPARAM lParam,
      PSBCALC pSBCalc)
  {
      BOOL fHit;
      POINT ptHit;
      PSBTRACK pSBTrack = PWNDTOPSBTRACK(pwnd);
      int cmsTimer;
  
      UNREFERENCED_PARAMETER(wParam);
      UNREFERENCED_PARAMETER(pSBCalc);
  
      CheckLock(pwnd);
      UserAssert(IsWinEventNotifyDeferredOK());
  
      if (pSBTrack == NULL)
          return;
  
      if (message != WM_NULL && HIBYTE(message) != HIBYTE(WM_MOUSEFIRST))
          return;
  
      if (pSBTrack->fTrackRecalc) {
          RecalcTrackRect(pSBTrack);
          pSBTrack->fTrackRecalc = FALSE;
      }
  
      ptHit.x = GET_X_LPARAM(lParam);
      ptHit.y = GET_Y_LPARAM(lParam);
      fHit = PtInRect(&pSBTrack->rcTrack, ptHit);
  
      if (fHit != (BOOL)pSBTrack->fHitOld) {
          zzzDrawInvertScrollArea(pwnd, pSBTrack, fHit, pSBTrack->cmdSB);
          // After zzz, pSBTrack may no longer be valid
          RETURN_IF_PSBTRACK_INVALID(pSBTrack, pwnd);
      }
  
      cmsTimer = gpsi->dtScroll / 8;
  
      switch (message) {
      case WM_LBUTTONUP:
          xxxEndScroll(pwnd, FALSE);
          // Note: after xxx, pSBTrack may no longer be valid
          break;
  
      case WM_LBUTTONDOWN:
          pSBTrack->hTimerSB = 0;
          cmsTimer = gpsi->dtScroll;
  
          /*
           *** FALL THRU **
           */
  
      case WM_MOUSEMOVE:
          if (fHit && fHit != (BOOL)pSBTrack->fHitOld) {
  
              /*
               * We moved back into the normal rectangle: reset timer
               */
              pSBTrack->hTimerSB = _SetSystemTimer(pwnd, IDSYS_SCROLL,
                      cmsTimer, xxxContScroll);
  
              /*
               * DoScroll does thread locking on these two pwnds -
               * this is ok since they are not used after this
               * call.
               */
              if (pSBTrack->spwndSBNotify != NULL) {
                  xxxDoScroll(pSBTrack->spwndSB, pSBTrack->spwndSBNotify,
                          pSBTrack->cmdSB, 0, pSBTrack->fTrackVert);
                  // Note: after xxx, pSBTrack may no longer be valid
              }
          }
      }
      // After xxxDoScroll or xxxEndScroll, revalidate pSBTrack
      RETURN_IF_PSBTRACK_INVALID(pSBTrack, pwnd);
      pSBTrack->fHitOld = fHit;
  }
  
  
  /***************************************************************************\
  * xxxTrackThumb
  *
  *
  *
  * History:
  \***************************************************************************/
  
  void xxxTrackThumb(
      PWND pwnd,
      UINT message,
      WPARAM wParam,
      LPARAM lParam,
      PSBCALC pSBCalc)
  {
      int px;
      PSBTRACK pSBTrack = PWNDTOPSBTRACK(pwnd);
      POINT pt;
  
      UNREFERENCED_PARAMETER(wParam);
  
      CheckLock(pwnd);
  
      if (HIBYTE(message) != HIBYTE(WM_MOUSEFIRST))
          return;
  
      if (pSBTrack == NULL)
          return;
  
      // Make sure that the SBINFO structure contains data for the
      // window being tracked -- if not, recalculate data in SBINFO
  //    CheckScrollRecalc(pwnd, pSBState, pSBCalc);
      if (pSBTrack->fTrackRecalc) {
          RecalcTrackRect(pSBTrack);
          pSBTrack->fTrackRecalc = FALSE;
      }
  
  
      pt.y = GET_Y_LPARAM(lParam);
      pt.x = GET_X_LPARAM(lParam);
      if (!PtInRect(&pSBTrack->rcTrack, pt))
          px = pSBCalc->pxStart;
      else {
          px = (pSBTrack->fTrackVert ? pt.y : pt.x) + pSBTrack->dpxThumb;
          if (px < pSBCalc->pxMin)
              px = pSBCalc->pxMin;
          else if (px >= pSBCalc->pxMin + pSBCalc->cpx)
              px = pSBCalc->pxMin + pSBCalc->cpx;
      }
  
      xxxMoveThumb(pwnd, pSBCalc, px);
  
      /*
       * We won't get the WM_LBUTTONUP message if we got here through
       * the scroll menu, so test the button state directly.
       */
      if (message == WM_LBUTTONUP || _GetKeyState(VK_LBUTTON) >= 0) {
          xxxEndScroll(pwnd, FALSE);
      }
  
  }
  
  /***************************************************************************\
  * xxxSBTrackLoop
  *
  *
  *
  * History:
  \***************************************************************************/
  
  void xxxSBTrackLoop(
      PWND pwnd,
      LPARAM lParam,
      PSBCALC pSBCalc)
  {
      MSG msg;
      UINT cmd;
      PTHREADINFO ptiCurrent;
      VOID (*xxxpfnSB)(PWND, UINT, WPARAM, LPARAM, PSBCALC);
      PSBTRACK pSBTrack;
  
      CheckLock(pwnd);
      UserAssert(IsWinEventNotifyDeferredOK());
  
      pSBTrack = PWNDTOPSBTRACK(pwnd);
  
      if ((pSBTrack == NULL) || (NULL == (xxxpfnSB = pSBTrack->xxxpfnSB)))
          // mode cancelled -- exit track loop
          return;
  
      if (pSBTrack->fTrackVert)
          SetWF(pwnd, WFVERTSCROLLTRACK);
  
      xxxWindowEvent(EVENT_SYSTEM_SCROLLINGSTART, pwnd,
              (pSBTrack->fCtlSB ? OBJID_CLIENT :
                      (pSBTrack->fTrackVert ? OBJID_VSCROLL : OBJID_HSCROLL)),
              INDEXID_CONTAINER, 0);
      // Note: after xxx, pSBTrack may no longer be valid
  
      (*xxxpfnSB)(pwnd, WM_LBUTTONDOWN, 0, lParam, pSBCalc);
      // Note: after xxx, pSBTrack may no longer be valid
  
      ptiCurrent = PtiCurrent();
  
      while (ptiCurrent->pq->spwndCapture == pwnd) {
          if (!xxxGetMessage(&msg, NULL, 0, 0)) {
              // Note: after xxx, pSBTrack may no longer be valid
              break;
          }
  
          if (!_CallMsgFilter(&msg, MSGF_SCROLLBAR)) {
              cmd = msg.message;
  
              if (msg.hwnd == HWq(pwnd) && ((cmd >= WM_MOUSEFIRST && cmd <=
                      WM_MOUSELAST) || (cmd >= WM_KEYFIRST &&
                      cmd <= WM_KEYLAST))) {
                  cmd = SystoChar(cmd, msg.lParam);
  
                  // After xxxWindowEvent, xxxpfnSB, xxxTranslateMessage or
                  // xxxDispatchMessage, re-evaluate pSBTrack.
                  REEVALUATE_PSBTRACK(pSBTrack, pwnd, "xxxTrackLoop");
                  if ((pSBTrack == NULL) || (NULL == (xxxpfnSB = pSBTrack->xxxpfnSB)))
                      // mode cancelled -- exit track loop
                      return;
  
                  (*xxxpfnSB)(pwnd, cmd, msg.wParam, msg.lParam, pSBCalc);
              } else {
                  xxxTranslateMessage(&msg, 0);
                  xxxDispatchMessage(&msg);
              }
          }
      }
  }
  
  
  /***************************************************************************\
  * xxxSBTrackInit
  *
  * History:
  \***************************************************************************/
  
  void xxxSBTrackInit(
      PWND pwnd,
      LPARAM lParam,
      int curArea,
      UINT uType)
  {
      int px;
      LPINT pwX;
      LPINT pwY;
      UINT wDisable;     // Scroll bar disable flags;
      SBCALC SBCalc;
      PSBCALC pSBCalc;
      RECT rcSB;
      PSBTRACK pSBTrack;
  
      CheckLock(pwnd);
  
  
      if (PWNDTOPSBTRACK(pwnd)) {
          RIPMSG1(RIP_WARNING, "xxxSBTrackInit: PWNDTOPSBTRACK(pwnd) == %#p",
                  PWNDTOPSBTRACK(pwnd));
          return;
      }
  
      pSBTrack = (PSBTRACK)UserAllocPoolWithQuota(sizeof(*pSBTrack), TAG_SCROLLTRACK);
      if (pSBTrack == NULL)
          return;
  
      pSBTrack->hTimerSB = 0;
      pSBTrack->fHitOld = FALSE;
  
      pSBTrack->xxxpfnSB = xxxTrackBox;
  
      pSBTrack->spwndTrack = NULL;
      pSBTrack->spwndSB = NULL;
      pSBTrack->spwndSBNotify = NULL;
      Lock(&pSBTrack->spwndTrack, pwnd);
      PWNDTOPSBTRACK(pwnd) = pSBTrack;
  
      pSBTrack->fCtlSB = (!curArea);
      if (pSBTrack->fCtlSB) {
  
          /*
           * This is a scroll bar control.
           */
          Lock(&pSBTrack->spwndSB, pwnd);
          pSBTrack->fTrackVert = ((PSBWND)pwnd)->fVert;
          Lock(&pSBTrack->spwndSBNotify, pwnd->spwndParent);
          wDisable = ((PSBWND)pwnd)->wDisableFlags;
          pSBCalc = &((PSBWND)pwnd)->SBCalc;
          pSBTrack->nBar = SB_CTL;
      } else {
  
          /*
           * This is a scroll bar that is part of the window frame.
           */
  
          //
          // Mirror the window coord of the scroll bar,
          // if it is a mirrored one
          //
          if (TestWF(pwnd,WEFLAYOUTRTL)) {
              lParam = MAKELONG(
                      pwnd->rcWindow.right - GET_X_LPARAM(lParam),
                      GET_Y_LPARAM(lParam) - pwnd->rcWindow.top);
          } else {
              lParam = MAKELONG(
                      GET_X_LPARAM(lParam) - pwnd->rcWindow.left,
                      GET_Y_LPARAM(lParam) - pwnd->rcWindow.top);
          }
  
          Lock(&pSBTrack->spwndSBNotify, pwnd);
          Lock(&pSBTrack->spwndSB, NULL);
          pSBTrack->fTrackVert = (curArea - HTHSCROLL);
          wDisable = GetWndSBDisableFlags(pwnd, pSBTrack->fTrackVert);
          pSBCalc = &SBCalc;
          pSBTrack->nBar = (curArea - HTHSCROLL) ? SB_VERT : SB_HORZ;
      }
  
      pSBTrack->pSBCalc = pSBCalc;
      /*
       *  Check if the whole scroll bar is disabled
       */
      if((wDisable & SB_DISABLE_MASK) == SB_DISABLE_MASK) {
          Unlock(&pSBTrack->spwndSBNotify);
          Unlock(&pSBTrack->spwndSB);
          Unlock(&pSBTrack->spwndTrack);
          UserFreePool(pSBTrack);
          PWNDTOPSBTRACK(pwnd) = NULL;
          return;  // It is a disabled scroll bar; So, do not respond.
      }
  
      if (!pSBTrack->fCtlSB) {
          CalcSBStuff(pwnd, pSBCalc, pSBTrack->fTrackVert);
      }
  
      pwX = (LPINT)&rcSB;
      pwY = pwX + 1;
      if (!pSBTrack->fTrackVert)
          pwX = pwY--;
  
      px = (pSBTrack->fTrackVert ? GET_Y_LPARAM(lParam) : GET_X_LPARAM(lParam));
  
      *(pwX + 0) = pSBCalc->pxLeft;
      *(pwY + 0) = pSBCalc->pxTop;
      *(pwX + 2) = pSBCalc->pxRight;
      *(pwY + 2) = pSBCalc->pxBottom;
      pSBTrack->cmdSB = (UINT)-1;
      if (px < pSBCalc->pxUpArrow) {
  
          /*
           *  The click occurred on Left/Up arrow; Check if it is disabled
           */
          if(wDisable & LTUPFLAG) {
              if(pSBTrack->fCtlSB) {   // If this is a scroll bar control,
                  zzzShowCaret(pSBTrack->spwndSB);  // show the caret before returning;
                  // After zzzShowCaret, revalidate pSBTrack
                  RETURN_IF_PSBTRACK_INVALID(pSBTrack, pwnd);
              }
  
              Unlock(&pSBTrack->spwndSBNotify);
              Unlock(&pSBTrack->spwndSB);
              Unlock(&pSBTrack->spwndTrack);
              UserFreePool(pSBTrack);
              PWNDTOPSBTRACK(pwnd) = NULL;
              return;         // Yes! disabled. Do not respond.
          }
  
          // LINEUP -- make rcSB the Up Arrow's Rectangle
          pSBTrack->cmdSB = SB_LINEUP;
          *(pwY + 2) = pSBCalc->pxUpArrow;
      } else if (px >= pSBCalc->pxDownArrow) {
  
          /*
           * The click occurred on Right/Down arrow; Check if it is disabled
           */
          if (wDisable & RTDNFLAG) {
              if (pSBTrack->fCtlSB) {    // If this is a scroll bar control,
                  zzzShowCaret(pSBTrack->spwndSB);  // show the caret before returning;
                  // After zzzShowCaret, revalidate pSBTrack
                  RETURN_IF_PSBTRACK_INVALID(pSBTrack, pwnd);
              }
  
              Unlock(&pSBTrack->spwndSBNotify);
              Unlock(&pSBTrack->spwndSB);
              Unlock(&pSBTrack->spwndTrack);
              UserFreePool(pSBTrack);
              PWNDTOPSBTRACK(pwnd) = NULL;
              return;// Yes! disabled. Do not respond.
          }
  
          // LINEDOWN -- make rcSB the Down Arrow's Rectangle
          pSBTrack->cmdSB = SB_LINEDOWN;
          *(pwY + 0) = pSBCalc->pxDownArrow;
      } else if (px < pSBCalc->pxThumbTop) {
          // PAGEUP -- make rcSB the rectangle between Up Arrow and Thumb
          pSBTrack->cmdSB = SB_PAGEUP;
          *(pwY + 0) = pSBCalc->pxUpArrow;
          *(pwY + 2) = pSBCalc->pxThumbTop;
      } else if (px < pSBCalc->pxThumbBottom) {
  
  DoThumbPos:
          /*
           * Elevator isn't there if there's no room.
           */
          if (pSBCalc->pxDownArrow - pSBCalc->pxUpArrow <= pSBCalc->cpxThumb) {
              Unlock(&pSBTrack->spwndSBNotify);
              Unlock(&pSBTrack->spwndSB);
              Unlock(&pSBTrack->spwndTrack);
              UserFreePool(pSBTrack);
              PWNDTOPSBTRACK(pwnd) = NULL;
              return;
          }
          // THUMBPOSITION -- we're tracking with the thumb
          pSBTrack->cmdSB = SB_THUMBPOSITION;
          CalcTrackDragRect(pSBTrack);
  
          pSBTrack->xxxpfnSB = xxxTrackThumb;
          pSBTrack->pxOld = pSBCalc->pxStart = pSBCalc->pxThumbTop;
          pSBTrack->posNew = pSBTrack->posOld = pSBCalc->pos;
          pSBTrack->dpxThumb = pSBCalc->pxStart - px;
  
          xxxCapture(PtiCurrent(), pwnd, WINDOW_CAPTURE);
          // After xxxCapture, revalidate pSBTrack
          RETURN_IF_PSBTRACK_INVALID(pSBTrack, pwnd);
  
          /*
           * DoScroll does thread locking on these two pwnds -
           * this is ok since they are not used after this
           * call.
           */
          if (pSBTrack->spwndSBNotify != NULL) {
              xxxDoScroll(pSBTrack->spwndSB, pSBTrack->spwndSBNotify,
                      SB_THUMBTRACK, pSBTrack->posOld, pSBTrack->fTrackVert
              );
              // Note: after xxx, pSBTrack may no longer be valid
          }
      } else if (px < pSBCalc->pxDownArrow) {
          // PAGEDOWN -- make rcSB the rectangle between Thumb and Down Arrow
          pSBTrack->cmdSB = SB_PAGEDOWN;
          *(pwY + 0) = pSBCalc->pxThumbBottom;
          *(pwY + 2) = pSBCalc->pxDownArrow;
      }
  
      /*
       * If the shift key is down, we'll position the thumb directly so it's
       * centered on the click point.
       */
      if ((uType == SCROLL_DIRECT && pSBTrack->cmdSB != SB_LINEUP && pSBTrack->cmdSB != SB_LINEDOWN) ||
              (uType == SCROLL_MENU)) {
          if (pSBTrack->cmdSB != SB_THUMBPOSITION) {
              goto DoThumbPos;
          }
          pSBTrack->dpxThumb = -(pSBCalc->cpxThumb / 2);
      }
  
      xxxCapture(PtiCurrent(), pwnd, WINDOW_CAPTURE);
      // After xxxCapture, revalidate pSBTrack
      RETURN_IF_PSBTRACK_INVALID(pSBTrack, pwnd);
  
      if (pSBTrack->cmdSB != SB_THUMBPOSITION) {
          CopyRect(&pSBTrack->rcTrack, &rcSB);
      }
  
      xxxSBTrackLoop(pwnd, lParam, pSBCalc);
  
      // After xxx, re-evaluate pSBTrack
      REEVALUATE_PSBTRACK(pSBTrack, pwnd, "xxxTrackLoop");
      if (pSBTrack) {
          Unlock(&pSBTrack->spwndSBNotify);
          Unlock(&pSBTrack->spwndSB);
          Unlock(&pSBTrack->spwndTrack);
          UserFreePool(pSBTrack);
          PWNDTOPSBTRACK(pwnd) = NULL;
      }
  }
  
  /***************************************************************************\
  * GetScrollMenu
  *
  * History:
  \***************************************************************************/
  
  PMENU xxxGetScrollMenu(
      PWND pwnd,
      BOOL fVert)
  {
      PMENU pMenu;
      PMENU *ppDesktopMenu;
  
      /*
       * Grab the menu from the desktop.  If the desktop menu
       * has not been loaded and this is not a system thread,
       * load it now.  Callbacks cannot be made from a system
       * thread or when a thread is in cleanup.
       */
      if (fVert) {
          ppDesktopMenu = &pwnd->head.rpdesk->spmenuVScroll;
      } else {
          ppDesktopMenu = &pwnd->head.rpdesk->spmenuHScroll;
      }
      pMenu = *ppDesktopMenu;
      if (pMenu == NULL && !(PtiCurrent()->TIF_flags & (TIF_SYSTEMTHREAD | TIF_INCLEANUP))) {
          UNICODE_STRING strMenuName;
  
          RtlInitUnicodeStringOrId(&strMenuName,
              fVert ? MAKEINTRESOURCE(ID_VSCROLLMENU) : MAKEINTRESOURCE(ID_HSCROLLMENU));
          pMenu = xxxClientLoadMenu(NULL, &strMenuName);
          LockDesktopMenu(ppDesktopMenu, pMenu);
      }
  
      /*
       * Return the handle to the scroll menu.
       */
      if (pMenu != NULL) {
          return _GetSubMenu(pMenu, 0);
      }
  
      return NULL;
  }
  
  /***************************************************************************\
  * xxxDoScrollMenu
  *
  * History:
  \***************************************************************************/
  
  VOID
  xxxDoScrollMenu(
      PWND pwndNotify,
      PWND pwndSB,
      BOOL fVert,
      LPARAM lParam)
  {
      PMENU pMenu;
      SBCALC SBCalc, *pSBCalc;
      UINT cmd;
      POINT pt;
      TL tlpmenu;
      UINT wDisable;
  
      /*
       * Check the compatibility flag.  Word 6.0 AV's when selecting an item
       * in this menu.
       * NOTE: If this hack is to be extended for other apps we should use
       * another bit for GACF_NOSCROLLBARCTXMENU as the current one is re-used
       *  MCostea #119380
       */
      if (GetAppCompatFlags(NULL) & GACF_NOSCROLLBARCTXMENU) {
          return;
      }
  
      /*
       * Initialize some stuff.
       */
      POINTSTOPOINT(pt, lParam);
      if (pwndSB) {
          SBCtlSetup((PSBWND)pwndSB);
          pSBCalc = &(((PSBWND)pwndSB)->SBCalc);
          wDisable = ((PSBWND)pwndSB)->wDisableFlags;
          pt.x -= pwndSB->rcWindow.left;
          pt.y -= pwndSB->rcWindow.top;
      } else {
          pSBCalc = &SBCalc;
          CalcSBStuff(pwndNotify, pSBCalc, fVert);
          wDisable = GetWndSBDisableFlags(pwndNotify, fVert);
          pt.x -= pwndNotify->rcWindow.left;
          pt.y -= pwndNotify->rcWindow.top;
      }
  
      /*
       * Make sure the scrollbar isn't disabled.
       */
      if ((wDisable & SB_DISABLE_MASK) == SB_DISABLE_MASK) {
          return;
      }
  
      /*
       * Put up a menu and scroll accordingly.
       */
      if ((pMenu = xxxGetScrollMenu(pwndNotify, fVert)) != NULL) {
          ThreadLockAlways(pMenu, &tlpmenu);
          cmd = xxxTrackPopupMenuEx(pMenu,
                                    TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY,
                                    GET_X_LPARAM(lParam),
                                    GET_Y_LPARAM(lParam),
                                    pwndNotify,
                                    NULL);
          ThreadUnlock(&tlpmenu);
          if (cmd) {
              if ((cmd & 0x00FF) == SB_THUMBPOSITION) {
                  if (pwndSB) {
                      xxxSBTrackInit(pwndSB, MAKELPARAM(pt.x, pt.y), 0, SCROLL_MENU);
                  } else {
                      xxxSBTrackInit(pwndNotify, lParam, fVert ? HTVSCROLL : HTHSCROLL, SCROLL_MENU);
                  }
              } else {
                  xxxDoScroll(pwndSB,
                              pwndNotify,
                              cmd & 0x00FF,
                              0,
                              fVert
                  );
                  xxxDoScroll(pwndSB,
                              pwndNotify,
                              SB_ENDSCROLL,
                              0,
                              fVert
                  );
              }
          }
      }
  }
  
  /***************************************************************************\
  * xxxSBWndProc
  *
  * History:
  * 08-15-95 jparsons Added guard against NULL lParam [51986]
  \***************************************************************************/
  
  LRESULT xxxSBWndProc(
      PSBWND psbwnd,
      UINT message,
      WPARAM wParam,
      LPARAM lParam)
  {
      LONG l;
      LONG lres;
      int cx;
      int cy;
      UINT cmd;
      UINT uSide;
      HDC hdc;
      RECT rc;
      POINT pt;
      BOOL fSizeReal;
      HBRUSH hbrSave;
      BOOL fSize;
      PAINTSTRUCT ps;
      UINT style;
      TL tlpwndParent;
      SCROLLINFO      si;
      LPSCROLLINFO    lpsi = &si;
      BOOL            fRedraw = FALSE;
      BOOL            fScroll;
  
      CheckLock(psbwnd);
      UserAssert(IsWinEventNotifyDeferredOK());
  
      VALIDATECLASSANDSIZE(((PWND)psbwnd), message, wParam, lParam, FNID_SCROLLBAR, WM_CREATE);
  
      style = LOBYTE(psbwnd->wnd.style);
      fSize = ((style & (SBS_SIZEBOX | SBS_SIZEGRIP)) != 0);
  
      switch (message) {
      case WM_CREATE:
          /*
           * Guard against lParam being NULL since the thunk allows it [51986]
           */
          if (lParam) {
              rc.right = (rc.left = ((LPCREATESTRUCT)lParam)->x) +
                      ((LPCREATESTRUCT)lParam)->cx;
              rc.bottom = (rc.top = ((LPCREATESTRUCT)lParam)->y) +
                      ((LPCREATESTRUCT)lParam)->cy;
              // This is because we can't just rev CardFile -- we should fix the
              // problem here in case anyone else happened to have some EXTRA
              // scroll styles on their scroll bar controls (jeffbog 03/21/94)
              if (!TestWF((PWND)psbwnd, WFWIN40COMPAT))
                  psbwnd->wnd.style &= ~(WS_HSCROLL | WS_VSCROLL);
  
              if (!fSize) {
                  l = PtrToLong(((LPCREATESTRUCT)lParam)->lpCreateParams);
                  psbwnd->SBCalc.pos = psbwnd->SBCalc.posMin = LOWORD(l);
                  psbwnd->SBCalc.posMax = HIWORD(l);
                  psbwnd->fVert = ((LOBYTE(psbwnd->wnd.style) & SBS_VERT) != 0);
                  psbwnd->SBCalc.page = 0;
              }
  
              if (psbwnd->wnd.style & WS_DISABLED)
                  psbwnd->wDisableFlags = SB_DISABLE_MASK;
  
              if (style & (SBS_TOPALIGN | SBS_BOTTOMALIGN)) {
                  if (fSize) {
                      if (style & SBS_SIZEBOXBOTTOMRIGHTALIGN) {
                          rc.left = rc.right - SYSMET(CXVSCROLL);
                          rc.top = rc.bottom - SYSMET(CYHSCROLL);
                      }
  
                      rc.right = rc.left + SYSMET(CXVSCROLL);
                      rc.bottom = rc.top + SYSMET(CYHSCROLL);
                  } else {
                      if (style & SBS_VERT) {
                          if (style & SBS_LEFTALIGN)
                              rc.right = rc.left + SYSMET(CXVSCROLL);
                          else
                              rc.left = rc.right - SYSMET(CXVSCROLL);
                      } else {
                          if (style & SBS_TOPALIGN)
                              rc.bottom = rc.top + SYSMET(CYHSCROLL);
                          else
                              rc.top = rc.bottom - SYSMET(CYHSCROLL);
                      }
                  }
  
                  xxxMoveWindow((PWND)psbwnd, rc.left, rc.top, rc.right - rc.left,
                           rc.bottom - rc.top, FALSE);
              }
          } /* if */
  
          else {
              RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING,
                      "xxxSBWndProc - NULL lParam for WM_CREATE\n") ;
          } /* else */
  
          break;
  
      case WM_SIZE:
          if (PtiCurrent()->pq->spwndFocus != (PWND)psbwnd)
              break;
  
          // scroll bar has the focus -- recalc it's thumb caret size
          // no need to DeferWinEventNotify() - see xxxCreateCaret below.
          zzzDestroyCaret();
  
              //   |             |
              //   |  FALL THRU  |
              //   V             V
  
      case WM_SETFOCUS:
          SBCtlSetup(psbwnd);
  
          cx = (psbwnd->fVert ? psbwnd->wnd.rcWindow.right - psbwnd->wnd.rcWindow.left
                              : psbwnd->SBCalc.cpxThumb) - 2 * SYSMET(CXEDGE);
          cy = (psbwnd->fVert ? psbwnd->SBCalc.cpxThumb
                              : psbwnd->wnd.rcWindow.bottom - psbwnd->wnd.rcWindow.top) - 2 * SYSMET(CYEDGE);
  
          xxxCreateCaret((PWND)psbwnd, (HBITMAP)1, cx, cy);
          zzzSetSBCaretPos(psbwnd);
          zzzShowCaret((PWND)psbwnd);
          break;
  
      case WM_KILLFOCUS:
          zzzDestroyCaret();
          break;
  
      case WM_ERASEBKGND:
  
          /*
           * Do nothing, but don't let DefWndProc() do it either.
           * It will be erased when its painted.
           */
          return (LONG)TRUE;
  
      case WM_PRINTCLIENT:
      case WM_PAINT:
          if ((hdc = (HDC)wParam) == NULL) {
              hdc = xxxBeginPaint((PWND)psbwnd, (LPPAINTSTRUCT)&ps);
          }
          if (!fSize) {
              SBCtlSetup(psbwnd);
              xxxDrawSB2((PWND)psbwnd, &psbwnd->SBCalc, hdc, psbwnd->fVert, psbwnd->wDisableFlags);
          } else {
              fSizeReal = TestWF((PWND)psbwnd, WFSIZEBOX);
              if (!fSizeReal)
                  SetWF((PWND)psbwnd, WFSIZEBOX);
  
              DrawSize((PWND)psbwnd, hdc, 0, 0);
  
              if (!fSizeReal)
                  ClrWF((PWND)psbwnd, WFSIZEBOX);
          }
  
          if (wParam == 0L)
              xxxEndPaint((PWND)psbwnd, (LPPAINTSTRUCT)&ps);
          break;
  
      case WM_GETDLGCODE:
          return DLGC_WANTARROWS;
  
      case WM_CONTEXTMENU:
              ThreadLock(psbwnd->wnd.spwndParent, &tlpwndParent);
              xxxDoScrollMenu(psbwnd->wnd.spwndParent, (PWND)psbwnd, psbwnd->fVert, lParam);
              ThreadUnlock(&tlpwndParent);
          break;
  
      case WM_NCHITTEST:
          if (style & SBS_SIZEGRIP) {
              /*
               * If the scroll bar is RTL mirrored, then
               * mirror the hittest of the grip location.
               */
              if (TestWF((PWND)psbwnd, WEFLAYOUTRTL)) {
                  return HTBOTTOMLEFT;
              } else {
                  return HTBOTTOMRIGHT;
              }
          } else {
              goto DoDefault;
          }
          break;
  
  #ifdef COLOR_HOTTRACKING
      case WM_MOUSELEAVE:
          xxxHotTrackSBCtl(psbwnd, 0, FALSE);
          psbwnd->ht = 0;
          break;
  
      case WM_MOUSEMOVE:
          {
              int ht;
  
              if (psbwnd->ht == 0) {
                  TRACKMOUSEEVENT tme = {sizeof(TRACKMOUSEEVENT), TME_LEAVE, HWq(psbwnd), 0};
                  TrackMouseEvent(&tme);
              }
  
              pt.x = GET_X_LPARAM(lParam);
              pt.y = GET_Y_LPARAM(lParam);
              ht = HitTestScrollBar((PWND)psbwnd, psbwnd->fVert, pt);
              if (psbwnd->ht != ht) {
                  xxxHotTrackSBCtl(psbwnd, ht, TRUE);
                  psbwnd->ht = ht;
              }
          }
          break;
  #endif // COLOR_HOTTRACKING
  
      case WM_LBUTTONDBLCLK:
          cmd = SC_ZOOM;
          if (fSize)
              goto postmsg;
  
          /*
           *** FALL THRU **
           */
  
      case WM_LBUTTONDOWN:
              //
              // Note that SBS_SIZEGRIP guys normally won't ever see button
              // downs.  This is because they return HTBOTTOMRIGHT to
              // WindowHitTest handling.  This will walk up the parent chain
              // to the first sizeable ancestor, bailing out at caption windows
              // of course.  That dude, if he exists, will handle the sizing
              // instead.
              //
          if (!fSize) {
              if (TestWF((PWND)psbwnd, WFTABSTOP)) {
                  xxxSetFocus((PWND)psbwnd);
              }
  
              zzzHideCaret((PWND)psbwnd);
              SBCtlSetup(psbwnd);
  
              /*
               * SBCtlSetup enters SEM_SB, and xxxSBTrackInit leaves it.
               */
              xxxSBTrackInit((PWND)psbwnd, lParam, 0, (_GetKeyState(VK_SHIFT) < 0) ? SCROLL_DIRECT : SCROLL_NORMAL);
              break;
          } else {
              cmd = SC_SIZE;
  postmsg:
              pt.x = GET_X_LPARAM(lParam);
              pt.y = GET_Y_LPARAM(lParam);
              _ClientToScreen((PWND)psbwnd, &pt);
              lParam = MAKELONG(pt.x, pt.y);
  
              /*
               * convert HT value into a move value.  This is bad,
               * but this is purely temporary.
               */
              if (TestWF(((PWND)psbwnd)->spwndParent,WEFLAYOUTRTL)) {
                  uSide = HTBOTTOMLEFT;
              } else {
                  uSide = HTBOTTOMRIGHT;
              }
              ThreadLock(((PWND)psbwnd)->spwndParent, &tlpwndParent);
              xxxSendMessage(((PWND)psbwnd)->spwndParent, WM_SYSCOMMAND,
                      (cmd | (uSide - HTSIZEFIRST + 1)), lParam);
              ThreadUnlock(&tlpwndParent);
          }
          break;
  
      case WM_KEYUP:
          switch (wParam) {
          case VK_HOME:
          case VK_END:
          case VK_PRIOR:
          case VK_NEXT:
          case VK_LEFT:
          case VK_UP:
          case VK_RIGHT:
          case VK_DOWN:
  
              /*
               * Send end scroll message when user up clicks on keyboard
               * scrolling.
               *
               * DoScroll does thread locking on these two pwnds -
               * this is ok since they are not used after this
               * call.
               */
              xxxDoScroll((PWND)psbwnd, psbwnd->wnd.spwndParent,
                      SB_ENDSCROLL, 0, psbwnd->fVert
              );
              break;
  
          default:
              break;
          }
          break;
  
      case WM_KEYDOWN:
          switch (wParam) {
          case VK_HOME:
              wParam = SB_TOP;
              goto KeyScroll;
  
          case VK_END:
              wParam = SB_BOTTOM;
              goto KeyScroll;
  
          case VK_PRIOR:
              wParam = SB_PAGEUP;
              goto KeyScroll;
  
          case VK_NEXT:
              wParam = SB_PAGEDOWN;
              goto KeyScroll;
  
          case VK_LEFT:
          case VK_UP:
              wParam = SB_LINEUP;
              goto KeyScroll;
  
          case VK_RIGHT:
          case VK_DOWN:
              wParam = SB_LINEDOWN;
  KeyScroll:
  
              /*
               * DoScroll does thread locking on these two pwnds -
               * this is ok since they are not used after this
               * call.
               */
              xxxDoScroll((PWND)psbwnd, psbwnd->wnd.spwndParent, (int)wParam,
                      0, psbwnd->fVert
              );
              break;
  
          default:
              break;
          }
          break;
  
      case WM_ENABLE:
          return xxxSendMessage((PWND)psbwnd, SBM_ENABLE_ARROWS,
                 (wParam ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH), 0);
  
      case SBM_ENABLE_ARROWS:
  
          /*
           * This is used to enable/disable the arrows in a SB ctrl
           */
          return (LONG)xxxEnableSBCtlArrows((PWND)psbwnd, (UINT)wParam);
  
      case SBM_GETPOS:
          return (LONG)psbwnd->SBCalc.pos;
  
      case SBM_GETRANGE:
          *((LPINT)wParam) = psbwnd->SBCalc.posMin;
          *((LPINT)lParam) = psbwnd->SBCalc.posMax;
          return MAKELRESULT(LOWORD(psbwnd->SBCalc.posMin), LOWORD(psbwnd->SBCalc.posMax));
  
      case SBM_GETSCROLLINFO:
          return (LONG)_SBGetParms((PWND)psbwnd, SB_CTL, (PSBDATA)&psbwnd->SBCalc, (LPSCROLLINFO) lParam);
  
      case SBM_SETRANGEREDRAW:
          fRedraw = TRUE;
  
      case SBM_SETRANGE:
          // Save the old values of Min and Max for return value
          si.cbSize = sizeof(si);
  //        si.nMin = LOWORD(lParam);
  //        si.nMax = HIWORD(lParam);
          si.nMin = (int)wParam;
          si.nMax = (int)lParam;
          si.fMask = SIF_RANGE | SIF_RETURNOLDPOS;
          goto SetInfo;
  
      case SBM_SETPOS:
          fRedraw = (BOOL) lParam;
          si.cbSize = sizeof(si);
          si.fMask = SIF_POS | SIF_RETURNOLDPOS;
          si.nPos  = (int)wParam;
          goto SetInfo;
  
      case SBM_SETSCROLLINFO:
          lpsi = (LPSCROLLINFO) lParam;
          fRedraw = (BOOL) wParam;
  SetInfo:
          fScroll = TRUE;
  
          if (SBSetParms((PSBDATA)&psbwnd->SBCalc, lpsi, &fScroll, &lres)) {
              xxxWindowEvent(EVENT_OBJECT_VALUECHANGE, (PWND)psbwnd, OBJID_CLIENT,
                      INDEX_SCROLLBAR_SELF, WEF_USEPWNDTHREAD);
          }
  
          if (!fRedraw)
              return lres;
  
  
          /*
           * We must set the new position of the caret irrespective of
           * whether the window is visible or not;
           * Still, this will work only if the app has done a xxxSetScrollPos
           * with fRedraw = TRUE;
           * Fix for Bug #5188 --SANKAR-- 10-15-89
           * No need to DeferWinEventNotify since psbwnd is locked.
           */
          zzzHideCaret((PWND)psbwnd);
          SBCtlSetup(psbwnd);
          zzzSetSBCaretPos(psbwnd);
  
              /*
               ** The following zzzShowCaret() must be done after the DrawThumb2(),
               ** otherwise this caret will be erased by DrawThumb2() resulting
               ** in this bug:
               ** Fix for Bug #9263 --SANKAR-- 02-09-90
               *
               */
  
              /*
               *********** zzzShowCaret((PWND)psbwnd); ******
               */
  
          if (_FChildVisible((PWND)psbwnd) && fRedraw) {
              UINT    wDisable;
              HBRUSH  hbrUse;
  
              if (!fScroll)
                  fScroll = !(lpsi->fMask & SIF_DISABLENOSCROLL);
  
              wDisable = (fScroll) ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH;
              xxxEnableScrollBar((PWND) psbwnd, SB_CTL, wDisable);
  
              hdc = _GetWindowDC((PWND)psbwnd);
              hbrSave = GreSelectBrush(hdc, hbrUse = xxxGetColorObjects((PWND)psbwnd, hdc));
  
                  /*
                   * Before we used to only hideshowthumb() if the mesage was
                   * not SBM_SETPOS.  I am not sure why but this case was ever
                   * needed for win 3.x but on NT it resulted in trashing the border
                   * of the scrollbar when the app called SetScrollPos() during
                   * scrollbar tracking.  - mikehar 8/26
                   */
              DrawThumb2((PWND)psbwnd, &psbwnd->SBCalc, hdc, hbrUse, psbwnd->fVert,
                           psbwnd->wDisableFlags);
              GreSelectBrush(hdc, hbrSave);
              _ReleaseDC(hdc);
          }
  
              /*
               * This zzzShowCaret() has been moved to this place from above
               * Fix for Bug #9263 --SANKAR-- 02-09-90
               */
          zzzShowCaret((PWND)psbwnd);
          return lres;
  
      case SBM_GETSCROLLBARINFO:
          return (LONG)xxxGetScrollBarInfo((PWND)psbwnd, OBJID_CLIENT, (PSCROLLBARINFO)lParam);
  
      default:
  DoDefault:
          return xxxDefWindowProc((PWND)psbwnd, message, wParam, lParam);
      }
  
      return 0L;
  }
  