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