#include "priv.h"
#include "sccls.h"
#include "bands.h"
#include "util.h"
#include "resource.h"
#include "dhuihand.h"
#include "droptgt.h"
#include "iface.h"
#include "isfband.h"
#include "itbdrop.h"
#include "browband.h"
#include <varutil.h>
#include "legacy.h"
#include "mnbandid.h"

#include "mluisupp.h"

#include "inetsmgr.h"
#ifdef UNIX
#include "unixstuff.h"
#endif

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

//***   CBrowserBand {
//

////////////////
///  BrowserOC band

CBrowserBand::CBrowserBand() :
    CToolBand()
{
    _fBlockSIDProxy = TRUE;
    _dwModeFlags = DBIMF_FIXEDBMP | DBIMF_VARIABLEHEIGHT;
    _sizeMin.cx = _sizeMin.cy = 0;
    _sizeMax.cx = _sizeMax.cy = 32000;
    _fCustomTitle = FALSE;
    return;
}

CBrowserBand::~CBrowserBand()
{
    if (_pidl)
        ILFree(_pidl);

}

HRESULT CBrowserBand::QueryInterface(REFIID riid, void **ppvObj)
{
    static const QITAB qit[] = {
        QITABENT(CBrowserBand, IContextMenu),          // IID_IContextMenu
        QITABENT(CBrowserBand, IWinEventHandler),      // IID_IWinEventHandler
        QITABENT(CBrowserBand, IDispatch),             // IID_IDispatch
        QITABENT(CBrowserBand, IPersistPropertyBag),   // IID_IPersistPropertyBag
        QITABENT(CBrowserBand, IBrowserBand),
        { 0 },
    };

    HRESULT hres = QISearch(this, qit, riid, ppvObj);

    if (FAILED(hres))
        hres = CToolBand::QueryInterface(riid, ppvObj);

    return hres;
}

HRESULT CBrowserBand_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
{
    // aggregation checking is handled in class factory

    CBrowserBand * p = new CBrowserBand();
    if (p) 
    {
        *ppunk = SAFECAST(p, IDeskBand *);
        return S_OK;
    }

    return E_OUTOFMEMORY;
}

HRESULT SHCreateBandForPidl(LPCITEMIDLIST pidl, IUnknown** ppunk, BOOL fAllowBrowserBand)
{
    IDeskBand *ptb = NULL;
    BOOL fBrowserBand;    
    DWORD dwAttrib = SFGAO_FOLDER | SFGAO_BROWSABLE;
    
    // if it's on the file system, we still might want to create a browser
    // band if it's a docobj (including .htm file)
    IEGetAttributesOf(pidl, &dwAttrib);    
    switch (dwAttrib & (SFGAO_FOLDER | SFGAO_BROWSABLE))
    {  
    case (SFGAO_FOLDER | SFGAO_BROWSABLE):
        TraceMsg(TF_WARNING, "SHCreateBandForPidl() Find out what the caller wants.  Last time we checked, nobody would set this - what does the caller want?");
    case SFGAO_BROWSABLE:
        fBrowserBand = TRUE;
        break;

    case SFGAO_FOLDER:
        fBrowserBand = FALSE;
        break;
        
    default:
        // if it's not a folder nor a browseable object, we can't host it.
        // Happens when use drags a text file and we want to turn off the
        // drop to create a band.
        return E_FAIL;

    }
    
    // this was a drag of a link or folder
    if (fBrowserBand)
    {
        if (fAllowBrowserBand)
        {
            // create browser to show web sites                        
            ptb = CBrowserBand_Create(pidl);
        }
    }
    else
    {
        // create an ISF band to show folders as hotlinks
        CISFBand_CreateEx(NULL, pidl, IID_PPV_ARG(IDeskBand, &ptb));
    }

    *ppunk = ptb;

    if (ptb)
        return S_OK;

    return E_OUTOFMEMORY;

}


HRESULT CBrowserBand::CloseDW(DWORD dw)
{
    _Connect(FALSE);
    
    return CToolBand::CloseDW(dw);
}

void CBrowserBand::_Connect(BOOL fConnect)
{
    ConnectToConnectionPoint(SAFECAST(this, IDeskBand*), DIID_DWebBrowserEvents2, fConnect, 
                             _pauto, &_dwcpCookie, NULL);
}

/////////////////////////////////////////////////////////////////////////////
// IDispatch::Invoke
/////////////////////////////////////////////////////////////////////////////
HRESULT CBrowserBand::Invoke
(
    DISPID          dispidMember,
    REFIID          riid,
    LCID            lcid,
    WORD            wFlags,
    DISPPARAMS *    pdispparams,
    VARIANT *       pvarResult,
    EXCEPINFO *     pexcepinfo,
    UINT *          puArgErr
)
{
    ASSERT(pdispparams);
    if(!pdispparams)
        return E_INVALIDARG;

    //
    // NOTES: If we have a custom title, we don't need to process this call.
    //  This code assumes DISPID_TITLECHANGE is the only id we support.
    //  If somebody add any other, move this check below. 
    //  
    if (_fCustomTitle)
        return (S_OK);

    switch (dispidMember)
    {
    case DISPID_TITLECHANGE:
    {
        int iArg = pdispparams->cArgs -1;
        if (iArg == 0 &&
            (pdispparams->rgvarg[iArg].vt == VT_BSTR)) {

            BSTR pTitle = pdispparams->rgvarg[iArg].bstrVal;
            StrCpyNW(_wszTitle, pTitle, ARRAYSIZE(_wszTitle));
            _BandInfoChanged();
        }
        break;
    }
    }

    return S_OK;
}


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

    if (_fBlockSIDProxy && IsEqualGUID(guidService, SID_SProxyBrowser)) {
        return E_FAIL;
    } 
    else if (IsEqualGUID(guidService, SID_STopFrameBrowser)) {
        // block this so SearchBand doesn't end up in global history
        return E_FAIL;
    }
    else if (_fBlockDrop && IsEqualGUID(guidService, SID_SDropBlocker))
    {
        return QueryInterface(riid, ppvObj);
    }

    return CToolBand::QueryService(guidService, riid, ppvObj);
}


HRESULT CBrowserBand::SetSite(IUnknown* punkSite)
{
    
    CToolBand::SetSite(punkSite);

    if (punkSite != NULL) {
        
        if (!_hwnd)
            _CreateOCHost();
    } else {

        ATOMICRELEASE(_pauto);
        ATOMICRELEASE(_poipao);
    }

    return S_OK;
}

//***   CBrowserBand::IInputObject::* {

HRESULT CBrowserBand::TranslateAcceleratorIO(LPMSG lpMsg)
{
#ifdef DEBUG
    if (lpMsg && lpMsg->message == WM_KEYDOWN && lpMsg->wParam == VK_F12) {
        // temp debug test code
        _DebugTestCode();
    }
#endif

    if (_poipao)
        return _poipao->TranslateAccelerator(lpMsg);

    return S_FALSE;
}

HRESULT CBrowserBand::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
{
    int iVerb = fActivate ? OLEIVERB_UIACTIVATE : OLEIVERB_INPLACEACTIVATE;

    HRESULT hr = OCHost_DoVerb(_hwnd, iVerb, lpMsg);

    // OCHost UIActivate is different than IInputObject::UIActivateIO.  It
    // doesn't do anything with the lpMsg parameter.  So, we need to pass
    // it to them via TranslateAccelerator.  Since the only case we care
    // about is when they're getting tabbed into (we want them to highlight
    // the first/last link), just do this in the case of a tab.  However,
    // don't give it to them if it's a ctl-tab.  The rule is that you shouldn't
    // handle ctl-tab when UI-active (ctl-tab switches between contexts), and
    // since Trident is always UI-active (for perf?), they'll always reject
    // ctl-tab.

    if (IsVK_TABCycler(lpMsg) && !IsVK_CtlTABCycler(lpMsg) && _poipao)
        hr = _poipao->TranslateAccelerator(lpMsg);

    return hr;
}

// }

//***   CBrowserBand::IOleCommandTarget::* {

HRESULT CBrowserBand::QueryStatus(const GUID *pguidCmdGroup,
    ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
{
    return MayQSForward(_pauto, OCTD_DOWN, pguidCmdGroup, cCmds, rgCmds, pcmdtext);
}

HRESULT CBrowserBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
    //  These are broadcast messages to the TRIDENT doc for GUID CGID_MSTHML
    if (pguidCmdGroup && IsEqualGUID(CGID_ExplorerBarDoc, *pguidCmdGroup))
    {
        if (_pauto)
        {
            LPTARGETFRAME2 ptgf;

            if (SUCCEEDED(_pauto->QueryInterface(IID_PPV_ARG(ITargetFrame2, &ptgf))))
            {
                LPOLECONTAINER pocDoc;
                if (SUCCEEDED(ptgf->GetFramesContainer(&pocDoc)) && pocDoc)
                {
                    IUnknown_Exec(pocDoc, &CGID_MSHTML, nCmdID, nCmdexecopt, 
                                    pvarargIn, pvarargOut);
                    pocDoc->Release();
                }
                ptgf->Release();
            }
        }
        return S_OK;
    }
    else
    {
        return MayExecForward(_pauto, OCTD_DOWN, pguidCmdGroup, nCmdID, nCmdexecopt,
            pvarargIn, pvarargOut);
    }
}

// }

HRESULT CBrowserBand::GetBandInfo(DWORD dwBandID, DWORD fViewMode, 
                                DESKBANDINFO* pdbi) 
{

    _dwBandID = dwBandID;

    // nt5:192868 make sure can't size to smaller than title/scrollbars
    // n.b. virt pdbi->pt.x,y is really phys y,x (i.e. phys long,short)
    pdbi->ptMinSize.x = _sizeMin.cx;
    pdbi->ptMinSize.y = max(16, _sizeMin.cy);   // FEATURE: 16 is bogus
#ifdef DEBUG
    if (pdbi->ptMinSize.x != 0 || pdbi->ptMinSize.y != 0)
        TraceMsg(DM_TRACE, "cbb.gbi: ptMinSize.(x,y)=%x,%x", pdbi->ptMinSize.x, pdbi->ptMinSize.y);
#endif
    pdbi->ptMaxSize.x = _sizeMax.cx;
    pdbi->ptMaxSize.y = _sizeMax.cy;
    pdbi->dwModeFlags = _dwModeFlags;

    pdbi->ptActual.y = -1;
    pdbi->ptActual.x = -1;
    pdbi->ptIntegral.y = 1;
    
    if (_wszTitle[0]) {
        StrCpyNW(pdbi->wszTitle, _wszTitle, ARRAYSIZE(pdbi->wszTitle));
    } else if ( _fCustomTitle) {
        pdbi->dwMask &= ~DBIM_TITLE;
    }    
    else{
        SHGetNameAndFlagsW(_pidl, SHGDN_NORMAL, pdbi->wszTitle, SIZECHARS(pdbi->wszTitle), NULL);
    }
    
    return S_OK;
} 


void CBrowserBand::_InitBrowser(void)
{
    ASSERT(IsWindow(_hwnd));

    OCHost_QueryInterface(_hwnd, IID_PPV_ARG(IWebBrowser2, &_pauto));
    OCHost_SetOwner(_hwnd, SAFECAST(this, IContextMenu*));

    if (EVAL(_pauto))
    {
        LPTARGETFRAME2 ptgf;

        if (SUCCEEDED(_pauto->QueryInterface(IID_PPV_ARG(ITargetFrame2, &ptgf))))
        {
            DWORD dwOptions;

            if (SUCCEEDED(ptgf->GetFrameOptions(&dwOptions)))
            {
                dwOptions |= FRAMEOPTIONS_BROWSERBAND | FRAMEOPTIONS_SCROLL_AUTO;
                ptgf->SetFrameOptions(dwOptions);
            }
            ptgf->Release();
        }

        _pauto->put_RegisterAsDropTarget(VARIANT_FALSE);

        // BUG do OCHost_QI
        // note only 1 active object (proxy)
        _pauto->QueryInterface(IID_PPV_ARG(IOleInPlaceActiveObject, &_poipao));
        ASSERT(_poipao != NULL);
        
        // set up the connection point
        _Connect(TRUE);
    }
}

HRESULT CBrowserBand::_NavigateOC()
{
    HRESULT hres = E_FAIL;
    if (_hwnd)
    {
        ASSERT(IsWindow(_hwnd));
        if (!_pidl) {
            if (_pauto) {
                hres = _pauto->GoHome();
            }
        } else {
            IServiceProvider* psp = NULL;

            OCHost_QueryInterface(_hwnd, IID_PPV_ARG(IServiceProvider, &psp));
            if (psp)
            {
                IShellBrowser* psb;
                if (EVAL(SUCCEEDED(psp->QueryService(SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &psb)))))
                {
                    hres = psb->BrowseObject(_pidl, SBSP_SAMEBROWSER);
                    psb->Release();
                }
                psp->Release();
            }

        }
    }

    return hres;
}


HRESULT CBrowserBand::_CreateOCHost()
{
    HRESULT hres = E_FAIL; // assume error

    // Register the OCHost window class
    SHDRC shdrc = {sizeof(SHDRC), SHDRCF_OCHOST};
    shdrc.cbSize = sizeof (SHDRC);
    shdrc.dwFlags |= SHDRCF_OCHOST;
    if (DllRegisterWindowClasses(&shdrc))
    {
        // Create an OCHost window
        _hwnd = CreateWindow(OCHOST_CLASS, NULL,
            WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_TABSTOP,
            0, 0, 1, 1,
            _hwndParent, NULL, HINST_THISDLL, NULL);

        if (_hwnd)
        {
            OCHINITSTRUCT ocs;
            ocs.cbSize = SIZEOF(OCHINITSTRUCT);   
            ocs.clsidOC  = CLSID_WebBrowser;
            ocs.punkOwner = SAFECAST(this, IDeskBand*);

            hres = OCHost_InitOC(_hwnd, (LPARAM)&ocs);        

            _InitBrowser();
            _NavigateOC();
            OCHost_DoVerb(_hwnd, OLEIVERB_INPLACEACTIVATE, FALSE);
        }
    }
    return hres;
}

//***   CBrowserBand::IWinEventHandler::* {

HRESULT CBrowserBand::OnWinEvent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres)
{
    switch (uMsg) {
    case WM_NOTIFY:
        _OnNotify((LPNMHDR)lParam);
        return S_OK;
        
    default:
        break;
    }

    return E_FAIL;
}

HRESULT CBrowserBand::IsWindowOwner(HWND hwnd)
{
    HRESULT hres;

    hres = SHIsChildOrSelf(_hwnd, hwnd);
    ASSERT(hwnd != NULL || hres == S_FALSE);
    ASSERT(_hwnd != NULL || hres == S_FALSE);
    return hres;
}

#if 0
static void HackFocus(HWND hwndFrom)
{
    TraceMsg(DM_FOCUS, "HackFocus: GetFocus()=%x hwndOCHost=%x", GetFocus(), hwndFrom);
    hwndFrom = GetWindow(hwndFrom, GW_CHILD);   // OCHost->shembed
    TraceMsg(DM_FOCUS, "HackFocus: hwndShEmbed=%x", hwndFrom);
    hwndFrom = GetWindow(hwndFrom, GW_CHILD);   // shembed->shdocvw
    TraceMsg(DM_FOCUS, "HackFocus: hwndShDocVw=%x", hwndFrom);
    hwndFrom = GetWindow(hwndFrom, GW_CHILD);   // shdocvw->iesvr
    TraceMsg(DM_FOCUS, "HackFocus: hwndIESvr=%x", hwndFrom);
    if (hwndFrom != 0) {
        TraceMsg(DM_FOCUS, "HackFocus: SetFocus(%x)", hwndFrom);
        SetFocus(hwndFrom);
    }
    return;
}
#endif

LRESULT CBrowserBand::_OnNotify(LPNMHDR pnm)
{
    switch (pnm->code)
    {
    case OCN_ONUIACTIVATE:  // UIActivate
        ASSERT(SHIsSameObject(((LPOCNONUIACTIVATEMSG)pnm)->punk, _poipao));
        
        // n.b. we pass up 'this' not pnm->punk, since we always want to
        // be the intermediary (e.g. for UIActivateIO calls to us)

        IUnknown_OnFocusChangeIS(_punkSite, SAFECAST(this, IInputObject*), TRUE);
        return OCNONUIACTIVATE_HANDLED;

    case OCN_ONSETSTATUSTEXT:
        {
            HRESULT hr = E_FAIL;
            IShellBrowser *psb;

            hr = QueryService(SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb));
            if (SUCCEEDED(hr)) {
                hr = psb->SetStatusTextSB(((LPOCNONSETSTATUSTEXTMSG)pnm)->pwszStatusText);
                psb->Release();
            }
        }
        break;

    case OCN_ONPOSRECTCHANGE:
        {
            LPCRECT lprcPosRect = ((LPOCNONPOSRECTCHANGEMSG)pnm)->prcPosRect;
            _sizeMin.cx = lprcPosRect->right - lprcPosRect->left;
            _sizeMin.cy = lprcPosRect->bottom - lprcPosRect->top;

            _BandInfoChanged();

            break;
        }

    default:
        break;
    }

    ASSERT(OCNONUIACTIVATE_HANDLED != 0);
    return 0;
}

// }

//***   CBrowserBand::IPersistStream::* {

HRESULT CBrowserBand::GetClassID(CLSID *pClassID)
{
    *pClassID = CLSID_BrowserBand;

    return S_OK;
}


// mask flags for BrowserBand persistence
//
#define BB_ILSTREAM   0x00000001
#define BB_PIDLASLINK 0x00000002

// FEATURE: REVIEW: it seems to me like we should let the WebBrowserOC
// persist it's location, not us...
//
HRESULT CBrowserBand::Load(IStream *pstm)
{
    HRESULT hres;
    DWORD dw;
    
    if (_pidl) {
        ILFree(_pidl);
    }

    hres = pstm->Read(&dw, SIZEOF(DWORD), NULL);
    if (SUCCEEDED(hres))
    {
        if (dw & BB_PIDLASLINK)
        {
            hres = LoadPidlAsLink(_punkSite, pstm, &_pidl);
        }
        else if (dw & BB_ILSTREAM) // for backwards compat
        {
            hres = ILLoadFromStream(pstm, &_pidl);
        }
    }
        
    if (SUCCEEDED(hres))
        _NavigateOC();
    
    return hres;
}

HRESULT CBrowserBand::Save(IStream *pstm, BOOL fClearDirty)
{
    HRESULT hres;
    DWORD dw = 0;
    BSTR bstrUrl = NULL;

    if (_pauto && SUCCEEDED(_pauto->get_LocationURL(&bstrUrl)) && bstrUrl) {
        TraceMsg(DM_PERSIST, "cbb.s: current/new url=%s", bstrUrl);
        if (_pidl) {
            ILFree(_pidl);
            _pidl = NULL;       // paranoia
        }
        IECreateFromPath(bstrUrl, &_pidl);
        SysFreeString(bstrUrl);
    }

    if (_pidl)
        dw |= BB_PIDLASLINK;

    hres = pstm->Write(&dw, SIZEOF(DWORD), NULL);

    if (SUCCEEDED(hres) && (dw & BB_PIDLASLINK))
        hres = SavePidlAsLink(_punkSite, pstm, _pidl);
    
    return hres;
}

// }

//***   CBrowserBand::IPersistPropertyBag::* {

HRESULT CBrowserBand::Load(IPropertyBag *pPBag, IErrorLog *pErrLog)
{
    HRESULT hres;
    TCHAR szUrl[MAX_URL_STRING];

    TraceMsg(DM_TRACE, "cbb.l(bag): enter");

    if (_pidl) {
        ILFree(_pidl);
    }

    hres = SHPropertyBag_ReadStr(pPBag, L"Url", szUrl, ARRAYSIZE(szUrl));
    if (SUCCEEDED(hres)) {
        TCHAR * pszFinalUrl;
        TCHAR   szPlug[MAX_PATH];
        TCHAR   szMuiPath[MAX_PATH];

        pszFinalUrl = szUrl;

        hres = SHPropertyBag_ReadStr(pPBag, L"Pluggable", szPlug, ARRAYSIZE(szPlug));

        if (SUCCEEDED(hres) && !StrCmpNI(TEXT("yes"), szPlug, ARRAYSIZE(szPlug)))
        {
            TCHAR * pszFile;

            // if this is loading html out of the windows\web folder
            // then we need to call SHGetWebFolderFilePath in order
            // to support pluggable UI

            pszFile = PathFindFileName(szUrl);
            hres = SHGetWebFolderFilePath(pszFile, szMuiPath, ARRAYSIZE(szMuiPath));
            if (SUCCEEDED(hres))
            {
                pszFinalUrl = szMuiPath;
            }
        }

        hres = IECreateFromPath(pszFinalUrl, &_pidl);
        if (SUCCEEDED(hres)) {
            _NavigateOC();
        }
    }
    
    return hres;
}

// }

//***   CBrowserBand::IContextMenu::* {

HRESULT CBrowserBand::QueryContextMenu(HMENU hmenu,
    UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
{
    int i = 0;
    HMENU hmenuMe = LoadMenuPopup_PrivateNoMungeW(MENU_BROWBAND);

    i += Shell_MergeMenus(hmenu, hmenuMe, indexMenu, idCmdFirst + i, idCmdLast, MM_ADDSEPARATOR) - (idCmdFirst + i);
    DestroyMenu(hmenuMe);

    // aka (S_OK|i)
    return MAKE_HRESULT(ERROR_SUCCESS, FACILITY_NULL, i);
}

HRESULT CBrowserBand::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
{
    TraceMsg(DM_MENU, "cbb.ic");

    HRESULT hres;
    int idCmd = -1;

    // FEATURE: todo: id -= _idCmdFirst ???

    if (!HIWORD(pici->lpVerb))
        idCmd = LOWORD(pici->lpVerb);

    //
    // Low memory paranoia safety check
    //
    if (!_pauto) {
        TraceMsg(DM_ERROR, "CBrowserBand::InvokeCommand: _pauto IS NULL");
        return E_OUTOFMEMORY;
    }

    switch (idCmd) {
    case IDM_BROWBAND_REFRESH:
#ifdef DEBUG
        if (GetKeyState(VK_SHIFT) < 0)
            hres = _pauto->GoHome();
        else
#endif
        hres = _pauto->Refresh();
        break;
    case IDM_BROWBAND_OPENNEW:   // clone window into 'real' browser
        {
        BSTR bstrURL = NULL;

        // n.b. this clones the *current page* into a 'real' browser,
        // not the link.

        // FEATURE: todo: we'd really rather get and navigate to
        // a PIDL, but that isn't supported yet in ie4.
        hres = _pauto->get_LocationURL(&bstrURL);
        if (SUCCEEDED(hres)) {
            VARIANT varFlags;

            VariantInit(&varFlags);
            varFlags.vt = VT_I4;
            varFlags.lVal = (navOpenInNewWindow|navNoHistory);

            // n.b. we drop the post data etc. on the floor, oh well...
            hres = _pauto->Navigate(bstrURL, /*flags*/&varFlags, /*targ*/NULL, /*post*/NULL, /*hdrs*/NULL);

            VariantClear(&varFlags);
        }

        if (bstrURL)
            SysFreeString(bstrURL);

        ASSERT(SUCCEEDED(hres));

        break;
        }
    default:
        TraceMsg(DM_ERROR, "cbb::ic cmd=%d not handled", idCmd);
        break;
    }

    return S_OK;
}

// }

SIZE CBrowserBand::_GetCurrentSize()
{
    SIZE size;

    RECT rc;
    GetWindowRect(_hwnd, &rc);

    size.cx = RECTWIDTH(rc);
    size.cy = RECTHEIGHT(rc);

    return size;
}

// *** IBrowserBand methods ***
HRESULT CBrowserBand::GetObjectBB(REFIID riid, LPVOID *ppv)
{
    return _pauto ? _pauto->QueryInterface(riid, ppv) : E_UNEXPECTED;
}

#ifdef DEBUG
void CBrowserBand::_DebugTestCode()
{
    DWORD dwMask = 0x10000000;  // non-NULL bogus mask

    BROWSERBANDINFO bbi;
    bbi.cbSize = SIZEOF(BROWSERBANDINFO);

    GetBrowserBandInfo(dwMask, &bbi);
}
#endif // DEBUG

void CBrowserBand::_MakeSizesConsistent(LPSIZE psizeCur)
{
    // _sizeMin overrules _sizeMax

    if (_dwModeFlags & DBIMF_FIXED) {
        // if they specified a current size, use that instead
        // of min size
        if (psizeCur)
            _sizeMin = *psizeCur;
        _sizeMax = _sizeMin;
    } else {
        _sizeMax.cx = max(_sizeMin.cx, _sizeMax.cx);
        _sizeMax.cy = max(_sizeMin.cy, _sizeMax.cy);

        if (psizeCur) {
            psizeCur->cx = max(_sizeMin.cx, psizeCur->cx);
            psizeCur->cy = max(_sizeMin.cy, psizeCur->cy);

            psizeCur->cx = min(_sizeMax.cx, psizeCur->cx);
            psizeCur->cy = min(_sizeMax.cy, psizeCur->cy);
        }
    }
}

HRESULT CBrowserBand::SetBrowserBandInfo(DWORD dwMask, PBROWSERBANDINFO pbbi)
{
    if (!pbbi || pbbi->cbSize != SIZEOF(BROWSERBANDINFO))
        return E_INVALIDARG;

    if (!dwMask || (dwMask & BBIM_MODEFLAGS))
        _dwModeFlags = pbbi->dwModeFlags;

    if (!dwMask || (dwMask & BBIM_TITLE)) {
        if (pbbi->bstrTitle) {
            _fCustomTitle = TRUE;
            // Change the internal _wszTitle used by Browser band
            StrCpyNW(_wszTitle, pbbi->bstrTitle, ARRAYSIZE(_wszTitle));
        } else {
            _fCustomTitle = FALSE;
        }
    }

    if (!dwMask || (dwMask & BBIM_SIZEMIN))
        _sizeMin = pbbi->sizeMin;

    if (!dwMask || (dwMask & BBIM_SIZEMAX))
        _sizeMax = pbbi->sizeMax;

    if (!dwMask || (dwMask & BBIM_SIZECUR)) {
        SIZE sizeCur = pbbi->sizeCur;
        _MakeSizesConsistent(&sizeCur);

        // HACKHACK: the only way to tell bandsite to change the height of a horizontal
        // band is to give it a new min/max height pair at the desired height.  the same
        // holds for setting the width of a vertical band.  so we temporarily give bandsite
        // new min/max size info, then restore old min/max.

        SIZE sizeMinOld = _sizeMin;
        SIZE sizeMaxOld = _sizeMax;
        _sizeMin = _sizeMax = sizeCur;

        _BandInfoChanged();

        _sizeMin = sizeMinOld;
        _sizeMax = sizeMaxOld;
    } else {
        _MakeSizesConsistent(NULL);
    }

    _BandInfoChanged();

    return S_OK;
}

// we don't have a client to test BBIM_TITLE, so leave it unimplemented for now.
#define BBIM_INVALIDFLAGS (~(BBIM_SIZEMIN | BBIM_SIZEMAX | BBIM_SIZECUR | BBIM_MODEFLAGS))

HRESULT CBrowserBand::GetBrowserBandInfo(DWORD dwMask, PBROWSERBANDINFO pbbi)
{
    if (!pbbi || pbbi->cbSize != SIZEOF(BROWSERBANDINFO))
        return E_INVALIDARG;

    if (dwMask & BBIM_INVALIDFLAGS)
        return E_INVALIDARG;

    pbbi->dwModeFlags = _dwModeFlags;
    pbbi->sizeMin = _sizeMin;
    pbbi->sizeMax = _sizeMax;
    pbbi->sizeCur =_GetCurrentSize();

    return S_OK;
}

IDeskBand* CBrowserBand_Create(LPCITEMIDLIST pidl)
{
    CBrowserBand *p = new CBrowserBand();
    if(p) {
        if (pidl)
            p->_pidl = ILClone(pidl);
    }
    return p;
}

// }

class CSearchSecurityMgrImpl : public CInternetSecurityMgrImpl 
{
    // *** IID_IInternetSecurityManager ***
    
    virtual STDMETHODIMP ProcessUrlAction(LPCWSTR pwszUrl, DWORD dwAction, BYTE *pPolicy, DWORD cbPolicy,
                                  BYTE *pContext, DWORD cbContext, DWORD dwFlags, DWORD dwReserved)
    {
        HRESULT hres = INET_E_DEFAULT_ACTION;

        switch (dwAction)
        {
            case URLACTION_ACTIVEX_RUN:
            case URLACTION_SCRIPT_RUN:
            case URLACTION_SCRIPT_SAFE_ACTIVEX:
            case URLACTION_HTML_SUBMIT_FORMS:
                if (_IsSafeUrl(pwszUrl))
                {
                    if (cbPolicy >= SIZEOF(DWORD))
                    {
                        *(DWORD *)pPolicy = URLPOLICY_ALLOW;
                        hres = S_OK;
                    }
                    else
                    {
                        hres = S_FALSE;
                    }
                }
                break;
        }
        
        return hres;
    }
};

class CCustomizeSearchHelper : public CInternetSecurityMgrImpl,
                               public IServiceProvider
{
public:

    CCustomizeSearchHelper() : _cRef(1) { }
    
    // *** IUnknown ***
    virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
    virtual STDMETHODIMP_(ULONG) AddRef(void);
    virtual STDMETHODIMP_(ULONG) Release(void);

    // *** IID_IInternetSecurityManager ***
    virtual STDMETHODIMP ProcessUrlAction(LPCWSTR pwszUrl, DWORD dwAction, BYTE *pPolicy, DWORD cbPolicy,
                                  BYTE *pContext, DWORD cbContext, DWORD dwFlags, DWORD dwReserved);

    // *** IServiceProvider ***
    virtual STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppvObject);

    virtual BOOL _IsSafeUrl(LPCWSTR pwszUrl) { return TRUE; }

private:
    ~CCustomizeSearchHelper() {};
    
    ULONG   _cRef;
};

STDMETHODIMP_(ULONG) CCustomizeSearchHelper::AddRef()
{
    return ++_cRef;
}

STDMETHODIMP_(ULONG) CCustomizeSearchHelper::Release(void)
{
    if( 0L != --_cRef )
        return _cRef;

    delete this;
    return 0L;
}

HRESULT CCustomizeSearchHelper::QueryInterface(REFIID riid, void **ppvObj)
{
    static const QITAB qit[] = {
        QITABENT(CCustomizeSearchHelper, IServiceProvider),
        QITABENT(CCustomizeSearchHelper, IInternetSecurityManager),
        { 0 },
    };

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

HRESULT CCustomizeSearchHelper::ProcessUrlAction(LPCWSTR pwszUrl, DWORD dwAction, BYTE *pPolicy, 
                                                 DWORD cbPolicy, BYTE *pContext, DWORD cbContext, 
                                                 DWORD dwFlags, DWORD dwReserved)
{
   HRESULT hres = INET_E_DEFAULT_ACTION;

    switch (dwAction)
    {
        case URLACTION_ACTIVEX_RUN:
        case URLACTION_SCRIPT_RUN:
        case URLACTION_SCRIPT_SAFE_ACTIVEX:
        case URLACTION_HTML_SUBMIT_FORMS:
            if (cbPolicy >= SIZEOF(DWORD))
            {
                *(DWORD *)pPolicy = URLPOLICY_ALLOW;
                hres = S_OK;
            }
            else
            {
                hres = S_FALSE;
            }
            break;
    }
    
    return hres;
}

STDMETHODIMP CCustomizeSearchHelper::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
{
    if (IID_IInternetSecurityManager == guidService)
    {
        return QueryInterface(riid, ppvObject);
    }
    else
    {
        *ppvObject = NULL;
        return E_NOINTERFACE;
    }
}


//***   CSearchBand {
//

////////////////
///  Search (BrowserOC) band

//  If you change this, change shdocvw also.
const WCHAR c_wszThisBandIsYourBand[] = L"$$SearchBand$$";

#define SEARCH_MENUID_OFFSET    100

class CSearchBand : public CBrowserBand, 
                    public IBandNavigate,
                    public ISearchBandTBHelper,
                    public CSearchSecurityMgrImpl
{
public:
    // *** IUnknown ***
    virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
    virtual STDMETHODIMP_(ULONG) AddRef(void);
    virtual STDMETHODIMP_(ULONG) Release(void);

    // *** IDeskBand methods ***
    virtual STDMETHODIMP GetBandInfo(DWORD dwBandID, DWORD fViewMode, 
                                   DESKBANDINFO* pdbi);

    // *** IPersistStream methods ***
    // (others use base class implementation) 
    virtual STDMETHODIMP GetClassID(CLSID *pClassID);
    virtual STDMETHODIMP Load(IStream *pStm);
    virtual STDMETHODIMP Save(IStream *pStm, BOOL fClearDirty);

    // *** IBandNavigate ***
    virtual STDMETHODIMP Select(LPCITEMIDLIST pidl);

    // *** IOleCommandTarget methods ***
    virtual STDMETHODIMP Exec(const GUID *pguidCmdGroup,
        DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut);

    // *** IDockingWindow methods ***
    virtual STDMETHODIMP ShowDW(BOOL fShow);

    // *** ISearchBandTBHelper methods ***
    virtual STDMETHODIMP AddNextMenuItem(LPCWSTR pwszText, int idItem);
    virtual STDMETHODIMP ResetNextMenu();
    virtual STDMETHODIMP SetOCCallback(IOleCommandTarget *pOleCmdTarget);
    virtual STDMETHODIMP NavigateToPidl(LPCITEMIDLIST pidl);

    // *** IServiceProvider methods ***
    virtual STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, LPVOID* ppvObj);

    // *** IWinEventHandler ***
    virtual STDMETHODIMP OnWinEvent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres);

protected:
    CSearchBand();
    virtual ~CSearchBand();

    virtual void _Connect(BOOL fConnect);
    virtual void _InitBrowser(void);
    virtual HRESULT _NavigateOC();
    
    void _AddButtons(BOOL fAdd);
    void _OnNextButtonSelect(int x, int y);
    void _OnNew();
    void _DoNext(int newPos);
    void _OnNextButtonClick();
    void _OnCustomize();
    void _OnHelp();
    void _NavigateToUrl(LPCTSTR pszUrl);
    void _EnsureImageListsLoaded();
    void _EnableNext(BOOL bEnable);
    void _NavigateToSearchUrl();

    virtual BOOL _IsSafeUrl(LPCWSTR pwszUrl);
    
    BOOL _fStrsAdded;
    LONG_PTR _lStrOffset;

    IOleCommandTarget *_pOCCmdTarget;

    HIMAGELIST  _himlNormal;
    HIMAGELIST  _himlHot;

    friend HRESULT CSearchBand_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi);       // for ctor
    friend IDeskBand* CSearchBand_Create();

    HMENU _hmenuNext;
    HWND _hwndParent;
    int _nextPos;

    BOOL _bNewUrl; // set to true when we are QS'd for IInternetSecurityMgr, i.e. when pane is renavigated
    BOOL _bUseDefault; // true if we should not use our security mgr
    WCHAR _wszCache[MAX_URL_STRING];
    DWORD _nCmpLength;
    BOOL  _bIsCacheSafe;
};

CSearchBand::CSearchBand() :
    CBrowserBand()
{
    _fBlockSIDProxy = FALSE;
    _fBlockDrop = TRUE;
    _bNewUrl    = TRUE;
    ASSERT(_wszCache[0] == TEXT('\0'));
    ASSERT(_nCmpLength == 0);  
    ASSERT(_bIsCacheSafe == FALSE);
}

CSearchBand::~CSearchBand()
{
    ResetNextMenu();

    if (NULL != _himlNormal)
    {
        ImageList_Destroy(_himlNormal);
    }
    
    if (NULL != _himlHot)  
    {
        ImageList_Destroy(_himlHot);
    }

    ATOMICRELEASE(_pOCCmdTarget);
}

void CSearchBand::_NavigateToUrl(LPCTSTR pszUrl)
{
    if (NULL != _pidl)
    {
        ILFree(_pidl);
    }

    IECreateFromPath(pszUrl, &_pidl);
    _NavigateOC();
}

void CSearchBand::_NavigateToSearchUrl()
{
    TCHAR szUrl[INTERNET_MAX_URL_LENGTH];
    BOOL bFound;
    BOOL bWebSearch = FALSE;
    IBrowserService2 *pbs;
        
    if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IBrowserService2, &pbs))))
    {
        ITEMIDLIST *pidl;

        if (SUCCEEDED(pbs->GetPidl(&pidl)))
        {
            // FEATURE: This code should be using IShellFolder2::GetDefaultSearchGUID() and
            //     keying off SRCID_SWebSearch (vs. SRCID_SFileSearch/SRCID_SFindComputer/SRCID_SFindPrinter)
            bWebSearch = ILIsWeb(pidl);
            ILFree(pidl);
        }
        pbs->Release();
    }

    ResetNextMenu();

    if (bWebSearch)
    {
        bFound = GetDefaultInternetSearchUrl(szUrl, ARRAYSIZE(szUrl), TRUE);
    }
    else
    {
        bFound = GetSearchAssistantUrl(szUrl, ARRAYSIZE(szUrl), TRUE, FALSE);
    }

    if (bFound)
    {
        _NavigateToUrl(szUrl);
    }
}

void CSearchBand::_OnNew()
{
    VARIANT var;
    var.vt = VT_BOOL;
    var.boolVal = VARIANT_FALSE;

    if (NULL != _pOCCmdTarget)
    {
        HRESULT hr = _pOCCmdTarget->Exec(NULL, SBID_SEARCH_NEW, 0, NULL, &var);
        
        if (FAILED(hr))
        {
            var.boolVal = VARIANT_FALSE;
        }
    }

    if ((var.vt != VT_BOOL) || (!var.boolVal))
    {
        _NavigateToSearchUrl();
    }
}

void CSearchBand::_OnNextButtonSelect(int x, int y)
{
    HWND hwnd;

    if (SUCCEEDED(IUnknown_GetWindow(_punkSite, &hwnd)))
    {
        int idItem = TrackPopupMenu(_hmenuNext, TPM_RETURNCMD, x, y, 0, hwnd, NULL);

        if (0 != idItem)
        {
            _DoNext(GetMenuPosFromID(_hmenuNext, idItem));
        }        
    }
}

void CSearchBand::_DoNext(int newPos)
{
    if (NULL != _pOCCmdTarget)
    {
        CheckMenuItem(_hmenuNext, _nextPos, MF_BYPOSITION | MF_UNCHECKED);

        _nextPos = newPos;

        CheckMenuItem(_hmenuNext, _nextPos, MF_BYPOSITION | MF_CHECKED);

        VARIANT var;
        
        var.vt = VT_I4;
        var.lVal = GetMenuItemID(_hmenuNext, _nextPos) - SEARCH_MENUID_OFFSET;
        
        HRESULT hr = _pOCCmdTarget->Exec(NULL, SBID_SEARCH_NEXT, 0, &var, NULL);
        
        ASSERT(SUCCEEDED(hr));
    }
}

void CSearchBand::_OnNextButtonClick()
{
    int newPos = _nextPos + 1;
    
    if (newPos >= GetMenuItemCount(_hmenuNext))
    {
        newPos = 0;
    }

    _DoNext(newPos);
}

void CSearchBand::_OnCustomize()
{
    TCHAR szUrl[INTERNET_MAX_URL_LENGTH];
    HWND hwnd;

    IUnknown_GetWindow(_punkSite, &hwnd);

    if (GetSearchAssistantUrl(szUrl, ARRAYSIZE(szUrl), TRUE, TRUE))
    {
        if (InternetGoOnline(szUrl, hwnd, 0))
        {
            IMoniker *pmk;

            if (SUCCEEDED(CreateURLMoniker(NULL, szUrl, &pmk)))
            {
                IHostDialogHelper *pTridentAPI;
                
                if (SUCCEEDED(CoCreateInstance(CLSID_HostDialogHelper, NULL, CLSCTX_INPROC_SERVER,
                                               IID_PPV_ARG(IHostDialogHelper, &pTridentAPI))))
                {
                    IUnknown *punkCustHelper = NULL;

                    if (_IsSafeUrl(szUrl))
                    {
                        punkCustHelper = (IUnknown *)(IServiceProvider *)new CCustomizeSearchHelper;
                    }

                    pTridentAPI->ShowHTMLDialog(hwnd, pmk, NULL, L"help:no;resizable:1", NULL, punkCustHelper);

                    if (NULL != punkCustHelper)
                    {
                        punkCustHelper->Release();
                    }

                    pTridentAPI->Release();
                }

                pmk->Release();
            }
        }
    }
}

void CSearchBand::_OnHelp()
{
    HWND hwnd;

    IUnknown_GetWindow(_punkSite, &hwnd);

#ifndef UNIX
    SHHtmlHelpOnDemandWrap(hwnd, TEXT("iexplore.chm > iedefault"), 0, (DWORD_PTR) TEXT("srchasst.htm"), ML_CROSSCODEPAGE);
#else
    {
        IServiceProvider* psp = NULL;

        OCHost_QueryInterface(_hwnd, IID_PPV_ARG(IServiceProvider, &psp));
        if (psp)
        {
            IShellBrowser* psb;
            if (EVAL(SUCCEEDED(psp->QueryService(SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &psb)))))
            {
                UnixHelp(L"Search Help", psb);
                psb->Release();
            }
            psp->Release();
        }
    }
#endif
}

HRESULT CSearchBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
    if (pguidCmdGroup && IsEqualGUID(CGID_SearchBand, *pguidCmdGroup))
    {
        switch (nCmdID)
        {
        case SBID_SEARCH_NEW:
            _OnNew();
            return S_OK;

        case SBID_SEARCH_NEXT:
            if (nCmdexecopt == OLECMDEXECOPT_PROMPTUSER)
            {
                if ((NULL != pvarargIn) && (pvarargIn->vt == VT_I4))
                {
                    ASSERT(NULL != _hmenuNext);
                    _OnNextButtonSelect(GET_X_LPARAM(pvarargIn->lVal), GET_Y_LPARAM(pvarargIn->lVal));
                }
            }
            else
            {
                _OnNextButtonClick();
            }
            return S_OK;

        case SBID_SEARCH_CUSTOMIZE:
            _OnCustomize();
            return S_OK;

        case SBID_SEARCH_HELP:
            _OnHelp();
            return S_OK;

        case SBID_HASPIDL:
            // Simply return whether or not the band has a pidl
            if (_pidl)
                return S_OK;
            else
                return S_FALSE;
            
        case SBID_GETPIDL:
            {
                HRESULT hres = E_INVALIDARG;
                
                if (pvarargOut)
                {
                    hres = E_OUTOFMEMORY;
                    VariantInit(pvarargOut); // zero init it
                    if (!_pidl || SUCCEEDED(InitVariantFromIDList(pvarargOut, _pidl)))
                        hres = S_OK;
                }
                return hres;
            }
        }
    }
    return CBrowserBand::Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
}

#define INDEX_NEXT          1
#define INDEX_CUSTOMIZE     3
static const TBBUTTON c_tbSearch[] =
{
    {  0,           SBID_SEARCH_NEW,       TBSTATE_ENABLED,   BTNS_AUTOSIZE | BTNS_SHOWTEXT,                 {0, 0}, 0, 0},
    {  1,           SBID_SEARCH_NEXT,      0,                 BTNS_AUTOSIZE | BTNS_DROPDOWN | BTNS_SHOWTEXT, {0, 0}, 0, 1},
    {  0,           0,                     TBSTATE_ENABLED,   BTNS_SEP,                                      {0, 0}, 0, 0},
    {  I_IMAGENONE, SBID_SEARCH_CUSTOMIZE, TBSTATE_ENABLED,   BTNS_AUTOSIZE | BTNS_SHOWTEXT,                 {0, 0}, 0, 2}
};

void CSearchBand::_EnableNext(BOOL bEnable)
{
    IExplorerToolbar* piet;

    if (SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IExplorerToolbar, &piet))))
    {
        UINT state;

        if (SUCCEEDED(piet->GetState(&CGID_SearchBand, SBID_SEARCH_NEXT, &state)))
        {
            if (bEnable)
            {
                state |= TBSTATE_ENABLED;
            }
            else
            {
                state &= ~TBSTATE_ENABLED;
            }
            piet->SetState(&CGID_SearchBand, SBID_SEARCH_NEXT, state);
        }

        piet->Release();
    }
}

void CSearchBand::_AddButtons(BOOL fAdd)
{
    IExplorerToolbar* piet;

    if (SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IExplorerToolbar, &piet))))
    {
        if (fAdd)
        {
            piet->SetCommandTarget((IUnknown*)SAFECAST(this, IOleCommandTarget*), &CGID_SearchBand, 0);

            if (!_fStrsAdded)
            {
                LONG_PTR   cbOffset;
                piet->AddString(&CGID_SearchBand, MLGetHinst(), IDS_SEARCH_BAR_LABELS, &cbOffset);
                _lStrOffset = cbOffset;
                _fStrsAdded = TRUE;
            }

            _EnsureImageListsLoaded();
            piet->SetImageList(&CGID_SearchBand, _himlNormal, _himlHot, NULL);

            TBBUTTON tbSearch[ARRAYSIZE(c_tbSearch)];
            UpdateButtonArray(tbSearch, c_tbSearch, ARRAYSIZE(c_tbSearch), _lStrOffset);

            if (SHRestricted2(REST_NoSearchCustomization, NULL, 0))
            {
                tbSearch[INDEX_CUSTOMIZE].fsState &= ~TBSTATE_ENABLED;
            }

            if (NULL != _hmenuNext)
            {
                tbSearch[INDEX_NEXT].fsState |= TBSTATE_ENABLED;
            }

            piet->AddButtons(&CGID_SearchBand, ARRAYSIZE(tbSearch), tbSearch);
        }
        else
            piet->SetCommandTarget(NULL, NULL, 0);

        piet->Release();
    }
}

void CSearchBand::_EnsureImageListsLoaded()
{
    if (_himlNormal == NULL)
    {
        _himlNormal = ImageList_LoadImage(HINST_THISDLL, 
                                          MAKEINTRESOURCE(IDB_SEARCHBANDDEF), 
                                          18, 
                                          0, 
                                          RGB(255, 0, 255),
                                          IMAGE_BITMAP, 
                                          LR_CREATEDIBSECTION);
    }

    if (_himlHot == NULL)
    {
        _himlHot = ImageList_LoadImage(HINST_THISDLL, 
                                       MAKEINTRESOURCE(IDB_SEARCHBANDHOT), 
                                       18, 
                                       0, 
                                       RGB(255, 0, 255),
                                       IMAGE_BITMAP, 
                                       LR_CREATEDIBSECTION);
    }
}

HRESULT CSearchBand::AddNextMenuItem(LPCWSTR pwszText, int idItem)
{
    if (NULL == _hmenuNext)
    {
        _hmenuNext = CreatePopupMenu();
    }

    ASSERT(NULL != _hmenuNext);

    if (NULL != _hmenuNext)
    {

#ifdef DEBUG
        //  Check to see if an item with this ID has already been added
        MENUITEMINFO dbgMii = { sizeof(dbgMii) };
        dbgMii.fMask = MIIM_STATE;
        if (GetMenuItemInfo(_hmenuNext, idItem + SEARCH_MENUID_OFFSET, FALSE, &dbgMii))
        {
            TraceMsg(DM_ERROR, "Adding duplicate menu item in CSearchBand::AddNextMenuItem");
        }
#endif

        int nItems = GetMenuItemCount(_hmenuNext);
        
        MENUITEMINFOW mii = { sizeof(mii) };

        mii.fMask = MIIM_ID | MIIM_TYPE;
        mii.wID = (WORD)idItem + SEARCH_MENUID_OFFSET;
        mii.fType = MFT_RADIOCHECK | MFT_STRING;
        mii.dwTypeData = (LPWSTR)pwszText;
        mii.cch = lstrlenW(pwszText);

        BOOL result = InsertMenuItemW(_hmenuNext, nItems, TRUE, &mii);

        if (result)
        {
            if (0 == nItems)
            {
                CheckMenuItem(_hmenuNext, 0, MF_BYPOSITION | MF_CHECKED);
                _EnableNext(TRUE);
            }
        }
    }
    
    return S_OK;
}

HRESULT CSearchBand::ResetNextMenu()
{
    if (NULL != _hmenuNext)
    {
        _nextPos = 0;
        _EnableNext(FALSE);
        DestroyMenu(_hmenuNext);
        _hmenuNext = NULL;
    }
    return S_OK;
}

HRESULT CSearchBand::SetOCCallback(IOleCommandTarget *pOleCmdTarget)
{
    ResetNextMenu();

    ATOMICRELEASE(_pOCCmdTarget);

    _pOCCmdTarget = pOleCmdTarget;

    if (NULL != _pOCCmdTarget)
    {
        _pOCCmdTarget->AddRef();
    }
    
    return S_OK;
}

//
// CSearchBand::NavigateToPidl
//
// Implements ISearchBandTBHelper::NavigateToPidl
//
// This is *almost* the same as CSearchBand::Select, except that it always navigates, whereas
// CSearchBand::Select will skip navigation if the search band is already displaying that pidl.
// We need that function to retain that behavior so that the search pane retains its results when
// opened.
//
HRESULT CSearchBand::NavigateToPidl(LPCITEMIDLIST pidl)
{
    ILFree(_pidl);
    _pidl = ILClone(pidl);
    return _NavigateOC();
}

HRESULT CSearchBand::ShowDW(BOOL fShow)
{
#ifdef UNIX
    // We overide this method in order to provide renavigation if our earlier navigation
    // was cancelled for some reason.

    if (fShow)
    {
         // Should be there.
         // Just being sure we are able to navigate.

         ASSERT(IsWindow(_hwnd));
         ASSERT( _pidl );

         if (_pauto)
         {
             BSTR bstrURL = NULL;
             TCHAR szLocation[MAX_URL_STRING];

             // SHUnicodeToTChar does this - no memset 
             // memset(szLocation, 0, SIZEOF(szLocation));

             if( SUCCEEDED(_pauto->get_LocationURL(&bstrURL)) && bstrURL )
             {
                 // Fire navigation again if the navigation was canceled earlier for
                 // some reason (Maybe the proxy was not set).

                 SHUnicodeToTChar(bstrURL, szLocation, ARRAYSIZE(szLocation));
                 if( !StrCmpI( szLocation, NAVCANCELLED_URL ) ||
                     !StrCmpI( szLocation, OFFLINEINFO_URL  ) )
                     _NavigateOC();

                 SysFreeString(bstrURL);
             }

         }
    }
#endif

    HRESULT hres = CBrowserBand::ShowDW(fShow);
    _AddButtons(fShow);
    return hres;
}

HRESULT CSearchBand_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
{
    *ppunk = NULL;

    // aggregation checking is handled in class factory
    HRESULT hr = CreateFromRegKey(REGSTR_PATH_EXPLORER, TEXT("WebFindBandHook"), IID_PPV_ARG(IUnknown, ppunk));
    if (FAILED(hr))
    {
        CSearchBand *p = new CSearchBand();
        if (p)
        {
            *ppunk = SAFECAST(p, IDeskBand*);
            hr = S_OK;
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }
    }
    return hr;
}

IDeskBand* CSearchBand_Create()
{
    IDeskBand* pistb = NULL;

    IUnknown *punk;
    HRESULT hr = CSearchBand_CreateInstance(NULL, &punk, NULL);
    if (SUCCEEDED(hr))
    {
        punk->QueryInterface(IID_PPV_ARG(IDeskBand, &pistb));
        ASSERT(pistb);
        punk->Release();
    }
    return pistb;
}

HRESULT CSearchBand::QueryInterface(REFIID riid, void **ppvObj)
{
    static const QITAB qit[] = {
        QITABENT(CSearchBand, IContextMenu),
        QITABENT(CSearchBand, IBandNavigate),
        QITABENT(CSearchBand, ISearchBandTBHelper),
        QITABENT(CSearchBand, IServiceProvider),
        QITABENT(CSearchBand, IInternetSecurityManager),
        { 0 },
    };

    HRESULT hres = QISearch(this, qit, riid, ppvObj);

    if (FAILED(hres))
        hres = CBrowserBand::QueryInterface(riid, ppvObj);

    return hres;
}

ULONG CSearchBand::AddRef()
{
    return CBrowserBand::AddRef();
}

ULONG CSearchBand::Release()
{
    return CBrowserBand::Release();
}

void CSearchBand::_Connect(BOOL fConnect)
{
    CBrowserBand::_Connect(fConnect);

    //  Now we need to expose ourselves so the control in the search assistant
    //  can talk to us.

    if (_pauto) 
    {
        IWebBrowserApp *pWebBrowserApp;
        HRESULT hr = _pauto->QueryInterface(IID_PPV_ARG(IWebBrowserApp, &pWebBrowserApp));

        if (SUCCEEDED(hr))
        {
            ASSERT(NULL != pWebBrowserApp);
            
            BSTR bstrProp = SysAllocString(c_wszThisBandIsYourBand);
    
            if (NULL != bstrProp)
            {
                VARIANT varThis;
    
                if (fConnect)
                {
                    varThis.vt = VT_UNKNOWN;
                    varThis.punkVal = (IBandNavigate *)this;
                }
                else
                {
                    varThis.vt = VT_EMPTY;
                }           
    
                pWebBrowserApp->PutProperty(bstrProp, varThis);
                
                SysFreeString(bstrProp);
            }
    
            pWebBrowserApp->Release();
        }
    }
}

void CSearchBand::_InitBrowser(void)
{
    CBrowserBand::_InitBrowser();
}

HRESULT CSearchBand::_NavigateOC()
{
    HRESULT hres = E_FAIL;

    if (_pidl) // don't want search pane to be navigated to home.
        return CBrowserBand::_NavigateOC();

    return hres;
}

HRESULT CSearchBand::GetBandInfo(DWORD dwBandID, DWORD fViewMode, 
                                DESKBANDINFO* pdbi) 
{
    _dwBandID = dwBandID;
    pdbi->dwModeFlags = DBIMF_FIXEDBMP | DBIMF_VARIABLEHEIGHT;
    
    pdbi->ptMinSize.x = 16;
    pdbi->ptMinSize.y = 0;
    pdbi->ptMaxSize.x = 32000; // random
    pdbi->ptMaxSize.y = 32000; // random
    pdbi->ptActual.y = -1;
    pdbi->ptActual.x = -1;
    pdbi->ptIntegral.y = 1;

    MLLoadStringW(IDS_BAND_SEARCH, pdbi->wszTitle, ARRAYSIZE(pdbi->wszTitle));
    
    return S_OK;
} 

//***   CSearchBand::IPersistStream::* {

HRESULT CSearchBand::GetClassID(CLSID *pClassID)
{
    *pClassID = CLSID_SearchBand;

    return S_OK;
}

HRESULT CSearchBand::Load(IStream *pstm)
{
    _NavigateOC();
    
    return S_OK;
}

HRESULT CSearchBand::Save(IStream *pstm, BOOL fClearDirty)
{
    return S_OK;
}

HRESULT CSearchBand::Select(LPCITEMIDLIST pidl)
{
    HRESULT hres = S_OK;
    IServiceProvider *psp = NULL;
    LPITEMIDLIST pidlTemp = NULL;

    OCHost_QueryInterface(_hwnd, IID_PPV_ARG(IServiceProvider, &psp));
    if (psp)
    {
        IBrowserService * pbs;
        if (EVAL(SUCCEEDED(psp->QueryService(SID_SShellBrowser, IID_PPV_ARG(IBrowserService, &pbs)))))
        {
            pbs->GetPidl(&pidlTemp);
            pbs->Release();
        }
        psp->Release();
    }

    if ((!pidlTemp) || (!ILIsEqual(pidlTemp, pidl)))
    {
        ILFree(_pidl);
        _pidl = ILClone(pidl);
        hres = _NavigateOC();
    }
    ILFree(pidlTemp);
    return hres;
}

STDMETHODIMP CSearchBand::QueryService(REFGUID guidService, REFIID riid, LPVOID* ppvObj)
{
    HRESULT hres;
    
    if (IsEqualGUID(guidService, SID_SInternetSecurityManager))
    {
        _bNewUrl = TRUE;
        hres = QueryInterface(riid, ppvObj);
    }
    else
        hres = CBrowserBand::QueryService(guidService, riid, ppvObj);

    return hres;
}

HRESULT CSearchBand::OnWinEvent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres)
{
    if ((WM_WININICHANGE == uMsg) && lParam &&
        ((0 == StrCmpW((LPCWSTR)lParam, SEARCH_SETTINGS_CHANGEDW)) ||
         (0 == StrCmpA((LPCSTR) lParam, SEARCH_SETTINGS_CHANGEDA))))
    {
        _NavigateToSearchUrl();
    }   

    return CBrowserBand::OnWinEvent(hwnd, uMsg, wParam, lParam, plres);
}



BOOL CSearchBand::_IsSafeUrl(LPCWSTR pwszUrl)
{
    BOOL bRet = FALSE;
    HKEY hkey;

    if (_bNewUrl || !_bUseDefault)
    {
        WCHAR wsz[MAX_URL_STRING];
        DWORD cch = ARRAYSIZE(wsz);

        if (SUCCEEDED(UrlCanonicalizeW(pwszUrl, wsz, &cch, 0)) && cch > 0)
        {
            // the first time this f-n is called, url passed in is the url of
            // the top most frame -- if that's not one of our 'safe' urls we
            // don't want to use this security mgr because it is possible 
            // that the outer frame hosts iframe w/ 'safe' site and scripts
            // shell dispatch from the outside thus being able to do anything
            // it wants.
            if (_wszCache[0] != L'\0')
            {
                if ((_nCmpLength && StrCmpNIW(wsz, _wszCache, _nCmpLength) == 0)
                || (!_nCmpLength && StrCmpIW(wsz, _wszCache) == 0))
                    return _bIsCacheSafe;
            }
            
            if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\SafeSites", 0, KEY_READ, &hkey) == ERROR_SUCCESS)
            {        
                WCHAR wszValue[MAX_PATH];
                WCHAR wszData[MAX_URL_STRING];
                DWORD cbData = SIZEOF(wszData);
                DWORD cchValue = ARRAYSIZE(wszValue);

                for (int i=0; RegEnumValueW(hkey, i, wszValue, &cchValue, NULL, NULL, (LPBYTE)wszData, &cbData) == ERROR_SUCCESS; i++)
                {
                    if (SHExpandEnvironmentStringsW(wszData, _wszCache, ARRAYSIZE(_wszCache)) > 0)
                    {
                        cchValue = ARRAYSIZE(_wszCache);
                        if (SUCCEEDED(UrlCanonicalizeW(_wszCache, _wszCache, &cchValue, 0)) && (cchValue > 0))
                        {
                            if (_wszCache[cchValue - 1] == L'*')
                            {
                                _nCmpLength = cchValue - 1;
                                bRet = StrCmpNIW(wsz, _wszCache, _nCmpLength) == 0;
                            }
                            else
                            {
                                _nCmpLength = 0;
                                bRet = StrCmpIW(wsz, _wszCache) == 0;
                            }

                            _bIsCacheSafe = bRet;
                            if (bRet)
                                break;
                        }
                        cbData = SIZEOF(_wszCache);
                        cchValue = ARRAYSIZE(wszValue);
                    }
                }
                RegCloseKey(hkey);        
            }

            // we did not find the url in the list of 'safe' sites
            // _wszCache now point to the last url read from the registry
            // ajdust it to point pwszUrl, _bIsCacheSafe is correct already
            if (!bRet)
                lstrcpynW(_wszCache, wsz, ARRAYSIZE(_wszCache));

            if (_bNewUrl)
            {
                _bNewUrl = FALSE;
                _bUseDefault = !bRet;
            }
        }
    }
        
    return bRet;
}



//***   CCommBand {
//

////////////////
///  Comm (BrowserOC) band

class CCommBand : public CBrowserBand
{

public:    
    // *** IPersistStream methods ***
    // (others use base class implementation) 
    virtual STDMETHODIMP GetClassID(CLSID *pClassID);
    virtual STDMETHODIMP Load(IStream *pStm);
    virtual STDMETHODIMP Save(IStream *pStm, BOOL fClearDirty);

    // *** IDockingWindow methods ***
    virtual STDMETHODIMP ShowDW(BOOL fShow);

protected:
    CCommBand();
    virtual ~CCommBand();

    friend HRESULT CCommBand_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi);       // for ctor

};

CCommBand::CCommBand() :
    CBrowserBand()
{
    _fBlockSIDProxy = FALSE;
    _fBlockDrop = TRUE;
    _fCustomTitle = TRUE;
    _wszTitle[0] = L'\0';

    _dwModeFlags = DBIMF_VARIABLEHEIGHT;

    return;
}

CCommBand::~CCommBand()
{
}

HRESULT CCommBand_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
{
    // aggregation checking is handled in class factory
    *ppunk = NULL;
    LPITEMIDLIST pidlNew;
    HRESULT hr = IECreateFromPath(L"about:blank", &pidlNew);
    if (SUCCEEDED(hr))
    {
        CCommBand *p = new CCommBand();
        if (p)
        {
            p->_pidl = pidlNew;
            *ppunk = SAFECAST(p, IDeskBand*);
            hr = S_OK;
        }
        else
        {
            ILFree(pidlNew);
            hr = E_OUTOFMEMORY;
        }
    }
    return hr;
}



//***   CCommBand::IPersistStream::* {

HRESULT CCommBand::GetClassID(CLSID *pClassID)
{
    *pClassID = CLSID_CommBand;

    return S_OK;
}

HRESULT CCommBand::Load(IStream *pstm)
{
//    _NavigateOC();
    
    return S_OK;
}

HRESULT CCommBand::Save(IStream *pstm, BOOL fClearDirty)
{
    return S_OK;
}

HRESULT CCommBand::ShowDW(BOOL fShow)
{
    // so that the contained Browser OC event gets fired
    if (_pauto) {
        _pauto->put_Visible(fShow);
    }

    return CBrowserBand::ShowDW(fShow);
}

// }

