Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

405 lines
11 KiB

/**************************** Module Header ********************************\
* Module Name: mnkey.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* Menu Keyboard Handling Routines
*
* History:
* 10-10-90 JimA Cleanup.
* 03-18-91 IanJa Window revalidation added
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
int xxxClientFindMnemChar(
PUNICODE_STRING pstrSrc,
WCHAR ch,
BOOL fFirst,
BOOL fPrefix);
/* MenuSwitch commands */
#define CMDSWITCH 1
#define CMDQUERY 2
/***************************************************************************\
* FindNextValidMenuItem
*
* !
*
* History:
\***************************************************************************/
UINT MNFindNextValidItem(
PMENU pMenu,
int i,
int dir,
UINT flags)
{
int iStart;
BOOL cont = TRUE;
int cItems = pMenu->cItems;
PITEM pItem;
if ((i < 0) && (dir > 0))
// going forward from beginning -- stop after last menu item
i = iStart = cItems;
else if ((i >= cItems) && (dir < 0))
// going backward from end -- stop after first menu item
i = iStart = -1;
else
iStart = i;
if (!cItems)
return(MFMWFP_NOITEM);
// b#8997 - if we have these conditions and enter
// loop will go blistic ( infin )
// fix: jump over code and come loop to i == iStart will now stop us
if ( ( i == 0 ) && ( cItems == 1 ) && ( dir > 0 ) )
{
dir = 0;
goto artsquickndirtybugfix;
}
//
// Loop thru menu items til (1) we find a valid item
// or (2) we make it back to the start item (iStart)
while (TRUE) {
i += dir;
if ((i == iStart) || (dir == 0))
// we made it back to start item -- return NOT FOUND
return MFMWFP_NOITEM;
// keep 'i' in the range: 0 <= i < cItems
if (i >= cItems) {
i = -1;
continue;
}
else if (i < 0) {
i = cItems;
continue;
}
artsquickndirtybugfix:
pItem = pMenu->rgItems + i;
// skip ownerdraw - seperators even though there not NULL
if (TestMFT(pItem, MFT_SEPARATOR)) {
//
// Skip non-separator (if asked) empty items. With hot-tracking,
// it is acceptable for them to be selected. In truth, it was possible
// in Win3.1 too, but less likely.
//
if (!(flags & MNF_DONTSKIPSEPARATORS)) {
continue;
}
} else if ((pItem->hbmp >= HBMMENU_MBARFIRST) && (pItem->hbmp <= HBMMENU_MBARLAST)) {
/*
* Skip close & minimize & restore buttons
*/
continue;
}
// return index of found item
return(i);
}
//
// We should never get here!
//
UserAssert(FALSE);
}
/***************************************************************************\
* MKF_FindMenuItemInColumn
*
* Finds closest item in the pull-down menu's next "column".
*
* History:
\***************************************************************************/
UINT MNFindItemInColumn(
PMENU pMenu,
UINT idxB,
int dir,
BOOL fRoot)
{
int dxMin;
int dyMin;
int dxMax;
int dyMax;
int xB;
int yB;
UINT idxE;
UINT idxR;
UINT cItems;
PITEM pItem;
cItems = pMenu->cItems;
idxR = MFMWFP_NOITEM;
idxE = MNFindNextValidItem(pMenu, MFMWFP_NOITEM, dir, 0);
if (idxE == -1)
goto End;
dxMin = dyMin = 20000;
if (idxB >= pMenu->cItems)
return idxR;
pItem = &pMenu->rgItems[idxB];
xB = pItem->xItem;
yB = pItem->yItem;
while (cItems-- > 0 &&
(idxB = MNFindNextValidItem(pMenu, idxB, dir, 0)) != idxE &&
(idxB != MFMWFP_NOITEM)) {
pItem = &pMenu->rgItems[idxB];
dxMax = xB - pItem->xItem;
dyMax = yB - pItem->yItem;
if (dxMax < 0)
dxMax = (-dxMax);
if (dyMax < 0)
dyMax = (-dyMax);
// See if this item is nearer than the last item found
// --------------------------------------------------------
// (fRoot || dxMax) -- this condition means that if it's
// not the actual menu bar menu that we're dealing with,
// then the item below/above (same X value as) the selected
// item is not a valid one to move to
if ((dyMax < dyMin) && (fRoot || dxMax) && dxMax <= dxMin) {
dxMin = dxMax;
dyMin = dyMax;
idxR = idxB;
}
}
End:
return idxR;
}
/***************************************************************************\
* MKF_FindMenuChar
*
* Translates Virtual cursor key movements into pseudo-ascii values. Maps a
* character to an item number.
*
* History:
\***************************************************************************/
UINT xxxMNFindChar(
PMENU pMenu,
UINT ch,
int idxC,
LPINT lpr) /* Put match type here */
{
int idxFirst = MFMWFP_NOITEM;
int idxB;
int idxF;
int rT;
LPWSTR lpstr;
PITEM pItem;
if (ch == 0)
return 0;
/*
* First time thru go for the very first menu.
*/
idxF = MFMWFP_NOITEM;
rT = 0;
idxB = idxC;
if (idxB < 0)
// if (idxB & 0x8000)
idxB = MNFindNextValidItem(pMenu, pMenu->cItems, MFMWFP_NOITEM, MNF_DONTSKIPSEPARATORS);
do {
INT idxPrev;
idxPrev = idxC;
idxC = MNFindNextValidItem(pMenu, idxC, 1, MNF_DONTSKIPSEPARATORS);
if (idxC == MFMWFP_NOITEM || idxC == idxFirst)
break;
if (idxFirst == MFMWFP_NOITEM)
idxFirst = idxC;
pItem = &pMenu->rgItems[idxC];
if (pItem->lpstr != NULL) {
if (pItem->cch != 0) {
UNICODE_STRING strMnem;
lpstr = TextPointer(pItem->lpstr);
if (*lpstr == CH_HELPPREFIX) {
/*
* Skip help prefix if it is there so that we can mnemonic
* to the first character of a right justified string.
*/
lpstr++;
}
RtlInitUnicodeString(&strMnem, lpstr);
if (((rT = (UINT)xxxClientFindMnemChar(&strMnem,
(WCHAR)ch, TRUE, TRUE)) == 0x0080) &&
(idxF == MFMWFP_NOITEM))
idxF = idxC;
}
}
if (idxC == idxPrev) {
break; // no progress - break inf. loop
}
} while (rT != 1 && idxB != idxC);
*lpr = rT;
if (rT == 1)
return idxC;
return idxF;
}
/***************************************************************************\
* xxxMenuKeyFilter
*
* !
*
* Revalidation notes:
* o Routine assumes it is called with pMenuState->hwndMenu non-NULL and valid.
* o If one or more of the popup menu windows is unexpectedly destroyed, this is
* detected in xxxMenuWndProc(), which sets pMenuState->fSabotaged and calls
* xxxKillMenuState(). Therefore, if we return from an xxxRoutine with
* pMenuState->fSabotaged set, we must abort immediately.
* o If pMenuState->hwndMenu is unexpectedly destroyed, we abort only if we
* need to use the corresponding pwndMenu.
* o pMenuState->hwndMenu may be supplied as a parameter to various routines
* (eg: xxxNextItem), whether valid or not.
* o Any label preceded with xxx (eg: xxxMKF_UnlockAndExit) may be reached with
* pMenuState->hwndMenu invalid.
* o If this routine is not called while in xxxMenuLoop(), then it must
* clear pMenuState->fSabotaged before returning.
*
* History:
\***************************************************************************/
void xxxMNKeyFilter(
PPOPUPMENU ppopupMenu,
PMENUSTATE pMenuState,
UINT ch)
{
BOOL fLocalInsideMenuLoop = pMenuState->fInsideMenuLoop;
if (pMenuState->fButtonDown) {
/*
* Ignore keystrokes while the mouse is pressed (except ESC).
*/
return;
}
if (!pMenuState->fInsideMenuLoop) {
/*
* Need to send the WM_INITMENU message before we pull down the menu.
*/
if (!xxxMNStartMenu(ppopupMenu, KEYBDHOLD)) {
return;
}
pMenuState->fInsideMenuLoop = TRUE;
}
switch (ch) {
case 0:
/*
* If we get a WM_KEYDOWN alt key and then a KEYUP alt key, we need to
* activate the first item on the menu. ie. user hits and releases alt
* key so just select first item. USER sends us a SC_KEYMENU with
* lParam 0 when the user does this.
*/
xxxMNSelectItem(ppopupMenu, pMenuState, 0);
break;
case MENUCHILDSYSMENU:
if (!TestwndChild(ppopupMenu->spwndNotify)) {
/*
* Change made to fix MDI problem: child window gets a keymenu,
* and pops up sysmenu of frame when maximized. Need to act like
* MENUCHAR if hwndMenu is a top-level.
*/
goto MenuCharHandler;
}
/*
* else fall through.
*/
case MENUSYSMENU:
if (!TestWF(ppopupMenu->spwndNotify, WFSYSMENU)) {
xxxMessageBeep(0);
goto MenuCancel;
}
/*
* Popup any hierarchies we have.
*/
xxxMNCloseHierarchy(ppopupMenu, pMenuState);
if (!ppopupMenu->fIsSysMenu && ppopupMenu->spmenuAlternate)
xxxMNSwitchToAlternateMenu(ppopupMenu);
if (!ppopupMenu->fIsSysMenu) {
/*
* If no system menu, get out.
*/
goto MenuCancel;
}
MNPositionSysMenu(ppopupMenu->spwndPopupMenu, ppopupMenu->spmenu);
xxxMNSelectItem(ppopupMenu, pMenuState, 0);
xxxMNOpenHierarchy(ppopupMenu, pMenuState);
ppopupMenu->fToggle = FALSE;
break;
default:
/*
* Handle ALT-Character sequences for items on top level menu bar.
* Note that fInsideMenuLoop may be set to false on return from this
* function if the app decides to return 1 to the WM_MENUCHAR message.
* We detect this and not enter MenuLoop if fInsideMenuLoop is reset
* to false.
*/
MenuCharHandler:
xxxMNChar(ppopupMenu, pMenuState, ch);
if (ppopupMenu->posSelectedItem == MFMWFP_NOITEM) {
/*
* No selection found.
*/
goto MenuCancel;
}
break;
}
if (!fLocalInsideMenuLoop && pMenuState->fInsideMenuLoop) {
xxxMNLoop(ppopupMenu, pMenuState, 0, FALSE);
}
return;
MenuCancel:
pMenuState->fModelessMenu = FALSE;
if (!ppopupMenu->fInCancel) {
xxxMNDismiss(pMenuState);
}
UserAssert(!pMenuState->fInsideMenuLoop && !pMenuState->fMenuStarted);
return;
}