#include "stdafx.h"
#include "Ctrl.h"
#include "SmCheckBox.h"

#if ENABLE_MSGTABLE_API

/***************************************************************************\
*****************************************************************************
*
* class SmCheckBox
*
*****************************************************************************
\***************************************************************************/

const SIZE SIZE_Box = {13, 13};

HFONT   SmCheckBox::s_hfntCheck = NULL;
HFONT   SmCheckBox::s_hfntText  = NULL;

//------------------------------------------------------------------------------
HRESULT
SmCheckBox::ApiOnEvent(EventMsg * pmsg)
{
    switch (GET_EVENT_DEST(pmsg))
    {
    case GMF_DIRECT:
    case GMF_EVENT:
        switch (pmsg->nMsg)
        {
        case GM_PAINT:
            {
                GMSG_PAINT * pmsgPaint = (GMSG_PAINT *) pmsg;
                if (pmsgPaint->nCmd == GPAINT_RENDER) {
                    switch (pmsgPaint->nSurfaceType)
                    {
                    case GSURFACE_HDC:
                        {
                            GMSG_PAINTRENDERI * pmsgR = (GMSG_PAINTRENDERI *) pmsgPaint;
                            OnDraw(pmsgR->hdc, pmsgR);
                        }
                        break;

                    case GSURFACE_GPGRAPHICS:
                        {
                            GMSG_PAINTRENDERF * pmsgR = (GMSG_PAINTRENDERF *) pmsgPaint;
                            OnDraw(pmsgR->pgpgr, pmsgR);
                        }
                        break;
                    default:
                        Trace("WARNING: Unknown surface type\n");
                    }

                    return DU_S_PARTIAL;
                }
            }
            break;

        case GM_CHANGERECT:
            {
                GMSG_CHANGERECT * pmsgR = (GMSG_CHANGERECT *) pmsg;
                if (TestFlag(pmsgR->nFlags, SGR_SIZE)) {
                    ComputeLayout();
                }
            }
            break;
    
        case GM_INPUT:
            {
MouseDown:
                GMSG_INPUT * pmsgI = (GMSG_INPUT *) pmsg;
                if (pmsgI->nDevice == GINPUT_MOUSE) {
                    GMSG_MOUSE * pmsgM = (GMSG_MOUSE *) pmsgI;
                    if (pmsgM->nCode == GMOUSE_DOWN) {
                        return OnMouseDown(pmsgM);
                    }
                }
            }
            break;

        case GM_CHANGESTATE:
            {
ChangeState:
                GMSG_CHANGESTATE * pmsgS = (GMSG_CHANGESTATE *) pmsg;
                switch (pmsgS->nCode)
                {
                case GSTATE_KEYBOARDFOCUS:
                    SetKeyboardFocus(pmsgS->nCmd == GSC_SET);
                    return DU_S_PARTIAL;

#if TEST_MOUSEFOCUS
                case GSTATE_MOUSEFOCUS:
                    SetMouseFocus(pmsgS->nCmd == GSC_SET);
                    return DU_S_PARTIAL;
#endif // TEST_MOUSEFOCUS
                }
            }
            break;
        case GM_QUERY:
            {
                GMSG_QUERY * pmsgQ = (GMSG_QUERY *) pmsg;
                switch (pmsgQ->nCode)
                {
                case GQUERY_DESCRIPTION:
                    {
                        GMSG_QUERYDESC * pmsgQD = (GMSG_QUERYDESC *) pmsg;
                        CopyString(pmsgQD->szType, L"SmCheckBox", _countof(pmsgQD->szType));
                        return DU_S_COMPLETE;
                    }
                }
            }
            break;
        }
        break;

    case GMF_ROUTED:
        //
        // Routed message before handled by control.
        //

        switch (pmsg->nMsg)
        {
        case GM_CHANGESTATE:
            //
            // When focus changes for the checkbox or one of its children,
            // the checkbox wants to know.
            //
            goto ChangeState;
        }
        break;

    case GMF_BUBBLED:
        //
        // Bubbled message not handled by control
        //

        switch (pmsg->nMsg)
        {
        case GM_INPUT:
            goto MouseDown;
        }
        break;
    }

    return SVisual::ApiOnEvent(pmsg);
}


inline bool
PtInBox(int cx, int cy, POINT & pt)
{
    return (pt.x <= cx) && (pt.y <= cy) && (pt.x >= 0) && (pt.y >= 0);
}


//------------------------------------------------------------------------------
UINT
SmCheckBox::OnMouseDown(GMSG_MOUSE * pmsg)
{
    if (pmsg->bButton == GBUTTON_LEFT) {
        if (((GET_EVENT_DEST(pmsg) == GMF_DIRECT) && PtInRect(&m_rcCheckPxl, pmsg->ptClientPxl)) ||
            PtInBox(m_rcItemPxl.right, m_rcItemPxl.bottom, pmsg->ptClientPxl)) {

            m_bChecked++;
            if (m_bChecked > GetMaxCheck()) {
                m_bChecked = 0;
            }
            GetStub()->Invalidate();

            return DU_S_COMPLETE;
        }
    }

    return DU_S_NOTHANDLED;
}


//------------------------------------------------------------------------------
void        
SmCheckBox::OnDraw(HDC hdc, GMSG_PAINTRENDERI * pmsgR) 
{
    UNREFERENCED_PARAMETER(pmsgR);

    HFONT hfntOld   = (HFONT) SelectObject(hdc, s_hfntCheck);
    COLORREF crOld  = SetTextColor(hdc, m_crCheckBox);
    int nOldMode    = 0;

    RECT rcDraw = m_rcCheckPxl;
    OffsetRect(&rcDraw, pmsgR->prcGadgetPxl->left, pmsgR->prcGadgetPxl->top);

    WCHAR * pszCheck;
    switch (m_bChecked)
    {
    default:
    case CheckBoxGadget::csUnchecked:
        pszCheck = NULL;
        break;

    case CheckBoxGadget::csChecked:
        pszCheck = L"a";
        nOldMode = SetBkMode(hdc, TRANSPARENT);
        break;

    case CheckBoxGadget::csUnknown:
        pszCheck = L"a";
        nOldMode = SetBkMode(hdc, OPAQUE);
        SetBkColor(hdc, RGB(223, 223, 223));
        break;
    }

    if (pszCheck != NULL) {
        OS()->TextOut(hdc, rcDraw.left - 1, rcDraw.top, pszCheck, 1);
    }

    DrawEdge(hdc, &rcDraw, EDGE_SUNKEN, BF_RECT);

    switch (m_bChecked)
    {
    case CheckBoxGadget::csChecked:
    case CheckBoxGadget::csUnknown:
        SetBkMode(hdc, nOldMode);
        break;
    }

    SetTextColor(hdc, crOld);
    SelectObject(hdc, hfntOld);

    if (m_fKeyboardFocus) {
        RECT rcItem = m_rcItemPxl;
        OffsetRect(&rcItem, pmsgR->prcGadgetPxl->left, pmsgR->prcGadgetPxl->top);
        InflateRect(&rcItem, 2, 1);
        DrawFocusRect(hdc, &rcItem);
    }
}


//------------------------------------------------------------------------------
void        
SmCheckBox::OnDraw(Gdiplus::Graphics * pgpgr, GMSG_PAINTRENDERF * pmsgR)
{
    UNREFERENCED_PARAMETER(pmsgR);

    Gdiplus::Color cr(GetRValue(m_crCheckBox), GetGValue(m_crCheckBox), GetBValue(m_crCheckBox));
    Gdiplus::SolidBrush br(cr);
    Gdiplus::Font fnt(L"Marlett", 8.0f, Gdiplus::FontStyleRegular, Gdiplus::UnitPoint);
    Gdiplus::RectF rc(
            (float) m_rcCheckPxl.left, 
            (float) m_rcCheckPxl.top,
            (float) (m_rcCheckPxl.right - m_rcCheckPxl.left),
            (float) (m_rcCheckPxl.bottom - m_rcCheckPxl.top));
    rc.Offset((float) pmsgR->prcGadgetPxl->X, (float) pmsgR->prcGadgetPxl->Y);

    WCHAR * pszCheck;
    switch (m_bChecked)
    {
    default:
    case CheckBoxGadget::csUnchecked:
        pszCheck = NULL;
        break;

    case CheckBoxGadget::csChecked:
        pszCheck = L"\xf061";
        break;

    case CheckBoxGadget::csUnknown:
        pszCheck = L"\xf061";
        break;
    }

    if (pszCheck != NULL) {
        pgpgr->DrawString(pszCheck, 1, &fnt, rc, 0, &br);
    }

    if (m_fKeyboardFocus) {
        Gdiplus::RectF rcItem(
                (float) m_rcItemPxl.left,
                (float) m_rcItemPxl.top,
                (float) (m_rcItemPxl.right - m_rcItemPxl.left),
                (float) (m_rcItemPxl.bottom - m_rcItemPxl.top));
        rcItem.Offset(pmsgR->prcGadgetPxl->X, pmsgR->prcGadgetPxl->Y);
        rcItem.Inflate(2.0f, 1.0f);

        Gdiplus::Pen pen(cr);
        pgpgr->DrawRectangle(&pen, rcItem);
    }
}


//------------------------------------------------------------------------------
HRESULT
SmCheckBox::ApiGetColor(CheckBoxGadget::GetColorMsg * pmsg)
{
    pmsg->crCheckBox = m_crCheckBox;
    return S_OK;
}


//------------------------------------------------------------------------------
HRESULT
SmCheckBox::ApiSetColor(CheckBoxGadget::SetColorMsg * pmsg)
{
    m_crCheckBox = pmsg->crCheckBox;
    GetStub()->Invalidate();
    return S_OK;
}


//------------------------------------------------------------------------------
HRESULT
SmCheckBox::ApiGetCheck(CheckBoxGadget::GetCheckMsg * pmsg)
{
    pmsg->nCheck = m_bChecked;
    return S_OK;
}


//------------------------------------------------------------------------------
HRESULT
SmCheckBox::ApiSetCheck(CheckBoxGadget::SetCheckMsg * pmsg)
{
    if (pmsg->nCheck <= GetMaxCheck()) {
        m_bChecked = (BYTE) pmsg->nCheck;
        GetStub()->Invalidate();
        return S_OK;
    }

    return E_INVALIDARG;
}


//------------------------------------------------------------------------------
HRESULT
SmCheckBox::ApiGetType(CheckBoxGadget::GetTypeMsg * pmsg)
{
    pmsg->nType = m_bType;
    return S_OK;
}


//------------------------------------------------------------------------------
HRESULT
SmCheckBox::ApiSetType(CheckBoxGadget::SetTypeMsg * pmsg)
{
    if (pmsg->nType <= CheckBoxGadget::ctMax) {
        m_bType = (BYTE) pmsg->nType;
        return S_OK;
    }

    return E_INVALIDARG;
}


//------------------------------------------------------------------------------
HRESULT
SmCheckBox::ApiGetItem(CheckBoxGadget::GetItemMsg * pmsg)
{
    pmsg->pgvItem = m_pgvItem;
    return S_OK;
}


//------------------------------------------------------------------------------
HRESULT
SmCheckBox::ApiSetItem(CheckBoxGadget::SetItemMsg * pmsg)
{
    AssertMsg(m_pgvItem == NULL, "TODO: Destroy existing item");

    m_pgvItem = pmsg->pgvItem;

    if (m_pgvItem != NULL) {
        ComputeLayout();
    }

    m_fText = FALSE;
    return S_OK;
}


//------------------------------------------------------------------------------
HRESULT
SmCheckBox::ApiSetText(CheckBoxGadget::SetTextMsg * pmsg)
{
    TextGadget * pgvText = NULL;
    if (!m_fText) {
        AssertMsg(m_pgvItem == NULL, "TODO: Destroy existing item");

        pgvText = BuildVisual<TextGadget>((Visual *) m_pgad);
        m_pgvItem = pgvText;
        if (m_pgvItem == NULL) {
            return S_OK;
        }

        m_fText = TRUE;
    } else {
        pgvText = (TextGadget *) m_pgvItem;
    }

    AssertMsg(m_pgvItem != NULL, "Must have valid TextGadget by now");

    if (s_hfntText == NULL) {
        s_hfntText = UtilBuildFont(L"Tahoma", 85, FS_NORMAL);
    }

    pgvText->SetAutoSize(TRUE);
    pgvText->SetFont(s_hfntText);
    pgvText->SetText(pmsg->pszText);
    pgvText->SetColor(RGB(0, 0, 128));

    ComputeLayout();

    return S_OK;
}


//------------------------------------------------------------------------------
void        
SmCheckBox::ComputeLayout()
{
    SIZE sizePxl;
    GetStub()->GetSize(&sizePxl);
    int nHeight = sizePxl.cy;

    if (m_pgvItem != NULL) {
        SIZE sizeBound, sizeBorderPxl;
        sizeBound.cx        = sizePxl.cx - (SIZE_Box.cx + 4 + 4);
        sizeBound.cy        = sizePxl.cy - 2;
        sizeBorderPxl.cx    = SIZE_Box.cx + 4;
        sizeBorderPxl.cy    = (sizePxl.cy - sizeBound.cy) / 2;

        m_pgvItem->SetRect(SGR_MOVE | SGR_SIZE | SGR_PARENT | SGR_NOINVALIDATE, 
                sizeBorderPxl.cx, sizeBorderPxl.cy, sizeBound.cx, sizeBound.cy);

        m_rcItemPxl.left    = sizeBorderPxl.cx;
        m_rcItemPxl.top     = sizeBorderPxl.cy;
        m_rcItemPxl.right   = m_rcItemPxl.left + sizeBound.cx;
        m_rcItemPxl.bottom  = m_rcItemPxl.top + sizeBound.cy;
    }

    m_rcCheckPxl.left   = 0;
    m_rcCheckPxl.top    = (nHeight - SIZE_Box.cy) / 2;
    m_rcCheckPxl.right  = m_rcCheckPxl.left + SIZE_Box.cx;
    m_rcCheckPxl.bottom = m_rcCheckPxl.top + SIZE_Box.cy;
}

#endif // ENABLE_MSGTABLE_API