#include "shellprv.h"
#include "common.h"
#include "menuband.h"
#include "dpastuff.h"       // COrderList_*
#include "resource.h"
#include "mnbase.h"
#include "oleacc.h"
#include "iaccess.h"
#include "uemapp.h"
#include "util.h"

#ifdef UXCTRL_VERSION
#include <uxtheme.h>
#include <tmschema.h>
#endif

// Conflicts with one defined in winuserp.h
#undef WINEVENT_VALID       //It's tripping on this...
#include "winable.h"

const TCHAR c_wzMenuBandTheme[] = TEXT("MenuBand");

#define DM_MISC     0               // miscellany

#define MAXUEMTIMEOUT 2000

/*----------------------------------------------------------
Purpose: Return the button command given the position.

*/
int GetButtonCmd(HWND hwnd, int iPos)
{
    ASSERT(IsWindow(hwnd));
    int nRet = -1;          // Punt on failure

    TBBUTTON tbb;
    if (ToolBar_GetButton(hwnd, iPos, &tbb))
    {
        nRet = tbb.idCommand;
    }
    return nRet;
}    



void* ItemDataFromPos(HWND hwndTB, int iPos)
{
    TBBUTTONINFO tbbi;
    tbbi.cbSize = sizeof(tbbi);
    tbbi.dwMask = TBIF_LPARAM | TBIF_BYINDEX;
    if (ToolBar_GetButtonInfo(hwndTB, iPos, &tbbi) >= 0)
    {
        return (void*)tbbi.lParam;
    }

    return NULL;
}

long GetIndexFromChild(BOOL fTop, int iIndex)
{
    return  (fTop? TOOLBAR_MASK: 0) | iIndex + 1;
}

//--------------------------------------------------------------------------------
//
// CMenuToolbarBase
//
//--------------------------------------------------------------------------------

CMenuToolbarBase::~CMenuToolbarBase()
{
    Str_SetPtr(&_pszTheme, NULL);
    if (_hTheme)
    {
        CloseThemeData(_hTheme);
    }
}

CMenuToolbarBase::CMenuToolbarBase(CMenuBand* pmb, DWORD dwFlags) : _pcmb(pmb)
{
#ifdef DEBUG
    _cRef = 1;
#endif
    _dwFlags = dwFlags;
    _nItemTimer = -1;
    _idCmdChevron = -1;
    _fFirstTime = TRUE;
}

// *** IObjectWithSite methods ***

HRESULT CMenuToolbarBase::SetSite(IUnknown *punkSite)
{
    ASSERT(punkSite && IS_VALID_READ_PTR(punkSite, CMenuBand*));

    // We are guaranteed the lifetime of this object is contained within
    // the menuband, so we don't addref pcmb.
    if (SUCCEEDED(punkSite->QueryInterface(CLSID_MenuBand, (void**)&_pcmb))) 
    {
        punkSite->Release();
    } 
    else 
    {
        ASSERT(0);
    }

    _fVerticalMB = !BOOLIFY(_pcmb->_dwFlags & SMINIT_HORIZONTAL);
    _fTopLevel = BOOLIFY(_pcmb->_dwFlags & SMINIT_TOPLEVEL);
    
    return S_OK;
}

HRESULT CMenuToolbarBase::GetSite(REFIID riid, void ** ppvSite)
{
    if (!_pcmb)
        return E_FAIL;

    return _pcmb->QueryInterface(riid, ppvSite);
}

// *** IUnknown methods ***

STDMETHODIMP_(ULONG) CMenuToolbarBase::AddRef()
{
    DEBUG_CODE(_cRef++);
    if (_pcmb)
    {
        return _pcmb->AddRef();
    }

    return 0;
}


STDMETHODIMP_(ULONG) CMenuToolbarBase::Release()
{
    ASSERT(_cRef > 0);
    DEBUG_CODE(_cRef--);

    if (_pcmb)
    {
        return _pcmb->Release();
    }

    return 0;
}

HRESULT CMenuToolbarBase::QueryInterface(REFIID riid, void** ppvObj)
{
    HRESULT hres;
    if (IsEqualGUID(riid, CLSID_MenuToolbarBase) && ppvObj) 
    {
        AddRef();
        *ppvObj = (void*)this;
        hres = S_OK;
    }
    else
        hres = _pcmb->QueryInterface(riid, ppvObj);

    return hres;
}

void CMenuToolbarBase::SetToTop(BOOL bToTop)
{
    // A menu toolbar can be at the top or the bottom of the menu.
    // This is an exclusive attribute.
    if (bToTop)
    {
        _dwFlags |= SMSET_TOP;
        _dwFlags &= ~SMSET_BOTTOM;
    }
    else
    {
        _dwFlags |= SMSET_BOTTOM;
        _dwFlags &= ~SMSET_TOP;
    }
}


void CMenuToolbarBase::KillPopupTimer()
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    TraceMsg(TF_MENUBAND, "(pmb=%#08lx): Killing Popout Timer...", this);
    KillTimer(_hwndMB, MBTIMER_POPOUT);
    _nItemTimer = -1;
}


void CMenuToolbarBase::SetWindowPos(LPSIZE psize, LPRECT prc, DWORD dwFlags)
{
    if (_hwndMB)
    {
        ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
        DWORD rectWidth = RECTWIDTH(*prc);
        TraceMsg(TF_MENUBAND, "CMTB::SetWindowPos %d - (%d,%d,%d,%d)", psize?psize->cx:0,
            prc->left, prc->top, prc->right, prc->bottom);
        ::SetWindowPos(_hwndMB, NULL, prc->left, prc->top, 
            rectWidth, RECTHEIGHT(*prc), SWP_NOZORDER | SWP_NOACTIVATE | dwFlags);
        // hackhack:  we only do this when multicolumn.  this call is to facilitate the size negotiation between 
        // static menu and folder menu.  Set the width of the toolbar to the width of the button in case 
        // of non-multicolumn.
        if (!(_fMulticolumnMB) && psize)
        {
            int cx = psize->cx;
            ToolBar_SetButtonWidth(_hwndMB, cx, cx);
        }

        // Force this to redraw. I put this here because the HMenu portion was painting after the shell
        // folder portion was done enumerating the folder, which is pretty slow. I wanted the HMENU portion
        // to paint right away...
        RedrawWindow(_hwndMB, NULL, NULL, RDW_UPDATENOW);
    }
}

// NOTE: if psize is (0,0) we use tb button size as param in figuring out ideal tb size
//   else we use max of psize length and tb button length as our metric
void CMenuToolbarBase::GetSize(SIZE* psize)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first

    if (_hwndMB)
    {
        LRESULT lButtonSize;

        lButtonSize = TB_GetButtonSizeWithoutThemeBorder(_hwndMB, _hTheme);

        if (psize->cx || psize->cy)
        {
            int cx = max(psize->cx, LOWORD(lButtonSize));
            int cy = max(psize->cy, HIWORD(lButtonSize));
            lButtonSize = MAKELONG(cx, cy);
        }

        if (_fVerticalMB)
        {
            psize->cx = LOWORD(lButtonSize);
            SendMessage(_hwndMB, TB_GETIDEALSIZE, TRUE, (LPARAM)psize);
        }
        else
        {
            psize->cy = HIWORD(lButtonSize);
            SendMessage(_hwndMB, TB_GETIDEALSIZE, FALSE, (LPARAM)psize);
        }

        TraceMsg(TF_MENUBAND, "CMTB::GetSize (%d, %d)", psize->cx, psize->cy);
    }
}


/*----------------------------------------------------------
Purpose: Timer handler.  Used to pop open/close cascaded submenus.

*/
LRESULT CMenuToolbarBase::_OnTimer(WPARAM wParam)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first

    switch (wParam) 
    {

    case MBTIMER_INFOTIP:
        {
            // Do we have a hot item to display the tooltip for?
            int iHotItem = ToolBar_GetHotItem(_hwndMB);
            KillTimer(_hwndMB, wParam);
            if (iHotItem >= 0)
            {
                // Yep.
                TCHAR szTip[MAX_PATH];
                int idCmd = GetButtonCmd(_hwndMB, iHotItem);

                // Ask the superclass for the tip
                if (S_OK == v_GetInfoTip(idCmd, szTip, ARRAYSIZE(szTip)))
                {
                    // Now display it. Yawn.
                    _pcmb->_pmbState->CenterOnButton(_hwndMB, FALSE, idCmd, NULL, szTip);
                }
            }
        }
        break;

    case MBTIMER_CHEVRONTIP:
        KillTimer(_hwndMB, wParam);
       _pcmb->_pmbState->HideTooltip(TRUE);
       break;
 
    case MBTIMER_FLASH:
        {
            _cFlashCount++;
            if (_cFlashCount == COUNT_ENDFLASH)
            {
                _cFlashCount = 0;
                KillTimer(_hwndMB, wParam);
                ToolBar_MarkButton(_hwndMB, _idCmdChevron, FALSE);
                _SetTimer(MBTIMER_UEMTIMEOUT);

                // Now that we've flashed, let's show the Chevron tip.
                // This is for a confused user: If they've hovered over an item for too long,
                // or this is the first time they've seen intellimenus, then we flash and display
                // the tooltip. We only want to display this if we are shown: We would end up with
                // and dangling tooltip if you happen to move to another menu while it was flashing.
                // Ummm, is the Chevron still visible?
                if (_fShowMB && _idCmdChevron != -1)
                {
                    TCHAR szTip[MAX_PATH];
                    TCHAR szTitle[MAX_PATH];
                    if (S_OK == v_CallCBItem(_idCmdChevron, SMC_CHEVRONGETTIP, (WPARAM)szTitle, (LPARAM)szTip))
                    {
                        _pcmb->_pmbState->CenterOnButton(_hwndMB, TRUE, _idCmdChevron, szTitle, szTip);
                        _SetTimer(MBTIMER_CHEVRONTIP);
                    }
                }
            }
            else
                ToolBar_MarkButton(_hwndMB, _idCmdChevron, (_cFlashCount % 2) == 0);
        }
        break;

    case MBTIMER_UEMTIMEOUT:
        {
            POINT pt;
            RECT rect;

            // Don't fire timeouts when we're in edit mode.
            if (_fEditMode)
            {
                KillTimer(_hwndMB, wParam);
                break;
            }

            GetWindowRect(_hwndMB, &rect);
            GetCursorPos(&pt);
            if (PtInRect(&rect, pt))
            {
                TraceMsg(TF_MENUBAND, "*** UEM TimeOut. At Tick Count (%d) ***", GetTickCount());
                _FireEvent(UEM_TIMEOUT);
            }
            else
            {
                TraceMsg(TF_MENUBAND, " *** UEM TimeOut. At Tick Count (%d)."
                    " Mouse outside menu. Killing *** ", GetTickCount());
                KillTimer(_hwndMB, wParam);
            }
        }
        break;


    case MBTIMER_EXPAND:
        KillTimer(_hwndMB, wParam);
        if (_fShowMB)
        {
            v_CallCBItem(_idCmdChevron, SMC_CHEVRONEXPAND, 0, 0);
            Expand(TRUE);
            _fClickHandled = TRUE;
            _SetTimer(MBTIMER_CLICKUNHANDLE); 
        }
        break;

    case MBTIMER_DRAGPOPDOWN:
        // There has not been a drag enter in this band for a while, 
        // so we'll try to cancel the menus.
        KillTimer(_hwndMB, wParam);
        PostMessage(_pcmb->_pmbState->GetSubclassedHWND(), g_nMBDragCancel, 0, 0);
        break;

    case MBTIMER_DRAGOVER:
        {
            TraceMsg(TF_MENUBAND, "CMenuToolbarBase::OnTimer(DRAG)");
            KillTimer(_hwndMB, wParam);
            DAD_ShowDragImage(FALSE);
            // Does this item cascade?
            int idBtn = GetButtonCmd(_hwndMB, v_GetDragOverButton());
            DWORD dwFlags = v_GetFlags(idBtn);
            if (dwFlags & SMIF_SUBMENU)
            {
                TraceMsg(TF_MENUBAND, "CMenuToolbarBase::OnTimer(DRAG): Is a submenu");
                // Yes; pop it open
                if (!_fVerticalMB)
                    _pcmb->_fInvokedByDrag = TRUE;
                _DoPopup(idBtn, FALSE);
            }
            else if (dwFlags & SMIF_DRAGNDROP)
            {
                v_CallCBItem(idBtn, SMC_EXEC, 0, 1);
            }
            else if (idBtn == _idCmdChevron)
            {
                Expand(TRUE);

            }
            else
            {
                _pcmb->_SubMenuOnSelect(MPOS_CANCELLEVEL);
            }

        }
        break;

    case MBTIMER_POPOUT:
        {
            int nItemTimer = _nItemTimer;
            KillPopupTimer();

            // Popup a new submenu?
            if (-1 != nItemTimer)
            {
                if (nItemTimer != _pcmb->_nItemCur)
                {
                    // Yes;  post message since the currently expanded submenu
                    // may be a CTrackPopup object, which posts its cancel mode.

                    TraceMsg(TF_MENUBAND, "(pmb=%#08lx): Timer went off.  Expanding...", this);
                    PostPopup(nItemTimer, FALSE, FALSE);
                }
            }
            else 
            {
                // No; just collapse the currently open submenu
                TraceMsg(TF_MENUBAND, "(pmb=%#08lx): _OnTimer sending MPOS_CANCELLEVEL to submenu popup", this);
                _pcmb->_SubMenuOnSelect(MPOS_CANCELLEVEL);
            }
            break;
        }
    
    case MBTIMER_CLOSE:
        KillTimer(_hwndMB, wParam);

        TraceMsg(TF_MENUBAND, "(pmb=%#08lx): _OnTimer sending MPOS_FULLCANCEL", this);

        if (_fVerticalMB)
            _pcmb->_SiteOnSelect(MPOS_FULLCANCEL);
        else
        {
            _pcmb->_SubMenuOnSelect(MPOS_FULLCANCEL);
        }
        break;
    }
        
    return 1;
}


void CMenuToolbarBase::_DrawMenuArrowGlyph( HDC hdc, RECT * prc, COLORREF rgbText )
{
    SIZE size = {_pcmb->_pmbm->_cxArrow, _pcmb->_pmbm->_cyArrow};

    //
    // If the DC is mirrred, then the Arrow should be mirrored
    // since it is done thru TextOut, NOT the 2D graphics APIs [samera]
    //

    _DrawMenuGlyph(hdc, 
                   _pcmb->_pmbm->_hFontArrow,
                   prc, 
                   (IS_DC_RTL_MIRRORED(hdc)) ? CH_MENUARROWRTLA :
                   CH_MENUARROWA, 
                   rgbText, 
                   &size);
}


void CMenuToolbarBase::_DrawMenuGlyph( HDC hdc, HFONT hFont, RECT * prc, 
                               CHAR ch, COLORREF rgbText,
                               LPSIZE psize)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    if (_pcmb->_pmbm->_hFontArrow)
    {
        SIZE    size;
        int cx, cy, y, x;
        HFONT hFontOld;
        int iOldBk = SetBkMode(hdc, TRANSPARENT);
        hFontOld = (HFONT)SelectObject(hdc, hFont);
        if (psize == NULL)
        {
            GetTextExtentPoint32A( hdc, &ch, 1, &size);
            psize = &size;
        }

        cy = prc->bottom - prc->top;
        y = prc->top  + ((cy - psize->cy) / 2);

        cx = prc->right - prc->left;
        x = prc->left + ((cx - psize->cx) /2);
    
        COLORREF rgbOld = SetTextColor(hdc, rgbText);

        TextOutA(hdc, x, y, &ch, 1);
    
        SetTextColor(hdc, rgbOld);
        SetBkMode(hdc, iOldBk);
        SelectObject(hdc, hFontOld);
    }
}

void CMenuToolbarBase::SetMenuBandMetrics(CMenuBandMetrics* pmbm)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first

    // This can be called before the toolbar is created. 
    // So we'll check this condition. When the toolbar is created, then
    // the toolbar will get the metrics at that point.
    if (!_hwndMB)
        return;

    //Loop through toolbar.
    for (int iButton = ToolBar_ButtonCount(_hwndMB)-1; iButton >= 0; iButton--)
    {
        IOleCommandTarget* poct;

        int idCmd = GetButtonCmd(_hwndMB, iButton);

        // If it's not a seperator, see if there is a sub menu.
        if (idCmd != -1 &&
            SUCCEEDED(v_GetSubMenu(idCmd, NULL, IID_PPV_ARG(IOleCommandTarget, &poct))))
        {
            VARIANT Var;
            Var.vt = VT_UNKNOWN;
            Var.punkVal = SAFECAST(pmbm, IUnknown*);

            // Exec to set new Metrics.
            poct->Exec(&CGID_MenuBand, MBANDCID_SETFONTS, 0, &Var, NULL);
            poct->Release();
        }
    }

    _SetFontMetrics();
    // return
}

void CMenuToolbarBase::_SetFontMetrics()
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    if (_hwndMB && _pcmb->_pmbm)
    {
        SendMessage(_hwndMB, WM_SETFONT, (WPARAM)_pcmb->_pmbm->_hFontMenu, FALSE);

        IUnknown_QueryServiceExec(_pcmb->_punkSite, SID_SMenuPopup, &CGID_MENUDESKBAR, 
            MBCID_SETFLAT, _pcmb->_pmbm->_fFlatMenuMode, NULL, NULL);
    }
}


HRESULT CMenuToolbarBase::CreateToolbar(HWND hwndParent)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    ASSERT( _hwndMB != NULL );
    DWORD dwToolBarStyle = TBSTYLE_TRANSPARENT;

    // if we're set up as a popup, don't do any transparent stuff
    if (_fVerticalMB) 
    {
        dwToolBarStyle  = TBSTYLE_CUSTOMERASE;    // Vertical Toolbars don't get Transparent
        DWORD dwExtendedStyle = 0;

        // This is for TBMenu which actually has a Horizontal menubar within the 
        // Vertical menuband.
        if (!_fHorizInVerticalMB)
            dwExtendedStyle |= TBSTYLE_EX_VERTICAL;

        if (_fMulticolumnMB)
            dwExtendedStyle |= TBSTYLE_EX_MULTICOLUMN;

        ToolBar_SetExtendedStyle(_hwndMB, 
            dwExtendedStyle, TBSTYLE_EX_VERTICAL | TBSTYLE_EX_MULTICOLUMN);

        ToolBar_SetListGap(_hwndMB, LIST_GAP);
    }

    ToolBar_SetExtendedStyle(_hwndMB, TBSTYLE_EX_DOUBLEBUFFER, TBSTYLE_EX_DOUBLEBUFFER);

    SHSetWindowBits(_hwndMB, GWL_STYLE, 
        TBSTYLE_TRANSPARENT | TBSTYLE_CUSTOMERASE, dwToolBarStyle );

    ToolBar_SetInsertMarkColor(_hwndMB, GetSysColor(COLOR_MENUTEXT));

    v_UpdateIconSize(_pcmb->_uIconSize, FALSE);

    _SetFontMetrics();

    if (_pszTheme)
    {
        SendMessage(_hwndMB, TB_SETWINDOWTHEME, 0, (LPARAM)_pszTheme);
        _RefreshTheme();
    }

    return S_OK;
}


HRESULT CMenuToolbarBase::_SetMenuBand(IShellMenu* psm)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    HRESULT hres = E_FAIL;
    IBandSite* pmbs = NULL;
    if (!_pcmb->_pmpSubMenu)
    {
        hres = CoCreateInstance(CLSID_MenuDeskBar, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IMenuPopup, &_pcmb->_pmpSubMenu));
        if (SUCCEEDED(hres))
        {
            IUnknown_SetSite(_pcmb->_pmpSubMenu, SAFECAST(_pcmb, IOleCommandTarget*));
            hres = CoCreateInstance(CLSID_MenuBandSite, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IBandSite, &pmbs));
            if (SUCCEEDED(hres))
            {
                hres = _pcmb->_pmpSubMenu->SetClient(pmbs);
                // Don't release pmbs here. We are using below
            }
            // Menu band will Release _pmpSubMenu.
        }
    }
    else
    {
        IUnknown* punk;
        _pcmb->_pmpSubMenu->GetClient(&punk);
        if (punk)
        {
            hres = punk->QueryInterface(IID_PPV_ARG(IBandSite, &pmbs));
            punk->Release();
        }
    }

    if (pmbs)
    {
        if (SUCCEEDED(hres))
            hres = pmbs->AddBand(psm);

        pmbs->Release();
    }
 
    return hres;
}

HRESULT CMenuToolbarBase::GetSubMenu(int idCmd, GUID* pguidService, REFIID riid, void** ppvObj)
{
    // pguidService is for asking a for specifically the Shell Folder portion or the Static portion
    HRESULT hres = E_FAIL;
    if (v_GetFlags(idCmd) & SMIF_TRACKPOPUP ||
        _pcmb->_dwFlags & SMINIT_DEFAULTTOTRACKPOPUP)
    {
        hres = v_CreateTrackPopup(idCmd, riid, (void**)ppvObj);
        if (SUCCEEDED(hres))
        {
            _pcmb->SetTrackMenuPopup((IUnknown*)*ppvObj);
        }
    }
    else
    {
        IShellMenu* psm;
        hres = v_GetSubMenu(idCmd, pguidService, IID_PPV_ARG(IShellMenu, &psm));
        if (SUCCEEDED(hres)) 
        {
            if (_pszTheme)
            {
                IShellMenu2* psm2;
                if (SUCCEEDED(psm->QueryInterface(IID_PPV_ARG(IShellMenu2, &psm2))))
                {
                    if (_fNoBorder)
                    {
                        psm2->SetNoBorder(_fNoBorder);
                    }

                    psm2->SetTheme(_pszTheme);
                    psm2->Release();
                }
            }

            TraceMsg(TF_MENUBAND, "GetUIObject psm %#lx", psm);
            _pcmb->SetTracked(this);

            hres = _SetMenuBand(psm);
            psm->Release();

            // Did we succeed in getting a menupopup?
            if (SUCCEEDED(hres))
            {
                // Yep; Sweet!
                _pcmb->_pmpSubMenu->QueryInterface(riid, ppvObj);

                HWND hwnd;
                IUnknown_GetWindow(_pcmb->_pmpSubMenu, &hwnd);
                PostMessage(_pcmb->_pmbState->GetSubclassedHWND(), g_nMBAutomation, (WPARAM)hwnd, (LPARAM)-1);
            }
        }
    }

    return hres;
}

HRESULT CMenuToolbarBase::PositionSubmenu(int idCmd)
{
    IMenuPopup* pmp = NULL;
    HRESULT hres = E_FAIL;
    DWORD dwFlags = 0;

    if (_pcmb->_fInSubMenu)
    {
        // Since the selection has probrably changed, we use the cached item id
        // to calculate the postion rect
        idCmd = _pcmb->_nItemSubMenu;
        dwFlags = MPPF_REPOSITION | MPPF_NOANIMATE;
        pmp = _pcmb->_pmpSubMenu;
        pmp->AddRef();

        ASSERT(pmp);    // If _fInSubmenu is set, then this must be valid
        hres = S_OK;
    }
    else
    {
        // Only do these when we're not repositioning.
        if (_pcmb->_fInitialSelect)
            dwFlags |= MPPF_INITIALSELECT;

        if (!_pcmb->_fCascadeAnimate)
            dwFlags |= MPPF_NOANIMATE;

        _pcmb->_nItemSubMenu = idCmd;

        hres = GetSubMenu(idCmd, NULL, IID_PPV_ARG(IMenuPopup, &pmp));
    }

    ASSERT(idCmd != -1);    // Make sure at this point we have an item.


    if (SUCCEEDED(hres))
    {
        ASSERT(pmp);

        // Make sure the menuitem is pressed
        _PressBtn(idCmd, TRUE);

        RECT rc;
        RECT rcTB;
        RECT rcTemp;
        POINT pt;

        if (!SendMessage(_hwndMB, TB_GETRECT, idCmd, (LPARAM)&rc))
        {
            // Under weird conditions, idCmd can be invalid.
            // (See bug 403077.)  So just blow off the reposition
            // and keep using the old position.
            hres = E_FAIL;
        }
        else
        {
            GetClientRect(_hwndMB, &rcTB);

            if (rc.right > rcTB.right)
            {
                rc.right = rcTB.right;
            }

            // Is the button rect within the boundries of the
            // visible toolbar?
            if (!IntersectRect(&rcTemp, &rcTB, &rc))
            {
                // No; Then we need to bias that rect into
                // the visible region of the toolbar.
                // We only want to bias one side
                if (rc.left > rcTB.right)
                {
                    rc.left = rcTB.right - (rc.right - rc.left);
                    rc.right = rcTB.right;
                }
            }

            MapWindowPoints(_hwndMB, HWND_DESKTOP, (POINT*)&rc, 2);

            if (_fVerticalMB)
            {
                pt.x = rc.right;
                pt.y = rc.top;
            }
            else
            {
                //
                // If the shell dropdown (toolbar button) menus are mirrored,
                // then take the right edge as the anchor point
                //
                if (IS_WINDOW_RTL_MIRRORED(_hwndMB))
                    pt.x = rc.right;
                else
                    pt.x = rc.left;
                pt.y = rc.bottom;
            }

            // Since toolbar buttons expand almost to the end of the basebar,
            // shrink the exclude rect so if overlaps.
            // NOTE: the items are GetSystemMetrics(SM_CXEDGE) larger than before. So adjust to that.

            if (_pcmb->_fExpanded)
                InflateRect(&rc, -GetSystemMetrics(SM_CXEDGE), 0);

            // We want to stop showing the chevron tip when we cascade into another menu
            _pcmb->_pmbState->HideTooltip(TRUE);

            // Only animate the first show at this level.
            _pcmb->_fCascadeAnimate = FALSE;

            hres = pmp->Popup((POINTL*)&pt, (RECTL*)&rc, dwFlags);
        }
        pmp->Release();

    }
    return hres;
}
/*----------------------------------------------------------
Purpose: Cascade to the _nItemCur item's menu popup.

         If the popup call was modal, S_FALSE is returned; otherwise
         it is S_OK, or error.

*/
HRESULT CMenuToolbarBase::PopupOpen(int idBtn)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    HRESULT hres = E_FAIL;


    // Tell the current submenu popup to cancel.  This must be done 
    // before the PostMessage b/c CTrackPopupBar itself posts a message
    // which it must receive before we receive our post.
    TraceMsg(TF_MENUBAND, "(pmb=%#08lx): PostPopup sending MPOS_CANCELLEVEL to submenu popup", this);
    if (_pcmb->_fInSubMenu)
        _pcmb->_SubMenuOnSelect(MPOS_CANCELLEVEL);

    hres = PositionSubmenu(idBtn);

    // Modal?
    if (S_FALSE == hres)
    {
        // Yes; take the capture back
        GetMessageFilter()->RetakeCapture();

        // return S_OK so we stay in the menu mode
        hres = S_OK;
    }
    else if (FAILED(hres))
        _PressBtn(idBtn, FALSE);

    // Since CTrackPopupBar is modal, it should be a useless blob 
    // of bits in memory by now...
    _pcmb->SetTrackMenuPopup(NULL);
   
    return hres;
}


/*----------------------------------------------------------
Purpose: Called to hide a modeless menu.

*/
void CMenuToolbarBase::PopupClose(void)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    if (-1 != _pcmb->_nItemCur)
    {
        _PressBtn(_pcmb->_nItemCur, FALSE);
        NotifyWinEvent(EVENT_OBJECT_FOCUS, _hwndMB, OBJID_CLIENT, 
            GetIndexFromChild(_dwFlags & SMSET_TOP, ToolBar_CommandToIndex(_hwndMB, _pcmb->_nItemCur)));

        _pcmb->_fInSubMenu = FALSE;
        _pcmb->_fInvokedByDrag = FALSE;
        _pcmb->_nItemCur = -1;
    }
}    


LRESULT CMenuToolbarBase::_OnWrapHotItem(NMTBWRAPHOTITEM* pnmwh)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    if (_fProcessingWrapHotItem ||
        !_pcmb->_pmtbTracked ||
        (_pcmb->_pmtbTop == _pcmb->_pmtbBottom && !_fHasDemotedItems))
        return 0;

    _fProcessingWrapHotItem = TRUE;


    // If we want ourselves to not be wrapped into (Like for empty items) 
    // Then forward the wrap message to the other toolbar
    if (_pcmb->_pmtbTracked->_dwFlags & SMSET_TOP && !(_pcmb->_pmtbBottom->_fDontShowEmpty))
    {
        _pcmb->SetTracked(_pcmb->_pmtbBottom);
    }
    else if (!(_pcmb->_pmtbTop->_fDontShowEmpty))
    {
        _pcmb->SetTracked(_pcmb->_pmtbTop);
    }

    int iIndex;

    if (pnmwh->iDir < 0)
    {
        HWND hwnd = _pcmb->_pmtbTracked->_hwndMB;
        iIndex = ToolBar_ButtonCount(hwnd) - 1;
        int idCmd = GetButtonCmd(hwnd, iIndex);

        // We do not want to wrap onto a chevron.
        if (idCmd == _idCmdChevron)
            iIndex -= 1;

    }
    else
    {
        iIndex = 0;
    }

    _pcmb->_pmtbTracked->SetHotItem(pnmwh->iDir, iIndex, -1, pnmwh->nReason);


    _fProcessingWrapHotItem = FALSE;

    return 1;
}


LRESULT CMenuToolbarBase::_OnWrapAccelerator(NMTBWRAPACCELERATOR* pnmwa)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    int iHotItem = -1;
    int iNumTopAccel = 0;
    int iNumBottomAccel = 0;

    if (_pcmb->_fProcessingDup)
        return 0;

    // Check to see if there is only one toolbar.
    if (_pcmb->_pmtbTop == _pcmb->_pmtbBottom)
        return 0;

    ToolBar_HasAccelerator(_pcmb->_pmtbTop->_hwndMB, pnmwa->ch, &iNumTopAccel);
    ToolBar_HasAccelerator(_pcmb->_pmtbBottom->_hwndMB, pnmwa->ch, &iNumBottomAccel);

    _pcmb->_fProcessingDup = TRUE;

    CMenuToolbarBase* pmbtb = NULL;
    if (_pcmb->_pmtbTracked->_dwFlags & SMSET_TOP)
    {
        ToolBar_MapAccelerator(_pcmb->_pmtbBottom->_hwndMB, pnmwa->ch, &iHotItem);
        pmbtb = _pcmb->_pmtbBottom;
    }
    else
    {
        ToolBar_MapAccelerator(_pcmb->_pmtbTop->_hwndMB, pnmwa->ch, &iHotItem);
        pmbtb = _pcmb->_pmtbTop;
    }

    _pcmb->_fProcessingDup = FALSE;

    if (iHotItem != -1)
    {
        _pcmb->SetTracked(pmbtb);
        int idCmd = ToolBar_CommandToIndex(pmbtb->_hwndMB, iHotItem);
        DWORD dwFlags = HICF_ACCELERATOR;

        // If either (but not both) toolbars have the accelerator, and it is exactly one,
        // then cause the drop down.
        if ( (iNumTopAccel >= 1) ^ (iNumBottomAccel >= 1) &&
             (iNumTopAccel == 1 || iNumBottomAccel == 1) )
            dwFlags |= HICF_TOGGLEDROPDOWN;

        SendMessage(pmbtb->_hwndMB, TB_SETHOTITEM2, idCmd, dwFlags);
        pnmwa->iButton = -1;
        return 1;
    }

    return 0;
}


LRESULT CMenuToolbarBase::_OnDupAccelerator(NMTBDUPACCELERATOR* pnmda)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    if (_pcmb->_fProcessingDup || (_pcmb->_pmtbBottom == _pcmb->_pmtbTop))
        return 0;

    _pcmb->_fProcessingDup = TRUE;

    int iNumTopAccel = 0;
    int iNumBottomAccel = 0;
    
    if (_pcmb->_pmtbTop)
        ToolBar_HasAccelerator(_pcmb->_pmtbTop->_hwndMB, pnmda->ch, &iNumTopAccel);

    if (_pcmb->_pmtbBottom)
        ToolBar_HasAccelerator(_pcmb->_pmtbBottom->_hwndMB, pnmda->ch, &iNumBottomAccel);


    _pcmb->_fProcessingDup = FALSE;

    if (0 == iNumTopAccel && 0 == iNumBottomAccel)
    {
        // We want to return 1 if Both of them have one. 
        //Otherwise, return 0, and let the toolbar handle it itself.
        return 0;
    }

    pnmda->fDup = TRUE;

    return 1;
}

/*----------------------------------------------------------
Purpose: Handle WM_NOTIFY

*/
LRESULT CMenuToolbarBase::_OnNotify(LPNMHDR pnm)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    LRESULT lres = 0;
    CMBMsgFilter* pmf = GetMessageFilter();

    // These are notifies we handle even when disengaged from the message hook.
    switch (pnm->code)
    {
    case NM_CUSTOMDRAW:
        {
            BOOL fHandled = FALSE;
            NMCUSTOMDRAW *pnmcd = (NMCUSTOMDRAW*)pnm;
            // first we give the callback a chance to handle it
            if (_pcmb->_dwFlags & SMINIT_CUSTOMDRAW)
            {
                fHandled = (S_OK == v_CallCBItem((int)pnmcd->dwItemSpec, SMC_CUSTOMDRAW, (WPARAM)&lres, (LPARAM)pnmcd));

            }
            if (!fHandled)
            {
                // We now custom draw even the TopLevelMenuBand (for the correct font)
                lres = v_OnCustomDraw(pnmcd);
            }
        }
        break;
    case NM_THEMECHANGED:
        {
            _RefreshTheme();
        }
    }
    
    
    // Is the Global Message filter Disengaged? This will happen when the Subclassed window
    // looses activation to a dialog box of some kind.
    if (lres == 0 && !pmf->IsEngaged())
    {
        // Yes; We've lost activation so we don't want to track like a normal menu...

        // For hot item change, return 1 so that the toolbar does not change the hot item.
        if (pnm->code == TBN_HOTITEMCHANGE && _pcmb->_fMenuMode)
            return 1;

        // For all other items, don't do anything....
        return 0;
    }

    switch (pnm->code)
    {
    case NM_RELEASEDCAPTURE:
        pmf->RetakeCapture();
        break;

    case NM_KEYDOWN:
        BLOCK
        {
            LPNMKEY pnmk = (LPNMKEY)pnm;
            lres = _OnKey(TRUE, pnmk->nVKey, pnmk->uFlags);
        }
        break;

    case NM_CHAR:
        {
            LPNMCHAR pnmc = (LPNMCHAR)pnm;
            if (pnmc->ch == TEXT(' '))
                return TRUE;

            if (pnmc->dwItemNext == -1 &&
                !_pcmb->_fVertical)
            {
                // If it's horizontal, then it must be top level.
                ASSERT(_pcmb->_fTopLevel);
                _pcmb->_CancelMode(MPOS_FULLCANCEL);
            }
        }
        break;

    case TBN_HOTITEMCHANGE:
        lres = _OnHotItemChange((LPNMTBHOTITEM)pnm);
        break;

    case NM_LDOWN:
        // We need to kill the expand timer, because the user might
        // move out of the chevron and accidentally select another item.
        if ( (int)((LPNMCLICK)pnm)->dwItemSpec == _idCmdChevron && _idCmdChevron != -1)
        {
            KillTimer(_hwndMB, MBTIMER_EXPAND);
            _fIgnoreHotItemChange = TRUE;
        }
        break;

    case NM_CLICK:
        {
            int idCmd = (int)((LPNMCLICK)pnm)->dwItemSpec;
            _fIgnoreHotItemChange = FALSE;
            if ( idCmd == -1 )
            {
                _pcmb->_SubMenuOnSelect(MPOS_CANCELLEVEL);
                _pcmb->SetTracked(NULL);
                lres = 1;
            }
            else if ( idCmd == _idCmdChevron )
            {
                // Retake the capture on the button-up, b/c the toolbar took
                // it away for a moment.
                pmf->RetakeCapture();

                v_CallCBItem(_idCmdChevron, SMC_CHEVRONEXPAND, 0, 0);
                Expand(TRUE);
                _fClickHandled = TRUE;
                _SetTimer(MBTIMER_CLICKUNHANDLE);
                lres = 1;
            }
            else if (!_fEmpty)
            {
                TraceMsg(TF_MENUBAND, "(pmb=%#08lx): upclick %d", this, idCmd);

                // Retake the capture on the button-up, b/c the toolbar took
                // it away for a moment.
                pmf->RetakeCapture();

                if (v_GetFlags(idCmd) & SMIF_SUBMENU)     // Submenus support double click
                {
                    if (_iLastClickedTime == 0) // First time it was clicked
                    {
                        _iLastClickedTime = GetTickCount();
                        _idCmdLastClicked = idCmd;
                    }
                    // Did they click on the same item twice?
                    else if (idCmd != _idCmdLastClicked)
                    {
                        _iLastClickedTime = _idCmdLastClicked = 0;
                    }
                    else
                    {
                        // Was this item double clicked on?
                        if ((GetTickCount() - _iLastClickedTime) < GetDoubleClickTime())
                        {
                            // We need to post this back to ourselves, because
                            // the Tray will become in active when double clicking
                            // on something like programs. This happens because the 
                            // Toolbar will set capture back to itself and the tray
                            // doesn't get any more messages.
                            PostMessage(_hwndMB, g_nMBExecute, idCmd, 0);
                            _fClickHandled = TRUE;
                        }

                        _iLastClickedTime = _idCmdLastClicked = 0;
                    }
                }

                // Sent on the button-up.  Handle the same way.
                if (!_fClickHandled && -1 != idCmd)
                    _DropDownOrExec(idCmd, FALSE);

                _fClickHandled = FALSE;
                lres = 1;
            }
        }
        break;

    case TBN_DROPDOWN:
        lres = _OnDropDown((LPNMTOOLBAR)pnm);
        break;

#ifdef UNICODE
    case TBN_GETINFOTIPA:
        {
            LPNMTBGETINFOTIPA pnmTT = (LPNMTBGETINFOTIPA)pnm;
            UINT uiCmd = pnmTT->iItem;
            TCHAR szTip[MAX_PATH];

            if ( S_OK == v_GetInfoTip(pnmTT->iItem, szTip, ARRAYSIZE(szTip)) )
            {
                SHUnicodeToAnsi(szTip, pnmTT->pszText, pnmTT->cchTextMax);
            }
            else
            {
                // Set the lpszText to NULL to prevent the toolbar from setting
                // the button text by default
                pnmTT->pszText = NULL;
            }


            lres = 1;
            break;

        }
#endif
    case TBN_GETINFOTIP:
        {
            LPNMTBGETINFOTIP pnmTT = (LPNMTBGETINFOTIP)pnm;
            UINT uiCmd = pnmTT->iItem;

            if ( S_OK != v_GetInfoTip(pnmTT->iItem, pnmTT->pszText, pnmTT->cchTextMax) )
            {
                // Set the lpszText to NULL to prevent the toolbar from setting
                // the button text by default
                pnmTT->pszText = NULL;
            }
            lres = 1;
            break;
        }

    case NM_RCLICK:
        // When we go into a context menu, stop monitoring.
        KillTimer(_hwndMB, MBTIMER_EXPAND);
        KillTimer(_hwndMB, MBTIMER_UEMTIMEOUT);
        break;

    case TBN_WRAPHOTITEM:
        lres = _OnWrapHotItem((NMTBWRAPHOTITEM*)pnm);
        break;

    case TBN_WRAPACCELERATOR:
        lres = _OnWrapAccelerator((NMTBWRAPACCELERATOR*)pnm);
        break;

    case TBN_DUPACCELERATOR:
        lres = _OnDupAccelerator((NMTBDUPACCELERATOR*)pnm);
        break;

    case TBN_DRAGOVER:
        // This message is sent when drag and drop within the toolbar indicates that it
        // is about to mark a button. Since this gets messed up because of LockWindowUpdate
        // we tell it not to do anything.
        lres = 1;
        break;
    }

    return(lres);
}


BOOL CMenuToolbarBase::_SetTimer(int nTimer)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    long lTimeOut;

    // If we're on NT5 or Win98, use the cool new SPI
    if (SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &g_lMenuPopupTimeout, 0))
    {
        // Woo-hoo, all done.
    }
    else if (g_lMenuPopupTimeout == -1)
    {
        // NT4 or Win95.  Grovel the registry (yuck).
        DWORD dwType;
        TCHAR szDelay[6]; // int is 5 characters + null.
        DWORD cbSize = ARRAYSIZE(szDelay);

        g_lMenuPopupTimeout = MBTIMER_TIMEOUT;

        if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"),
            TEXT("MenuShowDelay"), &dwType, (void*)szDelay, &cbSize))
        {
            g_lMenuPopupTimeout = (UINT)StrToInt(szDelay);
        }
    }

    lTimeOut = g_lMenuPopupTimeout;

    switch (nTimer)
    {
    case MBTIMER_EXPAND:
    case MBTIMER_DRAGPOPDOWN:
        lTimeOut *= 2;
        if (lTimeOut < MAXUEMTIMEOUT)
            lTimeOut = MAXUEMTIMEOUT;
        break;

    case MBTIMER_UEMTIMEOUT:
            if (!_fHasDemotedItems || _pcmb->_pmbState->GetExpand() || _fEditMode)
                return TRUE;
            lTimeOut *= 5;

            // We want a minimum of MAXUEMTIMEOUT for people who set the expand rate to zero
            if (lTimeOut < MAXUEMTIMEOUT)
                lTimeOut = MAXUEMTIMEOUT;
            TraceMsg(TF_MENUBAND, "*** UEM SetTimeOut to (%d) milliseconds" 
                "at Tick Count (%d).*** ", GetTickCount());
            break;

    case MBTIMER_CHEVRONTIP:
        lTimeOut = 60 * 1000;    // Please make the intellimenu's balloon tip go 
                                 // away after one minute of no action.
        break;

    case MBTIMER_INFOTIP:
        lTimeOut = 500;    // Half a second hovering over an item?
        break;
    }

    TraceMsg(TF_MENUBAND, "(pmb=%#08lx): Setting %d Timer to %d milliseconds at tickcount %d", 
        this, nTimer, lTimeOut, GetTickCount());
    return (BOOL)SetTimer(_hwndMB, nTimer, lTimeOut, NULL);
}

BOOL CMenuToolbarBase::_HandleObscuredItem(int idCmd)
{
    RECT rc;
    GetClientRect(_hwndMB, &rc);

    int iButton = (int)SendMessage(_hwndMB, TB_COMMANDTOINDEX, idCmd, 0);

    if (SHIsButtonObscured(_hwndMB, &rc, iButton)) 
    {
        // clear hot item
        ToolBar_SetHotItem(_hwndMB, -1);

        _pcmb->_SubMenuOnSelect(MPOS_FULLCANCEL);
        _pcmb->_CancelMode(MPOS_FULLCANCEL);        // This is for the track menus.

        HWND hwnd = _pcmb->_pmbState->GetSubclassedHWND();

        PostMessage(hwnd? hwnd: _hwndMB, g_nMBOpenChevronMenu, (WPARAM)idCmd, 0);

        return TRUE;
    }

    return FALSE;
}


LRESULT CMenuToolbarBase::_OnHotItemChange(NMTBHOTITEM * pnmhot)
{
	ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    LRESULT lres = 0;

    if (_pcmb->_fMenuMode && _pcmb->_fShow && !_fIgnoreHotItemChange)
    {
        // Always kill the expand timer when something changes
        KillTimer(_hwndMB, MBTIMER_EXPAND);
        KillTimer(_hwndMB, MBTIMER_INFOTIP);

        // Is this toolbar being entered?
        if (!(pnmhot->dwFlags & HICF_LEAVING))
        {
            // Yes; set it to be the currently tracking toolbar
            TraceMsg(TF_MENUBAND, "CMTB::OnHotItemChange. Setting Tracked....", this);
            _pcmb->SetTracked(this);

            _pcmb->_pmbState->HideTooltip(FALSE);
            _SetTimer(MBTIMER_INFOTIP);
        }

        // If the Toolbar has keybaord focus, we need to send OBJID_CLIENT so that we track correctly.
        if (!(pnmhot->dwFlags & HICF_LEAVING))
        {
            NotifyWinEvent(EVENT_OBJECT_FOCUS, _hwndMB, OBJID_CLIENT, 
                GetIndexFromChild(_dwFlags & SMSET_TOP, ToolBar_CommandToIndex(_hwndMB, pnmhot->idNew)));
        }

        DEBUG_CODE( TraceMsg(TF_MENUBAND, "(pmb=%#08lx): TBN_HOTITEMCHANGE (state:%#02lx, %d-->%d)", 
                             this, pnmhot->dwFlags, 
                             (pnmhot->dwFlags & HICF_ENTERING) ? -1 : pnmhot->idOld, 
                             (pnmhot->dwFlags & HICF_LEAVING) ? -1 : pnmhot->idNew); )

        // While in edit mode, we do not automatically cascade 
        // submenus, unless while dropping.  But the dropping case
        // is handled in HitTest, not here.  So don't deal with that
        // here.

        // Is this because an accelerator key was hit?
        if (pnmhot->dwFlags & HICF_ACCELERATOR)
        {
            KillPopupTimer();
            KillTimer(_hwndMB, MBTIMER_UEMTIMEOUT);
            // Yes; now that TBSTYLE_DROPDOWN is used, let _DropDownOrExec handle it
            // in response to TBN_DROPDOWN.
        }
        // Is this because direction keys were hit?
        else if (pnmhot->dwFlags & HICF_ARROWKEYS)
        {
            // Yes
            KillPopupTimer();
            KillTimer(_hwndMB, MBTIMER_UEMTIMEOUT);

            if (!_fVerticalMB && 
                _HandleObscuredItem(pnmhot->idNew))
            {
                lres = 1;
            }
            else
            {
                // It doesn't make sense that we would get these keyboard
                // notifications if there is a submenu open...it should get
                // the messages
                ASSERT(!_pcmb->_fInSubMenu);
                v_SendMenuNotification(pnmhot->idNew, FALSE);

                // Since the only way that the chevron can get the highlight is
                // through a keyboard down, then we expand.
                if (_fHasDemotedItems && pnmhot->idNew == (int)_idCmdChevron)
                {
                    v_CallCBItem(_idCmdChevron, SMC_CHEVRONEXPAND, 0, 0);
                    Expand(TRUE);
                    lres = 1;       // We already handled the hot item change
                }
            }

            _pcmb->_pmbState->HideTooltip(FALSE);
            _SetTimer(MBTIMER_INFOTIP);
        }
        // Is this because the mouse moved or an explicit sendmessage?
        else if (!(pnmhot->dwFlags & HICF_LEAVING) && 
                 (pnmhot->idNew != _pcmb->_nItemCur || // Ignore if we're moving over same item
                  (_nItemTimer != -1 && _pcmb->_nItemCur == pnmhot->idNew)))     // we need to go through here to reset if the user went back to the cascaded guy
        {
            // Yes
            if (!_fVerticalMB)    // Horizontal menus will always have an underlying hmenu
            {
                if (_HandleObscuredItem(pnmhot->idNew))
                {
                    lres = 1;
                }
                else if (_pcmb->_fInSubMenu)
                {
                    // Only popup a menu since we're already in one (as mouse
                    // moves across bar).

                    TraceMsg(TF_MENUBAND, "(pmb=%#08lx): TBN_HOTITEMCHG: Posting CMBPopup message", this);
                    PostPopup(pnmhot->idNew, FALSE, _pcmb->_fKeyboardSelected);  // Will handle menu notification on receipt of message
                }
                else
                    v_SendMenuNotification(pnmhot->idNew, FALSE);
            }
            else if (!_fEditMode)
            {
                v_SendMenuNotification(pnmhot->idNew, FALSE);

                // check to see if we have just entered a new item and it is a sub-menu...

                // Did we already set a timer?
                if (-1 != _nItemTimer)
                {
                    // Yes; kill it b/c the mouse moved to another item
                    KillPopupTimer();
                }

                // if we're not over the currently expanded guy
                // Have we moved over an item that expands OR
                // are we moving away from a cascaded item?
                DWORD dwFlags = v_GetFlags(pnmhot->idNew);
                // Reset the user timer
                KillTimer(_hwndMB, MBTIMER_UEMTIMEOUT);

                // UEMStuff
                if (!(dwFlags & SMIF_SUBMENU))
                {
                    _SetTimer(MBTIMER_UEMTIMEOUT);
                    _FireEvent(UEM_HOT_ITEM);
                }

                if ( (pnmhot->dwFlags & HICF_MOUSE) && _pcmb->_nItemCur != pnmhot->idNew) 
                {
                    if (dwFlags & SMIF_SUBMENU || _pcmb->_fInSubMenu)
                    {
                        // Is this the only item in the menu?
                        if ( _cPromotedItems == 1 && 
                            !(_fHasDemotedItems && _pcmb->_fExpanded) && 
                            dwFlags & SMIF_SUBMENU)
                        {
                            // Yes; Then we want to pop it open immediatly, 
                            // instead of waiting for the timeout
                            PostPopup(pnmhot->idNew, FALSE, FALSE);
                        }
                        else if (_SetTimer(MBTIMER_POPOUT))
                        {
                            // No; fire a timer to open/close the submenu
                            TraceMsg(TF_MENUBAND, "(pmb=%#08lx): TBN_HOTITEMCHG: Starting timer for id=%d", this, pnmhot->idNew);
                            if (v_GetFlags(pnmhot->idNew) & SMIF_SUBMENU)
                                _nItemTimer = pnmhot->idNew;
                            else
                                _nItemTimer = -1;
                        }
                    }
                    
                    if (_fHasDemotedItems && pnmhot->idNew == (int)_idCmdChevron)
                    {
                        _SetTimer(MBTIMER_EXPAND);
                    }

                    _pcmb->_pmbState->HideTooltip(FALSE);
                    _SetTimer(MBTIMER_INFOTIP);
                }

            }
        }
        else if (pnmhot->dwFlags & HICF_LEAVING)
        {
            v_SendMenuNotification(pnmhot->idOld, TRUE);

            if (-1 != _nItemTimer && !_fEditMode)
            {
                // kill the cascading menu popup timer...
                TraceMsg(TF_MENUBAND, "(pmb=%#08lx): TBN_HOTITEMCHG: Killing timer", this);
            
                KillPopupTimer();
            }
            _pcmb->_pmbState->HideTooltip(FALSE);
        }

        if ( !(pnmhot->dwFlags & HICF_LEAVING) )
            _pcmb->_SiteOnSelect(MPOS_CHILDTRACKING);
    }

    return lres;
}    

LRESULT CMenuToolbarBase::_DropDownOrExec(UINT idCmd, BOOL bKeyboard)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    TraceMsg(TF_MENUBAND, "(pmb=%#08lx): _DropDownOrExec %d", this, idCmd);

    // Don't do anything when we're in edit mode
    if (_fEditMode)
        return 0;

    if ( v_GetFlags(idCmd) & SMIF_SUBMENU )
    {
        v_SendMenuNotification(idCmd, FALSE);
        
        PostPopup(idCmd, FALSE, bKeyboard);
    }
    else if (idCmd != -1)
    {
        RECT rc;
        AddRef();   // I might get released in the call.

        // Fading Selection
        SHPlaySound(TEXT("MenuCommand"));
        SendMessage(_hwndMB, TB_GETRECT, idCmd, (LPARAM)&rc);
        MapWindowPoints(_hwndMB, HWND_DESKTOP, (POINT*)&rc, 2);

        if (!(GetKeyState(VK_SHIFT) < 0))
        {
            // Initiate the fade (if enabled) before blowing away the menus
            _pcmb->_pmbState->FadeRect(&rc);
            _pcmb->_SiteOnSelect(MPOS_EXECUTE);
        }

        if (g_dwProfileCAP & 0x00002000) 
            StartCAP();
        v_ExecItem(idCmd);
        if (g_dwProfileCAP & 0x00002000) 
            StopCAP();

        Release();
    }
    else
        MessageBeep(MB_OK);

    return 0;
}


/*----------------------------------------------------------
Purpose: Handles TBN_DROPDOWN, which is sent on the button-down.

*/
LRESULT CMenuToolbarBase::_OnDropDown(LPNMTOOLBAR pnmtb)
{
    DWORD dwInput = _fTopLevel ? 0 : -1;    // -1: don't track, 0: do
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    LRESULT lres = 0;

    // Expected behavior with the mouse:
    //
    // 1) For cascading menuitems-
    //    a) expand on button-down
    //    b) collapse on button-up (horizontal menu only)
    //    c) if the button-down occurs on the item that is
    //       already selected, then assume the click indicates
    //       a drag/drop scenario
    // 2) For other menuitems-
    //    a) execute on button-up

#ifdef DEBUG
    if (_fTopLevel)
    {
        // browser menu comes thru here; start menu goes elsewhere (via tray.c)
        //ASSERT(!_fVertical);
        TraceMsg(DM_MISC, "cmtbb._odd: _fTopLevel(1) mouse=%d", GetKeyState(VK_LBUTTON) < 0);
    }
#endif
    // Is this because the mouse button was used?
    if (GetKeyState(VK_LBUTTON) < 0)
    {
        // Yes

        // Assume it won't be handled.  This will allow the toolbar
        // to see the button-down as a potential drag and drop.
        lres = TBDDRET_TREATPRESSED;

        // Clicking on same item that is currently expanded?
        if (pnmtb->iItem == _pcmb->_nItemCur)
        {

            // Is this horizontal?
            if (!_fVerticalMB)
            {
                // Yes; toggle the dropdown
                _pcmb->_SubMenuOnSelect(MPOS_FULLCANCEL);
                
                // Say it is handled, so the button will toggle
                lres = TBDDRET_DEFAULT;
            }
            
            _fClickHandled = TRUE;
            
            // Otherwise don't do anything more, user might be starting a 
            // drag-drop procedure on the cascading menuitem
        }
        else
        {
            if (v_GetFlags(pnmtb->iItem) & SMIF_SUBMENU)
            {
                // Handle on the button-down
                _fClickHandled = TRUE;
                lres = _DropDownOrExec(pnmtb->iItem, FALSE);
            }
        }

        if (dwInput != -1)
            dwInput = UIBL_INPMOUSE;
    }
    else
    {
        // No; must be the keyboard
        _fClickHandled = TRUE;
        lres = _DropDownOrExec(pnmtb->iItem, TRUE);

        if (dwInput != -1)
            dwInput = UIBL_INPMENU;
    }

    // browser menu (*not* start menu) alt+key, mouse
    if (dwInput != -1)
        UEMFireEvent(&UEMIID_SHELL, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_UIINPUT, dwInput);

    return lres;
}    


/*----------------------------------------------------------
Purpose: Handle WM_KEYDOWN/WM_KEYUP

Returns: TRUE if handled
*/
BOOL CMenuToolbarBase::_OnKey(BOOL bDown, UINT vk, UINT uFlags)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first

    int idCmd;
    HWND hwnd = _hwndMB;

    _pcmb->_pmbState->SetKeyboardCue(TRUE);

    //
    // If the menu window is RTL mirrored, then the arrow keys should
    // be mirrored to reflect proper cursor movement. [samera]
    //
    if (IS_WINDOW_RTL_MIRRORED(hwnd))
    {
        switch (vk)
        {
        case VK_LEFT:
          vk = VK_RIGHT;
          break;

        case VK_RIGHT:
          vk = VK_LEFT;
          break;
        }
    }

    switch (vk)
    {
    case VK_LEFT:
        if (_fVerticalMB)
        {
            _pcmb->_SiteOnSelect(MPOS_SELECTLEFT);
            return TRUE;
        }
        break;

    case VK_RIGHT:
        if (_fVerticalMB)
            goto Cascade;
        break;

    case VK_DOWN:
    case VK_UP:
        if (!_fVerticalMB)
        {
Cascade:
            idCmd = GetButtonCmd(hwnd, ToolBar_GetHotItem(hwnd));
            if (v_GetFlags(idCmd) & SMIF_SUBMENU)
            {
                // Enter the submenu
                TraceMsg(TF_MENUBAND, "(pmb=%#08lx): _OnKey: Posting CMBPopup message", this);
                
                PostPopup(idCmd, FALSE, TRUE);
            }
            else if (VK_RIGHT == vk)
            {
                // Nothing to cascade to, move to next sibling menu
                _pcmb->_SiteOnSelect(MPOS_SELECTRIGHT);
            }
            return TRUE;
        }
        else
        {
#if 0
            _pcmb->_OnSelectArrow(vk == VK_UP? -1 : 1);
            return TRUE;
#endif
        }
        break;

    case VK_SPACE:

        if (!_pcmb->_fExpanded && _fHasDemotedItems)
        {
            v_CallCBItem(_idCmdChevron, SMC_CHEVRONEXPAND, 0, 0);
            Expand(TRUE);
        }
        else
        {
            // Toolbars map the spacebar to VK_RETURN.  Menus don't except
            // in the horizontal menubar.
            if (_fVerticalMB)
                MessageBeep(MB_OK);
        }
        return TRUE;

#if 0
    case VK_RETURN:
        // Handle this now, rather than letting the toolbar handle it.
        // This way we don't have to rely on WM_COMMAND, which doesn't
        // convey whether it was invoked by the keyboard or the mouse.
        idCmd = GetButtonCmd(hwnd, ToolBar_GetHotItem(hwnd));
        _DropDownOrExec(idCmd, TRUE);
        return TRUE;
#endif
    }

    return FALSE;
}    

/*----------------------------------------------------------
Purpose: There are two flavors of this function: _DoPopup and
         PostPopup.  Both cancel the existing submenu (relative 
         to this band) and pops open a new submenu.  _DoPopup
         does it atomically.  PostPopup posts a message to
         handle it.

*/
void CMenuToolbarBase::_DoPopup(int idCmd, BOOL bInitialSelect)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    if (-1 != idCmd)
    {
        PopupHelper(idCmd, bInitialSelect);
    }
}    


/*----------------------------------------------------------
Purpose: See the _DoPopup comment
*/
void CMenuToolbarBase::PostPopup(int idCmd, BOOL bSetItem, BOOL bInitialSelect)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    if (-1 != idCmd)
    {
        _pcmb->_SubMenuOnSelect(MPOS_CANCELLEVEL);
        _pcmb->SetTracked(this);
        HWND hwnd = _pcmb->_pmbState->GetSubclassedHWND();

        PostMessage(hwnd? hwnd: _hwndMB, g_nMBPopupOpen, idCmd, MAKELPARAM(bSetItem, bInitialSelect));
    }
}    


/*----------------------------------------------------------
Purpose: Helper function to finally invoke submenu.  Use _DoPopup
         or PostPopup
*/
void CMenuToolbarBase::PopupHelper(int idCmd, BOOL bInitialSelect)
{
    // We do not want to pop open a sub menu if we are not displayed. This is especially
    // a problem during drag and drop.
    if (_fShowMB)
    {
        _pcmb->_nItemNew = idCmd;
        ASSERT(-1 != _pcmb->_nItemNew);
        _pcmb->SetTracked(this);
        _pcmb->_fPopupNewMenu = TRUE;
        _pcmb->_fInitialSelect = BOOLIFY(bInitialSelect);
        _pcmb->UIActivateIO(TRUE, NULL);
        _FireEvent(UEM_HOT_FOLDER);
        _SetTimer(MBTIMER_UEMTIMEOUT);
    }
}


// Paints the 3d rect around a button.
void    CMenuToolbarBase::_PaintButton3D(HDC hdc, int idCmd, LPRECT prc, DWORD dwSMIF)
{

    // Only needed when the menu is expanded.
    // We don't need no stink'n 3D stuff in Flat mode.
    if (!_pcmb->_fExpanded || _pcmb->_pmbm->_fFlatMenuMode)
        return;

    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    RECT rcClient;
    GetClientRect(_hwndMB, &rcClient);
#ifndef DRAWEDGE
    // Draw Left Edge
    HPEN hPenOld = (HPEN)SelectObject(hdc, _pcmb->_pmbm->_hPenHighlight);
    MoveToEx(hdc, prc->left, prc->top, NULL);
    LineTo(hdc, prc->left, prc->bottom);
#endif

    if (!(dwSMIF & SMIF_DEMOTED))
    {
#ifdef DRAWEDGE
        DWORD dwEdge = BF_RIGHT;

        // Don't paint the edge next to the bitmap.
        if (_uIconSizeMB == ISFBVIEWMODE_SMALLICONS)
            dwEdge |= BF_LEFT;


        RECT rc = *prc;
#else
        // Draw Right Edge:
        SelectObject(hdc, _pcmb->_pmbm->_hPenShadow);
        MoveToEx(hdc, prc->right-1, prc->top, NULL);
        LineTo(hdc, prc->right-1, prc->bottom);
#endif

        HWND hwnd = _hwndMB;
        int iPos = ToolBar_CommandToIndex(hwnd, idCmd);
        if (iPos == -1)
        {
            iPos = ToolBar_ButtonCount(hwnd) - 1;
        }

        if (iPos >= 0)
        {
            int iNumButtons = ToolBar_ButtonCount(hwnd);
            int idCmd2 = GetButtonCmd(hwnd, iPos + 1);
            CMenuToolbarBase* pmtb = this;
            BOOL    fOverflowed = FALSE;

            // Situations for Drawing the Bottom line
            // 1) This button is at the bottom.
            // 2) This button is at the bottom and the toolbar
            //      below is not visible (_fDontShowEmpty).
            // 3) This button is at the bottom and the button
            //      at the top of the bottom toolbar is demoted.
            // 4) The button below this one in the toolbar is
            //      demoted.
            // 5) The botton below this one is demoted and we're
            //      not expanded
    
            if (iPos + 1 >= iNumButtons)
            {
                if (_pcmb->_pmtbBottom != this &&
                    !_pcmb->_pmtbBottom->_fDontShowEmpty)
                {
                    pmtb = _pcmb->_pmtbBottom;
                    hwnd = pmtb->_hwndMB;
                    idCmd2 = GetButtonCmd(hwnd, 0);
                }
                else
                    fOverflowed = TRUE;
            }
            else if (prc->bottom == rcClient.bottom &&
                _pcmb->_pmtbBottom == this)   // This button is at the top.
                fOverflowed = TRUE;


            DWORD dwFlags = pmtb->v_GetFlags(idCmd2);

            if ((_pcmb->_fExpanded && dwFlags & SMIF_DEMOTED) || 
                 fOverflowed)
            {
#ifdef DRAWEDGE
                dwEdge |= BF_BOTTOM;
#else
                int iLeft = prc->left;
                if (iPos != iNumButtons - 1)   
                    iLeft ++;   // Move the next line in.

                MoveToEx(hdc, iLeft, prc->bottom-1, NULL);
                LineTo(hdc, prc->right-1, prc->bottom-1);
#endif
            }

            // Situations for Drawing the Top line
            // 1) This button is at the top.
            // 2) This button is at the top and the toolbar
            //      above is not visible (_fDontShowEmpty).
            // 3) This button is at the top and the button
            //      at the bottom of the top toolbar is demoted.
            // 4) The button above this one in the toolbar is
            //      demoted.
            // 5) If the button above this is demoted, and we're
            //      not expanded

            fOverflowed = FALSE; 

            if (iPos - 1 < 0)
            {
                if (_pcmb->_pmtbTop != this && 
                    !_pcmb->_pmtbTop->_fDontShowEmpty)
                {
                    pmtb = _pcmb->_pmtbTop;
                    hwnd = pmtb->_hwndMB;
                    idCmd2 = GetButtonCmd(hwnd, ToolBar_ButtonCount(hwnd) - 1);
                }
                else
                    fOverflowed = TRUE; // There is nothing at the top of this menu, draw the line.
            }
            else
            {
                hwnd = _hwndMB;
                idCmd2 = GetButtonCmd(hwnd, iPos - 1);
                pmtb = this;

                if (prc->top == rcClient.top &&
                    _pcmb->_pmtbTop == this)   // This button is at the top.
                    fOverflowed = TRUE;
            }

            dwFlags = pmtb->v_GetFlags(idCmd2);

            if ((_pcmb->_fExpanded && dwFlags & SMIF_DEMOTED) ||
                fOverflowed)
            {
#ifdef DRAWEDGE
                dwEdge |= BF_TOP;
#else
                SelectObject(hdc, _pcmb->_pmbm->_hPenHighlight);
                MoveToEx(hdc, prc->left, prc->top, NULL);
                LineTo(hdc, prc->right-1, prc->top);
#endif
            }
        }

#ifdef DRAWEDGE
        DrawEdge(hdc, &rc, BDR_RAISEDINNER, dwEdge);
#endif
    }

#ifndef DRAWEDGE
    SelectObject(hdc, hPenOld);
#endif
}

int GetTBImageListWidth(HWND hwnd)
{
    int cx = 0;
    int cy;
    HIMAGELIST himl = (HIMAGELIST)SendMessage(hwnd, TB_GETIMAGELIST, 0, 0);
    if (himl)
    {
        
        // Start with the width of the button
        ImageList_GetIconSize(himl, &cx, &cy);
    }

    return cx;
}

LRESULT CMenuToolbarBase::v_OnCustomDraw(NMCUSTOMDRAW * pnmcd)
{
    // Make it look like a menu
    NMTBCUSTOMDRAW * ptbcd = (NMTBCUSTOMDRAW *)pnmcd;
    DWORD dwRet = 0;
    DWORD dwSMIF = v_GetFlags((UINT)pnmcd->dwItemSpec);


    // Edit mode never hot tracks, and the selected item being
    // moved has a black frame around it.  Items that cascade are 
    // still highlighted normally, even in edit mode.

    switch(pnmcd->dwDrawStage)
    {

    case CDDS_PREPAINT:
        dwRet = CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT;
        break;

    case CDDS_ITEMPREPAINT:
        if (_fVerticalMB)
        {
            if (pnmcd->dwItemSpec == -1)
            {
                if (_hTheme)
                {
                    DrawThemeBackground(_hTheme, pnmcd->hdc, MDP_SEPERATOR, 0, &pnmcd->rc, 0);
                }
                else
                {
                    // a -1 is sent with a seperator
                    RECT rc = pnmcd->rc;
                    rc.top += 3;    // Hard coded in toolbar.
                    rc.right -= GetSystemMetrics(SM_CXEDGE);
                    rc.left += GetSystemMetrics(SM_CXEDGE);

                    if (_pcmb->_pmbm->_fFlatMenuMode)
                    {
                        // Sep is a 1 pixel line
                        rc.bottom = rc.top + 1;
                        SHFillRectClr(pnmcd->hdc, &rc, GetSysColor(COLOR_BTNSHADOW));
                    }
                    else
                        DrawEdge(pnmcd->hdc, &rc, EDGE_ETCHED, BF_TOP);

                    _PaintButton3D(pnmcd->hdc, -1, &pnmcd->rc, dwSMIF);
                }

                dwRet = CDRF_SKIPDEFAULT;
            }
            else
            {
                ptbcd->clrText = _pcmb->_pmbm->_clrMenuText;

                // This is for Darwin Ads.
                if (dwSMIF & SMIF_ALTSTATE)
                {
                    ptbcd->clrText = GetSysColor(COLOR_BTNSHADOW);
                }

                ptbcd->rcText.right = ptbcd->rcText.right - _pcmb->_pmbm->_cxMargin + ICONFUDGE;
                ptbcd->clrBtnFace = (_pcmb->_pmbm->_fFlatMenuMode && !GetSystemMetrics(SM_REMOTESESSION)) ? CLR_NONE : _pcmb->_pmbm->_clrBackground;

                if (_hTheme)
                {
                    if (dwSMIF & SMIF_SUBMENU)
                        ptbcd->rcText.right -= _pcmb->_pmbm->_cxArrow;
                }
                else
                {
                    if (_fHasSubMenu)
                        ptbcd->rcText.right -= _pcmb->_pmbm->_cxArrow;
                }

                // Draw the chevron.
                if ( _fHasDemotedItems && _idCmdChevron == (int)pnmcd->dwItemSpec && 
                    !_pcmb->_pmbm->_fFlatMenuMode)  // In flat mode, we only draw the chevron glyph, which is below.
                {
                    _DrawChevron(pnmcd->hdc, &pnmcd->rc, 
                        (BOOL)(pnmcd->uItemState & CDIS_HOT) ||
                         (BOOL)(pnmcd->uItemState & CDIS_MARKED), 
                        (BOOL)(pnmcd->uItemState & CDIS_SELECTED));

                    dwRet |= CDRF_SKIPDEFAULT;
                }
                else
                {
                    if (!_pszTheme)
                    {
                        // Yes; draw with highlight
                        if (pnmcd->uItemState & (CDIS_CHECKED | CDIS_SELECTED | CDIS_HOT))
                        {
                            if (_pcmb->_pmbm->_fFlatMenuMode)
                            {
                                ptbcd->clrHighlightHotTrack = GetSysColor(COLOR_MENUHILIGHT);
                                ptbcd->clrBtnFace = GetSysColor(COLOR_MENUHILIGHT);
                            }
                            else
                            {
                                ptbcd->clrHighlightHotTrack = GetSysColor(COLOR_HIGHLIGHT);
                                ptbcd->clrBtnFace = GetSysColor(COLOR_HIGHLIGHT);
                            }
                            ptbcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
                            dwRet |= TBCDRF_HILITEHOTTRACK;
                        }
                    }


                    // Is this menu empty?
                    if (_fEmpty)
                    {
                        // Yes, draw the empty string as disabled.
                        if (!_hTheme)
                        {
                            pnmcd->uItemState |= CDIS_DISABLED;
                        }
                        ptbcd->clrText = ptbcd->clrBtnFace;

                        // Don't draw the etched effect if it is selected
                        if (pnmcd->uItemState & CDIS_HOT)
                            dwRet |= TBCDRF_NOETCHEDEFFECT;
                    }

                    // When this item is demoted, we only want to paint his background
                    // then we are in edit mode _OR_ it is not selected, checked or hot.
                    if (dwSMIF & SMIF_DEMOTED)
                    {
                        BOOL fDrawDemoted = TRUE;
                        if (_fEditMode)
                            fDrawDemoted = TRUE;

                        if (pnmcd->uItemState & (CDIS_CHECKED | CDIS_SELECTED | CDIS_HOT))
                            fDrawDemoted = FALSE;

                        if (fDrawDemoted)
                        {
                            RECT rc = pnmcd->rc;
                            // In flat mode we only paint the icon background in a demoted state. This matches office
                            COLORREF crDemoted = _pcmb->_pmbm->_clrDemoted;
                            if (_pszTheme)
                            {
                                crDemoted = RGB(GetRValue(COLOR_MENU) / 2, GetGValue(COLOR_MENU) / 2, GetBValue(COLOR_MENU) / 2);
                            }

                            if (_pcmb->_pmbm->_fFlatMenuMode)
                            {
                                rc.right = rc.left + GetTBImageListWidth(_hwndMB) + ICONBACKGROUNDFUDGE;
                                SHFillRectClr(pnmcd->hdc, &rc, crDemoted);
                            }
                            else
                            {
                                ptbcd->clrBtnFace = CLR_NONE;
                                SHFillRectClr(pnmcd->hdc, &rc, crDemoted);
                            }
                        }
                    }

                    // "New" items get the tooltip look
                    if (dwSMIF & SMIF_NEW && !(pnmcd->uItemState & (CDIS_CHECKED | CDIS_SELECTED | CDIS_HOT)))
                    {
                        if (_hTheme)
                        {
                            DrawThemeBackground(_hTheme, pnmcd->hdc, MDP_NEWAPPBUTTON, 0, &pnmcd->rc, 0);
                            dwRet |= TBCDRF_NOBACKGROUND;
                        }
                        else
                        {
                            ptbcd->clrBtnFace = CLR_NONE;
                            SHFillRectClr(pnmcd->hdc, &pnmcd->rc, GetSysColor(COLOR_INFOBK));
                            ptbcd->clrText = GetSysColor(COLOR_INFOTEXT);
                        }
                    }

                    // We draw our own highlighting
                    if (!_pszTheme)
                    {
                        dwRet |= (TBCDRF_NOEDGES | TBCDRF_NOOFFSET);
                    }
                }
            }
        }
        else
        {
            // If g_fRunOnMemphis or g_fRunOnNT5 are not defined then the menus will
            // never be grey.
            if (!_pcmb->_fAppActive)
                // menus from user use Button Shadow for non active menus
                ptbcd->clrText = GetSysColor(COLOR_3DSHADOW);
            else
                ptbcd->clrText = _pcmb->_pmbm->_clrMenuText;

            // If we're in high contrast mode make the menu bar look like
            // veritcal items on select or in flat mode use the new "selection" ui.
            if (_pcmb->_pmbm->_fHighContrastMode || _pcmb->_pmbm->_fFlatMenuMode)
            {
                // Yes; draw with highlight
                if (pnmcd->uItemState & (CDIS_CHECKED | CDIS_SELECTED | CDIS_HOT))
                {
                    ptbcd->clrHighlightHotTrack = GetSysColor(COLOR_HIGHLIGHT);
                    ptbcd->clrBtnFace = GetSysColor(COLOR_HIGHLIGHT);
                    ptbcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);

                    if (!_pszTheme)
                    {
                        dwRet |= (TBCDRF_NOEDGES | TBCDRF_NOOFFSET | TBCDRF_HILITEHOTTRACK);
                    }
                }
            }
        }
        dwRet |= CDRF_NOTIFYPOSTPAINT | TBCDRF_NOMARK;
        break;
    case CDDS_ITEMPOSTPAINT:
        if (_fVerticalMB)
        {
            RECT rc = pnmcd->rc;
            COLORREF rgbText =  _pcmb->_pmbm->_clrMenuText;
            if (_pcmb->_pmbm->_fFlatMenuMode)
            {
                // Flat menu mode has a rect around the selection.
                if (pnmcd->uItemState & (CDIS_SELECTED | CDIS_HOT) && !_pszTheme)
                {
                    SHOutlineRect(pnmcd->hdc, &pnmcd->rc, GetSysColor(COLOR_HIGHLIGHT));
                }

                // Draw the chevron Glyph
                if ( _fHasDemotedItems && _idCmdChevron == (int)pnmcd->dwItemSpec)
                {
                    _DrawChevron(pnmcd->hdc, &pnmcd->rc, 
                        (BOOL)(pnmcd->uItemState & CDIS_HOT) ||
                         (BOOL)(pnmcd->uItemState & CDIS_MARKED), 
                        (BOOL)(pnmcd->uItemState & CDIS_SELECTED));
                }
            }
            else if (pnmcd->uItemState & (CDIS_SELECTED | CDIS_HOT))
            {
                rgbText = GetSysColor( COLOR_HIGHLIGHTTEXT );
            }

            // Is this item Checked?
            if (dwSMIF & SMIF_CHECKED)
            {
                rc.right = rc.left + (rc.bottom - rc.top);
                _DrawMenuGlyph(pnmcd->hdc, _pcmb->_pmbm->_hFontArrow
                    , &rc, CH_MENUCHECKA, rgbText, NULL);
                rc = pnmcd->rc;
            }

            // Is this a cascading item?
            if (dwSMIF & SMIF_SUBMENU)
            {
                // Yes; draw the arrow
                COLORREF crArrow = rgbText;

                if (_hTheme)
                {
                    int iState = MDS_NORMAL;
                    if (pnmcd->uItemState & CDIS_DISABLED)
                        iState = MDS_DISABLED;
                    else if ((pnmcd->uItemState & CDIS_HOT) && (pnmcd->uItemState & CDIS_CHECKED))
                        iState = MDS_HOTCHECKED;
                    else if (pnmcd->uItemState & CDIS_HOT)
                        iState = MDS_HOT;
                    else if (pnmcd->uItemState & CDIS_CHECKED)
                        iState = MDS_CHECKED;

                    GetThemeColor(_hTheme, 0, iState, TMT_TEXTCOLOR, &crArrow);
                }

                RECT rcT = rc;
   
                RECT rcClient;
                GetClientRect(_hwndMB, &rcClient);
                if (rcClient.right < rcT.right)
                {
                    rcT.right = rcClient.right;
                }
                rcT.left = rcT.right - _pcmb->_pmbm->_cxArrow;
                _DrawMenuArrowGlyph(pnmcd->hdc, &rcT, crArrow);
            }

            if (!_pszTheme)
            {
                _PaintButton3D(pnmcd->hdc, (UINT)pnmcd->dwItemSpec, &rc, dwSMIF);
            }
        }
        break;
    case CDDS_PREERASE:
        if (!_pszTheme)
        {
            RECT rcClient;
            GetClientRect(_hwndMB, &rcClient);
            ptbcd->clrBtnFace = _pcmb->_pmbm->_clrBackground;
            SHFillRectClr(pnmcd->hdc, &rcClient, _pcmb->_pmbm->_clrBackground);
            dwRet = CDRF_SKIPDEFAULT;
        }
        break;
    }

    return dwRet;
}    



void CMenuToolbarBase::_PressBtn(int idBtn, BOOL bDown)
{
    if (!_fVerticalMB)
    {
        DWORD dwState = ToolBar_GetState(_hwndMB, idBtn);

        if (bDown)
            dwState |= TBSTATE_PRESSED;
        else
            dwState &= ~TBSTATE_PRESSED;

        ToolBar_SetState(_hwndMB, idBtn, dwState);

        // Avoid ugly late repaints
        UpdateWindow(_hwndMB);
    }
}    


/*----------------------------------------------------------
Purpose: IWinEventHandler::OnWinEvent method

         Processes messages passed on from the menuband.
*/
STDMETHODIMP CMenuToolbarBase::OnWinEvent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plres)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    HRESULT hres = S_FALSE;

    EnterModeless();

    switch (uMsg)
    {
    case WM_SETTINGCHANGE:
        if ((SHIsExplorerIniChange(wParam, lParam) == EICH_UNKNOWN) || 
            (wParam == SPI_SETNONCLIENTMETRICS))
        {
            v_UpdateIconSize(-1, TRUE);
            v_Refresh();
            goto L_WM_SYSCOLORCHANGE;
        }
        break;

    case WM_SYSCOLORCHANGE:
    L_WM_SYSCOLORCHANGE:
        ToolBar_SetInsertMarkColor(_hwndMB, GetSysColor(COLOR_MENUTEXT));
        SendMessage(_hwndMB, uMsg, wParam, lParam);
        InvalidateRect(_hwndMB, NULL, TRUE);
        hres = S_OK;
        break;

    case WM_PALETTECHANGED:
        InvalidateRect( _hwndMB, NULL, FALSE );
        SendMessage( _hwndMB, uMsg, wParam, lParam );
        hres = S_OK;
        break;

    case WM_NOTIFY:
        *plres = _OnNotify((LPNMHDR)lParam);
        hres = S_OK;
        break;
    }

    ExitModeless();

    return hres;
}


void CMenuToolbarBase::v_CalcWidth(int* pcxMin, int* pcxMax)
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    ASSERT(IS_VALID_WRITE_PTR(pcxMin, int));
    ASSERT(IS_VALID_WRITE_PTR(pcxMax, int));

    *pcxMin = 0;
    *pcxMax = 0;

    if (_fVerticalMB && _pcmb->_pmbm && _pcmb->_pmbm->_hFontMenu)
    {
        HIMAGELIST himl;
        int cel;
        int cxItemMax = 0;
        int cyItemMax = 0;
        HWND hwnd = _hwndMB;
        
        ASSERT(hwnd);
        
        HDC hdc = GetDC(hwnd);

        if (hdc)
        {
            HFONT hFontOld = (HFONT) SelectObject(hdc, _pcmb->_pmbm->_hFontMenu);

            if (hFontOld)
            {
                TCHAR sz[MAX_PATH];
                cel = ToolBar_ButtonCount(hwnd);

                // Find the maximum length text
                for(int i = 0; i < cel; i++)
                {
                    int idCmd = GetButtonCmd(hwnd, i);
                    if (_idCmdChevron != idCmd &&
                        !(!_pcmb->_fExpanded && v_GetFlags(idCmd) & SMIF_DEMOTED) &&
                        SendMessage(hwnd, TB_GETBUTTONTEXT, idCmd, (LPARAM)sz) > 0)
                    {
                        RECT rect = {0};
                        DWORD dwDTFlags = DT_CALCRECT | DT_SINGLELINE | DT_LEFT | DT_VCENTER;
                        if (ShowAmpersand())
                            dwDTFlags |= DT_NOPREFIX;

                        DrawText(hdc, sz, -1, &rect, dwDTFlags);
                        cxItemMax = max(rect.right, cxItemMax);
                        cyItemMax = max(rect.bottom, cyItemMax);
                    }
                }

                SelectObject(hdc, hFontOld);
            }
            ReleaseDC(hwnd, hdc);
        }
        
        himl = (HIMAGELIST)SendMessage(hwnd, TB_GETIMAGELIST, 0, 0);
        if (himl)
        {
            int cy;
            
            // Start with the width of the button
            ImageList_GetIconSize(himl, pcxMin, &cy);

            // We want at least a bit of space around the icon
            if (_uIconSizeMB != ISFBVIEWMODE_SMALLICONS)
            {
                // Old FSMenu code took the height of the larger of 
                // the icon and text then added 2.
                ToolBar_SetPadding(hwnd, ICONFUDGE, 0);
                *pcxMin += 10;
            }
            else 
            {
                // Old FSMenu code took the height of the larger of 
                // the icon and text then added cySpacing, which defaults to 6.
                ToolBar_SetPadding(hwnd, ICONFUDGE, ICONFUDGE);
                *pcxMin += 3 * GetSystemMetrics(SM_CXEDGE);
            }

            cyItemMax = max(cyItemMax, cy) + ICONFUDGE;
        }

        
        RECT rect = {0};
        int cxDesired = _pcmb->_pmbm->_cxMargin + cxItemMax + _pcmb->_pmbm->_cxArrow;
        int cxMax = 0;
           
        if (SystemParametersInfoA(SPI_GETWORKAREA, 0, &rect, 0))
        {
            // We're figuring a third of the screen is a good max width
            cxMax = (rect.right-rect.left) / 3;
        }

        *pcxMin += min(cxDesired, cxMax) + LIST_GAP;
        *pcxMax = *pcxMin;
    }

    *pcxMin = max(*pcxMin, _cxMinMenu);
    TraceMsg(TF_MENUBAND, "CMenuToolbarBase::v_CalcWidth(%d, %d)", *pcxMin, *pcxMax);
}


void CMenuToolbarBase::_SetToolbarState()
{
    SHSetWindowBits(_hwndMB, GWL_STYLE, TBSTYLE_LIST, TBSTYLE_LIST);
}


void CMenuToolbarBase::v_ForwardMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    RECT rc;
    POINT pt;
    
    pt.x = GET_X_LPARAM(lParam);
    pt.y = GET_Y_LPARAM(lParam);

    GetWindowRect(_hwndMB, &rc);

    if (PtInRect(&rc, pt))
    {
        ScreenToClient(_hwndMB, &pt);
        SendMessage(_hwndMB, uMsg, wParam, MAKELONG(pt.x, pt.y));
    }
}

void CMenuToolbarBase::NegotiateSize()
{
    HWND hwndParent = GetParent(_hwndMB);
    if (hwndParent && (GetDesktopWindow() != hwndParent))
    {
        RECT rc = {0};
        GetClientRect(hwndParent, &rc);
        _pcmb->OnPosRectChangeDB(&rc);
    }
    // If we came in here it's because the Menubar did not change sizes or position.
}

void CMenuToolbarBase::SetParent(HWND hwndParent) 
{ 
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    if (hwndParent)
    {
        if (!_hwndMB)
            CreateToolbar(hwndParent);
    }
    else
    {
        // As an optimization, we implement "disowning" ourselves
        // as just moving ourselves offscreen.  The previous parent
        // still owns us.  The parent is invariably the menusite.
        RECT rc = {-1,-1,-1,-1};
        SetWindowPos(NULL, &rc, 0);
    }

    // We want to set the parent all the time because we don't want to destroy the 
    // window with it's parent..... Sizing to -1,-1,-1,-1 causes it not to be displayed.
    if (_hwndMB)
    {
        ::SetParent(_hwndMB, hwndParent); 
        SendMessage(_hwndMB, TB_SETPARENT, (WPARAM)hwndParent, NULL);
    }
}


void CMenuToolbarBase::v_OnEmptyToolbar()
{
    ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
    for (int iNumButtons = ToolBar_ButtonCount(_hwndMB) -1;
         iNumButtons >= 0; 
         iNumButtons--)
    {
        // HACKHACK (lamadio): For some reason, _fEmptyingToolbar gets set to FALSE.
        // We then Do a TB_DELETEBUTTON, which sends a notify. This does go through on
        // the top level menubands (Start Menu, Browser menu bar), and deletes the 
        // associated data. We then try and delete it again.
        // So now, I set null into the sub menu, so that the other code gracefully fails.

        TBBUTTONINFO tbbi = {0};
        tbbi.cbSize = sizeof(tbbi);
        tbbi.dwMask = TBIF_LPARAM | TBIF_BYINDEX;
        ToolBar_GetButtonInfo(_hwndMB, iNumButtons, &tbbi);

        void *pData = (void*)tbbi.lParam;
        tbbi.lParam = NULL;

        ToolBar_SetButtonInfo(_hwndMB, iNumButtons, &tbbi);

        SendMessage(_hwndMB, TB_DELETEBUTTON, iNumButtons, 0);
        v_OnDeleteButton(pData);
    }
}

void CMenuToolbarBase::EmptyToolbar()
{
    if (_hwndMB)
    {
        _fEmptyingToolbar = TRUE;
        v_OnEmptyToolbar();
        _fEmptyingToolbar = FALSE;
    }
}

void CMenuToolbarBase::v_Close()
{
    EmptyToolbar();
    if (_hwndMB)
    {
        //Kill timers to prevent race condition
        KillTimer(_hwndMB, MBTIMER_POPOUT);
        KillTimer(_hwndMB, MBTIMER_DRAGOVER);
        KillTimer(_hwndMB, MBTIMER_EXPAND);
        KillTimer(_hwndMB, MBTIMER_ENDEDIT);
        KillTimer(_hwndMB, MBTIMER_CLOSE);
        KillTimer(_hwndMB, MBTIMER_CLICKUNHANDLE);
        KillTimer(_hwndMB, MBTIMER_DRAGPOPDOWN);

        DestroyWindow(_hwndMB);
        _hwndMB = NULL;
    }
}

void CMenuToolbarBase::Activate(BOOL fActivate)
{
    if (fActivate == FALSE)
    {
        _fEditMode = FALSE;
    }
}

int CMenuToolbarBase::_CalcChevronSize()
{

    int dSeg;
    int dxy = _pcmb->_pmbm->_cyChevron;

    dxy -= 4;
    dSeg = dxy / 4;

    return dSeg * 4 + 4;
}

void CMenuToolbarBase::_DrawChevron(HDC hdc, LPRECT prect, BOOL fFocus, BOOL fSelected)
{
    RECT rcBox = *prect;
    RECT rcDrop;

    const int dExtra = 3;
    int dxy;

    rcBox.left += dExtra;
    rcBox.right -= dExtra;
    dxy = _CalcChevronSize();

    rcDrop.left = ((rcBox.right + rcBox.left) >> 1) - (dxy/4);
    rcDrop.right = rcDrop.left + dxy - 1;

    int dSeg = ((RECTWIDTH(rcDrop) - 2) >> 2);

    rcDrop.top = (rcBox.top + rcBox.bottom)/2 - (2 * dSeg + 1);
    //rcDrop.bottom = rcBox.top;

    if (fFocus && !_pszTheme)
    {
        if (_pcmb->_pmbm->_fFlatMenuMode)
        {
            SHFillRectClr(hdc, prect, GetSysColor(COLOR_MENUHILIGHT));
            SHOutlineRect(hdc, prect, GetSysColor(COLOR_HIGHLIGHT));
        }
        else
        {
            InflateRect(&rcBox, 0, -3);
            SHFillRectClr(hdc, &rcBox, _pcmb->_pmbm->_clrDemoted);
            DrawEdge(hdc, &rcBox, fSelected? BDR_SUNKENINNER : BDR_RAISEDINNER, BF_RECT);

            if (fSelected)
            {
                rcDrop.top += 1;
                rcDrop.left += 1;
            }
        }
    }

    HBRUSH hbrOld = SelectBrush(hdc, _pcmb->_pmbm->_hbrText);


    int y = rcDrop.top + 1;
    int xBase = rcDrop.left+ dSeg;

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

        y += (x >= 0) ? -1 : 1;
    }

    SelectBrush(hdc, hbrOld);
}


// Takes into accout Separators, hidden and Disabled items
/*----------------------------------------------------------
Purpose: This function sets the nearest legal button to be
         the hot item, skipping over any separators, or hidden
         or disabled buttons.
    

*/

int CMenuToolbarBase::GetValidHotItem(int iDir, int iIndex, int iCount, DWORD dwFlags)
{
    if (iIndex == MBSI_LASTITEM)
    {
        // -2 is special value meaning "last item on toolbar"
        int cButtons = (int)SendMessage(_hwndMB, TB_BUTTONCOUNT, 0, 0);
        iIndex = cButtons - 1;
    }

    while ( (iCount == -1 || iIndex < iCount) && iIndex >= 0)
    {
        TBBUTTON tbb;

        // Toolbar will trap out of bounds condition when iCount is -1
        if (!SendMessage(_hwndMB, TB_GETBUTTON, iIndex, (LPARAM)&tbb))
            return -1;

        int idCmd = GetButtonCmd(_hwndMB, iIndex);


        if (tbb.fsState & TBSTATE_ENABLED && 
            !(tbb.fsStyle & TBSTYLE_SEP || 
              tbb.fsState & TBSTATE_HIDDEN) &&
              !(v_GetFlags(idCmd) & SMIF_DEMOTED && !_pcmb->_fExpanded) )
        {
            return iIndex;
        }
        else
            iIndex += iDir;
    }

    return -1;
}

BOOL CMenuToolbarBase::SetHotItem(int iDir, int iIndex, int iCount, DWORD dwFlags)
{
    int iPos = GetValidHotItem(iDir, iIndex, iCount, dwFlags);
    if (iPos >= 0)
        SendMessage(_hwndMB, TB_SETHOTITEM2, iPos, dwFlags);

    return (BOOL)(iPos >= 0);
}


static const BYTE g_rgsStateMap[][3] = 
{
#if defined(FIRST)
//     T,  I,  F
    {  0,  1,  2},      // State 0
    {  3,  1,  2},      // State 1
    {  4,  1,  2},      // State 2
    { 11,  5,  2},      // State 3
    { 10,  1,  6},      // State 4
    {  7,  1,  2},      // State 5
    {  8,  1,  2},      // State 6
    { 11,  9,  2},      // State 7
    { 10,  1, 10},      // State 8
    { 11,  1,  2},      // State 9
    { 10,  1,  2},      // State 10     // End State
    { 12,  1,  2},      // State 11     // Flash.
    { 10,  1,  2},      // State 12
#elif defined(SECOND)
//     T,  I,  F
    {  0,  1,  2},      // State 0
    {  3,  1,  2},      // State 1
    {  4,  1,  2},      // State 2
    { 11,  5,  6},      // State 3
    { 10,  5,  6},      // State 4
    {  7,  5,  6},      // State 5
    {  8,  9,  6},      // State 6
    { 11,  9,  8},      // State 7
    { 10,  9, 10},      // State 8
    { 11,  9,  8},      // State 9
    { 10, 10, 10},      // State 10     // End State
    { 10,  9,  8},      // State 11     // Flash.
    { 10,  9,  8},      // State 12
    { 10,  9,  8},      // State 13
#elif defined(THIRD)
//     T,  I,  F
    {  0,  1,  2},      // State 0
    {  3,  1,  2},      // State 1
    { 12,  1,  2},      // State 2
    { 11,  5,  6},      // State 3
    { 10,  5,  6},      // State 4
    {  7,  5,  6},      // State 5
    { 13,  5,  6},      // State 6
    { 11,  9,  8},      // State 7
    { 10,  9, 10},      // State 8
    { 11,  9,  8},      // State 9
    { 10, 10, 10},      // State 10     // End State
    { 10,  9,  8},      // State 11     // Flash.
    {  4,  1,  2},      // State 12
    {  8,  5,  6},      // State 13
#else
//     T,  I,  F
    {  0,  1,  2},      // State 0
    {  3,  1,  2},      // State 1
    {  4,  1,  2},      // State 2
    { 11,  5,  6},      // State 3
    { 10,  5,  6},      // State 4
    {  7,  5,  6},      // State 5
    {  8,  5,  6},      // State 6
    { 11,  9,  8},      // State 7
    { 10,  9, 10},      // State 8
    { 11,  9,  8},      // State 9
    { 10, 10, 10},      // State 10     // End State
    {  4,  3,  4},      // State 11     // Flash.
#endif
};

#define MAX_STATE 13

void CMenuToolbarBase::_FireEvent(BYTE bEvent)
{
    // We don't want to expand and cover up any dialogs.
    if (_fSuppressUserMonitor)
        return;

    if (!_fHasDemotedItems)
        return;

    if (UEM_RESET == bEvent)
    {
        TraceMsg(TF_MENUBAND, "CMTB::UEM Reset state to 0");
        _pcmb->_pmbState->SetUEMState(0);
        return;
    }

    ASSERT(bEvent >= UEM_TIMEOUT && 
            bEvent <= UEM_HOT_FOLDER);

    BYTE bOldState = _pcmb->_pmbState->GetUEMState();
    BYTE bNewState = g_rgsStateMap[_pcmb->_pmbState->GetUEMState()][bEvent];

    ASSERT(bOldState >= 0 &&  bOldState <= MAX_STATE);

    TraceMsg(TF_MENUBAND, "*** UEM OldState (%d), New State (%d) ***", bOldState, bNewState);

    _pcmb->_pmbState->SetUEMState(bNewState);

    switch (bNewState)
    {
    case 10:    // End State
        TraceMsg(TF_MENUBAND, "*** UEM Entering State 10. Expanding *** ", bOldState, bNewState);
        KillTimer(_hwndMB, MBTIMER_UEMTIMEOUT);
        if (_pcmb->_fInSubMenu)
        {
            IUnknown_QueryServiceExec(_pcmb->_pmpSubMenu, SID_SMenuBandChild,
                &CGID_MenuBand, MBANDCID_EXPAND, 0, NULL, NULL);
        }
        else
        {
            Expand(TRUE);
        }
        _pcmb->_pmbState->SetUEMState(0);
        break;

    case 11:   // Flash
        // This gets reset when the flash is done...
        TraceMsg(TF_MENUBAND, "*** UEM Entering State 11 Flashing *** ");
        KillTimer(_hwndMB, MBTIMER_UEMTIMEOUT);
        _FlashChevron();
        break;
    }
}


void CMenuToolbarBase::_FlashChevron()
{
    if (_idCmdChevron != -1)
    {
        _cFlashCount = 0;
        ToolBar_MarkButton(_hwndMB, _idCmdChevron, FALSE);
        SetTimer(_hwndMB, MBTIMER_FLASH, MBTIMER_FLASHTIME, NULL);
    }
}


LRESULT CMenuToolbarBase::_DefWindowProcMB(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    // Are we being asked for the IAccessible for the client?
    if (uMsg == WM_GETOBJECT && (OBJID_CLIENT == lParam))
    {
        // Don't process OBJID_MENU. By the time we get here, we ARE the menu.
        LRESULT lres = 0;
        CAccessible* pacc = new CAccessible(SAFECAST(_pcmb, IMenuBand*));
        if (pacc)
        {
            lres = pacc->InitAcc();
            if (SUCCEEDED((HRESULT)lres))
            {
                lres = LresultFromObject(IID_IAccessible, wParam, SAFECAST(pacc, IAccessible*));
            }
            pacc->Release();
        }

        return lres;
    }

    return 0;
}

void CMenuToolbarBase::v_Show(BOOL fShow, BOOL fForceUpdate)
{
    // HACKHACK (lamadio): When we create the menubands, we do not set the
    // TOP level band's fonts until a refresh. This code here fixes it.
    if (_fFirstTime && _pcmb->_fTopLevel)
    {
        SetMenuBandMetrics(_pcmb->_pmbm);
    }

    if (fShow)
    {
        SetKeyboardCue();
        _pcmb->_pmbState->PutTipOnTop();
    }
    else
    {
        _fHasDrop = FALSE;
        KillTimer(_hwndMB, MBTIMER_DRAGPOPDOWN);
        KillTimer(_hwndMB, MBTIMER_INFOTIP);    // Don't show it if we're not displayed :-)
        _pcmb->_pmbState->HideTooltip(TRUE);
    }

    _fSuppressUserMonitor = FALSE;
}

void CMenuToolbarBase::SetKeyboardCue()
{
    if (_pcmb->_pmbState)
    {
        SendMessage(GetParent(_hwndMB), WM_CHANGEUISTATE, 
            MAKEWPARAM(_pcmb->_pmbState->GetKeyboardCue() ? UIS_CLEAR : UIS_SET,
            UISF_HIDEACCEL), 0);
    }
}

HRESULT CMenuToolbarBase::SetMinWidth(int cxMenu)
{
    _cxMinMenu = cxMenu;
    return S_OK;
}

HRESULT CMenuToolbarBase::SetNoBorder(BOOL fNoBorder)
{
    _fNoBorder = fNoBorder;
    return S_OK;
}

HRESULT CMenuToolbarBase::SetTheme(LPCWSTR pszTheme)
{
    HRESULT hr = S_OK;

    Str_SetPtr(&_pszTheme, pszTheme);

    if (_hwndMB)
    {
        SendMessage(_hwndMB, TB_SETWINDOWTHEME, 0, (LPARAM)_pszTheme);
        _RefreshTheme();
    }

    return hr;
}


void CMenuToolbarBase::_RefreshTheme()
{
    if (_hTheme)
    {
        CloseThemeData(_hTheme);
    }

    _hTheme = OpenThemeData(_hwndMB, c_wzMenuBandTheme);
}