717 lines
20 KiB
C++
717 lines
20 KiB
C++
#include "shellprv.h"
|
|
#include "tbmenu.h"
|
|
#include "isfband.h"
|
|
#include "isfmenu.h"
|
|
|
|
#include "mluisupp.h"
|
|
#define SMFORWARD(x) if (!_psm) { return E_FAIL; } else return _psm->x
|
|
|
|
class CTrackShellMenu : public ITrackShellMenu,
|
|
public IShellMenu2,
|
|
public IObjectWithSite,
|
|
public IServiceProvider
|
|
{
|
|
public:
|
|
// *** IUnknown ***
|
|
virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
|
|
virtual STDMETHODIMP_(ULONG) AddRef(void);
|
|
virtual STDMETHODIMP_(ULONG) Release(void);
|
|
|
|
// *** IShellMenu methods ***
|
|
virtual STDMETHODIMP Initialize(IShellMenuCallback* psmc, UINT uId, UINT uIdAncestor, DWORD dwFlags);
|
|
virtual STDMETHODIMP GetMenuInfo(IShellMenuCallback** ppsmc, UINT* puId,
|
|
UINT* puIdAncestor, DWORD* pdwFlags);
|
|
virtual STDMETHODIMP SetShellFolder(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HKEY hkey, DWORD dwFlags);
|
|
virtual STDMETHODIMP GetShellFolder(DWORD* pdwFlags, LPITEMIDLIST* ppidl, REFIID riid, void** ppvObj);
|
|
virtual STDMETHODIMP SetMenu(HMENU hmenu, HWND hwnd, DWORD dwFlags);
|
|
virtual STDMETHODIMP GetMenu(HMENU* phmenu, HWND* phwnd, DWORD* pdwFlags);
|
|
virtual STDMETHODIMP InvalidateItem(LPSMDATA psmd, DWORD dwFlags);
|
|
virtual STDMETHODIMP GetState(LPSMDATA psmd);
|
|
virtual STDMETHODIMP SetMenuToolbar(IUnknown* punk, DWORD dwFlags);
|
|
|
|
// *** ITrackShellMenu methods ***
|
|
virtual STDMETHODIMP SetObscured(HWND hwndTB, IUnknown* punkBand, DWORD dwSMSetFlags);
|
|
virtual STDMETHODIMP Popup(HWND hwnd, POINTL *ppt, RECTL *prcExclude, DWORD dwFlags);
|
|
|
|
// *** IObjectWithSite methods ***
|
|
virtual STDMETHODIMP SetSite(IUnknown* punkSite);
|
|
virtual STDMETHODIMP GetSite(REFIID ridd, void** ppvObj) { *ppvObj = NULL; return E_NOTIMPL; };
|
|
|
|
// *** IServiceProvider methods ***
|
|
virtual STDMETHODIMP QueryService(REFGUID guidService,
|
|
REFIID riid, void **ppvObj);
|
|
|
|
// *** IShellMenu2 methods ***
|
|
virtual STDMETHODIMP GetSubMenu(UINT idCmd, REFIID riid, void **ppvObj);
|
|
virtual STDMETHODIMP SetToolbar(HWND hwnd, DWORD dwFlags);
|
|
virtual STDMETHODIMP SetMinWidth(int cxMenu);
|
|
virtual STDMETHODIMP SetNoBorder(BOOL fNoBorder);
|
|
virtual STDMETHODIMP SetTheme(LPCWSTR pszTheme);
|
|
|
|
CTrackShellMenu();
|
|
private:
|
|
virtual ~CTrackShellMenu();
|
|
|
|
IShellMenu* _psmClient;
|
|
IShellMenu* _psm;
|
|
IShellMenu2* _psm2;
|
|
IUnknown* _punkSite;
|
|
int _cRef;
|
|
HMENU _hmenu;
|
|
BITBOOL _fDestroyTopLevel : 1;
|
|
};
|
|
|
|
|
|
typedef struct
|
|
{
|
|
WNDPROC pfnOriginal;
|
|
IMenuBand* pmb;
|
|
} MENUHOOK;
|
|
|
|
#define SZ_MENUHOOKPROP TEXT("MenuHookProp")
|
|
|
|
LRESULT CALLBACK MenuHookWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
MENUHOOK* pmh = (MENUHOOK*)GetProp(hwnd, SZ_MENUHOOKPROP);
|
|
|
|
if (pmh)
|
|
{
|
|
MSG msg;
|
|
LRESULT lres;
|
|
|
|
msg.hwnd = hwnd;
|
|
msg.message = uMsg;
|
|
msg.wParam = wParam;
|
|
msg.lParam = lParam;
|
|
|
|
if (pmh->pmb->TranslateMenuMessage(&msg, &lres) == S_OK)
|
|
return lres;
|
|
|
|
wParam = msg.wParam;
|
|
lParam = msg.lParam;
|
|
return CallWindowProc(pmh->pfnOriginal, hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
HRESULT HookMenuWindow(HWND hwnd, IMenuBand* pmb)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
|
|
ASSERT(IsWindow(hwnd));
|
|
|
|
// make sure we haven't already hooked this window
|
|
if (GetProp(hwnd, SZ_MENUHOOKPROP) == NULL)
|
|
{
|
|
MENUHOOK* pmh = new MENUHOOK;
|
|
if (pmh)
|
|
{
|
|
pmh->pfnOriginal = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
|
|
pmh->pmb = pmb;
|
|
|
|
SetProp(hwnd, SZ_MENUHOOKPROP, pmh);
|
|
|
|
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)MenuHookWndProc);
|
|
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
void UnHookMenuWindow(HWND hwnd)
|
|
{
|
|
|
|
MENUHOOK* pmh = (MENUHOOK*)GetProp(hwnd, SZ_MENUHOOKPROP);
|
|
if (pmh)
|
|
{
|
|
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) pmh->pfnOriginal);
|
|
SetProp(hwnd, SZ_MENUHOOKPROP, NULL);
|
|
delete pmh;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// This class is here to implement a "Menu Filter". We need this because the old style of
|
|
// implementing obscured Menus does not work because user munges the WM_INITMENUPOPUP information
|
|
// based on the relative position within the HMENU. So here we keep that information, we just hide the item.
|
|
|
|
class CShellMenuCallbackWrapper : public IShellMenuCallback,
|
|
public CObjectWithSite
|
|
{
|
|
int _cRef;
|
|
IShellMenuCallback* _psmc;
|
|
HWND _hwnd;
|
|
RECT _rcTB;
|
|
~CShellMenuCallbackWrapper()
|
|
{
|
|
ATOMICRELEASE(_psmc);
|
|
}
|
|
|
|
public:
|
|
CShellMenuCallbackWrapper(HWND hwnd, IShellMenuCallback* psmc) : _cRef(1)
|
|
{
|
|
_psmc = psmc;
|
|
if (_psmc)
|
|
_psmc->AddRef();
|
|
_hwnd = hwnd;
|
|
GetClientRect(_hwnd, &_rcTB);
|
|
}
|
|
|
|
// *** IUnknown methods ***
|
|
STDMETHODIMP QueryInterface (REFIID riid, LPVOID * ppvObj)
|
|
{
|
|
static const QITAB qit[] =
|
|
{
|
|
QITABENT(CShellMenuCallbackWrapper, IShellMenuCallback),
|
|
QITABENT(CShellMenuCallbackWrapper, IObjectWithSite),
|
|
{ 0 },
|
|
};
|
|
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) AddRef()
|
|
{
|
|
_cRef++;
|
|
return _cRef;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) Release()
|
|
{
|
|
ASSERT(_cRef > 0);
|
|
_cRef--;
|
|
|
|
if (_cRef > 0)
|
|
return _cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
// *** CObjectWithSite methods (override)***
|
|
STDMETHODIMP SetSite(IUnknown* punk) { IUnknown_SetSite(_psmc, punk); return S_OK; }
|
|
STDMETHODIMP GetSite(REFIID riid, void** ppObj) { return IUnknown_GetSite(_psmc, riid, ppObj); }
|
|
|
|
// *** IShellMenuCallback methods ***
|
|
STDMETHODIMP CallbackSM(LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
|
|
if (_psmc)
|
|
hres = _psmc->CallbackSM(psmd, uMsg, wParam, lParam);
|
|
|
|
if (uMsg == SMC_GETINFO)
|
|
{
|
|
SMINFO* psminfo = (SMINFO*)lParam;
|
|
int iPos = (int)SendMessage(_hwnd, TB_COMMANDTOINDEX, psmd->uId, 0);
|
|
|
|
if (psminfo->dwMask & SMIM_FLAGS &&
|
|
iPos >= 0 &&
|
|
!SHIsButtonObscured(_hwnd, &_rcTB, iPos))
|
|
{
|
|
psminfo->dwFlags |= SMIF_HIDDEN;
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
};
|
|
|
|
STDAPI CTrackShellMenu_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
CTrackShellMenu* pObj = new CTrackShellMenu();
|
|
if (pObj)
|
|
{
|
|
hres = pObj->QueryInterface(riid, ppv);
|
|
pObj->Release();
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
CTrackShellMenu::CTrackShellMenu() : _cRef(1)
|
|
{
|
|
if (SUCCEEDED(CoCreateInstance(CLSID_MenuBand, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellMenu, &_psm))))
|
|
{
|
|
_psm->QueryInterface(IID_PPV_ARG(IShellMenu2, &_psm2));
|
|
}
|
|
}
|
|
|
|
CTrackShellMenu::~CTrackShellMenu()
|
|
{
|
|
ATOMICRELEASE(_psm2);
|
|
ATOMICRELEASE(_psm);
|
|
ATOMICRELEASE(_psmClient);
|
|
ASSERT(!_punkSite); // else someone neglected to call matching SetSite(NULL)
|
|
}
|
|
|
|
ULONG CTrackShellMenu::AddRef()
|
|
{
|
|
_cRef++;
|
|
return _cRef;
|
|
}
|
|
|
|
ULONG CTrackShellMenu::Release()
|
|
{
|
|
ASSERT(_cRef > 0);
|
|
_cRef--;
|
|
|
|
if (_cRef > 0)
|
|
return _cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
HRESULT CTrackShellMenu::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENTMULTI(CTrackShellMenu, IShellMenu, ITrackShellMenu),
|
|
QITABENT(CTrackShellMenu, ITrackShellMenu),
|
|
QITABENT(CTrackShellMenu, IShellMenu2),
|
|
QITABENT(CTrackShellMenu, IObjectWithSite),
|
|
QITABENT(CTrackShellMenu, IServiceProvider),
|
|
{ 0 },
|
|
};
|
|
|
|
HRESULT hres = QISearch(this, qit, riid, ppvObj);
|
|
|
|
return hres;
|
|
}
|
|
|
|
// *** IServiceProvider methods ***
|
|
HRESULT CTrackShellMenu::QueryService(REFGUID guidService,
|
|
REFIID riid, void **ppvObj)
|
|
{
|
|
return IUnknown_QueryService(_psm, guidService, riid, ppvObj);
|
|
}
|
|
|
|
// *** IShellMenu methods ***
|
|
STDMETHODIMP CTrackShellMenu::Initialize(IShellMenuCallback* psmc, UINT uId, UINT uIdAncestor, DWORD dwFlags)
|
|
{ SMFORWARD(Initialize(psmc, uId, uIdAncestor, dwFlags)); }
|
|
|
|
STDMETHODIMP CTrackShellMenu::GetMenuInfo(IShellMenuCallback** ppsmc, UINT* puId, UINT* puIdAncestor, DWORD* pdwFlags)
|
|
{ SMFORWARD(GetMenuInfo(ppsmc, puId, puIdAncestor, pdwFlags)); }
|
|
|
|
STDMETHODIMP CTrackShellMenu::SetShellFolder(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HKEY hkey, DWORD dwFlags)
|
|
{ SMFORWARD(SetShellFolder(psf, pidlFolder, hkey, dwFlags)); }
|
|
|
|
STDMETHODIMP CTrackShellMenu::GetShellFolder(DWORD* pdwFlags, LPITEMIDLIST* ppidl, REFIID riid, void** ppvObj)
|
|
{ SMFORWARD(GetShellFolder(pdwFlags, ppidl, riid, ppvObj)); }
|
|
|
|
STDMETHODIMP CTrackShellMenu::SetMenu(HMENU hmenu, HWND hwnd, DWORD dwFlags)
|
|
{ SMFORWARD(SetMenu(hmenu, hwnd, dwFlags)); }
|
|
|
|
STDMETHODIMP CTrackShellMenu::GetMenu(HMENU* phmenu, HWND* phwnd, DWORD* pdwFlags)
|
|
{ SMFORWARD(GetMenu(phmenu, phwnd, pdwFlags)); }
|
|
|
|
STDMETHODIMP CTrackShellMenu::InvalidateItem(LPSMDATA psmd, DWORD dwFlags)
|
|
{ SMFORWARD(InvalidateItem(psmd, dwFlags)); }
|
|
|
|
STDMETHODIMP CTrackShellMenu::GetState(LPSMDATA psmd)
|
|
{ SMFORWARD(GetState(psmd)); }
|
|
|
|
STDMETHODIMP CTrackShellMenu::SetMenuToolbar(IUnknown* punk, DWORD dwFlags)
|
|
{ SMFORWARD(SetMenuToolbar(punk, dwFlags)); }
|
|
|
|
STDMETHODIMP CTrackShellMenu::GetSubMenu(UINT idCmd, REFIID riid, void **ppvObj)
|
|
{
|
|
if (_psm2)
|
|
{
|
|
return _psm2->GetSubMenu(idCmd, riid, ppvObj);
|
|
}
|
|
else
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP CTrackShellMenu::SetToolbar([in] HWND hwnd, [in] DWORD dwFlags)
|
|
{
|
|
if (_psm2)
|
|
{
|
|
return _psm2->SetToolbar(hwnd, dwFlags);
|
|
}
|
|
else
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP CTrackShellMenu::SetMinWidth([in] int cxMenu)
|
|
{
|
|
if (_psm2)
|
|
{
|
|
return _psm2->SetMinWidth(cxMenu);
|
|
}
|
|
else
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP CTrackShellMenu::SetNoBorder([in] BOOL fNoBorder)
|
|
{
|
|
if (_psm2)
|
|
{
|
|
return _psm2->SetNoBorder(fNoBorder);
|
|
}
|
|
else
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP CTrackShellMenu::SetTheme([in] LPCWSTR pszTheme)
|
|
{
|
|
if (_psm2)
|
|
{
|
|
return _psm2->SetTheme(pszTheme);
|
|
}
|
|
else
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
}
|
|
|
|
// *** ITrackShellMenu methods ***
|
|
HRESULT CTrackShellMenu::SetObscured(HWND hwndTB, IUnknown* punkBand, DWORD dwSMSetFlags)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
// Make sure we created the Inner Shell Menu
|
|
if (!_psm)
|
|
return hr;
|
|
|
|
if (punkBand &&
|
|
SUCCEEDED(punkBand->QueryInterface(IID_PPV_ARG(IShellMenu, &_psmClient))))
|
|
{
|
|
UINT uId, uIdAncestor;
|
|
DWORD dwFlags;
|
|
IShellMenuCallback* psmcb;
|
|
|
|
hr = _psmClient->GetMenuInfo(&psmcb, &uId, &uIdAncestor, &dwFlags);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IShellMenuCallback* psmcbClone = NULL;
|
|
if (psmcb)
|
|
{
|
|
if (S_FALSE == psmcb->CallbackSM(NULL, SMC_GETOBJECT,
|
|
(WPARAM)&IID_IShellMenuCallback,
|
|
(LPARAM)(LPVOID*)&psmcbClone))
|
|
{
|
|
psmcbClone = psmcb;
|
|
psmcbClone->AddRef();
|
|
}
|
|
}
|
|
|
|
dwFlags &= ~SMINIT_HORIZONTAL;
|
|
|
|
CShellMenuCallbackWrapper* psmcw = new CShellMenuCallbackWrapper(hwndTB, psmcbClone);
|
|
|
|
// We want the bands to think it is:
|
|
// Top level - because it has no menuband parent
|
|
// Vertical - because it's not a menubar
|
|
dwFlags |= SMINIT_TOPLEVEL | SMINIT_VERTICAL;
|
|
hr = _psm->Initialize(psmcw, uId, ANCESTORDEFAULT, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
HWND hwndOwner;
|
|
HMENU hmenuObscured;
|
|
hr = _psmClient->GetMenu(&hmenuObscured, &hwndOwner, NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _psm->SetMenu(hmenuObscured, hwndOwner, dwSMSetFlags | SMSET_DONTOWN); // Menuband takes ownership;
|
|
}
|
|
}
|
|
|
|
if (psmcb)
|
|
psmcb->Release();
|
|
|
|
if (psmcbClone)
|
|
psmcbClone->Release();
|
|
|
|
if (psmcw)
|
|
psmcw->Release();
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IShellMenu2 *psm2;
|
|
hr = _psm->QueryInterface(IID_PPV_ARG(IShellMenu2, &psm2));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psm2->SetToolbar(hwndTB, dwSMSetFlags);
|
|
psm2->Release();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTrackShellMenu::Popup(HWND hwnd, POINTL *ppt, RECTL *prcExclude, DWORD dwFlags)
|
|
{
|
|
IMenuBand* pmb;
|
|
HRESULT hres = E_INVALIDARG;
|
|
|
|
|
|
if (!_psm)
|
|
return hres;
|
|
|
|
hres = _psm->QueryInterface(IID_PPV_ARG(IMenuBand, &pmb));
|
|
if (FAILED(hres))
|
|
return hres;
|
|
|
|
HWND hwndParent = GetTopLevelAncestor(hwnd);
|
|
|
|
// Did the user set a menu into the Shell Menu?
|
|
HWND hwndSubclassed = NULL;
|
|
GetMenu(NULL, &hwndSubclassed, NULL);
|
|
if (hwndSubclassed == NULL)
|
|
{
|
|
// No; We need to artificially set one so that the message filtering and stuff works
|
|
SetMenu(NULL, hwndParent, 0);
|
|
}
|
|
|
|
SetForegroundWindow(hwndParent);
|
|
|
|
IMenuPopup* pmp;
|
|
hres = CoCreateInstance(CLSID_MenuDeskBar, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IMenuPopup, &pmp));
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
IBandSite* pbs;
|
|
hres = CoCreateInstance(CLSID_MenuBandSite, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IBandSite, &pbs));
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = pmp->SetClient(pbs);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
IDeskBand* pdb;
|
|
hres = _psm->QueryInterface(IID_PPV_ARG(IDeskBand, &pdb));
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = pbs->AddBand(pdb);
|
|
pdb->Release();
|
|
}
|
|
}
|
|
pbs->Release();
|
|
}
|
|
|
|
// If we've got a site ourselves, have MenuDeskBar use that.
|
|
if (_punkSite)
|
|
IUnknown_SetSite(pmp, _punkSite);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
CMBMsgFilter* pmf = GetMessageFilter();
|
|
void* pvContext = GetMessageFilter()->GetContext();
|
|
hres = HookMenuWindow(hwndParent, pmb);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// This collapses any modal menus before we proceed. When switching between
|
|
// Chevron menus, we need to collapse the previous menu. Refer to the comment
|
|
// at the function definition.
|
|
pmf->ForceModalCollapse();
|
|
|
|
pmp->Popup(ppt, (LPRECTL)prcExclude, dwFlags);
|
|
|
|
pmf->SetModal(TRUE);
|
|
|
|
MSG msg;
|
|
while (GetMessage(&msg, NULL, 0, 0))
|
|
{
|
|
HRESULT hres = pmb->IsMenuMessage(&msg);
|
|
if (hres == E_FAIL)
|
|
{
|
|
// menuband says it's time to pack up and go home.
|
|
// re-post this message so that it gets handled after
|
|
// we've cleaned up the menu (avoid re-entrancy issues &
|
|
// let rebar restore state of chevron button to unpressed)
|
|
PostMessage(msg.hwnd, msg.message, msg.wParam, msg.lParam);
|
|
break;
|
|
}
|
|
else if (hres != S_OK)
|
|
{
|
|
// menuband didn't handle this one
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
hres = S_OK;
|
|
UnHookMenuWindow(hwndParent);
|
|
// We cannot change the context when modal, so unset the modal flag so that we can undo the context block.
|
|
pmf->SetModal(FALSE);
|
|
pmf->SetContext(pvContext, TRUE);
|
|
}
|
|
pmb->Release();
|
|
}
|
|
|
|
if (_psmClient)
|
|
{
|
|
// This is to fix a bug where if there is a cached ISHellMenu in the submenu,
|
|
// and you share the callback (For example, Broweser menu callback and the
|
|
// favorites menu being shared between the browser bar and the chevron menu)
|
|
// when on menu collapsed, we were destroying the sub menu by doing a set site.
|
|
// since we no longer do the set site on the sub menu, we need a way to say "Reset
|
|
// your parent". and this is the best way.
|
|
IUnknown_Exec(_psmClient, &CGID_MenuBand, MBANDCID_REFRESH, 0, NULL, NULL);
|
|
}
|
|
|
|
// This call is required regardless of whether we had a _punkSite above;
|
|
// MenuDeskBar does its cleanup on SetSite(NULL).
|
|
IUnknown_SetSite(pmp, NULL);
|
|
pmp->Release();
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
// *** IObjectWithSite methods ***
|
|
HRESULT CTrackShellMenu::SetSite(IUnknown* punkSite)
|
|
{
|
|
ASSERT(NULL == punkSite || IS_VALID_CODE_PTR(punkSite, IUnknown));
|
|
|
|
ATOMICRELEASE(_punkSite);
|
|
|
|
_punkSite = punkSite;
|
|
|
|
if (punkSite)
|
|
punkSite->AddRef();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL IsISFBand(IUnknown* punk)
|
|
{
|
|
OLECMD rgCmds[] = {
|
|
ISFBID_PRIVATEID, 0,
|
|
};
|
|
|
|
IUnknown_QueryStatus(punk, &CGID_ISFBand, SIZEOF(rgCmds), rgCmds, NULL);
|
|
|
|
return BOOLIFY(rgCmds[0].cmdf);
|
|
}
|
|
|
|
HRESULT DoISFBandStuff(CTrackShellMenu* ptsm, IUnknown* punk)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (punk && ptsm)
|
|
{
|
|
IShellFolderBand* psfb;
|
|
hr = punk->QueryInterface(IID_PPV_ARG(IShellFolderBand, &psfb));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
BANDINFOSFB bi;
|
|
bi.dwMask = ISFB_MASK_IDLIST | ISFB_MASK_SHELLFOLDER;
|
|
|
|
hr = psfb->GetBandInfoSFB(&bi);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CISFMenuCallback* pCallback = new CISFMenuCallback();
|
|
|
|
if (pCallback)
|
|
{
|
|
hr = pCallback->Initialize(punk);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ptsm->Initialize(SAFECAST(pCallback, IShellMenuCallback*), 0,
|
|
ANCESTORDEFAULT, SMINIT_VERTICAL | SMINIT_TOPLEVEL);
|
|
|
|
hr = ptsm->SetShellFolder(bi.psf, bi.pidl, NULL, SMSET_COLLAPSEONEMPTY);
|
|
}
|
|
pCallback->Release();
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
bi.psf->Release();
|
|
ILFree(bi.pidl);
|
|
}
|
|
|
|
psfb->Release();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// An API for internal use.
|
|
HRESULT ToolbarMenu_Popup(HWND hwnd, LPRECT prc, IUnknown* punk, HWND hwndTB, int idMenu, DWORD dwFlags)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
CTrackShellMenu* ptsm = new CTrackShellMenu();
|
|
|
|
if (ptsm)
|
|
{
|
|
hres = S_OK;
|
|
if (IsISFBand(punk))
|
|
{
|
|
hres = DoISFBandStuff(ptsm, punk);
|
|
}
|
|
else if (hwndTB)
|
|
{
|
|
ptsm->Initialize(NULL, 0, ANCESTORDEFAULT, SMINIT_TOPLEVEL | SMINIT_VERTICAL | SMINIT_RESTRICT_DRAGDROP);
|
|
hres = ptsm->SetObscured(hwndTB, punk, SMSET_TOP);
|
|
}
|
|
|
|
IUnknown* punkSite;
|
|
if (SUCCEEDED(IUnknown_GetSite(punk, IID_PPV_ARG(IUnknown, &punkSite))))
|
|
ptsm->SetSite(punkSite);
|
|
|
|
HMENU hmenu = idMenu ? SHLoadMenuPopup(HINST_THISDLL, idMenu) : NULL;
|
|
|
|
if (SUCCEEDED(hres) && hmenu)
|
|
hres = ptsm->SetMenu(hmenu, hwnd, SMSET_BOTTOM);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
DWORD dwPopupFlags = MPPF_BOTTOM;
|
|
|
|
// select first/last menu item if specified
|
|
if (dwFlags == DBPC_SELECTFIRST)
|
|
{
|
|
dwPopupFlags |= MPPF_INITIALSELECT;
|
|
}
|
|
else if (dwFlags == DBPC_SELECTLAST)
|
|
{
|
|
dwPopupFlags |= MPPF_FINALSELECT;
|
|
}
|
|
else if (dwFlags != 0)
|
|
{
|
|
VARIANT var;
|
|
var.vt = VT_I4;
|
|
var.lVal = dwFlags;
|
|
IUnknown* punk;
|
|
if (SUCCEEDED(ptsm->QueryInterface(IID_PPV_ARG(IUnknown, &punk))))
|
|
{
|
|
IUnknown_QueryServiceExec(punk, SID_SMenuBandChild, &CGID_MenuBand, MBANDCID_SELECTITEM, 0, &var, NULL);
|
|
punk->Release();
|
|
}
|
|
}
|
|
|
|
POINTL ptPop = {prc->left, prc->bottom};
|
|
hres = ptsm->Popup(hwnd, &ptPop, (LPRECTL)prc, dwPopupFlags);
|
|
}
|
|
|
|
ptsm->SetSite(NULL);
|
|
ptsm->Release();
|
|
}
|
|
return hres;
|
|
}
|