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.
1096 lines
32 KiB
1096 lines
32 KiB
/**************************** Module Header ********************************\
|
|
* Module Name: mncomput.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* Menu Layout Calculation Routines
|
|
*
|
|
* History:
|
|
* 10-10-90 JimA Cleanup.
|
|
* 03-18-91 IanJa Window revalidation added
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
DWORD MNRecalcTabStrings(HDC hdc, PMENU pMenu, UINT iBeg, UINT iEnd,
|
|
DWORD xTab, DWORD hCount);
|
|
|
|
/***************************************************************************\
|
|
* xxxMNGetBitmapSize
|
|
*
|
|
* Returns TRUE if measureitem was sent and FALSE if not
|
|
*
|
|
* History:
|
|
* 07-23-96 GerardoB - Added header & fixed up for 5.0
|
|
\***************************************************************************/
|
|
BOOL xxxMNGetBitmapSize(
|
|
LPITEM pItem,
|
|
PWND pwndNotify)
|
|
{
|
|
MEASUREITEMSTRUCT mis;
|
|
|
|
if (pItem->cxBmp != MNIS_MEASUREBMP) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Send a measure item message to the owner
|
|
mis.CtlType = ODT_MENU;
|
|
mis.CtlID = 0;
|
|
mis.itemID = pItem->wID;
|
|
mis.itemWidth = 0;
|
|
// After scrollable menus
|
|
// mis32.itemHeight= gcyMenuFontChar;
|
|
mis.itemHeight= (UINT)gpsi->cySysFontChar;
|
|
mis.itemData = pItem->dwItemData;
|
|
|
|
xxxSendMessage(pwndNotify, WM_MEASUREITEM, 0, (LPARAM)&mis);
|
|
|
|
pItem->cxBmp = mis.itemWidth;
|
|
pItem->cyBmp = mis.itemHeight;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxItemSize
|
|
*
|
|
* Calc the dimensions of bitmaps and strings. Loword of returned
|
|
* value contains width, high word contains height of item.
|
|
*
|
|
* History:
|
|
* 07-23-96 GerardoB - fixed up for 5.0
|
|
\***************************************************************************/
|
|
BOOL xxxMNItemSize(
|
|
PMENU pMenu,
|
|
PWND pwndNotify,
|
|
HDC hdc,
|
|
PITEM pItem,
|
|
BOOL fPopup,
|
|
LPPOINT lppt)
|
|
{
|
|
BITMAP bmp;
|
|
int width = 0;
|
|
int height = 0;
|
|
DWORD xRightJustify;
|
|
LPWSTR lpMenuString;
|
|
HFONT hfnOld;
|
|
int tcExtra;
|
|
|
|
UNREFERENCED_PARAMETER(pMenu);
|
|
|
|
CheckLock(pMenu);
|
|
CheckLock(pwndNotify);
|
|
|
|
if (!fPopup) {
|
|
|
|
/*
|
|
* Save off the height of the top menu bar since we will used this often
|
|
* if the pItem is not in a popup. (ie. it is in the top level menu bar)
|
|
*/
|
|
height = SYSMET(CYMENUSIZE);
|
|
}
|
|
|
|
hfnOld = NULL;
|
|
if (TestMFS(pItem, MFS_DEFAULT)) {
|
|
if (ghMenuFontDef)
|
|
hfnOld = GreSelectFont(hdc, ghMenuFontDef);
|
|
else {
|
|
tcExtra = GreGetTextCharacterExtra(hdc);
|
|
GreSetTextCharacterExtra(hdc, tcExtra + 1 + (gcxMenuFontChar / gpsi->cxSysFontChar));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Compute bitmap dimensions if needed
|
|
*/
|
|
if (pItem->hbmp != NULL) {
|
|
if (pItem->hbmp == HBMMENU_CALLBACK) {
|
|
xxxMNGetBitmapSize(pItem, pwndNotify);
|
|
} else if (pItem->cxBmp == MNIS_MEASUREBMP) {
|
|
if (TestMFS(pItem, MFS_CACHEDBMP)) {
|
|
pItem->cxBmp = SYSMET(CXMENUSIZE);
|
|
pItem->cyBmp = SYSMET(CYMENUSIZE);
|
|
if (pItem->hbmp == HBMMENU_SYSTEM) {
|
|
pItem->cxBmp += SYSMET(CXEDGE);
|
|
/*
|
|
* Chicago/Memphis only stretch the width,
|
|
* not the height. NT Bug 124779. FritzS
|
|
*/
|
|
// pItem->cyBmp += SYSMET(CXEDGE);
|
|
}
|
|
} else {
|
|
if (GreExtGetObjectW(pItem->hbmp, sizeof(BITMAP), (LPSTR)&bmp)) {
|
|
pItem->cxBmp = bmp.bmWidth;
|
|
pItem->cyBmp = bmp.bmHeight;
|
|
} else {
|
|
/*
|
|
* If the bitmap is not useable, this is as good a default
|
|
* as any.
|
|
*/
|
|
pItem->cxBmp = SYSMET(CXMENUSIZE);
|
|
pItem->cyBmp = SYSMET(CYMENUSIZE);
|
|
}
|
|
}
|
|
}
|
|
width = pItem->cxBmp;
|
|
/*
|
|
* Remember the max bitmap width to align the text in all items.
|
|
*/
|
|
pMenu->cxTextAlign = max(pMenu->cxTextAlign, (DWORD)width);
|
|
/*
|
|
* In menu bars, we force the item to be at least CYMNSIZE.
|
|
* Fixes many, many problems w/ apps that fake own MDI.
|
|
*/
|
|
if (fPopup) {
|
|
height = pItem->cyBmp;
|
|
} else {
|
|
height = max((int)pItem->cyBmp, height);
|
|
}
|
|
} else if (TestMFT(pItem, MFT_OWNERDRAW)) {
|
|
// This is an ownerdraw item -- the width and height are stored in
|
|
// cxBmp and cyBmp
|
|
xxxMNGetBitmapSize(pItem, pwndNotify);
|
|
width = pItem->cxBmp;
|
|
//
|
|
// Ignore height with menu bar now--that's set by user.
|
|
//
|
|
if (fPopup) {
|
|
height = pItem->cyBmp;
|
|
// If this item has a popup (hierarchical) menu associated with it, then
|
|
// reserve room for the bitmap that tells the user that a hierarchical
|
|
// menu exists here.
|
|
// B#2966, t-arthb
|
|
|
|
UserAssert(fPopup == (TestMF(pMenu, MFISPOPUP) != 0));
|
|
|
|
width = width + (gcxMenuFontChar << 1);
|
|
}
|
|
}
|
|
|
|
if ((pItem->lpstr != NULL) && (!TestMFT(pItem, MFT_OWNERDRAW)) ) {
|
|
SIZE size;
|
|
|
|
/*
|
|
* This menu item contains a string
|
|
*/
|
|
|
|
/*
|
|
* We want to keep the menu bar height if this isn't a popup.
|
|
*/
|
|
if (fPopup) {
|
|
/* The thickness of mnemonic underscore is CYBORDER and the gap
|
|
* between the characters and the underscore is another CYBORDER
|
|
*/
|
|
height = max(height, gcyMenuFontChar + gcyMenuFontExternLeading + SYSMET(CYEDGE));
|
|
}
|
|
|
|
lpMenuString = TextPointer(pItem->lpstr);
|
|
xRightJustify = FindCharPosition(lpMenuString, TEXT('\t'));
|
|
|
|
xxxPSMGetTextExtent(hdc, lpMenuString, xRightJustify, &size);
|
|
|
|
if (width) {
|
|
width += MNXSPACE + size.cx;
|
|
} else {
|
|
width = size.cx;
|
|
}
|
|
}
|
|
|
|
if (fPopup && !TestMFT(pItem, MFT_OWNERDRAW)) {
|
|
/*
|
|
* Add on space for checkmark, then horz spacing for default & disabled
|
|
* plus some left margin.
|
|
*/
|
|
if (TestMF(pMenu, MNS_CHECKORBMP) || !TestMF(pMenu, MNS_NOCHECK)) {
|
|
width += gpsi->oembmi[OBI_MENUCHECK].cx;
|
|
}
|
|
width += MNXSPACE + MNLEFTMARGIN + 2;
|
|
height += 2;
|
|
}
|
|
|
|
if (TestMFS(pItem, MFS_DEFAULT)) {
|
|
if (hfnOld)
|
|
GreSelectFont(hdc, hfnOld);
|
|
else
|
|
GreSetTextCharacterExtra(hdc, tcExtra);
|
|
}
|
|
|
|
/*
|
|
* Loword contains width, high word contains height of item.
|
|
*/
|
|
lppt->x = width;
|
|
lppt->y = height;
|
|
|
|
return(TestMFT(pItem, MFT_OWNERDRAW));
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMNCompute
|
|
*
|
|
* !
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
int xxxMNCompute(
|
|
PMENU pMenu,
|
|
PWND pwndNotify,
|
|
DWORD yMenuTop,
|
|
DWORD xMenuLeft,
|
|
DWORD cxMax,
|
|
LPDWORD lpdwMenuHeight)
|
|
{
|
|
UINT cItem;
|
|
DWORD cxItem;
|
|
DWORD cyItem;
|
|
DWORD cyItemKeep;
|
|
DWORD yPopupTop;
|
|
INT cMaxWidth;
|
|
DWORD cMaxHeight;
|
|
UINT cItemBegCol;
|
|
DWORD temp;
|
|
PITEM pCurItem;
|
|
POINT ptMNItemSize;
|
|
BOOL fOwnerDrawItems;
|
|
BOOL fMenuBreak;
|
|
LPWSTR lpsz;
|
|
BOOL fPopupMenu;
|
|
DWORD menuHeight = 0;
|
|
HDC hdc;
|
|
HFONT hOldFont;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
BOOL fStringAndBitmapItems;
|
|
|
|
CheckLock(pMenu);
|
|
CheckLock(pwndNotify);
|
|
|
|
/*
|
|
* Whoever computes the menu, becomes the owner.
|
|
*/
|
|
if (pwndNotify != pMenu->spwndNotify) {
|
|
Lock(&pMenu->spwndNotify, pwndNotify);
|
|
}
|
|
|
|
|
|
if (lpdwMenuHeight != NULL) {
|
|
menuHeight = *lpdwMenuHeight;
|
|
}
|
|
|
|
/*
|
|
* Empty menus have a height of zero.
|
|
*/
|
|
if (pMenu->cItems == 0) {
|
|
return 0;
|
|
}
|
|
|
|
hdc = _GetDCEx(NULL, NULL, DCX_WINDOW | DCX_CACHE);
|
|
hOldFont = GreSelectFont(hdc, ghMenuFont);
|
|
|
|
/*
|
|
* Try to make a non-multirow menu first.
|
|
*/
|
|
pMenu->fFlags &= (~MFMULTIROW);
|
|
|
|
fPopupMenu = TestMF(pMenu, MFISPOPUP);
|
|
|
|
if (fPopupMenu) {
|
|
/*
|
|
* Reset the menu bar height to 0 if this is a popup since we are
|
|
* being called from menu.c MN_SIZEWINDOW.
|
|
*/
|
|
menuHeight = 0;
|
|
} else if (pwndNotify != NULL) {
|
|
pMenu->cxMenu = cxMax;
|
|
}
|
|
|
|
/*
|
|
* Initialize the computing variables.
|
|
*/
|
|
cMaxWidth = cyItemKeep = 0L;
|
|
cItemBegCol = 0;
|
|
|
|
cyItem = yPopupTop = yMenuTop;
|
|
cxItem = xMenuLeft;
|
|
|
|
pCurItem = (PITEM)&pMenu->rgItems[0];
|
|
/*
|
|
* cxTextAlign is used to align the text in all items; this is useful
|
|
* in popup menus that mix text only items with bitmap-text items. It's
|
|
* set to the max bitmap width plus some spacing.
|
|
* Do this for new menus wich use string AND bitmaps on the same item
|
|
*/
|
|
fStringAndBitmapItems = FALSE;
|
|
pMenu->cxTextAlign = 0;
|
|
|
|
/*
|
|
* Process each item in the menu.
|
|
*/
|
|
fOwnerDrawItems = FALSE;
|
|
for (cItem = 0; cItem < pMenu->cItems; cItem++) {
|
|
|
|
/*
|
|
* If it's not a separator, find the dimensions of the object.
|
|
*/
|
|
if (TestMFT(pCurItem, MFT_SEPARATOR) &&
|
|
( !TestMFT(pCurItem, MFT_OWNERDRAW) ||
|
|
(LOWORD(ptiCurrent->dwExpWinVer) < VER40)) ) {
|
|
/*
|
|
* If version is less than 4.0 don't test the MFT_OWNERDRAW
|
|
* flag. Bug 21922; App MaxEda has both separator and Ownerdraw
|
|
* flags on. In 3.51 we didn't test the OwnerDraw flag
|
|
*/
|
|
|
|
//
|
|
// This is a separator. It's drawn as wide as the menu area,
|
|
// leaving some space above and below. Since the menu area is
|
|
// the max of the items' widths, we set our width to 0 so as not
|
|
// to affect the result.
|
|
//
|
|
pCurItem->cxItem = 0;
|
|
pCurItem->cyItem = SYSMET(CYMENUSIZE) / 2;
|
|
|
|
|
|
} else {
|
|
/*
|
|
* Are we using NT5 strings and bitmaps?
|
|
*/
|
|
fStringAndBitmapItems |= ((pCurItem->hbmp != NULL) && (pCurItem->lpstr != NULL));
|
|
/*
|
|
* Get the item's X and Y dimensions.
|
|
*/
|
|
if (xxxMNItemSize(pMenu, pwndNotify, hdc, pCurItem, fPopupMenu, &ptMNItemSize))
|
|
fOwnerDrawItems = TRUE;
|
|
|
|
pCurItem->cxItem = ptMNItemSize.x;
|
|
pCurItem->cyItem = ptMNItemSize.y;
|
|
if (!fPopupMenu && ((pCurItem->hbmp == NULL) || (pCurItem->lpstr != NULL))) {
|
|
pCurItem->cxItem += gcxMenuFontChar * 2;
|
|
}
|
|
}
|
|
|
|
if (menuHeight != 0)
|
|
pCurItem->cyItem = menuHeight;
|
|
|
|
/*
|
|
* If this is the first item, initialize cMaxHeight.
|
|
*/
|
|
if (cItem == 0)
|
|
cMaxHeight = pCurItem->cyItem;
|
|
|
|
/*
|
|
* Is this a Pull-Down menu?
|
|
*/
|
|
if (fPopupMenu) {
|
|
|
|
/*
|
|
* If this item has a break or is the last item...
|
|
*/
|
|
if ((fMenuBreak = TestMFT(pCurItem, MFT_BREAK)) ||
|
|
pMenu->cItems == cItem + (UINT)1) {
|
|
|
|
/*
|
|
* Keep cMaxWidth around if this is not the last item.
|
|
*/
|
|
temp = cMaxWidth;
|
|
if (pMenu->cItems == cItem + (UINT)1) {
|
|
if ((int)(pCurItem->cxItem) > cMaxWidth)
|
|
temp = pCurItem->cxItem;
|
|
}
|
|
|
|
/*
|
|
* Get new width of string from RecalcTabStrings.
|
|
*/
|
|
temp = MNRecalcTabStrings(hdc, pMenu, cItemBegCol,
|
|
(UINT)(cItem + (fMenuBreak ? 0 : 1)), temp, cxItem);
|
|
|
|
/*
|
|
* If this item has a break, account for it.
|
|
*/
|
|
if (fMenuBreak) {
|
|
//
|
|
// Add on space for the etch and a border on either side.
|
|
// NOTE: For old apps that do weird stuff with owner
|
|
// draw, keep 'em happy by adding on the same amount
|
|
// of space we did in 3.1.
|
|
//
|
|
if (fOwnerDrawItems && !TestWF(pwndNotify, WFWIN40COMPAT))
|
|
cxItem = temp + SYSMET(CXBORDER);
|
|
else
|
|
cxItem = temp + 2 * SYSMET(CXEDGE);
|
|
|
|
/*
|
|
* Reset the cMaxWidth to the current item.
|
|
*/
|
|
cMaxWidth = pCurItem->cxItem;
|
|
|
|
/*
|
|
* Start at the top.
|
|
*/
|
|
cyItem = yPopupTop;
|
|
|
|
/*
|
|
* Save the item where this column begins.
|
|
*/
|
|
cItemBegCol = cItem;
|
|
|
|
/*
|
|
* If this item is also the last item, recalc for this
|
|
* column.
|
|
*/
|
|
if (pMenu->cItems == (UINT)(cItem + 1)) {
|
|
temp = MNRecalcTabStrings(hdc, pMenu, cItem,
|
|
(UINT)(cItem + 1), cMaxWidth, cxItem);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If this is the last entry, supply the width.
|
|
*/
|
|
if (pMenu->cItems == cItem + (UINT)1)
|
|
pMenu->cxMenu = temp;
|
|
}
|
|
|
|
pCurItem->xItem = cxItem;
|
|
pCurItem->yItem = cyItem;
|
|
|
|
cyItem += pCurItem->cyItem;
|
|
|
|
if (cyItemKeep < cyItem) {
|
|
cyItemKeep = cyItem;
|
|
}
|
|
} else {
|
|
/*
|
|
* This a Top Level menu, not a Pull-Down.
|
|
*/
|
|
|
|
/*
|
|
* Adjust right aligned items before testing for multirow
|
|
*/
|
|
if (pCurItem->lpstr != NULL) {
|
|
lpsz = TextPointer(pCurItem->lpstr);
|
|
if ((lpsz != NULL) && (*lpsz == CH_HELPPREFIX)) {
|
|
pCurItem->cxItem -= gcxMenuFontChar;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* If this is a new line or a menu break.
|
|
*/
|
|
if ((TestMFT(pCurItem, MFT_BREAK)) ||
|
|
(((cxItem + pCurItem->cxItem + gcxMenuFontChar) >
|
|
(xMenuLeft + pMenu->cxMenu)) && (cItem != 0))) {
|
|
cyItem += cMaxHeight;
|
|
|
|
cxItem = xMenuLeft;
|
|
cMaxHeight = pCurItem->cyItem;
|
|
pMenu->fFlags |= MFMULTIROW;
|
|
}
|
|
|
|
pCurItem->yItem = cyItem;
|
|
|
|
pCurItem->xItem = cxItem;
|
|
cxItem += pCurItem->cxItem;
|
|
}
|
|
|
|
if (cMaxWidth < (int)(pCurItem->cxItem)) {
|
|
cMaxWidth = pCurItem->cxItem;
|
|
}
|
|
|
|
if (cMaxHeight != pCurItem->cyItem) {
|
|
if (cMaxHeight < pCurItem->cyItem)
|
|
cMaxHeight = pCurItem->cyItem;
|
|
|
|
if (!fPopupMenu) {
|
|
menuHeight = cMaxHeight;
|
|
}
|
|
}
|
|
|
|
if (!fPopupMenu) {
|
|
cyItemKeep = cyItem + cMaxHeight;
|
|
}
|
|
|
|
pCurItem++;
|
|
}
|
|
|
|
/*
|
|
* Determine where the strings should be drawn so they are aligned.
|
|
* The alignment is only for popup (vertical) menus (see
|
|
* xxxRealDrawMenuItem). The actual space depends on the MNS_NOCHECK
|
|
* and MNS_CHECKORBMP styles Multicolumn menus don't get aligment (now
|
|
* that we have scrollbars, multicolumn is out).
|
|
*/
|
|
if (!fStringAndBitmapItems || (cItemBegCol != 0)) {
|
|
pMenu->cxTextAlign = 0;
|
|
} else if (TestMF(pMenu, MNS_NOCHECK)) {
|
|
pMenu->cxTextAlign += MNXSPACE;
|
|
} else if (TestMF(pMenu, MNS_CHECKORBMP)) {
|
|
pMenu->cxTextAlign = max(pMenu->cxTextAlign, (UINT)gpsi->oembmi[OBI_MENUCHECK].cx);
|
|
pMenu->cxTextAlign += MNXSPACE;
|
|
} else {
|
|
pMenu->cxTextAlign += gpsi->oembmi[OBI_MENUCHECK].cx + MNXSPACE;
|
|
}
|
|
|
|
/*
|
|
* Add the left margin
|
|
*/
|
|
if (pMenu->cxTextAlign != 0) {
|
|
pMenu->cxTextAlign += MNLEFTMARGIN;
|
|
}
|
|
|
|
|
|
if (cItemBegCol && pMenu->cItems &&
|
|
TestMFT(((PITEM)&pMenu->rgItems[0]), MFT_RIGHTJUSTIFY)) {
|
|
//
|
|
// multi-column, if we are in RtoL mode, reverse the columns
|
|
//
|
|
pCurItem = &pMenu->rgItems[0];
|
|
|
|
for (cItem = 0; cItem < pMenu->cItems; cItem++, pCurItem++) {
|
|
pCurItem->xItem = pMenu->cxMenu -
|
|
(pCurItem->xItem + pCurItem->cxItem);
|
|
}
|
|
}
|
|
|
|
GreSelectFont(hdc, hOldFont);
|
|
_ReleaseDC(hdc);
|
|
|
|
pMenu->cyMenu = cyItemKeep - yMenuTop;
|
|
|
|
if (lpdwMenuHeight != NULL) {
|
|
*lpdwMenuHeight = menuHeight;
|
|
}
|
|
|
|
return pMenu->cyMenu;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MBC_RightJustifyMenu
|
|
*
|
|
* !
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
VOID MBC_RightJustifyMenu(
|
|
PMENU pMenu)
|
|
{
|
|
PITEM pItem;
|
|
int cItem;
|
|
int iFirstRJItem = MFMWFP_NOITEM;
|
|
DWORD xMenuPos;
|
|
DWORD yPos;
|
|
DWORD xPosStart;
|
|
DWORD xPosEnd;
|
|
int cItemEnd;
|
|
int cItemStart;
|
|
BOOL bIsWin95;
|
|
|
|
//
|
|
// Need to compensate for MDI menus. Need to do all here as Win31/Hebrew did
|
|
// this. Also messed up computation, anything non-text was not moved.
|
|
//
|
|
if (pMenu->cItems == 0) {
|
|
return;
|
|
}
|
|
|
|
pItem = (PITEM)&pMenu->rgItems[0];
|
|
cItemStart = 0;
|
|
|
|
if (TestMF(pMenu,MFRTL)) {
|
|
bIsWin95 = TestWF(pMenu->spwndNotify, WFWIN40COMPAT);
|
|
|
|
while (cItemStart < (int)pMenu->cItems) {
|
|
if (bIsWin95) {
|
|
//
|
|
// deal with fake MDI dude.
|
|
//
|
|
if (!cItemStart && IsMDIItem(pItem)) {
|
|
goto StillFindStart;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (TestMFT(pItem, MFT_BITMAP)) {
|
|
if (pItem->hbmp > HBMMENU_MAX) {
|
|
break;
|
|
} else {
|
|
goto StillFindStart;
|
|
}
|
|
}
|
|
|
|
if (!TestMFT(pItem, MFT_OWNERDRAW)) {
|
|
break;
|
|
}
|
|
|
|
StillFindStart:
|
|
cItemStart++;
|
|
pItem = pMenu->rgItems + cItemStart;
|
|
}
|
|
|
|
//
|
|
// Anything before cItems should be left where it is. Now need to find
|
|
// the last item to fool with.
|
|
//
|
|
cItemEnd = pMenu->cItems - 1;
|
|
pItem = pMenu->rgItems + cItemEnd;
|
|
|
|
while (cItemEnd > cItemStart) {
|
|
if (bIsWin95) {
|
|
//
|
|
// fake mdi dudes
|
|
//
|
|
if (IsMDIItem(pItem)) {
|
|
goto StillFindEnd;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!TestMFT(pItem, MFT_BITMAP) && !TestMFT(pItem, MFT_OWNERDRAW)) {
|
|
break;
|
|
}
|
|
StillFindEnd:
|
|
cItemEnd--;
|
|
pItem = pMenu->rgItems + cItemEnd;
|
|
}
|
|
|
|
yPos = pMenu->rgItems[0].yItem;
|
|
xMenuPos = pMenu->cxMenu ;
|
|
xPosStart = xMenuPos; // for 2nd row onward
|
|
xPosEnd = pMenu->rgItems[cItemStart].xItem ;
|
|
|
|
for (cItem = pMenu->cItems-1; cItem > cItemEnd; cItem--) {
|
|
//
|
|
// Force any MDI dudes back to the top line again.
|
|
//
|
|
pItem = pMenu->rgItems + cItem;
|
|
xMenuPos = pItem->xItem = xMenuPos - pItem->cxItem;
|
|
pItem->yItem = yPos;
|
|
}
|
|
|
|
for (cItem = cItemStart; cItem <= cItemEnd; cItem++) {
|
|
pItem = pMenu->rgItems + cItem;
|
|
if (xMenuPos - pItem->cxItem > xPosEnd) {
|
|
xMenuPos -= pItem->cxItem;
|
|
} else {
|
|
xMenuPos = xPosStart - pItem->cxItem;
|
|
yPos += pItem->cyItem;
|
|
xPosEnd = 0;
|
|
}
|
|
pItem->xItem = xMenuPos;
|
|
pItem->yItem = yPos;
|
|
}
|
|
} else {
|
|
// B#4055
|
|
// Use signed arithmetic so comparison cItem >= iFirstRJItem won't
|
|
// cause underflow.
|
|
for (cItem = 0; cItem < (int)pMenu->cItems; cItem++) {
|
|
// Find the first item which is right justified.
|
|
if (TestMFT((pMenu->rgItems + cItem), MFT_RIGHTJUSTIFY)) {
|
|
iFirstRJItem = cItem;
|
|
xMenuPos = pMenu->cxMenu + pMenu->rgItems[0].xItem;
|
|
for (cItem = (int)pMenu->cItems - 1; cItem >= iFirstRJItem; cItem--) {
|
|
pItem = pMenu->rgItems + cItem;
|
|
xMenuPos -= pItem->cxItem;
|
|
if (pItem->xItem < xMenuPos)
|
|
pItem->xItem = xMenuPos;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMenuBarCompute
|
|
*
|
|
* Returns the height of the menubar menu. yMenuTop, xMenuLeft, and
|
|
* cxMax are used when computing the height/width of top level menu bars in
|
|
* windows.
|
|
*
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
int xxxMenuBarCompute(
|
|
PMENU pMenu,
|
|
PWND pwndNotify,
|
|
DWORD yMenuTop,
|
|
DWORD xMenuLeft,
|
|
int cxMax)
|
|
{
|
|
int size;
|
|
/* menuHeight is set by MNCompute when dealing with a top level menu and
|
|
* not all items in the menu bar have the same height. Thus, by setting
|
|
* menuHeight, MNCompute is called a second time to set every item to the
|
|
* same height. The actual value stored in menuHeight is the maximum
|
|
* height of all the menu bar items
|
|
*/
|
|
DWORD menuHeight = 0;
|
|
|
|
CheckLock(pwndNotify);
|
|
CheckLock(pMenu);
|
|
|
|
size = xxxMNCompute(pMenu, pwndNotify, yMenuTop, xMenuLeft, cxMax, &menuHeight);
|
|
|
|
if (!TestMF(pMenu, MFISPOPUP)) {
|
|
if (menuHeight != 0) {
|
|
|
|
/*
|
|
* Add a border for the multi-row case.
|
|
*/
|
|
size = xxxMNCompute(pMenu, pwndNotify, yMenuTop, xMenuLeft,
|
|
cxMax, &menuHeight);
|
|
}
|
|
|
|
/*
|
|
* Right justification of HELP items is only needed on top level
|
|
* menus.
|
|
*/
|
|
MBC_RightJustifyMenu(pMenu);
|
|
}
|
|
|
|
/*
|
|
* There's an extra border underneath the menu bar, if it's not empty!
|
|
*/
|
|
return(size ? size + SYSMET(CYBORDER) : size);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMNRecomputeBarIfNeeded
|
|
*
|
|
* !
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
VOID xxxMNRecomputeBarIfNeeded(
|
|
PWND pwndNotify,
|
|
PMENU pMenu)
|
|
{
|
|
int cxFrame;
|
|
int cyFrame;
|
|
|
|
UserAssert(!TestMF(pMenu, MFISPOPUP));
|
|
|
|
CheckLock(pwndNotify);
|
|
CheckLock(pMenu);
|
|
|
|
if (!TestMF(pMenu, MFSYSMENU)
|
|
&& ((pMenu->spwndNotify != pwndNotify) || !pMenu->cxMenu || !pMenu->cyMenu)) {
|
|
int cBorders;
|
|
|
|
cBorders = GetWindowBorders(pwndNotify->style, pwndNotify->ExStyle, TRUE, FALSE);
|
|
cxFrame = cBorders * SYSMET(CXBORDER);
|
|
cyFrame = cBorders * SYSMET(CYBORDER);
|
|
|
|
cyFrame += GetCaptionHeight(pwndNotify);
|
|
|
|
// The width passed in this call was larger by cxFrame;
|
|
// Fix for Bug #11466 - Fixed by SANKAR - 01/06/92 --
|
|
xxxMenuBarCompute(pMenu, pwndNotify, cyFrame, cxFrame,
|
|
(pwndNotify->rcWindow.right - pwndNotify->rcWindow.left) - cxFrame * 2);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* RecalcTabStrings
|
|
*
|
|
* !
|
|
*
|
|
* History:
|
|
* 10-11-90 JimA Translated from ASM
|
|
\***************************************************************************/
|
|
DWORD MNRecalcTabStrings(
|
|
HDC hdc,
|
|
PMENU pMenu,
|
|
UINT iBeg,
|
|
UINT iEnd,
|
|
DWORD xTab,
|
|
DWORD hCount)
|
|
{
|
|
UINT i;
|
|
UINT cOwnerDraw;
|
|
int adx;
|
|
int maxWidth = 0;
|
|
int cx;
|
|
PITEM pItem;
|
|
CheckLock(pMenu);
|
|
|
|
xTab += hCount;
|
|
|
|
if ((iBeg >= pMenu->cItems) || (iBeg > iEnd))
|
|
goto SeeYa;
|
|
|
|
cOwnerDraw = 0;
|
|
|
|
for (i = iBeg, pItem = pMenu->rgItems + iBeg; i < iEnd; pItem++, i++) {
|
|
adx = 0;
|
|
|
|
/*
|
|
* Subtract hCount to make dxTab relative to start of column for
|
|
* multi-column menus.
|
|
*/
|
|
|
|
pItem->dxTab = xTab - hCount;
|
|
|
|
// Skip non-string or empty string items
|
|
if ((pItem->lpstr != NULL) && !TestMFT(pItem, MFT_OWNERDRAW)) {
|
|
LPWSTR lpString = TextPointer(pItem->lpstr);
|
|
int tp;
|
|
SIZE size;
|
|
|
|
// Are there any tabs?
|
|
tp = FindCharPosition(lpString, TEXT('\t'));
|
|
if (tp < (int) pItem->cch) {
|
|
PTHREADINFO ptiCurrent = PtiCurrentShared();
|
|
|
|
if (CALL_LPK(ptiCurrent)) {
|
|
xxxClientGetTextExtentPointW(hdc, lpString + tp + 1,
|
|
pItem->cch - tp - 1, &size);
|
|
} else {
|
|
GreGetTextExtentW(hdc, lpString + tp + 1,
|
|
pItem->cch - tp - 1, &size, GGTE_WIN3_EXTENT);
|
|
}
|
|
adx = gcxMenuFontChar + size.cx;
|
|
}
|
|
} else if (TestMFT(pItem, MFT_OWNERDRAW))
|
|
cOwnerDraw++;
|
|
|
|
adx += xTab;
|
|
|
|
if (adx > maxWidth)
|
|
maxWidth = adx;
|
|
|
|
}
|
|
|
|
/*
|
|
* Add on space for hierarchical arrow. So basically, popup menu items
|
|
* can have 4 columns:
|
|
* (1) Checkmark
|
|
* (2) Text
|
|
* (3) Tabbed text for accel
|
|
* (4) Hierarchical arrow
|
|
*
|
|
* But, we only do this if at least one item isn't ownerdraw
|
|
* and if there's at least one submenu in the popup.
|
|
*/
|
|
if (cOwnerDraw != (iEnd - iBeg)) {
|
|
maxWidth += gcxMenuFontChar + gpsi->oembmi[OBI_MENUCHECK].cx;
|
|
}
|
|
|
|
cx = maxWidth - hCount;
|
|
|
|
for (i = iBeg, pItem = pMenu->rgItems + iBeg; i < iEnd; pItem++, i++)
|
|
pItem->cxItem = cx;
|
|
|
|
SeeYa:
|
|
return(maxWidth);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* GetMenuPwnd
|
|
*
|
|
* This function is used by xxxGetMenuItemRect and xxxMenuItemFromPoint
|
|
* which expect a pointer to the menu window for popup menus.
|
|
*
|
|
* In 4.0, apps had to go the extra mile to find the menu window; but this
|
|
* is bogus since menu windows are an internally thing not directly exposed
|
|
* to applications.
|
|
*
|
|
* 08/19/97 GerardoB Created
|
|
\***************************************************************************/
|
|
PWND GetMenuPwnd(
|
|
PWND pwnd,
|
|
PMENU pmenu)
|
|
{
|
|
if (TestMF(pmenu, MFISPOPUP)) {
|
|
if ((pwnd == NULL) || (GETFNID(pwnd) != FNID_MENU)) {
|
|
PPOPUPMENU ppopup = MNGetPopupFromMenu(pmenu, NULL);
|
|
if (ppopup != NULL) {
|
|
UserAssert(ppopup->spmenu == pmenu);
|
|
pwnd = ppopup->spwndPopupMenu;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pwnd;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* GetMenuItemRect
|
|
\***************************************************************************/
|
|
BOOL xxxGetMenuItemRect(
|
|
PWND pwnd,
|
|
PMENU pMenu,
|
|
UINT uIndex,
|
|
LPRECT lprcScreen)
|
|
{
|
|
PITEM pItem;
|
|
int dx, dy;
|
|
BOOL fRTL;
|
|
|
|
CheckLock(pwnd);
|
|
CheckLock(pMenu);
|
|
|
|
SetRectEmpty(lprcScreen);
|
|
|
|
if (uIndex >= pMenu->cItems) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Raid #315084: Compatiblity with NT4/Win95/98
|
|
*
|
|
* WordPerfect does a long complex way to calc the menu rect
|
|
* by calling this API. It calls GetMenuItemRect() with the app's
|
|
* window.
|
|
*/
|
|
if (pwnd == NULL || TestWF(pwnd, WFWIN50COMPAT)) {
|
|
pwnd = GetMenuPwnd(pwnd, pMenu);
|
|
}
|
|
|
|
/*
|
|
* If no pwnd, no go.
|
|
* IMPORTANT: For MFISPOPUP we might get a different pwnd but we don't lock
|
|
* it because we won't call back.
|
|
*/
|
|
if (pwnd == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
fRTL = TestWF(pwnd, WEFLAYOUTRTL);
|
|
|
|
if (TestMF(pMenu, MFISPOPUP)) {
|
|
if (fRTL) {
|
|
dx = pwnd->rcClient.right;
|
|
} else {
|
|
dx = pwnd->rcClient.left;
|
|
}
|
|
dy = pwnd->rcClient.top;
|
|
} else {
|
|
xxxMNRecomputeBarIfNeeded(pwnd, pMenu);
|
|
if (fRTL) {
|
|
dx = pwnd->rcWindow.right;
|
|
} else {
|
|
dx = pwnd->rcWindow.left;
|
|
}
|
|
dy = pwnd->rcWindow.top;
|
|
}
|
|
|
|
if (uIndex >= pMenu->cItems) {
|
|
return FALSE;
|
|
}
|
|
|
|
pItem = pMenu->rgItems + uIndex;
|
|
|
|
lprcScreen->right = pItem->cxItem;
|
|
lprcScreen->bottom = pItem->cyItem;
|
|
|
|
if (fRTL) {
|
|
dx -= (pItem->cxItem + pItem->xItem);
|
|
} else {
|
|
dx += pItem->xItem;
|
|
}
|
|
dy += pItem->yItem;
|
|
OffsetRect(lprcScreen, dx, dy);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMenuItemFromPoint
|
|
\***************************************************************************/
|
|
int xxxMenuItemFromPoint(
|
|
PWND pwnd,
|
|
PMENU pMenu,
|
|
POINT ptScreen)
|
|
{
|
|
CheckLock(pwnd);
|
|
CheckLock(pMenu);
|
|
|
|
/*
|
|
* If no pwnd, no go.
|
|
*
|
|
* IMPORTANT: For MFISPOPUP we might get a different pwnd but we don't lock
|
|
* it because we won't call back.
|
|
*/
|
|
pwnd = GetMenuPwnd(pwnd, pMenu);
|
|
if (pwnd == NULL) {
|
|
return MFMWFP_NOITEM;
|
|
}
|
|
|
|
if (!TestMF(pMenu, MFISPOPUP)) {
|
|
xxxMNRecomputeBarIfNeeded(pwnd, pMenu);
|
|
}
|
|
|
|
return MNItemHitTest(pMenu, pwnd, ptScreen);
|
|
}
|
|
|
|
|
|
PMENU MakeMenuRtoL(
|
|
PMENU pMenu,
|
|
BOOL bRtoL)
|
|
{
|
|
PITEM pItem;
|
|
int cItem;
|
|
|
|
if (bRtoL) {
|
|
SetMF(pMenu,MFRTL);
|
|
} else {
|
|
ClearMF(pMenu,MFRTL);
|
|
}
|
|
|
|
for (cItem = 0; cItem < (int)pMenu->cItems; cItem++) {
|
|
pItem = pMenu->rgItems + cItem;
|
|
if (bRtoL) {
|
|
SetMFT(pItem, MFT_RIGHTJUSTIFY);
|
|
SetMFT(pItem, MFT_RIGHTORDER);
|
|
} else {
|
|
ClearMFT(pItem, MFT_RIGHTJUSTIFY);
|
|
ClearMFT(pItem, MFT_RIGHTORDER);
|
|
}
|
|
|
|
if (pItem->spSubMenu) {
|
|
MakeMenuRtoL(pItem->spSubMenu, bRtoL);
|
|
}
|
|
}
|
|
|
|
return pMenu;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxCalcMenuBar
|
|
*
|
|
* 3/8/2000 vadimg created
|
|
\***************************************************************************/
|
|
UINT xxxCalcMenuBar(
|
|
PWND pwnd,
|
|
int iLeftOffset,
|
|
int iRightOffset,
|
|
int iTopOffset,
|
|
LPCRECT prcWnd)
|
|
{
|
|
PMENU pMenu;
|
|
UINT cyMenu, cxMenuMax;
|
|
TL tlpMenu;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
pMenu = pwnd->spmenu;
|
|
|
|
if (TestwndChild(pwnd) || pMenu == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
ThreadLockMenuNoModify(pMenu, &tlpMenu);
|
|
|
|
cxMenuMax = (prcWnd->right - iRightOffset) - (prcWnd->left + iLeftOffset);
|
|
|
|
xxxMenuBarCompute(pMenu, pwnd, iTopOffset, iLeftOffset, cxMenuMax);
|
|
|
|
cyMenu = pMenu->cyMenu;
|
|
ThreadUnlockMenuNoModify(&tlpMenu);
|
|
|
|
return cyMenu;
|
|
}
|