Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

602 lines
17 KiB

/**************************** Module Header ********************************\
* Module Name: mnstate.c
*
* Copyright 1985-90, Microsoft Corporation
*
* Menu State Routines
*
* History:
* 10-10-90 JimA Cleanup.
* 03-18-91 IanJa Windowrevalidation added
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
PMENU GetInitMenuParam(PWND pwndMenu, BOOL * lpfSystem);
/***************************************************************************\
* void PositionSysMenu(pwnd, hSysMenu)
*
*
* History:
* 4-25-91 Mikehar Port for 3.1 merge
\***************************************************************************/
void MNPositionSysMenu(
PWND pwnd,
PMENU pmenusys)
{
RECT rc;
PITEM pItem;
if (pmenusys == NULL) {
RIPERR0(ERROR_INVALID_HANDLE,
RIP_WARNING,
"Invalid menu handle pmenusys (NULL) to MNPositionSysMenu");
return;
}
/*
* Setup the SysMenu hit rectangle.
*/
rc.top = rc.left = 0;
if (TestWF(pwnd, WEFTOOLWINDOW)) {
rc.right = SYSMET(CXSMSIZE);
rc.bottom = SYSMET(CYSMSIZE);
} else {
rc.right = SYSMET(CXSIZE);
rc.bottom = SYSMET(CYSIZE);
}
if (!TestWF(pwnd, WFMINIMIZED)) {
int cBorders;
cBorders = GetWindowBorders(pwnd->style, pwnd->ExStyle, TRUE, FALSE);
OffsetRect(&rc, cBorders*SYSMET(CXBORDER), cBorders*SYSMET(CYBORDER));
}
/*
* Offset the System popup menu.
*/
Lock(&pmenusys->spwndNotify, pwnd);
if (!TestMF(pmenusys, MF_POPUP) && (pmenusys->cItems > 0)) {
pItem = pmenusys->rgItems;
if (pItem) {
pItem->yItem = rc.top;
pItem->xItem = rc.left;
pItem->cyItem = rc.bottom - rc.top;
pItem->cxItem = rc.right - rc.left;
}
}
else
// BOGUS -- MF_POPUP should never be set on a MENU -- only a MENU ITEM
UserAssert(FALSE);
}
/***************************************************************************\
* MNFlushDestroyedPopups
*
* Walk the ppmDelayedFree list freeing those marked as destroyed.
*
* 05-14-96 GerardoB Created
\***************************************************************************/
void MNFlushDestroyedPopups (PPOPUPMENU ppopupmenu, BOOL fUnlock)
{
PPOPUPMENU ppmDestroyed, ppmFree;
UserAssert(IsRootPopupMenu(ppopupmenu));
/*
* Walk ppmDelayedFree
*/
ppmDestroyed = ppopupmenu;
while (ppmDestroyed->ppmDelayedFree != NULL) {
/*
* If it's marked as destroyed, unlink it and free it
*/
if (ppmDestroyed->ppmDelayedFree->fDestroyed) {
ppmFree = ppmDestroyed->ppmDelayedFree;
ppmDestroyed->ppmDelayedFree = ppmFree->ppmDelayedFree;
UserAssert(ppmFree != ppmFree->ppopupmenuRoot);
MNFreePopup(ppmFree);
} else {
/*
* fUnlock is TRUE if the root popup is being destroyed; if
* so, reset fDelayedFree and unlink it
*/
if (fUnlock) {
/*
* This means that the root popup is going away before
* some of the hierarchical popups have been destroyed.
* This can happen if someone destroys one of the menu
* windows breaking the spwndNextPopup chain.
*/
ppmDestroyed->ppmDelayedFree->fDelayedFree = FALSE;
/*
* Stop here so we can figure how this happened.
*/
UserAssert(ppmDestroyed->ppmDelayedFree->fDelayedFree);
ppmDestroyed->ppmDelayedFree = ppmDestroyed->ppmDelayedFree->ppmDelayedFree;
} else {
/*
* Not fDestroyed so move to the next one.
*/
ppmDestroyed = ppmDestroyed->ppmDelayedFree;
} /* fUnlock */
} /* fDestroyed */
} /* while ppmDelayedFree */
}
/***************************************************************************\
*
* Preallocated popupmenu structure -- this allows us to ensure that
* there is memory to pull down a menu, even when USER's heap is full.
*
* History:
* 10-Mar-1992 mikeke
\***************************************************************************/
static POPUPMENU gpopupMenu;
static BOOL gfPopupInUse = FALSE;
static MENUSTATE gpMenuState;
static BOOL gfMenuStateInUse = FALSE;
/***************************************************************************\
* MNAllocPopup
*
\***************************************************************************/
PPOPUPMENU MNAllocPopup(BOOL fForceAlloc)
{
PPOPUPMENU ppm;
if (!fForceAlloc && !gfPopupInUse) {
gfPopupInUse = TRUE;
ppm = &gpopupMenu;
} else {
ppm = (PPOPUPMENU)UserAllocPoolWithQuota(sizeof(POPUPMENU), TAG_POPUPMENU);
}
if (ppm) {
RtlZeroMemory(ppm, sizeof(POPUPMENU));
}
return (ppm);
}
/***************************************************************************\
* MNFreePopup
*
\***************************************************************************/
VOID MNFreePopup(
PPOPUPMENU ppopupmenu)
{
PMENU pmenu;
PMENU pmenuSys;
#ifdef DEBUG
Validateppopupmenu(ppopupmenu);
#endif
if (IsRootPopupMenu(ppopupmenu)) {
MNFlushDestroyedPopups (ppopupmenu, TRUE);
}
/*
* This app is finished using the global system menu: unlock any objects
* it is using!
*
* NOTE: This global system menu thing doesn't work: two apps can use
* it at the same time: which would be a disasterous bug!
*/
if (ppopupmenu->spwndNotify != NULL) {
pmenuSys = ppopupmenu->spwndNotify->head.rpdesk->spmenuSys;
if (pmenuSys != NULL) {
Unlock(&pmenuSys->spwndNotify);
if ((pmenu = _GetSubMenu(pmenuSys, 0)) != NULL)
Unlock(&pmenu->spwndNotify);
}
Unlock(&ppopupmenu->spwndNotify);
}
Unlock(&ppopupmenu->spwndPopupMenu);
Unlock(&ppopupmenu->spwndNextPopup);
Unlock(&ppopupmenu->spwndPrevPopup);
Unlock(&ppopupmenu->spmenu);
Unlock(&ppopupmenu->spmenuAlternate);
Unlock(&ppopupmenu->spwndActivePopup);
#ifdef DEBUG
ppopupmenu->fFreed = TRUE;
#endif
if (ppopupmenu == &gpopupMenu) {
UserAssert(gfPopupInUse);
gfPopupInUse = FALSE;
} else {
UserFreePool(ppopupmenu);
}
}
/***************************************************************************\
* MNEndMenuStateNotify
*
* spwndNotify might have been created by a thread other than the one
* the menu mode is running on. If this is the case, this function
* NULLs out pMenuState for the thread that owns spwndNotify.
*
* 05-21-96 GerardoB Created
\***************************************************************************/
void MNEndMenuStateNotify (PMENUSTATE pMenuState)
{
PTHREADINFO ptiNotify;
if (pMenuState->pGlobalPopupMenu->spwndNotify != NULL) {
ptiNotify = GETPTI(pMenuState->pGlobalPopupMenu->spwndNotify);
if (ptiNotify != pMenuState->ptiMenuStateOwner) {
UserAssert(ptiNotify->pMenuState == pMenuState);
ptiNotify->pMenuState = NULL;
}
}
}
/***************************************************************************\
* MNEndMenuState
*
* This funtion must be called to clean up pMenuState after getting out
* of menu mode. It must be called by the same thread that initialized
* pMenuState either manually or by calling xxxMNStartMenuState.
*
* 05-20-96 GerardoB Created
\***************************************************************************/
void MNEndMenuState (BOOL fFreePopup)
{
PTHREADINFO ptiCurrent;
PMENUSTATE pMenuState;
ptiCurrent = PtiCurrent();
pMenuState = ptiCurrent->pMenuState;
UserAssert(ptiCurrent->pMenuState != NULL);
UserAssert(ptiCurrent == pMenuState->ptiMenuStateOwner);
MNEndMenuStateNotify(pMenuState);
if (fFreePopup) {
UserAssert(pMenuState->pGlobalPopupMenu->fIsMenuBar || pMenuState->pGlobalPopupMenu->fDestroyed);
MNFreePopup(pMenuState->pGlobalPopupMenu);
} else {
/*
* This means that we're ending the menustate but the popup menu
* window is still around. This can happen when called from
* xxxDestroyThreadInfo.
*/
UserAssert(pMenuState->pGlobalPopupMenu->fIsTrackPopup);
pMenuState->pGlobalPopupMenu->fDelayedFree = FALSE;
}
if (pMenuState == &gpMenuState) {
UserAssert(gfMenuStateInUse);
gfMenuStateInUse = FALSE;
} else {
/*
* Don't use UserFreePool so the debug code below will work right
*/
ExFreePool(pMenuState);
}
ptiCurrent->pMenuState = NULL;
/*
* This menu mode is off
*/
UserAssert(guMenuStateCount != 0);
guMenuStateCount--;
#ifdef DEBUG
{
/*
* No pti should point to this pMenuState anymore
* If guMenuStateCount is zero, all pMenuState must be NULL
*/
PLIST_ENTRY pHead, pEntry;
PTHREADINFO ptiT;
pHead = &(ptiCurrent->rpdesk->PtiList);
for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
ptiT = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
UserAssert(ptiT->pMenuState != pMenuState);
if (guMenuStateCount == 0) {
UserAssert(ptiT->pMenuState == NULL);
}
}
}
#endif
}
/***************************************************************************\
* MNAllocMenuState
*
* Allocates and initializes a pMenuState
*
* 5-21-96 GerardoB Created
\***************************************************************************/
PMENUSTATE MNAllocMenuState(PTHREADINFO ptiCurrent, PTHREADINFO ptiNotify, PPOPUPMENU ppopupmenuRoot)
{
PMENUSTATE pMenuState;
UserAssert(PtiCurrent() == ptiCurrent);
UserAssert(ptiCurrent->rpdesk == ptiNotify->rpdesk);
/*
* If gpMenuState is already taken, allocate one.
*/
if (gfMenuStateInUse) {
pMenuState = (PMENUSTATE)UserAllocPoolWithQuota(sizeof(MENUSTATE), TAG_MENUSTATE);
if (pMenuState == NULL) {
return NULL;
}
} else {
gfMenuStateInUse = TRUE;
pMenuState = &gpMenuState;
}
/*
* This is used by IsSomeOneInMenuMode and for debugging purposes
*/
guMenuStateCount++;
/*
* Initialize pMenuState.
*/
RtlZeroMemory(pMenuState, sizeof(*pMenuState));
pMenuState->pGlobalPopupMenu = ppopupmenuRoot;
pMenuState->ptiMenuStateOwner = ptiCurrent;
UserAssert(ptiCurrent->pMenuState == NULL);
ptiCurrent->pMenuState = pMenuState;
if (ptiNotify != ptiCurrent) {
UserAssert(ptiNotify->pMenuState == NULL);
ptiNotify->pMenuState = pMenuState;
}
return pMenuState;
}
/***************************************************************************\
* xxxMNStartMenuState
*
* This function is called when the menu bar is about to be activated (the
* app's main menu). It makes sure that the threads involved are not in
* menu mode already, finds the owner/notification window, initializes
* pMenuState and sends the WM_ENTERMENULOOP message.
* It successful, it returns a pointer to a pMenuState. If so, the caller
* must call MNEndMenuState when done.
*
* History:
* 4-25-91 Mikehar Port for 3.1 merge
* 5-20-96 GerardoB Renamed and changed (Old name: xxxMNGetPopup)
\***************************************************************************/
PMENUSTATE xxxMNStartMenuState(PWND pwnd)
{
PPOPUPMENU ppopupmenu;
PTHREADINFO ptiCurrent, ptiNotify;
PMENUSTATE pMenuState;
TL tlpwnd;
CheckLock(pwnd);
/*
* Bail if the current thread is already in menu mode
*/
ptiCurrent = PtiCurrent();
if (ptiCurrent->pMenuState != NULL) {
return NULL;
}
/*
* If the window doesn't have any children, return pwndActive.
*/
if (!TestwndChild(pwnd)) {
pwnd = GETPTI(pwnd)->pq->spwndActive;
} else {
/*
* Search up the parents for a window with a System Menu.
*/
while (TestwndChild(pwnd)) {
if (TestWF(pwnd, WFSYSMENU))
break;
pwnd = pwnd->spwndParent;
}
}
if (pwnd == NULL) {
return NULL;
}
if (!TestwndChild(pwnd) && (pwnd->spmenu != NULL)) {
goto hasmenu;
}
if (!TestWF(pwnd, WFSYSMENU)) {
return NULL;
}
hasmenu:
/*
* If the owner/notification window was created by another thread,
* make sure that it's not in menu mode already
* This can happen if PtiCurrent() is attached to other threads, one of
* which created pwnd.
*/
ptiNotify = GETPTI(pwnd);
if (ptiNotify->pMenuState != NULL) {
return NULL;
}
/*
* Allocate ppoupmenu and pMenuState
*/
ppopupmenu = MNAllocPopup(FALSE);
if (ppopupmenu == NULL) {
return NULL;
}
pMenuState = MNAllocMenuState(ptiCurrent, ptiNotify, ppopupmenu);
if (pMenuState == NULL) {
MNFreePopup(ppopupmenu);
return NULL;
}
ppopupmenu->fIsMenuBar = TRUE;
ppopupmenu->fHasMenuBar = TRUE;
Lock(&(ppopupmenu->spwndNotify), pwnd);
ppopupmenu->posSelectedItem = MFMWFP_NOITEM;
Lock(&(ppopupmenu->spwndPopupMenu), pwnd);
ppopupmenu->ppopupmenuRoot = ppopupmenu;
/*
* Notify the app we are entering menu mode. wParam is always 0 since this
* procedure will only be called for menu bar menus not TrackPopupMenu
* menus.
*/
ThreadLockAlways(pwnd, &tlpwnd);
xxxSendMessage(pwnd, WM_ENTERMENULOOP, 0, 0L);
ThreadUnlock(&tlpwnd);
return pMenuState;
}
/***************************************************************************\
* xxxStartMenuState
*
* Note that this function calls back many times so we might be forced
* out of menu mode at any time. We don't want to check this after
* each callback so we lock what we need and go on. Be careful.
*
* History:
* 4-25-91 Mikehar Port for 3.1 merge
\***************************************************************************/
BOOL xxxMNStartState(
PPOPUPMENU ppopupmenu,
int mn)
{
PWND pwndMenu;
PMENUSTATE pMenuState;
TL tlpwndMenu;
TL tlpmenu;
UserAssert(IsRootPopupMenu(ppopupmenu));
if (ppopupmenu->fDestroyed) {
return FALSE;
}
pwndMenu = ppopupmenu->spwndNotify;
ThreadLock(pwndMenu, &tlpwndMenu);
pMenuState = GetpMenuState(pwndMenu);
if (pMenuState == NULL) {
RIPMSG0(RIP_ERROR, "xxxMNStartState: pMenuState == NULL");
return FALSE;
}
pMenuState->mnFocus = mn;
pMenuState->fMenuStarted = TRUE;
pMenuState->fButtonDown = FALSE;
/*
* Lotus Freelance demo programs depend on GetCapture returning their hwnd
* when in menumode.
*/
xxxCapture(PtiCurrent(), ppopupmenu->spwndNotify, SCREEN_CAPTURE);
xxxSendMessage(pwndMenu, WM_SETCURSOR, (DWORD)HWq(pwndMenu),
MAKELONG(MSGF_MENU, 0));
if (ppopupmenu->fIsMenuBar) {
BOOL fSystemMenu;
PMENU pMenu;
pMenu = GetInitMenuParam(pwndMenu, &fSystemMenu);
if (pMenu == NULL)
{
pMenuState->fMenuStarted = FALSE;
xxxSetCapture(NULL);
ThreadUnlock(&tlpwndMenu);
return(FALSE);
}
Lock(&(ppopupmenu->spmenu), pMenu);
ppopupmenu->fIsSysMenu = (fSystemMenu != 0);
if (!fSystemMenu)
Lock(&(ppopupmenu->spmenuAlternate), GetSysMenu(pwndMenu, FALSE));
}
pMenuState->fIsSysMenu = (ppopupmenu->fIsSysMenu != 0);
if (!ppopupmenu->fNoNotify) {
PMENU pMenu;
if (ppopupmenu->fIsTrackPopup && ppopupmenu->fIsSysMenu)
pMenu = GetInitMenuParam(pwndMenu, NULL);
else
pMenu = ppopupmenu->spmenu;
xxxSendMessage(pwndMenu, WM_INITMENU, (DWORD)PtoH(pMenu), 0L);
}
if (!ppopupmenu->fIsTrackPopup)
{
if (ppopupmenu->fIsSysMenu) {
MNPositionSysMenu(pwndMenu, ppopupmenu->spmenu);
} else if (ppopupmenu->fIsMenuBar)
{
ThreadLock(ppopupmenu->spmenu, &tlpmenu);
xxxMNRecomputeBarIfNeeded(pwndMenu, ppopupmenu->spmenu);
ThreadUnlock(&tlpmenu);
MNPositionSysMenu(pwndMenu, ppopupmenu->spmenuAlternate);
}
}
ThreadUnlock(&tlpwndMenu);
return !ppopupmenu->fDestroyed;
}
// --------------------------------------------------------------------------
//
// GetInitMenuParam()
//
// Gets the HMENU sent as the wParam of WM_INITMENU, and for menu bars, is
// the actual menu to be interacted with.
//
// --------------------------------------------------------------------------
PMENU GetInitMenuParam(PWND pwndMenu, BOOL * lpfSystem)
{
//
// Find out what menu we should be sending in WM_INITMENU:
// If minimized/child/empty menubar, use system menu
//
if (TestWF(pwndMenu, WFMINIMIZED) ||
TestwndChild(pwndMenu) ||
(pwndMenu->spmenu == NULL) ||
!pwndMenu->spmenu->cItems)
{
if (lpfSystem != NULL)
*lpfSystem = TRUE;
return(GetSysMenu(pwndMenu, FALSE));
}
else
{
if (lpfSystem != NULL)
*lpfSystem = FALSE;
return(pwndMenu->spmenu);
}
}