mirror of https://github.com/lianthony/NT4.0
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.
1076 lines
31 KiB
1076 lines
31 KiB
/**************************** Module Header ********************************\
|
|
* Module Name: mnchange.c
|
|
*
|
|
* Copyright 1985-90, 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
|
|
#ifdef DEBUG
|
|
extern BOOL gfTrackLocks;
|
|
#endif
|
|
|
|
/*
|
|
* Allocation/deallocation increments. Make them
|
|
* different to avoid possible thrashing when an item
|
|
* is repeatedly added and removed.
|
|
*/
|
|
#define CMENUITEMALLOC 8
|
|
#define CMENUITEMDEALLOC 10
|
|
|
|
#ifdef MEMPHIS_MENUS
|
|
BOOL xxxSetLPITEMInfo(PMENU pMenu, PITEM pItem,
|
|
LPMENUITEMINFOW lpmii, PUNICODE_STRING pstr);
|
|
void xxxMNUpdateShownMenu(PPOPUPMENU, LPITEM, BOOL);
|
|
PPOPUPMENU MNCheckMenuUp(PMENU);
|
|
#else // MEMPHIS_MENUS
|
|
BOOL SetLPITEMInfo(PMENU pMenu, PITEM pItem,
|
|
LPMENUITEMINFOW lpmii, PUNICODE_STRING pstr);
|
|
#endif //MEMPHIS_MENUS
|
|
typedef BOOL (*MENUAPIFN)(PMENU, UINT, BOOL, LPMENUITEMINFOW);
|
|
|
|
#ifdef DEBUG
|
|
VOID RelocateMenuLockRecords(
|
|
PITEM pItem,
|
|
int cItem,
|
|
int cbMove)
|
|
{
|
|
while (cItem > 0) {
|
|
if (pItem->spSubMenu != NULL) {
|
|
HMRelocateLockRecord(&(pItem->spSubMenu), cbMove);
|
|
}
|
|
pItem++;
|
|
cItem--;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/**********************************************
|
|
* Global Insert/Append/Set client/server interface
|
|
*
|
|
* 01-13-94 FritzS Created
|
|
***********************************************/
|
|
#ifdef MEMPHIS_MENUS
|
|
BOOL xxxSetMenuItemInfo(
|
|
#else // MEMPHIS_MENUS
|
|
BOOL _SetMenuItemInfo(
|
|
#endif //MEMPHIS_MENUS
|
|
PMENU pMenu,
|
|
UINT wIndex,
|
|
BOOL fByPosition,
|
|
LPMENUITEMINFOW lpmii,
|
|
PUNICODE_STRING pstrItem)
|
|
{
|
|
|
|
PITEM pItem;
|
|
#ifdef MEMPHIS_MENUS
|
|
CheckLock(pMenu);
|
|
#endif // MEMPHIS_MENUS
|
|
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;
|
|
}
|
|
#ifdef MEMPHIS_MENUS
|
|
return xxxSetLPITEMInfo(pMenu, pItem, lpmii, pstrItem);
|
|
#else // MEMPHIS_MENUS
|
|
return SetLPITEMInfo(pMenu, pItem, lpmii, pstrItem);
|
|
#endif // MEMPHIS_MENUS
|
|
}
|
|
|
|
#ifdef MEMPHIS_MENU_WATERMARKS
|
|
/***************************************************************************\
|
|
* xxxSetMenuInfo (API)
|
|
*
|
|
*
|
|
* History:
|
|
* 12-Feb-1996 JudeJ Ported from Memphis
|
|
\***************************************************************************/
|
|
BOOL APIENTRY xxxSetMenuInfo(PMENU pMenu, LPCMENUINFO lpmi)
|
|
{
|
|
PPOPUPMENU ppopup;
|
|
BOOL fRecompute = FALSE;
|
|
BOOL fRedraw = FALSE;
|
|
|
|
CheckLock(pMenu);
|
|
if (lpmi->fMask & MIM_STYLE) {
|
|
if (lpmi->dwStyle & MNS_NOCHECK)
|
|
SetMF(pMenu, MFNOCHECK);
|
|
else
|
|
ClearMF(pMenu, MFNOCHECK);
|
|
fRecompute = TRUE;
|
|
}
|
|
|
|
// if (lpmi->fMask & MIM_MAXHEIGHT) {
|
|
// pMenu->cyMax = LOWORD(lpmi->cyMax);
|
|
// fRecompute = TRUE;
|
|
// }
|
|
|
|
if (lpmi->fMask & MIM_BACKGROUND) {
|
|
pMenu->hbrBack = lpmi->hbrBack;
|
|
fRedraw = TRUE;
|
|
}
|
|
|
|
// if (lpmi->fMask & MIM_HELPID) {
|
|
// pMenu->dwContextHelpID = lpmi->dwContextHelpID;
|
|
// }
|
|
|
|
// if (lpmi->fMask & MIM_MENUDATA) {
|
|
// pMenu->dwMenuData = lpmi->dwMenuData;
|
|
// }
|
|
|
|
|
|
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;
|
|
|
|
Redraw:
|
|
if (ppopup = MNCheckMenuUp(pMenu)) {
|
|
// this menu is currently being displayed -- redisplay the menu,
|
|
// recomputing if necessary
|
|
xxxMNUpdateShownMenu(ppopup, NULL, FALSE);
|
|
}
|
|
} else if (fRedraw) {
|
|
goto Redraw;
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
#endif // MEMPHIS_MENU_WATERMARKS
|
|
|
|
#ifdef MEMPHIS_MENUS
|
|
BOOL xxxInsertMenuItem(
|
|
#else
|
|
BOOL _InsertMenuItem(
|
|
#endif // MEMPHIS_MENUS
|
|
PMENU pMenu,
|
|
UINT wIndex,
|
|
BOOL fByPosition,
|
|
LPMENUITEMINFOW lpmii,
|
|
PUNICODE_STRING pstrItem)
|
|
{
|
|
PITEM pItem;
|
|
|
|
PMENU pMenuItemIsOn;
|
|
PITEM pNewItems;
|
|
#ifdef MEMPHIS_MENUS
|
|
PPOPUPMENU ppopup;
|
|
TL tlMenu;
|
|
|
|
CheckLock(pMenu);
|
|
#endif // MEMPHIS_MENUS
|
|
// 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;
|
|
#ifdef MEMPHIS_MENUS
|
|
if (pMenu->cItems && (!(lpmii->fMask & MIIM_BITMAP) || (lpmii->hbmpItem >= (HBITMAP)MENUHBM_MAX)))
|
|
#else // MEMPHIS_MENUS
|
|
if (pMenu->cItems && (!(lpmii->fType & MFT_BITMAP) || (lpmii->dwTypeData >= (LPWSTR)MENUHBM_MAX)))
|
|
#endif // MEMPHIS_MENUS
|
|
{
|
|
// keep normal menu items between the MDI system bitmap items
|
|
UINT wSave, w;
|
|
PITEM pItemWalk;
|
|
wSave = w = wIndex;
|
|
|
|
if (pItem && !fByPosition)
|
|
w = ((PBYTE)pItem - (PBYTE)(pMenu->rgItems)) / sizeof(ITEM);
|
|
|
|
if (!w)
|
|
{
|
|
pItemWalk = pMenu->rgItems;
|
|
#ifdef MEMPHIS_MENUS
|
|
if ((pItemWalk->hbmp == (HBITMAP)MENUHBM_SYSTEM))
|
|
#else // MEMPHIS_MENUS
|
|
if ((pItemWalk->fType & MFT_BITMAP) && (pItemWalk->hTypeData == (HANDLE)MENUHBM_SYSTEM))
|
|
#endif //MEMPHIS_MENUS
|
|
wIndex = 1;
|
|
}
|
|
else
|
|
{
|
|
if (w == MFMWFP_NOITEM)
|
|
w = pMenu->cItems;
|
|
|
|
w--;
|
|
pItemWalk = pMenu->rgItems + w;
|
|
#ifdef MEMPHIS_MENUS
|
|
while (w && (pItemWalk->hbmp) && (pItemWalk->hbmp < (HBITMAP)MENUHBM_MAX))
|
|
#else // MEMPHIS_MENUS
|
|
while (w && (pItemWalk->fType & MFT_BITMAP) && (pItemWalk->hTypeData < (HANDLE)MENUHBM_MAX))
|
|
|
|
#endif // MEMPHIS_MENUS
|
|
{
|
|
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->hheapDesktop,
|
|
(pMenu->cAlloced + CMENUITEMALLOC) * sizeof(ITEM));
|
|
if (pNewItems) {
|
|
RtlCopyMemory(pNewItems, pMenu->rgItems,
|
|
pMenu->cAlloced * sizeof(ITEM));
|
|
#ifdef DEBUG
|
|
if (gfTrackLocks) {
|
|
RelocateMenuLockRecords(pNewItems, pMenu->cItems,
|
|
((PBYTE)pNewItems) - (PBYTE)(pMenu->rgItems));
|
|
}
|
|
#endif
|
|
DesktopFree(pMenu->head.rpdesk->hheapDesktop, pMenu->rgItems);
|
|
}
|
|
} else {
|
|
pNewItems = (PITEM)DesktopAlloc(pMenu->head.rpdesk->hheapDesktop,
|
|
sizeof(ITEM) * CMENUITEMALLOC);
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
pMenu->cItems++;
|
|
|
|
if (pItem != NULL) {
|
|
// Move this item up to make room for the one we want to insert.
|
|
memmove(pItem + 1, pItem, (pMenu->cItems - 1) *
|
|
sizeof(ITEM) - ((char *)pItem - (char *)pMenu->rgItems));
|
|
#ifdef DEBUG
|
|
if (gfTrackLocks) {
|
|
RelocateMenuLockRecords(pItem + 1,
|
|
&(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->hTypeData = NULL;
|
|
pItem->cch = 0;
|
|
pItem->dwItemData = 0;
|
|
pItem->xItem = 0;
|
|
pItem->yItem = 0;
|
|
pItem->cxItem = 0;
|
|
pItem->cyItem = 0;
|
|
#ifdef MEMPHIS_MENUS
|
|
pItem->hbmp = NULL;
|
|
pItem->cxBmp = -1;
|
|
pItem->cyBmp = -1;
|
|
pItem->lpstr = NULL;
|
|
if (ppopup = MNCheckMenuUp(pMenu))
|
|
{
|
|
// this menu is currently being displayed -- increment the selection
|
|
// pos if it is after the point of insertion
|
|
if (ppopup->posSelectedItem >=
|
|
(int) ((pItem - pMenu->rgItems) / sizeof(ITEM)) )
|
|
ppopup->posSelectedItem++;
|
|
}
|
|
ThreadLock(pMenu, &tlMenu);
|
|
if (!xxxSetLPITEMInfo(pMenu, pItem, lpmii, pstrItem)) {
|
|
#else
|
|
if (!SetLPITEMInfo(pMenu, pItem, lpmii, pstrItem)) {
|
|
#endif // MEMPHIS_MENUS
|
|
MNFreeItem(pMenu, pItem, TRUE);
|
|
|
|
|
|
// Move things up since we removed/deleted the item
|
|
memmove(pItem, pItem + 1, pMenu->cItems * (UINT)sizeof(ITEM) +
|
|
(UINT)((char *)&pMenu->rgItems[0] - (char *)(pItem + 1)));
|
|
#ifdef DEBUG
|
|
if (gfTrackLocks) {
|
|
RelocateMenuLockRecords(pItem,
|
|
&(pMenu->rgItems[pMenu->cItems - 1]) - pItem,
|
|
-(int)sizeof(ITEM));
|
|
}
|
|
#endif
|
|
pMenu->cItems--;
|
|
#ifdef MEMPHIS_MENUS
|
|
ThreadUnlock(&tlMenu);
|
|
#endif // MEMPHIS_MENUS
|
|
return(FALSE);
|
|
}
|
|
|
|
#ifdef MEMPHIS_MENUS
|
|
ThreadUnlock(&tlMenu);
|
|
#endif // MEMPHIS_MENUS
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
void _SetMenuItemInfoStruct(
|
|
UINT wFlags,
|
|
UINT wIDNew,
|
|
LPCWSTR lpszNew,
|
|
LPMENUITEMINFO pmii,
|
|
PUNICODE_STRING pstr)
|
|
{
|
|
#ifdef MEMPHIS_MENUS
|
|
pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
|
|
#else // MEMPHIS_MENUS
|
|
pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
|
|
|
|
#endif // MEMPHIS_MENUS
|
|
if (wFlags & MF_POPUP) {
|
|
pmii->fMask |= MIIM_SUBMENU;
|
|
pmii->hSubMenu = (HMENU)wIDNew;
|
|
}
|
|
|
|
if (wFlags & MF_OWNERDRAW) {
|
|
pmii->fMask |= MIIM_DATA;
|
|
pmii->dwItemData = (DWORD) lpszNew;
|
|
}
|
|
|
|
pmii->fState = wFlags & MFS_OLDAPI_MASK;
|
|
pmii->fType = wFlags & MFT_OLDAPI_MASK;
|
|
pmii->wID = wIDNew;
|
|
pmii->dwTypeData = (LPWSTR) lpszNew;
|
|
#ifdef MEMPHIS_MENUS
|
|
if (wFlags & MIIM_STRING)
|
|
#else // MEMPHIS_MENUS
|
|
if (!(pmii->fType & MFT_NONSTRING))
|
|
#endif // MEMPHIS_MENUS
|
|
RtlInitUnicodeString(pstr, lpszNew);
|
|
}
|
|
|
|
#ifndef MEMPHIS_MENUS
|
|
void FreeTypeData(PMENU pMenu, PITEM pItem)
|
|
{
|
|
// Free up hItem unless it's a bitmap handle or nonexistent.
|
|
// Apps are responsible for freeing their bitmaps.
|
|
if (!TestMFT(pItem, MFT_NONSTRING) && (pItem->hTypeData != NULL))
|
|
DesktopFree(pMenu->head.rpdesk->hheapDesktop, pItem->hTypeData);
|
|
|
|
if (TestMFT(pItem, MFT_BITMAP)) {
|
|
/*
|
|
* Assign ownership of the bitmap to the process that is
|
|
* destroying the menu to ensure that bitmap will
|
|
* eventually be destroyed.
|
|
*/
|
|
GreSetBitmapOwner((HBITMAP)(pItem->hTypeData), OBJECT_OWNER_CURRENT);
|
|
}
|
|
|
|
// Zap this pointer in case we try to free or reference it again
|
|
pItem->hTypeData = NULL;
|
|
}
|
|
#else // MEMPHIS_MENUS
|
|
void FreeItemBitmap(PMENU pMenu, PITEM pItem)
|
|
{
|
|
// Free up hItem unless it's a bitmap handle or nonexistent.
|
|
// Apps are responsible for freeing their bitmaps.
|
|
if (pItem->hbmp) {
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
void FreeItemString(PMENU pMenu, PITEM pItem)
|
|
{
|
|
// Free up Item's string
|
|
if ((pItem->lpstr != NULL)) {
|
|
DesktopFree(pMenu->head.rpdesk->hheapDesktop, pItem->lpstr);
|
|
}
|
|
// Zap this pointer in case we try to free or reference it again
|
|
pItem->lpstr = NULL;
|
|
}
|
|
|
|
#endif // MEMPHIS_MENUS
|
|
void NEAR FreePopup(PITEM pItem)
|
|
{
|
|
if (pItem->spSubMenu != NULL) {
|
|
_DestroyMenu(pItem->spSubMenu);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* 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)
|
|
{
|
|
#ifdef MEMPHIS_MENUS
|
|
FreeItemBitmap(pMenu, pItem);
|
|
FreeItemString(pMenu, pItem);
|
|
#else // MEMPHIS_MENUS
|
|
FreeTypeData(pMenu, pItem);
|
|
#endif //MEMPHIS_MENUS
|
|
|
|
if (fFreeItemPopup)
|
|
FreePopup(pItem);
|
|
|
|
/*
|
|
* This'll work: the item didn't go away if it was locked.
|
|
*/
|
|
Unlock(&pItem->spSubMenu);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* 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:
|
|
\***************************************************************************/
|
|
|
|
#ifdef MEMPHIS_MENUS
|
|
BOOL xxxRemoveDeleteMenuHelper(
|
|
#else
|
|
BOOL RemoveDeleteMenuHelper(
|
|
#endif // MEMPHIS_MENUS
|
|
PMENU pMenu,
|
|
UINT nPosition,
|
|
DWORD wFlags,
|
|
BOOL fDeleteMenu)
|
|
{
|
|
PITEM pItem;
|
|
PITEM pNewItems;
|
|
PMENU pMenuSave;
|
|
|
|
#ifdef MEMPHIS_MENUS
|
|
PPOPUPMENU ppopup;
|
|
UINT uiPos = 0;
|
|
|
|
CheckLock(pMenu);
|
|
#endif // MEMPHIS_MENUS
|
|
|
|
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;
|
|
}
|
|
|
|
#ifdef MEMPHIS_MENUS
|
|
if (ppopup = MNCheckMenuUp(pMenu))
|
|
{
|
|
// this menu is currently being displayed -- decrement the selection
|
|
// pos if it is after the point of deletion; clear selection pos if it
|
|
// is AT the point of deletion
|
|
uiPos = ((pItem - pMenu->rgItems) / sizeof(ITEM));
|
|
if (ppopup->posSelectedItem == uiPos)
|
|
ppopup->posSelectedItem = 0xffff;// MI_NONE;
|
|
else if (ppopup->posSelectedItem > uiPos)
|
|
ppopup->posSelectedItem--;
|
|
}
|
|
#endif // MEMPHIS_MENUS
|
|
MNFreeItem(pMenu, pItem, fDeleteMenu);
|
|
|
|
/*
|
|
* Reset the menu size so that it gets recomputed next time.
|
|
*/
|
|
pMenu->cyMenu = pMenu->cxMenu = 0;
|
|
|
|
/*
|
|
* Chicago removed the counterpart to this line (hMenu->hwndNotify = 0)
|
|
* We probably still need for deref
|
|
*/
|
|
Unlock(&pMenu->spwndNotify);
|
|
|
|
if (pMenu->cItems == 1) {
|
|
DesktopFree(pMenu->head.rpdesk->hheapDesktop, pMenu->rgItems);
|
|
pMenu->cAlloced = 0;
|
|
pNewItems = NULL;
|
|
} else {
|
|
|
|
/*
|
|
* Move things up since we removed/deleted the item
|
|
*/
|
|
|
|
memmove(pItem, pItem + 1, pMenu->cItems * (UINT)sizeof(ITEM) +
|
|
(UINT)((char *)&pMenu->rgItems[0] - (char *)(pItem + 1)));
|
|
#ifdef DEBUG
|
|
if (gfTrackLocks) {
|
|
RelocateMenuLockRecords(pItem,
|
|
&(pMenu->rgItems[pMenu->cItems - 1]) - pItem,
|
|
-(int)sizeof(ITEM));
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* We're shrinking so if localalloc fails, just leave the mem as is.
|
|
*/
|
|
if (pMenu->cItems <= (pMenu->cAlloced - CMENUITEMDEALLOC + 1)) {
|
|
pNewItems = (PITEM)DesktopAlloc(pMenu->head.rpdesk->hheapDesktop,
|
|
(pMenu->cAlloced - CMENUITEMDEALLOC) * sizeof(ITEM));
|
|
if (pNewItems != NULL) {
|
|
|
|
RtlCopyMemory(pNewItems, pMenu->rgItems,
|
|
(pMenu->cAlloced - CMENUITEMDEALLOC) * sizeof(ITEM));
|
|
#ifdef DEBUG
|
|
if (gfTrackLocks) {
|
|
RelocateMenuLockRecords(pNewItems, pMenu->cItems - 1,
|
|
((PBYTE)pNewItems) - (PBYTE)(pMenu->rgItems));
|
|
}
|
|
#endif
|
|
DesktopFree(pMenu->head.rpdesk->hheapDesktop, pMenu->rgItems);
|
|
pMenu->cAlloced -= CMENUITEMDEALLOC;
|
|
} else
|
|
pNewItems = pMenu->rgItems;
|
|
} else
|
|
pNewItems = pMenu->rgItems;
|
|
}
|
|
|
|
pMenu->rgItems = pNewItems;
|
|
pMenu->cItems--;
|
|
|
|
#ifdef MEMPHIS_MENUS
|
|
if (ppopup) {
|
|
// this menu is currently being displayed -- redisplay the menu with
|
|
// this item removed
|
|
xxxMNUpdateShownMenu(ppopup, pMenu->rgItems + uiPos, TRUE);
|
|
}
|
|
#endif // MEMPHIS_MENUS
|
|
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:
|
|
\***************************************************************************/
|
|
|
|
#ifdef MEMPHIS_MENUS
|
|
BOOL xxxRemoveMenu(
|
|
#else
|
|
BOOL _RemoveMenu(
|
|
#endif // MEMPHIS_MENUS
|
|
PMENU pMenu,
|
|
UINT nPosition,
|
|
UINT wFlags)
|
|
{
|
|
#ifdef MEMPHIS_MENUS
|
|
TL tlpMenu;
|
|
BOOL fReturn = FALSE;
|
|
CheckLock(pMenu);
|
|
ThreadLock(pMenu, &tlpMenu);
|
|
fReturn = xxxRemoveDeleteMenuHelper(pMenu, nPosition, wFlags, FALSE);
|
|
ThreadUnlock(&tlpMenu);
|
|
return fReturn;
|
|
#else
|
|
return RemoveDeleteMenuHelper(pMenu, nPosition, wFlags, FALSE);
|
|
#endif // MEMPHIS_MENUS
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DeleteMenu
|
|
*
|
|
* Deletes an item. ie. Removes it and recovers the memory used by it.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
#ifdef MEMPHIS_MENUS
|
|
BOOL xxxDeleteMenu(
|
|
#else
|
|
BOOL _DeleteMenu(
|
|
#endif // MEMPHIS_MENUS
|
|
PMENU pMenu,
|
|
UINT nPosition,
|
|
UINT wFlags)
|
|
{
|
|
PMENU pSystemMenu;
|
|
#ifdef MEMPHIS_MENUS
|
|
CheckLock(pMenu);
|
|
#endif // MEMPHIS_MENUS
|
|
|
|
// Nasty Hack to fix MSVC 1.5, they remove the close item
|
|
// from their system menu, so we prevent them from doing
|
|
// this and act like nothing happened ( ie return TRUE )
|
|
// Bug#8154, [t-arthb]
|
|
|
|
if ( nPosition == 6 &&
|
|
(wFlags & MF_BYPOSITION) &&
|
|
(pMenu->spwndNotify) &&
|
|
! TestWF( pMenu->spwndNotify, WFWIN40COMPAT ) &&
|
|
TestWF( pMenu->spwndNotify, WEFMDICHILD ) &&
|
|
(pSystemMenu = GetSysMenuHandle(pMenu->spwndNotify)) &&
|
|
(pSystemMenu->rgItems[0].spSubMenu == pMenu) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef MEMPHIS_MENUS
|
|
return xxxRemoveDeleteMenuHelper(pMenu, nPosition, wFlags, TRUE);
|
|
#else
|
|
return RemoveDeleteMenuHelper(pMenu, nPosition, wFlags, TRUE);
|
|
#endif // MEMPHIS_MENUS
|
|
}
|
|
|
|
#ifdef MEMPHIS_MENUS
|
|
BOOL NEAR xxxSetLPITEMInfo(
|
|
PMENU pMenu,
|
|
PITEM pItem,
|
|
LPMENUITEMINFOW lpmii,
|
|
PUNICODE_STRING pstrItem)
|
|
{
|
|
|
|
HANDLE hstr;
|
|
UINT cch;
|
|
BOOL fRecompute = FALSE;
|
|
BOOL fRedraw = FALSE;
|
|
PPOPUPMENU ppopup;
|
|
// TL tlpwndNotify;
|
|
|
|
CheckLock(pMenu);
|
|
if ( lpmii->fMask & MIIM_FTYPE ) {
|
|
UINT fType = lpmii->fType;
|
|
pItem->fType &= ~MFT_MASK;
|
|
pItem->fType |= fType;
|
|
if ( fType& MFT_SEPARATOR ) {
|
|
pItem->fState |= MFS_DISABLED ;
|
|
}
|
|
fRecompute = TRUE;
|
|
}
|
|
|
|
if ( lpmii->fMask & MIIM_STRING ) {
|
|
if (pstrItem->Length != 0) {
|
|
hstr = (HANDLE)DesktopAlloc(pMenu->head.rpdesk->hheapDesktop,
|
|
pstrItem->MaximumLength);
|
|
|
|
if (hstr == NULL)
|
|
return FALSE;
|
|
try {
|
|
RtlCopyMemory(hstr, pstrItem->Buffer, pstrItem->MaximumLength);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
DesktopFree(pMenu->head.rpdesk->hheapDesktop, hstr);
|
|
return FALSE;
|
|
}
|
|
cch = pstrItem->Length / sizeof(WCHAR);
|
|
} else {
|
|
cch = 0;
|
|
hstr = NULL;
|
|
}
|
|
FreeItemString(pMenu,pItem);
|
|
pItem->cch = cch;
|
|
pItem->lpstr = hstr;
|
|
fRecompute = TRUE;
|
|
fRedraw = TRUE;
|
|
}
|
|
|
|
if ( lpmii->fMask & MIIM_BITMAP ) {
|
|
FreeItemBitmap(pMenu, pItem);
|
|
pItem->hbmp = lpmii->hbmpItem;
|
|
pItem->cxBmp =-1;
|
|
pItem->cyBmp =-1;
|
|
fRecompute = TRUE;
|
|
fRedraw = TRUE;
|
|
}
|
|
|
|
if (lpmii->fMask & MIIM_ID)
|
|
pItem->wID = lpmii->wID;
|
|
|
|
if (lpmii->fMask & MIIM_DATA)
|
|
pItem->dwItemData = lpmii->dwItemData;
|
|
|
|
if (lpmii->fMask & MIIM_STATE) {
|
|
pItem->fState = lpmii->fState | (pItem->fState & (MFS_HILITE | MFS_DEFAULT));
|
|
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) {
|
|
FreePopup(pItem);
|
|
}
|
|
if (pSubMenu!=NULL) {
|
|
Lock(&(pItem->spSubMenu), pSubMenu);
|
|
SetMF(pItem->spSubMenu, MFISPOPUP);
|
|
} else
|
|
Unlock(&(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 && !pItem->hbmp ){
|
|
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;
|
|
|
|
/*
|
|
* Chicago removed the counterpart to this line (hMenu->hwndNotify = 0)
|
|
* We probably still need for deref
|
|
*/
|
|
if ( fRedraw ) {
|
|
if (ppopup = MNCheckMenuUp(pMenu)) {
|
|
// this menu is currently being displayed -- redisplay the menu,
|
|
// recomputing if necessary
|
|
xxxMNUpdateShownMenu(ppopup, pItem, FALSE);
|
|
}
|
|
}
|
|
//Unlock(&pMenu->spwndNotify);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
#else // MEMPHIS_MENUS
|
|
BOOL NEAR SetLPITEMInfo(
|
|
PMENU pMenu,
|
|
PITEM pItem,
|
|
LPMENUITEMINFOW lpmii,
|
|
PUNICODE_STRING pstrItem)
|
|
{
|
|
|
|
if (lpmii->fMask & MIIM_TYPE) {
|
|
HANDLE hstr;
|
|
UINT cch;
|
|
UINT fType = lpmii->fType;
|
|
|
|
if (!(fType & MFT_NONSTRING)) {
|
|
if (lpmii->dwTypeData == 0)
|
|
fType = MFT_SEPARATOR;
|
|
else if (pstrItem->Length != 0) {
|
|
hstr = (HANDLE)DesktopAlloc(pMenu->head.rpdesk->hheapDesktop,
|
|
pstrItem->MaximumLength);
|
|
|
|
if (hstr == NULL)
|
|
return FALSE;
|
|
try {
|
|
RtlCopyMemory(hstr, pstrItem->Buffer, pstrItem->MaximumLength);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
DesktopFree(pMenu->head.rpdesk->hheapDesktop, hstr);
|
|
return FALSE;
|
|
}
|
|
cch = pstrItem->Length / sizeof(WCHAR);
|
|
} else {
|
|
cch = 0;
|
|
hstr = NULL;
|
|
}
|
|
}
|
|
|
|
FreeTypeData(pMenu, pItem);
|
|
pItem->fType &= ~MFT_MASK;
|
|
pItem->fType |= fType;
|
|
|
|
if (fType & MFT_NONSTRING) {
|
|
if (fType & MFT_BITMAP)
|
|
pItem->hTypeData = lpmii->dwTypeData;
|
|
else {
|
|
if (fType & MFT_SEPARATOR)
|
|
pItem->fState |= MFS_DISABLED;
|
|
|
|
// if MFT_OWNERDRAW, LEAVE lpItem->dwTypeData ALONE !!
|
|
if ( !(fType & MFT_OWNERDRAW ) )
|
|
pItem->hTypeData = NULL;
|
|
}
|
|
|
|
pItem->cch = 0;
|
|
} else {
|
|
pItem->cch = cch;
|
|
pItem->hTypeData = hstr;
|
|
}
|
|
|
|
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;
|
|
|
|
/*
|
|
* Chicago removed the counterpart to this line (hMenu->hwndNotify = 0)
|
|
* We probably still need for deref
|
|
*/
|
|
Unlock(&pMenu->spwndNotify);
|
|
}
|
|
|
|
if (lpmii->fMask & MIIM_ID)
|
|
pItem->wID = lpmii->wID;
|
|
|
|
if (lpmii->fMask & MIIM_DATA)
|
|
pItem->dwItemData = lpmii->dwItemData;
|
|
|
|
if (lpmii->fMask & MIIM_STATE) {
|
|
pItem->fState = lpmii->fState | (pItem->fState & (MFS_HILITE | MFS_DEFAULT));
|
|
if (pItem->fType & MFT_SEPARATOR)
|
|
pItem->fState |= MFS_DISABLED;
|
|
}
|
|
|
|
if (lpmii->fMask & MIIM_CHECKMARKS) {
|
|
pItem->hbmpChecked = lpmii->hbmpChecked;
|
|
pItem->hbmpUnchecked = lpmii->hbmpUnchecked;
|
|
}
|
|
|
|
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) {
|
|
FreePopup(pItem);
|
|
}
|
|
if (pSubMenu!=NULL) {
|
|
Lock(&(pItem->spSubMenu), pSubMenu);
|
|
SetMF(pItem->spSubMenu, MFISPOPUP);
|
|
} else
|
|
Unlock(&(pItem->spSubMenu));
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
#endif // MEMPHIS_MENUS
|
|
|
|
BOOL _SetMenuContextHelpId(PMENU pMenu, DWORD dwContextHelpId)
|
|
{
|
|
|
|
// Set the new context help Id;
|
|
pMenu->dwContextHelpId = dwContextHelpId;
|
|
|
|
return(TRUE); // Success!
|
|
}
|
|
#ifdef MEMPHIS_MENUS
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// MNCheckMenuUp(PMENU)
|
|
//
|
|
// 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
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
PPOPUPMENU MNCheckMenuUp(PMENU pMenu)
|
|
{
|
|
|
|
PTHREADINFO pti = NULL;
|
|
PWND pwnd = NULL;
|
|
PPOPUPMENU ppopup = NULL;
|
|
|
|
// check the hwndNotify's queue to see if it is currently in menu mode.
|
|
// If so, initialize the start condition for the popup search
|
|
// If not, we're not in menu mode, so bail.
|
|
if (!(pMenu->spwndNotify) ||
|
|
!(pti = pMenu->spwndNotify->head.pti) ||
|
|
!pti->MenuState.fInsideMenuLoop ||
|
|
!(ppopup = pti->MenuState.pGlobalPopupMenu->ppopupmenuRoot))
|
|
return(NULL);
|
|
|
|
while (TRUE)
|
|
{
|
|
if (ppopup->spmenu == pMenu)
|
|
// matching ppopup found -- return it
|
|
return(ppopup);
|
|
else if (!(pwnd = ppopup->spwndNextPopup))
|
|
// no more ppopup's found -- no match found -- return NULL
|
|
return(NULL);
|
|
|
|
// set up for next iteration to search next ppopup
|
|
ppopup = (PPOPUPMENU) ((PMENUWND)pwnd)->ppopupmenu;
|
|
}
|
|
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// xxxMNUpdateShownMenu(PPOPUPMENU, LPITEM)
|
|
//
|
|
// updates a given ppopup menu window to reflect the inserting, deleting,
|
|
// or altering of the given lpItem.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
void xxxMNUpdateShownMenu(PPOPUPMENU ppopup, PITEM pItem, BOOL fDelete)
|
|
{
|
|
RECT rc;
|
|
PWND pwnd = ppopup->spwndPopupMenu;
|
|
PMENU pMenu = ppopup->spmenu;
|
|
TL tlpwnd;
|
|
|
|
CopyRect( &rc, &(pwnd->rcClient) );
|
|
|
|
if (!pMenu->cxMenu) {
|
|
RECT rcScroll = rc;
|
|
int cySave = rc.bottom;
|
|
int cxSave = rc.right;
|
|
DWORD dwSize;
|
|
WORD fArrowFlags = 0;
|
|
|
|
/****** Activate after porting scrollable menus
|
|
// necessary evil -- compiler freaks out when I try to do this a bit
|
|
// more directly (i.e. fArrowsOld ^ hMenu->fArrowsOn) -- so I had to
|
|
// use this approach -- can cleanup in port to NT -- jeffbog 11/02/95
|
|
if (pMenu->fArrowsOn)
|
|
fArrowFlags = 1; */
|
|
|
|
ThreadLock(pwnd, &tlpwnd);
|
|
dwSize = xxxSendMessage(pwnd, MN_SIZEWINDOW, 1, 0L);
|
|
ThreadUnlock(&tlpwnd);
|
|
/*
|
|
if (hMenu->fArrowsOn)
|
|
fArrowFlags |= 2;
|
|
if ((fArrowFlags == 1) || (fArrowFlags == 2)) {
|
|
// scroll arrows appeared or disappeared -- redraw entire client
|
|
InvalidateRect(hwnd, NULL, TRUE);
|
|
return;
|
|
}
|
|
*/
|
|
|
|
rc.right = LOWORD(dwSize);
|
|
if (pItem) {
|
|
if (rc.right != cxSave) {
|
|
// width changed, redraw everything
|
|
// BUGBUG -- this could be tuned to just redraw items with
|
|
// submenus and/or accelerator fields
|
|
ThreadLock( pwnd, &tlpwnd);
|
|
xxxInvalidateRect(pwnd, NULL, TRUE);
|
|
ThreadUnlock( &tlpwnd );
|
|
}
|
|
/* Activate after scrollable menus
|
|
else {
|
|
rc.bottom = pMenu->cyMenu;
|
|
if (hMenu->fArrowsOn) {
|
|
if (rc.bottom <= cySave) {
|
|
rc.top = lpItem->yItem - MNTopItem(hMenu)->yItem;
|
|
goto InvalidateRest;
|
|
}
|
|
|
|
GetClientRect(hwnd, &rcScroll);
|
|
}
|
|
|
|
rc.top = rcScroll.top = lpItem->yItem - MNTopItem(hMenu)->yItem;
|
|
if ((rc.top >= 0) && (rc.top < hMenu->cyMenu))
|
|
ScrollWindowEx(hwnd, 0, rc.bottom - cySave, &rcScroll, &rc, NULL, NULL, SW_INVALIDATE | SW_ERASE);
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
if ( pItem && !fDelete) {
|
|
// if the item is not being deleted, then we need to redraw it
|
|
// rc.top = pItem->yItem - MNTopItem(hMenu)->yItem;
|
|
rc.top = pItem->yItem - pMenu->rgItems->yItem;
|
|
rc.bottom = rc.top + pItem->cyItem;
|
|
|
|
//InvalidateRest:
|
|
if ((rc.top >= 0) && (rc.top < (LONG)pMenu->cyMenu)) {
|
|
ThreadLock( pwnd, &tlpwnd);
|
|
xxxInvalidateRect(pwnd, &rc, TRUE);
|
|
ThreadUnlock( &tlpwnd );
|
|
}
|
|
}
|
|
|
|
if ( !pItem && !fDelete) {
|
|
ThreadLock( pwnd, &tlpwnd);
|
|
xxxInvalidateRect(pwnd, NULL, TRUE);
|
|
ThreadUnlock( &tlpwnd );
|
|
}
|
|
}
|
|
#endif // MEMPHIS_MENUS
|