#include "priv.h"
#include "sccls.h"
#include <varutil.h>

#include "itbdrop.h"
#include <urlhist.h>
#include "autocomp.h"
#include "itbar.h"
#include "address.h"
#include <winbase.h>
#include "basebar.h"
#include "shbrowse.h"
#include "brand.h"
#include "resource.h"
#include "theater.h"
#include "browmenu.h"
#include "util.h"
#include "droptgt.h"
#include "legacy.h"
#include "apithk.h"
#include "shbrows2.h"
#include "stdenum.h"
#include "iehelpid.h"
#include <tb_ids.h>
#include "mediautil.h"

#define WANT_CBANDSITE_CLASS
#include "bandsite.h"
#include "schedule.h"
#include "uemapp.h"

#include "mluisupp.h"

#ifdef UNIX
extern "C"  const GUID  CLSID_MsgBand;
#endif

// The edit button hackery needs to be moved to shdocvw.  This define identifies this code.
#define EDIT_HACK

// Offset of the comctl32 default bitmaps
#define OFFSET_HIST             (MAX_TB_BUTTONS - 1 + 0)   // 15
#define OFFSET_STD              (MAX_TB_BUTTONS - 1 + 6)   // 21
#define OFFSET_VIEW             (MAX_TB_BUTTONS - 1 + 21)  // 36

// This is the offset in the toolbar for the Shell glyphs and the Shell toolbar labels
#define SHELLTOOLBAR_OFFSET     (MAX_TB_BUTTONS - 1 + 1)  // 16
#define FONTGLYPH_OFFSET        (MAX_TB_BUTTONS - 1 + 38) // 53
#define BRIEFCASEGLYPH_OFFSET   (MAX_TB_BUTTONS - 1 + 34) // 49
#define RNAUIGLYPH_OFFSET       (MAX_TB_BUTTONS - 1 + 36) // 51
#define WEBCHECKGLYPH_OFFSET    (MAX_TB_BUTTONS - 1 + 42) // 57
#ifdef EDIT_HACK
#define EDITGLYPH_OFFSET        (9)
#endif

#define IDT_UPDATETOOLBAR       0x1
#define TIMEOUT_UPDATETOOLBAR   400

const GUID CLSID_Separator = { 0x67077B90L, 0x4F9D, 0x11D0, 0xB8, 0x84, 0x00, 0xAA, 0x00, 0xB6, 0x01, 0x04 };

extern HRESULT VariantClearLazy(VARIANTARG *pvarg);

// How many CT_TABLE structures to allocated at a time.
#define TBBMPLIST_CHUNK     5

#define MAX_EXTERNAL_BAND_NAME_LEN 64

#define MAX_TB_COMPRESSED_WIDTH 42
// 16 is added to the the MAX_TB defines. This is added through the strings
// in the RC file. This is done so that the localization folks can increase
// or decrease the width of the toolbar buttons
#define MAX_TB_WIDTH_LORES      38
#define MAX_TB_WIDTH_HIRES      60

// Dimensions of Coolbar Glyphs ..
#define TB_SMBMP_CX               16
#define TB_SMBMP_CY               16
#define TB_BMP_CX                 20
#define TB_BMP_CY                 20
#define TB_BMP_CX_ALPHABITMAP     24
#define TB_BMP_CY_ALPHABITMAP     24

int g_iToolBarLargeIconWidth = TB_BMP_CX;
int g_iToolBarLargeIconHeight = TB_BMP_CY;


#define CX_SEPARATOR    6     // we override toolbar control's default separator width of 8

#define DM_TBSITE   0
#define DM_TBCMD    0
#define DM_TBREF    TF_SHDREF
#define DM_LAYOUT   0
#define DM_ITBAR    0

#define TF_TBCUST   0x01000000

#if CBIDX_LAST != 5
#error Expected CBIDX_LAST to have value of 5
#endif

#if (FCIDM_EXTERNALBANDS_LAST - FCIDM_EXTERNALBANDS_FIRST + 1) < MAXEXTERNALBANDS
#error Insufficient range for FCIDM_EXTERNALBANDS_FIRST to FCIDM_EXTERNALBANDS_LAST
#endif


__inline UINT EXTERNALBAND_VBF_BIT(UINT uiBandExt)
{
    ASSERT(uiBandExt < MAXEXTERNALBANDS);

    // Formula: take 1, shift left by uiBandExt + 16
    //      => a bit in range (0x80000000, 0x00010000)
    UINT uBit = 1 << (uiBandExt + 16);
    ASSERT(uBit & VBF_EXTERNALBANDS);

    return uBit;
}

__inline BOOL IS_EXTERNALBAND(int idBand)
{
    return (InRange(idBand, CBIDX_EXTERNALFIRST, CBIDX_EXTERNALLAST));
}

__inline int MAP_TO_EXTERNAL(int idBand)
{
    ASSERT(IS_EXTERNALBAND(idBand));

    // CBIDX_LAST is one-based, mapping is zero-based
    return (idBand - (1 + CBIDX_LAST));
}


// maximum number of menu items in the context menus for back and forward.
#define MAX_NAV_MENUITEMS               9

#define DEFAULT_SEARCH_GUID    SRCID_SFileSearch //SRCID_SWebSearch

#define SZ_PROP_CUSTDLG     TEXT("Itbar custom dialog hwnd")

#define REG_KEY_BANDSTATE  TEXT("Software\\Microsoft\\Internet Explorer\\Toolbar")

// MHTML Editing
#define SZ_IE_DEFAULT_MHTML_EDITOR  "Default MHTML Editor"
#define REGSTR_PATH_DEFAULT_MHTML_EDITOR TSZIEPATH TEXT("\\") TEXT(SZ_IE_DEFAULT_MHTML_EDITOR)
#define REGSTR_KEY_DEFAULT_MHTML_EDITOR  TEXT(SZ_IE_DEFAULT_MHTML_EDITOR)

DWORD DoNetConnect(HWND hwnd);
DWORD DoNetDisconnect(HWND hwnd);


void _LoadToolbarGlyphs(HWND hwnd, IMLCACHE *pimlCache, int cx, int idBmp,
                        int iBitmapBaseIndex, BOOL bUseClassicGlyphs, HINSTANCE hInst);

BOOL _UseSmallIcons();


typedef struct tagTBBMP_LIST
{
    HINSTANCE hInst;
    UINT_PTR  uiResID;
    UINT  uiOffset;
    BITBOOL  fNormal:1;
    BITBOOL  fHot:1;
    BITBOOL  fDisabled:1;
    UINT  uiCount;
} TBBMP_LIST;

typedef struct tagCMDMAP
{
    GUID    guidButtonGroup;
    UINT    nCmdID;
    LPARAM lParam;  // app's data
} CMDMAP;

typedef struct tagCMDMAPCUSTOMIZE
{
    TBBUTTON btn;
    CMDMAP cm;
} CMDMAPCUSTOMIZE;

typedef struct {

    // the IOleCommandTarget info:
    GUID guid;
    UINT nCmdID;
    UINT fButtonState;
} BUTTONSAVEINFO;

#define TBSI_VERSION            7
typedef struct {
    int cVersion;
} TOOLBARSAVEINFO;

typedef struct {
    HDSA hdsa;
    BITBOOL fAdjust:1;
    BITBOOL fDirty:1;
} CUSTOMIZEINFO, *LPCUSTOMIZEINFO;

//Current latest version.
#define CBS_VERSION             17

// NOTE: Be very careful changing COOLBARSAVE because _LoadUpgradeSettings makes
// assumptions about the layout of the structure.  To avoid breaking that
// upgrade code, be sure you:
//
//  - don't change the order of existing members
//  - always add new members to the end of the structure.
//  - update _LoadUpgradeSettings if appropriate
//
typedef struct tagCOOLBARSAVE
{
    UINT        cbVer;
    UINT        uiMaxTBWidth;
    UINT        uiMaxQLWidth;
#ifdef UNIX
    BITBOOL     fUnUsed : 28;       // unused
#endif
    BITBOOL     fVertical : 1;      // The bar is oriented vertically
    BITBOOL     fNoText :1;         // "NoText"
    BITBOOL     fList : 1;          // toolbar is TBSTYLE_LIST (text on right) + TBSTYLE_EX_MIXEDBUTTONS
    BITBOOL     fAutoHide : 1;      // Auto hide toolbar in theater mode
    BITBOOL     fStatusBar : 1;     // Status bar in theater mode
    BITBOOL     fSaveInShellIntegrationMode : 1;     // Did we save in shell integration mode?
    UINT        uiVisible;          // "Visible bands"
    UINT        cyRebar;
    BANDSAVE    bs[CBANDSMAX];
    CLSID       aclsidExternalBands[ MAXEXTERNALBANDS ];  // Check classid
    CLSID       clsidVerticalBar;       //clsid of bar persisted within vertical band
    CLSID       clsidHorizontalBar;
} COOLBARSAVE, *LPCOOLBARSAVE;

//Flags for dwFlags passed to UpdateToolbarDisplay()
#define UTD_TEXTLABEL  0x00000001
#define UTD_VISIBLE    0x00000002

static const TCHAR c_szRegKeyCoolbar[] = TSZIEPATH TEXT("\\Toolbar");
static const TCHAR c_szValueTheater[]  = TEXT("Theater");

typedef struct tagFOLDERSEARCHITEM
{
    UINT    idCmd;
    GUID    guidSearch;
    int     iIcon;
    WCHAR   wszUrl[MAX_URL_STRING];
    WCHAR   wszName[80];           // friendly name
}FOLDERSEARCHITEM, *LPFOLDERSEARCHITEM;

BOOL NavigateSearchBar(IWebBrowser2 *pwb2, LPCWSTR pwszUrl);
BOOL _GetSearchHKEY(LPGUID lpguidSearch, HKEY *phkey);

#define REG_SZ_STATIC       TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FindExtensions\\Static")
#define REG_SZ_SEARCH_GUID  TEXT("SearchGUID")
#define REG_SZ_SEARCH_URL   TEXT("SearchGUID\\Url")

#define VIEW_OFFSET (SHELLGLYPHS_OFFSET + HIST_MAX + STD_MAX)
#define VIEW_ALLFOLDERS  (VIEW_NETCONNECT + 14)

static const TBBUTTON    c_tbExplorer[] =
{
    // override default toolbar width for separators; iBitmap member of
    // TBBUTTON struct is a union of bitmap index & separator width

    { 0, TBIDM_BACK  ,      0,               BTNS_DROPDOWN | BTNS_SHOWTEXT, {0,0}, 0, 0 },
    { 1, TBIDM_FORWARD,     0,               BTNS_DROPDOWN, {0,0}, 0, 1 },

    { 2, TBIDM_STOPDOWNLOAD, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, 2 },
    { 3, TBIDM_REFRESH,      TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, 3 },
    { 4, TBIDM_HOME,         TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, 4 },

    { VIEW_PARENTFOLDER + VIEW_OFFSET,    TBIDM_PREVIOUSFOLDER,   TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, VIEW_PARENTFOLDER + VIEW_OFFSET },
    { VIEW_NETCONNECT + VIEW_OFFSET,      TBIDM_CONNECT,          TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, VIEW_NETCONNECT + VIEW_OFFSET },
    { VIEW_NETDISCONNECT + VIEW_OFFSET,   TBIDM_DISCONNECT,       TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, VIEW_NETDISCONNECT + VIEW_OFFSET },

    { CX_SEPARATOR, 0,          TBSTATE_ENABLED, BTNS_SEP, {0,0}, 0, -1 },
    { 5, TBIDM_SEARCH,          TBSTATE_ENABLED, BTNS_SHOWTEXT, {0,0}, 0, 5 },
    { VIEW_ALLFOLDERS + VIEW_OFFSET,    TBIDM_ALLFOLDERS,         TBSTATE_ENABLED, BTNS_SHOWTEXT, {0,0}, 0, VIEW_ALLFOLDERS + VIEW_OFFSET },
    { 6, TBIDM_FAVORITES,       TBSTATE_ENABLED,  BTNS_SHOWTEXT, {0,0}, 0, 6 },
    // IF YOU CHANGE THE ORDERING OF THIS, please change AddMediaBarButton to reflect this ordering.
    { 11, TBIDM_MEDIABAR,       TBSTATE_ENABLED, BTNS_SHOWTEXT, {0,0}, 0, 11 },
    { 12, TBIDM_HISTORY,        TBSTATE_ENABLED, 0/*BTNS_SHOWTEXT*/, {0,0}, 0, 12},
    { CX_SEPARATOR,    0,       TBSTATE_ENABLED, BTNS_SEP, {0,0}, 0, -1 },
#ifndef DISABLE_FULLSCREEN
    // IE UNIX : No theater mode for beta1
    { 14, TBIDM_THEATER,         TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, 14 },
#endif
};

// these IDs are the array indices into c_tbExplorer above
// keep in sync, be aware there are ASSERTs to enforce them to be in sync
#define TBXID_BACK              0
#define TBXID_FORWARD           1
#define TBXID_STOPDOWNLOAD      2
#define TBXID_REFRESH           3
#define TBXID_HOME              4
#define TBXID_PREVIOUSFOLDER    5
#define TBXID_CONNECT           6
#define TBXID_DISCONNECT        7
#define TBXID_SEPARATOR1        8
#define TBXID_SEARCH            9
#define TBXID_ALLFOLDERS       10
#define TBXID_FAVORITES        11
#define TBXID_MEDIABAR         12
#define TBXID_HISTORY          13
#define TBXID_SEPARATOR2       14
#define TBXID_THEATER          15


static const BROWSER_RESTRICTIONS c_rest[] = {
    REST_BTN_BACK,
    REST_BTN_FORWARD,
    REST_BTN_STOPDOWNLOAD,
    REST_BTN_REFRESH,
    REST_BTN_HOME,
    REST_BROWSER_NONE,      // no policy for up
    REST_BROWSER_NONE,      // no policy for map drive
    REST_BROWSER_NONE,      // no policy for disconnect drive
    REST_BROWSER_NONE,      // separator
    REST_BTN_SEARCH,
    REST_BTN_ALLFOLDERS,
    REST_BTN_FAVORITES,
    REST_BTN_MEDIABAR,
    REST_BTN_HISTORY,
    REST_BROWSER_NONE,      // separator
#ifndef DISABLE_FULLSCREEN
    REST_BTN_THEATER,
#endif
};


// init flags to avoid multiple inits of toolbar and buttons
#define TBBIF_REG_PATH      TEXT("Software\\Microsoft\\Internet Explorer")
#define TBBIF_REG_KEY       TEXT("AddButtons")
// defined flags as bitfield
#define TBBIF_NONE          0
#define TBBIF_XBAR          0x1     // reserved/used to distinguish pre IE6/RC1 where PersonalBar/xBar was configured
#define TBBIF_MEDIA         0x2

#define SUPERCLASS CBaseBar

class CInternetToolbar :
   public CBaseBar,
   public IDockingWindow,
   public IObjectWithSite,  // *not* CObjectWithSite (want _ptbSite)
   public IExplorerToolbar,
   public DWebBrowserEvents,
   public IPersistStreamInit,
   public IShellChangeNotify,
   public ISearchItems
{
public:
    // *** IUnknown ***
    virtual STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj);
    virtual STDMETHODIMP_(ULONG) AddRef(void) { return SUPERCLASS::AddRef(); };
    virtual STDMETHODIMP_(ULONG) Release(void){ return SUPERCLASS::Release(); };

    // *** IOleWindow methods ***
    virtual STDMETHODIMP GetWindow(HWND * lphwnd) { return SUPERCLASS::GetWindow(lphwnd);};
    virtual STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode) {return SUPERCLASS::ContextSensitiveHelp(fEnterMode);};

    // *** IDockingWindow methods ***
    virtual STDMETHODIMP ShowDW(BOOL fShow);
    virtual STDMETHODIMP CloseDW(DWORD dwReserved);
    virtual STDMETHODIMP ResizeBorderDW(LPCRECT prcBorder, IUnknown* punkToolbarSite, BOOL fReserved);

    // *** IObjectWithSite methods ***
    virtual STDMETHODIMP SetSite(IUnknown* punkSite);
    // is E_NOTIMPL ok?
    virtual STDMETHODIMP GetSite(REFIID riid, void** ppvSite) { ASSERT(0); return E_NOTIMPL; };

    // *** IInputObjectSite methods ***
    virtual STDMETHODIMP OnFocusChangeIS(IUnknown *punk, BOOL fSetFocus);

    // *** IInputObject methods ***
    virtual STDMETHODIMP TranslateAcceleratorIO(LPMSG lpMsg);

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

    // *** IExplorerToolbar method ***
    virtual STDMETHODIMP SetCommandTarget(IUnknown* punkCmdTarget, const GUID* pguidButtonGroup, DWORD dwFlags);
    virtual STDMETHODIMP AddStdBrowserButtons(void);

    virtual STDMETHODIMP AddButtons(const GUID* pguidButtonGroup, UINT nButtons, const TBBUTTON * lpButtons);
    virtual STDMETHODIMP AddString(const GUID * pguidButtonGroup, HINSTANCE hInst, UINT_PTR uiResID, LONG_PTR *pOffset);
    virtual STDMETHODIMP GetButton(const GUID* pguidButtonGroup, UINT uiCommand, LPTBBUTTON lpButton);
    virtual STDMETHODIMP GetState(const GUID* pguidButtonGroup, UINT uiCommand, UINT * pfState);
    virtual STDMETHODIMP SetState(const GUID* pguidButtonGroup, UINT uiCommand, UINT fState);
    virtual STDMETHODIMP AddBitmap(const GUID * pguidButtonGroup, UINT uiBMPType, UINT uiCount, TBADDBITMAP * ptb,
                                   LRESULT * pOffset, COLORREF rgbMask);
    virtual STDMETHODIMP GetBitmapSize(UINT * uiID);
    virtual STDMETHODIMP SendToolbarMsg(const GUID * pguidButtonGroup, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT * plRes);

    virtual STDMETHODIMP SetImageList( const GUID* pguidCmdGroup, HIMAGELIST himlNormal, HIMAGELIST himlHot, HIMAGELIST himlDisabled);
    virtual STDMETHODIMP ModifyButton( const GUID * pguidButtonGroup, UINT uiCommand, LPTBBUTTON lpButton);

    // IOleCommandTarget
    virtual STDMETHODIMP QueryStatus(const GUID *pguidCmdGroup,
                                     ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext);
    virtual STDMETHODIMP Exec(const GUID *pguidCmdGroup,
                              DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn,
                              VARIANTARG *pvarargOut);

    // IPersistStreamInit
    STDMETHOD(GetClassID)(GUID *pguid);
    STDMETHOD(Load)(IStream *pStm);
    STDMETHOD(Save)(IStream *pStm, BOOL fClearDirty);
    STDMETHOD(InitNew)(void);
    STDMETHOD(IsDirty)(void);
    STDMETHOD(GetSizeMax)(ULARGE_INTEGER  *pcbSize);

    /* IDispatch methods */
    virtual STDMETHODIMP GetTypeInfoCount(UINT *pctinfo);

    virtual STDMETHODIMP GetTypeInfo(UINT itinfo,LCID lcid,ITypeInfo **pptinfo);

    virtual STDMETHODIMP GetIDsOfNames(REFIID riid,OLECHAR **rgszNames,UINT cNames,
                                       LCID lcid, DISPID * rgdispid);

    virtual STDMETHODIMP Invoke(DISPID dispidMember,REFIID riid,LCID lcid,WORD wFlags,
                                DISPPARAMS * pdispparams, VARIANT * pvarResult,
                                EXCEPINFO * pexcepinfo,UINT * puArgErr);

    // IShellChangeNotify
    virtual STDMETHODIMP OnChange(LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);

    // CBaseBar overrides
    virtual LRESULT v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // *** ISearchItems methods ***
    virtual STDMETHODIMP GetDefaultSearchUrl(LPWSTR pwzUrl, UINT cch);

    CInternetToolbar();
protected:
    virtual ~CInternetToolbar();
    static LRESULT CALLBACK SizableWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    void _OnCommand(WPARAM wParam, LPARAM lParam);
    BOOL _SendToToolband(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plres);
    LRESULT _OnNotify(LPNMHDR pnmh);
    void _OnTooltipNeeded(LPTOOLTIPTEXT pnmTT);
    void _QueryStatusTip(IOleCommandTarget *pct, LPTOOLTIPTEXT pnmTT, UINT uiCmd, const GUID* pguid);

    BOOL _UpEnabled();
    void _UpdateCommonButton(int iCmd, UINT nCmdID);
    void _UpdateToolbar(BOOL fForce);
    void _UpdateToolbarNow();
    void _UpdateGroup(const GUID *pguidCmdGroup, int cnt, OLECMD rgcmds[], const GUID* pguidButton, const int buttonsInternal[]);
    void _CSHSetStatusBar(BOOL fOn);
    void _StartDownload();
    void _StopDownload(BOOL fClosing);
    void _SendDocCommand(UINT idCmd);
    BOOL _CompressBands(BOOL fCompress, UINT uRowsNew, BOOL fForceUpdate);
    void _TrackSliding(int x, int y);
    HRESULT _DoNavigateA(LPSTR pszURL,int iNewSelection);
    HRESULT _DoNavigateW(LPWSTR pwzURL,int iNewSelection);
    HRESULT _DoNavigate(BSTR bstrURL,int iNewSelection);
    void _Unadvise(void);
    LRESULT _OnBeginDrag(NMREBAR* pnm);

    void _InsertURL(LPTSTR pszURL);

    void _ShowContextMenu(HWND hwnd, LPARAM lParam, LPRECT prcExclude);
    BOOL _ShowBackForwardMenu(BOOL fForward, POINT pt, LPRECT prcExclude);
    // search helper methods
    BOOL _GetFolderSearchData();
    void _SetSearchStuff();
    BOOL _GetSearchUrl(LPWSTR pwszUrl, DWORD cch);
    HRESULT _GetFolderSearches(IFolderSearches **ppfs);


    void _ReloadButtons();
    void _UpdateToolsStyle(BOOL fList);
    void _InitBitmapDSA();
    void _ReloadBitmapDSA();
    void _InitForScreenSize();
    void _InitToolbar();
    BOOL _FoldersButtonAvailable();
    void _AdminMarkDefaultButtons(PTBBUTTON ptbb, UINT cButtons);
    void _MarkDefaultButtons(PTBBUTTON ptbb, UINT cButtons);
    void _AddCommonButtons();
    HRESULT _CreateBands();
    BOOL    _ShowBands(UINT fVisible);
    HRESULT _ShowTools(PBANDSAVE pbs);
    HRESULT _ShowAddressBand(PBANDSAVE pbs);
    HRESULT _ShowExternalBand(PBANDSAVE pbs, int idBand );
    HRESULT _ShowLinks(PBANDSAVE pbs);
    HRESULT _ShowBrand(PBANDSAVE pbs);
    HRESULT _ShowMenu(PBANDSAVE pbs);
    void _ShowBandCommon(PBANDSAVE pbs, LPBANDITEMDATA pbid, BOOL fShow);
    void _EnsureAllBandsShown();
    HRESULT _GetMinRowHeight();

    HBITMAP _LoadBackBitmap();
    void    _SetBackground();
    void    _CommonHandleFileSysChange(LONG lEvent, LPITEMIDLIST* ppidl);
    LPITEMIDLIST _GetCurrentPidl(void);
    int     _ConvertHwndToID(HWND hwnd);

    HRESULT _GetPersistedBand(const CLSID clsid, REFIID riid, void ** ppiface);

    // Multiple command target
    LRESULT _AddBitmapFromForeignModule(UINT uiGetMSG, UINT uiSetMSG, UINT uiCount, HINSTANCE hinst,
                                        UINT_PTR nID, COLORREF rgbMask);

    HRESULT _LoadDefaultSettings(void);
    HRESULT _LoadUpgradeSettings(ULONG cbRead);
    HRESULT _LoadDefaultWidths(void);
    void _TryLoadIE3Settings();
    HRESULT _UpdateToolbarDisplay(DWORD dwFlags, UINT uVisibleBands, BOOL fNoText, BOOL fPersist);
    void _UpdateBrandSize();
    void _ShowVisible(DWORD dwVisible, BOOL fPersist);
    void _BuildSaveStruct(COOLBARSAVE* pcs);
    void _RestoreSaveStruct(COOLBARSAVE* pcs);
    void _GetVisibleBrowserBar(UINT idBar, CLSID *pclsidOut);
    VOID _UpdateLocking();

    LPBANDITEMDATA _AddNewBand(IDeskBand* pdb, DWORD dwID);

    void _TheaterModeLayout(BOOL fEnter);

    HBITMAP          _bmpBack; // this is the state we think the itbar is in
    static BMPCACHE  s_bmpBackShell; // this is the state of the shell bmp cache
    static BMPCACHE  s_bmpBackInternet; // this is the state of the internet bmp cache
    static IMLCACHE  s_imlTBGlyphs;

    HWND            _hwndMenu;
    HWND            _hwndAddressBand;

    IDockingWindowSite* _ptbsite;
    IOleCommandTarget*  _ptbsitect;
    IBrowserService*    _pbs;
    IBrowserService2*   _pbs2;
    IServiceProvider*   _psp;
    IBandProxy *        _pbp;

    BITBOOL            _fCreatedBandProxy:1;
    BITBOOL            _fBackEnabled:1;
    BITBOOL            _fForwardEnabled:1;
    BITBOOL            _fEditEnabled:1;
    BITBOOL            _fShow:1;
    BITBOOL            _fAnimating:1;
    BITBOOL            _fCompressed:1;
    BITBOOL            _fUserNavigated :1;
    BITBOOL            _fAutoCompInitialized :1;
    BITBOOL            _fDirty:1;
    BITBOOL            _fUsingDefaultBands:1;
    BITBOOL            _fTransitionToHTML:1;
    BITBOOL            _fInitialPidlIsWeb:1;
    BITBOOL            _fTheater: 1; // are we in theater mode?  claim no border space
    BITBOOL            _fAutoHide :1;
    BITBOOL            _fRebarDragging :1;
    BITBOOL            _fShellView:1;   // are we in shell view or web view?
    BITBOOL            _fNoShowMenu:1;    // can show menu band?
    BITBOOL            _fUpdateToolbarTimer:1;
    BITBOOL            _fNeedUpdateToolbar:1;
    BITBOOL            _fNavigateComplete:1;
    BITBOOL            _fLoading:1;     // are we still loading the bar?
    BITBOOL            _fDestroyed:1;   // Did we destroy our member varibles and are shutting down? If so, don't use the varibles. (Stress bug w/messages coming in)
    BITBOOL            _fLockedToolbar:1;

    UINT            _nVisibleBands;     // bitmask of which bands are visible: VBF_*

    IWebBrowser2*   _pdie;
    DWORD           _dwcpCookie;        // DIID_DWebBrowserEvents2
    int             _xCapture;
    int             _yCapture;
    // for multiple command target support
    HDSA            _hdsaTBBMPs;
    UINT            _uiMaxTBWidth;
    UINT            _uiTBTextRows;
    UINT            _uiTBDefaultTextRows;
    // search stuff
    HDPA            _hdpaFSI; // folder search items
    GUID            _guidCurrentSearch;
    GUID            _guidDefaultSearch;

    COOLBARSAVE     _cs;             //Coolbar layout info from registry!
    BOOL            _fDontSave;      // force ourselves not to persist out the state.

    struct EXTERNALBANDINFO {
        CLSID       clsid;          // CLSID of the band
        LPWSTR      pwszName;       // Band name
        LPWSTR      pwszHelp;       // Band help text
    };
    EXTERNALBANDINFO _rgebi[ MAXEXTERNALBANDS ];

    void _LoadExternalBandInfo();

    TBBUTTON _tbExplorer[ARRAYSIZE(c_tbExplorer)];
    int      _iButtons;


#ifdef EDIT_HACK
    // Variables for customizing the edit button glyph
    HIMAGELIST      _himlEdit;          // Monochrome Image list for the edit button
    HIMAGELIST      _himlEditHot;       // Hot image list for edit button
    int             _iEditIcon;         // index of current edit icon
    int             _cxEditGlyph;       // cx of glyph size
    int             _cyEditGlyph;       // cx of glyph size

    // Functions for managing a custom edit glyph
    void _InitEditButtonStyle();
    void _SetEditGlyph(int iIcon);
    void _RefreshEditGlyph();
    void _UpdateEditButton();
    static HIMAGELIST _CreateGrayScaleImagelist(HBITMAP hbmpImage, HBITMAP hbmpMask);
    static BSTR _GetEditProgID(IHTMLDocument2* pHTMLDocument);

    //
    // We can have multiple edit verbs associated with a document.  The following class
    // maintains a list of verbs.
    //
    #define FCIDM_EDITFIRST  2000
    #define FCIDM_EDITLAST   2100
    #define SZ_EDITVERB_PROP  TEXT("CEditVerb_This")
    #define IL_EDITBUTTON 2     // Index of image list used for the edit button
    #define IL_SEARCHBUTTON 3   //                   ||             search button

    // MSAA Menu Info declarations.
    // These will eventually be incorporated into oleacc.h - but for the
    // moment, we declare them privately...
    #define MSAA_MENU_SIG  0xAA0DF00DL

    class CEditVerb
    {
    public:
        CEditVerb();
        ~CEditVerb();

        // Functions for managing the verbs
        BOOL Add(LPTSTR pszProgID);
        UINT GetSize() { return _nElements; }
        void RemoveAll();

        // Functions to access the default edit verb
        int   GetIcon() { return (_nElements && _pVerb[_nDefault].fShowIcon) ? _GetVerb(_nDefault).iIcon : -1; }
        BOOL  GetToolTip(LPTSTR pszToolTip, UINT cchMax, BOOL fStripAmpersands = TRUE);
        BOOL  GetMenuText(LPTSTR pszText, UINT cchMax) { return GetToolTip(pszText, cchMax, FALSE); }
        void  Edit(LPCTSTR pszUrl) { _Edit(pszUrl, _nDefault); }

        // Pop-up menu
        BOOL ShowEditMenu(POINT pt, HWND hwnd, LPTSTR pszUrl);

        // Get default editor from the registry
        void InitDefaultEditor(HKEY hkey = NULL);

    protected:
        struct MSAAMenuInfo
        {
            DWORD m_MSAASig;  // Must be MSAA_MENU_SIG
            DWORD m_CharLen;  // Length in characters of text, excluding terminating NUL
            LPWSTR m_pWStr;   // Menu text, in UNICODE, with terminating UNICODE-NUL.
        };

        struct EDITVERB
        {
            MSAAMenuInfo m_MSAA;     // MSAA info - must be first element.
            HKEY    hkeyProgID;      // Key the we shellexec
            BITBOOL fUseOpenVerb:1;  // use open verb instead of edit
            BITBOOL fInit:1;         // true if the data below has beed initialized
            BITBOOL fShowIcon:1;     // true if icon should show up on button
            int     iIcon;           // cached icon index
            UINT    idCmd;           // menu id
            LPTSTR  pszDesc;         // executable name or document name
            LPTSTR  pszMenuText;     // Menu text
            LPTSTR  pszExe;          // Path of the exe used to edit
        };

        EDITVERB* _Add(HKEY hkeyProgID, BOOL fPermitOpenVerb, BOOL fCheckForOfficeApp, BOOL fShowIcon);
        EDITVERB& _GetVerb(UINT nIndex);
        void      _FetchInfo(UINT nIndex);
        void      _Edit(LPCTSTR pszUrl, UINT nIndex);
        LPCTSTR   _GetDescription(EDITVERB& rVerb);
        void      _SetMSAAMenuInfo(EDITVERB& rVerb);
        void      _ClearMSAAMenuInfo(EDITVERB& rVerb);
        void      _FormatMenuText(UINT nIndex);
        BOOL      _IsUnique(EDITVERB& rNewVerb);
        BOOL      _IsHtmlStub(LPCWSTR pszPath);
        LPCTSTR   _GetExePath(EDITVERB& rVerb);
        LPCTSTR   _GetDefaultEditor();
        void      _InitDefaultMHTMLEditor();

        static LRESULT CALLBACK _WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

        // Member data
        UINT        _nElements;         // number of edit verbs
        UINT        _nDefault;          // Default edit verb
        EDITVERB*   _pVerb;             // array of edit verbs
        WNDPROC     _lpfnOldWndProc;    // former wndProc
        LPWSTR      _pszDefaultEditor;  // Friendly name of default HTML editor
        BOOL        _fInitEditor;       // if we checked for a default editor
    };
    CEditVerb  _aEditVerb;

#endif

    // internal bandsite class
    class CBrowserToolsBand;
    class CITBandSite : public CBandSite
    {
        CITBandSite();

        virtual STDMETHODIMP Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut);
        virtual STDMETHODIMP AddBand(IUnknown *punk);
        virtual STDMETHODIMP HasFocusIO();

    protected:
        virtual void v_SetTabstop(LPREBARBANDINFO prbbi);
        BOOL _SetMinDimensions();
        friend class CInternetToolbar;
        friend class CBrowserToolsBand;

        virtual HRESULT _OnContextMenu(WPARAM wParm, LPARAM lParam);
        virtual HRESULT _Initialize(HWND hwndParent);

    };
    CITBandSite _bs;


#define TOOLSBANDCLASS CInternetToolbar::CBrowserToolsBand
    class CBrowserToolsBand : public CToolbarBand
    {
        CMDMAP* _GetCmdMapByIndex(int nIndex) { return _GetCmdMap(nIndex, TRUE);};
        CMDMAP* _GetCmdMapByID(int id)  { return _GetCmdMap(id, FALSE);};
        LRESULT _ToolsCustNotify (LPNMHDR pnmh);  // Handle TBCustomization Notify
        BOOL _SaveRestoreToolbar(BOOL fSave);
        void _FreeCustomizeInfo();
        void _FreeCmdMap(CMDMAP*);
        BOOL _RemoveAllButtons();
        int _CommandFromIndex(UINT uIndex);
        HRESULT _ConvertCmd(const GUID* pguidButtonGroup, UINT id, GUID* pguidOut, UINT * pid);
        void _OnDeletingButton(TBNOTIFY* ptbn);
        LONG_PTR _AddString(LPWSTR pwstr);
        void _PreProcessButtonString(TBBUTTON *ptbn, DWORD dwFlags);
        void _PreProcessExternalTBButton(TBBUTTON *ptbn);
        UINT _ProcessExternalButtons(PTBBUTTON ptbb, UINT cButtons);
        void _GetButtons(IOleCommandTarget* pct, const GUID* pguid, HDSA hdsa);
        void _RecalcButtonWidths();
        void _AddMediaBarButton();

        void            _UpdateTextSettings(INT_PTR ids);
        static BOOL_PTR CALLBACK _BtnAttrDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
        static void     _PopulateComboBox(HWND hwnd, const int iResource[], UINT cResources);
        static void     _SetComboSelection(HWND hwnd, int iCurOption);
        void            _SetDialogSelections(HWND hDlg, BOOL fSmallIcons);
        static void     _PopulateDialog(HWND hDlg);
        void            _OnBeginCustomize(LPNMTBCUSTOMIZEDLG pnm);

        BOOL _BuildButtonDSA();
        CMDMAPCUSTOMIZE* _GetCmdMapCustomize(GUID* guid, UINT nCmdID);

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

        virtual STDMETHODIMP GetClassID(CLSID *pClassID) {return E_NOTIMPL;};
        virtual STDMETHODIMP Load(IStream *pStm) {return E_NOTIMPL;};
        virtual STDMETHODIMP Save(IStream *pStm, BOOL fClearDirty) {return E_NOTIMPL;};

        // *** IUnknown ***
        virtual STDMETHODIMP_(ULONG) AddRef(void) { return CToolBand::AddRef(); };
        virtual STDMETHODIMP_(ULONG) Release(void){ return CToolBand::Release(); };
        virtual STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj);

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

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

        // *** IDockingWindow methods ***
        virtual STDMETHODIMP CloseDW(DWORD dwReserved) { return S_OK;};

        // *** IInputObject methods ***
        virtual STDMETHODIMP TranslateAcceleratorIO(LPMSG lpMsg);

    protected:
        IOleCommandTarget* _CommandTargetFromCmdMap(CMDMAP* pcm);
        LRESULT _OnToolbarDropDown(TBNOTIFY *ptbn);
        virtual LRESULT _OnNotify(LPNMHDR pnmh);
        LRESULT _OnContextMenu(LPARAM lParam, WPARAM wParam);
        CMDMAP* _GetCmdMap(int i, BOOL fByIndex);
        void _OnEndCustomize();
        LRESULT _TryShowBackForwardMenu(DWORD dwItemSpec, LPPOINT ppt, LPRECT prcExclude);
        CBrowserToolsBand();
        void _FreeBtnsAdded();

        friend class CInternetToolbar;
        friend class CITBandSite;

        GUID            _guidCurrentButtonGroup;
        IOleCommandTarget* _pctCurrentButtonGroup;
        LPTBBUTTON      _pbtnsAdded;
        int             _cBtnsAdded;
        DWORD            _nNextCommandID;
        CUSTOMIZEINFO *_pcinfo;
        BITBOOL    _fCustomize :1;
        BITBOOL    _fNeedFreeCmdMapsAdded :1;
    };

    CBrowserToolsBand _btb;

    friend class CBrowserToolsBand;
    friend class CITBandSite;
    friend void CInternetToolbar_CleanUp();
    friend void CInternetToolbar_Preload();
    friend void ITBar_LoadToolbarGlyphs(HWND hwnd);
};

//
// Gets the stream corresponding to the type of the given pidl
//     If the stream already doesn't exist, then it returns NULL.

HRESULT _GetStreamName(DWORD dwITBS, LPTSTR pszName, DWORD cchSize)
{
    HRESULT hr = S_OK;

    ASSERT(pszName);

    switch (dwITBS)
    {
    case ITBS_WEB:
        StrCpyN(pszName, TEXT("WebBrowser"), cchSize);
        break;

    case ITBS_SHELL:
        StrCpyN(pszName, TEXT("ShellBrowser"), cchSize);
        break;

    case ITBS_EXPLORER:
        StrCpyN(pszName, TEXT("Explorer"), cchSize);
        break;

    default:
        hr = E_FAIL;
        break;
    }

    if (FAILED(hr))
        pszName[0] = '\0';

    return hr;
}


//
// Gets the stream corresponding to the type of the given pidl
//     If the stream already doesn't exist, then it returns NULL.

IStream *GetRegStream(BOOL fInternet, LPCTSTR pszValue, DWORD grfMode)
{
    IStream *pstm = NULL;
    HKEY    hkToolbar;

    if (RegCreateKey(HKEY_CURRENT_USER, c_szRegKeyCoolbar, &hkToolbar) == ERROR_SUCCESS)
    {
        TCHAR   szStreamName[MAX_PATH];

        if (SUCCEEDED(_GetStreamName(fInternet, szStreamName, ARRAYSIZE(szStreamName))))
            pstm = OpenRegStream(hkToolbar, szStreamName, pszValue, grfMode);

        RegCloseKey(hkToolbar);
    }

    return(pstm);
}


//
// Gets the stream corresponding to the type of the given pidl
//     If the stream already doesn't exist, then it returns NULL.

IStream *GetITBarStream(BOOL fInternet, DWORD grfMode)
{
    return GetRegStream(fInternet, TEXT("ITBarLayout"), grfMode);
}


IMLCACHE CInternetToolbar::s_imlTBGlyphs = {NULL};
BMPCACHE CInternetToolbar::s_bmpBackShell = {NULL};
BMPCACHE CInternetToolbar::s_bmpBackInternet = {NULL};
BOOL g_fSmallIcons = FALSE;

void IMLCACHE_CleanUp(IMLCACHE * pimlCache, DWORD dwFlags)
{
    for (int i = 0; i < CIMLISTS; i++)
    {
        if (pimlCache->arhimlPendingDelete[i])
            ImageList_Destroy(pimlCache->arhimlPendingDelete[i]);

        if ((dwFlags & IML_DESTROY) && pimlCache->arhiml[i])
            ImageList_Destroy(pimlCache->arhiml[i]);
    }
}

// DO NOT change the numbering of the following bitmap specs. 
// If you want to add new sizes, add them _after_ ITB_1616_HOT_HICOLOR

#define    ITB_2020_NORMAL             0
#define    ITB_2020_HOT                1
#define    ITB_1616_NORMAL             2
#define    ITB_1616_HOT                3
#define    ITB_2020_NORMAL_HICOLOR     4
#define    ITB_2020_HOT_HICOLOR        5
#define    ITB_1616_NORMAL_HICOLOR     6
#define    ITB_1616_HOT_HICOLOR        7

void ITBar_LoadToolbarGlyphs(HWND hwnd)
{
    int cx, idBmpType;
    int iBitmapBaseIndex;
    BOOL bUseClassicGlyphs = SHUseClassicToolbarGlyphs();
    HINSTANCE hInst;

    g_fSmallIcons = _UseSmallIcons();

    if (bUseClassicGlyphs)
    {
        g_iToolBarLargeIconWidth = TB_BMP_CX;
        g_iToolBarLargeIconHeight = TB_BMP_CY;

        iBitmapBaseIndex = IDB_SHSTD;
        hInst = HINST_THISDLL;
    }
    else
    {
        g_iToolBarLargeIconWidth = TB_BMP_CX_ALPHABITMAP;
        g_iToolBarLargeIconHeight = TB_BMP_CY_ALPHABITMAP;

        iBitmapBaseIndex = IDB_TB_SH_BASE;
        hInst = GetModuleHandle(TEXT("shell32.dll"));
    }

    if (g_fSmallIcons)
    {
        cx = TB_SMBMP_CX;
        idBmpType = ITB_1616_NORMAL;
    }
    else
    {
        cx = g_iToolBarLargeIconWidth;
        idBmpType = ITB_2020_NORMAL;
    }

    if (SHGetCurColorRes() > 8)
        idBmpType += DELTA_HICOLOR;

    _LoadToolbarGlyphs(hwnd, &CInternetToolbar::s_imlTBGlyphs, cx, idBmpType,
                       iBitmapBaseIndex, bUseClassicGlyphs, hInst);
}


void CInternetToolbar_Preload()
{
   ENTERCRITICAL;
   ITBar_LoadToolbarGlyphs(NULL);
   Brand_InitBrandContexts();
   LEAVECRITICAL;
}


void CInternetToolbar_CleanUp()
{
    TraceMsg(DM_ITBAR, "CInternetToolbar_CleanUp: Destroying shared GDI objects");
    if (CInternetToolbar::s_bmpBackInternet.hbmp)
        DeleteObject(CInternetToolbar::s_bmpBackInternet.hbmp);
    if (CInternetToolbar::s_bmpBackShell.hbmp)
        DeleteObject(CInternetToolbar::s_bmpBackShell.hbmp);

    IMLCACHE_CleanUp(&CInternetToolbar::s_imlTBGlyphs, IML_DESTROY);
}

STDAPI CInternetToolbar_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
{
    // aggregation checking is handled in class factory

    CInternetToolbar *pitbar = new CInternetToolbar();
    if (pitbar)
    {
        *ppunk = SAFECAST(pitbar, IDockingWindow *);
        return S_OK;
    }

    return E_OUTOFMEMORY;
}

LRESULT CInternetToolbar::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if ( uMsg == WM_SYSCOLORCHANGE )
    {
        // refresh the back drop incase the colours have changed
        _SetBackground();
    }

    return SUPERCLASS::v_WndProc( hwnd, uMsg, wParam, lParam );
}

void CInternetToolbar::_LoadExternalBandInfo()
{
#ifdef DEBUG
    int i;
    // Should have been zero-initialized
    for (i = 0; i < ARRAYSIZE(_rgebi); i++)
    {

        ASSERT(IsEqualGUID(_rgebi[i].clsid, GUID_NULL));
        ASSERT(_rgebi[i].pwszName == NULL);
        ASSERT(_rgebi[i].pwszHelp == NULL);
    }
#endif

    if ((!SHRegGetBoolUSValue(TEXT("SOFTWARE\\Microsoft\\Internet Explorer\\Main"), TEXT("Enable Browser Extensions"), FALSE, TRUE))
        || (GetSystemMetrics(SM_CLEANBOOT)!=0))
    {
        return;
    }

    HKEY hkey;
    DWORD dwClsidIndex = 0;
    if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_KEY_BANDSTATE, 0, KEY_READ, &hkey) == ERROR_SUCCESS)
    {
        TCHAR tszReg[MAX_PATH];
        StrCpy(tszReg, TEXT("CLSID\\"));
        const int cchClsidPrefix = 6;      // 6 = strlen("CLSID\\")
        LPTSTR ptszClsid = tszReg + cchClsidPrefix;
        DWORD cchClsid;
        for (DWORD dwIndex = 0;
             cchClsid = ARRAYSIZE(tszReg) - cchClsidPrefix,
             dwClsidIndex < ARRAYSIZE(_rgebi) &&
             RegEnumValue( hkey, dwIndex, ptszClsid, &cchClsid, NULL, NULL, NULL, NULL ) == ERROR_SUCCESS;
             dwIndex++)
        {
            CLSID clsid;

            // We want to ignore the radio toolband {8E718888-423F-11D2-876E-00A0C9082467}
            // without affecting its existing registration. Makes uninstall easier.
            if (GUIDFromString( ptszClsid, &clsid )
                && StrCmpI(ptszClsid, TEXT("{8E718888-423F-11D2-876E-00A0C9082467}")))
            {
                HKEY hkeyClsid;
                if (RegOpenKeyEx(HKEY_CLASSES_ROOT, tszReg, 0, KEY_READ, &hkeyClsid) == ERROR_SUCCESS)
                {
                    // Don't save the CLSID until we're sure it worked
                    _rgebi[dwClsidIndex].clsid = clsid;

                    WCHAR wszBuf[MAX_PATH];

                    // Get the name; use SHLoadRegUIString so the app can localize
                    SHLoadRegUIStringW( hkeyClsid, L"", wszBuf, ARRAYSIZE(wszBuf) );
                    Str_SetPtrW( &_rgebi[dwClsidIndex].pwszName, wszBuf);

                    // Get the help; use SHLoadRegUIString so the app can localize
                    SHLoadRegUIStringW( hkeyClsid, L"HelpText", wszBuf, ARRAYSIZE(wszBuf) );
                    Str_SetPtrW( &_rgebi[dwClsidIndex].pwszHelp, wszBuf);

                    RegCloseKey(hkeyClsid);

                    dwClsidIndex++;
                }
            }
        }
        RegCloseKey( hkey );
    }
}

CInternetToolbar::CInternetToolbar() : CBaseBar(), _yCapture(-1), _iButtons(-1)
#ifdef EDIT_HACK
, _iEditIcon(-1), _cxEditGlyph(-1), _cyEditGlyph(-1)
#endif
{
    DllAddRef();

    if (GetSystemMetrics(SM_CXSCREEN) < 650)
        _uiMaxTBWidth = MAX_TB_WIDTH_LORES;
    else
        _uiMaxTBWidth = MAX_TB_WIDTH_HIRES;

    ASSERT(_fLoading == FALSE);
    ASSERT(_hwnd == NULL);
    ASSERT(_btb._guidCurrentButtonGroup == CLSID_NULL);
    _btb._nNextCommandID = 1000;

    DWORD dwResult = FALSE, dwType, dwcbData = sizeof(dwResult), dwDefault = TRUE;
    SHRegGetUSValue(c_szRegKeyCoolbar, TEXT("Locked"), &dwType, &dwResult, &dwcbData, FALSE, &dwDefault, sizeof(dwDefault));
    SHSetValue(HKEY_CURRENT_USER, c_szRegKeyCoolbar, TEXT("Locked"), REG_DWORD, &dwResult, sizeof(dwResult));
    _fLockedToolbar = dwResult;

    _LoadExternalBandInfo();
}

void CInternetToolbar::_Unadvise(void)
{
    if(_dwcpCookie)
    {
        ConnectToConnectionPoint(NULL, DIID_DWebBrowserEvents2, FALSE, _pdie, &_dwcpCookie, NULL);
    }
}

int CALLBACK DeleteDPAPtrCB(void *pItem, void *pData)
{
    if ( pItem )
    {
        ASSERT( ::LocalSize(pItem) == sizeof(FOLDERSEARCHITEM) );
        LocalFree(pItem);
        pItem = NULL;
    }

    return TRUE;
}

CInternetToolbar::~CInternetToolbar()
{
    ATOMICRELEASE(_pdie);

    if(_pbp && _fCreatedBandProxy)
    {
        _pbp->SetSite(NULL);
    }
    
    if (IsWindow(_hwnd))
    {
        DestroyWindow(_hwnd);
    }

    ATOMICRELEASE(_pbp);

    ASSERT(!_ptbsite && !_ptbsitect && !_psp && !_pbs && !_pbs2);
    SetSite(NULL);

    if ( _hdpaFSI )
    {
        DPA_DestroyCallback(_hdpaFSI, DeleteDPAPtrCB, NULL);
        _hdpaFSI = NULL;
    }

    for (int i = 0; i < ARRAYSIZE(_rgebi); i++)
    {
        Str_SetPtrW( &_rgebi[i].pwszName, NULL);
        Str_SetPtrW( &_rgebi[i].pwszHelp, NULL);
    }

    TraceMsg(TF_SHDLIFE, "dtor CInternetToolbar %x", this);
    DllRelease();
}

#define IID_DWebBrowserEvents DIID_DWebBrowserEvents


HRESULT CInternetToolbar::QueryInterface(REFIID riid, void ** ppvObj)
{
    static const QITAB qit[] = {
        // perf: last tuned 980728
        QITABENTMULTI(CInternetToolbar, IDispatch, DWebBrowserEvents),  // IID_IDispatch
        QITABENT(CInternetToolbar, IExplorerToolbar),       // IID_IDispatch
        QITABENT(CInternetToolbar, IObjectWithSite),        // IID_IObjectWithSite
        QITABENT(CInternetToolbar, IPersistStreamInit),     // IID_IPersistStreamInit
        QITABENT(CInternetToolbar, IDockingWindow),         // IID_IDockingWindow
        QITABENT(CInternetToolbar, DWebBrowserEvents),      // IID_DWebBrowserEvents
        QITABENT(CInternetToolbar, IShellChangeNotify),     // rare IID_IShellChangeNotify
        QITABENT(CInternetToolbar, ISearchItems),           // rare IID_ISearchItems
        { 0 },
    };

    HRESULT hres = QISearch(this, qit, riid, ppvObj);
    if (FAILED(hres))
        hres = SUPERCLASS::QueryInterface(riid, ppvObj);

    return hres;
}

/* IDispatch methods */
HRESULT CInternetToolbar::GetTypeInfoCount(UINT *pctinfo)
{
    return(E_NOTIMPL);
}

HRESULT CInternetToolbar::GetTypeInfo(UINT itinfo,LCID lcid,ITypeInfo **pptinfo)
{
    return(E_NOTIMPL);
}

HRESULT CInternetToolbar::GetIDsOfNames(REFIID riid,OLECHAR **rgszNames,UINT cNames,
                                        LCID lcid, DISPID * rgdispid)
{
    return(E_NOTIMPL);
}

#if 0
//  NOTE - StevePro changed it so this code isnt called
//  this is a goodness, because it calls SHVerbExists() which
//  is a TCHAR API, that is actually compiled as an ANSI API
//  and since we are UNICODE it just always fails.
//  leaving this in so that we know about the issue of
//  frontpad.exe possibly needing to be disabled.
BOOL _ShowEditForExtension(LPCTSTR pszExtension)
{
    TCHAR szBuf[MAX_PATH];
    if (SHVerbExists(pszExtension, TEXT("edit"), szBuf))
    {
        // don't show it if it's just our own
        if (StrStrI(szBuf, TEXT("frontpad.exe")))
        {
            return FALSE;
        }
        return TRUE;
    }

    return FALSE;
}
#endif

#ifdef EDIT_HACK
//+-------------------------------------------------------------------------
// This function scans the html document for META tags that indicate the
// program that was used to create the HTML page.  Examples are:
//
//  <meta name="ProgID" content="word.document" >
//  <meta name="ProgID" content="excel.sheet" >
//
// If a match is found, the content of the first match is returned.  This
// progid is used to edit the document.
//--------------------------------------------------------------------------
BSTR CInternetToolbar::_GetEditProgID(IHTMLDocument2* pHTMLDocument)
{
    BSTR bstrProgID = NULL;

    //
    // First get all document elements.  Note that this is very fast in
    // ie5 because the collection directly accesses the internal tree.
    //
    IHTMLElementCollection * pAllCollection;
    if (SUCCEEDED(pHTMLDocument->get_all(&pAllCollection)))
    {
        IHTMLMetaElement* pMetaElement;
        IHTMLBodyElement* pBodyElement;
        IHTMLFrameSetElement* pFrameSetElement;
        IDispatch* pDispItem;

        //
        // Now we scan the document for meta tags.  Since these must reside in
        // in the head, and since Trident always creates a body tag, we can
        // stop looking when we hit the body.
        //
        // Note, the alternative of using pAllCollection->tags to return the
        // collection of meta tags is likely more expensive because it will
        // walk the whole tree (unless Trident optimizes this).
        //
        long lItemCnt;
        VARIANT vEmpty;
        V_VT(&vEmpty) = VT_EMPTY;

        VARIANT vIndex;
        V_VT(&vIndex) = VT_I4;

        EVAL(SUCCEEDED(pAllCollection->get_length(&lItemCnt)));

        for (long lItem = 0; lItem < lItemCnt; lItem++)
        {
            V_I4(&vIndex) = lItem;

            if (S_OK == pAllCollection->item(vIndex, vEmpty, &pDispItem))
            {
                //
                // First see if it's a meta tag
                //
                if (SUCCEEDED(pDispItem->QueryInterface(IID_PPV_ARG(IHTMLMetaElement, &pMetaElement))))
                {
                    BSTR bstrName = NULL;

                    //
                    // We have a META element, check its NAME and CONTENT
                    //
                    if ( SUCCEEDED(pMetaElement->get_name(&bstrName)) && (bstrName != NULL) &&
                         (StrCmpIW(bstrName, OLESTR("ProgId")) == 0) &&
                         SUCCEEDED(pMetaElement->get_content(&bstrProgID)) && (bstrProgID != NULL)
                       )
                    {
                        // We got the ProgID, so terminate the search;
                        lItem = lItemCnt;
                    }

                    if (bstrName != NULL)
                        SysFreeString(bstrName);

                    pMetaElement->Release();
                }
                //
                // Next check for the body tag
                //
                else if (SUCCEEDED(pDispItem->QueryInterface(IID_PPV_ARG(IHTMLBodyElement, &pBodyElement))))
                {
                    // Found the body tag, so terminate the search
                    lItem = lItemCnt;
                    pBodyElement->Release();
                }
                //
                // Finally, check for a frameset tag
                //
                else if (SUCCEEDED(pDispItem->QueryInterface(IID_PPV_ARG(IHTMLFrameSetElement, &pFrameSetElement))))
                {
                    // Found a frameset tag, so terminate the search
                    lItem = lItemCnt;
                    pFrameSetElement->Release();
                }
                pDispItem->Release();
            }
        }
        // Make sure that these don't have to be cleared (should not have been modified)
        ASSERT(vEmpty.vt == VT_EMPTY);
        ASSERT(vIndex.vt == VT_I4);

        pAllCollection->Release();
    }

    return bstrProgID;
}

//+-------------------------------------------------------------------------
// Returns grey-scale image from the icon passed in.
//--------------------------------------------------------------------------
HIMAGELIST CInternetToolbar::_CreateGrayScaleImagelist(HBITMAP hbmpImage, HBITMAP hbmpMask)
{
    // Determine the button dimensions
    int cx = g_fSmallIcons ? TB_SMBMP_CX : g_iToolBarLargeIconWidth;
    int cy = g_fSmallIcons ? TB_SMBMP_CY : g_iToolBarLargeIconHeight;

    // Start with a 24 bit color image list
    HIMAGELIST himlEdit = ImageList_Create(cx, cy, ILC_COLOR24 | ILC_MASK, 1, 1);
    if (NULL == himlEdit)
    {
        return NULL;
    }

    ImageList_Add(himlEdit, hbmpImage, hbmpMask);

    // Get the dib section from the image list
    IMAGEINFO ii;
    if (ImageList_GetImageInfo(himlEdit, 0, &ii))
    {
        DIBSECTION ds = {0};
        if (GetObject(ii.hbmImage, sizeof(ds), &ds))
        {
            //
            // Map each pixel to a monochrome equivalent.
            //
            BYTE* pBits = (BYTE*)ds.dsBm.bmBits;
            BYTE* pScan = pBits;
            int xWid = ds.dsBm.bmWidth;
            int yHei = ds.dsBm.bmHeight;
            long cbScan = ((xWid * 24 + 31) & ~31) / 8;

            for (int y=0; y < yHei; ++y)
            {
                for (int x=0; x < xWid; ++x)
                {
                    //
                    // Map to equivalent gray color by setting r,g,b to the same value.
                    // Using the average of r,g,b can be too dark, and using the max
                    // of r,g,b can be too bright.  So, as a simple algorithm we use
                    // the average of the two schemes.  This is cheaper than using true
                    // intensity matching.
                    //
                    BYTE nMax = max(max(pScan[0], pScan[1]), pScan[2]);
                    BYTE nAve = ((UINT)pScan[0] + pScan[1] + pScan[2])/3;
                    pScan[0] = pScan[1] = pScan[2] = ((UINT)nMax + nAve)/2;

                    // Increment to next pixel
                    pScan += 3;
                }

                // Increment to the next scan line
                pBits += cbScan;
                pScan = pBits;
            }
        }
    }
    return himlEdit;
}

//+-------------------------------------------------------------------------
// Returns image and mask bitmaps for the desired image list item
//--------------------------------------------------------------------------
BOOL MyImageList_GetBitmaps
(
    HIMAGELIST himl,        // image list to use
    int iImage,             // image to copy
    int x,                  // x-offset to draw in bitmap
    int y,                  // x-offset to draw in bitmap
    int cx,                 // width of bitmap
    int cy,                 // height of bitmap
    HBITMAP* phbmpImage,    // returned color bitmap
    HBITMAP* phbmpMask      // returned mask bitmap
)
{
    ASSERT(phbmpImage);
    ASSERT(phbmpMask);

    BOOL fRet = FALSE;
    HDC hdc = GetDC(NULL);

    if (hdc)
    {
        HDC hdcDst = CreateCompatibleDC(hdc);
        if (hdcDst)
        {
            HBITMAP hbmpImage = CreateCompatibleBitmap(hdc, cx, cy);
            if (hbmpImage)
            {
                HBITMAP hbmpMask = CreateBitmap(cx, cy, 1, 1, NULL);
                if (hbmpMask)
                {
                    // Draw  mask bitmap
                    HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcDst, hbmpMask);
                    PatBlt(hdcDst, 0, 0, cx, cy, WHITENESS);
                    ImageList_Draw(himl, iImage, hdcDst, x, y, ILD_MASK);

                    // Draw image bitmap
                    SelectObject(hdcDst, hbmpImage);
                    ImageList_Draw(himl, iImage, hdcDst, x, y, ILD_NORMAL);

                    SelectObject(hdcDst, hbmpOld);

                    *phbmpImage = hbmpImage;
                    *phbmpMask  = hbmpMask;
                    fRet = TRUE;
                }
                else
                {
                    DeleteObject(hbmpImage);
                }
            }
            DeleteDC(hdcDst);
        }
        ReleaseDC(NULL, hdc);
    }

    return fRet;
}
extern HBITMAP CreateMirroredBitmap( HBITMAP hbmOrig);

//+-------------------------------------------------------------------------
// Creates a special image list for the edit button and configures the edit
// button to use it.  If the hIcon is -1, the edit button is reset to use
// it's default glyph.
//--------------------------------------------------------------------------
void CInternetToolbar::_SetEditGlyph
(
    int iIcon   // new edit button glyph, index into shell image cache
)
{
    // If no toolbar, we just need to see if we need to free the old image lists.
    if (_btb._hwnd == NULL)
    {
        if (iIcon == -1)
        {
            if (_himlEdit)
            {
                ImageList_Destroy(_himlEdit);
                _himlEdit = NULL;
            }
            if (_himlEditHot)
            {
                ImageList_Destroy(_himlEditHot);
                _himlEditHot = NULL;
            }
        }
        else
        {
            // Can't set the glyph if no toolbar!
            ASSERT(FALSE);
        }
        return;
    }

    // Determine the button dimensions
    int cx = g_fSmallIcons ? TB_SMBMP_CX : g_iToolBarLargeIconWidth;
    int cy = g_fSmallIcons ? TB_SMBMP_CY : g_iToolBarLargeIconHeight;


    UINT uiCmd = -1;
    // Dochost merges under one of two clsids, so have to check both
    if (FAILED(_btb._ConvertCmd(&CLSID_InternetButtons, DVIDM_EDITPAGE, NULL, &uiCmd)) &&
        FAILED(_btb._ConvertCmd(&CLSID_MSOButtons, DVIDM_EDITPAGE, NULL, &uiCmd)))
    {
        // The edit button is not on toolbar, so free the edit glyphs
        iIcon = -1;
    }

    // If the current icon is already set, we are done
    if ((_iEditIcon == iIcon) && (_cxEditGlyph == cx) && (_cyEditGlyph == cy))
    {
        if (_himlEdit)
        {
            // Set up the new image lists
            SendMessage(_btb._hwnd, TB_SETIMAGELIST, IL_EDITBUTTON, (LPARAM)_himlEdit);
            if (_himlEditHot)
            {
                SendMessage(_btb._hwnd, TB_SETHOTIMAGELIST, IL_EDITBUTTON, (LPARAM)_himlEditHot);
            }

            // Redirect the edit button to the new image list
            TBBUTTONINFO tbi = {0};
            tbi.cbSize = sizeof(tbi);
            tbi.dwMask = TBIF_IMAGE;
            tbi.iImage = MAKELONG(0, IL_EDITBUTTON);

            SendMessage(_btb._hwnd, TB_SETBUTTONINFO, uiCmd, (LPARAM)&tbi);
        }
        return;
    }

    _iEditIcon = iIcon;
    _cxEditGlyph = cx;
    _cyEditGlyph = cy;

    if (-1 == iIcon)
    {
        if (_himlEdit)
        {
            if (uiCmd != -1)
            {
                // Reset to the original edit glyph
                TBBUTTONINFO tbi = {0};
                tbi.cbSize = sizeof(tbi);
                tbi.dwMask = TBIF_IMAGE;
                tbi.iImage = EDITGLYPH_OFFSET;
                SendMessage(_btb._hwnd, TB_SETBUTTONINFO, uiCmd, (LPARAM)&tbi);
            }

            // Destroy the custom edit glyphs.  Note that we have to reset the primary image list
            // or the image sizes are messed up.
            SendMessage(_btb._hwnd, TB_SETIMAGELIST, IL_EDITBUTTON, (LPARAM)NULL);
            ImageList_Destroy(_himlEdit);
            _himlEdit = NULL;
        }

        if (_himlEditHot)
        {
            SendMessage(_btb._hwnd, TB_SETHOTIMAGELIST, IL_EDITBUTTON, (LPARAM)NULL);
            ImageList_Destroy(_himlEditHot);
            _himlEditHot = NULL;
        }
    }
    else
    {
        // Get the image bitmaps
        HBITMAP hbmpImage = NULL;
        HBITMAP hbmpMask = NULL;
        BOOL bMirrored = IS_WINDOW_RTL_MIRRORED(_btb._hwnd);
        HIMAGELIST himlSmall;
        int cxSmall;
        int cySmall;

        if (Shell_GetImageLists(NULL, &himlSmall) &&
            ImageList_GetIconSize(himlSmall, &cxSmall, &cySmall) &&
            MyImageList_GetBitmaps(himlSmall, iIcon, (cx - cxSmall)/2, (cy - cySmall)/2,
                                   cx, cy, &hbmpImage, &hbmpMask))
        {

            if (bMirrored) 
            {
                HBITMAP hbmpTemp;

                hbmpTemp = CreateMirroredBitmap(hbmpImage);
                if (hbmpTemp)
                {
                    DeleteObject(hbmpImage);
                    hbmpImage = hbmpTemp;
                }
                hbmpTemp = CreateMirroredBitmap(hbmpMask);
                if (hbmpTemp)
                {
                    DeleteObject(hbmpMask);
                    hbmpMask = hbmpTemp;
                }
            }
            // Create a monochrome glyph for the edit button
            HIMAGELIST himlEdit = _CreateGrayScaleImagelist(hbmpImage, hbmpMask);
            SendMessage(_btb._hwnd, TB_SETIMAGELIST, IL_EDITBUTTON, (LPARAM)himlEdit);
            if (_himlEdit)
            {
                ImageList_Destroy(_himlEdit);
            }
            _himlEdit = himlEdit;

            // Create a hot glyph for the edit button
            HIMAGELIST himlEditHot = ImageList_Create(cx, cy, ILC_COLORDDB | ILC_MASK, 1, 1);
            int nIndex = ImageList_Add(himlEditHot, hbmpImage, hbmpMask);

            SendMessage(_btb._hwnd, TB_SETHOTIMAGELIST, IL_EDITBUTTON, (LPARAM)himlEditHot);
            if (_himlEditHot)
            {
                ImageList_Destroy(_himlEditHot);
            }
            _himlEditHot = himlEditHot;

            // Redirect the edit button to the new image list
            if (_himlEdit)
            {
                TBBUTTONINFO tbi = {0};
                tbi.cbSize = sizeof(tbi);
                tbi.dwMask = TBIF_IMAGE;
                tbi.iImage = MAKELONG(nIndex, IL_EDITBUTTON);

                SendMessage(_btb._hwnd, TB_SETBUTTONINFO, uiCmd, (LPARAM)&tbi);
            }

            DeleteObject(hbmpImage);
            DeleteObject(hbmpMask);
               
        }
        else
        {
            // Couldn't create images so use the default edit glyph
            _SetEditGlyph(-1);
        }
    }
}

//+-------------------------------------------------------------------------
// Initializes the edit button to display a drop-down menu if there are
// multiple verbs.  Also optionally displays a custion glyph.
//--------------------------------------------------------------------------
void CInternetToolbar::_InitEditButtonStyle()
{
    // If we have or want a custon edit glyph, load it
    _SetEditGlyph(_aEditVerb.GetIcon());

    UINT uiCmd;

    // Dochost merges under one of two clsids, so have to check both
    if (SUCCEEDED(_btb._ConvertCmd(&CLSID_InternetButtons, DVIDM_EDITPAGE, NULL, &uiCmd)) ||
        SUCCEEDED(_btb._ConvertCmd(&CLSID_MSOButtons, DVIDM_EDITPAGE, NULL, &uiCmd)))
    {
        ASSERT(uiCmd != -1);

        // If multiple verbs, make the button a split button
        TBBUTTONINFO tbi = {0};
        tbi.cbSize = sizeof(tbi);
        tbi.dwMask = TBIF_STYLE | TBIF_STATE;
        tbi.fsState = 0;

        if (_aEditVerb.GetSize() > 1)
        {
            tbi.fsStyle |= BTNS_DROPDOWN;
        }

        if (_aEditVerb.GetSize() > 0)
        {
            tbi.fsState = TBSTATE_ENABLED;
        }
        SendMessage(_btb._hwnd, TB_SETBUTTONINFO, uiCmd, (LPARAM)&tbi);
    }
}

//+-------------------------------------------------------------------------
// If the edit button is displaying a custon glyph, this function reloads
// the glyph.
//--------------------------------------------------------------------------
void CInternetToolbar::_RefreshEditGlyph()
{
    // If we have a custon edit glyph, reload it
    if (_himlEdit)
    {
        // Refresh the edit glyph
        _iEditIcon = -1;
        _InitEditButtonStyle();
    }
}

//+-------------------------------------------------------------------------
// Updates the edit button based on the document type currently loaded
//--------------------------------------------------------------------------
void CInternetToolbar::_UpdateEditButton()
{
    _aEditVerb.RemoveAll();
    _fEditEnabled = FALSE;
    BOOL fNoEditSpecified = FALSE;

    //
    // First add editors associated with the url
    //
    BSTR bstrUrl = NULL;
    _pdie->get_LocationURL(&bstrUrl);
    if (bstrUrl)
    {
        LPTSTR pszExt;
        //
        // Find the cache file associated with the url.  The file extension for this entry
        // is based off of the mime type. (Note that get_mimeType on the document
        // returns a frindly name that is hard to translate back to an actual mimetype.
        // So we use the file extension instead.)
        //
        WCHAR szCacheFileName[MAX_PATH];
        *szCacheFileName = 0;
        if (FAILED(URLToCacheFile(bstrUrl, szCacheFileName, ARRAYSIZE(szCacheFileName))))
        {
            // If we can't get a file associated with the url, probably want to disable the edit button
            // because most apps need a file to edit.
            SysFreeString(bstrUrl);
            return;
        }

        pszExt = PathFindExtension(szCacheFileName);

        // bug 79055 - The cache has a bug where some html entries are not
        // given a file extension.  Too risky to fix for 5.x, so we'll just
        // assume .htm for http if no extension is present.
        if (L'\0' == *pszExt && GetUrlScheme(bstrUrl) == URL_SCHEME_HTTP)
        {
            StrCpyN(szCacheFileName, L".htm", ARRAYSIZE(szCacheFileName));
            pszExt = szCacheFileName;
        }

        if (*pszExt)
        {
            _aEditVerb.Add(pszExt);

            // If ".html", use the ".htm" editors too
            if (StrCmpI(pszExt, L".html") == 0 )
            {
                //  This is an html document, so add the .htm editors
                if (!_aEditVerb.Add(TEXT(".htm")) && StrCmpI(pszExt, L".html") != 0)
                {
                    _aEditVerb.Add(TEXT(".html"));
                }
            }
        }

        SysFreeString(bstrUrl);
    }

    //
    // See if the feature to search the doc for the progid is enabled
    //
    static int fCheckDocForProgID = -1;
    if (fCheckDocForProgID == -1)
    {
        fCheckDocForProgID = SHRegGetBoolUSValue(REGSTR_PATH_MAIN,
                 TEXT("CheckDocumentForProgID"), FALSE, TRUE) ? 1 : 0;
    }

    // Check for a meta tag that specifies a progid for editing this document
    if (fCheckDocForProgID)
    {
        //
        // Next see if this is an html document with a progid
        //
        IWebBrowser2*       pWB2 = NULL;
        IDispatch *         pDispatch = NULL;
        IHTMLDocument2 *    pHTMLDocument = NULL;

        // Get the html document currently loaded
        if (_psp &&
            SUCCEEDED(_psp->QueryService(SID_SWebBrowserApp, IID_PPV_ARG(IWebBrowser2, &pWB2))) &&
            SUCCEEDED(pWB2->get_Document(&pDispatch)) &&
            SUCCEEDED(pDispatch->QueryInterface(IID_PPV_ARG(IHTMLDocument2, &pHTMLDocument))))
        {
            //
            // Check the current document for a META tag specifying the program to use to
            // edit this file.
            //
            BSTR bstrProgID = _GetEditProgID(pHTMLDocument);
            if (bstrProgID)
            {
                if (lstrcmpi(bstrProgID, TEXT("NoEdit")) == 0)
                {
                    fNoEditSpecified = TRUE;
                }
                else
                {
                    USES_CONVERSION;
                    _aEditVerb.Add(W2T(bstrProgID));
                    SysFreeString(bstrProgID);
                }
            }
        }

        SAFERELEASE(pWB2);
        SAFERELEASE(pDispatch);
        SAFERELEASE(pHTMLDocument);
    }


    if (!fNoEditSpecified)
    {
        _fEditEnabled = (_aEditVerb.GetSize() > 0);
    }

    // Update edit glyph, drop-down style, & enabled state
    _InitEditButtonStyle();
}
#endif //EDIT_HACK

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

    switch(dispidMember)
    {

    case DISPID_NAVIGATECOMPLETE2:
    {
        //
        // Notify the brand and theater mode objects about whether we're in shell or
        // web mode. Wait til now to do it (rather than doing it in SetCommandTarget)
        // because they might want to ask the browser about the new pidl, which isn't
        // yet filled in at SetCommandTarget time.
        //
        DWORD nCmdexecopt = _fShellView ? CITE_SHELL : CITE_INTERNET;

        LPBANDITEMDATA pbid = _bs._GetBandItemDataStructByID(CBIDX_BRAND);
        if (pbid)
        {
            IUnknown_Exec(pbid->pdb, &CGID_PrivCITCommands, CITIDM_ONINTERNET, nCmdexecopt, NULL, NULL);
        }

        if (_fTheater)
        {
            IUnknown_Exec(_ptbsite, &CGID_Theater, THID_ONINTERNET, nCmdexecopt, NULL, NULL);
        }

        // If notification is not from a frame, set the _fNavigateComplete flag
        for (DWORD i = 0; i < pdispparams->cArgs; i++)
        {
            if (pdispparams->rgvarg[i].vt == VT_DISPATCH)
            {
                // See who's sending us this event
                IBrowserService* pbs = NULL;
                HRESULT hr = IUnknown_QueryService(pdispparams->rgvarg[i].pdispVal, SID_SShellBrowser, IID_PPV_ARG(IBrowserService, &pbs));
                if (pbs)
                {
                    // We don't really need this interface, just its address
                    pbs->Release();
                }
                if (SUCCEEDED(hr) && pbs == _pbs)
                {
                    // Notification did not come from a frame, 
                    _fNavigateComplete = TRUE;
                }
            }
        }
    }
    break;

    case DISPID_BEFORENAVIGATE:
    {
        BOOL fWeb = FALSE;

        ASSERT((pdispparams->rgvarg[5].vt == VT_BSTR) &&
               (pdispparams->rgvarg[5].bstrVal != NULL));

        PARSEDURL pu = { 0 };
        USES_CONVERSION;

        pu.cbSize = sizeof(pu);
        ParseURL(W2T(pdispparams->rgvarg[5].bstrVal), &pu);

        if ((URL_SCHEME_UNKNOWN != pu.nScheme) && (URL_SCHEME_FILE != pu.nScheme))
            fWeb = TRUE;

        UINT uiState = 0;
        GetState(&CLSID_CommonButtons, TBIDM_STOPDOWNLOAD, &uiState);

        if ((uiState & TBSTATE_HIDDEN) && fWeb)
        {

            _fTransitionToHTML = TRUE;
            uiState &= ~TBSTATE_HIDDEN;
            SetState(&CLSID_CommonButtons, TBIDM_STOPDOWNLOAD, uiState);
        }

        // Default to the edit button hidden
        _fEditEnabled = FALSE;
    }
    break;

    case DISPID_DOWNLOADBEGIN:// This is when we just started to navigate?  No bits?
        _StartDownload();
        break;

    case DISPID_DOWNLOADCOMPLETE:    // we be done
        _fTransitionToHTML = FALSE;
        _StopDownload(FALSE);
        break;

    case DISPID_DOCUMENTCOMPLETE:   // This is where we have all the bits
    {
        //
        // Sometimes we get a premature document complete (for framesets).  We can catch this
        // by checking to see if we have received a DISPID_NAVIGATECOMPLETE2 event
        // for the top window.  We have to update the edit button here instead of in 
        // navigate complete because otherwise the document is not in the interactive
        // state and our metatag search sees the previous document. 
        //
        // REARCHITECT: Is it possible that this event came from a frame and the document is not
        // interactive yet? Trident posts an interactive event right before calling us so
        // we're probably ok.  We don't want to wait for the document complete for the top window
        // in framesets because it can take too long.  Really need to sink DISPID_READYSTATECHANGE
        // and wait for the document to go interactive.
        //
        if (_fNavigateComplete)
        {
            _fNavigateComplete = FALSE;
            _UpdateEditButton();
        }
        break;
    }

    case DISPID_COMMANDSTATECHANGE:
        BOOL fEnable;

        if(!pdispparams || (pdispparams->cArgs != 2) ||
           (pdispparams->rgvarg[0].vt != VT_BOOL) ||
           (pdispparams->rgvarg[1].vt != VT_I4))
            return E_INVALIDARG;

        fEnable = (BOOL) pdispparams->rgvarg[0].boolVal;
        UINT uiCmd;

        switch (pdispparams->rgvarg[1].lVal)
        {
        case CSC_UPDATECOMMANDS:
            // corresponds to OLECMDID_UPDATECOMMANDS from Exec()
            _UpdateToolbar(FALSE);
            break;

        case CSC_NAVIGATEBACK:
            _fBackEnabled = fEnable;
            _btb._ConvertCmd(&CLSID_CommonButtons, TBIDM_BACK, NULL, &uiCmd);
            SendMessage(_btb._hwnd, TB_ENABLEBUTTON, uiCmd,    MAKELONG(fEnable, 0));
            break;

        case CSC_NAVIGATEFORWARD:
            _fForwardEnabled = fEnable;
            _btb._ConvertCmd(&CLSID_CommonButtons, TBIDM_FORWARD, NULL, &uiCmd);
            SendMessage(_btb._hwnd, TB_ENABLEBUTTON, uiCmd, MAKELONG(fEnable, 0));
            break;

        default:
            return(E_INVALIDARG);
        }

        // FEATURE need to handle the case of navigation failure and
        // do some cleanup

    }

    return S_OK;
}

// *** IInputObjectSite methods ***

HRESULT CInternetToolbar::OnFocusChangeIS(IUnknown *punk, BOOL fSetFocus)
{
    return IUnknown_OnFocusChangeIS(_ptbsite, SAFECAST(this, IInputObject*), fSetFocus);
}


//***   CInternetToolbar::IInputObject::* {

HRESULT CInternetToolbar::TranslateAcceleratorIO(LPMSG lpMsg)
{
    LPBANDITEMDATA pbid;

    if (_fShow)
    {
        if (lpMsg->message == WM_KEYDOWN)
        {
            switch (lpMsg->wParam)
            {
            case VK_F4:
        Laddrband:
                if (_nVisibleBands & VBF_ADDRESS)
                {
                    pbid = _bs._GetBandItemDataStructByID(CBIDX_ADDRESS);
                    if (EVAL(pbid))
                    {
                        HRESULT hrT;

                        hrT = IUnknown_TranslateAcceleratorIO(pbid->pdb, lpMsg);
                        ASSERT(hrT == S_OK);
                    }
                }
                return S_OK;    // (even if we just eat it)
            }
        }
        else if(lpMsg->message == WM_SYSCHAR)
        {
            static CHAR szAccel[2] = "\0";
            CHAR   szChar [2] = "\0";

            if ('\0' == szAccel[0])
                MLLoadStringA(IDS_ADDRBAND_ACCELLERATOR, szAccel, ARRAYSIZE(szAccel));

            szChar[0] = (CHAR)lpMsg->wParam;
            
            if (lstrcmpiA(szChar,szAccel) == 0)
            {
                goto Laddrband;
            }
        }
        return _bs.TranslateAcceleratorIO(lpMsg);
    }
    return S_FALSE;
}


// }

HRESULT CInternetToolbar::SetSite(IUnknown* punkSite)
{
    ATOMICRELEASE(_ptbsite);
    ATOMICRELEASE(_ptbsitect);
    ATOMICRELEASE(_pbs);
    ATOMICRELEASE(_pbs2);
    ATOMICRELEASE(_psp);

    _Unadvise();

    ATOMICRELEASE(_pdie);

    ASSERT(_ptbsite==NULL);
    ASSERT(_ptbsitect==NULL);
    ASSERT(_pbs==NULL);
    ASSERT(_pbs2==NULL);
    ASSERT(_pdie==NULL);

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

    if (punkSite)
    {
        punkSite->QueryInterface(IID_PPV_ARG(IDockingWindowSite, &_ptbsite));
        punkSite->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &_ptbsitect));
        punkSite->QueryInterface(IID_PPV_ARG(IBrowserService2, &_pbs2));
        punkSite->QueryInterface(IID_PPV_ARG(IServiceProvider, &_psp));

        if (_psp)
        {
            _psp->QueryService(SID_SWebBrowserApp, IID_PPV_ARG(IWebBrowser2, &_pdie));
            _psp->QueryService(SID_SShellBrowser, IID_PPV_ARG(IBrowserService, &_pbs));
            ASSERT(_pdie);
        }
        else
        {
            ASSERT(0);
        }

    }
    else
    {
        SetClient(NULL);
    }


    return S_OK;
}


//***
//
void CInternetToolbar::_UpdateGroup(const GUID *pguidCmdGroup, int cnt,
    OLECMD rgcmds[], const GUID* pguidButton, const int buttonsInternal[])
{

    if (!IsEqualGUID(*pguidButton, CLSID_CommonButtons) &&
        !IsEqualGUID(*pguidButton, _btb._guidCurrentButtonGroup))
        return; // we don't have any buttons at this time, so no use checking

    if (_ptbsitect)
    {
        _ptbsitect->QueryStatus(pguidCmdGroup, cnt, rgcmds, NULL);

        // make sure stop is enabled while we are animating
        if (_fAnimating && pguidCmdGroup == NULL && rgcmds[0].cmdID == OLECMDID_STOP)
        {
            rgcmds[0].cmdf = OLECMDF_ENABLED;
        }
    }

    for (int i = 0; i < cnt; i++)
    {
        // do nothing if command is not available or not in our table
        if (rgcmds[i].cmdf & OLECMDF_SUPPORTED)
        {
            UINT idBut;
            if (SUCCEEDED(_btb._ConvertCmd(pguidButton, buttonsInternal[i], NULL, (UINT*)&idBut)))
            {
                SendMessage(_btb._hwnd, TB_ENABLEBUTTON, idBut,
                    (rgcmds[i].cmdf & OLECMDF_ENABLED) ? TRUE : FALSE);

                SendMessage(_btb._hwnd, TB_CHECKBUTTON, idBut,
                    (rgcmds[i].cmdf & OLECMDF_LATCHED) ? TRUE : FALSE);
            }
        }
    }
    return;
}

void CInternetToolbar::_UpdateToolbar(BOOL fForce)
{
    if (fForce || SHIsChildOrSelf(GetForegroundWindow(), _hwnd) == S_OK)
    {
        if (!_fUpdateToolbarTimer)
        {
            SetTimer(_hwnd, IDT_UPDATETOOLBAR, TIMEOUT_UPDATETOOLBAR, NULL);
            _fUpdateToolbarTimer = TRUE;
            _UpdateToolbarNow();
        }
        else
        {
            _fNeedUpdateToolbar = TRUE;
        }
    }
}

BOOL CInternetToolbar::_UpEnabled()
{
    OLECMD rgcmd = { FCIDM_PREVIOUSFOLDER, 0 };
    _ptbsitect->QueryStatus(&CGID_ShellBrowser, 1, &rgcmd, NULL);

    return (rgcmd.cmdf & OLECMDF_ENABLED);
}

void CInternetToolbar::_UpdateCommonButton(int iCmd, UINT nCmdID)
{
    switch (nCmdID)
    {
    case TBIDM_THEATER:
        SendMessage(_btb._hwnd, TB_CHECKBUTTON, iCmd, _fTheater);
        break;

    case TBIDM_PREVIOUSFOLDER:
    case TBIDM_BACK:
    case TBIDM_FORWARD:
        {
            BOOL fEnabled;

            switch (nCmdID)
            {
            case TBIDM_PREVIOUSFOLDER:  fEnabled = _UpEnabled();       break;
            case TBIDM_BACK:            fEnabled = _fBackEnabled;      break;
            case TBIDM_FORWARD:         fEnabled = _fForwardEnabled;   break;
            }

            SendMessage(_btb._hwnd, TB_ENABLEBUTTON, iCmd, MAKELONG(fEnabled, 0));
        }
        break;
    }
}

void CInternetToolbar::_UpdateToolbarNow()
{
    _fNeedUpdateToolbar = FALSE;

    {
        // MUST not be static (due to ConvertCmd overwrite)
        OLECMD rgcmds[] = {
            { OLECMDID_STOP, 0 }, // NOTE: must be first
            { OLECMDID_REFRESH, 0 },
        };

        static const int buttonsInternal[] = { // MUST be in same order as above array
            TBIDM_STOPDOWNLOAD,
            TBIDM_REFRESH,
        };
        _UpdateGroup(NULL, ARRAYSIZE(buttonsInternal), rgcmds, &CLSID_CommonButtons, buttonsInternal);
    }

    {
        OLECMD rgcmds[] = {
            { SBCMDID_SEARCHBAR, 0 },
            { SBCMDID_FAVORITESBAR, 0 },
            { SBCMDID_HISTORYBAR, 0 },
            { SBCMDID_EXPLORERBAR, 0 },
            { SBCMDID_MEDIABAR, 0 },
        };
        static const int buttonsInternal[] = { // MUST be in same order as above array
            TBIDM_SEARCH,
            TBIDM_FAVORITES,
            TBIDM_HISTORY,
            TBIDM_ALLFOLDERS,
            TBIDM_MEDIABAR,
        };

        _UpdateGroup(&CGID_Explorer, ARRAYSIZE(buttonsInternal), rgcmds, &CLSID_CommonButtons, buttonsInternal);
    }

    int nButtons = (int) SendMessage(_btb._hwnd, TB_BUTTONCOUNT, 0, 0L);

    for (int nIndex = 0; nIndex < nButtons; nIndex++)
    {
        CMDMAP* pcm = _btb._GetCmdMapByIndex(nIndex);
        if (pcm)
        {
            int iCmd = _btb._CommandFromIndex(nIndex);
            if (IsEqualGUID(pcm->guidButtonGroup, CLSID_CommonButtons))
            {
                _UpdateCommonButton(iCmd, pcm->nCmdID);
            }
            else
            {
                // NOTE (andrewgu): ie5.5 b#106047 - the two conditions below used to be ASSERTs,
                // the second one was faulting under stress. if either one of these checks fails,
                // the button is stale.
                if (IsEqualGUID(pcm->guidButtonGroup, _btb._guidCurrentButtonGroup) &&
                    NULL != _btb._pctCurrentButtonGroup)
                {
                    OLECMD ocButton;
                    ocButton.cmdID = pcm->nCmdID;
                    ocButton.cmdf = 0;

                    if (SUCCEEDED(_btb._pctCurrentButtonGroup->QueryStatus(&pcm->guidButtonGroup, 1, &ocButton, NULL)))
                    {
                        SendMessage(_btb._hwnd, TB_ENABLEBUTTON, iCmd,
                                    (ocButton.cmdf & OLECMDF_ENABLED) ? TRUE : FALSE);

                        SendMessage(_btb._hwnd, TB_CHECKBUTTON, iCmd,
                                    (ocButton.cmdf & OLECMDF_LATCHED) ? TRUE : FALSE);
                    }
                }
            }
        }
    }

    if (_btb._hwnd)
    {
        _btb._BandInfoChanged();
    }
}

void CInternetToolbar::_StartDownload()
{
    UINT uiCmd;
    if (SUCCEEDED(_btb._ConvertCmd(&CLSID_CommonButtons, TBIDM_STOPDOWNLOAD, NULL, &uiCmd)))
    {
        SendMessage(_btb._hwnd, TB_ENABLEBUTTON, uiCmd, MAKELONG(TRUE, 0));

        _fAnimating = TRUE;
    }
}

//
// Parameters:
//  fClosing -- TRUE only if we are calling this from CloseDW member.
//              In that case, we can skip all UI-update code.
//
void CInternetToolbar::_StopDownload(BOOL fClosing)
{
    _fAnimating = FALSE;
}

HRESULT CInternetToolbar::CloseDW(DWORD dwReserved)
{
    _fDestroyed = TRUE; // Stop using the member variables, they are invalid.
    _StopDownload(TRUE);

    ASSERT(!_btb._pcinfo);
    ATOMICRELEASE(_btb._pctCurrentButtonGroup);

    _btb._FreeBtnsAdded();

    if (_btb._hwnd)
    {
        _btb._RemoveAllButtons();

        SendMessage(_btb._hwnd, TB_SETIMAGELIST, 0, NULL);
        SendMessage(_btb._hwnd, TB_SETHOTIMAGELIST, 0, NULL);

        DSA_Destroy(_hdsaTBBMPs);
        _hdsaTBBMPs = NULL;  // So we don't try to re-destroy in _InitBitmapDSA()
    }
#ifdef EDIT_HACK
    _SetEditGlyph(-1);
#endif

    _bs._Close();

    SUPERCLASS::CloseDW(dwReserved);

    _btb._hwnd = NULL;

    // We advise during ShowDW, so unadvise here. Also, we hit a stress
    // case where it seems that an event came in after closedw but before
    // one of the other _Unadvise calls. This event percolated down to
    // a reference to _hdsaCT which we freed above, causing a GPF.
    //
    _Unadvise();

    return S_OK;
}

void CInternetToolbar::CITBandSite::v_SetTabstop(LPREBARBANDINFO prbbi)
{
    // Don't set tabstops for all bands in the browser case.  A band
    // can still make itself a tabstop by setting WS_TABSTOP.
    return;
}

BOOL CInternetToolbar::CITBandSite::_SetMinDimensions()
{
    INT_PTR fRedraw = SendMessage(_hwnd, WM_SETREDRAW, FALSE, 0);

    int icBands = (int) SendMessage( _hwnd, RB_GETBANDCOUNT, 0, 0 );
    for (int i = 0; i < icBands; i++)
    {
        REBARBANDINFO rbbi;
        rbbi.cbSize = sizeof(REBARBANDINFO);
        rbbi.fMask = RBBIM_ID | RBBIM_CHILDSIZE;
        if (SendMessage(_hwnd, RB_GETBANDINFO, i, (LPARAM) &rbbi))
        {
            LPBANDITEMDATA pbid = (LPBANDITEMDATA)_GetBandItemDataStructByID(rbbi.wID);
            if (EVAL(pbid) && IS_VALID_HANDLE(pbid->hwnd, WND))
            {
                rbbi.cxMinChild = pbid->ptMinSize.x;
                rbbi.cyMinChild = pbid->ptMinSize.y;
            }
            else
            {
                rbbi.cxMinChild = 0;
                rbbi.cyMinChild = 0;
            }

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

    SendMessage(_hwnd, WM_SETREDRAW, fRedraw, 0);

    return TRUE;
}


BOOL HimlCacheDirty(IMLCACHE* pimlCache, BOOL fSmallIcons)
{

    if (fSmallIcons != pimlCache->fSmallIcons)
        return TRUE;

    COLORREF cr3D = GetSysColor(COLOR_3DFACE);

    if (cr3D != pimlCache->cr3D)
        return TRUE;

    if (SHUseClassicToolbarGlyphs() != pimlCache->fUseClassicGlyphs)
        return TRUE;

    for (int i = 0; i < CIMLISTS; i++)
        if (!pimlCache->arhiml[i])
            return TRUE;

    return FALSE;
}


#define SZ_REGKEY_SMALLICONS       REGSTR_PATH_EXPLORER TEXT("\\SmallIcons")
#define SZ_REGVALUE_SMALLICONS     TEXT("SmallIcons")

BOOL _DefaultToSmallIcons()
{
    // We default to small icons if:
    //
    // This is NT 5, or the policy says to use small icons, or this is any
    // of the Whistler servers (server, adv server, dtc)

    return ((GetUIVersion() == 5) || SHRestricted2(REST_SMALLICONS, NULL, 0) ||
            (IsOS(OS_WHISTLERORGREATER) && IsOS(OS_ANYSERVER)));
}

BOOL _UseSmallIcons()
{
    BOOL fDefaultToSmall = _DefaultToSmallIcons();

    return SHRegGetBoolUSValue(SZ_REGKEY_SMALLICONS, SZ_REGVALUE_SMALLICONS,
                                        FALSE, fDefaultToSmall);
}


BOOL _UseMapNetDrvBtns()
{
#define SZ_REGKEY_ADVFOLDER        TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced")
#define SZ_REGVALUE_MAPNETDRV      TEXT("MapNetDrvBtn")

    DWORD dwData = 0;
    if (GetUIVersion() >= 4)
    {
        DWORD cbData = sizeof(dwData);
        DWORD dwDefault = 0;
        DWORD cbDefault = sizeof(dwDefault);

        SHRegGetUSValue(SZ_REGKEY_ADVFOLDER, SZ_REGVALUE_MAPNETDRV, NULL,
                            &dwData, &cbData, FALSE, &dwDefault, cbDefault);
    }
    return dwData;
}

HIMAGELIST _LoadThemedToolbarGlyphs(int idBmpType, int iTemperature, int cx, COLORREF crMask, UINT uFlags, BOOL bUseClassicGlyphs, HINSTANCE hInst);

void _LoadToolbarGlyphs(HWND hwnd, IMLCACHE *pimlCache, int cx, int idBmpType,
                        int iBitmapBaseIndex, BOOL bUseClassicGlyphs, HINSTANCE hInst)
{
    // set uMsg and uFlags for first iteration of loop (default state)
    UINT uMsg = TB_SETIMAGELIST;
    UINT uFlags = LR_CREATEDIBSECTION;
    int i;
    HBITMAP hBMP;
    BOOL fSmallIcons = g_fSmallIcons;

    if (HimlCacheDirty(pimlCache, fSmallIcons))
    {
        COLORREF cr3D   = GetSysColor(COLOR_3DFACE);
        COLORREF crMask = RGB( 255, 0, 255 );

#ifdef UNIX
        if (SHGetCurColorRes() < 2 )
        {
            crMask = CLR_NONE;
        }
#endif

        ENTERCRITICAL;

        if (!HimlCacheDirty(pimlCache, fSmallIcons) )
            goto DontReload;

        for (i = 0; i < CIMLISTS; i++)
        {
            if ((!pimlCache->arhiml[i]) || (cr3D != pimlCache->cr3D) ||
                (fSmallIcons != pimlCache->fSmallIcons) || (bUseClassicGlyphs != pimlCache->fUseClassicGlyphs))
            {
                TraceMsg(DM_ITBAR, "_LoadToolbarGlyphs: Loading New Images");

                if (pimlCache->arhimlPendingDelete[i])
                    ImageList_Destroy(pimlCache->arhimlPendingDelete[i]);

                pimlCache->arhimlPendingDelete[i] = pimlCache->arhiml[i];

                pimlCache->arhiml[i] = _LoadThemedToolbarGlyphs(idBmpType, i, cx, crMask, uFlags, bUseClassicGlyphs, hInst);

                if (pimlCache->arhiml[i])
                {
                    // add shell glyphs
                    int idShellBmp = iBitmapBaseIndex + idBmpType;
                    hBMP = (HBITMAP) LoadImage (hInst, MAKEINTRESOURCE(idShellBmp + i), IMAGE_BITMAP,
                                      0, 0, uFlags);

                    ImageList_AddMasked(pimlCache->arhiml[i], (HBITMAP)hBMP, crMask);

                    DeleteObject(hBMP);
                }
            }
        }
        pimlCache->cr3D = cr3D;
        pimlCache->fSmallIcons = fSmallIcons;
        pimlCache->fUseClassicGlyphs = bUseClassicGlyphs;
DontReload:
        LEAVECRITICAL;
    }

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

        for (i = 0; i < CIMLISTS; i++)
        {
            SendMessage(hwnd, uMsg, 0, (LPARAM) pimlCache->arhiml[i]);

            // set uMsg and uFlags for last iteration of loop (hot state)
            uMsg = TB_SETHOTIMAGELIST;
            uFlags = 0;
        }
    }
}

#ifdef THEME_BUTTONS
BOOL _GetThemeSetting(HKEY hkey, PDWORD pdwSetting)
{
    ASSERT(pdwSetting);
    BOOL fRet = FALSE;
    DWORD dwType, dwcbData = sizeof(*pdwSetting);
    *pdwSetting = 0;
    if (ERROR_SUCCESS==SHGetValue(hkey, 
                                  c_szRegKeyCoolbar, TEXT("UseTheme"), &dwType, pdwSetting, &dwcbData))
    {
        // We going to assume that the data type and size is correct. This way, we can break
        // into an error state.
        fRet = TRUE;
        // Acceptable values:
        // 0: use IE6 icons
        // 1: use IE5.5 icons
        // 2: use themed icons
        // other values are reserved.
        if (*pdwSetting > 2)
        {
            *pdwSetting = 0;
        }
    }
    return fRet;
}


HIMAGELIST _LoadThemedToolbarGlyphs(int idBmpType, int iTemperature, int cx, COLORREF crMask,
                                    UINT uFlags, BOOL bUseClassicGlyphs, HINSTANCE hInst)
{
    HIMAGELIST himl = NULL;
    // Restriction goes here.

    HKEY hkey = HKEY_CURRENT_USER;
    DWORD dwSetting = 0;
    if (!_GetThemeSetting(hkey, &dwSetting))
    {
        hkey = HKEY_LOCAL_MACHINE;
        _GetThemeSetting(hkey, &dwSetting);
    }

    DWORD dwType, dwcbData;
    if (dwSetting==2)
    {
        TCHAR szPath[MAX_PATH];
        TCHAR szItem[] = TEXT("ITBx");
        szItem[3] = TEXT('0') + idBmpType + iTemperature;

        dwcbData = sizeof(szPath);
        if ((ERROR_SUCCESS==SHGetValue(hkey, 
                                       c_szRegKeyCoolbar, szItem, &dwType, szPath, &dwcbData))
            && (dwType==REG_SZ))
        {
            int nBmpIndex = PathParseIconLocation(szPath);

            WCHAR szExpPath[MAX_PATH];
            SHExpandEnvironmentStrings(szPath, szExpPath, ARRAYSIZE(szExpPath));

            // If no resource id, assume it's a bmp file
            if (nBmpIndex==0)
            {
                himl = CreateImageList(NULL,
                                       szExpPath, cx, 0, crMask,
                                       IMAGE_BITMAP, uFlags | LR_LOADFROMFILE,
                                       !bUseClassicGlyphs);
            }

            // Otherwise, see if it's a resouce
            if (!himl)
            {
                HINSTANCE hInst = LoadLibraryEx(szExpPath, NULL, LOAD_LIBRARY_AS_DATAFILE);
                if (hInst)
                {
                    himl = CreateImageList(hInst,
                                           MAKEINTRESOURCE(nBmpIndex), cx, 0, crMask,
                                           IMAGE_BITMAP, uFlags, !bUseClassicGlyphs);
                    FreeLibrary(hInst);
                }
            }

            if (himl)
            {
                // You just can't trust anyone these days. If the graphics aren't the expected 
                // size, or there aren't the expected number, we're going to use the defaults

                // ISSUE: 16 is a magic number. Do we have a constant anywhere? I can't find it.
                int ecx, ecy;
                if (!(ImageList_GetIconSize(himl, &ecx, &ecy)
                    && (ecx==ecy)
                    && (ecy==cx)
                    && (ImageList_GetImageCount(himl)==16)))
                {
                    ImageList_Destroy(himl);
                    himl = NULL;
                }
            }
        }
    }

    if (!himl)
    {
        // dwSetting must be 1 to get IE6 icons. If dwSetting isn't set to 1, then default
        // either to the new Whistler icons or the default IE icons (as appropriate)
        int iResource;

        if (dwSetting == 1)
        {
            iResource = IDB_IE6_TOOLBAR;
        }
        else
        {
            if (bUseClassicGlyphs)
            {
                iResource = IDB_IETOOLBAR;
            }
            else
            {
                iResource = IDB_TB_IE_BASE;
            }
        }

        iResource += (idBmpType + iTemperature);

        himl = CreateImageList(hInst,
                               MAKEINTRESOURCE(iResource), cx, 0, crMask,
                               IMAGE_BITMAP, uFlags, !bUseClassicGlyphs);
    }
    return himl;
}

#else
HIMAGELIST _LoadThemedToolbarGlyphs(int idBmpType, int iTemperature, int cx, COLORREF crMask,
                                    UINT uFlags, BOOL bUseClassicGlyphs, HINSTANCE hInst)
{
    HIMAGELIST himl = NULL;
    int iResource;
    if (bUseClassicGlyphs)
    {
        iResource = IDB_IETOOLBAR;
    }
    else
    {
        iResource = IDB_TB_IE_BASE;
    }
    iResource += (idBmpType + iTemperature);

    himl = CreateImageList(hInst,
                           MAKEINTRESOURCE(iResource), cx, 0, crMask,
                           IMAGE_BITMAP, uFlags, !bUseClassicGlyphs);
    return himl;
}

#endif

void CInternetToolbar::_InitBitmapDSA()
{
    DSA_Destroy(_hdsaTBBMPs);
    _hdsaTBBMPs = DSA_Create(sizeof(TBBMP_LIST), TBBMPLIST_CHUNK);

    if (_hdsaTBBMPs)
    {
        TBBMP_LIST tbl = { HINST_COMMCTRL, 0, 0, TRUE, TRUE, FALSE };

        tbl.uiResID = IDB_STD_SMALL_COLOR;
        tbl.uiOffset = OFFSET_STD;
        DSA_AppendItem(_hdsaTBBMPs, &tbl);
        tbl.uiResID = IDB_STD_LARGE_COLOR;
        DSA_AppendItem(_hdsaTBBMPs, &tbl);

        tbl.uiResID = IDB_VIEW_SMALL_COLOR;
        tbl.uiOffset = OFFSET_VIEW;
        DSA_AppendItem(_hdsaTBBMPs, &tbl);
        tbl.uiResID = IDB_VIEW_LARGE_COLOR;
        DSA_AppendItem(_hdsaTBBMPs, &tbl);

        tbl.uiResID = IDB_HIST_SMALL_COLOR;
        tbl.uiOffset = OFFSET_HIST;
        DSA_AppendItem(_hdsaTBBMPs, &tbl);
        tbl.uiResID = IDB_HIST_LARGE_COLOR;
        DSA_AppendItem(_hdsaTBBMPs, &tbl);
    }
}


void CInternetToolbar::_ReloadBitmapDSA()
{
    if (_hdsaTBBMPs)
    {
        INT i = 6; 
        TBBMP_LIST * pTBBs = NULL;
        int nCount = DSA_GetItemCount(_hdsaTBBMPs);
        // We want to skip the first 6 entries in the DSA, which are added by InitBitmapDSA
        for (int nIndex = 6; nIndex < nCount; nIndex++)
        {
            pTBBs = (TBBMP_LIST*)DSA_GetItemPtr(_hdsaTBBMPs, nIndex);
            if (pTBBs)
            {
                HIMAGELIST himl = NULL;
                if (pTBBs->fNormal)
                {
                    himl = (HIMAGELIST)SendMessage(_btb._hwnd, TB_GETIMAGELIST, 0, 0L);
                    if (himl
                        && (pTBBs->uiOffset==ImageList_GetImageCount(himl)))
                    {
                        LRESULT lOffset = _AddBitmapFromForeignModule(TB_GETIMAGELIST, TB_SETIMAGELIST, pTBBs->uiCount, pTBBs->hInst, pTBBs->uiResID, RGB(192,192,192));
                        ASSERT(pTBBs->uiOffset==lOffset);
                    }
                }
                if (pTBBs->fHot)
                {
                    himl = (HIMAGELIST)SendMessage(_btb._hwnd, TB_GETHOTIMAGELIST, 0, 0L);
                    if (himl
                        && (pTBBs->uiOffset==ImageList_GetImageCount(himl)))
                    {
                        LRESULT lOffset = _AddBitmapFromForeignModule(TB_GETHOTIMAGELIST, TB_SETHOTIMAGELIST, pTBBs->uiCount, pTBBs->hInst, pTBBs->uiResID, RGB(192,192,192));
                        ASSERT(pTBBs->uiOffset==lOffset);
                    }
                }
                if (pTBBs->fDisabled)
                {
                    himl = (HIMAGELIST)SendMessage(_btb._hwnd, TB_GETDISABLEDIMAGELIST, 0, 0L);
                    if (himl
                        && (pTBBs->uiOffset==ImageList_GetImageCount(himl)))
                    {
                        LRESULT lOffset = _AddBitmapFromForeignModule(TB_GETDISABLEDIMAGELIST, TB_SETDISABLEDIMAGELIST, pTBBs->uiCount, pTBBs->hInst, pTBBs->uiResID, RGB(192,192,192));
                        ASSERT(pTBBs->uiOffset==lOffset);
                    }
                }
            }
        }
    }
}


void CInternetToolbar::_InitForScreenSize()
{
    TCHAR szScratch[16];
    if (GetSystemMetrics(SM_CXSCREEN) < 650) 
    {
        MLLoadString(IDS_TB_WIDTH_EXTRA_LORES, szScratch, ARRAYSIZE(szScratch));
        _uiMaxTBWidth = MAX_TB_WIDTH_LORES;
    } 
    else 
    {
        MLLoadString(IDS_TB_WIDTH_EXTRA_HIRES, szScratch, ARRAYSIZE(szScratch));
        _uiMaxTBWidth = MAX_TB_WIDTH_HIRES;
    }
    _uiMaxTBWidth += StrToInt(szScratch) * WIDTH_FACTOR;
}


// removes all buttons marked hidden.  returns the number
// of buttons left
int RemoveHiddenButtons(TBBUTTON* ptbn, int iCount)
{
    int i;
    int iTotal = 0;
    TBBUTTON* ptbn1 = ptbn;
    for (i = 0; i < iCount; i++, ptbn1++) 
    {
        if (!(ptbn1->fsState & TBSTATE_HIDDEN)) 
        {
            if (ptbn1 != ptbn) 
            {
                *ptbn = *ptbn1;
            }
            ptbn++;
            iTotal++;
        }
    }
    return iTotal;
}

#ifdef DEBUG
void _AssertRestrictionOrderIsCorrect()
{
    COMPILETIME_ASSERT(ARRAYSIZE(c_tbExplorer) == ARRAYSIZE(c_rest));

    for (UINT i = 0; i < ARRAYSIZE(c_tbExplorer); i++)
    {
        // If any of these rip, it means that c_rest and c_tbExplorer have
        // gotten out of sync.  Need to fix up c_rest to match c_tbExplorer.
        switch (c_tbExplorer[i].idCommand)
        {
            case TBIDM_BACK:            ASSERT(c_rest[i] == REST_BTN_BACK);         break;
            case TBIDM_FORWARD:         ASSERT(c_rest[i] == REST_BTN_FORWARD);      break;
            case TBIDM_STOPDOWNLOAD:    ASSERT(c_rest[i] == REST_BTN_STOPDOWNLOAD); break;
            case TBIDM_REFRESH:         ASSERT(c_rest[i] == REST_BTN_REFRESH);      break;
            case TBIDM_HOME:            ASSERT(c_rest[i] == REST_BTN_HOME);         break;
            case TBIDM_SEARCH:          ASSERT(c_rest[i] == REST_BTN_SEARCH);       break;
            case TBIDM_HISTORY:         ASSERT(c_rest[i] == REST_BTN_HISTORY);      break;
            case TBIDM_FAVORITES:       ASSERT(c_rest[i] == REST_BTN_FAVORITES);    break;
            case TBIDM_ALLFOLDERS:      ASSERT(c_rest[i] == REST_BTN_ALLFOLDERS);   break;
            case TBIDM_THEATER:         ASSERT(c_rest[i] == REST_BTN_THEATER);      break;
            case TBIDM_MEDIABAR:        ASSERT(c_rest[i] == REST_BTN_MEDIABAR);     break;
            default:                    ASSERT(c_rest[i] == REST_BROWSER_NONE);     break;
        }
    }
}
#endif

__inline BOOL CInternetToolbar::_FoldersButtonAvailable()
{
    return (GetUIVersion() >= 4);
}

void CInternetToolbar::_AdminMarkDefaultButtons(PTBBUTTON ptbb, UINT cButtons)
{
    // We only have policies for web buttons.
    ASSERT(!_fShellView);

    // Caller should have checked this.
    ASSERT(SHRestricted2(REST_SPECIFYDEFAULTBUTTONS, NULL, 0));


    // SHRestricted2 returns 0 if it can't find the policy.  Assert that
    // this lines up with RESTOPT_BTN_STATE_DEFAULT.
    COMPILETIME_ASSERT(RESTOPT_BTN_STATE_DEFAULT == 0);

    for (UINT i = 0; i < cButtons; i++) 
    {
        if (c_rest[i] != 0)
        {
            DWORD dwRest = SHRestricted2(c_rest[i], NULL, 0);
            ptbb[i].fsState = SHBtnStateFromRestriction(dwRest, ptbb[i].fsState);
        }
    }

    // Folders button is not available on non-integrated platforms, so
    // set state to hidden even if policy specifies that it should be shown.
    ASSERT(c_tbExplorer[TBXID_ALLFOLDERS].idCommand == TBIDM_ALLFOLDERS);
    if (!_FoldersButtonAvailable())
        ptbb[TBXID_ALLFOLDERS].fsState |= TBSTATE_HIDDEN;
}

void CInternetToolbar::_MarkDefaultButtons(PTBBUTTON ptbb, UINT cButtons)
{
    if (SHRestricted(REST_NONLEGACYSHELLMODE))
    {
        ASSERT(ptbb[TBXID_BACK].idCommand == TBIDM_BACK);
        ptbb[TBXID_BACK].fsState |= TBSTATE_HIDDEN;
        ASSERT(ptbb[TBXID_FORWARD].idCommand == TBIDM_FORWARD);
        ptbb[TBXID_FORWARD].fsState |= TBSTATE_HIDDEN;
    }

    if (_fShellView) 
    {
        ASSERT(c_tbExplorer[TBXID_STOPDOWNLOAD].idCommand == TBIDM_STOPDOWNLOAD);
        ptbb[TBXID_STOPDOWNLOAD].fsState |= TBSTATE_HIDDEN;
        ASSERT(c_tbExplorer[TBXID_REFRESH].idCommand == TBIDM_REFRESH);
        ptbb[TBXID_REFRESH].fsState |= TBSTATE_HIDDEN;
        ASSERT(c_tbExplorer[TBXID_HOME].idCommand == TBIDM_HOME);
        ptbb[TBXID_HOME].fsState |= TBSTATE_HIDDEN;

        ASSERT(c_tbExplorer[TBXID_SEARCH].idCommand == TBIDM_SEARCH);
        ASSERT(c_tbExplorer[TBXID_HISTORY].idCommand == TBIDM_HISTORY);
        ASSERT(c_tbExplorer[TBXID_SEPARATOR2].idCommand == 0);    // (a separator)

        if (GetUIVersion() < 5) 
        {
            ptbb[TBXID_SEARCH].fsState |= TBSTATE_HIDDEN;
            ptbb[TBXID_HISTORY].fsState |= TBSTATE_HIDDEN;
            ptbb[TBXID_SEPARATOR2].fsState |= TBSTATE_HIDDEN;
        }
        else
        {
            if (GetUIVersion() > 5)
            {
                ptbb[TBXID_HISTORY].fsState |= TBSTATE_HIDDEN;
            }

            if (SHRestricted(REST_NOSHELLSEARCHBUTTON))
            {
                ptbb[TBXID_SEARCH].fsState |= TBSTATE_HIDDEN;
            }
            ASSERT(c_tbExplorer[TBXID_CONNECT].idCommand == TBIDM_CONNECT);
            ASSERT(c_tbExplorer[TBXID_DISCONNECT].idCommand == TBIDM_DISCONNECT);
            if (SHRestricted(REST_NONETCONNECTDISCONNECT))
            {
                ptbb[TBXID_CONNECT].fsState |= TBSTATE_HIDDEN;
                ptbb[TBXID_DISCONNECT].fsState |= TBSTATE_HIDDEN;
            }
        }

        ASSERT(c_tbExplorer[TBXID_FAVORITES].idCommand == TBIDM_FAVORITES);
        ptbb[TBXID_FAVORITES].fsState |= TBSTATE_HIDDEN;
    }

    ASSERT(c_tbExplorer[TBXID_PREVIOUSFOLDER].idCommand == TBIDM_PREVIOUSFOLDER);
    if (!_fShellView)
        ptbb[TBXID_PREVIOUSFOLDER].fsState |= TBSTATE_HIDDEN;

    ASSERT(c_tbExplorer[TBXID_CONNECT].idCommand == TBIDM_CONNECT);
    ASSERT(c_tbExplorer[TBXID_DISCONNECT].idCommand == TBIDM_DISCONNECT);
    if (!_fShellView || !_UseMapNetDrvBtns()) 
    {
        ptbb[TBXID_CONNECT].fsState |= TBSTATE_HIDDEN;
        ptbb[TBXID_DISCONNECT].fsState |= TBSTATE_HIDDEN;
    }

    // If this TBIDM_ALLFOLDERS assertion rips, remember to fix up _AdminMarkDefaultButtons too.
    ASSERT(c_tbExplorer[TBXID_ALLFOLDERS].idCommand == TBIDM_ALLFOLDERS);
    if (!_fShellView || GetUIVersion() < 5)
        ptbb[TBXID_ALLFOLDERS].fsState |= TBSTATE_HIDDEN;

    ASSERT(c_tbExplorer[TBXID_THEATER].idCommand == TBIDM_THEATER);
    ptbb[TBXID_THEATER].fsState |= TBSTATE_HIDDEN;

    ASSERT(c_tbExplorer[TBXID_MEDIABAR].idCommand == TBIDM_MEDIABAR);
    if (_fShellView || SHRestricted2W(REST_No_LaunchMediaBar, NULL, 0) || !CMediaBarUtil::IsWMP7OrGreaterCapable())
    {
        ptbb[TBXID_MEDIABAR].fsState |= TBSTATE_HIDDEN;
    }

    ASSERT(c_tbExplorer[TBXID_PREVIOUSFOLDER].idCommand == TBIDM_PREVIOUSFOLDER);
    if (!_fShellView) 
    {
        ptbb[TBXID_PREVIOUSFOLDER].fsState |= TBSTATE_HIDDEN;
    }
}

void CInternetToolbar::_AddCommonButtons()
{
    TBBUTTON  tbExplorer[ARRAYSIZE(c_tbExplorer)];

    memcpy(tbExplorer, c_tbExplorer, sizeof(TBBUTTON) * ARRAYSIZE(c_tbExplorer));

    if (IS_BIDI_LOCALIZED_SYSTEM())
    {
        if (!SHUseClassicToolbarGlyphs())
        {
            tbExplorer[0].iBitmap = 1;
            tbExplorer[1].iBitmap = 0;
        }
    }

    _MarkDefaultButtons(tbExplorer, ARRAYSIZE(c_tbExplorer));

#ifdef DEBUG
    _AssertRestrictionOrderIsCorrect();
#endif

    if (!_fShellView && SHRestricted2(REST_SPECIFYDEFAULTBUTTONS, NULL, 0))
        _AdminMarkDefaultButtons(tbExplorer, ARRAYSIZE(c_tbExplorer));

    int iButtons = RemoveHiddenButtons(tbExplorer, ARRAYSIZE(tbExplorer));

    for (int i = 0; i < iButtons; i++) 
    {
        if (!(tbExplorer[i].fsStyle & BTNS_SEP)) 
        {
            CMDMAP* pcm = (CMDMAP*)LocalAlloc(LPTR, sizeof(CMDMAP));
            if (pcm) 
            {
                pcm->guidButtonGroup = CLSID_CommonButtons;
                pcm->nCmdID = tbExplorer[i].idCommand;

                tbExplorer[i].idCommand = _btb._nNextCommandID++;
                tbExplorer[i].dwData = (LPARAM)pcm;
            }
        }
    }

    SendMessage(_btb._hwnd, TB_ADDBUTTONS, iButtons, (LPARAM) tbExplorer);

    _btb._RecalcButtonWidths();
}

#define IS_LIST_STYLE(hwnd) (BOOLIFY(GetWindowLong(hwnd, GWL_STYLE) & TBSTYLE_LIST))

void CInternetToolbar::_UpdateToolsStyle(BOOL fList)
{
    if (BOOLIFY(fList) != IS_LIST_STYLE(_btb._hwnd))
    {
        _fDirty = TRUE;

        // toggle TBSTYLE_LIST
        SHSetWindowBits(_btb._hwnd, GWL_STYLE, TBSTYLE_LIST, fList ? TBSTYLE_LIST : 0);
        // toggle TBSTYLE_EX_MIXEDBUTTONS
        SendMessage(_btb._hwnd, TB_SETEXTENDEDSTYLE, TBSTYLE_EX_MIXEDBUTTONS, fList ? TBSTYLE_EX_MIXEDBUTTONS : 0);
    }
}

void CInternetToolbar::_InitToolbar()
{
    TCHAR szShellTBText[1024];  // This should be enough
    ZeroMemory(szShellTBText, sizeof(szShellTBText));
    int nRows = _fCompressed ? 0 : _uiTBTextRows;
    DWORD dwStyle = TBSTYLE_EX_DRAWDDARROWS | TBSTYLE_EX_HIDECLIPPEDBUTTONS;

    if (IsOS(OS_WHISTLERORGREATER))
        dwStyle |= TBSTYLE_EX_DOUBLEBUFFER;

    // this tells the toolbar what version we are
    SendMessage(_btb._hwnd, TB_BUTTONSTRUCTSIZE,    sizeof(TBBUTTON), 0);
    SendMessage(_btb._hwnd, TB_SETEXTENDEDSTYLE, dwStyle, dwStyle);
    SendMessage(_btb._hwnd, TB_SETMAXTEXTROWS,      nRows, 0L);
    SendMessage(_btb._hwnd, TB_SETDROPDOWNGAP,  GetSystemMetrics(SM_CXEDGE) / 2, 0);
    SendMessage(_btb._hwnd, CCM_SETVERSION, COMCTL32_VERSION, 0);

    _UpdateToolsStyle(_cs.fList);
    _fDirty = FALSE; // _UpdateToolsStyle unfortunately sets this early on; but we can assume we're not dirty now.

    ITBar_LoadToolbarGlyphs(_btb._hwnd);
    _InitBitmapDSA();

    _InitForScreenSize();

    SendMessage(_btb._hwnd, TB_ADDSTRING, (WPARAM)MLGetHinst(), IDS_IE_TB_LABELS);

    _AddCommonButtons();

    INT_PTR nRet = SendMessage(_btb._hwnd, TB_ADDSTRING, (WPARAM)MLGetHinst(), IDS_SHELL_TB_LABELS);

#ifdef DEBUG
    if (nRet != SHELLTOOLBAR_OFFSET)
        TraceMsg(TF_ERROR, "CInternetToolbar::_InitToolbar -- nRet != SHELLTOOLBAR_OFFSET");
#endif
}

HRESULT CInternetToolbar::_ShowTools(PBANDSAVE pbs)
{
    HRESULT         hr  = S_OK;
    LPBANDITEMDATA  pbid = _bs._GetBandItemDataStructByID(CBIDX_TOOLS);

    if (!pbid)
    {
        ASSERT(!_btb._hwnd);

        _btb._hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, TOOLBARCLASSNAME, NULL,
                                WS_CHILD | TBSTYLE_FLAT |
                                TBSTYLE_TOOLTIPS |
                                WS_CLIPCHILDREN |
                                WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NOPARENTALIGN |
                                CCS_NORESIZE,
                                0, 0, 0, 0, _bs._hwnd, (HMENU) FCIDM_TOOLBAR, HINST_THISDLL, NULL);

        if (_btb._hwnd)
        {
            _InitToolbar();
            pbid = _AddNewBand((IDeskBand*)&_btb, CBIDX_TOOLS);
        }

        if (!pbid)
            return E_OUTOFMEMORY;
    }
    else
    {
        pbs = NULL;
    }

    _ShowBandCommon(pbs, pbid, _nVisibleBands & VBF_TOOLS);
    return hr;
}

void CInternetToolbar::_ShowBandCommon(PBANDSAVE pbs, LPBANDITEMDATA pbid, BOOL fShow)
{
    REBARBANDINFO   rbbi;

    pbid->fShow = BOOLIFY(fShow);
    if (pbid->pdb)
    {
        pbid->pdb->ShowDW(pbid->fShow);
    }

    INT_PTR i = BandIDtoIndex(_bs._hwnd, pbid->dwBandID);

    if (pbs)
    {
        rbbi.cbSize = sizeof(REBARBANDINFO);
        rbbi.fMask = RBBIM_SIZE | RBBIM_STYLE;

        // we just want to change the RBBS_BREAK bit
        // assert that our caller doesn't expect to set any other bits
        // ASSERT(!(pbs->fStyle & ~RBBS_BREAK)); <--- I hit this assert all the time

        // get old style
        SendMessage(_bs._hwnd, RB_GETBANDINFO, i, (LPARAM)&rbbi);

        rbbi.fStyle = (rbbi.fStyle & ~RBBS_BREAK) | (pbs->fStyle & RBBS_BREAK);
        rbbi.cx = pbs->cx;

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


    if ( pbid->dwModeFlags & DBIMF_BREAK )
    {
        rbbi.cbSize = sizeof(REBARBANDINFO);
        rbbi.fMask = RBBIM_STYLE;
        if (SendMessage(_bs._hwnd, RB_GETBANDINFO, i, (LPARAM) &rbbi))
        {
            // in theater mode we don't allow bands to have breaks
            if ((rbbi.fStyle & RBBS_BREAK ) && _fTheater)
            {
                rbbi.fStyle &= ~RBBS_BREAK;
                SendMessage(_bs._hwnd, RB_SETBANDINFO, i, (LPARAM) &rbbi);
            }
        }
    }

    SendMessage(_bs._hwnd, RB_SHOWBAND, i, pbid->fShow);
}


HRESULT CInternetToolbar::_GetPersistedBand(const CLSID clsid, REFIID riid, void ** ppiface)
{
    HRESULT hr  = E_FAIL;
    TCHAR szStreamName[MAX_PATH];

    if (SUCCEEDED(_GetStreamName(_fInitialPidlIsWeb, szStreamName, ARRAYSIZE(szStreamName))))
    {
        static BOOL fBrowserOnly = (WhichPlatform() != PLATFORM_INTEGRATED);
        TCHAR szKey[MAX_PATH];
        TCHAR szGUID[MAX_PATH];

        wnsprintf(szKey, ARRAYSIZE(szKey), TEXT("Software\\Microsoft\\Internet Explorer\\Toolbar\\%s"), szStreamName);
        SHStringFromGUID(clsid, szGUID, ARRAYSIZE(szGUID));

        if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, szKey, szGUID, NULL, NULL, NULL))
        {
            // Was the stream saved by an Integrated shell and we are in browser only mode?
            if ((_cs.fSaveInShellIntegrationMode) && fBrowserOnly)
            {
                // Yes, so we need to ignore the stream.
            }
            else
            {
                IStream * pstm = GetRegStream(_fInitialPidlIsWeb, szGUID, STGM_READ);
                if (pstm)
                {
                    hr = _bs.LoadFromStreamBS(pstm, riid, ppiface);
                    pstm->Release();
                }
            }
        }
    }

    if (FAILED(hr))
    {
        hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, riid, ppiface);
        if (SUCCEEDED(hr))
        {
            IPersistStreamInit * ppsi;
            ((IUnknown *) *ppiface)->QueryInterface(IID_PPV_ARG(IPersistStreamInit, &ppsi));
            if (ppsi)
            {
                ppsi->InitNew();
                ppsi->Release();
            }
        }
    }

    return hr;
}


HRESULT CInternetToolbar::_ShowExternalBand( PBANDSAVE pbs, int idBand )
{
    HRESULT hr;
    if (IS_EXTERNALBAND(idBand))
    {
        int idBandExt = MAP_TO_EXTERNAL(idBand);

        if (!IsEqualCLSID(_rgebi[idBandExt].clsid, GUID_NULL))
        {
            LPBANDITEMDATA pbid = _bs._GetBandItemDataStructByID( idBand );
            BOOL fIsVisible = _nVisibleBands & EXTERNALBAND_VBF_BIT(idBandExt);
            if (!pbid && fIsVisible)
            {
                IDeskBand *pitbBand;
                hr = _GetPersistedBand(_rgebi[idBandExt].clsid, IID_PPV_ARG(IDeskBand, &pitbBand));
                if (SUCCEEDED(hr))
                {
                    pbid = _AddNewBand( pitbBand, idBand );
                    pitbBand->Release();
                }
                if (!pbid)
                    return E_OUTOFMEMORY;
            }
            else
            {
                pbs = NULL;
                if (!pbid)
                    return S_OK;
            }
            _ShowBandCommon(pbs, pbid, fIsVisible );
        }
    }
    return S_OK;
}


HRESULT CInternetToolbar::_ShowAddressBand(PBANDSAVE pbs)
{
    HRESULT         hr  = S_OK;
    LPBANDITEMDATA  pbid = _bs._GetBandItemDataStructByID(CBIDX_ADDRESS);
    if (!pbid)
    {
       if (_nVisibleBands & VBF_ADDRESS)
       {
            IDeskBand *pitbAddressBand;

            hr = _GetPersistedBand(CLSID_AddressBand, IID_PPV_ARG(IDeskBand, &pitbAddressBand));
            if (SUCCEEDED(hr))
            {
                pbid = _AddNewBand(pitbAddressBand, CBIDX_ADDRESS);
                if (pbid)
                {
                    _hwndAddressBand = pbid->hwnd;

                    if (!pbs)
                    {
                        for (int i = 0; i < CBANDSMAX; i++)
                        {
                            if (_cs.bs[i].wID == CBIDX_ADDRESS)
                            {
                                pbs = _cs.bs + i;
                                break;
                            }
                        }
                    }
                }

                pitbAddressBand->Release();
            }
        }
        else
        {
            return S_OK;
        }

        if (!pbid)
            return E_OUTOFMEMORY;
    }
    else
        pbs = NULL;


    _ShowBandCommon(pbs, pbid, _nVisibleBands & VBF_ADDRESS);
    return S_OK;
}

LPBANDITEMDATA CInternetToolbar::_AddNewBand(IDeskBand* pdb, DWORD dwID)
{
    if (SUCCEEDED(_bs._AddBandByID(pdb, dwID)))
    {
        return _bs._GetBandItemDataStructByID(dwID);
    }
    return NULL;
}


HRESULT CInternetToolbar::_ShowLinks(PBANDSAVE pbs)
{
    HRESULT hr = S_OK;

    LPBANDITEMDATA pbid = _bs._GetBandItemDataStructByID(CBIDX_LINKS);
    if (!pbid)
    {
        IDeskBand* pdbLinks = NULL;

        // Check if custom link band GUID is present in the registry,
        // and if so, do a full CoCreateInstance using this GUID.
        // Otherwise, just do the normal internal call to the link's band factory.

        if (_nVisibleBands & VBF_LINKS)
        {
            if (!_fInitialPidlIsWeb ||
                FAILED(CreateFromRegKey(c_szRegKeyCoolbar, TEXT("QuickLinksCLSID"), IID_PPV_ARG(IDeskBand, &pdbLinks))))
            {
                hr = _GetPersistedBand(CLSID_QuickLinks, IID_PPV_ARG(IDeskBand, &pdbLinks));
                IUnknown_Exec(pdbLinks, &CLSID_QuickLinks, QLCMD_SINGLELINE, 1, NULL, NULL);
            }
        }
        else
        {
            return S_OK;
        }

        if (pdbLinks)
        {
            // mark it so ISFBand knows it's qlinks (for UAssist)
            VARIANTARG v;
#ifdef DEBUG
            {
                // n.b. we overwrite old persisted guys (which should be -1)
                IUnknown_Exec(pdbLinks, &CGID_ISFBand, ISFBID_PRIVATEID, 0, NULL, &v);
                ASSERT(v.lVal == -1 || v.lVal == CSIDL_FAVORITES);
            }
#endif
            v.vt = VT_I4;
            v.lVal = CSIDL_FAVORITES;   // close enough for our purposes...
            IUnknown_Exec(pdbLinks, &CGID_ISFBand, ISFBID_PRIVATEID, 0, &v, NULL);
            pbid = _AddNewBand(pdbLinks, CBIDX_LINKS);

            if (pbid && !pbs)
            {
                for (int i = 0; i < CBANDSMAX; i++)
                {
                    if (_cs.bs[i].wID == CBIDX_LINKS)
                    {
                        pbs = _cs.bs + i;
                        break;
                    }
                }
            }

            pdbLinks->Release();
        }

        if (!pbid)
            return E_OUTOFMEMORY;
    }
    else
        pbs = NULL;

    _ShowBandCommon(pbs, pbid, _nVisibleBands & VBF_LINKS);


    return hr;
}

HRESULT CInternetToolbar::_ShowMenu(PBANDSAVE pbs)
{
    LPBANDITEMDATA pbid = _bs._GetBandItemDataStructByID(CBIDX_MENU);
    if (!pbid)
    {
        CFavoritesCallback* pfcb = new CFavoritesCallback();
        if (pfcb)
        {
            IShellMenu* psm;
            if (SUCCEEDED(CoCreateInstance(CLSID_MenuBand, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellMenu, &psm))))
            {
                VARIANTARG var;

                if (SUCCEEDED(IUnknown_Exec(_pbs2, &CGID_Explorer, SBCMDID_GETCURRENTMENU, 0, NULL, &var)) &&
                        var.vt == VT_INT_PTR && var.byref)
                {
                    IDeskBand* pdbMenu;
                    if (SUCCEEDED(psm->Initialize(pfcb, -1, ANCESTORDEFAULT, SMINIT_HORIZONTAL | SMINIT_TOPLEVEL)) &&
                        SUCCEEDED(psm->SetMenu((HMENU)var.byref, GetParent(_hwnd), SMSET_DONTOWN)) &&
                        SUCCEEDED(psm->QueryInterface(IID_PPV_ARG(IDeskBand, &pdbMenu))))
                    {
                        pbid = _AddNewBand(pdbMenu, CBIDX_MENU);
                        if (pbid)
                        {
                            // Tell the menuband we're not a real bar/bandsite/band
                            IUnknown_Exec(pbid->pdb, &CGID_MenuBand, MBANDCID_NOTAREALSITE, TRUE, NULL, NULL);

                            _bs.SetBandState(CBIDX_MENU, BSSF_NOTITLE, BSSF_NOTITLE);
                            _hwndMenu = pbid->hwnd;
                        }

                        pdbMenu->Release();
                    }
                }
                psm->Release();
            }
            pfcb->Release();
        }

        if (!pbid)
            return E_OUTOFMEMORY;
    }
    else
        pbs = NULL;


    _ShowBandCommon(pbs, pbid, _nVisibleBands & VBF_MENU);
    return S_OK;
}

HBITMAP CInternetToolbar::_LoadBackBitmap()
{
    if (SHIsLowMemoryMachine(ILMM_IE4))
        return NULL;

    if (_fInitialPidlIsWeb)
    {
        static LPTSTR s_pszBitmapInternet = NULL;
        return LoadToolbarBackBmp(&s_pszBitmapInternet, &s_bmpBackInternet, _fInitialPidlIsWeb);
    }
    else
    {
        static LPTSTR s_pszBitmapShell = NULL;
        return LoadToolbarBackBmp(&s_pszBitmapShell, &s_bmpBackShell, _fInitialPidlIsWeb);
    }
}

void CInternetToolbar::_SetBackground()
{
    REBARBANDINFO   rbbi;
    HBITMAP         hbmp;

    // Theater mode doesn't allow bitmap customization, so don't bother loading one from the cache
    if (_fTheater)
        hbmp = NULL;
    else
        hbmp = _LoadBackBitmap();

    // don't bother updating the bkcolor if we know we'll just set it to CLR_NONE below (otherwise rebar invalidates)
    if (!hbmp)
        SendMessage(_bs._hwnd, RB_SETBKCOLOR, 0, (LPARAM)GetSysColor(COLOR_BTNFACE));

    // If we think we have a bitmap, or the cache thinks we have a bitmap, we have some work to do
    if (_bmpBack || hbmp)
    {
        BOOL fRemove = (NULL!=_bmpBack && NULL==hbmp);

        if (hbmp)
            SendMessage(_bs._hwnd, RB_SETBKCOLOR, 0, (LPARAM)CLR_NONE);
        _bmpBack = hbmp;

        rbbi.cbSize = sizeof(REBARBANDINFO);

        INT_PTR fRedraw = SendMessage(_bs._hwnd, WM_SETREDRAW, FALSE, 0);

        INT icBands = (INT) SendMessage( _bs._hwnd, RB_GETBANDCOUNT, 0, 0 );
        for (int i = 0; i < icBands; i++)
        {
            rbbi.fMask = RBBIM_ID | RBBIM_CHILD | RBBIM_BACKGROUND;
            if (SendMessage(_bs._hwnd, RB_GETBANDINFO, i, (LPARAM) &rbbi))
            {
                if (rbbi.wID != CBIDX_BRAND && rbbi.hbmBack != hbmp)
                {
                    rbbi.fMask = RBBIM_BACKGROUND;
                    rbbi.hbmBack = hbmp;
                    SendMessage(_bs._hwnd, RB_SETBANDINFO, i, (LPARAM) &rbbi);
                    InvalidateRect(rbbi.hwndChild, NULL, TRUE);
                }
            }
        }
        SendMessage(_bs._hwnd, WM_SETREDRAW, fRedraw, 0);

        // When removing the background bitmap, we need to invalidate *outside*
        // of the WM_SETREDRAW so we actually erase the background properly
        //
        if (fRemove)
            InvalidateRect(_bs._hwnd, NULL, TRUE);

    }
}


HRESULT CInternetToolbar::_ShowBrand(PBANDSAVE pbs)
{
    REBARBANDINFO       rbbi;
    LPBANDITEMDATA      pbid;
    INT_PTR             i;
    HRESULT             hr = S_OK;
    BOOL                fCreated = FALSE;

    pbid = _bs._GetBandItemDataStructByID(CBIDX_BRAND);
    if (!pbid)
    {
        IDeskBand *pdbBrandBand;
        hr = CBrandBand_CreateInstance(NULL, (IUnknown **)&pdbBrandBand, NULL);
        if (SUCCEEDED(hr))
        {
            pbid = _AddNewBand(pdbBrandBand, CBIDX_BRAND);
            fCreated = TRUE;
            pdbBrandBand->Release();
        }
        else
            return hr;
    }

    if (!pbid)
        return E_OUTOFMEMORY;

    pbid->pdb->ShowDW(TRUE);

    i = BandIDtoIndex(_bs._hwnd, CBIDX_BRAND);
    if (fCreated)
    {
        // add these to ::IDeskBand::GetBandInfo()
        rbbi.cbSize = sizeof(REBARBANDINFO);
        rbbi.fMask = RBBIM_STYLE;
        rbbi.fStyle = RBBS_FIXEDSIZE | RBBS_VARIABLEHEIGHT;

        if (pbs)
        {
            rbbi.fMask |= RBBIM_SIZE;
            rbbi.fStyle |= pbs->fStyle;
            rbbi.cx = pbs->cx;
        }
        SendMessage(_bs._hwnd, RB_SETBANDINFO, i, (LPARAM)&rbbi);
        // this can cause the band to move because a fixed size band
        // is forced in a particular location.
        // so we need to re-fetch the index
        i = BandIDtoIndex(_bs._hwnd, CBIDX_BRAND);
    }
    SendMessage(_bs._hwnd, RB_SHOWBAND, i, _nVisibleBands & VBF_BRAND);
    return S_OK;
}

void CInternetToolbar::_EnsureAllBandsShown()
{
    if (_hwnd)
    {
        INT_PTR fRedraw = SendMessage(_bs._hwnd, WM_SETREDRAW, FALSE, 0);

        _ShowMenu(NULL);
        _ShowTools(NULL);
        _ShowAddressBand(NULL);
        _ShowLinks(NULL);
        _ShowBrand(NULL);
        for (int i = CBIDX_EXTERNALFIRST; i <= CBIDX_EXTERNALLAST; i++)
        {
            _ShowExternalBand( NULL, i );
        }

        _SetBackground();
        _bs._SetMinDimensions();
        _UpdateLocking();
        
        SendMessage(_bs._hwnd, WM_SETREDRAW, fRedraw, 0);
    }
}

BOOL CInternetToolbar::_ShowBands(UINT fVisible)
{
    fVisible &= VBF_VALID;

    if (fVisible == _nVisibleBands)
        return(TRUE);

    _nVisibleBands = fVisible;
    _EnsureAllBandsShown();
    ShowDW(_fShow);

    return(TRUE);
}

HRESULT CInternetToolbar::_CreateBands()
{
    HRESULT hr = S_OK;

    if (!_hwnd && _ptbsite)
    {
        HWND hwndParent;

        hr = _ptbsite->GetWindow(&hwndParent);
        if (SUCCEEDED(hr))
        {
            TCHAR szScratch[16];
            int i;

            // Check if coolbar layout had already been loaded from the registry
            if(_cs.cbVer != CBS_VERSION)
            {
                TraceMsg(DM_ITBAR, "CInternetToolbar::_CreateBands failed. Bad Version");
                ASSERT(0);

                return(S_FALSE);
            }

            _nVisibleBands = _cs.uiVisible;

            _InitComCtl32();    // don't check result, if this fails our CreateWindows will fail

            MLLoadString(IDS_WEB_TB_TEXTROWS, szScratch, ARRAYSIZE(szScratch));
            _uiTBTextRows = _uiTBDefaultTextRows = StrToInt(szScratch);

            _fCompressed = (_cs.fNoText != FALSE);

            _hwnd = SHCreateWorkerWindow(SizableWndProc, hwndParent, 0, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                                       (HMENU)FCIDM_REBAR, this);

            if (!IS_VALID_HANDLE(_hwnd, WND))
            {
                _fDontSave = TRUE;
                TraceMsg(TF_ERROR, "CInternetToolbar::_CreateBands() - _hwnd failed");
                return E_OUTOFMEMORY;
            }

            // delay until now
            // this sets up the parent child chain so that these children can
            // queryservice through us
            hr = SetClient(SAFECAST(&_bs, IInputObjectSite*));
            if (SUCCEEDED(hr))
            {
                INT_PTR fRedraw = SendMessage(_bs._hwnd, WM_SETREDRAW, FALSE, 0);

                for (i = 0; i < CBANDSMAX; i++)
                {
                    hr = S_OK;
                    switch (_cs.bs[i].wID)
                    {
                    case CBIDX_TOOLS:
                        if(!SHRestricted2W(REST_NoToolBar, NULL, 0))
                        {
                            hr = _ShowTools(_cs.bs + i);
                        }
                        else
                        {
                            _nVisibleBands &= ~VBF_TOOLS;
                        }
                        break;

                    case CBIDX_ADDRESS:
                        if(!SHRestricted2W(REST_NoAddressBar, NULL, 0))
                        {
                            hr = _ShowAddressBand(_cs.bs + i);
                        }
                        else
                        {
                            _nVisibleBands &= ~VBF_ADDRESS;
                        }
                        break;

                    case CBIDX_LINKS:
                        if(!SHRestricted2W(REST_NoLinksBar, NULL, 0))
                        {
                            hr = _ShowLinks(_cs.bs + i);
                        }
                        else
                        {
                            _nVisibleBands &= ~VBF_LINKS;
                        }
                        break;

                    case CBIDX_BRAND:
                        hr = _ShowBrand(_cs.bs + i);
                        break;

                    case CBIDX_MENU:
                        hr = _ShowMenu(_cs.bs + i);
                        break;

                    // If there is no id associated, there's nothing more to restore.
                    case 0:
                    {
                        // Out of bands; stop loop.
                        i = CBANDSMAX;
                        break;
                    }

                    default:
                        if (IS_EXTERNALBAND(_cs.bs[i].wID))
                        {
                            for (DWORD j = 0; j < MAXEXTERNALBANDS; j++)
                            {
                                if (_cs.aclsidExternalBands[MAP_TO_EXTERNAL(_cs.bs[i].wID)] == _rgebi[j].clsid)
                                {
                                    CLSID clsidTemp = _rgebi[j].clsid;
                                    _rgebi[j].clsid = _rgebi[MAP_TO_EXTERNAL(_cs.bs[i].wID)].clsid;
                                    _rgebi[MAP_TO_EXTERNAL(_cs.bs[i].wID)].clsid = clsidTemp;
                                    hr = _ShowExternalBand(_cs.bs + i, _cs.bs[i].wID);
                                    break;
                                }
                            }
                        }
                        break;
                    }

                    if (hr != S_OK)
                    {
                        TraceMsg(TF_ERROR, "CInternetToolbar::_CreateBands -- band ID %x creation failed", _cs.bs[i].wID);

                        // if band creation failed, we still go ahead and open the browser and do our normal stuff --
                        // including persisting the band state.
                        // unfortunately for the user who opens too many windows, they hit GDI limits and we can't
                        // create the bands and we fail, and then persist out that state.  so all future windows
                        // are broken without the file menu etc. and the user cant get out.
                        _fDontSave = TRUE;
                    }
                }

                _SetBackground();
                _bs._SetMinDimensions();
                _UpdateLocking();

                SendMessage(_bs._hwnd, WM_SETREDRAW, fRedraw, 0);
            }
        }
    }
    return hr;
}

HRESULT CInternetToolbar::ShowDW(BOOL fShow)
{
    if ((g_dwProfileCAP & 0x00000008) && s_imlTBGlyphs.arhiml[0])
    {
        StartCAP();
    }

    HRESULT hres = _CreateBands();
    if (FAILED(hres))
        return hres;

    if (!_nVisibleBands && fShow)
        return(FALSE);

    _fShow = fShow;

    _bs.UIActivateDBC(fShow ? DBC_SHOW : DBC_HIDE);

    ResizeBorderDW(NULL, NULL, FALSE);
    ShowWindow(_hwnd, fShow ? SW_SHOW : SW_HIDE);


    BOOL fConnect = (fShow && _dwcpCookie == 0);
    if (fConnect || (!fShow && _dwcpCookie != 0))
    {
        ConnectToConnectionPoint(SAFECAST(this, IDockingWindow*), DIID_DWebBrowserEvents2, fConnect, _pdie, &_dwcpCookie, NULL);
    }

    return hres;
}

int ITBar_TrackPopupMenuEx(HMENU hmenu, UINT uFlags, int x, int y, HWND hwnd, LPRECT prcExclude)
{
    TPMPARAMS tpm;
    if (prcExclude)
    {
        tpm.cbSize = sizeof(TPMPARAMS);
        CopyRect(&tpm.rcExclude, prcExclude);
    }
    return TrackPopupMenuEx(hmenu, uFlags, x, y, hwnd, prcExclude ? &tpm : NULL);
}

/*******************************************************************

NAME:       CInternetToolbar::_ShowBackForwardMenu

SYNOPSIS:
NOTES:
********************************************************************/
BOOL CInternetToolbar::_ShowBackForwardMenu(BOOL fForward, POINT pt, LPRECT prcExclude)
{
    BOOL fRet = FALSE;
    HMENU hmenuBF = CreatePopupMenu();
    if (hmenuBF)
    {
        ASSERT(_pbs2);
        ITravelLog *ptl;

        _pbs2->GetTravelLog(&ptl);
        if (NULL != ptl)
        {
            HRESULT hr;

            hr = ptl->InsertMenuEntries(_pbs2, hmenuBF, 0, 1, MAX_NAV_MENUITEMS, fForward ? TLMENUF_FORE : TLMENUF_BACK);
            if (S_OK == hr)
            {
                OLECMD cmd;

                cmd.cmdID = SBCMDID_HISTORYBAR;
                cmd.cmdf  = 0;

                ASSERT(NULL != _ptbsitect);
                _ptbsitect->QueryStatus(&CGID_Explorer, 1, &cmd, NULL);

                if (((cmd.cmdf & OLECMDF_ENABLED) && !(cmd.cmdf & OLECMDF_LATCHED)) &&
                    (1 /* MAX_NAV_MENUITEMS */ < ptl->CountEntries(_pbs2)))
                {
                    static TCHAR s_szMenuText[MAX_PATH];
                    static int   s_iMenuIcon = -1;

                    if (TEXT('\0') == s_szMenuText[0])
                    {
                        MLLoadString(IDS_MI_BACK_HISTORY, s_szMenuText, ARRAYSIZE(s_szMenuText));
                        ASSERT(TEXT('\0') != s_szMenuText[0]);
                    }

                    if (-1 == s_iMenuIcon)
                    {
                        IShellFolder  *psfParent;
                        LPITEMIDLIST  pidlHistory;
                        LPCITEMIDLIST pidlItem;

                        psfParent   = NULL;
                        pidlHistory = NULL;
                        pidlItem    = NULL;

                        SHGetSpecialFolderLocation(NULL, CSIDL_HISTORY, &pidlHistory);
                        if (NULL != pidlHistory)
                        {
                            SHBindToIDListParent(pidlHistory, IID_PPV_ARG(IShellFolder, &psfParent), &pidlItem);
                            if (NULL != psfParent)
                            {
                                ASSERT(NULL != pidlItem);

                                hr = SHMapPIDLToSystemImageListIndex(psfParent, pidlItem, &s_iMenuIcon);
                                if (FAILED(hr))
                                    s_iMenuIcon = -1;

                                psfParent->Release();
                            }

                            ILFree(pidlHistory);
                        }
                    }

                    ULONG_PTR rgpData[2];

                    rgpData[0] = (ULONG_PTR)s_szMenuText;
                    rgpData[1] = s_iMenuIcon;

                    AppendMenu(hmenuBF, MF_SEPARATOR, -1, NULL);
                    AppendMenu(hmenuBF, MF_OWNERDRAW, FCIDM_VBBHISTORYBAND, (PCTSTR)rgpData);
                }

                // If any menu items were added, show the menu and navigate to it
#ifndef MAINWIN
                int nIndex;

                if (nIndex = ITBar_TrackPopupMenuEx (hmenuBF, TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD, pt.x, pt.y, _hwnd, prcExclude))
#else
                    // Because mainwin doesn't support win95 look and feel we are
                    // having a problem to keep the popup from dismissing when we
                    // pass NULL as noDismissal area.
                    RECT rect;
                    GetWindowRect( _hwnd, &rect );
                    if (nIndex = (int)TrackPopupMenu (hmenuBF,
                        TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD,
                        pt.x, pt.y, 0, _hwnd,
                        &rect))
#endif
                {
                    if (FCIDM_VBBHISTORYBAND != nIndex)
                    {
                        ptl->Travel(_pbs2, (fForward ? nIndex : -nIndex));
                    }
                    else
                    {
                        VARIANTARG varOn;

                        varOn.vt   = VT_I4;
                        varOn.lVal = 1;

                        _ptbsitect->Exec(&CGID_Explorer, SBCMDID_HISTORYBAR, OLECMDEXECOPT_DONTPROMPTUSER, &varOn, NULL);
                    }
                }
            }

            ptl->Release();
        }

        DestroyMenu (hmenuBF);
    }

    return fRet;
}

// get per folder search items and default search
BOOL CInternetToolbar::_GetFolderSearchData()
{
    int iInserted=0;

    if (_pbs2)
    {
        LPCBASEBROWSERDATA pbbd;

        if (SUCCEEDED(_pbs2->GetBaseBrowserData(&pbbd)) && (pbbd->_psfPending || pbbd->_psf))
        {
            IShellFolder2 * psf2;
            IShellFolder*   psf = pbbd->_psfPending ? pbbd->_psfPending : pbbd->_psf;

            if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
            {
                LPENUMEXTRASEARCH penum;
                GUID              guid;

                if (SUCCEEDED(psf2->GetDefaultSearchGUID(&guid)))
                    _guidDefaultSearch = guid;

                // get per folder search items
                if (_hdpaFSI && SUCCEEDED(psf2->EnumSearches(&penum)))
                {
                    EXTRASEARCH  xs;

                    while(penum->Next(1, &xs, NULL) == S_OK)
                    {
                        LPFOLDERSEARCHITEM pfsi = (LPFOLDERSEARCHITEM)LocalAlloc(LPTR, sizeof(FOLDERSEARCHITEM));
                        if (pfsi)
                        {
                            pfsi->idCmd = -1;
                            pfsi->guidSearch = xs.guidSearch;
                            StrCpyNW(pfsi->wszUrl, xs.wszUrl, ARRAYSIZE(pfsi->wszUrl));
                            StrCpyNW(pfsi->wszName, xs.wszFriendlyName, ARRAYSIZE(pfsi->wszName));

                            if (DPA_InsertPtr(_hdpaFSI, iInserted, pfsi) != -1)
                                iInserted++;
                            else
                                LocalFree(pfsi);
                        }
                    }
                    penum->Release();
                }
                psf2->Release();
            }
        }
    }

    return (iInserted > 0);
}

BOOL NavigateSearchBar(IWebBrowser2 *pwb2, LPCWSTR pwszUrl)
{
    BOOL bRet  = FALSE;

    SA_BSTRGUID bstr;
    InitFakeBSTR(&bstr, CLSID_SearchBand);

    VARIANT var;
    var.vt = VT_BSTR;
    var.bstrVal = bstr.wsz;

    // show a search bar
    VARIANT varEmpty = {0};
    if (SUCCEEDED(pwb2->ShowBrowserBar(&var, &varEmpty, &varEmpty)))
    {
        VARIANT varTargetFrame = {0};
        VARIANT varFlags = {0};

        StrCpyNW(bstr.wsz, pwszUrl, ARRAYSIZE(bstr.wsz));
        bstr.cb = lstrlenW(bstr.wsz) * sizeof(WCHAR);

        varFlags.vt = VT_I4;
        varFlags.lVal = navBrowserBar;

        var.vt = VT_BSTR;
        var.bstrVal = bstr.wsz;

        // navigate the search bar to the correct url
        if (SUCCEEDED(pwb2->Navigate2(&var, &varFlags, &varTargetFrame, &varEmpty, &varEmpty)))
            bRet = TRUE;
    }

    return bRet;
}

void RestrictItbarViewMenu(HMENU hmenu, IUnknown *punkBar )
{
    BOOL fIsRestricted = SHRestricted2(REST_NOBANDCUSTOMIZE, NULL, 0);
    if (fIsRestricted)
    {
        _EnableMenuItem(hmenu, FCIDM_VIEWLINKS, FALSE);
        _EnableMenuItem(hmenu, FCIDM_VIEWMENU, FALSE);
        _EnableMenuItem(hmenu, FCIDM_VIEWADDRESS, FALSE);
        _EnableMenuItem(hmenu, FCIDM_VIEWTOOLS, FALSE);
    }

    for (int i = 0; i < MAXEXTERNALBANDS; i++)
    {
        OLECMD cmd = { CITIDM_VIEWEXTERNALBAND_FIRST + i, 0 };
        OLECMDTEXTV<MAX_EXTERNAL_BAND_NAME_LEN> cmdtv;
        OLECMDTEXT *pcmdText = &cmdtv;
        pcmdText->cmdtextf = OLECMDTEXTF_NAME;
        pcmdText->cwActual = 0;
        pcmdText->cwBuf = MAX_EXTERNAL_BAND_NAME_LEN;

        IUnknown_QueryStatus( punkBar, &CGID_PrivCITCommands, 1, &cmd, pcmdText );
        if (cmd.cmdf & OLECMDF_SUPPORTED)
        {
            DWORD dwMenuCommand = FCIDM_EXTERNALBANDS_FIRST + i;
            InsertMenu( hmenu, FCIDM_VIEWCONTEXTMENUSEP, MF_BYCOMMAND, dwMenuCommand, pcmdText->rgwz );
            if (cmd.cmdf & OLECMDF_ENABLED)
            {
                _CheckMenuItem( hmenu, dwMenuCommand, TRUE );
            }
            if (fIsRestricted)
            {
                _EnableMenuItem( hmenu, dwMenuCommand, FALSE );
            }
        }
    }
}

void CInternetToolbar::_ShowContextMenu(HWND hwnd, LPARAM lParam, LPRECT prcExclude)
{
    // Bail if this context menu doesn't correspond to a band (fixes NT5 #181899)
    POINT pt;
    int iIndex = _bs._ContextMenuHittest(lParam, &pt);
    int idBandActive = _bs._IndexToBandID(iIndex);
    if (!InRange(idBandActive, CBIDX_FIRST, CBANDSMAX))
        return;

    // Bail if we can't find the resource
    HMENU hmenuITB = LoadMenuPopup(MENU_ITOOLBAR);
    if (!hmenuITB)
        return;

    UEMFireEvent(&UEMIID_SHELL, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_UICONTEXT, idBandActive == -1 ? UIBL_CTXTITBBKGND : UIBL_CTXTITBITEM);

    // Set the initial state of the menu
    _CheckMenuItem (hmenuITB, FCIDM_VIEWTOOLS, _nVisibleBands & VBF_TOOLS);
    _CheckMenuItem (hmenuITB, FCIDM_VIEWADDRESS, _nVisibleBands & VBF_ADDRESS);
    _CheckMenuItem (hmenuITB, FCIDM_VIEWLINKS, _nVisibleBands & VBF_LINKS);
    
    int cItemsBelowSep = 4;

    // only in theater mode can we autohide
    if (!_fTheater)
    {
        DeleteMenu(hmenuITB, FCIDM_VIEWAUTOHIDE, MF_BYCOMMAND);
        cItemsBelowSep--;
        if (_nVisibleBands & VBF_MENU || _fNoShowMenu)
            DeleteMenu(hmenuITB, FCIDM_VIEWMENU, MF_BYCOMMAND);
    }
    else
    {
        if (_fNoShowMenu)
            DeleteMenu(hmenuITB, FCIDM_VIEWMENU, MF_BYCOMMAND);
        DeleteMenu(hmenuITB, FCIDM_VIEWTOOLS, MF_BYCOMMAND);
        _CheckMenuItem (hmenuITB, FCIDM_VIEWAUTOHIDE, _fAutoHide);
        _CheckMenuItem (hmenuITB, FCIDM_VIEWMENU, _nVisibleBands & VBF_MENU);

    }

    if (_fTheater || SHRestricted2(REST_NOBANDCUSTOMIZE, NULL, 0))
    {
        // No lock in theater mode or Windows Explorer
        DeleteMenu(hmenuITB, FCIDM_VIEWLOCKTOOLBAR, MF_BYCOMMAND);
    }
    else
    {
        _CheckMenuItem(hmenuITB, FCIDM_VIEWLOCKTOOLBAR, _fLockedToolbar);
    }

    // if it was done via the keyboard, but focus wasn't on the tools band,
    // then don't have customize menu option
    // or if click didn't happen on the band
    if (!(_btb._fCustomize && idBandActive == CBIDX_TOOLS))
    {
        DeleteMenu(hmenuITB, FCIDM_VIEWTOOLBARCUSTOMIZE, MF_BYCOMMAND);
        cItemsBelowSep--;
    }

    BOOL fGoButtonAvailable =
        WasOpenedAsBrowser(static_cast<IExplorerToolbar *>(this)) || (GetUIVersion() >= 5);

    // Only show the go button item when you click on the address bar
    if (idBandActive != CBIDX_ADDRESS || !fGoButtonAvailable)
    {
        DeleteMenu(hmenuITB, FCIDM_VIEWGOBUTTON, MF_BYCOMMAND);
        cItemsBelowSep--;
    }
    else
    {
        BOOL fShowGoButton = SHRegGetBoolUSValue(REGSTR_PATH_MAIN,
                                TEXT("ShowGoButton"), FALSE, /*default*/TRUE);
        _CheckMenuItem(hmenuITB, FCIDM_VIEWGOBUTTON, fShowGoButton);
    }

    if (_fTheater || _btb._fCustomize || SHRestricted2(REST_LOCKICONSIZE, NULL, 0))
    {
        DeleteMenu(hmenuITB, FCIDM_VIEWTEXTLABELS, MF_BYCOMMAND);
        cItemsBelowSep--;
    }
    else
    {
        // If customize is unavailable, then there's no way for the user to
        // turn list style on/off.  In this case we want toggling text labels
        // to work the way it did in IE4 -- that is, switch between "text on
        // all buttons" and "text on no buttons".  So, if we're in "selective
        // text on right" mode, we say that labels are turned off.  If the user
        // picks this menu option, we'll go into "text on all buttons" mode.

        BOOL fChecked = !_fCompressed && !IS_LIST_STYLE(_btb._hwnd);
        _CheckMenuItem(hmenuITB, FCIDM_VIEWTEXTLABELS, fChecked);
    }

    if (!cItemsBelowSep)
        DeleteMenu(hmenuITB, FCIDM_VIEWCONTEXTMENUSEP, MF_BYCOMMAND);

    RestrictItbarViewMenu(hmenuITB, SAFECAST( this, IOleCommandTarget* ) );
    ITBar_TrackPopupMenuEx(hmenuITB, TPM_LEFTBUTTON | TPM_RIGHTBUTTON, pt.x, pt.y, _hwnd, prcExclude);

    // HACK: since the ITBar isn't a real bar/bandsite, we have to
    // do this so any menuband that might be up can take back the
    // mouse capture.
    LPBANDITEMDATA pbid = _bs._GetBandItemDataStructByID(CBIDX_MENU);
    if (pbid)
        IUnknown_Exec(pbid->pdb, &CGID_MenuBand, MBANDCID_RECAPTURE, 0, NULL, NULL);

    DestroyMenu (hmenuITB);
}


void CInternetToolbar::_QueryStatusTip(IOleCommandTarget *pct, LPTOOLTIPTEXT pnmTT, UINT uiCmd, const GUID* pguid)
{
    OLECMD rgcmd = { uiCmd, 0 };
    OLECMDTEXTV<MAX_TOOLTIP_STRING> cmdtv;
    OLECMDTEXT *pcmdText = &cmdtv;

    pcmdText->cwBuf = MAX_TOOLTIP_STRING;
    pcmdText->cmdtextf = OLECMDTEXTF_NAME;
    pcmdText->rgwz[0] = 0;
    pct->QueryStatus(pguid, 1, &rgcmd, pcmdText);
    if (rgcmd.cmdf & OLECMDF_ENABLED)
    {
        SHUnicodeToTChar(pcmdText->rgwz, pnmTT->szText, MAX_TOOLTIP_STRING);
    }

}

BOOL _IsDocHostGUID(const GUID* pguid)
{
    // Dochost merges under one of two clsids, so have to check both
    BOOL fRet = IsEqualGUID(*pguid, CLSID_InternetButtons) ||
                IsEqualGUID(*pguid, CLSID_MSOButtons);
    return fRet;
}

void _PruneAmpersands(LPTSTR psz)
{
    //
    // Collapse double ampersands in the string to single
    // ampersands, and rip out single ampersands.  e.g.,
    //
    //  "AT&T" -> "ATT"
    //  "AT&&T" -> "AT&T"
    //
    // We need to do this to hack around the ToolTips control's
    // annoying prefix behavior.  When TTS_NOPREFIX is set
    // (which is true of itbar's), TT leaves prefix characters
    // alone completely.  However when it is not set, besides just
    // letting DrawText do it's prefix thing on the string, TT also
    // pre-processes the string using an analogous version of the
    // below function, collapsing double ampersands to single
    // ampersands and ripping out single ampersands.  This is so
    // that that if you use menu text (e.g. "&File") as a tooltip,
    // you won't get an underline.  Unfortunately, the side effect
    // is that you're in trouble if you really wanted an ampersand in
    // the title (e.g. "AT&&T"), since the preprocessing turns "AT&&T"
    // into "AT&T", which DrawText then renders with an underline.
    //
    // Thus we have to leave TTS_NOPREFIX set and mimic the DrawText
    // preprocessing ourselves.
    //

    if (psz)
    {
        LPTSTR pszOut = psz;
        BOOL fLastAmpSkipped = FALSE;

        while (*psz)
        {
            if (*psz == TEXT('&'))
            {
                if (fLastAmpSkipped)
                {
                    fLastAmpSkipped = FALSE;
                    *pszOut++ = *psz;
                }
                else
                {
                    fLastAmpSkipped = TRUE;
                }
            }
            else
            {
                *pszOut++ = *psz;
            }

            psz++;
        }

        *pszOut = TEXT('\0');
    }
}

void CInternetToolbar::_OnTooltipNeeded(LPTOOLTIPTEXT pnmTT)
{
    UINT uiCmd;
    GUID guid;

    ASSERT(pnmTT->hdr.hwndFrom == (HWND)SendMessage(_btb._hwnd, TB_GETTOOLTIPS, 0, 0));

    // Make sure tooltips don't filter out ampersands
    LONG lStyle = GetWindowLong(pnmTT->hdr.hwndFrom, GWL_STYLE);
    if (!IsFlagSet(lStyle, TTS_NOPREFIX))
    {
        SetWindowLong(pnmTT->hdr.hwndFrom, GWL_STYLE, lStyle | TTS_NOPREFIX);
    }


    if (SUCCEEDED(_btb._ConvertCmd(NULL, (UINT)pnmTT->hdr.idFrom, &guid, &uiCmd)))
    {
        if (IsEqualGUID(guid, CLSID_CommonButtons))
        {
            switch (uiCmd)
            {
            case TBIDM_FORWARD:
            case TBIDM_BACK:
                if (_ptbsite)
                {
                    IBrowserService *pbsvc;

                    if (SUCCEEDED(_ptbsite->QueryInterface(IID_PPV_ARG(IBrowserService, &pbsvc))))
                    {
                        // FEATURE raymondc - make ITravelLog UNICODE someday
                        ITravelLog *ptl;
                        pbsvc->GetTravelLog( &ptl );
                        if (ptl)
                        {
                            WCHAR szTemp[ARRAYSIZE(pnmTT->szText)];
                            if (uiCmd == TBIDM_BACK)
                                ptl->GetToolTipText(pbsvc, TLOG_BACK, 0, szTemp, ARRAYSIZE(szTemp));
                            else if (uiCmd == TBIDM_FORWARD)
                                ptl->GetToolTipText(pbsvc, TLOG_FORE, 0, szTemp, ARRAYSIZE(szTemp));

                            SHUnicodeToTChar(szTemp, pnmTT->szText, ARRAYSIZE(pnmTT->szText));

                            ptl->Release();
                        }
                        pbsvc->Release();
                    }
                }
            }
        }
#ifdef EDIT_HACK
        // Dochost merges under one of two clsids, so have to check both
        else if (_IsDocHostGUID(&guid))
        {
            if (uiCmd == DVIDM_EDITPAGE)
            {
                _aEditVerb.GetToolTip(pnmTT->szText, ARRAYSIZE(pnmTT->szText));
            }
            else
            {
                // If the button text is hidden or truncated, we use that text for the tooltip.
                TBBUTTONINFO tbbi = {0};
                tbbi.cbSize = sizeof(TBBUTTONINFO);
                tbbi.dwMask = TBIF_STYLE | TBIF_STATE;
                SendMessage(_btb._hwnd, TB_GETBUTTONINFO, pnmTT->hdr.idFrom, (LPARAM)&tbbi);

                if (_fCompressed || 
                    IS_LIST_STYLE(_btb._hwnd) && !(tbbi.fsStyle & BTNS_SHOWTEXT) ||
                    (tbbi.fsState & TBSTATE_ELLIPSES))
                {
                    //
                    // Get the button text and fix up the ampersands so that the
                    // tooltip will look right
                    //
                    if (SendMessage(_btb._hwnd, TB_GETBUTTONTEXT, pnmTT->hdr.idFrom, (LPARAM)&pnmTT->szText))
                    {
                        _PruneAmpersands(pnmTT->szText);
                    }
                }
            }
        }
#endif
    }
}

LRESULT CInternetToolbar::_OnBeginDrag(NMREBAR *pnm)
{
    if (SHRestricted2(REST_NOBANDCUSTOMIZE, NULL, 0) || _fLockedToolbar)
    {
        return 1;
    }

    if (_fTheater)
    {
        // if we're in theater mode, we do our own drag handling where we force
        // all  the mouse moves into the middle of the band, thereby disallowing
        // the user to make a multi line rebar
        SetCapture(_hwnd);
        SendMessage(_bs._hwnd, RB_BEGINDRAG, pnm->uBand, (LPARAM)-2);
        _fRebarDragging = TRUE;
        return 1;
    }
    return SHRestricted2(REST_NoToolbarOptions, NULL, 0);
}

LRESULT CInternetToolbar::_OnNotify(LPNMHDR pnmh)
{
    LRESULT lres = 0;
    if (!_pdie)
        return 0;

    if (pnmh->code == TTN_NEEDTEXT  && pnmh->hwndFrom == (HWND)SendMessage(_btb._hwnd, TB_GETTOOLTIPS, 0, 0))
    {
        _OnTooltipNeeded((LPTOOLTIPTEXT)pnmh);
        return 0;
    }

    if(_SendToToolband(pnmh->hwndFrom, WM_NOTIFY,0, (LPARAM)pnmh, &lres))
        return lres;

    switch (pnmh->idFrom)
    {
    case FCIDM_REBAR:
        switch (pnmh->code)
        {
        case RBN_BEGINDRAG:
            return _OnBeginDrag((NMREBAR*)pnmh);

        case RBN_HEIGHTCHANGE:
            ResizeBorderDW(NULL, NULL, FALSE);
            break;

        case RBN_CHILDSIZE:
        {
            // make the brand always take the full height
            NMREBARCHILDSIZE *pnm = (NMREBARCHILDSIZE*)pnmh;
            if (pnm->wID == CBIDX_BRAND)
            {
                pnm->rcChild.top = pnm->rcBand.top;
                pnm->rcChild.bottom = pnm->rcBand.bottom;
            }
            break;
        }

        case RBN_LAYOUTCHANGED:
            //Because the layout has changed, remember to save later!
            _fDirty = TRUE;
            _UpdateBrandSize();

            if (_ptbsitect)
                _ptbsitect->Exec(&CGID_ShellBrowser, FCIDM_PERSISTTOOLBAR, 0, NULL, NULL);
            break;

        case RBN_GETOBJECT:
        {
            NMOBJECTNOTIFY *pnmon = (NMOBJECTNOTIFY *)pnmh;
            if (IsEqualIID(*pnmon->piid, IID_IDropTarget))
            {
                HWND hwnd;

                switch (pnmon->iItem)
                {
                case CBIDX_MENU:
                case CBIDX_LINKS:
                {
                     LPBANDITEMDATA pbid = _bs._GetBandItemDataStructByID(pnmon->iItem);
                     if (pbid && pbid->pdb)
                     {
                        pnmon->hResult = pbid->pdb->QueryInterface(IID_IDropTarget, (void**)&pnmon->pObject);
                     }
                     break;
                }

                case CBIDX_TOOLS:
                    hwnd = _btb._hwnd;
                    pnmon->hResult = (HRESULT)SendMessage(hwnd, TB_GETOBJECT,
                                        (WPARAM)&IID_IDropTarget, (LPARAM)&pnmon->pObject);
                    break;

                case CBIDX_ADDRESS:
                    if (_ptbsite)
                    {
                        pnmon->hResult = _ptbsite->QueryInterface(IID_IDropTarget, (void**)&pnmon->pObject);
                    }
                    break;
                }
            }
            ASSERT((SUCCEEDED(pnmon->hResult) && pnmon->pObject) ? (IS_VALID_CODE_PTR(pnmon->pObject, IUnknown)) : (pnmon->pObject == NULL));
            return TRUE;
        }

        case RBN_CHEVRONPUSHED:
        {
            LPNMREBARCHEVRON pnmch = (LPNMREBARCHEVRON) pnmh;
            if (pnmch->wID == CBIDX_TOOLS)
            {
                int idMenu = MENU_TBMENU;
                // this must be the tools band (not enumerated in bandsite)
                MapWindowPoints(pnmh->hwndFrom, HWND_DESKTOP, (LPPOINT)&pnmch->rc, 2);
                if (!_btb._fCustomize)
                    idMenu = 0;

                ToolbarMenu_Popup(_hwnd, &pnmch->rc, NULL, _btb._hwnd, idMenu, (DWORD)pnmch->lParamNM);
                return TRUE;
            }
            _bs._OnNotify(pnmh);
            break;
        }

        default:
            return _bs._OnNotify(pnmh);

        } // switch (pnmh->code)
        break;

    } // switch (pnmh->idFrom)
    return 0;
}


/*******************************************************************

NAME:       CInternetToolbar::_DoNavigateA

SYNOPSIS:   Called when the user types in or selects a URL
to navigate to through the address bar

NOTES:      This function calls the helper function _DoNavigate.
This is just here to convert the ANSI string to
a BSTR.

********************************************************************/
HRESULT CInternetToolbar::_DoNavigateA(LPSTR pszURL, int iNewSelection)
{
    ASSERT(pszURL);    // must have valid URL to navigate to

    LBSTR::CString          strText;

    LPTSTR          pstrText = strText.GetBuffer( MAX_URL_STRING );

    if ( strText.GetAllocLength() < MAX_URL_STRING )
    {
        TraceMsg( TF_WARNING, "CInternetToolbar::_DoNavigateA() - strText Allocation Failed!" );

        return E_OUTOFMEMORY;
    }
    else
    {
        SHAnsiToUnicode( pszURL, pstrText, MAX_URL_STRING );

        strText.ReleaseBuffer();

        return _DoNavigate( strText, iNewSelection );
    }
}

/*******************************************************************

NAME:       CInternetToolbar::_DoNavigateW

SYNOPSIS:   Called when the user types in or selects a URL
to navigate to through the address bar

NOTES:      This function calls the helper function _DoNavigate.
This is just here to convert the Unicode string to
a BSTR.

********************************************************************/
HRESULT CInternetToolbar::_DoNavigateW(LPWSTR pwzURL, int iNewSelection)
{
    ASSERT(pwzURL);    // must have valid URL to navigate to

    LBSTR::CString          strPath( pwzURL );

    return _DoNavigate( strPath, iNewSelection );
}

/*******************************************************************

NAME:       CInternetToolbar::_DoNavigate

SYNOPSIS:   Called when the user types in or selects a URL
to navigate to through the address bar

ENTRY:      bstrURL - string of URL to navigate to, in BSTR form
iNewSelection - index of current selection in address
bar combo box

********************************************************************/
HRESULT CInternetToolbar::_DoNavigate(BSTR bstrURL,int iNewSelection)
{
    HRESULT hr;

    ASSERT(bstrURL); // must have valid URL to browse to
    ASSERT(_pdie);  // must have valid pointer to automation interface

    VARIANTARG v;
    VariantInit (&v);

    v.vt = VT_I4;
    v.lVal = navAllowAutosearch;

    // call automation interface to make browser navigate to this URL
    hr = _pdie->Navigate(bstrURL, &v, PVAREMPTY, PVAREMPTY, PVAREMPTY);

    VariantClearLazy(&v);

    return hr;
}

LPITEMIDLIST CInternetToolbar::_GetCurrentPidl(void)
{
    LPITEMIDLIST pidl = NULL;

    ASSERT(_pbs2);
    if (_pbs2)
    {
        _pbs2->GetPidl(&pidl);
    }

    return pidl;
}

void CInternetToolbar::_CommonHandleFileSysChange(LONG lEvent, LPITEMIDLIST* ppidl)
{
    // stuff that needs to be done tree or no tree
    switch (lEvent)
    {
        // README:
        // If you need to add events here, then you must change SHELLBROWSER_FSNOTIFY_FLAGS in
        // shbrowse.cpp in order to get the notifications
    case SHCNE_DRIVEREMOVED:
    case SHCNE_MEDIAREMOVED:
    case SHCNE_MEDIAINSERTED:
    case SHCNE_DRIVEADD:
    case SHCNE_UPDATEIMAGE:
    case SHCNE_UPDATEITEM:
        // Forward this command to CAddressBand::FileSysChange()
        // by using IToolbandHelper::OnWinEvent().
        {
            LPBANDITEMDATA pbid = _bs._GetBandItemDataStructByID(CBIDX_ADDRESS);
            if (pbid)
            {
                // REVIEW: why don't we use IShellChangeNotify here?
                //
                IUnknown_FileSysChange(pbid->pdb, (DWORD)lEvent, (LPCITEMIDLIST*)ppidl);
            }
        }
        break;
    }
}

HRESULT CInternetToolbar::OnChange(LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
    LPITEMIDLIST ppidl[2] = {(LPITEMIDLIST)pidl1, (LPITEMIDLIST)pidl2};
    _CommonHandleFileSysChange(lEvent, ppidl);

    return S_OK;
}

void CInternetToolbar::_OnCommand(WPARAM wParam, LPARAM lParam)
{
    if (!_pdie)
        return;

    HWND hwndControl = GET_WM_COMMAND_HWND(wParam, lParam);
    UINT idCmd = GET_WM_COMMAND_ID(wParam, lParam);

    // If this is a command from the toolbar, and it is not one of the StdBrowseButtons
    // call Exec() on the appropriate CmdTarget
    if (hwndControl == _btb._hwnd)
    {
        UINT uiInternalCmdID = idCmd;

        // Convert to the real thing and get the guid
        CMDMAP* pcm = _btb._GetCmdMapByID(idCmd);

        IOleCommandTarget* pct = _btb._CommandTargetFromCmdMap(pcm);
        if (pct)
        {
            VARIANTARG var;
            var.vt = VT_I4;
            var.lVal = uiInternalCmdID;
            if (SHIsSameObject(_btb._pctCurrentButtonGroup, pct))
            {
                // give the browser a chance to pick this off in case
                // focus doesn't belong to the view currently
                if (SUCCEEDED(_ptbsitect->Exec(&IID_IExplorerToolbar, pcm->nCmdID, 0, NULL, &var)))
                    return;
            }

            UEMFireEvent(&UEMIID_BROWSER, UEME_UITOOLBAR, UEMF_XEVENT,
                UIG_COMMON, (LPARAM)pcm->nCmdID);
            pct->Exec(&pcm->guidButtonGroup, (DWORD)pcm->nCmdID, 0, NULL, &var);
        }
        return;
    }

    if (_SendToToolband(hwndControl, WM_COMMAND, wParam, lParam, NULL))
        return;


    // this switch block actually executes
    switch(idCmd)
    {
    case FCIDM_VIEWTOOLBARCUSTOMIZE:
        ASSERT(!SHRestricted2(REST_NOTOOLBARCUSTOMIZE, NULL, 0));
        SendMessage (_btb._hwnd, TB_CUSTOMIZE, 0, 0L);
        break;

    case FCIDM_DRIVELIST:
        _SendToToolband(_hwndAddressBand, WM_COMMAND, wParam, lParam, NULL);
        break;

    case FCIDM_VIEWADDRESS:
    case FCIDM_VIEWTOOLS:
    case FCIDM_VIEWMENU:
    case FCIDM_VIEWLINKS:
        if (!SHRestricted2(REST_NOBANDCUSTOMIZE, NULL, 0)
            && !SHRestricted2(REST_NoToolbarOptions, NULL, 0))
        {
            DWORD dw = _nVisibleBands;
            switch (idCmd)
            {
            case FCIDM_VIEWTOOLS:
                dw ^= VBF_TOOLS;
                break;
            case FCIDM_VIEWMENU:
                dw ^= VBF_MENU;
                break;
            case FCIDM_VIEWADDRESS:
                dw ^= VBF_ADDRESS;
                break;
            case FCIDM_VIEWLINKS:
                dw ^= VBF_LINKS;
                break;
            }
            if ( !( dw & ~VBF_BRAND))
            {
                _pdie->put_ToolBar( FALSE );
            }

            _ShowVisible(dw, TRUE);
        }
        return;

    case FCIDM_VIEWAUTOHIDE:
    {
        ASSERT(_fTheater);
        _fAutoHide = !_fAutoHide;

        VARIANTARG v = {0};
        v.vt = VT_I4;
        v.lVal = _fAutoHide;
        IUnknown_Exec(_ptbsite, &CGID_Theater, THID_SETTOOLBARAUTOHIDE, 0, &v, NULL);

        ResizeBorderDW(NULL, NULL, FALSE);

        break;
    }

    case FCIDM_VIEWLOCKTOOLBAR:
    {
        _fLockedToolbar = !_fLockedToolbar;
        DWORD dwResult = _fLockedToolbar;
        SHSetValue(HKEY_CURRENT_USER, c_szRegKeyCoolbar, TEXT("Locked"), REG_DWORD, &dwResult, sizeof(dwResult));
        _UpdateLocking();
        break;
    }

    case FCIDM_VIEWTEXTLABELS:
        if(!SHRestricted2(REST_NoToolbarOptions, NULL, 0))
        {
            if (!_btb._fCustomize && IS_LIST_STYLE(_btb._hwnd))
            {
                // If customize is unavailable, then there's no way for the user to
                // turn list style on/off.  In this case we want toggling text labels
                // to work the way it did in IE4 -- that is, switch between "text on
                // all buttons" and "text on no buttons".  So, if we're in "selective
                // text on right" mode, we say that labels are turned off.  If the user
                // picks this menu option, we'll go into "text on all buttons" mode.

                _UpdateToolsStyle(FALSE);

                // Make ourselves believe that text labels are turned off (so
                // that _UpdateToolbarDisplay will turn them on)

                _fCompressed = TRUE;
            }
            _UpdateToolbarDisplay(UTD_TEXTLABEL, 0, !_fCompressed, TRUE);
        }
        return;

    case FCIDM_VIEWGOBUTTON:
        _SendToToolband(_hwndAddressBand, WM_COMMAND, wParam, lParam, NULL);
        break;

    default:
        if (InRange( idCmd, FCIDM_EXTERNALBANDS_FIRST, FCIDM_EXTERNALBANDS_LAST ))
        {
            if (!SHRestricted2(REST_NOBANDCUSTOMIZE, NULL, 0))
            {
                DWORD dw = _nVisibleBands;
                dw ^= EXTERNALBAND_VBF_BIT(idCmd - FCIDM_EXTERNALBANDS_FIRST);
                if ( !( dw & ~VBF_BRAND))
                {
                    _pdie->put_ToolBar( FALSE );
                }
                _ShowVisible(dw, TRUE);
            }
            return;
        }
        break;
    }
}

// get the doc property, then get the command target from that and do it
void CInternetToolbar::_SendDocCommand(UINT idCmd)
{
    if (_ptbsitect)
    {
        VARIANTARG varIn;
        VARIANTARG varOut;
        VARIANTARG *pvarIn = &varIn;
        VARIANTARG *pvarOut = &varOut;

        VariantInit(&varIn);
        VariantInit(&varOut);


        switch (idCmd)
        {
        case OLECMDID_HIDETOOLBARS:
        case OLECMDID_PRINT:
            // word & excel barf if there are arguments passed to print
            pvarIn = NULL;
            pvarOut = NULL;
            break;

        case OLECMDID_ZOOM:
            // get the zoom range
            int iMax, iMin;
            if (FAILED(_ptbsitect->Exec(NULL, OLECMDID_GETZOOMRANGE, OLECMDEXECOPT_DONTPROMPTUSER, NULL, &varOut)))
                goto Bail;

            if (varOut.vt == VT_I4)
            {
                iMin = (int)(short)LOWORD(varOut.lVal);
                iMax = (int)(short)HIWORD(varOut.lVal);
            }
            else
                goto Bail;

            varOut.vt = VT_EMPTY; // return to VariantInit state

            // get the current zoom depth
            if (FAILED(_ptbsitect->Exec(NULL, OLECMDID_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, NULL, &varIn)))
                goto Bail;

            if (varIn.vt == VT_I4)
            {
                varIn.lVal++;
                if ((int)varIn.lVal > iMax)
                {
                    varIn.lVal = iMin;
                }
            }
            else
            {
                goto Bail;
            }

            break;

        }

#ifdef FEATURE_IE40
        _ptbsitect->Exec(NULL, idCmd, OLECMDEXECOPT_DONTPROMPTUSER, pvarIn, pvarOut);
#else
        _ptbsitect->Exec(NULL, idCmd, OLECMDEXECOPT_PROMPTUSER, pvarIn, pvarOut);
#endif

Bail:
    // if this fails, we should maybe do the invoke with a
    // proper verb?
    VariantClearLazy(&varIn);
    VariantClearLazy(&varOut);

    }
}

BOOL CInternetToolbar::_CompressBands(BOOL fCompress, UINT uRowsNew, BOOL fForceUpdate)
{
    UINT_PTR uRowsOld = SendMessage(_btb._hwnd, TB_GETTEXTROWS, 0, 0L);
    if (fCompress)
        uRowsNew = 0;

    if (!fForceUpdate && (uRowsOld == uRowsNew))
    {
        // same as what we've already got, blow it off
        return FALSE;
    }

    _fCompressed = fCompress;

    // Change the size of the Brand window and add ot remove the text
    SendMessage(_btb._hwnd, TB_SETMAXTEXTROWS, uRowsNew, 0L);

    UINT uWidthNew = _fCompressed ? MAX_TB_COMPRESSED_WIDTH : _uiMaxTBWidth;
    SendMessage(_btb._hwnd, TB_SETBUTTONWIDTH, 0, (LPARAM) MAKELONG(0, uWidthNew));

    _btb._BandInfoChanged();

    _UpdateBrandSize();
    _bs._SetMinDimensions();

    return TRUE;
}

#define ABS(x)  (((x) < 0) ? -(x) : (x))

void CInternetToolbar::_TrackSliding(int x, int y)
{
    INT_PTR cBands    = SendMessage(_bs._hwnd, RB_GETBANDCOUNT, 0, 0L);
    INT_PTR cRows     = SendMessage(_bs._hwnd, RB_GETROWCOUNT, 0, 0L);
    INT_PTR cyHalfRow = SendMessage(_bs._hwnd, RB_GETROWHEIGHT, cBands-1, 0L) / 2;
    RECT rc;
    int cyBefore;
    int c;
    BOOL_PTR fChanged = FALSE;

    // do this instead of GetClientRect so that we include borders
    GetWindowRect(_bs._hwnd, &rc);
    MapWindowPoints(HWND_DESKTOP, _bs._hwnd, (LPPOINT)&rc, 2);
    cyBefore = rc.bottom - rc.top;

    c = y - _yCapture;
    rc.bottom = y;

    // was there enough change?
    if (ABS(c) <= cyHalfRow)
        return;

    if ((cRows == 1) || _fCompressed)
    {
        if (c < -cyHalfRow)
            fChanged = _CompressBands(TRUE, 0, FALSE);
        else
            fChanged = _CompressBands(FALSE, _uiTBTextRows, FALSE);

    }

    if (!fChanged)
    {
        // if the compressing bands didn't change anything, try to fit it to size
        fChanged = SendMessage(_bs._hwnd, RB_SIZETORECT, 0, (LPARAM)&rc);
    }


    // TODO: There is a drawing glitch when you resize from 3 bars (No Text) to 3 bars
    // with text. The _yCapture gets set to a value greater than y. So on the
    // next MOUSEMOVE it figures that the user moved up and switches from 3 bars with text
    // to 2 bars with text.
    if (fChanged)
    {
        _UpdateBrandSize();
        GetWindowRect(_bs._hwnd, &rc);
        _yCapture += (rc.bottom - rc.top) - cyBefore;
        _fDirty = TRUE; //Since the band layout changed, set the dirty bit ON.
        if (_ptbsitect)
            _ptbsitect->Exec(&CGID_ShellBrowser, FCIDM_PERSISTTOOLBAR, 0, NULL, NULL);
    }
}


void CInternetToolbar::_ShowVisible(DWORD dwVisibleBands, BOOL fPersist)
{
    // PERF (scotth): is this even necessary now that we have a
    //  menu band always showing?
    BOOL fShowInitial = (! (_nVisibleBands & ~VBF_BRAND));

    _UpdateToolbarDisplay(UTD_VISIBLE, dwVisibleBands, _fCompressed, fPersist);

    if (fShowInitial)
        _pdie->put_ToolBar(TRUE);
}


HRESULT CInternetToolbar::_UpdateToolbarDisplay(DWORD dwFlags, UINT uVisibleBands, BOOL fNoText, BOOL fPersist)
{
    _fDirty = TRUE;  //Since we are making changes, set the dirty bit!

    //Update the back bitmap
    _SetBackground();

    //Show the bands.
    if(dwFlags & UTD_VISIBLE)
        _ShowBands(uVisibleBands);

    //Show/Hide the text.
    if(dwFlags & UTD_TEXTLABEL)
        _CompressBands(fNoText, _uiTBTextRows, TRUE);

    _fDirty = TRUE;  //Since we are making changes, set the dirty bit!
    if (!_fTheater && fPersist && _ptbsitect)
        _ptbsitect->Exec(&CGID_ShellBrowser, FCIDM_PERSISTTOOLBAR, 0, NULL, NULL);

    return S_OK;
}

void CInternetToolbar::_UpdateBrandSize()
{
    LPBANDITEMDATA pbid = _bs._GetBandItemDataStructByID(CBIDX_BRAND);
    if (pbid && ((_nVisibleBands & (VBF_TOOLS | VBF_BRAND)) == (VBF_TOOLS | VBF_BRAND)))
    {
        BOOL fMinAlways = _fCompressed;

        if (!fMinAlways)
        {
            INT_PTR iTools = BandIDtoIndex(_bs._hwnd, CBIDX_TOOLS);
            INT_PTR iBrand = BandIDtoIndex(_bs._hwnd, CBIDX_BRAND);

            if (iBrand < iTools && !_fTheater)
                fMinAlways = TRUE;
        }

        VARIANTARG v = {0};
        v.vt = VT_I4;
        v.lVal = fMinAlways;
        IUnknown_Exec(pbid->pdb, &CGID_PrivCITCommands, CITIDM_BRANDSIZE, 0, &v, NULL);
    }
}

LRESULT CALLBACK CInternetToolbar::SizableWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CInternetToolbar* pitbar = (CInternetToolbar*)GetWindowPtr0(hwnd); // GetWindowLong(hwnd, 0)

    switch(uMsg)
    {
    case WM_SETCURSOR:
    {
        if (pitbar->_fLockedToolbar)
        {
            SetCursor(LoadCursor(NULL, IDC_ARROW));
            return TRUE;
        }

        if ((HWND)wParam == hwnd && LOWORD(lParam) == HTCLIENT && 
              !SHRestricted2(REST_NOBANDCUSTOMIZE, NULL, 0)) 
        {
            SetCursor(LoadCursor(NULL, IDC_SIZENS));
            return TRUE;
        }
        goto DoDefault;
    }

    case WM_SYSCOLORCHANGE:
    case WM_WININICHANGE:
        if (FALSE == pitbar->_fDestroyed)
        {
            DWORD dwSection = SHIsExplorerIniChange(wParam, lParam);

            BOOL fRebuild = (uMsg == WM_SYSCOLORCHANGE) ||
                            (dwSection == EICH_KWINEXPLSMICO) ||
                            (wParam == SPI_SETNONCLIENTMETRICS);

            if (fRebuild)
            {
                pitbar->_InitForScreenSize();
                ITBar_LoadToolbarGlyphs(pitbar->_btb._hwnd);
                pitbar->_ReloadBitmapDSA();
                pitbar->_SetSearchStuff();
                pitbar->_ReloadButtons();
#ifdef EDIT_HACK
                if (uMsg == WM_SYSCOLORCHANGE)
                {
                    pitbar->_RefreshEditGlyph();
                }
#endif
            }
            
#ifdef EDIT_HACK
            if (dwSection == EICH_KINET)
            {
                pitbar->_aEditVerb.InitDefaultEditor();
                pitbar->_UpdateEditButton();
            }
#endif

            SendMessage(pitbar->_bs._hwnd, uMsg, wParam, lParam);
            pitbar->_SendToToolband(HWND_BROADCAST, uMsg, wParam, lParam, NULL);

            if (fRebuild)
            {
                pitbar->_SetBackground();
                InvalidateRect(pitbar->_bs._hwnd, NULL, TRUE);
                pitbar->_bs._SetMinDimensions();
            }
        }
        break;

    case WM_LBUTTONDOWN:
        //            RelayToToolTips(prb->hwndToolTips, hwnd, wMsg, wParam, lParam);
        // Don't allow toolbar resizing in theater mode
        if (!pitbar->_fTheater && 
            !SHRestricted2(REST_NOBANDCUSTOMIZE, NULL, 0) && 
            (!pitbar->_fLockedToolbar))
        {
            pitbar->_xCapture = GET_X_LPARAM(lParam);
            pitbar->_yCapture = GET_Y_LPARAM(lParam);
            SetCapture(hwnd);
        }
        break;

    case WM_MOUSEMOVE:
        //            RelayToToolTips(prb->hwndToolTips, hwnd, wMsg, wParam, lParam);

        if (pitbar->_yCapture != -1)
        {
            if (hwnd != GetCapture())
                pitbar->_yCapture = -1;
            else
                pitbar->_TrackSliding(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
        }
        else if (pitbar->_fRebarDragging)
        {
            RECT rc;
            POINT pt;
            GetClientRect(pitbar->_bs._hwnd, &rc);
            GetCursorPos(&pt);
            MapWindowPoints(HWND_DESKTOP, pitbar->_bs._hwnd, &pt, 1);
            rc.bottom /= 2;
            if (pt.y > rc.bottom)
                pt.y = rc.bottom;
            SendMessage(pitbar->_bs._hwnd, RB_DRAGMOVE, 0, MAKELPARAM(pt.x, pt.y));
        }
        break;

    case WM_LBUTTONUP:
    case WM_RBUTTONUP:
        //            RelayToToolTips(prb->hwndToolTips, hwnd, wMsg, wParam, lParam);

        pitbar->_yCapture = -1;
        if (pitbar->_fRebarDragging)
        {
            pitbar->_fRebarDragging = FALSE;
            SendMessage(pitbar->_bs._hwnd, RB_ENDDRAG, 0, 0);
        }
        if (GetCapture() == hwnd)
            ReleaseCapture();
        break;

    case WM_CONTEXTMENU:
        pitbar->_bs.OnWinEvent(pitbar->_hwnd, uMsg, wParam, lParam, NULL);
        break;

    case WM_VKEYTOITEM:
    case WM_CHARTOITEM:
        // We must swallow these messages to avoid infinit SendMessage
        break;

    case WM_NOTIFY:
        // We must swallow these messages to avoid infinit SendMessage
        return pitbar->_OnNotify((LPNMHDR)lParam);

    case WM_NOTIFYFORMAT:
        if (NF_QUERY == lParam)
            return (DLL_IS_UNICODE ? NFR_UNICODE : NFR_ANSI);
        break;

    case WM_COMMAND:
        pitbar->_OnCommand(wParam, lParam);
        break;

    case WM_ERASEBKGND:
        {
            HDC hdc = (HDC)wParam;
            RECT rc;
            GetClientRect(hwnd, &rc);
            SHFillRectClr(hdc, &rc, (pitbar->_fTheater) ? RGB(0,0,0) : GetSysColor(COLOR_3DFACE));
            break;
         }

    case WM_PALETTECHANGED:
        //
        // PERF: we could optimize this by realizing and checking the
        // return value
        //
        // for now we will just invalidate ourselves and all children...
        //
        RedrawWindow(hwnd, NULL, NULL,
                     RDW_INVALIDATE  | RDW_ERASE | RDW_ALLCHILDREN);
        break;

    case WM_MEASUREITEM:
    {
        PMEASUREITEMSTRUCT pmis;

        ASSERT(NULL != lParam);
        pmis = (PMEASUREITEMSTRUCT)lParam;

        switch (pmis->itemID)
        {
        case FCIDM_VBBHISTORYBAND:
            ASSERT(0 == wParam);
            {
                PULONG_PTR pData = (PULONG_PTR)pmis->itemData;
                ASSERT(NULL != pData);
                MeasureMenuItem(pmis, (PCTSTR)pData[0]);
            }
            break;

        default:
            goto DoDefault;
        }
        break;
    }

    case WM_DRAWITEM:
    {
        PDRAWITEMSTRUCT pdis;

        ASSERT(NULL != lParam);
        pdis = (PDRAWITEMSTRUCT)lParam;

        switch (pdis->itemID)
        {
        case FCIDM_VBBHISTORYBAND:
            ASSERT(0 == wParam);
            {
                PULONG_PTR pData = (PULONG_PTR)pdis->itemData;
                ASSERT(NULL != pData);
                DrawMenuItem(pdis, (PCTSTR)pData[0], (UINT) pData[1]);
            }
            break;

        default:
            goto DoDefault;
        }
        break;
    }

    case WM_MENUCHAR:
        if (MF_POPUP == HIWORD(wParam))
        {
            MENUITEMINFO mii = { 0 };

            mii.cbSize = sizeof(mii);
            mii.fMask  = MIIM_DATA | MIIM_TYPE;
            GetMenuItemInfo((HMENU)lParam, FCIDM_VBBHISTORYBAND, FALSE, &mii);

            if (TEXT('h') == LOWORD(wParam) || TEXT('H') == LOWORD(wParam))
            {
                return MAKELRESULT(FCIDM_VBBHISTORYBAND, MNC_EXECUTE);
            }
        }
        break;

    case WM_TIMER:
        switch (wParam)
        {
        case IDT_UPDATETOOLBAR:
            pitbar->_fUpdateToolbarTimer = FALSE;
            KillTimer(hwnd, wParam);
            if (pitbar->_fNeedUpdateToolbar)
                pitbar->_UpdateToolbarNow();
            break;
        }
        break;

    case WM_DESTROY:
        pitbar->_Unadvise(); // remove ref-loop with _pdie
        TraceMsg(DM_TBREF, "CInternetToolbar::SizableWndProc() - Called RemoveProp. Called Release new _cRef=%d", pitbar->_cRef);
        goto DoDefault;

DoDefault:
    default:
        return(DefWindowProcWrap(hwnd, uMsg, wParam, lParam));
    }

    return 0L;
}


HRESULT CInternetToolbar::ResizeBorderDW(LPCRECT prcBorder,
                                         IUnknown* punkToolbarSite,
                                         BOOL fReserved)
{
    TraceMsg(DM_LAYOUT, "CITB::ResizeBorderDW called (_fShow==%d)", _fShow);
    HRESULT hres = S_OK;

    ASSERT(_ptbsite);
    if (_ptbsite)
    {
        RECT rcRequest = { 0, 0, 0, 0 };

        if (_fShow)
        {
            RECT rcRebar, rcBorder;
            int  cx,cy;

            GetWindowRect(_bs._hwnd, &rcRebar);
            cx = rcRebar.right - rcRebar.left;
            cy = rcRebar.bottom - rcRebar.top;

            int iExtra = 3;
            if (_fTheater)
            {
                // 1 for the 1 pixel border on the bottom
                iExtra = 1;
            }
            else if (_fLockedToolbar)
            {
                iExtra = 0;
            }

            TraceMsg(DM_LAYOUT, "CITB::ResizeBorderDW cy = %d", cy);

            if (!prcBorder)
            {
                _ptbsite->GetBorderDW(SAFECAST(this, IDockingWindow*), &rcBorder);
                prcBorder = &rcBorder;
            }

            cx = prcBorder->right - prcBorder->left;


            SetWindowPos(_bs._hwnd, NULL, 0, 0,
                         cx, cy,  SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);

            GetWindowRect(_bs._hwnd, &rcRebar);


            rcRequest.top = rcRebar.bottom - rcRebar.top + iExtra;
            SetWindowPos(_hwnd, NULL, prcBorder->left, prcBorder->top,
                         rcRebar.right - rcRebar.left, rcRequest.top, SWP_NOZORDER | SWP_NOACTIVATE);
        }

        if (_fTheater && _fAutoHide)
        {
            // if we're in theater mode, then we should request no space
            rcRequest.left = rcRequest.top = 0;
        }

        TraceMsg(DM_LAYOUT, "CITB::ResizeBorderDW calling RequstBS with %d,%d,%d,%d",
                 rcRequest.left, rcRequest.top, rcRequest.right, rcRequest.bottom);
        _ptbsite->RequestBorderSpaceDW(SAFECAST(this, IDockingWindow*), &rcRequest);

        TraceMsg(DM_LAYOUT, "CITB::ResizeBorderDW calling SetBS with %d,%d,%d,%d",
                 rcRequest.left, rcRequest.top, rcRequest.right, rcRequest.bottom);
        _ptbsite->SetBorderSpaceDW(SAFECAST(this, IDockingWindow*), &rcRequest);
    }

    return hres;
}

HRESULT CInternetToolbar::QueryStatus(const GUID *pguidCmdGroup,
                                      ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
{
    HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;

    LPBANDITEMDATA pbid = _bs._GetBandItemDataStructByID(CBIDX_ADDRESS);
    if (pbid && pbid->pdb)
    {
        IOleCommandTarget *poct;
        if (SUCCEEDED(pbid->pdb->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &poct))))
        {
            hr = poct->QueryStatus(pguidCmdGroup, cCmds, rgCmds, pcmdtext);
            poct->Release();
        }
    }

    if (pguidCmdGroup && IsEqualGUID(CGID_PrivCITCommands, *pguidCmdGroup))
    {
        hr = S_OK;
        for (ULONG i = 0 ; i < cCmds; i++)
        {
            rgCmds[i].cmdf = 0;
            switch (rgCmds[i].cmdID)
            {
            case CITIDM_VIEWTOOLS:
                if (_nVisibleBands & VBF_TOOLS)
                    rgCmds[i].cmdf = OLECMDF_ENABLED;
                break;

            case CITIDM_VIEWMENU:
                if (_nVisibleBands & VBF_MENU)
                    rgCmds[i].cmdf = OLECMDF_ENABLED;
                break;

            case CITIDM_VIEWTOOLBARCUSTOMIZE:
                if (_btb._fCustomize)
                    rgCmds[i].cmdf = OLECMDF_ENABLED;
                break;

            case CITIDM_VIEWAUTOHIDE:
                if (_fAutoHide)
                    rgCmds[i].cmdf = OLECMDF_ENABLED;
                break;

            case CITIDM_VIEWLOCKTOOLBAR:
                if (_fLockedToolbar)
                    rgCmds[i].cmdf = OLECMDF_ENABLED;
                break;

            case CITIDM_VIEWADDRESS:
                if (_nVisibleBands & VBF_ADDRESS)
                    rgCmds[i].cmdf = OLECMDF_ENABLED;
                break;

            case CITIDM_VIEWLINKS:
                if (_nVisibleBands & VBF_LINKS)
                    rgCmds[i].cmdf = OLECMDF_ENABLED;
                break;

            case CITIDM_TEXTLABELS:
                if (!_fCompressed)
                    rgCmds[i].cmdf = OLECMDF_ENABLED;
                break;

            case CITIDM_EDITPAGE:
                if (_fEditEnabled)
                    rgCmds[i].cmdf = OLECMDF_ENABLED;
#ifdef EDIT_HACK
                // The tooltip text is also used for the menu
                if (pcmdtext)
                {
                    TCHAR szBuf[MAX_PATH];
                    if ((pcmdtext->cmdtextf == OLECMDTEXTF_NAME) &&
                         _aEditVerb.GetMenuText(szBuf, ARRAYSIZE(szBuf)))
                    {
                        SHTCharToUnicode(szBuf, pcmdtext->rgwz, pcmdtext->cwBuf);
                        pcmdtext->cwActual = lstrlenW(pcmdtext->rgwz) + 1;
                    }
                    else
                    {
                        pcmdtext->cwActual = 0;
                    }
                }
#endif
                break;
            default:
                if (InRange( rgCmds[i].cmdID, CITIDM_VIEWEXTERNALBAND_FIRST, CITIDM_VIEWEXTERNALBAND_LAST))
                {
                    int iBand = rgCmds[i].cmdID - CITIDM_VIEWEXTERNALBAND_FIRST;
                    if (!IsEqualCLSID( _rgebi[iBand].clsid, GUID_NULL ))
                    {
                        rgCmds[i].cmdf |= OLECMDF_SUPPORTED;
                        if (_nVisibleBands & EXTERNALBAND_VBF_BIT( iBand ))
                        {
                            rgCmds[i].cmdf |= OLECMDF_ENABLED;
                        }
                        if (pcmdtext)
                        {
                            pcmdtext->rgwz[0] = TEXT('\0');
                            switch (pcmdtext->cmdtextf)
                            {
                            case OLECMDTEXTF_NAME:
                                if (_rgebi[iBand].pwszName)
                                    Str_GetPtrW(_rgebi[iBand].pwszName, pcmdtext->rgwz, pcmdtext->cwBuf );
                                break;

                            case OLECMDTEXTF_STATUS:
                                if (_rgebi[iBand].pwszHelp)
                                    Str_GetPtrW(_rgebi[iBand].pwszHelp, pcmdtext->rgwz, pcmdtext->cwBuf );
                                break;

                            default:
                                break;
                            }
                            pcmdtext->cwActual = lstrlen( pcmdtext->rgwz );
                        }
                    }
                }
                break;
            }
        }
    }
    return hr;
}

void CInternetToolbar::_RestoreSaveStruct(COOLBARSAVE* pcs)
{
    REBARBANDINFO rbbi;
    rbbi.cbSize = sizeof(REBARBANDINFO);
    int i;

    _fAutoHide = pcs->fAutoHide;
    _ShowVisible(pcs->uiVisible, FALSE);

    BOOL fAllowRetry = TRUE;
    BOOL fNeedRetry = FALSE;
    INT_PTR fRedraw = SendMessage(_bs._hwnd, WM_SETREDRAW, FALSE, 0);
Retry:
    for (i = 0; i < CBANDSMAX; i++)
    {
        INT_PTR iIndex = SendMessage(_bs._hwnd, RB_IDTOINDEX, pcs->bs[i].wID, 0);
        if (iIndex != -1)
        {
            SendMessage(_bs._hwnd, RB_MOVEBAND, iIndex, i);
            rbbi.fMask = RBBIM_STYLE;
            if (SendMessage(_bs._hwnd, RB_GETBANDINFO, i, (LPARAM) &rbbi))
            {
                rbbi.fMask = RBBIM_SIZE | RBBIM_STYLE;
                rbbi.cx = pcs->bs[i].cx;
                rbbi.fStyle = pcs->bs[i].fStyle;
                SendMessage(_bs._hwnd, RB_SETBANDINFO, i, (LPARAM) &rbbi);
            }

            // the SetBandInfo could have potentially caused items to shift around
            // verify that this didn't happen.
            iIndex = SendMessage(_bs._hwnd, RB_IDTOINDEX, pcs->bs[i].wID, 0);
            if (iIndex != i)
            {
                fNeedRetry = TRUE;
            }
        }
    }

    if (fAllowRetry && fNeedRetry)
    {
        fAllowRetry = FALSE;
        goto Retry;
    }

    _CSHSetStatusBar(pcs->fStatusBar);
    _UpdateToolsStyle(pcs->fList);

    RECT rc;
    GetWindowRect(_bs._hwnd, &rc);
    SetWindowPos(_bs._hwnd, NULL, 0,0, RECTWIDTH(rc), pcs->cyRebar, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);

    SendMessage(_bs._hwnd, WM_SETREDRAW, fRedraw, 0);
}

void CInternetToolbar::_CSHSetStatusBar(BOOL fOn)
{
    VARIANTARG v = { 0 };
    v.vt = VT_I4;
    v.lVal = fOn;
    IUnknown_Exec(_ptbsite, &CGID_ShellBrowser, FCIDM_SETSTATUSBAR,
        0, &v, NULL);
}

void CInternetToolbar::_TheaterModeLayout(BOOL fEnter)
{
    static const struct {
        int id;
        int cx;
    } c_layout[] =
    {
        { CBIDX_TOOLS, 400 },
        { CBIDX_MENU, 200 },
        { CBIDX_ADDRESS, 300 },
        { CBIDX_LINKS, 40 }
    };

    REBARBANDINFO rbbi;
    rbbi.cbSize = sizeof(REBARBANDINFO);

    BOOL_PTR fRedraw = SendMessage(_bs._hwnd, WM_SETREDRAW, FALSE, 0);
    SHSetWindowBits(_bs._hwnd, GWL_STYLE, RBS_AUTOSIZE, 0);
    if (fEnter)
    {
        _BuildSaveStruct(&_cs);

        // turn off text labels
        COOLBARSAVE cs;
        DWORD dwType, cbSize = sizeof(COOLBARSAVE);
        if (SHRegGetUSValue(c_szRegKeyCoolbar, c_szValueTheater, &dwType, (void*)&cs, &cbSize, FALSE, NULL, 0) == ERROR_SUCCESS &&
           cs.cbVer == CBS_VERSION)
        {
            _RestoreSaveStruct(&cs);
            _UpdateToolbarDisplay(UTD_TEXTLABEL, 0, TRUE, TRUE);
        }
        else
        {
            _UpdateToolbarDisplay(UTD_TEXTLABEL, 0, TRUE, TRUE);
            _ShowVisible(VBF_TOOLS | VBF_BRAND, FALSE); // only show tools band by default
            RECT rc = { 0, 0, GetSystemMetrics(SM_CXSCREEN), 20 }; // something arbitrarily small vertically
            SendMessage(_bs._hwnd, RB_SIZETORECT, 0, (LPARAM)&rc);

            int cBands = (int) SendMessage(_bs._hwnd, RB_GETBANDCOUNT, 0, 0L);
            int i;
            // strip off all blanks
            rbbi.fMask = RBBIM_STYLE;
            for (i = 0; i < cBands; i++)
            {
                if (SendMessage(_bs._hwnd, RB_GETBANDINFO, i, (LPARAM) &rbbi))
                {
                    rbbi.fStyle &= ~RBBS_BREAK;
                    SendMessage(_bs._hwnd, RB_SETBANDINFO, i, (LPARAM) &rbbi);
                }
            }

            // then move into the proper order and size
            for (i = 0; i < ARRAYSIZE(c_layout); i++)
            {
                INT_PTR iIndex = SendMessage(_bs._hwnd, RB_IDTOINDEX, c_layout[i].id, 0);
                if (iIndex != -1)
                {
                    SendMessage(_bs._hwnd, RB_MOVEBAND, iIndex, i);

                    rbbi.fMask = RBBIM_SIZE;
                    rbbi.cx = c_layout[i].cx;
                    SendMessage(_bs._hwnd, RB_SETBANDINFO, i, (LPARAM) &rbbi);
                }
            }
            _CSHSetStatusBar(FALSE);  // default value in theater mode
        }
        SHSetWindowBits(_bs._hwnd, GWL_STYLE, RBS_BANDBORDERS | WS_BORDER, RBS_BANDBORDERS);
    }
    else
    {
        COOLBARSAVE cs;
        _BuildSaveStruct(&cs);
        SHRegSetUSValue(c_szRegKeyCoolbar, c_szValueTheater, REG_BINARY,
                        (void*)&cs, sizeof(COOLBARSAVE), SHREGSET_HKCU | SHREGSET_FORCE_HKCU);
        _RestoreSaveStruct(&_cs);
        _UpdateToolbarDisplay(UTD_TEXTLABEL, 0, _cs.fNoText, FALSE);
        SHSetWindowBits(_bs._hwnd, GWL_STYLE, RBS_BANDBORDERS | WS_BORDER, RBS_BANDBORDERS | WS_BORDER);
    }

    _SetBackground();
    SHSetWindowBits(_bs._hwnd, GWL_STYLE, RBS_AUTOSIZE, RBS_AUTOSIZE);
    SendMessage(_bs._hwnd, WM_SETREDRAW, fRedraw, 0);

    SetWindowPos(_bs._hwnd, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
}

HRESULT CInternetToolbar::_GetMinRowHeight()
{
    UINT iHeight = 0;
    int icBands = (int) SendMessage( _bs._hwnd, RB_GETBANDCOUNT, 0, 0 );
    for (int i = 0; i < icBands; i++)
    {
        REBARBANDINFO rbbi;
        rbbi.cbSize = sizeof(REBARBANDINFO);
        rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_STYLE;
        if (SendMessage(_bs._hwnd, RB_GETBANDINFO, i, (LPARAM)&rbbi))
        {
            // go until the end of the row
            if (rbbi.fStyle & RBBS_BREAK)
                break;

            if (!(rbbi.fStyle & RBBS_HIDDEN))
            {
                if (rbbi.cyMinChild > iHeight)
                    iHeight = rbbi.cyMinChild;
            }
        }
    }

    return ResultFromShort(iHeight);
}

BOOL IsBarRefreshable(IDeskBar* pdb)
{
    ASSERT(pdb);
    BOOL fIsRefreshable = TRUE;
    VARIANT varClsid = {0};

    if (SUCCEEDED(IUnknown_Exec(pdb, &CGID_DeskBarClient, DBCID_CLSIDOFBAR, 1, NULL, &varClsid)) && (varClsid.vt == VT_BSTR))
    {
        CLSID clsidBar;

        //if the bar is hidden, it returns GUID_NULL, so don't refresh it
        if ( GUIDFromString(varClsid.bstrVal, &clsidBar) &&
             (IsEqualGUID(clsidBar, GUID_NULL)) )
        {
            fIsRefreshable = FALSE;
        }
        else
        {
            //APPHACK for office discussions band (and possibly others)
            //CLSID\GUID\Instance
            WCHAR wszKey[6+40+1+9];
            DWORD dwValue, dwType=REG_DWORD, dwcbData = 4;
            wnsprintf(wszKey, ARRAYSIZE(wszKey), L"CLSID\\%s\\Instance", varClsid.bstrVal);

            if ( (SHGetValue(HKEY_CLASSES_ROOT, wszKey, L"DontRefresh", &dwType, &dwValue, &dwcbData) == ERROR_SUCCESS) &&
                 (dwValue != 0) )
            {
                fIsRefreshable = FALSE;
            }
        }
        VariantClear(&varClsid);
    }
    return fIsRefreshable;
}

HRESULT CInternetToolbar::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
    DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
    HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;  // assume failure

    if (!pguidCmdGroup)
    {
        goto Lother;
    }
    else if (IsEqualGUID(CLSID_CommonButtons, *pguidCmdGroup))
    {
        if (pvarargOut)
        {
            ASSERT(pvarargOut && pvarargOut->vt == VT_I4);
            UINT uiInternalCmdID = pvarargOut->lVal;

            if (nCmdID == TBIDM_SEARCH && uiInternalCmdID == -1)
                _btb._ConvertCmd(pguidCmdGroup, nCmdID, NULL, &uiInternalCmdID);

            switch (nCmdID)
            {
            case TBIDM_BACK:
            case TBIDM_FORWARD:
            case TBIDM_STOPDOWNLOAD:
            case TBIDM_REFRESH:
            case TBIDM_HOME:
            case TBIDM_SEARCH:
            case TBIDM_FAVORITES:
            case TBIDM_HISTORY:
            case TBIDM_ALLFOLDERS:
            case TBIDM_MEDIABAR:
                if (!SendMessage(_btb._hwnd, TB_ISBUTTONENABLED, uiInternalCmdID, 0))
                    return S_OK;
                break;
            }
            if (nCmdexecopt == OLECMDEXECOPT_PROMPTUSER)
            {
                // the user hit the drop down
                if (_ptbsitect && pvarargIn && pvarargIn->vt == VT_INT_PTR)
                {
                    // v.vt = VT_I4;
                    POINT pt;
                    RECT* prc = (RECT*)pvarargIn->byref;
                    pt.x = prc->left;
                    pt.y = prc->bottom;

                    switch (nCmdID)
                    {
                    case TBIDM_BACK:
                        _ShowBackForwardMenu(FALSE, pt, prc);
                        break;

                    case TBIDM_FORWARD:
                        _ShowBackForwardMenu(TRUE, pt, prc);
                        break;
                    }
                    // VariantClearLazy(&v);
                }
                return S_OK;
            }

            switch(nCmdID)
            {
            case TBIDM_PREVIOUSFOLDER:
                _ptbsitect->Exec(&CGID_ShellBrowser, FCIDM_PREVIOUSFOLDER, 0, NULL, NULL);
                break;

            case TBIDM_CONNECT:
                DoNetConnect(_hwnd);
                break;

            case TBIDM_DISCONNECT:
                DoNetDisconnect(_hwnd);
                break;

            case TBIDM_BACK:
                _pdie->GoBack();
                break;

            case TBIDM_FORWARD:
                _pdie->GoForward();
                break;

            case TBIDM_HOME:
                _pdie->GoHome();
                break;

            case TBIDM_SEARCH:
                if (_ptbsitect)
                {
                    VARIANTARG vaOut = {0};
                    VARIANTARG* pvaOut = NULL;
                    LPITEMIDLIST pidl = NULL;

                    // i'm leaving this not #ifdefed out because it is used by explorer bar
                    // persistance (reljai)
                    //_SetSearchStuff initializes _guidDefaultSearch, which may or may not have
                    // been called yet
                    if (IsEqualGUID(_guidDefaultSearch, GUID_NULL))
                        _SetSearchStuff();

                    // see if what the state of search pane is, so we can toggle it...
                    OLECMD rgcmds[] = {{ SBCMDID_SEARCHBAR, 0 },};
                    
                    if (_ptbsitect)
                        _ptbsitect->QueryStatus(&CGID_Explorer, ARRAYSIZE(rgcmds), rgcmds, NULL);
                    // not pressed, then show the pane
                    if (!(rgcmds[0].cmdf & OLECMDF_LATCHED))
                    {
                        WCHAR       wszUrl[MAX_URL_STRING];

                        if (_GetSearchUrl(wszUrl, ARRAYSIZE(wszUrl)))
                        {
                            CLSID clsid;

                            if (GUIDFromString(wszUrl, &clsid))
                            {
                                IContextMenu* pcm;

                                if (SUCCEEDED(SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IContextMenu, &pcm))))
                                {
                                    CMINVOKECOMMANDINFO ici = {0};
                                    CHAR                szGuid[GUIDSTR_MAX];
                                    BOOL                bSetSite = TRUE;

                                    ici.cbSize = sizeof(ici);
                                    ici.hwnd = _hwnd;
                                    ici.lpVerb = (LPSTR)MAKEINTRESOURCE(0);
                                    ici.nShow  = SW_NORMAL;
                                    SHStringFromGUIDA(_guidDefaultSearch, szGuid, ARRAYSIZE(szGuid));
                                    ici.lpParameters = szGuid;

                                    // in case of rooted browser we need to open the search in the new window
                                    // 'coz otherwise the pane opens in the same window and user starts search
                                    // and browseobject (or someone) detects the rooted case and launches new
                                    // browser for our search results view (which is blank because it cannot do
                                    // search by itself and also there is not search pane) (reljai)
                                    if (_pbs2)
                                    {
                                        LPITEMIDLIST pidl;

                                        if (SUCCEEDED(_pbs2->GetPidl(&pidl)))
                                        {
                                            bSetSite = !ILIsRooted(pidl);
                                            ILFree(pidl);
                                        }
                                    }
                                    // if there is no site, InvokeCommand bellow will launch new browser w/ the 
                                    // search pane open
                                    if (bSetSite)
                                        IUnknown_SetSite(pcm, _psp);
                                    hr = pcm->InvokeCommand(&ici);
                                    if (bSetSite)
                                        IUnknown_SetSite(pcm, NULL);
                                    pcm->Release();
                                }
                                break;
                            }
                            //_guidCurrentSearch = _guidDefaultSearch;//done on set state
                            IECreateFromPathW(wszUrl, &pidl);
                            // convert pidl into VARIANT
                            // way to pass the pidl, so...
                            InitVariantFromIDList(&vaOut, pidl);
                            pvaOut = &vaOut;
                        }
                    }

                    hr = _ptbsitect->Exec(&CGID_Explorer, SBCMDID_SEARCHBAR, OLECMDEXECOPT_DONTPROMPTUSER, NULL, pvaOut); // vaIn:NULL means toggle
                    ASSERT(SUCCEEDED(hr));
                    if (pvaOut)
                        VariantClear(pvaOut);
                    ILFree(pidl);
                }
                else
                {
                    TraceMsg(DM_ERROR, "CITBar::Exec: no IOleCommandTarget!");
                }
                break;

            case TBIDM_FAVORITES:
            case TBIDM_HISTORY:
            case TBIDM_ALLFOLDERS:
            case TBIDM_MEDIABAR:
                if (_ptbsitect) 
                {
                    static const int tbtab[] = {
                        TBIDM_FAVORITES    , TBIDM_HISTORY    ,   TBIDM_ALLFOLDERS   , TBIDM_MEDIABAR  ,
                    };
                    static const int cttab[] = {
                        SBCMDID_FAVORITESBAR, SBCMDID_HISTORYBAR, SBCMDID_EXPLORERBAR, SBCMDID_MEDIABAR,
                    };
                    HRESULT hres;
                    int idCT;

                    idCT = SHSearchMapInt(tbtab, cttab, ARRAYSIZE(tbtab), nCmdID);
                    hres = _ptbsitect->Exec(&CGID_Explorer, idCT, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL);  // vaIn:NULL means toggle
                    ASSERT(SUCCEEDED(hres));
                }
                else
                {
                    TraceMsg(DM_ERROR, "CITBar::Exec: no IOleCommandTarget!");
                }
                break;

            case TBIDM_THEATER:
                {
                    VARIANT_BOOL b;
                    if (SUCCEEDED(_pdie->get_TheaterMode(&b)))
                        _pdie->put_TheaterMode( b == VARIANT_TRUE ? VARIANT_FALSE : VARIANT_TRUE);
                    break;
                }

            case TBIDM_STOPDOWNLOAD:
                if (_fTransitionToHTML)
                {
                    UINT uiState;
                    _fTransitionToHTML = FALSE;
                    if (SUCCEEDED(GetState(&CLSID_CommonButtons, TBIDM_STOPDOWNLOAD, &uiState)))
                    {
                        uiState |= TBSTATE_HIDDEN;
                        SetState(&CLSID_CommonButtons, TBIDM_STOPDOWNLOAD, uiState);
                    }
                    SendMessage(_hwndAddressBand, CB_SETEDITSEL, NULL, (LPARAM)MAKELONG(-1,0));
                }
                _pdie->Stop();
                break;

            case TBIDM_REFRESH:
            {
                VARIANT v = {0};
                v.vt = VT_I4;
                v.lVal = (GetAsyncKeyState(VK_CONTROL) < 0) ?
                         OLECMDIDF_REFRESH_COMPLETELY|OLECMDIDF_REFRESH_PROMPTIFOFFLINE :
                         OLECMDIDF_REFRESH_NO_CACHE|OLECMDIDF_REFRESH_PROMPTIFOFFLINE;
                _pdie->Refresh2(&v);

                if (_hwndAddressBand)
                {
                    LPBANDITEMDATA pbid = _bs._GetBandItemDataStructByID(CBIDX_ADDRESS);
                    if (pbid && pbid->pdb)
                    {
                        IAddressBand *pab = NULL;
                        if (SUCCEEDED(pbid->pdb->QueryInterface(IID_PPV_ARG(IAddressBand, &pab))))
                        {
                            VARIANTARG varType = {0};
                            varType.vt = VT_I4;
                            varType.lVal = OLECMD_REFRESH_TOPMOST;
                            pab->Refresh(&varType);
                            pab->Release();
                        }
                    }
                }

                // pass this to vert and horz bars
                IDockingWindowFrame *psb;
                if (_psp && SUCCEEDED(_psp->QueryService(SID_STopLevelBrowser, IID_PPV_ARG(IDockingWindowFrame, &psb))))
                {
                    IDeskBar* pdb;

                    if (SUCCEEDED(psb->FindToolbar(INFOBAR_TBNAME, IID_PPV_ARG(IDeskBar, &pdb))) && pdb &&
                        IsBarRefreshable(pdb))
                    {
                        IUnknown_Exec(pdb, NULL, OLECMDID_REFRESH, OLECMDIDF_REFRESH_NORMAL|OLECMDIDF_REFRESH_PROMPTIFOFFLINE, NULL, NULL);
                        pdb->Release();
                    }
                    if (SUCCEEDED(psb->FindToolbar(COMMBAR_TBNAME, IID_PPV_ARG(IDeskBar, &pdb))) && pdb &&
                        IsBarRefreshable(pdb))
                    {
                        IUnknown_Exec(pdb, NULL, OLECMDID_REFRESH, OLECMDIDF_REFRESH_NORMAL|OLECMDIDF_REFRESH_PROMPTIFOFFLINE, NULL, NULL);
                        pdb->Release();
                    }
                    psb->Release();
                }

            }
                break;
            }
        }
    } 
    else if (IsEqualGUID(IID_IExplorerToolbar, *pguidCmdGroup)) 
    {
        switch (nCmdID)
        {
        case ETCMDID_GETBUTTONS:
            {
                if (_iButtons == -1) 
                {
                    // haven't initialized yet
                    _iButtons = ARRAYSIZE(c_tbExplorer);
                    memcpy(_tbExplorer, c_tbExplorer, sizeof(TBBUTTON) * ARRAYSIZE(c_tbExplorer));

                    if (IS_BIDI_LOCALIZED_SYSTEM())
                    {
                        if (!SHUseClassicToolbarGlyphs())
                        {
                            _tbExplorer[0].iBitmap = 1;
                            _tbExplorer[1].iBitmap = 0;
                        }
                    }

                    if (GetUIVersion() < 5) 
                    {
                        // we don't want up button and network drive buttons available
                        // on < nt5 shell (by trident pm design)

                        // no customization in shell view on < nt5
                        ASSERT(!_fShellView);

                        ASSERT(c_tbExplorer[TBXID_PREVIOUSFOLDER].idCommand == TBIDM_PREVIOUSFOLDER);
                        _tbExplorer[TBXID_PREVIOUSFOLDER].fsState |= TBSTATE_HIDDEN;

                        ASSERT(c_tbExplorer[TBXID_CONNECT].idCommand == TBIDM_CONNECT);
                        _tbExplorer[TBXID_CONNECT].fsState |= TBSTATE_HIDDEN;

                        ASSERT(c_tbExplorer[TBXID_DISCONNECT].idCommand == TBIDM_DISCONNECT);
                        _tbExplorer[TBXID_DISCONNECT].fsState |= TBSTATE_HIDDEN;

                        ASSERT(c_tbExplorer[TBXID_ALLFOLDERS].idCommand == TBIDM_ALLFOLDERS);
                        if (!_FoldersButtonAvailable())
                            _tbExplorer[TBXID_ALLFOLDERS].fsState |= TBSTATE_HIDDEN;
                    }
                    else
                    {
                        ASSERT(c_tbExplorer[TBXID_SEARCH].idCommand == TBIDM_SEARCH);
                        if (_fShellView && SHRestricted(REST_NOSHELLSEARCHBUTTON))
                            _tbExplorer[TBXID_SEARCH].fsState |= TBSTATE_HIDDEN;
                            
                        ASSERT(c_tbExplorer[TBXID_CONNECT].idCommand == TBIDM_CONNECT);
                        ASSERT(c_tbExplorer[TBXID_DISCONNECT].idCommand == TBIDM_DISCONNECT);
                        if (SHRestricted(REST_NONETCONNECTDISCONNECT))
                        {
                            _tbExplorer[TBXID_CONNECT].fsState |= TBSTATE_HIDDEN;
                            _tbExplorer[TBXID_DISCONNECT].fsState |= TBSTATE_HIDDEN;
                        }
                    }
                    ASSERT(_tbExplorer[TBXID_MEDIABAR].idCommand == TBIDM_MEDIABAR);
                    if (_fShellView || SHRestricted2W(REST_No_LaunchMediaBar, NULL, 0) || !CMediaBarUtil::IsWMP7OrGreaterCapable())
                    {
                        _tbExplorer[TBXID_MEDIABAR].fsState |= TBSTATE_HIDDEN;
                    }

                    ASSERT(_tbExplorer[TBXID_PREVIOUSFOLDER].idCommand == TBIDM_PREVIOUSFOLDER);
                    if (!_fShellView) 
                    {
                        _tbExplorer[TBXID_PREVIOUSFOLDER].fsState |= TBSTATE_HIDDEN;
                    }
                    
                    _iButtons = RemoveHiddenButtons(_tbExplorer, ARRAYSIZE(_tbExplorer));
                }

                pvarargOut->vt = VT_BYREF;
                pvarargOut->byref = (void*)_tbExplorer;
                *pvarargIn->plVal = _iButtons;
            }
            return S_OK;
        }
    }
    else if (IsEqualGUID(CGID_PrivCITCommands, *pguidCmdGroup))
    {
        DWORD dw;
        hr = S_OK;
        switch (nCmdID)
        {
        case CITIDM_GETFOLDERSEARCHES:
            {
                hr = E_INVALIDARG;

                if (pvarargOut)
                {
                    IFolderSearches *pfs;
                    hr = _GetFolderSearches(&pfs);

                    if (SUCCEEDED(hr))
                    {
                        VariantClear(pvarargOut);
                        pvarargOut->vt = VT_UNKNOWN;
                        pvarargOut->punkVal = pfs;
                    }
                }
            }
            break;

        case CITIDM_SET_DIRTYBIT:
            _fDirty = BOOLIFY(nCmdexecopt);
            break;

        case CITIDM_GETMINROWHEIGHT:
            hr = _GetMinRowHeight();
            break;

        case CITIDM_VIEWTOOLBARCUSTOMIZE:
            _OnCommand(GET_WM_COMMAND_MPS(FCIDM_VIEWTOOLBARCUSTOMIZE, _hwnd, 0));
            break;

        case CITIDM_TEXTLABELS:
            _OnCommand(GET_WM_COMMAND_MPS(FCIDM_VIEWTEXTLABELS, _hwnd, 0));
            break;

        case CITIDM_EDITPAGE:
            // FEATURE: temp code -- edit code moving to dochost.cpp
            _btb.Exec(&CLSID_InternetButtons, DVIDM_EDITPAGE, 0, NULL, NULL);
            break;

        case CITIDM_ONINTERNET:
            switch (nCmdexecopt)
            {
            case CITE_INTERNET:
                _fInitialPidlIsWeb = TRUE;
                _fShellView = !_fInitialPidlIsWeb;
                break;
            case CITE_SHELL:
                _fInitialPidlIsWeb = FALSE;
                _fShellView = !_fInitialPidlIsWeb;
                break;
            case CITE_QUERY:
                return ResultFromScode(_fShellView ? CITE_SHELL : CITE_INTERNET);
                break;
            }
            return ResultFromScode(_fInitialPidlIsWeb ? CITE_INTERNET : CITE_SHELL);

        case CITIDM_VIEWTOOLS:
            _OnCommand(GET_WM_COMMAND_MPS(FCIDM_VIEWTOOLS, _hwnd, 0));
            break;

        case CITIDM_VIEWAUTOHIDE:
            _OnCommand(GET_WM_COMMAND_MPS(FCIDM_VIEWAUTOHIDE, _hwnd, 0));
            break;

        case CITIDM_VIEWLOCKTOOLBAR:
            _OnCommand(GET_WM_COMMAND_MPS(FCIDM_VIEWLOCKTOOLBAR, _hwnd, 0));
            break;

        case CITIDM_VIEWADDRESS:
            _OnCommand(GET_WM_COMMAND_MPS(FCIDM_VIEWADDRESS, _hwnd, 0));
            break;

        case CITIDM_VIEWLINKS:
            _OnCommand(GET_WM_COMMAND_MPS(FCIDM_VIEWLINKS, _hwnd, 0));
            break;

        case CITIDM_VIEWMENU:
            _OnCommand(GET_WM_COMMAND_MPS(FCIDM_VIEWMENU, _hwnd, 0));
            break;

        case CITIDM_SHOWTOOLS:
            dw = VBF_TOOLS;
            goto ShowABand;

        case CITIDM_SHOWADDRESS:
            dw = VBF_ADDRESS;
            goto ShowABand;

        case CITIDM_SHOWLINKS:
            dw = VBF_LINKS;
            goto ShowABand;

#ifdef UNIX
        case CITIDM_SHOWBRAND:
            dw = VBF_BRAND;
            goto ShowABand;
#endif

        case CITIDM_SHOWMENU:
            dw = VBF_MENU;
ShowABand:
            if (nCmdexecopt)
                dw |= _nVisibleBands;           // Set
            else
                dw = (_nVisibleBands & ~dw);    // Clear

            _ShowVisible(dw, TRUE);
            _fUsingDefaultBands = FALSE;
            break;

        case CITIDM_DISABLESHOWMENU:
            _fNoShowMenu = BOOLIFY(nCmdexecopt);
            break;

        case CITIDM_STATUSCHANGED:
            _fDirty = TRUE;
            if (_ptbsitect)
                _ptbsitect->Exec(&CGID_ShellBrowser, FCIDM_PERSISTTOOLBAR, 0, NULL, NULL);
            break;

        case CITIDM_THEATER:

            if (_fShow)
            {

                // IF YOU SEE HTIS ASSERT, TRY TO REMEMBER WHAT YOU DID AND CALL CHEE
                ASSERT(_fTheater || _nVisibleBands & VBF_MENU);

                switch (nCmdexecopt)
                {

                case THF_ON:
                    _fTheater = TRUE;
                    ResizeBorderDW(NULL, NULL, FALSE);
                    _TheaterModeLayout(TRUE);
                    // theater has its own brand, so needs to know whether we're in shell or web view so it can show the right brand
                    IUnknown_Exec(_ptbsite, &CGID_Theater, THID_ONINTERNET, _fShellView ? CITE_SHELL : CITE_INTERNET, NULL, NULL);

                    // pass back _fAutoHide
                    pvarargOut->vt = VT_I4;
                    pvarargOut->lVal = _fAutoHide;

                    goto notify_bands;


                case THF_OFF:
                    _fTheater = FALSE;
                    ResizeBorderDW(NULL, NULL, FALSE);
                    _TheaterModeLayout(FALSE);

                    // position everything properly (needed after reparenting)
                    SendMessage(_hwnd, RB_PRIV_RESIZE, 0, 0);
                    goto notify_bands;

notify_bands:
                    {
                        int icBands = (int) SendMessage( _bs._hwnd, RB_GETBANDCOUNT, 0, 0 );
                        for (int i = 0; i < icBands; i++)
                        {
                            REBARBANDINFO rbbi;
                            rbbi.cbSize = sizeof(REBARBANDINFO);
                            rbbi.fMask = RBBIM_ID;

                            if (SendMessage(_bs._hwnd, RB_GETBANDINFO, i, (LPARAM) &rbbi))
                            {
                                LPBANDITEMDATA pbid = (LPBANDITEMDATA)_bs._GetBandItemDataStructByID(rbbi.wID);
                                if (pbid)
                                    IUnknown_Exec(pbid->pdb, pguidCmdGroup, CITIDM_THEATER, nCmdexecopt, NULL, NULL);
                            }
                        }
                    }
                    break;

                case THF_UNHIDE:
                    // position everything properly (needed after reparenting)
                    SendMessage(_hwnd, RB_PRIV_RESIZE, 0, 0);
                    break;
                }

                // IF YOU SEE HTIS ASSERT, TRY TO REMEMBER WHAT YOU DID AND CALL CHEE
                ASSERT(_fTheater || _nVisibleBands & VBF_MENU);
            }

            break;
        case CITIDM_VIEWEXTERNALBAND_BYCLASSID:
            if ((pvarargIn->vt == VT_BSTR) && pvarargIn->bstrVal)
            {
                CLSID clsid;
                if (GUIDFromString( pvarargIn->bstrVal, &clsid ))
                {
                    hr = E_FAIL;
                    for (DWORD i = 0; i < MAXEXTERNALBANDS; i++)
                    {
                        if (clsid == _rgebi[i].clsid)
                        {
                            DWORD dw = _nVisibleBands;
                            DWORD dwBit = EXTERNALBAND_VBF_BIT( i );
                            dw = (nCmdexecopt) ? dw | dwBit : dw & ~dwBit;
                            if ( !( dw & ~VBF_BRAND))
                            {
                                _pdie->put_ToolBar( FALSE );
                            }
                            _ShowVisible(dw, TRUE);
                            _fUsingDefaultBands = FALSE;
                            hr = S_OK;
                            break;
                        }
                    }
                }
            }
            break;
        default:
            if (InRange( nCmdID, CITIDM_VIEWEXTERNALBAND_FIRST, CITIDM_VIEWEXTERNALBAND_LAST ))
            {
                _OnCommand(GET_WM_COMMAND_MPS( nCmdID - CITIDM_VIEWEXTERNALBAND_FIRST + FCIDM_EXTERNALBANDS_FIRST, _hwnd, 0));
                break;
            }
            ASSERT(0);
            break;
        }
    }
    else
    {
Lother:
        LPBANDITEMDATA pbid = _bs._GetBandItemDataStructByID(CBIDX_ADDRESS);
        if (pbid)
        {
            hr = IUnknown_Exec(pbid->pdb, pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
        }
    }

    return hr;
}

BOOL _GetSearchHKEY(REFGUID guidSearch, HKEY *phkey)
{
    HKEY hkey;
    BOOL bRet = FALSE;

    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_SZ_STATIC, 0, KEY_READ, &hkey) == ERROR_SUCCESS)
    {
        TCHAR szExt[MAX_PATH];//extension key name
        DWORD cchExt = ARRAYSIZE(szExt);
        int  iExt;
        BOOL bNoUrl = FALSE; // true iff guidSearch is found and there is no Url subkey

        for (iExt=0;
             !bRet && RegEnumKeyEx(hkey, iExt, szExt, &cchExt, NULL, NULL, NULL, NULL) == ERROR_SUCCESS;
             cchExt = ARRAYSIZE(szExt), iExt++)
        {
            HKEY hkeyExt; // static extension key

            if (RegOpenKeyEx(hkey, szExt, 0, KEY_READ, &hkeyExt) == ERROR_SUCCESS)
            {
                int i;
                TCHAR szSubKey[32];
                HKEY  hkeySub;

                for (i = 0; !bRet && (wnsprintf(szSubKey, ARRAYSIZE(szSubKey), TEXT("%d"), i),
                            RegOpenKey(hkeyExt, szSubKey, &hkeySub) == ERROR_SUCCESS);
                     i++)
                {
                    TCHAR szSearchGuid[GUIDSTR_MAX];
                    DWORD cb;
                    DWORD dwType;

                    cb = sizeof(szSearchGuid);
                    if (SHGetValue(hkeySub, REG_SZ_SEARCH_GUID, NULL, &dwType, (BYTE*)szSearchGuid, &cb) == ERROR_SUCCESS)
                    {
                        GUID guid;

                        if (GUIDFromString(szSearchGuid, &guid) &&
                            IsEqualGUID(guid, guidSearch))
                        {
                            HKEY hkeyTmp;

                            if (RegOpenKey(hkeySub, REG_SZ_SEARCH_URL, &hkeyTmp) == ERROR_SUCCESS)
                                RegCloseKey(hkeyTmp);
                            else
                                bNoUrl = TRUE;

                            bRet = TRUE;
                        }
                    }
                    if (!bRet || bNoUrl)
                        RegCloseKey(hkeySub);
                    else
                        *phkey = hkeySub;

                }
                if (!bNoUrl)
                    RegCloseKey(hkeyExt);
                else
                {
                    ASSERT(bRet);
                    *phkey = hkeyExt;
                }
            }
        }
        RegCloseKey(hkey);
    }
    return bRet;
}

HRESULT CInternetToolbar::GetDefaultSearchUrl(LPWSTR pwszUrl, UINT cch)
{
    HRESULT hr = E_FAIL;

    if (GetDefaultInternetSearchUrlW(pwszUrl, cch, TRUE))
        hr = S_OK;
    return hr;
}

void WINAPI CopyEnumElement(void *pDest, const void *pSource, DWORD dwSize)
{
    if (!pDest)
        return;

    memcpy(pDest, pSource, dwSize);
}

class CFolderSearches : public IFolderSearches
{
public:
    // *** IUnknown ***
    STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj);
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);
    // *** IFolderSearches ***
    STDMETHODIMP EnumSearches(IEnumUrlSearch **ppenum);
    STDMETHODIMP DefaultSearch(GUID *pguid);

    CFolderSearches(GUID *pguid, int iCount, URLSEARCH *pUrlSearch);
    ~CFolderSearches();
private:
    LONG _cRef;
    int  _iCount;
    GUID _guidDefault;
    URLSEARCH *_pUrlSearch;
};

CFolderSearches::CFolderSearches(GUID *pguid, int iCount, URLSEARCH *pUrlSearch)
{
    _cRef = 1;
    _iCount = iCount;
    _guidDefault = *pguid;
    _pUrlSearch = pUrlSearch;
}

CFolderSearches::~CFolderSearches()
{
    if (_pUrlSearch)
        LocalFree(_pUrlSearch);
}

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

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

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

ULONG CFolderSearches::Release()
{
    if (--_cRef > 0)
        return _cRef;

    delete this;
    return 0;
}

HRESULT CFolderSearches::EnumSearches(IEnumUrlSearch **ppenum)
{
    HRESULT hres = E_OUTOFMEMORY;

    *ppenum = (IEnumUrlSearch *)new CStandardEnum(IID_IEnumUrlSearch, FALSE,
                _iCount, sizeof(URLSEARCH), _pUrlSearch, CopyEnumElement);
    if (*ppenum)
    {
        _pUrlSearch = NULL;
        _iCount = 0;
        hres = S_OK;
    }
    return hres;
}

HRESULT CFolderSearches::DefaultSearch(GUID *pguid)
{
    *pguid = _guidDefault;
    return S_OK;
}

HRESULT CInternetToolbar::_GetFolderSearches(IFolderSearches **ppfs)
{
    HRESULT hres = E_FAIL;

    *ppfs = NULL;
    if (_hdpaFSI)
    {
        LPURLSEARCH pUrlSearch = NULL;
        int iCount = 0;
        int cFSIs = DPA_GetPtrCount(_hdpaFSI);

        hres = E_OUTOFMEMORY;
        if (cFSIs > 0
            && ((pUrlSearch = (LPURLSEARCH)LocalAlloc(LPTR, sizeof(URLSEARCH)*cFSIs)) != NULL))
        {
            LPFOLDERSEARCHITEM pfsi;
            int i;

            // insert per folder items
            for (i = 0; i < cFSIs && (pfsi = (LPFOLDERSEARCHITEM)DPA_GetPtr(_hdpaFSI, i)) != NULL; i++)
            {
                CLSID  clsid;

                // check if Url is actually a GUID. if yes we cannot enumerate it because
                // we need Title/Url pair.
                if (!GUIDFromStringW(pfsi->wszUrl, &clsid))
                {
                    lstrcpynW(pUrlSearch[iCount].wszName, pfsi->wszName, ARRAYSIZE(pUrlSearch[iCount].wszName));
                    lstrcpynW(pUrlSearch[iCount].wszUrl,  pfsi->wszUrl,  ARRAYSIZE(pUrlSearch[iCount].wszUrl));
                    iCount++;
                }
            }
        }
        *ppfs = new CFolderSearches(&_guidDefaultSearch, iCount, pUrlSearch);
        if (*ppfs)
            hres = S_OK;
        else
            LocalFree(pUrlSearch);
    }
    return hres;
}

BOOL CInternetToolbar::_GetSearchUrl(LPWSTR pwszUrl, DWORD cch)
{
    BOOL        bRet = FALSE;
    HKEY        hkey;

    if (pwszUrl)
    {
        pwszUrl[0] = L'\0';

        // if we are looking for web search url bypass the registry lookup and the
        // per folder items and go straight to GetDefaultSearchUrl which call
        // GetSearchAssistantUrlW
        if (!IsEqualGUID(_guidDefaultSearch, SRCID_SWebSearch))
        {
            // _GetSearchHKEY looks in the registry where shell search items are registered
            // if we have old shell32 then we don't display shell search items so we should
            // not look in the registry
            if (GetUIVersion() >= 5 && _GetSearchHKEY(_guidDefaultSearch, &hkey))
            {
                DWORD cb = cch*sizeof(TCHAR);
                TCHAR szGuid[GUIDSTR_MAX];
                DWORD cbGuid = sizeof(szGuid);

                // is there a url key
                if (SHGetValueW(hkey, REG_SZ_SEARCH_URL, NULL, NULL, pwszUrl, &cb) == ERROR_SUCCESS)
                    bRet = TRUE;
                // no? try the default value, maybe it's the clsid
                else if (SHGetValueW(hkey, NULL, NULL, NULL, szGuid, &cbGuid) == ERROR_SUCCESS)
                {
                    GUID guid;
                    // is it a valid guid string
                    if (GUIDFromString(szGuid, &guid))
                    {
                        StrCpyNW(pwszUrl, szGuid, cch);
                        bRet = TRUE;
                    }
                }

                RegCloseKey(hkey);
            }
            // maybe it's one of the per-folder items...
            else if (_hdpaFSI) //FSI = folder search items
            {
                int i;
                LPFOLDERSEARCHITEM pfsi;

                for (i=0; (pfsi = (LPFOLDERSEARCHITEM)DPA_GetPtr(_hdpaFSI, i)) != NULL; i++)
                {
                    if (IsEqualGUID(_guidDefaultSearch, pfsi->guidSearch))
                    {
                        StrCpyNW(pwszUrl, pfsi->wszUrl, cch);
                        bRet = TRUE;
                        break;
                    }
                }
            }
        }

        if (!bRet)
            bRet = SUCCEEDED(GetDefaultSearchUrl(pwszUrl, cch));
    }

    return bRet;
}

void CInternetToolbar::_SetSearchStuff()
{
    UINT uiState;
    BOOL bChecked = FALSE;

    if (SUCCEEDED(GetState(&CLSID_CommonButtons, TBIDM_SEARCH, &uiState)))
        bChecked = uiState & TBSTATE_CHECKED;

    if (!_hdpaFSI)
    {
        _hdpaFSI = DPA_Create(2);
    }
    else
    {
        DPA_EnumCallback(_hdpaFSI, DeleteDPAPtrCB, NULL); // delete all ptrs
        DPA_DeleteAllPtrs(_hdpaFSI); // now tell hdpa to forget about them
    }

    // this is bogus -- _fShellView is always FALSE when using automation
    if (_fShellView)
        _guidDefaultSearch = SRCID_SFileSearch;
    else
        _guidDefaultSearch = SRCID_SWebSearch;

    // get per folder search items and the default search (if any)
    // and insert them to _himlSrc
    _GetFolderSearchData();

    if (!bChecked)
    {
        _guidCurrentSearch = _guidDefaultSearch;
    }
}

//
// CInternetToolbar::SetCommandTarget()
//
// This function sets the current command target and button group.  A client calls this
// before merging in buttons with the AddButtons method.
//
// There are a couple of tricky things about this function.
//
// NTRAID#NTBUG9-196149-2000/12/11-AIDANL  Hide Address Bar by default in explorer for Per/Pro
//      We no longer allow dwFlags to set links, address, tools, brand, relying on 
//      CInternetToolbar::_LoadDefaultSettings() for those settings.
//
// One is that the client can pass some flags (dwFlags param) specifying the bands it wants
// showing by default (menu, links, address, tools, brand, external).  But we don't let them change
// the state of the menu band.  And, if another client has already set the default bands, we
// don't let them change the state of any of the bands.
//
// The other is that we do some stuff to figure out if the caller is just another instantiation of the same
// client.  If we think this is a new client (new guidButtonGroup), we flush the toolbar and return S_OK.
// But if we think this is the same client reincarnated (same guidButtonGroup and non-NULL command target),
// we return S_FALSE without flushing the toolbar.  This is done for performance.  A new dochost is instantiated
// on each navigation, but its toolbar buttons never change, so don't bother remerging its toolbar buttons.
//
HRESULT CInternetToolbar::SetCommandTarget(IUnknown* punkCmdTarget, const GUID* pguidButtonGroup, DWORD dwFlags)
{
    if (!pguidButtonGroup || !punkCmdTarget || IsEqualGUID(CLSID_CommonButtons, *pguidButtonGroup))
        return E_INVALIDARG;

    // RAID 196149 - disallow changes to links, address, tools, brand, instead use _cs.uiVisible for these settings
    dwFlags = dwFlags & ~(VBF_LINKS| VBF_ADDRESS | VBF_TOOLS | VBF_BRAND);
    dwFlags |= (_cs.uiVisible & (VBF_LINKS| VBF_ADDRESS | VBF_TOOLS | VBF_BRAND));

    // this should not change the menu bit or external bands.
    dwFlags |= (_nVisibleBands & (VBF_MENU | VBF_EXTERNALBANDS));

    _btb._fCustomize = !((dwFlags & VBF_NOCUSTOMIZE) || SHRestricted2(REST_NOTOOLBARCUSTOMIZE, NULL, 0));

    // if the new button group is the internet button group, then we're
    // in internet mode; else we're in shell mode
    _fShellView = !(_IsDocHostGUID(pguidButtonGroup));

    _SetSearchStuff();

    HRESULT hr = S_FALSE;

    BOOL fNewButtonGroup = !IsEqualGUID(*pguidButtonGroup, _btb._guidCurrentButtonGroup);
    BOOL fNewCommandTarget = !SHIsSameObject(_btb._pctCurrentButtonGroup, punkCmdTarget);

    // when changing button groups we need to invalidate our cache of buttons for customization
    // why? well, with browse in separate process not turned on, navigating from shell to web
    // reuses the toolbar and some buttons may be disabled for shell but not for browser and vice versa.
    if (fNewButtonGroup)
        _iButtons = -1;
        
    if (fNewButtonGroup || fNewCommandTarget) 
    {
        if (_btb._pctCurrentButtonGroup)
            _btb._pctCurrentButtonGroup->Exec(&IID_IExplorerToolbar, ETCMDID_NEWCOMMANDTARGET, 0, NULL, NULL);

        _btb._guidCurrentButtonGroup = *pguidButtonGroup;
        ATOMICRELEASE(_btb._pctCurrentButtonGroup);
        punkCmdTarget->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &_btb._pctCurrentButtonGroup));

        // A new view can tell us how many rows of text it NEEDs.
        // if it doesn't specify, we give it the default. (stored in _uiDefaultTBTextRows)
        if (dwFlags & VBF_ONELINETEXT )
            _uiTBTextRows = 1;
        else if (dwFlags & VBF_TWOLINESTEXT)
            _uiTBTextRows = 2;
        else
            _uiTBTextRows = _uiTBDefaultTextRows;

        _CompressBands(_fCompressed, _uiTBTextRows, FALSE);

        if (fNewButtonGroup) 
        {
            // new button group; flush toolbar
            _btb._RemoveAllButtons();
            hr = S_OK;
        }

        if (_fUsingDefaultBands && !_fTheater)
            _fUsingDefaultBands = FALSE;
        else
            dwFlags = _nVisibleBands;

        if (dwFlags)
            _ShowBands(dwFlags);
    }

    return hr;
}

HRESULT CInternetToolbar::AddStdBrowserButtons()
{
    //
    // code to add browser buttons has moved to CDocObjectHost::_AddButtons
    //
    ASSERT(0);
    return E_NOTIMPL;
}

void CInternetToolbar::_ReloadButtons()
{
    if (!IsEqualGUID(_btb._guidCurrentButtonGroup, CLSID_CommonButtons) &&
            _btb._pctCurrentButtonGroup)
    {
        HRESULT hres = _btb._pctCurrentButtonGroup->Exec(&IID_IExplorerToolbar, ETCMDID_RELOADBUTTONS, 0, NULL, NULL);
        if (FAILED(hres))
            AddButtons(&_btb._guidCurrentButtonGroup, _btb._cBtnsAdded, _btb._pbtnsAdded);

#ifdef EDIT_HACK
        _InitEditButtonStyle();
#endif

        _UpdateToolbar(TRUE);
    }
}

// The cmdTarget should have already added the Imagelists and the strings.
HRESULT CInternetToolbar::AddButtons(const GUID* pguidButtonGroup, UINT nNewButtons, const TBBUTTON * lpButtons)
{
    if (!pguidButtonGroup || !IsEqualGUID(*pguidButtonGroup, _btb._guidCurrentButtonGroup))
        return E_INVALIDARG;

    if (!IsWindow(_btb._hwnd))
        return E_FAIL;

    LPTBBUTTON lpTBCopy = (LPTBBUTTON)LocalAlloc(LPTR, nNewButtons * sizeof(TBBUTTON));
    if (!lpTBCopy)
        return E_OUTOFMEMORY;

    _CreateBands();
    _btb._RemoveAllButtons();

    memcpy(lpTBCopy, lpButtons, sizeof(TBBUTTON) * nNewButtons);

    nNewButtons = _btb._ProcessExternalButtons(lpTBCopy, nNewButtons);

    // Free the old button array
    _btb._FreeBtnsAdded();

    _btb._pbtnsAdded = lpTBCopy;
    _btb._cBtnsAdded = nNewButtons;

    if (_btb._fCustomize && _btb._SaveRestoreToolbar(FALSE))
    {
        // Customization mechanism filled the toolbar for us

        // 
        // The customization mechanism allocated its own set of
        // cmdmaps for the buttons, which means that we need to
        // free those hanging off _pbtnsAdded when _pbtnsAdded is
        // freed.
        //
        _btb._fNeedFreeCmdMapsAdded = TRUE;
        
        _btb._RecalcButtonWidths();

#ifdef EDIT_HACK
        //
        // If we had a custom edit glyph, reload it so that we
        // don't momentarily flash the default glyph during
        // navigation.  We'll update it again when we get a
        // DISPID_DOCUMENTCOMPLETE event.
        //
        _RefreshEditGlyph();
#endif //EDIT_HACK

        if (!_fShellView)
        {
            _btb._AddMediaBarButton();
        }
    }
    else
    {
        // No customization found for this button group

        //
        // We're adding the button array to toolbar directly,
        // and the cmdmaps get freed on TBN_DELETINGBUTTON, so
        // we shouldn't also try to free them when _pbtnsAdded
        // is freed.
        //
        _btb._fNeedFreeCmdMapsAdded = FALSE;

        _AddCommonButtons();
        SendMessage(_btb._hwnd, TB_ADDBUTTONS, nNewButtons, (LPARAM)lpTBCopy);
    }

    _bs._SetMinDimensions();
    return S_OK;
}

HRESULT CInternetToolbar::AddString(const GUID * pguidButtonGroup, HINSTANCE hInst, UINT_PTR uiResID, LONG_PTR *pOffset)
{
    TraceMsg(DM_ITBAR, "CITBar::AddString called");

    *pOffset = -1;

    if (!IsWindow(_btb._hwnd))
    {
        TraceMsg(DM_ERROR, "CITBar::AddString failed");
        return E_FAIL;
    }

    *pOffset= SendMessage(_btb._hwnd, TB_ADDSTRING, (WPARAM)hInst, (LPARAM)uiResID);

    if (*pOffset != -1)
        return S_OK;

    TraceMsg(DM_ERROR, "CITBar::AddString failed");
    return E_FAIL;
}

HRESULT CInternetToolbar::GetButton(const GUID* pguidButtonGroup, UINT uiCommand, LPTBBUTTON lpButton)
{
    UINT_PTR uiIndex = 0;
    TraceMsg(DM_ITBAR, "CITBar::GetButton called");

    if (!pguidButtonGroup || !IsWindow(_btb._hwnd))
        return E_FAIL;

    if (SUCCEEDED(_btb._ConvertCmd(pguidButtonGroup, uiCommand, NULL, &uiCommand)))
    {
        uiIndex = SendMessage(_btb._hwnd, TB_COMMANDTOINDEX, uiCommand, 0L);
        if (SendMessage(_btb._hwnd, TB_GETBUTTON, uiIndex, (LPARAM)lpButton))
        {
            GUID guid;
            _btb._ConvertCmd(NULL, lpButton->idCommand, &guid, (UINT*)&lpButton->idCommand);
            return S_OK;
        }
    }
    return E_FAIL;
}

HRESULT CInternetToolbar::GetState(const GUID* pguidButtonGroup, UINT uiCommand, UINT * pfState)
{
    TraceMsg(DM_ITBAR, "CITBar::GetState called");

    if (!pguidButtonGroup || !IsWindow(_btb._hwnd))
        return E_FAIL;

    if (SUCCEEDED(_btb._ConvertCmd(pguidButtonGroup, uiCommand, NULL, &uiCommand)))
    {
        *pfState = (UINT)SendMessage(_btb._hwnd, TB_GETSTATE, uiCommand, 0L);
        return S_OK;
    }

    return E_FAIL;
}

HRESULT CInternetToolbar::SetState(const GUID* pguidButtonGroup, UINT uiCommand, UINT fState)
{
    BOOL bIsSearchBtn;

    if (!pguidButtonGroup || !IsWindow(_btb._hwnd))
        return E_FAIL;

    TraceMsg(DM_ITBAR, "CITBar::SetState called");

    bIsSearchBtn = uiCommand == TBIDM_SEARCH;
    if (SUCCEEDED(_btb._ConvertCmd(pguidButtonGroup, uiCommand, NULL, &uiCommand)))
    {
        UINT_PTR uiState;

        uiState = SendMessage(_btb._hwnd, TB_GETSTATE, uiCommand, NULL);
        uiState ^= fState;
        if (uiState)
        {
            // search button is being unchecked, change the icon to the default search's
            if (bIsSearchBtn && !(fState & TBSTATE_CHECKED) && !IsEqualGUID(_guidCurrentSearch, _guidDefaultSearch))
            {
                _guidCurrentSearch = _guidDefaultSearch;
            }
            if (SendMessage(_btb._hwnd, TB_SETSTATE, uiCommand, (LPARAM)fState))
            {
                if (uiState & TBSTATE_HIDDEN)
                    _bs._SetMinDimensions();
            }
        }
        return S_OK;
    }
    return E_FAIL;
}

//
//  A bitmap can be added in two ways:
//  1.  Send a bitmap in the hBMPNew field. The uiBMPType parameter needs to be a BITMAP_BMP*
//      The uiCount and the ptb parameters are ignored
//      The offset is placed in puiOffset
//
//  2.  A TBADDBITMAP struct can be sent. The uiCount should have the count
//      uiBMPType parameter needs to be a BITMAP_TBA* value
//      The offset is placed in puiOffset
HRESULT CInternetToolbar::AddBitmap(const GUID * pguidButtonGroup, UINT uiBMPType, UINT uiCount, TBADDBITMAP * ptb, LRESULT * pOffset, COLORREF rgbMask)
{
    UINT uiGetMSG, uiSetMSG;
    TBBMP_LIST tbl = {NULL};

    TraceMsg(DM_ITBAR, "CITBar::AddBitmap called");
    *pOffset = -1;

    _CreateBands();
    if ((!pguidButtonGroup) || (!IsWindow(_btb._hwnd)) || !_hdsaTBBMPs)
    {
        TraceMsg(DM_ERROR, "CITBar::AddBitmap failed - NULL pguidButtonGroup or invalid _hwnd");
        return E_FAIL;
    }

    // See if we already have the bitmap loaded.
    TBBMP_LIST * pTBBs = NULL;
    int nCount = DSA_GetItemCount(_hdsaTBBMPs);
    for (int nIndex = 0; nIndex < nCount; nIndex++)
    {
        pTBBs = (TBBMP_LIST*)DSA_GetItemPtr(_hdsaTBBMPs, nIndex);
        if ((pTBBs) && (pTBBs->hInst == ptb->hInst) && (pTBBs->uiResID == ptb->nID))
            break;
        pTBBs = NULL;
    }

    // If it was in the commctrl, then we should already have an entry in the DSA
    if ((ptb->hInst == HINST_COMMCTRL) && (!pTBBs))
    {
        TraceMsg(DM_ERROR, "CITBar::AddBitmap failed - bogus ResID for HINST_COMMCTL");
        return E_FAIL;
    }

    // If the icons being added are from fontsext.dll or from dialup networking
    // or the briefcase, then we have it. So just send return the offset
    if (ptb->hInst != HINST_COMMCTRL)
    {
        TCHAR szDLLFileName[MAX_PATH], *pszFN;
        memset(szDLLFileName, 0, ARRAYSIZE(szDLLFileName));
        if (GetModuleFileName(ptb->hInst, szDLLFileName, ARRAYSIZE(szDLLFileName)))
        {
            pszFN = PathFindFileName(szDLLFileName);
            if(!lstrcmpi(pszFN, TEXT("fontext.dll")))
                *pOffset = FONTGLYPH_OFFSET;
            else if (!lstrcmpi(pszFN, TEXT("shell32.dll")))
            {
                // 140 and 141 are the glyphs that Shell32.dll uses:
                // IDB_BRF_TB_SMALL        140
                // IDB_BRF_TB_LARGE        141
                if ((ptb->nID == 140) || (ptb->nID == 141))
                    *pOffset = BRIEFCASEGLYPH_OFFSET;
            }
            else if (!lstrcmpi(pszFN, TEXT("rnaui.dll")))
                *pOffset = RNAUIGLYPH_OFFSET;
            else if (!lstrcmpi(pszFN, TEXT("webcheck.dll")))
                *pOffset = WEBCHECKGLYPH_OFFSET;
            if (*pOffset != -1)
                return S_OK;
        }
    }

    // So the bitmaps is not from commctrl. And we have never seen this before.
    // Add an entry into the DSA and then add the bitmap to the himage list.
    if (!pTBBs)
    {
        tbl.hInst = ptb->hInst;
        tbl.uiResID = ptb->nID;
        nIndex = DSA_AppendItem(_hdsaTBBMPs, &tbl);
        if (nIndex  < 0)
        {
            TraceMsg(DM_ERROR, "CITBar::AddBitmap failed - nIndex < 0!");
            return E_FAIL;
        }
        pTBBs = (TBBMP_LIST*)DSA_GetItemPtr(_hdsaTBBMPs, nIndex);
        if (!pTBBs)
        {
            TraceMsg(DM_ERROR, "CITBar::AddBitmap failed - pTBBS is NULL!");
            return E_FAIL;
        }
    }


    switch(uiBMPType)
    {
    case BITMAP_NORMAL:
        if ((pTBBs) && (pTBBs->fNormal))
        {
            *pOffset = pTBBs->uiOffset;
            return S_OK;
        }
        else if (pTBBs)
            pTBBs->fNormal = TRUE;

        uiGetMSG = TB_GETIMAGELIST; uiSetMSG = TB_SETIMAGELIST;
        break;

    case BITMAP_HOT:
        if ((pTBBs) && (pTBBs->fHot))
        {
            *pOffset = pTBBs->uiOffset;
            return S_OK;
        }
        else if (pTBBs)
            pTBBs->fHot = TRUE;

        uiGetMSG = TB_GETHOTIMAGELIST; uiSetMSG = TB_SETHOTIMAGELIST;
        break;

    case BITMAP_DISABLED:
        if ((pTBBs) && (pTBBs->fDisabled))
        {
            *pOffset = pTBBs->uiOffset;
            return S_OK;
        }
        else if (pTBBs)
            pTBBs->fDisabled = TRUE;

        uiGetMSG = TB_GETDISABLEDIMAGELIST; uiSetMSG = TB_SETDISABLEDIMAGELIST;
        break;

    default:
        ASSERT(FALSE);
        return E_FAIL;
    }
    pTBBs->uiCount = uiCount;
    
    *pOffset = _AddBitmapFromForeignModule(uiGetMSG, uiSetMSG, uiCount, ptb->hInst, ptb->nID, rgbMask);
    if (pTBBs)
        pTBBs->uiOffset = (UINT)*pOffset;

    return S_OK;
}

// the CmdTarget needs to call this to see what size of bmps we are using.
HRESULT CInternetToolbar::GetBitmapSize(UINT * uiSize)
{
    TraceMsg(DM_ITBAR, "CITBar::GetBitmapSize called");

    *uiSize = g_fSmallIcons ? MAKELONG(TB_SMBMP_CX, TB_SMBMP_CY) : MAKELONG(g_iToolBarLargeIconWidth,g_iToolBarLargeIconHeight);
    return S_OK;
}

HRESULT CInternetToolbar::SetImageList( const GUID* pguidCmdGroup, HIMAGELIST himlNormal, HIMAGELIST himlHot, HIMAGELIST himlDisabled)
{
    if (IsEqualGUID(*pguidCmdGroup, _btb._guidCurrentButtonGroup))
    {
        SendMessage(_btb._hwnd, TB_SETIMAGELIST, 1, (LPARAM)himlNormal);
        SendMessage(_btb._hwnd, TB_SETHOTIMAGELIST, 1, (LPARAM)himlHot);
        SendMessage(_btb._hwnd, TB_SETDISABLEDIMAGELIST, 1, (LPARAM)himlDisabled);
    }
    return S_OK;
}

HRESULT CInternetToolbar::ModifyButton( const GUID * pguidButtonGroup, UINT uiCommand, LPTBBUTTON lpButton)
{
    UINT uiIndex = 0;
    TraceMsg(DM_ITBAR, "CITBar::ModifyButton called");

    if (!pguidButtonGroup || !IsWindow(_btb._hwnd))
        return E_FAIL;

    if (SUCCEEDED(_btb._ConvertCmd(pguidButtonGroup, uiCommand, NULL, &uiCommand)))
    {
        TBBUTTONINFO tbbi;
        tbbi.cbSize = sizeof(tbbi);
        tbbi.dwMask = TBIF_STATE | TBIF_IMAGE;
        tbbi.fsState = lpButton->fsState;
        tbbi.iImage = lpButton->iBitmap;

        if (SendMessage(_btb._hwnd, TB_SETBUTTONINFO, uiCommand, (LPARAM)&tbbi))
        {
            return S_OK;
        }
    }
    return E_FAIL;
}

HRESULT CInternetToolbar::SendToolbarMsg(const GUID* pguidButtonGroup, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT * plRes)
{
    LRESULT lRes;
    if (!IsWindow(_btb._hwnd))
    {
        TraceMsg(DM_ERROR, "CITBar::SendToolbarMsg Message failed");
        return E_FAIL;
    }

    if (
        // this api is only here for back compat, and these messages didn't
        // exist when the old clients were written

        uMsg == TB_GETBUTTONINFOA ||
        uMsg == TB_GETBUTTONINFOW ||
        uMsg == TB_SETBUTTONINFOA ||
        uMsg == TB_SETBUTTONINFOW ||

        // unsupported right now
        uMsg == TB_ADDBUTTONSA || uMsg == TB_ADDBUTTONSW
       )
    {

        ASSERT(0);
        return E_FAIL;
    }



    if ((uMsg == TB_ENABLEBUTTON) || (uMsg == TB_HIDEBUTTON) || (uMsg == TB_CHECKBUTTON) ||
        (uMsg == TB_PRESSBUTTON) || (uMsg == TB_MARKBUTTON))
    {
        unsigned int uiTemp;
        if (SUCCEEDED(_btb._ConvertCmd(pguidButtonGroup, (UINT)wParam, NULL, &uiTemp)))
            wParam = uiTemp;
    }

    if (uMsg == TB_INSERTBUTTON && lParam)
    {
        TBBUTTON btn = (*(TBBUTTON*)lParam);
        _btb._PreProcessExternalTBButton(&btn);
        lRes = SendMessage(_btb._hwnd, uMsg, wParam, (LPARAM)&btn);
    }
    else
    {
        lRes = SendMessage(_btb._hwnd, uMsg, wParam, lParam);

        if (uMsg == TB_GETBUTTON)
        {
            TBBUTTON* pbtn = (TBBUTTON*)lParam;
            if (pbtn && pbtn->dwData)
            {
                CMDMAP* pcm = (CMDMAP*)pbtn->dwData;
                pbtn->idCommand = pcm->nCmdID;
                pbtn->dwData = pcm->lParam;
            }
        }
    }

    if (plRes)
        *plRes = lRes;
    return S_OK;
}

TOOLSBANDCLASS::CBrowserToolsBand() : CToolbarBand()
{
    _fCanFocus = TRUE;
}

#define DEFAULT_LIST_VALUE()    (GetUIVersion() >= 5)

void TOOLSBANDCLASS::_FreeBtnsAdded()
{
    if (_pbtnsAdded)
    {
        if (_fNeedFreeCmdMapsAdded)
        {
            for (int i = 0; i < _cBtnsAdded; i++)
            {
                CMDMAP* pcm = (CMDMAP*)_pbtnsAdded[i].dwData;
                _FreeCmdMap(pcm);
            }
        }

        LocalFree(_pbtnsAdded);

        _pbtnsAdded = NULL;
        _cBtnsAdded = 0;
    }
}

LRESULT TOOLSBANDCLASS::_ToolsCustNotify (LPNMHDR pnmh)
{
    LPTBNOTIFY ptbn = (LPTBNOTIFY) pnmh;

    switch (pnmh->code)
    {

    case TBN_SAVE:
    {
        NMTBSAVE *pnmtbs = (NMTBSAVE*)pnmh;
        if (pnmtbs->iItem == -1)
        {
            // before the save
            int nButtons = (int) SendMessage(_hwnd, TB_BUTTONCOUNT, 0, 0L);
            int uSize = pnmtbs->cbData +
                        sizeof(BUTTONSAVEINFO) * nButtons +  // stuff for each button
                        sizeof(TOOLBARSAVEINFO); // stuff for the toolbar
            pnmtbs->pData = (LPDWORD)LocalAlloc(LPTR, uSize);
            pnmtbs->pCurrent = pnmtbs->pData;
            pnmtbs->cbData = uSize;

            if (pnmtbs->pData)
            {
                TOOLBARSAVEINFO *ptbsi = (TOOLBARSAVEINFO*)pnmtbs->pData;
                ptbsi->cVersion = TBSI_VERSION;
                pnmtbs->pCurrent = (LPDWORD)(ptbsi+1);
            }
        }
        else
        {
            CMDMAP *pcm = (CMDMAP*)pnmtbs->tbButton.dwData;
            BUTTONSAVEINFO* pbsi = (BUTTONSAVEINFO*)pnmtbs->pCurrent;
            pnmtbs->pCurrent = (LPDWORD)(pbsi+1);
            if (pcm)
            {
                pbsi->guid = pcm->guidButtonGroup;
                pbsi->nCmdID = pcm->nCmdID;
                pbsi->fButtonState = pnmtbs->tbButton.fsState;
#ifdef DEBUG
                TCHAR szGuid[80];
                SHStringFromGUID(pcm->guidButtonGroup, szGuid, ARRAYSIZE(szGuid));
                TraceMsg(TF_TBCUST, "Saving: %s - %d (%x)", szGuid, pbsi->nCmdID, pbsi->nCmdID);
#endif
            }
            else
            {
                ASSERT(pnmtbs->tbButton.fsStyle & BTNS_SEP);
                if (pnmtbs->tbButton.idCommand)
                {
                    TraceMsg(TF_TBCUST, "Saving: a separator w/ id %d (%x)", pnmtbs->tbButton.idCommand, pnmtbs->tbButton.idCommand);
                    pbsi->guid = CLSID_Separator;
                    pbsi->nCmdID = pnmtbs->tbButton.idCommand;
                }
                else
                {
                    TraceMsg(TF_TBCUST, "Saving: a separator");
                }
            }
        }
        break;
    }

    case TBN_RESTORE:
        {
            NMTBRESTORE* pnmtbr = (NMTBRESTORE*)pnmh;
            if (pnmtbr->iItem == -1)
            {
                // before the restore.
                // take the data, verify the version,
                // fill in the button count, bytes per record
                // initialize the pCurrent to the end of the tb header
                //
                TOOLBARSAVEINFO* ptbsi = (TOOLBARSAVEINFO*)pnmtbr->pCurrent;
                if (ptbsi->cVersion != TBSI_VERSION)
                {
                    TraceMsg( TF_WARNING, "TOOLSBANDCLASS::_ToolsCustNotify() - Wrong Toolbar Save Info Version (0x%x vs. 0x%x)!", ptbsi->cVersion, TBSI_VERSION );
                    return 1; // abort
                }


                // we're actually going to do a restore.  initialize our database:
                _BuildButtonDSA();

                pnmtbr->pCurrent = (LPDWORD)(ptbsi+1);
                pnmtbr->cbBytesPerRecord += sizeof(BUTTONSAVEINFO);
                pnmtbr->cButtons = (pnmtbr->cbData - sizeof(TOOLBARSAVEINFO)) / pnmtbr->cbBytesPerRecord;
                // make sure we did the math right and there are no remainders
                ASSERT(((pnmtbr->cbData - sizeof(TOOLBARSAVEINFO)) % pnmtbr->cbBytesPerRecord) == 0);

                //this is going to clobber all of the buttons in the current toolbar.
                // since toolbar control just writes over the dwords, we need to go free them now.
                int nButtons = (int) SendMessage(_hwnd, TB_BUTTONCOUNT, 0, 0L);

                for (int nTemp = 0; nTemp < nButtons; nTemp++)
                {
                    CMDMAP *pcm = _GetCmdMapByIndex(nTemp);
                    _FreeCmdMap(pcm);
                    TBBUTTONINFO tbbi;
                    tbbi.cbSize = sizeof(tbbi);
                    tbbi.lParam = (LPARAM)NULL;
                    tbbi.dwMask = TBIF_LPARAM | TBIF_BYINDEX;
                    SendMessage(_hwnd, TB_SETBUTTONINFO, nTemp, (LPARAM)&tbbi);
                }
            }
            else
            {
                BUTTONSAVEINFO* pbsi = (BUTTONSAVEINFO*)pnmtbr->pCurrent;
                pnmtbr->pCurrent = (LPDWORD)(pbsi+1);
                pnmtbr->tbButton.dwData = 0;
                pnmtbr->tbButton.iString = -1;

                if (IsEqualGUID(CLSID_Separator, pbsi->guid))
                {
                    // restore a separator with a command id
                    pnmtbr->tbButton.fsStyle = BTNS_SEP;
                    TraceMsg(TF_TBCUST, "Restoring: a separator w/ id %d (%x)", pnmtbr->tbButton.idCommand, pnmtbr->tbButton.idCommand);
                }
                else if (!(pnmtbr->tbButton.fsStyle & BTNS_SEP))
                {
                    // Make sure that the button exists for this site
                    CMDMAPCUSTOMIZE* pcmc = _GetCmdMapCustomize(&pbsi->guid, pbsi->nCmdID);
                    if ((pcmc == NULL) || (SHRestricted(REST_NONLEGACYSHELLMODE) && ((pbsi->nCmdID == TBIDM_BACK) || (pbsi->nCmdID == TBIDM_FORWARD))))
                    {
                        // Ignore this button
                        return 1;
                    }

                    CMDMAP* pcm = (CMDMAP*)LocalAlloc(LPTR, sizeof(CMDMAP));
                    if (pcm)
                    {
                        pcm->guidButtonGroup = pbsi->guid;
                        pcm->nCmdID = pbsi->nCmdID;

#ifdef DEBUG
                        TCHAR szGuid[80];
                        SHStringFromGUID(pcm->guidButtonGroup, szGuid, ARRAYSIZE(szGuid));
                        TraceMsg(TF_TBCUST, "Restoring: %s - %d (%x)", szGuid, pbsi->nCmdID, pbsi->nCmdID);
#endif

                        // fill in the rest of the info
                        pnmtbr->tbButton = pcmc->btn;
                        pnmtbr->tbButton.fsState = pbsi->fButtonState;
                        pnmtbr->tbButton.dwData = (DWORD_PTR) pcm;

                    }
                }
                else
                {
                    TraceMsg(TF_TBCUST, "Restoring: a separator");
                }
            }
        }
        break;

    case TBN_ENDADJUST:
        _OnEndCustomize();
        break;

    case TBN_TOOLBARCHANGE:
        _pcinfo->fDirty = TRUE;
        break;

    case TBN_INITCUSTOMIZE:
        _OnBeginCustomize((NMTBCUSTOMIZEDLG*)pnmh);
        return TBNRF_HIDEHELP;

    case TBN_RESET:
        _pcinfo->fDirty = FALSE;
        if (_pctCurrentButtonGroup)
        {
            NMTBCUSTOMIZEDLG *pnm = (NMTBCUSTOMIZEDLG*)pnmh;
            CInternetToolbar* pitbar = IToClass(CInternetToolbar, _btb, this);
            TCHAR szGuid[GUIDSTR_MAX];
            SHStringFromGUID(_guidCurrentButtonGroup, szGuid, ARRAYSIZE(szGuid));
            SHDeleteValue(HKEY_CURRENT_USER, REGSTR_PATH_TOOLBAR, szGuid);

            // Default text labels setting should be as follows:
            //
            //  If fullscreen mode, any platform -- "No text labels"
            //  Else if NT5 -- "Selective text on right"
            //  Else  -- "Show text labels"
            //
            int idsDefault;

            if (pitbar->_fTheater)
                idsDefault = IDS_NOTEXTLABELS;
            else if (DEFAULT_LIST_VALUE())
                idsDefault = IDS_PARTIALTEXT;
            else
                idsDefault = IDS_TEXTLABELS;

            _UpdateTextSettings(idsDefault);

            HWND hwnd = (HWND) GetProp(pnm->hDlg, SZ_PROP_CUSTDLG);
            if (hwnd)
            {
                // update our dialog's control selection states
                _SetDialogSelections(hwnd, _DefaultToSmallIcons());
            }

            _RemoveAllButtons();
            _OnEndCustomize();
            if (_pbtnsAdded)
            {
                pitbar->AddButtons(&_guidCurrentButtonGroup, _cBtnsAdded, _pbtnsAdded);

#ifdef EDIT_HACK
                // Restore the edit button
                pitbar->_InitEditButtonStyle();
#endif

                pitbar->_UpdateToolbar(TRUE);
            }
            else
            {
                return TBNRF_ENDCUSTOMIZE;
            }
        }
        break;

    case TBN_QUERYINSERT:
        return TRUE;

    case TBN_QUERYDELETE:
        return (SendMessage(_hwnd, TB_ISBUTTONHIDDEN,
                            (WPARAM) ptbn->tbButton.idCommand,
                            (LPARAM) 0)) ? FALSE : TRUE;

    case TBN_GETBUTTONINFO:
        if (ptbn->iItem < DSA_GetItemCount(_pcinfo->hdsa))
        {
            CMDMAPCUSTOMIZE *pcmc;
            pcmc = (CMDMAPCUSTOMIZE*)DSA_GetItemPtr(_pcinfo->hdsa, ptbn->iItem);
            ptbn->tbButton = pcmc->btn;
            ptbn->tbButton.fsState &= ~TBSTATE_HIDDEN;
            return TRUE;
        }
        return FALSE;

    case TBN_BEGINADJUST:
        if (!_pcinfo || !_pcinfo->fAdjust)
            return 1;
        break;

    }
    return FALSE;
}

BOOL TOOLSBANDCLASS::_SaveRestoreToolbar(BOOL fSave)
{
    TBSAVEPARAMS tbsp;
    TCHAR szGuid[GUIDSTR_MAX];
    SHStringFromGUID(_guidCurrentButtonGroup, szGuid, ARRAYSIZE(szGuid));

    tbsp.hkr = HKEY_CURRENT_USER;
    tbsp.pszSubKey = REGSTR_PATH_TOOLBAR;
    tbsp.pszValueName = szGuid;
    BOOL fRet = BOOLFROMPTR(SendMessage(_hwnd, TB_SAVERESTORE, (WPARAM) fSave, (LPARAM) &tbsp));

    _FreeCustomizeInfo();
    return fRet;
}

void TOOLSBANDCLASS::_AddMediaBarButton()
{
    // Wrap this with a reg key to make sure it happens only once
    // then check to make sure it's not already in the thing

    if (_hwnd && !SHRestricted2W(REST_No_LaunchMediaBar, NULL, 0) && !SHRestricted2(REST_BTN_MEDIABAR, NULL, 0) && CMediaBarUtil::IsWMP7OrGreaterCapable())
    {
        DWORD dwType, dwSize;
        DWORD dwValue = TBBIF_NONE;
        dwSize = sizeof(dwValue);
        if (   (ERROR_SUCCESS==SHGetValue(HKEY_CURRENT_USER, TBBIF_REG_PATH, TBBIF_REG_KEY, &dwType, &dwValue, &dwSize))
            && (dwType==REG_DWORD)
            && ((dwValue & TBBIF_MEDIA) != 0))
        {
            return;
        }
        dwValue |= TBBIF_MEDIA;
        SHSetValue(HKEY_CURRENT_USER, TBBIF_REG_PATH, TBBIF_REG_KEY, REG_DWORD, &dwValue, sizeof(dwValue));

        TBBUTTONINFO tbbi;
        tbbi.cbSize = sizeof(tbbi);
        tbbi.dwMask = TBIF_STATE | TBIF_BYINDEX | TBIF_LPARAM;

        int iFavs = -1, iHist = -1, iLastTool = -1;
        int iMedia = -1;
        
        // lookup MediaBar in current toolbar
        // this could be a previous PersonalBar since the MediaBar reuses the cmdID
        BOOL fFound = FALSE;
        int cntButtons = (int)SendMessage(_hwnd, TB_BUTTONCOUNT, 0, 0);
        for (int j = 0; j < cntButtons; j++)
        {
            if (SendMessage(_hwnd, TB_GETBUTTONINFO, j, (LPARAM)&tbbi)!=-1)
            {
                CMDMAP* pcm = (CMDMAP*)tbbi.lParam;
                if (pcm)
                {
                    // already present ?
                    if (pcm->nCmdID == TBIDM_MEDIABAR)
                    {
                        fFound = TRUE;  // no need to add, only care about updating position in toolbar
                        iMedia = j;
                    }
                    // will try placing after Favorites....
                    else if (pcm->nCmdID == TBIDM_FAVORITES)
                    {
                        iFavs = j;
                        iLastTool = j;
                    }
                    // ... and/or before History
                    else if (pcm->nCmdID == TBIDM_HISTORY)
                    {
                        iHist = j;
                        iLastTool = j;
                    }
                    // ... but at least after last found button in second group
                    else if (   (pcm->nCmdID == TBIDM_SEARCH)
                             || (pcm->nCmdID == TBIDM_ALLFOLDERS))
                    {
                        iLastTool = j;
                    }
                }                
            }
        }

        // force-expose MediaBar button at least once
        if (!fFound)
        {
            TBBUTTON tbXBar;
            memcpy((VOID*)&tbXBar, (VOID*)&c_tbExplorer[TBXID_MEDIABAR], sizeof(TBBUTTON));

            CMDMAP* pcm = (CMDMAP*)LocalAlloc(LPTR, sizeof(CMDMAP));
            if (pcm) 
            {
                pcm->guidButtonGroup = CLSID_CommonButtons;
                pcm->nCmdID = c_tbExplorer[TBXID_MEDIABAR].idCommand;

                tbXBar.idCommand = _nNextCommandID++;
                tbXBar.dwData = (LPARAM)pcm;
                SendMessage(_hwnd, TB_ADDBUTTONS, 1, (LPARAM)&tbXBar);
            }
        }

        if (iMedia < 0)
        {
            cntButtons = (int)SendMessage(_hwnd, TB_BUTTONCOUNT, 0, 0);
            for (j = cntButtons - 1; j >= 0; j--)
            {
                if (SendMessage(_hwnd, TB_GETBUTTONINFO, j, (LPARAM)&tbbi)!=-1)
                {
                    CMDMAP* pcm = (CMDMAP*)tbbi.lParam;
                    if (pcm)
                    {
                        // already present ?
                        if (pcm->nCmdID == TBIDM_MEDIABAR)
                        {
                            iMedia = j;
                            break;
                        }
                    }
                }
            }
        }

        // update position of MediaBar button
        // do we know a preferred position where the MediaBar button should move to?
        if ((iMedia >= 0) && ((iFavs >= 0) || (iHist >= 0) || (iLastTool >= 0)))
        {
            int iNewPos = -1;
            if (iFavs >= 0)
            {
                iNewPos = iFavs;
            }
            else if (iHist >= 0)
            {
                iNewPos = max(iHist - 1, 0);
            }
            else if (iLastTool >= 0)
            {
                iNewPos = iLastTool;
            }

            if (iNewPos >= 0)
            {
                if (iNewPos < iMedia)
                    SendMessage(_hwnd, TB_MOVEBUTTON, iMedia, iNewPos + 1);
                else
                    SendMessage(_hwnd, TB_MOVEBUTTON, iMedia, iNewPos);

                _SaveRestoreToolbar(TRUE);
                _RecalcButtonWidths();
            }
        }

    }
}

int TOOLSBANDCLASS::_CommandFromIndex(UINT uIndex)
{
    TBBUTTONINFO tbbi;
    tbbi.cbSize = sizeof(tbbi);
    tbbi.dwMask = TBIF_COMMAND | TBIF_BYINDEX;
    SendMessage(_hwnd, TB_GETBUTTONINFO, uIndex, (LPARAM)&tbbi);
    return tbbi.idCommand;
}

//  _btb._ConvertCmd()
//  This is used to covert a external Command ID to an internal ID or vice versa
//  If we are converting to an external ID then
//      call with pguidButtonGroup == NULL                  (to external:  pguidButtonGroup == NULL)
//      otherwise call with the external button group GUID  (to internal:  pguidOut == NULL)
HRESULT TOOLSBANDCLASS::_ConvertCmd(const GUID* pguidButtonGroup, UINT id, GUID* pguidOut, UINT * pid)
{
    HRESULT hres = E_FAIL;
    BOOL fToInternal = (bool) (pguidButtonGroup);

    ASSERT((pguidButtonGroup == NULL) ^ (pguidOut == NULL));

    // First look for the command
    if (fToInternal)
    {
        if (_hwnd)
        {
            int nCount = (int) SendMessage(_hwnd, TB_BUTTONCOUNT, 0, 0);
            for (int i = 0; i < nCount; i++)
            {
                CMDMAP *pcm = _GetCmdMapByIndex(i);

                if (pcm)
                {
                    // loop through the command mapping structures until we
                    // find this guid and id
                    if (IsEqualGUID(pcm->guidButtonGroup, *pguidButtonGroup) &&
                        id == pcm->nCmdID)
                    {
                        *pid = _CommandFromIndex(i);
                        hres = S_OK;
                        break;
                    }
                }
            }
        }
    }
    else
    {

        // going from toolbar id to commandtarget info
        CMDMAP *pcm = _GetCmdMapByID(id);
        if (pcm)
        {
            *pguidOut = pcm->guidButtonGroup;
            *pid = pcm->nCmdID;
            hres = S_OK;
        }
    }
    return hres;
}



LRESULT CInternetToolbar::_AddBitmapFromForeignModule(UINT uiGetMSG, UINT uiSetMSG, UINT uiCount, HINSTANCE hinst, UINT_PTR nID, COLORREF rgbMask)
{
    HBITMAP hBMPRaw = NULL, hBMPFixedUp = NULL;
    HBITMAP * phBmp = &hBMPFixedUp;
    BITMAP bmp;
    HIMAGELIST himlTemp;
    LRESULT lRes = 1L;
    BOOL fOk = TRUE;
    HDC dc = NULL, dcMemSrc = NULL, dcMemDest = NULL;
    HBITMAP hbmpOldDest = NULL, hbmpOldSrc = NULL;
    int cxOrg = 0;
    int xDest = 0, yDest = 0;
    RECT rect = {0,0,0,0};
    HBRUSH hbr = NULL;

    // What if hinst == NULL?  That means that nID is really an HBITMAP
    ASSERT( hinst != NULL );

    if (!(hBMPRaw = LoadBitmap(hinst, MAKEINTRESOURCE(nID))))
        return 0L;

    fOk = (BOOL)(GetObject(hBMPRaw, sizeof(BITMAP), &bmp) != 0);

    // Check is the size is OK
    if (fOk && (bmp.bmWidth != (LONG)(g_iToolBarLargeIconWidth * uiCount)) || (bmp.bmHeight != (LONG)g_iToolBarLargeIconHeight) )
    {
        int cxBmp;
        int cyBmp;

        if (g_fSmallIcons)
        {
            cxBmp = TB_SMBMP_CX;
            cyBmp = TB_SMBMP_CY;
        }
        else
        {
            cxBmp = g_iToolBarLargeIconWidth;
            cyBmp = g_iToolBarLargeIconHeight;
        }

        // If the height is 15, the we assume that this is one of the old bitmaps therefore
        // the width is 16. We cannot rely on the (bmp.bmWidth / uiCount) because some apps
        // like SecureFile give us a bitmap 192 wide and say that there are 10 glyphs in it.
        if (bmp.bmHeight == 15)
            cxOrg = 16;
        else
            cxOrg = bmp.bmWidth / (uiCount ? uiCount : 1);

        if (rgbMask)
            fOk = (BOOL)((hbr = CreateSolidBrush(rgbMask))!= NULL);

        if (fOk)
            fOk = (BOOL)((dc = GetDC(_btb._hwnd)) != NULL);

        if (fOk)
            fOk = (BOOL)((hBMPFixedUp = CreateCompatibleBitmap(dc, (cxBmp * uiCount), cyBmp)) != NULL);

        if (fOk)
            fOk = (BOOL)((dcMemSrc = CreateCompatibleDC(dc)) != NULL);

        if (fOk)
            fOk = (BOOL)((dcMemDest = CreateCompatibleDC(dc)) != NULL);

        if (!fOk)
            goto Error;

        hbmpOldSrc = (HBITMAP)SelectObject(dcMemSrc, hBMPRaw);
        hbmpOldDest = (HBITMAP)SelectObject(dcMemDest, hBMPFixedUp);

        rect.right = (cxBmp * uiCount);
        rect.bottom = cyBmp;
        if (rgbMask)
            FillRect(dcMemDest, &rect, hbr);

        for (UINT n = 0; n < uiCount; n++)
        {

            int cxCopy;
            int cyCopy;

            xDest = (n * cxBmp);
            if (cxOrg < cxBmp)
            {
                // if the bitmap is too small, we need to center it.
                // the amount we copy is the full bitmap
                cxCopy = cxOrg;
                xDest += ((cxBmp - cxOrg) / 2);
            }
            else
            {
                // if the bitmap is big enough, we align it to top left and
                // we strecth(shrink) it down to fit
                cxCopy = cxBmp;
            }

            if (bmp.bmHeight < cyBmp)
            {
                cyCopy = bmp.bmHeight;
                yDest = ((cyBmp - bmp.bmHeight) / 2);
            }
            else
            {
                cyCopy = cyBmp;
                yDest = 0;
            }
            StretchBlt(dcMemDest, xDest, yDest, cxOrg, bmp.bmHeight,
                   dcMemSrc, (cxOrg * n), 0, cxCopy, cyCopy, SRCCOPY);

        }

        SelectObject(dcMemDest, hbmpOldDest);
        SelectObject(dcMemSrc, hbmpOldSrc);
    }
    else
        phBmp = &hBMPRaw;

    if (!(himlTemp = (HIMAGELIST)SendMessage(_btb._hwnd, uiGetMSG, 0, 0L)))
    {
        TraceMsg(DM_ERROR, "CITBar::_AddBitmapFromForeignModule Failed - uiGetMSG SendMessage Failure");
        fOk = FALSE;
        goto Error;
    }

    if (rgbMask)
        lRes = ImageList_AddMasked(himlTemp, (HBITMAP)*phBmp, rgbMask);
    else
        lRes = ImageList_Add(himlTemp, (HBITMAP)*phBmp, NULL);

    if (lRes == -1)
    {
        TraceMsg(DM_ERROR, "CITBar::_AddBitmapFromForeignModule Failed - lRes == -1");
        fOk = FALSE;
        goto Error;
    }


    if (!SendMessage(_btb._hwnd, uiSetMSG, 0, (LPARAM)himlTemp))
    {
        TraceMsg(DM_ERROR, "CITBar::_AddBitmapFromForeignModule Failed - uiSetMSG SendMessage Failed");
        fOk = FALSE;
        goto Error;
    }

Error:
    if (hBMPFixedUp)
        DeleteObject(hBMPFixedUp);

    if (hBMPRaw)
        DeleteObject(hBMPRaw);

    if (dc)
        ReleaseDC(_btb._hwnd, dc);

    if (dcMemSrc)
        DeleteDC(dcMemSrc);

    if (dcMemDest)
        DeleteDC(dcMemDest);

    if (hbr)
        DeleteObject(hbr);

    if (!fOk)
        lRes = 0L;

    return lRes;
}

#define VERY_HIGH_NUMBER    4000
HRESULT CInternetToolbar::_LoadDefaultSettings()
{
    memset(&_cs, 0, sizeof(COOLBARSAVE));
    _cs.cbVer       = CBS_VERSION;

    _cs.bs[0].wID    = CBIDX_MENU;
    _cs.bs[0].cx     = VERY_HIGH_NUMBER;

    _cs.bs[1].wID    = CBIDX_BRAND;

    _cs.bs[2].wID    = CBIDX_TOOLS;
    _cs.bs[2].cx     = VERY_HIGH_NUMBER;
    _cs.bs[2].fStyle = RBBS_BREAK;

    _cs.bs[3].wID    = CBIDX_ADDRESS;
    _cs.bs[3].cx     = VERY_HIGH_NUMBER;
    _cs.bs[3].fStyle = RBBS_BREAK;

    _cs.bs[4].wID    = CBIDX_LINKS;

    if (!_fInitialPidlIsWeb)
    {
        // we're in shell view, or we're rooted.  for perf, don't bother creating the links band.
        if (IsOS(OS_WHISTLERORGREATER) && (IsOS(OS_PERSONAL)))
        {
            _cs.uiVisible = (VBF_MENU | VBF_TOOLS | VBF_BRAND);
        }
        else
        {
            _cs.uiVisible = (VBF_MENU | VBF_TOOLS | VBF_ADDRESS | VBF_BRAND);
        }
    }
    else
    {
        // web page
        _cs.uiVisible = (VBF_MENU | VBF_TOOLS | VBF_ADDRESS | VBF_LINKS | VBF_BRAND);
    }

    _cs.clsidVerticalBar = GUID_NULL;
    _cs.clsidHorizontalBar = GUID_NULL;
    _cs.fNoText = FALSE;
    _cs.fList = DEFAULT_LIST_VALUE();

    _fUsingDefaultBands = TRUE;

    return(NOERROR);
}

typedef struct tagCOOLBARSAVEv12    // IE4
{
    UINT        cbVer;
    UINT        uiMaxTBWidth;
    UINT        uiMaxQLWidth;
#ifdef UNIX
    BITBOOL     fUnUsed : 28;       // unused
#endif
    BOOL        fVertical : 1;      // The bar is oriented vertically
    BOOL        fNoText :1;         // "NoText"
    BOOL        fAutoHide : 1;      // Auto hide toolbar in theater mode
    BOOL        fStatusBar : 1;     // Status bar in theater mode
    BOOL        fSaveInShellIntegrationMode : 1;     // Did we save in shell
    UINT        uiVisible;          // "Visible bands"
    UINT        cyRebar;
    BANDSAVE    bs[5];
} COOLBARSAVEv12;

typedef struct tagCOOLBARSAVEv15    // IE5 Beta2
{
    UINT        cbVer;
    UINT        uiMaxTBWidth;
    UINT        uiMaxQLWidth;
#ifdef UNIX
    BITBOOL     fUnUsed : 28;       // unused
#endif
    BITBOOL     fVertical : 1;      // The bar is oriented vertically
    BITBOOL     fNoText :1;         // "NoText"
    BITBOOL     fList : 1;          // toolbar is TBSTYLE_LIST (text on right) + TBSTYLE_EX_MIXEDBUTTONS
    BITBOOL     fAutoHide : 1;      // Auto hide toolbar in theater mode
    BITBOOL     fStatusBar : 1;     // Status bar in theater mode
    BITBOOL     fSaveInShellIntegrationMode : 1;     // Did we save in shell integration mode?
    UINT        uiVisible;          // "Visible bands"
    UINT        cyRebar;
    BANDSAVE    bs[5];
    CLSID       clsidVerticalBar;       //clsid of bar persisted within vertical band
    CLSID       clsidHorizontalBar;
} COOLBARSAVEv15;

#define CB_V12  (sizeof(COOLBARSAVEv12))
#define CB_V13  (sizeof(COOLBARSAVEv15))
#define CB_V14  CB_V13          // 14: added fList:1 (in middle!)
#define CB_V15  CB_V14          // 15: new rbbi.fStyle semantics
#define CB_V17  (sizeof(COOLBARSAVE))

HRESULT CInternetToolbar::_LoadUpgradeSettings(ULONG cbRead)
{
    // If we shipped with the CBS_VERSION you're incrementing, you need
    // to add upgrade code here for that version, then update this assertion.
    COMPILETIME_ASSERT(CBS_VERSION == 17);

    // Double-check our size calculations.
#ifndef UNIX
    COMPILETIME_ASSERT(CB_V12 == (6 * sizeof(UINT) + CBIDX_LAST * sizeof(BANDSAVE)));
#endif
    COMPILETIME_ASSERT(CB_V12 == (CB_V15 - sizeof(CLSID) * 2));
    COMPILETIME_ASSERT(CB_V13 == (CB_V12 + 2 * sizeof(CLSID)));
    COMPILETIME_ASSERT(CB_V14 == (CB_V13 + 0));
    COMPILETIME_ASSERT(CB_V15 == (CB_V14 + 0));
    COMPILETIME_ASSERT(CB_V17 == (CB_V15 + (MAXEXTERNALBANDS * sizeof(BANDSAVE)) + (MAXEXTERNALBANDS * sizeof(CLSID))));

    // If the stream was shorter than the version data field, there's nothing we can do.
    if (cbRead < sizeof(_cs.cbVer))
    {
        return E_FAIL;
    }

    // Check to see if the version is one we know about and that the stream
    // size is the same as that version's structure size.
    if (!((_cs.cbVer == 12 && cbRead == CB_V12) ||      // IE4
          (_cs.cbVer == 13 && cbRead == CB_V13) ||      // ?
          (_cs.cbVer == 14 && cbRead == CB_V14) ||      // ?
          (_cs.cbVer == 15 && cbRead == CB_V15)))       // IE5 Beta2
    {
        return E_FAIL;
    }

    TraceMsg(DM_WARNING, "citb._lus: try upgrade %d->%d", _cs.cbVer, CBS_VERSION);

    // Make a scratch copy of _cs so we don't worry about overwriting
    // parts of _cs we need to read later.
    COOLBARSAVE cs = _cs;

    if (_cs.cbVer == 12)
    {
        // clsidVerticalBar/clsidHorizontalBar weren't a part
        // of the structure until v13
        cs.clsidVerticalBar = GUID_NULL;
        cs.clsidHorizontalBar = GUID_NULL;
    }
    else
    {
        ASSERT(_cs.cbVer < 16);

        // Band array (bs) grew in v16 to include external bands, so
        // clsidVerticalBar/clsidHorizontalBar were at a different offset.
        COOLBARSAVEv15 *pv15 = (COOLBARSAVEv15 *) &_cs;
        cs.clsidVerticalBar = pv15->clsidVerticalBar;
        cs.clsidHorizontalBar = pv15->clsidHorizontalBar;
        cs.bs[CBIDX_LAST].wID = 0xFFFFFFFF;
    }

    if (InRange(_cs.cbVer, 12, 13))
    {
        // fList was inserted into the middle of the bitbool list in v14.
        // Copy the displaced bitbools and initialize fList.
        COOLBARSAVEv12 *pv12 = (COOLBARSAVEv12 *) &_cs;
        cs.fAutoHide = pv12->fAutoHide;
        cs.fStatusBar = pv12->fStatusBar;
        cs.fSaveInShellIntegrationMode = pv12->fSaveInShellIntegrationMode;
        cs.fList = DEFAULT_LIST_VALUE();
    }

    // Force FALSE as no longer support vertical itbar mode.
    cs.fVertical = FALSE;

    // Strip off any invalid visible band bits
    cs.uiVisible &= VBF_VALID;

    // Set current version and copy scratch cs back to _cs
    cs.cbVer = CBS_VERSION;
    _cs = cs;

    return S_OK;
}

HRESULT CInternetToolbar::_LoadDefaultWidths()
{
    // If there was no max width set for the QL bar or the Toolbar, then
    // before we use the default, check it the localization people wanted to
    // increase the width. The RC file string range from '0' to '9'
    TCHAR szScratch[16];
    UINT uiExtraWidth = 0;

    if (GetSystemMetrics(SM_CXSCREEN) < 650)
    {
        MLLoadString(IDS_TB_WIDTH_EXTRA_LORES, szScratch, ARRAYSIZE(szScratch));
        _uiMaxTBWidth = MAX_TB_WIDTH_LORES;
    }
    else
    {
        MLLoadString(IDS_TB_WIDTH_EXTRA_HIRES, szScratch, ARRAYSIZE(szScratch));
        _uiMaxTBWidth = MAX_TB_WIDTH_HIRES;
    }
    _uiMaxTBWidth += StrToInt(szScratch) * WIDTH_FACTOR;


    return(NOERROR);
}

BOOL IsClsidInHKCR(REFGUID pclsid)
{
    HKEY hkeyResult;

    if (SHRegGetCLSIDKeyW(pclsid, NULL, FALSE, FALSE, &hkeyResult) == ERROR_SUCCESS)
    {
        RegCloseKey(hkeyResult);
        return TRUE;
    }
    return FALSE;
}

typedef struct tagCOOLBARSAVEv2     // IE3
{
    UINT        cbVer;
    UINT        uiMaxTBWidth;
    UINT        uiMaxQLWidth;
    BOOL        fVertical;             // The bar is oriented vertically
    BANDSAVE    bs[4];
} COOLBARSAVEv2;

#define VBF_VALIDv2               (VBF_TOOLS | VBF_ADDRESS | VBF_LINKS)

void CInternetToolbar::_TryLoadIE3Settings()
{
    HKEY hKey;

    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, c_szRegKeyCoolbar, 0, KEY_QUERY_VALUE, &hKey))
    {
        COOLBARSAVEv2 cbv2;
        DWORD dwcbData = sizeof(cbv2);
        if (SHQueryValueEx(hKey, TEXT("Layout"), NULL, NULL, (LPBYTE)&cbv2, &dwcbData) == ERROR_SUCCESS)
        {
            _cs.uiMaxTBWidth = cbv2.uiMaxTBWidth;
            _cs.uiMaxQLWidth = cbv2.uiMaxQLWidth;
            // FEATURE: todo -- read in bs field too; need to do some conversions as
            // CBIDX_ numbers were zero-based and there was no menuband in IE3.
        }

        BOOL fNoText;
        dwcbData = sizeof(fNoText);
        if (SHQueryValueEx(hKey, TEXT("NoText"), NULL, NULL, (LPBYTE)&fNoText, &dwcbData) == ERROR_SUCCESS)
        {
            // Set the no-text flag.
            _cs.fNoText = BOOLIFY(fNoText);
        }

        UINT uiVisible;
        dwcbData = sizeof(uiVisible);
        if (SHQueryValueEx(hKey, TEXT("VisibleBands"), NULL, NULL, (LPBYTE)&uiVisible, &dwcbData) == ERROR_SUCCESS)
        {
            // Set the visible bands, changing only the ones that IE3 knew about.
            _cs.uiVisible = (_cs.uiVisible &~ VBF_VALIDv2) | (uiVisible & VBF_VALIDv2);
        }

        RegCloseKey(hKey);
    }
}

VOID CInternetToolbar::_UpdateLocking()
{
    // if we have no gripper then turn them off
    BANDSITEINFO bsinfo;
    bsinfo.dwMask = BSIM_STYLE;
    bsinfo.dwStyle = BSIS_LEFTALIGN | (_fLockedToolbar ? BSIS_NOGRIPPER : 0);
    _bs.SetBandSiteInfo(&bsinfo);
    _bs._UpdateAllBands(FALSE, TRUE);
    ResizeBorderDW(NULL, NULL, FALSE);
}

HRESULT CInternetToolbar::Load(IStream *pstm)
{
    ULONG  ulRead;

    //Read from the given stream and initialize the Toolbar data!

    _fLoading = TRUE;
    HRESULT hr = pstm->Read(&_cs, sizeof(COOLBARSAVE), &ulRead);
    if (SUCCEEDED(hr))
    {
        if (ulRead != sizeof(COOLBARSAVE) || _cs.cbVer != CBS_VERSION)
        {
            hr = _LoadUpgradeSettings(ulRead);
        }
    }

    if (FAILED(hr))
    {
        _LoadDefaultSettings();
    }

    ASSERT(_cs.uiVisible & VBF_MENU);
    // make sure that the settings include a menu
    _cs.uiVisible |= VBF_MENU;

    _LoadDefaultWidths();
    hr = _CreateBands();

    _UpdateLocking();
    
    //if in web view, show the last visible browser bars too
    if (!_fShellView)
    {
        VARIANT varOut = {0};
        varOut.vt = VT_I4;

        if (!IsEqualGUID(_cs.clsidVerticalBar, GUID_NULL) && IsClsidInHKCR(_cs.clsidVerticalBar))
        {
            BOOL fSearch = IsEqualGUID(_cs.clsidVerticalBar, CLSID_SearchBand) 
                           || IsEqualGUID(_cs.clsidVerticalBar, CLSID_FileSearchBand);

            WCHAR wsz[GUIDSTR_MAX];
            SHStringFromGUID((const CLSID)_cs.clsidVerticalBar, wsz, ARRAYSIZE(wsz));

#ifdef UNIX
            // IEUNIX: Donot persist/load MsgBand
            if (!IsEqualGUID(_cs.clsidVerticalBar, CLSID_MsgBand))
#endif
            {
                if (!fSearch)
                {
                    VARIANT varClsid;
                    varClsid.vt = VT_BSTR;
                    varClsid.bstrVal = wsz;

                    IUnknown_Exec(_pbs2, &CGID_ShellDocView, SHDVID_SHOWBROWSERBAR, 1, &varClsid, &varOut);
                }
                else
                {
                    // if it's the search band, must be shown in this way to get correct search
                    VARIANTARG var;
                    var.vt = VT_I4;
                    var.lVal = -1;

                    Exec(&CLSID_CommonButtons, TBIDM_SEARCH, 0, NULL, &var);
                }
            }
        }

        if (!IsEqualGUID(_cs.clsidHorizontalBar, GUID_NULL) && IsClsidInHKCR(_cs.clsidHorizontalBar))
        {
            WCHAR wsz[GUIDSTR_MAX];
            SHStringFromGUID((const CLSID)_cs.clsidHorizontalBar, wsz, ARRAYSIZE(wsz));

            VARIANT varClsid;
            varClsid.vt = VT_BSTR;
            varClsid.bstrVal = wsz;

            IUnknown_Exec(_pbs2, &CGID_ShellDocView, SHDVID_SHOWBROWSERBAR, 1, &varClsid, &varOut);
        }
    }
    _fLoading = FALSE;

    return hr;
}

//see APPHACK note below
const GUID CLSID_AlexaVert = { 0xBA0B386CL, 0x7143, 0x11d1, 0xba, 0x8c, 0x00, 0x60, 0x08, 0x27, 0x87, 0x8d };
const GUID CLSID_AlexaHorz = { 0xBA0B386EL, 0x7143, 0x11d1, 0xba, 0x8c, 0x00, 0x60, 0x08, 0x27, 0x87, 0x8d };

void CInternetToolbar::_GetVisibleBrowserBar(UINT idBar, CLSID *pclsidOut)
{
    *pclsidOut = GUID_NULL;

    ASSERT(idBar == IDBAR_VERTICAL || idBar == IDBAR_HORIZONTAL);

    IDockingWindowFrame *psb;
    if (_psp && SUCCEEDED(_psp->QueryService(SID_STopLevelBrowser, IID_PPV_ARG(IDockingWindowFrame, &psb))))
    {
        IDeskBar* pdb;

        if ( (IDBAR_VERTICAL   == idBar && (SUCCEEDED(psb->FindToolbar(INFOBAR_TBNAME, IID_PPV_ARG(IDeskBar, &pdb))) && pdb)) ||
             (IDBAR_HORIZONTAL == idBar && (SUCCEEDED(psb->FindToolbar(COMMBAR_TBNAME, IID_PPV_ARG(IDeskBar, &pdb))) && pdb)) )
        {
            VARIANT varClsid = {0};

            if (SUCCEEDED(IUnknown_Exec(pdb, &CGID_DeskBarClient, DBCID_CLSIDOFBAR, 1, NULL, &varClsid)))
            {
                if (varClsid.vt == VT_BSTR)
                {
                    GUIDFromString(varClsid.bstrVal, pclsidOut);
                    VariantClear(&varClsid);
                }

//APPHACK
// Alexa 3.0 has some code so that their explorer bar persists that works in ie4. however, when ie5
// persists them, they don't handle the case where the main page has not finished loading yet, which
// causes them to fault on launch of the browser. see IE5 55895.
                if ( (IDBAR_VERTICAL   == idBar && (IsEqualGUID(*pclsidOut, CLSID_AlexaVert))) ||
                     (IDBAR_HORIZONTAL == idBar && (IsEqualGUID(*pclsidOut, CLSID_AlexaHorz))) )
                {
                    *pclsidOut = GUID_NULL;
                }
//END APPHACK
            }

            pdb->Release();
        }
        psb->Release();
    }
}

void CInternetToolbar::_BuildSaveStruct(COOLBARSAVE* pcs)
{
    REBARBANDINFO   rbbi;
    RECT rc;
    static BOOL fBrowserOnly = (WhichPlatform() != PLATFORM_INTEGRATED);

    //Save into the given stream!
    memset(pcs, 0, sizeof(COOLBARSAVE));
    pcs->cbVer = CBS_VERSION;

    // Browser Only can't load Shell Integrated streams because of the Favorites
    // shell extension created pidls unreadable by browser only which doesn't have the Favorites ShellExt
    pcs->fSaveInShellIntegrationMode = !fBrowserOnly;

    GetWindowRect(_bs._hwnd, &rc);
    pcs->cyRebar = RECTHEIGHT(rc);
    //Save the new fields.
    pcs->fAutoHide = _fAutoHide;
    pcs->fNoText = _fCompressed;
    pcs->fList = IS_LIST_STYLE(_btb._hwnd);
    pcs->uiVisible = _nVisibleBands;

    //only persist the visible bars for web view
    if (!_fShellView)
    {
        _GetVisibleBrowserBar(IDBAR_VERTICAL, &pcs->clsidVerticalBar);
        _GetVisibleBrowserBar(IDBAR_HORIZONTAL, &pcs->clsidHorizontalBar);
    }
    //else pcs->clsid*Bar nulled out by memset above

    LRESULT lStyle = GetWindowLong(_bs._hwnd, GWL_STYLE);
    pcs->fVertical = BOOLIFY(lStyle & CCS_VERT);

    pcs->uiMaxTBWidth = _uiMaxTBWidth;

    rbbi.cbSize = sizeof(REBARBANDINFO);
    rbbi.fMask = RBBIM_STYLE | RBBIM_SIZE | RBBIM_ID;
    int icBands = (int) SendMessage( _bs._hwnd, RB_GETBANDCOUNT, 0, 0 );
    for (int i = 0; i < icBands; i++)
    {
        pcs->bs[i].wID = 0xFFFFFFFF;
        if (SendMessage(_bs._hwnd, RB_GETBANDINFO, i, (LPARAM) &rbbi))
        {
            if (rbbi.wID < CBANDSMAX)
            {
                // desk band objects have the choice of not saving there visibility
                // state
                BANDITEMDATA *pbid = _bs._GetBandItem( i );
                UINT uiMask = rbbi.wID <= CBIDX_LAST ? ( 1 << (rbbi.wID - 1) ) : EXTERNALBAND_VBF_BIT(rbbi.wID - CBIDX_LAST-1);
                if (pbid && pbid->pdb && (pcs->uiVisible & uiMask))
                {
                    OLECMD cmd;
                    cmd.cmdID = CITIDM_DISABLEVISIBILITYSAVE;
                    cmd.cmdf = 0;
                    IUnknown_QueryStatus( pbid->pdb, &CGID_PrivCITCommands, 1, &cmd, NULL );
                    if ( cmd.cmdf &  OLECMDF_ENABLED )
                    {
                        pcs->uiVisible &= ~uiMask;
                        rbbi.fStyle |= RBBS_HIDDEN;
                    }
                }
                pcs->bs[i].fStyle = rbbi.fStyle;
                pcs->bs[i].cx = rbbi.cx;
                pcs->bs[i].wID = rbbi.wID;
                if (IS_EXTERNALBAND(rbbi.wID))
                {
                    pcs->aclsidExternalBands[MAP_TO_EXTERNAL(rbbi.wID)] = _rgebi[MAP_TO_EXTERNAL(rbbi.wID)].clsid;
                }
            }
        }
    }
    // Query CShellBrowser for status bar state
    VARIANTARG v = { 0 };
    v.vt = VT_I4;
    IUnknown_Exec(_ptbsite, &CGID_ShellBrowser, FCIDM_GETSTATUSBAR,
        0, NULL, &v);
    pcs->fStatusBar = v.lVal;
}

typedef struct tagCLSID_BANDTYPE
{
    const CLSID * pclsid;
    DWORD dwBandID;
} CLSID_BANDTYPE;

CLSID_BANDTYPE c_CLSIDsToSave[] =
{
    {&CLSID_AddressBand, CBIDX_ADDRESS},
    {&CLSID_QuickLinks, CBIDX_LINKS},
};

HRESULT CInternetToolbar::Save(IStream *pstm, BOOL fClearDirty)
{
    COOLBARSAVE cs;
    HRESULT hr = S_FALSE;

    // avoid the hit of saving when we are still loading.  State will not have
    // changed, at least not enough to justify saving, until after we are loaded.
    if (_fLoading)
        return S_OK;

    // Check the dirty bit to see if we need to save.
    if (!_fDirty)
        return S_OK;

    // if we failed during creation, our current state isnt good enough to persist.
    if (_fDontSave)
        return S_OK;

    ASSERT(!_fTheater);
    _BuildSaveStruct(&cs);

    if(SUCCEEDED(hr = pstm->Write(&cs, sizeof(COOLBARSAVE), NULL)) && fClearDirty)
        _fDirty = FALSE;

    REBARBANDINFO rbbi;
    rbbi.cbSize = sizeof(REBARBANDINFO);
    rbbi.fMask = RBBIM_ID;

    int icBands = (int) SendMessage( _bs._hwnd, RB_GETBANDCOUNT, 0, 0 );
    for (int i = 0; i < icBands; i++)
    {
        if (SendMessage(_bs._hwnd, RB_GETBANDINFO, i, (LPARAM) &rbbi))
        {
            if ((rbbi.wID == CBIDX_ADDRESS) || (rbbi.wID == CBIDX_LINKS) || IS_EXTERNALBAND(rbbi.wID))
            {
                BANDITEMDATA *pbid = _bs._GetBandItem( i );
                if (pbid && pbid->pdb)
                {
                    CLSID clsid;
                    IPersistStream *pStream;
                    if (SUCCEEDED(pbid->pdb->QueryInterface(IID_PPV_ARG(IPersistStream, &pStream))))
                    {
                        pStream->GetClassID( &clsid );
                        IStream * pstm;
                        TCHAR szGUID[MAX_PATH];
                        SHStringFromGUID( clsid, szGUID, ARRAYSIZE(szGUID) );
                        pstm = GetRegStream( _fInitialPidlIsWeb, szGUID, STGM_WRITE | STGM_CREATE );
                        if (pstm)
                        {
                            HRESULT hrInternal = _bs.SaveToStreamBS(pbid->pdb, pstm);

                            // Only return Success values
                            if (SUCCEEDED(hrInternal))
                                hr = S_OK;
                            pstm->Release();
                        }
                        pStream->Release();
                    }
                }
            }
        }
    }
    return(hr);
}

HRESULT CInternetToolbar::InitNew(void)
{
    // This shouldn't get called if Load has already been called, so assert
    // that _cs is uninitialized.
    ASSERT(_cs.cbVer == 0);

    _LoadDefaultSettings();

    // Look for any IE3 settings and override defaults with those.  (IE3
    // wrote structures directly to registry, rather than via IPersistStream).
    _TryLoadIE3Settings();

    _LoadDefaultWidths();

    return S_OK;
}

BOOL CInternetToolbar::_SendToToolband(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres)
{
    return _bs._SendToToolband(hwnd, uMsg, wParam, lParam, plres);
}


HRESULT CInternetToolbar::IsDirty(void)
{
    if (_fDirty && !_fLoading)
        return S_OK;
    else
        return S_FALSE;
}


HRESULT CInternetToolbar::QueryService(REFGUID guidService,
                                       REFIID riid, void **ppvObj)
{
    HRESULT hres = E_NOTIMPL;

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

            hres = CreateIBandProxyAndSetSite(SAFECAST(_ptbsitect, IUnknown *), riid, &_pbp, ppvObj);
            if(_pbp)
            {
                ASSERT(S_OK == hres);
                _fCreatedBandProxy = TRUE;
            }
        }
        return hres;
    }
    else if (IsEqualGUID(guidService, IID_IBandSite))
    {
        return _bs.QueryInterface(riid, ppvObj);
    }
    else if (IsEqualGUID(guidService, IID_IAddressBand))
    {
        LPBANDITEMDATA pbid = _bs._GetBandItemDataStructByID(CBIDX_ADDRESS);
        if (pbid && pbid->pdb)
        {
            return pbid->pdb->QueryInterface(riid, ppvObj);
        }
        else
        {
            *ppvObj = NULL;
            return E_FAIL;
        }
    }

    if (_psp)
        return _psp->QueryService(guidService, riid, ppvObj);


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

//
// FEATURE: Do we really need to implement the following two functions?
// Currently nobody uses them.
//
HRESULT CInternetToolbar::GetClassID(GUID *pguid)
{
    *pguid = CLSID_InternetToolbar;
    return(S_OK);
}

HRESULT CInternetToolbar::GetSizeMax(ULARGE_INTEGER *ulMaxSize)
{
    ulMaxSize->LowPart = sizeof(COOLBARSAVE);
    ulMaxSize->HighPart = 0;
    return(S_OK);
}


CInternetToolbar::CITBandSite::CITBandSite() : CBandSite(NULL)
{
    // HACKHACK: set the initial band ID to something bigger
    //           than the number of toolbars that is in this
    //           object.  Currently those toolbars are not
    //           individual bands, but we want CBandSite to
    //           at least be aware of them.
    //
    _dwBandIDNext = CBANDSMAX;
}

HRESULT CInternetToolbar::CITBandSite::_OnContextMenu(WPARAM wParam, LPARAM lParam)
{
    CInternetToolbar* pitbar = IToClass(CInternetToolbar, _bs, this);
    pitbar->_ShowContextMenu((HWND)wParam, lParam, NULL);
    return S_OK;
}

HRESULT CInternetToolbar::CITBandSite::_Initialize(HWND hwndParent)
{
    _hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, REBARCLASSNAME, NULL,
                           RBS_VARHEIGHT | RBS_BANDBORDERS | RBS_REGISTERDROP | RBS_DBLCLKTOGGLE |
                           WS_VISIBLE | WS_BORDER | WS_CHILD | WS_CLIPCHILDREN |
//                           WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN |
                           WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NOPARENTALIGN,
                           0, 0, 100, 36, hwndParent, (HMENU) FCIDM_REBAR, HINST_THISDLL, NULL);

    if (_hwnd)
    {
        Comctl32_SetWindowTheme(_hwnd, TEXT("ExplorerToolbar"));
        SendMessage(_hwnd, RB_SETTEXTCOLOR, 0, CLR_DEFAULT);
        SendMessage(_hwnd, RB_SETBKCOLOR, 0, CLR_DEFAULT);
        SendMessage(_hwnd, CCM_SETVERSION, COMCTL32_VERSION, 0);
    }

    return CBandSite::_Initialize(hwndParent);
}


HRESULT CInternetToolbar::CITBandSite::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
    DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
    if (!pguidCmdGroup)
    {
        /*NOTHING*/
    }
    else if (IsEqualGUID(CGID_PrivCITCommands, *pguidCmdGroup))
    {
        CInternetToolbar* pitbar = IToClass(CInternetToolbar, _bs, this);
        return pitbar->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
    }
    else if (IsEqualGUID(CGID_Theater, *pguidCmdGroup))
    {
        CInternetToolbar* pitbar = IToClass(CInternetToolbar, _bs, this);
        return IUnknown_Exec(pitbar->_ptbsite, pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
    }
    return CBandSite::Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
}

HRESULT CInternetToolbar::CITBandSite::AddBand(IUnknown *punk)
{
    HRESULT hres = CBandSite::AddBand(punk);
    if (SUCCEEDED(hres))
    {
        CInternetToolbar* pitbar = IToClass(CInternetToolbar, _bs, this);
        pitbar->_SetBackground();
    }
    return hres;
}

HRESULT CInternetToolbar::CITBandSite::HasFocusIO()
{
    HRESULT hres = CBandSite::HasFocusIO();
    if (hres == S_FALSE)
    {
        CInternetToolbar* pitbar = IToClass(CInternetToolbar, _bs, this);
        if (pitbar->_btb._hwnd == GetFocus())
            hres = S_OK;

    }
    return hres;
}

// This will remove all the buttons except the first 2
BOOL TOOLSBANDCLASS::_RemoveAllButtons()
{
    INT_PTR nCount = SendMessage(_hwnd, TB_BUTTONCOUNT, 0, 0L);

    if (!nCount)
        return FALSE;

    while (nCount-- > 0)
    {
        SendMessage(_hwnd, TB_DELETEBUTTON, nCount, 0L);
    }

    return S_OK;
}


HRESULT TOOLSBANDCLASS::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
    DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
     CInternetToolbar* pitbar = IToClass(CInternetToolbar, _btb, this);
    if (!pguidCmdGroup)
    {
        /*NOTHING*/
#ifdef DEBUG
    }
    else if (IsEqualGUID(*pguidCmdGroup, IID_IExplorerToolbar))
    {
        switch(nCmdID)
        {
        case ETCMDID_GETBUTTONS:
            // if this rips call tjgreen
            ASSERT(0);
            return E_FAIL;
        }
#endif
    }
    else if (_IsDocHostGUID(pguidCmdGroup))
    {
        UEMFireEvent(&UEMIID_BROWSER, UEME_UITOOLBAR, UEMF_XEVENT, UIG_INET, nCmdID);

        if (nCmdexecopt == OLECMDEXECOPT_PROMPTUSER)
        {
            // the user hit the drop down
            if (pitbar->_ptbsitect && pvarargIn && pvarargIn->vt == VT_INT_PTR)
            {
                // v.vt = VT_I4;
                POINT pt;
                RECT* prc = (RECT*)pvarargIn->byref;
                pt.x = prc->left;
                pt.y = prc->bottom;

                switch (nCmdID)
                {
#ifdef EDIT_HACK
                case DVIDM_EDITPAGE:
                    {
                        // Show the edit pop-up
                        BSTR bstrURL;
                        pitbar->_pdie->get_LocationURL(&bstrURL);
                        if (bstrURL)
                        {
                            USES_CONVERSION;
                            pitbar->_aEditVerb.ShowEditMenu(pt,  pitbar->_hwnd, W2T(bstrURL));
                            SysFreeString(bstrURL);
                        }
                        break;
                    }
#endif

                default:
                    // if this rips find tjgreen
                    ASSERT(0);
                    break;
                }
            }
            return S_OK;
        }

        switch(nCmdID)
        {
        case DVIDM_EDITPAGE:
        {
            BSTR bstrURL;
            ULONG fMask = 0;
            TCHAR szCacheFileName[MAX_PATH + MAX_URL_STRING + 2];
            memset(szCacheFileName, 0, sizeof(szCacheFileName));

            pitbar->_pdie->get_LocationURL(&bstrURL);
            if (NULL == bstrURL)
                break;

            USES_CONVERSION;
            LPCTSTR szURL = W2T(bstrURL);

#ifdef EDIT_HACK
            // Use the default edit verb
            pitbar->_aEditVerb.Edit(szURL);
#else
            SHELLEXECUTEINFO sei = {0};
            LPTSTR pszTemp = PathFindExtension(szURL);

            // If we did not find an extension, we assume it is an .htm or .html
            if (*pszTemp != TEXT('.'))
            {
                if (SHVerbExists(TEXT(".htm"), TEXT("edit"), NULL))
                    sei.lpClass = TEXT(".htm");
                else
                    sei.lpClass = TEXT(".html");
            }
            else
            {
                sei.lpClass = pszTemp;
            }

            if (PathIsURL(szURL))
            {
                // (reinerf)
                // Some apps (FrontPad, Office99, etc) want the URL passed to
                // them instead of the cache filename. We therefore create a string
                // that has the URL name after the null:
                //
                //  "CacheFileName/0UrlName"
                //
                // and pass it as the lpFile parameter to shellexecute.
                // We also pass SEE_MASK_FILEANDURL, so shellexecute can
                // recognize this case.
                int iLength;

                URLDownloadToCacheFile(NULL, szURL, szCacheFileName, 0, URLOSTRM_USECACHEDCOPY, NULL);
                iLength = lstrlen(szCacheFileName);

                // copy in the URL name
                StrCpy(&szCacheFileName[iLength + 1], szURL);

                // add the mask so shellexecute knows to check for the URL, if necessary.
                fMask |= SEE_MASK_FILEANDURL;
            }
            else
            {
                StrCpy(szCacheFileName, szURL);
            }

            sei.cbSize = sizeof(SHELLEXECUTEINFO);
            sei.fMask = fMask;
            sei.hwnd = NULL;
            sei.lpVerb = TEXT("edit");
            sei.lpFile = szCacheFileName;
            sei.lpParameters = NULL;
            sei.lpDirectory = NULL;
            sei.nShow = SW_SHOWNORMAL;
            sei.hInstApp = NULL;

            ShellExecuteEx(&sei);

            SysFreeString(bstrURL);
#endif //!EDIT_HACK
        }
        break;

        default:
            // if this rips call tjgreen
            ASSERT(0);
            break;
        }
    }

    return S_OK;
}

// *** IInputObject methods ***
HRESULT TOOLSBANDCLASS::TranslateAcceleratorIO(LPMSG lpMsg)
{
    if (SendMessage(_hwnd, TB_TRANSLATEACCELERATOR, 0, (LPARAM)lpMsg))
        return S_OK;

    return CToolBand::TranslateAcceleratorIO(lpMsg);
}

// *** IUnknown methods ***
HRESULT TOOLSBANDCLASS::QueryInterface(REFIID riid, void ** ppvObj)
{
    static const QITAB qit[] = {
        QITABENT(TOOLSBANDCLASS, IWinEventHandler),
        { 0 },
    };

    HRESULT hres = QISearch(this, qit, riid, ppvObj);
    if (FAILED(hres))
        hres = CToolBand::QueryInterface(riid, ppvObj);

    return hres;
}

// *** IDeskBand methods ***
HRESULT TOOLSBANDCLASS::GetBandInfo(DWORD dwBandID, DWORD fViewMode, DESKBANDINFO* pdbi)
{

    _dwBandID = dwBandID;

    // set dwModeFlags
    pdbi->dwModeFlags = DBIMF_FIXEDBMP | DBIMF_USECHEVRON;

    // set ptMinSize
    {
        if (SendMessage(_hwnd, TB_BUTTONCOUNT, 0, 0))
        {
            // make our min size just big enough to show the first button
            RECT rc;
            SendMessage(_hwnd, TB_GETITEMRECT, 0, (LPARAM)&rc);
            pdbi->ptMinSize.x = RECTWIDTH(rc);
            pdbi->ptMinSize.y = RECTHEIGHT(rc);
        }
        else
        {
            // we don't have any buttons; so use standard button size
            LONG lButtonSize = (long) SendMessage(_hwnd, TB_GETBUTTONSIZE, 0, 0);
            pdbi->ptMinSize.x = LOWORD(lButtonSize);
            pdbi->ptMinSize.y = HIWORD(lButtonSize);
        }

        CInternetToolbar* pitbar = IToClass(CInternetToolbar, _btb, this);
        if (pitbar->_fTheater && (pdbi->ptMinSize.y < (THEATER_CYTOOLBAR - 1)))
            pdbi->ptMinSize.y = (THEATER_CYTOOLBAR - 1);
    }

    // set ptActual
    {
        SIZE size;
        size.cy = pdbi->ptMinSize.y;
        SendMessage(_hwnd, TB_GETIDEALSIZE, FALSE, (LPARAM)&size);
        pdbi->ptActual.x = size.cx;
        pdbi->ptActual.y = size.cy;
    }

    // no title
    pdbi->dwMask &= ~DBIM_TITLE;

    return S_OK;
}

IOleCommandTarget* TOOLSBANDCLASS::_CommandTargetFromCmdMap(CMDMAP* pcm)
{
    IOleCommandTarget* pct = NULL;

    if (pcm)
    {
        if (IsEqualGUID(pcm->guidButtonGroup, CLSID_CommonButtons))
        {
            CInternetToolbar* pitbar = IToClass(CInternetToolbar, _btb, this);
            pct = SAFECAST(pitbar, IOleCommandTarget*);
        }
        else
        {
            // If either of these rip, the button is stale
            ASSERT(IsEqualGUID(pcm->guidButtonGroup, _guidCurrentButtonGroup));
            ASSERT(_pctCurrentButtonGroup);

            pct = _pctCurrentButtonGroup;
        }
    }

    return pct;
}

BOOL ShiftRectToEdgeOfMonitor(RECT *prc)
{
    BOOL bRet = FALSE;
    POINT pt = {prc->left, prc->top};

    HMONITOR hmon = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
    if (hmon)
    {
        MONITORINFO mi = {sizeof(MONITORINFO)};
        if (GetMonitorInfo(hmon, &mi))
        {
            // get the different between the monitor's left edge and the rect's left edge
            int iShift = mi.rcMonitor.left - prc->left;
            if (iShift > 0)
            {
                prc->left += iShift;
                prc->right += iShift;

                bRet = TRUE;
            }
        }
    }
    return bRet;
}

LRESULT TOOLSBANDCLASS::_OnToolbarDropDown(TBNOTIFY *ptbn)
{
    if (ptbn->hdr.hwndFrom == _hwnd)
    {
        CMDMAP* pcm = _GetCmdMapByID(ptbn->iItem);
        IOleCommandTarget* pct = _CommandTargetFromCmdMap(pcm);

        if (pct)
        {
            VARIANTARG var;
            var.vt = VT_I4;
            var.lVal = ptbn->iItem;

            // REARCHITECT: use VARIANT[TO/FROM]BUFFER here to fix win64 problem

            VARIANT v = {VT_INT_PTR};
            v.byref = &ptbn->rcButton;

            MapWindowRect(_hwnd, HWND_DESKTOP, &ptbn->rcButton);

            //
            // If this window is mirrored, then let's take the
            // other coordinate [samera]
            //
            if (IS_WINDOW_RTL_MIRRORED(_hwnd))
            {
                int iTmp = ptbn->rcButton.right;
                ptbn->rcButton.right = ptbn->rcButton.left;
                ptbn->rcButton.left  = iTmp;
            }

            // TrackMenuPopup is lame when confronted with negative co-ordinates... lets clip to the edge of the screen.
            ShiftRectToEdgeOfMonitor(&ptbn->rcButton);

#ifdef EDIT_HACK
            // FEATURE: temp code -- edit code moving to dochost.cpp
            if (_IsDocHostGUID(&pcm->guidButtonGroup) && pcm->nCmdID == DVIDM_EDITPAGE)
                Exec(&pcm->guidButtonGroup, (DWORD)pcm->nCmdID, OLECMDEXECOPT_PROMPTUSER, &v, &var);
            else
#endif
                pct->Exec(&pcm->guidButtonGroup, (DWORD)pcm->nCmdID, OLECMDEXECOPT_PROMPTUSER, &v, &var);
        }
    }

    return TBDDRET_DEFAULT;
}

LRESULT TOOLSBANDCLASS::_TryShowBackForwardMenu(DWORD dwItemSpec, LPPOINT ppt, LPRECT prcExclude)
{
    LRESULT lres = 0;

    GUID guid;
    UINT id;
    if (SUCCEEDED(_ConvertCmd(NULL, dwItemSpec, &guid, &id)))
    {
        // If the user right clicked on the the back or forward button, show the context menu
        // On all other buttons show the regular shortcut menu
        if (IsEqualGUID(guid, CLSID_CommonButtons))
        {
            CInternetToolbar* pitbar = IToClass(CInternetToolbar, _btb, this);
            if (id == TBIDM_BACK)
            {
                pitbar->_ShowBackForwardMenu(FALSE, *ppt, prcExclude);
                lres = 1;
            }
            else if (id == TBIDM_FORWARD)
            {
                pitbar->_ShowBackForwardMenu(TRUE, *ppt, prcExclude);
                lres = 1;
            }
        }
    }
    return lres;
}

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

    ASSERT(pnmh->idFrom == FCIDM_TOOLBAR);

    switch (pnmh->code)
    {

    case NM_RCLICK:
        {
            NMCLICK * pnm = (LPNMCLICK)pnmh;

            if (!pnm)
                break;

            // Convert to Screen coordinates
            MapWindowPoints(pnmh->hwndFrom, HWND_DESKTOP, &pnm->pt, 1);

            if (pnmh->hwndFrom == _hwnd)
                lres = _TryShowBackForwardMenu((DWORD)pnm->dwItemSpec, &pnm->pt, NULL);
        }
        break;

    case TBN_DROPDOWN:
        lres = _OnToolbarDropDown((TBNOTIFY *)pnmh);
        break;

    case TBN_DELETINGBUTTON:
        _OnDeletingButton((TBNOTIFY*)pnmh);
        break;

    case TBN_SAVE:
    case TBN_RESET:
    case TBN_INITCUSTOMIZE:
    case TBN_RESTORE:
    case TBN_BEGINADJUST:
    case TBN_GETBUTTONINFO:
    case TBN_ENDADJUST:
    case TBN_QUERYDELETE:
    case TBN_QUERYINSERT:
    case TBN_TOOLBARCHANGE:
        if (pnmh->hwndFrom == _hwnd)
            lres = _ToolsCustNotify (pnmh);
        break;

    case TBN_GETOBJECT:
        {
            NMOBJECTNOTIFY *pnmon = (NMOBJECTNOTIFY *)pnmh;
            if (IsEqualIID(*pnmon->piid, IID_IDropTarget))
            {
                if (pnmh->hwndFrom == _hwnd)
                {
                    UINT uiCmd;
                    GUID guid;
                    _ConvertCmd(NULL, pnmon->iItem, &guid, &uiCmd);

                    if (IsEqualGUID(guid, CLSID_CommonButtons) &&
                            (uiCmd == TBIDM_HOME || uiCmd == TBIDM_FAVORITES))
                    {
                        CITBarDropTarget *pdtgt = new CITBarDropTarget(_hwnd, uiCmd);
                        if (pdtgt)
                        {
                            pnmon->pObject = SAFECAST(pdtgt, IDropTarget*);
                            pnmon->hResult = NOERROR;
                        }
                    }
                    else     // pass back CDropDummy to handle basics.
                    {
                        CDropDummy *pdtgt = new CDropDummy(_hwnd);
                        if (pdtgt)
                        {
                            pnmon->pObject = SAFECAST(pdtgt, IDropTarget*);
                            pnmon->hResult = NOERROR;
                        }
                    }

                }
                lres = TRUE;
            }
        }
        break;

    default:
        lres = CToolbarBand::_OnNotify(pnmh);
        break;
    }

    return lres;
}

LRESULT TOOLSBANDCLASS::_OnContextMenu(LPARAM lParam, WPARAM wParam)
{
    LRESULT lres = 0;

    if (IS_WM_CONTEXTMENU_KEYBOARD(lParam))
    {
        // keyboard context menu.  figure out where to pop up menu and
        // which context menu to use, and tell itbar to pop it up.
        RECT rc;
        BOOL fBackForward = FALSE;

        // figure out coordinates to use
        INT_PTR iBtn = SendMessage(_hwnd, TB_GETHOTITEM, 0, 0);
        if (iBtn != -1)
        {
            // use lower left corner of current hot button
            SendMessage(_hwnd, TB_GETITEMRECT, iBtn, (LPARAM)&rc);
        }
        else
        {
            // no hot button; use top left corner of tools window
            SetRect(&rc, 0, 0, 0, 0);
        }
        MapWindowPoints(_hwnd, HWND_DESKTOP, (LPPOINT)&rc, 2);

        if (iBtn != -1)
        {
            // get hot button's command
            TBBUTTONINFO tbbi;
            tbbi.cbSize = sizeof(TBBUTTONINFO);
            tbbi.dwMask = TBIF_BYINDEX | TBIF_COMMAND;
            SendMessage(_hwnd, TB_GETBUTTONINFO, iBtn, (LPARAM)&tbbi);

            POINT pt = {rc.left, rc.bottom};

            // try popping up the back/forward context menu
            if (_TryShowBackForwardMenu(tbbi.idCommand, &pt, &rc))
                fBackForward = TRUE;
        }

        if (!fBackForward)
        {
            // pop up the standard context menu
            CInternetToolbar* pitbar = IToClass(CInternetToolbar, _btb, this);
            pitbar->_ShowContextMenu((HWND)wParam, MAKELONG(rc.left, rc.bottom), (iBtn == -1 ? NULL : &rc));
        }

        lres = 1;
    }
    return lres;
}

void TOOLSBANDCLASS::_RecalcButtonWidths()
{
    // We need the toolbars buttons to use ONLY exactly as much space as is needed.
    // By setting the size a a really small number like 10, and then setting it to
    // the real number we can accomplish this.
    // If we don't use do this, then when we add new buttons after doing this
    // RemoveAllButtons(), the new buttons will be at least as wide as the widest
    // button that existed on the last set of buttons (the ones we are just removing)
    CInternetToolbar* pitbar = IToClass(CInternetToolbar, _btb, this);
    SendMessage(_hwnd, TB_SETBUTTONWIDTH, 0, (LPARAM)MAKELONG(0, 10));
    SendMessage(_hwnd, TB_SETBUTTONWIDTH, 0, (LPARAM)(pitbar->_fCompressed ? MAKELONG(0, MAX_TB_COMPRESSED_WIDTH) : MAKELONG(0, pitbar->_uiMaxTBWidth)));
}

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

    switch (dwMsg)
    {
    case WM_CONTEXTMENU:
        *plres = _OnContextMenu(lParam, wParam);
        break;

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

    case WM_WININICHANGE:
        *plres = SendMessage(_hwnd, dwMsg, wParam, lParam);
        if (wParam == SPI_SETNONCLIENTMETRICS)
        {
            _RecalcButtonWidths();
            _BandInfoChanged();
        }
        break;

    default:
        hres = CToolbarBand::OnWinEvent(hwnd, dwMsg, wParam, lParam, plres);
        break;
    }

    return hres;
}

CMDMAP* TOOLSBANDCLASS::_GetCmdMap(int i, BOOL fByIndex)
{
    TBBUTTONINFO tbbi;
    tbbi.cbSize = sizeof(tbbi);
    tbbi.dwMask = TBIF_LPARAM;
    tbbi.lParam = 0;
    if (fByIndex)
        tbbi.dwMask |= TBIF_BYINDEX;
    SendMessage(_hwnd, TB_GETBUTTONINFO, i, (LPARAM)&tbbi);
    return (CMDMAP*)(void*)tbbi.lParam;
}


void TOOLSBANDCLASS::_FreeCmdMap(CMDMAP* pcm)
{
    if (pcm)
        LocalFree(pcm);
}

void TOOLSBANDCLASS::_OnDeletingButton(TBNOTIFY* ptbn)
{
    CMDMAP *pcm = (CMDMAP*)(void*)ptbn->tbButton.dwData;
    _FreeCmdMap(pcm);
}

LONG_PTR TOOLSBANDCLASS::_AddString(LPWSTR pwstr)
{
    LONG_PTR lOffset;
    CInternetToolbar* pitbar = IToClass(CInternetToolbar, _btb, this);
    pitbar->AddString(&_guidCurrentButtonGroup, 0, (UINT_PTR)pwstr, &lOffset);

    return lOffset;
}

#define PPBS_LOOKINTOOLBAR  0x00000001
#define PPBS_EXTERNALBUTTON 0x00000002

void TOOLSBANDCLASS::_PreProcessButtonString(TBBUTTON *ptbn, DWORD dwFlags)
{
    // Caller should have checked this.
    ASSERT(!(ptbn->fsStyle & BTNS_SEP));

    // If we don't have a command target, we shouldn't have any external buttons.
    ASSERT(_pctCurrentButtonGroup || !(dwFlags & PPBS_EXTERNALBUTTON));

    if (ptbn->iString < 0 && ptbn->iBitmap <= MAX_SHELLGLYPHINDEX)
    {
        // total hack
        // we're hard coding the strings in to match
        // the bitmap.  so if anyone uses the shell bitmaps,
        // they're going to get our text labels
        // also hacking in that the bitmap array and string array are
        // matched
        // who designed reviewed this???

        ptbn->iString = ptbn->iBitmap;
    }
    else if (!ptbn->iString && (dwFlags & PPBS_EXTERNALBUTTON))
    {
        // Some Extensions are giving us bogus string ids (Font ext sends 0)
        ptbn->iString = -1;
    }
    else if (ptbn->iString != -1 && !IS_INTRESOURCE(ptbn->iString))
    {
        // It's a string pointer.  The customization mechanism requires that all buttons
        // use strings from the tb string pool.  So add the string to the pool and set
        // iString to the pool index.
        ptbn->iString = _AddString((LPWSTR)ptbn->iString);
    }

    if (ptbn->iString == -1 && IsFlagSet(dwFlags, PPBS_LOOKINTOOLBAR | PPBS_EXTERNALBUTTON))
    {
        // If we're building the customization dsa rather than adding new buttons to the
        // toolbar, we may already have this button in the toolbar.  If so, use that string.

        UINT idCommand;
        if (SUCCEEDED(_ConvertCmd(&_guidCurrentButtonGroup, ptbn->idCommand, NULL, &idCommand)))
        {
            TBBUTTON tbb;
            if (SendMessage(_hwnd, TB_GETBUTTON, idCommand, (LPARAM)&tbb))
                ptbn->iString = tbb.iString;
        }
    }

    if (ptbn->iString == -1 && (dwFlags & PPBS_EXTERNALBUTTON))
    {
        // Still don't have a string for this puppy.  Last resort is to ask via QueryStatus.
        OLECMDTEXTV<MAX_TOOLTIP_STRING> cmdtv;
        OLECMDTEXT *pcmdText = &cmdtv;

        pcmdText->cwBuf = MAX_TOOLTIP_STRING;
        pcmdText->cmdtextf = OLECMDTEXTF_NAME;
        pcmdText->cwActual = 0;

        OLECMD rgcmd = {ptbn->idCommand, 0};

        HRESULT hr = _pctCurrentButtonGroup->QueryStatus(&_guidCurrentButtonGroup, 1, &rgcmd, pcmdText);
        if (SUCCEEDED(hr) && (pcmdText->cwActual))
            ptbn->iString = _AddString(pcmdText->rgwz);
    }

    // If it's an internal button, we'd better have found a string for it.
    ASSERT(ptbn->iString != -1 || (dwFlags & PPBS_EXTERNALBUTTON));
}

void TOOLSBANDCLASS::_PreProcessExternalTBButton(TBBUTTON *ptbn)
{
    if (!(ptbn->fsStyle & BTNS_SEP))
    {
        CMDMAP* pcm = (CMDMAP*)LocalAlloc(LPTR, sizeof(CMDMAP));
        if (pcm)
        {
            pcm->guidButtonGroup = _guidCurrentButtonGroup;
            pcm->nCmdID = ptbn->idCommand;

            _PreProcessButtonString(ptbn, PPBS_EXTERNALBUTTON);

            _nNextCommandID++;
            pcm->lParam = ptbn->dwData;
        }

        ptbn->dwData = (LPARAM)pcm;
    }
    else
    {
        ptbn->dwData = 0;

        // override default toolbar width for separators; iBitmap member of
        // TBBUTTON struct is a union of bitmap index & separator width
        ptbn->iBitmap = CX_SEPARATOR;
    }
}

UINT TOOLSBANDCLASS::_ProcessExternalButtons(PTBBUTTON ptbb, UINT cButtons)
{
    cButtons = RemoveHiddenButtons(ptbb, cButtons);

    for (UINT i = 0; i < cButtons; i++)
        _PreProcessExternalTBButton(&ptbb[i]);

    return cButtons;
}

void TOOLSBANDCLASS::_GetButtons(IOleCommandTarget* pct, const GUID* pguid, HDSA hdsa)
{
    LONG lCount;
    VARIANTARG v1;
    VariantInit(&v1);
    v1.vt = VT_BYREF | VT_I4;
    v1.plVal = &lCount;

    VARIANTARG v2;
    VariantInit(&v2);
    if (SUCCEEDED(pct->Exec(&IID_IExplorerToolbar, ETCMDID_GETBUTTONS, 0, &v1, &v2)) && v2.vt == VT_BYREF)
    {
        CMDMAPCUSTOMIZE cmc;
        TBBUTTON* pbtn = (TBBUTTON*)v2.byref;

        cmc.cm.guidButtonGroup = *pguid;

        DWORD dwFlags = PPBS_LOOKINTOOLBAR;

        if (!IsEqualGUID(*pguid, CLSID_CommonButtons))
            dwFlags |= PPBS_EXTERNALBUTTON;

        for (long l = 0; l < lCount; l++)
        {
            cmc.btn = pbtn[l];
            if (!(cmc.btn.fsStyle & BTNS_SEP))
            {
                cmc.cm.nCmdID = pbtn[l].idCommand;

                _PreProcessButtonString(&cmc.btn, dwFlags);

                if (FAILED(_ConvertCmd(pguid, cmc.cm.nCmdID, NULL, (UINT*)&cmc.btn.idCommand)))
                {
                    // not already in the toolbar, generate a new id
                    cmc.btn.idCommand = _nNextCommandID++;
                }

                DSA_AppendItem(hdsa, &cmc);
            }
            else
            {
                cmc.btn.dwData = 0;
            }
        }
    }
}

void TOOLSBANDCLASS::_OnEndCustomize()
{

    if (_pcinfo)
    {
        // loop through and make sure that any items added have the appropriate cmdmap
        int i;
        INT_PTR nCount = SendMessage(_hwnd, TB_BUTTONCOUNT, 0, 0L);
        _pcinfo->fAdjust = FALSE;
        for(i = 0; i < nCount; i++)
        {
            CMDMAP* pcm = _GetCmdMapByIndex(i);
            if (!pcm)
            {
                // no command map for this item
                // find the corresponding CMDMAP in our hdsa, clone it and give it to this button.

                // the command id's are the same, so get the toolbar command id, find the corresponding
                // one in the hdsa and clone away.
                TBBUTTONINFO tbbi;
                tbbi.cbSize = sizeof(tbbi);
                tbbi.dwMask = TBIF_COMMAND | TBIF_BYINDEX;
                SendMessage(_hwnd, TB_GETBUTTONINFO, i, (LPARAM)&tbbi);

                int j;
                for (j = 0; j < DSA_GetItemCount(_pcinfo->hdsa); j++)
                {
                    CMDMAPCUSTOMIZE* pcmc = (CMDMAPCUSTOMIZE*)DSA_GetItemPtr(_pcinfo->hdsa, j);
                    ASSERT(pcmc);
                    if (pcmc->btn.idCommand == tbbi.idCommand)
                    {
                        // found it!

                        // clone the cmdmap
                        CMDMAP *pcm = (CMDMAP*)LocalAlloc(LPTR, sizeof(CMDMAP));
                        if (pcm)
                        {
                            *pcm = pcmc->cm;
                            tbbi.lParam = (LPARAM)pcm;
                            tbbi.dwMask = TBIF_LPARAM | TBIF_BYINDEX;
                            SendMessage(_hwnd, TB_SETBUTTONINFO, i, (LPARAM)&tbbi);
                        }
                    }
                }
            }
        }

        if (_pcinfo->fDirty)
            _SaveRestoreToolbar(TRUE);
        
        _FreeCustomizeInfo();

        _RecalcButtonWidths();
        CInternetToolbar* pitbar = IToClass(CInternetToolbar, _btb, this);

#ifdef EDIT_HACK
        pitbar->_InitEditButtonStyle();
#endif
        if (g_fSmallIcons != _UseSmallIcons())
        {
            SendShellIEBroadcastMessage(WM_WININICHANGE, 0, (LPARAM)SZ_REGKEY_SMALLICONS, 3000);

            // Resize the Theater Controls based upon the icon changes.
            IUnknown_Exec( _punkSite, &CGID_Theater, THID_RECALCSIZING, 0, NULL, NULL );
        }

        pitbar->_UpdateToolbar(TRUE);
    }
}

void TOOLSBANDCLASS::_FreeCustomizeInfo()
{
    if (_pcinfo)
    {
        DSA_Destroy(_pcinfo->hdsa);
        _pcinfo->hdsa = NULL;
        LocalFree(_pcinfo);
        _pcinfo = NULL;
    }
}

CMDMAPCUSTOMIZE* TOOLSBANDCLASS::_GetCmdMapCustomize(GUID* pguid, UINT nCmdID)
{
    int j;
    for (j = 0; j < DSA_GetItemCount(_pcinfo->hdsa); j++)
    {
        CMDMAPCUSTOMIZE* pcmc = (CMDMAPCUSTOMIZE*)DSA_GetItemPtr(_pcinfo->hdsa, j);

        if (pcmc->cm.nCmdID == nCmdID &&
            IsEqualGUID(*pguid, pcmc->cm.guidButtonGroup))
        {
            return pcmc;
        }
    }

    return NULL;
}

BOOL TOOLSBANDCLASS::_BuildButtonDSA()
{
    CInternetToolbar* pitbar = IToClass(CInternetToolbar, _btb, this);

    ASSERT(!_pcinfo);
    _pcinfo = (CUSTOMIZEINFO*)LocalAlloc(LPTR, sizeof(CUSTOMIZEINFO));

    if (_pcinfo)
    {
        // build a CMDMAP array of all the buttons available
        _pcinfo->hdsa = DSA_Create(sizeof(CMDMAPCUSTOMIZE), 4);

        if (_pcinfo->hdsa)
        {
            // add the common set (back,forward, stop, refresh, home and search
            _GetButtons(pitbar, &CLSID_CommonButtons, _pcinfo->hdsa);
            _GetButtons(_pctCurrentButtonGroup, &_guidCurrentButtonGroup, _pcinfo->hdsa);
            return TRUE;
        }
        else
        {
            _FreeCustomizeInfo();
            return FALSE;
        }
    }
    return FALSE;
}

void TOOLSBANDCLASS::_UpdateTextSettings(INT_PTR ids)
{
    CInternetToolbar* pitbar = IToClass(CInternetToolbar, _btb, this);

    BOOL fText, fList;

    switch (ids)
    {
    case IDS_TEXTLABELS:
        fList = FALSE;
        fText = TRUE;
        break;

    case IDS_PARTIALTEXT:
        fList = TRUE;
        fText = TRUE;
        break;

    case IDS_NOTEXTLABELS:
        fList = FALSE;  // (but we really don't care)
        fText = FALSE;
        break;

    default:
        ASSERT(0);
        fList = FALSE;
        fText = FALSE;
        break;
    }

    pitbar->_UpdateToolsStyle(fList);

    // (_fCompressed == TRUE means no text labels)
    pitbar->_UpdateToolbarDisplay(UTD_TEXTLABEL, 0, !fText, TRUE);
}

const static DWORD c_aBtnAttrHelpIDs[] = {
    IDC_SHOWTEXT,       IDH_BROWSEUI_TB_TEXTOPTNS,
    IDC_SMALLICONS,     IDH_BROWSEUI_TB_ICONOPTNS,
    0, 0
};

BOOL_PTR CALLBACK TOOLSBANDCLASS::_BtnAttrDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CInternetToolbar* pitbar = (CInternetToolbar*)GetWindowPtr(hDlg, DWLP_USER);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        SetWindowLongPtr(hDlg, DWLP_USER, lParam);  /* LPADJUSTDLGDATA pointer */
        return TRUE;

    case WM_COMMAND:
        if (GET_WM_COMMAND_ID(wParam, lParam) == IDC_SHOWTEXT)
        {
            if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELENDOK ||
                GET_WM_COMMAND_CMD(wParam, lParam) == CBN_CLOSEUP)
            {
                // what'd they pick?
                HWND hwndText = GET_WM_COMMAND_HWND(wParam, lParam);
                INT_PTR iSel = SendMessage(hwndText, CB_GETCURSEL, 0, 0);
                INT_PTR idsSel = SendMessage(hwndText, CB_GETITEMDATA, iSel, 0);

                pitbar->_btb._UpdateTextSettings(idsSel);

                return TRUE;
            }
        }
        break;

    case WM_CONTEXTMENU:
        SHWinHelpOnDemandWrap((HWND) wParam, c_szHelpFile,
            HELP_CONTEXTMENU, (DWORD_PTR)(LPTSTR) c_aBtnAttrHelpIDs);
        return TRUE;

    case WM_HELP:
        SHWinHelpOnDemandWrap((HWND) ((LPHELPINFO) lParam)->hItemHandle, c_szHelpFile,
            HELP_WM_HELP, (DWORD_PTR)(LPTSTR) c_aBtnAttrHelpIDs);
        return TRUE;

    case WM_DESTROY:
        {
#define SZ_YES  TEXT("yes")
#define SZ_NO   TEXT("no")

            HWND hwndIcons = GetDlgItem(hDlg, IDC_SMALLICONS);
            if (TPTR(hwndIcons))
            {
                INT_PTR iSel = SendMessage(hwndIcons, CB_GETCURSEL, 0, 0);
                BOOL fSmallIcons = (SendMessage(hwndIcons, CB_GETITEMDATA, iSel, 0) == IDS_SMALLICONS);

                LPCTSTR szData;
                DWORD cbData;

                if (fSmallIcons)
                {
                    szData = SZ_YES;
                    cbData = sizeof(SZ_YES);
                }
                else
                {
                    szData = SZ_NO;
                    cbData = sizeof(SZ_NO);
                }
                SHRegSetUSValue(SZ_REGKEY_SMALLICONS, SZ_REGVALUE_SMALLICONS, REG_SZ, (void*)szData, cbData, SHREGSET_FORCE_HKCU);
            }
        }
        return TRUE;
    }

    return FALSE;
}

void TOOLSBANDCLASS::_PopulateComboBox(HWND hwnd, const int iResource[], UINT cResources)
{
    TCHAR sz[256];

    // loop through iResource[], load each string resource and insert into combobox
    for (UINT i = 0; i < cResources; i++)
    {
        if (MLLoadString(iResource[i], sz, ARRAYSIZE(sz)))
        {
            INT_PTR iIndex = SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)sz);
            SendMessage(hwnd, CB_SETITEMDATA, iIndex, iResource[i]);
        }
    }
}

void TOOLSBANDCLASS::_SetComboSelection(HWND hwnd, int iCurOption)
{
    INT_PTR cItems = SendMessage(hwnd, CB_GETCOUNT, 0, 0);

    while (cItems--)
    {
        INT_PTR iItemData = SendMessage(hwnd, CB_GETITEMDATA, cItems, 0);

        if (iItemData == iCurOption)
        {
            SendMessage(hwnd, CB_SETCURSEL, cItems, 0);
            break;
        }
        else
        {
            // iCurOption should be in list somewhere;
            // assert that we're not done looking
            ASSERT(cItems);
        }
    }
}

void TOOLSBANDCLASS::_SetDialogSelections(HWND hDlg, BOOL fSmallIcons)
{
    CInternetToolbar* pitbar = IToClass(CInternetToolbar, _btb, this);

    int iCurOption;
    HWND hwnd;

    hwnd = GetDlgItem(hDlg, IDC_SHOWTEXT);

    if (pitbar->_fCompressed)
        iCurOption = IDS_NOTEXTLABELS;
    else if (IS_LIST_STYLE(_hwnd))
        iCurOption = IDS_PARTIALTEXT;
    else
        iCurOption = IDS_TEXTLABELS;

    _SetComboSelection(hwnd, iCurOption);
    if (pitbar->_fTheater)
        SHSetWindowBits(hwnd, GWL_STYLE, WS_DISABLED, WS_DISABLED);

    hwnd = GetDlgItem(hDlg, IDC_SMALLICONS);
    iCurOption = (fSmallIcons ? IDS_SMALLICONS : IDS_LARGEICONS);
    _SetComboSelection(hwnd, iCurOption);
}

static const int c_iTextOptions[] = {
    IDS_TEXTLABELS,
    IDS_PARTIALTEXT,
    IDS_NOTEXTLABELS,
};

static const int c_iIconOptions[] = {
    IDS_SMALLICONS,
    IDS_LARGEICONS,
};

void TOOLSBANDCLASS::_PopulateDialog(HWND hDlg)
{
    HWND hwnd;

    hwnd = GetDlgItem(hDlg, IDC_SHOWTEXT);
    _PopulateComboBox(hwnd, c_iTextOptions, ARRAYSIZE(c_iTextOptions));

    hwnd = GetDlgItem(hDlg, IDC_SMALLICONS);
    _PopulateComboBox(hwnd, c_iIconOptions, ARRAYSIZE(c_iIconOptions));
}

void TOOLSBANDCLASS::_OnBeginCustomize(LPNMTBCUSTOMIZEDLG pnm)
{
    CInternetToolbar* pitbar = IToClass(CInternetToolbar, _btb, this);
    HWND hwnd = (HWND) GetProp(pnm->hDlg, SZ_PROP_CUSTDLG);

    if (!hwnd)
    {
        //
        // hasn't been initialized.
        //
        // we need to check this because this init will be called
        // when the user hits reset as well

        hwnd = CreateDialogParam(MLGetHinst(), MAKEINTRESOURCE(DLG_TEXTICONOPTIONS), pnm->hDlg, _BtnAttrDlgProc, (LPARAM)pitbar);
        if (hwnd)
        {
            // store hwnd of our dialog as property on tb cust dialog
            SetProp(pnm->hDlg, SZ_PROP_CUSTDLG, hwnd);

            // populate dialog controls
            _PopulateDialog(hwnd);

            // initialize dialog control selection states
            _SetDialogSelections(hwnd, g_fSmallIcons);

            RECT rc, rcWnd, rcClient;
            GetWindowRect(pnm->hDlg, &rcWnd);
            GetClientRect(pnm->hDlg, &rcClient);
            GetWindowRect(hwnd, &rc);

            // enlarge tb dialog to make room for our dialog
            SetWindowPos(pnm->hDlg, NULL, rcWnd.left, rcWnd.top + 64, RECTWIDTH(rcWnd), RECTHEIGHT(rcWnd) + RECTHEIGHT(rc), SWP_NOZORDER);

            // position our dialog at the bottom of the tb dialog
            SetWindowPos(hwnd, HWND_TOP, rcClient.left, rcClient.bottom, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
        }
    }

    if (_BuildButtonDSA())
    {
        _pcinfo->fAdjust = TRUE;
    }
}

class CBitmapPreload : public IRunnableTask
{
public:
    STDMETHOD ( QueryInterface ) ( REFIID riid, void ** ppvObj );
    STDMETHOD_( ULONG, AddRef ) ();
    STDMETHOD_( ULONG, Release ) ();

    STDMETHOD (Run)( void );
    STDMETHOD (Kill)( BOOL fWait );
    STDMETHOD (Suspend)( );
    STDMETHOD (Resume)( );
    STDMETHOD_( ULONG, IsRunning )( void );

protected:
    friend HRESULT CBitmapPreload_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi);

    CBitmapPreload();
    ~CBitmapPreload();

    LONG            m_cRef;
    LONG            m_lState;
};

STDAPI CBitmapPreload_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
{
    // aggregation checking and *ppunk zeroing are handled in class factory
    ASSERT(pUnkOuter == NULL);

    CBitmapPreload* pbp = new CBitmapPreload();

    if (pbp)
    {
        *ppunk = SAFECAST(pbp, IRunnableTask*);
        return S_OK;
    }
    else
    {
        *ppunk = NULL; // redundant but doesn't hurt
        return E_OUTOFMEMORY;
    }
}


CBitmapPreload::CBitmapPreload() : m_cRef(1)
{
    m_lState = IRTIR_TASK_NOT_RUNNING;
}


CBitmapPreload::~CBitmapPreload()
{
}


STDMETHODIMP CBitmapPreload::QueryInterface (REFIID riid, void ** ppv)
{
    static const QITAB qit[] = {
        QITABENT(CBitmapPreload, IRunnableTask),
        { 0 },
    };
    return QISearch(this, qit, riid, ppv);
}


STDMETHODIMP_( ULONG ) CBitmapPreload:: AddRef ()
{
    return InterlockedIncrement( &m_cRef );
}

STDMETHODIMP_( ULONG ) CBitmapPreload:: Release ()
{
    if (InterlockedDecrement( &m_cRef ) == 0 )
    {
        delete this;
        return 0;
    }
    return m_cRef;
}

STDMETHODIMP CBitmapPreload::Run ( void )
{
    if ( m_lState != IRTIR_TASK_NOT_RUNNING )
    {
        return E_FAIL;
    }

    InterlockedExchange( &m_lState, IRTIR_TASK_RUNNING );

    CInternetToolbar_Preload( );

    InterlockedExchange( &m_lState, IRTIR_TASK_FINISHED );

    return NOERROR;
}


STDMETHODIMP CBitmapPreload::Kill ( BOOL fWait )
{
    return E_NOTIMPL;
}


STDMETHODIMP CBitmapPreload::Suspend ( )
{
    return E_NOTIMPL;
}


STDMETHODIMP CBitmapPreload::Resume ( )
{
    return E_NOTIMPL;
}


STDMETHODIMP_( ULONG ) CBitmapPreload:: IsRunning ( void )
{
    return m_lState;
}



#ifdef EDIT_HACK

//+-------------------------------------------------------------------------
//  Constructor
//--------------------------------------------------------------------------
CInternetToolbar::CEditVerb::CEditVerb()
{
    ASSERT(_nElements == 0);
    ASSERT(_nDefault == 0);
    ASSERT(_pVerb == NULL);
    ASSERT(_lpfnOldWndProc == NULL);
    ASSERT(_pszDefaultEditor == NULL);
    ASSERT(_fInitEditor == FALSE);
}

//+-------------------------------------------------------------------------
//  Destructor
//--------------------------------------------------------------------------
CInternetToolbar::CEditVerb::~CEditVerb()
{
    if (_pVerb) RemoveAll();
    SetStr(&_pszDefaultEditor, NULL);
}

//+-------------------------------------------------------------------------
// Removes all cached edit verbs and associated memory
//--------------------------------------------------------------------------
void CInternetToolbar::CEditVerb::RemoveAll()
{
    if (_nElements > 0)
    {
        for (UINT i=0; i < _nElements; ++i)
        {
            EDITVERB& rVerb = _pVerb[i];

            SetStr(&rVerb.pszDesc, NULL);
            SetStr(&rVerb.pszMenuText, NULL);
            SetStr(&rVerb.pszExe, NULL);
            if (rVerb.hkeyProgID)
            {
                RegCloseKey(rVerb.hkeyProgID);
            }
            _ClearMSAAMenuInfo(rVerb);
        }

        LocalFree(_pVerb);

        _pVerb = NULL;
        _nElements = 0;
        _nDefault = 0;
    }
}

void _AddToOpenWithList(HKEY hkeyProgid, LPCWSTR pszVerb, LPCWSTR pszFileExt)
{
    ASSERT(hkeyProgid);
    ASSERT(pszVerb);
    ASSERT(pszFileExt);

    // First get the name of the exe
    WCHAR szPath[MAX_PATH];

    if (SUCCEEDED(AssocQueryStringByKey(ASSOCF_VERIFY, ASSOCSTR_EXECUTABLE, hkeyProgid,
        pszVerb, szPath, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(szPath)))))
    {
        // Now see if it is in the openwith list for the given file extension
        LPCWSTR pszExe = PathFindFileName(szPath);

        WCHAR szKey[MAX_PATH];
        wnsprintf(szKey, ARRAYSIZE(szKey), L"%s\\OpenWithList\\%s", pszFileExt, pszExe);
        HKEY hkey;

        DWORD dwDisp;
        if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CLASSES_ROOT, szKey, 0, L"", REG_OPTION_NON_VOLATILE,
                                      KEY_READ | KEY_WRITE, NULL, &hkey, &dwDisp))
        {
            // If we create a new key, we then need to check that verb is registered
            // for this app
            if (dwDisp == REG_CREATED_NEW_KEY)
            {
                AssocMakeApplicationByKey(ASSOCMAKEF_VERIFY, hkeyProgid, pszVerb);
            }
            RegCloseKey(hkey);
        }
    }
}

//+-------------------------------------------------------------------------
// Check registry for a default mhtml editor.  If a new editor is detected,
// it is added to the mhtml openwith list.
//--------------------------------------------------------------------------
void CInternetToolbar::CEditVerb::_InitDefaultMHTMLEditor()
{
    //
    // Check for a default MHTML editor.
    //
    HKEY hkeyEdit = NULL;
    if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_PATH_DEFAULT_MHTML_EDITOR, 0, KEY_READ | KEY_WRITE, &hkeyEdit))
    {
        // Migrate hklm setting to hkcu
        HKEY hkeySrc;
        if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_DEFAULT_MHTML_EDITOR, 0, KEY_READ, &hkeySrc))
        {
            HKEY hkeyDest;
            if (ERROR_SUCCESS == RegCreateKey(HKEY_CURRENT_USER, REGSTR_PATH_DEFAULT_MHTML_EDITOR, &hkeyDest))
            {
                SHCopyKey(hkeySrc, NULL, hkeyDest, 0);
                hkeyEdit = hkeyDest;
            }
            RegCloseKey(hkeySrc);
        }
    }

    if (hkeyEdit)
    {
        // If the mhtml editor has changed, copy it into the mhtml openwithlist
        DWORD dwType;
        WCHAR szCurrent[MAX_PATH];
        DWORD cb = sizeof(szCurrent);
        if (ERROR_SUCCESS == SHGetValue(hkeyEdit, L"shell\\edit\\command", NULL, &dwType, szCurrent, &cb) &&
            dwType == REG_SZ)
        {
            WCHAR szLast[MAX_PATH];
            DWORD cb = sizeof(szLast);
            if (ERROR_SUCCESS != SHGetValue(hkeyEdit, NULL, L"Last", &dwType, szLast, &cb) ||
                (dwType == REG_SZ && StrCmp(szLast, szCurrent) != 0))
            {
                // Copy the MHTML editor into our MHTML openwithlist
                _AddToOpenWithList(hkeyEdit, L"edit", L".mhtml");

                // Remember that we migrated this key. Copying to the openwithlist can be slow
                // because we need to hit the disk to verify the exe name. So it's worth the effort
                // to avoid doing this unecessarily.
                SHSetValue(hkeyEdit, NULL, L"Last", REG_SZ, szCurrent, CbFromCch(lstrlen(szCurrent) +1));
            }
        }

        RegCloseKey(hkeyEdit);
    }
}

//+-------------------------------------------------------------------------
// Check registry for the friendly name of the default html editor.  This
// editor is configured by inetcpl or by office 2000.  If necessary, the
// associated verb is moved to the OpenWithList for .htm files.
//--------------------------------------------------------------------------
void CInternetToolbar::CEditVerb::InitDefaultEditor(HKEY hkey)
{
    //
    // First see if the default editor is in HKCU
    //
    HKEY hkeyEdit = hkey;
    if (hkey ||
        ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_PATH_DEFAULT_HTML_EDITOR,
                                      0, KEY_READ | KEY_WRITE, &hkeyEdit))
    {
        //
        // See if we have a default editor selected
        //
        WCHAR szBuf[MAX_PATH];
        DWORD cbBuf = sizeof(szBuf);
        if (ERROR_SUCCESS == SHGetValue(hkeyEdit, NULL, L"Description", NULL, szBuf, &cbBuf))
        {
            // We got it!  Save the friendly name.
            PathRemoveBlanks(szBuf);
            SetStr(&_pszDefaultEditor, szBuf);
        }
        else
        {
            // No default editor description, so check to see if an edit verb was added.
            // (Office/inetcpl deletes the description key to signal to us that something changed).
            IQueryAssociations *pqa;

            if (SUCCEEDED(AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa))))
            {
                if (SUCCEEDED(pqa->Init(0, NULL, hkeyEdit, NULL)) &&
                ( SUCCEEDED(pqa->GetString(ASSOCF_VERIFY, ASSOCSTR_FRIENDLYAPPNAME, L"edit", szBuf, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(szBuf))))
                || SUCCEEDED(pqa->GetString(ASSOCF_VERIFY, ASSOCSTR_FRIENDLYAPPNAME, NULL, szBuf, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(szBuf))))))
                {
                    PathRemoveBlanks(szBuf);

                    // Save the name of the default editor
                    SetStr(&_pszDefaultEditor, szBuf);
                    SHSetValue(hkeyEdit, NULL, L"Description", REG_SZ, szBuf, CbFromCch(lstrlen(szBuf) +1));

                    // Add it to our openwithlist for .htm files
                    _AddToOpenWithList(hkeyEdit, L"edit", L".htm");
                }

                pqa->Release();
            }
        }

        // Close the key if it wasn't passed in
        if (hkeyEdit && NULL == hkey)
        {
            RegCloseKey(hkeyEdit);
        }
    }

    // During setup, Office places the orginial edit verb in HKLM. We need to copy this to HKCU.
    else if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_DEFAULT_HTML_EDITOR, 0, KEY_READ, &hkeyEdit))
    {
        // Migrate this key into HKCU
        HKEY hkeyDest;
        if (ERROR_SUCCESS == RegCreateKey(HKEY_CURRENT_USER, REGSTR_PATH_DEFAULT_HTML_EDITOR, &hkeyDest))
        {
            SHCopyKey(hkeyEdit, NULL, hkeyDest, 0);

            // Try again
            InitDefaultEditor(hkeyDest);
            RegCloseKey(hkeyDest);
        }
        RegCloseKey(hkeyEdit);
    }

    //
    // Check for a default MHTML editor.
    //
    if (hkey == NULL)   // Don't do on recursion
    {
        _InitDefaultMHTMLEditor();
    }
}

BOOL _GetAppKey(LPCWSTR pszApp, HKEY *phkApp)
{
    ASSERT(pszApp && *pszApp);
    WCHAR szKey[MAX_PATH];
    StrCpy(szKey, L"Applications\\");
    StrCatBuff(szKey, pszApp, SIZECHARS(szKey));

    return (NOERROR == RegOpenKeyEx(
        HKEY_CLASSES_ROOT,
        szKey,
        0L,
        MAXIMUM_ALLOWED,
        phkApp));
}

//+-------------------------------------------------------------------------
// Make sure that notepad is registered in the OpenWithList for .htm files.
// This is called when this dll is registered (at setup time)
//--------------------------------------------------------------------------
void AddNotepadToOpenWithList()
{
    // Add notepad to the openwith list for .htm files
    HKEY hkeyOpenWith;
    DWORD dwDisp;
    if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CLASSES_ROOT, L".htm\\OpenWithList\\notepad.exe", 0, L"",
                                        REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hkeyOpenWith, &dwDisp))
    {
        // Add the edit verb for notepad
//        if (g_fRunningOnNT) // didn't work on nt4
//        {
//            const WCHAR szPath[] = L"%SystemRoot%\\notepad.exe %1";
//            SHSetValue(HKEY_CLASSES_ROOT, L"Applications\\notepad.exe\\shell\\edit\\command", NULL, REG_EXPAND_SZ, szPath, sizeof(szPath));
//        }
//        else
        {
            WCHAR szPath[MAX_PATH];
            GetWindowsDirectory(szPath, ARRAYSIZE(szPath));
            PathAddBackslash(szPath);
            StrCatBuff(szPath, L"notepad.exe %1", ARRAYSIZE(szPath));
            SHSetValue(HKEY_CLASSES_ROOT, L"Applications\\notepad.exe\\shell\\edit\\command", NULL, REG_SZ, szPath, (lstrlen(szPath)+1) * sizeof(WCHAR));
        }

        // Add a localizable name for the edit verb
        TCHAR szEditVerb[MAX_PATH];
        int cch = MLLoadShellLangString(IDS_EDITVERB, szEditVerb, ARRAYSIZE(szEditVerb));
        if (cch > 0)
        {
            SHSetValue(HKEY_CLASSES_ROOT, L"Applications\\notepad.exe\\shell\\edit", NULL, REG_SZ, szEditVerb, (cch + 1) * sizeof(WCHAR));
        }

        RegCloseKey(hkeyOpenWith);
    }
}

//+-------------------------------------------------------------------------
// Returns the friendly name of the default HTML editor
//--------------------------------------------------------------------------
LPCTSTR CInternetToolbar::CEditVerb::_GetDefaultEditor()
{
    // Do a lazy init of the default editor
    if (!_fInitEditor)
    {
        InitDefaultEditor();
        _fInitEditor = TRUE;
    }
    return _pszDefaultEditor;
}

//+-------------------------------------------------------------------------
// Gets the path of the exe associated with the verb and stores the
// result in rVerb.  The caller is responsible for freeing the string
// returned.
//--------------------------------------------------------------------------
LPCTSTR CInternetToolbar::CEditVerb::_GetExePath(EDITVERB& rVerb)
{
    // If we already have the path, simply return it
    if (NULL == rVerb.pszExe)
    {
        ASSERT(rVerb.hkeyProgID);
        TCHAR sz[MAX_PATH];
        if (SUCCEEDED(AssocQueryStringByKey(ASSOCF_VERIFY, ASSOCSTR_EXECUTABLE, rVerb.hkeyProgID,
            rVerb.fUseOpenVerb ? NULL : L"edit", sz, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(sz)))))
            rVerb.pszExe = StrDup(sz);
    }

    return rVerb.pszExe;
}

//+-------------------------------------------------------------------------
// Returns TRUE if path of the exe associated with the verb is not found in
// any of the existing verbs.
//--------------------------------------------------------------------------
BOOL CInternetToolbar::CEditVerb::_IsUnique(EDITVERB& rNewVerb)
{
    // Get the friendly name of the new element
    LPCTSTR pszNewDesc = _GetDescription(rNewVerb);
    if (NULL == pszNewDesc)
    {
        // Executable must not exist
        return FALSE;
    }

    // Scan existing elements for the same executable
    for (UINT i=0; i < _nElements; ++i)
    {
        LPCTSTR pszDesc = _GetDescription(_pVerb[i]);
        if (pszDesc && (StrCmpI(pszNewDesc, pszDesc) == 0))
        {
            // Match found, so free the friendly name for the new verb
            SetStr(&rNewVerb.pszDesc, NULL);

            // If the new item shows its icon on the button, make the duplicate
            // do the same.
            if (rNewVerb.fShowIcon)
            {
                _pVerb[i].fShowIcon = TRUE;
                _nDefault = i;
            }
            return FALSE;
        }
    }

    return TRUE;
}

//+-------------------------------------------------------------------------
// Some programs such as msothmed.exe act as stubs that redirect the edit
// command to the appropriate executable. This function returns true if
// the path contains the name of a known stub.
//--------------------------------------------------------------------------
BOOL CInternetToolbar::CEditVerb::_IsHtmlStub
(
    LPCWSTR pszPath
)
{
    BOOL fRet = FALSE;

    // Get the MULTISZ list of known redirectors
    TCHAR szRedir[MAX_PATH];
    ZeroInit(szRedir, ARRAYSIZE(szRedir)); // Protect against non-multisz strings in the reg
    DWORD dwType;
    DWORD cb = sizeof(szRedir) - 4;
    if (ERROR_SUCCESS != SHGetValue(HKEY_LOCAL_MACHINE, REGSTR_PATH_DEFAULT_HTML_EDITOR, L"Stubs", &dwType, szRedir, &cb))
    {
        // Nothing in registry, so default to ignore the Office redirector
        StrCpyN(szRedir, L"msohtmed.exe\0", ARRAYSIZE(szRedir));
    }

    // See if the path contains the name of a redirectors
    // Note that PathFindFileName doesn't work well for pathes with parameters so we just
    // check for the exe name in the path)
    for (LPTSTR p = szRedir; *p != NULL; p += lstrlen(p) + 1)
    {
        if (StrStrI(pszPath, p))
        {
            fRet = TRUE;
            break;
        }
    }
    return fRet;
}

//+-------------------------------------------------------------------------
//  Adds a new edit verb.  Returns a pointer to the new verb if it
//  successfully added.
//--------------------------------------------------------------------------
CInternetToolbar::CEditVerb::EDITVERB* CInternetToolbar::CEditVerb::_Add
(
    HKEY hkeyProgID,        // location of of verb
    BOOL fPermitOpenVerb,   // permit open as well as edit verb
    BOOL fCheckForOfficeApp,// redirect to office app
    BOOL fShowIcon          // if button face icon should be customized
)
{
    EDITVERB* pNewVerb = NULL;

    if (hkeyProgID)
    {
        BOOL fUseOpenVerb = FALSE;

        //
        // See if an appropriate verb exists.
        //
        TCHAR szCommand[MAX_PATH];
        HRESULT hr = AssocQueryStringByKey(0, ASSOCSTR_COMMAND, hkeyProgID, L"edit", szCommand, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(szCommand)));
        if (FAILED(hr) && fPermitOpenVerb)
        {
            hr = AssocQueryStringByKey(0, ASSOCSTR_COMMAND, hkeyProgID, NULL, szCommand, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(szCommand)));
            if (SUCCEEDED(hr))
            {
                fUseOpenVerb = TRUE;
            }
        }

        // If no verb or if this is the office redirector, ignore this progid
        // Otherwise we can get two entries that do the same thing.
        if (FAILED(hr) || _IsHtmlStub(szCommand))
        {
            RegCloseKey(hkeyProgID);
            return NULL;
        }

        if (fCheckForOfficeApp)
        {
            ASSERT(*szCommand);

            //
            // HACK: Office2000 needs us to call a special proxy to get around thier DDE bugs and
            // to check the HTML document for the name of the original document.  These problems
            // should be fixed in the apps themselves.
            //
            // So if this is an office app, we will redirect to the appropriate progid. Note that
            // we don't need to do this if a progid was from the html meta tag because this progid
            // already supports the proxy.
            //
            struct OfficeHackery {LPCWSTR pszApp; LPCWSTR pszProgID;};

            // Must not have been a progid passed in.
            static const OfficeHackery exeToProgID[] =
            {
                {L"winword",   L"Word.Document"},
                {L"excel",     L"Excel.Sheet"},
                {L"powerpnt",  L"PowerPoint.Slide"},
                {L"msaccess",  L"Access.Application"},
                {L"frontpg",   L"FrontPage.Editor.Document"},
            };

            for (int i=0; i < ARRAYSIZE(exeToProgID); ++i)
            {
                if (StrStrI(szCommand, exeToProgID[i].pszApp))
                {
                    // Match found!
                    HKEY hkeyOffice = NULL;
                    if (SUCCEEDED(AssocQueryKey(0, ASSOCKEY_SHELLEXECCLASS, exeToProgID[i].pszProgID, NULL, &hkeyOffice)))
                    {
                        // Redirect to the office progid
                        RegCloseKey(hkeyProgID);
                        hkeyProgID = hkeyOffice;

                        // The office apps always use the open verb
                        fUseOpenVerb = TRUE;

                        // The icon is shown on the button face for office apps
                        fShowIcon = TRUE;
                    }
                    break;
                }
            }
        }

        EDITVERB newVerb = {0};
        newVerb.hkeyProgID = hkeyProgID;
        newVerb.fUseOpenVerb = fUseOpenVerb;
        newVerb.fShowIcon = fShowIcon;

        // Ignore it if we have another verb to the same exe.
        if (!_IsUnique(newVerb))
        {
            RegCloseKey(hkeyProgID);
        }
        else
        {
            EDITVERB* pVerbsNew;
            if (_pVerb == NULL)
            {
                pVerbsNew = (EDITVERB*)LocalAlloc(LPTR, sizeof(EDITVERB));
            }
            else
            {
                pVerbsNew = (EDITVERB*)LocalReAlloc(_pVerb, (_nElements+1) * sizeof(EDITVERB), LMEM_MOVEABLE | LMEM_ZEROINIT);
            }

            if (pVerbsNew == NULL)
            {
                RegCloseKey(hkeyProgID);
            }
            else
            {
                _pVerb = pVerbsNew;
                pNewVerb = &_pVerb[_nElements];
                *pNewVerb = newVerb;

                //
                // If the description of the executable matches that of the default editor, make
                // it our default edit verb.  If we are not checking for the office app, we
                // can assume that this verb was from a progid in an html file and we will also
                // make it our default.
                //
                LPCWSTR pszDefDesc = _GetDefaultEditor();
                LPCWSTR pszNewDesc = _GetDescription(*pNewVerb);

                if (!fCheckForOfficeApp ||
                    (pszDefDesc && pszNewDesc && StrCmp(pszDefDesc, pNewVerb->pszDesc) == 0))
                {
                    _nDefault = _nElements;
                }

                ++_nElements;
            }
        }
    }

    return pNewVerb;
}

//+-------------------------------------------------------------------------
//  Adds a new edit verb.  Returns TRUE if a verb was successfully added.
//--------------------------------------------------------------------------
BOOL CInternetToolbar::CEditVerb::Add
(
    LPTSTR pszProgID    // program id or file extension associated with verb
)
{
    ASSERT(pszProgID);

    BOOL fRet = FALSE;
    BOOL fFileExt = (pszProgID[0] == TEXT('.'));

    //
    // Open the associated reg key and try to add it to our list of verbs
    //
    BOOL fUseOpenVerb = FALSE;
    HKEY hkeyProgID = NULL;
    BOOL fPermitOpenVerb = !fFileExt;
    BOOL fShowIcon = !fFileExt;    // If a progid was passed in, we will show the icon on the button face

    if (SUCCEEDED(AssocQueryKey(0, ASSOCKEY_SHELLEXECCLASS, pszProgID, NULL, &hkeyProgID)))
    {
        EDITVERB* pNewVerb = _Add(hkeyProgID, fPermitOpenVerb, fFileExt, fShowIcon);
        if (pNewVerb)
        {
            fRet = TRUE;
        }
    }

    //
    // If a file extension was passed in, we also add the alternative editors from the
    // OpenWithList
    //
    if (fFileExt)
    {
        WCHAR szOpenWith[MAX_PATH];
        StrCpyN(szOpenWith, pszProgID, ARRAYSIZE(szOpenWith));
        StrCatBuff(szOpenWith, L"\\OpenWithList", ARRAYSIZE(szOpenWith));

        HKEY hkeyOpenWithList;

        // See if there is an OpenWithList
        if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, szOpenWith, 0, KEY_READ, &hkeyOpenWithList))
        {
            DWORD dwIndex = 0;
            DWORD dwSize = ARRAYSIZE(szOpenWith);
            HKEY hkeyOpenWith = NULL;
            while (ERROR_SUCCESS == RegEnumKeyEx(hkeyOpenWithList, dwIndex, szOpenWith, &dwSize, NULL, NULL, NULL, NULL))
            {
                if (_GetAppKey(szOpenWith, &hkeyOpenWith))
                {
                    // We only permit the edit verbs from here
                    EDITVERB* pNewVerb = _Add(hkeyOpenWith, FALSE, TRUE, FALSE);
                    if (pNewVerb)
                    {
                        fRet = TRUE;
                    }

                    ++dwIndex;

                    // Note that we don't close hkeyOpenWith here.  It is either closed if it was not added, or
                    // it will be closed later.
                }
                else
                {
                    // Invalid entry, so try to fix it (may be old format):
                    //
                    // In IE5.0 we use to store the friendly names of apps in the openwithlist.  For shell compatibility, we need
                    // to convert these entries to store the exe name instead:
                    //
                    //  ----> permanent entries are stored un HKCR
                    //  HKCR
                    //     \.Ext
                    //         \OpenWithList
                    //             \app.exe
                    //
                    //  ----> and applications or the system can write app association here
                    //     \Applications
                    //         \APP.EXE
                    //             \shell...
                    //         \foo.exe
                    //             \shell...
                    //
                    if (ERROR_SUCCESS == RegOpenKeyEx(hkeyOpenWithList, szOpenWith, 0, KEY_READ, &hkeyOpenWith))
                    {
                        _AddToOpenWithList(hkeyOpenWith, L"edit", L".htm");

                        // Remove the invalid entry
                        if (ERROR_SUCCESS != SHDeleteKey(hkeyOpenWith, L""))
                        {
                            // NOTE (andrewgu): ie5.5 b#108551 - on locked-down nt5 this will fail
                            // and if dwIndex is not incremented, will result in an infinite loop.
                            dwIndex++;
                        }

                        RegCloseKey(hkeyOpenWith);
                    }
                }
                dwSize = ARRAYSIZE(szOpenWith);
            }

            RegCloseKey(hkeyOpenWithList);
        }

        //
        // If a ".htm" or ".html" was passed in, add our default html editor
        //
        if ((StrCmpI(pszProgID, L".htm") == 0 || StrCmpI(pszProgID, L".html") == 0) &&
            _GetDefaultEditor())
        {
            HKEY hkeyDefault;
            if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_PATH_DEFAULT_HTML_EDITOR, 0, KEY_READ, &hkeyDefault))
            {
                if (_Add(hkeyDefault, TRUE, TRUE, FALSE))
                {
                    fRet = TRUE;
                }
            }
        }
    }

    return fRet;
}

//+-------------------------------------------------------------------------
// Returns the tooltip for the default edit verb
//--------------------------------------------------------------------------
BOOL CInternetToolbar::CEditVerb::GetToolTip
(
    LPTSTR pszToolTip,
    UINT cchMax,
    BOOL fStripAmpersands
)
{
    if (_nElements == 0)
    {
        return FALSE;
    }

    // Use the menu text for the tooltip.
    _FormatMenuText(_nDefault);

    // Copy text stripping out any ampersands
    LPWSTR pszDest = pszToolTip;
    LPWSTR pszSrc = _GetVerb(_nDefault).pszMenuText;
    if (0 < cchMax)
    {
        // Leave room for the null terminator
        while (0 < --cchMax)
        {
            // strip out '&'
            if (fStripAmpersands)
            {
                while (*pszSrc == L'&')
                {
                    ++pszSrc;
                }
            }

            if ( !(*pszDest++ = *pszSrc++) )
            {
                --pszDest;
                break;
            }
        }

        if (0 == cchMax)
            *pszDest = L'\0';

        ASSERT(*pszDest == 0);

        //
        // In some locals, the accelerator is identified in brackets at the
        // end of the string, so if we strip ampersands, we strip these too.
        //
        if (fStripAmpersands && --pszDest >= pszToolTip && *pszDest == L')')
        {
            while (--pszDest >= pszToolTip)
            {
                if (*pszDest == L'(')
                {
                    *pszDest = L'\0';
                    break;
                }
            }
        }
    }

    return TRUE;
}

//+-------------------------------------------------------------------------
// "Lazy-fetches" the verb info, and returns the desired info.
//--------------------------------------------------------------------------
CInternetToolbar::CEditVerb::EDITVERB& CInternetToolbar::CEditVerb::_GetVerb(UINT nIndex)
{
    ASSERT(nIndex < _nElements);

    // We fetch the info when first asked for it.
    if (!_pVerb[nIndex].fInit)
    {
        _FetchInfo(nIndex);
        _pVerb[nIndex].fInit = TRUE;
    }
    return _pVerb[nIndex];
}

//+-------------------------------------------------------------------------
// Gets the name of the app associated with the verb.
//--------------------------------------------------------------------------
LPCTSTR CInternetToolbar::CEditVerb::_GetDescription(EDITVERB& rVerb)
{
    // If we already have a description, we are done
    if (NULL == rVerb.pszDesc)
    {
        ASSERT(rVerb.hkeyProgID);

        TCHAR sz[MAX_PATH];
        if (SUCCEEDED(AssocQueryStringByKey(ASSOCF_VERIFY, ASSOCSTR_FRIENDLYAPPNAME, rVerb.hkeyProgID,
            rVerb.fUseOpenVerb ? NULL : L"edit", sz, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(sz)))))
        {
            rVerb.pszDesc = StrDup(sz);
            if (rVerb.pszDesc)
            {
                // Remove preceeding and trailing blanks
                PathRemoveBlanks(rVerb.pszDesc);
            }
        }
    }

    return rVerb.pszDesc;
}

//+-------------------------------------------------------------------------
// Reads the info associated with the progid at the given index.  This
// function allows us to do a lazy fetch of the info when requested.
//--------------------------------------------------------------------------
void CInternetToolbar::CEditVerb::_FetchInfo(UINT nIndex)
{
    ASSERT(nIndex < _nElements);
    ASSERT(_pVerb[nIndex].hkeyProgID != NULL);

    EDITVERB& rVerb = _pVerb[nIndex];

    //
    // Get the path to the edit verb's exe
    //
    if (_GetExePath(rVerb))
    {
        ASSERT(rVerb.pszExe);

        // Note that we fetched the friendly name earlier
        ASSERT(rVerb.pszDesc);

        // Now get the icon
        rVerb.iIcon = Shell_GetCachedImageIndex(rVerb.pszExe, 0, 0);
    }
    else
    {
        rVerb.iIcon = -1;
    }
}

//+-------------------------------------------------------------------------
// SetMSAAMenuInfo()
//
// Fills in MSAAMenuInfo part of EDITVERB from the other fields of the rVerb
//--------------------------------------------------------------------------

void CInternetToolbar::CEditVerb::_SetMSAAMenuInfo( EDITVERB& rVerb )
{

#ifdef UNICODE

    // If we're UNICODE, we can just refer to the m_pName of the MenuEntry itself...
    rVerb.m_MSAA.m_CharLen = lstrlen( rVerb.pszMenuText );
    rVerb.m_MSAA.m_pWStr = rVerb.pszMenuText;

#else // UNICODE

    // If we're ANSI, need to create a UNICODE string for the MSAA text...

    // Call MultiByteToWideChar first with 0 to get the size needed. (Assumes
    // m_pName is ASCII NUL terminated - cChars will include the terminating NUL)
    int cChars = MultiByteToWideChar( CP_ACP, 0, rVerb.pszMenuText, -1, NULL, 0 );

    // Don't want to include NUL in character length, so subtract one...
    rVerb.m_MSAA.m_CharLen = cChars - 1;

    // Now call MultiByteToWideChar to do the conversion.
    // MultiByteToWideChar adds the terminating WIDE-NUL for us, so we don't have to
    // add it explicitly...
    rVerb.m_MSAA.m_pWStr = new WCHAR [ cChars ];
    if (rVerb.m_MSAA.m_pWStr)
    {
        // Note - we don't delete[] the above allocated memory in this sample
        // code because we know that in this case it will be reclaimed by the system on
        // exit and won't give a leak.
        MultiByteToWideChar( CP_ACP, 0, rVerb.pszMenuText, -1, rVerb.m_MSAA.m_pWStr, cChars );
    }

#endif // UNICODE

    // Finally, add MSAAINFO signature...
    rVerb.m_MSAA.m_MSAASig = MSAA_MENU_SIG;
}


//+-------------------------------------------------------------------------
// ClearMSAAMenuInfo()
//
// Clean up MSAAMenuInfo - specifically, release the allocated
// UNICODE string, if appropriate...
//--------------------------------------------------------------------------

void CInternetToolbar::CEditVerb::_ClearMSAAMenuInfo( EDITVERB& rVerb )
{
    // Paranoia - clear signature...
    rVerb.m_MSAA.m_MSAASig = 0;

#ifdef UNICODE

    // We're unicode - nothing to do, since we didn't allocate anything.

#else // UNICODE

    // We're ANSI - release allocated UNICODE string...
    delete [] rVerb.m_MSAA.m_pWStr;

#endif // UNICODE
}


//+-------------------------------------------------------------------------
// Shows the edit pop-up menu.
//--------------------------------------------------------------------------
BOOL CInternetToolbar::CEditVerb::ShowEditMenu(POINT pt, HWND hwnd, LPTSTR pszURL)
{
    BOOL  bRet  = FALSE;
    HMENU hmEdit = CreatePopupMenu();

    if (hmEdit)
    {
        UINT idCmd = FCIDM_EDITFIRST;
        UINT nMax = FCIDM_EDITLAST - FCIDM_EDITFIRST;

        // Add each verb to the menu
        for (UINT i=0; i<_nElements && i < nMax; ++i)
        {
            EDITVERB& rVerb = _GetVerb(i);
            _FormatMenuText(i);
            rVerb.idCmd = idCmd;
            AppendMenu(hmEdit, MF_OWNERDRAW, idCmd, (LPCTSTR) &rVerb );

            // Fix up MSAAMenuInfo part...
            _SetMSAAMenuInfo( rVerb );

            ++idCmd;
        }

        // Temporarily subclass the hwnd to intercept the owner-draw messages
        if (SetProp(hwnd, SZ_EDITVERB_PROP, this))
        {
            ASSERT(!_lpfnOldWndProc);
            _lpfnOldWndProc = (WNDPROC) SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) _WndProc);

            idCmd = ITBar_TrackPopupMenuEx(hmEdit, TPM_RETURNCMD, pt.x, pt.y, hwnd, NULL);

            SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)_lpfnOldWndProc);
            _lpfnOldWndProc = NULL;
            RemoveProp(hwnd, SZ_EDITVERB_PROP);

            if (InRange(idCmd, FCIDM_EDITFIRST, FCIDM_EDITLAST))
            {
                // Execute the selected edit verb
                _Edit(pszURL, idCmd - FCIDM_EDITFIRST);
            }
        }

        DestroyMenu(hmEdit);
    }

    return bRet;
}

//+-------------------------------------------------------------------------
// Creates a menu string from the progid's description
//--------------------------------------------------------------------------
void CInternetToolbar::CEditVerb::_FormatMenuText(UINT nIndex)
{
    ASSERT(nIndex < _nElements);

    EDITVERB& rVerb = _GetVerb(nIndex);
    if (rVerb.pszMenuText == NULL)
    {
        if (_GetDescription(rVerb))
        {
            TCHAR szFormat[100];
            TCHAR szMenuText[200];

            MLLoadString(IDS_EDITWITH, szFormat, ARRAYSIZE(szFormat));
            wnsprintf(szMenuText, ARRAYSIZE(szMenuText), szFormat, rVerb.pszDesc);
            SetStr(&((EDITVERB&)rVerb).pszMenuText, szMenuText);
        }
        else
        {
            // Things are really messed up
            ASSERT(FALSE);
            SetStr(&((EDITVERB&)rVerb).pszMenuText, TEXT(""));
        }
    }
}


//+-------------------------------------------------------------------------
// Executes the edit verb indicated by nIndex.
//--------------------------------------------------------------------------
void CInternetToolbar::CEditVerb::_Edit
(
    LPCTSTR pszURL,     // url assocated with the verb
    UINT nIndex         // verb to execute
)
{
    ASSERT(pszURL);

    if (nIndex >= _nElements)
    {
        return;
    }

    EDITVERB& rVerb = _pVerb[nIndex];
    int fMask = SEE_MASK_CLASSKEY;

    SHELLEXECUTEINFO sei = {0};

    TCHAR szCacheFileName[MAX_PATH + MAX_URL_STRING + 2];
    memset(szCacheFileName, 0, sizeof(szCacheFileName));

    if (PathIsURL(pszURL))
    {
        // We pass the url if the app has register that it wants this
        if ((WhichPlatform() == PLATFORM_BROWSERONLY) && DoesAppWantUrl(rVerb.pszExe))
        {
            //
            // Old versions of shell32 (PLATFORM_BROWSERONLY) ignore the SEE_MASK_FILEANDURL
            // flag, so on these platforms we check ourselves to see if the app
            // wants the url instead of the cache file name.
            //
            StrCpyN(szCacheFileName, pszURL, ARRAYSIZE(szCacheFileName));
            sei.lpFile = szCacheFileName;
        }
        else
        {
            // (reinerf)
            // Some apps (FrontPad, Office99, etc) want the URL passed to
            // them instead of the cache filename. We therefore create a string
            // that has the URL name after the null:
            //
            //  "CacheFileName/0UrlName"
            //
            // and pass it as the lpFile parameter to shellexecute.
            // We also pass SEE_MASK_FILEANDURL, so shellexecute can
            // recognize this case.
            //
            int iLength;

            if (FAILED(URLToCacheFile(pszURL, szCacheFileName, ARRAYSIZE(szCacheFileName))))
            {
                // Frontpage express crashes if we pass a null file name, so if the app doesn't
                // prefer the url instead, we bail.
                if (!DoesAppWantUrl(rVerb.pszExe))
                {
                    return;
                }
            }
            iLength = lstrlen(szCacheFileName);

            // copy in the URL name
            StrCpyN(&szCacheFileName[iLength + 1], pszURL, ARRAYSIZE(szCacheFileName) - (iLength + 1));

            // add the mask so shellexecute knows to check for the URL, if necessary.
            fMask |= SEE_MASK_FILEANDURL;
            sei.lpFile = szCacheFileName;
        }
    }
    else
    {
        // Not a URL, so pass the filename
        StrCpyN(szCacheFileName, pszURL, ARRAYSIZE(szCacheFileName));
        sei.lpFile = szCacheFileName;
    }

    // Hack for IE5 bug 50033 - Can remove when fpxpress fixes mru buffer overrun
    _GetExePath(rVerb);
    if(StrStr(rVerb.pszExe, TEXT("fpxpress.exe")) != NULL)
        szCacheFileName[256] = TEXT('\0');

    sei.cbSize = sizeof(SHELLEXECUTEINFO);
    sei.fMask = fMask;
    sei.hwnd = NULL;
    sei.lpVerb = rVerb.fUseOpenVerb ? NULL : TEXT("edit");
//    sei.lpFile = szCacheFileName;
//    sei.lpParameters = NULL;
    sei.lpDirectory = NULL;
    sei.nShow = SW_SHOWNORMAL;
    sei.hInstApp = NULL;
    sei.hkeyClass= rVerb.hkeyProgID;

    //
    // The office guys want us to call a special proxy to get around some DDE problems
    // and to sniff the html file for the original document name. Hackers! So let's
    // see if it is registered.
    //
    HKEY hkeyProxy = NULL;
    if (ERROR_SUCCESS == RegOpenKeyEx(rVerb.hkeyProgID, TEXT("HTML Handler"), 0, KEY_READ, &hkeyProxy))
    {
        DWORD cch;
        if (SUCCEEDED(AssocQueryStringByKey(0, ASSOCSTR_COMMAND, hkeyProxy, L"edit", NULL, &cch)))
        {
            sei.lpVerb = L"edit";
            sei.hkeyClass = hkeyProxy;
        }
        else if (SUCCEEDED(AssocQueryStringByKey(0, ASSOCSTR_COMMAND, hkeyProxy, NULL, NULL, &cch)))
        {
            sei.lpVerb = NULL;
            sei.hkeyClass = hkeyProxy;
        }
    }

    ShellExecuteEx(&sei);

    if (hkeyProxy)
    {
        RegCloseKey(hkeyProxy);
    }
}

//+-------------------------------------------------------------------------
// This window procedure intercepts owner-draw menu messages when the edit
// pop-up menu is displayed.
//--------------------------------------------------------------------------
LRESULT CALLBACK CInternetToolbar::CEditVerb::_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CEditVerb* pThis = (CEditVerb*)GetProp(hwnd, SZ_EDITVERB_PROP);

    if (!pThis)
        return DefWindowProcWrap(hwnd, uMsg, wParam, lParam);

    switch(uMsg)
    {
        case WM_DRAWITEM:
        case WM_MEASUREITEM:
        {
            UINT idCmd;

            switch (uMsg)
            {
                case WM_DRAWITEM:
                    idCmd = ((EDITVERB*)((DRAWITEMSTRUCT*)lParam)->itemData)->idCmd;
                    break;
                case WM_MEASUREITEM:
                    idCmd = ((EDITVERB*)((MEASUREITEMSTRUCT*)lParam)->itemData)->idCmd;
                    break;
            }

            if (InRange(idCmd, FCIDM_EDITFIRST, FCIDM_EDITLAST))
            {
                // do our own measuring
                UINT index  = idCmd - FCIDM_EDITFIRST;
                const EDITVERB& rVerb = pThis->_GetVerb(index);

                // We don't want the same accelerator on all items,
                // so remove underlines
                WCHAR wzBuf[MAX_PATH];
                UINT cchMax = ARRAYSIZE(wzBuf);
                LPWSTR pszTo = wzBuf;
                LPWSTR pszFrom = rVerb.pszMenuText;
                if (pszFrom)
                {
                    while (0 < --cchMax)
                    {
                        if (*pszFrom == L'&')
                        {
                            pszFrom++;
                            continue;
                        }

                        if ( !(*pszTo++ = *pszFrom++) )
                        {
                            --pszTo;
                            break;
                        }
                    }

                    if (0 == cchMax)
                        *pszTo = L'\0';

                    //
                    // In some locals, the accelerator is identified in brackets at the
                    // end of the string, so if we strip ampersands, we strip these too.
                    //
                    if (--pszTo >= wzBuf && *pszTo == L')')
                    {
                        while (--pszTo >= wzBuf)
                        {
                            if (*pszTo == L'(')
                            {
                                *pszTo = L'\0';
                                break;
                            }
                        }
                    }
                }
                else
                {
                    wzBuf[0] = 0;
                }

                switch (uMsg)
                {
                    case WM_MEASUREITEM:
                        MeasureMenuItem((MEASUREITEMSTRUCT *)lParam, wzBuf);
                        break;
                    case WM_DRAWITEM:
                        int iIcon = (rVerb.iIcon != -1) ? rVerb.iIcon : 0;
                        DrawMenuItem((LPDRAWITEMSTRUCT)lParam, wzBuf, iIcon);
                        break;
                }
            }
        }
        default:
           return CallWindowProc(pThis->_lpfnOldWndProc, hwnd, uMsg, wParam, lParam);
    }
    return 0L;
}

#endif // EDIT_HACK
