Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

655 lines
19 KiB

/**************************** Module Header ********************************\
* Module Name: mnsys.c
*
* Copyright 1985-90, 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);
/***************************************************************************\
* GetSysMenuHandle
*
* Returns a handle to the system menu of the given window. NULL if
* the window doesn't have a system menu.
*
* History:
\***************************************************************************/
PMENU GetSysMenuHandle(
PWND pwnd)
{
PMENU pMenu;
PHE phe;
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.
*/
pMenu = pwnd->head.rpdesk->spmenuSys;
if (pMenu == NULL && !(PtiCurrent()->TIF_flags & TIF_SYSTEMTHREAD)) {
UNICODE_STRING strMenuName;
RtlInitUnicodeStringOrId(&strMenuName,
MAKEINTRESOURCE(ID_SYSMENU));
pMenu = xxxClientLoadMenu(NULL, &strMenuName);
Lock(&pwnd->head.rpdesk->spmenuSys, pMenu);
/*
* Set the owner to NULL to specify system ownership.
*/
if (pMenu != NULL) {
phe = HMPheFromObject(pMenu);
phe->pOwner = NULL;
phe = HMPheFromObject(_GetSubMenu(pMenu, 0));
phe->pOwner = NULL;
}
}
}
} else
pMenu = NULL;
return pMenu;
}
/***************************************************************************\
*
* GetSysMenu()
*
* Sets up the system menu first, then returns it.
*
\***************************************************************************/
PMENU GetSysMenu(PWND pwnd, BOOL fSubMenu)
{
PMENU pMenu;
SetSysMenu(pwnd);
if ((pMenu = GetSysMenuHandle(pwnd)) != NULL) {
if (fSubMenu)
pMenu = _GetSubMenu(pMenu, 0);
}
return(pMenu);
}
/***************************************************************************\
* SetSysMenu
*
* !
*
* History:
\***************************************************************************/
void SetSysMenu(
PWND pwnd)
{
PMENU pMenu;
UINT wSize;
UINT wMinimize;
UINT wMaximize;
UINT wMove;
UINT wRestore;
UINT wDefault;
BOOL fFramedDialogBox;
/*
* Get the handle of the current system menu.
*/
if ((pMenu = GetSysMenuHandle(pwnd)) != NULL) {
/*
* Are we dealing with a framed dialog box with a sys menu?
*/
fFramedDialogBox =
((TestWF(pwnd, WFBORDERMASK) == (BYTE)LOBYTE(WFDLGFRAME)) ||
(TestWF(pwnd, WEFDLGMODALFRAME)));
/*
* Needed for initial ALT-Space combination.
*/
MNPositionSysMenu(pwnd, pMenu);
pMenu = _GetSubMenu(pMenu, 0);
if (!pMenu)
return;
/*
* 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)) {
int dxMax, dyMax;
dxMax = gpsi->rcWork.right - gpsi->rcWork.left;
dyMax = gpsi->rcWork.bottom - gpsi->rcWork.top;
// FUDGE for WinOldAp
// if (InternalGetProp(pwnd, MAKEINTATOM(atmWinOldAp)))
// {
// dxMax += 2*(CXSIZEFRAME + CXEDGE);
// dyMax += 2*(CYSIZEFRAME + CYEDGE);
// }
if ((pwnd->rcWindow.right - pwnd->rcWindow.left < dxMax) ||
(pwnd->rcWindow.bottom - pwnd->rcWindow.top < dyMax))
{
wMove = 0;
}
}
wSize = MFS_GRAYED;
wMaximize = MFS_GRAYED;
}
if (!TestWF(pwnd, WFSIZEBOX))
wSize = MFS_GRAYED;
if (!fFramedDialogBox) {
_EnableMenuItem(pMenu, (UINT)SC_SIZE, wSize);
if (!TestWF(pwnd, WEFTOOLWINDOW))
{
_EnableMenuItem(pMenu, (UINT)SC_MINIMIZE, wMinimize);
_EnableMenuItem(pMenu, (UINT)SC_MAXIMIZE, wMaximize);
_EnableMenuItem(pMenu, (UINT)SC_RESTORE, wRestore);
}
}
_EnableMenuItem(pMenu, (UINT)SC_MOVE, wMove);
if (pMenu == pwnd->head.rpdesk->spmenuSys ||
pMenu == pwnd->head.rpdesk->spmenuDialogSys) {
/*
* Enable Close if this is the global system menu. Some hosebag may
* have disabled it.
*/
_EnableMenuItem(pMenu, (UINT)SC_CLOSE, 0);
}
if (wDefault == SC_CLOSE)
_SetCloseDefault(pMenu);
else
_SetMenuDefaultItem(pMenu, wDefault, MF_BYCOMMAND);
}
}
/***************************************************************************\
* GetSystemMenu
*
* !
*
* History:
\***************************************************************************/
PMENU _GetSystemMenu(
PWND pwnd,
BOOL fRevert)
{
PMENU pmenu;
/*
* Should we start with a fresh copy?
*/
if (fRevert) {
/*
* Destroy the old system menu.
*/
if (pwnd->spmenuSys != NULL &&
pwnd->spmenuSys != pwnd->head.rpdesk->spmenuSys &&
pwnd->spmenuSys != pwnd->head.rpdesk->spmenuDialogSys) {
pmenu = pwnd->spmenuSys;
Unlock(&pwnd->spmenuSys);
_DestroyMenu(pmenu);
}
} else {
/*
* Do we need to load a new system menu?
*/
if ((pwnd->spmenuSys == NULL ||
pwnd->spmenuSys == pwnd->head.rpdesk->spmenuSys ||
pwnd->spmenuSys == pwnd->head.rpdesk->spmenuDialogSys ) &&
TestWF(pwnd, WFSYSMENU)) {
PPOPUPMENU pGlobalPopupMenu;
UNICODE_STRING strMenuName;
RtlInitUnicodeStringOrId(&strMenuName,
(pwnd->spmenuSys == NULL ? MAKEINTRESOURCE(ID_SYSMENU) :
MAKEINTRESOURCE(ID_DIALOGSYSMENU)));
Lock(&(pwnd->spmenuSys), xxxClientLoadMenu(NULL, &strMenuName));
pmenu = pwnd->spmenuSys;
pGlobalPopupMenu = GetpGlobalPopupMenu(pwnd);
if (pGlobalPopupMenu && !pGlobalPopupMenu->fIsTrackPopup &&
pGlobalPopupMenu->spwndPopupMenu == pwnd) {
if (pGlobalPopupMenu->fIsSysMenu)
Lock(&pGlobalPopupMenu->spmenu, pmenu);
else
Lock(&pGlobalPopupMenu->spmenuAlternate, pmenu);
}
}
}
/*
* Return the handle to the system menu.
*/
if (pwnd->spmenuSys != NULL) {
pmenu = _GetSubMenu(pwnd->spmenuSys, 0);
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)
{
PITEM pItem;
DWORD wRet;
/*
* Get a pointer the the menu item
*/
if ((pItem = MNLookUpItem(pMenu, wCmd, (BOOL) (wFlags & MF_BYPOSITION), NULL)) == 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 _EnableMenuItem(
PMENU pMenu,
UINT wIDEnableItem,
UINT wEnable)
{
DWORD dres;
PMENU pSystemMenu;
dres = MenuItemState(pMenu, wIDEnableItem, wEnable,
MFS_GRAYED);
if ((pMenu->spwndNotify != NULL) &&
(pSystemMenu = GetSysMenuHandle(pMenu->spwndNotify)) &&
(pSystemMenu->cItems > 0) &&
(pSystemMenu->rgItems[0].spSubMenu == pMenu))
{
TL tlpwnd;
// enabling/disabling a system menu item -- this could affect the
// caption buttons so redraw them
ThreadLock(pMenu->spwndNotify, &tlpwnd);
xxxRedrawTitle(pMenu->spwndNotify, DC_BUTTONS);
ThreadUnlock(&tlpwnd);
}
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);
}
/***************************************************************************\
*
* CheckMenuRadioItem() -
*
* Checks one menu item in a range, unchecking the others. This can be
* done either MF_BYCOMMAND or MF_BYPOSITION. It works similarly to
* CheckRadioButton().
*
* The return value is TRUE if the given item was checked, FALSE if not.
*
\***************************************************************************/
BOOL _CheckMenuRadioItem(PMENU pMenu, UINT wIDFirst, UINT wIDLast,
UINT wIDCheck, UINT flags)
{
BOOL fByPosition = (BOOL) (flags & MF_BYPOSITION);
PMENU pMenuItemIsOn;
PITEM pItem;
UINT wIDCur;
BOOL fChecked = FALSE;
BOOL fFirst = TRUE;
for (wIDCur = wIDFirst; wIDCur <= wIDLast; wIDCur++) {
pItem = MNLookUpItem(pMenu, wIDCur, fByPosition, &pMenuItemIsOn);
if ((pItem != NULL) && !TestMFT(pItem, MFT_SEPARATOR)) {
if (fFirst) {
pMenu = pMenuItemIsOn;
fFirst = FALSE;
}
if (pMenu == pMenuItemIsOn) {
if (wIDCur == wIDCheck) {
// Check this item on.
SetMFT(pItem, MFT_RADIOCHECK);
SetMFS(pItem, MFS_CHECKED);
fChecked = TRUE;
} else {
// Check this item off, since it's in the uncheck range.
// NOTE: don't remove MFT_RADIOCHECK type
ClearMFS(pItem, MFS_CHECKED);
}
}
}
}
return(fChecked);
}
/***************************************************************************\
*
* 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))
{
//
// Bloody hell. 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 dork 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)HWq(pwndChild);
// lpItem->dwTypeData = MAKELONG(LOWORD(lpItem->dwTypeData), HW16(hwndChild));
}
}