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

menu.c

Go to the documentation of this file.
00001 00002 /**************************** Module Header ********************************\ 00003 * Module Name: menu.c 00004 * 00005 * Copyright (c) 1985 - 1999, Microsoft Corporation 00006 * 00007 * Keyboard Accelerator Routines 00008 * 00009 * History: 00010 * 05-25-91 Mikehar Ported from Win3.1 00011 \***************************************************************************/ 00012 00013 #include "precomp.h" 00014 #pragma hdrstop 00015 00016 /***********************************************************************\ 00017 * MNGetpItemIndex 00018 * 00019 * 11/19/96 GerardoB Created 00020 \***********************************************************************/ 00021 #if DBG 00022 UINT DBGMNGetpItemIndex(PMENU pmenu, PITEM pitem) 00023 { 00024 UINT uiPos; 00025 UserAssert((ULONG_PTR)pitem >= (ULONG_PTR)pmenu->rgItems); 00026 uiPos = _MNGetpItemIndex(pmenu, pitem); 00027 UserAssert(uiPos < pmenu->cItems); 00028 return uiPos; 00029 } 00030 #endif 00031 /**************************************************************************\ 00032 * xxxMNDismiss 00033 * 00034 * 12/03/96 GerardoB Created 00035 \**************************************************************************/ 00036 void xxxMNDismiss (PMENUSTATE pMenuState) 00037 { 00038 xxxMNCancel(pMenuState, 0, 0, 0); 00039 } 00040 00041 /***************************************************************************\ 00042 * MNFadeSelection 00043 * 00044 * 2/5/1998 vadimg created 00045 \***************************************************************************/ 00046 00047 BOOL MNFadeSelection(PMENU pmenu, PITEM pitem) 00048 { 00049 PWND pwnd; 00050 HDC hdc; 00051 RECT rc; 00052 PPOPUPMENU ppopup; 00053 00054 if (!TestALPHA(SELECTIONFADE)) 00055 return FALSE; 00056 00057 /* 00058 * Get the window for the currently active popup menu. 00059 */ 00060 if ((ppopup = MNGetPopupFromMenu(pmenu, NULL)) == NULL) 00061 return FALSE; 00062 00063 if ((pwnd = ppopup->spwndPopupMenu) == NULL) 00064 return FALSE; 00065 00066 rc.left = pwnd->rcClient.left + pitem->xItem; 00067 rc.top = pwnd->rcClient.top + pitem->yItem; 00068 rc.right = rc.left + pitem->cxItem; 00069 rc.bottom = rc.top + pitem->cyItem; 00070 00071 /* 00072 * Initialize the fade animation and get the DC to draw the selection into. 00073 */ 00074 if ((hdc = CreateFade(NULL, &rc, CMS_SELECTIONFADE, 0)) == NULL) 00075 return FALSE; 00076 00077 /* 00078 * Read the current menu selection right from the screen, since the menu 00079 * is still visible and it's always on top. In the worst case we could 00080 * offset the origin of the DC and call xxxDrawMenuItem, but just reading 00081 * from the screen is much faster. 00082 */ 00083 GreBitBlt(hdc, 0, 0, pitem->cxItem, pitem->cyItem, gpDispInfo->hdcScreen, 00084 rc.left, rc.top, SRCCOPY, 0); 00085 00086 ShowFade(); 00087 00088 return TRUE; 00089 } 00090 00091 /**************************************************************************\ 00092 * xxxMNDismissWithNotify 00093 * 00094 * Generates parameters for WM_COMMAND or WM_SYSCOMMAND message. 00095 * 00096 * 12/03/96 GerardoB Created 00097 \**************************************************************************/ 00098 void xxxMNDismissWithNotify (PMENUSTATE pMenuState, PMENU pmenu, PITEM pitem, 00099 UINT uPos, LPARAM lParam) 00100 { 00101 UINT uMsg; 00102 UINT uCmd; 00103 00104 if (pMenuState->pGlobalPopupMenu->fIsSysMenu) { 00105 uMsg = WM_SYSCOMMAND; 00106 uCmd = pitem->wID; 00107 /* lParam set by caller */ 00108 } else if (pMenuState->fNotifyByPos) { 00109 uMsg = WM_MENUCOMMAND; 00110 uCmd = uPos; 00111 lParam = (LPARAM)PtoHq(pmenu); 00112 } else { 00113 uMsg = WM_COMMAND; 00114 uCmd = pitem->wID; 00115 lParam = 0; 00116 } 00117 00118 /* 00119 * The menu is about to go away, see if we want to fade out the selection. 00120 */ 00121 if (MNFadeSelection(pmenu, pitem)) { 00122 StartFade(); 00123 } 00124 00125 /* 00126 * Dismiss the menu. 00127 */ 00128 xxxMNCancel(pMenuState, uMsg, uCmd, lParam); 00129 } 00130 00131 /**************************************************************************\ 00132 * MNGetpItem 00133 * 00134 * 11/15/96 GerardoB Created 00135 \**************************************************************************/ 00136 PITEM MNGetpItem(PPOPUPMENU ppopup, UINT uIndex) 00137 { 00138 if ((ppopup == NULL) 00139 || (uIndex >= ppopup->spmenu->cItems)) { 00140 00141 return NULL; 00142 } 00143 00144 return ppopup->spmenu->rgItems + uIndex; 00145 } 00146 /***************************************************************************\ 00147 * MNSetCapture 00148 * 00149 * History: 00150 * 11/18/96 GerardoB Created 00151 \***************************************************************************/ 00152 void xxxMNSetCapture (PPOPUPMENU ppopup) 00153 { 00154 PTHREADINFO ptiCurrent = PtiCurrent(); 00155 00156 /* 00157 * Set the capture and lock it so no one will be able to steal it 00158 * from us. 00159 */ 00160 xxxCapture(ptiCurrent, ppopup->spwndNotify, SCREEN_CAPTURE); 00161 UserAssert (ptiCurrent->pq->spwndCapture == ppopup->spwndNotify); 00162 ptiCurrent->pq->QF_flags |= QF_CAPTURELOCKED; 00163 ptiCurrent->pMenuState->fSetCapture = TRUE; 00164 #if DBG 00165 /* 00166 * Unless we're in the foreground, this menu mode won't go away 00167 * when the user clicks outside the menu. This is because only 00168 * the foreground queue capture sees clicks outside its windows. 00169 */ 00170 if (ptiCurrent->pq != gpqForeground) { 00171 RIPMSG0(RIP_WARNING, "xxxMNSetCapture: Menu mode is not in foreground queue"); 00172 } 00173 #endif 00174 } 00175 /***************************************************************************\ 00176 * MNReleaseCapture 00177 * 00178 * History: 00179 * 11/18/96 GerardoB Created 00180 \***************************************************************************/ 00181 void xxxMNReleaseCapture (void) 00182 { 00183 PTHREADINFO ptiCurrent = PtiCurrent(); 00184 00185 /* 00186 * Bail if we didn't set capture 00187 */ 00188 if ((ptiCurrent->pMenuState == NULL) || 00189 (! ptiCurrent->pMenuState->fSetCapture)) { 00190 return; 00191 } 00192 ptiCurrent->pMenuState->fSetCapture = FALSE; 00193 00194 /* 00195 * Unlock capture and release it. 00196 */ 00197 PtiCurrent()->pq->QF_flags &= ~QF_CAPTURELOCKED; 00198 xxxReleaseCapture(); 00199 } 00200 /***************************************************************************\ 00201 * MNCheckButtonDownState 00202 * 00203 * History: 00204 * 11/14/96 GerardoB Created 00205 \***************************************************************************/ 00206 void MNCheckButtonDownState (PMENUSTATE pMenuState) 00207 { 00208 /* 00209 * Modeless menus don't capture the mouse so when a mouse down 00210 * goes off the window, we need to keep watching its state. 00211 * We also might not see the button up when going on DoDragDrop loop 00212 */ 00213 UserAssert(pMenuState->fDragAndDrop || pMenuState->fModelessMenu); 00214 pMenuState->fButtonDown = ((_GetKeyState(pMenuState->vkButtonDown) & 0x8000) != 0); 00215 if (!pMenuState->fButtonDown) { 00216 pMenuState->fDragging = 00217 pMenuState->fIgnoreButtonUp = FALSE; 00218 UnlockMFMWFPWindow(&pMenuState->uButtonDownHitArea); 00219 } 00220 } 00221 /***************************************************************************\ 00222 * GetMenuStateWindow 00223 * 00224 * This function is called when we need to post a message to the menu loop. 00225 * The actual pwnd is not important since we just want to reach 00226 * xxxHandleMenuMessages or xxxMenuWindowProc. So we just pick a window that 00227 * has a good chance to be around as long as we are in menu mode. 00228 * 00229 * History: 00230 * 10/31/96 GerardoB Created 00231 \***************************************************************************/ 00232 PWND GetMenuStateWindow (PMENUSTATE pMenuState) 00233 { 00234 if (pMenuState == NULL) { 00235 return NULL; 00236 } else if (pMenuState->pGlobalPopupMenu->fIsTrackPopup) { 00237 return pMenuState->pGlobalPopupMenu->spwndPopupMenu; 00238 } else if (pMenuState->pGlobalPopupMenu->spwndNextPopup != NULL) { 00239 return pMenuState->pGlobalPopupMenu->spwndNextPopup; 00240 } else { 00241 return pMenuState->pGlobalPopupMenu->spwndActivePopup; 00242 } 00243 } 00244 /***************************************************************************\ 00245 * UnlockPopupMenuWindow 00246 * 00247 * This function is called when locking/unlocking a menu into a popup structure. 00248 * It makes sure that pmenu doesn't keep the notification window locked 00249 * unneccessarily 00250 * 00251 * It unlocks pmenu->spwndNotify if the menu it's not locked into pmenu->spwndNotify 00252 * itself AND it's currently locked to pwnd. 00253 * It's also unlocked if pmenu->spwndNotify is marked as destroyed 00254 * 00255 * History: 00256 * 10/15/96 GerardoB Created 00257 \***************************************************************************/ 00258 void UnlockPopupMenuWindow(PMENU pmenu, PWND pwnd) 00259 { 00260 /* 00261 * Bail if there's nothing to unlock 00262 */ 00263 if ((pmenu == NULL) 00264 || (pmenu->spwndNotify == NULL)) { 00265 return; 00266 } 00267 /* 00268 * if pmenu->spwndNotify owns the menu, bail 00269 */ 00270 if ((pmenu == pmenu->spwndNotify->spmenu) 00271 || (pmenu == pmenu->spwndNotify->spmenuSys)) { 00272 return; 00273 } 00274 /* 00275 * If pwnd doesn't own the menu, and pmenu->spwndNotify is not destroyed, bail 00276 */ 00277 if ((pwnd != pmenu->spwndNotify) 00278 && !TestWF(pmenu->spwndNotify, WFDESTROYED)) { 00279 return; 00280 } 00281 /* 00282 * Unlock it 00283 */ 00284 Unlock(&pmenu->spwndNotify); 00285 return; 00286 } 00287 /***************************************************************************\ 00288 * LockPopupMenu 00289 * 00290 * Locks a given menu into a popup strucuture and makes the 00291 * popup's notification window the owner of the menu 00292 * 00293 * History: 00294 * 10/15/96 GerardoB Created 00295 \***************************************************************************/ 00296 PVOID LockPopupMenu(PPOPUPMENU ppopup, PMENU * pspmenu, PMENU pmenu) 00297 { 00298 /* 00299 * If you hit this assertion, you're probably not passing the right thing 00300 */ 00301 UserAssert((pspmenu == &ppopup->spmenu) || (pspmenu == &ppopup->spmenuAlternate)); 00302 Validateppopupmenu(ppopup); 00303 /* 00304 * This won't work properly if the popup hasn't locked the notification 00305 * window. 00306 */ 00307 UserAssert(ppopup->spwndNotify != NULL); 00308 00309 /* 00310 * When using modeless menus, menus can be shared by several active popups. 00311 * If the menu has owner draw items, the app better knows how to draw them 00312 * correctly. This shouldn't happen with modal menus though 00313 */ 00314 #if DBG 00315 if ((*pspmenu != NULL) 00316 && ((*pspmenu)->spwndNotify != NULL) 00317 && ((*pspmenu)->spwndNotify != ppopup->spwndNotify)) { 00318 00319 RIPMSG3(RIP_WARNING, "LockPopupMenu: Current Menu %#p shared by %#p and %#p", 00320 *pspmenu, (*pspmenu)->spwndNotify, ppopup->spwndNotify); 00321 } 00322 #endif 00323 00324 /* 00325 * Unlock the current's menu spwndNotify if needed 00326 */ 00327 UnlockPopupMenuWindow(*pspmenu, ppopup->spwndNotify); 00328 00329 /* 00330 * Lock the notification window into the menu structure 00331 */ 00332 if (pmenu != NULL) { 00333 00334 /* 00335 * Display a warning if this menu is being shared 00336 */ 00337 #if DBG 00338 if ((pmenu->spwndNotify != NULL) 00339 && (pmenu->spwndNotify != ppopup->spwndNotify) 00340 && (pmenu != pmenu->spwndNotify->head.rpdesk->spmenuDialogSys)) { 00341 00342 RIPMSG3(RIP_WARNING, "LockPopupMenu: New Menu %#p shared by %#p and %#p", 00343 pmenu, pmenu->spwndNotify, ppopup->spwndNotify); 00344 } 00345 #endif 00346 00347 /* 00348 * spwndNotify "owns" this menu now. 00349 */ 00350 Lock(&pmenu->spwndNotify, ppopup->spwndNotify); 00351 } 00352 00353 /* 00354 * Lock the menu into the popup structure (unlock the previous one) 00355 */ 00356 return Lock(pspmenu, pmenu); 00357 } 00358 /***************************************************************************\ 00359 * UnlockPopupMenu 00360 * 00361 * Unlocks a given menu from a popup strucuture and makes sure that the 00362 * menu is no longer "owned" by the popup's notification window; if needed 00363 * 00364 * History: 00365 * 10/15/96 GerardoB Created 00366 \***************************************************************************/ 00367 PVOID UnlockPopupMenu(PPOPUPMENU ppopup, PMENU * pspmenu) 00368 { 00369 /* 00370 * If you hit this assertion, you're probably not passing the right thing 00371 */ 00372 UserAssert((pspmenu == &ppopup->spmenu) || (pspmenu == &ppopup->spmenuAlternate)); 00373 /* 00374 * If nothing is locked, bail. 00375 */ 00376 if (*pspmenu == NULL) { 00377 return NULL; 00378 } 00379 00380 /* 00381 * This won't work properly if the popup already unlocked the notification 00382 * window. However, this can happen with the root popup if the 00383 * notification window gets destroyed while in menu mode. 00384 */ 00385 UserAssert((ppopup->spwndNotify != NULL) || IsRootPopupMenu(ppopup)); 00386 00387 /* 00388 * When using modeless menus, menus can be shared by several active 00389 * popups/notification windows. If the menu has owner draw items, 00390 * the app better knows how to paint them right. It shouldn't 00391 * happen with modal menus though. 00392 */ 00393 #if DBG 00394 if (((*pspmenu)->spwndNotify != NULL) 00395 && (ppopup->spwndNotify != NULL) 00396 && (ppopup->spwndNotify != (*pspmenu)->spwndNotify)) { 00397 00398 RIPMSG3(RIP_WARNING, "UnlockPopupMenu: Menu %#p shared by %#p and %#p", 00399 *pspmenu, (*pspmenu)->spwndNotify, ppopup->spwndNotify); 00400 } 00401 #endif 00402 00403 /* 00404 * Unlock the menu's spwndNotify if needed 00405 */ 00406 UnlockPopupMenuWindow(*pspmenu, ppopup->spwndNotify); 00407 00408 /* 00409 * Unlock the menu from the popup structure 00410 */ 00411 return Unlock(pspmenu); 00412 } 00413 /***************************************************************************\ 00414 * LockWndMenu 00415 * 00416 * Locks a given menu into a window structure and locks the window into 00417 * the menu strucuture. 00418 * 00419 * History: 00420 * 10/15/96 GerardoB Created 00421 \***************************************************************************/ 00422 PVOID LockWndMenu(PWND pwnd, PMENU * pspmenu, PMENU pmenu) 00423 { 00424 /* 00425 * If you hit this assertion, you're probably not passing the right thing 00426 */ 00427 UserAssert((pspmenu == &pwnd->spmenu) || (pspmenu == &pwnd->spmenuSys)); 00428 00429 /* 00430 * If the current menu is owned by this window, unlock it 00431 */ 00432 if ((*pspmenu != NULL) && ((*pspmenu)->spwndNotify == pwnd)) { 00433 Unlock(&((*pspmenu)->spwndNotify)); 00434 } 00435 00436 /* 00437 * If nobody owns the new menu, make this window the owner 00438 */ 00439 if ((pmenu != NULL) && (pmenu->spwndNotify == NULL)) { 00440 Lock(&pmenu->spwndNotify, pwnd); 00441 } 00442 00443 /* 00444 * Lock the menu into the window structure (unlock the previous menu) 00445 */ 00446 return Lock(pspmenu, pmenu); 00447 00448 } 00449 /***************************************************************************\ 00450 * UnlockWndMenu 00451 * 00452 * Unlocks a given menu from a window strucutre and the window from the 00453 * menu strucuture 00454 * 00455 * History: 00456 * 10/15/96 GerardoB Created 00457 \***************************************************************************/ 00458 PVOID UnlockWndMenu(PWND pwnd, PMENU * pspmenu) 00459 { 00460 /* 00461 * If you hit this assertion, you're probably not passing the right thing 00462 */ 00463 UserAssert((pspmenu == &pwnd->spmenu) || (pspmenu == &pwnd->spmenuSys)); 00464 00465 /* 00466 * If nothing is locked, bail 00467 */ 00468 if (*pspmenu == NULL) { 00469 return NULL; 00470 } 00471 00472 /* 00473 * If this window owns the menu, unlock it from the menu strucutre 00474 */ 00475 if (pwnd == (*pspmenu)->spwndNotify) { 00476 Unlock(&((*pspmenu)->spwndNotify)); 00477 } 00478 00479 /* 00480 * Unlock the menu from the window structure 00481 */ 00482 return Unlock(pspmenu); 00483 } 00484 00485 00486 /***************************************************************************\ 00487 * MNSetTop 00488 * 00489 * sets the first visible item in a scrollable menu to the given iNewTop; 00490 * returns TRUE if iTop was changed; FALSE if iNewTop is already the 00491 * first visible item 00492 * 00493 * 08/13/96 GerardoB Ported From Memphis. 00494 \***************************************************************************/ 00495 BOOL xxxMNSetTop(PPOPUPMENU ppopup, int iNewTop) 00496 { 00497 PMENU pMenu = ppopup->spmenu; 00498 int dy; 00499 00500 if (iNewTop < 0) { 00501 iNewTop = 0; 00502 } else if (iNewTop > pMenu->iMaxTop) { 00503 iNewTop = pMenu->iMaxTop; 00504 } 00505 00506 /* 00507 * If no change, done 00508 */ 00509 if (iNewTop == pMenu->iTop) { 00510 return FALSE; 00511 } 00512 00513 #if DBG 00514 /* 00515 * We're going to scroll, so validate iMaxTop, cyMax and cyMenu 00516 */ 00517 UserAssert((pMenu->cyMax == 0) || (pMenu->cyMax >= pMenu->cyMenu)); 00518 if ((UINT)pMenu->iMaxTop < pMenu->cItems) { 00519 PITEM pitemLast = pMenu->rgItems + pMenu->cItems - 1; 00520 PITEM pitemMaxTop = pMenu->rgItems + pMenu->iMaxTop; 00521 UINT uHeight = pitemLast->yItem + pitemLast->cyItem - pitemMaxTop->yItem; 00522 UserAssert(uHeight <= pMenu->cyMenu); 00523 /* 00524 * Let's guess a max item height 00525 */ 00526 UserAssert(pMenu->cyMenu - uHeight <= 2 * pitemLast->cyItem); 00527 } else { 00528 UserAssert((UINT)pMenu->iMaxTop < pMenu->cItems); 00529 } 00530 #endif 00531 00532 00533 /* 00534 * if we've made it this far, the new iTop WILL change -- thus if the 00535 * current iTop is at the top it won't be after this change -- same goes 00536 * for iTop at the bottom 00537 */ 00538 if (pMenu->dwArrowsOn == MSA_ATTOP) { 00539 pMenu->dwArrowsOn = MSA_ON; 00540 if (pMenu->hbrBack == NULL) { 00541 MNDrawArrow(NULL, ppopup, MFMWFP_UPARROW); 00542 } 00543 } else if (pMenu->dwArrowsOn == MSA_ATBOTTOM) { 00544 pMenu->dwArrowsOn = MSA_ON; 00545 if (pMenu->hbrBack == NULL) { 00546 MNDrawArrow(NULL, ppopup, MFMWFP_DOWNARROW); 00547 } 00548 } 00549 00550 UserAssert((UINT)iNewTop < pMenu->cItems); 00551 dy = MNGetToppItem(pMenu)->yItem - (pMenu->rgItems + iNewTop)->yItem; 00552 00553 if ((dy > 0 ? dy : -dy) > (int)pMenu->cyMenu) { 00554 xxxInvalidateRect(ppopup->spwndPopupMenu, NULL, TRUE); 00555 } else { 00556 xxxScrollWindowEx(ppopup->spwndPopupMenu, 0, dy, NULL, NULL, NULL, NULL, SW_INVALIDATE | SW_ERASE); 00557 } 00558 00559 pMenu->iTop = iNewTop; 00560 00561 if (iNewTop == 0) { 00562 pMenu->dwArrowsOn = MSA_ATTOP; 00563 if (pMenu->hbrBack == NULL) { 00564 MNDrawArrow(NULL, ppopup, MFMWFP_UPARROW); 00565 } 00566 } else if (iNewTop == pMenu->iMaxTop) { 00567 pMenu->dwArrowsOn = MSA_ATBOTTOM; 00568 if (pMenu->hbrBack == NULL) { 00569 MNDrawArrow(NULL, ppopup, MFMWFP_DOWNARROW); 00570 } 00571 } 00572 00573 if (pMenu->hbrBack != NULL) { 00574 MNDrawFullNC(ppopup->spwndPopupMenu, NULL, ppopup); 00575 } 00576 00577 return TRUE; 00578 00579 } 00580 00581 /***************************************************************************\ 00582 * xxxMNDoScroll 00583 * 00584 * scrolls a scrollable menu (ppopup) if the given position (uArrow) is one of 00585 * the menu scroll arrows and sets a timer to auto-scroll when necessary; 00586 * returns FALSE if the given position was not a menu scroll arrow; returns 00587 * TRUE otherwise 00588 * 00589 * 08/13/96 GerardoB Ported From Memphis. 00590 \***************************************************************************/ 00591 BOOL xxxMNDoScroll(PPOPUPMENU ppopup, UINT uArrow, BOOL fSetTimer) 00592 { 00593 int iScrollTop = ppopup->spmenu->iTop; 00594 00595 if (uArrow == MFMWFP_UPARROW) { 00596 iScrollTop--; 00597 } else if (uArrow == MFMWFP_DOWNARROW) { 00598 iScrollTop++; 00599 } else { 00600 return FALSE; 00601 } 00602 00603 if (!xxxMNSetTop(ppopup, iScrollTop)) { 00604 if (!fSetTimer) { 00605 _KillTimer(ppopup->spwndPopupMenu, uArrow); 00606 } 00607 } else { 00608 /* 00609 * Set this timer just like we do in the scrollbar code: 00610 * the first time we wait a little longer. 00611 */ 00612 _SetTimer(ppopup->spwndPopupMenu, uArrow, 00613 (fSetTimer ? gpsi->dtScroll : gpsi->dtScroll / 4), NULL); 00614 } 00615 00616 return TRUE; 00617 } 00618 00619 /***************************************************************************\ 00620 * MNCheckScroll 00621 * 00622 * checks to see if the given menu (pMenu) can be displayed in it's entirety 00623 * or if it can't, in which case it sets the menu to be scrollable 00624 * 00625 * 08/13/96 GerardoB Ported From Memphis. 00626 \***************************************************************************/ 00627 int MNCheckScroll(PMENU pMenu, PMONITOR pMonitor) 00628 { 00629 int i; 00630 UINT cyMax; 00631 PITEM pItem; 00632 00633 /* 00634 * Max height that fits on the monitor 00635 */ 00636 cyMax = (pMonitor->rcMonitor.bottom - pMonitor->rcMonitor.top); 00637 00638 /* 00639 * If the menu has a valid max height, use it 00640 */ 00641 if ((pMenu->cyMax != 0) && (pMenu->cyMax < cyMax)) { 00642 cyMax = pMenu->cyMax; 00643 } 00644 00645 /* 00646 * Bail if menu is either empty, multicolumn, or able to fit 00647 * without scrolling 00648 */ 00649 if ((pMenu->rgItems == 0) 00650 || (pMenu->rgItems->cxItem != pMenu->cxMenu) 00651 || (pMenu->cyMenu + (2 * SYSMET(CYFIXEDFRAME)) <= cyMax)) { 00652 00653 pMenu->dwArrowsOn = MSA_OFF; 00654 pMenu->iTop = 0; 00655 pMenu->iMaxTop = 0; 00656 return pMenu->cyMenu; 00657 } 00658 00659 /* 00660 * Max client height 00661 */ 00662 cyMax -= 2 * (SYSMET(CYFIXEDFRAME) + gcyMenuScrollArrow); 00663 00664 /* 00665 * Determine the menu height 00666 * Find the first item that won't fit. 00667 */ 00668 pItem = pMenu->rgItems; 00669 for (i = 0; i < (int)pMenu->cItems; i++, pItem++) { 00670 if (pItem->yItem > (UINT)cyMax) { 00671 break; 00672 } 00673 } 00674 if (i != 0) { 00675 pItem--; 00676 } 00677 pMenu->cyMenu = pItem->yItem; 00678 00679 /* 00680 * compute the last possible top item when all remaining items are fully 00681 * visible 00682 */ 00683 cyMax = 0; 00684 i = pMenu->cItems - 1; 00685 pItem = pMenu->rgItems + i; 00686 for (; i >= 0; i--, pItem--) { 00687 cyMax += pItem->cyItem; 00688 if (cyMax > pMenu->cyMenu) { 00689 break; 00690 } 00691 } 00692 if ((UINT)i != pMenu->cItems - 1) { 00693 i++; 00694 } 00695 pMenu->iMaxTop = i; 00696 00697 /* 00698 * Update top item and scroll state 00699 */ 00700 if (pMenu->iTop > i) { 00701 pMenu->iTop = i; 00702 } 00703 00704 if (pMenu->iTop == i) { 00705 pMenu->dwArrowsOn = MSA_ATBOTTOM; 00706 } else if (pMenu->iTop == 0) { 00707 pMenu->dwArrowsOn = MSA_ATTOP; 00708 } else { 00709 pMenu->dwArrowsOn = MSA_ON; 00710 } 00711 00712 /* 00713 * This is funtion is called by MN_SIZEWINDOW which doesn't check 00714 * if the scroll bars are present but simply adds (2 * SYSMET(CYFIXEDFRAME)) 00715 * to calculate the window height. So we add the scrollbars height 00716 * here. (I believe MN_SIZEWINDOW is a private-but-publicly-known message) 00717 */ 00718 return (pMenu->cyMenu + (2 * gcyMenuScrollArrow)); 00719 } 00720 00721 /***************************************************************************\ 00722 * MNIsPopupItem 00723 * 00724 * 00725 \***************************************************************************/ 00726 00727 BOOL MNIsPopupItem(ITEM *lpItem) 00728 { 00729 return((lpItem) && (lpItem->spSubMenu) && 00730 !TestMFS(lpItem, MFS_GRAYED)); 00731 } 00732 00733 /***************************************************************************\ 00734 * Validateppopupmenu 00735 * 00736 * 05-15-96 GerardoB Created 00737 \***************************************************************************/ 00738 #if DBG 00739 void Validateppopupmenu (PPOPUPMENU ppopupmenu) 00740 { 00741 UserAssert(ppopupmenu != NULL); 00742 try { 00743 UserAssert(!ppopupmenu->fFreed); 00744 00745 /* 00746 * If this popup is being destroyed to soon, ppopupmenuRoot can be NULL 00747 */ 00748 if (ppopupmenu->ppopupmenuRoot != NULL) { 00749 if (ppopupmenu->ppopupmenuRoot != ppopupmenu) { 00750 /* 00751 * This must be a hierarchical popup 00752 */ 00753 UserAssert(ppopupmenu->spwndPrevPopup != NULL); 00754 UserAssert(!ppopupmenu->fIsMenuBar && !ppopupmenu->fIsTrackPopup); 00755 Validateppopupmenu(ppopupmenu->ppopupmenuRoot); 00756 } else { 00757 /* 00758 * This must be the root popupmenu 00759 */ 00760 UserAssert(ppopupmenu->spwndPrevPopup == NULL); 00761 UserAssert(ppopupmenu->fIsMenuBar || ppopupmenu->fIsTrackPopup); 00762 } 00763 } 00764 00765 /* 00766 * This can be NULL when called from xxxDeleteThreadInfo 00767 */ 00768 if (ppopupmenu->spwndPopupMenu != NULL) { 00769 UserAssert(ppopupmenu->spwndPopupMenu == RevalidateCatHwnd(HW(ppopupmenu->spwndPopupMenu))); 00770 } 00771 00772 /* 00773 * This can be NULL when called from xxxDestroyWindow (spwndNotify) 00774 * or from xxxDeleteThreadInfo 00775 */ 00776 if (ppopupmenu->spwndNotify != NULL) { 00777 UserAssert(ppopupmenu->spwndNotify == RevalidateCatHwnd(HW(ppopupmenu->spwndNotify))); 00778 } 00779 00780 } except (W32ExceptionHandler(FALSE, RIP_ERROR)) { 00781 RIPMSG1(RIP_ERROR, "Validateppopupmenu: Invalid popup:%#p", ppopupmenu); 00782 } 00783 } 00784 #endif 00785 /***************************************************************************\ 00786 * xxxSwitchToAlternateMenu 00787 * 00788 * Switches to the alternate popupmenu. Returns TRUE if we switch 00789 * else FALSE. 00790 * 00791 * History: 00792 * 05-25-91 Mikehar Ported from Win3.1 00793 \***************************************************************************/ 00794 00795 BOOL xxxMNSwitchToAlternateMenu( 00796 PPOPUPMENU ppopupmenu) 00797 { 00798 PMENU pmenuSwap = NULL; 00799 PMENUSTATE pMenuState; 00800 TL tlpwndPopupMenu; 00801 00802 if (!ppopupmenu->fIsMenuBar || !ppopupmenu->spmenuAlternate) { 00803 /* 00804 * Do nothing if no menu or not top level menu bar. 00805 * ppopupmenu->spmenuAlternate can be NULL when an app has 00806 * either system menu or menu bar but not both. If that menu 00807 * has only one popup that it's not dropped, then hitting 00808 * VK_RIGHT or VK_LEFT causes xxxMNKeyDown to end up here. 00809 * ppopupmenu->fIsMenuBar can be false when you drop the 00810 * system menu of an app with no menu bar; then hit VK_RIGHT 00811 * on an item that doesn't have a popup and you'll get here 00812 * There might be some other situations like this; in any case 00813 * the assertion got to go 00814 */ 00815 return FALSE; 00816 } 00817 00818 /* 00819 * If we're getting out of menu mode, do nothing 00820 */ 00821 if (ppopupmenu->fDestroyed) { 00822 return FALSE; 00823 } 00824 00825 /* 00826 * Select no items in the current menu. 00827 */ 00828 ThreadLock(ppopupmenu->spwndPopupMenu, &tlpwndPopupMenu); 00829 UserAssert(ppopupmenu->spwndPopupMenu != NULL); 00830 pMenuState = GetpMenuState(ppopupmenu->spwndPopupMenu); 00831 if (pMenuState == NULL) { 00832 RIPMSG0(RIP_ERROR, "xxxMNSwitchToAlternateMenu: pMenuState == NULL"); 00833 ThreadUnlock(&tlpwndPopupMenu); 00834 return FALSE; 00835 } 00836 xxxMNSelectItem(ppopupmenu, pMenuState, MFMWFP_NOITEM); 00837 00838 00839 UserAssert(ppopupmenu->spmenu->spwndNotify == ppopupmenu->spmenuAlternate->spwndNotify); 00840 Lock(&pmenuSwap, ppopupmenu->spmenuAlternate); 00841 Lock(&ppopupmenu->spmenuAlternate, ppopupmenu->spmenu); 00842 Lock(&ppopupmenu->spmenu, pmenuSwap); 00843 Unlock(&pmenuSwap); 00844 00845 /* 00846 * Set this global because it is used in SendMenuSelect() 00847 */ 00848 if (!TestWF(ppopupmenu->spwndNotify, WFSYSMENU)) { 00849 pMenuState->fIsSysMenu = FALSE; 00850 } else if (ppopupmenu->spwndNotify->spmenuSys != NULL) { 00851 pMenuState->fIsSysMenu = (ppopupmenu->spwndNotify->spmenuSys == 00852 ppopupmenu->spmenu); 00853 } else { 00854 pMenuState->fIsSysMenu = !!TestMF(ppopupmenu->spmenu, MFSYSMENU); 00855 } 00856 00857 ppopupmenu->fIsSysMenu = pMenuState->fIsSysMenu; 00858 00859 if (FWINABLE()) { 00860 xxxWindowEvent(EVENT_SYSTEM_MENUEND, ppopupmenu->spwndNotify, 00861 (ppopupmenu->fIsSysMenu ? OBJID_MENU : OBJID_SYSMENU), INDEXID_CONTAINER, 0); 00862 xxxWindowEvent(EVENT_SYSTEM_MENUSTART, ppopupmenu->spwndNotify, 00863 (ppopupmenu->fIsSysMenu ? OBJID_SYSMENU : OBJID_MENU), INDEXID_CONTAINER, 0); 00864 } 00865 00866 ThreadUnlock(&tlpwndPopupMenu); 00867 00868 return TRUE; 00869 } 00870 00871 /***************************************************************************\ 00872 * MenuDestroyHandler 00873 * 00874 * cleans up after this menu 00875 * 00876 * History: 00877 * 05-25-91 Mikehar Ported from Win3.1 00878 \***************************************************************************/ 00879 00880 void xxxMNDestroyHandler( 00881 PPOPUPMENU ppopupmenu) 00882 { 00883 PITEM pItem; 00884 TL tlpwndT; 00885 00886 if (ppopupmenu == NULL) { 00887 /* 00888 * This can happen if WM_NCCREATE failed to allocate the ppopupmenu 00889 * in xxxMenuWindowProc. 00890 */ 00891 RIPMSG0(RIP_WARNING, "xxxMNDestroyHandler: NULL \"ppopupmenu\""); 00892 return; 00893 } 00894 00895 #if DBG 00896 /* 00897 * When destroying a desktop's spwndMenu that is not in use (i.e., the 00898 * desktop is going away), the ppopupmenu is not exactly valid (i.e., 00899 * we're not in menu mode) but it should be properly NULLed out so 00900 * everything should go smoothly 00901 */ 00902 Validateppopupmenu(ppopupmenu); 00903 #endif 00904 00905 if (ppopupmenu->spwndNextPopup != NULL) { 00906 /* 00907 * We used to send the message to spwndNextPopup here. The message should 00908 * go to the current popup so it'll close spwndNextPopup (not to the next 00909 * to close its next, if any). 00910 * I don't see how the current spwndPopupMenu can be NULL but we better 00911 * handle it since we never accessed it before. This menu code is tricky... 00912 */ 00913 PWND pwnd; 00914 UserAssert(ppopupmenu->spwndPopupMenu != NULL); 00915 pwnd = (ppopupmenu->spwndPopupMenu != NULL ? ppopupmenu->spwndPopupMenu : ppopupmenu->spwndNextPopup); 00916 ThreadLockAlways(pwnd, &tlpwndT); 00917 xxxSendMessage(pwnd, MN_CLOSEHIERARCHY, 0, 0); 00918 ThreadUnlock(&tlpwndT); 00919 } 00920 00921 if ((ppopupmenu->spmenu!=NULL) && MNIsItemSelected(ppopupmenu)) 00922 { 00923 /* 00924 * Unset the hilite bit on the hilited item. 00925 */ 00926 if (ppopupmenu->posSelectedItem < ppopupmenu->spmenu->cItems) { 00927 /* 00928 * this extra check saves Ambiente 1.02 -- they have a menu with 00929 * one item in it. When that command is chosen, the app proceeds 00930 * to remove that one item -- leaving us in the oh so strange state 00931 * of having a valid hMenu with a NULL rgItems. 00932 */ 00933 pItem = &(ppopupmenu->spmenu->rgItems[ppopupmenu->posSelectedItem]); 00934 pItem->fState &= ~MFS_HILITE; 00935 } 00936 } 00937 00938 if (ppopupmenu->fShowTimer) { 00939 _KillTimer(ppopupmenu->spwndPopupMenu, IDSYS_MNSHOW); 00940 } 00941 00942 if (ppopupmenu->fHideTimer) { 00943 _KillTimer(ppopupmenu->spwndPopupMenu, IDSYS_MNHIDE); 00944 } 00945 00946 /* 00947 * Send WM_UNINITMENUPOPUP so the menu owner can clean up. 00948 */ 00949 if (ppopupmenu->fSendUninit 00950 && (ppopupmenu->spwndNotify != NULL)) { 00951 00952 ThreadLockAlways(ppopupmenu->spwndNotify, &tlpwndT); 00953 xxxSendMessage(ppopupmenu->spwndNotify, WM_UNINITMENUPOPUP, 00954 (WPARAM)PtoH(ppopupmenu->spmenu), 00955 MAKELONG(0, (ppopupmenu->fIsSysMenu ? MF_SYSMENU: 0))); 00956 ThreadUnlock(&tlpwndT); 00957 } 00958 00959 ppopupmenu->fDestroyed = TRUE; 00960 if (!ppopupmenu->fDesktopMenu) { 00961 00962 if (ppopupmenu->spwndPopupMenu != NULL) { 00963 ((PMENUWND)(ppopupmenu->spwndPopupMenu))->ppopupmenu = NULL; 00964 } 00965 00966 } 00967 00968 if (!ppopupmenu->fDelayedFree) { 00969 MNFreePopup(ppopupmenu); 00970 } else if (ppopupmenu->ppopupmenuRoot != NULL) { 00971 ppopupmenu->ppopupmenuRoot->fFlushDelayedFree = TRUE; 00972 #if DBG 00973 { 00974 /* 00975 * If this is not the rootpopup, 00976 * assert that this popup is linked in the delayed free list 00977 */ 00978 if (!IsRootPopupMenu(ppopupmenu)) { 00979 BOOL fFound = FALSE; 00980 PPOPUPMENU ppm = ppopupmenu->ppopupmenuRoot; 00981 while (ppm->ppmDelayedFree != NULL) { 00982 if (ppm->ppmDelayedFree == ppopupmenu) { 00983 fFound = TRUE; 00984 break; 00985 } 00986 ppm = ppm->ppmDelayedFree; 00987 } 00988 UserAssert(fFound); 00989 } 00990 } 00991 #endif 00992 } else { 00993 UserAssertMsg1(FALSE, "Leaking ppopupmenu:%p?", ppopupmenu); 00994 } 00995 00996 } 00997 00998 00999 /***************************************************************************\ 01000 * MenuCharHandler 01001 * 01002 * Handles char messages for the given menu. This procedure is called 01003 * directly if the menu char is for the top level menu bar else it is called 01004 * by the menu window proc on behalf of the window that should process the 01005 * key. 01006 * 01007 * History: 01008 * 05-25-91 Mikehar Ported from Win3.1 01009 \***************************************************************************/ 01010 01011 void xxxMNChar( 01012 PPOPUPMENU ppopupmenu, 01013 PMENUSTATE pMenuState, 01014 UINT character) 01015 { 01016 PMENU pMenu; 01017 UINT flags; 01018 LRESULT result; 01019 int item; 01020 INT matchType; 01021 BOOL fExecute = FALSE; 01022 TL tlpwndNotify; 01023 01024 pMenu = ppopupmenu->spmenu; 01025 01026 Validateppopupmenu(ppopupmenu); 01027 01028 /* 01029 * If this comes in with a NULL pMenu, then we could have problems. 01030 * This could happen if the xxxMNStartMenuState never gets called 01031 * because the fInsideMenuLoop is set. 01032 * 01033 * This is placed in here temporarily until we can discover why 01034 * this pMenu isn't set. We will prevent the system from crashing 01035 * in the meantime. 01036 * 01037 * HACK: ChrisWil 01038 */ 01039 if (pMenu == NULL) { 01040 UserAssert(pMenu); 01041 xxxMNDismiss(pMenuState); 01042 return; 01043 } 01044 01045 /* 01046 * If we're getting out of menu mode, bail 01047 */ 01048 if (ppopupmenu->fDestroyed) { 01049 return; 01050 } 01051 01052 item = xxxMNFindChar(pMenu, character, 01053 ppopupmenu->posSelectedItem, &matchType); 01054 if (item != MFMWFP_NOITEM) { 01055 int item1; 01056 int firstItem = item; 01057 01058 /* 01059 * Find first ENABLED menu item with the given mnemonic 'character' 01060 * !!! If none found, exit menu loop !!! 01061 */ 01062 while (pMenu->rgItems[item].fState & MFS_GRAYED) { 01063 item = xxxMNFindChar(pMenu, character, item, &matchType); 01064 if (item == firstItem) { 01065 xxxMNDismiss(pMenuState); 01066 return; 01067 } 01068 } 01069 item1 = item; 01070 01071 /* 01072 * Find next ENABLED menu item with the given mnemonic 'character' 01073 * This is to see if we have a DUPLICATE MNEMONIC situation 01074 */ 01075 do { 01076 item = xxxMNFindChar(pMenu, character, item, &matchType); 01077 } while ((pMenu->rgItems[item].fState & MFS_GRAYED) && (item != firstItem)); 01078 01079 if ((firstItem == item) || (item == item1)) 01080 fExecute = TRUE; 01081 01082 item = item1; 01083 } 01084 01085 if ((item == MFMWFP_NOITEM) && ppopupmenu->fIsMenuBar && (character == TEXT(' '))) { 01086 01087 /* 01088 * Handle the case of the user cruising through the top level menu bar 01089 * without any popups dropped. We need to handle switching to and from 01090 * the system menu. 01091 */ 01092 if (ppopupmenu->fIsSysMenu) { 01093 01094 /* 01095 * If we are on the system menu and user hits space, bring 01096 * down thesystem menu. 01097 */ 01098 item = 0; 01099 fExecute = TRUE; 01100 } else if (ppopupmenu->spmenuAlternate != NULL) { 01101 01102 /* 01103 * We are not currently on the system menu but one exists. So 01104 * switch to it and bring it down. 01105 */ 01106 item = 0; 01107 goto SwitchToAlternate; 01108 } 01109 } 01110 01111 if ((item == MFMWFP_NOITEM) && ppopupmenu->fIsMenuBar && ppopupmenu->spmenuAlternate) { 01112 01113 /* 01114 * No matching item found on this top level menu (could be either the 01115 * system menu or the menu bar). We need to check the other menu. 01116 */ 01117 item = xxxMNFindChar(ppopupmenu->spmenuAlternate, 01118 character, 0, &matchType); 01119 01120 if (item != MFMWFP_NOITEM) { 01121 SwitchToAlternate: 01122 if (xxxMNSwitchToAlternateMenu(ppopupmenu)) { 01123 xxxMNChar(ppopupmenu, pMenuState, character); 01124 } 01125 return; 01126 } 01127 } 01128 01129 if (item == MFMWFP_NOITEM) { 01130 flags = (ppopupmenu->fIsSysMenu) ? MF_SYSMENU : 0; 01131 01132 if (!ppopupmenu->fIsMenuBar) { 01133 flags |= MF_POPUP; 01134 } 01135 01136 ThreadLock(ppopupmenu->spwndNotify, &tlpwndNotify); 01137 result = xxxSendMessage(ppopupmenu->spwndNotify, WM_MENUCHAR, 01138 MAKELONG((WORD)character, (WORD)flags), 01139 (LPARAM)PtoH(ppopupmenu->spmenu)); 01140 ThreadUnlock(&tlpwndNotify); 01141 01142 switch (HIWORD(result)) { 01143 case MNC_IGNORE: 01144 xxxMessageBeep(0); 01145 /* 01146 * If we're on the menu bar, cancel menu mode (fall through). 01147 * We do this because you can really freak out an end user 01148 * who accidentally tapped the Alt key (causing us to go 01149 * into "invisible" menu mode) and now can't type anything! 01150 */ 01151 if (flags & MF_POPUP) { 01152 return; 01153 } 01154 /* 01155 * Fall through. 01156 */ 01157 01158 case MNC_CLOSE: 01159 xxxMNDismiss(pMenuState); 01160 return; 01161 01162 case MNC_EXECUTE: 01163 fExecute = TRUE; 01164 /* fall thru */ 01165 01166 case MNC_SELECT: 01167 item = (UINT)(short)LOWORD(result); 01168 if ((WORD) item >= ppopupmenu->spmenu->cItems) 01169 { 01170 RIPMSG1(RIP_WARNING, "Invalid item number returned from WM_MENUCHAR %#lx", result); 01171 return; 01172 } 01173 break; 01174 } 01175 } 01176 01177 if (item != MFMWFP_NOITEM) { 01178 xxxMNSelectItem(ppopupmenu, pMenuState, item); 01179 01180 if (fExecute) 01181 xxxMNKeyDown(ppopupmenu, pMenuState, VK_RETURN); 01182 } 01183 } 01184 01185 /***************************************************************************\ 01186 * 01187 * GetMenuInheritedContextHelpId(PPOPUPMENU ppopup) 01188 * Given a ppopup, this function will see if that menu has a context help 01189 * id and return it. If it does not have a context help id, it will look up 01190 * in the parent menu, parent of the parent etc., all the way to the top 01191 * top level menu bar till it finds a context help id and returns it. If no 01192 * context help id is found, it returns a zero. 01193 * 01194 \***************************************************************************/ 01195 01196 DWORD GetMenuInheritedContextHelpId(PPOPUPMENU ppopup) 01197 { 01198 PWND pWnd; 01199 01200 /* 01201 * If we are already at the menubar, simply return it's ContextHelpId 01202 */ 01203 UserAssert(ppopup != NULL); 01204 if (ppopup->fIsMenuBar) 01205 goto Exit_GMI; 01206 01207 while(TRUE) { 01208 UserAssert(ppopup != NULL); 01209 01210 /* 01211 * See if the given popup has a context help id. 01212 */ 01213 if (ppopup->spmenu->dwContextHelpId) { 01214 /* Found the context Id */ 01215 break; 01216 } 01217 01218 /* 01219 * Get the previous popup menu; 01220 * Check if the previous menu is the menu bar. 01221 */ 01222 if ( (ppopup->fHasMenuBar) && 01223 (ppopup->spwndPrevPopup == ppopup->spwndNotify)) { 01224 01225 ppopup = ppopup -> ppopupmenuRoot; 01226 break; 01227 } else { 01228 /* 01229 * See if this has a valid prevPopup; (it could be TrackPopup menu) 01230 */ 01231 if ((pWnd = ppopup -> spwndPrevPopup) == NULL) { 01232 return ((DWORD)0); 01233 } 01234 01235 ppopup = ((PMENUWND)pWnd)->ppopupmenu; 01236 } 01237 } 01238 01239 Exit_GMI: 01240 return(ppopup->spmenu->dwContextHelpId); 01241 } 01242 01243 /***************************************************************************\ 01244 * void MenuKeyDownHandler(PPOPUPMENU ppopupmenu, UINT key) 01245 * effects: Handles a keydown for the given menu. 01246 * 01247 * History: 01248 * 05-25-91 Mikehar Ported from Win3.1 01249 \***************************************************************************/ 01250 01251 void xxxMNKeyDown( 01252 PPOPUPMENU ppopupmenu, 01253 PMENUSTATE pMenuState, 01254 UINT key) 01255 { 01256 LRESULT dwMDIMenu; 01257 UINT item; 01258 BOOL fHierarchyWasDropped = FALSE; 01259 TL tlpwndT; 01260 PPOPUPMENU ppopupSave; 01261 BOOL bFakedKey; 01262 UINT keyOrig = key; 01263 01264 /* 01265 * Blow off keyboard if mouse down. 01266 */ 01267 if ((pMenuState->fButtonDown) && (key != VK_F1)) { 01268 /* 01269 * Check if the user wants to cancel dragging. 01270 */ 01271 if (pMenuState->fDragging && (key == VK_ESCAPE)) { 01272 RIPMSG0(RIP_WARNING, "xxxMNKeyDown: ESC while dragging"); 01273 pMenuState->fIgnoreButtonUp = TRUE; 01274 } 01275 01276 return; 01277 } 01278 01279 switch (key) { 01280 case VK_MENU: 01281 case VK_F10: 01282 { 01283 /* 01284 * Modeless don't go away when the menu key is hit. They just 01285 * ignore it. 01286 */ 01287 if (pMenuState->fModelessMenu) { 01288 return; 01289 } 01290 01291 if (gwinOldAppHackoMaticFlags & WOAHACK_CHECKALTKEYSTATE) { 01292 01293 /* 01294 * Winoldapp is telling us to put up/down the system menu. Due to 01295 * possible race conditions, we need to check the state of the alt 01296 * key before throwing away the menu. 01297 */ 01298 if (gwinOldAppHackoMaticFlags & WOAHACK_IGNOREALTKEYDOWN) { 01299 return; 01300 } 01301 } 01302 xxxMNDismiss(pMenuState); 01303 01304 /* 01305 * We're going to exit menu mode but the ALT key is down, so clear 01306 * pMenuState->fUnderline to cause xxxMNLoop not to erase the underlines 01307 */ 01308 if (key == VK_MENU) { 01309 pMenuState->fUnderline = FALSE; 01310 } 01311 } 01312 return; 01313 01314 case VK_ESCAPE: 01315 01316 /* 01317 * Escape key was hit. Get out of one level of menus. If no active 01318 * popups or we are minimized and there are no active popups below 01319 * this, we need to get out of menu mode. Otherwise, we popup up 01320 * one level in the hierarchy. 01321 */ 01322 if (ppopupmenu->fIsMenuBar || 01323 ppopupmenu == ppopupmenu->ppopupmenuRoot || 01324 TestWF(ppopupmenu->ppopupmenuRoot->spwndNotify, WFMINIMIZED)) { 01325 xxxMNDismiss(pMenuState); 01326 } else { 01327 /* 01328 * Pop back one level of menus. 01329 */ 01330 if (ppopupmenu->fHasMenuBar && 01331 ppopupmenu->spwndPrevPopup == ppopupmenu->spwndNotify) { 01332 01333 PPOPUPMENU ppopupmenuRoot = ppopupmenu->ppopupmenuRoot; 01334 01335 ppopupmenuRoot->fDropNextPopup = FALSE; 01336 01337 #if 0 01338 /* 01339 * We are on a menu bar hierarchy and there is only one popup 01340 * visible. We have to cancel this popup and put focus back on 01341 * the menu bar. 01342 */ 01343 if (_IsIconic(ppopupmenuRoot->spwndNotify)) { 01344 01345 /* 01346 * However, if we are iconic there really is no menu 01347 * bar so let's make it easier for users and get out 01348 * of menu mode completely. 01349 */ 01350 xxxMNDismiss(pMenuState); 01351 } else 01352 #endif 01353 /* 01354 * If the popup is closed, a modeless menu won't 01355 * have a window to get the keys. So modeless menu 01356 * cancel the menu at this point. Modal menus go 01357 * to the menu bar. 01358 */ 01359 if (pMenuState->fModelessMenu) { 01360 xxxMNDismiss(pMenuState); 01361 } else { 01362 xxxMNCloseHierarchy(ppopupmenuRoot, pMenuState); 01363 } 01364 } else { 01365 ThreadLock(ppopupmenu->spwndPrevPopup, &tlpwndT); 01366 xxxSendMessage(ppopupmenu->spwndPrevPopup, MN_CLOSEHIERARCHY, 01367 0, 0); 01368 ThreadUnlock(&tlpwndT); 01369 } 01370 } 01371 return; 01372 01373 case VK_UP: 01374 case VK_DOWN: 01375 if (ppopupmenu->fIsMenuBar) { 01376 01377 /* 01378 * If we are on the top level menu bar, try to open the popup if 01379 * possible. 01380 */ 01381 if (xxxMNOpenHierarchy(ppopupmenu, pMenuState) == (PWND)-1) 01382 return; 01383 } else { 01384 item = MNFindNextValidItem(ppopupmenu->spmenu, 01385 ppopupmenu->posSelectedItem, (key == VK_UP ? -1 : 1), 0); 01386 xxxMNSelectItem(ppopupmenu, pMenuState, item); 01387 } 01388 return; 01389 01390 case VK_LEFT: 01391 case VK_RIGHT: 01392 #ifdef USE_MIRRORING 01393 bFakedKey = (!!ppopupmenu->fRtoL) ^ (!!TestWF(ppopupmenu->spwndPopupMenu, WEFLAYOUTRTL)); 01394 #else 01395 bFakedKey = ppopupmenu->fRtoL; 01396 #endif 01397 if (bFakedKey) 01398 /* 01399 * turn the keys around, we drew the menu backwards. 01400 */ 01401 key = (key == VK_LEFT) ? VK_RIGHT : VK_LEFT; 01402 if (!ppopupmenu->fIsMenuBar && (key == VK_RIGHT) && 01403 !ppopupmenu->spwndNextPopup) { 01404 /* 01405 * Try to open the hierarchy at this item if there is one. 01406 */ 01407 if (xxxMNOpenHierarchy(ppopupmenu, pMenuState) == (PWND)-1) 01408 return; 01409 if (ppopupmenu->fHierarchyDropped) { 01410 return; 01411 } 01412 } 01413 01414 if (ppopupmenu->spwndNextPopup) { 01415 fHierarchyWasDropped = TRUE; 01416 if ((key == VK_LEFT) && !ppopupmenu->fIsMenuBar) { 01417 xxxMNCloseHierarchy(ppopupmenu, pMenuState); 01418 return; 01419 } 01420 } else if (ppopupmenu->fDropNextPopup) 01421 fHierarchyWasDropped = TRUE; 01422 01423 ppopupSave = ppopupmenu; 01424 01425 item = MNFindItemInColumn(ppopupmenu->spmenu, 01426 ppopupmenu->posSelectedItem, 01427 (key == VK_LEFT ? -1 : 1), 01428 (ppopupmenu->fHasMenuBar && 01429 ppopupmenu == ppopupmenu->ppopupmenuRoot)); 01430 01431 if (item == MFMWFP_NOITEM) { 01432 01433 /* 01434 * No valid item found in the given direction so send it up to our 01435 * parent to handle. 01436 */ 01437 if (ppopupmenu->fHasMenuBar && 01438 ppopupmenu->spwndPrevPopup == ppopupmenu->spwndNotify) { 01439 01440 /* 01441 * if we turned the key round, then turn it back again. 01442 */ 01443 if (bFakedKey) 01444 key = (key == VK_LEFT) ? VK_RIGHT : VK_LEFT; 01445 /* 01446 * Go to next/prev item in menu bar since a popup was down and 01447 * no item on the popup to go to. 01448 */ 01449 xxxMNKeyDown(ppopupmenu->ppopupmenuRoot, pMenuState, key); 01450 return; 01451 } 01452 01453 if (ppopupmenu == ppopupmenu->ppopupmenuRoot) { 01454 if (!ppopupmenu->fIsMenuBar) { 01455 01456 /* 01457 * No menu bar associated with this menu so do nothing. 01458 */ 01459 return; 01460 } 01461 } else { 01462 ThreadLock(ppopupmenu->spwndPrevPopup, &tlpwndT); 01463 xxxSendMessage(ppopupmenu->spwndPrevPopup, WM_KEYDOWN, keyOrig, 0); 01464 ThreadUnlock(&tlpwndT); 01465 return; 01466 } 01467 } 01468 01469 if (!ppopupmenu->fIsMenuBar) { 01470 if (item != MFMWFP_NOITEM) { 01471 xxxMNSelectItem(ppopupmenu, pMenuState, item); 01472 } 01473 return; 01474 01475 } else { 01476 01477 /* 01478 * Special handling if keydown occurred on a menu bar. 01479 */ 01480 if (item == MFMWFP_NOITEM) { 01481 01482 if (TestWF(ppopupmenu->spwndNotify, WFSYSMENU)) { 01483 PTHREADINFO ptiCurrent = PtiCurrent(); 01484 PWND pwndNextMenu; 01485 PMENU pmenuNextMenu, pmenuUse; 01486 MDINEXTMENU mnm; 01487 TL tlpmenuNextMenu; 01488 TL tlpwndNextMenu; 01489 01490 mnm.hmenuIn = (HMENU)0; 01491 mnm.hmenuNext = (HMENU)0; 01492 mnm.hwndNext = (HWND)0; 01493 01494 /* 01495 * We are in the menu bar and need to go up to the system menu 01496 * or go from the system menu to the menu bar. 01497 */ 01498 pmenuNextMenu = ppopupmenu->fIsSysMenu ? 01499 _GetSubMenu(ppopupmenu->spmenu, 0) : 01500 ppopupmenu->spmenu; 01501 mnm.hmenuIn = PtoH(pmenuNextMenu); 01502 ThreadLock(ppopupmenu->spwndNotify, &tlpwndT); 01503 dwMDIMenu = xxxSendMessage(ppopupmenu->spwndNotify, 01504 WM_NEXTMENU, (WPARAM)keyOrig, (LPARAM)&mnm); 01505 ThreadUnlock(&tlpwndT); 01506 01507 pwndNextMenu = RevalidateHwnd(mnm.hwndNext); 01508 if (pwndNextMenu == NULL) 01509 goto TryAlternate; 01510 01511 /* 01512 * If this window belongs to another thread, we cannot 01513 * use it. The menu loop won't get any messages 01514 * directed to that thread. 01515 */ 01516 if (GETPTI(pwndNextMenu) != ptiCurrent) { 01517 RIPMSG1(RIP_WARNING, "xxxMNKeyDown: Ignoring mnm.hwndNext bacause it belongs to another thread: %#p", pwndNextMenu); 01518 goto TryAlternate; 01519 } 01520 01521 01522 pmenuNextMenu = RevalidateHmenu(mnm.hmenuNext); 01523 if (pmenuNextMenu == NULL) 01524 goto TryAlternate; 01525 01526 ThreadLock(pmenuNextMenu, &tlpmenuNextMenu); 01527 ThreadLock(pwndNextMenu, &tlpwndNextMenu); 01528 01529 /* 01530 * If the system menu is for a minimized MDI child, 01531 * make sure the menu is dropped to give the user a 01532 * visual clue that they are in menu mode 01533 */ 01534 if (TestWF(pwndNextMenu, WFMINIMIZED)) 01535 fHierarchyWasDropped = TRUE; 01536 01537 xxxMNSelectItem(ppopupmenu, pMenuState, MFMWFP_NOITEM); 01538 01539 pMenuState->fIsSysMenu = TRUE; 01540 UnlockPopupMenu(ppopupmenu, &ppopupmenu->spmenuAlternate); 01541 ppopupmenu->fToggle = FALSE; 01542 /* 01543 * GetSystemMenu(pwnd, FALSE) and pwnd->spmenuSys are 01544 * NOT equivalent -- GetSystemMenu returns the 1st submenu 01545 * of pwnd->spmenuSys -- make up for that here 01546 */ 01547 pmenuUse = (((pwndNextMenu->spmenuSys != NULL) 01548 && (_GetSubMenu(pwndNextMenu->spmenuSys, 0) == pmenuNextMenu)) 01549 ? pwndNextMenu->spmenuSys 01550 : pmenuNextMenu); 01551 /* 01552 * We're going to change the notification window AND the menu. 01553 * LockPopupMenu needs to unlock the current pmenu-spwndNotify 01554 * but also lock the new pmenu-spwndNotify. Since we cannot 01555 * give it the current AND the new pair, we unlock the 01556 * current one first, switch the notification window and 01557 * then call LockPopupMenu to lock the new pmenu-spwndNotify. 01558 */ 01559 UserAssert(IsRootPopupMenu(ppopupmenu)); 01560 UnlockPopupMenu(ppopupmenu, &ppopupmenu->spmenu); 01561 Lock(&ppopupmenu->spwndNotify, pwndNextMenu); 01562 Lock(&ppopupmenu->spwndPopupMenu, pwndNextMenu); 01563 LockPopupMenu(ppopupmenu, &ppopupmenu->spmenu, pmenuUse); 01564 /* 01565 * We just switched to a new notification window so 01566 * we need to Adjust capture accordingly 01567 */ 01568 if (!pMenuState->fModelessMenu) { 01569 ptiCurrent->pq->QF_flags &= ~QF_CAPTURELOCKED; 01570 xxxMNSetCapture(ppopupmenu); 01571 } 01572 01573 01574 if (!TestWF(pwndNextMenu, WFCHILD) && 01575 ppopupmenu->spmenu != NULL) { 01576 01577 /* 01578 * This window has a system menu and a main menu bar 01579 * Set the alternate menu to the appropriate menu 01580 */ 01581 if (pwndNextMenu->spmenu == ppopupmenu->spmenu) { 01582 LockPopupMenu(ppopupmenu, &ppopupmenu->spmenuAlternate, 01583 pwndNextMenu->spmenuSys); 01584 pMenuState->fIsSysMenu = FALSE; 01585 } else { 01586 LockPopupMenu(ppopupmenu, &ppopupmenu->spmenuAlternate, 01587 pwndNextMenu->spmenu); 01588 } 01589 } 01590 01591 ThreadUnlock(&tlpwndNextMenu); 01592 ThreadUnlock(&tlpmenuNextMenu); 01593 01594 ppopupmenu->fIsSysMenu = pMenuState->fIsSysMenu; 01595 01596 item = 0; 01597 } else 01598 TryAlternate: 01599 if (xxxMNSwitchToAlternateMenu(ppopupmenu)) { 01600 /* 01601 * go to first or last menu item int ppopup->hMenu 01602 * based on 'key' 01603 */ 01604 int dir = (key == VK_RIGHT) ? 1 : -1; 01605 01606 item = MNFindNextValidItem(ppopupmenu->spmenu, MFMWFP_NOITEM, dir, 0); 01607 } 01608 } 01609 01610 if (item != MFMWFP_NOITEM) { 01611 /* 01612 * we found a new menu item to go to 01613 * 1) close up the previous menu if it was dropped 01614 * 2) select the new menu item to go to 01615 * 3) drop the new menu if the previous menu was dropped 01616 */ 01617 01618 if (ppopupSave->spwndNextPopup) 01619 xxxMNCloseHierarchy(ppopupSave, pMenuState); 01620 01621 xxxMNSelectItem(ppopupmenu, pMenuState, item); 01622 01623 if (fHierarchyWasDropped) { 01624 DropHierarchy: 01625 if (xxxMNOpenHierarchy(ppopupmenu, pMenuState) == (PWND)-1) { 01626 return; 01627 } 01628 } 01629 } 01630 } 01631 return; 01632 01633 case VK_RETURN: 01634 { 01635 BOOL fEnabled; 01636 PITEM pItem; 01637 01638 if (ppopupmenu->posSelectedItem >= ppopupmenu->spmenu->cItems) { 01639 xxxMNDismiss(pMenuState); 01640 return; 01641 } 01642 01643 pItem = ppopupmenu->spmenu->rgItems + ppopupmenu->posSelectedItem; 01644 fEnabled = !(pItem->fState & MFS_GRAYED); 01645 if ((pItem->spSubMenu != NULL) && fEnabled) 01646 goto DropHierarchy; 01647 01648 /* 01649 * If no item is selected, throw away menu and return. 01650 */ 01651 if (fEnabled) { 01652 xxxMNDismissWithNotify(pMenuState, ppopupmenu->spmenu, pItem, ppopupmenu->posSelectedItem, 0); 01653 } else { 01654 xxxMNDismiss(pMenuState); 01655 } 01656 return; 01657 } 01658 01659 case VK_F1: /* Provide context sensitive help. */ 01660 { 01661 PITEM pItem; 01662 01663 pItem = ppopupmenu->spmenu->rgItems + ppopupmenu->posSelectedItem; 01664 ThreadLock(ppopupmenu->spwndNotify, &tlpwndT); 01665 xxxSendHelpMessage(ppopupmenu->spwndNotify, HELPINFO_MENUITEM, pItem->wID, 01666 PtoHq(ppopupmenu->spmenu), 01667 GetMenuInheritedContextHelpId(ppopupmenu)); 01668 ThreadUnlock(&tlpwndT); 01669 break; 01670 } 01671 01672 } 01673 } 01674 /***************************************************************************\ 01675 * xxxMNPositionHierarchy 01676 * 01677 * Calculates the x.y postion to drop a hierarchy and returns the direction 01678 * to be used when animating (PAS_* value) 01679 * 01680 * 11/19/96 GerardoB Extracted from xxxMNOpenHierarchy 01681 \***************************************************************************/ 01682 UINT 01683 xxxMNPositionHierarchy( 01684 PPOPUPMENU ppopup, 01685 PITEM pitem, 01686 int cx, 01687 int cy, 01688 int *px, 01689 int *py, 01690 PMONITOR *ppMonitor) 01691 { 01692 int x, y; 01693 UINT uPAS; 01694 PMONITOR pMonitor; 01695 01696 UserAssert(ppopup->fHierarchyDropped && (ppopup->spwndNextPopup != NULL)); 01697 01698 if (ppopup->fIsMenuBar) { 01699 /* 01700 * This is a menu being dropped from the top menu bar. We need to 01701 * position it differently than hierarchicals which are dropped from 01702 * another popup. 01703 */ 01704 01705 BOOL fIconic = (TestWF(ppopup->spwndPopupMenu, WFMINIMIZED) != 0); 01706 RECT rcWindow; 01707 01708 /* 01709 * Menu bar popups animate down. 01710 */ 01711 uPAS = PAS_DOWN; 01712 01713 CopyRect(&rcWindow, &ppopup->spwndPopupMenu->rcWindow); 01714 if (fIconic && IsTrayWindow(ppopup->spwndPopupMenu)) { 01715 xxxSendMinRectMessages(ppopup->spwndPopupMenu, &rcWindow); 01716 } 01717 01718 /* 01719 * x position 01720 */ 01721 if (!SYSMET(MENUDROPALIGNMENT) && !TestMF(ppopup->spmenu,MFRTL)) { 01722 if (fIconic) { 01723 x = rcWindow.left; 01724 } else { 01725 x = rcWindow.left + pitem->xItem; 01726 } 01727 } else { 01728 ppopup->fDroppedLeft = TRUE; 01729 if (fIconic) { 01730 x = rcWindow.right - cx; 01731 } else { 01732 x = rcWindow.left + pitem->xItem + pitem->cxItem - cx; 01733 } 01734 } 01735 01736 /* 01737 * For a menu bar dropdown, pin to the monitor that owns the 01738 * majority of the menu item. Otherwise, pin to the monitor that 01739 * owns the minimized window (the tray rect for min-to-tray dudes). 01740 */ 01741 if (!fIconic) 01742 { 01743 /* 01744 * Use rcWindow as scratch for the menu bar item rect. We want 01745 * to pin this menu on whatever monitor owns most of the menu 01746 * item clicked on. 01747 */ 01748 rcWindow.left += pitem->xItem; 01749 rcWindow.top += pitem->yItem; 01750 rcWindow.right = rcWindow.left + pitem->cxItem; 01751 rcWindow.bottom = rcWindow.top + pitem->cyItem; 01752 } 01753 01754 pMonitor = _MonitorFromRect(&rcWindow, MONITOR_DEFAULTTOPRIMARY); 01755 01756 /* 01757 * y position 01758 */ 01759 if (!fIconic) { 01760 y = rcWindow.bottom; 01761 } else { 01762 /* 01763 * If the window is iconic, pop the menu up. Since we're 01764 * minimized, the sysmenu button doesn't really exist. 01765 */ 01766 y = rcWindow.top - cy; 01767 if (y < pMonitor->rcMonitor.top) { 01768 y = rcWindow.bottom; 01769 } 01770 } 01771 01772 /* 01773 * Make sure the menu doesn't go off right side of monitor 01774 */ 01775 x = min(x, pMonitor->rcMonitor.right - cx); 01776 01777 #ifdef USE_MIRRORING 01778 if (TestWF(ppopup->spwndPopupMenu, WEFLAYOUTRTL)) { 01779 x = ppopup->spwndPopupMenu->rcWindow.right - x + ppopup->spwndPopupMenu->rcWindow.left - cx; 01780 } 01781 #endif 01782 01783 } else { /* if (ppopup->fIsMenuBar) */ 01784 01785 /* Now position the hierarchical menu window. 01786 * We want to overlap by the amount of the frame, to help in the 01787 * 3D illusion. 01788 */ 01789 01790 /* 01791 * By default, hierachical popups animate to the right 01792 */ 01793 uPAS = PAS_RIGHT; 01794 x = ppopup->spwndPopupMenu->rcWindow.left + pitem->xItem + pitem->cxItem; 01795 01796 /* Note that we DO want the selections in the item and its popup to 01797 * align horizontally. 01798 */ 01799 y = ppopup->spwndPopupMenu->rcWindow.top + pitem->yItem; 01800 if (ppopup->spmenu->dwArrowsOn != MSA_OFF) { 01801 y += gcyMenuScrollArrow - MNGetToppItem(ppopup->spmenu)->yItem; 01802 } 01803 01804 /* 01805 * Try to make sure the menu doesn't go off right side of the 01806 * monitor. If it does, drop it left, overlapping the checkmark 01807 * area. Unless it would cover the previous menu... 01808 * 01809 * Use the monitor that the parent menu is on to keep all hierarchicals 01810 * in the same place. 01811 */ 01812 pMonitor = _MonitorFromWindow( 01813 ppopup->spwndPopupMenu, MONITOR_DEFAULTTOPRIMARY); 01814 01815 #ifdef USE_MIRRORING 01816 if ((!!ppopup->fDroppedLeft) ^ (!!TestWF(ppopup->spwndPopupMenu, WEFLAYOUTRTL))) { 01817 #else 01818 if (ppopup->fDroppedLeft) { 01819 #endif 01820 int xTmp; 01821 01822 /* 01823 * If this menu has dropped left, see if our hierarchy can be made 01824 * to drop to the left also. 01825 */ 01826 xTmp = ppopup->spwndPopupMenu->rcWindow.left + SYSMET(CXFIXEDFRAME) - cx; 01827 if (xTmp >= pMonitor->rcMonitor.left) { 01828 x = xTmp; 01829 uPAS = PAS_LEFT; 01830 } 01831 } 01832 01833 /* 01834 * Make sure the menu doesn't go off right side of screen. Make it drop 01835 * left if it does. 01836 */ 01837 if (x + cx > pMonitor->rcMonitor.right) { 01838 x = ppopup->spwndPopupMenu->rcWindow.left + SYSMET(CXFIXEDFRAME) - cx; 01839 uPAS = PAS_LEFT; 01840 } 01841 #ifdef USE_MIRRORING 01842 if (TestWF(ppopup->spwndPopupMenu, WEFLAYOUTRTL)) { 01843 uPAS ^= PAS_HORZ; 01844 } 01845 #endif 01846 01847 } /* else if (ppopup->fIsMenuBar) */ 01848 01849 /* 01850 * Does the menu extend beyond bottom of monitor? 01851 */ 01852 UserAssert(pMonitor); 01853 if (y + cy > pMonitor->rcMonitor.bottom) { 01854 y -= cy; 01855 01856 /* 01857 * Try to pop above menu bar first 01858 */ 01859 if (ppopup->fIsMenuBar) { 01860 y -= SYSMET(CYMENUSIZE); 01861 if (y >= pMonitor->rcMonitor.top) { 01862 uPAS = PAS_UP; 01863 } 01864 } else { 01865 /* 01866 * Account for nonclient frame above & below 01867 */ 01868 y += pitem->cyItem + 2*SYSMET(CYFIXEDFRAME); 01869 } 01870 01871 /* 01872 * Make sure that starting point is on a monitor, and all of menu shows. 01873 */ 01874 if ( (y < pMonitor->rcMonitor.top) || 01875 (y + cy > pMonitor->rcMonitor.bottom)) 01876 /* 01877 * Pin it to the bottom. 01878 */ 01879 y = pMonitor->rcMonitor.bottom - cy; 01880 } 01881 01882 /* 01883 * Make sure Upper Left corner of menu is always visible. 01884 */ 01885 x = max(x, pMonitor->rcMonitor.left); 01886 y = max(y, pMonitor->rcMonitor.top); 01887 01888 /* 01889 * Propagate position 01890 */ 01891 *px = x; 01892 *py = y; 01893 *ppMonitor = pMonitor; 01894 01895 /* 01896 * Return animation direction 01897 */ 01898 return uPAS; 01899 } 01900 01901 01902 /***************************************************************************\ 01903 * xxxCleanupDesktopMenu 01904 * 01905 * History: 01906 * 10/19/98 GerardoB Extracted from xxxMNCloseHierarchy 01907 \***************************************************************************/ 01908 void xxxCleanupDesktopMenu (PWND pwndDeskMenu, PDESKTOP pdesk) 01909 { 01910 TL tlpwnd; 01911 CheckLock(pwndDeskMenu); 01912 /* 01913 * Put it on the message window tree so it is out of the way. 01914 */ 01915 UserAssert(pwndDeskMenu->spwndParent == _GetDesktopWindow()); 01916 ThreadLockAlways(pdesk->spwndMessage, &tlpwnd); 01917 xxxSetParent(pwndDeskMenu, pdesk->spwndMessage); 01918 ThreadUnlock(&tlpwnd); 01919 01920 /* 01921 * Give ownershipe back to the desktop thread 01922 */ 01923 pwndDeskMenu->head.pti = pdesk->pDeskInfo->spwnd->head.pti; 01924 Unlock(&pwndDeskMenu->spwndOwner); 01925 } 01926 /***************************************************************************\ 01927 * PWND MenuOpenHierarchyHandler(PPOPUPMENU ppopupmenu) 01928 * effects: Drops one level of the hierarchy at the selection. 01929 * 01930 * History: 01931 * 05-25-91 Mikehar Ported from Win3.1 01932 \***************************************************************************/ 01933 01934 PWND xxxMNOpenHierarchy( 01935 PPOPUPMENU ppopupmenu, PMENUSTATE pMenuState) 01936 { 01937 PWND ret = 0; 01938 PITEM pItem; 01939 PWND pwndHierarchy; 01940 PPOPUPMENU ppopupmenuHierarchy; 01941 LONG sizeHierarchy; 01942 int xLeft; 01943 int yTop; 01944 int cxPopup, cyPopup; 01945 TL tlpwndT; 01946 TL tlpwndHierarchy; 01947 PTHREADINFO ptiCurrent = PtiCurrent(); 01948 PDESKTOP pdesk = ptiCurrent->rpdesk; 01949 BOOL fSendUninit = FALSE; 01950 HMENU hmenuInit; 01951 PMONITOR pMonitor; 01952 01953 01954 if (ppopupmenu->posSelectedItem == MFMWFP_NOITEM) { 01955 /* 01956 * No selection so fail. 01957 */ 01958 return NULL; 01959 } 01960 01961 if (ppopupmenu->posSelectedItem >= ppopupmenu->spmenu->cItems) 01962 return NULL; 01963 01964 if (ppopupmenu->fHierarchyDropped) { 01965 if (ppopupmenu->fHideTimer) { 01966 xxxMNCloseHierarchy(ppopupmenu,pMenuState); 01967 } else { 01968 /* 01969 * Hierarchy already dropped. What are we doing here? 01970 */ 01971 UserAssert(!ppopupmenu->fHierarchyDropped); 01972 return NULL; 01973 } 01974 } 01975 01976 if (ppopupmenu->fShowTimer) { 01977 _KillTimer(ppopupmenu->spwndPopupMenu, IDSYS_MNSHOW); 01978 ppopupmenu->fShowTimer = FALSE; 01979 } 01980 01981 /* 01982 * Get a pointer to the currently selected item in this menu. 01983 */ 01984 pItem = &(ppopupmenu->spmenu->rgItems[ppopupmenu->posSelectedItem]); 01985 01986 if (pItem->spSubMenu == NULL) 01987 goto Exit; 01988 01989 /* 01990 * Send the initmenupopup message. 01991 */ 01992 if (!ppopupmenu->fNoNotify) { 01993 ThreadLock(ppopupmenu->spwndNotify, &tlpwndT); 01994 /* 01995 * WordPerfect's Grammatik app doesn't know that TRUE means NON-ZERO, 01996 * not 1. So we must use 0 & 1 explicitly for fIsSysMenu here 01997 * -- Win95B B#4947 -- 2/13/95 -- jeffbog 01998 */ 01999 hmenuInit = PtoHq(pItem->spSubMenu); 02000 xxxSendMessage(ppopupmenu->spwndNotify, WM_INITMENUPOPUP, 02001 (WPARAM)hmenuInit, MAKELONG(ppopupmenu->posSelectedItem, 02002 (ppopupmenu->fIsSysMenu ? 1: 0))); 02003 ThreadUnlock(&tlpwndT); 02004 fSendUninit = TRUE; 02005 } 02006 02007 02008 /* 02009 * B#1517 02010 * Check if we're still in menu loop 02011 */ 02012 if (!pMenuState->fInsideMenuLoop) { 02013 RIPMSG0(RIP_WARNING, "Menu loop ended unexpectedly by WM_INITMENUPOPUP"); 02014 ret = (PWND)-1; 02015 goto Exit; 02016 } 02017 02018 /* 02019 * The WM_INITMENUPOPUP message may have resulted in a change to the 02020 * menu. Make sure the selection is still valid. 02021 */ 02022 if (ppopupmenu->posSelectedItem >= ppopupmenu->spmenu->cItems) { 02023 /* 02024 * Selection is out of range, so fail. 02025 */ 02026 goto Exit; 02027 } 02028 02029 /* 02030 * Get a pointer to the currently selected item in this menu. 02031 * Bug #17867 - the call can cause this thing to change, so reload it. 02032 */ 02033 pItem = &(ppopupmenu->spmenu->rgItems[ppopupmenu->posSelectedItem]); 02034 02035 if (TestMFS(pItem, MFS_GRAYED) || (pItem->spSubMenu == NULL) || (pItem->spSubMenu->cItems == 0)) { 02036 /* 02037 * The item is disabled, no longer a popup, or empty so don't drop. 02038 */ 02039 /* 02040 * No items in menu. 02041 */ 02042 goto Exit; 02043 } 02044 02045 /* 02046 * Let's make sure that the current thread is in menu mode and 02047 * it uses this pMenuState. Otherwise the window we're about to 02048 * create (or set the thread to) will point to a different pMenuState 02049 */ 02050 UserAssert(ptiCurrent->pMenuState == pMenuState); 02051 02052 if (ppopupmenu->fIsMenuBar && (pdesk->spwndMenu != NULL) && 02053 (!(pdesk->dwDTFlags & DF_MENUINUSE)) && 02054 !TestWF(pdesk->spwndMenu, WFVISIBLE)) { 02055 02056 pdesk->dwDTFlags |= DF_MENUINUSE; 02057 02058 if (HMPheFromObject(pdesk->spwndMenu)->bFlags & HANDLEF_DESTROY) { 02059 PPROCESSINFO ppi = pdesk->rpwinstaParent->pTerm->ptiDesktop->ppi; 02060 PPROCESSINFO ppiSave; 02061 PWND pwndMenu; 02062 DWORD dwDisableHooks; 02063 02064 /* 02065 * the menu window is destroyed -- recreate it 02066 * 02067 * clear the desktopMenu flag so that the popup is 02068 * freed. 02069 */ 02070 UserAssert(ppopupmenu->fDesktopMenu); 02071 02072 ppopupmenu->fDesktopMenu = FALSE; 02073 ppopupmenu->fDelayedFree = FALSE; 02074 02075 Unlock(&pdesk->spwndMenu); 02076 ppiSave = ptiCurrent->ppi; 02077 ptiCurrent->ppi = ppi; 02078 02079 /* 02080 * HACK HACK HACK!!! (adams) In order to create the menu window 02081 * with the correct desktop, we set the desktop of the current thread 02082 * to the new desktop. But in so doing we allow hooks on the current 02083 * thread to also hook this new desktop. This is bad, because we don't 02084 * want the menu window to be hooked while it is created. So we 02085 * temporarily disable hooks of the current thread or desktop, 02086 * and reenable them after switching back to the original desktop. 02087 */ 02088 02089 dwDisableHooks = ptiCurrent->TIF_flags & TIF_DISABLEHOOKS; 02090 ptiCurrent->TIF_flags |= TIF_DISABLEHOOKS; 02091 02092 pwndMenu = xxxCreateWindowEx( 02093 WS_EX_TOOLWINDOW | WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE, 02094 (PLARGE_STRING)MENUCLASS, 02095 NULL, 02096 WS_POPUP | WS_BORDER, 02097 0, 02098 0, 02099 100, 02100 100, 02101 NULL, 02102 NULL, 02103 hModuleWin, 02104 NULL, 02105 WINVER); 02106 02107 UserAssert(ptiCurrent->TIF_flags & TIF_DISABLEHOOKS); 02108 ptiCurrent->TIF_flags = (ptiCurrent->TIF_flags & ~TIF_DISABLEHOOKS) | dwDisableHooks; 02109 02110 Lock(&(pdesk->spwndMenu), pwndMenu); 02111 02112 UserAssert(((PMENUWND)pwndMenu)->ppopupmenu != NULL); 02113 02114 /* 02115 * Set the desktopMenu flag to mark that this is the popup 02116 * allocated for pdesk->spwndMenu 02117 * Unlock spwndPopupMenu to avoid this special case in the common code path. 02118 */ 02119 ((PMENUWND)pwndMenu)->ppopupmenu->fDesktopMenu = TRUE; 02120 Unlock(&((PMENUWND)pwndMenu)->ppopupmenu->spwndPopupMenu); 02121 02122 ptiCurrent->ppi = ppiSave; 02123 02124 HMChangeOwnerThread(pdesk->spwndMenu, pdesk->rpwinstaParent->pTerm->ptiDesktop); 02125 } else { 02126 TL tlpwndDesk; 02127 02128 ThreadLockAlways(pdesk->spwndMenu, &tlpwndT); 02129 ThreadLockAlways(pdesk->pDeskInfo->spwnd, &tlpwndDesk); 02130 xxxSetParent(pdesk->spwndMenu, pdesk->pDeskInfo->spwnd); 02131 ThreadUnlock(&tlpwndDesk); 02132 ThreadUnlock(&tlpwndT); 02133 02134 } 02135 02136 02137 pwndHierarchy = pdesk->spwndMenu; 02138 Lock(&pwndHierarchy->spwndOwner, ppopupmenu->spwndNotify); 02139 pwndHierarchy->head.pti = ptiCurrent; 02140 02141 /* 02142 * Make the topmost state match the menu mode 02143 */ 02144 if ((TestWF(pdesk->spwndMenu, WEFTOPMOST) && pMenuState->fModelessMenu) 02145 || (!TestWF(pdesk->spwndMenu, WEFTOPMOST) && !pMenuState->fModelessMenu)) { 02146 02147 ThreadLock(pdesk->spwndMenu, &tlpwndHierarchy); 02148 xxxSetWindowPos(pdesk->spwndMenu, 02149 (pMenuState->fModelessMenu ? PWND_NOTOPMOST: PWND_TOPMOST), 02150 0,0,0,0, 02151 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING); 02152 ThreadUnlock(&tlpwndHierarchy); 02153 } 02154 02155 ppopupmenuHierarchy = ((PMENUWND)pwndHierarchy)->ppopupmenu; 02156 02157 UserAssert(ppopupmenuHierarchy->fDesktopMenu); 02158 /* 02159 * clear any leftover data from the last time we used it 02160 * Assert that we're not zapping any locks here 02161 */ 02162 UserAssert(ppopupmenuHierarchy->spwndPopupMenu == NULL); 02163 UserAssert(ppopupmenuHierarchy->spwndNextPopup == NULL); 02164 UserAssert(ppopupmenuHierarchy->spwndPrevPopup == NULL); 02165 UserAssert(ppopupmenuHierarchy->spmenu == NULL); 02166 UserAssert(ppopupmenuHierarchy->spmenuAlternate == NULL); 02167 UserAssert(ppopupmenuHierarchy->spwndNotify == NULL); 02168 UserAssert(ppopupmenuHierarchy->spwndActivePopup == NULL); 02169 02170 RtlZeroMemory((PVOID)ppopupmenuHierarchy, sizeof(POPUPMENU)); 02171 ppopupmenuHierarchy->fDesktopMenu = TRUE; 02172 02173 ppopupmenuHierarchy->posSelectedItem = MFMWFP_NOITEM; 02174 Lock(&ppopupmenuHierarchy->spwndPopupMenu, pdesk->spwndMenu); 02175 02176 Lock(&(ppopupmenuHierarchy->spwndNotify), ppopupmenu->spwndNotify); 02177 LockPopupMenu(ppopupmenuHierarchy, &ppopupmenuHierarchy->spmenu, pItem->spSubMenu); 02178 02179 } else { 02180 02181 ThreadLock(ppopupmenu->spwndNotify, &tlpwndT); 02182 02183 pwndHierarchy = xxxCreateWindowEx( 02184 WS_EX_TOOLWINDOW | WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE, 02185 (PLARGE_STRING)MENUCLASS, NULL, 02186 WS_POPUP | WS_BORDER, 0, 0, 100, 100, ppopupmenu->spwndNotify, 02187 NULL, (HANDLE)ppopupmenu->spwndNotify->hModule, 02188 (LPVOID)pItem->spSubMenu, WINVER); 02189 02190 ThreadUnlock(&tlpwndT); 02191 02192 if (!pwndHierarchy) 02193 goto Exit; 02194 02195 /* 02196 * Do this so old apps don't get weird borders on the popups of 02197 * hierarchical items! 02198 */ 02199 ClrWF(pwndHierarchy, WFOLDUI); 02200 02201 ppopupmenuHierarchy = ((PMENUWND)pwndHierarchy)->ppopupmenu; 02202 02203 } 02204 02205 /* 02206 * Mark this as fDelayedFree and link it 02207 */ 02208 ppopupmenuHierarchy->fDelayedFree = TRUE; 02209 ppopupmenuHierarchy->ppmDelayedFree = ppopupmenu->ppopupmenuRoot->ppmDelayedFree; 02210 ppopupmenu->ppopupmenuRoot->ppmDelayedFree = ppopupmenuHierarchy; 02211 02212 #ifdef USE_MIRRORING 02213 if (TestWF(ppopupmenu->spwndPopupMenu, WEFLAYOUTRTL)) { 02214 SetWF(pwndHierarchy, WEFLAYOUTRTL); 02215 } else { 02216 ClrWF(pwndHierarchy, WEFLAYOUTRTL); 02217 } 02218 #endif 02219 02220 02221 Lock(&(ppopupmenuHierarchy->spwndNotify), ppopupmenu->spwndNotify); 02222 #if DBG 02223 /* 02224 * We should associate ppopupmenuHierarchy to the same menu we sent the 02225 * WM_INITMsENUPOPUP message. Otherwise, the WM_UNINITMENUPOPUP 02226 * will go to the wrong window. It would be the app's fault... 02227 */ 02228 if (!ppopupmenu->fNoNotify && (hmenuInit != PtoHq(pItem->spSubMenu))) { 02229 RIPMSG2(RIP_WARNING, "xxxMNOpenHierarchy: bad app changed submenu from %#p to %#p", 02230 hmenuInit, PtoHq(pItem->spSubMenu)); 02231 } 02232 #endif 02233 LockPopupMenu(ppopupmenuHierarchy, &ppopupmenuHierarchy->spmenu, pItem->spSubMenu); 02234 Lock(&(ppopupmenu->spwndNextPopup), pwndHierarchy); 02235 ppopupmenu->posDropped = ppopupmenu->posSelectedItem; 02236 Lock(&(ppopupmenuHierarchy->spwndPrevPopup), ppopupmenu->spwndPopupMenu); 02237 ppopupmenuHierarchy->ppopupmenuRoot = ppopupmenu->ppopupmenuRoot; 02238 ppopupmenuHierarchy->fHasMenuBar = ppopupmenu->fHasMenuBar; 02239 ppopupmenuHierarchy->fIsSysMenu = ppopupmenu->fIsSysMenu; 02240 ppopupmenuHierarchy->fNoNotify = ppopupmenu->fNoNotify; 02241 ppopupmenuHierarchy->fSendUninit = TRUE; 02242 ppopupmenuHierarchy->fRtoL = ppopupmenu->fRtoL; 02243 02244 /* 02245 * The menu window has been created and intialized so if 02246 * something fails, the WM_UNINITMENUPOPUP message will 02247 * be sent from xxxMNDestroyHandler 02248 */ 02249 fSendUninit = FALSE; 02250 02251 /* 02252 * Set/clear the underline flag 02253 */ 02254 if (pMenuState->fUnderline) { 02255 SetMF(ppopupmenuHierarchy->spmenu, MFUNDERLINE); 02256 } else { 02257 ClearMF(ppopupmenuHierarchy->spmenu, MFUNDERLINE); 02258 } 02259 02260 ppopupmenuHierarchy->fAboutToHide = FALSE; 02261 02262 /* 02263 * Find the size of the menu window and actually size it (wParam = 1) 02264 */ 02265 ThreadLock(pwndHierarchy, &tlpwndHierarchy); 02266 sizeHierarchy = (LONG)xxxSendMessage(pwndHierarchy, MN_SIZEWINDOW, MNSW_SIZE, 0); 02267 02268 if (!sizeHierarchy) { 02269 /* 02270 * No size for this menu so zero it and blow off. 02271 */ 02272 UserAssert(ppopupmenuHierarchy->fDelayedFree); 02273 if (ppopupmenuHierarchy->fDesktopMenu) { 02274 xxxMNDestroyHandler(ppopupmenuHierarchy); 02275 xxxCleanupDesktopMenu(pwndHierarchy, pdesk); 02276 } 02277 02278 if (ThreadUnlock(&tlpwndHierarchy)) { 02279 if (!ppopupmenuHierarchy->fDesktopMenu) { 02280 xxxDestroyWindow(pwndHierarchy); 02281 } 02282 } 02283 02284 Unlock(&ppopupmenu->spwndNextPopup); 02285 goto Exit; 02286 } 02287 02288 cxPopup = LOWORD(sizeHierarchy) + 2*SYSMET(CXFIXEDFRAME); 02289 cyPopup = HIWORD(sizeHierarchy) + 2*SYSMET(CYFIXEDFRAME); 02290 02291 ppopupmenu->fHierarchyDropped = TRUE; 02292 02293 /* 02294 * Find out the x,y position to drop the hierarchy and the animation 02295 * direction 02296 */ 02297 ppopupmenuHierarchy->iDropDir = xxxMNPositionHierarchy( 02298 ppopupmenu, pItem, cxPopup, cyPopup, &xLeft, &yTop, &pMonitor); 02299 02300 if (ppopupmenu->fIsMenuBar && _GetAsyncKeyState(VK_LBUTTON) & 0x8000) { 02301 /* 02302 * If the menu had to be pinned to the bottom of the screen and 02303 * the mouse button is down, make sure the mouse isn't over the 02304 * menu rect. 02305 */ 02306 RECT rc; 02307 RECT rcParent; 02308 int xrightdrop; 02309 int xleftdrop; 02310 02311 /* 02312 * Get rect of hierarchical 02313 */ 02314 CopyOffsetRect( 02315 &rc, 02316 &pwndHierarchy->rcWindow, 02317 xLeft - pwndHierarchy->rcWindow.left, 02318 yTop - pwndHierarchy->rcWindow.top); 02319 02320 /* 02321 * Get the rect of the menu bar popup item 02322 */ 02323 rcParent.left = pItem->xItem + ppopupmenu->spwndPopupMenu->rcWindow.left; 02324 rcParent.top = pItem->yItem + ppopupmenu->spwndPopupMenu->rcWindow.top; 02325 rcParent.right = rcParent.left + pItem->cxItem; 02326 rcParent.bottom = rcParent.top + pItem->cyItem; 02327 02328 if (IntersectRect(&rc, &rc, &rcParent)) { 02329 02330 /* 02331 * Oh, oh... The cursor will sit right on top of a menu item. 02332 * If the user up clicks, a menu will be accidently selected. 02333 * 02334 * Calc x position of hierarchical if we dropped it to the 02335 * right/left of the menu bar item. 02336 */ 02337 xrightdrop = ppopupmenu->spwndPopupMenu->rcWindow.left + 02338 pItem->xItem + pItem->cxItem + cxPopup; 02339 02340 if (xrightdrop > pMonitor->rcMonitor.right) { 02341 xrightdrop = 0; 02342 } 02343 02344 xleftdrop = ppopupmenu->spwndPopupMenu->rcWindow.left + 02345 pItem->xItem - cxPopup; 02346 02347 if (xleftdrop < pMonitor->rcMonitor.left) { 02348 xleftdrop = 0; 02349 } 02350 02351 if (((SYSMET(MENUDROPALIGNMENT) || TestMFT(pItem, MFT_RIGHTORDER)) 02352 && xleftdrop) || !xrightdrop) { 02353 xLeft = ppopupmenu->spwndPopupMenu->rcWindow.left + 02354 pItem->xItem - cxPopup; 02355 ppopupmenuHierarchy->iDropDir = PAS_LEFT; 02356 } else if (xrightdrop) { 02357 xLeft = ppopupmenu->spwndPopupMenu->rcWindow.left + 02358 pItem->xItem + pItem->cxItem; 02359 ppopupmenuHierarchy->iDropDir = PAS_RIGHT; 02360 } 02361 } 02362 } 02363 02364 /* 02365 * Take care of fDropNextPopup (menu bar) or fDroppedLeft (popups) 02366 * Set animation flag 02367 */ 02368 if (ppopupmenu->fIsMenuBar) { 02369 /* 02370 * Only the first popup being dropped off the menu bar 02371 * is animated. 02372 */ 02373 if (!ppopupmenu->fDropNextPopup) { 02374 ppopupmenuHierarchy->iDropDir |= PAS_OUT; 02375 } 02376 02377 /* 02378 * Propagate right-to-left direction. 02379 */ 02380 if (ppopupmenu->fDroppedLeft || (ppopupmenuHierarchy->iDropDir == PAS_LEFT)) { 02381 ppopupmenuHierarchy->fDroppedLeft = TRUE; 02382 } 02383 /* 02384 * Once a popup is dropped from the menu bar, moving to the next 02385 * item on the menu bar should drop the popup. 02386 */ 02387 ppopupmenu->fDropNextPopup = TRUE; 02388 } else { 02389 /* 02390 * Submenus always animate. 02391 */ 02392 ppopupmenuHierarchy->iDropDir |= PAS_OUT; 02393 02394 /* 02395 * Is this popup a lefty? 02396 */ 02397 if (ppopupmenuHierarchy->iDropDir == PAS_LEFT) { 02398 ppopupmenuHierarchy->fDroppedLeft = TRUE; 02399 } 02400 } 02401 02402 /* 02403 * The previous active dude must be visible 02404 */ 02405 UserAssert((ppopupmenu->ppopupmenuRoot->spwndActivePopup == NULL) 02406 || TestWF(ppopupmenu->ppopupmenuRoot->spwndActivePopup, WFVISIBLE)); 02407 02408 /* 02409 * This is the new active popup 02410 */ 02411 Lock(&(ppopupmenu->ppopupmenuRoot->spwndActivePopup), pwndHierarchy); 02412 02413 /* 02414 * Paint the owner window before the popup menu comes up so that 02415 * the proper bits are saved. 02416 */ 02417 if (ppopupmenuHierarchy->spwndNotify != NULL) { 02418 ThreadLockAlways(ppopupmenuHierarchy->spwndNotify, &tlpwndT); 02419 xxxUpdateWindow(ppopupmenuHierarchy->spwndNotify); 02420 ThreadUnlock(&tlpwndT); 02421 } 02422 02423 /* 02424 * If this is a drag and drop menu, then we need to register the window 02425 * as a drop target. 02426 */ 02427 if (pMenuState->fDragAndDrop) { 02428 if (!NT_SUCCESS(xxxClientRegisterDragDrop(HW(pwndHierarchy)))) { 02429 RIPMSG1(RIP_ERROR, "xxxMNOpenHierarchy: xxxClientRegisterDragDrop failed:%#p", pwndHierarchy); 02430 } 02431 } 02432 02433 /* 02434 * Show the window. Modeless menus are not topmost and get activated. 02435 * Modal menus are topmost but don't get activated. 02436 */ 02437 PlayEventSound(USER_SOUND_MENUPOPUP); 02438 02439 xxxSetWindowPos(pwndHierarchy, 02440 (pMenuState->fModelessMenu ? PWND_TOP : PWND_TOPMOST), 02441 xLeft, yTop, 0, 0, 02442 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOOWNERZORDER 02443 | (pMenuState->fModelessMenu ? 0 : SWP_NOACTIVATE)); 02444 02445 if (FWINABLE()) { 02446 xxxWindowEvent(EVENT_SYSTEM_MENUPOPUPSTART, pwndHierarchy, OBJID_CLIENT, INDEXID_CONTAINER, 0); 02447 } 02448 02449 /* 02450 * Select the first item IFF we're in keyboard mode. This fixes a 02451 * surprising number of compatibility problems with keyboard macros, 02452 * scripts, etc. 02453 */ 02454 if (pMenuState->mnFocus == KEYBDHOLD) { 02455 xxxSendMessage(pwndHierarchy, MN_SELECTITEM, 0, 0L); 02456 } 02457 02458 /* 02459 * This is needed so that popup menus are properly drawn on sys 02460 * modal dialog boxes. 02461 */ 02462 xxxUpdateWindow(pwndHierarchy); 02463 02464 ret = pwndHierarchy; 02465 ThreadUnlock(&tlpwndHierarchy); 02466 02467 Exit: 02468 /* 02469 * send matching WM_UNINITMENUPOPUP if needed (i.e, something 02470 * failed). 02471 */ 02472 if (fSendUninit 02473 && (ppopupmenu->spwndNotify != NULL)) { 02474 02475 ThreadLockAlways(ppopupmenu->spwndNotify, &tlpwndT); 02476 xxxSendMessage(ppopupmenu->spwndNotify, WM_UNINITMENUPOPUP, 02477 (WPARAM)hmenuInit, 02478 MAKELONG(0, (ppopupmenu->fIsSysMenu ? MF_SYSMENU : 0))); 02479 ThreadUnlock(&tlpwndT); 02480 } 02481 02482 return ret; 02483 } 02484 02485 /***************************************************************************\ 02486 * 02487 * MNHideNextHierarchy() 02488 * 02489 * Closes any submenu coming off of this popup. 02490 * 02491 \***************************************************************************/ 02492 BOOL xxxMNHideNextHierarchy(PPOPUPMENU ppopup) 02493 { 02494 if (ppopup->spwndNextPopup != NULL) { 02495 TL tlpwndT; 02496 02497 ThreadLockAlways(ppopup->spwndNextPopup, &tlpwndT); 02498 if (ppopup->spwndNextPopup != ppopup->spwndActivePopup) 02499 xxxSendMessage(ppopup->spwndNextPopup, MN_CLOSEHIERARCHY, 0, 0L); 02500 02501 xxxSendMessage(ppopup->spwndNextPopup, MN_SELECTITEM, (WPARAM)-1, 0L); 02502 ThreadUnlock(&tlpwndT); 02503 return TRUE; 02504 } 02505 return FALSE; 02506 } 02507 02508 02509 /***************************************************************************\ 02510 * void MenuCloseHierarchyHandler(PPOPUPMENU ppopupmenu) 02511 * effects: Close all hierarchies from this window down. 02512 * 02513 * History: 02514 * 05-25-91 Mikehar Ported from Win3.1 02515 \***************************************************************************/ 02516 02517 void xxxMNCloseHierarchy( 02518 PPOPUPMENU ppopupmenu, PMENUSTATE pMenuState) 02519 { 02520 TL tlpwndNext; 02521 TL tlpwnd; 02522 TL tlpopup; 02523 PTHREADINFO ptiCurrent = PtiCurrent(); 02524 PDESKTOP pdesk; 02525 PWND pwndNext; 02526 02527 Validateppopupmenu(ppopupmenu); 02528 02529 /* 02530 * Terminate any animation 02531 */ 02532 MNAnimate(pMenuState, FALSE); 02533 02534 /* 02535 * If a hierarchy exists, close all childen below us. Do it in reversed 02536 * order so savebits work out. 02537 */ 02538 if (!ppopupmenu->fHierarchyDropped) { 02539 /* 02540 * Assert that there's no next or it might not get closed 02541 */ 02542 UserAssert(ppopupmenu->spwndNextPopup == NULL); 02543 return; 02544 } 02545 02546 if (ppopupmenu->fHideTimer) 02547 { 02548 _KillTimer(ppopupmenu->spwndPopupMenu, IDSYS_MNHIDE); 02549 ppopupmenu->fHideTimer = FALSE; 02550 } 02551 02552 pwndNext = ppopupmenu->spwndNextPopup; 02553 if (pwndNext != NULL) { 02554 02555 ThreadLockAlways(pwndNext, &tlpwndNext); 02556 xxxSendMessage(pwndNext, MN_CLOSEHIERARCHY, 0, 0); 02557 02558 /* 02559 * If modeless menu, activate the this popup since we're about 02560 * to destroy the current active one. We want to keep activation 02561 * on a menu window so we can get the keys. Also, modeless menus 02562 * are canceled when a non-menu window is activated in their queue 02563 */ 02564 if (pMenuState->fModelessMenu 02565 && pMenuState->fInsideMenuLoop 02566 && !ppopupmenu->fIsMenuBar) { 02567 02568 ThreadLockAlways(ppopupmenu->spwndPopupMenu, &tlpwnd); 02569 xxxActivateThisWindow(ppopupmenu->spwndPopupMenu, 0, 0); 02570 ThreadUnlock(&tlpwnd); 02571 } 02572 02573 if (FWINABLE()) { 02574 xxxWindowEvent(EVENT_SYSTEM_MENUPOPUPEND, pwndNext, OBJID_CLIENT, INDEXID_CONTAINER, 0); 02575 } 02576 02577 /* 02578 * If the current thread is not in the right pdesk, then that could 02579 * be the cause of the stuck menu bug. 02580 * In other words, are we nuking this menu out of context? 02581 */ 02582 UserAssert(ptiCurrent->pMenuState != NULL); 02583 pdesk = ptiCurrent->rpdesk; 02584 02585 if (pwndNext == pdesk->spwndMenu) { 02586 PPOPUPMENU ppopupmenuReal; 02587 02588 UserAssert(pdesk->dwDTFlags & DF_MENUINUSE); 02589 02590 /* 02591 * If this is our precreated real popup window, 02592 * initialize ourselves and just hide. 02593 */ 02594 xxxShowWindow(pwndNext, SW_HIDE | TEST_PUDF(PUDF_ANIMATE)); 02595 02596 /* 02597 * Its possible that during Logoff the above xxxShowWindow 02598 * won't get prossessed and because this window is a special 02599 * window that is owned by they desktop we have to manually mark 02600 * it as invisible. 02601 */ 02602 if (TestWF(pwndNext, WFVISIBLE)) { 02603 SetVisible(pwndNext, SV_UNSET); 02604 } 02605 02606 #ifdef HAVE_MN_GETPPOPUPMENU 02607 ppopupmenuReal = (PPOPUPMENU)xxxSendMessage(pwndNext, 02608 MN_GETPPOPUPMENU,0, 0L); 02609 #else 02610 ppopupmenuReal = ((PMENUWND)pwndNext)->ppopupmenu; 02611 #endif 02612 UserAssert(ppopupmenuReal->fDesktopMenu == TRUE); 02613 02614 /* 02615 * We don't want this window to be a drop target anymore. 02616 * Non cached menu windows revoke it on WM_FINALDESTROY. 02617 */ 02618 if (pMenuState->fDragAndDrop) { 02619 if (!NT_SUCCESS(xxxClientRevokeDragDrop(HW(pwndNext)))) { 02620 RIPMSG1(RIP_ERROR, "xxxMNCloseHierarchy: xxxClientRevokeRegisterDragDrop failed:%#p", pwndNext); 02621 } 02622 } 02623 02624 if (ppopupmenuReal != NULL) { 02625 xxxMNDestroyHandler(ppopupmenuReal); 02626 /* 02627 * We used to clear the popup contents here but the popup might be 02628 * still in use if this is happening during a callback. So we let 02629 * MNFreePopup do that. If it didn't happen during the call above, 02630 * it'll happen when MNFlushDestroyedPopups is executed. 02631 */ 02632 } 02633 02634 xxxCleanupDesktopMenu(pwndNext, pdesk); 02635 02636 ThreadUnlock(&tlpwndNext); 02637 } else if (ThreadUnlock(&tlpwndNext)) { 02638 /* 02639 * We know this is not the current thread's desktop menu window. 02640 * Let's assert that it's not the menu window of another desktop. 02641 */ 02642 UserAssert(pwndNext != pwndNext->head.rpdesk->spwndMenu); 02643 xxxDestroyWindow(pwndNext); 02644 } 02645 02646 Unlock(&ppopupmenu->spwndNextPopup); 02647 ppopupmenu->fHierarchyDropped = FALSE; 02648 02649 } 02650 02651 if (ppopupmenu->fIsMenuBar) { 02652 Unlock(&ppopupmenu->spwndActivePopup); 02653 } else { 02654 Lock(&(ppopupmenu->ppopupmenuRoot->spwndActivePopup), 02655 ppopupmenu->spwndPopupMenu); 02656 } 02657 02658 if (pMenuState->fInsideMenuLoop && 02659 (ppopupmenu->posSelectedItem != MFMWFP_NOITEM)) { 02660 /* 02661 * Send a menu select as if this item had just been selected. This 02662 * allows people to easily update their menu status bars when a 02663 * hierarchy from this item has been closed. 02664 */ 02665 PWND pwnd = ppopupmenu->ppopupmenuRoot->spwndNotify; 02666 if (pwnd) { 02667 ThreadLockAlways(pwnd, &tlpwnd); 02668 ThreadLockAlways(ppopupmenu->spwndPopupMenu, &tlpopup); 02669 xxxSendMenuSelect(pwnd, ppopupmenu->spwndPopupMenu, 02670 ppopupmenu->spmenu, ppopupmenu->posSelectedItem); 02671 ThreadUnlock(&tlpopup); 02672 ThreadUnlock(&tlpwnd); 02673 } 02674 } 02675 02676 } 02677 02678 /***************************************************************************\ 02679 * 02680 * MNDoubleClick() 02681 * 02682 * If an item isn't a hierarchical, then the double-click works just like 02683 * single click did. Otherwise, we traverse the submenu hierarchy to find 02684 * a valid default element. If we reach a submenu that has no valid default 02685 * subitems and it itself has a valid ID, that becomes the valid default 02686 * element. 02687 * 02688 * Note: This function does not remove the double click message 02689 * from the message queue, so the caller must do so. 02690 * 02691 * BOGUS 02692 * How about opening the hierarchies if we don't find anything? 02693 * 02694 * Returns TRUE if handled. 02695 * 02696 \***************************************************************************/ 02697 BOOL xxxMNDoubleClick(PMENUSTATE pMenuState, PPOPUPMENU ppopup, int idxItem) 02698 { 02699 PMENU pMenu; 02700 PITEM pItem; 02701 MSG msg; 02702 UINT uPos; 02703 02704 /* 02705 * This code to swallow double clicks isn't executed! MNLoop will 02706 * swallow all double clicks for us. Swallow the up button for the 02707 * double dude instead. Word will not be happy if they get a spurious 02708 * WM_LBUTTONUP on the menu bar if their code to close the MDI child 02709 * doesn't swallow it soon enough. 02710 */ 02711 02712 /* 02713 * Eat the click. 02714 */ 02715 if (xxxPeekMessage(&msg, NULL, 0, 0, PM_NOYIELD)) { 02716 if ((msg.message == WM_LBUTTONUP) || 02717 (msg.message == WM_NCLBUTTONUP)) { 02718 xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE); 02719 } 02720 #if DBG 02721 else if (msg.message == WM_LBUTTONDBLCLK || 02722 msg.message == WM_NCLBUTTONDBLCLK) 02723 { 02724 UserAssertMsg0(FALSE, "xxxMNDoubleClick found a double click"); 02725 } 02726 #endif 02727 } 02728 02729 /* 02730 * Get current item. 02731 */ 02732 pMenu = ppopup->spmenu; 02733 if ((pMenu==NULL) || ((UINT)idxItem >= pMenu->cItems)) { 02734 xxxMNDoScroll(ppopup, ppopup->posSelectedItem, FALSE); 02735 goto Done; 02736 } 02737 02738 pItem = pMenu->rgItems + idxItem; 02739 uPos = idxItem; 02740 02741 /* 02742 * Do nothing if item is disabled. 02743 */ 02744 if (pItem->fState & MFS_GRAYED) 02745 goto Done; 02746 02747 /* 02748 * Traverse the hierarchy down as far as possible. 02749 */ 02750 do 02751 { 02752 if (pItem->spSubMenu != NULL) { 02753 /* 02754 * The item is a popup menu, so continue traversing. 02755 */ 02756 pMenu = pItem->spSubMenu; 02757 idxItem = (UINT)_GetMenuDefaultItem(pMenu, MF_BYPOSITION, 0); 02758 02759 if (idxItem != -1) { 02760 pItem = pMenu->rgItems + idxItem; 02761 uPos = idxItem; 02762 continue; 02763 } else /* if (lpItem->wID == -1) How do we know this popup has an ID? */ 02764 break; 02765 } 02766 02767 /* 02768 * We've found a leaf node of some kind, either a MFS_DEFAULT popup 02769 * with a valid cmd ID that has no valid MFS_DEFAULT children, or 02770 * a real cmd with MFS_DEFAULT style. 02771 * 02772 * Exit menu mode and send command ID. 02773 */ 02774 02775 /* 02776 * For old apps we need to generate a WM_MENUSELECT message first. 02777 * Old apps, esp. Word 6.0, can't handle double-clicks on maximized 02778 * child sys menus because they never get a WM_MENUSELECT for the 02779 * item, unlike with normal keyboard/mouse choosing. We need to 02780 * fake it so they won't fault. Several VB apps have a similar 02781 * problem. 02782 */ 02783 if (!TestWF(ppopup->ppopupmenuRoot->spwndNotify, WFWIN40COMPAT)) 02784 { 02785 TL tlpwndNotify, tlpopup; 02786 02787 ThreadLock(ppopup->ppopupmenuRoot->spwndNotify, &tlpwndNotify); 02788 ThreadLock(ppopup->spwndPopupMenu, &tlpopup); 02789 xxxSendMenuSelect(ppopup->ppopupmenuRoot->spwndNotify, 02790 ppopup->spwndPopupMenu, pMenu, idxItem); 02791 ThreadUnlock(&tlpopup); 02792 ThreadUnlock(&tlpwndNotify); 02793 } 02794 02795 xxxMNDismissWithNotify(pMenuState, pMenu, pItem, uPos, 0); 02796 return TRUE; 02797 } 02798 while (TRUE); 02799 02800 Done: 02801 return(FALSE); 02802 } 02803 02804 02805 /***************************************************************************\ 02806 * UINT MenuSelectItemHandler(PPOPUPMENU ppopupmenu, int itemPos) 02807 * 02808 * Unselects the old selection, selects the item at itemPos and highlights it. 02809 * 02810 * MFMWFP_NOITEM if no item is to be selected. 02811 * 02812 * Returns the item flags of the item being selected. 02813 * 02814 * History: 02815 * 05-25-91 Mikehar Ported from Win3.1 02816 \***************************************************************************/ 02817 02818 PITEM xxxMNSelectItem( 02819 PPOPUPMENU ppopupmenu, 02820 PMENUSTATE pMenuState, 02821 UINT itemPos) 02822 { 02823 PITEM pItem = NULL; 02824 TL tlpwndNotify; 02825 TL tlpwndPopup; 02826 TL tlpmenu; 02827 PWND pwndNotify; 02828 PMENU pmenu; 02829 02830 if (ppopupmenu->posSelectedItem == itemPos) { 02831 02832 /* 02833 * If this item is already selectected, just return its flags. 02834 */ 02835 if ((itemPos != MFMWFP_NOITEM) && (itemPos < ppopupmenu->spmenu->cItems)) { 02836 return &(ppopupmenu->spmenu->rgItems[itemPos]); 02837 } 02838 return NULL; 02839 } 02840 02841 /* 02842 * Terminate any animation 02843 */ 02844 MNAnimate(pMenuState, FALSE); 02845 02846 if (ppopupmenu->fShowTimer) { 02847 _KillTimer(ppopupmenu->spwndPopupMenu, IDSYS_MNSHOW); 02848 ppopupmenu->fShowTimer = FALSE; 02849 } 02850 02851 ThreadLock(pmenu = ppopupmenu->spmenu, &tlpmenu); 02852 ThreadLock(pwndNotify = ppopupmenu->spwndNotify, &tlpwndNotify); 02853 02854 if (ppopupmenu->fAboutToHide) 02855 { 02856 PPOPUPMENU ppopupPrev = ((PMENUWND)(ppopupmenu->spwndPrevPopup))->ppopupmenu; 02857 02858 _KillTimer(ppopupPrev->spwndPopupMenu, IDSYS_MNHIDE); 02859 ppopupPrev->fHideTimer = FALSE; 02860 if (ppopupPrev->fShowTimer) 02861 { 02862 _KillTimer(ppopupPrev->spwndPopupMenu, IDSYS_MNSHOW); 02863 ppopupPrev->fShowTimer = FALSE; 02864 } 02865 02866 if (ppopupPrev->posSelectedItem != ppopupPrev->posDropped) 02867 { 02868 TL tlpmenuPopupMenuPrev; 02869 ThreadLock(ppopupPrev->spmenu, &tlpmenuPopupMenuPrev); 02870 if (ppopupPrev->posSelectedItem != MFMWFP_NOITEM) { 02871 xxxMNInvertItem(ppopupPrev, ppopupPrev->spmenu, 02872 ppopupPrev->posSelectedItem, ppopupPrev->spwndNotify, FALSE); 02873 } 02874 02875 ppopupPrev->posSelectedItem = ppopupPrev->posDropped; 02876 02877 xxxMNInvertItem(ppopupPrev, ppopupPrev->spmenu, 02878 ppopupPrev->posDropped, ppopupPrev->spwndNotify, TRUE); 02879 ThreadUnlock(&tlpmenuPopupMenuPrev); 02880 } 02881 02882 ppopupmenu->fAboutToHide = FALSE; 02883 Lock(&ppopupmenu->ppopupmenuRoot->spwndActivePopup, ppopupmenu->spwndPopupMenu); 02884 } 02885 02886 if (MNIsItemSelected(ppopupmenu)) { 02887 /* 02888 * Something else is selected so we need to unselect it. 02889 */ 02890 if (ppopupmenu->spwndNextPopup) { 02891 if (ppopupmenu->fIsMenuBar) { 02892 xxxMNCloseHierarchy(ppopupmenu, pMenuState); 02893 } else { 02894 MNSetTimerToCloseHierarchy(ppopupmenu); 02895 } 02896 } 02897 02898 goto DeselectItem; 02899 } else if (MNIsScrollArrowSelected(ppopupmenu)) { 02900 _KillTimer(ppopupmenu->spwndPopupMenu, ppopupmenu->posSelectedItem); 02901 DeselectItem: 02902 02903 xxxMNInvertItem(ppopupmenu, pmenu, 02904 ppopupmenu->posSelectedItem, pwndNotify, FALSE); 02905 } 02906 02907 ppopupmenu->posSelectedItem = itemPos; 02908 02909 if (itemPos != MFMWFP_NOITEM) { 02910 /* 02911 * If an item is selected, no autodismiss plus this means 02912 * that the mouse is on the menu 02913 */ 02914 pMenuState->fAboutToAutoDismiss = 02915 pMenuState->fMouseOffMenu = FALSE; 02916 02917 if (pMenuState->fButtonDown) { 02918 xxxMNDoScroll(ppopupmenu, itemPos, TRUE); 02919 } 02920 02921 pItem = xxxMNInvertItem(ppopupmenu, pmenu, 02922 itemPos, pwndNotify, TRUE); 02923 ThreadUnlock(&tlpwndNotify); 02924 ThreadUnlock(&tlpmenu); 02925 return pItem; 02926 02927 } else if (FWINABLE()) { 02928 /* 02929 * Notify that nothing is now focused in this menu. 02930 */ 02931 xxxWindowEvent(EVENT_OBJECT_FOCUS, ppopupmenu->spwndPopupMenu, 02932 ((ppopupmenu->spwndNotify != ppopupmenu->spwndPopupMenu) ? OBJID_CLIENT : 02933 (ppopupmenu->fIsSysMenu ? OBJID_SYSMENU : OBJID_MENU)), 0, 0); 02934 } 02935 02936 ThreadUnlock(&tlpwndNotify); 02937 ThreadUnlock(&tlpmenu); 02938 02939 if (ppopupmenu->spwndPrevPopup != NULL) { 02940 PPOPUPMENU pp; 02941 02942 /* 02943 * Get the popupMenu data for the previous menu 02944 * Use the root popupMenu if the previous menu is the menu bar 02945 */ 02946 if (ppopupmenu->fHasMenuBar && (ppopupmenu->spwndPrevPopup == 02947 ppopupmenu->spwndNotify)) { 02948 pp = ppopupmenu->ppopupmenuRoot; 02949 } else { 02950 #ifdef HAVE_MN_GETPPOPUPMENU 02951 TL tlpwndPrevPopup; 02952 ThreadLock(ppopupmenu->spwndPrevPopup, &tlpwndPrevPopup); 02953 pp = (PPOPUPMENU)xxxSendMessage(ppopupmenu->spwndPrevPopup, 02954 MN_GETPPOPUPMENU, 0, 0L); 02955 ThreadUnlock(&tlpwndPrevPopup); 02956 #else 02957 pp = ((PMENUWND)ppopupmenu->spwndPrevPopup)->ppopupmenu; 02958 #endif 02959 } 02960 02961 /* 02962 * Generate a WM_MENUSELECT for the previous menu to re-establish 02963 * it's current item as the SELECTED item 02964 */ 02965 ThreadLock(pp->spwndNotify, &tlpwndNotify); 02966 ThreadLock(pp->spwndPopupMenu, &tlpwndPopup); 02967 xxxSendMenuSelect(pp->spwndNotify, pp->spwndPopupMenu, pp->spmenu, pp->posSelectedItem); 02968 ThreadUnlock(&tlpwndPopup); 02969 ThreadUnlock(&tlpwndNotify); 02970 } 02971 02972 return NULL; 02973 } 02974 02975 /***************************************************************************\ 02976 * 02977 * MNItemHitTest() 02978 * 02979 * Given a hMenu and a point in screen coordinates, returns the position 02980 * of the item the point is in. Returns -1 if no item exists there. 02981 * 02982 \***************************************************************************/ 02983 UINT MNItemHitTest(PMENU pMenu, PWND pwnd, POINT pt) 02984 { 02985 PITEM pItem; 02986 UINT iItem; 02987 RECT rect; 02988 02989 PTHREADINFO ptiCurrent = PtiCurrent(); 02990 02991 if (pMenu->cItems == 0) 02992 return MFMWFP_NOITEM; 02993 02994 02995 /* 02996 * This point is screen-relative. Menu bar coordinates relative 02997 * to the window. But popup menu coordinates are relative to the client. 02998 */ 02999 if (TestMF(pMenu, MFISPOPUP)) { 03000 03001 /* 03002 * Bail if it's outside rcWindow 03003 */ 03004 CopyInflateRect(&rect, &(pwnd->rcWindow), 03005 -SYSMET(CXFIXEDFRAME), -SYSMET(CYFIXEDFRAME)); 03006 03007 if (!PtInRect(&rect, pt)) { 03008 return MFMWFP_NOITEM; 03009 } 03010 03011 /* ScreenToClient */ 03012 #ifdef USE_MIRRORING 03013 if (TestWF(pwnd, WEFLAYOUTRTL)) { 03014 pt.x = pwnd->rcClient.right - pt.x; 03015 } else 03016 #endif 03017 { 03018 pt.x -= pwnd->rcClient.left; 03019 } 03020 pt.y -= pwnd->rcClient.top; 03021 03022 /* 03023 * If on the non client area, then it's on the scroll arrows 03024 */ 03025 if (pt.y < 0) { 03026 return MFMWFP_UPARROW; 03027 } else if (pt.y > (int)pMenu->cyMenu) { 03028 return MFMWFP_DOWNARROW; 03029 } 03030 03031 } else { 03032 /* ScreenToWindow */ 03033 #ifdef USE_MIRRORING 03034 if (TestWF(pwnd, WEFLAYOUTRTL) && 03035 ( 03036 (ptiCurrent->pq->codeCapture == SCREEN_CAPTURE) || (ptiCurrent->pq->codeCapture == NO_CAP_SYS) 03037 ) 03038 ) { 03039 pt.x = pwnd->rcWindow.right - pt.x; 03040 } else 03041 #endif 03042 { 03043 pt.x -= pwnd->rcWindow.left; 03044 } 03045 pt.y -= pwnd->rcWindow.top; 03046 } 03047 03048 /* 03049 * Step through all the items in the menu. 03050 * If scrollable menu 03051 */ 03052 if (pMenu->dwArrowsOn != MSA_OFF) { 03053 UserAssert(TestMF(pMenu, MFISPOPUP)); 03054 pItem = MNGetToppItem(pMenu); 03055 rect.left = rect.top = 0; 03056 rect.right = pItem->cxItem; 03057 rect.bottom = pItem->cyItem; 03058 for (iItem = pMenu->iTop; (iItem < (int)pMenu->cItems) && (rect.top < (int)pMenu->cyMenu); iItem++) { 03059 03060 if (PtInRect(&rect, pt)) { 03061 return iItem; 03062 } 03063 03064 pItem++; 03065 rect.top = rect.bottom; 03066 rect.bottom += pItem->cyItem; 03067 } 03068 } else { 03069 /* 03070 * No scroll bars. 03071 */ 03072 for (iItem = 0, pItem = pMenu->rgItems; iItem < pMenu->cItems; iItem++, pItem++) { 03073 /* Is the mouse inside this item's rectangle? */ 03074 rect.left = pItem->xItem; 03075 rect.top = pItem->yItem; 03076 rect.right = pItem->xItem + pItem->cxItem; 03077 rect.bottom = pItem->yItem + pItem->cyItem; 03078 03079 if (PtInRect(&rect, pt)) { 03080 return(iItem); 03081 } 03082 } 03083 } 03084 03085 03086 return(MFMWFP_NOITEM); 03087 } 03088 /***************************************************************************\ 03089 * LockMFMWFPWindow 03090 * 03091 * This function is called when we need to save the return value of 03092 * xxxMNFindWindowFromPoint. 03093 * 03094 * History: 03095 * 11/14/96 GerardoB Created 03096 \***************************************************************************/ 03097 void LockMFMWFPWindow (PULONG_PTR puHitArea, ULONG_PTR uNewHitArea) 03098 { 03099 /* 03100 * Bail if there is nothing to do. 03101 */ 03102 if (*puHitArea == uNewHitArea) { 03103 return; 03104 } 03105 03106 /* 03107 * Unlock current hit area 03108 */ 03109 UnlockMFMWFPWindow(puHitArea); 03110 03111 /* 03112 * Lock new hit area 03113 */ 03114 if (IsMFMWFPWindow(uNewHitArea)) { 03115 Lock(puHitArea, (PWND)uNewHitArea); 03116 } else { 03117 *puHitArea = uNewHitArea; 03118 } 03119 } 03120 /***************************************************************************\ 03121 * UnlockMFMWFPWindow 03122 * 03123 * You must called this if you ever called LockMFMWFPWindow 03124 * 03125 * History: 03126 * 11/14/96 GerardoB Created 03127 \***************************************************************************/ 03128 void UnlockMFMWFPWindow (PULONG_PTR puHitArea) 03129 { 03130 if (IsMFMWFPWindow(*puHitArea)) { 03131 Unlock(puHitArea); 03132 } else { 03133 *puHitArea = MFMWFP_OFFMENU; 03134 } 03135 } 03136 /***************************************************************************\ 03137 * IsMFMWFPWindow 03138 * 03139 * Test whether or not the return value of xxxMNFindWindowFromPoint is 03140 * a window. Not that uHitArea could be an HWND or a PWND. 03141 * 03142 * History: 03143 * 10-02-96 GerardoB Created 03144 \***************************************************************************/ 03145 BOOL IsMFMWFPWindow (ULONG_PTR uHitArea) 03146 { 03147 switch(uHitArea) { 03148 case MFMWFP_OFFMENU: 03149 case MFMWFP_NOITEM: 03150 case MFMWFP_ALTMENU: 03151 return FALSE; 03152 03153 default: 03154 return TRUE; 03155 } 03156 } 03157 /***************************************************************************\ 03158 * LONG MenuFindMenuWindowFromPoint( 03159 * PPOPUPMENU ppopupmenu, PUINT pIndex, POINT screenPt) 03160 * 03161 * effects: Determines in which window the point lies. 03162 * 03163 * Returns 03164 * - PWND of the hierarchical menu the point is on, 03165 * - MFMWFP_ALTMENU if point lies on the alternate popup menu. 03166 * - MFMWFP_NOITEM if there is no item at that point on the menu or the 03167 * point lies on the menu bar. 03168 * - MFMWFP_OFFMENU if point lies elsewhere. 03169 * 03170 * Returns in pIndex 03171 * - the index of the item hit, 03172 * - MFMWFP_NOITEM if there is no item at that point on the menu or 03173 * point lies on the menu bar. 03174 * 03175 * History: 03176 * 05-25-91 Mikehar Ported from Win3.1 03177 * 8-11-92 Sanfords added MFMWFP_ constants 03178 \***************************************************************************/ 03179 03180 LONG_PTR xxxMNFindWindowFromPoint( 03181 PPOPUPMENU ppopupmenu, 03182 PUINT pIndex, 03183 POINTS screenPt) 03184 { 03185 POINT pt; 03186 RECT rect; 03187 LONG_PTR longHit; 03188 UINT itemHit; 03189 PWND pwnd; 03190 TL tlpwndT; 03191 #ifdef USE_MIRRORING 03192 int cx; 03193 #endif 03194 03195 03196 *pIndex = 0; 03197 03198 if (ppopupmenu->spwndNextPopup) { 03199 03200 /* 03201 * Check if this point is on any of our children before checking if it 03202 * is on ourselves. 03203 */ 03204 ThreadLockAlways(ppopupmenu->spwndNextPopup, &tlpwndT); 03205 longHit = xxxSendMessage(ppopupmenu->spwndNextPopup, 03206 MN_FINDMENUWINDOWFROMPOINT, (WPARAM)&itemHit, 03207 MAKELONG(screenPt.x, screenPt.y)); 03208 ThreadUnlock(&tlpwndT); 03209 03210 /* 03211 * If return value is an hwnd, convert to pwnd. 03212 */ 03213 if (IsMFMWFPWindow(longHit)) { 03214 longHit = (LONG_PTR)RevalidateHwnd((HWND)longHit); 03215 } 03216 03217 if (longHit) { 03218 03219 /* 03220 * Hit occurred on one of our children. 03221 */ 03222 03223 *pIndex = itemHit; 03224 return longHit; 03225 } 03226 } 03227 03228 if (ppopupmenu->fIsMenuBar) { 03229 int cBorders; 03230 03231 /* 03232 * Check if this point is on the menu bar 03233 */ 03234 pwnd = ppopupmenu->spwndNotify; 03235 if (pwnd == NULL) { 03236 return MFMWFP_OFFMENU; 03237 } 03238 03239 pt.x = screenPt.x; 03240 pt.y = screenPt.y; 03241 03242 if (ppopupmenu->fIsSysMenu) { 03243 03244 if (!_HasCaptionIcon(pwnd)) { 03245 /* 03246 * no system menu rect to click in if it doesn't have an icon 03247 */ 03248 return(0L); 03249 } 03250 03251 /* 03252 * Check if this is a click on the system menu icon. 03253 */ 03254 if (TestWF(pwnd, WFMINIMIZED)) { 03255 03256 /* 03257 * If the window is minimized, then check if there was a hit in 03258 * the client area of the icon's window. 03259 */ 03260 03261 /* 03262 * Mikehar 5/27 03263 * Don't know how this ever worked. If we are the system menu of an icon 03264 * we want to punt the menus if the click occurs ANYWHERE outside of the 03265 * menu. 03266 * Johnc 03-Jun-1992 the next 4 lines were commented out for Mike's 03267 * problem above but that made clicking on a minimized window with 03268 * the system menu already up, bring down the menu and put it right 03269 * up again (bug 10951) because the mnloop wouldn't swallow the mouse 03270 * down click message. The problem Mike mentions no longer shows up. 03271 */ 03272 03273 if (PtInRect(&(pwnd->rcWindow), pt)) { 03274 return MFMWFP_NOITEM; 03275 } 03276 03277 /* 03278 * It's an iconic window, so can't be hitting anywhere else. 03279 */ 03280 return MFMWFP_OFFMENU; 03281 } 03282 03283 /* 03284 * Check if we are hitting on the system menu rectangle on the top 03285 * left of windows. 03286 */ 03287 rect.top = rect.left = 0; 03288 rect.right = SYSMET(CXSIZE); 03289 rect.bottom = SYSMET(CYSIZE); 03290 03291 cBorders = GetWindowBorders(pwnd->style, pwnd->ExStyle, TRUE, FALSE); 03292 03293 OffsetRect(&rect, pwnd->rcWindow.left + cBorders*SYSMET(CXBORDER), 03294 pwnd->rcWindow.top + cBorders*SYSMET(CYBORDER)); 03295 #ifdef USE_MIRRORING 03296 //Mirror the rect because the buttons in the left hand side of the window if it mirrored 03297 if (TestWF(pwnd, WEFLAYOUTRTL)) { 03298 cx = rect.right - rect.left; 03299 rect.right = pwnd->rcWindow.right - (rect.left - pwnd->rcWindow.left); 03300 rect.left = rect.right - cx; 03301 } 03302 #endif 03303 if (PtInRect(&rect, pt)) { 03304 *pIndex = 0; 03305 return(MFMWFP_NOITEM); 03306 } 03307 /* 03308 * Check if we hit in the alternate menu if available. 03309 */ 03310 if (ppopupmenu->spmenuAlternate) { 03311 itemHit = MNItemHitTest(ppopupmenu->spmenuAlternate, pwnd, pt); 03312 if (itemHit != MFMWFP_NOITEM) { 03313 *pIndex = itemHit; 03314 return MFMWFP_ALTMENU; 03315 } 03316 } 03317 return MFMWFP_OFFMENU; 03318 } else { 03319 if (TestWF(ppopupmenu->spwndNotify, WFMINIMIZED)) { 03320 03321 /* 03322 * If we are minimized, we can't hit on the main menu bar. 03323 */ 03324 return MFMWFP_OFFMENU; 03325 } 03326 } 03327 } else { 03328 pwnd = ppopupmenu->spwndPopupMenu; 03329 03330 /* 03331 * else this is a popup window and we need to check if we are hitting 03332 * anywhere on this popup window. 03333 */ 03334 pt.x = screenPt.x; 03335 pt.y = screenPt.y; 03336 if (!PtInRect(&pwnd->rcWindow, pt)) { 03337 03338 /* 03339 * Point completely outside the popup menu window so return 0. 03340 */ 03341 return MFMWFP_OFFMENU; 03342 } 03343 } 03344 03345 pt.x = screenPt.x; 03346 pt.y = screenPt.y; 03347 03348 itemHit = MNItemHitTest(ppopupmenu->spmenu, pwnd, pt); 03349 03350 if (ppopupmenu->fIsMenuBar) { 03351 03352 /* 03353 * If hit is on menu bar but no item is there, treat it as if the user 03354 * hit nothing. 03355 */ 03356 if (itemHit == MFMWFP_NOITEM) { 03357 03358 /* 03359 * Check if we hit in the alternate menu if available. 03360 */ 03361 if (ppopupmenu->spmenuAlternate) { 03362 itemHit = MNItemHitTest(ppopupmenu->spmenuAlternate, pwnd, pt); 03363 if (itemHit != MFMWFP_NOITEM) { 03364 *pIndex = itemHit; 03365 return MFMWFP_ALTMENU; 03366 } 03367 } 03368 return MFMWFP_OFFMENU; 03369 } 03370 03371 *pIndex = itemHit; 03372 return MFMWFP_NOITEM; 03373 } else { 03374 03375 /* 03376 * If hit is on popup menu but no item is there, itemHit 03377 * will be MFMWFP_NOITEM 03378 */ 03379 *pIndex = itemHit; 03380 return (LONG_PTR)pwnd; 03381 } 03382 return MFMWFP_OFFMENU; 03383 } 03384 03385 /***************************************************************************\ 03386 *void MenuCancelMenus(PPOPUPMENU ppopupmenu, 03387 * UINT cmd, BOOL fSend) 03388 * Should only be sent to the top most ppopupmenu/menu window in the 03389 * hierarchy. 03390 * 03391 * History: 03392 * 05-25-91 Mikehar Ported from Win3.1 03393 \***************************************************************************/ 03394 03395 void xxxMNCancel( 03396 PMENUSTATE pMenuState, 03397 UINT uMsg, 03398 UINT cmd, 03399 LPARAM lParam) 03400 { 03401 PPOPUPMENU ppopupmenu = pMenuState->pGlobalPopupMenu; 03402 BOOL fSynchronous = ppopupmenu->fSynchronous; 03403 BOOL fTrackFlagsSet = ppopupmenu->fIsTrackPopup; 03404 BOOL fIsSysMenu = ppopupmenu->fIsSysMenu; 03405 BOOL fIsMenuBar = ppopupmenu->fIsMenuBar; 03406 BOOL fNotify = !ppopupmenu->fNoNotify; 03407 PWND pwndT; 03408 TL tlpwndT; 03409 TL tlpwndPopupMenu; 03410 03411 Validateppopupmenu(ppopupmenu); 03412 03413 pMenuState->fInsideMenuLoop = FALSE; 03414 pMenuState->fButtonDown = FALSE; 03415 /* 03416 * Mark the popup as destroyed so people will not use it anymore. 03417 * This means that root popups can be marked as destroyed before 03418 * actually being destroyed (nice and confusing). 03419 */ 03420 ppopupmenu->fDestroyed = TRUE; 03421 03422 /* 03423 * Only the menu loop owner can destroy the menu windows (i.e, xxxMNCloseHierarchy) 03424 */ 03425 if (PtiCurrent() != pMenuState->ptiMenuStateOwner) { 03426 RIPMSG1(RIP_WARNING, "xxxMNCancel: Thread %#p doesn't own the menu loop", PtiCurrent()); 03427 return; 03428 } 03429 03430 /* 03431 * If the menu loop is running on a thread different than the thread 03432 * that owns spwndNotify, we can have two threads trying to cancel 03433 * this popup at the same time. 03434 */ 03435 if (ppopupmenu->fInCancel) { 03436 RIPMSG1(RIP_WARNING, "xxxMNCancel: already in cancel. ppopupmenu:%#p", ppopupmenu); 03437 return; 03438 } 03439 ppopupmenu->fInCancel = TRUE; 03440 03441 ThreadLock(ppopupmenu->spwndPopupMenu, &tlpwndPopupMenu); 03442 03443 /* 03444 * Close all hierarchies from this point down. 03445 */ 03446 xxxMNCloseHierarchy(ppopupmenu, pMenuState); 03447 03448 /* 03449 * Unselect any items on this top level window 03450 */ 03451 xxxMNSelectItem(ppopupmenu, pMenuState, MFMWFP_NOITEM); 03452 03453 pMenuState->fMenuStarted = FALSE; 03454 03455 pwndT = ppopupmenu->spwndNotify; 03456 03457 ThreadLock(pwndT, &tlpwndT); 03458 03459 xxxMNReleaseCapture(); 03460 03461 if (fTrackFlagsSet) { 03462 /* 03463 * Send a POPUPEND so people watching see them paired 03464 */ 03465 if (FWINABLE()) { 03466 xxxWindowEvent(EVENT_SYSTEM_MENUPOPUPEND, 03467 ppopupmenu->spwndPopupMenu, OBJID_CLIENT, 0, 0); 03468 } 03469 03470 UserAssert(ppopupmenu->spwndPopupMenu != ppopupmenu->spwndPopupMenu->head.rpdesk->spwndMenu); 03471 xxxDestroyWindow(ppopupmenu->spwndPopupMenu); 03472 } 03473 03474 if (pwndT == NULL) { 03475 ThreadUnlock(&tlpwndT); 03476 ThreadUnlock(&tlpwndPopupMenu); 03477 return; 03478 } 03479 03480 /* 03481 * SMS_NOMENU hack so we can send MenuSelect messages with 03482 * (loword(lparam) = -1) when 03483 * the menu pops back up for the CBT people. In 3.0, all WM_MENUSELECT 03484 * messages went through the message filter so go through the function 03485 * SendMenuSelect. We need to do this in 3.1 since WordDefect for Windows 03486 * depends on this. 03487 */ 03488 xxxSendMenuSelect(pwndT, NULL, SMS_NOMENU, MFMWFP_NOITEM); 03489 03490 if (FWINABLE()) { 03491 xxxWindowEvent(EVENT_SYSTEM_MENUEND, pwndT, (fIsSysMenu ? 03492 OBJID_SYSMENU : (fIsMenuBar ? OBJID_MENU : OBJID_WINDOW)), 03493 INDEXID_CONTAINER, 0); 03494 } 03495 03496 if (fNotify) { 03497 /* 03498 * Notify app we are exiting the menu loop. Mainly for WinOldApp 386. 03499 * wParam is 1 if a TrackPopupMenu else 0. 03500 */ 03501 xxxSendMessage(pwndT, WM_EXITMENULOOP, 03502 ((fTrackFlagsSet && !fIsSysMenu)? 1 : 0), 0); 03503 } 03504 03505 if (uMsg != 0) { 03506 PlayEventSound(USER_SOUND_MENUCOMMAND); 03507 pMenuState->cmdLast = cmd; 03508 if (!fSynchronous) { 03509 if (!fIsSysMenu && fTrackFlagsSet && !TestWF(pwndT, WFWIN31COMPAT)) { 03510 xxxSendMessage(pwndT, uMsg, cmd, lParam); 03511 } else { 03512 _PostMessage(pwndT, uMsg, cmd, lParam); 03513 } 03514 } 03515 } else 03516 pMenuState->cmdLast = 0; 03517 03518 ThreadUnlock(&tlpwndT); 03519 03520 ThreadUnlock(&tlpwndPopupMenu); 03521 03522 } 03523 /***************************************************************************\ 03524 * void MenuButtonDownHandler(PPOPUPMENU ppopupmenu, int posItemHit) 03525 * effects: Handles a mouse down on the menu associated with ppopupmenu at 03526 * item index posItemHit. posItemHit could be MFMWFP_NOITEM if user hit on a 03527 * menu where no item exists. 03528 * 03529 * History: 03530 * 05-25-91 Mikehar Ported from Win3.1 03531 \***************************************************************************/ 03532 03533 void xxxMNButtonDown( 03534 PPOPUPMENU ppopupmenu, 03535 PMENUSTATE pMenuState, 03536 UINT posItemHit, BOOL fClick) 03537 { 03538 PITEM pItem; 03539 BOOL fOpenHierarchy; 03540 03541 /* 03542 * A different item was hit than is currently selected, so select it 03543 * and drop its menu if available. Make sure we toggle click state. 03544 */ 03545 if (ppopupmenu->posSelectedItem != posItemHit) { 03546 /* 03547 * We are clicking on a new item, not moving the mouse over to it. 03548 * So reset cancel toggle state. We don't want button up from 03549 * this button down to cancel. 03550 */ 03551 if (fClick) { 03552 fOpenHierarchy = TRUE; 03553 ppopupmenu->fToggle = FALSE; 03554 } 03555 else 03556 { 03557 fOpenHierarchy = (ppopupmenu->fDropNextPopup != 0); 03558 } 03559 03560 03561 /* 03562 * If the item has a popup and isn't disabled, open it. Note that 03563 * selecting this item will cancel any hierarchies associated with 03564 * the previously selected item. 03565 */ 03566 pItem = xxxMNSelectItem(ppopupmenu, pMenuState, posItemHit); 03567 if (MNIsPopupItem(pItem) && fOpenHierarchy) { 03568 /* Punt if menu was destroyed. */ 03569 if (xxxMNOpenHierarchy(ppopupmenu, pMenuState) == (PWND)-1) { 03570 return; 03571 } 03572 } 03573 } else { 03574 /* 03575 * We are moving over to the already-selected item. If we are 03576 * clicking for real, reset cancel toggle state. We want button 03577 * up to cancel if on same item. Otherwise, do nothing if just 03578 * moving... 03579 */ 03580 if (fClick) { 03581 ppopupmenu->fToggle = TRUE; 03582 } 03583 03584 if (!xxxMNHideNextHierarchy(ppopupmenu) && fClick && xxxMNOpenHierarchy(ppopupmenu, pMenuState)) 03585 ppopupmenu->fToggle = FALSE; 03586 } 03587 03588 if (fClick) { 03589 pMenuState->fButtonDown = TRUE; 03590 xxxMNDoScroll(ppopupmenu, posItemHit, TRUE); 03591 } 03592 } 03593 03594 /***************************************************************************\ 03595 * MNSetTimerToAutoDissmiss 03596 * 03597 * History: 03598 * 11/14/96 GerardoB Created 03599 \***************************************************************************/ 03600 void MNSetTimerToAutoDismiss(PMENUSTATE pMenuState, PWND pwnd) 03601 { 03602 if (pMenuState->fAutoDismiss && !pMenuState->fAboutToAutoDismiss) { 03603 if (_SetTimer(pwnd, IDSYS_MNAUTODISMISS, 16 * gdtMNDropDown, NULL)) { 03604 pMenuState->fAboutToAutoDismiss = TRUE; 03605 } else { 03606 RIPMSG0(RIP_WARNING, "xxxMNMouseMove: Failed to set autodismiss timer"); 03607 } 03608 } 03609 } 03610 /***************************************************************************\ 03611 * void MenuMouseMoveHandler(PPOPUPMENU ppopupmenu, POINT screenPt) 03612 * Handles a mouse move to the given point. 03613 * 03614 * History: 03615 * 05-25-91 Mikehar Ported from Win3.1 03616 \***************************************************************************/ 03617 03618 void xxxMNMouseMove( 03619 PPOPUPMENU ppopup, 03620 PMENUSTATE pMenuState, 03621 POINTS ptScreen) 03622 { 03623 LONG_PTR cmdHitArea; 03624 UINT uFlags; 03625 UINT cmdItem; 03626 PWND pwnd; 03627 TL tlpwndT; 03628 03629 03630 if (!IsRootPopupMenu(ppopup)) { 03631 RIPMSG0(RIP_ERROR, 03632 "MenuMouseMoveHandler() called for a non top most menu"); 03633 return; 03634 } 03635 03636 /* 03637 * Ignore mouse moves that aren't really moves. MSTEST jiggles 03638 * the mouse for some reason. And windows coming and going will 03639 * force mouse moves, to reset the cursor. 03640 */ 03641 if ((ptScreen.x == pMenuState->ptMouseLast.x) && (ptScreen.y == pMenuState->ptMouseLast.y)) 03642 return; 03643 03644 pMenuState->ptMouseLast.x = ptScreen.x; 03645 pMenuState->ptMouseLast.y = ptScreen.y; 03646 03647 /* 03648 * Find out where this mouse move occurred. 03649 */ 03650 cmdHitArea = xxxMNFindWindowFromPoint(ppopup, &cmdItem, ptScreen); 03651 03652 /* 03653 * If coming from an IDropTarget call out, remember the hit test 03654 */ 03655 if (pMenuState->fInDoDragDrop) { 03656 xxxMNUpdateDraggingInfo(pMenuState, cmdHitArea, cmdItem); 03657 } 03658 03659 if (pMenuState->mnFocus == KEYBDHOLD) { 03660 /* 03661 * Ignore mouse moves when in keyboard mode if the mouse isn't over any 03662 * menu at all. Also ignore mouse moves if over minimized window, 03663 * because we pretend that its entire window is like system menu. 03664 */ 03665 if ((cmdHitArea == MFMWFP_OFFMENU) || 03666 ((cmdHitArea == MFMWFP_NOITEM) && TestWF(ppopup->spwndNotify, WFMINIMIZED))) { 03667 return; 03668 } 03669 03670 pMenuState->mnFocus = MOUSEHOLD; 03671 } 03672 03673 if (cmdHitArea == MFMWFP_ALTMENU) { 03674 /* 03675 * User clicked in the other menu so switch to it ONLY IF 03676 * MOUSE IS DOWN. Usability testing proves that people frequently 03677 * get kicked into the system menu accidentally when browsing the 03678 * menu bar. We support the Win3.1 behavior when the mouse is 03679 * down however. 03680 */ 03681 if (pMenuState->fButtonDown) { 03682 xxxMNSwitchToAlternateMenu(ppopup); 03683 cmdHitArea = MFMWFP_NOITEM; 03684 } else 03685 goto OverNothing; 03686 } 03687 03688 if (cmdHitArea == MFMWFP_NOITEM) { 03689 /* 03690 * Mouse move occurred to an item in the main menu bar. If the item 03691 * is different than the one already selected, close up the current 03692 * one, select the new one and drop its menu. But if the item is the 03693 * same as the one currently selected, we need to pull up any popups 03694 * if needed and just keep the current level visible. Hey, this is 03695 * the same as a mousedown so lets do that instead. 03696 */ 03697 xxxMNButtonDown(ppopup, pMenuState, cmdItem, FALSE); 03698 return; 03699 } else if (cmdHitArea != 0) { 03700 /* This is a popup window we moved onto. */ 03701 pwnd = (PWND)(cmdHitArea); 03702 ThreadLock(pwnd, &tlpwndT); 03703 03704 UserAssert(TestWF(pwnd, WFVISIBLE)); 03705 03706 /* 03707 * Modeless menus don't capture the mouse, so track it to know 03708 * when it leaves the popup. 03709 */ 03710 ppopup = ((PMENUWND)pwnd)->ppopupmenu; 03711 if (pMenuState->fModelessMenu 03712 && !pMenuState->fInDoDragDrop 03713 && !ppopup->fTrackMouseEvent) { 03714 03715 TRACKMOUSEEVENT tme; 03716 03717 /* tme.cbSize = sizeof(TRACKMOUSEEVENT); Not checked on kernel side */ 03718 tme.dwFlags = TME_LEAVE; 03719 tme.hwndTrack = PtoH(pwnd); 03720 TrackMouseEvent(&tme); 03721 ppopup->fTrackMouseEvent = TRUE; 03722 03723 /* 03724 * We just entered this window so make sure the cursor 03725 * is properly set. 03726 */ 03727 xxxSendMessage(pwnd, WM_SETCURSOR, (WPARAM)HWq(pwnd), MAKELONG(MSGF_MENU, 0)); 03728 03729 } 03730 03731 /* 03732 * Select the item. 03733 */ 03734 uFlags = (UINT)xxxSendMessage(pwnd, MN_SELECTITEM, (WPARAM)cmdItem, 0L); 03735 if ((uFlags & MF_POPUP) && !(uFlags & MFS_GRAYED)) { 03736 /* 03737 * User moved back onto an item with a hierarchy. Hide the 03738 * the dropped popup. 03739 */ 03740 if (!xxxSendMessage(pwnd, MN_SETTIMERTOOPENHIERARCHY, 0, 0L)) { 03741 xxxMNHideNextHierarchy(ppopup); 03742 } 03743 } 03744 ThreadUnlock(&tlpwndT); 03745 } else 03746 OverNothing: 03747 { 03748 /* We moved off all menu windows... */ 03749 if (ppopup->spwndActivePopup != NULL) { 03750 pwnd = ppopup->spwndActivePopup; 03751 03752 ThreadLock(pwnd, &tlpwndT); 03753 xxxSendMessage(pwnd, MN_SELECTITEM, MFMWFP_NOITEM, 0L); 03754 MNSetTimerToAutoDismiss(pMenuState, pwnd); 03755 ThreadUnlock(&tlpwndT); 03756 } else { 03757 xxxMNSelectItem(ppopup, pMenuState, MFMWFP_NOITEM); 03758 } 03759 03760 } 03761 } 03762 03763 03764 /***************************************************************************\ 03765 * void MenuButtonUpHandler(PPOPUPMENU ppopupmenu, int posItemHit) 03766 * effects: Handles a mouse button up at the given point. 03767 * 03768 * History: 03769 * 05-25-91 Mikehar Ported from Win3.1 03770 \***************************************************************************/ 03771 03772 void xxxMNButtonUp( 03773 PPOPUPMENU ppopup, 03774 PMENUSTATE pMenuState, 03775 UINT posItemHit, 03776 LPARAM lParam) 03777 { 03778 PITEM pItem; 03779 03780 if (!pMenuState->fButtonDown) { 03781 03782 /* 03783 * Ignore if button was never down... Really shouldn't happen... 03784 */ 03785 return; 03786 } 03787 03788 if (posItemHit == MFMWFP_NOITEM) { 03789 RIPMSG0(RIP_WARNING, "button up on no item"); 03790 goto ExitButtonUp; 03791 } 03792 03793 if (ppopup->posSelectedItem != posItemHit) { 03794 goto ExitButtonUp; 03795 } 03796 03797 if (ppopup->fIsMenuBar) { 03798 03799 /* 03800 * Handle button up in menubar specially. 03801 */ 03802 if (ppopup->fHierarchyDropped) { 03803 if (!ppopup->fToggle) { 03804 goto ExitButtonUp; 03805 } else { 03806 /* 03807 * Cancel menu now. 03808 */ 03809 ppopup->fToggle = FALSE; 03810 xxxMNDismiss(pMenuState); 03811 return; 03812 } 03813 } 03814 } else if (ppopup->fShowTimer) { 03815 ppopup->fToggle = FALSE; 03816 03817 /* 03818 * Open hierarchy on popup 03819 */ 03820 xxxMNOpenHierarchy(ppopup, pMenuState); 03821 03822 goto ExitButtonUp; 03823 } 03824 03825 /* 03826 * If nothing is selected, get out. This occurs mainly on unbalanced 03827 * multicolumn menus where one of the columns isn't completely full. 03828 */ 03829 if (ppopup->posSelectedItem == MFMWFP_NOITEM) 03830 goto ExitButtonUp; 03831 03832 if (ppopup->posSelectedItem >= ppopup->spmenu->cItems) 03833 goto ExitButtonUp; 03834 03835 /* 03836 * Get a pointer to the currently selected item in this menu. 03837 */ 03838 pItem = &(ppopup->spmenu->rgItems[ppopup->posSelectedItem]); 03839 03840 /* 03841 * Kick out of menu mode if user clicked on a non-separator, enabled, 03842 * non-hierarchical item. 03843 * 03844 * BOGUS 03845 * Why doesn't MFS_GRAYED check work for separators now? Find out later. 03846 */ 03847 if (!(pItem->fType & MFT_SEPARATOR) 03848 && !(pItem->fState & MFS_GRAYED) 03849 && (pItem->spSubMenu == NULL)) { 03850 03851 xxxMNDismissWithNotify(pMenuState, ppopup->spmenu, pItem, 03852 ppopup->posSelectedItem, lParam); 03853 return; 03854 } 03855 03856 ExitButtonUp: 03857 pMenuState->fButtonDown = 03858 pMenuState->fButtonAlwaysDown = FALSE; 03859 } 03860 03861 03862 /***************************************************************************\ 03863 *UINT MenuSetTimerToOpenHierarchy(PPOPUPMENU ppopupmenu) 03864 * Given the current selection, set a timer to show this hierarchy if 03865 * valid else return 0. If a timer should be set but couldn't return -1. 03866 * 03867 * History: 03868 * 05-25-91 Mikehar Ported from Win3.1 03869 \***************************************************************************/ 03870 UINT MNSetTimerToOpenHierarchy( 03871 PPOPUPMENU ppopup) 03872 { 03873 PITEM pItem; 03874 03875 /* 03876 * No selection so fail 03877 */ 03878 if (ppopup->posSelectedItem == MFMWFP_NOITEM) 03879 return(0); 03880 03881 if (ppopup->posSelectedItem >= ppopup->spmenu->cItems) 03882 return(0); 03883 03884 /* 03885 * Is item an enabled popup? 03886 * Get a pointer to the currently selected item in this menu. 03887 */ 03888 pItem = ppopup->spmenu->rgItems + ppopup->posSelectedItem; 03889 if ((pItem->spSubMenu == NULL) || (pItem->fState & MFS_GRAYED)) 03890 return(0); 03891 03892 if (ppopup->fShowTimer 03893 || (ppopup->fHierarchyDropped 03894 && (ppopup->posSelectedItem == ppopup->posDropped))) { 03895 03896 /* 03897 * A timer is already set or the hierarchy is already opened. 03898 */ 03899 return 1; 03900 } 03901 03902 if (!_SetTimer(ppopup->spwndPopupMenu, IDSYS_MNSHOW, gdtMNDropDown, NULL)) 03903 return (UINT)-1; 03904 03905 ppopup->fShowTimer = TRUE; 03906 03907 return 1; 03908 } 03909 03910 03911 03912 /***************************************************************************\ 03913 * MNSetTimerToCloseHierarchy 03914 * 03915 \***************************************************************************/ 03916 03917 UINT MNSetTimerToCloseHierarchy(PPOPUPMENU ppopup) 03918 { 03919 03920 if (!ppopup->fHierarchyDropped) 03921 return(0); 03922 03923 if (ppopup->fHideTimer) 03924 return(1); 03925 03926 if (!_SetTimer(ppopup->spwndPopupMenu, IDSYS_MNHIDE, gdtMNDropDown, NULL)) 03927 return((UINT) -1); 03928 03929 ppopup->fHideTimer = TRUE; 03930 03931 ppopup = ((PMENUWND)(ppopup->spwndNextPopup))->ppopupmenu; 03932 ppopup->fAboutToHide = TRUE; 03933 03934 return(1); 03935 } 03936 03937 03938 /***************************************************************************\ 03939 * xxxCallHandleMenuMessages 03940 * 03941 * Modeless menus don't have a modal loop so we don't see the messages until 03942 * they are dispatched to xxxMenuWindowProc. So we call this function to 03943 * process the message just like we would in the modal case, only that 03944 * the message has already been pulled out of the queue. 03945 * This is also calledfrom xxxScanSysQueue to pass mouse messages on the menu 03946 * bar or from xxxMNDragOver to upadate the mouse position when being draged over. 03947 * 03948 * History: 03949 * 10/25/96 GerardoB Created 03950 \***************************************************************************/ 03951 BOOL xxxCallHandleMenuMessages(PMENUSTATE pMenuState, PWND pwnd, UINT message, WPARAM wParam, LPARAM lParam) 03952 { 03953 BOOL fHandled; 03954 MSG msg; 03955 03956 CheckLock(pwnd); 03957 03958 UserAssert(pMenuState->fModelessMenu || pMenuState->fInDoDragDrop); 03959 03960 /* 03961 * Since modeless menus don't capture the mouse, then we need to 03962 * keep checking on the mouse button when the mouse is off the 03963 * menu. 03964 * Note that we do not set fMouseOffMenu if fInDoDragDrop is set 03965 */ 03966 if (pMenuState->fMouseOffMenu && pMenuState->fButtonDown) { 03967 UserAssert(!pMenuState->fInDoDragDrop && pMenuState->fModelessMenu); 03968 MNCheckButtonDownState(pMenuState); 03969 } 03970 03971 /* 03972 * Setup the msg structure 03973 */ 03974 msg.hwnd = HW(pwnd); 03975 msg.message = message; 03976 msg.wParam = wParam; 03977 03978 /* 03979 * xxxHandleMenuMessages expects screen coordinates 03980 */ 03981 if ((message >= WM_MOUSEFIRST) && (message <= WM_MOUSELAST)) { 03982 msg.lParam = MAKELONG(GET_X_LPARAM(lParam) + pwnd->rcClient.left, 03983 GET_Y_LPARAM(lParam) + pwnd->rcClient.top); 03984 } else { 03985 msg.lParam = lParam; 03986 } 03987 03988 /* 03989 * Not used by xxxHandleMenuMessages 03990 */ 03991 msg.time = 0; 03992 msg.pt.x = msg.pt.x = 0; 03993 03994 03995 UserAssert(pMenuState->pGlobalPopupMenu != NULL); 03996 03997 pMenuState->fInCallHandleMenuMessages = TRUE; 03998 fHandled = xxxHandleMenuMessages(&msg, pMenuState, pMenuState->pGlobalPopupMenu); 03999 pMenuState->fInCallHandleMenuMessages = FALSE; 04000 04001 /* 04002 * If the message was handled and this is a modeless menu, 04003 * check to see if it's time to go. 04004 */ 04005 if (fHandled 04006 && pMenuState->fModelessMenu 04007 && ExitMenuLoop (pMenuState, pMenuState->pGlobalPopupMenu)) { 04008 04009 xxxEndMenuLoop (pMenuState, pMenuState->pGlobalPopupMenu); 04010 xxxMNEndMenuState(TRUE); 04011 } 04012 04013 return fHandled; 04014 } 04015 /***************************************************************************\ 04016 * 04017 * History: 04018 * 05-25-91 Mikehar Ported from Win3.1 04019 * 08-12-96 jparsons Catch NULL lParam on WM_CREATE [51986] 04020 \***************************************************************************/ 04021 04022 LRESULT xxxMenuWindowProc( 04023 PWND pwnd, 04024 UINT message, 04025 WPARAM wParam, 04026 LPARAM lParam) 04027 { 04028 BOOL fIsRecursedMenu; 04029 LRESULT lRet; 04030 PAINTSTRUCT ps; 04031 PPOPUPMENU ppopupmenu; 04032 PMENUSTATE pMenuState; 04033 PMENU pmenu; 04034 PITEM pItem; 04035 TL tlpmenu; 04036 TL tlpwndNotify; 04037 PDESKTOP pdesk = pwnd->head.rpdesk; 04038 POINT ptOrg; 04039 HDC hdcAni; 04040 04041 CheckLock(pwnd); 04042 04043 VALIDATECLASSANDSIZE(pwnd, message, wParam, lParam, FNID_MENU, WM_NCCREATE); 04044 04045 /* 04046 * If we're not in menu mode or this window is just being created, 04047 * there are only few messages we care about. 04048 */ 04049 pMenuState = GetpMenuState(pwnd); 04050 ppopupmenu = ((PMENUWND)pwnd)->ppopupmenu; 04051 pmenu = (ppopupmenu != NULL ? ppopupmenu->spmenu : NULL); 04052 if ((pMenuState == NULL) || (pmenu == NULL)) { 04053 switch (message) { 04054 case WM_NCCREATE: 04055 case WM_FINALDESTROY: 04056 break; 04057 04058 case MN_SETHMENU: 04059 if (ppopupmenu != NULL) { 04060 break; 04061 } else { 04062 return 0; 04063 } 04064 04065 default: 04066 goto CallDWP; 04067 } 04068 } else { 04069 /* 04070 * TPM_RECURSE support: make sure we grab the proper pMenuState. 04071 */ 04072 fIsRecursedMenu = ((ppopupmenu->ppopupmenuRoot != NULL) 04073 && IsRecursedMenuState(pMenuState, ppopupmenu)); 04074 if (fIsRecursedMenu) { 04075 while (IsRecursedMenuState(pMenuState, ppopupmenu) 04076 && (pMenuState->pmnsPrev != NULL)) { 04077 pMenuState = pMenuState->pmnsPrev; 04078 } 04079 UserAssert(pMenuState->pGlobalPopupMenu == ppopupmenu->ppopupmenuRoot); 04080 } 04081 04082 Validateppopupmenu(ppopupmenu); 04083 04084 /* 04085 * If this is a modeless menu, give xxxHandleMenuMessages the first 04086 * shot at the message 04087 */ 04088 if (pMenuState->fModelessMenu && !pMenuState->fInCallHandleMenuMessages) { 04089 /* 04090 * If this is a recursed menu, we don't want to process any 04091 * input for it until the current menu goes away. 04092 */ 04093 if (fIsRecursedMenu) { 04094 if (((message >= WM_MOUSEFIRST) && (message <= WM_MOUSELAST)) 04095 || ((message >= WM_KEYFIRST) && (message <= WM_KEYLAST)) 04096 || ((message >= WM_NCMOUSEFIRST) && (message <= WM_NCMOUSELAST))) { 04097 04098 goto CallDWP; 04099 } 04100 } else { 04101 if (xxxCallHandleMenuMessages(pMenuState, pwnd, message, wParam, lParam)) { 04102 return 0; 04103 } 04104 } 04105 } 04106 } /* else of if ((pMenuState == NULL) || (ppopupmenu == NULL)) */ 04107 04108 switch (message) { 04109 case WM_NCCREATE: 04110 /* 04111 * Ignore evil messages to prevent leaks. 04112 * Use RIP_ERROR for a while to make sure to see if we're getting here 04113 */ 04114 if (((PMENUWND)pwnd)->ppopupmenu != NULL) { 04115 RIPMSG1(RIP_ERROR, "xxxMenuWindowProc: evil WM_NCCREATE. already initialized. pwnd:%p", pwnd); 04116 return FALSE; 04117 } 04118 ppopupmenu = MNAllocPopup(TRUE); 04119 if (ppopupmenu == NULL) { 04120 return FALSE; 04121 } 04122 04123 ((PMENUWND)pwnd)->ppopupmenu = ppopupmenu; 04124 ppopupmenu->posSelectedItem = MFMWFP_NOITEM; 04125 Lock(&(ppopupmenu->spwndPopupMenu), pwnd); 04126 return TRUE; 04127 04128 case WM_NCCALCSIZE: 04129 xxxDefWindowProc(pwnd, message, wParam, lParam); 04130 if (pmenu->dwArrowsOn != MSA_OFF) { 04131 InflateRect((PRECT)lParam, 0, -gcyMenuScrollArrow); 04132 } 04133 break; 04134 04135 case WM_ERASEBKGND: 04136 if (pmenu->hbrBack != NULL) { 04137 MNEraseBackground ((HDC) wParam, pmenu, 04138 0, 0, 04139 pwnd->rcClient.right - pwnd->rcClient.left, 04140 pwnd->rcClient.bottom - pwnd->rcClient.top); 04141 return TRUE; 04142 } else { 04143 goto CallDWP; 04144 } 04145 break; 04146 04147 case WM_PRINT: 04148 /* 04149 * default processing of WM_PRINT does not handle custom non- 04150 * client painting -- which scrollable menus have -- so take 04151 * care of drawing nonclient area and then let DefWindowProc 04152 * handle the rest 04153 */ 04154 if ((lParam & PRF_NONCLIENT) && (pmenu->dwArrowsOn != MSA_OFF)) { 04155 04156 MNDrawFullNC(pwnd, (HDC)wParam, ppopupmenu); 04157 GreGetWindowOrg((HDC)wParam, &ptOrg); 04158 GreSetWindowOrg((HDC)wParam, 04159 ptOrg.x - MNXBORDER, 04160 ptOrg.y - MNYBORDER - gcyMenuScrollArrow, 04161 NULL); 04162 xxxDefWindowProc(pwnd, message, wParam, lParam & ~PRF_NONCLIENT); 04163 GreSetWindowOrg((HDC)wParam, ptOrg.x, ptOrg.y, NULL); 04164 04165 } else { 04166 goto CallDWP; 04167 } 04168 break; 04169 04170 case WM_WINDOWPOSCHANGING: 04171 if (!(((LPWINDOWPOS)lParam)->flags & SWP_SHOWWINDOW)) 04172 goto CallDWP; 04173 04174 if (!TestEffectUP(MENUANIMATION) || !(ppopupmenu->iDropDir & PAS_OUT) 04175 || (GetAppCompatFlags2(VER40) & GACF2_ANIMATIONOFF)) { 04176 NoAnimation: 04177 ppopupmenu->iDropDir &= ~PAS_OUT; 04178 goto CallDWP; 04179 } 04180 04181 /* 04182 * Create the animation bitmap. 04183 */ 04184 pMenuState->cxAni = pwnd->rcWindow.right - pwnd->rcWindow.left; 04185 pMenuState->cyAni = pwnd->rcWindow.bottom - pwnd->rcWindow.top; 04186 04187 if (TestALPHA(MENUFADE)) { 04188 if ((hdcAni = CreateFade(pwnd, NULL, CMS_MENUFADE, 04189 FADE_SHOW | FADE_MENU)) == NULL) { 04190 goto NoAnimation; 04191 } 04192 } else { 04193 04194 if (!MNCreateAnimationBitmap(pMenuState, pMenuState->cxAni, 04195 pMenuState->cyAni)) { 04196 goto NoAnimation; 04197 } 04198 04199 /* 04200 * We shouldn't be animating at this time. 04201 */ 04202 UserAssert(pMenuState->hdcWndAni == NULL); 04203 04204 /* 04205 * This window must be the active popup 04206 */ 04207 UserAssert(pMenuState->pGlobalPopupMenu->spwndActivePopup == pwnd); 04208 04209 /* 04210 * Initialize animation info 04211 */ 04212 pMenuState->hdcWndAni = _GetDCEx(pwnd, HRGN_FULL, DCX_WINDOW | DCX_USESTYLE | DCX_INTERSECTRGN); 04213 pMenuState->iAniDropDir = ppopupmenu->iDropDir; 04214 pMenuState->ixAni = (pMenuState->iAniDropDir & PAS_HORZ) ? 0 : pMenuState->cxAni; 04215 pMenuState->iyAni = (pMenuState->iAniDropDir & PAS_VERT) ? 0 : pMenuState->cyAni; 04216 hdcAni = pMenuState->hdcAni; 04217 } 04218 04219 /* 04220 * MFWINDOWDC is used by MNEraseBackground to determine where the 04221 * brush org should be set. 04222 */ 04223 SetMF(pmenu, MFWINDOWDC); 04224 04225 xxxSendMessage(pwnd, WM_PRINT, (WPARAM)hdcAni, PRF_CLIENT | PRF_NONCLIENT | PRF_ERASEBKGND); 04226 04227 ClearMF(pmenu, MFWINDOWDC); 04228 04229 /* 04230 *If owner draw, we just passed hdcAni to the client side. 04231 * The app might have deleted it (??); no blue screen seems to 04232 * happen but only painting on that DC will fail from 04233 * now on. I won't waste time handling this unless it turns 04234 * out to be a problem (it's unlikely an app would do so). 04235 */ 04236 UserAssert(GreValidateServerHandle(hdcAni, DC_TYPE)); 04237 04238 /* 04239 * While the window is still hidden, load the first fade animation 04240 * frame to avoid flicker when the window is actually shown. 04241 * 04242 * There would still be flicker with slide animations, though. It 04243 * could be fixed by using the window region, similar to 04244 * AnimateWindow. For now, too many functions would become xxx, so 04245 * let's not do it, unless it becomes a big issue. 04246 */ 04247 if (TestFadeFlags(FADE_MENU)) { 04248 ShowFade(); 04249 } 04250 goto CallDWP; 04251 04252 case WM_WINDOWPOSCHANGED: 04253 if (!(((LPWINDOWPOS)lParam)->flags & SWP_SHOWWINDOW)) 04254 goto CallDWP; 04255 04256 /* 04257 * If not animating, nothing else to do here. 04258 */ 04259 if (!(ppopupmenu->iDropDir & PAS_OUT)) 04260 goto CallDWP; 04261 04262 /* 04263 * Start the animation cycle now. 04264 */ 04265 if (TestFadeFlags(FADE_MENU)) { 04266 StartFade(); 04267 } else { 04268 pMenuState->dwAniStartTime = NtGetTickCount(); 04269 _SetTimer(pwnd, IDSYS_MNANIMATE, 1, NULL); 04270 } 04271 ppopupmenu->iDropDir &= ~PAS_OUT; 04272 goto CallDWP; 04273 04274 case WM_NCPAINT: 04275 04276 if (ppopupmenu->iDropDir & PAS_OUT) { 04277 04278 /* 04279 * When animating, validate itself to ensure no further drawing 04280 * that is not related to the animation. 04281 */ 04282 xxxValidateRect(pwnd, NULL); 04283 } else { 04284 04285 /* 04286 * If we have scroll bars, draw them 04287 */ 04288 if (pmenu->dwArrowsOn != MSA_OFF) { 04289 04290 HDC hdc = _GetDCEx(pwnd, (HRGN)wParam, 04291 DCX_USESTYLE | DCX_WINDOW | DCX_INTERSECTRGN | DCX_NODELETERGN | DCX_LOCKWINDOWUPDATE); 04292 MNDrawFullNC(pwnd, hdc, ppopupmenu); 04293 _ReleaseDC(hdc); 04294 } else { 04295 goto CallDWP; 04296 } 04297 } 04298 break; 04299 04300 case WM_PRINTCLIENT: 04301 ThreadLock(pmenu, &tlpmenu); 04302 xxxMenuDraw((HDC)wParam, pmenu); 04303 ThreadUnlock(&tlpmenu); 04304 break; 04305 04306 case WM_FINALDESTROY: 04307 /* 04308 * If we're animating, we must haved been killed in a rude way.... 04309 */ 04310 UserAssert((pMenuState == NULL) || (pMenuState->hdcWndAni == NULL)); 04311 04312 /* 04313 * If this is a drag and drop menu, then call RevokeDragDrop. 04314 */ 04315 if ((pMenuState != NULL) && pMenuState->fDragAndDrop) { 04316 if (!SUCCEEDED(xxxClientRevokeDragDrop(HW(pwnd)))) { 04317 RIPMSG1(RIP_ERROR, "xxxMenuWindowProc: xxxClientRevokeRegisterDragDrop failed:%#p", pwnd); 04318 } 04319 } 04320 04321 xxxMNDestroyHandler(ppopupmenu); 04322 return 0; 04323 04324 04325 case WM_PAINT: 04326 ThreadLock(pmenu, &tlpmenu); 04327 xxxBeginPaint(pwnd, &ps); 04328 xxxMenuDraw(ps.hdc, pmenu); 04329 xxxEndPaint(pwnd, &ps); 04330 ThreadUnlock(&tlpmenu); 04331 break; 04332 04333 case WM_CHAR: 04334 case WM_SYSCHAR: 04335 xxxMNChar(ppopupmenu, pMenuState, (UINT)wParam); 04336 break; 04337 04338 case WM_KEYDOWN: 04339 case WM_SYSKEYDOWN: 04340 xxxMNKeyDown(ppopupmenu, pMenuState, (UINT)wParam); 04341 break; 04342 04343 case WM_TIMER: 04344 switch (wParam) { 04345 case IDSYS_MNSHOW: 04346 /* 04347 * Open the window and kill the show timer. 04348 * 04349 * Cancel any toggle state we might have. We don't 04350 * want to dismiss this on button up if shown from 04351 * button down. 04352 */ 04353 ppopupmenu->fToggle = FALSE; 04354 xxxMNOpenHierarchy(ppopupmenu, pMenuState); 04355 break; 04356 04357 case IDSYS_MNHIDE: 04358 ppopupmenu->fToggle = FALSE; 04359 xxxMNCloseHierarchy(ppopupmenu,pMenuState); 04360 break; 04361 04362 case IDSYS_MNUP: 04363 case IDSYS_MNDOWN: 04364 if (pMenuState->fButtonDown) { 04365 xxxMNDoScroll(ppopupmenu, (UINT)wParam, FALSE); 04366 } else { 04367 _KillTimer(pwnd, (UINT)wParam); 04368 } 04369 break; 04370 04371 case IDSYS_MNANIMATE: 04372 if (pMenuState->hdcWndAni != NULL) { 04373 MNAnimate(pMenuState, TRUE); 04374 } else { 04375 /* 04376 * This timer shouldn't be set. Left over in msg queue? 04377 */ 04378 UserAssert(pMenuState->hdcWndAni != NULL); 04379 } 04380 break; 04381 04382 case IDSYS_MNAUTODISMISS: 04383 /* 04384 * This is a one shot timer, so kill it. 04385 * Dismiss the popup if the flag hasn't been reset. 04386 */ 04387 _KillTimer(pwnd, IDSYS_MNAUTODISMISS); 04388 if (pMenuState->fAboutToAutoDismiss) { 04389 goto EndMenu; 04390 } 04391 } 04392 break; 04393 04394 /* 04395 * Menu messages. 04396 */ 04397 case MN_SETHMENU: 04398 04399 /* 04400 * wParam - new hmenu to associate with this menu window 04401 * Don't let them set the spmenu to NULL of we have to deal with 04402 * that all over. Use RIP_ERROR for a while to make sure this is OK 04403 */ 04404 if (wParam != 0) { 04405 if ((wParam = (WPARAM)ValidateHmenu((HMENU)wParam)) == 0) { 04406 break; 04407 } 04408 LockPopupMenu(ppopupmenu, &(ppopupmenu->spmenu), (PMENU)wParam); 04409 } else { 04410 RIPMSG1(RIP_ERROR, "xxxMenuWindowProc: MN_SETHMENU ignoring NULL wParam. pwnd:%p", pwnd); 04411 } 04412 break; 04413 04414 case MN_GETHMENU: 04415 04416 /* 04417 * returns the hmenu associated with this menu window 04418 */ 04419 return (LRESULT)PtoH(pmenu); 04420 04421 case MN_SIZEWINDOW: 04422 { 04423 04424 /* 04425 * Computes the size of the menu associated with this window and resizes 04426 * it if needed. Size is returned x in loword, y in highword. wParam 04427 * is 0 to just return new size. wParam is non zero if we should also resize 04428 * window. 04429 * When called by xxxMNUpdateShownMenu, we might need to redraw the 04430 * frame (i.e, the scrollbars). So we check for MNSW_DRAWFRAME in wParam. 04431 * If some app is sending this message and that bit is set, then we'll 04432 * do some extra work, but I think everything should be cool. 04433 */ 04434 int cx, cy; 04435 PMONITOR pMonitor; 04436 04437 /* 04438 * Call menucomputeHelper directly since this is the entry point for 04439 * non toplevel menu bars. 04440 */ 04441 if (pmenu == NULL) 04442 break; 04443 04444 ThreadLockAlways(pmenu, &tlpmenu); 04445 ThreadLock(ppopupmenu->spwndNotify, &tlpwndNotify); 04446 UserAssert(pmenu->spwndNotify == ppopupmenu->spwndNotify); 04447 xxxMNCompute(pmenu, ppopupmenu->spwndNotify, 0, 0, 0, 0); 04448 ThreadUnlock(&tlpwndNotify); 04449 ThreadUnlock(&tlpmenu); 04450 04451 pMonitor = _MonitorFromWindow(pwnd, MONITOR_DEFAULTTOPRIMARY); 04452 cx = pmenu->cxMenu; 04453 cy = MNCheckScroll(pmenu, pMonitor); 04454 04455 /* 04456 * Size the window? 04457 */ 04458 if (wParam != 0) { 04459 LONG lPos; 04460 int x, y; 04461 DWORD dwFlags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER; 04462 04463 /* 04464 * Need to redraw the frame? 04465 */ 04466 if (wParam & MNSW_DRAWFRAME) { 04467 dwFlags |= SWP_DRAWFRAME; 04468 } 04469 04470 /* 04471 * If the window is visible, it's being resized while 04472 * shown. So make sure that it still fits on the screen 04473 * (i.e, move it to the best pos). 04474 */ 04475 if (TestWF(pwnd, WFVISIBLE)) { 04476 lPos = FindBestPos( 04477 pwnd->rcWindow.left, 04478 pwnd->rcWindow.top, 04479 cx, 04480 cy, 04481 NULL, 04482 0, 04483 ppopupmenu, 04484 pMonitor); 04485 04486 x = GET_X_LPARAM(lPos); 04487 y = GET_Y_LPARAM(lPos); 04488 } else { 04489 dwFlags |= SWP_NOMOVE; 04490 } 04491 04492 xxxSetWindowPos( 04493 pwnd, 04494 PWND_TOP, 04495 x, 04496 y, 04497 cx + 2*SYSMET(CXFIXEDFRAME), /* For shadow */ 04498 cy + 2*SYSMET(CYFIXEDFRAME), /* For shadow */ 04499 dwFlags); 04500 04501 } 04502 04503 return MAKELONG(cx, cy); 04504 } 04505 04506 case MN_OPENHIERARCHY: 04507 { 04508 PWND pwndT; 04509 /* 04510 * Opens one level of the hierarchy at the selected item, if 04511 * present. Return 0 if error, else hwnd of opened hierarchy. 04512 */ 04513 pwndT = xxxMNOpenHierarchy(ppopupmenu, pMenuState); 04514 return (LRESULT)HW(pwndT); 04515 } 04516 04517 case MN_CLOSEHIERARCHY: 04518 xxxMNCloseHierarchy(ppopupmenu, pMenuState); 04519 break; 04520 04521 case MN_SELECTITEM: 04522 /* 04523 * wParam - the item to select. Must be a valid 04524 * Returns the item flags of the wParam (0 if failure) 04525 */ 04526 if ((wParam >= pmenu->cItems) && (wParam < MFMWFP_MINVALID)) { 04527 UserAssertMsg1(FALSE, "Bad wParam %x for MN_SELECTITEM", wParam); 04528 break; 04529 } 04530 04531 pItem = xxxMNSelectItem(ppopupmenu, pMenuState, (UINT)wParam); 04532 if (pItem != NULL) { 04533 return((LONG)(DWORD)(WORD)(pItem->fState | 04534 ((pItem->spSubMenu != NULL) ? MF_POPUP : 0))); 04535 } 04536 04537 break; 04538 04539 case MN_SELECTFIRSTVALIDITEM: { 04540 UINT item; 04541 04542 item = MNFindNextValidItem(pmenu, -1, 1, TRUE); 04543 xxxSendMessage(pwnd, MN_SELECTITEM, item, 0L); 04544 return (LONG)item; 04545 } 04546 04547 case MN_CANCELMENUS: 04548 04549 /* 04550 * Cancels all menus, unselects everything, destroys windows, and cleans 04551 * everything up for this hierarchy. wParam is the command to send and 04552 * lParam says if it is valid or not. 04553 */ 04554 xxxMNCancel(pMenuState, (UINT)wParam, (BOOL)LOWORD(lParam), 0); 04555 break; 04556 04557 case MN_FINDMENUWINDOWFROMPOINT: 04558 /* 04559 * lParam is point to search for from this hierarchy down. 04560 * returns MFMWFP_* value or a pwnd. 04561 */ 04562 lRet = xxxMNFindWindowFromPoint(ppopupmenu, (PUINT)wParam, MAKEPOINTS(lParam)); 04563 04564 /* 04565 * Convert return value to a handle. 04566 */ 04567 if (IsMFMWFPWindow(lRet)) { 04568 return (LRESULT)HW((PWND)lRet); 04569 } else { 04570 return lRet; 04571 } 04572 04573 04574 case MN_SHOWPOPUPWINDOW: 04575 /* 04576 * Forces the dropped down popup to be visible and if modeless, also active. 04577 */ 04578 PlayEventSound(USER_SOUND_MENUPOPUP); 04579 xxxShowWindow(pwnd, (pMenuState->fModelessMenu ? SW_SHOW : SW_SHOWNOACTIVATE)); 04580 break; 04581 04582 case MN_ACTIVATEPOPUP: 04583 /* 04584 * Activates a popup. This messages is posted in response to WM_ACTIVATEAPP 04585 * or WM_ACTIVATE 04586 */ 04587 UserAssert(pMenuState->fModelessMenu); 04588 xxxActivateThisWindow(pwnd, 0, 0); 04589 break; 04590 04591 case MN_ENDMENU: 04592 /* 04593 * End the menu. This message is posted to avoid ending the menu 04594 * at randmom moments. By posting the message, the request is 04595 * queued after any pending/current processing. 04596 */ 04597 EndMenu: 04598 xxxEndMenuLoop(pMenuState, pMenuState->pGlobalPopupMenu); 04599 if (pMenuState->fModelessMenu) { 04600 UserAssert(!pMenuState->fInCallHandleMenuMessages); 04601 xxxMNEndMenuState(TRUE); 04602 } 04603 return 0; 04604 04605 case MN_DODRAGDROP: 04606 /* 04607 * Let the app know that the user is dragging. 04608 */ 04609 if (pMenuState->fDragging 04610 && (ppopupmenu->spwndNotify != NULL) 04611 && IsMFMWFPWindow(pMenuState->uButtonDownHitArea)) { 04612 /* 04613 * Get the pmenu that contains the item being dragged 04614 */ 04615 pmenu = (((PMENUWND)pMenuState->uButtonDownHitArea)->ppopupmenu)->spmenu; 04616 /* 04617 * If this is a modal menu, release the capture lock so 04618 * DoDragDrop (if called) can get it. 04619 */ 04620 if (!pMenuState->fModelessMenu) { 04621 UserAssert(PtiCurrent()->pq->QF_flags & QF_CAPTURELOCKED); 04622 PtiCurrent()->pq->QF_flags &= ~QF_CAPTURELOCKED; 04623 } 04624 04625 LockMenuState(pMenuState); 04626 ThreadLockAlways(ppopupmenu->spwndNotify, &tlpwndNotify); 04627 04628 /* 04629 * Give them a chance to call DoDragDrop 04630 */ 04631 pMenuState->fInDoDragDrop = TRUE; 04632 lRet = xxxSendMessage(ppopupmenu->spwndNotify, WM_MENUDRAG, 04633 pMenuState->uButtonDownIndex, (LPARAM)PtoH(pmenu)); 04634 pMenuState->fInDoDragDrop = FALSE; 04635 04636 if (lRet == MND_ENDMENU) { 04637 /* 04638 * Go away. 04639 */ 04640 ThreadUnlock(&tlpwndNotify); 04641 if (!xxxUnlockMenuState(pMenuState)) { 04642 goto EndMenu; 04643 } else { 04644 return 0; 04645 } 04646 break; 04647 } else { 04648 /* 04649 * If the user starts dragging, we always 04650 * ignore the following button up 04651 */ 04652 pMenuState->fIgnoreButtonUp = TRUE; 04653 } 04654 04655 /* 04656 * Check the button state since we might have not seen the button up 04657 * If so, this will cancel the dragging state 04658 */ 04659 MNCheckButtonDownState(pMenuState); 04660 04661 /* 04662 * If this is a modal menu, make sure we recover capture 04663 */ 04664 if (!pMenuState->fModelessMenu) { 04665 xxxMNSetCapture(ppopupmenu); 04666 } 04667 04668 ThreadUnlock(&tlpwndNotify); 04669 xxxUnlockMenuState(pMenuState); 04670 } 04671 return 0; 04672 04673 case MN_BUTTONDOWN: 04674 04675 /* 04676 * wParam is position (index) of item the button was clicked on. 04677 * Must be a valid 04678 */ 04679 if ((wParam >= pmenu->cItems) && (wParam < MFMWFP_MINVALID)) { 04680 UserAssertMsg1(FALSE, "Bad wParam %x for MN_BUTTONDOWN", wParam); 04681 break; 04682 } 04683 xxxMNButtonDown(ppopupmenu, pMenuState, (UINT)wParam, TRUE); 04684 break; 04685 04686 case MN_MOUSEMOVE: 04687 04688 /* 04689 * lParam is mouse move coordinate wrt screen. 04690 */ 04691 xxxMNMouseMove(ppopupmenu, pMenuState, MAKEPOINTS(lParam)); 04692 break; 04693 04694 case MN_BUTTONUP: 04695 04696 /* 04697 * wParam is position (index) of item the button was up clicked on. 04698 */ 04699 if ((wParam >= pmenu->cItems) && (wParam < MFMWFP_MINVALID)) { 04700 UserAssertMsg1(FALSE, "Bad wParam %x for MN_BUTTONUP", wParam); 04701 break; 04702 } 04703 xxxMNButtonUp(ppopupmenu, pMenuState, (UINT)wParam, lParam); 04704 break; 04705 04706 case MN_SETTIMERTOOPENHIERARCHY: 04707 04708 /* 04709 * Given the current selection, set a timer to show this hierarchy if 04710 * valid else return 0. 04711 */ 04712 return (LONG)(WORD)MNSetTimerToOpenHierarchy(ppopupmenu); 04713 04714 case MN_DBLCLK: 04715 // 04716 // User double-clicked on item. wParamLo is the item. 04717 // 04718 xxxMNDoubleClick(pMenuState, ppopupmenu, (int)wParam); 04719 break; 04720 04721 case WM_MOUSELEAVE: 04722 UserAssert(pMenuState->fModelessMenu); 04723 /* 04724 * If we're in DoDragDrop loop, we don't track the mouse 04725 * when it goes off the menu window 04726 */ 04727 pMenuState->fMouseOffMenu = !pMenuState->fInDoDragDrop; 04728 ppopupmenu->fTrackMouseEvent = FALSE; 04729 /* 04730 * See if we need to set the timer to autodismiss 04731 */ 04732 MNSetTimerToAutoDismiss(pMenuState, pwnd); 04733 /* 04734 * If we left the active popup, remove the selection 04735 */ 04736 if (ppopupmenu->spwndPopupMenu == pMenuState->pGlobalPopupMenu->spwndActivePopup) { 04737 xxxMNSelectItem(ppopupmenu, pMenuState, MFMWFP_NOITEM); 04738 } 04739 break; 04740 04741 case WM_ACTIVATEAPP: 04742 if (pMenuState->fModelessMenu 04743 && (pwnd == pMenuState->pGlobalPopupMenu->spwndActivePopup)) { 04744 /* 04745 * If the application is getting activated, we post a message 04746 * to let the dust settle and then re-activate spwndPopupActive 04747 */ 04748 if (wParam) { 04749 _PostMessage(pwnd, MN_ACTIVATEPOPUP, 0, 0); 04750 /* 04751 * If we're not in the foregruond queue, we want to keep 04752 * the frame off. 04753 * This flag will also tell us that if we lose activation 04754 * while coming to the foregrund (later), we don't want 04755 * to dismiss the menu. 04756 */ 04757 pMenuState->fActiveNoForeground = (gpqForeground != PtiCurrent()->pq); 04758 } 04759 04760 /* 04761 * Make the notification window frame show that we're active/inactive. 04762 * If the application is inactive but the user moves the mouse 04763 * over the menu, then we can get this message when the first 04764 * window in the app gets activated (i.e., the move causes a popup to 04765 * be closed/opened). So turn on the frame only if we're in 04766 * the foreground. 04767 */ 04768 if (ppopupmenu->spwndNotify != NULL) { 04769 ThreadLockAlways(ppopupmenu->spwndNotify, &tlpwndNotify); 04770 xxxDWP_DoNCActivate(ppopupmenu->spwndNotify, 04771 ((wParam && !pMenuState->fActiveNoForeground) ? NCA_ACTIVE : NCA_FORCEFRAMEOFF), 04772 HRGN_FULL); 04773 ThreadUnlock(&tlpwndNotify); 04774 } 04775 } 04776 break; 04777 04778 case WM_ACTIVATE: 04779 if (pMenuState->fModelessMenu) { 04780 /* 04781 * If activation is NOT going to a menu window or 04782 * it's going to a recursed menu, bail 04783 */ 04784 if ((LOWORD(wParam) == WA_INACTIVE) 04785 && !pMenuState->fInCallHandleMenuMessages 04786 && !pMenuState->pGlobalPopupMenu->fInCancel) { 04787 04788 lParam = (LPARAM)RevalidateHwnd((HWND)lParam); 04789 if ((lParam != 0) 04790 && ((GETFNID((PWND)lParam) != FNID_MENU) 04791 || IsRecursedMenuState(pMenuState, ((PMENUWND)lParam)->ppopupmenu))) { 04792 /* 04793 * If we're just coming to the foreground, then 04794 * activate the popup later and stay up. 04795 */ 04796 if (pMenuState->fActiveNoForeground 04797 && (gpqForeground == PtiCurrent()->pq)) { 04798 04799 pMenuState->fActiveNoForeground = FALSE; 04800 _PostMessage(pwnd, MN_ACTIVATEPOPUP, 0, 0); 04801 } else { 04802 /* 04803 * Since the menu window is active, ending the menu 04804 * now would set a new active window, messing the 04805 * current activation that sent us this message. 04806 * so end the menu later. 04807 */ 04808 _PostMessage(pwnd, MN_ENDMENU, 0, 0); 04809 break; 04810 } 04811 } 04812 } 04813 goto CallDWP; 04814 } /* if (pMenuState->fModelessMenu) */ 04815 04816 /* 04817 * We must make sure that the menu window does not get activated. 04818 * Powerpoint 2.00e activates it deliberately and this causes problems. 04819 * We try to activate the previously active window in such a case. 04820 * Fix for Bug #13961 --SANKAR-- 09/26/91-- 04821 */ 04822 /* 04823 * In Win32, wParam has other information in the hi 16bits, so to 04824 * prevent infinite recursion, we need to mask off those bits 04825 * Fix for NT bug #13086 -- 23-Jun-1992 JonPa 04826 * 04827 */ 04828 04829 if (LOWORD(wParam)) { 04830 TL tlpwnd; 04831 /* 04832 * This is a super bogus hack. Let's start failing this for 5.0 apps. 04833 */ 04834 if (Is500Compat(PtiCurrent()->dwExpWinVer)) { 04835 RIPMSG1(RIP_ERROR, "xxxMenuWindowProc: Menu window activated:%#p", pwnd); 04836 _PostMessage(pwnd, MN_ENDMENU, 0, 0); 04837 break; 04838 } 04839 04840 #if 0 04841 /* 04842 * Activate the previously active wnd 04843 */ 04844 xxxActivateWindow(pwnd, AW_SKIP2); 04845 #else 04846 /* 04847 * Try the previously active window. 04848 */ 04849 if ((gpqForegroundPrev != NULL) && 04850 !FBadWindow(gpqForegroundPrev->spwndActivePrev) && 04851 !ISAMENU(gpqForegroundPrev->spwndActivePrev)) { 04852 pwnd = gpqForegroundPrev->spwndActivePrev; 04853 } else { 04854 04855 /* 04856 * Find a new active window from the top-level window list. 04857 * Bug 78131: Make sure we don't loop for ever. This is a pretty 04858 * unusual scenario (in addtion, normally we should not hit this code path) 04859 * So let's use a counter to rule out the possibility that another 04860 * weird window configuration is going to make us loop for ever 04861 */ 04862 PWND pwndMenu = pwnd; 04863 UINT uCounter = 0; 04864 do { 04865 pwnd = NextTopWindow(PtiCurrent(), pwnd, NULL, 0); 04866 if (pwnd && !FBadWindow(pwnd->spwndLastActive) && 04867 !ISAMENU(pwnd->spwndLastActive)) { 04868 pwnd = pwnd->spwndLastActive; 04869 uCounter = 0; 04870 break; 04871 } 04872 } while ((pwnd != NULL) && (uCounter++ < 255)); 04873 /* 04874 * If we couldn't find a window, just bail. 04875 */ 04876 if (uCounter != 0) { 04877 RIPMSG0(RIP_ERROR, "xxxMenuWindowProc: couldn't fix active window"); 04878 _PostMessage(pwndMenu, MN_ENDMENU, 0, 0); 04879 break; 04880 } 04881 } 04882 04883 if (pwnd != NULL) { 04884 PTHREADINFO pti = PtiCurrent(); 04885 ThreadLockAlwaysWithPti(pti, pwnd, &tlpwnd); 04886 04887 /* 04888 * If GETPTI(pwnd) isn't pqCurrent this is a AW_SKIP* activation 04889 * we'll want to a do a xxxSetForegroundWindow(). 04890 */ 04891 if (GETPTI(pwnd)->pq != pti->pq) { 04892 04893 /* 04894 * Only allow this if we're on the current foreground queue. 04895 */ 04896 if (gpqForeground == pti->pq) { 04897 xxxSetForegroundWindow(pwnd, FALSE); 04898 } 04899 } else { 04900 xxxActivateThisWindow(pwnd, 0, ATW_SETFOCUS); 04901 } 04902 04903 ThreadUnlock(&tlpwnd); 04904 } 04905 #endif 04906 } 04907 break; 04908 04909 case WM_SIZE: 04910 case WM_MOVE: 04911 /* 04912 * When a popup has been sized/moved, we need to make 04913 * sure any dropped hierarchy is moved accordingly. 04914 */ 04915 if (ppopupmenu->spwndNextPopup != NULL) { 04916 pItem = MNGetpItem(ppopupmenu, ppopupmenu->posDropped); 04917 if (pItem != NULL) { 04918 int x, y; 04919 PMONITOR pMonitorDummy; 04920 04921 /* 04922 * If the dropped hierarchy needs to be recomputed, do it 04923 */ 04924 #define pmenuNext (((PMENUWND)ppopupmenu->spwndNextPopup)->ppopupmenu->spmenu) 04925 if (pmenuNext->cxMenu == 0) { 04926 xxxSendMessage(ppopupmenu->spwndNextPopup, MN_SIZEWINDOW, MNSW_RETURNSIZE, 0L); 04927 } 04928 04929 /* 04930 * Find out the new position 04931 */ 04932 xxxMNPositionHierarchy(ppopupmenu, pItem, 04933 pmenuNext->cxMenu + (2 * SYSMET(CXFIXEDFRAME)), 04934 pmenuNext->cyMenu + (2 * SYSMET(CXFIXEDFRAME)), 04935 &x, &y, &pMonitorDummy); 04936 04937 /* 04938 * Move it 04939 */ 04940 ThreadLockAlways(ppopupmenu->spwndNextPopup, &tlpwndNotify); 04941 xxxSetWindowPos(ppopupmenu->spwndNextPopup, NULL, 04942 x, y, 0, 0, 04943 SWP_NOSIZE | SWP_NOZORDER | SWP_NOSENDCHANGING); 04944 ThreadUnlock(&tlpwndNotify); 04945 #undef pmenuNext 04946 } /* if (pItem != NULL) */ 04947 } /* if (ppopupmenu->spwndNextPopup != NULL) */ 04948 break; 04949 04950 case WM_NCHITTEST: 04951 /* 04952 * Since modeless menus don't capture the mouse, we 04953 * process this message to make sure that we always receive 04954 * a mouse move when the mouse in our window. 04955 * This also causes us to receive the WM_MOUSELEAVE only when 04956 * the mouse leaves the window and not just the client area. 04957 */ 04958 if (pMenuState->fModelessMenu) { 04959 ptOrg.x = GET_X_LPARAM(lParam); 04960 ptOrg.y = GET_Y_LPARAM(lParam); 04961 if (PtInRect(&pwnd->rcWindow, ptOrg)) { 04962 return HTCLIENT; 04963 } else { 04964 return HTNOWHERE; 04965 } 04966 } else { 04967 goto CallDWP; 04968 } 04969 04970 04971 default: 04972 CallDWP: 04973 return xxxDefWindowProc(pwnd, message, wParam, lParam); 04974 } 04975 04976 return 0; 04977 }

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