Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

591 lines
18 KiB

  1. #include "shellprv.h"
  2. #include "tbmenu.h"
  3. #include "isfband.h"
  4. #include "isfmenu.h"
  5. #include "mluisupp.h"
  6. #define SMFORWARD(x) if (!_psm) { return E_FAIL; } else return _psm->x
  7. class CTrackShellMenu : public ITrackShellMenu,
  8. public IShellMenu2,
  9. public IObjectWithSite,
  10. public IServiceProvider
  11. {
  12. public:
  13. // *** IUnknown ***
  14. virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
  15. virtual STDMETHODIMP_(ULONG) AddRef(void);
  16. virtual STDMETHODIMP_(ULONG) Release(void);
  17. // *** IShellMenu methods ***
  18. virtual STDMETHODIMP Initialize(IShellMenuCallback* psmc, UINT uId, UINT uIdAncestor, DWORD dwFlags);
  19. virtual STDMETHODIMP GetMenuInfo(IShellMenuCallback** ppsmc, UINT* puId,
  20. UINT* puIdAncestor, DWORD* pdwFlags);
  21. virtual STDMETHODIMP SetShellFolder(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HKEY hkey, DWORD dwFlags);
  22. virtual STDMETHODIMP GetShellFolder(DWORD* pdwFlags, LPITEMIDLIST* ppidl, REFIID riid, void** ppvObj);
  23. virtual STDMETHODIMP SetMenu(HMENU hmenu, HWND hwnd, DWORD dwFlags);
  24. virtual STDMETHODIMP GetMenu(HMENU* phmenu, HWND* phwnd, DWORD* pdwFlags);
  25. virtual STDMETHODIMP InvalidateItem(LPSMDATA psmd, DWORD dwFlags);
  26. virtual STDMETHODIMP GetState(LPSMDATA psmd);
  27. virtual STDMETHODIMP SetMenuToolbar(IUnknown* punk, DWORD dwFlags);
  28. // *** ITrackShellMenu methods ***
  29. virtual STDMETHODIMP SetObscured(HWND hwndTB, IUnknown* punkBand, DWORD dwSMSetFlags);
  30. virtual STDMETHODIMP Popup(HWND hwnd, POINTL *ppt, RECTL *prcExclude, DWORD dwFlags);
  31. // *** IObjectWithSite methods ***
  32. virtual STDMETHODIMP SetSite(IUnknown* punkSite);
  33. virtual STDMETHODIMP GetSite(REFIID ridd, void** ppvObj) { *ppvObj = NULL; return E_NOTIMPL; };
  34. // *** IServiceProvider methods ***
  35. virtual STDMETHODIMP QueryService(REFGUID guidService,
  36. REFIID riid, void **ppvObj);
  37. // *** IShellMenu2 methods ***
  38. virtual STDMETHODIMP GetSubMenu(UINT idCmd, REFIID riid, void **ppvObj);
  39. virtual STDMETHODIMP SetToolbar(HWND hwnd, DWORD dwFlags);
  40. virtual STDMETHODIMP SetMinWidth(int cxMenu);
  41. virtual STDMETHODIMP SetNoBorder(BOOL fNoBorder);
  42. virtual STDMETHODIMP SetTheme(LPCWSTR pszTheme);
  43. CTrackShellMenu();
  44. private:
  45. virtual ~CTrackShellMenu();
  46. IShellMenu* _psmClient;
  47. IShellMenu* _psm;
  48. IShellMenu2* _psm2;
  49. IUnknown* _punkSite;
  50. int _cRef;
  51. HMENU _hmenu;
  52. BITBOOL _fDestroyTopLevel : 1;
  53. };
  54. typedef struct
  55. {
  56. WNDPROC pfnOriginal;
  57. IMenuBand* pmb;
  58. } MENUHOOK;
  59. #define SZ_MENUHOOKPROP TEXT("MenuHookProp")
  60. LRESULT CALLBACK MenuHookWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  61. {
  62. MENUHOOK* pmh = (MENUHOOK*)GetProp(hwnd, SZ_MENUHOOKPROP);
  63. if (pmh)
  64. {
  65. MSG msg;
  66. LRESULT lres;
  67. msg.hwnd = hwnd;
  68. msg.message = uMsg;
  69. msg.wParam = wParam;
  70. msg.lParam = lParam;
  71. if (pmh->pmb->TranslateMenuMessage(&msg, &lres) == S_OK)
  72. return lres;
  73. wParam = msg.wParam;
  74. lParam = msg.lParam;
  75. return CallWindowProc(pmh->pfnOriginal, hwnd, uMsg, wParam, lParam);
  76. }
  77. return 0;
  78. }
  79. HRESULT HookMenuWindow(HWND hwnd, IMenuBand* pmb)
  80. {
  81. HRESULT hres = E_FAIL;
  82. ASSERT(IsWindow(hwnd));
  83. // make sure we haven't already hooked this window
  84. if (GetProp(hwnd, SZ_MENUHOOKPROP) == NULL)
  85. {
  86. MENUHOOK* pmh = new MENUHOOK;
  87. if (pmh)
  88. {
  89. pmh->pfnOriginal = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
  90. pmh->pmb = pmb;
  91. SetProp(hwnd, SZ_MENUHOOKPROP, pmh);
  92. SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)MenuHookWndProc);
  93. hres = S_OK;
  94. }
  95. }
  96. return hres;
  97. }
  98. void UnHookMenuWindow(HWND hwnd)
  99. {
  100. MENUHOOK* pmh = (MENUHOOK*)GetProp(hwnd, SZ_MENUHOOKPROP);
  101. if (pmh)
  102. {
  103. SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) pmh->pfnOriginal);
  104. SetProp(hwnd, SZ_MENUHOOKPROP, NULL);
  105. delete pmh;
  106. }
  107. }
  108. // This class is here to implement a "Menu Filter". We need this because the old style of
  109. // implementing obscured Menus does not work because user munges the WM_INITMENUPOPUP information
  110. // based on the relative position within the HMENU. So here we keep that information, we just hide the item.
  111. class CShellMenuCallbackWrapper : public IShellMenuCallback,
  112. public CObjectWithSite
  113. {
  114. int _cRef;
  115. IShellMenuCallback* _psmc;
  116. HWND _hwnd;
  117. RECT _rcTB;
  118. ~CShellMenuCallbackWrapper()
  119. {
  120. ATOMICRELEASE(_psmc);
  121. }
  122. public:
  123. CShellMenuCallbackWrapper(HWND hwnd, IShellMenuCallback* psmc) : _cRef(1)
  124. {
  125. _psmc = psmc;
  126. if (_psmc)
  127. _psmc->AddRef();
  128. _hwnd = hwnd;
  129. GetClientRect(_hwnd, &_rcTB);
  130. }
  131. // *** IUnknown methods ***
  132. STDMETHODIMP QueryInterface (REFIID riid, LPVOID * ppvObj)
  133. {
  134. static const QITAB qit[] =
  135. {
  136. QITABENT(CShellMenuCallbackWrapper, IShellMenuCallback),
  137. QITABENT(CShellMenuCallbackWrapper, IObjectWithSite),
  138. { 0 },
  139. };
  140. return QISearch(this, qit, riid, ppvObj);
  141. }
  142. STDMETHODIMP_(ULONG) AddRef()
  143. {
  144. _cRef++;
  145. return _cRef;
  146. }
  147. STDMETHODIMP_(ULONG) Release()
  148. {
  149. ASSERT(_cRef > 0);
  150. _cRef--;
  151. if (_cRef > 0)
  152. return _cRef;
  153. delete this;
  154. return 0;
  155. }
  156. // *** CObjectWithSite methods (override)***
  157. STDMETHODIMP SetSite(IUnknown* punk) { IUnknown_SetSite(_psmc, punk); return S_OK; }
  158. STDMETHODIMP GetSite(REFIID riid, void** ppObj) { return IUnknown_GetSite(_psmc, riid, ppObj); }
  159. // *** IShellMenuCallback methods ***
  160. STDMETHODIMP CallbackSM(LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  161. {
  162. HRESULT hres = S_FALSE;
  163. if (_psmc)
  164. hres = _psmc->CallbackSM(psmd, uMsg, wParam, lParam);
  165. if (uMsg == SMC_GETINFO)
  166. {
  167. SMINFO* psminfo = (SMINFO*)lParam;
  168. int iPos = (int)SendMessage(_hwnd, TB_COMMANDTOINDEX, psmd->uId, 0);
  169. if (psminfo->dwMask & SMIM_FLAGS &&
  170. iPos >= 0 &&
  171. !SHIsButtonObscured(_hwnd, &_rcTB, iPos))
  172. {
  173. psminfo->dwFlags |= SMIF_HIDDEN;
  174. hres = S_OK;
  175. }
  176. }
  177. return hres;
  178. }
  179. };
  180. STDAPI CTrackShellMenu_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv)
  181. {
  182. HRESULT hres = E_OUTOFMEMORY;
  183. CTrackShellMenu* pObj = new CTrackShellMenu();
  184. if (pObj)
  185. {
  186. hres = pObj->QueryInterface(riid, ppv);
  187. pObj->Release();
  188. }
  189. return hres;
  190. }
  191. CTrackShellMenu::CTrackShellMenu() : _cRef(1)
  192. {
  193. if (SUCCEEDED(CoCreateInstance(CLSID_MenuBand, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellMenu, &_psm))))
  194. {
  195. _psm->QueryInterface(IID_PPV_ARG(IShellMenu2, &_psm2));
  196. }
  197. }
  198. CTrackShellMenu::~CTrackShellMenu()
  199. {
  200. ATOMICRELEASE(_psm2);
  201. ATOMICRELEASE(_psm);
  202. ATOMICRELEASE(_psmClient);
  203. ASSERT(!_punkSite); // else someone neglected to call matching SetSite(NULL)
  204. }
  205. ULONG CTrackShellMenu::AddRef()
  206. {
  207. _cRef++;
  208. return _cRef;
  209. }
  210. ULONG CTrackShellMenu::Release()
  211. {
  212. ASSERT(_cRef > 0);
  213. _cRef--;
  214. if (_cRef > 0)
  215. return _cRef;
  216. delete this;
  217. return 0;
  218. }
  219. HRESULT CTrackShellMenu::QueryInterface(REFIID riid, void **ppvObj)
  220. {
  221. static const QITAB qit[] = {
  222. QITABENTMULTI(CTrackShellMenu, IShellMenu, ITrackShellMenu),
  223. QITABENT(CTrackShellMenu, ITrackShellMenu),
  224. QITABENT(CTrackShellMenu, IShellMenu2),
  225. QITABENT(CTrackShellMenu, IObjectWithSite),
  226. QITABENT(CTrackShellMenu, IServiceProvider),
  227. { 0 },
  228. };
  229. HRESULT hres = QISearch(this, qit, riid, ppvObj);
  230. return hres;
  231. }
  232. // *** IServiceProvider methods ***
  233. HRESULT CTrackShellMenu::QueryService(REFGUID guidService,
  234. REFIID riid, void **ppvObj)
  235. {
  236. return IUnknown_QueryService(_psm, guidService, riid, ppvObj);
  237. }
  238. // *** IShellMenu methods ***
  239. STDMETHODIMP CTrackShellMenu::Initialize(IShellMenuCallback* psmc, UINT uId, UINT uIdAncestor, DWORD dwFlags)
  240. { SMFORWARD(Initialize(psmc, uId, uIdAncestor, dwFlags)); }
  241. STDMETHODIMP CTrackShellMenu::GetMenuInfo(IShellMenuCallback** ppsmc, UINT* puId, UINT* puIdAncestor, DWORD* pdwFlags)
  242. { SMFORWARD(GetMenuInfo(ppsmc, puId, puIdAncestor, pdwFlags)); }
  243. STDMETHODIMP CTrackShellMenu::SetShellFolder(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HKEY hkey, DWORD dwFlags)
  244. { SMFORWARD(SetShellFolder(psf, pidlFolder, hkey, dwFlags)); }
  245. STDMETHODIMP CTrackShellMenu::GetShellFolder(DWORD* pdwFlags, LPITEMIDLIST* ppidl, REFIID riid, void** ppvObj)
  246. { SMFORWARD(GetShellFolder(pdwFlags, ppidl, riid, ppvObj)); }
  247. STDMETHODIMP CTrackShellMenu::SetMenu(HMENU hmenu, HWND hwnd, DWORD dwFlags)
  248. { SMFORWARD(SetMenu(hmenu, hwnd, dwFlags)); }
  249. STDMETHODIMP CTrackShellMenu::GetMenu(HMENU* phmenu, HWND* phwnd, DWORD* pdwFlags)
  250. { SMFORWARD(GetMenu(phmenu, phwnd, pdwFlags)); }
  251. STDMETHODIMP CTrackShellMenu::InvalidateItem(LPSMDATA psmd, DWORD dwFlags)
  252. { SMFORWARD(InvalidateItem(psmd, dwFlags)); }
  253. STDMETHODIMP CTrackShellMenu::GetState(LPSMDATA psmd)
  254. { SMFORWARD(GetState(psmd)); }
  255. STDMETHODIMP CTrackShellMenu::SetMenuToolbar(IUnknown* punk, DWORD dwFlags)
  256. { SMFORWARD(SetMenuToolbar(punk, dwFlags)); }
  257. STDMETHODIMP CTrackShellMenu::GetSubMenu(UINT idCmd, REFIID riid, void **ppvObj)
  258. {
  259. if (_psm2)
  260. {
  261. return _psm2->GetSubMenu(idCmd, riid, ppvObj);
  262. }
  263. else
  264. {
  265. return E_NOTIMPL;
  266. }
  267. }
  268. STDMETHODIMP CTrackShellMenu::SetToolbar([in] HWND hwnd, [in] DWORD dwFlags)
  269. {
  270. if (_psm2)
  271. {
  272. return _psm2->SetToolbar(hwnd, dwFlags);
  273. }
  274. else
  275. {
  276. return E_NOTIMPL;
  277. }
  278. }
  279. STDMETHODIMP CTrackShellMenu::SetMinWidth([in] int cxMenu)
  280. {
  281. if (_psm2)
  282. {
  283. return _psm2->SetMinWidth(cxMenu);
  284. }
  285. else
  286. {
  287. return E_NOTIMPL;
  288. }
  289. }
  290. STDMETHODIMP CTrackShellMenu::SetNoBorder([in] BOOL fNoBorder)
  291. {
  292. if (_psm2)
  293. {
  294. return _psm2->SetNoBorder(fNoBorder);
  295. }
  296. else
  297. {
  298. return E_NOTIMPL;
  299. }
  300. }
  301. STDMETHODIMP CTrackShellMenu::SetTheme([in] LPCWSTR pszTheme)
  302. {
  303. if (_psm2)
  304. {
  305. return _psm2->SetTheme(pszTheme);
  306. }
  307. else
  308. {
  309. return E_NOTIMPL;
  310. }
  311. }
  312. // *** ITrackShellMenu methods ***
  313. HRESULT CTrackShellMenu::SetObscured(HWND hwndTB, IUnknown* punkBand, DWORD dwSMSetFlags)
  314. {
  315. HRESULT hr = E_OUTOFMEMORY;
  316. // Make sure we created the Inner Shell Menu
  317. if (!_psm)
  318. return hr;
  319. if (punkBand &&
  320. SUCCEEDED(punkBand->QueryInterface(IID_PPV_ARG(IShellMenu, &_psmClient))))
  321. {
  322. UINT uId, uIdAncestor;
  323. DWORD dwFlags;
  324. IShellMenuCallback* psmcb;
  325. hr = _psmClient->GetMenuInfo(&psmcb, &uId, &uIdAncestor, &dwFlags);
  326. if (SUCCEEDED(hr))
  327. {
  328. IShellMenuCallback* psmcbClone = NULL;
  329. if (psmcb)
  330. {
  331. if (S_FALSE == psmcb->CallbackSM(NULL, SMC_GETOBJECT,
  332. (WPARAM)&IID_IShellMenuCallback,
  333. (LPARAM)(LPVOID*)&psmcbClone))
  334. {
  335. psmcbClone = psmcb;
  336. psmcbClone->AddRef();
  337. }
  338. }
  339. dwFlags &= ~SMINIT_HORIZONTAL;
  340. CShellMenuCallbackWrapper* psmcw = new CShellMenuCallbackWrapper(hwndTB, psmcbClone);
  341. // We want the bands to think it is:
  342. // Top level - because it has no menuband parent
  343. // Vertical - because it's not a menubar
  344. dwFlags |= SMINIT_TOPLEVEL | SMINIT_VERTICAL;
  345. hr = _psm->Initialize(psmcw, uId, ANCESTORDEFAULT, dwFlags);
  346. if (SUCCEEDED(hr))
  347. {
  348. HWND hwndOwner;
  349. HMENU hmenuObscured;
  350. hr = _psmClient->GetMenu(&hmenuObscured, &hwndOwner, NULL);
  351. if (SUCCEEDED(hr))
  352. {
  353. hr = _psm->SetMenu(hmenuObscured, hwndOwner, dwSMSetFlags | SMSET_DONTOWN); // Menuband takes ownership;
  354. }
  355. }
  356. if (psmcb)
  357. psmcb->Release();
  358. if (psmcbClone)
  359. psmcbClone->Release();
  360. if (psmcw)
  361. psmcw->Release();
  362. }
  363. }
  364. else
  365. {
  366. IShellMenu2 *psm2;
  367. hr = _psm->QueryInterface(IID_PPV_ARG(IShellMenu2, &psm2));
  368. if (SUCCEEDED(hr))
  369. {
  370. hr = psm2->SetToolbar(hwndTB, dwSMSetFlags);
  371. psm2->Release();
  372. }
  373. }
  374. return hr;
  375. }
  376. HRESULT CTrackShellMenu::Popup(HWND hwnd, POINTL *ppt, RECTL *prcExclude, DWORD dwFlags)
  377. {
  378. IMenuBand* pmb;
  379. HRESULT hres = E_INVALIDARG;
  380. if (!_psm)
  381. return hres;
  382. hres = _psm->QueryInterface(IID_PPV_ARG(IMenuBand, &pmb));
  383. if (FAILED(hres))
  384. return hres;
  385. HWND hwndParent = GetTopLevelAncestor(hwnd);
  386. // Did the user set a menu into the Shell Menu?
  387. HWND hwndSubclassed = NULL;
  388. GetMenu(NULL, &hwndSubclassed, NULL);
  389. if (hwndSubclassed == NULL)
  390. {
  391. // No; We need to artificially set one so that the message filtering and stuff works
  392. SetMenu(NULL, hwndParent, 0);
  393. }
  394. SetForegroundWindow(hwndParent);
  395. IMenuPopup* pmp;
  396. hres = CoCreateInstance(CLSID_MenuDeskBar, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IMenuPopup, &pmp));
  397. if (SUCCEEDED(hres))
  398. {
  399. IBandSite* pbs;
  400. hres = CoCreateInstance(CLSID_MenuBandSite, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IBandSite, &pbs));
  401. if (SUCCEEDED(hres))
  402. {
  403. hres = pmp->SetClient(pbs);
  404. if (SUCCEEDED(hres))
  405. {
  406. IDeskBand* pdb;
  407. hres = _psm->QueryInterface(IID_PPV_ARG(IDeskBand, &pdb));
  408. if (SUCCEEDED(hres))
  409. {
  410. hres = pbs->AddBand(pdb);
  411. pdb->Release();
  412. }
  413. }
  414. pbs->Release();
  415. }
  416. // If we've got a site ourselves, have MenuDeskBar use that.
  417. if (_punkSite)
  418. IUnknown_SetSite(pmp, _punkSite);
  419. if (SUCCEEDED(hres))
  420. {
  421. CMBMsgFilter* pmf = GetMessageFilter();
  422. void* pvContext = GetMessageFilter()->GetContext();
  423. hres = HookMenuWindow(hwndParent, pmb);
  424. if (SUCCEEDED(hres))
  425. {
  426. // This collapses any modal menus before we proceed. When switching between
  427. // Chevron menus, we need to collapse the previous menu. Refer to the comment
  428. // at the function definition.
  429. pmf->ForceModalCollapse();
  430. pmp->Popup(ppt, (LPRECTL)prcExclude, dwFlags);
  431. pmf->SetModal(TRUE);
  432. MSG msg;
  433. while (GetMessage(&msg, NULL, 0, 0))
  434. {
  435. HRESULT hres = pmb->IsMenuMessage(&msg);
  436. if (hres == E_FAIL)
  437. {
  438. // menuband says it's time to pack up and go home.
  439. // re-post this message so that it gets handled after
  440. // we've cleaned up the menu (avoid re-entrancy issues &
  441. // let rebar restore state of chevron button to unpressed)
  442. PostMessage(msg.hwnd, msg.message, msg.wParam, msg.lParam);
  443. break;
  444. }
  445. else if (hres != S_OK)
  446. {
  447. // menuband didn't handle this one
  448. TranslateMessage(&msg);
  449. DispatchMessage(&msg);
  450. }
  451. }
  452. hres = S_OK;
  453. UnHookMenuWindow(hwndParent);
  454. // We cannot change the context when modal, so unset the modal flag so that we can undo the context block.
  455. pmf->SetModal(FALSE);
  456. pmf->SetContext(pvContext, TRUE);
  457. }
  458. pmb->Release();
  459. }
  460. if (_psmClient)
  461. {
  462. // This is to fix a bug where if there is a cached ISHellMenu in the submenu,
  463. // and you share the callback (For example, Broweser menu callback and the
  464. // favorites menu being shared between the browser bar and the chevron menu)
  465. // when on menu collapsed, we were destroying the sub menu by doing a set site.
  466. // since we no longer do the set site on the sub menu, we need a way to say "Reset
  467. // your parent". and this is the best way.
  468. IUnknown_Exec(_psmClient, &CGID_MenuBand, MBANDCID_REFRESH, 0, NULL, NULL);
  469. }
  470. // This call is required regardless of whether we had a _punkSite above;
  471. // MenuDeskBar does its cleanup on SetSite(NULL).
  472. IUnknown_SetSite(pmp, NULL);
  473. pmp->Release();
  474. }
  475. return hres;
  476. }
  477. // *** IObjectWithSite methods ***
  478. HRESULT CTrackShellMenu::SetSite(IUnknown* punkSite)
  479. {
  480. ASSERT(NULL == punkSite || IS_VALID_CODE_PTR(punkSite, IUnknown));
  481. ATOMICRELEASE(_punkSite);
  482. _punkSite = punkSite;
  483. if (punkSite)
  484. punkSite->AddRef();
  485. return S_OK;
  486. }