|
|
/**************************** Module Header ********************************\
* Module Name: mnchange.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * Change Menu Routine * * History: * 10-10-90 JimA Cleanup. * 03-18-91 IanJa Window revalidation added (none required) \***************************************************************************/
#include "precomp.h"
#pragma hdrstop
/*
* Allocation/deallocation increments. Make them * different to avoid possible thrashing when an item * is repeatedly added and removed. */ #define CMENUITEMALLOC 8
#define CMENUITEMDEALLOC 10
BOOL xxxSetLPITEMInfo(PMENU pMenu, PITEM pItem, LPMENUITEMINFOW lpmii, PUNICODE_STRING pstr); typedef BOOL (*MENUAPIFN)(PMENU, UINT, BOOL, LPMENUITEMINFOW);
#if DBG
VOID RelocateMenuLockRecords( PITEM pItem, int cItem, LONG_PTR cbMove) { while (cItem > 0) { if (pItem->spSubMenu != NULL) { HMRelocateLockRecord(&(pItem->spSubMenu), cbMove); } pItem++; cItem--; } } #endif
/***************************************************************************\
* UnlockSubMenu * * Unlocks the pSubMenu and removes the MENULIST element corresponding to pMenu * * History: * Nov-20-98 MCostea \***************************************************************************/ PMENU UnlockSubMenu( PMENU pMenu, PMENU* ppSubMenu) { PMENULIST* pp; PMENULIST pMLFound;
if (*ppSubMenu == NULL) { return NULL; } /*
* Remove the item from pMenu's pParentsList */ for (pp = &(*ppSubMenu)->pParentMenus; *pp != NULL; pp = &(*pp)->pNext) { if ((*pp)->pMenu == pMenu) { pMLFound = *pp; *pp = (*pp)->pNext; DesktopFree(pMenu->head.rpdesk, pMLFound); break; } } return Unlock(ppSubMenu); }
#define NESTED_MENU_LIMIT 25
/***************************************************************************\
* GetMenuDepth * * Returns the menu depth (how many nested submenus this menu has). * This helps catch loops in the menu hierarchy or deep evil apps. * * History: * Sept-22-98 MCostea \***************************************************************************/ CHAR GetMenuDepth(PMENU pMenu, UINT uMaxAllowedDepth) { UINT uItems, uMaxDepth = 0, uSubMenuDepth; PITEM pItem;
/*
* This will prevent us from getting trapped in loops */ if (uMaxAllowedDepth == 0) { return NESTED_MENU_LIMIT; } pItem = pMenu->rgItems; for (uItems = pMenu->cItems; uItems--; pItem++) { if (pItem->spSubMenu != NULL) { uSubMenuDepth = GetMenuDepth(pItem->spSubMenu, uMaxAllowedDepth-1); if (uSubMenuDepth > uMaxDepth) { /*
* Don't walk the other submenus if a deep branch was found */ if (uSubMenuDepth >= NESTED_MENU_LIMIT) { return NESTED_MENU_LIMIT; } uMaxDepth = uSubMenuDepth; } } } return uMaxDepth + 1; }
/***************************************************************************\
* GetMenuAncestors * * Returns the maximum number of levels above pMenu in the menu hierarchy. * Walking the "parent" tree should not be expensive as it is pretty unusual * for menus to appear in different places in the hierarchy. The tree is * usualy a simple linked list. * * History: * Nov-10-98 MCostea \***************************************************************************/ CHAR GetMenuAncestors(PMENU pMenu) { PMENULIST pParentMenu; CHAR uParentAncestors; CHAR retVal = 0;
for (pParentMenu = pMenu->pParentMenus; pParentMenu; pParentMenu = pParentMenu->pNext) { uParentAncestors = GetMenuAncestors(pParentMenu->pMenu); if (uParentAncestors > retVal) { retVal = uParentAncestors; } } return retVal+1; }
/**********************************************
* Global Insert/Append/Set client/server interface * * 01-13-94 FritzS Created ***********************************************/ BOOL xxxSetMenuItemInfo( PMENU pMenu, UINT wIndex, BOOL fByPosition, LPMENUITEMINFOW lpmii, PUNICODE_STRING pstrItem) {
PITEM pItem;
CheckLock(pMenu);
pItem = MNLookUpItem(pMenu, wIndex, fByPosition,NULL); if (pItem == NULL) { /*
* Word doesn't like not finding SC_TASKLIST -- so it that's what * they're looking for, let's pretend we changed it. */ if (!fByPosition && (wIndex == SC_TASKLIST)) return TRUE;
/*
* Item not found. Return false. */ RIPERR0(ERROR_MENU_ITEM_NOT_FOUND, RIP_WARNING, "ModifyMenu: Menu item not found"); return FALSE; } /*
* we need to treat MFT_RIGHTORDER separately as this is propogated down * to the entire menu not just to this item so that we stay in ssync. This * is pretty similar to the use of MFT_RIGHTJUST, we actually do the * propogation because we need the flag in all sorts of places, not just * in MBC_RightJustifyMenu() */
/*
* See ValidateMENUITEMINFO in client\clmenu.c will add more flags to fMask if it use to be MIIM_TYPE * Then fMask will not be any more == MIIM_TYPE. */
if (lpmii->fMask & MIIM_TYPE) { BOOL bRtoL = (lpmii->fType & MFT_RIGHTORDER) ? TRUE : FALSE;
if (bRtoL || TestMF(pMenu, MFRTL)) { MakeMenuRtoL(pMenu, bRtoL); } } return xxxSetLPITEMInfo(pMenu, pItem, lpmii, pstrItem); }
/***************************************************************************\
* xxxSetMenuInfo (API) * * * History: * 12-Feb-1996 JudeJ Ported from Memphis * 23-Jun-1996 GerardoB Fixed up for 5.0 \***************************************************************************/ BOOL xxxSetMenuInfo(PMENU pMenu, LPCMENUINFO lpmi) { PPOPUPMENU ppopup; BOOL fRecompute = FALSE; BOOL fRedraw = FALSE; UINT uFlags = MNUS_DEFAULT; PITEM pItem; UINT uItems; TL tlSubMenu;
CheckLock(pMenu);
if (lpmi->fMask & MIM_STYLE) { pMenu->fFlags ^= (pMenu->fFlags ^ lpmi->dwStyle) & MNS_VALID; fRecompute = TRUE; }
if (lpmi->fMask & MIM_MAXHEIGHT) { pMenu->cyMax = lpmi->cyMax; fRecompute = TRUE; }
if (lpmi->fMask & MIM_BACKGROUND) { pMenu->hbrBack = lpmi->hbrBack; fRedraw = TRUE; if (pMenu->dwArrowsOn != MSA_OFF) { uFlags |= MNUS_DRAWFRAME; } }
if (lpmi->fMask & MIM_HELPID) { pMenu->dwContextHelpId = lpmi->dwContextHelpID; }
if (lpmi->fMask & MIM_MENUDATA) { pMenu->dwMenuData = lpmi->dwMenuData; }
/*
* Do we need to set this for all submenus? */ if (lpmi->fMask & MIM_APPLYTOSUBMENUS) { pItem = pMenu->rgItems; for (uItems = pMenu->cItems; uItems--; pItem++) { if (pItem->spSubMenu != NULL) { ThreadLock(pItem->spSubMenu, &tlSubMenu); xxxSetMenuInfo(pItem->spSubMenu, lpmi); ThreadUnlock(&tlSubMenu); } } }
if (fRecompute) { // Set the size of this menu to be 0 so that it gets recomputed with this
// new item...
pMenu->cyMenu = pMenu->cxMenu = 0; }
if (fRecompute || fRedraw) { if (ppopup = MNGetPopupFromMenu(pMenu, NULL)) { // this menu is currently being displayed -- redisplay the menu,
// recomputing if necessary
xxxMNUpdateShownMenu(ppopup, NULL, uFlags); } }
return TRUE; } /***************************************************************************\
* MNDeleteAdjustIndex * * History: * 11/19/96 GerardoB Created \***************************************************************************/ void NNDeleteAdjustIndex (UINT * puAdjustIndex, UINT uDelIndex) { if (*puAdjustIndex == uDelIndex) { *puAdjustIndex = MFMWFP_NOITEM; } else if ((int)*puAdjustIndex > (int)uDelIndex) { (*puAdjustIndex)--; } } /***************************************************************************\
* MNDeleteAdjustIndexes * * This function is called when an item on an active menu is about * to be deleted. It makes sure that other indexes like posSelectedItem, * uButtonDownIndex and uDraggingIndex are adjusted to reflect the change * It "clears" the index if it is AT the point of deletion or * decrements it if it is after the point of deletion * * History: * 01/16/97 GerardoB Created \***************************************************************************/ void MNDeleteAdjustIndexes (PMENUSTATE pMenuState, PPOPUPMENU ppopup, UINT uiPos) { /*
* Adjust the index of the selected item and the dropped popup, if needed. */ NNDeleteAdjustIndex(&ppopup->posSelectedItem, uiPos); if (ppopup->fHierarchyDropped) { NNDeleteAdjustIndex(&ppopup->posDropped, uiPos); }
/*
* Adjust uButtonDownIndex and uDraggingIndex if needed */ if (pMenuState->uButtonDownHitArea == (ULONG_PTR)ppopup->spwndPopupMenu) { NNDeleteAdjustIndex(&pMenuState->uButtonDownIndex, uiPos); } if (pMenuState->uDraggingHitArea == (ULONG_PTR)ppopup->spwndPopupMenu) { NNDeleteAdjustIndex(&pMenuState->uDraggingIndex, uiPos); } } /***************************************************************************\
* xxxInsertMenuItem * \***************************************************************************/ BOOL xxxInsertMenuItem( PMENU pMenu, UINT wIndex, BOOL fByPosition, LPMENUITEMINFOW lpmii, PUNICODE_STRING pstrItem) { BOOL fRet = TRUE; PITEM pItem; PMENU pMenuItemIsOn; PMENUSTATE pMenuState; PITEM pNewItems; PPOPUPMENU ppopup = NULL; TL tlMenu; UINT uiPos;
CheckLock(pMenu);
// Find out where the item we are inserting should go.
if (wIndex != MFMWFP_NOITEM) { pItem = MNLookUpItem(pMenu, wIndex, fByPosition, &pMenuItemIsOn);
if (pItem != NULL) { pMenu = pMenuItemIsOn; } else { wIndex = MFMWFP_NOITEM; } } else { pItem = NULL; } /*
* keep normal menu items between the MDI system bitmap items */ if (!TestMF(pMenu, MFISPOPUP) && (pMenu->cItems != 0) && (!(lpmii->fMask & MIIM_BITMAP) || (lpmii->hbmpItem > HBMMENU_MBARLAST) || (lpmii->hbmpItem == 0) )) {
UINT wSave, w; PITEM pItemWalk; wSave = w = wIndex;
if (pItem && !fByPosition) { w = MNGetpItemIndex(pMenu, pItem); w = (UINT)((PBYTE)pItem - (PBYTE)(pMenu->rgItems)) / sizeof(ITEM); }
if (!w) { pItemWalk = pMenu->rgItems; if ((pItemWalk->hbmp == HBMMENU_SYSTEM)) { wIndex = 1; } } else { if (w == MFMWFP_NOITEM) { w = pMenu->cItems; }
w--; pItemWalk = pMenu->rgItems + w; while (w && (pItemWalk->hbmp) && (pItemWalk->hbmp < HBMMENU_MBARLAST)) { wIndex = w--; pItemWalk--; } }
if (wIndex != wSave) { pItem = pMenu->rgItems + wIndex; } }
// LATER -- we currently realloc every 10 items. investigate the
// performance hit/gain we get from this and adjust accordingly.
if (pMenu->cItems >= pMenu->cAlloced) { if (pMenu->rgItems) { pNewItems = (PITEM)DesktopAlloc(pMenu->head.rpdesk, (pMenu->cAlloced + CMENUITEMALLOC) * sizeof(ITEM), DTAG_MENUITEM); if (pNewItems) { RtlCopyMemory(pNewItems, pMenu->rgItems, pMenu->cAlloced * sizeof(ITEM)); #if DBG
if (IsDbgTagEnabled(DBGTAG_TrackLocks)) { RelocateMenuLockRecords(pNewItems, pMenu->cItems, ((PBYTE)pNewItems) - (PBYTE)(pMenu->rgItems)); } #endif
DesktopFree(pMenu->head.rpdesk, pMenu->rgItems); } } else { pNewItems = (PITEM)DesktopAlloc(pMenu->head.rpdesk, sizeof(ITEM) * CMENUITEMALLOC, DTAG_MENUITEM); }
if (pNewItems == NULL) { return FALSE; }
pMenu->cAlloced += CMENUITEMALLOC; pMenu->rgItems = pNewItems;
/*
* Now look up the item again since it probably moved when we realloced the * memory. */ if (wIndex != MFMWFP_NOITEM) pItem = MNLookUpItem(pMenu, wIndex, fByPosition, &pMenuItemIsOn);
}
/*
* If this menu is being displayed right now and we're not appending * an item, then we need to adjust the positions we keep track of. * We want to do this before moving the items to accomodate the * new one, in case we need to clear the insertion bar */ if ((pItem != NULL) && (ppopup = MNGetPopupFromMenu(pMenu, &pMenuState))) { /*
* This menu is active. Adjust the index the selected * item and the dropped popup, if needed */ uiPos = MNGetpItemIndex(pMenu, pItem); if (ppopup->posSelectedItem >= (int)uiPos) { ppopup->posSelectedItem++; } if (ppopup->fHierarchyDropped && (ppopup->posDropped >= (int)uiPos)) { ppopup->posDropped++; }
/*
* Adjust uButtonDownIndex and uDraggingIndex if needed */ if (pMenuState->uButtonDownHitArea == (ULONG_PTR)ppopup->spwndPopupMenu) { if ((int)pMenuState->uButtonDownIndex >= (int)uiPos) { pMenuState->uButtonDownIndex++; } } if (pMenuState->uDraggingHitArea == (ULONG_PTR)ppopup->spwndPopupMenu) { /*
* Check to see if an item is inserted right on the insertion * bar. If so, clean up any present insertion bar state */ if (((int)pMenuState->uDraggingIndex == (int)uiPos) && (pMenuState->uDraggingFlags & MNGOF_TOPGAP)) {
xxxMNSetGapState(pMenuState->uDraggingHitArea, pMenuState->uDraggingIndex, pMenuState->uDraggingFlags, FALSE); }
if ((int)pMenuState->uDraggingIndex >= (int)uiPos) { pMenuState->uDraggingIndex++; } } }
pMenu->cItems++; if (pItem != NULL) { // Move this item up to make room for the one we want to insert.
RtlMoveMemory(pItem + 1, pItem, (pMenu->cItems - 1) * sizeof(ITEM) - ((char *)pItem - (char *)pMenu->rgItems)); #if DBG
if (IsDbgTagEnabled(DBGTAG_TrackLocks)) { RelocateMenuLockRecords(pItem + 1, (int)(&(pMenu->rgItems[pMenu->cItems]) - (pItem + 1)), sizeof(ITEM)); } #endif
} else {
// If lpItem is null, we will be inserting the item at the end of the
// menu.
pItem = pMenu->rgItems + pMenu->cItems - 1; }
// Need to zero these fields in case we are inserting this item in the
// middle of the item list.
pItem->fType = 0; pItem->fState = 0; pItem->wID = 0; pItem->spSubMenu = NULL; pItem->hbmpChecked = NULL; pItem->hbmpUnchecked = NULL; pItem->cch = 0; pItem->dwItemData = 0; pItem->xItem = 0; pItem->yItem = 0; pItem->cxItem = 0; pItem->cyItem = 0; pItem->hbmp = NULL; pItem->cxBmp = MNIS_MEASUREBMP; pItem->lpstr = NULL;
/*
* We might have reassigned pMenu above, so lock it */ ThreadLock(pMenu, &tlMenu); if (!xxxSetLPITEMInfo(pMenu, pItem, lpmii, pstrItem)) {
/*
* Reset any of the indexes we might have adjusted above */ if (ppopup != NULL) { MNDeleteAdjustIndexes(pMenuState, ppopup, uiPos); }
MNFreeItem(pMenu, pItem, TRUE);
// Move things up since we removed/deleted the item
RtlMoveMemory(pItem, pItem + 1, pMenu->cItems * (UINT)sizeof(ITEM) + (UINT)((char *)&pMenu->rgItems[0] - (char *)(pItem + 1))); #if DBG
if (IsDbgTagEnabled(DBGTAG_TrackLocks)) { RelocateMenuLockRecords(pItem, (int)(&(pMenu->rgItems[pMenu->cItems - 1]) - pItem), -(int)sizeof(ITEM)); } #endif
pMenu->cItems--; fRet = FALSE; } else { /*
* Like MFT_RIGHTJUSTIFY, this staggers down the menu, * (but we inherit, to make localisation etc MUCH easier). * * MFT_RIGHTORDER is the same value as MFT_SYSMENU. We distinguish * between the two by also looking for MFT_BITMAP. */ if (TestMF(pMenu, MFRTL) || (pItem && TestMFT(pItem, MFT_RIGHTORDER) && !TestMFT(pItem, MFT_BITMAP))) { pItem->fType |= (MFT_RIGHTORDER | MFT_RIGHTJUSTIFY); if (pItem->spSubMenu) { MakeMenuRtoL(pItem->spSubMenu, TRUE); } } }
ThreadUnlock(&tlMenu); return fRet;
}
/***************************************************************************\
* FreeItemBitmap * * History: * 07-23-96 GerardoB - Added header and Fixed up for 5.0 \***************************************************************************/ void FreeItemBitmap(PITEM pItem) { // Free up hItem unless it's a bitmap handle or nonexistent.
// Apps are responsible for freeing their bitmaps.
if ((pItem->hbmp != NULL) && !TestMFS(pItem, MFS_CACHEDBMP)) { /*
* Assign ownership of the bitmap to the process that is * destroying the menu to ensure that bitmap will * eventually be destroyed. */ GreSetBitmapOwner((HBITMAP)(pItem->hbmp), OBJECT_OWNER_CURRENT); }
// Zap this pointer in case we try to free or reference it again
pItem->hbmp = NULL; } /***************************************************************************\
* FreeItemString * * History: * 07-23-96 GerardoB - Added header and Fixed up for 5.0 \***************************************************************************/
void FreeItemString(PMENU pMenu, PITEM pItem) { // Free up Item's string
if ((pItem->lpstr != NULL)) { DesktopFree(pMenu->head.rpdesk, pItem->lpstr); } // Zap this pointer in case we try to free or reference it again
pItem->lpstr = NULL; }
/***************************************************************************\
* FreeItem * * Free a menu item and its associated resources. * * History: * 10-11-90 JimA Translated from ASM \***************************************************************************/
void MNFreeItem( PMENU pMenu, PITEM pItem, BOOL fFreeItemPopup) { PMENU pSubMenu;
FreeItemBitmap(pItem); FreeItemString(pMenu, pItem);
pSubMenu = UnlockSubMenu(pMenu, &(pItem->spSubMenu)); if (pSubMenu) { if (fFreeItemPopup) { _DestroyMenu(pSubMenu); } } } /***************************************************************************\
* RemoveDeleteMenuHelper * * This removes the menu item from the given menu. If * fDeleteMenuItem, the memory associted with the popup menu associated with * the item being removed is freed and recovered. * * History: \***************************************************************************/
BOOL xxxRemoveDeleteMenuHelper( PMENU pMenu, UINT nPosition, DWORD wFlags, BOOL fDeleteMenu) { PITEM pItem; PITEM pNewItems; PMENU pMenuSave; PMENUSTATE pMenuState; PPOPUPMENU ppopup; UINT uiPos;
CheckLock(pMenu);
pMenuSave = pMenu;
pItem = MNLookUpItem(pMenu, nPosition, (BOOL) (wFlags & MF_BYPOSITION), &pMenu); if (pItem == NULL) {
/*
* Hack for apps written for Win95. In Win95 the prototype for * this function was with 'WORD nPosition' and because of this * the HIWORD(nPosition) got set to 0. * We are doing this just for system menu commands. */ if (nPosition >= 0xFFFFF000 && !(wFlags & MF_BYPOSITION)) { nPosition &= 0x0000FFFF; pMenu = pMenuSave; pItem = MNLookUpItem(pMenu, nPosition, FALSE, &pMenu);
if (pItem == NULL) return FALSE; } else return FALSE; }
if (ppopup = MNGetPopupFromMenu(pMenu, &pMenuState)) { /*
* This menu is active; since we're about to insert an item, * make sure that any of the positions we've stored are * adjusted properly */ uiPos = MNGetpItemIndex(pMenu, pItem); MNDeleteAdjustIndexes(pMenuState, ppopup, uiPos); } MNFreeItem(pMenu, pItem, fDeleteMenu);
/*
* Reset the menu size so that it gets recomputed next time. */ pMenu->cyMenu = pMenu->cxMenu = 0;
if (pMenu->cItems == 1) { DesktopFree(pMenu->head.rpdesk, pMenu->rgItems); pMenu->cAlloced = 0; pNewItems = NULL; } else { /*
* Move things up since we removed/deleted the item. */
RtlMoveMemory(pItem, pItem + 1, pMenu->cItems * (UINT)sizeof(ITEM) + (UINT)((char *)&pMenu->rgItems[0] - (char *)(pItem + 1))); #if DBG
if (IsDbgTagEnabled(DBGTAG_TrackLocks)) { RelocateMenuLockRecords(pItem, (int)(&(pMenu->rgItems[pMenu->cItems - 1]) - pItem), -(int)sizeof(ITEM)); } #endif
/*
* We're shrinking so if localalloc fails, just leave the mem as is. */ UserAssert(pMenu->cAlloced >= pMenu->cItems); if ((pMenu->cAlloced - pMenu->cItems) >= CMENUITEMDEALLOC - 1) { pNewItems = (PITEM)DesktopAlloc(pMenu->head.rpdesk, (pMenu->cAlloced - CMENUITEMDEALLOC) * sizeof(ITEM), DTAG_MENUITEM); if (pNewItems != NULL) {
RtlCopyMemory(pNewItems, pMenu->rgItems, (pMenu->cAlloced - CMENUITEMDEALLOC) * sizeof(ITEM)); #if DBG
if (IsDbgTagEnabled(DBGTAG_TrackLocks)) { RelocateMenuLockRecords(pNewItems, pMenu->cItems - 1, ((PBYTE)pNewItems) - (PBYTE)(pMenu->rgItems)); } #endif
DesktopFree(pMenu->head.rpdesk, pMenu->rgItems); pMenu->cAlloced -= CMENUITEMDEALLOC; } else { pNewItems = pMenu->rgItems; } } else { pNewItems = pMenu->rgItems; } }
pMenu->rgItems = pNewItems; pMenu->cItems--;
if (ppopup != NULL) { /*
* this menu is currently being displayed -- redisplay the menu with * this item removed */ xxxMNUpdateShownMenu(ppopup, pMenu->rgItems + uiPos, MNUS_DELETE); } return TRUE; }
/***************************************************************************\
* RemoveMenu * * Removes and item but doesn't delete it. Only useful for items with * an associated popup since this will remove the item from the menu with * destroying the popup menu handle. * * History: \***************************************************************************/
BOOL xxxRemoveMenu( PMENU pMenu, UINT nPosition, UINT wFlags) { return xxxRemoveDeleteMenuHelper(pMenu, nPosition, wFlags, FALSE); }
/***************************************************************************\
* DeleteMenu * * Deletes an item. ie. Removes it and recovers the memory used by it. * * History: \***************************************************************************/
BOOL xxxDeleteMenu( PMENU pMenu, UINT nPosition, UINT wFlags) { return xxxRemoveDeleteMenuHelper(pMenu, nPosition, wFlags, TRUE); }
/***************************************************************************\
* xxxSetLPITEMInfo * * History: * 07-23-96 GerardoB - Added header and Fixed up for 5.0 \***************************************************************************/ BOOL NEAR xxxSetLPITEMInfo( PMENU pMenu, PITEM pItem, LPMENUITEMINFOW lpmii, PUNICODE_STRING pstrItem) {
HANDLE hstr; UINT cch; BOOL fRecompute = FALSE; BOOL fRedraw = FALSE; PPOPUPMENU ppopup;
CheckLock(pMenu);
if (lpmii->fMask & MIIM_FTYPE) { pItem->fType &= ~MFT_MASK; pItem->fType |= lpmii->fType; if (lpmii->fType & MFT_SEPARATOR ) { pItem->fState |= MFS_DISABLED ; } fRecompute = TRUE; fRedraw = (lpmii->fType & MFT_OWNERDRAW); }
if (lpmii->fMask & MIIM_STRING) { if (pstrItem->Buffer != NULL) { hstr = (HANDLE)DesktopAlloc(pMenu->head.rpdesk, pstrItem->Length + sizeof(UNICODE_NULL), DTAG_MENUTEXT);
if (hstr == NULL) { return FALSE; }
try { RtlCopyMemory(hstr, pstrItem->Buffer, pstrItem->Length); } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { DesktopFree(pMenu->head.rpdesk, hstr); return FALSE; } cch = pstrItem->Length / sizeof(WCHAR); /*
* We don't need to null terminate the string, since DesktopAlloc * zero-fills for us. */ } else { cch = 0; hstr = NULL; } FreeItemString(pMenu,pItem); pItem->cch = cch; pItem->lpstr = hstr; fRecompute = TRUE; fRedraw = TRUE; }
if (lpmii->fMask & MIIM_BITMAP) { FreeItemBitmap(pItem); pItem->hbmp = lpmii->hbmpItem; fRecompute = TRUE; fRedraw = TRUE; pItem->cxBmp = MNIS_MEASUREBMP; /*
* If this is one of the special bitmaps, mark it as such */ if ((pItem->hbmp > HBMMENU_MIN) && (pItem->hbmp < HBMMENU_MAX)) { SetMFS(pItem, MFS_CACHEDBMP); } else { ClearMFS(pItem, MFS_CACHEDBMP); } }
if (lpmii->fMask & MIIM_ID) { pItem->wID = lpmii->wID; }
if (lpmii->fMask & MIIM_DATA) { pItem->dwItemData = lpmii->dwItemData; }
if (lpmii->fMask & MIIM_STATE) { /*
* Preserve private bits (~MFS_MASK). * Also preserve MFS_HILITE | MFS_DEFAULT if already set; if not set, * let the caller turn them on. */ UserAssert(!(lpmii->fState & ~MFS_MASK)); pItem->fState &= ~MFS_MASK | MFS_HILITE | MFS_DEFAULT; pItem->fState |= lpmii->fState; if (pItem->fType & MFT_SEPARATOR) pItem->fState |= MFS_DISABLED; fRedraw = TRUE; }
if (lpmii->fMask & MIIM_CHECKMARKS) { pItem->hbmpChecked = lpmii->hbmpChecked; pItem->hbmpUnchecked = lpmii->hbmpUnchecked; fRedraw = TRUE; }
if (lpmii->fMask & MIIM_SUBMENU) { PMENU pSubMenu = NULL;
if (lpmii->hSubMenu != NULL) { pSubMenu = ValidateHmenu(lpmii->hSubMenu); }
// Free the popup associated with this item, if any and if needed.
if (pItem->spSubMenu != pSubMenu) { if (pItem->spSubMenu != NULL) { _DestroyMenu(pItem->spSubMenu); } if (pSubMenu != NULL) {
BOOL bMenuCreated = FALSE; /*
* Fix MSTest that sets a submenu to itself by giving it a different handle. * So the loop is broken and we won't fail their call later * MCostea #243374 */ if (pSubMenu == pMenu) { pSubMenu = _CreateMenu(); if (!pSubMenu) { return FALSE; } bMenuCreated = TRUE; } /*
* Link the submenu and then check for loops */ Lock(&(pItem->spSubMenu), pSubMenu); SetMF(pItem->spSubMenu, MFISPOPUP); /*
* We just added a submenu. Check to see if the menu tree is not * unreasonable deep and there is no loop forming. * This will prevent us from running out of stack * MCostea #226460 */ if (GetMenuDepth(pSubMenu, NESTED_MENU_LIMIT) + GetMenuAncestors(pMenu) >= NESTED_MENU_LIMIT) { FailInsertion: RIPMSG1(RIP_WARNING, "The menu hierarchy is very deep or has a loop %#p", pMenu); ClearMF(pItem->spSubMenu, MFISPOPUP); Unlock(&(pItem->spSubMenu)); if (bMenuCreated) { _DestroyMenu(pSubMenu); } return FALSE; } /*
* Add pMenu to the pSubMenu->pParentMenus list */ { PMENULIST pMenuList = DesktopAlloc(pMenu->head.rpdesk, sizeof(MENULIST), DTAG_MENUITEM); if (!pMenuList) { goto FailInsertion; } pMenuList->pMenu = pMenu; pMenuList->pNext = pSubMenu->pParentMenus; pSubMenu->pParentMenus = pMenuList; } } else { UnlockSubMenu(pMenu, &(pItem->spSubMenu)); } fRedraw = TRUE; } }
// For support of the old way of defining a separator i.e. if it is not a string
// or a bitmap or a ownerdraw, then it must be a separator.
// This should prpbably be moved to MIIOneWayConvert -jjk
if (!(pItem->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) && (pItem->lpstr == NULL) && (pItem->hbmp == NULL)) {
pItem->fType = MFT_SEPARATOR; pItem->fState|=MFS_DISABLED; }
if (fRecompute) { pItem->dxTab = 0; pItem->ulX = UNDERLINE_RECALC; pItem->ulWidth = 0;
// Set the size of this menu to be 0 so that it gets recomputed with this
// new item...
pMenu->cyMenu = pMenu->cxMenu = 0;
if (fRedraw) { if (ppopup = MNGetPopupFromMenu(pMenu, NULL)) { // this menu is currently being displayed -- redisplay the menu,
// recomputing if necessary
xxxMNUpdateShownMenu(ppopup, pItem, MNUS_DEFAULT); } }
}
return TRUE; }
BOOL _SetMenuContextHelpId(PMENU pMenu, DWORD dwContextHelpId) {
// Set the new context help Id;
pMenu->dwContextHelpId = dwContextHelpId;
return TRUE; }
BOOL _SetMenuFlagRtoL(PMENU pMenu) {
// This is a right-to-left menu being created;
SetMF(pMenu, MFRTL);
return TRUE; }
/***************************************************************************\
* MNGetPopupFromMenu * * checks to see if the given hMenu is currently being shown in a popup. * returns the PPOPUPMENU associated with this hMenu if it is being shown; * NULL if the hMenu is not currently being shown * * History: * 07-23-96 GerardoB - Added header & fixed up for 5.0 \***************************************************************************/ PPOPUPMENU MNGetPopupFromMenu(PMENU pMenu, PMENUSTATE *ppMenuState) { PPOPUPMENU ppopup; PMENUSTATE pMenuState;
/*
* If this menu doesn't have a notification window, then * it cannot be in menu mode */ if (pMenu->spwndNotify == NULL) { return NULL; }
/*
* If no pMenuState, no menu mode */ pMenuState = GetpMenuState(pMenu->spwndNotify); if (pMenuState == NULL) { return NULL; }
/*
* If not in the menu loop, not yet or no longer in menu mode */ if (!pMenuState->fInsideMenuLoop) { return NULL; }
/*
* return pMenuState if requested */ if (ppMenuState != NULL) { *ppMenuState = pMenuState; }
/*
* Starting from the root popup, find the popup associated to this menu */ ppopup = pMenuState->pGlobalPopupMenu; while (ppopup != NULL) { /*
* found? */ if (ppopup->spmenu == pMenu) { if (ppopup->fIsMenuBar) { return NULL; } /*
* Since the menu is being modified, let's kill any animation. */ MNAnimate(pMenuState, FALSE); return ppopup; } /*
* If no more popups, bail */ if (ppopup->spwndNextPopup == NULL) { return NULL; }
/*
* Next popup */ ppopup = ((PMENUWND)ppopup->spwndNextPopup)->ppopupmenu; }
return NULL; }
/***************************************************************************\
* xxxMNUpdateShownMenu * * updates a given ppopup menu window to reflect the inserting, deleting, * or altering of the given lpItem. * * History: * 07-23-96 GerardoB - Added header & fixed up for 5.0 \***************************************************************************/ void xxxMNUpdateShownMenu(PPOPUPMENU ppopup, PITEM pItem, UINT uFlags) { RECT rc; PWND pwnd = ppopup->spwndPopupMenu; PMENU pMenu = ppopup->spmenu; TL tlpwnd; TL tlpmenu;
/*
* The popup might get destroyed while we callback, so lock this pwnd. */ ThreadLock(pwnd, &tlpwnd); ThreadLock(ppopup->spmenu, &tlpmenu);
_GetClientRect(pwnd, &rc);
/*
* If we need to resize menu window */ if (pMenu->cxMenu == 0) { RECT rcScroll = rc; int cySave = rc.bottom; int cxSave = rc.right; DWORD dwSize; DWORD dwArrowsOnBefore;
dwArrowsOnBefore = pMenu->dwArrowsOn; UserAssert(uFlags != 0); dwSize = (DWORD)xxxSendMessage(pwnd, MN_SIZEWINDOW, uFlags, 0L); uFlags &= ~MNUS_DRAWFRAME; /*
* If scroll arrows appeared or disappeared, redraw entire client */ if (dwArrowsOnBefore ^ pMenu->dwArrowsOn) { goto InvalidateAll; }
rc.right = LOWORD(dwSize); if (pItem != NULL) { if (rc.right != cxSave) { /*
* The width changed, so redraw everything. * NOTE -- This could be tuned to just redraw items with * submenus and/or accelerator fields. */ goto InvalidateAll; } else { rc.bottom = pMenu->cyMenu; if (pMenu->dwArrowsOn != MSA_OFF) { if (rc.bottom <= cySave) { rc.top = pItem->yItem - MNGetToppItem(pMenu)->yItem; goto InvalidateRest; }
_GetClientRect(pwnd, &rcScroll); }
rc.top = rcScroll.top = pItem->yItem - MNGetToppItem(pMenu)->yItem; if ((rc.top >= 0) && (rc.top < (int)pMenu->cyMenu)) { xxxScrollWindowEx(pwnd, 0, rc.bottom - cySave, &rcScroll, &rc, NULL, NULL, SW_INVALIDATE | SW_ERASE); } } /* else of if (rc.right != cxSave) */ } /* if (pItem != NULL) */ } /* if (pMenu->cxMenu == 0) */
if (!(uFlags & MNUS_DELETE)) { if (pItem != NULL) { rc.top = pItem->yItem - MNGetToppItem(pMenu)->yItem; rc.bottom = rc.top + pItem->cyItem; InvalidateRest: if ((rc.top >= 0) && (rc.top < (int)pMenu->cyMenu)) { xxxInvalidateRect(pwnd, &rc, TRUE); } } else { InvalidateAll: xxxInvalidateRect(pwnd, NULL, TRUE); } if (uFlags & MNUS_DRAWFRAME) { xxxSetWindowPos(pwnd, NULL, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOSIZE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER); } }
ThreadUnlock(&tlpmenu); ThreadUnlock(&tlpwnd); }
|