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.
 
 
 
 
 
 

773 lines
23 KiB

/**************************** Module Header ********************************\
* Module Name: mnsys.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* System Menu Routines
*
* History:
* 10-10-90 JimA Cleanup.
* 03-18-91 IanJa Window revalidation added (none required)
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
void _SetCloseDefault(PMENU pSubMenu);
PWND FindFakeMDIChild(PWND pwndParent);
/***************************************************************************\
* LoadSysDesktopMenu
*
* Loads and locks a desktop system menu. Since we have to call the client
* to load the menu, while thread 1 is loading the menu, thread 2
* might grab the critical section, check pdesk->spmenu* and decide that
* the menu needs to be loaded. Hence we could load the menu more than once.
* this function handles that case to avoid leaking menus.
*
* 10/24/97 Gerardob Created
\***************************************************************************/
#ifdef LAME_BUTTON
PMENU xxxLoadSysDesktopMenu (PMENU * ppmenu, UINT uMenuId, PWND pwnd)
#else
PMENU xxxLoadSysDesktopMenu (PMENU * ppmenu, UINT uMenuId)
#endif // LAME_BUTTON
{
PMENU pmenu;
/*
* This should only be called when the menu hasn't been loaded
*/
UserAssert(*ppmenu == NULL);
#ifdef LAME_BUTTON
pmenu = xxxLoadSysMenu(uMenuId, pwnd);
#else
pmenu = xxxLoadSysMenu(uMenuId);
#endif // LAME_BUTTON
if (pmenu == NULL) {
return NULL;
}
/*
* If someone beat us loading the menu, destroy this one
* and return the one already loaded
*/
if (*ppmenu != NULL) {
UserAssert(TestMF(*ppmenu, MFSYSMENU));
RIPMSG1(RIP_WARNING,
"LoadSysDesktopMenu: Menu loaded during callback. ppmenu:%#p",
ppmenu);
_DestroyMenu(pmenu);
return *ppmenu;
}
/*
* Mark it, lock it and done
*/
SetMF(pmenu, MFSYSMENU);
LockDesktopMenu(ppmenu, pmenu);
return pmenu;
}
/***************************************************************************\
* Lock/UnlockDesktopMenu
*
* These functions lock/unlock a pmenu into a desktop structure (spmenuSys or
* spmenuDialogSys) and mark/clear it as such.
* We mark these menus so we can identify them quickly on single bit test.
* We also don't want any one to modify these menus or any submenu.
*
* Note that this assumes that there is only one submenu. If more are added,
* these functions have to be fixed accordingly.
*
* 08/18/97 Gerardob Created
\***************************************************************************/
PVOID LockDesktopMenu(PMENU * ppmenu, PMENU pmenu)
{
PMENU pSubMenu;
PTHREADINFO ptiDesktop;
/*
* We only load desktop sys menus once.
*/
UserAssert(*ppmenu == NULL);
if (pmenu == NULL) {
return NULL;
}
SetMF(pmenu, MFDESKTOP);
/*
* This is awful but this is the real owner of this object. We used to set it
* to NULL but that was forcing us to handle the NULL owner all over the place
*/
ptiDesktop = PtiCurrent()->rpdesk->rpwinstaParent->pTerm->ptiDesktop;
HMChangeOwnerProcess(pmenu, ptiDesktop);
pSubMenu = pmenu->rgItems->spSubMenu;
UserAssert(pSubMenu != NULL);
SetMF(pSubMenu, MFDESKTOP);
HMChangeOwnerProcess(pSubMenu, ptiDesktop);
#if DBG
{
/*
* Assert that there are no other submenus that would need to be
* marked as MFDESKTOP.
*/
PITEM pitem;
UINT uItems;
UserAssert(pmenu->cItems == 1);
pitem = pSubMenu->rgItems;
uItems = pSubMenu->cItems;
while (uItems--) {
UserAssert(pitem->spSubMenu == NULL);
pitem++;
}
}
#endif
return Lock(ppmenu, pmenu);
}
PVOID UnlockDesktopMenu(PMENU * ppmenu)
{
UserAssert(*ppmenu != NULL);
UserAssert(TestMF(*ppmenu, MFDESKTOP));
ClearMF(*ppmenu, MFDESKTOP);
UserAssert(TestMF((*ppmenu)->rgItems->spSubMenu, MFDESKTOP));
ClearMF((*ppmenu)->rgItems->spSubMenu, MFDESKTOP);
return Unlock(ppmenu);
}
/***************************************************************************\
* GetSysMenuHandle
*
* Returns a handle to the system menu of the given window. NULL if
* the window doesn't have a system menu.
*
* History:
\***************************************************************************/
PMENU xxxGetSysMenuHandle(
PWND pwnd)
{
PMENU pMenu;
CheckLock(pwnd);
if (TestWF(pwnd, WFSYSMENU)) {
pMenu = pwnd->spmenuSys;
/*
* If the window doesn't have a System Menu, use the default one.
*/
if (pMenu == NULL) {
/*
* Grab the menu from the desktop. If the desktop menu
* has not been loaded and this is not a system thread,
* load it now. Callbacks cannot be made from a system
* thread or when a thread is in cleanup.
*/
pMenu = pwnd->head.rpdesk->spmenuSys;
/*
* Do not do callbacks if the thread is exiting. We ran into this when
* destroying a thread's window and the window it was promoting to
* foreground was a hard error popup.
*/
if (pMenu == NULL && !(PtiCurrent()->TIF_flags & (TIF_SYSTEMTHREAD | TIF_INCLEANUP))) {
#ifdef LAME_BUTTON
pMenu = xxxLoadSysDesktopMenu (&pwnd->head.rpdesk->spmenuSys, ID_SYSMENU, pwnd);
#else
pMenu = xxxLoadSysDesktopMenu (&pwnd->head.rpdesk->spmenuSys, ID_SYSMENU);
#endif // LAME_BUTTON
}
}
} else {
pMenu = NULL;
}
return pMenu;
}
/***************************************************************************\
*
* GetSysMenu()
*
* Sets up the system menu first, then returns it.
*
\***************************************************************************/
PMENU xxxGetSysMenu(PWND pwnd, BOOL fSubMenu)
{
PMENU pMenu;
CheckLock(pwnd);
xxxSetSysMenu(pwnd);
if ((pMenu = xxxGetSysMenuHandle(pwnd)) != NULL) {
if (fSubMenu)
pMenu = _GetSubMenu(pMenu, 0);
}
return(pMenu);
}
/***************************************************************************\
* IsSmallerThanScreen
*
\***************************************************************************/
BOOL IsSmallerThanScreen(PWND pwnd)
{
int dxMax, dyMax;
PMONITOR pMonitor;
pMonitor = _MonitorFromWindow(pwnd, MONITOR_DEFAULTTOPRIMARY);
dxMax = pMonitor->rcWork.right - pMonitor->rcWork.left;
dyMax = pMonitor->rcWork.bottom - pMonitor->rcWork.top;
if ((pwnd->rcWindow.right - pwnd->rcWindow.left < dxMax) ||
(pwnd->rcWindow.bottom - pwnd->rcWindow.top < dyMax)) {
return TRUE;
}
return FALSE;
}
/***************************************************************************\
* SetSysMenu
*
* !
*
* History:
\***************************************************************************/
void xxxSetSysMenu(
PWND pwnd)
{
PMENU pMenu;
UINT wSize;
UINT wMinimize;
UINT wMaximize;
UINT wMove;
UINT wRestore;
UINT wDefault;
BOOL fFramedDialogBox;
TL tlmenu;
CheckLock(pwnd);
/*
* Get the handle of the current system menu.
*/
if ((pMenu = xxxGetSysMenuHandle(pwnd)) != NULL) {
pMenu = _GetSubMenu(pMenu, 0);
if (!pMenu)
return;
ThreadLockAlways(pMenu, &tlmenu);
/*
* System modal window: no size, icon, zoom, or move.
*/
// No system modal windows on NT.
// wSize = wMaximize = wMinimize = wMove =
// (UINT)((_GetSysModalWindow() == NULL) || hTaskLockInput ? 0: MFS_GRAYED);
wSize = wMaximize = wMinimize = wMove = 0;
wRestore = MFS_GRAYED;
//
// Default menu command is close.
//
wDefault = SC_CLOSE;
/*
* Minimized exceptions: no minimize, restore.
*/
// we need to reverse these because VB has a "special" window
// that is both minimized but without a minbox.
if (TestWF(pwnd, WFMINIMIZED))
{
wRestore = 0;
wMinimize = MFS_GRAYED;
wSize = MFS_GRAYED;
wDefault = SC_RESTORE;
if (IsTrayWindow(pwnd))
wMove = MFS_GRAYED;
}
else if (!TestWF(pwnd, WFMINBOX))
wMinimize = MFS_GRAYED;
/*
* Maximized exceptions: no maximize, restore.
*/
if (!TestWF(pwnd, WFMAXBOX))
wMaximize = MFS_GRAYED;
else if (TestWF(pwnd, WFMAXIMIZED)) {
wRestore = 0;
/*
* If the window is maximized but it isn't larger than the
* screen, we allow the user to move the window around the
* desktop (but we don't allow resizing).
*/
wMove = MFS_GRAYED;
if (!TestWF(pwnd, WFCHILD)) {
if (IsSmallerThanScreen(pwnd)) {
wMove = 0;
}
}
wSize = MFS_GRAYED;
wMaximize = MFS_GRAYED;
}
if (!TestWF(pwnd, WFSIZEBOX))
wSize = MFS_GRAYED;
/*
* Are we dealing with a framed dialog box with a sys menu?
* Dialogs with min/max/size boxes get a regular system menu
* (as opposed to the dialog menu)
*/
fFramedDialogBox =
(((TestWF(pwnd, WFBORDERMASK) == (BYTE)LOBYTE(WFDLGFRAME))
|| (TestWF(pwnd, WEFDLGMODALFRAME)))
&& !TestWF(pwnd, WFSIZEBOX | WFMINBOX | WFMAXBOX));
if (!fFramedDialogBox) {
xxxEnableMenuItem(pMenu, (UINT)SC_SIZE, wSize);
if (!TestWF(pwnd, WEFTOOLWINDOW))
{
xxxEnableMenuItem(pMenu, (UINT)SC_MINIMIZE, wMinimize);
xxxEnableMenuItem(pMenu, (UINT)SC_MAXIMIZE, wMaximize);
xxxEnableMenuItem(pMenu, (UINT)SC_RESTORE, wRestore);
}
}
xxxEnableMenuItem(pMenu, (UINT)SC_MOVE, wMove);
#if DBG
/*
* Assert that nobody managed to change the desktop menus.
*/
if (TestMF(pMenu, MFSYSMENU)) {
PITEM pItem = MNLookUpItem(pMenu, SC_CLOSE, FALSE, NULL);
UserAssert((pItem != NULL) && !TestMFS(pItem, MFS_GRAYED));
}
#endif
if (wDefault == SC_CLOSE)
_SetCloseDefault(pMenu);
else
_SetMenuDefaultItem(pMenu, wDefault, MF_BYCOMMAND);
ThreadUnlock(&tlmenu);
}
}
/***************************************************************************\
* GetSystemMenu
*
* !
*
* History:
\***************************************************************************/
PMENU xxxGetSystemMenu(
PWND pwnd,
BOOL fRevert)
{
PMENU pmenu;
CheckLock(pwnd);
/*
* Should we start with a fresh copy?
*/
pmenu = pwnd->spmenuSys;
if (fRevert) {
/*
* Destroy the old system menu.
*/
if ((pmenu != NULL) && !TestMF(pmenu, MFSYSMENU)) {
if (UnlockWndMenu(pwnd, &pwnd->spmenuSys)) {
_DestroyMenu(pmenu);
}
}
} else {
/*
* Do we need to load a new system menu?
*/
if (((pmenu == NULL) || TestMF(pmenu, MFSYSMENU))
&& TestWF(pwnd, WFSYSMENU)) {
PPOPUPMENU pGlobalPopupMenu;
UINT uMenuId = (pwnd->spmenuSys == NULL ? ID_SYSMENU : ID_DIALOGSYSMENU);
#ifdef LAME_BUTTON
pmenu = xxxLoadSysMenu(uMenuId, pwnd);
#else
pmenu = xxxLoadSysMenu(uMenuId);
#endif // LAME_BUTTON
if (pmenu == NULL) {
RIPMSG1(RIP_WARNING, "_GetSystemMenu: xxxLoadSysMenu Failed. pwnd:%#p", pwnd);
}
LockWndMenu(pwnd, &pwnd->spmenuSys, pmenu);
pmenu = pwnd->spmenuSys;
pGlobalPopupMenu = GetpGlobalPopupMenu(pwnd);
if ((pGlobalPopupMenu != NULL)
&& !pGlobalPopupMenu->fIsTrackPopup
&& (pGlobalPopupMenu->spwndPopupMenu == pwnd)) {
UserAssert(pGlobalPopupMenu->spwndNotify == pwnd);
if (pGlobalPopupMenu->fIsSysMenu) {
Lock(&pGlobalPopupMenu->spmenu, pmenu);
} else {
Lock(&pGlobalPopupMenu->spmenuAlternate, pmenu);
}
}
}
}
/*
* Return the handle to the system menu.
*/
if (pwnd->spmenuSys != NULL) {
/*
* The app is probably going to modify this menu and then we'll need to
* redraw the caption buttons. Hence we need to store the window pointer
* in this pmenu or we won't be able to know what window to repaint.
* The bogus thing is that we cannot call LockWndMenu here because this is
* not the actual pmenuSys.
*/
pmenu = _GetSubMenu(pwnd->spmenuSys, 0);
if (pmenu) {
SetMF(pmenu, MFAPPSYSMENU);
Lock(&pmenu->spwndNotify, pwnd);
}
return pmenu;
}
return NULL;
}
/***************************************************************************\
* MenuItemState
*
* Sets the menu item flags identified by wMask to the states identified
* by wFlags.
*
* History:
* 10-11-90 JimA Translated from ASM
\***************************************************************************/
DWORD MenuItemState(
PMENU pMenu,
UINT wCmd,
DWORD wFlags,
DWORD wMask,
PMENU *ppMenu)
{
PITEM pItem;
DWORD wRet;
/*
* Get a pointer the the menu item
*/
if ((pItem = MNLookUpItem(pMenu, wCmd, (BOOL) (wFlags & MF_BYPOSITION), ppMenu)) == NULL)
return (DWORD)-1;
/*
* Return previous state
*/
wRet = pItem->fState & wMask;
/*
* Set new state
*/
pItem->fState ^= ((wRet ^ wFlags) & wMask);
return wRet;
}
/***************************************************************************\
* EnableMenuItem
*
* Enable, disable or gray a menu item.
*
* History:
* 10-11-90 JimA Translated from ASM
\***************************************************************************/
DWORD xxxEnableMenuItem(
PMENU pMenu,
UINT wIDEnableItem,
UINT wEnable)
{
DWORD dres;
PMENU pRealMenu;
PPOPUPMENU ppopup;
CheckLock(pMenu);
dres = MenuItemState(pMenu, wIDEnableItem, wEnable,
MFS_GRAYED, &pRealMenu);
/*
* If enabling/disabling a system menu item, redraw the caption buttons
*/
if (TestMF(pMenu, MFAPPSYSMENU) && (pMenu->spwndNotify != NULL) && (wEnable != dres)) {
TL tlpwnd;
switch (wIDEnableItem) {
case SC_SIZE:
case SC_MOVE:
case SC_MINIMIZE:
case SC_MAXIMIZE:
case SC_CLOSE:
case SC_RESTORE:
ThreadLock(pMenu->spwndNotify, &tlpwnd);
xxxRedrawTitle(pMenu->spwndNotify, DC_BUTTONS);
ThreadUnlock(&tlpwnd);
}
}
/* 367162: If the menu is already being displayed we need to redraw it */
if(pRealMenu && (ppopup = MNGetPopupFromMenu(pRealMenu, NULL))){
xxxMNUpdateShownMenu(ppopup, NULL, MNUS_DEFAULT);
}
return dres;
}
/***************************************************************************\
* CheckMenuItem (API)
*
* Check or un-check a popup menu item.
*
* History:
* 10-11-90 JimA Translated from ASM
\***************************************************************************/
DWORD _CheckMenuItem(
PMENU pMenu,
UINT wIDCheckItem,
UINT wCheck)
{
return MenuItemState(pMenu, wIDCheckItem, wCheck, (UINT)MF_CHECKED, NULL);
}
/***************************************************************************\
*
* SetMenuDefaultItem() -
*
* Sets the default item in the menu, by command or by position based on the
* fByPosition flag.
* We unset all the other items as the default, then set the given one.
*
* The return value is TRUE if the given item was set as default, FALSE
* if not.
*
\***************************************************************************/
BOOL _SetMenuDefaultItem(PMENU pMenu, UINT wID, BOOL fByPosition)
{
UINT iItem;
UINT cItems;
PITEM pItem;
PITEM pItemFound;
PMENU pMenuFound;
//
// We need to check if wId actually exists on this menu. 0xFFFF means
// clear all default items.
//
if (wID != MFMWFP_NOITEM)
{
pItemFound = MNLookUpItem(pMenu, wID, fByPosition, &pMenuFound);
// item must be on same menu and can't be a separator
if ((pItemFound == NULL) || (pMenuFound != pMenu) || TestMFT(pItemFound, MFT_SEPARATOR))
return(FALSE);
}
else
pItemFound = NULL;
pItem = pMenu->rgItems;
cItems = pMenu->cItems;
// Walk the menu list, clearing MFS_DEFAULT from all other items, and
// setting MFS_DEFAULT on the requested one.
for (iItem = 0; iItem < cItems; iItem++, pItem++) {
//
// Note we don't change the state of lpItemFound if it exists. This
// is so that below, where we try to set the default, we can tell
// if we need to recalculate the underline.
//
if (TestMFS(pItem, MFS_DEFAULT) && (pItem != pItemFound))
{
//
// We are changing the default item. As such, it will be drawn
// with a different font than the one used to calculate it, if
// the menu has already been drawn once. We need to ensure
// that the underline gets drawn in the right place the next
// time the menu comes up. Cause it to recalculate.
//
// We do NOT do this if the item
// (a) isn't default--otherwise we'll recalculate the
// underline for every system menu item every time we go into
// menu mode because sysmenu init will call SetMenuDefaultItem.
// (b) isn't the item we're going to set as the default.
// That way we don't recalculate the underline when the item
// isn't changing state.
//
ClearMFS(pItem, MFS_DEFAULT);
pItem->ulX = UNDERLINE_RECALC;
pItem->ulWidth = 0;
}
}
if (wID != MFMWFP_NOITEM)
{
if (!TestMFS(pItemFound, MFS_DEFAULT))
{
//
// We are changing from non-default to default. Clear out
// the underline info. If the menu has never painted, this
// won't do anything. But it matters a lot if it has.
//
SetMFS(pItemFound, MFS_DEFAULT);
pItemFound->ulX = UNDERLINE_RECALC;
pItemFound->ulWidth = 0;
}
}
return(TRUE);
}
// --------------------------------------------------------------------------
//
// SetCloseDefault()
//
// Tries to find a close item in the first level of menu items. Looks
// for SC_CLOSE, then a couple other IDs. We'd rather not do lstrstri's
// for "Close", which is slow.
//
// --------------------------------------------------------------------------
void _SetCloseDefault(PMENU pSubMenu)
{
if (!_SetMenuDefaultItem(pSubMenu, SC_CLOSE, MF_BYCOMMAND))
{
//
// Let's try a couple other values.
// * Project -- 0x7000 less
// * FoxPro -- 0xC070
//
if (!_SetMenuDefaultItem(pSubMenu, SC_CLOSE - 0x7000, MF_BYCOMMAND))
_SetMenuDefaultItem(pSubMenu, 0xC070, MF_BYCOMMAND);
}
}
// --------------------------------------------------------------------------
//
// FindFakeMDIChild()
//
// Attempts to find first child visible child window in the zorder that
// has a system menu or is maxed. We can't check for an exact system
// menu match because several apps make their own copy of the sys menu.
//
// --------------------------------------------------------------------------
PWND FindFakeMDIChild(PWND pwnd)
{
PWND pwndReturn;
// Skip invisible windows and their descendants
if (!TestWF(pwnd, WFVISIBLE))
return(NULL);
// Did we hit pay dirt?
if (TestWF(pwnd, WFCHILD) && (TestWF(pwnd, WFMAXIMIZED) || (pwnd->spmenuSys)))
return(pwnd);
// Check our children
for (pwnd = pwnd->spwndChild; pwnd; pwnd = pwnd->spwndNext)
{
pwndReturn = FindFakeMDIChild(pwnd);
if (pwndReturn)
return(pwndReturn);
}
return(NULL);
}
// --------------------------------------------------------------------------
//
// SetupFakeMDIAppStuff()
//
// For apps that mess around with their own MDI (Excel, Word, Project,
// Quattro Pro), we want to make them a little more Chicago friendly.
// Namely we:
//
// (1) Set the default menu item to SC_CLOSE if there isn't one (this
// won't help FoxPro, but they do so much wrong stuff it doesn't
// really matter).
// That way double-clicks will still work.
//
// (2) Get the right small icon.
//
// The way we do this is to go find the child window of the menu bar parent
// who has a system menu that is this one.
//
// If the system menu is the standard one, then we can't do (2).
//
// --------------------------------------------------------------------------
void SetupFakeMDIAppStuff(PMENU lpMenu, PITEM lpItem)
{
PMENU pSubMenu;
PWND pwndParent;
PWND pwndChild;
if (!(pSubMenu = lpItem->spSubMenu))
return;
pwndParent = lpMenu->spwndNotify;
//
// Set up the default menu item. Project and FoxPro renumber their
// IDs so we do some special stuff for them, among others.
//
if (!TestWF(pwndParent, WFWIN40COMPAT))
{
if (_GetMenuDefaultItem(pSubMenu, TRUE, GMDI_USEDISABLED) == -1L)
_SetCloseDefault(pSubMenu);
}
//
// Don't touch the HIWORD if we don't find an HWND. That way apps
// like Excel which have starting-up maxed children can benefit a little.
// The first time the menu bar is redrawn, the child isn't visible/
// around (they add the item too early). But if it redraws later, or
// you max a child, the icon will kick in.
//
if (pwndChild = FindFakeMDIChild(pwndParent)) {
lpItem->dwItemData = (ULONG_PTR)HWq(pwndChild);
// lpItem->dwTypeData = MAKELONG(LOWORD(lpItem->dwTypeData), HW16(hwndChild));
}
}