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.
 
 
 
 
 
 

873 lines
30 KiB

/**************************** Module Header ********************************\
* Module Name: mnloop.c
*
* Copyright 1985-90, Microsoft Corporation
*
* Menu Modal Loop Routines
*
* History:
* 10-10-90 JimA Cleanup.
* 03-18-91 IanJa Window revalidation added
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
/***************************************************************************\
* xxxHandleMenuMessages
*
* History:
\***************************************************************************/
void xxxHandleMenuMessages(
LPMSG lpmsg,
PMENUSTATE pMenuState,
PPOPUPMENU ppopupmenu)
{
DWORD ch;
MSG msg;
UINT cmdHitArea;
UINT cmdItem;
LONG lParam;
BOOL fThreadLock = FALSE;
TL tlpwndHitArea;
TL tlpwndT;
UINT msgRightButton;
/*
* Get things out of the structure so that we can access them quicker.
*/
ch = lpmsg->wParam;
lParam = lpmsg->lParam;
/*
* In this switch statement, we only look at messages we want to handle and
* swallow. Messages we don't understand will get translated and
* dispatched.
*/
switch (lpmsg->message) {
case WM_RBUTTONDOWN:
case WM_NCRBUTTONDOWN:
if (ppopupmenu->fRightButton)
goto HandleButtonDown;
if (pMenuState->fButtonDown) {
msgRightButton = WM_RBUTTONDOWN;
EatRightButtons:
if (xxxPeekMessage(&msg, NULL, 0, 0, PM_NOYIELD) &&
(msg.message == msgRightButton)) {
xxxPeekMessage(&msg, NULL, msgRightButton, msgRightButton,
PM_REMOVE);
}
return;
}
if (xxxPeekMessage(&msg, NULL, 0, 0, PM_NOYIELD) &&
(msg.message == lpmsg->message))
xxxPeekMessage(&msg, NULL, lpmsg->message, lpmsg->message, PM_REMOVE);
else
return;
break;
case WM_LBUTTONDOWN:
case WM_NCLBUTTONDOWN:
// Commented out due to TandyT whinings...
// if ((ppopupmenu->trackPopupMenuFlags & TPM_RIGHTBUTTON))
// break;
HandleButtonDown:
/*
* Find out where this mouse down occurred.
*/
pMenuState->mnFocus = MOUSEHOLD;
pMenuState->ptMouseLast.x = LOWORD(lParam);
pMenuState->ptMouseLast.y = HIWORD(lParam);
cmdHitArea = xxxMNFindWindowFromPoint(ppopupmenu, &cmdItem,
MAKEPOINTS(lParam));
/*
* Thread lock this if it is a pwnd. This certainly isn't the way
* you'd implement this if you had locking to begin with.
*/
switch(cmdHitArea) {
case MFMWFP_OFFMENU:
case MFMWFP_MAINMENU:
case MFMWFP_NOITEM:
case MFMWFP_ALTMENU:
fThreadLock = FALSE;
break;
default:
ThreadLock((PWND)cmdHitArea, &tlpwndHitArea);
fThreadLock = TRUE;
break;
}
if ((cmdHitArea == MFMWFP_OFFMENU) && (cmdItem == 0)) {
//
// Clicked in middle of nowhere, so terminate menus, and
// let button pass through.
CancelOut:
xxxMNCancel(ppopupmenu, 0, FALSE, 0);
goto Unlock;
} else if (ppopupmenu->fHasMenuBar && (cmdHitArea == MFMWFP_ALTMENU)) {
//
// Switching between menu bar & popup
//
xxxMNSwitchToAlternateMenu(ppopupmenu);
cmdHitArea = MFMWFP_NOITEM;
}
if (cmdHitArea == MFMWFP_NOITEM) {
//
// On menu bar (system or main)
//
xxxMNButtonDown(ppopupmenu, pMenuState, cmdItem, TRUE);
} else {
// On popup window menu
UserAssert(cmdHitArea);
xxxSendMessage((PWND)cmdHitArea, MN_BUTTONDOWN, cmdItem, 0L);
}
/*
* Swallow the message since we handled it.
*/
/*
* The excel guys change a wm_rbuttondown to a wm_lbuttondown message
* in their message filter hook. Remove the message here or we'll
* get in a nasty loop.
*
* We need to swallow msg32.message ONLY. It is possible for
* the LBUTTONDOWN to not be at the head of the input queue.
* If not, we will swallow a WM_MOUSEMOVE or something else like
* that. The reason Peek() doesn't need to check the range
* is because we've already Peek(PM_NOYIELD'ed) before, which
* locked the sys queue.
*/
if (xxxPeekMessage(&msg, NULL, 0, 0, PM_NOYIELD) &&
((msg.message == lpmsg->message) || (msg.message == WM_RBUTTONDOWN))) {
xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
}
goto Unlock;
case WM_MOUSEMOVE:
case WM_NCMOUSEMOVE:
xxxMNMouseMove(ppopupmenu, pMenuState, MAKEPOINTS(lParam));
return;
case WM_RBUTTONUP:
case WM_NCRBUTTONUP:
if (ppopupmenu->fRightButton)
goto HandleButtonUp;
if (pMenuState->fButtonDown) {
msgRightButton = WM_RBUTTONUP;
goto EatRightButtons;
}
#ifdef MEMPHIS_MENUS
// New feature for shell start menu -- notify when a right click
// occurs on a menu item, and open a window of opportunity for
// menus to recurse, allowing them to popup a context-sensitive
// menu for that item. (jeffbog 9/28/95)
//
// BUGBUG: need to add check for Nashville+ app
if ((lpmsg->message == WM_RBUTTONUP) && !ppopupmenu->fNoNotify) {
PPOPUPMENU ppopupActive;
if ( ppopupmenu->spwndActivePopup &&
( ppopupActive = ((PMENUWND)(ppopupmenu->spwndActivePopup))->ppopupmenu) &&
MNISITEMSELECTED(ppopupActive))
{
TL tlpwndNotify;
ThreadLock( ppopupActive->spwndNotify, &tlpwndNotify );
xxxSendMessage(ppopupActive->spwndNotify, WM_MENURBUTTONUP,
MNSELECTEDITEM(ppopupActive)->wID, lParam);
ThreadUnlock( &tlpwndNotify );
}
}
#endif // MEMPHIS_MENUS
break;
case WM_LBUTTONUP:
case WM_NCLBUTTONUP:
// Commented out due to TandyT whinings...
// if ((ppopupmenu->trackPopupMenuFlags & TPM_RIGHTBUTTON))
// break;
HandleButtonUp:
if (!pMenuState->fButtonDown) {
/*
* Don't care about this mouse up since we never saw the button
* down for some reason.
*/
return;
}
/*
* Find out where this mouse up occurred.
*/
pMenuState->ptMouseLast.x = LOWORD(lParam);
pMenuState->ptMouseLast.y = HIWORD(lParam);
cmdHitArea = xxxMNFindWindowFromPoint(ppopupmenu, &cmdItem,
MAKEPOINTS(lParam));
/*
* Thread lock this if it is a pwnd. This certainly isn't the way
* you'd implement this if you had locking to begin with.
*/
switch(cmdHitArea) {
case MFMWFP_OFFMENU:
case MFMWFP_MAINMENU:
case MFMWFP_NOITEM:
case MFMWFP_ALTMENU:
fThreadLock = FALSE;
break;
default:
ThreadLock((PWND)cmdHitArea, &tlpwndHitArea);
fThreadLock = TRUE;
break;
}
if (ppopupmenu->fHasMenuBar) {
if (((cmdHitArea == MFMWFP_OFFMENU) && (cmdItem == 0)) ||
((cmdHitArea == MFMWFP_NOITEM) && ppopupmenu->fIsSysMenu && ppopupmenu->fToggle))
// Button up occurred in some random spot. Terminate
// menus and swallow the message.
goto CancelOut;
} else {
if ((cmdHitArea == MFMWFP_OFFMENU) && (cmdItem == 0)) {
if (!ppopupmenu->fFirstClick) {
//
// User upclicked in some random spot. Terminate
// menus and don't swallow the message.
//
//
// Don't do anything with HWND here cuz the window is
// destroyed after this SendMessage().
//
// DONTREVALIDATE();
ThreadLock(ppopupmenu->spwndPopupMenu, &tlpwndT);
xxxSendMessage(ppopupmenu->spwndPopupMenu, MN_CANCELMENUS, 0, 0);
ThreadUnlock(&tlpwndT);
goto Unlock;
}
}
ppopupmenu->fFirstClick = FALSE;
}
if (cmdHitArea == MFMWFP_NOITEM) {
//
// This is a system menu or a menu bar and the button up
// occurred on the system menu or on a menu bar item.
//
xxxMNButtonUp(ppopupmenu, pMenuState, cmdItem, 0);
} else if ((cmdHitArea != MFMWFP_OFFMENU) && (cmdHitArea != MFMWFP_ALTMENU)) {
//
// Warning: It's common for the popup to go away during the
// processing of this message, so don't add any code that
// messes with hwnd after this call!
//
// DONTREVALIDATE();
//
// We send lParam (that has the mouse co-ords ) for the app
// to get it in its SC_RESTORE/SC_MINIMIZE messages 3.0
// compat
//
xxxSendMessage((PWND)cmdHitArea, MN_BUTTONUP, (DWORD)cmdItem, lParam);
} else
pMenuState->fButtonDown = FALSE;
Unlock:
if (fThreadLock)
ThreadUnlock(&tlpwndHitArea);
return;
case WM_RBUTTONDBLCLK:
case WM_NCRBUTTONDBLCLK:
if (!(ppopupmenu->fRightButton)) {
if (pMenuState->fButtonDown) {
msgRightButton = lpmsg->message;
goto EatRightButtons;
}
break;
} else {
pMenuState->mnFocus = MOUSEHOLD;
cmdHitArea = xxxMNFindWindowFromPoint(
ppopupmenu, &cmdItem, MAKEPOINTS(lParam));
if (cmdHitArea == MFMWFP_OFFMENU) {
/*
*Double click on no menu, cancel out and don't swallow so
* that double clicks get us out.
*/
xxxMNCancel(ppopupmenu, 0, 0, 0);
return;
}
}
/*
* Swallow the message since we handled it.
*/
if (xxxPeekMessage(&msg, NULL, 0, 0, PM_NOYIELD) &&
((msg.message == WM_RBUTTONDBLCLK) ||
(msg.message == WM_NCRBUTTONDBLCLK))) {
xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
}
return;
case WM_LBUTTONDBLCLK:
case WM_NCLBUTTONDBLCLK:
// Commented out due to TandyT whinings...
// if (ppopup->fRightButton)
// break;
pMenuState->mnFocus = MOUSEHOLD;
cmdHitArea = xxxMNFindWindowFromPoint(
ppopupmenu, &cmdItem, MAKEPOINTS(lParam));
if ((cmdHitArea == MFMWFP_OFFMENU) && (cmdItem == 0)) {
// Dbl-clicked in middle of nowhere, so terminate menus, and
// let button pass through.
xxxMNCancel(ppopupmenu, 0, 0, 0L);
return;
} else if (ppopupmenu->fHasMenuBar && (cmdHitArea == MFMWFP_ALTMENU)) {
//
// BOGUS
// TREAT LIKE BUTTON DOWN since we didn't dblclk on same item.
//
xxxMNSwitchToAlternateMenu(ppopupmenu);
cmdHitArea = MFMWFP_NOITEM;
}
if (cmdHitArea == MFMWFP_NOITEM)
xxxMNDoubleClick(ppopupmenu, cmdItem);
else {
UserAssert(cmdHitArea);
ThreadLock((PWND)cmdHitArea, &tlpwndHitArea);
xxxSendMessage((PWND)cmdHitArea, MN_DBLCLK,
(DWORD)cmdItem, 0L);
ThreadUnlock(&tlpwndHitArea);
}
return;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
/*
* If mouse button is down, ignore keyboard input (fix #3899, IanJa)
*/
if (pMenuState->fButtonDown && (ch != VK_F1)) {
return;
}
pMenuState->mnFocus = KEYBDHOLD;
switch (ch) {
case VK_UP:
case VK_DOWN:
case VK_LEFT:
case VK_RIGHT:
case VK_RETURN:
case VK_CANCEL:
case VK_ESCAPE:
case VK_MENU:
case VK_F10:
case VK_F1:
if (ppopupmenu->spwndActivePopup) {
ThreadLockAlways(ppopupmenu->spwndActivePopup, &tlpwndT);
xxxSendMessage(ppopupmenu->spwndActivePopup, lpmsg->message,
ch, 0L);
ThreadUnlock(&tlpwndT);
} else {
xxxMNKeyDown(ppopupmenu, pMenuState, (UINT)ch);
}
break;
default:
TranslateKey:
_TranslateMessage(lpmsg, 0);
break;
}
return;
case WM_CHAR:
case WM_SYSCHAR:
if (ppopupmenu->spwndActivePopup) {
ThreadLockAlways(ppopupmenu->spwndActivePopup, &tlpwndT);
xxxSendMessage(ppopupmenu->spwndActivePopup, lpmsg->message,
ch, 0L);
ThreadUnlock(&tlpwndT);
} else {
xxxMNChar(ppopupmenu, pMenuState, (UINT)ch);
}
return;
case WM_SYSKEYUP:
/*
* Ignore ALT and F10 keyup messages since they are handled on
* the KEYDOWN message.
*/
if (ch == VK_MENU || ch == VK_F10) {
if (winOldAppHackoMaticFlags & WOAHACK_CHECKALTKEYSTATE) {
if (winOldAppHackoMaticFlags & WOAHACK_IGNOREALTKEYDOWN) {
winOldAppHackoMaticFlags &= ~WOAHACK_IGNOREALTKEYDOWN;
winOldAppHackoMaticFlags &= ~WOAHACK_CHECKALTKEYSTATE;
} else
winOldAppHackoMaticFlags |= WOAHACK_IGNOREALTKEYDOWN;
}
return;
}
/*
** fall thru **
*/
case WM_KEYUP:
/*
* Do RETURNs on the up transition only
*/
goto TranslateKey;
case WM_SYSTIMER:
/*
* Prevent the caret from flashing by eating all WM_SYSTIMER messages.
*/
return;
default:
break;
}
if (PtiCurrent()->pq->codeCapture == NO_CAP_CLIENT)
xxxCapture(PtiCurrent(), ppopupmenu->spwndNotify, SCREEN_CAPTURE);
_TranslateMessage(lpmsg, 0);
xxxDispatchMessage(lpmsg);
}
/***************************************************************************\
* xxxMenuLoop
*
* The menu processing entry point.
* assumes: pMenuState->spwndMenu is the window which is the owner of the menu
* we are processing.
*
* History:
\***************************************************************************/
int xxxMNLoop(
PPOPUPMENU ppopupmenu,
PMENUSTATE pMenuState,
LONG lParam,
BOOL fDblClk)
{
int hit;
MSG msg;
BOOL fSendIdle = TRUE;
BOOL fInQueue = FALSE;
DWORD menuState;
PTHREADINFO pti;
TL tlpwndT;
UserAssert(IsRootPopupMenu(ppopupmenu));
pMenuState->fInsideMenuLoop = TRUE;
pMenuState->cmdLast = 0;
pti = PtiCurrent();
pMenuState->ptMouseLast.x = pti->ptLast.x;
pMenuState->ptMouseLast.y = pti->ptLast.y;
/*
* Set flag to false, so that we can track if windows have
* been activated since entering this loop.
*/
pti->pq->QF_flags &= ~QF_ACTIVATIONCHANGE;
/*
* Were we called from xxxMenuKeyFilter? If not, simulate a LBUTTONDOWN
* message to bring up the popup.
*/
if (lParam != 0x7FFFFFFFL) {
if (_GetKeyState(((ppopupmenu->fRightButton) ?
VK_RBUTTON : VK_LBUTTON)) >= 0) {
/*
* We think the mouse button should be down but the call to get key
* state says different so we need to get outta menu mode. This
* happens if clicking on the menu causes a sys modal message box to
* come up before we can enter this stuff. For example, run
* winfile, click on drive a: to see its tree. Activate some other
* app, then open drive a: and activate winfile by clicking on the
* menu. This causes a sys modal msg box to come up just before
* entering menu mode. The user may have the mouse button up but
* menu mode code thinks it is down...
*/
/*
* Need to notify the app we are exiting menu mode because we told
* it we were entering menu mode just before entering this function
* in xxxSysCommand()...
*/
if (!ppopupmenu->fNoNotify) {
ThreadLock(ppopupmenu->spwndNotify, &tlpwndT);
xxxSendNotifyMessage(ppopupmenu->spwndNotify, WM_EXITMENULOOP,
((ppopupmenu->fIsTrackPopup && !ppopupmenu->fIsSysMenu) ? TRUE : FALSE), 0);
ThreadUnlock(&tlpwndT);
}
goto ExitMenuLoop;
}
/*
* Simulate a WM_LBUTTONDOWN message.
*/
if (!ppopupmenu->fIsTrackPopup) {
/*
* For TrackPopupMenus, we do it in the TrackPopupMenu function
* itself so we don't want to do it again.
*/
if (!xxxMNStartState(ppopupmenu, MOUSEHOLD))
goto ExitMenuLoop;
}
if ((ppopupmenu->fRightButton)) {
msg.message = (fDblClk ? WM_RBUTTONDBLCLK : WM_RBUTTONDOWN);
} else {
msg.message = (fDblClk ? WM_LBUTTONDBLCLK : WM_LBUTTONDOWN);
}
msg.lParam = lParam;
msg.hwnd = HW(ppopupmenu->spwndPopupMenu);
goto DoMsg;
}
while (pMenuState->fInsideMenuLoop) {
/*
* Is a message waiting for us?
*/
BOOL fPeek = xxxPeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_NOREMOVE);
#ifdef DEBUG
Validateppopupmenu(ppopupmenu);
#endif
if (fPeek) {
/*
* Bail if we have been forced out of menu loop
*/
if (ExitMenuLoop (pMenuState, ppopupmenu)) {
goto ExitMenuLoop;
}
/*
* Since we could have blocked in xxxWaitMessage (see last line
* of loop) or xxxPeekMessage, reset the cached copy of
* ptiCurrent()->pq: It could have changed if someone did a
* DetachThreadInput() while we were away.
*/
if ((!ppopupmenu->fIsTrackPopup &&
pti->pq->spwndActive != ppopupmenu->spwndNotify &&
((pti->pq->spwndActive == NULL) || !_IsChild(pti->pq->spwndActive, ppopupmenu->spwndNotify)))) {
/*
* End menu processing if we are no longer the active window.
* This is needed in case a system modal dialog box pops up
* while we are tracking the menu code for example. It also
* helps out Tracer if a macro is executed while a menu is down.
*/
/*
* Also, end menu processing if we think the mouse button is
* down but it really isn't. (Happens if a sys modal dialog int
* time dlg box comes up while we are in menu mode.)
*/
goto ExitMenuLoop;
}
if (ppopupmenu->fIsMenuBar && msg.message == WM_LBUTTONDBLCLK) {
/*
* Was the double click on the system menu or caption?
*/
hit = FindNCHit(ppopupmenu->spwndNotify, msg.lParam);
if (hit == HTCAPTION) {
PWND pwnd;
PMENU pmenu;
/*
* Get the message out of the queue since we're gonna
* process it.
*/
xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
if (ExitMenuLoop (pMenuState, ppopupmenu)) {
goto ExitMenuLoop;
} else {
pmenu = GetSysMenuHandle(ppopupmenu->spwndNotify);
pwnd = ppopupmenu->spwndNotify;
menuState = _GetMenuState(pmenu, SC_RESTORE & 0x0000FFF0,
MF_BYCOMMAND);
/*
* Only send the sys command if the item is valid. If
* the item doesn't exist or is disabled, then don't
* post the syscommand. Note that for win2 apps, we
* always send the sys command if it is a child window.
* This is so hosebag apps can change the sys menu.
*/
if (!(menuState & MFS_GRAYED)) {
_PostMessage(pwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
}
/*
* Get out of menu mode.
*/
goto ExitMenuLoop;
}
} else if (hit == HTMENU) {
#ifdef NOJWAPPHACK
if (!TestWF(ppopupmenu->spwndNotify, WFWIN31COMPAT) &&
GetAppCompatFlags(NULL) & GACF_SENDMENUDBLCLK) {
/*
* Hack for JustWrite. If double click on menu bar, get out
* of menu mode, and don't swallow the double click
* message. This way the message goes to the app and it can
* process it however it pleases.
*/
goto ExitMenuLoop;
}
#endif
}
}
fInQueue = (msg.message == WM_LBUTTONDOWN ||
msg.message == WM_RBUTTONDOWN ||
msg.message == WM_NCLBUTTONDOWN ||
msg.message == WM_NCRBUTTONDOWN);
if (!fInQueue) {
/*
* Note that we call xxxPeekMessage() with the filter
* set to the message we got from xxxPeekMessage() rather
* than simply 0, 0. This prevents problems when
* xxxPeekMessage() returns something like a WM_TIMER,
* and after we get here to remove it a WM_LBUTTONDOWN,
* or some higher-priority input message, gets in the
* queue and gets removed accidently. Basically we want
* to be sure we remove the right message in this case.
* NT bug 3852 was caused by this problem.
* Set the TIF_IGNOREPLAYBACKDELAY bit in case journal playback
* is happening: this allows us to proceed even if the hookproc
* incorrectly returns a delay now. The bit will be cleared if
* this happens, so we can see why the Peek-Remove below fails.
* Lotus' Freelance Graphics tutorial does such bad journalling
*/
pti->TIF_flags |= TIF_IGNOREPLAYBACKDELAY;
if (!xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE)) {
if (pti->TIF_flags & TIF_IGNOREPLAYBACKDELAY) {
pti->TIF_flags &= ~TIF_IGNOREPLAYBACKDELAY;
/*
* It wasn't a bad journal playback: something else
* made the previously peeked message disappear before
* we could peek it again to remove it.
*/
RIPMSG1(RIP_WARNING, "Disappearing msg 0x%08lx", msg.message);
goto ShowPopup;
}
}
pti->TIF_flags &= ~TIF_IGNOREPLAYBACKDELAY;
}
if (!_CallMsgFilter(&msg, MSGF_MENU)) {
DoMsg:
xxxHandleMenuMessages(&msg, pMenuState, ppopupmenu);
#ifdef DEBUG
Validateppopupmenu(ppopupmenu);
#endif
if (ExitMenuLoop (pMenuState, ppopupmenu)) {
goto ExitMenuLoop;
}
if ((pti->pq->QF_flags & QF_ACTIVATIONCHANGE) &&
pMenuState->fInsideMenuLoop) {
/*
* Run away and exit menu mode if another window has become
* active while a menu was up.
*/
RIPMSG0(RIP_WARNING, "Exiting menu mode: another window activated");
goto ExitMenuLoop;
}
if (pti->pq->spwndCapture !=
ppopupmenu->spwndNotify) {
TL tlpwndT3;
/*
* We dispatched a message to the app while in menu mode,
* but the app released the mouse capture when it never
* owned it and now, we will cause menu mode to screw up
* unless we fix ourselves up. Set the capture back to
* what we set it to in StartMenuState. (WinWorks does this)
*
* Lotus Freelance demo programs depend on GetCapture
* returning their hwnd when in menumode.
*/
xxxCapture(PtiCurrent(),
ppopupmenu->spwndNotify,
SCREEN_CAPTURE);
ThreadLock(ppopupmenu->spwndNotify, &tlpwndT3);
xxxSendMessage(ppopupmenu->spwndNotify, WM_SETCURSOR,
(DWORD)HW(ppopupmenu->spwndNotify),
MAKELONG(MSGF_MENU, 0));
ThreadUnlock(&tlpwndT3);
}
if (msg.message == WM_SYSTIMER ||
msg.message == WM_TIMER ||
msg.message == WM_PAINT)
goto ShowPopup;
} else {
if (fInQueue)
xxxPeekMessage(&msg, NULL, msg.message, msg.message,
PM_REMOVE);
}
/*
* Reenable WM_ENTERIDLE messages.
*/
fSendIdle = TRUE;
} else {
ShowPopup:
/*
* Bail if we have been forced out of menu loop
*/
if (ExitMenuLoop (pMenuState, ppopupmenu)) {
goto ExitMenuLoop;
}
/*
* Is a non visible popup menu around waiting to be shown?
*/
if (ppopupmenu->spwndActivePopup &&
!TestWF(ppopupmenu->spwndActivePopup, WFVISIBLE)) {
TL tlpwndT2;
PWND spwndActivePopup = ppopupmenu->spwndActivePopup;
/*
* Lock this so it doesn't go away during either of these
* calls. Don't rely on ppopupmenu->spwndActivePopup
* remaining the same.
*/
ThreadLock(spwndActivePopup, &tlpwndT2);
/*
* Paint the owner window before the popup menu comes up so that
* the proper bits are saved.
*/
if (ppopupmenu->spwndNotify) {
ThreadLockAlways(ppopupmenu->spwndNotify, &tlpwndT);
xxxUpdateWindow(ppopupmenu->spwndNotify);
ThreadUnlock(&tlpwndT);
}
xxxSendMessage(spwndActivePopup, MN_SHOWPOPUPWINDOW,
0, 0);
/*
* This is needed so that popup menus are properly drawn on sys
* modal dialog boxes.
*/
xxxUpdateWindow(spwndActivePopup);
ThreadUnlock(&tlpwndT2);
continue;
}
if (msg.message == WM_PAINT || msg.message == WM_TIMER) {
/*
* We don't want to send enter idle messages if we came here via
* a goto ShowPopup on paint message because there may be other
* paint messages for us to handle. Zero out the msg.message
* field so that if PeekMessage returns null next time around,
* this outdated WM_PAINT won't be left over in the message
* struct.
*/
msg.message = 0;
continue;
}
#ifdef MEMPHIS_MENU_ANIMATION
if (MNAnimate(TRUE))
continue;
#endif // MEMPHIS_MENU_ANIMATION
/*
* If a hierarchical popup has been destroyed, this is a
* good time to flush ppmDelayedFree
*/
if (ppopupmenu->fFlushDelayedFree) {
MNFlushDestroyedPopups (ppopupmenu, FALSE);
ppopupmenu->fFlushDelayedFree = FALSE;
}
/*
* We need to send the WM_ENTERIDLE message only the first time
* there are no messages for us to process. Subsequent times we
* need to yield via WaitMessage(). This will allow other tasks to
* get some time while we have a menu down.
*/
if (fSendIdle) {
if (ppopupmenu->spwndNotify != NULL) {
ThreadLockAlways(ppopupmenu->spwndNotify, &tlpwndT);
xxxSendMessage(ppopupmenu->spwndNotify, WM_ENTERIDLE, MSGF_MENU,
(DWORD)HW(ppopupmenu->spwndActivePopup));
ThreadUnlock(&tlpwndT);
}
fSendIdle = FALSE;
} else
xxxWaitMessage();
} /* if (PeekMessage(&msg, NULL, 0, 0, PM_NOYIELD)) else */
} /* end while (fInsideMenuLoop) */
ExitMenuLoop:
pMenuState->fInsideMenuLoop = FALSE;
/*
* Make sure that the menu has been ended/canceled
*/
if (ppopupmenu->fIsTrackPopup) {
if (!ppopupmenu->fInCancel) {
xxxMNCancel(ppopupmenu, 0, 0, 0);
}
} else {
if (!pMenuState->fInEndMenu) {
xxxEndMenu(pMenuState);
}
}
xxxReleaseCapture();
// Throw in an extra peek here when we exit the menu loop to ensure that the input queue
// for this thread gets unlocked if there is no more input left for him.
xxxPeekMessage(&msg, NULL, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_NOYIELD | PM_NOREMOVE);
return(pMenuState->cmdLast);
} /* xxxMenuLoop() */