3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 /* $Id: menu.c,v 1.29 2003/08/23 17:06:07 weiden Exp $
21 * PROJECT: ReactOS user32.dll
22 * FILE: lib/user32/windows/menu.c
24 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
26 * 09-05-2001 CSH Created
29 /* INCLUDES ******************************************************************/
39 #include <user32/callback.h>
40 #include "user32/regcontrol.h"
41 #include "../controls/controls.h"
43 /* TYPES *********************************************************************/
45 #define MENU_TYPE_MASK ((MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
47 #define MENU_ITEM_TYPE(flags) \
48 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
50 #define MENU_BAR_ITEMS_SPACE (12)
51 #define SEPARATOR_HEIGHT (5)
52 #define MENU_TAB_SPACE (8)
55 #define MF_END (0x0080)
59 #define MIIM_STRING (0x00000040)
62 #define MAKEINTATOMA(atom) ((LPCSTR)((ULONG_PTR)((WORD)(atom))))
63 #define MAKEINTATOMW(atom) ((LPCWSTR)((ULONG_PTR)((WORD)(atom))))
64 #define POPUPMENU_CLASS_ATOMA MAKEINTATOMA(32768) /* PopupMenu */
65 #define POPUPMENU_CLASS_ATOMW MAKEINTATOMW(32768) /* PopupMenu */
67 /*********************************************************************
68 * PopupMenu class descriptor
70 const struct builtin_class_descr POPUPMENU_builtin_class
=
72 POPUPMENU_CLASS_ATOMW
, /* name */
73 CS_GLOBALCLASS
| CS_SAVEBITS
| CS_DBLCLKS
, /* style */
74 (WNDPROC
) NULL
, /* FIXME - procW */
75 sizeof(MENUINFO
*), /* extra */
76 (LPCWSTR
) IDC_ARROW
, /* cursor */
77 (HBRUSH
)COLOR_MENU
/* brush */
81 /* INTERNAL FUNCTIONS ********************************************************/
83 /* Rip the fun and easy to use and fun WINE unicode string manipulation routines.
84 * Of course I didnt copy the ASM code because we want this to be portable
85 * and it needs to go away.
88 static inline unsigned int strlenW( const WCHAR
*str
)
95 static inline WCHAR
*strncpyW( WCHAR
*str1
, const WCHAR
*str2
, int n
)
98 while (n
-- > 0) if (!(*str1
++ = *str2
++)) break;
99 while (n
-- > 0) *str1
++ = 0;
103 static inline WCHAR
*strcpyW( WCHAR
*dst
, const WCHAR
*src
)
106 while ((*p
++ = *src
++));
110 static inline WCHAR
*strcatW( WCHAR
*dst
, const WCHAR
*src
)
112 strcpyW( dst
+ strlenW(dst
), src
);
117 #define GET_WORD(ptr) (*(WORD *)(ptr))
120 #define GET_DWORD(ptr) (*(DWORD *)(ptr))
123 HFONT hMenuFont
= NULL
;
124 HFONT hMenuFontBold
= NULL
;
126 /**********************************************************************
127 * MENUEX_ParseResource
129 * Parse an extended menu resource and add items to the menu.
130 * Return a pointer to the end of the resource.
132 * FIXME - should we be passing an LPCSTR to a predominantly UNICODE function?
134 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
142 mii
.cbSize
= sizeof(mii
);
143 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
144 mii
.fType
= GET_DWORD(res
);
145 res
+= sizeof(DWORD
);
146 mii
.fState
= GET_DWORD(res
);
147 res
+= sizeof(DWORD
);
148 mii
.wID
= GET_DWORD(res
);
149 res
+= sizeof(DWORD
);
150 resinfo
= GET_WORD(res
);
152 /* Align the text on a word boundary. */
153 res
+= (~((int)res
- 1)) & 1;
154 mii
.dwTypeData
= (LPWSTR
) res
;
155 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
156 /* Align the following fields on a dword boundary. */
157 res
+= (~((int)res
- 1)) & 3;
159 if (resinfo
& 1) /* Pop-up? */
161 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
162 res
+= sizeof(DWORD
);
163 mii
.hSubMenu
= CreatePopupMenu();
166 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
)))
168 DestroyMenu(mii
.hSubMenu
);
171 mii
.fMask
|= MIIM_SUBMENU
;
172 mii
.fType
|= MF_POPUP
;
174 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
176 DbgPrint("WARN: Converting NULL menu item %04x, type %04x to SEPARATOR\n",
178 mii
.fType
|= MF_SEPARATOR
;
180 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
182 while (!(resinfo
& MF_END
));
187 /**********************************************************************
190 * Parse a standard menu resource and add items to the menu.
191 * Return a pointer to the end of the resource.
193 * NOTE: flags is equivalent to the mtOption field
195 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
204 flags
= GET_WORD(res
);
206 /* remove MF_END flag before passing it to AppendMenu()! */
207 end
= (flags
& MF_END
);
208 if(end
) flags
^= MF_END
;
211 if(!(flags
& MF_POPUP
))
218 res
+= strlen(str
) + 1;
220 res
+= (strlenW((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
221 if (flags
& MF_POPUP
)
223 hSubMenu
= CreatePopupMenu();
224 if(!hSubMenu
) return NULL
;
225 if(!(res
= MENU_ParseResource(res
, hSubMenu
, unicode
)))
228 AppendMenuA(hMenu
, flags
, (UINT
)hSubMenu
, str
);
230 AppendMenuW(hMenu
, flags
, (UINT
)hSubMenu
, (LPCWSTR
)str
);
232 else /* Not a popup */
235 AppendMenuA(hMenu
, flags
, id
, *str
? str
: NULL
);
237 AppendMenuW(hMenu
, flags
, id
,
238 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
247 User32LoadSysMenuTemplateForKernel(PVOID Arguments
, ULONG ArgumentLength
)
251 hUser32
= GetModuleHandleW(L
"USER32");
252 Result
= (LRESULT
)LoadMenuW(hUser32
, L
"SYSMENU");
253 return(ZwCallbackReturn(&Result
, sizeof(LRESULT
), STATUS_SUCCESS
));
260 NONCLIENTMETRICSW ncm
;
262 /* get the menu font */
263 if(!hMenuFont
|| !hMenuFontBold
)
265 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
), &ncm
, 0))
267 DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
271 hMenuFont
= CreateFontIndirectW(&ncm
.lfMenuFont
);
272 if(hMenuFont
== NULL
)
274 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
278 ncm
.lfMenuFont
.lfWeight
= max(ncm
.lfMenuFont
.lfWeight
+ 300, 1000);
279 hMenuFontBold
= CreateFontIndirectW(&ncm
.lfMenuFont
);
280 if(hMenuFontBold
== NULL
)
282 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
292 MenuGetMenuBarHeight(HWND hWnd
, ULONG MenuBarWidth
, LONG OrgX
, LONG OrgY
)
299 MenuId = GetWindowLong(hWnd, GWL_ID);
300 Menu = MenuGetMenu((HMENU)MenuId);
305 hDC = GetDCEx(hWnd, 0, DCX_CACHE | DCX_WINDOW);
306 SelectObject(hDC, hMenuFont);
307 SetRect(&Rect, OrgX, OrgY, OrgX + MenuBarWidth,
308 OrgY + GetSystemMetrics(SM_CYMENU));
309 MenuMenuBarCalcSize(hDC, &Rect, Menu, hWnd);
310 ReleaseDC(hWnd, hDC);*/
311 return(GetSystemMetrics(SM_CYMENU
));
316 MenuDrawMenuBar(HDC hDC
, LPRECT Rect
, HWND hWnd
, BOOL Draw
)
318 /* FIXME cache menu bar items using NtUserDrawMenuBarTemp() */
320 /* FIXME select menu font first */
322 DrawTextW(hDC
, L
"FIXME: Draw Menubar", -1, Rect
, DT_SINGLELINE
| DT_VCENTER
);
324 return(Rect
->bottom
- Rect
->top
);
329 MenuTrackMouseMenuBar(HWND hWnd
, ULONG Ht
, POINT Pt
)
335 MenuTrackKbdMenuBar(HWND hWnd
, ULONG wParam
, ULONG Key
)
339 /* FUNCTIONS *****************************************************************/
342 MenuIsStringItem(ULONG TypeData)
344 return((TypeData & MENU_TYPE_MASK) == MF_STRING);
352 AppendMenuA(HMENU hMenu
,
357 return(InsertMenuA(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
366 AppendMenuW(HMENU hMenu
,
371 return(InsertMenuW(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
380 CheckMenuItem(HMENU hmenu
,
384 return NtUserCheckMenuItem(hmenu
, uIDCheckItem
, uCheck
);
392 CheckMenuRadioItem(HMENU hmenu
,
409 return NtUserCreateMenu();
417 CreatePopupMenu(VOID
)
419 /* FIXME - add MF_POPUP style? */
420 return NtUserCreateMenu();
428 DeleteMenu(HMENU hMenu
,
432 return NtUserDeleteMenu(hMenu
, uPosition
, uFlags
);
440 DestroyMenu(HMENU hMenu
)
442 return NtUserDestroyMenu(hMenu
);
450 DrawMenuBar(HWND hWnd
)
453 /* FIXME - return NtUserCallHwndLock(hWnd, 0x55); */
462 EnableMenuItem(HMENU hMenu
,
466 return NtUserEnableMenuItem(hMenu
, uIDEnableItem
, uEnable
);
476 /* FIXME - return NtUserEndMenu(); */
487 return (HMENU
)NtUserCallOneParam((DWORD
)hWnd
, ONEPARAM_ROUTINE_GETMENU
);
495 GetMenuBarInfo(HWND hwnd
,
509 GetMenuCheckMarkDimensions(VOID
)
511 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK
),
512 GetSystemMetrics(SM_CYMENUCHECK
)));
520 GetMenuDefaultItem(HMENU hMenu
,
524 return NtUserGetMenuDefaultItem(hMenu
, fByPos
, gmdiFlags
);
532 GetMenuInfo(HMENU hmenu
,
538 if(!lpcmi
|| (lpcmi
->cbSize
!= sizeof(MENUINFO
)))
541 RtlZeroMemory(&mi
, sizeof(MENUINFO
));
542 mi
.cbSize
= sizeof(MENUINFO
);
543 mi
.fMask
= lpcmi
->fMask
;
545 res
= NtUserMenuInfo(hmenu
, &mi
, FALSE
);
547 memcpy(lpcmi
, &mi
, sizeof(MENUINFO
));
556 GetMenuItemCount(HMENU hMenu
)
558 return NtUserBuildMenuItemList(hMenu
, NULL
, 0, 0);
566 GetMenuItemID(HMENU hMenu
,
571 mii
.cbSize
= sizeof(MENUITEMINFOW
);
572 mii
.fMask
= MIIM_ID
| MIIM_SUBMENU
;
574 if(!NtUserMenuItemInfo(hMenu
, nPos
, MF_BYPOSITION
, &mii
, FALSE
))
579 if(mii
.hSubMenu
) return -1;
580 if(mii
.wID
== 0) return -1;
594 LPMENUITEMINFOA lpmii
)
610 LPMENUITEMINFOW lpmii
)
621 GetMenuItemRect(HWND hWnd
,
642 mii
.cbSize
= sizeof(MENUITEMINFOW
);
643 mii
.fMask
= MIIM_STATE
| MIIM_TYPE
| MIIM_SUBMENU
;
646 if(NtUserMenuItemInfo(hMenu
, uId
, uFlags
, &mii
, FALSE
))
651 nSubItems
= (UINT
)NtUserBuildMenuItemList(mii
.hSubMenu
, NULL
, 0, 0);
653 /* FIXME - ported from wine, does that work (0xff)? */
654 if(GetLastError() != ERROR_INVALID_MENU_HANDLE
)
655 return (nSubItems
<< 8) | ((mii
.fState
| mii
.fType
) & 0xff);
657 return (UINT
)-1; /* Invalid submenu */
660 /* FIXME - ported from wine, does that work? */
661 return (mii
.fType
| mii
.fState
);
712 mi
.cbSize
= sizeof(mi
);
713 mi
.fMask
= MIIM_SUBMENU
;
714 if(NtUserMenuItemInfo(hMenu
, (UINT
)nPos
, MF_BYPOSITION
, &mi
, FALSE
))
733 return NtUserHiliteMenuItem(hwnd
, hmenu
, uItemHilite
, uHilite
);
750 mii
.cbSize
= sizeof(MENUITEMINFOA
);
751 mii
.fMask
= MIIM_FTYPE
| MIIM_STRING
;
753 if(uFlags
& MF_BITMAP
)
754 mii
.fType
|= MFT_BITMAP
;
757 if(uFlags
& MF_STRING
)
759 mii
.fType
|= MFT_STRING
;
761 else if(uFlags
& MF_OWNERDRAW
)
763 mii
.fType
|= MFT_OWNERDRAW
;
767 SetLastError(ERROR_INVALID_PARAMETER
);
771 mii
.dwTypeData
= (LPSTR
)lpNewItem
;
773 return InsertMenuItemA(hMenu
, uPosition
, (WINBOOL
)!(MF_BYPOSITION
& uFlags
), &mii
);
786 LPCMENUITEMINFOA lpmii
)
790 BOOL CleanHeap
= FALSE
;
793 if((lpmii
->cbSize
== sizeof(MENUITEMINFOA
)) ||
794 (lpmii
->cbSize
== sizeof(MENUITEMINFOA
) - sizeof(HBITMAP
)))
796 RtlMoveMemory ( &mi
, lpmii
, lpmii
->cbSize
);
798 /* copy the text string */
799 if((mi
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
800 (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
) && mi
.dwTypeData
)
802 Status
= HEAP_strdupAtoW ( &mi
.dwTypeData
, (LPCSTR
)mi
.dwTypeData
, &mi
.cch
);
803 if (!NT_SUCCESS (Status
))
805 SetLastError (RtlNtStatusToDosError(Status
));
811 res
= NtUserInsertMenuItem(hMenu
, uItem
, fByPosition
, &mi
);
813 if ( CleanHeap
) HEAP_free ( mi
.dwTypeData
);
828 LPCMENUITEMINFOW lpmii
)
832 BOOL CleanHeap
= FALSE
;
834 HANDLE hHeap
= RtlGetProcessHeap();
835 mi
.hbmpItem
= (HBITMAP
)0;
837 // while we could just pass 'lpmii' to win32k, we make a copy so that
838 // if a bad user passes bad data, we crash his process instead of the
841 if((lpmii
->cbSize
== sizeof(MENUITEMINFOW
)) ||
842 (lpmii
->cbSize
== sizeof(MENUITEMINFOW
) - sizeof(HBITMAP
)))
844 memcpy(&mi
, lpmii
, lpmii
->cbSize
);
846 /* copy the text string */
847 if((mi
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
848 (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
) && mi
.dwTypeData
)
852 len
= lstrlenW(lpmii
->dwTypeData
);
853 mi
.dwTypeData
= RtlAllocateHeap(hHeap
, 0, (len
+ 1) * sizeof(WCHAR
));
856 SetLastError (RtlNtStatusToDosError(STATUS_NO_MEMORY
));
859 memcpy(&mi
.dwTypeData
, &lpmii
->dwTypeData
, len
);
865 res
= NtUserInsertMenuItem(hMenu
, uItem
, fByPosition
, &mi
);
867 if(CleanHeap
) RtlFreeHeap (hHeap
, 0, mi
.dwTypeData
);
886 mii
.cbSize
= sizeof(MENUITEMINFOW
);
887 mii
.fMask
= MIIM_FTYPE
| MIIM_STRING
;
890 if(uFlags
& MF_BITMAP
)
892 mii
.fType
|= MFT_BITMAP
;
894 else if(uFlags
& MF_OWNERDRAW
)
896 mii
.fType
|= MFT_OWNERDRAW
;
898 else if(uFlags
& MF_POPUP
)
900 mii
.fMask
|= MIIM_SUBMENU
;
901 mii
.hSubMenu
= (HMENU
)uIDNewItem
;
904 mii
.dwTypeData
= (LPWSTR
)lpNewItem
;
906 return InsertMenuItemW(hMenu
, uPosition
, (WINBOOL
)!(MF_BYPOSITION
& uFlags
), &mii
);
919 SetLastError(ERROR_SUCCESS
);
920 ret
= NtUserBuildMenuItemList(hMenu
, NULL
, 0, 0);
921 return ((ret
== (DWORD
)-1) || (GetLastError() == ERROR_INVALID_MENU_HANDLE
));
929 LoadMenuA(HINSTANCE hInstance
,
932 HANDLE Resource
= FindResourceA(hInstance
, lpMenuName
, MAKEINTRESOURCEA(4));
933 if (Resource
== NULL
)
937 return(LoadMenuIndirectA((PVOID
)LoadResource(hInstance
, Resource
)));
945 LoadMenuIndirectA(CONST MENUTEMPLATE
*lpMenuTemplate
)
947 return(LoadMenuIndirectW(lpMenuTemplate
));
955 LoadMenuIndirectW(CONST MENUTEMPLATE
*lpMenuTemplate
)
958 WORD version
, offset
;
959 LPCSTR p
= (LPCSTR
)lpMenuTemplate
;
961 version
= GET_WORD(p
);
966 case 0: /* standard format is version of 0 */
967 offset
= GET_WORD(p
);
968 p
+= sizeof(WORD
) + offset
;
969 if (!(hMenu
= CreateMenu())) return 0;
970 if (!MENU_ParseResource(p
, hMenu
, TRUE
))
976 case 1: /* extended format is version of 1 */
977 offset
= GET_WORD(p
);
978 p
+= sizeof(WORD
) + offset
;
979 if (!(hMenu
= CreateMenu())) return 0;
980 if (!MENUEX_ParseResource(p
, hMenu
))
982 DestroyMenu( hMenu
);
987 DbgPrint("LoadMenuIndirectW(): version %d not supported.\n", version
);
997 LoadMenuW(HINSTANCE hInstance
,
1000 HANDLE Resource
= FindResourceW(hInstance
, lpMenuName
, RT_MENU
);
1001 if (Resource
== NULL
)
1005 return(LoadMenuIndirectW((PVOID
)LoadResource(hInstance
, Resource
)));
1033 UINT_PTR uIDNewItem
,
1050 UINT_PTR uIDNewItem
,
1068 return NtUserRemoveMenu(hMenu
, uPosition
, uFlags
);
1079 return NtUserSetMenu(hWnd
, hMenu
, TRUE
);
1093 return NtUserSetMenuDefaultItem(hMenu
, uItem
, fByPos
);
1108 if(lpcmi
->cbSize
!= sizeof(MENUINFO
))
1111 memcpy(&mi
, lpcmi
, sizeof(MENUINFO
));
1112 return NtUserMenuInfo(hmenu
, &mi
, TRUE
);
1125 HBITMAP hBitmapUnchecked
,
1126 HBITMAP hBitmapChecked
)
1141 WINBOOL fByPosition
,
1142 LPMENUITEMINFOA lpmii
)
1157 WINBOOL fByPosition
,
1158 LPMENUITEMINFOW lpmii
)
1177 CONST RECT
*prcRect
)
1207 SetMenuContextHelpId(HMENU hmenu
,
1208 DWORD dwContextHelpId
)
1210 return NtUserSetMenuContextHelpId(hmenu
, dwContextHelpId
);
1219 GetMenuContextHelpId(HMENU hmenu
)
1222 mi
.cbSize
= sizeof(MENUINFO
);
1223 mi
.fMask
= MIM_HELPID
;
1225 if(NtUserMenuInfo(hmenu
, &mi
, FALSE
))
1227 return mi
.dwContextHelpID
;