#include "priv.h"
#include "sccls.h"
#include <uxtheme.h>
#define WANT_CBANDSITE_CLASS
#include "bandsite.h"
#include "bandobj.h"
#include "caggunk.h"
#include "droptgt.h"
#include "resource.h"
#include "bands.h"
#include "legacy.h"
#include "apithk.h"

#include "mluisupp.h"

#define TF_BANDDD   0x00400000
#define DM_INIT     0               //
#define DM_PERSIST  0               // trace IPS::Load, ::Save, etc.
#define DM_MENU     0               // menu code
#define DM_DRAG     0               // drag&drop
#define DM_FOCUS    0               // focus
#define DM_PERF     0               // perf tune
#define DM_PERF2    0               // perf tune (verbose)

#define IDM_DRAGDROP    1

#define ISMOVEDDISABLED(dwBandID)   ((S_OK == _IsRestricted(dwBandID, RA_MOVE, BAND_ADMIN_NOMOVE)) ? TRUE : FALSE)
#define ISDDCLOSEDISABLED(dwBandID) ((S_OK == _IsRestricted(dwBandID, RA_DRAG, BAND_ADMIN_NODDCLOSE)) ? TRUE : FALSE)

// drag state (NOTE from dockbar.h)
#define DRAG_NIL        0       // nil
#define DRAG_MOVE       1       // moving
#define DRAG_SIZE       2       // sizing

typedef struct {
    UINT cx;
    UINT fStyle;
    UINT cxMinChild;
    UINT cyMinChild;
    UINT cyIntegral;
    UINT cyMaxChild;
    UINT cyChild;
} PERSISTBANDINFO_V3;


typedef struct {
    UINT cx;
    UINT fStyle;
    UINT cxMinChild;  // UNUSED. reclaim!
    UINT cyMinChild;
    UINT cyIntegral;   // UNUSED
    UINT cyMaxChild;    // UNUSED.
    UINT cyChild;
    DWORD dwAdminSettings;
    BITBOOL fNoTitle:1;
} PERSISTBANDINFO;
#define RBBIM_XPERSIST  (RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE)

#ifdef DEBUG

extern unsigned long DbStreamTell(IStream *pstm);

#else

#define DbStreamTell(pstm)      ((ULONG) 0)

#endif

UINT _FixMenuIndex(HMENU hmenu, UINT indexMenu)
{
    UINT i;

    i = GetMenuItemCount(hmenu);
    if (indexMenu > i)
        indexMenu = i;
    return indexMenu;
}

#define SUPERCLASS CAggregatedUnknown


HRESULT CBandSite::v_InternalQueryInterface(REFIID riid, void **ppvObj)
{
    static const QITAB qit[] = {
        // perf: last tuned 980728
        QITABENT(CBandSite, IBandSite),             // IID_IBandSite
        QITABENT(CBandSite, IInputObject),          // IID_IInputObject
        QITABENT(CBandSite, IServiceProvider),      // IID_IServiceProvider
        QITABENT(CBandSite, IOleCommandTarget),     // IID_IOleCommandTarget
        QITABENTMULTI(CBandSite, IOleWindow, IDeskBarClient),   // IID_IOleWindow
        QITABENT(CBandSite, IWinEventHandler),      // IID_IWinEventHandler
        QITABENT(CBandSite, IInputObjectSite),      // IID_IInputObjectSite
        QITABENT(CBandSite, IDeskBarClient),        // IID_IDeskBarClient
        QITABENTMULTI(CBandSite, IPersist, IPersistStream),     // rare IID_IPersist
        QITABENT(CBandSite, IPersistStream),        // rare IID_IPersistStream
        QITABENT(CBandSite, IBandSiteHelper),       // rare IBandSiteHelper
        QITABENT(CBandSite, IDropTarget),           // rare IID_IDropTarget
        { 0 },
    };
    
    return QISearch(this, qit, riid, ppvObj);
}

DWORD _SetDataListFlags(IUnknown *punk, DWORD dwMaskBits, DWORD dwValue)
{
    DWORD dw = 0;
    IShellLinkDataList *pdl;
    if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IShellLinkDataList, &pdl))))
    {
        pdl->GetFlags(&dw);
        dw = (dw & ~dwMaskBits) | (dwValue & dwMaskBits);
        pdl->SetFlags(dw);
        pdl->Release();
    }
    return dw;
}


/////  impl of IServiceProvider
HRESULT CBandSite::QueryService(REFGUID guidService, REFIID riid, void **ppvObj)
{
    HRESULT hres = E_FAIL;
    *ppvObj = NULL; // assume error

    if (IsEqualIID(guidService, SID_IBandProxy)) 
    {
        hres =  QueryService_SID_IBandProxy(_punkSite, riid, &_pbp, ppvObj);
        if(!_pbp)
        {
            // We need to create it ourselves since our parent couldn't help
            ASSERT(FALSE == _fCreatedBandProxy);

            hres = CreateIBandProxyAndSetSite(_punkSite, riid, &_pbp, ppvObj);
            if(_pbp)
            {
                ASSERT(S_OK == hres);
                _fCreatedBandProxy = TRUE;   
            }
        }
    } 
    else if (IsEqualIID(guidService, SID_ITopViewHost)) 
    {
        return QueryInterface(riid, ppvObj);
    } 
    else if (IsEqualIID(guidService, IID_IBandSite))
    {
        // It is common for bands to save/load pidls for persistence.
        // CShellLink is a robust way to do this, so let's share one
        // among all the bands.
        //
        // NOTE: This is shared between bands, so if you request it
        // you must complete your use of it within the scope of your
        // function call!
        //
        if (IsEqualIID(riid, IID_IShellLinkA) ||
            IsEqualIID(riid, IID_IShellLinkW))
        {
            if (NULL == _plink)
                CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellLinkA, &_plink));
            if (_plink)
            {
                // we know that the bandsite is going to be pointing to local folders
                // to avoid a perf hit we get in loading the LINKINFO.DLL we explictly
                // disable that functionality here.
                _SetDataListFlags(_plink, SLDF_FORCE_NO_LINKINFO, SLDF_FORCE_NO_LINKINFO);
                hres = _plink->QueryInterface(riid, ppvObj);
            }
        }
    } 
    else
    {
        hres = IUnknown_QueryService(_punkSite, guidService, riid, ppvObj);
    }
    return hres;
}

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

    return *lphwnd ?  S_OK : E_FAIL;
}

CBandSite::CBandSite(IUnknown* punkAgg) : SUPERCLASS(punkAgg)
{
    DWORD dwData = 0;
    DWORD dwSize = SIZEOF(dwData);

    // We assume this object was zero inited.
    ASSERT(!_pbp);
    ASSERT(FALSE == _fCreatedBandProxy);
    SHRegGetUSValue(SZ_REGKEY_GLOBALADMINSETTINGS, SZ_REGVALUE_GLOBALADMINSETTINGS,
        NULL, (LPVOID) &dwData, &dwSize, FALSE, NULL, 0);

    if (IsFlagSet(dwData, BAND_ADMIN_ADMINMACHINE))
        _fIEAKInstalled = TRUE;
    else
        _fIEAKInstalled = FALSE;

    _dwStyle = BSIS_AUTOGRIPPER;

    //
    //  We check whether or not this succeeded in CBandSite::_Initialize
    //
    _QueryOuterInterface(IID_PPV_ARG(IBandSite, &_pbsOuter));
    DllAddRef();
}

void CBandSite::_ReleaseBandItemData(LPBANDITEMDATA pbid, int iIndex)
{
    if (pbid->pdb) 
    {
        REBARBANDINFO rbbi;

        pbid->pdb->CloseDW(0);

        if (-1 != iIndex)
        {
            // The band's hwnd is typically destroyed in CloseDW
            rbbi.cbSize = sizeof(rbbi);
            rbbi.fMask = RBBIM_CHILD | RBBIM_LPARAM;
            rbbi.hwndChild = NULL;
            rbbi.lParam = NULL;
            EVAL( SendMessage(_hwnd, RB_SETBANDINFO, iIndex, (LPARAM) &rbbi) );
        }

        // this is called from remove and the destroy.
        IUnknown_SetSite(pbid->pdb, NULL);
        ATOMICRELEASE(pbid->pdb);
    }

    if (pbid->pweh == _pwehCache)
        ATOMICRELEASE(_pwehCache);

    ATOMICRELEASE(pbid->pweh);
    LocalFree(pbid);
}

CBandSite::~CBandSite()
{
    ATOMICRELEASE(_pdtobj);

    if(_pbp && _fCreatedBandProxy)
        _pbp->SetSite(NULL);
        
    ATOMICRELEASE(_pbp);

    ATOMICRELEASE(_pwehCache);
    _CacheActiveBand(NULL);

    _Close();

    SetDeskBarSite(NULL);

    if (_plink)
        _plink->Release();

    RELEASEOUTERINTERFACE(_pbsOuter);
    DllRelease();
}

//***   _IsBandDeleteable --
// ENTRY/EXIT
//  idBand  band ID
//  ret     TRUE if deletable, o.w. FALSE (also FALSE on bogus band)
BOOL CBandSite::_IsBandDeleteable(DWORD dwBandID)
{
    DWORD dwState;

    if (FAILED(_pbsOuter->QueryBand(dwBandID, NULL, &dwState, NULL, 0))
      || (dwState & BSSF_UNDELETEABLE))
    {
        return FALSE;
    }

    ASSERT(dwBandID != (DWORD)-1);  // make sure QueryBand catches this

    return TRUE;
}

DWORD CBandSite::_GetAdminSettings(DWORD dwBandID)
{
    LPBANDITEMDATA pbid = _GetBandItem(_BandIDToIndex(dwBandID)); 

    if (EVAL(pbid))
        return pbid->dwAdminSettings;

    return BAND_ADMIN_NORMAL;
}


void CBandSite::_SetAdminSettings(DWORD dwBandID, DWORD dwNewAdminSettings)
{
    LPBANDITEMDATA pbid = _GetBandItem(_BandIDToIndex(dwBandID)); 

    if (EVAL(pbid))
        pbid->dwAdminSettings = dwNewAdminSettings;
}


//***   CBandSite::IBandSite::* {

/*----------------------------------------------------------
Purpose: IBandSite::EnumBands method

*/
HRESULT CBandSite::EnumBands(UINT uBand, DWORD* pdwBandID)
{
    ASSERT((NULL == pdwBandID && (UINT)-1 == uBand) || 
           IS_VALID_WRITE_PTR(pdwBandID, DWORD));

    if (uBand == (UINT)-1)
        return _GetBandItemCount();      // query count

    LPBANDITEMDATA pbid = _GetBandItem(uBand);
    if (pbid)
    {
        *pdwBandID = pbid->dwBandID;
        return S_OK;
    }
    return E_FAIL;
}


/*----------------------------------------------------------
Purpose: IBandSite::QueryBand method

*/
HRESULT CBandSite::QueryBand(DWORD dwBandID, IDeskBand** ppstb, DWORD* pdwState, LPWSTR pszName, int cchName)
{
    ASSERT(NULL == ppstb || IS_VALID_WRITE_PTR(ppstb, IDeskBand));
    ASSERT(NULL == pdwState || IS_VALID_WRITE_PTR(pdwState, DWORD));
    ASSERT(NULL == pszName || IS_VALID_WRITE_BUFFER(pszName, WCHAR, cchName));

    if (ppstb)
        *ppstb = NULL;

    LPBANDITEMDATA pbid = _GetBandItemDataStructByID(dwBandID);
    if (!pbid)
        return E_FAIL;
    if (pszName)
    {
        StrCpyNW(pszName, pbid->szTitle, cchName);
    }

    if (ppstb)
    {
        *ppstb = pbid->pdb;
        if (pbid->pdb)
        {
            pbid->pdb->AddRef();
        }
    }
    
    if (pdwState)
    {
        *pdwState = 0;
        if (pbid->fShow)
            *pdwState = BSSF_VISIBLE;
        if (pbid->fNoTitle)
            *pdwState |= BSSF_NOTITLE;
        if (pbid->dwModeFlags & DBIMF_UNDELETEABLE)
            *pdwState |= BSSF_UNDELETEABLE;
    }

    return S_OK;
}


/*----------------------------------------------------------
Purpose: IBandSite::SetBandState

* NOTES
*   failure handling is inconsistent (1 band vs. all bands case)
*/
HRESULT CBandSite::SetBandState(DWORD dwBandID, DWORD dwMask, DWORD dwState)
{
    LPBANDITEMDATA pbid;
    HRESULT hr;

    if (dwBandID == (DWORD) -1)
    {
        BOOL fChange = FALSE;
        for (int i = _GetBandItemCount() - 1; i >= 0; i--)
        {
            pbid = _GetBandItem(i);
            if (pbid)
            {
                hr = _SetBandStateHelper(pbid->dwBandID, dwMask, dwState);
                ASSERT(SUCCEEDED(hr));
                fChange |= (hr != S_OK);
            }
            else
            {
                return E_FAIL;
            }
        }
        if (fChange)
            _UpdateAllBands(FALSE, FALSE);
        return S_OK;
    }
    else
    {
        hr = _SetBandStateHelper(dwBandID, dwMask, dwState);
        if (SUCCEEDED(hr) && hr != S_OK)
        {
            _UpdateBand(dwBandID);
            return S_OK;
        }
    }
    return E_FAIL;
}

//***
// ENTRY/EXIT
//  ret     S_OK|changed on success, o.w. E_*.
// NOTES
//  only a helper for SetBandState, don't call directly
HRESULT CBandSite::_SetBandStateHelper(DWORD dwBandID, DWORD dwMask, DWORD dwState)
{
    LPBANDITEMDATA pbid;

    pbid = _GetBandItem(_BandIDToIndex(dwBandID));
    if (pbid)
    {
        DWORD dwOldState;

        if (FAILED(QueryBand(dwBandID, NULL, &dwOldState, NULL, 0)))
        {
            ASSERT(0);  // 'impossible'
            dwOldState = (DWORD)-1;
        }

        if (dwMask & BSSF_VISIBLE)
            _ShowBand(pbid, dwState & BSSF_VISIBLE);

        if (dwMask & BSSF_NOTITLE)
            pbid->fNoTitle = BOOLIFY(dwState & BSSF_NOTITLE);
            
        // FEATURE: (kkahl): BSSF_UNDELETABLE cannot currently be modified with
        // this interface.

        return ResultFromShort((dwOldState ^ dwState) & dwMask);
    }
    return E_FAIL;
}

//***   _CheckNotifyOnAddRemove -- handle notifies for add/remove/empty
// DESCRIPTION
//  add/remove always sends a BSID_BANDADDED/BSID_BANDREMOVED.
//  remove of last always sends a DBCID_EMPTY.
//  in floating mode, a transition to/from 1 band does a refresh.
//
void CBandSite::_CheckNotifyOnAddRemove(DWORD dwBandID, int iCode)
{
    int cBands;
    if (!_pct)
        return;

    if (iCode == CNOAR_CLOSEBAR)
    {
        // Shut down the whole thing
        cBands = 0;
    }
    else
    {
        VARIANTARG var;
        int nCmdID;

        cBands = _GetBandItemCount();   // post-op # (since op happened in caller)

        VariantInit(&var);
        var.vt = VT_UI4;
        var.ulVal = dwBandID;

        BOOL fOne = FALSE;
        switch (iCode)
        {
        case CNOAR_ADDBAND:
            fOne = (cBands == 2);   // 1->2
            nCmdID = BSID_BANDADDED;
            break;
        case CNOAR_REMOVEBAND:
            fOne = (cBands == 1);   // 2->1
            nCmdID = BSID_BANDREMOVED;
            break;
        default:
            ASSERT(0);
            return;
        }

        if ((fOne && (_dwMode & DBIF_VIEWMODE_FLOATING)))
        {
            // n.b. fBSOnly *must* be TRUE for perf
            _UpdateAllBands(TRUE, TRUE);  // force refresh of optional gripper/title
        }

        _pct->Exec(&CGID_BandSite, nCmdID, 0, &var, NULL);
    }

    if (cBands == 0)
    {
        ASSERT(iCode != CNOAR_ADDBAND);     // sanity check
        _pct->Exec(&CGID_DeskBarClient, DBCID_EMPTY, 0, NULL, NULL);
    }

    return;
}

/*----------------------------------------------------------
Purpose: IBandSite::RemoveBand method

*/
HRESULT CBandSite::RemoveBand(DWORD dwBandID)
{
    int iIndex = _BandIDToIndex(dwBandID);
    LPBANDITEMDATA pbid = _GetBandItem(iIndex);
    if (pbid)
    {
        // Release the banditem data first, while it can still
        // receive cleanup notifications from its control.  *Then*
        // delete the band item.
        _ReleaseBandItemData(pbid, iIndex);
        _DeleteBandItem(iIndex);    // unhook from host (rebar)
        _CheckNotifyOnAddRemove(dwBandID, CNOAR_REMOVEBAND);
        return S_OK;
    }
    return E_FAIL;
}


void CBandSite::_OnCloseBand(DWORD dwBandID)
{
    if (dwBandID == -1)
    {
        // Close everything
        _CheckNotifyOnAddRemove(dwBandID, CNOAR_CLOSEBAR);
    }
    else
    {
        // Close just this band

        LPBANDITEMDATA pbid = _GetBandItemDataStructByID(dwBandID);
        USES_CONVERSION;

        if (EVAL(pbid) && ConfirmRemoveBand(_hwnd, IDS_CONFIRMCLOSEBAND,W2T(pbid->szTitle)))
            RemoveBand(dwBandID);
    }
}

void CBandSite::_MinimizeBand(DWORD dwBandID)
{
    SendMessage(_hwnd, RB_MINIMIZEBAND, _BandIDToIndex(dwBandID), TRUE);
}

void CBandSite::_MaximizeBand(DWORD dwBandID)
{
    SendMessage(_hwnd, RB_MAXIMIZEBAND, _BandIDToIndex(dwBandID), TRUE);
}

//
// private insert a band into the container control by ID
// returns the band ID in ShortFromResult(hres)
//

HRESULT CBandSite::_AddBandByID(IUnknown *punk, DWORD dwID)
{
    IDeskBand *pdb;
    HRESULT hr = punk->QueryInterface(IID_PPV_ARG(IDeskBand, &pdb));
    if (SUCCEEDED(hr)) 
    {
        ASSERT(pdb);
        BANDITEMDATA *pbid = (BANDITEMDATA *)LocalAlloc(LPTR, sizeof(BANDITEMDATA));
        if (pbid)
        {
            pbid->dwBandID = dwID;
            pbid->pdb = pdb;      // ref held by QI above
            pbid->fShow = TRUE;     // initially visible

            pbid->pdb->QueryInterface(IID_PPV_ARG(IWinEventHandler, &pbid->pweh));
            hr = IUnknown_SetSite(pbid->pdb, SAFECAST(this, IBandSite*));
            if (SUCCEEDED(hr))
            {
                hr = pbid->pdb->GetWindow(&pbid->hwnd);
                if (SUCCEEDED(hr))
                {
                    if (_AddBandItem(pbid))
                    {
                        if (_dwShowState == DBC_SHOW) 
                        {
                            ASSERT(pbid->fShow);
                            pbid->pdb->ShowDW(TRUE);
                            _MinimizeBand(pbid->dwBandID);
                        }
                
                        _CheckNotifyOnAddRemove(pbid->dwBandID, CNOAR_ADDBAND);
                        hr = ResultFromShort(pbid->dwBandID); // success
                    }
                    else
                    {
                        hr = E_FAIL;
                    }
                }
            }

            if (FAILED(hr))
            {
                // clean up
                _ReleaseBandItemData(pbid, -1);
            }

            //
            // Now that we've added the band, clear the _SendToToolband cache.
            //
            // We need to do this because we might have gotten a message for
            // the band before it was inserted, in which case we'll have cached
            // a NULL handler for the band's hwnd (preventing the band from
            // getting any messages thereafter).
            //
            ATOMICRELEASE(_pwehCache);
            _hwndCache = NULL;
        } 
        else
        {
            hr = E_OUTOFMEMORY;
            pdb->Release();    // don't hold on to this
        }
    }

    return hr;
}


/*----------------------------------------------------------
Purpose: IBandSite::AddBand method.

         Insert a band into the container control.

Returns: the band ID in ShortFromResult(hres)

*/
HRESULT CBandSite::AddBand(IUnknown *punk)
{
    HRESULT hres = _AddBandByID(punk, _dwBandIDNext);
    if (SUCCEEDED(hres))
    {
        _dwBandIDNext++;
    }
    return hres;
}

void CBandSite::_UpdateBand(DWORD dwBandID)
{
    LPBANDITEMDATA pbid = _GetBandItem(_BandIDToIndex(dwBandID)); 
    if (pbid)
    {
        _UpdateBandInfo(pbid, FALSE);
        _OnRBAutoSize(NULL);
    }
}

void CBandSite::_UpdateAllBands(BOOL fBSOnly, BOOL fNoAutoSize)
{
    BOOL_PTR fRedraw = SendMessage(_hwnd, WM_SETREDRAW, FALSE, 0);

    for (int i = _GetBandItemCount() - 1; i >= 0; i--)
    {
        LPBANDITEMDATA pbid = _GetBandItem(i);
        if (pbid)
            _UpdateBandInfo(pbid, fBSOnly);
    }    

    SendMessage(_hwnd, WM_SETREDRAW, fRedraw, 0);

    if (!fNoAutoSize)
    {
        SendMessage(_hwnd, RB_SIZETORECT, 0, 0);
        _OnRBAutoSize(NULL);
    }
}

// *** IOleCommandTarget ***
HRESULT CBandSite::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
{
    HRESULT hres = OLECMDERR_E_UNKNOWNGROUP;

    if (pguidCmdGroup)
    {
        if (IsEqualIID(*pguidCmdGroup, IID_IDockingWindow))
        {
            for (ULONG i=0 ; i<cCmds ; i++)
            {
                switch (rgCmds[i].cmdID)
                {
                case DBID_BANDINFOCHANGED:
                case DBID_PUSHCHEVRON:
                    rgCmds[i].cmdf = OLECMDF_ENABLED;
                    break;

                case DBID_PERMITAUTOHIDE:
                    // defer decision to the bands
                    for (int iBand = _GetBandItemCount() - 1; iBand >= 0; iBand--)
                    {
                        LPBANDITEMDATA pbid = _GetBandItem(iBand);
                        if (pbid)
                        {
                            if (SUCCEEDED(IUnknown_QueryStatus(pbid->pdb, pguidCmdGroup, 1, &rgCmds[i], pcmdtext)) &&
                                ((rgCmds[i].cmdf & OLECMDF_SUPPORTED) && !(rgCmds[i].cmdf & OLECMDF_ENABLED)))
                            {
                                break;
                            }
                        }
                    }
                    break;
    
                default:
                    rgCmds[i].cmdf = 0;
                    break;
                }
            }
            return S_OK;
        }
        else if (IsEqualIID(*pguidCmdGroup, CGID_Explorer))
        {
            return IUnknown_QueryStatus(_ptbActive, pguidCmdGroup, cCmds, rgCmds, pcmdtext);
        }
    }

    // if we got here, we didn't handle it
    // forward it down
    return MayQSForward(_ptbActive, OCTD_DOWN, pguidCmdGroup, cCmds, rgCmds, pcmdtext);
}


int _QueryServiceCallback(LPBANDITEMDATA pbid, void *pv)
{
    QSDATA* pqsd = (QSDATA*)pv;

    if (pbid->fShow)
        pqsd->hres = IUnknown_QueryService(pbid->pdb, *(pqsd->pguidService), *(pqsd->piid), pqsd->ppvObj);

    // stop if we found the service
    return SUCCEEDED(pqsd->hres) ? FALSE : TRUE;
}


typedef struct {
    HRESULT hres;
    const GUID *pguidCmdGroup;
    DWORD nCmdID;
    DWORD nCmdexecopt;
    VARIANTARG *pvarargIn;
    VARIANTARG *pvarargOut;
} EXECDATA;

int _ExecCallback(LPBANDITEMDATA pbid, void *pv)
{
    EXECDATA* ped = (EXECDATA*)pv;
    
    ped->hres = IUnknown_Exec(pbid->pdb, ped->pguidCmdGroup, ped->nCmdID, ped->nCmdexecopt,
        ped->pvarargIn, ped->pvarargOut);
    return 1;
}

HRESULT CBandSite::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
    HRESULT hres = OLECMDERR_E_UNKNOWNGROUP;
    HRESULT hresTmp;

    if (pguidCmdGroup == NULL)
    {
        /*NOTHING*/
        ;
    }
    else if (IsEqualIID(*pguidCmdGroup, CGID_DeskBand))
    {
        switch (nCmdID)
        {
        case DBID_BANDINFOCHANGED:
            if (!pvarargIn)
                _UpdateAllBands(FALSE, FALSE);
            else if (pvarargIn->vt == VT_I4) 
                _UpdateBand(pvarargIn->lVal);
            hres = S_OK;
            
            // forward this up.
            if (_pct)
            {
                _pct->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
            }
            goto Lret;

        case DBID_PUSHCHEVRON:
            if (pvarargIn && pvarargIn->vt == VT_I4)
            {
                int iIndex = _BandIDToIndex(nCmdexecopt);
                SendMessage(_hwnd, RB_PUSHCHEVRON, iIndex, pvarargIn->lVal);
                hres = S_OK;
            }
            goto Lret;

        case DBID_MAXIMIZEBAND:
            if (pvarargIn && pvarargIn->vt == VT_UI4)
                _MaximizeBand(pvarargIn->ulVal);
            hres = S_OK;
            goto Lret;
#if 1 // { FEATURE: temporary until add cbs::Select() mfunc
        case DBID_SHOWONLY:
            {
                int iCount = _GetBandItemCount();
                
                // pvaIn->punkVal:
                //  punk hide everyone except me
                //  0    hide everyone
                //  1    show everyone
                // FEATURE: we should use pvaIn->lVal not punkVal since we're
                // allowing 0 & 1 !!! (and not doing addref/release)
                ASSERT(pvarargIn && pvarargIn->vt == VT_UNKNOWN);
                if (pvarargIn->punkVal == NULL || pvarargIn->punkVal == (IUnknown*)1)
                    TraceMsg(TF_BANDDD, "cbs.e: (id=DBID_SHOWONLY, punk=%x)", pvarargIn->punkVal);
                // show myself, hide everyone else
                TraceMsg(TF_BANDDD, "cbs.Exec(DBID_SHOWONLY): n=%d", _GetBandItemCount());

                // wait to show this band until we've hidden the others
                LPBANDITEMDATA pbidShow = NULL;
                // FEATURE: this (IUnknown*)1 is bogus! Also mentioned above.
                BOOL bShowAll = (pvarargIn->punkVal == (IUnknown*)1);
                for (int i = iCount - 1; i >= 0; i--)
                {
                    LPBANDITEMDATA pbid = _GetBandItem(i);

                    if (pbid)
                    {
                        BOOL fShow;

                        fShow = bShowAll || SHIsSameObject(pbid->pdb, pvarargIn->punkVal);
                        if (!fShow || bShowAll)
                            _ShowBand(pbid, fShow);
                        else
                            pbidShow = pbid;
                    }
                }
                if (pbidShow)
                {
                    _ShowBand(pbidShow, TRUE);
                    // nash:37290 set focus to band on open
                    if (_dwShowState == DBC_SHOW)
                        IUnknown_UIActivateIO(pbidShow->pdb, TRUE, NULL);
                    else
                        ASSERT(0);
                }
            }
            break;
#endif // }
        }
    }
    else if (IsEqualIID(*pguidCmdGroup, CGID_Explorer))
    {
        return IUnknown_Exec(_ptbActive, pguidCmdGroup, nCmdID, nCmdexecopt,
                pvarargIn, pvarargOut);
    }
    else if (IsEqualIID(*pguidCmdGroup, CGID_DeskBarClient))
    {
        switch (nCmdID)
        {
        case DBCID_ONDRAG:
            if (EVAL(pvarargIn->vt == VT_I4))
            {
                ASSERT(pvarargIn->lVal == 0 || pvarargIn->lVal == DRAG_MOVE);
                TraceMsg(DM_TRACE, "cbs.e: DBCID_ONDRAG i=%d", pvarargIn->lVal);
                _fDragging = pvarargIn->lVal;
            }
            break;

        case DBCID_GETBAR:
            // return IUnkown of my IDeskBar host
            if ((pvarargOut != NULL) && _pdb)
            {
                ::VariantInit(pvarargOut);
                V_VT(pvarargOut)      = VT_UNKNOWN;
                V_UNKNOWN(pvarargOut) = _pdb;
                _pdb->AddRef();
                hres = S_OK;
                goto Lret;
            }
            break;
        }
    }


    // if we got here, we didn't handle it
    // see if we should forward it down
    hresTmp = IsExecForward(pguidCmdGroup, nCmdID);
    if (SUCCEEDED(hresTmp) && HRESULT_CODE(hresTmp) > 0)
    {
        // down (singleton or broadcast)
        if (HRESULT_CODE(hresTmp) == OCTD_DOWN)
        {
            // down (singleton)

            hres = IUnknown_Exec(_ptbActive, pguidCmdGroup, nCmdID, nCmdexecopt,
                pvarargIn, pvarargOut);
        }
        else
        {
            // down (broadcast)
            // n.b. hres is a bit weird: 'last one wins'
            // FEATURE: should we just return S_OK?
            ASSERT(HRESULT_CODE(hresTmp) == OCTD_DOWNBROADCAST);

            EXECDATA ctd = { hres, pguidCmdGroup, nCmdID, nCmdexecopt,
                pvarargIn, pvarargOut };

            _BandItemEnumCallback(1, _ExecCallback, &ctd);
            hres = ctd.hres;
        }
    }

Lret:
    return hres;
}

/***    _ShowBand -- show/hide band (cached state, band, and rebar band)
 */
void CBandSite::_ShowBand(LPBANDITEMDATA pbid, BOOL fShow)
{
    int i;

    pbid->fShow = BOOLIFY(fShow);
    if (pbid->pdb)
    {
        pbid->pdb->ShowDW(fShow && (_dwShowState == DBC_SHOW));
    }
    
    i = _BandIDToIndex(pbid->dwBandID);
    SendMessage(_hwnd, RB_SHOWBAND, i, fShow);

    // get me a window to draw D&D curosors on. . .
    SHGetTopBrowserWindow(SAFECAST(this, IBandSite*), &_hwndDD);
}


/*----------------------------------------------------------
Purpose: IBandSite::GetBandSiteInfo

*/
HRESULT CBandSite::GetBandSiteInfo(BANDSITEINFO * pbsinfo)
{
    ASSERT(IS_VALID_WRITE_PTR(pbsinfo, BANDSITEINFO));

    if (pbsinfo->dwMask & BSIM_STATE)
        pbsinfo->dwState = _dwMode;

    if (pbsinfo->dwMask & BSIM_STYLE)
        pbsinfo->dwStyle = _dwStyle;

    return S_OK;
}


/*----------------------------------------------------------
Purpose: IBandSite::SetBandSiteInfo

*/
HRESULT CBandSite::SetBandSiteInfo(const BANDSITEINFO * pbsinfo)
{
    ASSERT(IS_VALID_READ_PTR(pbsinfo, BANDSITEINFO));

    if (pbsinfo->dwMask & BSIM_STATE)
        _dwMode = pbsinfo->dwState;

    if (pbsinfo->dwMask & BSIM_STYLE)
    {
        // If the BSIS_SINGLECLICK style changed, change the rebar style
        if ( _hwnd && ((_dwStyle ^ pbsinfo->dwStyle) & BSIS_SINGLECLICK) )
            SHSetWindowBits(_hwnd, GWL_STYLE, RBS_DBLCLKTOGGLE, (pbsinfo->dwStyle & BSIS_SINGLECLICK)?0:RBS_DBLCLKTOGGLE);
            
        _dwStyle = pbsinfo->dwStyle;
    }

    return S_OK;
}


/*----------------------------------------------------------
Purpose: IBandSite::GetBandObject

*/
HRESULT CBandSite::GetBandObject(DWORD dwBandID, REFIID riid, void **ppvObj)
{
    HRESULT hres = E_FAIL;
    
    *ppvObj = NULL;

    if (IsEqualIID(riid, IID_IDataObject)) 
    {
        *ppvObj = _DataObjForBand(dwBandID);
        if (*ppvObj)
            hres = S_OK;
    }
    else 
    {
        LPBANDITEMDATA pbid = _GetBandItemDataStructByID(dwBandID);
        if (pbid && pbid->pdb)
        {
            hres = pbid->pdb->QueryInterface(riid, ppvObj);
        }
    }

    return hres;
}


/*----------------------------------------------------------
Purpose: Returns a pointer to the band item data given an
         externally known band ID.

Returns: NULL if band ID is illegal
*/
LPBANDITEMDATA CBandSite::_GetBandItemDataStructByID(DWORD uID)
{
    int iBand = _BandIDToIndex(uID);
    if (iBand == -1)
        return NULL;
    return _GetBandItem(iBand);
}


__inline HRESULT _FwdWinEvent(IWinEventHandler* pweh, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plres)
{
    ASSERT(pweh);
    ASSERT(hwnd == HWND_BROADCAST || pweh->IsWindowOwner(hwnd) == S_OK);

    return pweh->OnWinEvent(hwnd, uMsg, wParam, lParam, plres);
}

/*----------------------------------------------------------
Purpose: Forwards messages to the band that owns the window.

Returns: TRUE if the message was forwarded

*/
BOOL CBandSite::_SendToToolband(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plres)
{
    BOOL fSent = FALSE;
    LRESULT lres = 0;

    if (hwnd)
    {
        if (hwnd == _hwndCache)
        {
            ASSERT(hwnd != HWND_BROADCAST);

            if (_pwehCache)
            {
                _FwdWinEvent(_pwehCache, hwnd, uMsg, wParam, lParam, &lres);
                fSent = TRUE;
            }
        }
        else
        {
            LPBANDITEMDATA pbid = NULL;
            int i;

            for (i = _GetBandItemCount() - 1; i >= 0; i--)
            {
                pbid = _GetBandItem(i);
                if (pbid)
                {
                    if (pbid->pweh)
                    {
                        if (hwnd == HWND_BROADCAST || 
                          (pbid->pweh->IsWindowOwner(hwnd) == S_OK))
                        {
                            _FwdWinEvent(pbid->pweh, hwnd, uMsg, wParam, lParam, &lres);
                            fSent = TRUE;

                            if (hwnd != HWND_BROADCAST)
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                        if (hwnd == HWND_BROADCAST && pbid->hwnd)
                        {
                            lres = SendMessage(pbid->hwnd, uMsg, wParam, lParam);
                            fSent = TRUE;
                        }
                    }
                }
            }

            if (hwnd != HWND_BROADCAST)
            {
                ATOMICRELEASE(_pwehCache);
                _hwndCache = hwnd;
                if (fSent && pbid)
                {
                    _pwehCache = pbid->pweh;
                    _pwehCache->AddRef();
                }
            }
        }
    }

    if (plres)
        *plres = lres;
    
    return fSent;
}

typedef struct {
    HWND hwnd;
    HRESULT hres;
} WINDOWOWNERDATA;

int _IsWindowOwnerCallback(LPBANDITEMDATA pbid, void *pv)
{
    WINDOWOWNERDATA* pwod = (WINDOWOWNERDATA*)pv;
    
    if (pbid->pweh && (pbid->pweh->IsWindowOwner(pwod->hwnd) == S_OK)) 
    {
        pwod->hres = S_OK;
        return 0;
    }
    return 1;
}

HRESULT CBandSite::IsWindowOwner(HWND hwnd)
{
    if (hwnd == _hwnd)
        return S_OK;
    
    WINDOWOWNERDATA wod = { hwnd, S_FALSE };
    _BandItemEnumCallback(1, _IsWindowOwnerCallback, &wod);
    return wod.hres;
}

//***   CBandSite::IDeskBarClient::* {
HRESULT CBandSite::GetSize(DWORD dwWhich, LPRECT prc)
{
    HRESULT hres = E_FAIL;
    switch (dwWhich)
    {
    case DBC_GS_IDEAL:
        {
            prc->right = 0;
            prc->bottom = 0;
            
            BOOL_PTR fRedraw = SendMessage(_hwnd, WM_SETREDRAW, FALSE, 0);
            for (int i = _GetBandItemCount() - 1; i >= 0; i--)
            {
                LPBANDITEMDATA pbid = _GetBandItem(i);
                if (pbid)
                {
                    RECT rc;
                    
                    SendMessage(_hwnd, RB_GETBANDBORDERS, _BandIDToIndex(pbid->dwBandID), (LPARAM) &rc);
                    _UpdateBandInfo(pbid, FALSE);
                    
                    if (pbid->fShow)
                    {
                        if (_dwMode & (DBIF_VIEWMODE_FLOATING | DBIF_VIEWMODE_VERTICAL))
                        {
                            prc->right = max(prc->right, pbid->ptActual.x + (rc.left + rc.right));
                            prc->bottom += pbid->ptActual.y + rc.top + rc.bottom;
                        }
                        else
                        {
                            prc->bottom = max(prc->right, pbid->ptActual.x + (rc.left + rc.right));
                            prc->right += pbid->ptActual.y + rc.top + rc.bottom;
                        }
                    }
                    hres = S_OK;
                }
            }
            SendMessage(_hwnd, WM_SETREDRAW, fRedraw, 0);
        }
        break;
        
    case DBC_GS_SIZEDOWN:
        {
            // Used to make a band change size in chuncks
            SendMessage(_hwnd, RB_SIZETORECT, RBSTR_CHANGERECT, (LPARAM)prc);
            hres = S_OK;
        }
        break;
    }
    return hres;
}


void CBandSite::_Close() 
{        
    if (_hwnd)
    {
        //  (scotth): This method is getting called by the destructor,
        //  and calls _DeleteAllBandItems, which sends messages to _hwnd.
        //  _hwnd is already destroyed by this time.  If you hit this assert
        //  it is because in debug windows it RIPs like crazy.
        // 970508 (adp): pblm was that we weren't doing DestroyWnd etc.
        //  
        //  Do no remove this assert....please fix the root problem.
        ASSERT(IS_VALID_HANDLE(_hwnd, WND));
        SendMessage(_hwnd, WM_SETREDRAW, 0, 0);
        _DeleteAllBandItems(); 

        DestroyWindow(_hwnd);
        _hwnd = 0;
    }
}

    
HRESULT CBandSite::UIActivateDBC(DWORD dwState)
{
    if (dwState != _dwShowState)
    {
        BOOL fShow = dwState;

        _dwShowState = dwState;
        // map UIActivateDBC to ShowDW
        if (DBC_SHOWOBSCURE == dwState)
            fShow = FALSE;

        BOOL_PTR fRedraw = SendMessage(_hwnd, WM_SETREDRAW, FALSE, 0);
        for (int i = _GetBandItemCount() - 1; i >= 0; i--)
        {
            LPBANDITEMDATA pbid = _GetBandItem(i);
            if (pbid && pbid->pdb)
                pbid->pdb->ShowDW(fShow && pbid->fShow);
        }

        // do this now intead of at creation so that 
        // rebar doesn't keep trying to autosize us while
        // we're not even visible
        SHSetWindowBits(_hwnd, GWL_STYLE, RBS_AUTOSIZE, RBS_AUTOSIZE);
        SendMessage(_hwnd, WM_SIZE, 0, 0);
        SendMessage(_hwnd, WM_SETREDRAW, (DBC_SHOW == dwState) ? TRUE : fRedraw, 0);
    }
    return S_OK;
}

DWORD CBandSite::_GetWindowStyle(DWORD* pdwExStyle)
{
    *pdwExStyle = WS_EX_TOOLWINDOW;
    DWORD dwStyle = RBS_REGISTERDROP | RBS_VARHEIGHT | RBS_BANDBORDERS |
                    WS_VISIBLE |  WS_CHILD | WS_CLIPCHILDREN |
                    WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NORESIZE | CCS_NOPARENTALIGN;
    if (_dwStyle & BSIS_LEFTALIGN)
    {
        dwStyle |= RBS_VERTICALGRIPPER;
    }

    if (!(_dwStyle & BSIS_SINGLECLICK))
    {
        dwStyle |= RBS_DBLCLKTOGGLE;
    }

    return dwStyle;
}

HRESULT CBandSite::_Initialize(HWND hwndParent)
{
    //
    //  I hope we have an IBandSite to talk to.
    //
    if (!_pbsOuter)
        return E_FAIL;

    if (!_hwnd) 
    {
        DWORD dwExStyle;
        DWORD dwStyle = _GetWindowStyle(&dwExStyle);

        _hwnd = CreateWindowEx(dwExStyle, REBARCLASSNAME, NULL, dwStyle,
                               0, 0, 0, 0, hwndParent, (HMENU) FCIDM_REBAR, HINST_THISDLL, NULL);

        if (_hwnd)
        {
            SendMessage(_hwnd, RB_SETTEXTCOLOR, 0, CLR_DEFAULT);
            SendMessage(_hwnd, RB_SETBKCOLOR, 0, CLR_DEFAULT);
            SendMessage(_hwnd, WM_SETREDRAW, FALSE, 0);
        }
    }
    
    return _hwnd ? S_OK : E_OUTOFMEMORY;
}

HRESULT CBandSite::SetDeskBarSite(IUnknown* punkSite)
{
    HRESULT hr = S_OK;

    if (!punkSite)
    {
        // Time to tell the bands to free their
        // back pointers on us or we never get freed...

        // 970325 for now bs::SetDeskBarSite(NULL) is 'overloaded'
        // to mean do both a CloseDW and a SetSite.
        // when we clean up our act and have a bs::Close iface
        // we'll go back to the '#else' code below.
        if (_hwnd)
            _Close();
    }

    ATOMICRELEASE(_pct);
    ATOMICRELEASE(_pdb);
    ATOMICRELEASE(_punkSite);

    if (_pbp && _fCreatedBandProxy)
        _pbp->SetSite(punkSite);

    if (punkSite)
    {
        _punkSite = punkSite;
        _punkSite->AddRef();

        if (!_hwnd) 
        {
            HWND hwndParent;
            IUnknown_GetWindow(punkSite, &hwndParent);
            hr = _Initialize(hwndParent);
        }

        punkSite->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &_pct));
        punkSite->QueryInterface(IID_PPV_ARG(IDeskBar, &_pdb));
    }
    
    return hr;
}

HRESULT CBandSite::SetModeDBC(DWORD dwMode)
{
    if (dwMode != _dwMode)
    {
        _dwMode = dwMode;

        if (_hwnd)
        {
            DWORD dwStyle = 0;
            if (dwMode & (DBIF_VIEWMODE_FLOATING | DBIF_VIEWMODE_VERTICAL))
            {
                dwStyle |= CCS_VERT;
            }
            SHSetWindowBits(_hwnd, GWL_STYLE, CCS_VERT, dwStyle);
        }

        _UpdateAllBands(FALSE, FALSE);
    }
    return S_OK;
}

// }

IDropTarget* CBandSite::_WrapDropTargetForBand(IDropTarget* pdtBand)
{
    if (!pdtBand || (_dwStyle & BSIS_NODROPTARGET))
    {
        // addref it for the new pointer
        if (pdtBand)
            pdtBand->AddRef();
        return pdtBand;
    }
    else
    {
        return DropTargetWrap_CreateInstance(pdtBand, SAFECAST(this, IDropTarget*), _hwndDD);
    }
}

LRESULT CBandSite::_OnNotify(LPNMHDR pnm)
{
    NMOBJECTNOTIFY *pnmon = (NMOBJECTNOTIFY *)pnm;
    
    switch (pnm->code)
    {
    case RBN_GETOBJECT:
    {
        pnmon->hResult = E_FAIL;
        
        // if we're the drag source, then a band is dragging... we want to only
        // give out the bandsite's drop target
        if (pnmon->iItem != -1 && !_fDragSource) 
        {
            LPBANDITEMDATA pbid = _GetBandItemDataStructByID(pnmon->iItem);
            if (EVAL(pbid) && pbid->pdb)
            {
                pnmon->hResult = pbid->pdb->QueryInterface(*pnmon->piid, &pnmon->pObject);

                // give a wrapped droptarget instead of the band's droptarget
                if (IsEqualIID(*pnmon->piid, IID_IDropTarget))
                {
                    IDropTarget* pdtBand;
                    BOOL fNeedReleasePdtBand = FALSE;

                    if (SUCCEEDED(pnmon->hResult))
                    {
                        pdtBand = (IDropTarget*)(pnmon->pObject);
                    }
                    else
                    {
                        CDropDummy *pdtgt = new CDropDummy(_hwndDD);
                        pdtBand = SAFECAST(pdtgt, IDropTarget*);
                        fNeedReleasePdtBand = TRUE;
                    }

                    IDropTarget* pdt = _WrapDropTargetForBand(pdtBand);
                    if (pdt)
                    {
                        pnmon->pObject = pdt;
                        pnmon->hResult = S_OK;

                        // we've handed off pdtBand to pdt
                        fNeedReleasePdtBand = TRUE;
                    }

                    if (fNeedReleasePdtBand && pdtBand)
                        pdtBand->Release();
                }

                if (FAILED(pnmon->hResult) && !(_dwStyle & BSIS_NODROPTARGET)) 
                    pnmon->hResult = QueryInterface(*pnmon->piid, &pnmon->pObject);
            }
        } 
        break;
    }

    case RBN_BEGINDRAG:
        return _OnBeginDrag((NMREBAR*)pnm);

    case RBN_AUTOSIZE:
        _OnRBAutoSize((NMRBAUTOSIZE*)pnm);
        break;

    case RBN_CHEVRONPUSHED:
    {
        LPNMREBARCHEVRON pnmch = (LPNMREBARCHEVRON) pnm;
        LPBANDITEMDATA pbid = _GetBandItem(pnmch->uBand);
        if (EVAL(pbid))
        {
            MapWindowPoints(_hwnd, HWND_DESKTOP, (LPPOINT)&pnmch->rc, 2);
            ToolbarMenu_Popup(_hwnd, &pnmch->rc, pbid->pdb, pbid->hwnd, 0, (DWORD)pnmch->lParamNM);
        }
        break;
    }

    case RBN_AUTOBREAK:
    {
        if (_dwStyle & BSIS_PREFERNOLINEBREAK)
        {
            Comctl32_FixAutoBreak(pnm);
        }
        break;
    }
    }

    return 0;
}

void CBandSite::_OnRBAutoSize(NMRBAUTOSIZE* pnm)
{
    // DRAG_MOVE: we turn off autosize during (most of) a move because
    // fVertical is out of sync until the very end
    if (_pdb && _GetBandItemCount() && _fDragging != DRAG_MOVE)
    {
        RECT rc;
        int iHeightCur;
        int iHeight = (int)SendMessage(_hwnd, RB_GETBARHEIGHT, 0, 0);

#ifdef DEBUG
        DWORD dwStyle = GetWindowLong(_hwnd, GWL_STYLE);
#endif

        GetWindowRect(_hwnd, &rc);
        MapWindowRect(HWND_DESKTOP, GetParent(_hwnd), &rc);

        if (_dwMode & (DBIF_VIEWMODE_FLOATING | DBIF_VIEWMODE_VERTICAL))
        {
            ASSERT((dwStyle & CCS_VERT));
            iHeightCur = RECTWIDTH(rc);
            rc.right = rc.left + iHeight;
        }
        else
        {
            ASSERT(!(dwStyle & CCS_VERT));
            iHeightCur = RECTHEIGHT(rc);
            rc.bottom = rc.top + iHeight;
        }

        if ((iHeightCur != iHeight) || (IsOS(OS_WHISTLERORGREATER)))
        {
            _pdb->OnPosRectChangeDB(&rc);
        }
    }
}

IDataObject* CBandSite::_DataObjForBand(DWORD dwBandID)
{
    IDataObject* pdtobjReturn = NULL;

    LPBANDITEMDATA pbid = _GetBandItemDataStructByID(dwBandID);
    if (EVAL(pbid) && pbid->pdb)
    {
        HRESULT hres;
        
        CBandDataObject* pdtobj = new CBandDataObject();
        if (pdtobj)
        {
            hres = pdtobj->Init(pbid->pdb, this, dwBandID);

            if (SUCCEEDED(hres))
            {
                pdtobjReturn = pdtobj;
                pdtobjReturn->AddRef();
            }
            
            pdtobj->Release();
        }
    }
    return pdtobjReturn;
}

LRESULT CBandSite::_OnBeginDrag(NMREBAR* pnm)
{
    LRESULT lres = 0;
    DWORD dwBandID = _IndexToBandID(pnm->uBand);

    IDataObject* pdtobj = _DataObjForBand(dwBandID);

    ATOMICRELEASE(_pdtobj);

    _uDragBand = pnm->uBand;
    _pdtobj = pdtobj;
    // because the RBN_BEGINDRAG is synchronous and so is SHDoDragDrop
    // post this message to ourselves instead of calling dragdrop directly.
    // note that we don't have a window of our own, so we post to our parent
    // and let the message reflector send it back to us
    PostMessage(GetParent(_hwnd), WM_COMMAND, MAKELONG(0, IDM_DRAGDROP), (LPARAM)_hwnd);
    return 1;
}

// return TRUE if the user drags out of the rect of the rebar meaning that we should
// go into ole drag drop.
BOOL CBandSite::_PreDragDrop()
{
    BOOL f = FALSE;
    RECT rc;
    POINT pt;
    DWORD dwBandID = _IndexToBandID(_uDragBand);    // Find the BandID before an reordering that may happen.
    
    GetWindowRect(_hwnd, &rc);
    SetCapture(_hwnd);

    InflateRect(&rc, GetSystemMetrics(SM_CXEDGE) * 3, GetSystemMetrics(SM_CYEDGE) * 3);
    while (GetCapture() == _hwnd)
    {
        MSG msg;
        
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            switch (msg.message)
            {
            case WM_MOUSEMOVE:
                GetCursorPos(&pt);
                if (!ISMOVEDDISABLED(dwBandID))
                {
                    if (PtInRect(&rc, pt))
                    {
                        SendMessage(_hwnd, RB_DRAGMOVE, 0, (LPARAM)-1);
                    } else if (!ISDDCLOSEDISABLED(dwBandID) && _pdtobj)
                    {
                        // we've moved out of the bounds of the rebar..  switch to ole drag
                        f = TRUE;
                        SetCapture(NULL);
                    }
                }
                break;

            case WM_LBUTTONUP:
            case WM_LBUTTONDOWN:
            case WM_MBUTTONUP:
            case WM_MBUTTONDOWN:
            case WM_RBUTTONUP:
            case WM_RBUTTONDOWN:
                // bail on any mouse button action
                SetCapture(NULL);
                break;
                
            case WM_KEYDOWN:
                switch (msg.wParam)
                {
                case VK_ESCAPE:
                    SetCapture(NULL);
                    break;
                }
                // fall through
                
            default:
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    }

    if (ISDDCLOSEDISABLED(dwBandID) || !_IsBandDeleteable(dwBandID))
    {
        /// if don't allow close, never return true for ole drag.
        f = FALSE;
    }

    return f;
}

void CBandSite::_DoDragDrop()
{
    DWORD dwBandID = _IndexToBandID(_uDragBand);
    DWORD dwEffect = DROPEFFECT_MOVE;

    _fDragSource = TRUE;

    SendMessage(_hwnd, RB_BEGINDRAG, _uDragBand, (LPARAM)-2);

    HRESULT hres = S_OK;

    // first check to see if we even need to go into Ole drag, or if
    // it can all be contained within the rebar
    if (_PreDragDrop())
    {
        SHLoadOLE(SHELLNOTIFY_OLELOADED); // Browser Only - our shell32 doesn't know ole has been loaded
        hres = SHDoDragDrop(_hwnd, _pdtobj, NULL, dwEffect, &dwEffect);
    }
    else
    {
        // if we kept it all within win32 dragging, then set no drop effect
        dwEffect = DROPEFFECT_NONE;
    }

    SendMessage(_hwnd, RB_ENDDRAG, 0, 0);
    _fDragSource = FALSE;
    if (dwEffect & DROPEFFECT_MOVE) 
    {
        RemoveBand(dwBandID);
    } 
    else if (!dwEffect && hres == DRAGDROP_S_DROP) 
    {
        // if the drop was done, but the target didn't allow
        // then we float the band.
    }

    ATOMICRELEASE(_pdtobj);
}

HMENU CBandSite::_LoadContextMenu()
{
    return LoadMenuPopup_PrivateNoMungeW(MENU_BANDSITE1);
}

HRESULT CBandSite::_OnBSCommand(int idCmd, DWORD idBandActive, LPBANDITEMDATA pbid)
{
    HRESULT hr = S_OK;

    switch (idCmd)
    {
    case BSIDM_CLOSEBAND:
        _OnCloseBand(idBandActive);
        break;

    case BSIDM_SHOWTITLEBAND:
        ASSERT(idBandActive != (DWORD)-1 && pbid);
        if (pbid)
        {
            pbid->fNoTitle = !pbid->fNoTitle;
            _UpdateBandInfo(pbid, FALSE);
        }
        break;

    case BSIDM_IEAK_DISABLE_MOVE:
    case BSIDM_IEAK_DISABLE_DDCLOSE:
        ASSERT(idBandActive != (DWORD)-1);
        if (idBandActive != (DWORD)-1)
        {
            static const int idCmds[]  = { BSIDM_IEAK_DISABLE_MOVE,   BSIDM_IEAK_DISABLE_DDCLOSE  };
            static const int idFlags[] = { BAND_ADMIN_NOMOVE,         BAND_ADMIN_NODDCLOSE        };

            DWORD dwFlag = SHSearchMapInt(idCmds, idFlags, ARRAYSIZE(idCmds), idCmd);
            DWORD dwAdminSettings = _GetAdminSettings(idBandActive);

            // Toggle Setting.
            ToggleFlag(dwAdminSettings, dwFlag);

            // Set Menu Item Check Mark appropriately.
            _SetAdminSettings(idBandActive, dwAdminSettings);
        }
        break;

    default:
        ASSERT(0);
        hr = E_FAIL;
        break;
    }

    return hr;
}

// returns the index of the band hit by lParam using context menu semantics (lParam == -1 for keyboard)
int CBandSite::_ContextMenuHittest(LPARAM lParam, POINT* ppt)
{
    int iBandIndex;

    if (lParam == (LPARAM) -1)
    {
        // Keyboard activation.  Use active band.
        DWORD dwBandID = _BandIDFromPunk(_ptbActive);
        iBandIndex = _BandIDToIndex(dwBandID);

        LPBANDITEMDATA pbid = _GetBandItem(iBandIndex);
        if (pbid)
        {
            RECT rc;
            GetWindowRect(pbid->hwnd, &rc);
            ppt->x = rc.left;
            ppt->y = rc.top;
        }
    }
    else
    {
        // Mouse activation.  Figure out which band got clicked.
        RBHITTESTINFO rbht;

        ppt->x = GET_X_LPARAM(lParam);
        ppt->y = GET_Y_LPARAM(lParam);
        rbht.pt = *ppt;
        ScreenToClient(_hwnd, &rbht.pt);
        SendMessage(_hwnd, RB_HITTEST, 0, (LPARAM)&rbht);
        iBandIndex = rbht.iBand;
    }

    return iBandIndex;
}

HRESULT CBandSite::_OnContextMenu(WPARAM wParam, LPARAM lParam)
{
    HRESULT hres = S_OK;

    HMENU hmenu = CreatePopupMenu();

    if (hmenu)
    {
        HRESULT hresT;
        int idCmd = 1;
        IContextMenu *pcm, *pcmParent = NULL, *pcmChild = NULL;

        POINT pt;
        int iBandIndex = _ContextMenuHittest(lParam, &pt);

        // map rebar index to band id
        // get band info for that band id
        DWORD idBandActive = _IndexToBandID(iBandIndex);
        LPBANDITEMDATA pbid = _GetBandItemDataStructByID(idBandActive);

        //
        // self (top)
        //
        int idCmdBS1 = idCmd;

        HMENU hmenuMe = _LoadContextMenu();
        if (hmenuMe)
        {
            BOOL fDeleteShowTitle = TRUE;
            if (pbid && !(_dwStyle & BSIS_LOCKED))
            {
                DESKBANDINFO dbi;

                CheckMenuItem(hmenuMe, BSIDM_SHOWTITLEBAND,
                    pbid->fNoTitle ? MF_BYCOMMAND|MF_UNCHECKED : MF_BYCOMMAND|MF_CHECKED);
                dbi.dwMask = 0;     // paranoia (and needed for taskband!)
                _GetBandInfo(pbid, &dbi);
                // make sure pbid in sync
                ASSERT((dbi.dwMask & DBIM_TITLE) || pbid->fNoTitle);
                if ((dbi.dwMask & DBIM_TITLE) && _IsEnableTitle(pbid))
                {
                    fDeleteShowTitle = FALSE;
                }
            }

            if (fDeleteShowTitle)
            {
                DeleteMenu(hmenuMe, BSIDM_SHOWTITLEBAND, MF_BYCOMMAND);
            }

            idCmd += Shell_MergeMenus(hmenu, hmenuMe, 0, idCmd, 0x7fff, 0) - (idCmd);
            DestroyMenu(hmenuMe);
        }

        //
        // child
        //
        int idCmdChild = idCmd;

        if (pbid && pbid->pdb)
        {
            // merge in band's menu (at front)
            hresT = pbid->pdb->QueryInterface(IID_PPV_ARG(IContextMenu, &pcmChild));
            if (SUCCEEDED(hresT))
            {
                // 0=at front
                hresT = pcmChild->QueryContextMenu(hmenu, 0, idCmd, 0x7fff, 0);
                if (SUCCEEDED(hresT))
                    idCmd += HRESULT_CODE(hresT);
            }
        }

        //
        // self (bottom)
        //
        int idCmdBS2 = idCmd;

        if (!(_dwStyle & BSIS_NOCONTEXTMENU))
        {
            hmenuMe = LoadMenuPopup_PrivateNoMungeW(MENU_BANDSITE2);
            if (hmenuMe)
            {
                // disable 'Close Band' if it's marked undeleteable
                // nash:17821: don't disable when 0 bands (so user can easily
                // get out of toasted mode)
                if ((idBandActive == (DWORD)-1) || // if mouse not over a band, delete close menu item
                    (!_IsBandDeleteable(idBandActive) ||
                     ISDDCLOSEDISABLED(idBandActive)) ||
                     (_dwStyle & BSIS_LOCKED))
                {
                    DeleteMenu(hmenuMe, BSIDM_CLOSEBAND, MF_BYCOMMAND);
                }

                if (!_fIEAKInstalled)
                {
                    DeleteMenu(hmenuMe, BSIDM_IEAK_DISABLE_DDCLOSE, MF_BYCOMMAND);
                    DeleteMenu(hmenuMe, BSIDM_IEAK_DISABLE_MOVE, MF_BYCOMMAND);
                }
                else
                {
                    DWORD dwAdminSettings = _GetAdminSettings(idBandActive);

                    if (IsFlagSet(dwAdminSettings, BAND_ADMIN_NODDCLOSE))
                        _CheckMenuItem(hmenuMe, BSIDM_IEAK_DISABLE_DDCLOSE, TRUE);

                    if (IsFlagSet(dwAdminSettings, BAND_ADMIN_NOMOVE))
                        _CheckMenuItem(hmenuMe, BSIDM_IEAK_DISABLE_MOVE, TRUE);
                }

                idCmd += Shell_MergeMenus(hmenu, hmenuMe, (UINT) -1, idCmd, 0x7fff, 0) - (idCmd);
                DestroyMenu(hmenuMe);
            }
        }

        //
        // parent
        //
        int idCmdParent = idCmd;
        
        if (_punkSite)
        {
            UINT uFlags = 0;
            ASSERT(_pcm3Parent == NULL);
            if (SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IContextMenu3, &_pcm3Parent))))
            {
                uFlags |= CMF_ICM3;
            }

            hresT = _punkSite->QueryInterface(IID_PPV_ARG(IContextMenu, &pcmParent));
            if (SUCCEEDED(hresT))
            {
                // APPCOMPAT: fix parents and kids to handle...
                // we'd like to pass in -1 but not everyone handles that.
                // workaround: use _FixMenuIndex...
                hresT = pcmParent->QueryContextMenu(hmenu, _FixMenuIndex(hmenu, -1), idCmd, 0x7fff, uFlags);

                ASSERT(SUCCEEDED(hresT));
                idCmd += HRESULT_CODE(hresT);
            }
        }

        //
        // do it
        //
        {
            HWND hwndParent = GetParent(_hwnd);
            if (!hwndParent)
                hwndParent = _hwnd;
            idCmd = TrackPopupMenu(hmenu,
                                   TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
                                   pt.x, pt.y, 0, hwndParent, NULL);
        }

        if (idCmd)
        {
            // must test from smallest to largest
            ASSERT(idCmdBS1 <= idCmdChild);
            ASSERT(idCmdChild <= idCmdBS2);    // o.w. test in wrong order
            ASSERT(idCmdBS2 <= idCmdParent);

            if ((idCmd>= idCmdBS1) && (idCmd < idCmdChild))
            {
                idCmd -= idCmdBS1;
                hres = _OnBSCommand(idCmd, idBandActive, pbid);
            }
            else if ((idCmd>= idCmdBS2) && (idCmd < idCmdParent))
            {
                idCmd -= idCmdBS2;
                hres = _OnBSCommand(idCmd, idBandActive, pbid);
            }
            else
            {
                // A parent or child command
                if (idCmd < idCmdParent)
                {
                    pcm = pcmChild;
                    idCmd -= idCmdChild;
                }
                else
                {
                    pcm = pcmParent;
                    idCmd -= idCmdParent;
                }

                ASSERT(pcm);

                //
                // Call InvokeCommand
                //
                CMINVOKECOMMANDINFOEX ici =
                {
                    SIZEOF(CMINVOKECOMMANDINFOEX),
                    0L,
                    _hwnd,
                    (LPSTR)MAKEINTRESOURCE(idCmd),
                    NULL, NULL,
                    SW_NORMAL,
                };

                hres = pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
            }
        }

        ATOMICRELEASE(_pcm3Parent);

        if (pcmParent)
            pcmParent->Release();
        if (pcmChild)
            pcmChild->Release();
        
        DestroyMenu(hmenu);
    }
    
    return hres;
}


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

         Processes messages passed on from the bar.  Forward
         messages to the bands as appropriate.

*/
HRESULT CBandSite::OnWinEvent(HWND h, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres)
{
    HRESULT hres = E_FAIL;
    HWND hwnd = HWND_BROADCAST;
    
    switch (uMsg)
    {
    case WM_WININICHANGE:
        _UpdateAllBands(FALSE, FALSE);
        goto L_WM_SYSCOLORCHANGE;

    case WM_SYSCOLORCHANGE:
    case WM_PALETTECHANGED:
    L_WM_SYSCOLORCHANGE:
        // propagate to rebar
        if (_hwnd)
            SendMessage(_hwnd, uMsg, wParam, lParam);

        // by not returning here, it will get forwarded to the bands also... 
        break;
        
    case WM_CONTEXTMENU:
        // if it came from the keyboard, wParam is somewhat useless.  it's always out hwnd
        if (IS_WM_CONTEXTMENU_KEYBOARD(lParam))
            hwnd = GetFocus();
        else
            hwnd = (HWND)wParam;
        break;

    case WM_COMMAND:
        hwnd = GET_WM_COMMAND_HWND(wParam, lParam);
        break;
            
    case WM_NOTIFY:
        if (lParam)
            hwnd = ((LPNMHDR)lParam)->hwndFrom;
        break;

    case WM_INITMENUPOPUP:
    case WM_MEASUREITEM:
    case WM_DRAWITEM:
    case WM_MENUCHAR:
        if (_pcm3Parent)
        {
            //
            // If _pcm3Parent, then we've got a context menu up and 
            // an ICM3 client who might care about this message.
            //
            hwnd = _hwnd;
        }
        break;

    default:
        return E_FAIL;
    }
    
    LRESULT lres = 0;
    
    if (hwnd)
    {
        if (_hwnd == hwnd)
        {
            // a message for us
            switch (uMsg)
            {
            case WM_NOTIFY:
                lres = _OnNotify((LPNMHDR)lParam);
                hres = S_OK;
                break;

            case WM_COMMAND:
                switch (GET_WM_COMMAND_CMD(wParam, lParam))
                {
                case IDM_DRAGDROP:
                    _DoDragDrop();
                    break;
                }
                break;

            case WM_INITMENUPOPUP:
            case WM_MEASUREITEM:
            case WM_DRAWITEM:
            case WM_MENUCHAR:
                ASSERT(_pcm3Parent);
                hres = _pcm3Parent->HandleMenuMsg2(uMsg, wParam, lParam, &lres);
                break;
            }
        }
        else
        {
            if (_SendToToolband(hwnd, uMsg, wParam, lParam, &lres))
                hres = S_OK;
        }
    }
    
    
    switch (uMsg)
    {
    case WM_WININICHANGE:
        SendMessage(_hwnd, WM_SIZE, 0, 0);
        break;

    case WM_CONTEXTMENU:
        if (!lres)
            return _OnContextMenu(wParam, lParam);
        break;
    }

    if (plres)
        *plres = lres;
    
    return hres;
}

HRESULT CBandSite_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
{
    CBandSite *pbs = new CBandSite(pUnkOuter);
    if (pbs)
    {
        *ppunk = pbs->_GetInner();
        return S_OK;
    }
    *ppunk = NULL;
    return E_OUTOFMEMORY;
}

//*** CBandSite::IPersistStream*::* {
//

HRESULT CBandSite::GetClassID(CLSID *pClassID)
{
    *pClassID = CLSID_RebarBandSite;
    return S_OK;
}

HRESULT CBandSite::IsDirty(void)
{
    ASSERT(0);
    return S_FALSE; // FEATURE: never be dirty?
}

HRESULT CBandSite::_AddBand(IUnknown* punk)
{
    if (_pbsOuter)
    {
        // Give the outer guy first crack
        return _pbsOuter->AddBand(punk);
    }
    else
    {
        return AddBand(punk);
    }
}

//
// Persisted CBandSite, use types that have fixes sizes
//
struct SBandSite
{
    DWORD   cbSize;
    DWORD   cbVersion;
    DWORD   cBands;
    // ...followed by length-preceded bands
};

#define SBS_WOADMIN_VERSION 3   // Before we added admin settings.
#define SBS_VERSION 8

//***   CBandSite::Load, Save -- 
// DESCRIPTION
//  for each band...
//  Load            Read (i); OLFS(obj)+AddBand; Read (rbbi); RB_SBI
//  Save    RB_GBI; Write(i); OSTS(obj)+nil    ; Write(rbbi)
// NOTES
//  FEATURE: needs error recovery
//  WARNING: we might have done a CreateBand w/o an AddBand; if so our
//  assumption about the rebar bands and the iunknowns being 'parallel'
//  is bogus.

HRESULT CBandSite::Load(IStream *pstm)
{
    HRESULT hres;
    SBandSite sfoo;

    hres = IStream_Read(pstm, &sfoo, SIZEOF(sfoo));     // pstm->Read
    if (hres == S_OK)
    {
        if (!(sfoo.cbSize == SIZEOF(SBandSite) &&
          (sfoo.cbVersion == SBS_VERSION || sfoo.cbVersion == SBS_WOADMIN_VERSION)))
        {
            hres = E_FAIL;
        }

        IBandSiteHelper *pbsh;
        hres = QueryInterface(IID_PPV_ARG(IBandSiteHelper, &pbsh));
        if (EVAL(SUCCEEDED(hres)))
        {
            BOOL_PTR fRedraw = SendMessage(_hwnd, WM_SETREDRAW, FALSE, 0);
            for (DWORD i = 0; i < sfoo.cBands && SUCCEEDED(hres); ++i)
            {
                DWORD j;
                hres = IStream_Read(pstm, &j, SIZEOF(j));   // pstm->Read
                if (hres == S_OK)
                {
                    if (j == i)             // for sanity check
                    {
                        IUnknown* punk;
                        hres = pbsh->LoadFromStreamBS(pstm, IID_PPV_ARG(IUnknown, &punk));
                        if (SUCCEEDED(hres))
                        {
                            hres = _AddBand(punk);
                            if (SUCCEEDED(hres))
                            {
                                hres = _LoadBandInfo(pstm, i, sfoo.cbVersion);
                            }
                            punk->Release();
                        }
                    }
                    else
                    {
                        hres = E_FAIL;
                    }
                }
            }
            SendMessage(_hwnd, WM_SETREDRAW, fRedraw, 0);

            pbsh->Release();
        }

        _UpdateAllBands(FALSE, TRUE);     // force refresh
    }

    return hres;
}

HRESULT CBandSite::Save(IStream *pstm, BOOL fClearDirty)
{
    HRESULT hres;
    SBandSite sfoo;
    
    TraceMsg(DM_PERSIST, "cbs.s enter(this=%x pstm=%x) tell()=%x", this, pstm, DbStreamTell(pstm));

    sfoo.cbSize = SIZEOF(SBandSite);
    sfoo.cbVersion = SBS_VERSION;
    sfoo.cBands = _GetBandItemCount();
    TraceMsg(DM_PERSIST, "cdb.s: cbands=%d", sfoo.cBands);

    hres = pstm->Write(&sfoo, SIZEOF(sfoo), NULL);
    if (SUCCEEDED(hres))
    {
        for (DWORD i = 0; i < sfoo.cBands; i++) 
        {
            // FEATURE: put seek ptr so can resync after bogus streams
            hres = pstm->Write(&i, SIZEOF(i), NULL);    // for sanity check
            if (SUCCEEDED(hres))
            {
                LPBANDITEMDATA pbid = _GetBandItem(i);
                if (EVAL(pbid) && pbid->pdb)
                {
                    IBandSiteHelper *pbsh;
                    hres = QueryInterface(IID_PPV_ARG(IBandSiteHelper, &pbsh));
                    if (SUCCEEDED(hres)) 
                    {
                        hres = pbsh->SaveToStreamBS(SAFECAST(pbid->pdb, IUnknown*), pstm);
                        pbsh->Release();
                    }
                }

                hres = _SaveBandInfo(pstm, i);
                ASSERT(SUCCEEDED(hres));
            }
        }
    }

    TraceMsg(DM_PERSIST, "cbs.s leave tell()=%x", DbStreamTell(pstm));
    return hres;
}

HRESULT CBandSite::GetSizeMax(ULARGE_INTEGER *pcbSize)
{
    static const ULARGE_INTEGER cbMax = { SIZEOF(SBandSite), 0 };
    *pcbSize = cbMax;
    return S_OK;
}

BOOL CBandSite::_IsHeightReasonable(UINT cy)
{
    static UINT s_cyMon = 0;
    if (s_cyMon == 0)
    {
        HMONITOR hmon = MonitorFromWindow(_hwnd, MONITOR_DEFAULTTONEAREST);
        if (hmon)
        {
            RECT rc;
            if (GetMonitorRect(hmon, &rc))
            {
                s_cyMon = RECTHEIGHT(rc);
            }
        }
    }

    return (s_cyMon != 0) ? (cy < 4 * s_cyMon) : TRUE;
}

// returns: IStream::Read() semantics, S_OK means complete read

HRESULT CBandSite::_LoadBandInfo(IStream *pstm, int i, DWORD dwVersion)
{
    PERSISTBANDINFO bi;
    HRESULT hres;
    DWORD dwSize = SIZEOF(bi);
    bi.dwAdminSettings = BAND_ADMIN_NORMAL;     // Assume Normal since it's not specified

    if (SBS_WOADMIN_VERSION == dwVersion)
        dwSize = SIZEOF(PERSISTBANDINFO_V3);

    hres = IStream_Read(pstm, &bi, dwSize);     // pstm->Read
    if (hres == S_OK)
    {
        //
        // Sanity-check the height specified by PERSISTBANDINFO before proceeding.
        // Some people are hitting a stress scenario where a bad height gets
        // persisted out.  If the height is not reasonable, then just discard
        // the sizing values (leaving the defaults in place).
        //
        if (_IsHeightReasonable(bi.cyChild))
        {
            REBARBANDINFO rbbi;

            rbbi.cbSize = SIZEOF(rbbi);
            rbbi.fMask = RBBIM_XPERSIST;
            rbbi.cx = bi.cx;
            rbbi.fStyle = bi.fStyle;
            
            // these things can change from instantiation to instantiation.
            // we want to restore the visual state, not the sizing rules.
            // the sizing rules re retreived each time in getbandinfo
            rbbi.cyMinChild = -1;
            rbbi.cyMaxChild = -1;
            rbbi.cyIntegral = -1;
            rbbi.cxMinChild = -1;

            if (rbbi.fStyle & RBBS_VARIABLEHEIGHT)
            {
                rbbi.cyChild = bi.cyChild;
            }
            else
            {
                rbbi.cyMinChild = bi.cyMinChild;
            }

            SendMessage(_hwnd, RB_SETBANDINFO, i, (LPARAM) &rbbi);
        }

        LPBANDITEMDATA pbid = _GetBandItem(i);
        if (pbid)
        {
            pbid->dwAdminSettings = bi.dwAdminSettings;
            pbid->fNoTitle = bi.fNoTitle;
        }
    }
    return hres;
}

HRESULT CBandSite::_SaveBandInfo(IStream *pstm, int i)
{
    REBARBANDINFO rbbi = {0};
    PERSISTBANDINFO bi = {0};
    LPBANDITEMDATA pbid;

    rbbi.cbSize = SIZEOF(rbbi);
    rbbi.fMask = RBBIM_XPERSIST;
    SendMessage(_hwnd, RB_GETBANDINFO, i, (LPARAM) &rbbi);

    ASSERT((rbbi.fMask & RBBIM_XPERSIST) == RBBIM_XPERSIST);

    bi.cx = rbbi.cx;
    bi.fStyle = rbbi.fStyle;
    bi.cyMinChild = rbbi.cyMinChild;
    bi.cyChild = rbbi.cyChild;

    pbid = _GetBandItem(i);
    if (pbid)
    {
        bi.dwAdminSettings = pbid->dwAdminSettings;
        bi.fNoTitle = pbid->fNoTitle;
    }

    return pstm->Write(&bi, SIZEOF(bi), NULL);
}

void CBandSite::_CacheActiveBand(IUnknown *ptb)
{
    if (ptb == _ptbActive)
        return;

    if (SHIsSameObject(ptb, _ptbActive))
        return;

    ATOMICRELEASE(_ptbActive);

    if (ptb != NULL) 
    {
#ifdef DEBUG
        // better be an IInputObject or else why did you call us?
        IInputObject *pio;
        if (EVAL(SUCCEEDED(ptb->QueryInterface(IID_PPV_ARG(IInputObject, &pio)))))
            pio->Release();

        // overly strict, but in our case it's true...
        IDeskBand *pdb;
        if (EVAL(SUCCEEDED(ptb->QueryInterface(IID_PPV_ARG(IDeskBand, &pdb)))))
            pdb->Release();
#endif
        _ptbActive = ptb;
        _ptbActive->AddRef();
    }

    return;
}

DWORD CBandSite::_BandIDFromPunk(IUnknown* punk)
{
    DWORD dwBandID = -1;
    DWORD dwBandIDTest;
    int cBands = EnumBands(-1, NULL);
    IUnknown* punkTest;

    if (punk)
    {
        for (int i = 0; i < cBands; i++)
        {
            if (SUCCEEDED(EnumBands(i, &dwBandIDTest)))
            {
                if (SUCCEEDED(GetBandObject(dwBandIDTest, IID_PPV_ARG(IUnknown, &punkTest))))
                {
                    BOOL fEq = SHIsSameObject(punk, punkTest);

                    punkTest->Release();

                    if (fEq)
                    {
                        dwBandID = dwBandIDTest;
                        break;
                    }
                }
            }
        }
    }

    return dwBandID;
}

//*** IInputObjectSite methods ***

HRESULT CBandSite::OnFocusChangeIS(IUnknown *punk, BOOL fSetFocus)
{
    if (_ptbActive)
    {
        if (!SHIsSameObject(_ptbActive, punk))
        {
            // Deactivate current band since the current band is 
            // not the caller
            TraceMsg(TF_ACCESSIBILITY, "CBandSite::OnFocusChangeIS (hwnd=0x%08X) deactivate band", _hwnd);
            UIActivateIO(FALSE, NULL);
        }
    }

    if (fSetFocus)
        _CacheActiveBand(punk);

    return IUnknown_OnFocusChangeIS(_punkSite, SAFECAST(this, IInputObject*), fSetFocus);
}


//*** IInputObject methods ***

HRESULT CBandSite::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
{
    HRESULT hres = E_FAIL;

    TraceMsg(TF_ACCESSIBILITY, "CBandSite::UIActivateIO (hwnd=0x%08X) fActivate=%d", _hwnd, fActivate);
    ASSERT(NULL == lpMsg || IS_VALID_WRITE_PTR(lpMsg, MSG));

    if (_ptbActive)
    {
        hres = IUnknown_UIActivateIO(_ptbActive, fActivate, lpMsg);
    }
    else
    {
        hres = OnFocusChangeIS(NULL, fActivate);
    }

    if (fActivate)
    {
        if (!_ptbActive)
        {
            if (IsVK_TABCycler(lpMsg))
                hres = _CycleFocusBS(lpMsg);
            else
                hres = S_OK;
        }
    }
    else
    {
        _CacheActiveBand(NULL);
    }

    return hres;
}

HRESULT CBandSite::HasFocusIO()
{
    // Rebar should never get focus
    // NT #288832 Is one case where (GetFocus() == _hwnd)
    //    which is caused when the "Folder Bar" disappears.
    //    CExplorerBand::ShowDW() calls ShowWindow(hwndTreeView, SW_HIDE)
    //    which by default sets focus to the parent (us).
    //    This is ok because when this function is called,
    //    it will return E_FAIL which the caller will treat
    //    as S_FALSE and give the focus to the next deserving
    //    dude in line.
    return IUnknown_HasFocusIO(_ptbActive);
}

HRESULT CBandSite::TranslateAcceleratorIO(LPMSG lpMsg)
{
    TraceMsg(TF_ACCESSIBILITY, "CBandSite::TranslateAcceleratorIO (hwnd=0x%08X) key=%d", _hwnd, lpMsg->wParam);
    if (IUnknown_TranslateAcceleratorIO(_ptbActive, lpMsg) == S_OK)
    {
        TraceMsg(TF_ACCESSIBILITY, "CBandSite::TranslateAcceleratorIO (hwnd=0x%08X) key=%d; handled by active band", _hwnd, lpMsg->wParam);
        // active band handled it
        return S_OK;
    }
    else if (IsVK_TABCycler(lpMsg))
    {
        TraceMsg(TF_ACCESSIBILITY, "CBandSite::TranslateAcceleratorIO (hwnd=0x%08X) cycle focus", _hwnd);
        // it's a tab; cycle focus
        return _CycleFocusBS(lpMsg);
    }

    return S_FALSE;
}

int CBandSite::_BandIndexFromPunk(IUnknown *punk)
{
    for (int i = 0; i < _GetBandItemCount(); i++)
    {
        LPBANDITEMDATA pbid = _GetBandItem(i);

        if (EVAL(pbid) && SHIsSameObject(pbid->pdb, punk))
            return i;
    }

    return -1;
}

BOOL CBandSite::_IsBandTabstop(LPBANDITEMDATA pbid)
{
    // A band is a tabstop if it is visible and has WS_TABSTOP

    if (pbid->fShow && pbid->hwnd && IsWindowVisible(pbid->hwnd))
    {
        if (WS_TABSTOP & GetWindowStyle(pbid->hwnd))
            return TRUE;
    }

    return FALSE;
}

#define INCDEC(i, fDec)   (fDec ? i - 1 : i + 1)

IUnknown* CBandSite::_GetNextTabstopBand(IUnknown* ptb, BOOL fBackwards)
{
    // Find the first tabstop candidate
    int iBandCount = _GetBandItemCount();
    int iBand = _BandIndexFromPunk(ptb);
    
    if (iBand == -1)
    {
        // Start at the end/beginning
        if (fBackwards)
            iBand = iBandCount - 1;
        else
            iBand = 0;
    }
    else
    {
        // Start one off the current band
        iBand = INCDEC(iBand, fBackwards);
    }

    // Loop til we find a tabstop band or we run off the end
    while (0 <= iBand && iBand < iBandCount)
    {
        LPBANDITEMDATA pbid = _GetBandItem(iBand);
        if (EVAL(pbid))
        {
            if (_IsBandTabstop(pbid))
                return pbid->pdb;
        }

        // Try the next band
        iBand = INCDEC(iBand, fBackwards);
    }

    return NULL;
}

HRESULT CBandSite::_CycleFocusBS(LPMSG lpMsg)
{
    HRESULT hr = S_FALSE;

    IUnknown* ptbSave = NULL;

    if (_ptbActive)
    {
        // Save off the active band in ptbSave
        ptbSave = _ptbActive;
        ptbSave->AddRef();

        // Deactivate active band and clear cache
        IUnknown_UIActivateIO(_ptbActive, FALSE, NULL);
        _CacheActiveBand(NULL);
    }

    if (ptbSave && IsVK_CtlTABCycler(lpMsg))
    {
        // If ctl-tab and a band was active, then reject focus
        ASSERT(hr == S_FALSE);
    }
    else
    {
        BOOL fShift = (GetKeyState(VK_SHIFT) < 0);

        // Loop til we find a tabstop and successfully activate it
        // or til we run out of bands.

        // FEATURE: todo -- call SetFocus if UIActivateIO fails?

        IUnknown* ptbNext = ptbSave;
        while (ptbNext = _GetNextTabstopBand(ptbNext, fShift))
        {
            if (IUnknown_UIActivateIO(ptbNext, TRUE, lpMsg) == S_OK)
            {
                hr = S_OK;
                break;
            }
        }
    }

    ATOMICRELEASE(ptbSave);

    return hr;
}

//*** CBandSite::IBandSiteHelper::* {

// stuff to make it possible to overload the OleLoad/Save stuff so the
// taskbar band does not have to be CoCreat able. kinda a hack...

HRESULT CBandSite::LoadFromStreamBS(IStream *pstm, REFIID riid, void **ppv)
{
    return OleLoadFromStream(pstm, riid, ppv);
}

HRESULT CBandSite::SaveToStreamBS(IUnknown *punk, IStream *pstm)
{
    IPersistStream *ppstm;
    HRESULT hres = punk->QueryInterface(IID_PPV_ARG(IPersistStream, &ppstm));
    if (SUCCEEDED(hres)) 
    {
        hres = OleSaveToStream(ppstm, pstm);
        ppstm->Release();
    }
    return hres;
}

// }


// *** IDropTarget *** {

HRESULT CBandSite::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
    TraceMsg(TF_BANDDD, "CBandSite::DragEnter %d %d", pt.x, pt.y);

    if (!_fDragSource)
    {
        FORMATETC fmte = {g_cfDeskBand, NULL, 0, -1, TYMED_ISTREAM};
        _dwDropEffect = DROPEFFECT_NONE;
        
        if (pdtobj->QueryGetData(&fmte) == S_OK)
        {
            _dwDropEffect = DROPEFFECT_MOVE;
        }
        else
        {

            LPITEMIDLIST pidl;

            if (SUCCEEDED(SHPidlFromDataObject(pdtobj, &pidl, NULL, 0)))
            {
                ASSERT(pidl && IS_VALID_PIDL(pidl));

                DWORD dwAttrib = SFGAO_FOLDER | SFGAO_BROWSABLE;
                IEGetAttributesOf(pidl, &dwAttrib);
                ILFree(pidl);

                DWORD   dwRAction;
        
                if (FAILED(IUnknown_HandleIRestrict(_punkSite, &RID_RDeskBars, RA_DROP, NULL, &dwRAction)))
                    dwRAction = RR_ALLOW;

                if (dwRAction == RR_DISALLOW)
                    _dwDropEffect = DROPEFFECT_NONE;
                else
                {                
                    // if it's not a folder nor a browseable object, we can't host it.
                    if ((dwAttrib & SFGAO_FOLDER) ||
                        (dwAttrib & SFGAO_BROWSABLE) && (grfKeyState & (MK_CONTROL | MK_SHIFT)) == (MK_CONTROL | MK_SHIFT)) 
                        _dwDropEffect = DROPEFFECT_LINK | DROPEFFECT_COPY;

                    _dwDropEffect |= GetPreferedDropEffect(pdtobj);
                }
            }
        }
        *pdwEffect &= _dwDropEffect;
    }
    
    return S_OK;
}

HRESULT CBandSite::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
{
    TraceMsg(TF_BANDDD, "CBandSite::DragOver %d %d", ptl.x, ptl.y);
    if (_fDragSource)
    {
        RECT rc;
        POINT pt;
        pt.x = ptl.x;
        pt.y = ptl.y;
        GetWindowRect(_hwnd, &rc);
        if (PtInRect(&rc, pt))
            SendMessage(_hwnd, RB_DRAGMOVE, 0, (LPARAM)-1);
    }
    else
    {
        *pdwEffect &= _dwDropEffect;
    }
    return S_OK;    
}

HRESULT CBandSite::DragLeave(void)
{
    return S_OK;
}

HRESULT CBandSite::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
    HRESULT hres = E_FAIL;
    
    TraceMsg(TF_BANDDD, "CBandSite::Drop");
    if (_fDragSource)
    {
        SendMessage(_hwnd, RB_ENDDRAG, 0, 0);
        *pdwEffect = DROPEFFECT_NONE;
        hres = S_OK;
    }
    else
    {
        FORMATETC fmte = {g_cfDeskBand, NULL, 0, -1, TYMED_ISTREAM};
        STGMEDIUM stg;
        IUnknown *punk = NULL;
        LPITEMIDLIST pidl;
        
        // if it was an object of our type, create it!
        if ((*pdwEffect & DROPEFFECT_MOVE) &&
            SUCCEEDED(pdtobj->GetData(&fmte, &stg)))
        {

            hres = OleLoadFromStream(stg.pstm, IID_PPV_ARG(IUnknown, &punk));
            if (SUCCEEDED(hres))
            {
                *pdwEffect = DROPEFFECT_MOVE;
            }

            ReleaseStgMedium(&stg);
        } 
        else if ((*pdwEffect & (DROPEFFECT_COPY | DROPEFFECT_LINK)) &&
                 SUCCEEDED(SHPidlFromDataObject(pdtobj, &pidl, NULL, 0)))
        {

            hres = SHCreateBandForPidl(pidl, &punk, (grfKeyState & (MK_CONTROL | MK_SHIFT)) == (MK_CONTROL | MK_SHIFT));
            ILFree(pidl);

            if (SUCCEEDED(hres))
            {
                if (*pdwEffect & DROPEFFECT_LINK)
                    *pdwEffect = DROPEFFECT_LINK;
                else
                    *pdwEffect = DROPEFFECT_COPY;
            }
        }
    
        if (punk)
        {
            hres = _AddBand(punk);

            if (SUCCEEDED(hres))
            {
                DWORD dwState;

                dwState = IDataObject_GetDeskBandState(pdtobj);
                SetBandState(ShortFromResult(hres), BSSF_NOTITLE, dwState & BSSF_NOTITLE);
            }

            punk->Release();
        }
    }
    
    if (FAILED(hres)) 
        *pdwEffect = DROPEFFECT_NONE;
    return hres;
}

// }

//***   ::_MergeBS -- merge two bandsites into one
// ENTRY/EXIT
//  pdtDst  [INOUT] destination DropTarget (always from bandsite)
//  pbsSrc  [INOUT] source bandsite; deleted if all bands moved successfully
//  ret     S_OK if all bands moved; S_FALSE if some moved; E_* o.w.
// NOTES
//  note that if all the bands are moved successfully, pbsSrc will be deleted
//  as a side-effect.
//  pdtDst is assumed to accept multiple drops (bandsite does).
//  pdtDst may be from marshal/unmarshal (tray bandsite).
HRESULT _MergeBS(IDropTarget *pdtDst, IBandSite *pbsSrc)
{
    HRESULT hres = E_FAIL;
    DWORD idBand;

    pbsSrc->AddRef();           // don't go away until we're all done!

    // drag each band in turn
    while (SUCCEEDED(pbsSrc->EnumBands(0, &idBand)))
    {
        // note our (bogus?) assumption that bands which can't be
        // dragged will percolate down to a contiguous range of
        // iBands 0..n.  if that's bogus i'm not sure how we can
        // keep track of where we are.

        IDataObject *pdoSrc;
        hres = pbsSrc->GetBandObject(idBand, IID_PPV_ARG(IDataObject, &pdoSrc));
        if (SUCCEEDED(hres))
        {
            DWORD dwEffect = DROPEFFECT_MOVE | DROPEFFECT_COPY;
            hres = SHSimulateDrop(pdtDst, pdoSrc, 0, NULL, &dwEffect);
            pdoSrc->Release();

            if (SUCCEEDED(hres) && (dwEffect & DROPEFFECT_MOVE))
            {
                hres = pbsSrc->RemoveBand(idBand);
                ASSERT(SUCCEEDED(hres));
            }
        }

        // we failed to move the band, bail

        if (FAILED(hres))
        {
            ASSERT(0);
            break;
        }
    }

    pbsSrc->Release();

    TraceMsg(DM_DRAG, "dba.ms: ret hres=%x", hres);
    return hres;
}


void CBandSite::_BandItemEnumCallback(int dincr, PFNBANDITEMENUMCALLBACK pfnCB, void *pv)
{
    UINT iFirst = 0;

    ASSERT(dincr == 1 || dincr == -1);
    if (dincr < 0)
    {
        iFirst = _GetBandItemCount() - 1;  // start from last
    }

    for (UINT i = iFirst; i < (UINT) _GetBandItemCount(); i += dincr)
    {
        LPBANDITEMDATA pbid = _GetBandItem(i);
        if (pbid && !pfnCB(pbid, pv))
            break;
    }
}

void CBandSite::_DeleteAllBandItems()
{
    for (int i = _GetBandItemCount() - 1; i >= 0; i--)
    {
        LPBANDITEMDATA pbid = _GetBandItem(i);

        // Release the banditem data first, while it can still
        // receive cleanup notifications from its control.  *Then*
        // delete the band item.
        if (pbid)
            _ReleaseBandItemData(pbid, i);

        // REARCHITECT: chrisfra 5/13/97 if you skip deleting, rebar can
        // rearrange on delete, moving a band so that it is never seen
        // and consequently we leak BrandBand and much else
        _DeleteBandItem(i);    // unhook from host (rebar)
    }
}

LPBANDITEMDATA CBandSite::_GetBandItem(int i)
{
    REBARBANDINFO rbbi;
    rbbi.cbSize = SIZEOF(rbbi);
    rbbi.fMask = RBBIM_LPARAM;
    rbbi.lParam = NULL; // in case of failure below

    if (_hwnd)
        SendMessage(_hwnd, RB_GETBANDINFO, i, (LPARAM)&rbbi);
    return (LPBANDITEMDATA)rbbi.lParam;
}

int CBandSite::_GetBandItemCount()
{
    int cel = 0;

    if (_hwnd)
    {
        ASSERT(IS_VALID_HANDLE(_hwnd, WND));

        cel = (int)SendMessage(_hwnd, RB_GETBANDCOUNT, 0, 0);
    }
    return cel;
}

void CBandSite::_GetBandInfo(LPBANDITEMDATA pbid, DESKBANDINFO *pdbi)
{
    pdbi->dwMask = DBIM_MINSIZE | DBIM_MAXSIZE | DBIM_INTEGRAL | DBIM_ACTUAL | DBIM_TITLE | DBIM_MODEFLAGS | DBIM_BKCOLOR;
                 
    pdbi->ptMinSize = pbid->ptMinSize;
    pdbi->ptMaxSize = pbid->ptMaxSize;
    pdbi->ptIntegral = pbid->ptIntegral;
    pdbi->ptActual = pbid->ptActual;
    StrCpyW(pdbi->wszTitle, pbid->szTitle);
    pdbi->dwModeFlags = pbid->dwModeFlags;
    pdbi->crBkgnd = pbid->crBkgnd;
    
    if (pbid->pdb)
    {
        pbid->pdb->GetBandInfo(pbid->dwBandID, _dwMode, pdbi);
    }
    if (pdbi->wszTitle[0] == 0)
    {
        pdbi->dwMask &= ~DBIM_TITLE;
    }

    pbid->ptMinSize = pdbi->ptMinSize;
    pbid->ptMaxSize = pdbi->ptMaxSize;
    pbid->ptIntegral = pdbi->ptIntegral;
    pbid->ptActual = pdbi->ptActual;
    StrCpyW(pbid->szTitle, pdbi->wszTitle);
    pbid->dwModeFlags = pdbi->dwModeFlags;
    pbid->crBkgnd = pdbi->crBkgnd;

    if (!(pdbi->dwMask & DBIM_TITLE))   // title not supported
        pbid->fNoTitle = TRUE;

    ASSERT(pdbi->dwModeFlags & DBIMF_VARIABLEHEIGHT ? pbid->ptIntegral.y : TRUE);
}

void CBandSite::_BandInfoFromBandItem(REBARBANDINFO *prbbi, LPBANDITEMDATA pbid, BOOL fBSOnly)
{
    // REVIEW: could be optimized more
    DESKBANDINFO dbi;

    if (!fBSOnly)
        _GetBandInfo(/*INOUT*/ pbid, &dbi);

    // now add the view as a band in the rebar
    // add links band
    prbbi->fMask = RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_ID | RBBIM_IDEALSIZE | RBBIM_TEXT;
    if (fBSOnly)
        prbbi->fMask = RBBIM_STYLE|RBBIM_TEXT;

    // clear the bits the are band settable
    prbbi->fStyle |= RBBS_FIXEDBMP;
    prbbi->fStyle &= ~(RBBS_NOGRIPPER | RBBS_GRIPPERALWAYS | RBBS_VARIABLEHEIGHT | RBBS_USECHEVRON);

    if (_dwStyle & BSIS_NOGRIPPER)    
        prbbi->fStyle |= RBBS_NOGRIPPER;
    else if (_dwStyle & BSIS_ALWAYSGRIPPER)    
        prbbi->fStyle |= RBBS_GRIPPERALWAYS;
    else
    {
        // BSIS_AUTOGRIPPER...
        if (!(prbbi->fStyle & RBBS_FIXEDSIZE) &&
            !(_dwMode & DBIF_VIEWMODE_FLOATING))
            prbbi->fStyle |= RBBS_GRIPPERALWAYS;
    }

    if (pbid->dwModeFlags & DBIMF_VARIABLEHEIGHT) 
        prbbi->fStyle |= RBBS_VARIABLEHEIGHT;

    if (pbid->dwModeFlags & DBIMF_USECHEVRON)
        prbbi->fStyle |= RBBS_USECHEVRON;

    if (pbid->dwModeFlags & DBIMF_BREAK)
        prbbi->fStyle |= RBBS_BREAK;

    if (pbid->dwModeFlags & DBIMF_TOPALIGN)
        prbbi->fStyle |= RBBS_TOPALIGN;

    if (!fBSOnly)
    {
        prbbi->hwndChild = pbid->hwnd;
        prbbi->wID = pbid->dwBandID;

        // set up the geometries
        prbbi->cxMinChild = pbid->ptMinSize.x;
        prbbi->cyMinChild = pbid->ptMinSize.y;
        prbbi->cyMaxChild = pbid->ptMaxSize.y;
        prbbi->cyIntegral = pbid->ptIntegral.y;

        if (_dwMode & (DBIF_VIEWMODE_FLOATING | DBIF_VIEWMODE_VERTICAL)) 
        {
            // after we're up, it's the "ideal" point
            prbbi->cxIdeal = pbid->ptActual.y;
        } 
        else 
        {
            // after we're up, it's the "ideal" point
            prbbi->cxIdeal = pbid->ptActual.x;
        }

        if (prbbi->cxIdeal == (UINT)-1)
            prbbi->cxIdeal = 0;

        if (pbid->dwModeFlags & DBIMF_BKCOLOR)
        {
            if (dbi.dwMask & DBIM_BKCOLOR)
            {
                prbbi->fMask |= RBBIM_COLORS;
                prbbi->clrFore = CLR_DEFAULT;
                prbbi->clrBack = dbi.crBkgnd;
            }
        }
        ASSERT(pbid->fNoTitle || (dbi.dwMask & DBIM_TITLE));    // pbid in sync?
    }

    SHUnicodeToTChar(pbid->szTitle, prbbi->lpText, prbbi->cch);
    if (!pbid->fNoTitle && _IsEnableTitle(pbid) && !(_dwStyle & BSIS_NOCAPTION))
    {
        prbbi->fStyle &= ~RBBS_HIDETITLE;
    }
    else
    {
        // No text please
        prbbi->fStyle |= RBBS_HIDETITLE;
    }
        

    // Make this band a tabstop.  Itbar will override v_SetTabstop
    // since for the browser we don't want every band to be a tabstop.
    v_SetTabstop(prbbi);
}

void CBandSite::v_SetTabstop(LPREBARBANDINFO prbbi)
{
    // We specify that a band should be a tabstop by setting the WS_TABSTOP
    // bit.  Never make RBBS_FIXEDSIZE bands (i.e., the brand) tabstops.
    if (prbbi && prbbi->hwndChild && !(prbbi->fStyle & RBBS_FIXEDSIZE))
        SHSetWindowBits(prbbi->hwndChild, GWL_STYLE, WS_TABSTOP, WS_TABSTOP);
}

//***   cbs::_IsEnableTitle -- should we enable (ungray) title
// DESCRIPTION
//  used for handing back title and for enabling menu
// NOTES
//  pbid unused...
//
#ifndef UNIX
_inline
#endif
BOOL CBandSite::_IsEnableTitle(LPBANDITEMDATA pbid)
{
    ASSERT(pbid);
    return (/*pbid && !pbid->fNoTitle &&*/
      !((_dwMode & DBIF_VIEWMODE_FLOATING) && _GetBandItemCount() <= 1));
}

BOOL CBandSite::_UpdateBandInfo(LPBANDITEMDATA pbid, BOOL fBSOnly)
{
    REBARBANDINFO rbbi = {SIZEOF(rbbi)};
    int iRB = _BandIDToIndex(pbid->dwBandID);

    // now update the info
    rbbi.fMask = RBBIM_ID | RBBIM_CHILDSIZE | RBBIM_SIZE | RBBIM_STYLE;
    if (fBSOnly)
        rbbi.fMask = RBBIM_STYLE;

    SendMessage(_hwnd, RB_GETBANDINFO, iRB, (LPARAM)&rbbi);

    if (!fBSOnly)
    {
        if (_dwMode & (DBIF_VIEWMODE_FLOATING | DBIF_VIEWMODE_VERTICAL)) 
        {
            pbid->ptActual.x = rbbi.cyChild;
            pbid->ptActual.y = rbbi.cxIdeal;
        } 
        else 
        {
            pbid->ptActual.x = rbbi.cxIdeal;
            pbid->ptActual.y = rbbi.cyChild;
        }
        pbid->ptMinSize.x = rbbi.cxMinChild;
        pbid->ptMinSize.y = rbbi.cyMinChild;
        pbid->ptMaxSize.y = rbbi.cyMaxChild;
    }

    TCHAR szBand[40];
    rbbi.lpText = szBand;
    rbbi.cch = ARRAYSIZE(szBand);

    _BandInfoFromBandItem(&rbbi, pbid, fBSOnly);
    
    return BOOLFROMPTR(SendMessage(_hwnd, RB_SETBANDINFO, (UINT)iRB, (LPARAM)&rbbi));
}

BOOL CBandSite::_AddBandItem(LPBANDITEMDATA pbid)
{
    REBARBANDINFO rbbi = {SIZEOF(rbbi)};

    pbid->ptActual.x = -1;
    pbid->ptActual.y = -1;

    TCHAR szBand[40];
    rbbi.lpText = szBand;
    rbbi.cch = ARRAYSIZE(szBand);

    _BandInfoFromBandItem(&rbbi, pbid, FALSE);

    rbbi.cyChild = pbid->ptActual.y;
    rbbi.fMask |= RBBIM_LPARAM;
    rbbi.lParam = (LPARAM)pbid;

    ASSERT(rbbi.fMask & RBBIM_ID);

    return BOOLFROMPTR(SendMessage(_hwnd, RB_INSERTBAND, (UINT) (pbid->dwModeFlags & DBIMF_ADDTOFRONT) ? 0 : -1, (LPARAM)&rbbi));
}

void CBandSite::_DeleteBandItem(int i)
{
    SendMessage(_hwnd, RB_DELETEBAND, i, 0);
}

DWORD CBandSite::_IndexToBandID(int i)
{
    REBARBANDINFO rbbi = {SIZEOF(rbbi)};
    rbbi.fMask = RBBIM_ID;

    if (SendMessage(_hwnd, RB_GETBANDINFO, i, (LPARAM)&rbbi))
        return rbbi.wID;
    else
        return -1;
}


/*----------------------------------------------------------
Purpose: Given the band ID, returns the internal band index.

*/
int CBandSite::_BandIDToIndex(DWORD dwBandID)
{
    int nRet = -1;

    if (_hwnd)
        nRet = (int)SendMessage(_hwnd, RB_IDTOINDEX, (WPARAM) dwBandID, (LPARAM) 0);
    return nRet;
}


/*----------------------------------------------------------
Purpose: The Parent Site may want to override what the admin
         specified.

Return Values:
    S_OK: Do lock band.
    S_FALSE: Do NOT Lock band.

*/
HRESULT CBandSite::_IsRestricted(DWORD dwBandID, DWORD dwRestrictAction, DWORD dwBandFlags)
{
    HRESULT hr;
    DWORD dwRestrictionAction;

    hr = IUnknown_HandleIRestrict(_punkSite, &RID_RDeskBars, dwRestrictAction, NULL, &dwRestrictionAction);
    if (RR_NOCHANGE == dwRestrictionAction)    // If our parent didn't handle it, we will.
        dwRestrictionAction = IsFlagSet(_GetAdminSettings(dwBandID), dwBandFlags) ? RR_DISALLOW : RR_ALLOW;

    if (RR_DISALLOW == dwRestrictionAction)
        hr = S_OK;
    else
        hr = S_FALSE;

    ASSERT(SUCCEEDED(hr));  // FAIL(hr) other than hr == E_NOTIMPLE; is not good.
    return hr;
}

BOOL ConfirmRemoveBand(HWND hwnd, UINT uID, LPCTSTR pszName)
{
    TCHAR szTemp[1024], *pszTemp2, *pszStr, szTitle[80];
    BOOL bRet = TRUE;
    DWORD dwLen;


    MLLoadString(IDS_CONFIRMCLOSETITLE, szTitle, ARRAYSIZE(szTitle));

    // Calling FormatMessage with FORMAT_MESSAGE_FROM_HMODULE fails
    MLLoadString(uID, szTemp, ARRAYSIZE(szTemp));

    dwLen = (lstrlen(szTemp) + lstrlen(pszName) + 1) * sizeof(TCHAR);
    if((pszTemp2 = (TCHAR *)LocalAlloc(LPTR, dwLen)) != NULL)
    {
        _FormatMessage(szTemp, pszTemp2, dwLen, pszName);

        MLLoadString(IDS_CONFIRMCLOSETEXT, szTemp, ARRAYSIZE(szTemp));

        dwLen = (lstrlen(szTemp) + lstrlen(pszTemp2) + 1) * sizeof(TCHAR);
        if((pszStr = (TCHAR *)LocalAlloc(LPTR, dwLen)) != NULL)
        {
            _FormatMessage(szTemp, pszStr, dwLen, pszTemp2);

            bRet = (IDOK == SHMessageBoxCheck(hwnd, pszStr, szTitle, MB_OKCANCEL, IDOK, TEXT("WarnBeforeCloseBand")));

            LocalFree(pszStr);
        }

        LocalFree(pszTemp2);
    }

    return bRet;
}
