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.

710 lines
21 KiB

  1. #include "priv.h"
  2. #include "sccls.h"
  3. #include "iface.h"
  4. #include "resource.h"
  5. #include "caggunk.h"
  6. #include "menuisf.h"
  7. #include "menubar.h"
  8. #include "menuband.h"
  9. #include "iaccess.h"
  10. #include "apithk.h"
  11. //=================================================================
  12. // Implementation of CMenuAgent
  13. //
  14. // The single global object of this class (g_menuagent) is the
  15. // manager of the message filter proc used to track mouse and
  16. // keyboard messages on behalf of CTrackPopupBar while a menu is
  17. // in a modal menu loop in TrackPopupMenu.
  18. //
  19. // We track these messages so we can pop out of the menu, behaving
  20. // as if the visual menu bar consisted of a homogeneous menu
  21. // object.
  22. //
  23. //=================================================================
  24. extern "C" void DumpMsg(LPCTSTR pszLabel, MSG * pmsg);
  25. struct CMenuAgent
  26. {
  27. public:
  28. HHOOK _hhookMsg;
  29. HWND _hwndSite; // hwnd to receive forwarded messages
  30. HWND _hwndParent;
  31. CTrackPopupBar * _ptpbar;
  32. IMenuPopup * _pmpParent;
  33. void* _pvContext;
  34. HANDLE _hEvent;
  35. BITBOOL _fEscHit: 1;
  36. // we need to keep track of whether the last selected
  37. // menu item was on a popup or not. we can do this by storing the
  38. // last WM_MENUSELECT flags
  39. UINT _uFlagsLastSelected;
  40. HMENU _hmenuLastSelected;
  41. POINT _ptLastMove;
  42. void Init(void* pvContext, CTrackPopupBar * ptpbar, IMenuPopup * pmpParent, HWND hwndParent, HWND hwndSite);
  43. void Reset(void* pvContext);
  44. void CancelMenu(void* pvContext);
  45. static LRESULT CALLBACK MsgHook(int nCode, WPARAM wParam, LPARAM lParam);
  46. //private:
  47. void _OnMenuSelect(HMENU hmenu, int i, UINT uFlags);
  48. BOOL _OnKey(WPARAM vkey);
  49. };
  50. // Just one of these, b/c we only need one message filter
  51. CMenuAgent g_menuagent = { 0 };
  52. /*----------------------------------------------------------
  53. Purpose: Initialize the message filter hook
  54. */
  55. void CMenuAgent::Init(void* pvContext, CTrackPopupBar * ptpbar, IMenuPopup * pmpParent,
  56. HWND hwndParent, HWND hwndSite)
  57. {
  58. TraceMsg(TF_MENUBAND, "Initialize CMenuAgent");
  59. ASSERT(IS_VALID_READ_PTR(ptpbar, CTrackPopupBar));
  60. ASSERT(IS_VALID_CODE_PTR(pmpParent, IMenuPopup));
  61. ASSERT(IS_VALID_HANDLE(hwndSite, WND));
  62. if (_pvContext != pvContext)
  63. {
  64. // When switching contexts, we need to collapse the old menu. This keeps us from
  65. // hosing the menubands when switching from one browser to another.
  66. CancelMenu(_pvContext);
  67. ATOMICRELEASE(_ptpbar);
  68. ATOMICRELEASE(_pmpParent);
  69. _pvContext = pvContext;
  70. }
  71. pmpParent->SetSubMenu(ptpbar, TRUE);
  72. _hwndSite = hwndSite;
  73. _hwndParent = hwndParent;
  74. // Since the message hook wants to forward messages to the toolbar,
  75. // we need to ask the pager control to do this
  76. Pager_ForwardMouse(_hwndSite, TRUE);
  77. _pmpParent = pmpParent;
  78. _pmpParent->AddRef();
  79. _ptpbar = ptpbar;
  80. _ptpbar->AddRef();
  81. // HACKHACKHACKHACKHACK (lamadio)
  82. // On Windows 9x kernel can't handle the reentrancy problem where you have two hooks
  83. // in two separate processes. When one process looses focus, we collapse the Menu.
  84. // After that we remove our hook. Problem is: Between the Loosing focus and
  85. // removing the hook, the other IE process popped up a menu and installed a hook
  86. // the two hooks mutilate each other.
  87. if (IsOS(OS_WINDOWS))
  88. {
  89. ASSERT(_hEvent == NULL);
  90. _hEvent = OpenEventA(EVENT_ALL_ACCESS, FALSE, "Shell.MenuAgent");
  91. if (!_hEvent) //event routines return NULL on failure.
  92. // Don't need to use CreateAllAccessSecurityAttributes since this
  93. // is Win9x-only code anyway
  94. _hEvent = CreateEventA(NULL, TRUE, TRUE, "Shell.MenuAgent");
  95. if (_hEvent)
  96. WaitForSingleObject(_hEvent, INFINITE);
  97. }
  98. if (NULL == _hhookMsg)
  99. {
  100. if (_hEvent)
  101. ResetEvent(_hEvent);
  102. _hhookMsg = SetWindowsHookEx(WH_MSGFILTER, MsgHook, HINST_THISDLL, 0);
  103. if (!_hhookMsg && _hEvent)
  104. {
  105. SetEvent(_hEvent);
  106. CloseHandle(_hEvent);
  107. _hEvent = NULL;
  108. }
  109. }
  110. _fEscHit = FALSE;
  111. GetCursorPos(&_ptLastMove);
  112. }
  113. /*----------------------------------------------------------
  114. Purpose: Reset the menu agent; no longer track mouse and keyboard
  115. messages. The menuband calls this when it exits menu mode.
  116. */
  117. void CMenuAgent::Reset(void* pvContext)
  118. {
  119. if (_pvContext == pvContext)
  120. {
  121. _pmpParent->SetSubMenu(_ptpbar, FALSE);
  122. // The only time to not send MPOS_FULLCANCEL is if the escape
  123. // key caused the menu to terminate.
  124. if ( !_fEscHit )
  125. _pmpParent->OnSelect(MPOS_FULLCANCEL);
  126. // Eat any mouse-down/up sequence left in the queue. This is how
  127. // we keep the toolbar from getting a mouse-down if the user
  128. // clicks on the same menuitem as what is currently popped down.
  129. // (E.g., click File, then click File again. W/o this, the menu
  130. // would never toggle up.)
  131. MSG msg;
  132. while (PeekMessage(&msg, _hwndSite, WM_LBUTTONDOWN, WM_LBUTTONUP, PM_REMOVE))
  133. ; // Do nothing
  134. Pager_ForwardMouse(_hwndSite, FALSE);
  135. _hwndSite = NULL;
  136. _hwndParent = NULL;
  137. ATOMICRELEASE(_pmpParent);
  138. ATOMICRELEASE(_ptpbar);
  139. if (_hhookMsg)
  140. {
  141. TraceMsg(TF_MENUBAND, "CMenuAgent: Hook removed");
  142. UnhookWindowsHookEx(_hhookMsg);
  143. _hhookMsg = NULL;
  144. if (_hEvent)
  145. {
  146. SetEvent(_hEvent);
  147. CloseHandle(_hEvent);
  148. _hEvent = NULL;
  149. }
  150. }
  151. _pvContext = NULL;
  152. }
  153. }
  154. /*----------------------------------------------------------
  155. Purpose: Make the menu go away
  156. */
  157. void CMenuAgent::CancelMenu(void* pvContext)
  158. {
  159. if (_pvContext == pvContext)
  160. {
  161. if (_hwndParent)
  162. {
  163. ASSERT(IS_VALID_HANDLE(_hwndParent, WND));
  164. TraceMsg(TF_MENUBAND, "Sending cancel mode to menu");
  165. // Use PostMessage so USER32 doesn't RIP on us in
  166. // MsgHook when it returns from the WM_MOUSEMOVE
  167. // that triggered this code path in the first place.
  168. PostMessage(_hwndParent, WM_CANCELMODE, 0, 0);
  169. // Disguise this as if the escape key was hit,
  170. // since this is called when the mouse hovers over
  171. // another menu sibling.
  172. _fEscHit = TRUE;
  173. _pmpParent->SetSubMenu(_ptpbar, FALSE);
  174. }
  175. }
  176. }
  177. // store away the identity of the selected menu item.
  178. // if uFlags & MF_POPUP then i is the index.
  179. // otherwise it's the command and we need to convert it to the index.
  180. // we store index always because some popups don't have ids
  181. void CMenuAgent::_OnMenuSelect(HMENU hmenu, int i, UINT uFlags)
  182. {
  183. _uFlagsLastSelected = uFlags;
  184. _hmenuLastSelected = hmenu;
  185. }
  186. BOOL CMenuAgent::_OnKey(WPARAM vkey)
  187. {
  188. //
  189. // If the menu window is RTL mirrored, then the arrow keys should
  190. // be mirrored to reflect proper cursor movement. [samera]
  191. //
  192. if (IS_WINDOW_RTL_MIRRORED(_hwndSite))
  193. {
  194. switch (vkey)
  195. {
  196. case VK_LEFT:
  197. vkey = VK_RIGHT;
  198. break;
  199. case VK_RIGHT:
  200. vkey = VK_LEFT;
  201. break;
  202. }
  203. }
  204. switch (vkey)
  205. {
  206. case VK_RIGHT:
  207. if (!_hmenuLastSelected || !(_uFlagsLastSelected & MF_POPUP) || (_uFlagsLastSelected & MF_DISABLED) )
  208. {
  209. // if the currently selected item does not have a cascade, then
  210. // we need to cancel out of all of this and tell the top menu bar to go right
  211. _pmpParent->OnSelect(MPOS_SELECTRIGHT);
  212. }
  213. break;
  214. case VK_LEFT:
  215. if (!_hmenuLastSelected || _hmenuLastSelected == _ptpbar->GetPopupMenu()) {
  216. // if the currently selected menu item is in our top level menu,
  217. // then we need to cancel out of all this menu loop and tell the top menu bar
  218. // to go left
  219. _pmpParent->OnSelect(MPOS_SELECTLEFT);
  220. }
  221. break;
  222. default:
  223. return FALSE;
  224. }
  225. return TRUE;
  226. }
  227. /*----------------------------------------------------------
  228. Purpose: Message hook used to track keyboard and mouse messages
  229. while in a TrackPopupMenu modal loop.
  230. */
  231. LRESULT CMenuAgent::MsgHook(int nCode, WPARAM wParam, LPARAM lParam)
  232. {
  233. LRESULT lRet = 0;
  234. MSG * pmsg = (MSG *)lParam;
  235. switch (nCode)
  236. {
  237. case MSGF_MENU:
  238. #ifdef DEBUG
  239. if (IsFlagSet(g_dwDumpFlags, DF_MSGHOOK))
  240. DumpMsg(TEXT("MsgHook"), pmsg);
  241. #endif
  242. switch (pmsg->message)
  243. {
  244. case WM_MENUSELECT:
  245. // keep track of the items as the are selected.
  246. g_menuagent._OnMenuSelect(GET_WM_MENUSELECT_HMENU(pmsg->wParam, pmsg->lParam),
  247. GET_WM_MENUSELECT_CMD(pmsg->wParam, pmsg->lParam),
  248. GET_WM_MENUSELECT_FLAGS(pmsg->wParam, pmsg->lParam));
  249. break;
  250. case WM_LBUTTONDOWN:
  251. case WM_RBUTTONDOWN:
  252. // Since we've received this msg, any previous escapes
  253. // (like escaping out of a cascaded menu) should be cleared
  254. // to prevent a false reason for termination.
  255. g_menuagent._fEscHit = FALSE;
  256. break;
  257. case WM_KEYDOWN:
  258. if (g_menuagent._OnKey(pmsg->wParam))
  259. break;
  260. case WM_SYSKEYDOWN:
  261. g_menuagent._fEscHit = (VK_ESCAPE == pmsg->wParam);
  262. break;
  263. case WM_MOUSEMOVE:
  264. // HACKHACK (isn't all of this a hack?): ignore zero-move
  265. // mouse moves, so the mouse does not contend with the keyboard.
  266. POINT pt;
  267. // In screen coords....
  268. pt.x = GET_X_LPARAM(pmsg->lParam);
  269. pt.y = GET_Y_LPARAM(pmsg->lParam);
  270. if (g_menuagent._ptLastMove.x == pt.x &&
  271. g_menuagent._ptLastMove.y == pt.y)
  272. {
  273. TraceMsg(TF_MENUBAND, "CMenuAgent: skipping dup mousemove");
  274. break;
  275. }
  276. g_menuagent._ptLastMove = pt;
  277. // Since we got a WM_MOUSEMOVE, we need to tell the Menuband global message hook.
  278. // We need to do this because this message hook steels all of the messages, and
  279. // the Menuband message hook never updates it's internal cache for removing duplicate
  280. // WM_MOUSEMOVE messages which cause problems as outlined in CMsgFilter::_HandleMouseMessages
  281. GetMessageFilter()->AcquireMouseLocation();
  282. // Forward the mouse moves to the toolbar so the toolbar still
  283. // has a chance to hot track. Must convert the points to the
  284. // toolbar's client space.
  285. ScreenToClient(g_menuagent._hwndSite, &pt);
  286. SendMessage(g_menuagent._hwndSite, pmsg->message, pmsg->wParam,
  287. MAKELPARAM(pt.x, pt.y));
  288. break;
  289. }
  290. break;
  291. default:
  292. if (0 > nCode)
  293. return CallNextHookEx(g_menuagent._hhookMsg, nCode, wParam, lParam);
  294. break;
  295. }
  296. // Pass it on to the next hook in the chain
  297. if (0 == lRet)
  298. lRet = CallNextHookEx(g_menuagent._hhookMsg, nCode, wParam, lParam);
  299. return lRet;
  300. }
  301. //=================================================================
  302. // Implementation of a menu deskbar object that uses TrackPopupMenu.
  303. //
  304. // This object uses traditional USER32 menus (via TrackPopupMenu)
  305. // to implement menu behavior. It uses the CMenuAgent object to
  306. // help get its work done. Since the menu deskbar site (_punkSite)
  307. // sits in a modal loop while any menu is up, it needs to know when
  308. // to quit its loop. The child object accomplishes this by sending
  309. // an OnSelect(MPOS_FULLCANCEL).
  310. //
  311. // The only time that TrackPopupMenu returns (but we don't want to
  312. // send an MPOS_FULLCANCEL) is if it's b/c the Escape key was hit.
  313. // This just means cancel the current level. Returning from Popup
  314. // is sufficient for this case. Otherwise, all other cases of
  315. // returning from TrackPopupMenu means we send a MPOS_FULLCANCEL.
  316. //
  317. // Summary:
  318. //
  319. // 1) User clicked outside the menu. This is a full cancel.
  320. // 2) User hit the Alt key. This is a full cancel.
  321. // 3) User hit the Esc key. This just cancels the current level.
  322. // (TrackPopupMenu handles this fine. No notification needs
  323. // to be sent b/c we want the top-level menu to stay in its
  324. // modal loop.)
  325. // 4) User selected a menu item. This is a full cancel.
  326. //
  327. //=================================================================
  328. #undef THISCLASS
  329. #undef SUPERCLASS
  330. #define SUPERCLASS CMenuDeskBar
  331. STDAPI CTrackPopupBar_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  332. {
  333. // aggregation checking is handled in class factory
  334. *ppunk = NULL;
  335. CTrackPopupBar * pTPB = new CTrackPopupBar();
  336. if (pTPB)
  337. {
  338. *ppunk = SAFECAST(pTPB, IOleCommandTarget *);
  339. return NOERROR;
  340. }
  341. return E_OUTOFMEMORY;
  342. }
  343. HRESULT CTrackPopupBar::InitTrackPopupBar(void *pvContext, int id, HMENU hmenu, HWND hwnd)
  344. {
  345. _hmenu = hmenu;
  346. _hwndParent = hwnd;
  347. _id = id;
  348. _pvContext = pvContext;
  349. return S_OK;
  350. }
  351. // Constructor
  352. CTrackPopupBar::CTrackPopupBar()
  353. {
  354. _nMBIgnoreNextDeselect = RegisterWindowMessage(TEXT("CMBIgnoreNextDeselect"));
  355. }
  356. // Destructor
  357. CTrackPopupBar::~CTrackPopupBar()
  358. {
  359. SetSite(NULL);
  360. }
  361. STDMETHODIMP_(ULONG) CTrackPopupBar::AddRef()
  362. {
  363. return SUPERCLASS::AddRef();
  364. }
  365. STDMETHODIMP_(ULONG) CTrackPopupBar::Release()
  366. {
  367. return SUPERCLASS::Release();
  368. }
  369. STDMETHODIMP CTrackPopupBar::QueryInterface(REFIID riid, void **ppvObj)
  370. {
  371. static const QITAB qit[] = {
  372. QITABENT(CTrackPopupBar, IMenuPopup),
  373. QITABENT(CTrackPopupBar, IObjectWithSite),
  374. QITABENT(CTrackPopupBar, IInitTrackPopupBar),
  375. { 0 },
  376. };
  377. HRESULT hres = QISearch(this, qit, riid, ppvObj);
  378. if (FAILED(hres))
  379. {
  380. hres = SUPERCLASS::QueryInterface(riid, ppvObj);
  381. }
  382. return hres;
  383. }
  384. /*----------------------------------------------------------
  385. Purpose: IServiceProvider::QueryService method
  386. */
  387. STDMETHODIMP CTrackPopupBar::QueryService(REFGUID guidService, REFIID riid, void **ppvObj)
  388. {
  389. if (IsEqualGUID(guidService, SID_SMenuBandChild))
  390. {
  391. if (IsEqualIID(riid, IID_IAccessible))
  392. {
  393. HRESULT hres = E_OUTOFMEMORY;
  394. CAccessible* pacc = new CAccessible(_hmenu, _id);
  395. if (pacc)
  396. {
  397. hres = pacc->InitAcc();
  398. if (SUCCEEDED(hres))
  399. {
  400. hres = pacc->QueryInterface(riid, ppvObj);
  401. }
  402. pacc->Release();
  403. }
  404. return hres;
  405. }
  406. else
  407. return QueryInterface(riid, ppvObj);
  408. }
  409. else
  410. return SUPERCLASS::QueryService(guidService, riid, ppvObj);
  411. }
  412. /*----------------------------------------------------------
  413. Purpose: IMenuPopup::OnSelect method
  414. This allows the parent menubar to tell us when to
  415. bail out of the TrackPopupMenu
  416. */
  417. STDMETHODIMP CTrackPopupBar::OnSelect(DWORD dwType)
  418. {
  419. switch (dwType)
  420. {
  421. case MPOS_CANCELLEVEL:
  422. case MPOS_FULLCANCEL:
  423. g_menuagent.CancelMenu(_pvContext);
  424. break;
  425. default:
  426. TraceMsg(TF_WARNING, "CTrackPopupBar doesn't handle this MPOS_ value: %d", dwType);
  427. break;
  428. }
  429. return S_OK;
  430. }
  431. /*----------------------------------------------------------
  432. Purpose: IMenuPopup::SetSubMenu method
  433. */
  434. STDMETHODIMP CTrackPopupBar::SetSubMenu(IMenuPopup * pmp, BOOL bSet)
  435. {
  436. return E_NOTIMPL;
  437. }
  438. // HACKHACK: DO NOT TOUCH! This is the only way to select
  439. // the first item for a user menu. TrackMenuPopup by default does
  440. // not select the first item. We pump these messages to our window.
  441. // User snags these messages, and thinks the user pressed the down button
  442. // and selects the first item for us. The lParam is needed because Win95 gold
  443. // validated this message before using it. Another solution would be to listen
  444. // to WM_INITMENUPOPUP and look for the HWND of the menu. Then send that
  445. // window the private message MN_SELECTFIRSTVALIDITEM. But thats nasty compared
  446. // to this. - lamadio 1.5.99
  447. void CTrackPopupBar::SelectFirstItem()
  448. {
  449. HWND hwndFocus = GetFocus();
  450. // pulled the funny lparam numbers out of spy's butt.
  451. if (hwndFocus) {
  452. PostMessage(hwndFocus, WM_KEYDOWN, VK_DOWN, 0x11500001);
  453. PostMessage(hwndFocus, WM_KEYUP, VK_DOWN, 0xD1500001);
  454. #ifdef UNIX
  455. /* HACK HACK
  456. * The above PostMessages were causing the second menu item
  457. * to be selected if you access the menu from the keyboard.
  458. * The following PostMessages will nullify the above effect.
  459. * This is to make sure that menus in shdocvw work properly
  460. * with user32 menus.
  461. */
  462. PostMessage(hwndFocus, WM_KEYDOWN, VK_UP, 0x11500001);
  463. PostMessage(hwndFocus, WM_KEYUP, VK_UP, 0xD1500001);
  464. #endif /* UNIX */
  465. }
  466. }
  467. DWORD GetBuildNumber()
  468. {
  469. OSVERSIONINFO osvi;
  470. osvi.dwOSVersionInfoSize = sizeof(osvi);
  471. if (GetVersionEx(&osvi))
  472. return osvi.dwBuildNumber;
  473. else
  474. return 0;
  475. }
  476. /*----------------------------------------------------------
  477. Purpose: IMenuPopup::Popup method
  478. Invoke the menu.
  479. */
  480. STDMETHODIMP CTrackPopupBar::Popup(POINTL *ppt, RECTL* prcExclude, DWORD dwFlags)
  481. {
  482. static dwBuildNumber = GetBuildNumber();
  483. ASSERT(IS_VALID_READ_PTR(ppt, POINTL));
  484. ASSERT(NULL == prcExclude || IS_VALID_READ_PTR(prcExclude, RECTL));
  485. ASSERT(IS_VALID_CODE_PTR(_pmpParent, IMenuPopup));
  486. // We must be able to talk to the parent menu bar
  487. if (NULL == _pmpParent)
  488. return E_FAIL;
  489. ASSERT(IS_VALID_HANDLE(_hmenu, MENU));
  490. ASSERT(IS_VALID_CODE_PTR(_punkSite, IUnknown));
  491. HMENU hmenu = GetSubMenu(_hmenu, _id);
  492. HWND hwnd;
  493. TPMPARAMS tpm;
  494. TPMPARAMS * ptpm = NULL;
  495. // User32 does not want to fix this for compatibility reasons,
  496. // but TrackPopupMenu does not snap to the nearest monitor on Single and Multi-Mon
  497. // systems. This has the side effect that if we pass a non-visible coordinate, then
  498. // User places menu at a random location on screen. So instead, we're going to bias
  499. // the point to the monitor.
  500. MONITORINFO mi = {0};
  501. mi.cbSize = sizeof(mi);
  502. HMONITOR hMonitor = MonitorFromPoint(*((POINT*)ppt), MONITOR_DEFAULTTONEAREST);
  503. GetMonitorInfo(hMonitor, &mi);
  504. if (ppt->x >= mi.rcMonitor.right)
  505. ppt->x = mi.rcMonitor.right;
  506. if (ppt->y >= mi.rcMonitor.bottom)
  507. ppt->y = mi.rcMonitor.bottom;
  508. if (ppt->x <= mi.rcMonitor.left)
  509. ppt->x = mi.rcMonitor.left;
  510. if (ppt->y <= mi.rcMonitor.top)
  511. ppt->y = mi.rcMonitor.top;
  512. if (prcExclude)
  513. {
  514. tpm.cbSize = SIZEOF(tpm);
  515. tpm.rcExclude = *((LPRECT)prcExclude);
  516. ptpm = &tpm;
  517. }
  518. // The forwarding code in CShellBrowser::_ShouldForwardMenu
  519. // and CDocObjectHost::_ShouldForwardMenu expects the first
  520. // WM_MENUSELECT to be sent for the top-level menu item.
  521. //
  522. // We need to fake an initial menu select on the top menu band
  523. // to mimic USER and satisfy this expectation.
  524. //
  525. UINT uMSFlags = MF_POPUP;
  526. SendMessage(_hwndParent, WM_MENUSELECT, MAKEWPARAM(_id, uMSFlags), (LPARAM)_hmenu);
  527. SendMessage(_hwndParent, _nMBIgnoreNextDeselect, NULL, NULL);
  528. // Initialize the menu agent
  529. IUnknown_GetWindow(_punkSite, &hwnd);
  530. VARIANTARG v = {0};
  531. UINT uFlags = TPM_VERTICAL | TPM_TOPALIGN;
  532. UINT uAnimateFlags = 0;
  533. if (SUCCEEDED(IUnknown_Exec(_punkSite, &CGID_MENUDESKBAR, MBCID_GETSIDE, 0, NULL, &v))) {
  534. if (v.vt == VT_I4 &&
  535. (v.lVal == MENUBAR_RIGHT ||
  536. v.lVal == MENUBAR_LEFT))
  537. {
  538. uFlags = TPM_TOPALIGN;
  539. }
  540. switch (v.lVal)
  541. {
  542. case MENUBAR_LEFT: uAnimateFlags = TPM_HORNEGANIMATION;
  543. break;
  544. case MENUBAR_RIGHT: uAnimateFlags = TPM_HORPOSANIMATION;
  545. break;
  546. case MENUBAR_TOP: uAnimateFlags = TPM_VERNEGANIMATION;
  547. break;
  548. case MENUBAR_BOTTOM: uAnimateFlags = TPM_VERPOSANIMATION;
  549. break;
  550. }
  551. }
  552. g_menuagent.Init(_pvContext, this, _pmpParent, _hwndParent, hwnd);
  553. ASSERT(IS_VALID_HANDLE(hmenu, MENU));
  554. if (dwFlags & MPPF_INITIALSELECT)
  555. SelectFirstItem();
  556. // This feature only works on build 1794 or greater.
  557. if (g_bRunOnNT5 && dwBuildNumber >= 1794 && dwFlags & MPPF_NOANIMATE)
  558. uFlags |= TPM_NOANIMATION;
  559. #ifndef MAINWIN
  560. if (g_bRunOnMemphis || g_bRunOnNT5)
  561. uFlags |= uAnimateFlags;
  562. TrackPopupMenuEx(hmenu, uFlags,
  563. ppt->x, ppt->y, _hwndParent, ptpm);
  564. #else
  565. // Current MainWin's implementation of TrackPopupMenuEx is buggy.
  566. // I failed to fix it, so I replaced the call by TrackPopupMenu,
  567. // that provides partial functionality.
  568. // Hopefully, jluu will be able to fix it.
  569. TrackPopupMenu(hmenu, uFlags, ppt->x, ppt->y, 0, _hwndParent,
  570. &ptpm->rcExclude);
  571. #endif
  572. // Tell the parent that the menu is now gone
  573. SendMessage(_hwndParent, WM_MENUSELECT, MAKEWPARAM(0, 0xFFFF), NULL);
  574. g_menuagent.Reset(_pvContext);
  575. return S_FALSE;
  576. }