//---------------------------------------------------------------------------------------
//  File : Pager.cpp
//  Description :
//        This file implements the pager control
//---------------------------------------------------------------------------------------
#include "ctlspriv.h"
#include "pager.h"

#ifdef UNIX
#include "unixstuff.h"
#endif

#define MINBUTTONSIZE   12

//Timer Flags
#define PGT_SCROLL       1

void NEAR DrawScrollArrow(HDC hdc, LPRECT lprc, WORD wControlState);

#ifdef DEBUG
#if 0
extern "C" {
extern void _InvalidateRect(HWND hwnd, LPRECT prc, BOOL fInval);
extern void _RedrawWindow(HWND hwnd, LPRECT prc, HANDLE hrgn, UINT uFlags);
extern void _SetWindowPos(HWND hwnd, HWND hwnd2, int x, int y, int cx, int cy, UINT uFlags);
};
#define InvalidateRect(hwnd, prc, fInval) _InvalidateRect(hwnd, prc, fInval)
#define RedrawWindow(hwnd, prc, hrgn, uFlags) _RedrawWindow(hwnd, prc, hrgn, uFlags)
#define SetWindowPos(hwnd, hwnd2, x, y, cx, cy, uFlags) _SetWindowPos(hwnd, hwnd2, x, y, cx, cy, uFlags)
#endif
#endif

//Public Functions
//---------------------------------------------------------------------------------------
extern "C" {

//This function registers  the pager window class
BOOL InitPager(HINSTANCE hinst)
{
    WNDCLASS wc;
    TraceMsg(TF_PAGER, "Init Pager");

    if (!GetClassInfo(hinst, WC_PAGESCROLLER, &wc)) {
        wc.lpfnWndProc     = CPager::PagerWndProc;
        wc.hCursor         = LoadCursor(NULL, IDC_ARROW);
        wc.hIcon           = NULL;
        wc.lpszMenuName    = NULL;
        wc.hInstance       = hinst;
        wc.lpszClassName   = WC_PAGESCROLLER;
        wc.hbrBackground   = (HBRUSH)(COLOR_BTNFACE + 1); // NULL;
        wc.style           = CS_GLOBALCLASS;
        wc.cbWndExtra      = sizeof(LPVOID);
        wc.cbClsExtra      = 0;

        return RegisterClass(&wc);
    }
    return TRUE;
}

}; // extern "C"

//---------------------------------------------------------------------------------------
CPager::CPager()
{
    _clrBk = g_clrBtnFace;
    
    //Initialize Static Members
    _iButtonSize = (int) g_cxScrollbar * 3 / 4;
    if (_iButtonSize < MINBUTTONSIZE) {
        _iButtonSize = MINBUTTONSIZE;
    }

    _ptLastMove.x = -1;
    _ptLastMove.y = -1;

    _cLinesPerTimeout = 0;
    _cPixelsPerLine = 0;
    _cTimeout = GetDoubleClickTime() / 8;
}

//---------------------------------------------------------------------------------------
// Static Pager Window Procedure


LRESULT CPager::PagerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CPager *pp = (CPager*)GetWindowPtr(hwnd, 0);
    if (uMsg == WM_CREATE) {
        ASSERT(!pp);
        pp = new CPager();
        if (!pp)
            return 0L;
    }

    if (pp) {
        return pp->v_WndProc(hwnd, uMsg, wParam, lParam);
    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
//---------------------------------------------------------------------------------------
LRESULT CPager::PagerDragCallback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CPager *pp = (CPager*)GetWindowPtr(hwnd, 0);

    if (pp) {
        return pp->_DragCallback(hwnd, uMsg, wParam, lParam);
    }
    return -1;
}


//---------------------------------------------------------------------------------------
// CControl Class Implementation
//---------------------------------------------------------------------------------------


LRESULT CControl::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LRESULT lres = 0;
    switch (uMsg) {

    case WM_CREATE:
        CCCreateWindow();
        SetWindowPtr(hwnd, 0, this);
        CIInitialize(&ci, hwnd, (CREATESTRUCT*)lParam);
        return v_OnCreate();

    case WM_NCCALCSIZE:
        if (v_OnNCCalcSize(wParam, lParam, &lres))
            break;
        goto DoDefault;

    case WM_SIZE:
        v_OnSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
        break;
        
    case WM_NOTIFYFORMAT:
        return CIHandleNotifyFormat(&ci, lParam);

    case WM_NOTIFY:
        return v_OnNotify(wParam, lParam);
    
    case WM_STYLECHANGED:
        v_OnStyleChanged(wParam, lParam);
        break;

    case WM_COMMAND:
        return v_OnCommand(wParam, lParam);

    case WM_NCPAINT:
        v_OnNCPaint();
        goto DoDefault;

    case WM_PRINTCLIENT:
    case WM_PAINT:
        _OnPaint((HDC)wParam);
        break;
        
    case WM_DESTROY:
        CCDestroyWindow();
        SetWindowLongPtr(hwnd, 0, 0);
        delete this;
        break;

    case TB_SETPARENT:
        {
            HWND hwndOld = ci.hwndParent;

            ci.hwndParent = (HWND)wParam;
            return (LRESULT)hwndOld;
        }


    default:
        if (CCWndProc(&ci, uMsg, wParam, lParam, &lres))
            return lres;
DoDefault:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    return lres;
}

//---------------------------------------------------------------------------------------
BOOL CControl::v_OnNCCalcSize(WPARAM wParam, LPARAM lParam, LRESULT *plres)
{
    return FALSE;
}

//---------------------------------------------------------------------------------------
DWORD CControl::v_OnStyleChanged(WPARAM wParam, LPARAM lParam)
{
    LPSTYLESTRUCT lpss = (LPSTYLESTRUCT) lParam;
    DWORD dwChanged = 0;    
    if (wParam == GWL_STYLE) {
        ci.style = lpss->styleNew;

        dwChanged = (lpss->styleOld ^ lpss->styleNew);
    } else if (wParam == GWL_EXSTYLE) {
        //
        // Save the new ex-style bits
        //
        dwChanged    = (lpss->styleOld ^ lpss->styleNew);
        ci.dwExStyle = lpss->styleNew;
    }

    TraceMsg(TF_PAGER, "cctl.v_osc: style=%x ret dwChged=%x", ci.style, dwChanged);
    return dwChanged;
}

//---------------------------------------------------------------------------------------
void CControl::_OnPaint(HDC hdc)
{
    if (hdc) {
        v_OnPaint(hdc);
    } else {
        PAINTSTRUCT ps;
        hdc = BeginPaint(ci.hwnd, &ps);
        v_OnPaint(hdc);
        EndPaint(ci.hwnd, &ps);
    }
}

//---------------------------------------------------------------------------------------
//  CPager Class Implementation
//---------------------------------------------------------------------------------------
inline int CPager::_GetButtonSize()
{
    return _iButtonSize;
}

//---------------------------------------------------------------------------------------

LRESULT CPager::_DragCallback(HWND hwnd, UINT code, WPARAM wp, LPARAM lp)
{
    LRESULT lres = -1;
    switch (code)
    {
    case DPX_DRAGHIT:
        if (lp)
        {
            POINT pt; 
            int iButton;
            pt.x = ((POINTL *)lp)->x;
            pt.y = ((POINTL *)lp)->y;

            MapWindowPoints(NULL, ci.hwnd, &pt, 1);

            iButton = _HitTest(pt.x, pt.y);

            if (iButton >= 0) 
            {
                if(!_fTimerSet)
                {
                    _fTimerSet = TRUE;
                    _iButtonTrack = iButton;
                    SetTimer(ci.hwnd, PGT_SCROLL, _cTimeout, NULL);
                }

            } else {
                _KillTimer();
                _iButtonTrack = -1;
            }
        }
        else
            lres = -1;
        break;

    case DPX_LEAVE:
        _KillTimer();
        _iButtonTrack = -1;
        break;

    default: 
        lres = -1;
        break;
    }
    return lres;
}

//---------------------------------------------------------------------------------------
void CPager::_NeedScrollbars(RECT rc)
{  
    int parentheight;
    int childheight;
    POINT ptPos = _ptPos;
   
    if( ci.style & PGS_HORZ ) {
        FlipRect(&rc);
        FlipPoint(&ptPos);
    }
    
    //Get Parent Window height
    parentheight = RECTHEIGHT(rc);

    //Get Child Window height
    rc = _rcChildIdeal;
    if (ci.style & PGS_HORZ ) {
        FlipRect(&rc);
    }
    
    childheight = RECTHEIGHT(rc);

    TraceMsg(TF_PAGER, "cps.nsb: cyChild=%d cyParent=%d _yPos=%d", childheight, parentheight, ptPos.y);

    if (childheight < parentheight ) 
    {
        ptPos.y = 0;
    }

    int iButton = _HitTestCursor();
    //See if we need top scrollbar
    if (ptPos.y > 0 ) {

        // if this button is the one that is hot tracked and the style is not PGS_AUTOSCROLL
        // then we set the state to PGF_HOT otherwise the state is set to PGF_NORMAL
        _dwState[PGB_TOPORLEFT] |= PGF_NORMAL;
        _dwState[PGB_TOPORLEFT] &= ~PGF_GRAYED;

    } else {
        if (!(ci.style & PGS_AUTOSCROLL) && (iButton == PGB_TOPORLEFT || _iButtonTrack == PGB_TOPORLEFT)) {
            _dwState[PGB_TOPORLEFT] |= PGF_GRAYED;
        } else {
            _dwState[PGB_TOPORLEFT] = PGF_INVISIBLE;
        }
    }

    if (_dwState[PGB_TOPORLEFT] != PGF_INVISIBLE)
    {
        parentheight -= _GetButtonSize();
    }
    
    //See if we need botton scrollbar
    if ((childheight - ptPos.y) > parentheight ) {
        //We need botton scroll bar

        // if this button is the one that is hot tracked and the style is not PGS_AUTOSCROLL
        // then we set the state to PGF_HOT otherwise the state is set to PGF_NORMAL
        _dwState[PGB_BOTTOMORRIGHT] |= PGF_NORMAL;
        _dwState[PGB_BOTTOMORRIGHT] &= ~PGF_GRAYED;
        
    } else {
        
        if (!(ci.style & PGS_AUTOSCROLL) && (iButton == PGB_BOTTOMORRIGHT || _iButtonTrack == PGB_BOTTOMORRIGHT)) {
            _dwState[PGB_BOTTOMORRIGHT] |= PGF_GRAYED;
        } else {
            _dwState[PGB_BOTTOMORRIGHT] = PGF_INVISIBLE;
        }
    }
}
//---------------------------------------------------------------------------------------
BOOL CPager::v_OnNCCalcSize(WPARAM wParam, LPARAM lParam, LRESULT *plres)
{    
    *plres = DefWindowProc(ci.hwnd, WM_NCCALCSIZE, wParam, lParam ) ;
    if (wParam) {
        BOOL bHorzMirror = ((ci.dwExStyle & RTL_MIRRORED_WINDOW) && (ci.style & PGS_HORZ));
        DWORD dwStateOld[2];
        NCCALCSIZE_PARAMS* pnp = (NCCALCSIZE_PARAMS*)lParam;
        _rcDefClient = pnp->rgrc[0];
        InflateRect(&_rcDefClient, -_iBorder, -_iBorder);
        _GetChildSize();
        
        dwStateOld[0] = _dwState[0];
        dwStateOld[1] = _dwState[1];
        _NeedScrollbars(pnp->rgrc[0]);

        // invalidate only if something has changed to force a new size
        if ((dwStateOld[0] != _dwState[0] && (dwStateOld[0] == PGF_INVISIBLE || _dwState[0] == PGF_INVISIBLE)) ||
            (dwStateOld[1] != _dwState[1] && (dwStateOld[1] == PGF_INVISIBLE || _dwState[1] == PGF_INVISIBLE)) 
           ) {
            RedrawWindow(ci.hwnd, NULL,NULL,RDW_INVALIDATE|RDW_ERASE);
        }

        // Check and change for horizontal mode
        if( ci.style & PGS_HORZ ) {
            FlipRect(&(pnp->rgrc[0]));
        }
    
        if( _dwState[PGB_TOPORLEFT] != PGF_INVISIBLE ) {
            //
            // Check for RTL mirrored window
            // 
            if (bHorzMirror)
                pnp->rgrc[0].bottom -= _GetButtonSize();
            else
                pnp->rgrc[0].top += _GetButtonSize();
        } else
            pnp->rgrc[0].top += _iBorder;

        if( _dwState[PGB_BOTTOMORRIGHT] != PGF_INVISIBLE ) {
            //
            // Check for RTL mirrored window
            // 
            if (bHorzMirror)
                pnp->rgrc[0].top += _GetButtonSize();
            else
                pnp->rgrc[0].bottom -= _GetButtonSize();
        } else
            pnp->rgrc[0].bottom -= _iBorder;
   
        if (pnp->rgrc[0].bottom < pnp->rgrc[0].top)
            pnp->rgrc[0].bottom = pnp->rgrc[0].top;
        
        //Change back
        if( ci.style & PGS_HORZ ) {
            FlipRect(&(pnp->rgrc[0]));
        }
    }

    return TRUE;
}

int CPager::_HitTestCursor()
{
    POINT pt;
    GetCursorPos(&pt);
    return _HitTestScreen(&pt);
}

int CPager::_HitTestScreen(POINT* ppt)
{
    RECT rc, rc1;
    GetWindowRect(ci.hwnd, &rc);

    if (!PtInRect(&rc, *ppt)) {
        return -1;
    }
    //Get the button Rects;
    rc  = _GetButtonRect(PGB_TOPORLEFT);
    rc1 = _GetButtonRect(PGB_BOTTOMORRIGHT);

    
    if (PtInRect(&rc, *ppt)) {
        return (_dwState[PGB_TOPORLEFT] != PGF_INVISIBLE ? PGB_TOPORLEFT : -1);
    }else if (PtInRect(&rc1, *ppt)) {
        return (_dwState[PGB_BOTTOMORRIGHT] != PGF_INVISIBLE ? PGB_BOTTOMORRIGHT : -1);
    }

    return -1;
}

//---------------------------------------------------------------------------------------
int CPager::_HitTest(int x, int y)
{
    POINT pt;

    pt.x = x;
    pt.y = y;
    
    ClientToScreen(ci.hwnd, &pt);
    return _HitTestScreen(&pt);
}

//---------------------------------------------------------------------------------------
void CPager::_DrawBlank(HDC hdc, int button)
{
    RECT rc;
    UINT uFlags = 0;
    int iHeight;
    BOOL fRelDC  = FALSE;
    
    if (hdc == NULL) {
        hdc = GetWindowDC(ci.hwnd);
        fRelDC = TRUE;
    }
     
    GetWindowRect(ci.hwnd, &rc);
    MapWindowRect(NULL, ci.hwnd, &rc);

    // client to window coordinates    
    OffsetRect(&rc, -rc.left, -rc.top);

    //Check for horizontal mode
    if( ci.style & PGS_HORZ ) {
        FlipRect(&rc);
    }

    iHeight = _dwState[button] == PGF_INVISIBLE ? _iBorder : _GetButtonSize();
    switch(button) {
    case PGB_TOPORLEFT:
        rc.bottom = rc.top + iHeight;
        break;

    case PGB_BOTTOMORRIGHT:
        rc.top = rc.bottom - iHeight;
        break;
    }

    if( ci.style & PGS_HORZ ) {
        FlipRect(&rc);
    }

    FillRectClr(hdc, &rc, _clrBk);
    if (fRelDC)
        ReleaseDC(ci.hwnd, hdc);
}

//---------------------------------------------------------------------------------------
void CPager::_DrawButton(HDC hdc, int button)
{
    RECT rc;
    UINT uFlags = 0;
    BOOL fRelDC = FALSE;
    GetWindowRect(ci.hwnd, &rc);
    MapWindowRect(NULL, ci.hwnd, &rc);
    int state = _dwState[button];
    
    if (state == PGF_INVISIBLE)
        return;
    
     if (hdc == NULL) {
        hdc = GetWindowDC(ci.hwnd);
        fRelDC = TRUE;
     }
    
    if (state & PGF_GRAYED ) {
        uFlags |= DCHF_INACTIVE;
    } else if (state & PGF_DEPRESSED ) {
        uFlags |= DCHF_PUSHED;
    } else if (state & PGF_HOT ) {
        uFlags |=  DCHF_HOT;
    }

    // screen to window coordinates    
    OffsetRect(&rc, -rc.left, -rc.top);

    //Check for horizontal mode
    if( ci.style & PGS_HORZ ) {
        FlipRect(&rc);
    }
    
    if( ci.style & PGS_HORZ ) 
        uFlags |= DCHF_HORIZONTAL;
    
    if (button == PGB_BOTTOMORRIGHT) 
        uFlags |= DCHF_FLIPPED;

    switch(button) {
    case PGB_TOPORLEFT:
        rc.bottom = rc.top + _GetButtonSize();
        rc.left  += _iBorder;
        rc.right -= _iBorder;
        break;

    case PGB_BOTTOMORRIGHT:
        rc.top = rc.bottom - _GetButtonSize();
        rc.left  += _iBorder;
        rc.right -= _iBorder;
        break;
    default:
        ASSERT(FALSE);
    }

    if( ci.style & PGS_HORZ ) {
        FlipRect(&rc);
    }

    SetBkColor(hdc, _clrBk);
    DrawScrollArrow(hdc, &rc, uFlags);

    if (fRelDC)
        ReleaseDC(ci.hwnd, hdc);
}

//---------------------------------------------------------------------------------------
void CPager::v_OnNCPaint()
{
    HDC hdc = GetWindowDC(ci.hwnd);
    _DrawBlank(hdc, PGB_TOPORLEFT);
    _DrawButton(hdc, PGB_TOPORLEFT);
    
    _DrawBlank(hdc, PGB_BOTTOMORRIGHT);                        
    _DrawButton(hdc, PGB_BOTTOMORRIGHT);
    ReleaseDC(ci.hwnd, hdc);
}

//---------------------------------------------------------------------------------------
void CPager::v_OnPaint(HDC hdc)
{
    //There's nothing to paint in the client area.
}
//---------------------------------------------------------------------------------------
BOOL CPager::_OnPrint(HDC hdc, UINT uFlags)
{
    //We'll be partying with the hdc in this function so save it.
    int iDC = SaveDC(hdc);

    //Print only the Non Client Area.
    if (uFlags & PRF_NONCLIENT) {        
        int cx = 0;
        int cy = 0;
        RECT rc;


         //Draw the top/left button 
        _DrawBlank(hdc, PGB_TOPORLEFT);
        _DrawButton(hdc, PGB_TOPORLEFT);

        //Draw the bottom/left button
        _DrawBlank(hdc, PGB_BOTTOMORRIGHT);                        
        _DrawButton(hdc, PGB_BOTTOMORRIGHT);

        //Is the top button visible
        if (_dwState[PGB_TOPORLEFT] != PGF_INVISIBLE) {
            //yes, find the space taken
            if ( ci.style & PGS_HORZ ) {
                cx = _GetButtonSize();
            }else {
                cy = _GetButtonSize();
            }

        }
        //Restrict the child draw area to our client area    
        GetClientRect(ci.hwnd, &rc);
        IntersectClipRect(hdc, cx, cy, cx + RECTWIDTH(rc), cy + RECTHEIGHT(rc));  

        //Since We have drawn the non client area, Nuke the PRF_NONCLIENT flag         
        uFlags &= ~PRF_NONCLIENT;
        
    }

    //Pass it to the def window proc for default processing
    DefWindowProc(ci.hwnd, WM_PRINT, (WPARAM)hdc, (LPARAM)uFlags);
    //Restore the saved  DC 
    RestoreDC(hdc, iDC);
    return TRUE;
}

//---------------------------------------------------------------------------------------
LRESULT CPager::v_OnCommand(WPARAM wParam, LPARAM lParam)
{
    // forward to parent
    return SendMessage(ci.hwndParent, WM_COMMAND, wParam, lParam);
}
//---------------------------------------------------------------------------------------
LRESULT CPager::v_OnNotify(WPARAM wParam, LPARAM lParam)
{
    // forward to parent
    LPNMHDR lpNmhdr = (LPNMHDR)lParam;
    
    return SendNotifyEx(ci.hwndParent, (HWND) -1,
                         lpNmhdr->code, lpNmhdr, ci.bUnicode);
}


//---------------------------------------------------------------------------------------
DWORD CPager::v_OnStyleChanged(WPARAM wParam, LPARAM lParam)
{
    DWORD dwChanged = CControl::v_OnStyleChanged(wParam, lParam);

    if (dwChanged & PGS_DRAGNDROP) {
        if ((ci.style & PGS_DRAGNDROP) && !_hDragProxy) {

            _hDragProxy = CreateDragProxy(ci.hwnd, PagerDragCallback, TRUE);

        } else  if (! (ci.style & PGS_DRAGNDROP)  && _hDragProxy) {

            DestroyDragProxy(_hDragProxy);
        }
    }
    
    if (dwChanged)
        CCInvalidateFrame(ci.hwnd);     // SWP_FRAMECHANGED etc.
    return dwChanged;
}


//---------------------------------------------------------------------------------------

LRESULT CPager::v_OnCreate()
{
    if (ci.style & PGS_DRAGNDROP)
        _hDragProxy = CreateDragProxy(ci.hwnd, PagerDragCallback, TRUE);
    return TRUE;
}
//---------------------------------------------------------------------------------------
void CPager::_GetChildSize()
{
    if (_hwndChild) {

        RECT rc;
        NMPGCALCSIZE nmpgcalcsize;
        int width , height;
        rc = _rcDefClient;

        if( ci.style & PGS_HORZ ) {
            nmpgcalcsize.dwFlag = PGF_CALCWIDTH;
        } else {
            nmpgcalcsize.dwFlag  = PGF_CALCHEIGHT;
        }
        nmpgcalcsize.iWidth  = RECTWIDTH(rc);    // pager width
        nmpgcalcsize.iHeight = RECTHEIGHT(rc);  // best-guess for child

        CCSendNotify(&ci, PGN_CALCSIZE, &nmpgcalcsize.hdr);

        if( ci.style & PGS_HORZ ) {
            width  = nmpgcalcsize.iWidth;
            height = RECTHEIGHT(rc);
        } else {
            width  = RECTWIDTH(rc);
            height = nmpgcalcsize.iHeight;
        }

        GetWindowRect(_hwndChild, &rc);
        MapWindowRect(NULL, ci.hwnd, &rc);
        if( ci.style & PGS_HORZ ) {
            rc.top = _iBorder;
        } else {
            rc.left = _iBorder;
        }
        rc.right = rc.left + width;
        rc.bottom = rc.top + height;
        _rcChildIdeal = rc;
    }
}

//---------------------------------------------------------------------------------------
void CPager::v_OnSize(int x, int y)
{
    if (_hwndChild) {
        RECT rc = _rcChildIdeal;
        _SetChildPos(&rc, 0);   // SetWindowPos
    }
}

//---------------------------------------------------------------------------------------
//***   _SetChildPos -- SetWindowPos of child, w/ validation
// NOTES
//  'validation' means in sane state -- min size, and not off end.
//  WARNING: we don't update *prcChild.
//  BUGBUG what happens if we're called w/ NOMOVE or NOSIZE?
void CPager::_SetChildPos(IN RECT * prcChild, UINT uFlags)
{
    POINT ptPos = _ptPos;
    RECT rcChild = *prcChild;
    RECT rcPager;

    ASSERT(!(uFlags & SWP_NOMOVE));     // won't work

    // BUGBUG (scotth): is it okay that _hwndChild is NULL sometimes?
    //  If so, should this whole function be wrapped with if (_hwndChild)
    //  or just the call to SetWindowPos below?
    ASSERT(IS_VALID_HANDLE(_hwndChild, WND));

    rcPager = _rcDefClient;


    if ( ci.style & PGS_HORZ ) {
        FlipPoint(&ptPos);
        FlipRect(&rcChild);
        FlipRect(&rcPager);
    }

    
    int yNew = ptPos.y;

    if (RECTHEIGHT(rcChild) < RECTHEIGHT(rcPager)) {
        // force to min height

        // this handles the case where: i have an ISFBand that fills up the
        // whole pager, i stretch the pager width, and the ISFBand reformats
        // to take less height, so it shrinks its height and ends up shorter
        // than the pager.
        TraceMsg(TF_PAGER, "cps.s: h=%d h'=%d", RECTHEIGHT(rcChild), RECTHEIGHT(rcPager));
        ASSERT(!(uFlags & SWP_NOSIZE));     // won't work
        rcChild.bottom = rcChild.top + RECTHEIGHT(rcPager);
        yNew = 0;
    }

    // Maximum we can scroll is child height minus pager height.
    // Here rcPager also includes scrollbutton so  we need to add that also
    /*
          ___________  Button Width
         |
         V  ---------------- Max we can scroll (yMax)
         __ |
        /  \V
         - ---------pager-----------
        |  |-------------------------|--------------------------------
        | ||                         |                                |
        | ||    child                |                                |
        |  |-------------------------|--------------------------------
         - -------------------------
        \/\/
Border  |  |
   <-----  -------------->We need to take care of this gap.
       \-----------------------------/
        ^
        |______  RECTHEIGHT(rcChild) - RECTHEIGHT(rcPager)
       
            rcPager
     We need to add the difference between the button size and border to 
    */
    int yMax = RECTHEIGHT(rcChild) - RECTHEIGHT(rcPager) + (_GetButtonSize() - _iBorder);

    // make sure we don't end up off the top/end, and we always show
    // at least 1 page worth (if we have that much)
    // n.b. pager can override client's policy (BUGBUG?)
    if (yNew < 0) {
        // 1st page
        yNew = 0;
    } else if (yNew  > yMax) {
        // last page
        yNew = yMax;
    }

    int yOffset = yNew;
    
    // When the top button is grayed we do not want to display our child away from the button . 
    // it should be drawn right below the button. For this we tweak the position of the child window.

    //Check for the condition of grayed top button in which case we need to set position even behind
    // so that the child window falls below the grayed button
    if( _dwState[PGB_TOPORLEFT] & PGF_GRAYED )
    {
        yOffset += (_GetButtonSize() - _iBorder);
    }

    //yOffset is the tweaked value. Its just for making the child window to appear below the grayed button
    
    OffsetRect(&rcChild, 0, -yOffset - rcChild.top);

    //yNew is the actual logical positon of the window .
    ptPos.y = yNew;


    if (ci.style & PGS_HORZ) {
        // restore for copy and SWP
        FlipPoint(&ptPos);
        FlipRect(&rcChild);
    }

    _ptPos = ptPos;

    SetWindowPos(_hwndChild, NULL, rcChild.left, rcChild.top, RECTWIDTH(rcChild), RECTHEIGHT(rcChild), uFlags);

    return;
}
//---------------------------------------------------------------------------------------
//***   PGFToPGNDirection -- convert PGB_TOPORLEFT/btmorright to up/down/left/right
// NOTES
//  BUGBUG maybe PGN_* should we just take the PGF flags?
//  BUGBUG should make a macro (including some ordering magic)
DWORD CPager::_PGFToPGNDirection(DWORD dwDir)
{
    ASSERT(dwDir == PGB_TOPORLEFT || dwDir == PGB_BOTTOMORRIGHT);
    if (ci.style & PGS_HORZ) {
        return (dwDir == PGB_TOPORLEFT) ? PGF_SCROLLLEFT : PGF_SCROLLRIGHT;
    }
    else {
        return (dwDir == PGB_TOPORLEFT) ? PGF_SCROLLUP : PGF_SCROLLDOWN;
    }
}
//---------------------------------------------------------------------------------------
void CPager::_Scroll(DWORD dwDirection)
{
    RECT rc;
    NMPGSCROLL nmpgscroll;
    int iXoffset =0, iYoffset=0;
    WORD fwKeys = 0;
    int iNewPos ;
    
    // if grayed, you can't scroll.
    if (_dwState[dwDirection] & PGF_GRAYED)
        return;

    if (GetKeyState(VK_CONTROL) < 0 )
        fwKeys |= PGK_CONTROL;

    if (GetKeyState(VK_SHIFT) < 0 )
        fwKeys |= PGK_SHIFT;

    if (GetKeyState(VK_MENU) < 0 )
        fwKeys |= PGK_MENU;

    dwDirection = _PGFToPGNDirection(dwDirection);

    // set some defaults
    GetClientRect(ci.hwnd, &rc);
    nmpgscroll.fwKeys  = fwKeys;
    nmpgscroll.rcParent = rc;
    nmpgscroll.iXpos  = _ptPos.x;
    nmpgscroll.iYpos  = _ptPos.y;
    nmpgscroll.iDir   = dwDirection;

    int iScroll = (ci.style & PGS_HORZ) ? RECTWIDTH(rc) : RECTHEIGHT(rc);
    if (_cLinesPerTimeout)
        iScroll = _cLinesPerTimeout  * _cPixelsPerLine;

    nmpgscroll.iScroll = iScroll;

    // let client override
    CCSendNotify(&ci, PGN_SCROLL, &nmpgscroll.hdr);

    // do it
    switch (dwDirection)
    {
        case PGF_SCROLLDOWN:
            iNewPos = _ptPos.y + nmpgscroll.iScroll;
            break;

        case PGF_SCROLLUP:
            iNewPos = _ptPos.y - nmpgscroll.iScroll;
            break;

        case PGF_SCROLLRIGHT:
            iNewPos = _ptPos.x + nmpgscroll.iScroll;
            break;

        case PGF_SCROLLLEFT:
            iNewPos = _ptPos.x - nmpgscroll.iScroll;
            break;
    }

    _OnSetPos(iNewPos);

}
//---------------------------------------------------------------------------------------
void CPager::_OnLButtonChange(UINT uMsg,LPARAM lParam)
{
    POINT pt;
    int iButton;
    pt.x = GET_X_LPARAM(lParam);
    pt.y = GET_Y_LPARAM(lParam);

    iButton = _HitTest(pt.x, pt.y);
    
    if( uMsg == WM_LBUTTONDOWN ) {

        // Check the button is valid and is not grayed 
        // if it is grayed then dont do anything
        if (iButton >= 0) {
            SetCapture(ci.hwnd);
            _fOwnsButtonDown = TRUE;
            _iButtonTrack = iButton;
            _dwState[iButton] |= PGF_DEPRESSED;
            _DrawButton(NULL, iButton);
            _Scroll(iButton);
            SetTimer(ci.hwnd, PGT_SCROLL, _cTimeout * 4, NULL);
        }
            
    } else {
        if (_iButtonTrack >= 0) {
            _dwState[_iButtonTrack] &= ~PGF_DEPRESSED;
            _DrawButton(NULL, _iButtonTrack);
            _iButtonTrack = -1;
        }
        _KillTimer();
        
        if (iButton < 0)
            _OnMouseLeave();
    }
}
//---------------------------------------------------------------------------------------
RECT  CPager :: _GetButtonRect(int iButton)
{
    RECT rc;

    GetWindowRect(ci.hwnd, &rc);

    if( ci.style & PGS_HORZ ) {
        FlipRect(&rc);
    }

    //
    // Mirror the rects if the parent is mirrored
    //
    if (((ci.dwExStyle & RTL_MIRRORED_WINDOW) && (ci.style & PGS_HORZ))) {
        switch (iButton) {
        case PGB_TOPORLEFT:
            iButton = PGB_BOTTOMORRIGHT;
            break;

        case PGB_BOTTOMORRIGHT:
            iButton = PGB_TOPORLEFT;
            break;
        }
    }

    switch(iButton) {
    case PGB_TOPORLEFT:
        rc.bottom = rc.top +  _GetButtonSize();        
        rc.left  += _iBorder;
        rc.right -= _iBorder;
        break;
        
    case PGB_BOTTOMORRIGHT:
        rc.top  = rc.bottom - _GetButtonSize();
        rc.left  += _iBorder;
        rc.right -= _iBorder;
        break;
    }

    if( ci.style & PGS_HORZ ) {
        FlipRect(&rc);
    }
    return rc;
}

//---------------------------------------------------------------------------------------
void CPager :: _OnMouseLeave()
{
    //Whether we leave the window (WM_MOUSELEAVE) or Leave one of the scroll buttons (WM_MOUSEMOVE)
    // We do the same thing. 

    // We are leaving the pager window.
    if (GetCapture() == ci.hwnd) {
        CCReleaseCapture(&ci);
    }

    // if we are tracking some button then release that mouse and that button
    if (_iButtonTrack >= 0)  {
        _iButtonTrack = -1;
    }
    
    if (_dwState[PGB_TOPORLEFT] & (PGF_HOT | PGF_DEPRESSED)) {
        _dwState[PGB_TOPORLEFT] &= ~(PGF_HOT | PGF_DEPRESSED);
        _DrawButton(NULL, PGB_TOPORLEFT);
    }
    
    if (_dwState[PGB_BOTTOMORRIGHT] & (PGF_HOT | PGF_DEPRESSED)) {
        _dwState[PGB_BOTTOMORRIGHT] &= ~(PGF_HOT | PGF_DEPRESSED);
        _DrawButton(NULL, PGB_BOTTOMORRIGHT);
    }

    _KillTimer();
    _fOwnsButtonDown = FALSE;
    //If any of the button is in gray state then it needs to be removed.
    if ((_dwState[PGB_TOPORLEFT] & PGF_GRAYED) || (_dwState[PGB_BOTTOMORRIGHT] & PGF_GRAYED))  {
        //This forces a recalc for scrollbars and removes those that are not needed
        CCInvalidateFrame(ci.hwnd);
    }
}


//---------------------------------------------------------------------------------------
void CPager::_OnMouseMove(WPARAM wParam, LPARAM lparam) 
{
    RECT rc;
    POINT pt;
    int iButton;

    pt.x = GET_X_LPARAM(lparam);
    pt.y = GET_Y_LPARAM(lparam);

    // Ignore zero-mouse moves
    if (pt.x == _ptLastMove.x && pt.y == _ptLastMove.y)
        return;

    _ptLastMove = pt;
    iButton = _HitTest(pt.x, pt.y);

    if (_iButtonTrack >= 0 ) 
    {        
        
        if (_dwState[_iButtonTrack] != PGF_INVISIBLE)
        {
            //Some Button is pressed right now
            ClientToScreen(ci.hwnd,  &pt);
            rc = _GetButtonRect(_iButtonTrack);

            DWORD dwOldState = _dwState[_iButtonTrack];
            if (PtInRect(&rc, pt)) 
            {
                _dwState[_iButtonTrack] |= PGF_DEPRESSED;
            } 
            else 
            {
                _dwState[_iButtonTrack] &= ~PGF_DEPRESSED;
            }
        
            if (dwOldState != _dwState[_iButtonTrack]) 
                _DrawButton(NULL, _iButtonTrack);
        }
        
        // if we were tracking it, but the mouse is up and gone
        if (GetCapture() == ci.hwnd && !((wParam & MK_LBUTTON) || (ci.style & PGS_AUTOSCROLL)) && iButton != _iButtonTrack)
            _OnMouseLeave();

    } 
    else 
    { 
        // No button  is pressed .
        if( iButton >= 0 ) 
        {

            //Capture the mouse so that we can keep track of when the mouse is leaving our button            
            SetCapture(ci.hwnd);
            
            // if the style is PGS_AUTOSCROLL then we dont make the button hot when hovering 
            // over button.

            //Is PGS_AUTOSCROLL set 
            _dwState[iButton] |= PGF_HOT;
            if (ci.style & PGS_AUTOSCROLL) 
            {
                _dwState[iButton] |= PGF_DEPRESSED;
            }

            //If the lbutton is down and the mouse is over one of the button then 
            // someone is trying to do drag and drop so autoscroll to help them.
            // Make sure the lbutton down did not happen in the  button before scrolling
            if ( ((wParam & MK_LBUTTON) && 
                  (_iButtonTrack < 0)) || 
                 (ci.style & PGS_AUTOSCROLL) ) 
            {
                _iButtonTrack = iButton;
                SetTimer(ci.hwnd, PGT_SCROLL, _cTimeout, NULL);
            }
            _DrawButton(NULL, iButton);
        }
        else
        {

            //Mouse is not over any button or it has left one of the scroll buttons.
            //In either case call _OnMouseLeave
           
            _OnMouseLeave();
        }
        
    }
}
//---------------------------------------------------------------------------------------
void CPager::_OnSetChild(HWND hwnd, HWND hwndChild)
{
    ASSERT(IS_VALID_HANDLE(hwndChild, WND));

    RECT rc;
    _hwndChild = hwndChild;
    _ptPos.x  = 0;
    _ptPos.y  = 0;
    _fReCalcSend = FALSE;
    if (GetCapture() == ci.hwnd)
    {
        CCReleaseCapture(&ci);
    }
    _iButtonTrack = -1;
    GetClientRect(hwnd, &rc);

    _OnReCalcSize();
}
//---------------------------------------------------------------------------------------
void CPager::_OnReCalcSize()
{
    RECT rc;
    CCInvalidateFrame(ci.hwnd);     // SWP_FRAMECHANGED etc.
    _fReCalcSend = FALSE;
    rc = _rcChildIdeal;
    _SetChildPos(&rc, 0);   // SetWindowPos

}
//---------------------------------------------------------------------------------------
void CPager::_OnSetPos(int iPos)
{
    RECT rc = _rcChildIdeal;

    if( ci.style & PGS_HORZ ) {
        FlipRect(&rc);
        FlipPoint(&_ptPos);
    }

    int height;
    if (iPos < 0)
        iPos = 0;

    height = RECTHEIGHT(rc);

    if( iPos < 0  ||  iPos >  height || _ptPos.y == iPos ) {
        //Invalid Position specified or no change . Igonore it.
        return;
    }

    _ptPos.y = iPos;

    if( ci.style & PGS_HORZ ) {
        FlipRect(&rc);
        FlipPoint(&_ptPos);
    }

    CCInvalidateFrame(ci.hwnd);
    _SetChildPos(&rc , 0);
}

//---------------------------------------------------------------------------------------
int  CPager::_OnGetPos()
{
    if( ci.style  & PGS_HORZ ) {
        return _ptPos.x;
    }else{
        return _ptPos.y;
    }
}
//---------------------------------------------------------------------------------------
DWORD CPager::_GetButtonState(int iButton)
{
    
    DWORD dwState = 0;
    // Is the button id valid ?
    if ((iButton == PGB_TOPORLEFT) || (iButton == PGB_BOTTOMORRIGHT))
    {
        //yes , Get the current state of the button
        dwState = _dwState[iButton];
    }
    return dwState;
}
//---------------------------------------------------------------------------------------
void CPager::_OnTimer(UINT id)
{
    switch (id)
    {
    case PGT_SCROLL:
        if (_iButtonTrack >= 0)
        {
            // set it again because we do it faster every subsequent time
            SetTimer(ci.hwnd, PGT_SCROLL, _cTimeout, NULL);
            if (_HitTestCursor() == _iButtonTrack)
            {
                _Scroll(_iButtonTrack);
            }
            else if (!_fOwnsButtonDown) 
            {
                // if we don't own the mouse tracking (ie, the user didn't button down on us to begin with,
                // then we're done once we leave the button
                _OnMouseLeave();
            }
        }
        break;
    }
}

void CPager::_KillTimer()
{
    KillTimer(ci.hwnd, PGT_SCROLL);
    _fTimerSet = FALSE;
}
//---------------------------------------------------------------------------------------
int  CPager::_OnSetBorder(int iBorder)
{
    int iOld = _iBorder;
    int iNew = iBorder;

    //Border can't be negative
    if (iNew < 0 )
    {
        iNew = 0;
    }

    //Border can't be bigger than the button size
    if (iNew > _GetButtonSize())
    {
       iNew = _GetButtonSize();
    }
    
    _iBorder = iNew;
    CCInvalidateFrame(ci.hwnd);
    RECT rc = _rcChildIdeal;
    _SetChildPos(&rc, 0);   // SetWindowPos
    return iOld;
}

//---------------------------------------------------------------------------------------
int  CPager::_OnSetButtonSize(int iSize)
{
    int iOldSize = _iButtonSize;
    _iButtonSize = iSize;
        
    if (_iButtonSize < MINBUTTONSIZE) 
    {
        _iButtonSize = MINBUTTONSIZE;
    }

    // Border can't be bigger than button size
    if (_iBorder > _iButtonSize)
    {
        _iBorder = _iButtonSize;
    }

    CCInvalidateFrame(ci.hwnd);
    RECT rc = _rcChildIdeal;
    _SetChildPos(&rc, 0);   // SetWindowPos
    return iOldSize;

}

//---------------------------------------------------------------------------------------
LRESULT CPager::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg) {
    case PGM_GETDROPTARGET:
        if (!_hDragProxy)
            _hDragProxy = CreateDragProxy(ci.hwnd, PagerDragCallback, FALSE);
        
        GetDragProxyTarget(_hDragProxy, (IDropTarget**)lParam);
        break;

    case PGM_SETSCROLLINFO:
        _cLinesPerTimeout = LOWORD(lParam);
        _cPixelsPerLine = HIWORD(lParam);
        _cTimeout = (UINT)wParam;
        break;
        
    case PGM_SETCHILD:
        _OnSetChild(hwnd, (HWND)lParam);
        break;

    case PGM_RECALCSIZE:
        if (!_fReCalcSend )
        {
            _fReCalcSend = TRUE;
            PostMessage(hwnd, PGMP_RECALCSIZE, wParam, lParam);
        }
        break;

    case PGMP_RECALCSIZE:
         _OnReCalcSize();
         break;

    case PGM_FORWARDMOUSE:
        // forward mouse messages
        _fForwardMouseMsgs = BOOLIFY(wParam);
        break;

        
    case PGM_SETBKCOLOR:
    {
        COLORREF clr = _clrBk;
        if ((COLORREF) lParam == CLR_DEFAULT)
            _clrBk = g_clrBtnFace;
        else
            _clrBk = (COLORREF)lParam;
        _fBkColorSet = TRUE;
        CCInvalidateFrame(ci.hwnd);
        //Force a paint
        RedrawWindow(ci.hwnd, NULL,NULL,RDW_INVALIDATE|RDW_ERASE);
        return clr;
    }

    case PGM_GETBKCOLOR:
        return (LRESULT)_clrBk;    

    case PGM_SETBORDER:
        return _OnSetBorder((int)lParam);

    case PGM_GETBORDER:
        return (LRESULT)_iBorder;
        
    case PGM_SETPOS:
        _OnSetPos((int)lParam);
        break;

    case PGM_GETPOS:
        return _OnGetPos();

    case PGM_SETBUTTONSIZE:
        return _OnSetButtonSize((int)lParam);

    case PGM_GETBUTTONSIZE:
        return _GetButtonSize();
    
    case PGM_GETBUTTONSTATE:
        return _GetButtonState((int)lParam);

    case WM_PRINT:
        return _OnPrint((HDC)wParam, (UINT)lParam);

    case WM_NCHITTEST:
    {
        POINT pt;
        pt.x = GET_X_LPARAM(lParam);
        pt.y = GET_Y_LPARAM(lParam);
        if (_HitTestScreen(&pt) == -1)
            return HTTRANSPARENT;
        return HTCLIENT;
    }

    case WM_SYSCOLORCHANGE:
        if (!_fBkColorSet)
        {
            InitGlobalColors();
            _clrBk = g_clrBtnFace;
            CCInvalidateFrame(ci.hwnd);
        }
        break;

    case WM_SETFOCUS:
        SetFocus(_hwndChild);
        return 0;

    case WM_LBUTTONDOWN:
        //Fall Through
    case WM_LBUTTONUP:
        if(!(ci.style & PGS_AUTOSCROLL)) {        
            _OnLButtonChange(uMsg,lParam);
        }
        break;

    case WM_MOUSEMOVE:
        // Only forward if the point is within the client rect of pager.
        if (_fForwardMouseMsgs && _hwndChild)
        {
            POINT pt;
            RECT rcClient;

            // BUGBUG (scotth): cache this
            GetClientRect(ci.hwnd, &rcClient);

            pt.x = GET_X_LPARAM(lParam);
            pt.y = GET_Y_LPARAM(lParam);

            // Is this point in our client rect?
            if (PtInRect(&rcClient, pt))
            {
                // Yes; then convert coords and forward it
                pt.x += _ptPos.x;
                pt.y += _ptPos.y;

                SendMessage(_hwndChild, WM_MOUSEMOVE, wParam, MAKELPARAM(pt.x, pt.y));
            }
        }

        _OnMouseMove(wParam,lParam);
        break;

    case WM_MOUSELEAVE :
        _OnMouseLeave();
        break;

    case WM_ERASEBKGND:
    {
        LRESULT lres = CCForwardEraseBackground(ci.hwnd, (HDC) wParam);

        if (_iBorder) {
            // paint the borders
            RECT rc;
            RECT rc2;
            GetClientRect(ci.hwnd, &rc);
            rc2 = rc;
        
            if( ci.style & PGS_HORZ ) {
                FlipRect(&rc2);
            }
            rc2.right = rc2.left + _iBorder + 1;

            if( ci.style & PGS_HORZ ) {
                FlipRect(&rc2);
            }            
            FillRectClr((HDC)wParam, &rc2, _clrBk);
            rc2 = rc;

            if( ci.style & PGS_HORZ ) {
                FlipRect(&rc2);
            }
            rc2.left = rc2.right - _iBorder - 1;

            if( ci.style & PGS_HORZ ) {
                FlipRect(&rc2);
            }

            FillRectClr((HDC)wParam, &rc2, _clrBk);
        }
        return TRUE;
    }

    case WM_TIMER:
        _OnTimer((UINT)wParam);
        return 0;       

    case WM_SETTINGCHANGE:
        InitGlobalMetrics(wParam);
        _iButtonSize = (int) g_cxScrollbar * 3 / 4;
        if (_iButtonSize < MINBUTTONSIZE) {
            _iButtonSize = MINBUTTONSIZE;
        }
        break;

    case WM_DESTROY:
        if (_hDragProxy)
            DestroyDragProxy(_hDragProxy);
        break;
    }
    return CControl::v_WndProc(hwnd, uMsg, wParam, lParam);
}

//---------------------------------------------------------------------------------------
// call with cyCh == 0 to specify auto vsizing
BOOL DrawChar(HDC hdc, LPRECT lprc, UINT wState, TCHAR ch, UINT cyCh, BOOL fAlwaysGrayed, BOOL fTopAlign)
{
    COLORREF rgb;
    BOOL    fDrawDisabled = !fAlwaysGrayed && (wState & DCHF_INACTIVE);
    BOOL    fDrawPushed = wState & DCHF_PUSHED;
    // Bad UI to have a pushed disabled button
    ASSERT (!fDrawDisabled || !fDrawPushed);
    RECT rc = *lprc;
    UINT uFormat = DT_CENTER | DT_SINGLELINE;

    if (fAlwaysGrayed)
        rgb = g_clrBtnShadow;
    else if (fDrawDisabled)
        rgb = g_clrBtnHighlight;
    else 
        rgb = g_clrBtnText;
    
    rgb = SetTextColor(hdc, rgb);

    if (cyCh)
    {
        if (fTopAlign)
            rc.bottom = rc.top + cyCh;
        else
        {
            rc.top += ((RECTHEIGHT(rc) - cyCh) / 2);
            rc.bottom = rc.top + cyCh;
        }
        uFormat |= DT_BOTTOM;
    }
    else
        uFormat |= DT_VCENTER;

    if (fDrawDisabled || fDrawPushed)
        OffsetRect(&rc, 1, 1);

    DrawText(hdc, &ch, 1, &rc, uFormat);

    if (fDrawDisabled)
    {
        OffsetRect(&rc, -1, -1);
        SetTextColor(hdc, g_clrBtnShadow);
        DrawText(hdc, &ch, 1, &rc, uFormat);
    }

    SetTextColor(hdc, rgb);
    return(TRUE);
}

void DrawBlankButton(HDC hdc, LPRECT lprc, DWORD wControlState)
{
    BOOL fAdjusted;

    if (wControlState & (DCHF_HOT | DCHF_PUSHED) &&
        !(wControlState & DCHF_NOBORDER)) {
        COLORSCHEME clrsc;

        clrsc.dwSize = 1;
        if (GetBkColor(hdc) == g_clrBtnShadow) {
            clrsc.clrBtnHighlight = g_clrBtnHighlight;
            clrsc.clrBtnShadow = g_clrBtnText;
        } else
            clrsc.clrBtnHighlight = clrsc.clrBtnShadow = CLR_DEFAULT;

        // if button is both DCHF_HOT and DCHF_PUSHED, DCHF_HOT wins here
        CCDrawEdge(hdc, lprc, (wControlState & DCHF_HOT) ? BDR_RAISEDINNER : BDR_SUNKENOUTER,
                 (UINT) (BF_ADJUST | BF_RECT), &clrsc);
        fAdjusted = TRUE;
    } else {
        fAdjusted = FALSE;
    }

    if (!(wControlState & DCHF_TRANSPARENT))
        FillRectClr(hdc, lprc, GetBkColor(hdc));
    
    if (!fAdjusted)
        InflateRect(lprc, -g_cxBorder, -g_cyBorder);
}

//---------------------------------------------------------------------------------------
void DrawCharButton(HDC hdc, LPRECT lprc, UINT wControlState, TCHAR ch, UINT cyCh, BOOL fAlwaysGrayed, BOOL fTopAlign)
{
    RECT rc;
    CopyRect(&rc, lprc);

    DrawBlankButton(hdc, &rc, wControlState);

    if ((RECTWIDTH(rc) <= 0) || (RECTHEIGHT(rc) <= 0))
        return;

#if defined(UNIX)
    HBITMAP hbit;
    int x,y,width,height;

    x = rc.left + (rc.right  - rc.left) /2;
    y = rc.top  + (rc.bottom - rc.top ) /2;

    if (wControlState & (DCHF_INACTIVE | DCHF_PUSHED))
    {
        x++;
        y++;
    }

    UnixPaintArrow( hdc, 
         (wControlState & DCHF_HORIZONTAL), 
         (wControlState & DCHF_FLIPPED), 
         x,y,
         min(ARROW_WIDTH,  (rc.right  - rc.left)),
         min(ARROW_HEIGHT, (rc.bottom - rc.top ))
        );

#else
    
    int iOldBk = SetBkMode(hdc, TRANSPARENT);
    DrawChar(hdc, &rc, wControlState, ch, cyCh, fAlwaysGrayed, fTopAlign);
    SetBkMode(hdc, iOldBk);

#endif

}

// --------------------------------------------------------------------------------------
//
//  DrawScrollArrow
//
// --------------------------------------------------------------------------------------
void DrawScrollArrow(HDC hdc, LPRECT lprc, UINT wControlState)
{
#define szfnMarlett  TEXT("MARLETT")
    TCHAR ch = (wControlState & DCHF_HORIZONTAL) ? TEXT('3') : TEXT('5');

    //
    // Flip the direction arrow in case of a RTL mirrored DC,
    // since it won't be flipped automatically (textout!)
    //
    if (IS_DC_RTL_MIRRORED(hdc) && (wControlState & DCHF_HORIZONTAL))
        wControlState ^= DCHF_FLIPPED;

    LONG lMin = min(RECTWIDTH(*lprc), RECTHEIGHT(*lprc)) - (2 * g_cxBorder);  // g_cxBorder fudge notches font size down

    HFONT hFont = CreateFont(lMin, 0, 0, 0, FW_NORMAL, 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0, szfnMarlett);
    
    HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
    
    if (wControlState & DCHF_FLIPPED)
        ch++;
    
    DrawCharButton(hdc, lprc, wControlState, ch, 0, FALSE, FALSE);
    SelectObject(hdc, hOldFont);
    DeleteObject(hFont);   
}

//---------------------------------------------------------------------------------------

#define CX_EDGE         2
#define CX_LGEDGE       4

#define CX_INCREMENT    1
#define CX_DECREMENT    (-CX_INCREMENT)

#define MIDPOINT(x1, x2)        ((x1 + x2) / 2)
#define CHEVRON_WIDTH(dSeg)     (4 * dSeg)

void DrawChevron(HDC hdc, LPRECT lprc, DWORD dwFlags)
{
    RECT rc;
    CopyRect(&rc, lprc);

    // draw the border and background
    DrawBlankButton(hdc, &rc, dwFlags);

    // offset the arrow if pushed
    if (dwFlags & DCHF_PUSHED)
        OffsetRect(&rc, CX_INCREMENT, CX_INCREMENT);

    // draw the arrow
    HBRUSH hbrSave = SelectBrush(hdc, GetSysColorBrush(COLOR_BTNTEXT));
    int dSeg = (dwFlags & DCHF_LARGE) ? CX_LGEDGE : CX_EDGE;

    if (dwFlags & DCHF_HORIZONTAL)
    {
        // horizontal arrow
        int x = MIDPOINT(rc.left, rc.right - CHEVRON_WIDTH(dSeg));
        int yBase;

        if (dwFlags & DCHF_TOPALIGN)
            yBase = rc.top + dSeg + (2 * CX_EDGE);
        else
            yBase = MIDPOINT(rc.top, rc.bottom);

        for (int y = -dSeg; y <= dSeg; y++)
        {
            PatBlt(hdc, x, yBase + y, dSeg, CX_INCREMENT, PATCOPY);
            PatBlt(hdc, x + (dSeg * 2), yBase + y, dSeg, CX_INCREMENT, PATCOPY);

            x += (y < 0) ? CX_INCREMENT : CX_DECREMENT;
        }
    }
    else
    {
        // vertical arrow
        int y = rc.top + CX_INCREMENT;
        int xBase = MIDPOINT(rc.left, rc.right);

        for (int x = -dSeg; x <= dSeg; x++)
        {
            PatBlt(hdc, xBase + x, y, CX_INCREMENT, dSeg, PATCOPY);
            PatBlt(hdc, xBase + x, y + (dSeg * 2), CX_INCREMENT, dSeg, PATCOPY);

            y += (x < 0) ? CX_INCREMENT : CX_DECREMENT;
        }
    }

    // clean up
    SelectBrush(hdc, hbrSave);
}