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.
2485 lines
75 KiB
2485 lines
75 KiB
/**************************** Module Header ********************************\
|
|
* Module Name: mndraw.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* Menu Painting Routines
|
|
*
|
|
* History:
|
|
* 10-10-90 JimA Cleanup.
|
|
* 03-18-91 IanJa Window revalidation added
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
#define SRCSTENCIL 0x00B8074AL
|
|
#define MENU_STRLEN 255
|
|
|
|
/***************************************************************************\
|
|
* MNIsCachedBmpOnly
|
|
*
|
|
* 04/02/97 GerardoB Created
|
|
\***************************************************************************/
|
|
__inline BOOL MNIsCachedBmpOnly(
|
|
PITEM pItem)
|
|
{
|
|
return TestMFS(pItem, MFS_CACHEDBMP) && (pItem->lpstr == NULL);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* MNDrawHilite
|
|
*
|
|
* Don't draw the hilite if:
|
|
* The insertion bar is on (MFS_GAPDROP)
|
|
* or this is a cached bitmap (close, min, max, etc) with no text
|
|
*
|
|
* 08/12/96 GerardoB Ported From Memphis.
|
|
\***************************************************************************/
|
|
BOOL MNDrawHilite(
|
|
PITEM pItem)
|
|
{
|
|
return TestMFS(pItem, MFS_HILITE)
|
|
&& !TestMFS(pItem, MFS_GAPDROP)
|
|
&& !MNIsCachedBmpOnly(pItem);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* MNDrawMenu3DHotTracking
|
|
*
|
|
* 03/10/97 yutakas created
|
|
* 04/07/97 vadimg ported from Memphis
|
|
\***************************************************************************/
|
|
VOID MNDrawMenu3DHotTracking(
|
|
HDC hdc,
|
|
PMENU pMenu,
|
|
PITEM pItem)
|
|
{
|
|
HBRUSH hbrTopLeft, hbrBottomRight;
|
|
BOOL fDrawEdge;
|
|
|
|
UserAssertMsg0(!MNIsFlatMenu(), "3D Hot Tracking not valid for flat menus");
|
|
|
|
if (pItem->hbmp && TestMFS(pItem, MFS_CACHEDBMP)) {
|
|
return;
|
|
}
|
|
|
|
fDrawEdge = FALSE;
|
|
|
|
if (!TestMF(pMenu, MFISPOPUP)) {
|
|
if (TestMFS(pItem, MFS_HILITE)) {
|
|
hbrTopLeft = SYSHBR(3DSHADOW);
|
|
hbrBottomRight = SYSHBR(3DHILIGHT);
|
|
SetMFS(pItem, MFS_HOTTRACKDRAWN);
|
|
fDrawEdge = TRUE;
|
|
} else if (TestMFS(pItem, MFS_HOTTRACK)) {
|
|
hbrTopLeft = SYSHBR(3DHILIGHT);
|
|
hbrBottomRight = SYSHBR(3DSHADOW);
|
|
SetMFS(pItem, MFS_HOTTRACKDRAWN);
|
|
fDrawEdge = TRUE;
|
|
} else if (TestMFS(pItem, MFS_HOTTRACKDRAWN)) {
|
|
if (pMenu->hbrBack == NULL) {
|
|
hbrTopLeft = SYSHBR(MENU);
|
|
hbrBottomRight = SYSHBR(MENU);
|
|
} else {
|
|
hbrTopLeft = pMenu->hbrBack;
|
|
hbrBottomRight = pMenu->hbrBack;
|
|
}
|
|
|
|
ClearMFS(pItem, MFS_HOTTRACKDRAWN);
|
|
fDrawEdge = TRUE;
|
|
}
|
|
}
|
|
if (fDrawEdge) {
|
|
int x = pItem->xItem, y = pItem->yItem;
|
|
int cx = pItem->cxItem, cy = pItem->cyItem;
|
|
HBRUSH hbrOld = GreSelectBrush(hdc, hbrTopLeft);
|
|
GrePatBlt(hdc, x, y, cx - CXMENU3DEDGE, CYMENU3DEDGE, PATCOPY);
|
|
GrePatBlt(hdc, x, y, CXMENU3DEDGE, cy - CYMENU3DEDGE, PATCOPY);
|
|
GreSelectBrush(hdc, hbrBottomRight);
|
|
GrePatBlt(hdc, x, y + cy - CYMENU3DEDGE, cx - CXMENU3DEDGE, CYMENU3DEDGE, PATCOPY);
|
|
GrePatBlt(hdc, x + cx - CYMENU3DEDGE, y, CXMENU3DEDGE, cy, PATCOPY);
|
|
GreSelectBrush(hdc, hbrOld);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* MNDrawArrow
|
|
*
|
|
* Redraws the specified arrow (uArrow) in a scrollable menu (ppopup) to reflect
|
|
* its current state of enabled or disabled, drawing it in HOTLIGHT if fOn is
|
|
* TRUE.
|
|
*
|
|
* 08/12/96 GerardoB Ported From Memphis.
|
|
\***************************************************************************/
|
|
VOID MNDrawArrow(
|
|
HDC hdcIn,
|
|
PPOPUPMENU ppopup,
|
|
UINT uArrow)
|
|
{
|
|
PWND pwnd = ppopup->spwndPopupMenu;
|
|
HDC hdc;
|
|
int x, y;
|
|
DWORD dwBmp;
|
|
DWORD dwAtCheck;
|
|
DWORD dwState;
|
|
|
|
if (ppopup->spmenu->dwArrowsOn == MSA_OFF) {
|
|
return;
|
|
}
|
|
|
|
if (hdcIn == NULL) {
|
|
hdc = _GetDCEx(pwnd, NULL, DCX_USESTYLE | DCX_WINDOW | DCX_LOCKWINDOWUPDATE);
|
|
} else {
|
|
hdc = hdcIn;
|
|
}
|
|
|
|
x = SYSMET(CXFIXEDFRAME);
|
|
if (!TestMF(ppopup->spmenu, MNS_NOCHECK)) {
|
|
/*
|
|
* Win9x: x += MNByteAlignItem(oemInfo.bm[OBI_MENUCHECK].cx);
|
|
*/
|
|
x += gpsi->oembmi[OBI_MENUCHECK].cx;
|
|
} else {
|
|
x += SYSMET(CXEDGE) * 2;
|
|
}
|
|
|
|
if (uArrow == MFMWFP_UPARROW) {
|
|
y = SYSMET(CXFIXEDFRAME);
|
|
dwBmp = OBI_MENUARROWUP;
|
|
dwAtCheck = MSA_ATTOP;
|
|
dwState = DFCS_MENUARROWUP;
|
|
} else {
|
|
y = pwnd->rcWindow.bottom - pwnd->rcWindow.top - SYSMET(CYFIXEDFRAME) - gcyMenuScrollArrow;
|
|
dwBmp = OBI_MENUARROWDOWN;
|
|
dwAtCheck = MSA_ATBOTTOM;
|
|
dwState = DFCS_MENUARROWDOWN;
|
|
}
|
|
|
|
if (ppopup->spmenu->dwArrowsOn == dwAtCheck) {
|
|
/*
|
|
* go 2 ahead to inactive state bitmap
|
|
*/
|
|
dwBmp += 2;
|
|
dwState |= DFCS_INACTIVE;
|
|
}
|
|
|
|
if (ppopup->spmenu->hbrBack != NULL) {
|
|
/*
|
|
* For menus with background brushes, we can't do a straight blt
|
|
* of the scroll arrows 'cause the background wouldn't be right;
|
|
* need to call DrawFrameControl with DFCS_TRANSPARENT instead.
|
|
*/
|
|
RECT rc;
|
|
rc.top = y;
|
|
rc.left = x;
|
|
rc.right = x + gpsi->oembmi[OBI_MENUARROWUP].cx;
|
|
rc.bottom = y + gpsi->oembmi[OBI_MENUARROWUP].cy;
|
|
DrawFrameControl(hdc, &rc, DFC_MENU, dwState | DFCS_TRANSPARENT);
|
|
} else {
|
|
BitBltSysBmp(hdc, x, y, dwBmp);
|
|
BitBltSysBmp(hdc, x, y, dwBmp);
|
|
}
|
|
|
|
if (hdcIn == NULL) {
|
|
_ReleaseDC(hdc);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* DrawOutline
|
|
*
|
|
* Draws an outline of a specified thickness using a given brush.
|
|
*
|
|
* History:
|
|
* 03-03-00 JStall - Created
|
|
\***************************************************************************/
|
|
VOID DrawOutline(
|
|
HDC hdc,
|
|
int x,
|
|
int y,
|
|
int w,
|
|
int h,
|
|
int nThick,
|
|
HBRUSH hbrFill)
|
|
{
|
|
POLYPATBLT rgPPB[4];
|
|
|
|
/* Left */
|
|
rgPPB[0].x = x;
|
|
rgPPB[0].y = y;
|
|
rgPPB[0].cx = nThick;
|
|
rgPPB[0].cy = h;
|
|
rgPPB[0].BrClr.hbr = hbrFill;
|
|
|
|
/* Top */
|
|
rgPPB[1].x = x;
|
|
rgPPB[1].y = y;
|
|
rgPPB[1].cx = w;
|
|
rgPPB[1].cy = nThick;
|
|
rgPPB[1].BrClr.hbr = hbrFill;
|
|
|
|
/* Right */
|
|
rgPPB[2].x = x + w - nThick;
|
|
rgPPB[2].y = y;
|
|
rgPPB[2].cx = nThick;
|
|
rgPPB[2].cy = h;
|
|
rgPPB[2].BrClr.hbr = hbrFill;
|
|
|
|
/* Bottom */
|
|
rgPPB[3].x = x;
|
|
rgPPB[3].y = y + h - nThick;
|
|
rgPPB[3].cx = w;
|
|
rgPPB[3].cy = nThick;
|
|
rgPPB[3].BrClr.hbr = hbrFill;
|
|
|
|
GrePolyPatBlt(hdc, PATCOPY, rgPPB, 4, PPB_BRUSH);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* MNDrawEdge
|
|
*
|
|
* Draws the edge of the menu for flat-menus. The given rectangle can
|
|
* optionally be modified to exclude the drawn area.
|
|
*
|
|
* History:
|
|
* 03-03-00 JStall - Created
|
|
\***************************************************************************/
|
|
VOID MNDrawEdge(
|
|
PMENU pmenu,
|
|
HDC hdc,
|
|
RECT *prcDraw,
|
|
UINT nFlags)
|
|
{
|
|
int nWidth, nHeight, nBorder, nTemp;
|
|
HBRUSH hbr;
|
|
|
|
UserAssertMsg0(MNIsFlatMenu(), "Should only be called for flat menus");
|
|
|
|
nWidth = prcDraw->right - prcDraw->left;
|
|
nHeight = prcDraw->bottom - prcDraw->top;
|
|
nBorder = SYSMET(CXBORDER);
|
|
|
|
/*
|
|
* Draw the flat outline around the menu
|
|
*/
|
|
DrawOutline(hdc, 0, 0, nWidth, nHeight, nBorder, SYSHBR(BTNSHADOW));
|
|
|
|
/*
|
|
* Draw inside the menu
|
|
*/
|
|
nTemp = 2 * nBorder;
|
|
if (pmenu->hbrBack == NULL) {
|
|
hbr = SYSHBR(MENU);
|
|
} else {
|
|
hbr = pmenu->hbrBack;
|
|
}
|
|
|
|
DrawOutline(hdc, nBorder, nBorder, nWidth - nTemp, nHeight - nTemp, nTemp, hbr);
|
|
|
|
/*
|
|
* Adjust the given rectangle from the equivalent calls for 3D-menus
|
|
* when the non-client area is drawn by DefWindowProc. DrawEdge will
|
|
* inset the rectangle 2 times. (once for the inner and once for the
|
|
* outer) With the additional InflateRect() the rectangle gets
|
|
* insetted 3 times total.
|
|
*/
|
|
if (nFlags == BF_ADJUST) {
|
|
nTemp = -3 * nBorder;
|
|
InflateRect(prcDraw, nTemp, nTemp);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* MNDrawFullNC
|
|
*
|
|
* Performs the custom nonclient painting needed for scrollable menus.
|
|
* Assumes that the given menu is a scrollable menu.
|
|
*
|
|
* History:
|
|
* 08-14-96 GerardoB - Ported from Memphis
|
|
\***************************************************************************/
|
|
VOID MNDrawFullNC(
|
|
PWND pwnd,
|
|
HDC hdcIn,
|
|
PPOPUPMENU ppopup)
|
|
{
|
|
RECT rc;
|
|
HDC hdc;
|
|
HBRUSH hbrOld;
|
|
int yTop, yBottom;
|
|
POINT ptOrg;
|
|
|
|
if (hdcIn == NULL) {
|
|
hdc = _GetDCEx(pwnd, NULL, DCX_USESTYLE | DCX_WINDOW | DCX_LOCKWINDOWUPDATE);
|
|
} else {
|
|
hdc = hdcIn;
|
|
}
|
|
|
|
rc.left = rc.top = 0;
|
|
rc.right = pwnd->rcWindow.right - pwnd->rcWindow.left;
|
|
rc.bottom = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
|
|
|
|
if (MNIsFlatMenu()) {
|
|
MNDrawEdge(ppopup->spmenu, hdcIn, &rc, BF_ADJUST);
|
|
} else {
|
|
DrawEdge(hdc, &rc, EDGE_RAISED, (BF_RECT | BF_ADJUST));
|
|
DrawFrame(hdc, &rc, 1, DF_3DFACE);
|
|
InflateRect(&rc, -SYSMET(CXBORDER), -SYSMET(CYBORDER));
|
|
}
|
|
|
|
yTop = rc.top;
|
|
yBottom = rc.bottom - gcyMenuScrollArrow;
|
|
|
|
GreGetBrushOrg(hdc, &ptOrg);
|
|
if (ppopup->spmenu->hbrBack != NULL) {
|
|
GreSetBrushOrg(hdc, 0,
|
|
-(int)MNGetToppItem(ppopup->spmenu)->yItem, NULL);
|
|
hbrOld = GreSelectBrush(hdc, ppopup->spmenu->hbrBack);
|
|
} else {
|
|
hbrOld = GreSelectBrush(hdc, SYSHBR(MENU));
|
|
}
|
|
|
|
rc.right -= rc.left;
|
|
GrePatBlt(hdc, rc.left, yTop, rc.right, gcyMenuScrollArrow, PATCOPY);
|
|
MNDrawArrow(hdc, ppopup, MFMWFP_UPARROW);
|
|
GrePatBlt(hdc, rc.left, yBottom, rc.right, gcyMenuScrollArrow, PATCOPY);
|
|
MNDrawArrow(hdc, ppopup, MFMWFP_DOWNARROW);
|
|
|
|
GreSetBrushOrg(hdc, ptOrg.x, ptOrg.y, NULL);
|
|
GreSelectBrush(hdc, hbrOld);
|
|
|
|
if (hdcIn == NULL) {
|
|
_ReleaseDC(hdc);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MNEraseBackground
|
|
*
|
|
* Erases the background making sure that the background pattern (i.e, watermark)
|
|
* aligns with the pattern in the nonclient area.
|
|
*
|
|
* History:
|
|
* 08-23-96 GerardoB - Created
|
|
\***************************************************************************/
|
|
VOID MNEraseBackground(
|
|
HDC hdc,
|
|
PMENU pmenu,
|
|
int x,
|
|
int y,
|
|
int cx,
|
|
int cy)
|
|
{
|
|
BOOL fSetOrg;
|
|
HBRUSH hbrOld;
|
|
POINT ptOrg;
|
|
|
|
UserAssert(pmenu->hbrBack != NULL);
|
|
|
|
fSetOrg = TRUE;
|
|
GreGetBrushOrg(hdc, &ptOrg);
|
|
/*
|
|
* If we have scrollbars
|
|
*/
|
|
if (pmenu->dwArrowsOn != MSA_OFF) {
|
|
/*
|
|
* If not drawing on the client area only
|
|
*/
|
|
if (TestMF(pmenu, MFWINDOWDC)) {
|
|
ptOrg.x = 0;
|
|
ptOrg.y = -(int)MNGetToppItem(pmenu)->yItem;
|
|
} else {
|
|
ptOrg.x = -MNXBORDER;
|
|
ptOrg.y = -MNYBORDER - gcyMenuScrollArrow - MNGetToppItem(pmenu)->yItem;
|
|
}
|
|
} else {
|
|
if (TestMF(pmenu, MFWINDOWDC)) {
|
|
ptOrg.x = MNXBORDER;
|
|
ptOrg.y = MNYBORDER;
|
|
} else {
|
|
fSetOrg = FALSE;
|
|
}
|
|
}
|
|
|
|
if (fSetOrg) {
|
|
GreSetBrushOrg(hdc, ptOrg.x, ptOrg.y, &ptOrg);
|
|
}
|
|
hbrOld = GreSelectBrush(hdc, pmenu->hbrBack);
|
|
|
|
GrePatBlt(hdc, x, y, cx, cy, PATCOPY);
|
|
|
|
if (fSetOrg) {
|
|
GreSetBrushOrg(hdc, ptOrg.x, ptOrg.y, NULL);
|
|
}
|
|
GreSelectBrush(hdc, hbrOld);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MNAnimate
|
|
*
|
|
* If fIterate is TRUE, then perform the next iteration in the menu animation
|
|
* sequence. If fIterate is FALSE, terminate the animation sequence.
|
|
*
|
|
* History:
|
|
* 07-23-96 GerardoB - fixed up for 5.0
|
|
\***************************************************************************/
|
|
VOID MNAnimate(
|
|
PMENUSTATE pMenuState,
|
|
BOOL fIterate)
|
|
{
|
|
DWORD dwTimeElapsed;
|
|
int x, y, xOff, yOff, xLast, yLast;
|
|
|
|
if (TestFadeFlags(FADE_MENU)) {
|
|
if (!fIterate) {
|
|
StopFade();
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If we're not animating, bail
|
|
*/
|
|
if (pMenuState->hdcWndAni == NULL) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* The active popup must be visible. It's supposed to be the
|
|
* window we're animating
|
|
*/
|
|
UserAssert(TestWF(pMenuState->pGlobalPopupMenu->spwndActivePopup, WFVISIBLE));
|
|
|
|
/*
|
|
* End animation if asked to do so, if it's taking too long
|
|
* or someone is waiting for the critical section
|
|
*/
|
|
dwTimeElapsed = NtGetTickCount() - pMenuState->dwAniStartTime;
|
|
if (!fIterate
|
|
|| (dwTimeElapsed > CMS_QANIMATION)
|
|
|| (ExGetExclusiveWaiterCount(gpresUser) > 0)
|
|
|| (ExGetSharedWaiterCount(gpresUser) > 0)) {
|
|
|
|
GreBitBlt(pMenuState->hdcWndAni, 0, 0, pMenuState->cxAni, pMenuState->cyAni, pMenuState->hdcAni,
|
|
0, 0, SRCCOPY | NOMIRRORBITMAP, 0xFFFFFF);
|
|
|
|
goto AnimationCompleted;
|
|
}
|
|
|
|
/*
|
|
* Remember current animation point and calculate new one
|
|
*/
|
|
xLast = pMenuState->ixAni;
|
|
yLast = pMenuState->iyAni;
|
|
if (pMenuState->iAniDropDir & PAS_HORZ) {
|
|
pMenuState->ixAni = MultDiv(gcxMenuFontChar, dwTimeElapsed, CMS_QANIMATION / 20);
|
|
if (pMenuState->ixAni > pMenuState->cxAni) {
|
|
pMenuState->ixAni = pMenuState->cxAni;
|
|
}
|
|
}
|
|
|
|
if (pMenuState->iAniDropDir & PAS_VERT) {
|
|
pMenuState->iyAni = MultDiv(gcyMenuFontChar, dwTimeElapsed, CMS_QANIMATION / 10);
|
|
if (pMenuState->iyAni > pMenuState->cyAni) {
|
|
pMenuState->iyAni = pMenuState->cyAni;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* if no change -- bail out
|
|
*/
|
|
if ((pMenuState->ixAni == xLast) && (pMenuState->iyAni == yLast)) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Calculate source and dest coordinates
|
|
*/
|
|
if (pMenuState->iAniDropDir & PAS_LEFT) {
|
|
x = pMenuState->cxAni - pMenuState->ixAni;
|
|
xOff = 0;
|
|
} else {
|
|
xOff = pMenuState->cxAni - pMenuState->ixAni;
|
|
x = 0;
|
|
}
|
|
|
|
if (pMenuState->iAniDropDir & PAS_UP) {
|
|
y = pMenuState->cyAni - pMenuState->iyAni;
|
|
yOff = 0;
|
|
} else {
|
|
yOff = pMenuState->cyAni - pMenuState->iyAni;
|
|
y = 0;
|
|
}
|
|
|
|
/*
|
|
* Do it
|
|
*/
|
|
GreBitBlt(pMenuState->hdcWndAni, x, y, pMenuState->ixAni, pMenuState->iyAni,
|
|
pMenuState->hdcAni, xOff, yOff, SRCCOPY | NOMIRRORBITMAP, 0xFFFFFF);
|
|
|
|
/*
|
|
* Check if we're done
|
|
*/
|
|
if ((pMenuState->cxAni == pMenuState->ixAni)
|
|
&& (pMenuState->cyAni == pMenuState->iyAni)) {
|
|
|
|
AnimationCompleted:
|
|
|
|
MNDestroyAnimationBitmap(pMenuState);
|
|
_ReleaseDC(pMenuState->hdcWndAni);
|
|
pMenuState->hdcWndAni = NULL;
|
|
_KillTimer(pMenuState->pGlobalPopupMenu->spwndActivePopup, IDSYS_MNANIMATE);
|
|
}
|
|
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrawMenuItemCheckMark() -
|
|
*
|
|
* Draws the proper check mark for the given item. Note that ownerdraw
|
|
* items should NOT be passed to this procedure, otherwise we'd draw a
|
|
* checkmark for them when they are already going to take care of it.
|
|
*
|
|
* Returns TRUE is a bitmap was drawn (or at least we attempted to draw it).
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
BOOL DrawMenuItemCheckMark(
|
|
HDC hdc,
|
|
PITEM pItem,
|
|
int xPos)
|
|
{
|
|
int yCenter;
|
|
HBITMAP hbm;
|
|
DWORD textColorSave;
|
|
DWORD bkColorSave;
|
|
BOOL fChecked;
|
|
POEMBITMAPINFO pOem;
|
|
BOOL fRet = TRUE;
|
|
DWORD dwFlags = BC_INVERT;
|
|
|
|
UserAssert(hdc != ghdcMem2);
|
|
pOem = gpsi->oembmi + OBI_MENUCHECK;
|
|
yCenter = pItem->cyItem - pOem->cy;
|
|
if (yCenter < 0)
|
|
yCenter = 0;
|
|
yCenter /= 2;
|
|
|
|
fChecked = TestMFS(pItem, MFS_CHECKED);
|
|
|
|
if (hbm = (fChecked) ? pItem->hbmpChecked : pItem->hbmpUnchecked) {
|
|
HBITMAP hbmSave;
|
|
|
|
// Use the app supplied bitmaps.
|
|
if (hbmSave = GreSelectBitmap(ghdcMem2, hbm)) {
|
|
|
|
textColorSave = GreSetTextColor(hdc, 0x00000000L);
|
|
bkColorSave = GreSetBkColor (hdc, 0x00FFFFFFL);
|
|
|
|
if (TestMFT(pItem, MFT_RIGHTORDER))
|
|
xPos = pItem->cxItem - pOem->cx;
|
|
|
|
GreBitBlt(hdc,
|
|
xPos,
|
|
yCenter,
|
|
pOem->cx,
|
|
pOem->cy,
|
|
ghdcMem2,
|
|
0,
|
|
0,
|
|
SRCSTENCIL,
|
|
0x00FFFFFF);
|
|
|
|
GreSetTextColor(hdc, textColorSave);
|
|
GreSetBkColor(hdc, bkColorSave);
|
|
|
|
GreSelectBitmap(ghdcMem2, hbmSave);
|
|
}
|
|
|
|
} else if (fChecked) {
|
|
|
|
if (TestMFT(pItem, MFT_RADIOCHECK))
|
|
pOem = gpsi->oembmi + OBI_MENUBULLET;
|
|
|
|
if (TestMFT(pItem, MFT_RIGHTORDER))
|
|
xPos = pItem->cxItem - pOem->cx;
|
|
|
|
// 389917: Mirror active menu's check mark if hdc is mirroed.
|
|
if ((GreGetLayout(hdc) & LAYOUT_RTL) && (hdc != gpDispInfo->hdcGray)) {
|
|
dwFlags |= BC_NOMIRROR;
|
|
}
|
|
BltColor(hdc,
|
|
NULL,
|
|
HDCBITS(),
|
|
xPos,
|
|
yCenter,
|
|
pOem->cx,
|
|
pOem->cy,
|
|
pOem->x,
|
|
pOem->y,
|
|
dwFlags);
|
|
} else {
|
|
fRet = FALSE;
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxDrawItemtUnderline
|
|
*
|
|
* Draws or hides an underline for a menu item
|
|
*
|
|
* 07/23/96 vadimg separated into a separate routine
|
|
\***************************************************************************/
|
|
VOID xxxDrawItemUnderline(
|
|
PITEM pItem,
|
|
HDC hdc,
|
|
int xLeft,
|
|
int yTop,
|
|
LPWSTR pszMenu,
|
|
LONG lResLo)
|
|
{
|
|
int cx;
|
|
PTHREADINFO ptiCurrent = PtiCurrentShared();
|
|
|
|
//
|
|
// LOWORD of result is 0xFFFF if there is no underlined character.
|
|
// Therefore ulX must be valid or be UNDERLINE_RECALC because the item
|
|
// or menu mode changed.
|
|
//
|
|
// Bail out if there isn't one.
|
|
//
|
|
if (lResLo == 0xFFFF) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// For proportional fonts, or if an LPK is installed, find starting
|
|
// point of underline.
|
|
//
|
|
if ((pItem->ulX == UNDERLINE_RECALC) || (PpiCurrent()->dwLpkEntryPoints & LPK_INSTALLED)) {
|
|
if (lResLo != 0) {
|
|
SIZE size;
|
|
|
|
if (CALL_LPK(ptiCurrent)) {
|
|
xxxClientGetTextExtentPointW(hdc, pszMenu, lResLo, &size);
|
|
} else {
|
|
GreGetTextExtentW(hdc, pszMenu, lResLo, &size, GGTE_WIN3_EXTENT);
|
|
}
|
|
pItem->ulX = size.cx - gcxMenuFontOverhang;
|
|
} else
|
|
pItem->ulX = 0;
|
|
}
|
|
|
|
xLeft += pItem->ulX;
|
|
|
|
//
|
|
// Adjust for proportional font when setting the length of the underline
|
|
// and height of text.
|
|
//
|
|
// Calculate underline width.
|
|
if (!pItem->ulWidth) {
|
|
SIZE size;
|
|
|
|
if (CALL_LPK(ptiCurrent)) {
|
|
xxxClientGetTextExtentPointW(hdc, pszMenu + lResLo, 1, &size);
|
|
} else {
|
|
GreGetTextExtentW(hdc, pszMenu + lResLo, 1, &size, GGTE_WIN3_EXTENT);
|
|
}
|
|
pItem->ulWidth = size.cx - gcxMenuFontOverhang;
|
|
}
|
|
cx = pItem->ulWidth;
|
|
|
|
// Get ascent of text (units above baseline) so that underline can be drawn
|
|
// below the text
|
|
yTop += gcyMenuFontAscent;
|
|
|
|
// Proper brush should be selected into dc.
|
|
GrePatBlt(hdc, xLeft, yTop, pItem->ulWidth, SYSMET(CYBORDER), PATCOPY);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxDrawMenuItemText
|
|
*
|
|
* Draws menu text with underline.
|
|
\***************************************************************************/
|
|
VOID xxxDrawMenuItemText(
|
|
PITEM pItem,
|
|
HDC hdc,
|
|
int xLeft,
|
|
int yTop,
|
|
LPWSTR lpsz,
|
|
int cch,
|
|
BOOL fShowUnderlines)
|
|
{
|
|
LONG result;
|
|
WCHAR szMenu[MENU_STRLEN], *pchOut;
|
|
PTHREADINFO ptiCurrent = PtiCurrentShared();
|
|
TL tl;
|
|
|
|
if (cch > MENU_STRLEN) {
|
|
pchOut = (WCHAR*)UserAllocPool((cch+1) * sizeof(WCHAR), TAG_RTL);
|
|
if (pchOut == NULL)
|
|
return;
|
|
ThreadLockPool(ptiCurrent, pchOut, &tl);
|
|
} else {
|
|
pchOut = szMenu;
|
|
}
|
|
result = GetPrefixCount(lpsz, cch, pchOut, cch);
|
|
|
|
if (CALL_LPK(ptiCurrent)) {
|
|
xxxClientExtTextOutW(hdc, xLeft, yTop, 0, NULL, pchOut, cch - HIWORD(result), NULL);
|
|
} else {
|
|
GreExtTextOutW(hdc, xLeft, yTop, 0, NULL, pchOut, cch - HIWORD(result), NULL);
|
|
}
|
|
|
|
if (fShowUnderlines || TEST_BOOL_ACCF(ACCF_KEYBOARDPREF) || TestEffectInvertUP(KEYBOARDCUES)
|
|
|| (GetAppCompatFlags2(VER40) & GACF2_KCOFF)) {
|
|
if (CALL_LPK(ptiCurrent)) {
|
|
xxxPSMTextOut(hdc, xLeft, yTop, lpsz, cch, DT_PREFIXONLY);
|
|
} else{
|
|
xxxDrawItemUnderline(pItem, hdc, xLeft, yTop, pchOut, LOWORD(result));
|
|
}
|
|
}
|
|
if (pchOut != szMenu) {
|
|
ThreadUnlockAndFreePool(ptiCurrent, &tl);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxSendMenuDrawItemMessage
|
|
*
|
|
* Sends a WM_DRAWITEM message to the owner of the menu (pMenuState->hwndMenu).
|
|
* All state is determined in this routine so HILITE state must be properly
|
|
* set before entering this routine..
|
|
*
|
|
* Revalidation notes:
|
|
* This routine must be called with a valid and non-NULL pwnd.
|
|
* Revalidation is not required in this routine: no windows are used after
|
|
* potentially leaving the critsect.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
VOID xxxSendMenuDrawItemMessage(
|
|
HDC hdc,
|
|
UINT itemAction,
|
|
PMENU pmenu,
|
|
PITEM pItem,
|
|
BOOL fBitmap,
|
|
int iOffset)
|
|
{
|
|
DRAWITEMSTRUCT dis;
|
|
TL tlpwndNotify;
|
|
int y;
|
|
|
|
CheckLock(pmenu);
|
|
|
|
dis.CtlType = ODT_MENU;
|
|
dis.CtlID = 0;
|
|
|
|
dis.itemID = pItem->wID;
|
|
|
|
dis.itemAction = itemAction;
|
|
dis.itemState =
|
|
((pItem->fState & MF_GRAYED) ? ODS_GRAYED : 0) |
|
|
((pItem->fState & MFS_DEFAULT) ? ODS_DEFAULT : 0) |
|
|
((pItem->fState & MFS_CHECKED) ? ODS_CHECKED : 0) |
|
|
((pItem->fState & MFS_DISABLED) ? ODS_DISABLED : 0) |
|
|
(MNDrawHilite(pItem) ? ODS_SELECTED : 0) |
|
|
((pItem->fState & MFS_HOTTRACK) ? ODS_HOTLIGHT : 0) |
|
|
(TestMF(pmenu, MFINACTIVE) ? ODS_INACTIVE : 0) |
|
|
(!TestMF(pmenu, MFUNDERLINE) ? ODS_NOACCEL : 0);
|
|
|
|
dis.hwndItem = (HWND)PtoH(pmenu);
|
|
dis.hDC = hdc;
|
|
|
|
y = pItem->yItem;
|
|
if (fBitmap) {
|
|
y = (pItem->cyItem - pItem->cyBmp) / 2;
|
|
}
|
|
|
|
dis.rcItem.left = iOffset + pItem->xItem;
|
|
dis.rcItem.top = y;
|
|
dis.rcItem.right = iOffset + pItem->xItem + (fBitmap ? pItem->cxBmp : pItem->cxItem);
|
|
dis.rcItem.bottom = y + (fBitmap ? pItem->cyBmp : pItem->cyItem);
|
|
dis.itemData = pItem->dwItemData;
|
|
|
|
if (pmenu->spwndNotify != NULL) {
|
|
ThreadLockAlways(pmenu->spwndNotify, &tlpwndNotify);
|
|
xxxSendMessage(pmenu->spwndNotify, WM_DRAWITEM, 0, (LPARAM)&dis);
|
|
ThreadUnlock(&tlpwndNotify);
|
|
}
|
|
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* CalcbfExtra
|
|
*
|
|
* History:
|
|
* 08-09-96 GerardoB Made into an inline function (code from xxxMenuDraw)
|
|
\***************************************************************************/
|
|
__inline UINT CalcbfExtra(
|
|
VOID)
|
|
{
|
|
if (SYSRGB(3DHILIGHT) == SYSRGB(MENU) && SYSRGB(3DSHADOW) == SYSRGB(MENU)) {
|
|
return BF_FLAT | BF_MONO;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MNDrawInsertionBar
|
|
*
|
|
* History:
|
|
* 11/21/96 GerardoB Created
|
|
\***************************************************************************/
|
|
VOID MNDrawInsertionBar(
|
|
HDC hdc,
|
|
PITEM pItem)
|
|
{
|
|
BOOL fTop;
|
|
POLYPATBLT PolyData [3], *ppd;
|
|
|
|
/*
|
|
* If no insertion bar for this item, bail
|
|
*/
|
|
fTop = TestMFS(pItem, MFS_TOPGAPDROP);
|
|
if (!fTop && !TestMFS(pItem, (MFS_BOTTOMGAPDROP))) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Vertical line on the left
|
|
*/
|
|
ppd = PolyData;
|
|
ppd->x = pItem->xItem + SYSMET(CXDRAG);
|
|
ppd->cx = SYSMET(CXDRAG);
|
|
ppd->cy = SYSMET(CYDRAG);
|
|
if (fTop) {
|
|
ppd->y = pItem->yItem;
|
|
} else {
|
|
ppd->y = pItem->yItem + pItem->cyItem - ppd->cy;
|
|
}
|
|
ppd->BrClr.hbr = SYSHBR(HIGHLIGHT);
|
|
|
|
/*
|
|
* Horizontal line in the middle
|
|
*/
|
|
ppd++;
|
|
ppd->x = pItem->xItem + (2 * SYSMET(CXDRAG));
|
|
ppd->cx = pItem->cxItem - (4 * SYSMET(CXDRAG));
|
|
ppd->cy = SYSMET(CYDRAG) / 2;
|
|
if (fTop) {
|
|
ppd->y = pItem->yItem;
|
|
} else {
|
|
ppd->y = pItem->yItem + pItem->cyItem - ppd->cy;
|
|
}
|
|
ppd->BrClr.hbr = PolyData->BrClr.hbr;
|
|
|
|
/*
|
|
* Vertical line on the right
|
|
*/
|
|
ppd++;
|
|
ppd->x = pItem->xItem + pItem->cxItem - (2 * SYSMET(CXDRAG));
|
|
ppd->cx = PolyData->cx;
|
|
ppd->cy = PolyData->cy;
|
|
ppd->y = PolyData->y;
|
|
ppd->BrClr.hbr = PolyData->BrClr.hbr;
|
|
|
|
GrePolyPatBlt(hdc, PATCOPY, PolyData, 3, PPB_BRUSH);
|
|
|
|
}
|
|
/***************************************************************************\
|
|
* xxxDrawMenuItem
|
|
*
|
|
* !
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
VOID xxxDrawMenuItem(
|
|
HDC hdc,
|
|
PMENU pMenu,
|
|
PITEM pItem,
|
|
DWORD dwFlags)
|
|
{
|
|
BOOL fHilite;
|
|
BOOL fFlatMenuBar;
|
|
BOOL fFlatMenu;
|
|
HFONT hfnOld;
|
|
int tcExtra;
|
|
UINT uFlags;
|
|
int iBkSave;
|
|
hfnOld = NULL;
|
|
uFlags = DST_COMPLEX;
|
|
|
|
|
|
CheckLock(pMenu);
|
|
|
|
fFlatMenu = MNIsFlatMenu();
|
|
|
|
/*
|
|
* If the insertion bar is on (MFS_GAPDROP), don't draw the item hilited
|
|
*/
|
|
if (fFlatMenu) {
|
|
fHilite = MNDrawHilite(pItem) || TestMFS(pItem, MFS_HOTTRACK) || TestMFS(pItem, MFS_HOTTRACKDRAWN);
|
|
fFlatMenuBar = !TestMF(pMenu, MFISPOPUP);
|
|
} else {
|
|
fHilite = MNDrawHilite(pItem);
|
|
fFlatMenuBar = FALSE;
|
|
}
|
|
|
|
|
|
if (TestMFS(pItem, MFS_DEFAULT))
|
|
{
|
|
if (ghMenuFontDef != NULL)
|
|
hfnOld = GreSelectFont(hdc, ghMenuFontDef);
|
|
else
|
|
{
|
|
uFlags |= DSS_DEFAULT;
|
|
tcExtra = GreGetTextCharacterExtra(hdc);
|
|
GreSetTextCharacterExtra(hdc, tcExtra + 1 + (gcxMenuFontChar / gpsi->cxSysFontChar));
|
|
}
|
|
}
|
|
|
|
if (TestMFT(pItem, MFT_OWNERDRAW)) {
|
|
|
|
/*
|
|
* If ownerdraw, just set the default menu colors since the app is
|
|
* responsible for handling the rest.
|
|
*/
|
|
GreSetTextColor(hdc, SYSRGB(MENUTEXT));
|
|
GreSetBkColor(hdc, fFlatMenuBar ? SYSRGB(MENUBAR) : SYSRGB(MENU));
|
|
|
|
/*
|
|
* Send drawitem message since this is an ownerdraw item.
|
|
*/
|
|
xxxSendMenuDrawItemMessage(hdc,
|
|
(UINT)((dwFlags & DMI_INVERT) ? ODA_SELECT : ODA_DRAWENTIRE),
|
|
pMenu, pItem,FALSE,0);
|
|
|
|
// Draw the hierarchical arrow for the cascade menu.
|
|
if (TestMF(pMenu, MFISPOPUP) && (pItem->spSubMenu != NULL))
|
|
{
|
|
POEMBITMAPINFO pOem;
|
|
HBRUSH hbr = fHilite ? SYSHBR(HIGHLIGHTTEXT) : SYSHBR(MENUTEXT);
|
|
|
|
pOem = gpsi->oembmi + (TestMFT(pItem, MFT_RIGHTORDER)
|
|
? OBI_MENUARROW_L : OBI_MENUARROW);
|
|
|
|
// This item has a hierarchical popup associated with it. Draw the
|
|
// bitmap dealy to signify its presence. Note we check if fPopup is set
|
|
// so that this isn't drawn for toplevel menus that have popups.
|
|
|
|
BltColor(hdc,
|
|
hbr,
|
|
HDCBITS(),
|
|
TestMFT(pItem, MFT_RIGHTORDER)
|
|
? pItem->xItem + pOem->cx :
|
|
pItem->xItem + pItem->cxItem - pOem->cx,
|
|
pItem->yItem + max((INT)(pItem->cyItem - 2 - pOem->cy) / 2,
|
|
0),
|
|
pOem->cx,
|
|
pOem->cy,
|
|
pOem->x,
|
|
pOem->y,
|
|
BC_INVERT);
|
|
}
|
|
} else {
|
|
COLORREF crBack;
|
|
COLORREF crFore;
|
|
GRAYMENU gm;
|
|
|
|
HBRUSH hbrBack;
|
|
HBRUSH hbrFore;
|
|
|
|
//
|
|
// Setup colors and state
|
|
//
|
|
if (fHilite) {
|
|
if (fFlatMenu) {
|
|
crBack = SYSRGB(MENUHILIGHT);
|
|
hbrBack = SYSHBR(MENUHILIGHT);
|
|
crFore = SYSRGB(HIGHLIGHTTEXT);
|
|
hbrFore = SYSHBR(HIGHLIGHTTEXT);
|
|
} else {
|
|
/*
|
|
* If we are not using flat menu's, only draw the highlight if
|
|
* the item is a popup menu. If it is in the menubar, don't
|
|
* hilight it since it will be beveled.
|
|
*/
|
|
if (TestMF(pMenu, MFISPOPUP)) {
|
|
crBack = SYSRGB(HIGHLIGHT);
|
|
hbrBack = SYSHBR(HIGHLIGHT);
|
|
crFore = SYSRGB(HIGHLIGHTTEXT);
|
|
hbrFore = SYSHBR(HIGHLIGHTTEXT);
|
|
} else {
|
|
goto NormalMenuItem;
|
|
}
|
|
}
|
|
} else if (fFlatMenuBar) {
|
|
UserAssertMsg0(fFlatMenu, "fFlatMenuBar should only be set for flat menus");
|
|
|
|
crFore = SYSRGB(MENUTEXT);
|
|
hbrFore = SYSHBR(MENUTEXT);
|
|
if (pMenu->hbrBack != NULL) {
|
|
crBack = SYSRGB(MENUBAR);
|
|
hbrBack = pMenu->hbrBack;
|
|
} else {
|
|
crBack = SYSRGB(MENUBAR);
|
|
hbrBack = SYSHBR(MENUBAR);
|
|
}
|
|
} else {
|
|
NormalMenuItem:
|
|
crBack = SYSRGB(MENU);
|
|
hbrBack = SYSHBR(MENU);
|
|
crFore = SYSRGB(MENUTEXT);
|
|
hbrFore = SYSHBR(MENUTEXT);
|
|
}
|
|
|
|
// B#4157 - Lotus doesn't like it if we draw
|
|
// its disabled menu items in GRAY, t-arthb
|
|
// MAKE SURE MF_GRAYED stays 0x0001 NOT 0x0003 for this to fix
|
|
|
|
/*
|
|
* System bitmaps are already grayed so don't draw them disabled
|
|
* if the menu is inactive
|
|
*/
|
|
if (!MNIsCachedBmpOnly(pItem)
|
|
&& (TestMFS(pItem, MF_GRAYED) || TestMF(pMenu, MFINACTIVE))) {
|
|
//
|
|
// Only do embossing if menu color is same as 3D color. The
|
|
// emboss uses 3D hilight & 3D shadow, which doesn't look cool
|
|
// on a different background.
|
|
//
|
|
if ((fHilite) ||
|
|
(crBack != SYSRGB(3DFACE)) || SYSMET(SLOWMACHINE)) {
|
|
//
|
|
// If gray text won't show up on background, then dither.
|
|
//
|
|
if (SYSRGB(GRAYTEXT) == crBack) {
|
|
uFlags |= DSS_UNION;
|
|
} else {
|
|
crFore = SYSRGB(GRAYTEXT);
|
|
hbrFore = SYSHBR(GRAYTEXT);
|
|
}
|
|
} else {
|
|
if ((SYSRGB(3DSHADOW) == crBack) && (SYSRGB(3DHILIGHT) == crBack)) {
|
|
uFlags |= DSS_UNION;
|
|
} else {
|
|
uFlags |= TestMF(pMenu, MFINACTIVE) ? DSS_INACTIVE : DSS_DISABLED;
|
|
}
|
|
}
|
|
}
|
|
|
|
GreSetBkColor(hdc, crBack);
|
|
GreSetTextColor(hdc, crFore);
|
|
if (((dwFlags & DMI_INVERT) && (pMenu->hbrBack == NULL))
|
|
|| fHilite || fFlatMenuBar) {
|
|
|
|
POLYPATBLT PolyData;
|
|
|
|
/*
|
|
* Only fill the background if we're being called on behalf of
|
|
* MNInvertItem. This is so that we don't waste time filling
|
|
* the unselected rect when the menu is first pulled down.
|
|
* If the menu has a background brush and we were called by
|
|
* MNInvertItem, that function will have already taken care of
|
|
* filling the background
|
|
*/
|
|
|
|
PolyData.x = pItem->xItem;
|
|
PolyData.y = pItem->yItem;
|
|
PolyData.cx = pItem->cxItem;
|
|
PolyData.cy = pItem->cyItem;
|
|
PolyData.BrClr.hbr = hbrBack;
|
|
|
|
GrePolyPatBlt(hdc, PATCOPY, &PolyData, 1, PPB_BRUSH);
|
|
|
|
if (fHilite && fFlatMenu) {
|
|
DrawOutline(hdc, pItem->xItem, pItem->yItem,
|
|
pItem->cxItem, pItem->cyItem, 1, SYSHBR(HIGHLIGHT));
|
|
}
|
|
}
|
|
|
|
if (pMenu->hbrBack != NULL) {
|
|
iBkSave = GreSetBkMode(hdc, TRANSPARENT);
|
|
}
|
|
GreSelectBrush(hdc, hbrFore);
|
|
|
|
//
|
|
// Draw the image
|
|
//
|
|
gm.pItem = pItem;
|
|
gm.pMenu = pMenu;
|
|
|
|
xxxDrawState(hdc,
|
|
hbrFore,
|
|
(LPARAM)(PGRAYMENU)&gm,
|
|
pItem->xItem,
|
|
pItem->yItem,
|
|
pItem->cxItem,
|
|
pItem->cyItem,
|
|
uFlags);
|
|
|
|
if (! fFlatMenu) {
|
|
MNDrawMenu3DHotTracking(hdc, pMenu, pItem);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Draw the drop insertion bar, if any
|
|
*/
|
|
MNDrawInsertionBar (hdc, pItem);
|
|
|
|
|
|
if (pMenu->hbrBack != NULL)
|
|
GreSetBkMode(hdc, iBkSave);
|
|
|
|
if (TestMFS(pItem, MFS_DEFAULT))
|
|
{
|
|
if (hfnOld)
|
|
GreSelectFont(hdc, hfnOld);
|
|
else
|
|
GreSetTextCharacterExtra(hdc, tcExtra);
|
|
}
|
|
}
|
|
|
|
extern void SetupFakeMDIAppStuff(PMENU lpMenu, PITEM lpItem);
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* xxxRealDrawMenuItem()
|
|
*
|
|
* Callback from xxxDrawState() to draw the menu item, either normally or into
|
|
* an offscreen bitmp. We don't know where we're drawing, and shouldn't
|
|
* have to.
|
|
*
|
|
\***************************************************************************/
|
|
BOOL CALLBACK xxxRealDrawMenuItem(
|
|
HDC hdc,
|
|
PGRAYMENU pGray,
|
|
int cx,
|
|
int cy)
|
|
{
|
|
PMENU pMenu;
|
|
PITEM pItem;
|
|
BOOL fPopup;
|
|
int cch;
|
|
int xLeft;
|
|
int yTop;
|
|
int tp;
|
|
int rp;
|
|
LPWSTR lpsz;
|
|
int cyTemp;
|
|
int xHilite = 0;
|
|
int yHilite = 0;
|
|
TL tlpwndChild;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
BOOL fCheckDrawn = FALSE;
|
|
int xFarLeft;
|
|
//
|
|
// BOGUS
|
|
// Use cx and cy instead of lpItem->cxItem, lpItem->cyItem to
|
|
// speed stuff up.
|
|
//
|
|
pMenu = pGray->pMenu;
|
|
CheckLock(pMenu);
|
|
pItem = pGray->pItem;
|
|
fPopup = TestMF(pMenu, MFISPOPUP);
|
|
|
|
if (fPopup) {
|
|
xLeft = MNLEFTMARGIN;
|
|
if (TestMF(pMenu, MNS_NOCHECK)) {
|
|
xLeft += MNXSPACE;
|
|
} else {
|
|
fCheckDrawn = DrawMenuItemCheckMark(hdc, pItem, xLeft);
|
|
if (!TestMF(pMenu, MNS_CHECKORBMP)
|
|
|| ((pItem->hbmp == NULL) || fCheckDrawn)) {
|
|
|
|
xLeft += TestMFT(pItem, MFT_RIGHTORDER)
|
|
? 0 : (gpsi->oembmi[OBI_MENUCHECK].cx + MNXSPACE);
|
|
}
|
|
}
|
|
} else {
|
|
xLeft = 0;
|
|
|
|
/*
|
|
* If not drawing flat menus in the menubar, "depress" the menu item
|
|
* when it becomes highlighted.
|
|
*/
|
|
if ((! MNIsFlatMenu()) && TestMFS(pItem, MFS_HILITE)) {
|
|
xHilite = CXMENU3DEDGE;
|
|
yHilite = CYMENU3DEDGE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If there is not bitmap or we don't to draw it, go draw the text
|
|
*/
|
|
if ((pItem->hbmp == NULL)
|
|
|| (fCheckDrawn
|
|
&& TestMF(pMenu, MNS_CHECKORBMP))) {
|
|
goto RealDrawMenuItemText;
|
|
}
|
|
|
|
/*
|
|
* Draw the bitmap
|
|
*/
|
|
if (TestMFS(pItem, MFS_CACHEDBMP)) {
|
|
if (pItem->hbmp == HBMMENU_SYSTEM) {
|
|
/*
|
|
* Drawing app icon (system menu)
|
|
*/
|
|
PWND pwndChild;
|
|
PICON pIcon = NULL;
|
|
UINT cyUse, cxUse;
|
|
|
|
AintNothingLikeTheRealMDIThing:
|
|
if (!(pItem->dwItemData))
|
|
SetupFakeMDIAppStuff(pMenu, pItem);
|
|
|
|
pwndChild = HMValidateHandleNoRip((HWND)(pItem->dwItemData),TYPE_WINDOW);
|
|
if (!pwndChild)
|
|
{
|
|
//
|
|
// Oops, child window isn't valid anymore. Go find
|
|
// the new one.
|
|
//
|
|
if (pItem->dwItemData)
|
|
{
|
|
pItem->dwItemData = 0;
|
|
goto AintNothingLikeTheRealMDIThing;
|
|
}
|
|
|
|
pIcon = NULL;
|
|
}
|
|
else {
|
|
ThreadLock(pwndChild, &tlpwndChild);
|
|
pIcon = xxxGetWindowSmIcon(pwndChild, FALSE);
|
|
ThreadUnlock(&tlpwndChild);
|
|
}
|
|
|
|
|
|
if (!pIcon)
|
|
pIcon = SYSICO(WINLOGO);
|
|
|
|
cyUse = cy - SYSMET(CYEDGE);
|
|
cxUse = cx - (SYSMET(CXEDGE) * 2);
|
|
/*
|
|
* If this is a popup, make sure that no weird
|
|
* width/height stretch takes place.
|
|
*/
|
|
if (fPopup && (cyUse < cxUse)) {
|
|
cxUse = cyUse;
|
|
}
|
|
|
|
_DrawIconEx(hdc, xLeft + (SYSMET(CXEDGE) * 2),
|
|
SYSMET(CYBORDER), pIcon, cxUse,
|
|
cyUse, 0, SYSHBR(MENU), DI_NORMAL | DI_NOMIRROR);
|
|
|
|
} else {
|
|
/*
|
|
* This is a cached bitmap
|
|
*/
|
|
UINT wBmp;
|
|
int xBmpLeft = xLeft;
|
|
int y;
|
|
POEMBITMAPINFO pOem;
|
|
|
|
switch ((ULONG_PTR)pItem->hbmp) {
|
|
case (ULONG_PTR)HBMMENU_MBAR_RESTORE:
|
|
wBmp = OBI_RESTORE_MBAR;
|
|
goto DrawSysBmp;
|
|
|
|
case (ULONG_PTR)HBMMENU_MBAR_MINIMIZE:
|
|
wBmp = OBI_REDUCE_MBAR;
|
|
xBmpLeft += SYSMET(CXEDGE);
|
|
goto DrawSysBmp;
|
|
|
|
case (ULONG_PTR)HBMMENU_MBAR_CLOSE:
|
|
wBmp = OBI_CLOSE_MBAR;
|
|
goto DrawSysBmp;
|
|
|
|
case (ULONG_PTR)HBMMENU_MBAR_CLOSE_D:
|
|
wBmp = OBI_CLOSE_MBAR_I;
|
|
goto DrawSysBmp2;
|
|
|
|
case (ULONG_PTR)HBMMENU_MBAR_MINIMIZE_D:
|
|
wBmp = OBI_REDUCE_MBAR_I;
|
|
xBmpLeft += SYSMET(CXEDGE);
|
|
goto DrawSysBmp2;
|
|
|
|
DrawSysBmp:
|
|
/*
|
|
* Select proper bitmap based on the item state
|
|
*/
|
|
if (TestMFS(pItem, MFS_HILITE)) {
|
|
wBmp += DOBI_PUSHED;
|
|
}
|
|
|
|
DrawSysBmp2:
|
|
BitBltSysBmp(hdc, xBmpLeft, SYSMET(CYEDGE), wBmp);
|
|
break;
|
|
|
|
default:
|
|
UserAssert((pItem->hbmp >= HBMMENU_POPUPFIRST)
|
|
&& (pItem->hbmp <= HBMMENU_POPUPLAST));
|
|
|
|
wBmp = OBI_POPUPFIRST + HandleToUlong(pItem->hbmp) - HandleToUlong(HBMMENU_POPUPFIRST);
|
|
UserAssert(wBmp < OBI_COUNT);
|
|
|
|
pOem = gpsi->oembmi + wBmp;
|
|
y = (pItem->cyItem - pOem->cy) / 2;
|
|
if (y < 0) {
|
|
y = 0;
|
|
}
|
|
BltColor(hdc, NULL, HDCBITS(), xLeft, y,
|
|
pOem->cx, pOem->cy, pOem->x, pOem->y, BC_INVERT);
|
|
break;
|
|
}
|
|
|
|
} /* if (pItem->hbmp == HBMMENU_SYSTEM) */
|
|
|
|
|
|
} else if (pItem->hbmp == HBMMENU_CALLBACK) {
|
|
/*
|
|
* Owner draw bitmap
|
|
*/
|
|
xxxSendMenuDrawItemMessage(hdc,ODA_DRAWENTIRE, pMenu, pItem, TRUE, xLeft);
|
|
|
|
} else {
|
|
/*
|
|
* Drawing a regular bitmap.
|
|
*/
|
|
|
|
int dx, dy;
|
|
HBITMAP hbmSave;
|
|
|
|
//
|
|
// Is this the zero'th item in a menu bar that's not all
|
|
// bitmaps? Hmm, sounds like it could be a fake MDI dude.
|
|
// If it is, use the windows icon instead
|
|
//
|
|
/*
|
|
* Let's fail this for > 4.0 apps so we can get rid of
|
|
* this horrible hack someday. The HBMMENU_ constants
|
|
* have been made public so people can use them freely.
|
|
*
|
|
* Note: even if the app is marked as 4.0, he could be
|
|
* recompiled and may utilizes the new feature in NT5 menu.
|
|
* So just in case, we have to check both dwItemData and lpstr
|
|
* so that the menu could have bitmap, dwItemData and a menu string.
|
|
*
|
|
*/
|
|
if (LOWORD(ptiCurrent->dwExpWinVer) <= VER40) {
|
|
if (pItem->dwItemData && pItem->lpstr == NULL)
|
|
goto AintNothingLikeTheRealMDIThing;
|
|
else if (!fPopup &&
|
|
(pItem == pMenu->rgItems) &&
|
|
(pMenu->cItems > 1) &&
|
|
!(pMenu->rgItems[1].hbmp) &&
|
|
(pItem->spSubMenu)) {
|
|
RIPMSG0(RIP_WARNING, "Fake MDI detected, using window icon in place of bitmap");
|
|
goto AintNothingLikeTheRealMDIThing;
|
|
}
|
|
}
|
|
|
|
UserAssert(hdc != ghdcMem2);
|
|
|
|
dx = pItem->cxBmp;
|
|
|
|
if (fPopup) {
|
|
dy = pItem->cyBmp;
|
|
|
|
//
|
|
// Center bitmap in middle of item area
|
|
//
|
|
cyTemp = (pItem->cyItem - dy);
|
|
if (cyTemp > 0)
|
|
cyTemp = cyTemp / 2;
|
|
else
|
|
cyTemp = 0;
|
|
} else {
|
|
dy = max(pItem->cyBmp, SYSMET(CYMENUSIZE));
|
|
cyTemp = 0;
|
|
if (pItem->lpstr != NULL) {
|
|
xLeft += gcxMenuFontChar;
|
|
}
|
|
}
|
|
|
|
if (hbmSave = GreSelectBitmap(ghdcMem2, pItem->hbmp)) {
|
|
BITMAP bmp;
|
|
//
|
|
// Draw the bitmap leaving some room on the left for the
|
|
// optional check mark if we are in a popup menu. (as opposed
|
|
// to a top level menu bar).
|
|
//
|
|
// We can do cool stuff with monochrome bitmap itmes
|
|
// by merging with the current colors.
|
|
//
|
|
// If the item is selected and the bitmap isn't monochrome,
|
|
// we just invert the thing when we draw it. We can't do
|
|
// anything more clever unless we want to convert to
|
|
// monochrome.
|
|
//
|
|
GreExtGetObjectW(pItem->hbmp, sizeof(bmp), (LPSTR)&bmp);
|
|
GreBitBlt(hdc, xLeft + xHilite, cyTemp + xHilite, dx, dy, ghdcMem2, 0, 0,
|
|
(bmp.bmPlanes * bmp.bmBitsPixel == 1) ?
|
|
SRCSTENCIL :
|
|
(MNDrawHilite(pItem) ? NOTSRCCOPY : SRCCOPY),
|
|
0x00FFFFFF);
|
|
GreSelectBitmap(ghdcMem2, hbmSave);
|
|
} else {
|
|
RIPMSG3(RIP_WARNING, "Menu 0x%08X, item 0x%08X: Tried to draw invalid bitmap 0x%08X", pMenu, pItem, pItem->hbmp) ;
|
|
}
|
|
}
|
|
|
|
|
|
RealDrawMenuItemText:
|
|
if (pItem->lpstr != NULL) {
|
|
/*
|
|
* We want the text in all popup menu items to be aligned
|
|
* if an alignment offset is available.
|
|
*/
|
|
if (fPopup && (pMenu->cxTextAlign != 0)) {
|
|
xLeft = pMenu->cxTextAlign;
|
|
} else if (pItem->hbmp != NULL) {
|
|
xLeft += pItem->cxBmp + SYSMET(CXEDGE);
|
|
}
|
|
|
|
// This item is a text string item. Display it.
|
|
yTop = gcyMenuFontExternLeading;
|
|
|
|
cyTemp = pItem->cyItem - (gcyMenuFontChar + gcyMenuFontExternLeading + SYSMET(CYBORDER));
|
|
if (cyTemp > 0)
|
|
yTop += (cyTemp / 2);
|
|
|
|
if (!fPopup && (pItem->hbmp == NULL)) {
|
|
xLeft += gcxMenuFontChar;
|
|
}
|
|
|
|
lpsz = TextPointer(pItem->lpstr);
|
|
if (lpsz!=NULL) {
|
|
cch = pItem->cch;
|
|
|
|
// Even though we no longer support any funky handling of the
|
|
// help prefix character, we still need to eat it up if we run
|
|
// across it so that the menu item is drawn correctly
|
|
if ((*lpsz == CH_HELPPREFIX) && !fPopup) {
|
|
// Skip help prefix character.
|
|
lpsz++;
|
|
cch--;
|
|
}
|
|
|
|
// tp will contain the character position of the \t indicator
|
|
// in the menu string. This is where we add a tab to the string.
|
|
//
|
|
// rp will contain the character position of the \a indicator
|
|
// in the string. All text following this is right aligned.
|
|
tp = FindCharPosition(lpsz, TEXT('\t'));
|
|
rp = FindCharPosition(lpsz, TEXT('\t') - 1);
|
|
|
|
xFarLeft = pItem->cxItem - (gpsi->oembmi[OBI_MENUCHECK].cx + MNXSPACE);
|
|
|
|
if (rp && (rp != cch)) {
|
|
// Display all the text up to the \a
|
|
if (TestMFT(pItem, MFT_RIGHTORDER) && fPopup) {
|
|
SIZE extent;
|
|
|
|
xxxPSMGetTextExtent(hdc, lpsz, rp, &extent);
|
|
xLeft = xFarLeft - extent.cx;
|
|
}
|
|
xxxDrawMenuItemText(pItem, hdc, xLeft + xHilite, yTop + xHilite, lpsz, rp,
|
|
TestMF(pMenu, MFUNDERLINE));
|
|
|
|
// Do we also have a tab beyond the \a ??
|
|
if (tp > rp + 1) {
|
|
SIZE extent;
|
|
|
|
if (TestMFT(pItem, MFT_RIGHTORDER) && fPopup) {
|
|
xLeft = xFarLeft - pItem->dxTab ;
|
|
} else {
|
|
xxxPSMGetTextExtent(hdc, lpsz + rp + 1,
|
|
(UINT)(tp - rp - 1), &extent);
|
|
xLeft = (int)(pItem->dxTab - extent.cx);
|
|
}
|
|
//
|
|
// lotus in Hebrew make their menus by putting the
|
|
// accelerator on the left and the text on the right
|
|
//
|
|
xxxPSMTextOut(hdc, xLeft, yTop, (LPWSTR)(lpsz + rp + 1), tp - rp - 1,
|
|
TestMF(pMenu, MFUNDERLINE) ? 0 : DT_HIDEPREFIX);
|
|
}
|
|
} else if (tp && rp == cch) {
|
|
// Display text up to the tab position
|
|
if (TestMFT(pItem, MFT_RIGHTORDER)) {
|
|
SIZE extent;
|
|
|
|
xxxPSMGetTextExtent(hdc, lpsz, tp, &extent);
|
|
xLeft = xFarLeft - extent.cx;
|
|
if (!fPopup && (pItem->hbmp == NULL)) {
|
|
xLeft += gcxMenuFontChar;
|
|
}
|
|
}
|
|
xxxDrawMenuItemText(pItem, hdc, xLeft + xHilite, yTop + xHilite, lpsz, tp,
|
|
TestMF(pMenu, MFUNDERLINE));
|
|
}
|
|
|
|
// Any text left to display (like after the tab) ??
|
|
if (tp < cch - 1) {
|
|
if (TestMFT(pItem, MFT_RIGHTORDER) && fPopup) {
|
|
SIZE extent;
|
|
|
|
xxxPSMGetTextExtent(hdc, lpsz + tp + 1, (int)cch - tp - 1, &extent);
|
|
xLeft = pItem->cxItem - pItem->dxTab - extent.cx;
|
|
} else {
|
|
xLeft = pItem->dxTab + gcxMenuFontChar;
|
|
}
|
|
xxxPSMTextOut(hdc, xLeft, yTop, lpsz + tp + 1, cch - tp - 1,
|
|
TestMF(pMenu, MFUNDERLINE) ? 0 : DT_HIDEPREFIX);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Draw the hierarchical arrow for the cascade menu.
|
|
//
|
|
if (fPopup && (pItem->spSubMenu != NULL)) {
|
|
POEMBITMAPINFO pOem;
|
|
|
|
pOem = gpsi->oembmi + (TestMFT(pItem, MFT_RIGHTORDER)
|
|
? OBI_MENUARROW_L : OBI_MENUARROW);
|
|
|
|
// This item has a hierarchical popup associated with it. Draw the
|
|
// bitmap dealy to signify its presence. Note we check if fPopup is set
|
|
// so that this isn't drawn for toplevel menus that have popups.
|
|
|
|
BltColor(hdc,
|
|
NULL,
|
|
HDCBITS(),
|
|
TestMFT(pItem, MFT_RIGHTORDER)
|
|
? pOem->cx :
|
|
pItem->cxItem - pOem->cx,
|
|
max((INT)(pItem->cyItem - 2 - pOem->cy) / 2, 0),
|
|
pOem->cx,
|
|
pOem->cy,
|
|
pOem->x,
|
|
pOem->y,
|
|
BC_INVERT);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMenuBarDraw
|
|
*
|
|
* History:
|
|
* 11-Mar-1992 mikeke From win31
|
|
\***************************************************************************/
|
|
int xxxMenuBarDraw(
|
|
PWND pwnd,
|
|
HDC hdc,
|
|
int cxFrame,
|
|
int cyFrame)
|
|
{
|
|
UINT cxMenuMax;
|
|
UINT cyMenu;
|
|
int yTop;
|
|
PMENU pMenu;
|
|
BOOL fClipped = FALSE;
|
|
TL tlpMenu;
|
|
HBRUSH hbrT;
|
|
CheckLock(pwnd);
|
|
|
|
pMenu = pwnd->spmenu;
|
|
if (pMenu == NULL) {
|
|
return SYSMET(CYBORDER);
|
|
}
|
|
|
|
/*
|
|
* NT5 menus are drawn inactive when the window is not active.
|
|
*/
|
|
if (TestwndFrameOn(pwnd) || (GetAppCompatFlags2(VER40) & GACF2_ACTIVEMENUS)) {
|
|
ClearMF(pMenu, MFINACTIVE);
|
|
} else {
|
|
SetMF(pMenu, MFINACTIVE);
|
|
}
|
|
|
|
/*
|
|
* Lock the menu so we can poke around.
|
|
*/
|
|
ThreadLockMenuNoModify(pMenu, &tlpMenu);
|
|
|
|
yTop = cyFrame;
|
|
yTop += GetCaptionHeight(pwnd);
|
|
|
|
|
|
/*
|
|
* Calculate maximum available horizontal real estate
|
|
*/
|
|
cxMenuMax = (pwnd->rcWindow.right - pwnd->rcWindow.left) - cxFrame * 2;
|
|
|
|
/*
|
|
* If the menu has switched windows, or if either count is 0,
|
|
* then we need to recompute the menu width.
|
|
*/
|
|
if (pwnd != pMenu->spwndNotify ||
|
|
pMenu->cxMenu == 0 ||
|
|
pMenu->cyMenu == 0) {
|
|
|
|
xxxMenuBarCompute(pMenu, pwnd, yTop, cxFrame, cxMenuMax);
|
|
}
|
|
|
|
/*
|
|
* If the menu rectangle is wider than allowed, or the
|
|
* bottom would overlap the size border, we need to clip.
|
|
*/
|
|
if (pMenu->cxMenu > cxMenuMax ||
|
|
(int)(yTop + pMenu->cyMenu) > (int)((pwnd->rcWindow.bottom - pwnd->rcWindow.top)
|
|
- cyFrame)) {
|
|
|
|
/*
|
|
* Lock the display while we're playing around with visrgns. Make
|
|
* a local copy of the saved-visrgn so it can be restored in case
|
|
* we make a callback (i.e. WM_DRAWITEM).
|
|
*/
|
|
GreLockDisplay(gpDispInfo->hDev);
|
|
|
|
fClipped = TRUE;
|
|
|
|
GreIntersectVisRect(hdc, pwnd->rcWindow.left + cxFrame,
|
|
pwnd->rcWindow.top,
|
|
pwnd->rcWindow.left + cxFrame + cxMenuMax,
|
|
pwnd->rcWindow.bottom - cyFrame);
|
|
|
|
GreUnlockDisplay(gpDispInfo->hDev);
|
|
}
|
|
|
|
{
|
|
// Draw menu background in MENU color
|
|
// Draw border under menu in proper BORDER color
|
|
|
|
POLYPATBLT PolyData[2];
|
|
BOOL fFlatMenu;
|
|
|
|
/*
|
|
* We can't use MFISPOPUP to determine if we are drawing a menu bar.
|
|
* This is because a menu can be created as a popup, but then attached
|
|
* to a window using SetMenu(), making it a menubar.
|
|
*/
|
|
fFlatMenu = MNIsFlatMenu();
|
|
|
|
PolyData[0].x = cxFrame;
|
|
PolyData[0].y = yTop;
|
|
PolyData[0].cx = pMenu->cxMenu;
|
|
PolyData[0].cy = pMenu->cyMenu;
|
|
PolyData[0].BrClr.hbr = (pMenu->hbrBack) ? pMenu->hbrBack : (fFlatMenu ? SYSHBR(MENUBAR) : SYSHBR(MENU));
|
|
|
|
PolyData[1].x = cxFrame;
|
|
PolyData[1].y = yTop + pMenu->cyMenu;
|
|
PolyData[1].cx = pMenu->cxMenu;
|
|
PolyData[1].cy = SYSMET(CYBORDER);
|
|
PolyData[1].BrClr.hbr = (TestWF(pwnd, WEFEDGEMASK) && !TestWF(pwnd, WFOLDUI))? SYSHBR(3DFACE) : SYSHBR(WINDOWFRAME);
|
|
|
|
GrePolyPatBlt(hdc,PATCOPY,&PolyData[0],2,PPB_BRUSH);
|
|
}
|
|
|
|
/*
|
|
* Finally, draw the menu itself.
|
|
*/
|
|
|
|
hbrT = GreSelectBrush(hdc, (TestWF(pwnd, WEFEDGEMASK) && !TestWF(pwnd, WFOLDUI))? SYSHBR(3DFACE) : SYSHBR(WINDOWFRAME));
|
|
xxxMenuDraw(hdc, pMenu);
|
|
GreSelectBrush(hdc, hbrT);
|
|
|
|
if (fClipped) {
|
|
/*
|
|
* Recalculate the DC visrgn after calling back.
|
|
*/
|
|
PDCE pdce;
|
|
if ((pdce = LookupDC(hdc)) != NULL) {
|
|
InvalidateDce(pdce);
|
|
}
|
|
}
|
|
|
|
cyMenu = pMenu->cyMenu + SYSMET(CYBORDER);
|
|
ThreadUnlockMenuNoModify(&tlpMenu);
|
|
return cyMenu;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMenuDraw
|
|
*
|
|
* Draws the menu
|
|
*
|
|
* Revalidation notes:
|
|
* This routine must be called with a valid and non-NULL pwnd.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
VOID xxxMenuDraw(
|
|
HDC hdc,
|
|
PMENU pmenu)
|
|
{
|
|
PITEM pItem;
|
|
UINT i, cy;
|
|
RECT rcItem;
|
|
HFONT hFontOld;
|
|
UINT bfExtra;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
UINT oldAlign;
|
|
int iBkSave;
|
|
POINT ptOrg;
|
|
|
|
CheckLock(pmenu);
|
|
|
|
if (pmenu == NULL) {
|
|
RIPERR0(ERROR_INVALID_HANDLE,
|
|
RIP_WARNING,
|
|
"xxxMenuDraw: Invalid menu handle (NULL)");
|
|
|
|
return;
|
|
}
|
|
|
|
GreGetViewportOrg(hdc, &ptOrg);
|
|
hFontOld = GreSelectFont(hdc, ghMenuFont);
|
|
|
|
oldAlign = GreGetTextAlign(hdc);
|
|
if (pmenu->rgItems && TestMFT(pmenu->rgItems, MFT_RIGHTORDER)) {
|
|
GreSetTextAlign(hdc, oldAlign | TA_RTLREADING);
|
|
}
|
|
|
|
bfExtra = CalcbfExtra();
|
|
|
|
if (pmenu->hbrBack != NULL) {
|
|
iBkSave = GreSetBkMode(hdc, TRANSPARENT);
|
|
}
|
|
|
|
if (pmenu->dwArrowsOn != MSA_OFF) {
|
|
pItem = MNGetToppItem(pmenu);
|
|
GreSetViewportOrg(hdc, ptOrg.x, ptOrg.y - ((int)pItem->yItem), NULL);
|
|
i = pmenu->iTop;
|
|
} else {
|
|
pItem = (PITEM)pmenu->rgItems;
|
|
i = 0;
|
|
}
|
|
|
|
cy = 0;
|
|
for (; i < pmenu->cItems; i++, pItem++) {
|
|
if (TestMFT(pItem, MFT_MENUBARBREAK) &&
|
|
TestMF(pmenu, MFISPOPUP)) {
|
|
|
|
//
|
|
// Draw a vertical etch. This is done by calling DrawEdge(),
|
|
// sunken, with BF_LEFT | BF_RIGHT.
|
|
//
|
|
if(TestMFT(pItem, MFT_RIGHTORDER) && i) {
|
|
//
|
|
// going backwards, so the correct place is just before the
|
|
// _previous_ item.
|
|
//
|
|
PITEM pi;
|
|
|
|
pi = pItem - 1;
|
|
rcItem.left = pi->xItem - SYSMET(CXFIXEDFRAME);
|
|
rcItem.top = 0;
|
|
rcItem.right = pi->xItem - SYSMET(CXBORDER);
|
|
rcItem.bottom = pmenu->cyMenu;
|
|
} else {
|
|
rcItem.left = pItem->xItem - SYSMET(CXFIXEDFRAME);
|
|
rcItem.top = 0;
|
|
rcItem.right = pItem->xItem - SYSMET(CXBORDER);
|
|
rcItem.bottom = pmenu->cyMenu;
|
|
}
|
|
|
|
DrawEdge(hdc, &rcItem, BDR_SUNKENOUTER, BF_LEFT | BF_RIGHT | bfExtra);
|
|
}
|
|
/*
|
|
* If this is a separator, draw it and return.
|
|
* 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
|
|
*/
|
|
if (TestMFT(pItem, MFT_SEPARATOR)
|
|
&& (!TestMFT(pItem, MFT_OWNERDRAW)
|
|
|| (LOWORD(ptiCurrent->dwExpWinVer) < VER40))) {
|
|
|
|
/*
|
|
* Draw a horizontal etch.
|
|
*/
|
|
int yT = pItem->yItem + (pItem->cyItem / 2) - SYSMET(CYBORDER);
|
|
RECT rcItem;
|
|
|
|
rcItem.left = pItem->xItem + 1;
|
|
rcItem.top = yT;
|
|
rcItem.right = pItem->xItem + pItem->cxItem - 1;
|
|
rcItem.bottom = yT + SYSMET(CYEDGE);
|
|
|
|
DrawEdge(hdc, &rcItem, BDR_SUNKENOUTER, BF_TOP | BF_BOTTOM | bfExtra);
|
|
/*
|
|
* Draw drop insertion bar, if any.
|
|
*/
|
|
MNDrawInsertionBar (hdc, pItem);
|
|
|
|
} else {
|
|
xxxDrawMenuItem(hdc, pmenu, pItem, 0);
|
|
}
|
|
|
|
if (pmenu->dwArrowsOn != MSA_OFF) {
|
|
cy += pItem->cyItem;
|
|
if (cy > pmenu->cyMenu) {
|
|
/*
|
|
* This is a scrollable menu and the item just drawn falls below
|
|
* the bottom of the visible menu -- no need to draw any further
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pmenu->hbrBack != NULL) {
|
|
GreSetBkMode(hdc, iBkSave);
|
|
}
|
|
|
|
GreSetViewportOrg(hdc, ptOrg.x, ptOrg.y, NULL);
|
|
GreSetTextAlign(hdc, oldAlign);
|
|
GreSelectFont(hdc, hFontOld);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxDrawMenuBar
|
|
*
|
|
* Forces redraw of the menu bar
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
BOOL xxxDrawMenuBar(
|
|
PWND pwnd)
|
|
{
|
|
CheckLock(pwnd);
|
|
|
|
if (!TestwndChild(pwnd)) {
|
|
xxxRedrawFrame(pwnd);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxMenuInvert
|
|
*
|
|
* Invert menu item
|
|
*
|
|
* Revalidation notes:
|
|
* This routine must be called with a valid and non-NULL pwnd.
|
|
*
|
|
* fOn - TRUE if the item is selected thus it needs to be inverted
|
|
* fNotify - TRUE if the parent should be notified (as appropriate), FALSE
|
|
* if we are just redrawing the selected item.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
PITEM xxxMNInvertItem(
|
|
PPOPUPMENU ppopupmenu,
|
|
PMENU pmenu,
|
|
int itemNumber,
|
|
PWND pwndNotify,
|
|
BOOL fOn)
|
|
{
|
|
PITEM pItem = NULL;
|
|
HDC hdc;
|
|
int y, iNewTop;
|
|
RECT rcItem;
|
|
BOOL fSysMenuIcon = FALSE;
|
|
PMENU pmenusys;
|
|
BOOL fClipped = FALSE;
|
|
HFONT hOldFont;
|
|
PWND pwnd;
|
|
POINT ptOrg;
|
|
TL tlpwnd;
|
|
UINT oldAlign;
|
|
|
|
CheckLock(pmenu);
|
|
CheckLock(pwndNotify);
|
|
|
|
/*
|
|
* If we are in the middle of trying to get out of menu mode, hMenu
|
|
* and/or pwndNotify will be NULL, so just bail out now.
|
|
*/
|
|
if ((pmenu == NULL) || (pwndNotify == NULL)) {
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* If ppopupmenu is NULL, we're not in menu mode (i.e, call from
|
|
* HiliteMenuItem).
|
|
*/
|
|
if (ppopupmenu == NULL) {
|
|
pwnd = pwndNotify;
|
|
} else {
|
|
pwnd = ppopupmenu->spwndPopupMenu;
|
|
}
|
|
|
|
if (pwnd != pwndNotify) {
|
|
ThreadLock(pwnd, &tlpwnd);
|
|
}
|
|
|
|
|
|
if (itemNumber < 0) {
|
|
|
|
if (ppopupmenu != NULL) {
|
|
if ((itemNumber == MFMWFP_UPARROW) || (itemNumber == MFMWFP_DOWNARROW)) {
|
|
MNDrawArrow(NULL, ppopupmenu, itemNumber);
|
|
}
|
|
}
|
|
|
|
xxxSendMenuSelect(pwndNotify, pwnd, pmenu, itemNumber);
|
|
goto SeeYa;
|
|
}
|
|
|
|
if (!TestMF(pmenu, MFISPOPUP)) {
|
|
pmenusys = xxxGetSysMenuHandle(pwndNotify);
|
|
if (pmenu == pmenusys) {
|
|
MNPositionSysMenu(pwndNotify, pmenusys);
|
|
fSysMenuIcon = TRUE;
|
|
}
|
|
}
|
|
|
|
if ((UINT)itemNumber >= pmenu->cItems)
|
|
goto SeeYa;
|
|
|
|
pItem = &pmenu->rgItems[itemNumber];
|
|
|
|
if (!TestMF(pmenu, MFISPOPUP) && TestWF(pwndNotify, WFMINIMIZED)) {
|
|
|
|
/*
|
|
* Skip inverting top level menus if the window is iconic.
|
|
*/
|
|
goto SeeYa;
|
|
}
|
|
|
|
/*
|
|
* Is this a separator?
|
|
*/
|
|
if (TestMFT(pItem, MFT_SEPARATOR)) {
|
|
goto SendSelectMsg;
|
|
}
|
|
|
|
if ((BOOL)TestMFS(pItem, MFS_HILITE) == (BOOL)fOn) {
|
|
|
|
/*
|
|
* Item's state isn't really changing. Just return.
|
|
*/
|
|
goto SeeYa;
|
|
}
|
|
|
|
if (fOn && (ppopupmenu != NULL) && (pmenu->dwArrowsOn != MSA_OFF)) {
|
|
/*
|
|
* when selecting an item, ensure that the item is fully visible
|
|
* NOTE -- For mouse use, partially visible should be acceptable
|
|
* -- Can we get mouse info down this far ?
|
|
*/
|
|
|
|
if (itemNumber < pmenu->iTop) {
|
|
iNewTop = itemNumber;
|
|
goto NewTop;
|
|
} else {
|
|
PITEM pWalk = MNGetToppItem(pmenu);
|
|
int dy = pItem->yItem + pItem->cyItem - pWalk->yItem - pmenu->cyMenu;
|
|
iNewTop = pmenu->iTop;
|
|
while ((dy > 0) && (iNewTop < (int)pmenu->cItems)) {
|
|
dy -= pWalk->cyItem;
|
|
pWalk++;
|
|
iNewTop++;
|
|
}
|
|
if (iNewTop >= (int)pmenu->cItems) {
|
|
iNewTop = pmenu->cItems;
|
|
}
|
|
NewTop:
|
|
if (xxxMNSetTop(ppopupmenu, iNewTop)) {
|
|
xxxUpdateWindow(pwnd);
|
|
}
|
|
}
|
|
}
|
|
|
|
rcItem.left = pItem->xItem;
|
|
rcItem.top = pItem->yItem;
|
|
rcItem.right = pItem->xItem + pItem->cxItem;
|
|
rcItem.bottom = pItem->yItem + pItem->cyItem;
|
|
|
|
y = pItem->cyItem;
|
|
|
|
if (TestMF(pmenu, MFISPOPUP)) {
|
|
hdc = _GetDC(pwnd);
|
|
} else {
|
|
hdc = _GetWindowDC(pwnd);
|
|
if (TestWF(pwnd, WFSIZEBOX) && !fSysMenuIcon) {
|
|
|
|
/*
|
|
* If the window is small enough that some of the menu bar has been
|
|
* obscured by the frame, we don't want to draw on the bottom of the
|
|
* sizing frame. Note that we don't want to do this if we are
|
|
* inverting the system menu icon since that will be clipped to the
|
|
* window rect. (otherwise we end up with only half the sys menu
|
|
* icon inverted)
|
|
*/
|
|
int xMenuMax = (pwnd->rcWindow.right - pwnd->rcWindow.left) - SYSMET(CXSIZEFRAME);
|
|
|
|
if (rcItem.right > xMenuMax ||
|
|
rcItem.bottom > ((pwnd->rcWindow.bottom -
|
|
pwnd->rcWindow.top) - SYSMET(CYSIZEFRAME))) {
|
|
|
|
/*
|
|
* Lock the display while we're playing around with visrgns.
|
|
* Make a local copy of the visrgn, so that it can be
|
|
* properly restored on potential callbacks (i.e. WM_DRAWITEM).
|
|
*/
|
|
GreLockDisplay(gpDispInfo->hDev);
|
|
|
|
fClipped = TRUE;
|
|
|
|
GreIntersectVisRect(hdc,
|
|
pwnd->rcWindow.left + rcItem.left,
|
|
pwnd->rcWindow.top + rcItem.top,
|
|
pwnd->rcWindow.left + xMenuMax,
|
|
pwnd->rcWindow.bottom - SYSMET(CYSIZEFRAME));
|
|
|
|
GreUnlockDisplay(gpDispInfo->hDev);
|
|
}
|
|
}
|
|
}
|
|
|
|
oldAlign = GreGetTextAlign(hdc);
|
|
if (pItem && TestMFT(pItem, MFT_RIGHTORDER))
|
|
GreSetTextAlign(hdc, oldAlign | TA_RTLREADING);
|
|
|
|
hOldFont = GreSelectFont(hdc, ghMenuFont);
|
|
GreGetViewportOrg(hdc, &ptOrg);
|
|
|
|
if (fOn) {
|
|
SetMFS(pItem, MFS_HILITE);
|
|
} else {
|
|
ClearMFS(pItem, MFS_HILITE);
|
|
}
|
|
|
|
if (!fSysMenuIcon
|
|
&& ((pItem->hbmp != HBMMENU_SYSTEM)
|
|
|| (TestMF(pmenu, MFISPOPUP)))) {
|
|
|
|
if (pmenu->dwArrowsOn != MSA_OFF) {
|
|
GreSetViewportOrg(hdc, ptOrg.x, ptOrg.y - ((int)MNGetToppItem(pmenu)->yItem), NULL);
|
|
}
|
|
|
|
if ((pmenu->hbrBack != NULL)
|
|
&& !MNDrawHilite(pItem)
|
|
&& !TestMFT(pItem, MFT_OWNERDRAW)) {
|
|
|
|
/*
|
|
* fill the background here so xxxDrawMenuItem doesn't have to fool
|
|
* around with hbrBack
|
|
*/
|
|
int iBkSave = GreSetBkMode(hdc, TRANSPARENT);
|
|
MNEraseBackground (hdc, pmenu,
|
|
pItem->xItem, pItem->yItem,
|
|
pItem->cxItem, pItem->cyItem);
|
|
GreSetBkMode(hdc, iBkSave);
|
|
}
|
|
|
|
xxxDrawMenuItem(hdc, pmenu, pItem, DMI_INVERT);
|
|
}
|
|
|
|
|
|
if (fClipped) {
|
|
/*
|
|
* Recalculate the DC visrgn after calling back.
|
|
*/
|
|
PDCE pdce;
|
|
if ((pdce = LookupDC(hdc)) != NULL) {
|
|
InvalidateDce(pdce);
|
|
}
|
|
}
|
|
|
|
GreSelectFont(hdc, hOldFont);
|
|
GreSetViewportOrg(hdc, ptOrg.x, ptOrg.y, NULL);
|
|
GreSetTextAlign(hdc, oldAlign);
|
|
_ReleaseDC(hdc);
|
|
|
|
SendSelectMsg:
|
|
/*
|
|
* send select msg only if we are selecting an item.
|
|
*/
|
|
if (fOn) {
|
|
xxxSendMenuSelect(pwndNotify, pwnd, pmenu, itemNumber);
|
|
}
|
|
|
|
SeeYa:
|
|
if (pwnd != pwndNotify) {
|
|
ThreadUnlock(&tlpwnd);
|
|
}
|
|
|
|
return pItem;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxDrawMenuBarTemp
|
|
*
|
|
* This is so the control panel can let us do the work -- and make their
|
|
* preview windows that much more accurate. The only reason I put the hwnd in
|
|
* here is because, in the low level menu routines, we assume that an hwnd is
|
|
* associated with the hmenu -- I didn't want to slow that code down by adding
|
|
* NULL checks.
|
|
*
|
|
* The SYSMET(CYMENU) with regard to the given font is returned -- this
|
|
* way control panel can say, "The user wants this menu font (hfont) with this
|
|
* menu height (lprc)", and we can respond "this is the height we ended up
|
|
* using."
|
|
*
|
|
* NOTE: It's OK to over-write lprc because this function receives a pointer
|
|
* to the rectangle captured in NtUserDrawMenuBarTemp.
|
|
*
|
|
* History:
|
|
* 20-Sep-95 BradG Ported from Win95 (inctlpan.c)
|
|
\***************************************************************************/
|
|
int xxxDrawMenuBarTemp(
|
|
PWND pwnd,
|
|
HDC hdc,
|
|
LPRECT lprc,
|
|
PMENU pMenu,
|
|
HFONT hfont)
|
|
{
|
|
int cyMenu;
|
|
HFONT hOldFont;
|
|
HFONT hFontSave;
|
|
int cxCharSave;
|
|
int cxOverhangSave;
|
|
int cyCharSave;
|
|
int cyLeadingSave;
|
|
int cyAscentSave;
|
|
int cySizeSave;
|
|
PWND pwndNotify;
|
|
TL tlpwndNotify;
|
|
|
|
hFontSave = ghMenuFont;
|
|
cxCharSave = gcxMenuFontChar;
|
|
cxOverhangSave = gcxMenuFontOverhang;
|
|
cyCharSave = gcyMenuFontChar;
|
|
cyLeadingSave = gcyMenuFontExternLeading;
|
|
cyAscentSave = gcyMenuFontAscent;
|
|
cySizeSave = SYSMET(CYMENUSIZE);
|
|
|
|
CheckLock(pwnd);
|
|
CheckLock(pMenu);
|
|
|
|
ThreadLock(pMenu->spwndNotify, &tlpwndNotify);
|
|
pwndNotify = pMenu->spwndNotify;
|
|
|
|
cyMenu = lprc->bottom - lprc->top;
|
|
|
|
if (hfont) {
|
|
TEXTMETRIC textMetrics;
|
|
|
|
/*
|
|
* Compute the new menu font info if needed
|
|
*/
|
|
ghMenuFont = hfont;
|
|
hOldFont = GreSelectFont(HDCBITS(), ghMenuFont);
|
|
gcxMenuFontChar = GetCharDimensions(
|
|
HDCBITS(), &textMetrics, &gcyMenuFontChar);
|
|
|
|
gcxMenuFontOverhang = textMetrics.tmOverhang;
|
|
GreSelectFont(HDCBITS(), hOldFont);
|
|
|
|
gcyMenuFontExternLeading = textMetrics.tmExternalLeading;
|
|
gcyMenuFontAscent = textMetrics.tmAscent + SYSMET(CYBORDER);
|
|
}
|
|
|
|
cyMenu -= SYSMET(CYBORDER);
|
|
cyMenu = max(cyMenu, (gcyMenuFontChar + gcyMenuFontExternLeading + SYSMET(CYEDGE)));
|
|
SYSMET(CYMENUSIZE) = cyMenu;
|
|
SYSMET(CYMENU) = cyMenu + SYSMET(CYBORDER);
|
|
|
|
/*
|
|
* Compute the dimensons of the menu (hope that we don't leave the
|
|
* USER critical section)
|
|
*/
|
|
xxxMenuBarCompute(pMenu, pwnd, lprc->top, lprc->left, lprc->right);
|
|
|
|
/*
|
|
* Draw menu border in menu color
|
|
*/
|
|
lprc->bottom = lprc->top + pMenu->cyMenu;
|
|
FillRect(hdc, lprc, MNIsFlatMenu() ? SYSHBR(MENUBAR) : SYSHBR(MENU));
|
|
|
|
/*
|
|
* Finally, draw the menu itself.
|
|
*/
|
|
xxxMenuDraw(hdc, pMenu);
|
|
|
|
/*
|
|
* Restore the old state
|
|
*/
|
|
ghMenuFont = hFontSave;
|
|
gcxMenuFontChar = cxCharSave;
|
|
gcxMenuFontOverhang = cxOverhangSave;
|
|
gcyMenuFontChar = cyCharSave;
|
|
gcyMenuFontExternLeading = cyLeadingSave;
|
|
gcyMenuFontAscent = cyAscentSave;
|
|
SYSMET(CYMENUSIZE) = cySizeSave;
|
|
|
|
cyMenu = SYSMET(CYMENU);
|
|
SYSMET(CYMENU) = cySizeSave + SYSMET(CYBORDER);
|
|
|
|
Lock(&pMenu->spwndNotify, pwndNotify);
|
|
ThreadUnlock(&tlpwndNotify);
|
|
|
|
return cyMenu;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxDrawMenuBarUnderlines
|
|
*
|
|
* Description: Shows or hides all underlines on a menu bar.
|
|
*
|
|
* History:
|
|
* 07/23/96 vadimg created
|
|
\***************************************************************************/
|
|
VOID xxxDrawMenuBarUnderlines(
|
|
PWND pwnd,
|
|
BOOL fShow)
|
|
{
|
|
HDC hdc;
|
|
PMENU pmenu;
|
|
PITEM pitem;
|
|
ULONG i, yTop, cyTemp;
|
|
LPWSTR psz;
|
|
WCHAR szMenu[MENU_STRLEN], *pchOut;
|
|
LONG result;
|
|
HBRUSH hbr;
|
|
TL tlpMenu;
|
|
PTHREADINFO ptiCurrent = PtiCurrentShared();
|
|
int xLeft;
|
|
LPWSTR lpsz;
|
|
SIZE extent;
|
|
BOOL fFlatMenuBar;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
/*
|
|
* Bail if menu underlines are always on.
|
|
*/
|
|
if (TEST_BOOL_ACCF(ACCF_KEYBOARDPREF) || TestEffectInvertUP(KEYBOARDCUES)
|
|
|| (GetAppCompatFlags2(VER40) & GACF2_KCOFF)) {
|
|
return;
|
|
}
|
|
|
|
// if there is no menu, bail out right away
|
|
|
|
pwnd = GetTopLevelWindow(pwnd);
|
|
if (pwnd == NULL || !TestWF(pwnd, WFMPRESENT)) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* We don't clear WFMPRESENT when the menu is unlocked so make sure we have
|
|
* one.
|
|
*/
|
|
pmenu = pwnd->spmenu;
|
|
if (pmenu == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (MNIsFlatMenu()) {
|
|
fFlatMenuBar = !TestMF(pmenu, MFISPOPUP);
|
|
} else {
|
|
fFlatMenuBar = FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* set/clear the underline state. There are cases when the
|
|
* menu loop doesn't remove the keys from the queue; so after
|
|
* exiting we might get here but nothing needs to be drawn
|
|
*/
|
|
if (fShow) {
|
|
if (TestMF(pmenu, MFUNDERLINE)) {
|
|
return;
|
|
}
|
|
hbr = SYSHBR(MENUTEXT);
|
|
SetMF(pmenu, MFUNDERLINE);
|
|
} else {
|
|
if (!TestMF(pmenu, MFUNDERLINE)) {
|
|
return;
|
|
}
|
|
if (pmenu->hbrBack != NULL) {
|
|
hbr = pmenu->hbrBack;
|
|
} else if (fFlatMenuBar) {
|
|
hbr = SYSHBR(MENUBAR);
|
|
} else {
|
|
hbr = SYSHBR(MENU);
|
|
}
|
|
ClearMF(pmenu, MFUNDERLINE);
|
|
}
|
|
|
|
pitem = (PITEM)pmenu->rgItems;
|
|
|
|
hdc = _GetDCEx(pwnd, NULL, DCX_WINDOW | DCX_USESTYLE | DCX_CACHE);
|
|
|
|
// select in the correct brush and font
|
|
|
|
GreSelectFont(hdc, ghMenuFont);
|
|
|
|
ThreadLockMenuNoModify(pmenu, &tlpMenu);
|
|
for (i = 0; i < pmenu->cItems; i++, pitem++) {
|
|
if (((psz = TextPointer(pitem->lpstr)) == NULL)
|
|
&& !TestMFT(pitem, MFT_OWNERDRAW)) {
|
|
continue;
|
|
}
|
|
|
|
if (TestMFT(pitem, MFT_OWNERDRAW)) {
|
|
GreSetViewportOrg(hdc, 0, 0, NULL);
|
|
} else {
|
|
GreSetViewportOrg(hdc, pitem->xItem, pitem->yItem, NULL);
|
|
}
|
|
|
|
// this funky xLeft and yTop calculation stolen from RealDrawMenuItem
|
|
|
|
yTop = gcyMenuFontExternLeading;
|
|
cyTemp = pitem->cyItem - (gcyMenuFontChar + gcyMenuFontExternLeading +
|
|
SYSMET(CYBORDER));
|
|
if (cyTemp > 0) {
|
|
yTop += (cyTemp / 2);
|
|
}
|
|
|
|
if (fShow && TestMFS(pitem, MFS_HOTTRACK)) {
|
|
GreSelectBrush(hdc, SYSHBR(HOTLIGHT));
|
|
} else {
|
|
GreSelectBrush(hdc, hbr);
|
|
}
|
|
|
|
if (TestMFT(pitem, MFT_OWNERDRAW)) {
|
|
xxxSendMenuDrawItemMessage(hdc, ODA_DRAWENTIRE, pmenu, pitem, FALSE, 0);
|
|
} else {
|
|
TL tl;
|
|
|
|
if (pitem->cch > MENU_STRLEN) {
|
|
pchOut = (WCHAR*)UserAllocPool((pitem->cch+1) * sizeof(WCHAR), TAG_RTL);
|
|
if (pchOut == NULL) {
|
|
break;
|
|
}
|
|
ThreadLockPool(ptiCurrent, pchOut, &tl);
|
|
} else {
|
|
pchOut = szMenu;
|
|
}
|
|
|
|
xLeft = gcxMenuFontChar;
|
|
|
|
if (TestMFT(pitem, MFT_RIGHTORDER) &&
|
|
((lpsz = TextPointer(pitem->lpstr)) != NULL))
|
|
{
|
|
xxxPSMGetTextExtent(hdc, lpsz, pitem->cch, &extent);
|
|
xLeft += (pitem->cxItem - (gpsi->oembmi[OBI_MENUCHECK].cx + MNXSPACE) - extent.cx);
|
|
}
|
|
|
|
if (CALL_LPK(ptiCurrent)) {
|
|
if (!fShow) {
|
|
//Becuase PSMTextOut does not use PatBlt it uses ExtTextOut.
|
|
GreSetTextColor(hdc, fFlatMenuBar ? SYSRGB(MENUBAR) : SYSRGB(MENU));
|
|
}
|
|
xxxPSMTextOut(hdc, xLeft, yTop, psz, pitem->cch, DT_PREFIXONLY);
|
|
|
|
} else {
|
|
result = GetPrefixCount(psz, pitem->cch, pchOut, pitem->cch);
|
|
xxxDrawItemUnderline(pitem, hdc, xLeft, yTop, pchOut,
|
|
LOWORD(result));
|
|
}
|
|
if (pchOut != szMenu) {
|
|
ThreadUnlockAndFreePool(ptiCurrent, &tl);
|
|
}
|
|
}
|
|
}
|
|
ThreadUnlockMenuNoModify(&tlpMenu);
|
|
|
|
_ReleaseDC(hdc);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxPaintMenuBar
|
|
*
|
|
* 3/8/2000 vadimg created
|
|
\***************************************************************************/
|
|
|
|
UINT xxxPaintMenuBar(PWND pwnd, HDC hdc, int iLeftOffset, int iRightOffset,
|
|
int iTopOffset, DWORD dwFlags)
|
|
{
|
|
PMENU pMenu;
|
|
UINT cyMenu, cxMenuMax;
|
|
HBRUSH hbrOld, hbr;
|
|
TL tlpMenu;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
pMenu = (PMENU)pwnd->spmenu;
|
|
if (pMenu == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
ThreadLockMenuNoModify(pMenu, &tlpMenu);
|
|
|
|
/*
|
|
* Figure out whether to paint it in active or inactive colors.
|
|
*/
|
|
if (dwFlags & PMB_ACTIVE) {
|
|
ClearMF(pMenu, MFINACTIVE);
|
|
} else {
|
|
SetMF(pMenu, MFINACTIVE);
|
|
}
|
|
|
|
/*
|
|
* Calculate maximum available horizontal real estate.
|
|
*/
|
|
cxMenuMax = ((pwnd->rcWindow.right - iRightOffset) -
|
|
(pwnd->rcWindow.left + iLeftOffset));
|
|
|
|
/*
|
|
* If the menu has switched windows, or if either count is 0,
|
|
* then we need to recompute the menu width.
|
|
*/
|
|
if (pwnd != pMenu->spwndNotify || pMenu->cxMenu == 0 ||
|
|
pMenu->cyMenu == 0) {
|
|
xxxMenuBarCompute(pMenu, pwnd, iTopOffset, iLeftOffset, cxMenuMax);
|
|
}
|
|
|
|
if (pMenu->hbrBack) {
|
|
hbr = pMenu->hbrBack;
|
|
} else {
|
|
hbr = SYSHBR(MENUBAR);
|
|
}
|
|
hbrOld = GreSelectBrush(hdc, hbr);
|
|
GrePatBlt(hdc, iLeftOffset, iTopOffset, pMenu->cxMenu, pMenu->cyMenu, PATCOPY);
|
|
|
|
/*
|
|
* Finally draw the menu itself.
|
|
*/
|
|
xxxMenuDraw(hdc, pMenu);
|
|
GreSelectBrush(hdc, hbrOld);
|
|
|
|
cyMenu = pMenu->cyMenu;
|
|
ThreadUnlockMenuNoModify(&tlpMenu);
|
|
return cyMenu;
|
|
}
|
|
|