#include "bands.h"

#define DM_PERSIST      0           // trace IPS::Load, ::Save, etc.
#define DM_MENU         0           // menu code
#define DM_FOCUS        0           // focus
#define DM_FOCUS2       0           // like DM_FOCUS, but verbose

//=================================================================
// Implementation of CToolBand
//=================================================================

ULONG CToolBand::AddRef()
{
    _cRef++;
    return _cRef;
}

ULONG CToolBand::Release()
{
    ASSERT(_cRef > 0);
    _cRef--;

    if (_cRef > 0)
        return _cRef;

    delete this;
    return 0;
}

HRESULT CToolBand::QueryInterface(REFIID riid, void **ppvObj)
{
    static const QITAB qit[] =
    {
        QITABENT(CToolBand, IDeskBand),         // IID_IDeskBand
        QITABENTMULTI(CToolBand, IOleWindow, IDeskBand),        // IID_IOleWindod
        QITABENTMULTI(CToolBand, IDockingWindow, IDeskBand),    // IID_IDockingWindow
        QITABENT(CToolBand, IInputObject),      // IID_IInputObject
        QITABENT(CToolBand, IOleCommandTarget), // IID_IOleCommandTarget
        QITABENT(CToolBand, IServiceProvider),  // IID_IServiceProvider
        QITABENT(CToolBand, IPersistStream),    // IID_IPersistStream
        QITABENTMULTI(CToolBand, IPersist, IPersistStream),     // IID_IPersist
        QITABENT(CToolBand, IObjectWithSite),   // IID_IObjectWithSite
        { 0 },
    };

    return QISearch(this, qit, riid, ppvObj);
}

//  *** IOleCommandTarget methods ***

HRESULT CToolBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
{
    return E_NOTIMPL;
}

HRESULT CToolBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
    return E_NOTIMPL;
}

//  *** IServiceProvider methods ***

HRESULT CToolBand::QueryService(REFGUID guidService,
                                  REFIID riid, void **ppvObj)
{
    return IUnknown_QueryService(_punkSite, guidService, riid, ppvObj);
}

//  *** IOleWindow methods ***

HRESULT CToolBand::GetWindow(HWND * lphwnd)
{
    *lphwnd = _hwnd;

    if (*lphwnd)
        return S_OK;

    return E_FAIL;
}

//  *** IInputObject methods ***

HRESULT CToolBand::TranslateAcceleratorIO(LPMSG lpMsg)
{
    return E_NOTIMPL;
}

HRESULT CToolBand::HasFocusIO()
{
    HRESULT hres;
    HWND hwndFocus = GetFocus();

    hres = SHIsChildOrSelf(_hwnd, hwndFocus);
    ASSERT(hwndFocus != NULL || hres == S_FALSE);
    ASSERT(_hwnd != NULL || hres == S_FALSE);

    return hres;
}

HRESULT CToolBand::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
{
    ASSERT(NULL == lpMsg || IS_VALID_WRITE_PTR(lpMsg, MSG));

    TraceMsg(DM_FOCUS, "ctb.uiaio(fActivate=%d) _fCanFocus=%d _hwnd=%x GF()=%x", fActivate, _fCanFocus, _hwnd, GetFocus());

    if (!_fCanFocus) {
        TraceMsg(DM_FOCUS, "ctb.uiaio: !_fCanFocus ret S_FALSE");
        return S_FALSE;
    }

    if (fActivate)
    {
        IUnknown_OnFocusChangeIS(_punkSite, SAFECAST(this, IInputObject*), TRUE);
        SetFocus(_hwnd);
    }

    return S_OK;
}

HRESULT CToolBand::ResizeBorderDW(LPCRECT prcBorder,
                                         IUnknown* punkToolbarSite,
                                         BOOL fReserved)
{
    return S_OK;
}


HRESULT CToolBand::ShowDW(BOOL fShow)
{
    return S_OK;
}

HRESULT CToolBand::SetSite(IUnknown *punkSite)
{
    if (punkSite != _punkSite)
    {
        IUnknown_Set(&_punkSite, punkSite);
        IUnknown_GetWindow(_punkSite, &_hwndParent);
    }
    return S_OK;
}

HRESULT CToolBand::_BandInfoChanged()
{
    VARIANTARG v = {0};
    VARIANTARG* pv = NULL;
    if (_dwBandID != (DWORD)-1)
    {
        v.vt = VT_I4;
        v.lVal = _dwBandID;
        pv = &v;
    }
    else
    {
        // if this fires, fix your band's GetBandInfo to set _dwBandID.
        // o.w. it's a *big* perf loss since we refresh *all* bands rather
        // than just yours.
        // do *not* remove this ASSERT, bad perf *is* a bug.
        ASSERT(_dwBandID != (DWORD)-1);
    }
    return IUnknown_Exec(_punkSite, &CGID_DeskBand, DBID_BANDINFOCHANGED, 0, pv, NULL);
}

//  *** IPersistStream methods ***

HRESULT CToolBand::IsDirty(void)
{
    return S_FALSE;     // never be dirty
}

HRESULT CToolBand::GetSizeMax(ULARGE_INTEGER *pcbSize)
{
    return E_NOTIMPL;
}

CToolBand::CToolBand() : _cRef(1)
{
    _dwBandID = (DWORD)-1;
    DllAddRef();
}

CToolBand::~CToolBand()
{
    ASSERT(_hwnd == NULL);      // CloseDW was called
    ASSERT(_punkSite == NULL);  // SetSite(NULL) was called

    DllRelease();
}

HRESULT CToolBand::CloseDW(DWORD dw)
{
    if (_hwnd)
    {
        DestroyWindow(_hwnd);
        _hwnd = NULL;
    }
    
    return S_OK;
}


//=================================================================
// Implementation of CToolbarBand
//=================================================================
// Class for bands whose _hwnd is a toolbar control.  Implements
// functionality generic to all such bands (e.g. hottracking 
// behavior).
//=================================================================

HRESULT CToolbarBand::_PushChevron(BOOL bLast)
{
    if (_dwBandID == (DWORD)-1)
        return E_UNEXPECTED;

    VARIANTARG v;
    v.vt = VT_I4;
    v.lVal = bLast ? DBPC_SELECTLAST : DBPC_SELECTFIRST;

    return IUnknown_Exec(_punkSite, &CGID_DeskBand, DBID_PUSHCHEVRON, _dwBandID, &v, NULL);
}

LRESULT CToolbarBand::_OnHotItemChange(LPNMTBHOTITEM pnmtb)
{
    LRESULT lres = 0;

    if (!(pnmtb->dwFlags & (HICF_LEAVING | HICF_MOUSE)))
    {
        // check to see if new hot button is clipped.  if it is,
        // then we pop down the chevron menu.
        RECT rc;
        GetClientRect(_hwnd, &rc);

        int iButton = (int)SendMessage(_hwnd, TB_COMMANDTOINDEX, pnmtb->idNew, 0);
        DWORD dwEdge = SHIsButtonObscured(_hwnd, &rc, iButton);
        if (dwEdge)
        {
            //
            // Only pop down the menu if the button is obscured
            // along the axis of the toolbar
            //
            BOOL fVertical = (ToolBar_GetStyle(_hwnd) & CCS_VERT);

            if ((fVertical && (dwEdge & (EDGE_TOP | EDGE_BOTTOM)))
                || (!fVertical && (dwEdge & (EDGE_LEFT | EDGE_RIGHT))))
            {
                // clear hot item
                SendMessage(_hwnd, TB_SETHOTITEM, -1, 0);

                // figure out whether to highlight first or last button in dd menu
                int cButtons = (int)SendMessage(_hwnd, TB_BUTTONCOUNT, 0, 0);
                BOOL bLast = (iButton == cButtons - 1);
                _PushChevron(bLast);
                lres = 1;
            }
        }
    }

    return lres;
}

LRESULT CToolbarBand::_OnNotify(LPNMHDR pnmh)
{
    LRESULT lres = 0;

    switch (pnmh->code)
    {
    case TBN_HOTITEMCHANGE:
        lres = _OnHotItemChange((LPNMTBHOTITEM)pnmh);
        break;
    }

    return lres;
}

// *** IWinEventHandler methods ***

HRESULT CToolbarBand::OnWinEvent(HWND hwnd, UINT dwMsg, WPARAM wParam, LPARAM lParam, LRESULT* plres)
{
    HRESULT hres = S_OK;

    switch (dwMsg)
    {
    case WM_NOTIFY:
        *plres = _OnNotify((LPNMHDR)lParam);
        break;

    case WM_WININICHANGE:
        InvalidateRect(_hwnd, NULL, TRUE);
        _BandInfoChanged();
        break;
    }

    return hres;
}

HRESULT CToolbarBand::IsWindowOwner(HWND hwnd)
{
    if (hwnd == _hwnd)
        return S_OK;
    else
        return S_FALSE;
}
