Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

mnloop.c

Go to the documentation of this file.
00001 /**************************** Module Header ********************************\ 00002 * Module Name: mnloop.c 00003 * 00004 * Copyright (c) 1985 - 1999, Microsoft Corporation 00005 * 00006 * Menu Modal Loop Routines 00007 * 00008 * History: 00009 * 10-10-90 JimA Cleanup. 00010 * 03-18-91 IanJa Window revalidation added 00011 \***************************************************************************/ 00012 00013 #include "precomp.h" 00014 #pragma hdrstop 00015 00016 /***************************************************************************\ 00017 * xxxMNRemoveMessage 00018 * 00019 * History 00020 * 11/23/96 GerardoB Created 00021 \***************************************************************************/ 00022 BOOL xxxMNRemoveMessage (UINT message1, UINT message2) 00023 { 00024 MSG msg; 00025 if (!xxxPeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_NOREMOVE)) { 00026 return FALSE; 00027 } 00028 00029 if ((msg.message == message1) || (msg.message == message2)) { 00030 UserAssert(msg.message != 0); 00031 xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE); 00032 return TRUE; 00033 } else { 00034 return FALSE; 00035 } 00036 } 00037 /***************************************************************************\ 00038 * xxxHandleMenuMessages 00039 * 00040 * History: 00041 \***************************************************************************/ 00042 00043 BOOL xxxHandleMenuMessages( 00044 LPMSG lpmsg, 00045 PMENUSTATE pMenuState, 00046 PPOPUPMENU ppopupmenu) 00047 { 00048 DWORD ch; 00049 ULONG_PTR cmdHitArea; 00050 UINT cmdItem; 00051 LPARAM lParam; 00052 BOOL fThreadLock = FALSE; 00053 TL tlpwndHitArea; 00054 TL tlpwndT; 00055 POINT pt; 00056 PWND pwnd; 00057 RECT rc; 00058 00059 /* 00060 * Paranoia. Let's bail up front if we don't have a menu. 00061 * Some code checks for NULL spmenu, other parts assume it's always not NULL 00062 * Use RIP_ERROR for a while to make sure this is OK 00063 */ 00064 if (ppopupmenu->spmenu == NULL) { 00065 RIPMSG2(RIP_ERROR, "xxxHandleMenuMessages NULL spmenu. pMenuSate:%p ppopupmenu:%p", 00066 pMenuState, ppopupmenu); 00067 return FALSE; 00068 } 00069 /* 00070 * Get things out of the structure so that we can access them quicker. 00071 */ 00072 ch = (DWORD)lpmsg->wParam; 00073 lParam = lpmsg->lParam; 00074 00075 /* 00076 * In this switch statement, we only look at messages we want to handle and 00077 * swallow. Messages we don't understand will get translated and 00078 * dispatched. 00079 */ 00080 switch (lpmsg->message) { 00081 case WM_RBUTTONDOWN: 00082 case WM_NCRBUTTONDOWN: 00083 00084 if (ppopupmenu->fRightButton) { 00085 goto HandleButtonDown; 00086 } 00087 /* 00088 * Fall through 00089 */ 00090 case WM_RBUTTONDBLCLK: 00091 case WM_NCRBUTTONDBLCLK: 00092 /* 00093 * Right click outside the menu dismisses the menu 00094 * (we didn't use to do this for single right clicks on 4.0) 00095 */ 00096 pMenuState->mnFocus = MOUSEHOLD; 00097 cmdHitArea = xxxMNFindWindowFromPoint(ppopupmenu, &cmdItem, MAKEPOINTS(lParam)); 00098 if (cmdHitArea == MFMWFP_OFFMENU) { 00099 xxxMNDismiss(pMenuState); 00100 return TRUE; 00101 } 00102 /* 00103 * Do nothing on right clicks on the menu 00104 */ 00105 if (!pMenuState->fModelessMenu) { 00106 xxxMNRemoveMessage(lpmsg->message, 0); 00107 } 00108 return TRUE; 00109 00110 case WM_LBUTTONDOWN: 00111 case WM_NCLBUTTONDOWN: 00112 // Commented out due to TandyT whinings... 00113 // if ((ppopupmenu->trackPopupMenuFlags & TPM_RIGHTBUTTON)) 00114 // break; 00115 00116 HandleButtonDown: 00117 00118 /* 00119 * Find out where this mouse down occurred. 00120 */ 00121 pMenuState->mnFocus = MOUSEHOLD; 00122 pMenuState->ptMouseLast.x = GET_X_LPARAM(lParam); 00123 pMenuState->ptMouseLast.y = GET_Y_LPARAM(lParam); 00124 cmdHitArea = xxxMNFindWindowFromPoint(ppopupmenu, &cmdItem, MAKEPOINTS(lParam)); 00125 00126 00127 /* 00128 * Thread lock this if it is a pwnd. This certainly isn't the way 00129 * you'd implement this if you had locking to begin with. 00130 */ 00131 fThreadLock = IsMFMWFPWindow(cmdHitArea); 00132 if (fThreadLock) { 00133 ThreadLock((PWND)cmdHitArea, &tlpwndHitArea); 00134 } 00135 00136 /* 00137 * If this is a drag and drop menu, remember the mouse 00138 * position and the hit test results. 00139 */ 00140 if (pMenuState->fDragAndDrop) { 00141 pMenuState->ptButtonDown = pMenuState->ptMouseLast; 00142 pMenuState->uButtonDownIndex = cmdItem; 00143 LockMFMWFPWindow(&pMenuState->uButtonDownHitArea, cmdHitArea); 00144 } 00145 00146 /* 00147 * Modeless menus don't capture the mouse so we might not see 00148 * the button up. We also release capture when sending the 00149 * WM_MENUDODRAGDROP message. So we want to remember what 00150 * mouse button went down. 00151 */ 00152 if (pMenuState->fDragAndDrop || pMenuState->fModelessMenu) { 00153 if (ch & MK_RBUTTON) { 00154 pMenuState->vkButtonDown = VK_RBUTTON; 00155 } else { 00156 pMenuState->vkButtonDown = VK_LBUTTON; 00157 } 00158 } 00159 00160 00161 if ((cmdHitArea == MFMWFP_OFFMENU) && (cmdItem == 0)) { 00162 // 00163 // Clicked in middle of nowhere, so terminate menus, and 00164 // let button pass through. 00165 CancelOut: 00166 xxxMNDismiss(pMenuState); 00167 goto Unlock; 00168 } else if (ppopupmenu->fHasMenuBar && (cmdHitArea == MFMWFP_ALTMENU)) { 00169 // 00170 // Switching between menu bar & popup 00171 // 00172 xxxMNSwitchToAlternateMenu(ppopupmenu); 00173 cmdHitArea = MFMWFP_NOITEM; 00174 } 00175 00176 if (cmdHitArea == MFMWFP_NOITEM) { 00177 // 00178 // On menu bar (system or main) 00179 // 00180 xxxMNButtonDown(ppopupmenu, pMenuState, cmdItem, TRUE); 00181 } else { 00182 // On popup window menu 00183 UserAssert(cmdHitArea); 00184 xxxSendMessage((PWND)cmdHitArea, MN_BUTTONDOWN, cmdItem, 0L); 00185 } 00186 00187 /* 00188 * Swallow the message since we handled it. 00189 */ 00190 /* 00191 * The excel guys change a wm_rbuttondown to a wm_lbuttondown message 00192 * in their message filter hook. Remove the message here or we'll 00193 * get in a nasty loop. 00194 * 00195 * We need to swallow msg32.message ONLY. It is possible for 00196 * the LBUTTONDOWN to not be at the head of the input queue. 00197 * If not, we will swallow a WM_MOUSEMOVE or something else like 00198 * that. The reason Peek() doesn't need to check the range 00199 * is because we've already Peek(PM_NOYIELD'ed) before, which 00200 * locked the sys queue. 00201 */ 00202 if (!pMenuState->fModelessMenu) { 00203 xxxMNRemoveMessage(lpmsg->message, WM_RBUTTONDOWN); 00204 } 00205 goto Unlock; 00206 00207 case WM_MOUSEMOVE: 00208 case WM_NCMOUSEMOVE: 00209 00210 /* 00211 * Is the user starting to drag? 00212 */ 00213 if (pMenuState->fDragAndDrop 00214 && pMenuState->fButtonDown 00215 && !pMenuState->fDragging 00216 && !pMenuState->fButtonAlwaysDown 00217 && (pMenuState->uButtonDownHitArea != MFMWFP_OFFMENU)) { 00218 00219 /* 00220 * We expect the mouse to go down on a menu item before a drag can start 00221 */ 00222 UserAssert(!ppopupmenu->fFirstClick); 00223 00224 /* 00225 * Calculate drag detect rectangle using the position the mouse went 00226 * down on 00227 */ 00228 *(LPPOINT)&rc.left = pMenuState->ptButtonDown; 00229 *(LPPOINT)&rc.right = pMenuState->ptButtonDown; 00230 InflateRect(&rc, SYSMET(CXDRAG), SYSMET(CYDRAG)); 00231 00232 pt.x = GET_X_LPARAM(lParam); 00233 pt.y = GET_Y_LPARAM(lParam); 00234 00235 /* 00236 * If we've moved outside the drag rect, then the user is dragging 00237 */ 00238 if (!PtInRect(&rc, pt)) { 00239 /* 00240 * Post a message so we'll finish processing this message 00241 * and get out of here before telling the app that the user 00242 * is dragging 00243 */ 00244 pwnd = GetMenuStateWindow(pMenuState); 00245 if (pwnd != NULL) { 00246 pMenuState->fDragging = TRUE; 00247 _PostMessage(pwnd, MN_DODRAGDROP, 0, 0); 00248 } else { 00249 RIPMSG0(RIP_ERROR, "xxxMNMouseMove. Unble to post MN_DODGRAGDROP"); 00250 } 00251 } 00252 } /* if (pMenuState->fDragAndDrop */ 00253 00254 xxxMNMouseMove(ppopupmenu, pMenuState, MAKEPOINTS(lParam)); 00255 return TRUE; 00256 00257 case WM_RBUTTONUP: 00258 case WM_NCRBUTTONUP: 00259 if (ppopupmenu->fRightButton) { 00260 goto HandleButtonUp; 00261 } 00262 /* 00263 * If the button is down, simply swallow this message 00264 */ 00265 if (pMenuState->fButtonDown) { 00266 if (!pMenuState->fModelessMenu) { 00267 xxxMNRemoveMessage(lpmsg->message, 0); 00268 } 00269 return TRUE; 00270 } 00271 // New feature for shell start menu -- notify when a right click 00272 // occurs on a menu item, and open a window of opportunity for 00273 // menus to recurse, allowing them to popup a context-sensitive 00274 // menu for that item. (jeffbog 9/28/95) 00275 // 00276 // BUGBUG: need to add check for Nashville+ app 00277 if ((lpmsg->message == WM_RBUTTONUP) && !ppopupmenu->fNoNotify) { 00278 PPOPUPMENU ppopupActive; 00279 00280 if ((ppopupmenu->spwndActivePopup != NULL) 00281 && (ppopupActive = ((PMENUWND)(ppopupmenu->spwndActivePopup))->ppopupmenu) 00282 && MNIsItemSelected(ppopupActive)) 00283 { 00284 TL tlpwndNotify; 00285 ThreadLock( ppopupActive->spwndNotify, &tlpwndNotify ); 00286 xxxSendMessage(ppopupActive->spwndNotify, WM_MENURBUTTONUP, 00287 ppopupActive->posSelectedItem, (LPARAM)PtoH(ppopupActive->spmenu)); 00288 ThreadUnlock( &tlpwndNotify ); 00289 } 00290 } 00291 break; 00292 00293 case WM_LBUTTONUP: 00294 case WM_NCLBUTTONUP: 00295 // Commented out due to TandyT whinings... 00296 // if ((ppopupmenu->trackPopupMenuFlags & TPM_RIGHTBUTTON)) 00297 // break; 00298 00299 HandleButtonUp: 00300 if (!pMenuState->fButtonDown) { 00301 00302 /* 00303 * Don't care about this mouse up since we never saw the button 00304 * down for some reason. 00305 */ 00306 return TRUE; 00307 } 00308 00309 /* 00310 * Cancel the dragging state, if any. 00311 */ 00312 if (pMenuState->fDragAndDrop) { 00313 00314 UnlockMFMWFPWindow(&pMenuState->uButtonDownHitArea); 00315 pMenuState->fDragging = FALSE; 00316 00317 if (pMenuState->fIgnoreButtonUp) { 00318 pMenuState->fButtonDown = 00319 pMenuState->fIgnoreButtonUp = FALSE; 00320 return TRUE; 00321 } 00322 } 00323 00324 /* 00325 * Find out where this mouse up occurred. 00326 */ 00327 pMenuState->ptMouseLast.x = GET_X_LPARAM(lParam); 00328 pMenuState->ptMouseLast.y = GET_Y_LPARAM(lParam); 00329 cmdHitArea = xxxMNFindWindowFromPoint(ppopupmenu, &cmdItem, MAKEPOINTS(lParam)); 00330 00331 00332 /* 00333 * If this is not true, some the code below won't work right. 00334 */ 00335 UserAssert((cmdHitArea != MFMWFP_OFFMENU) || (cmdItem == 0)); 00336 UserAssert(cmdHitArea != 0x0000FFFF); 00337 00338 /* 00339 * Thread lock this if it is a pwnd. This certainly isn't the way 00340 * you'd implement this if you had locking to begin with. 00341 */ 00342 fThreadLock = IsMFMWFPWindow(cmdHitArea); 00343 if (fThreadLock) { 00344 ThreadLock((PWND)cmdHitArea, &tlpwndHitArea); 00345 } 00346 00347 00348 if (ppopupmenu->fHasMenuBar) { 00349 if (((cmdHitArea == MFMWFP_OFFMENU) && (cmdItem == 0)) || 00350 ((cmdHitArea == MFMWFP_NOITEM) && ppopupmenu->fIsSysMenu && ppopupmenu->fToggle)) 00351 // Button up occurred in some random spot. Terminate 00352 // menus and swallow the message. 00353 goto CancelOut; 00354 } else { 00355 if ((cmdHitArea == MFMWFP_OFFMENU) && (cmdItem == 0)) { 00356 if (!ppopupmenu->fFirstClick) { 00357 // 00358 // User upclicked in some random spot. Terminate 00359 // menus and don't swallow the message. 00360 // 00361 00362 // 00363 // Don't do anything with HWND here cuz the window is 00364 // destroyed after this SendMessage(). 00365 // 00366 // DONTREVALIDATE(); 00367 ThreadLock(ppopupmenu->spwndPopupMenu, &tlpwndT); 00368 xxxSendMessage(ppopupmenu->spwndPopupMenu, MN_CANCELMENUS, 0, 0); 00369 ThreadUnlock(&tlpwndT); 00370 goto Unlock; 00371 } 00372 } 00373 00374 ppopupmenu->fFirstClick = FALSE; 00375 } 00376 00377 if (cmdHitArea == MFMWFP_NOITEM) { 00378 // 00379 // This is a system menu or a menu bar and the button up 00380 // occurred on the system menu or on a menu bar item. 00381 // 00382 xxxMNButtonUp(ppopupmenu, pMenuState, cmdItem, 0); 00383 } else if ((cmdHitArea != MFMWFP_OFFMENU) && (cmdHitArea != MFMWFP_ALTMENU)) { 00384 // 00385 // Warning: It's common for the popup to go away during the 00386 // processing of this message, so don't add any code that 00387 // messes with hwnd after this call! 00388 // 00389 // DONTREVALIDATE(); 00390 00391 // 00392 // We send lParam (that has the mouse co-ords ) for the app 00393 // to get it in its SC_RESTORE/SC_MINIMIZE messages 3.0 00394 // compat 00395 // 00396 xxxSendMessage((PWND)cmdHitArea, MN_BUTTONUP, (DWORD)cmdItem, lParam); 00397 } else { 00398 pMenuState->fButtonDown = 00399 pMenuState->fButtonAlwaysDown = FALSE; 00400 } 00401 Unlock: 00402 if (fThreadLock) 00403 ThreadUnlock(&tlpwndHitArea); 00404 return TRUE; 00405 00406 00407 case WM_LBUTTONDBLCLK: 00408 case WM_NCLBUTTONDBLCLK: 00409 00410 // Commented out due to TandyT whinings... 00411 // if (ppopup->fRightButton) 00412 // break; 00413 pMenuState->mnFocus = MOUSEHOLD; 00414 cmdHitArea = xxxMNFindWindowFromPoint(ppopupmenu, &cmdItem, MAKEPOINTS(lParam)); 00415 if ((cmdHitArea == MFMWFP_OFFMENU) && (cmdItem == 0)) { 00416 // Dbl-clicked in middle of nowhere, so terminate menus, and 00417 // let button pass through. 00418 xxxMNDismiss(pMenuState); 00419 return TRUE; 00420 } else if (ppopupmenu->fHasMenuBar && (cmdHitArea == MFMWFP_ALTMENU)) { 00421 // 00422 // BOGUS 00423 // TREAT LIKE BUTTON DOWN since we didn't dblclk on same item. 00424 // 00425 xxxMNSwitchToAlternateMenu(ppopupmenu); 00426 cmdHitArea = MFMWFP_NOITEM; 00427 } 00428 00429 if (cmdHitArea == MFMWFP_NOITEM) 00430 xxxMNDoubleClick(pMenuState, ppopupmenu, cmdItem); 00431 else { 00432 UserAssert(cmdHitArea); 00433 00434 ThreadLock((PWND)cmdHitArea, &tlpwndHitArea); 00435 xxxSendMessage((PWND)cmdHitArea, MN_DBLCLK, 00436 (DWORD)cmdItem, 0L); 00437 ThreadUnlock(&tlpwndHitArea); 00438 } 00439 return TRUE; 00440 00441 case WM_KEYDOWN: 00442 case WM_SYSKEYDOWN: 00443 00444 /* 00445 * If mouse button is down, ignore keyboard input (fix #3899, IanJa) 00446 */ 00447 if (pMenuState->fButtonDown && (ch != VK_F1)) { 00448 00449 /* 00450 * Check if the user wants to cancel dragging. 00451 */ 00452 if (pMenuState->fDragging && (ch == VK_ESCAPE)) { 00453 RIPMSG0(RIP_WARNING, "xxxHandleMenuMessages: ESC while dragging"); 00454 pMenuState->fIgnoreButtonUp = TRUE; 00455 } 00456 00457 return TRUE; 00458 } 00459 pMenuState->mnFocus = KEYBDHOLD; 00460 switch (ch) { 00461 case VK_UP: 00462 case VK_DOWN: 00463 case VK_LEFT: 00464 case VK_RIGHT: 00465 case VK_RETURN: 00466 case VK_CANCEL: 00467 case VK_ESCAPE: 00468 case VK_MENU: 00469 case VK_F10: 00470 case VK_F1: 00471 if (ppopupmenu->spwndActivePopup) { 00472 ThreadLockAlways(ppopupmenu->spwndActivePopup, &tlpwndT); 00473 xxxSendMessage(ppopupmenu->spwndActivePopup, lpmsg->message, 00474 ch, 0L); 00475 ThreadUnlock(&tlpwndT); 00476 } else { 00477 xxxMNKeyDown(ppopupmenu, pMenuState, (UINT)ch); 00478 } 00479 break; 00480 00481 case VK_TAB: 00482 /* 00483 * People hit the ALT key now just to turn underlines ON in dialogs. 00484 * This throws them into "invisible menu mode". If they hit any char 00485 * at that point, we'll bail in xxxMNChar. But not so if they hit ctrl-tab, 00486 * which is used to navigate property sheets. So let's help them out. 00487 */ 00488 if (ppopupmenu->fIsMenuBar && (ppopupmenu->spwndActivePopup == NULL)) { 00489 xxxMNDismiss(pMenuState); 00490 return TRUE; 00491 } 00492 /* 00493 * Fall through 00494 */ 00495 00496 default: 00497 TranslateKey: 00498 if (!pMenuState->fModelessMenu) { 00499 xxxTranslateMessage(lpmsg, 0); 00500 } 00501 break; 00502 } 00503 return TRUE; 00504 00505 case WM_CHAR: 00506 case WM_SYSCHAR: 00507 if (ppopupmenu->spwndActivePopup) { 00508 ThreadLockAlways(ppopupmenu->spwndActivePopup, &tlpwndT); 00509 xxxSendMessage(ppopupmenu->spwndActivePopup, lpmsg->message, 00510 ch, 0L); 00511 ThreadUnlock(&tlpwndT); 00512 } else { 00513 xxxMNChar(ppopupmenu, pMenuState, (UINT)ch); 00514 } 00515 return TRUE; 00516 00517 case WM_SYSKEYUP: 00518 00519 /* 00520 * Ignore ALT and F10 keyup messages since they are handled on 00521 * the KEYDOWN message. 00522 */ 00523 if (ch == VK_MENU || ch == VK_F10) { 00524 if (gwinOldAppHackoMaticFlags & WOAHACK_CHECKALTKEYSTATE) { 00525 if (gwinOldAppHackoMaticFlags & WOAHACK_IGNOREALTKEYDOWN) { 00526 gwinOldAppHackoMaticFlags &= ~WOAHACK_IGNOREALTKEYDOWN; 00527 gwinOldAppHackoMaticFlags &= ~WOAHACK_CHECKALTKEYSTATE; 00528 } else 00529 gwinOldAppHackoMaticFlags |= WOAHACK_IGNOREALTKEYDOWN; 00530 } 00531 00532 return TRUE; 00533 } 00534 00535 /* 00536 ** fall thru ** 00537 */ 00538 00539 case WM_KEYUP: 00540 00541 /* 00542 * Do RETURNs on the up transition only 00543 */ 00544 goto TranslateKey; 00545 00546 case WM_SYSTIMER: 00547 00548 /* 00549 * Prevent the caret from flashing by eating all WM_SYSTIMER messages. 00550 */ 00551 return TRUE; 00552 00553 default: 00554 break; 00555 } 00556 00557 #if DBG 00558 /* 00559 * Nobody should be able to steal capture from modal menus. 00560 */ 00561 if (!pMenuState->fModelessMenu 00562 && !pMenuState->fInDoDragDrop 00563 && !ExitMenuLoop (pMenuState, ppopupmenu) ) { 00564 00565 UserAssert(PtiCurrent()->pq->QF_flags & QF_CAPTURELOCKED); 00566 UserAssert(PtiCurrent()->pq->spwndCapture == ppopupmenu->spwndNotify); 00567 } 00568 #endif 00569 00570 /* 00571 * We didn't handle this message 00572 */ 00573 return FALSE; 00574 } 00575 00576 /***************************************************************************\ 00577 * xxxEndMenuLoop 00578 * 00579 * Makes sure that the menu has been ended/canceled 00580 * 00581 * History: 00582 * 10/25/96 GerardoB Extracted from xxxMNLoop 00583 \***************************************************************************/ 00584 void xxxEndMenuLoop (PMENUSTATE pMenuState, PPOPUPMENU ppopupmenu) 00585 { 00586 00587 UserAssert(IsRootPopupMenu(ppopupmenu)); 00588 00589 if (ppopupmenu->fIsTrackPopup) { 00590 if (!ppopupmenu->fInCancel) { 00591 xxxMNDismiss(pMenuState); 00592 } 00593 } else { 00594 if (pMenuState->fUnderline) { 00595 TL tlpwnd; 00596 ThreadLock(ppopupmenu->spwndNotify, &tlpwnd); 00597 xxxDrawMenuBarUnderlines(ppopupmenu->spwndNotify, FALSE); 00598 ThreadUnlock(&tlpwnd); 00599 } 00600 if (!pMenuState->fInEndMenu) { 00601 xxxEndMenu(pMenuState); 00602 } 00603 } 00604 /* 00605 * If this is a modeless menu, make sure that the notification 00606 * window caption is drawn in the proper state 00607 */ 00608 if (pMenuState->fModelessMenu && (ppopupmenu->spwndNotify != NULL)) { 00609 PWND pwndNotify = ppopupmenu->spwndNotify; 00610 PTHREADINFO pti = GETPTI(pwndNotify); 00611 BOOL fFrameOn = (pti->pq == gpqForeground) 00612 && (pti->pq->spwndActive == pwndNotify); 00613 TL tlpwndNotify; 00614 00615 if (fFrameOn ^ !!TestWF(pwndNotify, WFFRAMEON)) { 00616 ThreadLockAlways(pwndNotify, &tlpwndNotify); 00617 xxxDWP_DoNCActivate(pwndNotify, 00618 (fFrameOn ? NCA_ACTIVE : NCA_FORCEFRAMEOFF), 00619 HRGN_FULL); 00620 ThreadUnlock(&tlpwndNotify); 00621 00622 } 00623 } 00624 } 00625 /***************************************************************************\ 00626 * xxxMenuLoop 00627 * 00628 * The menu processing entry point. 00629 * assumes: pMenuState->spwndMenu is the window which is the owner of the menu 00630 * we are processing. 00631 * 00632 * History: 00633 \***************************************************************************/ 00634 00635 int xxxMNLoop( 00636 PPOPUPMENU ppopupmenu, 00637 PMENUSTATE pMenuState, 00638 LPARAM lParam, 00639 BOOL fDblClk) 00640 { 00641 int hit; 00642 MSG msg; 00643 BOOL fSendIdle = TRUE; 00644 BOOL fInQueue = FALSE; 00645 DWORD menuState; 00646 PTHREADINFO pti; 00647 TL tlpwndT; 00648 00649 UserAssert(IsRootPopupMenu(ppopupmenu)); 00650 00651 pMenuState->fInsideMenuLoop = TRUE; 00652 pMenuState->cmdLast = 0; 00653 00654 pti = PtiCurrent(); 00655 00656 pMenuState->ptMouseLast.x = pti->ptLast.x; 00657 pMenuState->ptMouseLast.y = pti->ptLast.y; 00658 00659 /* 00660 * Set flag to false, so that we can track if windows have 00661 * been activated since entering this loop. 00662 */ 00663 pti->pq->QF_flags &= ~QF_ACTIVATIONCHANGE; 00664 00665 /* 00666 * Were we called from xxxMenuKeyFilter? If not, simulate a LBUTTONDOWN 00667 * message to bring up the popup. 00668 */ 00669 if (!pMenuState->fMenuStarted) { 00670 if (_GetKeyState(((ppopupmenu->fRightButton) ? 00671 VK_RBUTTON : VK_LBUTTON)) >= 0) { 00672 00673 /* 00674 * We think the mouse button should be down but the call to get key 00675 * state says different so we need to get outta menu mode. This 00676 * happens if clicking on the menu causes a sys modal message box to 00677 * come up before we can enter this stuff. For example, run 00678 * winfile, click on drive a: to see its tree. Activate some other 00679 * app, then open drive a: and activate winfile by clicking on the 00680 * menu. This causes a sys modal msg box to come up just before 00681 * entering menu mode. The user may have the mouse button up but 00682 * menu mode code thinks it is down... 00683 */ 00684 00685 /* 00686 * Need to notify the app we are exiting menu mode because we told 00687 * it we were entering menu mode just before entering this function 00688 * in xxxSysCommand()... 00689 */ 00690 if (!ppopupmenu->fNoNotify) { 00691 ThreadLock(ppopupmenu->spwndNotify, &tlpwndT); 00692 xxxSendNotifyMessage(ppopupmenu->spwndNotify, WM_EXITMENULOOP, 00693 ((ppopupmenu->fIsTrackPopup && !ppopupmenu->fIsSysMenu) ? TRUE : FALSE), 0); 00694 ThreadUnlock(&tlpwndT); 00695 } 00696 goto ExitMenuLoop; 00697 } 00698 00699 /* 00700 * Simulate a WM_LBUTTONDOWN message. 00701 */ 00702 if (!ppopupmenu->fIsTrackPopup) { 00703 00704 /* 00705 * For TrackPopupMenus, we do it in the TrackPopupMenu function 00706 * itself so we don't want to do it again. 00707 */ 00708 if (!xxxMNStartMenu(ppopupmenu, MOUSEHOLD)) { 00709 goto ExitMenuLoop; 00710 } 00711 } 00712 00713 if ((ppopupmenu->fRightButton)) { 00714 msg.message = (fDblClk ? WM_RBUTTONDBLCLK : WM_RBUTTONDOWN); 00715 msg.wParam = MK_RBUTTON; 00716 } else { 00717 msg.message = (fDblClk ? WM_LBUTTONDBLCLK : WM_LBUTTONDOWN); 00718 msg.wParam = MK_LBUTTON; 00719 } 00720 msg.lParam = lParam; 00721 msg.hwnd = HW(ppopupmenu->spwndPopupMenu); 00722 xxxHandleMenuMessages(&msg, pMenuState, ppopupmenu); 00723 } 00724 00725 /* 00726 * If this is a modeless menu, release capture, mark it in the menu state 00727 * and return. Decrement foreground lock count. 00728 */ 00729 if (pMenuState->fModelessMenu) { 00730 xxxMNReleaseCapture(); 00731 00732 DecSFWLockCount(); 00733 DBGDecModalMenuCount(); 00734 return 0; 00735 } 00736 00737 while (pMenuState->fInsideMenuLoop) { 00738 00739 /* 00740 * Is a message waiting for us? 00741 */ 00742 BOOL fPeek = xxxPeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_NOREMOVE); 00743 00744 Validateppopupmenu(ppopupmenu); 00745 00746 if (fPeek) { 00747 /* 00748 * Bail if we have been forced out of menu loop 00749 */ 00750 if (ExitMenuLoop (pMenuState, ppopupmenu)) { 00751 goto ExitMenuLoop; 00752 } 00753 00754 /* 00755 * Since we could have blocked in xxxWaitMessage (see last line 00756 * of loop) or xxxPeekMessage, reset the cached copy of 00757 * ptiCurrent()->pq: It could have changed if someone did a 00758 * DetachThreadInput() while we were away. 00759 */ 00760 if ((!ppopupmenu->fIsTrackPopup && 00761 pti->pq->spwndActive != ppopupmenu->spwndNotify && 00762 ((pti->pq->spwndActive == NULL) || !_IsChild(pti->pq->spwndActive, ppopupmenu->spwndNotify)))) { 00763 00764 /* 00765 * End menu processing if we are no longer the active window. 00766 * This is needed in case a system modal dialog box pops up 00767 * while we are tracking the menu code for example. It also 00768 * helps out Tracer if a macro is executed while a menu is down. 00769 */ 00770 00771 /* 00772 * Also, end menu processing if we think the mouse button is 00773 * down but it really isn't. (Happens if a sys modal dialog int 00774 * time dlg box comes up while we are in menu mode.) 00775 */ 00776 00777 goto ExitMenuLoop; 00778 } 00779 00780 if (ppopupmenu->fIsMenuBar && msg.message == WM_LBUTTONDBLCLK) { 00781 00782 /* 00783 * Was the double click on the system menu or caption? 00784 */ 00785 hit = FindNCHit(ppopupmenu->spwndNotify, (LONG)msg.lParam); 00786 if (hit == HTCAPTION) { 00787 PWND pwnd; 00788 PMENU pmenu; 00789 00790 /* 00791 * Get the message out of the queue since we're gonna 00792 * process it. 00793 */ 00794 xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE); 00795 if (ExitMenuLoop (pMenuState, ppopupmenu)) { 00796 goto ExitMenuLoop; 00797 } else { 00798 pwnd = ppopupmenu->spwndNotify; 00799 ThreadLockAlways(pwnd, &tlpwndT); 00800 pmenu = xxxGetSysMenuHandle(pwnd); 00801 UserAssert(pwnd == ppopupmenu->spwndNotify); 00802 00803 menuState = _GetMenuState(pmenu, SC_RESTORE & 0x0000FFF0, 00804 MF_BYCOMMAND); 00805 00806 /* 00807 * Only send the sys command if the item is valid. If 00808 * the item doesn't exist or is disabled, then don't 00809 * post the syscommand. Note that for win2 apps, we 00810 * always send the sys command if it is a child window. 00811 * This is so hosebag apps can change the sys menu. 00812 */ 00813 if (!(menuState & MFS_GRAYED)) { 00814 _PostMessage(pwnd, WM_SYSCOMMAND, SC_RESTORE, 0); 00815 } 00816 00817 /* 00818 * Get out of menu mode. 00819 */ 00820 ThreadUnlock(&tlpwndT); 00821 goto ExitMenuLoop; 00822 } 00823 } 00824 } 00825 00826 fInQueue = (msg.message == WM_LBUTTONDOWN || 00827 msg.message == WM_RBUTTONDOWN || 00828 msg.message == WM_NCLBUTTONDOWN || 00829 msg.message == WM_NCRBUTTONDOWN); 00830 00831 if (!fInQueue) { 00832 00833 /* 00834 * Note that we call xxxPeekMessage() with the filter 00835 * set to the message we got from xxxPeekMessage() rather 00836 * than simply 0, 0. This prevents problems when 00837 * xxxPeekMessage() returns something like a WM_TIMER, 00838 * and after we get here to remove it a WM_LBUTTONDOWN, 00839 * or some higher-priority input message, gets in the 00840 * queue and gets removed accidently. Basically we want 00841 * to be sure we remove the right message in this case. 00842 * NT bug 3852 was caused by this problem. 00843 * Set the TIF_IGNOREPLAYBACKDELAY bit in case journal playback 00844 * is happening: this allows us to proceed even if the hookproc 00845 * incorrectly returns a delay now. The bit will be cleared if 00846 * this happens, so we can see why the Peek-Remove below fails. 00847 * Lotus' Freelance Graphics tutorial does such bad journalling 00848 */ 00849 00850 pti->TIF_flags |= TIF_IGNOREPLAYBACKDELAY; 00851 if (!xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE)) { 00852 if (pti->TIF_flags & TIF_IGNOREPLAYBACKDELAY) { 00853 pti->TIF_flags &= ~TIF_IGNOREPLAYBACKDELAY; 00854 /* 00855 * It wasn't a bad journal playback: something else 00856 * made the previously peeked message disappear before 00857 * we could peek it again to remove it. 00858 */ 00859 RIPMSG1(RIP_WARNING, "Disappearing msg 0x%08lx", msg.message); 00860 goto NoMsg; 00861 } 00862 } 00863 pti->TIF_flags &= ~TIF_IGNOREPLAYBACKDELAY; 00864 } 00865 00866 if (!_CallMsgFilter(&msg, MSGF_MENU)) { 00867 if (!xxxHandleMenuMessages(&msg, pMenuState, ppopupmenu)) { 00868 xxxTranslateMessage(&msg, 0); 00869 xxxDispatchMessage(&msg); 00870 } 00871 00872 Validateppopupmenu(ppopupmenu); 00873 00874 if (ExitMenuLoop (pMenuState, ppopupmenu)) { 00875 goto ExitMenuLoop; 00876 } 00877 00878 if (pti->pq->QF_flags & QF_ACTIVATIONCHANGE) { 00879 00880 /* 00881 * Run away and exit menu mode if another window has become 00882 * active while a menu was up. 00883 */ 00884 RIPMSG0(RIP_WARNING, "Exiting menu mode: another window activated"); 00885 goto ExitMenuLoop; 00886 } 00887 00888 #if DBG 00889 /* 00890 * Nobody should be able to still capture from us. 00891 */ 00892 if (!pMenuState->fInDoDragDrop) { 00893 UserAssert(pti->pq->QF_flags & QF_CAPTURELOCKED); 00894 UserAssert(pti->pq->spwndCapture == ppopupmenu->spwndNotify); 00895 } 00896 #endif 00897 00898 /* 00899 * If we get a system timer, then it's like we're idle 00900 */ 00901 if (msg.message == WM_SYSTIMER) { 00902 goto NoMsg; 00903 } 00904 00905 /* 00906 * Don't set fSendIdle if we got these messages 00907 */ 00908 if ((msg.message == WM_TIMER) || (msg.message == WM_PAINT)) { 00909 continue; 00910 } 00911 00912 } else { 00913 if (fInQueue) 00914 xxxPeekMessage(&msg, NULL, msg.message, msg.message, 00915 PM_REMOVE); 00916 } 00917 00918 /* 00919 * Reenable WM_ENTERIDLE messages. 00920 */ 00921 fSendIdle = TRUE; 00922 00923 } else { 00924 NoMsg: 00925 /* 00926 * Bail if we have been forced out of menu loop 00927 */ 00928 if (ExitMenuLoop (pMenuState, ppopupmenu)) { 00929 goto ExitMenuLoop; 00930 } 00931 00932 UserAssert((ppopupmenu->spwndActivePopup == NULL) 00933 || (TestWF(ppopupmenu->spwndActivePopup, WFVISIBLE))); 00934 00935 00936 /* 00937 * If a hierarchical popup has been destroyed, this is a 00938 * good time to flush ppmDelayedFree 00939 */ 00940 if (ppopupmenu->fFlushDelayedFree) { 00941 MNFlushDestroyedPopups (ppopupmenu, FALSE); 00942 ppopupmenu->fFlushDelayedFree = FALSE; 00943 } 00944 00945 /* 00946 * We need to send the WM_ENTERIDLE message only the first time 00947 * there are no messages for us to process. Subsequent times we 00948 * need to yield via WaitMessage(). This will allow other tasks to 00949 * get some time while we have a menu down. 00950 */ 00951 if (fSendIdle) { 00952 if (ppopupmenu->spwndNotify != NULL) { 00953 ThreadLockAlways(ppopupmenu->spwndNotify, &tlpwndT); 00954 xxxSendMessage(ppopupmenu->spwndNotify, WM_ENTERIDLE, MSGF_MENU, 00955 (LPARAM)HW(ppopupmenu->spwndActivePopup)); 00956 ThreadUnlock(&tlpwndT); 00957 } 00958 fSendIdle = FALSE; 00959 } else { 00960 /* 00961 * If we're animating, sleep only 1 ms to reduce the chance 00962 * of jerky animation. 00963 * When not animating, this is the same as a xxxWaitMessage call 00964 */ 00965 xxxSleepThread(QS_ALLINPUT | QS_EVENT, (pMenuState->hdcWndAni != NULL), TRUE); 00966 } 00967 00968 } /* if (PeekMessage(&msg, NULL, 0, 0, PM_NOYIELD)) else */ 00969 00970 } /* end while (fInsideMenuLoop) */ 00971 00972 00973 00974 ExitMenuLoop: 00975 pMenuState->fInsideMenuLoop = FALSE; 00976 pMenuState->fModelessMenu = FALSE; 00977 00978 /* 00979 * Make sure that the menu has been ended/canceled 00980 */ 00981 xxxEndMenuLoop (pMenuState, ppopupmenu); 00982 00983 xxxMNReleaseCapture(); 00984 00985 // Throw in an extra peek here when we exit the menu loop to ensure that the input queue 00986 // for this thread gets unlocked if there is no more input left for him. 00987 xxxPeekMessage(&msg, NULL, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_NOYIELD | PM_NOREMOVE); 00988 return(pMenuState->cmdLast); 00989 } /* xxxMenuLoop() */

Generated on Sat May 15 19:40:51 2004 for test by doxygen 1.3.7