|
|
/**************************** Module Header ********************************\
* Module Name: mnloop.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * Menu Modal Loop Routines * * History: * 10-10-90 JimA Cleanup. * 03-18-91 IanJa Window revalidation added \***************************************************************************/
#include "precomp.h"
#pragma hdrstop
/***************************************************************************\
* xxxMNRemoveMessage * * History * 11/23/96 GerardoB Created \***************************************************************************/ BOOL xxxMNRemoveMessage (UINT message1, UINT message2) { MSG msg; if (!xxxPeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_NOREMOVE)) { return FALSE; }
if ((msg.message == message1) || (msg.message == message2)) { UserAssert(msg.message != 0); xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE); return TRUE; } else { return FALSE; } } /***************************************************************************\
* xxxHandleMenuMessages * * History: \***************************************************************************/
BOOL xxxHandleMenuMessages( LPMSG lpmsg, PMENUSTATE pMenuState, PPOPUPMENU ppopupmenu) { DWORD ch; ULONG_PTR cmdHitArea; UINT cmdItem; LPARAM lParam; BOOL fThreadLock = FALSE; TL tlpwndHitArea; TL tlpwndT; POINT pt; PWND pwnd; RECT rc;
/*
* Paranoia. Let's bail up front if we don't have a menu. * Some code checks for NULL spmenu, other parts assume it's always not NULL * Use RIP_ERROR for a while to make sure this is OK */ if (ppopupmenu->spmenu == NULL) { RIPMSG2(RIP_ERROR, "xxxHandleMenuMessages NULL spmenu. pMenuSate:%p ppopupmenu:%p", pMenuState, ppopupmenu); return FALSE; } /*
* Get things out of the structure so that we can access them quicker. */ ch = (DWORD)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; } /*
* Fall through */ case WM_RBUTTONDBLCLK: case WM_NCRBUTTONDBLCLK: /*
* Right click outside the menu dismisses the menu * (we didn't use to do this for single right clicks on 4.0) */ pMenuState->mnFocus = MOUSEHOLD; cmdHitArea = xxxMNFindWindowFromPoint(ppopupmenu, &cmdItem, MAKEPOINTS(lParam)); if (cmdHitArea == MFMWFP_OFFMENU) { xxxMNDismiss(pMenuState); return TRUE; } /*
* Do nothing on right clicks on the menu */ if (!pMenuState->fModelessMenu) { xxxMNRemoveMessage(lpmsg->message, 0); } return TRUE;
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 = GET_X_LPARAM(lParam); pMenuState->ptMouseLast.y = GET_Y_LPARAM(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. */ fThreadLock = IsMFMWFPWindow(cmdHitArea); if (fThreadLock) { ThreadLock((PWND)cmdHitArea, &tlpwndHitArea); }
/*
* If this is a drag and drop menu, remember the mouse * position and the hit test results. */ if (pMenuState->fDragAndDrop) { pMenuState->ptButtonDown = pMenuState->ptMouseLast; pMenuState->uButtonDownIndex = cmdItem; LockMFMWFPWindow(&pMenuState->uButtonDownHitArea, cmdHitArea); }
/*
* Modeless menus don't capture the mouse so we might not see * the button up. We also release capture when sending the * WM_MENUDODRAGDROP message. So we want to remember what * mouse button went down. */ if (pMenuState->fDragAndDrop || pMenuState->fModelessMenu) { if (ch & MK_RBUTTON) { pMenuState->vkButtonDown = VK_RBUTTON; } else { pMenuState->vkButtonDown = VK_LBUTTON; } }
if ((cmdHitArea == MFMWFP_OFFMENU) && (cmdItem == 0)) { //
// Clicked in middle of nowhere, so terminate menus, and
// let button pass through.
CancelOut: xxxMNDismiss(pMenuState); 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 (!pMenuState->fModelessMenu) { xxxMNRemoveMessage(lpmsg->message, WM_RBUTTONDOWN); } goto Unlock;
case WM_MOUSEMOVE: case WM_NCMOUSEMOVE:
/*
* Is the user starting to drag? */ if (pMenuState->fDragAndDrop && pMenuState->fButtonDown && !pMenuState->fDragging && !pMenuState->fButtonAlwaysDown && (pMenuState->uButtonDownHitArea != MFMWFP_OFFMENU)) {
/*
* We expect the mouse to go down on a menu item before a drag can start */ UserAssert(!ppopupmenu->fFirstClick);
/*
* Calculate drag detect rectangle using the position the mouse went * down on */ *(LPPOINT)&rc.left = pMenuState->ptButtonDown; *(LPPOINT)&rc.right = pMenuState->ptButtonDown; InflateRect(&rc, SYSMET(CXDRAG), SYSMET(CYDRAG));
pt.x = GET_X_LPARAM(lParam); pt.y = GET_Y_LPARAM(lParam);
/*
* If we've moved outside the drag rect, then the user is dragging */ if (!PtInRect(&rc, pt)) { /*
* Post a message so we'll finish processing this message * and get out of here before telling the app that the user * is dragging */ pwnd = GetMenuStateWindow(pMenuState); if (pwnd != NULL) { pMenuState->fDragging = TRUE; _PostMessage(pwnd, MN_DODRAGDROP, 0, 0); } else { RIPMSG0(RIP_ERROR, "xxxMNMouseMove. Unble to post MN_DODGRAGDROP"); } } } /* if (pMenuState->fDragAndDrop */
xxxMNMouseMove(ppopupmenu, pMenuState, MAKEPOINTS(lParam)); return TRUE;
case WM_RBUTTONUP: case WM_NCRBUTTONUP: if (ppopupmenu->fRightButton) { goto HandleButtonUp; } /*
* If the button is down, simply swallow this message */ if (pMenuState->fButtonDown) { if (!pMenuState->fModelessMenu) { xxxMNRemoveMessage(lpmsg->message, 0); } return TRUE; } // 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)
//
// NOTE: Though this feature was added for Nashville, it was enabled
// by default for all apps on Win2K and exhibited no app compat
// problems. Thus, it is assumed that no version checking is required;
// moreoever, changing at this point is needlessly dangerous.
if ((lpmsg->message == WM_RBUTTONUP) && !ppopupmenu->fNoNotify) { PPOPUPMENU ppopupActive;
if ((ppopupmenu->spwndActivePopup != NULL) && (ppopupActive = ((PMENUWND)(ppopupmenu->spwndActivePopup))->ppopupmenu) && MNIsItemSelected(ppopupActive)) { TL tlpwndNotify; ThreadLock( ppopupActive->spwndNotify, &tlpwndNotify ); xxxSendMessage(ppopupActive->spwndNotify, WM_MENURBUTTONUP, ppopupActive->posSelectedItem, (LPARAM)PtoH(ppopupActive->spmenu)); ThreadUnlock( &tlpwndNotify ); } } 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 TRUE; }
/*
* Cancel the dragging state, if any. */ if (pMenuState->fDragAndDrop) {
UnlockMFMWFPWindow(&pMenuState->uButtonDownHitArea); pMenuState->fDragging = FALSE;
if (pMenuState->fIgnoreButtonUp) { pMenuState->fButtonDown = pMenuState->fIgnoreButtonUp = FALSE; return TRUE; } }
/*
* Find out where this mouse up occurred. */ pMenuState->ptMouseLast.x = GET_X_LPARAM(lParam); pMenuState->ptMouseLast.y = GET_Y_LPARAM(lParam); cmdHitArea = xxxMNFindWindowFromPoint(ppopupmenu, &cmdItem, MAKEPOINTS(lParam));
/*
* If this is not true, some the code below won't work right. */ UserAssert((cmdHitArea != MFMWFP_OFFMENU) || (cmdItem == 0)); UserAssert(cmdHitArea != 0x0000FFFF);
/*
* 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. */ fThreadLock = IsMFMWFPWindow(cmdHitArea); if (fThreadLock) { ThreadLock((PWND)cmdHitArea, &tlpwndHitArea); }
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 = pMenuState->fButtonAlwaysDown = FALSE; } Unlock: if (fThreadLock) ThreadUnlock(&tlpwndHitArea); return TRUE;
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.
xxxMNDismiss(pMenuState); return TRUE; } 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(pMenuState, ppopupmenu, cmdItem); else { UserAssert(cmdHitArea);
ThreadLock((PWND)cmdHitArea, &tlpwndHitArea); xxxSendMessage((PWND)cmdHitArea, MN_DBLCLK, (DWORD)cmdItem, 0L); ThreadUnlock(&tlpwndHitArea); } return TRUE;
case WM_KEYDOWN: case WM_SYSKEYDOWN:
/*
* If mouse button is down, ignore keyboard input (fix #3899, IanJa) */ if (pMenuState->fButtonDown && (ch != VK_F1)) {
/*
* Check if the user wants to cancel dragging. */ if (pMenuState->fDragging && (ch == VK_ESCAPE)) { RIPMSG0(RIP_WARNING, "xxxHandleMenuMessages: ESC while dragging"); pMenuState->fIgnoreButtonUp = TRUE; }
return TRUE; } 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;
case VK_TAB: /*
* People hit the ALT key now just to turn underlines ON in dialogs. * This throws them into "invisible menu mode". If they hit any char * at that point, we'll bail in xxxMNChar. But not so if they hit ctrl-tab, * which is used to navigate property sheets. So let's help them out. */ if (ppopupmenu->fIsMenuBar && (ppopupmenu->spwndActivePopup == NULL)) { xxxMNDismiss(pMenuState); return TRUE; } /*
* Fall through */
default: TranslateKey: if (!pMenuState->fModelessMenu) { xxxTranslateMessage(lpmsg, 0); } break; } return TRUE;
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 TRUE;
case WM_SYSKEYUP:
/*
* Ignore ALT and F10 keyup messages since they are handled on * the KEYDOWN message. */ if (ch == VK_MENU || ch == VK_F10) { return TRUE; }
/*
** 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 TRUE;
default: break; }
#if DBG
/*
* Nobody should be able to steal capture from modal menus. */ if (!pMenuState->fModelessMenu && !pMenuState->fInDoDragDrop && !ExitMenuLoop (pMenuState, ppopupmenu) ) {
UserAssert(PtiCurrent()->pq->QF_flags & QF_CAPTURELOCKED); UserAssert(PtiCurrent()->pq->spwndCapture == ppopupmenu->spwndNotify); } #endif
/*
* We didn't handle this message */ return FALSE; }
/***************************************************************************\
* xxxEndMenuLoop * * Makes sure that the menu has been ended/canceled * * History: * 10/25/96 GerardoB Extracted from xxxMNLoop \***************************************************************************/ void xxxEndMenuLoop (PMENUSTATE pMenuState, PPOPUPMENU ppopupmenu) {
UserAssert(IsRootPopupMenu(ppopupmenu));
if (ppopupmenu->fIsTrackPopup) { if (!ppopupmenu->fInCancel) { xxxMNDismiss(pMenuState); } } else { if (pMenuState->fUnderline) { TL tlpwnd; ThreadLock(ppopupmenu->spwndNotify, &tlpwnd); xxxDrawMenuBarUnderlines(ppopupmenu->spwndNotify, FALSE); ThreadUnlock(&tlpwnd); } if (!pMenuState->fInEndMenu) { xxxEndMenu(pMenuState); } } /*
* If this is a modeless menu, make sure that the notification * window caption is drawn in the proper state */ if (pMenuState->fModelessMenu && (ppopupmenu->spwndNotify != NULL)) { PWND pwndNotify = ppopupmenu->spwndNotify; PTHREADINFO pti = GETPTI(pwndNotify); BOOL fFrameOn = (pti->pq == gpqForeground) && (pti->pq->spwndActive == pwndNotify); TL tlpwndNotify;
if (fFrameOn ^ !!TestWF(pwndNotify, WFFRAMEON)) { ThreadLockAlways(pwndNotify, &tlpwndNotify); xxxDWP_DoNCActivate(pwndNotify, (fFrameOn ? NCA_ACTIVE : NCA_FORCEFRAMEOFF), HRGN_FULL); ThreadUnlock(&tlpwndNotify);
} } } /***************************************************************************\
* 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, LPARAM 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 (!pMenuState->fMenuStarted) { 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 (!xxxMNStartMenu(ppopupmenu, MOUSEHOLD)) { goto ExitMenuLoop; } }
if ((ppopupmenu->fRightButton)) { msg.message = (fDblClk ? WM_RBUTTONDBLCLK : WM_RBUTTONDOWN); msg.wParam = MK_RBUTTON; } else { msg.message = (fDblClk ? WM_LBUTTONDBLCLK : WM_LBUTTONDOWN); msg.wParam = MK_LBUTTON; } msg.lParam = lParam; msg.hwnd = HW(ppopupmenu->spwndPopupMenu); xxxHandleMenuMessages(&msg, pMenuState, ppopupmenu); }
/*
* If this is a modeless menu, release capture, mark it in the menu state * and return. Decrement foreground lock count. */ if (pMenuState->fModelessMenu) { xxxMNReleaseCapture();
DecSFWLockCount(); DBGDecModalMenuCount(); return 0; }
while (pMenuState->fInsideMenuLoop) {
/*
* Is a message waiting for us? */ BOOL fPeek = xxxPeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_NOREMOVE);
Validateppopupmenu(ppopupmenu);
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, (LONG)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 { pwnd = ppopupmenu->spwndNotify; ThreadLockAlways(pwnd, &tlpwndT); pmenu = xxxGetSysMenuHandle(pwnd); UserAssert(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. */ ThreadUnlock(&tlpwndT); goto ExitMenuLoop; } } }
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 NoMsg; } } pti->TIF_flags &= ~TIF_IGNOREPLAYBACKDELAY; }
if (!_CallMsgFilter(&msg, MSGF_MENU)) { if (!xxxHandleMenuMessages(&msg, pMenuState, ppopupmenu)) { xxxTranslateMessage(&msg, 0); xxxDispatchMessage(&msg); }
Validateppopupmenu(ppopupmenu);
if (ExitMenuLoop (pMenuState, ppopupmenu)) { goto ExitMenuLoop; }
if (pti->pq->QF_flags & QF_ACTIVATIONCHANGE) {
/*
* 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 DBG
/*
* Nobody should be able to steal capture from us. */ if (!pMenuState->fInDoDragDrop) { UserAssert(pti->pq->QF_flags & QF_CAPTURELOCKED); UserAssert(pti->pq->spwndCapture == ppopupmenu->spwndNotify); } #endif
/*
* If we get a system timer, then it's like we're idle */ if (msg.message == WM_SYSTIMER) { goto NoMsg; }
/*
* Don't set fSendIdle if we got these messages */ if ((msg.message == WM_TIMER) || (msg.message == WM_PAINT)) { continue; }
} else { if (fInQueue) xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE); }
/*
* Reenable WM_ENTERIDLE messages. */ fSendIdle = TRUE;
} else { NoMsg: /*
* Bail if we have been forced out of menu loop */ if (ExitMenuLoop (pMenuState, ppopupmenu)) { goto ExitMenuLoop; }
UserAssert((ppopupmenu->spwndActivePopup == NULL) || (TestWF(ppopupmenu->spwndActivePopup, WFVISIBLE)));
/*
* 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, (LPARAM)HW(ppopupmenu->spwndActivePopup)); ThreadUnlock(&tlpwndT); } fSendIdle = FALSE; } else { /*
* If we're animating, sleep only 1 ms to reduce the chance * of jerky animation. * When not animating, this is the same as a xxxWaitMessage call */ #ifdef MESSAGE_PUMP_HOOK
xxxWaitMessageEx(QS_ALLINPUT | QS_EVENT, pMenuState->hdcWndAni != NULL); #else
xxxSleepThread(QS_ALLINPUT | QS_EVENT, (pMenuState->hdcWndAni != NULL), TRUE); #endif
}
} /* if (PeekMessage(&msg, NULL, 0, 0, PM_NOYIELD)) else */
} /* end while (fInsideMenuLoop) */
ExitMenuLoop: pMenuState->fInsideMenuLoop = FALSE; pMenuState->fModelessMenu = FALSE;
/*
* Make sure that the menu has been ended/canceled */ xxxEndMenuLoop (pMenuState, ppopupmenu);
xxxMNReleaseCapture();
// 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() */
|