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.27 2003/08/21 20:29:43 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
)
203 flags
= GET_WORD(res
);
205 /* remove MF_END flag before passing it to AppendMenu()! */
206 end
= (flags
& MF_END
);
207 if(end
) flags
^= MF_END
;
210 if(!(flags
& MF_POPUP
))
217 res
+= strlen(str
) + 1;
219 res
+= (strlenW((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
220 if (flags
& MF_POPUP
)
222 HMENU hSubMenu
= CreatePopupMenu();
223 if(!hSubMenu
) return NULL
;
224 if(!(res
= MENU_ParseResource(res
, hSubMenu
, unicode
)))
227 AppendMenuA(hMenu
, flags
, (UINT
)hSubMenu
, str
);
229 AppendMenuW(hMenu
, flags
, (UINT
)hSubMenu
, (LPCWSTR
)str
);
231 else /* Not a popup */
234 AppendMenuA(hMenu
, flags
, id
, *str
? str
: NULL
);
236 AppendMenuW(hMenu
, flags
, id
,
237 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
246 User32LoadSysMenuTemplateForKernel(PVOID Arguments
, ULONG ArgumentLength
)
250 hUser32
= GetModuleHandleW(L
"user32.dll");
251 Result
= (LRESULT
)LoadMenuW(hUser32
, L
"SYSMENU");
252 return(ZwCallbackReturn(&Result
, sizeof(LRESULT
), STATUS_SUCCESS
));
259 NONCLIENTMETRICSW ncm
;
261 /* get the menu font */
262 if(!hMenuFont
|| !hMenuFontBold
)
264 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
), &ncm
, 0))
266 DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
270 hMenuFont
= CreateFontIndirectW(&ncm
.lfMenuFont
);
271 if(hMenuFont
== NULL
)
273 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
277 ncm
.lfMenuFont
.lfWeight
= max(ncm
.lfMenuFont
.lfWeight
+ 300, 1000);
278 hMenuFontBold
= CreateFontIndirectW(&ncm
.lfMenuFont
);
279 if(hMenuFontBold
== NULL
)
281 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
291 MenuGetMenuBarHeight(HWND hWnd
, ULONG MenuBarWidth
, LONG OrgX
, LONG OrgY
)
298 MenuId = GetWindowLong(hWnd, GWL_ID);
299 Menu = MenuGetMenu((HMENU)MenuId);
304 hDC = GetDCEx(hWnd, 0, DCX_CACHE | DCX_WINDOW);
305 SelectObject(hDC, hMenuFont);
306 SetRect(&Rect, OrgX, OrgY, OrgX + MenuBarWidth,
307 OrgY + GetSystemMetrics(SM_CYMENU));
308 MenuMenuBarCalcSize(hDC, &Rect, Menu, hWnd);
309 ReleaseDC(hWnd, hDC);*/
310 return(GetSystemMetrics(SM_CYMENU
));
315 MenuDrawMenuBar(HDC hDC
, LPRECT Rect
, HWND hWnd
, BOOL Draw
)
317 /* FIXME cache menu bar items using NtUserDrawMenuBarTemp() */
319 /* FIXME select menu font first */
321 DrawTextW(hDC
, L
"FIXME: Draw Menubar", -1, Rect
, DT_SINGLELINE
| DT_VCENTER
);
323 return(Rect
->bottom
- Rect
->top
);
328 MenuTrackMouseMenuBar(HWND hWnd
, ULONG Ht
, POINT Pt
)
334 MenuTrackKbdMenuBar(HWND hWnd
, ULONG wParam
, ULONG Key
)
338 /* FUNCTIONS *****************************************************************/
341 MenuIsStringItem(ULONG TypeData)
343 return((TypeData & MENU_TYPE_MASK) == MF_STRING);
351 AppendMenuA(HMENU hMenu
,
356 return(InsertMenuA(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
365 AppendMenuW(HMENU hMenu
,
370 return(InsertMenuW(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
379 CheckMenuItem(HMENU hmenu
,
383 return NtUserCheckMenuItem(hmenu
, uIDCheckItem
, uCheck
);
391 CheckMenuRadioItem(HMENU hmenu
,
408 return NtUserCreateMenu();
416 CreatePopupMenu(VOID
)
418 /* FIXME - add MF_POPUP style? */
419 return NtUserCreateMenu();
427 DeleteMenu(HMENU hMenu
,
431 return NtUserDeleteMenu(hMenu
, uPosition
, uFlags
);
439 DestroyMenu(HMENU hMenu
)
441 return NtUserDestroyMenu(hMenu
);
449 DrawMenuBar(HWND hWnd
)
452 /* FIXME - return NtUserCallHwndLock(hWnd, 0x55); */
461 EnableMenuItem(HMENU hMenu
,
465 return NtUserEnableMenuItem(hMenu
, uIDEnableItem
, uEnable
);
475 /* FIXME - return NtUserEndMenu(); */
486 return (HMENU
)NtUserCallOneParam((DWORD
)hWnd
, ONEPARAM_ROUTINE_GETMENU
);
494 GetMenuBarInfo(HWND hwnd
,
508 GetMenuCheckMarkDimensions(VOID
)
510 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK
),
511 GetSystemMetrics(SM_CYMENUCHECK
)));
519 GetMenuDefaultItem(HMENU hMenu
,
523 return NtUserGetMenuDefaultItem(hMenu
, fByPos
, gmdiFlags
);
531 GetMenuInfo(HMENU hmenu
,
537 if(!lpcmi
|| (lpcmi
->cbSize
!= sizeof(MENUINFO
)))
540 RtlZeroMemory(&mi
, sizeof(MENUINFO
));
541 mi
.cbSize
= sizeof(MENUINFO
);
542 mi
.fMask
= lpcmi
->fMask
;
544 res
= NtUserMenuInfo(hmenu
, &mi
, FALSE
);
546 memcpy(lpcmi
, &mi
, sizeof(MENUINFO
));
555 GetMenuItemCount(HMENU hMenu
)
557 return NtUserBuildMenuItemList(hMenu
, NULL
, 0, 0);
565 GetMenuItemID(HMENU hMenu
,
570 mii
.cbSize
= sizeof(MENUITEMINFOW
);
571 mii
.fMask
= MIIM_ID
| MIIM_SUBMENU
;
573 if(!NtUserMenuItemInfo(hMenu
, nPos
, MF_BYPOSITION
, &mii
, FALSE
))
578 if(mii
.hSubMenu
) return -1;
579 if(mii
.wID
== 0) return -1;
593 LPMENUITEMINFOA lpmii
)
609 LPMENUITEMINFOW lpmii
)
620 GetMenuItemRect(HWND hWnd
,
641 mii
.cbSize
= sizeof(MENUITEMINFOW
);
642 mii
.fMask
= MIIM_STATE
| MIIM_TYPE
| MIIM_SUBMENU
;
645 if(NtUserMenuItemInfo(hMenu
, uId
, uFlags
, &mii
, FALSE
))
650 nSubItems
= (UINT
)NtUserBuildMenuItemList(mii
.hSubMenu
, NULL
, 0, 0);
652 /* FIXME - ported from wine, does that work (0xff)? */
653 if(GetLastError() != ERROR_INVALID_MENU_HANDLE
)
654 return (nSubItems
<< 8) | ((mii
.fState
| mii
.fType
) & 0xff);
656 return (UINT
)-1; /* Invalid submenu */
659 /* FIXME - ported from wine, does that work? */
660 return (mii
.fType
| mii
.fState
);
711 mi
.cbSize
= sizeof(mi
);
712 mi
.fMask
= MIIM_SUBMENU
;
713 if(NtUserMenuItemInfo(hMenu
, (UINT
)nPos
, MF_BYPOSITION
, &mi
, FALSE
))
732 return NtUserHiliteMenuItem(hwnd
, hmenu
, uItemHilite
, uHilite
);
762 LPCMENUITEMINFOA lpmii
)
766 BOOL CleanHeap
= FALSE
;
769 if((lpmii
->cbSize
== sizeof(MENUITEMINFOA
)) ||
770 (lpmii
->cbSize
== sizeof(MENUITEMINFOA
) - sizeof(HBITMAP
)))
772 RtlMoveMemory ( &mi
, lpmii
, lpmii
->cbSize
);
774 /* copy the text string */
775 if((mi
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
776 (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
) && mi
.dwTypeData
)
778 Status
= HEAP_strdupAtoW ( &mi
.dwTypeData
, (LPCSTR
)mi
.dwTypeData
, &mi
.cch
);
779 if (!NT_SUCCESS (Status
))
781 SetLastError (RtlNtStatusToDosError(Status
));
787 res
= NtUserInsertMenuItem(hMenu
, uItem
, fByPosition
, &mi
);
789 if ( CleanHeap
) HEAP_free ( mi
.dwTypeData
);
804 LPCMENUITEMINFOW lpmii
)
808 BOOL CleanHeap
= FALSE
;
810 HANDLE hHeap
= RtlGetProcessHeap();
812 // while we could just pass 'lpmii' to win32k, we make a copy so that
813 // if a bad user passes bad data, we crash his process instead of the
816 if((lpmii
->cbSize
== sizeof(MENUITEMINFOW
)) ||
817 (lpmii
->cbSize
== sizeof(MENUITEMINFOW
) - sizeof(HBITMAP
)))
819 memcpy(&mi
, lpmii
, lpmii
->cbSize
);
821 /* copy the text string */
822 if((mi
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
823 (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
) && mi
.dwTypeData
)
825 len
= lstrlenW(lpmii
->dwTypeData
);
826 mi
.dwTypeData
= RtlAllocateHeap(hHeap
, 0, (len
+ 1) * sizeof(WCHAR
));
829 SetLastError (RtlNtStatusToDosError(STATUS_NO_MEMORY
));
832 memcpy(&mi
.dwTypeData
, &lpmii
->dwTypeData
, len
);
837 res
= NtUserInsertMenuItem(hMenu
, uItem
, fByPosition
, &mi
);
839 if(CleanHeap
) RtlFreeHeap (hHeap
, 0, mi
.dwTypeData
);
871 SetLastError(ERROR_SUCCESS
);
872 ret
= NtUserBuildMenuItemList(hMenu
, NULL
, 0, 0);
873 return ((ret
== (DWORD
)-1) || (GetLastError() == ERROR_INVALID_MENU_HANDLE
));
881 LoadMenuA(HINSTANCE hInstance
,
884 HANDLE Resource
= FindResourceA(hInstance
, lpMenuName
, MAKEINTRESOURCEA(4));
885 if (Resource
== NULL
)
889 return(LoadMenuIndirectA((PVOID
)LoadResource(hInstance
, Resource
)));
897 LoadMenuIndirectA(CONST MENUTEMPLATE
*lpMenuTemplate
)
899 return(LoadMenuIndirectW(lpMenuTemplate
));
907 LoadMenuIndirectW(CONST MENUTEMPLATE
*lpMenuTemplate
)
910 WORD version
, offset
;
911 LPCSTR p
= (LPCSTR
)lpMenuTemplate
;
913 version
= GET_WORD(p
);
918 case 0: /* standard format is version of 0 */
919 offset
= GET_WORD(p
);
920 p
+= sizeof(WORD
) + offset
;
921 if (!(hMenu
= CreateMenu())) return 0;
922 if (!MENU_ParseResource(p
, hMenu
, TRUE
))
928 case 1: /* extended format is version of 1 */
929 offset
= GET_WORD(p
);
930 p
+= sizeof(WORD
) + offset
;
931 if (!(hMenu
= CreateMenu())) return 0;
932 if (!MENUEX_ParseResource(p
, hMenu
))
934 DestroyMenu( hMenu
);
939 DbgPrint("LoadMenuIndirectW(): version %d not supported.\n", version
);
949 LoadMenuW(HINSTANCE hInstance
,
952 HANDLE Resource
= FindResourceW(hInstance
, lpMenuName
, RT_MENU
);
953 if (Resource
== NULL
)
957 return(LoadMenuIndirectW((PVOID
)LoadResource(hInstance
, Resource
)));
1002 UINT_PTR uIDNewItem
,
1020 return NtUserRemoveMenu(hMenu
, uPosition
, uFlags
);
1031 return NtUserSetMenu(hWnd
, hMenu
, TRUE
);
1045 return NtUserSetMenuDefaultItem(hMenu
, uItem
, fByPos
);
1060 if(lpcmi
->cbSize
!= sizeof(MENUINFO
))
1063 memcpy(&mi
, lpcmi
, sizeof(MENUINFO
));
1064 return NtUserMenuInfo(hmenu
, &mi
, TRUE
);
1077 HBITMAP hBitmapUnchecked
,
1078 HBITMAP hBitmapChecked
)
1093 WINBOOL fByPosition
,
1094 LPMENUITEMINFOA lpmii
)
1109 WINBOOL fByPosition
,
1110 LPMENUITEMINFOW lpmii
)
1129 CONST RECT
*prcRect
)
1159 SetMenuContextHelpId(HMENU hmenu
,
1160 DWORD dwContextHelpId
)
1162 return NtUserSetMenuContextHelpId(hmenu
, dwContextHelpId
);
1171 GetMenuContextHelpId(HMENU hmenu
)
1174 mi
.cbSize
= sizeof(MENUINFO
);
1175 mi
.fMask
= MIM_HELPID
;
1177 if(NtUserMenuInfo(hmenu
, &mi
, FALSE
))
1179 return mi
.dwContextHelpID
;