//+---------------------------------------------------------------------
//
//   File:       ipborder.cxx
//
//   Classes:    InPlaceBorder
//
//   Notes:      Use of this class limits windows to the use
//               of the non-client region for UIAtive borders
//               only: Standard (non-control window) scroll bars
//               are specifically NOT supported.
//
//   History:    14-May-93   CliffG        Created.
//
//------------------------------------------------------------------------

#include "headers.hxx"
#pragma hdrstop

#define IPB_GRABFACTOR  3

#define SetWF(hwnd,wf)  SetWindowLongPtr(hwnd, GWL_STYLE, \
            GetWindowLongPtr(hwnd,GWL_STYLE) | (wf))
#define ClrWF(hwnd,wf)  SetWindowLongPtr(hwnd, GWL_STYLE, \
            GetWindowLongPtr(hwnd,GWL_STYLE) &~(wf))
#define TestWF(hwnd,wf) (GetWindowLongPtr(hwnd,GWL_STYLE) & (wf))


    WORD InPlaceBorder::_cUsage = 0;
    HBRUSH InPlaceBorder::_hbrActiveCaption;
    HBRUSH InPlaceBorder::_hbrInActiveCaption;


//+---------------------------------------------------------------
//
//  Member: InPlaceBorder::DrawFrame
//
//---------------------------------------------------------------
void
InPlaceBorder::DrawFrame(HWND hwnd)
{
    if(!_fUIActive)
        return;

    HDC hdc = GetWindowDC(hwnd);
    if (!hdc)
        return;

    HBRUSH hbr;
    if(_fParentActive)
        hbr = _hbrActiveCaption;
    else
        hbr = _hbrInActiveCaption;

    if (hbr)
    {
        RECT rcWhole;
        GetWindowRect(hwnd, &rcWhole);
        OffsetRect(&rcWhole, -rcWhole.left, -rcWhole.top);
        RECT rc;
        //Top
        rc = rcWhole;
        rc.bottom = rc.top + _cyFrame;
        FillRect(hdc, &rc, hbr);
        //Left
        rc = rcWhole;
        rc.right = rc.left + _cxFrame;
        FillRect(hdc, &rc, hbr);
        //Bottom
        rc = rcWhole;
        rc.top = rc.bottom - _cyFrame;
        FillRect(hdc, &rc, hbr);
        //Right
        rc = rcWhole;
        rc.left = rc.right - _cxFrame;
        FillRect(hdc, &rc, hbr);

        if(TestWF(hwnd, WS_THICKFRAME)) //Resizable window?
        {
            //
            //Draw grab handles at 4 corners of the border...
            //
            hbr = (HBRUSH)GetStockObject( BLACK_BRUSH );
            //TopLeft
            rc = rcWhole;
            rc.right = rc.left + _cxFrame;
            rc.bottom = rc.top + _cyFrame * IPB_GRABFACTOR;
            FillRect(hdc, &rc, hbr);
            rc.bottom = rc.top + _cyFrame;
            rc.right = rc.left + _cxFrame * IPB_GRABFACTOR;
            FillRect(hdc, &rc, hbr);
            //TopRight
            rc.right = rcWhole.right;
            rc.left = rc.right - _cxFrame * IPB_GRABFACTOR;
            FillRect(hdc, &rc, hbr);
            rc.left = rc.right - _cxFrame;
            rc.bottom = rc.top + _cyFrame * IPB_GRABFACTOR;
            FillRect(hdc, &rc, hbr);
            //BottomLeft
            rc = rcWhole;
            rc.top = rc.bottom - _cyFrame * IPB_GRABFACTOR;
            rc.right = rc.left + _cxFrame;
            FillRect(hdc, &rc, hbr);
            rc.top = rc.bottom - _cyFrame;
            rc.right = rc.left + _cxFrame * IPB_GRABFACTOR;
            FillRect(hdc, &rc, hbr);
            //BottomRight
            rc.right = rcWhole.right;
            rc.left = rc.right - _cxFrame * IPB_GRABFACTOR;
            FillRect(hdc, &rc, hbr);
            rc.right = rcWhole.right;
            rc.left = rc.right - _cxFrame;
            rc.top = rc.bottom - _cyFrame * IPB_GRABFACTOR;
            FillRect(hdc, &rc, hbr);
        }
    }

    ReleaseDC(hwnd, hdc);
}

//+---------------------------------------------------------------
//
//  Member: InPlaceBorder::HitTest
//
//---------------------------------------------------------------
LONG
InPlaceBorder::HitTest(HWND hwnd, int x, int y)
{
    POINT pt = { x, y };
    RECT rcClient;

    GetWindowRect(hwnd, &rcClient);
    CalcClientRect(hwnd, &rcClient);
    if (PtInRect(&rcClient, pt))
        return HTCLIENT;

    // We're somewhere on the window frame.
    if (y >= rcClient.bottom)
    {
        if (x <= rcClient.left + _cxFrame * IPB_GRABFACTOR)
            return HTBOTTOMLEFT;
        if (x >= rcClient.right - _cxFrame * IPB_GRABFACTOR)
            return HTBOTTOMRIGHT;
        return HTBOTTOM;
    }
    else if (y <= rcClient.top)
    {
        if (x <= rcClient.left + _cxFrame * IPB_GRABFACTOR)
            return(HTTOPLEFT);
        if (x >= rcClient.right - _cxFrame * IPB_GRABFACTOR)
            return(HTTOPRIGHT);
        return HTTOP;
    }
    else if (x <= rcClient.left)
    {
        if (y <= rcClient.top + _cyFrame * IPB_GRABFACTOR)
            return HTTOPLEFT;
        if (y >= rcClient.bottom - _cyFrame * IPB_GRABFACTOR)
            return HTBOTTOMLEFT;
        return HTLEFT;
    }
    else
    {
        if (y <= rcClient.top + _cyFrame * IPB_GRABFACTOR)
            return HTTOPRIGHT;
        if (y >= rcClient.bottom - _cyFrame * IPB_GRABFACTOR)
            return HTBOTTOMRIGHT;
        return HTRIGHT;
    }

    return HTNOWHERE;
}

//+---------------------------------------------------------------
//
//  Member: InPlaceBorder::DefWindowProc
//
//---------------------------------------------------------------
LRESULT
InPlaceBorder::DefWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    LRESULT lRet = 0L;
    //REVIEW code assumes an SDI app...
    if(_hwnd == NULL)
        return ::DefWindowProc(hwnd,msg,wParam,lParam);

    switch (msg)
    {
    case WM_WINDOWPOSCHANGED:
        if(_pSite != NULL && _cResizing == 0 && _fUIActive)
        {
            ++_cResizing;
            RECT rc;
            GetChildWindowRect(hwnd, &rc);
            InflateRect(&rc,-_cxFrame,-_cyFrame);
            _pSite->OnPosRectChange(&rc);
            _cResizing--;
        }
        break;
    case WM_NCCALCSIZE:     // lParam == LPRECT of window rect
        //
        //Turn off the WS_THICKFRAME style bit during
        //default processing so we keep ownership of visualization...
        //
        if (TestWF(hwnd, WS_THICKFRAME))
        {
            ClrWF(hwnd, WS_THICKFRAME);
            lRet = ::DefWindowProc(hwnd, msg, wParam,lParam);
            SetWF(hwnd, WS_THICKFRAME);
        }
        CalcClientRect(hwnd, (LPRECT)lParam);
        return lRet;

    case WM_NCHITTEST:      // lParam is POINT in screen cords
        return HitTest(hwnd, LOWORD(lParam), HIWORD(lParam));

    case WM_NCPAINT:
        DrawFrame(hwnd);
        return 0L;

    case WM_NCLBUTTONDOWN:
    case WM_NCMOUSEMOVE:
    case WM_NCLBUTTONUP:
    case WM_NCLBUTTONDBLCLK:
    case WM_NCRBUTTONDOWN:
    case WM_NCRBUTTONUP:
    case WM_NCRBUTTONDBLCLK:
    case WM_NCMBUTTONDOWN:
    case WM_NCMBUTTONUP:
    case WM_NCMBUTTONDBLCLK:
    case WM_NCACTIVATE:     // wParam == active state
    case WM_NCCREATE:       // Sent before WM_CREATE
    case WM_NCDESTROY:      // Sent before WM_DESTROY
        break;
    }

    return ::DefWindowProc(hwnd, msg, wParam, lParam);
}

//+---------------------------------------------------------------
//
//  Member: InPlaceBorder::InPlaceBorder
//
//---------------------------------------------------------------
InPlaceBorder::InPlaceBorder(void)
{
    _fParentActive = TRUE;
    _fUIActive = FALSE;
    _hwnd = NULL;
    _pSite = NULL;
    _cResizing = 0;
    _cxFrame = _cyFrame = IPBORDER_THICKNESS;
    if(_cUsage++ == 0)
    {
        // the following could fail and we would be hosed...
        _hbrActiveCaption = CreateHatchBrush(HS_BDIAGONAL,
            GetSysColor(COLOR_ACTIVECAPTION));
        _hbrInActiveCaption = CreateHatchBrush(HS_BDIAGONAL,
            GetSysColor(COLOR_WINDOWFRAME));
    }
}

//+---------------------------------------------------------------
//
//  Member: InPlaceBorder::~InPlaceBorder
//
//---------------------------------------------------------------
InPlaceBorder::~InPlaceBorder(void)
{
    if(--_cUsage == 0)
    {
        DeleteObject(_hbrActiveCaption);
        _hbrActiveCaption = NULL;
        DeleteObject(_hbrInActiveCaption);
        _hbrInActiveCaption = NULL;
    }
}

//+---------------------------------------------------------------
//
//  Member: InPlaceBorder::SetState
//
//Change the border state: reflected in the nonclient window border
//---------------------------------------------------------------
void
InPlaceBorder::SetUIActive(BOOL fUIActive)
{
    if(_hwnd == NULL)
        return;

    if(_fUIActive != fUIActive)
    {
        RECT rcClient;
        GetChildWindowRect(_hwnd, &rcClient);
        int cx = rcClient.right - rcClient.left;
        int cy = rcClient.bottom - rcClient.top;
        int x = rcClient.left;
        int y = rcClient.top;
        BOOL fResize = FALSE;
        if (fUIActive)
        {
            fResize = TRUE;
            cx += _cxFrame * 2;
            cy += _cyFrame * 2;
            x -= _cxFrame;
            y -= _cyFrame;
        }
        else if (_fUIActive)
        {
            fResize = TRUE;
            cx -= _cxFrame * 2;
            cy -= _cyFrame * 2;
            x += _cxFrame;
            y += _cyFrame;
        }
        if (fResize)
        {
            //
            //Set our state member up so CalcClientRect generates correct values,
            //then move the window (keeping client area same size and position)
            //
            _fUIActive = fUIActive;
            ++_cResizing;
            SetWindowPos( _hwnd, NULL, x, y, cx, cy, SWP_FRAMECHANGED |
                SWP_NOACTIVATE | SWP_NOZORDER);
            RedrawFrame();
            _cResizing--;
        }
        else
        {
            InvalidateFrame();
        }
    }
    _fUIActive = fUIActive;
}

//+---------------------------------------------------------------
//
//  Member: InPlaceBorder::SetSize
//
//---------------------------------------------------------------
void
InPlaceBorder::SetSize(HWND hwnd, RECT& rc)
{
    int cx = rc.right - rc.left;
    int cy = rc.bottom - rc.top;
    int x = rc.left;
    int y = rc.top;
    if(_fUIActive)
    {
        cx += _cxFrame * 2;
        cy += _cyFrame * 2;
        x -= _cxFrame;
        y -= _cyFrame;
    }
    ++_cResizing;
    MoveWindow(hwnd, x, y, cx, cy, TRUE);
    _cResizing--;
}

//+---------------------------------------------------------------
//
//  Member: InPlaceBorder::Erase
//
// Force border state to non-UIActive, managing the coresponding
// change in border appearance
//
//---------------------------------------------------------------
void
InPlaceBorder::Erase(void)
{
    if(_hwnd == NULL)
        return;
    SetUIActive(FALSE);
    _fParentActive = TRUE;
    InvalidateFrame();
    RedrawFrame();
}


//+---------------------------------------------------------------
//
//  Member: InPlaceBorder::SetBorderSize
//
//---------------------------------------------------------------
void
InPlaceBorder::SetBorderSize( int cx, int cy )
{
    if(cx < 0)
        cx = 0;
    if(cy < 0)
        cy = 0;
    _cxFrame = cx;
    _cyFrame = cy;
    InvalidateFrame();
    RedrawFrame();
}

//+---------------------------------------------------------------
//
//  Member: InPlaceBorder::GetBorderSize
//
//---------------------------------------------------------------
void
InPlaceBorder::GetBorderSize( LPINT pcx, LPINT pcy )
{
    *pcx = _cxFrame;
    *pcy = _cyFrame;
}

//+---------------------------------------------------------------
//
//  Member: InPlaceBorder::Attach
//
//---------------------------------------------------------------
void
InPlaceBorder::Attach(HWND hwnd, BOOL fUIActive)
{
    if((_hwnd = hwnd) != NULL)
    {
        SetUIActive(fUIActive);
        InvalidateFrame();  //force first-time NCCALC
    }
}

void
InPlaceBorder::Bind(LPOLEINPLACESITE pSite, HWND hwnd, BOOL fUIActive)
{
    _pSite = NULL;
    if((_hwnd = hwnd) != NULL && (_pSite = pSite) != NULL)
    {
        SetUIActive(fUIActive);
        InvalidateFrame();  //force first-time NCCALC
    }
}

