[USER32] Improve the check for item in MENU_FindItem().
[reactos.git] / reactos / win32ss / user / user32 / windows / menu.c
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 ( (Item->fType & MF_POPUP) && (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) lpmii->fType |= MFT_BITMAP;
205 lpmii->hbmpItem = pItem->hbmp; /* not on Win9x/ME */
206 if( lpmii->fType & MFT_BITMAP)
207 {
208 lpmii->dwTypeData = (LPWSTR) pItem->hbmp;
209 lpmii->cch = 0;
210 }
211 else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR))
212 {
213 /* this does not happen on Win9x/ME */
214 lpmii->dwTypeData = 0;
215 lpmii->cch = 0;
216 }
217 }
218
219 /* copy the text string */
220 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)))
221 {
222 if( !pItem->Xlpstr )
223 { // Very strange this fixes a wine test with a crash.
224 if(lpmii->dwTypeData && lpmii->cch && !(GdiValidateHandle((HGDIOBJ)lpmii->dwTypeData)) )
225 {
226 if( unicode)
227 *((WCHAR *)lpmii->dwTypeData) = 0;
228 else
229 *((CHAR *)lpmii->dwTypeData) = 0;
230 }
231 lpmii->cch = 0;
232 }
233 else
234 {
235 int len;
236 LPWSTR text = DesktopPtrToUser(pItem->Xlpstr);
237 if (unicode)
238 {
239 len = strlenW(text);
240 if(lpmii->dwTypeData && lpmii->cch)
241 lstrcpynW(lpmii->dwTypeData, text, lpmii->cch);
242 }
243 else
244 {
245 len = WideCharToMultiByte( CP_ACP, 0, text, -1, NULL, 0, NULL, NULL ) - 1;
246 if(lpmii->dwTypeData && lpmii->cch)
247 if (!WideCharToMultiByte( CP_ACP, 0, text, -1,
248 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
249 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
250 }
251 /* if we've copied a substring we return its length */
252 if(lpmii->dwTypeData && lpmii->cch)
253 if (lpmii->cch <= len + 1)
254 lpmii->cch--;
255 else
256 lpmii->cch = len;
257 else
258 {
259 /* return length of string */
260 /* not on Win9x/ME if fType & MFT_BITMAP */
261 lpmii->cch = len;
262 }
263 }
264 }
265
266 if (lpmii->fMask & MIIM_FTYPE)
267 lpmii->fType = pItem->fType & MENUITEMINFO_TYPE_MASK;
268
269 if (lpmii->fMask & MIIM_BITMAP)
270 lpmii->hbmpItem = pItem->hbmp;
271
272 if (lpmii->fMask & MIIM_STATE)
273 lpmii->fState = pItem->fState & MENUITEMINFO_STATE_MASK;
274
275 if (lpmii->fMask & MIIM_ID)
276 lpmii->wID = pItem->wID;
277
278 if (lpmii->fMask & MIIM_SUBMENU && pItem->spSubMenu )
279 {
280 PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
281 HMENU hSubMenu = UserHMGetHandle(pSubMenu);
282 lpmii->hSubMenu = hSubMenu;
283 }
284 else
285 {
286 /* hSubMenu is always cleared
287 * (not on Win9x/ME ) */
288 lpmii->hSubMenu = 0;
289 }
290
291 if (lpmii->fMask & MIIM_CHECKMARKS)
292 {
293 lpmii->hbmpChecked = pItem->hbmpChecked;
294 lpmii->hbmpUnchecked = pItem->hbmpUnchecked;
295 }
296 if (lpmii->fMask & MIIM_DATA)
297 lpmii->dwItemData = pItem->dwItemData;
298
299 return TRUE;
300 }
301
302
303 //
304 // User side Menu Class Proc.
305 //
306 LRESULT WINAPI
307 PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
308 {
309 LRESULT lResult;
310 PWND pWnd;
311
312 TRACE("PMWPW : hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
313
314 pWnd = ValidateHwnd(Wnd);
315 if (pWnd)
316 {
317 if (!pWnd->fnid)
318 {
319 if (Message != WM_NCCREATE)
320 {
321 return DefWindowProcW(Wnd, Message, wParam, lParam);
322 }
323 }
324 else
325 {
326 if (pWnd->fnid != FNID_MENU)
327 {
328 ERR("Wrong window class for Menu!\n");
329 return 0;
330 }
331 }
332 }
333
334 switch(Message)
335 {
336 case WM_DESTROY:
337 case WM_NCDESTROY:
338 case WM_NCCREATE:
339 case WM_CREATE:
340 case MM_SETMENUHANDLE:
341 case MM_GETMENUHANDLE:
342 case MN_SETHMENU:
343 case MN_GETHMENU:
344 case WM_PAINT:
345 case WM_PRINTCLIENT:
346 {
347 TRACE("Menu Class ProcW\n");
348 NtUserMessageCall( Wnd, Message, wParam, lParam, (ULONG_PTR)&lResult, FNID_MENU, FALSE);
349 return lResult;
350 }
351 case WM_MOUSEACTIVATE: /* We don't want to be activated */
352 return MA_NOACTIVATE;
353
354 case WM_ERASEBKGND:
355 return 1;
356
357 case WM_SHOWWINDOW: // Not sure what this does....
358 if (0 != wParam)
359 {
360 if (0 == GetWindowLongPtrW(Wnd, 0))
361 {
362 OutputDebugStringA("no menu to display\n");
363 }
364 }
365 else
366 {
367 //SetWindowLongPtrW(Wnd, 0, 0);
368 }
369 break;
370
371 default:
372 return DefWindowProcW(Wnd, Message, wParam, lParam);
373 }
374
375 return 0;
376 }
377
378 LRESULT WINAPI PopupMenuWndProcA(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
379 {
380 PWND pWnd;
381
382 pWnd = ValidateHwnd(Wnd);
383 if (pWnd && !pWnd->fnid && Message != WM_NCCREATE)
384 {
385 return DefWindowProcA(Wnd, Message, wParam, lParam);
386 }
387 TRACE("YES! hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
388
389 switch(Message)
390 {
391 case WM_NCCREATE:
392 case WM_CREATE:
393 case WM_MOUSEACTIVATE:
394 case WM_PAINT:
395 case WM_PRINTCLIENT:
396 case WM_ERASEBKGND:
397 case WM_DESTROY:
398 case WM_NCDESTROY:
399 case WM_SHOWWINDOW:
400 case MM_SETMENUHANDLE:
401 case MM_GETMENUHANDLE:
402 case MN_SETHMENU:
403 case MN_GETHMENU:
404 return PopupMenuWndProcW(Wnd, Message, wParam, lParam);
405
406 default:
407 return DefWindowProcA(Wnd, Message, wParam, lParam);
408 }
409 return 0;
410 }
411
412 /**********************************************************************
413 * MENU_ParseResource
414 *
415 * Parse a standard menu resource and add items to the menu.
416 * Return a pointer to the end of the resource.
417 *
418 * NOTE: flags is equivalent to the mtOption field
419 */
420 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu)
421 {
422 WORD flags, id = 0;
423 HMENU hSubMenu;
424 LPCWSTR str;
425 BOOL end = FALSE;
426
427 do
428 {
429 flags = GET_WORD(res);
430
431 /* remove MF_END flag before passing it to AppendMenu()! */
432 end = (flags & MF_END);
433 if(end) flags ^= MF_END;
434
435 res += sizeof(WORD);
436 if(!(flags & MF_POPUP))
437 {
438 id = GET_WORD(res);
439 res += sizeof(WORD);
440 }
441 str = (LPCWSTR)res;
442 res += (strlenW(str) + 1) * sizeof(WCHAR);
443
444 if (flags & MF_POPUP)
445 {
446 hSubMenu = CreatePopupMenu();
447 if(!hSubMenu) return NULL;
448 if(!(res = MENU_ParseResource(res, hSubMenu))) return NULL;
449 AppendMenuW(hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str);
450 }
451 else /* Not a popup */
452 {
453 AppendMenuW(hMenu, flags, id, *(LPCWSTR)str ? (LPCWSTR)str : NULL);
454 }
455 } while(!end);
456 return res;
457 }
458
459 /**********************************************************************
460 * MENUEX_ParseResource
461 *
462 * Parse an extended menu resource and add items to the menu.
463 * Return a pointer to the end of the resource.
464 */
465 static LPCSTR MENUEX_ParseResource(LPCSTR res, HMENU hMenu)
466 {
467 WORD resinfo;
468 do
469 {
470 MENUITEMINFOW mii;
471
472 mii.cbSize = sizeof(mii);
473 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
474 mii.fType = GET_DWORD(res);
475 res += sizeof(DWORD);
476 mii.fState = GET_DWORD(res);
477 res += sizeof(DWORD);
478 mii.wID = GET_DWORD(res);
479 res += sizeof(DWORD);
480 resinfo = GET_WORD(res);
481 res += sizeof(WORD);
482 /* Align the text on a word boundary. */
483 res += (~((UINT_PTR)res - 1)) & 1;
484 mii.dwTypeData = (LPWSTR)res;
485 mii.cch = strlenW(mii.dwTypeData);
486 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
487 /* Align the following fields on a dword boundary. */
488 res += (~((UINT_PTR)res - 1)) & 3;
489
490 TRACE("Menu item: [%08x,%08x,%04x,%04x,%S]\n",
491 mii.fType, mii.fState, mii.wID, resinfo, mii.dwTypeData);
492
493 if (resinfo & 1) /* Pop-up? */
494 {
495 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
496 res += sizeof(DWORD);
497 mii.hSubMenu = CreatePopupMenu();
498 if (!mii.hSubMenu)
499 {
500 ERR("CreatePopupMenu failed\n");
501 return NULL;
502 }
503
504 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu)))
505 {
506 ERR("MENUEX_ParseResource failed\n");
507 DestroyMenu(mii.hSubMenu);
508 return NULL;
509 }
510 mii.fMask |= MIIM_SUBMENU;
511 }
512 else if (!mii.dwTypeData[0] && !(mii.fType & MF_SEPARATOR))
513 {
514 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
515 mii.wID, mii.fType);
516 mii.fType |= MF_SEPARATOR;
517 }
518 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
519 } while (!(resinfo & MF_END));
520 return res;
521 }
522
523
524 /**********************************************************************
525 * MENU_mnu2mnuii
526 *
527 * Uses flags, id and text ptr, passed by InsertMenu() and
528 * ModifyMenu() to setup a MenuItemInfo structure.
529 */
530 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str, LPMENUITEMINFOW pmii, BOOL Unicode)
531 {
532 RtlZeroMemory( pmii, sizeof( MENUITEMINFOW));
533 pmii->cbSize = sizeof( MENUITEMINFOW);
534 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
535 /* setting bitmap clears text and vice versa */
536 if( IS_STRING_ITEM(flags)) {
537 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
538 if( !str)
539 flags |= MF_SEPARATOR;
540 /* Item beginning with a backspace is a help item */
541 /* FIXME: wrong place, this is only true in win16 */
542 else
543 {
544 if (Unicode)
545 {
546 if (*str == '\b')
547 {
548 flags |= MF_HELP;
549 str++;
550 }
551 }
552 else
553 {
554 LPCSTR NewItemA = (LPCSTR) str;
555 if (*NewItemA == '\b')
556 {
557 flags |= MF_HELP;
558 NewItemA++;
559 str = (LPCWSTR) NewItemA;
560 }
561 TRACE("A cch %d\n",strlen(NewItemA));
562 }
563 }
564 pmii->dwTypeData = (LPWSTR)str;
565 } else if( flags & MFT_BITMAP){
566 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
567 pmii->hbmpItem = (HBITMAP)str;
568 }
569 if( flags & MF_OWNERDRAW){
570 pmii->fMask |= MIIM_DATA;
571 pmii->dwItemData = (ULONG_PTR) str;
572 }
573 if( flags & MF_POPUP && MENU_GetMenu((HMENU)id)) {
574 pmii->fMask |= MIIM_SUBMENU;
575 pmii->hSubMenu = (HMENU)id;
576 }
577 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
578 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
579 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
580 pmii->wID = (UINT)id;
581 }
582
583 /**********************************************************************
584 * MENU_NormalizeMenuItemInfoStruct
585 *
586 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
587 * check, copy and extend the MENUITEMINFO struct from the version that the application
588 * supplied to the version used by wine source. */
589 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
590 MENUITEMINFOW *pmii_out )
591 {
592 /* do we recognize the size? */
593 if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
594 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) {
595 SetLastError( ERROR_INVALID_PARAMETER);
596 return FALSE;
597 }
598 /* copy the fields that we have */
599 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
600 /* if the hbmpItem member is missing then extend */
601 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
602 pmii_out->cbSize = sizeof( MENUITEMINFOW);
603 pmii_out->hbmpItem = NULL;
604 }
605 /* test for invalid bit combinations */
606 if( (pmii_out->fMask & MIIM_TYPE &&
607 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
608 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
609 ERR("invalid combination of fMask bits used\n");
610 /* this does not happen on Win9x/ME */
611 SetLastError( ERROR_INVALID_PARAMETER);
612 return FALSE;
613 }
614 /* convert old style (MIIM_TYPE) to the new and keep the old one too */
615 if( pmii_out->fMask & MIIM_TYPE){
616 pmii_out->fMask |= MIIM_FTYPE;
617 if( IS_STRING_ITEM(pmii_out->fType)){
618 pmii_out->fMask |= MIIM_STRING;
619 } else if( (pmii_out->fType) & MFT_BITMAP){
620 pmii_out->fMask |= MIIM_BITMAP;
621 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
622 }
623 }
624 if (pmii_out->fMask & MIIM_FTYPE )
625 {
626 pmii_out->fType &= ~MENUITEMINFO_TYPE_MASK;
627 pmii_out->fType |= pmii_in->fType & MENUITEMINFO_TYPE_MASK;
628 }
629 if (pmii_out->fMask & MIIM_STATE)
630 /* Other menu items having MFS_DEFAULT are not converted
631 to normal items */
632 pmii_out->fState = pmii_in->fState & MENUITEMINFO_STATE_MASK;
633
634 if (pmii_out->fMask & MIIM_SUBMENU)
635 {
636 if ((pmii_out->hSubMenu != NULL) && !IsMenu(pmii_out->hSubMenu))
637 return FALSE;
638 }
639
640 return TRUE;
641 }
642
643 BOOL
644 MenuInit(VOID)
645 {
646 return TRUE;
647 }
648
649 VOID
650 MenuCleanup(VOID)
651 {
652 }
653
654
655 NTSTATUS WINAPI
656 User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength)
657 {
658 LRESULT Result = 0;
659
660 // Use this for Menu Ole!!
661
662 return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS));
663 }
664
665 NTSTATUS WINAPI
666 User32CallLoadMenuFromKernel(PVOID Arguments, ULONG ArgumentLength)
667 {
668 PLOADMENU_CALLBACK_ARGUMENTS Common;
669 LRESULT Result;
670
671 Common = (PLOADMENU_CALLBACK_ARGUMENTS) Arguments;
672
673 Result = (LRESULT)LoadMenuW( Common->hModule, Common->InterSource ? MAKEINTRESOURCE(Common->InterSource) : (LPCWSTR)&Common->MenuName);
674
675 return ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS);
676 }
677
678
679 /* FUNCTIONS *****************************************************************/
680
681 /*
682 * @implemented
683 */
684 BOOL WINAPI
685 AppendMenuA(HMENU hMenu,
686 UINT uFlags,
687 UINT_PTR uIDNewItem,
688 LPCSTR lpNewItem)
689 {
690 MENUITEMINFOW mii;
691 UNICODE_STRING UnicodeString;
692 BOOL res;
693
694 RtlInitUnicodeString(&UnicodeString, 0);
695
696 MENU_mnu2mnuii( uFlags, uIDNewItem, (LPCWSTR)lpNewItem, &mii, FALSE);
697
698 /* copy the text string, it will be one or the other */
699 if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
700 {
701 if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
702 {
703 SetLastError (ERROR_NOT_ENOUGH_MEMORY);
704 return FALSE;
705 }
706 mii.dwTypeData = UnicodeString.Buffer;
707 mii.cch = UnicodeString.Length / sizeof(WCHAR);
708 }
709 else
710 {
711 TRACE("AMA Handle bitmaps\n");
712 }
713 ////// Answer a question, why a -1? To hunt for the end of the item list. Get it, to Append?
714 res = NtUserThunkedMenuItemInfo(hMenu, -1, TRUE, TRUE, &mii, &UnicodeString);
715 if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString );
716 return res;
717 }
718
719 /*
720 * @implemented
721 */
722 BOOL WINAPI
723 AppendMenuW(HMENU hMenu,
724 UINT uFlags,
725 UINT_PTR uIDNewItem,
726 LPCWSTR lpNewItem)
727 {
728 MENUITEMINFOW mii;
729 UNICODE_STRING MenuText;
730 BOOL res;
731
732 RtlInitUnicodeString(&MenuText, 0);
733
734 MENU_mnu2mnuii( uFlags, uIDNewItem, lpNewItem, &mii, TRUE);
735
736 /* copy the text string, it will be one or the other */
737 if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
738 {
739 RtlInitUnicodeString(&MenuText, (PWSTR)mii.dwTypeData);
740 mii.dwTypeData = MenuText.Buffer;
741 mii.cch = MenuText.Length / sizeof(WCHAR);
742 }
743 res = NtUserThunkedMenuItemInfo(hMenu, -1, TRUE, TRUE, &mii, &MenuText);
744 return res;
745 }
746
747 /*
748 * @implemented
749 */
750 DWORD WINAPI
751 CheckMenuItem(HMENU hmenu,
752 UINT uIDCheckItem,
753 UINT uCheck)
754 {
755 PITEM item;
756 DWORD Ret;
757 UINT uID = uIDCheckItem;
758
759 if (!ValidateHandle(hmenu, TYPE_MENU))
760 return -1;
761
762 if (!(item = MENU_FindItem( &hmenu, &uID, uCheck ))) return -1;
763
764 Ret = item->fState & MFS_CHECKED;
765 if ( Ret == (uCheck & MFS_CHECKED)) return Ret; // Already Checked...
766
767 return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck);
768 }
769
770 /*
771 * @implemented
772 */
773 BOOL WINAPI
774 CheckMenuRadioItem(HMENU hMenu,
775 UINT first,
776 UINT last,
777 UINT check,
778 UINT bypos)
779 {
780 BOOL done = FALSE;
781 UINT i;
782 PITEM mi_first = NULL, mi_check;
783 HMENU m_first, m_check;
784 MENUITEMINFOW mii;
785 mii.cbSize = sizeof( mii);
786
787 for (i = first; i <= last; i++)
788 {
789 UINT pos = i;
790
791 if (!mi_first)
792 {
793 m_first = hMenu;
794 mi_first = MENU_FindItem(&m_first, &pos, bypos);
795 if (!mi_first) continue;
796 mi_check = mi_first;
797 m_check = m_first;
798 }
799 else
800 {
801 m_check = hMenu;
802 mi_check = MENU_FindItem(&m_check, &pos, bypos);
803 if (!mi_check) continue;
804 }
805
806 if (m_first != m_check) continue;
807 if (mi_check->fType == MFT_SEPARATOR) continue;
808
809 if (i == check)
810 {
811 if (!(mi_check->fType & MFT_RADIOCHECK) || !(mi_check->fState & MFS_CHECKED))
812 {
813 mii.fMask = MIIM_FTYPE | MIIM_STATE;
814 mii.fType = (mi_check->fType & MENUITEMINFO_TYPE_MASK) | MFT_RADIOCHECK;
815 mii.fState = (mi_check->fState & MII_STATE_MASK) | MFS_CHECKED;
816 NtUserThunkedMenuItemInfo(m_check, i, bypos, FALSE, &mii, NULL);
817 }
818 done = TRUE;
819 }
820 else
821 {
822 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
823 if (mi_check->fState & MFS_CHECKED)
824 {
825 mii.fMask = MIIM_STATE;
826 mii.fState = (mi_check->fState & MII_STATE_MASK) & ~MFS_CHECKED;
827 NtUserThunkedMenuItemInfo(m_check, i, bypos, FALSE, &mii, NULL);
828 }
829 }
830 }
831 return done;
832 }
833
834 /*
835 * @implemented
836 */
837 HMENU WINAPI
838 CreateMenu(VOID)
839 {
840 return NtUserxCreateMenu();
841 }
842
843 /*
844 * @implemented
845 */
846 HMENU WINAPI
847 CreatePopupMenu(VOID)
848 {
849 return NtUserxCreatePopupMenu();
850 }
851
852 /*
853 * @implemented
854 */
855 BOOL WINAPI
856 DrawMenuBar(HWND hWnd)
857 {
858 return NtUserxDrawMenuBar(hWnd);
859 }
860
861 /*
862 * @implemented
863 */
864 BOOL WINAPI
865 EnableMenuItem(HMENU hMenu,
866 UINT uIDEnableItem,
867 UINT uEnable)
868 {
869 return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable);
870 }
871
872 /*
873 * @implemented
874 */
875 HMENU WINAPI
876 GetMenu(HWND hWnd)
877 {
878 PWND Wnd = ValidateHwnd(hWnd);
879
880 if (!Wnd)
881 return NULL;
882
883 return UlongToHandle(Wnd->IDMenu);
884 }
885
886 /*
887 * @implemented
888 */
889 LONG WINAPI
890 GetMenuCheckMarkDimensions(VOID)
891 {
892 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK),
893 GetSystemMetrics(SM_CYMENUCHECK)));
894 }
895
896 /*
897 * @implemented
898 */
899 DWORD
900 WINAPI
901 GetMenuContextHelpId(HMENU hmenu)
902 {
903 PMENU pMenu;
904 if ((pMenu = ValidateHandle(hmenu, TYPE_MENU)))
905 return pMenu->dwContextHelpId;
906 return 0;
907 }
908
909 /*
910 * @implemented
911 */
912 UINT WINAPI
913 GetMenuDefaultItem(HMENU hMenu,
914 UINT fByPos,
915 UINT gmdiFlags)
916 {
917 PMENU pMenu;
918 DWORD gismc = 0;
919 if (!(pMenu = ValidateHandle(hMenu, TYPE_MENU)))
920 return (UINT)-1;
921
922 return IntGetMenuDefaultItem( pMenu, (BOOL)fByPos, gmdiFlags, &gismc);
923 }
924
925 /*
926 * @implemented
927 */
928 BOOL WINAPI
929 GetMenuInfo(HMENU hmenu,
930 LPMENUINFO lpcmi)
931 {
932 PMENU pMenu;
933
934 if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
935 {
936 SetLastError(ERROR_INVALID_PARAMETER);
937 return FALSE;
938 }
939
940 if (!(pMenu = ValidateHandle(hmenu, TYPE_MENU)))
941 return FALSE;
942
943 if (lpcmi->fMask & MIM_BACKGROUND)
944 lpcmi->hbrBack = pMenu->hbrBack;
945
946 if (lpcmi->fMask & MIM_HELPID)
947 lpcmi->dwContextHelpID = pMenu->dwContextHelpId;
948
949 if (lpcmi->fMask & MIM_MAXHEIGHT)
950 lpcmi->cyMax = pMenu->cyMax;
951
952 if (lpcmi->fMask & MIM_MENUDATA)
953 lpcmi->dwMenuData = pMenu->dwMenuData;
954
955 if (lpcmi->fMask & MIM_STYLE)
956 lpcmi->dwStyle = pMenu->fFlags & MNS_STYLE_MASK;
957
958 return TRUE;
959 }
960
961 /*
962 * @implemented
963 */
964 int WINAPI
965 GetMenuItemCount(HMENU hmenu)
966 {
967 PMENU pMenu;
968 if ((pMenu = ValidateHandle(hmenu, TYPE_MENU)))
969 return pMenu->cItems;
970 return -1;
971 }
972
973 /*
974 * @implemented
975 */
976 UINT WINAPI
977 GetMenuItemID(HMENU hMenu,
978 int nPos)
979 {
980 ITEM * lpmi;
981 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
982 if (lpmi->spSubMenu) return -1;
983 return lpmi->wID;
984 }
985
986 /*
987 * @implemented
988 */
989 BOOL WINAPI
990 GetMenuItemInfoA(
991 HMENU hmenu,
992 UINT item,
993 BOOL bypos,
994 LPMENUITEMINFOA lpmii)
995 {
996 BOOL ret;
997 MENUITEMINFOA mii;
998
999 if( lpmii->cbSize != sizeof( mii) &&
1000 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem))
1001 {
1002 SetLastError( ERROR_INVALID_PARAMETER);
1003 return FALSE;
1004 }
1005 memcpy( &mii, lpmii, lpmii->cbSize);
1006 mii.cbSize = sizeof( mii);
1007 ret = GetMenuItemInfo_common (hmenu,
1008 item,
1009 bypos,
1010 (LPMENUITEMINFOW)&mii,
1011 FALSE);
1012 mii.cbSize = lpmii->cbSize;
1013 memcpy( lpmii, &mii, mii.cbSize);
1014 return ret;
1015 }
1016
1017 /*
1018 * @implemented
1019 */
1020 BOOL WINAPI
1021 GetMenuItemInfoW(
1022 HMENU hMenu,
1023 UINT Item,
1024 BOOL bypos,
1025 LPMENUITEMINFOW lpmii)
1026 {
1027 BOOL ret;
1028 MENUITEMINFOW mii;
1029 if( lpmii->cbSize != sizeof( mii) && lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem))
1030 {
1031 SetLastError( ERROR_INVALID_PARAMETER);
1032 return FALSE;
1033 }
1034 memcpy( &mii, lpmii, lpmii->cbSize);
1035 mii.cbSize = sizeof( mii);
1036 ret = GetMenuItemInfo_common (hMenu, Item, bypos, &mii, TRUE);
1037 mii.cbSize = lpmii->cbSize;
1038 memcpy( lpmii, &mii, mii.cbSize);
1039 return ret;
1040 }
1041
1042 /*
1043 * @implemented
1044 */
1045 UINT
1046 WINAPI
1047 GetMenuState(
1048 HMENU hMenu,
1049 UINT uId,
1050 UINT uFlags)
1051 {
1052 PITEM pItem;
1053 UINT Type = 0;
1054 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, uId, uFlags);
1055 if (!(pItem = MENU_FindItem( &hMenu, &uId, uFlags ))) return -1;
1056
1057 if (!pItem->Xlpstr && pItem->hbmp) Type = MFT_BITMAP;
1058
1059 if (pItem->spSubMenu)
1060 {
1061 PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
1062 HMENU hsubmenu = UserHMGetHandle(pSubMenu);
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