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

830 lines
25 KiB

  1. /**************************** Module Header ********************************\
  2. * Module Name: mnstate.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Menu State Routines
  7. *
  8. * History:
  9. * 10-10-90 JimA Cleanup.
  10. * 03-18-91 IanJa Windowrevalidation added
  11. \***************************************************************************/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. PMENU xxxGetInitMenuParam(PWND pwndMenu, BOOL *lpfSystem);
  15. /***************************************************************************\
  16. * MNPositionSysMenu
  17. *
  18. * History:
  19. * 4-25-91 Mikehar Port for 3.1 merge
  20. \***************************************************************************/
  21. VOID MNPositionSysMenu(
  22. PWND pwnd,
  23. PMENU pmenusys)
  24. {
  25. RECT rc;
  26. PITEM pItem;
  27. if (pmenusys == NULL) {
  28. RIPERR0(ERROR_INVALID_HANDLE,
  29. RIP_WARNING,
  30. "Invalid menu handle pmenusys (NULL) to MNPositionSysMenu");
  31. return;
  32. }
  33. /*
  34. * Whoever positions the menu becomes the owner
  35. */
  36. if (pwnd != pmenusys->spwndNotify) {
  37. Lock(&pmenusys->spwndNotify, pwnd);
  38. }
  39. /*
  40. * Setup the SysMenu hit rectangle.
  41. */
  42. rc.top = rc.left = 0;
  43. if (TestWF(pwnd, WEFTOOLWINDOW)) {
  44. rc.right = SYSMET(CXSMSIZE);
  45. rc.bottom = SYSMET(CYSMSIZE);
  46. } else {
  47. rc.right = SYSMET(CXSIZE);
  48. rc.bottom = SYSMET(CYSIZE);
  49. }
  50. if (!TestWF(pwnd, WFMINIMIZED)) {
  51. int cBorders;
  52. cBorders = GetWindowBorders(pwnd->style, pwnd->ExStyle, TRUE, FALSE);
  53. OffsetRect(&rc, cBorders*SYSMET(CXBORDER), cBorders*SYSMET(CYBORDER));
  54. }
  55. /*
  56. * Offset the System popup menu.
  57. */
  58. if (!TestMF(pmenusys, MF_POPUP) && (pmenusys->cItems > 0)) {
  59. pItem = pmenusys->rgItems;
  60. if (pItem) {
  61. pItem->yItem = rc.top;
  62. pItem->xItem = rc.left;
  63. pItem->cyItem = rc.bottom - rc.top;
  64. pItem->cxItem = rc.right - rc.left;
  65. }
  66. } else {
  67. // BOGUS -- MF_POPUP should never be set on a MENU -- only a MENU ITEM
  68. RIPMSG1(RIP_ERROR, "pmenu %#p has MF_POPUP set or 0 items", pmenusys);
  69. }
  70. }
  71. /***************************************************************************\
  72. * MNFlushDestroyedPopups
  73. *
  74. * Walk the ppmDelayedFree list freeing those marked as destroyed.
  75. *
  76. * 05-14-96 GerardoB Created
  77. \***************************************************************************/
  78. VOID MNFlushDestroyedPopups(
  79. PPOPUPMENU ppopupmenu,
  80. BOOL fUnlock)
  81. {
  82. PPOPUPMENU ppmDestroyed, ppmFree;
  83. UserAssert(IsRootPopupMenu(ppopupmenu));
  84. /*
  85. * Walk ppmDelayedFree
  86. */
  87. ppmDestroyed = ppopupmenu;
  88. while (ppmDestroyed->ppmDelayedFree != NULL) {
  89. /*
  90. * If it's marked as destroyed, unlink it and free it
  91. */
  92. if (ppmDestroyed->ppmDelayedFree->fDestroyed) {
  93. ppmFree = ppmDestroyed->ppmDelayedFree;
  94. ppmDestroyed->ppmDelayedFree = ppmFree->ppmDelayedFree;
  95. UserAssert(ppmFree != ppmFree->ppopupmenuRoot);
  96. MNFreePopup(ppmFree);
  97. } else {
  98. /*
  99. * fUnlock is TRUE if the root popup is being destroyed; if
  100. * so, reset fDelayedFree and unlink it.
  101. */
  102. if (fUnlock) {
  103. /*
  104. * This means that the root popup is going away before
  105. * some of the hierarchical popups have been destroyed.
  106. *
  107. * This can happen if someone destroys one of the menu
  108. * windows breaking the spwndNextPopup chain.
  109. */
  110. ppmDestroyed->ppmDelayedFree->fDelayedFree = FALSE;
  111. /*
  112. * Stop here so we can figure how this happened.
  113. */
  114. UserAssert(ppmDestroyed->ppmDelayedFree->fDelayedFree);
  115. ppmDestroyed->ppmDelayedFree = ppmDestroyed->ppmDelayedFree->ppmDelayedFree;
  116. } else {
  117. /*
  118. * Not fDestroyed so move to the next one.
  119. */
  120. ppmDestroyed = ppmDestroyed->ppmDelayedFree;
  121. }
  122. }
  123. }
  124. }
  125. /***************************************************************************\
  126. * MNAllocPopup
  127. *
  128. \***************************************************************************/
  129. PPOPUPMENU MNAllocPopup(
  130. BOOL fForceAlloc)
  131. {
  132. PPOPUPMENU ppm;
  133. if (!fForceAlloc && !TEST_PUDF(PUDF_POPUPINUSE)) {
  134. SET_PUDF(PUDF_POPUPINUSE);
  135. ppm = &gpopupMenu;
  136. } else {
  137. ppm = (PPOPUPMENU)UserAllocPoolWithQuota(sizeof(POPUPMENU), TAG_POPUPMENU);
  138. }
  139. if (ppm) {
  140. RtlZeroMemory(ppm, sizeof(POPUPMENU));
  141. }
  142. return ppm;
  143. }
  144. /***************************************************************************\
  145. * MNFreePopup
  146. *
  147. \***************************************************************************/
  148. VOID MNFreePopup(
  149. PPOPUPMENU ppopupmenu)
  150. {
  151. Validateppopupmenu(ppopupmenu);
  152. if (IsRootPopupMenu(ppopupmenu)) {
  153. MNFlushDestroyedPopups(ppopupmenu, TRUE);
  154. }
  155. if (ppopupmenu->spwndPopupMenu &&
  156. GETFNID(ppopupmenu->spwndPopupMenu) == FNID_MENU &&
  157. ppopupmenu != &gpopupMenu) {
  158. ((PMENUWND)ppopupmenu->spwndPopupMenu)->ppopupmenu = NULL;
  159. }
  160. Unlock(&ppopupmenu->spwndPopupMenu);
  161. /*
  162. * If spwndNextPopup is not NULL, we're breaking the chain and spwndNext
  163. * won't get closed. I won't remove the unlock since it has always been
  164. * there.
  165. */
  166. UserAssert(ppopupmenu->spwndNextPopup == NULL);
  167. Unlock(&ppopupmenu->spwndNextPopup);
  168. Unlock(&ppopupmenu->spwndPrevPopup);
  169. UnlockPopupMenu(ppopupmenu, &ppopupmenu->spmenu);
  170. UnlockPopupMenu(ppopupmenu, &ppopupmenu->spmenuAlternate);
  171. Unlock(&ppopupmenu->spwndNotify);
  172. Unlock(&ppopupmenu->spwndActivePopup);
  173. #if DBG
  174. ppopupmenu->fFreed = TRUE;
  175. #endif
  176. if (ppopupmenu == &gpopupMenu) {
  177. UserAssert(TEST_PUDF(PUDF_POPUPINUSE));
  178. CLEAR_PUDF(PUDF_POPUPINUSE);
  179. } else {
  180. UserFreePool(ppopupmenu);
  181. }
  182. }
  183. /***************************************************************************\
  184. * MNEndMenuStateNotify
  185. *
  186. * spwndNotify might have been created by a thread other than the one
  187. * the menu mode is running on. If this is the case, this function
  188. * NULLs out pMenuState for the thread that owns spwndNotify.
  189. *
  190. * It returns TRUE if the menu state owner doesn't own the notification
  191. * window (multiple threads involved).
  192. *
  193. * 05-21-96 GerardoB Created
  194. \***************************************************************************/
  195. BOOL MNEndMenuStateNotify(
  196. PMENUSTATE pMenuState)
  197. {
  198. PTHREADINFO ptiNotify;
  199. if (pMenuState->pGlobalPopupMenu->spwndNotify != NULL) {
  200. ptiNotify = GETPTI(pMenuState->pGlobalPopupMenu->spwndNotify);
  201. if (ptiNotify != pMenuState->ptiMenuStateOwner) {
  202. /*
  203. * Later5.0 GerardoB. xxxMNStartMenuState no longer allows this.
  204. * This is dead code that I'll remove eventually
  205. */
  206. UserAssert(ptiNotify == pMenuState->ptiMenuStateOwner);
  207. UserAssert(ptiNotify->pMenuState == pMenuState);
  208. UserAssert(pMenuState->pmnsPrev == NULL);
  209. ptiNotify->pMenuState = NULL;
  210. return TRUE;
  211. }
  212. }
  213. return FALSE;
  214. }
  215. /***************************************************************************\
  216. * xxxMNEndMenuState
  217. *
  218. * This funtion must be called to clean up pMenuState after getting out
  219. * of menu mode. It must be called by the same thread that initialized
  220. * pMenuState either manually or by calling xxxMNStartMenuState.
  221. *
  222. * 05-20-96 GerardoB Created
  223. \***************************************************************************/
  224. VOID xxxMNEndMenuState(
  225. BOOL fFreePopup)
  226. {
  227. PTHREADINFO ptiCurrent = PtiCurrent();
  228. PMENUSTATE pMenuState;
  229. pMenuState = ptiCurrent->pMenuState;
  230. UserAssert(ptiCurrent->pMenuState != NULL);
  231. UserAssert(ptiCurrent == pMenuState->ptiMenuStateOwner);
  232. /*
  233. * If the menu is locked, someone doesn't want it to go just yet.
  234. */
  235. if (pMenuState->dwLockCount != 0) {
  236. RIPMSG1(RIP_WARNING, "xxxMNEndMenuState Locked:%#p", pMenuState);
  237. return;
  238. }
  239. MNEndMenuStateNotify(pMenuState);
  240. /*
  241. * pMenuState->pGlobalPopupMenu could be NULL if xxxMNAllocMenuState failed
  242. */
  243. if (pMenuState->pGlobalPopupMenu != NULL) {
  244. if (fFreePopup) {
  245. UserAssert(pMenuState->pGlobalPopupMenu->fIsMenuBar || pMenuState->pGlobalPopupMenu->fDestroyed);
  246. MNFreePopup(pMenuState->pGlobalPopupMenu);
  247. } else {
  248. /*
  249. * This means that we're ending the menustate but the popup menu
  250. * window is still around. This can happen when called from
  251. * xxxDestroyThreadInfo.
  252. */
  253. UserAssert(pMenuState->pGlobalPopupMenu->fIsTrackPopup);
  254. pMenuState->pGlobalPopupMenu->fDelayedFree = FALSE;
  255. }
  256. }
  257. /*
  258. * Unlock MFMWFP windows.
  259. */
  260. UnlockMFMWFPWindow(&pMenuState->uButtonDownHitArea);
  261. UnlockMFMWFPWindow(&pMenuState->uDraggingHitArea);
  262. /*
  263. * Restore the previous state, if any
  264. */
  265. ptiCurrent->pMenuState = pMenuState->pmnsPrev;
  266. /*
  267. * This (modal) menu mode is off
  268. */
  269. if (!pMenuState->fModelessMenu) {
  270. DecSFWLockCount();
  271. DBGDecModalMenuCount();
  272. }
  273. if (pMenuState->hbmAni != NULL) {
  274. MNDestroyAnimationBitmap(pMenuState);
  275. }
  276. /*
  277. * Free the menu state
  278. */
  279. if (pMenuState == &gMenuState) {
  280. UserAssert(TEST_PUDF(PUDF_MENUSTATEINUSE));
  281. CLEAR_PUDF(PUDF_MENUSTATEINUSE);
  282. GreSetDCOwner(gMenuState.hdcAni, OBJECT_OWNER_PUBLIC);
  283. } else {
  284. if (pMenuState->hdcAni != NULL) {
  285. GreDeleteDC(pMenuState->hdcAni);
  286. }
  287. UserFreePool(pMenuState);
  288. }
  289. /*
  290. * If returning to a modeless menu, make sure we have activation.
  291. * If returning to a modal menu, make sure we have capture.
  292. */
  293. if (ptiCurrent->pMenuState != NULL) {
  294. if (ptiCurrent->pMenuState->fModelessMenu) {
  295. xxxActivateThisWindow(ptiCurrent->pMenuState->pGlobalPopupMenu->spwndActivePopup,
  296. 0, 0);
  297. } else {
  298. xxxMNSetCapture(ptiCurrent->pMenuState->pGlobalPopupMenu);
  299. }
  300. }
  301. #if DBG
  302. /*
  303. * No pti should point to this pMenuState anymore.
  304. * If guModalMenuStateCount is zero, all pMenuState must be NULL or
  305. * modeless.
  306. */
  307. {
  308. PLIST_ENTRY pHead, pEntry;
  309. PTHREADINFO ptiT;
  310. pHead = &(ptiCurrent->rpdesk->PtiList);
  311. for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
  312. ptiT = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
  313. UserAssert(ptiT->pMenuState != pMenuState);
  314. if (guModalMenuStateCount == 0) {
  315. UserAssert(ptiT->pMenuState == NULL || ptiT->pMenuState->fModelessMenu);
  316. }
  317. }
  318. }
  319. #endif
  320. }
  321. /***************************************************************************\
  322. * MNCreateAnimationBitmap
  323. *
  324. \***************************************************************************/
  325. BOOL MNCreateAnimationBitmap(
  326. PMENUSTATE pMenuState,
  327. UINT cx,
  328. UINT cy)
  329. {
  330. HBITMAP hbm = GreCreateCompatibleBitmap(gpDispInfo->hdcScreen, cx, cy);
  331. if (hbm == NULL) {
  332. RIPMSG0(RIP_WARNING, "MNSetupAnimationBitmap: Failed to create hbmAni");
  333. return FALSE;
  334. }
  335. #if DBG
  336. if (pMenuState->hdcAni == NULL) {
  337. RIPMSG0(RIP_WARNING, "MNCreateAnimationBitmap: hdcAni is NULL");
  338. }
  339. if (pMenuState->hbmAni != NULL) {
  340. RIPMSG0(RIP_WARNING, "MNCreateAnimationBitmap: hbmAni already exists");
  341. }
  342. #endif // DBG
  343. GreSelectBitmap(pMenuState->hdcAni, hbm);
  344. pMenuState->hbmAni = hbm;
  345. return TRUE;
  346. }
  347. /***************************************************************************\
  348. * MNDestroyAnimationBitmap
  349. *
  350. \***************************************************************************/
  351. VOID MNDestroyAnimationBitmap(
  352. PMENUSTATE pMenuState)
  353. {
  354. GreSelectBitmap(pMenuState->hdcAni, GreGetStockObject(PRIV_STOCK_BITMAP));
  355. UserVerify(GreDeleteObject(pMenuState->hbmAni));
  356. pMenuState->hbmAni = NULL;
  357. }
  358. /***************************************************************************\
  359. * MNSetupAnimationDC
  360. *
  361. * 9/20/96 GerardoB Created
  362. \***************************************************************************/
  363. BOOL MNSetupAnimationDC(
  364. PMENUSTATE pMenuState)
  365. {
  366. pMenuState->hdcAni = GreCreateCompatibleDC(gpDispInfo->hdcScreen);
  367. if (pMenuState->hdcAni == NULL) {
  368. RIPMSG0(RIP_WARNING, "MNSetupAnimationDC: Failed to create hdcAnimate");
  369. UserAssert(pMenuState != &gMenuState);
  370. return FALSE;
  371. }
  372. GreSelectFont(pMenuState->hdcAni, ghMenuFont);
  373. return TRUE;
  374. }
  375. /***************************************************************************\
  376. * xxxUnlockMenuState
  377. *
  378. * 11/24/96 GerardoB Created
  379. \***************************************************************************/
  380. BOOL xxxUnlockMenuState(
  381. PMENUSTATE pMenuState)
  382. {
  383. UserAssert(pMenuState->dwLockCount != 0);
  384. (pMenuState->dwLockCount)--;
  385. if ((pMenuState->dwLockCount == 0) && ExitMenuLoop(pMenuState, pMenuState->pGlobalPopupMenu)) {
  386. xxxMNEndMenuState(TRUE);
  387. return TRUE;
  388. }
  389. return FALSE;
  390. }
  391. /***************************************************************************\
  392. * xxxMNAllocMenuState
  393. *
  394. * Allocates and initializes a pMenuState.
  395. *
  396. * 5-21-96 GerardoB Created
  397. \***************************************************************************/
  398. PMENUSTATE xxxMNAllocMenuState(
  399. PTHREADINFO ptiCurrent,
  400. PTHREADINFO ptiNotify,
  401. PPOPUPMENU ppopupmenuRoot)
  402. {
  403. BOOL fAllocate;
  404. PMENUSTATE pMenuState;
  405. UserAssert(PtiCurrent() == ptiCurrent);
  406. UserAssert(ptiCurrent->rpdesk == ptiNotify->rpdesk);
  407. /*
  408. * If gMenuState is already taken, allocate one.
  409. */
  410. fAllocate = TEST_PUDF(PUDF_MENUSTATEINUSE);
  411. if (fAllocate) {
  412. pMenuState = (PMENUSTATE)UserAllocPoolWithQuota(sizeof(MENUSTATE), TAG_MENUSTATE);
  413. if (pMenuState == NULL) {
  414. return NULL;
  415. }
  416. } else {
  417. /*
  418. * Use chache global which already has the animation DC setup
  419. */
  420. SET_PUDF(PUDF_MENUSTATEINUSE);
  421. pMenuState = &gMenuState;
  422. UserAssert(gMenuState.hdcAni != NULL);
  423. GreSetDCOwner(gMenuState.hdcAni, OBJECT_OWNER_CURRENT);
  424. }
  425. /*
  426. * Prevent anyone from changing the foreground while this menu is active
  427. */
  428. IncSFWLockCount();
  429. DBGIncModalMenuCount();
  430. /*
  431. * Initialize pMenuState.
  432. * Animation DC stuff is already setup so don't zero init it.
  433. */
  434. RtlZeroMemory(pMenuState, sizeof(MENUSTATE) - sizeof(MENUANIDC));
  435. pMenuState->pGlobalPopupMenu = ppopupmenuRoot;
  436. pMenuState->ptiMenuStateOwner = ptiCurrent;
  437. /*
  438. * Save previous state, if any. Then set new state.
  439. */
  440. pMenuState->pmnsPrev = ptiCurrent->pMenuState;
  441. ptiCurrent->pMenuState = pMenuState;
  442. if (ptiNotify != ptiCurrent) {
  443. UserAssert(ptiNotify->pMenuState == NULL);
  444. ptiNotify->pMenuState = pMenuState;
  445. }
  446. /*
  447. * If the menustate was allocated, set up animation stuff.
  448. * This is done here because in case of failure, MNEndMenuState
  449. * will find ptiCurrent->pMenuState properly.
  450. */
  451. if (fAllocate) {
  452. RtlZeroMemory((PBYTE)pMenuState + sizeof(MENUSTATE) -
  453. sizeof(MENUANIDC), sizeof(MENUANIDC));
  454. if (!MNSetupAnimationDC(pMenuState)) {
  455. xxxMNEndMenuState(TRUE);
  456. return NULL;
  457. }
  458. }
  459. return pMenuState;
  460. }
  461. /***************************************************************************\
  462. * xxxMNStartMenuState
  463. *
  464. * This function is called when the menu bar is about to be activated (the
  465. * app's main menu). It makes sure that the threads involved are not in
  466. * menu mode already, finds the owner/notification window, initializes
  467. * pMenuState and sends the WM_ENTERMENULOOP message.
  468. * It successful, it returns a pointer to a pMenuState. If so, the caller
  469. * must call MNEndMenuState when done.
  470. *
  471. * History:
  472. * 4-25-91 Mikehar Port for 3.1 merge
  473. * 5-20-96 GerardoB Renamed and changed (Old name: xxxMNGetPopup)
  474. \***************************************************************************/
  475. PMENUSTATE xxxMNStartMenuState(
  476. PWND pwnd,
  477. DWORD cmd,
  478. LPARAM lParam)
  479. {
  480. PPOPUPMENU ppopupmenu;
  481. PTHREADINFO ptiCurrent, ptiNotify;
  482. PMENUSTATE pMenuState;
  483. TL tlpwnd;
  484. PWND pwndT;
  485. CheckLock(pwnd);
  486. /*
  487. * Bail if the current thread is already in menu mode
  488. */
  489. ptiCurrent = PtiCurrent();
  490. if (ptiCurrent->pMenuState != NULL) {
  491. return NULL;
  492. }
  493. /*
  494. * If pwnd is not owned by ptiCurrent, the _PostMessage call below might
  495. * send us in a loop
  496. */
  497. UserAssert(ptiCurrent == GETPTI(pwnd));
  498. /*
  499. * If this is not a child window, use the active window on its queue
  500. */
  501. if (!TestwndChild(pwnd)) {
  502. pwnd = GETPTI(pwnd)->pq->spwndActive;
  503. } else {
  504. /*
  505. * Search up the parents for a window with a System Menu.
  506. */
  507. while (TestwndChild(pwnd)) {
  508. if (TestWF(pwnd, WFSYSMENU)) {
  509. break;
  510. }
  511. pwnd = pwnd->spwndParent;
  512. }
  513. }
  514. if (pwnd == NULL) {
  515. return NULL;
  516. }
  517. if (!TestwndChild(pwnd) && (pwnd->spmenu != NULL)) {
  518. goto hasmenu;
  519. }
  520. if (!TestWF(pwnd, WFSYSMENU)) {
  521. return NULL;
  522. }
  523. hasmenu:
  524. /*
  525. * If the owner/notification window was created by another thread,
  526. * make sure that it's not in menu mode already
  527. * This can happen if PtiCurrent() is attached to other threads, one of
  528. * which created pwnd.
  529. */
  530. ptiNotify = GETPTI(pwnd);
  531. if (ptiNotify->pMenuState != NULL) {
  532. return NULL;
  533. }
  534. /*
  535. * If the notification window is owned by another thread,
  536. * then the menu loop wouldn't get any keyboard or mouse
  537. * messages because we set capture to the notification window.
  538. * So we pass the WM_SYSCOMMAND to that thread and bail
  539. */
  540. if (ptiNotify != ptiCurrent) {
  541. RIPMSG2(RIP_WARNING, "Passing WM_SYSCOMMAND SC_*MENU from thread %#p to %#p", ptiCurrent, ptiNotify);
  542. _PostMessage(pwnd, WM_SYSCOMMAND, cmd, lParam);
  543. return NULL;
  544. }
  545. /*
  546. * Allocate ppoupmenu and pMenuState.
  547. */
  548. ppopupmenu = MNAllocPopup(FALSE);
  549. if (ppopupmenu == NULL) {
  550. return NULL;
  551. }
  552. pMenuState = xxxMNAllocMenuState(ptiCurrent, ptiNotify, ppopupmenu);
  553. if (pMenuState == NULL) {
  554. MNFreePopup(ppopupmenu);
  555. return NULL;
  556. }
  557. ppopupmenu->fIsMenuBar = TRUE;
  558. ppopupmenu->fHasMenuBar = TRUE;
  559. Lock(&(ppopupmenu->spwndNotify), pwnd);
  560. ppopupmenu->posSelectedItem = MFMWFP_NOITEM;
  561. Lock(&(ppopupmenu->spwndPopupMenu), pwnd);
  562. ppopupmenu->ppopupmenuRoot = ppopupmenu;
  563. pwndT = pwnd;
  564. while(TestwndChild(pwndT))
  565. pwndT = pwndT->spwndParent;
  566. if (pwndT->spmenu) {
  567. ppopupmenu->fRtoL = TestMF(pwndT->spmenu, MFRTL) ?TRUE:FALSE;
  568. } else {
  569. //
  570. // No way to know, no menu, but there is a system menu. Thus arrow
  571. // keys are really not important. However lets take the next best
  572. // thing just to be safe.
  573. //
  574. ppopupmenu->fRtoL = TestWF(pwnd, WEFRTLREADING) ?TRUE :FALSE;
  575. }
  576. /*
  577. * Notify the app we are entering menu mode. wParam is always 0 since this
  578. * procedure will only be called for menu bar menus not TrackPopupMenu
  579. * menus.
  580. */
  581. ThreadLockAlways(pwnd, &tlpwnd);
  582. xxxSendMessage(pwnd, WM_ENTERMENULOOP, 0, 0);
  583. ThreadUnlock(&tlpwnd);
  584. return pMenuState;
  585. }
  586. /***************************************************************************\
  587. * xxxMNStartMenu
  588. *
  589. * Note that this function calls back many times so we might be forced
  590. * out of menu mode at any time. We don't want to check this after
  591. * each callback so we lock what we need and go on. Be careful.
  592. *
  593. * History:
  594. * 4-25-91 Mikehar Port for 3.1 merge
  595. \***************************************************************************/
  596. BOOL xxxMNStartMenu(
  597. PPOPUPMENU ppopupmenu,
  598. int mn)
  599. {
  600. PWND pwndMenu;
  601. PMENU pMenu;
  602. PMENUSTATE pMenuState;
  603. TL tlpwndMenu;
  604. TL tlpMenu;
  605. UserAssert(IsRootPopupMenu(ppopupmenu));
  606. if (ppopupmenu->fDestroyed) {
  607. return FALSE;
  608. }
  609. pwndMenu = ppopupmenu->spwndNotify;
  610. ThreadLock(pwndMenu, &tlpwndMenu);
  611. pMenuState = GetpMenuState(pwndMenu);
  612. if (pMenuState == NULL) {
  613. RIPMSG0(RIP_ERROR, "xxxMNStartMenu: pMenuState == NULL");
  614. ThreadUnlock(&tlpwndMenu);
  615. return FALSE;
  616. }
  617. pMenuState->mnFocus = mn;
  618. pMenuState->fMenuStarted = TRUE;
  619. pMenuState->fButtonDown =
  620. pMenuState->fButtonAlwaysDown = ((_GetKeyState(VK_LBUTTON) & 0x8000) != 0);
  621. xxxMNSetCapture(ppopupmenu);
  622. xxxSendMessage(pwndMenu, WM_SETCURSOR, (WPARAM)HWq(pwndMenu),
  623. MAKELONG(MSGF_MENU, 0));
  624. if (ppopupmenu->fIsMenuBar) {
  625. BOOL fSystemMenu;
  626. pMenu = xxxGetInitMenuParam(pwndMenu, &fSystemMenu);
  627. if (pMenu == NULL) {
  628. pMenuState->fMenuStarted = FALSE;
  629. xxxMNReleaseCapture();
  630. ThreadUnlock(&tlpwndMenu);
  631. return FALSE;
  632. }
  633. LockPopupMenu(ppopupmenu, &ppopupmenu->spmenu, pMenu);
  634. ppopupmenu->fIsSysMenu = (fSystemMenu != 0);
  635. if (!fSystemMenu) {
  636. pMenu = xxxGetSysMenu(pwndMenu, FALSE);
  637. LockPopupMenu(ppopupmenu, &ppopupmenu->spmenuAlternate, pMenu);
  638. }
  639. }
  640. pMenuState->fIsSysMenu = (ppopupmenu->fIsSysMenu != 0);
  641. if (!ppopupmenu->fNoNotify) {
  642. if (ppopupmenu->fIsTrackPopup && ppopupmenu->fIsSysMenu) {
  643. pMenu = xxxGetInitMenuParam(pwndMenu, NULL);
  644. } else {
  645. pMenu = ppopupmenu->spmenu;
  646. }
  647. xxxSendMessage(pwndMenu, WM_INITMENU, (WPARAM)PtoH(pMenu), 0L);
  648. }
  649. if (!ppopupmenu->fIsTrackPopup) {
  650. if (ppopupmenu->fIsSysMenu) {
  651. MNPositionSysMenu(pwndMenu, ppopupmenu->spmenu);
  652. } else if (ppopupmenu->fIsMenuBar) {
  653. ThreadLockMenuNoModify(ppopupmenu->spmenu, &tlpMenu);
  654. xxxMNRecomputeBarIfNeeded(pwndMenu, ppopupmenu->spmenu);
  655. ThreadUnlockMenuNoModify(&tlpMenu);
  656. MNPositionSysMenu(pwndMenu, ppopupmenu->spmenuAlternate);
  657. }
  658. }
  659. /*
  660. * If returning TRUE, set menu style in pMenuState
  661. */
  662. if (!ppopupmenu->fDestroyed) {
  663. if (TestMF(ppopupmenu->spmenu, MNS_MODELESS)) {
  664. pMenuState->fModelessMenu = TRUE;
  665. }
  666. if (TestMF(ppopupmenu->spmenu, MNS_DRAGDROP)) {
  667. if (NT_SUCCESS(xxxClientLoadOLE())) {
  668. pMenuState->fDragAndDrop = TRUE;
  669. }
  670. }
  671. if (TestMF(ppopupmenu->spmenu, MNS_AUTODISMISS)) {
  672. pMenuState->fAutoDismiss = TRUE;
  673. }
  674. if (TestMF(ppopupmenu->spmenu, MNS_NOTIFYBYPOS)) {
  675. pMenuState->fNotifyByPos = TRUE;
  676. }
  677. }
  678. /*
  679. * Bogus! We don't always know that this is the system menu. We
  680. * will frequently pass on an OBJID_MENU even when you hit Alt+Space.
  681. *
  682. * Hence, MNSwitchToAlternate will send a EVENT_SYSTEM_MENUEND for the
  683. * menu bar and an EVENT_SYSTEM_MENUSTART for the sysmenu when we "switch".
  684. */
  685. xxxWindowEvent(EVENT_SYSTEM_MENUSTART, pwndMenu,
  686. (ppopupmenu->fIsSysMenu ? OBJID_SYSMENU : (ppopupmenu->fIsMenuBar ? OBJID_MENU : OBJID_WINDOW)),
  687. INDEXID_CONTAINER, 0);
  688. ThreadUnlock(&tlpwndMenu);
  689. return !ppopupmenu->fDestroyed;
  690. }
  691. /***************************************************************************\
  692. * xxxGetInitMenuParam
  693. *
  694. * Gets the HMENU sent as the wParam of WM_INITMENU, and for menu bars, is
  695. * the actual menu to be interacted with.
  696. *
  697. * History:
  698. * ????
  699. \***************************************************************************/
  700. PMENU xxxGetInitMenuParam(
  701. PWND pwndMenu,
  702. BOOL *lpfSystem)
  703. {
  704. //
  705. // Find out what menu we should be sending in WM_INITMENU:
  706. // If minimized/child/empty menubar, use system menu
  707. //
  708. CheckLock(pwndMenu);
  709. if (TestWF(pwndMenu, WFMINIMIZED) ||
  710. TestwndChild(pwndMenu) ||
  711. (pwndMenu->spmenu == NULL) ||
  712. !pwndMenu->spmenu->cItems) {
  713. if (lpfSystem != NULL)
  714. *lpfSystem = TRUE;
  715. return xxxGetSysMenu(pwndMenu, FALSE);
  716. } else {
  717. if (lpfSystem != NULL) {
  718. *lpfSystem = FALSE;
  719. }
  720. return pwndMenu->spmenu;
  721. }
  722. }