reactos/reactos/dll/win32/user32/windows/menu.c
Matthias Kupfer 9c5d76e1bf - due to minor differences (marletting related) combine drawing routines for check box and radio button
- initial version for popup arrows, maybe someone know how to remove the bitmap and memory-dc stuff properly

svn path=/trunk/; revision=39940
2009-03-10 21:39:22 +00:00

5210 lines
136 KiB
C

/* $Id$
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS user32.dll
* FILE: user32/windows/menu.c
* PURPOSE: Menus
*
* PROGRAMMERS: Casper S. Hornstrup
* James Tabor
*/
/* INCLUDES ******************************************************************/
#include <user32.h>
#include <wine/debug.h>
WINE_DEFAULT_DEBUG_CHANNEL(user32);
LRESULT DefWndNCPaint(HWND hWnd, HRGN hRgn, BOOL Active);
/* internal popup menu window messages */
#define MM_SETMENUHANDLE (WM_USER + 0)
#define MM_GETMENUHANDLE (WM_USER + 1)
/* Internal MenuTrackMenu() flags */
#define TPM_INTERNAL 0xF0000000
#define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
#define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
#define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
/* TYPES *********************************************************************/
#define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)
#define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK)
#define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
#define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
#define IS_SYSTEM_MENU(MenuInfo) \
(0 == ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
#define IS_SYSTEM_POPUP(MenuInfo) \
(0 != ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
#define IS_MAGIC_ITEM(Bmp) ((int) Bmp <12)
#define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
#define MENU_ITEM_HBMP_SPACE (5)
#define MENU_BAR_ITEMS_SPACE (12)
#define SEPARATOR_HEIGHT (5)
#define MENU_TAB_SPACE (8)
#define ITEM_PREV -1
#define ITEM_NEXT 1
#define MAKEINTATOMA(atom) ((LPCSTR)((ULONG_PTR)((WORD)(atom))))
#define MAKEINTATOMW(atom) ((LPCWSTR)((ULONG_PTR)((WORD)(atom))))
#define POPUPMENU_CLASS_ATOMA MAKEINTATOMA(32768) /* PopupMenu */
#define POPUPMENU_CLASS_ATOMW MAKEINTATOMW(32768) /* PopupMenu */
/* internal flags for menu tracking */
#define TF_ENDMENU 0x0001
#define TF_SUSPENDPOPUP 0x0002
#define TF_SKIPREMOVE 0x0004
typedef struct
{
UINT TrackFlags;
HMENU CurrentMenu; /* current submenu (can be equal to hTopMenu)*/
HMENU TopMenu; /* initial menu */
HWND OwnerWnd; /* where notifications are sent */
POINT Pt;
} MTRACKER;
static LRESULT WINAPI PopupMenuWndProcW(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
/*********************************************************************
* PopupMenu class descriptor
*/
const struct builtin_class_descr POPUPMENU_builtin_class =
{
POPUPMENU_CLASS_ATOMW, /* name */
CS_SAVEBITS | CS_DBLCLKS, /* style */
(WNDPROC) NULL, /* FIXME - procA */
(WNDPROC) PopupMenuWndProcW, /* FIXME - procW */
sizeof(MENUINFO *), /* extra */
(LPCWSTR) IDC_ARROW, /* cursor */
(HBRUSH)(COLOR_MENU + 1) /* brush */
};
/* INTERNAL FUNCTIONS ********************************************************/
/* Rip the fun and easy to use and fun WINE unicode string manipulation routines.
* Of course I didnt copy the ASM code because we want this to be portable
* and it needs to go away.
*/
#ifndef GET_WORD
#define GET_WORD(ptr) (*(WORD *)(ptr))
#endif
#ifndef GET_DWORD
#define GET_DWORD(ptr) (*(DWORD *)(ptr))
#endif
HFONT hMenuFont = NULL;
HFONT hMenuFontBold = NULL;
/* Flag set by EndMenu() to force an exit from menu tracking */
static BOOL fEndMenu = FALSE;
/* Use global popup window because there's no way 2 menus can
* be tracked at the same time. */
static HWND TopPopup;
/* Dimension of the menu bitmaps */
static HBITMAP BmpSysMenu = NULL;
static SIZE MenuCharSize;
/***********************************************************************
* MenuGetRosMenuInfo
*
* Get full information about menu
*/
static BOOL FASTCALL
MenuGetRosMenuInfo(PROSMENUINFO MenuInfo, HMENU Menu)
{
MenuInfo->cbSize = sizeof(ROSMENUINFO);
MenuInfo->fMask = MIM_BACKGROUND | MIM_HELPID | MIM_MAXHEIGHT | MIM_MENUDATA | MIM_STYLE;
return NtUserMenuInfo(Menu, MenuInfo, FALSE);
}
/***********************************************************************
* MenuSetRosMenuInfo
*
* Set full information about menu
*/
static BOOL FASTCALL
MenuSetRosMenuInfo(PROSMENUINFO MenuInfo)
{
MenuInfo->cbSize = sizeof(ROSMENUINFO);
MenuInfo->fMask = MIM_BACKGROUND | MIM_HELPID | MIM_MAXHEIGHT | MIM_MENUDATA | MIM_STYLE;
return NtUserMenuInfo(MenuInfo->Self, MenuInfo, TRUE);
}
/***********************************************************************
* MenuInitRosMenuItemInfo
*
* Initialize a buffer for use with MenuGet/SetRosMenuItemInfo
*/
static VOID FASTCALL
MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
{
ZeroMemory(ItemInfo, sizeof(ROSMENUITEMINFO));
ItemInfo->cbSize = sizeof(ROSMENUITEMINFO);
}
/***********************************************************************
* MenuGetRosMenuItemInfo
*
* Get full information about a menu item
*/
static BOOL FASTCALL
MenuGetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo)
{
UINT Save_Mask = ItemInfo->fMask; /* Save the org mask bits. */
if (ItemInfo->dwTypeData != NULL)
{
HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData);
}
ItemInfo->fMask = MIIM_BITMAP | MIIM_CHECKMARKS | MIIM_DATA | MIIM_FTYPE
| MIIM_ID | MIIM_STATE | MIIM_STRING | MIIM_SUBMENU | MIIM_TYPE;
ItemInfo->dwTypeData = NULL;
if (! NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, FALSE))
{
ItemInfo->fType = 0;
return FALSE;
}
if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING)
{
ItemInfo->cch++;
ItemInfo->dwTypeData = HeapAlloc(GetProcessHeap(), 0,
ItemInfo->cch * sizeof(WCHAR));
if (NULL == ItemInfo->dwTypeData)
{
return FALSE;
}
if (! NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, FALSE))
{
ItemInfo->fType = 0;
return FALSE;
}
ItemInfo->dwTypeData[ItemInfo->cch - 1] = UNICODE_NULL;
}
ItemInfo->fMask = Save_Mask;
return TRUE;
}
/***********************************************************************
* MenuSetRosMenuItemInfo
*
* Set selected information about a menu item, need to set the mask bits.
*/
static BOOL FASTCALL
MenuSetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo)
{
BOOL Ret;
if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING &&
ItemInfo->dwTypeData != NULL)
{
ItemInfo->cch = strlenW(ItemInfo->dwTypeData);
}
Ret = NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, TRUE);
return Ret;
}
/***********************************************************************
* MenuCleanupRosMenuItemInfo
*
* Cleanup after use of MenuGet/SetRosMenuItemInfo
*/
static VOID FASTCALL
MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
{
if (ItemInfo->dwTypeData != NULL)
{
HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData);
ItemInfo->dwTypeData = NULL;
}
}
/***********************************************************************
* MenuGetAllRosMenuItemInfo
*
* Get full information about all menu items
*/
static INT FASTCALL
MenuGetAllRosMenuItemInfo(HMENU Menu, PROSMENUITEMINFO *ItemInfo)
{
DWORD BufSize;
BufSize = NtUserBuildMenuItemList(Menu, (VOID *) 1, 0, 0);
if (BufSize == (DWORD) -1 || BufSize == 0)
{
return -1;
}
*ItemInfo = HeapAlloc(GetProcessHeap(), 0, BufSize);
if (NULL == *ItemInfo)
{
return -1;
}
return NtUserBuildMenuItemList(Menu, *ItemInfo, BufSize, 0);
}
/***********************************************************************
* MenuCleanupAllRosMenuItemInfo
*
* Cleanup after use of MenuGetAllRosMenuItemInfo
*/
static VOID FASTCALL
MenuCleanupAllRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
{
HeapFree(GetProcessHeap(), 0, ItemInfo);
}
/***********************************************************************
* MenuLoadBitmaps
*
* Load the arrow bitmap. We can't do this from MenuInit since user32
* can also be used (and thus initialized) from text-mode.
*/
static void FASTCALL
MenuLoadBitmaps(VOID)
{
/* Load system buttons bitmaps */
if (NULL == BmpSysMenu)
{
BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
}
}
/***********************************************************************
* MenuGetBitmapItemSize
*
* Get the size of a bitmap item.
*/
static void FASTCALL
MenuGetBitmapItemSize(PROSMENUITEMINFO lpitem, SIZE *Size, HWND WndOwner)
{
BITMAP Bm;
HBITMAP Bmp = lpitem->hbmpItem;
Size->cx = Size->cy = 0;
/* check if there is a magic menu item associated with this item */
if (0 != Bmp && IS_MAGIC_ITEM((INT)(Bmp)))
{
switch((INT_PTR) Bmp)
{
case (INT_PTR)HBMMENU_CALLBACK:
{
MEASUREITEMSTRUCT measItem;
measItem.CtlType = ODT_MENU;
measItem.CtlID = 0;
measItem.itemID = lpitem->wID;
measItem.itemWidth = lpitem->Rect.right - lpitem->Rect.left;
measItem.itemHeight = lpitem->Rect.bottom - lpitem->Rect.top;
measItem.itemData = lpitem->dwItemData;
SendMessageW( WndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
Size->cx = measItem.itemWidth;
Size->cy = measItem.itemHeight;
return;
}
break;
case (INT_PTR) HBMMENU_SYSTEM:
if (0 != lpitem->dwItemData)
{
Bmp = (HBITMAP) lpitem->dwItemData;
break;
}
/* fall through */
case (INT_PTR) HBMMENU_MBAR_RESTORE:
case (INT_PTR) HBMMENU_MBAR_MINIMIZE:
case (INT_PTR) HBMMENU_MBAR_CLOSE:
case (INT_PTR) HBMMENU_MBAR_MINIMIZE_D:
case (INT_PTR) HBMMENU_MBAR_CLOSE_D:
/* FIXME: Why we need to subtract these magic values? */
/* to make them smaller than the menu bar? */
Size->cx = GetSystemMetrics(SM_CXSIZE) - 2;
Size->cy = GetSystemMetrics(SM_CYSIZE) - 4;
return;
case (INT_PTR) HBMMENU_POPUP_CLOSE:
case (INT_PTR) HBMMENU_POPUP_RESTORE:
case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
default:
FIXME("Magic menu bitmap not implemented\n");
return;
}
}
if (GetObjectW(Bmp, sizeof(BITMAP), &Bm))
{
Size->cx = Bm.bmWidth;
Size->cy = Bm.bmHeight;
}
}
/***********************************************************************
* MenuDrawBitmapItem
*
* Draw a bitmap item.
*/
static void FASTCALL
MenuDrawBitmapItem(HDC Dc, PROSMENUITEMINFO Item, const RECT *Rect,
HMENU hmenu, HWND WndOwner, UINT odaction, BOOL MenuBar)
{
BITMAP Bm;
DWORD Rop;
HDC DcMem;
HBITMAP Bmp;
int w = Rect->right - Rect->left;
int h = Rect->bottom - Rect->top;
int BmpXoffset = 0;
int Left, Top;
HBITMAP hbmpToDraw = (HBITMAP) Item->hbmpItem;
Bmp = hbmpToDraw;
/* Check if there is a magic menu item associated with this item */
if (IS_MAGIC_ITEM(hbmpToDraw))
{
UINT Flags = 0;
RECT r;
r = *Rect;
switch ((INT_PTR)hbmpToDraw)
{
case (INT_PTR) HBMMENU_SYSTEM:
if (NULL != Item->dwTypeData)
{
Bmp = (HBITMAP)Item->dwTypeData;
if (! GetObjectW(Bmp, sizeof(BITMAP), &Bm))
{
return;
}
}
else
{
if (!BmpSysMenu) BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
Bmp = BmpSysMenu;
if (! GetObjectW(Bmp, sizeof(BITMAP), &Bm))
{
return;
}
/* only use right half of the bitmap */
BmpXoffset = Bm.bmWidth / 2;
Bm.bmWidth -= BmpXoffset;
}
goto got_bitmap;
case (INT_PTR) HBMMENU_MBAR_RESTORE:
Flags = DFCS_CAPTIONRESTORE;
break;
case (INT_PTR) HBMMENU_MBAR_MINIMIZE:
r.right += 1;
Flags = DFCS_CAPTIONMIN;
break;
case (INT_PTR) HBMMENU_MBAR_MINIMIZE_D:
r.right += 1;
Flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
break;
case (INT_PTR) HBMMENU_MBAR_CLOSE:
Flags = DFCS_CAPTIONCLOSE;
break;
case (INT_PTR) HBMMENU_MBAR_CLOSE_D:
Flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
break;
case (INT_PTR) HBMMENU_CALLBACK:
{
DRAWITEMSTRUCT drawItem;
POINT origorg;
drawItem.CtlType = ODT_MENU;
drawItem.CtlID = 0;
drawItem.itemID = Item->wID;
drawItem.itemAction = odaction;
drawItem.itemState = (Item->fState & MF_CHECKED)?ODS_CHECKED:0;
drawItem.itemState |= (Item->fState & MF_DEFAULT)?ODS_DEFAULT:0;
drawItem.itemState |= (Item->fState & MF_DISABLED)?ODS_DISABLED:0;
drawItem.itemState |= (Item->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
drawItem.itemState |= (Item->fState & MF_HILITE)?ODS_SELECTED:0;
drawItem.hwndItem = (HWND)hmenu;
drawItem.hDC = Dc;
drawItem.rcItem = *Rect;
drawItem.itemData = Item->dwItemData;
/* some applications make this assumption on the DC's origin */
SetViewportOrgEx( Dc, Item->Rect.left, Item->Rect.top, &origorg);
OffsetRect( &drawItem.rcItem, - Item->Rect.left, - Item->Rect.top);
SendMessageW( WndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
SetViewportOrgEx( Dc, origorg.x, origorg.y, NULL);
return;
}
break;
case (INT_PTR) HBMMENU_POPUP_CLOSE:
case (INT_PTR) HBMMENU_POPUP_RESTORE:
case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
default:
FIXME("Magic menu bitmap not implemented\n");
return;
}
InflateRect(&r, -1, -1);
if (0 != (Item->fState & MF_HILITE))
{
Flags |= DFCS_PUSHED;
}
DrawFrameControl(Dc, &r, DFC_CAPTION, Flags);
return;
}
if (NULL == Bmp || ! GetObjectW(Bmp, sizeof(BITMAP), &Bm))
{
return;
}
got_bitmap:
DcMem = CreateCompatibleDC(Dc);
SelectObject(DcMem, Bmp);
/* handle fontsize > bitmap_height */
Top = (Bm.bmHeight < h) ? Rect->top + (h - Bm.bmHeight) / 2 : Rect->top;
Left = Rect->left;
Rop= ((Item->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmpToDraw)) ? NOTSRCCOPY : SRCCOPY;
if ((Item->fState & MF_HILITE) && Item->hbmpItem)
{
SetBkColor(Dc, GetSysColor(COLOR_HIGHLIGHT));
}
BitBlt(Dc, Left, Top, w, h, DcMem, BmpXoffset, 0, Rop);
DeleteDC(DcMem);
}
/***********************************************************************
* MenuDrawMenuItem
*
* Draw a single menu item.
*/
static void FASTCALL
MenuDrawMenuItem(HWND hWnd, PROSMENUINFO MenuInfo, HWND WndOwner, HDC Dc,
PROSMENUITEMINFO Item, UINT Height, BOOL MenuBar, UINT Action)
{
RECT Rect;
PWCHAR Text;
BOOL flat_menu = FALSE;
int bkgnd;
PWINDOW Wnd = ValidateHwnd(hWnd);
if (!Wnd)
return;
if (0 != (Item->fType & MF_SYSMENU))
{
if ( (Wnd->Style & WS_MINIMIZE))
{
UserGetInsideRectNC(Wnd, &Rect);
UserDrawSysMenuButton(hWnd, Dc, &Rect,
Item->fState & (MF_HILITE | MF_MOUSESELECT));
}
return;
}
SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
bkgnd = (MenuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
/* Setup colors */
if (0 != (Item->fState & MF_HILITE))
{
if (MenuBar && !flat_menu)
{
SetTextColor(Dc, GetSysColor(COLOR_MENUTEXT));
SetBkColor(Dc, GetSysColor(COLOR_MENU));
}
else
{
if (0 != (Item->fState & MF_GRAYED))
{
SetTextColor(Dc, GetSysColor(COLOR_GRAYTEXT));
}
else
{
SetTextColor(Dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
}
SetBkColor(Dc, GetSysColor(COLOR_HIGHLIGHT));
}
}
else
{
if (0 != (Item->fState & MF_GRAYED))
{
SetTextColor(Dc, GetSysColor(COLOR_GRAYTEXT));
}
else
{
SetTextColor(Dc, GetSysColor(COLOR_MENUTEXT));
}
SetBkColor(Dc, GetSysColor(bkgnd));
}
Rect = Item->Rect;
if (Item->fType & MF_OWNERDRAW)
{
/*
** Experimentation under Windows reveals that an owner-drawn
** menu is given the rectangle which includes the space it requested
** in its response to WM_MEASUREITEM _plus_ width for a checkmark
** and a popup-menu arrow. This is the value of lpitem->rect.
** Windows will leave all drawing to the application except for
** the popup-menu arrow. Windows always draws that itself, after
** the menu owner has finished drawing.
*/
DRAWITEMSTRUCT dis;
dis.CtlType = ODT_MENU;
dis.CtlID = 0;
dis.itemID = Item->wID;
dis.itemData = (DWORD)Item->dwItemData;
dis.itemState = 0;
if (0 != (Item->fState & MF_CHECKED))
{
dis.itemState |= ODS_CHECKED;
}
if (0 != (Item->fState & MF_GRAYED))
{
dis.itemState |= ODS_GRAYED | ODS_DISABLED;
}
if (0 != (Item->fState & MF_HILITE))
{
dis.itemState |= ODS_SELECTED;
}
dis.itemAction = Action; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
dis.hwndItem = (HWND) MenuInfo->Self;
dis.hDC = Dc;
dis.rcItem = Rect;
TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
"hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hWnd,
dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
dis.rcItem.bottom);
SendMessageW(WndOwner, WM_DRAWITEM, 0, (LPARAM) &dis);
/* Draw the popup-menu arrow */
if (0 != (Item->fType & MF_POPUP))
{
INT y = Rect.top + Rect.bottom;
UINT CheckBitmapWidth = GetSystemMetrics(SM_CXMENUCHECK);
UINT CheckBitmapHeight = GetSystemMetrics(SM_CYMENUCHECK);
RECT r;
HBITMAP bm = CreateBitmap(CheckBitmapWidth, CheckBitmapHeight, 1, 1, NULL);
HDC DcMem = CreateCompatibleDC(Dc);
SelectObject(DcMem, bm);
SetRect( &r, 0, 0, CheckBitmapWidth, CheckBitmapHeight);
DrawFrameControl(DcMem, &r, DFC_MENU, DFCS_MENUARROW);
BitBlt(Dc, Rect.right - CheckBitmapWidth, (y - r.bottom) / 2, r.right, r.bottom, DcMem, 0, 0, SRCCOPY );
DeleteDC(DcMem);
DeleteObject(bm);
/*
SetRect(&r,Rect.right - CheckBitmapWidth, (y - CheckBitmapHeight) / 2, Rect.right, Rect.bottom);
DrawFrameControl(Dc, &r, DFC_MENU, DFCS_MENUARROW);
*/
}
return;
}
TRACE("rect={%ld,%ld,%ld,%ld}\n", Item->Rect.left, Item->Rect.top,
Item->Rect.right, Item->Rect.bottom);
if (MenuBar && 0 != (Item->fType & MF_SEPARATOR))
{
return;
}
if (Item->fState & MF_HILITE)
{
if (flat_menu)
{
InflateRect (&Rect, -1, -1);
FillRect(Dc, &Rect, GetSysColorBrush(COLOR_MENUHILIGHT));
InflateRect (&Rect, 1, 1);
FrameRect(Dc, &Rect, GetSysColorBrush(COLOR_HIGHLIGHT));
}
else
{
if (MenuBar)
{
DrawEdge(Dc, &Rect, BDR_SUNKENOUTER, BF_RECT);
}
else
{
FillRect(Dc, &Rect, GetSysColorBrush(COLOR_HIGHLIGHT));
}
}
}
else
{
FillRect(Dc, &Rect, GetSysColorBrush(bkgnd));
}
SetBkMode(Dc, TRANSPARENT);
/* vertical separator */
if (! MenuBar && 0 != (Item->fType & MF_MENUBARBREAK))
{
HPEN oldPen;
RECT rc = Rect;
rc.left -= 3;
rc.top = 3;
rc.bottom = Height - 3;
if (flat_menu)
{
oldPen = SelectObject( Dc, GetSysColorPen(COLOR_BTNSHADOW) );
MoveToEx( Dc, rc.left, rc.top, NULL );
LineTo( Dc, rc.left, rc.bottom );
SelectObject( Dc, oldPen );
}
else
DrawEdge(Dc, &rc, EDGE_ETCHED, BF_LEFT);
}
/* horizontal separator */
if (0 != (Item->fType & MF_SEPARATOR))
{
HPEN oldPen;
RECT rc = Rect;
rc.left++;
rc.right--;
rc.top += SEPARATOR_HEIGHT / 2;
if (flat_menu)
{
oldPen = SelectObject( Dc, GetSysColorPen(COLOR_BTNSHADOW) );
MoveToEx( Dc, rc.left, rc.top, NULL );
LineTo( Dc, rc.right, rc.top );
SelectObject( Dc, oldPen );
}
else
DrawEdge(Dc, &rc, EDGE_ETCHED, BF_TOP);
return;
}
#if 0
/* helper lines for debugging */
/* This is a very good test tool when hacking menus! (JT) 07/16/2006 */
FrameRect(Dc, &Rect, GetStockObject(BLACK_BRUSH));
SelectObject(Dc, GetSysColorPen(COLOR_WINDOWFRAME));
MoveToEx(Dc, Rect.left, (Rect.top + Rect.bottom) / 2, NULL);
LineTo(Dc, Rect.right, (Rect.top + Rect.bottom) / 2);
#endif
if (! MenuBar)
{
INT y = Rect.top + Rect.bottom;
RECT Rc = Rect;
UINT CheckBitmapWidth = GetSystemMetrics(SM_CXMENUCHECK);
UINT CheckBitmapHeight = GetSystemMetrics(SM_CYMENUCHECK);
int checked = FALSE;
/* Draw the check mark
*
* FIXME:
* Custom checkmark bitmaps are monochrome but not always 1bpp.
*/
if( !(MenuInfo->dwStyle & MNS_NOCHECK))
{
HBITMAP bm = 0 != (Item->fState & MF_CHECKED) ? Item->hbmpChecked : Item->hbmpUnchecked;
if (NULL != bm) /* we have a custom bitmap */
{
HDC DcMem = CreateCompatibleDC(Dc);
SelectObject(DcMem, bm);
BitBlt(Dc, Rc.left, (y - CheckBitmapHeight) / 2,
CheckBitmapWidth, CheckBitmapHeight,
DcMem, 0, 0, SRCCOPY);
DeleteDC(DcMem);
checked = TRUE;
}
else if (0 != (Item->fState & MF_CHECKED)) /* standard bitmaps */
{
RECT r;
HBITMAP bm = CreateBitmap(CheckBitmapWidth, CheckBitmapHeight, 1, 1, NULL);
HDC DcMem = CreateCompatibleDC(Dc);
SelectObject(DcMem, bm);
SetRect( &r, 0, 0, CheckBitmapWidth, CheckBitmapHeight);
DrawFrameControl(DcMem, &r, DFC_MENU,
0 != (Item->fType & MFT_RADIOCHECK) ?
DFCS_MENUBULLET : DFCS_MENUCHECK);
BitBlt(Dc, Rc.left, (y - r.bottom) / 2, r.right, r.bottom,
DcMem, 0, 0, SRCCOPY );
DeleteDC(DcMem);
DeleteObject(bm);
checked = TRUE;
}
}
if ((Item->hbmpItem)&& !( checked && (MenuInfo->dwStyle & MNS_CHECKORBMP)))
{
MenuDrawBitmapItem(Dc, Item, &Rect, MenuInfo->Self, WndOwner, Action, MenuBar);
}
/* Draw the popup-menu arrow */
if (0 != (Item->fType & MF_POPUP))
{
RECT r;
HBITMAP bm = CreateBitmap(CheckBitmapWidth, CheckBitmapHeight, 1, 1, NULL);
HDC DcMem = CreateCompatibleDC(Dc);
SelectObject(DcMem, bm);
SetRect( &r, 0, 0, CheckBitmapWidth, CheckBitmapHeight);
DrawFrameControl(DcMem, &r, DFC_MENU, DFCS_MENUARROW);
BitBlt(Dc, Rect.right - CheckBitmapWidth, (y - r.bottom) / 2, r.right, r.bottom, DcMem, 0, 0, SRCCOPY );
DeleteDC(DcMem);
DeleteObject(bm);
/*
SetRect(&r,Rect.right - CheckBitmapWidth, (y - CheckBitmapHeight) / 2, Rect.right, Rect.bottom);
DrawFrameControl(Dc, &r, DFC_MENU, DFCS_MENUARROW);
*/
}
Rect.left += 4;
if( !(MenuInfo->dwStyle & MNS_NOCHECK))
Rect.left += CheckBitmapWidth;
Rect.right -= CheckBitmapWidth;
}
else if (Item->hbmpItem) /* Draw the bitmap */
{
MenuDrawBitmapItem(Dc, Item, &Rect, MenuInfo->Self, WndOwner, Action, MenuBar);
}
/* No bitmap - process text if present */
if (Item->Text)
{
register int i = 0;
HFONT FontOld = NULL;
UINT uFormat = MenuBar ? DT_CENTER | DT_VCENTER | DT_SINGLELINE
: DT_LEFT | DT_VCENTER | DT_SINGLELINE;
if( !(MenuInfo->dwStyle & MNS_CHECKORBMP))
Rect.left += MenuInfo->maxBmpSize.cx;
if (0 != (Item->fState & MFS_DEFAULT))
{
FontOld = SelectObject(Dc, hMenuFontBold);
}
if (MenuBar)
{
Rect.left += MENU_BAR_ITEMS_SPACE / 2;
Rect.right -= MENU_BAR_ITEMS_SPACE / 2;
}
if (Item->hbmpItem == HBMMENU_CALLBACK || MenuInfo->maxBmpSize.cx != 0 )
{
Rect.left += MenuInfo->maxBmpSize.cx;
Rect.right -= MenuInfo->maxBmpSize.cx;
}
Text = (PWCHAR) Item->dwTypeData;
if(Text)
{
for (i = 0; L'\0' != Text[i]; i++)
{
if (L'\t' == Text[i] || L'\b' == Text[i])
{
break;
}
}
}
if (0 != (Item->fState & MF_GRAYED))
{
if (0 == (Item->fState & MF_HILITE))
{
++Rect.left; ++Rect.top; ++Rect.right; ++Rect.bottom;
SetTextColor(Dc, RGB(0xff, 0xff, 0xff));
DrawTextW(Dc, Text, i, &Rect, uFormat);
--Rect.left; --Rect.top; --Rect.right; --Rect.bottom;
}
SetTextColor(Dc, RGB(0x80, 0x80, 0x80));
}
DrawTextW(Dc, Text, i, &Rect, uFormat);
/* paint the shortcut text */
if (! MenuBar && L'\0' != Text[i]) /* There's a tab or flush-right char */
{
if (L'\t' == Text[i])
{
Rect.left = Item->XTab;
uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
}
else
{
Rect.right = Item->XTab;
uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
}
if (0 != (Item->fState & MF_GRAYED))
{
if (0 == (Item->fState & MF_HILITE))
{
++Rect.left; ++Rect.top; ++Rect.right; ++Rect.bottom;
SetTextColor(Dc, RGB(0xff, 0xff, 0xff));
DrawTextW(Dc, Text + i + 1, -1, &Rect, uFormat);
--Rect.left; --Rect.top; --Rect.right; --Rect.bottom;
}
SetTextColor(Dc, RGB(0x80, 0x80, 0x80));
}
DrawTextW(Dc, Text + i + 1, -1, &Rect, uFormat);
}
if (NULL != FontOld)
{
SelectObject(Dc, FontOld);
}
}
}
/***********************************************************************
* MenuDrawPopupMenu
*
* Paint a popup menu.
*/
static void FASTCALL
MenuDrawPopupMenu(HWND Wnd, HDC Dc, HMENU Menu)
{
HBRUSH PrevBrush = NULL;
HPEN PrevPen;
RECT Rect;
ROSMENUINFO MenuInfo;
ROSMENUITEMINFO ItemInfo;
UINT u;
TRACE("wnd=%x dc=%x menu=%x\n", Wnd, Dc, Menu);
GetClientRect(Wnd, &Rect);
if (NULL != (PrevBrush = SelectObject(Dc, GetSysColorBrush(COLOR_MENU)))
&& NULL != SelectObject(Dc, hMenuFont))
{
Rectangle(Dc, Rect.left, Rect.top, Rect.right, Rect.bottom);
PrevPen = SelectObject(Dc, GetStockObject(NULL_PEN));
if (NULL != PrevPen)
{
BOOL flat_menu = FALSE;
SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
if (flat_menu)
FrameRect(Dc, &Rect, GetSysColorBrush(COLOR_BTNSHADOW));
else
DrawEdge(Dc, &Rect, EDGE_RAISED, BF_RECT);
/* draw menu items */
if (MenuGetRosMenuInfo(&MenuInfo, Menu) && 0 != MenuInfo.MenuItemCount)
{
MenuInitRosMenuItemInfo(&ItemInfo);
for (u = 0; u < MenuInfo.MenuItemCount; u++)
{
if (MenuGetRosMenuItemInfo(MenuInfo.Self, u, &ItemInfo))
{
MenuDrawMenuItem(Wnd, &MenuInfo, MenuInfo.WndOwner, Dc, &ItemInfo,
MenuInfo.Height, FALSE, ODA_DRAWENTIRE);
}
}
MenuCleanupRosMenuItemInfo(&ItemInfo);
}
}
else
{
SelectObject(Dc, PrevBrush);
}
}
}
static LRESULT WINAPI
PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
switch(Message)
{
case WM_CREATE:
{
CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
SetWindowLongPtrW(Wnd, 0, (LONG) cs->lpCreateParams);
return 0;
}
case WM_MOUSEACTIVATE: /* We don't want to be activated */
return MA_NOACTIVATE;
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(Wnd, &ps);
MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrW(Wnd, 0));
EndPaint(Wnd, &ps);
return 0;
}
case WM_ERASEBKGND:
return 1;
case WM_DESTROY:
/* zero out global pointer in case resident popup window was destroyed. */
if (Wnd == TopPopup)
{
TopPopup = NULL;
}
break;
case WM_SHOWWINDOW:
if (0 != wParam)
{
if (0 == GetWindowLongPtrW(Wnd, 0))
{
OutputDebugStringA("no menu to display\n");
}
}
else
{
SetWindowLongPtrW(Wnd, 0, 0);
}
break;
case MM_SETMENUHANDLE:
SetWindowLongPtrW(Wnd, 0, wParam);
break;
case MM_GETMENUHANDLE:
return GetWindowLongPtrW(Wnd, 0);
default:
return DefWindowProcW(Wnd, Message, wParam, lParam);
}
return 0;
}
/**********************************************************************
* MENUEX_ParseResource
*
* Parse an extended menu resource and add items to the menu.
* Return a pointer to the end of the resource.
*
* FIXME - should we be passing an LPCSTR to a predominantly UNICODE function?
*/
static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
{
WORD resinfo;
do
{
MENUITEMINFOW mii;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
mii.fType = GET_DWORD(res);
res += sizeof(DWORD);
mii.fState = GET_DWORD(res);
res += sizeof(DWORD);
mii.wID = GET_DWORD(res);
res += sizeof(DWORD);
resinfo = GET_WORD(res);
res += sizeof(WORD);
/* Align the text on a word boundary. */
res += (~((int)res - 1)) & 1;
mii.dwTypeData = (LPWSTR) res;
res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
/* Align the following fields on a dword boundary. */
res += (~((int)res - 1)) & 3;
if (resinfo & 1) /* Pop-up? */
{
/* DWORD helpid = GET_DWORD(res); FIXME: use this. */
res += sizeof(DWORD);
mii.hSubMenu = CreatePopupMenu();
if (!mii.hSubMenu)
return NULL;
if (!(res = MENUEX_ParseResource(res, mii.hSubMenu)))
{
DestroyMenu(mii.hSubMenu);
return NULL;
}
mii.fMask |= MIIM_SUBMENU;
mii.fType |= MF_POPUP;
mii.wID = (UINT) mii.hSubMenu;
}
else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
{
mii.fType |= MF_SEPARATOR;
}
InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
}
while (!(resinfo & MF_END));
return res;
}
/**********************************************************************
* MENU_ParseResource
*
* Parse a standard menu resource and add items to the menu.
* Return a pointer to the end of the resource.
*
* NOTE: flags is equivalent to the mtOption field
*/
static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
{
WORD flags, id = 0;
HMENU hSubMenu;
LPCSTR str;
BOOL end = FALSE;
do
{
flags = GET_WORD(res);
/* remove MF_END flag before passing it to AppendMenu()! */
end = (flags & MF_END);
if(end) flags ^= MF_END;
res += sizeof(WORD);
if(!(flags & MF_POPUP))
{
id = GET_WORD(res);
res += sizeof(WORD);
}
str = res;
if(!unicode)
res += strlen(str) + 1;
else
res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
if (flags & MF_POPUP)
{
hSubMenu = CreatePopupMenu();
if(!hSubMenu) return NULL;
if(!(res = MENU_ParseResource(res, hSubMenu, unicode)))
return NULL;
if(!unicode)
AppendMenuA(hMenu, flags, (UINT)hSubMenu, str);
else
AppendMenuW(hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str);
}
else /* Not a popup */
{
if (*str == 0)
flags = MF_SEPARATOR;
if (flags & MF_SEPARATOR)
{
if (!(flags & (MF_GRAYED | MF_DISABLED)))
flags |= MF_GRAYED | MF_DISABLED;
}
if(!unicode)
AppendMenuA(hMenu, flags, id, *str ? str : NULL);
else
AppendMenuW(hMenu, flags, id,
*(LPCWSTR)str ? (LPCWSTR)str : NULL);
}
} while(!end);
return res;
}
NTSTATUS WINAPI
User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength)
{
LRESULT Result;
Result = (LRESULT)LoadMenuW(User32Instance, L"SYSMENU");
return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS));
}
BOOL
MenuInit(VOID)
{
NONCLIENTMETRICSW ncm;
/* get the menu font */
if(!hMenuFont || !hMenuFontBold)
{
ncm.cbSize = sizeof(ncm);
if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
{
DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
return FALSE;
}
hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont);
if(hMenuFont == NULL)
{
DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
return FALSE;
}
ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont);
if(hMenuFontBold == NULL)
{
DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
DeleteObject(hMenuFont);
hMenuFont = NULL;
return FALSE;
}
}
return TRUE;
}
VOID
MenuCleanup(VOID)
{
if (hMenuFont)
{
DeleteObject(hMenuFont);
hMenuFont = NULL;
}
if (hMenuFontBold)
{
DeleteObject(hMenuFontBold);
hMenuFontBold = NULL;
}
}
/***********************************************************************
* MenuCalcItemSize
*
* Calculate the size of the menu item and store it in ItemInfo->rect.
*/
static void FASTCALL
MenuCalcItemSize(HDC Dc, PROSMENUITEMINFO ItemInfo, PROSMENUINFO MenuInfo, HWND WndOwner,
INT OrgX, INT OrgY, BOOL MenuBar)
{
PWCHAR p;
INT itemheight = 0;
UINT CheckBitmapWidth = GetSystemMetrics(SM_CXMENUCHECK);
TRACE("dc=%x owner=%x (%d,%d)\n", Dc, WndOwner, OrgX, OrgY);
MenuCharSize.cx = GdiGetCharDimensions( Dc, NULL, &MenuCharSize.cy );
SetRect(&ItemInfo->Rect, OrgX, OrgY, OrgX, OrgY);
if (0 != (ItemInfo->fType & MF_OWNERDRAW))
{
/*
** Experimentation under Windows reveals that an owner-drawn
** menu is expected to return the size of the content part of
** the menu item, not including the checkmark nor the submenu
** arrow. Windows adds those values itself and returns the
** enlarged rectangle on subsequent WM_DRAWITEM messages.
*/
MEASUREITEMSTRUCT mis;
mis.CtlType = ODT_MENU;
mis.CtlID = 0;
mis.itemID = ItemInfo->wID;
mis.itemData = (DWORD)ItemInfo->dwItemData;
mis.itemHeight = HIWORD( GetDialogBaseUnits());
mis.itemWidth = 0;
SendMessageW(WndOwner, WM_MEASUREITEM, 0, (LPARAM) &mis);
/* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
* width of a menufont character to the width of an owner-drawn menu.
*/
ItemInfo->Rect.right += mis.itemWidth + 2 * MenuCharSize.cx;
if (MenuBar)
{
/* under at least win95 you seem to be given a standard
height for the menu and the height value is ignored */
ItemInfo->Rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
}
else
{
ItemInfo->Rect.bottom += mis.itemHeight;
}
TRACE("id=%04x size=%dx%d\n", ItemInfo->wID, mis.itemWidth, mis.itemHeight);
return;
}
if (0 != (ItemInfo->fType & MF_SEPARATOR))
{
ItemInfo->Rect.bottom += SEPARATOR_HEIGHT;
if( !MenuBar)
ItemInfo->Rect.right += CheckBitmapWidth + MenuCharSize.cx;
return;
}
ItemInfo->XTab = 0;
if (ItemInfo->hbmpItem)
{
SIZE Size;
if (!MenuBar) /* hbmpItem */
{
MenuGetBitmapItemSize(ItemInfo, &Size, WndOwner );
/* Keep the size of the bitmap in callback mode to be able
* to draw it correctly */
ItemInfo->Rect.right = ItemInfo->Rect.left + Size.cx;
if (MenuInfo->maxBmpSize.cx < abs(Size.cx) + MENU_ITEM_HBMP_SPACE ||
MenuInfo->maxBmpSize.cy < abs(Size.cy))
{
MenuInfo->maxBmpSize.cx = abs(Size.cx) + MENU_ITEM_HBMP_SPACE;
MenuInfo->maxBmpSize.cy = abs(Size.cy);
}
MenuSetRosMenuInfo(MenuInfo);
ItemInfo->Rect.right += Size.cx + 2;
itemheight = Size.cy + 2;
if( !(MenuInfo->dwStyle & MNS_NOCHECK))
ItemInfo->Rect.right += 2 * CheckBitmapWidth;
ItemInfo->Rect.right += 4 + MenuCharSize.cx;
ItemInfo->XTab = ItemInfo->Rect.right;
ItemInfo->Rect.right += CheckBitmapWidth;
}
else /* hbmpItem & MenuBar */
{
MenuGetBitmapItemSize(ItemInfo, &Size, WndOwner );
ItemInfo->Rect.right += Size.cx;
if( ItemInfo->Text) ItemInfo->Rect.right += 2;
itemheight = Size.cy;
/* Special case: Minimize button doesn't have a space behind it. */
if (ItemInfo->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE ||
ItemInfo->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE_D)
ItemInfo->Rect.right -= 1;
}
}
else if (!MenuBar)
{
if( !(MenuInfo->dwStyle & MNS_NOCHECK))
ItemInfo->Rect.right += CheckBitmapWidth;
ItemInfo->Rect.right += 4 + MenuCharSize.cx;
ItemInfo->XTab = ItemInfo->Rect.right;
ItemInfo->Rect.right += CheckBitmapWidth;
}
/* it must be a text item - unless it's the system menu */
if (0 == (ItemInfo->fType & MF_SYSMENU) && ItemInfo->Text)
{
HFONT hfontOld = NULL;
RECT rc = ItemInfo->Rect;
LONG txtheight, txtwidth;
if ( ItemInfo->fState & MFS_DEFAULT )
{
hfontOld = SelectObject( Dc, hMenuFontBold );
}
if (MenuBar)
{
txtheight = DrawTextW( Dc, ItemInfo->dwTypeData, -1, &rc,
DT_SINGLELINE|DT_CALCRECT);
ItemInfo->Rect.right += rc.right - rc.left;
itemheight = max( max( itemheight, txtheight),
GetSystemMetrics( SM_CYMENU) - 1);
ItemInfo->Rect.right += 2 * MenuCharSize.cx;
}
else
{
if ((p = strchrW( ItemInfo->dwTypeData, '\t' )) != NULL)
{
RECT tmprc = rc;
LONG tmpheight;
int n = (int)( p - ItemInfo->dwTypeData);
/* Item contains a tab (only meaningful in popup menus) */
/* get text size before the tab */
txtheight = DrawTextW( Dc, ItemInfo->dwTypeData, n, &rc,
DT_SINGLELINE|DT_CALCRECT);
txtwidth = rc.right - rc.left;
p += 1; /* advance past the Tab */
/* get text size after the tab */
tmpheight = DrawTextW( Dc, p, -1, &tmprc, DT_SINGLELINE|DT_CALCRECT);
ItemInfo->XTab += txtwidth;
txtheight = max( txtheight, tmpheight);
txtwidth += MenuCharSize.cx + /* space for the tab */
tmprc.right - tmprc.left; /* space for the short cut */
}
else
{
txtheight = DrawTextW( Dc, ItemInfo->dwTypeData, -1, &rc,
DT_SINGLELINE|DT_CALCRECT);
txtwidth = rc.right - rc.left;
ItemInfo->XTab += txtwidth;
}
ItemInfo->Rect.right += 2 + txtwidth;
itemheight = max( itemheight, max( txtheight + 2, MenuCharSize.cy + 4));
}
if (hfontOld) SelectObject (Dc, hfontOld);
}
else if( MenuBar)
{
itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
}
ItemInfo->Rect.bottom += itemheight;
TRACE("(%ld,%ld)-(%ld,%ld)\n", ItemInfo->Rect.left, ItemInfo->Rect.top, ItemInfo->Rect.right, ItemInfo->Rect.bottom);
}
/***********************************************************************
* MenuPopupMenuCalcSize
*
* Calculate the size of a popup menu.
*/
static void FASTCALL
MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo, HWND WndOwner)
{
ROSMENUITEMINFO ItemInfo;
HDC Dc;
int Start, i;
int OrgX, OrgY, MaxX, MaxTab, MaxTabWidth;
MenuInfo->Width = MenuInfo->Height = 0;
if (0 == MenuInfo->MenuItemCount)
{
MenuSetRosMenuInfo(MenuInfo);
return;
}
Dc = GetDC(NULL);
SelectObject(Dc, hMenuFont);
Start = 0;
MaxX = 2 + 1;
MenuInfo->maxBmpSize.cx = 0;
MenuInfo->maxBmpSize.cy = 0;
MenuInitRosMenuItemInfo(&ItemInfo);
while (Start < MenuInfo->MenuItemCount)
{
OrgX = MaxX;
OrgY = 2;
MaxTab = MaxTabWidth = 0;
/* Parse items until column break or end of menu */
for (i = Start; i < MenuInfo->MenuItemCount; i++)
{
if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo))
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
MenuSetRosMenuInfo(MenuInfo);
return;
}
if (i != Start &&
0 != (ItemInfo.fType & (MF_MENUBREAK | MF_MENUBARBREAK)))
{
break;
}
MenuCalcItemSize(Dc, &ItemInfo, MenuInfo, WndOwner, OrgX, OrgY, FALSE);
if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo))
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
MenuSetRosMenuInfo(MenuInfo);
return;
}
// Not sure here,, The patch from wine removes this.
// if (0 != (ItemInfo.fType & MF_MENUBARBREAK))
// {
// OrgX++;
// }
MaxX = max(MaxX, ItemInfo.Rect.right);
OrgY = ItemInfo.Rect.bottom;
if ((ItemInfo.Text) && 0 != ItemInfo.XTab)
{
MaxTab = max(MaxTab, ItemInfo.XTab);
MaxTabWidth = max(MaxTabWidth, ItemInfo.Rect.right - ItemInfo.XTab);
}
}
/* Finish the column (set all items to the largest width found) */
MaxX = max(MaxX, MaxTab + MaxTabWidth);
while (Start < i)
{
if (MenuGetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo))
{
ItemInfo.Rect.right = MaxX;
if ((ItemInfo.Text) && 0 != ItemInfo.XTab)
{
ItemInfo.XTab = MaxTab;
}
MenuSetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo);
}
Start++;
}
MenuInfo->Height = max(MenuInfo->Height, OrgY);
}
MenuInfo->Width = MaxX;
/* space for 3d border */
MenuInfo->Height += 2;
MenuInfo->Width += 2;
ReleaseDC(NULL, Dc);
MenuCleanupRosMenuItemInfo(&ItemInfo);
MenuSetRosMenuInfo(MenuInfo);
}
/***********************************************************************
* MenuMenuBarCalcSize
*
* FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
* height is off by 1 pixel which causes lengthy window relocations when
* active document window is maximized/restored.
*
* Calculate the size of the menu bar.
*/
static void FASTCALL
MenuMenuBarCalcSize(HDC Dc, LPRECT Rect, PROSMENUINFO MenuInfo, HWND WndOwner)
{
ROSMENUITEMINFO ItemInfo;
int Start, i, OrgX, OrgY, MaxY, HelpPos;
if (NULL == Rect || NULL == MenuInfo)
{
return;
}
if (0 == MenuInfo->MenuItemCount)
{
return;
}
TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
Rect->left, Rect->top, Rect->right, Rect->bottom);
MenuInfo->Width = Rect->right - Rect->left;
MenuInfo->Height = 0;
MaxY = Rect->top + 1;
Start = 0;
HelpPos = -1;
MenuInfo->maxBmpSize.cx = 0;
MenuInfo->maxBmpSize.cy = 0;
MenuInitRosMenuItemInfo(&ItemInfo);
while (Start < MenuInfo->MenuItemCount)
{
if (! MenuGetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo))
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
return;
}
OrgX = Rect->left;
OrgY = MaxY;
/* Parse items until line break or end of menu */
for (i = Start; i < MenuInfo->MenuItemCount; i++)
{
if (-1 == HelpPos && 0 != (ItemInfo.fType & MF_RIGHTJUSTIFY))
{
HelpPos = i;
}
if (i != Start &&
0 != (ItemInfo.fType & (MF_MENUBREAK | MF_MENUBARBREAK)))
{
break;
}
TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", OrgX, OrgY);
MenuCalcItemSize(Dc, &ItemInfo, MenuInfo, WndOwner, OrgX, OrgY, TRUE);
if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo))
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
return;
}
if (ItemInfo.Rect.right > Rect->right)
{
if (i != Start)
{
break;
}
else
{
ItemInfo.Rect.right = Rect->right;
}
}
MaxY = max(MaxY, ItemInfo.Rect.bottom );
OrgX = ItemInfo.Rect.right;
if (i + 1 < MenuInfo->MenuItemCount)
{
if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i + 1, &ItemInfo))
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
return;
}
}
}
/* FIXME: Is this really needed? */ /*NO! it is not needed, why make the
HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */
#if 0
/* Finish the line (set all items to the largest height found) */
while (Start < i)
{
if (MenuGetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo))
{
ItemInfo.Rect.bottom = MaxY;
MenuSetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo);
}
Start++;
}
#else
Start = i; /* This works! */
#endif
}
Rect->bottom = MaxY;
MenuInfo->Height = Rect->bottom - Rect->top;
MenuSetRosMenuInfo(MenuInfo);
if (-1 != HelpPos)
{
/* Flush right all items between the MF_RIGHTJUSTIFY and */
/* the last item (if several lines, only move the last line) */
if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->MenuItemCount - 1, &ItemInfo))
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
return;
}
OrgY = ItemInfo.Rect.top;
OrgX = Rect->right;
for (i = MenuInfo->MenuItemCount - 1; HelpPos <= i; i--)
{
if (i < HelpPos)
{
break; /* done */
}
if (ItemInfo.Rect.top != OrgY)
{
break; /* Other line */
}
if (OrgX <= ItemInfo.Rect.right)
{
break; /* Too far right already */
}
ItemInfo.Rect.left += OrgX - ItemInfo.Rect.right;
ItemInfo.Rect.right = OrgX;
OrgX = ItemInfo.Rect.left;
MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo);
if (HelpPos + 1 <= i &&
! MenuGetRosMenuItemInfo(MenuInfo->Self, i - 1, &ItemInfo))
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
return;
}
}
}
MenuCleanupRosMenuItemInfo(&ItemInfo);
}
/***********************************************************************
* DrawMenuBarTemp (USER32.@)
*
* UNDOCUMENTED !!
*
* called by W98SE desk.cpl Control Panel Applet
*
* Not 100% sure about the param names, but close.
*
* @implemented
*/
DWORD WINAPI
DrawMenuBarTemp(HWND Wnd, HDC DC, LPRECT Rect, HMENU Menu, HFONT Font)
{
ROSMENUINFO MenuInfo;
ROSMENUITEMINFO ItemInfo;
UINT i;
HFONT FontOld = NULL;
BOOL flat_menu = FALSE;
SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
if (NULL == Menu)
{
Menu = GetMenu(Wnd);
}
if (NULL == Font)
{
Font = hMenuFont;
}
if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu))
{
return GetSystemMetrics(SM_CYMENU);
}
TRACE("(%x, %x, %p, %x, %x)\n", Wnd, DC, Rect, Menu, Font);
FontOld = SelectObject(DC, Font);
if (0 == MenuInfo.Height)
{
MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd);
}
Rect->bottom = Rect->top + MenuInfo.Height;
FillRect(DC, Rect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
SelectObject(DC, GetSysColorPen(COLOR_3DFACE));
MoveToEx(DC, Rect->left, Rect->bottom, NULL);
LineTo(DC, Rect->right, Rect->bottom);
if (0 == MenuInfo.MenuItemCount)
{
SelectObject(DC, FontOld);
return GetSystemMetrics(SM_CYMENU);
}
MenuInitRosMenuItemInfo(&ItemInfo);
for (i = 0; i < MenuInfo.MenuItemCount; i++)
{
if (MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo))
{
MenuDrawMenuItem(Wnd, &MenuInfo, Wnd, DC, &ItemInfo,
MenuInfo.Height, TRUE, ODA_DRAWENTIRE);
}
}
MenuCleanupRosMenuItemInfo(&ItemInfo);
SelectObject(DC, FontOld);
return MenuInfo.Height;
}
/***********************************************************************
* MenuDrawMenuBar
*
* Paint a menu bar. Returns the height of the menu bar.
* called from [windows/nonclient.c]
*/
UINT MenuDrawMenuBar(HDC DC, LPRECT Rect, HWND Wnd, BOOL SuppressDraw)
{
ROSMENUINFO MenuInfo;
HFONT FontOld = NULL;
HMENU Menu = GetMenu(Wnd);
if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu))
{
return GetSystemMetrics(SM_CYMENU);
}
if (SuppressDraw)
{
FontOld = SelectObject(DC, hMenuFont);
MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd);
Rect->bottom = Rect->top + MenuInfo.Height;
if (NULL != FontOld)
{
SelectObject(DC, FontOld);
}
return MenuInfo.Height;
}
else
{
return DrawMenuBarTemp(Wnd, DC, Rect, Menu, NULL);
}
}
/***********************************************************************
* MenuInitTracking
*/
static BOOL FASTCALL
MenuInitTracking(HWND Wnd, HMENU Menu, BOOL Popup, UINT Flags)
{
TRACE("Wnd=%p Menu=%p\n", Wnd, Menu);
HideCaret(0);
/* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
if (0 == (Flags & TPM_NONOTIFY))
{
SendMessageW(Wnd, WM_ENTERMENULOOP, Popup, 0);
}
SendMessageW(Wnd, WM_SETCURSOR, (WPARAM) Wnd, HTCAPTION);
if (0 == (Flags & TPM_NONOTIFY))
{
ROSMENUINFO MenuInfo;
SendMessageW(Wnd, WM_INITMENU, (WPARAM)Menu, 0);
MenuGetRosMenuInfo(&MenuInfo, Menu);
if (0 == MenuInfo.Height)
{
/* app changed/recreated menu bar entries in WM_INITMENU
Recalculate menu sizes else clicks will not work */
SetWindowPos(Wnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
}
/* This makes the menus of applications built with Delphi work.
* It also enables menus to be displayed in more than one window,
* but there are some bugs left that need to be fixed in this case.
*/
if(MenuInfo.Self == Menu)
{
MenuInfo.Wnd = Wnd;
MenuSetRosMenuInfo(&MenuInfo);
}
}
return TRUE;
}
/***********************************************************************
* MenuShowPopup
*
* Display a popup menu.
*/
static BOOL FASTCALL
MenuShowPopup(HWND WndOwner, HMENU Menu, UINT Id,
INT X, INT Y, INT XAnchor, INT YAnchor )
{
ROSMENUINFO MenuInfo;
ROSMENUITEMINFO ItemInfo;
UINT Width, Height;
TRACE("owner=%x hmenu=%x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
WndOwner, Menu, Id, X, Y, XAnchor, YAnchor);
if (! MenuGetRosMenuInfo(&MenuInfo, Menu))
{
return FALSE;
}
if (NO_SELECTED_ITEM != MenuInfo.FocusedItem)
{
MenuInitRosMenuItemInfo(&ItemInfo);
if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo))
{
ItemInfo.fMask |= MIIM_STATE;
ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
MenuSetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo);
}
MenuCleanupRosMenuItemInfo(&ItemInfo);
MenuInfo.FocusedItem = NO_SELECTED_ITEM;
}
/* store the owner for DrawItem */
MenuInfo.WndOwner = WndOwner;
MenuSetRosMenuInfo(&MenuInfo);
MenuPopupMenuCalcSize(&MenuInfo, WndOwner);
/* adjust popup menu pos so that it fits within the desktop */
Width = MenuInfo.Width + GetSystemMetrics(SM_CXBORDER);
Height = MenuInfo.Height + GetSystemMetrics(SM_CYBORDER);
if (GetSystemMetrics(SM_CXSCREEN ) < X + Width)
{
if (0 != XAnchor && X >= Width - XAnchor)
{
X -= Width - XAnchor;
}
if (GetSystemMetrics(SM_CXSCREEN) < X + Width)
{
X = GetSystemMetrics(SM_CXSCREEN) - Width;
}
}
if (X < 0 )
{
X = 0;
}
if (GetSystemMetrics(SM_CYSCREEN) < Y + Height)
{
if (0 != YAnchor && Y >= Height + YAnchor)
{
Y -= Height + YAnchor;
}
if (GetSystemMetrics(SM_CYSCREEN) < Y + Height)
{
Y = GetSystemMetrics(SM_CYSCREEN) - Height;
}
}
if (Y < 0 )
{
Y = 0;
}
/* NOTE: In Windows, top menu popup is not owned. */
MenuInfo.Wnd = CreateWindowExW(0, POPUPMENU_CLASS_ATOMW, NULL,
WS_POPUP, X, Y, Width, Height,
WndOwner, 0, (HINSTANCE) GetWindowLongPtrW(WndOwner, GWLP_HINSTANCE),
(LPVOID) MenuInfo.Self);
if (NULL == MenuInfo.Wnd || ! MenuSetRosMenuInfo(&MenuInfo))
{
return FALSE;
}
if (NULL == TopPopup)
{
TopPopup = MenuInfo.Wnd;
}
/* Display the window */
SetWindowPos(MenuInfo.Wnd, HWND_TOPMOST, 0, 0, 0, 0,
SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
UpdateWindow(MenuInfo.Wnd);
return TRUE;
}
/***********************************************************************
* MenuFindSubMenu
*
* Find a Sub menu. Return the position of the submenu, and modifies
* *hmenu in case it is found in another sub-menu.
* If the submenu cannot be found, NO_SELECTED_ITEM is returned.
*/
static UINT FASTCALL
MenuFindSubMenu(HMENU *Menu, HMENU SubTarget)
{
ROSMENUINFO MenuInfo;
ROSMENUITEMINFO ItemInfo;
UINT i;
HMENU SubMenu;
UINT Pos;
if ((HMENU) 0xffff == *Menu
|| ! MenuGetRosMenuInfo(&MenuInfo, *Menu))
{
return NO_SELECTED_ITEM;
}
MenuInitRosMenuItemInfo(&ItemInfo);
for (i = 0; i < MenuInfo.MenuItemCount; i++)
{
if (! MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo))
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
return NO_SELECTED_ITEM;
}
if (0 == (ItemInfo.fType & MF_POPUP))
{
continue;
}
if (ItemInfo.hSubMenu == SubTarget)
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
return i;
}
SubMenu = ItemInfo.hSubMenu;
Pos = MenuFindSubMenu(&SubMenu, SubTarget);
if (NO_SELECTED_ITEM != Pos)
{
*Menu = SubMenu;
return Pos;
}
}
MenuCleanupRosMenuItemInfo(&ItemInfo);
return NO_SELECTED_ITEM;
}
/***********************************************************************
* MenuSelectItem
*/
static void FASTCALL
MenuSelectItem(HWND WndOwner, PROSMENUINFO MenuInfo, UINT Index,
BOOL SendMenuSelect, HMENU TopMenu)
{
HDC Dc;
ROSMENUITEMINFO ItemInfo;
ROSMENUINFO TopMenuInfo;
int Pos;
TRACE("owner=%x menu=%p index=0x%04x select=0x%04x\n", WndOwner, MenuInfo, Index, SendMenuSelect);
if (NULL == MenuInfo || 0 == MenuInfo->MenuItemCount || NULL == MenuInfo->Wnd)
{
return;
}
if (MenuInfo->FocusedItem == Index)
{
return;
}
if (0 != (MenuInfo->Flags & MF_POPUP))
{
Dc = GetDC(MenuInfo->Wnd);
}
else
{
Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
}
if (NULL == TopPopup)
{
TopPopup = MenuInfo->Wnd;
}
SelectObject(Dc, hMenuFont);
MenuInitRosMenuItemInfo(&ItemInfo);
/* Clear previous highlighted item */
if (NO_SELECTED_ITEM != MenuInfo->FocusedItem)
{
if (MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
{
ItemInfo.fMask |= MIIM_STATE;
ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
}
MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo,
MenuInfo->Height, ! (MenuInfo->Flags & MF_POPUP),
ODA_SELECT);
}
/* Highlight new item (if any) */
MenuInfo->FocusedItem = Index;
MenuSetRosMenuInfo(MenuInfo);
if (NO_SELECTED_ITEM != MenuInfo->FocusedItem)
{
if (MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
{
if (0 == (ItemInfo.fType & MF_SEPARATOR))
{
ItemInfo.fMask |= MIIM_STATE;
ItemInfo.fState |= MF_HILITE;
MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc,
&ItemInfo, MenuInfo->Height, ! (MenuInfo->Flags & MF_POPUP),
ODA_SELECT);
}
if (SendMenuSelect)
{
SendMessageW(WndOwner, WM_MENUSELECT,
MAKELONG(ItemInfo.fType & MF_POPUP ? Index : ItemInfo.wID,
ItemInfo.fType | ItemInfo.fState | MF_MOUSESELECT |
(MenuInfo->Flags & MF_SYSMENU)), (LPARAM) MenuInfo->Self);
}
}
}
else if (SendMenuSelect)
{
if (NULL != TopMenu)
{
Pos = MenuFindSubMenu(&TopMenu, MenuInfo->Self);
if (NO_SELECTED_ITEM != Pos)
{
if (MenuGetRosMenuInfo(&TopMenuInfo, TopMenu)
&& MenuGetRosMenuItemInfo(TopMenu, Pos, &ItemInfo))
{
SendMessageW(WndOwner, WM_MENUSELECT,
MAKELONG(Pos, ItemInfo.fType | ItemInfo.fState
| MF_MOUSESELECT
| (TopMenuInfo.Flags & MF_SYSMENU)),
(LPARAM) TopMenu);
}
}
}
}
MenuCleanupRosMenuItemInfo(&ItemInfo);
ReleaseDC(MenuInfo->Wnd, Dc);
}
/***********************************************************************
* MenuMoveSelection
*
* Moves currently selected item according to the Offset parameter.
* If there is no selection then it should select the last item if
* Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
*/
static void FASTCALL
MenuMoveSelection(HWND WndOwner, PROSMENUINFO MenuInfo, INT Offset)
{
INT i;
ROSMENUITEMINFO ItemInfo;
INT OrigPos;
TRACE("hwnd=%x menu=%x off=0x%04x\n", WndOwner, MenuInfo, Offset);
/* Prevent looping */
if (0 == MenuInfo->MenuItemCount || 0 == Offset)
return;
else if (Offset < -1)
Offset = -1;
else if (Offset > 1)
Offset = 1;
MenuInitRosMenuItemInfo(&ItemInfo);
OrigPos = MenuInfo->FocusedItem;
if (OrigPos == NO_SELECTED_ITEM) /* NO_SELECTED_ITEM is not -1 ! */
{
OrigPos = 0;
i = -1;
}
else
{
i = MenuInfo->FocusedItem;
}
do
{
/* Step */
i += Offset;
/* Clip and wrap around */
if (i < 0)
{
i = MenuInfo->MenuItemCount - 1;
}
else if (i >= MenuInfo->MenuItemCount)
{
i = 0;
}
/* If this is a good candidate; */
if (MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo) &&
0 == (ItemInfo.fType & MF_SEPARATOR))
{
MenuSelectItem(WndOwner, MenuInfo, i, TRUE, NULL);
MenuCleanupRosMenuItemInfo(&ItemInfo);
return;
}
} while (i != OrigPos);
/* Not found */
MenuCleanupRosMenuItemInfo(&ItemInfo);
}
/***********************************************************************
* MenuInitSysMenuPopup
*
* Grey the appropriate items in System menu.
*/
void FASTCALL
MenuInitSysMenuPopup(HMENU Menu, DWORD Style, DWORD ClsStyle, LONG HitTest )
{
BOOL Gray;
UINT DefItem;
#if 0
MENUITEMINFOW mii;
#endif
Gray = 0 == (Style & WS_THICKFRAME) || 0 != (Style & (WS_MAXIMIZE | WS_MINIMIZE));
EnableMenuItem(Menu, SC_SIZE, (Gray ? MF_GRAYED : MF_ENABLED));
Gray = 0 != (Style & WS_MAXIMIZE);
EnableMenuItem(Menu, SC_MOVE, (Gray ? MF_GRAYED : MF_ENABLED));
Gray = 0 == (Style & WS_MINIMIZEBOX) || 0 != (Style & WS_MINIMIZE);
EnableMenuItem(Menu, SC_MINIMIZE, (Gray ? MF_GRAYED : MF_ENABLED));
Gray = 0 == (Style & WS_MAXIMIZEBOX) || 0 != (Style & WS_MAXIMIZE);
EnableMenuItem(Menu, SC_MAXIMIZE, (Gray ? MF_GRAYED : MF_ENABLED));
Gray = 0 == (Style & (WS_MAXIMIZE | WS_MINIMIZE));
EnableMenuItem(Menu, SC_RESTORE, (Gray ? MF_GRAYED : MF_ENABLED));
Gray = 0 != (ClsStyle & CS_NOCLOSE);
/* The menu item must keep its state if it's disabled */
if (Gray)
{
EnableMenuItem(Menu, SC_CLOSE, MF_GRAYED);
}
/* Set default menu item */
if(Style & WS_MINIMIZE)
{
DefItem = SC_RESTORE;
}
else
{
if(HitTest == HTCAPTION)
{
DefItem = ((Style & (WS_MAXIMIZE | WS_MINIMIZE)) ? SC_RESTORE : SC_MAXIMIZE);
}
else
{
DefItem = SC_CLOSE;
}
}
#if 0
mii.cbSize = sizeof(MENUITEMINFOW);
mii.fMask |= MIIM_STATE;
if((DefItem != SC_CLOSE) && GetMenuItemInfoW(Menu, DefItem, FALSE, &mii) &&
(mii.fState & (MFS_GRAYED | MFS_DISABLED)))
{
DefItem = SC_CLOSE;
}
#endif
SetMenuDefaultItem(Menu, DefItem, MF_BYCOMMAND);
}
/***********************************************************************
* MenuShowSubPopup
*
* Display the sub-menu of the selected item of this menu.
* Return the handle of the submenu, or menu if no submenu to display.
*/
static HMENU FASTCALL
MenuShowSubPopup(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SelectFirst, UINT Flags)
{
extern void FASTCALL NcGetSysPopupPos(HWND Wnd, RECT *Rect);
RECT Rect;
ROSMENUITEMINFO ItemInfo;
ROSMENUINFO SubMenuInfo;
HDC Dc;
HMENU Ret;
TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, MenuInfo, SelectFirst);
if (NO_SELECTED_ITEM == MenuInfo->FocusedItem)
{
return MenuInfo->Self;
}
MenuInitRosMenuItemInfo(&ItemInfo);
if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
return MenuInfo->Self;
}
if (0 == (ItemInfo.fType & MF_POPUP) || 0 != (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)))
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
return MenuInfo->Self;
}
/* message must be sent before using item,
because nearly everything may be changed by the application ! */
/* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
if (0 == (Flags & TPM_NONOTIFY))
{
SendMessageW(WndOwner, WM_INITMENUPOPUP, (WPARAM) ItemInfo.hSubMenu,
MAKELONG(MenuInfo->FocusedItem, IS_SYSTEM_MENU(MenuInfo)));
}
if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
return MenuInfo->Self;
}
Rect = ItemInfo.Rect;
/* correct item if modified as a reaction to WM_INITMENUPOPUP message */
if (0 == (ItemInfo.fState & MF_HILITE))
{
if (0 != (MenuInfo->Flags & MF_POPUP))
{
Dc = GetDC(MenuInfo->Wnd);
}
else
{
Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
}
SelectObject(Dc, hMenuFont);
ItemInfo.fMask |= MIIM_STATE;
ItemInfo.fState |= MF_HILITE;
MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, MenuInfo->Height,
! (MenuInfo->Flags & MF_POPUP), ODA_DRAWENTIRE);
ReleaseDC(MenuInfo->Wnd, Dc);
}
if (0 == ItemInfo.Rect.top && 0 == ItemInfo.Rect.left
&& 0 == ItemInfo.Rect.bottom && 0 == ItemInfo.Rect.right)
{
ItemInfo.Rect = Rect;
}
ItemInfo.fMask |= MIIM_STATE;
ItemInfo.fState |= MF_MOUSESELECT;
MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
if (IS_SYSTEM_MENU(MenuInfo))
{
MenuInitSysMenuPopup(ItemInfo.hSubMenu, GetWindowLongW(MenuInfo->Wnd, GWL_STYLE),
GetClassLongW(MenuInfo->Wnd, GCL_STYLE), HTSYSMENU);
NcGetSysPopupPos(MenuInfo->Wnd, &Rect);
Rect.top = Rect.bottom;
Rect.right = GetSystemMetrics(SM_CXSIZE);
Rect.bottom = GetSystemMetrics(SM_CYSIZE);
}
else
{
GetWindowRect(MenuInfo->Wnd, &Rect);
if (0 != (MenuInfo->Flags & MF_POPUP))
{
Rect.left += ItemInfo.Rect.right - GetSystemMetrics(SM_CXBORDER);
Rect.top += ItemInfo.Rect.top - 3;
Rect.right = ItemInfo.Rect.left - ItemInfo.Rect.right + GetSystemMetrics(SM_CXBORDER);
Rect.bottom = ItemInfo.Rect.top - ItemInfo.Rect.bottom - 3 - 2
- GetSystemMetrics(SM_CYBORDER);
}
else
{
Rect.left += ItemInfo.Rect.left;
Rect.top += ItemInfo.Rect.bottom;
Rect.right = ItemInfo.Rect.right - ItemInfo.Rect.left;
Rect.bottom = ItemInfo.Rect.bottom - ItemInfo.Rect.top;
}
}
MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->FocusedItem,
Rect.left, Rect.top, Rect.right, Rect.bottom );
if (SelectFirst && MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
{
MenuMoveSelection(WndOwner, &SubMenuInfo, ITEM_NEXT);
}
Ret = ItemInfo.hSubMenu;
MenuCleanupRosMenuItemInfo(&ItemInfo);
return Ret;
}
/***********************************************************************
* MenuHideSubPopups
*
* Hide the sub-popup menus of this menu.
*/
static void FASTCALL
MenuHideSubPopups(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SendMenuSelect)
{
ROSMENUINFO SubMenuInfo;
ROSMENUITEMINFO ItemInfo;
TRACE("owner=%x menu=%x 0x%04x\n", WndOwner, MenuInfo, SendMenuSelect);
if (NULL != MenuInfo && NULL != TopPopup && NO_SELECTED_ITEM != MenuInfo->FocusedItem)
{
MenuInitRosMenuItemInfo(&ItemInfo);
ItemInfo.fMask |= MIIM_FTYPE | MIIM_STATE;
if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo)
|| 0 == (ItemInfo.fType & MF_POPUP)
|| 0 == (ItemInfo.fState & MF_MOUSESELECT))
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
return;
}
ItemInfo.fState &= ~MF_MOUSESELECT;
ItemInfo.fMask |= MIIM_STATE;
MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
if (MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
{
MenuHideSubPopups(WndOwner, &SubMenuInfo, FALSE);
MenuSelectItem(WndOwner, &SubMenuInfo, NO_SELECTED_ITEM, SendMenuSelect, NULL);
DestroyWindow(SubMenuInfo.Wnd);
SubMenuInfo.Wnd = NULL;
MenuSetRosMenuInfo(&SubMenuInfo);
}
}
}
/***********************************************************************
* MenuSwitchTracking
*
* Helper function for menu navigation routines.
*/
static void FASTCALL
MenuSwitchTracking(MTRACKER* Mt, PROSMENUINFO PtMenuInfo, UINT Index)
{
ROSMENUINFO TopMenuInfo;
TRACE("%x menu=%x 0x%04x\n", Mt, PtMenuInfo->Self, Index);
if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu) &&
Mt->TopMenu != PtMenuInfo->Self &&
0 == ((PtMenuInfo->Flags | TopMenuInfo.Flags) & MF_POPUP))
{
/* both are top level menus (system and menu-bar) */
MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE);
MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, FALSE, NULL);
Mt->TopMenu = PtMenuInfo->Self;
}
else
{
MenuHideSubPopups(Mt->OwnerWnd, PtMenuInfo, FALSE);
}
MenuSelectItem(Mt->OwnerWnd, PtMenuInfo, Index, TRUE, NULL);
}
/***********************************************************************
* MenuExecFocusedItem
*
* Execute a menu item (for instance when user pressed Enter).
* Return the wID of the executed item. Otherwise, -1 indicating
* that no menu item was executed, -2 if a popup is shown;
* Have to receive the flags for the TrackPopupMenu options to avoid
* sending unwanted message.
*
*/
static INT FASTCALL
MenuExecFocusedItem(MTRACKER *Mt, PROSMENUINFO MenuInfo, UINT Flags)
{
ROSMENUITEMINFO ItemInfo;
UINT wID;
TRACE("%p menu=%p\n", Mt, MenuInfo);
if (0 == MenuInfo->MenuItemCount || NO_SELECTED_ITEM == MenuInfo->FocusedItem)
{
return -1;
}
MenuInitRosMenuItemInfo(&ItemInfo);
if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
return -1;
}
TRACE("%p %08x %p\n", MenuInfo, ItemInfo.wID, ItemInfo.hSubMenu);
if (0 == (ItemInfo.fType & MF_POPUP))
{
if (0 == (ItemInfo.fState & (MF_GRAYED | MF_DISABLED))
&& 0 == (ItemInfo.fType & MF_SEPARATOR))
{
/* If TPM_RETURNCMD is set you return the id, but
do not send a message to the owner */
if (0 == (Flags & TPM_RETURNCMD))
{
if (0 != (MenuInfo->Flags & MF_SYSMENU))
{
PostMessageW(Mt->OwnerWnd, WM_SYSCOMMAND, ItemInfo.wID,
MAKELPARAM((SHORT) Mt->Pt.x, (SHORT) Mt->Pt.y));
}
else
{
if (MenuInfo->dwStyle & MNS_NOTIFYBYPOS)
PostMessageW(Mt->OwnerWnd, WM_MENUCOMMAND,
MenuInfo->FocusedItem,
(LPARAM)MenuInfo->Self);
else
PostMessageW(Mt->OwnerWnd, WM_COMMAND, ItemInfo.wID, 0);
}
}
wID = ItemInfo.wID;
MenuCleanupRosMenuItemInfo(&ItemInfo);
return wID;
}
}
else
{
Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, MenuInfo, TRUE, Flags);
return -2;
}
return -1;
}
/***********************************************************************
* MenuButtonDown
*
* Return TRUE if we can go on with menu tracking.
*/
static BOOL FASTCALL
MenuButtonDown(MTRACKER* Mt, HMENU PtMenu, UINT Flags)
{
int Index;
ROSMENUINFO MenuInfo;
ROSMENUITEMINFO Item;
TRACE("%x PtMenu=%p\n", Mt, PtMenu);
if (NULL != PtMenu)
{
if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
{
return FALSE;
}
if (IS_SYSTEM_MENU(&MenuInfo))
{
Index = 0;
}
else
{
Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
}
MenuInitRosMenuItemInfo(&Item);
if (NO_SELECTED_ITEM == Index || ! MenuGetRosMenuItemInfo(PtMenu, Index, &Item))
{
MenuCleanupRosMenuItemInfo(&Item);
return FALSE;
}
if (!(Item.fType & MF_SEPARATOR) &&
!(Item.fState & (MFS_DISABLED | MFS_GRAYED)) )
{
if (MenuInfo.FocusedItem != Index)
{
MenuSwitchTracking(Mt, &MenuInfo, Index);
}
/* If the popup menu is not already "popped" */
if (0 == (Item.fState & MF_MOUSESELECT))
{
Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
}
}
MenuCleanupRosMenuItemInfo(&Item);
return TRUE;
}
/* else the click was on the menu bar, finish the tracking */
return FALSE;
}
/***********************************************************************
* MenuButtonUp
*
* Return the value of MenuExecFocusedItem if
* the selected item was not a popup. Else open the popup.
* A -1 return value indicates that we go on with menu tracking.
*
*/
static INT FASTCALL
MenuButtonUp(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
{
UINT Id;
ROSMENUINFO MenuInfo;
ROSMENUITEMINFO ItemInfo;
TRACE("%p hmenu=%x\n", Mt, PtMenu);
if (NULL != PtMenu)
{
Id = 0;
if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
{
return -1;
}
if (! IS_SYSTEM_MENU(&MenuInfo))
{
Id = NtUserMenuItemFromPoint(Mt->OwnerWnd, MenuInfo.Self, Mt->Pt.x, Mt->Pt.y);
}
MenuInitRosMenuItemInfo(&ItemInfo);
if (0 <= Id && MenuGetRosMenuItemInfo(MenuInfo.Self, Id, &ItemInfo) &&
MenuInfo.FocusedItem == Id)
{
if (0 == (ItemInfo.fType & MF_POPUP))
{
INT ExecutedMenuId = MenuExecFocusedItem(Mt, &MenuInfo, Flags);
MenuCleanupRosMenuItemInfo(&ItemInfo);
return (ExecutedMenuId < 0) ? -1 : ExecutedMenuId;
}
MenuCleanupRosMenuItemInfo(&ItemInfo);
/* If we are dealing with the top-level menu */
/* and this is a click on an already "popped" item: */
/* Stop the menu tracking and close the opened submenus */
if (Mt->TopMenu == MenuInfo.Self && MenuInfo.TimeToHide)
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
return 0;
}
}
MenuCleanupRosMenuItemInfo(&ItemInfo);
MenuInfo.TimeToHide = TRUE;
MenuSetRosMenuInfo(&MenuInfo);
}
return -1;
}
/***********************************************************************
* MenuPtMenu
*
* Walks menu chain trying to find a menu pt maps to.
*/
static HMENU FASTCALL
MenuPtMenu(HMENU Menu, POINT Pt)
{
extern LRESULT DefWndNCHitTest(HWND hWnd, POINT Point);
ROSMENUINFO MenuInfo;
ROSMENUITEMINFO ItemInfo;
HMENU Ret = NULL;
INT Ht;
if (! MenuGetRosMenuInfo(&MenuInfo, Menu))
{
return NULL;
}
/* try subpopup first (if any) */
if (NO_SELECTED_ITEM != MenuInfo.FocusedItem)
{
MenuInitRosMenuItemInfo(&ItemInfo);
if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo) &&
0 != (ItemInfo.fType & MF_POPUP) &&
0 != (ItemInfo.fState & MF_MOUSESELECT))
{
Ret = MenuPtMenu(ItemInfo.hSubMenu, Pt);
if (NULL != Ret)
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
return Ret;
}
}
MenuCleanupRosMenuItemInfo(&ItemInfo);
}
/* check the current window (avoiding WM_HITTEST) */
Ht = DefWndNCHitTest(MenuInfo.Wnd, Pt);
if (0 != (MenuInfo.Flags & MF_POPUP))
{
if (HTNOWHERE != Ht && HTERROR != Ht)
{
Ret = Menu;
}
}
else if (HTSYSMENU == Ht)
{
Ret = NtUserGetSystemMenu(MenuInfo.Wnd, FALSE);
}
else if (HTMENU == Ht)
{
Ret = GetMenu(MenuInfo.Wnd);
}
return Ret;
}
/***********************************************************************
* MenuMouseMove
*
* Return TRUE if we can go on with menu tracking.
*/
static BOOL FASTCALL
MenuMouseMove(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
{
UINT Index;
ROSMENUINFO MenuInfo;
ROSMENUITEMINFO ItemInfo;
if (NULL != PtMenu)
{
if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
{
return TRUE;
}
if (IS_SYSTEM_MENU(&MenuInfo))
{
Index = 0;
}
else
{
Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
}
}
else
{
Index = NO_SELECTED_ITEM;
}
if (NO_SELECTED_ITEM == Index)
{
if (Mt->CurrentMenu == MenuInfo.Self ||
MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
{
MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NO_SELECTED_ITEM,
TRUE, Mt->TopMenu);
}
}
else if (MenuInfo.FocusedItem != Index)
{
MenuInitRosMenuItemInfo(&ItemInfo);
if (MenuGetRosMenuItemInfo(MenuInfo.Self, Index, &ItemInfo) &&
!(ItemInfo.fType & MF_SEPARATOR))
{
MenuSwitchTracking(Mt, &MenuInfo, Index);
if (!(ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED)))
Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
}
MenuCleanupRosMenuItemInfo(&ItemInfo);
}
return TRUE;
}
/******************************************************************************
*
* UINT MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo)
*/
static UINT MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo)
{
UINT i;
PROSMENUITEMINFO MenuItems;
i = MenuInfo->FocusedItem;
if (NO_SELECTED_ITEM == i)
{
return i;
}
if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0)
{
return NO_SELECTED_ITEM;
}
for (i++ ; i < MenuInfo->MenuItemCount; i++)
{
if (0 != (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK)))
{
return i;
}
}
return NO_SELECTED_ITEM;
}
/******************************************************************************
*
* UINT MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo)
*/
static UINT FASTCALL
MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo)
{
UINT i;
PROSMENUITEMINFO MenuItems;
if (0 == MenuInfo->FocusedItem || NO_SELECTED_ITEM == MenuInfo->FocusedItem)
{
return NO_SELECTED_ITEM;
}
if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0)
{
return NO_SELECTED_ITEM;
}
/* Find the start of the column */
for (i = MenuInfo->FocusedItem;
0 != i && 0 == (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
--i)
{
; /* empty */
}
if (0 == i)
{
MenuCleanupAllRosMenuItemInfo(MenuItems);
return NO_SELECTED_ITEM;
}
for (--i; 0 != i; --i)
{
if (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
{
break;
}
}
MenuCleanupAllRosMenuItemInfo(MenuItems);
TRACE("ret %d.\n", i );
return i;
}
/***********************************************************************
* MenuGetSubPopup
*
* Return the handle of the selected sub-popup menu (if any).
*/
static HMENU FASTCALL
MenuGetSubPopup(HMENU Menu)
{
ROSMENUINFO MenuInfo;
ROSMENUITEMINFO ItemInfo;
if (! MenuGetRosMenuInfo(&MenuInfo, Menu)
|| NO_SELECTED_ITEM == MenuInfo.FocusedItem)
{
return NULL;
}
MenuInitRosMenuItemInfo(&ItemInfo);
if (! MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo))
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
return NULL;
}
if (0 != (ItemInfo.fType & MF_POPUP) && 0 != (ItemInfo.fState & MF_MOUSESELECT))
{
MenuCleanupRosMenuItemInfo(&ItemInfo);
return ItemInfo.hSubMenu;
}
MenuCleanupRosMenuItemInfo(&ItemInfo);
return NULL;
}
/***********************************************************************
* MenuDoNextMenu
*
* NOTE: WM_NEXTMENU documented in Win32 is a bit different.
*/
static LRESULT FASTCALL
MenuDoNextMenu(MTRACKER* Mt, UINT Vk)
{
ROSMENUINFO TopMenuInfo;
ROSMENUINFO MenuInfo;
if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
{
return (LRESULT) FALSE;
}
if ((VK_LEFT == Vk && 0 == TopMenuInfo.FocusedItem)
|| (VK_RIGHT == Vk && TopMenuInfo.FocusedItem == TopMenuInfo.MenuItemCount - 1))
{
MDINEXTMENU NextMenu;
HMENU NewMenu;
HWND NewWnd;
UINT Id = 0;
NextMenu.hmenuIn = (IS_SYSTEM_MENU(&TopMenuInfo)) ? GetSubMenu(Mt->TopMenu, 0) : Mt->TopMenu;
NextMenu.hmenuNext = NULL;
NextMenu.hwndNext = NULL;
SendMessageW(Mt->OwnerWnd, WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
TRACE("%p [%p] -> %p [%p]\n",
Mt->CurrentMenu, Mt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
{
DWORD Style = GetWindowLongW(Mt->OwnerWnd, GWL_STYLE);
NewWnd = Mt->OwnerWnd;
if (IS_SYSTEM_MENU(&TopMenuInfo))
{
/* switch to the menu bar */
if (0 != (Style & WS_CHILD)
|| NULL == (NewMenu = GetMenu(NewWnd)))
{
return FALSE;
}
if (VK_LEFT == Vk)
{
if (! MenuGetRosMenuInfo(&MenuInfo, NewMenu))
{
return FALSE;
}
Id = MenuInfo.MenuItemCount - 1;
}
}
else if (0 != (Style & WS_SYSMENU))
{
/* switch to the system menu */
NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
}
else
{
return FALSE;
}
}
else /* application returned a new menu to switch to */
{
NewMenu = NextMenu.hmenuNext;
NewWnd = NextMenu.hwndNext;
if (IsMenu(NewMenu) && IsWindow(NewWnd))
{
DWORD Style = GetWindowLongW(NewWnd, GWL_STYLE);
if (0 != (Style & WS_SYSMENU)
&& GetSystemMenu(NewWnd, FALSE) == NewMenu)
{
/* get the real system menu */
NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
}
else if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu)
{
/* FIXME: Not sure what to do here;
* perhaps try to track NewMenu as a popup? */
WARN(" -- got confused.\n");
return FALSE;
}
}
else
{
return FALSE;
}
}
if (NewMenu != Mt->TopMenu)
{
MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM,
FALSE, 0 );
if (Mt->CurrentMenu != Mt->TopMenu)
{
MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE);
}
}
if (NewWnd != Mt->OwnerWnd)
{
Mt->OwnerWnd = NewWnd;
SetCapture(Mt->OwnerWnd);
(void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, Mt->OwnerWnd);
}
Mt->TopMenu = Mt->CurrentMenu = NewMenu; /* all subpopups are hidden */
if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
{
MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, Id, TRUE, 0);
}
return TRUE;
}
return FALSE;
}
/***********************************************************************
* MenuSuspendPopup
*
* The idea is not to show the popup if the next input message is
* going to hide it anyway.
*/
static BOOL FASTCALL
MenuSuspendPopup(MTRACKER* Mt, UINT Message)
{
MSG Msg;
Msg.hwnd = Mt->OwnerWnd;
PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
Mt->TrackFlags |= TF_SKIPREMOVE;
switch (Message)
{
case WM_KEYDOWN:
PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
if (WM_KEYUP == Msg.message || WM_PAINT == Msg.message)
{
PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
if (WM_KEYDOWN == Msg.message
&& (VK_LEFT == Msg.wParam || VK_RIGHT == Msg.wParam))
{
Mt->TrackFlags |= TF_SUSPENDPOPUP;
return TRUE;
}
}
break;
}
/* failures go through this */
Mt->TrackFlags &= ~TF_SUSPENDPOPUP;
return FALSE;
}
/***********************************************************************
* MenuKeyEscape
*
* Handle a VK_ESCAPE key event in a menu.
*/
static BOOL FASTCALL
MenuKeyEscape(MTRACKER *Mt, UINT Flags)
{
BOOL EndMenu = TRUE;
ROSMENUINFO MenuInfo;
HMENU MenuTmp, MenuPrev;
if (Mt->CurrentMenu != Mt->TopMenu)
{
if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)
&& 0 != (MenuInfo.Flags & MF_POPUP))
{
MenuPrev = MenuTmp = Mt->TopMenu;
/* close topmost popup */
while (MenuTmp != Mt->CurrentMenu)
{
MenuPrev = MenuTmp;
MenuTmp = MenuGetSubPopup(MenuPrev);
}
if (MenuGetRosMenuInfo(&MenuInfo, MenuPrev))
{
MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, TRUE);
}
Mt->CurrentMenu = MenuPrev;
EndMenu = FALSE;
}
}
return EndMenu;
}
/***********************************************************************
* MenuKeyLeft
*
* Handle a VK_LEFT key event in a menu.
*/
static void FASTCALL
MenuKeyLeft(MTRACKER* Mt, UINT Flags)
{
ROSMENUINFO MenuInfo;
ROSMENUINFO TopMenuInfo;
ROSMENUINFO PrevMenuInfo;
HMENU MenuTmp, MenuPrev;
UINT PrevCol;
MenuPrev = MenuTmp = Mt->TopMenu;
if (! MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
{
return;
}
/* Try to move 1 column left (if possible) */
if (NO_SELECTED_ITEM != (PrevCol = MenuGetStartOfPrevColumn(&MenuInfo)))
{
if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
{
MenuSelectItem(Mt->OwnerWnd, &MenuInfo, PrevCol, TRUE, 0);
}
return;
}
/* close topmost popup */
while (MenuTmp != Mt->CurrentMenu)
{
MenuPrev = MenuTmp;
MenuTmp = MenuGetSubPopup(MenuPrev);
}
if (! MenuGetRosMenuInfo(&PrevMenuInfo, MenuPrev))
{
return;
}
MenuHideSubPopups(Mt->OwnerWnd, &PrevMenuInfo, TRUE);
Mt->CurrentMenu = MenuPrev;
if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
{
return;
}
if ((MenuPrev == Mt->TopMenu) && 0 == (TopMenuInfo.Flags & MF_POPUP))
{
/* move menu bar selection if no more popups are left */
if (! MenuDoNextMenu(Mt, VK_LEFT))
{
MenuMoveSelection(Mt->OwnerWnd, &TopMenuInfo, ITEM_PREV);
}
if (MenuPrev != MenuTmp || 0 != (Mt->TrackFlags & TF_SUSPENDPOPUP))
{
/* A sublevel menu was displayed - display the next one
* unless there is another displacement coming up */
if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
&& MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
{
Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &TopMenuInfo,
TRUE, Flags);
}
}
}
}
/***********************************************************************
* MenuKeyRight
*
* Handle a VK_RIGHT key event in a menu.
*/
static void FASTCALL
MenuKeyRight(MTRACKER *Mt, UINT Flags)
{
HMENU MenuTmp;
ROSMENUINFO MenuInfo;
ROSMENUINFO CurrentMenuInfo;
UINT NextCol;
TRACE("MenuKeyRight called, cur %p, top %p.\n",
Mt->CurrentMenu, Mt->TopMenu);
if (! MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu))
{
return;
}
if (0 != (MenuInfo.Flags & MF_POPUP) || (Mt->CurrentMenu != Mt->TopMenu))
{
/* If already displaying a popup, try to display sub-popup */
MenuTmp = Mt->CurrentMenu;
if (MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
{
Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &CurrentMenuInfo, TRUE, Flags);
}
/* if subpopup was displayed then we are done */
if (MenuTmp != Mt->CurrentMenu)
{
return;
}
}
if (! MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
{
return;
}
/* Check to see if there's another column */
if (NO_SELECTED_ITEM != (NextCol = MenuGetStartOfNextColumn(&CurrentMenuInfo)))
{
TRACE("Going to %d.\n", NextCol);
if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
{
MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NextCol, TRUE, 0);
}
return;
}
if (0 == (MenuInfo.Flags & MF_POPUP)) /* menu bar tracking */
{
if (Mt->CurrentMenu != Mt->TopMenu)
{
MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, FALSE );
MenuTmp = Mt->CurrentMenu = Mt->TopMenu;
}
else
{
MenuTmp = NULL;
}
/* try to move to the next item */
if (! MenuDoNextMenu(Mt, VK_RIGHT))
{
MenuMoveSelection(Mt->OwnerWnd, &MenuInfo, ITEM_NEXT);
}
if (NULL != MenuTmp || 0 != (Mt->TrackFlags & TF_SUSPENDPOPUP))
{
if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
&& MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu))
{
Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo,
TRUE, Flags);
}
}
}
}
/***********************************************************************
* MenuFindItemByKey
*
* Find the menu item selected by a key press.
* Return item id, -1 if none, -2 if we should close the menu.
*/
static UINT FASTCALL
MenuFindItemByKey(HWND WndOwner, PROSMENUINFO MenuInfo,
WCHAR Key, BOOL ForceMenuChar)
{
ROSMENUINFO SysMenuInfo;
PROSMENUITEMINFO Items, ItemInfo;
LRESULT MenuChar;
UINT i;
TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char) Key, Key, MenuInfo);
if (NULL == MenuInfo || ! IsMenu(MenuInfo->Self))
{
if (MenuGetRosMenuInfo(&SysMenuInfo, GetSystemMenu(WndOwner, FALSE)))
{
MenuInfo = &SysMenuInfo;
}
else
{
MenuInfo = NULL;
}
}
if (NULL != MenuInfo)
{
if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &Items) <= 0)
{
return -1;
}
if (! ForceMenuChar)
{
Key = toupperW(Key);
ItemInfo = Items;
for (i = 0; i < MenuInfo->MenuItemCount; i++, ItemInfo++)
{
if ((ItemInfo->Text) && NULL != ItemInfo->dwTypeData)
{
WCHAR *p = (WCHAR *) ItemInfo->dwTypeData - 2;
do
{
p = strchrW(p + 2, '&');
}
while (NULL != p && L'&' == p[1]);
if (NULL != p && (toupperW(p[1]) == Key))
{
return i;
}
}
}
}
MenuChar = SendMessageW(WndOwner, WM_MENUCHAR,
MAKEWPARAM(Key, MenuInfo->Flags), (LPARAM) MenuInfo->Self);
if (2 == HIWORD(MenuChar))
{
return LOWORD(MenuChar);
}
if (1 == HIWORD(MenuChar))
{
return (UINT) (-2);
}
}
return (UINT)(-1);
}
/***********************************************************************
* MenuTrackMenu
*
* Menu tracking code.
*/
static INT FASTCALL
MenuTrackMenu(HMENU Menu, UINT Flags, INT x, INT y,
HWND Wnd, const RECT *Rect )
{
MSG Msg;
ROSMENUINFO MenuInfo;
ROSMENUITEMINFO ItemInfo;
BOOL fRemove;
INT ExecutedMenuId = -1;
MTRACKER Mt;
BOOL EnterIdleSent = FALSE;
Mt.TrackFlags = 0;
Mt.CurrentMenu = Menu;
Mt.TopMenu = Menu;
Mt.OwnerWnd = Wnd;
Mt.Pt.x = x;
Mt.Pt.y = y;
TRACE("Menu=%x Flags=0x%08x (%d,%d) Wnd=%x (%ld,%ld)-(%ld,%ld)\n",
Menu, Flags, x, y, Wnd, Rect ? Rect->left : 0, Rect ? Rect->top : 0,
Rect ? Rect->right : 0, Rect ? Rect->bottom : 0);
fEndMenu = FALSE;
if (! MenuGetRosMenuInfo(&MenuInfo, Menu))
{
return FALSE;
}
if (0 != (Flags & TPM_BUTTONDOWN))
{
/* Get the result in order to start the tracking or not */
fRemove = MenuButtonDown(&Mt, Menu, Flags);
fEndMenu = ! fRemove;
}
SetCapture(Mt.OwnerWnd);
(void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, Mt.OwnerWnd);
while (! fEndMenu)
{
/* we have to keep the message in the queue until it's
* clear that menu loop is not over yet. */
for (;;)
{
if (PeekMessageW(&Msg, 0, 0, 0, PM_NOREMOVE))
{
if (! CallMsgFilterW(&Msg, MSGF_MENU))
{
break;
}
/* remove the message from the queue */
PeekMessageW(&Msg, 0, Msg.message, Msg.message, PM_REMOVE );
}
else
{
if (! EnterIdleSent)
{
HWND Win = (0 != (Flags & TPM_ENTERIDLEEX)
&& 0 != (MenuInfo.Flags & MF_POPUP)) ? MenuInfo.Wnd : NULL;
EnterIdleSent = TRUE;
SendMessageW(Mt.OwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM) Win);
}
WaitMessage();
}
}
/* check if EndMenu() tried to cancel us, by posting this message */
if (WM_CANCELMODE == Msg.message)
{
/* we are now out of the loop */
fEndMenu = TRUE;
/* remove the message from the queue */
PeekMessageW(&Msg, 0, Msg.message, Msg.message, PM_REMOVE);
/* break out of internal loop, ala ESCAPE */
break;
}
TranslateMessage(&Msg);
Mt.Pt = Msg.pt;
if (Msg.hwnd == MenuInfo.Wnd || WM_TIMER != Msg.message)
{
EnterIdleSent = FALSE;
}
fRemove = FALSE;
if (WM_MOUSEFIRST <= Msg.message && Msg.message <= WM_MOUSELAST)
{
/*
* Use the mouse coordinates in lParam instead of those in the MSG
* struct to properly handle synthetic messages. They are already
* in screen coordinates.
*/
Mt.Pt.x = (short) LOWORD(Msg.lParam);
Mt.Pt.y = (short) HIWORD(Msg.lParam);
/* Find a menu for this mouse event */
Menu = MenuPtMenu(Mt.TopMenu, Mt.Pt);
switch(Msg.message)
{
/* no WM_NC... messages in captured state */
case WM_RBUTTONDBLCLK:
case WM_RBUTTONDOWN:
if (0 == (Flags & TPM_RIGHTBUTTON))
{
break;
}
/* fall through */
case WM_LBUTTONDBLCLK:
case WM_LBUTTONDOWN:
/* If the message belongs to the menu, removes it from the queue */
/* Else, end menu tracking */
fRemove = MenuButtonDown(&Mt, Menu, Flags);
fEndMenu = ! fRemove;
break;
case WM_RBUTTONUP:
if (0 == (Flags & TPM_RIGHTBUTTON))
{
break;
}
/* fall through */
case WM_LBUTTONUP:
/* Check if a menu was selected by the mouse */
if (NULL != Menu)
{
ExecutedMenuId = MenuButtonUp(&Mt, Menu, Flags);
/* End the loop if ExecutedMenuId is an item ID */
/* or if the job was done (ExecutedMenuId = 0). */
fEndMenu = fRemove = (-1 != ExecutedMenuId);
}
else
{
/* No menu was selected by the mouse */
/* if the function was called by TrackPopupMenu, continue
with the menu tracking. If not, stop it */
fEndMenu = (0 != (Flags & TPM_POPUPMENU) ? FALSE : TRUE);
}
break;
case WM_MOUSEMOVE:
if (Menu)
{
fEndMenu |= ! MenuMouseMove(&Mt, Menu, Flags);
}
break;
} /* switch(Msg.message) - mouse */
}
else if (WM_KEYFIRST <= Msg.message && Msg.message <= WM_KEYLAST)
{
fRemove = TRUE; /* Keyboard messages are always removed */
switch(Msg.message)
{
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
switch(Msg.wParam)
{
case VK_MENU:
fEndMenu = TRUE;
break;
case VK_HOME:
case VK_END:
if (MenuGetRosMenuInfo(&MenuInfo, Mt.CurrentMenu))
{
MenuSelectItem(Mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM,
FALSE, 0 );
}
/* fall through */
case VK_UP:
if (MenuGetRosMenuInfo(&MenuInfo, Mt.CurrentMenu))
{
MenuMoveSelection(Mt.OwnerWnd, &MenuInfo,
VK_HOME == Msg.wParam ? ITEM_NEXT : ITEM_PREV);
}
break;
case VK_DOWN: /* If on menu bar, pull-down the menu */
if (MenuGetRosMenuInfo(&MenuInfo, Mt.CurrentMenu))
{
if (0 == (MenuInfo.Flags & MF_POPUP))
{
if (MenuGetRosMenuInfo(&MenuInfo, Mt.TopMenu))
{
Mt.CurrentMenu = MenuShowSubPopup(Mt.OwnerWnd, &MenuInfo,
TRUE, Flags);
}
}
else /* otherwise try to move selection */
{
MenuMoveSelection(Mt.OwnerWnd, &MenuInfo, ITEM_NEXT);
}
}
break;
case VK_LEFT:
MenuKeyLeft(&Mt, Flags);
break;
case VK_RIGHT:
MenuKeyRight(&Mt, Flags);
break;
case VK_ESCAPE:
fEndMenu = MenuKeyEscape(&Mt, Flags);
break;
case VK_F1:
{
HELPINFO hi;
hi.cbSize = sizeof(HELPINFO);
hi.iContextType = HELPINFO_MENUITEM;
if (MenuGetRosMenuInfo(&MenuInfo, Mt.CurrentMenu))
{
if (NO_SELECTED_ITEM == MenuInfo.FocusedItem)
{
hi.iCtrlId = 0;
}
else
{
MenuInitRosMenuItemInfo(&ItemInfo);
if (MenuGetRosMenuItemInfo(MenuInfo.Self,
MenuInfo.FocusedItem,
&ItemInfo))
{
hi.iCtrlId = ItemInfo.wID;
}
else
{
hi.iCtrlId = 0;
}
MenuCleanupRosMenuItemInfo(&ItemInfo);
}
}
hi.hItemHandle = Menu;
hi.dwContextId = MenuInfo.dwContextHelpID;
hi.MousePos = Msg.pt;
SendMessageW(Wnd, WM_HELP, 0, (LPARAM) &hi);
break;
}
default:
break;
}
break; /* WM_KEYDOWN */
case WM_CHAR:
case WM_SYSCHAR:
{
UINT Pos;
if (! MenuGetRosMenuInfo(&MenuInfo, Mt.CurrentMenu))
{
break;
}
if (L'\r' == Msg.wParam || L' ' == Msg.wParam)
{
ExecutedMenuId = MenuExecFocusedItem(&Mt, &MenuInfo, Flags);
fEndMenu = (ExecutedMenuId != -2);
break;
}
/* Hack to avoid control chars. */
/* We will find a better way real soon... */
if (Msg.wParam < 32)
{
break;
}
Pos = MenuFindItemByKey(Mt.OwnerWnd, &MenuInfo,
LOWORD(Msg.wParam), FALSE);
if ((UINT) -2 == Pos)
{
fEndMenu = TRUE;
}
else if ((UINT) -1 == Pos)
{
MessageBeep(0);
}
else
{
MenuSelectItem(Mt.OwnerWnd, &MenuInfo, Pos, TRUE, 0);
ExecutedMenuId = MenuExecFocusedItem(&Mt, &MenuInfo, Flags);
fEndMenu = (-2 != ExecutedMenuId);
}
}
break;
} /* switch(msg.message) - kbd */
}
else
{
PeekMessageW( &Msg, 0, Msg.message, Msg.message, PM_REMOVE );
DispatchMessageW(&Msg);
continue;
}
if (! fEndMenu)
{
fRemove = TRUE;
}
/* finally remove message from the queue */
if (fRemove && 0 == (Mt.TrackFlags & TF_SKIPREMOVE))
{
PeekMessageW(&Msg, 0, Msg.message, Msg.message, PM_REMOVE);
}
else
{
Mt.TrackFlags &= ~TF_SKIPREMOVE;
}
}
(void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, NULL);
SetCapture(NULL); /* release the capture */
/* If dropdown is still painted and the close box is clicked on
then the menu will be destroyed as part of the DispatchMessage above.
This will then invalidate the menu handle in Mt.hTopMenu. We should
check for this first. */
if (IsMenu(Mt.TopMenu))
{
if (IsWindow(Mt.OwnerWnd))
{
if (MenuGetRosMenuInfo(&MenuInfo, Mt.TopMenu))
{
MenuHideSubPopups(Mt.OwnerWnd, &MenuInfo, FALSE);
if (0 != (MenuInfo.Flags & MF_POPUP))
{
DestroyWindow(MenuInfo.Wnd);
MenuInfo.Wnd = NULL;
}
MenuSelectItem(Mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, FALSE, NULL);
}
SendMessageW(Mt.OwnerWnd, WM_MENUSELECT, MAKELONG(0, 0xffff), 0);
}
if (MenuGetRosMenuInfo(&MenuInfo, Mt.TopMenu))
{
/* Reset the variable for hiding menu */
MenuInfo.TimeToHide = FALSE;
MenuSetRosMenuInfo(&MenuInfo);
}
}
/* The return value is only used by TrackPopupMenu */
if (!(Flags & TPM_RETURNCMD)) return TRUE;
if (ExecutedMenuId < 0) ExecutedMenuId = 0;
return ExecutedMenuId;
}
/***********************************************************************
* MenuExitTracking
*/
static BOOL FASTCALL
MenuExitTracking(HWND Wnd)
{
TRACE("hwnd=%p\n", Wnd);
SendMessageW(Wnd, WM_EXITMENULOOP, 0, 0);
ShowCaret(0);
return TRUE;
}
VOID
MenuTrackMouseMenuBar(HWND Wnd, ULONG Ht, POINT Pt)
{
HMENU Menu = (HTSYSMENU == Ht) ? NtUserGetSystemMenu(Wnd, FALSE) : GetMenu(Wnd);
UINT Flags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", Wnd, Ht, Pt.x, Pt.y);
if (IsMenu(Menu))
{
/* map point to parent client coordinates */
HWND Parent = GetAncestor(Wnd, GA_PARENT );
if (Parent != GetDesktopWindow())
{
ScreenToClient(Parent, &Pt);
}
MenuInitTracking(Wnd, Menu, FALSE, Flags);
MenuTrackMenu(Menu, Flags, Pt.x, Pt.y, Wnd, NULL);
MenuExitTracking(Wnd);
}
}
VOID
MenuTrackKbdMenuBar(HWND hWnd, UINT wParam, WCHAR wChar)
{
UINT uItem = NO_SELECTED_ITEM;
HMENU hTrackMenu;
ROSMENUINFO MenuInfo;
UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hWnd, wParam, wChar);
/* find window that has a menu */
while (!((GetWindowLongW( hWnd, GWL_STYLE ) &
(WS_CHILD | WS_POPUP)) != WS_CHILD))
if (!(hWnd = GetAncestor( hWnd, GA_PARENT ))) return;
/* check if we have to track a system menu */
hTrackMenu = GetMenu( hWnd );
if (!hTrackMenu || IsIconic(hWnd) || wChar == ' ' )
{
if (!(GetWindowLongW( hWnd, GWL_STYLE ) & WS_SYSMENU)) return;
hTrackMenu = NtUserGetSystemMenu(hWnd, FALSE);
uItem = 0;
wParam |= HTSYSMENU; /* prevent item lookup */
}
if (!IsMenu( hTrackMenu )) return;
MenuInitTracking( hWnd, hTrackMenu, FALSE, wFlags );
if (! MenuGetRosMenuInfo(&MenuInfo, hTrackMenu))
{
goto track_menu;
}
if( wChar && wChar != ' ' )
{
uItem = MenuFindItemByKey( hWnd, &MenuInfo, wChar, (wParam & HTSYSMENU) );
if ( uItem >= (UINT)(-2) )
{
if( uItem == (UINT)(-1) ) MessageBeep(0);
/* schedule end of menu tracking */
wFlags |= TF_ENDMENU;
goto track_menu;
}
}
MenuSelectItem( hWnd, &MenuInfo, uItem, TRUE, 0 );
if (wParam & HTSYSMENU)
{
/* prevent sysmenu activation for managed windows on Alt down/up */
// if (GetPropA( hwnd, "__wine_x11_managed" ))
wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
}
else
{
if( uItem == NO_SELECTED_ITEM )
MenuMoveSelection( hWnd, &MenuInfo, ITEM_NEXT );
else
PostMessageW( hWnd, WM_KEYDOWN, VK_DOWN, 0L );
}
track_menu:
MenuTrackMenu( hTrackMenu, wFlags, 0, 0, hWnd, NULL );
MenuExitTracking( hWnd );
}
/*
* From MSDN:
* The MFT_BITMAP, MFT_SEPARATOR, and MFT_STRING values cannot be combined
* with one another. Also MFT_OWNERDRAW. Set fMask to MIIM_TYPE to use fType.
*
* Windows 2K/XP: fType is used only if fMask has a value of MIIM_FTYPE.
*
* MIIM_TYPE: Retrieves or sets the fType and dwTypeData members. Windows
* 2K/XP: MIIM_TYPE is replaced by MIIM_BITMAP, MIIM_FTYPE, and MIIM_STRING.
* MFT_STRING is replaced by MIIM_STRING.
* (So, I guess we should use MIIM_STRING only for strings?)
*
* MIIM_FTYPE: Windows 2K/Windows XP: Retrieves or sets the fType member.
*
* Based on wine, SetMenuItemInfo_common:
* 1) set MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP any one with MIIM_TYPE,
* it will result in a error.
* 2) set menu mask to MIIM_FTYPE and MFT_BITMAP ftype it will result in a error.
* These conditions are addressed in Win32k IntSetMenuItemInfo.
*
*/
static
BOOL
FASTCALL
MenuSetItemData(
LPMENUITEMINFOW mii,
UINT Flags,
UINT_PTR IDNewItem,
LPCWSTR NewItem,
BOOL Unicode)
{
/*
* Let us assume MIIM_FTYPE is set and building a new menu item structure.
*/
if(Flags & MF_BITMAP)
{
mii->fMask |= MIIM_BITMAP; /* Use the new way of seting hbmpItem.*/
mii->hbmpItem = (HBITMAP) NewItem;
if (Flags & MF_HELP)
{
/* increase ident */
mii->fType |= MF_HELP;
}
}
else if(Flags & MF_OWNERDRAW)
{
mii->fType |= MFT_OWNERDRAW;
mii->fMask |= MIIM_DATA;
mii->dwItemData = (DWORD) NewItem;
}
else if (Flags & MF_SEPARATOR)
{
mii->fType |= MFT_SEPARATOR;
if (!(Flags & (MF_GRAYED|MF_DISABLED)))
Flags |= MF_GRAYED|MF_DISABLED;
}
else /* Default action MF_STRING. */
{
/* Item beginning with a backspace is a help item */
if (NewItem != NULL)
{
if (Unicode)
{
if (*NewItem == '\b')
{
mii->fType |= MF_HELP;
NewItem++;
}
}
else
{
LPCSTR NewItemA = (LPCSTR) NewItem;
if (*NewItemA == '\b')
{
mii->fType |= MF_HELP;
NewItemA++;
NewItem = (LPCWSTR) NewItemA;
}
}
if (Flags & MF_HELP)
mii->fType |= MF_HELP;
mii->fMask |= MIIM_STRING;
mii->fType |= MFT_STRING; /* Zero */
mii->dwTypeData = (LPWSTR)NewItem;
if (Unicode)
mii->cch = (NULL == NewItem ? 0 : strlenW(NewItem));
else
mii->cch = (NULL == NewItem ? 0 : strlen((LPCSTR)NewItem));
}
else
{
mii->fType |= MFT_SEPARATOR;
if (!(Flags & (MF_GRAYED|MF_DISABLED)))
Flags |= MF_GRAYED|MF_DISABLED;
}
}
if(Flags & MF_RIGHTJUSTIFY) /* Same as MF_HELP */
{
mii->fType |= MFT_RIGHTJUSTIFY;
}
if(Flags & MF_MENUBREAK)
{
mii->fType |= MFT_MENUBREAK;
}
else if(Flags & MF_MENUBARBREAK)
{
mii->fType |= MFT_MENUBARBREAK;
}
if(Flags & MF_GRAYED || Flags & MF_DISABLED)
{
if (Flags & MF_GRAYED)
mii->fState |= MF_GRAYED;
if (Flags & MF_DISABLED)
mii->fState |= MF_DISABLED;
mii->fMask |= MIIM_STATE;
}
else if (Flags & MF_HILITE)
{
mii->fState |= MF_HILITE;
mii->fMask |= MIIM_STATE;
}
else /* default state */
{
mii->fState |= MFS_ENABLED;
mii->fMask |= MIIM_STATE;
}
if(Flags & MF_POPUP)
{
mii->fType |= MF_POPUP;
mii->fMask |= MIIM_SUBMENU;
mii->hSubMenu = (HMENU)IDNewItem;
}
else
{
mii->fMask |= MIIM_ID;
mii->wID = (UINT)IDNewItem;
}
return TRUE;
}
/* FUNCTIONS *****************************************************************/
/*static BOOL
MenuIsStringItem(ULONG TypeData)
{
return(MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType));
}*/
/*
* @implemented
*/
BOOL WINAPI
AppendMenuA(HMENU hMenu,
UINT uFlags,
UINT_PTR uIDNewItem,
LPCSTR lpNewItem)
{
return(InsertMenuA(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
lpNewItem));
}
/*
* @implemented
*/
BOOL WINAPI
AppendMenuW(HMENU hMenu,
UINT uFlags,
UINT_PTR uIDNewItem,
LPCWSTR lpNewItem)
{
return(InsertMenuW(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
lpNewItem));
}
/*
* @implemented
*/
DWORD WINAPI
CheckMenuItem(HMENU hmenu,
UINT uIDCheckItem,
UINT uCheck)
{
return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck);
}
static
BOOL
MenuCheckMenuRadioItem(HMENU hMenu, UINT idFirst, UINT idLast, UINT idCheck, UINT uFlags, BOOL bCheck, PUINT pChecked, PUINT pUnchecked, PUINT pMenuChanged)
{
UINT ItemCount, i;
PROSMENUITEMINFO Items = NULL;
UINT cChecked, cUnchecked;
BOOL bRet = TRUE;
//ROSMENUINFO mi;
if(idFirst > idLast)
return FALSE;
ItemCount = GetMenuItemCount(hMenu);
//mi.cbSize = sizeof(ROSMENUINFO);
//if(!NtUserMenuInfo(hmenu, &mi, FALSE)) return ret;
if(MenuGetAllRosMenuItemInfo(hMenu, &Items) <= 0)
{
ERR("MenuGetAllRosMenuItemInfo failed\n");
return FALSE;
}
cChecked = cUnchecked = 0;
for (i = 0 ; i < ItemCount; i++)
{
BOOL check = FALSE;
if (0 != (Items[i].fType & MF_MENUBARBREAK)) continue;
if (0 != (Items[i].fType & MF_SEPARATOR)) continue;
if ((Items[i].fType & MF_POPUP) && (uFlags == MF_BYCOMMAND))
{
MenuCheckMenuRadioItem(Items[i].hSubMenu, idFirst, idLast, idCheck, uFlags, bCheck, pChecked, pUnchecked, pMenuChanged);
continue;
}
if (uFlags & MF_BYPOSITION)
{
if (i < idFirst || i > idLast)
continue;
if (i == idCheck)
{
cChecked++;
check = TRUE;
}
else
{
cUnchecked++;
}
}
else
{
if (Items[i].wID < idFirst || Items[i].wID > idLast)
continue;
if (Items[i].wID == idCheck)
{
cChecked++;
check = TRUE;
}
else
{
cUnchecked++;
}
}
if (!bCheck)
continue;
Items[i].fMask = MIIM_STATE | MIIM_FTYPE;
if (check)
{
Items[i].fType |= MFT_RADIOCHECK;
Items[i].fState |= MFS_CHECKED;
}
else
{
Items[i].fState &= ~MFS_CHECKED;
}
if(!MenuSetRosMenuItemInfo(hMenu, i ,&Items[i]))
{
ERR("MenuSetRosMenuItemInfo failed\n");
bRet = FALSE;
break;
}
}
HeapFree(GetProcessHeap(), 0, Items);
*pChecked += cChecked;
*pUnchecked += cUnchecked;
if (cChecked || cUnchecked)
(*pMenuChanged)++;
return bRet;
}
/*
* @implemented
*/
BOOL WINAPI
CheckMenuRadioItem(HMENU hmenu,
UINT idFirst,
UINT idLast,
UINT idCheck,
UINT uFlags)
{
UINT cChecked = 0;
UINT cUnchecked = 0;
UINT cMenuChanged = 0;
if (!MenuCheckMenuRadioItem(hmenu, idFirst, idLast, idCheck, uFlags, FALSE, &cChecked, &cUnchecked, &cMenuChanged))
return FALSE;
if (cMenuChanged > 1)
return FALSE;
cMenuChanged = 0;
cChecked = 0;
cUnchecked = 0;
if (!MenuCheckMenuRadioItem(hmenu, idFirst, idLast, idCheck, uFlags, TRUE, &cChecked, &cUnchecked, &cMenuChanged))
return FALSE;
return (cChecked != 0);
}
/*
* @implemented
*/
HMENU WINAPI
CreateMenu(VOID)
{
MenuLoadBitmaps();
return (HMENU)NtUserCallNoParam(NOPARAM_ROUTINE_CREATEMENU);
}
/*
* @implemented
*/
HMENU WINAPI
CreatePopupMenu(VOID)
{
MenuLoadBitmaps();
return (HMENU)NtUserCallNoParam(NOPARAM_ROUTINE_CREATEMENUPOPUP);
}
/*
* @implemented
*/
BOOL WINAPI
DrawMenuBar(HWND hWnd)
{
// return (BOOL)NtUserCallHwndLock(hWnd, HWNDLOCK_ROUTINE_DRAWMENUBAR);
ROSMENUINFO MenuInfo;
HMENU hMenu;
hMenu = GetMenu(hWnd);
if (!hMenu)
return FALSE;
MenuGetRosMenuInfo(&MenuInfo, hMenu);
MenuInfo.Height = 0; // make sure to recalc size
MenuSetRosMenuInfo(&MenuInfo);
/* The wine method doesn't work and I suspect it's more effort
then hackfix solution
SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
SWP_NOZORDER | SWP_FRAMECHANGED );
return TRUE;*/
// FIXME: hackfix
DefWndNCPaint(hWnd,(HRGN)-1,-1);
return TRUE;
}
/*
* @implemented
*/
BOOL WINAPI
EnableMenuItem(HMENU hMenu,
UINT uIDEnableItem,
UINT uEnable)
{
return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable);
}
/*
* @implemented
*/
BOOL WINAPI
EndMenu(VOID)
{
GUITHREADINFO guii;
guii.cbSize = sizeof(GUITHREADINFO);
if(GetGUIThreadInfo(GetCurrentThreadId(), &guii) && guii.hwndMenuOwner)
{
PostMessageW(guii.hwndMenuOwner, WM_CANCELMODE, 0, 0);
}
return TRUE;
}
/*
* @implemented
*/
HMENU WINAPI
GetMenu(HWND hWnd)
{
return NtUserGetMenu(hWnd);
}
/*
* @implemented
*/
LONG WINAPI
GetMenuCheckMarkDimensions(VOID)
{
return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK),
GetSystemMetrics(SM_CYMENUCHECK)));
}
/*
* @implemented
*/
UINT WINAPI
GetMenuDefaultItem(HMENU hMenu,
UINT fByPos,
UINT gmdiFlags)
{
return NtUserGetMenuDefaultItem(hMenu, fByPos, gmdiFlags);
}
/*
* @implemented
*/
BOOL WINAPI
GetMenuInfo(HMENU hmenu,
LPMENUINFO lpcmi)
{
ROSMENUINFO mi;
BOOL res = FALSE;
if(!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
return FALSE;
RtlZeroMemory(&mi, sizeof(MENUINFO));
mi.cbSize = sizeof(MENUINFO);
mi.fMask = lpcmi->fMask;
res = NtUserMenuInfo(hmenu, &mi, FALSE);
memcpy(lpcmi, &mi, sizeof(MENUINFO));
return res;
}
/*
* @implemented
*/
int WINAPI
GetMenuItemCount(HMENU Menu)
{
ROSMENUINFO MenuInfo;
return MenuGetRosMenuInfo(&MenuInfo, Menu) ? MenuInfo.MenuItemCount : 0;
}
/*
* @implemented
*/
UINT WINAPI
GetMenuItemID(HMENU hMenu,
int nPos)
{
ROSMENUITEMINFO mii;
mii.cbSize = sizeof(MENUITEMINFOW);
mii.fMask = MIIM_ID | MIIM_SUBMENU;
if (! NtUserMenuItemInfo(hMenu, nPos, MF_BYPOSITION, &mii, FALSE))
{
return -1;
}
if (NULL != mii.hSubMenu)
{
return -1;
}
if (0 == mii.wID)
{
return -1;
}
return mii.wID;
}
/*
* @implemented
*/
BOOL WINAPI
GetMenuItemInfoA(
HMENU Menu,
UINT Item,
BOOL ByPosition,
LPMENUITEMINFOA mii)
{
MENUITEMINFOW miiW;
LPSTR AnsiBuffer;
INT Count;
if (mii->cbSize != sizeof(MENUITEMINFOA) &&
mii->cbSize != sizeof(MENUITEMINFOA) - sizeof(HBITMAP))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if(!(mii->fMask & (MIIM_TYPE | MIIM_STRING)))
{
/* No text requested, just pass on */
return NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) mii, FALSE);
}
AnsiBuffer = mii->dwTypeData;
Count = miiW.cch = mii->cch;
RtlCopyMemory(&miiW, mii, mii->cbSize);
miiW.dwTypeData = 0;
if (AnsiBuffer)
{
miiW.dwTypeData = RtlAllocateHeap(GetProcessHeap(), 0,
miiW.cch * sizeof(WCHAR));
if (miiW.dwTypeData == NULL) return FALSE;
miiW.dwTypeData[0] = 0;
}
if (!NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO)&miiW, FALSE))
{
if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
return FALSE;
}
RtlCopyMemory(mii, &miiW, miiW.cbSize);
if (!AnsiBuffer || !Count)
{
if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
mii->dwTypeData = AnsiBuffer;
mii->cch = miiW.cch;
return TRUE;
}
if ((miiW.fMask & MIIM_STRING) || (IS_STRING_ITEM(miiW.fType)))
{
if (miiW.cch)
{
if (!WideCharToMultiByte(CP_ACP, 0, miiW.dwTypeData, miiW.cch, AnsiBuffer, mii->cch, NULL, NULL))
{
AnsiBuffer[0] = 0;
}
if (Count > miiW.cch)
{
AnsiBuffer[miiW.cch] = 0;
}
mii->cch = mii->cch;
}
}
else
{
AnsiBuffer[0] = 0;
}
RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
mii->dwTypeData = AnsiBuffer;
return TRUE;
}
/*
* @implemented
*/
BOOL WINAPI
GetMenuItemInfoW(
HMENU Menu,
UINT Item,
BOOL ByPosition,
LPMENUITEMINFOW mii)
{
MENUITEMINFOW miiW;
LPWSTR String;
INT Count;
if (mii->cbSize != sizeof(MENUITEMINFOW) &&
mii->cbSize != sizeof(MENUITEMINFOW) - sizeof(HBITMAP))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if(!(mii->fMask & (MIIM_TYPE | MIIM_STRING)))
{
/* No text requested, just pass on */
return NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) mii, FALSE);
}
String = mii->dwTypeData;
Count = mii->cch;
RtlCopyMemory(&miiW, mii, mii->cbSize);
miiW.dwTypeData = 0;
if (String)
{
miiW.dwTypeData = RtlAllocateHeap(GetProcessHeap(), 0,
miiW.cch * sizeof(WCHAR));
if (miiW.dwTypeData == NULL) return FALSE;
miiW.dwTypeData[0] = 0;
}
if (!NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) &miiW, FALSE))
{
if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
return FALSE;
}
RtlCopyMemory(mii, &miiW, miiW.cbSize); // Okay to over write user data.
if (!String || !Count)
{
if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
mii->dwTypeData = String; // may not be zero.
mii->cch = miiW.cch;
return TRUE;
}
if ((miiW.fMask & MIIM_STRING) || (IS_STRING_ITEM(miiW.fType)))
{
lstrcpynW( String, miiW.dwTypeData, Count );
}
RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
mii->dwTypeData = String;
mii->cch = strlenW(String);
return TRUE;
}
/*
* @implemented
*/
UINT
WINAPI
GetMenuState(
HMENU hMenu,
UINT uId,
UINT uFlags)
{
ROSMENUINFO MenuInfo;
ROSMENUITEMINFO mii;
memset( &mii, 0, sizeof(mii) );
mii.cbSize = sizeof(MENUITEMINFOW);
mii.fMask = MIIM_STATE | MIIM_FTYPE | MIIM_SUBMENU;
SetLastError(0);
if(NtUserMenuItemInfo(hMenu, uId, uFlags, &mii, FALSE))
{
UINT nSubItems = 0;
if(mii.hSubMenu)
{
if (! MenuGetRosMenuInfo(&MenuInfo, mii.hSubMenu))
{
return (UINT) -1;
}
nSubItems = MenuInfo.MenuItemCount;
/* FIXME - ported from wine, does that work (0xff)? */
if(GetLastError() != ERROR_INVALID_MENU_HANDLE)
return (nSubItems << 8) | ((mii.fState | mii.fType) & 0xff);
return (UINT)-1; /* Invalid submenu */
}
/* FIXME - ported from wine, does that work? */
return (mii.fType | mii.fState);
}
return (UINT)-1;
}
/*
* @implemented
*/
int
WINAPI
GetMenuStringA(
HMENU hMenu,
UINT uIDItem,
LPSTR lpString,
int nMaxCount,
UINT uFlag)
{
MENUITEMINFOA mii;
memset( &mii, 0, sizeof(mii) );
mii.dwTypeData = lpString;
mii.fMask = MIIM_STRING | MIIM_FTYPE;
mii.fType = MFT_STRING;
mii.cbSize = sizeof(MENUITEMINFOA);
mii.cch = nMaxCount;
if(!(GetMenuItemInfoA( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&mii)))
return 0;
else
return mii.cch;
}
/*
* @implemented
*/
int
WINAPI
GetMenuStringW(
HMENU hMenu,
UINT uIDItem,
LPWSTR lpString,
int nMaxCount,
UINT uFlag)
{
MENUITEMINFOW miiW;
memset( &miiW, 0, sizeof(miiW) );
miiW.dwTypeData = lpString;
miiW.fMask = MIIM_STRING | MIIM_FTYPE;
miiW.fType = MFT_STRING;
miiW.cbSize = sizeof(MENUITEMINFOW);
miiW.cch = nMaxCount;
if(!(GetMenuItemInfoW( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&miiW)))
return 0;
else
return miiW.cch;
}
/*
* @implemented
*/
HMENU
WINAPI
GetSubMenu(
HMENU hMenu,
int nPos)
{
ROSMENUITEMINFO mi;
mi.cbSize = sizeof(MENUITEMINFOW);
mi.fMask = MIIM_SUBMENU;
if (NtUserMenuItemInfo(hMenu, (UINT)nPos, MF_BYPOSITION, &mi, FALSE))
{
return IsMenu(mi.hSubMenu) ? mi.hSubMenu : NULL;
}
return NULL;
}
/*
* @implemented
*/
HMENU
WINAPI
GetSystemMenu(
HWND hWnd,
BOOL bRevert)
{
HMENU TopMenu;
TopMenu = NtUserGetSystemMenu(hWnd, bRevert);
return NULL == TopMenu ? NULL : GetSubMenu(TopMenu, 0);
}
/*
* @implemented
*/
BOOL
WINAPI
InsertMenuA(
HMENU hMenu,
UINT uPosition,
UINT uFlags,
UINT_PTR uIDNewItem,
LPCSTR lpNewItem)
{
MENUITEMINFOA mii;
memset( &mii, 0, sizeof(mii) );
mii.cbSize = sizeof(MENUITEMINFOA);
mii.fMask = MIIM_FTYPE;
MenuSetItemData((LPMENUITEMINFOW) &mii,
uFlags,
uIDNewItem,
(LPCWSTR) lpNewItem,
FALSE);
return InsertMenuItemA(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii);
}
/*
* @implemented
*/
BOOL
WINAPI
InsertMenuItemA(
HMENU hMenu,
UINT uItem,
BOOL fByPosition,
LPCMENUITEMINFOA lpmii)
{
MENUITEMINFOW mi;
UNICODE_STRING MenuText;
BOOL res = FALSE;
BOOL CleanHeap = FALSE;
NTSTATUS Status;
if((lpmii->cbSize == sizeof(MENUITEMINFOA)) ||
(lpmii->cbSize == sizeof(MENUITEMINFOA) - sizeof(HBITMAP)))
{
RtlCopyMemory ( &mi, lpmii, lpmii->cbSize );
if( lpmii->cbSize != sizeof( MENUITEMINFOW))
{
mi.cbSize = sizeof( MENUITEMINFOW);
mi.hbmpItem = NULL;
}
/* copy the text string */
if (((mi.fMask & MIIM_STRING) ||
((mi.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mi.fType) == MF_STRING)))
&& mi.dwTypeData != NULL)
{
Status = RtlCreateUnicodeStringFromAsciiz(&MenuText, (LPSTR)mi.dwTypeData);
if (!NT_SUCCESS (Status))
{
SetLastError (RtlNtStatusToDosError(Status));
return FALSE;
}
mi.dwTypeData = MenuText.Buffer;
mi.cch = MenuText.Length / sizeof(WCHAR);
CleanHeap = TRUE;
}
res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mi, NULL);
if ( CleanHeap ) RtlFreeUnicodeString ( &MenuText );
}
return res;
}
/*
* @implemented
*/
BOOL
WINAPI
InsertMenuItemW(
HMENU hMenu,
UINT uItem,
BOOL fByPosition,
LPCMENUITEMINFOW lpmii)
{
MENUITEMINFOW mi;
UNICODE_STRING MenuText;
BOOL res = FALSE;
/* while we could just pass 'lpmii' to win32k, we make a copy so that
if a bad user passes bad data, we crash his process instead of the
entire kernel */
if((lpmii->cbSize == sizeof(MENUITEMINFOW)) ||
(lpmii->cbSize == sizeof(MENUITEMINFOW) - sizeof(HBITMAP)))
{
RtlCopyMemory(&mi, lpmii, lpmii->cbSize);
if( lpmii->cbSize != sizeof( MENUITEMINFOW))
{
mi.cbSize = sizeof( MENUITEMINFOW);
mi.hbmpItem = NULL;
}
/* copy the text string */
if (((mi.fMask & MIIM_STRING) ||
((mi.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mi.fType) == MF_STRING)))
&& mi.dwTypeData != NULL)
{
RtlInitUnicodeString(&MenuText, (PWSTR)lpmii->dwTypeData);
mi.dwTypeData = MenuText.Buffer;
mi.cch = MenuText.Length / sizeof(WCHAR);
}
res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mi, NULL);
}
return res;
}
/*
* @implemented
*/
BOOL
WINAPI
InsertMenuW(
HMENU hMenu,
UINT uPosition,
UINT uFlags,
UINT_PTR uIDNewItem,
LPCWSTR lpNewItem)
{
MENUITEMINFOW mii;
memset( &mii, 0, sizeof(mii) );
mii.cbSize = sizeof(MENUITEMINFOW);
mii.fMask = MIIM_FTYPE;
MenuSetItemData( &mii,
uFlags,
uIDNewItem,
lpNewItem,
TRUE);
return InsertMenuItemW(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii);
}
/*
* @implemented
*/
BOOL
WINAPI
IsMenu(
HMENU Menu)
{
ROSMENUINFO MenuInfo;
return MenuGetRosMenuInfo(&MenuInfo, Menu);
}
/*
* @implemented
*/
HMENU WINAPI
LoadMenuA(HINSTANCE hInstance,
LPCSTR lpMenuName)
{
HANDLE Resource = FindResourceA(hInstance, lpMenuName, MAKEINTRESOURCEA(4));
if (Resource == NULL)
{
return(NULL);
}
return(LoadMenuIndirectA((PVOID)LoadResource(hInstance, Resource)));
}
/*
* @implemented
*/
HMENU WINAPI
LoadMenuIndirectA(CONST MENUTEMPLATE *lpMenuTemplate)
{
return(LoadMenuIndirectW(lpMenuTemplate));
}
/*
* @implemented
*/
HMENU WINAPI
LoadMenuIndirectW(CONST MENUTEMPLATE *lpMenuTemplate)
{
HMENU hMenu;
WORD version, offset;
LPCSTR p = (LPCSTR)lpMenuTemplate;
version = GET_WORD(p);
p += sizeof(WORD);
switch (version)
{
case 0: /* standard format is version of 0 */
offset = GET_WORD(p);
p += sizeof(WORD) + offset;
if (!(hMenu = CreateMenu())) return 0;
if (!MENU_ParseResource(p, hMenu, TRUE))
{
DestroyMenu(hMenu);
return 0;
}
return hMenu;
case 1: /* extended format is version of 1 */
offset = GET_WORD(p);
p += sizeof(WORD) + offset;
if (!(hMenu = CreateMenu())) return 0;
if (!MENUEX_ParseResource(p, hMenu))
{
DestroyMenu( hMenu );
return 0;
}
return hMenu;
default:
DbgPrint("LoadMenuIndirectW(): version %d not supported.\n", version);
return 0;
}
}
/*
* @implemented
*/
HMENU WINAPI
LoadMenuW(HINSTANCE hInstance,
LPCWSTR lpMenuName)
{
HANDLE Resource = FindResourceW(hInstance, lpMenuName, RT_MENU);
if (Resource == NULL)
{
return(NULL);
}
return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource)));
}
/*
* @implemented
*/
int
WINAPI
MenuItemFromPoint(
HWND hWnd,
HMENU hMenu,
POINT ptScreen)
{
return NtUserMenuItemFromPoint(hWnd, hMenu, ptScreen.x, ptScreen.y);
}
/*
* @implemented
*/
BOOL
WINAPI
ModifyMenuA(
HMENU hMnu,
UINT uPosition,
UINT uFlags,
UINT_PTR uIDNewItem,
LPCSTR lpNewItem)
{
ROSMENUINFO mi;
ROSMENUITEMINFO rmii;
MENUITEMINFOA mii;
memset( &mii, 0, sizeof(mii) );
mii.cbSize = sizeof(MENUITEMINFOA);
mii.fMask = MIIM_FTYPE;
if (!MenuGetRosMenuInfo( &mi, hMnu )) return FALSE;
mi.Height = 0;
if (!MenuSetRosMenuInfo( &mi )) return FALSE;
MenuInitRosMenuItemInfo( &rmii );
if(!MenuGetRosMenuItemInfo( hMnu, uPosition, &rmii)) return FALSE;
if ((rmii.fType & MF_POPUP) && (uFlags & MF_POPUP) && (rmii.hSubMenu != (HMENU)uIDNewItem))
NtUserDestroyMenu( rmii.hSubMenu ); /* ModifyMenu() spec */
MenuCleanupRosMenuItemInfo( &rmii );
MenuSetItemData((LPMENUITEMINFOW) &mii,
uFlags,
uIDNewItem,
(LPCWSTR) lpNewItem,
FALSE);
return SetMenuItemInfoA( hMnu,
uPosition,
(BOOL)(MF_BYPOSITION & uFlags),
&mii);
}
/*
* @implemented
*/
BOOL
WINAPI
ModifyMenuW(
HMENU hMnu,
UINT uPosition,
UINT uFlags,
UINT_PTR uIDNewItem,
LPCWSTR lpNewItem)
{
ROSMENUINFO mi;
ROSMENUITEMINFO rmii;
MENUITEMINFOW mii;
memset ( &mii, 0, sizeof(mii) );
mii.cbSize = sizeof(MENUITEMINFOW);
mii.fMask = MIIM_FTYPE;
if (!MenuGetRosMenuInfo( &mi, hMnu )) return FALSE;
mi.Height = 0; // Force size recalculation.
if (!MenuSetRosMenuInfo( &mi )) return FALSE;
MenuInitRosMenuItemInfo( &rmii );
if(!MenuGetRosMenuItemInfo( hMnu, uPosition, &rmii)) return FALSE;
if ((rmii.fType & MF_POPUP) && (uFlags & MF_POPUP) && (rmii.hSubMenu != (HMENU)uIDNewItem))
NtUserDestroyMenu( rmii.hSubMenu ); /* ModifyMenu() spec */
MenuCleanupRosMenuItemInfo( &rmii );
/* Init new data for this menu item */
MenuSetItemData( &mii,
uFlags,
uIDNewItem,
lpNewItem,
TRUE);
/* Now, make Win32k IntSetMenuItemInfo handle the changes to this menu item. */
return SetMenuItemInfoW( hMnu,
uPosition,
(BOOL)(MF_BYPOSITION & uFlags),
&mii);
}
/*
* @implemented
*/
BOOL WINAPI
SetMenu(HWND hWnd,
HMENU hMenu)
{
return NtUserSetMenu(hWnd, hMenu, TRUE);
}
/*
* @implemented
*/
BOOL
WINAPI
SetMenuInfo(
HMENU hmenu,
LPCMENUINFO lpcmi)
{
ROSMENUINFO mi;
BOOL res = FALSE;
if(lpcmi->cbSize != sizeof(MENUINFO))
return res;
memcpy(&mi, lpcmi, sizeof(MENUINFO));
return NtUserMenuInfo(hmenu, &mi, TRUE);
}
/*
* @implemented
*/
BOOL
WINAPI
SetMenuItemBitmaps(
HMENU hMenu,
UINT uPosition,
UINT uFlags,
HBITMAP hBitmapUnchecked,
HBITMAP hBitmapChecked)
{
ROSMENUITEMINFO uItem;
memset ( &uItem, 0, sizeof(uItem) );
uItem.fMask = MIIM_STATE | MIIM_BITMAP;
if(!(NtUserMenuItemInfo(hMenu, uPosition,
(BOOL)(MF_BYPOSITION & uFlags), &uItem, FALSE))) return FALSE;
if (!hBitmapChecked && !hBitmapUnchecked)
{
uItem.fState &= ~MF_USECHECKBITMAPS;
}
else /* Install new bitmaps */
{
uItem.hbmpChecked = hBitmapChecked;
uItem.hbmpUnchecked = hBitmapUnchecked;
uItem.fState |= MF_USECHECKBITMAPS;
}
return NtUserMenuItemInfo(hMenu, uPosition,
(BOOL)(MF_BYPOSITION & uFlags), &uItem, TRUE);
}
/*
* @implemented
*/
BOOL
WINAPI
SetMenuItemInfoA(
HMENU hMenu,
UINT uItem,
BOOL fByPosition,
LPCMENUITEMINFOA lpmii)
{
MENUITEMINFOW MenuItemInfoW;
UNICODE_STRING UnicodeString;
NTSTATUS Status;
ULONG Result = FALSE;
RtlCopyMemory(&MenuItemInfoW, lpmii, min(lpmii->cbSize, sizeof(MENUITEMINFOW)));
if( lpmii->cbSize != sizeof( MENUITEMINFOW))
{
MenuItemInfoW.cbSize = sizeof( MENUITEMINFOW);
MenuItemInfoW.hbmpItem = NULL;
}
/*
* MIIM_STRING == good
* MIIM_TYPE & MFT_STRING == good
* MIIM_STRING & MFT_STRING == good
* MIIM_STRING & MFT_OWNERSRAW == good
*/
if (((MenuItemInfoW.fMask & MIIM_STRING) ||
((MenuItemInfoW.fMask & MIIM_TYPE) &&
(MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
&& MenuItemInfoW.dwTypeData != NULL)
{
/* cch is ignored when the content of a menu item is set by calling SetMenuItemInfo. */
Status = RtlCreateUnicodeStringFromAsciiz(&UnicodeString,
(LPSTR)MenuItemInfoW.dwTypeData);
if (!NT_SUCCESS (Status))
{
SetLastError (RtlNtStatusToDosError(Status));
return FALSE;
}
MenuItemInfoW.dwTypeData = UnicodeString.Buffer;
MenuItemInfoW.cch = UnicodeString.Length / sizeof(WCHAR);
}
else
{
UnicodeString.Buffer = NULL;
}
Result = NtUserMenuItemInfo(hMenu, uItem, fByPosition,
(PROSMENUITEMINFO)&MenuItemInfoW, TRUE);
if (UnicodeString.Buffer != NULL)
{
RtlFreeUnicodeString(&UnicodeString);
}
return Result;
}
/*
* @implemented
*/
BOOL
WINAPI
SetMenuItemInfoW(
HMENU hMenu,
UINT uItem,
BOOL fByPosition,
LPCMENUITEMINFOW lpmii)
{
MENUITEMINFOW MenuItemInfoW;
ULONG Result;
RtlCopyMemory(&MenuItemInfoW, lpmii, min(lpmii->cbSize, sizeof(MENUITEMINFOW)));
if( lpmii->cbSize != sizeof( MENUITEMINFOW))
{
MenuItemInfoW.cbSize = sizeof( MENUITEMINFOW);
MenuItemInfoW.hbmpItem = NULL;
}
if (((MenuItemInfoW.fMask & MIIM_STRING) ||
((MenuItemInfoW.fMask & MIIM_TYPE) &&
(MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
&& MenuItemInfoW.dwTypeData != NULL)
{
MenuItemInfoW.cch = strlenW(MenuItemInfoW.dwTypeData);
}
Result = NtUserMenuItemInfo(hMenu, uItem, fByPosition,
(PROSMENUITEMINFO)&MenuItemInfoW, TRUE);
return Result;
}
/*
* @implemented
*/
BOOL
WINAPI
SetSystemMenu (
HWND hwnd,
HMENU hMenu)
{
if(!hwnd)
{
SetLastError(ERROR_INVALID_WINDOW_HANDLE);
return FALSE;
}
if(!hMenu)
{
SetLastError(ERROR_INVALID_MENU_HANDLE);
return FALSE;
}
return NtUserSetSystemMenu(hwnd, hMenu);
}
/*
* @implemented
*/
BOOL
WINAPI
TrackPopupMenu(
HMENU Menu,
UINT Flags,
int x,
int y,
int Reserved,
HWND Wnd,
CONST RECT *Rect)
{
BOOL ret = FALSE;
MenuInitTracking(Wnd, Menu, TRUE, Flags);
/* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
if (0 == (Flags & TPM_NONOTIFY))
{
SendMessageW(Wnd, WM_INITMENUPOPUP, (WPARAM) Menu, 0);
}
if (MenuShowPopup(Wnd, Menu, 0, x, y, 0, 0 ))
{
ret = MenuTrackMenu(Menu, Flags | TPM_POPUPMENU, 0, 0, Wnd, Rect);
}
MenuExitTracking(Wnd);
return ret;
}
/*
* @unimplemented
*/
BOOL
WINAPI
TrackPopupMenuEx(
HMENU Menu,
UINT Flags,
int x,
int y,
HWND Wnd,
LPTPMPARAMS Tpm)
{
/* Not fully implemented */
return TrackPopupMenu(Menu, Flags, x, y, 0, Wnd,
NULL != Tpm ? &Tpm->rcExclude : NULL);
}
//
// Example for the Win32/User32 rewrite.
// Def = TrackPopupMenuEx@24=NtUserTrackPopupMenuEx@24
//
//
BOOL
WINAPI
NEWTrackPopupMenu(
HMENU Menu,
UINT Flags,
int x,
int y,
int Reserved,
HWND Wnd,
CONST RECT *Rect)
{
return NtUserTrackPopupMenuEx( Menu,
Flags,
x,
y,
Wnd,
NULL); // LPTPMPARAMS is null
}
/*
* @implemented
*/
DWORD
WINAPI
GetMenuContextHelpId(HMENU hmenu)
{
ROSMENUINFO mi;
mi.cbSize = sizeof(ROSMENUINFO);
mi.fMask = MIM_HELPID;
if(NtUserMenuInfo(hmenu, &mi, FALSE))
{
return mi.dwContextHelpID;
}
return 0;
}
/*
* @unimplemented
*/
LRESULT
WINAPI
MenuWindowProcA(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @unimplemented
*/
LRESULT
WINAPI
MenuWindowProcW(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @implemented
*/
BOOL
WINAPI
ChangeMenuW(
HMENU hMenu,
UINT cmd,
LPCWSTR lpszNewItem,
UINT cmdInsert,
UINT flags)
{
/*
FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
for MF_DELETE. We should check the parameters for all others
MF_* actions also (anybody got a doc on ChangeMenu?).
*/
switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
{
case MF_APPEND :
return AppendMenuW(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
case MF_DELETE :
return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
case MF_CHANGE :
return ModifyMenuW(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
case MF_REMOVE :
return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
flags &~ MF_REMOVE);
default : /* MF_INSERT */
return InsertMenuW(hMenu, cmd, flags, cmdInsert, lpszNewItem);
};
}
/*
* @implemented
*/
BOOL
WINAPI
ChangeMenuA(
HMENU hMenu,
UINT cmd,
LPCSTR lpszNewItem,
UINT cmdInsert,
UINT flags)
{
/*
FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
for MF_DELETE. We should check the parameters for all others
MF_* actions also (anybody got a doc on ChangeMenu?).
*/
switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
{
case MF_APPEND :
return AppendMenuA(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
case MF_DELETE :
return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
case MF_CHANGE :
return ModifyMenuA(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
case MF_REMOVE :
return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
flags &~ MF_REMOVE);
default : /* MF_INSERT */
return InsertMenuA(hMenu, cmd, flags, cmdInsert, lpszNewItem);
};
}