|
|
#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; }
|