added support for right-aligned menus
[reactos.git] / reactos / lib / user32 / windows / menu.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id: menu.c,v 1.46 2004/02/01 20:45:02 weiden Exp $
20 *
21 * PROJECT: ReactOS user32.dll
22 * FILE: lib/user32/windows/menu.c
23 * PURPOSE: Menus
24 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
25 * UPDATE HISTORY:
26 * 09-05-2001 CSH Created
27 */
28
29 /* INCLUDES ******************************************************************/
30
31 #include <windows.h>
32 #include <user32.h>
33 #include <debug.h>
34 #include <string.h>
35 #include <draw.h>
36 #include <window.h>
37 #include <strpool.h>
38
39 #include <user32/callback.h>
40 #include "user32/regcontrol.h"
41 #include "../controls/controls.h"
42
43 /* TYPES *********************************************************************/
44
45 #define MENU_TYPE_MASK ((MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
46
47 #define MENU_ITEM_TYPE(flags) \
48 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
49
50 #define MENU_BAR_ITEMS_SPACE (12)
51 #define SEPARATOR_HEIGHT (5)
52 #define MENU_TAB_SPACE (8)
53
54 #ifndef MF_END
55 #define MF_END (0x0080)
56 #endif
57
58 #ifndef MIIM_STRING
59 #define MIIM_STRING (0x00000040)
60 #endif
61
62 #define MAKEINTATOMA(atom) ((LPCSTR)((ULONG_PTR)((WORD)(atom))))
63 #define MAKEINTATOMW(atom) ((LPCWSTR)((ULONG_PTR)((WORD)(atom))))
64 #define POPUPMENU_CLASS_ATOMA MAKEINTATOMA(32768) /* PopupMenu */
65 #define POPUPMENU_CLASS_ATOMW MAKEINTATOMW(32768) /* PopupMenu */
66
67 static LRESULT WINAPI PopupMenuWndProcW(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
68
69 /*********************************************************************
70 * PopupMenu class descriptor
71 */
72 const struct builtin_class_descr POPUPMENU_builtin_class =
73 {
74 POPUPMENU_CLASS_ATOMW, /* name */
75 CS_GLOBALCLASS | CS_SAVEBITS | CS_DBLCLKS, /* style */
76 (WNDPROC) PopupMenuWndProcW, /* FIXME - procW */
77 (WNDPROC) NULL, /* FIXME - procA */
78 sizeof(MENUINFO *), /* extra */
79 (LPCWSTR) IDC_ARROW, /* cursor */
80 (HBRUSH)(COLOR_MENU + 1) /* brush */
81 };
82
83
84 /* INTERNAL FUNCTIONS ********************************************************/
85
86 /* Rip the fun and easy to use and fun WINE unicode string manipulation routines.
87 * Of course I didnt copy the ASM code because we want this to be portable
88 * and it needs to go away.
89 */
90
91 #ifndef GET_WORD
92 #define GET_WORD(ptr) (*(WORD *)(ptr))
93 #endif
94 #ifndef GET_DWORD
95 #define GET_DWORD(ptr) (*(DWORD *)(ptr))
96 #endif
97
98 HFONT hMenuFont = NULL;
99 HFONT hMenuFontBold = NULL;
100
101 static LRESULT WINAPI PopupMenuWndProcW(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
102 {
103 switch(message)
104 {
105 default:
106 return DefWindowProcW(hwnd, message, wParam, lParam);
107 }
108 return 0;
109 }
110
111 /**********************************************************************
112 * MENUEX_ParseResource
113 *
114 * Parse an extended menu resource and add items to the menu.
115 * Return a pointer to the end of the resource.
116 *
117 * FIXME - should we be passing an LPCSTR to a predominantly UNICODE function?
118 */
119 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
120 {
121 WORD resinfo;
122
123 do
124 {
125 MENUITEMINFOW mii;
126
127 mii.cbSize = sizeof(mii);
128 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
129 mii.fType = GET_DWORD(res);
130 res += sizeof(DWORD);
131 mii.fState = GET_DWORD(res);
132 res += sizeof(DWORD);
133 mii.wID = GET_DWORD(res);
134 res += sizeof(DWORD);
135 resinfo = GET_WORD(res);
136 res += sizeof(WORD);
137 /* Align the text on a word boundary. */
138 res += (~((int)res - 1)) & 1;
139 mii.dwTypeData = (LPWSTR) res;
140 res += (1 + wcslen(mii.dwTypeData)) * sizeof(WCHAR);
141 /* Align the following fields on a dword boundary. */
142 res += (~((int)res - 1)) & 3;
143
144 if (resinfo & 1) /* Pop-up? */
145 {
146 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
147 res += sizeof(DWORD);
148 mii.hSubMenu = CreatePopupMenu();
149 if (!mii.hSubMenu)
150 return NULL;
151 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu)))
152 {
153 DestroyMenu(mii.hSubMenu);
154 return NULL;
155 }
156 mii.fMask |= MIIM_SUBMENU;
157 mii.fType |= MF_POPUP;
158 }
159 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
160 {
161 DbgPrint("WARN: Converting NULL menu item %04x, type %04x to SEPARATOR\n",
162 mii.wID, mii.fType);
163 mii.fType |= MF_SEPARATOR;
164 }
165 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
166 }
167 while (!(resinfo & MF_END));
168 return res;
169 }
170
171
172 /**********************************************************************
173 * MENU_ParseResource
174 *
175 * Parse a standard menu resource and add items to the menu.
176 * Return a pointer to the end of the resource.
177 *
178 * NOTE: flags is equivalent to the mtOption field
179 */
180 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
181 {
182 WORD flags, id = 0;
183 HMENU hSubMenu;
184 LPCSTR str;
185 BOOL end = FALSE;
186
187 do
188 {
189 flags = GET_WORD(res);
190
191 /* remove MF_END flag before passing it to AppendMenu()! */
192 end = (flags & MF_END);
193 if(end) flags ^= MF_END;
194
195 res += sizeof(WORD);
196 if(!(flags & MF_POPUP))
197 {
198 id = GET_WORD(res);
199 res += sizeof(WORD);
200 }
201 str = res;
202 if(!unicode)
203 res += strlen(str) + 1;
204 else
205 res += (wcslen((LPCWSTR)str) + 1) * sizeof(WCHAR);
206 if (flags & MF_POPUP)
207 {
208 hSubMenu = CreatePopupMenu();
209 if(!hSubMenu) return NULL;
210 if(!(res = MENU_ParseResource(res, hSubMenu, unicode)))
211 return NULL;
212 if(!unicode)
213 AppendMenuA(hMenu, flags, (UINT)hSubMenu, str);
214 else
215 AppendMenuW(hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str);
216 }
217 else /* Not a popup */
218 {
219 if(!unicode)
220 AppendMenuA(hMenu, flags, id, *str ? str : NULL);
221 else
222 AppendMenuW(hMenu, flags, id,
223 *(LPCWSTR)str ? (LPCWSTR)str : NULL);
224 }
225 } while(!end);
226
227 return res;
228 }
229
230
231 NTSTATUS STDCALL
232 User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength)
233 {
234 LRESULT Result;
235 HMODULE hUser32;
236 hUser32 = GetModuleHandleW(L"USER32");
237 Result = (LRESULT)LoadMenuW(hUser32, L"SYSMENU");
238 return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS));
239 }
240
241
242 BOOL
243 MenuInit(VOID)
244 {
245 NONCLIENTMETRICSW ncm;
246
247 /* get the menu font */
248 if(!hMenuFont || !hMenuFontBold)
249 {
250 ncm.cbSize = sizeof(ncm);
251 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
252 {
253 DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
254 return FALSE;
255 }
256
257 hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont);
258 if(hMenuFont == NULL)
259 {
260 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
261 return FALSE;
262 }
263
264 ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
265 hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont);
266 if(hMenuFontBold == NULL)
267 {
268 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
269 return FALSE;
270 }
271 }
272
273 return TRUE;
274 }
275
276
277 static BOOL
278 MeasureMenuItem(HWND hWnd, HMENU mnu, HDC hDC, MENUITEMINFOW *mii, RECT *mir, LPWSTR str)
279 {
280 BOOL res = FALSE;
281 MEASUREITEMSTRUCT mis;
282 SIZE sz;
283
284 if(mii->fType & MFT_OWNERDRAW)
285 {
286 /* send WM_MEASUREITEM message to window */
287 mis.CtlType = ODT_MENU;
288 mis.CtlID = 0;
289 mis.itemID = mii->wID;
290 mis.itemWidth = 0;
291 mis.itemHeight = 0;
292 mis.itemData = mii->dwItemData;
293 res = (BOOL)SendMessageW(hWnd, WM_MEASUREITEM, 0, (LPARAM)&mis);
294 if(res)
295 {
296 mir->right = mir->left + mis.itemWidth;
297 mir->bottom = mir->top + mis.itemHeight;
298 }
299 else
300 {
301 /* FIXME calculate size internally assuming the menu item is empty */
302 mir->right = mir->left + 1;
303 mir->bottom = mir->top + 1;
304 }
305 return res;
306 }
307 else
308 {
309 GetTextExtentPoint32W(hDC, str, mii->cch, &sz);
310 /* FIXME calculate the size of the menu item */
311 mir->right = mir->left + sz.cx + 6;
312 mir->bottom = mir->top + max(sz.cy, GetSystemMetrics(SM_CYMENU));
313 return TRUE;
314 }
315 }
316
317 static BOOL
318 DrawMenuItem(HWND hWnd, HMENU mnu, HDC hDC, MENUITEMINFOW *mii, RECT *mir, LPWSTR str)
319 {
320 BOOL res = FALSE;
321 DRAWITEMSTRUCT dis;
322
323 if(mii->fType & MFT_OWNERDRAW)
324 {
325 /* send WM_DRAWITEM message to window */
326 dis.CtlType = ODT_MENU;
327 dis.CtlID = 0;
328 dis.itemID = mii->wID;
329 dis.itemAction = ODA_DRAWENTIRE; /* FIXME */
330 dis.itemState = 0; /* FIXME */
331 dis.hwndItem = (HWND)mnu;
332 dis.hDC = hDC;
333 RtlCopyMemory(&dis.rcItem, mir, sizeof(RECT));
334 dis.itemData = mii->dwItemData;
335 res = (BOOL)SendMessageW(hWnd, WM_DRAWITEM, 0, (LPARAM)&dis);
336 return res;
337 }
338 else
339 {
340 /* FIXME draw the menu item */
341 SetTextColor(hDC, COLOR_MENUTEXT);
342 SetBkMode(hDC, TRANSPARENT);
343 DrawTextW(hDC, str, mii->cch, mir, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
344 }
345 return res;
346 }
347
348
349 UINT
350 MenuDrawMenuBar(HDC hDC, LPRECT Rect, HWND hWnd, BOOL Draw)
351 {
352 UINT bottom, line;
353 HMENU mnu;
354 HANDLE hHeap;
355 PVOID Buf, hBuf;
356 DWORD BufSize, Items, Items2, hItems;
357 MENUITEMINFOW *mii;
358 PVOID milist, *mih;
359 SETMENUITEMRECT smir;
360 RECT *omir, *mir = NULL;
361 LPWSTR str;
362
363 mnu = GetMenu(hWnd); /* Fixme - pass menu handle as parameter */
364 /* get menu item list size */
365 BufSize = NtUserBuildMenuItemList(mnu, (VOID*)1, 0, 0);
366 if(BufSize)
367 {
368 hHeap = GetProcessHeap();
369 hBuf = HeapAlloc(hHeap, 0, BufSize);
370 if(!hBuf)
371 return 0;
372 Buf = hBuf;
373 /* copy menu items into buffer */
374 Items = Items2 = NtUserBuildMenuItemList(mnu, Buf, BufSize, 0);
375
376 if(!Draw)
377 {
378 bottom = line = Rect->top;
379 smir.fByPosition = TRUE;
380 smir.uItem = 0;
381 /* calculate menu item rectangles */
382 milist = NULL;
383 hItems = 0;
384 while(Items > 0)
385 {
386 omir = mir;
387 mii = (LPMENUITEMINFOW)Buf;
388 Buf += sizeof(MENUITEMINFOW);
389 mir = (LPRECT)Buf;
390 Buf += sizeof(RECT);
391 if(mii->cch)
392 {
393 str = (LPWSTR)Buf;
394 Buf += (mii->cch + 1) * sizeof(WCHAR);
395 }
396 else
397 str = NULL;
398 if(omir)
399 {
400 mir->left = omir->right + 1;
401 mir->top = omir->top;
402 mir->right += mir->left;
403 mir->bottom += mir->top;
404 }
405 else
406 {
407 mir->left = Rect->left;
408 mir->top = Rect->top;
409 }
410 if((mii->fType & MFT_RIGHTJUSTIFY) && !milist)
411 {
412 milist = mih = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Items * sizeof(MENUITEMINFOW*));
413 hItems = Items;
414 }
415
416 MeasureMenuItem(hWnd, mnu, hDC, mii, mir, str);
417
418 if((mir->right > Rect->right) || (mii->fType & (MFT_MENUBREAK | MFT_MENUBARBREAK)))
419 {
420 mir->right -= (mir->left - Rect->left);
421 mir->left = Rect->left;
422 mir->top += line;
423 mir->bottom += line;
424 line = mir->bottom - mir->top;
425 }
426
427 if(!milist)
428 {
429 smir.rcRect = *mir;
430 NtUserSetMenuItemRect(mnu, &smir);
431 smir.uItem++;
432 }
433 else
434 {
435 *(mih++) = mii;
436 }
437
438 bottom = max(bottom, mir->bottom);
439 line = max(line, mir->bottom - mir->top);
440 Items--;
441 }
442 /* align help menus to the right */
443 if(milist)
444 {
445 LONG x;
446 x = Rect->right;
447 mii = NULL;
448 smir.uItem = Items2 - 1;
449 while(hItems > 0)
450 {
451 LONG wd;
452 MENUITEMINFOW *omii;
453
454 omii = mii;
455 mii = (MENUITEMINFOW*)(*(--mih));
456 mir = (LPRECT)(mii + 1);
457
458 if(omii && ((mir->right >= x) || (omii->fType & (MFT_MENUBREAK | MFT_MENUBARBREAK))))
459 {
460 x = Rect->right;
461 }
462
463 wd = (mir->right - mir->left);
464 mir->right = x - 1;
465 mir->left = mir->right - wd;
466 x = mir->left;
467
468 smir.rcRect = *mir;
469
470 NtUserSetMenuItemRect(mnu, &smir);
471 smir.uItem--;
472 hItems--;
473 }
474 }
475
476 if(milist)
477 HeapFree(GetProcessHeap(), 0, milist);
478
479 bottom = max(bottom, Rect->top + GetSystemMetrics(SM_CYMENU));
480 if(bottom - Rect->top > 0)
481 {
482 NtUserSetMenuBarHeight(mnu, bottom - Rect->top);
483 }
484 }
485 else
486 {
487 bottom = Rect->bottom;
488 FillRect(hDC, Rect, GetSysColorBrush(COLOR_MENU));
489 Buf = hBuf;
490 /* draw menu items */
491 while (Items2 > 0)
492 {
493 mii = (LPMENUITEMINFOW)Buf;
494 Buf += sizeof(MENUITEMINFOW);
495 mir = (LPRECT)Buf;
496 Buf += sizeof(RECT);
497 if(mii->cch)
498 {
499 str = (LPWSTR)Buf;
500 Buf += (mii->cch + 1) * sizeof(WCHAR);
501 }
502 else
503 str = NULL;
504 /* DbgPrint("Draw menu item %ws at (%d, %d, %d, %d)\n", str, mir->left, mir->top, mir->right, mir->bottom); */
505 DrawMenuItem(hWnd, mnu, hDC, mii, mir, str);
506 Items2--;
507 }
508 }
509
510 HeapFree(hHeap, 0, hBuf);
511 }
512
513 return max(bottom - Rect->top, GetSystemMetrics(SM_CYMENU));
514 }
515
516
517 VOID
518 MenuTrackMouseMenuBar(HWND hWnd, ULONG Ht, POINT Pt)
519 {
520 int Item = NtUserMenuItemFromPoint(hWnd, GetMenu(hWnd), Pt.x, Pt.y);
521
522 if(Item > -1)
523 {
524 /* FIXME */
525 }
526 }
527
528
529 VOID
530 MenuTrackKbdMenuBar(HWND hWnd, ULONG wParam, ULONG Key)
531 {
532 }
533
534 /* FUNCTIONS *****************************************************************/
535
536 /*static BOOL
537 MenuIsStringItem(ULONG TypeData)
538 {
539 return((TypeData & MENU_TYPE_MASK) == MF_STRING);
540 }*/
541
542
543 /*
544 * @implemented
545 */
546 BOOL STDCALL
547 AppendMenuA(HMENU hMenu,
548 UINT uFlags,
549 UINT_PTR uIDNewItem,
550 LPCSTR lpNewItem)
551 {
552 return(InsertMenuA(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
553 lpNewItem));
554 }
555
556
557 /*
558 * @implemented
559 */
560 BOOL STDCALL
561 AppendMenuW(HMENU hMenu,
562 UINT uFlags,
563 UINT_PTR uIDNewItem,
564 LPCWSTR lpNewItem)
565 {
566 return(InsertMenuW(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
567 lpNewItem));
568 }
569
570
571 /*
572 * @implemented
573 */
574 DWORD STDCALL
575 CheckMenuItem(HMENU hmenu,
576 UINT uIDCheckItem,
577 UINT uCheck)
578 {
579 return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck);
580 }
581
582
583 /*
584 * @unimplemented
585 */
586 BOOL STDCALL
587 CheckMenuRadioItem(HMENU hmenu,
588 UINT idFirst,
589 UINT idLast,
590 UINT idCheck,
591 UINT uFlags)
592 {
593 UNIMPLEMENTED;
594 return FALSE;
595 }
596
597
598 /*
599 * @implemented
600 */
601 HMENU STDCALL
602 CreateMenu(VOID)
603 {
604 return NtUserCreateMenu(FALSE);
605 }
606
607
608 /*
609 * @implemented
610 */
611 HMENU STDCALL
612 CreatePopupMenu(VOID)
613 {
614 return NtUserCreateMenu(TRUE);
615 }
616
617
618 /*
619 * @implemented
620 */
621 BOOL STDCALL
622 DeleteMenu(HMENU hMenu,
623 UINT uPosition,
624 UINT uFlags)
625 {
626 return NtUserDeleteMenu(hMenu, uPosition, uFlags);
627 }
628
629
630 /*
631 * @implemented
632 */
633 BOOL STDCALL
634 DestroyMenu(HMENU hMenu)
635 {
636 return NtUserDestroyMenu(hMenu);
637 }
638
639
640 /*
641 * @unimplemented
642 */
643 BOOL STDCALL
644 DrawMenuBar(HWND hWnd)
645 {
646 UNIMPLEMENTED
647 /* FIXME - return NtUserCallHwndLock(hWnd, 0x55); */
648 return FALSE;
649 }
650
651
652 /*
653 * @implemented
654 */
655 UINT STDCALL
656 EnableMenuItem(HMENU hMenu,
657 UINT uIDEnableItem,
658 UINT uEnable)
659 {
660 return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable);
661 }
662
663 /*
664 * @implemented
665 */
666 BOOL STDCALL
667 EndMenu(VOID)
668 {
669 GUITHREADINFO guii;
670 guii.cbSize = sizeof(GUITHREADINFO);
671 if(GetGUIThreadInfo(GetCurrentThreadId(), &guii) && guii.hwndMenuOwner)
672 {
673 PostMessageW(guii.hwndMenuOwner, WM_CANCELMODE, 0, 0);
674 }
675 return TRUE;
676 }
677
678
679 /*
680 * @implemented
681 */
682 HMENU STDCALL
683 GetMenu(HWND hWnd)
684 {
685 return NtUserGetMenu(hWnd);
686 }
687
688
689 /*
690 * @implemented
691 */
692 BOOL STDCALL
693 GetMenuBarInfo(HWND hwnd,
694 LONG idObject,
695 LONG idItem,
696 PMENUBARINFO pmbi)
697 {
698 return (BOOL)NtUserGetMenuBarInfo(hwnd, idObject, idItem, pmbi);
699 }
700
701
702 /*
703 * @implemented
704 */
705 LONG STDCALL
706 GetMenuCheckMarkDimensions(VOID)
707 {
708 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK),
709 GetSystemMetrics(SM_CYMENUCHECK)));
710 }
711
712
713 /*
714 * @implemented
715 */
716 UINT STDCALL
717 GetMenuDefaultItem(HMENU hMenu,
718 UINT fByPos,
719 UINT gmdiFlags)
720 {
721 return NtUserGetMenuDefaultItem(hMenu, fByPos, gmdiFlags);
722 }
723
724
725 /*
726 * @implemented
727 */
728 BOOL STDCALL
729 GetMenuInfo(HMENU hmenu,
730 LPMENUINFO lpcmi)
731 {
732 MENUINFO mi;
733 BOOL res = FALSE;
734
735 if(!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
736 return FALSE;
737
738 RtlZeroMemory(&mi, sizeof(MENUINFO));
739 mi.cbSize = sizeof(MENUINFO);
740 mi.fMask = lpcmi->fMask;
741
742 res = NtUserMenuInfo(hmenu, &mi, FALSE);
743
744 memcpy(lpcmi, &mi, sizeof(MENUINFO));
745 return res;
746 }
747
748
749 /*
750 * @implemented
751 */
752 int STDCALL
753 GetMenuItemCount(HMENU hMenu)
754 {
755 return NtUserBuildMenuItemList(hMenu, NULL, 0, 0);
756 }
757
758
759 /*
760 * @implemented
761 */
762 UINT STDCALL
763 GetMenuItemID(HMENU hMenu,
764 int nPos)
765 {
766 MENUITEMINFOW mii;
767
768 mii.cbSize = sizeof(MENUITEMINFOW);
769 mii.fMask = MIIM_ID | MIIM_SUBMENU;
770
771 if(!NtUserMenuItemInfo(hMenu, nPos, MF_BYPOSITION, &mii, FALSE))
772 {
773 return -1;
774 }
775
776 if(mii.hSubMenu) return -1;
777 if(mii.wID == 0) return -1;
778
779 return mii.wID;
780 }
781
782
783 /*
784 * @unimplemented
785 */
786 BOOL STDCALL
787 GetMenuItemInfoA(
788 HMENU hMenu,
789 UINT uItem,
790 BOOL fByPosition,
791 LPMENUITEMINFOA lpmii)
792 {
793 UNIMPLEMENTED;
794 return FALSE;
795 }
796
797
798 /*
799 * @unimplemented
800 */
801 BOOL
802 STDCALL
803 GetMenuItemInfoW(
804 HMENU hMenu,
805 UINT uItem,
806 BOOL fByPosition,
807 LPMENUITEMINFOW lpmii)
808 {
809 UNIMPLEMENTED;
810 return FALSE;
811 }
812
813
814 /*
815 * @unimplemented
816 */
817 BOOL STDCALL
818 GetMenuItemRect(HWND hWnd,
819 HMENU hMenu,
820 UINT uItem,
821 LPRECT lprcItem)
822 {
823 UNIMPLEMENTED;
824 return(FALSE);
825 }
826
827
828 /*
829 * @implemented
830 */
831 UINT
832 STDCALL
833 GetMenuState(
834 HMENU hMenu,
835 UINT uId,
836 UINT uFlags)
837 {
838 MENUITEMINFOW mii;
839 mii.cbSize = sizeof(MENUITEMINFOW);
840 mii.fMask = MIIM_STATE | MIIM_TYPE | MIIM_SUBMENU;
841
842 SetLastError(0);
843 if(NtUserMenuItemInfo(hMenu, uId, uFlags, &mii, FALSE))
844 {
845 UINT nSubItems = 0;
846 if(mii.hSubMenu)
847 {
848 nSubItems = (UINT)NtUserBuildMenuItemList(mii.hSubMenu, NULL, 0, 0);
849
850 /* FIXME - ported from wine, does that work (0xff)? */
851 if(GetLastError() != ERROR_INVALID_MENU_HANDLE)
852 return (nSubItems << 8) | ((mii.fState | mii.fType) & 0xff);
853
854 return (UINT)-1; /* Invalid submenu */
855 }
856
857 /* FIXME - ported from wine, does that work? */
858 return (mii.fType | mii.fState);
859 }
860
861 return (UINT)-1;
862 }
863
864
865 /*
866 * @unimplemented
867 */
868 int
869 STDCALL
870 GetMenuStringA(
871 HMENU hMenu,
872 UINT uIDItem,
873 LPSTR lpString,
874 int nMaxCount,
875 UINT uFlag)
876 {
877 UNIMPLEMENTED;
878 return 0;
879 }
880
881
882 /*
883 * @unimplemented
884 */
885 int
886 STDCALL
887 GetMenuStringW(
888 HMENU hMenu,
889 UINT uIDItem,
890 LPWSTR lpString,
891 int nMaxCount,
892 UINT uFlag)
893 {
894 UNIMPLEMENTED;
895 return 0;
896 }
897
898
899 /*
900 * @implemented
901 */
902 HMENU
903 STDCALL
904 GetSubMenu(
905 HMENU hMenu,
906 int nPos)
907 {
908 MENUITEMINFOW mi;
909 mi.cbSize = sizeof(mi);
910 mi.fMask = MIIM_SUBMENU;
911 if(NtUserMenuItemInfo(hMenu, (UINT)nPos, MF_BYPOSITION, &mi, FALSE))
912 {
913 return mi.hSubMenu;
914 }
915 return (HMENU)0;
916 }
917
918
919 /*
920 * @implemented
921 */
922 BOOL
923 STDCALL
924 HiliteMenuItem(
925 HWND hwnd,
926 HMENU hmenu,
927 UINT uItemHilite,
928 UINT uHilite)
929 {
930 return NtUserHiliteMenuItem(hwnd, hmenu, uItemHilite, uHilite);
931 }
932
933
934 /*
935 * @implemented
936 */
937 BOOL
938 STDCALL
939 InsertMenuA(
940 HMENU hMenu,
941 UINT uPosition,
942 UINT uFlags,
943 UINT_PTR uIDNewItem,
944 LPCSTR lpNewItem)
945 {
946 MENUITEMINFOA mii;
947 mii.cbSize = sizeof(MENUITEMINFOA);
948 mii.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE;
949 mii.fType = 0;
950 mii.fState = MFS_ENABLED;
951
952 if(uFlags & MF_BITMAP)
953 {
954 mii.fType |= MFT_BITMAP;
955 }
956 else if(uFlags & MF_OWNERDRAW)
957 {
958 mii.fType |= MFT_OWNERDRAW;
959 }
960
961 if(uFlags & MF_RIGHTJUSTIFY)
962 {
963 mii.fType |= MFT_RIGHTJUSTIFY;
964 }
965 if(uFlags & MF_MENUBREAK)
966 {
967 mii.fType |= MFT_MENUBREAK;
968 }
969 if(uFlags & MF_MENUBARBREAK)
970 {
971 mii.fType |= MFT_MENUBARBREAK;
972 }
973 if(uFlags & MF_DISABLED)
974 {
975 mii.fState |= MFS_DISABLED;
976 }
977 if(uFlags & MF_GRAYED)
978 {
979 mii.fState |= MFS_GRAYED;
980 }
981
982 mii.dwTypeData = (LPSTR)lpNewItem;
983 if(uFlags & MF_POPUP)
984 {
985 mii.fMask |= MIIM_SUBMENU;
986 mii.hSubMenu = (HMENU)uIDNewItem;
987 }
988 else
989 {
990 mii.fMask |= MIIM_ID;
991 mii.wID = (UINT)uIDNewItem;
992 }
993 return InsertMenuItemA(hMenu, uPosition, (BOOL)!(MF_BYPOSITION & uFlags), &mii);
994 }
995
996
997 /*
998 * @implemented
999 */
1000 BOOL
1001 STDCALL
1002 InsertMenuItemA(
1003 HMENU hMenu,
1004 UINT uItem,
1005 BOOL fByPosition,
1006 LPCMENUITEMINFOA lpmii)
1007 {
1008 MENUITEMINFOW mi;
1009 UNICODE_STRING MenuText;
1010 BOOL res = FALSE;
1011 BOOL CleanHeap = FALSE;
1012 NTSTATUS Status;
1013
1014 if((lpmii->cbSize == sizeof(MENUITEMINFOA)) ||
1015 (lpmii->cbSize == sizeof(MENUITEMINFOA) - sizeof(HBITMAP)))
1016 {
1017 RtlMoveMemory ( &mi, lpmii, lpmii->cbSize );
1018
1019 /* copy the text string */
1020 if((mi.fMask & (MIIM_TYPE | MIIM_STRING)) &&
1021 (MENU_ITEM_TYPE(mi.fType) == MF_STRING) && mi.dwTypeData)
1022 {
1023 Status = HEAP_strdupAtoW ( &mi.dwTypeData, (LPCSTR)mi.dwTypeData, &mi.cch );
1024 if (!NT_SUCCESS (Status))
1025 {
1026 SetLastError (RtlNtStatusToDosError(Status));
1027 return FALSE;
1028 }
1029 RtlInitUnicodeString(&MenuText, (PWSTR)mi.dwTypeData);
1030 mi.dwTypeData = (LPWSTR)&MenuText;
1031 CleanHeap = TRUE;
1032 }
1033
1034 res = NtUserInsertMenuItem(hMenu, uItem, fByPosition, &mi);
1035
1036 if ( CleanHeap ) HEAP_free ( mi.dwTypeData );
1037 }
1038 return res;
1039 }
1040
1041
1042 /*
1043 * @implemented
1044 */
1045 BOOL
1046 STDCALL
1047 InsertMenuItemW(
1048 HMENU hMenu,
1049 UINT uItem,
1050 BOOL fByPosition,
1051 LPCMENUITEMINFOW lpmii)
1052 {
1053 MENUITEMINFOW mi;
1054 UNICODE_STRING MenuText;
1055 BOOL res = FALSE;
1056 BOOL CleanHeap = FALSE;
1057 HANDLE hHeap = RtlGetProcessHeap();
1058 mi.hbmpItem = (HBITMAP)0;
1059
1060 // while we could just pass 'lpmii' to win32k, we make a copy so that
1061 // if a bad user passes bad data, we crash his process instead of the
1062 // entire kernel
1063
1064 if((lpmii->cbSize == sizeof(MENUITEMINFOW)) ||
1065 (lpmii->cbSize == sizeof(MENUITEMINFOW) - sizeof(HBITMAP)))
1066 {
1067 memcpy(&mi, lpmii, lpmii->cbSize);
1068
1069 /* copy the text string */
1070 if((mi.fMask & (MIIM_TYPE | MIIM_STRING)) &&
1071 (MENU_ITEM_TYPE(mi.fType) == MF_STRING) && mi.dwTypeData)
1072 {
1073 if(lpmii->cch > 0)
1074 {
1075 if(!RtlCreateUnicodeString(&MenuText, (PWSTR)lpmii->dwTypeData))
1076 {
1077 SetLastError (RtlNtStatusToDosError(STATUS_NO_MEMORY));
1078 return FALSE;
1079 }
1080 mi.dwTypeData = (LPWSTR)&MenuText;
1081 mi.cch = MenuText.Length / sizeof(WCHAR);
1082 CleanHeap = TRUE;
1083 }
1084 };
1085
1086 res = NtUserInsertMenuItem(hMenu, uItem, fByPosition, &mi);
1087
1088 if(CleanHeap) RtlFreeHeap (hHeap, 0, mi.dwTypeData);
1089 }
1090 return res;
1091 }
1092
1093
1094 /*
1095 * @implemented
1096 */
1097 BOOL
1098 STDCALL
1099 InsertMenuW(
1100 HMENU hMenu,
1101 UINT uPosition,
1102 UINT uFlags,
1103 UINT_PTR uIDNewItem,
1104 LPCWSTR lpNewItem)
1105 {
1106 MENUITEMINFOW mii;
1107 mii.cbSize = sizeof(MENUITEMINFOW);
1108 mii.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE;
1109 mii.fType = 0;
1110 mii.fState = MFS_ENABLED;
1111
1112 if(uFlags & MF_BITMAP)
1113 {
1114 mii.fType |= MFT_BITMAP;
1115 }
1116 else if(uFlags & MF_OWNERDRAW)
1117 {
1118 mii.fType |= MFT_OWNERDRAW;
1119 }
1120
1121 if(uFlags & MF_RIGHTJUSTIFY)
1122 {
1123 mii.fType |= MFT_RIGHTJUSTIFY;
1124 }
1125 if(uFlags & MF_MENUBREAK)
1126 {
1127 mii.fType |= MFT_MENUBREAK;
1128 }
1129 if(uFlags & MF_MENUBARBREAK)
1130 {
1131 mii.fType |= MFT_MENUBARBREAK;
1132 }
1133 if(uFlags & MF_DISABLED)
1134 {
1135 mii.fState |= MFS_DISABLED;
1136 }
1137 if(uFlags & MF_GRAYED)
1138 {
1139 mii.fState |= MFS_GRAYED;
1140 }
1141
1142 mii.dwTypeData = (LPWSTR)lpNewItem;
1143 if(uFlags & MF_POPUP)
1144 {
1145 mii.fMask |= MIIM_SUBMENU;
1146 mii.hSubMenu = (HMENU)uIDNewItem;
1147 }
1148 else
1149 {
1150 mii.fMask |= MIIM_ID;
1151 mii.wID = (UINT)uIDNewItem;
1152 }
1153 return InsertMenuItemW(hMenu, uPosition, (BOOL)!(MF_BYPOSITION & uFlags), &mii);
1154 }
1155
1156
1157 /*
1158 * @implemented
1159 */
1160 BOOL
1161 STDCALL
1162 IsMenu(
1163 HMENU hMenu)
1164 {
1165 DWORD ret;
1166 SetLastError(ERROR_SUCCESS);
1167 ret = NtUserBuildMenuItemList(hMenu, NULL, 0, 0);
1168 return ((ret == (DWORD)-1) || (GetLastError() == ERROR_INVALID_MENU_HANDLE));
1169 }
1170
1171
1172 /*
1173 * @implemented
1174 */
1175 HMENU STDCALL
1176 LoadMenuA(HINSTANCE hInstance,
1177 LPCSTR lpMenuName)
1178 {
1179 HANDLE Resource = FindResourceA(hInstance, lpMenuName, MAKEINTRESOURCEA(4));
1180 if (Resource == NULL)
1181 {
1182 return(NULL);
1183 }
1184 return(LoadMenuIndirectA((PVOID)LoadResource(hInstance, Resource)));
1185 }
1186
1187
1188 /*
1189 * @implemented
1190 */
1191 HMENU STDCALL
1192 LoadMenuIndirectA(CONST MENUTEMPLATE *lpMenuTemplate)
1193 {
1194 return(LoadMenuIndirectW(lpMenuTemplate));
1195 }
1196
1197
1198 /*
1199 * @implemented
1200 */
1201 HMENU STDCALL
1202 LoadMenuIndirectW(CONST MENUTEMPLATE *lpMenuTemplate)
1203 {
1204 HMENU hMenu;
1205 WORD version, offset;
1206 LPCSTR p = (LPCSTR)lpMenuTemplate;
1207
1208 version = GET_WORD(p);
1209 p += sizeof(WORD);
1210
1211 switch (version)
1212 {
1213 case 0: /* standard format is version of 0 */
1214 offset = GET_WORD(p);
1215 p += sizeof(WORD) + offset;
1216 if (!(hMenu = CreateMenu())) return 0;
1217 if (!MENU_ParseResource(p, hMenu, TRUE))
1218 {
1219 DestroyMenu(hMenu);
1220 return 0;
1221 }
1222 return hMenu;
1223 case 1: /* extended format is version of 1 */
1224 offset = GET_WORD(p);
1225 p += sizeof(WORD) + offset;
1226 if (!(hMenu = CreateMenu())) return 0;
1227 if (!MENUEX_ParseResource(p, hMenu))
1228 {
1229 DestroyMenu( hMenu );
1230 return 0;
1231 }
1232 return hMenu;
1233 default:
1234 DbgPrint("LoadMenuIndirectW(): version %d not supported.\n", version);
1235 return 0;
1236 }
1237 }
1238
1239
1240 /*
1241 * @implemented
1242 */
1243 HMENU STDCALL
1244 LoadMenuW(HINSTANCE hInstance,
1245 LPCWSTR lpMenuName)
1246 {
1247 HANDLE Resource = FindResourceW(hInstance, lpMenuName, RT_MENU);
1248 if (Resource == NULL)
1249 {
1250 return(NULL);
1251 }
1252 return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource)));
1253 }
1254
1255
1256 /*
1257 * @implemented
1258 */
1259 int
1260 STDCALL
1261 MenuItemFromPoint(
1262 HWND hWnd,
1263 HMENU hMenu,
1264 POINT ptScreen)
1265 {
1266 return NtUserMenuItemFromPoint(hWnd, hMenu, ptScreen.x, ptScreen.y);
1267 }
1268
1269
1270 /*
1271 * @unimplemented
1272 */
1273 BOOL
1274 STDCALL
1275 ModifyMenuA(
1276 HMENU hMnu,
1277 UINT uPosition,
1278 UINT uFlags,
1279 UINT_PTR uIDNewItem,
1280 LPCSTR lpNewItem)
1281 {
1282 UNIMPLEMENTED;
1283 return FALSE;
1284 }
1285
1286
1287 /*
1288 * @unimplemented
1289 */
1290 BOOL
1291 STDCALL
1292 ModifyMenuW(
1293 HMENU hMnu,
1294 UINT uPosition,
1295 UINT uFlags,
1296 UINT_PTR uIDNewItem,
1297 LPCWSTR lpNewItem)
1298 {
1299 UNIMPLEMENTED;
1300 return FALSE;
1301 }
1302
1303
1304 /*
1305 * @implemented
1306 */
1307 BOOL
1308 STDCALL
1309 RemoveMenu(
1310 HMENU hMenu,
1311 UINT uPosition,
1312 UINT uFlags)
1313 {
1314 return NtUserRemoveMenu(hMenu, uPosition, uFlags);
1315 }
1316
1317
1318 /*
1319 * @implemented
1320 */
1321 BOOL STDCALL
1322 SetMenu(HWND hWnd,
1323 HMENU hMenu)
1324 {
1325 return NtUserSetMenu(hWnd, hMenu, TRUE);
1326 }
1327
1328
1329 /*
1330 * @implemented
1331 */
1332 BOOL
1333 STDCALL
1334 SetMenuDefaultItem(
1335 HMENU hMenu,
1336 UINT uItem,
1337 UINT fByPos)
1338 {
1339 return NtUserSetMenuDefaultItem(hMenu, uItem, fByPos);
1340 }
1341
1342
1343 /*
1344 * @implemented
1345 */
1346 BOOL
1347 STDCALL
1348 SetMenuInfo(
1349 HMENU hmenu,
1350 LPCMENUINFO lpcmi)
1351 {
1352 MENUINFO mi;
1353 BOOL res = FALSE;
1354 if(lpcmi->cbSize != sizeof(MENUINFO))
1355 return res;
1356
1357 memcpy(&mi, lpcmi, sizeof(MENUINFO));
1358 return NtUserMenuInfo(hmenu, &mi, TRUE);
1359 }
1360
1361
1362 /*
1363 * @unimplemented
1364 */
1365 BOOL
1366 STDCALL
1367 SetMenuItemBitmaps(
1368 HMENU hMenu,
1369 UINT uPosition,
1370 UINT uFlags,
1371 HBITMAP hBitmapUnchecked,
1372 HBITMAP hBitmapChecked)
1373 {
1374 UNIMPLEMENTED;
1375 return FALSE;
1376 }
1377
1378
1379 /*
1380 * @unimplemented
1381 */
1382 BOOL
1383 STDCALL
1384 SetMenuItemInfoA(
1385 HMENU hMenu,
1386 UINT uItem,
1387 BOOL fByPosition,
1388 LPMENUITEMINFOA lpmii)
1389 {
1390 UNIMPLEMENTED;
1391 return FALSE;
1392 }
1393
1394
1395 /*
1396 * @unimplemented
1397 */
1398 BOOL
1399 STDCALL
1400 SetMenuItemInfoW(
1401 HMENU hMenu,
1402 UINT uItem,
1403 BOOL fByPosition,
1404 LPMENUITEMINFOW lpmii)
1405 {
1406 UNIMPLEMENTED;
1407 return FALSE;
1408 }
1409
1410
1411 /*
1412 * @implemented
1413 */
1414 BOOL
1415 STDCALL
1416 TrackPopupMenu(
1417 HMENU hMenu,
1418 UINT uFlags,
1419 int x,
1420 int y,
1421 int nReserved,
1422 HWND hWnd,
1423 CONST RECT *prcRect)
1424 {
1425 TPMPARAMS tpm;
1426
1427 if(prcRect)
1428 {
1429 tpm.cbSize = sizeof(TPMPARAMS);
1430 tpm.rcExclude = *prcRect;
1431 }
1432
1433 return (BOOL)NtUserTrackPopupMenuEx(hMenu, uFlags, x, y, hWnd,
1434 (prcRect ? &tpm : NULL));
1435 }
1436
1437
1438 /*
1439 * @implemented
1440 */
1441 BOOL
1442 STDCALL
1443 TrackPopupMenuEx(
1444 HMENU hmenu,
1445 UINT fuFlags,
1446 int x,
1447 int y,
1448 HWND hwnd,
1449 LPTPMPARAMS lptpm)
1450 {
1451 return (BOOL)NtUserTrackPopupMenuEx(hmenu, fuFlags, x, y, hwnd, lptpm);
1452 }
1453
1454
1455 /*
1456 * @implemented
1457 */
1458 BOOL
1459 STDCALL
1460 SetMenuContextHelpId(HMENU hmenu,
1461 DWORD dwContextHelpId)
1462 {
1463 return NtUserSetMenuContextHelpId(hmenu, dwContextHelpId);
1464 }
1465
1466
1467 /*
1468 * @implemented
1469 */
1470 DWORD
1471 STDCALL
1472 GetMenuContextHelpId(HMENU hmenu)
1473 {
1474 MENUINFO mi;
1475 mi.cbSize = sizeof(MENUINFO);
1476 mi.fMask = MIM_HELPID;
1477
1478 if(NtUserMenuInfo(hmenu, &mi, FALSE))
1479 {
1480 return mi.dwContextHelpID;
1481 }
1482 return 0;
1483 }
1484
1485 /*
1486 * @unimplemented
1487 */
1488 DWORD
1489 STDCALL
1490 DrawMenuBarTemp(
1491 HWND hwnd,
1492 HDC hDC,
1493 LPRECT lprect,
1494 HMENU hMenu,
1495 HFONT hFont
1496 )
1497 {
1498 UNIMPLEMENTED;
1499 return FALSE;
1500 }
1501
1502 /*
1503 * @unimplemented
1504 */
1505 LRESULT
1506 STDCALL
1507 MenuWindowProcA(
1508 HWND hWnd,
1509 UINT Msg,
1510 WPARAM wParam,
1511 LPARAM lParam
1512 )
1513 {
1514 UNIMPLEMENTED;
1515 return FALSE;
1516 }
1517
1518 /*
1519 * @unimplemented
1520 */
1521 LRESULT
1522 STDCALL
1523 MenuWindowProcW(
1524 HWND hWnd,
1525 UINT Msg,
1526 WPARAM wParam,
1527 LPARAM lParam
1528 )
1529 {
1530 UNIMPLEMENTED;
1531 return FALSE;
1532 }
1533
1534 /*
1535 * @unimplemented
1536 */
1537 BOOL
1538 STDCALL
1539 ChangeMenuW(
1540 HMENU hMenu,
1541 UINT cmd,
1542 LPCWSTR lpszNewItem,
1543 UINT cmdInsert,
1544 UINT flags)
1545 {
1546 UNIMPLEMENTED;
1547 return FALSE;
1548 }
1549
1550 /*
1551 * @unimplemented
1552 */
1553 BOOL
1554 STDCALL
1555 ChangeMenuA(
1556 HMENU hMenu,
1557 UINT cmd,
1558 LPCSTR lpszNewItem,
1559 UINT cmdInsert,
1560 UINT flags)
1561 {
1562 UNIMPLEMENTED;
1563 return FALSE;
1564 }