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.19 2003/08/06 13:17: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 ******************************************************************/
38 /* TYPES *********************************************************************/
40 #define MENU_TYPE_MASK ((MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
42 #define MENU_ITEM_TYPE(flags) \
43 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
45 #define MENU_BAR_ITEMS_SPACE (12)
46 #define SEPARATOR_HEIGHT (5)
47 #define MENU_TAB_SPACE (8)
49 #define MENU_MAGIC (0x554D)
51 #define NO_SELECTED_ITEM (0xffff)
54 #define MF_END (0x0080)
58 #define MIIM_STRING (0x00000040)
62 /* INTERNAL FUNCTIONS ********************************************************/
64 /* Rip the fun and easy to use and fun WINE unicode string manipulation routines.
65 * Of course I didnt copy the ASM code because we want this to be portable
66 * and it needs to go away.
69 static inline unsigned int strlenW( const WCHAR
*str
)
76 static inline WCHAR
*strncpyW( WCHAR
*str1
, const WCHAR
*str2
, int n
)
79 while (n
-- > 0) if (!(*str1
++ = *str2
++)) break;
80 while (n
-- > 0) *str1
++ = 0;
84 static inline WCHAR
*strcpyW( WCHAR
*dst
, const WCHAR
*src
)
87 while ((*p
++ = *src
++));
91 static inline WCHAR
*strcatW( WCHAR
*dst
, const WCHAR
*src
)
93 strcpyW( dst
+ strlenW(dst
), src
);
98 STATIC
HEAP_strdupA2W ( HANDLE hHeap
, LPWSTR
* ppszW
, LPCSTR lpszA
, UINT
* NewLen
)
104 return STATUS_SUCCESS
;
105 len
= lstrlenA(lpszA
);
106 *ppszW
= RtlAllocateHeap ( hHeap
, 0, (len
+1) * sizeof(WCHAR
) );
108 return STATUS_NO_MEMORY
;
109 Status
= RtlMultiByteToUnicodeN ( *ppszW
, len
*sizeof(WCHAR
), NULL
, (PCHAR
)lpszA
, len
);
110 (*ppszW
)[len
] = L
'\0';
111 if(NewLen
) (*NewLen
) = (UINT
)len
;
116 #define GET_WORD(ptr) (*(WORD *)(ptr))
119 #define GET_DWORD(ptr) (*(DWORD *)(ptr))
122 HFONT hMenuFont
= NULL
;
123 HFONT hMenuFontBold
= NULL
;
125 /**********************************************************************
126 * MENUEX_ParseResource
128 * Parse an extended menu resource and add items to the menu.
129 * Return a pointer to the end of the resource.
131 * FIXME - should we be passing an LPCSTR to a predominantly UNICODE function?
133 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
141 mii
.cbSize
= sizeof(mii
);
142 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
143 mii
.fType
= GET_DWORD(res
);
144 res
+= sizeof(DWORD
);
145 mii
.fState
= GET_DWORD(res
);
146 res
+= sizeof(DWORD
);
147 mii
.wID
= GET_DWORD(res
);
148 res
+= sizeof(DWORD
);
149 resinfo
= GET_WORD(res
);
151 /* Align the text on a word boundary. */
152 res
+= (~((int)res
- 1)) & 1;
153 mii
.dwTypeData
= (LPWSTR
) res
;
154 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
155 /* Align the following fields on a dword boundary. */
156 res
+= (~((int)res
- 1)) & 3;
158 if (resinfo
& 1) /* Pop-up? */
160 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
161 res
+= sizeof(DWORD
);
162 mii
.hSubMenu
= CreatePopupMenu();
165 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
)))
167 DestroyMenu(mii
.hSubMenu
);
170 mii
.fMask
|= MIIM_SUBMENU
;
171 mii
.fType
|= MF_POPUP
;
173 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
175 DbgPrint("WARN: Converting NULL menu item %04x, type %04x to SEPARATOR\n",
177 mii
.fType
|= MF_SEPARATOR
;
179 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
181 while (!(resinfo
& MF_END
));
186 /**********************************************************************
189 * Parse a standard menu resource and add items to the menu.
190 * Return a pointer to the end of the resource.
192 * NOTE: flags is equivalent to the mtOption field
194 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
202 flags
= GET_WORD(res
);
204 /* remove MF_END flag before passing it to AppendMenu()! */
205 end
= (flags
& MF_END
);
206 if(end
) flags
^= MF_END
;
209 if(!(flags
& MF_POPUP
))
216 res
+= strlen(str
) + 1;
218 res
+= (strlenW((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
219 if (flags
& MF_POPUP
)
221 HMENU hSubMenu
= CreatePopupMenu();
222 if(!hSubMenu
) return NULL
;
223 if(!(res
= MENU_ParseResource(res
, hSubMenu
, unicode
)))
226 AppendMenuA(hMenu
, flags
, (UINT
)hSubMenu
, str
);
228 AppendMenuW(hMenu
, flags
, (UINT
)hSubMenu
, (LPCWSTR
)str
);
230 else /* Not a popup */
233 AppendMenuA(hMenu
, flags
, id
, *str
? str
: NULL
);
235 AppendMenuW(hMenu
, flags
, id
,
236 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
247 NONCLIENTMETRICSW ncm
;
249 /* get the menu font */
250 if(!hMenuFont
|| !hMenuFontBold
)
252 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
), &ncm
, 0))
254 DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
258 hMenuFont
= CreateFontIndirect(&ncm
.lfMenuFont
);
259 if(hMenuFont
== NULL
)
261 DbgPrint("MenuInit(): CreateFontIndirect(hMenuFont) failed!\n");
265 ncm
.lfMenuFont
.lfWeight
= max(ncm
.lfMenuFont
.lfWeight
+ 300, 1000);
266 hMenuFontBold
= CreateFontIndirect(&ncm
.lfMenuFont
);
267 if(hMenuFontBold
== NULL
)
269 DbgPrint("MenuInit(): CreateFontIndirect(hMenuFontBold) failed!\n");
279 MenuGetMenuBarHeight(HWND hWnd
, ULONG MenuBarWidth
, LONG OrgX
, LONG OrgY
)
286 MenuId = GetWindowLong(hWnd, GWL_ID);
287 Menu = MenuGetMenu((HMENU)MenuId);
292 hDC = GetDCEx(hWnd, 0, DCX_CACHE | DCX_WINDOW);
293 SelectObject(hDC, hMenuFont);
294 SetRect(&Rect, OrgX, OrgY, OrgX + MenuBarWidth,
295 OrgY + GetSystemMetrics(SM_CYMENU));
296 MenuMenuBarCalcSize(hDC, &Rect, Menu, hWnd);
297 ReleaseDC(hWnd, hDC);*/
298 return(GetSystemMetrics(SM_CYMENU
));
303 MenuDrawMenuBar(HDC hDC
, LPRECT Rect
, HWND hWnd
, BOOL Draw
)
305 HFONT hFontOld
= SelectObject(hDC
, hMenuFont
);
306 //DrawTextA(hDC, "This is the menu bar", 19, Rect, DT_SINGLELINE);
307 SelectObject(hDC
, hFontOld
);
308 return(GetSystemMetrics(SM_CYMENU
));
315 MenuID = GetWindowLong(hWnd, GWL_ID);
316 Menu = MenuGetMenu((HMENU)MenuID);
318 if (Menu == NULL || Rect == NULL)
320 return(GetSystemMetrics(SM_CYMENU));
323 hFontOld = SelectObject(hDC, hMenuFont);
325 if (Menu->Height == 0)
327 MenuMenuBarCalcSize(hDC, Rect, Menu, hWnd);
330 Rect->bottom = Rect->top + Menu->Height;
334 SelectObject(hDC, hFontOld);
335 return(Menu->Height);
338 FillRect(hDC, Rect, GetSysColorBrush(COLOR_MENU));
340 SelectObject(hDC, GetSysColorPen(COLOR_WINDOWFRAME));
341 MoveToEx(hDC, Rect->left, Rect->bottom, NULL);
342 LineTo(hDC, Rect->right, Rect->bottom);
344 if (Menu->NrItems == 0)
346 SelectObject(hDC, hFontOld);
347 return(GetSystemMetrics(SM_CYMENU));
350 for (i = 0; i < Menu->NrItems; i++)
352 MenuDrawMenuItem(hWnd, (HMENU)MenuID, hWnd, hDC,
353 Menu->Items + i, Menu->Height, TRUE, ODA_DRAWENTIRE);
356 SelectObject(hDC, hFontOld);
357 return(Menu->Height);*/
362 MenuTrackMouseMenuBar(HWND hWnd
, ULONG Ht
, POINT Pt
)
368 MenuTrackKbdMenuBar(HWND hWnd
, ULONG wParam
, ULONG Key
)
372 /* FUNCTIONS *****************************************************************/
375 MenuIsStringItem(ULONG TypeData)
377 return((TypeData & MENU_TYPE_MASK) == MF_STRING);
385 AppendMenuA(HMENU hMenu
,
390 return(InsertMenuA(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
399 AppendMenuW(HMENU hMenu
,
404 return(InsertMenuW(hMenu
, -1, uFlags
| MF_BYPOSITION
, uIDNewItem
,
413 CheckMenuItem(HMENU hmenu
,
417 return NtUserCheckMenuItem(hmenu
, uIDCheckItem
, uCheck
);
425 CheckMenuRadioItem(HMENU hmenu
,
442 return NtUserCreateMenu();
450 CreatePopupMenu(VOID
)
452 /* FIXME - add MF_POPUP style? */
453 return NtUserCreateMenu();
461 DeleteMenu(HMENU hMenu
,
465 return NtUserDeleteMenu(hMenu
, uPosition
, uFlags
);
473 DestroyMenu(HMENU hMenu
)
475 return NtUserDestroyMenu(hMenu
);
483 DrawMenuBar(HWND hWnd
)
486 /* FIXME - return NtUserCallHwndLock(hWnd, 0x55); */
495 EnableMenuItem(HMENU hMenu
,
499 return NtUserEnableMenuItem(hMenu
, uIDEnableItem
, uEnable
);
509 /* FIXME - return NtUserEndMenu(); */
520 return (HMENU
)NtUserCallOneParam((DWORD
)hWnd
, ONEPARAM_ROUTINE_GETMENU
);
528 GetMenuBarInfo(HWND hwnd
,
542 GetMenuCheckMarkDimensions(VOID
)
544 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK
),
545 GetSystemMetrics(SM_CYMENUCHECK
)));
553 GetMenuDefaultItem(HMENU hMenu
,
566 GetMenuInfo(HMENU hmenu
,
572 if(!lpcmi
|| (lpcmi
->cbSize
!= sizeof(MENUINFO
)))
575 RtlZeroMemory(&mi
, sizeof(MENUINFO
));
576 mi
.cbSize
= sizeof(MENUINFO
);
577 mi
.fMask
= lpcmi
->fMask
;
579 res
= NtUserMenuInfo(hmenu
, &mi
, FALSE
);
581 memcpy(lpcmi
, &mi
, sizeof(MENUINFO
));
590 GetMenuItemCount(HMENU hMenu
)
592 return NtUserBuildMenuItemList(hMenu
, NULL
, 0, 0);
600 GetMenuItemID(HMENU hMenu
,
605 mii
.cbSize
= sizeof(MENUITEMINFOW
);
606 mii
.fMask
= MIIM_ID
| MIIM_SUBMENU
;
608 if(!NtUserMenuItemInfo(hMenu
, nPos
, MF_BYPOSITION
, &mii
, FALSE
))
613 if(mii
.hSubMenu
) return -1;
614 if(mii
.wID
== 0) return -1;
628 LPMENUITEMINFOA lpmii
)
644 LPMENUITEMINFOW lpmii
)
655 GetMenuItemRect(HWND hWnd
,
676 mii
.cbSize
= sizeof(MENUITEMINFOW
);
677 mii
.fMask
= MIIM_STATE
| MIIM_TYPE
| MIIM_SUBMENU
;
680 if(NtUserMenuItemInfo(hMenu
, uId
, uFlags
, &mii
, FALSE
))
685 nSubItems
= (UINT
)NtUserBuildMenuItemList(mii
.hSubMenu
, NULL
, 0, 0);
687 /* FIXME - ported from wine, does that work (0xff)? */
688 if(GetLastError() != ERROR_INVALID_MENU_HANDLE
)
689 return (nSubItems
<< 8) | ((mii
.fState
| mii
.fType
) & 0xff);
691 return (UINT
)-1; /* Invalid submenu */
694 /* FIXME - ported from wine, does that work? */
695 return (mii
.fType
| mii
.fState
);
746 mi
.cbSize
= sizeof(MENUITEMINFO
);
747 mi
.fMask
= MIIM_SUBMENU
;
748 if(NtUserMenuItemInfo(hMenu
, (UINT
)nPos
, MF_BYPOSITION
, &mi
, FALSE
))
767 return NtUserHiliteMenuItem(hwnd
, hmenu
, uItemHilite
, uHilite
);
797 LPCMENUITEMINFOA lpmii
)
801 BOOL CleanHeap
= FALSE
;
803 HANDLE hHeap
= RtlGetProcessHeap();
805 if((lpmii
->cbSize
== sizeof(MENUITEMINFO
)) ||
806 (lpmii
->cbSize
== sizeof(MENUITEMINFO
) - sizeof(HBITMAP
)))
808 memcpy(&mi
, lpmii
, lpmii
->cbSize
);
810 /* copy the text string */
811 if((mi
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
812 (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
) && mi
.dwTypeData
)
814 Status
= HEAP_strdupA2W (hHeap
, &mi
.dwTypeData
, (LPCSTR
)mi
.dwTypeData
, &mi
.cch
);
815 if (!NT_SUCCESS (Status
))
817 SetLastError (RtlNtStatusToDosError(Status
));
823 res
= NtUserInsertMenuItem(hMenu
, uItem
, fByPosition
, &mi
);
825 if(CleanHeap
) RtlFreeHeap (hHeap
, 0, mi
.dwTypeData
);
840 LPCMENUITEMINFOW lpmii
)
844 BOOL CleanHeap
= FALSE
;
846 HANDLE hHeap
= RtlGetProcessHeap();
848 if((lpmii
->cbSize
== sizeof(MENUITEMINFO
)) ||
849 (lpmii
->cbSize
== sizeof(MENUITEMINFO
) - sizeof(HBITMAP
)))
851 memcpy(&mi
, lpmii
, lpmii
->cbSize
);
853 /* copy the text string */
854 if((mi
.fMask
& (MIIM_TYPE
| MIIM_STRING
)) &&
855 (MENU_ITEM_TYPE(mi
.fType
) == MF_STRING
) && mi
.dwTypeData
)
857 len
= lstrlenW(lpmii
->dwTypeData
);
859 mi
.dwTypeData
= RtlAllocateHeap(hHeap
, 0, (len
+ 1) * sizeof(WCHAR
));
862 SetLastError (RtlNtStatusToDosError(STATUS_NO_MEMORY
));
865 memcpy(&mi
.dwTypeData
, &lpmii
->dwTypeData
, len
);
870 res
= NtUserInsertMenuItem(hMenu
, uItem
, fByPosition
, &mi
);
872 if(CleanHeap
) RtlFreeHeap (hHeap
, 0, mi
.dwTypeData
);
903 SetLastError(ERROR_SUCCESS
);
904 DWORD ret
= NtUserBuildMenuItemList(hMenu
, NULL
, 0, 0);
905 return ((ret
== -1) || (GetLastError() == ERROR_INVALID_MENU_HANDLE
));
913 LoadMenuA(HINSTANCE hInstance
,
916 HANDLE Resource
= FindResourceA(hInstance
, lpMenuName
, MAKEINTRESOURCEA(4));
917 if (Resource
== NULL
)
921 return(LoadMenuIndirectA((PVOID
)LoadResource(hInstance
, Resource
)));
929 LoadMenuIndirectA(CONST MENUTEMPLATE
*lpMenuTemplate
)
931 return(LoadMenuIndirectW(lpMenuTemplate
));
939 LoadMenuIndirectW(CONST MENUTEMPLATE
*lpMenuTemplate
)
942 WORD version
, offset
;
943 LPCSTR p
= (LPCSTR
)lpMenuTemplate
;
945 version
= GET_WORD(p
);
950 case 0: /* standard format is version of 0 */
951 offset
= GET_WORD(p
);
952 p
+= sizeof(WORD
) + offset
;
953 if (!(hMenu
= CreateMenu())) return 0;
954 if (!MENU_ParseResource(p
, hMenu
, TRUE
))
960 case 1: /* extended format is version of 1 */
961 offset
= GET_WORD(p
);
962 p
+= sizeof(WORD
) + offset
;
963 if (!(hMenu
= CreateMenu())) return 0;
964 if (!MENUEX_ParseResource(p
, hMenu
))
966 DestroyMenu( hMenu
);
971 DbgPrint("LoadMenuIndirectW(): version %d not supported.\n", version
);
981 LoadMenuW(HINSTANCE hInstance
,
984 HANDLE Resource
= FindResourceW(hInstance
, lpMenuName
, RT_MENU
);
985 if (Resource
== NULL
)
989 return(LoadMenuIndirectW((PVOID
)LoadResource(hInstance
, Resource
)));
1017 UINT_PTR uIDNewItem
,
1034 UINT_PTR uIDNewItem
,
1052 return NtUserRemoveMenu(hMenu
, uPosition
, uFlags
);
1063 return NtUserSetMenu(hWnd
, hMenu
, TRUE
);
1077 return NtUserSetMenuDefaultItem(hMenu
, uItem
, fByPos
);
1092 if(lpcmi
->cbSize
!= sizeof(MENUINFO
))
1095 memcpy(&mi
, lpcmi
, sizeof(MENUINFO
));
1096 return NtUserMenuInfo(hmenu
, &mi
, TRUE
);
1109 HBITMAP hBitmapUnchecked
,
1110 HBITMAP hBitmapChecked
)
1125 WINBOOL fByPosition
,
1126 LPMENUITEMINFOA lpmii
)
1141 WINBOOL fByPosition
,
1142 LPMENUITEMINFOW lpmii
)
1161 CONST RECT
*prcRect
)
1191 SetMenuContextHelpId(HMENU hmenu
,
1192 DWORD dwContextHelpId
)
1194 return NtUserSetMenuContextHelpId(hmenu
, dwContextHelpId
);
1203 GetMenuContextHelpId(HMENU hmenu
)
1206 mi
.cbSize
= sizeof(MENUINFO
);
1207 mi
.fMask
= MIM_HELPID
;
1209 if(NtUserMenuInfo(hmenu
, &mi
, FALSE
))
1211 return mi
.dwContextHelpID
;