/////////////////////////////////////////////////////////////////////////////
//
// This file is part of ResizableLib
// http://sourceforge.net/projects/resizablelib
//
// Copyright (C) 2000-2004 by Paolo Messina
// http://www.geocities.com/ppescher - mailto:ppescher@hotmail.com
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////

/*!
 *  @file
 *  @brief Implementation of the CResizableGrip class.
 */

#include "stdafx.h"
#include "ResizableGrip.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CResizableGrip::CResizableGrip()
{
    m_nShowCount = 0;
}

CResizableGrip::~CResizableGrip()
{

}

void CResizableGrip::UpdateSizeGrip()
{
    if (!::IsWindow(m_wndGrip.m_hWnd))
        return;

    // size-grip goes bottom right in the client area
    // (any right-to-left adjustment should go here)

    RECT rect;
    GetResizableWnd()->GetClientRect(&rect);

    rect.left = rect.right - m_wndGrip.m_size.cx;
    rect.top = rect.bottom - m_wndGrip.m_size.cy;

    // must stay below other children
    m_wndGrip.SetWindowPos(&CWnd::wndBottom, rect.left, rect.top, 0, 0,
        SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOREPOSITION
        | (IsSizeGripVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
}

// pbStatus points to a variable, maintained by the caller, that
// holds its visibility status. Initialize the variable with 1
// to allow to temporarily hide the grip, 0 to allow to
// temporarily show the grip (with respect to the dwMask bit).

// NB: visibility is effective only after an update

void CResizableGrip::ShowSizeGrip(DWORD* pStatus, DWORD dwMask /*= 1*/)
{
    ASSERT(pStatus != NULL);

    if (!(*pStatus & dwMask))
    {
        m_nShowCount++;
        (*pStatus) |= dwMask;
    }
}

void CResizableGrip::HideSizeGrip(DWORD* pStatus, DWORD dwMask /*= 1*/)
{
    ASSERT(pStatus != NULL);

    if (*pStatus & dwMask)
    {
        m_nShowCount--;
        (*pStatus) &= ~dwMask;
    }
}

BOOL CResizableGrip::IsSizeGripVisible()
{
    // NB: visibility is effective only after an update
    return (m_nShowCount > 0);
}

void CResizableGrip::SetSizeGripVisibility(BOOL bVisible)
{
    if (bVisible)
        m_nShowCount = 1;
    else
        m_nShowCount = 0;
}

BOOL CResizableGrip::SetSizeGripBkMode(int nBkMode)
{
    if (::IsWindow(m_wndGrip.m_hWnd))
    {
        if (nBkMode == OPAQUE)
            m_wndGrip.SetTransparency(FALSE);
        else if (nBkMode == TRANSPARENT)
            m_wndGrip.SetTransparency(TRUE);
        else
            return FALSE;
        return TRUE;
    }
    return FALSE;
}

void CResizableGrip::SetSizeGripShape(BOOL bTriangular)
{
    if (::IsWindow(m_wndGrip.m_hWnd))
        m_wndGrip.SetTriangularShape(bTriangular);
}

BOOL CResizableGrip::CreateSizeGrip(BOOL bVisible /*= TRUE*/,
        BOOL bTriangular /*= TRUE*/, BOOL bTransparent /*= FALSE*/)
{
    // create grip
    CRect rect(0 , 0, m_wndGrip.m_size.cx, m_wndGrip.m_size.cy);
    BOOL bRet = m_wndGrip.Create(WS_CHILD | WS_CLIPSIBLINGS
        | SBS_SIZEGRIP, rect, GetResizableWnd(), 0);

    if (bRet)
    {
        // set options
        m_wndGrip.SetTriangularShape(bTriangular);
        m_wndGrip.SetTransparency(bTransparent);
        SetSizeGripVisibility(bVisible);

        // update position
        UpdateSizeGrip();
    }

    return bRet;
}

/////////////////////////////////////////////////////////////////////////////
// CSizeGrip implementation

BOOL CResizableGrip::CSizeGrip::IsRTL()
{
    return GetExStyle() & WS_EX_LAYOUTRTL;
}

BOOL CResizableGrip::CSizeGrip::PreCreateWindow(CREATESTRUCT& cs)
{
    // set window size
    m_size.cx = GetSystemMetrics(SM_CXVSCROLL);
    m_size.cy = GetSystemMetrics(SM_CYHSCROLL);

    cs.cx = m_size.cx;
    cs.cy = m_size.cy;

    return CScrollBar::PreCreateWindow(cs);
}

LRESULT CResizableGrip::CSizeGrip::WindowProc(UINT message,
                                              WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_GETDLGCODE:
        // fix to prevent the control to gain focus, using arrow keys
        // (standard grip returns DLGC_WANTARROWS, like any standard scrollbar)
        return DLGC_STATIC;

    case WM_SETFOCUS:
        // fix to prevent the control to gain focus, if set directly
        // (for example when it's the only one control in a dialog)
        return 0;

    case WM_NCHITTEST:
        // choose proper cursor shape
        if (IsRTL())
            return HTBOTTOMLEFT;
        else
            return HTBOTTOMRIGHT;
        break;

    case WM_SETTINGCHANGE:
        {
            // update grip's size
            CSize sizeOld = m_size;
            m_size.cx = GetSystemMetrics(SM_CXVSCROLL);
            m_size.cy = GetSystemMetrics(SM_CYHSCROLL);

            // resize transparency bitmaps
            if (m_bTransparent)
            {
                CClientDC dc(this);

                // destroy bitmaps
                m_bmGrip.DeleteObject();
                m_bmMask.DeleteObject();

                // re-create bitmaps
                m_bmGrip.CreateCompatibleBitmap(&dc, m_size.cx, m_size.cy);
                m_bmMask.CreateBitmap(m_size.cx, m_size.cy, 1, 1, NULL);
            }

            // re-calc shape
            if (m_bTriangular)
                SetTriangularShape(m_bTriangular);

            // reposition the grip
            CRect rect;
            GetWindowRect(rect);
            rect.InflateRect(m_size.cx - sizeOld.cx, m_size.cy - sizeOld.cy, 0, 0);
            ::MapWindowPoints(NULL, GetParent()->GetSafeHwnd(), (LPPOINT)&rect, 2);
            MoveWindow(rect, TRUE);
        }
        break;

    case WM_DESTROY:
        // perform clean up
        if (m_bTransparent)
            SetTransparency(FALSE);
        break;

    case WM_PAINT:
    case WM_PRINTCLIENT:
        if (m_bTransparent)
        {
            PAINTSTRUCT ps;
            CDC* pDC = (message == WM_PAINT && wParam == 0) ?
                BeginPaint(&ps) : CDC::FromHandle((HDC)wParam);

            // select bitmaps
            CBitmap *pOldGrip, *pOldMask;

            pOldGrip = m_dcGrip.SelectObject(&m_bmGrip);
            pOldMask = m_dcMask.SelectObject(&m_bmMask);

            // obtain original grip bitmap, make the mask and prepare masked bitmap
            CScrollBar::WindowProc(message, (WPARAM)m_dcGrip.GetSafeHdc(), lParam);
            m_dcGrip.SetBkColor(m_dcGrip.GetPixel(0, 0));
            m_dcMask.BitBlt(0, 0, m_size.cx, m_size.cy, &m_dcGrip, 0, 0, SRCCOPY);
            m_dcGrip.BitBlt(0, 0, m_size.cx, m_size.cy, &m_dcMask, 0, 0, 0x00220326);

            // draw transparently
            pDC->BitBlt(0, 0, m_size.cx, m_size.cy, &m_dcMask, 0, 0, SRCAND);
            pDC->BitBlt(0, 0, m_size.cx, m_size.cy, &m_dcGrip, 0, 0, SRCPAINT);

            // unselect bitmaps
            m_dcGrip.SelectObject(pOldGrip);
            m_dcMask.SelectObject(pOldMask);

            if (message == WM_PAINT && wParam == 0)
                EndPaint(&ps);
            return 0;
        }
        break;
    }

    return CScrollBar::WindowProc(message, wParam, lParam);
}

void CResizableGrip::CSizeGrip::SetTransparency(BOOL bActivate)
{
    // creates or deletes DCs and Bitmaps used for
    // implementing a transparent size grip

    if (bActivate && !m_bTransparent)
    {
        m_bTransparent = TRUE;

        CClientDC dc(this);

        // create memory DCs and bitmaps
        m_dcGrip.CreateCompatibleDC(&dc);
        m_bmGrip.CreateCompatibleBitmap(&dc, m_size.cx, m_size.cy);

        m_dcMask.CreateCompatibleDC(&dc);
        m_bmMask.CreateBitmap(m_size.cx, m_size.cy, 1, 1, NULL);
    }
    else if (!bActivate && m_bTransparent)
    {
        m_bTransparent = FALSE;

        // destroy memory DCs and bitmaps
        m_dcGrip.DeleteDC();
        m_bmGrip.DeleteObject();

        m_dcMask.DeleteDC();
        m_bmMask.DeleteObject();
    }
}

void CResizableGrip::CSizeGrip::SetTriangularShape(BOOL bEnable)
{
    m_bTriangular = bEnable;

    if (bEnable)
    {
        // set a triangular window region
        CRect rect;
        GetWindowRect(rect);
        rect.OffsetRect(-rect.TopLeft());
        POINT arrPoints[] =
        {
            { rect.left, rect.bottom },
            { rect.right, rect.bottom },
            { rect.right, rect.top }
        };
        CRgn rgnGrip;
        rgnGrip.CreatePolygonRgn(arrPoints, 3, WINDING);
        SetWindowRgn((HRGN)rgnGrip.Detach(), IsWindowVisible());
    }
    else
    {
        SetWindowRgn((HRGN)NULL, IsWindowVisible());
    }
}
