xref: /reactos/win32ss/user/user32/windows/menu.c (revision 8a978a17)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS user32.dll
4  * FILE:            win32ss/user/user32/windows/menu.c
5  * PURPOSE:         Menus
6  *
7  * PROGRAMMERS:     Casper S. Hornstrup
8  *                  James Tabor
9  */
10 
11 #include <user32.h>
12 
13 BOOL WINAPI GdiValidateHandle(HGDIOBJ hobj);
14 
15 WINE_DEFAULT_DEBUG_CHANNEL(menu);
16 
17 /* internal popup menu window messages */
18 
19 #define MM_SETMENUHANDLE	(WM_USER + 0)
20 #define MM_GETMENUHANDLE	(WM_USER + 1)
21 
22 #define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)
23 
24 #define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK)
25 
26 #define MNS_STYLE_MASK (MNS_NOCHECK|MNS_MODELESS|MNS_DRAGDROP|MNS_AUTODISMISS|MNS_NOTIFYBYPOS|MNS_CHECKORBMP)
27 
28 #define MENUITEMINFO_TYPE_MASK \
29                 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
30                  MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
31                  MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
32 
33 #define TYPE_MASK  (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
34 
35 #define STATE_MASK (~TYPE_MASK)
36 
37 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
38 
39 #define MII_STATE_MASK (MFS_GRAYED|MFS_CHECKED|MFS_HILITE|MFS_DEFAULT)
40 
41 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
42 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
43 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
44 
45 #define IS_SYSTEM_MENU(MenuInfo)  \
46 	(0 == ((MenuInfo)->fFlags & MNF_POPUP) && 0 != ((MenuInfo)->fFlags & MNF_SYSMENU))
47 
48 #define IS_SYSTEM_POPUP(MenuInfo) \
49 	(0 != ((MenuInfo)->fFlags & MNF_POPUP) && 0 != ((MenuInfo)->fFlags & MNF_SYSMENU))
50 
51 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
52 
53 /*********************************************************************
54  * PopupMenu class descriptor
55  */
56 const struct builtin_class_descr POPUPMENU_builtin_class =
57 {
58     WC_MENU,                     /* name */
59     CS_SAVEBITS | CS_DBLCLKS,                  /* style  */
60     NULL,                                      /* FIXME - procA */
61     PopupMenuWndProcW,                         /* FIXME - procW */
62     sizeof(MENUINFO *),                        /* extra */
63     (LPCWSTR) IDC_ARROW,                       /* cursor */
64     (HBRUSH)(COLOR_MENU + 1)                   /* brush */
65 };
66 
67 #ifndef GET_WORD
68 #define GET_WORD(ptr)  (*(WORD *)(ptr))
69 #endif
70 #ifndef GET_DWORD
71 #define GET_DWORD(ptr) (*(DWORD *)(ptr))
72 #endif
73 
74 
75 /***********************************************************************
76  *           MENU_GetMenu
77  *
78  * Validate the given menu handle and returns the menu structure pointer.
79  */
80 FORCEINLINE PMENU MENU_GetMenu(HMENU hMenu)
81 {
82     return ValidateHandleNoErr(hMenu, TYPE_MENU);
83 }
84 
85 /***********************************************************************
86  *           MENU_FindItem
87  *
88  * Find a menu item. Return a pointer on the item, and modifies *hmenu
89  * in case the item was in a sub-menu.
90  */
91 ITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
92 {
93     MENU *menu;
94     ITEM *fallback = NULL;
95     UINT fallback_pos = 0;
96     UINT i;
97     PITEM pItem;
98 
99     if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
100     if (wFlags & MF_BYPOSITION)
101     {
102 	if (*nPos >= menu->cItems) return NULL;
103 	pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
104 	if (pItem) pItem = &pItem[*nPos];
105 	return pItem;
106     }
107     else
108     {
109         PITEM item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
110 	for (i = 0; item && (i < menu->cItems); i++, item++)
111 	{
112 	    if (item->spSubMenu)
113 	    {
114 	        PMENU pSubMenu = DesktopPtrToUser(item->spSubMenu);
115 		HMENU hsubmenu = UserHMGetHandle(pSubMenu);
116 		ITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
117 		if (subitem)
118 		{
119 		    *hmenu = hsubmenu;
120 		    return subitem;
121 		}
122 		else if (item->wID == *nPos)
123 		{
124 		    /* fallback to this item if nothing else found */
125 		    fallback_pos = i;
126 		    fallback = item;
127 		}
128 	    }
129 	    else if (item->wID == *nPos)
130 	    {
131 		*nPos = i;
132 		return item;
133 	    }
134 	}
135     }
136 
137     if (fallback)
138         *nPos = fallback_pos;
139 
140     return fallback;
141 }
142 
143 UINT FASTCALL
144 IntGetMenuDefaultItem(PMENU Menu, BOOL fByPos, UINT gmdiFlags, DWORD *gismc)
145 {
146    UINT i = 0;
147    PITEM Item = Menu->rgItems ? DesktopPtrToUser(Menu->rgItems) : NULL;
148 
149    /* empty menu */
150    if (!Item) return -1;
151 
152    while ( !( Item->fState & MFS_DEFAULT ) )
153    {
154       i++; Item++;
155       if  (i >= Menu->cItems ) return -1;
156    }
157 
158    /* default: don't return disabled items */
159    if ( (!(GMDI_USEDISABLED & gmdiFlags)) && (Item->fState & MFS_DISABLED )) return -1;
160 
161    /* search rekursiv when needed */
162    if ( (gmdiFlags & GMDI_GOINTOPOPUPS) && Item->spSubMenu )
163    {
164       UINT ret;
165       (*gismc)++;
166       ret = IntGetMenuDefaultItem( DesktopPtrToUser(Item->spSubMenu), fByPos, gmdiFlags, gismc );
167       (*gismc)--;
168       if ( -1 != ret ) return ret;
169 
170       /* when item not found in submenu, return the popup item */
171    }
172    return ( fByPos ) ? i : Item->wID;
173 }
174 
175 static BOOL GetMenuItemInfo_common ( HMENU hmenu,
176                                      UINT item,
177                                      BOOL bypos,
178                                      LPMENUITEMINFOW lpmii,
179                                      BOOL unicode)
180 {
181     ITEM *pItem = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
182 
183     //debug_print_menuitem("GetMenuItemInfo_common: ", pItem, "");
184 
185     if (!pItem)
186     {
187         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
188         return FALSE;
189     }
190 
191     if( lpmii->fMask & MIIM_TYPE)
192     {
193         if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP))
194         {
195             ERR("invalid combination of fMask bits used\n");
196             /* this does not happen on Win9x/ME */
197             SetLastError( ERROR_INVALID_PARAMETER);
198             return FALSE;
199         }
200 	lpmii->fType = pItem->fType & MENUITEMINFO_TYPE_MASK;
201         if (pItem->hbmp && !IS_MAGIC_BITMAP(pItem->hbmp))
202             lpmii->fType |= MFT_BITMAP;
203 	lpmii->hbmpItem = pItem->hbmp; /* not on Win9x/ME */
204         if( lpmii->fType & MFT_BITMAP)
205         {
206 	    lpmii->dwTypeData = (LPWSTR) pItem->hbmp;
207 	    lpmii->cch = 0;
208         }
209         else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR))
210         {
211             /* this does not happen on Win9x/ME */
212 	    lpmii->dwTypeData = 0;
213 	    lpmii->cch = 0;
214         }
215     }
216 
217     /* copy the text string */
218     if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)))
219     {
220          if( !pItem->Xlpstr )
221          {                                         // Very strange this fixes a wine test with a crash.
222                 if(lpmii->dwTypeData && lpmii->cch && !(GdiValidateHandle((HGDIOBJ)lpmii->dwTypeData)) )
223                 {
224                     if( unicode)
225                         *((WCHAR *)lpmii->dwTypeData) = 0;
226                     else
227                         *((CHAR *)lpmii->dwTypeData) = 0;
228                 }
229                 lpmii->cch = 0;
230          }
231          else
232          {
233             int len;
234             LPWSTR text = DesktopPtrToUser(pItem->Xlpstr);
235             if (unicode)
236             {
237                 len = strlenW(text);
238                 if(lpmii->dwTypeData && lpmii->cch)
239                     lstrcpynW(lpmii->dwTypeData, text, lpmii->cch);
240             }
241             else
242             {
243                 len = WideCharToMultiByte( CP_ACP, 0, text, -1, NULL, 0, NULL, NULL ) - 1;
244                 if(lpmii->dwTypeData && lpmii->cch)
245                     if (!WideCharToMultiByte( CP_ACP, 0, text, -1,
246                             (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
247                         ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
248             }
249             /* if we've copied a substring we return its length */
250             if(lpmii->dwTypeData && lpmii->cch)
251                 if (lpmii->cch <= len + 1)
252                     lpmii->cch--;
253                 else
254                     lpmii->cch = len;
255             else
256             {
257                 /* return length of string */
258                 /* not on Win9x/ME if fType & MFT_BITMAP */
259                 lpmii->cch = len;
260             }
261         }
262     }
263 
264     if (lpmii->fMask & MIIM_FTYPE)
265 	lpmii->fType = pItem->fType & MENUITEMINFO_TYPE_MASK;
266 
267     if (lpmii->fMask & MIIM_BITMAP)
268 	lpmii->hbmpItem = pItem->hbmp;
269 
270     if (lpmii->fMask & MIIM_STATE)
271 	lpmii->fState = pItem->fState & MENUITEMINFO_STATE_MASK;
272 
273     if (lpmii->fMask & MIIM_ID)
274 	lpmii->wID = pItem->wID;
275 
276     if (lpmii->fMask & MIIM_SUBMENU && pItem->spSubMenu )
277     {
278         PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
279         HMENU hSubMenu = UserHMGetHandle(pSubMenu);
280 	lpmii->hSubMenu = hSubMenu;
281     }
282     else
283     {
284         /* hSubMenu is always cleared
285          * (not on Win9x/ME ) */
286         lpmii->hSubMenu = 0;
287     }
288 
289     if (lpmii->fMask & MIIM_CHECKMARKS)
290     {
291 	lpmii->hbmpChecked = pItem->hbmpChecked;
292 	lpmii->hbmpUnchecked = pItem->hbmpUnchecked;
293     }
294     if (lpmii->fMask & MIIM_DATA)
295 	lpmii->dwItemData = pItem->dwItemData;
296 
297   return TRUE;
298 }
299 
300 
301 //
302 // User side Menu Class Proc.
303 //
304 LRESULT WINAPI
305 PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
306 {
307   LRESULT lResult;
308   PWND pWnd;
309 
310   TRACE("PMWPW : hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
311 
312   pWnd = ValidateHwnd(Wnd);
313   if (pWnd)
314   {
315      if (!pWnd->fnid)
316      {
317         if (Message != WM_NCCREATE)
318         {
319            return DefWindowProcW(Wnd, Message, wParam, lParam);
320         }
321      }
322      else
323      {
324         if (pWnd->fnid != FNID_MENU)
325         {
326            ERR("Wrong window class for Menu!\n");
327            return 0;
328         }
329      }
330   }
331 
332   switch(Message)
333     {
334     case WM_DESTROY:
335     case WM_NCDESTROY:
336     case WM_NCCREATE:
337     case WM_CREATE:
338     case MM_SETMENUHANDLE:
339     case MM_GETMENUHANDLE:
340     case MN_SETHMENU:
341     case MN_GETHMENU:
342     case WM_PAINT:
343     case WM_PRINTCLIENT:
344       {
345         TRACE("Menu Class ProcW\n");
346         NtUserMessageCall( Wnd, Message, wParam, lParam, (ULONG_PTR)&lResult, FNID_MENU, FALSE);
347         return lResult;
348       }
349     case WM_MOUSEACTIVATE:  /* We don't want to be activated */
350       return MA_NOACTIVATE;
351 
352     case WM_ERASEBKGND:
353       return 1;
354 
355     case WM_SHOWWINDOW: // Not sure what this does....
356       if (0 != wParam)
357         {
358           if (0 == GetWindowLongPtrW(Wnd, 0))
359             {
360               OutputDebugStringA("no menu to display\n");
361             }
362         }
363       else
364         {
365           //SetWindowLongPtrW(Wnd, 0, 0);
366         }
367       break;
368 
369     default:
370       return DefWindowProcW(Wnd, Message, wParam, lParam);
371     }
372 
373   return 0;
374 }
375 
376 LRESULT WINAPI PopupMenuWndProcA(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
377 {
378   PWND pWnd;
379 
380   pWnd = ValidateHwnd(Wnd);
381   if (pWnd && !pWnd->fnid && Message != WM_NCCREATE)
382   {
383      return DefWindowProcA(Wnd, Message, wParam, lParam);
384   }
385   TRACE("YES! hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
386 
387   switch(Message)
388     {
389     case WM_NCCREATE:
390     case WM_CREATE:
391     case WM_MOUSEACTIVATE:
392     case WM_PAINT:
393     case WM_PRINTCLIENT:
394     case WM_ERASEBKGND:
395     case WM_DESTROY:
396     case WM_NCDESTROY:
397     case WM_SHOWWINDOW:
398     case MM_SETMENUHANDLE:
399     case MM_GETMENUHANDLE:
400     case MN_SETHMENU:
401     case MN_GETHMENU:
402       return PopupMenuWndProcW(Wnd, Message, wParam, lParam);
403 
404     default:
405       return DefWindowProcA(Wnd, Message, wParam, lParam);
406     }
407   return 0;
408 }
409 
410 /**********************************************************************
411  *         MENU_ParseResource
412  *
413  * Parse a standard menu resource and add items to the menu.
414  * Return a pointer to the end of the resource.
415  *
416  * NOTE: flags is equivalent to the mtOption field
417  */
418 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu)
419 {
420     WORD flags, id = 0;
421     HMENU hSubMenu;
422     LPCWSTR str;
423     BOOL end = FALSE;
424 
425     do
426     {
427         flags = GET_WORD(res);
428 
429         /* remove MF_END flag before passing it to AppendMenu()! */
430         end = (flags & MF_END);
431         if(end) flags ^= MF_END;
432 
433         res += sizeof(WORD);
434         if(!(flags & MF_POPUP))
435         {
436             id = GET_WORD(res);
437             res += sizeof(WORD);
438         }
439         str = (LPCWSTR)res;
440         res += (strlenW(str) + 1) * sizeof(WCHAR);
441 
442         if (flags & MF_POPUP)
443         {
444             hSubMenu = CreatePopupMenu();
445             if(!hSubMenu) return NULL;
446             if(!(res = MENU_ParseResource(res, hSubMenu))) return NULL;
447             AppendMenuW(hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str);
448         }
449         else  /* Not a popup */
450         {
451             AppendMenuW(hMenu, flags, id, *(LPCWSTR)str ? (LPCWSTR)str : NULL);
452         }
453     } while(!end);
454     return res;
455 }
456 
457 /**********************************************************************
458  *         MENUEX_ParseResource
459  *
460  * Parse an extended menu resource and add items to the menu.
461  * Return a pointer to the end of the resource.
462  */
463 static LPCSTR MENUEX_ParseResource(LPCSTR res, HMENU hMenu)
464 {
465     WORD resinfo;
466     do
467     {
468         MENUITEMINFOW mii;
469 
470         mii.cbSize = sizeof(mii);
471         mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
472         mii.fType = GET_DWORD(res);
473         res += sizeof(DWORD);
474         mii.fState = GET_DWORD(res);
475         res += sizeof(DWORD);
476         mii.wID = GET_DWORD(res);
477         res += sizeof(DWORD);
478         resinfo = GET_WORD(res);
479         res += sizeof(WORD);
480         /* Align the text on a word boundary.  */
481         res += (~((UINT_PTR)res - 1)) & 1;
482         mii.dwTypeData = (LPWSTR)res;
483         mii.cch = strlenW(mii.dwTypeData);
484         res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
485         /* Align the following fields on a dword boundary.  */
486         res += (~((UINT_PTR)res - 1)) & 3;
487 
488         TRACE("Menu item: [%08x,%08x,%04x,%04x,%S]\n",
489               mii.fType, mii.fState, mii.wID, resinfo, mii.dwTypeData);
490 
491         if (resinfo & 1) /* Pop-up? */
492         {
493             /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
494             res += sizeof(DWORD);
495             mii.hSubMenu = CreatePopupMenu();
496             if (!mii.hSubMenu)
497             {
498                 ERR("CreatePopupMenu failed\n");
499                 return NULL;
500             }
501 
502             if (!(res = MENUEX_ParseResource(res, mii.hSubMenu)))
503             {
504                 ERR("MENUEX_ParseResource failed\n");
505                 DestroyMenu(mii.hSubMenu);
506                 return NULL;
507             }
508             mii.fMask |= MIIM_SUBMENU;
509             mii.fType |= MF_POPUP;
510         }
511         else if (!mii.dwTypeData[0] && !(mii.fType & MF_SEPARATOR))
512         {
513             WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
514                 mii.wID, mii.fType);
515             mii.fType |= MF_SEPARATOR;
516         }
517         InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
518     } while (!(resinfo & MF_END));
519     return res;
520 }
521 
522 
523 /**********************************************************************
524  *         MENU_mnu2mnuii
525  *
526  * Uses flags, id and text ptr, passed by InsertMenu() and
527  * ModifyMenu() to setup a MenuItemInfo structure.
528  */
529 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str, LPMENUITEMINFOW pmii, BOOL Unicode)
530 {
531     RtlZeroMemory( pmii, sizeof( MENUITEMINFOW));
532     pmii->cbSize = sizeof( MENUITEMINFOW);
533     pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
534     /* setting bitmap clears text and vice versa */
535     if( IS_STRING_ITEM(flags)) {
536         pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
537         if( !str)
538             flags |= MF_SEPARATOR;
539         /* Item beginning with a backspace is a help item */
540         /* FIXME: wrong place, this is only true in win16 */
541         else
542         {
543             if (Unicode)
544            {
545               if (*str == '\b')
546               {
547                 flags |= MF_HELP;
548                 str++;
549               }
550            }
551            else
552            {
553               LPCSTR NewItemA = (LPCSTR) str;
554               if (*NewItemA == '\b')
555               {
556                  flags |= MF_HELP;
557                  NewItemA++;
558                  str = (LPCWSTR) NewItemA;
559               }
560               TRACE("A cch %d\n",strlen(NewItemA));
561            }
562         }
563         pmii->dwTypeData = (LPWSTR)str;
564     } else if( flags & MFT_BITMAP){
565         pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
566         pmii->hbmpItem = (HBITMAP)str;
567     }
568     if( flags & MF_OWNERDRAW){
569         pmii->fMask |= MIIM_DATA;
570         pmii->dwItemData = (ULONG_PTR) str;
571     }
572     if( flags & MF_POPUP && MENU_GetMenu((HMENU)id)) {
573         pmii->fMask |= MIIM_SUBMENU;
574         pmii->hSubMenu = (HMENU)id;
575     }
576     if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
577     pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
578     pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
579     pmii->wID = (UINT)id;
580 }
581 
582 /**********************************************************************
583  *		MENU_NormalizeMenuItemInfoStruct
584  *
585  * Helper for SetMenuItemInfo and InsertMenuItemInfo:
586  * check, copy and extend the MENUITEMINFO struct from the version that the application
587  * supplied to the version used by wine source. */
588 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
589                                               MENUITEMINFOW *pmii_out )
590 {
591     /* do we recognize the size? */
592     if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
593             pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) {
594         SetLastError( ERROR_INVALID_PARAMETER);
595         return FALSE;
596     }
597     /* copy the fields that we have */
598     memcpy( pmii_out, pmii_in, pmii_in->cbSize);
599     /* if the hbmpItem member is missing then extend */
600     if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
601         pmii_out->cbSize = sizeof( MENUITEMINFOW);
602         pmii_out->hbmpItem = NULL;
603     }
604     /* test for invalid bit combinations */
605     if( (pmii_out->fMask & MIIM_TYPE &&
606          pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
607         (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
608         ERR("invalid combination of fMask bits used\n");
609         /* this does not happen on Win9x/ME */
610         SetLastError( ERROR_INVALID_PARAMETER);
611         return FALSE;
612     }
613     /* convert old style (MIIM_TYPE) to the new and keep the old one too */
614     if( pmii_out->fMask & MIIM_TYPE){
615         pmii_out->fMask |= MIIM_FTYPE;
616         if( IS_STRING_ITEM(pmii_out->fType)){
617             pmii_out->fMask |= MIIM_STRING;
618         } else if( (pmii_out->fType) & MFT_BITMAP){
619             pmii_out->fMask |= MIIM_BITMAP;
620             pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
621         }
622     }
623     if (pmii_out->fMask & MIIM_FTYPE )
624     {
625         pmii_out->fType &= ~MENUITEMINFO_TYPE_MASK;
626         pmii_out->fType |= pmii_in->fType & MENUITEMINFO_TYPE_MASK;
627     }
628     if (pmii_out->fMask & MIIM_STATE)
629     /* Other menu items having MFS_DEFAULT are not converted
630        to normal items */
631        pmii_out->fState = pmii_in->fState & MENUITEMINFO_STATE_MASK;
632 
633     if (pmii_out->fMask & MIIM_SUBMENU)
634     {
635        if ((pmii_out->hSubMenu != NULL) && !IsMenu(pmii_out->hSubMenu))
636           return FALSE;
637     }
638 
639     return TRUE;
640 }
641 
642 BOOL
643 MenuInit(VOID)
644 {
645   return TRUE;
646 }
647 
648 VOID
649 MenuCleanup(VOID)
650 {
651 }
652 
653 
654 NTSTATUS WINAPI
655 User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength)
656 {
657   LRESULT Result = 0;
658 
659   // Use this for Menu Ole!!
660 
661   return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS));
662 }
663 
664 NTSTATUS WINAPI
665 User32CallLoadMenuFromKernel(PVOID Arguments, ULONG ArgumentLength)
666 {
667   PLOADMENU_CALLBACK_ARGUMENTS Common;
668   LRESULT Result;
669 
670   Common = (PLOADMENU_CALLBACK_ARGUMENTS) Arguments;
671 
672   Result = (LRESULT)LoadMenuW(Common->hModule, Common->InterSource ? MAKEINTRESOURCEW(Common->InterSource) : (LPCWSTR)&Common->MenuName);
673 
674   return ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS);
675 }
676 
677 
678 /* FUNCTIONS *****************************************************************/
679 
680 /*
681  * @implemented
682  */
683 BOOL WINAPI
684 AppendMenuA(HMENU hMenu,
685 	    UINT uFlags,
686 	    UINT_PTR uIDNewItem,
687 	    LPCSTR lpNewItem)
688 {
689   MENUITEMINFOW mii;
690   UNICODE_STRING UnicodeString;
691   BOOL res;
692 
693   RtlInitUnicodeString(&UnicodeString, 0);
694 
695   MENU_mnu2mnuii( uFlags, uIDNewItem, (LPCWSTR)lpNewItem, &mii, FALSE);
696 
697   /* copy the text string, it will be one or the other */
698   if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
699   {
700       if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
701       {
702         SetLastError (ERROR_NOT_ENOUGH_MEMORY);
703         return FALSE;
704       }
705       mii.dwTypeData = UnicodeString.Buffer;
706       mii.cch = UnicodeString.Length / sizeof(WCHAR);
707   }
708   else
709   {
710       TRACE("AMA Handle bitmaps\n");
711   }
712   ////// Answer a question, why a -1? To hunt for the end of the item list. Get it, to Append?
713   res = NtUserThunkedMenuItemInfo(hMenu, -1, TRUE, TRUE, &mii, &UnicodeString);
714   if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString );
715   return res;
716 }
717 
718 /*
719  * @implemented
720  */
721 BOOL WINAPI
722 AppendMenuW(HMENU hMenu,
723 	    UINT uFlags,
724 	    UINT_PTR uIDNewItem,
725 	    LPCWSTR lpNewItem)
726 {
727   MENUITEMINFOW mii;
728   UNICODE_STRING MenuText;
729   BOOL res;
730 
731   RtlInitUnicodeString(&MenuText, 0);
732 
733   MENU_mnu2mnuii( uFlags, uIDNewItem, lpNewItem, &mii, TRUE);
734 
735   /* copy the text string, it will be one or the other */
736   if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
737   {
738     RtlInitUnicodeString(&MenuText, (PWSTR)mii.dwTypeData);
739     mii.dwTypeData = MenuText.Buffer;
740     mii.cch = MenuText.Length / sizeof(WCHAR);
741   }
742   res = NtUserThunkedMenuItemInfo(hMenu, -1, TRUE, TRUE, &mii, &MenuText);
743   return res;
744 }
745 
746 /*
747  * @implemented
748  */
749 DWORD WINAPI
750 CheckMenuItem(HMENU hmenu,
751 	      UINT uIDCheckItem,
752 	      UINT uCheck)
753 {
754   PITEM item;
755   DWORD Ret;
756   UINT uID = uIDCheckItem;
757 
758   if (!ValidateHandle(hmenu, TYPE_MENU))
759      return -1;
760 
761   if (!(item = MENU_FindItem( &hmenu, &uID, uCheck ))) return -1;
762 
763   Ret = item->fState & MFS_CHECKED;
764   if ( Ret == (uCheck & MFS_CHECKED)) return Ret; // Already Checked...
765 
766   return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck);
767 }
768 
769 /*
770  * @implemented
771  */
772 BOOL WINAPI
773 CheckMenuRadioItem(HMENU hMenu,
774                    UINT first,
775                    UINT last,
776                    UINT check,
777                    UINT bypos)
778 {
779     BOOL done = FALSE;
780     UINT i;
781     PITEM mi_first = NULL, mi_check;
782     HMENU m_first, m_check;
783     MENUITEMINFOW mii;
784     mii.cbSize = sizeof( mii);
785 
786     for (i = first; i <= last; i++)
787     {
788         UINT pos = i;
789 
790         if (!mi_first)
791         {
792             m_first = hMenu;
793             mi_first = MENU_FindItem(&m_first, &pos, bypos);
794             if (!mi_first) continue;
795             mi_check = mi_first;
796             m_check = m_first;
797         }
798         else
799         {
800             m_check = hMenu;
801             mi_check = MENU_FindItem(&m_check, &pos, bypos);
802             if (!mi_check) continue;
803         }
804 
805         if (m_first != m_check) continue;
806         if (mi_check->fType == MFT_SEPARATOR) continue;
807 
808         if (i == check)
809         {
810             if (!(mi_check->fType & MFT_RADIOCHECK) || !(mi_check->fState & MFS_CHECKED))
811             {
812                mii.fMask = MIIM_FTYPE | MIIM_STATE;
813                mii.fType = (mi_check->fType & MENUITEMINFO_TYPE_MASK) | MFT_RADIOCHECK;
814                mii.fState = (mi_check->fState & MII_STATE_MASK) | MFS_CHECKED;
815                NtUserThunkedMenuItemInfo(m_check, i, bypos, FALSE, &mii, NULL);
816             }
817             done = TRUE;
818         }
819         else
820         {
821             /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
822             if (mi_check->fState & MFS_CHECKED)
823             {
824                mii.fMask = MIIM_STATE;
825                mii.fState = (mi_check->fState & MII_STATE_MASK) & ~MFS_CHECKED;
826                NtUserThunkedMenuItemInfo(m_check, i, bypos, FALSE, &mii, NULL);
827             }
828         }
829     }
830     return done;
831 }
832 
833 /*
834  * @implemented
835  */
836 HMENU WINAPI
837 CreateMenu(VOID)
838 {
839   return NtUserxCreateMenu();
840 }
841 
842 /*
843  * @implemented
844  */
845 HMENU WINAPI
846 CreatePopupMenu(VOID)
847 {
848   return NtUserxCreatePopupMenu();
849 }
850 
851 /*
852  * @implemented
853  */
854 BOOL WINAPI
855 DrawMenuBar(HWND hWnd)
856 {
857   return NtUserxDrawMenuBar(hWnd);
858 }
859 
860 /*
861  * @implemented
862  */
863 BOOL WINAPI
864 EnableMenuItem(HMENU hMenu,
865 	       UINT uIDEnableItem,
866 	       UINT uEnable)
867 {
868   return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable);
869 }
870 
871 /*
872  * @implemented
873  */
874 HMENU WINAPI
875 GetMenu(HWND hWnd)
876 {
877        PWND Wnd = ValidateHwnd(hWnd);
878 
879        if (!Wnd)
880                return NULL;
881 
882        return UlongToHandle(Wnd->IDMenu);
883 }
884 
885 /*
886  * @implemented
887  */
888 LONG WINAPI
889 GetMenuCheckMarkDimensions(VOID)
890 {
891   return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK),
892 		  GetSystemMetrics(SM_CYMENUCHECK)));
893 }
894 
895 /*
896  * @implemented
897  */
898 DWORD
899 WINAPI
900 GetMenuContextHelpId(HMENU hmenu)
901 {
902   PMENU pMenu;
903   if ((pMenu = ValidateHandle(hmenu, TYPE_MENU)))
904      return pMenu->dwContextHelpId;
905   return 0;
906 }
907 
908 /*
909  * @implemented
910  */
911 UINT WINAPI
912 GetMenuDefaultItem(HMENU hMenu,
913 		   UINT fByPos,
914 		   UINT gmdiFlags)
915 {
916   PMENU pMenu;
917   DWORD gismc = 0;
918   if (!(pMenu = ValidateHandle(hMenu, TYPE_MENU)))
919        return (UINT)-1;
920 
921   return IntGetMenuDefaultItem( pMenu, (BOOL)fByPos, gmdiFlags, &gismc);
922 }
923 
924 /*
925  * @implemented
926  */
927 BOOL WINAPI
928 GetMenuInfo(HMENU hmenu,
929 	    LPMENUINFO lpcmi)
930 {
931   PMENU pMenu;
932 
933   if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
934   {
935      SetLastError(ERROR_INVALID_PARAMETER);
936      return FALSE;
937   }
938 
939   if (!(pMenu = ValidateHandle(hmenu, TYPE_MENU)))
940      return FALSE;
941 
942   if (lpcmi->fMask & MIM_BACKGROUND)
943       lpcmi->hbrBack = pMenu->hbrBack;
944 
945   if (lpcmi->fMask & MIM_HELPID)
946       lpcmi->dwContextHelpID = pMenu->dwContextHelpId;
947 
948   if (lpcmi->fMask & MIM_MAXHEIGHT)
949       lpcmi->cyMax = pMenu->cyMax;
950 
951   if (lpcmi->fMask & MIM_MENUDATA)
952       lpcmi->dwMenuData = pMenu->dwMenuData;
953 
954   if (lpcmi->fMask & MIM_STYLE)
955       lpcmi->dwStyle = pMenu->fFlags & MNS_STYLE_MASK;
956 
957   return TRUE;
958 }
959 
960 /*
961  * @implemented
962  */
963 int WINAPI
964 GetMenuItemCount(HMENU hmenu)
965 {
966   PMENU pMenu;
967   if ((pMenu = ValidateHandle(hmenu, TYPE_MENU)))
968      return pMenu->cItems;
969   return -1;
970 }
971 
972 /*
973  * @implemented
974  */
975 UINT WINAPI
976 GetMenuItemID(HMENU hMenu,
977 	      int nPos)
978 {
979   ITEM * lpmi;
980   if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
981   if (lpmi->spSubMenu) return -1;
982   return lpmi->wID;
983 }
984 
985 /*
986  * @implemented
987  */
988 BOOL WINAPI
989 GetMenuItemInfoA(
990    HMENU hmenu,
991    UINT item,
992    BOOL bypos,
993    LPMENUITEMINFOA lpmii)
994 {
995     BOOL ret;
996     MENUITEMINFOA mii;
997 
998     if( lpmii->cbSize != sizeof( mii) &&
999         lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem))
1000     {
1001         SetLastError( ERROR_INVALID_PARAMETER);
1002         return FALSE;
1003     }
1004     memcpy( &mii, lpmii, lpmii->cbSize);
1005     mii.cbSize = sizeof( mii);
1006     ret = GetMenuItemInfo_common (hmenu,
1007                                   item,
1008                                   bypos,
1009                                   (LPMENUITEMINFOW)&mii,
1010                                   FALSE);
1011     mii.cbSize = lpmii->cbSize;
1012     memcpy( lpmii, &mii, mii.cbSize);
1013     return ret;
1014 }
1015 
1016 /*
1017  * @implemented
1018  */
1019 BOOL WINAPI
1020 GetMenuItemInfoW(
1021    HMENU hMenu,
1022    UINT Item,
1023    BOOL bypos,
1024    LPMENUITEMINFOW lpmii)
1025 {
1026    BOOL ret;
1027    MENUITEMINFOW mii;
1028    if( lpmii->cbSize != sizeof( mii) && lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem))
1029    {
1030       SetLastError( ERROR_INVALID_PARAMETER);
1031       return FALSE;
1032    }
1033    memcpy( &mii, lpmii, lpmii->cbSize);
1034    mii.cbSize = sizeof( mii);
1035    ret = GetMenuItemInfo_common (hMenu, Item, bypos, &mii, TRUE);
1036    mii.cbSize = lpmii->cbSize;
1037    memcpy( lpmii, &mii, mii.cbSize);
1038    return ret;
1039 }
1040 
1041 /*
1042  * @implemented
1043  */
1044 UINT
1045 WINAPI
1046 GetMenuState(
1047   HMENU hMenu,
1048   UINT uId,
1049   UINT uFlags)
1050 {
1051   PITEM pItem;
1052   UINT Type = 0;
1053   TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, uId, uFlags);
1054   if (!(pItem = MENU_FindItem( &hMenu, &uId, uFlags ))) return -1;
1055 
1056   if (!pItem->Xlpstr && pItem->hbmp) Type = MFT_BITMAP;
1057 
1058   if (pItem->spSubMenu)
1059   {
1060      PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
1061      HMENU hsubmenu = UserHMGetHandle(pSubMenu);
1062      Type |= MF_POPUP; // Fix CORE-9269
1063      if (!IsMenu(hsubmenu)) return (UINT)-1;
1064      else return (pSubMenu->cItems << 8) | ((pItem->fState|pItem->fType|Type) & 0xff);
1065   }
1066   else
1067      return (pItem->fType | pItem->fState | Type);
1068 }
1069 
1070 /*
1071  * @implemented
1072  */
1073 int
1074 WINAPI
1075 GetMenuStringA(
1076   HMENU hMenu,
1077   UINT uIDItem,
1078   LPSTR lpString,
1079   int nMaxCount,
1080   UINT uFlag)
1081 {
1082   ITEM *item;
1083   LPWSTR text;
1084   ////// wine Code, seems to be faster.
1085   TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, uIDItem, lpString, nMaxCount, uFlag );
1086 
1087   if (lpString && nMaxCount) lpString[0] = '\0';
1088 
1089   if (!(item = MENU_FindItem( &hMenu, &uIDItem, uFlag )))
1090   {
1091       SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
1092       return 0;
1093   }
1094 
1095   text = item->Xlpstr ? DesktopPtrToUser(item->Xlpstr) : NULL;
1096 
1097   if (!text) return 0;
1098   if (!lpString || !nMaxCount) return WideCharToMultiByte( CP_ACP, 0, text, -1, NULL, 0, NULL, NULL );
1099   if (!WideCharToMultiByte( CP_ACP, 0, text, -1, lpString, nMaxCount, NULL, NULL ))
1100       lpString[nMaxCount-1] = 0;
1101   TRACE("A returning %s\n", lpString);
1102   return strlen(lpString);
1103 }
1104 
1105 /*
1106  * @implemented
1107  */
1108 int
1109 WINAPI
1110 GetMenuStringW(
1111   HMENU hMenu,
1112   UINT uIDItem,
1113   LPWSTR lpString,
1114   int nMaxCount,
1115   UINT uFlag)
1116 {
1117   ITEM *item;
1118   LPWSTR text;
1119 
1120   TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, uIDItem, lpString, nMaxCount, uFlag );
1121 
1122   if (lpString && nMaxCount) lpString[0] = '\0';
1123 
1124   if (!(item = MENU_FindItem( &hMenu, &uIDItem, uFlag )))
1125   {
1126       SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
1127       return 0;
1128   }
1129 
1130   text = item->Xlpstr ? DesktopPtrToUser(item->Xlpstr) : NULL;
1131 
1132   if (!lpString || !nMaxCount) return text ? strlenW(text) : 0;
1133   if( !(text))
1134   {
1135       lpString[0] = 0;
1136       return 0;
1137   }
1138   lstrcpynW( lpString, text, nMaxCount );
1139   TRACE("W returning %S\n", lpString);
1140   return strlenW(lpString);
1141 }
1142 
1143 /*
1144  * @implemented
1145  */
1146 HMENU
1147 WINAPI
1148 GetSubMenu(
1149   HMENU hMenu,
1150   int nPos)
1151 {
1152   PITEM pItem;
1153   if (!(pItem = MENU_FindItem( &hMenu, (UINT*)&nPos, MF_BYPOSITION ))) return NULL;
1154 
1155   if (pItem->spSubMenu)
1156   {
1157      PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
1158      HMENU hsubmenu = UserHMGetHandle(pSubMenu);
1159      if (IsMenu(hsubmenu)) return hsubmenu;
1160   }
1161   return NULL;
1162 }
1163 
1164 /*
1165  * @implemented
1166  */
1167 HMENU
1168 WINAPI
1169 GetSystemMenu(HWND hWnd, BOOL bRevert)
1170 {
1171     return NtUserGetSystemMenu(hWnd, bRevert);
1172 }
1173 
1174 /*
1175  * @implemented
1176  */
1177 BOOL
1178 WINAPI
1179 InsertMenuA(
1180   HMENU hMenu,
1181   UINT uPosition,
1182   UINT uFlags,
1183   UINT_PTR uIDNewItem,
1184   LPCSTR lpNewItem)
1185 {
1186   MENUITEMINFOW mii;
1187   UNICODE_STRING UnicodeString;
1188   BOOL res;
1189 
1190   RtlInitUnicodeString(&UnicodeString, 0);
1191 
1192   MENU_mnu2mnuii( uFlags, uIDNewItem, (LPCWSTR)lpNewItem, &mii, FALSE);
1193 
1194   /* copy the text string, it will be one or the other */
1195   if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
1196   {
1197       if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
1198       {
1199         SetLastError (ERROR_NOT_ENOUGH_MEMORY);
1200         return FALSE;
1201       }
1202       mii.dwTypeData = UnicodeString.Buffer;
1203       mii.cch = UnicodeString.Length / sizeof(WCHAR);
1204   }
1205   else
1206   {
1207       TRACE("Handle bitmaps\n");
1208   }
1209   res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), TRUE, &mii, &UnicodeString);
1210   if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString );
1211   return res;
1212 }
1213 
1214 /*
1215  * @implemented
1216  */
1217 BOOL
1218 WINAPI
1219 InsertMenuItemA(
1220   HMENU hMenu,
1221   UINT uItem,
1222   BOOL fByPosition,
1223   LPCMENUITEMINFOA lpmii)
1224 {
1225   MENUITEMINFOW mii;
1226   UNICODE_STRING UnicodeString;
1227   BOOL res;
1228 
1229   TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii);
1230 
1231   RtlInitUnicodeString(&UnicodeString, 0);
1232 
1233   if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
1234 
1235   /* copy the text string */
1236   if (((mii.fMask & MIIM_STRING) ||
1237       ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING)))
1238         && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) )
1239   {
1240       if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
1241       {
1242         SetLastError (ERROR_NOT_ENOUGH_MEMORY);
1243         return FALSE;
1244       }
1245       mii.dwTypeData = UnicodeString.Buffer;
1246       mii.cch = UnicodeString.Length / sizeof(WCHAR);
1247   }
1248   else
1249   {
1250       TRACE("Handle bitmaps\n");
1251   }
1252   res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mii, &UnicodeString);
1253   if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString );
1254   return res;
1255 }
1256 
1257 /*
1258  * @implemented
1259  */
1260 BOOL
1261 WINAPI
1262 InsertMenuItemW(
1263   HMENU hMenu,
1264   UINT uItem,
1265   BOOL fByPosition,
1266   LPCMENUITEMINFOW lpmii)
1267 {
1268   MENUITEMINFOW mii;
1269   UNICODE_STRING MenuText;
1270   BOOL res = FALSE;
1271 
1272   /* while we could just pass 'lpmii' to win32k, we make a copy so that
1273      if a bad user passes bad data, we crash his process instead of the
1274      entire kernel */
1275 
1276   TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii);
1277 
1278   RtlInitUnicodeString(&MenuText, 0);
1279 
1280   if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
1281 
1282   /* copy the text string */
1283   if (((mii.fMask & MIIM_STRING) ||
1284       ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING)))
1285         && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) )
1286   {
1287     RtlInitUnicodeString(&MenuText, (PWSTR)lpmii->dwTypeData);
1288     mii.dwTypeData = MenuText.Buffer;
1289     mii.cch = MenuText.Length / sizeof(WCHAR);
1290   }
1291   res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mii, &MenuText);
1292   return res;
1293 }
1294 
1295 /*
1296  * @implemented
1297  */
1298 BOOL
1299 WINAPI
1300 InsertMenuW(
1301   HMENU hMenu,
1302   UINT uPosition,
1303   UINT uFlags,
1304   UINT_PTR uIDNewItem,
1305   LPCWSTR lpNewItem)
1306 {
1307   MENUITEMINFOW mii;
1308   UNICODE_STRING MenuText;
1309   BOOL res;
1310 
1311   RtlInitUnicodeString(&MenuText, 0);
1312 
1313   MENU_mnu2mnuii( uFlags, uIDNewItem, lpNewItem, &mii, TRUE);
1314 
1315   /* copy the text string, it will be one or the other */
1316   if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
1317   {
1318     RtlInitUnicodeString(&MenuText, (PWSTR)mii.dwTypeData);
1319     mii.dwTypeData = MenuText.Buffer;
1320     mii.cch = MenuText.Length / sizeof(WCHAR);
1321   }
1322   res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), TRUE, &mii, &MenuText);
1323   return res;
1324 }
1325 
1326 /*
1327  * @implemented
1328  */
1329 BOOL
1330 WINAPI
1331 IsMenu(
1332   HMENU Menu)
1333 {
1334   if (ValidateHandle(Menu, TYPE_MENU)) return TRUE;
1335   return FALSE;
1336 }
1337 
1338 /*
1339  * @implemented
1340  */
1341 HMENU WINAPI
1342 LoadMenuA(HINSTANCE hInstance,
1343 	  LPCSTR lpMenuName)
1344 {
1345   HANDLE Resource = FindResourceA(hInstance, lpMenuName, MAKEINTRESOURCEA(4));
1346   if (Resource == NULL)
1347     {
1348       return(NULL);
1349     }
1350   return(LoadMenuIndirectA((PVOID)LoadResource(hInstance, Resource)));
1351 }
1352 
1353 /*
1354  * @implemented
1355  */
1356 HMENU WINAPI
1357 LoadMenuIndirectA(CONST MENUTEMPLATE *lpMenuTemplate)
1358 {
1359   return(LoadMenuIndirectW(lpMenuTemplate));
1360 }
1361 
1362 /*
1363  * @implemented
1364  */
1365 HMENU WINAPI
1366 LoadMenuIndirectW(CONST MENUTEMPLATE *lpMenuTemplate)
1367 {
1368   HMENU hMenu;
1369   WORD version, offset;
1370   LPCSTR p = (LPCSTR)lpMenuTemplate;
1371 
1372   version = GET_WORD(p);
1373   p += sizeof(WORD);
1374 
1375   switch (version)
1376   {
1377     case 0: /* standard format is version of 0 */
1378       offset = GET_WORD(p);
1379       p += sizeof(WORD) + offset;
1380       if (!(hMenu = CreateMenu())) return 0;
1381       if (!MENU_ParseResource(p, hMenu))
1382       {
1383         DestroyMenu(hMenu);
1384         return 0;
1385       }
1386       return hMenu;
1387     case 1: /* extended format is version of 1 */
1388       offset = GET_WORD(p);
1389       p += sizeof(WORD) + offset;
1390       if (!(hMenu = CreateMenu())) return 0;
1391       if (!MENUEX_ParseResource(p, hMenu))
1392       {
1393         DestroyMenu( hMenu );
1394         return 0;
1395       }
1396       return hMenu;
1397     default:
1398       ERR("Menu template version %d not supported.\n", version);
1399       return 0;
1400   }
1401 }
1402 
1403 /*
1404  * @implemented
1405  */
1406 HMENU WINAPI
1407 LoadMenuW(HINSTANCE hInstance,
1408 	  LPCWSTR lpMenuName)
1409 {
1410   HANDLE Resource = FindResourceW(hInstance, lpMenuName, RT_MENU);
1411   if (Resource == NULL)
1412     {
1413       return(NULL);
1414     }
1415   return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource)));
1416 }
1417 
1418 /*
1419  * @implemented
1420  */
1421 BOOL
1422 WINAPI
1423 ModifyMenuA(
1424   HMENU hMenu,
1425   UINT uPosition,
1426   UINT uFlags,
1427   UINT_PTR uIDNewItem,
1428   LPCSTR lpNewItem)
1429 {
1430   MENUITEMINFOW mii;
1431   UNICODE_STRING UnicodeString;
1432   BOOL res;
1433 
1434   RtlInitUnicodeString(&UnicodeString, 0);
1435 
1436   MENU_mnu2mnuii( uFlags, uIDNewItem, (LPCWSTR)lpNewItem, &mii, FALSE);
1437 
1438   /* copy the text string, it will be one or the other */
1439   if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
1440   {
1441       if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
1442       {
1443         SetLastError (ERROR_NOT_ENOUGH_MEMORY);
1444         return FALSE;
1445       }
1446       mii.dwTypeData = UnicodeString.Buffer;
1447       mii.cch = UnicodeString.Length / sizeof(WCHAR);
1448   }
1449   else
1450   {
1451       TRACE("Handle bitmaps\n");
1452   }
1453   res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), FALSE, &mii, &UnicodeString);
1454   if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString );
1455   return res;
1456 }
1457 
1458 /*
1459  * @implemented
1460  */
1461 BOOL
1462 WINAPI
1463 ModifyMenuW(
1464   HMENU hMenu,
1465   UINT uPosition,
1466   UINT uFlags,
1467   UINT_PTR uIDNewItem,
1468   LPCWSTR lpNewItem)
1469 {
1470   MENUITEMINFOW mii;
1471   UNICODE_STRING MenuText;
1472   BOOL res;
1473 
1474   RtlInitUnicodeString(&MenuText, 0);
1475 
1476   MENU_mnu2mnuii( uFlags, uIDNewItem, lpNewItem, &mii, TRUE);
1477 
1478   /* copy the text string, it will be one or the other */
1479   if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
1480   {
1481     RtlInitUnicodeString(&MenuText, (PWSTR)mii.dwTypeData);
1482     mii.dwTypeData = MenuText.Buffer;
1483     mii.cch = MenuText.Length / sizeof(WCHAR);
1484   }
1485   else
1486   {
1487       TRACE("Handle bitmaps\n");
1488   }
1489   res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), FALSE, &mii, &MenuText);
1490   return res;
1491 }
1492 
1493 /*
1494  * @implemented
1495  */
1496 BOOL WINAPI
1497 SetMenu(HWND hWnd,
1498 	HMENU hMenu)
1499 {
1500   return NtUserSetMenu(hWnd, hMenu, TRUE);
1501 }
1502 
1503 /*
1504  * @implemented
1505  */
1506 BOOL
1507 WINAPI
1508 SetMenuInfo(
1509   HMENU hmenu,
1510   LPCMENUINFO lpcmi)
1511 {
1512   MENUINFO mi;
1513   BOOL res = FALSE;
1514 
1515   if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
1516   {
1517     SetLastError(ERROR_INVALID_PARAMETER);
1518     return res;
1519   }
1520 
1521   memcpy(&mi, lpcmi, sizeof(MENUINFO));
1522   return NtUserThunkedMenuInfo(hmenu, (LPCMENUINFO)&mi);
1523 }
1524 
1525 /*
1526  * @implemented
1527  */
1528 BOOL
1529 WINAPI
1530 SetMenuItemBitmaps(
1531   HMENU hMenu,
1532   UINT uPosition,
1533   UINT uFlags,
1534   HBITMAP hBitmapUnchecked,
1535   HBITMAP hBitmapChecked)
1536 {
1537   MENUITEMINFOW uItem;
1538   memset ( &uItem, 0, sizeof(uItem) );
1539   uItem.cbSize = sizeof(MENUITEMINFOW);
1540   uItem.fMask = MIIM_CHECKMARKS;
1541   uItem.hbmpUnchecked = hBitmapUnchecked;
1542   uItem.hbmpChecked = hBitmapChecked;
1543   return SetMenuItemInfoW(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), &uItem);
1544 }
1545 
1546 /*
1547  * @implemented
1548  */
1549 BOOL
1550 WINAPI
1551 SetMenuItemInfoA(
1552   HMENU hmenu,
1553   UINT item,
1554   BOOL bypos,
1555   LPCMENUITEMINFOA lpmii)
1556 {
1557   MENUITEMINFOW mii;
1558   UNICODE_STRING UnicodeString;
1559   BOOL Ret;
1560 
1561   TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
1562 
1563   RtlInitUnicodeString(&UnicodeString, 0);
1564 
1565   if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
1566 /*
1567  *  MIIM_STRING              == good
1568  *  MIIM_TYPE & MFT_STRING   == good
1569  *  MIIM_STRING & MFT_STRING == good
1570  *  MIIM_STRING & MFT_OWNERDRAW == good
1571  */
1572   if (((mii.fMask & MIIM_STRING) ||
1573       ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING)))
1574         && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) )
1575   {
1576     /* cch is ignored when the content of a menu item is set by calling SetMenuItemInfo. */
1577      if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
1578      {
1579         SetLastError (ERROR_NOT_ENOUGH_MEMORY);
1580         return FALSE;
1581      }
1582      mii.dwTypeData = UnicodeString.Buffer;
1583      mii.cch = UnicodeString.Length / sizeof(WCHAR);
1584   }
1585   else
1586   {
1587      UnicodeString.Buffer = NULL;
1588   }
1589   Ret = NtUserThunkedMenuItemInfo(hmenu, item, bypos, FALSE, &mii, &UnicodeString);
1590   if (UnicodeString.Buffer != NULL) RtlFreeUnicodeString(&UnicodeString);
1591   return Ret;
1592 }
1593 
1594 /*
1595  * @implemented
1596  */
1597 BOOL
1598 WINAPI
1599 SetMenuItemInfoW(
1600   HMENU hMenu,
1601   UINT uItem,
1602   BOOL fByPosition,
1603   LPCMENUITEMINFOW lpmii)
1604 {
1605   MENUITEMINFOW MenuItemInfoW;
1606   UNICODE_STRING UnicodeString;
1607   BOOL Ret;
1608 
1609   TRACE("hmenu %p, item %u, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii);
1610 
1611   RtlInitUnicodeString(&UnicodeString, 0);
1612 
1613   if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &MenuItemInfoW )) return FALSE;
1614 
1615   if (((MenuItemInfoW.fMask & MIIM_STRING) ||
1616       ((MenuItemInfoW.fMask & MIIM_TYPE) &&
1617                            (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
1618         && MenuItemInfoW.dwTypeData && !(GdiValidateHandle((HGDIOBJ)MenuItemInfoW.dwTypeData)) )
1619   {
1620       RtlInitUnicodeString(&UnicodeString, (PCWSTR)MenuItemInfoW.dwTypeData);
1621       MenuItemInfoW.cch = strlenW(MenuItemInfoW.dwTypeData);
1622   }
1623   Ret = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, FALSE, &MenuItemInfoW, &UnicodeString);
1624 
1625   return Ret;
1626 }
1627 
1628 /*
1629  * @implemented
1630  */
1631 BOOL
1632 WINAPI
1633 SetSystemMenu (
1634   HWND hwnd,
1635   HMENU hMenu)
1636 {
1637   if(!hwnd)
1638   {
1639     SetLastError(ERROR_INVALID_WINDOW_HANDLE);
1640     return FALSE;
1641   }
1642   if(!hMenu)
1643   {
1644     SetLastError(ERROR_INVALID_MENU_HANDLE);
1645     return FALSE;
1646   }
1647   return NtUserSetSystemMenu(hwnd, hMenu);
1648 }
1649 
1650 BOOL
1651 WINAPI
1652 TrackPopupMenu(
1653   HMENU Menu,
1654   UINT Flags,
1655   int x,
1656   int y,
1657   int Reserved,
1658   HWND Wnd,
1659   CONST RECT *Rect)
1660 {
1661   return NtUserTrackPopupMenuEx( Menu,
1662                                 Flags,
1663                                     x,
1664                                     y,
1665                                   Wnd,
1666                                  NULL); // LPTPMPARAMS is null
1667 }
1668 
1669 /*
1670  * @unimplemented
1671  */
1672 LRESULT
1673 WINAPI
1674 MenuWindowProcA(
1675 		HWND   hWnd,
1676 		ULONG_PTR Result,
1677 		UINT   Msg,
1678 		WPARAM wParam,
1679 		LPARAM lParam
1680 		)
1681 {
1682   if ( Msg < WM_USER)
1683   {
1684      return PopupMenuWndProcA(hWnd, Msg, wParam, lParam );
1685   }
1686   return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, TRUE);
1687 }
1688 
1689 /*
1690  * @unimplemented
1691  */
1692 LRESULT
1693 WINAPI
1694 MenuWindowProcW(
1695 		HWND   hWnd,
1696 		ULONG_PTR Result,
1697 		UINT   Msg,
1698 		WPARAM wParam,
1699 		LPARAM lParam
1700 		)
1701 {
1702   if ( Msg < WM_USER)
1703   {
1704      return PopupMenuWndProcW(hWnd, Msg, wParam, lParam );
1705   }
1706   return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, FALSE);
1707 }
1708 
1709 /*
1710  * @implemented
1711  */
1712 BOOL
1713 WINAPI
1714 ChangeMenuW(
1715     HMENU hMenu,
1716     UINT cmd,
1717     LPCWSTR lpszNewItem,
1718     UINT cmdInsert,
1719     UINT flags)
1720 {
1721     /*
1722         FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
1723         for MF_DELETE. We should check the parameters for all others
1724         MF_* actions also (anybody got a doc on ChangeMenu?).
1725     */
1726 
1727     switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
1728     {
1729         case MF_APPEND :
1730             return AppendMenuW(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
1731 
1732         case MF_DELETE :
1733             return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
1734 
1735         case MF_CHANGE :
1736             return ModifyMenuW(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
1737 
1738         case MF_REMOVE :
1739             return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
1740                                 flags &~ MF_REMOVE);
1741 
1742         default :   /* MF_INSERT */
1743             return InsertMenuW(hMenu, cmd, flags, cmdInsert, lpszNewItem);
1744     };
1745 }
1746 
1747 /*
1748  * @implemented
1749  */
1750 BOOL
1751 WINAPI
1752 ChangeMenuA(
1753     HMENU hMenu,
1754     UINT cmd,
1755     LPCSTR lpszNewItem,
1756     UINT cmdInsert,
1757     UINT flags)
1758 {
1759     /*
1760         FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
1761         for MF_DELETE. We should check the parameters for all others
1762         MF_* actions also (anybody got a doc on ChangeMenu?).
1763     */
1764 
1765     switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
1766     {
1767         case MF_APPEND :
1768             return AppendMenuA(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
1769 
1770         case MF_DELETE :
1771             return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
1772 
1773         case MF_CHANGE :
1774             return ModifyMenuA(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
1775 
1776         case MF_REMOVE :
1777             return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
1778                                 flags &~ MF_REMOVE);
1779 
1780         default :   /* MF_INSERT */
1781             return InsertMenuA(hMenu, cmd, flags, cmdInsert, lpszNewItem);
1782     };
1783 }
1784 
1785