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.

1194 lines
37 KiB

  1. /**************************** Module Header ********************************\
  2. * Module Name: mnchange.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Change Menu Routine
  7. *
  8. * History:
  9. * 10-10-90 JimA Cleanup.
  10. * 03-18-91 IanJa Window revalidation added (none required)
  11. \***************************************************************************/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. /*
  15. * Allocation/deallocation increments. Make them
  16. * different to avoid possible thrashing when an item
  17. * is repeatedly added and removed.
  18. */
  19. #define CMENUITEMALLOC 8
  20. #define CMENUITEMDEALLOC 10
  21. BOOL xxxSetLPITEMInfo(PMENU pMenu, PITEM pItem, LPMENUITEMINFOW lpmii, PUNICODE_STRING pstr);
  22. typedef BOOL (*MENUAPIFN)(PMENU, UINT, BOOL, LPMENUITEMINFOW);
  23. #if DBG
  24. VOID RelocateMenuLockRecords(
  25. PITEM pItem,
  26. int cItem,
  27. LONG_PTR cbMove)
  28. {
  29. while (cItem > 0) {
  30. if (pItem->spSubMenu != NULL) {
  31. HMRelocateLockRecord(&(pItem->spSubMenu), cbMove);
  32. }
  33. pItem++;
  34. cItem--;
  35. }
  36. }
  37. #endif
  38. /***************************************************************************\
  39. * UnlockSubMenu
  40. *
  41. * Unlocks the pSubMenu and removes the MENULIST element corresponding to pMenu
  42. *
  43. * History:
  44. * Nov-20-98 MCostea
  45. \***************************************************************************/
  46. PMENU UnlockSubMenu(
  47. PMENU pMenu,
  48. PMENU* ppSubMenu)
  49. {
  50. PMENULIST* pp;
  51. PMENULIST pMLFound;
  52. if (*ppSubMenu == NULL) {
  53. return NULL;
  54. }
  55. /*
  56. * Remove the item from pMenu's pParentsList
  57. */
  58. for (pp = &(*ppSubMenu)->pParentMenus; *pp != NULL; pp = &(*pp)->pNext) {
  59. if ((*pp)->pMenu == pMenu) {
  60. pMLFound = *pp;
  61. *pp = (*pp)->pNext;
  62. DesktopFree(pMenu->head.rpdesk, pMLFound);
  63. break;
  64. }
  65. }
  66. return Unlock(ppSubMenu);
  67. }
  68. #define NESTED_MENU_LIMIT 25
  69. /***************************************************************************\
  70. * GetMenuDepth
  71. *
  72. * Returns the menu depth (how many nested submenus this menu has).
  73. * This helps catch loops in the menu hierarchy or deep evil apps.
  74. *
  75. * History:
  76. * Sept-22-98 MCostea
  77. \***************************************************************************/
  78. CHAR GetMenuDepth(PMENU pMenu, UINT uMaxAllowedDepth)
  79. {
  80. UINT uItems, uMaxDepth = 0, uSubMenuDepth;
  81. PITEM pItem;
  82. /*
  83. * This will prevent us from getting trapped in loops
  84. */
  85. if (uMaxAllowedDepth == 0) {
  86. return NESTED_MENU_LIMIT;
  87. }
  88. pItem = pMenu->rgItems;
  89. for (uItems = pMenu->cItems; uItems--; pItem++) {
  90. if (pItem->spSubMenu != NULL) {
  91. uSubMenuDepth = GetMenuDepth(pItem->spSubMenu, uMaxAllowedDepth-1);
  92. if (uSubMenuDepth > uMaxDepth) {
  93. /*
  94. * Don't walk the other submenus if a deep branch was found
  95. */
  96. if (uSubMenuDepth >= NESTED_MENU_LIMIT) {
  97. return NESTED_MENU_LIMIT;
  98. }
  99. uMaxDepth = uSubMenuDepth;
  100. }
  101. }
  102. }
  103. return uMaxDepth + 1;
  104. }
  105. /***************************************************************************\
  106. * GetMenuAncestors
  107. *
  108. * Returns the maximum number of levels above pMenu in the menu hierarchy.
  109. * Walking the "parent" tree should not be expensive as it is pretty unusual
  110. * for menus to appear in different places in the hierarchy. The tree is
  111. * usualy a simple linked list.
  112. *
  113. * History:
  114. * Nov-10-98 MCostea
  115. \***************************************************************************/
  116. CHAR GetMenuAncestors(PMENU pMenu)
  117. {
  118. PMENULIST pParentMenu;
  119. CHAR uParentAncestors;
  120. CHAR retVal = 0;
  121. for (pParentMenu = pMenu->pParentMenus; pParentMenu; pParentMenu = pParentMenu->pNext) {
  122. uParentAncestors = GetMenuAncestors(pParentMenu->pMenu);
  123. if (uParentAncestors > retVal) {
  124. retVal = uParentAncestors;
  125. }
  126. }
  127. return retVal+1;
  128. }
  129. /**********************************************
  130. * Global Insert/Append/Set client/server interface
  131. *
  132. * 01-13-94 FritzS Created
  133. ***********************************************/
  134. BOOL xxxSetMenuItemInfo(
  135. PMENU pMenu,
  136. UINT wIndex,
  137. BOOL fByPosition,
  138. LPMENUITEMINFOW lpmii,
  139. PUNICODE_STRING pstrItem)
  140. {
  141. PITEM pItem;
  142. CheckLock(pMenu);
  143. pItem = MNLookUpItem(pMenu, wIndex, fByPosition,NULL);
  144. if (pItem == NULL) {
  145. /*
  146. * Word doesn't like not finding SC_TASKLIST -- so it that's what
  147. * they're looking for, let's pretend we changed it.
  148. */
  149. if (!fByPosition && (wIndex == SC_TASKLIST))
  150. return TRUE;
  151. /*
  152. * Item not found. Return false.
  153. */
  154. RIPERR0(ERROR_MENU_ITEM_NOT_FOUND, RIP_WARNING, "ModifyMenu: Menu item not found");
  155. return FALSE;
  156. }
  157. /*
  158. * we need to treat MFT_RIGHTORDER separately as this is propogated down
  159. * to the entire menu not just to this item so that we stay in ssync. This
  160. * is pretty similar to the use of MFT_RIGHTJUST, we actually do the
  161. * propogation because we need the flag in all sorts of places, not just
  162. * in MBC_RightJustifyMenu()
  163. */
  164. /*
  165. * See ValidateMENUITEMINFO in client\clmenu.c will add more flags to fMask if it use to be MIIM_TYPE
  166. * Then fMask will not be any more == MIIM_TYPE.
  167. */
  168. if (lpmii->fMask & MIIM_TYPE) {
  169. BOOL bRtoL = (lpmii->fType & MFT_RIGHTORDER) ? TRUE : FALSE;
  170. if (bRtoL || TestMF(pMenu, MFRTL)) {
  171. MakeMenuRtoL(pMenu, bRtoL);
  172. }
  173. }
  174. return xxxSetLPITEMInfo(pMenu, pItem, lpmii, pstrItem);
  175. }
  176. /***************************************************************************\
  177. * xxxSetMenuInfo (API)
  178. *
  179. *
  180. * History:
  181. * 12-Feb-1996 JudeJ Ported from Memphis
  182. * 23-Jun-1996 GerardoB Fixed up for 5.0
  183. \***************************************************************************/
  184. BOOL xxxSetMenuInfo(PMENU pMenu, LPCMENUINFO lpmi)
  185. {
  186. PPOPUPMENU ppopup;
  187. BOOL fRecompute = FALSE;
  188. BOOL fRedraw = FALSE;
  189. UINT uFlags = MNUS_DEFAULT;
  190. PITEM pItem;
  191. UINT uItems;
  192. TL tlSubMenu;
  193. CheckLock(pMenu);
  194. if (lpmi->fMask & MIM_STYLE) {
  195. pMenu->fFlags ^= (pMenu->fFlags ^ lpmi->dwStyle) & MNS_VALID;
  196. fRecompute = TRUE;
  197. }
  198. if (lpmi->fMask & MIM_MAXHEIGHT) {
  199. pMenu->cyMax = lpmi->cyMax;
  200. fRecompute = TRUE;
  201. }
  202. if (lpmi->fMask & MIM_BACKGROUND) {
  203. pMenu->hbrBack = lpmi->hbrBack;
  204. fRedraw = TRUE;
  205. if (pMenu->dwArrowsOn != MSA_OFF) {
  206. uFlags |= MNUS_DRAWFRAME;
  207. }
  208. }
  209. if (lpmi->fMask & MIM_HELPID) {
  210. pMenu->dwContextHelpId = lpmi->dwContextHelpID;
  211. }
  212. if (lpmi->fMask & MIM_MENUDATA) {
  213. pMenu->dwMenuData = lpmi->dwMenuData;
  214. }
  215. /*
  216. * Do we need to set this for all submenus?
  217. */
  218. if (lpmi->fMask & MIM_APPLYTOSUBMENUS) {
  219. pItem = pMenu->rgItems;
  220. for (uItems = pMenu->cItems; uItems--; pItem++) {
  221. if (pItem->spSubMenu != NULL) {
  222. ThreadLock(pItem->spSubMenu, &tlSubMenu);
  223. xxxSetMenuInfo(pItem->spSubMenu, lpmi);
  224. ThreadUnlock(&tlSubMenu);
  225. }
  226. }
  227. }
  228. if (fRecompute) {
  229. // Set the size of this menu to be 0 so that it gets recomputed with this
  230. // new item...
  231. pMenu->cyMenu = pMenu->cxMenu = 0;
  232. }
  233. if (fRecompute || fRedraw) {
  234. if (ppopup = MNGetPopupFromMenu(pMenu, NULL)) {
  235. // this menu is currently being displayed -- redisplay the menu,
  236. // recomputing if necessary
  237. xxxMNUpdateShownMenu(ppopup, NULL, uFlags);
  238. }
  239. }
  240. return TRUE;
  241. }
  242. /***************************************************************************\
  243. * MNDeleteAdjustIndex
  244. *
  245. * History:
  246. * 11/19/96 GerardoB Created
  247. \***************************************************************************/
  248. void NNDeleteAdjustIndex (UINT * puAdjustIndex, UINT uDelIndex)
  249. {
  250. if (*puAdjustIndex == uDelIndex) {
  251. *puAdjustIndex = MFMWFP_NOITEM;
  252. } else if ((int)*puAdjustIndex > (int)uDelIndex) {
  253. (*puAdjustIndex)--;
  254. }
  255. }
  256. /***************************************************************************\
  257. * MNDeleteAdjustIndexes
  258. *
  259. * This function is called when an item on an active menu is about
  260. * to be deleted. It makes sure that other indexes like posSelectedItem,
  261. * uButtonDownIndex and uDraggingIndex are adjusted to reflect the change
  262. * It "clears" the index if it is AT the point of deletion or
  263. * decrements it if it is after the point of deletion
  264. *
  265. * History:
  266. * 01/16/97 GerardoB Created
  267. \***************************************************************************/
  268. void MNDeleteAdjustIndexes (PMENUSTATE pMenuState, PPOPUPMENU ppopup, UINT uiPos)
  269. {
  270. /*
  271. * Adjust the index of the selected item and the dropped popup, if needed.
  272. */
  273. NNDeleteAdjustIndex(&ppopup->posSelectedItem, uiPos);
  274. if (ppopup->fHierarchyDropped) {
  275. NNDeleteAdjustIndex(&ppopup->posDropped, uiPos);
  276. }
  277. /*
  278. * Adjust uButtonDownIndex and uDraggingIndex if needed
  279. */
  280. if (pMenuState->uButtonDownHitArea == (ULONG_PTR)ppopup->spwndPopupMenu) {
  281. NNDeleteAdjustIndex(&pMenuState->uButtonDownIndex, uiPos);
  282. }
  283. if (pMenuState->uDraggingHitArea == (ULONG_PTR)ppopup->spwndPopupMenu) {
  284. NNDeleteAdjustIndex(&pMenuState->uDraggingIndex, uiPos);
  285. }
  286. }
  287. /***************************************************************************\
  288. * xxxInsertMenuItem
  289. *
  290. \***************************************************************************/
  291. BOOL xxxInsertMenuItem(
  292. PMENU pMenu,
  293. UINT wIndex,
  294. BOOL fByPosition,
  295. LPMENUITEMINFOW lpmii,
  296. PUNICODE_STRING pstrItem)
  297. {
  298. BOOL fRet = TRUE;
  299. PITEM pItem;
  300. PMENU pMenuItemIsOn;
  301. PMENUSTATE pMenuState;
  302. PITEM pNewItems;
  303. PPOPUPMENU ppopup = NULL;
  304. TL tlMenu;
  305. UINT uiPos;
  306. CheckLock(pMenu);
  307. // Find out where the item we are inserting should go.
  308. if (wIndex != MFMWFP_NOITEM) {
  309. pItem = MNLookUpItem(pMenu, wIndex, fByPosition, &pMenuItemIsOn);
  310. if (pItem != NULL) {
  311. pMenu = pMenuItemIsOn;
  312. } else {
  313. wIndex = MFMWFP_NOITEM;
  314. }
  315. } else {
  316. pItem = NULL;
  317. }
  318. /*
  319. * keep normal menu items between the MDI system bitmap items
  320. */
  321. if (!TestMF(pMenu, MFISPOPUP)
  322. && (pMenu->cItems != 0)
  323. && (!(lpmii->fMask & MIIM_BITMAP)
  324. || (lpmii->hbmpItem > HBMMENU_MBARLAST)
  325. || (lpmii->hbmpItem == 0)
  326. )) {
  327. UINT wSave, w;
  328. PITEM pItemWalk;
  329. wSave = w = wIndex;
  330. if (pItem && !fByPosition) {
  331. w = MNGetpItemIndex(pMenu, pItem);
  332. w = (UINT)((PBYTE)pItem - (PBYTE)(pMenu->rgItems)) / sizeof(ITEM);
  333. }
  334. if (!w) {
  335. pItemWalk = pMenu->rgItems;
  336. if ((pItemWalk->hbmp == HBMMENU_SYSTEM)) {
  337. wIndex = 1;
  338. }
  339. } else {
  340. if (w == MFMWFP_NOITEM) {
  341. w = pMenu->cItems;
  342. }
  343. w--;
  344. pItemWalk = pMenu->rgItems + w;
  345. while (w && (pItemWalk->hbmp) && (pItemWalk->hbmp < HBMMENU_MBARLAST)) {
  346. wIndex = w--;
  347. pItemWalk--;
  348. }
  349. }
  350. if (wIndex != wSave) {
  351. pItem = pMenu->rgItems + wIndex;
  352. }
  353. }
  354. // LATER -- we currently realloc every 10 items. investigate the
  355. // performance hit/gain we get from this and adjust accordingly.
  356. if (pMenu->cItems >= pMenu->cAlloced) {
  357. if (pMenu->rgItems) {
  358. pNewItems = (PITEM)DesktopAlloc(pMenu->head.rpdesk,
  359. (pMenu->cAlloced + CMENUITEMALLOC) * sizeof(ITEM),
  360. DTAG_MENUITEM);
  361. if (pNewItems) {
  362. RtlCopyMemory(pNewItems, pMenu->rgItems,
  363. pMenu->cAlloced * sizeof(ITEM));
  364. #if DBG
  365. if (IsDbgTagEnabled(DBGTAG_TrackLocks)) {
  366. RelocateMenuLockRecords(pNewItems, pMenu->cItems,
  367. ((PBYTE)pNewItems) - (PBYTE)(pMenu->rgItems));
  368. }
  369. #endif
  370. DesktopFree(pMenu->head.rpdesk, pMenu->rgItems);
  371. }
  372. } else {
  373. pNewItems = (PITEM)DesktopAlloc(pMenu->head.rpdesk,
  374. sizeof(ITEM) * CMENUITEMALLOC, DTAG_MENUITEM);
  375. }
  376. if (pNewItems == NULL) {
  377. return FALSE;
  378. }
  379. pMenu->cAlloced += CMENUITEMALLOC;
  380. pMenu->rgItems = pNewItems;
  381. /*
  382. * Now look up the item again since it probably moved when we realloced the
  383. * memory.
  384. */
  385. if (wIndex != MFMWFP_NOITEM)
  386. pItem = MNLookUpItem(pMenu, wIndex, fByPosition, &pMenuItemIsOn);
  387. }
  388. /*
  389. * If this menu is being displayed right now and we're not appending
  390. * an item, then we need to adjust the positions we keep track of.
  391. * We want to do this before moving the items to accomodate the
  392. * new one, in case we need to clear the insertion bar
  393. */
  394. if ((pItem != NULL)
  395. && (ppopup = MNGetPopupFromMenu(pMenu, &pMenuState))) {
  396. /*
  397. * This menu is active. Adjust the index the selected
  398. * item and the dropped popup, if needed
  399. */
  400. uiPos = MNGetpItemIndex(pMenu, pItem);
  401. if (ppopup->posSelectedItem >= (int)uiPos) {
  402. ppopup->posSelectedItem++;
  403. }
  404. if (ppopup->fHierarchyDropped && (ppopup->posDropped >= (int)uiPos)) {
  405. ppopup->posDropped++;
  406. }
  407. /*
  408. * Adjust uButtonDownIndex and uDraggingIndex if needed
  409. */
  410. if (pMenuState->uButtonDownHitArea == (ULONG_PTR)ppopup->spwndPopupMenu) {
  411. if ((int)pMenuState->uButtonDownIndex >= (int)uiPos) {
  412. pMenuState->uButtonDownIndex++;
  413. }
  414. }
  415. if (pMenuState->uDraggingHitArea == (ULONG_PTR)ppopup->spwndPopupMenu) {
  416. /*
  417. * Check to see if an item is inserted right on the insertion
  418. * bar. If so, clean up any present insertion bar state
  419. */
  420. if (((int)pMenuState->uDraggingIndex == (int)uiPos)
  421. && (pMenuState->uDraggingFlags & MNGOF_TOPGAP)) {
  422. xxxMNSetGapState(pMenuState->uDraggingHitArea,
  423. pMenuState->uDraggingIndex,
  424. pMenuState->uDraggingFlags,
  425. FALSE);
  426. }
  427. if ((int)pMenuState->uDraggingIndex >= (int)uiPos) {
  428. pMenuState->uDraggingIndex++;
  429. }
  430. }
  431. }
  432. pMenu->cItems++;
  433. if (pItem != NULL) {
  434. // Move this item up to make room for the one we want to insert.
  435. RtlMoveMemory(pItem + 1, pItem, (pMenu->cItems - 1) *
  436. sizeof(ITEM) - ((char *)pItem - (char *)pMenu->rgItems));
  437. #if DBG
  438. if (IsDbgTagEnabled(DBGTAG_TrackLocks)) {
  439. RelocateMenuLockRecords(pItem + 1,
  440. (int)(&(pMenu->rgItems[pMenu->cItems]) - (pItem + 1)),
  441. sizeof(ITEM));
  442. }
  443. #endif
  444. } else {
  445. // If lpItem is null, we will be inserting the item at the end of the
  446. // menu.
  447. pItem = pMenu->rgItems + pMenu->cItems - 1;
  448. }
  449. // Need to zero these fields in case we are inserting this item in the
  450. // middle of the item list.
  451. pItem->fType = 0;
  452. pItem->fState = 0;
  453. pItem->wID = 0;
  454. pItem->spSubMenu = NULL;
  455. pItem->hbmpChecked = NULL;
  456. pItem->hbmpUnchecked = NULL;
  457. pItem->cch = 0;
  458. pItem->dwItemData = 0;
  459. pItem->xItem = 0;
  460. pItem->yItem = 0;
  461. pItem->cxItem = 0;
  462. pItem->cyItem = 0;
  463. pItem->hbmp = NULL;
  464. pItem->cxBmp = MNIS_MEASUREBMP;
  465. pItem->lpstr = NULL;
  466. /*
  467. * We might have reassigned pMenu above, so lock it
  468. */
  469. ThreadLock(pMenu, &tlMenu);
  470. if (!xxxSetLPITEMInfo(pMenu, pItem, lpmii, pstrItem)) {
  471. /*
  472. * Reset any of the indexes we might have adjusted above
  473. */
  474. if (ppopup != NULL) {
  475. MNDeleteAdjustIndexes(pMenuState, ppopup, uiPos);
  476. }
  477. MNFreeItem(pMenu, pItem, TRUE);
  478. // Move things up since we removed/deleted the item
  479. RtlMoveMemory(pItem, pItem + 1, pMenu->cItems * (UINT)sizeof(ITEM) +
  480. (UINT)((char *)&pMenu->rgItems[0] - (char *)(pItem + 1)));
  481. #if DBG
  482. if (IsDbgTagEnabled(DBGTAG_TrackLocks)) {
  483. RelocateMenuLockRecords(pItem,
  484. (int)(&(pMenu->rgItems[pMenu->cItems - 1]) - pItem),
  485. -(int)sizeof(ITEM));
  486. }
  487. #endif
  488. pMenu->cItems--;
  489. fRet = FALSE;
  490. } else {
  491. /*
  492. * Like MFT_RIGHTJUSTIFY, this staggers down the menu,
  493. * (but we inherit, to make localisation etc MUCH easier).
  494. *
  495. * MFT_RIGHTORDER is the same value as MFT_SYSMENU. We distinguish
  496. * between the two by also looking for MFT_BITMAP.
  497. */
  498. if (TestMF(pMenu, MFRTL) ||
  499. (pItem && TestMFT(pItem, MFT_RIGHTORDER) && !TestMFT(pItem, MFT_BITMAP))) {
  500. pItem->fType |= (MFT_RIGHTORDER | MFT_RIGHTJUSTIFY);
  501. if (pItem->spSubMenu) {
  502. MakeMenuRtoL(pItem->spSubMenu, TRUE);
  503. }
  504. }
  505. }
  506. ThreadUnlock(&tlMenu);
  507. return fRet;
  508. }
  509. /***************************************************************************\
  510. * FreeItemBitmap
  511. *
  512. * History:
  513. * 07-23-96 GerardoB - Added header and Fixed up for 5.0
  514. \***************************************************************************/
  515. void FreeItemBitmap(PITEM pItem)
  516. {
  517. // Free up hItem unless it's a bitmap handle or nonexistent.
  518. // Apps are responsible for freeing their bitmaps.
  519. if ((pItem->hbmp != NULL) && !TestMFS(pItem, MFS_CACHEDBMP)) {
  520. /*
  521. * Assign ownership of the bitmap to the process that is
  522. * destroying the menu to ensure that bitmap will
  523. * eventually be destroyed.
  524. */
  525. GreSetBitmapOwner((HBITMAP)(pItem->hbmp), OBJECT_OWNER_CURRENT);
  526. }
  527. // Zap this pointer in case we try to free or reference it again
  528. pItem->hbmp = NULL;
  529. }
  530. /***************************************************************************\
  531. * FreeItemString
  532. *
  533. * History:
  534. * 07-23-96 GerardoB - Added header and Fixed up for 5.0
  535. \***************************************************************************/
  536. void FreeItemString(PMENU pMenu, PITEM pItem)
  537. {
  538. // Free up Item's string
  539. if ((pItem->lpstr != NULL)) {
  540. DesktopFree(pMenu->head.rpdesk, pItem->lpstr);
  541. }
  542. // Zap this pointer in case we try to free or reference it again
  543. pItem->lpstr = NULL;
  544. }
  545. /***************************************************************************\
  546. * FreeItem
  547. *
  548. * Free a menu item and its associated resources.
  549. *
  550. * History:
  551. * 10-11-90 JimA Translated from ASM
  552. \***************************************************************************/
  553. void MNFreeItem(
  554. PMENU pMenu,
  555. PITEM pItem,
  556. BOOL fFreeItemPopup)
  557. {
  558. PMENU pSubMenu;
  559. FreeItemBitmap(pItem);
  560. FreeItemString(pMenu, pItem);
  561. pSubMenu = UnlockSubMenu(pMenu, &(pItem->spSubMenu));
  562. if (pSubMenu) {
  563. if (fFreeItemPopup) {
  564. _DestroyMenu(pSubMenu);
  565. }
  566. }
  567. }
  568. /***************************************************************************\
  569. * RemoveDeleteMenuHelper
  570. *
  571. * This removes the menu item from the given menu. If
  572. * fDeleteMenuItem, the memory associted with the popup menu associated with
  573. * the item being removed is freed and recovered.
  574. *
  575. * History:
  576. \***************************************************************************/
  577. BOOL xxxRemoveDeleteMenuHelper(
  578. PMENU pMenu,
  579. UINT nPosition,
  580. DWORD wFlags,
  581. BOOL fDeleteMenu)
  582. {
  583. PITEM pItem;
  584. PITEM pNewItems;
  585. PMENU pMenuSave;
  586. PMENUSTATE pMenuState;
  587. PPOPUPMENU ppopup;
  588. UINT uiPos;
  589. CheckLock(pMenu);
  590. pMenuSave = pMenu;
  591. pItem = MNLookUpItem(pMenu, nPosition, (BOOL) (wFlags & MF_BYPOSITION), &pMenu);
  592. if (pItem == NULL) {
  593. /*
  594. * Hack for apps written for Win95. In Win95 the prototype for
  595. * this function was with 'WORD nPosition' and because of this
  596. * the HIWORD(nPosition) got set to 0.
  597. * We are doing this just for system menu commands.
  598. */
  599. if (nPosition >= 0xFFFFF000 && !(wFlags & MF_BYPOSITION)) {
  600. nPosition &= 0x0000FFFF;
  601. pMenu = pMenuSave;
  602. pItem = MNLookUpItem(pMenu, nPosition, FALSE, &pMenu);
  603. if (pItem == NULL)
  604. return FALSE;
  605. } else
  606. return FALSE;
  607. }
  608. if (ppopup = MNGetPopupFromMenu(pMenu, &pMenuState)) {
  609. /*
  610. * This menu is active; since we're about to insert an item,
  611. * make sure that any of the positions we've stored are
  612. * adjusted properly
  613. */
  614. uiPos = MNGetpItemIndex(pMenu, pItem);
  615. MNDeleteAdjustIndexes(pMenuState, ppopup, uiPos);
  616. }
  617. MNFreeItem(pMenu, pItem, fDeleteMenu);
  618. /*
  619. * Reset the menu size so that it gets recomputed next time.
  620. */
  621. pMenu->cyMenu = pMenu->cxMenu = 0;
  622. if (pMenu->cItems == 1) {
  623. DesktopFree(pMenu->head.rpdesk, pMenu->rgItems);
  624. pMenu->cAlloced = 0;
  625. pNewItems = NULL;
  626. } else {
  627. /*
  628. * Move things up since we removed/deleted the item.
  629. */
  630. RtlMoveMemory(pItem, pItem + 1, pMenu->cItems * (UINT)sizeof(ITEM) +
  631. (UINT)((char *)&pMenu->rgItems[0] - (char *)(pItem + 1)));
  632. #if DBG
  633. if (IsDbgTagEnabled(DBGTAG_TrackLocks)) {
  634. RelocateMenuLockRecords(pItem,
  635. (int)(&(pMenu->rgItems[pMenu->cItems - 1]) - pItem),
  636. -(int)sizeof(ITEM));
  637. }
  638. #endif
  639. /*
  640. * We're shrinking so if localalloc fails, just leave the mem as is.
  641. */
  642. UserAssert(pMenu->cAlloced >= pMenu->cItems);
  643. if ((pMenu->cAlloced - pMenu->cItems) >= CMENUITEMDEALLOC - 1) {
  644. pNewItems = (PITEM)DesktopAlloc(pMenu->head.rpdesk,
  645. (pMenu->cAlloced - CMENUITEMDEALLOC) * sizeof(ITEM),
  646. DTAG_MENUITEM);
  647. if (pNewItems != NULL) {
  648. RtlCopyMemory(pNewItems, pMenu->rgItems,
  649. (pMenu->cAlloced - CMENUITEMDEALLOC) * sizeof(ITEM));
  650. #if DBG
  651. if (IsDbgTagEnabled(DBGTAG_TrackLocks)) {
  652. RelocateMenuLockRecords(pNewItems, pMenu->cItems - 1,
  653. ((PBYTE)pNewItems) - (PBYTE)(pMenu->rgItems));
  654. }
  655. #endif
  656. DesktopFree(pMenu->head.rpdesk, pMenu->rgItems);
  657. pMenu->cAlloced -= CMENUITEMDEALLOC;
  658. } else {
  659. pNewItems = pMenu->rgItems;
  660. }
  661. } else {
  662. pNewItems = pMenu->rgItems;
  663. }
  664. }
  665. pMenu->rgItems = pNewItems;
  666. pMenu->cItems--;
  667. if (ppopup != NULL) {
  668. /*
  669. * this menu is currently being displayed -- redisplay the menu with
  670. * this item removed
  671. */
  672. xxxMNUpdateShownMenu(ppopup, pMenu->rgItems + uiPos, MNUS_DELETE);
  673. }
  674. return TRUE;
  675. }
  676. /***************************************************************************\
  677. * RemoveMenu
  678. *
  679. * Removes and item but doesn't delete it. Only useful for items with
  680. * an associated popup since this will remove the item from the menu with
  681. * destroying the popup menu handle.
  682. *
  683. * History:
  684. \***************************************************************************/
  685. BOOL xxxRemoveMenu(
  686. PMENU pMenu,
  687. UINT nPosition,
  688. UINT wFlags)
  689. {
  690. return xxxRemoveDeleteMenuHelper(pMenu, nPosition, wFlags, FALSE);
  691. }
  692. /***************************************************************************\
  693. * DeleteMenu
  694. *
  695. * Deletes an item. ie. Removes it and recovers the memory used by it.
  696. *
  697. * History:
  698. \***************************************************************************/
  699. BOOL xxxDeleteMenu(
  700. PMENU pMenu,
  701. UINT nPosition,
  702. UINT wFlags)
  703. {
  704. return xxxRemoveDeleteMenuHelper(pMenu, nPosition, wFlags, TRUE);
  705. }
  706. /***************************************************************************\
  707. * xxxSetLPITEMInfo
  708. *
  709. * History:
  710. * 07-23-96 GerardoB - Added header and Fixed up for 5.0
  711. \***************************************************************************/
  712. BOOL NEAR xxxSetLPITEMInfo(
  713. PMENU pMenu,
  714. PITEM pItem,
  715. LPMENUITEMINFOW lpmii,
  716. PUNICODE_STRING pstrItem)
  717. {
  718. HANDLE hstr;
  719. UINT cch;
  720. BOOL fRecompute = FALSE;
  721. BOOL fRedraw = FALSE;
  722. PPOPUPMENU ppopup;
  723. CheckLock(pMenu);
  724. if (lpmii->fMask & MIIM_FTYPE) {
  725. pItem->fType &= ~MFT_MASK;
  726. pItem->fType |= lpmii->fType;
  727. if (lpmii->fType & MFT_SEPARATOR ) {
  728. pItem->fState |= MFS_DISABLED ;
  729. }
  730. fRecompute = TRUE;
  731. fRedraw = (lpmii->fType & MFT_OWNERDRAW);
  732. }
  733. if (lpmii->fMask & MIIM_STRING) {
  734. if (pstrItem->Buffer != NULL) {
  735. hstr = (HANDLE)DesktopAlloc(pMenu->head.rpdesk,
  736. pstrItem->Length + sizeof(UNICODE_NULL), DTAG_MENUTEXT);
  737. if (hstr == NULL) {
  738. return FALSE;
  739. }
  740. try {
  741. RtlCopyMemory(hstr, pstrItem->Buffer, pstrItem->Length);
  742. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  743. DesktopFree(pMenu->head.rpdesk, hstr);
  744. return FALSE;
  745. }
  746. cch = pstrItem->Length / sizeof(WCHAR);
  747. /*
  748. * We don't need to null terminate the string, since DesktopAlloc
  749. * zero-fills for us.
  750. */
  751. } else {
  752. cch = 0;
  753. hstr = NULL;
  754. }
  755. FreeItemString(pMenu,pItem);
  756. pItem->cch = cch;
  757. pItem->lpstr = hstr;
  758. fRecompute = TRUE;
  759. fRedraw = TRUE;
  760. }
  761. if (lpmii->fMask & MIIM_BITMAP) {
  762. FreeItemBitmap(pItem);
  763. pItem->hbmp = lpmii->hbmpItem;
  764. fRecompute = TRUE;
  765. fRedraw = TRUE;
  766. pItem->cxBmp = MNIS_MEASUREBMP;
  767. /*
  768. * If this is one of the special bitmaps, mark it as such
  769. */
  770. if ((pItem->hbmp > HBMMENU_MIN) && (pItem->hbmp < HBMMENU_MAX)) {
  771. SetMFS(pItem, MFS_CACHEDBMP);
  772. } else {
  773. ClearMFS(pItem, MFS_CACHEDBMP);
  774. }
  775. }
  776. if (lpmii->fMask & MIIM_ID) {
  777. pItem->wID = lpmii->wID;
  778. }
  779. if (lpmii->fMask & MIIM_DATA) {
  780. pItem->dwItemData = lpmii->dwItemData;
  781. }
  782. if (lpmii->fMask & MIIM_STATE) {
  783. /*
  784. * Preserve private bits (~MFS_MASK).
  785. * Also preserve MFS_HILITE | MFS_DEFAULT if already set; if not set,
  786. * let the caller turn them on.
  787. */
  788. UserAssert(!(lpmii->fState & ~MFS_MASK));
  789. pItem->fState &= ~MFS_MASK | MFS_HILITE | MFS_DEFAULT;
  790. pItem->fState |= lpmii->fState;
  791. if (pItem->fType & MFT_SEPARATOR)
  792. pItem->fState |= MFS_DISABLED;
  793. fRedraw = TRUE;
  794. }
  795. if (lpmii->fMask & MIIM_CHECKMARKS) {
  796. pItem->hbmpChecked = lpmii->hbmpChecked;
  797. pItem->hbmpUnchecked = lpmii->hbmpUnchecked;
  798. fRedraw = TRUE;
  799. }
  800. if (lpmii->fMask & MIIM_SUBMENU) {
  801. PMENU pSubMenu = NULL;
  802. if (lpmii->hSubMenu != NULL) {
  803. pSubMenu = ValidateHmenu(lpmii->hSubMenu);
  804. }
  805. // Free the popup associated with this item, if any and if needed.
  806. if (pItem->spSubMenu != pSubMenu) {
  807. if (pItem->spSubMenu != NULL) {
  808. _DestroyMenu(pItem->spSubMenu);
  809. }
  810. if (pSubMenu != NULL) {
  811. BOOL bMenuCreated = FALSE;
  812. /*
  813. * Fix MSTest that sets a submenu to itself by giving it a different handle.
  814. * So the loop is broken and we won't fail their call later
  815. * MCostea #243374
  816. */
  817. if (pSubMenu == pMenu) {
  818. pSubMenu = _CreateMenu();
  819. if (!pSubMenu) {
  820. return FALSE;
  821. }
  822. bMenuCreated = TRUE;
  823. }
  824. /*
  825. * Link the submenu and then check for loops
  826. */
  827. Lock(&(pItem->spSubMenu), pSubMenu);
  828. SetMF(pItem->spSubMenu, MFISPOPUP);
  829. /*
  830. * We just added a submenu. Check to see if the menu tree is not
  831. * unreasonable deep and there is no loop forming.
  832. * This will prevent us from running out of stack
  833. * MCostea #226460
  834. */
  835. if (GetMenuDepth(pSubMenu, NESTED_MENU_LIMIT) + GetMenuAncestors(pMenu) >= NESTED_MENU_LIMIT) {
  836. FailInsertion:
  837. RIPMSG1(RIP_WARNING, "The menu hierarchy is very deep or has a loop %#p", pMenu);
  838. ClearMF(pItem->spSubMenu, MFISPOPUP);
  839. Unlock(&(pItem->spSubMenu));
  840. if (bMenuCreated) {
  841. _DestroyMenu(pSubMenu);
  842. }
  843. return FALSE;
  844. }
  845. /*
  846. * Add pMenu to the pSubMenu->pParentMenus list
  847. */
  848. {
  849. PMENULIST pMenuList = DesktopAlloc(pMenu->head.rpdesk,
  850. sizeof(MENULIST),
  851. DTAG_MENUITEM);
  852. if (!pMenuList) {
  853. goto FailInsertion;
  854. }
  855. pMenuList->pMenu = pMenu;
  856. pMenuList->pNext = pSubMenu->pParentMenus;
  857. pSubMenu->pParentMenus = pMenuList;
  858. }
  859. } else {
  860. UnlockSubMenu(pMenu, &(pItem->spSubMenu));
  861. }
  862. fRedraw = TRUE;
  863. }
  864. }
  865. // For support of the old way of defining a separator i.e. if it is not a string
  866. // or a bitmap or a ownerdraw, then it must be a separator.
  867. // This should prpbably be moved to MIIOneWayConvert -jjk
  868. if (!(pItem->fType & (MFT_OWNERDRAW | MFT_SEPARATOR))
  869. && (pItem->lpstr == NULL)
  870. && (pItem->hbmp == NULL)) {
  871. pItem->fType = MFT_SEPARATOR;
  872. pItem->fState|=MFS_DISABLED;
  873. }
  874. if (fRecompute) {
  875. pItem->dxTab = 0;
  876. pItem->ulX = UNDERLINE_RECALC;
  877. pItem->ulWidth = 0;
  878. // Set the size of this menu to be 0 so that it gets recomputed with this
  879. // new item...
  880. pMenu->cyMenu = pMenu->cxMenu = 0;
  881. if (fRedraw) {
  882. if (ppopup = MNGetPopupFromMenu(pMenu, NULL)) {
  883. // this menu is currently being displayed -- redisplay the menu,
  884. // recomputing if necessary
  885. xxxMNUpdateShownMenu(ppopup, pItem, MNUS_DEFAULT);
  886. }
  887. }
  888. }
  889. return TRUE;
  890. }
  891. BOOL _SetMenuContextHelpId(PMENU pMenu, DWORD dwContextHelpId)
  892. {
  893. // Set the new context help Id;
  894. pMenu->dwContextHelpId = dwContextHelpId;
  895. return TRUE;
  896. }
  897. BOOL _SetMenuFlagRtoL(PMENU pMenu)
  898. {
  899. // This is a right-to-left menu being created;
  900. SetMF(pMenu, MFRTL);
  901. return TRUE;
  902. }
  903. /***************************************************************************\
  904. * MNGetPopupFromMenu
  905. *
  906. * checks to see if the given hMenu is currently being shown in a popup.
  907. * returns the PPOPUPMENU associated with this hMenu if it is being shown;
  908. * NULL if the hMenu is not currently being shown
  909. *
  910. * History:
  911. * 07-23-96 GerardoB - Added header & fixed up for 5.0
  912. \***************************************************************************/
  913. PPOPUPMENU MNGetPopupFromMenu(PMENU pMenu, PMENUSTATE *ppMenuState)
  914. {
  915. PPOPUPMENU ppopup;
  916. PMENUSTATE pMenuState;
  917. /*
  918. * If this menu doesn't have a notification window, then
  919. * it cannot be in menu mode
  920. */
  921. if (pMenu->spwndNotify == NULL) {
  922. return NULL;
  923. }
  924. /*
  925. * If no pMenuState, no menu mode
  926. */
  927. pMenuState = GetpMenuState(pMenu->spwndNotify);
  928. if (pMenuState == NULL) {
  929. return NULL;
  930. }
  931. /*
  932. * If not in the menu loop, not yet or no longer in menu mode
  933. */
  934. if (!pMenuState->fInsideMenuLoop) {
  935. return NULL;
  936. }
  937. /*
  938. * return pMenuState if requested
  939. */
  940. if (ppMenuState != NULL) {
  941. *ppMenuState = pMenuState;
  942. }
  943. /*
  944. * Starting from the root popup, find the popup associated to this menu
  945. */
  946. ppopup = pMenuState->pGlobalPopupMenu;
  947. while (ppopup != NULL) {
  948. /*
  949. * found?
  950. */
  951. if (ppopup->spmenu == pMenu) {
  952. if (ppopup->fIsMenuBar) {
  953. return NULL;
  954. }
  955. /*
  956. * Since the menu is being modified, let's kill any animation.
  957. */
  958. MNAnimate(pMenuState, FALSE);
  959. return ppopup;
  960. }
  961. /*
  962. * If no more popups, bail
  963. */
  964. if (ppopup->spwndNextPopup == NULL) {
  965. return NULL;
  966. }
  967. /*
  968. * Next popup
  969. */
  970. ppopup = ((PMENUWND)ppopup->spwndNextPopup)->ppopupmenu;
  971. }
  972. return NULL;
  973. }
  974. /***************************************************************************\
  975. * xxxMNUpdateShownMenu
  976. *
  977. * updates a given ppopup menu window to reflect the inserting, deleting,
  978. * or altering of the given lpItem.
  979. *
  980. * History:
  981. * 07-23-96 GerardoB - Added header & fixed up for 5.0
  982. \***************************************************************************/
  983. void xxxMNUpdateShownMenu(PPOPUPMENU ppopup, PITEM pItem, UINT uFlags)
  984. {
  985. RECT rc;
  986. PWND pwnd = ppopup->spwndPopupMenu;
  987. PMENU pMenu = ppopup->spmenu;
  988. TL tlpwnd;
  989. TL tlpmenu;
  990. /*
  991. * The popup might get destroyed while we callback, so lock this pwnd.
  992. */
  993. ThreadLock(pwnd, &tlpwnd);
  994. ThreadLock(ppopup->spmenu, &tlpmenu);
  995. _GetClientRect(pwnd, &rc);
  996. /*
  997. * If we need to resize menu window
  998. */
  999. if (pMenu->cxMenu == 0) {
  1000. RECT rcScroll = rc;
  1001. int cySave = rc.bottom;
  1002. int cxSave = rc.right;
  1003. DWORD dwSize;
  1004. DWORD dwArrowsOnBefore;
  1005. dwArrowsOnBefore = pMenu->dwArrowsOn;
  1006. UserAssert(uFlags != 0);
  1007. dwSize = (DWORD)xxxSendMessage(pwnd, MN_SIZEWINDOW, uFlags, 0L);
  1008. uFlags &= ~MNUS_DRAWFRAME;
  1009. /*
  1010. * If scroll arrows appeared or disappeared, redraw entire client
  1011. */
  1012. if (dwArrowsOnBefore ^ pMenu->dwArrowsOn) {
  1013. goto InvalidateAll;
  1014. }
  1015. rc.right = LOWORD(dwSize);
  1016. if (pItem != NULL) {
  1017. if (rc.right != cxSave) {
  1018. /*
  1019. * The width changed, so redraw everything.
  1020. * NOTE -- This could be tuned to just redraw items with
  1021. * submenus and/or accelerator fields.
  1022. */
  1023. goto InvalidateAll;
  1024. } else {
  1025. rc.bottom = pMenu->cyMenu;
  1026. if (pMenu->dwArrowsOn != MSA_OFF) {
  1027. if (rc.bottom <= cySave) {
  1028. rc.top = pItem->yItem - MNGetToppItem(pMenu)->yItem;
  1029. goto InvalidateRest;
  1030. }
  1031. _GetClientRect(pwnd, &rcScroll);
  1032. }
  1033. rc.top = rcScroll.top = pItem->yItem - MNGetToppItem(pMenu)->yItem;
  1034. if ((rc.top >= 0) && (rc.top < (int)pMenu->cyMenu)) {
  1035. xxxScrollWindowEx(pwnd, 0, rc.bottom - cySave, &rcScroll, &rc, NULL, NULL, SW_INVALIDATE | SW_ERASE);
  1036. }
  1037. } /* else of if (rc.right != cxSave) */
  1038. } /* if (pItem != NULL) */
  1039. } /* if (pMenu->cxMenu == 0) */
  1040. if (!(uFlags & MNUS_DELETE)) {
  1041. if (pItem != NULL) {
  1042. rc.top = pItem->yItem - MNGetToppItem(pMenu)->yItem;
  1043. rc.bottom = rc.top + pItem->cyItem;
  1044. InvalidateRest:
  1045. if ((rc.top >= 0) && (rc.top < (int)pMenu->cyMenu)) {
  1046. xxxInvalidateRect(pwnd, &rc, TRUE);
  1047. }
  1048. } else {
  1049. InvalidateAll:
  1050. xxxInvalidateRect(pwnd, NULL, TRUE);
  1051. }
  1052. if (uFlags & MNUS_DRAWFRAME) {
  1053. xxxSetWindowPos(pwnd, NULL, 0, 0, 0, 0,
  1054. SWP_DRAWFRAME | SWP_NOSIZE | SWP_NOZORDER | SWP_NOMOVE
  1055. | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
  1056. }
  1057. }
  1058. ThreadUnlock(&tlpmenu);
  1059. ThreadUnlock(&tlpwnd);
  1060. }