Source code of Windows XP (NT5)
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.

4598 lines
139 KiB

  1. #include "shellprv.h"
  2. #define WANT_SHLWAPI_POSTSPLIT
  3. #include <shlwapi.h>
  4. #include "common.h"
  5. #include "initguid.h"
  6. #include "menuband.h"
  7. #include "bands.h"
  8. #include "isfband.h"
  9. #include "dpastuff.h" // COrderList_*
  10. #include "resource.h"
  11. #include "oleacc.h"
  12. #include "apithk.h"
  13. #include "uemapp.h"
  14. #include "mnbase.h"
  15. #include "mnfolder.h"
  16. #include "mnstatic.h"
  17. #include "iaccess.h"
  18. #include "util.h"
  19. #include "tbmenu.h"
  20. // NOTE: Conflicts with one defined in winuserp.h
  21. #undef WINEVENT_VALID //It's tripping on this...
  22. #include "winable.h"
  23. #define DM_MISC 0 // miscellany
  24. #define PF_USINGNTSD 0x00000400 // set this if you're debugging on ntsd
  25. // This must be reset to -1 on any WM_WININICHANGE. We do it in
  26. // shbrows2.cpp, but if there are no browser windows open when the
  27. // metric changes, we end up running around with a stale value. Oh well.
  28. long g_lMenuPopupTimeout = -1;
  29. // {AD35F50A-0CC0-11d3-AE2D-00C04F8EEA99}
  30. static const CLSID CLSID_MenuBandMetrics =
  31. { 0xad35f50a, 0xcc0, 0x11d3, { 0xae, 0x2d, 0x0, 0xc0, 0x4f, 0x8e, 0xea, 0x99
  32. } };
  33. // Registered window messages for the menuband
  34. UINT g_nMBPopupOpen = 0;
  35. UINT g_nMBFullCancel = 0;
  36. UINT g_nMBDragCancel = 0;
  37. UINT g_nMBAutomation = 0;
  38. UINT g_nMBExecute = 0;
  39. UINT g_nMBOpenChevronMenu = 0;
  40. HCURSOR g_hCursorArrow = NULL;
  41. //UINT g_nMBIgnoreNextDeselect = 0; // Dealt with in menuisf.cpp
  42. BOOL IsAncestor(HWND hwndChild, HWND hwndAncestor)
  43. {
  44. HWND hwnd = hwndChild;
  45. while (hwnd != hwndAncestor && hwnd != NULL)
  46. {
  47. hwnd = GetParent(hwnd);
  48. }
  49. return hwndAncestor == hwnd;
  50. }
  51. //=================================================================
  52. // Implementation of menuband message filter
  53. //=================================================================
  54. extern "C" void DumpMsg(LPCTSTR pszLabel, MSG * pmsg);
  55. // Just one of these, b/c we only need one message filter
  56. CMBMsgFilter g_msgfilter = { 0 };
  57. static DWORD g_tlsMessageFilter = -1;
  58. CMBMsgFilter* GetMessageFilter()
  59. {
  60. CMBMsgFilter* pmf = NULL;
  61. if (g_tlsMessageFilter == -1)
  62. {
  63. DWORD tls = TlsAlloc();
  64. if (tls != -1)
  65. {
  66. InterlockedExchange((LONG*)&g_tlsMessageFilter, tls);
  67. if (tls != g_tlsMessageFilter)
  68. {
  69. TlsFree(tls);
  70. }
  71. }
  72. }
  73. if (g_tlsMessageFilter != -1)
  74. {
  75. pmf = (CMBMsgFilter*)TlsGetValue(g_tlsMessageFilter);
  76. if (pmf == NULL)
  77. {
  78. pmf = new CMBMsgFilter;
  79. if (pmf)
  80. {
  81. pmf->_fAllocated = TRUE;
  82. TlsSetValue(g_tlsMessageFilter, pmf);
  83. }
  84. }
  85. }
  86. if (pmf == NULL)
  87. pmf = &g_msgfilter;
  88. return pmf;
  89. }
  90. void FreeMessageFilter(CMBMsgFilter* that)
  91. {
  92. if (g_tlsMessageFilter != -1)
  93. {
  94. CMBMsgFilter* pmf = (CMBMsgFilter*)TlsGetValue(g_tlsMessageFilter);
  95. if (pmf == that)
  96. {
  97. TlsSetValue(g_tlsMessageFilter, NULL);
  98. }
  99. }
  100. }
  101. void CMBMsgFilter::AddRef()
  102. {
  103. _cRef++;
  104. }
  105. void CMBMsgFilter::Release()
  106. {
  107. _cRef--;
  108. if (_cRef <= 0 && _fAllocated)
  109. {
  110. FreeMessageFilter(this);
  111. delete this;
  112. }
  113. }
  114. void CMBMsgFilter::SetModal(BOOL fModal)
  115. {
  116. // There was an interesting problem:
  117. // Click on the Chevron menu. Right click Delete.
  118. // The menus were hosed
  119. // Why?
  120. // Well, I'll tell you:
  121. // We got a deactivate on the subclassed window. We have
  122. // 2 menus subclassing it: The Main menu, and the modal
  123. // chevron menu. Problem is, the main menu snagged the WM_ACTIVATE
  124. // and does a set context. This causes a Pop and releases the Message hook.
  125. // Since I still had a menu up, this caused havoc.
  126. // So I introduced a concept of a "Modal" menuband.
  127. // This says: "Ignore any request to change contexts until I'm done". When
  128. // that modal band is done, it sets the old context back in.
  129. // Seems like a hack, but we need a better underlying archtecture for
  130. // the message passing.
  131. _fModal = fModal;
  132. }
  133. void CMBMsgFilter::ReEngage(void* pvContext)
  134. {
  135. // We need to make sure that we don't dis/reengage when
  136. // switching contexts
  137. if (pvContext == _pvContext)
  138. _fEngaged = TRUE;
  139. }
  140. void CMBMsgFilter::DisEngage(void* pvContext)
  141. {
  142. if (pvContext == _pvContext)
  143. _fEngaged = FALSE;
  144. }
  145. int CMBMsgFilter::GetCount()
  146. {
  147. return FDSA_GetItemCount(&_fdsa);
  148. }
  149. int MsgFilter_GetCount()
  150. {
  151. return GetMessageFilter()->GetCount();
  152. }
  153. CMenuBand * CMBMsgFilter::_GetTopPtr(void)
  154. {
  155. CMenuBand * pmb = NULL;
  156. int cItems = FDSA_GetItemCount(&_fdsa);
  157. if (0 < cItems)
  158. {
  159. MBELEM * pmbelem = FDSA_GetItemPtr(&_fdsa, cItems-1, MBELEM);
  160. pmb = pmbelem->pmb;
  161. }
  162. return pmb;
  163. }
  164. CMenuBand * CMBMsgFilter::_GetBottomMostSelected(void)
  165. {
  166. // Ick, I can't believe I just did this. Mix COM and C++ identities... Yuck.
  167. CMenuBand* pmb = NULL;
  168. if (_pmb)
  169. {
  170. IUnknown_QueryService(SAFECAST(_pmb, IMenuBand*), SID_SMenuBandBottomSelected, CLSID_MenuBand, (void**)&pmb);
  171. // Since we have the C++ identity, release the COM identity.
  172. if (pmb)
  173. pmb->Release();
  174. }
  175. return pmb;
  176. }
  177. CMenuBand * CMBMsgFilter::_GetWindowOwnerPtr(HWND hwnd)
  178. {
  179. CMenuBand * pmb = NULL;
  180. int cItems = FDSA_GetItemCount(&_fdsa);
  181. if (0 < cItems)
  182. {
  183. // Go thru the list of bands on the stack and return the
  184. // one who owns the given window.
  185. int i;
  186. for (i = 0; i < cItems; i++)
  187. {
  188. MBELEM * pmbelem = FDSA_GetItemPtr(&_fdsa, i, MBELEM);
  189. if (pmbelem->pmb && S_OK == pmbelem->pmb->IsWindowOwner(hwnd))
  190. {
  191. pmb = pmbelem->pmb;
  192. break;
  193. }
  194. }
  195. }
  196. return pmb;
  197. }
  198. /*----------------------------------------------------------
  199. Purpose: Return menuband or NULL based upon hittest. pt must be
  200. in screen coords
  201. */
  202. CMenuBand * CMBMsgFilter::_HitTest(POINT pt, HWND * phwnd)
  203. {
  204. CMenuBand * pmb = NULL;
  205. HWND hwnd = NULL;
  206. int cItems = FDSA_GetItemCount(&_fdsa);
  207. if (0 < cItems)
  208. {
  209. // Go thru the list of bands on the stack and return the
  210. // one who owns the given window. Work backwards since the
  211. // later bands are on top (z-order), if the menus ever overlap.
  212. int i = cItems - 1;
  213. while (0 <= i)
  214. {
  215. MBELEM * pmbelem = FDSA_GetItemPtr(&_fdsa, i, MBELEM);
  216. RECT rc;
  217. // Do this dynamically because the hwndBar hasn't been positioned
  218. // until after this mbelem has been pushed onto the msg filter stack.
  219. GetWindowRect(pmbelem->hwndBar, &rc);
  220. if (PtInRect(&rc, pt))
  221. {
  222. pmb = pmbelem->pmb;
  223. hwnd = pmbelem->hwndTB;
  224. break;
  225. }
  226. i--;
  227. }
  228. }
  229. if (phwnd)
  230. *phwnd = hwnd;
  231. return pmb;
  232. }
  233. void CMBMsgFilter::RetakeCapture(void)
  234. {
  235. // The TrackPopupMenu submenus can steal the capture. Take
  236. // it back. Don't take it back if the we're in edit mode,
  237. // because the modal drag/drop loop has the capture at that
  238. // point.
  239. // We do not want to take capture unless we are engaged.
  240. // We need to do this because we are not handling mouse messages lower down
  241. // in the code. When we set the capture, the messages that we do not handle
  242. // trickle up to the top level menu, and can cause weird problems (Such
  243. // as signaling a "click out of bounds" or a context menu of the ITBar)
  244. if (_hwndCapture && !_fPreventCapture && _fEngaged)
  245. {
  246. TraceMsg(TF_MENUBAND, "CMBMsgFilter: Setting capture to %#lx", _hwndCapture);
  247. SetCapture(_hwndCapture);
  248. }
  249. }
  250. void CMBMsgFilter::SetHook(BOOL fSet, BOOL fDontIgnoreSysChar)
  251. {
  252. if (fDontIgnoreSysChar)
  253. _iSysCharStack += fSet? 1: -1;
  254. if (NULL == _hhookMsg && fSet)
  255. {
  256. TraceMsg(TF_MENUBAND, "CMBMsgFilter: Initialize");
  257. _hhookMsg = SetWindowsHookEx(WH_GETMESSAGE, GetMsgHook, HINST_THISDLL, GetCurrentThreadId());
  258. _fDontIgnoreSysChar = fDontIgnoreSysChar;
  259. }
  260. else if (!fSet && _iSysCharStack == 0)
  261. {
  262. TraceMsg(TF_MENUBAND, "CMBMsgFilter: Hook removed");
  263. if (_hhookMsg)
  264. {
  265. UnhookWindowsHookEx(_hhookMsg);
  266. _hhookMsg = NULL;
  267. }
  268. }
  269. }
  270. // 1) Set Deskbars on Both Monitors and set to chevron
  271. // 2) On Monitor #2 open a chevron
  272. // 3) On Monitor #1 open a chevron then open the Start Menu
  273. // Result: Start Menu does not work.
  274. // The reason is, we set the _fModal of the global message filter. This prevents context switches. Why?
  275. // The modal flag was invented to solve context switching problems with the browser frame. So what causes this?
  276. // Well, when switching from #2 to #3, we have not switched contexts. But since we got a click out of bounds, we collapse
  277. // the previous menu. When switching from #3 to #4, neither have the context, so things get messy.
  278. void CMBMsgFilter::ForceModalCollapse()
  279. {
  280. if (_fModal)
  281. {
  282. _fModal = FALSE;
  283. SetContext(NULL, TRUE);
  284. }
  285. }
  286. void CMBMsgFilter::SetContext(void* pvContext, BOOL fSet)
  287. {
  288. TraceMsg(TF_MENUBAND, "CMBMsgFilter::SetContext from 0x%x to 0x%x", _pvContext, pvContext);
  289. // When changing a menuband context, we need to pop all of the items
  290. // in the stack. This is to prevent a race condition that can occur.
  291. // We do not want to pop all of the items off the stack if we're setting the same context.
  292. // We do a set context on Activation, Both when we switch from one Browser frame to another
  293. // but also when right clicking or causing the Rename dialog to be displayed.
  294. BOOL fPop = FALSE;
  295. if (_fModal)
  296. return;
  297. // Are we setting a new context?
  298. if (fSet)
  299. {
  300. // Is this different than the one we've got?
  301. if (pvContext != _pvContext)
  302. {
  303. // Yes, then we need to pop off all of the old items.
  304. fPop = TRUE;
  305. }
  306. _pvContext = pvContext;
  307. }
  308. else
  309. {
  310. // Then we are trying to unset the message hook. Make sure it still belongs to
  311. // this context
  312. if (pvContext == _pvContext)
  313. {
  314. // This context is trying to unset itself, and no other context owns it.
  315. // remove all the old items.
  316. fPop = TRUE;
  317. }
  318. }
  319. if (fPop)
  320. {
  321. CMenuBand* pcmb = _GetTopPtr();
  322. if (pcmb)
  323. {
  324. PostMessage(pcmb->_pmbState->GetSubclassedHWND(), g_nMBFullCancel, 0, 0);
  325. // No release.
  326. if (FDSA_GetItemCount(&_fdsa) != 0)
  327. {
  328. CMBMsgFilter* pmf = GetMessageFilter();
  329. while (pmf->Pop(pvContext))
  330. ;
  331. }
  332. }
  333. }
  334. }
  335. /*----------------------------------------------------------
  336. Purpose: Push another menuband onto the message filter's stack
  337. */
  338. void CMBMsgFilter::Push(void* pvContext, CMenuBand * pmb, IUnknown * punkSite)
  339. {
  340. ASSERT(IS_VALID_CODE_PTR(pmb, CMenuBand));
  341. TraceMsg(TF_MENUBAND, "CMBMsgFilter::Push called from context 0x%x", pvContext);
  342. if (pmb && pvContext == _pvContext)
  343. {
  344. BOOL bRet = TRUE;
  345. HWND hwndBand;
  346. pmb->GetWindow(&hwndBand);
  347. // If the bar isn't available use the band window
  348. HWND hwndBar = hwndBand;
  349. IOleWindow * pow;
  350. IUnknown_QueryService(punkSite, SID_SMenuPopup, IID_PPV_ARG(IOleWindow, &pow));
  351. if (pow)
  352. {
  353. pow->GetWindow(&hwndBar);
  354. pow->Release();
  355. }
  356. if (NULL == _hhookMsg)
  357. {
  358. // We want to ignore the WM_SYSCHAR message in the message filter because
  359. // we are using the IsMenuMessage call instead of the global message hook.
  360. SetHook(TRUE, FALSE);
  361. TraceMsg(TF_MENUBAND, "CMBMsgFilter::push Setting hook from context 0x%x", pvContext);
  362. _fSetAtPush = TRUE;
  363. }
  364. if (!_fInitialized)
  365. {
  366. ASSERT(NULL == _hwndCapture);
  367. _hwndCapture = hwndBar;
  368. _fInitialized = TRUE;
  369. bRet = FDSA_Initialize(sizeof(MBELEM), CMBELEM_GROW, &_fdsa, _rgmbelem, CMBELEM_INIT);
  370. // We need to initialize this for the top level guy so that we have the correct positioning
  371. // from the start of this new set of bands. This is used to eliminate spurious WM_MOUSEMOVE
  372. // messages which cause problems. See _HandleMouseMessages for more information
  373. AcquireMouseLocation();
  374. }
  375. if (EVAL(bRet))
  376. {
  377. MBELEM mbelem = {0};
  378. TraceMsg(TF_MENUBAND, "CMBMsgFilter: Push (pmp:%#08lx) onto stack", SAFECAST(pmb, IMenuPopup *));
  379. pmb->AddRef();
  380. mbelem.pmb = pmb;
  381. mbelem.hwndTB = hwndBand;
  382. mbelem.hwndBar = hwndBar;
  383. FDSA_AppendItem(&_fdsa, &mbelem);
  384. CMenuBand* pmbTop = _GetTopPtr();
  385. if ((pmbTop && (pmbTop->GetFlags() & SMINIT_LEGACYMENU)) || NULL == GetCapture())
  386. RetakeCapture();
  387. }
  388. else
  389. {
  390. UnhookWindowsHookEx(_hhookMsg);
  391. _hhookMsg = NULL;
  392. _hwndCapture = NULL;
  393. }
  394. }
  395. }
  396. /*----------------------------------------------------------
  397. Purpose: Pop a menuband off the message filter stack
  398. Returns the number of bands left on the stack
  399. */
  400. int CMBMsgFilter::Pop(void* pvContext)
  401. {
  402. int nRet = 0;
  403. TraceMsg(TF_MENUBAND, "CMBMsgFilter::pop called from context 0x%x", pvContext);
  404. // This can be called from a context switch or when we're exiting menu mode,
  405. // so we'll switch off the fact that we clear _hhookMsg when we pop the top twice.
  406. if (pvContext == _pvContext && _hhookMsg)
  407. {
  408. int iItem = FDSA_GetItemCount(&_fdsa) - 1;
  409. MBELEM * pmbelem;
  410. ASSERT(0 <= iItem);
  411. pmbelem = FDSA_GetItemPtr(&_fdsa, iItem, MBELEM);
  412. if (EVAL(pmbelem->pmb))
  413. {
  414. TraceMsg(TF_MENUBAND, "CMBMsgFilter: Pop (pmb=%#08lx) off stack", SAFECAST(pmbelem->pmb, IMenuPopup *));
  415. pmbelem->pmb->Release();
  416. pmbelem->pmb = NULL;
  417. }
  418. FDSA_DeleteItem(&_fdsa, iItem);
  419. if (0 == iItem)
  420. {
  421. TraceMsg(TF_MENUBAND, "CMBMsgFilter::pop removing hook from context 0x%x", pvContext);
  422. if (_fSetAtPush)
  423. SetHook(FALSE, FALSE);
  424. PreventCapture(FALSE);
  425. _fInitialized = FALSE;
  426. if (_hwndCapture && GetCapture() == _hwndCapture)
  427. {
  428. TraceMsg(TF_MENUBAND, "CMBMsgFilter: Releasing capture");
  429. ReleaseCapture();
  430. }
  431. _hwndCapture = NULL;
  432. }
  433. nRet = iItem;
  434. }
  435. return nRet;
  436. }
  437. LRESULT CMBMsgFilter::_HandleMouseMsgs(MSG * pmsg, BOOL bRemove)
  438. {
  439. LRESULT lRet = 0;
  440. CMenuBand * pmb;
  441. HWND hwnd = GetCapture();
  442. // Do we still have the capture?
  443. if (hwnd != _hwndCapture)
  444. {
  445. // No; is it b/c a CTrackPopupBar has it?
  446. #if 0 // Nuke this trace because I was getting annoyed.
  447. //def DEBUG
  448. pmb = _GetTopPtr();
  449. if (!EVAL(pmb) || !pmb->IsInSubMenu())
  450. {
  451. // No
  452. TraceMsg(TF_WARNING, "CMBMsgFilter: someone else has the capture (%#lx)", hwnd);
  453. }
  454. #endif
  455. if (NULL == hwnd)
  456. {
  457. // There are times that we must retake the capture because
  458. // TrackPopupMenuEx has taken it, or some context menu
  459. // might have taken it, so take it back.
  460. RetakeCapture();
  461. TraceMsg(TF_WARNING, "CMBMsgFilter: taking the capture back");
  462. }
  463. }
  464. else
  465. {
  466. // Yes; decide what to do with it
  467. POINT pt;
  468. HWND hwndPt;
  469. MSG msgT;
  470. pt.x = GET_X_LPARAM(pmsg->lParam);
  471. pt.y = GET_Y_LPARAM(pmsg->lParam);
  472. ClientToScreen(pmsg->hwnd, &pt);
  473. if (WM_MOUSEMOVE == pmsg->message)
  474. {
  475. // The mouse cursor can send repeated WM_MOUSEMOVE messages
  476. // with the same coordinates. When the user tries to navigate
  477. // thru the menus with the keyboard, and the mouse cursor
  478. // happens to be over a menu item, these spurious mouse
  479. // messages cause us to think the menu has been invoked under
  480. // the mouse cursor.
  481. //
  482. // To avoid this unpleasant rudeness, we eat any gratuitous
  483. // WM_MOUSEMOVE messages.
  484. if (_ptLastMove.x == pt.x && _ptLastMove.y == pt.y)
  485. {
  486. pmsg->message = WM_NULL;
  487. goto Bail;
  488. }
  489. // Since this is not a duplicate point, we need to keep it around.
  490. // We will use this stored point for the above comparison
  491. // msadek; W2k bug# 426005
  492. // On a mirrored system, we got a system bug as mouse coordinates has an off-by-one
  493. // This makes comparing the value with what we got from GetCursorPos() always fail.
  494. // Do not use AcquireMouseLocation().
  495. if(!IS_WINDOW_RTL_MIRRORED(pmsg->hwnd))
  496. {
  497. AcquireMouseLocation();
  498. }
  499. else
  500. {
  501. _ptLastMove.x = pt.x;
  502. _ptLastMove.y = pt.y;
  503. }
  504. if (_hcurArrow == NULL)
  505. _hcurArrow = LoadCursor(NULL, IDC_ARROW);
  506. if (GetCursor() != _hcurArrow)
  507. SetCursor(_hcurArrow);
  508. }
  509. // Use a stack variable b/c we don't want to confuse USER32
  510. // by changing the coords of the real message.
  511. msgT = *pmsg;
  512. msgT.lParam = MAKELPARAM(pt.x, pt.y);
  513. pmb = _HitTest(pt, &hwndPt);
  514. if (_TopFilterMouseMessage(&msgT, bRemove, pmb) == S_OK)
  515. {
  516. // Remember the changed message (if there was one)
  517. pmsg->message = msgT.message;
  518. }
  519. else if (pmb)
  520. {
  521. // Forward mouse message onto appropriate menuband. Note
  522. // the appropriate menuband's GetMsgFilterCB (below) will call
  523. // ScreenToClient to convert the coords correctly.
  524. lRet = pmb->GetMsgFilterCB(&msgT, bRemove);
  525. // Remember the changed message (if there was one)
  526. pmsg->message = msgT.message;
  527. }
  528. // Debug note: to debug menubands on ntsd, set the prototype
  529. // flag accordingly. This will keep menubands from going
  530. // away the moment the focus changes to the NTSD window.
  531. else if ((WM_LBUTTONDOWN == pmsg->message || WM_RBUTTONDOWN == pmsg->message) &&
  532. !(g_dwPrototype & PF_USINGNTSD))
  533. {
  534. // Mouse down happened outside the menu. Bail.
  535. pmb = _GetTopPtr();
  536. if (EVAL(pmb))
  537. {
  538. msgT.hwnd = pmsg->hwnd;
  539. msgT.message = g_nMBFullCancel;
  540. msgT.wParam = 0;
  541. msgT.lParam = 0;
  542. TraceMsg(TF_MENUBAND, "CMBMsgFilter (pmb=%#08lx): hittest outside, bailing", SAFECAST(pmb, IMenuPopup *));
  543. pmb->GetMsgFilterCB(&msgT, bRemove);
  544. }
  545. #if 0
  546. // Now send the message to the originally intended window
  547. SendMessage(pmsg->hwnd, pmsg->message, pmsg->wParam, pmsg->lParam);
  548. #endif
  549. }
  550. else
  551. {
  552. pmb = _GetTopPtr();
  553. if (pmb)
  554. {
  555. IUnknown_QueryServiceExec(SAFECAST(pmb, IOleCommandTarget*), SID_SMenuBandBottom,
  556. &CGID_MenuBand, MBANDCID_SELECTITEM, MBSI_NONE, NULL, NULL);
  557. }
  558. }
  559. }
  560. Bail:
  561. return lRet;
  562. }
  563. HRESULT CMBMsgFilter::_TopFilterMouseMessage(MSG *pmsg, BOOL bRemove, CMenuBand *pmbTarget)
  564. {
  565. CMenuBand *pmb = _GetTopPtr();
  566. if (pmb && pmb->_psmcb)
  567. {
  568. // This is a high-frequency message so we handle the callback
  569. // ourselves. (_CallCB will allocate memory.)
  570. SMDATA smd = {0};
  571. if (pmbTarget)
  572. {
  573. smd.punk = SAFECAST(pmbTarget, IShellMenu*);
  574. smd.uIdParent = pmbTarget->_uId;
  575. smd.uIdAncestor = pmbTarget->_uIdAncestor;
  576. smd.hwnd = pmbTarget->_hwnd;
  577. smd.hmenu = pmbTarget->_hmenu;
  578. smd.pvUserData = pmbTarget->_pvUserData;
  579. }
  580. return pmb->_psmcb->CallbackSM(&smd, SMC_MOUSEFILTER, bRemove, (LPARAM)pmsg);
  581. }
  582. return S_FALSE; // not handled
  583. }
  584. /*----------------------------------------------------------
  585. Purpose: Message hook used to track keyboard and mouse messages
  586. while the menuband is "active".
  587. The menuband can't steal the focus away -- we use this
  588. hook to catch messages.
  589. */
  590. LRESULT CMBMsgFilter::GetMsgHook(int nCode, WPARAM wParam, LPARAM lParam)
  591. {
  592. LRESULT lRet = 0;
  593. MSG * pmsg = (MSG *)lParam;
  594. BOOL bRemove = (PM_REMOVE == wParam);
  595. CMBMsgFilter* pmf = GetMessageFilter();
  596. // The global message filter may be in a state when we are not processing messages,
  597. // but the menubands are still displayed. A situation where this will occur is when
  598. // a dialog box is displayed because of an interaction with the menus.
  599. // Are we engaged? (Are we allowed to process messages?)
  600. if (pmf->_fEngaged)
  601. {
  602. if (WM_SYSCHAR == pmsg->message)
  603. {
  604. // _fDontIgnoreSysChar is set when the Menubands ONLY want to know about
  605. // WM_SYSCHAR and nothing else.
  606. if (pmf->_fDontIgnoreSysChar)
  607. {
  608. CMenuBand * pmb = pmf->GetTopMostPtr();
  609. if (pmb)
  610. lRet = pmb->GetMsgFilterCB(pmsg, bRemove);
  611. }
  612. }
  613. else if (pmf->_fInitialized) // Only filter if we are initalized (have items on the stack)
  614. {
  615. switch (nCode)
  616. {
  617. case HC_ACTION:
  618. #ifdef DEBUG
  619. if (g_dwDumpFlags & DF_GETMSGHOOK)
  620. DumpMsg(TEXT("GetMsg"), pmsg);
  621. #endif
  622. // A lesson about GetMsgHook: it gets the same message
  623. // multiple times for as long as someone calls PeekMessage
  624. // with the PM_NOREMOVE flag. So we want to take action
  625. // only when PM_REMOVE is set (so we don't handle more than
  626. // once). If we modify any messages to redirect them (on a
  627. // regular basis), we must modify all the time so we don't
  628. // confuse the app.
  629. // Messages get redirected to different bands in the stack
  630. // in this way:
  631. //
  632. // 1) Keyboard messages go to the currently open submenu
  633. // (topmost on the stack).
  634. //
  635. // 2) The PopupOpen message goes to the hwnd that belongs
  636. // to the menu band (via IsWindowOwner).
  637. //
  638. switch (pmsg->message)
  639. {
  640. case WM_SYSKEYDOWN:
  641. case WM_KEYDOWN:
  642. case WM_CHAR:
  643. case WM_KEYUP:
  644. case WM_CLOSE: // only this message filter gets WM_CLOSE
  645. {
  646. // There is a situation that can occur when the last selected
  647. // menu pane is NOT the bottom most pane.
  648. // We need to see if that last selected guy is tracking a context
  649. // menu so that we forward the messages correctly.
  650. CMenuBand * pmb = pmf->_GetBottomMostSelected();
  651. if (pmb)
  652. {
  653. // Is it tracking a context menu?
  654. if (S_OK == IUnknown_Exec(SAFECAST(pmb, IMenuBand*), &CGID_MenuBand,
  655. MBANDCID_ISTRACKING, 0, NULL, NULL))
  656. {
  657. // Yes, forward for proper handling.
  658. lRet = pmb->GetMsgFilterCB(pmsg, bRemove);
  659. }
  660. else
  661. {
  662. // No; Then do the default processing. This can happen if there is no
  663. // context menu, but there is a selected parent and not a selected child.
  664. goto TopHandler;
  665. }
  666. }
  667. else
  668. {
  669. TopHandler:
  670. pmb = pmf->_GetTopPtr();
  671. if (pmb)
  672. lRet = pmb->GetMsgFilterCB(pmsg, bRemove);
  673. }
  674. }
  675. break;
  676. case WM_NULL:
  677. // Handle this here (we do nothing) to avoid mistaking this for
  678. // g_nMBPopupOpen below, in case g_nMBPopupOpen is 0 if
  679. // RegisterWindowMessage fails.
  680. break;
  681. default:
  682. if (bRemove && IsInRange(pmsg->message, WM_MOUSEFIRST, WM_MOUSELAST))
  683. {
  684. lRet = pmf->_HandleMouseMsgs(pmsg, bRemove);
  685. }
  686. else if (pmsg->message == g_nMBPopupOpen)
  687. {
  688. CMenuBand * pmb = pmf->_GetWindowOwnerPtr(pmsg->hwnd);
  689. if (pmb)
  690. lRet = pmb->GetMsgFilterCB(pmsg, bRemove);
  691. }
  692. else if (pmsg->message == g_nMBExecute)
  693. {
  694. CMenuBand * pmb = pmf->_GetWindowOwnerPtr(pmsg->hwnd);
  695. if (pmb)
  696. {
  697. VARIANT var;
  698. var.vt = VT_UINT_PTR;
  699. var.ullVal = (UINT_PTR)pmsg->hwnd;
  700. pmb->Exec(&CGID_MenuBand, MBANDCID_EXECUTE, (DWORD)pmsg->wParam, &var, NULL);
  701. }
  702. }
  703. break;
  704. }
  705. break;
  706. default:
  707. if (0 > nCode)
  708. return CallNextHookEx(pmf->_hhookMsg, nCode, wParam, lParam);
  709. break;
  710. }
  711. }
  712. }
  713. // Pass it on to the next hook in the chain
  714. if (0 == lRet)
  715. return CallNextHookEx(pmf->_hhookMsg, nCode, wParam, lParam);
  716. return 0; // Always return 0
  717. }
  718. //=================================================================
  719. // Implementation of CMenuBand
  720. //=================================================================
  721. // Struct used by EXEC with a MBANDCID_GETFONTS to return fonts
  722. typedef struct tagMBANDFONTS
  723. {
  724. HFONT hFontMenu; // [out] TopLevelMenuBand's menu font
  725. HFONT hFontArrow; // [out] TopLevelMenuBand's font for drawing the cascade arrow
  726. int cyArrow; // [out] Height of TopLevelMenuBand's cascade arrow
  727. int cxArrow; // [out] Width of TopLevelMenuBand's cascade arrow
  728. int cxMargin; // [out] Margin b/t text and arrow
  729. } MBANDFONTS;
  730. #define THISCLASS CMenuBand
  731. #define SUPERCLASS CToolBand
  732. #ifdef DEBUG
  733. int g_nMenuLevel = 0;
  734. #define DBG_THIS _nMenuLevel, SAFECAST(this, IMenuPopup *)
  735. #else
  736. #define DBG_THIS 0, 0
  737. #endif
  738. CMenuBand::CMenuBand() :
  739. SUPERCLASS()
  740. {
  741. GetMessageFilter()->AddRef();
  742. _fCanFocus = TRUE;
  743. _fAppActive = TRUE;
  744. _nItemNew = -1;
  745. _nItemCur = -1;
  746. _nItemTimer = -1;
  747. _uIconSize = ISFBVIEWMODE_SMALLICONS;
  748. _uIdAncestor = ANCESTORDEFAULT;
  749. _nItemSubMenu = -1;
  750. }
  751. // The purpose of this method is to finish initializing Menubands,
  752. // since it can be initialized in many ways.
  753. HRESULT CMenuBand::_Initialize(DWORD dwFlags)
  754. {
  755. _fVertical = !BOOLIFY(dwFlags & SMINIT_HORIZONTAL);
  756. _fTopLevel = BOOLIFY(dwFlags & SMINIT_TOPLEVEL);
  757. _dwFlags = dwFlags;
  758. // We cannot have a horizontal menu if it is not the toplevel menu
  759. ASSERT(!_fVertical && _fTopLevel || _fVertical);
  760. HRESULT hr = S_OK;
  761. if (_fTopLevel)
  762. {
  763. if (!g_nMBPopupOpen)
  764. {
  765. g_nMBPopupOpen = RegisterWindowMessage(TEXT("CMBPopupOpen"));
  766. g_nMBFullCancel = RegisterWindowMessage(TEXT("CMBFullCancel"));
  767. g_nMBDragCancel = RegisterWindowMessage(TEXT("CMBDragCancel"));
  768. g_nMBAutomation = RegisterWindowMessage(TEXT("CMBAutomation"));
  769. g_nMBExecute = RegisterWindowMessage(TEXT("CMBExecute"));
  770. g_nMBOpenChevronMenu = RegisterWindowMessage(TEXT("CMBOpenChevronMenu"));
  771. g_hCursorArrow = LoadCursor(NULL, IDC_ARROW);
  772. TraceMsg(TF_MENUBAND, "CMBPopupOpen message = %#lx", g_nMBPopupOpen);
  773. TraceMsg(TF_MENUBAND, "CMBFullCancel message = %#lx", g_nMBFullCancel);
  774. }
  775. if (!_pmbState)
  776. _pmbState = new CMenuBandState;
  777. if (!_pmbm)
  778. _pmbm = new CMenuBandMetrics();
  779. if (!_pmbState || !_pmbm)
  780. {
  781. hr = E_OUTOFMEMORY;
  782. }
  783. }
  784. DEBUG_CODE( _nMenuLevel = -1; )
  785. return hr;
  786. }
  787. CMenuBand::~CMenuBand()
  788. {
  789. CMBMsgFilter* pmf = GetMessageFilter();
  790. // the message filter does not have a ref'd pointer to us!!!
  791. if (pmf->GetTopMostPtr() == this)
  792. pmf->SetTopMost(NULL);
  793. _CallCB(SMC_DESTROY);
  794. ATOMICRELEASE(_psmcb);
  795. // Cleanup
  796. CloseDW(0);
  797. if (_pmtbMenu)
  798. delete _pmtbMenu;
  799. if (_pmtbShellFolder)
  800. delete _pmtbShellFolder;
  801. Str_SetPtr(&_pszTheme, NULL);
  802. ASSERT(_punkSite == NULL);
  803. ATOMICRELEASE(_pmpTrackPopup);
  804. ATOMICRELEASE(_pmbm);
  805. if (_fTopLevel)
  806. {
  807. if (_pmbState)
  808. delete _pmbState;
  809. }
  810. GetMessageFilter()->Release();
  811. }
  812. /*----------------------------------------------------------
  813. Purpose: Create-instance function for class factory
  814. */
  815. HRESULT CMenuBand_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void **ppv)
  816. {
  817. // aggregation checking is handled in class factory
  818. HRESULT hr = E_OUTOFMEMORY;
  819. CMenuBand *pObj = new CMenuBand();
  820. if (pObj)
  821. {
  822. hr = pObj->QueryInterface(riid, ppv);
  823. pObj->Release();
  824. }
  825. return hr;
  826. }
  827. CMenuBand * CMenuBand_Create(IShellFolder* psf, LPCITEMIDLIST pidl,
  828. BOOL bHorizontal)
  829. {
  830. CMenuBand * pmb = NULL;
  831. if (psf || pidl)
  832. {
  833. DWORD dwFlags = bHorizontal ? (SMINIT_HORIZONTAL | SMINIT_TOPLEVEL) : 0;
  834. pmb = new CMenuBand();
  835. if (pmb)
  836. {
  837. pmb->_Initialize(dwFlags);
  838. pmb->SetShellFolder(psf, pidl, NULL, 0);
  839. }
  840. }
  841. return pmb;
  842. }
  843. void CMenuBand::_UpdateButtons()
  844. {
  845. if (_pmtbMenu)
  846. _pmtbMenu->v_UpdateButtons(FALSE);
  847. if (_pmtbShellFolder)
  848. _pmtbShellFolder->v_UpdateButtons(FALSE);
  849. _fForceButtonUpdate = FALSE;
  850. }
  851. HRESULT CMenuBand::ForwardChangeNotify(LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  852. {
  853. // Given a change notify from the ShellFolder child, we will forward that notify to each of our
  854. // sub menus, but only if they have a shell folder child.
  855. HRESULT hres = E_FAIL;
  856. BOOL fDone = FALSE;
  857. CMenuToolbarBase* pmtb = _pmtbBottom; // Start With the bottom toolbar. This is
  858. // is an optimization because typically
  859. // menus that have both a Shell Folder portion
  860. // and a static portion have the majority
  861. // of the change activity in the bottom portion.
  862. // This can be NULL on a shutdown, when we're deregistering change notifies
  863. if (pmtb && pmtb->_hwndMB)
  864. {
  865. HWND hwnd = pmtb->_hwndMB;
  866. for (int iButton = 0; !fDone; iButton++)
  867. {
  868. IShellChangeNotify* ptscn;
  869. int idCmd = GetButtonCmd(hwnd, iButton);
  870. #ifdef DEBUG
  871. TCHAR szSubmenuName[MAX_PATH];
  872. SendMessage(hwnd, TB_GETBUTTONTEXT, idCmd, (LPARAM)szSubmenuName);
  873. TraceMsg(TF_MENUBAND, "CMenuBand: Forwarding Change notify to %s", szSubmenuName);
  874. #endif
  875. // If it's not a seperator, see if there is a sub menu with a shell folder child.
  876. if (idCmd != -1 &&
  877. SUCCEEDED(pmtb->v_GetSubMenu(idCmd, &SID_MenuShellFolder, IID_PPV_ARG(IShellChangeNotify, &ptscn))))
  878. {
  879. IShellMenu* psm;
  880. // Don't forward this notify if the sub menu has specifically registered for change notify (By not passing
  881. // DontRegisterChangeNotify.
  882. if (SUCCEEDED(ptscn->QueryInterface(IID_PPV_ARG(IShellMenu, &psm))))
  883. {
  884. UINT uIdParent = 0;
  885. DWORD dwFlags = 0;
  886. // Get the flags
  887. psm->GetShellFolder(&dwFlags, NULL, IID_NULL, NULL);
  888. psm->GetMenuInfo(NULL, &uIdParent, NULL, NULL);
  889. // If this menupane is an "Optimized" pane, (meaning that we don't register for change notify
  890. // and forward from a top level menu down) then we want to forward. We also
  891. // forward if this is a child of Menu Folder. If it is a child,
  892. // then it also does not register for change notify, but does not explicitly set it in it's flags
  893. // (review: Should we set it in it's flags?)
  894. // If it is not an optimized pane, then don't forward.
  895. if ((dwFlags & SMSET_DONTREGISTERCHANGENOTIFY) ||
  896. uIdParent == MNFOLDER_IS_PARENT)
  897. {
  898. // There is!, then pass to the child the change.
  899. hres = ptscn->OnChange(lEvent, pidl1, pidl2);
  900. // Update Dir on a Recursive change notify forces us to update everyone... Good thing
  901. // this does not happen alot and is caused by user interaction most of the time.
  902. }
  903. psm->Release();
  904. }
  905. ptscn->Release();
  906. }
  907. // Did we go through all of the buttons on this toolbar?
  908. if (iButton >= ToolBar_ButtonCount(hwnd) - 1)
  909. {
  910. // Yes, then we need to switch to the next toolbar.
  911. if (_pmtbTop != _pmtbBottom && pmtb != _pmtbTop)
  912. {
  913. pmtb = _pmtbTop;
  914. hwnd = pmtb->_hwndMB;
  915. iButton = -1; // -1 because at the end of the loop the for loop will increment.
  916. }
  917. else
  918. {
  919. // No; Then we must be done.
  920. fDone = TRUE;
  921. }
  922. }
  923. }
  924. }
  925. else
  926. hres = S_OK; // Return success because we're shutting down.
  927. return hres;
  928. }
  929. // Resize the parent menubar
  930. VOID CMenuBand::ResizeMenuBar()
  931. {
  932. // If we're not shown, then we do not need to do any kind of resize.
  933. // NOTE: Horizontal menubands are always shown. Don't do any of the
  934. // vertical stuff if we're horizontal.
  935. if (!_fShow)
  936. return;
  937. // If we're horizontal, don't do any Vertical sizing stuff.
  938. if (!_fVertical)
  939. {
  940. // BandInfoChanged is only for Horizontal Menubands.
  941. _BandInfoChanged();
  942. return;
  943. }
  944. // We need to update the buttons before a resize so that the band is the right size.
  945. _UpdateButtons();
  946. // Have the menubar think about changing its height
  947. IUnknown_QueryServiceExec(_punkSite, SID_SMenuPopup, &CGID_MENUDESKBAR,
  948. MBCID_RESIZE, 0, NULL, NULL);
  949. }
  950. STDMETHODIMP CMenuBand::QueryInterface(REFIID riid, void **ppvObj)
  951. {
  952. HRESULT hres;
  953. static const QITAB qit[] = {
  954. // Do not need IOleWindow (base class for IDeskBar)
  955. // because CToolBand::IDeskBand::IDockingWindow::IOleWindow
  956. // handles it.
  957. QITABENT(CMenuBand, IDeskBar), // Base class for IMenuPopup
  958. QITABENT(CMenuBand, IMenuPopup),
  959. QITABENT(CMenuBand, IMenuBand),
  960. QITABENT(CMenuBand, IShellMenu),
  961. QITABENT(CMenuBand, IShellMenu2),
  962. QITABENT(CMenuBand, IWinEventHandler),
  963. QITABENT(CMenuBand, IShellMenuAcc),
  964. { 0 },
  965. };
  966. hres = QISearch(this, (LPCQITAB)qit, riid, ppvObj);
  967. if (FAILED(hres))
  968. hres = SUPERCLASS::QueryInterface(riid, ppvObj);
  969. if (FAILED(hres) && IsEqualGUID(riid, CLSID_MenuBand))
  970. {
  971. AddRef();
  972. *ppvObj = (LPVOID)this;
  973. hres = S_OK;
  974. }
  975. return hres;
  976. }
  977. /*----------------------------------------------------------
  978. Purpose: IServiceProvider::QueryService method
  979. */
  980. STDMETHODIMP CMenuBand::QueryService(REFGUID guidService,
  981. REFIID riid, void **ppvObj)
  982. {
  983. HRESULT hr = E_FAIL;
  984. *ppvObj = NULL; // assume error
  985. if (IsEqualIID(guidService, SID_SMenuPopup) ||
  986. IsEqualIID(guidService, SID_SMenuBandChild) ||
  987. IsEqualIID(guidService, SID_SMenuBandParent) ||
  988. (_fTopLevel && IsEqualIID(guidService, SID_SMenuBandTop)))
  989. {
  990. if (IsEqualIID(riid, IID_IAccessible) || IsEqualIID(riid, IID_IDispatch))
  991. {
  992. hr = E_OUTOFMEMORY;
  993. CAccessible* pacc = new CAccessible(SAFECAST(this, IMenuBand*));
  994. if (pacc)
  995. {
  996. hr = pacc->InitAcc();
  997. if (SUCCEEDED(hr))
  998. {
  999. hr = pacc->QueryInterface(riid, ppvObj);
  1000. }
  1001. pacc->Release();
  1002. }
  1003. }
  1004. else
  1005. hr = QueryInterface(riid, ppvObj);
  1006. }
  1007. else if (IsEqualIID(guidService, SID_SMenuBandBottom) ||
  1008. IsEqualIID(guidService, SID_SMenuBandBottomSelected))
  1009. {
  1010. // SID_SMenuBandBottom queries down
  1011. BOOL fLookingForSelected = IsEqualIID(SID_SMenuBandBottomSelected, guidService);
  1012. // Are we the leaf node?
  1013. if (!_fInSubMenu)
  1014. {
  1015. if ( fLookingForSelected &&
  1016. (_pmtbTracked == NULL ||
  1017. ToolBar_GetHotItem(_pmtbTracked->_hwndMB) == -1))
  1018. {
  1019. hr = E_FAIL;
  1020. }
  1021. else
  1022. {
  1023. hr = QueryInterface(riid, ppvObj); // Yes; QI ourselves
  1024. }
  1025. }
  1026. else
  1027. {
  1028. // No; QS down...
  1029. IMenuPopup* pmp = _pmpSubMenu;
  1030. if (_pmpTrackPopup)
  1031. pmp = _pmpTrackPopup;
  1032. ASSERT(pmp);
  1033. hr = IUnknown_QueryService(pmp, guidService, riid, ppvObj);
  1034. if (FAILED(hr) && fLookingForSelected && _pmtbTracked != NULL)
  1035. {
  1036. hr = QueryInterface(riid, ppvObj); // Yes; QI ourselves
  1037. }
  1038. }
  1039. }
  1040. else if (IsEqualIID(guidService, SID_MenuShellFolder))
  1041. {
  1042. // This is a method of some other menu in the scheme to get to specifically the MenuShellfolder,
  1043. // This is for the COM Identity property.
  1044. if (_pmtbShellFolder)
  1045. hr = _pmtbShellFolder->QueryInterface(riid, ppvObj);
  1046. }
  1047. else
  1048. hr = SUPERCLASS::QueryService(guidService, riid, ppvObj);
  1049. return hr;
  1050. }
  1051. /*----------------------------------------------------------
  1052. Purpose: IWinEventHandler::IsWindowOwner method
  1053. */
  1054. STDMETHODIMP CMenuBand::IsWindowOwner(HWND hwnd)
  1055. {
  1056. if (( _pmtbShellFolder && (_pmtbShellFolder->IsWindowOwner(hwnd) == S_OK) ) ||
  1057. (_pmtbMenu && (_pmtbMenu->IsWindowOwner(hwnd) == S_OK)))
  1058. return S_OK;
  1059. return S_FALSE;
  1060. }
  1061. #define MB_EICH_FLAGS (EICH_SSAVETASKBAR | EICH_SWINDOWMETRICS | EICH_SPOLICY | EICH_SSHELLMENU | EICH_KWINPOLICY)
  1062. /*----------------------------------------------------------
  1063. Purpose: IWinEventHandler::OnWinEvent method
  1064. Processes messages passed on from the bandsite.
  1065. */
  1066. STDMETHODIMP CMenuBand::OnWinEvent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres)
  1067. {
  1068. HRESULT hres = NOERROR;
  1069. EnterModeless();
  1070. // Could our metrics be changing? (We keep track of this only for the
  1071. // toplevel menu)
  1072. BOOL fProcessSettingChange = FALSE;
  1073. switch (uMsg)
  1074. {
  1075. case WM_SETTINGCHANGE:
  1076. fProcessSettingChange = !lParam ||
  1077. (SHIsExplorerIniChange(wParam, lParam) & MB_EICH_FLAGS) ||
  1078. wParam == SPI_SETDROPSHADOW ||
  1079. wParam == SPI_SETFLATMENU;
  1080. break;
  1081. case WM_SYSCOLORCHANGE:
  1082. case WM_DISPLAYCHANGE:
  1083. fProcessSettingChange = TRUE;
  1084. break;
  1085. }
  1086. if (_fTopLevel &&
  1087. fProcessSettingChange &&
  1088. _pmbState && !_pmbState->IsProcessingChangeNotify())
  1089. {
  1090. // There is a race condition that can occur during a refresh
  1091. // that's really nasty. It causes another one to get pumped in the
  1092. // middle of processing this one, Yuck!
  1093. _pmbState->PushChangeNotify();
  1094. // There is a race condiction that can occur when the menuband is created,
  1095. // but not yet initialized. This has been hit by the IEAK group....
  1096. if (_pmtbTop)
  1097. {
  1098. // Yes; create a new metrics object and tell the submenus
  1099. // about it.
  1100. CMenuBandMetrics* pmbm = new CMenuBandMetrics();
  1101. if (pmbm)
  1102. {
  1103. ATOMICRELEASE(_pmbm);
  1104. _pmbm = pmbm;
  1105. _pmbm->Init(_pmtbTop->_hwndMB);
  1106. if (_pmtbMenu)
  1107. _pmtbMenu->SetMenuBandMetrics(_pmbm);
  1108. if (_pmtbShellFolder)
  1109. _pmtbShellFolder->SetMenuBandMetrics(_pmbm);
  1110. _CallCB(SMC_REFRESH, wParam, lParam);
  1111. // We need to force a button update at some point so that the new sizes are calculated
  1112. // Setting this flag will cause the buttons to be updatted before the next time it
  1113. // is shown. If, however, the menu is currently displayed, then the ResizeMenuBar will
  1114. // recalculate immediatly.
  1115. _fForceButtonUpdate = TRUE;
  1116. RECT rcOld;
  1117. RECT rcNew;
  1118. // Resize the MenuBar
  1119. GetClientRect(_hwndParent, &rcOld);
  1120. ResizeMenuBar();
  1121. GetClientRect(_hwndParent, &rcNew);
  1122. // If the rect sizes haven't changed, then we need to re-layout the
  1123. // band because the button widths may have changed.
  1124. if (EqualRect(&rcOld, &rcNew) && _fVertical)
  1125. _pmtbTop->NegotiateSize();
  1126. }
  1127. }
  1128. if (_pmtbMenu)
  1129. hres = _pmtbMenu->OnWinEvent(hwnd, uMsg, wParam, lParam, plres);
  1130. if (_pmtbShellFolder)
  1131. hres = _pmtbShellFolder->OnWinEvent(hwnd, uMsg, wParam, lParam, plres);
  1132. _pmbState->PopChangeNotify();
  1133. }
  1134. else
  1135. {
  1136. if (_pmtbMenu && (_pmtbMenu->IsWindowOwner(hwnd) == S_OK) )
  1137. hres = _pmtbMenu->OnWinEvent(hwnd, uMsg, wParam, lParam, plres);
  1138. if (_pmtbShellFolder && (_pmtbShellFolder->IsWindowOwner(hwnd) == S_OK) )
  1139. hres = _pmtbShellFolder->OnWinEvent(hwnd, uMsg, wParam, lParam, plres);
  1140. }
  1141. ExitModeless();
  1142. return hres;
  1143. }
  1144. /*----------------------------------------------------------
  1145. Purpose: IOleWindow::GetWindow method
  1146. */
  1147. STDMETHODIMP CMenuBand::GetWindow(HWND * phwnd)
  1148. {
  1149. if (_pmtbMenu)
  1150. {
  1151. *phwnd = _pmtbMenu->_hwndMB;
  1152. return NOERROR;
  1153. }
  1154. else if (_pmtbShellFolder)
  1155. {
  1156. *phwnd = _pmtbShellFolder->_hwndMB;
  1157. return NOERROR;
  1158. }
  1159. else
  1160. {
  1161. *phwnd = NULL;
  1162. return E_FAIL;
  1163. }
  1164. }
  1165. /*----------------------------------------------------------
  1166. Purpose: IOleWindow::ContextSensitiveHelp method
  1167. */
  1168. STDMETHODIMP CMenuBand::ContextSensitiveHelp(BOOL bEnterMode)
  1169. {
  1170. return SUPERCLASS::ContextSensitiveHelp(bEnterMode);
  1171. }
  1172. /*----------------------------------------------------------
  1173. Purpose: Handle WM_CHAR for accelerators
  1174. This is handled for any vertical menu. Since we have
  1175. two toolbars (potentially), this function determines
  1176. which toolbar gets the message depending on the
  1177. accelerator.
  1178. */
  1179. HRESULT CMenuBand::_HandleAccelerators(MSG * pmsg)
  1180. {
  1181. TCHAR ch = (TCHAR)pmsg->wParam;
  1182. HWND hwndTop = _pmtbTop->_hwndMB;
  1183. HWND hwndBottom = _pmtbBottom->_hwndMB;
  1184. // Here's how this works: the menu can have one or two toolbars.
  1185. //
  1186. // One toolbar: we simply forward the message onto the toolbar
  1187. // and let it handle any potential accelerators.
  1188. //
  1189. // Two toolbars: get the count of accelerators that match the
  1190. // given char for each toolbar. If only one toolbar has at
  1191. // least one match, forward the message onto that toolbar.
  1192. // Otherwise, forward the message onto the currently tracked
  1193. // toolbar and let it negotiate which accelerator button to
  1194. // choose (we might get a TBN_WRAPHOTITEM).
  1195. //
  1196. // If no match occurs, we beep. Beep beep.
  1197. //
  1198. if (!_pmtbTracked)
  1199. SetTracked(_pmtbTop);
  1200. ASSERT(_pmtbTracked);
  1201. if (_pmtbTop != _pmtbBottom)
  1202. {
  1203. int iNumBottomAccel;
  1204. int iNumTopAccel;
  1205. // Tell the dup handler not to handle this one....
  1206. _fProcessingDup = TRUE;
  1207. ToolBar_HasAccelerator(hwndTop, ch, &iNumTopAccel);
  1208. ToolBar_HasAccelerator(hwndBottom, ch, &iNumBottomAccel);
  1209. BOOL bBottom = (0 < iNumBottomAccel);
  1210. BOOL bTop = (0 < iNumTopAccel);
  1211. // Does one or the other (but not both) have an accelerator?
  1212. if (bBottom ^ bTop)
  1213. {
  1214. // Yes; do the work here for that specific toolbar
  1215. HWND hwnd = bBottom ? hwndBottom : hwndTop;
  1216. int cAccel = bBottom ? iNumBottomAccel : iNumTopAccel;
  1217. int idCmd;
  1218. pmsg->message = WM_NULL; // no need to forward the message
  1219. // This should never really fail since we just checked
  1220. EVAL( ToolBar_MapAccelerator(hwnd, ch, &idCmd) );
  1221. DWORD dwFlags = HICF_ACCELERATOR | HICF_RESELECT;
  1222. if (cAccel == 1)
  1223. dwFlags |= HICF_TOGGLEDROPDOWN;
  1224. int iPos = ToolBar_CommandToIndex(hwnd, idCmd);
  1225. ToolBar_SetHotItem2(hwnd, iPos, dwFlags);
  1226. }
  1227. // No; were there no accelerators?
  1228. else if ( !bTop )
  1229. {
  1230. // Yes
  1231. if (_fVertical)
  1232. {
  1233. MessageBeep(MB_OK);
  1234. }
  1235. else
  1236. {
  1237. _CancelMode(MPOS_FULLCANCEL);
  1238. }
  1239. }
  1240. // Else allow the message to go to the top toolbar
  1241. _fProcessingDup = FALSE;
  1242. }
  1243. return NOERROR;
  1244. }
  1245. /*----------------------------------------------------------
  1246. Purpose: Callback for the get message filter. We handle the
  1247. keyboard messages here (rather than IInputObject::
  1248. TranslateAcceleratorIO) so that we can redirect the
  1249. message *and* have the message pump still call
  1250. TranslateMessage to generate WM_CHAR and WM_SYSCHAR
  1251. messages.
  1252. */
  1253. LRESULT CMenuBand::GetMsgFilterCB(MSG * pmsg, BOOL bRemove)
  1254. {
  1255. // (See the note in CMBMsgFilter::GetMsgHook about bRemove.)
  1256. if (bRemove && !_fVertical && (pmsg->message == g_nMBPopupOpen) && _pmtbTracked)
  1257. {
  1258. // Menu is being popped open, send a WM_MENUSELECT equivalent.
  1259. _pmtbTracked->v_SendMenuNotification((UINT)pmsg->wParam, FALSE);
  1260. }
  1261. if (_fTopLevel && // Only do this for the top level
  1262. _dwFlags & SMINIT_USEMESSAGEFILTER && // They want to use the message filter
  1263. // instead of IsMenuMessage
  1264. bRemove && // Only do this if we're removing it.
  1265. WM_SYSCHAR == pmsg->message) // We only care about WM_SYSCHAR
  1266. {
  1267. // We intercept Alt-key combos (when pressed together) here,
  1268. // to prevent USER from going into a false menu loop check.
  1269. // There are compatibility problems if we let that happen.
  1270. //
  1271. // Sent by USER32 when the user hits an Alt-char combination.
  1272. // We need to translate this into popping down the correct
  1273. // menu. Normally we intercept this in the message pump
  1274. //
  1275. if (_OnSysChar(pmsg, TRUE) == S_OK)
  1276. {
  1277. pmsg->message = WM_NULL;
  1278. }
  1279. }
  1280. // If a user menu is up, then we do not want to intercept those messages. Intercepting
  1281. // messages intended for the poped up user menu causes havoc with keyboard accessibility.
  1282. // We also don't want to process messages if we're displaying a sub menu (It should be
  1283. // handling them).
  1284. BOOL fTracking = FALSE;
  1285. if (_pmtbMenu)
  1286. fTracking = _pmtbMenu->v_TrackingSubContextMenu();
  1287. if (_pmtbShellFolder && !fTracking)
  1288. fTracking = _pmtbShellFolder->v_TrackingSubContextMenu();
  1289. if (!_fInSubMenu && !fTracking)
  1290. {
  1291. // We don't process these messages when we're in a (modal) submenu
  1292. switch (pmsg->message)
  1293. {
  1294. case WM_SYSKEYDOWN:
  1295. case WM_KEYDOWN:
  1296. if (bRemove &&
  1297. (VK_ESCAPE == pmsg->wParam || VK_MENU == pmsg->wParam))
  1298. {
  1299. TraceMsg(TF_MENUBAND, "%d (pmb=%#08lx): Received Esc in msg filter", DBG_THIS);
  1300. DWORD dwSelect = (VK_ESCAPE == pmsg->wParam) ? MPOS_CANCELLEVEL : MPOS_FULLCANCEL;
  1301. _CancelMode(dwSelect);
  1302. pmsg->message = WM_NULL;
  1303. return 1;
  1304. }
  1305. // Fall thru
  1306. case WM_CHAR:
  1307. // Hitting the spacebar should invoke the system menu
  1308. if (!_fVertical &&
  1309. WM_CHAR == pmsg->message && TEXT(' ') == (TCHAR)pmsg->wParam)
  1310. {
  1311. // We need to leave this modal loop before bringing
  1312. // up the system menu (otherwise the user would need to
  1313. // hit Alt twice to get out.) Post the message.
  1314. TraceMsg(TF_MENUBAND, "%d (pmb=%#08lx): Leaving menu mode for system menu", DBG_THIS);
  1315. UIActivateIO(FALSE, NULL);
  1316. // Say the Alt-key is down to catch DefWindowProc's attention
  1317. pmsg->lParam |= 0x20000000;
  1318. pmsg->message = WM_SYSCHAR;
  1319. // Use the parent of the toolbar, because toolbar does not
  1320. // forward WM_SYSCHAR onto DefWindowProc.
  1321. pmsg->hwnd = GetParent(_pmtbTop->_hwndMB);
  1322. return 1;
  1323. }
  1324. else if (_fVertical && WM_CHAR == pmsg->message &&
  1325. pmsg->wParam != VK_RETURN)
  1326. {
  1327. // We do not want to pass VK_RETURN to _HandleAccelerators
  1328. // because it will try to do a character match. When it fails
  1329. // it will beep. Then we pass the VK_RETURN to the tracked toolbar
  1330. // and it executes the command.
  1331. // Handle accelerators here
  1332. _HandleAccelerators(pmsg);
  1333. }
  1334. // Fall thru
  1335. case WM_KEYUP:
  1336. // Collection point for most key messages...
  1337. if (NULL == _pmtbTracked)
  1338. {
  1339. // Normally we default to the top toolbar, unless that toolbar
  1340. // cannot receive the selection (As is the case on the top level
  1341. // start menu where the fast items are (Empty).
  1342. // Can the top toolbar be cycled into?
  1343. if (_pmtbTop && !_pmtbTop->DontShowEmpty())
  1344. {
  1345. // Yes;
  1346. SetTracked(_pmtbTop); // default to the top toolbar
  1347. }
  1348. else
  1349. {
  1350. // No; Set the tracked to the bottom, and hope that he can....
  1351. SetTracked(_pmtbBottom);
  1352. }
  1353. }
  1354. // F10 has special meaning for menus.
  1355. // - F10 alone, should toggle the selection of the first item
  1356. // in a horizontal menu
  1357. // - Shift-F10 should display a context menu.
  1358. if (VK_F10 == pmsg->wParam)
  1359. {
  1360. // Is this the Shift-F10 Case?
  1361. if (GetKeyState(VK_SHIFT) < 0)
  1362. {
  1363. // Yes. We need to force this message into a context menu
  1364. // message.
  1365. pmsg->message = WM_CONTEXTMENU;
  1366. pmsg->lParam = -1;
  1367. pmsg->wParam = (WPARAM)_pmtbTracked->_hwndMB;
  1368. return 0;
  1369. }
  1370. else if (!_fVertical) //No; Then we need to toggle in the horizontal case
  1371. {
  1372. if (_pmtbMenu)
  1373. {
  1374. // Set the hot item to the first one.
  1375. int iHot = 0;
  1376. if (ToolBar_GetHotItem(_pmtbMenu->_hwndMB) != -1)
  1377. iHot = -1; // We're toggling the selection off.
  1378. ToolBar_SetHotItem(_pmtbMenu->_hwndMB, iHot);
  1379. }
  1380. return 0;
  1381. }
  1382. }
  1383. // Redirect to the toolbar
  1384. if (_pmtbTracked)
  1385. pmsg->hwnd = _pmtbTracked->_hwndMB;
  1386. return 0;
  1387. case WM_NULL:
  1388. // Handle this here (we do nothing) to avoid mistaking this for
  1389. // g_nMBPopupOpen below, in case g_nMBPopupOpen is 0 if
  1390. // RegisterWindowMessage fails.
  1391. return 0;
  1392. default:
  1393. // We used to handle g_nMBPopupOpen here. But we can't because calling TrackPopupMenu
  1394. // (via CTrackPopupBar::Popup) w/in a GetMessageFilter is very bad.
  1395. break;
  1396. }
  1397. }
  1398. if (bRemove)
  1399. {
  1400. // These messages must be processed even when no submenu is open
  1401. switch (pmsg->message)
  1402. {
  1403. case WM_CLOSE:
  1404. // Being deactivated. Bail out of menus.
  1405. TraceMsg(TF_MENUBAND, "%d (pmb=%#08lx): sending MPOS_FULLCANCEL", DBG_THIS);
  1406. _CancelMode(MPOS_FULLCANCEL);
  1407. break;
  1408. default:
  1409. if (IsInRange(pmsg->message, WM_MOUSEFIRST, WM_MOUSELAST))
  1410. {
  1411. // If we move the mouse, collapse the tip. Careful not to blow away a balloon tip...
  1412. if (_pmbState)
  1413. _pmbState->HideTooltip(FALSE);
  1414. if (_pmtbShellFolder)
  1415. _pmtbShellFolder->v_ForwardMouseMessage(pmsg->message, pmsg->wParam, pmsg->lParam);
  1416. if (_pmtbMenu)
  1417. _pmtbMenu->v_ForwardMouseMessage(pmsg->message, pmsg->wParam, pmsg->lParam);
  1418. // Don't let the message be dispatched now that we've
  1419. // forwarded it.
  1420. pmsg->message = WM_NULL;
  1421. }
  1422. else if (pmsg->message == g_nMBFullCancel)
  1423. {
  1424. // Popup
  1425. TraceMsg(TF_MENUBAND, "%d (pmb=%#08lx): Received private full cancel message", DBG_THIS);
  1426. _SubMenuOnSelect(MPOS_CANCELLEVEL);
  1427. _CancelMode(MPOS_FULLCANCEL);
  1428. return 1;
  1429. }
  1430. break;
  1431. }
  1432. }
  1433. return 0;
  1434. }
  1435. /*----------------------------------------------------------
  1436. Purpose: Handle WM_SYSCHAR
  1437. This is handled for the toplevel menu only.
  1438. */
  1439. HRESULT CMenuBand::_OnSysChar(MSG * pmsg, BOOL bFirstDibs)
  1440. {
  1441. TCHAR ch = (TCHAR)pmsg->wParam;
  1442. // HACKHACK (scotth): I'm only doing all this checking because I don't
  1443. // understand why the doc-obj case sometimes (and sometimes doesn't)
  1444. // intercept this in its message filter.
  1445. if (!bFirstDibs && _fSysCharHandled)
  1446. {
  1447. _fSysCharHandled = FALSE;
  1448. return S_FALSE;
  1449. }
  1450. if (TEXT(' ') == (TCHAR)pmsg->wParam)
  1451. {
  1452. _fAltSpace = TRUE; // In the words of Spock..."Remember"
  1453. // start menu alt+space
  1454. TraceMsg(DM_MISC, "cmb._osc: alt+space _fTopLevel(1)");
  1455. UEMFireEvent(&UEMIID_SHELL, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_UIINPUT, UIBL_INPMENU);
  1456. }
  1457. else if (!_fInSubMenu)
  1458. {
  1459. int idBtn;
  1460. ASSERT(_fTopLevel);
  1461. // There is a brief instant when we're merging a menu and pumping messages
  1462. // This results in a null _pmtbMenu.
  1463. if (_pmtbMenu)
  1464. {
  1465. // Only a toplevel menubar follows this codepath. This means only
  1466. // the static menu toolbar will exist (and not the shellfolder toolbar).
  1467. _pmtbTracked = _pmtbMenu;
  1468. HWND hwnd = _pmtbTracked->_hwndMB;
  1469. if (ToolBar_MapAccelerator(hwnd, ch, &idBtn))
  1470. {
  1471. // Post a message since we're already in a menu loop
  1472. TraceMsg(TF_MENUBAND, "%d (pmb=%#08lx): WM_SYSCHAR: Posting CMBPopup message", DBG_THIS);
  1473. UIActivateIO(TRUE, NULL);
  1474. _pmtbTracked->PostPopup(idBtn, TRUE, TRUE);
  1475. // browser menu alt+key, start menu alt+key
  1476. TraceMsg(DM_MISC, "cmb._osc: alt+key _fTopLevel(1)");
  1477. UEMFireEvent(&UEMIID_SHELL, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_UIINPUT, UIBL_INPMENU);
  1478. return S_OK;
  1479. }
  1480. }
  1481. }
  1482. // Set or reset
  1483. _fSysCharHandled = bFirstDibs ? TRUE : FALSE;
  1484. return S_FALSE;
  1485. }
  1486. HRESULT CMenuBand::_ProcessMenuPaneMessages(MSG* pmsg)
  1487. {
  1488. if (pmsg->message == g_nMBPopupOpen)
  1489. {
  1490. // Popup the submenu. Since the top-level menuband receives this first, the
  1491. // command must be piped down the chain to the bottom-most menuband.
  1492. IOleCommandTarget * poct;
  1493. QueryService(SID_SMenuBandBottom, IID_PPV_ARG(IOleCommandTarget, &poct));
  1494. if (poct)
  1495. {
  1496. BOOL bSetItem = LOWORD(pmsg->lParam);
  1497. BOOL bInitialSelect = HIWORD(pmsg->lParam);
  1498. VARIANTARG vargIn;
  1499. TraceMsg(TF_MENUBAND, "%d (pmb=%#08lx): Received private popup menu message", DBG_THIS);
  1500. DWORD dwOpt = 0;
  1501. vargIn.vt = VT_I4;
  1502. vargIn.lVal = (LONG)pmsg->wParam;
  1503. if (bSetItem)
  1504. dwOpt |= MBPUI_SETITEM;
  1505. if (bInitialSelect)
  1506. dwOpt |= MBPUI_INITIALSELECT;
  1507. poct->Exec(&CGID_MenuBand, MBANDCID_POPUPITEM, dwOpt, &vargIn, NULL);
  1508. poct->Release();
  1509. return S_OK;
  1510. }
  1511. }
  1512. else if (pmsg->message == g_nMBDragCancel)
  1513. {
  1514. // If we got a drag cancel, make sure that the bottom most
  1515. // menu does not have the drag enter.
  1516. IUnknown_QueryServiceExec(SAFECAST(this, IOleCommandTarget*),
  1517. SID_SMenuBandBottom, &CGID_MenuBand, MBANDCID_DRAGCANCEL, 0, NULL, NULL);
  1518. return S_OK;
  1519. }
  1520. else if (pmsg->message == g_nMBOpenChevronMenu)
  1521. {
  1522. VARIANTARG v;
  1523. v.vt = VT_I4;
  1524. v.lVal = (LONG)pmsg->wParam;
  1525. IUnknown_Exec(_punkSite, &CGID_DeskBand, DBID_PUSHCHEVRON, _dwBandID, &v, NULL);
  1526. }
  1527. else if (pmsg->message == g_nMBFullCancel)
  1528. {
  1529. _SubMenuOnSelect(MPOS_CANCELLEVEL);
  1530. _CancelMode(MPOS_FULLCANCEL);
  1531. return S_OK;
  1532. }
  1533. return S_FALSE;
  1534. }
  1535. /*----------------------------------------------------------
  1536. Purpose: IMenuBand::IsMenuMessage method
  1537. The thread's message pump calls this function to see if any
  1538. messages need to be redirected to the menu band.
  1539. This returns S_OK if the message is handled. The
  1540. message pump should not pass it onto TranslateMessage
  1541. or DispatchMessage if it does.
  1542. */
  1543. STDMETHODIMP CMenuBand::IsMenuMessage(MSG * pmsg)
  1544. {
  1545. HRESULT hres = S_FALSE;
  1546. ASSERT(IS_VALID_WRITE_PTR(pmsg, MSG));
  1547. #ifdef DEBUG
  1548. if (g_dwDumpFlags & DF_TRANSACCELIO)
  1549. DumpMsg(TEXT("CMB::IsMM"), pmsg);
  1550. #endif
  1551. if (!_fShow)
  1552. goto Return;
  1553. switch (pmsg->message)
  1554. {
  1555. case WM_SYSKEYDOWN:
  1556. // blow this off if it's a repeated keystroke
  1557. if (!(pmsg->lParam & 0x40000000))
  1558. {
  1559. SendMessage(_hwndParent, WM_CHANGEUISTATE ,MAKEWPARAM(UIS_CLEAR, UISF_HIDEACCEL), 0);
  1560. // Are we pressing the Alt key to activate the menu?
  1561. if (!_fMenuMode && pmsg->wParam == VK_MENU && _pmbState)
  1562. {
  1563. // Yes; The the menu was activated because of a keyboard,
  1564. // Set the global state to show the keyboard cues.
  1565. _pmbState->SetKeyboardCue(TRUE);
  1566. // Since this only happens on the top level menu,
  1567. // We only have to tell the "Top" menu to update it's state.
  1568. _pmtbTop->SetKeyboardCue();
  1569. }
  1570. }
  1571. break;
  1572. case WM_SYSKEYUP:
  1573. // If we're in menu mode, ignore this message.
  1574. //
  1575. if (_fMenuMode)
  1576. hres = S_OK;
  1577. break;
  1578. case WM_SYSCHAR:
  1579. // We intercept Alt-key combos (when pressed together) here,
  1580. // to prevent USER from going into a false menu loop check.
  1581. // There are compatibility problems if we let that happen.
  1582. //
  1583. // Sent by USER32 when the user hits an Alt-char combination.
  1584. // We need to translate this into popping down the correct
  1585. // menu. Normally we intercept this in the message pump
  1586. //
  1587. // Outlook Express needs a message hook in order to filter this
  1588. // message for perf we do not use that method.
  1589. // Athena fix 222185 (lamadio) We also don't want to do this if we are not active!
  1590. // otherwise when WAB is on top of OE, we'll steal it's messages
  1591. // (lamadio): If the Message filter is "engaged", then we can process accelerators.
  1592. // Engaged does not mean that the filter is running.
  1593. if (GetMessageFilter()->IsEngaged())
  1594. {
  1595. hres = (_OnSysChar(pmsg, TRUE) == S_OK) ? S_OK : S_FALSE;
  1596. }
  1597. break;
  1598. case WM_KEYDOWN:
  1599. case WM_CHAR:
  1600. case WM_KEYUP:
  1601. if (_fMenuMode)
  1602. {
  1603. // All keystrokes should be handled or eaten by menubands
  1604. // if we're engaged. We must do this, otherwise hosted
  1605. // components like mshtml or word will try to handle the
  1606. // keystroke in CBaseBrowser.
  1607. // Also, don't bother forwarding tabs
  1608. if (VK_TAB != pmsg->wParam)
  1609. {
  1610. // Since we're answer S_OK, dispatch it ourselves.
  1611. TranslateMessage(pmsg);
  1612. DispatchMessage(pmsg);
  1613. }
  1614. hres = S_OK;
  1615. }
  1616. break;
  1617. case WM_CONTEXTMENU:
  1618. // Since the start button has the keyboard focus,
  1619. // the start button will handle this. We need to forward this off to the
  1620. // currently tracked item at the bottom of the chain
  1621. LRESULT lres;
  1622. IWinEventHandler* pweh;
  1623. if (_fMenuMode &&
  1624. SUCCEEDED(QueryService(SID_SMenuBandBottomSelected, IID_PPV_ARG(IWinEventHandler, &pweh))))
  1625. {
  1626. pweh->OnWinEvent(HWND_BROADCAST, pmsg->message,
  1627. pmsg->wParam, pmsg->lParam, &lres);
  1628. pweh->Release();
  1629. hres = S_OK;
  1630. }
  1631. break;
  1632. default:
  1633. // We only want to process the pane messages in IsMenuMessage when there is no
  1634. // top level HWND. This is for the Deskbar menus. Outlook Express needs the
  1635. // TranslateMenuMessage entry point
  1636. if (_pmbState->GetSubclassedHWND() == NULL)
  1637. hres = _ProcessMenuPaneMessages(pmsg);
  1638. break;
  1639. }
  1640. Return:
  1641. if (!_fMenuMode && hres != S_OK)
  1642. hres = E_FAIL;
  1643. return hres;
  1644. }
  1645. BOOL HasWindowTopmostOwner(HWND hwnd)
  1646. {
  1647. HWND hwndOwner = hwnd;
  1648. while (hwndOwner = GetWindowOwner(hwndOwner))
  1649. {
  1650. if (GetWindowLong(hwndOwner, GWL_EXSTYLE) & WS_EX_TOPMOST)
  1651. return TRUE;
  1652. }
  1653. return FALSE;
  1654. }
  1655. /*----------------------------------------------------------
  1656. Purpose: IMenuBand::TranslateMenuMessage method
  1657. The main app's window proc calls this so the menuband
  1658. catches messages that are dispatched from a different
  1659. message pump (than the thread's main pump).
  1660. Translates messages specially for menubands. Some messages
  1661. are processed while the menuband is active. Others are only
  1662. processed when it is not. Messages that are not b/t
  1663. WM_KEYFIRST and WM_KEYLAST are handled here (the browser
  1664. does not send these messages to IInputObject::
  1665. TranslateAcceleratorIO).
  1666. Returns: S_OK if message is processed
  1667. */
  1668. STDMETHODIMP CMenuBand::TranslateMenuMessage(MSG * pmsg, LRESULT * plRet)
  1669. {
  1670. ASSERT(IS_VALID_WRITE_PTR(pmsg, MSG));
  1671. #ifdef DEBUG
  1672. if (g_dwDumpFlags & DF_TRANSACCELIO)
  1673. DumpMsg(TEXT("CMB::TMM"), pmsg);
  1674. #endif
  1675. switch (pmsg->message)
  1676. {
  1677. case WM_SYSCHAR:
  1678. // In certain doc-obj situations, the OLE message filter (??)
  1679. // grabs this before the main thread's message pump gets a
  1680. // whack at it. So we handle it here too, in case we're in
  1681. // this scenario.
  1682. //
  1683. // See the comments in IsMenuMessage regarding this message.
  1684. return _OnSysChar(pmsg, FALSE);
  1685. case WM_INITMENUPOPUP:
  1686. // Normally the LOWORD(lParam) is the index of the menu that
  1687. // is being popped up. TrackPopupMenu (which CMenuISF uses)
  1688. // always sends this message with an index of 0. This breaks
  1689. // clients (like DefView) who check this value. We need to
  1690. // massage this value if we find we're the source of the
  1691. // WM_INITMENUPOPUP.
  1692. //
  1693. // (This is not in TranslateAcceleratorIO b/c TrackPopupMenu's
  1694. // message pump does not call it. The wndproc must forward
  1695. // the message to this function for us to get it.)
  1696. if (_fInSubMenu && _pmtbTracked)
  1697. {
  1698. // Massage lParam to use the right index
  1699. int iPos = ToolBar_CommandToIndex(_pmtbTracked->_hwndMB, _nItemCur);
  1700. pmsg->lParam = MAKELPARAM(iPos, HIWORD(pmsg->lParam));
  1701. // Return S_FALSE so this message will still be handled
  1702. }
  1703. break;
  1704. case WM_UPDATEUISTATE:
  1705. if (_pmbState)
  1706. {
  1707. // we don't care about UISF_HIDEFOCUS
  1708. if (UISF_HIDEACCEL == HIWORD(pmsg->wParam))
  1709. _pmbState->SetKeyboardCue(UIS_CLEAR == LOWORD(pmsg->wParam) ? TRUE : FALSE);
  1710. }
  1711. break;
  1712. case WM_ACTIVATE:
  1713. {
  1714. CMBMsgFilter* pmf = GetMessageFilter();
  1715. // Debug note: to debug menubands on ntsd, set the prototype
  1716. // flag accordingly. This will keep menubands from going
  1717. // away the moment the focus changes.
  1718. // Becomming inactive?
  1719. if (WA_INACTIVE == LOWORD(pmsg->wParam))
  1720. {
  1721. // Yes; Free up the global object
  1722. // Athena fix (lamadio) 08.02.1998: Athena uses menubands. Since they
  1723. // have a band per window in one thread, we needed a mechanism to switch
  1724. // between them. So we used the Msgfilter to forward messages. Since there
  1725. // are multiple windows, we need to set correct one.
  1726. // But, On a deactivate, we need to NULL it out incase a window,
  1727. // running in the same thread, has normal USER menu. We don't want to steal
  1728. // their messages.
  1729. if (pmf->GetTopMostPtr() == this)
  1730. pmf->SetTopMost(NULL);
  1731. pmf->DisEngage(_pmbState->GetContext());
  1732. HWND hwndLostTo = (HWND)(pmsg->lParam);
  1733. // We won't bail on the menus if we're loosing activation to a child.
  1734. if (!IsAncestor(hwndLostTo, _pmbState->GetWorkerWindow(NULL)))
  1735. {
  1736. if (_fMenuMode &&
  1737. !(g_dwPrototype & PF_USINGNTSD) &&
  1738. !_fDragEntered)
  1739. {
  1740. // Being deactivated. Bail out of menus.
  1741. // (Only the toplevel band gets this message.)
  1742. if (_fInSubMenu)
  1743. {
  1744. IMenuPopup* pmp = _pmpSubMenu;
  1745. if (_pmpTrackPopup)
  1746. pmp = _pmpTrackPopup;
  1747. ASSERT(pmp); // This should be valid. If not, someone messed up.
  1748. pmp->OnSelect(MPOS_FULLCANCEL);
  1749. }
  1750. _CancelMode(MPOS_FULLCANCEL);
  1751. }
  1752. }
  1753. }
  1754. else if (WA_ACTIVE == LOWORD(pmsg->wParam) ||
  1755. WA_CLICKACTIVE == LOWORD(pmsg->wParam))
  1756. {
  1757. // If I have activation, the Worker Window needs to be bottom...
  1758. //
  1759. // NOTE: Don't do this if the worker window has a topmost owner
  1760. // (such as the tray). Setting a window to HWND_NOTOPMOST moves
  1761. // its owner windows to HWND_NOTOPMOST as well, which in this case
  1762. // was breaking the tray's "always on top" feature.
  1763. //
  1764. HWND hwndWorker = _pmbState->GetWorkerWindow(NULL);
  1765. if (hwndWorker && !HasWindowTopmostOwner(hwndWorker) && !_fDragEntered)
  1766. SetWindowPos(hwndWorker, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
  1767. // Set the context because when a menu heirarchy becomes active because the
  1768. // subclassed HWND becomes active, we need to reenable the message hook.
  1769. pmf->SetContext(this, TRUE);
  1770. // When we get reactivated, we need to position ourself above the start bar.
  1771. Exec(&CGID_MenuBand, MBANDCID_REPOSITION, TRUE, NULL, NULL);
  1772. // Becomming activated. We need to reengage the message hook so that
  1773. // we get the correct messages.
  1774. pmf->ReEngage(_pmbState->GetContext());
  1775. // Are we in menu mode?
  1776. if (_fMenuMode)
  1777. {
  1778. // Need to reengage some things.
  1779. // Take the capture back because we have lost it to context menus or dialogs.
  1780. pmf->RetakeCapture();
  1781. }
  1782. pmf->SetTopMost(this);
  1783. }
  1784. //
  1785. // Memphis and NT5 grey their horizontal menus when the windows is inactive.
  1786. //
  1787. if (!_fVertical && _pmtbMenu)
  1788. {
  1789. // This needs to stay here because of the above check...
  1790. if (WA_INACTIVE == LOWORD(pmsg->wParam))
  1791. {
  1792. _fAppActive = FALSE;
  1793. }
  1794. else
  1795. {
  1796. _fAppActive = TRUE;
  1797. }
  1798. // Reduces flicker by using this instead of an InvalidateWindow/UpdateWindow Pair
  1799. RedrawWindow(_pmtbMenu->_hwndMB, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
  1800. }
  1801. }
  1802. break;
  1803. case WM_SYSCOMMAND:
  1804. if ( !_fMenuMode )
  1805. {
  1806. switch (pmsg->wParam & 0xFFF0)
  1807. {
  1808. case SC_KEYMENU:
  1809. // The user either hit the Alt key by itself or Alt-space.
  1810. // If it was Alt-space, let DefWindowProc handle it so the
  1811. // system menu comes up. Otherwise, we'll handle it to
  1812. // toggle the menuband.
  1813. // Was it Alt-space?
  1814. if (_fAltSpace)
  1815. {
  1816. // Yes; let it go
  1817. TraceMsg(TF_MENUBAND, "%d (pmb=%#08lx): Caught the Alt-space", DBG_THIS);
  1818. _fAltSpace = FALSE;
  1819. }
  1820. else if (_fShow)
  1821. {
  1822. // No; activate the menu
  1823. TraceMsg(TF_MENUBAND, "%d (pmb=%#08lx): Caught the WM_SYSCOMMAND, SC_KEYMENU", DBG_THIS);
  1824. UIActivateIO(TRUE, NULL);
  1825. // We sit in a modal loop here because typically
  1826. // WM_SYSCOMMAND doesn't return until the menu is finished.
  1827. //
  1828. while (_fMenuMode)
  1829. {
  1830. MSG msg;
  1831. if (GetMessage(&msg, NULL, 0, 0))
  1832. {
  1833. if ( S_OK != IsMenuMessage(&msg) )
  1834. {
  1835. TranslateMessage(&msg);
  1836. DispatchMessage(&msg);
  1837. }
  1838. }
  1839. }
  1840. *plRet = 0;
  1841. return S_OK; // Caller shouldn't handle this
  1842. }
  1843. break;
  1844. }
  1845. }
  1846. break;
  1847. default:
  1848. // We only want to process the pane messages in IsMenuMessage when there is no
  1849. // top level HWND. This is for the Deskbar menus. Outlook Express needs the
  1850. // TranslateMenuMessage entry point
  1851. if (_pmbState->GetSubclassedHWND() != NULL)
  1852. return _ProcessMenuPaneMessages(pmsg);
  1853. break;
  1854. }
  1855. return S_FALSE;
  1856. }
  1857. /*----------------------------------------------------------
  1858. Purpose: IObjectWithSite::SetSite method
  1859. Called by the menusite to host this band. Since the
  1860. menuband contains two toolbars, we set their parent
  1861. window to be the site's hwnd.
  1862. */
  1863. STDMETHODIMP CMenuBand::SetSite(IUnknown* punkSite)
  1864. {
  1865. // Do this first because SetParent needs to query to the top level browser for
  1866. // sftbar who queries to the top level browser to get the drag and drop window.
  1867. HRESULT hres = SUPERCLASS::SetSite(punkSite);
  1868. if (_psmcb && _fTopLevel && !(_dwFlags & SMINIT_NOSETSITE))
  1869. IUnknown_SetSite(_psmcb, punkSite);
  1870. IUnknown_GetWindow(punkSite, &_hwndParent);
  1871. // Need this for Closing an expanded vertical menu. Start Menu knows to do this when it's top level,
  1872. // but the Favorites needs to know when it's parent is the horizontal menu.
  1873. VARIANT var = {0};
  1874. if (SUCCEEDED(IUnknown_QueryServiceExec(punkSite, SID_SMenuBandParent, &CGID_MenuBand,
  1875. MBANDCID_ISVERTICAL, 0, NULL, &var)) &&
  1876. var.boolVal == VARIANT_FALSE)
  1877. {
  1878. ASSERT(VT_BOOL == var.vt);
  1879. _fParentIsHorizontal = TRUE;
  1880. }
  1881. if (_fNoBorder)
  1882. {
  1883. IUnknown_QueryServiceExec(punkSite, SID_SMenuPopup, &CGID_MENUDESKBAR, MBCID_NOBORDER, _fNoBorder, NULL, NULL);
  1884. }
  1885. // Tell the toolbars who their new parent is
  1886. if (_pmtbMenu)
  1887. _pmtbMenu->SetParent(_hwndParent);
  1888. if (_pmtbShellFolder)
  1889. _pmtbShellFolder->SetParent(_hwndParent);
  1890. return hres;
  1891. }
  1892. /*----------------------------------------------------------
  1893. Purpose: IShellMenu::Initialize method
  1894. */
  1895. STDMETHODIMP CMenuBand::Initialize(IShellMenuCallback* psmcb, UINT uId, UINT uIdAncestor, DWORD dwFlags)
  1896. {
  1897. // Initalized can be called with NULL values to only set some of them.
  1898. // Default to Vertical
  1899. if (!(dwFlags & SMINIT_HORIZONTAL) && !(dwFlags & SMINIT_VERTICAL) && !(dwFlags & SMINIT_MULTICOLUMN))
  1900. dwFlags |= SMINIT_VERTICAL;
  1901. HRESULT hr = _Initialize(dwFlags);
  1902. if (SUCCEEDED(hr))
  1903. {
  1904. DEBUG_CODE( _fInitialized = TRUE; );
  1905. if (uIdAncestor != ANCESTORDEFAULT)
  1906. _uIdAncestor = uIdAncestor;
  1907. if (_uId != -1)
  1908. _uId = uId;
  1909. if (psmcb)
  1910. {
  1911. if (!SHIsSameObject(psmcb, _psmcb))
  1912. {
  1913. if (_punkSite && _fTopLevel && !(dwFlags & SMINIT_NOSETSITE))
  1914. IUnknown_SetSite(_psmcb, NULL);
  1915. ATOMICRELEASE(_psmcb);
  1916. _psmcb = psmcb;
  1917. _psmcb->AddRef();
  1918. // We do not set the site in case this callback is shared between 2 bands (Menubar/Chevron menu)
  1919. if (_punkSite && _fTopLevel && !(dwFlags & SMINIT_NOSETSITE))
  1920. IUnknown_SetSite(_psmcb, _punkSite);
  1921. // Only call this if we're setting a new one. Pass the address of the user associated
  1922. // data section. This is so that the callback can associate data with this pane only
  1923. _CallCB(SMC_CREATE, 0, (LPARAM)&_pvUserData);
  1924. }
  1925. }
  1926. }
  1927. return hr;
  1928. }
  1929. /*----------------------------------------------------------
  1930. Purpose: IShellMenu::GetMenuInfo method
  1931. */
  1932. STDMETHODIMP CMenuBand::GetMenuInfo(IShellMenuCallback** ppsmc, UINT* puId,
  1933. UINT* puIdAncestor, DWORD* pdwFlags)
  1934. {
  1935. if (ppsmc)
  1936. {
  1937. *ppsmc = _psmcb;
  1938. if (_psmcb)
  1939. ((IShellMenuCallback*)*ppsmc)->AddRef();
  1940. }
  1941. if (puId)
  1942. *puId = _uId;
  1943. if (puIdAncestor)
  1944. *puIdAncestor = _uIdAncestor;
  1945. if (pdwFlags)
  1946. *pdwFlags = _dwFlags;
  1947. return NOERROR;
  1948. }
  1949. HRESULT CMenuBand::_AddToolbar(CMenuToolbarBase* pmtb, DWORD dwFlags)
  1950. {
  1951. if (_pszTheme)
  1952. {
  1953. pmtb->SetTheme(_pszTheme);
  1954. }
  1955. if (_fNoBorder)
  1956. {
  1957. pmtb->SetNoBorder(_fNoBorder);
  1958. }
  1959. pmtb->SetSite(SAFECAST(this, IMenuBand*));
  1960. HRESULT hr = S_OK;
  1961. if (_hwndParent)
  1962. hr = pmtb->CreateToolbar(_hwndParent);
  1963. if (SUCCEEDED(hr))
  1964. {
  1965. // Treat this like a two-element stack, where this function
  1966. // behaves like a "push". The one additional trick is we
  1967. // could be pushing onto the top or the bottom of the "stack".
  1968. if (dwFlags & SMSET_BOTTOM)
  1969. {
  1970. if (_pmtbBottom)
  1971. {
  1972. // I don't need to release, because _pmtbTop and _pmtbBottom are aliases for
  1973. // _pmtbShellFolder and _pmtbMenu
  1974. _pmtbTop = _pmtbBottom;
  1975. _pmtbTop->SetToTop(TRUE);
  1976. }
  1977. _pmtbBottom = pmtb;
  1978. _pmtbBottom->SetToTop(FALSE);
  1979. }
  1980. else // Default to Top...
  1981. {
  1982. if (_pmtbTop)
  1983. {
  1984. _pmtbBottom = _pmtbTop;
  1985. _pmtbBottom->SetToTop(FALSE);
  1986. }
  1987. _pmtbTop = pmtb;
  1988. _pmtbTop->SetToTop(TRUE);
  1989. }
  1990. // _pmtbBottom should never be the only toolbar that exists in the menuband.
  1991. if (!_pmtbTop)
  1992. _pmtbTop = _pmtbBottom;
  1993. // The menuband determines there is a single toolbar by comparing
  1994. // the bottom with the top. So make the bottom the same if necessary.
  1995. if (!_pmtbBottom)
  1996. _pmtbBottom = _pmtbTop;
  1997. }
  1998. return hr;
  1999. }
  2000. /*----------------------------------------------------------
  2001. Purpose: IShellMenu::GetShellFolder method
  2002. */
  2003. STDMETHODIMP CMenuBand::GetShellFolder(DWORD* pdwFlags, LPITEMIDLIST* ppidl,
  2004. REFIID riid, void** ppvObj)
  2005. {
  2006. HRESULT hres = E_FAIL;
  2007. if (_pmtbShellFolder)
  2008. {
  2009. *pdwFlags = _pmtbShellFolder->GetFlags();
  2010. hres = S_OK;
  2011. if (ppvObj)
  2012. {
  2013. // HACK HACK. this should QI for a mnfolder specific interface to do this.
  2014. hres = _pmtbShellFolder->GetShellFolder(ppidl, riid, ppvObj);
  2015. }
  2016. }
  2017. return hres;
  2018. }
  2019. /*----------------------------------------------------------
  2020. Purpose: IShellMenu::SetShellFolder method
  2021. */
  2022. STDMETHODIMP CMenuBand::SetShellFolder(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HKEY hKey, DWORD dwFlags)
  2023. {
  2024. ASSERT(_fInitialized);
  2025. HRESULT hr = E_OUTOFMEMORY;
  2026. // If we're processing a change notify, we cannot do anything that will modify state.
  2027. // NOTE: if we don't have a state, we can't possibly processing a change notify
  2028. if (_pmbState && _pmbState->IsProcessingChangeNotify())
  2029. return E_PENDING;
  2030. // Only one shellfolder menu can exist per menuband. Additionally,
  2031. // a shellfolder menu can exist either at the top of the menu, or
  2032. // at the bottom (when it coexists with a static menu).
  2033. // Is there already a shellfolder menu?
  2034. if (_pmtbShellFolder)
  2035. {
  2036. IShellFolderBand* psfb;
  2037. _pmtbShellFolder->QueryInterface(IID_PPV_ARG(IShellFolderBand, &psfb));
  2038. ASSERTMSG(psfb != NULL, "CMenuBand::SetShellFolder should have gotten interface");
  2039. hr = psfb->InitializeSFB(psf, pidlFolder);
  2040. psfb->Release();
  2041. }
  2042. else
  2043. {
  2044. _pmtbShellFolder = new CMenuSFToolbar(this, psf, pidlFolder, hKey, dwFlags);
  2045. if (_pmtbShellFolder)
  2046. {
  2047. hr = _AddToolbar(_pmtbShellFolder, dwFlags);
  2048. }
  2049. }
  2050. return hr;
  2051. }
  2052. /*----------------------------------------------------------
  2053. Purpose: IMenuBand::GetMenu method
  2054. */
  2055. STDMETHODIMP CMenuBand::GetMenu(HMENU* phmenu, HWND* phwnd, DWORD* pdwFlags)
  2056. {
  2057. HRESULT hres = E_FAIL;
  2058. // HACK HACK. this should QI for a menustatic specific interface to do this.
  2059. if (_pmtbMenu)
  2060. hres = _pmtbMenu->GetMenu(phmenu, phwnd, pdwFlags);
  2061. return hres;
  2062. }
  2063. /*----------------------------------------------------------
  2064. Purpose: IMenuBand::SetMenu method
  2065. */
  2066. STDMETHODIMP CMenuBand::SetMenu(HMENU hmenu, HWND hwnd, DWORD dwFlags)
  2067. {
  2068. // Passing a NULL hmenu is valid. It means destroy our menu object.
  2069. ASSERT(_fInitialized);
  2070. HRESULT hr = E_FAIL;
  2071. // Only one static menu can exist per menuband. Additionally,
  2072. // a static menu can exist either at the top of the menu, or
  2073. // at the bottom (when it coexists with a shellfolder menu).
  2074. // Is there already a static menu?
  2075. if (_pmtbMenu)
  2076. {
  2077. // Since we're merging in a new menu, make sure to update the cache...
  2078. _hmenu = hmenu;
  2079. // Yes
  2080. // HACK HACK. this should QI for a menustatic specific interface to do this.
  2081. return _pmtbMenu->SetMenu(hmenu, hwnd, dwFlags);
  2082. }
  2083. else
  2084. {
  2085. // This is to work around a problem in the interface definintion: We have
  2086. // no method of setting the Subclassed HWND outside of a SetMenu. So I'm just piggybacking
  2087. // off of this. A better fix would be to introduce IMenuBand2::SetSubclass(HWND). IMenuBand
  2088. // actually implements the "Subclassing", so extending this interface would be worthwhile.
  2089. _hwndMenuOwner = hwnd;
  2090. if (_fTopLevel)
  2091. {
  2092. _pmbState->SetSubclassedHWND(hwnd);
  2093. }
  2094. if (hmenu)
  2095. {
  2096. _hmenu = hmenu;
  2097. _pmtbMenu = new CMenuStaticToolbar(this, hmenu, hwnd, _uId, dwFlags);
  2098. if (_pmtbMenu)
  2099. {
  2100. hr = _AddToolbar(_pmtbMenu, dwFlags);
  2101. }
  2102. else
  2103. hr = E_OUTOFMEMORY;
  2104. }
  2105. }
  2106. return hr;
  2107. }
  2108. /*----------------------------------------------------------
  2109. Purpose: IShellMenu::SetMenuToolbar method
  2110. */
  2111. STDMETHODIMP CMenuBand::SetMenuToolbar(IUnknown* punk, DWORD dwFlags)
  2112. {
  2113. HRESULT hr = E_INVALIDARG;
  2114. CMenuToolbarBase* pmtb;
  2115. if (punk && SUCCEEDED(punk->QueryInterface(CLSID_MenuToolbarBase, (void**)&pmtb)))
  2116. {
  2117. ASSERT(_pmtbShellFolder == NULL);
  2118. _pmtbShellFolder = pmtb;
  2119. hr = _AddToolbar(pmtb, dwFlags);
  2120. }
  2121. return hr;
  2122. }
  2123. /*----------------------------------------------------------
  2124. Purpose: IShellMenu::InvalidateItem method
  2125. */
  2126. STDMETHODIMP CMenuBand::InvalidateItem(LPSMDATA psmd, DWORD dwFlags)
  2127. {
  2128. HRESULT hres = S_FALSE;
  2129. // If psmd is NULL, we need to just dump the toolbars and do a full reset.
  2130. if (psmd == NULL)
  2131. {
  2132. // If we're processing a change notify, we cannot do anything that will modify state.
  2133. if (_pmbState && _pmbState->IsProcessingChangeNotify())
  2134. return E_PENDING;
  2135. if (_pmbState)
  2136. _pmbState->PushChangeNotify();
  2137. // Tell the callback we're refreshing so that it can
  2138. // reset any cached state
  2139. _CallCB(SMC_REFRESH);
  2140. // Reinitialize the callback if requested
  2141. if (dwFlags & SMINV_INITMENU)
  2142. {
  2143. _CallCB(SMC_INITMENU);
  2144. }
  2145. _fExpanded = FALSE;
  2146. // We don't need to refill if the caller only wanted to
  2147. // refresh the sub menus.
  2148. // Refresh the Shell Folder first because
  2149. // It may have no items after it's done, and the
  2150. // menuband may rely on this to add a seperator
  2151. if (_pmtbShellFolder)
  2152. _pmtbShellFolder->v_Refresh();
  2153. // Refresh the Static menu
  2154. if (_pmtbMenu)
  2155. _pmtbMenu->v_Refresh();
  2156. if (_pmpSubMenu)
  2157. {
  2158. _fInSubMenu = FALSE;
  2159. IUnknown_SetSite(_pmpSubMenu, NULL);
  2160. ATOMICRELEASE(_pmpSubMenu);
  2161. }
  2162. if (_pmbState)
  2163. _pmbState->PopChangeNotify();
  2164. }
  2165. else
  2166. {
  2167. if (_pmtbTop)
  2168. hres = _pmtbTop->v_InvalidateItem(psmd, dwFlags);
  2169. // We refresh everything at this level if the psmd is null
  2170. if (_pmtbBottom && hres != S_OK)
  2171. hres = _pmtbBottom->v_InvalidateItem(psmd, dwFlags);
  2172. }
  2173. return hres;
  2174. }
  2175. /*----------------------------------------------------------
  2176. Purpose: IShellMenu::GetState method
  2177. */
  2178. STDMETHODIMP CMenuBand::GetState(LPSMDATA psmd)
  2179. {
  2180. if (_pmtbTracked)
  2181. return _pmtbTracked->v_GetState(-1, psmd);
  2182. // todo: might want to put stuff from _CallCB (below) in here
  2183. return E_FAIL;
  2184. }
  2185. HRESULT CMenuBand::_CallCB(DWORD dwMsg, WPARAM wParam, LPARAM lParam)
  2186. {
  2187. if (!_psmcb)
  2188. return S_FALSE;
  2189. // We don't need to check callback mask here because these are not maskable events.
  2190. SMDATA smd = {0};
  2191. smd.punk = SAFECAST(this, IShellMenu*);
  2192. smd.uIdParent = _uId;
  2193. smd.uIdAncestor = _uIdAncestor;
  2194. smd.hwnd = _hwnd;
  2195. smd.hmenu = _hmenu;
  2196. smd.pvUserData = _pvUserData;
  2197. if (_pmtbShellFolder)
  2198. _pmtbShellFolder->GetShellFolder(&smd.pidlFolder, IID_PPV_ARG(IShellFolder, &smd.psf));
  2199. HRESULT hres = _psmcb->CallbackSM(&smd, dwMsg, wParam, lParam);
  2200. ILFree(smd.pidlFolder);
  2201. if (smd.psf)
  2202. smd.psf->Release();
  2203. return hres;
  2204. }
  2205. /*----------------------------------------------------------
  2206. Purpose: IInputObject::TranslateAcceleratorIO
  2207. This is called by the base browser only when the menuband
  2208. "has the focus", and only for messages b/t WM_KEYFIRST
  2209. and WM_KEYLAST. This isn't very useful for menubands.
  2210. See the explanations in GetMsgFilterCB, IsMenuMessage
  2211. and TranslateMenuMessage.
  2212. In addition, menubands cannot ever have the activation,
  2213. so this method should never be called.
  2214. Returns S_OK if handled.
  2215. */
  2216. STDMETHODIMP CMenuBand::TranslateAcceleratorIO(LPMSG pmsg)
  2217. {
  2218. AssertMsg(0, TEXT("Menuband has the activation but it shouldn't!"));
  2219. return S_FALSE;
  2220. }
  2221. /*----------------------------------------------------------
  2222. Purpose: IInputObject::HasFocusIO
  2223. */
  2224. STDMETHODIMP CMenuBand::HasFocusIO()
  2225. {
  2226. // We consider a menuband has the focus even if it has submenus
  2227. // that are currently cascaded out. All menubands in the chain
  2228. // have the focus.
  2229. return _fMenuMode ? S_OK : S_FALSE;
  2230. }
  2231. /*----------------------------------------------------------
  2232. Purpose: IMenuPopup::SetSubMenu method
  2233. The child menubar calls us with its IMenuPopup pointer.
  2234. */
  2235. STDMETHODIMP CMenuBand::SetSubMenu(IMenuPopup * pmp, BOOL fSet)
  2236. {
  2237. ASSERT(IS_VALID_CODE_PTR(pmp, IMenuPopup));
  2238. if (fSet)
  2239. {
  2240. _fInSubMenu = TRUE;
  2241. }
  2242. else
  2243. {
  2244. if (_pmtbTracked)
  2245. {
  2246. _pmtbTracked->PopupClose();
  2247. }
  2248. _fInSubMenu = FALSE;
  2249. _nItemSubMenu = -1;
  2250. }
  2251. return S_OK;
  2252. }
  2253. HRESULT CMenuBand::_SiteSetSubMenu(IMenuPopup * pmp, BOOL bSet)
  2254. {
  2255. HRESULT hres;
  2256. IMenuPopup * pmpSite;
  2257. hres = IUnknown_QueryService(_punkSite, SID_SMenuPopup, IID_PPV_ARG(IMenuPopup, &pmpSite));
  2258. if (SUCCEEDED(hres))
  2259. {
  2260. hres = pmpSite->SetSubMenu(pmp, bSet);
  2261. pmpSite->Release();
  2262. }
  2263. return hres;
  2264. }
  2265. /*----------------------------------------------------------
  2266. Purpose: Tell the GetMsg filter that this menuband is ready to
  2267. listen to messages.
  2268. */
  2269. HRESULT CMenuBand::_EnterMenuMode(void)
  2270. {
  2271. ASSERT(!_fMenuMode); // Must not push onto stack more than once
  2272. if (g_dwProfileCAP & 0x00002000)
  2273. StartCAP();
  2274. DEBUG_CODE( _nMenuLevel = g_nMenuLevel++; )
  2275. _fMenuMode = TRUE;
  2276. _fInSubMenu = FALSE;
  2277. _nItemMove = -1;
  2278. _fCascadeAnimate = TRUE;
  2279. _hwndFocusPrev = NULL;
  2280. if (_fTopLevel)
  2281. {
  2282. // REVIEW (scotth): some embedded controls (like the surround
  2283. // video ctl on the carpoint website) have another thread that
  2284. // eats all the messages when the control has the focus.
  2285. // This prevents us from getting any messages once we're in
  2286. // menu mode. I don't understand why USER menus work yet.
  2287. // One way to work around this bug is to detect this case and
  2288. // set the focus to our main window for the duration.
  2289. if (GetWindowThreadProcessId(GetFocus(), NULL) != GetCurrentThreadId())
  2290. {
  2291. IShellBrowser* psb;
  2292. if (SUCCEEDED(QueryService(SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb))))
  2293. {
  2294. HWND hwndT;
  2295. psb->GetWindow(&hwndT);
  2296. _hwndFocusPrev = SetFocus(hwndT);
  2297. psb->Release();
  2298. }
  2299. }
  2300. _hCursorOld = GetCursor();
  2301. SetCursor(g_hCursorArrow);
  2302. HideCaret(NULL);
  2303. }
  2304. _SiteSetSubMenu(this, TRUE);
  2305. if (_pmtbTop)
  2306. {
  2307. HWND hwnd = _pmtbTop->_hwndMB;
  2308. if (!_fVertical && -1 == _nItemNew)
  2309. {
  2310. // The Alt key always highlights the first menu item initially
  2311. SetTracked(_pmtbTop);
  2312. ToolBar_SetHotItem(hwnd, 0);
  2313. NotifyWinEvent(EVENT_OBJECT_FOCUS, _pmtbTop->_hwndMB, OBJID_CLIENT,
  2314. GetIndexFromChild(TRUE, 0));
  2315. }
  2316. _pmtbTop->Activate(TRUE);
  2317. // The toolbar usually tracks mouse events. However, as the mouse
  2318. // moves over submenus, we still want the parent menubar to
  2319. // behave as if it has retained the focus (that is, keep the
  2320. // last selected item highlighted). This also prevents the toolbar
  2321. // from handling WM_MOUSELEAVE messages unnecessarily.
  2322. ToolBar_SetAnchorHighlight(hwnd, TRUE);
  2323. TraceMsg(TF_MENUBAND, "%d (pmb=%#08lx): Entering menu mode", DBG_THIS);
  2324. NotifyWinEvent(_fVertical? EVENT_SYSTEM_MENUPOPUPSTART: EVENT_SYSTEM_MENUSTART,
  2325. hwnd, OBJID_CLIENT, CHILDID_SELF);
  2326. }
  2327. if (_pmtbBottom)
  2328. {
  2329. _pmtbBottom->Activate(TRUE);
  2330. ToolBar_SetAnchorHighlight(_pmtbBottom->_hwndMB, TRUE); // Turn off anchoring
  2331. }
  2332. GetMessageFilter()->Push(_pmbState->GetContext(), this, _punkSite);
  2333. return S_OK;
  2334. }
  2335. void CMenuBand::_ExitMenuMode(void)
  2336. {
  2337. _fMenuMode = FALSE;
  2338. _nItemCur = -1;
  2339. _fPopupNewMenu = FALSE;
  2340. _fInitialSelect = FALSE;
  2341. CMBMsgFilter* pmf = GetMessageFilter();
  2342. if (_pmtbTop)
  2343. {
  2344. HWND hwnd = _pmtbTop->_hwndMB;
  2345. ToolBar_SetAnchorHighlight(hwnd, FALSE); // Turn off anchoring
  2346. if (!_fVertical)
  2347. {
  2348. // Use the first item, since we're assuming every menu must have
  2349. // at least one item
  2350. _pmtbTop->v_SendMenuNotification(0, TRUE);
  2351. // The user may have clicked outside the menu, which would have
  2352. // cancelled it. But since we set the ANCHORHIGHLIGHT attribute,
  2353. // the toolbar won't receive a message to cause it to
  2354. // remove the highlight. So do it explicitly now.
  2355. SetTracked(NULL);
  2356. UpdateWindow(hwnd);
  2357. }
  2358. _pmtbTop->Activate(FALSE);
  2359. NotifyWinEvent(_fVertical? EVENT_SYSTEM_MENUPOPUPEND: EVENT_SYSTEM_MENUEND,
  2360. hwnd, OBJID_CLIENT, CHILDID_SELF);
  2361. }
  2362. if (_pmtbBottom)
  2363. {
  2364. _pmtbBottom->Activate(FALSE);
  2365. ToolBar_SetAnchorHighlight(_pmtbBottom->_hwndMB, FALSE); // Turn off anchoring
  2366. }
  2367. pmf->Pop(_pmbState->GetContext());
  2368. _SiteSetSubMenu(this, FALSE);
  2369. if (_fTopLevel)
  2370. {
  2371. SetCursor(_hCursorOld);
  2372. ShowCaret(NULL);
  2373. pmf->SetContext(this, FALSE);
  2374. // We do this here, because ShowDW(FALSE) does not get called on the
  2375. // top level menu band. This resets the state, so that the accelerators
  2376. // are not shown.
  2377. if (_pmbState)
  2378. _pmbState->SetKeyboardCue(FALSE);
  2379. // Tell the menus to update their state to the current global cue state.
  2380. if (_pmtbTop)
  2381. _pmtbTop->SetKeyboardCue();
  2382. if (_pmtbTop != _pmtbBottom && _pmtbBottom)
  2383. _pmtbBottom->SetKeyboardCue();
  2384. }
  2385. if (_hwndFocusPrev)
  2386. SetFocus(_hwndFocusPrev);
  2387. if (_fTopLevel)
  2388. {
  2389. //
  2390. // The top-level menu has gone away. Win32 focus and ui-activation don't
  2391. // actually change when this happens, so the browser and focused dude have
  2392. // no idea that something happened and won't generate any AA event. So, we
  2393. // do it here for them. Note that if there was a selection inside the focused
  2394. // dude, we'll lose it. This is the best we can do for now, as we don't
  2395. // currently have a way to tell the focused/ui-active guy (who knows about the
  2396. // current selection) to reannounce focus.
  2397. //
  2398. HWND hwndFocus = GetFocus();
  2399. NotifyWinEvent(EVENT_OBJECT_FOCUS, hwndFocus, OBJID_CLIENT, CHILDID_SELF);
  2400. }
  2401. TraceMsg(TF_MENUBAND, "%d (pmb=%#08lx): Exited menu mode", DBG_THIS);
  2402. DEBUG_CODE( g_nMenuLevel--; )
  2403. DEBUG_CODE( _nMenuLevel = -1; )
  2404. if (g_dwProfileCAP & 0x00002000)
  2405. StopCAP();
  2406. }
  2407. /*----------------------------------------------------------
  2408. Purpose: IInputObject::UIActivateIO
  2409. Menubands CANNOT take the activation. Normally
  2410. a band would return S_OK and call the site's
  2411. OnFocusChangeIS method, so that its TranslateAcceleratorIO
  2412. method would receive keyboard messages.
  2413. However, menus are different. The window/toolbar that
  2414. currently has the activation must retain that activation
  2415. when the menu pops down. Because of this, menubands use
  2416. a GetMessage filter to intercept messages.
  2417. */
  2418. STDMETHODIMP CMenuBand::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
  2419. {
  2420. HRESULT hres;
  2421. ASSERT(NULL == lpMsg || IS_VALID_WRITE_PTR(lpMsg, MSG));
  2422. if (lpMsg != NULL)
  2423. {
  2424. // don't allow TAB to band (or any other 'non-explicit' activation).
  2425. // (if we just cared about TAB we'd check IsVK_TABCycler).
  2426. // all kinds of badness would result if we did.
  2427. // the band can't take focus (see above), so it can't obey the
  2428. // UIAct/OnFocChg rules (e.g. can't call OnFocusChangeIS), so
  2429. // our basic activation-tracking assumptions would be broken.
  2430. return S_FALSE;
  2431. }
  2432. if (fActivate)
  2433. {
  2434. TraceMsg(TF_MENUBAND, "%d (pmb=%#08lx): UIActivateIO(%d)", DBG_THIS, fActivate);
  2435. if (!_fMenuMode)
  2436. {
  2437. _EnterMenuMode();
  2438. // The toplevel menuband does not set the real activation.
  2439. // But the children do, so activation can be communicated
  2440. // with the parent menuband.
  2441. if (_fVertical)
  2442. {
  2443. IUnknown_OnFocusChangeIS(_punkSite, SAFECAST(this, IInputObject*), TRUE);
  2444. }
  2445. else
  2446. {
  2447. IUnknown_Exec(_punkSite, &CGID_Theater, THID_TOOLBARACTIVATED, 0, NULL, NULL);
  2448. }
  2449. }
  2450. if (_fPopupNewMenu)
  2451. {
  2452. _nItemCur = _nItemNew;
  2453. ASSERT(-1 != _nItemCur);
  2454. ASSERT(_pmtbTracked);
  2455. _fPopupNewMenu = FALSE;
  2456. _nItemNew = -1;
  2457. // Popup a menu
  2458. hres = _pmtbTracked->PopupOpen(_nItemCur);
  2459. if (FAILED(hres))
  2460. {
  2461. // Don't fail the activation
  2462. TraceMsg(TF_ERROR, "%d (pmb=%#08lx): PopupOpen failed", DBG_THIS);
  2463. MessageBeep(MB_OK);
  2464. }
  2465. else if (S_FALSE == hres)
  2466. {
  2467. // The submenu was modal and is finished now
  2468. _ExitMenuMode();
  2469. }
  2470. }
  2471. }
  2472. else if (_fMenuMode)
  2473. {
  2474. TraceMsg(TF_MENUBAND, "%d (pmb=%#08lx): UIActivateIO(%d)", DBG_THIS, fActivate);
  2475. ASSERT( !_fInSubMenu );
  2476. if (!_fTopLevel)
  2477. IUnknown_OnFocusChangeIS(_punkSite, SAFECAST(this, IInputObject*), FALSE);
  2478. _ExitMenuMode();
  2479. }
  2480. return S_FALSE;
  2481. }
  2482. /*----------------------------------------------------------
  2483. Purpose: IDeskBand::GetBandInfo method
  2484. */
  2485. HRESULT CMenuBand::GetBandInfo(DWORD dwBandID, DWORD fViewMode,
  2486. DESKBANDINFO* pdbi)
  2487. {
  2488. HRESULT hres = NOERROR;
  2489. _dwBandID = dwBandID; // critical for perf! (BandInfoChanged)
  2490. pdbi->dwMask &= ~DBIM_TITLE; // no title (ever, for now)
  2491. // We expect that _pmtbBottom should never be the only toolbar
  2492. // that exists in the menuband.
  2493. ASSERT(NULL == _pmtbBottom || _pmtbTop);
  2494. pdbi->dwModeFlags = DBIMF_USECHEVRON;
  2495. if (_pmtbTop)
  2496. {
  2497. // If the buttons need to be updated in the toolbars, the we should
  2498. // do this before we start asking them about their sizes....
  2499. if (_fForceButtonUpdate)
  2500. {
  2501. _UpdateButtons();
  2502. }
  2503. if (_fVertical)
  2504. {
  2505. pdbi->ptMaxSize.y = 0;
  2506. pdbi->ptMaxSize.x = 0;
  2507. SIZE size = {0};
  2508. if (_pmtbMenu)
  2509. {
  2510. // size param zero here => it's just an out param
  2511. _pmtbMenu->GetSize(&size);
  2512. // HACKHACK (lamadio): On downlevel, LARGE metrics mode causes
  2513. // Start menu to push the programs menu item off screen.
  2514. if (size.cy > (3 * GetSystemMetrics(SM_CYSCREEN) / 4))
  2515. {
  2516. Exec(&CGID_MenuBand, MBANDCID_SETICONSIZE, ISFBVIEWMODE_SMALLICONS, NULL, NULL);
  2517. size.cx = 0;
  2518. size.cy = 0;
  2519. _pmtbMenu->GetSize(&size);
  2520. }
  2521. pdbi->ptMaxSize.y = size.cy;
  2522. pdbi->ptMaxSize.x = size.cx;
  2523. }
  2524. if (_pmtbShellFolder)
  2525. {
  2526. // size param should be non-zero here => it's an in/out param
  2527. _pmtbShellFolder->GetSize(&size);
  2528. pdbi->ptMaxSize.y += size.cy + ((_pmtbMenu && !_fExpanded)? 1 : 0); // Minor sizing problem
  2529. pdbi->ptMaxSize.x = max(size.cx, pdbi->ptMaxSize.x);
  2530. }
  2531. pdbi->ptMinSize = pdbi->ptMaxSize;
  2532. }
  2533. else
  2534. {
  2535. HWND hwnd = _pmtbTop->_hwndMB;
  2536. ShowDW(TRUE);
  2537. SIZE rgSize;
  2538. if ( SendMessage( hwnd, TB_GETMAXSIZE, 0, (LPARAM) &rgSize ))
  2539. {
  2540. pdbi->ptActual.y = rgSize.cy;
  2541. SendMessage(hwnd, TB_GETIDEALSIZE, FALSE, (LPARAM)&pdbi->ptActual);
  2542. }
  2543. // make our min size identical to the size of the first button
  2544. // (we're assuming that the toolbar has at least one button)
  2545. RECT rc;
  2546. SendMessage(hwnd, TB_GETITEMRECT, 0, (WPARAM)&rc);
  2547. pdbi->ptMinSize.x = RECTWIDTH(rc);
  2548. pdbi->ptMinSize.y = RECTHEIGHT(rc);
  2549. }
  2550. }
  2551. return hres;
  2552. }
  2553. /*----------------------------------------------------------
  2554. Purpose: IOleService::Exec method
  2555. */
  2556. STDMETHODIMP CMenuBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
  2557. DWORD nCmdExecOpt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
  2558. {
  2559. // Don't do anything if we're closing.
  2560. if (_fClosing)
  2561. return E_FAIL;
  2562. if (pguidCmdGroup == NULL)
  2563. {
  2564. /*NOTHING*/
  2565. }
  2566. else if (IsEqualGUID(CGID_MENUDESKBAR, *pguidCmdGroup))
  2567. {
  2568. switch (nCmdID)
  2569. {
  2570. case MBCID_GETSIDE:
  2571. if (pvarargOut)
  2572. {
  2573. BOOL fOurChoice = FALSE;
  2574. pvarargOut->vt = VT_I4;
  2575. if (!_fTopLevel)
  2576. {
  2577. // if we are not the top level menu, we
  2578. // must continue with the direction our parent was in
  2579. IMenuPopup* pmpParent;
  2580. IUnknown_QueryService(_punkSite, SID_SMenuPopup, IID_PPV_ARG(IMenuPopup, &pmpParent));
  2581. if (pmpParent)
  2582. {
  2583. if (FAILED(IUnknown_Exec(pmpParent, pguidCmdGroup, nCmdID, nCmdExecOpt, pvarargIn, pvarargOut)))
  2584. fOurChoice = TRUE;
  2585. pmpParent->Release();
  2586. }
  2587. } else
  2588. fOurChoice = TRUE;
  2589. if (!fOurChoice)
  2590. {
  2591. // only use the parent's side hint if it is in the same orientation (ie, horizontal menubar to vertical popup
  2592. // means we need to make a new choice)
  2593. BOOL fParentVertical = (pvarargOut->lVal == MENUBAR_RIGHT || pvarargOut->lVal == MENUBAR_LEFT);
  2594. if (BOOLIFY(_fVertical) != BOOLIFY(fParentVertical))
  2595. fOurChoice = TRUE;
  2596. }
  2597. if (fOurChoice)
  2598. {
  2599. if (_fVertical)
  2600. {
  2601. HWND hWndMenuBand;
  2602. //
  2603. // The MenuBand is Mirrored , then start the first Menu Window
  2604. // as Mirrored. [samera]
  2605. //
  2606. if ((SUCCEEDED(GetWindow(&hWndMenuBand))) &&
  2607. (IS_WINDOW_RTL_MIRRORED(hWndMenuBand)) )
  2608. pvarargOut->lVal = MENUBAR_LEFT;
  2609. else
  2610. pvarargOut->lVal = MENUBAR_RIGHT;
  2611. }
  2612. else
  2613. pvarargOut->lVal = MENUBAR_BOTTOM;
  2614. }
  2615. }
  2616. return S_OK;
  2617. }
  2618. }
  2619. else if (IsEqualGUID(CGID_MenuBand, *pguidCmdGroup))
  2620. {
  2621. switch (nCmdID)
  2622. {
  2623. case MBANDCID_GETFONTS:
  2624. if (pvarargOut)
  2625. {
  2626. if (EVAL(_pmbm))
  2627. {
  2628. // this is not marshal-safe, but we're inproc
  2629. pvarargOut->vt = VT_UNKNOWN;
  2630. _pmbm->QueryInterface(IID_PPV_ARG(IUnknown, &pvarargOut->punkVal));
  2631. return S_OK;
  2632. }
  2633. else
  2634. return E_FAIL;
  2635. }
  2636. else
  2637. return E_INVALIDARG;
  2638. break;
  2639. case MBANDCID_SETFONTS:
  2640. if (pvarargIn && VT_UNKNOWN == pvarargIn->vt && pvarargIn->punkVal)
  2641. {
  2642. // this is not marshal-safe, but we're inproc
  2643. ATOMICRELEASE(_pmbm);
  2644. pvarargIn->punkVal->QueryInterface(CLSID_MenuBandMetrics, (void**)&_pmbm);
  2645. _fForceButtonUpdate = TRUE;
  2646. // Force Update of Toolbars:
  2647. if (_pmtbMenu)
  2648. _pmtbMenu->SetMenuBandMetrics(_pmbm);
  2649. if (_pmtbShellFolder)
  2650. _pmtbShellFolder->SetMenuBandMetrics(_pmbm);
  2651. }
  2652. else
  2653. return E_INVALIDARG;
  2654. break;
  2655. case MBANDCID_RECAPTURE:
  2656. GetMessageFilter()->RetakeCapture();
  2657. break;
  2658. case MBANDCID_NOTAREALSITE:
  2659. _fParentIsNotASite = BOOLIFY(nCmdExecOpt);
  2660. break;
  2661. case MBANDCID_ITEMDROPPED:
  2662. {
  2663. _fDragEntered = FALSE;
  2664. HWND hwndWorker = _pmbState->GetWorkerWindow(NULL);
  2665. if (hwndWorker && !HasWindowTopmostOwner(hwndWorker))
  2666. SetWindowPos(hwndWorker, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
  2667. }
  2668. break;
  2669. case MBANDCID_DRAGENTER:
  2670. _fDragEntered = TRUE;
  2671. break;
  2672. case MBANDCID_DRAGLEAVE:
  2673. _fDragEntered = FALSE;
  2674. break;
  2675. case MBANDCID_SELECTITEM:
  2676. {
  2677. int iPos = nCmdExecOpt;
  2678. // If they are passing vararg in, then this is an ID, not a position
  2679. if (pvarargIn && pvarargIn->vt == VT_I4)
  2680. {
  2681. _nItemNew = pvarargIn->lVal;
  2682. _fPopupNewItemOnShow = TRUE;
  2683. }
  2684. // This can be called outside of a created band.
  2685. if (_pmtbTop)
  2686. {
  2687. if (iPos == MBSI_NONE)
  2688. {
  2689. SetTracked(NULL);
  2690. }
  2691. else
  2692. {
  2693. CMenuToolbarBase* pmtb = (iPos == MBSI_LASTITEM) ? _pmtbBottom : _pmtbTop;
  2694. ASSERT(pmtb);
  2695. SetTracked(pmtb);
  2696. _pmtbTracked->SetHotItem(1, iPos, -1, HICF_OTHER);
  2697. // If the new hot item is in the obscured part of the menu, then the
  2698. // above call will have reentered & nulled out _pmtbTracked (since we
  2699. // drop down the chevron menu if the new hot item is obscured). So we
  2700. // need to revalidate _pmtbTracked.
  2701. if (!_pmtbTracked)
  2702. break;
  2703. NotifyWinEvent(EVENT_OBJECT_FOCUS, _pmtbTracked->_hwndMB, OBJID_CLIENT,
  2704. GetIndexFromChild(TRUE, iPos));
  2705. }
  2706. }
  2707. }
  2708. break;
  2709. case MBANDCID_KEYBOARD:
  2710. // If we've been executed because of a keyboard, then set the global
  2711. // state to reflect that. This is sent by MenuBar when it's ::Popup
  2712. // member is called with the flag MPPF_KEYBOARD. This is for start menu.
  2713. if (_pmbState)
  2714. _pmbState->SetKeyboardCue(TRUE);
  2715. break;
  2716. case MBANDCID_POPUPITEM:
  2717. if (pvarargIn && VT_I4 == pvarargIn->vt)
  2718. {
  2719. // we don't want to popup a sub menu if we're tracking a context menu...
  2720. if ( !((_pmtbBottom && _pmtbBottom->v_TrackingSubContextMenu()) ||
  2721. (_pmtbTop && _pmtbTop->v_TrackingSubContextMenu())))
  2722. {
  2723. // No tracked item? Well default to the top (For the chevron menu)
  2724. if (!_pmtbTracked)
  2725. {
  2726. SetTracked(_pmtbTop);
  2727. }
  2728. // We don't want to display the sub menu if we're not shown.
  2729. // We do this because we could have been dismissed before the message
  2730. // was routed.
  2731. if (_fShow && _pmtbTracked)
  2732. {
  2733. int iItem;
  2734. int iPos;
  2735. if (nCmdExecOpt & MBPUI_ITEMBYPOS)
  2736. {
  2737. iPos = pvarargIn->lVal;
  2738. iItem = GetButtonCmd(_pmtbTracked->_hwndMB, pvarargIn->lVal);
  2739. }
  2740. else
  2741. {
  2742. iPos = ToolBar_CommandToIndex(_pmtbTracked->_hwndMB, pvarargIn->lVal);
  2743. iItem = pvarargIn->lVal;
  2744. }
  2745. if (nCmdExecOpt & MBPUI_SETITEM)
  2746. {
  2747. // Set the hot item explicitly since this can be
  2748. // invoked by the keyboard and the mouse could be
  2749. // anywhere.
  2750. _pmtbTracked->SetHotItem(1, iPos, -1, HICF_OTHER);
  2751. // If the new hot item is in the obscured part of the menu, then the
  2752. // above call will have reentered & nulled out _pmtbTracked (since we
  2753. // drop down the chevron menu if the new hot item is obscured). So we
  2754. // need to revalidate _pmtbTracked.
  2755. if (!_pmtbTracked)
  2756. break;
  2757. NotifyWinEvent(EVENT_OBJECT_FOCUS, _pmtbTracked->_hwndMB, OBJID_CLIENT,
  2758. GetIndexFromChild(TRUE, iPos) );
  2759. }
  2760. _pmtbTracked->PopupHelper(iItem, nCmdExecOpt & MBPUI_INITIALSELECT);
  2761. }
  2762. }
  2763. }
  2764. break;
  2765. case MBANDCID_ISVERTICAL:
  2766. if (pvarargOut)
  2767. {
  2768. pvarargOut->vt = VT_BOOL;
  2769. pvarargOut->boolVal = (_fVertical)? VARIANT_TRUE: VARIANT_FALSE;
  2770. }
  2771. break;
  2772. case MBANDCID_SETICONSIZE:
  2773. ASSERT(nCmdExecOpt == ISFBVIEWMODE_SMALLICONS ||
  2774. nCmdExecOpt == ISFBVIEWMODE_LARGEICONS);
  2775. _uIconSize = nCmdExecOpt;
  2776. if (_pmtbTop)
  2777. _pmtbTop->v_UpdateIconSize(nCmdExecOpt, TRUE);
  2778. if (_pmtbBottom)
  2779. _pmtbBottom->v_UpdateIconSize(nCmdExecOpt, TRUE);
  2780. break;
  2781. case MBANDCID_SETSTATEOBJECT:
  2782. if (pvarargIn && VT_INT_PTR == pvarargIn->vt)
  2783. {
  2784. _pmbState = (CMenuBandState*)pvarargIn->byref;
  2785. }
  2786. break;
  2787. case MBANDCID_ISINSUBMENU:
  2788. if (_fInSubMenu || (_pmtbTracked && _pmtbTracked->v_TrackingSubContextMenu()))
  2789. return S_OK;
  2790. else
  2791. return S_FALSE;
  2792. break;
  2793. case MBANDCID_ISTRACKING:
  2794. if (_pmtbTracked && _pmtbTracked->v_TrackingSubContextMenu())
  2795. return S_OK;
  2796. else
  2797. return S_FALSE;
  2798. break;
  2799. case MBANDCID_REPOSITION:
  2800. // Don't reposition unless we're shown (Avoids artifacts onscreen of a bad positioning)
  2801. if (_fShow)
  2802. {
  2803. // Don't forget to reposition US!!!
  2804. IMenuPopup* pmdb;
  2805. DWORD dwFlags = MPPF_REPOSITION | MPPF_NOANIMATE;
  2806. // If we should force a reposition. This is so that we get
  2807. // the trickle down reposition so things overlap correctly
  2808. if (nCmdExecOpt)
  2809. dwFlags |= MPPF_FORCEZORDER;
  2810. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SMenuPopup, IID_PPV_ARG(IMenuPopup, &pmdb))))
  2811. {
  2812. pmdb->Popup(NULL, NULL, dwFlags);
  2813. pmdb->Release();
  2814. }
  2815. // Reposition the Tracked sub menu based on the current popped up item
  2816. // since this pane has now moved
  2817. // If they have a sub menu, tell them to reposition as well.
  2818. if (_fInSubMenu && _pmtbTracked)
  2819. {
  2820. IUnknown_QueryServiceExec(_pmpSubMenu, SID_SMenuBandChild,
  2821. &CGID_MenuBand, MBANDCID_REPOSITION, nCmdExecOpt, NULL, NULL);
  2822. }
  2823. _pmbState->PutTipOnTop();
  2824. }
  2825. break;
  2826. case MBANDCID_REFRESH:
  2827. InvalidateItem(NULL, SMINV_REFRESH);
  2828. break;
  2829. case MBANDCID_EXPAND:
  2830. if (_pmtbShellFolder)
  2831. _pmtbShellFolder->Expand(TRUE);
  2832. if (_pmtbMenu)
  2833. _pmtbMenu->Expand(TRUE);
  2834. break;
  2835. case MBANDCID_DRAGCANCEL:
  2836. // If one of the Sub bands in the menu heirarchy has the drag
  2837. // (Either because of Drag enter or because of the drop) then
  2838. // we do not want to cancel.
  2839. if (!_pmbState->HasDrag())
  2840. _CancelMode(MPOS_FULLCANCEL);
  2841. break;
  2842. case MBANDCID_EXECUTE:
  2843. ASSERT(pvarargIn != NULL);
  2844. if (_pmtbTop && _pmtbTop->IsWindowOwner((HWND)pvarargIn->ullVal) == S_OK)
  2845. _pmtbTop->v_ExecItem((int)nCmdExecOpt);
  2846. else if (_pmtbBottom && _pmtbBottom->IsWindowOwner((HWND)pvarargIn->ullVal) == S_OK)
  2847. _pmtbBottom->v_ExecItem((int)nCmdExecOpt);
  2848. _SiteOnSelect(MPOS_EXECUTE);
  2849. break;
  2850. }
  2851. // Don't bother passing CGID_MenuBand commands to CToolBand
  2852. return S_OK;
  2853. }
  2854. return SUPERCLASS::Exec(pguidCmdGroup, nCmdID, nCmdExecOpt, pvarargIn, pvarargOut);
  2855. }
  2856. /*----------------------------------------------------------
  2857. Purpose: IDockingWindow::CloseDW method.
  2858. */
  2859. STDMETHODIMP CMenuBand::CloseDW(DWORD dw)
  2860. {
  2861. // We don't want to destroy the band if it's cached.
  2862. // That means it's the caller's respocibility to Unset this bit and call CloseDW explicitly
  2863. if (_dwFlags & SMINIT_CACHED)
  2864. return S_OK;
  2865. if (_pmtbMenu)
  2866. {
  2867. _pmtbMenu->v_Close();
  2868. }
  2869. if (_pmtbShellFolder)
  2870. {
  2871. _pmtbShellFolder->v_Close();
  2872. }
  2873. if (_pmpSubMenu)
  2874. {
  2875. _fInSubMenu = FALSE;
  2876. IUnknown_SetSite(_pmpSubMenu, NULL);
  2877. ATOMICRELEASE(_pmpSubMenu);
  2878. }
  2879. // Since we're blowing away all of the menus,
  2880. // Top and bottom are invalid
  2881. _pmtbTracked = _pmtbTop = _pmtbBottom = NULL;
  2882. // We don't want our base class to blow this window away. It belongs to someone else.
  2883. _hwnd = NULL;
  2884. _fClosing = TRUE;
  2885. return SUPERCLASS::CloseDW(dw);
  2886. }
  2887. /*----------------------------------------------------------
  2888. Purpose: IDockingWindow::ShowDW method
  2889. Notes:
  2890. for the start menu (non-browser) case, we bracket* the top-level popup
  2891. operation w/ a LockSetForegroundWindow so that another app can't steal
  2892. the foreground and collapse our menu. (nt5:172813: don't do it for
  2893. the browser case since a) we don't want to and b) ShowDW(FALSE) isn't
  2894. called until exit the browser so we'd be permanently locked!)
  2895. */
  2896. STDMETHODIMP CMenuBand::ShowDW(BOOL fShow)
  2897. {
  2898. CMBMsgFilter* pmf = GetMessageFilter();
  2899. // Prevent rentrancy when we're already shown.
  2900. ASSERT((int)_fShow == BOOLIFY(_fShow));
  2901. if ((int)_fShow == BOOLIFY(fShow))
  2902. return NOERROR;
  2903. HRESULT hr = SUPERCLASS::ShowDW(fShow);
  2904. if (SUCCEEDED(hr))
  2905. {
  2906. if (!fShow)
  2907. {
  2908. _fShow = FALSE;
  2909. if (_fTopLevel)
  2910. {
  2911. if (_fVertical)
  2912. {
  2913. // (_fTopLevel && _fVertical) => start menu
  2914. LockSetForegroundWindow(LSFW_UNLOCK);
  2915. }
  2916. else if (_dwFlags & SMINIT_USEMESSAGEFILTER)
  2917. {
  2918. pmf->SetHook(FALSE, TRUE);
  2919. pmf->SetTopMost(this);
  2920. }
  2921. }
  2922. if ((_fTopLevel || _fParentIsHorizontal) && _pmbState)
  2923. {
  2924. // Reset to not have the drag when we collapse.
  2925. _pmbState->HasDrag(FALSE);
  2926. _pmbState->SetExpand(FALSE);
  2927. _pmbState->SetUEMState(0);
  2928. }
  2929. hr = _CallCB(SMC_EXITMENU);
  2930. }
  2931. else
  2932. {
  2933. hr = _CallCB(SMC_INITMENU);
  2934. _fClosing = FALSE;
  2935. _fShow = TRUE;
  2936. _GetFontMetrics();
  2937. if (_fTopLevel)
  2938. {
  2939. // We set the context here so that the ReEngage causes the message filter
  2940. // to start taking messages on a TopLevel::Show. This prevents a problem
  2941. // where tracking doesn't work when switching between Favorites and Start Menu
  2942. _pmbState->SetContext(this);
  2943. pmf->SetContext(this, TRUE);
  2944. pmf->ReEngage(_pmbState->GetContext());
  2945. if (_hwndMenuOwner && _fVertical)
  2946. SetForegroundWindow(_hwndMenuOwner);
  2947. if (_fVertical)
  2948. {
  2949. // (_fTopLevel && _fVertical) => start menu
  2950. LockSetForegroundWindow(LSFW_LOCK);
  2951. }
  2952. else if (_dwFlags & SMINIT_USEMESSAGEFILTER)
  2953. {
  2954. pmf->SetHook(TRUE, TRUE);
  2955. pmf->SetTopMost(this);
  2956. }
  2957. _pmbState->CreateFader();
  2958. }
  2959. }
  2960. if (_pmtbShellFolder)
  2961. _pmtbShellFolder->v_Show(_fShow, _fForceButtonUpdate);
  2962. // Menu needs to be last so that it can update the seperator.
  2963. if (_pmtbMenu)
  2964. _pmtbMenu->v_Show(_fShow, _fForceButtonUpdate);
  2965. if (_fPopupNewItemOnShow)
  2966. {
  2967. HWND hwnd = _pmbState ? _pmbState->GetSubclassedHWND() : NULL;
  2968. if (hwnd || _pmtbMenu)
  2969. {
  2970. PostMessage(hwnd ? hwnd : _pmtbMenu->_hwndMB, g_nMBPopupOpen,
  2971. _nItemNew, MAKELPARAM(TRUE, TRUE));
  2972. }
  2973. _fPopupNewItemOnShow = FALSE;
  2974. }
  2975. _fForceButtonUpdate = FALSE;
  2976. }
  2977. return hr;
  2978. }
  2979. void CMenuBand::_GetFontMetrics()
  2980. {
  2981. if (_fTopLevel)
  2982. {
  2983. if (!_pmbm)
  2984. _pmbm = new CMenuBandMetrics();
  2985. if (_pmbm && _pmtbTop)
  2986. {
  2987. // We need only 1 HWND
  2988. _pmbm->Init(_pmtbTop->_hwndMB);
  2989. }
  2990. }
  2991. else if (!_pmbm)
  2992. {
  2993. AssertMsg(0, TEXT("When this menuband was created, someone forgot to set the metrics"));
  2994. VARIANTARG vargOut;
  2995. HRESULT hres = IUnknown_QueryServiceExec(_punkSite, SID_SMenuBandTop, &CGID_MenuBand, MBANDCID_GETFONTS, 0, NULL, &vargOut);
  2996. if (SUCCEEDED(hres))
  2997. {
  2998. if (vargOut.vt == VT_UNKNOWN && vargOut.punkVal)
  2999. {
  3000. vargOut.punkVal->QueryInterface(CLSID_MenuBandMetrics, (void**)&_pmbm);
  3001. }
  3002. VariantClear(&vargOut);
  3003. }
  3004. }
  3005. }
  3006. /*----------------------------------------------------------
  3007. Purpose: IMenuPopup::OnSelect method
  3008. This allows the child menubar to tell us when and how
  3009. to bail out of the menu.
  3010. */
  3011. STDMETHODIMP CMenuBand::OnSelect(DWORD dwType)
  3012. {
  3013. int iIndex;
  3014. switch (dwType)
  3015. {
  3016. case MPOS_CHILDTRACKING:
  3017. // this means that our child did get tracked over it, so we should abort any timeout to destroy it
  3018. if (_pmtbTracked)
  3019. {
  3020. HWND hwnd = _pmtbTracked->_hwndMB;
  3021. if (_nItemTimer)
  3022. {
  3023. _pmtbTracked->KillPopupTimer();
  3024. // Use the command id of the SubMenu that we actually have cascaded out.
  3025. iIndex = ToolBar_CommandToIndex(hwnd, _nItemSubMenu);
  3026. ToolBar_SetHotItem(hwnd, iIndex);
  3027. }
  3028. KillTimer(hwnd, MBTIMER_DRAGOVER);
  3029. _SiteOnSelect(dwType);
  3030. }
  3031. break;
  3032. case MPOS_SELECTLEFT:
  3033. if (!_fVertical)
  3034. _OnSelectArrow(-1);
  3035. else
  3036. {
  3037. // Cancel the child submenu. Hitting left arrow is like
  3038. // hitting escape.
  3039. _SubMenuOnSelect(MPOS_CANCELLEVEL);
  3040. }
  3041. break;
  3042. case MPOS_SELECTRIGHT:
  3043. if (!_fVertical)
  3044. _OnSelectArrow(1);
  3045. else
  3046. {
  3047. // The right arrow gets propagated up to the top, so
  3048. // a fully cascaded menu will be cancelled and the
  3049. // top level menuband will move to the next menu to the
  3050. // right.
  3051. _SiteOnSelect(dwType);
  3052. }
  3053. break;
  3054. case MPOS_CANCELLEVEL:
  3055. // Forward onto submenu
  3056. _SubMenuOnSelect(dwType);
  3057. break;
  3058. case MPOS_FULLCANCEL:
  3059. case MPOS_EXECUTE:
  3060. DEBUG_CODE( TraceMsg(TF_MENUBAND, "%d (pmb=%#08lx): CMenuToolbarBase received %s",
  3061. DBG_THIS, MPOS_FULLCANCEL == dwType ? TEXT("MPOS_FULLCANCEL") : TEXT("MPOS_EXECUTE")); )
  3062. _CancelMode(dwType);
  3063. break;
  3064. }
  3065. return S_OK;
  3066. }
  3067. void CMenuBand::SetTrackMenuPopup(IUnknown* punk)
  3068. {
  3069. ATOMICRELEASE(_pmpTrackPopup);
  3070. if (punk)
  3071. {
  3072. punk->QueryInterface(IID_PPV_ARG(IMenuPopup, &_pmpTrackPopup));
  3073. }
  3074. }
  3075. /*----------------------------------------------------------
  3076. Purpose: Set the currently tracked toolbar. Only one
  3077. of the toolbars can have the "activation" at one time.
  3078. */
  3079. BOOL CMenuBand::SetTracked(CMenuToolbarBase* pmtb)
  3080. {
  3081. if (pmtb == _pmtbTracked)
  3082. return FALSE;
  3083. if (_pmtbTracked)
  3084. {
  3085. // Tell the existing toolbar we're leaving him
  3086. SendMessage(_pmtbTracked->_hwndMB, TB_SETHOTITEM2, -1, HICF_LEAVING);
  3087. }
  3088. _pmtbTracked = pmtb;
  3089. if (_pmtbTracked)
  3090. {
  3091. // This is for accessibility.
  3092. HWND hwnd = _pmtbTracked->_hwndMB;
  3093. int iHotItem = ToolBar_GetHotItem(hwnd);
  3094. if (iHotItem >= 0)
  3095. {
  3096. // Toolbar Items are 0 based, Accessibility apps require 1 based
  3097. NotifyWinEvent(EVENT_OBJECT_FOCUS, hwnd, OBJID_CLIENT,
  3098. GetIndexFromChild(_pmtbTracked->GetFlags() & SMSET_TOP, iHotItem));
  3099. }
  3100. }
  3101. return TRUE;
  3102. }
  3103. void CMenuBand::_OnSelectArrow(int iDir)
  3104. {
  3105. _fKeyboardSelected = TRUE;
  3106. int iIndex;
  3107. if (!_pmtbTracked)
  3108. {
  3109. if (iDir < 0)
  3110. {
  3111. SetTracked(_pmtbBottom);
  3112. iIndex = ToolBar_ButtonCount(_pmtbTracked->_hwndMB) - 1;
  3113. }
  3114. else
  3115. {
  3116. SetTracked(_pmtbTop);
  3117. iIndex = 0;
  3118. }
  3119. // This can happen when going to the chevron.
  3120. if (_pmtbTracked)
  3121. _pmtbTracked->SetHotItem(iDir, iIndex, -1, HICF_ARROWKEYS);
  3122. }
  3123. else
  3124. {
  3125. HWND hwnd = _pmtbTracked->_hwndMB;
  3126. iIndex = ToolBar_GetHotItem(hwnd);
  3127. int iCount = ToolBar_ButtonCount(hwnd);
  3128. // Set the hot item explicitly since this is invoked by the
  3129. // keyboard and the mouse could be anywhere.
  3130. // cycle iIndex by iDir (add extra iCount to avoid negative number problems
  3131. iIndex = (iIndex + iCount + iDir) % iCount;
  3132. ToolBar_SetHotItem(hwnd, iIndex);
  3133. }
  3134. if (_pmtbTracked)
  3135. {
  3136. NotifyWinEvent(EVENT_OBJECT_FOCUS, _pmtbTracked->_hwndMB, OBJID_CLIENT,
  3137. GetIndexFromChild(_pmtbTracked->GetFlags() & SMSET_TOP, iIndex));
  3138. }
  3139. _fKeyboardSelected = FALSE;
  3140. }
  3141. void CMenuBand::_CancelMode(DWORD dwType)
  3142. {
  3143. // Tell the hosting site to cancel this level
  3144. if (_fParentIsNotASite)
  3145. UIActivateIO(FALSE, NULL);
  3146. else
  3147. _SiteOnSelect(dwType);
  3148. }
  3149. HRESULT CMenuBand::OnPosRectChangeDB (LPRECT prc)
  3150. {
  3151. // We want the HMENU portion to ALWAYS have the maximum allowed.
  3152. RECT rcMenu = {0};
  3153. SIZE sizeMenu = {0};
  3154. SIZE sizeSF = {0};
  3155. SIZE sizeMax;
  3156. if (_pmtbMenu)
  3157. _pmtbMenu->GetSize(&sizeMenu);
  3158. if (_pmtbShellFolder)
  3159. _pmtbShellFolder->GetSize(&sizeSF);
  3160. if (sizeSF.cx > sizeMenu.cx)
  3161. sizeMax = sizeSF;
  3162. else
  3163. sizeMax = sizeMenu;
  3164. if (_pmtbMenu)
  3165. {
  3166. if (_pmtbMenu->GetFlags() & SMSET_TOP)
  3167. {
  3168. rcMenu.bottom = sizeMenu.cy;
  3169. rcMenu.right = prc->right;
  3170. }
  3171. else
  3172. {
  3173. rcMenu.bottom = prc->bottom;
  3174. rcMenu.right = prc->right;
  3175. rcMenu.top = prc->bottom - sizeMenu.cy;
  3176. rcMenu.left = 0;
  3177. }
  3178. if (RECTHEIGHT(rcMenu) > RECTHEIGHT(*prc))
  3179. {
  3180. rcMenu.bottom -= RECTHEIGHT(rcMenu) - RECTHEIGHT(*prc);
  3181. }
  3182. _pmtbMenu->SetWindowPos(&sizeMax, &rcMenu, 0);
  3183. }
  3184. if (_pmtbShellFolder)
  3185. {
  3186. RECT rc = *prc;
  3187. if (_pmtbShellFolder->GetFlags() & SMSET_TOP)
  3188. {
  3189. rc.bottom = prc->bottom - RECTHEIGHT(rcMenu);
  3190. }
  3191. else
  3192. {
  3193. rc.top = prc->top + RECTHEIGHT(rcMenu);
  3194. }
  3195. _pmtbShellFolder->SetWindowPos(&sizeMax, &rc, 0);
  3196. }
  3197. return NOERROR;
  3198. }
  3199. HRESULT IUnknown_OnSelect(IUnknown* punk, DWORD dwType, REFGUID guid)
  3200. {
  3201. HRESULT hres;
  3202. IMenuPopup * pmp;
  3203. hres = IUnknown_QueryService(punk, guid, IID_PPV_ARG(IMenuPopup, &pmp));
  3204. if (SUCCEEDED(hres))
  3205. {
  3206. pmp->OnSelect(dwType);
  3207. pmp->Release();
  3208. }
  3209. return hres;
  3210. }
  3211. HRESULT CMenuBand::_SiteOnSelect(DWORD dwType)
  3212. {
  3213. return IUnknown_OnSelect(_punkSite, dwType, SID_SMenuPopup);
  3214. }
  3215. HRESULT CMenuBand::_SubMenuOnSelect(DWORD dwType)
  3216. {
  3217. IMenuPopup* pmp = _pmpSubMenu;
  3218. if (_pmpTrackPopup)
  3219. pmp = _pmpTrackPopup;
  3220. return IUnknown_OnSelect(pmp, dwType, SID_SMenuPopup);
  3221. }
  3222. HRESULT CMenuBand::GetTop(CMenuToolbarBase** ppmtbTop)
  3223. {
  3224. *ppmtbTop = _pmtbTop;
  3225. if (*ppmtbTop)
  3226. {
  3227. (*ppmtbTop)->AddRef();
  3228. return NOERROR;
  3229. }
  3230. return E_FAIL;
  3231. }
  3232. HRESULT CMenuBand::GetBottom(CMenuToolbarBase** ppmtbBottom)
  3233. {
  3234. *ppmtbBottom = _pmtbBottom;
  3235. if (*ppmtbBottom)
  3236. {
  3237. (*ppmtbBottom)->AddRef();
  3238. return NOERROR;
  3239. }
  3240. return E_FAIL;
  3241. }
  3242. HRESULT CMenuBand::GetTracked(CMenuToolbarBase** ppmtbTracked)
  3243. {
  3244. *ppmtbTracked = _pmtbTracked;
  3245. if (*ppmtbTracked)
  3246. {
  3247. (*ppmtbTracked)->AddRef();
  3248. return NOERROR;
  3249. }
  3250. return E_FAIL;
  3251. }
  3252. HRESULT CMenuBand::GetParentSite(REFIID riid, void** ppvObj)
  3253. {
  3254. if (_punkSite)
  3255. return _punkSite->QueryInterface(riid, ppvObj);
  3256. return E_FAIL;
  3257. }
  3258. HRESULT CMenuBand::GetState(BOOL* pfVertical, BOOL* pfOpen)
  3259. {
  3260. *pfVertical = _fVertical;
  3261. *pfOpen = _fMenuMode;
  3262. return NOERROR;
  3263. }
  3264. HRESULT CMenuBand::DoDefaultAction(VARIANT* pvarChild)
  3265. {
  3266. if (pvarChild->lVal != CHILDID_SELF)
  3267. {
  3268. CMenuToolbarBase* pmtb = (pvarChild->lVal & TOOLBAR_MASK)? _pmtbTop : _pmtbBottom;
  3269. int idCmd = GetButtonCmd(pmtb->_hwndMB, (pvarChild->lVal & ~TOOLBAR_MASK) - 1);
  3270. SendMessage(pmtb->_hwndMB, TB_SETHOTITEM2, idCmd, HICF_OTHER | HICF_TOGGLEDROPDOWN);
  3271. }
  3272. else
  3273. {
  3274. _CancelMode(MPOS_CANCELLEVEL);
  3275. }
  3276. return NOERROR;
  3277. }
  3278. /*----------------------------------------------------------
  3279. Purpose: IShellMenuAcc::GetSubMenu method
  3280. */
  3281. HRESULT CMenuBand::GetSubMenu(VARIANT* pvarChild, REFIID riid, void** ppvObj)
  3282. {
  3283. HRESULT hres = E_FAIL;
  3284. CMenuToolbarBase* pmtb = (pvarChild->lVal & TOOLBAR_MASK)? _pmtbTop : _pmtbBottom;
  3285. int idCmd = GetButtonCmd(pmtb->_hwndMB, (pvarChild->lVal & ~TOOLBAR_MASK) - 1);
  3286. *ppvObj = NULL;
  3287. if (idCmd != -1 && pmtb)
  3288. {
  3289. hres = pmtb->v_GetSubMenu(idCmd, &SID_SMenuBandChild, riid, ppvObj);
  3290. }
  3291. return hres;
  3292. }
  3293. /*----------------------------------------------------------
  3294. Purpose: IShellMenu2::GetSubMenu method
  3295. */
  3296. HRESULT CMenuBand::GetSubMenu(UINT idCmd, REFIID riid, void** ppvObj)
  3297. {
  3298. HRESULT hres = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
  3299. if (_pmtbMenu)
  3300. {
  3301. hres = _pmtbMenu->v_GetSubMenu(idCmd, &SID_SMenuBandChild, riid, ppvObj);
  3302. }
  3303. if (hres == HRESULT_FROM_WIN32(ERROR_NOT_FOUND) && _pmtbShellFolder)
  3304. {
  3305. hres = _pmtbShellFolder->v_GetSubMenu(idCmd, &SID_SMenuBandChild, riid, ppvObj);
  3306. }
  3307. return hres;
  3308. }
  3309. HRESULT CMenuBand::SetToolbar(HWND hwnd, DWORD dwFlags)
  3310. {
  3311. HRESULT hr = E_OUTOFMEMORY;
  3312. CMenuToolbarBase *pmtb = ToolbarMenu_Create(hwnd);
  3313. if (pmtb)
  3314. {
  3315. hr = SetMenuToolbar(SAFECAST(pmtb, IWinEventHandler*), dwFlags);
  3316. // DONT release! The menus break com identity rules because of a foobar when they were
  3317. // initially designed.
  3318. }
  3319. return hr;
  3320. }
  3321. HRESULT CMenuBand::SetMinWidth(int cxMenu)
  3322. {
  3323. if (_pmtbMenu)
  3324. {
  3325. // Yes
  3326. // HACK HACK. this should QI for a menustatic specific interface to do this.
  3327. return _pmtbMenu->SetMinWidth(cxMenu);
  3328. }
  3329. else
  3330. return E_FAIL;
  3331. }
  3332. HRESULT CMenuBand::SetNoBorder(BOOL fNoBorder)
  3333. {
  3334. HRESULT hr = S_OK;
  3335. _fNoBorder = fNoBorder;
  3336. if (_pmtbMenu)
  3337. {
  3338. // Yes
  3339. // HACK HACK. this should QI for a menustatic specific interface to do this.
  3340. hr = _pmtbMenu->SetNoBorder(fNoBorder);
  3341. }
  3342. if (_pmtbShellFolder)
  3343. {
  3344. _pmtbShellFolder->SetNoBorder(fNoBorder);
  3345. }
  3346. if (_punkSite)
  3347. {
  3348. hr = IUnknown_QueryServiceExec(_punkSite, SID_SMenuPopup, &CGID_MENUDESKBAR, MBCID_NOBORDER, fNoBorder, NULL, NULL);
  3349. }
  3350. return hr;
  3351. }
  3352. HRESULT CMenuBand::SetTheme(LPCWSTR pszTheme)
  3353. {
  3354. HRESULT hr = S_OK;
  3355. Str_SetPtr(&_pszTheme, pszTheme);
  3356. if (_pmtbMenu)
  3357. {
  3358. // Yes
  3359. // HACK HACK. this should QI for a menustatic specific interface to do this.
  3360. hr = _pmtbMenu->SetTheme(_pszTheme);
  3361. }
  3362. if (_pmtbShellFolder)
  3363. {
  3364. _pmtbShellFolder->SetTheme(_pszTheme);
  3365. }
  3366. return hr;
  3367. }
  3368. HRESULT CMenuBand::IsEmpty()
  3369. {
  3370. BOOL fReturn = TRUE;
  3371. if (_pmtbShellFolder)
  3372. fReturn = _pmtbShellFolder->IsEmpty();
  3373. if (fReturn && _pmtbMenu)
  3374. fReturn = _pmtbMenu->IsEmpty();
  3375. return fReturn? S_OK : S_FALSE;
  3376. }
  3377. //----------------------------------------------------------------------------
  3378. // CMenuBandMetrics
  3379. //
  3380. //----------------------------------------------------------------------------
  3381. COLORREF GetLumColor(int isys, int iLumAdjust)
  3382. {
  3383. WORD iHue;
  3384. WORD iLum;
  3385. WORD iSat;
  3386. COLORREF clr = (COLORREF)GetSysColor(isys);
  3387. HDC hdc = GetDC(NULL);
  3388. // Office CommandBars use this same algorithm for their "intellimenus"
  3389. // colors. We prefer to call them "expando menus"...
  3390. if (hdc)
  3391. {
  3392. int cColors = GetDeviceCaps(hdc, BITSPIXEL);
  3393. ReleaseDC(NULL, hdc);
  3394. switch (cColors)
  3395. {
  3396. case 4: // 16 Colors
  3397. case 8: // 256 Colors
  3398. // Default to using Button Face
  3399. break;
  3400. default: // 256+ colors
  3401. ColorRGBToHLS(clr, &iHue, &iLum, &iSat);
  3402. if (iLum > 220)
  3403. iLum -= iLumAdjust;
  3404. else if (iLum <= 20)
  3405. iLum += 2 * iLumAdjust;
  3406. else
  3407. iLum += iLumAdjust;
  3408. clr = ColorHLSToRGB(iHue, iLum, iSat);
  3409. break;
  3410. }
  3411. }
  3412. return clr;
  3413. }
  3414. ULONG CMenuBandMetrics::AddRef()
  3415. {
  3416. return ++_cRef;
  3417. }
  3418. ULONG CMenuBandMetrics::Release()
  3419. {
  3420. ASSERT(_cRef > 0);
  3421. if (--_cRef > 0)
  3422. return _cRef;
  3423. delete this;
  3424. return 0;
  3425. }
  3426. HRESULT CMenuBandMetrics::QueryInterface(REFIID riid, LPVOID * ppvObj)
  3427. {
  3428. if (IsEqualIID(riid, IID_IUnknown))
  3429. {
  3430. *ppvObj = SAFECAST(this, IUnknown*);
  3431. }
  3432. else if (IsEqualIID(riid, CLSID_MenuBandMetrics))
  3433. {
  3434. *ppvObj = this;
  3435. }
  3436. else
  3437. {
  3438. *ppvObj = NULL;
  3439. return E_FAIL;
  3440. }
  3441. AddRef();
  3442. return S_OK;
  3443. }
  3444. CMenuBandMetrics::CMenuBandMetrics()
  3445. : _cRef(1)
  3446. {
  3447. }
  3448. void CMenuBandMetrics::Init(HWND hwnd)
  3449. {
  3450. if (_fInit)
  3451. return;
  3452. _SetMenuFont();
  3453. _SetColors();
  3454. HIGHCONTRAST hc = {sizeof(HIGHCONTRAST)};
  3455. if (SystemParametersInfoA(SPI_GETHIGHCONTRAST, sizeof(hc), &hc, 0))
  3456. {
  3457. _fHighContrastMode = (HCF_HIGHCONTRASTON & hc.dwFlags);
  3458. }
  3459. if (g_dwPrototype & 0x00000100)
  3460. {
  3461. SystemParametersInfo(SPI_SETFLATMENU, 0, IntToPtr(TRUE), SPIF_SENDCHANGE);
  3462. SystemParametersInfo(SPI_SETDROPSHADOW, 0, IntToPtr(TRUE), SPIF_SENDCHANGE);
  3463. }
  3464. SystemParametersInfo(SPI_GETFLATMENU, 0, (PVOID)&_fFlatMenuMode, 0);
  3465. _SetArrowFont(hwnd);
  3466. _SetChevronFont(hwnd);
  3467. #ifndef DRAWEDGE
  3468. _SetPaintMetrics(hwnd);
  3469. #endif
  3470. _SetTextBrush(hwnd);
  3471. _fInit = TRUE;
  3472. }
  3473. CMenuBandMetrics::~CMenuBandMetrics()
  3474. {
  3475. if (_hFontMenu)
  3476. DeleteObject(_hFontMenu);
  3477. if (_hFontArrow)
  3478. DeleteObject(_hFontArrow);
  3479. if (_hFontChevron)
  3480. DeleteObject(_hFontChevron);
  3481. if (_hbrText)
  3482. DeleteObject(_hbrText);
  3483. #ifndef DRAWEDGE
  3484. if (_hPenHighlight)
  3485. DeleteObject(_hPenHighlight);
  3486. if (_hPenShadow)
  3487. DeleteObject(_hPenShadow);
  3488. #endif
  3489. }
  3490. HFONT CMenuBandMetrics::_CalcFont(HWND hwnd, LPCTSTR pszFont, DWORD dwCharSet, TCHAR ch, int* pcx,
  3491. int* pcy, int* pcxMargin, int iOrientation, int iWeight)
  3492. {
  3493. ASSERT(hwnd);
  3494. HFONT hFontOld, hFontRet = NULL;
  3495. TEXTMETRIC tm;
  3496. RECT rect={0};
  3497. int cx = 0, cy = 0, cxM = 0;
  3498. HDC hdc = GetDC(hwnd);
  3499. if (hdc)
  3500. {
  3501. hFontOld = (HFONT)SelectObject(hdc, _hFontMenu);
  3502. GetTextMetrics(hdc, &tm);
  3503. // Set the font height (based on original USER code)
  3504. cy = ((tm.tmHeight + tm.tmExternalLeading + GetSystemMetrics(SM_CYBORDER)) & 0xFFFE) - 1;
  3505. // Use the menu font's avg character width as the margin.
  3506. cxM = tm.tmAveCharWidth; // Not exactly how USER does it, but close
  3507. // Shlwapi wraps the ansi/unicode behavior.
  3508. hFontRet = CreateFontWrap(cy, 0, iOrientation, 0, iWeight, 0, 0, 0, dwCharSet, 0, 0, 0, 0, pszFont);
  3509. if (TPTR(hFontRet))
  3510. {
  3511. // Calc width of arrow using this new font
  3512. SelectObject(hdc, hFontRet);
  3513. if (DrawText(hdc, &ch, 1, &rect, DT_CALCRECT | DT_SINGLELINE | DT_LEFT | DT_VCENTER))
  3514. cx = rect.right;
  3515. else
  3516. cx = tm.tmMaxCharWidth;
  3517. }
  3518. else
  3519. {
  3520. cx = tm.tmMaxCharWidth;
  3521. }
  3522. SelectObject(hdc, hFontOld);
  3523. ReleaseDC(hwnd, hdc);
  3524. }
  3525. *pcx = cx;
  3526. *pcy = cy;
  3527. *pcxMargin = cxM;
  3528. return hFontRet;
  3529. }
  3530. /*
  3531. Call after _SetMenuFont()
  3532. */
  3533. void CMenuBandMetrics::_SetChevronFont(HWND hwnd)
  3534. {
  3535. ASSERT(!_hFontChevron);
  3536. TCHAR szPath[MAX_PATH];
  3537. NONCLIENTMETRICSA ncm;
  3538. ncm.cbSize = sizeof(ncm);
  3539. // Should only fail with bad parameters...
  3540. EVAL(SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0));
  3541. // Obtain the font's metrics
  3542. SHAnsiToTChar(ncm.lfMenuFont.lfFaceName, szPath, ARRAYSIZE(szPath));
  3543. _hFontChevron = _CalcFont(hwnd, szPath, DEFAULT_CHARSET, CH_MENUARROW, &_cxChevron, &_cyChevron,
  3544. &_cxChevron, -900, FW_NORMAL);
  3545. }
  3546. /*
  3547. Call after _SetMenuFont()
  3548. */
  3549. void CMenuBandMetrics::_SetArrowFont(HWND hwnd)
  3550. {
  3551. ASSERT(!_hFontArrow);
  3552. ASSERT(_hFontMenu);
  3553. // Obtain the font's metrics
  3554. if (_hFontMenu)
  3555. {
  3556. _hFontArrow = _CalcFont(hwnd, szfnMarlett, SYMBOL_CHARSET, CH_MENUARROW, &_cxArrow, &_cyArrow,
  3557. &_cxMargin, 0, FW_NORMAL);
  3558. }
  3559. else
  3560. {
  3561. _cxArrow = _cyArrow = _cxMargin = 0;
  3562. }
  3563. }
  3564. void CMenuBandMetrics::_SetMenuFont()
  3565. {
  3566. NONCLIENTMETRICS ncm;
  3567. ncm.cbSize = sizeof(ncm);
  3568. // Should only fail with bad parameters...
  3569. SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
  3570. _hFontMenu = CreateFontIndirect(&ncm.lfMenuFont);
  3571. }
  3572. void CMenuBandMetrics::_SetColors()
  3573. {
  3574. _clrBackground = GetSysColor(COLOR_MENU);
  3575. _clrMenuText = GetSysColor(COLOR_MENUTEXT);
  3576. _clrDemoted = GetLumColor(COLOR_MENU, 20);
  3577. _clrMenuGrad = GetLumColor(COLOR_MENU, -20);
  3578. }
  3579. #ifndef DRAWEDGE
  3580. // Office "IntelliMenu" style
  3581. void CMenuBandMetrics::_SetPaintMetrics(HWND hwnd)
  3582. {
  3583. DWORD dwSysHighlight = GetSysColor(COLOR_3DHIGHLIGHT);
  3584. DWORD dwSysShadow = GetSysColor(COLOR_3DSHADOW);
  3585. _hPenHighlight = CreatePen(PS_SOLID, 1, dwSysHighlight);
  3586. _hPenShadow = CreatePen(PS_SOLID, 1, dwSysShadow);
  3587. }
  3588. #endif
  3589. void CMenuBandMetrics::_SetTextBrush(HWND hwnd)
  3590. {
  3591. _hbrText = CreateSolidBrush(GetSysColor(COLOR_MENUTEXT));
  3592. }
  3593. CMenuBandState::CMenuBandState()
  3594. {
  3595. // We will default to NOT show the keyboard cues. This
  3596. // is overridden based on the User Settings.
  3597. _fKeyboardCue = FALSE;
  3598. }
  3599. CMenuBandState::~CMenuBandState()
  3600. {
  3601. ATOMICRELEASE(_ptFader);
  3602. ATOMICRELEASE(_pScheduler);
  3603. if (IsWindow(_hwndToolTip))
  3604. DestroyWindow(_hwndToolTip);
  3605. if (IsWindow(_hwndWorker)) // JANK : Fix for bug #101302
  3606. DestroyWindow(_hwndWorker);
  3607. }
  3608. int CMenuBandState::GetKeyboardCue()
  3609. {
  3610. return _fKeyboardCue;
  3611. }
  3612. void CMenuBandState::SetKeyboardCue(BOOL fKC)
  3613. {
  3614. _fKeyboardCue = fKC;
  3615. }
  3616. IShellTaskScheduler* CMenuBandState::GetScheduler()
  3617. {
  3618. HRESULT hr = S_OK;
  3619. if (!_pScheduler)
  3620. {
  3621. hr = CoCreateInstance(CLSID_ShellTaskScheduler, NULL, CLSCTX_INPROC,
  3622. IID_PPV_ARG(IShellTaskScheduler, &_pScheduler));
  3623. }
  3624. ASSERT((SUCCEEDED(hr) && _pScheduler) || (FAILED(hr) && !_pScheduler));
  3625. if (SUCCEEDED(hr))
  3626. _pScheduler->AddRef();
  3627. return _pScheduler;
  3628. }
  3629. HRESULT CMenuBandState::FadeRect(LPCRECT prc)
  3630. {
  3631. HRESULT hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  3632. BOOL fFade = FALSE;
  3633. SystemParametersInfo(SPI_GETSELECTIONFADE, 0, &fFade, 0);
  3634. if (_ptFader && fFade)
  3635. {
  3636. hr = _ptFader->FadeRect(prc);
  3637. }
  3638. return hr;
  3639. }
  3640. void CMenuBandState::CreateFader()
  3641. {
  3642. // We do this on first show, because in the Constuctor of CMenuBandState,
  3643. // the Window classes might not be registered yet (As is the case with start menu).
  3644. if (!_ptFader)
  3645. {
  3646. CoCreateInstance(CLSID_FadeTask, NULL, CLSCTX_INPROC, IID_PPV_ARG(IFadeTask, &_ptFader));
  3647. }
  3648. }
  3649. void CMenuBandState::CenterOnButton(HWND hwndTB, BOOL fBalloon, int idCmd, LPTSTR pszTitle, LPTSTR pszTip)
  3650. {
  3651. // Balloon style holds presidence over info tips
  3652. if (_fTipShown && _fBalloonStyle)
  3653. return;
  3654. if (!_hwndToolTip)
  3655. {
  3656. _hwndToolTip = CreateWindow(TOOLTIPS_CLASS, NULL,
  3657. WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_BALLOON,
  3658. CW_USEDEFAULT, CW_USEDEFAULT,
  3659. CW_USEDEFAULT, CW_USEDEFAULT,
  3660. NULL, NULL, g_hinst,
  3661. NULL);
  3662. if (_hwndToolTip)
  3663. {
  3664. // set the version so we can have non buggy mouse event forwarding
  3665. SendMessage(_hwndToolTip, CCM_SETVERSION, COMCTL32_VERSION, 0);
  3666. SendMessage(_hwndToolTip, TTM_SETMAXTIPWIDTH, 0, (LPARAM)300);
  3667. }
  3668. }
  3669. if (_hwndToolTip)
  3670. {
  3671. // Collapse the previous tip because we're going to be doing some stuff to it before displaying again.
  3672. SendMessage(_hwndToolTip, TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)0);
  3673. // Balloon tips don't have a border, but regular tips do. Swap now...
  3674. SHSetWindowBits(_hwndToolTip, GWL_STYLE, TTS_BALLOON | WS_BORDER, (fBalloon) ? TTS_BALLOON : WS_BORDER);
  3675. if (pszTip && pszTip[0])
  3676. {
  3677. POINT ptCursor;
  3678. RECT rcItemScreen, rcItemTB;
  3679. TOOLINFO ti = {0};
  3680. ti.cbSize = sizeof(ti);
  3681. // This was pretty bad: I kept adding tools, but never deleteing them. Now we get rid of the current
  3682. // one then add the new one.
  3683. if (SendMessage(_hwndToolTip, TTM_ENUMTOOLS, 0, (LPARAM)&ti))
  3684. {
  3685. SendMessage(_hwndToolTip, TTM_DELTOOL, 0, (LPARAM)&ti); // Delete the current tool.
  3686. }
  3687. SendMessage(hwndTB, TB_GETRECT, idCmd, (LPARAM)&rcItemScreen);
  3688. rcItemTB = rcItemScreen;
  3689. MapWindowPoints(hwndTB, HWND_DESKTOP, (POINT*)&rcItemScreen, 2);
  3690. ti.cbSize = sizeof(ti);
  3691. ti.uFlags = TTF_TRANSPARENT | (fBalloon? TTF_TRACK : 0);
  3692. // Check if the cursor is within the bounds of the hot item.
  3693. // If it is, then proceed as usual.
  3694. // If it isn't, then the hot item was activated via the keyboard, so the tooltip
  3695. // shouldn't be hung from the cursor. Stick it on the hot item instead.
  3696. // Set the vertical offset to use later.
  3697. // Notice the correction for the bottom: gsierra wanted it up a couple of pixels.
  3698. int nOffset = -3;
  3699. GetCursorPos(&ptCursor);
  3700. if (!PtInRect(&rcItemScreen, ptCursor))
  3701. {
  3702. ti.uFlags |= TTF_TRACK;
  3703. // Force the tool tip to track along the bottom.
  3704. nOffset = 1;
  3705. }
  3706. // The tooltip won't pick up the hot item's rect right, so
  3707. // do it manually.
  3708. ti.rect = rcItemTB;
  3709. SendMessage(_hwndToolTip, TTM_TRACKPOSITION, 0, MAKELONG((rcItemScreen.left + rcItemScreen.right)/2, rcItemScreen.bottom + nOffset));
  3710. ti.hwnd = hwndTB;
  3711. ti.uId = (UINT_PTR)hwndTB;
  3712. SendMessage(_hwndToolTip, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  3713. ti.lpszText = pszTip;
  3714. SendMessage(_hwndToolTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
  3715. SendMessage(_hwndToolTip, TTM_SETTITLE, TTI_INFO, (LPARAM)pszTitle);
  3716. SetWindowPos(_hwndToolTip, HWND_TOPMOST,
  3717. 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  3718. SendMessage(_hwndToolTip, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&ti);
  3719. _fTipShown = TRUE;
  3720. _fBalloonStyle = fBalloon;
  3721. }
  3722. }
  3723. }
  3724. void CMenuBandState::HideTooltip(BOOL fAllowBalloonCollapse)
  3725. {
  3726. if (_hwndToolTip && _fTipShown)
  3727. {
  3728. // Now we're going to latch the Balloon style. The rest of menuband blindly
  3729. // collapses the tooltip when selection changes. Here's where we say "Don't collapse
  3730. // the chevron balloon tip because of a selection change."
  3731. if ((_fBalloonStyle && fAllowBalloonCollapse) || !_fBalloonStyle)
  3732. {
  3733. SendMessage(_hwndToolTip, TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)0);
  3734. _fTipShown = FALSE;
  3735. }
  3736. }
  3737. }
  3738. void CMenuBandState::PutTipOnTop()
  3739. {
  3740. // Force the tooltip to the topmost.
  3741. if (_hwndToolTip)
  3742. {
  3743. SetWindowPos(_hwndToolTip, HWND_TOPMOST,
  3744. 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
  3745. }
  3746. }
  3747. HWND CMenuBandState::GetWorkerWindow(HWND hwndParent)
  3748. {
  3749. if (!_hwndSubclassed)
  3750. return NULL;
  3751. if (!_hwndWorker)
  3752. {
  3753. // We need a worker window, so that dialogs show up on top of our menus.
  3754. // HiddenWndProc is included from sftbar.h
  3755. _hwndWorker = SHCreateWorkerWindow(HiddenWndProc, _hwndSubclassed,
  3756. WS_EX_TOOLWINDOW, WS_POPUP, 0, (void*)_hwndSubclassed);
  3757. }
  3758. //hwndParent is unused at this time. I plan on using it to prevent the parenting to the subclassed window.
  3759. return _hwndWorker;
  3760. }