user32: Reimplement GetMenuStringW on top of NtUserThunkedMenuItemInfo.
[wine.git] / dlls / user32 / menu.c
blob434353e8892fecca27abcae2162acb7e9669e10b
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
35 * TODO:
36 * implements styles :
37 * - MNS_AUTODISMISS
38 * - MNS_DRAGDROP
39 * - MNS_MODELESS
42 #include <stdarg.h>
43 #include <string.h>
45 #define OEMRESOURCE
47 #include "windef.h"
48 #include "winbase.h"
49 #include "wingdi.h"
50 #include "winnls.h"
51 #include "wine/server.h"
52 #include "wine/exception.h"
53 #include "win.h"
54 #include "controls.h"
55 #include "user_private.h"
56 #include "wine/debug.h"
58 WINE_DEFAULT_DEBUG_CHANNEL(menu);
61 /* Space between 2 columns */
62 #define MENU_COL_SPACE 4
64 /* Margins for popup menus */
65 #define MENU_MARGIN 3
67 /* (other menu->FocusedItem values give the position of the focused item) */
68 #define NO_SELECTED_ITEM 0xffff
70 #define MENU_ITEM_TYPE(flags) \
71 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
73 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
74 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
75 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
77 #define MENUITEMINFO_TYPE_MASK \
78 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
79 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
80 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
81 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
82 #define STATE_MASK (~TYPE_MASK)
83 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
85 /*********************************************************************
86 * menu class descriptor
88 const struct builtin_class_descr MENU_builtin_class =
90 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
91 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
92 WINPROC_MENU, /* proc */
93 sizeof(HMENU), /* extra */
94 IDC_ARROW, /* cursor */
95 (HBRUSH)(COLOR_MENU+1) /* brush */
99 /***********************************************************************
100 * MENU_GetMenu
102 * Validate the given menu handle and returns the menu structure pointer.
104 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
106 POPUPMENU *menu = get_user_handle_ptr( hMenu, NTUSER_OBJ_MENU );
108 if (menu == OBJ_OTHER_PROCESS)
110 WARN( "other process menu %p?\n", hMenu);
111 return NULL;
113 if (menu) release_user_handle_ptr( menu ); /* FIXME! */
114 else WARN("invalid menu handle=%p\n", hMenu);
115 return menu;
118 static POPUPMENU *grab_menu_ptr(HMENU hMenu)
120 POPUPMENU *menu = get_user_handle_ptr( hMenu, NTUSER_OBJ_MENU );
122 if (menu == OBJ_OTHER_PROCESS)
124 WARN("other process menu %p?\n", hMenu);
125 return NULL;
128 if (menu)
129 menu->refcount++;
130 else
131 WARN("invalid menu handle=%p\n", hMenu);
132 return menu;
135 static void release_menu_ptr(POPUPMENU *menu)
137 if (menu)
139 menu->refcount--;
140 release_user_handle_ptr(menu);
144 /***********************************************************************
145 * MENU_CopySysPopup
147 * Return the default system menu.
149 static HMENU MENU_CopySysPopup(BOOL mdi)
151 HMENU hMenu = LoadMenuW(user32_module, mdi ? L"SYSMENUMDI" : L"SYSMENU");
153 if( hMenu ) {
154 MENUINFO minfo;
155 MENUITEMINFOW miteminfo;
156 POPUPMENU* menu = MENU_GetMenu(hMenu);
157 menu->wFlags |= MF_SYSMENU | MF_POPUP;
158 /* decorate the menu with bitmaps */
159 minfo.cbSize = sizeof( MENUINFO);
160 minfo.dwStyle = MNS_CHECKORBMP;
161 minfo.fMask = MIM_STYLE;
162 SetMenuInfo( hMenu, &minfo);
163 miteminfo.cbSize = sizeof( MENUITEMINFOW);
164 miteminfo.fMask = MIIM_BITMAP;
165 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
166 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
167 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
168 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
169 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
170 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
171 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
172 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
173 NtUserSetMenuDefaultItem( hMenu, SC_CLOSE, FALSE );
175 else
176 ERR("Unable to load default system menu\n" );
178 TRACE("returning %p (mdi=%d).\n", hMenu, mdi );
180 return hMenu;
184 /**********************************************************************
185 * MENU_GetSysMenu
187 * Create a copy of the system menu. System menu in Windows is
188 * a special menu bar with the single entry - system menu popup.
189 * This popup is presented to the outside world as a "system menu".
190 * However, the real system menu handle is sometimes seen in the
191 * WM_MENUSELECT parameters (and Word 6 likes it this way).
193 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
195 HMENU hMenu;
197 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
198 if ((hMenu = CreateMenu()))
200 POPUPMENU *menu = MENU_GetMenu(hMenu);
201 menu->wFlags = MF_SYSMENU;
202 menu->hWnd = WIN_GetFullHandle( hWnd );
203 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
205 if (!hPopupMenu)
207 if (GetWindowLongW(hWnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
208 hPopupMenu = MENU_CopySysPopup(TRUE);
209 else
210 hPopupMenu = MENU_CopySysPopup(FALSE);
213 if (hPopupMenu)
215 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
216 NtUserDeleteMenu( hPopupMenu, SC_CLOSE, MF_BYCOMMAND );
218 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
219 (UINT_PTR)hPopupMenu, NULL );
221 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
222 menu->items[0].fState = 0;
223 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
225 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
226 return hMenu;
228 NtUserDestroyMenu( hMenu );
230 ERR("failed to load system menu!\n");
231 return 0;
235 static POPUPMENU *find_menu_item(HMENU hmenu, UINT id, UINT flags, UINT *pos)
237 UINT fallback_pos = ~0u, i;
238 POPUPMENU *menu;
240 menu = grab_menu_ptr(hmenu);
241 if (!menu)
242 return NULL;
244 if (flags & MF_BYPOSITION)
246 if (id >= menu->nItems)
248 release_menu_ptr(menu);
249 return NULL;
252 if (pos) *pos = id;
253 return menu;
255 else
257 MENUITEM *item = menu->items;
258 for (i = 0; i < menu->nItems; i++, item++)
260 if (item->fType & MF_POPUP)
262 POPUPMENU *submenu = find_menu_item(item->hSubMenu, id, flags, pos);
264 if (submenu)
266 release_menu_ptr(menu);
267 return submenu;
269 else if (item->wID == id)
271 /* fallback to this item if nothing else found */
272 fallback_pos = i;
275 else if (item->wID == id)
277 if (pos) *pos = i;
278 return menu;
283 if (fallback_pos != ~0u)
284 *pos = fallback_pos;
285 else
287 release_menu_ptr(menu);
288 menu = NULL;
291 return menu;
295 /**********************************************************************
296 * MENU_ParseResource
298 * Parse a standard menu resource and add items to the menu.
299 * Return a pointer to the end of the resource.
301 * NOTE: flags is equivalent to the mtOption field
303 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
305 WORD flags, id = 0;
306 LPCWSTR str;
307 BOOL end_flag;
311 flags = GET_WORD(res);
312 end_flag = flags & MF_END;
313 /* Remove MF_END because it has the same value as MF_HILITE */
314 flags &= ~MF_END;
315 res += sizeof(WORD);
316 if (!(flags & MF_POPUP))
318 id = GET_WORD(res);
319 res += sizeof(WORD);
321 str = (LPCWSTR)res;
322 res += (lstrlenW(str) + 1) * sizeof(WCHAR);
323 if (flags & MF_POPUP)
325 HMENU hSubMenu = CreatePopupMenu();
326 if (!hSubMenu) return NULL;
327 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
328 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
330 else /* Not a popup */
332 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
334 } while (!end_flag);
335 return res;
339 /**********************************************************************
340 * MENUEX_ParseResource
342 * Parse an extended menu resource and add items to the menu.
343 * Return a pointer to the end of the resource.
345 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
347 WORD resinfo;
348 do {
349 MENUITEMINFOW mii;
351 mii.cbSize = sizeof(mii);
352 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
353 mii.fType = GET_DWORD(res);
354 res += sizeof(DWORD);
355 mii.fState = GET_DWORD(res);
356 res += sizeof(DWORD);
357 mii.wID = GET_DWORD(res);
358 res += sizeof(DWORD);
359 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
360 res += sizeof(WORD);
361 /* Align the text on a word boundary. */
362 res += (~((UINT_PTR)res - 1)) & 1;
363 mii.dwTypeData = (LPWSTR) res;
364 res += (1 + lstrlenW(mii.dwTypeData)) * sizeof(WCHAR);
365 /* Align the following fields on a dword boundary. */
366 res += (~((UINT_PTR)res - 1)) & 3;
368 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
369 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
371 if (resinfo & 1) { /* Pop-up? */
372 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
373 res += sizeof(DWORD);
374 mii.hSubMenu = CreatePopupMenu();
375 if (!mii.hSubMenu)
376 return NULL;
377 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
378 NtUserDestroyMenu( mii.hSubMenu );
379 return NULL;
381 mii.fMask |= MIIM_SUBMENU;
382 mii.fType |= MF_POPUP;
384 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
386 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
387 mii.wID, mii.fType);
388 mii.fType |= MF_SEPARATOR;
390 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
391 } while (!(resinfo & MF_END));
392 return res;
396 /**********************************************************************
397 * TrackPopupMenu (USER32.@)
399 * Like the win32 API, the function return the command ID only if the
400 * flag TPM_RETURNCMD is on.
403 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
404 INT nReserved, HWND hWnd, const RECT *lpRect )
406 return NtUserTrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL );
409 /***********************************************************************
410 * PopupMenuWndProc
412 * NOTE: Windows has totally different (and undocumented) popup wndproc.
414 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
416 switch(message)
418 case WM_DESTROY:
419 case WM_CREATE:
420 case WM_MOUSEACTIVATE:
421 case WM_PAINT:
422 case WM_PRINTCLIENT:
423 case WM_ERASEBKGND:
424 case WM_SHOWWINDOW:
425 case MN_GETHMENU:
426 return NtUserMessageCall( hwnd, message, wParam, lParam,
427 NULL, NtUserPopupMenuWndProc, FALSE );
429 default:
430 return DefWindowProcW( hwnd, message, wParam, lParam );
432 return 0;
436 /*******************************************************************
437 * ChangeMenuA (USER32.@)
439 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
440 UINT id, UINT flags )
442 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
443 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
444 id, data );
445 if (flags & MF_DELETE) return NtUserDeleteMenu( hMenu, pos, flags & ~MF_DELETE );
446 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
447 id, data );
448 if (flags & MF_REMOVE) return NtUserRemoveMenu( hMenu,
449 flags & MF_BYPOSITION ? pos : id,
450 flags & ~MF_REMOVE );
451 /* Default: MF_INSERT */
452 return InsertMenuA( hMenu, pos, flags, id, data );
456 /*******************************************************************
457 * ChangeMenuW (USER32.@)
459 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
460 UINT id, UINT flags )
462 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
463 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
464 id, data );
465 if (flags & MF_DELETE) return NtUserDeleteMenu( hMenu, pos, flags & ~MF_DELETE );
466 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
467 id, data );
468 if (flags & MF_REMOVE) return NtUserRemoveMenu( hMenu,
469 flags & MF_BYPOSITION ? pos : id,
470 flags & ~MF_REMOVE );
471 /* Default: MF_INSERT */
472 return InsertMenuW( hMenu, pos, flags, id, data );
476 /*******************************************************************
477 * GetMenuStringA (USER32.@)
479 INT WINAPI GetMenuStringA( HMENU menu, UINT item, char *str, INT count, UINT flags )
481 MENUITEMINFOA info;
482 int ret;
484 TRACE( "menu=%p item=%04x ptr=%p len=%d flags=%04x\n", menu, item, str, count, flags );
486 info.cbSize = sizeof(info);
487 info.fMask = MIIM_STRING;
488 info.dwTypeData = str;
489 info.cch = count;
490 ret = NtUserThunkedMenuItemInfo( menu, item, flags, NtUserGetMenuItemInfoA,
491 (MENUITEMINFOW *)&info, NULL );
492 if (ret) ret = info.cch;
493 TRACE( "returning %s %d\n", debugstr_a( str ), ret );
494 return ret;
498 /*******************************************************************
499 * GetMenuStringW (USER32.@)
501 INT WINAPI GetMenuStringW( HMENU menu, UINT item, WCHAR *str, INT count, UINT flags )
503 MENUITEMINFOW info;
504 int ret;
506 TRACE( "menu=%p item=%04x ptr=%p len=%d flags=%04x\n", menu, item, str, count, flags );
508 info.cbSize = sizeof(info);
509 info.fMask = MIIM_STRING;
510 info.dwTypeData = str;
511 info.cch = count;
512 ret = NtUserThunkedMenuItemInfo( menu, item, flags, NtUserGetMenuItemInfoW, &info, NULL );
513 if (ret) ret = info.cch;
514 TRACE( "returning %s %d\n", debugstr_w( str ), ret );
515 return ret;
519 /**********************************************************************
520 * GetMenuState (USER32.@)
522 UINT WINAPI GetMenuState( HMENU menu, UINT item, UINT flags )
524 return NtUserThunkedMenuItemInfo( menu, item, flags, NtUserGetMenuState, NULL, NULL );
528 /**********************************************************************
529 * GetMenuItemCount (USER32.@)
531 INT WINAPI GetMenuItemCount( HMENU menu )
533 return NtUserGetMenuItemCount( menu );
537 /**********************************************************************
538 * GetMenuItemID (USER32.@)
540 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
542 POPUPMENU *menu;
543 UINT id, pos;
545 if (!(menu = find_menu_item(hMenu, nPos, MF_BYPOSITION, &pos)))
546 return -1;
548 id = menu->items[pos].fType & MF_POPUP ? -1 : menu->items[pos].wID;
549 release_menu_ptr(menu);
550 return id;
554 /**********************************************************************
555 * MENU_mnu2mnuii
557 * Uses flags, id and text ptr, passed by InsertMenu() and
558 * ModifyMenu() to setup a MenuItemInfo structure.
560 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
561 LPMENUITEMINFOW pmii)
563 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
564 pmii->cbSize = sizeof( MENUITEMINFOW);
565 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
566 /* setting bitmap clears text and vice versa */
567 if( IS_STRING_ITEM(flags)) {
568 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
569 if( !str)
570 flags |= MF_SEPARATOR;
571 /* Item beginning with a backspace is a help item */
572 /* FIXME: wrong place, this is only true in win16 */
573 else if( *str == '\b') {
574 flags |= MF_HELP;
575 str++;
577 pmii->dwTypeData = (LPWSTR)str;
578 } else if( flags & MFT_BITMAP){
579 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
580 pmii->hbmpItem = (HBITMAP)str;
582 if( flags & MF_OWNERDRAW){
583 pmii->fMask |= MIIM_DATA;
584 pmii->dwItemData = (ULONG_PTR) str;
586 if( flags & MF_POPUP && MENU_GetMenu((HMENU)id)) {
587 pmii->fMask |= MIIM_SUBMENU;
588 pmii->hSubMenu = (HMENU)id;
590 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
591 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
592 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
593 pmii->wID = (UINT)id;
597 /*******************************************************************
598 * InsertMenuW (USER32.@)
600 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
601 UINT_PTR id, LPCWSTR str )
603 MENUITEMINFOW mii;
605 if (IS_STRING_ITEM(flags) && str)
606 TRACE("hMenu %p, pos %d, flags %08x, id %04Ix, str %s\n",
607 hMenu, pos, flags, id, debugstr_w(str) );
608 else TRACE("hMenu %p, pos %d, flags %08x, id %04Ix, str %p (not a string)\n",
609 hMenu, pos, flags, id, str );
611 MENU_mnu2mnuii( flags, id, str, &mii);
612 mii.fMask |= MIIM_CHECKMARKS;
613 return NtUserThunkedMenuItemInfo( hMenu, pos, flags, NtUserInsertMenuItem, &mii, NULL );
617 /*******************************************************************
618 * InsertMenuA (USER32.@)
620 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
621 UINT_PTR id, LPCSTR str )
623 BOOL ret = FALSE;
625 if (IS_STRING_ITEM(flags) && str)
627 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
628 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
629 if (newstr)
631 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
632 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
633 HeapFree( GetProcessHeap(), 0, newstr );
635 return ret;
637 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
641 /*******************************************************************
642 * AppendMenuA (USER32.@)
644 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
645 UINT_PTR id, LPCSTR data )
647 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
651 /*******************************************************************
652 * AppendMenuW (USER32.@)
654 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
655 UINT_PTR id, LPCWSTR data )
657 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
661 /*******************************************************************
662 * ModifyMenuW (USER32.@)
664 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
665 UINT_PTR id, LPCWSTR str )
667 MENUITEMINFOW mii;
669 if (IS_STRING_ITEM(flags))
670 TRACE("%p %d %04x %04Ix %s\n", hMenu, pos, flags, id, debugstr_w(str) );
671 else
672 TRACE("%p %d %04x %04Ix %p\n", hMenu, pos, flags, id, str );
674 MENU_mnu2mnuii( flags, id, str, &mii);
675 return NtUserThunkedMenuItemInfo( hMenu, pos, flags, NtUserSetMenuItemInfo, &mii, NULL );
679 /*******************************************************************
680 * ModifyMenuA (USER32.@)
682 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
683 UINT_PTR id, LPCSTR str )
685 BOOL ret = FALSE;
687 if (IS_STRING_ITEM(flags) && str)
689 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
690 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
691 if (newstr)
693 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
694 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
695 HeapFree( GetProcessHeap(), 0, newstr );
697 return ret;
699 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
703 /**********************************************************************
704 * CreatePopupMenu (USER32.@)
706 HMENU WINAPI CreatePopupMenu(void)
708 return NtUserCreateMenu( TRUE );
712 /**********************************************************************
713 * GetMenuCheckMarkDimensions (USER.417)
714 * GetMenuCheckMarkDimensions (USER32.@)
716 DWORD WINAPI GetMenuCheckMarkDimensions(void)
718 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
722 /**********************************************************************
723 * SetMenuItemBitmaps (USER32.@)
725 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
726 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
728 POPUPMENU *menu;
729 MENUITEM *item;
730 UINT pos;
732 if (!(menu = find_menu_item(hMenu, nPos, wFlags, &pos)))
733 return FALSE;
735 item = &menu->items[pos];
736 if (!hNewCheck && !hNewUnCheck)
738 item->fState &= ~MF_USECHECKBITMAPS;
740 else /* Install new bitmaps */
742 item->hCheckBit = hNewCheck;
743 item->hUnCheckBit = hNewUnCheck;
744 item->fState |= MF_USECHECKBITMAPS;
746 release_menu_ptr(menu);
748 return TRUE;
752 /**********************************************************************
753 * CreateMenu (USER32.@)
755 HMENU WINAPI CreateMenu(void)
757 return NtUserCreateMenu( FALSE );
761 /**********************************************************************
762 * GetMenu (USER32.@)
764 HMENU WINAPI GetMenu( HWND hWnd )
766 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
767 TRACE("for %p returning %p\n", hWnd, retvalue);
768 return retvalue;
772 /**********************************************************************
773 * GetSubMenu (USER32.@)
775 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
777 POPUPMENU *menu;
778 HMENU submenu;
779 UINT pos;
781 if (!(menu = find_menu_item(hMenu, nPos, MF_BYPOSITION, &pos)))
782 return 0;
784 if (menu->items[pos].fType & MF_POPUP)
785 submenu = menu->items[pos].hSubMenu;
786 else
787 submenu = 0;
789 release_menu_ptr(menu);
790 return submenu;
794 /**********************************************************************
795 * DrawMenuBar (USER32.@)
797 BOOL WINAPI DrawMenuBar( HWND hwnd )
799 return NtUserDrawMenuBar( hwnd );
803 /*****************************************************************
804 * LoadMenuA (USER32.@)
806 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
808 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
809 if (!hrsrc) return 0;
810 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
814 /*****************************************************************
815 * LoadMenuW (USER32.@)
817 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
819 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
820 if (!hrsrc) return 0;
821 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
825 /**********************************************************************
826 * LoadMenuIndirectW (USER32.@)
828 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
830 HMENU hMenu;
831 WORD version, offset;
832 LPCSTR p = template;
834 version = GET_WORD(p);
835 p += sizeof(WORD);
836 TRACE("%p, ver %d\n", template, version );
837 switch (version)
839 case 0: /* standard format is version of 0 */
840 offset = GET_WORD(p);
841 p += sizeof(WORD) + offset;
842 if (!(hMenu = CreateMenu())) return 0;
843 if (!MENU_ParseResource( p, hMenu ))
845 NtUserDestroyMenu( hMenu );
846 return 0;
848 return hMenu;
849 case 1: /* extended format is version of 1 */
850 offset = GET_WORD(p);
851 p += sizeof(WORD) + offset;
852 if (!(hMenu = CreateMenu())) return 0;
853 if (!MENUEX_ParseResource( p, hMenu))
855 NtUserDestroyMenu( hMenu );
856 return 0;
858 return hMenu;
859 default:
860 ERR("version %d not supported.\n", version);
861 return 0;
866 /**********************************************************************
867 * LoadMenuIndirectA (USER32.@)
869 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
871 return LoadMenuIndirectW( template );
875 /**********************************************************************
876 * IsMenu (USER32.@)
878 BOOL WINAPI IsMenu( HMENU menu )
880 MENUINFO info;
882 info.cbSize = sizeof(info);
883 info.fMask = 0;
884 if (GetMenuInfo( menu, &info )) return TRUE;
886 SetLastError(ERROR_INVALID_MENU_HANDLE);
887 return FALSE;
891 /**********************************************************************
892 * GetMenuItemInfoA (USER32.@)
894 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
895 LPMENUITEMINFOA lpmii)
897 BOOL ret;
898 MENUITEMINFOA mii;
899 if( lpmii->cbSize != sizeof( mii) &&
900 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
901 SetLastError( ERROR_INVALID_PARAMETER);
902 return FALSE;
904 memcpy( &mii, lpmii, lpmii->cbSize);
905 mii.cbSize = sizeof( mii);
906 ret = NtUserThunkedMenuItemInfo( hmenu, item, bypos ? MF_BYPOSITION : 0,
907 NtUserGetMenuItemInfoA, (MENUITEMINFOW *)&mii, NULL );
908 mii.cbSize = lpmii->cbSize;
909 memcpy( lpmii, &mii, mii.cbSize );
910 return ret;
913 /**********************************************************************
914 * GetMenuItemInfoW (USER32.@)
916 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
917 LPMENUITEMINFOW lpmii)
919 BOOL ret;
920 MENUITEMINFOW mii;
921 if( lpmii->cbSize != sizeof( mii) &&
922 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
923 SetLastError( ERROR_INVALID_PARAMETER);
924 return FALSE;
926 memcpy( &mii, lpmii, lpmii->cbSize);
927 mii.cbSize = sizeof( mii);
928 ret = NtUserThunkedMenuItemInfo( hmenu, item, bypos ? MF_BYPOSITION : 0,
929 NtUserGetMenuItemInfoW, &mii, NULL );
930 mii.cbSize = lpmii->cbSize;
931 memcpy( lpmii, &mii, mii.cbSize );
932 return ret;
936 /**********************************************************************
937 * MENU_NormalizeMenuItemInfoStruct
939 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
940 * check, copy and extend the MENUITEMINFO struct from the version that the application
941 * supplied to the version used by wine source. */
942 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
943 MENUITEMINFOW *pmii_out )
945 /* do we recognize the size? */
946 if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
947 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) {
948 SetLastError( ERROR_INVALID_PARAMETER);
949 return FALSE;
951 /* copy the fields that we have */
952 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
953 /* if the hbmpItem member is missing then extend */
954 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
955 pmii_out->cbSize = sizeof( MENUITEMINFOW);
956 pmii_out->hbmpItem = NULL;
958 /* test for invalid bit combinations */
959 if( (pmii_out->fMask & MIIM_TYPE &&
960 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
961 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
962 WARN("invalid combination of fMask bits used\n");
963 /* this does not happen on Win9x/ME */
964 SetLastError( ERROR_INVALID_PARAMETER);
965 return FALSE;
967 /* convert old style (MIIM_TYPE) to the new */
968 if( pmii_out->fMask & MIIM_TYPE){
969 pmii_out->fMask |= MIIM_FTYPE;
970 if( IS_STRING_ITEM(pmii_out->fType)){
971 pmii_out->fMask |= MIIM_STRING;
972 } else if( (pmii_out->fType) & MFT_BITMAP){
973 pmii_out->fMask |= MIIM_BITMAP;
974 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
977 return TRUE;
980 /**********************************************************************
981 * SetMenuItemInfoA (USER32.@)
983 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
984 const MENUITEMINFOA *lpmii)
986 WCHAR *strW = NULL;
987 MENUITEMINFOW mii;
988 BOOL ret;
990 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
992 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
994 if ((mii.fMask & MIIM_STRING) && mii.dwTypeData)
996 const char *str = (const char *)mii.dwTypeData;
997 UINT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
998 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return FALSE;
999 MultiByteToWideChar( CP_ACP, 0, str, -1, strW, len );
1000 mii.dwTypeData = strW;
1003 ret = NtUserThunkedMenuItemInfo( hmenu, item, bypos ? MF_BYPOSITION : 0,
1004 NtUserSetMenuItemInfo, &mii, NULL );
1006 HeapFree( GetProcessHeap(), 0, strW );
1007 return ret;
1010 /**********************************************************************
1011 * SetMenuItemInfoW (USER32.@)
1013 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
1014 const MENUITEMINFOW *lpmii)
1016 MENUITEMINFOW mii;
1018 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
1020 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
1022 return NtUserThunkedMenuItemInfo( hmenu, item, bypos ? MF_BYPOSITION : 0,
1023 NtUserSetMenuItemInfo, &mii, NULL );
1026 /**********************************************************************
1027 * GetMenuDefaultItem (USER32.@)
1029 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
1031 POPUPMENU *menu;
1032 MENUITEM * item;
1033 UINT i = 0;
1035 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
1037 if (!(menu = MENU_GetMenu(hmenu))) return -1;
1039 /* find default item */
1040 item = menu->items;
1042 /* empty menu */
1043 if (! item) return -1;
1045 while ( !( item->fState & MFS_DEFAULT ) )
1047 i++; item++;
1048 if (i >= menu->nItems ) return -1;
1051 /* default: don't return disabled items */
1052 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
1054 /* search rekursiv when needed */
1055 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
1057 UINT ret;
1058 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
1059 if ( -1 != ret ) return ret;
1061 /* when item not found in submenu, return the popup item */
1063 return ( bypos ) ? i : item->wID;
1068 /**********************************************************************
1069 * InsertMenuItemA (USER32.@)
1071 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
1072 const MENUITEMINFOA *lpmii)
1074 WCHAR *strW = NULL;
1075 MENUITEMINFOW mii;
1076 BOOL ret;
1078 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
1080 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
1082 if ((mii.fMask & MIIM_STRING) && mii.dwTypeData)
1084 const char *str = (const char *)mii.dwTypeData;
1085 UINT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
1086 if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return FALSE;
1087 MultiByteToWideChar( CP_ACP, 0, str, -1, strW, len );
1088 mii.dwTypeData = strW;
1091 ret = NtUserThunkedMenuItemInfo( hMenu, uItem, bypos ? MF_BYPOSITION : 0,
1092 NtUserInsertMenuItem, &mii, NULL );
1094 HeapFree( GetProcessHeap(), 0, strW );
1095 return ret;
1099 /**********************************************************************
1100 * InsertMenuItemW (USER32.@)
1102 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
1103 const MENUITEMINFOW *lpmii)
1105 MENUITEMINFOW mii;
1107 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
1109 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
1111 return NtUserThunkedMenuItemInfo( hMenu, uItem, bypos ? MF_BYPOSITION : 0,
1112 NtUserInsertMenuItem, &mii, NULL );
1115 /**********************************************************************
1116 * CheckMenuRadioItem (USER32.@)
1119 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu, UINT first, UINT last,
1120 UINT check, UINT flags)
1122 POPUPMENU *first_menu = NULL, *check_menu;
1123 UINT i, check_pos;
1124 BOOL done = FALSE;
1126 for (i = first; i <= last; i++)
1128 MENUITEM *item;
1130 if (!(check_menu = find_menu_item(hMenu, i, flags, &check_pos)))
1131 continue;
1133 if (!first_menu)
1134 first_menu = grab_menu_ptr(check_menu->obj.handle);
1136 if (first_menu != check_menu)
1138 release_menu_ptr(check_menu);
1139 continue;
1142 item = &check_menu->items[check_pos];
1143 if (item->fType != MFT_SEPARATOR)
1145 if (i == check)
1147 item->fType |= MFT_RADIOCHECK;
1148 item->fState |= MFS_CHECKED;
1149 done = TRUE;
1151 else
1153 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
1154 item->fState &= ~MFS_CHECKED;
1158 release_menu_ptr(check_menu);
1160 release_menu_ptr(first_menu);
1162 return done;
1166 /**********************************************************************
1167 * SetMenuInfo (USER32.@)
1169 BOOL WINAPI SetMenuInfo( HMENU menu, const MENUINFO *info )
1171 TRACE( "(%p %p)\n", menu, info );
1173 if (!info || info->cbSize != sizeof(*info))
1175 SetLastError( ERROR_INVALID_PARAMETER);
1176 return FALSE;
1179 return NtUserThunkedMenuInfo( menu, info );
1182 /**********************************************************************
1183 * GetMenuInfo (USER32.@)
1185 BOOL WINAPI GetMenuInfo( HMENU menu, MENUINFO *info )
1187 return NtUserGetMenuInfo( menu, info );
1191 /**********************************************************************
1192 * GetMenuContextHelpId (USER32.@)
1194 DWORD WINAPI GetMenuContextHelpId( HMENU menu )
1196 MENUINFO info;
1197 TRACE( "(%p)\n", menu );
1198 info.cbSize = sizeof(info);
1199 info.fMask = MIM_HELPID;
1200 return GetMenuInfo( menu, &info ) ? info.dwContextHelpID : 0;
1203 /**********************************************************************
1204 * MenuItemFromPoint (USER32.@)
1206 INT WINAPI MenuItemFromPoint( HWND hwnd, HMENU menu, POINT pt )
1208 return NtUserMenuItemFromPoint( hwnd, menu, pt.x, pt.y );
1212 /**********************************************************************
1213 * CalcMenuBar (USER32.@)
1215 DWORD WINAPI CalcMenuBar(HWND hwnd, DWORD left, DWORD right, DWORD top, RECT *rect)
1217 FIXME("(%p, %ld, %ld, %ld, %p): stub\n", hwnd, left, right, top, rect);
1218 return 0;
1222 /**********************************************************************
1223 * TranslateAcceleratorA (USER32.@)
1224 * TranslateAccelerator (USER32.@)
1226 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
1228 switch (msg->message)
1230 case WM_KEYDOWN:
1231 case WM_SYSKEYDOWN:
1232 return NtUserTranslateAccelerator( hWnd, hAccel, msg );
1234 case WM_CHAR:
1235 case WM_SYSCHAR:
1237 MSG msgW = *msg;
1238 char ch = LOWORD(msg->wParam);
1239 WCHAR wch;
1240 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
1241 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
1242 return NtUserTranslateAccelerator( hWnd, hAccel, &msgW );
1245 default:
1246 return 0;