test: menu.c Source File

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 }